@untangle-code/file-graph 0.0.0-stage → 0.1.1

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
@@ -0,0 +1,39 @@
1
+ /** Ausgewertete Projektkonfiguration eines Verzeichnisses. */
2
+ export interface ProjectConfigV1 {
3
+ /** repo-relativer Pfad der Konfigurationsdatei. */
4
+ configPath: string;
5
+ /** repo-relativ aufgeloester `baseUrl`; `undefined`, wenn nicht gesetzt. */
6
+ baseUrl?: string;
7
+ /** Aliasmuster → repo-relative Ziele. */
8
+ paths: Record<string, string[]>;
9
+ /** repo-relativ aufgeloester `rootDir`. */
10
+ rootDir?: string;
11
+ /** repo-relativ aufgeloester `outDir`. */
12
+ outDir?: string;
13
+ /** Konfigurationen, von denen geerbt wurde, in Erbfolge. */
14
+ extendsChain: string[];
15
+ }
16
+ /** Eine einzelne, bereits gelesene Konfigurationsdatei. */
17
+ export interface RawProjectConfigV1 {
18
+ /** repo-relativer Pfad. */
19
+ path: string;
20
+ text: string;
21
+ }
22
+ export type ProjectConfigResult = {
23
+ ok: true;
24
+ config: ProjectConfigV1;
25
+ warnings: string[];
26
+ } | {
27
+ ok: false;
28
+ error: string;
29
+ };
30
+ /**
31
+ * Wertet eine Projektkonfiguration samt `extends`-Kette aus.
32
+ *
33
+ * `readConfig` liefert den Text einer Konfigurationsdatei oder `null`, wenn es
34
+ * sie nicht gibt. Ein fehlendes `extends`-Ziel ist KEIN Abbruch, sondern eine
35
+ * Warnung im Ergebnis — sonst verlieren wir wegen einer nicht eingecheckten
36
+ * Basisdatei die Aliase des ganzen Pakets, ohne dass es jemand merkt (E4).
37
+ */
38
+ export declare function resolveProjectConfig(configPath: string, readConfig: (path: string) => string | null): ProjectConfigResult;
39
+ //# sourceMappingURL=project-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../../src/topology/project-config.ts"],"names":[],"mappings":"AAsBA,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,mBAAmB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAC1C,mBAAmB,CA2HrB"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Projektkonfiguration (`tsconfig.json` und Verwandte) → Pfad-Aliase und
3
+ * Ausgabeverzeichnisse.
4
+ *
5
+ * Zwei Dinge braucht der Dateigraph daraus:
6
+ *
7
+ * 1. `baseUrl` + `paths` — die Pfad-Aliase, die Schritt 3 der Resolver-
8
+ * Pipeline (architecture.md §16.2) aufloest.
9
+ * 2. `rootDir` + `outDir` — die Rueckabbildung von Build-Ausgabe auf Quelle.
10
+ * Ohne sie zeigt jeder Workspace-Import ueber eine `exports`-Map auf
11
+ * `dist/index.js`, also auf eine Datei, die im Quellindex gar nicht
12
+ * vorkommt; die Kante waere dann „unresolved", obwohl das Ziel bekannt
13
+ * ist. Mit ihr wird `dist/index.js` zu `src/index.ts`.
14
+ *
15
+ * `extends` wird verfolgt, weil in einem Monorepo praktisch jede
16
+ * Paketkonfiguration von einer gemeinsamen Basis erbt und `paths` fast immer
17
+ * genau dort steht.
18
+ */
19
+ import { dirnameOf, joinRelative, normalizePath } from "../paths.js";
20
+ import { isJsonObject, parseJsonc, stringField } from "./jsonc.js";
21
+ /**
22
+ * Wertet eine Projektkonfiguration samt `extends`-Kette aus.
23
+ *
24
+ * `readConfig` liefert den Text einer Konfigurationsdatei oder `null`, wenn es
25
+ * sie nicht gibt. Ein fehlendes `extends`-Ziel ist KEIN Abbruch, sondern eine
26
+ * Warnung im Ergebnis — sonst verlieren wir wegen einer nicht eingecheckten
27
+ * Basisdatei die Aliase des ganzen Pakets, ohne dass es jemand merkt (E4).
28
+ */
29
+ export function resolveProjectConfig(configPath, readConfig) {
30
+ const warnings = [];
31
+ const extendsChain = [];
32
+ const seen = new Set();
33
+ // Von der Wurzel der Erbkette zum konkreten Projekt hin sammeln, damit
34
+ // spaetere (spezifischere) Werte frueher gelesene ueberschreiben.
35
+ const layers = [];
36
+ let current = normalizePath(configPath);
37
+ let depth = 0;
38
+ while (current !== null) {
39
+ if (seen.has(current)) {
40
+ return { ok: false, error: `circular "extends" chain at ${current}` };
41
+ }
42
+ seen.add(current);
43
+ depth += 1;
44
+ if (depth > 32) {
45
+ return { ok: false, error: `"extends" chain longer than 32 at ${current}` };
46
+ }
47
+ const text = readConfig(current);
48
+ if (text === null) {
49
+ if (layers.length === 0) {
50
+ return { ok: false, error: `configuration file not found: ${current}` };
51
+ }
52
+ warnings.push(`extends target not found: ${current}`);
53
+ break;
54
+ }
55
+ const parsed = parseJsonc(text);
56
+ if (!parsed.ok) {
57
+ if (layers.length === 0) {
58
+ return { ok: false, error: `${current}: ${parsed.error}` };
59
+ }
60
+ warnings.push(`extends target unreadable (${current}): ${parsed.error}`);
61
+ break;
62
+ }
63
+ if (!isJsonObject(parsed.value)) {
64
+ warnings.push(`${current}: root is not a JSON object`);
65
+ break;
66
+ }
67
+ const root = parsed.value;
68
+ const options = isJsonObject(root["compilerOptions"]) ? root["compilerOptions"] : {};
69
+ layers.push({ path: current, options });
70
+ const extendsValue = stringField(root, "extends");
71
+ if (extendsValue === undefined) {
72
+ break;
73
+ }
74
+ const next = joinRelative(dirnameOf(current), extendsValue);
75
+ if (next === null) {
76
+ warnings.push(`extends target escapes the repository root: ${extendsValue}`);
77
+ break;
78
+ }
79
+ extendsChain.push(next);
80
+ // `extends` ohne Erweiterung meint eine JSON-Datei.
81
+ current = /\.json$/i.test(next) ? next : `${next}.json`;
82
+ }
83
+ const config = {
84
+ configPath: normalizePath(configPath),
85
+ paths: {},
86
+ extendsChain,
87
+ };
88
+ // Von der Basis (letzte Schicht) zum Projekt (erste Schicht) anwenden.
89
+ for (const layer of [...layers].reverse()) {
90
+ const dir = dirnameOf(layer.path);
91
+ const baseUrl = stringField(layer.options, "baseUrl");
92
+ if (baseUrl !== undefined) {
93
+ const resolved = joinRelative(dir, baseUrl);
94
+ if (resolved === null) {
95
+ warnings.push(`baseUrl escapes the repository root in ${layer.path}: ${baseUrl}`);
96
+ }
97
+ else {
98
+ config.baseUrl = resolved;
99
+ }
100
+ }
101
+ const rootDir = stringField(layer.options, "rootDir");
102
+ if (rootDir !== undefined) {
103
+ const resolved = joinRelative(dir, rootDir);
104
+ if (resolved === null) {
105
+ warnings.push(`rootDir escapes the repository root in ${layer.path}: ${rootDir}`);
106
+ }
107
+ else {
108
+ config.rootDir = resolved;
109
+ }
110
+ }
111
+ const outDir = stringField(layer.options, "outDir");
112
+ if (outDir !== undefined) {
113
+ const resolved = joinRelative(dir, outDir);
114
+ if (resolved === null) {
115
+ warnings.push(`outDir escapes the repository root in ${layer.path}: ${outDir}`);
116
+ }
117
+ else {
118
+ config.outDir = resolved;
119
+ }
120
+ }
121
+ const pathsValue = layer.options["paths"];
122
+ if (isJsonObject(pathsValue)) {
123
+ // `paths` ist relativ zu `baseUrl`, ersatzweise zum Verzeichnis der
124
+ // Datei, in der `paths` steht (TypeScript-Verhalten seit 4.1).
125
+ const anchor = config.baseUrl ?? dir;
126
+ for (const [pattern, targets] of Object.entries(pathsValue)) {
127
+ if (!Array.isArray(targets)) {
128
+ warnings.push(`paths["${pattern}"] in ${layer.path} is not an array`);
129
+ continue;
130
+ }
131
+ const resolvedTargets = [];
132
+ for (const target of targets) {
133
+ if (typeof target !== "string") {
134
+ warnings.push(`paths["${pattern}"] in ${layer.path} contains a non-string target`);
135
+ continue;
136
+ }
137
+ const resolved = joinRelative(anchor, target);
138
+ if (resolved === null) {
139
+ warnings.push(`paths["${pattern}"] target escapes the repository root: ${target}`);
140
+ continue;
141
+ }
142
+ resolvedTargets.push(resolved);
143
+ }
144
+ config.paths[pattern] = resolvedTargets;
145
+ }
146
+ }
147
+ }
148
+ return { ok: true, config, warnings };
149
+ }
150
+ //# sourceMappingURL=project-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-config.js","sourceRoot":"","sources":["../../src/topology/project-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export type WorkspaceDefinitionResult = {
2
+ ok: true;
3
+ globs: string[];
4
+ } | {
5
+ ok: false;
6
+ error: string;
7
+ };
8
+ /** Liest die `packages:`-Liste einer pnpm-Workspace-Definition. */
9
+ export declare function parsePnpmWorkspace(text: string): WorkspaceDefinitionResult;
10
+ /** Zerlegt eine Globliste in Einschluesse und Ausschluesse. */
11
+ export interface GlobSelectionV1 {
12
+ include: string[];
13
+ exclude: string[];
14
+ }
15
+ export declare function splitGlobs(globs: readonly string[]): GlobSelectionV1;
16
+ /**
17
+ * Uebersetzt ein Workspace-Glob in einen Regulaerausdruck ueber Pfadsegmente.
18
+ *
19
+ * `**` frisst beliebig viele Segmente einschliesslich null, `*` genau ein
20
+ * Segment ohne `/`, `?` genau ein Zeichen ohne `/`. Alles andere wird
21
+ * woertlich verglichen.
22
+ */
23
+ export declare function globToRegExp(glob: string): RegExp;
24
+ /** True, wenn `directory` von der Globauswahl erfasst wird. */
25
+ export declare function matchesGlobSelection(selection: GlobSelectionV1, directory: string): boolean;
26
+ //# sourceMappingURL=workspace-definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-definition.d.ts","sourceRoot":"","sources":["../../src/topology/workspace-definition.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,yBAAyB,GACnC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,CAsB1E;AAED,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CAWpE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgCjD;AAED,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAO3F"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Workspace-Definition und Globabgleich.
3
+ *
4
+ * Quellen fuer die Liste der Teilprojekte, in dieser Reihenfolge:
5
+ * 1. `pnpm-workspace.yaml` (`packages:`) — das pnpm-Protokoll;
6
+ * 2. `workspaces` im Wurzelmanifest (npm/Yarn).
7
+ *
8
+ * Der Globabgleich deckt genau das ab, was Workspace-Definitionen wirklich
9
+ * benutzen: `*` (ein Segment), `**` (beliebig viele Segmente), `?` (ein
10
+ * Zeichen) und ein fuehrendes `!` als Ausschluss. Ein vollstaendiger
11
+ * Glob-Dialekt waere hier ueberdimensioniert — und ein NICHT unterstuetztes
12
+ * Muster wird gemeldet statt still ignoriert (E4).
13
+ */
14
+ import { compareCodepoints } from "@untangle-code/domain";
15
+ import { parse as parseYaml } from "yaml";
16
+ import { normalizePath } from "../paths.js";
17
+ /** Liest die `packages:`-Liste einer pnpm-Workspace-Definition. */
18
+ export function parsePnpmWorkspace(text) {
19
+ let document;
20
+ try {
21
+ document = parseYaml(text);
22
+ }
23
+ catch (error) {
24
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
25
+ }
26
+ if (typeof document !== "object" || document === null || Array.isArray(document)) {
27
+ return { ok: false, error: "workspace document root is not a mapping" };
28
+ }
29
+ const packages = document["packages"];
30
+ if (packages === undefined) {
31
+ return { ok: false, error: 'workspace document has no "packages" list' };
32
+ }
33
+ if (!Array.isArray(packages)) {
34
+ return { ok: false, error: '"packages" is not a list' };
35
+ }
36
+ const globs = packages.filter((entry) => typeof entry === "string");
37
+ if (globs.length !== packages.length) {
38
+ return { ok: false, error: '"packages" contains non-string entries' };
39
+ }
40
+ return { ok: true, globs };
41
+ }
42
+ export function splitGlobs(globs) {
43
+ const include = [];
44
+ const exclude = [];
45
+ for (const glob of globs) {
46
+ if (glob.startsWith("!")) {
47
+ exclude.push(normalizePath(glob.slice(1)));
48
+ }
49
+ else {
50
+ include.push(normalizePath(glob));
51
+ }
52
+ }
53
+ return { include: include.sort(compareCodepoints), exclude: exclude.sort(compareCodepoints) };
54
+ }
55
+ /**
56
+ * Uebersetzt ein Workspace-Glob in einen Regulaerausdruck ueber Pfadsegmente.
57
+ *
58
+ * `**` frisst beliebig viele Segmente einschliesslich null, `*` genau ein
59
+ * Segment ohne `/`, `?` genau ein Zeichen ohne `/`. Alles andere wird
60
+ * woertlich verglichen.
61
+ */
62
+ export function globToRegExp(glob) {
63
+ const normalized = normalizePath(glob);
64
+ let pattern = "";
65
+ let index = 0;
66
+ while (index < normalized.length) {
67
+ const char = normalized[index];
68
+ if (char === "*") {
69
+ const isDoubleStar = normalized[index + 1] === "*";
70
+ if (isDoubleStar) {
71
+ // `a/**/b` und `a/**` sollen auch `a/b` bzw. `a` treffen.
72
+ if (normalized[index + 2] === "/") {
73
+ pattern += "(?:.*/)?";
74
+ index += 3;
75
+ continue;
76
+ }
77
+ pattern += ".*";
78
+ index += 2;
79
+ continue;
80
+ }
81
+ pattern += "[^/]*";
82
+ index += 1;
83
+ continue;
84
+ }
85
+ if (char === "?") {
86
+ pattern += "[^/]";
87
+ index += 1;
88
+ continue;
89
+ }
90
+ pattern += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
91
+ index += 1;
92
+ }
93
+ return new RegExp(`^${pattern}$`);
94
+ }
95
+ /** True, wenn `directory` von der Globauswahl erfasst wird. */
96
+ export function matchesGlobSelection(selection, directory) {
97
+ const candidate = normalizePath(directory);
98
+ const included = selection.include.some((glob) => globToRegExp(glob).test(candidate));
99
+ if (!included) {
100
+ return false;
101
+ }
102
+ return !selection.exclude.some((glob) => globToRegExp(glob).test(candidate));
103
+ }
104
+ //# sourceMappingURL=workspace-definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-definition.js","sourceRoot":"","sources":["../../src/topology/workspace-definition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Die Traversierung des Dateigraphen als FREIE FUNKTION — losgeloest von der
3
+ * Frage, ob der Graph gerade gebaut oder aus einem Abbild zurueckgeladen
4
+ * wurde.
5
+ *
6
+ * WARUM SIE HIER STEHT UND NICHT MEHR IN `graph.ts`: Der Lesepfad
7
+ * (`readGraphSnapshot` → `restoreFileGraph`) braucht exakt dieselbe
8
+ * Traversierung wie der Schreibpfad. Eine zweite, „nur fuer die Anzeige"
9
+ * geschriebene Fassung waere eine zweite Wahrheit ueber Zyklen und Budgets —
10
+ * und Zyklenmeldung wie Abschneidegrund sind genau die Aussagen, an denen ein
11
+ * Werkzeug luegt, wenn es sie zweimal implementiert. `FileDependencyGraph`
12
+ * und `RestoredFileGraph` rufen deshalb DIESELBE Funktion.
13
+ *
14
+ * Die drei Zusagen der Traversierung sind unveraendert:
15
+ *
16
+ * 1. Tiefensuche, nicht Breitensuche: nur ein Pfadstapel kann echte Zyklen
17
+ * BENENNEN statt bloss „hier war ich schon" zu vermerken.
18
+ * 2. Ein Zyklus wird gemeldet, nicht aufgebrochen.
19
+ * 3. Ein abgeschnittenes Ergebnis sagt, DASS und WARUM es abgeschnitten ist
20
+ * (`truncated` + `truncationReason`, E4). Die Tiefengrenze beschneidet nur
21
+ * den betrachteten Ast; ein erschoepftes GLOBALES Budget beendet den Lauf
22
+ * und nennt die nie angefassten Wurzeln.
23
+ */
24
+ import type { TraversalDirection, TraversalLimitsV1 } from "@untangle-code/domain";
25
+ import type { AdjacencyV1 } from "./metrics.js";
26
+ import type { FileTraversalResultV1 } from "./types.js";
27
+ /** Standardbudgets einer Traversierung. */
28
+ export declare const DEFAULT_TRAVERSAL_LIMITS: TraversalLimitsV1;
29
+ /** Uhr fuer Zeitbudgets. Injizierbar, damit Tests nicht auf echte Zeit warten. */
30
+ export type MonotonicClock = () => number;
31
+ /**
32
+ * Was die Traversierung vom Graphen wissen muss — und sonst nichts.
33
+ *
34
+ * `edgeIdBetween` ist bewusst eine Funktion und keine fertige Karte: der
35
+ * gebaute Graph beantwortet sie ueber seinen Kantenindex, der
36
+ * zurueckgeladene ueber die Kanten des Abbilds. Beide antworten dasselbe.
37
+ */
38
+ export interface FileGraphNeighborhoodV1 {
39
+ readonly adjacency: AdjacencyV1;
40
+ edgeIdBetween(from: string, to: string, direction: TraversalDirection): string | null;
41
+ }
42
+ export interface TraverseFileGraphOptionsV1 {
43
+ direction: TraversalDirection;
44
+ limits?: Partial<TraversalLimitsV1>;
45
+ now?: MonotonicClock;
46
+ }
47
+ /** Tiefensuche mit Budget und Zyklenmeldung. */
48
+ export declare function traverseFileGraph(neighborhood: FileGraphNeighborhoodV1, rootFileIds: readonly string[], options: TraverseFileGraphOptionsV1): FileTraversalResultV1;
49
+ //# sourceMappingURL=traverse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../src/traverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,2CAA2C;AAC3C,eAAO,MAAM,wBAAwB,EAAE,iBAKtC,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAAC;CACvF;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAQD,gDAAgD;AAChD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,uBAAuB,EACrC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,OAAO,EAAE,0BAA0B,GAClC,qBAAqB,CAkKvB"}
@@ -0,0 +1,167 @@
1
+ import { uniqueSortedByCodepoints } from "@untangle-code/domain";
2
+ /** Standardbudgets einer Traversierung. */
3
+ export const DEFAULT_TRAVERSAL_LIMITS = {
4
+ maxDepth: 16,
5
+ maxNodes: 5_000,
6
+ maxEdges: 20_000,
7
+ maxWallTimeMs: 5_000,
8
+ };
9
+ function defaultClock() {
10
+ // `performance.now()` ist monoton und wird ausschliesslich fuer die
11
+ // Budgetentscheidung benutzt — kein Zeitwert landet je in Ergebnisdaten.
12
+ return performance.now();
13
+ }
14
+ /** Tiefensuche mit Budget und Zyklenmeldung. */
15
+ export function traverseFileGraph(neighborhood, rootFileIds, options) {
16
+ const limits = { ...DEFAULT_TRAVERSAL_LIMITS, ...options.limits };
17
+ const now = options.now ?? defaultClock;
18
+ const startedAt = now();
19
+ const neighbors = options.direction === "forward"
20
+ ? neighborhood.adjacency.forward
21
+ : neighborhood.adjacency.reverse;
22
+ const roots = uniqueSortedByCodepoints([...rootFileIds]);
23
+ const visited = new Set(roots);
24
+ const visitedOrder = [];
25
+ const edgeIds = [];
26
+ const revisitSkipped = [];
27
+ const cycles = [];
28
+ const seenCycleKeys = new Set();
29
+ let depthReached = 0;
30
+ let truncated = false;
31
+ let truncationReason;
32
+ /**
33
+ * GLOBALES Budget erschoepft (Knoten, Kanten, Zeit) — im Unterschied zur
34
+ * Tiefengrenze, die NUR den gerade betrachteten Ast beschneidet. Beides in
35
+ * einem Flag zu fuehren war der Fehler: eine erreichte Tiefe in der ersten
36
+ * Wurzel warf alle weiteren Wurzeln weg, obwohl auf deren Aesten die
37
+ * Tiefengrenze noch gar nicht erreicht war.
38
+ */
39
+ let budgetExhausted = false;
40
+ const stop = (reason) => {
41
+ truncated = true;
42
+ // WP-196: NICHT der erste Grund, sondern der, an dem tatsaechlich etwas
43
+ // verloren ging. `max_depth` beschneidet nur den gerade betrachteten Ast
44
+ // und laesst die Traversierung weiterlaufen; ein erschoepftes GLOBALES
45
+ // Budget beendet sie. Wer beide in der Reihenfolge ihres Auftretens
46
+ // aufhebt, meldet bei „erst Tiefe, dann Knotenbudget" die Tiefe — und
47
+ // schickt den Leser damit an die falsche Stellschraube, waehrend die
48
+ // Wurzeln in `unvisitedRootFileIds` von einem Budget erzaehlen, das im
49
+ // Abschneidegrund gar nicht vorkommt.
50
+ if (truncationReason === undefined ||
51
+ (truncationReason === "max_depth" && reason !== "max_depth")) {
52
+ truncationReason = reason;
53
+ }
54
+ if (reason !== "max_depth")
55
+ budgetExhausted = true;
56
+ };
57
+ /**
58
+ * WP-194: Wurzeln, die der Graph GAR NICHT KENNT.
59
+ *
60
+ * `buildAdjacency` legt fuer jeden Knoten einen Eintrag an — auch fuer einen
61
+ * ohne jede Kante. Ein fehlender Eintrag heisst deshalb nicht „isolierter
62
+ * Knoten", sondern „diese Datei ist im Graphen nicht vorhanden". Bisher
63
+ * landete eine solche Wurzel unbesehen in `rootFileIds` und sah dort aus wie
64
+ * jede andere bearbeitete Wurzel: null Nachbarn, kein Hinweis. Wer nach
65
+ * einem Tippfehler im Pfad sucht, bekam ein leeres, unauffaelliges Ergebnis
66
+ * — die Frage wurde nie gestellt, und niemand sagte es.
67
+ */
68
+ const unknownRoots = [];
69
+ const unvisitedRoots = [];
70
+ for (const root of roots) {
71
+ if (!neighborhood.adjacency.forward.has(root)) {
72
+ unknownRoots.push(root);
73
+ continue;
74
+ }
75
+ if (budgetExhausted) {
76
+ // Nicht stillschweigend fallen lassen: diese Wurzeln wurden nie
77
+ // angefasst, und `rootFileIds` allein saehe so aus, als waeren sie
78
+ // bearbeitet worden (E4).
79
+ unvisitedRoots.push(root);
80
+ continue;
81
+ }
82
+ const path = [root];
83
+ const onPath = new Set([root]);
84
+ const work = [
85
+ { node: root, childIndex: 0, depth: 0 },
86
+ ];
87
+ while (work.length > 0) {
88
+ if (now() - startedAt > limits.maxWallTimeMs) {
89
+ stop("max_wall_time");
90
+ break;
91
+ }
92
+ const frame = work[work.length - 1];
93
+ const children = neighbors.get(frame.node) ?? [];
94
+ if (frame.childIndex >= children.length) {
95
+ work.pop();
96
+ const removed = path.pop();
97
+ if (removed !== undefined)
98
+ onPath.delete(removed);
99
+ continue;
100
+ }
101
+ const child = children[frame.childIndex];
102
+ frame.childIndex += 1;
103
+ if (onPath.has(child)) {
104
+ // Zyklus GEFUNDEN — nicht aufgebrochen, sondern benannt.
105
+ const cycle = path.slice(path.indexOf(child));
106
+ const key = cycle.join("/");
107
+ if (!seenCycleKeys.has(key)) {
108
+ seenCycleKeys.add(key);
109
+ cycles.push(cycle);
110
+ }
111
+ revisitSkipped.push(child);
112
+ continue;
113
+ }
114
+ const edgeId = neighborhood.edgeIdBetween(frame.node, child, options.direction);
115
+ if (edgeId !== null) {
116
+ if (edgeIds.length >= limits.maxEdges) {
117
+ stop("max_edges");
118
+ break;
119
+ }
120
+ edgeIds.push(edgeId);
121
+ }
122
+ if (visited.has(child)) {
123
+ revisitSkipped.push(child);
124
+ continue;
125
+ }
126
+ if (visitedOrder.length >= limits.maxNodes) {
127
+ stop("max_nodes");
128
+ break;
129
+ }
130
+ visited.add(child);
131
+ visitedOrder.push(child);
132
+ const childDepth = frame.depth + 1;
133
+ depthReached = Math.max(depthReached, childDepth);
134
+ if (childDepth >= limits.maxDepth) {
135
+ // Die Tiefengrenze ist erreicht: der Knoten zaehlt als besucht, seine
136
+ // Nachbarn werden nicht mehr betrachtet — und genau das wird gemeldet.
137
+ if ((neighbors.get(child) ?? []).length > 0) {
138
+ stop("max_depth");
139
+ }
140
+ continue;
141
+ }
142
+ path.push(child);
143
+ onPath.add(child);
144
+ work.push({ node: child, childIndex: 0, depth: childDepth });
145
+ }
146
+ }
147
+ return {
148
+ schemaVersion: 1,
149
+ direction: options.direction,
150
+ rootFileIds: roots,
151
+ visitedFileIds: visitedOrder,
152
+ edgeIds,
153
+ depthReached,
154
+ truncated,
155
+ ...(truncationReason === undefined ? {} : { truncationReason }),
156
+ revisitSkippedFileIds: uniqueSortedByCodepoints(revisitSkipped),
157
+ cycles,
158
+ ...(unvisitedRoots.length === 0
159
+ ? {}
160
+ : { unvisitedRootFileIds: uniqueSortedByCodepoints(unvisitedRoots) }),
161
+ ...(unknownRoots.length === 0
162
+ ? {}
163
+ : { unknownRootFileIds: uniqueSortedByCodepoints(unknownRoots) }),
164
+ limits,
165
+ };
166
+ }
167
+ //# sourceMappingURL=traverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.js","sourceRoot":"","sources":["../src/traverse.ts"],"names":[],"mappings":"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"}