@documentation-robotics/cli 0.1.0 → 0.1.7
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.
- package/LICENSE +21 -0
- package/README.md +466 -693
- package/dist/analysis/data-model-analyzer.js +189 -0
- package/dist/analysis/data-model-analyzer.js.map +7 -0
- package/dist/analysis/index.js +3 -0
- package/dist/analysis/index.js.map +7 -0
- package/dist/analysis/relationship-classifier.js +181 -0
- package/dist/analysis/relationship-classifier.js.map +7 -0
- package/dist/analyzers/base-analyzer.js +1 -0
- package/dist/analyzers/base-analyzer.js.map +7 -0
- package/dist/analyzers/call-graph-utils.js +39 -0
- package/dist/analyzers/call-graph-utils.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +1205 -0
- package/dist/analyzers/cbm-analyzer.js.map +7 -0
- package/dist/analyzers/heuristic-utils.js +55 -0
- package/dist/analyzers/heuristic-utils.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +146 -0
- package/dist/analyzers/mapping-loader.js.map +7 -0
- package/dist/analyzers/registry.js +124 -0
- package/dist/analyzers/registry.js.map +7 -0
- package/dist/analyzers/session-state.js +176 -0
- package/dist/analyzers/session-state.js.map +7 -0
- package/dist/analyzers/stdio-client.js +208 -0
- package/dist/analyzers/stdio-client.js.map +7 -0
- package/dist/analyzers/types.js +15 -0
- package/dist/analyzers/types.js.map +7 -0
- package/dist/analyzers/verify-engine.js +209 -0
- package/dist/analyzers/verify-engine.js.map +7 -0
- package/dist/analyzers/verify-ignore.js +130 -0
- package/dist/analyzers/verify-ignore.js.map +7 -0
- package/dist/analyzers/version.js +8 -0
- package/dist/analyzers/version.js.map +7 -0
- package/dist/audit/ai/runner.js +41 -0
- package/dist/audit/ai/runner.js.map +7 -0
- package/dist/audit/differential-analyzer.js +183 -0
- package/dist/audit/differential-analyzer.js.map +7 -0
- package/dist/audit/index.js +32 -0
- package/dist/audit/index.js.map +7 -0
- package/dist/audit/nodes/ai/evaluator.js +49 -0
- package/dist/audit/nodes/ai/evaluator.js.map +7 -0
- package/dist/audit/nodes/ai/parser.js +55 -0
- package/dist/audit/nodes/ai/parser.js.map +7 -0
- package/dist/audit/nodes/ai/prompts.js +103 -0
- package/dist/audit/nodes/ai/prompts.js.map +7 -0
- package/dist/audit/nodes/analysis/node-completeness-checker.js +32 -0
- package/dist/audit/nodes/analysis/node-completeness-checker.js.map +7 -0
- package/dist/audit/nodes/analysis/node-definition-analyzer.js +47 -0
- package/dist/audit/nodes/analysis/node-definition-analyzer.js.map +7 -0
- package/dist/audit/nodes/analysis/node-overlap-detector.js +79 -0
- package/dist/audit/nodes/analysis/node-overlap-detector.js.map +7 -0
- package/dist/audit/nodes/model/orchestrator.js +82 -0
- package/dist/audit/nodes/model/orchestrator.js.map +7 -0
- package/dist/audit/nodes/spec/loader.js +116 -0
- package/dist/audit/nodes/spec/loader.js.map +7 -0
- package/dist/audit/nodes/spec/orchestrator.js +68 -0
- package/dist/audit/nodes/spec/orchestrator.js.map +7 -0
- package/dist/audit/nodes/types.js +1 -0
- package/dist/audit/nodes/types.js.map +7 -0
- package/dist/audit/pipeline/index.js +5 -0
- package/dist/audit/pipeline/index.js.map +7 -0
- package/dist/audit/pipeline/pipeline-orchestrator.js +218 -0
- package/dist/audit/pipeline/pipeline-orchestrator.js.map +7 -0
- package/dist/audit/relationships/ai/evaluator.js +235 -0
- package/dist/audit/relationships/ai/evaluator.js.map +7 -0
- package/dist/audit/relationships/ai/parser.js +197 -0
- package/dist/audit/relationships/ai/parser.js.map +7 -0
- package/dist/audit/relationships/ai/prompts.js +274 -0
- package/dist/audit/relationships/ai/prompts.js.map +7 -0
- package/dist/audit/relationships/analysis/balance-assessor.js +115 -0
- package/dist/audit/relationships/analysis/balance-assessor.js.map +7 -0
- package/dist/audit/relationships/analysis/coverage-analyzer.js +99 -0
- package/dist/audit/relationships/analysis/coverage-analyzer.js.map +7 -0
- package/dist/audit/relationships/analysis/duplicate-detector.js +95 -0
- package/dist/audit/relationships/analysis/duplicate-detector.js.map +7 -0
- package/dist/audit/relationships/analysis/gap-analyzer.js +99 -0
- package/dist/audit/relationships/analysis/gap-analyzer.js.map +7 -0
- package/dist/audit/relationships/analysis/layer-templates.js +294 -0
- package/dist/audit/relationships/analysis/layer-templates.js.map +7 -0
- package/dist/audit/relationships/graph/connectivity.js +129 -0
- package/dist/audit/relationships/graph/connectivity.js.map +7 -0
- package/dist/audit/relationships/graph/relationship-graph.js +179 -0
- package/dist/audit/relationships/graph/relationship-graph.js.map +7 -0
- package/dist/audit/relationships/model/balance-assessor.js +49 -0
- package/dist/audit/relationships/model/balance-assessor.js.map +7 -0
- package/dist/audit/relationships/model/coverage-analyzer.js +34 -0
- package/dist/audit/relationships/model/coverage-analyzer.js.map +7 -0
- package/dist/audit/relationships/model/duplicate-detector.js +68 -0
- package/dist/audit/relationships/model/duplicate-detector.js.map +7 -0
- package/dist/audit/relationships/model/gap-analyzer.js +41 -0
- package/dist/audit/relationships/model/gap-analyzer.js.map +7 -0
- package/dist/audit/relationships/model/orchestrator.js +126 -0
- package/dist/audit/relationships/model/orchestrator.js.map +7 -0
- package/dist/audit/relationships/spec/orchestrator.js +157 -0
- package/dist/audit/relationships/spec/orchestrator.js.map +7 -0
- package/dist/audit/reports/index.js +5 -0
- package/dist/audit/reports/index.js.map +7 -0
- package/dist/audit/reports/report-generator.js +118 -0
- package/dist/audit/reports/report-generator.js.map +7 -0
- package/dist/audit/snapshot-storage.js +163 -0
- package/dist/audit/snapshot-storage.js.map +7 -0
- package/dist/audit/types.js +21 -0
- package/dist/audit/types.js.map +7 -0
- package/dist/cli.js +223 -92
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +147 -0
- package/dist/coding-agents/agents/claude-code.js.map +7 -0
- package/dist/coding-agents/agents/index.js +5 -0
- package/dist/coding-agents/agents/index.js.map +7 -0
- package/dist/coding-agents/agents/types.js +1 -0
- package/dist/coding-agents/agents/types.js.map +7 -0
- package/dist/coding-agents/base-chat-client.js +45 -0
- package/dist/coding-agents/base-chat-client.js.map +7 -0
- package/dist/coding-agents/chat-utils.js +23 -0
- package/dist/coding-agents/chat-utils.js.map +7 -0
- package/dist/coding-agents/claude-code-client.js +246 -0
- package/dist/coding-agents/claude-code-client.js.map +7 -0
- package/dist/coding-agents/copilot-client.js +209 -0
- package/dist/coding-agents/copilot-client.js.map +7 -0
- package/dist/commands/add.js +95 -33
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +511 -0
- package/dist/commands/analyzer.js.map +7 -0
- package/dist/commands/audit-diff.js +49 -0
- package/dist/commands/audit-diff.js.map +7 -0
- package/dist/commands/audit-snapshots.js +62 -0
- package/dist/commands/audit-snapshots.js.map +7 -0
- package/dist/commands/audit.js +81 -0
- package/dist/commands/audit.js.map +7 -0
- package/dist/commands/catalog.js +248 -0
- package/dist/commands/catalog.js.map +7 -0
- package/dist/commands/changeset.js +577 -66
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/chat.js +106 -83
- package/dist/commands/chat.js.map +2 -2
- package/dist/commands/claude.js +74 -0
- package/dist/commands/claude.js.map +7 -0
- package/dist/commands/conformance.js +71 -68
- package/dist/commands/conformance.js.map +2 -2
- package/dist/commands/copilot.js +75 -0
- package/dist/commands/copilot.js.map +7 -0
- package/dist/commands/delete.js +125 -11
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/discover-logic.js +44 -0
- package/dist/commands/discover-logic.js.map +7 -0
- package/dist/commands/docs.js +87 -0
- package/dist/commands/docs.js.map +7 -0
- package/dist/commands/element.js +14 -20
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/export.js +13 -3
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/import.js +51 -0
- package/dist/commands/import.js.map +7 -0
- package/dist/commands/info.js +30 -24
- package/dist/commands/info.js.map +2 -2
- package/dist/commands/init.js +27 -11
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/list.js +63 -13
- package/dist/commands/list.js.map +3 -3
- package/dist/commands/relationship.js +225 -37
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/report.js +54 -0
- package/dist/commands/report.js.map +7 -0
- package/dist/commands/reports.js +45 -0
- package/dist/commands/reports.js.map +7 -0
- package/dist/commands/schema.js +135 -0
- package/dist/commands/schema.js.map +7 -0
- package/dist/commands/search.js +40 -12
- package/dist/commands/search.js.map +2 -2
- package/dist/commands/show.js +58 -27
- package/dist/commands/show.js.map +2 -2
- package/dist/commands/stats.js +35 -0
- package/dist/commands/stats.js.map +7 -0
- package/dist/commands/trace.js +39 -16
- package/dist/commands/trace.js.map +2 -2
- package/dist/commands/update.js +141 -14
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +210 -46
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/validate.js +142 -23
- package/dist/commands/validate.js.map +3 -3
- package/dist/commands/version.js +8 -12
- package/dist/commands/version.js.map +2 -2
- package/dist/commands/visualize.js +162 -25
- package/dist/commands/visualize.js.map +2 -2
- package/dist/core/base-snapshot-manager.js +84 -0
- package/dist/core/base-snapshot-manager.js.map +7 -0
- package/dist/core/changeset-exporter.js +305 -0
- package/dist/core/changeset-exporter.js.map +7 -0
- package/dist/core/changeset-validator.js +41 -0
- package/dist/core/changeset-validator.js.map +7 -0
- package/dist/core/changeset.js +88 -173
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/dependency-tracker.js +4 -2
- package/dist/core/dependency-tracker.js.map +2 -2
- package/dist/core/element.js +98 -27
- package/dist/core/element.js.map +2 -2
- package/dist/core/golden-copy-cache.js +290 -0
- package/dist/core/golden-copy-cache.js.map +7 -0
- package/dist/core/graph-model.js +266 -0
- package/dist/core/graph-model.js.map +7 -0
- package/dist/core/layer.js +159 -16
- package/dist/core/layer.js.map +2 -2
- package/dist/core/layers.js +32 -0
- package/dist/core/layers.js.map +7 -0
- package/dist/core/manifest.js +32 -11
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +56 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model.js +424 -217
- package/dist/core/model.js.map +3 -3
- package/dist/core/mutation-handler.js +179 -0
- package/dist/core/mutation-handler.js.map +7 -0
- package/dist/core/reference-registry.js +29 -21
- package/dist/core/reference-registry.js.map +2 -2
- package/dist/core/relationship-catalog.js +189 -0
- package/dist/core/relationship-catalog.js.map +7 -0
- package/dist/core/relationship-registry.js.map +2 -2
- package/dist/core/relationships.js.map +1 -1
- package/dist/core/report-data-model.js +285 -0
- package/dist/core/report-data-model.js.map +7 -0
- package/dist/core/spec-data-service.js +185 -0
- package/dist/core/spec-data-service.js.map +7 -0
- package/dist/core/spec-loader-types.js +1 -0
- package/dist/core/spec-loader-types.js.map +7 -0
- package/dist/core/spec-loader.js +234 -0
- package/dist/core/spec-loader.js.map +7 -0
- package/dist/core/staged-changeset-storage.js +282 -0
- package/dist/core/staged-changeset-storage.js.map +7 -0
- package/dist/core/staging-area.js +1002 -0
- package/dist/core/staging-area.js.map +7 -0
- package/dist/core/stats-collector.js +169 -0
- package/dist/core/stats-collector.js.map +7 -0
- package/dist/core/virtual-projection.js +443 -0
- package/dist/core/virtual-projection.js.map +7 -0
- package/dist/export/archimate-exporter.js +99 -60
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/export/audit-formatters.js +479 -0
- package/dist/export/audit-formatters.js.map +7 -0
- package/dist/export/diff-formatters.js +99 -0
- package/dist/export/diff-formatters.js.map +7 -0
- package/dist/export/export-manager.js.map +2 -2
- package/dist/export/graphml-exporter.js +44 -27
- package/dist/export/graphml-exporter.js.map +2 -2
- package/dist/export/index.js +4 -0
- package/dist/export/index.js.map +2 -2
- package/dist/export/json-schema-exporter.js +55 -42
- package/dist/export/json-schema-exporter.js.map +2 -2
- package/dist/export/markdown-exporter.js +92 -70
- package/dist/export/markdown-exporter.js.map +2 -2
- package/dist/export/markdown-generator.js +248 -0
- package/dist/export/markdown-generator.js.map +7 -0
- package/dist/export/markdown-utils.js +30 -0
- package/dist/export/markdown-utils.js.map +7 -0
- package/dist/export/mermaid-markdown-exporter.js +37 -0
- package/dist/export/mermaid-markdown-exporter.js.map +7 -0
- package/dist/export/openapi-exporter.js +119 -71
- package/dist/export/openapi-exporter.js.map +2 -2
- package/dist/export/plantuml-exporter.js +104 -39
- package/dist/export/plantuml-exporter.js.map +2 -2
- package/dist/export/report-formatters.js +111 -0
- package/dist/export/report-formatters.js.map +7 -0
- package/dist/export/stats-formatters.js +98 -0
- package/dist/export/stats-formatters.js.map +7 -0
- package/dist/export/types.js +2 -14
- package/dist/export/types.js.map +2 -2
- package/dist/export/verify-formatters.js +153 -0
- package/dist/export/verify-formatters.js.map +7 -0
- package/dist/generated/compiled-validators.js +17 -0
- package/dist/generated/compiled-validators.js.map +7 -0
- package/dist/generated/index.js +31 -0
- package/dist/generated/index.js.map +7 -0
- package/dist/generated/layer-registry.js +190 -0
- package/dist/generated/layer-registry.js.map +7 -0
- package/dist/generated/layer-types.js +7 -0
- package/dist/generated/layer-types.js.map +7 -0
- package/dist/generated/node-types.js +1719 -0
- package/dist/generated/node-types.js.map +7 -0
- package/dist/generated/relationship-index.js +14552 -0
- package/dist/generated/relationship-index.js.map +7 -0
- package/dist/import/archimate-importer.js +166 -0
- package/dist/import/archimate-importer.js.map +7 -0
- package/dist/import/index.js +47 -0
- package/dist/import/index.js.map +7 -0
- package/dist/import/openapi-importer.js +143 -0
- package/dist/import/openapi-importer.js.map +7 -0
- package/dist/import/types.js +1 -0
- package/dist/import/types.js.map +7 -0
- package/dist/integrations/base-manager.js +311 -0
- package/dist/integrations/base-manager.js.map +7 -0
- package/dist/integrations/claude-manager.js +360 -0
- package/dist/integrations/claude-manager.js.map +7 -0
- package/dist/integrations/claude_code/agents/dr-advisor.md +253 -0
- package/dist/integrations/claude_code/agents/dr-architect.md +278 -0
- package/dist/integrations/claude_code/agents/dr-extractor.md +286 -0
- package/dist/integrations/claude_code/commands/dr-changeset.md +657 -0
- package/dist/integrations/claude_code/commands/dr-design.md +373 -0
- package/dist/integrations/claude_code/commands/dr-info.md +132 -0
- package/dist/integrations/claude_code/commands/dr-init.md +251 -0
- package/dist/integrations/claude_code/commands/dr-map.md +936 -0
- package/dist/integrations/claude_code/commands/dr-model.md +493 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +814 -0
- package/dist/integrations/claude_code/commands/dr-sync.md +279 -0
- package/dist/integrations/claude_code/commands/dr-validate.md +654 -0
- package/dist/integrations/claude_code/commands/dr-verify.md +559 -0
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +903 -0
- package/dist/integrations/claude_code/docs/QUICK_START.md +271 -0
- package/dist/integrations/claude_code/docs/README.md +347 -0
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +550 -0
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +620 -0
- package/dist/integrations/claude_code/skills/dr_03_security_layer/SKILL.md +642 -0
- package/dist/integrations/claude_code/skills/dr_04_application_layer/SKILL.md +911 -0
- package/dist/integrations/claude_code/skills/dr_05_technology_layer/SKILL.md +836 -0
- package/dist/integrations/claude_code/skills/dr_06_api_layer/SKILL.md +933 -0
- package/dist/integrations/claude_code/skills/dr_07_data_model_layer/SKILL.md +210 -0
- package/dist/integrations/claude_code/skills/dr_08_dataset_layer/SKILL.md +319 -0
- package/dist/integrations/claude_code/skills/dr_09_ux_layer/SKILL.md +319 -0
- package/dist/integrations/claude_code/skills/dr_10_navigation_layer/SKILL.md +318 -0
- package/dist/integrations/claude_code/skills/dr_11_apm_layer/SKILL.md +273 -0
- package/dist/integrations/claude_code/skills/dr_12_testing_layer/SKILL.md +273 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +187 -0
- package/dist/integrations/claude_code/skills/dr_codebase_memory/SKILL.md +590 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +295 -0
- package/dist/integrations/copilot-manager.js +328 -0
- package/dist/integrations/copilot-manager.js.map +7 -0
- package/dist/integrations/github_copilot/agents/dr-advisor.md +243 -0
- package/dist/integrations/github_copilot/agents/dr-architect.md +1567 -0
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +491 -0
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +539 -0
- package/dist/integrations/github_copilot/skills/dr_03_security_layer/SKILL.md +667 -0
- package/dist/integrations/github_copilot/skills/dr_04_application_layer/SKILL.md +670 -0
- package/dist/integrations/github_copilot/skills/dr_05_technology_layer/SKILL.md +660 -0
- package/dist/integrations/github_copilot/skills/dr_06_api_layer/SKILL.md +855 -0
- package/dist/integrations/github_copilot/skills/dr_07_data_model_layer/SKILL.md +171 -0
- package/dist/integrations/github_copilot/skills/dr_08_dataset_layer/SKILL.md +216 -0
- package/dist/integrations/github_copilot/skills/dr_09_ux_layer/SKILL.md +230 -0
- package/dist/integrations/github_copilot/skills/dr_10_navigation_layer/SKILL.md +228 -0
- package/dist/integrations/github_copilot/skills/dr_11_apm_layer/SKILL.md +234 -0
- package/dist/integrations/github_copilot/skills/dr_12_testing_layer/SKILL.md +230 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +187 -0
- package/dist/integrations/hash-utils.js +51 -0
- package/dist/integrations/hash-utils.js.map +7 -0
- package/dist/integrations/types.js +16 -0
- package/dist/integrations/types.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +278 -0
- package/dist/reports/model-layer-report-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +57 -0
- package/dist/reports/model-report-data.js.map +7 -0
- package/dist/reports/model-report-orchestrator.js +103 -0
- package/dist/reports/model-report-orchestrator.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +595 -0
- package/dist/schemas/bundled/analyzers/manifest.json +9 -0
- package/dist/schemas/bundled/api.json +3411 -0
- package/dist/schemas/bundled/apm.json +2371 -0
- package/dist/schemas/bundled/application.json +1253 -0
- package/dist/schemas/bundled/base.json +1622 -0
- package/dist/schemas/bundled/business.json +1279 -0
- package/dist/schemas/bundled/data-model.json +1298 -0
- package/dist/schemas/bundled/data-store.json +2025 -0
- package/dist/schemas/bundled/manifest.json +93 -0
- package/dist/schemas/bundled/motivation.json +1360 -0
- package/dist/schemas/bundled/navigation.json +1874 -0
- package/dist/schemas/bundled/security.json +3488 -0
- package/dist/schemas/bundled/technology.json +2067 -0
- package/dist/schemas/bundled/testing.json +3120 -0
- package/dist/schemas/bundled/ux.json +3592 -0
- package/dist/server/schemas.js +202 -0
- package/dist/server/schemas.js.map +7 -0
- package/dist/server/server.js +1336 -252
- package/dist/server/server.js.map +3 -3
- package/dist/server/telemetry-middleware.js +22 -15
- package/dist/server/telemetry-middleware.js.map +2 -2
- package/dist/server-entry.js +65 -0
- package/dist/server-entry.js.map +7 -0
- package/dist/telemetry/config.js +89 -0
- package/dist/telemetry/config.js.map +7 -0
- package/dist/telemetry/console-interceptor.js.map +1 -1
- package/dist/telemetry/detect.js +24 -0
- package/dist/telemetry/detect.js.map +7 -0
- package/dist/telemetry/fetch-otlp-exporter.js +112 -0
- package/dist/telemetry/fetch-otlp-exporter.js.map +7 -0
- package/dist/telemetry/index.js +153 -3
- package/dist/telemetry/index.js.map +2 -2
- package/dist/telemetry/resilient-exporter.js +22 -8
- package/dist/telemetry/resilient-exporter.js.map +2 -2
- package/dist/telemetry/resilient-log-exporter.js +17 -8
- package/dist/telemetry/resilient-log-exporter.js.map +2 -2
- package/dist/telemetry/test-instrumentation.js.map +2 -2
- package/dist/types/index.js +84 -0
- package/dist/types/index.js.map +3 -3
- package/dist/types/source-reference.js +1 -0
- package/dist/types/source-reference.js.map +7 -0
- package/dist/utils/chat-logger.js +292 -0
- package/dist/utils/chat-logger.js.map +7 -0
- package/dist/utils/claude-stream.js +8 -0
- package/dist/utils/claude-stream.js.map +7 -0
- package/dist/utils/date-utils.js +7 -0
- package/dist/utils/date-utils.js.map +7 -0
- package/dist/utils/element-utils.js.map +1 -1
- package/dist/utils/errors.js +88 -15
- package/dist/utils/errors.js.map +3 -3
- package/dist/utils/export-python-annotations.js +82 -0
- package/dist/utils/export-python-annotations.js.map +7 -0
- package/dist/utils/file-io.js +7 -2
- package/dist/utils/file-io.js.map +2 -2
- package/dist/utils/file-lock.js +156 -0
- package/dist/utils/file-lock.js.map +7 -0
- package/dist/utils/id-generator.js +18 -0
- package/dist/utils/id-generator.js.map +7 -0
- package/dist/utils/layer-name-formatter.js +16 -0
- package/dist/utils/layer-name-formatter.js.map +7 -0
- package/dist/utils/markdown-anchor.js +7 -0
- package/dist/utils/markdown-anchor.js.map +7 -0
- package/dist/utils/markdown-link.js +12 -0
- package/dist/utils/markdown-link.js.map +7 -0
- package/dist/utils/markdown-table.js +17 -0
- package/dist/utils/markdown-table.js.map +7 -0
- package/dist/utils/mermaid-utils.js +11 -0
- package/dist/utils/mermaid-utils.js.map +7 -0
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/source-reference.js +52 -0
- package/dist/utils/source-reference.js.map +7 -0
- package/dist/utils/spec-installer.js +40 -53
- package/dist/utils/spec-installer.js.map +2 -2
- package/dist/utils/spec-version.js +5 -1
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/utils/string-utils.js +12 -0
- package/dist/utils/string-utils.js.map +7 -0
- package/dist/utils/table-formatting.js +19 -0
- package/dist/utils/table-formatting.js.map +7 -0
- package/dist/validators/index.js +4 -0
- package/dist/validators/index.js.map +2 -2
- package/dist/validators/naming-validator.js +20 -23
- package/dist/validators/naming-validator.js.map +2 -2
- package/dist/validators/reference-validator.js +23 -38
- package/dist/validators/reference-validator.js.map +2 -2
- package/dist/validators/relationship-schema-validator.js +243 -0
- package/dist/validators/relationship-schema-validator.js.map +7 -0
- package/dist/validators/schema-validator.js +168 -68
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/semantic-validator.js +36 -60
- package/dist/validators/semantic-validator.js.map +2 -2
- package/dist/validators/types.js.map +2 -2
- package/dist/validators/validation-formatter.js +159 -0
- package/dist/validators/validation-formatter.js.map +7 -0
- package/dist/validators/validator.js +24 -38
- package/dist/validators/validator.js.map +2 -2
- package/dist/viewer/README.md +139 -0
- package/dist/viewer/assets/main-CY0Xufuj.css +1 -0
- package/dist/viewer/assets/main-DtgErnwF.js +81 -0
- package/dist/viewer/assets/vendor-CxKvDp2x.js +17 -0
- package/dist/viewer/favicon.svg +11 -0
- package/dist/viewer/fonts/inter/Inter-Black.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Bold.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-ExtraBold.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Light.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Medium.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Regular.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-SemiBold.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2 +0 -0
- package/dist/viewer/index.html +15 -0
- package/dist/viewer/manifest.json +99 -0
- package/dist/viewer/version.json +1 -0
- package/package.json +42 -16
- package/PYTHON_CLI_PARITY.md +0 -265
- package/PYTHON_CLI_STRUCTURE_UPDATE.md +0 -246
- package/biome.json +0 -42
- package/bunfig.toml +0 -3
- package/dist/ai/claude-client.js +0 -71
- package/dist/ai/claude-client.js.map +0 -7
- package/dist/ai/context-provider.js +0 -129
- package/dist/ai/context-provider.js.map +0 -7
- package/dist/ai/tools.js +0 -231
- package/dist/ai/tools.js.map +0 -7
- package/dist/commands/migrate.js +0 -36
- package/dist/commands/migrate.js.map +0 -7
- package/dist/commands/project.js +0 -74
- package/dist/commands/project.js.map +0 -7
- package/dist/core/active-changeset.js +0 -52
- package/dist/core/active-changeset.js.map +0 -7
- package/dist/core/projection-engine.js +0 -288
- package/dist/core/projection-engine.js.map +0 -7
- package/dist/schemas/bundled/01-motivation-layer.schema.json +0 -942
- package/dist/schemas/bundled/02-business-layer.schema.json +0 -1293
- package/dist/schemas/bundled/03-security-layer.schema.json +0 -2177
- package/dist/schemas/bundled/04-application-layer.schema.json +0 -1328
- package/dist/schemas/bundled/05-technology-layer.schema.json +0 -1465
- package/dist/schemas/bundled/06-api-layer.schema.json +0 -1743
- package/dist/schemas/bundled/07-data-model-layer.schema.json +0 -1253
- package/dist/schemas/bundled/08-datastore-layer.schema.json +0 -892
- package/dist/schemas/bundled/09-ux-layer.schema.json +0 -1506
- package/dist/schemas/bundled/10-navigation-layer.schema.json +0 -843
- package/dist/schemas/bundled/11-apm-observability-layer.schema.json +0 -1430
- package/dist/schemas/bundled/12-testing-layer.schema.json +0 -1091
- package/dist/schemas/bundled/common/layer-extensions.schema.json +0 -266
- package/dist/schemas/bundled/common/predicates.schema.json +0 -493
- package/dist/schemas/bundled/common/relationships.schema.json +0 -791
- package/dist/schemas/bundled/common/source-references.schema.json +0 -71
- package/dist/schemas/bundled/link-registry.json +0 -1839
- package/dist/schemas/bundled/link-registry.schema.json +0 -339
- package/dist/schemas/bundled/relationship-catalog.json +0 -1302
- package/dist/schemas/bundled/relationship-type.schema.json +0 -221
- package/dist/utils/model-path.js +0 -22
- package/dist/utils/model-path.js.map +0 -7
- package/docs/TEST-INSTRUMENTATION.md +0 -405
- package/docs/telemetry.md +0 -427
- package/test-debug.js +0 -33
- package/test-special.mjs +0 -46
- 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": "
|
|
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 {
|
|
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
|
|
9
|
+
return this.validateRelationshipPredicates(model, result), this.validateApmInferredWithoutSdk(model, result), result;
|
|
32
10
|
}
|
|
33
11
|
/**
|
|
34
|
-
*
|
|
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
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
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
|
-
|
|
52
|
-
for (const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
5
|
-
"mappings": "AAIA,SAAS,wBAAwB;AAEjC,SAAS,
|
|
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:
|
|
5
|
-
"mappings": "
|
|
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 {
|
|
7
|
-
|
|
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
|
|
18
|
+
* Validate a complete model through all 5 stages
|
|
18
19
|
*/
|
|
19
20
|
async validateModel(model) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
}
|
|
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]"),
|
|
48
|
-
}
|
|
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]"),
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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 {
|