@documentation-robotics/cli 0.1.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -2
- package/dist/analysis/relationship-classifier.js +2 -9
- package/dist/analysis/relationship-classifier.js.map +2 -2
- package/dist/analyzers/call-graph-utils.js +2 -2
- package/dist/analyzers/call-graph-utils.js.map +2 -2
- package/dist/analyzers/cards/build.js +171 -0
- package/dist/analyzers/cards/build.js.map +7 -0
- package/dist/analyzers/cards/classify.js +42 -0
- package/dist/analyzers/cards/classify.js.map +7 -0
- package/dist/analyzers/cards/edge-index.js +44 -0
- package/dist/analyzers/cards/edge-index.js.map +7 -0
- package/dist/analyzers/cards/facts.js +46 -0
- package/dist/analyzers/cards/facts.js.map +7 -0
- package/dist/analyzers/cards/graph.js +42 -0
- package/dist/analyzers/cards/graph.js.map +7 -0
- package/dist/analyzers/cards/index.js +8 -0
- package/dist/analyzers/cards/index.js.map +7 -0
- package/dist/analyzers/cards/tier0.js +18 -0
- package/dist/analyzers/cards/tier0.js.map +7 -0
- package/dist/analyzers/cards/types.js +1 -0
- package/dist/analyzers/cards/types.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +195 -171
- package/dist/analyzers/cbm-analyzer.js.map +2 -2
- package/dist/analyzers/cbm-responses.js +336 -0
- package/dist/analyzers/cbm-responses.js.map +7 -0
- package/dist/analyzers/edge-relationships.js +45 -0
- package/dist/analyzers/edge-relationships.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +9 -0
- package/dist/analyzers/mapping-loader.js.map +2 -2
- package/dist/analyzers/tier0-rules.js +153 -0
- package/dist/analyzers/tier0-rules.js.map +7 -0
- package/dist/analyzers/types.js.map +1 -1
- package/dist/analyzers/verify-engine.js +22 -19
- package/dist/analyzers/verify-engine.js.map +2 -2
- package/dist/audit/relationships/ai/prompts.js +10 -10
- package/dist/audit/relationships/ai/prompts.js.map +2 -2
- package/dist/audit/relationships/analysis/layer-templates.js +52 -52
- package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
- package/dist/cli.js +112 -28
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +3 -1
- package/dist/coding-agents/agents/claude-code.js.map +2 -2
- package/dist/coding-agents/claude-code-client.js +2 -1
- package/dist/coding-agents/claude-code-client.js.map +2 -2
- package/dist/coding-agents/copilot-client.js +17 -12
- package/dist/coding-agents/copilot-client.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +152 -0
- package/dist/coding-agents/default-permissions.js.map +7 -0
- package/dist/coding-agents/drbot-mcp.js +167 -0
- package/dist/coding-agents/drbot-mcp.js.map +7 -0
- package/dist/commands/add.js +12 -7
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +134 -23
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/audit.js +6 -6
- package/dist/commands/audit.js.map +2 -2
- package/dist/commands/changeset-verify.js +55 -0
- package/dist/commands/changeset-verify.js.map +7 -0
- package/dist/commands/changeset.js +261 -147
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +52 -19
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/element.js +3 -2
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/farm.js +773 -0
- package/dist/commands/farm.js.map +7 -0
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +122 -0
- package/dist/commands/mcp.js.map +7 -0
- package/dist/commands/relationship.js +251 -47
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +56 -24
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +99 -11
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/validate.js +41 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +2 -2
- package/dist/commands/version.js.map +2 -2
- package/dist/commands/visualize.js +4 -3
- package/dist/commands/visualize.js.map +2 -2
- package/dist/config.js +83 -0
- package/dist/config.js.map +7 -0
- package/dist/core/annotation-store.js +138 -0
- package/dist/core/annotation-store.js.map +7 -0
- package/dist/core/catalog-migration.js +33 -0
- package/dist/core/catalog-migration.js.map +7 -0
- package/dist/core/changeset-analysis.js +421 -0
- package/dist/core/changeset-analysis.js.map +7 -0
- package/dist/core/changeset-base-index.js +153 -0
- package/dist/core/changeset-base-index.js.map +7 -0
- package/dist/core/changeset-brief-delta.js +102 -0
- package/dist/core/changeset-brief-delta.js.map +7 -0
- package/dist/core/changeset-graph.js +281 -0
- package/dist/core/changeset-graph.js.map +7 -0
- package/dist/core/changeset-only-guard.js +32 -0
- package/dist/core/changeset-only-guard.js.map +7 -0
- package/dist/core/changeset-summary.js +68 -0
- package/dist/core/changeset-summary.js.map +7 -0
- package/dist/core/changeset-validator.js +66 -20
- package/dist/core/changeset-validator.js.map +2 -2
- package/dist/core/changeset-verify.js +518 -0
- package/dist/core/changeset-verify.js.map +7 -0
- package/dist/core/changeset.js +56 -9
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/element-type-migration.js +145 -0
- package/dist/core/element-type-migration.js.map +7 -0
- package/dist/core/farm-manifest.js +140 -0
- package/dist/core/farm-manifest.js.map +7 -0
- package/dist/core/farm-sync-engine.js +346 -0
- package/dist/core/farm-sync-engine.js.map +7 -0
- package/dist/core/farm-sync-state.js +167 -0
- package/dist/core/farm-sync-state.js.map +7 -0
- package/dist/core/manifest.js +6 -3
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +84 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model-upgrade.js +102 -0
- package/dist/core/model-upgrade.js.map +7 -0
- package/dist/core/model.js +257 -44
- package/dist/core/model.js.map +3 -3
- package/dist/core/mutation-handler.js +71 -6
- package/dist/core/mutation-handler.js.map +2 -2
- package/dist/core/relationship-migration-table.js +6639 -0
- package/dist/core/relationship-migration-table.js.map +7 -0
- package/dist/core/relationship-migration.js +272 -0
- package/dist/core/relationship-migration.js.map +7 -0
- package/dist/core/relationship-prune.js +49 -0
- package/dist/core/relationship-prune.js.map +7 -0
- package/dist/core/relationship-staging.js +68 -0
- package/dist/core/relationship-staging.js.map +7 -0
- package/dist/core/relationships.js +26 -4
- package/dist/core/relationships.js.map +2 -2
- package/dist/core/report-data-model.js +7 -13
- package/dist/core/report-data-model.js.map +2 -2
- package/dist/core/staged-changeset-storage.js +64 -17
- package/dist/core/staged-changeset-storage.js.map +2 -2
- package/dist/core/staged-source-reference.js +28 -0
- package/dist/core/staged-source-reference.js.map +7 -0
- package/dist/core/staging-area.js +83 -18
- package/dist/core/staging-area.js.map +2 -2
- package/dist/core/virtual-projection.js +14 -5
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/export/archimate-exporter.js +1 -0
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/generated/compiled-validators.js +2 -2
- package/dist/generated/compiled-validators.js.map +2 -2
- package/dist/generated/layer-registry.js +16 -16
- package/dist/generated/layer-registry.js.map +2 -2
- package/dist/generated/node-types.js +186 -168
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +2209 -7727
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
- package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
- package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
- package/dist/integrations/claude_code/commands/dr-map.md +13 -0
- package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
- package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
- package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
- package/dist/integrations/claude_code/docs/README.md +52 -1
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
- package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
- package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
- package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
- package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
- package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
- package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
- package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
- package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
- package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
- package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
- package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
- package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
- package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
- package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
- package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
- package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
- package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
- package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
- package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
- package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
- package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
- package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
- package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
- package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
- package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/hash-utils.js.map +2 -2
- package/dist/mcp/api-key-manager.js +111 -0
- package/dist/mcp/api-key-manager.js.map +7 -0
- package/dist/mcp/changeset-only.js +34 -0
- package/dist/mcp/changeset-only.js.map +7 -0
- package/dist/mcp/http-transport.js +176 -0
- package/dist/mcp/http-transport.js.map +7 -0
- package/dist/mcp/resource-registry.js +97 -0
- package/dist/mcp/resource-registry.js.map +7 -0
- package/dist/mcp/tool-registry.js +60 -0
- package/dist/mcp/tool-registry.js.map +7 -0
- package/dist/mcp/tools/annotation-create.js +39 -0
- package/dist/mcp/tools/annotation-create.js.map +7 -0
- package/dist/mcp/tools/annotation-delete.js +29 -0
- package/dist/mcp/tools/annotation-delete.js.map +7 -0
- package/dist/mcp/tools/annotation-get.js +29 -0
- package/dist/mcp/tools/annotation-get.js.map +7 -0
- package/dist/mcp/tools/annotation-list.js +24 -0
- package/dist/mcp/tools/annotation-list.js.map +7 -0
- package/dist/mcp/tools/annotation-reply.js +34 -0
- package/dist/mcp/tools/annotation-reply.js.map +7 -0
- package/dist/mcp/tools/annotation-shared.js +10 -0
- package/dist/mcp/tools/annotation-shared.js.map +7 -0
- package/dist/mcp/tools/annotation-update.js +39 -0
- package/dist/mcp/tools/annotation-update.js.map +7 -0
- package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
- package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
- package/dist/mcp/tools/changeset-editing.js +184 -0
- package/dist/mcp/tools/changeset-editing.js.map +7 -0
- package/dist/mcp/tools/changeset-lifecycle.js +83 -0
- package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
- package/dist/mcp/tools/changeset-list.js +33 -0
- package/dist/mcp/tools/changeset-list.js.map +7 -0
- package/dist/mcp/tools/changeset-shared.js +31 -0
- package/dist/mcp/tools/changeset-shared.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +55 -0
- package/dist/mcp/tools/changeset-show.js.map +7 -0
- package/dist/mcp/tools/changeset-verify.js +43 -0
- package/dist/mcp/tools/changeset-verify.js.map +7 -0
- package/dist/mcp/tools/chat-launch.js +50 -0
- package/dist/mcp/tools/chat-launch.js.map +7 -0
- package/dist/mcp/tools/chat-status.js +26 -0
- package/dist/mcp/tools/chat-status.js.map +7 -0
- package/dist/mcp/tools/index.js +65 -0
- package/dist/mcp/tools/index.js.map +7 -0
- package/dist/mcp/tools/model-add.js +107 -0
- package/dist/mcp/tools/model-add.js.map +7 -0
- package/dist/mcp/tools/model-delete.js +99 -0
- package/dist/mcp/tools/model-delete.js.map +7 -0
- package/dist/mcp/tools/model-export.js +72 -0
- package/dist/mcp/tools/model-export.js.map +7 -0
- package/dist/mcp/tools/model-info.js +51 -0
- package/dist/mcp/tools/model-info.js.map +7 -0
- package/dist/mcp/tools/model-list.js +53 -0
- package/dist/mcp/tools/model-list.js.map +7 -0
- package/dist/mcp/tools/model-reload.js +34 -0
- package/dist/mcp/tools/model-reload.js.map +7 -0
- package/dist/mcp/tools/model-search.js +102 -0
- package/dist/mcp/tools/model-search.js.map +7 -0
- package/dist/mcp/tools/model-show.js +68 -0
- package/dist/mcp/tools/model-show.js.map +7 -0
- package/dist/mcp/tools/model-stats.js +22 -0
- package/dist/mcp/tools/model-stats.js.map +7 -0
- package/dist/mcp/tools/model-trace.js +118 -0
- package/dist/mcp/tools/model-trace.js.map +7 -0
- package/dist/mcp/tools/model-update.js +121 -0
- package/dist/mcp/tools/model-update.js.map +7 -0
- package/dist/mcp/tools/model-validate.js +78 -0
- package/dist/mcp/tools/model-validate.js.map +7 -0
- package/dist/mcp/tools/relationship-tools.js +98 -0
- package/dist/mcp/tools/relationship-tools.js.map +7 -0
- package/dist/mcp/tools/shared.js +126 -0
- package/dist/mcp/tools/shared.js.map +7 -0
- package/dist/mcp/tools/spec-type.js +90 -0
- package/dist/mcp/tools/spec-type.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +5 -3
- package/dist/reports/model-layer-report-generator.js.map +2 -2
- package/dist/reports/model-readme-generator.js +116 -0
- package/dist/reports/model-readme-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +37 -1
- package/dist/reports/model-report-data.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +29 -6
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/reports/model-report-utils.js +9 -0
- package/dist/reports/model-report-utils.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1029 -1658
- package/dist/schemas/bundled/apm.json +499 -734
- package/dist/schemas/bundled/application.json +745 -370
- package/dist/schemas/bundled/base.json +3 -220
- package/dist/schemas/bundled/business.json +452 -366
- package/dist/schemas/bundled/data-model.json +403 -383
- package/dist/schemas/bundled/data-store.json +334 -558
- package/dist/schemas/bundled/manifest.json +18 -18
- package/dist/schemas/bundled/motivation.json +349 -368
- package/dist/schemas/bundled/navigation.json +415 -731
- package/dist/schemas/bundled/product.json +325 -159
- package/dist/schemas/bundled/security.json +864 -1232
- package/dist/schemas/bundled/technology.json +574 -703
- package/dist/schemas/bundled/testing.json +718 -1643
- package/dist/schemas/bundled/ux.json +739 -1352
- package/dist/server/schemas.js +8 -0
- package/dist/server/schemas.js.map +2 -2
- package/dist/server/server.js +234 -31
- package/dist/server/server.js.map +2 -2
- package/dist/server-entry.js +2 -1
- package/dist/server-entry.js.map +2 -2
- package/dist/telemetry/config.js +2 -40
- package/dist/telemetry/config.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/types/index.js.map +2 -2
- package/dist/utils/ansi-suppressor.js +23 -0
- package/dist/utils/ansi-suppressor.js.map +7 -0
- package/dist/utils/attribute-patch.js +41 -0
- package/dist/utils/attribute-patch.js.map +7 -0
- package/dist/utils/color-utils.js +34 -0
- package/dist/utils/color-utils.js.map +7 -0
- package/dist/utils/element-reference-shape.js +15 -1
- package/dist/utils/element-reference-shape.js.map +3 -3
- package/dist/utils/error-hints.js +29 -0
- package/dist/utils/error-hints.js.map +7 -0
- package/dist/utils/errors.js +75 -10
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/globals.js +17 -1
- package/dist/utils/globals.js.map +2 -2
- package/dist/utils/project-paths.js +50 -3
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/reference-path-parser.js +49 -0
- package/dist/utils/reference-path-parser.js.map +7 -0
- package/dist/utils/source-reference.js +17 -10
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +10 -2
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/validators/composed-reference-validator.js +240 -0
- package/dist/validators/composed-reference-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +57 -13
- package/dist/validators/reference-validator.js.map +3 -3
- package/dist/validators/relationship-schema-validator.js +33 -5
- package/dist/validators/relationship-schema-validator.js.map +3 -3
- package/dist/validators/schema-validator.js +27 -9
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/types.js.map +1 -1
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
- package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +7 -3
- package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
- package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import { Changeset } from "./changeset.js";
|
|
2
|
+
import { ChangesetGraph } from "./changeset-graph.js";
|
|
3
|
+
import { tryReadBaseIndex } from "./changeset-base-index.js";
|
|
4
|
+
import { stagedSourceReference } from "./staged-source-reference.js";
|
|
5
|
+
const LIFECYCLE_FIELDS = ["attributes.status"], LIFECYCLE_STAGES = [
|
|
6
|
+
["proposed", "draft"],
|
|
7
|
+
["planned", "under-review"],
|
|
8
|
+
["approved", "in-progress", "developing", "active"],
|
|
9
|
+
["implemented", "delivered", "achieved", "completed"],
|
|
10
|
+
["automated"]
|
|
11
|
+
];
|
|
12
|
+
function lifecycleStage(value) {
|
|
13
|
+
if (typeof value != "string")
|
|
14
|
+
return -1;
|
|
15
|
+
const v = value.trim().toLowerCase();
|
|
16
|
+
return LIFECYCLE_STAGES.findIndex((stage) => stage.includes(v));
|
|
17
|
+
}
|
|
18
|
+
function isLifecycleMove(difference) {
|
|
19
|
+
if (!LIFECYCLE_FIELDS.includes(difference.field))
|
|
20
|
+
return !1;
|
|
21
|
+
const from = lifecycleStage(difference.planned), to = lifecycleStage(difference.built);
|
|
22
|
+
return from >= 0 && to > from;
|
|
23
|
+
}
|
|
24
|
+
function planFromChangeset(changeset, context = {}) {
|
|
25
|
+
const resolve = context.resolve ?? ((id) => id), notes = [], elements = /* @__PURE__ */ new Map(), relationships = /* @__PURE__ */ new Map(), changes = [...changeset.changes].map((change, index) => ({ change, index })).sort((a, b) => (a.change.sequenceNumber ?? a.index) - (b.change.sequenceNumber ?? b.index));
|
|
26
|
+
for (const { change, index } of changes) {
|
|
27
|
+
const where = `change #${change.sequenceNumber ?? index} (${change.type} ${change.elementId})`;
|
|
28
|
+
if (change.type === "relationship-add" || change.type === "relationship-delete") {
|
|
29
|
+
const rel = readRelationship(change, resolve);
|
|
30
|
+
if (!rel) {
|
|
31
|
+
notes.push(`Skipped ${where}: it does not name a source, predicate and target.`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const key = relKey(rel), previous2 = relationships.get(key);
|
|
35
|
+
change.type === "relationship-add" ? previous2?.op === "delete" ? relationships.delete(key) : relationships.set(key, { ...rel, op: "add" }) : previous2?.op === "add" ? relationships.delete(key) : relationships.set(key, { ...rel, op: "delete" });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const id = resolve(change.elementId), previous = elements.get(id);
|
|
39
|
+
if (change.type === "add" || change.type === "update") {
|
|
40
|
+
if (!change.after) {
|
|
41
|
+
notes.push(`Skipped ${where}: it has no element data.`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const record = toVerifyElement(change.after, change.layerName, id);
|
|
45
|
+
if (change.type === "add" && previous?.op !== "delete")
|
|
46
|
+
elements.set(id, { ...record, op: "add" });
|
|
47
|
+
else if (previous?.op === "add")
|
|
48
|
+
elements.set(id, { ...record, op: "add" });
|
|
49
|
+
else {
|
|
50
|
+
const before = previous?.before ?? (change.before ? toVerifyElement(change.before, change.layerName, id) : void 0), fields = before ? changedFields(before, record) : void 0;
|
|
51
|
+
elements.set(id, {
|
|
52
|
+
...record,
|
|
53
|
+
op: "update",
|
|
54
|
+
...before && fields ? { before, changedFields: fields, baseValues: Object.fromEntries(fields.map((f) => [f, fieldValue(before, f)])) } : {}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (change.type === "delete") {
|
|
60
|
+
if (previous?.op === "add") {
|
|
61
|
+
elements.delete(id);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const known = context.lookup?.(id), before = previous?.before ?? (change.before ? toVerifyElement(change.before, change.layerName, id) : known ? fromGraphElement(known) : fromId(id, change.layerName));
|
|
65
|
+
elements.set(id, { ...before, id, op: "delete", before });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
notes.push(`Skipped ${where}: unknown change type.`);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
label: changeset.name,
|
|
72
|
+
elements: [...elements.values()].map(({ before: _before, ...rest }) => rest),
|
|
73
|
+
relationships: [...relationships.values()],
|
|
74
|
+
notes
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function planFromModel(model, changeset) {
|
|
78
|
+
const graph = await ChangesetGraph.build(model, changeset);
|
|
79
|
+
return planFromChangeset(changeset, {
|
|
80
|
+
resolve: (id) => graph.resolve(id),
|
|
81
|
+
lookup: (id) => graph.getElement(id)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function builtFromGraph(graph, label = graph.changeset.name) {
|
|
85
|
+
const elements = [], changedElements = [];
|
|
86
|
+
for (const element of graph.elements.values()) {
|
|
87
|
+
const built = {
|
|
88
|
+
...fromGraphElement(element),
|
|
89
|
+
...element.status !== "unchanged" ? { change: element.status } : {}
|
|
90
|
+
};
|
|
91
|
+
element.status !== "deleted" && elements.push(built), element.status !== "unchanged" && changedElements.push(built);
|
|
92
|
+
}
|
|
93
|
+
const relationships = [], changedRelationships = [];
|
|
94
|
+
for (const rel of graph.relationships()) {
|
|
95
|
+
const built = {
|
|
96
|
+
source: rel.source,
|
|
97
|
+
predicate: rel.predicate,
|
|
98
|
+
target: rel.target,
|
|
99
|
+
...rel.status !== "unchanged" ? { change: rel.status } : {}
|
|
100
|
+
};
|
|
101
|
+
rel.status !== "deleted" && relationships.push(built), rel.status !== "unchanged" && changedRelationships.push(built);
|
|
102
|
+
}
|
|
103
|
+
return { label, kind: "changeset", elements, relationships, changedElements, changedRelationships, notes: [] };
|
|
104
|
+
}
|
|
105
|
+
async function builtFromBase(model) {
|
|
106
|
+
const now = (/* @__PURE__ */ new Date()).toISOString(), empty = new Changeset({
|
|
107
|
+
id: "base",
|
|
108
|
+
name: "base model",
|
|
109
|
+
created: now,
|
|
110
|
+
modified: now,
|
|
111
|
+
status: "committed",
|
|
112
|
+
baseSnapshot: "",
|
|
113
|
+
changes: []
|
|
114
|
+
}), graph = await ChangesetGraph.build(model, empty), side = builtFromGraph(graph, "base model");
|
|
115
|
+
return {
|
|
116
|
+
label: "base model",
|
|
117
|
+
kind: "base",
|
|
118
|
+
elements: side.elements,
|
|
119
|
+
relationships: side.relationships,
|
|
120
|
+
notes: [
|
|
121
|
+
"Compared with the base model: what the build changed is not recorded, so unplanned changes are limited to relationships the build gave the elements this plan adds. Compare with an as-built changeset for a full list."
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function builtFromSnapshot(snapshot) {
|
|
126
|
+
const elements = snapshot.elements.filter((e) => e.change !== "deleted"), relationships = snapshot.relationships.filter((r) => r.change !== "deleted");
|
|
127
|
+
return {
|
|
128
|
+
label: snapshot.label,
|
|
129
|
+
kind: "snapshot",
|
|
130
|
+
elements,
|
|
131
|
+
relationships,
|
|
132
|
+
...snapshot.allChanged ? {
|
|
133
|
+
changedElements: snapshot.elements.map((e) => ({ ...e, change: e.change ?? "added" })),
|
|
134
|
+
changedRelationships: snapshot.relationships.map((r) => ({ ...r, change: r.change ?? "added" }))
|
|
135
|
+
} : {},
|
|
136
|
+
notes: snapshot.notes ?? []
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function verifyChangeset(plan, built, options = {}) {
|
|
140
|
+
const matchBySource = options.matchBySource ?? !0, notes = [...plan.notes, ...built.notes], builtById = new Map(built.elements.map((e) => [e.id, e])), changedIds = new Set((built.changedElements ?? []).map((e) => e.id)), claimed = /* @__PURE__ */ new Set(), elementMap = /* @__PURE__ */ new Map(), report = {
|
|
141
|
+
planned: { label: plan.label, elements: plan.elements.length, relationships: plan.relationships.length },
|
|
142
|
+
against: { label: built.label, kind: built.kind },
|
|
143
|
+
verdict: "match",
|
|
144
|
+
verdictText: "",
|
|
145
|
+
summary: { elements: emptyCounts(), relationships: emptyCounts() },
|
|
146
|
+
elements: { matched: [], divergent: [], missing: [], unplanned: [], deletionsNotDone: [] },
|
|
147
|
+
relationships: { matched: [], divergent: [], missing: [], unplanned: [], deletionsNotDone: [] },
|
|
148
|
+
notes
|
|
149
|
+
}, live = plan.elements.filter((e) => e.op !== "delete"), deletions = plan.elements.filter((e) => e.op === "delete");
|
|
150
|
+
for (const planned of live)
|
|
151
|
+
builtById.has(planned.id) && (claimed.add(planned.id), elementMap.set(planned.id, { built: planned.id, match: "exact" }));
|
|
152
|
+
const pending = live.filter((e) => !elementMap.has(e.id)), ambiguous = /* @__PURE__ */ new Map(), tiers = [
|
|
153
|
+
{
|
|
154
|
+
test: (p, c) => p.layer === c.layer && sameType(p, c) && namesMatch(p, c),
|
|
155
|
+
reason: (p, c) => `same layer and type, name matches ignoring case and separators ("${p.name}" ~ "${c.name}")`
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
test: (p, c) => p.layer === c.layer && !sameType(p, c) && namesMatch(p, c),
|
|
159
|
+
reason: (p, c) => `same layer and name, but built as ${c.type} instead of ${p.type}`
|
|
160
|
+
}
|
|
161
|
+
];
|
|
162
|
+
matchBySource && tiers.push({
|
|
163
|
+
test: (p, c) => p.layer === c.layer && sameType(p, c) && sharesSourceFile(p, c),
|
|
164
|
+
reason: (p, c) => `same layer and type, shares source file ${sharedSourceFiles(p, c).join(", ")}`
|
|
165
|
+
});
|
|
166
|
+
const baseIds = plan.baseElementIds ? new Set(plan.baseElementIds) : void 0, builtNeighbors = /* @__PURE__ */ new Map();
|
|
167
|
+
for (const rel of built.relationships)
|
|
168
|
+
for (const [a, b] of [
|
|
169
|
+
[rel.source, rel.target],
|
|
170
|
+
[rel.target, rel.source]
|
|
171
|
+
]) {
|
|
172
|
+
const set = builtNeighbors.get(a) ?? /* @__PURE__ */ new Set();
|
|
173
|
+
set.add(b), builtNeighbors.set(a, set);
|
|
174
|
+
}
|
|
175
|
+
const planTouched = /* @__PURE__ */ new Set([
|
|
176
|
+
...plan.elements.map((e) => e.id),
|
|
177
|
+
...plan.relationships.flatMap((r) => [r.source, r.target])
|
|
178
|
+
]), neighborCache = /* @__PURE__ */ new Map(), plannedNeighbors = (id) => {
|
|
179
|
+
const cached = neighborCache.get(id);
|
|
180
|
+
if (cached)
|
|
181
|
+
return cached;
|
|
182
|
+
const own = plan.relationships.filter((r) => r.op === "add" && (r.source === id || r.target === id)).map((r) => r.source === id ? r.target : r.source), ids = own.length > 0 ? own : [...planTouched].filter((other) => other !== id), near = new Set(ids.flatMap((other) => [other, elementMap.get(other)?.built ?? other]));
|
|
183
|
+
return neighborCache.set(id, near), near;
|
|
184
|
+
};
|
|
185
|
+
let fellBack = !1;
|
|
186
|
+
const canBeBuildOf = (planned, candidate) => {
|
|
187
|
+
if (planned.op !== "add")
|
|
188
|
+
return !0;
|
|
189
|
+
if (baseIds)
|
|
190
|
+
return !baseIds.has(candidate.id);
|
|
191
|
+
if (changedIds.has(candidate.id))
|
|
192
|
+
return !0;
|
|
193
|
+
fellBack = !0;
|
|
194
|
+
const near = plannedNeighbors(planned.id);
|
|
195
|
+
for (const other of builtNeighbors.get(candidate.id) ?? [])
|
|
196
|
+
if (near.has(other))
|
|
197
|
+
return !0;
|
|
198
|
+
return !1;
|
|
199
|
+
};
|
|
200
|
+
for (const tier of tiers)
|
|
201
|
+
for (const planned of pending) {
|
|
202
|
+
if (elementMap.has(planned.id))
|
|
203
|
+
continue;
|
|
204
|
+
const candidates = built.elements.filter((c) => !claimed.has(c.id) && tier.test(planned, c) && canBeBuildOf(planned, c));
|
|
205
|
+
if (candidates.length === 0)
|
|
206
|
+
continue;
|
|
207
|
+
let chosen = candidates;
|
|
208
|
+
if (chosen.length > 1 && changedIds.size > 0) {
|
|
209
|
+
const changed = chosen.filter((c) => changedIds.has(c.id));
|
|
210
|
+
changed.length > 0 && (chosen = changed);
|
|
211
|
+
}
|
|
212
|
+
if (chosen.length === 1) {
|
|
213
|
+
const candidate = chosen[0];
|
|
214
|
+
claimed.add(candidate.id), elementMap.set(planned.id, { built: candidate.id, match: "probable", reason: tier.reason(planned, candidate) }), ambiguous.delete(planned.id);
|
|
215
|
+
} else ambiguous.has(planned.id) || ambiguous.set(planned.id, chosen.map((c) => c.id));
|
|
216
|
+
}
|
|
217
|
+
fellBack && notes.push(`No base index was recorded for ${plan.label}, so the elements that existed before it are not known exactly: unchanged elements with no relationship to the plan's elements were not considered as the built form of a planned add.`);
|
|
218
|
+
const lifecycleMoves = [];
|
|
219
|
+
for (const planned of live) {
|
|
220
|
+
const mapping = elementMap.get(planned.id);
|
|
221
|
+
if (!mapping) {
|
|
222
|
+
const candidates = ambiguous.get(planned.id);
|
|
223
|
+
report.elements.missing.push({
|
|
224
|
+
planned: planned.id,
|
|
225
|
+
layer: planned.layer,
|
|
226
|
+
type: planned.type,
|
|
227
|
+
name: planned.name,
|
|
228
|
+
op: planned.op,
|
|
229
|
+
reason: candidates ? `several built elements could be it (${candidates.length}); none matched uniquely` : planned.op === "update" ? "the element the plan updates does not exist in the build" : "not found in the build by id, name or source file",
|
|
230
|
+
...candidates ? { candidates } : {}
|
|
231
|
+
});
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const builtElement = builtById.get(mapping.built), fields = compareElements(planned, builtElement, planned.op === "update" ? planned.changedFields : void 0), match = {
|
|
235
|
+
planned: planned.id,
|
|
236
|
+
built: mapping.built,
|
|
237
|
+
op: planned.op,
|
|
238
|
+
match: mapping.match,
|
|
239
|
+
...mapping.reason ? { reason: mapping.reason } : {}
|
|
240
|
+
};
|
|
241
|
+
if (planned.op === "update" && planned.changedFields && planned.changedFields.length > 0) {
|
|
242
|
+
const unmet = fields.filter((f) => planned.changedFields.includes(f.field)), touched = unmet.some((f) => buildTouched(planned, builtElement, f.field));
|
|
243
|
+
if (unmet.length === planned.changedFields.length && !touched) {
|
|
244
|
+
report.elements.missing.push({
|
|
245
|
+
planned: planned.id,
|
|
246
|
+
layer: planned.layer,
|
|
247
|
+
type: planned.type,
|
|
248
|
+
name: planned.name,
|
|
249
|
+
op: planned.op,
|
|
250
|
+
reason: "the element exists but the build does not carry the planned update",
|
|
251
|
+
fields: unmet
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const lifecycle = fields.filter(isLifecycleMove), differences = fields.filter((f) => !isLifecycleMove(f));
|
|
257
|
+
if (lifecycle.length > 0) {
|
|
258
|
+
match.lifecycle = lifecycle;
|
|
259
|
+
for (const f of lifecycle)
|
|
260
|
+
lifecycleMoves.push(`${planned.id} ${f.field} ${String(f.planned)} \u2192 ${String(f.built)}`);
|
|
261
|
+
}
|
|
262
|
+
differences.length > 0 ? report.elements.divergent.push({ ...match, fields: differences }) : report.elements.matched.push(match);
|
|
263
|
+
}
|
|
264
|
+
lifecycleMoves.length > 0 && notes.push(`Lifecycle progress, not counted as divergence (${LIFECYCLE_FIELDS.join(", ")} moving to a later stage of ${LIFECYCLE_STAGES.map((stage) => stage.join("/")).join(" \u2192 ")}): ${lifecycleMoves.join("; ")}.`);
|
|
265
|
+
for (const planned of deletions) {
|
|
266
|
+
const still = builtById.get(planned.id);
|
|
267
|
+
still ? (claimed.add(still.id), report.elements.deletionsNotDone.push({ planned: planned.id, built: still.id })) : (claimed.add(planned.id), report.elements.matched.push({ planned: planned.id, built: planned.id, op: "delete", match: "exact" }));
|
|
268
|
+
}
|
|
269
|
+
if (built.changedElements)
|
|
270
|
+
for (const element of built.changedElements)
|
|
271
|
+
claimed.has(element.id) || report.elements.unplanned.push({
|
|
272
|
+
id: element.id,
|
|
273
|
+
layer: element.layer,
|
|
274
|
+
type: element.type,
|
|
275
|
+
name: element.name,
|
|
276
|
+
change: element.change ?? "added"
|
|
277
|
+
});
|
|
278
|
+
const builtRels = new Map(built.relationships.map((r) => [relKey(r), r])), claimedRels = /* @__PURE__ */ new Set(), mapId = (id) => elementMap.get(id)?.built ?? id, probableEndpoints = (rel) => [rel.source, rel.target].filter((id, i, all) => all.indexOf(id) === i).filter((id) => elementMap.get(id)?.match === "probable").map((id) => `${id} \u2192 ${elementMap.get(id).built}`), missingEndpoints = new Set(report.elements.missing.map((m) => m.planned)), mapRel = (planned) => ({
|
|
279
|
+
source: mapId(planned.source),
|
|
280
|
+
predicate: planned.predicate,
|
|
281
|
+
target: mapId(planned.target)
|
|
282
|
+
}), matchInfoFor = (planned) => {
|
|
283
|
+
const probable = probableEndpoints(planned);
|
|
284
|
+
return probable.length > 0 ? { match: "probable", reason: `through probable element match: ${probable.join("; ")}` } : { match: "exact" };
|
|
285
|
+
}, unresolved = [];
|
|
286
|
+
for (const planned of plan.relationships) {
|
|
287
|
+
const mapped = mapRel(planned), found = builtRels.get(relKey(mapped));
|
|
288
|
+
if (!found || claimedRels.has(relKey(found))) {
|
|
289
|
+
unresolved.push(planned);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
claimedRels.add(relKey(found));
|
|
293
|
+
const plannedRef = strip(planned);
|
|
294
|
+
planned.op === "delete" ? report.relationships.deletionsNotDone.push({ planned: plannedRef, built: strip(found) }) : report.relationships.matched.push({ planned: plannedRef, built: strip(found), op: "add", ...matchInfoFor(planned) });
|
|
295
|
+
}
|
|
296
|
+
const addedBuiltIds = new Set(live.filter((e) => e.op === "add" && elementMap.has(e.id)).map((e) => elementMap.get(e.id).built));
|
|
297
|
+
for (const planned of unresolved) {
|
|
298
|
+
const mapped = mapRel(planned), plannedRef = strip(planned), matchInfo = matchInfoFor(planned);
|
|
299
|
+
if (planned.op === "delete") {
|
|
300
|
+
claimedRels.add(relKey(mapped)), report.relationships.matched.push({ planned: plannedRef, built: mapped, op: "delete", ...matchInfo });
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const isAlternative = (r) => !claimedRels.has(relKey(r)) && (r.source === mapped.source && r.target === mapped.target && r.predicate !== mapped.predicate || r.source === mapped.target && r.target === mapped.source && r.predicate === mapped.predicate), alternative = built.changedRelationships ? built.changedRelationships.filter((r) => r.change !== "deleted").find(isAlternative) : (
|
|
304
|
+
// What the build changed is not known: only a relationship touching an element the plan adds
|
|
305
|
+
// can be the build's version; one between pre-existing elements may simply predate the change.
|
|
306
|
+
built.relationships.find((r) => isAlternative(r) && (addedBuiltIds.has(r.source) || addedBuiltIds.has(r.target)))
|
|
307
|
+
);
|
|
308
|
+
if (alternative) {
|
|
309
|
+
claimedRels.add(relKey(alternative));
|
|
310
|
+
const fields = alternative.predicate !== mapped.predicate ? [{ field: "predicate", planned: mapped.predicate, built: alternative.predicate }] : [
|
|
311
|
+
{
|
|
312
|
+
field: "direction",
|
|
313
|
+
planned: `${mapped.source} \u2192 ${mapped.target}`,
|
|
314
|
+
built: `${alternative.source} \u2192 ${alternative.target}`
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
report.relationships.divergent.push({
|
|
318
|
+
planned: plannedRef,
|
|
319
|
+
built: strip(alternative),
|
|
320
|
+
op: "add",
|
|
321
|
+
...matchInfo,
|
|
322
|
+
fields
|
|
323
|
+
});
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const candidates = built.changedRelationships ? [] : built.relationships.filter(isAlternative).map(strip), absent = [planned.source, planned.target].filter((id) => missingEndpoints.has(id) || !elementMap.has(id) && !builtById.has(id));
|
|
327
|
+
report.relationships.missing.push({
|
|
328
|
+
planned: plannedRef,
|
|
329
|
+
reason: absent.length > 0 ? `not built; endpoint${absent.length > 1 ? "s" : ""} ${absent.join(", ")} not found in the build` : candidates.length > 0 ? "not found in the build; a relationship between the same elements exists, but it may predate the change" : "not found in the build",
|
|
330
|
+
...candidates.length > 0 ? { candidates } : {}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
if (built.changedRelationships)
|
|
334
|
+
for (const rel of built.changedRelationships)
|
|
335
|
+
claimedRels.has(relKey(rel)) || report.relationships.unplanned.push({ ...strip(rel), change: rel.change ?? "added" });
|
|
336
|
+
else
|
|
337
|
+
for (const rel of built.relationships)
|
|
338
|
+
claimedRels.has(relKey(rel)) || (addedBuiltIds.has(rel.source) || addedBuiltIds.has(rel.target)) && report.relationships.unplanned.push({ ...strip(rel), change: "present" });
|
|
339
|
+
report.summary.elements = countCategories(plan.elements.length, report.elements), report.summary.relationships = countCategories(plan.relationships.length, report.relationships);
|
|
340
|
+
const { verdict, text } = decideVerdict(report);
|
|
341
|
+
return report.verdict = verdict, report.verdictText = text, report;
|
|
342
|
+
}
|
|
343
|
+
async function verifyChangesetInModel(model, options) {
|
|
344
|
+
const { planned } = options, plan = await planFromModel(model, planned), notes = [], baseIndex = await tryReadBaseIndex(model.rootPath, planned.id);
|
|
345
|
+
baseIndex.index && (plan.baseElementIds = Object.keys(baseIndex.index.elements)), baseIndex.warning && notes.push(baseIndex.warning);
|
|
346
|
+
let built;
|
|
347
|
+
if ("changeset" in options.against) {
|
|
348
|
+
const asBuilt = options.against.changeset;
|
|
349
|
+
asBuilt.id === planned.id && notes.push("The planned and as-built changesets are the same changeset, so everything matches by definition."), built = builtFromGraph(await ChangesetGraph.build(model, asBuilt), `${asBuilt.name} (${asBuilt.id})`);
|
|
350
|
+
} else
|
|
351
|
+
built = await builtFromBase(model), planned.status === "staged" && notes.push("The planned changeset is still staged, so its own changes are not in the base model: anything reported missing is missing from what was committed to the base (e.g. an as-built changeset).");
|
|
352
|
+
const report = verifyChangeset(plan, built, { matchBySource: options.matchBySource });
|
|
353
|
+
return report.notes.push(...notes), { plannedChangeset: { id: planned.id, name: planned.name }, ...report };
|
|
354
|
+
}
|
|
355
|
+
function normalizeName(name) {
|
|
356
|
+
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
357
|
+
}
|
|
358
|
+
function nameKeys(element) {
|
|
359
|
+
const keys = /* @__PURE__ */ new Set();
|
|
360
|
+
element.name && keys.add(normalizeName(element.name));
|
|
361
|
+
const segment = idNameSegment(element.id);
|
|
362
|
+
return segment && keys.add(normalizeName(segment)), keys.delete(""), keys;
|
|
363
|
+
}
|
|
364
|
+
function namesMatch(a, b) {
|
|
365
|
+
const keys = nameKeys(b);
|
|
366
|
+
for (const key of nameKeys(a))
|
|
367
|
+
if (keys.has(key))
|
|
368
|
+
return !0;
|
|
369
|
+
return !1;
|
|
370
|
+
}
|
|
371
|
+
function sameType(a, b) {
|
|
372
|
+
return normalizeName(a.type) === normalizeName(b.type);
|
|
373
|
+
}
|
|
374
|
+
function sourceFiles(element) {
|
|
375
|
+
return (element.sourceReference?.locations ?? []).map((l) => typeof l?.file == "string" ? l.file.replace(/^\.\//, "").replace(/\\/g, "/") : "").filter((f) => f.length > 0);
|
|
376
|
+
}
|
|
377
|
+
function sharedSourceFiles(a, b) {
|
|
378
|
+
const files = new Set(sourceFiles(b));
|
|
379
|
+
return sourceFiles(a).filter((f) => files.has(f));
|
|
380
|
+
}
|
|
381
|
+
function sharesSourceFile(a, b) {
|
|
382
|
+
return sharedSourceFiles(a, b).length > 0;
|
|
383
|
+
}
|
|
384
|
+
function compareElements(planned, built, removedBy) {
|
|
385
|
+
const fields = [];
|
|
386
|
+
planned.type && !sameType(planned, built) && fields.push({ field: "type", planned: planned.type, built: built.type }), planned.name && normalizeName(planned.name) !== normalizeName(built.name) && fields.push({ field: "name", planned: planned.name, built: built.name }), planned.description !== void 0 && planned.description.trim() !== (built.description ?? "").trim() && fields.push({ field: "description", planned: planned.description, built: built.description });
|
|
387
|
+
for (const [key, value] of Object.entries(planned.attributes ?? {})) {
|
|
388
|
+
const builtValue = built.attributes?.[key];
|
|
389
|
+
sameValue(value, builtValue) || fields.push({ field: `attributes.${key}`, planned: value, built: builtValue });
|
|
390
|
+
}
|
|
391
|
+
for (const field of removedBy ?? [])
|
|
392
|
+
if (field === "description")
|
|
393
|
+
planned.description === void 0 && (built.description ?? "").trim() !== "" && fields.push({ field, planned: void 0, built: built.description });
|
|
394
|
+
else if (field.startsWith("attributes.")) {
|
|
395
|
+
const key = field.slice(11);
|
|
396
|
+
!(planned.attributes !== void 0 && Object.hasOwn(planned.attributes, key)) && built.attributes?.[key] !== void 0 && fields.push({ field, planned: void 0, built: built.attributes[key] });
|
|
397
|
+
}
|
|
398
|
+
return fields;
|
|
399
|
+
}
|
|
400
|
+
function fieldValue(element, field) {
|
|
401
|
+
if (field === "type")
|
|
402
|
+
return element.type;
|
|
403
|
+
if (field === "name")
|
|
404
|
+
return element.name;
|
|
405
|
+
if (field === "description")
|
|
406
|
+
return element.description;
|
|
407
|
+
if (field.startsWith("attributes."))
|
|
408
|
+
return element.attributes?.[field.slice(11)];
|
|
409
|
+
}
|
|
410
|
+
function sameFieldValue(field, a, b) {
|
|
411
|
+
return field === "type" || field === "name" ? normalizeName(String(a ?? "")) === normalizeName(String(b ?? "")) : field === "description" ? String(a ?? "").trim() === String(b ?? "").trim() : sameValue(a, b);
|
|
412
|
+
}
|
|
413
|
+
function buildTouched(planned, built, field) {
|
|
414
|
+
return planned.baseValues && Object.hasOwn(planned.baseValues, field) ? !sameFieldValue(field, fieldValue(built, field), planned.baseValues[field]) : built.change === "updated" || built.change === "added";
|
|
415
|
+
}
|
|
416
|
+
function changedFields(before, after) {
|
|
417
|
+
const fields = [];
|
|
418
|
+
before.type !== after.type && fields.push("type"), before.name !== after.name && fields.push("name"), (before.description ?? "") !== (after.description ?? "") && fields.push("description");
|
|
419
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(before.attributes ?? {}), ...Object.keys(after.attributes ?? {})]);
|
|
420
|
+
for (const key of keys)
|
|
421
|
+
sameValue(before.attributes?.[key], after.attributes?.[key]) || fields.push(`attributes.${key}`);
|
|
422
|
+
return fields;
|
|
423
|
+
}
|
|
424
|
+
function sameValue(a, b) {
|
|
425
|
+
return stableStringify(a) === stableStringify(b);
|
|
426
|
+
}
|
|
427
|
+
function stableStringify(value) {
|
|
428
|
+
return value === void 0 ? "undefined" : value === null || typeof value != "object" ? JSON.stringify(value) : Array.isArray(value) ? `[${value.map(stableStringify).join(",")}]` : `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
429
|
+
}
|
|
430
|
+
function relKey(rel) {
|
|
431
|
+
return `${rel.source}::${rel.predicate}::${rel.target}`;
|
|
432
|
+
}
|
|
433
|
+
function strip(rel) {
|
|
434
|
+
return { source: rel.source, predicate: rel.predicate, target: rel.target };
|
|
435
|
+
}
|
|
436
|
+
function readRelationship(change, resolve) {
|
|
437
|
+
const data = change.type === "relationship-delete" ? change.before ?? change.after : change.after, [source, predicate, target] = data?.source && data.predicate && data.target ? [data.source, data.predicate, data.target] : change.elementId.split("::");
|
|
438
|
+
return !source || !predicate || !target ? null : { source: resolve(source), predicate, target: resolve(target) };
|
|
439
|
+
}
|
|
440
|
+
function idNameSegment(id) {
|
|
441
|
+
const parts = id.split(".");
|
|
442
|
+
return parts.length >= 3 ? parts.slice(2).join(".") : void 0;
|
|
443
|
+
}
|
|
444
|
+
function fromId(id, layerName) {
|
|
445
|
+
const parts = id.split(".");
|
|
446
|
+
return {
|
|
447
|
+
id,
|
|
448
|
+
layer: parts.length >= 3 ? parts[0] : layerName,
|
|
449
|
+
type: parts.length >= 3 ? parts[1] : "",
|
|
450
|
+
name: idNameSegment(id) ?? id,
|
|
451
|
+
attributes: {}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function fromGraphElement(element) {
|
|
455
|
+
return {
|
|
456
|
+
id: element.id,
|
|
457
|
+
layer: element.layer,
|
|
458
|
+
type: element.type,
|
|
459
|
+
name: element.name,
|
|
460
|
+
...element.description !== void 0 ? { description: element.description } : {},
|
|
461
|
+
attributes: element.attributes ?? {},
|
|
462
|
+
...element.sourceReference ? { sourceReference: element.sourceReference } : {}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function toVerifyElement(record, layerName, id) {
|
|
466
|
+
const str = (value) => typeof value == "string" && value.length > 0 ? value : void 0, fallback = fromId(id, layerName);
|
|
467
|
+
return {
|
|
468
|
+
id,
|
|
469
|
+
layer: str(record.layer_id) ?? str(record.layer) ?? fallback.layer,
|
|
470
|
+
type: str(record.type) ?? fallback.type,
|
|
471
|
+
name: str(record.name) ?? fallback.name,
|
|
472
|
+
...str(record.description) !== void 0 ? { description: str(record.description) } : {},
|
|
473
|
+
attributes: record.attributes && typeof record.attributes == "object" ? record.attributes : {},
|
|
474
|
+
...stagedSourceReference(record) ? { sourceReference: stagedSourceReference(record) } : {}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function emptyCounts() {
|
|
478
|
+
return { planned: 0, matched: 0, probable: 0, missing: 0, divergent: 0, unplanned: 0, deletionsNotDone: 0 };
|
|
479
|
+
}
|
|
480
|
+
function countCategories(planned, section) {
|
|
481
|
+
return {
|
|
482
|
+
planned,
|
|
483
|
+
matched: section.matched.length + section.divergent.length,
|
|
484
|
+
probable: [...section.matched, ...section.divergent].filter((m) => m.match === "probable").length,
|
|
485
|
+
missing: section.missing.length,
|
|
486
|
+
divergent: section.divergent.length,
|
|
487
|
+
unplanned: section.unplanned.length,
|
|
488
|
+
deletionsNotDone: section.deletionsNotDone.length
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function decideVerdict(report) {
|
|
492
|
+
const e = report.summary.elements, r = report.summary.relationships;
|
|
493
|
+
if (e.planned === 0 && r.planned === 0)
|
|
494
|
+
return { verdict: "empty-plan", text: "The plan has no changes to verify." };
|
|
495
|
+
const parts = [], say = (n, what) => {
|
|
496
|
+
n > 0 && parts.push(`${n} ${what}`);
|
|
497
|
+
};
|
|
498
|
+
say(e.missing, plural(e.missing, "planned element", "planned elements") + " missing"), say(r.missing, plural(r.missing, "planned relationship", "planned relationships") + " missing"), say(e.deletionsNotDone + r.deletionsNotDone, "planned deletion(s) not done"), say(e.divergent, plural(e.divergent, "element differs", "elements differ")), say(r.divergent, plural(r.divergent, "relationship differs", "relationships differ")), say(e.unplanned, plural(e.unplanned, "unplanned element change", "unplanned element changes")), say(r.unplanned, plural(r.unplanned, "unplanned relationship", "unplanned relationships"));
|
|
499
|
+
const probable = e.probable + r.probable, probableText = probable > 0 ? ` ${probable} match(es) are probable, not exact; confirm them.` : "";
|
|
500
|
+
return e.missing + r.missing + e.deletionsNotDone + r.deletionsNotDone > 0 ? { verdict: "incomplete", text: `The build is incomplete: ${parts.join(", ")}.${probableText}` } : e.divergent + r.divergent + e.unplanned + r.unplanned > 0 ? { verdict: "diverges", text: `The build diverges from the plan: ${parts.join(", ")}.${probableText}` } : probable > 0 ? { verdict: "probable-match", text: `Everything planned was found.${probableText}` } : { verdict: "match", text: "The build matches the plan." };
|
|
501
|
+
}
|
|
502
|
+
function plural(n, one, many) {
|
|
503
|
+
return n === 1 ? one : many;
|
|
504
|
+
}
|
|
505
|
+
export {
|
|
506
|
+
LIFECYCLE_FIELDS,
|
|
507
|
+
LIFECYCLE_STAGES,
|
|
508
|
+
builtFromBase,
|
|
509
|
+
builtFromGraph,
|
|
510
|
+
builtFromSnapshot,
|
|
511
|
+
isLifecycleMove,
|
|
512
|
+
normalizeName,
|
|
513
|
+
planFromChangeset,
|
|
514
|
+
planFromModel,
|
|
515
|
+
verifyChangeset,
|
|
516
|
+
verifyChangesetInModel
|
|
517
|
+
};
|
|
518
|
+
//# sourceMappingURL=changeset-verify.js.map
|