@documentation-robotics/cli 0.1.8 → 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 (120) 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/core/layers.js +1 -0
  19. package/dist/core/layers.js.map +2 -2
  20. package/dist/core/migration-registry.js +59 -2
  21. package/dist/core/migration-registry.js.map +2 -2
  22. package/dist/core/model.js +3 -16
  23. package/dist/core/model.js.map +2 -2
  24. package/dist/core/spec-loader.js.map +1 -1
  25. package/dist/core/stats-collector.js +2 -0
  26. package/dist/core/stats-collector.js.map +2 -2
  27. package/dist/export/archimate-exporter.js.map +2 -2
  28. package/dist/export/json-schema-exporter.js.map +1 -1
  29. package/dist/export/markdown-exporter.js.map +1 -1
  30. package/dist/export/markdown-utils.js +1 -0
  31. package/dist/export/markdown-utils.js.map +2 -2
  32. package/dist/export/mermaid-markdown-exporter.js.map +1 -1
  33. package/dist/export/openapi-exporter.js.map +1 -1
  34. package/dist/export/plantuml-exporter.js.map +1 -1
  35. package/dist/export/report-formatters.js +2 -2
  36. package/dist/export/report-formatters.js.map +1 -1
  37. package/dist/export/types.js +1 -0
  38. package/dist/export/types.js.map +2 -2
  39. package/dist/generated/layer-registry.js +44 -31
  40. package/dist/generated/layer-registry.js.map +2 -2
  41. package/dist/generated/layer-types.js +1 -1
  42. package/dist/generated/layer-types.js.map +2 -2
  43. package/dist/generated/node-types.js +45 -0
  44. package/dist/generated/node-types.js.map +2 -2
  45. package/dist/generated/relationship-index.js +201 -19
  46. package/dist/generated/relationship-index.js.map +2 -2
  47. package/dist/import/archimate-importer.js.map +1 -1
  48. package/dist/import/openapi-importer.js.map +1 -1
  49. package/dist/integrations/claude_code/agents/dr-advisor.md +14 -13
  50. package/dist/integrations/claude_code/agents/dr-extractor.md +2 -2
  51. package/dist/integrations/claude_code/commands/dr-info.md +1 -1
  52. package/dist/integrations/claude_code/commands/dr-init.md +3 -3
  53. package/dist/integrations/claude_code/commands/dr-map.md +2 -2
  54. package/dist/integrations/claude_code/commands/dr-model.md +1 -1
  55. package/dist/integrations/claude_code/commands/dr-relate.md +14 -4
  56. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +3 -3
  57. package/dist/integrations/claude_code/docs/QUICK_START.md +1 -1
  58. package/dist/integrations/claude_code/docs/README.md +2 -2
  59. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +2 -2
  60. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +679 -0
  61. package/dist/integrations/claude_code/skills/{dr_08_dataset_layer → dr_09_dataset_layer}/SKILL.md +1 -1
  62. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +2 -2
  63. package/dist/integrations/github_copilot/agents/dr-advisor.md +14 -13
  64. package/dist/integrations/github_copilot/agents/dr-architect.md +15 -14
  65. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +2 -2
  66. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +253 -0
  67. package/dist/reports/model-report-orchestrator.js +3 -3
  68. package/dist/reports/model-report-orchestrator.js.map +1 -1
  69. package/dist/schemas/bundled/analyzers/cbm.json +2 -2
  70. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  71. package/dist/schemas/bundled/api.json +3 -3
  72. package/dist/schemas/bundled/apm.json +3 -3
  73. package/dist/schemas/bundled/application.json +3 -3
  74. package/dist/schemas/bundled/base.json +29 -1
  75. package/dist/schemas/bundled/business.json +1 -1
  76. package/dist/schemas/bundled/data-model.json +3 -3
  77. package/dist/schemas/bundled/data-store.json +3 -3
  78. package/dist/schemas/bundled/manifest.json +20 -13
  79. package/dist/schemas/bundled/motivation.json +1 -1
  80. package/dist/schemas/bundled/navigation.json +3 -3
  81. package/dist/schemas/bundled/product.json +469 -0
  82. package/dist/schemas/bundled/security.json +3 -3
  83. package/dist/schemas/bundled/technology.json +3 -3
  84. package/dist/schemas/bundled/testing.json +3 -3
  85. package/dist/schemas/bundled/ux.json +3 -3
  86. package/dist/server/server.js +4 -15
  87. package/dist/server/server.js.map +2 -2
  88. package/dist/telemetry/index.js +1 -1
  89. package/dist/utils/spec-installer.js +2 -2
  90. package/dist/utils/spec-installer.js.map +1 -1
  91. package/dist/utils/spec-version.js +1 -1
  92. package/dist/utils/spec-version.js.map +1 -1
  93. package/dist/viewer/README.md +6 -6
  94. package/dist/viewer/assets/main-D114QwJo.css +1 -0
  95. package/dist/viewer/assets/main-DyuokeQj.js +81 -0
  96. package/dist/viewer/index.html +2 -2
  97. package/dist/viewer/manifest.json +10 -10
  98. package/dist/viewer/version.json +1 -1
  99. package/package.json +1 -1
  100. package/dist/viewer/assets/main-CY0Xufuj.css +0 -1
  101. package/dist/viewer/assets/main-DtgErnwF.js +0 -81
  102. /package/dist/integrations/claude_code/skills/{dr_03_security_layer → dr_04_security_layer}/SKILL.md +0 -0
  103. /package/dist/integrations/claude_code/skills/{dr_04_application_layer → dr_05_application_layer}/SKILL.md +0 -0
  104. /package/dist/integrations/claude_code/skills/{dr_05_technology_layer → dr_06_technology_layer}/SKILL.md +0 -0
  105. /package/dist/integrations/claude_code/skills/{dr_06_api_layer → dr_07_api_layer}/SKILL.md +0 -0
  106. /package/dist/integrations/claude_code/skills/{dr_07_data_model_layer → dr_08_data_model_layer}/SKILL.md +0 -0
  107. /package/dist/integrations/claude_code/skills/{dr_09_ux_layer → dr_10_ux_layer}/SKILL.md +0 -0
  108. /package/dist/integrations/claude_code/skills/{dr_10_navigation_layer → dr_11_navigation_layer}/SKILL.md +0 -0
  109. /package/dist/integrations/claude_code/skills/{dr_11_apm_layer → dr_12_apm_layer}/SKILL.md +0 -0
  110. /package/dist/integrations/claude_code/skills/{dr_12_testing_layer → dr_13_testing_layer}/SKILL.md +0 -0
  111. /package/dist/integrations/github_copilot/skills/{dr_03_security_layer → dr_04_security_layer}/SKILL.md +0 -0
  112. /package/dist/integrations/github_copilot/skills/{dr_04_application_layer → dr_05_application_layer}/SKILL.md +0 -0
  113. /package/dist/integrations/github_copilot/skills/{dr_05_technology_layer → dr_06_technology_layer}/SKILL.md +0 -0
  114. /package/dist/integrations/github_copilot/skills/{dr_06_api_layer → dr_07_api_layer}/SKILL.md +0 -0
  115. /package/dist/integrations/github_copilot/skills/{dr_07_data_model_layer → dr_08_data_model_layer}/SKILL.md +0 -0
  116. /package/dist/integrations/github_copilot/skills/{dr_08_dataset_layer → dr_09_dataset_layer}/SKILL.md +0 -0
  117. /package/dist/integrations/github_copilot/skills/{dr_09_ux_layer → dr_10_ux_layer}/SKILL.md +0 -0
  118. /package/dist/integrations/github_copilot/skills/{dr_10_navigation_layer → dr_11_navigation_layer}/SKILL.md +0 -0
  119. /package/dist/integrations/github_copilot/skills/{dr_11_apm_layer → dr_12_apm_layer}/SKILL.md +0 -0
  120. /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/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
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/markdown-exporter.ts"],
4
- "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { ALL_LAYERS } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { formatLayerName } from \"../utils/layer-name-formatter.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { escapeMarkdown, valueToMarkdown, getLayerDescription } from \"./markdown-utils.js\";\n\n/**\n * Markdown Exporter - generates comprehensive markdown documentation\n * Supports all 12 layers\n */\nexport class MarkdownExporter implements Exporter {\n name = \"Markdown\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.markdown\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n // Document header\n lines.push(`# ${this.escapeMarkdownLocal(model.manifest.name)}`);\n lines.push(\"\");\n\n if (model.manifest.description) {\n lines.push(`${model.manifest.description}`);\n lines.push(\"\");\n }\n\n // Model metadata\n lines.push(\"## Model Information\");\n lines.push(\"\");\n lines.push(`| Property | Value |`);\n lines.push(`| -------- | ----- |`);\n lines.push(`| Version | ${model.manifest.version || \"1.0.0\"} |`);\n if (model.manifest.author) {\n lines.push(`| Author | ${this.escapeMarkdownLocal(model.manifest.author)} |`);\n }\n if (model.manifest.created) {\n lines.push(`| Created | ${model.manifest.created} |`);\n }\n if (model.manifest.modified) {\n lines.push(`| Modified | ${model.manifest.modified} |`);\n }\n lines.push(\"\");\n\n const layersToExport = options.layers || this.supportedLayers;\n\n // Generate documentation for each layer\n for (const layerName of layersToExport) {\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n\n const elements = Array.from(layer.listElements());\n if (elements.length === 0) continue;\n\n lines.push(`## Layer: ${this.formatLayerName(layerName)}`);\n lines.push(\"\");\n\n const description = this.getLayerDescription(layerName);\n if (description) {\n lines.push(`*${description}*`);\n lines.push(\"\");\n }\n\n lines.push(`### Elements (${elements.length})`);\n lines.push(\"\");\n\n // Element table\n lines.push(`| ID | Name | Type | Description |`);\n lines.push(`| -- | ---- | ---- | ----------- |`);\n\n for (const element of elements) {\n const displayId = element.path || element.id;\n const id = this.escapeMarkdownLocal(displayId);\n const name = this.escapeMarkdownLocal(element.name);\n const type = this.escapeMarkdownLocal(element.type);\n const desc = element.description\n ? this.escapeMarkdownLocal(element.description).substring(0, 100)\n : \"\";\n\n lines.push(`| \\`${id}\\` | ${name} | ${type} | ${desc} |`);\n }\n\n lines.push(\"\");\n\n // Element details\n lines.push(`### Element Details`);\n lines.push(\"\");\n\n for (const element of elements) {\n const displayId = element.path || element.id;\n lines.push(`#### ${this.escapeMarkdownLocal(element.name)} (\\`${displayId}\\`)`);\n lines.push(\"\");\n\n if (element.description) {\n lines.push(`${element.description}`);\n lines.push(\"\");\n }\n\n lines.push(\"**Type:** \" + element.type);\n lines.push(\"\");\n\n // Source Reference\n const sourceRef = element.getSourceReference();\n if (sourceRef) {\n lines.push(\"### Source Code Location\");\n lines.push(\"\");\n lines.push(`**Provenance**: ${sourceRef.provenance}`);\n lines.push(\"\");\n\n lines.push(\"**Locations:**\");\n lines.push(\"\");\n (sourceRef.locations ?? []).forEach((loc, idx) => {\n lines.push(`${idx + 1}. \\`${this.escapeMarkdownLocal(loc.file)}\\``);\n if (loc.symbol) {\n lines.push(` - Symbol: \\`${this.escapeMarkdownLocal(loc.symbol)}\\``);\n }\n });\n lines.push(\"\");\n\n if (sourceRef.repository) {\n lines.push(\"**Repository Context:**\");\n lines.push(\"\");\n if (sourceRef.repository.url) {\n lines.push(`- Remote: ${sourceRef.repository.url}`);\n }\n if (sourceRef.repository.commit) {\n const shortCommit = sourceRef.repository.commit.substring(0, 7);\n if (sourceRef.repository.url) {\n lines.push(\n `- Commit: [\\`${shortCommit}\\`](${sourceRef.repository.url}/commit/${sourceRef.repository.commit})`\n );\n } else {\n lines.push(`- Commit: \\`${shortCommit}\\``);\n }\n }\n lines.push(\"\");\n }\n }\n\n // Properties (attributes)\n if (Object.keys(element.attributes).length > 0) {\n lines.push(\"**Properties:**\");\n lines.push(\"\");\n lines.push(`| Property | Value |`);\n lines.push(`| -------- | ----- |`);\n\n for (const [key, value] of Object.entries(element.attributes)) {\n const displayValue = this.valueToMarkdownLocal(value);\n lines.push(`| \\`${key}\\` | ${displayValue} |`);\n }\n lines.push(\"\");\n }\n\n // References\n if (element.references.length > 0) {\n lines.push(\"**Cross-Layer References:**\");\n lines.push(\"\");\n\n for (const ref of element.references) {\n const refDesc = ref.description ? ` - ${this.escapeMarkdownLocal(ref.description)}` : \"\";\n lines.push(`- **${ref.type}**: \\`${ref.target}\\`${refDesc}`);\n }\n lines.push(\"\");\n }\n\n // Relationships\n if (element.relationships.length > 0) {\n lines.push(\"**Relationships:**\");\n lines.push(\"\");\n\n for (const rel of element.relationships) {\n lines.push(`- **${rel.predicate}**: \\`${rel.target}\\``);\n }\n lines.push(\"\");\n }\n }\n\n // Layer summary\n lines.push(`### Layer Summary`);\n lines.push(\"\");\n lines.push(`- **Total Elements:** ${elements.length}`);\n\n const refCount = elements.reduce((sum, e) => sum + e.references.length, 0);\n const relCount = elements.reduce((sum, e) => sum + e.relationships.length, 0);\n\n lines.push(`- **Cross-Layer References:** ${refCount}`);\n lines.push(`- **Relationships:** ${relCount}`);\n\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n // Architecture Summary\n lines.push(\"## Architecture Summary\");\n lines.push(\"\");\n\n let totalElements = 0;\n let totalReferences = 0;\n let totalRelationships = 0;\n\n for (const layerName of layersToExport) {\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n\n for (const element of layer.listElements()) {\n totalElements++;\n totalReferences += element.references.length;\n totalRelationships += element.relationships.length;\n }\n }\n\n lines.push(`| Metric | Count |`);\n lines.push(`| ------ | ----- |`);\n lines.push(`| Total Elements | ${totalElements} |`);\n lines.push(`| Cross-Layer References | ${totalReferences} |`);\n lines.push(`| Relationships | ${totalRelationships} |`);\n lines.push(\"\");\n\n const result = lines.join(\"\\n\");\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.referenceCount\", totalReferences);\n (span as any).setAttribute(\"export.relationshipCount\", totalRelationships);\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 * Escape markdown special characters\n */\n private escapeMarkdownLocal(str: string): string {\n return escapeMarkdown(str);\n }\n\n /**\n * Format layer name for display\n */\n private formatLayerName(name: string): string {\n return formatLayerName(name);\n }\n\n /**\n * Get layer description\n */\n private getLayerDescription(layer: string): string {\n return getLayerDescription(layer);\n }\n\n /**\n * Convert value to markdown representation\n */\n private valueToMarkdownLocal(value: unknown): string {\n return valueToMarkdown(value);\n }\n}\n"],
4
+ "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { ALL_LAYERS } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { formatLayerName } from \"../utils/layer-name-formatter.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { escapeMarkdown, valueToMarkdown, getLayerDescription } from \"./markdown-utils.js\";\n\n/**\n * Markdown Exporter - generates comprehensive markdown documentation\n * Supports all 13 layers\n */\nexport class MarkdownExporter implements Exporter {\n name = \"Markdown\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.markdown\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n // Document header\n lines.push(`# ${this.escapeMarkdownLocal(model.manifest.name)}`);\n lines.push(\"\");\n\n if (model.manifest.description) {\n lines.push(`${model.manifest.description}`);\n lines.push(\"\");\n }\n\n // Model metadata\n lines.push(\"## Model Information\");\n lines.push(\"\");\n lines.push(`| Property | Value |`);\n lines.push(`| -------- | ----- |`);\n lines.push(`| Version | ${model.manifest.version || \"1.0.0\"} |`);\n if (model.manifest.author) {\n lines.push(`| Author | ${this.escapeMarkdownLocal(model.manifest.author)} |`);\n }\n if (model.manifest.created) {\n lines.push(`| Created | ${model.manifest.created} |`);\n }\n if (model.manifest.modified) {\n lines.push(`| Modified | ${model.manifest.modified} |`);\n }\n lines.push(\"\");\n\n const layersToExport = options.layers || this.supportedLayers;\n\n // Generate documentation for each layer\n for (const layerName of layersToExport) {\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n\n const elements = Array.from(layer.listElements());\n if (elements.length === 0) continue;\n\n lines.push(`## Layer: ${this.formatLayerName(layerName)}`);\n lines.push(\"\");\n\n const description = this.getLayerDescription(layerName);\n if (description) {\n lines.push(`*${description}*`);\n lines.push(\"\");\n }\n\n lines.push(`### Elements (${elements.length})`);\n lines.push(\"\");\n\n // Element table\n lines.push(`| ID | Name | Type | Description |`);\n lines.push(`| -- | ---- | ---- | ----------- |`);\n\n for (const element of elements) {\n const displayId = element.path || element.id;\n const id = this.escapeMarkdownLocal(displayId);\n const name = this.escapeMarkdownLocal(element.name);\n const type = this.escapeMarkdownLocal(element.type);\n const desc = element.description\n ? this.escapeMarkdownLocal(element.description).substring(0, 100)\n : \"\";\n\n lines.push(`| \\`${id}\\` | ${name} | ${type} | ${desc} |`);\n }\n\n lines.push(\"\");\n\n // Element details\n lines.push(`### Element Details`);\n lines.push(\"\");\n\n for (const element of elements) {\n const displayId = element.path || element.id;\n lines.push(`#### ${this.escapeMarkdownLocal(element.name)} (\\`${displayId}\\`)`);\n lines.push(\"\");\n\n if (element.description) {\n lines.push(`${element.description}`);\n lines.push(\"\");\n }\n\n lines.push(\"**Type:** \" + element.type);\n lines.push(\"\");\n\n // Source Reference\n const sourceRef = element.getSourceReference();\n if (sourceRef) {\n lines.push(\"### Source Code Location\");\n lines.push(\"\");\n lines.push(`**Provenance**: ${sourceRef.provenance}`);\n lines.push(\"\");\n\n lines.push(\"**Locations:**\");\n lines.push(\"\");\n (sourceRef.locations ?? []).forEach((loc, idx) => {\n lines.push(`${idx + 1}. \\`${this.escapeMarkdownLocal(loc.file)}\\``);\n if (loc.symbol) {\n lines.push(` - Symbol: \\`${this.escapeMarkdownLocal(loc.symbol)}\\``);\n }\n });\n lines.push(\"\");\n\n if (sourceRef.repository) {\n lines.push(\"**Repository Context:**\");\n lines.push(\"\");\n if (sourceRef.repository.url) {\n lines.push(`- Remote: ${sourceRef.repository.url}`);\n }\n if (sourceRef.repository.commit) {\n const shortCommit = sourceRef.repository.commit.substring(0, 7);\n if (sourceRef.repository.url) {\n lines.push(\n `- Commit: [\\`${shortCommit}\\`](${sourceRef.repository.url}/commit/${sourceRef.repository.commit})`\n );\n } else {\n lines.push(`- Commit: \\`${shortCommit}\\``);\n }\n }\n lines.push(\"\");\n }\n }\n\n // Properties (attributes)\n if (Object.keys(element.attributes).length > 0) {\n lines.push(\"**Properties:**\");\n lines.push(\"\");\n lines.push(`| Property | Value |`);\n lines.push(`| -------- | ----- |`);\n\n for (const [key, value] of Object.entries(element.attributes)) {\n const displayValue = this.valueToMarkdownLocal(value);\n lines.push(`| \\`${key}\\` | ${displayValue} |`);\n }\n lines.push(\"\");\n }\n\n // References\n if (element.references.length > 0) {\n lines.push(\"**Cross-Layer References:**\");\n lines.push(\"\");\n\n for (const ref of element.references) {\n const refDesc = ref.description ? ` - ${this.escapeMarkdownLocal(ref.description)}` : \"\";\n lines.push(`- **${ref.type}**: \\`${ref.target}\\`${refDesc}`);\n }\n lines.push(\"\");\n }\n\n // Relationships\n if (element.relationships.length > 0) {\n lines.push(\"**Relationships:**\");\n lines.push(\"\");\n\n for (const rel of element.relationships) {\n lines.push(`- **${rel.predicate}**: \\`${rel.target}\\``);\n }\n lines.push(\"\");\n }\n }\n\n // Layer summary\n lines.push(`### Layer Summary`);\n lines.push(\"\");\n lines.push(`- **Total Elements:** ${elements.length}`);\n\n const refCount = elements.reduce((sum, e) => sum + e.references.length, 0);\n const relCount = elements.reduce((sum, e) => sum + e.relationships.length, 0);\n\n lines.push(`- **Cross-Layer References:** ${refCount}`);\n lines.push(`- **Relationships:** ${relCount}`);\n\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n // Architecture Summary\n lines.push(\"## Architecture Summary\");\n lines.push(\"\");\n\n let totalElements = 0;\n let totalReferences = 0;\n let totalRelationships = 0;\n\n for (const layerName of layersToExport) {\n const layer = await model.getLayer(layerName);\n if (!layer) continue;\n\n for (const element of layer.listElements()) {\n totalElements++;\n totalReferences += element.references.length;\n totalRelationships += element.relationships.length;\n }\n }\n\n lines.push(`| Metric | Count |`);\n lines.push(`| ------ | ----- |`);\n lines.push(`| Total Elements | ${totalElements} |`);\n lines.push(`| Cross-Layer References | ${totalReferences} |`);\n lines.push(`| Relationships | ${totalRelationships} |`);\n lines.push(\"\");\n\n const result = lines.join(\"\\n\");\n\n if (isTelemetryEnabled && span) {\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.referenceCount\", totalReferences);\n (span as any).setAttribute(\"export.relationshipCount\", totalRelationships);\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 * Escape markdown special characters\n */\n private escapeMarkdownLocal(str: string): string {\n return escapeMarkdown(str);\n }\n\n /**\n * Format layer name for display\n */\n private formatLayerName(name: string): string {\n return formatLayerName(name);\n }\n\n /**\n * Get layer description\n */\n private getLayerDescription(layer: string): string {\n return getLayerDescription(layer);\n }\n\n /**\n * Convert value to markdown representation\n */\n private valueToMarkdownLocal(value: unknown): string {\n return valueToMarkdown(value);\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  const LAYER_DESCRIPTIONS = {
2
2
  motivation: "Goals, requirements, drivers, and strategic outcomes of the architecture.",
3
3
  business: "Business processes, functions, roles, and services.",
4
+ product: "Product features, capabilities, personas, and milestones.",
4
5
  security: "Authentication, authorization, security threats, and controls.",
5
6
  application: "Application components, services, and interactions.",
6
7
  technology: "Infrastructure, platforms, systems, and technology components.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/markdown-utils.ts"],
4
- "sourcesContent": ["/**\n * Shared markdown utilities for exporters\n */\n\n/**\n * Layer descriptions for documentation\n */\nexport const LAYER_DESCRIPTIONS = {\n motivation: \"Goals, requirements, drivers, and strategic outcomes of the architecture.\",\n business: \"Business processes, functions, roles, and services.\",\n security: \"Authentication, authorization, security threats, and controls.\",\n application: \"Application components, services, and interactions.\",\n technology: \"Infrastructure, platforms, systems, and technology components.\",\n api: \"REST APIs, operations, endpoints, and API integrations.\",\n \"data-model\": \"Data entities, relationships, and data structure definitions.\",\n \"data-store\": \"Databases, data stores, and persistence mechanisms.\",\n ux: \"User interface components, screens, and user experience elements.\",\n navigation: \"Application routing, navigation flows, and page structures.\",\n apm: \"Observability, monitoring, metrics, logging, and tracing.\",\n testing: \"Test strategies, test cases, test data, and test coverage.\",\n} as const;\n\n/**\n * Escape markdown special characters for safe display in markdown documents\n *\n * Escapes the following characters:\n * - `\\` \u2192 `\\\\` (backslash: prevents escape sequences)\n * - `|` \u2192 `\\|` (pipe: prevents table syntax interpretation)\n * - `*` \u2192 `\\*` (asterisk: prevents bold/italic formatting)\n * - `[` \u2192 `\\[` (bracket: prevents link syntax)\n * - `]` \u2192 `\\]` (bracket: prevents link syntax)\n * - `{` \u2192 `\\{` (brace: prevents code interpolation)\n * - `}` \u2192 `\\}` (brace: prevents code interpolation)\n * - `<` \u2192 `&lt;` (less-than: HTML entity for safety)\n * - `>` \u2192 `&gt;` (greater-than: HTML entity for safety)\n *\n * Note: Backticks (`) and underscores (_) are intentionally NOT escaped to allow\n * inline code and emphasis formatting in natural text output.\n */\nexport function escapeMarkdown(str: string): string {\n return str\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\|/g, \"\\\\|\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/\\[/g, \"\\\\[\")\n .replace(/\\]/g, \"\\\\]\")\n .replace(/\\{/g, \"\\\\{\")\n .replace(/\\}/g, \"\\\\}\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\n/**\n * Convert any value to a safely escaped string representation for markdown display\n *\n * Handles different types with type-specific formatting:\n * - **Strings**: Fully escaped via escapeMarkdown() to prevent markdown interpretation\n * - **Numbers**: Rendered as plain numeric strings (no formatting needed)\n * - **Booleans**: Rendered as \"true\" or \"false\" strings\n * - **Arrays**: Rendered as `[item1, item2, ...]` with recursive escaping of elements\n * - **Objects**: Rendered as inline code block with JSON stringification: `{...}`\n * - **Null/Undefined**: Rendered as string representation\n */\nexport function valueToMarkdown(value: unknown): string {\n if (typeof value === \"string\") return escapeMarkdown(value);\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) {\n return `[${value.map((v) => valueToMarkdown(v)).join(\", \")}]`;\n }\n if (value && typeof value === \"object\") {\n return `\\`${JSON.stringify(value)}\\``;\n }\n return String(value);\n}\n\n/**\n * Get layer description by layer name\n */\nexport function getLayerDescription(layer: string): string {\n return LAYER_DESCRIPTIONS[layer as keyof typeof LAYER_DESCRIPTIONS] || \"Architecture layer\";\n}\n"],
5
- "mappings": "AAOO,MAAM,qBAAqB;EAChC,YAAY;EACZ,UAAU;EACV,UAAU;EACV,aAAa;EACb,YAAY;EACZ,KAAK;EACL,cAAc;EACd,cAAc;EACd,IAAI;EACJ,YAAY;EACZ,KAAK;EACL,SAAS;;AAoBL,SAAU,eAAe,KAAW;AACxC,SAAO,IACJ,QAAQ,OAAO,MAAM,EACrB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM;AACzB;AAaM,SAAU,gBAAgB,OAAc;AAC5C,SAAI,OAAO,SAAU,WAAiB,eAAe,KAAK,IACtD,OAAO,SAAU,YACjB,OAAO,SAAU,YAAkB,OAAO,KAAK,IAC/C,MAAM,QAAQ,KAAK,IACd,IAAI,MAAM,IAAI,CAAC,MAAM,gBAAgB,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,MAExD,SAAS,OAAO,SAAU,WACrB,KAAK,KAAK,UAAU,KAAK,CAAC,OAE5B,OAAO,KAAK;AACrB;AAKM,SAAU,oBAAoB,OAAa;AAC/C,SAAO,mBAAmB,KAAwC,KAAK;AACzE;",
4
+ "sourcesContent": ["/**\n * Shared markdown utilities for exporters\n */\n\n/**\n * Layer descriptions for documentation\n */\nexport const LAYER_DESCRIPTIONS = {\n motivation: \"Goals, requirements, drivers, and strategic outcomes of the architecture.\",\n business: \"Business processes, functions, roles, and services.\",\n product: \"Product features, capabilities, personas, and milestones.\",\n security: \"Authentication, authorization, security threats, and controls.\",\n application: \"Application components, services, and interactions.\",\n technology: \"Infrastructure, platforms, systems, and technology components.\",\n api: \"REST APIs, operations, endpoints, and API integrations.\",\n \"data-model\": \"Data entities, relationships, and data structure definitions.\",\n \"data-store\": \"Databases, data stores, and persistence mechanisms.\",\n ux: \"User interface components, screens, and user experience elements.\",\n navigation: \"Application routing, navigation flows, and page structures.\",\n apm: \"Observability, monitoring, metrics, logging, and tracing.\",\n testing: \"Test strategies, test cases, test data, and test coverage.\",\n} as const;\n\n/**\n * Escape markdown special characters for safe display in markdown documents\n *\n * Escapes the following characters:\n * - `\\` \u2192 `\\\\` (backslash: prevents escape sequences)\n * - `|` \u2192 `\\|` (pipe: prevents table syntax interpretation)\n * - `*` \u2192 `\\*` (asterisk: prevents bold/italic formatting)\n * - `[` \u2192 `\\[` (bracket: prevents link syntax)\n * - `]` \u2192 `\\]` (bracket: prevents link syntax)\n * - `{` \u2192 `\\{` (brace: prevents code interpolation)\n * - `}` \u2192 `\\}` (brace: prevents code interpolation)\n * - `<` \u2192 `&lt;` (less-than: HTML entity for safety)\n * - `>` \u2192 `&gt;` (greater-than: HTML entity for safety)\n *\n * Note: Backticks (`) and underscores (_) are intentionally NOT escaped to allow\n * inline code and emphasis formatting in natural text output.\n */\nexport function escapeMarkdown(str: string): string {\n return str\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\|/g, \"\\\\|\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/\\[/g, \"\\\\[\")\n .replace(/\\]/g, \"\\\\]\")\n .replace(/\\{/g, \"\\\\{\")\n .replace(/\\}/g, \"\\\\}\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\n/**\n * Convert any value to a safely escaped string representation for markdown display\n *\n * Handles different types with type-specific formatting:\n * - **Strings**: Fully escaped via escapeMarkdown() to prevent markdown interpretation\n * - **Numbers**: Rendered as plain numeric strings (no formatting needed)\n * - **Booleans**: Rendered as \"true\" or \"false\" strings\n * - **Arrays**: Rendered as `[item1, item2, ...]` with recursive escaping of elements\n * - **Objects**: Rendered as inline code block with JSON stringification: `{...}`\n * - **Null/Undefined**: Rendered as string representation\n */\nexport function valueToMarkdown(value: unknown): string {\n if (typeof value === \"string\") return escapeMarkdown(value);\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) {\n return `[${value.map((v) => valueToMarkdown(v)).join(\", \")}]`;\n }\n if (value && typeof value === \"object\") {\n return `\\`${JSON.stringify(value)}\\``;\n }\n return String(value);\n}\n\n/**\n * Get layer description by layer name\n */\nexport function getLayerDescription(layer: string): string {\n return LAYER_DESCRIPTIONS[layer as keyof typeof LAYER_DESCRIPTIONS] || \"Architecture layer\";\n}\n"],
5
+ "mappings": "AAOO,MAAM,qBAAqB;EAChC,YAAY;EACZ,UAAU;EACV,SAAS;EACT,UAAU;EACV,aAAa;EACb,YAAY;EACZ,KAAK;EACL,cAAc;EACd,cAAc;EACd,IAAI;EACJ,YAAY;EACZ,KAAK;EACL,SAAS;;AAoBL,SAAU,eAAe,KAAW;AACxC,SAAO,IACJ,QAAQ,OAAO,MAAM,EACrB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM;AACzB;AAaM,SAAU,gBAAgB,OAAc;AAC5C,SAAI,OAAO,SAAU,WAAiB,eAAe,KAAK,IACtD,OAAO,SAAU,YACjB,OAAO,SAAU,YAAkB,OAAO,KAAK,IAC/C,MAAM,QAAQ,KAAK,IACd,IAAI,MAAM,IAAI,CAAC,MAAM,gBAAgB,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,MAExD,SAAS,OAAO,SAAU,WACrB,KAAK,KAAK,UAAU,KAAK,CAAC,OAE5B,OAAO,KAAK;AACrB;AAKM,SAAU,oBAAoB,OAAa;AAC/C,SAAO,mBAAmB,KAAwC,KAAK;AACzE;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/mermaid-markdown-exporter.ts"],
4
- "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { ALL_LAYERS } from \"./types.js\";\nimport { MarkdownGenerator, type MarkdownGeneratorOptions } from \"./markdown-generator.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * Mermaid Markdown Exporter - generates comprehensive markdown documentation\n * with Mermaid diagrams and formatted tables\n *\n * Supports all 12 layers and includes:\n * - Architecture overview diagrams with Mermaid\n * - Layer-specific visualizations\n * - Formatted markdown tables\n * - Cross-layer relationship diagrams\n * - Element properties and relationship details\n */\nexport class MermaidMarkdownExporter implements Exporter {\n name = \"Mermaid Markdown\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.mermaid-markdown\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n // Create markdown generator with appropriate options\n const generatorOptions: MarkdownGeneratorOptions = {\n includeMermaid: true,\n includeTables: true,\n maxTableRows: 50,\n diagramType: \"graph\",\n };\n\n const generator = new MarkdownGenerator(model, generatorOptions);\n const markdown = await generator.generate();\n\n if (isTelemetryEnabled && span) {\n const totalElements = model.graph.nodes.size;\n const totalRelationships = model.graph.getAllEdges().length;\n\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.relationshipCount\", totalRelationships);\n (span as any).setAttribute(\"export.size\", markdown.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return markdown;\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 { ALL_LAYERS } from \"./types.js\";\nimport { MarkdownGenerator, type MarkdownGeneratorOptions } from \"./markdown-generator.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * Mermaid Markdown Exporter - generates comprehensive markdown documentation\n * with Mermaid diagrams and formatted tables\n *\n * Supports all 13 layers and includes:\n * - Architecture overview diagrams with Mermaid\n * - Layer-specific visualizations\n * - Formatted markdown tables\n * - Cross-layer relationship diagrams\n * - Element properties and relationship details\n */\nexport class MermaidMarkdownExporter implements Exporter {\n name = \"Mermaid Markdown\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.mermaid-markdown\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n })\n : null;\n\n try {\n // Create markdown generator with appropriate options\n const generatorOptions: MarkdownGeneratorOptions = {\n includeMermaid: true,\n includeTables: true,\n maxTableRows: 50,\n diagramType: \"graph\",\n };\n\n const generator = new MarkdownGenerator(model, generatorOptions);\n const markdown = await generator.generate();\n\n if (isTelemetryEnabled && span) {\n const totalElements = model.graph.nodes.size;\n const totalRelationships = model.graph.getAllEdges().length;\n\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.relationshipCount\", totalRelationships);\n (span as any).setAttribute(\"export.size\", markdown.length);\n (span as any).setStatus({ code: 0 });\n }\n\n return markdown;\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": "AAEA,SAAS,kBAAkB;AAC3B,SAAS,yBAAwD;AACjE,SAAS,oBAAoB,WAAW,eAAe;AACvD,SAAS,uBAAuB;AAa1B,MAAO,wBAAuB;EAClC,OAAO;EACP,kBAAkB;EAElB,MAAM,OAAO,OAAc,UAAyB,CAAA,GAAE;AACpD,UAAM,OAAO,qBACT,UAAU,kCAAkC;MAC1C,qBAAqB,QAAQ,QAAQ,UAAU,KAAK,gBAAgB;KACrE,IACD;AAEJ,QAAI;AAEF,YAAM,mBAA6C;QACjD,gBAAgB;QAChB,eAAe;QACf,cAAc;QACd,aAAa;SAIT,WAAW,MADC,IAAI,kBAAkB,OAAO,gBAAgB,EAC9B,SAAQ;AAEzC,UAAI,sBAAsB,MAAM;AAC9B,cAAM,gBAAgB,MAAM,MAAM,MAAM,MAClC,qBAAqB,MAAM,MAAM,YAAW,EAAG;AAEpD,aAAa,aAAa,uBAAuB,aAAa,GAC9D,KAAa,aAAa,4BAA4B,kBAAkB,GACxE,KAAa,aAAa,eAAe,SAAS,MAAM,GACxD,KAAa,UAAU,EAAE,MAAM,EAAC,CAAE;MACrC;AAEA,aAAO;IACT,SAAS,OAAO;AACd,YAAI,sBAAsB,SACvB,KAAa,gBAAgB,KAAc,GAC3C,KAAa,UAAU;QACtB,MAAM;QACN,SAAS,gBAAgB,KAAK;OAC/B,IAEG;IACR;AACE,cAAQ,IAAI;IACd;EACF;;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/openapi-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 * OpenAPI server definition\n * Specifies base URL and optional description for API access\n */\ninterface OpenAPIServer {\n url: string;\n description?: string;\n}\n\n/**\n * OpenAPI tag definition\n * Logical grouping for operations\n */\ninterface OpenAPITag {\n name: string;\n description?: string;\n}\n\n/**\n * OpenAPI external documentation link\n * Reference to external documentation\n */\ninterface OpenAPIExternalDocs {\n url: string;\n description?: string;\n}\n\n/**\n * OpenAPI info object\n * Metadata about the API\n */\ninterface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/**\n * OpenAPI components section\n * Reusable schema definitions and other components\n */\ninterface OpenAPIComponents {\n schemas?: Record<string, unknown>;\n responses?: Record<string, unknown>;\n parameters?: Record<string, unknown>;\n requestBodies?: Record<string, unknown>;\n headers?: Record<string, unknown>;\n securitySchemes?: Record<string, unknown>;\n}\n\n/**\n * OpenAPI specification root\n * Complete OpenAPI 3.0 document structure\n */\ninterface OpenAPISpec {\n openapi: string;\n info: OpenAPIInfo;\n paths: Record<string, unknown>;\n components: OpenAPIComponents;\n servers?: OpenAPIServer[];\n tags?: OpenAPITag[];\n externalDocs?: OpenAPIExternalDocs;\n security?: unknown[];\n}\n\n/**\n * Endpoint mapping entry\n * Groups endpoints by path and method for processing\n */\ninterface EndpointData {\n name: string;\n id: string;\n description?: string;\n getProperty: (key: string) => unknown;\n getSourceReference: () => unknown;\n}\n\ninterface EndpointMapping {\n method: string;\n element: EndpointData;\n}\n\n/**\n * OpenAPI 3.0 Exporter for layer 6 (API)\n */\nexport class OpenAPIExporter implements Exporter {\n name = \"OpenAPI\";\n supportedLayers = [\"api\"];\n\n async export(model: Model, _options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled ? startSpan(\"export.format.openapi\") : null;\n\n try {\n // Load API layer elements\n const apiLayer = await model.getLayer(\"api\");\n if (!apiLayer) {\n throw new Error(\"No API layer found in model\");\n }\n const elements = apiLayer.listElements();\n if (elements.length === 0) {\n throw new Error(\"No API layer found in model\");\n }\n\n // Note: _options is not used for OpenAPI exporter as it only supports the api layer\n // The parameter is kept for interface consistency with other exporters\n\n const spec: OpenAPISpec = {\n openapi: \"3.0.0\",\n info: {\n title: model.manifest.name,\n version: model.manifest.version || \"1.0.0\",\n },\n paths: {},\n components: {\n schemas: {},\n responses: {},\n parameters: {},\n requestBodies: {},\n headers: {},\n securitySchemes: {},\n },\n };\n\n if (model.manifest.description) {\n spec.info.description = model.manifest.description;\n }\n\n // Group operations by operationId-derived path\n const pathGroups = new Map<string, EndpointMapping[]>();\n\n // Iterate layer elements for operation/endpoint nodes\n for (const element of elements) {\n if (element.type === \"operation\" || element.type === \"endpoint\") {\n const operationId =\n (element.attributes?.operationId as string) || element.name;\n\n // Derive URL path from element attributes (for endpoints) or slug (for operations)\n let pathKey: string;\n const attrPath = element.attributes?.path as string;\n if (attrPath) {\n // Use path from attributes (e.g., \"/api/orders\")\n pathKey = attrPath;\n } else {\n // Derive URL path from element slug (last segment of path)\n // e.g., api.operation.get-user-profile \u2192 /get-user-profile\n const slugSegment = (element as { path?: string }).path?.split(\".\").pop();\n pathKey = slugSegment ? `/${slugSegment}` : `/${operationId}`;\n }\n\n // Infer HTTP method from operationId prefix keywords or use method attribute\n const method = ((element.attributes?.method as string) || (() => {\n const opIdLower = operationId.toLowerCase();\n if (/^(create|post|add|insert|register|submit|send)/.test(opIdLower)) {\n return \"post\";\n } else if (/^(update|put|edit|modify|replace|set|save)/.test(opIdLower)) {\n return \"put\";\n } else if (/^(patch|partial)/.test(opIdLower)) {\n return \"patch\";\n } else if (/^(delete|remove|destroy|purge|clear|unregister)/.test(opIdLower)) {\n return \"delete\";\n }\n return \"get\";\n })()).toLowerCase();\n\n if (!pathGroups.has(pathKey)) {\n pathGroups.set(pathKey, []);\n }\n\n const endpointData: EndpointData = {\n name: (element.attributes?.summary as string) || element.name,\n id: element.id,\n description: element.description,\n getProperty: (key: string) => element.attributes?.[key],\n getSourceReference: () => element.getSourceReference(),\n };\n\n pathGroups.get(pathKey)!.push({ method, element: endpointData });\n }\n }\n\n // Build paths section\n for (const [path, endpoints] of pathGroups) {\n const pathItem: Record<string, unknown> = {};\n\n for (const { method, element } of endpoints) {\n const operation: Record<string, unknown> = {\n summary: element.name,\n operationId: (element.getProperty(\"operationId\") as string) || element.id,\n responses: element.getProperty(\"responses\") || {\n \"200\": {\n description: \"Successful response\",\n },\n },\n };\n\n if (element.description) {\n operation.description = element.description;\n }\n\n const tags = element.getProperty(\"tags\");\n if (tags) {\n operation.tags = Array.isArray(tags) ? tags : [tags];\n }\n\n const parameters = element.getProperty(\"parameters\");\n if (parameters) {\n operation.parameters = parameters;\n }\n\n const requestBody = element.getProperty(\"requestBody\");\n if (requestBody) {\n operation.requestBody = requestBody;\n }\n\n const security = element.getProperty(\"security\");\n if (security) {\n operation.security = security;\n }\n\n const deprecated = element.getProperty(\"deprecated\");\n if (deprecated) {\n operation.deprecated = deprecated;\n }\n\n // Add source reference if present\n const sourceRef = element.getSourceReference();\n if (sourceRef) {\n operation[\"x-source-reference\"] = sourceRef;\n }\n\n pathItem[method] = operation;\n }\n\n // Add source reference to PathItem if any endpoint has one\n // (Use first endpoint's source reference for the path)\n if (endpoints.length > 0) {\n const firstSourceRef = endpoints[0].element.getSourceReference();\n if (firstSourceRef) {\n pathItem[\"x-source-reference\"] = firstSourceRef;\n }\n }\n\n (spec.paths as Record<string, unknown>)[path] = pathItem;\n }\n\n // Collect all schema definitions from elements\n for (const element of elements) {\n if (element.type === \"schema\") {\n const schemaName = (element.attributes?.schemaName as string) || element.id;\n const schema = element.attributes?.schema;\n if (schema) {\n spec.components.schemas![schemaName] = schema;\n }\n }\n }\n\n // Collect security schemes if defined\n for (const element of elements) {\n if (element.type === \"security-scheme\") {\n const schemeName = element.name;\n const schemeType = element.attributes?.type as string;\n if (schemeType) {\n const scheme: Record<string, unknown> = {\n type: schemeType,\n };\n\n const schemeProp = element.attributes?.scheme;\n if (schemeProp) {\n scheme.scheme = schemeProp;\n }\n\n const bearerFormat = element.attributes?.bearerFormat;\n if (bearerFormat) {\n scheme.bearerFormat = bearerFormat;\n }\n\n const flows = element.attributes?.flows;\n if (flows) {\n scheme.flows = flows;\n }\n\n const openIdConnectUrl = element.attributes?.openIdConnectUrl;\n if (openIdConnectUrl) {\n scheme.openIdConnectUrl = openIdConnectUrl;\n }\n\n if (element.description) {\n scheme.description = element.description;\n }\n\n spec.components.securitySchemes![schemeName] = scheme;\n }\n }\n }\n\n // Add servers if defined in manifest (would be stored separately)\n // For now, servers would need to be added via layer properties\n // This could be extended in the future\n\n // Remove empty component sections\n const components = spec.components;\n if (components.schemas && Object.keys(components.schemas).length === 0) {\n delete components.schemas;\n }\n if (components.responses && Object.keys(components.responses).length === 0) {\n delete components.responses;\n }\n if (components.parameters && Object.keys(components.parameters).length === 0) {\n delete components.parameters;\n }\n if (components.requestBodies && Object.keys(components.requestBodies).length === 0) {\n delete components.requestBodies;\n }\n if (components.headers && Object.keys(components.headers).length === 0) {\n delete components.headers;\n }\n if (components.securitySchemes && Object.keys(components.securitySchemes).length === 0) {\n delete components.securitySchemes;\n }\n if (Object.keys(components).length === 0) {\n delete (spec as Partial<OpenAPISpec>).components;\n }\n\n const result = JSON.stringify(spec, null, 2) + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n const endpointCount = Array.from(pathGroups.values()).reduce(\n (sum, endpoints) => sum + endpoints.length,\n 0\n );\n (span as any).setAttribute(\"export.endpointCount\", endpointCount);\n (span as any).setAttribute(\"export.pathCount\", pathGroups.size);\n (span as any).setAttribute(\n \"export.schemaCount\",\n Object.keys(spec.components?.schemas || {}).length\n );\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 * OpenAPI server definition\n * Specifies base URL and optional description for API access\n */\ninterface OpenAPIServer {\n url: string;\n description?: string;\n}\n\n/**\n * OpenAPI tag definition\n * Logical grouping for operations\n */\ninterface OpenAPITag {\n name: string;\n description?: string;\n}\n\n/**\n * OpenAPI external documentation link\n * Reference to external documentation\n */\ninterface OpenAPIExternalDocs {\n url: string;\n description?: string;\n}\n\n/**\n * OpenAPI info object\n * Metadata about the API\n */\ninterface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/**\n * OpenAPI components section\n * Reusable schema definitions and other components\n */\ninterface OpenAPIComponents {\n schemas?: Record<string, unknown>;\n responses?: Record<string, unknown>;\n parameters?: Record<string, unknown>;\n requestBodies?: Record<string, unknown>;\n headers?: Record<string, unknown>;\n securitySchemes?: Record<string, unknown>;\n}\n\n/**\n * OpenAPI specification root\n * Complete OpenAPI 3.0 document structure\n */\ninterface OpenAPISpec {\n openapi: string;\n info: OpenAPIInfo;\n paths: Record<string, unknown>;\n components: OpenAPIComponents;\n servers?: OpenAPIServer[];\n tags?: OpenAPITag[];\n externalDocs?: OpenAPIExternalDocs;\n security?: unknown[];\n}\n\n/**\n * Endpoint mapping entry\n * Groups endpoints by path and method for processing\n */\ninterface EndpointData {\n name: string;\n id: string;\n description?: string;\n getProperty: (key: string) => unknown;\n getSourceReference: () => unknown;\n}\n\ninterface EndpointMapping {\n method: string;\n element: EndpointData;\n}\n\n/**\n * OpenAPI 3.0 Exporter for layer 7 (API)\n */\nexport class OpenAPIExporter implements Exporter {\n name = \"OpenAPI\";\n supportedLayers = [\"api\"];\n\n async export(model: Model, _options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled ? startSpan(\"export.format.openapi\") : null;\n\n try {\n // Load API layer elements\n const apiLayer = await model.getLayer(\"api\");\n if (!apiLayer) {\n throw new Error(\"No API layer found in model\");\n }\n const elements = apiLayer.listElements();\n if (elements.length === 0) {\n throw new Error(\"No API layer found in model\");\n }\n\n // Note: _options is not used for OpenAPI exporter as it only supports the api layer\n // The parameter is kept for interface consistency with other exporters\n\n const spec: OpenAPISpec = {\n openapi: \"3.0.0\",\n info: {\n title: model.manifest.name,\n version: model.manifest.version || \"1.0.0\",\n },\n paths: {},\n components: {\n schemas: {},\n responses: {},\n parameters: {},\n requestBodies: {},\n headers: {},\n securitySchemes: {},\n },\n };\n\n if (model.manifest.description) {\n spec.info.description = model.manifest.description;\n }\n\n // Group operations by operationId-derived path\n const pathGroups = new Map<string, EndpointMapping[]>();\n\n // Iterate layer elements for operation/endpoint nodes\n for (const element of elements) {\n if (element.type === \"operation\" || element.type === \"endpoint\") {\n const operationId =\n (element.attributes?.operationId as string) || element.name;\n\n // Derive URL path from element attributes (for endpoints) or slug (for operations)\n let pathKey: string;\n const attrPath = element.attributes?.path as string;\n if (attrPath) {\n // Use path from attributes (e.g., \"/api/orders\")\n pathKey = attrPath;\n } else {\n // Derive URL path from element slug (last segment of path)\n // e.g., api.operation.get-user-profile \u2192 /get-user-profile\n const slugSegment = (element as { path?: string }).path?.split(\".\").pop();\n pathKey = slugSegment ? `/${slugSegment}` : `/${operationId}`;\n }\n\n // Infer HTTP method from operationId prefix keywords or use method attribute\n const method = ((element.attributes?.method as string) || (() => {\n const opIdLower = operationId.toLowerCase();\n if (/^(create|post|add|insert|register|submit|send)/.test(opIdLower)) {\n return \"post\";\n } else if (/^(update|put|edit|modify|replace|set|save)/.test(opIdLower)) {\n return \"put\";\n } else if (/^(patch|partial)/.test(opIdLower)) {\n return \"patch\";\n } else if (/^(delete|remove|destroy|purge|clear|unregister)/.test(opIdLower)) {\n return \"delete\";\n }\n return \"get\";\n })()).toLowerCase();\n\n if (!pathGroups.has(pathKey)) {\n pathGroups.set(pathKey, []);\n }\n\n const endpointData: EndpointData = {\n name: (element.attributes?.summary as string) || element.name,\n id: element.id,\n description: element.description,\n getProperty: (key: string) => element.attributes?.[key],\n getSourceReference: () => element.getSourceReference(),\n };\n\n pathGroups.get(pathKey)!.push({ method, element: endpointData });\n }\n }\n\n // Build paths section\n for (const [path, endpoints] of pathGroups) {\n const pathItem: Record<string, unknown> = {};\n\n for (const { method, element } of endpoints) {\n const operation: Record<string, unknown> = {\n summary: element.name,\n operationId: (element.getProperty(\"operationId\") as string) || element.id,\n responses: element.getProperty(\"responses\") || {\n \"200\": {\n description: \"Successful response\",\n },\n },\n };\n\n if (element.description) {\n operation.description = element.description;\n }\n\n const tags = element.getProperty(\"tags\");\n if (tags) {\n operation.tags = Array.isArray(tags) ? tags : [tags];\n }\n\n const parameters = element.getProperty(\"parameters\");\n if (parameters) {\n operation.parameters = parameters;\n }\n\n const requestBody = element.getProperty(\"requestBody\");\n if (requestBody) {\n operation.requestBody = requestBody;\n }\n\n const security = element.getProperty(\"security\");\n if (security) {\n operation.security = security;\n }\n\n const deprecated = element.getProperty(\"deprecated\");\n if (deprecated) {\n operation.deprecated = deprecated;\n }\n\n // Add source reference if present\n const sourceRef = element.getSourceReference();\n if (sourceRef) {\n operation[\"x-source-reference\"] = sourceRef;\n }\n\n pathItem[method] = operation;\n }\n\n // Add source reference to PathItem if any endpoint has one\n // (Use first endpoint's source reference for the path)\n if (endpoints.length > 0) {\n const firstSourceRef = endpoints[0].element.getSourceReference();\n if (firstSourceRef) {\n pathItem[\"x-source-reference\"] = firstSourceRef;\n }\n }\n\n (spec.paths as Record<string, unknown>)[path] = pathItem;\n }\n\n // Collect all schema definitions from elements\n for (const element of elements) {\n if (element.type === \"schema\") {\n const schemaName = (element.attributes?.schemaName as string) || element.id;\n const schema = element.attributes?.schema;\n if (schema) {\n spec.components.schemas![schemaName] = schema;\n }\n }\n }\n\n // Collect security schemes if defined\n for (const element of elements) {\n if (element.type === \"security-scheme\") {\n const schemeName = element.name;\n const schemeType = element.attributes?.type as string;\n if (schemeType) {\n const scheme: Record<string, unknown> = {\n type: schemeType,\n };\n\n const schemeProp = element.attributes?.scheme;\n if (schemeProp) {\n scheme.scheme = schemeProp;\n }\n\n const bearerFormat = element.attributes?.bearerFormat;\n if (bearerFormat) {\n scheme.bearerFormat = bearerFormat;\n }\n\n const flows = element.attributes?.flows;\n if (flows) {\n scheme.flows = flows;\n }\n\n const openIdConnectUrl = element.attributes?.openIdConnectUrl;\n if (openIdConnectUrl) {\n scheme.openIdConnectUrl = openIdConnectUrl;\n }\n\n if (element.description) {\n scheme.description = element.description;\n }\n\n spec.components.securitySchemes![schemeName] = scheme;\n }\n }\n }\n\n // Add servers if defined in manifest (would be stored separately)\n // For now, servers would need to be added via layer properties\n // This could be extended in the future\n\n // Remove empty component sections\n const components = spec.components;\n if (components.schemas && Object.keys(components.schemas).length === 0) {\n delete components.schemas;\n }\n if (components.responses && Object.keys(components.responses).length === 0) {\n delete components.responses;\n }\n if (components.parameters && Object.keys(components.parameters).length === 0) {\n delete components.parameters;\n }\n if (components.requestBodies && Object.keys(components.requestBodies).length === 0) {\n delete components.requestBodies;\n }\n if (components.headers && Object.keys(components.headers).length === 0) {\n delete components.headers;\n }\n if (components.securitySchemes && Object.keys(components.securitySchemes).length === 0) {\n delete components.securitySchemes;\n }\n if (Object.keys(components).length === 0) {\n delete (spec as Partial<OpenAPISpec>).components;\n }\n\n const result = JSON.stringify(spec, null, 2) + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n const endpointCount = Array.from(pathGroups.values()).reduce(\n (sum, endpoints) => sum + endpoints.length,\n 0\n );\n (span as any).setAttribute(\"export.endpointCount\", endpointCount);\n (span as any).setAttribute(\"export.pathCount\", pathGroups.size);\n (span as any).setAttribute(\n \"export.schemaCount\",\n Object.keys(spec.components?.schemas || {}).length\n );\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
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/export/plantuml-exporter.ts"],
4
- "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { ALL_LAYERS, LAYER_COLORS } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * PlantUML element entry for diagram generation\n * Contains element metadata for rendering from graph nodes\n */\ninterface PlantUMLElementEntry {\n id: string;\n name: string;\n type: string;\n description?: string;\n}\n\n/**\n * PlantUML Exporter - generates PlantUML syntax for diagram visualization\n * Supports all 12 layers\n *\n * Exports the model as PlantUML component diagram syntax with layers,\n * elements, relationships, and optional source references.\n */\nexport class PlantUMLExporter implements Exporter {\n name = \"PlantUML\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.plantuml\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n \"export.includeSources\": options.includeSources === true,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n lines.push(\"@startuml\");\n lines.push(`title \"${this.escapeQuotes(model.manifest.name)}\"`);\n lines.push(\"\");\n\n if (model.manifest.description) {\n lines.push(`note top : \"${this.escapeQuotes(model.manifest.description)}\"`);\n lines.push(\"\");\n }\n\n const layersToExport = options.layers || this.supportedLayers;\n const elementsByLayer = new Map<string, PlantUMLElementEntry[]>();\n const nodesByLayer = new Map<string, any[]>();\n // Map from raw graph node ID \u2192 sanitized PlantUML ID for edge rendering\n const rawToPlantUMLId = new Map<string, string>();\n\n // Collect nodes by layer from graph model\n for (const layerName of layersToExport) {\n const nodes = model.graph.getNodesByLayer(layerName);\n if (nodes.length === 0) continue;\n\n const layerElements: PlantUMLElementEntry[] = [];\n nodesByLayer.set(layerName, nodes);\n\n for (const node of nodes) {\n const pumlId = this.toPlantUMLId(node.id);\n rawToPlantUMLId.set(node.id, pumlId);\n layerElements.push({\n id: pumlId,\n name: node.name,\n type: node.type,\n description: node.description,\n });\n }\n elementsByLayer.set(layerName, layerElements);\n }\n\n // Create packages for each layer\n for (const layerName of layersToExport) {\n const elements = elementsByLayer.get(layerName);\n if (!elements || elements.length === 0) continue;\n\n const color = LAYER_COLORS[layerName] || \"FFFFFF\";\n lines.push(`package \"${layerName}\" #${color} {`);\n\n const nodes = nodesByLayer.get(layerName) || [];\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n lines.push(` component \"${this.escapeQuotes(element.name)}\" as ${element.id}`);\n\n // Add source reference as note if includeSources option is enabled\n if (options.includeSources) {\n const node = nodes[i];\n const sourceRef = this.extractSourceReference(node);\n if (sourceRef) {\n lines.push(` note right of ${element.id}`);\n lines.push(` ${this.escapeQuotes(sourceRef)}`);\n lines.push(` end note`);\n }\n }\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n }\n\n // Add relationships from graph edges\n const edges = model.graph.getAllEdges();\n\n for (const edge of edges) {\n // Only include edges between exported nodes (check raw IDs)\n const srcPuml = rawToPlantUMLId.get(edge.source);\n const dstPuml = rawToPlantUMLId.get(edge.destination);\n if (srcPuml && dstPuml) {\n const arrow = this.getArrowType(edge.predicate);\n lines.push(\n `${srcPuml} ${arrow} ${dstPuml} : ${this.escapeQuotes(edge.predicate)}`\n );\n }\n }\n\n lines.push(\"\");\n lines.push(\"@enduml\");\n\n const result = lines.join(\"\\n\") + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n // Count total elements and relationships\n let totalElements = 0;\n let totalEdges = 0;\n\n for (const elements of elementsByLayer.values()) {\n totalElements += elements.length;\n }\n\n for (const edge of edges) {\n if (rawToPlantUMLId.has(edge.source) && rawToPlantUMLId.has(edge.destination)) {\n totalEdges++;\n }\n }\n\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.relationshipCount\", totalEdges);\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 * Get PlantUML arrow type based on predicate\n */\n private getArrowType(predicate: string): string {\n const arrowMap: Record<string, string> = {\n composes: \"*--\",\n aggregates: \"o--\",\n supports: \"-->\",\n realizes: \"..|>\",\n \"flows-to\": \"-->\",\n \"refers-to\": \"..>\",\n };\n return arrowMap[predicate] || \"-->\";\n }\n\n /**\n * Extract and format source reference from node properties\n */\n private extractSourceReference(node: any): string | null {\n if (!node) return null;\n\n // Try node.source_reference first (primary field on GraphNode)\n let sourceRef = node.source_reference;\n\n // Fall back to x-source-reference in properties\n if (!sourceRef) {\n sourceRef = node.properties?.[\"x-source-reference\"];\n }\n\n // Fall back to nested source.reference format\n if (!sourceRef && node.properties?.source?.reference) {\n sourceRef = node.properties.source.reference;\n }\n\n if (!sourceRef) return null;\n\n // Format source reference for display\n const parts: string[] = [];\n\n if (Array.isArray(sourceRef.locations) && sourceRef.locations.length > 0) {\n const location = sourceRef.locations[0];\n if (location.file) {\n parts.push(`Source: ${location.file}`);\n }\n if (location.symbol) {\n parts.push(`Symbol: ${location.symbol}`);\n }\n }\n\n if (sourceRef.repository?.url) {\n parts.push(`Repo: ${sourceRef.repository.url}`);\n if (sourceRef.repository.commit) {\n parts.push(`Commit: ${sourceRef.repository.commit.substring(0, 8)}`);\n }\n }\n\n return parts.length > 0 ? parts.join(\"\\\\n\") : null;\n }\n\n /**\n * Escape quotes for PlantUML syntax\n */\n private escapeQuotes(str: string): string {\n // Escape double quotes within strings for PlantUML\n return str.replace(/\"/g, '\\\\\"');\n }\n\n /**\n * Convert a raw element ID (path or UUID) to a valid PlantUML identifier.\n * PlantUML identifiers cannot contain dots or hyphens, so replace with underscores.\n */\n private toPlantUMLId(id: string): string {\n return id.replace(/[.\\-]/g, \"_\");\n }\n}\n"],
4
+ "sourcesContent": ["import type { Model } from \"../core/model.js\";\nimport type { Exporter, ExportOptions } from \"./types.js\";\nimport { ALL_LAYERS, LAYER_COLORS } from \"./types.js\";\nimport { isTelemetryEnabled, startSpan, endSpan } from \"../telemetry/index.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * PlantUML element entry for diagram generation\n * Contains element metadata for rendering from graph nodes\n */\ninterface PlantUMLElementEntry {\n id: string;\n name: string;\n type: string;\n description?: string;\n}\n\n/**\n * PlantUML Exporter - generates PlantUML syntax for diagram visualization\n * Supports all 13 layers\n *\n * Exports the model as PlantUML component diagram syntax with layers,\n * elements, relationships, and optional source references.\n */\nexport class PlantUMLExporter implements Exporter {\n name = \"PlantUML\";\n supportedLayers = ALL_LAYERS;\n\n async export(model: Model, options: ExportOptions = {}): Promise<string> {\n const span = isTelemetryEnabled\n ? startSpan(\"export.format.plantuml\", {\n \"export.layerCount\": options.layers?.length || this.supportedLayers.length,\n \"export.includeSources\": options.includeSources === true,\n })\n : null;\n\n try {\n const lines: string[] = [];\n\n lines.push(\"@startuml\");\n lines.push(`title \"${this.escapeQuotes(model.manifest.name)}\"`);\n lines.push(\"\");\n\n if (model.manifest.description) {\n lines.push(`note top : \"${this.escapeQuotes(model.manifest.description)}\"`);\n lines.push(\"\");\n }\n\n const layersToExport = options.layers || this.supportedLayers;\n const elementsByLayer = new Map<string, PlantUMLElementEntry[]>();\n const nodesByLayer = new Map<string, any[]>();\n // Map from raw graph node ID \u2192 sanitized PlantUML ID for edge rendering\n const rawToPlantUMLId = new Map<string, string>();\n\n // Collect nodes by layer from graph model\n for (const layerName of layersToExport) {\n const nodes = model.graph.getNodesByLayer(layerName);\n if (nodes.length === 0) continue;\n\n const layerElements: PlantUMLElementEntry[] = [];\n nodesByLayer.set(layerName, nodes);\n\n for (const node of nodes) {\n const pumlId = this.toPlantUMLId(node.id);\n rawToPlantUMLId.set(node.id, pumlId);\n layerElements.push({\n id: pumlId,\n name: node.name,\n type: node.type,\n description: node.description,\n });\n }\n elementsByLayer.set(layerName, layerElements);\n }\n\n // Create packages for each layer\n for (const layerName of layersToExport) {\n const elements = elementsByLayer.get(layerName);\n if (!elements || elements.length === 0) continue;\n\n const color = LAYER_COLORS[layerName] || \"FFFFFF\";\n lines.push(`package \"${layerName}\" #${color} {`);\n\n const nodes = nodesByLayer.get(layerName) || [];\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n lines.push(` component \"${this.escapeQuotes(element.name)}\" as ${element.id}`);\n\n // Add source reference as note if includeSources option is enabled\n if (options.includeSources) {\n const node = nodes[i];\n const sourceRef = this.extractSourceReference(node);\n if (sourceRef) {\n lines.push(` note right of ${element.id}`);\n lines.push(` ${this.escapeQuotes(sourceRef)}`);\n lines.push(` end note`);\n }\n }\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n }\n\n // Add relationships from graph edges\n const edges = model.graph.getAllEdges();\n\n for (const edge of edges) {\n // Only include edges between exported nodes (check raw IDs)\n const srcPuml = rawToPlantUMLId.get(edge.source);\n const dstPuml = rawToPlantUMLId.get(edge.destination);\n if (srcPuml && dstPuml) {\n const arrow = this.getArrowType(edge.predicate);\n lines.push(\n `${srcPuml} ${arrow} ${dstPuml} : ${this.escapeQuotes(edge.predicate)}`\n );\n }\n }\n\n lines.push(\"\");\n lines.push(\"@enduml\");\n\n const result = lines.join(\"\\n\") + \"\\n\";\n\n if (isTelemetryEnabled && span) {\n // Count total elements and relationships\n let totalElements = 0;\n let totalEdges = 0;\n\n for (const elements of elementsByLayer.values()) {\n totalElements += elements.length;\n }\n\n for (const edge of edges) {\n if (rawToPlantUMLId.has(edge.source) && rawToPlantUMLId.has(edge.destination)) {\n totalEdges++;\n }\n }\n\n (span as any).setAttribute(\"export.elementCount\", totalElements);\n (span as any).setAttribute(\"export.relationshipCount\", totalEdges);\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 * Get PlantUML arrow type based on predicate\n */\n private getArrowType(predicate: string): string {\n const arrowMap: Record<string, string> = {\n composes: \"*--\",\n aggregates: \"o--\",\n supports: \"-->\",\n realizes: \"..|>\",\n \"flows-to\": \"-->\",\n \"refers-to\": \"..>\",\n };\n return arrowMap[predicate] || \"-->\";\n }\n\n /**\n * Extract and format source reference from node properties\n */\n private extractSourceReference(node: any): string | null {\n if (!node) return null;\n\n // Try node.source_reference first (primary field on GraphNode)\n let sourceRef = node.source_reference;\n\n // Fall back to x-source-reference in properties\n if (!sourceRef) {\n sourceRef = node.properties?.[\"x-source-reference\"];\n }\n\n // Fall back to nested source.reference format\n if (!sourceRef && node.properties?.source?.reference) {\n sourceRef = node.properties.source.reference;\n }\n\n if (!sourceRef) return null;\n\n // Format source reference for display\n const parts: string[] = [];\n\n if (Array.isArray(sourceRef.locations) && sourceRef.locations.length > 0) {\n const location = sourceRef.locations[0];\n if (location.file) {\n parts.push(`Source: ${location.file}`);\n }\n if (location.symbol) {\n parts.push(`Symbol: ${location.symbol}`);\n }\n }\n\n if (sourceRef.repository?.url) {\n parts.push(`Repo: ${sourceRef.repository.url}`);\n if (sourceRef.repository.commit) {\n parts.push(`Commit: ${sourceRef.repository.commit.substring(0, 8)}`);\n }\n }\n\n return parts.length > 0 ? parts.join(\"\\\\n\") : null;\n }\n\n /**\n * Escape quotes for PlantUML syntax\n */\n private escapeQuotes(str: string): string {\n // Escape double quotes within strings for PlantUML\n return str.replace(/\"/g, '\\\\\"');\n }\n\n /**\n * Convert a raw element ID (path or UUID) to a valid PlantUML identifier.\n * PlantUML identifiers cannot contain dots or hyphens, so replace with underscores.\n */\n private toPlantUMLId(id: string): string {\n return id.replace(/[.\\-]/g, \"_\");\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -59,7 +59,7 @@ function formatRelationshipsSection(lines, report) {
59
59
  }
60
60
  function formatDataModelSection(lines, report) {
61
61
  const dataModel = report.dataModel;
62
- if (lines.push(ansis.bold("Data Model (Layer 7):")), lines.push(` ${ansis.gray("Total Entities:")} ${dataModel.entityCount}`), lines.push(` ${ansis.gray("Total Attributes:")} ${dataModel.attributeCount}`), lines.push(` ${ansis.gray("Entity Relationships:")} ${dataModel.relationshipCount}`), lines.push(` ${ansis.gray("Referenced Entities:")} ${dataModel.referencedEntities}`), lines.push(""), dataModel.orphanedEntities.length > 0) {
62
+ if (lines.push(ansis.bold("Data Model (Layer 8):")), lines.push(` ${ansis.gray("Total Entities:")} ${dataModel.entityCount}`), lines.push(` ${ansis.gray("Total Attributes:")} ${dataModel.attributeCount}`), lines.push(` ${ansis.gray("Entity Relationships:")} ${dataModel.relationshipCount}`), lines.push(` ${ansis.gray("Referenced Entities:")} ${dataModel.referencedEntities}`), lines.push(""), dataModel.orphanedEntities.length > 0) {
63
63
  lines.push(ansis.bold(ansis.yellow("\u26A0\uFE0F Orphaned Entities:")));
64
64
  for (const entity of dataModel.orphanedEntities.slice(0, 5))
65
65
  lines.push(` - ${entity}`);
@@ -94,7 +94,7 @@ function formatRelationshipsMarkdown(lines, report) {
94
94
  }
95
95
  function formatDataModelMarkdown(lines, report) {
96
96
  const dataModel = report.dataModel;
97
- if (lines.push("## Data Model (Layer 7)"), lines.push(""), lines.push("### Overview"), lines.push("| Metric | Value |"), lines.push("|--------|-------|"), lines.push(`| Total Entities | ${dataModel.entityCount} |`), lines.push(`| Total Attributes | ${dataModel.attributeCount} |`), lines.push(`| Entity Relationships | ${dataModel.relationshipCount} |`), lines.push(`| Referenced Entities | ${dataModel.referencedEntities} |`), lines.push(""), dataModel.orphanedEntities.length > 0) {
97
+ if (lines.push("## Data Model (Layer 8)"), lines.push(""), lines.push("### Overview"), lines.push("| Metric | Value |"), lines.push("|--------|-------|"), lines.push(`| Total Entities | ${dataModel.entityCount} |`), lines.push(`| Total Attributes | ${dataModel.attributeCount} |`), lines.push(`| Entity Relationships | ${dataModel.relationshipCount} |`), lines.push(`| Referenced Entities | ${dataModel.referencedEntities} |`), lines.push(""), dataModel.orphanedEntities.length > 0) {
98
98
  lines.push("### Orphaned Entities");
99
99
  for (const entity of dataModel.orphanedEntities)
100
100
  lines.push(`- ${entity}`);