mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
* perf(native): query retained export table facts * docs(perf): record native export task comparisons * fix(native): preserve omitted export projection fields * docs(perf): refresh final export view qualification * test(native): verify sparse export view digests
252 lines
8.0 KiB
TypeScript
252 lines
8.0 KiB
TypeScript
import { z } from "zod";
|
|
import {
|
|
nativeExportsSchema,
|
|
peNativeExportsSchema as pe,
|
|
elfNativeExportsSchema as elf,
|
|
type NativeExports,
|
|
} from "../native/nativeExports.js";
|
|
import {
|
|
canBorrowParsedJsonSnapshot,
|
|
freezeOwnedJsonTreeSnapshot,
|
|
isImmutableJsonSnapshot,
|
|
} from "../immutableJson.js";
|
|
import type { AnalysisError } from "../analysisErrorBase.js";
|
|
import { err, ok, type Result } from "../result.js";
|
|
import type {
|
|
AnalysisViewParent,
|
|
AnalysisViewRequest,
|
|
UnsignedAnalysisView,
|
|
} from "./analysisView.js";
|
|
import { analysisViewInputError } from "./analysisViewErrors.js";
|
|
import { parseAnalysisViewResult } from "./analysisViewParent.js";
|
|
import {
|
|
boundedViewMessages,
|
|
completeWithinViewCoverage,
|
|
pageViewCoverage,
|
|
} from "./analysisViewCoverage.js";
|
|
import { selectViewRows } from "./selectViewRows.js";
|
|
|
|
const peRow = pe.shape.exports.element;
|
|
const elfRow = elf.shape.exports.element;
|
|
const issue = pe.shape.issues.element;
|
|
const sourceIndex = { source_index: z.int().nonnegative() };
|
|
const page = {
|
|
kind: z.literal("native-exports"),
|
|
offset: z.int().nonnegative().default(0),
|
|
limit: z.int().positive().default(64),
|
|
};
|
|
|
|
/** Exact names preserve all versions/aliases; indices are scoped to the complete parent. */
|
|
export const nativeExportsViewRequestSchema = z.discriminatedUnion(
|
|
"collection",
|
|
[
|
|
z.strictObject({
|
|
...page,
|
|
collection: z.literal("exports"),
|
|
names: z.array(z.string()).optional(),
|
|
indices: z.array(z.int().nonnegative()).optional(),
|
|
fields: z.array(z.union([peRow.keyof(), elfRow.keyof()])).optional(),
|
|
}),
|
|
z.strictObject({
|
|
...page,
|
|
collection: z.literal("issues"),
|
|
indices: z.array(z.int().nonnegative()).optional(),
|
|
}),
|
|
],
|
|
);
|
|
|
|
export const nativeExportsViewItemsSchema = z.union([
|
|
z.array(peRow.partial().extend(sourceIndex)),
|
|
z.array(
|
|
elfRow.partial().extend({
|
|
...sourceIndex,
|
|
// Historical defaults normalize complete source rows, never omitted columns.
|
|
name_bytes_base64: elfRow.shape.name_bytes_base64
|
|
.removeDefault()
|
|
.optional(),
|
|
name_utf8_decodable: elfRow.shape.name_utf8_decodable
|
|
.removeDefault()
|
|
.optional(),
|
|
}),
|
|
),
|
|
z.array(issue.extend(sourceIndex)),
|
|
]);
|
|
|
|
const counts = {
|
|
export_count: z.int().nonnegative(),
|
|
issue_count: z.int().nonnegative(),
|
|
limitation_count: z.int().nonnegative(),
|
|
};
|
|
/** Source coverage remains distinct from page coverage, including an empty partial page. */
|
|
export const nativeExportsSummarySchema = z.discriminatedUnion("format", [
|
|
pe.omit({ exports: true, issues: true, limitations: true }).extend(counts),
|
|
elf.omit({ exports: true, issues: true, limitations: true }).extend(counts),
|
|
]);
|
|
|
|
// This validates exact immutable Evidence, never a path or current filesystem state.
|
|
// Weak keys release their parsed representation with the owning retained record.
|
|
const checked = new WeakMap<object, NativeExports>();
|
|
const readReport = (
|
|
parent: AnalysisViewParent,
|
|
): Result<NativeExports, AnalysisError> => {
|
|
const source = parent.normalizedResult;
|
|
const reusable =
|
|
parent.sealedResult === true &&
|
|
typeof source === "object" &&
|
|
source !== null &&
|
|
isImmutableJsonSnapshot(source);
|
|
const known = reusable ? checked.get(source) : undefined;
|
|
if (known !== undefined) return ok(known);
|
|
const parsed = parseAnalysisViewResult(nativeExportsSchema, parent);
|
|
if (!parsed.ok) return parsed;
|
|
if (reusable) {
|
|
const report = canBorrowParsedJsonSnapshot(source, parsed.value)
|
|
? source
|
|
: freezeOwnedJsonTreeSnapshot(parsed.value);
|
|
checked.set(source, report);
|
|
return ok(report);
|
|
}
|
|
return parsed;
|
|
};
|
|
|
|
/** Read selected export facts without reopening the artifact or reinterpreting its tables. */
|
|
export const projectNativeExportsView = (
|
|
parent: AnalysisViewParent,
|
|
view: AnalysisViewRequest,
|
|
): Result<UnsignedAnalysisView, AnalysisError> => {
|
|
const invalid = (message: string) =>
|
|
err(
|
|
analysisViewInputError([
|
|
{ path: ["view"], reason: "invalid_value", message },
|
|
]),
|
|
);
|
|
if (view.kind !== "summary" && view.kind !== "native-exports")
|
|
return invalid(
|
|
"Native export Evidence supports summary and native-exports views.",
|
|
);
|
|
const parsed = readReport(parent);
|
|
if (!parsed.ok) return parsed;
|
|
const report = parsed.value;
|
|
const { exports, issues, limitations, ...identity } = report;
|
|
const summary = {
|
|
...identity,
|
|
export_count: exports.length,
|
|
issue_count: issues.length,
|
|
limitation_count: limitations.length,
|
|
};
|
|
const shared = {
|
|
parent_evidence_id: parent.evidenceId,
|
|
parent_operation: "inspect_native_exports" as const,
|
|
parent_digest: parent.evidenceId.slice(3),
|
|
artifact: { path: report.artifact.path, sha256: report.artifact.sha256 },
|
|
limitations: boundedViewMessages(
|
|
[...parent.limitations, ...limitations],
|
|
parent.evidenceId,
|
|
"limitations",
|
|
),
|
|
unknowns:
|
|
report.coverage.status === "complete"
|
|
? []
|
|
: [
|
|
report.coverage.reason ??
|
|
"Export-table coverage is incomplete; no-match pages do not prove absence.",
|
|
],
|
|
};
|
|
if (view.kind === "summary")
|
|
return ok({
|
|
...shared,
|
|
kind: "summary",
|
|
view,
|
|
summary,
|
|
coverage: completeWithinViewCoverage(exports.length, exports.length),
|
|
});
|
|
const indices =
|
|
view.indices === undefined ? undefined : new Set(view.indices);
|
|
if (view.collection === "issues") {
|
|
const selected =
|
|
indices === undefined
|
|
? {
|
|
rows: issues
|
|
.slice(view.offset, view.offset + view.limit)
|
|
.map((row, index) => ({
|
|
...row,
|
|
source_index: view.offset + index,
|
|
})),
|
|
total: issues.length,
|
|
}
|
|
: selectViewRows(
|
|
issues,
|
|
view,
|
|
(_, index) => indices.has(index),
|
|
(row, source_index) => ({ ...row, source_index }),
|
|
);
|
|
return ok({
|
|
...shared,
|
|
kind: "native-exports",
|
|
view,
|
|
exports: summary,
|
|
items: selected.rows,
|
|
coverage: pageViewCoverage(
|
|
view.offset,
|
|
selected.rows.length,
|
|
selected.total,
|
|
),
|
|
});
|
|
}
|
|
const allowed =
|
|
report.format === "pe" ? peRow.keyof().options : elfRow.keyof().options;
|
|
const fields =
|
|
view.fields === undefined ? allowed : [...new Set(view.fields)];
|
|
const allowedFields: readonly string[] = allowed;
|
|
if (fields.some((field) => !allowedFields.includes(field)))
|
|
return invalid(
|
|
`Selected export fields do not belong to ${report.format}; available fields: ${allowed.join(", ")}.`,
|
|
);
|
|
const names = view.names === undefined ? undefined : new Set(view.names);
|
|
// A projected decoded name still carries its exact bytes and encoding qualification.
|
|
const columns = new Set<string>(fields);
|
|
if (report.format === "elf" && columns.has("name")) {
|
|
columns.add("name_bytes_base64");
|
|
columns.add("name_utf8_decodable");
|
|
}
|
|
const rows: readonly NativeExports["exports"][number][] = exports;
|
|
const projectRow = (
|
|
row: NativeExports["exports"][number],
|
|
source_index: number,
|
|
) => ({
|
|
...Object.fromEntries(
|
|
Object.entries(row).filter(([key]) => columns.has(key)),
|
|
),
|
|
source_index,
|
|
});
|
|
// Unfiltered pages use the owner's original order directly; no whole-table scan.
|
|
const selected =
|
|
names === undefined && indices === undefined
|
|
? {
|
|
rows: rows
|
|
.slice(view.offset, view.offset + view.limit)
|
|
.map((row, index) => projectRow(row, view.offset + index)),
|
|
total: rows.length,
|
|
}
|
|
: selectViewRows(
|
|
rows,
|
|
view,
|
|
(row, index) =>
|
|
(indices === undefined || indices.has(index)) &&
|
|
(names === undefined || (row.name !== null && names.has(row.name))),
|
|
projectRow,
|
|
);
|
|
return ok({
|
|
...shared,
|
|
kind: "native-exports",
|
|
view,
|
|
exports: summary,
|
|
items: selected.rows,
|
|
coverage: pageViewCoverage(
|
|
view.offset,
|
|
selected.rows.length,
|
|
selected.total,
|
|
),
|
|
});
|
|
};
|