@documentation-robotics/cli 0.1.7 → 0.1.9

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 (123) hide show
  1. package/README.md +18 -17
  2. package/dist/audit/nodes/ai/prompts.js +1 -0
  3. package/dist/audit/nodes/ai/prompts.js.map +2 -2
  4. package/dist/audit/relationships/ai/prompts.js +20 -29
  5. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  6. package/dist/audit/relationships/analysis/gap-analyzer.js.map +1 -1
  7. package/dist/audit/relationships/analysis/layer-templates.js +36 -0
  8. package/dist/audit/relationships/analysis/layer-templates.js.map +2 -2
  9. package/dist/cli.js +8 -8
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/reports.js +3 -3
  12. package/dist/commands/reports.js.map +1 -1
  13. package/dist/commands/show.js.map +1 -1
  14. package/dist/commands/upgrade.js +1 -1
  15. package/dist/commands/upgrade.js.map +1 -1
  16. package/dist/commands/validate.js.map +1 -1
  17. package/dist/commands/version.js +1 -1
  18. package/dist/commands/visualize.js +2 -8
  19. package/dist/commands/visualize.js.map +2 -2
  20. package/dist/core/layers.js +1 -0
  21. package/dist/core/layers.js.map +2 -2
  22. package/dist/core/migration-registry.js +59 -2
  23. package/dist/core/migration-registry.js.map +2 -2
  24. package/dist/core/model.js +3 -16
  25. package/dist/core/model.js.map +2 -2
  26. package/dist/core/spec-loader.js.map +1 -1
  27. package/dist/core/stats-collector.js +2 -0
  28. package/dist/core/stats-collector.js.map +2 -2
  29. package/dist/export/archimate-exporter.js.map +2 -2
  30. package/dist/export/json-schema-exporter.js.map +1 -1
  31. package/dist/export/markdown-exporter.js.map +1 -1
  32. package/dist/export/markdown-utils.js +1 -0
  33. package/dist/export/markdown-utils.js.map +2 -2
  34. package/dist/export/mermaid-markdown-exporter.js.map +1 -1
  35. package/dist/export/openapi-exporter.js.map +1 -1
  36. package/dist/export/plantuml-exporter.js.map +1 -1
  37. package/dist/export/report-formatters.js +2 -2
  38. package/dist/export/report-formatters.js.map +1 -1
  39. package/dist/export/types.js +1 -0
  40. package/dist/export/types.js.map +2 -2
  41. package/dist/generated/layer-registry.js +44 -31
  42. package/dist/generated/layer-registry.js.map +2 -2
  43. package/dist/generated/layer-types.js +1 -1
  44. package/dist/generated/layer-types.js.map +2 -2
  45. package/dist/generated/node-types.js +45 -0
  46. package/dist/generated/node-types.js.map +2 -2
  47. package/dist/generated/relationship-index.js +201 -19
  48. package/dist/generated/relationship-index.js.map +2 -2
  49. package/dist/import/archimate-importer.js.map +1 -1
  50. package/dist/import/openapi-importer.js.map +1 -1
  51. package/dist/integrations/claude_code/agents/dr-advisor.md +14 -13
  52. package/dist/integrations/claude_code/agents/dr-extractor.md +2 -2
  53. package/dist/integrations/claude_code/commands/dr-info.md +1 -1
  54. package/dist/integrations/claude_code/commands/dr-init.md +3 -3
  55. package/dist/integrations/claude_code/commands/dr-map.md +2 -2
  56. package/dist/integrations/claude_code/commands/dr-model.md +1 -1
  57. package/dist/integrations/claude_code/commands/dr-relate.md +14 -4
  58. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +3 -3
  59. package/dist/integrations/claude_code/docs/QUICK_START.md +1 -1
  60. package/dist/integrations/claude_code/docs/README.md +2 -2
  61. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +2 -2
  62. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +679 -0
  63. package/dist/integrations/claude_code/skills/{dr_08_dataset_layer → dr_09_dataset_layer}/SKILL.md +1 -1
  64. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +2 -2
  65. package/dist/integrations/github_copilot/agents/dr-advisor.md +14 -13
  66. package/dist/integrations/github_copilot/agents/dr-architect.md +15 -14
  67. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +2 -2
  68. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +253 -0
  69. package/dist/reports/model-report-orchestrator.js +3 -3
  70. package/dist/reports/model-report-orchestrator.js.map +1 -1
  71. package/dist/schemas/bundled/analyzers/cbm.json +2 -2
  72. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  73. package/dist/schemas/bundled/api.json +3 -3
  74. package/dist/schemas/bundled/apm.json +3 -3
  75. package/dist/schemas/bundled/application.json +3 -3
  76. package/dist/schemas/bundled/base.json +29 -1
  77. package/dist/schemas/bundled/business.json +1 -1
  78. package/dist/schemas/bundled/data-model.json +3 -3
  79. package/dist/schemas/bundled/data-store.json +3 -3
  80. package/dist/schemas/bundled/manifest.json +20 -13
  81. package/dist/schemas/bundled/motivation.json +1 -1
  82. package/dist/schemas/bundled/navigation.json +3 -3
  83. package/dist/schemas/bundled/product.json +469 -0
  84. package/dist/schemas/bundled/security.json +3 -3
  85. package/dist/schemas/bundled/technology.json +3 -3
  86. package/dist/schemas/bundled/testing.json +3 -3
  87. package/dist/schemas/bundled/ux.json +3 -3
  88. package/dist/server/server.js +148 -101
  89. package/dist/server/server.js.map +2 -2
  90. package/dist/server-entry.js.map +2 -2
  91. package/dist/telemetry/index.js +1 -1
  92. package/dist/utils/spec-installer.js +2 -2
  93. package/dist/utils/spec-installer.js.map +1 -1
  94. package/dist/utils/spec-version.js +1 -1
  95. package/dist/utils/spec-version.js.map +1 -1
  96. package/dist/viewer/README.md +6 -6
  97. package/dist/viewer/assets/main-D114QwJo.css +1 -0
  98. package/dist/viewer/assets/main-DyuokeQj.js +81 -0
  99. package/dist/viewer/index.html +2 -2
  100. package/dist/viewer/manifest.json +10 -10
  101. package/dist/viewer/version.json +1 -1
  102. package/package.json +4 -1
  103. package/dist/viewer/assets/main-CY0Xufuj.css +0 -1
  104. package/dist/viewer/assets/main-DtgErnwF.js +0 -81
  105. /package/dist/integrations/claude_code/skills/{dr_03_security_layer → dr_04_security_layer}/SKILL.md +0 -0
  106. /package/dist/integrations/claude_code/skills/{dr_04_application_layer → dr_05_application_layer}/SKILL.md +0 -0
  107. /package/dist/integrations/claude_code/skills/{dr_05_technology_layer → dr_06_technology_layer}/SKILL.md +0 -0
  108. /package/dist/integrations/claude_code/skills/{dr_06_api_layer → dr_07_api_layer}/SKILL.md +0 -0
  109. /package/dist/integrations/claude_code/skills/{dr_07_data_model_layer → dr_08_data_model_layer}/SKILL.md +0 -0
  110. /package/dist/integrations/claude_code/skills/{dr_09_ux_layer → dr_10_ux_layer}/SKILL.md +0 -0
  111. /package/dist/integrations/claude_code/skills/{dr_10_navigation_layer → dr_11_navigation_layer}/SKILL.md +0 -0
  112. /package/dist/integrations/claude_code/skills/{dr_11_apm_layer → dr_12_apm_layer}/SKILL.md +0 -0
  113. /package/dist/integrations/claude_code/skills/{dr_12_testing_layer → dr_13_testing_layer}/SKILL.md +0 -0
  114. /package/dist/integrations/github_copilot/skills/{dr_03_security_layer → dr_04_security_layer}/SKILL.md +0 -0
  115. /package/dist/integrations/github_copilot/skills/{dr_04_application_layer → dr_05_application_layer}/SKILL.md +0 -0
  116. /package/dist/integrations/github_copilot/skills/{dr_05_technology_layer → dr_06_technology_layer}/SKILL.md +0 -0
  117. /package/dist/integrations/github_copilot/skills/{dr_06_api_layer → dr_07_api_layer}/SKILL.md +0 -0
  118. /package/dist/integrations/github_copilot/skills/{dr_07_data_model_layer → dr_08_data_model_layer}/SKILL.md +0 -0
  119. /package/dist/integrations/github_copilot/skills/{dr_08_dataset_layer → dr_09_dataset_layer}/SKILL.md +0 -0
  120. /package/dist/integrations/github_copilot/skills/{dr_09_ux_layer → dr_10_ux_layer}/SKILL.md +0 -0
  121. /package/dist/integrations/github_copilot/skills/{dr_10_navigation_layer → dr_11_navigation_layer}/SKILL.md +0 -0
  122. /package/dist/integrations/github_copilot/skills/{dr_11_apm_layer → dr_12_apm_layer}/SKILL.md +0 -0
  123. /package/dist/integrations/github_copilot/skills/{dr_12_testing_layer → dr_13_testing_layer}/SKILL.md +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/model.ts"],
4
- "sourcesContent": ["import { Layer } from \"./layer.js\";\nimport { Manifest } from \"./manifest.js\";\nimport { Element } from \"./element.js\";\nimport { GraphModel, type GraphEdge } from \"./graph-model.js\";\nimport { VirtualProjectionEngine } from \"./virtual-projection.js\";\nimport { StagedChangesetStorage } from \"./staged-changeset-storage.js\";\nimport { Relationships } from \"./relationships.js\";\nimport { CANONICAL_LAYER_NAMES } from \"./layers.js\";\nimport type { StagedChange } from \"./changeset.js\";\nimport { ensureDir, writeFile } from \"../utils/file-io.js\";\nimport { getCliVersion } from \"../utils/spec-version.js\";\nimport { startSpan, endSpan, startActiveSpan } from \"../telemetry/index.js\";\nimport { findProjectRoot } from \"../utils/project-paths.js\";\nimport { getNodeType } from \"../generated/node-types.js\";\nimport type { SpecNodeId } from \"../generated/node-types.js\";\nimport type { ManifestData, ModelOptions } from \"../types/index.js\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport crypto from \"node:crypto\";\n\n/**\n * Derive a deterministic UUID from a path string using SHA-256.\n * Produces a stable UUID4-like value so migration doesn't regenerate UUIDs on every load.\n */\nfunction deterministicUUID(path: string): string {\n const hash = crypto.createHash(\"sha256\").update(path).digest(\"hex\");\n return [\n hash.slice(0, 8),\n hash.slice(8, 12),\n \"4\" + hash.slice(13, 16), // version 4 marker\n ((parseInt(hash.slice(16, 18), 16) & 0x3f) | 0x80).toString(16) + hash.slice(18, 20), // variant\n hash.slice(20, 32),\n ].join(\"-\");\n}\n\n/**\n * Convert a PascalCase title to snake_case.\n * Handles acronyms correctly: \"UXApplication\" \u2192 \"ux_application\", \"OpenAPIDocument\" \u2192 \"open_api_document\".\n */\nfunction titleToSnakeCase(title: string): string {\n return title\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1_$2\") // \"APIDoc\" \u2192 \"API_Doc\"\n .replace(/([a-z\\d])([A-Z])/g, \"$1_$2\") // \"appComponent\" \u2192 \"app_Component\"\n .toLowerCase();\n}\n\n/**\n * Derive a snake_case file stem for a given element type.\n * Uses the spec node title for accurate word boundaries (e.g., \"InputSpacePartition\" \u2192 \"input_space_partition\").\n * Falls back to the raw type string when the title is unavailable.\n */\nfunction typeToSnakeStem(specNodeId: string | undefined, type: string): string {\n if (specNodeId) {\n const nodeType = getNodeType(specNodeId as SpecNodeId);\n if (nodeType?.title) {\n return titleToSnakeCase(nodeType.title);\n }\n }\n return type;\n}\n\n// Fallback for runtime environments where TELEMETRY_ENABLED is not defined by esbuild\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\n/**\n * Model class representing the complete architecture model\n * Uses GraphModel internally for graph-based storage\n */\nexport class Model {\n rootPath: string;\n manifest: Manifest;\n graph: GraphModel;\n layers: Map<string, Layer>;\n relationships: Relationships;\n lazyLoad: boolean;\n private loadedLayers: Set<string>;\n private virtualProjectionEngine?: VirtualProjectionEngine;\n /** ID of the currently active changeset, or null if none is active. Set during load(). */\n private _activeChangesetId: string | null = null;\n /** Layer names that exist only in staged changes (not yet committed to base model). */\n private _stagedLayerNames: Set<string> = new Set();\n\n constructor(rootPath: string, manifest: Manifest, options: ModelOptions = {}) {\n this.rootPath = rootPath;\n this.manifest = manifest;\n this.graph = new GraphModel();\n this.layers = new Map();\n this.relationships = new Relationships();\n this.lazyLoad = options.lazyLoad ?? false;\n this.loadedLayers = new Set();\n }\n\n /**\n * Get the committed base layer (no staging projection).\n * Used internally by VirtualProjectionEngine and commit() to access the raw base model\n * without triggering infinite recursion or double-projection.\n */\n async getBaseLayer(name: string): Promise<Layer | undefined> {\n if (this.lazyLoad && !this.loadedLayers.has(name)) {\n await this.loadLayer(name);\n }\n return this.layers.get(name);\n }\n\n /**\n * Get a layer, transparently merging staged changes when an active changeset exists.\n * Falls back to the committed base layer if no changeset is active or projection fails.\n */\n async getLayer(name: string): Promise<Layer | undefined> {\n if (this.lazyLoad && !this.loadedLayers.has(name)) {\n await this.loadLayer(name);\n }\n\n // If an active changeset is present, return a projected (base + staged) layer\n if (this._activeChangesetId) {\n try {\n return await this.getVirtualProjectionEngine().projectLayer(\n this,\n this._activeChangesetId,\n name\n );\n } catch {\n // Fall back to base layer on projection failure (e.g. corrupt changeset)\n return this.layers.get(name);\n }\n }\n\n return this.layers.get(name);\n }\n\n /**\n * Get an element by ID across all layers\n * Supports both UUID and semantic ID (layer.type.kebab-name) lookup\n */\n getElementById(id: string): Element | undefined {\n for (const layer of this.layers.values()) {\n const element = layer.getElement(id);\n if (element) {\n return element;\n }\n }\n return undefined;\n }\n\n /**\n * Get virtual projection engine (lazily initialized)\n */\n getVirtualProjectionEngine(): VirtualProjectionEngine {\n if (!this.virtualProjectionEngine) {\n this.virtualProjectionEngine = new VirtualProjectionEngine(this.rootPath);\n }\n return this.virtualProjectionEngine;\n }\n\n /**\n * Returns the active changeset ID, or null if no changeset is active.\n * Used by commands that need to project the full model (e.g. validate).\n */\n getActiveChangesetId(): string | null {\n return this._activeChangesetId;\n }\n\n /**\n * Load a layer from disk (model/XX_layername/*.yaml files)\n */\n async loadLayer(name: string): Promise<void> {\n await startActiveSpan(\"layer.load\", async (layerSpan) => {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n\n let layerPath: string | null = null;\n\n // Auto-discover by scanning directory with naming convention\n const modelDir = `${this.rootPath}/documentation-robotics/model`;\n\n try {\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n const layerDir = entries.find(\n (e) =>\n e.isDirectory() && e.name.match(/^\\d{2}_/) && e.name.replace(/^\\d{2}_/, \"\") === name\n );\n\n if (layerDir) {\n layerPath = `${modelDir}/${layerDir.name}`;\n }\n } catch {\n // Model directory doesn't exist or can't be read\n }\n\n if (!layerPath) {\n return; // Layer directory not found\n }\n\n const layer = new Layer(name, this.graph);\n let parseErrorCount = 0;\n\n // Load all YAML files in the layer directory\n const files = await fs.readdir(layerPath);\n for (const file of files) {\n if (file.endsWith(\".yaml\") || file.endsWith(\".yml\")) {\n const filePath = `${layerPath}/${file}`;\n try {\n const yamlContent = await fs.readFile(filePath, \"utf-8\");\n const elements = yaml.parse(yamlContent);\n\n // Add each element from the YAML file\n if (elements && typeof elements === \"object\") {\n for (const [key, element] of Object.entries(elements)) {\n if (element && typeof element === \"object\") {\n const el: any = element;\n\n // Derive id, path, and type using the 3-case migration logic:\n // Case 1: element already has both id (UUID) and path \u2192 use as-is\n // Case 2: element.id looks like a slug (layer.type.name) \u2192 move to path, generate UUID\n // Case 3: element has UUID id but no path \u2192 derive path from layer_id.type.kebab(name)\n const isSlugPattern = /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/.test(el.id || \"\");\n const isUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(el.id || \"\");\n\n let elementId: string;\n let elementPath: string;\n let extractedType: string | undefined = undefined;\n\n if (el.path && isUUIDPattern) {\n // Case 1: already has both path and UUID id\n elementId = el.id;\n elementPath = el.path;\n const pathParts = el.path.split(\".\");\n if (pathParts.length >= 3) extractedType = pathParts[1];\n } else if (el.path && !isUUIDPattern) {\n // Has a path but id is not a UUID \u2014 migrate id to deterministic UUID\n elementPath = el.path;\n elementId = deterministicUUID(el.path);\n const pathParts = el.path.split(\".\");\n if (pathParts.length >= 3) extractedType = pathParts[1];\n } else if (isSlugPattern) {\n // Case 2: slug-looking id \u2192 move to path, generate deterministic UUID\n elementPath = el.id;\n elementId = deterministicUUID(el.id);\n const parts = el.id.split(\".\");\n if (parts.length >= 3) extractedType = parts[1];\n } else if (isUUIDPattern) {\n // Case 3: UUID id, no path \u2192 derive path\n elementId = el.id;\n const layerId = el.layer_id || name;\n const rawType = el.type || \"\";\n const rawName = el.name || key;\n const kebabName = rawName.toLowerCase().replace(/[\\s_]+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n elementPath = `${layerId}.${rawType}.${kebabName}`;\n extractedType = rawType || undefined;\n } else {\n // Fallback: derive a path from available data, generate deterministic UUID\n const layerId = el.layer_id || name;\n const rawType = el.type || (typeof el.id === \"string\" && el.id.includes(\".\") ? el.id.split(\".\")[1] : \"\") || \"\";\n const rawName = el.name || key;\n const kebabName = rawName.toLowerCase().replace(/[\\s_]+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"unknown\";\n elementPath = `${layerId}.${rawType || \"element\"}.${kebabName}`;\n elementId = deterministicUUID(elementPath);\n if (rawType) extractedType = rawType;\n else if (typeof el.id === \"string\" && el.id.includes(\".\")) {\n const parts = el.id.split(\".\");\n if (parts.length >= 3) extractedType = parts[1];\n }\n }\n\n const elementName = el.name || key;\n const elementType = el.type || extractedType;\n const layerId = el.layer_id || name;\n\n const newElement = new Element({\n id: elementId,\n path: elementPath,\n spec_node_id: el.spec_node_id,\n layer_id: layerId,\n attributes: el.attributes,\n source_reference: el.source_reference,\n metadata: el.metadata,\n name: elementName,\n type: elementType,\n description: el.description || \"\",\n relationships: el.relationships || [],\n references: el.references || [],\n layer: layerId,\n } as any);\n layer.addElement(newElement);\n }\n }\n }\n } catch (fileError) {\n // Log YAML parsing errors but continue loading other files\n const errorMsg = fileError instanceof Error ? fileError.message : String(fileError);\n console.error(`Warning: Failed to parse ${filePath}: ${errorMsg}`);\n parseErrorCount++;\n // Continue with next file\n }\n }\n }\n\n // If we had parse errors, add a note to the layer\n if (parseErrorCount > 0) {\n console.error(\n `Warning: Layer '${name}' loaded with ${parseErrorCount} YAML parse error(s)`\n );\n }\n\n this.layers.set(name, layer);\n this.loadedLayers.add(name);\n\n layerSpan.setAttribute(\"layer.element_count\", layer.elements.size);\n }, { \"layer.name\": name });\n }\n\n /**\n * Add a layer to the model\n */\n addLayer(layer: Layer): void {\n // If the layer has elements in its isolated graph, migrate them to the model's graph\n if (layer.graph.getNodeCount() > 0) {\n for (const node of layer.graph.nodes.values()) {\n this.graph.addNode(node);\n }\n for (const edge of layer.graph.edges.values()) {\n this.graph.addEdge(edge);\n }\n }\n // Ensure layer uses the model's graph for future operations\n layer.graph = this.graph;\n this.layers.set(layer.name, layer);\n this.loadedLayers.add(layer.name);\n }\n\n /**\n * Get all layer names, including layers that exist only in staged changes.\n * When an active changeset introduces elements for a layer with no committed data,\n * that layer name is included so findElementLayer() and other callers can discover it.\n */\n getLayerNames(): string[] {\n const base = Array.from(this.layers.keys());\n if (this._stagedLayerNames.size === 0) {\n return base;\n }\n // Include staged-only layers (layers that have staged elements but no committed elements)\n const staged = Array.from(this._stagedLayerNames).filter((l) => !this.layers.has(l));\n return [...base, ...staged];\n }\n\n /**\n * Save a layer to disk (model/XX_layername/*.yaml)\n */\n async saveLayer(name: string): Promise<void> {\n const layer = this.layers.get(name);\n if (!layer) {\n throw new Error(`Layer ${name} not found`);\n }\n\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n\n let layerPath: string | null = null;\n\n // Discover by scanning directory\n const modelDir = `${this.rootPath}/documentation-robotics/model`;\n\n try {\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n const layerDir = entries.find(\n (e) =>\n e.isDirectory() && e.name.match(/^\\d{2}_/) && e.name.replace(/^\\d{2}_/, \"\") === name\n );\n\n if (layerDir) {\n layerPath = `${modelDir}/${layerDir.name}`;\n }\n } catch {\n // Model directory doesn't exist or can't be read\n }\n\n // If still not found, create using default format\n if (!layerPath) {\n // Find the order number for this layer\n // Handle layer names with or without numeric prefix (e.g., \"motivation\" or \"01-motivation\")\n const layerNameWithoutPrefix = name.replace(/^\\d{2}-/, \"\");\n const index = CANONICAL_LAYER_NAMES.indexOf(layerNameWithoutPrefix as typeof CANONICAL_LAYER_NAMES[number]);\n if (index >= 0) {\n const orderNum = String(index + 1).padStart(2, \"0\");\n layerPath = `${this.rootPath}/documentation-robotics/model/${orderNum}_${layerNameWithoutPrefix}`;\n await ensureDir(layerPath);\n } else {\n throw new Error(`Unknown layer ${name} and no path configured in manifest`);\n }\n }\n\n // Delete all existing YAML files in the layer directory to ensure\n // we don't keep stale files after element deletion\n try {\n const existingFiles = await fs.readdir(layerPath);\n for (const file of existingFiles) {\n if (file.endsWith(\".yaml\") || file.endsWith(\".yml\")) {\n await fs.unlink(`${layerPath}/${file}`);\n }\n }\n } catch (err) {\n // Only ignore \"directory not found\" errors \u2014 re-throw permission/I/O errors\n const isNotFoundError = (err as any)?.code === \"ENOENT\";\n if (!isNotFoundError) {\n throw new Error(\n `Failed to clean existing YAML files: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // Group elements by type, computing the snake_case file stem from the spec node title\n const elementsByType = new Map<string, { stem: string; elements: Element[] }>();\n for (const element of layer.elements.values()) {\n const typeKey = element.type;\n if (!elementsByType.has(typeKey)) {\n const stem = typeToSnakeStem(element.spec_node_id, element.type);\n elementsByType.set(typeKey, { stem, elements: [] });\n }\n elementsByType.get(typeKey)!.elements.push(element);\n }\n\n // Internal graph fields \u2014 never written to YAML\n const INTERNAL_FIELDS = new Set([\n \"__references__\", \"__relationships__\",\n \"source\", \"x-source-reference\",\n ]);\n\n // Write each type to its own YAML file\n for (const [, { stem, elements }] of elementsByType) {\n const filename = `${stem}.yaml`;\n const filePath = `${layerPath}/${filename}`;\n\n // Convert elements to spec-node format YAML\n const yamlData: any = {};\n for (const element of elements) {\n const json = element.toJSON();\n\n // Clean attributes: remove graph-internal keys that should not be persisted\n const cleanAttrs = json.attributes\n ? Object.fromEntries(\n Object.entries(json.attributes).filter(([k]) => !INTERNAL_FIELDS.has(k))\n )\n : undefined;\n\n // Use path as YAML key (human-readable); fall back to id for elements without path\n const key = element.path || element.id;\n yamlData[key] = {\n id: json.id,\n path: json.path,\n spec_node_id: json.spec_node_id,\n type: json.type,\n layer_id: json.layer_id,\n name: json.name,\n ...(json.description && { description: json.description }),\n ...(cleanAttrs && Object.keys(cleanAttrs).length > 0 && { attributes: cleanAttrs }),\n ...(json.source_reference && { source_reference: json.source_reference }),\n ...(json.metadata && { metadata: json.metadata }),\n ...(json.references && json.references.length > 0 && { references: json.references }),\n // Relationships are now stored in centralized relationships.yaml, not inline\n };\n }\n\n await writeFile(filePath, yaml.stringify(yamlData));\n }\n\n layer.markClean();\n }\n\n /**\n * Save model (saves all dirty layers and manifest)\n */\n async save(): Promise<void> {\n await this.saveDirtyLayers();\n await this.saveManifest();\n }\n\n /**\n * Save all dirty layers to disk\n */\n async saveDirtyLayers(): Promise<void> {\n // Count dirty layers before operation\n let dirtyLayerCount = 0;\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n dirtyLayerCount++;\n }\n }\n\n const span = isTelemetryEnabled\n ? startSpan(\"model.save\", {\n \"model.path\": this.rootPath,\n \"model.dirty_layer_count\": dirtyLayerCount,\n })\n : null;\n\n try {\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n await this.saveLayer(layer.name);\n }\n }\n } finally {\n if (isTelemetryEnabled) {\n endSpan(span);\n }\n }\n }\n\n /**\n * Save the manifest to disk (documentation-robotics/model/manifest.yaml)\n */\n async saveManifest(): Promise<void> {\n this.manifest.updateModified();\n const yaml = await import(\"yaml\");\n const manifestPath = `${this.rootPath}/documentation-robotics/model/manifest.yaml`;\n\n // Convert to YAML format\n const yamlData: any = {\n version: this.manifest.version,\n schema: \"documentation-robotics-v1\",\n cli_version: getCliVersion(),\n spec_version: this.manifest.specVersion,\n created: this.manifest.created,\n updated: this.manifest.modified,\n project: {\n name: this.manifest.name,\n description: this.manifest.description,\n version: this.manifest.version,\n },\n documentation: \".dr/README.md\",\n layers: {},\n };\n\n // Generate layer structure from current model state\n const layerOrder = [\n \"motivation\",\n \"business\",\n \"security\",\n \"application\",\n \"technology\",\n \"api\",\n \"data-model\",\n \"data-store\",\n \"ux\",\n \"navigation\",\n \"apm\",\n \"testing\",\n ];\n\n for (let i = 0; i < layerOrder.length; i++) {\n const layerName = layerOrder[i];\n const layer = this.layers.get(layerName);\n const orderNum = String(i + 1).padStart(2, \"0\");\n\n yamlData.layers[layerName] = {\n order: i + 1,\n name: layerName.charAt(0).toUpperCase() + layerName.slice(1).replace(\"-\", \" \"),\n path: `documentation-robotics/model/${orderNum}_${layerName}/`,\n enabled: true,\n ...(layer && { elements: this.getLayerElementCounts(layer) }),\n };\n }\n\n if (this.manifest.changeset_history && this.manifest.changeset_history.length > 0) {\n yamlData.changeset_history = this.manifest.changeset_history;\n }\n\n await ensureDir(`${this.rootPath}/documentation-robotics/model`);\n await writeFile(manifestPath, yaml.stringify(yamlData));\n }\n\n private getLayerElementCounts(layer: Layer): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const element of layer.elements.values()) {\n counts[element.type] = (counts[element.type] || 0) + 1;\n }\n return counts;\n }\n\n /**\n * Load relationships from relationships.yaml\n */\n async loadRelationships(): Promise<void> {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n const relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;\n\n let data: any;\n try {\n const yamlContent = await fs.readFile(relationshipsPath, \"utf-8\");\n data = yaml.parse(yamlContent);\n } catch (err) {\n // If file doesn't exist, that's okay - start with empty relationships\n if ((err as any).code !== \"ENOENT\") {\n // But if it's a different error (YAML parse, permission denied, etc), throw it\n // so the caller knows about the issue\n throw new Error(\n `Failed to load relationships.yaml: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n // ENOENT: file doesn't exist, return with empty relationships\n return;\n }\n\n // Validate that parsed content is an array\n if (!Array.isArray(data)) {\n const typeLabel = data === null ? \"null\" : typeof data;\n console.warn(\n `Warning: relationships.yaml contains non-array content (received ${typeLabel}). Expected an array of relationships. Skipping relationship loading.`\n );\n return;\n }\n\n this.relationships = Relationships.fromArray(data);\n // Sync relationships to graph as a secondary in-memory index.\n // addEdge() may throw if nodes from other layers haven't been loaded yet;\n // failures are silently skipped \u2014 this.relationships holds the primary data.\n for (let i = 0; i < data.length; i++) {\n const rel = data[i];\n const edge: GraphEdge = {\n id: `rel-${i}-${crypto.randomBytes(4).toString(\"hex\")}`,\n source: rel.source,\n destination: rel.target,\n predicate: rel.predicate,\n properties: rel.properties,\n category: rel.category,\n };\n try {\n this.graph.addEdge(edge);\n } catch {\n // Silently skip edges whose nodes haven't been loaded yet.\n // The primary relationship data lives in this.relationships; the graph\n // is a secondary index. Cross-layer relationships referencing nodes from\n // unloaded layers are expected during partial model construction.\n }\n }\n }\n\n /**\n * Save relationships to relationships.yaml\n */\n async saveRelationships(): Promise<void> {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n const relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;\n\n await ensureDir(`${this.rootPath}/documentation-robotics/model`);\n\n const data = this.relationships.toArray();\n\n // Add header comment with timestamp to ensure file always changes\n const timestamp = new Date().toISOString();\n const header = `# Intra-layer relationships\n# Format: source_id -> predicate -> target_id\n# Last updated: ${timestamp}\n\n`;\n const yamlContent = header + yaml.stringify(data);\n\n await fs.writeFile(relationshipsPath, yamlContent, \"utf-8\");\n this.relationships.markClean();\n }\n\n /**\n * Resolve model paths (private helper)\n *\n * @param startPath - Starting path for search\n * @returns Project root and manifest path\n */\n private static async resolveModelPaths(startPath?: string): Promise<{\n projectRoot: string;\n manifestPath: string;\n }> {\n const searchPath = startPath || process.cwd();\n\n // Optional override via env var (highest priority)\n if (process.env.DR_MODEL_PATH) {\n const envPath = path.resolve(process.env.DR_MODEL_PATH);\n let manifestPath: string;\n\n if (envPath.endsWith(\"manifest.yaml\")) {\n manifestPath = envPath;\n } else if (envPath.endsWith(\"model\")) {\n manifestPath = path.join(envPath, \"manifest.yaml\");\n } else {\n manifestPath = path.join(envPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n }\n\n try {\n await fs.access(manifestPath);\n const projectRoot = path.dirname(path.dirname(path.dirname(manifestPath)));\n return { projectRoot, manifestPath: path.normalize(manifestPath) };\n } catch {\n throw new Error(`Model not found at DR_MODEL_PATH: ${process.env.DR_MODEL_PATH}`);\n }\n }\n\n // Use findProjectRoot to locate documentation_robotics/ folder\n const projectRoot = await findProjectRoot(searchPath);\n\n if (!projectRoot) {\n throw new Error(\n \"No DR project found. Could not find documentation_robotics/ folder.\\n\" +\n 'Run \"dr init\" to create a new project, or navigate to a directory containing a DR project.'\n );\n }\n\n const manifestPath = path.join(projectRoot, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n\n try {\n await fs.access(manifestPath);\n return { projectRoot, manifestPath };\n } catch {\n throw new Error(\n `Found documentation_robotics/ at ${projectRoot} but no model found. ` +\n `Expected: ${manifestPath}\\n` +\n `Run \"dr init\" to create a model.`\n );\n }\n }\n\n /**\n * Load a model from disk\n *\n * Expected structure:\n * <project-root>/\n * \u2514\u2500\u2500 documentation_robotics/\n * \u2514\u2500\u2500 model/\n * \u2514\u2500\u2500 manifest.yaml\n *\n * @param startPath - Starting path for project search (defaults to process.cwd())\n * @param options - Model loading options\n * @returns Loaded Model instance\n */\n static async load(startPath?: string, options: ModelOptions = {}): Promise<Model> {\n return startActiveSpan(\"model.load\", async (span) => {\n // Resolve project root and manifest path\n const { projectRoot, manifestPath } = await Model.resolveModelPaths(startPath);\n\n const yaml = await import(\"yaml\");\n\n // Load manifest from resolved path\n const yamlContent = await fs.readFile(manifestPath, \"utf-8\");\n const manifestYaml = yaml.parse(yamlContent);\n\n // Convert manifest to internal format\n const manifestData: any = {\n name: manifestYaml.project?.name || \"Unnamed Model\",\n description: manifestYaml.project?.description || \"\",\n version: manifestYaml.project?.version || manifestYaml.version || \"0.1.0\",\n specVersion: manifestYaml.spec_version || \"0.6.0\",\n created: manifestYaml.created || new Date().toISOString(),\n modified: manifestYaml.updated || new Date().toISOString(),\n };\n const manifest = new Manifest(manifestData);\n const model = new Model(projectRoot, manifest, options);\n\n // Load all available layers if lazyLoad is false\n if (!options.lazyLoad) {\n try {\n // If manifest has layer definitions, use those\n if (manifestYaml.layers) {\n for (const layerName of Object.keys(manifestYaml.layers)) {\n const layerConfig = manifestYaml.layers[layerName];\n if (layerConfig.enabled !== false) {\n if (!options.layers || options.layers.includes(layerName)) {\n await model.loadLayer(layerName);\n }\n }\n }\n } else {\n // Otherwise scan for layer directories\n const modelDir = manifestPath.replace(\"/manifest.yaml\", \"\");\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.match(/^\\d{2}_/)) {\n const layerName = entry.name.replace(/^\\d{2}_/, \"\");\n if (!options.layers || options.layers.includes(layerName)) {\n await model.loadLayer(layerName);\n }\n }\n }\n }\n } catch (e) {\n // Check if this is a \"directory not found\" error (expected case)\n const isNotFoundError = (e as any)?.code === \"ENOENT\";\n if (!isNotFoundError) {\n // Re-throw non-ENOENT errors: permission errors, YAML parsing failures, I/O errors, etc.\n throw new Error(\n `Failed to load layers: ${e instanceof Error ? e.message : String(e)}`\n );\n }\n // ENOENT is expected if layer directory doesn't exist yet, so just continue\n }\n }\n\n // Load relationships from relationships.yaml\n await model.loadRelationships();\n\n // Check for active changeset and pre-load staged metadata for transparent projection.\n // This enables getLayer() to return projected views and getLayerNames() to include\n // layers that exist only in staged changes.\n const activePath = path.join(\n projectRoot,\n \"documentation-robotics\",\n \"changesets\",\n \".active\"\n );\n try {\n const activeContent = await fs.readFile(activePath, \"utf-8\");\n const activeId = activeContent.trim();\n if (activeId) {\n model._activeChangesetId = activeId;\n const storage = new StagedChangesetStorage(projectRoot);\n const changeset = await storage.load(activeId);\n if (changeset) {\n for (const change of changeset.changes as StagedChange[]) {\n // Track layer names for getLayerNames()\n if (\n change.type === \"add\" ||\n change.type === \"update\" ||\n change.type === \"delete\"\n ) {\n model._stagedLayerNames.add(change.layerName);\n }\n\n // Inject staged relationship-adds into model.relationships so that\n // read commands (list, show, export) transparently see staged relationships.\n if (change.type === \"relationship-add\" && change.after) {\n const rel = change.after as {\n source: string;\n target: string;\n predicate: string;\n layer: string;\n targetLayer?: string;\n category?: \"structural\" | \"behavioral\";\n properties?: Record<string, unknown>;\n };\n // Only inject if not already in relationships.yaml (prevents duplicates\n // if .active file is stale after a partial commit or manual edit).\n const alreadyPresent = model.relationships.find(\n rel.source,\n rel.target,\n rel.predicate\n );\n if (alreadyPresent.length === 0) {\n model.relationships.add({\n source: rel.source,\n target: rel.target,\n predicate: rel.predicate,\n layer: rel.layer,\n ...(rel.targetLayer ? { targetLayer: rel.targetLayer } : {}),\n category: rel.category ?? \"structural\",\n ...(rel.properties ? { properties: rel.properties } : {}),\n });\n }\n }\n\n // Remove staged relationship-deletes from model.relationships so that\n // read commands see the relationship as already deleted.\n if (change.type === \"relationship-delete\" && change.before) {\n const rel = change.before as {\n source: string;\n target: string;\n predicate?: string;\n };\n model.relationships.delete(rel.source, rel.target, rel.predicate);\n }\n }\n }\n }\n } catch {\n // No active changeset or unreadable \u2014 proceed with base model only\n model._activeChangesetId = null;\n }\n\n // Count total entities across all layers\n let entityCount = 0;\n for (const layer of model.layers.values()) {\n entityCount += layer.elements.size;\n }\n\n span.setAttribute(\"model.entity_count\", entityCount);\n span.setAttribute(\"model.layer_count\", model.layers.size);\n\n return model;\n }, {\n \"model.path\": startPath || process.cwd(),\n \"model.type\": \"dr\",\n });\n }\n\n /**\n * Initialize a new model in a directory\n */\n static async init(\n rootPath: string,\n manifestData: ManifestData,\n options: ModelOptions = {}\n ): Promise<Model> {\n // Create model directory structure\n await ensureDir(`${rootPath}/documentation-robotics/model`);\n\n for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {\n const orderNum = String(i + 1).padStart(2, \"0\");\n const layerName = CANONICAL_LAYER_NAMES[i];\n await ensureDir(`${rootPath}/documentation-robotics/model/${orderNum}_${layerName}`);\n }\n\n const manifest = new Manifest(manifestData);\n const model = new Model(rootPath, manifest, options);\n\n await model.saveManifest();\n\n return model;\n }\n\n /**\n * Graph-based API methods for querying the model\n * These methods delegate to the underlying GraphModel for efficient queries\n */\n\n /**\n * Get a node by its UUID\n */\n getNode(id: string) {\n return this.graph.getNode(id);\n }\n\n /**\n * Get all nodes in a specific layer\n */\n getNodesByLayer(layerId: string) {\n return this.graph.getNodesByLayer(layerId);\n }\n\n /**\n * Get all nodes of a specific type\n */\n getNodesByType(type: string) {\n return this.graph.getNodesByType(type);\n }\n\n /**\n * Get all edges (relationships) in the model\n */\n getAllEdges() {\n return this.graph.getAllEdges();\n }\n\n /**\n * Get edges originating from a specific node\n */\n getEdgesFrom(nodeId: string, predicate?: string) {\n return this.graph.getEdgesFrom(nodeId, predicate);\n }\n\n /**\n * Get edges terminating at a specific node\n */\n getEdgesTo(nodeId: string, predicate?: string) {\n return this.graph.getEdgesTo(nodeId, predicate);\n }\n\n /**\n * Get edges between two specific nodes\n */\n getEdgesBetween(sourceId: string, destinationId: string, predicate?: string) {\n return this.graph.getEdgesBetween(sourceId, destinationId, predicate);\n }\n\n /**\n * Traverse the graph starting from a node, following a specific predicate\n */\n async traverse(startNodeId: string, predicate: string, maxDepth: number = 10) {\n return this.graph.traverse(startNodeId, predicate, maxDepth);\n }\n\n /**\n * Create a backup of the current model state\n * Used for rollback support in migration operations\n *\n * @param label Descriptive label for the backup (e.g., \"pre-migration\")\n * @returns Path to the backup directory\n */\n async createBackup(label: string): Promise<string> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = path.join(this.rootPath, \".dr-backups\", `${label}-${timestamp}`);\n\n // Create backup directory\n await ensureDir(backupDir);\n\n // Backup layers directory\n const layersSource = path.join(this.rootPath, \"documentation-robotics\", \"model\");\n const layersBackup = path.join(backupDir, \"layers\");\n if (await this.directoryExists(layersSource)) {\n await this.copyDirectory(layersSource, layersBackup);\n }\n\n // Backup manifest\n const manifestSource = path.join(this.rootPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n const manifestBackup = path.join(backupDir, \"manifest.yaml\");\n if (await this.fileExists(manifestSource)) {\n await fs.copyFile(manifestSource, manifestBackup);\n }\n\n return backupDir;\n }\n\n /**\n * Restore model from a backup directory\n * Used for rollback after failed migrations\n *\n * @param backupPath Path to the backup directory\n */\n async restoreFromBackup(backupPath: string): Promise<void> {\n // Restore layers\n const layersBackup = path.join(backupPath, \"layers\");\n const layersTarget = path.join(this.rootPath, \"documentation-robotics\", \"model\");\n if (await this.directoryExists(layersBackup)) {\n await this.copyDirectory(layersBackup, layersTarget);\n }\n\n // Restore manifest\n const manifestBackup = path.join(backupPath, \"manifest.yaml\");\n const manifestTarget = path.join(this.rootPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n if (await this.fileExists(manifestBackup)) {\n await fs.copyFile(manifestBackup, manifestTarget);\n }\n\n // Reload model to refresh in-memory state\n const reloadedModel = await Model.load(this.rootPath, { lazyLoad: this.lazyLoad });\n this.layers = reloadedModel.layers;\n this.manifest = reloadedModel.manifest;\n this.graph = reloadedModel.graph;\n this.relationships = reloadedModel.relationships;\n }\n\n /**\n * Save dirty layers with atomic write semantics\n * Writes to temporary file first, then renames on success to ensure data safety\n *\n * @throws Error if atomic write fails\n */\n async saveDirtyLayersAtomic(): Promise<void> {\n // First, save all layers to temporary files\n const tempLayerWrites: Array<{ layer: Layer; tempPath: string }> = [];\n\n try {\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n const layerPath = path.join(\n this.rootPath,\n \"documentation-robotics\",\n \"model\",\n `${layer.name}_layer`\n );\n\n const tempPath = `${layerPath}.tmp`;\n await ensureDir(tempPath);\n\n // Write layer elements to temp directory\n for (const element of layer.elements.values()) {\n const elementPath = path.join(tempPath, `${element.path || element.id}.json`);\n const content = JSON.stringify(element.toJSON(), null, 2);\n await writeFile(elementPath, content);\n }\n\n tempLayerWrites.push({ layer, tempPath });\n }\n }\n\n // All temp writes succeeded, now commit by renaming\n for (const { layer, tempPath } of tempLayerWrites) {\n const layerPath = path.join(\n this.rootPath,\n \"documentation-robotics\",\n \"model\",\n `${layer.name}_layer`\n );\n\n // Remove old directory and rename temp to actual\n if (await this.directoryExists(layerPath)) {\n await fs.rm(layerPath, { recursive: true, force: true });\n }\n await fs.rename(tempPath, layerPath);\n }\n } catch (error) {\n // Cleanup any temp directories on failure\n for (const { tempPath } of tempLayerWrites) {\n if (await this.directoryExists(tempPath)) {\n await fs.rm(tempPath, { recursive: true, force: true });\n }\n }\n throw error;\n }\n }\n\n /**\n * Helper: Check if file exists\n */\n private async fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Helper: Check if directory exists\n */\n private async directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n }\n\n /**\n * Helper: Recursively copy directory\n */\n private async copyDirectory(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await this.copyDirectory(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Layer } from \"./layer.js\";\nimport { Manifest } from \"./manifest.js\";\nimport { Element } from \"./element.js\";\nimport { GraphModel, type GraphEdge } from \"./graph-model.js\";\nimport { VirtualProjectionEngine } from \"./virtual-projection.js\";\nimport { StagedChangesetStorage } from \"./staged-changeset-storage.js\";\nimport { Relationships } from \"./relationships.js\";\nimport { CANONICAL_LAYER_NAMES } from \"./layers.js\";\nimport type { StagedChange } from \"./changeset.js\";\nimport { ensureDir, writeFile } from \"../utils/file-io.js\";\nimport { getCliVersion } from \"../utils/spec-version.js\";\nimport { startSpan, endSpan, startActiveSpan } from \"../telemetry/index.js\";\nimport { findProjectRoot } from \"../utils/project-paths.js\";\nimport { getNodeType } from \"../generated/node-types.js\";\nimport type { SpecNodeId } from \"../generated/node-types.js\";\nimport type { ManifestData, ModelOptions } from \"../types/index.js\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport crypto from \"node:crypto\";\n\n/**\n * Derive a deterministic UUID from a path string using SHA-256.\n * Produces a stable UUID4-like value so migration doesn't regenerate UUIDs on every load.\n */\nfunction deterministicUUID(path: string): string {\n const hash = crypto.createHash(\"sha256\").update(path).digest(\"hex\");\n return [\n hash.slice(0, 8),\n hash.slice(8, 12),\n \"4\" + hash.slice(13, 16), // version 4 marker\n ((parseInt(hash.slice(16, 18), 16) & 0x3f) | 0x80).toString(16) + hash.slice(18, 20), // variant\n hash.slice(20, 32),\n ].join(\"-\");\n}\n\n/**\n * Convert a PascalCase title to snake_case.\n * Handles acronyms correctly: \"UXApplication\" \u2192 \"ux_application\", \"OpenAPIDocument\" \u2192 \"open_api_document\".\n */\nfunction titleToSnakeCase(title: string): string {\n return title\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1_$2\") // \"APIDoc\" \u2192 \"API_Doc\"\n .replace(/([a-z\\d])([A-Z])/g, \"$1_$2\") // \"appComponent\" \u2192 \"app_Component\"\n .toLowerCase();\n}\n\n/**\n * Derive a snake_case file stem for a given element type.\n * Uses the spec node title for accurate word boundaries (e.g., \"InputSpacePartition\" \u2192 \"input_space_partition\").\n * Falls back to the raw type string when the title is unavailable.\n */\nfunction typeToSnakeStem(specNodeId: string | undefined, type: string): string {\n if (specNodeId) {\n const nodeType = getNodeType(specNodeId as SpecNodeId);\n if (nodeType?.title) {\n return titleToSnakeCase(nodeType.title);\n }\n }\n return type;\n}\n\n// Fallback for runtime environments where TELEMETRY_ENABLED is not defined by esbuild\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\n/**\n * Model class representing the complete architecture model\n * Uses GraphModel internally for graph-based storage\n */\nexport class Model {\n rootPath: string;\n manifest: Manifest;\n graph: GraphModel;\n layers: Map<string, Layer>;\n relationships: Relationships;\n lazyLoad: boolean;\n private loadedLayers: Set<string>;\n private virtualProjectionEngine?: VirtualProjectionEngine;\n /** ID of the currently active changeset, or null if none is active. Set during load(). */\n private _activeChangesetId: string | null = null;\n /** Layer names that exist only in staged changes (not yet committed to base model). */\n private _stagedLayerNames: Set<string> = new Set();\n\n constructor(rootPath: string, manifest: Manifest, options: ModelOptions = {}) {\n this.rootPath = rootPath;\n this.manifest = manifest;\n this.graph = new GraphModel();\n this.layers = new Map();\n this.relationships = new Relationships();\n this.lazyLoad = options.lazyLoad ?? false;\n this.loadedLayers = new Set();\n }\n\n /**\n * Get the committed base layer (no staging projection).\n * Used internally by VirtualProjectionEngine and commit() to access the raw base model\n * without triggering infinite recursion or double-projection.\n */\n async getBaseLayer(name: string): Promise<Layer | undefined> {\n if (this.lazyLoad && !this.loadedLayers.has(name)) {\n await this.loadLayer(name);\n }\n return this.layers.get(name);\n }\n\n /**\n * Get a layer, transparently merging staged changes when an active changeset exists.\n * Falls back to the committed base layer if no changeset is active or projection fails.\n */\n async getLayer(name: string): Promise<Layer | undefined> {\n if (this.lazyLoad && !this.loadedLayers.has(name)) {\n await this.loadLayer(name);\n }\n\n // If an active changeset is present, return a projected (base + staged) layer\n if (this._activeChangesetId) {\n try {\n return await this.getVirtualProjectionEngine().projectLayer(\n this,\n this._activeChangesetId,\n name\n );\n } catch {\n // Fall back to base layer on projection failure (e.g. corrupt changeset)\n return this.layers.get(name);\n }\n }\n\n return this.layers.get(name);\n }\n\n /**\n * Get an element by ID across all layers\n * Supports both UUID and semantic ID (layer.type.kebab-name) lookup\n */\n getElementById(id: string): Element | undefined {\n for (const layer of this.layers.values()) {\n const element = layer.getElement(id);\n if (element) {\n return element;\n }\n }\n return undefined;\n }\n\n /**\n * Get virtual projection engine (lazily initialized)\n */\n getVirtualProjectionEngine(): VirtualProjectionEngine {\n if (!this.virtualProjectionEngine) {\n this.virtualProjectionEngine = new VirtualProjectionEngine(this.rootPath);\n }\n return this.virtualProjectionEngine;\n }\n\n /**\n * Returns the active changeset ID, or null if no changeset is active.\n * Used by commands that need to project the full model (e.g. validate).\n */\n getActiveChangesetId(): string | null {\n return this._activeChangesetId;\n }\n\n /**\n * Load a layer from disk (model/XX_layername/*.yaml files)\n */\n async loadLayer(name: string): Promise<void> {\n await startActiveSpan(\"layer.load\", async (layerSpan) => {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n\n let layerPath: string | null = null;\n\n // Auto-discover by scanning directory with naming convention\n const modelDir = `${this.rootPath}/documentation-robotics/model`;\n\n try {\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n const layerDir = entries.find(\n (e) =>\n e.isDirectory() && e.name.match(/^\\d{2}_/) && e.name.replace(/^\\d{2}_/, \"\") === name\n );\n\n if (layerDir) {\n layerPath = `${modelDir}/${layerDir.name}`;\n }\n } catch {\n // Model directory doesn't exist or can't be read\n }\n\n if (!layerPath) {\n return; // Layer directory not found\n }\n\n const layer = new Layer(name, this.graph);\n let parseErrorCount = 0;\n\n // Load all YAML files in the layer directory\n const files = await fs.readdir(layerPath);\n for (const file of files) {\n if (file.endsWith(\".yaml\") || file.endsWith(\".yml\")) {\n const filePath = `${layerPath}/${file}`;\n try {\n const yamlContent = await fs.readFile(filePath, \"utf-8\");\n const elements = yaml.parse(yamlContent);\n\n // Add each element from the YAML file\n if (elements && typeof elements === \"object\") {\n for (const [key, element] of Object.entries(elements)) {\n if (element && typeof element === \"object\") {\n const el: any = element;\n\n // Derive id, path, and type using the 3-case migration logic:\n // Case 1: element already has both id (UUID) and path \u2192 use as-is\n // Case 2: element.id looks like a slug (layer.type.name) \u2192 move to path, generate UUID\n // Case 3: element has UUID id but no path \u2192 derive path from layer_id.type.kebab(name)\n const isSlugPattern = /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/.test(el.id || \"\");\n const isUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(el.id || \"\");\n\n let elementId: string;\n let elementPath: string;\n let extractedType: string | undefined = undefined;\n\n if (el.path && isUUIDPattern) {\n // Case 1: already has both path and UUID id\n elementId = el.id;\n elementPath = el.path;\n const pathParts = el.path.split(\".\");\n if (pathParts.length >= 3) extractedType = pathParts[1];\n } else if (el.path && !isUUIDPattern) {\n // Has a path but id is not a UUID \u2014 migrate id to deterministic UUID\n elementPath = el.path;\n elementId = deterministicUUID(el.path);\n const pathParts = el.path.split(\".\");\n if (pathParts.length >= 3) extractedType = pathParts[1];\n } else if (isSlugPattern) {\n // Case 2: slug-looking id \u2192 move to path, generate deterministic UUID\n elementPath = el.id;\n elementId = deterministicUUID(el.id);\n const parts = el.id.split(\".\");\n if (parts.length >= 3) extractedType = parts[1];\n } else if (isUUIDPattern) {\n // Case 3: UUID id, no path \u2192 derive path\n elementId = el.id;\n const layerId = el.layer_id || name;\n const rawType = el.type || \"\";\n const rawName = el.name || key;\n const kebabName = rawName.toLowerCase().replace(/[\\s_]+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n elementPath = `${layerId}.${rawType}.${kebabName}`;\n extractedType = rawType || undefined;\n } else {\n // Fallback: derive a path from available data, generate deterministic UUID\n const layerId = el.layer_id || name;\n const rawType = el.type || (typeof el.id === \"string\" && el.id.includes(\".\") ? el.id.split(\".\")[1] : \"\") || \"\";\n const rawName = el.name || key;\n const kebabName = rawName.toLowerCase().replace(/[\\s_]+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"unknown\";\n elementPath = `${layerId}.${rawType || \"element\"}.${kebabName}`;\n elementId = deterministicUUID(elementPath);\n if (rawType) extractedType = rawType;\n else if (typeof el.id === \"string\" && el.id.includes(\".\")) {\n const parts = el.id.split(\".\");\n if (parts.length >= 3) extractedType = parts[1];\n }\n }\n\n const elementName = el.name || key;\n const elementType = el.type || extractedType;\n const layerId = el.layer_id || name;\n\n const newElement = new Element({\n id: elementId,\n path: elementPath,\n spec_node_id: el.spec_node_id,\n layer_id: layerId,\n attributes: el.attributes,\n source_reference: el.source_reference,\n metadata: el.metadata,\n name: elementName,\n type: elementType,\n description: el.description || \"\",\n relationships: el.relationships || [],\n references: el.references || [],\n layer: layerId,\n } as any);\n layer.addElement(newElement);\n }\n }\n }\n } catch (fileError) {\n // Log YAML parsing errors but continue loading other files\n const errorMsg = fileError instanceof Error ? fileError.message : String(fileError);\n console.error(`Warning: Failed to parse ${filePath}: ${errorMsg}`);\n parseErrorCount++;\n // Continue with next file\n }\n }\n }\n\n // If we had parse errors, add a note to the layer\n if (parseErrorCount > 0) {\n console.error(\n `Warning: Layer '${name}' loaded with ${parseErrorCount} YAML parse error(s)`\n );\n }\n\n this.layers.set(name, layer);\n this.loadedLayers.add(name);\n\n layerSpan.setAttribute(\"layer.element_count\", layer.elements.size);\n }, { \"layer.name\": name });\n }\n\n /**\n * Add a layer to the model\n */\n addLayer(layer: Layer): void {\n // If the layer has elements in its isolated graph, migrate them to the model's graph\n if (layer.graph.getNodeCount() > 0) {\n for (const node of layer.graph.nodes.values()) {\n this.graph.addNode(node);\n }\n for (const edge of layer.graph.edges.values()) {\n this.graph.addEdge(edge);\n }\n }\n // Ensure layer uses the model's graph for future operations\n layer.graph = this.graph;\n this.layers.set(layer.name, layer);\n this.loadedLayers.add(layer.name);\n }\n\n /**\n * Get all layer names, including layers that exist only in staged changes.\n * When an active changeset introduces elements for a layer with no committed data,\n * that layer name is included so findElementLayer() and other callers can discover it.\n */\n getLayerNames(): string[] {\n const base = Array.from(this.layers.keys());\n if (this._stagedLayerNames.size === 0) {\n return base;\n }\n // Include staged-only layers (layers that have staged elements but no committed elements)\n const staged = Array.from(this._stagedLayerNames).filter((l) => !this.layers.has(l));\n return [...base, ...staged];\n }\n\n /**\n * Save a layer to disk (model/XX_layername/*.yaml)\n */\n async saveLayer(name: string): Promise<void> {\n const layer = this.layers.get(name);\n if (!layer) {\n throw new Error(`Layer ${name} not found`);\n }\n\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n\n let layerPath: string | null = null;\n\n // Discover by scanning directory\n const modelDir = `${this.rootPath}/documentation-robotics/model`;\n\n try {\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n const layerDir = entries.find(\n (e) =>\n e.isDirectory() && e.name.match(/^\\d{2}_/) && e.name.replace(/^\\d{2}_/, \"\") === name\n );\n\n if (layerDir) {\n layerPath = `${modelDir}/${layerDir.name}`;\n }\n } catch {\n // Model directory doesn't exist or can't be read\n }\n\n // If still not found, create using default format\n if (!layerPath) {\n // Find the order number for this layer\n // Handle layer names with or without numeric prefix (e.g., \"motivation\" or \"01-motivation\")\n const layerNameWithoutPrefix = name.replace(/^\\d{2}-/, \"\");\n const index = CANONICAL_LAYER_NAMES.indexOf(layerNameWithoutPrefix as typeof CANONICAL_LAYER_NAMES[number]);\n if (index >= 0) {\n const orderNum = String(index + 1).padStart(2, \"0\");\n layerPath = `${this.rootPath}/documentation-robotics/model/${orderNum}_${layerNameWithoutPrefix}`;\n await ensureDir(layerPath);\n } else {\n throw new Error(`Unknown layer ${name} and no path configured in manifest`);\n }\n }\n\n // Delete all existing YAML files in the layer directory to ensure\n // we don't keep stale files after element deletion\n try {\n const existingFiles = await fs.readdir(layerPath);\n for (const file of existingFiles) {\n if (file.endsWith(\".yaml\") || file.endsWith(\".yml\")) {\n await fs.unlink(`${layerPath}/${file}`);\n }\n }\n } catch (err) {\n // Only ignore \"directory not found\" errors \u2014 re-throw permission/I/O errors\n const isNotFoundError = (err as any)?.code === \"ENOENT\";\n if (!isNotFoundError) {\n throw new Error(\n `Failed to clean existing YAML files: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // Group elements by type, computing the snake_case file stem from the spec node title\n const elementsByType = new Map<string, { stem: string; elements: Element[] }>();\n for (const element of layer.elements.values()) {\n const typeKey = element.type;\n if (!elementsByType.has(typeKey)) {\n const stem = typeToSnakeStem(element.spec_node_id, element.type);\n elementsByType.set(typeKey, { stem, elements: [] });\n }\n elementsByType.get(typeKey)!.elements.push(element);\n }\n\n // Internal graph fields \u2014 never written to YAML\n const INTERNAL_FIELDS = new Set([\n \"__references__\", \"__relationships__\",\n \"source\", \"x-source-reference\",\n ]);\n\n // Write each type to its own YAML file\n for (const [, { stem, elements }] of elementsByType) {\n const filename = `${stem}.yaml`;\n const filePath = `${layerPath}/${filename}`;\n\n // Convert elements to spec-node format YAML\n const yamlData: any = {};\n for (const element of elements) {\n const json = element.toJSON();\n\n // Clean attributes: remove graph-internal keys that should not be persisted\n const cleanAttrs = json.attributes\n ? Object.fromEntries(\n Object.entries(json.attributes).filter(([k]) => !INTERNAL_FIELDS.has(k))\n )\n : undefined;\n\n // Use path as YAML key (human-readable); fall back to id for elements without path\n const key = element.path || element.id;\n yamlData[key] = {\n id: json.id,\n path: json.path,\n spec_node_id: json.spec_node_id,\n type: json.type,\n layer_id: json.layer_id,\n name: json.name,\n ...(json.description && { description: json.description }),\n ...(cleanAttrs && Object.keys(cleanAttrs).length > 0 && { attributes: cleanAttrs }),\n ...(json.source_reference && { source_reference: json.source_reference }),\n ...(json.metadata && { metadata: json.metadata }),\n ...(json.references && json.references.length > 0 && { references: json.references }),\n // Relationships are now stored in centralized relationships.yaml, not inline\n };\n }\n\n await writeFile(filePath, yaml.stringify(yamlData));\n }\n\n layer.markClean();\n }\n\n /**\n * Save model (saves all dirty layers and manifest)\n */\n async save(): Promise<void> {\n await this.saveDirtyLayers();\n await this.saveManifest();\n }\n\n /**\n * Save all dirty layers to disk\n */\n async saveDirtyLayers(): Promise<void> {\n // Count dirty layers before operation\n let dirtyLayerCount = 0;\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n dirtyLayerCount++;\n }\n }\n\n const span = isTelemetryEnabled\n ? startSpan(\"model.save\", {\n \"model.path\": this.rootPath,\n \"model.dirty_layer_count\": dirtyLayerCount,\n })\n : null;\n\n try {\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n await this.saveLayer(layer.name);\n }\n }\n } finally {\n if (isTelemetryEnabled) {\n endSpan(span);\n }\n }\n }\n\n /**\n * Save the manifest to disk (documentation-robotics/model/manifest.yaml)\n */\n async saveManifest(): Promise<void> {\n this.manifest.updateModified();\n const yaml = await import(\"yaml\");\n const manifestPath = `${this.rootPath}/documentation-robotics/model/manifest.yaml`;\n\n // Convert to YAML format\n const yamlData: any = {\n version: this.manifest.version,\n schema: \"documentation-robotics-v1\",\n cli_version: getCliVersion(),\n spec_version: this.manifest.specVersion,\n created: this.manifest.created,\n updated: this.manifest.modified,\n project: {\n name: this.manifest.name,\n description: this.manifest.description,\n version: this.manifest.version,\n },\n documentation: \".dr/README.md\",\n layers: {},\n };\n\n // Generate layer structure from current model state\n for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {\n const layerName = CANONICAL_LAYER_NAMES[i];\n const layer = this.layers.get(layerName);\n const orderNum = String(i + 1).padStart(2, \"0\");\n\n yamlData.layers[layerName] = {\n order: i + 1,\n name: layerName.charAt(0).toUpperCase() + layerName.slice(1).replace(\"-\", \" \"),\n path: `documentation-robotics/model/${orderNum}_${layerName}/`,\n enabled: true,\n ...(layer && { elements: this.getLayerElementCounts(layer) }),\n };\n }\n\n if (this.manifest.changeset_history && this.manifest.changeset_history.length > 0) {\n yamlData.changeset_history = this.manifest.changeset_history;\n }\n\n await ensureDir(`${this.rootPath}/documentation-robotics/model`);\n await writeFile(manifestPath, yaml.stringify(yamlData));\n }\n\n private getLayerElementCounts(layer: Layer): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const element of layer.elements.values()) {\n counts[element.type] = (counts[element.type] || 0) + 1;\n }\n return counts;\n }\n\n /**\n * Load relationships from relationships.yaml\n */\n async loadRelationships(): Promise<void> {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n const relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;\n\n let data: any;\n try {\n const yamlContent = await fs.readFile(relationshipsPath, \"utf-8\");\n data = yaml.parse(yamlContent);\n } catch (err) {\n // If file doesn't exist, that's okay - start with empty relationships\n if ((err as any).code !== \"ENOENT\") {\n // But if it's a different error (YAML parse, permission denied, etc), throw it\n // so the caller knows about the issue\n throw new Error(\n `Failed to load relationships.yaml: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n // ENOENT: file doesn't exist, return with empty relationships\n return;\n }\n\n // Validate that parsed content is an array\n if (!Array.isArray(data)) {\n const typeLabel = data === null ? \"null\" : typeof data;\n console.warn(\n `Warning: relationships.yaml contains non-array content (received ${typeLabel}). Expected an array of relationships. Skipping relationship loading.`\n );\n return;\n }\n\n this.relationships = Relationships.fromArray(data);\n // Sync relationships to graph as a secondary in-memory index.\n // addEdge() may throw if nodes from other layers haven't been loaded yet;\n // failures are silently skipped \u2014 this.relationships holds the primary data.\n for (let i = 0; i < data.length; i++) {\n const rel = data[i];\n const edge: GraphEdge = {\n id: `rel-${i}-${crypto.randomBytes(4).toString(\"hex\")}`,\n source: rel.source,\n destination: rel.target,\n predicate: rel.predicate,\n properties: rel.properties,\n category: rel.category,\n };\n try {\n this.graph.addEdge(edge);\n } catch {\n // Silently skip edges whose nodes haven't been loaded yet.\n // The primary relationship data lives in this.relationships; the graph\n // is a secondary index. Cross-layer relationships referencing nodes from\n // unloaded layers are expected during partial model construction.\n }\n }\n }\n\n /**\n * Save relationships to relationships.yaml\n */\n async saveRelationships(): Promise<void> {\n const fs = await import(\"fs/promises\");\n const yaml = await import(\"yaml\");\n const relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;\n\n await ensureDir(`${this.rootPath}/documentation-robotics/model`);\n\n const data = this.relationships.toArray();\n\n // Add header comment with timestamp to ensure file always changes\n const timestamp = new Date().toISOString();\n const header = `# Intra-layer relationships\n# Format: source_id -> predicate -> target_id\n# Last updated: ${timestamp}\n\n`;\n const yamlContent = header + yaml.stringify(data);\n\n await fs.writeFile(relationshipsPath, yamlContent, \"utf-8\");\n this.relationships.markClean();\n }\n\n /**\n * Resolve model paths (private helper)\n *\n * @param startPath - Starting path for search\n * @returns Project root and manifest path\n */\n private static async resolveModelPaths(startPath?: string): Promise<{\n projectRoot: string;\n manifestPath: string;\n }> {\n const searchPath = startPath || process.cwd();\n\n // Optional override via env var (highest priority)\n if (process.env.DR_MODEL_PATH) {\n const envPath = path.resolve(process.env.DR_MODEL_PATH);\n let manifestPath: string;\n\n if (envPath.endsWith(\"manifest.yaml\")) {\n manifestPath = envPath;\n } else if (envPath.endsWith(\"model\")) {\n manifestPath = path.join(envPath, \"manifest.yaml\");\n } else {\n manifestPath = path.join(envPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n }\n\n try {\n await fs.access(manifestPath);\n const projectRoot = path.dirname(path.dirname(path.dirname(manifestPath)));\n return { projectRoot, manifestPath: path.normalize(manifestPath) };\n } catch {\n throw new Error(`Model not found at DR_MODEL_PATH: ${process.env.DR_MODEL_PATH}`);\n }\n }\n\n // Use findProjectRoot to locate documentation_robotics/ folder\n const projectRoot = await findProjectRoot(searchPath);\n\n if (!projectRoot) {\n throw new Error(\n \"No DR project found. Could not find documentation_robotics/ folder.\\n\" +\n 'Run \"dr init\" to create a new project, or navigate to a directory containing a DR project.'\n );\n }\n\n const manifestPath = path.join(projectRoot, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n\n try {\n await fs.access(manifestPath);\n return { projectRoot, manifestPath };\n } catch {\n throw new Error(\n `Found documentation_robotics/ at ${projectRoot} but no model found. ` +\n `Expected: ${manifestPath}\\n` +\n `Run \"dr init\" to create a model.`\n );\n }\n }\n\n /**\n * Load a model from disk\n *\n * Expected structure:\n * <project-root>/\n * \u2514\u2500\u2500 documentation_robotics/\n * \u2514\u2500\u2500 model/\n * \u2514\u2500\u2500 manifest.yaml\n *\n * @param startPath - Starting path for project search (defaults to process.cwd())\n * @param options - Model loading options\n * @returns Loaded Model instance\n */\n static async load(startPath?: string, options: ModelOptions = {}): Promise<Model> {\n return startActiveSpan(\"model.load\", async (span) => {\n // Resolve project root and manifest path\n const { projectRoot, manifestPath } = await Model.resolveModelPaths(startPath);\n\n const yaml = await import(\"yaml\");\n\n // Load manifest from resolved path\n const yamlContent = await fs.readFile(manifestPath, \"utf-8\");\n const manifestYaml = yaml.parse(yamlContent);\n\n // Convert manifest to internal format\n const manifestData: any = {\n name: manifestYaml.project?.name || \"Unnamed Model\",\n description: manifestYaml.project?.description || \"\",\n version: manifestYaml.project?.version || manifestYaml.version || \"0.1.0\",\n specVersion: manifestYaml.spec_version || \"0.6.0\",\n created: manifestYaml.created || new Date().toISOString(),\n modified: manifestYaml.updated || new Date().toISOString(),\n };\n const manifest = new Manifest(manifestData);\n const model = new Model(projectRoot, manifest, options);\n\n // Load all available layers if lazyLoad is false\n if (!options.lazyLoad) {\n try {\n // If manifest has layer definitions, use those\n if (manifestYaml.layers) {\n for (const layerName of Object.keys(manifestYaml.layers)) {\n const layerConfig = manifestYaml.layers[layerName];\n if (layerConfig.enabled !== false) {\n if (!options.layers || options.layers.includes(layerName)) {\n await model.loadLayer(layerName);\n }\n }\n }\n } else {\n // Otherwise scan for layer directories\n const modelDir = manifestPath.replace(\"/manifest.yaml\", \"\");\n const entries = await fs.readdir(modelDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name.match(/^\\d{2}_/)) {\n const layerName = entry.name.replace(/^\\d{2}_/, \"\");\n if (!options.layers || options.layers.includes(layerName)) {\n await model.loadLayer(layerName);\n }\n }\n }\n }\n } catch (e) {\n // Check if this is a \"directory not found\" error (expected case)\n const isNotFoundError = (e as any)?.code === \"ENOENT\";\n if (!isNotFoundError) {\n // Re-throw non-ENOENT errors: permission errors, YAML parsing failures, I/O errors, etc.\n throw new Error(\n `Failed to load layers: ${e instanceof Error ? e.message : String(e)}`\n );\n }\n // ENOENT is expected if layer directory doesn't exist yet, so just continue\n }\n }\n\n // Load relationships from relationships.yaml\n await model.loadRelationships();\n\n // Check for active changeset and pre-load staged metadata for transparent projection.\n // This enables getLayer() to return projected views and getLayerNames() to include\n // layers that exist only in staged changes.\n const activePath = path.join(\n projectRoot,\n \"documentation-robotics\",\n \"changesets\",\n \".active\"\n );\n try {\n const activeContent = await fs.readFile(activePath, \"utf-8\");\n const activeId = activeContent.trim();\n if (activeId) {\n model._activeChangesetId = activeId;\n const storage = new StagedChangesetStorage(projectRoot);\n const changeset = await storage.load(activeId);\n if (changeset) {\n for (const change of changeset.changes as StagedChange[]) {\n // Track layer names for getLayerNames()\n if (\n change.type === \"add\" ||\n change.type === \"update\" ||\n change.type === \"delete\"\n ) {\n model._stagedLayerNames.add(change.layerName);\n }\n\n // Inject staged relationship-adds into model.relationships so that\n // read commands (list, show, export) transparently see staged relationships.\n if (change.type === \"relationship-add\" && change.after) {\n const rel = change.after as {\n source: string;\n target: string;\n predicate: string;\n layer: string;\n targetLayer?: string;\n category?: \"structural\" | \"behavioral\";\n properties?: Record<string, unknown>;\n };\n // Only inject if not already in relationships.yaml (prevents duplicates\n // if .active file is stale after a partial commit or manual edit).\n const alreadyPresent = model.relationships.find(\n rel.source,\n rel.target,\n rel.predicate\n );\n if (alreadyPresent.length === 0) {\n model.relationships.add({\n source: rel.source,\n target: rel.target,\n predicate: rel.predicate,\n layer: rel.layer,\n ...(rel.targetLayer ? { targetLayer: rel.targetLayer } : {}),\n category: rel.category ?? \"structural\",\n ...(rel.properties ? { properties: rel.properties } : {}),\n });\n }\n }\n\n // Remove staged relationship-deletes from model.relationships so that\n // read commands see the relationship as already deleted.\n if (change.type === \"relationship-delete\" && change.before) {\n const rel = change.before as {\n source: string;\n target: string;\n predicate?: string;\n };\n model.relationships.delete(rel.source, rel.target, rel.predicate);\n }\n }\n }\n }\n } catch {\n // No active changeset or unreadable \u2014 proceed with base model only\n model._activeChangesetId = null;\n }\n\n // Count total entities across all layers\n let entityCount = 0;\n for (const layer of model.layers.values()) {\n entityCount += layer.elements.size;\n }\n\n span.setAttribute(\"model.entity_count\", entityCount);\n span.setAttribute(\"model.layer_count\", model.layers.size);\n\n return model;\n }, {\n \"model.path\": startPath || process.cwd(),\n \"model.type\": \"dr\",\n });\n }\n\n /**\n * Initialize a new model in a directory\n */\n static async init(\n rootPath: string,\n manifestData: ManifestData,\n options: ModelOptions = {}\n ): Promise<Model> {\n // Create model directory structure\n await ensureDir(`${rootPath}/documentation-robotics/model`);\n\n for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {\n const orderNum = String(i + 1).padStart(2, \"0\");\n const layerName = CANONICAL_LAYER_NAMES[i];\n await ensureDir(`${rootPath}/documentation-robotics/model/${orderNum}_${layerName}`);\n }\n\n const manifest = new Manifest(manifestData);\n const model = new Model(rootPath, manifest, options);\n\n await model.saveManifest();\n\n return model;\n }\n\n /**\n * Graph-based API methods for querying the model\n * These methods delegate to the underlying GraphModel for efficient queries\n */\n\n /**\n * Get a node by its UUID\n */\n getNode(id: string) {\n return this.graph.getNode(id);\n }\n\n /**\n * Get all nodes in a specific layer\n */\n getNodesByLayer(layerId: string) {\n return this.graph.getNodesByLayer(layerId);\n }\n\n /**\n * Get all nodes of a specific type\n */\n getNodesByType(type: string) {\n return this.graph.getNodesByType(type);\n }\n\n /**\n * Get all edges (relationships) in the model\n */\n getAllEdges() {\n return this.graph.getAllEdges();\n }\n\n /**\n * Get edges originating from a specific node\n */\n getEdgesFrom(nodeId: string, predicate?: string) {\n return this.graph.getEdgesFrom(nodeId, predicate);\n }\n\n /**\n * Get edges terminating at a specific node\n */\n getEdgesTo(nodeId: string, predicate?: string) {\n return this.graph.getEdgesTo(nodeId, predicate);\n }\n\n /**\n * Get edges between two specific nodes\n */\n getEdgesBetween(sourceId: string, destinationId: string, predicate?: string) {\n return this.graph.getEdgesBetween(sourceId, destinationId, predicate);\n }\n\n /**\n * Traverse the graph starting from a node, following a specific predicate\n */\n async traverse(startNodeId: string, predicate: string, maxDepth: number = 10) {\n return this.graph.traverse(startNodeId, predicate, maxDepth);\n }\n\n /**\n * Create a backup of the current model state\n * Used for rollback support in migration operations\n *\n * @param label Descriptive label for the backup (e.g., \"pre-migration\")\n * @returns Path to the backup directory\n */\n async createBackup(label: string): Promise<string> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = path.join(this.rootPath, \".dr-backups\", `${label}-${timestamp}`);\n\n // Create backup directory\n await ensureDir(backupDir);\n\n // Backup layers directory\n const layersSource = path.join(this.rootPath, \"documentation-robotics\", \"model\");\n const layersBackup = path.join(backupDir, \"layers\");\n if (await this.directoryExists(layersSource)) {\n await this.copyDirectory(layersSource, layersBackup);\n }\n\n // Backup manifest\n const manifestSource = path.join(this.rootPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n const manifestBackup = path.join(backupDir, \"manifest.yaml\");\n if (await this.fileExists(manifestSource)) {\n await fs.copyFile(manifestSource, manifestBackup);\n }\n\n return backupDir;\n }\n\n /**\n * Restore model from a backup directory\n * Used for rollback after failed migrations\n *\n * @param backupPath Path to the backup directory\n */\n async restoreFromBackup(backupPath: string): Promise<void> {\n // Restore layers\n const layersBackup = path.join(backupPath, \"layers\");\n const layersTarget = path.join(this.rootPath, \"documentation-robotics\", \"model\");\n if (await this.directoryExists(layersBackup)) {\n await this.copyDirectory(layersBackup, layersTarget);\n }\n\n // Restore manifest\n const manifestBackup = path.join(backupPath, \"manifest.yaml\");\n const manifestTarget = path.join(this.rootPath, \"documentation-robotics\", \"model\", \"manifest.yaml\");\n if (await this.fileExists(manifestBackup)) {\n await fs.copyFile(manifestBackup, manifestTarget);\n }\n\n // Reload model to refresh in-memory state\n const reloadedModel = await Model.load(this.rootPath, { lazyLoad: this.lazyLoad });\n this.layers = reloadedModel.layers;\n this.manifest = reloadedModel.manifest;\n this.graph = reloadedModel.graph;\n this.relationships = reloadedModel.relationships;\n }\n\n /**\n * Save dirty layers with atomic write semantics\n * Writes to temporary file first, then renames on success to ensure data safety\n *\n * @throws Error if atomic write fails\n */\n async saveDirtyLayersAtomic(): Promise<void> {\n // First, save all layers to temporary files\n const tempLayerWrites: Array<{ layer: Layer; tempPath: string }> = [];\n\n try {\n for (const layer of this.layers.values()) {\n if (layer.isDirty()) {\n const layerPath = path.join(\n this.rootPath,\n \"documentation-robotics\",\n \"model\",\n `${layer.name}_layer`\n );\n\n const tempPath = `${layerPath}.tmp`;\n await ensureDir(tempPath);\n\n // Write layer elements to temp directory\n for (const element of layer.elements.values()) {\n const elementPath = path.join(tempPath, `${element.path || element.id}.json`);\n const content = JSON.stringify(element.toJSON(), null, 2);\n await writeFile(elementPath, content);\n }\n\n tempLayerWrites.push({ layer, tempPath });\n }\n }\n\n // All temp writes succeeded, now commit by renaming\n for (const { layer, tempPath } of tempLayerWrites) {\n const layerPath = path.join(\n this.rootPath,\n \"documentation-robotics\",\n \"model\",\n `${layer.name}_layer`\n );\n\n // Remove old directory and rename temp to actual\n if (await this.directoryExists(layerPath)) {\n await fs.rm(layerPath, { recursive: true, force: true });\n }\n await fs.rename(tempPath, layerPath);\n }\n } catch (error) {\n // Cleanup any temp directories on failure\n for (const { tempPath } of tempLayerWrites) {\n if (await this.directoryExists(tempPath)) {\n await fs.rm(tempPath, { recursive: true, force: true });\n }\n }\n throw error;\n }\n }\n\n /**\n * Helper: Check if file exists\n */\n private async fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Helper: Check if directory exists\n */\n private async directoryExists(dirPath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n }\n\n /**\n * Helper: Recursively copy directory\n */\n private async copyDirectory(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await this.copyDirectory(srcPath, destPath);\n } else {\n await fs.copyFile(srcPath, destPath);\n }\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "fs", "layerId", "manifestPath"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/spec-loader.ts"],
4
- "sourcesContent": ["/**\n * SpecDataLoader - Loads specification metadata from compiled bundled dist files\n *\n * Reads directly from the 14 compiled JSON files in cli/src/schemas/bundled/:\n * manifest.json, base.json, {layer}.json (x12)\n *\n * Never reads from .dr/ or expanded individual schema files.\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"url\";\nimport fs from \"node:fs/promises\";\nimport { existsSync } from \"fs\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { logDebug } from \"../utils/globals.js\";\nimport {\n LayerSpec,\n NodeTypeSpec,\n RelationshipTypeSpec,\n PredicateSpec,\n SpecData,\n SpecStatistics,\n SpecLoaderOptions,\n NodeTypeQueryFilter,\n RelationshipTypeQueryFilter,\n AttributeSpec,\n} from \"./spec-loader-types.js\";\n\n// Bundled dist format types (matches spec/dist/ output)\ninterface CompiledManifest {\n specVersion: string;\n builtAt: string;\n layers: Array<{ id: string; number: number; name: string; nodeTypeCount: number; relationshipCount: number }>;\n}\n\ninterface CompiledBase {\n specVersion: string;\n schemas: Record<string, unknown>;\n predicates: Record<string, unknown>;\n}\n\ninterface CompiledRelationshipFlat {\n id: string;\n source_spec_node_id: string;\n source_layer: string;\n destination_spec_node_id: string;\n destination_layer: string;\n predicate: string;\n cardinality?: string;\n strength?: string;\n required?: boolean;\n}\n\ninterface CompiledLayer {\n layer: Record<string, unknown>;\n nodeSchemas: Record<string, Record<string, unknown>>;\n relationshipSchemas: Record<string, CompiledRelationshipFlat>;\n}\n\n/**\n * SpecDataLoader loads specification metadata from the compiled bundled dist files\n */\nexport class SpecDataLoader {\n private bundledDir: string;\n private options: SpecLoaderOptions;\n private cachedData: SpecData | null = null;\n private loadedAt: Date | null = null;\n\n constructor(options: SpecLoaderOptions = {}) {\n this.options = {\n cache: true,\n includeSchemas: false,\n ...options,\n };\n\n this.bundledDir = this.options.bundledDir || this.getDefaultBundledDir();\n }\n\n /**\n * Resolve the bundled dist directory.\n *\n * Priority:\n * 1. Explicit override via options.bundledDir\n * 2. CLI installation: schemas/bundled/ next to compiled source\n * 3. Development monorepo: spec/dist/\n */\n private getDefaultBundledDir(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n\n // Try 1: CLI installation (dist/core \u2192 dist/schemas/bundled)\n const installPath = path.join(currentDir, \"..\", \"schemas\", \"bundled\");\n if (existsSync(path.join(installPath, \"manifest.json\"))) {\n logDebug(`SpecDataLoader using bundled schemas: ${installPath}`);\n return installPath;\n }\n\n // Try 2: Development path \u2014 spec/dist/ in monorepo\n const devDistPath = path.join(currentDir, \"../../../spec/dist\");\n if (existsSync(path.join(devDistPath, \"manifest.json\"))) {\n logDebug(`SpecDataLoader using bundled schemas: ${devDistPath}`);\n return devDistPath;\n }\n\n throw new Error(\n \"Cannot find compiled spec schemas. Tried:\\n\" +\n ` 1. CLI installation: ${installPath}\\n` +\n ` 2. Development monorepo: ${devDistPath}\\n\\n` +\n \"Ensure you have built the spec (npm run build:spec) or installed the CLI.\"\n );\n }\n\n /**\n * Load all specification data\n */\n async load(): Promise<SpecData> {\n if (this.options.cache && this.cachedData) {\n return this.cachedData;\n }\n\n try {\n const [layers, nodeTypes, relationshipTypes, predicates] = await Promise.all([\n this.loadLayers(),\n this.loadNodeTypes(),\n this.loadRelationshipTypes(),\n this.loadPredicates(),\n ]);\n\n const data: SpecData = { layers, nodeTypes, relationshipTypes, predicates };\n\n if (this.options.cache) {\n this.cachedData = data;\n this.loadedAt = new Date();\n }\n\n return data;\n } catch (error) {\n throw new Error(\n `Failed to load specification data from ${this.bundledDir}: ${getErrorMessage(error)}`\n );\n }\n }\n\n /**\n * Load layer specifications from manifest.json + per-layer files\n */\n private async loadLayers(): Promise<LayerSpec[]> {\n const manifest = await this.readManifest();\n const layers: LayerSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n const layer = layerData.layer as unknown as LayerSpec;\n layers.push(layer);\n }\n\n return layers.sort((a, b) => a.number - b.number);\n }\n\n /**\n * Load node type specifications from nodeSchemas in each layer file\n */\n private async loadNodeTypes(): Promise<NodeTypeSpec[]> {\n const manifest = await this.readManifest();\n const nodeTypes: NodeTypeSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n\n for (const [, schema] of Object.entries(layerData.nodeSchemas)) {\n const spec_node_id = (schema.properties as Record<string, Record<string, unknown>>)?.spec_node_id?.const as string;\n const layer_id = (schema.properties as Record<string, Record<string, unknown>>)?.layer_id?.const as string;\n const type = (schema.properties as Record<string, Record<string, unknown>>)?.type?.const as string;\n\n if (!spec_node_id || !layer_id || !type) {\n throw new Error(\n `Node schema in ${entry.id}.json missing required const values: spec_node_id=${spec_node_id}, layer_id=${layer_id}, type=${type}`\n );\n }\n\n nodeTypes.push({\n spec_node_id,\n layer_id,\n type,\n title: (schema.title as string) || \"\",\n description: (schema.description as string) || \"\",\n attributes: this.extractAttributes((schema.properties as Record<string, unknown>)?.attributes as Record<string, unknown>),\n ...(this.options.includeSchemas && { schema: schema as Record<string, unknown> }),\n });\n }\n }\n\n return nodeTypes;\n }\n\n /**\n * Extract attribute specifications from node schema\n */\n private extractAttributes(attributesSchema: Record<string, unknown>): AttributeSpec[] {\n if (!attributesSchema || !attributesSchema.properties) {\n return [];\n }\n\n const props = attributesSchema.properties as Record<string, Record<string, unknown>>;\n const required = new Set((attributesSchema.required as string[]) || []);\n\n return Object.entries(props).map(([name, schema]) => ({\n name,\n type: (schema.type as string) || \"unknown\",\n format: schema.format as string | undefined,\n required: required.has(name),\n description: schema.description as string | undefined,\n }));\n }\n\n /**\n * Load relationship type specifications from relationshipSchemas in each layer file\n *\n * The bundled format stores flat data directly (not wrapped in JSON Schema),\n * so we read the fields directly rather than extracting from .const values.\n */\n private async loadRelationshipTypes(): Promise<RelationshipTypeSpec[]> {\n const manifest = await this.readManifest();\n const relationshipTypes: RelationshipTypeSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n\n for (const rel of Object.values(layerData.relationshipSchemas)) {\n if (!rel.id || !rel.source_spec_node_id || !rel.source_layer) {\n throw new Error(\n `Relationship schema in ${entry.id}.json missing required fields: id=${rel.id}`\n );\n }\n\n relationshipTypes.push({\n id: rel.id,\n source_spec_node_id: rel.source_spec_node_id,\n source_layer: rel.source_layer,\n destination_spec_node_id: rel.destination_spec_node_id || \"\",\n destination_layer: rel.destination_layer || \"\",\n predicate: rel.predicate || \"\",\n cardinality: rel.cardinality || \"many-to-many\",\n strength: rel.strength || \"medium\",\n required: rel.required,\n });\n }\n }\n\n return relationshipTypes;\n }\n\n /**\n * Load predicate definitions from base.json\n */\n private async loadPredicates(): Promise<Map<string, PredicateSpec>> {\n const basePath = path.join(this.bundledDir, \"base.json\");\n\n try {\n const content = await fs.readFile(basePath, \"utf-8\");\n let data: CompiledBase;\n try {\n data = JSON.parse(content) as CompiledBase;\n } catch (parseError) {\n throw new Error(`Failed to parse base.json at ${basePath}: ${getErrorMessage(parseError)}`);\n }\n\n const predicates = new Map<string, PredicateSpec>();\n for (const [, pred] of Object.entries(data.predicates)) {\n const predSpec = pred as unknown as PredicateSpec;\n predicates.set(predSpec.predicate, {\n predicate: predSpec.predicate,\n inverse: predSpec.inverse,\n category: predSpec.category,\n description: predSpec.description,\n archimate_alignment: predSpec.archimate_alignment,\n semantics: predSpec.semantics,\n });\n }\n\n return predicates;\n } catch (error) {\n throw new Error(\n `Failed to load predicates from ${basePath}: ${getErrorMessage(error)}`\n );\n }\n }\n\n /**\n * Read and parse the bundled manifest.json\n */\n private async readManifest(): Promise<CompiledManifest> {\n const manifestPath = path.join(this.bundledDir, \"manifest.json\");\n const content = await fs.readFile(manifestPath, \"utf-8\");\n return JSON.parse(content) as CompiledManifest;\n }\n\n /**\n * Get loaded specification data\n * Throws if data hasn't been loaded yet\n */\n getSpecData(): SpecData {\n if (!this.cachedData) {\n throw new Error(\"Specification data not loaded. Call load() first.\");\n }\n return this.cachedData;\n }\n\n isLoaded(): boolean {\n return this.cachedData !== null;\n }\n\n getStatistics(): SpecStatistics {\n const data = this.getSpecData();\n return {\n layerCount: data.layers.length,\n nodeTypeCount: data.nodeTypes.length,\n relationshipTypeCount: data.relationshipTypes.length,\n predicateCount: data.predicates.size,\n totalAttributes: data.nodeTypes.reduce((sum, nt) => sum + nt.attributes.length, 0),\n loadedAt: this.loadedAt || new Date(),\n };\n }\n\n clear(): void {\n this.cachedData = null;\n this.loadedAt = null;\n }\n\n findNodeTypes(filter: NodeTypeQueryFilter = {}): NodeTypeSpec[] {\n const data = this.getSpecData();\n\n return data.nodeTypes.filter((nt) => {\n if (filter.layer && nt.layer_id !== filter.layer) return false;\n if (filter.type && nt.type !== filter.type) return false;\n if (filter.specNodeId && nt.spec_node_id !== filter.specNodeId) return false;\n if (filter.title && !nt.title.toLowerCase().includes(filter.title.toLowerCase()))\n return false;\n return true;\n });\n }\n\n findRelationshipTypes(filter: RelationshipTypeQueryFilter = {}): RelationshipTypeSpec[] {\n const data = this.getSpecData();\n\n return data.relationshipTypes.filter((rt) => {\n if (filter.sourceLayer && rt.source_layer !== filter.sourceLayer) return false;\n if (filter.sourceSpecNodeId && rt.source_spec_node_id !== filter.sourceSpecNodeId)\n return false;\n if (filter.destinationLayer && rt.destination_layer !== filter.destinationLayer)\n return false;\n if (filter.destinationSpecNodeId && rt.destination_spec_node_id !== filter.destinationSpecNodeId)\n return false;\n if (filter.predicate && rt.predicate !== filter.predicate) return false;\n return true;\n });\n }\n\n getNodeTypesForLayer(layerId: string): NodeTypeSpec[] {\n return this.findNodeTypes({ layer: layerId });\n }\n\n getRelationshipTypesForLayerPair(sourceLayer: string, destLayer?: string): RelationshipTypeSpec[] {\n const filter: RelationshipTypeQueryFilter = { sourceLayer };\n if (destLayer) {\n filter.destinationLayer = destLayer;\n }\n return this.findRelationshipTypes(filter);\n }\n\n getPredicate(predicateName: string): PredicateSpec | undefined {\n return this.getSpecData().predicates.get(predicateName);\n }\n\n getLayer(layerId: string): LayerSpec | undefined {\n const data = this.getSpecData();\n return data.layers.find((l) => l.id === layerId);\n }\n\n getAllLayers(): LayerSpec[] {\n return this.getSpecData().layers;\n }\n\n getNodeType(specNodeId: string): NodeTypeSpec | undefined {\n const data = this.getSpecData();\n return data.nodeTypes.find((nt) => nt.spec_node_id === specNodeId);\n }\n\n getNodeTypesReferencingType(specNodeId: string): RelationshipTypeSpec[] {\n return this.findRelationshipTypes({ destinationSpecNodeId: specNodeId });\n }\n\n getNodeTypesReferencedByType(specNodeId: string): RelationshipTypeSpec[] {\n return this.findRelationshipTypes({ sourceSpecNodeId: specNodeId });\n }\n}\n"],
4
+ "sourcesContent": ["/**\n * SpecDataLoader - Loads specification metadata from compiled bundled dist files\n *\n * Reads directly from the 15 compiled JSON files in cli/src/schemas/bundled/:\n * manifest.json, base.json, {layer}.json (x13)\n *\n * Never reads from .dr/ or expanded individual schema files.\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"url\";\nimport fs from \"node:fs/promises\";\nimport { existsSync } from \"fs\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { logDebug } from \"../utils/globals.js\";\nimport {\n LayerSpec,\n NodeTypeSpec,\n RelationshipTypeSpec,\n PredicateSpec,\n SpecData,\n SpecStatistics,\n SpecLoaderOptions,\n NodeTypeQueryFilter,\n RelationshipTypeQueryFilter,\n AttributeSpec,\n} from \"./spec-loader-types.js\";\n\n// Bundled dist format types (matches spec/dist/ output)\ninterface CompiledManifest {\n specVersion: string;\n builtAt: string;\n layers: Array<{ id: string; number: number; name: string; nodeTypeCount: number; relationshipCount: number }>;\n}\n\ninterface CompiledBase {\n specVersion: string;\n schemas: Record<string, unknown>;\n predicates: Record<string, unknown>;\n}\n\ninterface CompiledRelationshipFlat {\n id: string;\n source_spec_node_id: string;\n source_layer: string;\n destination_spec_node_id: string;\n destination_layer: string;\n predicate: string;\n cardinality?: string;\n strength?: string;\n required?: boolean;\n}\n\ninterface CompiledLayer {\n layer: Record<string, unknown>;\n nodeSchemas: Record<string, Record<string, unknown>>;\n relationshipSchemas: Record<string, CompiledRelationshipFlat>;\n}\n\n/**\n * SpecDataLoader loads specification metadata from the compiled bundled dist files\n */\nexport class SpecDataLoader {\n private bundledDir: string;\n private options: SpecLoaderOptions;\n private cachedData: SpecData | null = null;\n private loadedAt: Date | null = null;\n\n constructor(options: SpecLoaderOptions = {}) {\n this.options = {\n cache: true,\n includeSchemas: false,\n ...options,\n };\n\n this.bundledDir = this.options.bundledDir || this.getDefaultBundledDir();\n }\n\n /**\n * Resolve the bundled dist directory.\n *\n * Priority:\n * 1. Explicit override via options.bundledDir\n * 2. CLI installation: schemas/bundled/ next to compiled source\n * 3. Development monorepo: spec/dist/\n */\n private getDefaultBundledDir(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n\n // Try 1: CLI installation (dist/core \u2192 dist/schemas/bundled)\n const installPath = path.join(currentDir, \"..\", \"schemas\", \"bundled\");\n if (existsSync(path.join(installPath, \"manifest.json\"))) {\n logDebug(`SpecDataLoader using bundled schemas: ${installPath}`);\n return installPath;\n }\n\n // Try 2: Development path \u2014 spec/dist/ in monorepo\n const devDistPath = path.join(currentDir, \"../../../spec/dist\");\n if (existsSync(path.join(devDistPath, \"manifest.json\"))) {\n logDebug(`SpecDataLoader using bundled schemas: ${devDistPath}`);\n return devDistPath;\n }\n\n throw new Error(\n \"Cannot find compiled spec schemas. Tried:\\n\" +\n ` 1. CLI installation: ${installPath}\\n` +\n ` 2. Development monorepo: ${devDistPath}\\n\\n` +\n \"Ensure you have built the spec (npm run build:spec) or installed the CLI.\"\n );\n }\n\n /**\n * Load all specification data\n */\n async load(): Promise<SpecData> {\n if (this.options.cache && this.cachedData) {\n return this.cachedData;\n }\n\n try {\n const [layers, nodeTypes, relationshipTypes, predicates] = await Promise.all([\n this.loadLayers(),\n this.loadNodeTypes(),\n this.loadRelationshipTypes(),\n this.loadPredicates(),\n ]);\n\n const data: SpecData = { layers, nodeTypes, relationshipTypes, predicates };\n\n if (this.options.cache) {\n this.cachedData = data;\n this.loadedAt = new Date();\n }\n\n return data;\n } catch (error) {\n throw new Error(\n `Failed to load specification data from ${this.bundledDir}: ${getErrorMessage(error)}`\n );\n }\n }\n\n /**\n * Load layer specifications from manifest.json + per-layer files\n */\n private async loadLayers(): Promise<LayerSpec[]> {\n const manifest = await this.readManifest();\n const layers: LayerSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n const layer = layerData.layer as unknown as LayerSpec;\n layers.push(layer);\n }\n\n return layers.sort((a, b) => a.number - b.number);\n }\n\n /**\n * Load node type specifications from nodeSchemas in each layer file\n */\n private async loadNodeTypes(): Promise<NodeTypeSpec[]> {\n const manifest = await this.readManifest();\n const nodeTypes: NodeTypeSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n\n for (const [, schema] of Object.entries(layerData.nodeSchemas)) {\n const spec_node_id = (schema.properties as Record<string, Record<string, unknown>>)?.spec_node_id?.const as string;\n const layer_id = (schema.properties as Record<string, Record<string, unknown>>)?.layer_id?.const as string;\n const type = (schema.properties as Record<string, Record<string, unknown>>)?.type?.const as string;\n\n if (!spec_node_id || !layer_id || !type) {\n throw new Error(\n `Node schema in ${entry.id}.json missing required const values: spec_node_id=${spec_node_id}, layer_id=${layer_id}, type=${type}`\n );\n }\n\n nodeTypes.push({\n spec_node_id,\n layer_id,\n type,\n title: (schema.title as string) || \"\",\n description: (schema.description as string) || \"\",\n attributes: this.extractAttributes((schema.properties as Record<string, unknown>)?.attributes as Record<string, unknown>),\n ...(this.options.includeSchemas && { schema: schema as Record<string, unknown> }),\n });\n }\n }\n\n return nodeTypes;\n }\n\n /**\n * Extract attribute specifications from node schema\n */\n private extractAttributes(attributesSchema: Record<string, unknown>): AttributeSpec[] {\n if (!attributesSchema || !attributesSchema.properties) {\n return [];\n }\n\n const props = attributesSchema.properties as Record<string, Record<string, unknown>>;\n const required = new Set((attributesSchema.required as string[]) || []);\n\n return Object.entries(props).map(([name, schema]) => ({\n name,\n type: (schema.type as string) || \"unknown\",\n format: schema.format as string | undefined,\n required: required.has(name),\n description: schema.description as string | undefined,\n }));\n }\n\n /**\n * Load relationship type specifications from relationshipSchemas in each layer file\n *\n * The bundled format stores flat data directly (not wrapped in JSON Schema),\n * so we read the fields directly rather than extracting from .const values.\n */\n private async loadRelationshipTypes(): Promise<RelationshipTypeSpec[]> {\n const manifest = await this.readManifest();\n const relationshipTypes: RelationshipTypeSpec[] = [];\n\n for (const entry of manifest.layers) {\n const layerFile = path.join(this.bundledDir, `${entry.id}.json`);\n const content = await fs.readFile(layerFile, \"utf-8\");\n const layerData = JSON.parse(content) as CompiledLayer;\n\n for (const rel of Object.values(layerData.relationshipSchemas)) {\n if (!rel.id || !rel.source_spec_node_id || !rel.source_layer) {\n throw new Error(\n `Relationship schema in ${entry.id}.json missing required fields: id=${rel.id}`\n );\n }\n\n relationshipTypes.push({\n id: rel.id,\n source_spec_node_id: rel.source_spec_node_id,\n source_layer: rel.source_layer,\n destination_spec_node_id: rel.destination_spec_node_id || \"\",\n destination_layer: rel.destination_layer || \"\",\n predicate: rel.predicate || \"\",\n cardinality: rel.cardinality || \"many-to-many\",\n strength: rel.strength || \"medium\",\n required: rel.required,\n });\n }\n }\n\n return relationshipTypes;\n }\n\n /**\n * Load predicate definitions from base.json\n */\n private async loadPredicates(): Promise<Map<string, PredicateSpec>> {\n const basePath = path.join(this.bundledDir, \"base.json\");\n\n try {\n const content = await fs.readFile(basePath, \"utf-8\");\n let data: CompiledBase;\n try {\n data = JSON.parse(content) as CompiledBase;\n } catch (parseError) {\n throw new Error(`Failed to parse base.json at ${basePath}: ${getErrorMessage(parseError)}`);\n }\n\n const predicates = new Map<string, PredicateSpec>();\n for (const [, pred] of Object.entries(data.predicates)) {\n const predSpec = pred as unknown as PredicateSpec;\n predicates.set(predSpec.predicate, {\n predicate: predSpec.predicate,\n inverse: predSpec.inverse,\n category: predSpec.category,\n description: predSpec.description,\n archimate_alignment: predSpec.archimate_alignment,\n semantics: predSpec.semantics,\n });\n }\n\n return predicates;\n } catch (error) {\n throw new Error(\n `Failed to load predicates from ${basePath}: ${getErrorMessage(error)}`\n );\n }\n }\n\n /**\n * Read and parse the bundled manifest.json\n */\n private async readManifest(): Promise<CompiledManifest> {\n const manifestPath = path.join(this.bundledDir, \"manifest.json\");\n const content = await fs.readFile(manifestPath, \"utf-8\");\n return JSON.parse(content) as CompiledManifest;\n }\n\n /**\n * Get loaded specification data\n * Throws if data hasn't been loaded yet\n */\n getSpecData(): SpecData {\n if (!this.cachedData) {\n throw new Error(\"Specification data not loaded. Call load() first.\");\n }\n return this.cachedData;\n }\n\n isLoaded(): boolean {\n return this.cachedData !== null;\n }\n\n getStatistics(): SpecStatistics {\n const data = this.getSpecData();\n return {\n layerCount: data.layers.length,\n nodeTypeCount: data.nodeTypes.length,\n relationshipTypeCount: data.relationshipTypes.length,\n predicateCount: data.predicates.size,\n totalAttributes: data.nodeTypes.reduce((sum, nt) => sum + nt.attributes.length, 0),\n loadedAt: this.loadedAt || new Date(),\n };\n }\n\n clear(): void {\n this.cachedData = null;\n this.loadedAt = null;\n }\n\n findNodeTypes(filter: NodeTypeQueryFilter = {}): NodeTypeSpec[] {\n const data = this.getSpecData();\n\n return data.nodeTypes.filter((nt) => {\n if (filter.layer && nt.layer_id !== filter.layer) return false;\n if (filter.type && nt.type !== filter.type) return false;\n if (filter.specNodeId && nt.spec_node_id !== filter.specNodeId) return false;\n if (filter.title && !nt.title.toLowerCase().includes(filter.title.toLowerCase()))\n return false;\n return true;\n });\n }\n\n findRelationshipTypes(filter: RelationshipTypeQueryFilter = {}): RelationshipTypeSpec[] {\n const data = this.getSpecData();\n\n return data.relationshipTypes.filter((rt) => {\n if (filter.sourceLayer && rt.source_layer !== filter.sourceLayer) return false;\n if (filter.sourceSpecNodeId && rt.source_spec_node_id !== filter.sourceSpecNodeId)\n return false;\n if (filter.destinationLayer && rt.destination_layer !== filter.destinationLayer)\n return false;\n if (filter.destinationSpecNodeId && rt.destination_spec_node_id !== filter.destinationSpecNodeId)\n return false;\n if (filter.predicate && rt.predicate !== filter.predicate) return false;\n return true;\n });\n }\n\n getNodeTypesForLayer(layerId: string): NodeTypeSpec[] {\n return this.findNodeTypes({ layer: layerId });\n }\n\n getRelationshipTypesForLayerPair(sourceLayer: string, destLayer?: string): RelationshipTypeSpec[] {\n const filter: RelationshipTypeQueryFilter = { sourceLayer };\n if (destLayer) {\n filter.destinationLayer = destLayer;\n }\n return this.findRelationshipTypes(filter);\n }\n\n getPredicate(predicateName: string): PredicateSpec | undefined {\n return this.getSpecData().predicates.get(predicateName);\n }\n\n getLayer(layerId: string): LayerSpec | undefined {\n const data = this.getSpecData();\n return data.layers.find((l) => l.id === layerId);\n }\n\n getAllLayers(): LayerSpec[] {\n return this.getSpecData().layers;\n }\n\n getNodeType(specNodeId: string): NodeTypeSpec | undefined {\n const data = this.getSpecData();\n return data.nodeTypes.find((nt) => nt.spec_node_id === specNodeId);\n }\n\n getNodeTypesReferencingType(specNodeId: string): RelationshipTypeSpec[] {\n return this.findRelationshipTypes({ destinationSpecNodeId: specNodeId });\n }\n\n getNodeTypesReferencedByType(specNodeId: string): RelationshipTypeSpec[] {\n return this.findRelationshipTypes({ sourceSpecNodeId: specNodeId });\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -66,6 +66,8 @@ class StatsCollector {
66
66
  // goals, principles, requirements, stakeholders
67
67
  business: 2,
68
68
  // capabilities, services
69
+ product: 5,
70
+ // capabilities, features, milestones, personas, workflows
69
71
  security: 4,
70
72
  // policies, threats, etc
71
73
  application: 2,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/stats-collector.ts"],
4
- "sourcesContent": ["/**\n * Statistics collector for architecture models\n * Gathers metrics about elements, relationships, and model completeness\n */\n\nimport { Model } from \"./model.js\";\nimport { Validator } from \"../validators/validator.js\";\n\nexport interface ElementTypeCount {\n [type: string]: number;\n}\n\nexport interface LayerStats {\n name: string;\n totalElements: number;\n elementsByType: ElementTypeCount;\n coverage: number; // Percentage of element types used (0-100)\n}\n\nexport interface RelationshipStats {\n total: number;\n byPredicate: Record<string, number>;\n crossLayerCount: number;\n intraLayerCount: number;\n byPair: Record<string, number>;\n}\n\nexport interface ValidationInfo {\n isValid: boolean;\n lastValidated?: string;\n errors: number;\n warnings: number;\n}\n\nexport interface ModelStats {\n project: {\n name: string;\n version: string;\n created: string;\n updated: string;\n };\n statistics: {\n totalElements: number;\n totalRelationships: number;\n totalLayers: number;\n populatedLayers: number;\n };\n validation: ValidationInfo;\n layers: LayerStats[];\n relationships: RelationshipStats;\n completeness: {\n overall: number; // 0-100\n byLayer: Record<string, number>;\n };\n orphanedElements: string[];\n}\n\n/**\n * Collects statistics from a model\n */\nexport class StatsCollector {\n constructor(private model: Model) {}\n\n /**\n * Collect all statistics from the model\n */\n async collect(): Promise<ModelStats> {\n const layerStats = await this.collectLayerStats();\n const relationshipStats = this.collectRelationshipStats();\n const validationInfo = await this.collectValidationInfo();\n const orphanedElements = await this.findOrphanedElements();\n\n const totalElements = layerStats.reduce((sum, l) => sum + l.totalElements, 0);\n const populatedLayers = layerStats.filter((l) => l.totalElements > 0).length;\n\n const overallCompleteness = this.calculateCompleteness(layerStats);\n const byLayerCompleteness: Record<string, number> = {};\n for (const layer of layerStats) {\n byLayerCompleteness[layer.name] = layer.coverage;\n }\n\n return {\n project: {\n name: this.model.manifest.name,\n version: this.model.manifest.version,\n created: this.model.manifest.created,\n updated: this.model.manifest.modified,\n },\n statistics: {\n totalElements,\n totalRelationships: relationshipStats.total,\n totalLayers: this.model.getLayerNames().length,\n populatedLayers,\n },\n validation: validationInfo,\n layers: layerStats,\n relationships: relationshipStats,\n completeness: {\n overall: overallCompleteness,\n byLayer: byLayerCompleteness,\n },\n orphanedElements,\n };\n }\n\n /**\n * Collect statistics for each layer\n */\n private async collectLayerStats(): Promise<LayerStats[]> {\n const layerNames = this.model.getLayerNames();\n const stats: LayerStats[] = [];\n\n for (const layerName of layerNames) {\n const layer = await this.model.getLayer(layerName);\n if (!layer) continue;\n\n const elements = layer.listElements();\n const elementsByType: ElementTypeCount = {};\n\n for (const element of elements) {\n if (!elementsByType[element.type]) {\n elementsByType[element.type] = 0;\n }\n elementsByType[element.type]++;\n }\n\n // Coverage is the percentage of element types used\n // For now, we use a simple heuristic: if layer has elements, coverage = number of types / expected types\n // This can be enhanced with layer schema information\n const typeCount = Object.keys(elementsByType).length;\n const expectedTypeCount = this.getExpectedTypeCount(layerName);\n const coverage =\n expectedTypeCount > 0 ? Math.min(100, (typeCount / expectedTypeCount) * 100) : 0;\n\n stats.push({\n name: layerName,\n totalElements: elements.length,\n elementsByType,\n coverage: Math.round(coverage),\n });\n }\n\n return stats;\n }\n\n /**\n * Get expected element type count for a layer\n * These are typical/common types for each layer\n */\n private getExpectedTypeCount(layerName: string): number {\n const expectedTypes: Record<string, number> = {\n motivation: 4, // goals, principles, requirements, stakeholders\n business: 2, // capabilities, services\n security: 4, // policies, threats, etc\n application: 2, // services, components\n technology: 4, // frameworks, libraries, databases, platforms\n api: 2, // operations, endpoints\n \"data-model\": 1, // entities/schemas\n \"data-store\": 1, // datastores\n ux: 2, // views, components\n navigation: 2, // routes, menus\n apm: 4, // metrics, analytics, collectors, etc\n testing: 4, // test-suites, fixtures, strategies, coverage\n };\n\n return expectedTypes[layerName] || 2;\n }\n\n /**\n * Collect relationship statistics\n */\n private collectRelationshipStats(): RelationshipStats {\n const relationships = this.model.relationships.getAll();\n\n const byPredicate: Record<string, number> = {};\n const byPair: Record<string, number> = {};\n let crossLayerCount = 0;\n let intraLayerCount = 0;\n\n for (const rel of relationships) {\n // Count by predicate\n if (!byPredicate[rel.predicate]) {\n byPredicate[rel.predicate] = 0;\n }\n byPredicate[rel.predicate]++;\n\n // Extract layers\n const sourceLayer = rel.source.split(\".\")[0];\n const targetLayer = rel.target.split(\".\")[0];\n\n // Count cross vs intra-layer\n if (sourceLayer === targetLayer) {\n intraLayerCount++;\n } else {\n crossLayerCount++;\n }\n\n // Count by pair\n const pair = `${sourceLayer}-${targetLayer}`;\n if (!byPair[pair]) {\n byPair[pair] = 0;\n }\n byPair[pair]++;\n }\n\n return {\n total: relationships.length,\n byPredicate,\n crossLayerCount,\n intraLayerCount,\n byPair,\n };\n }\n\n /**\n * Collect validation information\n * Runs model validation and returns actual validation status\n * Preserves error details for debugging\n */\n private async collectValidationInfo(): Promise<ValidationInfo> {\n try {\n const validator = new Validator();\n const result = await validator.validateModel(this.model);\n\n return {\n isValid: result.isValid(),\n lastValidated: new Date().toISOString(),\n errors: result.errors.length,\n warnings: result.warnings.length,\n };\n } catch (error) {\n // Preserve actual error information for debugging\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[stats-collector] Validation failed: ${errorMessage}`);\n\n // Return failed state with sentinel error count -1 indicating collection failure\n // (cannot be confused with actual error count which is always >= 0)\n return {\n isValid: false,\n lastValidated: new Date().toISOString(),\n errors: -1, // Sentinel: validation collection itself failed\n warnings: 0,\n };\n }\n }\n\n /**\n * Find orphaned elements (elements with no relationships)\n */\n private async findOrphanedElements(): Promise<string[]> {\n const orphaned: string[] = [];\n const allRelationships = this.model.relationships.getAll();\n\n // Get all element IDs\n const allElements = new Set<string>();\n const layerNames = this.model.getLayerNames();\n\n for (const layerName of layerNames) {\n const layer = await this.model.getLayer(layerName);\n if (!layer) continue;\n\n for (const element of layer.listElements()) {\n allElements.add(element.path || element.id);\n }\n }\n\n // Find elements with no relationships\n const elementsWithRelationships = new Set<string>();\n for (const rel of allRelationships) {\n elementsWithRelationships.add(rel.source);\n elementsWithRelationships.add(rel.target);\n }\n\n for (const elementId of allElements) {\n if (!elementsWithRelationships.has(elementId)) {\n orphaned.push(elementId);\n }\n }\n\n return orphaned;\n }\n\n /**\n * Calculate overall model completeness\n * Weighted average of layer coverage\n */\n private calculateCompleteness(layerStats: LayerStats[]): number {\n if (layerStats.length === 0) return 0;\n\n const totalCoverage = layerStats.reduce((sum, layer) => sum + layer.coverage, 0);\n return Math.round(totalCoverage / layerStats.length);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Statistics collector for architecture models\n * Gathers metrics about elements, relationships, and model completeness\n */\n\nimport { Model } from \"./model.js\";\nimport { Validator } from \"../validators/validator.js\";\n\nexport interface ElementTypeCount {\n [type: string]: number;\n}\n\nexport interface LayerStats {\n name: string;\n totalElements: number;\n elementsByType: ElementTypeCount;\n coverage: number; // Percentage of element types used (0-100)\n}\n\nexport interface RelationshipStats {\n total: number;\n byPredicate: Record<string, number>;\n crossLayerCount: number;\n intraLayerCount: number;\n byPair: Record<string, number>;\n}\n\nexport interface ValidationInfo {\n isValid: boolean;\n lastValidated?: string;\n errors: number;\n warnings: number;\n}\n\nexport interface ModelStats {\n project: {\n name: string;\n version: string;\n created: string;\n updated: string;\n };\n statistics: {\n totalElements: number;\n totalRelationships: number;\n totalLayers: number;\n populatedLayers: number;\n };\n validation: ValidationInfo;\n layers: LayerStats[];\n relationships: RelationshipStats;\n completeness: {\n overall: number; // 0-100\n byLayer: Record<string, number>;\n };\n orphanedElements: string[];\n}\n\n/**\n * Collects statistics from a model\n */\nexport class StatsCollector {\n constructor(private model: Model) {}\n\n /**\n * Collect all statistics from the model\n */\n async collect(): Promise<ModelStats> {\n const layerStats = await this.collectLayerStats();\n const relationshipStats = this.collectRelationshipStats();\n const validationInfo = await this.collectValidationInfo();\n const orphanedElements = await this.findOrphanedElements();\n\n const totalElements = layerStats.reduce((sum, l) => sum + l.totalElements, 0);\n const populatedLayers = layerStats.filter((l) => l.totalElements > 0).length;\n\n const overallCompleteness = this.calculateCompleteness(layerStats);\n const byLayerCompleteness: Record<string, number> = {};\n for (const layer of layerStats) {\n byLayerCompleteness[layer.name] = layer.coverage;\n }\n\n return {\n project: {\n name: this.model.manifest.name,\n version: this.model.manifest.version,\n created: this.model.manifest.created,\n updated: this.model.manifest.modified,\n },\n statistics: {\n totalElements,\n totalRelationships: relationshipStats.total,\n totalLayers: this.model.getLayerNames().length,\n populatedLayers,\n },\n validation: validationInfo,\n layers: layerStats,\n relationships: relationshipStats,\n completeness: {\n overall: overallCompleteness,\n byLayer: byLayerCompleteness,\n },\n orphanedElements,\n };\n }\n\n /**\n * Collect statistics for each layer\n */\n private async collectLayerStats(): Promise<LayerStats[]> {\n const layerNames = this.model.getLayerNames();\n const stats: LayerStats[] = [];\n\n for (const layerName of layerNames) {\n const layer = await this.model.getLayer(layerName);\n if (!layer) continue;\n\n const elements = layer.listElements();\n const elementsByType: ElementTypeCount = {};\n\n for (const element of elements) {\n if (!elementsByType[element.type]) {\n elementsByType[element.type] = 0;\n }\n elementsByType[element.type]++;\n }\n\n // Coverage is the percentage of element types used\n // For now, we use a simple heuristic: if layer has elements, coverage = number of types / expected types\n // This can be enhanced with layer schema information\n const typeCount = Object.keys(elementsByType).length;\n const expectedTypeCount = this.getExpectedTypeCount(layerName);\n const coverage =\n expectedTypeCount > 0 ? Math.min(100, (typeCount / expectedTypeCount) * 100) : 0;\n\n stats.push({\n name: layerName,\n totalElements: elements.length,\n elementsByType,\n coverage: Math.round(coverage),\n });\n }\n\n return stats;\n }\n\n /**\n * Get expected element type count for a layer\n * These are typical/common types for each layer\n */\n private getExpectedTypeCount(layerName: string): number {\n const expectedTypes: Record<string, number> = {\n motivation: 4, // goals, principles, requirements, stakeholders\n business: 2, // capabilities, services\n product: 5, // capabilities, features, milestones, personas, workflows\n security: 4, // policies, threats, etc\n application: 2, // services, components\n technology: 4, // frameworks, libraries, databases, platforms\n api: 2, // operations, endpoints\n \"data-model\": 1, // entities/schemas\n \"data-store\": 1, // datastores\n ux: 2, // views, components\n navigation: 2, // routes, menus\n apm: 4, // metrics, analytics, collectors, etc\n testing: 4, // test-suites, fixtures, strategies, coverage\n };\n\n return expectedTypes[layerName] || 2;\n }\n\n /**\n * Collect relationship statistics\n */\n private collectRelationshipStats(): RelationshipStats {\n const relationships = this.model.relationships.getAll();\n\n const byPredicate: Record<string, number> = {};\n const byPair: Record<string, number> = {};\n let crossLayerCount = 0;\n let intraLayerCount = 0;\n\n for (const rel of relationships) {\n // Count by predicate\n if (!byPredicate[rel.predicate]) {\n byPredicate[rel.predicate] = 0;\n }\n byPredicate[rel.predicate]++;\n\n // Extract layers\n const sourceLayer = rel.source.split(\".\")[0];\n const targetLayer = rel.target.split(\".\")[0];\n\n // Count cross vs intra-layer\n if (sourceLayer === targetLayer) {\n intraLayerCount++;\n } else {\n crossLayerCount++;\n }\n\n // Count by pair\n const pair = `${sourceLayer}-${targetLayer}`;\n if (!byPair[pair]) {\n byPair[pair] = 0;\n }\n byPair[pair]++;\n }\n\n return {\n total: relationships.length,\n byPredicate,\n crossLayerCount,\n intraLayerCount,\n byPair,\n };\n }\n\n /**\n * Collect validation information\n * Runs model validation and returns actual validation status\n * Preserves error details for debugging\n */\n private async collectValidationInfo(): Promise<ValidationInfo> {\n try {\n const validator = new Validator();\n const result = await validator.validateModel(this.model);\n\n return {\n isValid: result.isValid(),\n lastValidated: new Date().toISOString(),\n errors: result.errors.length,\n warnings: result.warnings.length,\n };\n } catch (error) {\n // Preserve actual error information for debugging\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[stats-collector] Validation failed: ${errorMessage}`);\n\n // Return failed state with sentinel error count -1 indicating collection failure\n // (cannot be confused with actual error count which is always >= 0)\n return {\n isValid: false,\n lastValidated: new Date().toISOString(),\n errors: -1, // Sentinel: validation collection itself failed\n warnings: 0,\n };\n }\n }\n\n /**\n * Find orphaned elements (elements with no relationships)\n */\n private async findOrphanedElements(): Promise<string[]> {\n const orphaned: string[] = [];\n const allRelationships = this.model.relationships.getAll();\n\n // Get all element IDs\n const allElements = new Set<string>();\n const layerNames = this.model.getLayerNames();\n\n for (const layerName of layerNames) {\n const layer = await this.model.getLayer(layerName);\n if (!layer) continue;\n\n for (const element of layer.listElements()) {\n allElements.add(element.path || element.id);\n }\n }\n\n // Find elements with no relationships\n const elementsWithRelationships = new Set<string>();\n for (const rel of allRelationships) {\n elementsWithRelationships.add(rel.source);\n elementsWithRelationships.add(rel.target);\n }\n\n for (const elementId of allElements) {\n if (!elementsWithRelationships.has(elementId)) {\n orphaned.push(elementId);\n }\n }\n\n return orphaned;\n }\n\n /**\n * Calculate overall model completeness\n * Weighted average of layer coverage\n */\n private calculateCompleteness(layerStats: LayerStats[]): number {\n if (layerStats.length === 0) return 0;\n\n const totalCoverage = layerStats.reduce((sum, layer) => sum + layer.coverage, 0);\n return Math.round(totalCoverage / layerStats.length);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/archimate-exporter.ts"],
4
- "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { escapeXml } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * ArchiMate XML Exporter for layers 1, 2, 4, 5\n * Generates valid ArchiMate 3.2 XML format\n */\nexport class ArchiMateExporter implements Exporter {\n name = \"ArchiMate\";\n supportedLayers = [\"motivation\", \"business\", \"application\", \"technology\"];\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.archimate\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n // XML declaration\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n\n // Root element with namespaces\n lines.push('<model xmlns=\"http://www.opengroup.org/xsd/archimate/3.0/\"');\n lines.push(' xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"');\n lines.push(\n ' xsi:schemaLocation=\"http://www.opengroup.org/xsd/archimate/3.0/ http://www.opengroup.org/xsd/archimate/3.1/archimate3_Diagram.xsd\">'\n );\n\n // Model metadata\n lines.push(` <name>${escapeXml(model.manifest.name)}</name>`);\n if (model.manifest.description) {\n lines.push(` <documentation>${escapeXml(model.manifest.description)}</documentation>`);\n }\n\n // Export elements - load from each layer directly\n lines.push(\" <elements>\");\n\n const layersToExport = options.layers || this.supportedLayers;\n const elementsByLayer: Array<{ layer: string; element: any }> = [];\n\n // Load elements from each supported layer\n for (const layerName of layersToExport) {\n if (!this.supportedLayers.includes(layerName)) continue;\n\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n const layerElements = layer.listElements();\n for (const element of layerElements) {\n elementsByLayer.push({ layer: layerName, element });\n }\n }\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.elementCount\", elementsByLayer.length);\n }\n\n for (const { layer, element } of elementsByLayer) {\n const archiType = this.mapToArchiMateType(element.type, layer);\n lines.push(` <element identifier=\"${element.path}\" xsi:type=\"${archiType}\">`);\n lines.push(` <name>${escapeXml(element.name)}</name>`);\n\n if (element.description) {\n lines.push(` <documentation>${escapeXml(element.description)}</documentation>`);\n }\n\n // Add properties section\n lines.push(\" <properties>\");\n\n // Handle source_reference from element (if present)\n if (element.source_reference) {\n const flattenedProps = this.flattenSourceReference(element.source_reference);\n for (const [flatKey, flatValue] of Object.entries(flattenedProps)) {\n lines.push(\n ` <property key=\"${escapeXml(flatKey)}\" value=\"${escapeXml(String(flatValue))}\" />`\n );\n }\n }\n\n // Add element attributes as properties\n const attrKeys = Object.keys(element.attributes || {});\n if (attrKeys.length > 0) {\n for (const key of attrKeys) {\n const val = element.attributes?.[key];\n const strValue = this.valueToString(val);\n lines.push(\n ` <property key=\"${escapeXml(key)}\" value=\"${escapeXml(strValue)}\" />`\n );\n }\n }\n\n lines.push(\" </properties>\");\n lines.push(\" </element>\");\n }\n\n lines.push(\" </elements>\");\n\n // Export relationships\n lines.push(\" <relationships>\");\n\n let relationshipCount = 0;\n const elementIds = new Set(elementsByLayer.map((e) => e.element.path));\n\n // Filter relationships to those between exported elements\n for (const rel of model.relationships.getAll()) {\n if (!elementIds.has(rel.source) || !elementIds.has(rel.target)) {\n continue;\n }\n\n const relId = this.escapeId(`${rel.source}-${rel.predicate}-${rel.target}`);\n lines.push(` <relationship identifier=\"${relId}\" `);\n lines.push(` source=\"${rel.source}\" target=\"${rel.target}\" `);\n lines.push(` xsi:type=\"Association\">`);\n lines.push(` <name>${rel.predicate}</name>`);\n lines.push(` </relationship>`);\n relationshipCount++;\n }\n\n lines.push(\" </relationships>\");\n lines.push(\"</model>\");\n\n const result = lines.join(\"\\n\");\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.relationshipCount\", relationshipCount);\n (span as any).setAttribute(\"export.size\", result.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return result;\n } catch (error) {\n if (isTelemetryEnabled && span) {\n (span as any).recordException(error as Error);\n (span as any).setStatus({\n code: 2,\n message: getErrorMessage(error),\n });\n }\n throw error;\n } finally {\n endSpan(span);\n }\n }\n\n /**\n * Map element type and layer to ArchiMate type\n */\n private mapToArchiMateType(type: string, layer: string): string {\n const mapping: Record<string, Record<string, string>> = {\n motivation: {\n stakeholder: \"Stakeholder\",\n driver: \"Driver\",\n assessment: \"Assessment\",\n goal: \"Goal\",\n outcome: \"Outcome\",\n principle: \"Principle\",\n requirement: \"Requirement\",\n constraint: \"Constraint\",\n meaning: \"Meaning\",\n value: \"Value\",\n },\n business: {\n businessactor: \"BusinessActor\",\n businessrole: \"BusinessRole\",\n businesscollaboration: \"BusinessCollaboration\",\n businessinterface: \"BusinessInterface\",\n businessprocess: \"BusinessProcess\",\n businessfunction: \"BusinessFunction\",\n businessinteraction: \"BusinessInteraction\",\n businessevent: \"BusinessEvent\",\n businessservice: \"BusinessService\",\n businessobject: \"BusinessObject\",\n contract: \"Contract\",\n product: \"Product\",\n representation: \"Representation\",\n },\n application: {\n applicationcomponent: \"ApplicationComponent\",\n applicationcollaboration: \"ApplicationCollaboration\",\n applicationinterface: \"ApplicationInterface\",\n applicationfunction: \"ApplicationFunction\",\n applicationinteraction: \"ApplicationInteraction\",\n applicationprocess: \"ApplicationProcess\",\n applicationevent: \"ApplicationEvent\",\n applicationservice: \"ApplicationService\",\n dataobject: \"DataObject\",\n },\n technology: {\n node: \"Node\",\n device: \"Device\",\n systemsoftware: \"SystemSoftware\",\n technologycollaboration: \"TechnologyCollaboration\",\n technologyinterface: \"TechnologyInterface\",\n path: \"Path\",\n communicationnetwork: \"CommunicationNetwork\",\n technologyfunction: \"TechnologyFunction\",\n technologyprocess: \"TechnologyProcess\",\n technologyinteraction: \"TechnologyInteraction\",\n technologyevent: \"TechnologyEvent\",\n technologyservice: \"TechnologyService\",\n artifact: \"Artifact\",\n },\n };\n\n return mapping[layer]?.[type] || \"Element\";\n }\n\n /**\n * Escape ID characters for use in identifiers\n */\n private escapeId(str: string): string {\n return str.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n }\n\n /**\n * Convert value to string representation\n */\n private valueToString(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) return `[${value.map((v) => this.valueToString(v)).join(\", \")}]`;\n if (value && typeof value === \"object\") {\n return JSON.stringify(value);\n }\n return String(value);\n }\n\n /**\n * Flatten source reference structure into ArchiMate properties\n * Converts nested structure like { provenance: \"extracted\", locations: [...] }\n * into flat properties like \"source.provenance\", \"source.file.0\", etc.\n */\n private flattenSourceReference(sourceRef: any): Record<string, string> {\n const props: Record<string, string> = {};\n\n if (!sourceRef || typeof sourceRef !== \"object\") {\n return props;\n }\n\n // Add provenance\n if (sourceRef.provenance) {\n props[\"source.provenance\"] = sourceRef.provenance;\n }\n\n // Add locations\n if (Array.isArray(sourceRef.locations)) {\n sourceRef.locations.forEach((location: any, index: number) => {\n if (location.file) {\n props[`source.file.${index}`] = location.file;\n }\n if (location.symbol) {\n props[`source.symbol.${index}`] = location.symbol;\n }\n if (location.line !== undefined) {\n props[`source.line.${index}`] = String(location.line);\n }\n if (location.column !== undefined) {\n props[`source.column.${index}`] = String(location.column);\n }\n });\n }\n\n // Add repository info\n if (sourceRef.repository) {\n if (sourceRef.repository.url) {\n props[\"source.repository.url\"] = sourceRef.repository.url;\n }\n if (sourceRef.repository.commit) {\n props[\"source.repository.commit\"] = sourceRef.repository.commit;\n }\n if (sourceRef.repository.branch) {\n props[\"source.repository.branch\"] = sourceRef.repository.branch;\n }\n }\n\n return props;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { escapeXml } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * ArchiMate XML Exporter for layers 1, 2, 5, 6 (Motivation, Business, Application, Technology)\n * Generates valid ArchiMate 3.2 XML format\n * Note: Product layer (layer 3) does not participate in ArchiMate export\n */\nexport class ArchiMateExporter implements Exporter {\n name = \"ArchiMate\";\n supportedLayers = [\"motivation\", \"business\", \"application\", \"technology\"];\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.archimate\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n // XML declaration\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n\n // Root element with namespaces\n lines.push('<model xmlns=\"http://www.opengroup.org/xsd/archimate/3.0/\"');\n lines.push(' xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"');\n lines.push(\n ' xsi:schemaLocation=\"http://www.opengroup.org/xsd/archimate/3.0/ http://www.opengroup.org/xsd/archimate/3.1/archimate3_Diagram.xsd\">'\n );\n\n // Model metadata\n lines.push(` <name>${escapeXml(model.manifest.name)}</name>`);\n if (model.manifest.description) {\n lines.push(` <documentation>${escapeXml(model.manifest.description)}</documentation>`);\n }\n\n // Export elements - load from each layer directly\n lines.push(\" <elements>\");\n\n const layersToExport = options.layers || this.supportedLayers;\n const elementsByLayer: Array<{ layer: string; element: any }> = [];\n\n // Load elements from each supported layer\n for (const layerName of layersToExport) {\n if (!this.supportedLayers.includes(layerName)) continue;\n\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n const layerElements = layer.listElements();\n for (const element of layerElements) {\n elementsByLayer.push({ layer: layerName, element });\n }\n }\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.elementCount\", elementsByLayer.length);\n }\n\n for (const { layer, element } of elementsByLayer) {\n const archiType = this.mapToArchiMateType(element.type, layer);\n lines.push(` <element identifier=\"${element.path}\" xsi:type=\"${archiType}\">`);\n lines.push(` <name>${escapeXml(element.name)}</name>`);\n\n if (element.description) {\n lines.push(` <documentation>${escapeXml(element.description)}</documentation>`);\n }\n\n // Add properties section\n lines.push(\" <properties>\");\n\n // Handle source_reference from element (if present)\n if (element.source_reference) {\n const flattenedProps = this.flattenSourceReference(element.source_reference);\n for (const [flatKey, flatValue] of Object.entries(flattenedProps)) {\n lines.push(\n ` <property key=\"${escapeXml(flatKey)}\" value=\"${escapeXml(String(flatValue))}\" />`\n );\n }\n }\n\n // Add element attributes as properties\n const attrKeys = Object.keys(element.attributes || {});\n if (attrKeys.length > 0) {\n for (const key of attrKeys) {\n const val = element.attributes?.[key];\n const strValue = this.valueToString(val);\n lines.push(\n ` <property key=\"${escapeXml(key)}\" value=\"${escapeXml(strValue)}\" />`\n );\n }\n }\n\n lines.push(\" </properties>\");\n lines.push(\" </element>\");\n }\n\n lines.push(\" </elements>\");\n\n // Export relationships\n lines.push(\" <relationships>\");\n\n let relationshipCount = 0;\n const elementIds = new Set(elementsByLayer.map((e) => e.element.path));\n\n // Filter relationships to those between exported elements\n for (const rel of model.relationships.getAll()) {\n if (!elementIds.has(rel.source) || !elementIds.has(rel.target)) {\n continue;\n }\n\n const relId = this.escapeId(`${rel.source}-${rel.predicate}-${rel.target}`);\n lines.push(` <relationship identifier=\"${relId}\" `);\n lines.push(` source=\"${rel.source}\" target=\"${rel.target}\" `);\n lines.push(` xsi:type=\"Association\">`);\n lines.push(` <name>${rel.predicate}</name>`);\n lines.push(` </relationship>`);\n relationshipCount++;\n }\n\n lines.push(\" </relationships>\");\n lines.push(\"</model>\");\n\n const result = lines.join(\"\\n\");\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.relationshipCount\", relationshipCount);\n (span as any).setAttribute(\"export.size\", result.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return result;\n } catch (error) {\n if (isTelemetryEnabled && span) {\n (span as any).recordException(error as Error);\n (span as any).setStatus({\n code: 2,\n message: getErrorMessage(error),\n });\n }\n throw error;\n } finally {\n endSpan(span);\n }\n }\n\n /**\n * Map element type and layer to ArchiMate type\n */\n private mapToArchiMateType(type: string, layer: string): string {\n const mapping: Record<string, Record<string, string>> = {\n motivation: {\n stakeholder: \"Stakeholder\",\n driver: \"Driver\",\n assessment: \"Assessment\",\n goal: \"Goal\",\n outcome: \"Outcome\",\n principle: \"Principle\",\n requirement: \"Requirement\",\n constraint: \"Constraint\",\n meaning: \"Meaning\",\n value: \"Value\",\n },\n business: {\n businessactor: \"BusinessActor\",\n businessrole: \"BusinessRole\",\n businesscollaboration: \"BusinessCollaboration\",\n businessinterface: \"BusinessInterface\",\n businessprocess: \"BusinessProcess\",\n businessfunction: \"BusinessFunction\",\n businessinteraction: \"BusinessInteraction\",\n businessevent: \"BusinessEvent\",\n businessservice: \"BusinessService\",\n businessobject: \"BusinessObject\",\n contract: \"Contract\",\n product: \"Product\",\n representation: \"Representation\",\n },\n application: {\n applicationcomponent: \"ApplicationComponent\",\n applicationcollaboration: \"ApplicationCollaboration\",\n applicationinterface: \"ApplicationInterface\",\n applicationfunction: \"ApplicationFunction\",\n applicationinteraction: \"ApplicationInteraction\",\n applicationprocess: \"ApplicationProcess\",\n applicationevent: \"ApplicationEvent\",\n applicationservice: \"ApplicationService\",\n dataobject: \"DataObject\",\n },\n technology: {\n node: \"Node\",\n device: \"Device\",\n systemsoftware: \"SystemSoftware\",\n technologycollaboration: \"TechnologyCollaboration\",\n technologyinterface: \"TechnologyInterface\",\n path: \"Path\",\n communicationnetwork: \"CommunicationNetwork\",\n technologyfunction: \"TechnologyFunction\",\n technologyprocess: \"TechnologyProcess\",\n technologyinteraction: \"TechnologyInteraction\",\n technologyevent: \"TechnologyEvent\",\n technologyservice: \"TechnologyService\",\n artifact: \"Artifact\",\n },\n };\n\n return mapping[layer]?.[type] || \"Element\";\n }\n\n /**\n * Escape ID characters for use in identifiers\n */\n private escapeId(str: string): string {\n return str.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n }\n\n /**\n * Convert value to string representation\n */\n private valueToString(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) return `[${value.map((v) => this.valueToString(v)).join(\", \")}]`;\n if (value && typeof value === \"object\") {\n return JSON.stringify(value);\n }\n return String(value);\n }\n\n /**\n * Flatten source reference structure into ArchiMate properties\n * Converts nested structure like { provenance: \"extracted\", locations: [...] }\n * into flat properties like \"source.provenance\", \"source.file.0\", etc.\n */\n private flattenSourceReference(sourceRef: any): Record<string, string> {\n const props: Record<string, string> = {};\n\n if (!sourceRef || typeof sourceRef !== \"object\") {\n return props;\n }\n\n // Add provenance\n if (sourceRef.provenance) {\n props[\"source.provenance\"] = sourceRef.provenance;\n }\n\n // Add locations\n if (Array.isArray(sourceRef.locations)) {\n sourceRef.locations.forEach((location: any, index: number) => {\n if (location.file) {\n props[`source.file.${index}`] = location.file;\n }\n if (location.symbol) {\n props[`source.symbol.${index}`] = location.symbol;\n }\n if (location.line !== undefined) {\n props[`source.line.${index}`] = String(location.line);\n }\n if (location.column !== undefined) {\n props[`source.column.${index}`] = String(location.column);\n }\n });\n }\n\n // Add repository info\n if (sourceRef.repository) {\n if (sourceRef.repository.url) {\n props[\"source.repository.url\"] = sourceRef.repository.url;\n }\n if (sourceRef.repository.commit) {\n props[\"source.repository.commit\"] = sourceRef.repository.commit;\n }\n if (sourceRef.repository.branch) {\n props[\"source.repository.branch\"] = sourceRef.repository.branch;\n }\n }\n\n return props;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/json-schema-exporter.ts"],
4
- "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * JSON Schema Draft 7 Exporter for layer 7 (Data Model)\n */\nexport class JsonSchemaExporter implements Exporter {\n name = \"JSON Schema\";\n supportedLayers = [\"data-model\"];\n\n async export(model: Model, _options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled ? startSpan(\"export.format.json-schema\") : null;\n\n try {\n // Query graph model for data-model layer nodes\n const nodes = model.graph.getNodesByLayer(\"data-model\");\n if (nodes.length === 0) {\n throw new Error(\"No Data Model layer found in model\");\n }\n\n // Create a root schema that references all entities\n const rootSchema: Record<string, unknown> = {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n $id: `${model.manifest.name.toLowerCase().replace(/\\s+/g, \"-\")}-schema`,\n title: model.manifest.name,\n description: model.manifest.description || \"Data model schema\",\n type: \"object\",\n definitions: {},\n };\n\n // Map from spec node type to JSON Schema type for cases where the type\n // is determined by the node kind rather than a stored attribute.\n const nodeTypeToSchemaType: Record<string, string> = {\n arrayschema: \"array\",\n stringschema: \"string\",\n numericschema: \"number\",\n objectschema: \"object\",\n entity: \"object\",\n };\n\n // Process all data-model nodes as JSON Schema definitions.\n // The spec uses types: jsonschema, schemadefinition, objectschema, arrayschema,\n // numericschema, stringschema, schemacomposition, schemaproperty, reference, entity.\n for (const node of nodes) {\n const entityName = node.name;\n // GraphNode stores element attributes as a direct field (node.attributes),\n // not nested under node.properties.\n const attrs = node.attributes as Record<string, unknown> | undefined;\n // 'properties' in JSON Schema context means the sub-schemas for object properties,\n // stored in the element's attributes.properties field.\n const properties = attrs?.properties as Record<string, unknown> | undefined;\n const required = (attrs?.required as string[] | undefined) ?? (node.properties?.required as string[] | undefined);\n const description = node.description;\n\n // Derive JSON Schema type: prefer the value stored in the element's attributes\n // (user-specified), then fall back to the node-type map, then \"object\".\n const schemaType =\n typeof attrs?.type === \"string\"\n ? attrs.type\n : (nodeTypeToSchemaType[node.type] ?? \"object\");\n\n const entitySchema: Record<string, unknown> = {\n title: entityName,\n ...(description && { description }),\n type: schemaType,\n ...(properties && { properties }),\n ...(required && { required }),\n };\n\n // Add additional metadata\n const additionalProperties = attrs?.additionalProperties as boolean;\n if (additionalProperties !== undefined) {\n entitySchema.additionalProperties = additionalProperties;\n }\n\n // Add constraints if present\n const constraints = attrs?.constraints as Record<string, unknown>;\n if (constraints) {\n Object.assign(entitySchema, constraints);\n }\n\n // Add source reference if present (check both naming conventions)\n const sourceRef =\n attrs?.[\"x-source-reference\"] || attrs?.[\"source-reference\"]\n || node.source_reference;\n if (sourceRef) {\n entitySchema[\"x-source-reference\"] = sourceRef;\n }\n\n (rootSchema.definitions as Record<string, unknown>)[node.id] = entitySchema;\n }\n\n // Add relationships as references between entities - query graph edges\n const relationships: Record<string, unknown> = {};\n const edges = model.graph.getAllEdges();\n const nodeIds = new Set(nodes.map((n) => n.id));\n\n for (const edge of edges) {\n // Only include edges where source is an entity in the data-model layer\n if (nodeIds.has(edge.source) && nodeIds.has(edge.destination)) {\n const relKey = `${edge.source}-${edge.predicate}`;\n if (!relationships[relKey]) {\n relationships[relKey] = [];\n }\n (relationships[relKey] as Array<{ target: string }>).push({\n target: edge.destination,\n });\n }\n }\n\n if (Object.keys(relationships).length > 0) {\n rootSchema.relationships = relationships;\n }\n\n // Add metadata section\n const entityCount = nodes.length;\n const metadata: Record<string, unknown> = {\n version: model.manifest.version || \"1.0.0\",\n ...(model.manifest.author && { author: model.manifest.author }),\n ...(model.manifest.created && { created: model.manifest.created }),\n ...(model.manifest.modified && { modified: model.manifest.modified }),\n entityCount,\n };\n rootSchema.metadata = metadata;\n\n const result = JSON.stringify(rootSchema, null, 2) + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\n \"export.entityCount\",\n Object.keys(rootSchema.definitions || {}).length\n );\n (span as any).setAttribute(\"export.relationshipCount\", Object.keys(relationships).length);\n (span as any).setAttribute(\"export.size\", result.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return result;\n } catch (error) {\n if (isTelemetryEnabled && span) {\n (span as any).recordException(error as Error);\n (span as any).setStatus({\n code: 2,\n message: getErrorMessage(error),\n });\n }\n throw error;\n } finally {\n endSpan(span);\n }\n }\n}\n"],
4
+ "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * JSON Schema Draft 7 Exporter for layer 8 (Data Model)\n */\nexport class JsonSchemaExporter implements Exporter {\n name = \"JSON Schema\";\n supportedLayers = [\"data-model\"];\n\n async export(model: Model, _options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled ? startSpan(\"export.format.json-schema\") : null;\n\n try {\n // Query graph model for data-model layer nodes\n const nodes = model.graph.getNodesByLayer(\"data-model\");\n if (nodes.length === 0) {\n throw new Error(\"No Data Model layer found in model\");\n }\n\n // Create a root schema that references all entities\n const rootSchema: Record<string, unknown> = {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n $id: `${model.manifest.name.toLowerCase().replace(/\\s+/g, \"-\")}-schema`,\n title: model.manifest.name,\n description: model.manifest.description || \"Data model schema\",\n type: \"object\",\n definitions: {},\n };\n\n // Map from spec node type to JSON Schema type for cases where the type\n // is determined by the node kind rather than a stored attribute.\n const nodeTypeToSchemaType: Record<string, string> = {\n arrayschema: \"array\",\n stringschema: \"string\",\n numericschema: \"number\",\n objectschema: \"object\",\n entity: \"object\",\n };\n\n // Process all data-model nodes as JSON Schema definitions.\n // The spec uses types: jsonschema, schemadefinition, objectschema, arrayschema,\n // numericschema, stringschema, schemacomposition, schemaproperty, reference, entity.\n for (const node of nodes) {\n const entityName = node.name;\n // GraphNode stores element attributes as a direct field (node.attributes),\n // not nested under node.properties.\n const attrs = node.attributes as Record<string, unknown> | undefined;\n // 'properties' in JSON Schema context means the sub-schemas for object properties,\n // stored in the element's attributes.properties field.\n const properties = attrs?.properties as Record<string, unknown> | undefined;\n const required = (attrs?.required as string[] | undefined) ?? (node.properties?.required as string[] | undefined);\n const description = node.description;\n\n // Derive JSON Schema type: prefer the value stored in the element's attributes\n // (user-specified), then fall back to the node-type map, then \"object\".\n const schemaType =\n typeof attrs?.type === \"string\"\n ? attrs.type\n : (nodeTypeToSchemaType[node.type] ?? \"object\");\n\n const entitySchema: Record<string, unknown> = {\n title: entityName,\n ...(description && { description }),\n type: schemaType,\n ...(properties && { properties }),\n ...(required && { required }),\n };\n\n // Add additional metadata\n const additionalProperties = attrs?.additionalProperties as boolean;\n if (additionalProperties !== undefined) {\n entitySchema.additionalProperties = additionalProperties;\n }\n\n // Add constraints if present\n const constraints = attrs?.constraints as Record<string, unknown>;\n if (constraints) {\n Object.assign(entitySchema, constraints);\n }\n\n // Add source reference if present (check both naming conventions)\n const sourceRef =\n attrs?.[\"x-source-reference\"] || attrs?.[\"source-reference\"]\n || node.source_reference;\n if (sourceRef) {\n entitySchema[\"x-source-reference\"] = sourceRef;\n }\n\n (rootSchema.definitions as Record<string, unknown>)[node.id] = entitySchema;\n }\n\n // Add relationships as references between entities - query graph edges\n const relationships: Record<string, unknown> = {};\n const edges = model.graph.getAllEdges();\n const nodeIds = new Set(nodes.map((n) => n.id));\n\n for (const edge of edges) {\n // Only include edges where source is an entity in the data-model layer\n if (nodeIds.has(edge.source) && nodeIds.has(edge.destination)) {\n const relKey = `${edge.source}-${edge.predicate}`;\n if (!relationships[relKey]) {\n relationships[relKey] = [];\n }\n (relationships[relKey] as Array<{ target: string }>).push({\n target: edge.destination,\n });\n }\n }\n\n if (Object.keys(relationships).length > 0) {\n rootSchema.relationships = relationships;\n }\n\n // Add metadata section\n const entityCount = nodes.length;\n const metadata: Record<string, unknown> = {\n version: model.manifest.version || \"1.0.0\",\n ...(model.manifest.author && { author: model.manifest.author }),\n ...(model.manifest.created && { created: model.manifest.created }),\n ...(model.manifest.modified && { modified: model.manifest.modified }),\n entityCount,\n };\n rootSchema.metadata = metadata;\n\n const result = JSON.stringify(rootSchema, null, 2) + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\n \"export.entityCount\",\n Object.keys(rootSchema.definitions || {}).length\n );\n (span as any).setAttribute(\"export.relationshipCount\", Object.keys(relationships).length);\n (span as any).setAttribute(\"export.size\", result.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return result;\n } catch (error) {\n if (isTelemetryEnabled && span) {\n (span as any).recordException(error as Error);\n (span as any).setStatus({\n code: 2,\n message: getErrorMessage(error),\n });\n }\n throw error;\n } finally {\n endSpan(span);\n }\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }