mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
refactor: use canonical implementations and types directly
Remove forwarding wrappers and shadow aliases, migrate callers and verification scripts to their owners, and share one exhaustive semantic resource-bound projection.
This commit is contained in:
@@ -44,7 +44,7 @@ See [docs/testing.md](docs/testing.md) for test scopes and verification lanes an
|
||||
|
||||
## Configuration & Environment Variables
|
||||
|
||||
Configuration is parsed and validated by `src/config.ts` and `src/config/`. Keep user-facing setup and provider configuration in [README.md](README.md) and the relevant guide under `docs/`; do not maintain a second environment-variable catalogue here.
|
||||
Configuration is parsed and validated by `src/config/`. Keep user-facing setup and provider configuration in [README.md](README.md) and the relevant guide under `docs/`; do not maintain a second environment-variable catalogue here.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ graph TD
|
||||
end
|
||||
|
||||
subgraph "Configuration"
|
||||
CONFIG["src/config.ts + src/config/<br/>Zod-validated env parsing<br/>provider settings and origins"]
|
||||
CONFIG["src/config/<br/>Zod-validated env parsing<br/>provider settings and origins"]
|
||||
end
|
||||
|
||||
subgraph "Tests"
|
||||
|
||||
@@ -4,7 +4,7 @@ import assert from "node:assert/strict";
|
||||
import { constants } from "node:buffer";
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
import { canonicalDigest } from "../../../dist/domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../../../dist/domain/canonicalDigest.js";
|
||||
import {
|
||||
createEvidence,
|
||||
parseEvidence,
|
||||
@@ -23,7 +23,7 @@ for (let index = 0; index < count; index += 1) {
|
||||
expected.update(encodedLeaf);
|
||||
}
|
||||
expected.update("]}");
|
||||
assert.equal(canonicalDigest(value), expected.digest("hex"));
|
||||
assert.equal(digestCanonicalValue(value), expected.digest("hex"));
|
||||
|
||||
const evidence = createEvidence(
|
||||
undefined,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import type { ProviderIdentity } from "./AnalysisProvider.js";
|
||||
import type { BrowserScenario } from "../domain/browserScenario.js";
|
||||
import type { BrowserScenarioCapture } from "../domain/browserScenarioCapture.js";
|
||||
@@ -13,7 +13,7 @@ const browserScenarioParameters = (
|
||||
scenario: BrowserScenario,
|
||||
): EvidenceObservation["parameters"] => ({
|
||||
...jsonObjectSchema.parse(scenario),
|
||||
scenario_sha256: canonicalDigest(scenario, "Browser scenario"),
|
||||
scenario_sha256: digestCanonicalValue(scenario, "Browser scenario"),
|
||||
});
|
||||
|
||||
/** Create Evidence without retaining resolved scenario secret values. */
|
||||
|
||||
@@ -5,7 +5,8 @@ import { silentLogger } from "../logger.js";
|
||||
import type { Logger } from "pino";
|
||||
import type { DirectAnalysisDependencies } from "./DirectAnalysisDependencies.js";
|
||||
|
||||
const runSessionStatus = async (
|
||||
/** Read target-free provider status and operation availability. */
|
||||
export const runSessionStatus = async (
|
||||
dependencies: Pick<
|
||||
DirectAnalysisDependencies,
|
||||
"createBinarySession" | "readConfiguration"
|
||||
@@ -26,21 +27,3 @@ const runSessionStatus = async (
|
||||
await session.close();
|
||||
}
|
||||
};
|
||||
|
||||
/** List binary-session provider candidates and auxiliary operation availability. */
|
||||
export const runProviderStatus = (
|
||||
dependencies: Pick<
|
||||
DirectAnalysisDependencies,
|
||||
"createBinarySession" | "readConfiguration"
|
||||
>,
|
||||
logger: Logger = silentLogger,
|
||||
) => runSessionStatus(dependencies, logger);
|
||||
|
||||
/** List binary-session operation descriptors; this is not the full MCP catalog. */
|
||||
export const runCapabilityStatus = (
|
||||
dependencies: Pick<
|
||||
DirectAnalysisDependencies,
|
||||
"createBinarySession" | "readConfiguration"
|
||||
>,
|
||||
logger: Logger = silentLogger,
|
||||
) => runSessionStatus(dependencies, logger);
|
||||
|
||||
@@ -26,13 +26,11 @@ import type {
|
||||
} from "./SetupTypes.js";
|
||||
import type { SetupClient } from "./SupportedClients.js";
|
||||
|
||||
const defaultCommand = (): readonly string[] => npxRegistrationCommand();
|
||||
|
||||
/** Configure one supported client's native stdio MCP registration shape. */
|
||||
export const configureClientConfiguration = (
|
||||
client: SetupClient,
|
||||
environment: SetupProviderEnvironment = {},
|
||||
command: readonly string[] = defaultCommand(),
|
||||
command: readonly string[] = npxRegistrationCommand(),
|
||||
): Promise<ClientConfigurationResult> => {
|
||||
if (client.format === undefined || client.format === "unsupported")
|
||||
return Promise.resolve({ status: "failed", reason: "readback" });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "../../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../../domain/canonicalDigest.js";
|
||||
import type { ProviderIdentity } from "../AnalysisProvider.js";
|
||||
import type { Evidence, EvidenceObservation } from "../../domain/evidence.js";
|
||||
import { createEvidence } from "../../domain/evidence.js";
|
||||
@@ -44,6 +44,6 @@ const parameters = (
|
||||
const scenario = jsonObjectSchema.parse(input);
|
||||
return {
|
||||
...scenario,
|
||||
scenario_sha256: canonicalDigest(scenario, "Electron scenario"),
|
||||
scenario_sha256: digestCanonicalValue(scenario, "Electron scenario"),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import type { ElectronIpcFinding } from "../../domain/javascript/electronStaticAnalysisTypes.js";
|
||||
import type { ElectronBoundarySummary } from "../../domain/javascript/javascriptApplicationAnalysis.js";
|
||||
import type { JavaScriptModuleArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js";
|
||||
@@ -37,7 +37,7 @@ export const collectElectronIpcRecords = (
|
||||
finding,
|
||||
})),
|
||||
)
|
||||
.sort((left, right) => compareCodePoints(left.key, right.key));
|
||||
.sort((left, right) => compareUnicodeCodePoints(left.key, right.key));
|
||||
|
||||
/** Match only exact literal channels and retain ambiguous matches separately. */
|
||||
export const classifyElectronIpcPairings = (
|
||||
|
||||
@@ -26,13 +26,11 @@ import {
|
||||
partialApplicationCoverage,
|
||||
} from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js";
|
||||
import type { JavaScriptSemanticResourceLimit } from "../../domain/javascript/javascriptSemanticValueTypes.js";
|
||||
import { semanticCoverageResourceLimits } from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import {
|
||||
SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
|
||||
semanticResourceLimitReason,
|
||||
} from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
||||
semanticCoverageResourceLimits,
|
||||
semanticResourceLimitCoverage,
|
||||
} from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import { semanticResourceLimitReason } from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
||||
import {
|
||||
addJavaScriptArtifactContainers,
|
||||
addJavaScriptArtifactFiles,
|
||||
@@ -138,7 +136,7 @@ const graphCoverage = (context: JavaScriptArtifactGraphContext) => {
|
||||
return partialApplicationCoverage([], null);
|
||||
if (resourceLimits.length > 0)
|
||||
return partialApplicationCoverage(
|
||||
resourceLimits.map(applicationGraphResourceLimit),
|
||||
semanticResourceLimitCoverage(resourceLimits),
|
||||
null,
|
||||
);
|
||||
if (unknownGap) return partialApplicationCoverage([], null);
|
||||
@@ -158,24 +156,6 @@ const semanticResourceLimits = (
|
||||
),
|
||||
].sort();
|
||||
|
||||
const applicationGraphResourceLimit = (
|
||||
resourceLimit: JavaScriptSemanticResourceLimit,
|
||||
) => ({
|
||||
name: `javascript_semantic_${resourceLimit.replaceAll("-", "_")}`,
|
||||
value:
|
||||
resourceLimit === "primitive-candidates"
|
||||
? SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT
|
||||
: resourceLimit === "primitive-bytes"
|
||||
? SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT
|
||||
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
unit:
|
||||
resourceLimit === "expression-depth"
|
||||
? ("depth" as const)
|
||||
: resourceLimit === "primitive-bytes"
|
||||
? ("bytes" as const)
|
||||
: ("items" as const),
|
||||
});
|
||||
|
||||
const graphLimitations = (
|
||||
context: JavaScriptArtifactGraphContext,
|
||||
coverage: "complete" | "partial" | "unknown" | "unavailable",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import type { JavaScriptJsonModuleObservation } from "./JavaScriptArtifactAnalysisTypes.js";
|
||||
import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js";
|
||||
|
||||
@@ -31,7 +31,7 @@ export const analyzeJavaScriptJsonModule = (
|
||||
}
|
||||
const keys =
|
||||
typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? Object.keys(value).sort(compareCodePoints)
|
||||
? Object.keys(value).sort(compareUnicodeCodePoints)
|
||||
: [];
|
||||
const retained = keys;
|
||||
return {
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { posix } from "node:path";
|
||||
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import type { ApplicationNode } from "../../domain/javascript/javascriptApplicationGraphSchemas.js";
|
||||
import {
|
||||
completeApplicationCoverage,
|
||||
partialApplicationCoverage,
|
||||
} from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js";
|
||||
import { semanticCoverageResourceLimits } from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import {
|
||||
SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
|
||||
semanticResourceLimitReason,
|
||||
} from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
||||
semanticCoverageResourceLimits,
|
||||
semanticResourceLimitCoverage,
|
||||
} from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import { semanticResourceLimitReason } from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
||||
import type {
|
||||
JavaScriptModuleOrigin,
|
||||
JavaScriptSemanticModuleLink,
|
||||
@@ -81,7 +79,7 @@ export const addJavaScriptSourceModules = (
|
||||
.flatMap(({ exportedName }) =>
|
||||
exportedName === null ? [] : [exportedName],
|
||||
)
|
||||
.sort(compareCodePoints);
|
||||
.sort(compareUnicodeCodePoints);
|
||||
const retainedExports = [...new Set(exports)];
|
||||
const source = context.accumulator.addNode({
|
||||
kind: "javascript-module",
|
||||
@@ -408,21 +406,7 @@ const semanticCoverage = (
|
||||
if (semantic.ir.coverage.status === "complete" && resourceLimits.length === 0)
|
||||
return completeApplicationCoverage();
|
||||
return partialApplicationCoverage(
|
||||
resourceLimits.map((resourceLimit) => ({
|
||||
name: `javascript_semantic_${resourceLimit.replaceAll("-", "_")}`,
|
||||
value:
|
||||
resourceLimit === "primitive-candidates"
|
||||
? SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT
|
||||
: resourceLimit === "primitive-bytes"
|
||||
? SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT
|
||||
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
unit:
|
||||
resourceLimit === "expression-depth"
|
||||
? ("depth" as const)
|
||||
: resourceLimit === "primitive-bytes"
|
||||
? ("bytes" as const)
|
||||
: ("items" as const),
|
||||
})),
|
||||
semanticResourceLimitCoverage(resourceLimits),
|
||||
semantic.ir.coverage.omittedCount,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraphSchemas.js";
|
||||
import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES } from "../../domain/javascript/javascriptSemanticGraphSchemas.js";
|
||||
import type { JavaScriptSemanticIr } from "../../domain/javascript/javascriptSemanticIr.js";
|
||||
@@ -65,7 +65,7 @@ export const createSemanticCallableOwnerLookup = (
|
||||
const rank =
|
||||
best === undefined
|
||||
? 1
|
||||
: compareCodePoints(candidate.priority, best.priority) ||
|
||||
: compareUnicodeCodePoints(candidate.priority, best.priority) ||
|
||||
best.order - candidate.order;
|
||||
if (node !== undefined && rank > 0) {
|
||||
best = candidate;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
semanticContainer,
|
||||
semanticPropertyPointer,
|
||||
type JavaScriptSemanticSlot,
|
||||
} from "../../domain/javascript/javascriptSemanticSlots.js";
|
||||
import type { JavaScriptSemanticBinding } from "../../domain/javascript/javascriptSemanticIr.js";
|
||||
import type { JavaScriptSemanticValue } from "../../domain/javascript/javascriptSemanticValueTypes.js";
|
||||
import type {
|
||||
JavaScriptSemanticProperty,
|
||||
JavaScriptSemanticValue,
|
||||
} from "../../domain/javascript/javascriptSemanticValueTypes.js";
|
||||
import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascript/javascriptSemanticGraph.js";
|
||||
import {
|
||||
retainSemanticGraphNode,
|
||||
@@ -155,7 +157,7 @@ export const semanticPropertySlot = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
objectBindingId: string,
|
||||
path: readonly string[],
|
||||
fact: JavaScriptSemanticSlot,
|
||||
fact: JavaScriptSemanticProperty,
|
||||
) =>
|
||||
retainSemanticGraphNode(context.state, context.file, {
|
||||
kind: "property-slot",
|
||||
|
||||
@@ -9,8 +9,6 @@ import { ARTIFACT_ANALYSIS_OPERATIONS } from "../contracts/artifactToolContracts
|
||||
export const ARTIFACT_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze(
|
||||
ARTIFACT_GRAPH_PROVIDER,
|
||||
);
|
||||
const IDENTITY = ARTIFACT_PROVIDER_IDENTITY;
|
||||
|
||||
/** Declare artifact coverage without constructing readers or acquiring resources. */
|
||||
export const artifactCapabilities = (
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
@@ -18,7 +16,7 @@ export const artifactCapabilities = (
|
||||
Object.freeze(
|
||||
ARTIFACT_ANALYSIS_OPERATIONS.map((operation) => {
|
||||
const common = {
|
||||
provider: IDENTITY,
|
||||
provider: ARTIFACT_PROVIDER_IDENTITY,
|
||||
operation,
|
||||
effects: Object.freeze({
|
||||
mutatesArtifact: false,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { lstat, open, opendir, realpath } from "node:fs/promises";
|
||||
import { isAbsolute, join, relative, sep } from "node:path";
|
||||
import type { Readable } from "node:stream";
|
||||
|
||||
import { compareCodePoints } from "../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
||||
|
||||
import {
|
||||
ArtifactReaderFailure,
|
||||
@@ -49,7 +49,7 @@ export class DirectoryArtifactReader implements ArtifactReader {
|
||||
await withDirectoryIoContext("inspect directory", directory, () =>
|
||||
assertContainedDirectory(root, directory),
|
||||
);
|
||||
children.sort(compareCodePoints);
|
||||
children.sort(compareUnicodeCodePoints);
|
||||
const directories: string[] = [];
|
||||
for (const name of children) {
|
||||
abortIfNeeded(signal);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { lstat, realpath } from "node:fs/promises";
|
||||
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { canonicalDigest } from "../../domain/comparisonSemantics.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import { digestCanonicalValue } from "../../domain/canonicalDigest.js";
|
||||
import { AsarArtifactReader } from "../AsarArtifactReader.js";
|
||||
import {
|
||||
ArtifactPathRegistry,
|
||||
@@ -178,7 +178,7 @@ const materializeSelection = async ({
|
||||
await reader.close();
|
||||
readerClosed = true;
|
||||
extracted.sort((left, right) =>
|
||||
compareCodePoints(left.relative_path, right.relative_path),
|
||||
compareUnicodeCodePoints(left.relative_path, right.relative_path),
|
||||
);
|
||||
const result = createExtractionResult(
|
||||
input,
|
||||
@@ -245,15 +245,15 @@ const createExtractionResult = (
|
||||
source_manifest_id: inventory.manifest.manifest_id,
|
||||
selected_occurrence_ids: selected
|
||||
.map(({ occurrence }) => occurrence.occurrence_id)
|
||||
.sort(compareCodePoints),
|
||||
files_sha256: canonicalDigest(extracted, "Artifact"),
|
||||
.sort(compareUnicodeCodePoints),
|
||||
files_sha256: digestCanonicalValue(extracted, "Artifact"),
|
||||
output_root_alias: "$OUTPUT_ROOT" as const,
|
||||
};
|
||||
return artifactExtractionResultSchema.parse({
|
||||
manifest: inventory.manifest,
|
||||
extraction_manifest: {
|
||||
...extractionSemantic,
|
||||
extraction_id: `aex_${canonicalDigest(extractionSemantic, "Artifact")}`,
|
||||
extraction_id: `aex_${digestCanonicalValue(extractionSemantic, "Artifact")}`,
|
||||
},
|
||||
output_root: input.outputRoot,
|
||||
artifacts: extracted,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { canonicalDigest } from "../../domain/comparisonSemantics.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import { digestCanonicalValue } from "../../domain/canonicalDigest.js";
|
||||
import {
|
||||
artifactEdgeId,
|
||||
artifactIdForContent,
|
||||
@@ -52,7 +52,7 @@ export const createOccurrence = (
|
||||
path: string,
|
||||
parent: string | null,
|
||||
): MutableOccurrence => ({
|
||||
occurrence_id: `occ_${canonicalDigest({ path, kind: entry.kind }, "Artifact")}`,
|
||||
occurrence_id: `occ_${digestCanonicalValue({ path, kind: entry.kind }, "Artifact")}`,
|
||||
artifact_id: null,
|
||||
parent_occurrence_id: parent,
|
||||
logical_path: path,
|
||||
@@ -80,8 +80,6 @@ export const createOccurrence = (
|
||||
});
|
||||
|
||||
/** Compare exact child names independently of host locale and traversal order. */
|
||||
export const compareDirectoryChildNames = compareCodePoints;
|
||||
|
||||
export const materializeDirectoryNodes = (
|
||||
occurrences: MutableOccurrence[],
|
||||
nodes: Map<string, ArtifactNode>,
|
||||
@@ -107,9 +105,9 @@ export const materializeDirectoryNodes = (
|
||||
artifact_id,
|
||||
entry_kind,
|
||||
}))
|
||||
.sort((left, right) => compareDirectoryChildNames(left.name, right.name));
|
||||
.sort((left, right) => compareUnicodeCodePoints(left.name, right.name));
|
||||
const node = createArtifactNode({
|
||||
sha256: canonicalDigest({ kind: "directory", children }, "Artifact"),
|
||||
sha256: digestCanonicalValue({ kind: "directory", children }, "Artifact"),
|
||||
size: 0,
|
||||
format: "directory",
|
||||
contentState: "virtual",
|
||||
@@ -131,7 +129,7 @@ export const createRootNode = (input: {
|
||||
createArtifactNode({
|
||||
sha256:
|
||||
input.digest?.sha256 ??
|
||||
canonicalDigest(
|
||||
digestCanonicalValue(
|
||||
{
|
||||
kind: "directory-root",
|
||||
children: input.occurrences
|
||||
@@ -140,7 +138,7 @@ export const createRootNode = (input: {
|
||||
artifact_id,
|
||||
}))
|
||||
.sort((left, right) =>
|
||||
compareDirectoryChildNames(left.logical_path, right.logical_path),
|
||||
compareUnicodeCodePoints(left.logical_path, right.logical_path),
|
||||
),
|
||||
},
|
||||
"Artifact",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySnapshot.js";
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
|
||||
import { createReadStream } from "node:fs";
|
||||
import { lstat } from "node:fs/promises";
|
||||
@@ -253,18 +253,20 @@ const verifyRootDigest = async (
|
||||
|
||||
const sortNodes = (nodes: ArtifactNode[]): ArtifactNode[] =>
|
||||
nodes.sort((left, right) =>
|
||||
compareCodePoints(left.artifact_id, right.artifact_id),
|
||||
compareUnicodeCodePoints(left.artifact_id, right.artifact_id),
|
||||
);
|
||||
|
||||
const sortOccurrences = (
|
||||
occurrences: MutableOccurrence[],
|
||||
): MutableOccurrence[] =>
|
||||
occurrences.sort((left, right) =>
|
||||
compareCodePoints(left.logical_path, right.logical_path),
|
||||
compareUnicodeCodePoints(left.logical_path, right.logical_path),
|
||||
);
|
||||
|
||||
const sortEdges = <T extends { edge_id: string }>(edges: T[]): T[] =>
|
||||
edges.sort((left, right) => compareCodePoints(left.edge_id, right.edge_id));
|
||||
edges.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
);
|
||||
|
||||
const buildManifest = ({
|
||||
rootNode,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
||||
import { isAbsolute } from "node:path";
|
||||
import type { Readable } from "node:stream";
|
||||
|
||||
import { compareCodePoints } from "../../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
||||
import { AsarArtifactReader } from "../AsarArtifactReader.js";
|
||||
import {
|
||||
ArtifactPathRegistry,
|
||||
@@ -84,13 +84,13 @@ export const readJavaScriptArtifactFiles = async (
|
||||
};
|
||||
await visitReader(reader, "", snapshot.manifest.root_sha256, context);
|
||||
const files = context.files.sort((left, right) =>
|
||||
compareCodePoints(left.path, right.path),
|
||||
compareUnicodeCodePoints(left.path, right.path),
|
||||
);
|
||||
assertExpectedFilesWereVisited(expected, files);
|
||||
return {
|
||||
files,
|
||||
containers: context.containers.sort((left, right) =>
|
||||
compareCodePoints(left.path, right.path),
|
||||
compareUnicodeCodePoints(left.path, right.path),
|
||||
),
|
||||
text_bytes_read: context.textBytes,
|
||||
invalid_utf8_files: context.invalidUtf8,
|
||||
|
||||
@@ -67,13 +67,12 @@ import { captureCdpScreenshot } from "./CdpScreenshot.js";
|
||||
import { comparePngScreenshots } from "./PngVisualDiff.js";
|
||||
|
||||
import { CDP_BROWSER_PROVIDER_IDENTITY } from "./providerIdentities.js";
|
||||
const IDENTITY = CDP_BROWSER_PROVIDER_IDENTITY;
|
||||
const CLEANUP_DOMAINS = ["Network", "Debugger", "Runtime", "Page"] as const;
|
||||
|
||||
/** Passive browser observation through a selected user-owned CDP endpoint. */
|
||||
export class CdpBrowserProvider implements BrowserObservationPort {
|
||||
identity(): ProviderIdentity {
|
||||
return IDENTITY;
|
||||
return CDP_BROWSER_PROVIDER_IDENTITY;
|
||||
}
|
||||
|
||||
async listTargets(
|
||||
@@ -440,4 +439,6 @@ const providerError = (
|
||||
? new BrowserObservationError(operation, cause.reason, { cause })
|
||||
: cause instanceof AnalysisError
|
||||
? cause
|
||||
: new ProviderAdapterError(IDENTITY.id, operation, { cause });
|
||||
: new ProviderAdapterError(CDP_BROWSER_PROVIDER_IDENTITY.id, operation, {
|
||||
cause,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import canonicalize from "canonicalize";
|
||||
|
||||
import type { InspectWebPageInput } from "../domain/browserObservation.js";
|
||||
import type { WebPageInspection } from "../domain/browserObservationSchemas.js";
|
||||
import { compareCodePoints } from "../domain/canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
||||
import { CdpConnection } from "./CdpConnection.js";
|
||||
import { optionalCdpCommand } from "./CdpOptionalCommand.js";
|
||||
import {
|
||||
@@ -85,7 +85,7 @@ export const captureStorageFingerprints = async ({
|
||||
await addIndexedDb(run, origin, indexedDbNames);
|
||||
await addCaches(run, caches);
|
||||
state.items.sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.scope}:${left.identity_sha256}`,
|
||||
`${right.scope}:${right.identity_sha256}`,
|
||||
),
|
||||
@@ -447,7 +447,7 @@ const stableValue = (value: unknown): unknown => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(record)
|
||||
.filter(([key]) => key !== "objectId" && key !== "customPreview")
|
||||
.sort(([left], [right]) => compareCodePoints(left, right))
|
||||
.sort(([left], [right]) => compareUnicodeCodePoints(left, right))
|
||||
.map(([key, item]) => [key, stableValue(item)]),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import type { ProgressReporter } from "../application/ProgressReporter.js";
|
||||
import type {
|
||||
ElectronPageInspection,
|
||||
@@ -289,7 +289,7 @@ const captureResources = async (
|
||||
? null
|
||||
: Math.max(0, numberValue(resource.contentSize) ?? 0),
|
||||
};
|
||||
const resourceKey = `electron_resource_${canonicalDigest(item, "CDP capture")}`;
|
||||
const resourceKey = `electron_resource_${digestCanonicalValue(item, "CDP capture")}`;
|
||||
if (seen.has(resourceKey)) continue;
|
||||
seen.add(resourceKey);
|
||||
resources.push({
|
||||
|
||||
@@ -30,12 +30,10 @@ import { inspectCdpElectronPage } from "./CdpElectronInspection.js";
|
||||
import { authorizedElectronFile } from "./ElectronFileScope.js";
|
||||
|
||||
import { CDP_ELECTRON_PROVIDER_IDENTITY } from "./providerIdentities.js";
|
||||
const IDENTITY = CDP_ELECTRON_PROVIDER_IDENTITY;
|
||||
|
||||
/** Passive Electron provider for local file pages exposed by loopback CDP. */
|
||||
export class CdpElectronProvider implements ElectronObservationPort {
|
||||
identity(): ProviderIdentity {
|
||||
return IDENTITY;
|
||||
return CDP_ELECTRON_PROVIDER_IDENTITY;
|
||||
}
|
||||
|
||||
async listTargets(
|
||||
@@ -169,4 +167,6 @@ const providerError = (
|
||||
? new BrowserObservationError(operation, cause.reason, { cause })
|
||||
: cause instanceof AnalysisError
|
||||
? cause
|
||||
: new ProviderAdapterError(IDENTITY.id, operation, { cause });
|
||||
: new ProviderAdapterError(CDP_ELECTRON_PROVIDER_IDENTITY.id, operation, {
|
||||
cause,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
ElectronPageInspection,
|
||||
InspectElectronPageInput,
|
||||
} from "../domain/javascript/electronObservation.js";
|
||||
import { canonicalDigest } from "../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import { createWebTextArtifact } from "../domain/webContentArtifact.js";
|
||||
import type { CdpConnection } from "./CdpConnection.js";
|
||||
import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js";
|
||||
@@ -49,7 +49,7 @@ export const captureElectronScripts = async (
|
||||
is_module: script.isModule,
|
||||
language: script.language,
|
||||
};
|
||||
const scriptKey = `electron_script_${canonicalDigest(identity, "CDP capture")}`;
|
||||
const scriptKey = `electron_script_${digestCanonicalValue(identity, "CDP capture")}`;
|
||||
if (seen.has(scriptKey)) continue;
|
||||
seen.add(scriptKey);
|
||||
total += 1;
|
||||
|
||||
@@ -22,10 +22,7 @@ export const registerUtilityCommands = (
|
||||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
environment: Readonly<Record<string, string | undefined>>,
|
||||
analysis: Pick<
|
||||
DirectAnalysis,
|
||||
"runProviderAnalysis" | "runProviderStatus" | "runCapabilityStatus"
|
||||
>,
|
||||
analysis: Pick<DirectAnalysis, "runProviderAnalysis" | "runSessionStatus">,
|
||||
): void => {
|
||||
const { runProviderAnalysis } = analysis;
|
||||
registerCapabilityCommands(cli, logger, analysis);
|
||||
@@ -129,10 +126,7 @@ const invalidNativeUiScenarioInput = (
|
||||
const registerCapabilityCommands = (
|
||||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
{
|
||||
runProviderStatus,
|
||||
runCapabilityStatus,
|
||||
}: Pick<DirectAnalysis, "runProviderStatus" | "runCapabilityStatus">,
|
||||
{ runSessionStatus }: Pick<DirectAnalysis, "runSessionStatus">,
|
||||
): void => {
|
||||
for (const command of [
|
||||
CLI_COMMANDS.capabilities,
|
||||
@@ -143,12 +137,7 @@ const registerCapabilityCommands = (
|
||||
command === "capabilities"
|
||||
? "List provider capabilities and side effects"
|
||||
: "List configured analysis providers",
|
||||
run: () =>
|
||||
logCliCommand(logger, command, () =>
|
||||
command === CLI_COMMANDS.providers
|
||||
? runProviderStatus(logger)
|
||||
: runCapabilityStatus(logger),
|
||||
),
|
||||
run: () => logCliCommand(logger, command, () => runSessionStatus(logger)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,10 +5,7 @@ import {
|
||||
runProviderAnalysis as executeProviderAnalysis,
|
||||
runManagedProviderExecution as executeManagedProvider,
|
||||
} from "../application/DirectAnalysis.js";
|
||||
import {
|
||||
runCapabilityStatus as executeCapabilityStatus,
|
||||
runProviderStatus as executeProviderStatus,
|
||||
} from "../application/DirectAnalysisStatus.js";
|
||||
import { runSessionStatus as executeSessionStatus } from "../application/DirectAnalysisStatus.js";
|
||||
import type { DirectAnalysisDependencies } from "../application/DirectAnalysisDependencies.js";
|
||||
import { createBinarySession, createManagedBinarySession } from "./binary.js";
|
||||
|
||||
@@ -26,8 +23,7 @@ export const createDirectAnalysis = (
|
||||
return {
|
||||
runDirectAnalysis: executeDirectAnalysis.bind(undefined, dependencies),
|
||||
runProviderAnalysis: executeProviderAnalysis.bind(undefined, dependencies),
|
||||
runProviderStatus: executeProviderStatus.bind(undefined, dependencies),
|
||||
runCapabilityStatus: executeCapabilityStatus.bind(undefined, dependencies),
|
||||
runSessionStatus: executeSessionStatus.bind(undefined, dependencies),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { canonicalDigest } from "../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
|
||||
const inventory = (digit: string) => {
|
||||
const sha = digit.repeat(64);
|
||||
const artifactId = `art_${canonicalDigest({ sha256: sha }, "Artifact example")}`;
|
||||
const occurrenceId = `occ_${canonicalDigest({ root: artifactId }, "Artifact example")}`;
|
||||
const artifactId = `art_${digestCanonicalValue({ sha256: sha }, "Artifact example")}`;
|
||||
const occurrenceId = `occ_${digestCanonicalValue({ root: artifactId }, "Artifact example")}`;
|
||||
const nodes = [
|
||||
{
|
||||
artifact_id: artifactId,
|
||||
@@ -36,7 +36,7 @@ const inventory = (digit: string) => {
|
||||
limitations: [],
|
||||
},
|
||||
];
|
||||
const graphSha256 = canonicalDigest(
|
||||
const graphSha256 = digestCanonicalValue(
|
||||
{
|
||||
nodes,
|
||||
occurrences,
|
||||
@@ -47,7 +47,7 @@ const inventory = (digit: string) => {
|
||||
);
|
||||
return jsonValueSchema.parse({
|
||||
manifest: {
|
||||
manifest_id: `agm_${canonicalDigest(
|
||||
manifest_id: `agm_${digestCanonicalValue(
|
||||
{
|
||||
root_artifact_id: artifactId,
|
||||
graph_sha256: graphSha256,
|
||||
|
||||
@@ -110,7 +110,8 @@ const semanticGraph = createJavaScriptSemanticGraph({
|
||||
limitations: ["Semantic relations are unavailable in this minimal example."],
|
||||
});
|
||||
|
||||
const staticEvidence = createEvidence(
|
||||
/** Minimal valid static application Evidence for graph workflow examples. */
|
||||
export const JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE = createEvidence(
|
||||
{ path: inputPath, sha256: applicationSha256, format: "directory" },
|
||||
{
|
||||
id: "rea-javascript-application",
|
||||
@@ -171,9 +172,6 @@ const staticEvidence = createEvidence(
|
||||
},
|
||||
);
|
||||
|
||||
/** Minimal valid static application Evidence for graph workflow examples. */
|
||||
export const JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE = staticEvidence;
|
||||
|
||||
const runtimeEvidence = createEvidence(
|
||||
undefined,
|
||||
{
|
||||
@@ -238,7 +236,7 @@ const runtimeEvidence = createEvidence(
|
||||
|
||||
/** Compact valid Evidence pair used by the public reconciliation contract. */
|
||||
export const JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE = {
|
||||
static_layers: [{ analysis: staticEvidence }],
|
||||
static_layers: [{ analysis: JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE }],
|
||||
runtime_observations: [runtimeEvidence],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import type { ToolContract } from "../toolContractTypes.js";
|
||||
import { managedOutputSchemas } from "../toolOutputSchemaGroups.js";
|
||||
import { toolContractMetadata } from "../toolEffects.js";
|
||||
import { requireOutputSchema } from "../toolOutputSchemaPrimitives.js";
|
||||
import {
|
||||
isAbsoluteLocalPath,
|
||||
localPathStringSchema,
|
||||
} from "../../domain/localPath.js";
|
||||
import { localPathStringSchema } from "../../domain/localPath.js";
|
||||
|
||||
/** Exact caller boundary for execution-free PE/CLI triage and identity. */
|
||||
export const managedTargetInputSchema = z.strictObject({
|
||||
path: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"path must be an absolute local filesystem path (for example /tmp/Example.dll or C:\\Example.dll)",
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { canonicalDigest } from "../domain/comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
|
||||
const inventory = (format: "apk" | "ipa" | "directory", digit: string) => {
|
||||
const sha = digit.repeat(64);
|
||||
const artifactId = `art_${canonicalDigest({ sha256: sha }, "Artifact inventory")}`;
|
||||
const occurrenceId = `occ_${canonicalDigest(
|
||||
const artifactId = `art_${digestCanonicalValue({ sha256: sha }, "Artifact inventory")}`;
|
||||
const occurrenceId = `occ_${digestCanonicalValue(
|
||||
{ root: artifactId },
|
||||
"Artifact inventory",
|
||||
)}`;
|
||||
@@ -39,7 +39,7 @@ const inventory = (format: "apk" | "ipa" | "directory", digit: string) => {
|
||||
limitations: [],
|
||||
},
|
||||
];
|
||||
const graphSha256 = canonicalDigest(
|
||||
const graphSha256 = digestCanonicalValue(
|
||||
{
|
||||
nodes,
|
||||
occurrences,
|
||||
@@ -50,7 +50,7 @@ const inventory = (format: "apk" | "ipa" | "directory", digit: string) => {
|
||||
);
|
||||
return jsonValueSchema.parse({
|
||||
manifest: {
|
||||
manifest_id: `agm_${canonicalDigest(
|
||||
manifest_id: `agm_${digestCanonicalValue(
|
||||
{
|
||||
root_artifact_id: artifactId,
|
||||
graph_sha256: graphSha256,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { isAbsolute } from "node:path";
|
||||
import { executableFormatHintSchema } from "../domain/dosCom.js";
|
||||
import {
|
||||
isAbsoluteLocalPath,
|
||||
localPathStringSchema,
|
||||
} from "../domain/localPath.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { z } from "zod";
|
||||
import { analysisProviderSelectorSchema } from "./providerSelection.js";
|
||||
|
||||
/** Caller-supplied analysis snapshot path; must name its destination absolutely. */
|
||||
const snapshotPathSchema = localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"snapshot_path must be an absolute local filesystem path (for example /tmp/rea/analysis.json or C:\\rea\\analysis.json)",
|
||||
})
|
||||
@@ -18,7 +16,7 @@ const snapshotPathSchema = localPathStringSchema
|
||||
|
||||
/** Caller-supplied analysis target path; must name the target absolutely. */
|
||||
const binaryTargetPathSchema = localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"path must be an absolute local filesystem path (for example /tmp/fixture.bin or C:\\analysis\\fixture.bin)",
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import {
|
||||
isAbsoluteLocalPath,
|
||||
localPathStringSchema,
|
||||
} from "../domain/localPath.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
|
||||
/** Optional expectations used to compare the live session with its caller. */
|
||||
export const binarySessionInputSchema = z.strictObject({
|
||||
@@ -13,7 +11,7 @@ export const binarySessionInputSchema = z.strictObject({
|
||||
.regex(/^[a-f0-9]{64}$/u)
|
||||
.optional(),
|
||||
expected_server_path: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"expected_server_path must be an absolute local filesystem path (for example /opt/rea/dist/main.js)",
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
isAbsoluteLocalPath,
|
||||
localPathStringSchema,
|
||||
} from "../domain/localPath.js";
|
||||
import { isAbsolute } from "node:path";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
import { artifactComparisonInputSchema } from "../domain/artifactComparison.js";
|
||||
import { bundleComparisonInputSchema } from "../domain/bundleComparison.js";
|
||||
import { callPathInputSchema } from "../domain/callPathSchemas.js";
|
||||
@@ -57,7 +55,7 @@ const session = <
|
||||
/** Session-owned Evidence bundle export options. */
|
||||
export const exportEvidenceBundleInputSchema = z.strictObject({
|
||||
path: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"path must be an absolute local filesystem path (for example /tmp/rea/evidence.json or C:\\rea\\evidence.json)",
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import {
|
||||
isAbsoluteLocalPath,
|
||||
localPathStringSchema,
|
||||
} from "../domain/localPath.js";
|
||||
import { localPathStringSchema } from "../domain/localPath.js";
|
||||
|
||||
import { processTraceSpecificationSchema } from "../domain/process/processTraceSpecification.js";
|
||||
import { evidenceSchema } from "../domain/evidence.js";
|
||||
@@ -26,7 +24,7 @@ export const addressContextInputSchema = z.strictObject({
|
||||
/** Session-owned Evidence bundle import options. */
|
||||
export const importEvidenceBundleInputSchema = z.strictObject({
|
||||
path: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"path must be an absolute local filesystem path (for example /tmp/evidence.json or C:\\rea\\evidence.json)",
|
||||
})
|
||||
|
||||
@@ -3,8 +3,8 @@ import { createHash } from "node:crypto";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { projectAppleApplication } from "./appleApplication.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { createEvidence, type Evidence } from "../evidence.js";
|
||||
import { jsonValueSchema } from "../jsonValue.js";
|
||||
|
||||
@@ -20,7 +20,7 @@ interface FixtureEntry {
|
||||
const sha = (text: string): string =>
|
||||
createHash("sha256").update(text).digest("hex");
|
||||
const artifactId = (sha256: string): string =>
|
||||
`art_${canonicalDigest({ sha256 }, "Artifact inventory")}`;
|
||||
`art_${digestCanonicalValue({ sha256 }, "Artifact inventory")}`;
|
||||
|
||||
/** Build one complete, content-addressed inventory page for pure projection tests. */
|
||||
const inventoryEvidence = (
|
||||
@@ -57,8 +57,8 @@ const inventoryEvidence = (
|
||||
) => ({
|
||||
occurrence_id:
|
||||
path === "."
|
||||
? `occ_${canonicalDigest({ root: rootId }, "Artifact inventory")}`
|
||||
: `occ_${canonicalDigest(
|
||||
? `occ_${digestCanonicalValue({ root: rootId }, "Artifact inventory")}`
|
||||
: `occ_${digestCanonicalValue(
|
||||
{ root_artifact_id: rootId, logical_path: path, entry_kind: kind },
|
||||
"Artifact inventory",
|
||||
)}`,
|
||||
@@ -97,9 +97,9 @@ const inventoryEvidence = (
|
||||
left.artifact_id.localeCompare(right.artifact_id),
|
||||
);
|
||||
const sortedOccurrences = occurrences.sort((left, right) =>
|
||||
compareCodePoints(left.logical_path, right.logical_path),
|
||||
compareUnicodeCodePoints(left.logical_path, right.logical_path),
|
||||
);
|
||||
const graphSha256 = canonicalDigest(
|
||||
const graphSha256 = digestCanonicalValue(
|
||||
{
|
||||
nodes: sortedNodes,
|
||||
occurrences: sortedOccurrences,
|
||||
@@ -122,7 +122,7 @@ const inventoryEvidence = (
|
||||
authority: "shipped-artifact",
|
||||
result: jsonValueSchema.parse({
|
||||
manifest: {
|
||||
manifest_id: `agm_${canonicalDigest(
|
||||
manifest_id: `agm_${digestCanonicalValue(
|
||||
{ root_artifact_id: rootId, graph_sha256: graphSha256 },
|
||||
"Artifact inventory",
|
||||
)}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
@@ -147,7 +147,7 @@ export const compareArtifacts = (
|
||||
right_manifest_id: right.inventory.manifest.manifest_id,
|
||||
summary,
|
||||
changes,
|
||||
limitations: [...new Set(limitations)].sort(compareCodePoints),
|
||||
limitations: [...new Set(limitations)].sort(compareUnicodeCodePoints),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -171,7 +171,7 @@ const compareOccurrences = (
|
||||
const rightRelations = relationsByPath(right);
|
||||
const paths = [
|
||||
...new Set([...leftByPath.keys(), ...rightByPath.keys()]),
|
||||
].sort(compareCodePoints);
|
||||
].sort(compareUnicodeCodePoints);
|
||||
const output: ArtifactChange[] = [];
|
||||
for (const path of paths) {
|
||||
const leftOccurrence = leftByPath.get(path);
|
||||
@@ -370,7 +370,7 @@ const relationsByPath = (
|
||||
}
|
||||
for (const values of output.values())
|
||||
values.sort((left, right) =>
|
||||
compareCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
compareUnicodeCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
);
|
||||
return output;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "./comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
import type {
|
||||
ArtifactEdge,
|
||||
ArtifactNode,
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
|
||||
/** Content-address an artifact independently of where it was observed. */
|
||||
export const artifactIdForContent = (sha256: string): string =>
|
||||
`art_${canonicalDigest({ sha256 }, "Artifact")}`;
|
||||
`art_${digestCanonicalValue({ sha256 }, "Artifact")}`;
|
||||
|
||||
/** Identify an occurrence by its root and exact logical location. */
|
||||
export const occurrenceIdForLocation = (input: {
|
||||
@@ -17,8 +17,8 @@ export const occurrenceIdForLocation = (input: {
|
||||
readonly entryKind: ArtifactOccurrence["entry_kind"];
|
||||
}): string =>
|
||||
input.logicalPath === "."
|
||||
? `occ_${canonicalDigest({ root: input.rootArtifactId }, "Artifact")}`
|
||||
: `occ_${canonicalDigest(
|
||||
? `occ_${digestCanonicalValue({ root: input.rootArtifactId }, "Artifact")}`
|
||||
: `occ_${digestCanonicalValue(
|
||||
{
|
||||
root_artifact_id: input.rootArtifactId,
|
||||
logical_path: input.logicalPath,
|
||||
@@ -34,7 +34,7 @@ export const artifactGraphDigest = (input: {
|
||||
readonly edges: readonly ArtifactEdge[];
|
||||
readonly contradictions: readonly IntegrityContradiction[];
|
||||
}): string =>
|
||||
canonicalDigest(
|
||||
digestCanonicalValue(
|
||||
{
|
||||
nodes: input.nodes,
|
||||
occurrences: input.occurrences,
|
||||
@@ -49,7 +49,7 @@ export const artifactManifestId = (
|
||||
rootArtifactId: string,
|
||||
graphSha256: string,
|
||||
): string =>
|
||||
`agm_${canonicalDigest(
|
||||
`agm_${digestCanonicalValue(
|
||||
{ root_artifact_id: rootArtifactId, graph_sha256: graphSha256 },
|
||||
"Artifact",
|
||||
)}`;
|
||||
@@ -61,7 +61,7 @@ export const artifactContradictionId = (input: {
|
||||
readonly declaredSha256: string;
|
||||
readonly observedSha256: string;
|
||||
}): string =>
|
||||
`ic_${canonicalDigest(
|
||||
`ic_${digestCanonicalValue(
|
||||
{
|
||||
root_artifact_id: input.rootArtifactId,
|
||||
logical_path: input.logicalPath,
|
||||
@@ -81,4 +81,4 @@ export const artifactEdgeId = (
|
||||
| "occurrence_id"
|
||||
| "logical_path"
|
||||
>,
|
||||
): string => `edge_${canonicalDigest(semantic, "Artifact")}`;
|
||||
): string => `edge_${digestCanonicalValue(semantic, "Artifact")}`;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
artifactInventoryResultSchema,
|
||||
type ArtifactInventoryResult,
|
||||
} from "./artifactGraph.js";
|
||||
import { canonicalDigest } from "./comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
import { uniqueSorted } from "./canonicalOrdering.js";
|
||||
import { evidenceSchema, parseEvidence, type Evidence } from "./evidence.js";
|
||||
import { jsonValueSchema } from "./jsonValue.js";
|
||||
@@ -170,7 +170,7 @@ export const createArtifactInspection = (
|
||||
},
|
||||
substeps: [
|
||||
{
|
||||
substep_id: `ais_${canonicalDigest(
|
||||
substep_id: `ais_${digestCanonicalValue(
|
||||
{
|
||||
operation: "inventory_artifact",
|
||||
evidence_id: inventoryEvidence.evidence_id,
|
||||
@@ -211,7 +211,7 @@ export const createArtifactInspection = (
|
||||
};
|
||||
return artifactInspectionResultSchema.parse({
|
||||
...semantic,
|
||||
inspection_id: `ai_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
inspection_id: `ai_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -258,7 +258,7 @@ const observation = (
|
||||
evidence_id: evidenceId,
|
||||
};
|
||||
return {
|
||||
observation_id: `aio_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
observation_id: `aio_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
...semantic,
|
||||
};
|
||||
};
|
||||
@@ -277,7 +277,7 @@ const allRelationships = (
|
||||
evidence_id: evidenceId,
|
||||
};
|
||||
return {
|
||||
relationship_id: `air_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
relationship_id: `air_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
...semantic,
|
||||
};
|
||||
});
|
||||
@@ -305,7 +305,7 @@ const allHypotheses = (
|
||||
"Format classification proposes a bounded follow-up; it is not semantic proof.",
|
||||
};
|
||||
return {
|
||||
hypothesis_id: `aih_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
hypothesis_id: `aih_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
...semantic,
|
||||
};
|
||||
});
|
||||
@@ -324,7 +324,7 @@ const contradictions = (
|
||||
evidence_id: evidenceId,
|
||||
};
|
||||
return {
|
||||
contradiction_id: `aic_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
contradiction_id: `aic_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
...semantic,
|
||||
};
|
||||
});
|
||||
@@ -418,7 +418,7 @@ const branch = (
|
||||
evidence_id: evidenceId,
|
||||
};
|
||||
return {
|
||||
branch_id: `aib_${canonicalDigest(semantic, "Artifact inspection")}`,
|
||||
branch_id: `aib_${digestCanonicalValue(semantic, "Artifact inspection")}`,
|
||||
...semantic,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type ArtifactOccurrence,
|
||||
type IntegrityContradiction,
|
||||
} from "./artifactGraph.js";
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
import { canonicalJson } from "./comparisonSemantics.js";
|
||||
import {
|
||||
artifactContradictionId,
|
||||
@@ -163,13 +163,13 @@ const orderedInventory = (
|
||||
},
|
||||
): InventorySet => {
|
||||
const nodes = [...values.nodes.values()].sort((left, right) =>
|
||||
compareCodePoints(left.artifact_id, right.artifact_id),
|
||||
compareUnicodeCodePoints(left.artifact_id, right.artifact_id),
|
||||
);
|
||||
const occurrences = [...values.occurrences.values()].sort((left, right) =>
|
||||
compareCodePoints(left.logical_path, right.logical_path),
|
||||
compareUnicodeCodePoints(left.logical_path, right.logical_path),
|
||||
);
|
||||
const edges = [...values.edges.values()].sort((left, right) =>
|
||||
compareCodePoints(left.edge_id, right.edge_id),
|
||||
compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
);
|
||||
return {
|
||||
manifest: first.manifest,
|
||||
@@ -179,7 +179,7 @@ const orderedInventory = (
|
||||
integrityContradictions: first.integrity_contradictions,
|
||||
limitations: [
|
||||
...new Set(pages.flatMap(({ inventory }) => inventory.limitations)),
|
||||
].sort(compareCodePoints),
|
||||
].sort(compareUnicodeCodePoints),
|
||||
complete:
|
||||
nodes.length === first.manifest.node_count &&
|
||||
occurrences.length === first.manifest.occurrence_count &&
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "./comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
|
||||
import type {
|
||||
BrowserScenarioArtifactKind,
|
||||
@@ -30,7 +30,7 @@ export const commitBrowserScenarioNormalization = (
|
||||
const commitment = { built_in_rules: [...BUILT_IN_RULES], rules };
|
||||
return {
|
||||
...commitment,
|
||||
sha256: canonicalDigest(commitment, "Browser scenario"),
|
||||
sha256: digestCanonicalValue(commitment, "Browser scenario"),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ export const digestNormalizedScenarioValue = (
|
||||
artifact: BrowserScenarioArtifactKind,
|
||||
rules: readonly BrowserScenarioNormalizationRule[],
|
||||
): string =>
|
||||
canonicalDigest(normalize(value, artifact, rules), "Browser scenario");
|
||||
digestCanonicalValue(normalize(value, artifact, rules), "Browser scenario");
|
||||
|
||||
const normalize = (
|
||||
value: unknown,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import { IDENTIFIER_PATTERN } from "./stringPatterns.js";
|
||||
|
||||
import { isAbsoluteLocalPath, localPathStringSchema } from "./localPath.js";
|
||||
import { localPathStringSchema } from "./localPath.js";
|
||||
import { browserNetworkContentSelectionSchema } from "./browserNetworkEvidence.js";
|
||||
|
||||
import {
|
||||
@@ -12,13 +13,10 @@ import {
|
||||
|
||||
export const scenarioIdentifierSchema = z.string().regex(IDENTIFIER_PATTERN);
|
||||
|
||||
const browserExecutablePathSchema = localPathStringSchema.refine(
|
||||
isAbsoluteLocalPath,
|
||||
{
|
||||
message:
|
||||
"executable_path must be an absolute local filesystem path (for example /opt/chromium/chrome or C:\\chromium\\chrome.exe)",
|
||||
},
|
||||
);
|
||||
const browserExecutablePathSchema = localPathStringSchema.refine(isAbsolute, {
|
||||
message:
|
||||
"executable_path must be an absolute local filesystem path (for example /opt/chromium/chrome or C:\\chromium\\chrome.exe)",
|
||||
});
|
||||
|
||||
const browserScenarioBaseUrlSchema = z
|
||||
.string()
|
||||
|
||||
@@ -157,9 +157,9 @@ const parseSnapshots = (
|
||||
const snapshots = new Map<string, FunctionSnapshot>();
|
||||
for (const group of groups) {
|
||||
const snapshot = parseFunctionEvidence(group);
|
||||
const address = normalizeAddress(snapshot.dossier.procedure.address);
|
||||
const address = parseCallPathAddress(snapshot.dossier.procedure.address);
|
||||
for (const callee of snapshot.dossier.callees)
|
||||
normalizeAddress(callee.address);
|
||||
parseCallPathAddress(callee.address);
|
||||
if (snapshots.has(address))
|
||||
throw new TypeError(`Duplicate function Evidence for ${address}`);
|
||||
snapshots.set(address, snapshot);
|
||||
@@ -196,7 +196,7 @@ const createGraph = (
|
||||
for (const [address, snapshot] of snapshots) {
|
||||
graph.mergeNode(address);
|
||||
for (const callee of snapshot.dossier.callees) {
|
||||
const calleeAddress = normalizeAddress(callee.address);
|
||||
const calleeAddress = parseCallPathAddress(callee.address);
|
||||
graph.mergeNode(calleeAddress);
|
||||
graph.mergeDirectedEdge(address, calleeAddress);
|
||||
}
|
||||
@@ -371,5 +371,3 @@ const uniqueEvidence = (snapshots: Iterable<FunctionSnapshot>): string[] =>
|
||||
[
|
||||
...new Set([...snapshots].flatMap((snapshot) => snapshotLinks(snapshot))),
|
||||
].sort((left, right) => left.localeCompare(right));
|
||||
|
||||
const normalizeAddress = (input: string): string => parseCallPathAddress(input);
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
|
||||
/**
|
||||
* Canonical code-point ordering shared across domain and application layers.
|
||||
*
|
||||
* Compares strings by Unicode code point so graph ordering, fingerprints, and
|
||||
* deterministic tie-breaks stay stable regardless of locale. Prefer this over
|
||||
* `String.prototype.localeCompare`, which is locale-dependent.
|
||||
*
|
||||
* Ordering by code point differs from ordering by UTF-16 code unit for astral
|
||||
* code points (above U+FFFF), whose lead surrogate is numerically below the
|
||||
* BMP tail U+E000-U+FFFF.
|
||||
*/
|
||||
export const compareCodePoints = (left: string, right: string): number =>
|
||||
compareUnicodeCodePoints(left, right);
|
||||
|
||||
/** Deduplicate and sort strings with canonical code-point ordering. */
|
||||
export const uniqueSorted = <Value extends string>(
|
||||
values: readonly Value[],
|
||||
): Value[] => [...new Set(values)].sort(compareCodePoints);
|
||||
): Value[] => [...new Set(values)].sort(compareUnicodeCodePoints);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import canonicalize from "canonicalize";
|
||||
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
|
||||
/** Whether an inventory can support a claim that an item is absent. */
|
||||
export const absenceClaimable = (coverage: {
|
||||
readonly status: string;
|
||||
@@ -12,14 +10,6 @@ export const absenceClaimable = (coverage: {
|
||||
coverage.truncated !== true &&
|
||||
(coverage.omitted_count === undefined || coverage.omitted_count === 0);
|
||||
|
||||
/** SHA-256 of canonical JSON for stable, provider-neutral comparison IDs. */
|
||||
export const canonicalDigest = (
|
||||
value: unknown,
|
||||
context = "Comparison",
|
||||
): string => {
|
||||
return digestCanonicalValue(value, context);
|
||||
};
|
||||
|
||||
/** Canonical JSON used for deterministic tie breaking and stable digests. */
|
||||
export const canonicalJson = (
|
||||
value: unknown,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
import { digestSchema } from "../digests.js";
|
||||
import { jsonValueSchema } from "../jsonValue.js";
|
||||
import { isAbsoluteLocalPath, localPathStringSchema } from "../localPath.js";
|
||||
import { localPathStringSchema } from "../localPath.js";
|
||||
|
||||
const bytes = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
||||
const range = z.strictObject({ offset: bytes, length: bytes.positive() });
|
||||
const target = {
|
||||
path: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message:
|
||||
"path must be an absolute local filesystem path (for example /tmp/firmware.bin or C:\\firmware\\firmware.bin)",
|
||||
})
|
||||
@@ -22,7 +23,7 @@ export const firmwareInputSchemas = {
|
||||
extract_firmware: z.strictObject({
|
||||
...target,
|
||||
output_directory: localPathStringSchema
|
||||
.refine(isAbsoluteLocalPath, {
|
||||
.refine(isAbsolute, {
|
||||
message: "output_directory must be an absolute local filesystem path",
|
||||
})
|
||||
.describe("Absolute, absent output directory; parent must exist"),
|
||||
|
||||
@@ -5,8 +5,9 @@ import { enhancedInputSchemas } from "../contracts/enhancedInputs.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import { createEvidence, type Evidence } from "./evidence.js";
|
||||
import { functionDossierSchema } from "./hopperValues.js";
|
||||
import { canonicalDigest, canonicalJson } from "./comparisonSemantics.js";
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
import { canonicalJson } from "./comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
import { jsonValueSchema } from "./jsonValue.js";
|
||||
|
||||
import { compareFunctions } from "./functionComparison.js";
|
||||
@@ -228,7 +229,7 @@ describe("function collection collation ties", () => {
|
||||
const canonicalProjection = callees
|
||||
.map(({ name }) => ({ direction: "out", name }))
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
compareUnicodeCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
);
|
||||
const result = compareFunctions(
|
||||
observe("b", calling(callees)),
|
||||
@@ -239,8 +240,8 @@ describe("function collection collation ties", () => {
|
||||
);
|
||||
expect(calls).toMatchObject({
|
||||
status: "unchanged",
|
||||
left_digest: canonicalDigest(canonicalProjection),
|
||||
right_digest: canonicalDigest(canonicalProjection),
|
||||
left_digest: digestCanonicalValue(canonicalProjection),
|
||||
right_digest: digestCanonicalValue(canonicalProjection),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { canonicalJson } from "./comparisonSemantics.js";
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
|
||||
import type { FunctionSnapshot } from "./functionDossierEvidence.js";
|
||||
import type { FunctionComparisonResult } from "./functionComparisonSchemas.js";
|
||||
@@ -179,7 +179,7 @@ export const sorted = (values: readonly unknown[]): readonly unknown[] =>
|
||||
value,
|
||||
json: canonicalJson(value, "Function normalization"),
|
||||
}))
|
||||
.sort((left, right) => compareCodePoints(left.json, right.json))
|
||||
.sort((left, right) => compareUnicodeCodePoints(left.json, right.json))
|
||||
.map(({ value }) => value);
|
||||
|
||||
export const isAutoName = (name: string): boolean =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "./comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "./canonicalDigest.js";
|
||||
import type {
|
||||
DimensionName,
|
||||
FunctionComparisonResult,
|
||||
@@ -87,4 +87,4 @@ export const summarize = (dimensions: readonly FunctionDimension[]) => ({
|
||||
});
|
||||
|
||||
const digest = (value: unknown): string =>
|
||||
canonicalDigest(value, "Function comparison");
|
||||
digestCanonicalValue(value, "Function comparison");
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import { isAbsoluteLocalPath, localPathStringSchema } from "../localPath.js";
|
||||
import { localPathStringSchema } from "../localPath.js";
|
||||
|
||||
const pathInputSchema = localPathStringSchema.refine(isAbsoluteLocalPath, {
|
||||
const pathInputSchema = localPathStringSchema.refine(isAbsolute, {
|
||||
message:
|
||||
"Electron executable, application, and root paths must be absolute local filesystem paths (for example /Applications/Electron.app/Contents/MacOS/Electron)",
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
electronStaticValue,
|
||||
objectProperty,
|
||||
} from "./electronStaticAnalysisValues.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { addLocatedFinding } from "./javascriptStaticAnalysisFindings.js";
|
||||
import {
|
||||
argumentNode,
|
||||
@@ -150,7 +150,9 @@ const collectWebPreferences = (
|
||||
if (value.status === "dynamic") unknown += 1;
|
||||
preferences.push({ name, value });
|
||||
}
|
||||
preferences.sort((left, right) => compareCodePoints(left.name, right.name));
|
||||
preferences.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.name, right.name),
|
||||
);
|
||||
return {
|
||||
status: "object-literal",
|
||||
preferences,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { stripQueryAndFragment } from "../artifactPathSyntax.js";
|
||||
import type { ElectronNativeAddonBindingFinding } from "./electronStaticAnalysisTypes.js";
|
||||
import { addLocatedFinding } from "./javascriptStaticAnalysisFindings.js";
|
||||
@@ -137,7 +137,7 @@ const addBinding = (input: NativeBindingInput): void => {
|
||||
const { context, node, specifier, kind } = input;
|
||||
const unique = [
|
||||
...new Set(input.members.filter((member) => member !== "")),
|
||||
].sort(compareCodePoints);
|
||||
].sort(compareUnicodeCodePoints);
|
||||
const members = unique.length === 0 ? ["*"] : unique;
|
||||
addLocatedFinding(context, {
|
||||
collection: context.accumulator.nativeAddonBindings,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ElectronStaticValue } from "./electronStaticAnalysisTypes.js";
|
||||
import {
|
||||
readExactJavaScriptLiteral,
|
||||
@@ -105,7 +105,7 @@ export const collectContextBridgeMembers = (
|
||||
unknown: 0,
|
||||
};
|
||||
collectMembersAt(node, "", 0, state);
|
||||
const members = [...new Set(state.members)].sort(compareCodePoints);
|
||||
const members = [...new Set(state.members)].sort(compareUnicodeCodePoints);
|
||||
return {
|
||||
status: "object-literal",
|
||||
members,
|
||||
|
||||
@@ -9,7 +9,8 @@ import type {
|
||||
ApplicationNode,
|
||||
} from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ApplicationVersionComparisonItem } from "./javascriptApplicationVersionComparisonSchemas.js";
|
||||
|
||||
interface ChangeGraphInput {
|
||||
@@ -218,7 +219,7 @@ const sourceCoverageLimits = (input: ChangeGraphInput) =>
|
||||
(limit) => [`${limit.name}\0${limit.value}\0${limit.unit}`, limit],
|
||||
),
|
||||
).values(),
|
||||
].sort((left, right) => compareCodePoints(left.name, right.name));
|
||||
].sort((left, right) => compareUnicodeCodePoints(left.name, right.name));
|
||||
|
||||
const combineOmittedCounts = (left: number | null, right: number | null) =>
|
||||
left === null || right === null ? null : left + right;
|
||||
@@ -238,7 +239,7 @@ const mergeNodes = (nodes: readonly ApplicationNode[]): ApplicationNode[] => {
|
||||
.map((observation) => [observation.observation_id, observation]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(left.observation_id, right.observation_id),
|
||||
compareUnicodeCodePoints(left.observation_id, right.observation_id),
|
||||
);
|
||||
return createJavaScriptApplicationNode({
|
||||
kind: first.kind,
|
||||
@@ -250,11 +251,11 @@ const mergeNodes = (nodes: readonly ApplicationNode[]): ApplicationNode[] => {
|
||||
});
|
||||
});
|
||||
return merged.sort((left, right) =>
|
||||
compareCodePoints(left.node_id, right.node_id),
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
);
|
||||
};
|
||||
|
||||
const uniqueEdges = (edges: readonly ApplicationEdge[]): ApplicationEdge[] =>
|
||||
[...new Map(edges.map((edge) => [edge.edge_id, edge])).values()].sort(
|
||||
(left, right) => compareCodePoints(left.edge_id, right.edge_id),
|
||||
(left, right) => compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { canonicalDigest, canonicalJson } from "../comparisonSemantics.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { canonicalJson } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { freezeOwnedJsonSnapshotSteps } from "../immutableJson.js";
|
||||
|
||||
import type {
|
||||
@@ -37,7 +39,7 @@ const normalizeLimits = (
|
||||
limits: ApplicationGraphEvidence["coverage"]["limits"],
|
||||
): ApplicationGraphEvidence["coverage"]["limits"] =>
|
||||
[...new Map(limits.map((limit) => [limitKey(limit), limit])).values()].sort(
|
||||
(left, right) => compareCodePoints(limitKey(left), limitKey(right)),
|
||||
(left, right) => compareUnicodeCodePoints(limitKey(left), limitKey(right)),
|
||||
);
|
||||
|
||||
const normalizeEvidence = (
|
||||
@@ -65,7 +67,7 @@ const nodeSemantic = (node: Pick<ApplicationNode, "kind" | "identity">) => ({
|
||||
});
|
||||
|
||||
const nodeId = (node: Pick<ApplicationNode, "kind" | "identity">): string =>
|
||||
`jag_node_${canonicalDigest(nodeSemantic(node), "JavaScript Application Graph")}`;
|
||||
`jag_node_${digestCanonicalValue(nodeSemantic(node), "JavaScript Application Graph")}`;
|
||||
|
||||
const observationSemantic = (
|
||||
nodeIdentifier: string,
|
||||
@@ -82,14 +84,14 @@ const observationId = (
|
||||
"observation_id"
|
||||
>,
|
||||
): string =>
|
||||
`jag_observation_${canonicalDigest(
|
||||
`jag_observation_${digestCanonicalValue(
|
||||
observationSemantic(nodeIdentifier, observation),
|
||||
)}`;
|
||||
|
||||
type EdgeSemantic = Omit<ApplicationEdge, "edge_id">;
|
||||
|
||||
const edgeId = (edge: EdgeSemantic): string =>
|
||||
`jag_edge_${canonicalDigest(edge, "JavaScript Application Graph")}`;
|
||||
`jag_edge_${digestCanonicalValue(edge, "JavaScript Application Graph")}`;
|
||||
|
||||
const sortedUniqueIssue = (
|
||||
values: readonly string[],
|
||||
@@ -98,7 +100,9 @@ const sortedUniqueIssue = (
|
||||
context: z.RefinementCtx,
|
||||
): void => {
|
||||
for (let index = 1; index < values.length; index += 1) {
|
||||
if (compareCodePoints(values[index - 1] ?? "", values[index] ?? "") < 0)
|
||||
if (
|
||||
compareUnicodeCodePoints(values[index - 1] ?? "", values[index] ?? "") < 0
|
||||
)
|
||||
continue;
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -394,7 +398,7 @@ const checkGraphInvariants = (
|
||||
const { graph_id: identifier, ...semantic } = graph;
|
||||
if (
|
||||
identifier !==
|
||||
`jag_${canonicalDigest(semantic, "JavaScript Application Graph")}`
|
||||
`jag_${digestCanonicalValue(semantic, "JavaScript Application Graph")}`
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -432,7 +436,7 @@ export const createJavaScriptApplicationNode = (
|
||||
});
|
||||
})
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(left.observation_id, right.observation_id),
|
||||
compareUnicodeCodePoints(left.observation_id, right.observation_id),
|
||||
);
|
||||
return applicationNodeSchema.parse({
|
||||
node_id: identifier,
|
||||
@@ -467,10 +471,10 @@ export const createJavaScriptApplicationGraph = (
|
||||
...parsed,
|
||||
root_node_ids: uniqueSorted(parsed.root_node_ids),
|
||||
nodes: [...parsed.nodes].sort((left, right) =>
|
||||
compareCodePoints(left.node_id, right.node_id),
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
),
|
||||
edges: [...parsed.edges].sort((left, right) =>
|
||||
compareCodePoints(left.edge_id, right.edge_id),
|
||||
compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
),
|
||||
coverage: {
|
||||
...parsed.coverage,
|
||||
@@ -480,7 +484,7 @@ export const createJavaScriptApplicationGraph = (
|
||||
};
|
||||
return javascriptApplicationGraphSchema.parse({
|
||||
...semantic,
|
||||
graph_id: `jag_${canonicalDigest(semantic, "JavaScript Application Graph")}`,
|
||||
graph_id: `jag_${digestCanonicalValue(semantic, "JavaScript Application Graph")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -517,7 +521,7 @@ export const parseJavaScriptApplicationGraph = (
|
||||
export const computeJavaScriptApplicationGraphSha256 = (
|
||||
input: unknown,
|
||||
): string =>
|
||||
canonicalDigest(
|
||||
digestCanonicalValue(
|
||||
parseJavaScriptApplicationGraph(input),
|
||||
"JavaScript Application Graph",
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import type { Evidence } from "../evidence.js";
|
||||
import { buildJavaScriptApplicationChangeGraph } from "./javascriptApplicationChangeGraph.js";
|
||||
@@ -78,7 +78,7 @@ export const compareJavaScriptApplicationVersions = (
|
||||
};
|
||||
return applicationVersionComparisonResultSchema.parse({
|
||||
...semantic,
|
||||
comparison_id: `javc_${canonicalDigest(semantic, "Application version comparison")}`,
|
||||
comparison_id: `javc_${digestCanonicalValue(semantic, "Application version comparison")}`,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ export const compareApplicationVersionsInputSchema = z
|
||||
}
|
||||
});
|
||||
|
||||
const emptyCandidateNodesSchema = emptyArraySchema;
|
||||
const candidateNodesSchema = z.tuple([nodeIdSchema]).rest(nodeIdSchema);
|
||||
const exactMatchSchema = z.strictObject({
|
||||
status: z.literal("matched"),
|
||||
@@ -49,22 +48,22 @@ const exactMatchSchema = z.strictObject({
|
||||
"exact-module-source-digest",
|
||||
]),
|
||||
confidence: z.literal("exact"),
|
||||
candidate_left_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_right_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_left_node_ids: emptyArraySchema,
|
||||
candidate_right_node_ids: emptyArraySchema,
|
||||
});
|
||||
const sourceMapMatchSchema = z.strictObject({
|
||||
status: z.literal("matched"),
|
||||
basis: z.literal("source-map-identity"),
|
||||
confidence: z.literal("high"),
|
||||
candidate_left_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_right_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_left_node_ids: emptyArraySchema,
|
||||
candidate_right_node_ids: emptyArraySchema,
|
||||
});
|
||||
const inferredMatchSchema = z.strictObject({
|
||||
status: z.literal("matched"),
|
||||
basis: z.enum(["structural-fingerprint", "semantic-key"]),
|
||||
confidence: z.literal("medium"),
|
||||
candidate_left_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_right_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_left_node_ids: emptyArraySchema,
|
||||
candidate_right_node_ids: emptyArraySchema,
|
||||
});
|
||||
const matchedItemMatchSchema = z.union([
|
||||
exactMatchSchema,
|
||||
@@ -75,14 +74,14 @@ const unmatchedItemMatchSchema = z.strictObject({
|
||||
status: z.literal("unmatched"),
|
||||
basis: z.literal("none"),
|
||||
confidence: z.literal("unknown"),
|
||||
candidate_left_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_right_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_left_node_ids: emptyArraySchema,
|
||||
candidate_right_node_ids: emptyArraySchema,
|
||||
});
|
||||
const leftAmbiguousMatchSchema = z.strictObject({
|
||||
status: z.literal("ambiguous"),
|
||||
basis: z.literal("none"),
|
||||
confidence: z.literal("unknown"),
|
||||
candidate_left_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_left_node_ids: emptyArraySchema,
|
||||
candidate_right_node_ids: candidateNodesSchema,
|
||||
});
|
||||
const rightAmbiguousMatchSchema = z.strictObject({
|
||||
@@ -90,7 +89,7 @@ const rightAmbiguousMatchSchema = z.strictObject({
|
||||
basis: z.literal("none"),
|
||||
confidence: z.literal("unknown"),
|
||||
candidate_left_node_ids: candidateNodesSchema,
|
||||
candidate_right_node_ids: emptyCandidateNodesSchema,
|
||||
candidate_right_node_ids: emptyArraySchema,
|
||||
});
|
||||
|
||||
const comparisonItemContextShape = {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import {
|
||||
absenceClaimable,
|
||||
canonicalDigest,
|
||||
canonicalJson,
|
||||
} from "../comparisonSemantics.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { absenceClaimable, canonicalJson } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { Evidence } from "../evidence.js";
|
||||
import type {
|
||||
ApplicationEdge,
|
||||
@@ -96,7 +93,9 @@ export const classifyJavaScriptApplicationVersions = (
|
||||
...matching.pairs.map((pair) => matchedItem(pair, fullContext)),
|
||||
...leftItems,
|
||||
...rightItems,
|
||||
].sort((left, right) => compareCodePoints(left.item_id, right.item_id)),
|
||||
].sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.item_id, right.item_id),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -326,7 +325,7 @@ const locations = (node: ApplicationNode) =>
|
||||
evidence.location.available ? evidence.location.value : evidence.location,
|
||||
)
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
compareUnicodeCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
);
|
||||
|
||||
const properties = (node: ApplicationNode) =>
|
||||
@@ -337,7 +336,7 @@ const properties = (node: ApplicationNode) =>
|
||||
source_map_reference,
|
||||
}))
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
compareUnicodeCodePoints(canonicalJson(left), canonicalJson(right)),
|
||||
);
|
||||
|
||||
const relationshipSignature = (
|
||||
@@ -371,7 +370,7 @@ const relationshipSignature = (
|
||||
];
|
||||
return [];
|
||||
});
|
||||
return canonicalJson(signatures.sort(compareCodePoints));
|
||||
return canonicalJson(signatures.sort(compareUnicodeCodePoints));
|
||||
};
|
||||
|
||||
interface EdgeSignatureContext {
|
||||
@@ -400,7 +399,7 @@ const itemEvidenceLinks = (
|
||||
...nativeLinks(left, context.leftNativeEvidence),
|
||||
...nativeLinks(right, context.rightNativeEvidence),
|
||||
]),
|
||||
].sort(compareCodePoints);
|
||||
].sort(compareUnicodeCodePoints);
|
||||
|
||||
const nativeLinks = (
|
||||
node: ApplicationNode | undefined,
|
||||
@@ -445,7 +444,7 @@ const itemWithId = <Semantic extends ComparisonItemSemantic>(
|
||||
semantic: Semantic,
|
||||
): Semantic & { readonly item_id: string } => ({
|
||||
...semantic,
|
||||
item_id: `javc_item_${canonicalDigest(semantic, "Application version item")}`,
|
||||
item_id: `javc_item_${digestCanonicalValue(semantic, "Application version item")}`,
|
||||
});
|
||||
|
||||
const stringProperty = (node: ApplicationNode, key: string): string | null => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { canonicalJson } from "../comparisonSemantics.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { JsonValue } from "../jsonValue.js";
|
||||
|
||||
@@ -85,7 +85,7 @@ export const matchJavaScriptApplicationVersions = (
|
||||
const rightIds = new Set(remainingRight.map(({ node_id: id }) => id));
|
||||
return {
|
||||
pairs: pairs.sort((a, b) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${a.left.node_id}\0${a.right.node_id}`,
|
||||
`${b.left.node_id}\0${b.right.node_id}`,
|
||||
),
|
||||
@@ -109,7 +109,7 @@ interface TierContext {
|
||||
const applyTier = (context: TierContext): void => {
|
||||
const leftByKey = nodesByKey(context.left.values(), context.tier.key);
|
||||
const rightByKey = nodesByKey(context.right.values(), context.tier.key);
|
||||
for (const key of [...leftByKey.keys()].sort(compareCodePoints)) {
|
||||
for (const key of [...leftByKey.keys()].sort(compareUnicodeCodePoints)) {
|
||||
const leftGroup = leftByKey.get(key) ?? [];
|
||||
const rightGroup = rightByKey.get(key) ?? [];
|
||||
if (rightGroup.length === 0) continue;
|
||||
@@ -174,7 +174,7 @@ const filteredCandidates = (
|
||||
[...candidates.entries()].flatMap(([nodeId, values]) => {
|
||||
const filtered = [...values]
|
||||
.filter((value) => retained.has(value))
|
||||
.sort(compareCodePoints);
|
||||
.sort(compareUnicodeCodePoints);
|
||||
return filtered.length === 0 ? [] : [[nodeId, filtered] as const];
|
||||
}),
|
||||
);
|
||||
@@ -309,10 +309,10 @@ const stringList = (value: unknown): string[] =>
|
||||
Array.isArray(value)
|
||||
? value
|
||||
.filter((item): item is string => typeof item === "string")
|
||||
.sort(compareCodePoints)
|
||||
.sort(compareUnicodeCodePoints)
|
||||
: [];
|
||||
|
||||
const sortedNodes = (nodes: Iterable<ApplicationNode>): ApplicationNode[] =>
|
||||
[...nodes].sort((left, right) =>
|
||||
compareCodePoints(left.node_id, right.node_id),
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as t from "@babel/types";
|
||||
|
||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { propertyName } from "./javascriptAstValues.js";
|
||||
import { stringValue } from "./javascriptStaticAnalysisHelpers.js";
|
||||
|
||||
@@ -37,7 +37,7 @@ export const collectJavaScriptExports = (node: t.Node): StaticExports => {
|
||||
},
|
||||
});
|
||||
return {
|
||||
values: [...exports].sort(compareCodePoints),
|
||||
values: [...exports].sort(compareUnicodeCodePoints),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import {
|
||||
canonicalExportShapeValue,
|
||||
digestExportShapeValue,
|
||||
@@ -107,7 +108,7 @@ const sortChanges = (
|
||||
}))
|
||||
.sort(
|
||||
(left, right) =>
|
||||
compareCodePoints(left.sortKey, right.sortKey) ||
|
||||
compareUnicodeCodePoints(left.sortKey, right.sortKey) ||
|
||||
left.index - right.index,
|
||||
)
|
||||
.map(({ change }) => change);
|
||||
|
||||
@@ -7,7 +7,6 @@ const evidenceIdSchema = prefixedDigestSchema("ev");
|
||||
const graphIdSchema = prefixedDigestSchema("jag");
|
||||
const nodeIdSchema = prefixedDigestSchema("jag_node");
|
||||
const textSchema = z.string().min(1);
|
||||
const selectorTextSchema = textSchema;
|
||||
const semanticPrimitiveSchema = z.union([
|
||||
z.string(),
|
||||
z.number().finite(),
|
||||
@@ -59,10 +58,10 @@ export const compareJavaScriptExportShapesInputSchema = z
|
||||
.strictObject({
|
||||
left: evidenceSchema,
|
||||
right: evidenceSchema,
|
||||
left_module_path: selectorTextSchema,
|
||||
left_export_name: selectorTextSchema,
|
||||
right_module_path: selectorTextSchema,
|
||||
right_export_name: selectorTextSchema,
|
||||
left_module_path: textSchema,
|
||||
left_export_name: textSchema,
|
||||
right_module_path: textSchema,
|
||||
right_export_name: textSchema,
|
||||
})
|
||||
.superRefine((input, context) => {
|
||||
if (input.left.evidence_id === input.right.evidence_id)
|
||||
@@ -151,8 +150,8 @@ const projectedReturnShapeSchema = z
|
||||
export const projectedExportReturnShapesSchema = z
|
||||
.strictObject({
|
||||
semantic_role: z.literal("export-return-shapes"),
|
||||
module_path: selectorTextSchema,
|
||||
exported_name: selectorTextSchema,
|
||||
module_path: textSchema,
|
||||
exported_name: textSchema,
|
||||
callable_id: textSchema,
|
||||
callable_kind: z.enum(["function", "class", "method"]),
|
||||
static_return_shapes: z.array(projectedReturnShapeSchema),
|
||||
@@ -179,8 +178,8 @@ export const projectedExportReturnShapesSchema = z
|
||||
|
||||
const exportCandidateSchema = z.strictObject({
|
||||
node_id: nodeIdSchema,
|
||||
module_path: selectorTextSchema,
|
||||
export_name: selectorTextSchema,
|
||||
module_path: textSchema,
|
||||
export_name: textSchema,
|
||||
matches_requested_module: z.boolean(),
|
||||
matches_requested_export: z.boolean(),
|
||||
});
|
||||
@@ -188,8 +187,8 @@ const exportCandidateSchema = z.strictObject({
|
||||
const selectorResultShape = {
|
||||
evidence_id: evidenceIdSchema,
|
||||
graph_id: graphIdSchema,
|
||||
requested_module_path: selectorTextSchema,
|
||||
requested_export_name: selectorTextSchema,
|
||||
requested_module_path: textSchema,
|
||||
requested_export_name: textSchema,
|
||||
candidates: z.array(exportCandidateSchema),
|
||||
omitted_candidates: z.number().int().min(0),
|
||||
};
|
||||
@@ -238,7 +237,8 @@ const propertyPresenceSchema = z.enum([
|
||||
"unknown-coverage",
|
||||
]);
|
||||
|
||||
const comparisonChangeSchema = z.strictObject({
|
||||
/** One change row; presence is required even when values stay unknown. */
|
||||
export const javaScriptExportShapeComparisonChangeSchema = z.strictObject({
|
||||
change_id: prefixedDigestSchema("jesc_change"),
|
||||
status: z.enum(["added", "removed", "changed", "unknown"]),
|
||||
path: jsonPointerSchema,
|
||||
@@ -255,10 +255,6 @@ const comparisonChangeSchema = z.strictObject({
|
||||
limitations: z.array(textSchema),
|
||||
});
|
||||
|
||||
/** One change row; presence is required even when values stay unknown. */
|
||||
export const javaScriptExportShapeComparisonChangeSchema =
|
||||
comparisonChangeSchema;
|
||||
|
||||
const inventoryPropertyCoverageSchema = z.strictObject({
|
||||
path: jsonPointerSchema,
|
||||
status: z.enum(["complete", "partial"]),
|
||||
@@ -286,7 +282,7 @@ export const javaScriptExportShapeComparisonResultSchema = z.strictObject({
|
||||
changed: z.number().int().min(0),
|
||||
unknown: z.number().int().min(0),
|
||||
}),
|
||||
changes: z.array(comparisonChangeSchema),
|
||||
changes: z.array(javaScriptExportShapeComparisonChangeSchema),
|
||||
coverage: z.strictObject({
|
||||
status: z.enum(["complete-within-inputs", "partial", "truncated"]),
|
||||
left_graph_status: z.enum([
|
||||
@@ -326,7 +322,7 @@ export type JavaScriptExportShapeComparisonResult = z.output<
|
||||
typeof javaScriptExportShapeComparisonResultSchema
|
||||
>;
|
||||
export type JavaScriptExportShapeComparisonChange = z.output<
|
||||
typeof comparisonChangeSchema
|
||||
typeof javaScriptExportShapeComparisonChangeSchema
|
||||
>;
|
||||
export type JavaScriptExportShapePropertyInventory = z.output<
|
||||
typeof propertyInventorySchema
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type JavaScriptExportShapeComparisonResult,
|
||||
type ProjectedExportReturnShapes,
|
||||
} from "./javascriptExportShapeComparisonSchemas.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
|
||||
type Shape = ProjectedExportReturnShapes["static_return_shapes"][number];
|
||||
type SelectorResult = JavaScriptExportShapeComparisonResult["left"];
|
||||
@@ -196,7 +196,7 @@ const exportCandidates = (
|
||||
}
|
||||
}
|
||||
return [...candidates.values()].sort((left, right) =>
|
||||
compareCodePoints(candidateKey(left), candidateKey(right)),
|
||||
compareUnicodeCodePoints(candidateKey(left), candidateKey(right)),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import type {
|
||||
JavaScriptExportShapeComparisonResult,
|
||||
JavaScriptExportShapePropertyInventory,
|
||||
} from "./javascriptExportShapeComparisonSchemas.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type {
|
||||
SelectedJavaScriptExport,
|
||||
RetainedJavaScriptExportShapes,
|
||||
@@ -252,7 +253,7 @@ const compareDiscriminants = (
|
||||
left: Discriminant,
|
||||
right: Discriminant,
|
||||
): number =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
canonicalExportShapeValue(left),
|
||||
canonicalExportShapeValue(right),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
||||
import {
|
||||
featureSeedMatchMode,
|
||||
@@ -30,7 +30,7 @@ export const findApplicationFeatureSeeds = (
|
||||
nodes
|
||||
.flatMap((node) => seedMatch(node, seed))
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.node_id}\0${left.basis}\0${left.field}`,
|
||||
`${right.node_id}\0${right.basis}\0${right.field}`,
|
||||
),
|
||||
@@ -230,7 +230,9 @@ const propertyFields = (
|
||||
continue;
|
||||
}
|
||||
if (current !== null && typeof current === "object") {
|
||||
for (const key of Object.keys(current).sort(compareCodePoints).reverse())
|
||||
for (const key of Object.keys(current)
|
||||
.sort(compareUnicodeCodePoints)
|
||||
.reverse())
|
||||
pending.push({
|
||||
current: current[key] ?? null,
|
||||
path: `${path}.${key}`,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { Evidence } from "../evidence.js";
|
||||
import {
|
||||
createJavaScriptApplicationGraph,
|
||||
@@ -86,7 +87,7 @@ export const traceApplicationFeature = (
|
||||
};
|
||||
return applicationFeatureTraceResultSchema.parse({
|
||||
...semantic,
|
||||
trace_id: `jatr_${canonicalDigest(semantic, "Feature trace")}`,
|
||||
trace_id: `jatr_${digestCanonicalValue(semantic, "Feature trace")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -126,7 +127,7 @@ const noMatchResult = (
|
||||
};
|
||||
return applicationFeatureTraceResultSchema.parse({
|
||||
...semantic,
|
||||
trace_id: `jatr_${canonicalDigest(semantic, "Feature trace")}`,
|
||||
trace_id: `jatr_${digestCanonicalValue(semantic, "Feature trace")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -170,7 +171,9 @@ const terminalPaths = (
|
||||
)
|
||||
.map((node) => pathTo(node, traversal.predecessors, edgeById))
|
||||
.filter((path): path is NonNullable<typeof path> => path !== null)
|
||||
.sort((left, right) => compareCodePoints(left.path_id, right.path_id));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.path_id, right.path_id),
|
||||
);
|
||||
};
|
||||
|
||||
const pathTo = (
|
||||
@@ -207,7 +210,7 @@ const pathTo = (
|
||||
};
|
||||
return {
|
||||
...semantic,
|
||||
path_id: `jatp_${canonicalDigest(semantic, "Feature trace")}`,
|
||||
path_id: `jatp_${digestCanonicalValue(semantic, "Feature trace")}`,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
ApplicationNode,
|
||||
} from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
|
||||
interface AdjacencyEntry {
|
||||
readonly edge: ApplicationEdge;
|
||||
@@ -53,9 +53,11 @@ export const traverseApplicationFeature = (
|
||||
nodes: [...visited]
|
||||
.map((nodeId) => nodeById.get(nodeId))
|
||||
.filter((node): node is ApplicationNode => node !== undefined)
|
||||
.sort((left, right) => compareCodePoints(left.node_id, right.node_id)),
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
),
|
||||
edges: [...retainedEdges.values()].sort((left, right) =>
|
||||
compareCodePoints(left.edge_id, right.edge_id),
|
||||
compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
),
|
||||
predecessors,
|
||||
};
|
||||
@@ -80,7 +82,7 @@ const buildAdjacency = (
|
||||
}
|
||||
for (const entries of adjacency.values())
|
||||
entries.sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.edge.edge_id}\0${left.nextNodeId}`,
|
||||
`${right.edge.edge_id}\0${right.nextNodeId}`,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { Evidence } from "../evidence.js";
|
||||
import {
|
||||
type ApplicationEdge,
|
||||
@@ -38,7 +38,7 @@ export const buildJavaScriptNativeHandoffs = (
|
||||
providers: uniqueProviders(linked),
|
||||
evidence_ids: linked
|
||||
.map(({ evidence_id: id }) => id)
|
||||
.sort(compareCodePoints),
|
||||
.sort(compareUnicodeCodePoints),
|
||||
recommended_tools: [
|
||||
"open_binary" as const,
|
||||
"binary_overview" as const,
|
||||
@@ -50,7 +50,7 @@ export const buildJavaScriptNativeHandoffs = (
|
||||
];
|
||||
})
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(left.native_node_id, right.native_node_id),
|
||||
compareUnicodeCodePoints(left.native_node_id, right.native_node_id),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -95,7 +95,9 @@ const nativeExportNodes = (
|
||||
return [...new Set(ids)]
|
||||
.map((id) => nodeById.get(id))
|
||||
.filter((node): node is ApplicationNode => node?.kind === "native-export")
|
||||
.sort((left, right) => compareCodePoints(left.node_id, right.node_id));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
);
|
||||
};
|
||||
|
||||
const requestedExports = (nodes: readonly ApplicationNode[]): string[] => {
|
||||
@@ -106,7 +108,7 @@ const requestedExports = (nodes: readonly ApplicationNode[]): string[] => {
|
||||
...stringArray(observation.properties.members),
|
||||
]),
|
||||
);
|
||||
return [...new Set(values)].sort(compareCodePoints);
|
||||
return [...new Set(values)].sort(compareUnicodeCodePoints);
|
||||
};
|
||||
|
||||
const stringArray = (value: unknown): string[] =>
|
||||
@@ -125,7 +127,7 @@ const uniqueProviders = (
|
||||
]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.id}\0${left.name}\0${left.version ?? ""}`,
|
||||
`${right.id}\0${right.name}\0${right.version ?? ""}`,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isAbsolute } from "node:path";
|
||||
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { isPathWithinRoot } from "../localPath.js";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -44,7 +44,7 @@ export const parseActiveElectronCapture = (
|
||||
kind: "electron-active",
|
||||
evidence,
|
||||
inspection: normalizeInspection(result),
|
||||
captureSha256: canonicalDigest(result, "Runtime reconciliation"),
|
||||
captureSha256: digestCanonicalValue(result, "Runtime reconciliation"),
|
||||
scriptsCompleteWithinScope: false,
|
||||
};
|
||||
};
|
||||
@@ -83,7 +83,7 @@ const normalizeInspection = (
|
||||
readonly completeness: BrowserCompleteness;
|
||||
} => ({
|
||||
target: {
|
||||
target_id: `electron-active:${canonicalDigest(result.application.application_path, "Runtime reconciliation").slice(0, 32)}`,
|
||||
target_id: `electron-active:${digestCanonicalValue(result.application.application_path, "Runtime reconciliation").slice(0, 32)}`,
|
||||
type: "electron-application",
|
||||
title: result.application.application_path,
|
||||
attached: true,
|
||||
|
||||
@@ -11,7 +11,8 @@ import type {
|
||||
ParsedRuntimeCapture,
|
||||
ParsedStaticLayer,
|
||||
} from "./javascriptRuntimeReconciliationParsing.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { RuntimeProjection } from "./javascriptRuntimeReconciliationRuntime.js";
|
||||
|
||||
interface ReconciledGraphInput {
|
||||
@@ -93,12 +94,14 @@ const boundGraphContent = (
|
||||
const allRoots = preferredRootIds(input);
|
||||
const rootNodeIds = allRoots;
|
||||
const sortedNodes = [...nodes].sort((left, right) =>
|
||||
compareCodePoints(left.node_id, right.node_id),
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
);
|
||||
const retainedNodes = sortedNodes;
|
||||
const retainedIds = new Set(retainedNodes.map(({ node_id: id }) => id));
|
||||
const retainedEdges = [...edges]
|
||||
.sort((left, right) => compareCodePoints(left.edge_id, right.edge_id))
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.edge_id, right.edge_id),
|
||||
)
|
||||
.filter(
|
||||
({ source_node_id: source, target_node_id: target }) =>
|
||||
retainedIds.has(source) && retainedIds.has(target),
|
||||
@@ -231,7 +234,7 @@ const mergeNodes = (nodes: readonly ApplicationNode[]): ApplicationNode[] => {
|
||||
.map((observation) => [observation.observation_id, observation]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(left.observation_id, right.observation_id),
|
||||
compareUnicodeCodePoints(left.observation_id, right.observation_id),
|
||||
);
|
||||
return createJavaScriptApplicationNode({
|
||||
kind: first.kind,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createJavaScriptApplicationEdge } from "./javascriptApplicationGraph.js";
|
||||
import type { ApplicationEdge } from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import type { ParsedStaticLayer } from "./javascriptRuntimeReconciliationParsing.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { JavaScriptRuntimeReconciliationItem } from "./javascriptRuntimeReconciliationSchemas.js";
|
||||
import type { RuntimeReconciliationEntity } from "./javascriptRuntimeReconciliationRuntime.js";
|
||||
import {
|
||||
@@ -29,7 +29,7 @@ export const reconcileRuntimeEntities = (input: {
|
||||
reconcileEntity(entity, index, input.layers),
|
||||
);
|
||||
const retained = evaluated.sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
left.item.reconciliation_id,
|
||||
right.item.reconciliation_id,
|
||||
),
|
||||
@@ -177,7 +177,7 @@ const candidatesWithLocation = (
|
||||
if (!located.has(key)) located.set(key, { candidate, mapping });
|
||||
}
|
||||
return [...located.values()].sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
candidateKey(left.candidate),
|
||||
candidateKey(right.candidate),
|
||||
),
|
||||
@@ -331,7 +331,7 @@ const createItem = (
|
||||
candidate_static_nodes: candidateReferences.items,
|
||||
} satisfies Omit<JavaScriptRuntimeReconciliationItem, "reconciliation_id">;
|
||||
return {
|
||||
reconciliation_id: `jrr_item_${canonicalDigest(semantic, "Runtime reconciliation")}`,
|
||||
reconciliation_id: `jrr_item_${digestCanonicalValue(semantic, "Runtime reconciliation")}`,
|
||||
...semantic,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
type JavaScriptRuntimeObservation,
|
||||
type JavaScriptRuntimeTargetLocation,
|
||||
} from "./javascriptRuntimeObservation.js";
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { parseEvidence, type Evidence } from "../evidence.js";
|
||||
import {
|
||||
javascriptApplicationAnalysisResultSchema,
|
||||
@@ -140,7 +140,9 @@ export const parseStaticLayers = (
|
||||
): ParsedStaticLayer[] =>
|
||||
layers
|
||||
.map((layer) => parseStaticLayer(layer))
|
||||
.sort((left, right) => compareCodePoints(left.layerId, right.layerId));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.layerId, right.layerId),
|
||||
);
|
||||
|
||||
/** Parse only supported passive web/Electron inspection Evidence. */
|
||||
export const parseRuntimeCaptures = (
|
||||
@@ -149,7 +151,10 @@ export const parseRuntimeCaptures = (
|
||||
observations
|
||||
.map((observation) => parseRuntimeCapture(observation))
|
||||
.sort((left, right) =>
|
||||
compareCodePoints(left.evidence.evidence_id, right.evidence.evidence_id),
|
||||
compareUnicodeCodePoints(
|
||||
left.evidence.evidence_id,
|
||||
right.evidence.evidence_id,
|
||||
),
|
||||
);
|
||||
|
||||
const parseStaticLayer = (layer: StaticLayerInput): ParsedStaticLayer => {
|
||||
@@ -176,7 +181,7 @@ const parseStaticLayer = (layer: StaticLayerInput): ParsedStaticLayer => {
|
||||
"JavaScript application Evidence subject disagrees with its result",
|
||||
);
|
||||
return {
|
||||
layerId: `jrl_${canonicalDigest(
|
||||
layerId: `jrl_${digestCanonicalValue(
|
||||
{
|
||||
role: layer.role,
|
||||
evidence_id: evidence.evidence_id,
|
||||
@@ -219,7 +224,7 @@ const parseRuntimeCapture = (input: Evidence): ParsedRuntimeCapture => {
|
||||
kind: "browser",
|
||||
evidence,
|
||||
inspection,
|
||||
captureSha256: canonicalDigest(inspection, "Runtime reconciliation"),
|
||||
captureSha256: digestCanonicalValue(inspection, "Runtime reconciliation"),
|
||||
scriptsCompleteWithinScope: scriptsComplete(inspection.completeness),
|
||||
};
|
||||
}
|
||||
@@ -247,7 +252,7 @@ const parseRuntimeCapture = (input: Evidence): ParsedRuntimeCapture => {
|
||||
kind: "electron",
|
||||
evidence,
|
||||
inspection,
|
||||
captureSha256: canonicalDigest(inspection, "Runtime reconciliation"),
|
||||
captureSha256: digestCanonicalValue(inspection, "Runtime reconciliation"),
|
||||
scriptsCompleteWithinScope: scriptsComplete(inspection.completeness),
|
||||
};
|
||||
}
|
||||
@@ -270,7 +275,7 @@ const parseRuntimeCapture = (input: Evidence): ParsedRuntimeCapture => {
|
||||
kind: "v8-inspector",
|
||||
evidence,
|
||||
inspection,
|
||||
captureSha256: canonicalDigest(result, "Runtime reconciliation"),
|
||||
captureSha256: digestCanonicalValue(result, "Runtime reconciliation"),
|
||||
scriptsCompleteWithinScope: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
|
||||
import { canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import type { StaticLoadStateProjection } from "./javascriptRuntimeLoadState.js";
|
||||
import type { RuntimeMatchingProjection } from "./javascriptRuntimeReconciliationMatching.js";
|
||||
@@ -49,7 +49,7 @@ export const createJavaScriptRuntimeReconciliationResult = (
|
||||
limitations: reconciliationLimitations(completion),
|
||||
};
|
||||
return javascriptRuntimeReconciliationResultSchema.parse({
|
||||
reconciliation_id: `jrr_${canonicalDigest(semantic, "Runtime reconciliation")}`,
|
||||
reconciliation_id: `jrr_${digestCanonicalValue(semantic, "Runtime reconciliation")}`,
|
||||
...semantic,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { evidenceSchema } from "../evidence.js";
|
||||
import { javascriptRuntimeUnresolvedLocationSchema } from "./javascriptRuntimeObservation.js";
|
||||
import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js";
|
||||
@@ -85,7 +85,7 @@ function normalizeRuntimeMappings(
|
||||
mappings.map((mapping) => [runtimeMappingKey(mapping), mapping]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(runtimeMappingKey(left), runtimeMappingKey(right)),
|
||||
compareUnicodeCodePoints(runtimeMappingKey(left), runtimeMappingKey(right)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import { posix, relative, win32 } from "node:path";
|
||||
|
||||
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ParsedStaticLayer } from "./javascriptRuntimeReconciliationParsing.js";
|
||||
import type { RuntimeReconciliationEntity } from "./javascriptRuntimeReconciliationRuntime.js";
|
||||
|
||||
@@ -39,8 +40,8 @@ export const collectStaticRuntimeCandidates = (
|
||||
.flatMap((layer) => candidatesForLayer(layer))
|
||||
.sort(
|
||||
(left, right) =>
|
||||
compareCodePoints(left.layer.layerId, right.layer.layerId) ||
|
||||
compareCodePoints(left.node.node_id, right.node.node_id),
|
||||
compareUnicodeCodePoints(left.layer.layerId, right.layer.layerId) ||
|
||||
compareUnicodeCodePoints(left.node.node_id, right.node.node_id),
|
||||
);
|
||||
|
||||
/** Translate one already-authorized runtime location into artifact paths. */
|
||||
@@ -284,7 +285,7 @@ const activeArtifactPrefixes = (
|
||||
)
|
||||
)
|
||||
prefixes.add(mapping.artifact_prefix);
|
||||
return [...prefixes].sort(compareCodePoints);
|
||||
return [...prefixes].sort(compareUnicodeCodePoints);
|
||||
};
|
||||
|
||||
const pathWithinPrefix = (path: string, prefix: string): boolean =>
|
||||
@@ -315,6 +316,6 @@ const uniqueMappedPaths = (
|
||||
).values(),
|
||||
].sort(
|
||||
(left, right) =>
|
||||
compareCodePoints(left.path, right.path) ||
|
||||
compareCodePoints(left.basis, right.basis),
|
||||
compareUnicodeCodePoints(left.path, right.path) ||
|
||||
compareUnicodeCodePoints(left.basis, right.basis),
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
|
||||
import { semanticBinding } from "./javascriptSemanticIr.js";
|
||||
import {
|
||||
bindingById,
|
||||
onlyCallable,
|
||||
topLevelBinding,
|
||||
} from "./javascriptSemanticAnalysis.fixture.js";
|
||||
@@ -39,7 +39,7 @@ describe("JavaScript semantic analysis: calls 1", () => {
|
||||
.filter(({ callSiteId }) => callSiteId === call.callSiteId)
|
||||
.map(({ argumentIndex, parameterBindingId }) => ({
|
||||
argumentIndex,
|
||||
parameter: semanticBinding(ir, parameterBindingId)?.name,
|
||||
parameter: bindingById(ir, parameterBindingId)?.name,
|
||||
})),
|
||||
).toEqual([
|
||||
{ argumentIndex: 0, parameter: "value" },
|
||||
@@ -309,7 +309,7 @@ describe("JavaScript semantic rest parameter flow", () => {
|
||||
expect(
|
||||
ir.argumentFlows.map(({ argumentIndex, parameterBindingId }) => [
|
||||
argumentIndex,
|
||||
semanticBinding(ir, parameterBindingId)?.name,
|
||||
bindingById(ir, parameterBindingId)?.name,
|
||||
]),
|
||||
).toEqual(expected);
|
||||
},
|
||||
|
||||
@@ -17,6 +17,12 @@ export const bindingsNamed = (
|
||||
): JavaScriptSemanticBinding[] =>
|
||||
ir.bindings.filter(({ name: candidate }) => candidate === name);
|
||||
|
||||
export const bindingById = (
|
||||
ir: JavaScriptSemanticIr,
|
||||
bindingId: string,
|
||||
): JavaScriptSemanticBinding | undefined =>
|
||||
ir.bindings.find(({ bindingId: candidate }) => candidate === bindingId);
|
||||
|
||||
export const onlyBinding = (
|
||||
ir: JavaScriptSemanticIr,
|
||||
name: string,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
|
||||
import {
|
||||
semanticBinding,
|
||||
semanticReferenceAt,
|
||||
} from "./javascriptSemanticIr.js";
|
||||
import {
|
||||
programScope,
|
||||
bindingById,
|
||||
bindingsNamed,
|
||||
onlyBinding,
|
||||
topLevelBinding,
|
||||
@@ -88,12 +85,12 @@ describe("JavaScript semantic analysis: structure 1", () => {
|
||||
);
|
||||
if (shadowReference === undefined || shadowReference.bindingId === null)
|
||||
throw new Error("Missing shadow reference");
|
||||
expect(semanticBinding(ir, shadowReference.bindingId)).toEqual(shadow);
|
||||
expect(bindingById(ir, shadowReference.bindingId)).toEqual(shadow);
|
||||
expect(
|
||||
semanticReferenceAt(
|
||||
ir,
|
||||
shadowReference.location.start.line,
|
||||
shadowReference.location.start.column,
|
||||
ir.references.find(
|
||||
({ location }) =>
|
||||
location.start.line === shadowReference.location.start.line &&
|
||||
location.start.column === shadowReference.location.start.column,
|
||||
),
|
||||
).toEqual(shadowReference);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
JavaScriptSemanticArgumentFlow,
|
||||
JavaScriptSemanticCallable,
|
||||
} from "./javascriptSemanticIr.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { semanticCallableIdForNode } from "./javascriptSemanticProjection.js";
|
||||
import {
|
||||
resolveSemanticBindingState,
|
||||
@@ -182,7 +182,9 @@ export const parameterBindings = (
|
||||
parameterAcceptsArgument(callable, location, index, state),
|
||||
),
|
||||
)
|
||||
.sort((left, right) => compareCodePoints(left.bindingId, right.bindingId));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.bindingId, right.bindingId),
|
||||
);
|
||||
};
|
||||
|
||||
const parameterAcceptsArgument = (
|
||||
@@ -216,4 +218,4 @@ const rangeContains = (
|
||||
outer.end.column >= inner.end.column));
|
||||
|
||||
const uniqueCallableIds = (values: readonly string[]): string[] =>
|
||||
[...new Set(values)].sort(compareCodePoints);
|
||||
[...new Set(values)].sort(compareUnicodeCodePoints);
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type JavaScriptSemanticAnalysisState,
|
||||
type JavaScriptSemanticBindingState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||
import { range } from "./javascriptStaticAnalysisHelpers.js";
|
||||
import {
|
||||
@@ -219,7 +219,7 @@ const childInteraction = (
|
||||
processBindingId: binding?.bindingId ?? null,
|
||||
linkedProcessIds: linked
|
||||
.map((candidate) => spawnId(candidate))
|
||||
.sort(compareCodePoints),
|
||||
.sort(compareUnicodeCodePoints),
|
||||
eventName: kind === "listener" ? eventName : null,
|
||||
signalName:
|
||||
kind === "signal"
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { JavaScriptSemanticResourceLimit } from "./javascriptSemanticValueTypes.js";
|
||||
import type { ApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
import {
|
||||
SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
|
||||
SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
|
||||
} from "./javascriptSemanticResourceLimits.js";
|
||||
|
||||
/** Exact retention state for one callable's direct returns. */
|
||||
export type JavaScriptSemanticReturnCoverage = {
|
||||
@@ -40,6 +46,33 @@ export const semanticCoverageResourceLimits = (
|
||||
coverage: JavaScriptSemanticCoverage,
|
||||
): readonly JavaScriptSemanticResourceLimit[] => coverage.resourceLimits ?? [];
|
||||
|
||||
const semanticResourceBounds: Record<
|
||||
JavaScriptSemanticResourceLimit,
|
||||
Pick<ApplicationCoverage["limits"][number], "value" | "unit">
|
||||
> = {
|
||||
"primitive-candidates": {
|
||||
value: SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
|
||||
unit: "items",
|
||||
},
|
||||
"primitive-bytes": {
|
||||
value: SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
|
||||
unit: "bytes",
|
||||
},
|
||||
"expression-depth": {
|
||||
value: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
|
||||
unit: "depth",
|
||||
},
|
||||
};
|
||||
|
||||
/** Map semantic evaluator bounds to their caller-visible application coverage. */
|
||||
export const semanticResourceLimitCoverage = (
|
||||
resourceLimits: readonly JavaScriptSemanticResourceLimit[],
|
||||
): ApplicationCoverage["limits"] =>
|
||||
resourceLimits.map((resourceLimit) => ({
|
||||
name: `javascript_semantic_${resourceLimit.replaceAll("-", "_")}`,
|
||||
...semanticResourceBounds[resourceLimit],
|
||||
}));
|
||||
|
||||
/** Classify one callable's direct-return coverage. */
|
||||
export const semanticReturnCoverage = (
|
||||
retainedCount: number,
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
JavaScriptSemanticConfigurationOperation,
|
||||
JavaScriptSemanticRequestOperation,
|
||||
} from "./javascriptSemanticIr.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import {
|
||||
semanticStaticPropertyKey,
|
||||
semanticStaticPropertyName,
|
||||
@@ -301,7 +301,7 @@ const collectResponseConsumers = (
|
||||
),
|
||||
linkedRequestIds: linked
|
||||
.map(({ operation }) => operation.requestId)
|
||||
.sort(compareCodePoints),
|
||||
.sort(compareUnicodeCodePoints),
|
||||
endpoint: null,
|
||||
fields: [],
|
||||
resolution:
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
||||
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { JavaScriptSemanticCallAnalysis } from "./javascriptSemanticCalls.js";
|
||||
import type {
|
||||
JavaScriptSemanticCallable,
|
||||
@@ -100,7 +100,7 @@ export const collectJavaScriptSemanticFingerprints = (
|
||||
),
|
||||
),
|
||||
literalSetSha256: digest(
|
||||
[...new Set(syntax.literals)].sort(compareCodePoints),
|
||||
[...new Set(syntax.literals)].sort(compareUnicodeCodePoints),
|
||||
),
|
||||
effects: [
|
||||
...(isAsyncCallable(node) ? (["async"] as const) : []),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { canonicalDigest, canonicalJson } from "../comparisonSemantics.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { canonicalJson } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { canonicalJsonDigestSteps } from "../canonicalJsonDigestSteps.js";
|
||||
import { freezeOwnedJsonSnapshotSteps } from "../immutableJson.js";
|
||||
import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
@@ -30,7 +32,7 @@ const normalizeEvidence = (
|
||||
coverage: {
|
||||
...evidence.coverage,
|
||||
limits: [...evidence.coverage.limits].sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
canonicalJson(left, "JavaScript semantic graph"),
|
||||
canonicalJson(right, "JavaScript semantic graph"),
|
||||
),
|
||||
@@ -44,7 +46,7 @@ const normalizeEvidence = (
|
||||
export const javaScriptSemanticNodeId = (
|
||||
node: Pick<JavaScriptSemanticGraphNode, "kind" | "identity">,
|
||||
): string =>
|
||||
`jsrg_node_${canonicalDigest({ kind: node.kind, identity: node.identity }, "JavaScript semantic graph")}`;
|
||||
`jsrg_node_${digestCanonicalValue({ kind: node.kind, identity: node.identity }, "JavaScript semantic graph")}`;
|
||||
|
||||
/** Normalize one semantic entity and derive its artifact-version identifier. */
|
||||
export const createJavaScriptSemanticGraphNode = (
|
||||
@@ -81,7 +83,7 @@ export const createJavaScriptSemanticGraphRelation = (
|
||||
};
|
||||
return javaScriptSemanticRelationSchema.parse({
|
||||
...semantic,
|
||||
relation_id: `jsrg_relation_${canonicalDigest(semantic, "JavaScript semantic graph")}`,
|
||||
relation_id: `jsrg_relation_${digestCanonicalValue(semantic, "JavaScript semantic graph")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -96,7 +98,7 @@ export const createJavaScriptSemanticGraphUnknown = (input: unknown) => {
|
||||
};
|
||||
return javaScriptSemanticUnknownSchema.parse({
|
||||
...semantic,
|
||||
unknown_id: `jsrg_unknown_${canonicalDigest(semantic, "JavaScript semantic graph")}`,
|
||||
unknown_id: `jsrg_unknown_${digestCanonicalValue(semantic, "JavaScript semantic graph")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -112,14 +114,14 @@ export const createJavaScriptSemanticFingerprint = (input: unknown) => {
|
||||
limitations: uniqueSorted(parsed.limitations),
|
||||
evidence: normalizeEvidence(parsed.evidence),
|
||||
};
|
||||
const fingerprintSha256 = canonicalDigest(
|
||||
const fingerprintSha256 = digestCanonicalValue(
|
||||
semantic.components,
|
||||
"JavaScript semantic graph",
|
||||
);
|
||||
return javaScriptSemanticFingerprintSchema.parse({
|
||||
...semantic,
|
||||
fingerprint_sha256: fingerprintSha256,
|
||||
fingerprint_id: `jsrg_fingerprint_${canonicalDigest(
|
||||
fingerprint_id: `jsrg_fingerprint_${digestCanonicalValue(
|
||||
{
|
||||
function_node_id: semantic.function_node_id,
|
||||
algorithm: semantic.algorithm,
|
||||
@@ -149,7 +151,9 @@ const sortedUniqueIssue = (
|
||||
context: GraphIssueReporter,
|
||||
): void => {
|
||||
for (let index = 1; index < values.length; index += 1) {
|
||||
if (compareCodePoints(values[index - 1] ?? "", values[index] ?? "") < 0)
|
||||
if (
|
||||
compareUnicodeCodePoints(values[index - 1] ?? "", values[index] ?? "") < 0
|
||||
)
|
||||
continue;
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -326,7 +330,8 @@ function* checkRelationsSteps(
|
||||
const { relation_id: identifier, ...semantic } = relation;
|
||||
if (
|
||||
identifier !==
|
||||
"jsrg_relation_" + canonicalDigest(semantic, "JavaScript semantic graph")
|
||||
"jsrg_relation_" +
|
||||
digestCanonicalValue(semantic, "JavaScript semantic graph")
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -370,7 +375,8 @@ function* checkUnknownsSteps(
|
||||
const { unknown_id: identifier, ...semantic } = unknown;
|
||||
if (
|
||||
identifier !==
|
||||
"jsrg_unknown_" + canonicalDigest(semantic, "JavaScript semantic graph")
|
||||
"jsrg_unknown_" +
|
||||
digestCanonicalValue(semantic, "JavaScript semantic graph")
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -439,7 +445,7 @@ function* checkFingerprintsSteps(
|
||||
});
|
||||
if (
|
||||
fingerprint.fingerprint_sha256 !==
|
||||
canonicalDigest(fingerprint.components, "JavaScript semantic graph")
|
||||
digestCanonicalValue(fingerprint.components, "JavaScript semantic graph")
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -448,7 +454,7 @@ function* checkFingerprintsSteps(
|
||||
});
|
||||
const expectedIdentifier =
|
||||
"jsrg_fingerprint_" +
|
||||
canonicalDigest(
|
||||
digestCanonicalValue(
|
||||
{
|
||||
function_node_id: fingerprint.function_node_id,
|
||||
algorithm: fingerprint.algorithm,
|
||||
@@ -514,7 +520,7 @@ const checkGraph = (graph: GraphRecord, context: GraphIssueReporter): void => {
|
||||
const { graph_id: identifier, ...semantic } = graph;
|
||||
if (
|
||||
identifier !==
|
||||
"jsrg_" + canonicalDigest(semantic, "JavaScript semantic graph")
|
||||
"jsrg_" + digestCanonicalValue(semantic, "JavaScript semantic graph")
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
@@ -538,21 +544,21 @@ const normalizeGraphInput = (
|
||||
...parsed,
|
||||
root_node_ids: uniqueSorted(parsed.root_node_ids),
|
||||
nodes: [...parsed.nodes].sort((left, right) =>
|
||||
compareCodePoints(left.node_id, right.node_id),
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
),
|
||||
relations: [...parsed.relations].sort((left, right) =>
|
||||
compareCodePoints(left.relation_id, right.relation_id),
|
||||
compareUnicodeCodePoints(left.relation_id, right.relation_id),
|
||||
),
|
||||
fingerprints: [...parsed.fingerprints].sort((left, right) =>
|
||||
compareCodePoints(left.fingerprint_id, right.fingerprint_id),
|
||||
compareUnicodeCodePoints(left.fingerprint_id, right.fingerprint_id),
|
||||
),
|
||||
unknowns: [...parsed.unknowns].sort((left, right) =>
|
||||
compareCodePoints(left.unknown_id, right.unknown_id),
|
||||
compareUnicodeCodePoints(left.unknown_id, right.unknown_id),
|
||||
),
|
||||
coverage: {
|
||||
...parsed.coverage,
|
||||
limits: [...parsed.coverage.limits].sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
canonicalJson(left, "JavaScript semantic graph"),
|
||||
canonicalJson(right, "JavaScript semantic graph"),
|
||||
),
|
||||
@@ -562,7 +568,9 @@ const normalizeGraphInput = (
|
||||
...family,
|
||||
unknown_ids: uniqueSorted(family.unknown_ids),
|
||||
}))
|
||||
.sort((left, right) => compareCodePoints(left.family, right.family)),
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.family, right.family),
|
||||
),
|
||||
},
|
||||
limitations: uniqueSorted(parsed.limitations),
|
||||
});
|
||||
@@ -579,7 +587,7 @@ export const createJavaScriptSemanticGraph = (
|
||||
// public schema still verifies arbitrary records and their commitments.
|
||||
const record: GraphRecord = {
|
||||
...semantic,
|
||||
graph_id: `jsrg_${canonicalDigest(semantic, "JavaScript semantic graph")}`,
|
||||
graph_id: `jsrg_${digestCanonicalValue(semantic, "JavaScript semantic graph")}`,
|
||||
};
|
||||
const issues: SemanticGraphIssue[] = [];
|
||||
checkGraphContent(record, {
|
||||
|
||||
@@ -435,24 +435,6 @@ export interface JavaScriptSemanticIr {
|
||||
readonly limitations: readonly string[];
|
||||
}
|
||||
|
||||
/** Find the innermost resolved reference at one source coordinate. */
|
||||
export const semanticReferenceAt = (
|
||||
ir: JavaScriptSemanticIr,
|
||||
line: number,
|
||||
column: number,
|
||||
): JavaScriptSemanticReference | undefined =>
|
||||
ir.references.find(
|
||||
({ location }) =>
|
||||
location.start.line === line && location.start.column === column,
|
||||
);
|
||||
|
||||
/** Read one binding by its deterministic semantic identifier. */
|
||||
export const semanticBinding = (
|
||||
ir: JavaScriptSemanticIr,
|
||||
bindingId: string,
|
||||
): JavaScriptSemanticBinding | undefined =>
|
||||
ir.bindings.find(({ bindingId: candidate }) => candidate === bindingId);
|
||||
|
||||
/** Fail-closed result when Babel cannot produce an inert syntax tree. */
|
||||
export const failedJavaScriptSemanticIr = (): JavaScriptSemanticIr => ({
|
||||
schema: "JavaScriptSemanticIR",
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
JavaScriptSemanticPrimitive,
|
||||
JavaScriptSemanticValue,
|
||||
} from "./javascriptSemanticValueTypes.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { semanticPrimitiveKey } from "./javascriptSemanticProvenance.js";
|
||||
import {
|
||||
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
|
||||
@@ -35,7 +35,7 @@ export const semanticPrimitiveSet = (
|
||||
return semanticResourceLimitUnknown("primitive-bytes");
|
||||
const unique = [...uniqueValues]
|
||||
.map((value) => ({ key: semanticPrimitiveKey(value), value }))
|
||||
.sort((left, right) => compareCodePoints(left.key, right.key))
|
||||
.sort((left, right) => compareUnicodeCodePoints(left.key, right.key))
|
||||
.map(({ value }) => value);
|
||||
const only = unique[0];
|
||||
return unique.length === 1 && only !== undefined
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type JavaScriptSemanticScopeState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import { semanticRequireOrigin } from "./javascriptSemanticRequireOrigin.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||
import {
|
||||
evaluateSemanticBinding,
|
||||
@@ -160,7 +160,7 @@ export const immutableSemanticScopes = (
|
||||
bindingsComplete: scope.bindingsComplete,
|
||||
bindingIds: [...scope.bindings.values()]
|
||||
.map(({ bindingId }) => bindingId)
|
||||
.sort(compareCodePoints),
|
||||
.sort(compareUnicodeCodePoints),
|
||||
}));
|
||||
|
||||
/** Evaluate and freeze bindings into deterministic IR order. */
|
||||
@@ -178,7 +178,9 @@ export const immutableSemanticBindings = (
|
||||
value: evaluateSemanticBinding(binding, state),
|
||||
provenance: evaluateSemanticProvenance(binding, state),
|
||||
}))
|
||||
.sort((left, right) => compareCodePoints(left.bindingId, right.bindingId));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.bindingId, right.bindingId),
|
||||
);
|
||||
|
||||
const callableKind = (
|
||||
node: t.Node,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
semanticResolutionBlocked,
|
||||
type JavaScriptSemanticAnalysisState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||
import { range } from "./javascriptStaticAnalysisHelpers.js";
|
||||
import {
|
||||
@@ -87,7 +87,7 @@ export const collectJavaScriptSemanticPromises = (
|
||||
ownerCallableId: candidate.ownerCallableId,
|
||||
...ownership,
|
||||
sourcePromiseIds: [...new Set(sources.promiseIds)].sort(
|
||||
compareCodePoints,
|
||||
compareUnicodeCodePoints,
|
||||
),
|
||||
sourceResolution: sources.status,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
JavaScriptModuleOrigin,
|
||||
} from "./javascriptSemanticIr.js";
|
||||
import type { JavaScriptSemanticPrimitive } from "./javascriptSemanticValueTypes.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
|
||||
/** Construct exact local provenance without inventing module origins. */
|
||||
export const semanticLocalProvenance = (): JavaScriptBindingProvenance => ({
|
||||
@@ -44,7 +44,7 @@ export const uniqueSemanticOrigins = (
|
||||
origins.map((origin) => [semanticOriginKey(origin), origin]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(semanticOriginKey(left), semanticOriginKey(right)),
|
||||
compareUnicodeCodePoints(semanticOriginKey(left), semanticOriginKey(right)),
|
||||
);
|
||||
|
||||
const semanticOriginKey = (origin: JavaScriptModuleOrigin): string =>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
JavaScriptSemanticGraphNode,
|
||||
JavaScriptSemanticGraphRelation,
|
||||
} from "./javascriptSemanticGraphSchemas.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { JavaScriptSemanticGraphUnknown } from "./javascriptSemanticGraphSchemas.js";
|
||||
import { isJavaScriptSemanticOwnershipRelation as ownershipRelation } from "./javascriptSemanticQueryRelations.js";
|
||||
import {
|
||||
@@ -147,7 +147,7 @@ const buildAdjacency = (
|
||||
}
|
||||
for (const entries of adjacency.values())
|
||||
entries.sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.relation.relation_id}\0${left.nextNodeId}`,
|
||||
`${right.relation.relation_id}\0${right.nextNodeId}`,
|
||||
),
|
||||
|
||||
@@ -4,7 +4,8 @@ import canonicalize from "canonicalize";
|
||||
|
||||
import type { JavaScriptSemanticGraph } from "./javascriptSemanticGraph.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "./javascriptSemanticGraphSchemas.js";
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { JavaScriptSemanticQueryInput } from "./javascriptSemanticQuerySchemas.js";
|
||||
|
||||
/** Canonical JSON used by semantic query commitments and ordering. */
|
||||
@@ -66,7 +67,7 @@ export const resolveJavaScriptSemanticQuerySeeds = (
|
||||
return graph.nodes
|
||||
.filter((node) => seedMatches(node, input, fingerprintFunctions))
|
||||
.map(({ node_id }) => node_id)
|
||||
.sort(compareCodePoints);
|
||||
.sort(compareUnicodeCodePoints);
|
||||
};
|
||||
|
||||
const seedMatches = (
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
ProjectedPropertyCoverage,
|
||||
ProjectedReturnField,
|
||||
} from "./javascriptExportShapeComparisonSchemas.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { semanticCallableIdForNode } from "./javascriptSemanticProjection.js";
|
||||
import {
|
||||
resolveSemanticBindingState,
|
||||
@@ -204,9 +204,9 @@ export const flattenSemanticReturnValue = (
|
||||
const fields: ProjectedReturnField[] = [];
|
||||
const propertyCoverage: ProjectedPropertyCoverage[] = [];
|
||||
flattenValue(value, "", fields, propertyCoverage);
|
||||
fields.sort((left, right) => compareCodePoints(left.path, right.path));
|
||||
fields.sort((left, right) => compareUnicodeCodePoints(left.path, right.path));
|
||||
propertyCoverage.sort((left, right) =>
|
||||
compareCodePoints(left.path, right.path),
|
||||
compareUnicodeCodePoints(left.path, right.path),
|
||||
);
|
||||
return { fields, propertyCoverage };
|
||||
};
|
||||
|
||||
@@ -3,12 +3,9 @@ import type {
|
||||
JavaScriptSemanticValue,
|
||||
} from "./javascriptSemanticValueTypes.js";
|
||||
|
||||
/** A container's own slot, using the value lattice's canonical presence model. */
|
||||
export type JavaScriptSemanticSlot = JavaScriptSemanticProperty;
|
||||
|
||||
/** Static container inventory; partial siblings do not weaken retained slots. */
|
||||
export interface JavaScriptSemanticContainer {
|
||||
readonly slots: readonly JavaScriptSemanticSlot[];
|
||||
readonly slots: readonly JavaScriptSemanticProperty[];
|
||||
readonly coverage:
|
||||
| { readonly status: "complete"; readonly omitted: 0 }
|
||||
| { readonly status: "partial"; readonly omitted: number | null };
|
||||
@@ -48,9 +45,9 @@ export function semanticContainer(
|
||||
export const semanticSlotAtPath = (
|
||||
value: JavaScriptSemanticValue,
|
||||
path: readonly string[],
|
||||
): JavaScriptSemanticSlot => {
|
||||
): JavaScriptSemanticProperty => {
|
||||
const leafName = path.at(-1) ?? "";
|
||||
let current: JavaScriptSemanticSlot = {
|
||||
let current: JavaScriptSemanticProperty = {
|
||||
name: "",
|
||||
presence: "present",
|
||||
value,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
type JavaScriptSemanticBindingState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import { semanticRequireOrigin } from "./javascriptSemanticRequireOrigin.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import {
|
||||
readExactJavaScriptLiteral,
|
||||
semanticStaticPropertyKey,
|
||||
@@ -300,7 +300,7 @@ const evaluateObject = (
|
||||
});
|
||||
}
|
||||
const properties = [...propertiesByName.values()].sort((left, right) =>
|
||||
compareCodePoints(left.name, right.name),
|
||||
compareUnicodeCodePoints(left.name, right.name),
|
||||
);
|
||||
return unknownProperties
|
||||
? {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
collectJavaScriptExports,
|
||||
fingerprintJavaScriptAst,
|
||||
} from "./javascriptAstFingerprint.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { addFindingOnce } from "./javascriptStaticAnalysisFindings.js";
|
||||
import {
|
||||
argumentValue,
|
||||
@@ -66,7 +66,7 @@ export const inspectBundlerRegistration = (
|
||||
unknown_async_chunk_keys:
|
||||
runtimeValue.unknownAsyncChunkKeys + recovered.unknownAsyncChunkKeys,
|
||||
modules: recovered.modules.sort((left, right) =>
|
||||
compareCodePoints(left.module_key, right.module_key),
|
||||
compareUnicodeCodePoints(left.module_key, right.module_key),
|
||||
),
|
||||
location: range(call),
|
||||
};
|
||||
@@ -107,7 +107,7 @@ export const inspectEsbuildWrapper = (
|
||||
async_chunk_keys: recovered.asyncChunkKeys,
|
||||
unknown_async_chunk_keys: recovered.unknownAsyncChunkKeys,
|
||||
modules: recovered.modules.sort((left, right) =>
|
||||
compareCodePoints(left.module_key, right.module_key),
|
||||
compareUnicodeCodePoints(left.module_key, right.module_key),
|
||||
),
|
||||
location: range(call),
|
||||
};
|
||||
@@ -225,7 +225,7 @@ const runtimeMetadata = (node: t.Node | null | undefined): RuntimeMetadata => {
|
||||
asyncChunkKeys: [],
|
||||
unknownAsyncChunkKeys: 0,
|
||||
};
|
||||
const entries = collectBundlerEntryModuleKeys(node, requireName);
|
||||
const entries = collectBundlerCallArgumentValues(node, requireName);
|
||||
const asyncChunks = collectBundlerAsyncChunkKeys(node, requireName);
|
||||
return {
|
||||
requireName,
|
||||
@@ -236,11 +236,6 @@ const runtimeMetadata = (node: t.Node | null | undefined): RuntimeMetadata => {
|
||||
};
|
||||
};
|
||||
|
||||
const collectBundlerEntryModuleKeys = (
|
||||
factory: BundlerFunction,
|
||||
requireName: string,
|
||||
): StaticValues => collectBundlerCallArgumentValues(factory, requireName);
|
||||
|
||||
const collectBundlerAsyncChunkKeys = (
|
||||
factory: BundlerFunction,
|
||||
requireName: string | null,
|
||||
@@ -277,7 +272,7 @@ const isBundlerFunction = (
|
||||
t.isObjectMethod(node);
|
||||
|
||||
const uniqueValues = (values: readonly string[], unknown = 0): StaticValues => {
|
||||
const unique = [...new Set(values)].sort(compareCodePoints);
|
||||
const unique = [...new Set(values)].sort(compareUnicodeCodePoints);
|
||||
return {
|
||||
values: unique,
|
||||
unknown,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
readExactJavaScriptLiteral,
|
||||
semanticStaticPropertyName,
|
||||
} from "./javascriptAstValues.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
|
||||
/** Explicit result for source text that Babel cannot parse. */
|
||||
export const failedJavaScriptStaticAnalysis = (): JavaScriptStaticAnalysis => ({
|
||||
@@ -156,17 +156,13 @@ export const staticArrayValues = (
|
||||
const value = argumentValue(element);
|
||||
return value === undefined ? [] : [value];
|
||||
});
|
||||
const values = [...new Set(staticValues)].sort(compareCodePoints);
|
||||
const values = [...new Set(staticValues)].sort(compareUnicodeCodePoints);
|
||||
return {
|
||||
values,
|
||||
unknown: array.elements.length - staticValues.length,
|
||||
};
|
||||
};
|
||||
|
||||
/** Resolve a path composed only from inert literal syntax. */
|
||||
export const staticPath = (node: t.Node): string | undefined =>
|
||||
staticPathAt(node);
|
||||
|
||||
/** Classify whether inert path syntax is a module specifier or file expression. */
|
||||
export const staticPathResolutionContext = (
|
||||
node: t.Node,
|
||||
@@ -175,13 +171,14 @@ export const staticPathResolutionContext = (
|
||||
? "filesystem-expression"
|
||||
: "module-specifier";
|
||||
|
||||
const staticPathAt = (node: t.Node): string | undefined => {
|
||||
/** Resolve a path composed only from inert literal syntax. */
|
||||
export const staticPath = (node: t.Node): string | undefined => {
|
||||
if (t.isStringLiteral(node)) return node.value;
|
||||
if (t.isTemplateLiteral(node) && node.expressions.length === 0)
|
||||
return stringValue(node);
|
||||
if (t.isBinaryExpression(node, { operator: "+" })) {
|
||||
const left = staticPathAt(node.left);
|
||||
const right = staticPathAt(node.right);
|
||||
const left = staticPath(node.left);
|
||||
const right = staticPath(node.right);
|
||||
return left === undefined || right === undefined
|
||||
? undefined
|
||||
: `${left}${right}`;
|
||||
@@ -215,7 +212,7 @@ const staticCallPath = (
|
||||
const parts: string[] = [];
|
||||
for (const argument of node.arguments) {
|
||||
if (isDirectoryIdentity(argumentNode(argument))) continue;
|
||||
const value = t.isNode(argument) ? staticPathAt(argument) : undefined;
|
||||
const value = t.isNode(argument) ? staticPath(argument) : undefined;
|
||||
if (value === undefined) return undefined;
|
||||
parts.push(value);
|
||||
}
|
||||
@@ -228,7 +225,7 @@ const staticFileUrlPath = (
|
||||
if (argument === undefined) return undefined;
|
||||
return isSourceFileUrl(argument)
|
||||
? sourceRelativeFileUrlPath(argument)
|
||||
: staticPathAt(argument);
|
||||
: staticPath(argument);
|
||||
};
|
||||
|
||||
/** Project a known source-relative URL through the actual file URL decoder. */
|
||||
@@ -284,7 +281,7 @@ const staticResolvedPath = (
|
||||
parts.push(null);
|
||||
continue;
|
||||
}
|
||||
const value = t.isNode(argument) ? staticPathAt(argument) : undefined;
|
||||
const value = t.isNode(argument) ? staticPath(argument) : undefined;
|
||||
// Validate every argument, including those before the last anchor.
|
||||
if (value === undefined) return undefined;
|
||||
parts.push(value);
|
||||
@@ -684,7 +681,7 @@ export const sortedUnique = <Value>(
|
||||
key: (value: Value) => string,
|
||||
): Value[] =>
|
||||
[...new Map(values.map((value) => [key(value), value])).values()].sort(
|
||||
(left, right) => compareCodePoints(key(left), key(right)),
|
||||
(left, right) => compareUnicodeCodePoints(key(left), key(right)),
|
||||
);
|
||||
|
||||
/** Hash exact UTF-8 source text. */
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
createJavaScriptApplicationNode,
|
||||
} from "./javascriptApplicationGraph.js";
|
||||
import { createHistoricalSourceGraph } from "../referenceSourceGraph.js";
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { compareSourceToBundle } from "./sourceToBundleComparison.js";
|
||||
import {
|
||||
SOURCE_TO_BUNDLE_SIGNAL_WEIGHTS,
|
||||
@@ -365,7 +365,7 @@ const historicalGraph = (
|
||||
content_state: "hashed" as const,
|
||||
limitations: [],
|
||||
})),
|
||||
].sort((left, right) => compareCodePoints(left.path, right.path)),
|
||||
].sort((left, right) => compareUnicodeCodePoints(left.path, right.path)),
|
||||
relationships: [],
|
||||
parse_failures: [],
|
||||
exclusions: [],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { compareCodePoints, uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { absenceClaimable, canonicalDigest } from "../comparisonSemantics.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import { absenceClaimable } from "../comparisonSemantics.js";
|
||||
import { digestCanonicalValue } from "../canonicalDigest.js";
|
||||
|
||||
import type { JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
|
||||
import type { HistoricalSourceGraph } from "../referenceSourceGraph.js";
|
||||
@@ -84,7 +86,7 @@ export const compareSourceToBundle = (
|
||||
};
|
||||
return sourceToBundleComparisonResultSchema.parse({
|
||||
...semanticResult,
|
||||
comparison_id: `stbc_${canonicalDigest(semanticResult, "Source-to-bundle comparison")}`,
|
||||
comparison_id: `stbc_${digestCanonicalValue(semanticResult, "Source-to-bundle comparison")}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -316,7 +318,7 @@ const mappingId = (
|
||||
input: SourceToBundleComparisonInput,
|
||||
item: Omit<SourceToBundleComparisonItem, "mapping_id">,
|
||||
): string =>
|
||||
`stbc_item_${canonicalDigest(
|
||||
`stbc_item_${digestCanonicalValue(
|
||||
{
|
||||
reference_root_sha256: input.reference.root_sha256,
|
||||
application_graph_id: input.application.graph.graph_id,
|
||||
@@ -382,4 +384,4 @@ const compareCandidates = (
|
||||
right: SourceToBundleCandidate,
|
||||
): number =>
|
||||
right.score - left.score ||
|
||||
compareCodePoints(left.current_node_id, right.current_node_id);
|
||||
compareUnicodeCodePoints(left.current_node_id, right.current_node_id);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { posix } from "node:path";
|
||||
|
||||
import { compareCodePoints } from "../canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { HistoricalSourceGraph } from "../referenceSourceGraph.js";
|
||||
import {
|
||||
@@ -70,7 +70,9 @@ export const sourceBearingNodes = (
|
||||
): ApplicationNode[] =>
|
||||
nodes
|
||||
.filter(({ kind }) => RELEVANT_NODE_KINDS.has(kind))
|
||||
.sort((left, right) => compareCodePoints(left.node_id, right.node_id));
|
||||
.sort((left, right) =>
|
||||
compareUnicodeCodePoints(left.node_id, right.node_id),
|
||||
);
|
||||
|
||||
/** Keep historical source files in stable path order. */
|
||||
export const historicalSourceFiles = (
|
||||
@@ -81,7 +83,7 @@ export const historicalSourceFiles = (
|
||||
(entry): entry is SourceFile =>
|
||||
entry.kind === "file" && entry.classifications.includes("source"),
|
||||
)
|
||||
.sort((left, right) => compareCodePoints(left.path, right.path));
|
||||
.sort((left, right) => compareUnicodeCodePoints(left.path, right.path));
|
||||
|
||||
/** Build deterministic candidate indices without assigning fuzzy matches. */
|
||||
export const buildSourceToBundleCandidateIndex = (
|
||||
@@ -130,7 +132,7 @@ export const candidateIdsForSource = (
|
||||
const output: string[] = [];
|
||||
for (const group of groups) {
|
||||
if (group === undefined) continue;
|
||||
for (const nodeId of [...group].sort(compareCodePoints)) {
|
||||
for (const nodeId of [...group].sort(compareUnicodeCodePoints)) {
|
||||
if (seen.has(nodeId)) continue;
|
||||
seen.add(nodeId);
|
||||
output.push(nodeId);
|
||||
@@ -256,7 +258,7 @@ const signal = (
|
||||
kind,
|
||||
weight: signalWeight(kind),
|
||||
source_value: sourceValue,
|
||||
current_values: [...new Set(currentValues)].sort(compareCodePoints),
|
||||
current_values: [...new Set(currentValues)].sort(compareUnicodeCodePoints),
|
||||
});
|
||||
|
||||
const candidateConfidence = (
|
||||
@@ -325,7 +327,7 @@ const uniquePaths = (paths: readonly CurrentPath[]): CurrentPath[] =>
|
||||
]),
|
||||
).values(),
|
||||
].sort((left, right) =>
|
||||
compareCodePoints(
|
||||
compareUnicodeCodePoints(
|
||||
`${left.kind}\0${left.value}`,
|
||||
`${right.kind}\0${right.value}`,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
||||
|
||||
const jsonValueTypeSchema = z.enum([
|
||||
"object",
|
||||
@@ -93,7 +93,7 @@ export const inferJsonShape = (text: string): JsonShape | null => {
|
||||
.sort(
|
||||
(left, right) =>
|
||||
left.path.localeCompare(right.path) ||
|
||||
compareCodePoints(left.path, right.path),
|
||||
compareUnicodeCodePoints(left.path, right.path),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,10 +7,6 @@ export const localPathStringSchema = z
|
||||
.min(1)
|
||||
.regex(/^[^\u0000]*$/u, "Local filesystem paths cannot contain NUL");
|
||||
|
||||
/** True when the value is an absolute filesystem path on the host platform. */
|
||||
export const isAbsoluteLocalPath = (value: string): boolean =>
|
||||
isAbsolute(value);
|
||||
|
||||
/** Test lexical containment on the host platform; callers must resolve symlinks first. */
|
||||
export const isPathWithinRoot = (root: string, path: string): boolean => {
|
||||
const value = relative(root, path);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user