@archwall/core 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,76 +24,6 @@ let node_path = require("node:path");
24
24
  node_path = __toESM(node_path, 1);
25
25
  let picomatch = require("picomatch");
26
26
  picomatch = __toESM(picomatch, 1);
27
- //#region src/paths.ts
28
- /** Forward slashes everywhere, so one string form crosses platforms. */
29
- function normalize(p) {
30
- return p.replaceAll("\\", "/");
31
- }
32
- /**
33
- * A file's path relative to `root`, forward-slashed, or `null` when it does not lie
34
- * strictly inside `root`.
35
- *
36
- * The one answer to "where is this file, in the terms my patterns are written in".
37
- * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
38
- * `within` all describe positions in a tree, and they must all agree on what a position
39
- * is — including on the edge cases: the root itself is not *inside* the root, and a file
40
- * above it has no position at all.
41
- *
42
- * A path that is already relative is taken as relative *to the root* rather than resolved
43
- * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
44
- * `@archwall/test-utils`) use bare ids, and resolving those against the working directory
45
- * would silently place every module outside the project.
46
- */
47
- function sourceRelative(root, file) {
48
- const normalized = normalize(file);
49
- if (!node_path.isAbsolute(normalized)) return normalized === "" ? null : normalized;
50
- const rel = normalize(node_path.relative(root, normalized));
51
- if (rel === "" || rel.startsWith("../") || rel === ".." || node_path.isAbsolute(rel)) return null;
52
- return rel;
53
- }
54
- /**
55
- * Repository-relative, for anything that leaves the process: violation fingerprints,
56
- * reporter output, SARIF `artifactLocation.uri`.
57
- *
58
- * Absolute paths are the right module identity *inside* a run and wrong in every output,
59
- * because they make results machine-specific. SARIF in particular is silently useless with
60
- * absolute URIs: GitHub code scanning cannot associate the result with a repository file.
61
- *
62
- * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
63
- * root is returned as-is rather than as `null`, because output must always print something,
64
- * whereas matching must be able to say "not here".
65
- */
66
- function toRelative(root, id) {
67
- const normalized = normalize(id);
68
- if (!node_path.isAbsolute(normalized)) return normalized;
69
- return sourceRelative(root, normalized) ?? normalized;
70
- }
71
- /**
72
- * FNV-1a, 64-bit, as 16 lowercase hex chars.
73
- *
74
- * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
75
- * worker, edge runtime), and this hash is used for identity, never for security.
76
- */
77
- function stableHash(input) {
78
- let h1 = 2166136261;
79
- let h2 = 16777619;
80
- for (let i = 0; i < input.length; i++) {
81
- const c = input.charCodeAt(i);
82
- h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
83
- h2 = Math.imul(h2 ^ (c << 5 | c >>> 3), 16777619) >>> 0;
84
- }
85
- return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
86
- }
87
- /**
88
- * Joins parts into one hashable string.
89
- *
90
- * `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
91
- * a delimiter that can occur inside a part makes two different tuples hash identically.
92
- */
93
- function hashParts(parts) {
94
- return stableHash(parts.join("\0"));
95
- }
96
- //#endregion
97
27
  //#region src/errors.ts
98
28
  var ArchWallError = class extends Error {
99
29
  constructor(message) {
@@ -317,6 +247,76 @@ function assertIrCompatible(graphVersion) {
317
247
  if (irMajor(graphVersion) !== irMajor("1.0.0")) throw new IrVersionMismatchError(graphVersion, IR_VERSION);
318
248
  }
319
249
  //#endregion
250
+ //#region src/paths.ts
251
+ /** Forward slashes everywhere, so one string form crosses platforms. */
252
+ function normalize(p) {
253
+ return p.replaceAll("\\", "/");
254
+ }
255
+ /**
256
+ * A file's path relative to `root`, forward-slashed, or `null` when it does not lie
257
+ * strictly inside `root`.
258
+ *
259
+ * The one answer to "where is this file, in the terms my patterns are written in".
260
+ * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
261
+ * `within` all describe positions in a tree, and they must all agree on what a position
262
+ * is — including on the edge cases: the root itself is not *inside* the root, and a file
263
+ * above it has no position at all.
264
+ *
265
+ * A path that is already relative is taken as relative *to the root* rather than resolved
266
+ * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
267
+ * `@archwall/test-utils`) use bare ids, and resolving those against the working directory
268
+ * would silently place every module outside the project.
269
+ */
270
+ function sourceRelative(root, file) {
271
+ const normalized = normalize(file);
272
+ if (!node_path.isAbsolute(normalized)) return normalized === "" ? null : normalized;
273
+ const rel = normalize(node_path.relative(root, normalized));
274
+ if (rel === "" || rel.startsWith("../") || rel === ".." || node_path.isAbsolute(rel)) return null;
275
+ return rel;
276
+ }
277
+ /**
278
+ * Repository-relative, for anything that leaves the process: violation fingerprints,
279
+ * reporter output, SARIF `artifactLocation.uri`.
280
+ *
281
+ * Absolute paths are the right module identity *inside* a run and wrong in every output,
282
+ * because they make results machine-specific. SARIF in particular is silently useless with
283
+ * absolute URIs: GitHub code scanning cannot associate the result with a repository file.
284
+ *
285
+ * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
286
+ * root is returned as-is rather than as `null`, because output must always print something,
287
+ * whereas matching must be able to say "not here".
288
+ */
289
+ function toRelative(root, id) {
290
+ const normalized = normalize(id);
291
+ if (!node_path.isAbsolute(normalized)) return normalized;
292
+ return sourceRelative(root, normalized) ?? normalized;
293
+ }
294
+ /**
295
+ * FNV-1a, 64-bit, as 16 lowercase hex chars.
296
+ *
297
+ * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
298
+ * worker, edge runtime), and this hash is used for identity, never for security.
299
+ */
300
+ function stableHash(input) {
301
+ let h1 = 2166136261;
302
+ let h2 = 16777619;
303
+ for (let i = 0; i < input.length; i++) {
304
+ const c = input.charCodeAt(i);
305
+ h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
306
+ h2 = Math.imul(h2 ^ (c << 5 | c >>> 3), 16777619) >>> 0;
307
+ }
308
+ return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
309
+ }
310
+ /**
311
+ * Joins parts into one hashable string.
312
+ *
313
+ * `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
314
+ * a delimiter that can occur inside a part makes two different tuples hash identically.
315
+ */
316
+ function hashParts(parts) {
317
+ return stableHash(parts.join("\0"));
318
+ }
319
+ //#endregion
320
320
  //#region src/analysis/cache.ts
321
321
  /**
322
322
  * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
@@ -942,4 +942,4 @@ Object.defineProperty(exports, "toRelative", {
942
942
  }
943
943
  });
944
944
 
945
- //# sourceMappingURL=prepare-CEaZxLPI.cjs.map
945
+ //# sourceMappingURL=prepare-DNER1gV3.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-CEaZxLPI.cjs","names":["path","#modules","#edges","#base","#mutableModules","#mutableEdges","#memo","#graph","#out","#in","#buildAdjacency","#byTag","#byKind","#byPackage","#index","#scope","#scopedEdges"],"sources":["../src/paths.ts","../src/errors.ts","../src/graph/ir.ts","../src/analysis/cache.ts","../src/graph/query.ts","../src/engine/prepare.ts"],"sourcesContent":["import * as path from \"node:path\";\n\n/** Forward slashes everywhere, so one string form crosses platforms. */\nfunction normalize(p: string): string {\n return p.replaceAll(\"\\\\\", \"/\");\n}\n\n/**\n * A file's path relative to `root`, forward-slashed, or `null` when it does not lie\n * strictly inside `root`.\n *\n * The one answer to \"where is this file, in the terms my patterns are written in\".\n * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s\n * `within` all describe positions in a tree, and they must all agree on what a position\n * is — including on the edge cases: the root itself is not *inside* the root, and a file\n * above it has no position at all.\n *\n * A path that is already relative is taken as relative *to the root* rather than resolved\n * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,\n * `@archwall/test-utils`) use bare ids, and resolving those against the working directory\n * would silently place every module outside the project.\n */\nexport function sourceRelative(root: string, file: string): string | null {\n const normalized = normalize(file);\n if (!path.isAbsolute(normalized)) return normalized === \"\" ? null : normalized;\n const rel = normalize(path.relative(root, normalized));\n if (rel === \"\" || rel.startsWith(\"../\") || rel === \"..\" || path.isAbsolute(rel)) return null;\n return rel;\n}\n\n/**\n * Repository-relative, for anything that leaves the process: violation fingerprints,\n * reporter output, SARIF `artifactLocation.uri`.\n *\n * Absolute paths are the right module identity *inside* a run and wrong in every output,\n * because they make results machine-specific. SARIF in particular is silently useless with\n * absolute URIs: GitHub code scanning cannot associate the result with a repository file.\n *\n * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the\n * root is returned as-is rather than as `null`, because output must always print something,\n * whereas matching must be able to say \"not here\".\n */\nexport function toRelative(root: string, id: string): string {\n const normalized = normalize(id);\n if (!path.isAbsolute(normalized)) return normalized;\n const rel = sourceRelative(root, normalized);\n // Outside the root: keep it absolute rather than emitting a ../../.. chain that is just\n // as machine-specific and harder to read.\n return rel ?? normalized;\n}\n\n/**\n * FNV-1a, 64-bit, as 16 lowercase hex chars.\n *\n * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,\n * worker, edge runtime), and this hash is used for identity, never for security.\n */\nexport function stableHash(input: string): string {\n // 64-bit FNV-1a via two 32-bit halves, since JS bitwise ops are 32-bit.\n let h1 = 0x811c9dc5;\n let h2 = 0x1000193;\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i);\n h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0;\n h2 = Math.imul(h2 ^ ((c << 5) | (c >>> 3)), 0x01000193) >>> 0;\n }\n return h1.toString(16).padStart(8, \"0\") + h2.toString(16).padStart(8, \"0\");\n}\n\n/**\n * Joins parts into one hashable string.\n *\n * `\\0` rather than a space: parts are paths and specifiers, which may contain spaces, and\n * a delimiter that can occur inside a part makes two different tuples hash identically.\n */\nexport function hashParts(parts: readonly string[]): string {\n return stableHash(parts.join(\"\\0\"));\n}\n","export class ArchWallError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class IrVersionMismatchError extends ArchWallError {\n constructor(\n readonly graphVersion: string,\n readonly coreVersion: string,\n ) {\n super(\n `Incompatible graph IR: adapter produced irVersion ${graphVersion}, but @archwall/core supports ${coreVersion} (majors must match). Upgrade the adapter or core so their IR majors align.`,\n );\n }\n}\n","import { ArchWallError, IrVersionMismatchError } from \"../errors.js\";\n\n/** Semver of the Project Graph IR schema itself, independent of package versions. */\nexport const IR_VERSION = \"1.0.0\";\n\n/**\n * A module's identity, in the IR's own vocabulary rather than the host's.\n *\n * ```\n * file:<repo-relative-posix-path> source | workspace | excluded\n * pkg:<name> package — the package, not one of its files\n * builtin:<specifier> builtin — always prefixed (builtin:node:fs)\n * virtual:<host>:<opaque> virtual — host-synthesized, host-specific by nature\n * unresolved:<raw-specifier> unresolved\n * ```\n *\n * Producers report host facts; `GraphBuilder` decides identity — the same division\n * {@link ModuleKind} already uses. That is what makes a violation's fingerprint the same under\n * every bundler, which is what makes a baseline file possible at all.\n */\nexport type ModuleId = string;\n\n/** The schemes {@link ModuleId} recognises. */\nexport const MODULE_ID_SCHEMES = [\"file\", \"pkg\", \"builtin\", \"virtual\", \"unresolved\"] as const;\n\nexport type ModuleIdScheme = (typeof MODULE_ID_SCHEMES)[number];\n\nconst SCHEME_OF = /^(file|pkg|builtin|virtual|unresolved):/;\n\n/**\n * Splits a canonical id into its scheme and body, or null when it carries no known scheme.\n *\n * Null is a legitimate answer, not an error: in-memory graphs (`@archwall/test-utils`, a\n * playground) use bare ids, and every consumer here degrades to treating the id as opaque.\n */\nexport function parseModuleId(id: ModuleId): { scheme: ModuleIdScheme; body: string } | null {\n const m = SCHEME_OF.exec(id);\n if (!m) return null;\n const scheme = m[1] as ModuleIdScheme;\n return { scheme, body: id.slice(scheme.length + 1) };\n}\n\n/**\n * The id as a human should read it: the path, the package name, the builtin specifier.\n *\n * Used by every reporter and offered to rules as `RuleContext.display`, so that a message names\n * `src/domain/rules.ts` and `react` rather than a scheme-prefixed id — or, as before canonical\n * ids existed, an absolute path from whichever machine produced the graph.\n *\n * `virtual:` keeps its prefix: it is not a path, and the prefix is the only thing that says so.\n */\nexport function displayModuleId(id: ModuleId): string {\n const parsed = parseModuleId(id);\n if (parsed === null || parsed.scheme === \"virtual\") return id;\n return parsed.body;\n}\n\nexport type WellKnownCapability =\n /** `Edge.loc` is populated. */\n | \"import-locations\"\n /** Dynamic `import()` edges are present and marked `kind: \"dynamic\"`. */\n | \"dynamic-imports\"\n /** Every module in the project is present; absence of a module IS evidence. */\n | \"complete-graph\"\n /** Re-export edges are distinguished from plain imports. */\n | \"reexport-edges\"\n /**\n * `Edge.rawSpecifier` is what the author wrote, not a copy of the resolved id. A rule\n * that matches on specifiers must require this, or it silently matches nothing on hosts\n * that cannot supply them and reports a clean run rather than an unavailable one.\n */\n | \"raw-specifiers\"\n /**\n * Type-only edges are PRESENT and carry `attributes.typeOnly`. See {@link EdgeAttributes}.\n *\n * A rule that treats type-only imports differently must require this. Without it, a host\n * that erases type imports (every bundler does) is indistinguishable from one where the\n * code genuinely has no type imports — and \"no `attributes.typeOnly` anywhere\" would be\n * read as \"nothing is type-only\" rather than \"nobody asked\".\n */\n | \"type-only-edges\";\n\n/**\n * Open union: adapters may declare capabilities core does not know about, and rules may\n * require them, without an IR major. `WellKnownCapability` keeps autocomplete useful for\n * the ones core ships.\n */\nexport type Capability = WellKnownCapability | (string & {});\n\nexport interface HostInfo {\n name: string;\n version: string;\n capabilities: ReadonlySet<Capability>;\n}\n\nexport interface SourceLocation {\n file: string;\n /** 1-based */\n line: number;\n /** 0-based */\n column: number;\n}\n\nexport type WellKnownEdgeKind = \"static\" | \"dynamic\" | \"reexport\";\n\n/**\n * Open union so future graph facts (CSS imports, worker edges) arrive additively. Consumers\n * must treat an unrecognised kind as \"some dependency exists\" — never assume exhaustiveness.\n *\n * `kind` answers ONE question — roughly \"what syntax produced this edge\" — and deliberately\n * keeps answering only that. Everything else an edge might be belongs in\n * {@link EdgeAttributes}; see the note there for why.\n */\nexport type EdgeKind = WellKnownEdgeKind | (string & {});\n\n/**\n * Orthogonal facts about an edge, as an OPEN bag.\n *\n * `kind` is one enum, but the domain is not one-dimensional. `export type * from \"./x\"` is\n * *both* a re-export and type-only; a dynamic import of a barrel is both dynamic and a\n * re-export. Modelling those as one enum forces every producer to pick a winner, and forces\n * every consumer to guess which axis the winner came from.\n *\n * So the axes split: `kind` keeps the syntactic one, and everything else lands here, where it\n * composes. A bag rather than named fields because the next axis is always unknown — import\n * attributes (`with { type: \"json\" }`), worker edges, CSS edges, `require` vs `import`\n * interop — and each one arriving as a new top-level `Edge` field would be a new IR major.\n *\n * **Absent means \"the host did not say\", never \"false\".** That distinction is the whole\n * reason {@link WellKnownCapability} has `type-only-edges`: a bundler that erased type imports\n * before ArchWall saw them reports nothing here, and a rule must be able to tell that apart\n * from a codebase with no type imports in it.\n */\nexport interface EdgeAttributes {\n /**\n * The import is erased at compile time (`import type`, `export type`, or an\n * `import { type X }` specifier where every named binding is type-only).\n *\n * Requires the `type-only-edges` capability to be meaningful. `true` or absent — never\n * `false`, so that a producer cannot accidentally assert the negative it does not know.\n */\n typeOnly?: true;\n /** Third parties and future core versions extend here without an IR major. */\n [key: string]: string | true | undefined;\n}\n\n/**\n * What a module *is*, relative to the project being analysed.\n *\n * - `source` — a first-party file inside the analysed project\n * - `workspace` — a file owned by a *different* package in the same monorepo\n * - `package` — a third-party dependency (node_modules)\n * - `builtin` — a runtime builtin (`node:fs`, `bun:sqlite`, …)\n * - `virtual` — generated by the toolchain; no file on disk\n * - `unresolved` — the specifier could not be resolved to anything\n * - `excluded` — a real project file the config's `exclude` removed from analysis\n *\n * The seven-way split is load-bearing: a purity rule that cannot tell `node:crypto` from\n * `lodash` from `@myorg/shared-kernel` gives the wrong answer for two of the three.\n */\nexport type ModuleKind =\n | \"source\"\n | \"workspace\"\n | \"package\"\n | \"builtin\"\n | \"virtual\"\n | \"unresolved\"\n | \"excluded\";\n\nexport interface ModuleNode {\n /** Canonical; see {@link ModuleId}. */\n id: ModuleId;\n /**\n * Absolute path, for the kinds that denote a file: `source`, `workspace`, `excluded`.\n *\n * Null for everything else — including `package`, because a dependency is one node\n * (`pkg:react`) rather than one node per file, so there is no single file to name.\n */\n file: string | null;\n kind: ModuleKind;\n /** npm package name, for `kind: \"package\"`. */\n packageName?: string;\n /** Owning workspace package name, for `kind: \"workspace\"`. */\n workspace?: string;\n /** Filled by classification, e.g. layer → \"features\". */\n tags: ReadonlyMap<string, string>;\n}\n\n/** Code the project owns and can change — including sibling packages in the monorepo. */\nexport const FIRST_PARTY_KINDS = [\"source\", \"workspace\"] as const satisfies readonly ModuleKind[];\n\n/** A dependency the project does not own: third-party code or a runtime builtin. */\nexport const THIRD_PARTY_KINDS = [\"package\", \"builtin\"] as const satisfies readonly ModuleKind[];\n\nexport function isFirstParty(kind: ModuleKind): boolean {\n return kind === \"source\" || kind === \"workspace\";\n}\n\nexport function isThirdParty(kind: ModuleKind): boolean {\n return kind === \"package\" || kind === \"builtin\";\n}\n\nexport interface Edge {\n from: ModuleId;\n to: ModuleId;\n /** What the source wrote: \"@/features/auth\". */\n rawSpecifier: string;\n /** What it actually is after resolution. */\n resolvedPath: string;\n kind: EdgeKind;\n /** Present only if host capability allows. */\n loc?: SourceLocation;\n /** Orthogonal facts; see {@link EdgeAttributes}. Absent when the host reported none. */\n attributes?: EdgeAttributes;\n}\n\nexport type GraphDelivery = \"complete\" | \"progressive\";\n\n/**\n * Process-local counter behind {@link ProjectGraphInit.revision}.\n *\n * A fresh number per constructed graph is the conservative choice: a consumer keying a cache\n * on `revision` can only ever be told \"this is a different graph\", never falsely told it is\n * the same one. Content-addressed revisions would enable cache HITS across rebuilds, which is\n * the incremental-validation problem and deliberately not solved here.\n */\nlet revisionCounter = 0;\n\nexport interface ProjectGraphInit {\n host: HostInfo;\n /** Default \"complete\". */\n delivery?: GraphDelivery;\n modules: Iterable<readonly [ModuleId, ModuleNode]> | ReadonlyMap<ModuleId, ModuleNode>;\n edges: readonly Edge[];\n /** Default {@link IR_VERSION}. Adapters should leave this alone. */\n irVersion?: string;\n /**\n * Opaque identity for this graph. Defaults to a fresh process-local number.\n *\n * The contract is one-directional and deliberately weak: **equal revisions mean the same\n * graph; unequal revisions mean nothing.** Set it explicitly only if you can guarantee the\n * first half — a producer that content-hashes its inputs, for instance.\n */\n revision?: number;\n}\n\n/**\n * The module graph, as an OPAQUE handle.\n *\n * The backing stores are private and no accessor hands them out. Everything a consumer\n * legitimately needs is a method here or on `GraphQuery`; if something is missing, the fix\n * is to add a method, never to expose the store.\n *\n * That is what keeps the *representation* out of the IR contract: a `ReadonlyMap` plus an\n * `Edge[]` is the current implementation, not the promise.\n */\nexport class ProjectGraph {\n readonly irVersion: string;\n readonly host: HostInfo;\n readonly delivery: GraphDelivery;\n /**\n * See {@link ProjectGraphInit.revision}. Preserved across {@link replaceStores}, because a\n * derived graph is a deterministic function of this one and the config that derived it —\n * so a cache keyed on `(revision, configKey)` stays sound through the prepare pipeline.\n */\n readonly revision: number;\n readonly #modules: ReadonlyMap<ModuleId, ModuleNode>;\n readonly #edges: readonly Edge[];\n\n private constructor(\n irVersion: string,\n host: HostInfo,\n delivery: GraphDelivery,\n revision: number,\n modules: ReadonlyMap<ModuleId, ModuleNode>,\n edges: readonly Edge[],\n ) {\n this.irVersion = irVersion;\n this.host = host;\n this.delivery = delivery;\n this.revision = revision;\n this.#modules = modules;\n this.#edges = edges;\n }\n\n static create(init: ProjectGraphInit): ProjectGraph {\n const modules =\n init.modules instanceof Map\n ? (init.modules as ReadonlyMap<ModuleId, ModuleNode>)\n : new Map(init.modules as Iterable<readonly [ModuleId, ModuleNode]>);\n return new ProjectGraph(\n init.irVersion ?? IR_VERSION,\n init.host,\n init.delivery ?? \"complete\",\n init.revision ?? ++revisionCounter,\n modules,\n init.edges,\n );\n }\n\n get moduleCount(): number {\n return this.#modules.size;\n }\n\n get edgeCount(): number {\n return this.#edges.length;\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#modules.get(id);\n }\n\n hasModule(id: ModuleId): boolean {\n return this.#modules.has(id);\n }\n\n /** Every module, in graph order. */\n modules(): Iterable<ModuleNode> {\n return this.#modules.values();\n }\n\n /** Every module id, in graph order. */\n moduleIds(): Iterable<ModuleId> {\n return this.#modules.keys();\n }\n\n /** Every edge, in graph order. Never copy this — it is already immutable. */\n edges(): readonly Edge[] {\n return this.#edges;\n }\n\n /**\n * A new graph with replaced stores, same identity fields.\n *\n * @internal Engine and {@link GraphDraft} only. Not part of the IR contract.\n */\n replaceStores(modules: ReadonlyMap<ModuleId, ModuleNode>, edges?: readonly Edge[]): ProjectGraph {\n return new ProjectGraph(\n this.irVersion,\n this.host,\n this.delivery,\n this.revision,\n modules,\n edges ?? this.#edges,\n );\n }\n}\n\n/**\n * The write surface a {@link GraphTransform} gets.\n *\n * A transform adds, patches, and removes; it never constructs a graph. That is what keeps\n * {@link ProjectGraph} opaque in practice rather than only in principle, and it means a\n * transform cannot drop an IR field it does not know about.\n */\nexport interface GraphMutation {\n /** Read side, mirroring {@link ProjectGraph}. */\n module(id: ModuleId): ModuleNode | undefined;\n hasModule(id: ModuleId): boolean;\n modules(): Iterable<ModuleNode>;\n edges(): readonly Edge[];\n /** Adds a module, or replaces one with the same id. */\n addModule(node: ModuleNode): void;\n /** Merges fields into an existing module; `tags` merge key-by-key. No-op if absent. */\n patchModule(\n id: ModuleId,\n patch: Partial<Omit<ModuleNode, \"id\" | \"tags\">> & { tags?: Record<string, string> },\n ): void;\n addEdge(edge: Edge): void;\n /** Removes every edge the predicate accepts. */\n removeEdges(predicate: (edge: Edge) => boolean): void;\n}\n\n/**\n * Copy-on-write {@link GraphMutation} over a {@link ProjectGraph}.\n *\n * A transform that touches nothing costs nothing: the stores are only cloned on the first\n * write, and `commit()` returns the original graph when there were none.\n *\n * @internal\n */\nexport class GraphDraft implements GraphMutation {\n readonly #base: ProjectGraph;\n #modules: Map<ModuleId, ModuleNode> | undefined;\n #edges: Edge[] | undefined;\n\n constructor(base: ProjectGraph) {\n this.#base = base;\n }\n\n #mutableModules(): Map<ModuleId, ModuleNode> {\n if (this.#modules === undefined) {\n this.#modules = new Map();\n for (const m of this.#base.modules()) this.#modules.set(m.id, m);\n }\n return this.#modules;\n }\n\n #mutableEdges(): Edge[] {\n this.#edges ??= [...this.#base.edges()];\n return this.#edges;\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#modules ? this.#modules.get(id) : this.#base.module(id);\n }\n\n hasModule(id: ModuleId): boolean {\n return this.#modules ? this.#modules.has(id) : this.#base.hasModule(id);\n }\n\n modules(): Iterable<ModuleNode> {\n return this.#modules ? this.#modules.values() : this.#base.modules();\n }\n\n edges(): readonly Edge[] {\n return this.#edges ?? this.#base.edges();\n }\n\n addModule(node: ModuleNode): void {\n this.#mutableModules().set(node.id, node);\n }\n\n patchModule(\n id: ModuleId,\n patch: Partial<Omit<ModuleNode, \"id\" | \"tags\">> & { tags?: Record<string, string> },\n ): void {\n const current = this.module(id);\n if (current === undefined) return;\n const { tags: tagPatch, ...rest } = patch;\n const next: ModuleNode = { ...current, ...rest };\n if (tagPatch !== undefined) {\n const tags = new Map(current.tags);\n for (const [k, v] of Object.entries(tagPatch)) tags.set(k, v);\n next.tags = tags;\n }\n this.#mutableModules().set(id, next);\n }\n\n addEdge(edge: Edge): void {\n this.#mutableEdges().push(edge);\n }\n\n removeEdges(predicate: (edge: Edge) => boolean): void {\n const kept = this.edges().filter((e) => !predicate(e));\n if (kept.length !== this.edges().length) this.#edges = kept;\n }\n\n /** The resulting graph, or the untouched original when nothing was written. */\n commit(): ProjectGraph {\n if (this.#modules === undefined && this.#edges === undefined) return this.#base;\n const modules =\n this.#modules ??\n new Map<ModuleId, ModuleNode>([...this.#base.modules()].map((m) => [m.id, m]));\n return this.#base.replaceStores(modules, this.#edges);\n }\n}\n\nexport function irMajor(version: string): number {\n const m = /^(\\d+)\\./.exec(version);\n if (!m) throw new ArchWallError(`Malformed IR version: \"${version}\"`);\n return Number(m[1]);\n}\n\nexport function assertIrCompatible(graphVersion: string): void {\n if (irMajor(graphVersion) !== irMajor(IR_VERSION)) {\n throw new IrVersionMismatchError(graphVersion, IR_VERSION);\n }\n}\n","import type { GraphComputation } from \"../contracts/analysis.js\";\nimport type { GraphView } from \"../graph/query.js\";\n\n/**\n * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost\n * one traversal.\n *\n * The view is part of the key because a computation is an ENUMERATION of the graph, and\n * enumeration is scoped. A cache bound to the root query\n * would hand a rule scoped to `apps/web` the cycles of the whole repository — the rule's\n * `ctx.graph` narrowed and its `ctx.compute` silently not.\n *\n * Rules sharing a scope share the base query object, so they share the entry; the common case\n * (no scope at all) is still one evaluation for everyone.\n */\nexport class GraphComputationCache {\n readonly #memo = new Map<GraphView, Map<GraphComputation<unknown>, unknown>>();\n\n get<T>(computation: GraphComputation<T>, graph: GraphView): T {\n let perView = this.#memo.get(graph);\n if (perView === undefined) {\n perView = new Map();\n this.#memo.set(graph, perView);\n }\n const key = computation as GraphComputation<unknown>;\n if (perView.has(key)) return perView.get(key) as T;\n const value = computation.compute(graph);\n perView.set(key, value);\n return value;\n }\n}\n","import type { Edge, EdgeKind, ModuleId, ModuleKind, ModuleNode, ProjectGraph } from \"./ir.js\";\n\nexport interface ModuleFilter {\n /** ALL entries must match module tags. */\n tag?: Record<string, string>;\n /**\n * Any listed kind matches. `FIRST_PARTY_KINDS` / `THIRD_PARTY_KINDS` cover the two\n * groupings that are actually meaningful.\n */\n moduleKind?: ModuleKind | readonly ModuleKind[];\n packageName?: string;\n}\n\nexport interface EdgeFilter {\n kind?: EdgeKind;\n /** Any listed kind matches, applied to the edge's target. */\n toModuleKind?: ModuleKind | readonly ModuleKind[];\n fromTag?: Record<string, string>;\n toTag?: Record<string, string>;\n /** Tag key; keep edge iff BOTH endpoints have the tag and values differ. */\n crossing?: string;\n /**\n * Selects on {@link EdgeAttributes}. `true` requires the attribute present; `false` requires\n * it ABSENT; a string requires that exact value.\n *\n * `false` and \"absent\" are the same test on purpose — attributes are never stored as `false`\n * (see {@link EdgeAttributes}), so \"not type-only\" and \"nobody said\" are indistinguishable\n * *here* by construction. A rule that must tell them apart declares the corresponding\n * capability and gets skipped loudly instead, which is the only honest answer.\n */\n attributes?: Readonly<Record<string, string | boolean>>;\n}\n\nfunction matchesKind(m: ModuleNode, want: ModuleKind | readonly ModuleKind[]): boolean {\n return typeof want === \"string\" ? m.kind === want : want.includes(m.kind);\n}\n\nfunction matchesTags(m: ModuleNode, want: Record<string, string>): boolean {\n for (const k of Object.keys(want)) if (m.tags.get(k) !== want[k]) return false;\n return true;\n}\n\nconst NO_EDGES: readonly Edge[] = [];\n\n/**\n * Stable key for a filter, so the engine can bucket rules that want the same slice of the\n * graph and evaluate that slice once for all of them.\n */\nexport function filterKey(filter: EdgeFilter | ModuleFilter | undefined): string {\n if (filter === undefined) return \"*\";\n return JSON.stringify(filter, Object.keys(filter).sort());\n}\n\n/**\n * The adjacency and attribute indexes over one graph, built LAZILY per axis.\n *\n * One index serves every query over a graph, scoped or not: a scope narrows *which results\n * are returned*, and does not change what the graph contains, so it must never rebuild the\n * index of it.\n *\n * Each axis is built on first use. A run whose rules only walk edges never pays for the\n * tag, kind, and package indexes.\n */\nexport class GraphIndex {\n readonly #graph: ProjectGraph;\n #out: Map<ModuleId, Edge[]> | undefined;\n #in: Map<ModuleId, Edge[]> | undefined;\n #byTag: Map<string, ModuleId[]> | undefined;\n #byKind: Map<ModuleKind, ModuleNode[]> | undefined;\n #byPackage: Map<string, ModuleNode[]> | undefined;\n\n constructor(graph: ProjectGraph) {\n this.#graph = graph;\n }\n\n #buildAdjacency(): void {\n const out = new Map<ModuleId, Edge[]>();\n const inn = new Map<ModuleId, Edge[]>();\n for (const e of this.#graph.edges()) {\n const o = out.get(e.from);\n if (o) o.push(e);\n else out.set(e.from, [e]);\n const i = inn.get(e.to);\n if (i) i.push(e);\n else inn.set(e.to, [e]);\n }\n this.#out = out;\n this.#in = inn;\n }\n\n outOf(id: ModuleId): readonly Edge[] {\n if (this.#out === undefined) this.#buildAdjacency();\n return this.#out?.get(id) ?? NO_EDGES;\n }\n\n into(id: ModuleId): readonly Edge[] {\n if (this.#in === undefined) this.#buildAdjacency();\n return this.#in?.get(id) ?? NO_EDGES;\n }\n\n byTag(key: string, value: string): readonly ModuleId[] {\n if (this.#byTag === undefined) {\n const index = new Map<string, ModuleId[]>();\n for (const m of this.#graph.modules()) {\n for (const [k, v] of m.tags) {\n const bucket = `${k}\\0${v}`;\n const ids = index.get(bucket);\n if (ids) ids.push(m.id);\n else index.set(bucket, [m.id]);\n }\n }\n this.#byTag = index;\n }\n return this.#byTag.get(`${key}\\0${value}`) ?? [];\n }\n\n byKind(kind: ModuleKind): readonly ModuleNode[] {\n if (this.#byKind === undefined) {\n const index = new Map<ModuleKind, ModuleNode[]>();\n for (const m of this.#graph.modules()) {\n const bucket = index.get(m.kind);\n if (bucket) bucket.push(m);\n else index.set(m.kind, [m]);\n }\n this.#byKind = index;\n }\n return this.#byKind.get(kind) ?? [];\n }\n\n byPackage(name: string): readonly ModuleNode[] {\n if (this.#byPackage === undefined) {\n const index = new Map<string, ModuleNode[]>();\n for (const m of this.#graph.modules()) {\n if (m.packageName === undefined) continue;\n const bucket = index.get(m.packageName);\n if (bucket) bucket.push(m);\n else index.set(m.packageName, [m]);\n }\n this.#byPackage = index;\n }\n return this.#byPackage.get(name) ?? [];\n }\n}\n\n/**\n * A set of modules, with the operations a rule actually performs on one.\n *\n * An interface rather than a class: a selection carries the query it came from, and one\n * built by hand against a different graph would answer edge questions about the wrong one.\n * Only {@link GraphQuery} can produce one.\n */\nexport interface ModuleSelection extends Iterable<ModuleNode> {\n readonly size: number;\n isEmpty(): boolean;\n toArray(): readonly ModuleNode[];\n ids(): ModuleId[];\n forEach(fn: (m: ModuleNode) => void): void;\n /** Chainable: narrows this selection without going back to the graph. */\n filter(predicate: (m: ModuleNode) => boolean): ModuleSelection;\n edgesOut(filter?: EdgeFilter): readonly Edge[];\n /** The mirror of {@link edgesOut}: edges arriving at any module in this selection. */\n edgesIn(filter?: EdgeFilter): readonly Edge[];\n}\n\n/**\n * The read surface a rule gets — and the type it should name.\n *\n * An INTERFACE rather than the class, because the two are different promises. What ArchWall\n * owes a rule author is a set of questions that can be asked about a graph; what it must stay\n * free to change is how those questions are answered. Naming the class in `RuleContext` fused\n * the two: the concrete implementation became observable via `instanceof`, a test double became\n * impossible to supply, and an interned or columnar store became a breaking change rather than\n * an optimisation.\n *\n * {@link GraphQuery} is the only implementation core ships, and it lives in\n * `@archwall/core/internal`. Rules never construct one — they are handed one — so nothing is\n * taken away by that; a rule author who needs one for a TEST gets it from\n * `@archwall/test-utils`, which is the supported way to build a graph by hand.\n *\n * See {@link GraphQuery} for what scope does to each of these operations.\n */\nexport interface GraphView {\n module(id: ModuleId): ModuleNode | undefined;\n moduleCount(): number;\n moduleIds(): Iterable<ModuleId>;\n has(id: ModuleId): boolean;\n tagOf(id: ModuleId, key: string): string | undefined;\n modules(filter?: ModuleFilter): ModuleSelection;\n edges(filter?: EdgeFilter): readonly Edge[];\n edgesOutOf(id: ModuleId): readonly Edge[];\n edgesInto(id: ModuleId): readonly Edge[];\n reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;\n reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;\n pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null;\n filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[];\n matchesEdge(e: Edge, filter: EdgeFilter): boolean;\n matchesModule(m: ModuleNode, filter: ModuleFilter): boolean;\n}\n\n/**\n * The only sanctioned way to read a graph; the sole implementation of {@link GraphView}.\n *\n * A scoped query is a VIEW: it shares the underlying {@link GraphIndex} with the query it\n * came from and differs only in which modules it is *about*.\n *\n * ## What scope does, exactly\n *\n * One rule: **an operation is scoped if and only if it ENUMERATES. An operation that answers a\n * question about a module you named is never scoped.**\n *\n * | Operation | Scoped |\n * |---|---|\n * | `modules`, `moduleIds`, `moduleCount`, `edges` | yes — they enumerate |\n * | `module`, `has`, `tagOf` | no — you named the module |\n * | `edgesOutOf`, `edgesInto` | no — you named the module |\n * | `reachableFrom`, `reaching`, `pathBetween` | no — traversal from a named module |\n * | `ModuleSelection.edgesOut` / `edgesIn` | anchored: endpoints in-selection, edges unfiltered |\n * | `RuleContext.compute` | yes — a computation enumerates |\n *\n * The asymmetry is deliberate rather than incidental. A scoped rule must be able to ask what an\n * out-of-scope import target *is*, because an edge leaving the scope is the most interesting\n * thing it can find; hiding the target would turn `layer-dependencies` under a scope from a\n * finding into silence.\n */\nexport class GraphQuery implements GraphView {\n readonly #graph: ProjectGraph;\n readonly #index: GraphIndex;\n /** When present, the ANCHOR set: which modules this view is about. See the class doc. */\n readonly #scope: ReadonlySet<ModuleId> | undefined;\n /** Scoped `edges()` is one filter over the whole edge list; do it once, not per call. */\n #scopedEdges: readonly Edge[] | undefined;\n\n constructor(graph: ProjectGraph, index?: GraphIndex, scope?: ReadonlySet<ModuleId>) {\n this.#graph = graph;\n this.#index = index ?? new GraphIndex(graph);\n this.#scope = scope;\n }\n\n /** A view of the same graph restricted to `scope`, sharing this query's index. */\n scoped(scope: ReadonlySet<ModuleId>): GraphQuery {\n return new GraphQuery(this.#graph, this.#index, scope);\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#graph.module(id);\n }\n\n /** Modules in scope, or all of them when unscoped. */\n moduleCount(): number {\n return this.#scope ? this.#scope.size : this.#graph.moduleCount;\n }\n\n /** Every in-scope module id, in graph order. The traversal primitive. */\n moduleIds(): Iterable<ModuleId> {\n if (!this.#scope) return this.#graph.moduleIds();\n const scope = this.#scope;\n return (function* (ids) {\n for (const id of ids) if (scope.has(id)) yield id;\n })(this.#graph.moduleIds());\n }\n\n /** Whether the graph contains this module at all — distinct from \"is it a source file\". */\n has(id: ModuleId): boolean {\n return this.#graph.hasModule(id);\n }\n\n tagOf(id: ModuleId, key: string): string | undefined {\n return this.#graph.module(id)?.tags.get(key);\n }\n\n modules(filter?: ModuleFilter): ModuleSelection {\n // Pick the narrowest index the filter allows rather than scanning every module.\n let candidates: Iterable<ModuleNode>;\n const tagEntries = filter?.tag ? Object.entries(filter.tag) : [];\n if (tagEntries.length > 0) {\n const sets = tagEntries\n .map(([k, v]) => this.#index.byTag(k, v))\n .sort((a, b) => a.length - b.length);\n const rest = sets.slice(1).map((ids) => new Set(ids));\n const narrowed: ModuleNode[] = [];\n for (const id of sets[0] ?? []) {\n if (!rest.every((s) => s.has(id))) continue;\n const m = this.#graph.module(id);\n if (m !== undefined) narrowed.push(m);\n }\n candidates = narrowed;\n } else if (filter?.packageName !== undefined) {\n candidates = this.#index.byPackage(filter.packageName);\n } else if (filter?.moduleKind !== undefined) {\n const kinds = typeof filter.moduleKind === \"string\" ? [filter.moduleKind] : filter.moduleKind;\n candidates =\n kinds.length === 1\n ? this.#index.byKind(kinds[0]!)\n : kinds.flatMap((k) => this.#index.byKind(k) as ModuleNode[]);\n } else {\n candidates = this.#graph.modules();\n }\n\n const nodes: ModuleNode[] = [];\n for (const m of candidates) {\n if (this.#scope !== undefined && !this.#scope.has(m.id)) continue;\n if (filter?.moduleKind !== undefined && !matchesKind(m, filter.moduleKind)) continue;\n if (filter?.packageName !== undefined && m.packageName !== filter.packageName) continue;\n nodes.push(m);\n }\n return new Selection(this, nodes);\n }\n\n /**\n * In-scope edges matching `filter`.\n *\n * Returns the graph's own array when nothing narrows it — it is already immutable, so a\n * defensive copy per call would protect nobody and cost a full edge list per rule.\n */\n edges(filter?: EdgeFilter): readonly Edge[] {\n let all: readonly Edge[];\n if (this.#scope === undefined) {\n all = this.#graph.edges();\n } else {\n const scope = this.#scope;\n this.#scopedEdges ??= this.#graph.edges().filter((e) => scope.has(e.from));\n all = this.#scopedEdges;\n }\n return this.filterEdges(all, filter);\n }\n\n edgesOutOf(id: ModuleId): readonly Edge[] {\n return this.#index.outOf(id);\n }\n\n edgesInto(id: ModuleId): readonly Edge[] {\n return this.#index.into(id);\n }\n\n /**\n * Every module reachable from `id` by following edges, excluding `id` itself unless a\n * cycle leads back to it. Iterative: a 10k-module chain would overflow the stack.\n */\n reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId> {\n const seen = new Set<ModuleId>();\n const stack: ModuleId[] = [id];\n while (stack.length > 0) {\n const current = stack.pop()!;\n for (const e of this.filterEdges(this.edgesOutOf(current), filter)) {\n if (seen.has(e.to)) continue;\n seen.add(e.to);\n stack.push(e.to);\n }\n }\n return seen;\n }\n\n /** The mirror of {@link reachableFrom}: everything that can reach `id`. */\n reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId> {\n const seen = new Set<ModuleId>();\n const stack: ModuleId[] = [id];\n while (stack.length > 0) {\n const current = stack.pop()!;\n for (const e of this.filterEdges(this.edgesInto(current), filter)) {\n if (seen.has(e.from)) continue;\n seen.add(e.from);\n stack.push(e.from);\n }\n }\n return seen;\n }\n\n /**\n * Shortest dependency path from `from` to `to`, inclusive of both, or null. BFS, because\n * the useful evidence for \"domain reaches infrastructure\" is the shortest chain, not\n * whichever one a traversal happened to find first.\n */\n pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null {\n if (from === to) return [from];\n const previous = new Map<ModuleId, ModuleId>();\n const queue: ModuleId[] = [from];\n const seen = new Set<ModuleId>([from]);\n for (let i = 0; i < queue.length; i++) {\n const current = queue[i]!;\n for (const e of this.filterEdges(this.edgesOutOf(current), filter)) {\n if (seen.has(e.to)) continue;\n seen.add(e.to);\n previous.set(e.to, current);\n if (e.to === to) {\n const path: ModuleId[] = [to];\n for (let at = to; previous.has(at); ) {\n at = previous.get(at)!;\n path.push(at);\n }\n return path.reverse();\n }\n queue.push(e.to);\n }\n }\n return null;\n }\n\n /** Applies an {@link EdgeFilter} to an edge list. Returns the input when there is none. */\n filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[] {\n if (!filter) return edges;\n return edges.filter((e) => this.matchesEdge(e, filter));\n }\n\n /** Whether one edge satisfies a filter. The unit the engine's visitor dispatch uses. */\n matchesEdge(e: Edge, filter: EdgeFilter): boolean {\n if (filter.kind !== undefined && e.kind !== filter.kind) return false;\n const from = this.#graph.module(e.from);\n const to = this.#graph.module(e.to);\n if (filter.toModuleKind !== undefined && (!to || !matchesKind(to, filter.toModuleKind)))\n return false;\n if (filter.fromTag && (!from || !matchesTags(from, filter.fromTag))) return false;\n if (filter.toTag && (!to || !matchesTags(to, filter.toTag))) return false;\n if (filter.crossing !== undefined) {\n const a = from?.tags.get(filter.crossing);\n const b = to?.tags.get(filter.crossing);\n if (a === undefined || b === undefined || a === b) return false;\n }\n if (filter.attributes !== undefined) {\n for (const [key, want] of Object.entries(filter.attributes)) {\n const has = e.attributes?.[key];\n if (want === false) {\n if (has !== undefined) return false;\n } else if (want === true) {\n if (has === undefined) return false;\n } else if (has !== want) return false;\n }\n }\n return true;\n }\n\n /** Whether one module satisfies a filter. Paired with {@link matchesEdge}. */\n matchesModule(m: ModuleNode, filter: ModuleFilter): boolean {\n if (filter.tag && !matchesTags(m, filter.tag)) return false;\n if (filter.moduleKind !== undefined && !matchesKind(m, filter.moduleKind)) return false;\n if (filter.packageName !== undefined && m.packageName !== filter.packageName) return false;\n return true;\n }\n}\n\nclass Selection implements ModuleSelection {\n constructor(\n private readonly query: GraphQuery,\n private readonly nodes: readonly ModuleNode[],\n ) {}\n\n [Symbol.iterator](): Iterator<ModuleNode> {\n return this.nodes[Symbol.iterator]();\n }\n\n get size(): number {\n return this.nodes.length;\n }\n\n isEmpty(): boolean {\n return this.nodes.length === 0;\n }\n\n toArray(): readonly ModuleNode[] {\n return this.nodes;\n }\n\n ids(): ModuleId[] {\n return this.nodes.map((m) => m.id);\n }\n\n forEach(fn: (m: ModuleNode) => void): void {\n this.nodes.forEach(fn);\n }\n\n filter(predicate: (m: ModuleNode) => boolean): ModuleSelection {\n return new Selection(this.query, this.nodes.filter(predicate));\n }\n\n edgesOut(filter?: EdgeFilter): readonly Edge[] {\n const all = this.nodes.flatMap((m) => this.query.edgesOutOf(m.id) as Edge[]);\n return this.query.filterEdges(all, filter);\n }\n\n edgesIn(filter?: EdgeFilter): readonly Edge[] {\n const all = this.nodes.flatMap((m) => this.query.edgesInto(m.id) as Edge[]);\n return this.query.filterEdges(all, filter);\n }\n}\n","import picomatch from \"picomatch\";\nimport type { Classifier, ClassifierContext } from \"../contracts/classifier.js\";\nimport type { Diagnostic } from \"../contracts/diagnostic.js\";\nimport type { GraphTransform } from \"../contracts/transform.js\";\nimport type { Capability, ModuleId, ModuleNode, ProjectGraph } from \"../graph/ir.js\";\nimport { GraphDraft } from \"../graph/ir.js\";\nimport { sourceRelative } from \"../paths.js\";\n\n/** What the project boundary needs: where sources start and which of them count. */\nexport interface BoundaryConfig {\n sourceRoot: string;\n include: readonly string[];\n exclude: readonly string[];\n}\n\n/** Adds what transforms need, which is the repository root they report paths against. */\nexport interface PrepareConfig extends BoundaryConfig {\n repoRoot: string;\n}\n\nexport interface PrepareResult {\n graph: ProjectGraph;\n diagnostics: Diagnostic[];\n /** Capabilities contributed by transforms that actually ran. */\n provided: Capability[];\n}\n\n/**\n * The one pipeline: project boundary → transforms → boundary again → classification.\n *\n * The boundary belongs to the ENGINE, not to producers: producers are the component that\n * varies, so anything that must be identical across hosts cannot live in them. Producers\n * over-collect; the engine trims.\n *\n * It runs twice because a transform may ADD modules, and those must be bounded exactly as\n * if a producer had supplied them. Running it again is safe because it is idempotent — it\n * only ever re-kinds `source` → `excluded`. With no transforms configured, boundary and\n * classification are one fused pass over the modules.\n *\n * Excluded modules are re-kinded, never deleted. An edge *into* an excluded file still says\n * something true about the architecture, and deleting the node would silently rewrite the\n * graph's shape (a cycle through a test helper would vanish).\n */\nexport function prepareGraph(\n graph: ProjectGraph,\n config: PrepareConfig,\n transforms: readonly GraphTransform[],\n classifiers: readonly Classifier[],\n): PrepareResult {\n const diagnostics: Diagnostic[] = [];\n const provided: Capability[] = [];\n\n let current = graph;\n if (transforms.length > 0) {\n // Transforms must see which modules are actually in the project, so the boundary runs\n // before them as well as after.\n current = boundaryAndClassify(current, config, []);\n const ctx = {\n sourceRoot: config.sourceRoot,\n repoRoot: config.repoRoot,\n relative: (file: string) => sourceRelative(config.sourceRoot, file),\n };\n for (const t of transforms) {\n const draft = new GraphDraft(current);\n try {\n t.transform(draft, ctx);\n current = draft.commit();\n for (const c of t.provides ?? []) provided.push(c);\n } catch (err) {\n // The same isolation a rule gets, for the same reason: one broken enricher must not\n // destroy the run. The draft is discarded, so a transform that threw halfway leaves\n // no partial writes behind, and its capabilities are NOT added — rules depending on\n // them skip loudly rather than running against a graph that never got enriched.\n diagnostics.push({\n code: \"transform-failed\",\n severity: \"error\",\n message: `Graph transform \"${t.name}\" threw and was skipped: ${err instanceof Error ? err.message : String(err)}`,\n ...(err instanceof Error && err.stack !== undefined\n ? { details: { stack: err.stack } }\n : {}),\n });\n }\n }\n }\n\n return { graph: boundaryAndClassify(current, config, classifiers), diagnostics, provided };\n}\n\n/**\n * One pass over the modules applying both the boundary and classification.\n *\n * They do not interact — the boundary decides `kind` from the file path alone, and\n * classification decides `tags` from the node — so one pass serves both, halving the\n * per-run allocation. At 50k modules a second pass would mean another 50k node copies and\n * 50k tag Maps on every watch rebuild.\n *\n * Two further allocations are avoided: a module whose kind is unchanged AND that no\n * classifier tagged is passed through by reference, and the tag map is cloned only when a\n * classifier actually contributes something.\n */\nfunction boundaryAndClassify(\n graph: ProjectGraph,\n config: BoundaryConfig,\n classifiers: readonly Classifier[],\n): ProjectGraph {\n const isIncluded = picomatch(config.include as string[], { dot: true });\n const isExcluded = picomatch(config.exclude as string[], { dot: true });\n const ctx: ClassifierContext = {\n sourceRoot: config.sourceRoot,\n relative: (file: string) => sourceRelative(config.sourceRoot, file),\n };\n const modules = new Map<ModuleId, ModuleNode>();\n\n for (const m of graph.modules()) {\n // --- boundary ---------------------------------------------------------------\n // Only first-party source is subject to it. `package`/`builtin`/`virtual` are outside\n // the project by definition and already handled by `kind`; re-testing them against\n // `include` would silently reclassify every dependency as excluded.\n let node = m;\n if (m.kind === \"source\" && m.file !== null) {\n const rel = sourceRelative(config.sourceRoot, m.file);\n if (rel === null || !isIncluded(rel) || isExcluded(rel)) {\n node = { ...m, kind: \"excluded\" };\n }\n }\n\n // --- classification ---------------------------------------------------------\n // Later classifiers override earlier ones on the same tag key. Every module is offered\n // to every classifier — a classifier may legitimately tag packages.\n let tags: Map<string, string> | undefined;\n for (const classifier of classifiers) {\n const patch = classifier.classify(node, ctx);\n if (!patch) continue;\n tags ??= new Map(node.tags);\n for (const [k, v] of Object.entries(patch)) tags.set(k, v);\n }\n\n modules.set(m.id, tags === undefined ? node : { ...node, tags });\n }\n\n return graph.replaceStores(modules);\n}\n\n/**\n * The project boundary on its own, for callers that want kinds settled without tagging.\n * One implementation, shared with {@link prepareGraph}.\n */\nexport function applyProjectBoundary(graph: ProjectGraph, config: BoundaryConfig): ProjectGraph {\n return boundaryAndClassify(graph, config, []);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"prepare-DNER1gV3.cjs","names":["#modules","#edges","#base","#mutableModules","#mutableEdges","path","#memo","#graph","#out","#in","#buildAdjacency","#byTag","#byKind","#byPackage","#index","#scope","#scopedEdges"],"sources":["../src/errors.ts","../src/graph/ir.ts","../src/paths.ts","../src/analysis/cache.ts","../src/graph/query.ts","../src/engine/prepare.ts"],"sourcesContent":["export class ArchWallError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class IrVersionMismatchError extends ArchWallError {\n constructor(\n readonly graphVersion: string,\n readonly coreVersion: string,\n ) {\n super(\n `Incompatible graph IR: adapter produced irVersion ${graphVersion}, but @archwall/core supports ${coreVersion} (majors must match). Upgrade the adapter or core so their IR majors align.`,\n );\n }\n}\n","import { ArchWallError, IrVersionMismatchError } from \"../errors.js\";\n\n/** Semver of the Project Graph IR schema itself, independent of package versions. */\nexport const IR_VERSION = \"1.0.0\";\n\n/**\n * A module's identity, in the IR's own vocabulary rather than the host's.\n *\n * ```\n * file:<repo-relative-posix-path> source | workspace | excluded\n * pkg:<name> package — the package, not one of its files\n * builtin:<specifier> builtin — always prefixed (builtin:node:fs)\n * virtual:<host>:<opaque> virtual — host-synthesized, host-specific by nature\n * unresolved:<raw-specifier> unresolved\n * ```\n *\n * Producers report host facts; `GraphBuilder` decides identity — the same division\n * {@link ModuleKind} already uses. That is what makes a violation's fingerprint the same under\n * every bundler, which is what makes a baseline file possible at all.\n */\nexport type ModuleId = string;\n\n/** The schemes {@link ModuleId} recognises. */\nexport const MODULE_ID_SCHEMES = [\"file\", \"pkg\", \"builtin\", \"virtual\", \"unresolved\"] as const;\n\nexport type ModuleIdScheme = (typeof MODULE_ID_SCHEMES)[number];\n\nconst SCHEME_OF = /^(file|pkg|builtin|virtual|unresolved):/;\n\n/**\n * Splits a canonical id into its scheme and body, or null when it carries no known scheme.\n *\n * Null is a legitimate answer, not an error: in-memory graphs (`@archwall/test-utils`, a\n * playground) use bare ids, and every consumer here degrades to treating the id as opaque.\n */\nexport function parseModuleId(id: ModuleId): { scheme: ModuleIdScheme; body: string } | null {\n const m = SCHEME_OF.exec(id);\n if (!m) return null;\n const scheme = m[1] as ModuleIdScheme;\n return { scheme, body: id.slice(scheme.length + 1) };\n}\n\n/**\n * The id as a human should read it: the path, the package name, the builtin specifier.\n *\n * Used by every reporter and offered to rules as `RuleContext.display`, so that a message names\n * `src/domain/rules.ts` and `react` rather than a scheme-prefixed id — or, as before canonical\n * ids existed, an absolute path from whichever machine produced the graph.\n *\n * `virtual:` keeps its prefix: it is not a path, and the prefix is the only thing that says so.\n */\nexport function displayModuleId(id: ModuleId): string {\n const parsed = parseModuleId(id);\n if (parsed === null || parsed.scheme === \"virtual\") return id;\n return parsed.body;\n}\n\nexport type WellKnownCapability =\n /** `Edge.loc` is populated. */\n | \"import-locations\"\n /** Dynamic `import()` edges are present and marked `kind: \"dynamic\"`. */\n | \"dynamic-imports\"\n /** Every module in the project is present; absence of a module IS evidence. */\n | \"complete-graph\"\n /** Re-export edges are distinguished from plain imports. */\n | \"reexport-edges\"\n /**\n * `Edge.rawSpecifier` is what the author wrote, not a copy of the resolved id. A rule\n * that matches on specifiers must require this, or it silently matches nothing on hosts\n * that cannot supply them and reports a clean run rather than an unavailable one.\n */\n | \"raw-specifiers\"\n /**\n * Type-only edges are PRESENT and carry `attributes.typeOnly`. See {@link EdgeAttributes}.\n *\n * A rule that treats type-only imports differently must require this. Without it, a host\n * that erases type imports (every bundler does) is indistinguishable from one where the\n * code genuinely has no type imports — and \"no `attributes.typeOnly` anywhere\" would be\n * read as \"nothing is type-only\" rather than \"nobody asked\".\n */\n | \"type-only-edges\";\n\n/**\n * Open union: adapters may declare capabilities core does not know about, and rules may\n * require them, without an IR major. `WellKnownCapability` keeps autocomplete useful for\n * the ones core ships.\n */\nexport type Capability = WellKnownCapability | (string & {});\n\nexport interface HostInfo {\n name: string;\n version: string;\n capabilities: ReadonlySet<Capability>;\n}\n\nexport interface SourceLocation {\n file: string;\n /** 1-based */\n line: number;\n /** 0-based */\n column: number;\n}\n\nexport type WellKnownEdgeKind = \"static\" | \"dynamic\" | \"reexport\";\n\n/**\n * Open union so future graph facts (CSS imports, worker edges) arrive additively. Consumers\n * must treat an unrecognised kind as \"some dependency exists\" — never assume exhaustiveness.\n *\n * `kind` answers ONE question — roughly \"what syntax produced this edge\" — and deliberately\n * keeps answering only that. Everything else an edge might be belongs in\n * {@link EdgeAttributes}; see the note there for why.\n */\nexport type EdgeKind = WellKnownEdgeKind | (string & {});\n\n/**\n * Orthogonal facts about an edge, as an OPEN bag.\n *\n * `kind` is one enum, but the domain is not one-dimensional. `export type * from \"./x\"` is\n * *both* a re-export and type-only; a dynamic import of a barrel is both dynamic and a\n * re-export. Modelling those as one enum forces every producer to pick a winner, and forces\n * every consumer to guess which axis the winner came from.\n *\n * So the axes split: `kind` keeps the syntactic one, and everything else lands here, where it\n * composes. A bag rather than named fields because the next axis is always unknown — import\n * attributes (`with { type: \"json\" }`), worker edges, CSS edges, `require` vs `import`\n * interop — and each one arriving as a new top-level `Edge` field would be a new IR major.\n *\n * **Absent means \"the host did not say\", never \"false\".** That distinction is the whole\n * reason {@link WellKnownCapability} has `type-only-edges`: a bundler that erased type imports\n * before ArchWall saw them reports nothing here, and a rule must be able to tell that apart\n * from a codebase with no type imports in it.\n */\nexport interface EdgeAttributes {\n /**\n * The import is erased at compile time (`import type`, `export type`, or an\n * `import { type X }` specifier where every named binding is type-only).\n *\n * Requires the `type-only-edges` capability to be meaningful. `true` or absent — never\n * `false`, so that a producer cannot accidentally assert the negative it does not know.\n */\n typeOnly?: true;\n /** Third parties and future core versions extend here without an IR major. */\n [key: string]: string | true | undefined;\n}\n\n/**\n * What a module *is*, relative to the project being analysed.\n *\n * - `source` — a first-party file inside the analysed project\n * - `workspace` — a file owned by a *different* package in the same monorepo\n * - `package` — a third-party dependency (node_modules)\n * - `builtin` — a runtime builtin (`node:fs`, `bun:sqlite`, …)\n * - `virtual` — generated by the toolchain; no file on disk\n * - `unresolved` — the specifier could not be resolved to anything\n * - `excluded` — a real project file the config's `exclude` removed from analysis\n *\n * The seven-way split is load-bearing: a purity rule that cannot tell `node:crypto` from\n * `lodash` from `@myorg/shared-kernel` gives the wrong answer for two of the three.\n */\nexport type ModuleKind =\n | \"source\"\n | \"workspace\"\n | \"package\"\n | \"builtin\"\n | \"virtual\"\n | \"unresolved\"\n | \"excluded\";\n\nexport interface ModuleNode {\n /** Canonical; see {@link ModuleId}. */\n id: ModuleId;\n /**\n * Absolute path, for the kinds that denote a file: `source`, `workspace`, `excluded`.\n *\n * Null for everything else — including `package`, because a dependency is one node\n * (`pkg:react`) rather than one node per file, so there is no single file to name.\n */\n file: string | null;\n kind: ModuleKind;\n /** npm package name, for `kind: \"package\"`. */\n packageName?: string;\n /** Owning workspace package name, for `kind: \"workspace\"`. */\n workspace?: string;\n /** Filled by classification, e.g. layer → \"features\". */\n tags: ReadonlyMap<string, string>;\n}\n\n/** Code the project owns and can change — including sibling packages in the monorepo. */\nexport const FIRST_PARTY_KINDS = [\"source\", \"workspace\"] as const satisfies readonly ModuleKind[];\n\n/** A dependency the project does not own: third-party code or a runtime builtin. */\nexport const THIRD_PARTY_KINDS = [\"package\", \"builtin\"] as const satisfies readonly ModuleKind[];\n\nexport function isFirstParty(kind: ModuleKind): boolean {\n return kind === \"source\" || kind === \"workspace\";\n}\n\nexport function isThirdParty(kind: ModuleKind): boolean {\n return kind === \"package\" || kind === \"builtin\";\n}\n\nexport interface Edge {\n from: ModuleId;\n to: ModuleId;\n /** What the source wrote: \"@/features/auth\". */\n rawSpecifier: string;\n /** What it actually is after resolution. */\n resolvedPath: string;\n kind: EdgeKind;\n /** Present only if host capability allows. */\n loc?: SourceLocation;\n /** Orthogonal facts; see {@link EdgeAttributes}. Absent when the host reported none. */\n attributes?: EdgeAttributes;\n}\n\nexport type GraphDelivery = \"complete\" | \"progressive\";\n\n/**\n * Process-local counter behind {@link ProjectGraphInit.revision}.\n *\n * A fresh number per constructed graph is the conservative choice: a consumer keying a cache\n * on `revision` can only ever be told \"this is a different graph\", never falsely told it is\n * the same one. Content-addressed revisions would enable cache HITS across rebuilds, which is\n * the incremental-validation problem and deliberately not solved here.\n */\nlet revisionCounter = 0;\n\nexport interface ProjectGraphInit {\n host: HostInfo;\n /** Default \"complete\". */\n delivery?: GraphDelivery;\n modules: Iterable<readonly [ModuleId, ModuleNode]> | ReadonlyMap<ModuleId, ModuleNode>;\n edges: readonly Edge[];\n /** Default {@link IR_VERSION}. Adapters should leave this alone. */\n irVersion?: string;\n /**\n * Opaque identity for this graph. Defaults to a fresh process-local number.\n *\n * The contract is one-directional and deliberately weak: **equal revisions mean the same\n * graph; unequal revisions mean nothing.** Set it explicitly only if you can guarantee the\n * first half — a producer that content-hashes its inputs, for instance.\n */\n revision?: number;\n}\n\n/**\n * The module graph, as an OPAQUE handle.\n *\n * The backing stores are private and no accessor hands them out. Everything a consumer\n * legitimately needs is a method here or on `GraphQuery`; if something is missing, the fix\n * is to add a method, never to expose the store.\n *\n * That is what keeps the *representation* out of the IR contract: a `ReadonlyMap` plus an\n * `Edge[]` is the current implementation, not the promise.\n */\nexport class ProjectGraph {\n readonly irVersion: string;\n readonly host: HostInfo;\n readonly delivery: GraphDelivery;\n /**\n * See {@link ProjectGraphInit.revision}. Preserved across {@link replaceStores}, because a\n * derived graph is a deterministic function of this one and the config that derived it —\n * so a cache keyed on `(revision, configKey)` stays sound through the prepare pipeline.\n */\n readonly revision: number;\n readonly #modules: ReadonlyMap<ModuleId, ModuleNode>;\n readonly #edges: readonly Edge[];\n\n private constructor(\n irVersion: string,\n host: HostInfo,\n delivery: GraphDelivery,\n revision: number,\n modules: ReadonlyMap<ModuleId, ModuleNode>,\n edges: readonly Edge[],\n ) {\n this.irVersion = irVersion;\n this.host = host;\n this.delivery = delivery;\n this.revision = revision;\n this.#modules = modules;\n this.#edges = edges;\n }\n\n static create(init: ProjectGraphInit): ProjectGraph {\n const modules =\n init.modules instanceof Map\n ? (init.modules as ReadonlyMap<ModuleId, ModuleNode>)\n : new Map(init.modules as Iterable<readonly [ModuleId, ModuleNode]>);\n return new ProjectGraph(\n init.irVersion ?? IR_VERSION,\n init.host,\n init.delivery ?? \"complete\",\n init.revision ?? ++revisionCounter,\n modules,\n init.edges,\n );\n }\n\n get moduleCount(): number {\n return this.#modules.size;\n }\n\n get edgeCount(): number {\n return this.#edges.length;\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#modules.get(id);\n }\n\n hasModule(id: ModuleId): boolean {\n return this.#modules.has(id);\n }\n\n /** Every module, in graph order. */\n modules(): Iterable<ModuleNode> {\n return this.#modules.values();\n }\n\n /** Every module id, in graph order. */\n moduleIds(): Iterable<ModuleId> {\n return this.#modules.keys();\n }\n\n /** Every edge, in graph order. Never copy this — it is already immutable. */\n edges(): readonly Edge[] {\n return this.#edges;\n }\n\n /**\n * A new graph with replaced stores, same identity fields.\n *\n * @internal Engine and {@link GraphDraft} only. Not part of the IR contract.\n */\n replaceStores(modules: ReadonlyMap<ModuleId, ModuleNode>, edges?: readonly Edge[]): ProjectGraph {\n return new ProjectGraph(\n this.irVersion,\n this.host,\n this.delivery,\n this.revision,\n modules,\n edges ?? this.#edges,\n );\n }\n}\n\n/**\n * The write surface a {@link GraphTransform} gets.\n *\n * A transform adds, patches, and removes; it never constructs a graph. That is what keeps\n * {@link ProjectGraph} opaque in practice rather than only in principle, and it means a\n * transform cannot drop an IR field it does not know about.\n */\nexport interface GraphMutation {\n /** Read side, mirroring {@link ProjectGraph}. */\n module(id: ModuleId): ModuleNode | undefined;\n hasModule(id: ModuleId): boolean;\n modules(): Iterable<ModuleNode>;\n edges(): readonly Edge[];\n /** Adds a module, or replaces one with the same id. */\n addModule(node: ModuleNode): void;\n /** Merges fields into an existing module; `tags` merge key-by-key. No-op if absent. */\n patchModule(\n id: ModuleId,\n patch: Partial<Omit<ModuleNode, \"id\" | \"tags\">> & { tags?: Record<string, string> },\n ): void;\n addEdge(edge: Edge): void;\n /** Removes every edge the predicate accepts. */\n removeEdges(predicate: (edge: Edge) => boolean): void;\n}\n\n/**\n * Copy-on-write {@link GraphMutation} over a {@link ProjectGraph}.\n *\n * A transform that touches nothing costs nothing: the stores are only cloned on the first\n * write, and `commit()` returns the original graph when there were none.\n *\n * @internal\n */\nexport class GraphDraft implements GraphMutation {\n readonly #base: ProjectGraph;\n #modules: Map<ModuleId, ModuleNode> | undefined;\n #edges: Edge[] | undefined;\n\n constructor(base: ProjectGraph) {\n this.#base = base;\n }\n\n #mutableModules(): Map<ModuleId, ModuleNode> {\n if (this.#modules === undefined) {\n this.#modules = new Map();\n for (const m of this.#base.modules()) this.#modules.set(m.id, m);\n }\n return this.#modules;\n }\n\n #mutableEdges(): Edge[] {\n this.#edges ??= [...this.#base.edges()];\n return this.#edges;\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#modules ? this.#modules.get(id) : this.#base.module(id);\n }\n\n hasModule(id: ModuleId): boolean {\n return this.#modules ? this.#modules.has(id) : this.#base.hasModule(id);\n }\n\n modules(): Iterable<ModuleNode> {\n return this.#modules ? this.#modules.values() : this.#base.modules();\n }\n\n edges(): readonly Edge[] {\n return this.#edges ?? this.#base.edges();\n }\n\n addModule(node: ModuleNode): void {\n this.#mutableModules().set(node.id, node);\n }\n\n patchModule(\n id: ModuleId,\n patch: Partial<Omit<ModuleNode, \"id\" | \"tags\">> & { tags?: Record<string, string> },\n ): void {\n const current = this.module(id);\n if (current === undefined) return;\n const { tags: tagPatch, ...rest } = patch;\n const next: ModuleNode = { ...current, ...rest };\n if (tagPatch !== undefined) {\n const tags = new Map(current.tags);\n for (const [k, v] of Object.entries(tagPatch)) tags.set(k, v);\n next.tags = tags;\n }\n this.#mutableModules().set(id, next);\n }\n\n addEdge(edge: Edge): void {\n this.#mutableEdges().push(edge);\n }\n\n removeEdges(predicate: (edge: Edge) => boolean): void {\n const kept = this.edges().filter((e) => !predicate(e));\n if (kept.length !== this.edges().length) this.#edges = kept;\n }\n\n /** The resulting graph, or the untouched original when nothing was written. */\n commit(): ProjectGraph {\n if (this.#modules === undefined && this.#edges === undefined) return this.#base;\n const modules =\n this.#modules ??\n new Map<ModuleId, ModuleNode>([...this.#base.modules()].map((m) => [m.id, m]));\n return this.#base.replaceStores(modules, this.#edges);\n }\n}\n\nexport function irMajor(version: string): number {\n const m = /^(\\d+)\\./.exec(version);\n if (!m) throw new ArchWallError(`Malformed IR version: \"${version}\"`);\n return Number(m[1]);\n}\n\nexport function assertIrCompatible(graphVersion: string): void {\n if (irMajor(graphVersion) !== irMajor(IR_VERSION)) {\n throw new IrVersionMismatchError(graphVersion, IR_VERSION);\n }\n}\n","import * as path from \"node:path\";\n\n/** Forward slashes everywhere, so one string form crosses platforms. */\nfunction normalize(p: string): string {\n return p.replaceAll(\"\\\\\", \"/\");\n}\n\n/**\n * A file's path relative to `root`, forward-slashed, or `null` when it does not lie\n * strictly inside `root`.\n *\n * The one answer to \"where is this file, in the terms my patterns are written in\".\n * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s\n * `within` all describe positions in a tree, and they must all agree on what a position\n * is — including on the edge cases: the root itself is not *inside* the root, and a file\n * above it has no position at all.\n *\n * A path that is already relative is taken as relative *to the root* rather than resolved\n * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,\n * `@archwall/test-utils`) use bare ids, and resolving those against the working directory\n * would silently place every module outside the project.\n */\nexport function sourceRelative(root: string, file: string): string | null {\n const normalized = normalize(file);\n if (!path.isAbsolute(normalized)) return normalized === \"\" ? null : normalized;\n const rel = normalize(path.relative(root, normalized));\n if (rel === \"\" || rel.startsWith(\"../\") || rel === \"..\" || path.isAbsolute(rel)) return null;\n return rel;\n}\n\n/**\n * Repository-relative, for anything that leaves the process: violation fingerprints,\n * reporter output, SARIF `artifactLocation.uri`.\n *\n * Absolute paths are the right module identity *inside* a run and wrong in every output,\n * because they make results machine-specific. SARIF in particular is silently useless with\n * absolute URIs: GitHub code scanning cannot associate the result with a repository file.\n *\n * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the\n * root is returned as-is rather than as `null`, because output must always print something,\n * whereas matching must be able to say \"not here\".\n */\nexport function toRelative(root: string, id: string): string {\n const normalized = normalize(id);\n if (!path.isAbsolute(normalized)) return normalized;\n const rel = sourceRelative(root, normalized);\n // Outside the root: keep it absolute rather than emitting a ../../.. chain that is just\n // as machine-specific and harder to read.\n return rel ?? normalized;\n}\n\n/**\n * FNV-1a, 64-bit, as 16 lowercase hex chars.\n *\n * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,\n * worker, edge runtime), and this hash is used for identity, never for security.\n */\nexport function stableHash(input: string): string {\n // 64-bit FNV-1a via two 32-bit halves, since JS bitwise ops are 32-bit.\n let h1 = 0x811c9dc5;\n let h2 = 0x1000193;\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i);\n h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0;\n h2 = Math.imul(h2 ^ ((c << 5) | (c >>> 3)), 0x01000193) >>> 0;\n }\n return h1.toString(16).padStart(8, \"0\") + h2.toString(16).padStart(8, \"0\");\n}\n\n/**\n * Joins parts into one hashable string.\n *\n * `\\0` rather than a space: parts are paths and specifiers, which may contain spaces, and\n * a delimiter that can occur inside a part makes two different tuples hash identically.\n */\nexport function hashParts(parts: readonly string[]): string {\n return stableHash(parts.join(\"\\0\"));\n}\n","import type { GraphComputation } from \"../contracts/analysis.js\";\nimport type { GraphView } from \"../graph/query.js\";\n\n/**\n * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost\n * one traversal.\n *\n * The view is part of the key because a computation is an ENUMERATION of the graph, and\n * enumeration is scoped. A cache bound to the root query\n * would hand a rule scoped to `apps/web` the cycles of the whole repository — the rule's\n * `ctx.graph` narrowed and its `ctx.compute` silently not.\n *\n * Rules sharing a scope share the base query object, so they share the entry; the common case\n * (no scope at all) is still one evaluation for everyone.\n */\nexport class GraphComputationCache {\n readonly #memo = new Map<GraphView, Map<GraphComputation<unknown>, unknown>>();\n\n get<T>(computation: GraphComputation<T>, graph: GraphView): T {\n let perView = this.#memo.get(graph);\n if (perView === undefined) {\n perView = new Map();\n this.#memo.set(graph, perView);\n }\n const key = computation as GraphComputation<unknown>;\n if (perView.has(key)) return perView.get(key) as T;\n const value = computation.compute(graph);\n perView.set(key, value);\n return value;\n }\n}\n","import type { Edge, EdgeKind, ModuleId, ModuleKind, ModuleNode, ProjectGraph } from \"./ir.js\";\n\nexport interface ModuleFilter {\n /** ALL entries must match module tags. */\n tag?: Record<string, string>;\n /**\n * Any listed kind matches. `FIRST_PARTY_KINDS` / `THIRD_PARTY_KINDS` cover the two\n * groupings that are actually meaningful.\n */\n moduleKind?: ModuleKind | readonly ModuleKind[];\n packageName?: string;\n}\n\nexport interface EdgeFilter {\n kind?: EdgeKind;\n /** Any listed kind matches, applied to the edge's target. */\n toModuleKind?: ModuleKind | readonly ModuleKind[];\n fromTag?: Record<string, string>;\n toTag?: Record<string, string>;\n /** Tag key; keep edge iff BOTH endpoints have the tag and values differ. */\n crossing?: string;\n /**\n * Selects on {@link EdgeAttributes}. `true` requires the attribute present; `false` requires\n * it ABSENT; a string requires that exact value.\n *\n * `false` and \"absent\" are the same test on purpose — attributes are never stored as `false`\n * (see {@link EdgeAttributes}), so \"not type-only\" and \"nobody said\" are indistinguishable\n * *here* by construction. A rule that must tell them apart declares the corresponding\n * capability and gets skipped loudly instead, which is the only honest answer.\n */\n attributes?: Readonly<Record<string, string | boolean>>;\n}\n\nfunction matchesKind(m: ModuleNode, want: ModuleKind | readonly ModuleKind[]): boolean {\n return typeof want === \"string\" ? m.kind === want : want.includes(m.kind);\n}\n\nfunction matchesTags(m: ModuleNode, want: Record<string, string>): boolean {\n for (const k of Object.keys(want)) if (m.tags.get(k) !== want[k]) return false;\n return true;\n}\n\nconst NO_EDGES: readonly Edge[] = [];\n\n/**\n * Stable key for a filter, so the engine can bucket rules that want the same slice of the\n * graph and evaluate that slice once for all of them.\n */\nexport function filterKey(filter: EdgeFilter | ModuleFilter | undefined): string {\n if (filter === undefined) return \"*\";\n return JSON.stringify(filter, Object.keys(filter).sort());\n}\n\n/**\n * The adjacency and attribute indexes over one graph, built LAZILY per axis.\n *\n * One index serves every query over a graph, scoped or not: a scope narrows *which results\n * are returned*, and does not change what the graph contains, so it must never rebuild the\n * index of it.\n *\n * Each axis is built on first use. A run whose rules only walk edges never pays for the\n * tag, kind, and package indexes.\n */\nexport class GraphIndex {\n readonly #graph: ProjectGraph;\n #out: Map<ModuleId, Edge[]> | undefined;\n #in: Map<ModuleId, Edge[]> | undefined;\n #byTag: Map<string, ModuleId[]> | undefined;\n #byKind: Map<ModuleKind, ModuleNode[]> | undefined;\n #byPackage: Map<string, ModuleNode[]> | undefined;\n\n constructor(graph: ProjectGraph) {\n this.#graph = graph;\n }\n\n #buildAdjacency(): void {\n const out = new Map<ModuleId, Edge[]>();\n const inn = new Map<ModuleId, Edge[]>();\n for (const e of this.#graph.edges()) {\n const o = out.get(e.from);\n if (o) o.push(e);\n else out.set(e.from, [e]);\n const i = inn.get(e.to);\n if (i) i.push(e);\n else inn.set(e.to, [e]);\n }\n this.#out = out;\n this.#in = inn;\n }\n\n outOf(id: ModuleId): readonly Edge[] {\n if (this.#out === undefined) this.#buildAdjacency();\n return this.#out?.get(id) ?? NO_EDGES;\n }\n\n into(id: ModuleId): readonly Edge[] {\n if (this.#in === undefined) this.#buildAdjacency();\n return this.#in?.get(id) ?? NO_EDGES;\n }\n\n byTag(key: string, value: string): readonly ModuleId[] {\n if (this.#byTag === undefined) {\n const index = new Map<string, ModuleId[]>();\n for (const m of this.#graph.modules()) {\n for (const [k, v] of m.tags) {\n const bucket = `${k}\\0${v}`;\n const ids = index.get(bucket);\n if (ids) ids.push(m.id);\n else index.set(bucket, [m.id]);\n }\n }\n this.#byTag = index;\n }\n return this.#byTag.get(`${key}\\0${value}`) ?? [];\n }\n\n byKind(kind: ModuleKind): readonly ModuleNode[] {\n if (this.#byKind === undefined) {\n const index = new Map<ModuleKind, ModuleNode[]>();\n for (const m of this.#graph.modules()) {\n const bucket = index.get(m.kind);\n if (bucket) bucket.push(m);\n else index.set(m.kind, [m]);\n }\n this.#byKind = index;\n }\n return this.#byKind.get(kind) ?? [];\n }\n\n byPackage(name: string): readonly ModuleNode[] {\n if (this.#byPackage === undefined) {\n const index = new Map<string, ModuleNode[]>();\n for (const m of this.#graph.modules()) {\n if (m.packageName === undefined) continue;\n const bucket = index.get(m.packageName);\n if (bucket) bucket.push(m);\n else index.set(m.packageName, [m]);\n }\n this.#byPackage = index;\n }\n return this.#byPackage.get(name) ?? [];\n }\n}\n\n/**\n * A set of modules, with the operations a rule actually performs on one.\n *\n * An interface rather than a class: a selection carries the query it came from, and one\n * built by hand against a different graph would answer edge questions about the wrong one.\n * Only {@link GraphQuery} can produce one.\n */\nexport interface ModuleSelection extends Iterable<ModuleNode> {\n readonly size: number;\n isEmpty(): boolean;\n toArray(): readonly ModuleNode[];\n ids(): ModuleId[];\n forEach(fn: (m: ModuleNode) => void): void;\n /** Chainable: narrows this selection without going back to the graph. */\n filter(predicate: (m: ModuleNode) => boolean): ModuleSelection;\n edgesOut(filter?: EdgeFilter): readonly Edge[];\n /** The mirror of {@link edgesOut}: edges arriving at any module in this selection. */\n edgesIn(filter?: EdgeFilter): readonly Edge[];\n}\n\n/**\n * The read surface a rule gets — and the type it should name.\n *\n * An INTERFACE rather than the class, because the two are different promises. What ArchWall\n * owes a rule author is a set of questions that can be asked about a graph; what it must stay\n * free to change is how those questions are answered. Naming the class in `RuleContext` fused\n * the two: the concrete implementation became observable via `instanceof`, a test double became\n * impossible to supply, and an interned or columnar store became a breaking change rather than\n * an optimisation.\n *\n * {@link GraphQuery} is the only implementation core ships, and it lives in\n * `@archwall/core/internal`. Rules never construct one — they are handed one — so nothing is\n * taken away by that; a rule author who needs one for a TEST gets it from\n * `@archwall/test-utils`, which is the supported way to build a graph by hand.\n *\n * See {@link GraphQuery} for what scope does to each of these operations.\n */\nexport interface GraphView {\n module(id: ModuleId): ModuleNode | undefined;\n moduleCount(): number;\n moduleIds(): Iterable<ModuleId>;\n has(id: ModuleId): boolean;\n tagOf(id: ModuleId, key: string): string | undefined;\n modules(filter?: ModuleFilter): ModuleSelection;\n edges(filter?: EdgeFilter): readonly Edge[];\n edgesOutOf(id: ModuleId): readonly Edge[];\n edgesInto(id: ModuleId): readonly Edge[];\n reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;\n reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;\n pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null;\n filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[];\n matchesEdge(e: Edge, filter: EdgeFilter): boolean;\n matchesModule(m: ModuleNode, filter: ModuleFilter): boolean;\n}\n\n/**\n * The only sanctioned way to read a graph; the sole implementation of {@link GraphView}.\n *\n * A scoped query is a VIEW: it shares the underlying {@link GraphIndex} with the query it\n * came from and differs only in which modules it is *about*.\n *\n * ## What scope does, exactly\n *\n * One rule: **an operation is scoped if and only if it ENUMERATES. An operation that answers a\n * question about a module you named is never scoped.**\n *\n * | Operation | Scoped |\n * |---|---|\n * | `modules`, `moduleIds`, `moduleCount`, `edges` | yes — they enumerate |\n * | `module`, `has`, `tagOf` | no — you named the module |\n * | `edgesOutOf`, `edgesInto` | no — you named the module |\n * | `reachableFrom`, `reaching`, `pathBetween` | no — traversal from a named module |\n * | `ModuleSelection.edgesOut` / `edgesIn` | anchored: endpoints in-selection, edges unfiltered |\n * | `RuleContext.compute` | yes — a computation enumerates |\n *\n * The asymmetry is deliberate rather than incidental. A scoped rule must be able to ask what an\n * out-of-scope import target *is*, because an edge leaving the scope is the most interesting\n * thing it can find; hiding the target would turn `layer-dependencies` under a scope from a\n * finding into silence.\n */\nexport class GraphQuery implements GraphView {\n readonly #graph: ProjectGraph;\n readonly #index: GraphIndex;\n /** When present, the ANCHOR set: which modules this view is about. See the class doc. */\n readonly #scope: ReadonlySet<ModuleId> | undefined;\n /** Scoped `edges()` is one filter over the whole edge list; do it once, not per call. */\n #scopedEdges: readonly Edge[] | undefined;\n\n constructor(graph: ProjectGraph, index?: GraphIndex, scope?: ReadonlySet<ModuleId>) {\n this.#graph = graph;\n this.#index = index ?? new GraphIndex(graph);\n this.#scope = scope;\n }\n\n /** A view of the same graph restricted to `scope`, sharing this query's index. */\n scoped(scope: ReadonlySet<ModuleId>): GraphQuery {\n return new GraphQuery(this.#graph, this.#index, scope);\n }\n\n module(id: ModuleId): ModuleNode | undefined {\n return this.#graph.module(id);\n }\n\n /** Modules in scope, or all of them when unscoped. */\n moduleCount(): number {\n return this.#scope ? this.#scope.size : this.#graph.moduleCount;\n }\n\n /** Every in-scope module id, in graph order. The traversal primitive. */\n moduleIds(): Iterable<ModuleId> {\n if (!this.#scope) return this.#graph.moduleIds();\n const scope = this.#scope;\n return (function* (ids) {\n for (const id of ids) if (scope.has(id)) yield id;\n })(this.#graph.moduleIds());\n }\n\n /** Whether the graph contains this module at all — distinct from \"is it a source file\". */\n has(id: ModuleId): boolean {\n return this.#graph.hasModule(id);\n }\n\n tagOf(id: ModuleId, key: string): string | undefined {\n return this.#graph.module(id)?.tags.get(key);\n }\n\n modules(filter?: ModuleFilter): ModuleSelection {\n // Pick the narrowest index the filter allows rather than scanning every module.\n let candidates: Iterable<ModuleNode>;\n const tagEntries = filter?.tag ? Object.entries(filter.tag) : [];\n if (tagEntries.length > 0) {\n const sets = tagEntries\n .map(([k, v]) => this.#index.byTag(k, v))\n .sort((a, b) => a.length - b.length);\n const rest = sets.slice(1).map((ids) => new Set(ids));\n const narrowed: ModuleNode[] = [];\n for (const id of sets[0] ?? []) {\n if (!rest.every((s) => s.has(id))) continue;\n const m = this.#graph.module(id);\n if (m !== undefined) narrowed.push(m);\n }\n candidates = narrowed;\n } else if (filter?.packageName !== undefined) {\n candidates = this.#index.byPackage(filter.packageName);\n } else if (filter?.moduleKind !== undefined) {\n const kinds = typeof filter.moduleKind === \"string\" ? [filter.moduleKind] : filter.moduleKind;\n candidates =\n kinds.length === 1\n ? this.#index.byKind(kinds[0]!)\n : kinds.flatMap((k) => this.#index.byKind(k) as ModuleNode[]);\n } else {\n candidates = this.#graph.modules();\n }\n\n const nodes: ModuleNode[] = [];\n for (const m of candidates) {\n if (this.#scope !== undefined && !this.#scope.has(m.id)) continue;\n if (filter?.moduleKind !== undefined && !matchesKind(m, filter.moduleKind)) continue;\n if (filter?.packageName !== undefined && m.packageName !== filter.packageName) continue;\n nodes.push(m);\n }\n return new Selection(this, nodes);\n }\n\n /**\n * In-scope edges matching `filter`.\n *\n * Returns the graph's own array when nothing narrows it — it is already immutable, so a\n * defensive copy per call would protect nobody and cost a full edge list per rule.\n */\n edges(filter?: EdgeFilter): readonly Edge[] {\n let all: readonly Edge[];\n if (this.#scope === undefined) {\n all = this.#graph.edges();\n } else {\n const scope = this.#scope;\n this.#scopedEdges ??= this.#graph.edges().filter((e) => scope.has(e.from));\n all = this.#scopedEdges;\n }\n return this.filterEdges(all, filter);\n }\n\n edgesOutOf(id: ModuleId): readonly Edge[] {\n return this.#index.outOf(id);\n }\n\n edgesInto(id: ModuleId): readonly Edge[] {\n return this.#index.into(id);\n }\n\n /**\n * Every module reachable from `id` by following edges, excluding `id` itself unless a\n * cycle leads back to it. Iterative: a 10k-module chain would overflow the stack.\n */\n reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId> {\n const seen = new Set<ModuleId>();\n const stack: ModuleId[] = [id];\n while (stack.length > 0) {\n const current = stack.pop()!;\n for (const e of this.filterEdges(this.edgesOutOf(current), filter)) {\n if (seen.has(e.to)) continue;\n seen.add(e.to);\n stack.push(e.to);\n }\n }\n return seen;\n }\n\n /** The mirror of {@link reachableFrom}: everything that can reach `id`. */\n reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId> {\n const seen = new Set<ModuleId>();\n const stack: ModuleId[] = [id];\n while (stack.length > 0) {\n const current = stack.pop()!;\n for (const e of this.filterEdges(this.edgesInto(current), filter)) {\n if (seen.has(e.from)) continue;\n seen.add(e.from);\n stack.push(e.from);\n }\n }\n return seen;\n }\n\n /**\n * Shortest dependency path from `from` to `to`, inclusive of both, or null. BFS, because\n * the useful evidence for \"domain reaches infrastructure\" is the shortest chain, not\n * whichever one a traversal happened to find first.\n */\n pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null {\n if (from === to) return [from];\n const previous = new Map<ModuleId, ModuleId>();\n const queue: ModuleId[] = [from];\n const seen = new Set<ModuleId>([from]);\n for (let i = 0; i < queue.length; i++) {\n const current = queue[i]!;\n for (const e of this.filterEdges(this.edgesOutOf(current), filter)) {\n if (seen.has(e.to)) continue;\n seen.add(e.to);\n previous.set(e.to, current);\n if (e.to === to) {\n const path: ModuleId[] = [to];\n for (let at = to; previous.has(at); ) {\n at = previous.get(at)!;\n path.push(at);\n }\n return path.reverse();\n }\n queue.push(e.to);\n }\n }\n return null;\n }\n\n /** Applies an {@link EdgeFilter} to an edge list. Returns the input when there is none. */\n filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[] {\n if (!filter) return edges;\n return edges.filter((e) => this.matchesEdge(e, filter));\n }\n\n /** Whether one edge satisfies a filter. The unit the engine's visitor dispatch uses. */\n matchesEdge(e: Edge, filter: EdgeFilter): boolean {\n if (filter.kind !== undefined && e.kind !== filter.kind) return false;\n const from = this.#graph.module(e.from);\n const to = this.#graph.module(e.to);\n if (filter.toModuleKind !== undefined && (!to || !matchesKind(to, filter.toModuleKind)))\n return false;\n if (filter.fromTag && (!from || !matchesTags(from, filter.fromTag))) return false;\n if (filter.toTag && (!to || !matchesTags(to, filter.toTag))) return false;\n if (filter.crossing !== undefined) {\n const a = from?.tags.get(filter.crossing);\n const b = to?.tags.get(filter.crossing);\n if (a === undefined || b === undefined || a === b) return false;\n }\n if (filter.attributes !== undefined) {\n for (const [key, want] of Object.entries(filter.attributes)) {\n const has = e.attributes?.[key];\n if (want === false) {\n if (has !== undefined) return false;\n } else if (want === true) {\n if (has === undefined) return false;\n } else if (has !== want) return false;\n }\n }\n return true;\n }\n\n /** Whether one module satisfies a filter. Paired with {@link matchesEdge}. */\n matchesModule(m: ModuleNode, filter: ModuleFilter): boolean {\n if (filter.tag && !matchesTags(m, filter.tag)) return false;\n if (filter.moduleKind !== undefined && !matchesKind(m, filter.moduleKind)) return false;\n if (filter.packageName !== undefined && m.packageName !== filter.packageName) return false;\n return true;\n }\n}\n\nclass Selection implements ModuleSelection {\n constructor(\n private readonly query: GraphQuery,\n private readonly nodes: readonly ModuleNode[],\n ) {}\n\n [Symbol.iterator](): Iterator<ModuleNode> {\n return this.nodes[Symbol.iterator]();\n }\n\n get size(): number {\n return this.nodes.length;\n }\n\n isEmpty(): boolean {\n return this.nodes.length === 0;\n }\n\n toArray(): readonly ModuleNode[] {\n return this.nodes;\n }\n\n ids(): ModuleId[] {\n return this.nodes.map((m) => m.id);\n }\n\n forEach(fn: (m: ModuleNode) => void): void {\n this.nodes.forEach(fn);\n }\n\n filter(predicate: (m: ModuleNode) => boolean): ModuleSelection {\n return new Selection(this.query, this.nodes.filter(predicate));\n }\n\n edgesOut(filter?: EdgeFilter): readonly Edge[] {\n const all = this.nodes.flatMap((m) => this.query.edgesOutOf(m.id) as Edge[]);\n return this.query.filterEdges(all, filter);\n }\n\n edgesIn(filter?: EdgeFilter): readonly Edge[] {\n const all = this.nodes.flatMap((m) => this.query.edgesInto(m.id) as Edge[]);\n return this.query.filterEdges(all, filter);\n }\n}\n","import picomatch from \"picomatch\";\nimport type { Classifier, ClassifierContext } from \"../contracts/classifier.js\";\nimport type { Diagnostic } from \"../contracts/diagnostic.js\";\nimport type { GraphTransform } from \"../contracts/transform.js\";\nimport type { Capability, ModuleId, ModuleNode, ProjectGraph } from \"../graph/ir.js\";\nimport { GraphDraft } from \"../graph/ir.js\";\nimport { sourceRelative } from \"../paths.js\";\n\n/** What the project boundary needs: where sources start and which of them count. */\nexport interface BoundaryConfig {\n sourceRoot: string;\n include: readonly string[];\n exclude: readonly string[];\n}\n\n/** Adds what transforms need, which is the repository root they report paths against. */\nexport interface PrepareConfig extends BoundaryConfig {\n repoRoot: string;\n}\n\nexport interface PrepareResult {\n graph: ProjectGraph;\n diagnostics: Diagnostic[];\n /** Capabilities contributed by transforms that actually ran. */\n provided: Capability[];\n}\n\n/**\n * The one pipeline: project boundary → transforms → boundary again → classification.\n *\n * The boundary belongs to the ENGINE, not to producers: producers are the component that\n * varies, so anything that must be identical across hosts cannot live in them. Producers\n * over-collect; the engine trims.\n *\n * It runs twice because a transform may ADD modules, and those must be bounded exactly as\n * if a producer had supplied them. Running it again is safe because it is idempotent — it\n * only ever re-kinds `source` → `excluded`. With no transforms configured, boundary and\n * classification are one fused pass over the modules.\n *\n * Excluded modules are re-kinded, never deleted. An edge *into* an excluded file still says\n * something true about the architecture, and deleting the node would silently rewrite the\n * graph's shape (a cycle through a test helper would vanish).\n */\nexport function prepareGraph(\n graph: ProjectGraph,\n config: PrepareConfig,\n transforms: readonly GraphTransform[],\n classifiers: readonly Classifier[],\n): PrepareResult {\n const diagnostics: Diagnostic[] = [];\n const provided: Capability[] = [];\n\n let current = graph;\n if (transforms.length > 0) {\n // Transforms must see which modules are actually in the project, so the boundary runs\n // before them as well as after.\n current = boundaryAndClassify(current, config, []);\n const ctx = {\n sourceRoot: config.sourceRoot,\n repoRoot: config.repoRoot,\n relative: (file: string) => sourceRelative(config.sourceRoot, file),\n };\n for (const t of transforms) {\n const draft = new GraphDraft(current);\n try {\n t.transform(draft, ctx);\n current = draft.commit();\n for (const c of t.provides ?? []) provided.push(c);\n } catch (err) {\n // The same isolation a rule gets, for the same reason: one broken enricher must not\n // destroy the run. The draft is discarded, so a transform that threw halfway leaves\n // no partial writes behind, and its capabilities are NOT added — rules depending on\n // them skip loudly rather than running against a graph that never got enriched.\n diagnostics.push({\n code: \"transform-failed\",\n severity: \"error\",\n message: `Graph transform \"${t.name}\" threw and was skipped: ${err instanceof Error ? err.message : String(err)}`,\n ...(err instanceof Error && err.stack !== undefined\n ? { details: { stack: err.stack } }\n : {}),\n });\n }\n }\n }\n\n return { graph: boundaryAndClassify(current, config, classifiers), diagnostics, provided };\n}\n\n/**\n * One pass over the modules applying both the boundary and classification.\n *\n * They do not interact — the boundary decides `kind` from the file path alone, and\n * classification decides `tags` from the node — so one pass serves both, halving the\n * per-run allocation. At 50k modules a second pass would mean another 50k node copies and\n * 50k tag Maps on every watch rebuild.\n *\n * Two further allocations are avoided: a module whose kind is unchanged AND that no\n * classifier tagged is passed through by reference, and the tag map is cloned only when a\n * classifier actually contributes something.\n */\nfunction boundaryAndClassify(\n graph: ProjectGraph,\n config: BoundaryConfig,\n classifiers: readonly Classifier[],\n): ProjectGraph {\n const isIncluded = picomatch(config.include as string[], { dot: true });\n const isExcluded = picomatch(config.exclude as string[], { dot: true });\n const ctx: ClassifierContext = {\n sourceRoot: config.sourceRoot,\n relative: (file: string) => sourceRelative(config.sourceRoot, file),\n };\n const modules = new Map<ModuleId, ModuleNode>();\n\n for (const m of graph.modules()) {\n // --- boundary ---------------------------------------------------------------\n // Only first-party source is subject to it. `package`/`builtin`/`virtual` are outside\n // the project by definition and already handled by `kind`; re-testing them against\n // `include` would silently reclassify every dependency as excluded.\n let node = m;\n if (m.kind === \"source\" && m.file !== null) {\n const rel = sourceRelative(config.sourceRoot, m.file);\n if (rel === null || !isIncluded(rel) || isExcluded(rel)) {\n node = { ...m, kind: \"excluded\" };\n }\n }\n\n // --- classification ---------------------------------------------------------\n // Later classifiers override earlier ones on the same tag key. Every module is offered\n // to every classifier — a classifier may legitimately tag packages.\n let tags: Map<string, string> | undefined;\n for (const classifier of classifiers) {\n const patch = classifier.classify(node, ctx);\n if (!patch) continue;\n tags ??= new Map(node.tags);\n for (const [k, v] of Object.entries(patch)) tags.set(k, v);\n }\n\n modules.set(m.id, tags === undefined ? node : { ...node, tags });\n }\n\n return graph.replaceStores(modules);\n}\n\n/**\n * The project boundary on its own, for callers that want kinds settled without tagging.\n * One implementation, shared with {@link prepareGraph}.\n */\nexport function applyProjectBoundary(graph: ProjectGraph, config: BoundaryConfig): ProjectGraph {\n return boundaryAndClassify(graph, config, []);\n}\n"],"mappings":"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"}
@@ -282,27 +282,6 @@ declare class GraphDraft implements GraphMutation {
282
282
  declare function irMajor(version: string): number;
283
283
  declare function assertIrCompatible(graphVersion: string): void;
284
284
  //#endregion
285
- //#region src/contracts/classifier.d.ts
286
- interface ClassifierContext {
287
- /**
288
- * Absolute source root from resolved config. Classifier patterns describe the shape of
289
- * the source tree, so they are relative to this and never to the repository root.
290
- */
291
- sourceRoot: string;
292
- /**
293
- * A file's path relative to {@link sourceRoot}, forward-slashed, or null when it lies
294
- * outside. Every path-based classifier needs exactly this, and none of them should be
295
- * re-deriving it — guards, slash normalisation and all — in user code.
296
- */
297
- relative(file: string): string | null;
298
- }
299
- type TagPatch = Record<string, string> | null | undefined | void;
300
- interface Classifier {
301
- name: string;
302
- classify(module: ModuleNode, ctx: ClassifierContext): TagPatch;
303
- }
304
- declare function defineClassifier(classifier: Classifier): Classifier;
305
- //#endregion
306
285
  //#region src/violations.d.ts
307
286
  /**
308
287
  * The ONE severity vocabulary, shared by violations and diagnostics.
@@ -435,6 +414,27 @@ declare function countBySeverity(violations: readonly {
435
414
  */
436
415
  declare function compareViolations(a: Violation, b: Violation): number;
437
416
  //#endregion
417
+ //#region src/contracts/classifier.d.ts
418
+ interface ClassifierContext {
419
+ /**
420
+ * Absolute source root from resolved config. Classifier patterns describe the shape of
421
+ * the source tree, so they are relative to this and never to the repository root.
422
+ */
423
+ sourceRoot: string;
424
+ /**
425
+ * A file's path relative to {@link sourceRoot}, forward-slashed, or null when it lies
426
+ * outside. Every path-based classifier needs exactly this, and none of them should be
427
+ * re-deriving it — guards, slash normalisation and all — in user code.
428
+ */
429
+ relative(file: string): string | null;
430
+ }
431
+ type TagPatch = Record<string, string> | null | undefined | void;
432
+ interface Classifier {
433
+ name: string;
434
+ classify(module: ModuleNode, ctx: ClassifierContext): TagPatch;
435
+ }
436
+ declare function defineClassifier(classifier: Classifier): Classifier;
437
+ //#endregion
438
438
  //#region src/contracts/diagnostic.d.ts
439
439
  /** Alias, for readability at use sites — it IS {@link Severity}. */
440
440
  type DiagnosticSeverity = Severity;
@@ -477,7 +477,18 @@ type WellKnownDiagnosticCode =
477
477
  * "debt we accepted" and becomes a permanent hole, and the stale entry will silently
478
478
  * re-suppress the finding if it ever comes back.
479
479
  */
480
- "baseline-stale";
480
+ "baseline-stale" |
481
+ /**
482
+ * A baseline is configured but could not be used: missing, unparseable, or written under a
483
+ * different fingerprint scheme.
484
+ *
485
+ * Gated with the other configuration errors, and therefore failing by default. The failure
486
+ * this prevents is specific: an unusable baseline suppresses nothing, so the run reports
487
+ * every accepted finding as new — and a team that reads that as "the baseline is broken"
488
+ * is the lucky case. Silence here would instead let a *scheme bump* look like a fresh
489
+ * regression, or let a deleted baseline look like a clean repo.
490
+ */
491
+ "baseline-invalid";
481
492
  type DiagnosticCode = WellKnownDiagnosticCode | (string & {});
482
493
  /**
483
494
  * Everything the run wants to say that is *not* a violation of the user's architecture: a
@@ -509,6 +520,20 @@ interface UnscannableFilesDetails {
509
520
  /** A bounded sample of repo-relative paths, for a message a human can follow. */
510
521
  sample: readonly string[];
511
522
  }
523
+ /** Payload shape for `code: "baseline-stale"`. */
524
+ interface BaselineStaleDetails {
525
+ /** How many accepted entries this run did not reproduce. */
526
+ count: number;
527
+ /** The fingerprints, so a tool can prune the file without re-running the analysis. */
528
+ fingerprints: readonly string[];
529
+ }
530
+ /** Payload shape for `code: "baseline-invalid"`. */
531
+ interface BaselineInvalidDetails {
532
+ /** Repo-relative path to the configured baseline. */
533
+ path: string;
534
+ /** Why it could not be used, as a sentence fragment. */
535
+ reason: string;
536
+ }
512
537
  /** Payload shape for `code: "empty-scope"`. */
513
538
  interface EmptyScopeDetails {
514
539
  /** The scope as configured, so the message can be acted on without reopening the config. */
@@ -759,5 +784,5 @@ interface GraphTransform {
759
784
  }
760
785
  declare function defineTransform(transform: GraphTransform): GraphTransform;
761
786
  //#endregion
762
- export { ProjectGraph as $, primaryEdge as A, EdgeAttributes as B, Violation as C, countBySeverity as D, compareViolations as E, ClassifierContext as F, GraphMutation as G, FIRST_PARTY_KINDS as H, TagPatch as I, MODULE_ID_SCHEMES as J, HostInfo as K, defineClassifier as L, primarySourceLocation as M, renderMessage as N, fingerprintOf as O, Classifier as P, ModuleNode as Q, Capability as R, SeverityCounts as S, ViolationLocation as T, GraphDelivery as U, EdgeKind as V, GraphDraft as W, ModuleIdScheme as X, ModuleId as Y, ModuleKind as Z, RuleSkippedDetails as _, defineGraphComputation as a, assertIrCompatible as at, FINGERPRINT_SCHEME as b, GraphQuery as c, isFirstParty as ct, ModuleSelection as d, ProjectGraphInit as et, filterKey as f, EmptyScopeDetails as g, DiagnosticSeverity as h, GraphComputation as i, WellKnownEdgeKind as it, primaryModule as j, locationsOf as k, GraphView as l, isThirdParty as lt, DiagnosticCode as m, TransformContext as n, THIRD_PARTY_KINDS as nt, EdgeFilter as o, displayModuleId as ot, Diagnostic as p, IR_VERSION as q, defineTransform as r, WellKnownCapability as rt, GraphIndex as s, irMajor as st, GraphTransform as t, SourceLocation as tt, ModuleFilter as u, parseModuleId as ut, UnscannableFilesDetails as v, ViolationInput as w, Severity as x, WellKnownDiagnosticCode as y, Edge as z };
763
- //# sourceMappingURL=transform-CzxyWbUC.d.mts.map
787
+ export { ModuleKind as $, ViolationInput as A, Capability as B, ClassifierContext as C, Severity as D, FINGERPRINT_SCHEME as E, locationsOf as F, GraphDelivery as G, EdgeAttributes as H, primaryEdge as I, HostInfo as J, GraphDraft as K, primaryModule as L, compareViolations as M, countBySeverity as N, SeverityCounts as O, fingerprintOf as P, ModuleIdScheme as Q, primarySourceLocation as R, Classifier as S, defineClassifier as T, EdgeKind as U, Edge as V, FIRST_PARTY_KINDS as W, MODULE_ID_SCHEMES as X, IR_VERSION as Y, ModuleId as Z, DiagnosticSeverity as _, defineGraphComputation as a, WellKnownCapability as at, UnscannableFilesDetails as b, GraphQuery as c, displayModuleId as ct, ModuleSelection as d, isThirdParty as dt, ModuleNode as et, filterKey as f, parseModuleId as ft, DiagnosticCode as g, Diagnostic as h, GraphComputation as i, THIRD_PARTY_KINDS as it, ViolationLocation as j, Violation as k, GraphView as l, irMajor as lt, BaselineStaleDetails as m, TransformContext as n, ProjectGraphInit as nt, EdgeFilter as o, WellKnownEdgeKind as ot, BaselineInvalidDetails as p, GraphMutation as q, defineTransform as r, SourceLocation as rt, GraphIndex as s, assertIrCompatible as st, GraphTransform as t, ProjectGraph as tt, ModuleFilter as u, isFirstParty as ut, EmptyScopeDetails as v, TagPatch as w, WellKnownDiagnosticCode as x, RuleSkippedDetails as y, renderMessage as z };
788
+ //# sourceMappingURL=transform-beNJIws-.d.cts.map