@sdd-method/author-cli 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +6 -2
- package/dist/author-bin.js +8 -0
- package/dist/author-program.js +99 -0
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +37 -0
- package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +18 -0
- package/dist/lib/api-surface/analyzers/library.js +95 -0
- package/dist/lib/api-surface/analyzers/serverless.js +336 -0
- package/dist/lib/api-surface/analyzers/spring.js +237 -0
- package/dist/lib/api-surface/analyzers/terraform.js +109 -0
- package/dist/lib/api-surface/detect.js +25 -0
- package/dist/lib/api-surface/emit.js +109 -0
- package/dist/lib/api-surface/file-set.js +43 -0
- package/dist/lib/api-surface/index.js +134 -0
- package/dist/lib/api-surface/types.js +17 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +132 -0
- package/dist/lib/briefing/variables.js +92 -0
- package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
- package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
- package/dist/lib/catalogue/archetype/resolver.js +103 -0
- package/dist/lib/catalogue/archetype/wireframe.js +418 -0
- package/dist/lib/catalogue/baseline-check.js +151 -0
- package/dist/lib/catalogue/build.js +1502 -0
- package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
- package/dist/lib/catalogue/builders/adrs.js +222 -0
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
- package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
- package/dist/lib/catalogue/builders/c4-components.js +229 -0
- package/dist/lib/catalogue/builders/c4-containers.js +240 -0
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
- package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
- package/dist/lib/catalogue/builders/c4-systems.js +98 -0
- package/dist/lib/catalogue/builders/capabilities.js +488 -0
- package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
- package/dist/lib/catalogue/builders/capability-edges.js +256 -0
- package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
- package/dist/lib/catalogue/builders/capability-spec.js +348 -0
- package/dist/lib/catalogue/builders/container-dependency.js +183 -0
- package/dist/lib/catalogue/builders/contracts.js +402 -0
- package/dist/lib/catalogue/builders/data-items.js +344 -0
- package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
- package/dist/lib/catalogue/builders/domain-models.js +175 -0
- package/dist/lib/catalogue/builders/domains.js +119 -0
- package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
- package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
- package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
- package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
- package/dist/lib/catalogue/builders/entity-slug.js +47 -0
- package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
- package/dist/lib/catalogue/builders/event-edges.js +327 -0
- package/dist/lib/catalogue/builders/events.js +168 -0
- package/dist/lib/catalogue/builders/evidence-class.js +43 -0
- package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
- package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
- package/dist/lib/catalogue/builders/feature-contract.js +77 -0
- package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
- package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
- package/dist/lib/catalogue/builders/feature-groups.js +63 -0
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
- package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
- package/dist/lib/catalogue/builders/features.js +300 -0
- package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
- package/dist/lib/catalogue/builders/glossary.js +204 -0
- package/dist/lib/catalogue/builders/implementations.js +214 -0
- package/dist/lib/catalogue/builders/index.js +64 -0
- package/dist/lib/catalogue/builders/integration-components.js +344 -0
- package/dist/lib/catalogue/builders/integrations.js +181 -0
- package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
- package/dist/lib/catalogue/builders/persona-container.js +133 -0
- package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
- package/dist/lib/catalogue/builders/personas.js +152 -0
- package/dist/lib/catalogue/builders/product-edges.js +106 -0
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
- package/dist/lib/catalogue/builders/products.js +162 -0
- package/dist/lib/catalogue/builders/quality-scores.js +635 -0
- package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
- package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
- package/dist/lib/catalogue/builders/repo-edges.js +340 -0
- package/dist/lib/catalogue/builders/repositories.js +458 -0
- package/dist/lib/catalogue/builders/scaffolds.js +197 -0
- package/dist/lib/catalogue/builders/screen-tree.js +222 -0
- package/dist/lib/catalogue/builders/screens.js +396 -0
- package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
- package/dist/lib/catalogue/builders/service-contract.js +74 -0
- package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
- package/dist/lib/catalogue/builders/services.js +232 -0
- package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
- package/dist/lib/catalogue/builders/user-journeys.js +253 -0
- package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
- package/dist/lib/catalogue/cli-version.js +10 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/coverage-predicates.js +603 -0
- package/dist/lib/catalogue/coverage-render.js +552 -0
- package/dist/lib/catalogue/coverage-run.js +115 -0
- package/dist/lib/catalogue/csv-reader.js +97 -0
- package/dist/lib/catalogue/csv-writer.js +96 -0
- package/dist/lib/catalogue/data-plane-coherence.js +77 -0
- package/dist/lib/catalogue/ddl-generator.js +233 -0
- package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
- package/dist/lib/catalogue/engine-validator.js +239 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
- package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
- package/dist/lib/catalogue/finding-identity.js +183 -0
- package/dist/lib/catalogue/findings.js +46 -0
- package/dist/lib/catalogue/fk-validator.js +136 -0
- package/dist/lib/catalogue/freshness.js +70 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -0
- package/dist/lib/catalogue/impact-run.js +228 -0
- package/dist/lib/catalogue/manifest-loader.js +435 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/population-checks.js +87 -0
- package/dist/lib/catalogue/relationship-semantics.js +66 -0
- package/dist/lib/catalogue/reverse-product-quality.js +99 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
- package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
- package/dist/lib/catalogue/scaffold-delta.js +261 -0
- package/dist/lib/catalogue/schema-compat.js +52 -0
- package/dist/lib/catalogue/schema-loader.js +87 -0
- package/dist/lib/catalogue/scores-coverage.js +573 -0
- package/dist/lib/catalogue/screen-validation-loader.js +89 -0
- package/dist/lib/catalogue/service-architecture-parser.js +193 -0
- package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
- package/dist/lib/catalogue/staleness-verdicts.js +345 -0
- package/dist/lib/catalogue/traceability-loader.js +350 -0
- package/dist/lib/catalogue/validate.js +200 -0
- package/dist/lib/catalogue/version.js +18 -0
- package/dist/lib/conformance/binding.js +70 -0
- package/dist/lib/conformance/finding.js +69 -0
- package/dist/lib/conformance/index.js +578 -0
- package/dist/lib/context-pull/index.js +191 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/context-pull/retired-mcp-setup.js +7 -0
- package/dist/lib/contracts-availability/index.js +267 -0
- package/dist/lib/generate-briefing/index.js +77 -0
- package/dist/lib/list/discover.js +100 -0
- package/dist/lib/list/format.js +84 -0
- package/dist/lib/list/index.js +16 -0
- package/dist/lib/lock-file.js +437 -0
- package/dist/lib/pack/log.js +18 -0
- package/dist/lib/pack/schema.js +121 -0
- package/dist/lib/pack/seed.js +182 -0
- package/dist/lib/pack/validate.js +49 -0
- package/dist/lib/package-version.js +92 -0
- package/dist/lib/plan/index.js +69 -0
- package/dist/lib/plan/read-sdd.js +102 -0
- package/dist/lib/plan/render-template.js +84 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scaffold/domain.js +234 -0
- package/dist/lib/scaffold/registry.js +136 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/sdd-index/repo-config.js +245 -0
- package/dist/lib/sdd-reader.js +91 -0
- package/dist/lib/snapshot/advance.js +454 -0
- package/dist/lib/snapshot/diff.js +190 -0
- package/dist/lib/snapshot/fold.js +208 -0
- package/dist/lib/snapshot/land.js +178 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/snapshot/targets.js +169 -0
- package/dist/lib/snapshot/verify.js +91 -0
- package/dist/lib/sync/briefing-merge.js +106 -0
- package/dist/lib/sync/bundle-cache.js +109 -0
- package/dist/lib/sync/yaml-block-merge.js +120 -0
- package/dist/lib/validate/aggregate.js +51 -0
- package/dist/lib/validate/bundle-coherence.js +74 -0
- package/dist/lib/validate/dispatch.js +243 -0
- package/dist/lib/validate/index.js +110 -0
- package/dist/lib/validate/plan.js +54 -0
- package/dist/lib/validate/runner.js +85 -0
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/api-surface.js +71 -0
- package/dist/verbs/catalogue-check.js +28 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/conformance.js +34 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/contracts.js +34 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/plan.js +42 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/scaffold.js +83 -0
- package/dist/verbs/snapshot.js +134 -0
- package/dist/verbs/validate.js +48 -0
- package/dist/verify-program.js +122 -0
- package/dist/verify-version.json +4 -0
- package/package.json +32 -4
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { opendir } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { readLockFile } from "../lock-file.js";
|
|
4
|
+
import { readRepoKind } from "../repo-kind.js";
|
|
5
|
+
const SKIP_DIRS = new Set([
|
|
6
|
+
"node_modules",
|
|
7
|
+
".git",
|
|
8
|
+
"dist",
|
|
9
|
+
"build",
|
|
10
|
+
".next",
|
|
11
|
+
".turbo",
|
|
12
|
+
".cache",
|
|
13
|
+
"coverage",
|
|
14
|
+
"tmp",
|
|
15
|
+
"export",
|
|
16
|
+
".venv",
|
|
17
|
+
"__pycache__",
|
|
18
|
+
]);
|
|
19
|
+
export async function discoverSddRepos(roots) {
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
const repos = [];
|
|
22
|
+
for (const root of roots) {
|
|
23
|
+
const abs = resolve(root);
|
|
24
|
+
await walk(abs, seen, repos);
|
|
25
|
+
}
|
|
26
|
+
repos.sort((a, b) => a.path.localeCompare(b.path));
|
|
27
|
+
return repos;
|
|
28
|
+
}
|
|
29
|
+
async function walk(dir, seen, repos) {
|
|
30
|
+
if (seen.has(dir))
|
|
31
|
+
return;
|
|
32
|
+
seen.add(dir);
|
|
33
|
+
const kind = await tryReadRepoKind(dir);
|
|
34
|
+
if (kind) {
|
|
35
|
+
const bundles = await tryReadBundles(dir);
|
|
36
|
+
const forwardBase = bundles.find((b) => b.isForwardBase) ?? bundles[0];
|
|
37
|
+
const bundleVersion = forwardBase?.version ?? null;
|
|
38
|
+
repos.push({ path: dir, kind, bundleVersion, bundles });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let entries;
|
|
42
|
+
try {
|
|
43
|
+
entries = await opendir(dir);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for await (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory())
|
|
50
|
+
continue;
|
|
51
|
+
if (entry.name.startsWith(".") &&
|
|
52
|
+
entry.name !== ".iteraitive" &&
|
|
53
|
+
entry.name !== ".sdd-method") {
|
|
54
|
+
if (SKIP_DIRS.has(entry.name))
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (SKIP_DIRS.has(entry.name))
|
|
58
|
+
continue;
|
|
59
|
+
await walk(join(dir, entry.name), seen, repos);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function tryReadRepoKind(dir) {
|
|
63
|
+
try {
|
|
64
|
+
return await readRepoKind(dir);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function tryReadBundles(dir) {
|
|
71
|
+
try {
|
|
72
|
+
const lock = await readLockFile(dir);
|
|
73
|
+
if (!lock)
|
|
74
|
+
return [];
|
|
75
|
+
return lock.bundles.map(toDiscoveredBundle);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function toDiscoveredBundle(entry) {
|
|
82
|
+
const bundle = {
|
|
83
|
+
name: entry.name,
|
|
84
|
+
version: entry.bundleVersion,
|
|
85
|
+
isForwardBase: entry.isForwardBase,
|
|
86
|
+
managedPathCount: entry.managedPaths.length,
|
|
87
|
+
};
|
|
88
|
+
if (entry.producedByAdopter)
|
|
89
|
+
bundle.producedByAdopter = entry.producedByAdopter;
|
|
90
|
+
if (entry.scope)
|
|
91
|
+
bundle.scope = entry.scope;
|
|
92
|
+
if (entry.requiresForwardBase) {
|
|
93
|
+
bundle.requiresForwardBase = entry.requiresForwardBase.bundle;
|
|
94
|
+
}
|
|
95
|
+
if (entry.requiresPack) {
|
|
96
|
+
bundle.requiresPack = entry.requiresPack.name;
|
|
97
|
+
}
|
|
98
|
+
return bundle;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { profileFromRepoKind } from "../repo-kind.js";
|
|
2
|
+
const PROFILE_ORDER = ["platform", "application", "integration"];
|
|
3
|
+
export function format(repos, opts) {
|
|
4
|
+
const filtered = opts.profileFilter
|
|
5
|
+
? repos.filter((r) => profileFromRepoKind(r.kind) === opts.profileFilter)
|
|
6
|
+
: repos;
|
|
7
|
+
if (opts.format === "json")
|
|
8
|
+
return formatJson(filtered);
|
|
9
|
+
return formatText(filtered, opts.verbose === true);
|
|
10
|
+
}
|
|
11
|
+
function formatJson(repos) {
|
|
12
|
+
const payload = repos.map((r) => ({
|
|
13
|
+
path: r.path,
|
|
14
|
+
profile: profileFromRepoKind(r.kind),
|
|
15
|
+
kind: r.kind,
|
|
16
|
+
bundle_version: r.bundleVersion,
|
|
17
|
+
bundles: r.bundles.map(toJsonBundle),
|
|
18
|
+
}));
|
|
19
|
+
return JSON.stringify(payload, null, 2) + "\n";
|
|
20
|
+
}
|
|
21
|
+
function toJsonBundle(b) {
|
|
22
|
+
const out = {
|
|
23
|
+
name: b.name,
|
|
24
|
+
version: b.version,
|
|
25
|
+
is_forward_base: b.isForwardBase,
|
|
26
|
+
managed_path_count: b.managedPathCount,
|
|
27
|
+
};
|
|
28
|
+
if (b.producedByAdopter)
|
|
29
|
+
out.produced_by_adopter = b.producedByAdopter;
|
|
30
|
+
if (b.scope)
|
|
31
|
+
out.scope = b.scope;
|
|
32
|
+
if (b.requiresForwardBase)
|
|
33
|
+
out.requires_forward_base = b.requiresForwardBase;
|
|
34
|
+
if (b.requiresPack)
|
|
35
|
+
out.requires_pack = b.requiresPack;
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function formatText(repos, verbose) {
|
|
39
|
+
if (repos.length === 0)
|
|
40
|
+
return "No SDD repositories found.\n";
|
|
41
|
+
const grouped = new Map();
|
|
42
|
+
for (const repo of repos) {
|
|
43
|
+
const profile = profileFromRepoKind(repo.kind);
|
|
44
|
+
const list = grouped.get(profile) ?? [];
|
|
45
|
+
list.push(repo);
|
|
46
|
+
grouped.set(profile, list);
|
|
47
|
+
}
|
|
48
|
+
const lines = [];
|
|
49
|
+
for (const profile of PROFILE_ORDER) {
|
|
50
|
+
const list = grouped.get(profile);
|
|
51
|
+
if (!list || list.length === 0)
|
|
52
|
+
continue;
|
|
53
|
+
lines.push(`${profile} (${list.length}):`);
|
|
54
|
+
for (const repo of list) {
|
|
55
|
+
lines.push(formatRepoLine(repo, verbose));
|
|
56
|
+
// Render non-forward-base bundles (adopter packs) as indented
|
|
57
|
+
// '+' lines beneath the repo line. Forward-base info is already
|
|
58
|
+
// in the repo line itself.
|
|
59
|
+
for (const b of repo.bundles) {
|
|
60
|
+
if (b.isForwardBase)
|
|
61
|
+
continue;
|
|
62
|
+
lines.push(formatPackLine(b, verbose));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
lines.push("");
|
|
66
|
+
}
|
|
67
|
+
return lines.join("\n");
|
|
68
|
+
}
|
|
69
|
+
function formatRepoLine(repo, verbose) {
|
|
70
|
+
const version = repo.bundleVersion ?? "no-lock";
|
|
71
|
+
const base = ` ${repo.path} [${version}]`;
|
|
72
|
+
if (!verbose)
|
|
73
|
+
return base;
|
|
74
|
+
const fwd = repo.bundles.find((b) => b.isForwardBase);
|
|
75
|
+
const count = fwd ? ` (${fwd.managedPathCount} managed paths)` : "";
|
|
76
|
+
return base + count;
|
|
77
|
+
}
|
|
78
|
+
function formatPackLine(b, verbose) {
|
|
79
|
+
const producer = b.producedByAdopter ? ` from ${b.producedByAdopter}` : "";
|
|
80
|
+
const requires = b.requiresPack ? ` requires ${b.requiresPack}` : "";
|
|
81
|
+
const count = verbose ? ` (${b.managedPathCount} managed paths)` : "";
|
|
82
|
+
return ` + ${b.name} ${b.version}${producer}${requires}${count}`;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { discoverSddRepos } from "./discover.js";
|
|
2
|
+
import { format } from "./format.js";
|
|
3
|
+
export async function runList(inputs, write) {
|
|
4
|
+
const roots = inputs.workspaceRoots.length > 0
|
|
5
|
+
? inputs.workspaceRoots
|
|
6
|
+
: [process.cwd()];
|
|
7
|
+
const repos = await discoverSddRepos(roots);
|
|
8
|
+
const output = format(repos, {
|
|
9
|
+
format: inputs.format,
|
|
10
|
+
verbose: inputs.verbose === true,
|
|
11
|
+
...(inputs.profileFilter ? { profileFilter: inputs.profileFilter } : {}),
|
|
12
|
+
});
|
|
13
|
+
write(output);
|
|
14
|
+
return { exitCode: 0 };
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { bundleNameFromRepoKind, isForwardBase, readRepoKind, } from "./repo-kind.js";
|
|
4
|
+
export const LOCK_FILE = "method-baseline.lock";
|
|
5
|
+
/**
|
|
6
|
+
* lock_version 3 adds a **per-path sha256** to each `managed_paths` entry.
|
|
7
|
+
*
|
|
8
|
+
* Why: a dry run could previously say only "N conflicting managed file(s)",
|
|
9
|
+
* which reads as "you have local edits at risk". Twice the truth was "N files
|
|
10
|
+
* differ because you are several minors behind and every one is untouched" —
|
|
11
|
+
* established both times by hand-extracting bundles and diffing. The lock
|
|
12
|
+
* identified WHICH bundle was applied but nothing about its CONTENT, and the
|
|
13
|
+
* content is unreachable: forward bases are deliberately not cached
|
|
14
|
+
* (M-ADR 0176 §4.2), `dist/` is gitignored, and lock_version 2 recorded paths
|
|
15
|
+
* without digests. Recording the digest of each file AS APPLIED makes the lock
|
|
16
|
+
* self-verifying: pristine-vs-edited is then decidable from the repo alone —
|
|
17
|
+
* no network, no bundle store, and it survives a fresh clone and CI, which is
|
|
18
|
+
* exactly where the answer is needed.
|
|
19
|
+
*
|
|
20
|
+
* NOT retroactive, by construction: a repo whose last sync predates v3 has no
|
|
21
|
+
* digests to compare against. That case must say so explicitly rather than
|
|
22
|
+
* guess — see `classifyConflicts`.
|
|
23
|
+
*/
|
|
24
|
+
export const LOCK_VERSION = 3;
|
|
25
|
+
/** Lock versions this build can read. Older ones are migrated in-memory on read. */
|
|
26
|
+
const READABLE_LOCK_VERSIONS = [2, 3];
|
|
27
|
+
/** `- <64-hex sha256> <path>` — the sha256sum convention, one line per path. */
|
|
28
|
+
const DIGEST_ENTRY_RE = /^([0-9a-f]{64}) (.+)$/;
|
|
29
|
+
export async function readLockFile(repoRoot) {
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = await readFile(join(repoRoot, LOCK_FILE), "utf8");
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err.code === "ENOENT")
|
|
36
|
+
return null;
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
if (isVersioned(raw))
|
|
40
|
+
return parseLockFile(raw);
|
|
41
|
+
const repoKind = await readRepoKind(repoRoot);
|
|
42
|
+
if (!repoKind) {
|
|
43
|
+
throw new Error(`Legacy lock detected but cannot infer bundle name: ${LOCK_FILE} is v1 ` +
|
|
44
|
+
`and .sdd-repo-kind is missing. Set .sdd-repo-kind manually or ` +
|
|
45
|
+
`delete the lock file before re-syncing.`);
|
|
46
|
+
}
|
|
47
|
+
return parseLockFile(raw, { legacyBundleName: bundleNameFromRepoKind(repoKind) });
|
|
48
|
+
}
|
|
49
|
+
export async function writeLockFile(repoRoot, lock) {
|
|
50
|
+
await writeFile(join(repoRoot, LOCK_FILE), serialiseLockFile(lock), "utf8");
|
|
51
|
+
}
|
|
52
|
+
export function parseLockFile(raw, opts = {}) {
|
|
53
|
+
if (isVersioned(raw))
|
|
54
|
+
return parseVersioned(raw);
|
|
55
|
+
if (!opts.legacyBundleName) {
|
|
56
|
+
throw new Error(`Cannot parse legacy v1 lock without legacyBundleName option ` +
|
|
57
|
+
`(needed to migrate to v2 shape).`);
|
|
58
|
+
}
|
|
59
|
+
return migrateV1(raw, opts.legacyBundleName);
|
|
60
|
+
}
|
|
61
|
+
export function serialiseLockFile(lock) {
|
|
62
|
+
const lines = [
|
|
63
|
+
`method_repo: ${lock.methodRepo}`,
|
|
64
|
+
`lock_version: ${lock.lockVersion}`,
|
|
65
|
+
...(lock.ciBinding !== undefined ? [`ci_binding: ${lock.ciBinding}`] : []),
|
|
66
|
+
...(lock.ciRunner !== undefined ? [`ci_runner: "${lock.ciRunner}"`] : []),
|
|
67
|
+
...(lock.ciWorkflowBlockSha256 !== undefined ? [`ci_workflow_block_sha256: "${lock.ciWorkflowBlockSha256}"`] : []),
|
|
68
|
+
...(lock.ciModuleSha256 !== undefined ? [`ci_module_sha256: "${lock.ciModuleSha256}"`] : []),
|
|
69
|
+
"bundles:",
|
|
70
|
+
];
|
|
71
|
+
for (const b of lock.bundles) {
|
|
72
|
+
lines.push(` - name: ${b.name}`, ` bundle_version: "${b.bundleVersion}"`, ` source_commit: "${b.sourceCommit}"`, ` bundle_sha256: "${b.bundleSha256}"`, ` synced_at_utc: "${b.syncedAtUtc}"`, ` is_forward_base: ${b.isForwardBase}`);
|
|
73
|
+
if (b.producedByAdopter !== undefined) {
|
|
74
|
+
lines.push(` produced_by_adopter: "${b.producedByAdopter}"`);
|
|
75
|
+
}
|
|
76
|
+
if (b.scope !== undefined) {
|
|
77
|
+
lines.push(` scope: "${b.scope}"`);
|
|
78
|
+
}
|
|
79
|
+
if (b.requiresForwardBase) {
|
|
80
|
+
lines.push(` requires_forward_base:`, ` bundle: "${b.requiresForwardBase.bundle}"`, ` minimum_version: "${b.requiresForwardBase.minimumVersion}"`, ` source_repo: "${b.requiresForwardBase.sourceRepo}"`);
|
|
81
|
+
}
|
|
82
|
+
if (b.requiresPack) {
|
|
83
|
+
lines.push(` requires_pack:`, ` name: "${b.requiresPack.name}"`, ` version: "${b.requiresPack.version}"`);
|
|
84
|
+
}
|
|
85
|
+
lines.push(` managed_paths:`);
|
|
86
|
+
for (const p of b.managedPaths) {
|
|
87
|
+
// `- <digest> <path>` where a digest is known, bare `- <path>` otherwise.
|
|
88
|
+
// A bare entry is a truthful "not recorded", which is what a lock migrated
|
|
89
|
+
// from version 2 carries until its next sync.
|
|
90
|
+
const digest = b.pathDigests?.[p];
|
|
91
|
+
lines.push(digest ? ` - ${digest} ${p}` : ` - ${p}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return lines.join("\n") + "\n";
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Internal helpers
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/** Declared lock version, or null for a legacy v1 lock (no version line). */
|
|
100
|
+
function lockVersionOf(raw) {
|
|
101
|
+
const m = /^lock_version: *"?(\d+)"?/m.exec(raw);
|
|
102
|
+
return m ? Number(m[1]) : null;
|
|
103
|
+
}
|
|
104
|
+
function isVersioned(raw) {
|
|
105
|
+
return lockVersionOf(raw) !== null;
|
|
106
|
+
}
|
|
107
|
+
function stripQuotes(value) {
|
|
108
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
109
|
+
return value.slice(1, -1);
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function parseVersioned(raw) {
|
|
114
|
+
let methodRepo;
|
|
115
|
+
let lockVersion;
|
|
116
|
+
let ciWorkflowBlockSha256;
|
|
117
|
+
let ciBinding;
|
|
118
|
+
let ciRunner;
|
|
119
|
+
let ciModuleSha256;
|
|
120
|
+
const bundles = [];
|
|
121
|
+
let inBundles = false;
|
|
122
|
+
let inPaths = false;
|
|
123
|
+
let inForwardBase = false;
|
|
124
|
+
let inPack = false;
|
|
125
|
+
let cur = {
|
|
126
|
+
managedPaths: [],
|
|
127
|
+
};
|
|
128
|
+
let forwardBase = {};
|
|
129
|
+
let pack = {};
|
|
130
|
+
const flushNestedObjects = () => {
|
|
131
|
+
if (inForwardBase && forwardBase.bundle && forwardBase.minimumVersion) {
|
|
132
|
+
cur.requiresForwardBase = {
|
|
133
|
+
bundle: forwardBase.bundle,
|
|
134
|
+
minimumVersion: forwardBase.minimumVersion,
|
|
135
|
+
sourceRepo: forwardBase.sourceRepo ?? "sdd-method",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (inPack && pack.name && pack.version) {
|
|
139
|
+
cur.requiresPack = { name: pack.name, version: pack.version };
|
|
140
|
+
}
|
|
141
|
+
inForwardBase = false;
|
|
142
|
+
inPack = false;
|
|
143
|
+
forwardBase = {};
|
|
144
|
+
pack = {};
|
|
145
|
+
};
|
|
146
|
+
const flushBundle = () => {
|
|
147
|
+
if (cur.name === undefined)
|
|
148
|
+
return;
|
|
149
|
+
flushNestedObjects();
|
|
150
|
+
const required = [
|
|
151
|
+
"name",
|
|
152
|
+
"bundleVersion",
|
|
153
|
+
"sourceCommit",
|
|
154
|
+
"bundleSha256",
|
|
155
|
+
"syncedAtUtc",
|
|
156
|
+
"isForwardBase",
|
|
157
|
+
];
|
|
158
|
+
for (const k of required) {
|
|
159
|
+
if (cur[k] === undefined) {
|
|
160
|
+
throw new Error(`${LOCK_FILE} bundle entry "${cur.name}" missing required key: ${k}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
bundles.push(cur);
|
|
164
|
+
cur = { managedPaths: [] };
|
|
165
|
+
inPaths = false;
|
|
166
|
+
};
|
|
167
|
+
for (const rawLine of raw.split("\n")) {
|
|
168
|
+
const line = rawLine.replace(/\r$/, "");
|
|
169
|
+
if (!inBundles) {
|
|
170
|
+
if (line === "bundles:") {
|
|
171
|
+
inBundles = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const top = /^([a-z_][a-z0-9_]*): (.*)$/.exec(line);
|
|
175
|
+
if (top) {
|
|
176
|
+
const [, key, value] = top;
|
|
177
|
+
if (key === "method_repo")
|
|
178
|
+
methodRepo = stripQuotes(value);
|
|
179
|
+
else if (key === "lock_version")
|
|
180
|
+
lockVersion = Number(stripQuotes(value));
|
|
181
|
+
else if (key === "ci_workflow_block_sha256")
|
|
182
|
+
ciWorkflowBlockSha256 = stripQuotes(value);
|
|
183
|
+
else if (key === "ci_binding")
|
|
184
|
+
ciBinding = stripQuotes(value);
|
|
185
|
+
else if (key === "ci_runner")
|
|
186
|
+
ciRunner = stripQuotes(value);
|
|
187
|
+
else if (key === "ci_module_sha256")
|
|
188
|
+
ciModuleSha256 = stripQuotes(value);
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// Inside the bundles block.
|
|
193
|
+
if (line.startsWith(" - name:")) {
|
|
194
|
+
flushBundle();
|
|
195
|
+
const name = stripQuotes(line.slice(" - name:".length).trim());
|
|
196
|
+
cur.name = name;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (line === " managed_paths:") {
|
|
200
|
+
flushNestedObjects();
|
|
201
|
+
inPaths = true;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (inPaths && line.startsWith(" - ")) {
|
|
205
|
+
// Tolerate a double-quoted scalar. The writer emits these unquoted
|
|
206
|
+
// today, but the entries contain ": " (ADR filenames carry a colon), so
|
|
207
|
+
// a YAML reader parses 113 of 325 entries in one real lock as MAPPINGS
|
|
208
|
+
// rather than strings — silently, with no error. Quoting on write is the
|
|
209
|
+
// fix; reading both forms ships first so no lock can be written that an
|
|
210
|
+
// older reader cannot parse. See method-open-items §3l.
|
|
211
|
+
const entry = stripQuotes(line.slice(" - ".length));
|
|
212
|
+
const withDigest = DIGEST_ENTRY_RE.exec(entry);
|
|
213
|
+
if (withDigest) {
|
|
214
|
+
const [, digest, path] = withDigest;
|
|
215
|
+
cur.managedPaths.push(path);
|
|
216
|
+
(cur.pathDigests ??= {})[path] = digest;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// Bare path: a version-2 entry, or a v3 path whose digest was not
|
|
220
|
+
// recorded. Left out of pathDigests so it reads as "not recorded".
|
|
221
|
+
cur.managedPaths.push(entry);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (line === " requires_forward_base:") {
|
|
226
|
+
flushNestedObjects();
|
|
227
|
+
inForwardBase = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (line === " requires_pack:") {
|
|
231
|
+
flushNestedObjects();
|
|
232
|
+
inPack = true;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
// Two-space-indented nested keys (under requires_forward_base / requires_pack)
|
|
236
|
+
if (inForwardBase || inPack) {
|
|
237
|
+
const nested = /^ ([a-z_][a-z0-9_]*): (.*)$/.exec(line);
|
|
238
|
+
if (nested) {
|
|
239
|
+
const [, key, rawValue] = nested;
|
|
240
|
+
const value = stripQuotes(rawValue);
|
|
241
|
+
if (inForwardBase) {
|
|
242
|
+
if (key === "bundle")
|
|
243
|
+
forwardBase.bundle = value;
|
|
244
|
+
else if (key === "minimum_version")
|
|
245
|
+
forwardBase.minimumVersion = value;
|
|
246
|
+
else if (key === "source_repo")
|
|
247
|
+
forwardBase.sourceRepo = value;
|
|
248
|
+
}
|
|
249
|
+
else if (inPack) {
|
|
250
|
+
if (key === "name")
|
|
251
|
+
pack.name = value;
|
|
252
|
+
else if (key === "version")
|
|
253
|
+
pack.version = value;
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
// A non-nested line ends the object scope.
|
|
258
|
+
flushNestedObjects();
|
|
259
|
+
}
|
|
260
|
+
const meta = /^ ([a-z_][a-z0-9_]*): (.*)$/.exec(line);
|
|
261
|
+
if (meta) {
|
|
262
|
+
inPaths = false;
|
|
263
|
+
const [, key, rawValue] = meta;
|
|
264
|
+
const value = stripQuotes(rawValue);
|
|
265
|
+
switch (key) {
|
|
266
|
+
case "bundle_version":
|
|
267
|
+
cur.bundleVersion = value;
|
|
268
|
+
break;
|
|
269
|
+
case "source_commit":
|
|
270
|
+
cur.sourceCommit = value;
|
|
271
|
+
break;
|
|
272
|
+
case "bundle_sha256":
|
|
273
|
+
cur.bundleSha256 = value;
|
|
274
|
+
break;
|
|
275
|
+
case "synced_at_utc":
|
|
276
|
+
cur.syncedAtUtc = value;
|
|
277
|
+
break;
|
|
278
|
+
case "is_forward_base":
|
|
279
|
+
cur.isForwardBase = value === "true";
|
|
280
|
+
break;
|
|
281
|
+
case "produced_by_adopter":
|
|
282
|
+
cur.producedByAdopter = value;
|
|
283
|
+
break;
|
|
284
|
+
case "scope":
|
|
285
|
+
cur.scope = value;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (line.length > 0 && !line.startsWith(" ") && !line.startsWith("\t")) {
|
|
291
|
+
inBundles = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
flushBundle();
|
|
295
|
+
if (methodRepo === undefined) {
|
|
296
|
+
throw new Error(`${LOCK_FILE} is missing required key: method_repo`);
|
|
297
|
+
}
|
|
298
|
+
// Older readable versions migrate in-memory and are upgraded on the next
|
|
299
|
+
// write; a NEWER version is a hard failure, because this build cannot know
|
|
300
|
+
// what it would be silently dropping.
|
|
301
|
+
if (!READABLE_LOCK_VERSIONS.includes(lockVersion)) {
|
|
302
|
+
throw new Error(`${LOCK_FILE} lock_version is ${lockVersion}; this build reads ` +
|
|
303
|
+
`${READABLE_LOCK_VERSIONS.join(" or ")} and writes ${LOCK_VERSION}` +
|
|
304
|
+
(lockVersion !== undefined && lockVersion > LOCK_VERSION
|
|
305
|
+
? ` — the lock was written by a newer sdd-cli; upgrade rather than downgrading the lock.`
|
|
306
|
+
: `.`));
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
methodRepo,
|
|
310
|
+
lockVersion: LOCK_VERSION,
|
|
311
|
+
bundles,
|
|
312
|
+
...(ciBinding !== undefined ? { ciBinding } : {}),
|
|
313
|
+
...(ciRunner !== undefined ? { ciRunner } : {}),
|
|
314
|
+
...(ciWorkflowBlockSha256 !== undefined ? { ciWorkflowBlockSha256 } : {}),
|
|
315
|
+
...(ciModuleSha256 !== undefined ? { ciModuleSha256 } : {}),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function migrateV1(raw, legacyBundleName) {
|
|
319
|
+
const scalars = new Map();
|
|
320
|
+
const managedPaths = [];
|
|
321
|
+
let inManagedList = false;
|
|
322
|
+
for (const rawLine of raw.split("\n")) {
|
|
323
|
+
const line = rawLine.replace(/\r$/, "");
|
|
324
|
+
if (inManagedList) {
|
|
325
|
+
if (line.startsWith(" - ")) {
|
|
326
|
+
managedPaths.push(line.slice(4));
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
inManagedList = false;
|
|
330
|
+
}
|
|
331
|
+
if (line === "managed_paths:") {
|
|
332
|
+
inManagedList = true;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const m = /^([a-z_][a-z0-9_]*): (.*)$/.exec(line);
|
|
336
|
+
if (m)
|
|
337
|
+
scalars.set(m[1], stripQuotes(m[2]));
|
|
338
|
+
}
|
|
339
|
+
const required = [
|
|
340
|
+
"method_repo",
|
|
341
|
+
"bundle_version",
|
|
342
|
+
"source_commit",
|
|
343
|
+
"bundle_sha256",
|
|
344
|
+
"synced_at_utc",
|
|
345
|
+
];
|
|
346
|
+
for (const key of required) {
|
|
347
|
+
if (!scalars.has(key)) {
|
|
348
|
+
throw new Error(`${LOCK_FILE} (legacy) is missing required key: ${key}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
methodRepo: scalars.get("method_repo"),
|
|
353
|
+
lockVersion: LOCK_VERSION,
|
|
354
|
+
bundles: [
|
|
355
|
+
{
|
|
356
|
+
name: legacyBundleName,
|
|
357
|
+
bundleVersion: scalars.get("bundle_version"),
|
|
358
|
+
sourceCommit: scalars.get("source_commit"),
|
|
359
|
+
bundleSha256: scalars.get("bundle_sha256"),
|
|
360
|
+
syncedAtUtc: scalars.get("synced_at_utc"),
|
|
361
|
+
isForwardBase: isForwardBase(legacyBundleName),
|
|
362
|
+
managedPaths,
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
// Convenience helpers for sync code.
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
export function findBundle(lock, name) {
|
|
371
|
+
if (!lock)
|
|
372
|
+
return null;
|
|
373
|
+
return lock.bundles.find((b) => b.name === name) ?? null;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Replace the bundle entry with `entry.name`, or append it. Returns a
|
|
377
|
+
* new LockFile preserving order (existing bundles keep their position;
|
|
378
|
+
* a new bundle goes at the end).
|
|
379
|
+
*/
|
|
380
|
+
export function upsertBundle(lock, entry) {
|
|
381
|
+
const base = lock ?? {
|
|
382
|
+
methodRepo: "sdd-method",
|
|
383
|
+
lockVersion: LOCK_VERSION,
|
|
384
|
+
bundles: [],
|
|
385
|
+
};
|
|
386
|
+
const idx = base.bundles.findIndex((b) => b.name === entry.name);
|
|
387
|
+
const bundles = idx >= 0
|
|
388
|
+
? base.bundles.map((b, i) => (i === idx ? entry : b))
|
|
389
|
+
: [...base.bundles, entry];
|
|
390
|
+
return { ...base, bundles };
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Remove the bundle entry named `name`, preserving the order of the rest.
|
|
394
|
+
* Returns the lock unchanged if the name is absent. Pure — composed with
|
|
395
|
+
* `upsertBundle` this gives bundle MIGRATION as a single lock value
|
|
396
|
+
* (`upsertBundle(removeBundle(lock, old), incoming)`), so one
|
|
397
|
+
* `writeLockFile` call swaps the blocks with no intermediate state where
|
|
398
|
+
* neither bundle is recorded.
|
|
399
|
+
*/
|
|
400
|
+
export function removeBundle(lock, name) {
|
|
401
|
+
const base = lock ?? {
|
|
402
|
+
methodRepo: "sdd-method",
|
|
403
|
+
lockVersion: LOCK_VERSION,
|
|
404
|
+
bundles: [],
|
|
405
|
+
};
|
|
406
|
+
return { ...base, bundles: base.bundles.filter((b) => b.name !== name) };
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* True if any bundle in the lock other than `excluding` is a forward
|
|
410
|
+
* base. Used to enforce requires_forward_base for additive layers.
|
|
411
|
+
*/
|
|
412
|
+
export function hasForwardBase(lock, excluding) {
|
|
413
|
+
if (!lock)
|
|
414
|
+
return false;
|
|
415
|
+
return lock.bundles.some((b) => b.name !== excluding && b.isForwardBase);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Find the forward-base bundle entry in a lock, or null if none.
|
|
419
|
+
*/
|
|
420
|
+
export function findForwardBase(lock, excluding) {
|
|
421
|
+
if (!lock)
|
|
422
|
+
return null;
|
|
423
|
+
return (lock.bundles.find((b) => b.name !== excluding && b.isForwardBase) ?? null);
|
|
424
|
+
}
|
|
425
|
+
export function pathToBundleMap(lock, excluding) {
|
|
426
|
+
const m = new Map();
|
|
427
|
+
if (!lock)
|
|
428
|
+
return m;
|
|
429
|
+
for (const b of lock.bundles) {
|
|
430
|
+
if (excluding !== undefined && b.name === excluding)
|
|
431
|
+
continue;
|
|
432
|
+
for (const p of b.managedPaths)
|
|
433
|
+
m.set(p, b.name);
|
|
434
|
+
}
|
|
435
|
+
return m;
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=lock-file.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Output helpers shared by `pack build` and `pack validate`. Split out so
|
|
2
|
+
// `pack validate` can be imported without pulling in the builder — the
|
|
3
|
+
// verify-only CLI ships validation and must not ship the code that writes
|
|
4
|
+
// a pack (M-ADR 0212 §4.3b).
|
|
5
|
+
export const SECTION_RULE = "═".repeat(59);
|
|
6
|
+
export function section(title) {
|
|
7
|
+
return `\n${SECTION_RULE}\n ${title}\n${SECTION_RULE}\n\n`;
|
|
8
|
+
}
|
|
9
|
+
export function info(msg) {
|
|
10
|
+
return `[INFO] ${msg}\n`;
|
|
11
|
+
}
|
|
12
|
+
export function pass(msg) {
|
|
13
|
+
return `[PASS] ${msg}\n`;
|
|
14
|
+
}
|
|
15
|
+
export function fail(msg) {
|
|
16
|
+
return `[FAIL] ${msg}\n`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=log.js.map
|