@codependix/boundaries 0.0.3

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.
@@ -0,0 +1,736 @@
1
+ import { CodependixBoundaryRule } from '@codependix/configuration';
2
+ import { CodependixBoundarySelector } from '@codependix/configuration';
3
+ import { CodependixGraphType } from '@codependix/configuration';
4
+ import { CodependixProjectConfiguration } from '@codependix/configuration';
5
+ import { CodependixRunMode } from '@codependix/core';
6
+ import { ConfigurationService } from '@codependix/configuration';
7
+ import { MapCommandOptions } from '@codependix/configuration';
8
+ import { ModuleGraphService } from '@codependix/nestjs-modules';
9
+ import { NeighborhoodService } from '@codependix/nx-projects';
10
+ import { NestjsModuleGraph } from '@codependix/nestjs-modules';
11
+ import { NestjsProjectService } from '@codependix/nestjs-modules';
12
+ import { NxProject } from '@codependix/nx-projects';
13
+ import { NxProjectGraph } from '@codependix/nx-projects';
14
+ import { PythonImportGraph } from '@codependix/file-imports';
15
+ import { PythonService } from '@codependix/file-imports';
16
+ import { ResolvedCodependixConfiguration } from '@codependix/configuration';
17
+ import { TypescriptImportGraph } from '@codependix/file-imports';
18
+ import { TypescriptService } from '@codependix/file-imports';
19
+ import { WorkspaceGraph } from '@codependix/nx-projects';
20
+ import { WorkspaceGraphService } from '@codependix/nx-projects';
21
+
22
+ /** Wires rule evaluation together with selection, cycle finding, and reporting. */
23
+ export declare class BoundariesModule {
24
+ }
25
+
26
+ /**
27
+ * Judges a built graph against the rules declared for its level.
28
+ *
29
+ * The whole package's public act: a `BoundaryGraph` and a list of rules go in,
30
+ * and the edges and cycles that break them come out. Nothing here knows how a
31
+ * graph was built, where a rule was written, or what a caller will do with a
32
+ * violation — which is what keeps the package a leaf, depending on nothing but
33
+ * `@codependix/configuration` for the rule shapes it reads.
34
+ *
35
+ * A rule that matches nothing is not an error. It reports no violation, the
36
+ * same as a rule everything satisfies: a workspace that has not yet grown the
37
+ * code a rule was written for should not be failed for it, and a rule kept
38
+ * around after the code it covered was deleted is a cleanup rather than a
39
+ * breakage.
40
+ */
41
+ export declare class BoundariesService {
42
+ private readonly cyclesService;
43
+ private readonly selectorService;
44
+ constructor(cyclesService: BoundaryCyclesService, selectorService: BoundarySelectorService);
45
+ /** Turns one condemned edge into the violation reported for it. */
46
+ private buildAccessViolation;
47
+ /**
48
+ * The sentence a violation is reported as.
49
+ *
50
+ * A rule's own `message` is appended to the generated one rather than
51
+ * replacing it, so no wording a configuration can choose ever costs the
52
+ * report the two things it must always carry: the rule that fired, and both
53
+ * ends of what it fired on. The generated half says what happened, and the
54
+ * configured half says why it matters.
55
+ */
56
+ private buildMessage;
57
+ /**
58
+ * Reports every edge an `allow` or `forbid` rule condemns.
59
+ *
60
+ * The two kinds differ only in which verdict the target's match produces,
61
+ * so they are one traversal rather than two: `forbid` condemns an edge
62
+ * reaching what `to` claims, and `allow` condemns one reaching anything it
63
+ * does not.
64
+ */
65
+ private evaluateAccessRule;
66
+ /** Reports every cycle an `acyclic` rule's selected nodes still form. */
67
+ private evaluateAcyclicRule;
68
+ /** Indexes a graph's nodes by identifier, so an edge can look its ends up. */
69
+ private indexNodes;
70
+ /**
71
+ * Whether a rule judges this edge at all, before either end is selected.
72
+ *
73
+ * A rule naming no `edges` judges every one, which is the stricter reading
74
+ * and the right default for a rule about what a project may _depend on_.
75
+ * Naming `implicit: false` narrows it to what an
76
+ * `@nx/enforce-module-boundaries` `depConstraint` actually sees — that rule
77
+ * reads import statements, so an `implicitDependencies` entry is invisible
78
+ * to it. An edge at a level that draws no implicit edges is read as
79
+ * explicit, since a file import is not "explicitly" anything.
80
+ */
81
+ private judgesEdge;
82
+ /**
83
+ * The node an edge names, or a bare one carrying only that identifier.
84
+ *
85
+ * An edge naming a node the graph does not list is judged on its identifier
86
+ * alone rather than skipped: silently dropping the edge would hide the rule
87
+ * rather than the mistake. The consequence is that a `path`, `project`, or
88
+ * `tags` selector cannot match such an endpoint, since a bare node carries
89
+ * none of them — so a `forbid` written against `path` reports nothing for
90
+ * it, which is the safer of the two wrong answers.
91
+ *
92
+ * Every real builder lists both ends of every edge it draws — an import
93
+ * graph only draws edges between files in its own `fileNames`, and the Nx
94
+ * and NestJS graphs both list every node they connect — so this is reached
95
+ * only by a hand-written graph.
96
+ */
97
+ private resolveNode;
98
+ /**
99
+ * Every violation one graph's rules report, in the order the rules were
100
+ * declared.
101
+ *
102
+ * Declaration order rather than severity or node order: a configuration's
103
+ * author reads their own file top to bottom, and a report ordered the same
104
+ * way needs no key.
105
+ */
106
+ evaluate(args: EvaluateBoundariesArguments): BoundaryViolation[];
107
+ }
108
+
109
+ /**
110
+ * The order the four graph levels are judged in.
111
+ *
112
+ * Cheapest first, and it is not a small difference: the Nx level reads a graph
113
+ * the run already holds, while the NestJS level boots every container in
114
+ * preview mode and the TypeScript level builds a `ts.Program` per project. A
115
+ * workspace declaring only Nx rules never pays for either, because a level
116
+ * with no rules is not built at all.
117
+ *
118
+ * Written as a list rather than left implicit in the order of four `if`
119
+ * blocks, so the order a report comes out in is stated once, in the place a
120
+ * reader looks for it. `typescript` and `python` are `boundaries.fileImports`'s
121
+ * two nested languages, judged as separate levels here even though
122
+ * `codependix-file-imports` builds and exports them as one merged graph type.
123
+ */
124
+ export declare const BOUNDARY_LEVEL_ORDER: readonly ["nxProjects", "nestjsModules", "typescript", "python"];
125
+
126
+ /**
127
+ * Everything a boundary check reads about the workspace it is judging.
128
+ *
129
+ * Stated apart from `GraphRunContext` — the wider run context this package
130
+ * also owns, which carries an export mode the gate never reads — so a caller
131
+ * that only judges boundaries never has to name an export mode it has none
132
+ * of. `GraphRunContext` is structurally assignable to this, so nothing has to
133
+ * be repacked at the call site.
134
+ */
135
+ export declare interface BoundaryCheckContext {
136
+ readonly configuration: ResolvedCodependixConfiguration;
137
+ /**
138
+ * The graph types this run judges.
139
+ *
140
+ * Read by `BoundaryCheckService.run` to skip every level under a disabled
141
+ * graph type before a single graph is built — see
142
+ * `RunContextService.resolveEnabledGraphTypes`, which is where a run
143
+ * resolves this from `--no-file-imports`, `--no-nestjs-modules`, and
144
+ * `--no-nx-projects`.
145
+ */
146
+ readonly enabledGraphTypes: ReadonlySet<CodependixGraphType>;
147
+ readonly graph: NxProjectGraph;
148
+ /** Every project the graph knows, apart from the workspace root. */
149
+ readonly projects: NxProject[];
150
+ /**
151
+ * The projects the run was narrowed to, which is what every level judges.
152
+ *
153
+ * Identical to `projects` unless `--projects` or `--tags` named a
154
+ * selection, so the gate judges the whole workspace by default and a
155
+ * narrowed run is something the command line asked for explicitly.
156
+ */
157
+ readonly selectedProjects: NxProject[];
158
+ readonly workingDirectory: string;
159
+ }
160
+
161
+ /** One project whose graph could not be built, and why. */
162
+ export declare interface BoundaryCheckFailure {
163
+ readonly error: string;
164
+ readonly projectName: string;
165
+ }
166
+
167
+ /** Wires rule evaluation together with the four graph builders it judges. */
168
+ export declare class BoundaryCheckModule {
169
+ }
170
+
171
+ /**
172
+ * What one `--check boundaries` pass found.
173
+ *
174
+ * Failures are carried beside violations rather than thrown, for the same
175
+ * reason every export pass carries them: a NestJS project that cannot boot
176
+ * its container says nothing about whether the other thirty-six break a rule,
177
+ * and a run that abandoned the rest would report a smaller problem than it
178
+ * has.
179
+ */
180
+ export declare interface BoundaryCheckOutcome {
181
+ readonly failures: BoundaryCheckFailure[];
182
+ readonly violations: BoundaryViolation[];
183
+ }
184
+
185
+ /**
186
+ * Judges every level's graph against the rules declared for it.
187
+ *
188
+ * A level whose rule list is empty is never built at all, which is what makes
189
+ * the gate affordable: judging the NestJS level means booting every
190
+ * `framework:nestjs` container in preview mode, and judging the TypeScript
191
+ * level means building a `ts.Program` per project. A workspace declaring only
192
+ * Nx rules pays for neither.
193
+ *
194
+ * Nothing here writes or reads a destination. A violation goes to the console
195
+ * and the exit code, so `--check boundaries` leaves every committed export
196
+ * exactly as it found it — the property that lets it gate a branch, where the
197
+ * exports are expected to be behind the workspace they describe.
198
+ */
199
+ export declare class BoundaryCheckService {
200
+ private readonly boundariesService;
201
+ private readonly boundaryGraphService;
202
+ private readonly moduleGraphService;
203
+ private readonly nestjsProjectService;
204
+ private readonly pythonService;
205
+ private readonly typescriptService;
206
+ private readonly workspaceGraphService;
207
+ constructor(boundariesService: BoundariesService, boundaryGraphService: BoundaryGraphService, moduleGraphService: ModuleGraphService, nestjsProjectService: NestjsProjectService, pythonService: PythonService, typescriptService: TypescriptService, workspaceGraphService: WorkspaceGraphService);
208
+ /** Turns a raised error into a `BoundaryCheckFailure` for the given project. */
209
+ private collectProjectFailure;
210
+ /**
211
+ * The `CodependixGraphType` each boundary level is judged under.
212
+ *
213
+ * Finer-grained than `CodependixGraphType` itself: `python` and
214
+ * `typescript` both fall under `fileImports`, since `codependix-file-imports`
215
+ * builds and exports both as one graph type even though `boundaries`
216
+ * still nests their rules by language. `--no-file-imports` therefore skips
217
+ * both levels together.
218
+ */
219
+ private graphTypeForLevel;
220
+ /**
221
+ * Resolves one level's declared rules out of the nested boundaries shape.
222
+ *
223
+ * A record keyed by level rather than a switch, the same reason `runLevel`
224
+ * is one: every `CodependixBoundaryLevel` must have an entry, so a fifth
225
+ * level added to that union fails to compile here instead of silently
226
+ * resolving to nothing.
227
+ */
228
+ private rulesForLevel;
229
+ /**
230
+ * Judges one level, whichever of the four builders it needs.
231
+ *
232
+ * A record keyed by level rather than a switch: the record type requires
233
+ * every `CodependixBoundaryLevel` to have an entry, so a fifth level added
234
+ * to that union fails to compile here instead of silently going unchecked.
235
+ */
236
+ private runLevel;
237
+ /** Judges every `framework:nestjs` project's module graph. */
238
+ private runNestjsLevel;
239
+ /** Judges the whole-workspace Nx project graph. */
240
+ private runNxLevel;
241
+ /**
242
+ * Judges every project at one level, isolating each project's failure.
243
+ *
244
+ * The three per-project levels differ only in how a project is discovered
245
+ * and how its graph is built, so the loop around them is written once: a
246
+ * project that raises is collected as a failure and every other project is
247
+ * still judged. `buildGraph` may be asynchronous because the NestJS level's
248
+ * is — booting a container is the one graph this tool cannot build
249
+ * synchronously.
250
+ */
251
+ private runProjectLevel;
252
+ /** Judges every `language:python` project's file-level import graph. */
253
+ private runPythonImportsLevel;
254
+ /** Judges every TypeScript project's file-level import graph. */
255
+ private runTypescriptImportsLevel;
256
+ /**
257
+ * Judges every level that has a rule to judge it by and whose graph type
258
+ * this run enabled.
259
+ *
260
+ * The four levels are independent, so a NestJS project failing to boot has
261
+ * no bearing on whether the Nx or import graphs break a rule, and every
262
+ * level is attempted regardless of what an earlier one reported. A level
263
+ * declaring no rule is skipped before anything is built, which is what
264
+ * keeps the gate affordable — and a level whose graph type
265
+ * `context.enabledGraphTypes` excludes is skipped the same way, so
266
+ * `--no-nestjs-modules` never boots a single container to judge it.
267
+ */
268
+ run(context: BoundaryCheckContext): Promise<BoundaryCheckOutcome>;
269
+ }
270
+
271
+ /**
272
+ * One cycle found in a graph.
273
+ *
274
+ * `source` and `target` are the edge that closed it — the last node walked,
275
+ * and the node already on the path it returned to — so a cycle reports the
276
+ * same two endpoints every other violation does, without a caller having to
277
+ * index into `path` and prove to the compiler that it is non-empty.
278
+ */
279
+ export declare interface BoundaryCycle {
280
+ /** The whole path, with the closing node repeated at the end. */
281
+ readonly path: readonly string[];
282
+ readonly source: string;
283
+ readonly target: string;
284
+ }
285
+
286
+ /**
287
+ * Finds the cycles in a graph, within a selected set of nodes.
288
+ *
289
+ * A cycle is the one finding no rule reading a single file can make, which is
290
+ * why it is a rule kind of its own rather than an access rule with a clever
291
+ * selector. Scoped to a node set rather than always the whole graph: a rule
292
+ * covering one directory should not fail because of a cycle running entirely
293
+ * through code it was never asked about.
294
+ *
295
+ * One cycle is reported per distinct set of nodes, keyed on the cycle rotated
296
+ * to start at its smallest node — so a three-node cycle is one finding rather
297
+ * than the same finding printed once per node it passes through.
298
+ */
299
+ export declare class BoundaryCyclesService {
300
+ constructor();
301
+ /** Builds the outgoing-edge map, keeping only edges inside the node set. */
302
+ private buildAdjacency;
303
+ /**
304
+ * The key every cycle over the same nodes shares.
305
+ *
306
+ * The node set rather than the path: `a → b → a` and `b → a → b` are one
307
+ * finding written two ways, and so are the two directions round a
308
+ * three-node cycle. Reporting the first path found through a set of nodes
309
+ * and nothing else is what keeps one tangle from printing as six findings.
310
+ */
311
+ private buildCycleKey;
312
+ /** Records the cycle closed by walking back to a node already on the path. */
313
+ private recordCycle;
314
+ /** Walks one node's subtree depth-first, recording every cycle it closes. */
315
+ private walk;
316
+ /**
317
+ * Every cycle lying entirely inside the given node set.
318
+ *
319
+ * Nodes are walked in sorted order and each cycle is reported starting from
320
+ * the node the walk reached first, so the same graph always produces the
321
+ * same findings in the same order — a report that reshuffles itself between
322
+ * runs is one nobody can diff.
323
+ */
324
+ findCycles(args: FindCyclesArguments): BoundaryCycle[];
325
+ }
326
+
327
+ /**
328
+ * One node depending on another, at whatever level the graph is.
329
+ *
330
+ * `implicit` is carried rather than dropped because it is the one fact a lint
331
+ * rule reading import statements cannot reach: an Nx `implicitDependencies`
332
+ * entry creates a project-graph edge with no import statement to flag. Absent
333
+ * at every level that has no such notion, rather than defaulted to `false` —
334
+ * a file import is not "explicitly" anything, it is just an import.
335
+ */
336
+ export declare interface BoundaryEdge {
337
+ readonly implicit?: boolean | undefined;
338
+ readonly source: string;
339
+ readonly target: string;
340
+ }
341
+
342
+ /**
343
+ * One built graph, reduced to what rule evaluation needs.
344
+ *
345
+ * Deliberately not `Neighborhood`, `NestjsModuleGraph`, `TypescriptImportGraph`
346
+ * or `PythonImportGraph`: reading any of those would drag `@nx/devkit`,
347
+ * `@nestjs/core` and `typescript` behind anything that wants only rule
348
+ * evaluation. The four already share an identical `{ source, target }` edge
349
+ * shape by construction, so the adapters that flatten them into this live in
350
+ * the host that already builds all four — see `codependix-cli`.
351
+ */
352
+ export declare interface BoundaryGraph {
353
+ readonly edges: readonly BoundaryEdge[];
354
+ /** Which of codependix's four levels this graph was built at. */
355
+ readonly level: CodependixBoundaryLevel;
356
+ readonly nodes: readonly BoundaryNode[];
357
+ /**
358
+ * What the graph covers: an Nx project name, or the workspace itself.
359
+ *
360
+ * Reported beside every violation, because the same rule evaluated at file
361
+ * level fails once per project and a bare pair of file paths does not say
362
+ * which project's files they are.
363
+ */
364
+ readonly scope: string;
365
+ }
366
+
367
+ /**
368
+ * Flattens each of codependix's four graphs into the one shape rules read.
369
+ *
370
+ * The adapters live here, in the host that already builds all four, rather
371
+ * than in `@codependix/boundaries` — which would otherwise have to depend on
372
+ * `@nestjs/core` and `typescript` to name the types it translates, for a
373
+ * package whose whole job is evaluating rules. The four
374
+ * already share an identical `{ source, target }` edge shape by construction,
375
+ * so each adapter is only ever nodes, edges, and the attributes rules select
376
+ * on.
377
+ *
378
+ * What each level knows differs, and the node shape says so: an Nx project
379
+ * carries tags and a root, a file carries its project-relative path and the
380
+ * project it belongs to, and a NestJS module carries its class name, declaring
381
+ * file path, and project name.
382
+ */
383
+ export declare class BoundaryGraphService {
384
+ constructor();
385
+ /** Builds one file-level graph's nodes, which both languages share. */
386
+ private buildFileNodes;
387
+ /**
388
+ * A project's workspace-relative root, or nothing when the graph knows of a
389
+ * project the discovered list does not carry.
390
+ */
391
+ private resolveProjectRoot;
392
+ /**
393
+ * Adapts one project's NestJS module graph.
394
+ *
395
+ * Nodes carry a name, their project-relative declaring file path, and the
396
+ * project name they belong to.
397
+ */
398
+ buildNestjsGraph(graph: NestjsModuleGraph): BoundaryGraph;
399
+ /**
400
+ * Adapts the whole-workspace Nx project graph.
401
+ *
402
+ * The workspace graph rather than each project's one-hop neighborhood: a
403
+ * rule is a statement about the shape of the graph, and a neighborhood is
404
+ * the same graph shown twice from either end of every edge.
405
+ *
406
+ * Tags come off the `NxProject`s themselves, because they are what makes
407
+ * this level worth having: a tag rule reaches nothing unless the nodes it
408
+ * judges carry them.
409
+ */
410
+ buildNxGraph(args: {
411
+ projects: readonly NxProject[];
412
+ scope: string;
413
+ workingDirectory: string;
414
+ workspaceGraph: WorkspaceGraph;
415
+ }): BoundaryGraph;
416
+ /** Adapts one project's Python file-level import graph. */
417
+ buildPythonImportGraph(graph: PythonImportGraph): BoundaryGraph;
418
+ /** Adapts one project's TypeScript file-level import graph. */
419
+ buildTypescriptImportGraph(graph: TypescriptImportGraph): BoundaryGraph;
420
+ }
421
+
422
+ /**
423
+ * One node in a graph, with whatever a level knows about it.
424
+ *
425
+ * One shape for three vocabularies: an Nx project has a name, a root, and
426
+ * tags; a file has a project-relative path and project; a NestJS module has a
427
+ * class name, declaring file path, and project. Every field beyond `id` is
428
+ * therefore optional, and a selector naming one a level does not carry
429
+ * matches nothing there rather than everything — see `BoundarySelectorService`.
430
+ *
431
+ * No `kind` field: the graph already states its `level`, and a second field
432
+ * saying the same thing is a second thing that can be wrong.
433
+ */
434
+ export declare interface BoundaryNode {
435
+ readonly id: string;
436
+ readonly path?: string | undefined;
437
+ readonly project?: string | undefined;
438
+ readonly tags?: readonly string[] | undefined;
439
+ }
440
+
441
+ /**
442
+ * Renders violations into the lines a run prints.
443
+ *
444
+ * Rendering lives here rather than in the host for the same reason each graph
445
+ * package renders its own mermaid: the package that knows what a finding means
446
+ * is the one that should decide how it reads. There is no configured
447
+ * destination and no file — a violation report is a list of things currently
448
+ * wrong, which is not a document worth regenerating on the default branch or
449
+ * checking for staleness.
450
+ */
451
+ export declare class BoundaryReportService {
452
+ constructor();
453
+ /**
454
+ * One line summarizing what a run found.
455
+ *
456
+ * Counts rules as well as violations, because the two answer different
457
+ * questions: one broken rule reporting forty edges is a single decision to
458
+ * revisit, and forty rules reporting one edge each is not.
459
+ */
460
+ renderSummary(violations: readonly BoundaryViolation[]): string;
461
+ /**
462
+ * One line per violation, each naming its level and scope before the rule's
463
+ * own sentence.
464
+ *
465
+ * The level and scope lead because the message cannot carry them: the same
466
+ * rule evaluated at file level fails once per project, and a bare pair of
467
+ * file paths does not say whose files they are.
468
+ */
469
+ renderViolations(violations: readonly BoundaryViolation[]): string[];
470
+ }
471
+
472
+ /**
473
+ * Decides which nodes a rule's selector claims.
474
+ *
475
+ * Split out from `BoundariesService` because it is the one part of rule
476
+ * evaluation with no opinion about rules at all: a selector and a node go in,
477
+ * and a yes or no comes out. Both rule kinds ask it the same question, and so
478
+ * does anything that later wants to know what a rule covers without running
479
+ * it.
480
+ */
481
+ export declare class BoundarySelectorService {
482
+ constructor();
483
+ /**
484
+ * Whether one of a node's string attributes matches a list of globs.
485
+ *
486
+ * A selector naming an attribute the node's level does not carry matches
487
+ * nothing rather than everything — a `path` rule evaluated against a NestJS
488
+ * module graph, which carries no file paths, selects no module instead of
489
+ * silently selecting all of them.
490
+ */
491
+ private matchesGlobs;
492
+ /**
493
+ * Whether a node carries a tag matching one of a list of globs.
494
+ *
495
+ * One tag matching is enough, which is what makes `tags: ["type:package"]`
496
+ * read the way an Nx tag list does — a project carries several, and a rule
497
+ * naming one is asking whether that one is among them.
498
+ */
499
+ private matchesTags;
500
+ /**
501
+ * Whether a selector claims a node.
502
+ *
503
+ * Every field the selector states must match — the fields narrow each
504
+ * other, so `{ project: ["lexico"], path: ["src/**"] }` means a file in
505
+ * lexico's `src` rather than either of those. Within one field, one glob
506
+ * matching is enough.
507
+ */
508
+ matches(node: BoundaryNode, selector: CodependixBoundarySelector): boolean;
509
+ /**
510
+ * The ids of every node a selector claims.
511
+ *
512
+ * A selector of `undefined` claims every node, which is what an `acyclic`
513
+ * rule naming no scope means. An empty selector is a different thing and
514
+ * never reaches here: the configuration schema refuses one outright, since
515
+ * a selector stating nothing reads exactly like a typo.
516
+ */
517
+ selectIds(nodes: readonly BoundaryNode[], selector: CodependixBoundarySelector | undefined): Set<string>;
518
+ }
519
+
520
+ /** One edge, or one cycle, breaking one declared rule. */
521
+ export declare interface BoundaryViolation {
522
+ /**
523
+ * The whole cycle, closing node repeated at the end, for an `acyclic` rule.
524
+ *
525
+ * Absent for an access rule, which is about one edge and has no path to
526
+ * report.
527
+ */
528
+ readonly cycle: readonly string[] | undefined;
529
+ readonly level: CodependixBoundaryLevel;
530
+ /** The sentence reported, whether the rule's own or the generated one. */
531
+ readonly message: string;
532
+ /** The `name` of the rule that reported it. */
533
+ readonly rule: string;
534
+ readonly scope: string;
535
+ readonly source: string;
536
+ readonly target: string;
537
+ }
538
+
539
+ /**
540
+ * The level a boundary graph was built at, or a violation was found at.
541
+ *
542
+ * Finer-grained than `CodependixGraphType`: `fileImports` builds one merged
543
+ * graph type per project, but `boundaries.fileImports` still nests rules by
544
+ * language, and a violation has to say which language's graph it came from.
545
+ * `nestjsModules` and `nxProjects` match `CodependixGraphType` one for one,
546
+ * since those two levels carry no such split.
547
+ */
548
+ declare type CodependixBoundaryLevel = "nestjsModules" | "nxProjects" | "python" | "typescript";
549
+
550
+ /** How a cycle's nodes are joined when one is reported. */
551
+ export declare const CYCLE_SEPARATOR = " \u2192 ";
552
+
553
+ /**
554
+ * The sentence an `acyclic` rule reports when it names none of its own.
555
+ *
556
+ * Names the whole path rather than only the edge that closed it: a cycle is a
557
+ * statement about a shape, and the two nodes of its closing edge are the
558
+ * least useful two to be handed.
559
+ */
560
+ export declare const describeCycle: (args: {
561
+ cycle: readonly string[];
562
+ rule: string;
563
+ }) => string;
564
+
565
+ /**
566
+ * The sentence a violated `allow` rule reports when it names none of its own.
567
+ *
568
+ * Worded as a permitted surface rather than as a forbidden edge, because that
569
+ * is what distinguishes it from `forbid`: the reader's next question is what
570
+ * the rule does allow, and the rule's name is the only place this can point
571
+ * them at without restating the configuration.
572
+ */
573
+ export declare const describeDisallowedEdge: (args: {
574
+ rule: string;
575
+ source: string;
576
+ target: string;
577
+ }) => string;
578
+
579
+ /** The sentence a violated `forbid` rule reports when it names none of its own. */
580
+ export declare const describeForbiddenEdge: (args: {
581
+ rule: string;
582
+ source: string;
583
+ target: string;
584
+ }) => string;
585
+
586
+ /** Arguments accepted when judging one graph against one level's rules. */
587
+ export declare interface EvaluateBoundariesArguments {
588
+ readonly graph: BoundaryGraph;
589
+ readonly rules: readonly CodependixBoundaryRule[];
590
+ }
591
+
592
+ /** Arguments accepted when finding the cycles in a graph. */
593
+ export declare interface FindCyclesArguments {
594
+ readonly edges: readonly BoundaryEdge[];
595
+ /** The nodes a cycle must lie entirely within. */
596
+ readonly nodeIds: ReadonlySet<string>;
597
+ }
598
+
599
+ /**
600
+ * Everything every graph-type pass reads, resolved once per run rather than
601
+ * once per pass — see `GraphRunService.run`.
602
+ *
603
+ * Stated here, beside `BoundaryCheckContext`, because resolving it is the
604
+ * first thing every run does and the boundary gate is one of its readers:
605
+ * the wider context carries an export mode the gate never looks at, and is
606
+ * structurally assignable to `BoundaryCheckContext`, so nothing is repacked
607
+ * at the call site.
608
+ */
609
+ export declare interface GraphRunContext {
610
+ configuration: ResolvedCodependixConfiguration;
611
+ /**
612
+ * The graph types this run builds, checks, and writes.
613
+ *
614
+ * All three unless `--no-file-imports`, `--no-nestjs-modules`, or
615
+ * `--no-nx-projects` narrowed it — see `RunContextService.build`. Read by
616
+ * `GraphRunService.run` to skip a whole pass, and by
617
+ * `BoundaryCheckService.run` to skip a boundary level, so a developer can
618
+ * run a narrower, faster check locally without editing configuration.
619
+ */
620
+ enabledGraphTypes: ReadonlySet<CodependixGraphType>;
621
+ graph: NxProjectGraph;
622
+ mode: CodependixRunMode;
623
+ /**
624
+ * Every project's own `codependix.config.ts`, keyed by project name — or
625
+ * `undefined` for a project naming none of its own.
626
+ *
627
+ * Loaded once per run, alongside `projects`, so every pass reads the same
628
+ * snapshot rather than each re-reading the filesystem for every graph type
629
+ * it resolves. Read as-is by `ConfigurationService.resolveForProject` — see
630
+ * that method for why no further merge happens here.
631
+ */
632
+ projectConfigurations: Map<string, CodependixProjectConfiguration | undefined>;
633
+ /** Every project the graph knows, apart from the workspace root. */
634
+ projects: NxProject[];
635
+ /**
636
+ * The projects `--projects` and `--tags` narrowed the run to.
637
+ *
638
+ * Identical to `projects` when a run named neither, which is what keeps the
639
+ * Workspace Graph whole and the boundary gate judging every project by
640
+ * default. `include`/`exclude` never reach this: they decide which projects
641
+ * have exports written, not which projects a graph is drawn over.
642
+ */
643
+ selectedProjects: NxProject[];
644
+ workingDirectory: string;
645
+ }
646
+
647
+ /** Arguments accepted when judging one graph level against its rules. */
648
+ export declare interface LevelCheckArguments {
649
+ readonly context: BoundaryCheckContext;
650
+ readonly level: CodependixBoundaryLevel;
651
+ readonly rules: readonly CodependixBoundaryRule[];
652
+ }
653
+
654
+ /**
655
+ * Wires run-context resolution to the configuration and project graph it
656
+ * reads, which are the only two things a run has to resolve before any pass
657
+ * can run.
658
+ */
659
+ export declare class RunContextModule {
660
+ }
661
+
662
+ /**
663
+ * Resolves everything one run reads, once, before any pass runs.
664
+ *
665
+ * Kept apart from the passes that read it because the two answer different
666
+ * questions: what this workspace is, versus what to do about it. Every pass —
667
+ * the four export passes, the Workspace Graph, and the boundary gate — is
668
+ * handed the same resolved context rather than resolving its own, so a run
669
+ * reads the project graph once and judges one workspace.
670
+ */
671
+ export declare class RunContextService {
672
+ private readonly configurationService;
673
+ private readonly neighborhoodService;
674
+ constructor(configurationService: ConfigurationService, neighborhoodService: NeighborhoodService);
675
+ /**
676
+ * Loads every project's own `codependix.config.ts`, keyed by project name.
677
+ *
678
+ * Loaded once per run, up front, rather than once per graph-type pass: a
679
+ * project with no file of its own is a normal, expected outcome rather than
680
+ * a failure, so every project is attempted and the map simply holds
681
+ * `undefined` for the ones naming none.
682
+ */
683
+ private loadProjectConfigurations;
684
+ /**
685
+ * Reads the three graph-type toggle flags into the set of graph types this
686
+ * run builds, checks, and writes.
687
+ *
688
+ * A graph type is enabled unless its own `--no-*` flag disabled it —
689
+ * `--file-imports`/`--nestjs-modules`/`--nx-projects` exist only for
690
+ * symmetry with the negated form, matching every graph type's default of
691
+ * "on" when neither flag was given.
692
+ */
693
+ private resolveEnabledGraphTypes;
694
+ /**
695
+ * Resolves a supplied project graph's path against the workspace root.
696
+ *
697
+ * The same root every export path resolves against — `--directory` keeps
698
+ * its one meaning, and a supplied graph's workspace-relative node roots
699
+ * resolve underneath it too.
700
+ */
701
+ private resolveProjectGraphPath;
702
+ /**
703
+ * Narrows every project to the set `--projects` and `--tags` named.
704
+ *
705
+ * A run naming neither selects every project, which is what keeps the
706
+ * Workspace Graph whole and the boundary gate judging the whole workspace by
707
+ * default. `include`/`exclude` deliberately do not reach this: they decide
708
+ * which projects have exports written for them, not which projects a graph
709
+ * is drawn over.
710
+ */
711
+ private selectProjects;
712
+ /**
713
+ * Reads the configuration and the project graph a run is about to act on.
714
+ *
715
+ * Called once per run, by the command — every pass takes the context it
716
+ * returns. `--check boundaries` reads exactly the same one, which is what
717
+ * lets a single run both export and gate without reading the workspace
718
+ * twice.
719
+ */
720
+ build(args: {
721
+ mode: CodependixRunMode;
722
+ options: MapCommandOptions;
723
+ workingDirectory: string;
724
+ }): Promise<GraphRunContext>;
725
+ }
726
+
727
+ /**
728
+ * The scope a violation found in the whole-workspace Nx graph is reported
729
+ * under.
730
+ *
731
+ * The Nx level is judged once for the repository rather than once per
732
+ * project, so it has no project name to report against.
733
+ */
734
+ export declare const WORKSPACE_SCOPE = "workspace";
735
+
736
+ export { }