@documentation-robotics/cli 0.2.0 → 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 +36 -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/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 +12 -2
- package/dist/cli.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +31 -1
- package/dist/coding-agents/default-permissions.js.map +2 -2
- package/dist/coding-agents/drbot-mcp.js +167 -0
- package/dist/coding-agents/drbot-mcp.js.map +7 -0
- package/dist/commands/add.js +3 -2
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +117 -14
- package/dist/commands/analyzer.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 +43 -22
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +10 -4
- package/dist/commands/delete.js.map +2 -2
- package/dist/commands/farm.js +176 -60
- package/dist/commands/farm.js.map +2 -2
- package/dist/commands/mcp.js +19 -4
- package/dist/commands/mcp.js.map +2 -2
- package/dist/commands/relationship.js +207 -26
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/update.js +42 -23
- 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/version.js +1 -1
- package/dist/commands/visualize.js +4 -3
- package/dist/commands/visualize.js.map +2 -2
- 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 +12 -6
- package/dist/core/farm-manifest.js.map +2 -2
- package/dist/core/farm-sync-engine.js +1 -1
- package/dist/core/farm-sync-engine.js.map +2 -2
- package/dist/core/farm-sync-state.js +10 -2
- package/dist/core/farm-sync-state.js.map +2 -2
- package/dist/core/migration-registry.js +75 -46
- 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 +82 -30
- package/dist/core/model.js.map +2 -2
- 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 +13 -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 +2236 -7790
- 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 +26 -2
- package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
- package/dist/integrations/claude_code/docs/README.md +48 -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 +14 -1
- package/dist/mcp/api-key-manager.js.map +2 -2
- package/dist/mcp/changeset-only.js +34 -0
- package/dist/mcp/changeset-only.js.map +7 -0
- package/dist/mcp/tool-registry.js +19 -3
- package/dist/mcp/tool-registry.js.map +2 -2
- 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-shared.js +31 -0
- package/dist/mcp/tools/changeset-shared.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +17 -1
- package/dist/mcp/tools/changeset-show.js.map +2 -2
- package/dist/mcp/tools/changeset-verify.js +43 -0
- package/dist/mcp/tools/changeset-verify.js.map +7 -0
- package/dist/mcp/tools/index.js +19 -1
- package/dist/mcp/tools/index.js.map +2 -2
- package/dist/mcp/tools/model-add.js +6 -5
- package/dist/mcp/tools/model-add.js.map +2 -2
- package/dist/mcp/tools/model-delete.js +2 -2
- package/dist/mcp/tools/model-delete.js.map +2 -2
- package/dist/mcp/tools/model-search.js +14 -0
- package/dist/mcp/tools/model-search.js.map +2 -2
- package/dist/mcp/tools/model-show.js +29 -2
- package/dist/mcp/tools/model-show.js.map +2 -2
- package/dist/mcp/tools/model-trace.js +93 -30
- package/dist/mcp/tools/model-trace.js.map +2 -2
- package/dist/mcp/tools/model-update.js +35 -19
- package/dist/mcp/tools/model-update.js.map +2 -2
- 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 +25 -2
- package/dist/mcp/tools/shared.js.map +2 -2
- package/dist/mcp/tools/spec-type.js +90 -0
- package/dist/mcp/tools/spec-type.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 +735 -371
- package/dist/schemas/bundled/base.json +3 -220
- package/dist/schemas/bundled/business.json +452 -366
- package/dist/schemas/bundled/data-model.json +395 -411
- 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 +218 -34
- 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/index.js +1 -1
- package/dist/utils/ansi-suppressor.js +6 -8
- package/dist/utils/ansi-suppressor.js.map +2 -2
- package/dist/utils/attribute-patch.js +41 -0
- package/dist/utils/attribute-patch.js.map +7 -0
- package/dist/utils/error-hints.js +29 -0
- package/dist/utils/error-hints.js.map +7 -0
- package/dist/utils/errors.js +9 -3
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/project-paths.js +26 -1
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/source-reference.js +14 -12
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +2 -2
- package/dist/utils/spec-version.js.map +1 -1
- package/dist/validators/composed-reference-validator.js +5 -27
- package/dist/validators/composed-reference-validator.js.map +2 -2
- package/dist/validators/reference-validator.js +0 -7
- package/dist/validators/reference-validator.js.map +2 -2
- package/dist/validators/relationship-schema-validator.js +3 -3
- package/dist/validators/relationship-schema-validator.js.map +2 -2
- 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 +2 -1
- package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
- package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { readdir } from "fs/promises";
|
|
2
|
+
import { Model } from "./model.js";
|
|
3
|
+
import { MigrationRegistry } from "./migration-registry.js";
|
|
4
|
+
import { applyCatalogMigrations } from "./catalog-migration.js";
|
|
5
|
+
import { BaseSnapshotManager } from "./base-snapshot-manager.js";
|
|
6
|
+
import { StagedChangesetStorage } from "./staged-changeset-storage.js";
|
|
7
|
+
import { FileLock } from "../utils/file-lock.js";
|
|
8
|
+
import { getErrorMessage } from "../utils/errors.js";
|
|
9
|
+
async function upgradeModel(startPath, options) {
|
|
10
|
+
const model = await Model.load(startPath, { lazyLoad: !1 }), baselines = await captureChangesetBaselines(model);
|
|
11
|
+
let migration;
|
|
12
|
+
if (options.versionBumpOnly)
|
|
13
|
+
model.manifest.specVersion = options.toVersion, await model.saveManifest();
|
|
14
|
+
else if (options.catalogOnly) {
|
|
15
|
+
const catalog = await applyCatalogMigrations(model);
|
|
16
|
+
if (catalog.errors.length > 0)
|
|
17
|
+
throw new Error(`Catalog migration failed: ${catalog.errors.join("; ")}`);
|
|
18
|
+
migration = {
|
|
19
|
+
applied: [
|
|
20
|
+
{
|
|
21
|
+
from: options.fromVersion,
|
|
22
|
+
to: options.toVersion,
|
|
23
|
+
description: `Catalog changes applied (${catalog.summaries.join("; ")})`,
|
|
24
|
+
changes: {
|
|
25
|
+
migrationsApplied: 1,
|
|
26
|
+
filesModified: catalog.filesModified,
|
|
27
|
+
description: catalog.summaries.join("; "),
|
|
28
|
+
details: catalog.details
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
currentVersion: options.fromVersion,
|
|
33
|
+
targetVersion: options.toVersion,
|
|
34
|
+
totalChanges: 1
|
|
35
|
+
}, await model.saveDirtyLayers();
|
|
36
|
+
} else
|
|
37
|
+
migration = await new MigrationRegistry().applyMigrations(model, {
|
|
38
|
+
fromVersion: options.fromVersion,
|
|
39
|
+
toVersion: options.toVersion,
|
|
40
|
+
dryRun: !1,
|
|
41
|
+
validate: options.validate ?? !0
|
|
42
|
+
}), await model.saveManifest(), await model.saveDirtyLayers();
|
|
43
|
+
return { migration, changesets: await rebaselineChangesets(startPath, baselines) };
|
|
44
|
+
}
|
|
45
|
+
async function captureChangesetBaselines(model) {
|
|
46
|
+
if (model.lazyLoad)
|
|
47
|
+
throw new Error("captureChangesetBaselines needs a model loaded with lazyLoad: false");
|
|
48
|
+
const { changesets, errors } = await listStagedChangesets(model.rootPath), baselines = { snapshot: null, inStep: [], drifted: [], errors };
|
|
49
|
+
if (changesets.length === 0)
|
|
50
|
+
return baselines;
|
|
51
|
+
const snapshot = await new BaseSnapshotManager().captureSnapshot(model);
|
|
52
|
+
baselines.snapshot = snapshot;
|
|
53
|
+
for (const { ref, baseSnapshot } of changesets)
|
|
54
|
+
baseSnapshot && (baseSnapshot === snapshot ? baselines.inStep : baselines.drifted).push(ref);
|
|
55
|
+
return baselines;
|
|
56
|
+
}
|
|
57
|
+
async function rebaselineChangesets(startPath, baselines) {
|
|
58
|
+
const result = {
|
|
59
|
+
rebaselined: [],
|
|
60
|
+
drifted: baselines.drifted,
|
|
61
|
+
errors: [...baselines.errors]
|
|
62
|
+
};
|
|
63
|
+
if (baselines.inStep.length === 0)
|
|
64
|
+
return result;
|
|
65
|
+
const upgraded = await Model.load(startPath, { lazyLoad: !1 }), snapshot = await new BaseSnapshotManager().captureSnapshot(upgraded), storage = new StagedChangesetStorage(upgraded.rootPath);
|
|
66
|
+
for (const ref of baselines.inStep)
|
|
67
|
+
try {
|
|
68
|
+
await new FileLock(storage.getChangesetPath(ref.id)).withLock(async () => {
|
|
69
|
+
const changeset = await storage.load(ref.id);
|
|
70
|
+
!changeset || changeset.status !== "staged" || changeset.baseSnapshot !== baselines.snapshot || (changeset.baseSnapshot = snapshot, changeset.updateModified(), await storage.save(changeset), result.rebaselined.push(ref));
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
result.errors.push(`Could not re-baseline changeset '${ref.name}': ${getErrorMessage(error)}; committing it needs --force`);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
async function listStagedChangesets(rootPath) {
|
|
78
|
+
const storage = new StagedChangesetStorage(rootPath), changesets = [], errors = [];
|
|
79
|
+
let ids;
|
|
80
|
+
try {
|
|
81
|
+
ids = (await readdir(storage.getChangesetsDir(), { withFileTypes: !0 })).filter((entry) => entry.isDirectory() && !entry.name.endsWith(".lock")).map((entry) => entry.name).sort();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return error.code !== "ENOENT" && errors.push(`Could not list changesets: ${getErrorMessage(error)}`), { changesets, errors };
|
|
84
|
+
}
|
|
85
|
+
for (const id of ids)
|
|
86
|
+
try {
|
|
87
|
+
const changeset = await storage.load(id);
|
|
88
|
+
changeset?.status === "staged" && changesets.push({
|
|
89
|
+
ref: { id, name: changeset.name || id },
|
|
90
|
+
baseSnapshot: changeset.baseSnapshot
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
errors.push(`Could not read changeset '${id}': ${getErrorMessage(error)}`);
|
|
94
|
+
}
|
|
95
|
+
return { changesets, errors };
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
captureChangesetBaselines,
|
|
99
|
+
rebaselineChangesets,
|
|
100
|
+
upgradeModel
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=model-upgrade.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/model-upgrade.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Model upgrade - moves a model to a newer spec version and keeps its staged changesets\n * committable.\n *\n * A staged changeset records a hash of the model it was created on (its base snapshot), and\n * committing it fails while the model no longer has that hash. An upgrade always changes the\n * hash: it writes a new spec version into the manifest, and migrations may rewrite element\n * files. So the upgrade re-baselines each staged changeset that was in step with the model\n * before the upgrade onto the upgraded model. A changeset that had drifted before the upgrade\n * keeps its base snapshot, so that drift still blocks its commit.\n */\n\nimport { readdir } from \"fs/promises\";\nimport { Model } from \"./model.js\";\nimport { MigrationRegistry, type ApplyMigrationsResult } from \"./migration-registry.js\";\nimport { applyCatalogMigrations } from \"./catalog-migration.js\";\nimport { BaseSnapshotManager } from \"./base-snapshot-manager.js\";\nimport { StagedChangesetStorage } from \"./staged-changeset-storage.js\";\nimport { FileLock } from \"../utils/file-lock.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\nexport interface ModelUpgradeOptions {\n fromVersion: string;\n toVersion: string;\n /** No migration path exists between the versions: only write the new spec version */\n versionBumpOnly?: boolean;\n /**\n * The model is at the target version but still names retired types or relationships: apply\n * the catalog migration (element type renames and relationship rewrites) on its own\n */\n catalogOnly?: boolean;\n /** Passed to the migrations (default true) */\n validate?: boolean;\n}\n\n/** A staged changeset, by directory id and name */\nexport interface ChangesetRef {\n id: string;\n name: string;\n}\n\nexport interface ChangesetRebaselineResult {\n /** Staged changesets now based on the upgraded model */\n rebaselined: ChangesetRef[];\n /** Staged changesets that had drifted before the upgrade; they keep their base snapshot */\n drifted: ChangesetRef[];\n /** Changesets that could not be read or re-baselined */\n errors: string[];\n}\n\nexport interface ModelUpgradeResult {\n /** The migrations applied; absent for a version bump */\n migration?: ApplyMigrationsResult;\n changesets: ChangesetRebaselineResult;\n}\n\n/** Staged changesets sorted by whether they match the model before an upgrade */\nexport interface ChangesetBaselines {\n /** Snapshot of the model before the upgrade; null when there is no staged changeset */\n snapshot: string | null;\n inStep: ChangesetRef[];\n drifted: ChangesetRef[];\n errors: string[];\n}\n\n/**\n * Upgrade the model at startPath to options.toVersion and save it, then re-baseline its\n * staged changesets on the upgraded model.\n */\nexport async function upgradeModel(\n startPath: string,\n options: ModelUpgradeOptions\n): Promise<ModelUpgradeResult> {\n // Load every layer, as `dr changeset commit` does before it checks for drift\n const model = await Model.load(startPath, { lazyLoad: false });\n const baselines = await captureChangesetBaselines(model);\n\n let migration: ApplyMigrationsResult | undefined;\n if (options.versionBumpOnly) {\n model.manifest.specVersion = options.toVersion;\n await model.saveManifest();\n } else if (options.catalogOnly) {\n const catalog = await applyCatalogMigrations(model);\n if (catalog.errors.length > 0) {\n throw new Error(`Catalog migration failed: ${catalog.errors.join(\"; \")}`);\n }\n migration = {\n applied: [\n {\n from: options.fromVersion,\n to: options.toVersion,\n description: `Catalog changes applied (${catalog.summaries.join(\"; \")})`,\n changes: {\n migrationsApplied: 1,\n filesModified: catalog.filesModified,\n description: catalog.summaries.join(\"; \"),\n details: catalog.details,\n },\n },\n ],\n currentVersion: options.fromVersion,\n targetVersion: options.toVersion,\n totalChanges: 1,\n };\n await model.saveDirtyLayers();\n } else {\n migration = await new MigrationRegistry().applyMigrations(model, {\n fromVersion: options.fromVersion,\n toVersion: options.toVersion,\n dryRun: false,\n validate: options.validate ?? true,\n });\n await model.saveManifest();\n await model.saveDirtyLayers();\n }\n\n return { migration, changesets: await rebaselineChangesets(startPath, baselines) };\n}\n\n/**\n * Hash the model and sort its staged changesets into those whose base snapshot matches it\n * and those that had drifted from it. Call before the model changes.\n *\n * @param model - loaded with lazyLoad: false, as `dr changeset commit` loads it, because the\n * snapshot hashes every loaded layer\n */\nexport async function captureChangesetBaselines(model: Model): Promise<ChangesetBaselines> {\n if (model.lazyLoad) {\n throw new Error(\"captureChangesetBaselines needs a model loaded with lazyLoad: false\");\n }\n\n const { changesets, errors } = await listStagedChangesets(model.rootPath);\n const baselines: ChangesetBaselines = { snapshot: null, inStep: [], drifted: [], errors };\n if (changesets.length === 0) {\n return baselines;\n }\n\n const snapshot = await new BaseSnapshotManager().captureSnapshot(model);\n baselines.snapshot = snapshot;\n for (const { ref, baseSnapshot } of changesets) {\n // A changeset without a base snapshot never reports drift, so it needs no re-baseline\n if (!baseSnapshot) {\n continue;\n }\n (baseSnapshot === snapshot ? baselines.inStep : baselines.drifted).push(ref);\n }\n return baselines;\n}\n\n/**\n * Point the changesets that were in step before the upgrade at the upgraded model. Call after\n * the upgraded model is saved.\n *\n * @param startPath - the path the model was loaded from before the upgrade\n */\nexport async function rebaselineChangesets(\n startPath: string,\n baselines: ChangesetBaselines\n): Promise<ChangesetRebaselineResult> {\n const result: ChangesetRebaselineResult = {\n rebaselined: [],\n drifted: baselines.drifted,\n errors: [...baselines.errors],\n };\n if (baselines.inStep.length === 0) {\n return result;\n }\n\n // Reload rather than hash the model in memory: migrations may rewrite files on disk\n // without updating it, and the commit hashes what it loads from disk\n const upgraded = await Model.load(startPath, { lazyLoad: false });\n const snapshot = await new BaseSnapshotManager().captureSnapshot(upgraded);\n const storage = new StagedChangesetStorage(upgraded.rootPath);\n\n for (const ref of baselines.inStep) {\n try {\n await new FileLock(storage.getChangesetPath(ref.id)).withLock(async () => {\n const changeset = await storage.load(ref.id);\n // Skip a changeset whose status or base changed while the upgrade ran\n if (\n !changeset ||\n changeset.status !== \"staged\" ||\n changeset.baseSnapshot !== baselines.snapshot\n ) {\n return;\n }\n changeset.baseSnapshot = snapshot;\n changeset.updateModified();\n await storage.save(changeset);\n result.rebaselined.push(ref);\n });\n } catch (error) {\n result.errors.push(\n `Could not re-baseline changeset '${ref.name}': ${getErrorMessage(error)}; committing it needs --force`\n );\n }\n }\n return result;\n}\n\nasync function listStagedChangesets(rootPath: string): Promise<{\n changesets: Array<{ ref: ChangesetRef; baseSnapshot: string | undefined }>;\n errors: string[];\n}> {\n const storage = new StagedChangesetStorage(rootPath);\n const changesets: Array<{ ref: ChangesetRef; baseSnapshot: string | undefined }> = [];\n const errors: string[] = [];\n\n let ids: string[];\n try {\n ids = (await readdir(storage.getChangesetsDir(), { withFileTypes: true }))\n // FileLock keeps \"<changeset>.lock\" directories beside the changesets\n .filter((entry) => entry.isDirectory() && !entry.name.endsWith(\".lock\"))\n .map((entry) => entry.name)\n .sort();\n } catch (error) {\n if ((error as { code?: string }).code !== \"ENOENT\") {\n errors.push(`Could not list changesets: ${getErrorMessage(error)}`);\n }\n return { changesets, errors };\n }\n\n for (const id of ids) {\n try {\n const changeset = await storage.load(id);\n if (changeset?.status === \"staged\") {\n changesets.push({\n ref: { id, name: changeset.name || id },\n baseSnapshot: changeset.baseSnapshot,\n });\n }\n } catch (error) {\n errors.push(`Could not read changeset '${id}': ${getErrorMessage(error)}`);\n }\n }\n return { changesets, errors };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/core/model.js
CHANGED
|
@@ -12,6 +12,7 @@ import { startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
|
|
|
12
12
|
import { findProjectRoot } from "../utils/project-paths.js";
|
|
13
13
|
import { getCodebasePath } from "../utils/globals.js";
|
|
14
14
|
import { getErrorMessage } from "../utils/errors.js";
|
|
15
|
+
import { assertBaseModelWritable } from "./changeset-only-guard.js";
|
|
15
16
|
import { getNodeType } from "../generated/node-types.js";
|
|
16
17
|
import path from "node:path";
|
|
17
18
|
import fs from "node:fs/promises";
|
|
@@ -64,6 +65,11 @@ class Model {
|
|
|
64
65
|
_activeChangesetId = null;
|
|
65
66
|
/** Layer names that exist only in staged changes (not yet committed to base model). */
|
|
66
67
|
_stagedLayerNames = /* @__PURE__ */ new Set();
|
|
68
|
+
/**
|
|
69
|
+
* relationships.yaml as committed, captured at load time before the active changeset's staged
|
|
70
|
+
* relationship changes are merged into `relationships`. Null when no changeset was active.
|
|
71
|
+
*/
|
|
72
|
+
_baseRelationships = null;
|
|
67
73
|
constructor(rootPath, manifest, options = {}) {
|
|
68
74
|
this.rootPath = rootPath, this.codebaseRoot = options.codebaseRoot ?? rootPath, this.manifest = manifest, this.graph = new GraphModel(), this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayerFilter = options.layers, this.loadedLayers = /* @__PURE__ */ new Set();
|
|
69
75
|
}
|
|
@@ -83,8 +89,11 @@ class Model {
|
|
|
83
89
|
if (this.lazyLoad && !this.loadedLayers.has(name) && await this.loadLayer(name), this._activeChangesetId)
|
|
84
90
|
try {
|
|
85
91
|
return await this.getVirtualProjectionEngine().projectLayer(this, this._activeChangesetId, name);
|
|
86
|
-
} catch {
|
|
87
|
-
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const errorMessage = err instanceof Error ? err.message : String(err), isChangesetNotFound = errorMessage.includes("Changeset") && errorMessage.includes("not found"), isCorrupted = errorMessage.includes("corrupted");
|
|
94
|
+
if (isChangesetNotFound || isCorrupted)
|
|
95
|
+
return this.layers.get(name);
|
|
96
|
+
throw err;
|
|
88
97
|
}
|
|
89
98
|
return this.layers.get(name);
|
|
90
99
|
}
|
|
@@ -112,6 +121,14 @@ class Model {
|
|
|
112
121
|
getActiveChangesetId() {
|
|
113
122
|
return this._activeChangesetId;
|
|
114
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* The committed relationships, without the active changeset's staged additions and removals
|
|
126
|
+
* that load() merges into `relationships` for read commands. Equal to `relationships.getAll()`
|
|
127
|
+
* when no changeset was active at load time.
|
|
128
|
+
*/
|
|
129
|
+
getBaseRelationships() {
|
|
130
|
+
return this._baseRelationships ? [...this._baseRelationships] : this.relationships.getAll();
|
|
131
|
+
}
|
|
115
132
|
/**
|
|
116
133
|
* Load a layer from disk (model/XX_layername/*.yaml files)
|
|
117
134
|
*/
|
|
@@ -223,10 +240,19 @@ class Model {
|
|
|
223
240
|
const staged = Array.from(this._stagedLayerNames).filter((l) => !this.layers.has(l));
|
|
224
241
|
return [...base, ...staged];
|
|
225
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Records that the active changeset now stages an element change in `layerName`, so
|
|
245
|
+
* getLayerNames() (and findElementLayer()) discover a layer the base model does not have without
|
|
246
|
+
* a reload. StagingAreaManager calls this when it stages into this model's active changeset.
|
|
247
|
+
*/
|
|
248
|
+
noteStagedLayer(changesetId, layerName) {
|
|
249
|
+
this._activeChangesetId && this._activeChangesetId === changesetId && layerName && this._stagedLayerNames.add(layerName);
|
|
250
|
+
}
|
|
226
251
|
/**
|
|
227
252
|
* Save a layer to disk (model/XX_layername/*.yaml)
|
|
228
253
|
*/
|
|
229
254
|
async saveLayer(name) {
|
|
255
|
+
assertBaseModelWritable();
|
|
230
256
|
const layer = this.layers.get(name);
|
|
231
257
|
if (!layer)
|
|
232
258
|
throw new Error(`Layer ${name} not found`);
|
|
@@ -323,7 +349,7 @@ class Model {
|
|
|
323
349
|
* Save the manifest to disk (documentation-robotics/model/manifest.yaml)
|
|
324
350
|
*/
|
|
325
351
|
async saveManifest() {
|
|
326
|
-
this.manifest.updateModified();
|
|
352
|
+
assertBaseModelWritable(), this.manifest.updateModified();
|
|
327
353
|
const yaml = await import("yaml"), manifestPath = `${this.rootPath}/documentation-robotics/model/manifest.yaml`, yamlData = {
|
|
328
354
|
version: this.manifest.version,
|
|
329
355
|
schema: "documentation-robotics-v1",
|
|
@@ -393,15 +419,28 @@ class Model {
|
|
|
393
419
|
}
|
|
394
420
|
/**
|
|
395
421
|
* Save relationships to relationships.yaml
|
|
422
|
+
*
|
|
423
|
+
* @param options.preserveHeader - Keep the file's existing leading comment block
|
|
424
|
+
* (refreshing its `# Last updated:` line) instead of writing the standard header.
|
|
396
425
|
*/
|
|
397
|
-
async saveRelationships() {
|
|
426
|
+
async saveRelationships(options = {}) {
|
|
427
|
+
assertBaseModelWritable();
|
|
398
428
|
const fs2 = await import("fs/promises"), yaml = await import("yaml"), relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;
|
|
399
429
|
await ensureDir(`${this.rootPath}/documentation-robotics/model`);
|
|
400
|
-
const data = this.relationships.toArray(),
|
|
430
|
+
const data = this.relationships.toArray(), timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
431
|
+
let header = `# Intra-layer relationships
|
|
401
432
|
# Format: source_id -> predicate -> target_id
|
|
402
|
-
# Last updated: ${
|
|
433
|
+
# Last updated: ${timestamp}
|
|
403
434
|
|
|
404
|
-
|
|
435
|
+
`;
|
|
436
|
+
if (options.preserveHeader)
|
|
437
|
+
try {
|
|
438
|
+
const { extractRelationshipsHeader } = await import("./relationship-prune.js"), existing = extractRelationshipsHeader(await fs2.readFile(relationshipsPath, "utf-8"));
|
|
439
|
+
existing && (header = existing.replace(/^(# Last updated:).*$/m, `$1 ${timestamp}`) + `
|
|
440
|
+
`);
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
const yamlContent = header + yaml.stringify(data);
|
|
405
444
|
await fs2.writeFile(relationshipsPath, yamlContent, "utf-8"), this.relationships.markClean();
|
|
406
445
|
}
|
|
407
446
|
/**
|
|
@@ -444,11 +483,15 @@ class Model {
|
|
|
444
483
|
const candidateManifestPath = path.join(currentPath, "documentation-robotics", "model", "manifest.yaml");
|
|
445
484
|
try {
|
|
446
485
|
return await fs.access(candidateManifestPath), candidateManifestPath;
|
|
447
|
-
} catch {
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (!(err?.code === "ENOENT"))
|
|
488
|
+
throw err;
|
|
448
489
|
}
|
|
449
490
|
try {
|
|
450
491
|
return await fs.access(path.join(currentPath, ".git")), null;
|
|
451
|
-
} catch {
|
|
492
|
+
} catch (err) {
|
|
493
|
+
if (!(err?.code === "ENOENT"))
|
|
494
|
+
throw err;
|
|
452
495
|
}
|
|
453
496
|
const parentPath = path.dirname(currentPath);
|
|
454
497
|
if (parentPath === currentPath)
|
|
@@ -604,29 +647,37 @@ Run "dr init" to create a model.`);
|
|
|
604
647
|
try {
|
|
605
648
|
const activeId = (await fs.readFile(activePath, "utf-8")).trim();
|
|
606
649
|
if (activeId) {
|
|
607
|
-
model._activeChangesetId = activeId;
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
650
|
+
model._activeChangesetId = activeId, model._baseRelationships = model.relationships.getAll();
|
|
651
|
+
const storage = new StagedChangesetStorage(projectRoot);
|
|
652
|
+
try {
|
|
653
|
+
const changeset = await storage.load(activeId);
|
|
654
|
+
if (changeset)
|
|
655
|
+
for (const change of changeset.changes) {
|
|
656
|
+
if ((change.type === "add" || change.type === "update" || change.type === "delete") && model._stagedLayerNames.add(change.layerName), change.type === "relationship-add" && change.after) {
|
|
657
|
+
const rel = change.after;
|
|
658
|
+
model.relationships.find(rel.source, rel.target, rel.predicate).length === 0 && model.relationships.add({
|
|
659
|
+
source: rel.source,
|
|
660
|
+
target: rel.target,
|
|
661
|
+
predicate: rel.predicate,
|
|
662
|
+
layer: rel.layer,
|
|
663
|
+
...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
|
|
664
|
+
category: rel.category ?? "structural",
|
|
665
|
+
...rel.properties ? { properties: rel.properties } : {}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (change.type === "relationship-delete" && change.before) {
|
|
669
|
+
const rel = change.before;
|
|
670
|
+
model.relationships.delete(rel.source, rel.target, rel.predicate);
|
|
671
|
+
}
|
|
626
672
|
}
|
|
627
|
-
|
|
673
|
+
} catch (err) {
|
|
674
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
675
|
+
throw new Error(`Failed to load active changeset '${activeId}': ${errorMessage}`, { cause: err });
|
|
676
|
+
}
|
|
628
677
|
}
|
|
629
|
-
} catch {
|
|
678
|
+
} catch (err) {
|
|
679
|
+
if (!(err?.code === "ENOENT"))
|
|
680
|
+
throw err;
|
|
630
681
|
model._activeChangesetId = null;
|
|
631
682
|
}
|
|
632
683
|
let entityCount = 0;
|
|
@@ -753,6 +804,7 @@ Run "dr init" to create a model.`);
|
|
|
753
804
|
* @throws Error if atomic write fails
|
|
754
805
|
*/
|
|
755
806
|
async saveDirtyLayersAtomic() {
|
|
807
|
+
assertBaseModelWritable();
|
|
756
808
|
const tempLayerWrites = [];
|
|
757
809
|
try {
|
|
758
810
|
for (const layer of this.layers.values())
|