@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.
package/dist/internal.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_prepare = require("./prepare-CEaZxLPI.cjs");
2
+ const require_prepare = require("./prepare-DNER1gV3.cjs");
3
3
  exports.GraphComputationCache = require_prepare.GraphComputationCache;
4
4
  exports.GraphDraft = require_prepare.GraphDraft;
5
5
  exports.GraphIndex = require_prepare.GraphIndex;
@@ -1,4 +1,4 @@
1
- import { $ as ProjectGraph, P as Classifier, R as Capability, W as GraphDraft, c as GraphQuery, f as filterKey, i as GraphComputation, l as GraphView, p as Diagnostic, s as GraphIndex, t as GraphTransform } from "./transform-CzxyWbUC.cjs";
1
+ import { B as Capability, K as GraphDraft, S as Classifier, c as GraphQuery, f as filterKey, h as Diagnostic, i as GraphComputation, l as GraphView, s as GraphIndex, t as GraphTransform, tt as ProjectGraph } from "./transform-beNJIws-.cjs";
2
2
  //#region src/analysis/cache.d.ts
3
3
  /**
4
4
  * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
@@ -1,4 +1,4 @@
1
- import { $ as ProjectGraph, P as Classifier, R as Capability, W as GraphDraft, c as GraphQuery, f as filterKey, i as GraphComputation, l as GraphView, p as Diagnostic, s as GraphIndex, t as GraphTransform } from "./transform-CzxyWbUC.mjs";
1
+ import { B as Capability, K as GraphDraft, S as Classifier, c as GraphQuery, f as filterKey, h as Diagnostic, i as GraphComputation, l as GraphView, s as GraphIndex, t as GraphTransform, tt as ProjectGraph } from "./transform-beNJIws-.mjs";
2
2
  //#region src/analysis/cache.d.ts
3
3
  /**
4
4
  * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
package/dist/internal.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { C as stableHash, S as sourceRelative, a as filterKey, c as GraphDraft, i as GraphQuery, n as prepareGraph, o as GraphComputationCache, r as GraphIndex, t as applyProjectBoundary, w as toRelative, x as hashParts } from "./prepare-BjMxyYOm.mjs";
1
+ import { a as filterKey, c as sourceRelative, f as GraphDraft, i as GraphQuery, l as stableHash, n as prepareGraph, o as GraphComputationCache, r as GraphIndex, s as hashParts, t as applyProjectBoundary, u as toRelative } from "./prepare-BxSeKFYq.mjs";
2
2
  export { GraphComputationCache, GraphDraft, GraphIndex, GraphQuery, applyProjectBoundary, filterKey, hashParts, prepareGraph, sourceRelative, stableHash, toRelative };
@@ -1,75 +1,5 @@
1
1
  import * as path from "node:path";
2
2
  import picomatch from "picomatch";
3
- //#region src/paths.ts
4
- /** Forward slashes everywhere, so one string form crosses platforms. */
5
- function normalize(p) {
6
- return p.replaceAll("\\", "/");
7
- }
8
- /**
9
- * A file's path relative to `root`, forward-slashed, or `null` when it does not lie
10
- * strictly inside `root`.
11
- *
12
- * The one answer to "where is this file, in the terms my patterns are written in".
13
- * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
14
- * `within` all describe positions in a tree, and they must all agree on what a position
15
- * is — including on the edge cases: the root itself is not *inside* the root, and a file
16
- * above it has no position at all.
17
- *
18
- * A path that is already relative is taken as relative *to the root* rather than resolved
19
- * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
20
- * `@archwall/test-utils`) use bare ids, and resolving those against the working directory
21
- * would silently place every module outside the project.
22
- */
23
- function sourceRelative(root, file) {
24
- const normalized = normalize(file);
25
- if (!path.isAbsolute(normalized)) return normalized === "" ? null : normalized;
26
- const rel = normalize(path.relative(root, normalized));
27
- if (rel === "" || rel.startsWith("../") || rel === ".." || path.isAbsolute(rel)) return null;
28
- return rel;
29
- }
30
- /**
31
- * Repository-relative, for anything that leaves the process: violation fingerprints,
32
- * reporter output, SARIF `artifactLocation.uri`.
33
- *
34
- * Absolute paths are the right module identity *inside* a run and wrong in every output,
35
- * because they make results machine-specific. SARIF in particular is silently useless with
36
- * absolute URIs: GitHub code scanning cannot associate the result with a repository file.
37
- *
38
- * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
39
- * root is returned as-is rather than as `null`, because output must always print something,
40
- * whereas matching must be able to say "not here".
41
- */
42
- function toRelative(root, id) {
43
- const normalized = normalize(id);
44
- if (!path.isAbsolute(normalized)) return normalized;
45
- return sourceRelative(root, normalized) ?? normalized;
46
- }
47
- /**
48
- * FNV-1a, 64-bit, as 16 lowercase hex chars.
49
- *
50
- * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
51
- * worker, edge runtime), and this hash is used for identity, never for security.
52
- */
53
- function stableHash(input) {
54
- let h1 = 2166136261;
55
- let h2 = 16777619;
56
- for (let i = 0; i < input.length; i++) {
57
- const c = input.charCodeAt(i);
58
- h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
59
- h2 = Math.imul(h2 ^ (c << 5 | c >>> 3), 16777619) >>> 0;
60
- }
61
- return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
62
- }
63
- /**
64
- * Joins parts into one hashable string.
65
- *
66
- * `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
67
- * a delimiter that can occur inside a part makes two different tuples hash identically.
68
- */
69
- function hashParts(parts) {
70
- return stableHash(parts.join("\0"));
71
- }
72
- //#endregion
73
3
  //#region src/errors.ts
74
4
  var ArchWallError = class extends Error {
75
5
  constructor(message) {
@@ -293,6 +223,76 @@ function assertIrCompatible(graphVersion) {
293
223
  if (irMajor(graphVersion) !== irMajor("1.0.0")) throw new IrVersionMismatchError(graphVersion, IR_VERSION);
294
224
  }
295
225
  //#endregion
226
+ //#region src/paths.ts
227
+ /** Forward slashes everywhere, so one string form crosses platforms. */
228
+ function normalize(p) {
229
+ return p.replaceAll("\\", "/");
230
+ }
231
+ /**
232
+ * A file's path relative to `root`, forward-slashed, or `null` when it does not lie
233
+ * strictly inside `root`.
234
+ *
235
+ * The one answer to "where is this file, in the terms my patterns are written in".
236
+ * `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
237
+ * `within` all describe positions in a tree, and they must all agree on what a position
238
+ * is — including on the edge cases: the root itself is not *inside* the root, and a file
239
+ * above it has no position at all.
240
+ *
241
+ * A path that is already relative is taken as relative *to the root* rather than resolved
242
+ * against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
243
+ * `@archwall/test-utils`) use bare ids, and resolving those against the working directory
244
+ * would silently place every module outside the project.
245
+ */
246
+ function sourceRelative(root, file) {
247
+ const normalized = normalize(file);
248
+ if (!path.isAbsolute(normalized)) return normalized === "" ? null : normalized;
249
+ const rel = normalize(path.relative(root, normalized));
250
+ if (rel === "" || rel.startsWith("../") || rel === ".." || path.isAbsolute(rel)) return null;
251
+ return rel;
252
+ }
253
+ /**
254
+ * Repository-relative, for anything that leaves the process: violation fingerprints,
255
+ * reporter output, SARIF `artifactLocation.uri`.
256
+ *
257
+ * Absolute paths are the right module identity *inside* a run and wrong in every output,
258
+ * because they make results machine-specific. SARIF in particular is silently useless with
259
+ * absolute URIs: GitHub code scanning cannot associate the result with a repository file.
260
+ *
261
+ * Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
262
+ * root is returned as-is rather than as `null`, because output must always print something,
263
+ * whereas matching must be able to say "not here".
264
+ */
265
+ function toRelative(root, id) {
266
+ const normalized = normalize(id);
267
+ if (!path.isAbsolute(normalized)) return normalized;
268
+ return sourceRelative(root, normalized) ?? normalized;
269
+ }
270
+ /**
271
+ * FNV-1a, 64-bit, as 16 lowercase hex chars.
272
+ *
273
+ * Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
274
+ * worker, edge runtime), and this hash is used for identity, never for security.
275
+ */
276
+ function stableHash(input) {
277
+ let h1 = 2166136261;
278
+ let h2 = 16777619;
279
+ for (let i = 0; i < input.length; i++) {
280
+ const c = input.charCodeAt(i);
281
+ h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
282
+ h2 = Math.imul(h2 ^ (c << 5 | c >>> 3), 16777619) >>> 0;
283
+ }
284
+ return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
285
+ }
286
+ /**
287
+ * Joins parts into one hashable string.
288
+ *
289
+ * `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
290
+ * a delimiter that can occur inside a part makes two different tuples hash identically.
291
+ */
292
+ function hashParts(parts) {
293
+ return stableHash(parts.join("\0"));
294
+ }
295
+ //#endregion
296
296
  //#region src/analysis/cache.ts
297
297
  /**
298
298
  * Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
@@ -767,6 +767,6 @@ function applyProjectBoundary(graph, config) {
767
767
  return boundaryAndClassify(graph, config, []);
768
768
  }
769
769
  //#endregion
770
- export { stableHash as C, sourceRelative as S, isThirdParty as _, filterKey as a, IrVersionMismatchError as b, GraphDraft as c, ProjectGraph as d, THIRD_PARTY_KINDS as f, isFirstParty as g, irMajor as h, GraphQuery as i, IR_VERSION as l, displayModuleId as m, prepareGraph as n, GraphComputationCache as o, assertIrCompatible as p, GraphIndex as r, FIRST_PARTY_KINDS as s, applyProjectBoundary as t, MODULE_ID_SCHEMES as u, parseModuleId as v, toRelative as w, hashParts as x, ArchWallError as y };
770
+ export { ArchWallError as C, parseModuleId as S, assertIrCompatible as _, filterKey as a, isFirstParty as b, sourceRelative as c, FIRST_PARTY_KINDS as d, GraphDraft as f, THIRD_PARTY_KINDS as g, ProjectGraph as h, GraphQuery as i, stableHash as l, MODULE_ID_SCHEMES as m, prepareGraph as n, GraphComputationCache as o, IR_VERSION as p, GraphIndex as r, hashParts as s, applyProjectBoundary as t, toRelative as u, displayModuleId as v, IrVersionMismatchError as w, isThirdParty as x, irMajor as y };
771
771
 
772
- //# sourceMappingURL=prepare-BjMxyYOm.mjs.map
772
+ //# sourceMappingURL=prepare-BxSeKFYq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-BjMxyYOm.mjs","names":["#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-BxSeKFYq.mjs","names":["#modules","#edges","#base","#mutableModules","#mutableEdges","#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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"}