@documentation-robotics/cli 0.1.0 → 0.1.5

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 (510) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +466 -693
  3. package/dist/analysis/data-model-analyzer.js +189 -0
  4. package/dist/analysis/data-model-analyzer.js.map +7 -0
  5. package/dist/analysis/index.js +3 -0
  6. package/dist/analysis/index.js.map +7 -0
  7. package/dist/analysis/relationship-classifier.js +181 -0
  8. package/dist/analysis/relationship-classifier.js.map +7 -0
  9. package/dist/analyzers/base-analyzer.js +1 -0
  10. package/dist/analyzers/base-analyzer.js.map +7 -0
  11. package/dist/analyzers/call-graph-utils.js +39 -0
  12. package/dist/analyzers/call-graph-utils.js.map +7 -0
  13. package/dist/analyzers/cbm-analyzer.js +1205 -0
  14. package/dist/analyzers/cbm-analyzer.js.map +7 -0
  15. package/dist/analyzers/heuristic-utils.js +55 -0
  16. package/dist/analyzers/heuristic-utils.js.map +7 -0
  17. package/dist/analyzers/mapping-loader.js +146 -0
  18. package/dist/analyzers/mapping-loader.js.map +7 -0
  19. package/dist/analyzers/registry.js +124 -0
  20. package/dist/analyzers/registry.js.map +7 -0
  21. package/dist/analyzers/session-state.js +176 -0
  22. package/dist/analyzers/session-state.js.map +7 -0
  23. package/dist/analyzers/stdio-client.js +206 -0
  24. package/dist/analyzers/stdio-client.js.map +7 -0
  25. package/dist/analyzers/types.js +15 -0
  26. package/dist/analyzers/types.js.map +7 -0
  27. package/dist/analyzers/verify-engine.js +209 -0
  28. package/dist/analyzers/verify-engine.js.map +7 -0
  29. package/dist/analyzers/verify-ignore.js +130 -0
  30. package/dist/analyzers/verify-ignore.js.map +7 -0
  31. package/dist/analyzers/version.js +8 -0
  32. package/dist/analyzers/version.js.map +7 -0
  33. package/dist/audit/ai/runner.js +41 -0
  34. package/dist/audit/ai/runner.js.map +7 -0
  35. package/dist/audit/differential-analyzer.js +183 -0
  36. package/dist/audit/differential-analyzer.js.map +7 -0
  37. package/dist/audit/index.js +32 -0
  38. package/dist/audit/index.js.map +7 -0
  39. package/dist/audit/nodes/ai/evaluator.js +49 -0
  40. package/dist/audit/nodes/ai/evaluator.js.map +7 -0
  41. package/dist/audit/nodes/ai/parser.js +55 -0
  42. package/dist/audit/nodes/ai/parser.js.map +7 -0
  43. package/dist/audit/nodes/ai/prompts.js +103 -0
  44. package/dist/audit/nodes/ai/prompts.js.map +7 -0
  45. package/dist/audit/nodes/analysis/node-completeness-checker.js +32 -0
  46. package/dist/audit/nodes/analysis/node-completeness-checker.js.map +7 -0
  47. package/dist/audit/nodes/analysis/node-definition-analyzer.js +47 -0
  48. package/dist/audit/nodes/analysis/node-definition-analyzer.js.map +7 -0
  49. package/dist/audit/nodes/analysis/node-overlap-detector.js +79 -0
  50. package/dist/audit/nodes/analysis/node-overlap-detector.js.map +7 -0
  51. package/dist/audit/nodes/model/orchestrator.js +82 -0
  52. package/dist/audit/nodes/model/orchestrator.js.map +7 -0
  53. package/dist/audit/nodes/spec/loader.js +116 -0
  54. package/dist/audit/nodes/spec/loader.js.map +7 -0
  55. package/dist/audit/nodes/spec/orchestrator.js +68 -0
  56. package/dist/audit/nodes/spec/orchestrator.js.map +7 -0
  57. package/dist/audit/nodes/types.js +1 -0
  58. package/dist/audit/nodes/types.js.map +7 -0
  59. package/dist/audit/pipeline/index.js +5 -0
  60. package/dist/audit/pipeline/index.js.map +7 -0
  61. package/dist/audit/pipeline/pipeline-orchestrator.js +218 -0
  62. package/dist/audit/pipeline/pipeline-orchestrator.js.map +7 -0
  63. package/dist/audit/relationships/ai/evaluator.js +235 -0
  64. package/dist/audit/relationships/ai/evaluator.js.map +7 -0
  65. package/dist/audit/relationships/ai/parser.js +197 -0
  66. package/dist/audit/relationships/ai/parser.js.map +7 -0
  67. package/dist/audit/relationships/ai/prompts.js +274 -0
  68. package/dist/audit/relationships/ai/prompts.js.map +7 -0
  69. package/dist/audit/relationships/analysis/balance-assessor.js +115 -0
  70. package/dist/audit/relationships/analysis/balance-assessor.js.map +7 -0
  71. package/dist/audit/relationships/analysis/coverage-analyzer.js +99 -0
  72. package/dist/audit/relationships/analysis/coverage-analyzer.js.map +7 -0
  73. package/dist/audit/relationships/analysis/duplicate-detector.js +95 -0
  74. package/dist/audit/relationships/analysis/duplicate-detector.js.map +7 -0
  75. package/dist/audit/relationships/analysis/gap-analyzer.js +99 -0
  76. package/dist/audit/relationships/analysis/gap-analyzer.js.map +7 -0
  77. package/dist/audit/relationships/analysis/layer-templates.js +294 -0
  78. package/dist/audit/relationships/analysis/layer-templates.js.map +7 -0
  79. package/dist/audit/relationships/graph/connectivity.js +129 -0
  80. package/dist/audit/relationships/graph/connectivity.js.map +7 -0
  81. package/dist/audit/relationships/graph/relationship-graph.js +179 -0
  82. package/dist/audit/relationships/graph/relationship-graph.js.map +7 -0
  83. package/dist/audit/relationships/model/balance-assessor.js +49 -0
  84. package/dist/audit/relationships/model/balance-assessor.js.map +7 -0
  85. package/dist/audit/relationships/model/coverage-analyzer.js +34 -0
  86. package/dist/audit/relationships/model/coverage-analyzer.js.map +7 -0
  87. package/dist/audit/relationships/model/duplicate-detector.js +68 -0
  88. package/dist/audit/relationships/model/duplicate-detector.js.map +7 -0
  89. package/dist/audit/relationships/model/gap-analyzer.js +41 -0
  90. package/dist/audit/relationships/model/gap-analyzer.js.map +7 -0
  91. package/dist/audit/relationships/model/orchestrator.js +126 -0
  92. package/dist/audit/relationships/model/orchestrator.js.map +7 -0
  93. package/dist/audit/relationships/spec/orchestrator.js +157 -0
  94. package/dist/audit/relationships/spec/orchestrator.js.map +7 -0
  95. package/dist/audit/reports/index.js +5 -0
  96. package/dist/audit/reports/index.js.map +7 -0
  97. package/dist/audit/reports/report-generator.js +118 -0
  98. package/dist/audit/reports/report-generator.js.map +7 -0
  99. package/dist/audit/snapshot-storage.js +163 -0
  100. package/dist/audit/snapshot-storage.js.map +7 -0
  101. package/dist/audit/types.js +21 -0
  102. package/dist/audit/types.js.map +7 -0
  103. package/dist/cli.js +223 -92
  104. package/dist/cli.js.map +3 -3
  105. package/dist/coding-agents/agents/claude-code.js +147 -0
  106. package/dist/coding-agents/agents/claude-code.js.map +7 -0
  107. package/dist/coding-agents/agents/index.js +5 -0
  108. package/dist/coding-agents/agents/index.js.map +7 -0
  109. package/dist/coding-agents/agents/types.js +1 -0
  110. package/dist/coding-agents/agents/types.js.map +7 -0
  111. package/dist/coding-agents/base-chat-client.js +45 -0
  112. package/dist/coding-agents/base-chat-client.js.map +7 -0
  113. package/dist/coding-agents/chat-utils.js +23 -0
  114. package/dist/coding-agents/chat-utils.js.map +7 -0
  115. package/dist/coding-agents/claude-code-client.js +246 -0
  116. package/dist/coding-agents/claude-code-client.js.map +7 -0
  117. package/dist/coding-agents/copilot-client.js +209 -0
  118. package/dist/coding-agents/copilot-client.js.map +7 -0
  119. package/dist/commands/add.js +95 -33
  120. package/dist/commands/add.js.map +2 -2
  121. package/dist/commands/analyzer.js +511 -0
  122. package/dist/commands/analyzer.js.map +7 -0
  123. package/dist/commands/audit-diff.js +49 -0
  124. package/dist/commands/audit-diff.js.map +7 -0
  125. package/dist/commands/audit-snapshots.js +62 -0
  126. package/dist/commands/audit-snapshots.js.map +7 -0
  127. package/dist/commands/audit.js +81 -0
  128. package/dist/commands/audit.js.map +7 -0
  129. package/dist/commands/catalog.js +248 -0
  130. package/dist/commands/catalog.js.map +7 -0
  131. package/dist/commands/changeset.js +577 -66
  132. package/dist/commands/changeset.js.map +2 -2
  133. package/dist/commands/chat.js +106 -83
  134. package/dist/commands/chat.js.map +2 -2
  135. package/dist/commands/claude.js +74 -0
  136. package/dist/commands/claude.js.map +7 -0
  137. package/dist/commands/conformance.js +71 -68
  138. package/dist/commands/conformance.js.map +2 -2
  139. package/dist/commands/copilot.js +75 -0
  140. package/dist/commands/copilot.js.map +7 -0
  141. package/dist/commands/delete.js +125 -11
  142. package/dist/commands/delete.js.map +3 -3
  143. package/dist/commands/discover-logic.js +44 -0
  144. package/dist/commands/discover-logic.js.map +7 -0
  145. package/dist/commands/docs.js +87 -0
  146. package/dist/commands/docs.js.map +7 -0
  147. package/dist/commands/element.js +14 -20
  148. package/dist/commands/element.js.map +2 -2
  149. package/dist/commands/export.js +13 -3
  150. package/dist/commands/export.js.map +2 -2
  151. package/dist/commands/import.js +51 -0
  152. package/dist/commands/import.js.map +7 -0
  153. package/dist/commands/info.js +30 -24
  154. package/dist/commands/info.js.map +2 -2
  155. package/dist/commands/init.js +27 -11
  156. package/dist/commands/init.js.map +2 -2
  157. package/dist/commands/list.js +63 -13
  158. package/dist/commands/list.js.map +3 -3
  159. package/dist/commands/relationship.js +225 -37
  160. package/dist/commands/relationship.js.map +2 -2
  161. package/dist/commands/report.js +54 -0
  162. package/dist/commands/report.js.map +7 -0
  163. package/dist/commands/reports.js +45 -0
  164. package/dist/commands/reports.js.map +7 -0
  165. package/dist/commands/schema.js +135 -0
  166. package/dist/commands/schema.js.map +7 -0
  167. package/dist/commands/search.js +40 -12
  168. package/dist/commands/search.js.map +2 -2
  169. package/dist/commands/show.js +58 -27
  170. package/dist/commands/show.js.map +2 -2
  171. package/dist/commands/stats.js +35 -0
  172. package/dist/commands/stats.js.map +7 -0
  173. package/dist/commands/trace.js +39 -16
  174. package/dist/commands/trace.js.map +2 -2
  175. package/dist/commands/update.js +141 -14
  176. package/dist/commands/update.js.map +2 -2
  177. package/dist/commands/upgrade.js +210 -46
  178. package/dist/commands/upgrade.js.map +3 -3
  179. package/dist/commands/validate.js +142 -23
  180. package/dist/commands/validate.js.map +3 -3
  181. package/dist/commands/version.js +8 -12
  182. package/dist/commands/version.js.map +2 -2
  183. package/dist/commands/visualize.js +155 -25
  184. package/dist/commands/visualize.js.map +2 -2
  185. package/dist/core/base-snapshot-manager.js +84 -0
  186. package/dist/core/base-snapshot-manager.js.map +7 -0
  187. package/dist/core/changeset-exporter.js +305 -0
  188. package/dist/core/changeset-exporter.js.map +7 -0
  189. package/dist/core/changeset-validator.js +41 -0
  190. package/dist/core/changeset-validator.js.map +7 -0
  191. package/dist/core/changeset.js +88 -173
  192. package/dist/core/changeset.js.map +2 -2
  193. package/dist/core/dependency-tracker.js +4 -2
  194. package/dist/core/dependency-tracker.js.map +2 -2
  195. package/dist/core/element.js +98 -27
  196. package/dist/core/element.js.map +2 -2
  197. package/dist/core/golden-copy-cache.js +290 -0
  198. package/dist/core/golden-copy-cache.js.map +7 -0
  199. package/dist/core/graph-model.js +266 -0
  200. package/dist/core/graph-model.js.map +7 -0
  201. package/dist/core/layer.js +159 -16
  202. package/dist/core/layer.js.map +2 -2
  203. package/dist/core/layers.js +32 -0
  204. package/dist/core/layers.js.map +7 -0
  205. package/dist/core/manifest.js +32 -11
  206. package/dist/core/manifest.js.map +2 -2
  207. package/dist/core/migration-registry.js +56 -1
  208. package/dist/core/migration-registry.js.map +2 -2
  209. package/dist/core/model.js +424 -217
  210. package/dist/core/model.js.map +3 -3
  211. package/dist/core/mutation-handler.js +179 -0
  212. package/dist/core/mutation-handler.js.map +7 -0
  213. package/dist/core/reference-registry.js +29 -21
  214. package/dist/core/reference-registry.js.map +2 -2
  215. package/dist/core/relationship-catalog.js +189 -0
  216. package/dist/core/relationship-catalog.js.map +7 -0
  217. package/dist/core/relationship-registry.js.map +2 -2
  218. package/dist/core/relationships.js.map +1 -1
  219. package/dist/core/report-data-model.js +285 -0
  220. package/dist/core/report-data-model.js.map +7 -0
  221. package/dist/core/spec-data-service.js +185 -0
  222. package/dist/core/spec-data-service.js.map +7 -0
  223. package/dist/core/spec-loader-types.js +1 -0
  224. package/dist/core/spec-loader-types.js.map +7 -0
  225. package/dist/core/spec-loader.js +234 -0
  226. package/dist/core/spec-loader.js.map +7 -0
  227. package/dist/core/staged-changeset-storage.js +282 -0
  228. package/dist/core/staged-changeset-storage.js.map +7 -0
  229. package/dist/core/staging-area.js +1002 -0
  230. package/dist/core/staging-area.js.map +7 -0
  231. package/dist/core/stats-collector.js +169 -0
  232. package/dist/core/stats-collector.js.map +7 -0
  233. package/dist/core/virtual-projection.js +443 -0
  234. package/dist/core/virtual-projection.js.map +7 -0
  235. package/dist/export/archimate-exporter.js +99 -60
  236. package/dist/export/archimate-exporter.js.map +2 -2
  237. package/dist/export/audit-formatters.js +479 -0
  238. package/dist/export/audit-formatters.js.map +7 -0
  239. package/dist/export/diff-formatters.js +99 -0
  240. package/dist/export/diff-formatters.js.map +7 -0
  241. package/dist/export/export-manager.js.map +2 -2
  242. package/dist/export/graphml-exporter.js +44 -27
  243. package/dist/export/graphml-exporter.js.map +2 -2
  244. package/dist/export/index.js +4 -0
  245. package/dist/export/index.js.map +2 -2
  246. package/dist/export/json-schema-exporter.js +55 -42
  247. package/dist/export/json-schema-exporter.js.map +2 -2
  248. package/dist/export/markdown-exporter.js +92 -70
  249. package/dist/export/markdown-exporter.js.map +2 -2
  250. package/dist/export/markdown-generator.js +248 -0
  251. package/dist/export/markdown-generator.js.map +7 -0
  252. package/dist/export/markdown-utils.js +30 -0
  253. package/dist/export/markdown-utils.js.map +7 -0
  254. package/dist/export/mermaid-markdown-exporter.js +37 -0
  255. package/dist/export/mermaid-markdown-exporter.js.map +7 -0
  256. package/dist/export/openapi-exporter.js +119 -71
  257. package/dist/export/openapi-exporter.js.map +2 -2
  258. package/dist/export/plantuml-exporter.js +104 -39
  259. package/dist/export/plantuml-exporter.js.map +2 -2
  260. package/dist/export/report-formatters.js +111 -0
  261. package/dist/export/report-formatters.js.map +7 -0
  262. package/dist/export/stats-formatters.js +98 -0
  263. package/dist/export/stats-formatters.js.map +7 -0
  264. package/dist/export/types.js +2 -14
  265. package/dist/export/types.js.map +2 -2
  266. package/dist/export/verify-formatters.js +153 -0
  267. package/dist/export/verify-formatters.js.map +7 -0
  268. package/dist/generated/compiled-validators.js +17 -0
  269. package/dist/generated/compiled-validators.js.map +7 -0
  270. package/dist/generated/index.js +31 -0
  271. package/dist/generated/index.js.map +7 -0
  272. package/dist/generated/layer-registry.js +190 -0
  273. package/dist/generated/layer-registry.js.map +7 -0
  274. package/dist/generated/layer-types.js +7 -0
  275. package/dist/generated/layer-types.js.map +7 -0
  276. package/dist/generated/node-types.js +1719 -0
  277. package/dist/generated/node-types.js.map +7 -0
  278. package/dist/generated/relationship-index.js +14552 -0
  279. package/dist/generated/relationship-index.js.map +7 -0
  280. package/dist/import/archimate-importer.js +166 -0
  281. package/dist/import/archimate-importer.js.map +7 -0
  282. package/dist/import/index.js +47 -0
  283. package/dist/import/index.js.map +7 -0
  284. package/dist/import/openapi-importer.js +143 -0
  285. package/dist/import/openapi-importer.js.map +7 -0
  286. package/dist/import/types.js +1 -0
  287. package/dist/import/types.js.map +7 -0
  288. package/dist/integrations/base-manager.js +311 -0
  289. package/dist/integrations/base-manager.js.map +7 -0
  290. package/dist/integrations/claude-manager.js +360 -0
  291. package/dist/integrations/claude-manager.js.map +7 -0
  292. package/dist/integrations/claude_code/agents/dr-advisor.md +253 -0
  293. package/dist/integrations/claude_code/agents/dr-architect.md +278 -0
  294. package/dist/integrations/claude_code/agents/dr-extractor.md +286 -0
  295. package/dist/integrations/claude_code/commands/dr-changeset.md +657 -0
  296. package/dist/integrations/claude_code/commands/dr-design.md +373 -0
  297. package/dist/integrations/claude_code/commands/dr-info.md +132 -0
  298. package/dist/integrations/claude_code/commands/dr-init.md +251 -0
  299. package/dist/integrations/claude_code/commands/dr-map.md +936 -0
  300. package/dist/integrations/claude_code/commands/dr-model.md +493 -0
  301. package/dist/integrations/claude_code/commands/dr-relate.md +814 -0
  302. package/dist/integrations/claude_code/commands/dr-sync.md +279 -0
  303. package/dist/integrations/claude_code/commands/dr-validate.md +654 -0
  304. package/dist/integrations/claude_code/commands/dr-verify.md +559 -0
  305. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +903 -0
  306. package/dist/integrations/claude_code/docs/QUICK_START.md +271 -0
  307. package/dist/integrations/claude_code/docs/README.md +347 -0
  308. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +550 -0
  309. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +620 -0
  310. package/dist/integrations/claude_code/skills/dr_03_security_layer/SKILL.md +642 -0
  311. package/dist/integrations/claude_code/skills/dr_04_application_layer/SKILL.md +911 -0
  312. package/dist/integrations/claude_code/skills/dr_05_technology_layer/SKILL.md +836 -0
  313. package/dist/integrations/claude_code/skills/dr_06_api_layer/SKILL.md +933 -0
  314. package/dist/integrations/claude_code/skills/dr_07_data_model_layer/SKILL.md +210 -0
  315. package/dist/integrations/claude_code/skills/dr_08_dataset_layer/SKILL.md +319 -0
  316. package/dist/integrations/claude_code/skills/dr_09_ux_layer/SKILL.md +319 -0
  317. package/dist/integrations/claude_code/skills/dr_10_navigation_layer/SKILL.md +318 -0
  318. package/dist/integrations/claude_code/skills/dr_11_apm_layer/SKILL.md +273 -0
  319. package/dist/integrations/claude_code/skills/dr_12_testing_layer/SKILL.md +273 -0
  320. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +187 -0
  321. package/dist/integrations/claude_code/skills/dr_codebase_memory/SKILL.md +590 -0
  322. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +295 -0
  323. package/dist/integrations/copilot-manager.js +328 -0
  324. package/dist/integrations/copilot-manager.js.map +7 -0
  325. package/dist/integrations/github_copilot/agents/dr-advisor.md +243 -0
  326. package/dist/integrations/github_copilot/agents/dr-architect.md +1567 -0
  327. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +491 -0
  328. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +539 -0
  329. package/dist/integrations/github_copilot/skills/dr_03_security_layer/SKILL.md +667 -0
  330. package/dist/integrations/github_copilot/skills/dr_04_application_layer/SKILL.md +670 -0
  331. package/dist/integrations/github_copilot/skills/dr_05_technology_layer/SKILL.md +660 -0
  332. package/dist/integrations/github_copilot/skills/dr_06_api_layer/SKILL.md +855 -0
  333. package/dist/integrations/github_copilot/skills/dr_07_data_model_layer/SKILL.md +171 -0
  334. package/dist/integrations/github_copilot/skills/dr_08_dataset_layer/SKILL.md +216 -0
  335. package/dist/integrations/github_copilot/skills/dr_09_ux_layer/SKILL.md +230 -0
  336. package/dist/integrations/github_copilot/skills/dr_10_navigation_layer/SKILL.md +228 -0
  337. package/dist/integrations/github_copilot/skills/dr_11_apm_layer/SKILL.md +234 -0
  338. package/dist/integrations/github_copilot/skills/dr_12_testing_layer/SKILL.md +230 -0
  339. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +187 -0
  340. package/dist/integrations/hash-utils.js +51 -0
  341. package/dist/integrations/hash-utils.js.map +7 -0
  342. package/dist/integrations/types.js +16 -0
  343. package/dist/integrations/types.js.map +7 -0
  344. package/dist/reports/model-layer-report-generator.js +278 -0
  345. package/dist/reports/model-layer-report-generator.js.map +7 -0
  346. package/dist/reports/model-report-data.js +57 -0
  347. package/dist/reports/model-report-data.js.map +7 -0
  348. package/dist/reports/model-report-orchestrator.js +103 -0
  349. package/dist/reports/model-report-orchestrator.js.map +7 -0
  350. package/dist/schemas/bundled/analyzers/cbm.json +595 -0
  351. package/dist/schemas/bundled/analyzers/manifest.json +9 -0
  352. package/dist/schemas/bundled/api.json +3411 -0
  353. package/dist/schemas/bundled/apm.json +2371 -0
  354. package/dist/schemas/bundled/application.json +1253 -0
  355. package/dist/schemas/bundled/base.json +1622 -0
  356. package/dist/schemas/bundled/business.json +1279 -0
  357. package/dist/schemas/bundled/data-model.json +1298 -0
  358. package/dist/schemas/bundled/data-store.json +2025 -0
  359. package/dist/schemas/bundled/manifest.json +93 -0
  360. package/dist/schemas/bundled/motivation.json +1360 -0
  361. package/dist/schemas/bundled/navigation.json +1874 -0
  362. package/dist/schemas/bundled/security.json +3488 -0
  363. package/dist/schemas/bundled/technology.json +2067 -0
  364. package/dist/schemas/bundled/testing.json +3120 -0
  365. package/dist/schemas/bundled/ux.json +3592 -0
  366. package/dist/server/schemas.js +202 -0
  367. package/dist/server/schemas.js.map +7 -0
  368. package/dist/server/server.js +1336 -252
  369. package/dist/server/server.js.map +3 -3
  370. package/dist/server/telemetry-middleware.js +22 -15
  371. package/dist/server/telemetry-middleware.js.map +2 -2
  372. package/dist/server-entry.js +65 -0
  373. package/dist/server-entry.js.map +7 -0
  374. package/dist/telemetry/config.js +89 -0
  375. package/dist/telemetry/config.js.map +7 -0
  376. package/dist/telemetry/console-interceptor.js.map +1 -1
  377. package/dist/telemetry/detect.js +24 -0
  378. package/dist/telemetry/detect.js.map +7 -0
  379. package/dist/telemetry/fetch-otlp-exporter.js +112 -0
  380. package/dist/telemetry/fetch-otlp-exporter.js.map +7 -0
  381. package/dist/telemetry/index.js +153 -3
  382. package/dist/telemetry/index.js.map +2 -2
  383. package/dist/telemetry/resilient-exporter.js +22 -8
  384. package/dist/telemetry/resilient-exporter.js.map +2 -2
  385. package/dist/telemetry/resilient-log-exporter.js +17 -8
  386. package/dist/telemetry/resilient-log-exporter.js.map +2 -2
  387. package/dist/telemetry/test-instrumentation.js.map +2 -2
  388. package/dist/types/index.js +84 -0
  389. package/dist/types/index.js.map +3 -3
  390. package/dist/types/source-reference.js +1 -0
  391. package/dist/types/source-reference.js.map +7 -0
  392. package/dist/utils/chat-logger.js +292 -0
  393. package/dist/utils/chat-logger.js.map +7 -0
  394. package/dist/utils/claude-stream.js +8 -0
  395. package/dist/utils/claude-stream.js.map +7 -0
  396. package/dist/utils/date-utils.js +7 -0
  397. package/dist/utils/date-utils.js.map +7 -0
  398. package/dist/utils/element-utils.js.map +1 -1
  399. package/dist/utils/errors.js +88 -15
  400. package/dist/utils/errors.js.map +3 -3
  401. package/dist/utils/export-python-annotations.js +82 -0
  402. package/dist/utils/export-python-annotations.js.map +7 -0
  403. package/dist/utils/file-io.js +7 -2
  404. package/dist/utils/file-io.js.map +2 -2
  405. package/dist/utils/file-lock.js +156 -0
  406. package/dist/utils/file-lock.js.map +7 -0
  407. package/dist/utils/id-generator.js +18 -0
  408. package/dist/utils/id-generator.js.map +7 -0
  409. package/dist/utils/layer-name-formatter.js +16 -0
  410. package/dist/utils/layer-name-formatter.js.map +7 -0
  411. package/dist/utils/markdown-anchor.js +7 -0
  412. package/dist/utils/markdown-anchor.js.map +7 -0
  413. package/dist/utils/markdown-link.js +12 -0
  414. package/dist/utils/markdown-link.js.map +7 -0
  415. package/dist/utils/markdown-table.js +17 -0
  416. package/dist/utils/markdown-table.js.map +7 -0
  417. package/dist/utils/mermaid-utils.js +11 -0
  418. package/dist/utils/mermaid-utils.js.map +7 -0
  419. package/dist/utils/project-paths.js.map +2 -2
  420. package/dist/utils/source-reference.js +52 -0
  421. package/dist/utils/source-reference.js.map +7 -0
  422. package/dist/utils/spec-installer.js +40 -53
  423. package/dist/utils/spec-installer.js.map +2 -2
  424. package/dist/utils/spec-version.js +5 -1
  425. package/dist/utils/spec-version.js.map +2 -2
  426. package/dist/utils/string-utils.js +12 -0
  427. package/dist/utils/string-utils.js.map +7 -0
  428. package/dist/utils/table-formatting.js +19 -0
  429. package/dist/utils/table-formatting.js.map +7 -0
  430. package/dist/validators/index.js +4 -0
  431. package/dist/validators/index.js.map +2 -2
  432. package/dist/validators/naming-validator.js +20 -23
  433. package/dist/validators/naming-validator.js.map +2 -2
  434. package/dist/validators/reference-validator.js +23 -38
  435. package/dist/validators/reference-validator.js.map +2 -2
  436. package/dist/validators/relationship-schema-validator.js +243 -0
  437. package/dist/validators/relationship-schema-validator.js.map +7 -0
  438. package/dist/validators/schema-validator.js +168 -68
  439. package/dist/validators/schema-validator.js.map +2 -2
  440. package/dist/validators/semantic-validator.js +36 -60
  441. package/dist/validators/semantic-validator.js.map +2 -2
  442. package/dist/validators/types.js.map +2 -2
  443. package/dist/validators/validation-formatter.js +159 -0
  444. package/dist/validators/validation-formatter.js.map +7 -0
  445. package/dist/validators/validator.js +24 -38
  446. package/dist/validators/validator.js.map +2 -2
  447. package/dist/viewer/README.md +139 -0
  448. package/dist/viewer/assets/main-CY0Xufuj.css +1 -0
  449. package/dist/viewer/assets/main-DtgErnwF.js +81 -0
  450. package/dist/viewer/assets/vendor-CxKvDp2x.js +17 -0
  451. package/dist/viewer/favicon.svg +11 -0
  452. package/dist/viewer/fonts/inter/Inter-Black.woff2 +0 -0
  453. package/dist/viewer/fonts/inter/Inter-Bold.woff2 +0 -0
  454. package/dist/viewer/fonts/inter/Inter-ExtraBold.woff2 +0 -0
  455. package/dist/viewer/fonts/inter/Inter-Light.woff2 +0 -0
  456. package/dist/viewer/fonts/inter/Inter-Medium.woff2 +0 -0
  457. package/dist/viewer/fonts/inter/Inter-Regular.woff2 +0 -0
  458. package/dist/viewer/fonts/inter/Inter-SemiBold.woff2 +0 -0
  459. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2 +0 -0
  460. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  461. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2 +0 -0
  462. package/dist/viewer/index.html +15 -0
  463. package/dist/viewer/manifest.json +99 -0
  464. package/dist/viewer/version.json +1 -0
  465. package/package.json +37 -13
  466. package/PYTHON_CLI_PARITY.md +0 -265
  467. package/PYTHON_CLI_STRUCTURE_UPDATE.md +0 -246
  468. package/biome.json +0 -42
  469. package/bunfig.toml +0 -3
  470. package/dist/ai/claude-client.js +0 -71
  471. package/dist/ai/claude-client.js.map +0 -7
  472. package/dist/ai/context-provider.js +0 -129
  473. package/dist/ai/context-provider.js.map +0 -7
  474. package/dist/ai/tools.js +0 -231
  475. package/dist/ai/tools.js.map +0 -7
  476. package/dist/commands/migrate.js +0 -36
  477. package/dist/commands/migrate.js.map +0 -7
  478. package/dist/commands/project.js +0 -74
  479. package/dist/commands/project.js.map +0 -7
  480. package/dist/core/active-changeset.js +0 -52
  481. package/dist/core/active-changeset.js.map +0 -7
  482. package/dist/core/projection-engine.js +0 -288
  483. package/dist/core/projection-engine.js.map +0 -7
  484. package/dist/schemas/bundled/01-motivation-layer.schema.json +0 -942
  485. package/dist/schemas/bundled/02-business-layer.schema.json +0 -1293
  486. package/dist/schemas/bundled/03-security-layer.schema.json +0 -2177
  487. package/dist/schemas/bundled/04-application-layer.schema.json +0 -1328
  488. package/dist/schemas/bundled/05-technology-layer.schema.json +0 -1465
  489. package/dist/schemas/bundled/06-api-layer.schema.json +0 -1743
  490. package/dist/schemas/bundled/07-data-model-layer.schema.json +0 -1253
  491. package/dist/schemas/bundled/08-datastore-layer.schema.json +0 -892
  492. package/dist/schemas/bundled/09-ux-layer.schema.json +0 -1506
  493. package/dist/schemas/bundled/10-navigation-layer.schema.json +0 -843
  494. package/dist/schemas/bundled/11-apm-observability-layer.schema.json +0 -1430
  495. package/dist/schemas/bundled/12-testing-layer.schema.json +0 -1091
  496. package/dist/schemas/bundled/common/layer-extensions.schema.json +0 -266
  497. package/dist/schemas/bundled/common/predicates.schema.json +0 -493
  498. package/dist/schemas/bundled/common/relationships.schema.json +0 -791
  499. package/dist/schemas/bundled/common/source-references.schema.json +0 -71
  500. package/dist/schemas/bundled/link-registry.json +0 -1839
  501. package/dist/schemas/bundled/link-registry.schema.json +0 -339
  502. package/dist/schemas/bundled/relationship-catalog.json +0 -1302
  503. package/dist/schemas/bundled/relationship-type.schema.json +0 -221
  504. package/dist/utils/model-path.js +0 -22
  505. package/dist/utils/model-path.js.map +0 -7
  506. package/docs/TEST-INSTRUMENTATION.md +0 -405
  507. package/docs/telemetry.md +0 -427
  508. package/test-debug.js +0 -33
  509. package/test-special.mjs +0 -46
  510. package/test_runner.ts +0 -7
@@ -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\nimport Ajv, { ValidateFunction, ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport { ValidationResult } from './types.js';\nimport type { Layer } from '../core/layer.js';\nimport { fileURLToPath } from 'url';\nimport path from 'path';\nimport { readFile } from '../utils/file-io.js';\nimport { startSpan, endSpan } from '../telemetry/index.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 */\nexport class SchemaValidator {\n private ajv: Ajv;\n private compiledSchemas: Map<string, ValidateFunction> = new Map();\n private schemasLoaded: boolean = false;\n\n constructor() {\n this.ajv = new Ajv({\n allErrors: true,\n strict: false,\n validateFormats: true,\n });\n addFormats(this.ajv);\n // Note: schemas are loaded lazily on first validation\n }\n\n /**\n * Ensure schemas are precompiled before validation\n */\n private async ensureSchemasLoaded(): Promise<void> {\n if (this.schemasLoaded) {\n return;\n }\n\n await this.precompileSchemas();\n this.schemasLoaded = true;\n }\n\n /**\n * Precompile all layer schemas for efficient validation\n */\n private async precompileSchemas(): Promise<void> {\n const layerMappings = {\n motivation: '01-motivation-layer.schema.json',\n business: '02-business-layer.schema.json',\n security: '03-security-layer.schema.json',\n application: '04-application-layer.schema.json',\n technology: '05-technology-layer.schema.json',\n api: '06-api-layer.schema.json',\n 'data-model': '07-data-model-layer.schema.json',\n 'data-store': '08-datastore-layer.schema.json',\n ux: '09-ux-layer.schema.json',\n navigation: '10-navigation-layer.schema.json',\n apm: '11-apm-observability-layer.schema.json',\n testing: '12-testing-layer.schema.json',\n };\n\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const schemasDir = path.join(__dirname, '..', 'schemas', 'bundled');\n\n // First, register all common schemas that other schemas reference\n const commonSchemas = [\n 'common/source-references.schema.json',\n 'common/predicates.schema.json',\n 'common/relationships.schema.json',\n 'common/layer-extensions.schema.json',\n ];\n\n for (const commonSchemaFile of commonSchemas) {\n try {\n const schemaPath = path.join(schemasDir, commonSchemaFile);\n const schemaContent = await readFile(schemaPath);\n const schema = JSON.parse(schemaContent);\n this.ajv.addSchema(schema);\n } catch (error) {\n // Common schemas not all layers may reference - warn but continue\n console.warn(\n `Warning: Failed to load common schema ${commonSchemaFile}:`,\n error\n );\n }\n }\n\n for (const [layerName, schemaFileName] of Object.entries(layerMappings)) {\n try {\n // Try primary schema path first (development)\n let schemaPath = path.join(schemasDir, schemaFileName);\n\n let schemaContent: string;\n try {\n schemaContent = await readFile(schemaPath);\n } catch {\n // Fallback to alternative path for bundled deployments\n schemaPath = path.join(\n __dirname,\n 'schemas',\n 'bundled',\n schemaFileName\n );\n schemaContent = await readFile(schemaPath);\n }\n\n const schema = JSON.parse(schemaContent);\n const validate = this.ajv.compile(schema);\n this.compiledSchemas.set(layerName, validate);\n } catch (error) {\n console.warn(`Failed to load schema for layer ${layerName}:`, error);\n }\n }\n }\n\n /**\n * Validate a layer against its schema\n */\n async validateLayer(layer: Layer): Promise<ValidationResult> {\n const span = isTelemetryEnabled ? startSpan('schema.validate', {\n 'schema.layer': layer.name,\n }) : null;\n\n try {\n const result = new ValidationResult();\n\n // Ensure schemas are loaded\n await this.ensureSchemasLoaded();\n\n const validate = this.compiledSchemas.get(layer.name);\n\n if (!validate) {\n // Schema not found - skip validation silently (matching Python CLI behavior)\n // Some layers may not have schemas yet, which is acceptable\n // Don't add warning - just return success\n if (isTelemetryEnabled && span) {\n span.setAttribute('schema.valid', true);\n span.setAttribute('schema.skipped', true);\n }\n\n return result;\n }\n\n const layerData = layer.toJSON();\n const valid = validate(layerData);\n\n if (!valid && validate.errors) {\n for (const error of validate.errors) {\n result.addError({\n layer: layer.name,\n message: `Schema validation failed: ${this.formatAjvError(error)}`,\n location: error.instancePath || '/',\n fixSuggestion: this.generateFixSuggestion(error),\n });\n }\n }\n\n if (isTelemetryEnabled && span) {\n span.setAttribute('schema.valid', valid);\n span.setAttribute('schema.error_count', validate.errors?.length || 0);\n }\n\n return result;\n } finally {\n endSpan(span);\n }\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 ${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 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 default:\n return undefined;\n }\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\";\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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",
6
6
  "names": []
7
7
  }
@@ -1,77 +1,53 @@
1
1
  import { ValidationResult } from "./types.js";
2
- import { fileURLToPath } from "url";
3
- import path from "path";
2
+ import { RELATIONSHIPS_BY_PREDICATE } from "../generated/relationship-index.js";
4
3
  class SemanticValidator {
5
- relationshipCatalog = null;
6
- catalogLoaded = !1;
7
- constructor() {
8
- }
9
- /**
10
- * Ensure relationship catalog is loaded
11
- */
12
- async ensureCatalogLoaded() {
13
- this.catalogLoaded || (await this.loadRelationshipCatalog(), this.catalogLoaded = !0);
14
- }
15
- /**
16
- * Load the relationship catalog for predicate validation
17
- */
18
- async loadRelationshipCatalog() {
19
- try {
20
- const __dirname = path.dirname(fileURLToPath(import.meta.url)), catalogPath = path.join(__dirname, "..", "schemas", "bundled", "relationship-catalog.json");
21
- this.relationshipCatalog = await import(catalogPath, { assert: { type: "json" } }).then((m) => m.default);
22
- } catch {
23
- this.relationshipCatalog = null;
24
- }
25
- }
26
4
  /**
27
5
  * Validate semantic constraints of a model
28
6
  */
29
7
  async validateModel(model) {
30
8
  const result = new ValidationResult();
31
- return await this.ensureCatalogLoaded(), this.validateUniqueIds(model, result), this.validateRelationshipPredicates(model, result), result;
9
+ return this.validateRelationshipPredicates(model, result), this.validateApmInferredWithoutSdk(model, result), result;
32
10
  }
33
11
  /**
34
- * Validate that all element IDs are unique across layers
12
+ * Rule: apm_inferred_without_sdk
13
+ *
14
+ * When the APM layer contains MetricInstrument, Span, TraceConfiguration, or
15
+ * ExporterConfig elements and ALL of them have source_reference.provenance = "inferred",
16
+ * emit a WARNING suggesting OTel SDK verification.
17
+ *
18
+ * Rationale: The APM skill's checklist can cause Claude to add instrumentation
19
+ * elements without verifying that an OTel SDK is present. This validator surfaces
20
+ * the accuracy concern on every subsequent `dr validate` run.
35
21
  */
36
- validateUniqueIds(model, result) {
37
- const seenIds = /* @__PURE__ */ new Map();
38
- for (const [layerName, layer] of model.layers)
39
- for (const element of layer.listElements())
40
- seenIds.has(element.id) ? result.addError({
41
- layer: layerName,
42
- elementId: element.id,
43
- message: `Duplicate element ID: '${element.id}' already exists in layer '${seenIds.get(element.id)}'`,
44
- fixSuggestion: "Element IDs must be unique across all layers"
45
- }) : seenIds.set(element.id, layerName);
22
+ validateApmInferredWithoutSdk(model, result) {
23
+ const apmLayer = model.layers.get("apm");
24
+ if (!apmLayer)
25
+ return;
26
+ const instrumentationTypes = /* @__PURE__ */ new Set(["metricinstrument", "span", "traceconfiguration", "exporterconfig"]), instrumentationElements = apmLayer.listElements().filter((e) => instrumentationTypes.has(e.type));
27
+ if (instrumentationElements.length === 0)
28
+ return;
29
+ instrumentationElements.every((e) => e.source_reference?.provenance === "inferred") && result.addWarning({
30
+ layer: "apm",
31
+ elementId: "apm.*",
32
+ message: `All ${instrumentationElements.length} APM instrumentation element(s) have provenance: inferred. If no @opentelemetry SDK exists in package.json, these elements are aspirational rather than implemented. Consider marking them as such in their descriptions.`,
33
+ fixSuggestion: "Run: grep '@opentelemetry' package.json \u2014 if absent, update element descriptions to note that instrumentation is aspirational and no OTel export is implemented."
34
+ });
46
35
  }
47
36
  /**
48
- * Validate relationship predicates against the catalog
37
+ * Validate relationship predicates against the generated spec relationship index.
38
+ * RELATIONSHIPS_BY_PREDICATE is compiled from all spec relationship schema files,
39
+ * so it is always consistent with the spec without manual synchronization.
49
40
  */
50
41
  validateRelationshipPredicates(model, result) {
51
- if (this.relationshipCatalog)
52
- for (const [layerName, layer] of model.layers) {
53
- const allowedPredicates = this.getAllowedPredicates(layerName);
54
- for (const element of layer.listElements())
55
- for (const rel of element.relationships || [])
56
- allowedPredicates.includes(rel.predicate) || result.addWarning({
57
- layer: layerName,
58
- elementId: element.id,
59
- message: `Unknown relationship predicate '${rel.predicate}' for layer ${layerName}`,
60
- fixSuggestion: allowedPredicates.length > 0 ? `Use one of: ${allowedPredicates.join(", ")}` : "Check relationship catalog for valid predicates"
61
- });
62
- }
63
- }
64
- /**
65
- * Get allowed predicates for a layer from the catalog
66
- */
67
- getAllowedPredicates(layerName) {
68
- if (!this.relationshipCatalog)
69
- return [];
70
- const layerCatalog = this.relationshipCatalog.layers?.[layerName];
71
- if (!layerCatalog)
72
- return [];
73
- const predicates = layerCatalog.predicates || layerCatalog.relationships || [];
74
- return Array.isArray(predicates) ? predicates : Object.keys(predicates || {});
42
+ for (const [layerName, layer] of model.layers)
43
+ for (const element of layer.listElements())
44
+ for (const rel of element.relationships || [])
45
+ RELATIONSHIPS_BY_PREDICATE.has(rel.predicate) || result.addWarning({
46
+ layer: layerName,
47
+ elementId: element.path || element.id,
48
+ message: `Relationship predicate '${rel.predicate}' is not defined in any spec relationship schema`,
49
+ fixSuggestion: `Run 'dr catalog types' to list valid predicates, or 'dr catalog search ${rel.predicate}' to find the correct predicate`
50
+ });
75
51
  }
76
52
  }
77
53
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/validators/semantic-validator.ts"],
4
- "sourcesContent": ["/**\n * Semantic validation for business rules\n */\n\nimport { ValidationResult } from './types.js';\nimport type { Model } from '../core/model.js';\nimport { fileURLToPath } from 'url';\nimport path from 'path';\n\n/**\n * Validator for semantic/business rule validation\n */\nexport class SemanticValidator {\n private relationshipCatalog: Record<string, any> | null = null;\n private catalogLoaded: boolean = false;\n\n constructor() {\n // Load catalog asynchronously on first use\n }\n\n /**\n * Ensure relationship catalog is loaded\n */\n private async ensureCatalogLoaded(): Promise<void> {\n if (this.catalogLoaded) {\n return;\n }\n\n await this.loadRelationshipCatalog();\n this.catalogLoaded = true;\n }\n\n /**\n * Load the relationship catalog for predicate validation\n */\n private async loadRelationshipCatalog(): Promise<void> {\n try {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const catalogPath = path.join(__dirname, '..', 'schemas', 'bundled', 'relationship-catalog.json');\n this.relationshipCatalog = await import(catalogPath, { assert: { type: 'json' } }).then(\n (m) => m.default\n );\n } catch {\n // Catalog load failure is gracefully handled - validation continues without predicate checking\n this.relationshipCatalog = null;\n }\n }\n\n /**\n * Validate semantic constraints of a model\n */\n async validateModel(model: Model): Promise<ValidationResult> {\n const result = new ValidationResult();\n\n await this.ensureCatalogLoaded();\n\n this.validateUniqueIds(model, result);\n this.validateRelationshipPredicates(model, result);\n\n return result;\n }\n\n /**\n * Validate that all element IDs are unique across layers\n */\n private validateUniqueIds(model: Model, result: ValidationResult): void {\n const seenIds = new Map<string, string>(); // id -> layer name\n\n for (const [layerName, layer] of model.layers) {\n for (const element of layer.listElements()) {\n if (seenIds.has(element.id)) {\n result.addError({\n layer: layerName,\n elementId: element.id,\n message: `Duplicate element ID: '${element.id}' already exists in layer '${seenIds.get(element.id)}'`,\n fixSuggestion: 'Element IDs must be unique across all layers',\n });\n } else {\n seenIds.set(element.id, layerName);\n }\n }\n }\n }\n\n /**\n * Validate relationship predicates against the catalog\n */\n private validateRelationshipPredicates(model: Model, result: ValidationResult): void {\n if (!this.relationshipCatalog) {\n // If we can't load the catalog, we can't validate predicates\n return;\n }\n\n for (const [layerName, layer] of model.layers) {\n const allowedPredicates = this.getAllowedPredicates(layerName);\n\n for (const element of layer.listElements()) {\n for (const rel of element.relationships || []) {\n if (!allowedPredicates.includes(rel.predicate)) {\n result.addWarning({\n layer: layerName,\n elementId: element.id,\n message: `Unknown relationship predicate '${rel.predicate}' for layer ${layerName}`,\n fixSuggestion:\n allowedPredicates.length > 0\n ? `Use one of: ${allowedPredicates.join(', ')}`\n : 'Check relationship catalog for valid predicates',\n });\n }\n }\n }\n }\n }\n\n /**\n * Get allowed predicates for a layer from the catalog\n */\n private getAllowedPredicates(layerName: string): string[] {\n if (!this.relationshipCatalog) {\n return [];\n }\n\n const layerCatalog = this.relationshipCatalog.layers?.[layerName];\n if (!layerCatalog) {\n return [];\n }\n\n const predicates = layerCatalog.predicates || layerCatalog.relationships || [];\n return Array.isArray(predicates) ? predicates : Object.keys(predicates || {});\n }\n}\n"],
5
- "mappings": "AAIA,SAAS,wBAAwB;AAEjC,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAKX,MAAO,kBAAiB;EACpB,sBAAkD;EAClD,gBAAyB;EAEjC,cAAA;EAEA;;;;EAKQ,MAAM,sBAAmB;AAC/B,IAAI,KAAK,kBAIT,MAAM,KAAK,wBAAuB,GAClC,KAAK,gBAAgB;EACvB;;;;EAKQ,MAAM,0BAAuB;AACnC,QAAI;AACF,YAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GACvD,cAAc,KAAK,KAAK,WAAW,MAAM,WAAW,WAAW,2BAA2B;AAChG,WAAK,sBAAsB,MAAM,OAAO,aAAa,EAAE,QAAQ,EAAE,MAAM,OAAM,EAAE,GAAI,KACjF,CAAC,MAAM,EAAE,OAAO;IAEpB,QAAQ;AAEN,WAAK,sBAAsB;IAC7B;EACF;;;;EAKA,MAAM,cAAc,OAAY;AAC9B,UAAM,SAAS,IAAI,iBAAgB;AAEnC,iBAAM,KAAK,oBAAmB,GAE9B,KAAK,kBAAkB,OAAO,MAAM,GACpC,KAAK,+BAA+B,OAAO,MAAM,GAE1C;EACT;;;;EAKQ,kBAAkB,OAAc,QAAwB;AAC9D,UAAM,UAAU,oBAAI,IAAG;AAEvB,eAAW,CAAC,WAAW,KAAK,KAAK,MAAM;AACrC,iBAAW,WAAW,MAAM,aAAY;AACtC,QAAI,QAAQ,IAAI,QAAQ,EAAE,IACxB,OAAO,SAAS;UACd,OAAO;UACP,WAAW,QAAQ;UACnB,SAAS,0BAA0B,QAAQ,EAAE,8BAA8B,QAAQ,IAAI,QAAQ,EAAE,CAAC;UAClG,eAAe;SAChB,IAED,QAAQ,IAAI,QAAQ,IAAI,SAAS;EAIzC;;;;EAKQ,+BAA+B,OAAc,QAAwB;AAC3E,QAAK,KAAK;AAKV,iBAAW,CAAC,WAAW,KAAK,KAAK,MAAM,QAAQ;AAC7C,cAAM,oBAAoB,KAAK,qBAAqB,SAAS;AAE7D,mBAAW,WAAW,MAAM,aAAY;AACtC,qBAAW,OAAO,QAAQ,iBAAiB,CAAA;AACzC,YAAK,kBAAkB,SAAS,IAAI,SAAS,KAC3C,OAAO,WAAW;cAChB,OAAO;cACP,WAAW,QAAQ;cACnB,SAAS,mCAAmC,IAAI,SAAS,eAAe,SAAS;cACjF,eACE,kBAAkB,SAAS,IACvB,eAAe,kBAAkB,KAAK,IAAI,CAAC,KAC3C;aACP;MAIT;EACF;;;;EAKQ,qBAAqB,WAAiB;AAC5C,QAAI,CAAC,KAAK;AACR,aAAO,CAAA;AAGT,UAAM,eAAe,KAAK,oBAAoB,SAAS,SAAS;AAChE,QAAI,CAAC;AACH,aAAO,CAAA;AAGT,UAAM,aAAa,aAAa,cAAc,aAAa,iBAAiB,CAAA;AAC5E,WAAO,MAAM,QAAQ,UAAU,IAAI,aAAa,OAAO,KAAK,cAAc,CAAA,CAAE;EAC9E;;",
4
+ "sourcesContent": ["/**\n * Semantic validation for business rules\n */\n\nimport { ValidationResult } from \"./types.js\";\nimport type { Model } from \"../core/model.js\";\nimport { RELATIONSHIPS_BY_PREDICATE } from \"../generated/relationship-index.js\";\n\n/**\n * Validator for semantic/business rule validation\n *\n * Validates model conformance to architectural business rules including:\n * - Relationship predicate validation against defined spec relationship schemas\n * - Cross-layer reference constraints\n * - Layer-specific validation rules\n */\nexport class SemanticValidator {\n /**\n * Validate semantic constraints of a model\n */\n async validateModel(model: Model): Promise<ValidationResult> {\n const result = new ValidationResult();\n\n this.validateRelationshipPredicates(model, result);\n this.validateApmInferredWithoutSdk(model, result);\n\n return result;\n }\n\n /**\n * Rule: apm_inferred_without_sdk\n *\n * When the APM layer contains MetricInstrument, Span, TraceConfiguration, or\n * ExporterConfig elements and ALL of them have source_reference.provenance = \"inferred\",\n * emit a WARNING suggesting OTel SDK verification.\n *\n * Rationale: The APM skill's checklist can cause Claude to add instrumentation\n * elements without verifying that an OTel SDK is present. This validator surfaces\n * the accuracy concern on every subsequent `dr validate` run.\n */\n private validateApmInferredWithoutSdk(model: Model, result: ValidationResult): void {\n const apmLayer = model.layers.get(\"apm\");\n if (!apmLayer) {\n return;\n }\n\n const instrumentationTypes = new Set([\"metricinstrument\", \"span\", \"traceconfiguration\", \"exporterconfig\"]);\n const instrumentationElements = apmLayer.listElements().filter((e) =>\n instrumentationTypes.has(e.type)\n );\n\n if (instrumentationElements.length === 0) {\n return;\n }\n\n const allInferred = instrumentationElements.every(\n (e) => e.source_reference?.provenance === \"inferred\"\n );\n\n if (allInferred) {\n result.addWarning({\n layer: \"apm\",\n elementId: \"apm.*\",\n message:\n `All ${instrumentationElements.length} APM instrumentation element(s) have provenance: inferred. ` +\n \"If no @opentelemetry SDK exists in package.json, these elements are aspirational rather than \" +\n \"implemented. Consider marking them as such in their descriptions.\",\n fixSuggestion:\n \"Run: grep '@opentelemetry' package.json \u2014 if absent, update element descriptions to note \" +\n \"that instrumentation is aspirational and no OTel export is implemented.\",\n });\n }\n }\n\n /**\n * Validate relationship predicates against the generated spec relationship index.\n * RELATIONSHIPS_BY_PREDICATE is compiled from all spec relationship schema files,\n * so it is always consistent with the spec without manual synchronization.\n */\n private validateRelationshipPredicates(model: Model, result: ValidationResult): void {\n for (const [layerName, layer] of model.layers) {\n for (const element of layer.listElements()) {\n for (const rel of element.relationships || []) {\n if (!RELATIONSHIPS_BY_PREDICATE.has(rel.predicate)) {\n result.addWarning({\n layer: layerName,\n elementId: element.path || element.id,\n message: `Relationship predicate '${rel.predicate}' is not defined in any spec relationship schema`,\n fixSuggestion: `Run 'dr catalog types' to list valid predicates, or 'dr catalog search ${rel.predicate}' to find the correct predicate`,\n });\n }\n }\n }\n }\n }\n}\n"],
5
+ "mappings": "AAIA,SAAS,wBAAwB;AAEjC,SAAS,kCAAkC;AAUrC,MAAO,kBAAiB;;;;EAI5B,MAAM,cAAc,OAAY;AAC9B,UAAM,SAAS,IAAI,iBAAgB;AAEnC,gBAAK,+BAA+B,OAAO,MAAM,GACjD,KAAK,8BAA8B,OAAO,MAAM,GAEzC;EACT;;;;;;;;;;;;EAaQ,8BAA8B,OAAc,QAAwB;AAC1E,UAAM,WAAW,MAAM,OAAO,IAAI,KAAK;AACvC,QAAI,CAAC;AACH;AAGF,UAAM,uBAAuB,oBAAI,IAAI,CAAC,oBAAoB,QAAQ,sBAAsB,gBAAgB,CAAC,GACnG,0BAA0B,SAAS,aAAY,EAAG,OAAO,CAAC,MAC9D,qBAAqB,IAAI,EAAE,IAAI,CAAC;AAGlC,QAAI,wBAAwB,WAAW;AACrC;AAOF,IAJoB,wBAAwB,MAC1C,CAAC,MAAM,EAAE,kBAAkB,eAAe,UAAU,KAIpD,OAAO,WAAW;MAChB,OAAO;MACP,WAAW;MACX,SACE,OAAO,wBAAwB,MAAM;MAGvC,eACE;KAEH;EAEL;;;;;;EAOQ,+BAA+B,OAAc,QAAwB;AAC3E,eAAW,CAAC,WAAW,KAAK,KAAK,MAAM;AACrC,iBAAW,WAAW,MAAM,aAAY;AACtC,mBAAW,OAAO,QAAQ,iBAAiB,CAAA;AACzC,UAAK,2BAA2B,IAAI,IAAI,SAAS,KAC/C,OAAO,WAAW;YAChB,OAAO;YACP,WAAW,QAAQ,QAAQ,QAAQ;YACnC,SAAS,2BAA2B,IAAI,SAAS;YACjD,eAAe,0EAA0E,IAAI,SAAS;WACvG;EAKX;;",
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 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
- "mappings": "AAmBM,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;;",
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"],
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
  }
@@ -0,0 +1,159 @@
1
+ import ansis from "ansis";
2
+ import { formatLayerNameWithSuffix } from "../utils/layer-name-formatter.js";
3
+ class ValidationFormatter {
4
+ /**
5
+ * Format validation result for display
6
+ */
7
+ static format(result, model, options = {}) {
8
+ return options.quiet ? this.formatQuiet(result) : options.verbose ? this.formatVerbose(result, model) : this.formatStandard(result, model);
9
+ }
10
+ /**
11
+ * Format quiet output (minimal)
12
+ */
13
+ static formatQuiet(result) {
14
+ if (result.isValid() && result.warnings.length === 0)
15
+ return ansis.green("\u2713 Validation passed");
16
+ const lines = [];
17
+ return result.errors.length > 0 && lines.push(ansis.red(`\u2717 ${result.errors.length} error(s)`)), result.warnings.length > 0 && lines.push(ansis.yellow(`\u26A0 ${result.warnings.length} warning(s)`)), lines.join(" ");
18
+ }
19
+ /**
20
+ * Format standard output (enhanced with stats)
21
+ */
22
+ static formatStandard(result, model) {
23
+ const lines = [];
24
+ lines.push(""), lines.push(ansis.bold("Validating Documentation Robotics Model")), lines.push(ansis.bold("========================================")), lines.push(""), lines.push(ansis.bold("Schema Validation:"));
25
+ const layerStats = this.getLayerStats(model);
26
+ for (const [layerName, count] of Object.entries(layerStats))
27
+ if (result.errors.some((e) => e.layer === layerName)) {
28
+ const errorCount = result.errors.filter((e) => e.layer === layerName).length;
29
+ lines.push(`${ansis.red("\u2717")} ${formatLayerNameWithSuffix(layerName)} (${count} elements) - ${errorCount} error(s)`);
30
+ } else
31
+ lines.push(`${ansis.green("\u2713")} ${formatLayerNameWithSuffix(layerName)} (${count} elements)`);
32
+ lines.push(""), lines.push(ansis.bold("Cross-Layer Validation:"));
33
+ const stats = this.calculateStats(model);
34
+ lines.push(`${ansis.green("\u2713")} ${stats.totalRelationships} relationships validated`), lines.push(""), result.errors.length > 0 && (lines.push(ansis.bold("Errors:")), lines.push(""), result.errors.forEach((error, index) => {
35
+ lines.push(`${index + 1}. ${error.message}`), error.location && lines.push(` ${ansis.dim(`File: ${error.location}`)}`), error.elementId && lines.push(` ${ansis.dim(`Element: ${error.elementId}`)}`), error.fixSuggestion && lines.push(` ${ansis.dim(`\u2192 Suggestion: ${error.fixSuggestion}`)}`), lines.push("");
36
+ }));
37
+ const schemaWarnings = result.warnings.filter((w) => w.category !== "orphan"), orphanWarnings = result.warnings.filter((w) => w.category === "orphan");
38
+ return schemaWarnings.length > 0 && (lines.push(ansis.bold("Warnings:")), lines.push(""), schemaWarnings.forEach((warning, index) => {
39
+ lines.push(`${index + 1}. ${warning.message}`), warning.location && lines.push(` ${ansis.dim(`File: ${warning.location}`)}`), warning.elementId && lines.push(` ${ansis.dim(`Element: ${warning.elementId}`)}`), warning.fixSuggestion && lines.push(` ${ansis.dim(`\u2192 Suggestion: ${warning.fixSuggestion}`)}`), lines.push("");
40
+ })), orphanWarnings.length > 0 && (lines.push(ansis.bold("Connectivity Warnings:")), lines.push(ansis.dim(` ${orphanWarnings.length} element(s) have no cross-layer references or intra-layer relationships.`)), lines.push(ansis.dim(" These are connectivity gaps, not structural errors \u2014 the model schema is valid.")), lines.push(""), orphanWarnings.forEach((warning, index) => {
41
+ lines.push(`${index + 1}. ${warning.message}`), warning.elementId && lines.push(` ${ansis.dim(`Element: ${warning.elementId}`)}`), warning.fixSuggestion && lines.push(` ${ansis.dim(`\u2192 Suggestion: ${warning.fixSuggestion}`)}`), lines.push("");
42
+ })), lines.push(ansis.bold("Summary:")), lines.push(`${result.errors.length === 0 ? ansis.green("\u2713") : ansis.red("\u2717")} ${stats.totalElements} total elements validated`), lines.push(`${ansis.green("\u2713")} ${stats.totalRelationships} relationships validated`), lines.push(`${ansis.green("\u2713")} ${Object.keys(layerStats).length} layers validated`), lines.push(`${result.errors.length === 0 ? ansis.green("\u2713") : ansis.red("\u2717")} ${result.errors.length} error(s), ${result.warnings.length} warning(s)`), lines.push(""), result.isValid() ? result.warnings.length === 0 ? lines.push(ansis.green("Model is valid and ready for use.")) : lines.push(ansis.yellow("Model is valid but consider addressing warnings.")) : lines.push(ansis.red("Model validation failed. Please fix errors before proceeding.")), lines.push(""), lines.join(`
43
+ `);
44
+ }
45
+ /**
46
+ * Format verbose output (detailed with relationship breakdown)
47
+ */
48
+ static formatVerbose(result, model) {
49
+ const lines = this.formatStandard(result, model).split(`
50
+ `), crossLayerIndex = lines.findIndex((l) => l.includes("Cross-Layer Validation:"));
51
+ if (crossLayerIndex >= 0) {
52
+ const stats = this.calculateStats(model), relationshipLines = [];
53
+ if (Object.keys(stats.relationshipsByPredicate).length > 0) {
54
+ relationshipLines.push(""), relationshipLines.push(ansis.dim("Relationship breakdown:"));
55
+ for (const [predicate, count] of Object.entries(stats.relationshipsByPredicate).sort())
56
+ relationshipLines.push(ansis.dim(` \u2022 ${predicate}: ${count} relationship(s)`));
57
+ }
58
+ lines.splice(crossLayerIndex + 4, 0, ...relationshipLines);
59
+ }
60
+ return lines.join(`
61
+ `);
62
+ }
63
+ /**
64
+ * Get statistics about layers
65
+ */
66
+ static getLayerStats(model) {
67
+ const stats = {};
68
+ for (const [layerName, layer] of model.layers) {
69
+ const count = layer.listElements().length;
70
+ count > 0 && (stats[layerName] = count);
71
+ }
72
+ return stats;
73
+ }
74
+ /**
75
+ * Calculate validation statistics
76
+ */
77
+ static calculateStats(model) {
78
+ const stats = {
79
+ totalElements: 0,
80
+ totalRelationships: 0,
81
+ elementsPerLayer: {},
82
+ relationshipsByPredicate: {},
83
+ orphanedElements: []
84
+ }, elementIds = /* @__PURE__ */ new Set(), crossLayerRefCounts = /* @__PURE__ */ new Map();
85
+ for (const [layerName, layer] of model.layers) {
86
+ let layerCount = 0;
87
+ for (const element of layer.listElements()) {
88
+ elementIds.add(element.path || element.id), layerCount++, stats.totalElements++;
89
+ const refCount = (element.references || []).length;
90
+ crossLayerRefCounts.set(element.path || element.id, refCount), stats.totalRelationships += refCount;
91
+ for (const ref of element.references || []) {
92
+ const predicate = ref.type || "references";
93
+ stats.relationshipsByPredicate[predicate] = (stats.relationshipsByPredicate[predicate] || 0) + 1;
94
+ }
95
+ }
96
+ layerCount > 0 && (stats.elementsPerLayer[layerName] = layerCount);
97
+ }
98
+ const intraLayerRels = model.relationships.getAll();
99
+ stats.totalRelationships += intraLayerRels.length;
100
+ const connectedViaIntraLayer = /* @__PURE__ */ new Set();
101
+ for (const rel of intraLayerRels)
102
+ stats.relationshipsByPredicate[rel.predicate] = (stats.relationshipsByPredicate[rel.predicate] || 0) + 1, connectedViaIntraLayer.add(rel.source), connectedViaIntraLayer.add(rel.target);
103
+ for (const elementId of elementIds) {
104
+ if (connectedViaIntraLayer.has(elementId))
105
+ continue;
106
+ const outgoingCrossLayer = crossLayerRefCounts.get(elementId) || 0;
107
+ let incomingCrossLayer = 0;
108
+ for (const [, layer] of model.layers)
109
+ for (const element of layer.listElements())
110
+ for (const ref of element.references || [])
111
+ ref.target === elementId && incomingCrossLayer++;
112
+ outgoingCrossLayer === 0 && incomingCrossLayer === 0 && stats.orphanedElements.push(elementId);
113
+ }
114
+ return stats;
115
+ }
116
+ /**
117
+ * Export validation result as JSON
118
+ */
119
+ static toJSON(result, model) {
120
+ const stats = this.calculateStats(model), layerStats = this.getLayerStats(model);
121
+ return {
122
+ valid: result.isValid(),
123
+ summary: {
124
+ totalElements: stats.totalElements,
125
+ totalRelationships: stats.totalRelationships,
126
+ errorCount: result.errors.length,
127
+ warningCount: result.warnings.length,
128
+ orphanedCount: stats.orphanedElements.length,
129
+ layersValidated: Object.keys(layerStats).length
130
+ },
131
+ layerStats,
132
+ relationshipsByPredicate: stats.relationshipsByPredicate,
133
+ orphanedElements: stats.orphanedElements,
134
+ errors: result.errors,
135
+ warnings: result.warnings
136
+ };
137
+ }
138
+ /**
139
+ * Export validation result as Markdown
140
+ */
141
+ static toMarkdown(result, model) {
142
+ const lines = [], stats = this.calculateStats(model), layerStats = this.getLayerStats(model);
143
+ lines.push("# Validation Report"), lines.push(""), lines.push(`**Status**: ${result.isValid() ? "\u2713 Valid" : "\u2717 Invalid"}`), lines.push(`**Generated**: ${(/* @__PURE__ */ new Date()).toISOString()}`), lines.push(""), lines.push("## Summary"), lines.push(""), lines.push("| Metric | Value |"), lines.push("|--------|-------|"), lines.push(`| Total Elements | ${stats.totalElements} |`), lines.push(`| Total Relationships | ${stats.totalRelationships} |`), lines.push(`| Layers Validated | ${Object.keys(layerStats).length} |`), lines.push(`| Errors | ${result.errors.length} |`), lines.push(`| Warnings | ${result.warnings.length} |`), lines.push(`| Orphaned Elements | ${stats.orphanedElements.length} |`), lines.push(""), lines.push("## Layer Statistics"), lines.push("");
144
+ for (const [layerName, count] of Object.entries(layerStats)) {
145
+ const status = result.errors.some((e) => e.layer === layerName) ? "\u2717" : "\u2713";
146
+ lines.push(`- ${status} ${layerName}: ${count} elements`);
147
+ }
148
+ return lines.push(""), result.errors.length > 0 && (lines.push("## Errors"), lines.push(""), result.errors.forEach((error, index) => {
149
+ lines.push(`### ${index + 1}. ${error.message}`), lines.push(""), error.location && lines.push(`**File**: \`${error.location}\``), error.elementId && lines.push(`**Element**: \`${error.elementId}\``), error.fixSuggestion && lines.push(`**Suggestion**: ${error.fixSuggestion}`), lines.push("");
150
+ })), result.warnings.length > 0 && (lines.push("## Warnings"), lines.push(""), result.warnings.forEach((warning, index) => {
151
+ lines.push(`### ${index + 1}. ${warning.message}`), lines.push(""), warning.location && lines.push(`**File**: \`${warning.location}\``), warning.elementId && lines.push(`**Element**: \`${warning.elementId}\``), warning.fixSuggestion && lines.push(`**Suggestion**: ${warning.fixSuggestion}`), lines.push("");
152
+ })), lines.join(`
153
+ `);
154
+ }
155
+ }
156
+ export {
157
+ ValidationFormatter
158
+ };
159
+ //# sourceMappingURL=validation-formatter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/validators/validation-formatter.ts"],
4
+ "sourcesContent": ["/**\n * Validation formatter for detailed and actionable output\n */\n\nimport ansis from \"ansis\";\nimport type { ValidationResult } from \"./types.js\";\nimport type { Model } from \"../core/model.js\";\nimport { formatLayerNameWithSuffix } from \"../utils/layer-name-formatter.js\";\n\ninterface ValidationStats {\n totalElements: number;\n totalRelationships: number;\n elementsPerLayer: Record<string, number>;\n relationshipsByPredicate: Record<string, number>;\n orphanedElements: string[];\n}\n\n/**\n * Formats validation results into detailed, human-readable output\n */\nexport class ValidationFormatter {\n /**\n * Format validation result for display\n */\n static format(\n result: ValidationResult,\n model: Model,\n options: {\n verbose?: boolean;\n quiet?: boolean;\n } = {}\n ): string {\n if (options.quiet) {\n return this.formatQuiet(result);\n }\n\n if (options.verbose) {\n return this.formatVerbose(result, model);\n }\n\n return this.formatStandard(result, model);\n }\n\n /**\n * Format quiet output (minimal)\n */\n private static formatQuiet(result: ValidationResult): string {\n if (result.isValid() && result.warnings.length === 0) {\n return ansis.green(\"\u2713 Validation passed\");\n }\n\n const lines: string[] = [];\n\n if (result.errors.length > 0) {\n lines.push(ansis.red(`\u2717 ${result.errors.length} error(s)`));\n }\n\n if (result.warnings.length > 0) {\n lines.push(ansis.yellow(`\u26A0 ${result.warnings.length} warning(s)`));\n }\n\n return lines.join(\" \");\n }\n\n /**\n * Format standard output (enhanced with stats)\n */\n private static formatStandard(result: ValidationResult, model: Model): string {\n const lines: string[] = [];\n\n lines.push(\"\");\n lines.push(ansis.bold(\"Validating Documentation Robotics Model\"));\n lines.push(ansis.bold(\"========================================\"));\n lines.push(\"\");\n\n // Layer-by-layer validation summary\n lines.push(ansis.bold(\"Schema Validation:\"));\n const layerStats = this.getLayerStats(model);\n for (const [layerName, count] of Object.entries(layerStats)) {\n const hasErrors = result.errors.some((e) => e.layer === layerName);\n if (hasErrors) {\n const errorCount = result.errors.filter((e) => e.layer === layerName).length;\n lines.push(\n `${ansis.red(\"\u2717\")} ${formatLayerNameWithSuffix(layerName)} (${count} elements) - ${errorCount} error(s)`\n );\n } else {\n lines.push(`${ansis.green(\"\u2713\")} ${formatLayerNameWithSuffix(layerName)} (${count} elements)`);\n }\n }\n\n lines.push(\"\");\n\n // Cross-layer validation\n lines.push(ansis.bold(\"Cross-Layer Validation:\"));\n const stats = this.calculateStats(model);\n lines.push(\n `${ansis.green(\"\u2713\")} ${stats.totalRelationships} relationships validated`\n );\n\n // Orphaned element warnings are injected into result.warnings in validate.ts\n // so they appear in the warnings section and are counted in the summary.\n // Do not display a separate orphan count here to avoid double-reporting.\n\n lines.push(\"\");\n\n // Error details\n if (result.errors.length > 0) {\n lines.push(ansis.bold(\"Errors:\"));\n lines.push(\"\");\n result.errors.forEach((error, index) => {\n lines.push(`${index + 1}. ${error.message}`);\n if (error.location) {\n lines.push(` ${ansis.dim(`File: ${error.location}`)}`);\n }\n if (error.elementId) {\n lines.push(` ${ansis.dim(`Element: ${error.elementId}`)}`);\n }\n if (error.fixSuggestion) {\n lines.push(` ${ansis.dim(`\u2192 Suggestion: ${error.fixSuggestion}`)}`);\n }\n lines.push(\"\");\n });\n }\n\n // Warning details \u2014 split schema/semantic warnings from connectivity (orphan) warnings\n const schemaWarnings = result.warnings.filter((w) => w.category !== \"orphan\");\n const orphanWarnings = result.warnings.filter((w) => w.category === \"orphan\");\n\n if (schemaWarnings.length > 0) {\n lines.push(ansis.bold(\"Warnings:\"));\n lines.push(\"\");\n schemaWarnings.forEach((warning, index) => {\n lines.push(`${index + 1}. ${warning.message}`);\n if (warning.location) {\n lines.push(` ${ansis.dim(`File: ${warning.location}`)}`);\n }\n if (warning.elementId) {\n lines.push(` ${ansis.dim(`Element: ${warning.elementId}`)}`);\n }\n if (warning.fixSuggestion) {\n lines.push(` ${ansis.dim(`\u2192 Suggestion: ${warning.fixSuggestion}`)}`);\n }\n lines.push(\"\");\n });\n }\n\n if (orphanWarnings.length > 0) {\n lines.push(ansis.bold(\"Connectivity Warnings:\"));\n lines.push(\n ansis.dim(\n ` ${orphanWarnings.length} element(s) have no cross-layer references or intra-layer relationships.`\n )\n );\n lines.push(\n ansis.dim(\" These are connectivity gaps, not structural errors \u2014 the model schema is valid.\")\n );\n lines.push(\"\");\n orphanWarnings.forEach((warning, index) => {\n lines.push(`${index + 1}. ${warning.message}`);\n if (warning.elementId) {\n lines.push(` ${ansis.dim(`Element: ${warning.elementId}`)}`);\n }\n if (warning.fixSuggestion) {\n lines.push(` ${ansis.dim(`\u2192 Suggestion: ${warning.fixSuggestion}`)}`);\n }\n lines.push(\"\");\n });\n }\n\n // Summary\n lines.push(ansis.bold(\"Summary:\"));\n lines.push(\n `${result.errors.length === 0 ? ansis.green(\"\u2713\") : ansis.red(\"\u2717\")} ${stats.totalElements} total elements validated`\n );\n lines.push(`${ansis.green(\"\u2713\")} ${stats.totalRelationships} relationships validated`);\n lines.push(`${ansis.green(\"\u2713\")} ${Object.keys(layerStats).length} layers validated`);\n lines.push(\n `${result.errors.length === 0 ? ansis.green(\"\u2713\") : ansis.red(\"\u2717\")} ${result.errors.length} error(s), ${result.warnings.length} warning(s)`\n );\n lines.push(\"\");\n\n if (result.isValid()) {\n if (result.warnings.length === 0) {\n lines.push(ansis.green(\"Model is valid and ready for use.\"));\n } else {\n lines.push(ansis.yellow(\"Model is valid but consider addressing warnings.\"));\n }\n } else {\n lines.push(ansis.red(\"Model validation failed. Please fix errors before proceeding.\"));\n }\n\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Format verbose output (detailed with relationship breakdown)\n */\n private static formatVerbose(result: ValidationResult, model: Model): string {\n const standard = this.formatStandard(result, model);\n const lines = standard.split(\"\\n\");\n\n // Find the \"Cross-Layer Validation:\" section and expand it\n const crossLayerIndex = lines.findIndex((l) => l.includes(\"Cross-Layer Validation:\"));\n if (crossLayerIndex >= 0) {\n const stats = this.calculateStats(model);\n const relationshipLines: string[] = [];\n\n // Add breakdown by relationship type\n if (Object.keys(stats.relationshipsByPredicate).length > 0) {\n relationshipLines.push(\"\");\n relationshipLines.push(ansis.dim(\"Relationship breakdown:\"));\n for (const [predicate, count] of Object.entries(stats.relationshipsByPredicate).sort()) {\n relationshipLines.push(ansis.dim(` \u2022 ${predicate}: ${count} relationship(s)`));\n }\n }\n\n // Insert after the cross-layer section\n lines.splice(crossLayerIndex + 4, 0, ...relationshipLines);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Get statistics about layers\n */\n private static getLayerStats(model: Model): Record<string, number> {\n const stats: Record<string, number> = {};\n\n for (const [layerName, layer] of model.layers) {\n const count = layer.listElements().length;\n if (count > 0) {\n stats[layerName] = count;\n }\n }\n\n return stats;\n }\n\n /**\n * Calculate validation statistics\n */\n static calculateStats(model: Model): ValidationStats {\n const stats: ValidationStats = {\n totalElements: 0,\n totalRelationships: 0,\n elementsPerLayer: {},\n relationshipsByPredicate: {},\n orphanedElements: [],\n };\n\n const elementIds = new Set<string>();\n const crossLayerRefCounts = new Map<string, number>();\n\n // Count elements and cross-layer references (element.references[])\n for (const [layerName, layer] of model.layers) {\n let layerCount = 0;\n for (const element of layer.listElements()) {\n elementIds.add(element.path || element.id);\n layerCount++;\n stats.totalElements++;\n\n const refCount = (element.references || []).length;\n crossLayerRefCounts.set(element.path || element.id, refCount);\n stats.totalRelationships += refCount;\n\n for (const ref of element.references || []) {\n const predicate = ref.type || \"references\";\n stats.relationshipsByPredicate[predicate] =\n (stats.relationshipsByPredicate[predicate] || 0) + 1;\n }\n }\n\n if (layerCount > 0) {\n stats.elementsPerLayer[layerName] = layerCount;\n }\n }\n\n // Also count intra-layer relationships from relationships.yaml\n const intraLayerRels = model.relationships.getAll();\n stats.totalRelationships += intraLayerRels.length;\n const connectedViaIntraLayer = new Set<string>();\n for (const rel of intraLayerRels) {\n stats.relationshipsByPredicate[rel.predicate] =\n (stats.relationshipsByPredicate[rel.predicate] || 0) + 1;\n connectedViaIntraLayer.add(rel.source);\n connectedViaIntraLayer.add(rel.target);\n }\n\n // Find orphaned elements: no cross-layer refs and not touched by any intra-layer relationship\n for (const elementId of elementIds) {\n if (connectedViaIntraLayer.has(elementId)) {\n continue;\n }\n\n const outgoingCrossLayer = crossLayerRefCounts.get(elementId) || 0;\n let incomingCrossLayer = 0;\n for (const [, layer] of model.layers) {\n for (const element of layer.listElements()) {\n for (const ref of element.references || []) {\n if (ref.target === elementId) {\n incomingCrossLayer++;\n }\n }\n }\n }\n\n if (outgoingCrossLayer === 0 && incomingCrossLayer === 0) {\n stats.orphanedElements.push(elementId);\n }\n }\n\n return stats;\n }\n\n /**\n * Export validation result as JSON\n */\n static toJSON(result: ValidationResult, model: Model): Record<string, unknown> {\n const stats = this.calculateStats(model);\n const layerStats = this.getLayerStats(model);\n\n return {\n valid: result.isValid(),\n summary: {\n totalElements: stats.totalElements,\n totalRelationships: stats.totalRelationships,\n errorCount: result.errors.length,\n warningCount: result.warnings.length,\n orphanedCount: stats.orphanedElements.length,\n layersValidated: Object.keys(layerStats).length,\n },\n layerStats,\n relationshipsByPredicate: stats.relationshipsByPredicate,\n orphanedElements: stats.orphanedElements,\n errors: result.errors,\n warnings: result.warnings,\n };\n }\n\n /**\n * Export validation result as Markdown\n */\n static toMarkdown(result: ValidationResult, model: Model): string {\n const lines: string[] = [];\n const stats = this.calculateStats(model);\n const layerStats = this.getLayerStats(model);\n\n lines.push(\"# Validation Report\");\n lines.push(\"\");\n lines.push(`**Status**: ${result.isValid() ? \"\u2713 Valid\" : \"\u2717 Invalid\"}`);\n lines.push(`**Generated**: ${new Date().toISOString()}`);\n lines.push(\"\");\n\n lines.push(\"## Summary\");\n lines.push(\"\");\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Elements | ${stats.totalElements} |`);\n lines.push(`| Total Relationships | ${stats.totalRelationships} |`);\n lines.push(`| Layers Validated | ${Object.keys(layerStats).length} |`);\n lines.push(`| Errors | ${result.errors.length} |`);\n lines.push(`| Warnings | ${result.warnings.length} |`);\n lines.push(`| Orphaned Elements | ${stats.orphanedElements.length} |`);\n lines.push(\"\");\n\n lines.push(\"## Layer Statistics\");\n lines.push(\"\");\n for (const [layerName, count] of Object.entries(layerStats)) {\n const hasErrors = result.errors.some((e) => e.layer === layerName);\n const status = hasErrors ? \"\u2717\" : \"\u2713\";\n lines.push(`- ${status} ${layerName}: ${count} elements`);\n }\n lines.push(\"\");\n\n if (result.errors.length > 0) {\n lines.push(\"## Errors\");\n lines.push(\"\");\n result.errors.forEach((error, index) => {\n lines.push(`### ${index + 1}. ${error.message}`);\n lines.push(\"\");\n if (error.location) {\n lines.push(`**File**: \\`${error.location}\\``);\n }\n if (error.elementId) {\n lines.push(`**Element**: \\`${error.elementId}\\``);\n }\n if (error.fixSuggestion) {\n lines.push(`**Suggestion**: ${error.fixSuggestion}`);\n }\n lines.push(\"\");\n });\n }\n\n if (result.warnings.length > 0) {\n lines.push(\"## Warnings\");\n lines.push(\"\");\n result.warnings.forEach((warning, index) => {\n lines.push(`### ${index + 1}. ${warning.message}`);\n lines.push(\"\");\n if (warning.location) {\n lines.push(`**File**: \\`${warning.location}\\``);\n }\n if (warning.elementId) {\n lines.push(`**Element**: \\`${warning.elementId}\\``);\n }\n if (warning.fixSuggestion) {\n lines.push(`**Suggestion**: ${warning.fixSuggestion}`);\n }\n lines.push(\"\");\n });\n }\n\n return lines.join(\"\\n\");\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -3,62 +3,48 @@ import { SchemaValidator } from "./schema-validator.js";
3
3
  import { NamingValidator } from "./naming-validator.js";
4
4
  import { ReferenceValidator } from "./reference-validator.js";
5
5
  import { SemanticValidator } from "./semantic-validator.js";
6
- import { startSpan, endSpan } from "../telemetry/index.js";
7
- const isTelemetryEnabled = !1;
6
+ import { RelationshipValidator } from "./relationship-schema-validator.js";
7
+ import { startActiveSpan } from "../telemetry/index.js";
8
8
  class Validator {
9
9
  schemaValidator;
10
10
  namingValidator;
11
11
  referenceValidator;
12
12
  semanticValidator;
13
+ relationshipValidator;
13
14
  constructor() {
14
- this.schemaValidator = new SchemaValidator(), this.namingValidator = new NamingValidator(), this.referenceValidator = new ReferenceValidator(), this.semanticValidator = new SemanticValidator();
15
+ this.schemaValidator = new SchemaValidator(), this.namingValidator = new NamingValidator(), this.referenceValidator = new ReferenceValidator(), this.semanticValidator = new SemanticValidator(), this.relationshipValidator = new RelationshipValidator();
15
16
  }
16
17
  /**
17
- * Validate a complete model through all 4 stages
18
+ * Validate a complete model through all 5 stages
18
19
  */
19
20
  async validateModel(model) {
20
- const rootSpan = isTelemetryEnabled ? startSpan("model.validate", {
21
- "model.path": model.rootPath
22
- }) : null, result = new ValidationResult();
23
- try {
24
- const schemaStageSpan = isTelemetryEnabled ? startSpan("validation.stage.schema") : null;
25
- try {
26
- for (const layer of model.layers.values()) {
21
+ return startActiveSpan("model.validate", async (rootSpan) => {
22
+ const result = new ValidationResult();
23
+ return await startActiveSpan("validation.stage.schema", async (stageSpan) => {
24
+ await Promise.all(Array.from(model.layers.values()).map(async (layer) => {
27
25
  const schemaResult = await this.schemaValidator.validateLayer(layer);
28
26
  result.merge(schemaResult, `[Schema/${layer.name}]`);
29
- }
30
- isTelemetryEnabled && schemaStageSpan && schemaStageSpan.setAttribute("validation.error_count", result.errors.length);
31
- } finally {
32
- endSpan(schemaStageSpan);
33
- }
34
- const namingStageSpan = isTelemetryEnabled ? startSpan("validation.stage.naming") : null;
35
- try {
27
+ })), stageSpan.setAttribute("validation.error_count", result.errors.length);
28
+ }), await startActiveSpan("validation.stage.naming", async (stageSpan) => {
36
29
  for (const layer of model.layers.values()) {
37
30
  const namingResult = this.namingValidator.validateLayer(layer);
38
31
  result.merge(namingResult, `[Naming/${layer.name}]`);
39
32
  }
40
- isTelemetryEnabled && namingStageSpan && namingStageSpan.setAttribute("validation.error_count", result.errors.length);
41
- } finally {
42
- endSpan(namingStageSpan);
43
- }
44
- const refStageSpan = isTelemetryEnabled ? startSpan("validation.stage.reference") : null;
45
- try {
33
+ stageSpan.setAttribute("validation.error_count", result.errors.length);
34
+ }), await startActiveSpan("validation.stage.reference", async (stageSpan) => {
46
35
  const referenceResult = this.referenceValidator.validateModel(model);
47
- result.merge(referenceResult, "[References]"), isTelemetryEnabled && refStageSpan && refStageSpan.setAttribute("validation.error_count", result.errors.length);
48
- } finally {
49
- endSpan(refStageSpan);
50
- }
51
- const semanticStageSpan = isTelemetryEnabled ? startSpan("validation.stage.semantic") : null;
52
- try {
36
+ result.merge(referenceResult, "[References]"), stageSpan.setAttribute("validation.error_count", result.errors.length);
37
+ }), await startActiveSpan("validation.stage.semantic", async (stageSpan) => {
53
38
  const semanticResult = await this.semanticValidator.validateModel(model);
54
- result.merge(semanticResult, "[Semantic]"), isTelemetryEnabled && semanticStageSpan && semanticStageSpan.setAttribute("validation.error_count", result.errors.length);
55
- } finally {
56
- endSpan(semanticStageSpan);
57
- }
58
- return isTelemetryEnabled && rootSpan && (rootSpan.setAttribute("validation.valid", result.isValid()), rootSpan.setAttribute("validation.error_count", result.errors.length)), result;
59
- } finally {
60
- endSpan(rootSpan);
61
- }
39
+ result.merge(semanticResult, "[Semantic]"), stageSpan.setAttribute("validation.error_count", result.errors.length);
40
+ }), await startActiveSpan("validation.stage.relationship-schema", async (stageSpan) => {
41
+ await this.relationshipValidator.initialize();
42
+ const relationshipResult = await this.relationshipValidator.validateModel(model);
43
+ result.merge(relationshipResult, "[Relationship Schema]"), stageSpan.setAttribute("validation.error_count", result.errors.length);
44
+ }), rootSpan.setAttribute("validation.valid", result.isValid()), rootSpan.setAttribute("validation.error_count", result.errors.length), result;
45
+ }, {
46
+ "model.path": model.rootPath
47
+ });
62
48
  }
63
49
  }
64
50
  export {