@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.
- 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 +206 -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 +155 -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 +37 -13
- 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
package/dist/core/model.js
CHANGED
|
@@ -1,138 +1,209 @@
|
|
|
1
1
|
import { Layer } from "./layer.js";
|
|
2
2
|
import { Manifest } from "./manifest.js";
|
|
3
3
|
import { Element } from "./element.js";
|
|
4
|
-
import {
|
|
4
|
+
import { GraphModel } from "./graph-model.js";
|
|
5
|
+
import { VirtualProjectionEngine } from "./virtual-projection.js";
|
|
6
|
+
import { StagedChangesetStorage } from "./staged-changeset-storage.js";
|
|
5
7
|
import { Relationships } from "./relationships.js";
|
|
6
|
-
import {
|
|
8
|
+
import { CANONICAL_LAYER_NAMES } from "./layers.js";
|
|
7
9
|
import { ensureDir, writeFile } from "../utils/file-io.js";
|
|
8
|
-
import {
|
|
10
|
+
import { getCliVersion } from "../utils/spec-version.js";
|
|
11
|
+
import { startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
|
|
9
12
|
import { findProjectRoot } from "../utils/project-paths.js";
|
|
13
|
+
import { getNodeType } from "../generated/node-types.js";
|
|
10
14
|
import path from "node:path";
|
|
11
15
|
import fs from "node:fs/promises";
|
|
16
|
+
import crypto from "node:crypto";
|
|
17
|
+
function deterministicUUID(path2) {
|
|
18
|
+
const hash = crypto.createHash("sha256").update(path2).digest("hex");
|
|
19
|
+
return [
|
|
20
|
+
hash.slice(0, 8),
|
|
21
|
+
hash.slice(8, 12),
|
|
22
|
+
"4" + hash.slice(13, 16),
|
|
23
|
+
// version 4 marker
|
|
24
|
+
(parseInt(hash.slice(16, 18), 16) & 63 | 128).toString(16) + hash.slice(18, 20),
|
|
25
|
+
// variant
|
|
26
|
+
hash.slice(20, 32)
|
|
27
|
+
].join("-");
|
|
28
|
+
}
|
|
29
|
+
function titleToSnakeCase(title) {
|
|
30
|
+
return title.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
function typeToSnakeStem(specNodeId, type) {
|
|
33
|
+
if (specNodeId) {
|
|
34
|
+
const nodeType = getNodeType(specNodeId);
|
|
35
|
+
if (nodeType?.title)
|
|
36
|
+
return titleToSnakeCase(nodeType.title);
|
|
37
|
+
}
|
|
38
|
+
return type;
|
|
39
|
+
}
|
|
12
40
|
const isTelemetryEnabled = !1;
|
|
13
41
|
class Model {
|
|
14
42
|
rootPath;
|
|
15
43
|
manifest;
|
|
44
|
+
graph;
|
|
16
45
|
layers;
|
|
17
46
|
relationships;
|
|
18
47
|
lazyLoad;
|
|
19
48
|
loadedLayers;
|
|
20
|
-
|
|
21
|
-
|
|
49
|
+
virtualProjectionEngine;
|
|
50
|
+
/** ID of the currently active changeset, or null if none is active. Set during load(). */
|
|
51
|
+
_activeChangesetId = null;
|
|
52
|
+
/** Layer names that exist only in staged changes (not yet committed to base model). */
|
|
53
|
+
_stagedLayerNames = /* @__PURE__ */ new Set();
|
|
22
54
|
constructor(rootPath, manifest, options = {}) {
|
|
23
|
-
this.rootPath = rootPath, this.manifest = manifest, this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayers = /* @__PURE__ */ new Set();
|
|
55
|
+
this.rootPath = rootPath, this.manifest = manifest, this.graph = new GraphModel(), this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayers = /* @__PURE__ */ new Set();
|
|
24
56
|
}
|
|
25
57
|
/**
|
|
26
|
-
* Get
|
|
58
|
+
* Get the committed base layer (no staging projection).
|
|
59
|
+
* Used internally by VirtualProjectionEngine and commit() to access the raw base model
|
|
60
|
+
* without triggering infinite recursion or double-projection.
|
|
27
61
|
*/
|
|
28
|
-
async
|
|
62
|
+
async getBaseLayer(name) {
|
|
29
63
|
return this.lazyLoad && !this.loadedLayers.has(name) && await this.loadLayer(name), this.layers.get(name);
|
|
30
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Get a layer, transparently merging staged changes when an active changeset exists.
|
|
67
|
+
* Falls back to the committed base layer if no changeset is active or projection fails.
|
|
68
|
+
*/
|
|
69
|
+
async getLayer(name) {
|
|
70
|
+
if (this.lazyLoad && !this.loadedLayers.has(name) && await this.loadLayer(name), this._activeChangesetId)
|
|
71
|
+
try {
|
|
72
|
+
return await this.getVirtualProjectionEngine().projectLayer(this, this._activeChangesetId, name);
|
|
73
|
+
} catch {
|
|
74
|
+
return this.layers.get(name);
|
|
75
|
+
}
|
|
76
|
+
return this.layers.get(name);
|
|
77
|
+
}
|
|
31
78
|
/**
|
|
32
79
|
* Get an element by ID across all layers
|
|
80
|
+
* Supports both UUID and semantic ID (layer.type.kebab-name) lookup
|
|
33
81
|
*/
|
|
34
82
|
getElementById(id) {
|
|
35
83
|
for (const layer of this.layers.values()) {
|
|
36
|
-
const element = layer.
|
|
84
|
+
const element = layer.getElement(id);
|
|
37
85
|
if (element)
|
|
38
86
|
return element;
|
|
39
87
|
}
|
|
40
88
|
}
|
|
41
89
|
/**
|
|
42
|
-
* Get projection engine (lazily initialized)
|
|
90
|
+
* Get virtual projection engine (lazily initialized)
|
|
43
91
|
*/
|
|
44
|
-
|
|
45
|
-
return this.
|
|
92
|
+
getVirtualProjectionEngine() {
|
|
93
|
+
return this.virtualProjectionEngine || (this.virtualProjectionEngine = new VirtualProjectionEngine(this.rootPath)), this.virtualProjectionEngine;
|
|
46
94
|
}
|
|
47
95
|
/**
|
|
48
|
-
*
|
|
96
|
+
* Returns the active changeset ID, or null if no changeset is active.
|
|
97
|
+
* Used by commands that need to project the full model (e.g. validate).
|
|
49
98
|
*/
|
|
50
|
-
|
|
51
|
-
return this.
|
|
99
|
+
getActiveChangesetId() {
|
|
100
|
+
return this._activeChangesetId;
|
|
52
101
|
}
|
|
53
102
|
/**
|
|
54
|
-
* Load a layer from disk (
|
|
55
|
-
* Supports reading layer paths from manifest for Python CLI compatibility
|
|
103
|
+
* Load a layer from disk (model/XX_layername/*.yaml files)
|
|
56
104
|
*/
|
|
57
105
|
async loadLayer(name) {
|
|
58
|
-
|
|
59
|
-
"layer.name": name
|
|
60
|
-
}) : null;
|
|
61
|
-
try {
|
|
106
|
+
await startActiveSpan("layer.load", async (layerSpan) => {
|
|
62
107
|
const fs2 = await import("fs/promises"), yaml = await import("yaml");
|
|
63
108
|
let layerPath = null;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await fs2.access(fullPath), layerPath = fullPath;
|
|
70
|
-
} catch {
|
|
71
|
-
process.env.DEBUG && console.debug(`Manifest specifies path ${layerConfig.path} but it doesn't exist`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!layerPath) {
|
|
76
|
-
const modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
77
|
-
try {
|
|
78
|
-
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
79
|
-
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
109
|
+
const modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
110
|
+
try {
|
|
111
|
+
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
112
|
+
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
113
|
+
} catch {
|
|
82
114
|
}
|
|
83
115
|
if (!layerPath)
|
|
84
116
|
return;
|
|
85
|
-
const layer = new Layer(name
|
|
117
|
+
const layer = new Layer(name, this.graph);
|
|
118
|
+
let parseErrorCount = 0;
|
|
119
|
+
const files = await fs2.readdir(layerPath);
|
|
86
120
|
for (const file of files)
|
|
87
121
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
88
|
-
const filePath = `${layerPath}/${file}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
122
|
+
const filePath = `${layerPath}/${file}`;
|
|
123
|
+
try {
|
|
124
|
+
const yamlContent = await fs2.readFile(filePath, "utf-8"), elements = yaml.parse(yamlContent);
|
|
125
|
+
if (elements && typeof elements == "object") {
|
|
126
|
+
for (const [key, element] of Object.entries(elements))
|
|
127
|
+
if (element && typeof element == "object") {
|
|
128
|
+
const el = element, isSlugPattern = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/.test(el.id || ""), isUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(el.id || "");
|
|
129
|
+
let elementId, elementPath, extractedType;
|
|
130
|
+
if (el.path && isUUIDPattern) {
|
|
131
|
+
elementId = el.id, elementPath = el.path;
|
|
132
|
+
const pathParts = el.path.split(".");
|
|
133
|
+
pathParts.length >= 3 && (extractedType = pathParts[1]);
|
|
134
|
+
} else if (el.path && !isUUIDPattern) {
|
|
135
|
+
elementPath = el.path, elementId = deterministicUUID(el.path);
|
|
136
|
+
const pathParts = el.path.split(".");
|
|
137
|
+
pathParts.length >= 3 && (extractedType = pathParts[1]);
|
|
138
|
+
} else if (isSlugPattern) {
|
|
139
|
+
elementPath = el.id, elementId = deterministicUUID(el.id);
|
|
140
|
+
const parts = el.id.split(".");
|
|
141
|
+
parts.length >= 3 && (extractedType = parts[1]);
|
|
142
|
+
} else if (isUUIDPattern) {
|
|
143
|
+
elementId = el.id;
|
|
144
|
+
const layerId2 = el.layer_id || name, rawType = el.type || "", kebabName = (el.name || key).toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
145
|
+
elementPath = `${layerId2}.${rawType}.${kebabName}`, extractedType = rawType || void 0;
|
|
146
|
+
} else {
|
|
147
|
+
const layerId2 = el.layer_id || name, rawType = el.type || (typeof el.id == "string" && el.id.includes(".") ? el.id.split(".")[1] : "") || "", kebabName = (el.name || key).toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
148
|
+
if (elementPath = `${layerId2}.${rawType || "element"}.${kebabName}`, elementId = deterministicUUID(elementPath), rawType)
|
|
149
|
+
extractedType = rawType;
|
|
150
|
+
else if (typeof el.id == "string" && el.id.includes(".")) {
|
|
151
|
+
const parts = el.id.split(".");
|
|
152
|
+
parts.length >= 3 && (extractedType = parts[1]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const elementName = el.name || key, elementType = el.type || extractedType, layerId = el.layer_id || name, newElement = new Element({
|
|
156
|
+
id: elementId,
|
|
157
|
+
path: elementPath,
|
|
158
|
+
spec_node_id: el.spec_node_id,
|
|
159
|
+
layer_id: layerId,
|
|
160
|
+
attributes: el.attributes,
|
|
161
|
+
source_reference: el.source_reference,
|
|
162
|
+
metadata: el.metadata,
|
|
163
|
+
name: elementName,
|
|
164
|
+
type: elementType,
|
|
165
|
+
description: el.description || "",
|
|
166
|
+
relationships: el.relationships || [],
|
|
167
|
+
references: el.references || [],
|
|
168
|
+
layer: layerId
|
|
169
|
+
});
|
|
170
|
+
layer.addElement(newElement);
|
|
97
171
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
description: el.description || el.documentation || "",
|
|
103
|
-
properties: el.properties || {},
|
|
104
|
-
relationships: el.relationships || [],
|
|
105
|
-
references: el.references || [],
|
|
106
|
-
layer: name,
|
|
107
|
-
// Track layer (Python CLI compatibility)
|
|
108
|
-
filePath,
|
|
109
|
-
// Track source file (Python CLI compatibility)
|
|
110
|
-
rawData: el
|
|
111
|
-
// Preserve raw YAML data (Python CLI compatibility)
|
|
112
|
-
});
|
|
113
|
-
layer.addElement(newElement);
|
|
114
|
-
}
|
|
172
|
+
}
|
|
173
|
+
} catch (fileError) {
|
|
174
|
+
const errorMsg = fileError instanceof Error ? fileError.message : String(fileError);
|
|
175
|
+
console.error(`Warning: Failed to parse ${filePath}: ${errorMsg}`), parseErrorCount++;
|
|
115
176
|
}
|
|
116
177
|
}
|
|
117
|
-
this.layers.set(name, layer), this.loadedLayers.add(name),
|
|
118
|
-
}
|
|
119
|
-
isTelemetryEnabled && endSpan(layerSpan);
|
|
120
|
-
}
|
|
178
|
+
parseErrorCount > 0 && console.error(`Warning: Layer '${name}' loaded with ${parseErrorCount} YAML parse error(s)`), this.layers.set(name, layer), this.loadedLayers.add(name), layerSpan.setAttribute("layer.element_count", layer.elements.size);
|
|
179
|
+
}, { "layer.name": name });
|
|
121
180
|
}
|
|
122
181
|
/**
|
|
123
182
|
* Add a layer to the model
|
|
124
183
|
*/
|
|
125
184
|
addLayer(layer) {
|
|
126
|
-
|
|
185
|
+
if (layer.graph.getNodeCount() > 0) {
|
|
186
|
+
for (const node of layer.graph.nodes.values())
|
|
187
|
+
this.graph.addNode(node);
|
|
188
|
+
for (const edge of layer.graph.edges.values())
|
|
189
|
+
this.graph.addEdge(edge);
|
|
190
|
+
}
|
|
191
|
+
layer.graph = this.graph, this.layers.set(layer.name, layer), this.loadedLayers.add(layer.name);
|
|
127
192
|
}
|
|
128
193
|
/**
|
|
129
|
-
* Get all
|
|
194
|
+
* Get all layer names, including layers that exist only in staged changes.
|
|
195
|
+
* When an active changeset introduces elements for a layer with no committed data,
|
|
196
|
+
* that layer name is included so findElementLayer() and other callers can discover it.
|
|
130
197
|
*/
|
|
131
198
|
getLayerNames() {
|
|
132
|
-
|
|
199
|
+
const base = Array.from(this.layers.keys());
|
|
200
|
+
if (this._stagedLayerNames.size === 0)
|
|
201
|
+
return base;
|
|
202
|
+
const staged = Array.from(this._stagedLayerNames).filter((l) => !this.layers.has(l));
|
|
203
|
+
return [...base, ...staged];
|
|
133
204
|
}
|
|
134
205
|
/**
|
|
135
|
-
* Save a layer to disk (
|
|
206
|
+
* Save a layer to disk (model/XX_layername/*.yaml)
|
|
136
207
|
*/
|
|
137
208
|
async saveLayer(name) {
|
|
138
209
|
const layer = this.layers.get(name);
|
|
@@ -140,40 +211,14 @@ class Model {
|
|
|
140
211
|
throw new Error(`Layer ${name} not found`);
|
|
141
212
|
const fs2 = await import("fs/promises"), yaml = await import("yaml");
|
|
142
213
|
let layerPath = null;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
await fs2.access(fullPath), layerPath = fullPath;
|
|
149
|
-
} catch {
|
|
150
|
-
await ensureDir(fullPath), layerPath = fullPath;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (!layerPath) {
|
|
155
|
-
const modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
156
|
-
try {
|
|
157
|
-
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
158
|
-
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
159
|
-
} catch {
|
|
160
|
-
}
|
|
214
|
+
const modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
215
|
+
try {
|
|
216
|
+
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
217
|
+
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
218
|
+
} catch {
|
|
161
219
|
}
|
|
162
220
|
if (!layerPath) {
|
|
163
|
-
const layerNameWithoutPrefix = name.replace(/^\d{2}-/, ""), index =
|
|
164
|
-
"motivation",
|
|
165
|
-
"business",
|
|
166
|
-
"security",
|
|
167
|
-
"application",
|
|
168
|
-
"technology",
|
|
169
|
-
"api",
|
|
170
|
-
"data-model",
|
|
171
|
-
"datastore",
|
|
172
|
-
"ux",
|
|
173
|
-
"navigation",
|
|
174
|
-
"apm",
|
|
175
|
-
"testing"
|
|
176
|
-
].indexOf(layerNameWithoutPrefix);
|
|
221
|
+
const layerNameWithoutPrefix = name.replace(/^\d{2}-/, ""), index = CANONICAL_LAYER_NAMES.indexOf(layerNameWithoutPrefix);
|
|
177
222
|
if (index >= 0) {
|
|
178
223
|
const orderNum = String(index + 1).padStart(2, "0");
|
|
179
224
|
layerPath = `${this.rootPath}/documentation-robotics/model/${orderNum}_${layerNameWithoutPrefix}`, await ensureDir(layerPath);
|
|
@@ -184,24 +229,41 @@ class Model {
|
|
|
184
229
|
const existingFiles = await fs2.readdir(layerPath);
|
|
185
230
|
for (const file of existingFiles)
|
|
186
231
|
(file.endsWith(".yaml") || file.endsWith(".yml")) && await fs2.unlink(`${layerPath}/${file}`);
|
|
187
|
-
} catch {
|
|
232
|
+
} catch (err) {
|
|
233
|
+
if (!(err?.code === "ENOENT"))
|
|
234
|
+
throw new Error(`Failed to clean existing YAML files: ${err instanceof Error ? err.message : String(err)}`);
|
|
188
235
|
}
|
|
189
236
|
const elementsByType = /* @__PURE__ */ new Map();
|
|
190
237
|
for (const element of layer.elements.values()) {
|
|
191
|
-
const
|
|
192
|
-
elementsByType.has(
|
|
238
|
+
const typeKey = element.type;
|
|
239
|
+
if (!elementsByType.has(typeKey)) {
|
|
240
|
+
const stem = typeToSnakeStem(element.spec_node_id, element.type);
|
|
241
|
+
elementsByType.set(typeKey, { stem, elements: [] });
|
|
242
|
+
}
|
|
243
|
+
elementsByType.get(typeKey).elements.push(element);
|
|
193
244
|
}
|
|
194
|
-
|
|
195
|
-
|
|
245
|
+
const INTERNAL_FIELDS = /* @__PURE__ */ new Set([
|
|
246
|
+
"__references__",
|
|
247
|
+
"__relationships__",
|
|
248
|
+
"source",
|
|
249
|
+
"x-source-reference"
|
|
250
|
+
]);
|
|
251
|
+
for (const [, { stem, elements }] of elementsByType) {
|
|
252
|
+
const filename = `${stem}.yaml`, filePath = `${layerPath}/${filename}`, yamlData = {};
|
|
196
253
|
for (const element of elements) {
|
|
197
|
-
const
|
|
254
|
+
const json = element.toJSON(), cleanAttrs = json.attributes ? Object.fromEntries(Object.entries(json.attributes).filter(([k]) => !INTERNAL_FIELDS.has(k))) : void 0, key = element.path || element.id;
|
|
198
255
|
yamlData[key] = {
|
|
199
|
-
id:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
256
|
+
id: json.id,
|
|
257
|
+
path: json.path,
|
|
258
|
+
spec_node_id: json.spec_node_id,
|
|
259
|
+
type: json.type,
|
|
260
|
+
layer_id: json.layer_id,
|
|
261
|
+
name: json.name,
|
|
262
|
+
...json.description && { description: json.description },
|
|
263
|
+
...cleanAttrs && Object.keys(cleanAttrs).length > 0 && { attributes: cleanAttrs },
|
|
264
|
+
...json.source_reference && { source_reference: json.source_reference },
|
|
265
|
+
...json.metadata && { metadata: json.metadata },
|
|
266
|
+
...json.references && json.references.length > 0 && { references: json.references }
|
|
205
267
|
// Relationships are now stored in centralized relationships.yaml, not inline
|
|
206
268
|
};
|
|
207
269
|
}
|
|
@@ -234,15 +296,14 @@ class Model {
|
|
|
234
296
|
}
|
|
235
297
|
}
|
|
236
298
|
/**
|
|
237
|
-
* Save the manifest to disk (
|
|
238
|
-
* Preserves Python CLI metadata fields for compatibility
|
|
299
|
+
* Save the manifest to disk (documentation-robotics/model/manifest.yaml)
|
|
239
300
|
*/
|
|
240
301
|
async saveManifest() {
|
|
241
302
|
this.manifest.updateModified();
|
|
242
303
|
const yaml = await import("yaml"), manifestPath = `${this.rootPath}/documentation-robotics/model/manifest.yaml`, yamlData = {
|
|
243
304
|
version: this.manifest.version,
|
|
244
305
|
schema: "documentation-robotics-v1",
|
|
245
|
-
cli_version:
|
|
306
|
+
cli_version: getCliVersion(),
|
|
246
307
|
spec_version: this.manifest.specVersion,
|
|
247
308
|
created: this.manifest.created,
|
|
248
309
|
updated: this.manifest.modified,
|
|
@@ -252,44 +313,32 @@ class Model {
|
|
|
252
313
|
version: this.manifest.version
|
|
253
314
|
},
|
|
254
315
|
documentation: ".dr/README.md",
|
|
255
|
-
layers:
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
];
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
enabled: !0,
|
|
280
|
-
...layer && { elements: this.getLayerElementCounts(layer) }
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
} else
|
|
284
|
-
for (const [layerName, layerConfig] of Object.entries(yamlData.layers)) {
|
|
285
|
-
const layer = this.layers.get(layerName);
|
|
286
|
-
layer && (layerConfig.elements = this.getLayerElementCounts(layer));
|
|
287
|
-
}
|
|
288
|
-
if (this.manifest.statistics) {
|
|
289
|
-
const stats = { ...this.manifest.statistics };
|
|
290
|
-
stats.total_elements = Array.from(this.layers.values()).reduce((sum, layer) => sum + layer.elements.size, 0), yamlData.statistics = stats;
|
|
316
|
+
layers: {}
|
|
317
|
+
}, layerOrder = [
|
|
318
|
+
"motivation",
|
|
319
|
+
"business",
|
|
320
|
+
"security",
|
|
321
|
+
"application",
|
|
322
|
+
"technology",
|
|
323
|
+
"api",
|
|
324
|
+
"data-model",
|
|
325
|
+
"data-store",
|
|
326
|
+
"ux",
|
|
327
|
+
"navigation",
|
|
328
|
+
"apm",
|
|
329
|
+
"testing"
|
|
330
|
+
];
|
|
331
|
+
for (let i = 0; i < layerOrder.length; i++) {
|
|
332
|
+
const layerName = layerOrder[i], layer = this.layers.get(layerName), orderNum = String(i + 1).padStart(2, "0");
|
|
333
|
+
yamlData.layers[layerName] = {
|
|
334
|
+
order: i + 1,
|
|
335
|
+
name: layerName.charAt(0).toUpperCase() + layerName.slice(1).replace("-", " "),
|
|
336
|
+
path: `documentation-robotics/model/${orderNum}_${layerName}/`,
|
|
337
|
+
enabled: !0,
|
|
338
|
+
...layer && { elements: this.getLayerElementCounts(layer) }
|
|
339
|
+
};
|
|
291
340
|
}
|
|
292
|
-
this.manifest.
|
|
341
|
+
this.manifest.changeset_history && this.manifest.changeset_history.length > 0 && (yamlData.changeset_history = this.manifest.changeset_history), await ensureDir(`${this.rootPath}/documentation-robotics/model`), await writeFile(manifestPath, yaml.stringify(yamlData));
|
|
293
342
|
}
|
|
294
343
|
getLayerElementCounts(layer) {
|
|
295
344
|
const counts = {};
|
|
@@ -302,11 +351,33 @@ class Model {
|
|
|
302
351
|
*/
|
|
303
352
|
async loadRelationships() {
|
|
304
353
|
const fs2 = await import("fs/promises"), yaml = await import("yaml"), relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;
|
|
354
|
+
let data;
|
|
305
355
|
try {
|
|
306
|
-
const yamlContent = await fs2.readFile(relationshipsPath, "utf-8")
|
|
307
|
-
|
|
356
|
+
const yamlContent = await fs2.readFile(relationshipsPath, "utf-8");
|
|
357
|
+
data = yaml.parse(yamlContent);
|
|
308
358
|
} catch (err) {
|
|
309
|
-
err.code !== "ENOENT"
|
|
359
|
+
if (err.code !== "ENOENT")
|
|
360
|
+
throw new Error(`Failed to load relationships.yaml: ${err instanceof Error ? err.message : String(err)}`);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (!Array.isArray(data)) {
|
|
364
|
+
console.warn(`Warning: relationships.yaml contains non-array content (received ${data === null ? "null" : typeof data}). Expected an array of relationships. Skipping relationship loading.`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
this.relationships = Relationships.fromArray(data);
|
|
368
|
+
for (let i = 0; i < data.length; i++) {
|
|
369
|
+
const rel = data[i], edge = {
|
|
370
|
+
id: `rel-${i}-${crypto.randomBytes(4).toString("hex")}`,
|
|
371
|
+
source: rel.source,
|
|
372
|
+
destination: rel.target,
|
|
373
|
+
predicate: rel.predicate,
|
|
374
|
+
properties: rel.properties,
|
|
375
|
+
category: rel.category
|
|
376
|
+
};
|
|
377
|
+
try {
|
|
378
|
+
this.graph.addEdge(edge);
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
310
381
|
}
|
|
311
382
|
}
|
|
312
383
|
/**
|
|
@@ -366,88 +437,224 @@ Run "dr init" to create a model.`);
|
|
|
366
437
|
* @returns Loaded Model instance
|
|
367
438
|
*/
|
|
368
439
|
static async load(startPath, options = {}) {
|
|
369
|
-
|
|
370
|
-
"
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
version: legacyData.project?.version || legacyData.version || "0.1.0",
|
|
378
|
-
specVersion: legacyData.spec_version || "0.6.0",
|
|
379
|
-
created: legacyData.created || (/* @__PURE__ */ new Date()).toISOString(),
|
|
380
|
-
modified: legacyData.updated || (/* @__PURE__ */ new Date()).toISOString(),
|
|
381
|
-
// Preserve Python CLI metadata fields
|
|
382
|
-
layers: legacyData.layers,
|
|
383
|
-
statistics: legacyData.statistics || {
|
|
384
|
-
total_elements: 0,
|
|
385
|
-
total_relationships: 0,
|
|
386
|
-
completeness: 0,
|
|
387
|
-
last_validation: null,
|
|
388
|
-
validation_status: "not_validated"
|
|
389
|
-
},
|
|
390
|
-
cross_references: legacyData.cross_references || {
|
|
391
|
-
total: 0,
|
|
392
|
-
by_type: {}
|
|
393
|
-
},
|
|
394
|
-
conventions: legacyData.conventions,
|
|
395
|
-
upgrade_history: legacyData.upgrade_history || []
|
|
440
|
+
return startActiveSpan("model.load", async (span) => {
|
|
441
|
+
const { projectRoot, manifestPath } = await Model.resolveModelPaths(startPath), yaml = await import("yaml"), yamlContent = await fs.readFile(manifestPath, "utf-8"), manifestYaml = yaml.parse(yamlContent), manifestData = {
|
|
442
|
+
name: manifestYaml.project?.name || "Unnamed Model",
|
|
443
|
+
description: manifestYaml.project?.description || "",
|
|
444
|
+
version: manifestYaml.project?.version || manifestYaml.version || "0.1.0",
|
|
445
|
+
specVersion: manifestYaml.spec_version || "0.6.0",
|
|
446
|
+
created: manifestYaml.created || (/* @__PURE__ */ new Date()).toISOString(),
|
|
447
|
+
modified: manifestYaml.updated || (/* @__PURE__ */ new Date()).toISOString()
|
|
396
448
|
}, manifest = new Manifest(manifestData), model = new Model(projectRoot, manifest, options);
|
|
397
449
|
if (!options.lazyLoad)
|
|
398
450
|
try {
|
|
399
|
-
if (
|
|
400
|
-
for (const layerName of Object.keys(
|
|
401
|
-
|
|
451
|
+
if (manifestYaml.layers)
|
|
452
|
+
for (const layerName of Object.keys(manifestYaml.layers))
|
|
453
|
+
manifestYaml.layers[layerName].enabled !== !1 && (!options.layers || options.layers.includes(layerName)) && await model.loadLayer(layerName);
|
|
402
454
|
else {
|
|
403
455
|
const modelDir = manifestPath.replace("/manifest.yaml", ""), entries = await fs.readdir(modelDir, { withFileTypes: !0 });
|
|
404
456
|
for (const entry of entries)
|
|
405
457
|
if (entry.isDirectory() && entry.name.match(/^\d{2}_/)) {
|
|
406
458
|
const layerName = entry.name.replace(/^\d{2}_/, "");
|
|
407
|
-
await model.loadLayer(layerName);
|
|
459
|
+
(!options.layers || options.layers.includes(layerName)) && await model.loadLayer(layerName);
|
|
408
460
|
}
|
|
409
461
|
}
|
|
410
462
|
} catch (e) {
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
error.includes("ENOENT") || console.debug(`Warning: Failed to load layers directory: ${error}`);
|
|
414
|
-
}
|
|
463
|
+
if (!(e?.code === "ENOENT"))
|
|
464
|
+
throw new Error(`Failed to load layers: ${e instanceof Error ? e.message : String(e)}`);
|
|
415
465
|
}
|
|
416
466
|
await model.loadRelationships();
|
|
467
|
+
const activePath = path.join(projectRoot, "documentation-robotics", "changesets", ".active");
|
|
468
|
+
try {
|
|
469
|
+
const activeId = (await fs.readFile(activePath, "utf-8")).trim();
|
|
470
|
+
if (activeId) {
|
|
471
|
+
model._activeChangesetId = activeId;
|
|
472
|
+
const changeset = await new StagedChangesetStorage(projectRoot).load(activeId);
|
|
473
|
+
if (changeset)
|
|
474
|
+
for (const change of changeset.changes) {
|
|
475
|
+
if ((change.type === "add" || change.type === "update" || change.type === "delete") && model._stagedLayerNames.add(change.layerName), change.type === "relationship-add" && change.after) {
|
|
476
|
+
const rel = change.after;
|
|
477
|
+
model.relationships.find(rel.source, rel.target, rel.predicate).length === 0 && model.relationships.add({
|
|
478
|
+
source: rel.source,
|
|
479
|
+
target: rel.target,
|
|
480
|
+
predicate: rel.predicate,
|
|
481
|
+
layer: rel.layer,
|
|
482
|
+
...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
|
|
483
|
+
category: rel.category ?? "structural",
|
|
484
|
+
...rel.properties ? { properties: rel.properties } : {}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
if (change.type === "relationship-delete" && change.before) {
|
|
488
|
+
const rel = change.before;
|
|
489
|
+
model.relationships.delete(rel.source, rel.target, rel.predicate);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} catch {
|
|
494
|
+
model._activeChangesetId = null;
|
|
495
|
+
}
|
|
417
496
|
let entityCount = 0;
|
|
418
497
|
for (const layer of model.layers.values())
|
|
419
498
|
entityCount += layer.elements.size;
|
|
420
|
-
return
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
499
|
+
return span.setAttribute("model.entity_count", entityCount), span.setAttribute("model.layer_count", model.layers.size), model;
|
|
500
|
+
}, {
|
|
501
|
+
"model.path": startPath || process.cwd(),
|
|
502
|
+
"model.type": "dr"
|
|
503
|
+
});
|
|
424
504
|
}
|
|
425
505
|
/**
|
|
426
|
-
* Initialize a new model in a directory
|
|
506
|
+
* Initialize a new model in a directory
|
|
427
507
|
*/
|
|
428
508
|
static async init(rootPath, manifestData, options = {}) {
|
|
429
509
|
await ensureDir(`${rootPath}/documentation-robotics/model`);
|
|
430
|
-
|
|
431
|
-
"
|
|
432
|
-
"business",
|
|
433
|
-
"security",
|
|
434
|
-
"application",
|
|
435
|
-
"technology",
|
|
436
|
-
"api",
|
|
437
|
-
"data-model",
|
|
438
|
-
"datastore",
|
|
439
|
-
"ux",
|
|
440
|
-
"navigation",
|
|
441
|
-
"apm",
|
|
442
|
-
"testing"
|
|
443
|
-
];
|
|
444
|
-
for (let i = 0; i < layerOrder.length; i++) {
|
|
445
|
-
const orderNum = String(i + 1).padStart(2, "0"), layerName = layerOrder[i];
|
|
510
|
+
for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {
|
|
511
|
+
const orderNum = String(i + 1).padStart(2, "0"), layerName = CANONICAL_LAYER_NAMES[i];
|
|
446
512
|
await ensureDir(`${rootPath}/documentation-robotics/model/${orderNum}_${layerName}`);
|
|
447
513
|
}
|
|
448
514
|
const manifest = new Manifest(manifestData), model = new Model(rootPath, manifest, options);
|
|
449
515
|
return await model.saveManifest(), model;
|
|
450
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Graph-based API methods for querying the model
|
|
519
|
+
* These methods delegate to the underlying GraphModel for efficient queries
|
|
520
|
+
*/
|
|
521
|
+
/**
|
|
522
|
+
* Get a node by its UUID
|
|
523
|
+
*/
|
|
524
|
+
getNode(id) {
|
|
525
|
+
return this.graph.getNode(id);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Get all nodes in a specific layer
|
|
529
|
+
*/
|
|
530
|
+
getNodesByLayer(layerId) {
|
|
531
|
+
return this.graph.getNodesByLayer(layerId);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Get all nodes of a specific type
|
|
535
|
+
*/
|
|
536
|
+
getNodesByType(type) {
|
|
537
|
+
return this.graph.getNodesByType(type);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Get all edges (relationships) in the model
|
|
541
|
+
*/
|
|
542
|
+
getAllEdges() {
|
|
543
|
+
return this.graph.getAllEdges();
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Get edges originating from a specific node
|
|
547
|
+
*/
|
|
548
|
+
getEdgesFrom(nodeId, predicate) {
|
|
549
|
+
return this.graph.getEdgesFrom(nodeId, predicate);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Get edges terminating at a specific node
|
|
553
|
+
*/
|
|
554
|
+
getEdgesTo(nodeId, predicate) {
|
|
555
|
+
return this.graph.getEdgesTo(nodeId, predicate);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get edges between two specific nodes
|
|
559
|
+
*/
|
|
560
|
+
getEdgesBetween(sourceId, destinationId, predicate) {
|
|
561
|
+
return this.graph.getEdgesBetween(sourceId, destinationId, predicate);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Traverse the graph starting from a node, following a specific predicate
|
|
565
|
+
*/
|
|
566
|
+
async traverse(startNodeId, predicate, maxDepth = 10) {
|
|
567
|
+
return this.graph.traverse(startNodeId, predicate, maxDepth);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Create a backup of the current model state
|
|
571
|
+
* Used for rollback support in migration operations
|
|
572
|
+
*
|
|
573
|
+
* @param label Descriptive label for the backup (e.g., "pre-migration")
|
|
574
|
+
* @returns Path to the backup directory
|
|
575
|
+
*/
|
|
576
|
+
async createBackup(label) {
|
|
577
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"), backupDir = path.join(this.rootPath, ".dr-backups", `${label}-${timestamp}`);
|
|
578
|
+
await ensureDir(backupDir);
|
|
579
|
+
const layersSource = path.join(this.rootPath, "documentation-robotics", "model"), layersBackup = path.join(backupDir, "layers");
|
|
580
|
+
await this.directoryExists(layersSource) && await this.copyDirectory(layersSource, layersBackup);
|
|
581
|
+
const manifestSource = path.join(this.rootPath, "documentation-robotics", "model", "manifest.yaml"), manifestBackup = path.join(backupDir, "manifest.yaml");
|
|
582
|
+
return await this.fileExists(manifestSource) && await fs.copyFile(manifestSource, manifestBackup), backupDir;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Restore model from a backup directory
|
|
586
|
+
* Used for rollback after failed migrations
|
|
587
|
+
*
|
|
588
|
+
* @param backupPath Path to the backup directory
|
|
589
|
+
*/
|
|
590
|
+
async restoreFromBackup(backupPath) {
|
|
591
|
+
const layersBackup = path.join(backupPath, "layers"), layersTarget = path.join(this.rootPath, "documentation-robotics", "model");
|
|
592
|
+
await this.directoryExists(layersBackup) && await this.copyDirectory(layersBackup, layersTarget);
|
|
593
|
+
const manifestBackup = path.join(backupPath, "manifest.yaml"), manifestTarget = path.join(this.rootPath, "documentation-robotics", "model", "manifest.yaml");
|
|
594
|
+
await this.fileExists(manifestBackup) && await fs.copyFile(manifestBackup, manifestTarget);
|
|
595
|
+
const reloadedModel = await Model.load(this.rootPath, { lazyLoad: this.lazyLoad });
|
|
596
|
+
this.layers = reloadedModel.layers, this.manifest = reloadedModel.manifest, this.graph = reloadedModel.graph, this.relationships = reloadedModel.relationships;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Save dirty layers with atomic write semantics
|
|
600
|
+
* Writes to temporary file first, then renames on success to ensure data safety
|
|
601
|
+
*
|
|
602
|
+
* @throws Error if atomic write fails
|
|
603
|
+
*/
|
|
604
|
+
async saveDirtyLayersAtomic() {
|
|
605
|
+
const tempLayerWrites = [];
|
|
606
|
+
try {
|
|
607
|
+
for (const layer of this.layers.values())
|
|
608
|
+
if (layer.isDirty()) {
|
|
609
|
+
const tempPath = `${path.join(this.rootPath, "documentation-robotics", "model", `${layer.name}_layer`)}.tmp`;
|
|
610
|
+
await ensureDir(tempPath);
|
|
611
|
+
for (const element of layer.elements.values()) {
|
|
612
|
+
const elementPath = path.join(tempPath, `${element.path || element.id}.json`), content = JSON.stringify(element.toJSON(), null, 2);
|
|
613
|
+
await writeFile(elementPath, content);
|
|
614
|
+
}
|
|
615
|
+
tempLayerWrites.push({ layer, tempPath });
|
|
616
|
+
}
|
|
617
|
+
for (const { layer, tempPath } of tempLayerWrites) {
|
|
618
|
+
const layerPath = path.join(this.rootPath, "documentation-robotics", "model", `${layer.name}_layer`);
|
|
619
|
+
await this.directoryExists(layerPath) && await fs.rm(layerPath, { recursive: !0, force: !0 }), await fs.rename(tempPath, layerPath);
|
|
620
|
+
}
|
|
621
|
+
} catch (error) {
|
|
622
|
+
for (const { tempPath } of tempLayerWrites)
|
|
623
|
+
await this.directoryExists(tempPath) && await fs.rm(tempPath, { recursive: !0, force: !0 });
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Helper: Check if file exists
|
|
629
|
+
*/
|
|
630
|
+
async fileExists(filePath) {
|
|
631
|
+
try {
|
|
632
|
+
return await fs.access(filePath), !0;
|
|
633
|
+
} catch {
|
|
634
|
+
return !1;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Helper: Check if directory exists
|
|
639
|
+
*/
|
|
640
|
+
async directoryExists(dirPath) {
|
|
641
|
+
try {
|
|
642
|
+
return (await fs.stat(dirPath)).isDirectory();
|
|
643
|
+
} catch {
|
|
644
|
+
return !1;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Helper: Recursively copy directory
|
|
649
|
+
*/
|
|
650
|
+
async copyDirectory(src, dest) {
|
|
651
|
+
await ensureDir(dest);
|
|
652
|
+
const entries = await fs.readdir(src, { withFileTypes: !0 });
|
|
653
|
+
for (const entry of entries) {
|
|
654
|
+
const srcPath = path.join(src, entry.name), destPath = path.join(dest, entry.name);
|
|
655
|
+
entry.isDirectory() ? await this.copyDirectory(srcPath, destPath) : await fs.copyFile(srcPath, destPath);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
451
658
|
}
|
|
452
659
|
export {
|
|
453
660
|
Model
|