@documentation-robotics/cli 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 (169) hide show
  1. package/PYTHON_CLI_PARITY.md +265 -0
  2. package/PYTHON_CLI_STRUCTURE_UPDATE.md +246 -0
  3. package/README.md +926 -0
  4. package/biome.json +42 -0
  5. package/bunfig.toml +3 -0
  6. package/dist/ai/claude-client.js +71 -0
  7. package/dist/ai/claude-client.js.map +7 -0
  8. package/dist/ai/context-provider.js +129 -0
  9. package/dist/ai/context-provider.js.map +7 -0
  10. package/dist/ai/tools.js +231 -0
  11. package/dist/ai/tools.js.map +7 -0
  12. package/dist/cli.js +224 -0
  13. package/dist/cli.js.map +7 -0
  14. package/dist/commands/add.js +51 -0
  15. package/dist/commands/add.js.map +7 -0
  16. package/dist/commands/changeset.js +174 -0
  17. package/dist/commands/changeset.js.map +7 -0
  18. package/dist/commands/chat.js +111 -0
  19. package/dist/commands/chat.js.map +7 -0
  20. package/dist/commands/conformance.js +132 -0
  21. package/dist/commands/conformance.js.map +7 -0
  22. package/dist/commands/delete.js +31 -0
  23. package/dist/commands/delete.js.map +7 -0
  24. package/dist/commands/element.js +28 -0
  25. package/dist/commands/element.js.map +7 -0
  26. package/dist/commands/export.js +65 -0
  27. package/dist/commands/export.js.map +7 -0
  28. package/dist/commands/info.js +40 -0
  29. package/dist/commands/info.js.map +7 -0
  30. package/dist/commands/init.js +45 -0
  31. package/dist/commands/init.js.map +7 -0
  32. package/dist/commands/list.js +32 -0
  33. package/dist/commands/list.js.map +7 -0
  34. package/dist/commands/migrate.js +36 -0
  35. package/dist/commands/migrate.js.map +7 -0
  36. package/dist/commands/project.js +74 -0
  37. package/dist/commands/project.js.map +7 -0
  38. package/dist/commands/relationship.js +118 -0
  39. package/dist/commands/relationship.js.map +7 -0
  40. package/dist/commands/search.js +48 -0
  41. package/dist/commands/search.js.map +7 -0
  42. package/dist/commands/show.js +38 -0
  43. package/dist/commands/show.js.map +7 -0
  44. package/dist/commands/trace.js +60 -0
  45. package/dist/commands/trace.js.map +7 -0
  46. package/dist/commands/update.js +37 -0
  47. package/dist/commands/update.js.map +7 -0
  48. package/dist/commands/upgrade.js +152 -0
  49. package/dist/commands/upgrade.js.map +7 -0
  50. package/dist/commands/validate.js +71 -0
  51. package/dist/commands/validate.js.map +7 -0
  52. package/dist/commands/version.js +20 -0
  53. package/dist/commands/version.js.map +7 -0
  54. package/dist/commands/visualize.js +51 -0
  55. package/dist/commands/visualize.js.map +7 -0
  56. package/dist/core/active-changeset.js +52 -0
  57. package/dist/core/active-changeset.js.map +7 -0
  58. package/dist/core/changeset.js +244 -0
  59. package/dist/core/changeset.js.map +7 -0
  60. package/dist/core/dependency-tracker.js +322 -0
  61. package/dist/core/dependency-tracker.js.map +7 -0
  62. package/dist/core/element.js +65 -0
  63. package/dist/core/element.js.map +7 -0
  64. package/dist/core/layer.js +75 -0
  65. package/dist/core/layer.js.map +7 -0
  66. package/dist/core/manifest.js +57 -0
  67. package/dist/core/manifest.js.map +7 -0
  68. package/dist/core/migration-registry.js +131 -0
  69. package/dist/core/migration-registry.js.map +7 -0
  70. package/dist/core/model.js +455 -0
  71. package/dist/core/model.js.map +7 -0
  72. package/dist/core/projection-engine.js +288 -0
  73. package/dist/core/projection-engine.js.map +7 -0
  74. package/dist/core/reference-registry.js +188 -0
  75. package/dist/core/reference-registry.js.map +7 -0
  76. package/dist/core/relationship-registry.js +114 -0
  77. package/dist/core/relationship-registry.js.map +7 -0
  78. package/dist/core/relationships.js +78 -0
  79. package/dist/core/relationships.js.map +7 -0
  80. package/dist/export/archimate-exporter.js +111 -0
  81. package/dist/export/archimate-exporter.js.map +7 -0
  82. package/dist/export/export-manager.js +63 -0
  83. package/dist/export/export-manager.js.map +7 -0
  84. package/dist/export/graphml-exporter.js +39 -0
  85. package/dist/export/graphml-exporter.js.map +7 -0
  86. package/dist/export/index.js +17 -0
  87. package/dist/export/index.js.map +7 -0
  88. package/dist/export/json-schema-exporter.js +60 -0
  89. package/dist/export/json-schema-exporter.js.map +7 -0
  90. package/dist/export/markdown-exporter.js +99 -0
  91. package/dist/export/markdown-exporter.js.map +7 -0
  92. package/dist/export/openapi-exporter.js +85 -0
  93. package/dist/export/openapi-exporter.js.map +7 -0
  94. package/dist/export/plantuml-exporter.js +60 -0
  95. package/dist/export/plantuml-exporter.js.map +7 -0
  96. package/dist/export/types.js +36 -0
  97. package/dist/export/types.js.map +7 -0
  98. package/dist/schemas/bundled/01-motivation-layer.schema.json +942 -0
  99. package/dist/schemas/bundled/02-business-layer.schema.json +1293 -0
  100. package/dist/schemas/bundled/03-security-layer.schema.json +2177 -0
  101. package/dist/schemas/bundled/04-application-layer.schema.json +1328 -0
  102. package/dist/schemas/bundled/05-technology-layer.schema.json +1465 -0
  103. package/dist/schemas/bundled/06-api-layer.schema.json +1743 -0
  104. package/dist/schemas/bundled/07-data-model-layer.schema.json +1253 -0
  105. package/dist/schemas/bundled/08-datastore-layer.schema.json +892 -0
  106. package/dist/schemas/bundled/09-ux-layer.schema.json +1506 -0
  107. package/dist/schemas/bundled/10-navigation-layer.schema.json +843 -0
  108. package/dist/schemas/bundled/11-apm-observability-layer.schema.json +1430 -0
  109. package/dist/schemas/bundled/12-testing-layer.schema.json +1091 -0
  110. package/dist/schemas/bundled/common/layer-extensions.schema.json +266 -0
  111. package/dist/schemas/bundled/common/predicates.schema.json +493 -0
  112. package/dist/schemas/bundled/common/relationships.schema.json +791 -0
  113. package/dist/schemas/bundled/common/source-references.schema.json +71 -0
  114. package/dist/schemas/bundled/link-registry.json +1839 -0
  115. package/dist/schemas/bundled/link-registry.schema.json +339 -0
  116. package/dist/schemas/bundled/relationship-catalog.json +1302 -0
  117. package/dist/schemas/bundled/relationship-type.schema.json +221 -0
  118. package/dist/server/server.js +1207 -0
  119. package/dist/server/server.js.map +7 -0
  120. package/dist/server/telemetry-middleware.js +24 -0
  121. package/dist/server/telemetry-middleware.js.map +7 -0
  122. package/dist/telemetry/console-interceptor.js +34 -0
  123. package/dist/telemetry/console-interceptor.js.map +7 -0
  124. package/dist/telemetry/index.js +54 -0
  125. package/dist/telemetry/index.js.map +7 -0
  126. package/dist/telemetry/resilient-exporter.js +41 -0
  127. package/dist/telemetry/resilient-exporter.js.map +7 -0
  128. package/dist/telemetry/resilient-log-exporter.js +41 -0
  129. package/dist/telemetry/resilient-log-exporter.js.map +7 -0
  130. package/dist/telemetry/test-instrumentation.js +32 -0
  131. package/dist/telemetry/test-instrumentation.js.map +7 -0
  132. package/dist/types/index.js +1 -0
  133. package/dist/types/index.js.map +7 -0
  134. package/dist/utils/element-utils.js +11 -0
  135. package/dist/utils/element-utils.js.map +7 -0
  136. package/dist/utils/errors.js +105 -0
  137. package/dist/utils/errors.js.map +7 -0
  138. package/dist/utils/file-io.js +69 -0
  139. package/dist/utils/file-io.js.map +7 -0
  140. package/dist/utils/globals.js +28 -0
  141. package/dist/utils/globals.js.map +7 -0
  142. package/dist/utils/model-path.js +22 -0
  143. package/dist/utils/model-path.js.map +7 -0
  144. package/dist/utils/project-paths.js +45 -0
  145. package/dist/utils/project-paths.js.map +7 -0
  146. package/dist/utils/spec-installer.js +85 -0
  147. package/dist/utils/spec-installer.js.map +7 -0
  148. package/dist/utils/spec-version.js +33 -0
  149. package/dist/utils/spec-version.js.map +7 -0
  150. package/dist/validators/index.js +15 -0
  151. package/dist/validators/index.js.map +7 -0
  152. package/dist/validators/naming-validator.js +82 -0
  153. package/dist/validators/naming-validator.js.map +7 -0
  154. package/dist/validators/reference-validator.js +106 -0
  155. package/dist/validators/reference-validator.js.map +7 -0
  156. package/dist/validators/schema-validator.js +148 -0
  157. package/dist/validators/schema-validator.js.map +7 -0
  158. package/dist/validators/semantic-validator.js +80 -0
  159. package/dist/validators/semantic-validator.js.map +7 -0
  160. package/dist/validators/types.js +48 -0
  161. package/dist/validators/types.js.map +7 -0
  162. package/dist/validators/validator.js +67 -0
  163. package/dist/validators/validator.js.map +7 -0
  164. package/docs/TEST-INSTRUMENTATION.md +405 -0
  165. package/docs/telemetry.md +427 -0
  166. package/package.json +70 -0
  167. package/test-debug.js +33 -0
  168. package/test-special.mjs +46 -0
  169. package/test_runner.ts +7 -0
@@ -0,0 +1,244 @@
1
+ import { readJSON, writeJSON, ensureDir, fileExists } from "../utils/file-io.js";
2
+ import { Element } from "./element.js";
3
+ class Changeset {
4
+ name;
5
+ description;
6
+ created;
7
+ modified;
8
+ changes = [];
9
+ status = "draft";
10
+ constructor(data) {
11
+ this.name = data.name, this.description = data.description, this.created = data.created, this.modified = data.modified, this.changes = data.changes || [], this.status = data.status || "draft";
12
+ }
13
+ /**
14
+ * Create a new changeset
15
+ */
16
+ static create(name, description) {
17
+ const now = (/* @__PURE__ */ new Date()).toISOString();
18
+ return new Changeset({
19
+ name,
20
+ description,
21
+ created: now,
22
+ modified: now,
23
+ changes: [],
24
+ status: "draft"
25
+ });
26
+ }
27
+ /**
28
+ * Add a change to the changeset
29
+ */
30
+ addChange(type, elementId, layerName, before, after) {
31
+ this.changes.push({
32
+ type,
33
+ elementId,
34
+ layerName,
35
+ before,
36
+ after,
37
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
38
+ }), this.updateModified();
39
+ }
40
+ /**
41
+ * Update the modified timestamp
42
+ */
43
+ updateModified() {
44
+ this.modified = (/* @__PURE__ */ new Date()).toISOString();
45
+ }
46
+ /**
47
+ * Get number of changes
48
+ */
49
+ getChangeCount() {
50
+ return this.changes.length;
51
+ }
52
+ /**
53
+ * Get changes by type
54
+ */
55
+ getChangesByType(type) {
56
+ return this.changes.filter((c) => c.type === type);
57
+ }
58
+ /**
59
+ * Mark changeset as applied
60
+ */
61
+ markApplied() {
62
+ this.status = "applied", this.updateModified();
63
+ }
64
+ /**
65
+ * Mark changeset as reverted
66
+ */
67
+ markReverted() {
68
+ this.status = "reverted", this.updateModified();
69
+ }
70
+ /**
71
+ * Serialize to JSON
72
+ */
73
+ toJSON() {
74
+ return {
75
+ name: this.name,
76
+ description: this.description,
77
+ created: this.created,
78
+ modified: this.modified,
79
+ changes: this.changes,
80
+ status: this.status
81
+ };
82
+ }
83
+ /**
84
+ * Create from JSON data
85
+ */
86
+ static fromJSON(data) {
87
+ return new Changeset(data);
88
+ }
89
+ }
90
+ class ChangesetManager {
91
+ changesetsDir;
92
+ constructor(modelPath) {
93
+ this.changesetsDir = `${modelPath}/.dr/changesets`;
94
+ }
95
+ /**
96
+ * Create a new changeset
97
+ */
98
+ async create(name, description) {
99
+ const changeset = Changeset.create(name, description);
100
+ return await this.save(changeset), changeset;
101
+ }
102
+ /**
103
+ * Save a changeset to disk
104
+ */
105
+ async save(changeset) {
106
+ await ensureDir(this.changesetsDir);
107
+ const filePath = `${this.changesetsDir}/${this.sanitizeName(changeset.name)}.json`;
108
+ await writeJSON(filePath, changeset.toJSON(), !0);
109
+ }
110
+ /**
111
+ * Load a changeset by name
112
+ */
113
+ async load(name) {
114
+ const filePath = `${this.changesetsDir}/${this.sanitizeName(name)}.json`;
115
+ if (!await fileExists(filePath))
116
+ return null;
117
+ const data = await readJSON(filePath);
118
+ return Changeset.fromJSON(data);
119
+ }
120
+ /**
121
+ * List all changesets
122
+ */
123
+ async list() {
124
+ if (!await fileExists(this.changesetsDir))
125
+ return [];
126
+ const files = await (await import("fs/promises")).readdir(this.changesetsDir), changesets = [];
127
+ for (const file of files)
128
+ if (file.endsWith(".json")) {
129
+ const data = await readJSON(`${this.changesetsDir}/${file}`);
130
+ changesets.push(Changeset.fromJSON(data));
131
+ }
132
+ return changesets;
133
+ }
134
+ /**
135
+ * Delete a changeset
136
+ */
137
+ async delete(name) {
138
+ const filePath = `${this.changesetsDir}/${this.sanitizeName(name)}.json`;
139
+ if (!await fileExists(filePath))
140
+ throw new Error(`Changeset '${name}' not found`);
141
+ await (await import("fs/promises")).unlink(filePath);
142
+ }
143
+ /**
144
+ * Apply a changeset to the model
145
+ */
146
+ async apply(model, name) {
147
+ const changeset = await this.load(name);
148
+ if (!changeset)
149
+ throw new Error(`Changeset '${name}' not found`);
150
+ const result = {
151
+ changeset: name,
152
+ applied: 0,
153
+ failed: 0,
154
+ errors: []
155
+ };
156
+ for (const change of changeset.changes)
157
+ try {
158
+ const layer = await model.getLayer(change.layerName);
159
+ if (!layer)
160
+ throw new Error(`Layer '${change.layerName}' not found`);
161
+ if (change.type === "add" && change.after) {
162
+ if (!layer.listElements().some((e) => e.id === change.elementId)) {
163
+ const elementData = change.after, element = new Element({
164
+ id: change.elementId,
165
+ type: elementData.type || "unknown",
166
+ name: elementData.name || change.elementId,
167
+ description: elementData.description,
168
+ properties: elementData
169
+ });
170
+ layer.addElement(element), result.applied++;
171
+ }
172
+ } else if (change.type === "update" && change.after) {
173
+ const element = layer.getElement(change.elementId);
174
+ if (element) {
175
+ const elementData = change.after;
176
+ Object.assign(element, elementData), result.applied++;
177
+ }
178
+ } else change.type === "delete" && layer.getElement(change.elementId) && (layer.deleteElement(change.elementId), result.applied++);
179
+ } catch (error) {
180
+ result.failed++, result.errors.push({
181
+ change,
182
+ error: error instanceof Error ? error.message : String(error)
183
+ });
184
+ }
185
+ return result.failed === 0 && (changeset.markApplied(), await this.save(changeset)), result;
186
+ }
187
+ /**
188
+ * Revert a changeset from the model
189
+ */
190
+ async revert(model, name) {
191
+ const changeset = await this.load(name);
192
+ if (!changeset)
193
+ throw new Error(`Changeset '${name}' not found`);
194
+ const result = {
195
+ changeset: name,
196
+ reverted: 0,
197
+ failed: 0,
198
+ errors: []
199
+ };
200
+ for (let i = changeset.changes.length - 1; i >= 0; i--) {
201
+ const change = changeset.changes[i];
202
+ try {
203
+ const layer = await model.getLayer(change.layerName);
204
+ if (!layer)
205
+ throw new Error(`Layer '${change.layerName}' not found`);
206
+ if (change.type === "add")
207
+ layer.getElement(change.elementId) && (layer.deleteElement(change.elementId), result.reverted++);
208
+ else if (change.type === "update" && change.before) {
209
+ const element = layer.getElement(change.elementId);
210
+ if (element) {
211
+ const elementData = change.before;
212
+ Object.assign(element, elementData), result.reverted++;
213
+ }
214
+ } else if (change.type === "delete" && change.before && !layer.listElements().some((e) => e.id === change.elementId)) {
215
+ const elementData = change.before, element = new Element({
216
+ id: change.elementId,
217
+ type: elementData.type || "unknown",
218
+ name: elementData.name || change.elementId,
219
+ description: elementData.description,
220
+ properties: elementData
221
+ });
222
+ layer.addElement(element), result.reverted++;
223
+ }
224
+ } catch (error) {
225
+ result.failed++, result.errors.push({
226
+ change,
227
+ error: error instanceof Error ? error.message : String(error)
228
+ });
229
+ }
230
+ }
231
+ return result.failed === 0 && (changeset.markReverted(), await this.save(changeset)), result;
232
+ }
233
+ /**
234
+ * Sanitize changeset name for use as filename
235
+ */
236
+ sanitizeName(name) {
237
+ return name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
238
+ }
239
+ }
240
+ export {
241
+ Changeset,
242
+ ChangesetManager
243
+ };
244
+ //# sourceMappingURL=changeset.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/changeset.ts"],
4
+ "sourcesContent": ["/**\n * Changeset - Represents a collection of model changes\n *\n * Changesets allow grouping related modifications to the architecture model\n * and applying/reverting them as a unit.\n */\n\nimport { readJSON, writeJSON, ensureDir, fileExists } from '../utils/file-io.js';\nimport type { Model } from './model.js';\nimport { Element } from './element.js';\n\n/**\n * Represents a single change in a changeset\n */\nexport interface Change {\n type: 'add' | 'update' | 'delete';\n elementId: string;\n layerName: string;\n before?: Record<string, unknown>;\n after?: Record<string, unknown>;\n timestamp: string;\n}\n\n/**\n * Changeset metadata\n */\nexport interface ChangesetData {\n name: string;\n description?: string;\n created: string;\n modified: string;\n changes: Change[];\n status: 'draft' | 'applied' | 'reverted';\n}\n\n/**\n * Changeset class for managing model changes\n */\nexport class Changeset {\n name: string;\n description?: string;\n created: string;\n modified: string;\n changes: Change[] = [];\n status: 'draft' | 'applied' | 'reverted' = 'draft';\n\n constructor(data: ChangesetData) {\n this.name = data.name;\n this.description = data.description;\n this.created = data.created;\n this.modified = data.modified;\n this.changes = data.changes || [];\n this.status = data.status || 'draft';\n }\n\n /**\n * Create a new changeset\n */\n static create(name: string, description?: string): Changeset {\n const now = new Date().toISOString();\n return new Changeset({\n name,\n description,\n created: now,\n modified: now,\n changes: [],\n status: 'draft',\n });\n }\n\n /**\n * Add a change to the changeset\n */\n addChange(\n type: 'add' | 'update' | 'delete',\n elementId: string,\n layerName: string,\n before?: Record<string, unknown>,\n after?: Record<string, unknown>\n ): void {\n this.changes.push({\n type,\n elementId,\n layerName,\n before,\n after,\n timestamp: new Date().toISOString(),\n });\n this.updateModified();\n }\n\n /**\n * Update the modified timestamp\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Get number of changes\n */\n getChangeCount(): number {\n return this.changes.length;\n }\n\n /**\n * Get changes by type\n */\n getChangesByType(type: 'add' | 'update' | 'delete'): Change[] {\n return this.changes.filter((c) => c.type === type);\n }\n\n /**\n * Mark changeset as applied\n */\n markApplied(): void {\n this.status = 'applied';\n this.updateModified();\n }\n\n /**\n * Mark changeset as reverted\n */\n markReverted(): void {\n this.status = 'reverted';\n this.updateModified();\n }\n\n /**\n * Serialize to JSON\n */\n toJSON(): ChangesetData {\n return {\n name: this.name,\n description: this.description,\n created: this.created,\n modified: this.modified,\n changes: this.changes,\n status: this.status,\n };\n }\n\n /**\n * Create from JSON data\n */\n static fromJSON(data: ChangesetData): Changeset {\n return new Changeset(data);\n }\n}\n\n/**\n * Changeset Manager for storing and retrieving changesets\n */\nexport class ChangesetManager {\n private changesetsDir: string;\n\n constructor(modelPath: string) {\n this.changesetsDir = `${modelPath}/.dr/changesets`;\n }\n\n /**\n * Create a new changeset\n */\n async create(name: string, description?: string): Promise<Changeset> {\n const changeset = Changeset.create(name, description);\n await this.save(changeset);\n return changeset;\n }\n\n /**\n * Save a changeset to disk\n */\n async save(changeset: Changeset): Promise<void> {\n await ensureDir(this.changesetsDir);\n const filePath = `${this.changesetsDir}/${this.sanitizeName(changeset.name)}.json`;\n await writeJSON(filePath, changeset.toJSON(), true);\n }\n\n /**\n * Load a changeset by name\n */\n async load(name: string): Promise<Changeset | null> {\n const filePath = `${this.changesetsDir}/${this.sanitizeName(name)}.json`;\n\n if (!(await fileExists(filePath))) {\n return null;\n }\n\n const data = await readJSON<ChangesetData>(filePath);\n return Changeset.fromJSON(data);\n }\n\n /**\n * List all changesets\n */\n async list(): Promise<Changeset[]> {\n if (!(await fileExists(this.changesetsDir))) {\n return [];\n }\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.changesetsDir);\n const changesets: Changeset[] = [];\n\n for (const file of files) {\n if (file.endsWith('.json')) {\n const data = await readJSON<ChangesetData>(\n `${this.changesetsDir}/${file}`\n );\n changesets.push(Changeset.fromJSON(data));\n }\n }\n\n return changesets;\n }\n\n /**\n * Delete a changeset\n */\n async delete(name: string): Promise<void> {\n const filePath = `${this.changesetsDir}/${this.sanitizeName(name)}.json`;\n\n if (!(await fileExists(filePath))) {\n throw new Error(`Changeset '${name}' not found`);\n }\n\n const fs = await import('fs/promises');\n await fs.unlink(filePath);\n }\n\n /**\n * Apply a changeset to the model\n */\n async apply(model: Model, name: string): Promise<ApplyResult> {\n const changeset = await this.load(name);\n\n if (!changeset) {\n throw new Error(`Changeset '${name}' not found`);\n }\n\n const result: ApplyResult = {\n changeset: name,\n applied: 0,\n failed: 0,\n errors: [],\n };\n\n for (const change of changeset.changes) {\n try {\n const layer = await model.getLayer(change.layerName);\n\n if (!layer) {\n throw new Error(`Layer '${change.layerName}' not found`);\n }\n\n if (change.type === 'add' && change.after) {\n // Add element to layer\n const elements = layer.listElements();\n const elementExists = elements.some((e) => e.id === change.elementId);\n\n if (!elementExists) {\n // Create element based on after data\n const elementData = change.after as Record<string, unknown>;\n const element = new Element({\n id: change.elementId,\n type: (elementData.type as string) || 'unknown',\n name: (elementData.name as string) || change.elementId,\n description: elementData.description as string | undefined,\n properties: elementData,\n });\n layer.addElement(element);\n result.applied++;\n }\n } else if (change.type === 'update' && change.after) {\n // Update element in layer\n const element = layer.getElement(change.elementId);\n\n if (element) {\n const elementData = change.after as Record<string, unknown>;\n Object.assign(element, elementData);\n result.applied++;\n }\n } else if (change.type === 'delete') {\n // Delete element from layer\n const element = layer.getElement(change.elementId);\n\n if (element) {\n layer.deleteElement(change.elementId);\n result.applied++;\n }\n }\n } catch (error) {\n result.failed++;\n result.errors.push({\n change,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (result.failed === 0) {\n changeset.markApplied();\n await this.save(changeset);\n }\n\n return result;\n }\n\n /**\n * Revert a changeset from the model\n */\n async revert(model: Model, name: string): Promise<RevertResult> {\n const changeset = await this.load(name);\n\n if (!changeset) {\n throw new Error(`Changeset '${name}' not found`);\n }\n\n const result: RevertResult = {\n changeset: name,\n reverted: 0,\n failed: 0,\n errors: [],\n };\n\n // Process changes in reverse order\n for (let i = changeset.changes.length - 1; i >= 0; i--) {\n const change = changeset.changes[i];\n\n try {\n const layer = await model.getLayer(change.layerName);\n\n if (!layer) {\n throw new Error(`Layer '${change.layerName}' not found`);\n }\n\n // Reverse each operation\n if (change.type === 'add') {\n // Remove the element that was added\n const element = layer.getElement(change.elementId);\n\n if (element) {\n layer.deleteElement(change.elementId);\n result.reverted++;\n }\n } else if (change.type === 'update' && change.before) {\n // Restore to previous state\n const element = layer.getElement(change.elementId);\n\n if (element) {\n const elementData = change.before as Record<string, unknown>;\n Object.assign(element, elementData);\n result.reverted++;\n }\n } else if (change.type === 'delete' && change.before) {\n // Recreate the deleted element\n const elements = layer.listElements();\n const elementExists = elements.some((e) => e.id === change.elementId);\n\n if (!elementExists) {\n const elementData = change.before as Record<string, unknown>;\n const element = new Element({\n id: change.elementId,\n type: (elementData.type as string) || 'unknown',\n name: (elementData.name as string) || change.elementId,\n description: elementData.description as string | undefined,\n properties: elementData,\n });\n layer.addElement(element);\n result.reverted++;\n }\n }\n } catch (error) {\n result.failed++;\n result.errors.push({\n change,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (result.failed === 0) {\n changeset.markReverted();\n await this.save(changeset);\n }\n\n return result;\n }\n\n /**\n * Sanitize changeset name for use as filename\n */\n private sanitizeName(name: string): string {\n return name\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^a-z0-9-]/g, '');\n }\n}\n\n/**\n * Result of applying a changeset\n */\nexport interface ApplyResult {\n changeset: string;\n applied: number;\n failed: number;\n errors: Array<{\n change: Change;\n error: string;\n }>;\n}\n\n/**\n * Result of reverting a changeset\n */\nexport interface RevertResult {\n changeset: string;\n reverted: number;\n failed: number;\n errors: Array<{\n change: Change;\n error: string;\n }>;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,322 @@
1
+ import Graph from "graphology";
2
+ var TraceDirection;
3
+ (function(TraceDirection2) {
4
+ TraceDirection2.UP = "up", TraceDirection2.DOWN = "down", TraceDirection2.BOTH = "both";
5
+ })(TraceDirection || (TraceDirection = {}));
6
+ class DependencyTracker {
7
+ registry;
8
+ graph;
9
+ model;
10
+ constructor(registryOrGraph, model) {
11
+ registryOrGraph instanceof Graph ? this.graph = registryOrGraph : (this.registry = registryOrGraph, this.graph = registryOrGraph.getDependencyGraph()), this.model = model;
12
+ }
13
+ /**
14
+ * Get dependency graph
15
+ */
16
+ getGraph() {
17
+ return this.registry && (this.graph = this.registry.getDependencyGraph()), this.graph;
18
+ }
19
+ /**
20
+ * Trace dependencies from an element
21
+ *
22
+ * @param elementId - Element ID to start from
23
+ * @param direction - Direction to trace (up, down, both)
24
+ * @param maxDepth - Maximum depth to trace (null = unlimited)
25
+ * @returns Array of dependent element IDs
26
+ */
27
+ traceDependencies(elementId, direction = TraceDirection.BOTH, maxDepth = null) {
28
+ const graph = this.getGraph();
29
+ if (!graph.hasNode(elementId))
30
+ return [];
31
+ const dependencies = /* @__PURE__ */ new Set();
32
+ return (direction === TraceDirection.UP || direction === TraceDirection.BOTH) && this._traceUp(graph, elementId, maxDepth).forEach((id) => dependencies.add(id)), (direction === TraceDirection.DOWN || direction === TraceDirection.BOTH) && this._traceDown(graph, elementId, maxDepth).forEach((id) => dependencies.add(id)), Array.from(dependencies);
33
+ }
34
+ /**
35
+ * Trace upward dependencies (what element depends on - successors)
36
+ * Private helper matching Python CLI behavior
37
+ */
38
+ _traceUp(graph, elementId, maxDepth) {
39
+ return maxDepth === null ? this._getDescendants(graph, elementId) : this._getDescendantsLimited(graph, elementId, maxDepth);
40
+ }
41
+ /**
42
+ * Trace downward dependencies (what depends on element - predecessors)
43
+ * Private helper matching Python CLI behavior
44
+ */
45
+ _traceDown(graph, elementId, maxDepth) {
46
+ return maxDepth === null ? this._getAncestors(graph, elementId) : this._getAncestorsLimited(graph, elementId, maxDepth);
47
+ }
48
+ /**
49
+ * Get all descendants (reachable via outgoing edges) - DFS
50
+ * Equivalent to networkx.descendants()
51
+ */
52
+ _getDescendants(graph, startNode) {
53
+ const visited = /* @__PURE__ */ new Set(), stack = [startNode];
54
+ for (; stack.length > 0; ) {
55
+ const node = stack.pop();
56
+ if (graph.hasNode(node))
57
+ for (const neighbor of graph.outNeighbors(node))
58
+ visited.has(neighbor) || (visited.add(neighbor), stack.push(neighbor));
59
+ }
60
+ return visited;
61
+ }
62
+ /**
63
+ * Get ancestors (reachable via incoming edges) - DFS
64
+ * Equivalent to networkx.ancestors()
65
+ */
66
+ _getAncestors(graph, startNode) {
67
+ const visited = /* @__PURE__ */ new Set(), stack = [startNode];
68
+ for (; stack.length > 0; ) {
69
+ const node = stack.pop();
70
+ if (graph.hasNode(node))
71
+ for (const neighbor of graph.inNeighbors(node))
72
+ visited.has(neighbor) || (visited.add(neighbor), stack.push(neighbor));
73
+ }
74
+ return visited;
75
+ }
76
+ /**
77
+ * Get descendants within max_depth using BFS level-by-level
78
+ * Matches Python CLI behavior exactly
79
+ */
80
+ _getDescendantsLimited(graph, startNode, maxDepth) {
81
+ const descendants = /* @__PURE__ */ new Set();
82
+ let currentLevel = /* @__PURE__ */ new Set([startNode]);
83
+ for (let depth = 0; depth < maxDepth; depth++) {
84
+ const nextLevel = /* @__PURE__ */ new Set();
85
+ for (const node of currentLevel)
86
+ if (graph.hasNode(node))
87
+ for (const neighbor of graph.outNeighbors(node))
88
+ nextLevel.add(neighbor);
89
+ if (nextLevel.forEach((id) => descendants.add(id)), currentLevel = nextLevel, currentLevel.size === 0)
90
+ break;
91
+ }
92
+ return descendants;
93
+ }
94
+ /**
95
+ * Get ancestors within max_depth using BFS level-by-level
96
+ * Matches Python CLI behavior exactly
97
+ */
98
+ _getAncestorsLimited(graph, startNode, maxDepth) {
99
+ const ancestors = /* @__PURE__ */ new Set();
100
+ let currentLevel = /* @__PURE__ */ new Set([startNode]);
101
+ for (let depth = 0; depth < maxDepth; depth++) {
102
+ const nextLevel = /* @__PURE__ */ new Set();
103
+ for (const node of currentLevel)
104
+ if (graph.hasNode(node))
105
+ for (const neighbor of graph.inNeighbors(node))
106
+ nextLevel.add(neighbor);
107
+ if (nextLevel.forEach((id) => ancestors.add(id)), currentLevel = nextLevel, currentLevel.size === 0)
108
+ break;
109
+ }
110
+ return ancestors;
111
+ }
112
+ /**
113
+ * Find all dependency paths between two elements
114
+ *
115
+ * @param sourceId - Source element ID
116
+ * @param targetId - Target element ID
117
+ * @param maxPaths - Maximum number of paths to return
118
+ * @returns Array of dependency paths
119
+ */
120
+ findDependencyPaths(sourceId, targetId, maxPaths = 10) {
121
+ const graph = this.getGraph();
122
+ return !graph.hasNode(sourceId) || !graph.hasNode(targetId) ? [] : this._findAllSimplePaths(graph, sourceId, targetId, maxPaths).map((path) => {
123
+ const relationshipTypes = [];
124
+ for (let i = 0; i < path.length - 1; i++) {
125
+ const edgeAttrs = graph.getEdgeAttributes(path[i], path[i + 1]);
126
+ relationshipTypes.push(edgeAttrs?.type || "unknown");
127
+ }
128
+ return {
129
+ source: sourceId,
130
+ target: targetId,
131
+ path,
132
+ depth: path.length - 1,
133
+ relationship_types: relationshipTypes
134
+ };
135
+ });
136
+ }
137
+ /**
138
+ * Find all simple paths between source and target
139
+ * Implements DFS path-finding similar to networkx.all_simple_paths
140
+ */
141
+ _findAllSimplePaths(graph, source, target, maxPaths) {
142
+ const paths = [], visited = /* @__PURE__ */ new Set(), dfs = (current, path) => {
143
+ if (!(paths.length >= maxPaths)) {
144
+ if (current === target) {
145
+ paths.push([...path, current]);
146
+ return;
147
+ }
148
+ if (visited.add(current), path.push(current), graph.hasNode(current))
149
+ for (const neighbor of graph.outNeighbors(current))
150
+ visited.has(neighbor) || dfs(neighbor, path);
151
+ path.pop(), visited.delete(current);
152
+ }
153
+ };
154
+ return dfs(source, []), paths;
155
+ }
156
+ /**
157
+ * Find hub elements (elements with many connections)
158
+ *
159
+ * @param threshold - Minimum number of connections to be considered a hub
160
+ * @returns Array of [elementId, connectionCount] tuples, sorted by count descending
161
+ */
162
+ getHubElements(threshold = 10) {
163
+ const graph = this.getGraph(), hubs = [];
164
+ for (const node of graph.nodes()) {
165
+ const degree = graph.degree(node);
166
+ degree >= threshold && hubs.push([node, degree]);
167
+ }
168
+ return hubs.sort((a, b) => b[1] - a[1]), hubs;
169
+ }
170
+ /**
171
+ * Get direct dependents (incoming edges)
172
+ */
173
+ getDependents(elementId) {
174
+ const graph = this.getGraph();
175
+ return graph.hasNode(elementId) ? graph.inNeighbors(elementId) : [];
176
+ }
177
+ /**
178
+ * Get direct dependencies (outgoing edges)
179
+ */
180
+ getDependencies(elementId) {
181
+ const graph = this.getGraph();
182
+ return graph.hasNode(elementId) ? graph.outNeighbors(elementId) : [];
183
+ }
184
+ /**
185
+ * Get all elements that transitively depend on the given element (ancestors)
186
+ */
187
+ getTransitiveDependents(elementId) {
188
+ const graph = this.getGraph();
189
+ return graph.hasNode(elementId) ? Array.from(this._getAncestors(graph, elementId)) : [];
190
+ }
191
+ /**
192
+ * Get all elements this element transitively depends on (descendants)
193
+ */
194
+ getTransitiveDependencies(elementId) {
195
+ const graph = this.getGraph();
196
+ return graph.hasNode(elementId) ? Array.from(this._getDescendants(graph, elementId)) : [];
197
+ }
198
+ /**
199
+ * Detect cycles in the dependency graph
200
+ */
201
+ detectCycles() {
202
+ const graph = this.getGraph(), visited = /* @__PURE__ */ new Set(), stack = /* @__PURE__ */ new Set(), path = [], cycleMap = /* @__PURE__ */ new Map(), canonicalKey = (cycle) => {
203
+ const unique = Array.from(new Set(cycle));
204
+ return unique.sort(), unique.join("|");
205
+ }, dfs = (node) => {
206
+ visited.add(node), stack.add(node), path.push(node);
207
+ for (const neighbor of graph.outNeighbors(node))
208
+ if (!visited.has(neighbor))
209
+ dfs(neighbor);
210
+ else if (stack.has(neighbor)) {
211
+ const idx = path.indexOf(neighbor);
212
+ if (idx !== -1) {
213
+ const cycle = path.slice(idx), key = canonicalKey(cycle);
214
+ cycleMap.has(key) || cycleMap.set(key, [...cycle]);
215
+ }
216
+ }
217
+ stack.delete(node), path.pop();
218
+ };
219
+ for (const node of graph.nodes())
220
+ visited.has(node) || dfs(node);
221
+ return Array.from(cycleMap.values());
222
+ }
223
+ /**
224
+ * Calculate basic graph metrics
225
+ */
226
+ getMetrics() {
227
+ const graph = this.getGraph(), nodeCount = graph.order, edgeCount = graph.size, cycleCount = this.detectCycles().length, visited = /* @__PURE__ */ new Set();
228
+ let connectedComponents = 0;
229
+ const bfs = (start) => {
230
+ const queue = [start];
231
+ for (visited.add(start); queue.length > 0; ) {
232
+ const node = queue.shift(), neighbors = /* @__PURE__ */ new Set([
233
+ ...graph.outNeighbors(node),
234
+ ...graph.inNeighbors(node)
235
+ ]);
236
+ for (const neighbor of neighbors)
237
+ visited.has(neighbor) || (visited.add(neighbor), queue.push(neighbor));
238
+ }
239
+ };
240
+ for (const node of graph.nodes())
241
+ visited.has(node) || (connectedComponents += 1, bfs(node));
242
+ return { nodeCount, edgeCount, cycleCount, connectedComponents };
243
+ }
244
+ /**
245
+ * Find elements with no incoming edges
246
+ */
247
+ findSourceElements() {
248
+ const graph = this.getGraph(), sources = [];
249
+ for (const node of graph.nodes())
250
+ graph.inNeighbors(node).length === 0 && sources.push(node);
251
+ return sources;
252
+ }
253
+ /**
254
+ * Find elements with no outgoing edges
255
+ */
256
+ findSinkElements() {
257
+ const graph = this.getGraph(), sinks = [];
258
+ for (const node of graph.nodes())
259
+ graph.outNeighbors(node).length === 0 && sinks.push(node);
260
+ return sinks;
261
+ }
262
+ /**
263
+ * Calculate how many elements would be impacted if this element changed
264
+ * (all ancestors/predecessors excluding the element itself)
265
+ */
266
+ getImpactRadius(elementId) {
267
+ const graph = this.getGraph();
268
+ return graph.hasNode(elementId) ? this._getAncestors(graph, elementId).size : 0;
269
+ }
270
+ /**
271
+ * Get longest dependency chain depth from this element (outgoing edges)
272
+ */
273
+ getDependencyDepth(elementId) {
274
+ const graph = this.getGraph();
275
+ if (!graph.hasNode(elementId))
276
+ return -1;
277
+ const memo = /* @__PURE__ */ new Map(), stack = /* @__PURE__ */ new Set(), dfsDepth = (node) => {
278
+ if (memo.has(node))
279
+ return memo.get(node);
280
+ if (stack.has(node))
281
+ return 0;
282
+ stack.add(node);
283
+ let maxDepth = 0;
284
+ for (const neighbor of graph.outNeighbors(node)) {
285
+ const neighborDepth = dfsDepth(neighbor);
286
+ maxDepth = Math.max(maxDepth, 1 + neighborDepth);
287
+ }
288
+ return stack.delete(node), memo.set(node, maxDepth), maxDepth;
289
+ };
290
+ return dfsDepth(elementId);
291
+ }
292
+ /**
293
+ * Group dependencies by layer (requires model)
294
+ */
295
+ getDependencyLayers(elementId) {
296
+ if (!this.model)
297
+ return {};
298
+ const byLayer = {}, dependencies = this.traceDependencies(elementId, TraceDirection.BOTH);
299
+ for (const depId of dependencies) {
300
+ const element = this.model.getElementById(depId);
301
+ !element || !element.layer || (byLayer[element.layer] || (byLayer[element.layer] = []), byLayer[element.layer].push(depId));
302
+ }
303
+ return byLayer;
304
+ }
305
+ /**
306
+ * Find elements with no references (requires model)
307
+ */
308
+ getOrphanedElements() {
309
+ if (!this.model)
310
+ return [];
311
+ const graph = this.getGraph(), orphaned = [];
312
+ for (const layer of this.model.layers.values())
313
+ for (const element of layer.elements.values())
314
+ graph.hasNode(element.id) ? graph.degree(element.id) === 0 && orphaned.push(element) : orphaned.push(element);
315
+ return orphaned;
316
+ }
317
+ }
318
+ export {
319
+ DependencyTracker,
320
+ TraceDirection
321
+ };
322
+ //# sourceMappingURL=dependency-tracker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/dependency-tracker.ts"],
4
+ "sourcesContent": ["import Graph from 'graphology';\nimport { ReferenceRegistry } from './reference-registry.js';\nimport { Model } from './model.js';\n\n/**\n * Direction to trace dependencies (matches Python CLI TraceDirection enum)\n */\nexport enum TraceDirection {\n UP = 'up', // Find what this depends on (successors/descendants)\n DOWN = 'down', // Find what depends on this (predecessors/ancestors)\n BOTH = 'both' // Both directions\n}\n\n/**\n * Represents a dependency path between two elements\n */\nexport interface DependencyPath {\n source: string;\n target: string;\n path: string[];\n depth: number;\n relationship_types: string[];\n}\n\n/**\n * Dependency tracker - analyzes dependencies between elements\n *\n * Uses reference registry and graph algorithms to provide\n * dependency analysis and impact assessment.\n *\n * Matches Python CLI behavior from dependency_tracker.py\n */\nexport class DependencyTracker {\n private registry?: ReferenceRegistry;\n private graph: Graph;\n private model?: Model;\n\n constructor(registryOrGraph: ReferenceRegistry | Graph, model?: Model) {\n if (registryOrGraph instanceof Graph) {\n this.graph = registryOrGraph;\n } else {\n this.registry = registryOrGraph;\n this.graph = registryOrGraph.getDependencyGraph();\n }\n this.model = model;\n }\n\n /**\n * Get dependency graph\n */\n private getGraph(): Graph {\n if (this.registry) {\n // Rebuild from registry each time to reflect new references\n this.graph = this.registry.getDependencyGraph();\n }\n return this.graph;\n }\n\n /**\n * Trace dependencies from an element\n *\n * @param elementId - Element ID to start from\n * @param direction - Direction to trace (up, down, both)\n * @param maxDepth - Maximum depth to trace (null = unlimited)\n * @returns Array of dependent element IDs\n */\n traceDependencies(\n elementId: string,\n direction: TraceDirection = TraceDirection.BOTH,\n maxDepth: number | null = null\n ): string[] {\n const graph = this.getGraph();\n\n if (!graph.hasNode(elementId)) {\n return [];\n }\n\n const dependencies = new Set<string>();\n\n // Trace upward (what this element depends on - successors)\n if (direction === TraceDirection.UP || direction === TraceDirection.BOTH) {\n const upDeps = this._traceUp(graph, elementId, maxDepth);\n upDeps.forEach(id => dependencies.add(id));\n }\n\n // Trace downward (what depends on this element - predecessors)\n if (direction === TraceDirection.DOWN || direction === TraceDirection.BOTH) {\n const downDeps = this._traceDown(graph, elementId, maxDepth);\n downDeps.forEach(id => dependencies.add(id));\n }\n\n return Array.from(dependencies);\n }\n\n /**\n * Trace upward dependencies (what element depends on - successors)\n * Private helper matching Python CLI behavior\n */\n private _traceUp(graph: Graph, elementId: string, maxDepth: number | null): Set<string> {\n if (maxDepth === null) {\n // Get all descendants (unlimited depth)\n return this._getDescendants(graph, elementId);\n } else {\n // Get descendants within depth using BFS\n return this._getDescendantsLimited(graph, elementId, maxDepth);\n }\n }\n\n /**\n * Trace downward dependencies (what depends on element - predecessors)\n * Private helper matching Python CLI behavior\n */\n private _traceDown(graph: Graph, elementId: string, maxDepth: number | null): Set<string> {\n if (maxDepth === null) {\n // Get all ancestors (unlimited depth)\n return this._getAncestors(graph, elementId);\n } else {\n // Get ancestors within depth using BFS\n return this._getAncestorsLimited(graph, elementId, maxDepth);\n }\n }\n\n /**\n * Get all descendants (reachable via outgoing edges) - DFS\n * Equivalent to networkx.descendants()\n */\n private _getDescendants(graph: Graph, startNode: string): Set<string> {\n const visited = new Set<string>();\n const stack = [startNode];\n\n while (stack.length > 0) {\n const node = stack.pop()!;\n\n if (!graph.hasNode(node)) continue;\n\n for (const neighbor of graph.outNeighbors(node)) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n stack.push(neighbor);\n }\n }\n }\n\n return visited;\n }\n\n /**\n * Get ancestors (reachable via incoming edges) - DFS\n * Equivalent to networkx.ancestors()\n */\n private _getAncestors(graph: Graph, startNode: string): Set<string> {\n const visited = new Set<string>();\n const stack = [startNode];\n\n while (stack.length > 0) {\n const node = stack.pop()!;\n\n if (!graph.hasNode(node)) continue;\n\n for (const neighbor of graph.inNeighbors(node)) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n stack.push(neighbor);\n }\n }\n }\n\n return visited;\n }\n\n /**\n * Get descendants within max_depth using BFS level-by-level\n * Matches Python CLI behavior exactly\n */\n private _getDescendantsLimited(graph: Graph, startNode: string, maxDepth: number): Set<string> {\n const descendants = new Set<string>();\n let currentLevel = new Set([startNode]);\n\n for (let depth = 0; depth < maxDepth; depth++) {\n const nextLevel = new Set<string>();\n\n for (const node of currentLevel) {\n if (!graph.hasNode(node)) continue;\n\n for (const neighbor of graph.outNeighbors(node)) {\n nextLevel.add(neighbor);\n }\n }\n\n nextLevel.forEach(id => descendants.add(id));\n currentLevel = nextLevel;\n\n if (currentLevel.size === 0) {\n break;\n }\n }\n\n return descendants;\n }\n\n /**\n * Get ancestors within max_depth using BFS level-by-level\n * Matches Python CLI behavior exactly\n */\n private _getAncestorsLimited(graph: Graph, startNode: string, maxDepth: number): Set<string> {\n const ancestors = new Set<string>();\n let currentLevel = new Set([startNode]);\n\n for (let depth = 0; depth < maxDepth; depth++) {\n const nextLevel = new Set<string>();\n\n for (const node of currentLevel) {\n if (!graph.hasNode(node)) continue;\n\n for (const neighbor of graph.inNeighbors(node)) {\n nextLevel.add(neighbor);\n }\n }\n\n nextLevel.forEach(id => ancestors.add(id));\n currentLevel = nextLevel;\n\n if (currentLevel.size === 0) {\n break;\n }\n }\n\n return ancestors;\n }\n\n /**\n * Find all dependency paths between two elements\n *\n * @param sourceId - Source element ID\n * @param targetId - Target element ID\n * @param maxPaths - Maximum number of paths to return\n * @returns Array of dependency paths\n */\n findDependencyPaths(\n sourceId: string,\n targetId: string,\n maxPaths: number = 10\n ): DependencyPath[] {\n const graph = this.getGraph();\n\n if (!graph.hasNode(sourceId) || !graph.hasNode(targetId)) {\n return [];\n }\n\n // Find all simple paths (no cycles)\n const paths = this._findAllSimplePaths(graph, sourceId, targetId, maxPaths);\n\n // Convert to DependencyPath objects\n return paths.map(path => {\n // Get relationship types along path\n const relationshipTypes: string[] = [];\n for (let i = 0; i < path.length - 1; i++) {\n const edgeAttrs = graph.getEdgeAttributes(path[i], path[i + 1]);\n relationshipTypes.push(edgeAttrs?.type || 'unknown');\n }\n\n return {\n source: sourceId,\n target: targetId,\n path,\n depth: path.length - 1,\n relationship_types: relationshipTypes\n };\n });\n }\n\n /**\n * Find all simple paths between source and target\n * Implements DFS path-finding similar to networkx.all_simple_paths\n */\n private _findAllSimplePaths(\n graph: Graph,\n source: string,\n target: string,\n maxPaths: number\n ): string[][] {\n const paths: string[][] = [];\n const visited = new Set<string>();\n\n const dfs = (current: string, path: string[]) => {\n if (paths.length >= maxPaths) {\n return;\n }\n\n if (current === target) {\n paths.push([...path, current]);\n return;\n }\n\n visited.add(current);\n path.push(current);\n\n if (graph.hasNode(current)) {\n for (const neighbor of graph.outNeighbors(current)) {\n if (!visited.has(neighbor)) {\n dfs(neighbor, path);\n }\n }\n }\n\n path.pop();\n visited.delete(current);\n };\n\n dfs(source, []);\n return paths;\n }\n\n /**\n * Find hub elements (elements with many connections)\n *\n * @param threshold - Minimum number of connections to be considered a hub\n * @returns Array of [elementId, connectionCount] tuples, sorted by count descending\n */\n getHubElements(threshold: number = 10): [string, number][] {\n const graph = this.getGraph();\n const hubs: [string, number][] = [];\n\n for (const node of graph.nodes()) {\n const degree = graph.degree(node);\n if (degree >= threshold) {\n hubs.push([node, degree]);\n }\n }\n\n // Sort by degree descending\n hubs.sort((a, b) => b[1] - a[1]);\n\n return hubs;\n }\n\n /**\n * Get direct dependents (incoming edges)\n */\n getDependents(elementId: string): string[] {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return [];\n return graph.inNeighbors(elementId);\n }\n\n /**\n * Get direct dependencies (outgoing edges)\n */\n getDependencies(elementId: string): string[] {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return [];\n return graph.outNeighbors(elementId);\n }\n\n /**\n * Get all elements that transitively depend on the given element (ancestors)\n */\n getTransitiveDependents(elementId: string): string[] {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return [];\n return Array.from(this._getAncestors(graph, elementId));\n }\n\n /**\n * Get all elements this element transitively depends on (descendants)\n */\n getTransitiveDependencies(elementId: string): string[] {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return [];\n return Array.from(this._getDescendants(graph, elementId));\n }\n\n /**\n * Detect cycles in the dependency graph\n */\n detectCycles(): string[][] {\n const graph = this.getGraph();\n const visited = new Set<string>();\n const stack = new Set<string>();\n const path: string[] = [];\n const cycleMap = new Map<string, string[]>();\n\n const canonicalKey = (cycle: string[]): string => {\n // Normalize cycle representation to avoid duplicates\n const unique = Array.from(new Set(cycle));\n unique.sort();\n return unique.join('|');\n };\n\n const dfs = (node: string) => {\n visited.add(node);\n stack.add(node);\n path.push(node);\n\n for (const neighbor of graph.outNeighbors(node)) {\n if (!visited.has(neighbor)) {\n dfs(neighbor);\n } else if (stack.has(neighbor)) {\n // Found a cycle, capture the slice from neighbor to current\n const idx = path.indexOf(neighbor);\n if (idx !== -1) {\n const cycle = path.slice(idx);\n const key = canonicalKey(cycle);\n if (!cycleMap.has(key)) {\n cycleMap.set(key, [...cycle]);\n }\n }\n }\n }\n\n stack.delete(node);\n path.pop();\n };\n\n for (const node of graph.nodes()) {\n if (!visited.has(node)) {\n dfs(node);\n }\n }\n\n return Array.from(cycleMap.values());\n }\n\n /**\n * Calculate basic graph metrics\n */\n getMetrics(): { nodeCount: number; edgeCount: number; cycleCount: number; connectedComponents: number } {\n const graph = this.getGraph();\n\n const nodeCount = graph.order;\n const edgeCount = graph.size;\n const cycleCount = this.detectCycles().length;\n\n // Count weakly connected components\n const visited = new Set<string>();\n let connectedComponents = 0;\n\n const bfs = (start: string) => {\n const queue = [start];\n visited.add(start);\n\n while (queue.length > 0) {\n const node = queue.shift()!;\n const neighbors = new Set<string>([\n ...graph.outNeighbors(node),\n ...graph.inNeighbors(node)\n ]);\n\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n };\n\n for (const node of graph.nodes()) {\n if (!visited.has(node)) {\n connectedComponents += 1;\n bfs(node);\n }\n }\n\n return { nodeCount, edgeCount, cycleCount, connectedComponents };\n }\n\n /**\n * Find elements with no incoming edges\n */\n findSourceElements(): string[] {\n const graph = this.getGraph();\n const sources: string[] = [];\n\n for (const node of graph.nodes()) {\n if (graph.inNeighbors(node).length === 0) {\n sources.push(node);\n }\n }\n\n return sources;\n }\n\n /**\n * Find elements with no outgoing edges\n */\n findSinkElements(): string[] {\n const graph = this.getGraph();\n const sinks: string[] = [];\n\n for (const node of graph.nodes()) {\n if (graph.outNeighbors(node).length === 0) {\n sinks.push(node);\n }\n }\n\n return sinks;\n }\n\n /**\n * Calculate how many elements would be impacted if this element changed\n * (all ancestors/predecessors excluding the element itself)\n */\n getImpactRadius(elementId: string): number {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return 0;\n const impacted = this._getAncestors(graph, elementId);\n return impacted.size;\n }\n\n /**\n * Get longest dependency chain depth from this element (outgoing edges)\n */\n getDependencyDepth(elementId: string): number {\n const graph = this.getGraph();\n if (!graph.hasNode(elementId)) return -1;\n\n const memo = new Map<string, number>();\n const stack = new Set<string>();\n\n const dfsDepth = (node: string): number => {\n if (memo.has(node)) return memo.get(node)!;\n if (stack.has(node)) return 0; // break cycles\n\n stack.add(node);\n let maxDepth = 0;\n\n for (const neighbor of graph.outNeighbors(node)) {\n const neighborDepth = dfsDepth(neighbor);\n maxDepth = Math.max(maxDepth, 1 + neighborDepth);\n }\n\n stack.delete(node);\n memo.set(node, maxDepth);\n return maxDepth;\n };\n\n return dfsDepth(elementId);\n }\n\n /**\n * Group dependencies by layer (requires model)\n */\n getDependencyLayers(elementId: string): Record<string, string[]> {\n if (!this.model) return {};\n\n const byLayer: Record<string, string[]> = {};\n const dependencies = this.traceDependencies(elementId, TraceDirection.BOTH);\n\n for (const depId of dependencies) {\n const element = this.model.getElementById(depId);\n if (!element || !element.layer) continue;\n\n if (!byLayer[element.layer]) {\n byLayer[element.layer] = [];\n }\n byLayer[element.layer].push(depId);\n }\n\n return byLayer;\n }\n\n /**\n * Find elements with no references (requires model)\n */\n getOrphanedElements(): any[] {\n if (!this.model) return [];\n\n const graph = this.getGraph();\n const orphaned: any[] = [];\n\n for (const layer of this.model.layers.values()) {\n for (const element of layer.elements.values()) {\n if (!graph.hasNode(element.id)) {\n orphaned.push(element);\n } else if (graph.degree(element.id) === 0) {\n orphaned.push(element);\n }\n }\n }\n\n return orphaned;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["TraceDirection"]
7
+ }