From 765e809e366eff21b4b9c224f1a813ad64245ea3 Mon Sep 17 00:00:00 2001
From: morluto <76467478+morluto@users.noreply.github.com>
Date: Fri, 9 Oct 2026 23:17:17 +0800
Subject: [PATCH] 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.
---
AGENTS.md | 2 +-
docs/architecture.mermaid | 2 +-
scripts/verify/javascript/digests.mjs | 4 +-
src/application/BrowserScenarioEvidence.ts | 4 +-
src/application/DirectAnalysisStatus.ts | 21 +------
src/application/SetupClientConfiguration.ts | 4 +-
.../javascript/ElectronActiveEvidence.ts | 4 +-
.../javascript/ElectronBoundaryAnalysis.ts | 4 +-
.../JavaScriptArtifactGraphBuilder.ts | 30 ++--------
.../javascript/JavaScriptJsonModules.ts | 4 +-
.../JavaScriptModuleRelationships.ts | 30 +++-------
.../JavaScriptSemanticGraphProjection.ts | 4 +-
.../JavaScriptSemanticGraphValueProjection.ts | 8 ++-
src/artifacts/ArtifactProviderMetadata.ts | 4 +-
src/artifacts/DirectoryArtifactReader.ts | 4 +-
.../extraction/ArtifactExtraction.ts | 12 ++--
.../inventory/ArtifactGraphConstruction.ts | 16 +++---
src/artifacts/inventory/scanCanonical.ts | 10 ++--
.../javascript/JavaScriptArtifactFiles.ts | 6 +-
src/browser/CdpBrowserProvider.ts | 7 ++-
src/browser/CdpCaptureStorageFingerprints.ts | 6 +-
src/browser/CdpElectronInspection.ts | 4 +-
src/browser/CdpElectronProvider.ts | 8 +--
src/browser/CdpElectronScripts.ts | 4 +-
src/cli/utilityCommands.ts | 17 +-----
src/composition/directAnalysis.ts | 8 +--
src/contracts/artifactComparisonExample.ts | 10 ++--
.../javascriptRuntimeReconciliationExample.ts | 8 +--
src/contracts/managed/managedToolContracts.ts | 8 +--
.../mobileApplicationGraphExamples.ts | 10 ++--
src/contracts/sessionLifecycleInputs.ts | 10 ++--
src/contracts/sessionStatusContract.ts | 8 +--
src/contracts/sessionToolContracts.ts | 8 +--
src/contracts/sessionToolSchemas.ts | 8 +--
src/domain/apple/appleApplication.test.ts | 16 +++---
src/domain/artifactComparison.ts | 8 +--
src/domain/artifactIdentity.ts | 16 +++---
src/domain/artifactInspection.ts | 16 +++---
src/domain/artifactInventoryEvidence.ts | 10 ++--
src/domain/browserScenarioNormalization.ts | 6 +-
src/domain/browserScenarioValues.ts | 14 ++---
src/domain/callPath.ts | 8 +--
src/domain/canonicalOrdering.ts | 16 +-----
src/domain/comparisonSemantics.ts | 10 ----
src/domain/firmware/firmwareAnalysis.ts | 7 ++-
src/domain/functionComparison.test.ts | 11 ++--
src/domain/functionComparisonNormalization.ts | 4 +-
src/domain/functionComparisonResults.ts | 4 +-
.../javascript/electronActiveObservation.ts | 5 +-
.../electronStaticAnalysisBrowser.ts | 6 +-
.../electronStaticAnalysisNative.ts | 4 +-
.../electronStaticAnalysisValues.ts | 4 +-
.../javascriptApplicationChangeGraph.ts | 11 ++--
.../javascript/javascriptApplicationGraph.ts | 30 +++++-----
.../javascriptApplicationVersionComparison.ts | 4 +-
...riptApplicationVersionComparisonSchemas.ts | 21 ++++---
.../javascriptApplicationVersionItems.ts | 23 ++++----
.../javascriptApplicationVersionKeys.ts | 12 ++--
.../javascript/javascriptAstFingerprint.ts | 4 +-
.../javascriptExportShapeComparison.ts | 5 +-
.../javascriptExportShapeComparisonSchemas.ts | 32 +++++------
.../javascriptExportShapeSelection.ts | 4 +-
.../javascriptExportShapeVariants.ts | 5 +-
.../javascript/javascriptFeatureSeed.ts | 8 ++-
.../javascript/javascriptFeatureTrace.ts | 15 +++--
.../javascript/javascriptFeatureTraversal.ts | 10 ++--
.../javascript/javascriptNativeHandoff.ts | 14 +++--
.../javascriptRuntimeReconciliationActive.ts | 6 +-
.../javascriptRuntimeReconciliationGraph.ts | 11 ++--
...javascriptRuntimeReconciliationMatching.ts | 10 ++--
.../javascriptRuntimeReconciliationParsing.ts | 21 ++++---
.../javascriptRuntimeReconciliationResult.ts | 4 +-
.../javascriptRuntimeReconciliationSchemas.ts | 4 +-
.../javascriptRuntimeStaticCandidates.ts | 13 +++--
.../javascriptSemanticAnalysis.calls.test.ts | 6 +-
.../javascriptSemanticAnalysis.fixture.ts | 6 ++
...vascriptSemanticAnalysis.structure.test.ts | 15 ++---
.../javascriptSemanticCallResolution.ts | 8 ++-
.../javascriptSemanticChildProcesses.ts | 4 +-
.../javascript/javascriptSemanticCoverage.ts | 33 +++++++++++
.../javascriptSemanticDataEffects.ts | 4 +-
.../javascriptSemanticFingerprints.ts | 4 +-
.../javascript/javascriptSemanticGraph.ts | 50 ++++++++++-------
src/domain/javascript/javascriptSemanticIr.ts | 18 ------
.../javascriptSemanticPrimitives.ts | 4 +-
.../javascriptSemanticProjection.ts | 8 ++-
.../javascript/javascriptSemanticPromises.ts | 4 +-
.../javascriptSemanticProvenance.ts | 4 +-
.../javascript/javascriptSemanticQuery.ts | 4 +-
.../javascriptSemanticQueryIdentity.ts | 5 +-
.../javascript/javascriptSemanticReturns.ts | 6 +-
.../javascript/javascriptSemanticSlots.ts | 9 +--
.../javascript/javascriptSemanticValues.ts | 4 +-
.../javascriptStaticAnalysisBundler.ts | 15 ++---
.../javascriptStaticAnalysisHelpers.ts | 23 ++++----
.../sourceToBundleComparison.test.ts | 4 +-
.../javascript/sourceToBundleComparison.ts | 12 ++--
.../javascript/sourceToBundleSignals.ts | 14 +++--
src/domain/jsonShape.ts | 4 +-
src/domain/localPath.ts | 4 --
.../managed/managedMemberComparisonMatch.ts | 14 ++---
src/domain/referenceSourceGraph.ts | 15 +++--
src/domain/referenceSourceImportParsing.ts | 5 +-
src/domain/webCaptureDiff.ts | 56 ++++++++++---------
src/dotnet/ManagedStaticProviderMetadata.ts | 4 +-
src/main/transport.ts | 7 +--
src/main/types.ts | 6 +-
src/native/NativeMacOSProviderMetadata.ts | 4 +-
src/reference/ReferenceSourceReaderEntries.ts | 6 +-
src/reference/ReferenceSourceReaderFile.ts | 10 ++--
src/reference/ReferenceSourceReaderTypes.ts | 8 +--
.../ReferenceSourceReaderValidate.ts | 12 ++--
src/server/createServer.ts | 4 +-
src/server/registerSessionStatusTool.ts | 4 +-
src/server/registerSessionTools.ts | 10 ++--
src/server/sessionAvailabilityPolicy.ts | 8 +--
.../artifactDirectoryIdentity.test.ts | 10 ++--
.../process/cliEnvironmentSeam.test.ts | 2 +-
.../javascriptArtifactReconstruction.test.ts | 20 +++++++
119 files changed, 577 insertions(+), 629 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 1acb82552..abfd2e301 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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
diff --git a/docs/architecture.mermaid b/docs/architecture.mermaid
index b02bfafbe..83c6e5cb2 100644
--- a/docs/architecture.mermaid
+++ b/docs/architecture.mermaid
@@ -58,7 +58,7 @@ graph TD
end
subgraph "Configuration"
- CONFIG["src/config.ts + src/config/
Zod-validated env parsing
provider settings and origins"]
+ CONFIG["src/config/
Zod-validated env parsing
provider settings and origins"]
end
subgraph "Tests"
diff --git a/scripts/verify/javascript/digests.mjs b/scripts/verify/javascript/digests.mjs
index 98530b07b..03c61bd81 100644
--- a/scripts/verify/javascript/digests.mjs
+++ b/scripts/verify/javascript/digests.mjs
@@ -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,
diff --git a/src/application/BrowserScenarioEvidence.ts b/src/application/BrowserScenarioEvidence.ts
index e2009b16e..5dae07c21 100644
--- a/src/application/BrowserScenarioEvidence.ts
+++ b/src/application/BrowserScenarioEvidence.ts
@@ -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. */
diff --git a/src/application/DirectAnalysisStatus.ts b/src/application/DirectAnalysisStatus.ts
index 5548e61c4..cc6df7901 100644
--- a/src/application/DirectAnalysisStatus.ts
+++ b/src/application/DirectAnalysisStatus.ts
@@ -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);
diff --git a/src/application/SetupClientConfiguration.ts b/src/application/SetupClientConfiguration.ts
index 2d5343a58..f88a1b5bf 100644
--- a/src/application/SetupClientConfiguration.ts
+++ b/src/application/SetupClientConfiguration.ts
@@ -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 => {
if (client.format === undefined || client.format === "unsupported")
return Promise.resolve({ status: "failed", reason: "readback" });
diff --git a/src/application/javascript/ElectronActiveEvidence.ts b/src/application/javascript/ElectronActiveEvidence.ts
index 9d8e199cb..489cd7caa 100644
--- a/src/application/javascript/ElectronActiveEvidence.ts
+++ b/src/application/javascript/ElectronActiveEvidence.ts
@@ -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"),
};
};
diff --git a/src/application/javascript/ElectronBoundaryAnalysis.ts b/src/application/javascript/ElectronBoundaryAnalysis.ts
index c1f842317..a718f6ed0 100644
--- a/src/application/javascript/ElectronBoundaryAnalysis.ts
+++ b/src/application/javascript/ElectronBoundaryAnalysis.ts
@@ -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 = (
diff --git a/src/application/javascript/JavaScriptArtifactGraphBuilder.ts b/src/application/javascript/JavaScriptArtifactGraphBuilder.ts
index 26d6b7b2e..0c39ce458 100644
--- a/src/application/javascript/JavaScriptArtifactGraphBuilder.ts
+++ b/src/application/javascript/JavaScriptArtifactGraphBuilder.ts
@@ -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",
diff --git a/src/application/javascript/JavaScriptJsonModules.ts b/src/application/javascript/JavaScriptJsonModules.ts
index 3733db5cb..ac18ea367 100644
--- a/src/application/javascript/JavaScriptJsonModules.ts
+++ b/src/application/javascript/JavaScriptJsonModules.ts
@@ -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 {
diff --git a/src/application/javascript/JavaScriptModuleRelationships.ts b/src/application/javascript/JavaScriptModuleRelationships.ts
index 36868fb62..8e12f3beb 100644
--- a/src/application/javascript/JavaScriptModuleRelationships.ts
+++ b/src/application/javascript/JavaScriptModuleRelationships.ts
@@ -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,
);
};
diff --git a/src/application/javascript/JavaScriptSemanticGraphProjection.ts b/src/application/javascript/JavaScriptSemanticGraphProjection.ts
index b83f2e970..182a67612 100644
--- a/src/application/javascript/JavaScriptSemanticGraphProjection.ts
+++ b/src/application/javascript/JavaScriptSemanticGraphProjection.ts
@@ -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;
diff --git a/src/application/javascript/JavaScriptSemanticGraphValueProjection.ts b/src/application/javascript/JavaScriptSemanticGraphValueProjection.ts
index bb2bbd520..cb2596b28 100644
--- a/src/application/javascript/JavaScriptSemanticGraphValueProjection.ts
+++ b/src/application/javascript/JavaScriptSemanticGraphValueProjection.ts
@@ -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",
diff --git a/src/artifacts/ArtifactProviderMetadata.ts b/src/artifacts/ArtifactProviderMetadata.ts
index 3e350508e..aa36eeaf4 100644
--- a/src/artifacts/ArtifactProviderMetadata.ts
+++ b/src/artifacts/ArtifactProviderMetadata.ts
@@ -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,
diff --git a/src/artifacts/DirectoryArtifactReader.ts b/src/artifacts/DirectoryArtifactReader.ts
index 1446294b8..673a84d37 100644
--- a/src/artifacts/DirectoryArtifactReader.ts
+++ b/src/artifacts/DirectoryArtifactReader.ts
@@ -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);
diff --git a/src/artifacts/extraction/ArtifactExtraction.ts b/src/artifacts/extraction/ArtifactExtraction.ts
index dcd933e7e..b87beb09a 100644
--- a/src/artifacts/extraction/ArtifactExtraction.ts
+++ b/src/artifacts/extraction/ArtifactExtraction.ts
@@ -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,
diff --git a/src/artifacts/inventory/ArtifactGraphConstruction.ts b/src/artifacts/inventory/ArtifactGraphConstruction.ts
index fd3b0dad3..55d391017 100644
--- a/src/artifacts/inventory/ArtifactGraphConstruction.ts
+++ b/src/artifacts/inventory/ArtifactGraphConstruction.ts
@@ -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,
@@ -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",
diff --git a/src/artifacts/inventory/scanCanonical.ts b/src/artifacts/inventory/scanCanonical.ts
index 8bf1c1a35..da046f18b 100644
--- a/src/artifacts/inventory/scanCanonical.ts
+++ b/src/artifacts/inventory/scanCanonical.ts
@@ -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 = (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,
diff --git a/src/artifacts/javascript/JavaScriptArtifactFiles.ts b/src/artifacts/javascript/JavaScriptArtifactFiles.ts
index 1e5a4e5c9..a726fd88a 100644
--- a/src/artifacts/javascript/JavaScriptArtifactFiles.ts
+++ b/src/artifacts/javascript/JavaScriptArtifactFiles.ts
@@ -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,
diff --git a/src/browser/CdpBrowserProvider.ts b/src/browser/CdpBrowserProvider.ts
index a527b2abf..a97ef7358 100644
--- a/src/browser/CdpBrowserProvider.ts
+++ b/src/browser/CdpBrowserProvider.ts
@@ -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,
+ });
diff --git a/src/browser/CdpCaptureStorageFingerprints.ts b/src/browser/CdpCaptureStorageFingerprints.ts
index 476d75763..02a52b3be 100644
--- a/src/browser/CdpCaptureStorageFingerprints.ts
+++ b/src/browser/CdpCaptureStorageFingerprints.ts
@@ -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)]),
);
};
diff --git a/src/browser/CdpElectronInspection.ts b/src/browser/CdpElectronInspection.ts
index 646ddf233..ca49edabd 100644
--- a/src/browser/CdpElectronInspection.ts
+++ b/src/browser/CdpElectronInspection.ts
@@ -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({
diff --git a/src/browser/CdpElectronProvider.ts b/src/browser/CdpElectronProvider.ts
index 39a88eb4f..8191a8ec2 100644
--- a/src/browser/CdpElectronProvider.ts
+++ b/src/browser/CdpElectronProvider.ts
@@ -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,
+ });
diff --git a/src/browser/CdpElectronScripts.ts b/src/browser/CdpElectronScripts.ts
index 2bf470af6..850cfd5f5 100644
--- a/src/browser/CdpElectronScripts.ts
+++ b/src/browser/CdpElectronScripts.ts
@@ -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;
diff --git a/src/cli/utilityCommands.ts b/src/cli/utilityCommands.ts
index 2aeef4228..9b8696bcd 100644
--- a/src/cli/utilityCommands.ts
+++ b/src/cli/utilityCommands.ts
@@ -22,10 +22,7 @@ export const registerUtilityCommands = (
cli: CliInstance,
logger: Logger,
environment: Readonly>,
- analysis: Pick<
- DirectAnalysis,
- "runProviderAnalysis" | "runProviderStatus" | "runCapabilityStatus"
- >,
+ analysis: Pick,
): void => {
const { runProviderAnalysis } = analysis;
registerCapabilityCommands(cli, logger, analysis);
@@ -129,10 +126,7 @@ const invalidNativeUiScenarioInput = (
const registerCapabilityCommands = (
cli: CliInstance,
logger: Logger,
- {
- runProviderStatus,
- runCapabilityStatus,
- }: Pick,
+ { runSessionStatus }: Pick,
): 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)),
});
}
};
diff --git a/src/composition/directAnalysis.ts b/src/composition/directAnalysis.ts
index 3a6b0d212..a5c6706ef 100644
--- a/src/composition/directAnalysis.ts
+++ b/src/composition/directAnalysis.ts
@@ -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),
};
};
diff --git a/src/contracts/artifactComparisonExample.ts b/src/contracts/artifactComparisonExample.ts
index 48438d562..b0f4ac767 100644
--- a/src/contracts/artifactComparisonExample.ts
+++ b/src/contracts/artifactComparisonExample.ts
@@ -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,
diff --git a/src/contracts/javascript/javascriptRuntimeReconciliationExample.ts b/src/contracts/javascript/javascriptRuntimeReconciliationExample.ts
index 6f7f3bd0f..127e13799 100644
--- a/src/contracts/javascript/javascriptRuntimeReconciliationExample.ts
+++ b/src/contracts/javascript/javascriptRuntimeReconciliationExample.ts
@@ -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],
};
diff --git a/src/contracts/managed/managedToolContracts.ts b/src/contracts/managed/managedToolContracts.ts
index 8b46881b2..2ff8d36bb 100644
--- a/src/contracts/managed/managedToolContracts.ts
+++ b/src/contracts/managed/managedToolContracts.ts
@@ -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)",
})
diff --git a/src/contracts/mobileApplicationGraphExamples.ts b/src/contracts/mobileApplicationGraphExamples.ts
index 299777b4e..cb946d495 100644
--- a/src/contracts/mobileApplicationGraphExamples.ts
+++ b/src/contracts/mobileApplicationGraphExamples.ts
@@ -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,
diff --git a/src/contracts/sessionLifecycleInputs.ts b/src/contracts/sessionLifecycleInputs.ts
index a230c9a4f..ca11b03a0 100644
--- a/src/contracts/sessionLifecycleInputs.ts
+++ b/src/contracts/sessionLifecycleInputs.ts
@@ -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)",
})
diff --git a/src/contracts/sessionStatusContract.ts b/src/contracts/sessionStatusContract.ts
index 7b509b39a..68385a728 100644
--- a/src/contracts/sessionStatusContract.ts
+++ b/src/contracts/sessionStatusContract.ts
@@ -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)",
})
diff --git a/src/contracts/sessionToolContracts.ts b/src/contracts/sessionToolContracts.ts
index 5436d445e..6c610b984 100644
--- a/src/contracts/sessionToolContracts.ts
+++ b/src/contracts/sessionToolContracts.ts
@@ -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)",
})
diff --git a/src/contracts/sessionToolSchemas.ts b/src/contracts/sessionToolSchemas.ts
index cac69149d..191acdb63 100644
--- a/src/contracts/sessionToolSchemas.ts
+++ b/src/contracts/sessionToolSchemas.ts
@@ -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)",
})
diff --git a/src/domain/apple/appleApplication.test.ts b/src/domain/apple/appleApplication.test.ts
index 8e7b9453b..5c846611a 100644
--- a/src/domain/apple/appleApplication.test.ts
+++ b/src/domain/apple/appleApplication.test.ts
@@ -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",
)}`,
diff --git a/src/domain/artifactComparison.ts b/src/domain/artifactComparison.ts
index f02379fa6..3a1101182 100644
--- a/src/domain/artifactComparison.ts
+++ b/src/domain/artifactComparison.ts
@@ -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;
};
diff --git a/src/domain/artifactIdentity.ts b/src/domain/artifactIdentity.ts
index f97cc419b..ad773de33 100644
--- a/src/domain/artifactIdentity.ts
+++ b/src/domain/artifactIdentity.ts
@@ -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")}`;
diff --git a/src/domain/artifactInspection.ts b/src/domain/artifactInspection.ts
index 2c9238659..f4c197b9c 100644
--- a/src/domain/artifactInspection.ts
+++ b/src/domain/artifactInspection.ts
@@ -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,
};
};
diff --git a/src/domain/artifactInventoryEvidence.ts b/src/domain/artifactInventoryEvidence.ts
index d24d8266f..a1c74794a 100644
--- a/src/domain/artifactInventoryEvidence.ts
+++ b/src/domain/artifactInventoryEvidence.ts
@@ -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 &&
diff --git a/src/domain/browserScenarioNormalization.ts b/src/domain/browserScenarioNormalization.ts
index 08c889ab9..6af5fb2ac 100644
--- a/src/domain/browserScenarioNormalization.ts
+++ b/src/domain/browserScenarioNormalization.ts
@@ -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,
diff --git a/src/domain/browserScenarioValues.ts b/src/domain/browserScenarioValues.ts
index dd47a0081..544d4c222 100644
--- a/src/domain/browserScenarioValues.ts
+++ b/src/domain/browserScenarioValues.ts
@@ -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()
diff --git a/src/domain/callPath.ts b/src/domain/callPath.ts
index 68df8db68..bc833828f 100644
--- a/src/domain/callPath.ts
+++ b/src/domain/callPath.ts
@@ -157,9 +157,9 @@ const parseSnapshots = (
const snapshots = new Map();
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): string[] =>
[
...new Set([...snapshots].flatMap((snapshot) => snapshotLinks(snapshot))),
].sort((left, right) => left.localeCompare(right));
-
-const normalizeAddress = (input: string): string => parseCallPathAddress(input);
diff --git a/src/domain/canonicalOrdering.ts b/src/domain/canonicalOrdering.ts
index 0675c081e..9ab153d34 100644
--- a/src/domain/canonicalOrdering.ts
+++ b/src/domain/canonicalOrdering.ts
@@ -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 = (
values: readonly Value[],
-): Value[] => [...new Set(values)].sort(compareCodePoints);
+): Value[] => [...new Set(values)].sort(compareUnicodeCodePoints);
diff --git a/src/domain/comparisonSemantics.ts b/src/domain/comparisonSemantics.ts
index 9c005d124..f697e6a86 100644
--- a/src/domain/comparisonSemantics.ts
+++ b/src/domain/comparisonSemantics.ts
@@ -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,
diff --git a/src/domain/firmware/firmwareAnalysis.ts b/src/domain/firmware/firmwareAnalysis.ts
index abea685e7..00f6675d0 100644
--- a/src/domain/firmware/firmwareAnalysis.ts
+++ b/src/domain/firmware/firmwareAnalysis.ts
@@ -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"),
diff --git a/src/domain/functionComparison.test.ts b/src/domain/functionComparison.test.ts
index f7fbc258a..665635c6b 100644
--- a/src/domain/functionComparison.test.ts
+++ b/src/domain/functionComparison.test.ts
@@ -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),
});
});
});
diff --git a/src/domain/functionComparisonNormalization.ts b/src/domain/functionComparisonNormalization.ts
index ea7b1defc..5bf43a972 100644
--- a/src/domain/functionComparisonNormalization.ts
+++ b/src/domain/functionComparisonNormalization.ts
@@ -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 =>
diff --git a/src/domain/functionComparisonResults.ts b/src/domain/functionComparisonResults.ts
index b605b947f..95465c3e7 100644
--- a/src/domain/functionComparisonResults.ts
+++ b/src/domain/functionComparisonResults.ts
@@ -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");
diff --git a/src/domain/javascript/electronActiveObservation.ts b/src/domain/javascript/electronActiveObservation.ts
index 0b4de6073..9f2e90b03 100644
--- a/src/domain/javascript/electronActiveObservation.ts
+++ b/src/domain/javascript/electronActiveObservation.ts
@@ -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)",
});
diff --git a/src/domain/javascript/electronStaticAnalysisBrowser.ts b/src/domain/javascript/electronStaticAnalysisBrowser.ts
index 465fcf683..f29e7cf8f 100644
--- a/src/domain/javascript/electronStaticAnalysisBrowser.ts
+++ b/src/domain/javascript/electronStaticAnalysisBrowser.ts
@@ -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,
diff --git a/src/domain/javascript/electronStaticAnalysisNative.ts b/src/domain/javascript/electronStaticAnalysisNative.ts
index 541645c32..1aef90f1a 100644
--- a/src/domain/javascript/electronStaticAnalysisNative.ts
+++ b/src/domain/javascript/electronStaticAnalysisNative.ts
@@ -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,
diff --git a/src/domain/javascript/electronStaticAnalysisValues.ts b/src/domain/javascript/electronStaticAnalysisValues.ts
index 0311009f9..796c5a397 100644
--- a/src/domain/javascript/electronStaticAnalysisValues.ts
+++ b/src/domain/javascript/electronStaticAnalysisValues.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptApplicationChangeGraph.ts b/src/domain/javascript/javascriptApplicationChangeGraph.ts
index e21fb8b6c..ce0e136b3 100644
--- a/src/domain/javascript/javascriptApplicationChangeGraph.ts
+++ b/src/domain/javascript/javascriptApplicationChangeGraph.ts
@@ -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),
);
diff --git a/src/domain/javascript/javascriptApplicationGraph.ts b/src/domain/javascript/javascriptApplicationGraph.ts
index 56c39dfb6..ba122e1f0 100644
--- a/src/domain/javascript/javascriptApplicationGraph.ts
+++ b/src/domain/javascript/javascriptApplicationGraph.ts
@@ -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) => ({
});
const nodeId = (node: Pick): 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;
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",
);
diff --git a/src/domain/javascript/javascriptApplicationVersionComparison.ts b/src/domain/javascript/javascriptApplicationVersionComparison.ts
index dd340c298..50f82c335 100644
--- a/src/domain/javascript/javascriptApplicationVersionComparison.ts
+++ b/src/domain/javascript/javascriptApplicationVersionComparison.ts
@@ -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")}`,
});
};
diff --git a/src/domain/javascript/javascriptApplicationVersionComparisonSchemas.ts b/src/domain/javascript/javascriptApplicationVersionComparisonSchemas.ts
index 49488de51..e30c74205 100644
--- a/src/domain/javascript/javascriptApplicationVersionComparisonSchemas.ts
+++ b/src/domain/javascript/javascriptApplicationVersionComparisonSchemas.ts
@@ -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 = {
diff --git a/src/domain/javascript/javascriptApplicationVersionItems.ts b/src/domain/javascript/javascriptApplicationVersionItems.ts
index a7784c195..7e931904f 100644
--- a/src/domain/javascript/javascriptApplicationVersionItems.ts
+++ b/src/domain/javascript/javascriptApplicationVersionItems.ts
@@ -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: 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 => {
diff --git a/src/domain/javascript/javascriptApplicationVersionKeys.ts b/src/domain/javascript/javascriptApplicationVersionKeys.ts
index e9536f3e0..134acd657 100644
--- a/src/domain/javascript/javascriptApplicationVersionKeys.ts
+++ b/src/domain/javascript/javascriptApplicationVersionKeys.ts
@@ -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[] =>
[...nodes].sort((left, right) =>
- compareCodePoints(left.node_id, right.node_id),
+ compareUnicodeCodePoints(left.node_id, right.node_id),
);
diff --git a/src/domain/javascript/javascriptAstFingerprint.ts b/src/domain/javascript/javascriptAstFingerprint.ts
index fd12f6435..8447de0eb 100644
--- a/src/domain/javascript/javascriptAstFingerprint.ts
+++ b/src/domain/javascript/javascriptAstFingerprint.ts
@@ -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),
};
};
diff --git a/src/domain/javascript/javascriptExportShapeComparison.ts b/src/domain/javascript/javascriptExportShapeComparison.ts
index 63cb22dd0..8fb493839 100644
--- a/src/domain/javascript/javascriptExportShapeComparison.ts
+++ b/src/domain/javascript/javascriptExportShapeComparison.ts
@@ -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);
diff --git a/src/domain/javascript/javascriptExportShapeComparisonSchemas.ts b/src/domain/javascript/javascriptExportShapeComparisonSchemas.ts
index 9e0671d2b..808066c4d 100644
--- a/src/domain/javascript/javascriptExportShapeComparisonSchemas.ts
+++ b/src/domain/javascript/javascriptExportShapeComparisonSchemas.ts
@@ -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
diff --git a/src/domain/javascript/javascriptExportShapeSelection.ts b/src/domain/javascript/javascriptExportShapeSelection.ts
index 22510f617..afc6862e7 100644
--- a/src/domain/javascript/javascriptExportShapeSelection.ts
+++ b/src/domain/javascript/javascriptExportShapeSelection.ts
@@ -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)),
);
};
diff --git a/src/domain/javascript/javascriptExportShapeVariants.ts b/src/domain/javascript/javascriptExportShapeVariants.ts
index e9420daed..bedf57cbe 100644
--- a/src/domain/javascript/javascriptExportShapeVariants.ts
+++ b/src/domain/javascript/javascriptExportShapeVariants.ts
@@ -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),
);
diff --git a/src/domain/javascript/javascriptFeatureSeed.ts b/src/domain/javascript/javascriptFeatureSeed.ts
index 25d34add6..534285068 100644
--- a/src/domain/javascript/javascriptFeatureSeed.ts
+++ b/src/domain/javascript/javascriptFeatureSeed.ts
@@ -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}`,
diff --git a/src/domain/javascript/javascriptFeatureTrace.ts b/src/domain/javascript/javascriptFeatureTrace.ts
index be492f943..e535080a9 100644
--- a/src/domain/javascript/javascriptFeatureTrace.ts
+++ b/src/domain/javascript/javascriptFeatureTrace.ts
@@ -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 => 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")}`,
};
};
diff --git a/src/domain/javascript/javascriptFeatureTraversal.ts b/src/domain/javascript/javascriptFeatureTraversal.ts
index 2f7b7c891..748314ba4 100644
--- a/src/domain/javascript/javascriptFeatureTraversal.ts
+++ b/src/domain/javascript/javascriptFeatureTraversal.ts
@@ -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}`,
),
diff --git a/src/domain/javascript/javascriptNativeHandoff.ts b/src/domain/javascript/javascriptNativeHandoff.ts
index 69051254e..ec197695f 100644
--- a/src/domain/javascript/javascriptNativeHandoff.ts
+++ b/src/domain/javascript/javascriptNativeHandoff.ts
@@ -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 ?? ""}`,
),
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationActive.ts b/src/domain/javascript/javascriptRuntimeReconciliationActive.ts
index 3dbd073c6..32ef65086 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationActive.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationActive.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts b/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts
index 1eb941ba7..7e7067804 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationMatching.ts b/src/domain/javascript/javascriptRuntimeReconciliationMatching.ts
index b94aa0955..96bbe2e59 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationMatching.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationMatching.ts
@@ -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;
return {
- reconciliation_id: `jrr_item_${canonicalDigest(semantic, "Runtime reconciliation")}`,
+ reconciliation_id: `jrr_item_${digestCanonicalValue(semantic, "Runtime reconciliation")}`,
...semantic,
};
};
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationParsing.ts b/src/domain/javascript/javascriptRuntimeReconciliationParsing.ts
index a339f7db6..4c4310341 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationParsing.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationParsing.ts
@@ -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,
};
}
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationResult.ts b/src/domain/javascript/javascriptRuntimeReconciliationResult.ts
index e39166d81..a814deae1 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationResult.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationResult.ts
@@ -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,
});
};
diff --git a/src/domain/javascript/javascriptRuntimeReconciliationSchemas.ts b/src/domain/javascript/javascriptRuntimeReconciliationSchemas.ts
index 38f606b4d..e60ecb784 100644
--- a/src/domain/javascript/javascriptRuntimeReconciliationSchemas.ts
+++ b/src/domain/javascript/javascriptRuntimeReconciliationSchemas.ts
@@ -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)),
);
}
diff --git a/src/domain/javascript/javascriptRuntimeStaticCandidates.ts b/src/domain/javascript/javascriptRuntimeStaticCandidates.ts
index 641f8c1e4..64a47d050 100644
--- a/src/domain/javascript/javascriptRuntimeStaticCandidates.ts
+++ b/src/domain/javascript/javascriptRuntimeStaticCandidates.ts
@@ -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),
);
diff --git a/src/domain/javascript/javascriptSemanticAnalysis.calls.test.ts b/src/domain/javascript/javascriptSemanticAnalysis.calls.test.ts
index 880aae06c..0fd5aa194 100644
--- a/src/domain/javascript/javascriptSemanticAnalysis.calls.test.ts
+++ b/src/domain/javascript/javascriptSemanticAnalysis.calls.test.ts
@@ -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);
},
diff --git a/src/domain/javascript/javascriptSemanticAnalysis.fixture.ts b/src/domain/javascript/javascriptSemanticAnalysis.fixture.ts
index 4d58866b6..92be0da04 100644
--- a/src/domain/javascript/javascriptSemanticAnalysis.fixture.ts
+++ b/src/domain/javascript/javascriptSemanticAnalysis.fixture.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptSemanticAnalysis.structure.test.ts b/src/domain/javascript/javascriptSemanticAnalysis.structure.test.ts
index c9fb833ab..ba0644056 100644
--- a/src/domain/javascript/javascriptSemanticAnalysis.structure.test.ts
+++ b/src/domain/javascript/javascriptSemanticAnalysis.structure.test.ts
@@ -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);
});
diff --git a/src/domain/javascript/javascriptSemanticCallResolution.ts b/src/domain/javascript/javascriptSemanticCallResolution.ts
index 26b270ab1..246472301 100644
--- a/src/domain/javascript/javascriptSemanticCallResolution.ts
+++ b/src/domain/javascript/javascriptSemanticCallResolution.ts
@@ -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);
diff --git a/src/domain/javascript/javascriptSemanticChildProcesses.ts b/src/domain/javascript/javascriptSemanticChildProcesses.ts
index 2ce7c2fe5..fe7b5e802 100644
--- a/src/domain/javascript/javascriptSemanticChildProcesses.ts
+++ b/src/domain/javascript/javascriptSemanticChildProcesses.ts
@@ -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"
diff --git a/src/domain/javascript/javascriptSemanticCoverage.ts b/src/domain/javascript/javascriptSemanticCoverage.ts
index 1f92406e1..c40edc03a 100644
--- a/src/domain/javascript/javascriptSemanticCoverage.ts
+++ b/src/domain/javascript/javascriptSemanticCoverage.ts
@@ -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
+> = {
+ "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,
diff --git a/src/domain/javascript/javascriptSemanticDataEffects.ts b/src/domain/javascript/javascriptSemanticDataEffects.ts
index e355d3ddb..83afd13d7 100644
--- a/src/domain/javascript/javascriptSemanticDataEffects.ts
+++ b/src/domain/javascript/javascriptSemanticDataEffects.ts
@@ -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:
diff --git a/src/domain/javascript/javascriptSemanticFingerprints.ts b/src/domain/javascript/javascriptSemanticFingerprints.ts
index 2e3336741..3f7309029 100644
--- a/src/domain/javascript/javascriptSemanticFingerprints.ts
+++ b/src/domain/javascript/javascriptSemanticFingerprints.ts
@@ -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) : []),
diff --git a/src/domain/javascript/javascriptSemanticGraph.ts b/src/domain/javascript/javascriptSemanticGraph.ts
index a515b2685..032a2802c 100644
--- a/src/domain/javascript/javascriptSemanticGraph.ts
+++ b/src/domain/javascript/javascriptSemanticGraph.ts
@@ -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,
): 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, {
diff --git a/src/domain/javascript/javascriptSemanticIr.ts b/src/domain/javascript/javascriptSemanticIr.ts
index 16a137a19..0e547f155 100644
--- a/src/domain/javascript/javascriptSemanticIr.ts
+++ b/src/domain/javascript/javascriptSemanticIr.ts
@@ -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",
diff --git a/src/domain/javascript/javascriptSemanticPrimitives.ts b/src/domain/javascript/javascriptSemanticPrimitives.ts
index 0223403d0..29cff687b 100644
--- a/src/domain/javascript/javascriptSemanticPrimitives.ts
+++ b/src/domain/javascript/javascriptSemanticPrimitives.ts
@@ -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
diff --git a/src/domain/javascript/javascriptSemanticProjection.ts b/src/domain/javascript/javascriptSemanticProjection.ts
index 1642a0a41..53881d632 100644
--- a/src/domain/javascript/javascriptSemanticProjection.ts
+++ b/src/domain/javascript/javascriptSemanticProjection.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptSemanticPromises.ts b/src/domain/javascript/javascriptSemanticPromises.ts
index 96eca56b2..d956d68f7 100644
--- a/src/domain/javascript/javascriptSemanticPromises.ts
+++ b/src/domain/javascript/javascriptSemanticPromises.ts
@@ -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,
};
diff --git a/src/domain/javascript/javascriptSemanticProvenance.ts b/src/domain/javascript/javascriptSemanticProvenance.ts
index 69a4a29a8..218c465b5 100644
--- a/src/domain/javascript/javascriptSemanticProvenance.ts
+++ b/src/domain/javascript/javascriptSemanticProvenance.ts
@@ -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 =>
diff --git a/src/domain/javascript/javascriptSemanticQuery.ts b/src/domain/javascript/javascriptSemanticQuery.ts
index 4b1c58c46..e793ccede 100644
--- a/src/domain/javascript/javascriptSemanticQuery.ts
+++ b/src/domain/javascript/javascriptSemanticQuery.ts
@@ -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}`,
),
diff --git a/src/domain/javascript/javascriptSemanticQueryIdentity.ts b/src/domain/javascript/javascriptSemanticQueryIdentity.ts
index 3876fe0a8..ba9aaf94d 100644
--- a/src/domain/javascript/javascriptSemanticQueryIdentity.ts
+++ b/src/domain/javascript/javascriptSemanticQueryIdentity.ts
@@ -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 = (
diff --git a/src/domain/javascript/javascriptSemanticReturns.ts b/src/domain/javascript/javascriptSemanticReturns.ts
index 119ec6bd8..5a1a3f415 100644
--- a/src/domain/javascript/javascriptSemanticReturns.ts
+++ b/src/domain/javascript/javascriptSemanticReturns.ts
@@ -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 };
};
diff --git a/src/domain/javascript/javascriptSemanticSlots.ts b/src/domain/javascript/javascriptSemanticSlots.ts
index 61b61041d..4150231e4 100644
--- a/src/domain/javascript/javascriptSemanticSlots.ts
+++ b/src/domain/javascript/javascriptSemanticSlots.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptSemanticValues.ts b/src/domain/javascript/javascriptSemanticValues.ts
index 8fac48e8e..791d3c388 100644
--- a/src/domain/javascript/javascriptSemanticValues.ts
+++ b/src/domain/javascript/javascriptSemanticValues.ts
@@ -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
? {
diff --git a/src/domain/javascript/javascriptStaticAnalysisBundler.ts b/src/domain/javascript/javascriptStaticAnalysisBundler.ts
index e82096f01..f11901c8c 100644
--- a/src/domain/javascript/javascriptStaticAnalysisBundler.ts
+++ b/src/domain/javascript/javascriptStaticAnalysisBundler.ts
@@ -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,
diff --git a/src/domain/javascript/javascriptStaticAnalysisHelpers.ts b/src/domain/javascript/javascriptStaticAnalysisHelpers.ts
index 28cd6e624..ed1cd8e21 100644
--- a/src/domain/javascript/javascriptStaticAnalysisHelpers.ts
+++ b/src/domain/javascript/javascriptStaticAnalysisHelpers.ts
@@ -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 = (
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. */
diff --git a/src/domain/javascript/sourceToBundleComparison.test.ts b/src/domain/javascript/sourceToBundleComparison.test.ts
index 5e1f4d9dd..f6179d901 100644
--- a/src/domain/javascript/sourceToBundleComparison.test.ts
+++ b/src/domain/javascript/sourceToBundleComparison.test.ts
@@ -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: [],
diff --git a/src/domain/javascript/sourceToBundleComparison.ts b/src/domain/javascript/sourceToBundleComparison.ts
index 6a8294b19..ac0007cb4 100644
--- a/src/domain/javascript/sourceToBundleComparison.ts
+++ b/src/domain/javascript/sourceToBundleComparison.ts
@@ -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,
): 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);
diff --git a/src/domain/javascript/sourceToBundleSignals.ts b/src/domain/javascript/sourceToBundleSignals.ts
index fedbdd80a..719062200 100644
--- a/src/domain/javascript/sourceToBundleSignals.ts
+++ b/src/domain/javascript/sourceToBundleSignals.ts
@@ -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}`,
),
diff --git a/src/domain/jsonShape.ts b/src/domain/jsonShape.ts
index 10e40a12b..2defacd58 100644
--- a/src/domain/jsonShape.ts
+++ b/src/domain/jsonShape.ts
@@ -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),
),
});
};
diff --git a/src/domain/localPath.ts b/src/domain/localPath.ts
index 6de151e5d..3226c2e44 100644
--- a/src/domain/localPath.ts
+++ b/src/domain/localPath.ts
@@ -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);
diff --git a/src/domain/managed/managedMemberComparisonMatch.ts b/src/domain/managed/managedMemberComparisonMatch.ts
index 229263c4f..2c9e0cf04 100644
--- a/src/domain/managed/managedMemberComparisonMatch.ts
+++ b/src/domain/managed/managedMemberComparisonMatch.ts
@@ -50,14 +50,14 @@ const keyMethod = (item: Method): Keyed => ({
item,
exactKey:
item.body.status === "present" && item.body.normalized_il_sha256 !== null
- ? stableKey([
+ ? sha256([
"method-exact",
item.signature.raw_sha256,
item.body.normalized_il_sha256,
])
: null,
// The raw blob is the exact signature; decoding only feeds shape keys.
- signatureKey: stableKey([
+ signatureKey: sha256([
"method-signature",
item.declaring_type,
item.name,
@@ -65,7 +65,7 @@ const keyMethod = (item: Method): Keyed => ({
]),
structuralKey:
item.signature.parse_status === "decoded" && item.body.status === "present"
- ? stableKey([
+ ? sha256([
"method-structural",
item.signature.kind,
item.signature.calling_convention,
@@ -98,17 +98,17 @@ const keyField = (item: Field): Keyed => ({
item,
exactKey:
item.signature.parse_status === "decoded"
- ? stableKey(["field-exact", item.signature.raw_sha256])
+ ? sha256(["field-exact", item.signature.raw_sha256])
: null,
// Only an undecoded field reaches this round: a decoded field with the
// same raw signature already met its counterpart in the exact round.
- signatureKey: stableKey([
+ signatureKey: sha256([
"field-signature",
item.declaring_type,
item.name,
item.signature.raw_sha256,
]),
- structuralKey: stableKey([
+ structuralKey: sha256([
"field-structural",
item.signature.kind,
item.signature.field_type,
@@ -116,8 +116,6 @@ const keyField = (item: Field): Keyed => ({
]),
});
-const stableKey = (value: JsonValue): string => sha256(value);
-
const matchMethods = (
left: readonly Keyed[],
right: readonly Keyed[],
diff --git a/src/domain/referenceSourceGraph.ts b/src/domain/referenceSourceGraph.ts
index 436df0729..1e8c3d782 100644
--- a/src/domain/referenceSourceGraph.ts
+++ b/src/domain/referenceSourceGraph.ts
@@ -2,7 +2,8 @@ import { posix } from "node:path";
import { z } from "zod";
-import { canonicalDigest, canonicalJson } from "./comparisonSemantics.js";
+import { canonicalJson } from "./comparisonSemantics.js";
+import { digestCanonicalValue } from "./canonicalDigest.js";
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
import { digestSchema } from "./../domain/digests.js";
import { prefixedDigestSchema } from "./../domain/digests.js";
@@ -473,7 +474,7 @@ const computeRootSha256 = (
entries: readonly z.infer[],
exclusions: readonly z.infer[],
): string =>
- canonicalDigest(
+ digestCanonicalValue(
rootCommitment(entries, exclusions),
"Reference source graph",
);
@@ -496,7 +497,10 @@ export const parseHistoricalSourceGraph = (
/** Compute a deterministic graph commitment containing no absolute root path. */
export const computeHistoricalSourceGraphSha256 = (input: unknown): string =>
- canonicalDigest(parseHistoricalSourceGraph(input), "Reference source graph");
+ digestCanonicalValue(
+ parseHistoricalSourceGraph(input),
+ "Reference source graph",
+ );
/** Build a deterministic, relocation-independent manifest for a source graph. */
export const createHistoricalSourceManifest = (
@@ -514,7 +518,7 @@ export const createHistoricalSourceManifest = (
};
return historicalSourceManifestBaseSchema.parse({
...semantic,
- manifest_id: `hsm_${canonicalDigest(semantic, "Reference source graph")}`,
+ manifest_id: `hsm_${digestCanonicalValue(semantic, "Reference source graph")}`,
});
};
@@ -525,7 +529,8 @@ export const parseHistoricalSourceManifest = (
const manifest = historicalSourceManifestBaseSchema.parse(input);
const { manifest_id: manifestId, ...semantic } = manifest;
if (
- `hsm_${canonicalDigest(semantic, "Reference source graph")}` !== manifestId
+ `hsm_${digestCanonicalValue(semantic, "Reference source graph")}` !==
+ manifestId
)
throw new TypeError("Historical source manifest identifier does not match");
return manifest;
diff --git a/src/domain/referenceSourceImportParsing.ts b/src/domain/referenceSourceImportParsing.ts
index 972a3157b..70097297a 100644
--- a/src/domain/referenceSourceImportParsing.ts
+++ b/src/domain/referenceSourceImportParsing.ts
@@ -201,9 +201,6 @@ const isRequireCallee = (callee: Node | null | undefined): boolean => {
return false;
};
-const isImportCallee = (callee: Node | null | undefined): boolean =>
- isImport(callee);
-
const collectModuleExpressions = (
node: unknown,
targets: Array,
@@ -272,7 +269,7 @@ const extractRequireAndDynamicImports = (
continue;
}
- if (isImportCallee(call.callee) && isModuleExpression(first)) {
+ if (isImport(call.callee) && isModuleExpression(first)) {
const result = moduleSpecifierFromExpression(first);
if (result !== undefined) {
appendRelationship(relationships, {
diff --git a/src/domain/webCaptureDiff.ts b/src/domain/webCaptureDiff.ts
index 6f666f163..c0ac4698a 100644
--- a/src/domain/webCaptureDiff.ts
+++ b/src/domain/webCaptureDiff.ts
@@ -1,4 +1,4 @@
-import { canonicalDigest } from "./comparisonSemantics.js";
+import { digestCanonicalValue } from "./canonicalDigest.js";
import type { WebPageInspection } from "./browserObservationSchemas.js";
import {
webCaptureDiffSchema,
@@ -106,7 +106,7 @@ const accessibilityDimension = (
return compareDimension(
singleton(
"accessibility_tree",
- digest(
+ digestCanonicalValue(
accessibilityProjection(
before.accessibility,
textComparable,
@@ -116,7 +116,7 @@ const accessibilityDimension = (
),
singleton(
"accessibility_tree",
- digest(
+ digestCanonicalValue(
accessibilityProjection(
after.accessibility,
textComparable,
@@ -167,16 +167,15 @@ const compareWebCaptureDimensions = (
): WebCaptureDiff["dimensions"] => {
const before = input.before.inspection;
const after = input.after.inspection;
- const dimension = compareDimension;
return {
- dom_structure: dimension(
- singleton("document", digest(domProjection(before))),
- singleton("document", digest(domProjection(after))),
+ dom_structure: compareDimension(
+ singleton("document", digestCanonicalValue(domProjection(before))),
+ singleton("document", digestCanonicalValue(domProjection(after))),
sectionsComplete(before, ["frames", "dom"]) &&
sectionsComplete(after, ["frames", "dom"]),
"DOM or frame capture was incomplete in at least one observation.",
),
- scripts: dimension(
+ scripts: compareDimension(
keyed(
before.scripts.items.map(scriptProjection),
(item) => item.script_key,
@@ -189,28 +188,28 @@ const compareWebCaptureDimensions = (
sectionsComplete(after, ["scripts"]),
"Script inventory was incomplete in at least one observation.",
),
- resources: dimension(
+ resources: compareDimension(
keyed(before.resources, (item) => item.resource_key),
keyed(after.resources, (item) => item.resource_key),
sectionsComplete(before, ["resources"]) &&
sectionsComplete(after, ["resources"]),
"Resource inventory was incomplete in at least one observation.",
),
- network: dimension(
+ network: compareDimension(
networkMap(before),
networkMap(after),
sectionsComplete(before, ["network_requests"]) &&
sectionsComplete(after, ["network_requests"]),
"Network capture is attach-window limited or incomplete.",
),
- metadata: dimension(
- singleton("metadata", digest(metadataProjection(before))),
- singleton("metadata", digest(metadataProjection(after))),
+ metadata: compareDimension(
+ singleton("metadata", digestCanonicalValue(metadataProjection(before))),
+ singleton("metadata", digestCanonicalValue(metadataProjection(after))),
sectionsComplete(before, ["metadata"]) &&
sectionsComplete(after, ["metadata"]),
"Safe metadata capture was incomplete in at least one observation.",
),
- webmcp: dimension(
+ webmcp: compareDimension(
webMcpMap(input.before.webmcp),
webMcpMap(input.after.webmcp),
webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp),
@@ -225,14 +224,16 @@ const keyed = (
values: readonly T[],
identity: (value: T) => string,
): ReadonlyMap =>
- new Map(values.map((value) => [identity(value), digest(value)]));
+ new Map(
+ values.map((value) => [identity(value), digestCanonicalValue(value)]),
+ );
const networkMap = (
inspection: WebPageInspection,
): ReadonlyMap => {
const grouped = new Map();
for (const request of inspection.network.requests) {
- const identity = `net_${digest({
+ const identity = `net_${digestCanonicalValue({
method: request.method,
url: request.url,
resource_type: request.resource_type,
@@ -257,7 +258,9 @@ const networkMap = (
return new Map(
[...grouped].map(([identity, values]) => [
identity,
- digest(values.map((value) => digest(value)).sort()),
+ digestCanonicalValue(
+ values.map((value) => digestCanonicalValue(value)).sort(),
+ ),
]),
);
};
@@ -317,11 +320,13 @@ const scriptProjection = (
const metadataProjection = (inspection: WebPageInspection) => ({
responses: inspection.metadata.responses
.map(({ request_id: _requestId, ...response }) => response)
- .map((value) => digest(value))
+ .map((value) => digestCanonicalValue(value))
+ .sort(),
+ dom_urls: inspection.metadata.dom_urls
+ .map((value) => digestCanonicalValue(value))
.sort(),
- dom_urls: inspection.metadata.dom_urls.map((value) => digest(value)).sort(),
agent_hints: inspection.metadata.agent_hints
- .map((value) => digest(value))
+ .map((value) => digestCanonicalValue(value))
.sort(),
excluded_dom_urls: inspection.metadata.excluded_dom_urls,
headers_allowlisted: inspection.metadata.headers_allowlisted,
@@ -330,8 +335,6 @@ const metadataProjection = (inspection: WebPageInspection) => ({
const singleton = (key: string, value: string): ReadonlyMap =>
new Map([[key, value]]);
-const digest = (value: unknown): string => canonicalDigest(value);
-
const accessibilityComparable = (
inspection: WebPageInspection,
): {
@@ -399,7 +402,7 @@ const storageMap = (
const map = new Map([
[
"storage:summary",
- digest({
+ digestCanonicalValue({
origin: storage.origin,
values_redacted: storage.values_redacted,
...(includeUsage
@@ -414,7 +417,7 @@ const storageMap = (
if (!includeKeys) return map;
const add = (kind: string, keys: readonly string[]) => {
for (const key of keys) {
- map.set(`storage:${kind}:${key}`, digest(key));
+ map.set(`storage:${kind}:${key}`, digestCanonicalValue(key));
}
};
add("local_storage", storage.local_storage_keys);
@@ -424,7 +427,10 @@ const storageMap = (
for (const fingerprint of storage.content_fingerprints) {
const identity = `${fingerprint.scope}:${fingerprint.identity_sha256}`;
if (!fingerprintIdentities.has(identity)) continue;
- map.set(`storage:content:${identity}`, digest(fingerprint.value_sha256));
+ map.set(
+ `storage:content:${identity}`,
+ digestCanonicalValue(fingerprint.value_sha256),
+ );
}
return map;
};
diff --git a/src/dotnet/ManagedStaticProviderMetadata.ts b/src/dotnet/ManagedStaticProviderMetadata.ts
index 4495cdd59..04b412828 100644
--- a/src/dotnet/ManagedStaticProviderMetadata.ts
+++ b/src/dotnet/ManagedStaticProviderMetadata.ts
@@ -12,14 +12,12 @@ import {
export const MANAGED_STATIC_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze(
MANAGED_STATIC_PROVIDER,
);
-const IDENTITY = MANAGED_STATIC_PROVIDER_IDENTITY;
-
/** Declare managed metadata coverage without opening or loading an assembly. */
export const managedStaticCapabilities = (): readonly CapabilityDescriptor[] =>
Object.freeze(
MANAGED_TOOL_CONTRACTS.map((contract) =>
Object.freeze({
- provider: IDENTITY,
+ provider: MANAGED_STATIC_PROVIDER_IDENTITY,
operation: contract.name,
available: true as const,
reason: null,
diff --git a/src/main/transport.ts b/src/main/transport.ts
index c4a921b91..729b5d6d1 100644
--- a/src/main/transport.ts
+++ b/src/main/transport.ts
@@ -14,15 +14,12 @@ import {
MCP_CONNECTION_START_FAILED,
} from "./messages.js";
-/** Optional adapters whose absence must not prevent the core MCP server. */
-export type OptionalProviders = OptionalProviderLoadResult;
-
interface ServerContext {
readonly environment: Readonly;
readonly delivery: ToolResultDelivery;
readonly logger: Logger;
readonly serverLogger: Logger;
- readonly loadOptionalProviders: () => Promise;
+ readonly loadOptionalProviders: () => Promise;
}
export const startMcpTransport = async (
@@ -38,7 +35,7 @@ export const startMcpTransport = async (
| { readonly ok: false }
> => {
const { serverLogger } = serverContext;
- let optionalProviders: OptionalProviders = {};
+ let optionalProviders: OptionalProviderLoadResult = {};
try {
optionalProviders = await serverContext.loadOptionalProviders();
} catch (cause: unknown) {
diff --git a/src/main/types.ts b/src/main/types.ts
index fa8de8ba9..8b46e25f4 100644
--- a/src/main/types.ts
+++ b/src/main/types.ts
@@ -1,3 +1,5 @@
+import type { OptionalProviderLoadResult } from "../application/OptionalObservationProviders.js";
+
export interface RuntimeDependencies {
readonly env: NodeJS.ProcessEnv;
readonly serve: typeof import("@modelcontextprotocol/server/stdio").serveStdio;
@@ -5,7 +7,5 @@ export interface RuntimeDependencies {
readonly setExitCode: (code: number) => void;
readonly registerShutdown: (handler: () => void) => () => void;
readonly createServer?: typeof import("../server/createServer.js").createServer;
- readonly loadOptionalProviders?: () => Promise<
- import("./transport.js").OptionalProviders
- >;
+ readonly loadOptionalProviders?: () => Promise;
}
diff --git a/src/native/NativeMacOSProviderMetadata.ts b/src/native/NativeMacOSProviderMetadata.ts
index 3f7c24dbd..18e7a100d 100644
--- a/src/native/NativeMacOSProviderMetadata.ts
+++ b/src/native/NativeMacOSProviderMetadata.ts
@@ -10,8 +10,6 @@ export const NATIVE_MACOS_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze({
name: "macOS native inspection utilities",
version: null,
});
-const IDENTITY = NATIVE_MACOS_PROVIDER_IDENTITY;
-
/** Operations whose target process can show UI, use the network and write files. */
const OWNED_PROCESS_OPERATIONS: ReadonlySet = new Set([
"capture_native_ui_scenario",
@@ -36,7 +34,7 @@ export const nativeMacOSCapabilities = (
reason: "Native macOS utilities require macOS.",
} as const);
return Object.freeze({
- provider: IDENTITY,
+ provider: NATIVE_MACOS_PROVIDER_IDENTITY,
operation: contract.name,
...availability,
effects: Object.freeze({
diff --git a/src/reference/ReferenceSourceReaderEntries.ts b/src/reference/ReferenceSourceReaderEntries.ts
index 2b60f20c2..36ba8952b 100644
--- a/src/reference/ReferenceSourceReaderEntries.ts
+++ b/src/reference/ReferenceSourceReaderEntries.ts
@@ -1,3 +1,4 @@
+import type { BigIntStats } from "node:fs";
import { readdir, readlink, realpath } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
@@ -18,7 +19,6 @@ import {
bigLstat,
} from "./ReferenceSourceReaderValidate.js";
import {
- type BigStats,
type PendingDirectory,
type ReferenceSourceEntry,
type ReferenceSourceResult,
@@ -119,7 +119,7 @@ const readDirectoryNames = async (
const readMetadata = async (
path: string,
): Promise<
- | { readonly ok: true; readonly value: BigStats }
+ | { readonly ok: true; readonly value: BigIntStats }
| { readonly ok: false; readonly message: string }
> => {
try {
@@ -193,7 +193,7 @@ const processFileEntry = async (
state: TraversalState,
absolute: string,
path: string,
- metadata: BigStats,
+ metadata: BigIntStats,
): Promise => {
const result = await readStableFile({
root: state.root,
diff --git a/src/reference/ReferenceSourceReaderFile.ts b/src/reference/ReferenceSourceReaderFile.ts
index 2e0845af8..b9c483e5d 100644
--- a/src/reference/ReferenceSourceReaderFile.ts
+++ b/src/reference/ReferenceSourceReaderFile.ts
@@ -1,3 +1,4 @@
+import type { BigIntStats } from "node:fs";
import { constants } from "node:fs";
import { open, type FileHandle } from "node:fs/promises";
import { dirname } from "node:path";
@@ -13,7 +14,6 @@ import {
validateDirectory,
} from "./ReferenceSourceReaderValidate.js";
import {
- type BigStats,
type ReferenceSourceEntry,
type StableFileRequest,
} from "./ReferenceSourceReaderTypes.js";
@@ -23,7 +23,7 @@ const READ_CHUNK_BYTES = 64 * 1024;
type PreparedFileRead =
| {
readonly status: "ready";
- readonly before: BigStats;
+ readonly before: BigIntStats;
}
| { readonly status: "failed"; readonly entry: ReferenceSourceEntry };
@@ -33,12 +33,12 @@ type FileContentsRead =
type FinalizeFileReadRequest = {
readonly root: string;
- readonly rootIdentity: BigStats;
+ readonly rootIdentity: BigIntStats;
readonly absolute: string;
readonly path: string;
readonly handle: FileHandle;
- readonly before: BigStats;
- readonly parentBefore: { readonly ok: true; readonly stats: BigStats };
+ readonly before: BigIntStats;
+ readonly parentBefore: { readonly ok: true; readonly stats: BigIntStats };
readonly chunks: Buffer[];
readonly total: number;
readonly signal?: AbortSignal;
diff --git a/src/reference/ReferenceSourceReaderTypes.ts b/src/reference/ReferenceSourceReaderTypes.ts
index 92c517c49..9ea1a0c5e 100644
--- a/src/reference/ReferenceSourceReaderTypes.ts
+++ b/src/reference/ReferenceSourceReaderTypes.ts
@@ -57,15 +57,13 @@ export interface ReferenceSourceReaderError {
readonly message: string;
}
-export type BigStats = BigIntStats;
-
export type PendingDirectory = {
readonly path: string;
};
export type TraversalState = {
readonly root: string;
- readonly rootIdentity: BigStats;
+ readonly rootIdentity: BigIntStats;
readonly signal?: AbortSignal;
readonly shouldExclude?: (path: string) => boolean;
readonly entries: ReferenceSourceEntry[];
@@ -75,10 +73,10 @@ export type TraversalState = {
export type StableFileRequest = {
readonly root: string;
- readonly rootIdentity: BigStats;
+ readonly rootIdentity: BigIntStats;
readonly absolute: string;
readonly path: string;
- readonly expected: BigStats;
+ readonly expected: BigIntStats;
readonly signal?: AbortSignal;
};
diff --git a/src/reference/ReferenceSourceReaderValidate.ts b/src/reference/ReferenceSourceReaderValidate.ts
index 01a258a28..a941f674b 100644
--- a/src/reference/ReferenceSourceReaderValidate.ts
+++ b/src/reference/ReferenceSourceReaderValidate.ts
@@ -1,3 +1,4 @@
+import type { BigIntStats } from "node:fs";
import { constants } from "node:fs";
import { lstat, realpath } from "node:fs/promises";
@@ -9,15 +10,14 @@ import {
} from "./ReferenceSourceReaderErrors.js";
import { isPathWithinRoot } from "../domain/localPath.js";
import {
- type BigStats,
type ReferenceSourceFailureCode,
type ReferenceSourceResult,
} from "./ReferenceSourceReaderTypes.js";
-export const bigLstat = (path: string): Promise =>
+export const bigLstat = (path: string): Promise =>
lstat(path, { bigint: true });
-export const sameFile = (left: BigStats, right: BigStats): boolean =>
+export const sameFile = (left: BigIntStats, right: BigIntStats): boolean =>
left.dev === right.dev &&
left.ino === right.ino &&
left.size === right.size &&
@@ -32,11 +32,11 @@ export const noFollowOpenSupported = (): boolean =>
export const validateDirectory = async (
root: string,
- rootIdentity: BigStats,
+ rootIdentity: BigIntStats,
path: string,
signal?: AbortSignal,
): Promise<
- | { readonly ok: true; readonly stats: BigStats }
+ | { readonly ok: true; readonly stats: BigIntStats }
| {
readonly ok: false;
readonly code: ReferenceSourceFailureCode;
@@ -94,7 +94,7 @@ export const prepareRoot = async (
): Promise<
ReferenceSourceResult<{
readonly canonicalRoot: string;
- readonly rootIdentity: BigStats;
+ readonly rootIdentity: BigIntStats;
}>
> => {
if (isAborted(signal)) return err(cancelled());
diff --git a/src/server/createServer.ts b/src/server/createServer.ts
index a56e51c07..b0d95ecbb 100644
--- a/src/server/createServer.ts
+++ b/src/server/createServer.ts
@@ -66,7 +66,7 @@ import { registerNativeTools } from "./registerNativeTools.js";
import { registerOfficialTools } from "./registerOfficialTools.js";
import { registerGuidedPrompts } from "./registerPrompts.js";
import { registerSessionTools } from "./registerSessionTools.js";
-import type { SessionAvailability } from "./sessionAvailabilityPolicy.js";
+import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
const TARGET_FREE_INSTRUCTIONS =
@@ -94,7 +94,7 @@ export interface CreateServerOptions {
readonly electronObservation?: ElectronObservationPort;
readonly electronActiveObservation?: ElectronActiveObservationPort;
readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort;
- readonly availabilityPolicy?: () => SessionAvailability;
+ readonly availabilityPolicy?: () => AvailabilityPolicy;
readonly optionalProviderLoadFailures?: OptionalProviderLoadFailures;
}
diff --git a/src/server/registerSessionStatusTool.ts b/src/server/registerSessionStatusTool.ts
index 5bc5eb4c4..c20170647 100644
--- a/src/server/registerSessionStatusTool.ts
+++ b/src/server/registerSessionStatusTool.ts
@@ -9,7 +9,7 @@ import type { ClientFeatureAvailability } from "../contracts/toolOutputSchemaPri
import { jsonObjectSchema } from "../domain/jsonValue.js";
import { createServerIdentity } from "../serverIdentity.js";
import { mcpClientMetadata } from "./mcpClientMetadata.js";
-import type { SessionAvailability } from "./sessionAvailabilityPolicy.js";
+import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
import { err } from "../domain/result.js";
@@ -21,7 +21,7 @@ export interface SessionStatusToolOptions {
readonly session: BinarySessionPort;
readonly contract: ReturnType>;
readonly startedAt: string;
- readonly availabilityPolicy: () => SessionAvailability;
+ readonly availabilityPolicy: () => AvailabilityPolicy;
readonly androidAnalysisAvailability: (
signal: AbortSignal,
) => Promise;
diff --git a/src/server/registerSessionTools.ts b/src/server/registerSessionTools.ts
index 9472c55ac..d4dc0b48a 100644
--- a/src/server/registerSessionTools.ts
+++ b/src/server/registerSessionTools.ts
@@ -29,10 +29,8 @@ import {
registerUnknownTools,
} from "./registerSessionRecordTools.js";
import { registerSessionStatusTool } from "./registerSessionStatusTool.js";
-import {
- sessionAvailabilityPolicy,
- type SessionAvailability,
-} from "./sessionAvailabilityPolicy.js";
+import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
+import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
import { logToolExecution } from "./toolLogging.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
@@ -140,7 +138,7 @@ export interface LifecycleToolRegistration {
readonly closeContract: ReturnType>;
readonly statusContract: ReturnType>;
readonly startedAt: string;
- readonly availabilityPolicy: () => SessionAvailability;
+ readonly availabilityPolicy: () => AvailabilityPolicy;
readonly androidAnalysisAvailability: (
signal: AbortSignal,
) => Promise;
@@ -218,7 +216,7 @@ const registerOpenLifecycleTool = ({
/** Register MCP-only target lifecycle operations on a long-lived session. */
export interface SessionToolOptions {
readonly startedAt?: string;
- readonly availabilityPolicy?: () => SessionAvailability;
+ readonly availabilityPolicy?: () => AvailabilityPolicy;
readonly androidAnalysisAvailability?: (
signal: AbortSignal,
) => Promise;
diff --git a/src/server/sessionAvailabilityPolicy.ts b/src/server/sessionAvailabilityPolicy.ts
index 66259f4a8..14996091d 100644
--- a/src/server/sessionAvailabilityPolicy.ts
+++ b/src/server/sessionAvailabilityPolicy.ts
@@ -2,14 +2,12 @@ import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
import type { OptionalProviderLoadFailures } from "../application/OptionalObservationProviders.js";
import { platform } from "node:process";
-export type SessionAvailability = AvailabilityPolicy;
-
export interface SessionAvailabilityDefaults {
readonly optionalProviderLoadFailures?:
| OptionalProviderLoadFailures
| undefined;
readonly optionalFeatures?: Pick<
- SessionAvailability,
+ AvailabilityPolicy,
| "evmInterfaceEnabled"
| "browserObservationEnabled"
| "browserScenarioEnabled"
@@ -27,9 +25,9 @@ export interface SessionAvailabilityDefaults {
/** Select configured availability reporting or the target-free defaults. */
export const sessionAvailabilityPolicy = (
- configured: (() => SessionAvailability) | undefined,
+ configured: (() => AvailabilityPolicy) | undefined,
defaults: SessionAvailabilityDefaults,
-): (() => SessionAvailability) => {
+): (() => AvailabilityPolicy) => {
const policy =
configured ??
(() => ({
diff --git a/tests/boundary/filesystem/artifactDirectoryIdentity.test.ts b/tests/boundary/filesystem/artifactDirectoryIdentity.test.ts
index b23e5edb2..5d54cc154 100644
--- a/tests/boundary/filesystem/artifactDirectoryIdentity.test.ts
+++ b/tests/boundary/filesystem/artifactDirectoryIdentity.test.ts
@@ -4,8 +4,8 @@ import { join } from "node:path";
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
import { describe, expect, it } from "vitest";
-import { compareCodePoints } from "../../../src/domain/canonicalOrdering.js";
-import { canonicalDigest } from "../../../src/domain/comparisonSemantics.js";
+import { compareUnicodeCodePoints } from "../../../src/domain/unicodeCodePointOrder.js";
+import { digestCanonicalValue } from "../../../src/domain/canonicalDigest.js";
import { inventoryArtifact } from "../../../src/artifacts/inventory/ArtifactInventory.js";
import { compareArtifacts } from "../../../src/domain/artifactComparison.js";
import type { ArtifactInventoryResult } from "../../../src/domain/artifactGraph.js";
@@ -54,9 +54,9 @@ const directoryIdForChildren = (
inventory: ArtifactInventoryResult,
paths: readonly string[],
) =>
- `art_${canonicalDigest(
+ `art_${digestCanonicalValue(
{
- sha256: canonicalDigest(
+ sha256: digestCanonicalValue(
{
kind: "directory",
children: paths
@@ -69,7 +69,7 @@ const directoryIdForChildren = (
};
})
.sort((left, right) =>
- compareCodePoints(String(left.name), String(right.name)),
+ compareUnicodeCodePoints(String(left.name), String(right.name)),
),
},
"Artifact",
diff --git a/tests/boundary/process/cliEnvironmentSeam.test.ts b/tests/boundary/process/cliEnvironmentSeam.test.ts
index f3304bcdc..fd1f03421 100644
--- a/tests/boundary/process/cliEnvironmentSeam.test.ts
+++ b/tests/boundary/process/cliEnvironmentSeam.test.ts
@@ -70,7 +70,7 @@ describe("the CLI takes its environment as an input", () => {
// environment rather than whatever the process happens to carry.
const result = await createDirectAnalysis({
REA_LOG_LEVEL: "not-a-level",
- }).runCapabilityStatus();
+ }).runSessionStatus();
expect(result).toMatchObject({ error: expect.anything() });
});
});
diff --git a/tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts b/tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
index b934e3e36..a75db7b0a 100644
--- a/tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
+++ b/tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
@@ -144,6 +144,26 @@ it("reports semantic value resource limits in application graph coverage", async
}),
]),
});
+ const sourceModuleLimits = graph.nodes
+ .filter(({ kind }) => kind === "javascript-module")
+ .flatMap(({ observations }) => observations)
+ .filter(
+ ({ evidence }) =>
+ evidence.extractor.operation === "recover-source-module",
+ )
+ .flatMap(({ evidence }) => evidence.coverage.limits);
+ expect(sourceModuleLimits).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ name: "javascript_semantic_primitive_candidates",
+ unit: "items",
+ }),
+ expect.objectContaining({
+ name: "javascript_semantic_primitive_bytes",
+ unit: "bytes",
+ }),
+ ]),
+ );
expect(graph.limitations).toContain(
"Primitive candidate budget exceeded (maximum 256 alternatives).",
);