diff --git a/docs/capability-migration.md b/docs/capability-migration.md index 4e4960fb41..568cf4411c 100644 --- a/docs/capability-migration.md +++ b/docs/capability-migration.md @@ -277,11 +277,23 @@ unchanged sources under `scripts/fixtures/javascript-recovery/`. Its default REA entrypoint still resolves to `scripts/rea.mjs`. Managed conformance imports the relocated compiled workflow; owning CI source filters follow the new paths. -This application move preserves the current directory/ASAR reader boundary, -including root/nested byte checks, UTF-8 diagnostics, cancellation and owned -close. Reader selection requires a separately traced increment. Runtime ports -retain their distinct Inspector, passive Electron and active Electron effects; -shipped Electron hooks remain at their existing provider boundary. +`src/artifacts/javascript/` owns directory/ASAR reader construction and relevant +file acquisition, including root/nested byte verification, UTF-8 diagnostics and +nested reader cleanup. Its narrow factory returns the existing `ArtifactReader`; +the application retains input admission, inventory, graph construction and its +original root-reader `finally` close. Pure file facts live in +`src/domain/javascript/javascriptArtifactFiles.ts`, and the shared immutable +inventory snapshot lives in `src/domain/artifactInventorySnapshot.ts`. Inventory +options and integrity policy remain application-owned. Shared stream hashing +lives in `src/artifacts/ArtifactHash.ts`, preserving complete byte counts and the +bounded classification prefix. Acquisition/hash guards reject imports from +application, composition or caller adapters; JavaScript workflows use the factory +instead of importing concrete ASAR/directory reader classes. + +ZIP, native-DMG and Mach-O inventory reader selection keep their existing +lifecycles. Runtime ports retain their distinct Inspector, passive Electron and +active Electron effects; shipped Electron hooks remain at their existing +provider boundary. ## Binary application ownership diff --git a/scripts/lib/module-boundaries.d.mts b/scripts/lib/module-boundaries.d.mts index abf8daf9b7..51d5957c6b 100644 --- a/scripts/lib/module-boundaries.d.mts +++ b/scripts/lib/module-boundaries.d.mts @@ -5,6 +5,7 @@ export interface ModuleBoundaryViolation { readonly specifier: string; readonly target: string; readonly boundary: + | "artifact-acquisition" | "pure-layer" | "application-composition" | "provider-construction" diff --git a/scripts/lib/module-boundaries.mjs b/scripts/lib/module-boundaries.mjs index 81bfce577e..e43ccd44cc 100644 --- a/scripts/lib/module-boundaries.mjs +++ b/scripts/lib/module-boundaries.mjs @@ -60,6 +60,22 @@ const failedBoundary = (file, target) => { if (!file.startsWith("src/") || !target.startsWith("src/")) return undefined; const layer = file.split("/")[1]; const targetLayer = target.split("/")[1]; + if ( + file.startsWith("src/application/javascript/") && + /^src\/artifacts\/(?:Asar|Directory)ArtifactReader\.(?:js|ts)$/u.test( + target, + ) + ) + return "provider-construction"; + if ( + (file.startsWith("src/artifacts/javascript/") || + /^src\/artifacts\/ArtifactHash\.(?:js|ts)$/u.test(file)) && + (["application", "composition", "server", "cli", "main"].includes( + targetLayer, + ) || + /^src\/(?:cli|main)\./u.test(target)) + ) + return "artifact-acquisition"; if ( PROVIDER_ROOTS.has(layer) && /^src\/generatedMcpToolCatalog\.(?:js|ts)$/u.test(target) diff --git a/src/application/ArtifactInventory.ts b/src/application/ArtifactInventory.ts index ea3a9becbf..a7b5b181f9 100644 --- a/src/application/ArtifactInventory.ts +++ b/src/application/ArtifactInventory.ts @@ -4,7 +4,7 @@ import { artifactInventoryResultSchema, type ArtifactInventoryResult, } from "../domain/artifactGraph.js"; -import { abortIfNeeded } from "./ArtifactInventory/hash.js"; +import { abortIfNeeded } from "../artifacts/ArtifactHash.js"; import { scanCanonicalArtifactInventory } from "./ArtifactInventory/scanCanonical.js"; import type { ArtifactIntegrityPolicy, diff --git a/src/application/ArtifactInventory/classify.ts b/src/application/ArtifactInventory/classify.ts index 8dcc0ce4d9..36e953712b 100644 --- a/src/application/ArtifactInventory/classify.ts +++ b/src/application/ArtifactInventory/classify.ts @@ -1,7 +1,7 @@ import { open } from "node:fs/promises"; import { classifyArtifactContent } from "../ArtifactGraphConstruction.js"; -import { ARTIFACT_CLASSIFICATION_PREFIX_BYTES } from "./hash.js"; +import { ARTIFACT_CLASSIFICATION_PREFIX_BYTES } from "../../artifacts/ArtifactHash.js"; import type { ArtifactNode } from "../../domain/artifactGraph.js"; import { hasZipSignature, diff --git a/src/application/ArtifactInventory/scanCanonical.ts b/src/application/ArtifactInventory/scanCanonical.ts index ea28fc56aa..b02005394b 100644 --- a/src/application/ArtifactInventory/scanCanonical.ts +++ b/src/application/ArtifactInventory/scanCanonical.ts @@ -23,7 +23,7 @@ import { } from "../ArtifactGraphConstruction.js"; import { canonicalDigest } from "../../domain/comparisonSemantics.js"; import { classifyRoot } from "./classify.js"; -import { hashReadable, type HashResult } from "./hash.js"; +import { hashReadable, type HashResult } from "../../artifacts/ArtifactHash.js"; import { createReader, inventoryLimitations } from "./reader.js"; import { scanReader, diff --git a/src/application/ArtifactInventory/scanReader.ts b/src/application/ArtifactInventory/scanReader.ts index c388a358be..fe24fe408e 100644 --- a/src/application/ArtifactInventory/scanReader.ts +++ b/src/application/ArtifactInventory/scanReader.ts @@ -22,7 +22,7 @@ import { STRICT_INTEGRITY_POLICY, type ArtifactIntegrityPolicy, } from "./types.js"; -import { hashReadable } from "./hash.js"; +import { hashReadable } from "../../artifacts/ArtifactHash.js"; export interface PendingIntegrityContradiction { readonly logicalPath: string; diff --git a/src/application/ArtifactInventory/types.ts b/src/application/ArtifactInventory/types.ts index 3c7004f060..741b070cfb 100644 --- a/src/application/ArtifactInventory/types.ts +++ b/src/application/ArtifactInventory/types.ts @@ -1,9 +1,3 @@ -import type { - ArtifactInventoryResult, - ArtifactNode, - IntegrityContradiction, -} from "../../domain/artifactGraph.js"; - /** Resolved integrity behavior admitted to the artifact scanner. */ export type ArtifactIntegrityPolicy = | { readonly mode: "fail" } @@ -19,15 +13,4 @@ export interface ArtifactInventoryOptions { readonly integrity?: ArtifactIntegrityPolicy | undefined; } -/** Immutable inventory produced by one complete artifact scan. */ -export interface ArtifactInventorySnapshot { - readonly manifest: ArtifactInventoryResult["manifest"]; - readonly nodes: readonly ArtifactNode[]; - readonly occurrences: ArtifactInventoryResult["occurrences"]; - readonly edges: ArtifactInventoryResult["edges"]; - readonly provenance: ReadonlyArray< - ArtifactInventoryResult["provenance"][number] - >; - readonly integrity_contradictions: readonly IntegrityContradiction[]; - readonly limitations: readonly string[]; -} +export type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySnapshot.js"; diff --git a/src/application/javascript/ElectronBoundaryAnalysis.ts b/src/application/javascript/ElectronBoundaryAnalysis.ts index eff377b710..99de494e2d 100644 --- a/src/application/javascript/ElectronBoundaryAnalysis.ts +++ b/src/application/javascript/ElectronBoundaryAnalysis.ts @@ -2,7 +2,7 @@ import { compareCodePoints } from "../../domain/canonicalOrdering.js"; import type { ElectronIpcFinding } from "../../domain/javascript/electronStaticAnalysisTypes.js"; import type { ElectronBoundarySummary } from "../../domain/javascript/javascriptApplicationAnalysis.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js"; /** One IPC fact retaining its exact owning artifact for graph projection. */ diff --git a/src/application/javascript/ElectronBoundaryGraphContext.ts b/src/application/javascript/ElectronBoundaryGraphContext.ts index 78e59d74b2..460f849dd0 100644 --- a/src/application/javascript/ElectronBoundaryGraphContext.ts +++ b/src/application/javascript/ElectronBoundaryGraphContext.ts @@ -1,7 +1,7 @@ import type { ApplicationNode } from "../../domain/javascript/javascriptApplicationGraph.js"; import { sha256Text } from "../../domain/javascript/javascriptStaticAnalysisHelpers.js"; import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import type { JavaScriptArtifactGraphContext, JavaScriptArtifactGraphCoverage, diff --git a/src/application/javascript/ElectronBoundaryGraphIpc.ts b/src/application/javascript/ElectronBoundaryGraphIpc.ts index 51139b6d42..64a69c4aba 100644 --- a/src/application/javascript/ElectronBoundaryGraphIpc.ts +++ b/src/application/javascript/ElectronBoundaryGraphIpc.ts @@ -1,6 +1,6 @@ import type { ApplicationNode } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { ElectronSenderValidationFinding } from "../../domain/javascript/electronStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { artifactLocalIdentity, javascriptAnalysisCoverage, diff --git a/src/application/javascript/ElectronBoundaryGraphNative.ts b/src/application/javascript/ElectronBoundaryGraphNative.ts index 27e6284282..fffb3f5fb1 100644 --- a/src/application/javascript/ElectronBoundaryGraphNative.ts +++ b/src/application/javascript/ElectronBoundaryGraphNative.ts @@ -1,5 +1,5 @@ import type { ElectronNativeAddonBindingFinding } from "../../domain/javascript/electronStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { javascriptAnalysisCoverage, resolveArtifactPath, diff --git a/src/application/javascript/ElectronBoundaryGraphWindows.ts b/src/application/javascript/ElectronBoundaryGraphWindows.ts index 2bce47d243..68ed49e177 100644 --- a/src/application/javascript/ElectronBoundaryGraphWindows.ts +++ b/src/application/javascript/ElectronBoundaryGraphWindows.ts @@ -4,7 +4,7 @@ import type { ElectronContextBridgeFinding, ElectronUtilityProcessFinding, } from "../../domain/javascript/electronStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { artifactLocalIdentity, createElectronRoleNode, diff --git a/src/application/javascript/JavaScriptArtifactAnalysis.ts b/src/application/javascript/JavaScriptArtifactAnalysis.ts index b7ef2ad696..9422eacff8 100644 --- a/src/application/javascript/JavaScriptArtifactAnalysis.ts +++ b/src/application/javascript/JavaScriptArtifactAnalysis.ts @@ -14,7 +14,7 @@ import { failedJavaScriptStaticAnalysis } from "../../domain/javascript/javascri import type { JavaScriptArtifactFile, JavaScriptArtifactFileSet, -} from "./JavaScriptArtifactFiles.js"; +} from "../../domain/javascript/javascriptArtifactFiles.js"; import type { AnalyzedJavaScriptArtifactFile, JavaScriptArtifactAnalysis, diff --git a/src/application/javascript/JavaScriptArtifactAnalysisTypes.ts b/src/application/javascript/JavaScriptArtifactAnalysisTypes.ts index df46f721bc..d1cdd2a6b5 100644 --- a/src/application/javascript/JavaScriptArtifactAnalysisTypes.ts +++ b/src/application/javascript/JavaScriptArtifactAnalysisTypes.ts @@ -3,7 +3,7 @@ import type { JavaScriptStaticAnalysis, } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; import type { JavaScriptSemanticIr } from "../../domain/javascript/javascriptSemanticIr.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; interface JavaScriptStructuredObservation { readonly path: string; diff --git a/src/application/javascript/JavaScriptArtifactGraphBuilder.ts b/src/application/javascript/JavaScriptArtifactGraphBuilder.ts index 096adb50b0..6af56f1a75 100644 --- a/src/application/javascript/JavaScriptArtifactGraphBuilder.ts +++ b/src/application/javascript/JavaScriptArtifactGraphBuilder.ts @@ -4,7 +4,7 @@ import { type JavaScriptApplicationGraph, } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFileSet } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFileSet } from "../../domain/javascript/javascriptArtifactFiles.js"; import { JavaScriptArtifactGraphAccumulator } from "./JavaScriptArtifactGraphAccumulator.js"; import type { JavaScriptArtifactGraphContext } from "./JavaScriptArtifactGraphContext.js"; import { diff --git a/src/application/javascript/JavaScriptArtifactGraphBundlers.ts b/src/application/javascript/JavaScriptArtifactGraphBundlers.ts index 49810f6c63..c23182d9bb 100644 --- a/src/application/javascript/JavaScriptArtifactGraphBundlers.ts +++ b/src/application/javascript/JavaScriptArtifactGraphBundlers.ts @@ -1,6 +1,6 @@ import type { ApplicationNode } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { JavaScriptBundlerRegistration } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { addAstContainsEdge, artifactLocalIdentity, diff --git a/src/application/javascript/JavaScriptArtifactGraphContext.ts b/src/application/javascript/JavaScriptArtifactGraphContext.ts index 945dd47147..8e398bc77a 100644 --- a/src/application/javascript/JavaScriptArtifactGraphContext.ts +++ b/src/application/javascript/JavaScriptArtifactGraphContext.ts @@ -11,7 +11,7 @@ import type { JavaScriptArtifactFile, JavaScriptArtifactFileSet, JavaScriptArtifactFileKind, -} from "./JavaScriptArtifactFiles.js"; +} from "../../domain/javascript/javascriptArtifactFiles.js"; import { JavaScriptArtifactGraphAccumulator } from "./JavaScriptArtifactGraphAccumulator.js"; import { artifactObservationEvidence, diff --git a/src/application/javascript/JavaScriptArtifactGraphFindings.ts b/src/application/javascript/JavaScriptArtifactGraphFindings.ts index abce528274..e3bf4c6e81 100644 --- a/src/application/javascript/JavaScriptArtifactGraphFindings.ts +++ b/src/application/javascript/JavaScriptArtifactGraphFindings.ts @@ -1,6 +1,6 @@ import type { ApplicationNode } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { JavaScriptStaticAnalysis } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { addStaticInferenceEdge, artifactLocalIdentity, diff --git a/src/application/javascript/JavaScriptArtifactGraphStructure.ts b/src/application/javascript/JavaScriptArtifactGraphStructure.ts index b2a1979367..7a135fdead 100644 --- a/src/application/javascript/JavaScriptArtifactGraphStructure.ts +++ b/src/application/javascript/JavaScriptArtifactGraphStructure.ts @@ -5,7 +5,7 @@ import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTyp import type { JavaScriptArtifactContainer, JavaScriptArtifactFile, -} from "./JavaScriptArtifactFiles.js"; +} from "../../domain/javascript/javascriptArtifactFiles.js"; import type { JavaScriptArtifactGraphAccumulator } from "./JavaScriptArtifactGraphAccumulator.js"; import { addArtifactContainsEdge, diff --git a/src/application/javascript/JavaScriptArtifactPathResolution.test.ts b/src/application/javascript/JavaScriptArtifactPathResolution.test.ts index 1536e32520..3f6232def5 100644 --- a/src/application/javascript/JavaScriptArtifactPathResolution.test.ts +++ b/src/application/javascript/JavaScriptArtifactPathResolution.test.ts @@ -4,7 +4,7 @@ import { resolveArtifactPathByContext, type ResolveArtifactPathInput, } from "./JavaScriptArtifactPathResolution.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { artifactLocalIdentity } from "./JavaScriptArtifactGraphContext.js"; import { applicationNodeIdentitySchema } from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js"; import { analyzeJavaScriptStaticSource } from "../../domain/javascript/javascriptStaticAnalysis.js"; diff --git a/src/application/javascript/JavaScriptArtifactPathResolution.ts b/src/application/javascript/JavaScriptArtifactPathResolution.ts index fcc102281d..57a5012b02 100644 --- a/src/application/javascript/JavaScriptArtifactPathResolution.ts +++ b/src/application/javascript/JavaScriptArtifactPathResolution.ts @@ -1,7 +1,7 @@ import { builtinModules } from "node:module"; import { posix } from "node:path"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { admitsCanonicalPathSyntax, hasScheme, diff --git a/src/application/javascript/JavaScriptArtifactReconstruction.ts b/src/application/javascript/JavaScriptArtifactReconstruction.ts index e5871c2b36..7acf03b058 100644 --- a/src/application/javascript/JavaScriptArtifactReconstruction.ts +++ b/src/application/javascript/JavaScriptArtifactReconstruction.ts @@ -1,16 +1,12 @@ import { lstat, realpath } from "node:fs/promises"; -import { AsarArtifactReader } from "../../artifacts/AsarArtifactReader.js"; -import { - ArtifactReaderFailure, - type ArtifactReader, -} from "../../artifacts/ArtifactReader.js"; -import { DirectoryArtifactReader } from "../../artifacts/DirectoryArtifactReader.js"; +import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js"; +import { createJavaScriptArtifactReader as createReader } from "../../artifacts/javascript/JavaScriptArtifactReader.js"; import type { JavaScriptApplicationGraph } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { JavaScriptSemanticGraph } from "../../domain/javascript/javascriptSemanticGraph.js"; import type { ElectronBoundarySummary } from "../../domain/javascript/javascriptApplicationAnalysis.js"; import { analyzeJavaScriptArtifactFiles } from "./JavaScriptArtifactAnalysis.js"; -import { readJavaScriptArtifactFiles } from "./JavaScriptArtifactFiles.js"; +import { readJavaScriptArtifactFiles } from "../../artifacts/javascript/JavaScriptArtifactFiles.js"; import { buildJavaScriptArtifactGraph } from "./JavaScriptArtifactGraphBuilder.js"; import { javascriptArtifactReconstructionInputSchema, @@ -124,14 +120,6 @@ const resolveFormat = async ( return observed; }; -const createReader = ( - path: string, - format: "asar" | "directory", -): ArtifactReader => - format === "asar" - ? new AsarArtifactReader(path) - : new DirectoryArtifactReader(path); - const abortIfNeeded = (signal?: AbortSignal): void => { if (signal?.aborted === true) throw new ArtifactReaderFailure( diff --git a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts index a5a9fe93cb..e225c0af17 100644 --- a/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts +++ b/src/application/javascript/JavaScriptHtmlBaseResolution.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; const paths = [ "renderer/index.html", "assets/app.js", diff --git a/src/application/javascript/JavaScriptJsonModules.ts b/src/application/javascript/JavaScriptJsonModules.ts index 4c0c825366..3733db5cb6 100644 --- a/src/application/javascript/JavaScriptJsonModules.ts +++ b/src/application/javascript/JavaScriptJsonModules.ts @@ -1,6 +1,6 @@ import { compareCodePoints } from "../../domain/canonicalOrdering.js"; import type { JavaScriptJsonModuleObservation } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; /** Parse one approved JSON module without evaluating JavaScript or resolving imports. */ export const analyzeJavaScriptJsonModule = ( diff --git a/src/application/javascript/JavaScriptModuleRelationships.ts b/src/application/javascript/JavaScriptModuleRelationships.ts index c3f4ed5e9f..d3b688ca4b 100644 --- a/src/application/javascript/JavaScriptModuleRelationships.ts +++ b/src/application/javascript/JavaScriptModuleRelationships.ts @@ -11,7 +11,7 @@ import type { JavaScriptSemanticIr, JavaScriptSemanticModuleLink, } from "../../domain/javascript/javascriptSemanticIr.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { addAstContainsEdge, artifactLocalIdentity, diff --git a/src/application/javascript/JavaScriptModuleSpecifierPaths.test.ts b/src/application/javascript/JavaScriptModuleSpecifierPaths.test.ts index cd5e3eedde..63f44337eb 100644 --- a/src/application/javascript/JavaScriptModuleSpecifierPaths.test.ts +++ b/src/application/javascript/JavaScriptModuleSpecifierPaths.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js"; const filesFor = (paths: readonly string[]) => diff --git a/src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts b/src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts index 90a61aeb10..e7d1c1240a 100644 --- a/src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts +++ b/src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts @@ -2,7 +2,7 @@ import { expect, it } from "vitest"; import { buildJavaScriptSemanticGraph } from "./JavaScriptSemanticGraphBuilder.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { queryJavaScriptSemanticGraph } from "../../domain/javascript/javascriptSemanticQuery.js"; import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js"; diff --git a/src/application/javascript/JavaScriptSemanticGraphBuilder.ts b/src/application/javascript/JavaScriptSemanticGraphBuilder.ts index c81418eedf..d82edde74f 100644 --- a/src/application/javascript/JavaScriptSemanticGraphBuilder.ts +++ b/src/application/javascript/JavaScriptSemanticGraphBuilder.ts @@ -14,7 +14,7 @@ import type { import { sourceRangesEqual as rangesEqual } from "../../domain/javascript/javascriptStaticAnalysisHelpers.js"; import type { JavaScriptApplicationGraph } from "../../domain/javascript/javascriptApplicationGraph.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { projectSemanticEvents, projectSemanticTimers, diff --git a/src/application/javascript/JavaScriptSemanticGraphConstruction.ts b/src/application/javascript/JavaScriptSemanticGraphConstruction.ts index faa46e7ee9..a19f749826 100644 --- a/src/application/javascript/JavaScriptSemanticGraphConstruction.ts +++ b/src/application/javascript/JavaScriptSemanticGraphConstruction.ts @@ -12,7 +12,7 @@ import type { JavaScriptSemanticGraphUnknown, } from "../../domain/javascript/javascriptSemanticGraphSchemas.js"; import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { inferredSemanticEvidence, observedSemanticEvidence, diff --git a/src/application/javascript/JavaScriptSemanticGraphEvidence.ts b/src/application/javascript/JavaScriptSemanticGraphEvidence.ts index 9cfaa88b48..4b339ec066 100644 --- a/src/application/javascript/JavaScriptSemanticGraphEvidence.ts +++ b/src/application/javascript/JavaScriptSemanticGraphEvidence.ts @@ -1,7 +1,7 @@ import type { ApplicationGraphEvidence } from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js"; import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraph.js"; import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; const INFERENCE_LIMITATION = "This relationship is a conservative static candidate; it does not prove runtime execution or causal flow."; diff --git a/src/application/javascript/JavaScriptSemanticGraphFingerprintProjection.ts b/src/application/javascript/JavaScriptSemanticGraphFingerprintProjection.ts index 0634b341e9..06c6068208 100644 --- a/src/application/javascript/JavaScriptSemanticGraphFingerprintProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphFingerprintProjection.ts @@ -4,7 +4,7 @@ import { type JavaScriptSemanticGraphNode, } from "../../domain/javascript/javascriptSemanticGraph.js"; import type { JavaScriptSemanticIr } from "../../domain/javascript/javascriptSemanticIr.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { observedSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js"; /** Bind domain fingerprint components to exact semantic function nodes. */ diff --git a/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts b/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts index c31aef320d..fba83df6b0 100644 --- a/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts @@ -3,7 +3,7 @@ import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javasc import type { JavaScriptSemanticIr } from "../../domain/javascript/javascriptSemanticIr.js"; import { sourceRangesEqual } from "../../domain/javascript/javascriptStaticAnalysisHelpers.js"; import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { addSemanticGraphNode, addSemanticGraphRelation, diff --git a/src/application/javascript/JavaScriptSemanticGraphResources.test.ts b/src/application/javascript/JavaScriptSemanticGraphResources.test.ts index fd14ef93c1..2f6efff32d 100644 --- a/src/application/javascript/JavaScriptSemanticGraphResources.test.ts +++ b/src/application/javascript/JavaScriptSemanticGraphResources.test.ts @@ -2,7 +2,7 @@ import { expect, it } from "vitest"; import { buildJavaScriptSemanticGraph } from "./JavaScriptSemanticGraphBuilder.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { queryJavaScriptSemanticGraph } from "../../domain/javascript/javascriptSemanticQuery.js"; import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js"; diff --git a/src/application/javascript/JavaScriptSemanticGraphScope.test.ts b/src/application/javascript/JavaScriptSemanticGraphScope.test.ts index ec7b0341ef..cb4a3b7deb 100644 --- a/src/application/javascript/JavaScriptSemanticGraphScope.test.ts +++ b/src/application/javascript/JavaScriptSemanticGraphScope.test.ts @@ -2,7 +2,7 @@ import { expect, it } from "vitest"; import { buildJavaScriptSemanticGraph } from "./JavaScriptSemanticGraphBuilder.js"; import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js"; -import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js"; import { topLevelBinding } from "../../domain/javascript/javascriptSemanticAnalysis.fixture.js"; diff --git a/src/application/ArtifactInventory/hash.ts b/src/artifacts/ArtifactHash.ts similarity index 78% rename from src/application/ArtifactInventory/hash.ts rename to src/artifacts/ArtifactHash.ts index c988717c1c..b3bf43bfab 100644 --- a/src/application/ArtifactInventory/hash.ts +++ b/src/artifacts/ArtifactHash.ts @@ -1,9 +1,10 @@ import { createHash } from "node:crypto"; import type { Readable } from "node:stream"; -import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js"; -import { streamChunkToBuffer } from "../../artifacts/StreamBytes.js"; +import { ArtifactReaderFailure } from "./ArtifactReader.js"; +import { streamChunkToBuffer } from "./StreamBytes.js"; +/** Full stream digest and byte count with bounded classification evidence. */ export type HashResult = { readonly sha256: string; readonly bytes: number; @@ -13,6 +14,7 @@ export type HashResult = { /** Bounded classification evidence; this is not an executable format size limit. */ export const ARTIFACT_CLASSIFICATION_PREFIX_BYTES = 8_192; +/** Preserve the inventory cancellation reason at a stream boundary. */ export const abortIfNeeded = (signal?: AbortSignal): void => { if (signal?.aborted === true) throw new ArtifactReaderFailure( @@ -21,6 +23,7 @@ export const abortIfNeeded = (signal?: AbortSignal): void => { ); }; +/** Hash every stream byte and retain its bounded classification prefix. */ export const hashReadable = async ( stream: Readable, signal?: AbortSignal, diff --git a/src/application/javascript/JavaScriptArtifactFiles.ts b/src/artifacts/javascript/JavaScriptArtifactFiles.ts similarity index 86% rename from src/application/javascript/JavaScriptArtifactFiles.ts rename to src/artifacts/javascript/JavaScriptArtifactFiles.ts index e70e765abc..6f9cdb845b 100644 --- a/src/application/javascript/JavaScriptArtifactFiles.ts +++ b/src/artifacts/javascript/JavaScriptArtifactFiles.ts @@ -3,61 +3,26 @@ import { isAbsolute } from "node:path"; import type { Readable } from "node:stream"; import { compareCodePoints } from "../../domain/canonicalOrdering.js"; -import { AsarArtifactReader } from "../../artifacts/AsarArtifactReader.js"; +import { AsarArtifactReader } from "../AsarArtifactReader.js"; import { ArtifactPathRegistry, normalizeArtifactPath, -} from "../../artifacts/ArtifactPaths.js"; +} from "../ArtifactPaths.js"; import { ArtifactReaderFailure, type ArtifactEntry, type ArtifactReader, -} from "../../artifacts/ArtifactReader.js"; -import { streamChunkToBuffer } from "../../artifacts/StreamBytes.js"; -import { hashReadable } from "../ArtifactInventory/hash.js"; -import type { ArtifactInventorySnapshot } from "../ArtifactInventory.js"; +} from "../ArtifactReader.js"; +import { streamChunkToBuffer } from "../StreamBytes.js"; +import { hashReadable } from "../ArtifactHash.js"; +import type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySnapshot.js"; -/** Relevant file categories projected from the complete artifact inventory. */ -export type JavaScriptArtifactFileKind = - | "package-json" - | "json" - | "javascript" - | "html" - | "source-map" - | "native-addon"; - -/** One content-addressed local artifact file and its text availability. */ -export interface JavaScriptArtifactFile { - readonly path: string; - readonly container_sha256: string; - readonly sha256: string; - readonly bytes: number; - readonly inventory_artifact_id: string; - readonly kind: JavaScriptArtifactFileKind; - readonly unpacked: boolean; - readonly text: - | { readonly included: true; readonly value: string } - | { - readonly included: false; - readonly reason: "not-applicable" | "invalid-utf8"; - }; -} - -/** One filesystem-backed ASAR nested beneath a directory input. */ -export interface JavaScriptArtifactContainer { - readonly path: string; - readonly sha256: string; - readonly bytes: number; - readonly inventory_artifact_id: string; -} - -/** Deterministic relevant-file projection plus explicit omissions. */ -export interface JavaScriptArtifactFileSet { - readonly files: readonly JavaScriptArtifactFile[]; - readonly containers: readonly JavaScriptArtifactContainer[]; - readonly text_bytes_read: number; - readonly invalid_utf8_files: number; -} +import type { + JavaScriptArtifactFileKind, + JavaScriptArtifactFile, + JavaScriptArtifactContainer, + JavaScriptArtifactFileSet, +} from "../../domain/javascript/javascriptArtifactFiles.js"; interface ExpectedFile { readonly path: string; diff --git a/src/artifacts/javascript/JavaScriptArtifactReader.ts b/src/artifacts/javascript/JavaScriptArtifactReader.ts new file mode 100644 index 0000000000..4d2b88c528 --- /dev/null +++ b/src/artifacts/javascript/JavaScriptArtifactReader.ts @@ -0,0 +1,12 @@ +import { AsarArtifactReader } from "../AsarArtifactReader.js"; +import type { ArtifactReader } from "../ArtifactReader.js"; +import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js"; + +/** Create the reader for an admitted JavaScript artifact format. */ +export const createJavaScriptArtifactReader = ( + path: string, + format: "asar" | "directory", +): ArtifactReader => + format === "asar" + ? new AsarArtifactReader(path) + : new DirectoryArtifactReader(path); diff --git a/src/domain/artifactInventorySnapshot.ts b/src/domain/artifactInventorySnapshot.ts new file mode 100644 index 0000000000..a219727a12 --- /dev/null +++ b/src/domain/artifactInventorySnapshot.ts @@ -0,0 +1,18 @@ +import type { + ArtifactInventoryResult, + ArtifactNode, + IntegrityContradiction, +} from "./artifactGraph.js"; + +/** Immutable inventory produced by one complete artifact scan. */ +export interface ArtifactInventorySnapshot { + readonly manifest: ArtifactInventoryResult["manifest"]; + readonly nodes: readonly ArtifactNode[]; + readonly occurrences: ArtifactInventoryResult["occurrences"]; + readonly edges: ArtifactInventoryResult["edges"]; + readonly provenance: ReadonlyArray< + ArtifactInventoryResult["provenance"][number] + >; + readonly integrity_contradictions: readonly IntegrityContradiction[]; + readonly limitations: readonly string[]; +} diff --git a/src/domain/javascript/javascriptArtifactFiles.ts b/src/domain/javascript/javascriptArtifactFiles.ts new file mode 100644 index 0000000000..f0f03e0a08 --- /dev/null +++ b/src/domain/javascript/javascriptArtifactFiles.ts @@ -0,0 +1,41 @@ +/** Relevant file categories projected from the complete artifact inventory. */ +export type JavaScriptArtifactFileKind = + | "package-json" + | "json" + | "javascript" + | "html" + | "source-map" + | "native-addon"; + +/** One content-addressed local artifact file and its text availability. */ +export interface JavaScriptArtifactFile { + readonly path: string; + readonly container_sha256: string; + readonly sha256: string; + readonly bytes: number; + readonly inventory_artifact_id: string; + readonly kind: JavaScriptArtifactFileKind; + readonly unpacked: boolean; + readonly text: + | { readonly included: true; readonly value: string } + | { + readonly included: false; + readonly reason: "not-applicable" | "invalid-utf8"; + }; +} + +/** One filesystem-backed ASAR nested beneath a directory input. */ +export interface JavaScriptArtifactContainer { + readonly path: string; + readonly sha256: string; + readonly bytes: number; + readonly inventory_artifact_id: string; +} + +/** Deterministic relevant-file projection plus explicit omissions. */ +export interface JavaScriptArtifactFileSet { + readonly files: readonly JavaScriptArtifactFile[]; + readonly containers: readonly JavaScriptArtifactContainer[]; + readonly text_bytes_read: number; + readonly invalid_utf8_files: number; +} diff --git a/tests/boundary/filesystem/artifactMzInventory.test.ts b/tests/boundary/filesystem/artifactMzInventory.test.ts index 26f1b91a73..fd9884f957 100644 --- a/tests/boundary/filesystem/artifactMzInventory.test.ts +++ b/tests/boundary/filesystem/artifactMzInventory.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from "vitest"; import { classifyArtifactContent } from "../../../src/application/ArtifactGraphConstruction.js"; import { classifyRoot } from "../../../src/application/ArtifactInventory/classify.js"; -import { ARTIFACT_CLASSIFICATION_PREFIX_BYTES } from "../../../src/application/ArtifactInventory/hash.js"; +import { ARTIFACT_CLASSIFICATION_PREFIX_BYTES } from "../../../src/artifacts/ArtifactHash.js"; import { scanCanonicalArtifactInventory } from "../../../src/application/ArtifactInventory/scanCanonical.js"; import { targetFormatSchema } from "../../../src/contracts/toolOutputSchemaPrimitives.js"; import { artifactInventoryResultSchema } from "../../../src/domain/artifactGraph.js"; diff --git a/tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts b/tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts index 93b1a7ab35..74fb9ab221 100644 --- a/tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts +++ b/tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts @@ -5,7 +5,7 @@ import { createPackage } from "@electron/asar"; import { describe, expect, it } from "vitest"; import { scanArtifactInventory } from "../../../src/application/ArtifactInventory.js"; -import { readJavaScriptArtifactFiles } from "../../../src/application/javascript/JavaScriptArtifactFiles.js"; +import { readJavaScriptArtifactFiles } from "../../../src/artifacts/javascript/JavaScriptArtifactFiles.js"; import { AsarArtifactReader } from "../../../src/artifacts/AsarArtifactReader.js"; import { DirectoryArtifactReader } from "../../../src/artifacts/DirectoryArtifactReader.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; diff --git a/tests/boundary/filesystem/javascriptArtifactReconstruction.test.ts b/tests/boundary/filesystem/javascriptArtifactReconstruction.test.ts index a510ea17f8..cfab06c6bb 100644 --- a/tests/boundary/filesystem/javascriptArtifactReconstruction.test.ts +++ b/tests/boundary/filesystem/javascriptArtifactReconstruction.test.ts @@ -6,7 +6,7 @@ import { expect, it } from "vitest"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; -import { readJavaScriptArtifactFiles } from "../../../src/application/javascript/JavaScriptArtifactFiles.js"; +import { readJavaScriptArtifactFiles } from "../../../src/artifacts/javascript/JavaScriptArtifactFiles.js"; import { reconstructJavaScriptArtifact } from "../../../src/application/javascript/JavaScriptArtifactReconstruction.js"; import { scanArtifactInventory } from "../../../src/application/ArtifactInventory.js"; import { diff --git a/tests/boundary/javascript/packageConditionalExports.test.ts b/tests/boundary/javascript/packageConditionalExports.test.ts index 88be2f269f..05b4465ef5 100644 --- a/tests/boundary/javascript/packageConditionalExports.test.ts +++ b/tests/boundary/javascript/packageConditionalExports.test.ts @@ -8,7 +8,7 @@ import { resolveArtifactPathByContext, type ArtifactPathResolution, } from "../../../src/application/javascript/JavaScriptArtifactPathResolution.js"; -import type { JavaScriptArtifactFile } from "../../../src/application/javascript/JavaScriptArtifactFiles.js"; +import type { JavaScriptArtifactFile } from "../../../src/domain/javascript/javascriptArtifactFiles.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; const execute = promisify(execFile); const TARGETS = [ diff --git a/tests/evaluation/moduleBoundaries.test.ts b/tests/evaluation/moduleBoundaries.test.ts index abf88802fd..b9b1146d81 100644 --- a/tests/evaluation/moduleBoundaries.test.ts +++ b/tests/evaluation/moduleBoundaries.test.ts @@ -18,6 +18,48 @@ describe("incremental module import boundaries", () => { ["src/contracts/probe.ts", "../android/Provider.js", false], ["src/contracts/probe.ts", "../cli.js", false], ["src/contracts/probe.ts", "../composition/android.js", false], + [ + "src/artifacts/javascript/probe.ts", + "../../application/ArtifactInventory.js", + false, + ], + [ + "src/artifacts/javascript/probe.ts", + "../../composition/android.js", + false, + ], + ["src/artifacts/javascript/probe.ts", "../../cli.js", false], + [ + "src/artifacts/ArtifactHash.ts", + "../application/ArtifactInventory/hash.js", + false, + ], + [ + "src/artifacts/javascript/probe.ts", + "../../domain/artifactInventorySnapshot.js", + true, + ], + ["src/artifacts/javascript/probe.ts", "../AsarArtifactReader.js", true], + [ + "src/application/javascript/probe.ts", + "../../artifacts/AsarArtifactReader.js", + false, + ], + [ + "src/application/javascript/probe.ts", + "../../artifacts/DirectoryArtifactReader.js", + false, + ], + [ + "src/application/javascript/probe.ts", + "../../artifacts/javascript/JavaScriptArtifactReader.js", + true, + ], + [ + "src/application/ArtifactInventory/probe.ts", + "../../artifacts/AsarArtifactReader.js", + true, + ], ["src/domain/probe.test.ts", "../composition/firmware.js", false], ["src/application/probe.ts", "../android/JadxProvider.js", false], ["src/application/probe.ts", "../composition/android.js", false],