@untangle-code/file-graph 0.0.0-stage → 0.1.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.
Files changed (130) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +284 -2
  4. package/THIRD_PARTY_LICENSES.md +29 -0
  5. package/dist/assignment.d.ts +15 -0
  6. package/dist/assignment.d.ts.map +1 -0
  7. package/dist/assignment.js +75 -0
  8. package/dist/assignment.js.map +1 -0
  9. package/dist/boundaries/endpoints.d.ts +11 -0
  10. package/dist/boundaries/endpoints.d.ts.map +1 -0
  11. package/dist/boundaries/endpoints.js +195 -0
  12. package/dist/boundaries/endpoints.js.map +1 -0
  13. package/dist/boundaries/index.d.ts +9 -0
  14. package/dist/boundaries/index.d.ts.map +1 -0
  15. package/dist/boundaries/index.js +93 -0
  16. package/dist/boundaries/index.js.map +1 -0
  17. package/dist/boundaries/requests.d.ts +12 -0
  18. package/dist/boundaries/requests.d.ts.map +1 -0
  19. package/dist/boundaries/requests.js +311 -0
  20. package/dist/boundaries/requests.js.map +1 -0
  21. package/dist/edges.d.ts +49 -0
  22. package/dist/edges.d.ts.map +1 -0
  23. package/dist/edges.js +223 -0
  24. package/dist/edges.js.map +1 -0
  25. package/dist/file-index.d.ts +37 -0
  26. package/dist/file-index.d.ts.map +1 -0
  27. package/dist/file-index.js +103 -0
  28. package/dist/file-index.js.map +1 -0
  29. package/dist/graph.d.ts +160 -0
  30. package/dist/graph.d.ts.map +1 -0
  31. package/dist/graph.js +448 -0
  32. package/dist/graph.js.map +1 -0
  33. package/dist/index.d.ts +35 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +48 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/metrics.d.ts +67 -0
  38. package/dist/metrics.d.ts.map +1 -0
  39. package/dist/metrics.js +272 -0
  40. package/dist/metrics.js.map +1 -0
  41. package/dist/package-catalog.d.ts +43 -0
  42. package/dist/package-catalog.d.ts.map +1 -0
  43. package/dist/package-catalog.js +136 -0
  44. package/dist/package-catalog.js.map +1 -0
  45. package/dist/paths.d.ts +56 -0
  46. package/dist/paths.d.ts.map +1 -0
  47. package/dist/paths.js +129 -0
  48. package/dist/paths.js.map +1 -0
  49. package/dist/persistence.d.ts +83 -0
  50. package/dist/persistence.d.ts.map +1 -0
  51. package/dist/persistence.js +538 -0
  52. package/dist/persistence.js.map +1 -0
  53. package/dist/resolve/candidates.d.ts +52 -0
  54. package/dist/resolve/candidates.d.ts.map +1 -0
  55. package/dist/resolve/candidates.js +202 -0
  56. package/dist/resolve/candidates.js.map +1 -0
  57. package/dist/resolve/package-exports.d.ts +31 -0
  58. package/dist/resolve/package-exports.d.ts.map +1 -0
  59. package/dist/resolve/package-exports.js +229 -0
  60. package/dist/resolve/package-exports.js.map +1 -0
  61. package/dist/resolve/pipeline.d.ts +11 -0
  62. package/dist/resolve/pipeline.d.ts.map +1 -0
  63. package/dist/resolve/pipeline.js +109 -0
  64. package/dist/resolve/pipeline.js.map +1 -0
  65. package/dist/resolve/resolvers.d.ts +25 -0
  66. package/dist/resolve/resolvers.d.ts.map +1 -0
  67. package/dist/resolve/resolvers.js +367 -0
  68. package/dist/resolve/resolvers.js.map +1 -0
  69. package/dist/restore.d.ts +75 -0
  70. package/dist/restore.d.ts.map +1 -0
  71. package/dist/restore.js +136 -0
  72. package/dist/restore.js.map +1 -0
  73. package/dist/topology/compose.d.ts +27 -0
  74. package/dist/topology/compose.d.ts.map +1 -0
  75. package/dist/topology/compose.js +208 -0
  76. package/dist/topology/compose.js.map +1 -0
  77. package/dist/topology/jsonc.d.ts +38 -0
  78. package/dist/topology/jsonc.d.ts.map +1 -0
  79. package/dist/topology/jsonc.js +137 -0
  80. package/dist/topology/jsonc.js.map +1 -0
  81. package/dist/topology/load.d.ts +61 -0
  82. package/dist/topology/load.d.ts.map +1 -0
  83. package/dist/topology/load.js +436 -0
  84. package/dist/topology/load.js.map +1 -0
  85. package/dist/topology/manifest.d.ts +48 -0
  86. package/dist/topology/manifest.d.ts.map +1 -0
  87. package/dist/topology/manifest.js +109 -0
  88. package/dist/topology/manifest.js.map +1 -0
  89. package/dist/topology/project-config.d.ts +39 -0
  90. package/dist/topology/project-config.d.ts.map +1 -0
  91. package/dist/topology/project-config.js +150 -0
  92. package/dist/topology/project-config.js.map +1 -0
  93. package/dist/topology/workspace-definition.d.ts +26 -0
  94. package/dist/topology/workspace-definition.d.ts.map +1 -0
  95. package/dist/topology/workspace-definition.js +104 -0
  96. package/dist/topology/workspace-definition.js.map +1 -0
  97. package/dist/traverse.d.ts +49 -0
  98. package/dist/traverse.d.ts.map +1 -0
  99. package/dist/traverse.js +167 -0
  100. package/dist/traverse.js.map +1 -0
  101. package/dist/types.d.ts +332 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +2 -0
  104. package/dist/types.js.map +1 -0
  105. package/package.json +63 -3
  106. package/src/assignment.ts +83 -0
  107. package/src/boundaries/endpoints.ts +273 -0
  108. package/src/boundaries/index.ts +118 -0
  109. package/src/boundaries/requests.ts +411 -0
  110. package/src/edges.ts +334 -0
  111. package/src/file-index.ts +127 -0
  112. package/src/graph.ts +591 -0
  113. package/src/index.ts +58 -0
  114. package/src/metrics.ts +350 -0
  115. package/src/package-catalog.ts +145 -0
  116. package/src/paths.ts +138 -0
  117. package/src/persistence.ts +746 -0
  118. package/src/resolve/candidates.ts +230 -0
  119. package/src/resolve/package-exports.ts +268 -0
  120. package/src/resolve/pipeline.ts +140 -0
  121. package/src/resolve/resolvers.ts +468 -0
  122. package/src/restore.ts +201 -0
  123. package/src/topology/compose.ts +268 -0
  124. package/src/topology/jsonc.ts +147 -0
  125. package/src/topology/load.ts +644 -0
  126. package/src/topology/manifest.ts +132 -0
  127. package/src/topology/project-config.ts +183 -0
  128. package/src/topology/workspace-definition.ts +115 -0
  129. package/src/traverse.ts +232 -0
  130. package/src/types.ts +372 -0
package/src/types.ts ADDED
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Paketeigene Vertraege des Dateigraphen.
3
+ *
4
+ * ABGRENZUNG ZU `@untangle-code/domain` (bewusste Entscheidung, siehe
5
+ * README „Entscheidungen"): Alles, was ein anderes Paket LESEN muss —
6
+ * `FileNodeV1`, `FileEdgeV1`, `FileGraphMetricsV1`, `SkipLedgerV1`,
7
+ * `TraversalLimitsV1` — kommt aus `domain` und wird hier NICHT
8
+ * neu definiert. Was hier steht, sind ausschliesslich die EINGABEN des
9
+ * Graphbaus und die Zwischenergebnisse der Aufloesung; die kennt
10
+ * niemand ausser diesem Paket.
11
+ *
12
+ * Einzige Ausnahme ist `FileTraversalResultV1`: `TraversalResultV1` aus
13
+ * `domain` traegt Feldnamen auf `*SymbolIds`, die fuer Dateien schlicht
14
+ * falsch waeren. Die verbindlichen Teile (`TraversalLimitsV1`,
15
+ * `TraversalDirection`, `TraversalTruncationReason`) werden uebernommen,
16
+ * nur die Feldnamen sind dateibezogen — und `cycles` kommt hinzu, weil
17
+ * ein Zyklus gemeldet und nicht stillschweigend aufgebrochen wird.
18
+ */
19
+ import type {
20
+ FileEdgeMechanism,
21
+ FileEdgeUnresolvedReason,
22
+ FileEdgeV1,
23
+ FileGraphMetricsV1,
24
+ FileNodeV1,
25
+ RawImport,
26
+ SkipLedgerV1,
27
+ TraversalDirection,
28
+ TraversalLimitsV1,
29
+ TraversalTruncationReason,
30
+ } from "@untangle-code/domain";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Eingaben
34
+ // ---------------------------------------------------------------------------
35
+
36
+ /**
37
+ * Eine Datei mit ihren Rohimporten — die Eingabe des Graphbaus.
38
+ *
39
+ * Der Dateigraph parst NICHT selbst. Er bekommt, was
40
+ * `LanguageAdapter.extractImports` geliefert hat, und ist damit gegen jeden
41
+ * Parser austauschbar (architecture.md §16.1 Schritt 2).
42
+ */
43
+ export interface FileImportsV1 {
44
+ fileId: string;
45
+ repoId: string;
46
+ refId: string;
47
+ /** `"/"`-normalisierter, repo-relativer Pfad. */
48
+ path: string;
49
+ languageId: string;
50
+ contentHash: string;
51
+ imports: readonly RawImport[];
52
+ /**
53
+ * Grund, warum `imports` leer ist, obwohl die Datei Importe haben koennte
54
+ * (Parse fehlgeschlagen, Sprache ohne Adapter, Budget erschoepft). Ohne
55
+ * diesen Grund gilt eine leere Liste als „diese Datei hat keine Importe";
56
+ * MIT ihm entsteht ein Ledger-Eintrag (E4).
57
+ */
58
+ importsMissingReason?: string;
59
+ }
60
+
61
+ /** Ein Repository des Workspace. */
62
+ export interface RepositoryInputV1 {
63
+ repoId: string;
64
+ refId: string;
65
+ /**
66
+ * Wurzel des Repositories relativ zur Workspace-Wurzel; `""` fuer ein
67
+ * Einzelrepo-Workspace. Nur fuer die Zuordnung von Manifesten noetig.
68
+ */
69
+ rootPath: string;
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Monorepo-Topologie (Nutzerwunsch A3)
74
+ // ---------------------------------------------------------------------------
75
+
76
+ /** Ein Paket des Workspace, aus einem Manifest gelesen. */
77
+ export interface WorkspacePackageV1 {
78
+ /** Paketname laut Manifest. */
79
+ name: string;
80
+ version?: string;
81
+ repoId: string;
82
+ /** repo-relatives Verzeichnis des Pakets; `""` fuer die Repositorywurzel. */
83
+ packageDir: string;
84
+ /** repo-relativer Pfad des Manifests. */
85
+ manifestPath: string;
86
+ /** `exports`-Feld des Manifests, unveraendert. */
87
+ exports?: unknown;
88
+ main?: string;
89
+ module?: string;
90
+ types?: string;
91
+ /** Subpath-Imports (`imports`-Feld, `#`-Spezifizierer). */
92
+ imports?: unknown;
93
+ /**
94
+ * Namen anderer Workspace-Pakete, auf die dieses Paket per
95
+ * `workspace:`-Protokoll oder per Namensgleichheit zeigt.
96
+ */
97
+ workspaceDependencies: string[];
98
+ /** Name → Versionsangabe, fuer Abhaengigkeiten ausserhalb des Workspace. */
99
+ externalDependencies: Record<string, string>;
100
+ /**
101
+ * `outDir`/`rootDir` aus der Projektkonfiguration des Pakets, sofern
102
+ * vorhanden. Grundlage der Rueckabbildung Build-Ausgabe → Quelldatei
103
+ * (siehe `resolve/candidates.ts`).
104
+ */
105
+ outDir?: string;
106
+ rootDir?: string;
107
+ }
108
+
109
+ /**
110
+ * Nachschlagbarer Paketkatalog des Workspace.
111
+ *
112
+ * BEWUSST KEINE `Map<name, package>`: Paketnamen sind ueber Repositorygrenzen
113
+ * hinweg NICHT eindeutig. Eine namensgeschluesselte Karte laesst gleichnamige
114
+ * Pakete einander ersatzlos verdecken — mit falschen Abhaengigkeitsdaten und
115
+ * Importen, die als „aufgeloest" ins falsche Repository zeigen. Jede Abfrage
116
+ * geht deshalb von einem fragenden Repository aus (`WorkspacePackageCatalog`
117
+ * in `package-catalog.ts` setzt das um).
118
+ */
119
+ export interface WorkspacePackageCatalogV1 {
120
+ /** Alle Pakete in kanonischer Ordnung (Name, Repository, Verzeichnis). */
121
+ all(): readonly WorkspacePackageV1[];
122
+ /** Alle Pakete dieses Namens, ueber alle Repositories. */
123
+ byPackageName(name: string): readonly WorkspacePackageV1[];
124
+ /**
125
+ * Das Paket dieses Namens im angegebenen Repository — nur wenn es dort
126
+ * GENAU EINES gibt. Zwei gleichnamige Manifeste desselben Repositories
127
+ * liefern `null`, nicht das erste davon.
128
+ */
129
+ inRepo(repoId: string, name: string): WorkspacePackageV1 | null;
130
+ /** Alle Pakete dieses Namens in diesem Repository, kanonisch geordnet. */
131
+ inRepoAll(repoId: string, name: string): readonly WorkspacePackageV1[];
132
+ /** True, wenn IRGENDEIN Repository ein Paket dieses Namens fuehrt. */
133
+ has(name: string): boolean;
134
+ /**
135
+ * Sucht `name` aus der Sicht von `fromRepoId`: eigenes Repository zuerst,
136
+ * dann ein eindeutiges fremdes. Mehrdeutig — und damit `ambiguous` statt
137
+ * geraten — ist sowohl der Fall mehrerer FREMDER Repositories als auch der
138
+ * mehrerer gleichnamiger Manifeste im EIGENEN.
139
+ */
140
+ lookup(fromRepoId: string, name: string): PackageLookupV1;
141
+ /** Namen, die von mehr als einem Manifest deklariert werden. */
142
+ ambiguousNames(): readonly string[];
143
+ }
144
+
145
+ /** Ergebnis einer Katalogabfrage aus der Sicht eines Repositories. */
146
+ export type PackageLookupV1 =
147
+ | { status: "found"; entry: WorkspacePackageV1; crossRepo: boolean }
148
+ | { status: "absent" }
149
+ | { status: "ambiguous"; candidates: readonly WorkspacePackageV1[] };
150
+
151
+ /** Eine Kante der Paketebene (groebere Ebene als der Dateigraph). */
152
+ export interface WorkspacePackageEdgeV1 {
153
+ fromPackage: string;
154
+ toPackage: string;
155
+ /** `workspace-dependency`: per `workspace:`-Protokoll oder Katalogtreffer. */
156
+ kind: "workspace-dependency";
157
+ /** In welchem Abhaengigkeitsblock des Manifests die Kante stand. */
158
+ dependencyField: "dependencies" | "devDependencies" | "peerDependencies" | "optionalDependencies";
159
+ /** Versionsangabe wie im Manifest, z. B. `"workspace:*"`. */
160
+ rawRange: string;
161
+ }
162
+
163
+ /** Ein Dienst aus einer Container-Kompositionsdatei. */
164
+ export interface ContainerServiceV1 {
165
+ name: string;
166
+ repoId: string;
167
+ /** repo-relativer Pfad der Kompositionsdatei. */
168
+ composeFile: string;
169
+ /** repo-relativer Build-Kontext, sofern angegeben. */
170
+ buildContext?: string;
171
+ image?: string;
172
+ dependsOn: string[];
173
+ networks: string[];
174
+ }
175
+
176
+ /** Eine Kante der Dienstebene. */
177
+ export interface ContainerServiceEdgeV1 {
178
+ fromService: string;
179
+ toService: string;
180
+ /**
181
+ * `depends_on` ist eine gerichtete Startreihenfolge, `shared-network` eine
182
+ * ungerichtete Erreichbarkeit — beide werden getrennt gefuehrt, weil sie
183
+ * verschiedene Fragen beantworten („wer braucht wen zuerst" gegen „wer
184
+ * kann wen ueberhaupt erreichen").
185
+ */
186
+ kind: "depends_on" | "shared-network";
187
+ /** Gesetzt bei `shared-network`. */
188
+ network?: string;
189
+ condition?: string;
190
+ }
191
+
192
+ /** Die grobkoernige Sicht auf ein Monorepo: Pakete und Dienste samt Kanten. */
193
+ export interface WorkspaceTopologyV1 {
194
+ schemaVersion: 1;
195
+ workspaceId: string;
196
+ /** Globmuster der Workspace-Definition (z. B. `packages/*`). */
197
+ workspaceGlobs: string[];
198
+ packages: WorkspacePackageV1[];
199
+ packageEdges: WorkspacePackageEdgeV1[];
200
+ services: ContainerServiceV1[];
201
+ serviceEdges: ContainerServiceEdgeV1[];
202
+ /** Alles, was beim Laden uebersprungen wurde, mit Grund (E4). */
203
+ skipped: SkipLedgerV1;
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Aufloesung
208
+ // ---------------------------------------------------------------------------
209
+
210
+ /** Ein Treffer der Aufloesung: Ziel INNERHALB des Workspace. */
211
+ export interface ResolvedInternalV1 {
212
+ status: "resolved";
213
+ mechanism: FileEdgeMechanism;
214
+ targetRepoId: string;
215
+ targetPath: string;
216
+ viaPackageName?: string;
217
+ }
218
+
219
+ /** Ein Treffer der Aufloesung: Ziel AUSSERHALB des Workspace. */
220
+ export interface ResolvedExternalV1 {
221
+ status: "external";
222
+ mechanism: FileEdgeMechanism;
223
+ packageName: string;
224
+ declaredVersion?: string;
225
+ }
226
+
227
+ /** Ein benannter Fehlschlag der Aufloesung (E4). */
228
+ export interface ResolvedUnresolvedV1 {
229
+ status: "unresolved";
230
+ reason: FileEdgeUnresolvedReason;
231
+ detail: string;
232
+ rejectedCandidates?: string[];
233
+ }
234
+
235
+ export type ResolveResultV1 = ResolvedInternalV1 | ResolvedExternalV1 | ResolvedUnresolvedV1;
236
+
237
+ /**
238
+ * Ein Schritt der Resolver-Pipeline (architecture.md §16.2).
239
+ *
240
+ * `supports` entscheidet allein anhand des Spezifizierers und des Kontexts,
241
+ * ob dieser Resolver ueberhaupt zustaendig ist. `resolve` ist synchron und
242
+ * rein: die gesamte Ein-/Ausgabe steckt im vorher aufgebauten
243
+ * `FileIndex` und in der Topologie — dadurch ist die Aufloesung
244
+ * reproduzierbar und ohne Dateisystem testbar.
245
+ */
246
+ export interface ImportResolverV1 {
247
+ /** Name des Resolvers, taucht in Diagnosetexten auf. */
248
+ readonly name: string;
249
+ supports(specifier: string, context: ResolveContextV1): boolean;
250
+ resolve(specifier: string, context: ResolveContextV1): ResolveResultV1 | null;
251
+ }
252
+
253
+ /** Nachschlagbares Verzeichnis aller bekannten Dateien des Workspace. */
254
+ export interface FileLookupV1 {
255
+ /** True, wenn (repoId, path) eine bekannte Datei ist. */
256
+ hasFile(repoId: string, path: string): boolean;
257
+ /** `file_id` der Datei; `null`, wenn unbekannt. */
258
+ fileIdOf(repoId: string, path: string): string | null;
259
+ /** Alle Repository-IDs, nach Codepoints sortiert. */
260
+ repoIds(): readonly string[];
261
+ }
262
+
263
+ /** Der Kontext einer einzelnen Aufloesung. */
264
+ export interface ResolveContextV1 {
265
+ workspaceId: string;
266
+ fromRepoId: string;
267
+ fromRefId: string;
268
+ /** repo-relativer Pfad der Quelldatei. */
269
+ fromPath: string;
270
+ fromLanguageId: string;
271
+ /** Paket, dem die Quelldatei zugeordnet ist. */
272
+ fromPackage?: WorkspacePackageV1;
273
+ files: FileLookupV1;
274
+ /**
275
+ * Paketkatalog der Topologie. Repository-bewusst, weil gleichnamige Pakete
276
+ * in verschiedenen Repositories verschiedene Pakete sind.
277
+ */
278
+ packageCatalog: WorkspacePackageCatalogV1;
279
+ /** Aliasmuster aus Projektkonfigurationen, in Anwendungsreihenfolge. */
280
+ projectAliases: readonly AliasRuleV1[];
281
+ /** Aliasmuster aus der Nutzerkonfiguration. */
282
+ configuredAliases: readonly AliasRuleV1[];
283
+ /** Erweiterungen in Probierreihenfolge, z. B. `[".ts", ".tsx", ...]`. */
284
+ extensionOrder: readonly string[];
285
+ }
286
+
287
+ /**
288
+ * Eine Aliasregel, wie sie `paths` einer Projektkonfiguration beschreibt.
289
+ * `pattern` darf genau einen `*`-Platzhalter enthalten; `targets` sind die
290
+ * Ersetzungen in Reihenfolge, jeweils repo-relativ.
291
+ */
292
+ export interface AliasRuleV1 {
293
+ pattern: string;
294
+ targets: string[];
295
+ /** Repository, in dem die Regel gilt. */
296
+ repoId: string;
297
+ /** Woher die Regel stammt, fuer Diagnosetexte. */
298
+ source: string;
299
+ }
300
+
301
+ // ---------------------------------------------------------------------------
302
+ // Ergebnis
303
+ // ---------------------------------------------------------------------------
304
+
305
+ /** Budgets des Graphbaus. Ohne Budget kann ein grosses Monorepo nicht enden. */
306
+ export interface FileGraphLimitsV1 {
307
+ /** Obergrenze der aufgezaehlten Elementarzyklen. */
308
+ maxCycles: number;
309
+ /** Laenge der `topCentralFiles`-Liste. */
310
+ topCentralCount: number;
311
+ /** Obergrenze der Knoten je stark zusammenhaengender Komponente bei der Zyklensuche. */
312
+ maxCycleSearchNodes: number;
313
+ }
314
+
315
+ /** Das vollstaendige, serialisierbare Ergebnis eines Graphbaus. */
316
+ export interface FileGraphSnapshotV1 {
317
+ schemaVersion: 1;
318
+ workspaceId: string;
319
+ graphGeneration: string;
320
+ /** Nach `fileId` codepoint-sortiert. */
321
+ nodes: FileNodeV1[];
322
+ /** Nach (`fromFileId`, `kind`, `rawSpecifier`, `occurrenceOrdinal`) sortiert. */
323
+ edges: FileEdgeV1[];
324
+ metrics: FileGraphMetricsV1;
325
+ topology: WorkspaceTopologyV1;
326
+ skipped: SkipLedgerV1;
327
+ }
328
+
329
+ /** Ergebnis einer Traversierung des Dateigraphen. */
330
+ export interface FileTraversalResultV1 {
331
+ schemaVersion: 1;
332
+ direction: TraversalDirection;
333
+ rootFileIds: string[];
334
+ /** Erreichte Dateien in Besuchsreihenfolge, ohne die Wurzeln. */
335
+ visitedFileIds: string[];
336
+ /** Besuchte Kanten in Besuchsreihenfolge. */
337
+ edgeIds: string[];
338
+ depthReached: number;
339
+ truncated: boolean;
340
+ /** Pflicht, wenn `truncated` true ist. */
341
+ truncationReason?: TraversalTruncationReason;
342
+ /** Dateien, die wegen Zyklenschutz nicht erneut besucht wurden. */
343
+ revisitSkippedFileIds: string[];
344
+ /**
345
+ * Waehrend der Traversierung beobachtete Zyklen, jeweils als Pfad vom
346
+ * wiedergetroffenen Knoten zurueck zu sich selbst. GEMELDET, nicht
347
+ * aufgebrochen: der Zyklenschutz verhindert nur die Endlosschleife, er
348
+ * verschweigt den Zyklus nicht.
349
+ */
350
+ cycles: string[][];
351
+ /**
352
+ * Wurzeln, die wegen eines erschoepften GLOBALEN Budgets (Knoten-, Kanten-
353
+ * oder Zeitgrenze) gar nicht erst betreten wurden.
354
+ *
355
+ * Ohne dieses Feld nennt das Ergebnis beide Wurzeln in `rootFileIds` und
356
+ * behauptet damit, beide seien bearbeitet worden — waehrend eine davon nie
357
+ * angefasst wurde. Gesetzt nur, wenn es solche Wurzeln gibt.
358
+ */
359
+ unvisitedRootFileIds?: string[];
360
+ /**
361
+ * WP-194: Wurzeln, die dieser Graph GAR NICHT ENTHAELT.
362
+ *
363
+ * Zu unterscheiden von {@link unvisitedRootFileIds} (bekannt, aber wegen
364
+ * Budget nicht betreten) und von einer bekannten Wurzel ohne Nachbarn
365
+ * (bearbeitet, Ergebnis leer). Ohne dieses Feld sind alle drei Faelle im
366
+ * Ergebnis nicht auseinanderzuhalten: eine falsch geschriebene `file_id`
367
+ * liefert dann ein leeres, unauffaelliges Resultat, das wie „diese Datei hat
368
+ * keine Abhaengigkeiten" aussieht. Gesetzt nur, wenn es solche Wurzeln gibt.
369
+ */
370
+ unknownRootFileIds?: string[];
371
+ limits: TraversalLimitsV1;
372
+ }