@documentation-robotics/cli 0.1.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/README.md +74 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/analyzers/verify-engine.js +22 -19
  34. package/dist/analyzers/verify-engine.js.map +2 -2
  35. package/dist/audit/relationships/ai/prompts.js +10 -10
  36. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  37. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  38. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  39. package/dist/cli.js +112 -28
  40. package/dist/cli.js.map +3 -3
  41. package/dist/coding-agents/agents/claude-code.js +3 -1
  42. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  43. package/dist/coding-agents/claude-code-client.js +2 -1
  44. package/dist/coding-agents/claude-code-client.js.map +2 -2
  45. package/dist/coding-agents/copilot-client.js +17 -12
  46. package/dist/coding-agents/copilot-client.js.map +2 -2
  47. package/dist/coding-agents/default-permissions.js +152 -0
  48. package/dist/coding-agents/default-permissions.js.map +7 -0
  49. package/dist/coding-agents/drbot-mcp.js +167 -0
  50. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  51. package/dist/commands/add.js +12 -7
  52. package/dist/commands/add.js.map +2 -2
  53. package/dist/commands/analyzer.js +134 -23
  54. package/dist/commands/analyzer.js.map +2 -2
  55. package/dist/commands/audit.js +6 -6
  56. package/dist/commands/audit.js.map +2 -2
  57. package/dist/commands/changeset-verify.js +55 -0
  58. package/dist/commands/changeset-verify.js.map +7 -0
  59. package/dist/commands/changeset.js +261 -147
  60. package/dist/commands/changeset.js.map +2 -2
  61. package/dist/commands/delete.js +52 -19
  62. package/dist/commands/delete.js.map +3 -3
  63. package/dist/commands/element.js +3 -2
  64. package/dist/commands/element.js.map +2 -2
  65. package/dist/commands/farm.js +773 -0
  66. package/dist/commands/farm.js.map +7 -0
  67. package/dist/commands/init.js +11 -1
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +122 -0
  70. package/dist/commands/mcp.js.map +7 -0
  71. package/dist/commands/relationship.js +251 -47
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +1 -1
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +56 -24
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -11
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +41 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +2 -2
  82. package/dist/commands/version.js.map +2 -2
  83. package/dist/commands/visualize.js +4 -3
  84. package/dist/commands/visualize.js.map +2 -2
  85. package/dist/config.js +83 -0
  86. package/dist/config.js.map +7 -0
  87. package/dist/core/annotation-store.js +138 -0
  88. package/dist/core/annotation-store.js.map +7 -0
  89. package/dist/core/catalog-migration.js +33 -0
  90. package/dist/core/catalog-migration.js.map +7 -0
  91. package/dist/core/changeset-analysis.js +421 -0
  92. package/dist/core/changeset-analysis.js.map +7 -0
  93. package/dist/core/changeset-base-index.js +153 -0
  94. package/dist/core/changeset-base-index.js.map +7 -0
  95. package/dist/core/changeset-brief-delta.js +102 -0
  96. package/dist/core/changeset-brief-delta.js.map +7 -0
  97. package/dist/core/changeset-graph.js +281 -0
  98. package/dist/core/changeset-graph.js.map +7 -0
  99. package/dist/core/changeset-only-guard.js +32 -0
  100. package/dist/core/changeset-only-guard.js.map +7 -0
  101. package/dist/core/changeset-summary.js +68 -0
  102. package/dist/core/changeset-summary.js.map +7 -0
  103. package/dist/core/changeset-validator.js +66 -20
  104. package/dist/core/changeset-validator.js.map +2 -2
  105. package/dist/core/changeset-verify.js +518 -0
  106. package/dist/core/changeset-verify.js.map +7 -0
  107. package/dist/core/changeset.js +56 -9
  108. package/dist/core/changeset.js.map +2 -2
  109. package/dist/core/element-type-migration.js +145 -0
  110. package/dist/core/element-type-migration.js.map +7 -0
  111. package/dist/core/farm-manifest.js +140 -0
  112. package/dist/core/farm-manifest.js.map +7 -0
  113. package/dist/core/farm-sync-engine.js +346 -0
  114. package/dist/core/farm-sync-engine.js.map +7 -0
  115. package/dist/core/farm-sync-state.js +167 -0
  116. package/dist/core/farm-sync-state.js.map +7 -0
  117. package/dist/core/manifest.js +6 -3
  118. package/dist/core/manifest.js.map +2 -2
  119. package/dist/core/migration-registry.js +84 -1
  120. package/dist/core/migration-registry.js.map +2 -2
  121. package/dist/core/model-upgrade.js +102 -0
  122. package/dist/core/model-upgrade.js.map +7 -0
  123. package/dist/core/model.js +257 -44
  124. package/dist/core/model.js.map +3 -3
  125. package/dist/core/mutation-handler.js +71 -6
  126. package/dist/core/mutation-handler.js.map +2 -2
  127. package/dist/core/relationship-migration-table.js +6639 -0
  128. package/dist/core/relationship-migration-table.js.map +7 -0
  129. package/dist/core/relationship-migration.js +272 -0
  130. package/dist/core/relationship-migration.js.map +7 -0
  131. package/dist/core/relationship-prune.js +49 -0
  132. package/dist/core/relationship-prune.js.map +7 -0
  133. package/dist/core/relationship-staging.js +68 -0
  134. package/dist/core/relationship-staging.js.map +7 -0
  135. package/dist/core/relationships.js +26 -4
  136. package/dist/core/relationships.js.map +2 -2
  137. package/dist/core/report-data-model.js +7 -13
  138. package/dist/core/report-data-model.js.map +2 -2
  139. package/dist/core/staged-changeset-storage.js +64 -17
  140. package/dist/core/staged-changeset-storage.js.map +2 -2
  141. package/dist/core/staged-source-reference.js +28 -0
  142. package/dist/core/staged-source-reference.js.map +7 -0
  143. package/dist/core/staging-area.js +83 -18
  144. package/dist/core/staging-area.js.map +2 -2
  145. package/dist/core/virtual-projection.js +14 -5
  146. package/dist/core/virtual-projection.js.map +2 -2
  147. package/dist/export/archimate-exporter.js +1 -0
  148. package/dist/export/archimate-exporter.js.map +2 -2
  149. package/dist/generated/compiled-validators.js +2 -2
  150. package/dist/generated/compiled-validators.js.map +2 -2
  151. package/dist/generated/layer-registry.js +16 -16
  152. package/dist/generated/layer-registry.js.map +2 -2
  153. package/dist/generated/node-types.js +186 -168
  154. package/dist/generated/node-types.js.map +2 -2
  155. package/dist/generated/relationship-index.js +2209 -7727
  156. package/dist/generated/relationship-index.js.map +2 -2
  157. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  158. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  159. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  160. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  161. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  162. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  163. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  164. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  165. package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
  166. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  167. package/dist/integrations/claude_code/docs/README.md +52 -1
  168. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  169. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  170. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  171. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  172. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  173. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  174. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  175. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  176. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  177. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  178. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  179. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  180. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  181. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  182. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  183. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  184. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  185. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  186. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  187. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  188. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  189. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  190. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  191. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  192. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  193. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  194. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  195. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  196. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  197. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  198. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  199. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  200. package/dist/integrations/hash-utils.js.map +2 -2
  201. package/dist/mcp/api-key-manager.js +111 -0
  202. package/dist/mcp/api-key-manager.js.map +7 -0
  203. package/dist/mcp/changeset-only.js +34 -0
  204. package/dist/mcp/changeset-only.js.map +7 -0
  205. package/dist/mcp/http-transport.js +176 -0
  206. package/dist/mcp/http-transport.js.map +7 -0
  207. package/dist/mcp/resource-registry.js +97 -0
  208. package/dist/mcp/resource-registry.js.map +7 -0
  209. package/dist/mcp/tool-registry.js +60 -0
  210. package/dist/mcp/tool-registry.js.map +7 -0
  211. package/dist/mcp/tools/annotation-create.js +39 -0
  212. package/dist/mcp/tools/annotation-create.js.map +7 -0
  213. package/dist/mcp/tools/annotation-delete.js +29 -0
  214. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  215. package/dist/mcp/tools/annotation-get.js +29 -0
  216. package/dist/mcp/tools/annotation-get.js.map +7 -0
  217. package/dist/mcp/tools/annotation-list.js +24 -0
  218. package/dist/mcp/tools/annotation-list.js.map +7 -0
  219. package/dist/mcp/tools/annotation-reply.js +34 -0
  220. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  221. package/dist/mcp/tools/annotation-shared.js +10 -0
  222. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  223. package/dist/mcp/tools/annotation-update.js +39 -0
  224. package/dist/mcp/tools/annotation-update.js.map +7 -0
  225. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  226. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  227. package/dist/mcp/tools/changeset-editing.js +184 -0
  228. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  229. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  230. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  231. package/dist/mcp/tools/changeset-list.js +33 -0
  232. package/dist/mcp/tools/changeset-list.js.map +7 -0
  233. package/dist/mcp/tools/changeset-shared.js +31 -0
  234. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  235. package/dist/mcp/tools/changeset-show.js +55 -0
  236. package/dist/mcp/tools/changeset-show.js.map +7 -0
  237. package/dist/mcp/tools/changeset-verify.js +43 -0
  238. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  239. package/dist/mcp/tools/chat-launch.js +50 -0
  240. package/dist/mcp/tools/chat-launch.js.map +7 -0
  241. package/dist/mcp/tools/chat-status.js +26 -0
  242. package/dist/mcp/tools/chat-status.js.map +7 -0
  243. package/dist/mcp/tools/index.js +65 -0
  244. package/dist/mcp/tools/index.js.map +7 -0
  245. package/dist/mcp/tools/model-add.js +107 -0
  246. package/dist/mcp/tools/model-add.js.map +7 -0
  247. package/dist/mcp/tools/model-delete.js +99 -0
  248. package/dist/mcp/tools/model-delete.js.map +7 -0
  249. package/dist/mcp/tools/model-export.js +72 -0
  250. package/dist/mcp/tools/model-export.js.map +7 -0
  251. package/dist/mcp/tools/model-info.js +51 -0
  252. package/dist/mcp/tools/model-info.js.map +7 -0
  253. package/dist/mcp/tools/model-list.js +53 -0
  254. package/dist/mcp/tools/model-list.js.map +7 -0
  255. package/dist/mcp/tools/model-reload.js +34 -0
  256. package/dist/mcp/tools/model-reload.js.map +7 -0
  257. package/dist/mcp/tools/model-search.js +102 -0
  258. package/dist/mcp/tools/model-search.js.map +7 -0
  259. package/dist/mcp/tools/model-show.js +68 -0
  260. package/dist/mcp/tools/model-show.js.map +7 -0
  261. package/dist/mcp/tools/model-stats.js +22 -0
  262. package/dist/mcp/tools/model-stats.js.map +7 -0
  263. package/dist/mcp/tools/model-trace.js +118 -0
  264. package/dist/mcp/tools/model-trace.js.map +7 -0
  265. package/dist/mcp/tools/model-update.js +121 -0
  266. package/dist/mcp/tools/model-update.js.map +7 -0
  267. package/dist/mcp/tools/model-validate.js +78 -0
  268. package/dist/mcp/tools/model-validate.js.map +7 -0
  269. package/dist/mcp/tools/relationship-tools.js +98 -0
  270. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  271. package/dist/mcp/tools/shared.js +126 -0
  272. package/dist/mcp/tools/shared.js.map +7 -0
  273. package/dist/mcp/tools/spec-type.js +90 -0
  274. package/dist/mcp/tools/spec-type.js.map +7 -0
  275. package/dist/reports/model-layer-report-generator.js +5 -3
  276. package/dist/reports/model-layer-report-generator.js.map +2 -2
  277. package/dist/reports/model-readme-generator.js +116 -0
  278. package/dist/reports/model-readme-generator.js.map +7 -0
  279. package/dist/reports/model-report-data.js +37 -1
  280. package/dist/reports/model-report-data.js.map +2 -2
  281. package/dist/reports/model-report-orchestrator.js +29 -6
  282. package/dist/reports/model-report-orchestrator.js.map +2 -2
  283. package/dist/reports/model-report-utils.js +9 -0
  284. package/dist/reports/model-report-utils.js.map +7 -0
  285. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  286. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  287. package/dist/schemas/bundled/api.json +1029 -1658
  288. package/dist/schemas/bundled/apm.json +499 -734
  289. package/dist/schemas/bundled/application.json +745 -370
  290. package/dist/schemas/bundled/base.json +3 -220
  291. package/dist/schemas/bundled/business.json +452 -366
  292. package/dist/schemas/bundled/data-model.json +403 -383
  293. package/dist/schemas/bundled/data-store.json +334 -558
  294. package/dist/schemas/bundled/manifest.json +18 -18
  295. package/dist/schemas/bundled/motivation.json +349 -368
  296. package/dist/schemas/bundled/navigation.json +415 -731
  297. package/dist/schemas/bundled/product.json +325 -159
  298. package/dist/schemas/bundled/security.json +864 -1232
  299. package/dist/schemas/bundled/technology.json +574 -703
  300. package/dist/schemas/bundled/testing.json +718 -1643
  301. package/dist/schemas/bundled/ux.json +739 -1352
  302. package/dist/server/schemas.js +8 -0
  303. package/dist/server/schemas.js.map +2 -2
  304. package/dist/server/server.js +234 -31
  305. package/dist/server/server.js.map +2 -2
  306. package/dist/server-entry.js +2 -1
  307. package/dist/server-entry.js.map +2 -2
  308. package/dist/telemetry/config.js +2 -40
  309. package/dist/telemetry/config.js.map +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/types/index.js.map +2 -2
  312. package/dist/utils/ansi-suppressor.js +23 -0
  313. package/dist/utils/ansi-suppressor.js.map +7 -0
  314. package/dist/utils/attribute-patch.js +41 -0
  315. package/dist/utils/attribute-patch.js.map +7 -0
  316. package/dist/utils/color-utils.js +34 -0
  317. package/dist/utils/color-utils.js.map +7 -0
  318. package/dist/utils/element-reference-shape.js +15 -1
  319. package/dist/utils/element-reference-shape.js.map +3 -3
  320. package/dist/utils/error-hints.js +29 -0
  321. package/dist/utils/error-hints.js.map +7 -0
  322. package/dist/utils/errors.js +75 -10
  323. package/dist/utils/errors.js.map +2 -2
  324. package/dist/utils/globals.js +17 -1
  325. package/dist/utils/globals.js.map +2 -2
  326. package/dist/utils/project-paths.js +50 -3
  327. package/dist/utils/project-paths.js.map +2 -2
  328. package/dist/utils/reference-path-parser.js +49 -0
  329. package/dist/utils/reference-path-parser.js.map +7 -0
  330. package/dist/utils/source-reference.js +17 -10
  331. package/dist/utils/source-reference.js.map +2 -2
  332. package/dist/utils/spec-version.js +10 -2
  333. package/dist/utils/spec-version.js.map +2 -2
  334. package/dist/validators/composed-reference-validator.js +240 -0
  335. package/dist/validators/composed-reference-validator.js.map +7 -0
  336. package/dist/validators/reference-validator.js +57 -13
  337. package/dist/validators/reference-validator.js.map +3 -3
  338. package/dist/validators/relationship-schema-validator.js +33 -5
  339. package/dist/validators/relationship-schema-validator.js.map +3 -3
  340. package/dist/validators/schema-validator.js +27 -9
  341. package/dist/validators/schema-validator.js.map +2 -2
  342. package/dist/validators/types.js.map +1 -1
  343. package/dist/viewer/README.md +6 -6
  344. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  345. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  346. package/dist/viewer/index.html +2 -2
  347. package/dist/viewer/manifest.json +10 -10
  348. package/dist/viewer/version.json +1 -1
  349. package/package.json +7 -3
  350. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  351. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/validators/relationship-schema-validator.ts"],
4
- "sourcesContent": ["/**\n * Relationship Schema Validation with Cardinality Enforcement\n *\n * Validates relationships against their spec relationship schemas, including:\n * - Schema compliance (source/destination node types)\n * - Cardinality constraints (one-to-one, one-to-many, many-to-one, many-to-many)\n * - Predicate validity\n * - Relationship attributes against schema\n */\n\nimport { ValidationResult } from \"./types.js\";\nimport type { Model } from \"../core/model.js\";\nimport type { Relationship } from \"../core/relationships.js\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport { readFile } from \"../utils/file-io.js\";\nimport { existsSync } from \"fs\";\nimport { startActiveSpan } from \"../telemetry/index.js\";\n\ninterface ManifestDistFile {\n layers: Array<{ id: string }>;\n}\n\ninterface LayerDistFile {\n relationshipSchemas: Record<string, {\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: \"one-to-one\" | \"one-to-many\" | \"many-to-one\" | \"many-to-many\";\n strength: string;\n required?: boolean;\n attributes?: Record<string, unknown>;\n }>;\n}\n\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\nexport interface RelationshipSchema {\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: \"one-to-one\" | \"one-to-many\" | \"many-to-one\" | \"many-to-many\";\n strength: string;\n required?: boolean;\n attributes?: Record<string, unknown>;\n}\n\n/**\n * Validator for relationship schema compliance and cardinality constraints\n *\n * Validates that relationships:\n * 1. Conform to their spec relationship schemas\n * 2. Respect cardinality constraints (e.g., one-to-one means max 1 relationship of that type)\n * 3. Reference valid predicates\n * 4. Have valid source/destination element types\n */\nexport class RelationshipValidator {\n private schemasDir: string;\n private relationshipSchemas: Map<string, RelationshipSchema> = new Map();\n\n constructor() {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n this.schemasDir = path.join(__dirname, \"..\", \"schemas\", \"bundled\");\n }\n\n /**\n * Initialize and load relationship schemas\n */\n async initialize(): Promise<void> {\n await this.loadRelationshipSchemas();\n }\n\n /**\n * Load relationship schemas from compiled bundled spec (manifest.json + per-layer JSON files)\n */\n private async loadRelationshipSchemas(): Promise<void> {\n const manifestPath = path.join(this.schemasDir, \"manifest.json\");\n\n if (!existsSync(manifestPath)) {\n throw new Error(`Bundled manifest not found at ${manifestPath}`);\n }\n\n const manifestContent = await readFile(manifestPath);\n const manifest = JSON.parse(manifestContent) as ManifestDistFile;\n\n for (const { id: layerId } of manifest.layers) {\n const layerPath = path.join(this.schemasDir, `${layerId}.json`);\n if (!existsSync(layerPath)) {\n continue;\n }\n\n try {\n const layerContent = await readFile(layerPath);\n const layerData = JSON.parse(layerContent) as LayerDistFile;\n\n for (const [relId, relSchema] of Object.entries(layerData.relationshipSchemas || {})) {\n this.relationshipSchemas.set(relId, {\n id: relSchema.id || relId,\n source_spec_node_id: relSchema.source_spec_node_id || \"\",\n source_layer: relSchema.source_layer || \"\",\n destination_spec_node_id: relSchema.destination_spec_node_id || \"\",\n destination_layer: relSchema.destination_layer || \"\",\n predicate: relSchema.predicate || \"\",\n cardinality: relSchema.cardinality || \"many-to-many\",\n strength: relSchema.strength || \"medium\",\n required: relSchema.required || false,\n attributes: relSchema.attributes,\n });\n }\n } catch (error: any) {\n throw new Error(`Failed to load relationship schemas for layer '${layerId}': ${error.message}`);\n }\n }\n }\n\n /**\n * Validate relationships in a model with cardinality enforcement\n */\n async validateModel(model: Model): Promise<ValidationResult> {\n return startActiveSpan(\n \"relationship-schema.validate-model\",\n async (span) => {\n // Ensure schemas are loaded\n if (this.relationshipSchemas.size === 0) {\n try {\n await this.initialize();\n } catch (error: any) {\n const result = new ValidationResult();\n result.addError({\n layer: \"system\",\n message: `${error.message} Relationship validation is not available.`,\n });\n return result;\n }\n }\n\n // Verify schemas were loaded. This covers two cases:\n // 1. initialize() succeeded but the manifest has no layers with relationship schemas\n // 2. Edge case where manifest exists but is empty\n // In both cases, relationship validation cannot proceed.\n if (this.relationshipSchemas.size === 0) {\n const result = new ValidationResult();\n result.addError({\n layer: \"system\",\n message: `No relationship schemas loaded from ${path.join(this.schemasDir, \"manifest.json\")}. Relationship validation is not available.`,\n });\n return result;\n }\n\n const result = new ValidationResult();\n const allRelationships = model.relationships.getAll();\n\n if (allRelationships.length === 0) {\n return result;\n }\n\n // Group relationships for cardinality validation\n const relationshipsBySource = this.groupRelationshipsBySourceTarget(\n allRelationships,\n \"source\"\n );\n const relationshipsByTarget = this.groupRelationshipsBySourceTarget(\n allRelationships,\n \"target\"\n );\n\n // Validate each relationship\n for (const relationship of allRelationships) {\n const relErrors = await this.validateRelationship(\n relationship,\n model,\n relationshipsBySource,\n relationshipsByTarget\n );\n\n if (relErrors.length > 0) {\n for (const error of relErrors) {\n result.addError(error);\n }\n }\n }\n\n if (isTelemetryEnabled) {\n span.setAttribute(\"relationship.error_count\", result.errors.length);\n span.setAttribute(\"relationship.warning_count\", result.warnings.length);\n span.setAttribute(\"relationship.validated_count\", allRelationships.length);\n }\n\n return result;\n },\n { \"relationship.layer_count\": model.layers.size }\n );\n }\n\n /**\n * Validate a single relationship\n */\n private async validateRelationship(\n relationship: Relationship,\n model: Model,\n relationshipsBySource: Map<string, Relationship[]>,\n relationshipsByTarget: Map<string, Relationship[]>\n ): Promise<Array<{ layer: string; message: string; elementId?: string }>> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n\n // Find source and destination elements\n const sourceElement = this.findElementInModel(model, relationship.source);\n const targetElement = this.findElementInModel(model, relationship.target);\n\n if (!sourceElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Relationship source element '${relationship.source}' not found`,\n });\n return errors;\n }\n\n if (!targetElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Relationship target element '${relationship.target}' not found`,\n });\n return errors;\n }\n\n // Extract element types AND layers (CRITICAL FIX for cross-layer relationships)\n // The source and target elements may be in different layers\n const sourceLayer = sourceElement.layer_id || sourceElement.layer || relationship.layer;\n const targetLayer = targetElement.layer_id || targetElement.layer || relationship.layer;\n\n // Use spec_node_id if present, otherwise construct from layer and type\n // spec_node_id contains the canonical type (e.g., \"application.applicationservice\")\n // Guard against empty spec_node_id from older CLI versions or manually authored elements\n const sourceSpecId = sourceElement.spec_node_id\n ? sourceElement.spec_node_id\n : `${sourceLayer}.${sourceElement.type}`;\n const targetSpecId = targetElement.spec_node_id\n ? targetElement.spec_node_id\n : `${targetLayer}.${targetElement.type}`;\n\n // Find applicable relationship schema using actual source/target layers and spec node IDs\n // (not relationship.layer for both, which assumes intra-layer relationships)\n const schemaKey = this.findRelationshipSchemaKeyBySpecId(\n sourceSpecId,\n targetSpecId,\n relationship.predicate\n );\n\n if (!schemaKey) {\n // Extract type names from spec_node_id for error message (e.g., \"applicationservice\" from \"application.applicationservice\")\n const sourceType = sourceSpecId.includes(\".\") ? sourceSpecId.split(\".\")[1] : sourceElement.type;\n const targetType = targetSpecId.includes(\".\") ? targetSpecId.split(\".\")[1] : targetElement.type;\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `No schema found for relationship: ${sourceLayer}.${sourceType} --[${relationship.predicate}]--> ${targetLayer}.${targetType}`,\n });\n return errors;\n }\n\n const schema = this.relationshipSchemas.get(schemaKey);\n if (!schema) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Failed to load schema for relationship: ${schemaKey}`,\n });\n return errors;\n }\n\n // Validate cardinality constraints\n const cardinalityErrors = this.validateCardinality(\n relationship,\n schema,\n relationshipsBySource,\n relationshipsByTarget\n );\n\n errors.push(...cardinalityErrors);\n\n // Validate relationship attributes if schema specifies them\n if (relationship.properties && schema.attributes) {\n const attrErrors = this.validateRelationshipAttributes(\n relationship,\n schema\n );\n errors.push(...attrErrors);\n }\n\n return errors;\n }\n\n /**\n * Validate cardinality constraints for a relationship\n *\n * Cardinality constraint semantics:\n * - \"one-to-one\": Each source can have at most 1 such relationship, and each target can be involved in at most 1\n * - \"one-to-many\": Each target can receive at most 1 such relationship (target is the \"one\"); source is unconstrained\n * - \"many-to-one\": Each source can be involved in at most 1 such relationship (maps to one target); multiple sources may share the same target\n * - \"many-to-many\": No cardinality constraints\n */\n private validateCardinality(\n relationship: Relationship,\n schema: RelationshipSchema,\n relationshipsBySource: Map<string, Relationship[]>,\n relationshipsByTarget: Map<string, Relationship[]>\n ): Array<{ layer: string; message: string; elementId?: string }> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n const cardinality = schema.cardinality;\n\n // Count OTHER relationships for this predicate from source and to target\n // (excluding the current relationship to get accurate violation detection)\n const sourceRelCount =\n relationshipsBySource\n .get(relationship.source)\n ?.filter(\n (r) =>\n r.predicate === relationship.predicate &&\n r.target !== relationship.target // Exclude current relationship\n ).length || 0;\n\n const targetRelCount =\n relationshipsByTarget\n .get(relationship.target)\n ?.filter(\n (r) =>\n r.predicate === relationship.predicate &&\n r.source !== relationship.source // Exclude current relationship\n ).length || 0;\n\n // Validate based on cardinality type\n switch (cardinality) {\n case \"one-to-one\":\n // Source can have at most 1 such relationship\n if (sourceRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-one', but source '${relationship.source}' already has this relationship`,\n });\n }\n // Target can be involved in at most 1 such relationship\n if (targetRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-one', but target '${relationship.target}' is already involved in this relationship`,\n });\n }\n break;\n\n case \"one-to-many\":\n // Standard ER one-to-many: the \"one\" is at the target end.\n // Each target can receive at most 1 incoming relationship of this type.\n // The source is unconstrained \u2014 it may have many outgoing of this type\n // to different targets.\n if (targetRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-many', but target '${relationship.target}' already has this relationship`,\n });\n }\n break;\n\n case \"many-to-one\":\n // Standard ER many-to-one: the \"one\" is at the source end.\n // Each source can be involved in at most 1 relationship of this type\n // (it maps to exactly one target). Multiple sources CAN share the same\n // target \u2014 fan-in is allowed and expected for shared services.\n if (sourceRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'many-to-one', but source '${relationship.source}' already has this relationship`,\n });\n }\n break;\n\n case \"many-to-many\":\n // No cardinality constraints\n break;\n }\n\n return errors;\n }\n\n /**\n * Validate relationship attributes against schema\n */\n private validateRelationshipAttributes(\n relationship: Relationship,\n schema: RelationshipSchema\n ): Array<{ layer: string; message: string; elementId?: string }> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n\n if (!schema.attributes || !relationship.properties) {\n return errors;\n }\n\n // Simple validation: check that properties match schema keys\n for (const key of Object.keys(relationship.properties)) {\n if (!(key in schema.attributes)) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Relationship property '${key}' not defined in schema for predicate '${relationship.predicate}'`,\n });\n }\n }\n\n return errors;\n }\n\n /**\n * Find relationship schema ID by source/dest spec node IDs and predicate\n *\n * Matches relationship schemas by:\n * 1. Predicate (must match exactly)\n * 2. Spec node ID pair (source and destination must match exactly)\n *\n * Spec node IDs are in the format \"layer.type\" (e.g., \"application.applicationservice\")\n * and uniquely identify an element type across the model.\n *\n * Supports both intra-layer (motivation -> motivation) and cross-layer\n * (motivation -> business) relationships.\n */\n private findRelationshipSchemaKeyBySpecId(\n sourceSpecId: string,\n destSpecId: string,\n predicate: string\n ): string | null {\n // Search for matching schema\n for (const [schemaId, schema] of this.relationshipSchemas.entries()) {\n // First filter: predicate must match exactly\n if (schema.predicate !== predicate) {\n continue;\n }\n\n // Second filter: spec node ID pair must match exactly\n // This directly compares canonical forms like \"application.applicationservice\"\n if (\n schema.source_spec_node_id === sourceSpecId &&\n schema.destination_spec_node_id === destSpecId\n ) {\n return schemaId;\n }\n }\n\n return null;\n }\n\n /**\n * Group relationships by source or target element\n */\n private groupRelationshipsBySourceTarget(\n relationships: Relationship[],\n by: \"source\" | \"target\"\n ): Map<string, Relationship[]> {\n const grouped = new Map<string, Relationship[]>();\n\n for (const rel of relationships) {\n const key = by === \"source\" ? rel.source : rel.target;\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(rel);\n }\n\n return grouped;\n }\n\n /**\n * Find an element in the model by ID\n *\n * Performs element lookup using Layer.getElement(), which supports multiple lookup strategies:\n * - Direct lookup by graph key (path)\n * - UUID lookup\n * - Semantic ID lookup (for legacy format elements converted to UUID)\n * - Element ID lookup\n *\n * This comprehensive lookup ensures elements are found regardless of how they were stored.\n */\n private findElementInModel(model: Model, elementId: string) {\n for (const layer of model.layers.values()) {\n const element = layer.getElement(elementId);\n if (element) {\n return element;\n }\n }\n return null;\n }\n\n}\n"],
5
- "mappings": "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",
6
- "names": ["result"]
4
+ "sourcesContent": ["/**\n * Relationship Schema Validation with Cardinality Enforcement\n *\n * Validates relationships against their spec relationship schemas, including:\n * - Schema compliance (source/destination node types)\n * - Cardinality constraints (one-to-one, one-to-many, many-to-one, many-to-many)\n * - Predicate validity\n * - Relationship attributes against schema\n */\n\nimport { ValidationResult } from \"./types.js\";\nimport type { Model } from \"../core/model.js\";\nimport type { Relationship } from \"../core/relationships.js\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport { readFile } from \"../utils/file-io.js\";\nimport { existsSync } from \"fs\";\nimport { startActiveSpan } from \"../telemetry/index.js\";\n\ninterface ManifestDistFile {\n layers: Array<{ id: string }>;\n}\n\ninterface LayerDistFile {\n relationshipSchemas: Record<string, {\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: \"one-to-one\" | \"one-to-many\" | \"many-to-one\" | \"many-to-many\";\n strength: string;\n required?: boolean;\n attributes?: Record<string, unknown>;\n }>;\n}\n\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\nexport interface RelationshipSchema {\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: \"one-to-one\" | \"one-to-many\" | \"many-to-one\" | \"many-to-many\";\n strength: string;\n required?: boolean;\n attributes?: Record<string, unknown>;\n}\n\n/**\n * Validator for relationship schema compliance and cardinality constraints\n *\n * Validates that relationships:\n * 1. Conform to their spec relationship schemas\n * 2. Respect cardinality constraints (e.g., one-to-one means max 1 relationship of that type)\n * 3. Reference valid predicates\n * 4. Have valid source/destination element types\n */\nexport class RelationshipValidator {\n private schemasDir: string;\n private relationshipSchemas: Map<string, RelationshipSchema> = new Map();\n\n constructor() {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n this.schemasDir = path.join(__dirname, \"..\", \"schemas\", \"bundled\");\n }\n\n /**\n * Initialize and load relationship schemas\n */\n async initialize(): Promise<void> {\n await this.loadRelationshipSchemas();\n }\n\n /**\n * Load relationship schemas from compiled bundled spec (manifest.json + per-layer JSON files)\n */\n private async loadRelationshipSchemas(): Promise<void> {\n const manifestPath = path.join(this.schemasDir, \"manifest.json\");\n\n if (!existsSync(manifestPath)) {\n throw new Error(`Bundled manifest not found at ${manifestPath}`);\n }\n\n const manifestContent = await readFile(manifestPath);\n const manifest = JSON.parse(manifestContent) as ManifestDistFile;\n\n for (const { id: layerId } of manifest.layers) {\n const layerPath = path.join(this.schemasDir, `${layerId}.json`);\n if (!existsSync(layerPath)) {\n continue;\n }\n\n try {\n const layerContent = await readFile(layerPath);\n const layerData = JSON.parse(layerContent) as LayerDistFile;\n\n for (const [relId, relSchema] of Object.entries(layerData.relationshipSchemas || {})) {\n this.relationshipSchemas.set(relId, {\n id: relSchema.id || relId,\n source_spec_node_id: relSchema.source_spec_node_id || \"\",\n source_layer: relSchema.source_layer || \"\",\n destination_spec_node_id: relSchema.destination_spec_node_id || \"\",\n destination_layer: relSchema.destination_layer || \"\",\n predicate: relSchema.predicate || \"\",\n cardinality: relSchema.cardinality || \"many-to-many\",\n strength: relSchema.strength || \"medium\",\n required: relSchema.required || false,\n attributes: relSchema.attributes,\n });\n }\n } catch (error: any) {\n throw new Error(`Failed to load relationship schemas for layer '${layerId}': ${error.message}`);\n }\n }\n }\n\n /**\n * Validate relationships in a model with cardinality enforcement\n */\n async validateModel(model: Model): Promise<ValidationResult> {\n return startActiveSpan(\n \"relationship-schema.validate-model\",\n async (span) => {\n // Ensure schemas are loaded\n if (this.relationshipSchemas.size === 0) {\n try {\n await this.initialize();\n } catch (error: any) {\n const result = new ValidationResult();\n result.addError({\n layer: \"system\",\n message: `${error.message} Relationship validation is not available.`,\n });\n return result;\n }\n }\n\n // Verify schemas were loaded. This covers two cases:\n // 1. initialize() succeeded but the manifest has no layers with relationship schemas\n // 2. Edge case where manifest exists but is empty\n // In both cases, relationship validation cannot proceed.\n if (this.relationshipSchemas.size === 0) {\n const result = new ValidationResult();\n result.addError({\n layer: \"system\",\n message: `No relationship schemas loaded from ${path.join(this.schemasDir, \"manifest.json\")}. Relationship validation is not available.`,\n });\n return result;\n }\n\n const result = new ValidationResult();\n const allRelationships = model.relationships.getAll();\n\n if (allRelationships.length === 0) {\n return result;\n }\n\n // Group relationships for cardinality validation\n const relationshipsBySource = this.groupRelationshipsBySourceTarget(\n allRelationships,\n \"source\"\n );\n const relationshipsByTarget = this.groupRelationshipsBySourceTarget(\n allRelationships,\n \"target\"\n );\n\n // Validate each relationship\n for (const relationship of allRelationships) {\n const relErrors = await this.validateRelationship(\n relationship,\n model,\n relationshipsBySource,\n relationshipsByTarget\n );\n\n if (relErrors.length > 0) {\n for (const error of relErrors) {\n result.addError(error);\n }\n }\n }\n\n if (isTelemetryEnabled) {\n span.setAttribute(\"relationship.error_count\", result.errors.length);\n span.setAttribute(\"relationship.warning_count\", result.warnings.length);\n span.setAttribute(\"relationship.validated_count\", allRelationships.length);\n }\n\n return result;\n },\n { \"relationship.layer_count\": model.layers.size }\n );\n }\n\n /**\n * Validate a single relationship\n */\n private async validateRelationship(\n relationship: Relationship,\n model: Model,\n relationshipsBySource: Map<string, Relationship[]>,\n relationshipsByTarget: Map<string, Relationship[]>\n ): Promise<Array<{ layer: string; message: string; elementId?: string }>> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n\n // Variables to cache found elements and avoid redundant lookups\n let sourceElement: any = null;\n let targetElement: any = null;\n let bothInFilterAndFound = false;\n\n // Check if relationship crosses layer boundary when --layers filter is active\n if (model.loadedLayerFilter && model.loadedLayerFilter.length > 0) {\n const sourceLayer = this.extractLayerFromElementId(relationship.source);\n const targetLayer = this.extractLayerFromElementId(relationship.target);\n\n // If we can parse layers and both endpoints are within the filter, proceed with full validation.\n // If one or both endpoints are outside the filter, only skip if both endpoints are\n // in unloaded layers. If either endpoint is in a loaded layer, we must validate it.\n if (sourceLayer && targetLayer) {\n const sourceInFilter = model.loadedLayerFilter.includes(sourceLayer);\n const targetInFilter = model.loadedLayerFilter.includes(targetLayer);\n\n // Skip only if BOTH endpoints are in unloaded layers\n if (!sourceInFilter && !targetInFilter) {\n return errors;\n }\n\n // If at least one endpoint is in a loaded layer, check if both elements exist in loaded layers\n // If one is missing from a loaded layer, report error (genuine break)\n if (sourceInFilter) {\n sourceElement = this.findElementInModel(model, relationship.source);\n if (!sourceElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Relationship source element '${relationship.source}' not found`,\n });\n return errors;\n }\n }\n\n if (targetInFilter) {\n targetElement = this.findElementInModel(model, relationship.target);\n if (!targetElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Relationship target element '${relationship.target}' not found`,\n });\n return errors;\n }\n }\n\n // If one or both endpoints are in unloaded layers, skip full validation\n if (!sourceInFilter || !targetInFilter) {\n return errors;\n }\n\n // Both endpoints are in loaded layers and we've already found them\n bothInFilterAndFound = true;\n }\n // If we can't parse layers (unparseable element IDs), proceed to validation\n // where findElementInModel will fail with a proper error message\n }\n\n // Find source and destination elements only if we haven't already found them\n if (!bothInFilterAndFound) {\n sourceElement = this.findElementInModel(model, relationship.source);\n targetElement = this.findElementInModel(model, relationship.target);\n }\n\n if (!sourceElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Relationship source element '${relationship.source}' not found`,\n });\n return errors;\n }\n\n if (!targetElement) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Relationship target element '${relationship.target}' not found`,\n });\n return errors;\n }\n\n // Extract element types AND layers (CRITICAL FIX for cross-layer relationships)\n // The source and target elements may be in different layers\n const sourceLayer = sourceElement.layer_id || sourceElement.layer || relationship.layer;\n const targetLayer = targetElement.layer_id || targetElement.layer || relationship.layer;\n\n // Use spec_node_id if present, otherwise construct from layer and type\n // spec_node_id contains the canonical type (e.g., \"application.applicationservice\")\n // Guard against empty spec_node_id from older CLI versions or manually authored elements\n const sourceSpecId = sourceElement.spec_node_id\n ? sourceElement.spec_node_id\n : `${sourceLayer}.${sourceElement.type}`;\n const targetSpecId = targetElement.spec_node_id\n ? targetElement.spec_node_id\n : `${targetLayer}.${targetElement.type}`;\n\n // Find applicable relationship schema using actual source/target layers and spec node IDs\n // (not relationship.layer for both, which assumes intra-layer relationships)\n const schemaKey = this.findRelationshipSchemaKeyBySpecId(\n sourceSpecId,\n targetSpecId,\n relationship.predicate\n );\n\n if (!schemaKey) {\n // Extract type names from spec_node_id for error message (e.g., \"applicationservice\" from \"application.applicationservice\")\n const sourceType = sourceSpecId.includes(\".\") ? sourceSpecId.split(\".\")[1] : sourceElement.type;\n const targetType = targetSpecId.includes(\".\") ? targetSpecId.split(\".\")[1] : targetElement.type;\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message:\n `No schema found for relationship: ${relationship.source} --[${relationship.predicate}]--> ${relationship.target} ` +\n `(${sourceLayer}.${sourceType} --[${relationship.predicate}]--> ${targetLayer}.${targetType})`,\n });\n return errors;\n }\n\n const schema = this.relationshipSchemas.get(schemaKey);\n if (!schema) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Failed to load schema for relationship: ${relationship.source} --[${relationship.predicate}]--> ${relationship.target} (${schemaKey})`,\n });\n return errors;\n }\n\n // Validate cardinality constraints\n const cardinalityErrors = this.validateCardinality(\n relationship,\n schema,\n relationshipsBySource,\n relationshipsByTarget\n );\n\n errors.push(...cardinalityErrors);\n\n // Validate relationship attributes if schema specifies them\n if (relationship.properties && schema.attributes) {\n const attrErrors = this.validateRelationshipAttributes(\n relationship,\n schema\n );\n errors.push(...attrErrors);\n }\n\n return errors;\n }\n\n /**\n * Validate cardinality constraints for a relationship\n *\n * Cardinality constraint semantics:\n * - \"one-to-one\": Each source can have at most 1 such relationship, and each target can be involved in at most 1\n * - \"one-to-many\": Each target can receive at most 1 such relationship (target is the \"one\"); source is unconstrained\n * - \"many-to-one\": Each source can be involved in at most 1 such relationship (maps to one target); multiple sources may share the same target\n * - \"many-to-many\": No cardinality constraints\n */\n private validateCardinality(\n relationship: Relationship,\n schema: RelationshipSchema,\n relationshipsBySource: Map<string, Relationship[]>,\n relationshipsByTarget: Map<string, Relationship[]>\n ): Array<{ layer: string; message: string; elementId?: string }> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n const cardinality = schema.cardinality;\n\n // Count OTHER relationships for this predicate from source and to target\n // (excluding the current relationship to get accurate violation detection)\n const sourceRelCount =\n relationshipsBySource\n .get(relationship.source)\n ?.filter(\n (r) =>\n r.predicate === relationship.predicate &&\n r.target !== relationship.target // Exclude current relationship\n ).length || 0;\n\n const targetRelCount =\n relationshipsByTarget\n .get(relationship.target)\n ?.filter(\n (r) =>\n r.predicate === relationship.predicate &&\n r.source !== relationship.source // Exclude current relationship\n ).length || 0;\n\n // Validate based on cardinality type\n switch (cardinality) {\n case \"one-to-one\":\n // Source can have at most 1 such relationship\n if (sourceRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-one', but source '${relationship.source}' already has this relationship`,\n });\n }\n // Target can be involved in at most 1 such relationship\n if (targetRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-one', but target '${relationship.target}' is already involved in this relationship`,\n });\n }\n break;\n\n case \"one-to-many\":\n // Standard ER one-to-many: the \"one\" is at the target end.\n // Each target can receive at most 1 incoming relationship of this type.\n // The source is unconstrained \u2014 it may have many outgoing of this type\n // to different targets.\n if (targetRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.target,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'one-to-many', but target '${relationship.target}' already has this relationship`,\n });\n }\n break;\n\n case \"many-to-one\":\n // Standard ER many-to-one: the \"one\" is at the source end.\n // Each source can be involved in at most 1 relationship of this type\n // (it maps to exactly one target). Multiple sources CAN share the same\n // target \u2014 fan-in is allowed and expected for shared services.\n if (sourceRelCount > 0) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Cardinality violation: '${relationship.predicate}' has cardinality 'many-to-one', but source '${relationship.source}' already has this relationship`,\n });\n }\n break;\n\n case \"many-to-many\":\n // No cardinality constraints\n break;\n }\n\n return errors;\n }\n\n /**\n * Validate relationship attributes against schema\n */\n private validateRelationshipAttributes(\n relationship: Relationship,\n schema: RelationshipSchema\n ): Array<{ layer: string; message: string; elementId?: string }> {\n const errors: Array<{ layer: string; message: string; elementId?: string }> = [];\n\n if (!schema.attributes || !relationship.properties) {\n return errors;\n }\n\n // Simple validation: check that properties match schema keys\n for (const key of Object.keys(relationship.properties)) {\n if (!(key in schema.attributes)) {\n errors.push({\n layer: relationship.layer,\n elementId: relationship.source,\n message: `Relationship property '${key}' on ${relationship.source} --[${relationship.predicate}]--> ${relationship.target} not defined in schema for predicate '${relationship.predicate}'`,\n });\n }\n }\n\n return errors;\n }\n\n /**\n * Find relationship schema ID by source/dest spec node IDs and predicate\n *\n * Matches relationship schemas by:\n * 1. Predicate (must match exactly)\n * 2. Spec node ID pair (source and destination must match exactly)\n *\n * Spec node IDs are in the format \"layer.type\" (e.g., \"application.applicationservice\")\n * and uniquely identify an element type across the model.\n *\n * Supports both intra-layer (motivation -> motivation) and cross-layer\n * (motivation -> business) relationships.\n */\n private findRelationshipSchemaKeyBySpecId(\n sourceSpecId: string,\n destSpecId: string,\n predicate: string\n ): string | null {\n // Search for matching schema\n for (const [schemaId, schema] of this.relationshipSchemas.entries()) {\n // First filter: predicate must match exactly\n if (schema.predicate !== predicate) {\n continue;\n }\n\n // Second filter: spec node ID pair must match exactly\n // This directly compares canonical forms like \"application.applicationservice\"\n if (\n schema.source_spec_node_id === sourceSpecId &&\n schema.destination_spec_node_id === destSpecId\n ) {\n return schemaId;\n }\n }\n\n return null;\n }\n\n /**\n * Group relationships by source or target element\n */\n private groupRelationshipsBySourceTarget(\n relationships: Relationship[],\n by: \"source\" | \"target\"\n ): Map<string, Relationship[]> {\n const grouped = new Map<string, Relationship[]>();\n\n for (const rel of relationships) {\n const key = by === \"source\" ? rel.source : rel.target;\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(rel);\n }\n\n return grouped;\n }\n\n /**\n * Find an element in the model by ID\n *\n * Performs element lookup using Layer.getElement(), which supports multiple lookup strategies:\n * - Direct lookup by graph key (path)\n * - UUID lookup\n * - Semantic ID lookup (for legacy format elements converted to UUID)\n * - Element ID lookup\n *\n * This comprehensive lookup ensures elements are found regardless of how they were stored.\n */\n private findElementInModel(model: Model, elementId: string) {\n for (const layer of model.layers.values()) {\n const element = layer.getElement(elementId);\n if (element) {\n return element;\n }\n }\n return null;\n }\n\n private extractLayerFromElementId(elementId: string): string | null {\n const parts = elementId.split(\".\");\n if (parts.length < 2) {\n return null;\n }\n return parts[0];\n }\n\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["result", "sourceLayer", "targetLayer"]
7
7
  }
@@ -7,6 +7,13 @@ import { readFile } from "../utils/file-io.js";
7
7
  import { startActiveSpan } from "../telemetry/index.js";
8
8
  import { existsSync } from "fs";
9
9
  import { validateSpecNode, validateSpecNodeRelationship, validateSourceReference, validateAttributeSpec } from "../generated/compiled-validators.js";
10
+ import { isValidLayer } from "../generated/layer-registry.js";
11
+ import { getNodeTypesForLayer, isValidNodeType } from "../generated/node-types.js";
12
+ import { findSimilar } from "../utils/errors.js";
13
+ function suggestNodeTypes(layer, type) {
14
+ const names = getNodeTypesForLayer(layer).map((t) => t.type), lower = type.toLowerCase(), containing = lower.length >= 4 ? names.filter((name) => name !== lower && (name.includes(lower) || lower.includes(name))) : [];
15
+ return Array.from(/* @__PURE__ */ new Set([...findSimilar(type, names, 3), ...containing])).slice(0, 3);
16
+ }
10
17
  const isTelemetryEnabled = !1;
11
18
  class SchemaValidator {
12
19
  // Static shared AJV instance to avoid schema registry conflicts
@@ -19,9 +26,11 @@ class SchemaValidator {
19
26
  static registeredSchemaIds = /* @__PURE__ */ new Set();
20
27
  // Layer-level cache: layer ID → nodeSchemas map (avoids re-reading layer dist files)
21
28
  static layerCache = /* @__PURE__ */ new Map();
29
+ // Compiled node schemas, keyed by layer.type. Shared like the AJV instance they were compiled
30
+ // in: registeredSchemaIds is static, so a per-instance cache would leave every later instance
31
+ // with a registered id and no validator, and it would silently fall back to the base schema.
32
+ static compiledSchemas = /* @__PURE__ */ new Map();
22
33
  ajv;
23
- compiledSchemas = /* @__PURE__ */ new Map();
24
- // Key: layer.type
25
34
  bundledDir;
26
35
  constructor() {
27
36
  SchemaValidator.sharedAjv || (SchemaValidator.sharedAjv = new Ajv({
@@ -54,7 +63,7 @@ class SchemaValidator {
54
63
  async ensureBaseSchemaLoaded() {
55
64
  if (SchemaValidator.baseSchemaLoadedInSharedInstance)
56
65
  return;
57
- this.compiledSchemas.set("spec-node", validateSpecNode), this.compiledSchemas.set("spec-node-relationship", validateSpecNodeRelationship), this.compiledSchemas.set("source-references", validateSourceReference), this.compiledSchemas.set("attribute-spec", validateAttributeSpec);
66
+ SchemaValidator.compiledSchemas.set("spec-node", validateSpecNode), SchemaValidator.compiledSchemas.set("spec-node-relationship", validateSpecNodeRelationship), SchemaValidator.compiledSchemas.set("source-references", validateSourceReference), SchemaValidator.compiledSchemas.set("attribute-spec", validateAttributeSpec);
58
67
  const baseJsonPath = path.join(this.bundledDir, "base.json");
59
68
  try {
60
69
  const baseContent = await readFile(baseJsonPath), baseData = JSON.parse(baseContent);
@@ -95,8 +104,8 @@ class SchemaValidator {
95
104
  */
96
105
  async loadSpecNodeSchema(layer, type) {
97
106
  const cacheKey = `${layer}.${type}`;
98
- if (this.compiledSchemas.has(cacheKey))
99
- return this.compiledSchemas.get(cacheKey);
107
+ if (SchemaValidator.compiledSchemas.has(cacheKey))
108
+ return SchemaValidator.compiledSchemas.get(cacheKey);
100
109
  await this.ensureBaseSchemaLoaded();
101
110
  try {
102
111
  const nodeSchemas = await this.loadLayerNodeSchemas(layer);
@@ -104,10 +113,10 @@ class SchemaValidator {
104
113
  return null;
105
114
  const schema = nodeSchemas[type], schemaId = schema.$id || `urn:dr:spec:node:${layer}.${type}`;
106
115
  if (SchemaValidator.registeredSchemaIds.has(schemaId))
107
- return this.compiledSchemas.get(cacheKey) || null;
116
+ return SchemaValidator.compiledSchemas.get(cacheKey) || null;
108
117
  try {
109
118
  const validate = this.ajv.compile(schema);
110
- return this.compiledSchemas.set(cacheKey, validate), SchemaValidator.registeredSchemaIds.add(schemaId), validate;
119
+ return SchemaValidator.compiledSchemas.set(cacheKey, validate), SchemaValidator.registeredSchemaIds.add(schemaId), validate;
111
120
  } catch (registrationError) {
112
121
  if (SchemaValidator.getErrorMessage(registrationError).includes("already exists"))
113
122
  return SchemaValidator.registeredSchemaIds.add(schemaId), null;
@@ -162,6 +171,14 @@ class SchemaValidator {
162
171
  });
163
172
  return result;
164
173
  }
174
+ if (isValidLayer(layerName) && !isValidNodeType(layerName, element.type)) {
175
+ const elementId = element.path || element.id, similar = suggestNodeTypes(layerName, element.type);
176
+ return result.validated = !0, result.hasErrors = !0, result.errors.push({
177
+ message: `Element '${elementId}' has type '${element.type}', which the spec does not define for the ${layerName} layer` + (similar.length > 0 ? ` (did you mean ${similar.map((s) => `'${s}'`).join(" or ")}?)` : ""),
178
+ location: "/type",
179
+ fixSuggestion: similar.length > 0 ? `Change the element's type to '${similar[0]}', or remove the element` : `Change the element's type to one the ${layerName} layer defines, or remove the element`
180
+ }), result;
181
+ }
165
182
  const validate = await this.loadSpecNodeSchema(layerName, element.type);
166
183
  if (!validate)
167
184
  return result.validated = !0, result;
@@ -239,10 +256,11 @@ class SchemaValidator {
239
256
  * });
240
257
  */
241
258
  static reset() {
242
- SchemaValidator.sharedAjv = null, SchemaValidator.baseSchemaLoadedInSharedInstance = !1, SchemaValidator.registeredSchemaIds.clear(), SchemaValidator.layerCache.clear();
259
+ SchemaValidator.sharedAjv = null, SchemaValidator.baseSchemaLoadedInSharedInstance = !1, SchemaValidator.registeredSchemaIds.clear(), SchemaValidator.layerCache.clear(), SchemaValidator.compiledSchemas.clear();
243
260
  }
244
261
  }
245
262
  export {
246
- SchemaValidator
263
+ SchemaValidator,
264
+ suggestNodeTypes
247
265
  };
248
266
  //# sourceMappingURL=schema-validator.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/validators/schema-validator.ts"],
4
- "sourcesContent": ["/**\n * Schema validation using AJV\n *\n * Validates model elements against their corresponding spec node schemas.\n * Each element type has a dedicated schema in spec/schemas/nodes/{layer}/{type}.node.schema.json\n */\n\nimport Ajv, { ValidateFunction, ErrorObject } from \"ajv\";\nimport addFormats from \"ajv-formats\";\nimport { ValidationResult } from \"./types.js\";\nimport type { Layer } from \"../core/layer.js\";\nimport type { Element } from \"../core/element.js\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport { readFile } from \"../utils/file-io.js\";\nimport { startActiveSpan } from \"../telemetry/index.js\";\nimport { existsSync } from \"fs\";\nimport {\n validateSpecNode,\n validateSpecNodeRelationship,\n validateSourceReference,\n validateAttributeSpec,\n} from \"../generated/compiled-validators.js\";\n\n// Fallback for runtime environments where TELEMETRY_ENABLED is not defined by esbuild\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\n/**\n * Validator for JSON Schema compliance\n *\n * Validates model elements against spec node schemas. Each element is validated\n * against its type-specific schema (e.g., motivation.goal elements validate against\n * spec/schemas/nodes/motivation/goal.node.schema.json).\n */\nexport class SchemaValidator {\n // Static shared AJV instance to avoid schema registry conflicts\n // When multiple SchemaValidator instances are created, they all use the same\n // AJV instance which maintains a single schema registry. This prevents\n // \"schema with key or id already exists\" errors when loading the same schemas.\n private static sharedAjv: Ajv | null = null;\n private static baseSchemaLoadedInSharedInstance: boolean = false;\n // Track schema IDs already registered in the shared AJV instance to avoid re-registering\n private static registeredSchemaIds: Set<string> = new Set();\n // Layer-level cache: layer ID \u2192 nodeSchemas map (avoids re-reading layer dist files)\n private static layerCache: Map<string, Record<string, unknown>> = new Map();\n\n private ajv: Ajv;\n private compiledSchemas: Map<string, ValidateFunction> = new Map(); // Key: layer.type\n private bundledDir: string;\n\n constructor() {\n // Initialize shared AJV instance once\n if (!SchemaValidator.sharedAjv) {\n SchemaValidator.sharedAjv = new Ajv({\n allErrors: true,\n strict: false,\n validateFormats: true,\n verbose: true,\n });\n addFormats(SchemaValidator.sharedAjv);\n }\n this.ajv = SchemaValidator.sharedAjv;\n\n // Determine bundled directory (compiled spec dist files)\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n this.bundledDir = path.join(__dirname, \"..\", \"schemas\", \"bundled\");\n }\n\n /**\n * Extract error message from unknown error type with proper type checking\n */\n private static getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === \"object\" && error !== null && \"message\" in error) {\n const msg = (error as Record<string, unknown>).message;\n if (typeof msg === \"string\") {\n return msg;\n }\n }\n return \"Unknown error\";\n }\n\n /**\n * Ensure base schemas are pre-compiled validators are initialized\n * Loads base schemas from base.json and registers them with AJV for reference resolution.\n * This allows per-type schemas to resolve $ref to base schemas during compilation.\n */\n private async ensureBaseSchemaLoaded(): Promise<void> {\n // Check if already loaded in the shared AJV instance (applies to all instances)\n if (SchemaValidator.baseSchemaLoadedInSharedInstance) {\n return;\n }\n\n // Register pre-compiled validators with AJV\n this.compiledSchemas.set(\"spec-node\", validateSpecNode);\n this.compiledSchemas.set(\"spec-node-relationship\", validateSpecNodeRelationship);\n this.compiledSchemas.set(\"source-references\", validateSourceReference);\n this.compiledSchemas.set(\"attribute-spec\", validateAttributeSpec);\n\n // Load base.json and register all schemas with AJV for $ref resolution\n // Schemas use URN-style $ids like \"urn:dr:spec:base:spec-node\" after compilation\n const baseJsonPath = path.join(this.bundledDir, \"base.json\");\n try {\n const baseContent = await readFile(baseJsonPath);\n const baseData = JSON.parse(baseContent) as {\n specVersion: string;\n schemas: Record<string, unknown>;\n predicates: unknown;\n };\n\n for (const [key, schema] of Object.entries(baseData.schemas)) {\n const s = schema as Record<string, unknown>;\n const schemaId = (s.$id as string) || `urn:dr:spec:base:${key}`;\n\n // Skip if already registered\n if (SchemaValidator.registeredSchemaIds.has(schemaId)) {\n continue;\n }\n\n try {\n this.ajv.addSchema(s, schemaId);\n SchemaValidator.registeredSchemaIds.add(schemaId);\n } catch (error: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(error);\n const isDuplicateSchemaError = /schema with key or id [^ ]+ already exists/.test(errorMsg);\n if (isDuplicateSchemaError) {\n SchemaValidator.registeredSchemaIds.add(schemaId);\n } else {\n console.warn(`[SCHEMA-LOAD-001] Failed to register base schema '${key}': ${errorMsg}`);\n }\n }\n }\n } catch (error: unknown) {\n console.warn(\n `[SCHEMA-LOAD-001] Failed to load base.json from ${baseJsonPath}: ${SchemaValidator.getErrorMessage(error)}`\n );\n }\n\n // Mark base schemas as loaded in shared instance (applies to all instances)\n SchemaValidator.baseSchemaLoadedInSharedInstance = true;\n }\n\n /**\n * Load the nodeSchemas map for a layer, using a static layer-level cache.\n * Reads bundled/{layer}.json once per layer and caches the nodeSchemas object.\n */\n private async loadLayerNodeSchemas(layer: string): Promise<Record<string, unknown> | null> {\n // Return cached layer schemas if available\n if (SchemaValidator.layerCache.has(layer)) {\n return SchemaValidator.layerCache.get(layer)!;\n }\n\n const layerPath = path.join(this.bundledDir, `${layer}.json`);\n if (!existsSync(layerPath)) {\n return null;\n }\n\n try {\n const content = await readFile(layerPath);\n const layerData = JSON.parse(content) as { nodeSchemas: Record<string, unknown> };\n const nodeSchemas = layerData.nodeSchemas || {};\n SchemaValidator.layerCache.set(layer, nodeSchemas);\n return nodeSchemas;\n } catch (error: unknown) {\n console.warn(\n `[SCHEMA-LOAD-002] Failed to load layer file for '${layer}': ${SchemaValidator.getErrorMessage(error)}`\n );\n return null;\n }\n }\n\n /**\n * Load and compile a spec node schema for a specific element type\n */\n private async loadSpecNodeSchema(layer: string, type: string): Promise<ValidateFunction | null> {\n const cacheKey = `${layer}.${type}`;\n\n // Check compiled schema cache first\n if (this.compiledSchemas.has(cacheKey)) {\n return this.compiledSchemas.get(cacheKey)!;\n }\n\n // Ensure base schemas are loaded first (required for reference resolution)\n await this.ensureBaseSchemaLoaded();\n\n try {\n // Load node schemas from layer dist file (layer-level cache)\n const nodeSchemas = await this.loadLayerNodeSchemas(layer);\n if (!nodeSchemas || !nodeSchemas[type]) {\n // No schema for this type - acceptable for custom types\n return null;\n }\n\n const schema = nodeSchemas[type] as Record<string, unknown>;\n const schemaId = (schema.$id as string) || `urn:dr:spec:node:${layer}.${type}`;\n\n // Check if schema with this $id is already registered to avoid duplicate compilation\n if (SchemaValidator.registeredSchemaIds.has(schemaId)) {\n // Schema already registered \u2014 return from compiled cache or null (fallback to base)\n return this.compiledSchemas.get(cacheKey) || null;\n }\n\n try {\n const validate = this.ajv.compile(schema);\n this.compiledSchemas.set(cacheKey, validate);\n SchemaValidator.registeredSchemaIds.add(schemaId);\n return validate;\n } catch (registrationError: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(registrationError);\n if (errorMsg.includes(\"already exists\")) {\n SchemaValidator.registeredSchemaIds.add(schemaId);\n return null;\n }\n throw registrationError;\n }\n } catch (error: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(error);\n if (errorMsg.includes(\"can't resolve reference\")) {\n return null;\n }\n console.warn(`[SCHEMA-LOAD-002] Failed to load spec node schema for ${layer}.${type}: ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Validate a layer against its spec node schemas\n *\n * This validates each element in the layer against its corresponding spec node schema.\n */\n async validateLayer(layer: Layer): Promise<ValidationResult> {\n return startActiveSpan(\n \"schema.validate\",\n async (span) => {\n const result = new ValidationResult();\n let validatedCount = 0;\n let skippedCount = 0;\n\n // Validate each element individually\n for (const element of layer.elements.values()) {\n const elementResult = await this.validateElement(element, layer.name);\n\n if (elementResult.hasErrors) {\n // Add element-specific errors to layer result\n for (const error of elementResult.errors) {\n result.addError({\n layer: layer.name,\n elementId: element.path || element.id,\n message: error.message,\n location: error.location,\n fixSuggestion: error.fixSuggestion,\n });\n }\n }\n\n if (elementResult.validated) {\n validatedCount++;\n } else {\n skippedCount++;\n }\n }\n\n if (isTelemetryEnabled) {\n span.setAttribute(\"schema.valid\", result.isValid());\n span.setAttribute(\"schema.error_count\", result.errors.length);\n span.setAttribute(\"schema.validated_count\", validatedCount);\n span.setAttribute(\"schema.skipped_count\", skippedCount);\n }\n\n return result;\n },\n { \"schema.layer\": layer.name }\n );\n }\n\n /**\n * Validate a single element against its spec node schema\n */\n private async validateElement(element: Element, layerName: string): Promise<{\n validated: boolean;\n hasErrors: boolean;\n errors: Array<{ message: string; location: string; fixSuggestion?: string }>;\n }> {\n const result = {\n validated: false,\n hasErrors: false,\n errors: [] as Array<{ message: string; location: string; fixSuggestion?: string }>,\n };\n\n const specNode = element.toSpecNode();\n\n // First: Validate against base spec-node schema using pre-compiled validator\n // This ensures all elements conform to the base structure before type-specific validation\n await this.ensureBaseSchemaLoaded();\n const baseValid = validateSpecNode(specNode);\n\n if (!baseValid && validateSpecNode.errors) {\n result.hasErrors = true;\n result.validated = true;\n for (const error of validateSpecNode.errors) {\n result.errors.push({\n message: `Element '${element.path || element.id}': ${this.formatAjvError(error)}`,\n location: error.instancePath || \"/\",\n fixSuggestion: this.generateFixSuggestion(error),\n });\n }\n return result; // Fail fast on base schema violation\n }\n\n // Second: Load and validate against type-specific schema (lazy-loaded)\n const validate = await this.loadSpecNodeSchema(layerName, element.type);\n\n if (!validate) {\n // No type-specific schema found - base schema validation passed\n // This is acceptable for custom types or types without schemas yet\n result.validated = true;\n return result;\n }\n\n result.validated = true;\n\n // Validate element against type-specific schema\n const typeValid = validate(specNode);\n\n if (!typeValid && validate.errors) {\n result.hasErrors = true;\n for (const error of validate.errors) {\n result.errors.push({\n message: `Element '${element.path || element.id}': ${this.formatAjvError(error)}`,\n location: error.instancePath || \"/\",\n fixSuggestion: this.generateFixSuggestion(error),\n });\n }\n }\n\n return result;\n }\n\n /**\n * Format AJV error message\n */\n private formatAjvError(error: ErrorObject): string {\n const keyword = error.keyword;\n const dataPath = error.instancePath || \"/\";\n\n switch (keyword) {\n case \"type\":\n return `At ${dataPath}: expected ${error.params?.type}, got ${error.data === null ? \"null\" : typeof error.data}`;\n case \"required\":\n return `At ${dataPath}: missing required property '${error.params?.missingProperty}'`;\n case \"enum\":\n return `At ${dataPath}: value must be one of [${error.params?.allowedValues?.join(\", \")}]`;\n case \"pattern\":\n return `At ${dataPath}: value must match pattern ${error.params?.pattern}`;\n case \"minLength\":\n return `At ${dataPath}: string must be at least ${error.params?.limit} characters`;\n case \"maxLength\":\n return `At ${dataPath}: string must be at most ${error.params?.limit} characters`;\n case \"minimum\":\n return `At ${dataPath}: value must be >= ${error.params?.limit}`;\n case \"maximum\":\n return `At ${dataPath}: value must be <= ${error.params?.limit}`;\n case \"additionalProperties\":\n return `At ${dataPath}: unexpected property '${error.params?.additionalProperty}'`;\n case \"const\":\n return `At ${dataPath}: value must be ${JSON.stringify(error.params?.allowedValue)}`;\n default:\n return error.message || `Validation failed`;\n }\n }\n\n /**\n * Generate a fix suggestion based on the error\n */\n private generateFixSuggestion(error: ErrorObject): string | undefined {\n const keyword = error.keyword;\n\n switch (keyword) {\n case \"required\":\n return `Add required field: ${error.params?.missingProperty}`;\n case \"type\":\n return `Expected type ${error.params?.type}`;\n case \"enum\":\n return `Use one of: ${error.params?.allowedValues?.join(\", \")}`;\n case \"pattern\":\n return `Value must match pattern: ${error.params?.pattern}`;\n case \"additionalProperties\":\n return `Remove unexpected property: ${error.params?.additionalProperty}`;\n case \"const\":\n return `Value must be exactly: ${JSON.stringify(error.params?.allowedValue)}`;\n default:\n return undefined;\n }\n }\n\n /**\n * Reset static state for test isolation\n *\n * Clears the shared AJV instance and schema registry, allowing tests\n * to load schemas from a clean state without conflicts.\n *\n * Usage (in test setup):\n * beforeEach(() => {\n * SchemaValidator.reset();\n * });\n */\n static reset(): void {\n SchemaValidator.sharedAjv = null;\n SchemaValidator.baseSchemaLoadedInSharedInstance = false;\n SchemaValidator.registeredSchemaIds.clear();\n SchemaValidator.layerCache.clear();\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Schema validation using AJV\n *\n * Validates model elements against their corresponding spec node schemas.\n * Each element type has a dedicated schema in spec/schemas/nodes/{layer}/{type}.node.schema.json\n */\n\nimport Ajv, { ValidateFunction, ErrorObject } from \"ajv\";\nimport addFormats from \"ajv-formats\";\nimport { ValidationResult } from \"./types.js\";\nimport type { Layer } from \"../core/layer.js\";\nimport type { Element } from \"../core/element.js\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport { readFile } from \"../utils/file-io.js\";\nimport { startActiveSpan } from \"../telemetry/index.js\";\nimport { existsSync } from \"fs\";\nimport {\n validateSpecNode,\n validateSpecNodeRelationship,\n validateSourceReference,\n validateAttributeSpec,\n} from \"../generated/compiled-validators.js\";\nimport { isValidLayer } from \"../generated/layer-registry.js\";\nimport { getNodeTypesForLayer, isValidNodeType } from \"../generated/node-types.js\";\nimport { findSimilar } from \"../utils/errors.js\";\n\n/**\n * Types in `layer` that `type` may have meant: close spellings, and names that contain it or that\n * it contains (an obsolete `capability` \u2192 `softwarecapability`).\n */\nexport function suggestNodeTypes(layer: string, type: string): string[] {\n const names = getNodeTypesForLayer(layer).map((t) => t.type);\n const lower = type.toLowerCase();\n const containing =\n lower.length >= 4\n ? names.filter((name) => name !== lower && (name.includes(lower) || lower.includes(name)))\n : [];\n return Array.from(new Set([...findSimilar(type, names, 3), ...containing])).slice(0, 3);\n}\n\n// Fallback for runtime environments where TELEMETRY_ENABLED is not defined by esbuild\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\n/**\n * Validator for JSON Schema compliance\n *\n * Validates model elements against spec node schemas. Each element is validated\n * against its type-specific schema (e.g., motivation.goal elements validate against\n * spec/schemas/nodes/motivation/goal.node.schema.json).\n */\nexport class SchemaValidator {\n // Static shared AJV instance to avoid schema registry conflicts\n // When multiple SchemaValidator instances are created, they all use the same\n // AJV instance which maintains a single schema registry. This prevents\n // \"schema with key or id already exists\" errors when loading the same schemas.\n private static sharedAjv: Ajv | null = null;\n private static baseSchemaLoadedInSharedInstance: boolean = false;\n // Track schema IDs already registered in the shared AJV instance to avoid re-registering\n private static registeredSchemaIds: Set<string> = new Set();\n // Layer-level cache: layer ID \u2192 nodeSchemas map (avoids re-reading layer dist files)\n private static layerCache: Map<string, Record<string, unknown>> = new Map();\n\n // Compiled node schemas, keyed by layer.type. Shared like the AJV instance they were compiled\n // in: registeredSchemaIds is static, so a per-instance cache would leave every later instance\n // with a registered id and no validator, and it would silently fall back to the base schema.\n private static compiledSchemas: Map<string, ValidateFunction> = new Map();\n\n private ajv: Ajv;\n private bundledDir: string;\n\n constructor() {\n // Initialize shared AJV instance once\n if (!SchemaValidator.sharedAjv) {\n SchemaValidator.sharedAjv = new Ajv({\n allErrors: true,\n strict: false,\n validateFormats: true,\n verbose: true,\n });\n addFormats(SchemaValidator.sharedAjv);\n }\n this.ajv = SchemaValidator.sharedAjv;\n\n // Determine bundled directory (compiled spec dist files)\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n this.bundledDir = path.join(__dirname, \"..\", \"schemas\", \"bundled\");\n }\n\n /**\n * Extract error message from unknown error type with proper type checking\n */\n private static getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (typeof error === \"object\" && error !== null && \"message\" in error) {\n const msg = (error as Record<string, unknown>).message;\n if (typeof msg === \"string\") {\n return msg;\n }\n }\n return \"Unknown error\";\n }\n\n /**\n * Ensure base schemas are pre-compiled validators are initialized\n * Loads base schemas from base.json and registers them with AJV for reference resolution.\n * This allows per-type schemas to resolve $ref to base schemas during compilation.\n */\n private async ensureBaseSchemaLoaded(): Promise<void> {\n // Check if already loaded in the shared AJV instance (applies to all instances)\n if (SchemaValidator.baseSchemaLoadedInSharedInstance) {\n return;\n }\n\n // Register pre-compiled validators with AJV\n SchemaValidator.compiledSchemas.set(\"spec-node\", validateSpecNode);\n SchemaValidator.compiledSchemas.set(\"spec-node-relationship\", validateSpecNodeRelationship);\n SchemaValidator.compiledSchemas.set(\"source-references\", validateSourceReference);\n SchemaValidator.compiledSchemas.set(\"attribute-spec\", validateAttributeSpec);\n\n // Load base.json and register all schemas with AJV for $ref resolution\n // Schemas use URN-style $ids like \"urn:dr:spec:base:spec-node\" after compilation\n const baseJsonPath = path.join(this.bundledDir, \"base.json\");\n try {\n const baseContent = await readFile(baseJsonPath);\n const baseData = JSON.parse(baseContent) as {\n specVersion: string;\n schemas: Record<string, unknown>;\n predicates: unknown;\n };\n\n for (const [key, schema] of Object.entries(baseData.schemas)) {\n const s = schema as Record<string, unknown>;\n const schemaId = (s.$id as string) || `urn:dr:spec:base:${key}`;\n\n // Skip if already registered\n if (SchemaValidator.registeredSchemaIds.has(schemaId)) {\n continue;\n }\n\n try {\n this.ajv.addSchema(s, schemaId);\n SchemaValidator.registeredSchemaIds.add(schemaId);\n } catch (error: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(error);\n const isDuplicateSchemaError = /schema with key or id [^ ]+ already exists/.test(errorMsg);\n if (isDuplicateSchemaError) {\n SchemaValidator.registeredSchemaIds.add(schemaId);\n } else {\n console.warn(`[SCHEMA-LOAD-001] Failed to register base schema '${key}': ${errorMsg}`);\n }\n }\n }\n } catch (error: unknown) {\n console.warn(\n `[SCHEMA-LOAD-001] Failed to load base.json from ${baseJsonPath}: ${SchemaValidator.getErrorMessage(error)}`\n );\n }\n\n // Mark base schemas as loaded in shared instance (applies to all instances)\n SchemaValidator.baseSchemaLoadedInSharedInstance = true;\n }\n\n /**\n * Load the nodeSchemas map for a layer, using a static layer-level cache.\n * Reads bundled/{layer}.json once per layer and caches the nodeSchemas object.\n */\n private async loadLayerNodeSchemas(layer: string): Promise<Record<string, unknown> | null> {\n // Return cached layer schemas if available\n if (SchemaValidator.layerCache.has(layer)) {\n return SchemaValidator.layerCache.get(layer)!;\n }\n\n const layerPath = path.join(this.bundledDir, `${layer}.json`);\n if (!existsSync(layerPath)) {\n return null;\n }\n\n try {\n const content = await readFile(layerPath);\n const layerData = JSON.parse(content) as { nodeSchemas: Record<string, unknown> };\n const nodeSchemas = layerData.nodeSchemas || {};\n SchemaValidator.layerCache.set(layer, nodeSchemas);\n return nodeSchemas;\n } catch (error: unknown) {\n console.warn(\n `[SCHEMA-LOAD-002] Failed to load layer file for '${layer}': ${SchemaValidator.getErrorMessage(error)}`\n );\n return null;\n }\n }\n\n /**\n * Load and compile a spec node schema for a specific element type\n */\n private async loadSpecNodeSchema(layer: string, type: string): Promise<ValidateFunction | null> {\n const cacheKey = `${layer}.${type}`;\n\n // Check compiled schema cache first\n if (SchemaValidator.compiledSchemas.has(cacheKey)) {\n return SchemaValidator.compiledSchemas.get(cacheKey)!;\n }\n\n // Ensure base schemas are loaded first (required for reference resolution)\n await this.ensureBaseSchemaLoaded();\n\n try {\n // Load node schemas from layer dist file (layer-level cache)\n const nodeSchemas = await this.loadLayerNodeSchemas(layer);\n if (!nodeSchemas || !nodeSchemas[type]) {\n // No schema for this type - acceptable for custom types\n return null;\n }\n\n const schema = nodeSchemas[type] as Record<string, unknown>;\n const schemaId = (schema.$id as string) || `urn:dr:spec:node:${layer}.${type}`;\n\n // Check if schema with this $id is already registered to avoid duplicate compilation\n if (SchemaValidator.registeredSchemaIds.has(schemaId)) {\n // Schema already registered \u2014 return from compiled cache or null (fallback to base)\n return SchemaValidator.compiledSchemas.get(cacheKey) || null;\n }\n\n try {\n const validate = this.ajv.compile(schema);\n SchemaValidator.compiledSchemas.set(cacheKey, validate);\n SchemaValidator.registeredSchemaIds.add(schemaId);\n return validate;\n } catch (registrationError: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(registrationError);\n if (errorMsg.includes(\"already exists\")) {\n SchemaValidator.registeredSchemaIds.add(schemaId);\n return null;\n }\n throw registrationError;\n }\n } catch (error: unknown) {\n const errorMsg = SchemaValidator.getErrorMessage(error);\n if (errorMsg.includes(\"can't resolve reference\")) {\n return null;\n }\n console.warn(`[SCHEMA-LOAD-002] Failed to load spec node schema for ${layer}.${type}: ${errorMsg}`);\n return null;\n }\n }\n\n /**\n * Validate a layer against its spec node schemas\n *\n * This validates each element in the layer against its corresponding spec node schema.\n */\n async validateLayer(layer: Layer): Promise<ValidationResult> {\n return startActiveSpan(\n \"schema.validate\",\n async (span) => {\n const result = new ValidationResult();\n let validatedCount = 0;\n let skippedCount = 0;\n\n // Validate each element individually\n for (const element of layer.elements.values()) {\n const elementResult = await this.validateElement(element, layer.name);\n\n if (elementResult.hasErrors) {\n // Add element-specific errors to layer result\n for (const error of elementResult.errors) {\n result.addError({\n layer: layer.name,\n elementId: element.path || element.id,\n message: error.message,\n location: error.location,\n fixSuggestion: error.fixSuggestion,\n });\n }\n }\n\n if (elementResult.validated) {\n validatedCount++;\n } else {\n skippedCount++;\n }\n }\n\n if (isTelemetryEnabled) {\n span.setAttribute(\"schema.valid\", result.isValid());\n span.setAttribute(\"schema.error_count\", result.errors.length);\n span.setAttribute(\"schema.validated_count\", validatedCount);\n span.setAttribute(\"schema.skipped_count\", skippedCount);\n }\n\n return result;\n },\n { \"schema.layer\": layer.name }\n );\n }\n\n /**\n * Validate a single element against its spec node schema\n */\n private async validateElement(element: Element, layerName: string): Promise<{\n validated: boolean;\n hasErrors: boolean;\n errors: Array<{ message: string; location: string; fixSuggestion?: string }>;\n }> {\n const result = {\n validated: false,\n hasErrors: false,\n errors: [] as Array<{ message: string; location: string; fixSuggestion?: string }>,\n };\n\n const specNode = element.toSpecNode();\n\n // First: Validate against base spec-node schema using pre-compiled validator\n // This ensures all elements conform to the base structure before type-specific validation\n await this.ensureBaseSchemaLoaded();\n const baseValid = validateSpecNode(specNode);\n\n if (!baseValid && validateSpecNode.errors) {\n result.hasErrors = true;\n result.validated = true;\n for (const error of validateSpecNode.errors) {\n result.errors.push({\n message: `Element '${element.path || element.id}': ${this.formatAjvError(error)}`,\n location: error.instancePath || \"/\",\n fixSuggestion: this.generateFixSuggestion(error),\n });\n }\n return result; // Fail fast on base schema violation\n }\n\n // An element whose type the spec doesn't define (often one the spec has since dropped or\n // renamed) has no type schema to check, so report it rather than letting it pass unchecked.\n if (isValidLayer(layerName) && !isValidNodeType(layerName, element.type)) {\n const elementId = element.path || element.id;\n const similar = suggestNodeTypes(layerName, element.type);\n result.validated = true;\n result.hasErrors = true;\n result.errors.push({\n message:\n `Element '${elementId}' has type '${element.type}', which the spec does not define for the ${layerName} layer` +\n (similar.length > 0 ? ` (did you mean ${similar.map((s) => `'${s}'`).join(\" or \")}?)` : \"\"),\n location: \"/type\",\n fixSuggestion:\n similar.length > 0\n ? `Change the element's type to '${similar[0]}', or remove the element`\n : `Change the element's type to one the ${layerName} layer defines, or remove the element`,\n });\n return result;\n }\n\n // Second: Load and validate against type-specific schema (lazy-loaded)\n const validate = await this.loadSpecNodeSchema(layerName, element.type);\n\n if (!validate) {\n // No type-specific schema found - base schema validation passed\n // This is acceptable for custom types or types without schemas yet\n result.validated = true;\n return result;\n }\n\n result.validated = true;\n\n // Validate element against type-specific schema\n const typeValid = validate(specNode);\n\n if (!typeValid && validate.errors) {\n result.hasErrors = true;\n for (const error of validate.errors) {\n result.errors.push({\n message: `Element '${element.path || element.id}': ${this.formatAjvError(error)}`,\n location: error.instancePath || \"/\",\n fixSuggestion: this.generateFixSuggestion(error),\n });\n }\n }\n\n return result;\n }\n\n /**\n * Format AJV error message\n */\n private formatAjvError(error: ErrorObject): string {\n const keyword = error.keyword;\n const dataPath = error.instancePath || \"/\";\n\n switch (keyword) {\n case \"type\":\n return `At ${dataPath}: expected ${error.params?.type}, got ${error.data === null ? \"null\" : typeof error.data}`;\n case \"required\":\n return `At ${dataPath}: missing required property '${error.params?.missingProperty}'`;\n case \"enum\":\n return `At ${dataPath}: value must be one of [${error.params?.allowedValues?.join(\", \")}]`;\n case \"pattern\":\n return `At ${dataPath}: value must match pattern ${error.params?.pattern}`;\n case \"minLength\":\n return `At ${dataPath}: string must be at least ${error.params?.limit} characters`;\n case \"maxLength\":\n return `At ${dataPath}: string must be at most ${error.params?.limit} characters`;\n case \"minimum\":\n return `At ${dataPath}: value must be >= ${error.params?.limit}`;\n case \"maximum\":\n return `At ${dataPath}: value must be <= ${error.params?.limit}`;\n case \"additionalProperties\":\n return `At ${dataPath}: unexpected property '${error.params?.additionalProperty}'`;\n case \"const\":\n return `At ${dataPath}: value must be ${JSON.stringify(error.params?.allowedValue)}`;\n default:\n return error.message || `Validation failed`;\n }\n }\n\n /**\n * Generate a fix suggestion based on the error\n */\n private generateFixSuggestion(error: ErrorObject): string | undefined {\n const keyword = error.keyword;\n\n switch (keyword) {\n case \"required\":\n return `Add required field: ${error.params?.missingProperty}`;\n case \"type\":\n return `Expected type ${error.params?.type}`;\n case \"enum\":\n return `Use one of: ${error.params?.allowedValues?.join(\", \")}`;\n case \"pattern\":\n return `Value must match pattern: ${error.params?.pattern}`;\n case \"additionalProperties\":\n return `Remove unexpected property: ${error.params?.additionalProperty}`;\n case \"const\":\n return `Value must be exactly: ${JSON.stringify(error.params?.allowedValue)}`;\n default:\n return undefined;\n }\n }\n\n /**\n * Reset static state for test isolation\n *\n * Clears the shared AJV instance and schema registry, allowing tests\n * to load schemas from a clean state without conflicts.\n *\n * Usage (in test setup):\n * beforeEach(() => {\n * SchemaValidator.reset();\n * });\n */\n static reset(): void {\n SchemaValidator.sharedAjv = null;\n SchemaValidator.baseSchemaLoadedInSharedInstance = false;\n SchemaValidator.registeredSchemaIds.clear();\n SchemaValidator.layerCache.clear();\n SchemaValidator.compiledSchemas.clear();\n }\n}\n"],
5
+ "mappings": "AAOA,OAAO,SAA4C;AACnD,OAAO,gBAAgB;AACvB,SAAS,wBAAwB;AAGjC,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AACjB,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAC3B,SACE,kBACA,8BACA,yBACA,6BACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB,uBAAuB;AACtD,SAAS,mBAAmB;AAMtB,SAAU,iBAAiB,OAAe,MAAY;AAC1D,QAAM,QAAQ,qBAAqB,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GACrD,QAAQ,KAAK,YAAW,GACxB,aACJ,MAAM,UAAU,IACZ,MAAM,OAAO,CAAC,SAAS,SAAS,UAAU,KAAK,SAAS,KAAK,KAAK,MAAM,SAAS,IAAI,EAAE,IACvF,CAAA;AACN,SAAO,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,YAAY,MAAM,OAAO,CAAC,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AACxF;AAIA,MAAM,qBAAgE;AAShE,MAAO,gBAAe;;;;;EAKlB,OAAO,YAAwB;EAC/B,OAAO,mCAA4C;;EAEnD,OAAO,sBAAmC,oBAAI,IAAG;;EAEjD,OAAO,aAAmD,oBAAI,IAAG;;;;EAKjE,OAAO,kBAAiD,oBAAI,IAAG;EAE/D;EACA;EAER,cAAA;AAEE,IAAK,gBAAgB,cACnB,gBAAgB,YAAY,IAAI,IAAI;MAClC,WAAW;MACX,QAAQ;MACR,iBAAiB;MACjB,SAAS;KACV,GACD,WAAW,gBAAgB,SAAS,IAEtC,KAAK,MAAM,gBAAgB;AAG3B,UAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,SAAK,aAAa,KAAK,KAAK,WAAW,MAAM,WAAW,SAAS;EACnE;;;;EAKQ,OAAO,gBAAgB,OAAc;AAC3C,QAAI,iBAAiB;AACnB,aAAO,MAAM;AAEf,QAAI,OAAO,SAAU,YAAY,UAAU,QAAQ,aAAa,OAAO;AACrE,YAAM,MAAO,MAAkC;AAC/C,UAAI,OAAO,OAAQ;AACjB,eAAO;IAEX;AACA,WAAO;EACT;;;;;;EAOQ,MAAM,yBAAsB;AAElC,QAAI,gBAAgB;AAClB;AAIF,oBAAgB,gBAAgB,IAAI,aAAa,gBAAgB,GACjE,gBAAgB,gBAAgB,IAAI,0BAA0B,4BAA4B,GAC1F,gBAAgB,gBAAgB,IAAI,qBAAqB,uBAAuB,GAChF,gBAAgB,gBAAgB,IAAI,kBAAkB,qBAAqB;AAI3E,UAAM,eAAe,KAAK,KAAK,KAAK,YAAY,WAAW;AAC3D,QAAI;AACF,YAAM,cAAc,MAAM,SAAS,YAAY,GACzC,WAAW,KAAK,MAAM,WAAW;AAMvC,iBAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AAC5D,cAAM,IAAI,QACJ,WAAY,EAAE,OAAkB,oBAAoB,GAAG;AAG7D,YAAI,iBAAgB,oBAAoB,IAAI,QAAQ;AAIpD,cAAI;AACF,iBAAK,IAAI,UAAU,GAAG,QAAQ,GAC9B,gBAAgB,oBAAoB,IAAI,QAAQ;UAClD,SAAS,OAAgB;AACvB,kBAAM,WAAW,gBAAgB,gBAAgB,KAAK;AAEtD,YAD+B,6CAA6C,KAAK,QAAQ,IAEvF,gBAAgB,oBAAoB,IAAI,QAAQ,IAEhD,QAAQ,KAAK,qDAAqD,GAAG,MAAM,QAAQ,EAAE;UAEzF;MACF;IACF,SAAS,OAAgB;AACvB,cAAQ,KACN,mDAAmD,YAAY,KAAK,gBAAgB,gBAAgB,KAAK,CAAC,EAAE;IAEhH;AAGA,oBAAgB,mCAAmC;EACrD;;;;;EAMQ,MAAM,qBAAqB,OAAa;AAE9C,QAAI,gBAAgB,WAAW,IAAI,KAAK;AACtC,aAAO,gBAAgB,WAAW,IAAI,KAAK;AAG7C,UAAM,YAAY,KAAK,KAAK,KAAK,YAAY,GAAG,KAAK,OAAO;AAC5D,QAAI,CAAC,WAAW,SAAS;AACvB,aAAO;AAGT,QAAI;AACF,YAAM,UAAU,MAAM,SAAS,SAAS,GAElC,cADY,KAAK,MAAM,OAAO,EACN,eAAe,CAAA;AAC7C,6BAAgB,WAAW,IAAI,OAAO,WAAW,GAC1C;IACT,SAAS,OAAgB;AACvB,qBAAQ,KACN,oDAAoD,KAAK,MAAM,gBAAgB,gBAAgB,KAAK,CAAC,EAAE,GAElG;IACT;EACF;;;;EAKQ,MAAM,mBAAmB,OAAe,MAAY;AAC1D,UAAM,WAAW,GAAG,KAAK,IAAI,IAAI;AAGjC,QAAI,gBAAgB,gBAAgB,IAAI,QAAQ;AAC9C,aAAO,gBAAgB,gBAAgB,IAAI,QAAQ;AAIrD,UAAM,KAAK,uBAAsB;AAEjC,QAAI;AAEF,YAAM,cAAc,MAAM,KAAK,qBAAqB,KAAK;AACzD,UAAI,CAAC,eAAe,CAAC,YAAY,IAAI;AAEnC,eAAO;AAGT,YAAM,SAAS,YAAY,IAAI,GACzB,WAAY,OAAO,OAAkB,oBAAoB,KAAK,IAAI,IAAI;AAG5E,UAAI,gBAAgB,oBAAoB,IAAI,QAAQ;AAElD,eAAO,gBAAgB,gBAAgB,IAAI,QAAQ,KAAK;AAG1D,UAAI;AACF,cAAM,WAAW,KAAK,IAAI,QAAQ,MAAM;AACxC,+BAAgB,gBAAgB,IAAI,UAAU,QAAQ,GACtD,gBAAgB,oBAAoB,IAAI,QAAQ,GACzC;MACT,SAAS,mBAA4B;AAEnC,YADiB,gBAAgB,gBAAgB,iBAAiB,EACrD,SAAS,gBAAgB;AACpC,iCAAgB,oBAAoB,IAAI,QAAQ,GACzC;AAET,cAAM;MACR;IACF,SAAS,OAAgB;AACvB,YAAM,WAAW,gBAAgB,gBAAgB,KAAK;AACtD,aAAI,SAAS,SAAS,yBAAyB,KAG/C,QAAQ,KAAK,yDAAyD,KAAK,IAAI,IAAI,KAAK,QAAQ,EAAE,GAC3F;IACT;EACF;;;;;;EAOA,MAAM,cAAc,OAAY;AAC9B,WAAO,gBACL,mBACA,OAAO,SAAQ;AACb,YAAM,SAAS,IAAI,iBAAgB;AACnC,UAAI,iBAAiB,GACjB,eAAe;AAGnB,iBAAW,WAAW,MAAM,SAAS,OAAM,GAAI;AAC7C,cAAM,gBAAgB,MAAM,KAAK,gBAAgB,SAAS,MAAM,IAAI;AAEpE,YAAI,cAAc;AAEhB,qBAAW,SAAS,cAAc;AAChC,mBAAO,SAAS;cACd,OAAO,MAAM;cACb,WAAW,QAAQ,QAAQ,QAAQ;cACnC,SAAS,MAAM;cACf,UAAU,MAAM;cAChB,eAAe,MAAM;aACtB;AAIL,QAAI,cAAc,YAChB,mBAEA;MAEJ;AAEA,aAAI,uBACF,KAAK,aAAa,gBAAgB,OAAO,QAAO,CAAE,GAClD,KAAK,aAAa,sBAAsB,OAAO,OAAO,MAAM,GAC5D,KAAK,aAAa,0BAA0B,cAAc,GAC1D,KAAK,aAAa,wBAAwB,YAAY,IAGjD;IACT,GACA,EAAE,gBAAgB,MAAM,KAAI,CAAE;EAElC;;;;EAKQ,MAAM,gBAAgB,SAAkB,WAAiB;AAK/D,UAAM,SAAS;MACb,WAAW;MACX,WAAW;MACX,QAAQ,CAAA;OAGJ,WAAW,QAAQ,WAAU;AAOnC,QAHA,MAAM,KAAK,uBAAsB,GAG7B,CAFc,iBAAiB,QAAQ,KAEzB,iBAAiB,QAAQ;AACzC,aAAO,YAAY,IACnB,OAAO,YAAY;AACnB,iBAAW,SAAS,iBAAiB;AACnC,eAAO,OAAO,KAAK;UACjB,SAAS,YAAY,QAAQ,QAAQ,QAAQ,EAAE,MAAM,KAAK,eAAe,KAAK,CAAC;UAC/E,UAAU,MAAM,gBAAgB;UAChC,eAAe,KAAK,sBAAsB,KAAK;SAChD;AAEH,aAAO;IACT;AAIA,QAAI,aAAa,SAAS,KAAK,CAAC,gBAAgB,WAAW,QAAQ,IAAI,GAAG;AACxE,YAAM,YAAY,QAAQ,QAAQ,QAAQ,IACpC,UAAU,iBAAiB,WAAW,QAAQ,IAAI;AACxD,oBAAO,YAAY,IACnB,OAAO,YAAY,IACnB,OAAO,OAAO,KAAK;QACjB,SACE,YAAY,SAAS,eAAe,QAAQ,IAAI,6CAA6C,SAAS,YACrG,QAAQ,SAAS,IAAI,kBAAkB,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,CAAC,OAAO;QAC1F,UAAU;QACV,eACE,QAAQ,SAAS,IACb,iCAAiC,QAAQ,CAAC,CAAC,6BAC3C,wCAAwC,SAAS;OACxD,GACM;IACT;AAGA,UAAM,WAAW,MAAM,KAAK,mBAAmB,WAAW,QAAQ,IAAI;AAEtE,QAAI,CAAC;AAGH,oBAAO,YAAY,IACZ;AAQT,QALA,OAAO,YAAY,IAKf,CAFc,SAAS,QAAQ,KAEjB,SAAS,QAAQ;AACjC,aAAO,YAAY;AACnB,iBAAW,SAAS,SAAS;AAC3B,eAAO,OAAO,KAAK;UACjB,SAAS,YAAY,QAAQ,QAAQ,QAAQ,EAAE,MAAM,KAAK,eAAe,KAAK,CAAC;UAC/E,UAAU,MAAM,gBAAgB;UAChC,eAAe,KAAK,sBAAsB,KAAK;SAChD;IAEL;AAEA,WAAO;EACT;;;;EAKQ,eAAe,OAAkB;AACvC,UAAM,UAAU,MAAM,SAChB,WAAW,MAAM,gBAAgB;AAEvC,YAAQ,SAAS;MACf,KAAK;AACH,eAAO,MAAM,QAAQ,cAAc,MAAM,QAAQ,IAAI,SAAS,MAAM,SAAS,OAAO,SAAS,OAAO,MAAM,IAAI;MAChH,KAAK;AACH,eAAO,MAAM,QAAQ,gCAAgC,MAAM,QAAQ,eAAe;MACpF,KAAK;AACH,eAAO,MAAM,QAAQ,2BAA2B,MAAM,QAAQ,eAAe,KAAK,IAAI,CAAC;MACzF,KAAK;AACH,eAAO,MAAM,QAAQ,8BAA8B,MAAM,QAAQ,OAAO;MAC1E,KAAK;AACH,eAAO,MAAM,QAAQ,6BAA6B,MAAM,QAAQ,KAAK;MACvE,KAAK;AACH,eAAO,MAAM,QAAQ,4BAA4B,MAAM,QAAQ,KAAK;MACtE,KAAK;AACH,eAAO,MAAM,QAAQ,sBAAsB,MAAM,QAAQ,KAAK;MAChE,KAAK;AACH,eAAO,MAAM,QAAQ,sBAAsB,MAAM,QAAQ,KAAK;MAChE,KAAK;AACH,eAAO,MAAM,QAAQ,0BAA0B,MAAM,QAAQ,kBAAkB;MACjF,KAAK;AACH,eAAO,MAAM,QAAQ,mBAAmB,KAAK,UAAU,MAAM,QAAQ,YAAY,CAAC;MACpF;AACE,eAAO,MAAM,WAAW;IAC5B;EACF;;;;EAKQ,sBAAsB,OAAkB;AAG9C,YAFgB,MAAM,SAEL;MACf,KAAK;AACH,eAAO,uBAAuB,MAAM,QAAQ,eAAe;MAC7D,KAAK;AACH,eAAO,iBAAiB,MAAM,QAAQ,IAAI;MAC5C,KAAK;AACH,eAAO,eAAe,MAAM,QAAQ,eAAe,KAAK,IAAI,CAAC;MAC/D,KAAK;AACH,eAAO,6BAA6B,MAAM,QAAQ,OAAO;MAC3D,KAAK;AACH,eAAO,+BAA+B,MAAM,QAAQ,kBAAkB;MACxE,KAAK;AACH,eAAO,0BAA0B,KAAK,UAAU,MAAM,QAAQ,YAAY,CAAC;MAC7E;AACE;IACJ;EACF;;;;;;;;;;;;EAaA,OAAO,QAAK;AACV,oBAAgB,YAAY,MAC5B,gBAAgB,mCAAmC,IACnD,gBAAgB,oBAAoB,MAAK,GACzC,gBAAgB,WAAW,MAAK,GAChC,gBAAgB,gBAAgB,MAAK;EACvC;;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/validators/types.ts"],
4
- "sourcesContent": ["/**\n * Validation result types and classes\n */\n\n/**\n * A single validation issue\n */\nexport interface ValidationIssue {\n layer: string;\n elementId?: string;\n message: string;\n severity: \"error\" | \"warning\";\n category?: \"orphan\" | \"schema\" | \"semantic\" | \"reference\";\n location?: string;\n fixSuggestion?: string;\n}\n\n/**\n * Result of a validation operation\n */\nexport class ValidationResult {\n errors: ValidationIssue[] = [];\n warnings: ValidationIssue[] = [];\n\n /**\n * Check if validation passed (no errors)\n */\n isValid(): boolean {\n return this.errors.length === 0;\n }\n\n /**\n * Add an error to the result\n */\n addError(issue: Omit<ValidationIssue, \"severity\">): void {\n this.errors.push({ ...issue, severity: \"error\" });\n }\n\n /**\n * Add a warning to the result\n */\n addWarning(issue: Omit<ValidationIssue, \"severity\">): void {\n this.warnings.push({ ...issue, severity: \"warning\" });\n }\n\n /**\n * Merge another validation result into this one\n */\n merge(other: ValidationResult, prefix?: string): void {\n const addPrefix = (issue: ValidationIssue): ValidationIssue => ({\n ...issue,\n message: prefix ? `${prefix}: ${issue.message}` : issue.message,\n });\n\n this.errors.push(...other.errors.map(addPrefix));\n this.warnings.push(...other.warnings.map(addPrefix));\n }\n\n /**\n * Convert to dictionary representation\n */\n toDict(): Record<string, unknown> {\n return {\n valid: this.isValid(),\n errors: this.errors,\n warnings: this.warnings,\n errorCount: this.errors.length,\n warningCount: this.warnings.length,\n };\n }\n}\n"],
4
+ "sourcesContent": ["/**\n * Validation result types and classes\n */\n\n/**\n * A single validation issue\n */\nexport interface ValidationIssue {\n layer: string;\n elementId?: string;\n message: string;\n severity: \"error\" | \"warning\";\n category?: \"orphan\" | \"schema\" | \"semantic\" | \"reference\" | \"expected\";\n location?: string;\n fixSuggestion?: string;\n}\n\n/**\n * Result of a validation operation\n */\nexport class ValidationResult {\n errors: ValidationIssue[] = [];\n warnings: ValidationIssue[] = [];\n\n /**\n * Check if validation passed (no errors)\n */\n isValid(): boolean {\n return this.errors.length === 0;\n }\n\n /**\n * Add an error to the result\n */\n addError(issue: Omit<ValidationIssue, \"severity\">): void {\n this.errors.push({ ...issue, severity: \"error\" });\n }\n\n /**\n * Add a warning to the result\n */\n addWarning(issue: Omit<ValidationIssue, \"severity\">): void {\n this.warnings.push({ ...issue, severity: \"warning\" });\n }\n\n /**\n * Merge another validation result into this one\n */\n merge(other: ValidationResult, prefix?: string): void {\n const addPrefix = (issue: ValidationIssue): ValidationIssue => ({\n ...issue,\n message: prefix ? `${prefix}: ${issue.message}` : issue.message,\n });\n\n this.errors.push(...other.errors.map(addPrefix));\n this.warnings.push(...other.warnings.map(addPrefix));\n }\n\n /**\n * Convert to dictionary representation\n */\n toDict(): Record<string, unknown> {\n return {\n valid: this.isValid(),\n errors: this.errors,\n warnings: this.warnings,\n errorCount: this.errors.length,\n warningCount: this.warnings.length,\n };\n }\n}\n"],
5
5
  "mappings": "AAoBM,MAAO,iBAAgB;EAC3B,SAA4B,CAAA;EAC5B,WAA8B,CAAA;;;;EAK9B,UAAO;AACL,WAAO,KAAK,OAAO,WAAW;EAChC;;;;EAKA,SAAS,OAAwC;AAC/C,SAAK,OAAO,KAAK,EAAE,GAAG,OAAO,UAAU,QAAO,CAAE;EAClD;;;;EAKA,WAAW,OAAwC;AACjD,SAAK,SAAS,KAAK,EAAE,GAAG,OAAO,UAAU,UAAS,CAAE;EACtD;;;;EAKA,MAAM,OAAyB,QAAe;AAC5C,UAAM,YAAY,CAAC,WAA6C;MAC9D,GAAG;MACH,SAAS,SAAS,GAAG,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM;;AAG1D,SAAK,OAAO,KAAK,GAAG,MAAM,OAAO,IAAI,SAAS,CAAC,GAC/C,KAAK,SAAS,KAAK,GAAG,MAAM,SAAS,IAAI,SAAS,CAAC;EACrD;;;;EAKA,SAAM;AACJ,WAAO;MACL,OAAO,KAAK,QAAO;MACnB,QAAQ,KAAK;MACb,UAAU,KAAK;MACf,YAAY,KAAK,OAAO;MACxB,cAAc,KAAK,SAAS;;EAEhC;;",
6
6
  "names": []
7
7
  }
@@ -4,15 +4,15 @@ This directory contains the pre-built embedded viewer for the Documentation Robo
4
4
 
5
5
  ## Bundle Information
6
6
 
7
- - **Version:** 0.6.1
8
- - **Build Date:** 2026-08-21T18:55:31.363Z
9
- - **Total Size:** 1.28 MB
10
- - **Estimated Gzip:** 406.75 KB
7
+ - **Version:** 0.8.0
8
+ - **Build Date:** 2026-11-04T11:59:21.186Z
9
+ - **Total Size:** 1.4 MB
10
+ - **Estimated Gzip:** 443.89 KB
11
11
 
12
12
  ## Files
13
13
 
14
- - `assets/main-B7AOQXXX.js` (870.96 KB)
15
- - `assets/main-vdrFrG7q.css` (199.56 KB)
14
+ - `assets/main-CxzcuU_o.js` (986.27 KB)
15
+ - `assets/main-j-7ULYpm.css` (204.05 KB)
16
16
  - `fonts/inter/Inter-SemiBold.woff2` (23.88 KB)
17
17
  - `fonts/inter/Inter-ExtraBold.woff2` (23.83 KB)
18
18
  - `fonts/inter/Inter-Bold.woff2` (23.79 KB)