@archwall/core 0.1.0 → 0.2.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.
@@ -54,7 +54,16 @@ type WellKnownCapability =
54
54
  * that matches on specifiers must require this, or it silently matches nothing on hosts
55
55
  * that cannot supply them and reports a clean run rather than an unavailable one.
56
56
  */
57
- "raw-specifiers";
57
+ "raw-specifiers" |
58
+ /**
59
+ * Type-only edges are PRESENT and carry `attributes.typeOnly`. See {@link EdgeAttributes}.
60
+ *
61
+ * A rule that treats type-only imports differently must require this. Without it, a host
62
+ * that erases type imports (every bundler does) is indistinguishable from one where the
63
+ * code genuinely has no type imports — and "no `attributes.typeOnly` anywhere" would be
64
+ * read as "nothing is type-only" rather than "nobody asked".
65
+ */
66
+ "type-only-edges";
58
67
  /**
59
68
  * Open union: adapters may declare capabilities core does not know about, and rules may
60
69
  * require them, without an IR major. `WellKnownCapability` keeps autocomplete useful for
@@ -75,11 +84,44 @@ interface SourceLocation {
75
84
  }
76
85
  type WellKnownEdgeKind = "static" | "dynamic" | "reexport";
77
86
  /**
78
- * Open union so future graph facts (CSS imports, worker edges, type-only edges) arrive
79
- * additively. Consumers must treat an unrecognised kind as "some dependency exists" —
80
- * never assume exhaustiveness.
87
+ * Open union so future graph facts (CSS imports, worker edges) arrive additively. Consumers
88
+ * must treat an unrecognised kind as "some dependency exists" — never assume exhaustiveness.
89
+ *
90
+ * `kind` answers ONE question — roughly "what syntax produced this edge" — and deliberately
91
+ * keeps answering only that. Everything else an edge might be belongs in
92
+ * {@link EdgeAttributes}; see the note there for why.
81
93
  */
82
94
  type EdgeKind = WellKnownEdgeKind | (string & {});
95
+ /**
96
+ * Orthogonal facts about an edge, as an OPEN bag.
97
+ *
98
+ * `kind` is one enum, but the domain is not one-dimensional. `export type * from "./x"` is
99
+ * *both* a re-export and type-only; a dynamic import of a barrel is both dynamic and a
100
+ * re-export. Modelling those as one enum forces every producer to pick a winner, and forces
101
+ * every consumer to guess which axis the winner came from.
102
+ *
103
+ * So the axes split: `kind` keeps the syntactic one, and everything else lands here, where it
104
+ * composes. A bag rather than named fields because the next axis is always unknown — import
105
+ * attributes (`with { type: "json" }`), worker edges, CSS edges, `require` vs `import`
106
+ * interop — and each one arriving as a new top-level `Edge` field would be a new IR major.
107
+ *
108
+ * **Absent means "the host did not say", never "false".** That distinction is the whole
109
+ * reason {@link WellKnownCapability} has `type-only-edges`: a bundler that erased type imports
110
+ * before ArchWall saw them reports nothing here, and a rule must be able to tell that apart
111
+ * from a codebase with no type imports in it.
112
+ */
113
+ interface EdgeAttributes {
114
+ /**
115
+ * The import is erased at compile time (`import type`, `export type`, or an
116
+ * `import { type X }` specifier where every named binding is type-only).
117
+ *
118
+ * Requires the `type-only-edges` capability to be meaningful. `true` or absent — never
119
+ * `false`, so that a producer cannot accidentally assert the negative it does not know.
120
+ */
121
+ typeOnly?: true;
122
+ /** Third parties and future core versions extend here without an IR major. */
123
+ [key: string]: string | true | undefined;
124
+ }
83
125
  /**
84
126
  * What a module *is*, relative to the project being analysed.
85
127
  *
@@ -129,6 +171,8 @@ interface Edge {
129
171
  kind: EdgeKind;
130
172
  /** Present only if host capability allows. */
131
173
  loc?: SourceLocation;
174
+ /** Orthogonal facts; see {@link EdgeAttributes}. Absent when the host reported none. */
175
+ attributes?: EdgeAttributes;
132
176
  }
133
177
  type GraphDelivery = "complete" | "progressive";
134
178
  interface ProjectGraphInit {
@@ -139,6 +183,14 @@ interface ProjectGraphInit {
139
183
  edges: readonly Edge[];
140
184
  /** Default {@link IR_VERSION}. Adapters should leave this alone. */
141
185
  irVersion?: string;
186
+ /**
187
+ * Opaque identity for this graph. Defaults to a fresh process-local number.
188
+ *
189
+ * The contract is one-directional and deliberately weak: **equal revisions mean the same
190
+ * graph; unequal revisions mean nothing.** Set it explicitly only if you can guarantee the
191
+ * first half — a producer that content-hashes its inputs, for instance.
192
+ */
193
+ revision?: number;
142
194
  }
143
195
  /**
144
196
  * The module graph, as an OPAQUE handle.
@@ -155,6 +207,12 @@ declare class ProjectGraph {
155
207
  readonly irVersion: string;
156
208
  readonly host: HostInfo;
157
209
  readonly delivery: GraphDelivery;
210
+ /**
211
+ * See {@link ProjectGraphInit.revision}. Preserved across {@link replaceStores}, because a
212
+ * derived graph is a deterministic function of this one and the config that derived it —
213
+ * so a cache keyed on `(revision, configKey)` stays sound through the prepare pipeline.
214
+ */
215
+ readonly revision: number;
158
216
  private constructor();
159
217
  static create(init: ProjectGraphInit): ProjectGraph;
160
218
  get moduleCount(): number;
@@ -261,8 +319,6 @@ type Severity = "error" | "warn" | "info";
261
319
  * offending location — it has N of them, and the old model could only name one and had to
262
320
  * serialise the rest into the message string. A finding about a package, a directory, or
263
321
  * the configuration has no module at all.
264
- *
265
- * See docs/adr/0004-violation-locations.md.
266
322
  */
267
323
  type ViolationLocation = {
268
324
  type: "edge";
@@ -355,8 +411,7 @@ declare function renderMessage(template: string, data: Readonly<Record<string, s
355
411
  * Fingerprint scheme version. Bump when the algorithm changes so that a stale baseline
356
412
  * ERRORS instead of silently mismatching every entry.
357
413
  *
358
- * `aw3` is the first scheme over canonical module ids
359
- * (docs/adr/0012-canonical-module-identity.md). Before it, a violation about `react` hashed the
414
+ * `aw3` is the first scheme over canonical module ids. Before it, a violation about `react` hashed the
360
415
  * host's own id — a resolved `node_modules` path under the CLI, the bare specifier under esbuild
361
416
  * — so the same finding fingerprinted differently under two bundlers.
362
417
  */
@@ -405,7 +460,24 @@ type WellKnownDiagnosticCode =
405
460
  /** A configured rule, or one of its options, is deprecated. */
406
461
  "rule-deprecated" |
407
462
  /** A graph transform threw. The pipeline continued without its contribution. */
408
- "transform-failed";
463
+ "transform-failed" |
464
+ /**
465
+ * Files are inside the project boundary that the producer cannot read, so they are absent
466
+ * from the graph entirely — not excluded, not unresolved, just invisible.
467
+ *
468
+ * Only a producer that ENUMERATES a directory tree can detect this, which in practice means
469
+ * the CLI: a bundler adapter is handed a graph whose membership its compiler already decided.
470
+ */
471
+ "unscannable-files" |
472
+ /**
473
+ * The baseline lists violations this run did not produce — they were fixed, or the code
474
+ * they were about is gone.
475
+ *
476
+ * Worth saying out loud rather than tolerating: a baseline that is never pruned stops being
477
+ * "debt we accepted" and becomes a permanent hole, and the stale entry will silently
478
+ * re-suppress the finding if it ever comes back.
479
+ */
480
+ "baseline-stale";
409
481
  type DiagnosticCode = WellKnownDiagnosticCode | (string & {});
410
482
  /**
411
483
  * Everything the run wants to say that is *not* a violation of the user's architecture: a
@@ -428,6 +500,15 @@ interface RuleSkippedDetails {
428
500
  missingCapabilities: readonly Capability[];
429
501
  host: string;
430
502
  }
503
+ /** Payload shape for `code: "unscannable-files"`. */
504
+ interface UnscannableFilesDetails {
505
+ /** How many in-boundary files the producer could not read. */
506
+ count: number;
507
+ /** Distinct extensions, most common first — what to act on. */
508
+ extensions: readonly string[];
509
+ /** A bounded sample of repo-relative paths, for a message a human can follow. */
510
+ sample: readonly string[];
511
+ }
431
512
  /** Payload shape for `code: "empty-scope"`. */
432
513
  interface EmptyScopeDetails {
433
514
  /** The scope as configured, so the message can be acted on without reopening the config. */
@@ -459,6 +540,16 @@ interface EdgeFilter {
459
540
  toTag?: Record<string, string>;
460
541
  /** Tag key; keep edge iff BOTH endpoints have the tag and values differ. */
461
542
  crossing?: string;
543
+ /**
544
+ * Selects on {@link EdgeAttributes}. `true` requires the attribute present; `false` requires
545
+ * it ABSENT; a string requires that exact value.
546
+ *
547
+ * `false` and "absent" are the same test on purpose — attributes are never stored as `false`
548
+ * (see {@link EdgeAttributes}), so "not type-only" and "nobody said" are indistinguishable
549
+ * *here* by construction. A rule that must tell them apart declares the corresponding
550
+ * capability and gets skipped loudly instead, which is the only honest answer.
551
+ */
552
+ attributes?: Readonly<Record<string, string | boolean>>;
462
553
  }
463
554
  /**
464
555
  * Stable key for a filter, so the engine can bucket rules that want the same slice of the
@@ -504,7 +595,41 @@ interface ModuleSelection extends Iterable<ModuleNode> {
504
595
  edgesIn(filter?: EdgeFilter): readonly Edge[];
505
596
  }
506
597
  /**
507
- * The only sanctioned way to read a graph.
598
+ * The read surface a rule gets and the type it should name.
599
+ *
600
+ * An INTERFACE rather than the class, because the two are different promises. What ArchWall
601
+ * owes a rule author is a set of questions that can be asked about a graph; what it must stay
602
+ * free to change is how those questions are answered. Naming the class in `RuleContext` fused
603
+ * the two: the concrete implementation became observable via `instanceof`, a test double became
604
+ * impossible to supply, and an interned or columnar store became a breaking change rather than
605
+ * an optimisation.
606
+ *
607
+ * {@link GraphQuery} is the only implementation core ships, and it lives in
608
+ * `@archwall/core/internal`. Rules never construct one — they are handed one — so nothing is
609
+ * taken away by that; a rule author who needs one for a TEST gets it from
610
+ * `@archwall/test-utils`, which is the supported way to build a graph by hand.
611
+ *
612
+ * See {@link GraphQuery} for what scope does to each of these operations.
613
+ */
614
+ interface GraphView {
615
+ module(id: ModuleId): ModuleNode | undefined;
616
+ moduleCount(): number;
617
+ moduleIds(): Iterable<ModuleId>;
618
+ has(id: ModuleId): boolean;
619
+ tagOf(id: ModuleId, key: string): string | undefined;
620
+ modules(filter?: ModuleFilter): ModuleSelection;
621
+ edges(filter?: EdgeFilter): readonly Edge[];
622
+ edgesOutOf(id: ModuleId): readonly Edge[];
623
+ edgesInto(id: ModuleId): readonly Edge[];
624
+ reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;
625
+ reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;
626
+ pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null;
627
+ filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[];
628
+ matchesEdge(e: Edge, filter: EdgeFilter): boolean;
629
+ matchesModule(m: ModuleNode, filter: ModuleFilter): boolean;
630
+ }
631
+ /**
632
+ * The only sanctioned way to read a graph; the sole implementation of {@link GraphView}.
508
633
  *
509
634
  * A scoped query is a VIEW: it shares the underlying {@link GraphIndex} with the query it
510
635
  * came from and differs only in which modules it is *about*.
@@ -528,7 +653,7 @@ interface ModuleSelection extends Iterable<ModuleNode> {
528
653
  * thing it can find; hiding the target would turn `layer-dependencies` under a scope from a
529
654
  * finding into silence.
530
655
  */
531
- declare class GraphQuery {
656
+ declare class GraphQuery implements GraphView {
532
657
  #private;
533
658
  constructor(graph: ProjectGraph, index?: GraphIndex, scope?: ReadonlySet<ModuleId>);
534
659
  /** A view of the same graph restricted to `scope`, sharing this query's index. */
@@ -585,7 +710,7 @@ declare class GraphQuery {
585
710
  */
586
711
  interface GraphComputation<T> {
587
712
  name: string;
588
- compute(graph: GraphQuery): T;
713
+ compute(graph: GraphView): T;
589
714
  }
590
715
  declare function defineGraphComputation<T>(computation: GraphComputation<T>): GraphComputation<T>;
591
716
  //#endregion
@@ -634,5 +759,5 @@ interface GraphTransform {
634
759
  }
635
760
  declare function defineTransform(transform: GraphTransform): GraphTransform;
636
761
  //#endregion
637
- export { THIRD_PARTY_KINDS as $, primarySourceLocation as A, GraphDelivery as B, ViolationLocation as C, locationsOf as D, fingerprintOf as E, defineClassifier as F, MODULE_ID_SCHEMES as G, GraphMutation as H, Capability as I, ModuleKind as J, ModuleId as K, Edge as L, Classifier as M, ClassifierContext as N, primaryEdge as O, TagPatch as P, SourceLocation as Q, EdgeKind as R, ViolationInput as S, countBySeverity as T, HostInfo as U, GraphDraft as V, IR_VERSION as W, ProjectGraph as X, ModuleNode as Y, ProjectGraphInit as Z, WellKnownDiagnosticCode as _, defineGraphComputation as a, isFirstParty as at, SeverityCounts as b, GraphQuery as c, filterKey as d, WellKnownCapability as et, Diagnostic as f, RuleSkippedDetails as g, EmptyScopeDetails as h, GraphComputation as i, irMajor as it, renderMessage as j, primaryModule as k, ModuleFilter as l, DiagnosticSeverity as m, TransformContext as n, assertIrCompatible as nt, EdgeFilter as o, isThirdParty as ot, DiagnosticCode as p, ModuleIdScheme as q, defineTransform as r, displayModuleId as rt, GraphIndex as s, parseModuleId as st, GraphTransform as t, WellKnownEdgeKind as tt, ModuleSelection as u, FINGERPRINT_SCHEME as v, compareViolations as w, Violation as x, Severity as y, FIRST_PARTY_KINDS as z };
638
- //# sourceMappingURL=transform-CnUPOO0E.d.mts.map
762
+ export { ProjectGraph as $, primaryEdge as A, EdgeAttributes as B, Violation as C, countBySeverity as D, compareViolations as E, ClassifierContext as F, GraphMutation as G, FIRST_PARTY_KINDS as H, TagPatch as I, MODULE_ID_SCHEMES as J, HostInfo as K, defineClassifier as L, primarySourceLocation as M, renderMessage as N, fingerprintOf as O, Classifier as P, ModuleNode as Q, Capability as R, SeverityCounts as S, ViolationLocation as T, GraphDelivery as U, EdgeKind as V, GraphDraft as W, ModuleIdScheme as X, ModuleId as Y, ModuleKind as Z, RuleSkippedDetails as _, defineGraphComputation as a, assertIrCompatible as at, FINGERPRINT_SCHEME as b, GraphQuery as c, isFirstParty as ct, ModuleSelection as d, ProjectGraphInit as et, filterKey as f, EmptyScopeDetails as g, DiagnosticSeverity as h, GraphComputation as i, WellKnownEdgeKind as it, primaryModule as j, locationsOf as k, GraphView as l, isThirdParty as lt, DiagnosticCode as m, TransformContext as n, THIRD_PARTY_KINDS as nt, EdgeFilter as o, displayModuleId as ot, Diagnostic as p, IR_VERSION as q, defineTransform as r, WellKnownCapability as rt, GraphIndex as s, irMajor as st, GraphTransform as t, SourceLocation as tt, ModuleFilter as u, parseModuleId as ut, UnscannableFilesDetails as v, ViolationInput as w, Severity as x, WellKnownDiagnosticCode as y, Edge as z };
763
+ //# sourceMappingURL=transform-CzxyWbUC.d.cts.map
@@ -54,7 +54,16 @@ type WellKnownCapability =
54
54
  * that matches on specifiers must require this, or it silently matches nothing on hosts
55
55
  * that cannot supply them and reports a clean run rather than an unavailable one.
56
56
  */
57
- "raw-specifiers";
57
+ "raw-specifiers" |
58
+ /**
59
+ * Type-only edges are PRESENT and carry `attributes.typeOnly`. See {@link EdgeAttributes}.
60
+ *
61
+ * A rule that treats type-only imports differently must require this. Without it, a host
62
+ * that erases type imports (every bundler does) is indistinguishable from one where the
63
+ * code genuinely has no type imports — and "no `attributes.typeOnly` anywhere" would be
64
+ * read as "nothing is type-only" rather than "nobody asked".
65
+ */
66
+ "type-only-edges";
58
67
  /**
59
68
  * Open union: adapters may declare capabilities core does not know about, and rules may
60
69
  * require them, without an IR major. `WellKnownCapability` keeps autocomplete useful for
@@ -75,11 +84,44 @@ interface SourceLocation {
75
84
  }
76
85
  type WellKnownEdgeKind = "static" | "dynamic" | "reexport";
77
86
  /**
78
- * Open union so future graph facts (CSS imports, worker edges, type-only edges) arrive
79
- * additively. Consumers must treat an unrecognised kind as "some dependency exists" —
80
- * never assume exhaustiveness.
87
+ * Open union so future graph facts (CSS imports, worker edges) arrive additively. Consumers
88
+ * must treat an unrecognised kind as "some dependency exists" — never assume exhaustiveness.
89
+ *
90
+ * `kind` answers ONE question — roughly "what syntax produced this edge" — and deliberately
91
+ * keeps answering only that. Everything else an edge might be belongs in
92
+ * {@link EdgeAttributes}; see the note there for why.
81
93
  */
82
94
  type EdgeKind = WellKnownEdgeKind | (string & {});
95
+ /**
96
+ * Orthogonal facts about an edge, as an OPEN bag.
97
+ *
98
+ * `kind` is one enum, but the domain is not one-dimensional. `export type * from "./x"` is
99
+ * *both* a re-export and type-only; a dynamic import of a barrel is both dynamic and a
100
+ * re-export. Modelling those as one enum forces every producer to pick a winner, and forces
101
+ * every consumer to guess which axis the winner came from.
102
+ *
103
+ * So the axes split: `kind` keeps the syntactic one, and everything else lands here, where it
104
+ * composes. A bag rather than named fields because the next axis is always unknown — import
105
+ * attributes (`with { type: "json" }`), worker edges, CSS edges, `require` vs `import`
106
+ * interop — and each one arriving as a new top-level `Edge` field would be a new IR major.
107
+ *
108
+ * **Absent means "the host did not say", never "false".** That distinction is the whole
109
+ * reason {@link WellKnownCapability} has `type-only-edges`: a bundler that erased type imports
110
+ * before ArchWall saw them reports nothing here, and a rule must be able to tell that apart
111
+ * from a codebase with no type imports in it.
112
+ */
113
+ interface EdgeAttributes {
114
+ /**
115
+ * The import is erased at compile time (`import type`, `export type`, or an
116
+ * `import { type X }` specifier where every named binding is type-only).
117
+ *
118
+ * Requires the `type-only-edges` capability to be meaningful. `true` or absent — never
119
+ * `false`, so that a producer cannot accidentally assert the negative it does not know.
120
+ */
121
+ typeOnly?: true;
122
+ /** Third parties and future core versions extend here without an IR major. */
123
+ [key: string]: string | true | undefined;
124
+ }
83
125
  /**
84
126
  * What a module *is*, relative to the project being analysed.
85
127
  *
@@ -129,6 +171,8 @@ interface Edge {
129
171
  kind: EdgeKind;
130
172
  /** Present only if host capability allows. */
131
173
  loc?: SourceLocation;
174
+ /** Orthogonal facts; see {@link EdgeAttributes}. Absent when the host reported none. */
175
+ attributes?: EdgeAttributes;
132
176
  }
133
177
  type GraphDelivery = "complete" | "progressive";
134
178
  interface ProjectGraphInit {
@@ -139,6 +183,14 @@ interface ProjectGraphInit {
139
183
  edges: readonly Edge[];
140
184
  /** Default {@link IR_VERSION}. Adapters should leave this alone. */
141
185
  irVersion?: string;
186
+ /**
187
+ * Opaque identity for this graph. Defaults to a fresh process-local number.
188
+ *
189
+ * The contract is one-directional and deliberately weak: **equal revisions mean the same
190
+ * graph; unequal revisions mean nothing.** Set it explicitly only if you can guarantee the
191
+ * first half — a producer that content-hashes its inputs, for instance.
192
+ */
193
+ revision?: number;
142
194
  }
143
195
  /**
144
196
  * The module graph, as an OPAQUE handle.
@@ -155,6 +207,12 @@ declare class ProjectGraph {
155
207
  readonly irVersion: string;
156
208
  readonly host: HostInfo;
157
209
  readonly delivery: GraphDelivery;
210
+ /**
211
+ * See {@link ProjectGraphInit.revision}. Preserved across {@link replaceStores}, because a
212
+ * derived graph is a deterministic function of this one and the config that derived it —
213
+ * so a cache keyed on `(revision, configKey)` stays sound through the prepare pipeline.
214
+ */
215
+ readonly revision: number;
158
216
  private constructor();
159
217
  static create(init: ProjectGraphInit): ProjectGraph;
160
218
  get moduleCount(): number;
@@ -261,8 +319,6 @@ type Severity = "error" | "warn" | "info";
261
319
  * offending location — it has N of them, and the old model could only name one and had to
262
320
  * serialise the rest into the message string. A finding about a package, a directory, or
263
321
  * the configuration has no module at all.
264
- *
265
- * See docs/adr/0004-violation-locations.md.
266
322
  */
267
323
  type ViolationLocation = {
268
324
  type: "edge";
@@ -355,8 +411,7 @@ declare function renderMessage(template: string, data: Readonly<Record<string, s
355
411
  * Fingerprint scheme version. Bump when the algorithm changes so that a stale baseline
356
412
  * ERRORS instead of silently mismatching every entry.
357
413
  *
358
- * `aw3` is the first scheme over canonical module ids
359
- * (docs/adr/0012-canonical-module-identity.md). Before it, a violation about `react` hashed the
414
+ * `aw3` is the first scheme over canonical module ids. Before it, a violation about `react` hashed the
360
415
  * host's own id — a resolved `node_modules` path under the CLI, the bare specifier under esbuild
361
416
  * — so the same finding fingerprinted differently under two bundlers.
362
417
  */
@@ -405,7 +460,24 @@ type WellKnownDiagnosticCode =
405
460
  /** A configured rule, or one of its options, is deprecated. */
406
461
  "rule-deprecated" |
407
462
  /** A graph transform threw. The pipeline continued without its contribution. */
408
- "transform-failed";
463
+ "transform-failed" |
464
+ /**
465
+ * Files are inside the project boundary that the producer cannot read, so they are absent
466
+ * from the graph entirely — not excluded, not unresolved, just invisible.
467
+ *
468
+ * Only a producer that ENUMERATES a directory tree can detect this, which in practice means
469
+ * the CLI: a bundler adapter is handed a graph whose membership its compiler already decided.
470
+ */
471
+ "unscannable-files" |
472
+ /**
473
+ * The baseline lists violations this run did not produce — they were fixed, or the code
474
+ * they were about is gone.
475
+ *
476
+ * Worth saying out loud rather than tolerating: a baseline that is never pruned stops being
477
+ * "debt we accepted" and becomes a permanent hole, and the stale entry will silently
478
+ * re-suppress the finding if it ever comes back.
479
+ */
480
+ "baseline-stale";
409
481
  type DiagnosticCode = WellKnownDiagnosticCode | (string & {});
410
482
  /**
411
483
  * Everything the run wants to say that is *not* a violation of the user's architecture: a
@@ -428,6 +500,15 @@ interface RuleSkippedDetails {
428
500
  missingCapabilities: readonly Capability[];
429
501
  host: string;
430
502
  }
503
+ /** Payload shape for `code: "unscannable-files"`. */
504
+ interface UnscannableFilesDetails {
505
+ /** How many in-boundary files the producer could not read. */
506
+ count: number;
507
+ /** Distinct extensions, most common first — what to act on. */
508
+ extensions: readonly string[];
509
+ /** A bounded sample of repo-relative paths, for a message a human can follow. */
510
+ sample: readonly string[];
511
+ }
431
512
  /** Payload shape for `code: "empty-scope"`. */
432
513
  interface EmptyScopeDetails {
433
514
  /** The scope as configured, so the message can be acted on without reopening the config. */
@@ -459,6 +540,16 @@ interface EdgeFilter {
459
540
  toTag?: Record<string, string>;
460
541
  /** Tag key; keep edge iff BOTH endpoints have the tag and values differ. */
461
542
  crossing?: string;
543
+ /**
544
+ * Selects on {@link EdgeAttributes}. `true` requires the attribute present; `false` requires
545
+ * it ABSENT; a string requires that exact value.
546
+ *
547
+ * `false` and "absent" are the same test on purpose — attributes are never stored as `false`
548
+ * (see {@link EdgeAttributes}), so "not type-only" and "nobody said" are indistinguishable
549
+ * *here* by construction. A rule that must tell them apart declares the corresponding
550
+ * capability and gets skipped loudly instead, which is the only honest answer.
551
+ */
552
+ attributes?: Readonly<Record<string, string | boolean>>;
462
553
  }
463
554
  /**
464
555
  * Stable key for a filter, so the engine can bucket rules that want the same slice of the
@@ -504,7 +595,41 @@ interface ModuleSelection extends Iterable<ModuleNode> {
504
595
  edgesIn(filter?: EdgeFilter): readonly Edge[];
505
596
  }
506
597
  /**
507
- * The only sanctioned way to read a graph.
598
+ * The read surface a rule gets and the type it should name.
599
+ *
600
+ * An INTERFACE rather than the class, because the two are different promises. What ArchWall
601
+ * owes a rule author is a set of questions that can be asked about a graph; what it must stay
602
+ * free to change is how those questions are answered. Naming the class in `RuleContext` fused
603
+ * the two: the concrete implementation became observable via `instanceof`, a test double became
604
+ * impossible to supply, and an interned or columnar store became a breaking change rather than
605
+ * an optimisation.
606
+ *
607
+ * {@link GraphQuery} is the only implementation core ships, and it lives in
608
+ * `@archwall/core/internal`. Rules never construct one — they are handed one — so nothing is
609
+ * taken away by that; a rule author who needs one for a TEST gets it from
610
+ * `@archwall/test-utils`, which is the supported way to build a graph by hand.
611
+ *
612
+ * See {@link GraphQuery} for what scope does to each of these operations.
613
+ */
614
+ interface GraphView {
615
+ module(id: ModuleId): ModuleNode | undefined;
616
+ moduleCount(): number;
617
+ moduleIds(): Iterable<ModuleId>;
618
+ has(id: ModuleId): boolean;
619
+ tagOf(id: ModuleId, key: string): string | undefined;
620
+ modules(filter?: ModuleFilter): ModuleSelection;
621
+ edges(filter?: EdgeFilter): readonly Edge[];
622
+ edgesOutOf(id: ModuleId): readonly Edge[];
623
+ edgesInto(id: ModuleId): readonly Edge[];
624
+ reachableFrom(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;
625
+ reaching(id: ModuleId, filter?: EdgeFilter): ReadonlySet<ModuleId>;
626
+ pathBetween(from: ModuleId, to: ModuleId, filter?: EdgeFilter): readonly ModuleId[] | null;
627
+ filterEdges(edges: readonly Edge[], filter?: EdgeFilter): readonly Edge[];
628
+ matchesEdge(e: Edge, filter: EdgeFilter): boolean;
629
+ matchesModule(m: ModuleNode, filter: ModuleFilter): boolean;
630
+ }
631
+ /**
632
+ * The only sanctioned way to read a graph; the sole implementation of {@link GraphView}.
508
633
  *
509
634
  * A scoped query is a VIEW: it shares the underlying {@link GraphIndex} with the query it
510
635
  * came from and differs only in which modules it is *about*.
@@ -528,7 +653,7 @@ interface ModuleSelection extends Iterable<ModuleNode> {
528
653
  * thing it can find; hiding the target would turn `layer-dependencies` under a scope from a
529
654
  * finding into silence.
530
655
  */
531
- declare class GraphQuery {
656
+ declare class GraphQuery implements GraphView {
532
657
  #private;
533
658
  constructor(graph: ProjectGraph, index?: GraphIndex, scope?: ReadonlySet<ModuleId>);
534
659
  /** A view of the same graph restricted to `scope`, sharing this query's index. */
@@ -585,7 +710,7 @@ declare class GraphQuery {
585
710
  */
586
711
  interface GraphComputation<T> {
587
712
  name: string;
588
- compute(graph: GraphQuery): T;
713
+ compute(graph: GraphView): T;
589
714
  }
590
715
  declare function defineGraphComputation<T>(computation: GraphComputation<T>): GraphComputation<T>;
591
716
  //#endregion
@@ -634,5 +759,5 @@ interface GraphTransform {
634
759
  }
635
760
  declare function defineTransform(transform: GraphTransform): GraphTransform;
636
761
  //#endregion
637
- export { THIRD_PARTY_KINDS as $, primarySourceLocation as A, GraphDelivery as B, ViolationLocation as C, locationsOf as D, fingerprintOf as E, defineClassifier as F, MODULE_ID_SCHEMES as G, GraphMutation as H, Capability as I, ModuleKind as J, ModuleId as K, Edge as L, Classifier as M, ClassifierContext as N, primaryEdge as O, TagPatch as P, SourceLocation as Q, EdgeKind as R, ViolationInput as S, countBySeverity as T, HostInfo as U, GraphDraft as V, IR_VERSION as W, ProjectGraph as X, ModuleNode as Y, ProjectGraphInit as Z, WellKnownDiagnosticCode as _, defineGraphComputation as a, isFirstParty as at, SeverityCounts as b, GraphQuery as c, filterKey as d, WellKnownCapability as et, Diagnostic as f, RuleSkippedDetails as g, EmptyScopeDetails as h, GraphComputation as i, irMajor as it, renderMessage as j, primaryModule as k, ModuleFilter as l, DiagnosticSeverity as m, TransformContext as n, assertIrCompatible as nt, EdgeFilter as o, isThirdParty as ot, DiagnosticCode as p, ModuleIdScheme as q, defineTransform as r, displayModuleId as rt, GraphIndex as s, parseModuleId as st, GraphTransform as t, WellKnownEdgeKind as tt, ModuleSelection as u, FINGERPRINT_SCHEME as v, compareViolations as w, Violation as x, Severity as y, FIRST_PARTY_KINDS as z };
638
- //# sourceMappingURL=transform-CnUPOO0E.d.cts.map
762
+ export { ProjectGraph as $, primaryEdge as A, EdgeAttributes as B, Violation as C, countBySeverity as D, compareViolations as E, ClassifierContext as F, GraphMutation as G, FIRST_PARTY_KINDS as H, TagPatch as I, MODULE_ID_SCHEMES as J, HostInfo as K, defineClassifier as L, primarySourceLocation as M, renderMessage as N, fingerprintOf as O, Classifier as P, ModuleNode as Q, Capability as R, SeverityCounts as S, ViolationLocation as T, GraphDelivery as U, EdgeKind as V, GraphDraft as W, ModuleIdScheme as X, ModuleId as Y, ModuleKind as Z, RuleSkippedDetails as _, defineGraphComputation as a, assertIrCompatible as at, FINGERPRINT_SCHEME as b, GraphQuery as c, isFirstParty as ct, ModuleSelection as d, ProjectGraphInit as et, filterKey as f, EmptyScopeDetails as g, DiagnosticSeverity as h, GraphComputation as i, WellKnownEdgeKind as it, primaryModule as j, locationsOf as k, GraphView as l, isThirdParty as lt, DiagnosticCode as m, TransformContext as n, THIRD_PARTY_KINDS as nt, EdgeFilter as o, displayModuleId as ot, Diagnostic as p, IR_VERSION as q, defineTransform as r, WellKnownCapability as rt, GraphIndex as s, irMajor as st, GraphTransform as t, SourceLocation as tt, ModuleFilter as u, parseModuleId as ut, UnscannableFilesDetails as v, ViolationInput as w, Severity as x, WellKnownDiagnosticCode as y, Edge as z };
763
+ //# sourceMappingURL=transform-CzxyWbUC.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archwall/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Graph IR, analysis engine, and rule/preset/reporter contracts for ArchWall.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"prepare-BJHgDEui.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/**\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, type-only edges) arrive\n * additively. Consumers must treat an unrecognised kind as \"some dependency exists\" —\n * never assume exhaustiveness.\n */\nexport type EdgeKind = WellKnownEdgeKind | (string & {});\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}\n\nexport type GraphDelivery = \"complete\" | \"progressive\";\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\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 readonly #modules: ReadonlyMap<ModuleId, ModuleNode>;\n readonly #edges: readonly Edge[];\n\n private constructor(\n irVersion: string,\n host: HostInfo,\n delivery: GraphDelivery,\n modules: ReadonlyMap<ModuleId, ModuleNode>,\n edges: readonly Edge[],\n ) {\n this.irVersion = irVersion;\n this.host = host;\n this.delivery = delivery;\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 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 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 { GraphQuery } 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<GraphQuery, Map<GraphComputation<unknown>, unknown>>();\n\n get<T>(computation: GraphComputation<T>, graph: GraphQuery): 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\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 only sanctioned way to read a graph.\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 {\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 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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"}