@sdd-method/sdd-cli 0.47.0 → 0.49.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.
Files changed (44) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +2 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/bundle.d.ts +6 -0
  5. package/dist/lib/bundle.d.ts.map +1 -1
  6. package/dist/lib/bundle.js +5 -0
  7. package/dist/lib/bundle.js.map +1 -1
  8. package/dist/lib/pack/builder.d.ts.map +1 -1
  9. package/dist/lib/pack/builder.js +12 -4
  10. package/dist/lib/pack/builder.js.map +1 -1
  11. package/dist/lib/pack/index.d.ts.map +1 -1
  12. package/dist/lib/pack/index.js +9 -2
  13. package/dist/lib/pack/index.js.map +1 -1
  14. package/dist/lib/pack/leakage.d.ts.map +1 -1
  15. package/dist/lib/pack/leakage.js +21 -10
  16. package/dist/lib/pack/leakage.js.map +1 -1
  17. package/dist/lib/pack/schema.d.ts +3 -2
  18. package/dist/lib/pack/schema.d.ts.map +1 -1
  19. package/dist/lib/pack/schema.js +23 -3
  20. package/dist/lib/pack/schema.js.map +1 -1
  21. package/dist/lib/pack/seed.d.ts +14 -1
  22. package/dist/lib/pack/seed.d.ts.map +1 -1
  23. package/dist/lib/pack/seed.js +14 -7
  24. package/dist/lib/pack/seed.js.map +1 -1
  25. package/dist/lib/scaffold/domain.d.ts +30 -0
  26. package/dist/lib/scaffold/domain.d.ts.map +1 -0
  27. package/dist/lib/scaffold/domain.js +234 -0
  28. package/dist/lib/scaffold/domain.js.map +1 -0
  29. package/dist/lib/scaffold/registry.d.ts +43 -0
  30. package/dist/lib/scaffold/registry.d.ts.map +1 -0
  31. package/dist/lib/scaffold/registry.js +104 -0
  32. package/dist/lib/scaffold/registry.js.map +1 -0
  33. package/dist/lib/sync/bundle-cache.d.ts +45 -0
  34. package/dist/lib/sync/bundle-cache.d.ts.map +1 -0
  35. package/dist/lib/sync/bundle-cache.js +92 -0
  36. package/dist/lib/sync/bundle-cache.js.map +1 -0
  37. package/dist/lib/sync/index.d.ts.map +1 -1
  38. package/dist/lib/sync/index.js +25 -3
  39. package/dist/lib/sync/index.js.map +1 -1
  40. package/dist/verbs/scaffold.d.ts +3 -0
  41. package/dist/verbs/scaffold.d.ts.map +1 -0
  42. package/dist/verbs/scaffold.js +82 -0
  43. package/dist/verbs/scaffold.js.map +1 -0
  44. package/package.json +1 -1
@@ -0,0 +1,234 @@
1
+ import { access, copyFile, mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join, relative } from "node:path";
3
+ /**
4
+ * Domain scaffolding (ADR 0173). Writes the invariant domain shape into a
5
+ * platform-root SDD, optionally seeded from a domain archetype. The generic
6
+ * shape is method-owned and domain-agnostic; archetypes overlay starter content.
7
+ *
8
+ * Only the `integration` archetype is built in (the method tier — the method
9
+ * owns that vocabulary via ADR 0171/0172). Reference-architecture and adopter
10
+ * archetypes are resolved from a consumed registry (not in this prototype).
11
+ */
12
+ export const METHOD_DOMAIN_ARCHETYPES = ["integration"];
13
+ export function isMethodDomainArchetype(value) {
14
+ return METHOD_DOMAIN_ARCHETYPES.includes(value);
15
+ }
16
+ export async function scaffoldDomain(input) {
17
+ const base = join(input.targetRepo, "docs", "domains", input.name);
18
+ const filesWritten = [];
19
+ const write = async (rel, content) => {
20
+ await writeFileAt(join(base, rel), content);
21
+ filesWritten.push(join("docs", "domains", input.name, rel));
22
+ };
23
+ const writeIfMissing = async (rel, content) => {
24
+ if (await fileExists(join(base, rel)))
25
+ return;
26
+ await write(rel, content);
27
+ };
28
+ // Registry archetype (ADR 0176 §4.4): copy seed files from the bundle
29
+ // cache FIRST — copy-and-own; the generic shape then fills only the
30
+ // gaps the seed didn't provide.
31
+ if (input.registryArchetype) {
32
+ const copied = await copyRegistrySeeds(input.registryArchetype, base);
33
+ for (const rel of copied) {
34
+ filesWritten.push(join("docs", "domains", input.name, rel));
35
+ }
36
+ }
37
+ await writeIfMissing("README.md", renderDomainReadme(input));
38
+ await writeIfMissing("architecture/glossary.md", renderDomainGlossary(input));
39
+ await writeIfMissing("architecture/data-model.mermaid.md", renderDomainDataModel(input));
40
+ await writeIfMissing("architecture/diagrams/.gitkeep", "");
41
+ await writeIfMissing("architecture/adr/.gitkeep", "");
42
+ await writeIfMissing("design/.gitkeep", "");
43
+ await writeIfMissing("product/.gitkeep", "");
44
+ if (input.withContracts) {
45
+ await writeIfMissing("contracts/.gitkeep", "");
46
+ }
47
+ // Archetype overlay (method tier).
48
+ if (input.archetype === "integration") {
49
+ await write("architecture/interaction-patterns.md", renderIntegrationArchetype());
50
+ }
51
+ return { filesWritten };
52
+ }
53
+ /**
54
+ * Copy a registry archetype's seed files from the bundle cache into the
55
+ * domain directory, stripping the seed prefix: a seed prefix
56
+ * `core-b2b-saas/domains/identity/` maps its files onto the domain root.
57
+ * Returns the relative paths written (relative to the domain dir).
58
+ */
59
+ async function copyRegistrySeeds(archetype, domainDir) {
60
+ const written = [];
61
+ for (const seedPrefix of archetype.seeds) {
62
+ const seedRoot = join(archetype.bundleCacheDir, seedPrefix);
63
+ const files = await walkFiles(seedRoot);
64
+ for (const abs of files) {
65
+ const rel = relative(seedRoot, abs);
66
+ const dst = join(domainDir, rel);
67
+ await mkdir(dirname(dst), { recursive: true });
68
+ await copyFile(abs, dst);
69
+ written.push(rel);
70
+ }
71
+ }
72
+ if (written.length === 0) {
73
+ throw new Error(`Archetype '${archetype.id}' (bundle ${archetype.bundleName}) has no seed files in the cache — re-sync the bundle.`);
74
+ }
75
+ return written;
76
+ }
77
+ async function walkFiles(root) {
78
+ const out = [];
79
+ let entries;
80
+ try {
81
+ entries = await readdir(root, { withFileTypes: true });
82
+ }
83
+ catch {
84
+ return out;
85
+ }
86
+ for (const e of entries) {
87
+ const child = join(root, e.name);
88
+ if (e.isDirectory()) {
89
+ out.push(...(await walkFiles(child)));
90
+ }
91
+ else if (e.isFile()) {
92
+ out.push(child);
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+ async function fileExists(path) {
98
+ try {
99
+ await access(path);
100
+ return true;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ function titleize(slug) {
107
+ return slug
108
+ .split("-")
109
+ .filter((p) => p.length > 0)
110
+ .map((p) => p[0].toUpperCase() + p.slice(1))
111
+ .join(" ");
112
+ }
113
+ function renderDomainReadme(input) {
114
+ const title = titleize(input.name);
115
+ const intro = input.archetype === "integration"
116
+ ? "Provider-specific integrations that bridge external systems to the platform via canonical contracts. Each integration is a separate `integration-product` SDD that registers into this domain (ADR 0136)."
117
+ : `TODO: one-paragraph description of the ${title} domain — the capability slice it owns and its boundary.`;
118
+ return ([
119
+ `# ${title} Domain`,
120
+ "",
121
+ `> ${intro}`,
122
+ "",
123
+ "## Architecture",
124
+ "",
125
+ "- [Glossary](architecture/glossary.md) — domain vocabulary",
126
+ "- [Data Model](architecture/data-model.mermaid.md) — entity-relationship diagram",
127
+ "- [ADRs](architecture/adr/) — domain-specific architecture decisions",
128
+ "- [Diagrams](architecture/diagrams/) — C4 and other diagrams",
129
+ ...(input.archetype === "integration"
130
+ ? [
131
+ "- [Interaction Patterns](architecture/interaction-patterns.md) — ingestion / invocation patterns (sdd-method#0171/#0172)",
132
+ ]
133
+ : []),
134
+ "",
135
+ "## Product",
136
+ "",
137
+ "- [Capability Specs](product/) — domain capabilities",
138
+ "",
139
+ "## Design",
140
+ "",
141
+ "- [Design](design/) — feature/design grounding",
142
+ ...(input.withContracts
143
+ ? ["", "## Contracts", "", "- [Contracts](contracts/) — contracts this domain exposes"]
144
+ : []),
145
+ "",
146
+ ].join("\n"));
147
+ }
148
+ function renderDomainGlossary(input) {
149
+ const title = titleize(input.name);
150
+ if (input.archetype === "integration") {
151
+ return ([
152
+ `# ${title} Domain Glossary`,
153
+ "",
154
+ "> Vocabulary for this domain. Interaction-pattern terms below are anchored in",
155
+ "> Enterprise Integration Patterns (sdd-method#0172).",
156
+ "",
157
+ "| Term | Definition |",
158
+ "|------|------------|",
159
+ "| Provider | An external system this platform integrates with. |",
160
+ "| Ingestion pattern | How an integration acquires data (event-stream-consume / webhook / socket-listen / poll). |",
161
+ "| Invocation pattern | How an integration invokes a provider (request-response / async-request / publish). |",
162
+ "| CanonicalEvent | The normalised event an integration translates provider data into. |",
163
+ "",
164
+ ].join("\n"));
165
+ }
166
+ return ([
167
+ `# ${title} Domain Glossary`,
168
+ "",
169
+ "> Owned vocabulary for this domain. One row per term the domain defines.",
170
+ "",
171
+ "| Term | Definition |",
172
+ "|------|------------|",
173
+ "| TODO | TODO — define the domain's core terms. |",
174
+ "",
175
+ ].join("\n"));
176
+ }
177
+ function renderDomainDataModel(input) {
178
+ const title = titleize(input.name);
179
+ return ([
180
+ `# ${title} Domain — Data Model`,
181
+ "",
182
+ "> Entity-relationship diagram for this domain (ADR 0142 — first-class catalogue entity).",
183
+ "",
184
+ "```mermaid",
185
+ "erDiagram",
186
+ " %% TODO: model this domain's entities and relationships",
187
+ " ENTITY {",
188
+ " string id",
189
+ " }",
190
+ "```",
191
+ "",
192
+ ].join("\n"));
193
+ }
194
+ function renderIntegrationArchetype() {
195
+ return ([
196
+ "# Integration Interaction Patterns",
197
+ "",
198
+ "> Seeded by the `integration` domain archetype (sdd-method#0173). The interaction",
199
+ "> pattern is the adopter-agnostic, EIP-anchored data-flow axis (sdd-method#0171),",
200
+ "> orthogonal to provider category (`profileType`, sdd-method#0137).",
201
+ "",
202
+ "## Ingestion (inbound)",
203
+ "",
204
+ "| Pattern | Meaning |",
205
+ "|---------|---------|",
206
+ "| `event-stream-consume` | Subscribe to / drain a provider-hosted broker, stream or feed. |",
207
+ "| `webhook` | Expose an endpoint the provider pushes events to. |",
208
+ "| `socket-listen` | Accept connection-oriented sessions and read framed data. |",
209
+ "| `poll` | Request the provider on a schedule. |",
210
+ "",
211
+ "## Invocation (outbound)",
212
+ "",
213
+ "| Pattern | Meaning |",
214
+ "|---------|---------|",
215
+ "| `request-response` | Synchronous call-and-wait. |",
216
+ "| `async-request` | Submit; result returns later via a separate channel, correlated. |",
217
+ "| `publish` | Fire-and-forget write to a broker. |",
218
+ "",
219
+ "## Adopter framework roles",
220
+ "",
221
+ "If your platform runs an integration/component framework, map its component roles",
222
+ "onto these patterns **here** (sdd-method#0171 §9.5) — the method stays role-name-neutral.",
223
+ "",
224
+ "| Your role | Ingestion / Invocation pattern |",
225
+ "|-----------|-------------------------------|",
226
+ "| TODO | TODO |",
227
+ "",
228
+ ].join("\n"));
229
+ }
230
+ async function writeFileAt(path, content) {
231
+ await mkdir(dirname(path), { recursive: true });
232
+ await writeFile(path, content, "utf8");
233
+ }
234
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../../src/lib/scaffold/domain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Archetype-registry resolution (ADR 0176 §4.1/§4.4).
3
+ *
4
+ * Non-method-tier domain archetypes are resolved from the registries of
5
+ * consumed bundles in the local bundle cache
6
+ * (`.sdd/bundle-cache/<bundle>@<version>/registry.yaml`). The method
7
+ * tier stays built into sdd-cli (ADR 0173).
8
+ *
9
+ * Reference form: `<id>` resolves when unique across cached bundles;
10
+ * `<bundle>:<id>` qualifies on collision (mirrors ADR 0140 §4.5).
11
+ */
12
+ export interface RegistryArchetype {
13
+ id: string;
14
+ tier: string;
15
+ layer?: string;
16
+ domainName?: string;
17
+ status?: string;
18
+ seeds: string[];
19
+ notes?: string;
20
+ /** Bundle (name, no version) whose registry declared this archetype. */
21
+ bundleName: string;
22
+ /** Absolute path to the cached bundle root (payload-shaped). */
23
+ bundleCacheDir: string;
24
+ }
25
+ export interface RegistryResolution {
26
+ ok: boolean;
27
+ archetype?: RegistryArchetype;
28
+ /** All discovered archetypes (for error messages / listings). */
29
+ available: RegistryArchetype[];
30
+ error?: string;
31
+ }
32
+ /**
33
+ * Scan the bundle cache for registries and return every archetype found.
34
+ * Bundles without a registry.yaml are skipped silently (vendor packs etc.).
35
+ */
36
+ export declare function discoverArchetypes(targetRepo: string): Promise<RegistryArchetype[]>;
37
+ /**
38
+ * Resolve an archetype reference against the cache. Accepts bare ids
39
+ * (`core/identity`) and bundle-qualified ids
40
+ * (`ref-arch-core-b2b-saas:core/identity`).
41
+ */
42
+ export declare function resolveArchetype(targetRepo: string, reference: string): Promise<RegistryResolution>;
43
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/scaffold/registry.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,iEAAiE;IACjE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAQD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAuD9B;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAmC7B"}
@@ -0,0 +1,104 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { BUNDLE_CACHE_REL, REGISTRY_FILE } from "../sync/bundle-cache.js";
5
+ /**
6
+ * Scan the bundle cache for registries and return every archetype found.
7
+ * Bundles without a registry.yaml are skipped silently (vendor packs etc.).
8
+ */
9
+ export async function discoverArchetypes(targetRepo) {
10
+ const cacheRoot = join(targetRepo, BUNDLE_CACHE_REL);
11
+ let entries;
12
+ try {
13
+ entries = await readdir(cacheRoot, { withFileTypes: true });
14
+ }
15
+ catch {
16
+ return [];
17
+ }
18
+ const out = [];
19
+ for (const e of entries) {
20
+ if (!e.isDirectory())
21
+ continue;
22
+ const bundleCacheDir = join(cacheRoot, e.name);
23
+ const at = e.name.lastIndexOf("@");
24
+ const bundleName = at > 0 ? e.name.slice(0, at) : e.name;
25
+ let raw;
26
+ try {
27
+ raw = await readFile(join(bundleCacheDir, REGISTRY_FILE), "utf8");
28
+ }
29
+ catch {
30
+ continue; // no registry in this bundle
31
+ }
32
+ let parsed;
33
+ try {
34
+ parsed = parseYaml(raw);
35
+ }
36
+ catch {
37
+ continue; // malformed registry — skip rather than break scaffold
38
+ }
39
+ if (parsed === null || typeof parsed !== "object")
40
+ continue;
41
+ if (!Array.isArray(parsed.archetypes))
42
+ continue;
43
+ const tier = typeof parsed.tier === "string" ? parsed.tier : "unknown";
44
+ for (const a of parsed.archetypes) {
45
+ if (a === null || typeof a !== "object")
46
+ continue;
47
+ const rec = a;
48
+ const id = typeof rec.id === "string" ? rec.id : undefined;
49
+ const seeds = Array.isArray(rec.seeds)
50
+ ? rec.seeds.filter((s) => typeof s === "string")
51
+ : [];
52
+ if (!id || seeds.length === 0)
53
+ continue;
54
+ out.push({
55
+ id,
56
+ tier,
57
+ seeds,
58
+ bundleName,
59
+ bundleCacheDir,
60
+ ...(typeof rec.layer === "string" && { layer: rec.layer }),
61
+ ...(typeof rec.domain_name === "string" && { domainName: rec.domain_name }),
62
+ ...(typeof rec.status === "string" && { status: rec.status }),
63
+ ...(typeof rec.notes === "string" && { notes: rec.notes }),
64
+ });
65
+ }
66
+ }
67
+ return out;
68
+ }
69
+ /**
70
+ * Resolve an archetype reference against the cache. Accepts bare ids
71
+ * (`core/identity`) and bundle-qualified ids
72
+ * (`ref-arch-core-b2b-saas:core/identity`).
73
+ */
74
+ export async function resolveArchetype(targetRepo, reference) {
75
+ const available = await discoverArchetypes(targetRepo);
76
+ let bundleFilter;
77
+ let id = reference;
78
+ const colon = reference.indexOf(":");
79
+ if (colon > 0) {
80
+ bundleFilter = reference.slice(0, colon);
81
+ id = reference.slice(colon + 1);
82
+ }
83
+ const matches = available.filter((a) => a.id === id && (bundleFilter === undefined || a.bundleName === bundleFilter));
84
+ if (matches.length === 1) {
85
+ return { ok: true, archetype: matches[0], available };
86
+ }
87
+ if (matches.length === 0) {
88
+ const known = available.map((a) => `${a.bundleName}:${a.id}`).join(", ");
89
+ return {
90
+ ok: false,
91
+ available,
92
+ error: available.length === 0
93
+ ? `No archetype registries found in ${BUNDLE_CACHE_REL}/ — sync an archetype-bearing bundle first (ADR 0176).`
94
+ : `Archetype '${reference}' not found in cached registries. Available: ${known}`,
95
+ };
96
+ }
97
+ const collisions = matches.map((a) => `${a.bundleName}:${a.id}`).join(", ");
98
+ return {
99
+ ok: false,
100
+ available,
101
+ error: `Archetype id '${id}' is declared by multiple bundles (${collisions}). Use the qualified form '<bundle>:<id>'.`,
102
+ };
103
+ }
104
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/lib/scaffold/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Consumer-side bundle cache (ADR 0176 §4.2).
3
+ *
4
+ * Every consumed non-forward-base bundle is extracted to
5
+ * `<targetRepo>/.sdd/bundle-cache/<bundleName>@<version>/` — the
6
+ * resolution substrate for `sdd-cli scaffold domain --archetype` and
7
+ * the staging source for materialise-mode paths. The cache directory
8
+ * is gitignored (ensured at sync time).
9
+ *
10
+ * Cache layout per bundle: the bundle's payload tree, verbatim (so
11
+ * registry.yaml and scaffold-source files sit at their shipped paths).
12
+ * Stale versions of the same bundle name are pruned on refresh.
13
+ */
14
+ export declare const BUNDLE_CACHE_REL = ".sdd/bundle-cache";
15
+ /** The registry file name (ADR 0176 §4.1) — always cache-only. */
16
+ export declare const REGISTRY_FILE = "registry.yaml";
17
+ /**
18
+ * Split a bundle's managed paths into the set to materialise into the
19
+ * repo tree vs the set that stays cache-only (ADR 0176 §4.3):
20
+ * scaffold-source-prefixed paths and the registry file are cache-only.
21
+ */
22
+ export declare function splitConsumptionModes(managedPaths: readonly string[], scaffoldSourcePaths: readonly string[] | undefined): {
23
+ materialise: string[];
24
+ cacheOnly: string[];
25
+ };
26
+ export interface CacheBundleInput {
27
+ targetRepo: string;
28
+ bundleName: string;
29
+ bundleVersion: string;
30
+ /** All managed paths (the full payload is cached, both modes). */
31
+ managedPaths: readonly string[];
32
+ payloadDir: string;
33
+ }
34
+ /**
35
+ * Extract a bundle's payload into the cache, replacing any previously
36
+ * cached version of the same bundle name (one cached version per
37
+ * bundle — the lock pins exactly one).
38
+ */
39
+ export declare function cacheBundle(input: CacheBundleInput): Promise<string>;
40
+ /**
41
+ * Ensure the consumer's .gitignore excludes the bundle cache. Appends
42
+ * a marked line when absent; leaves adopter content untouched otherwise.
43
+ */
44
+ export declare function ensureCacheGitignored(targetRepo: string): Promise<boolean>;
45
+ //# sourceMappingURL=bundle-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-cache.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/bundle-cache.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD,kEAAkE;AAClE,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,mBAAmB,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACjD;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAchD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAY1E;AAmBD;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBhF"}
@@ -0,0 +1,92 @@
1
+ import { appendFile, copyFile, mkdir, readFile, readdir, rm } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ /**
4
+ * Consumer-side bundle cache (ADR 0176 §4.2).
5
+ *
6
+ * Every consumed non-forward-base bundle is extracted to
7
+ * `<targetRepo>/.sdd/bundle-cache/<bundleName>@<version>/` — the
8
+ * resolution substrate for `sdd-cli scaffold domain --archetype` and
9
+ * the staging source for materialise-mode paths. The cache directory
10
+ * is gitignored (ensured at sync time).
11
+ *
12
+ * Cache layout per bundle: the bundle's payload tree, verbatim (so
13
+ * registry.yaml and scaffold-source files sit at their shipped paths).
14
+ * Stale versions of the same bundle name are pruned on refresh.
15
+ */
16
+ export const BUNDLE_CACHE_REL = ".sdd/bundle-cache";
17
+ /** The registry file name (ADR 0176 §4.1) — always cache-only. */
18
+ export const REGISTRY_FILE = "registry.yaml";
19
+ /**
20
+ * Split a bundle's managed paths into the set to materialise into the
21
+ * repo tree vs the set that stays cache-only (ADR 0176 §4.3):
22
+ * scaffold-source-prefixed paths and the registry file are cache-only.
23
+ */
24
+ export function splitConsumptionModes(managedPaths, scaffoldSourcePaths) {
25
+ const prefixes = scaffoldSourcePaths ?? [];
26
+ const materialise = [];
27
+ const cacheOnly = [];
28
+ for (const p of managedPaths) {
29
+ const isScaffoldSource = prefixes.some((prefix) => p.startsWith(prefix));
30
+ const isRegistry = p === REGISTRY_FILE;
31
+ if (isScaffoldSource || isRegistry) {
32
+ cacheOnly.push(p);
33
+ }
34
+ else {
35
+ materialise.push(p);
36
+ }
37
+ }
38
+ return { materialise, cacheOnly };
39
+ }
40
+ /**
41
+ * Extract a bundle's payload into the cache, replacing any previously
42
+ * cached version of the same bundle name (one cached version per
43
+ * bundle — the lock pins exactly one).
44
+ */
45
+ export async function cacheBundle(input) {
46
+ const cacheRoot = join(input.targetRepo, BUNDLE_CACHE_REL);
47
+ await prunePreviousVersions(cacheRoot, input.bundleName);
48
+ const dest = join(cacheRoot, `${input.bundleName}@${input.bundleVersion}`);
49
+ for (const p of input.managedPaths) {
50
+ const src = join(input.payloadDir, p);
51
+ const dst = join(dest, p);
52
+ await mkdir(dirname(dst), { recursive: true });
53
+ await copyFile(src, dst);
54
+ }
55
+ return dest;
56
+ }
57
+ async function prunePreviousVersions(cacheRoot, bundleName) {
58
+ let entries;
59
+ try {
60
+ entries = await readdir(cacheRoot, { withFileTypes: true });
61
+ }
62
+ catch {
63
+ return; // no cache yet
64
+ }
65
+ for (const e of entries) {
66
+ if (e.isDirectory() && e.name.startsWith(`${bundleName}@`)) {
67
+ await rm(join(cacheRoot, e.name), { recursive: true, force: true });
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Ensure the consumer's .gitignore excludes the bundle cache. Appends
73
+ * a marked line when absent; leaves adopter content untouched otherwise.
74
+ */
75
+ export async function ensureCacheGitignored(targetRepo) {
76
+ const giPath = join(targetRepo, ".gitignore");
77
+ const line = `${BUNDLE_CACHE_REL}/`;
78
+ let existing = "";
79
+ try {
80
+ existing = await readFile(giPath, "utf8");
81
+ }
82
+ catch {
83
+ // no .gitignore yet — create one below
84
+ }
85
+ if (existing.split("\n").some((l) => l.trim() === line || l.trim() === BUNDLE_CACHE_REL)) {
86
+ return false;
87
+ }
88
+ const prefixNewline = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
89
+ await appendFile(giPath, `${prefixNewline}# sdd-cli bundle cache (ADR 0176) — extracted bundle payloads, not source\n${line}\n`);
90
+ return true;
91
+ }
92
+ //# sourceMappingURL=bundle-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-cache.js","sourceRoot":"","sources":["../../../src/lib/sync/bundle-cache.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EASL,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AAmBzB,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAa/E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,OAAO,CAC3B,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAA2C,GACpE,OAAO,CAAC,UAAU,CAAC,CA8WrB;AA2BD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,cAAc,EACxB,iBAAiB,CAAC,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAUf;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,EACpD,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAqBf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC3C,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAef;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAkCtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EASL,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AAwBzB,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAa/E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,OAAO,CAC3B,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAA2C,GACpE,OAAO,CAAC,UAAU,CAAC,CAiZrB;AA2BD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,cAAc,EACxB,iBAAiB,CAAC,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAUf;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,EACpD,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAqBf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC3C,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,IAAI,CAef;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAkCtE"}
@@ -6,6 +6,7 @@ import { findBundle, findForwardBase, hasForwardBase, pathToBundleMap, readLockF
6
6
  import { REPO_KIND_FILE, profileFromBundleName, repoKindFromProfile, writeRepoKind, } from "../repo-kind.js";
7
7
  import { join } from "node:path";
8
8
  import { copyManagedFiles, removeManagedFiles } from "./apply.js";
9
+ import { cacheBundle, ensureCacheGitignored, splitConsumptionModes, } from "./bundle-cache.js";
9
10
  import { PROTECTED_PREFIXES, computeRemovals, findConflicts, findMissingPayloadFiles, findProtectedPaths, } from "./diff.js";
10
11
  import { syncMethodManagedSettings } from "./settings-merge.js";
11
12
  import { syncMethodManagedCopilotHooks } from "./copilot-hooks-merge.js";
@@ -45,13 +46,17 @@ export async function runSync(opts, stdout = (line) => process.stdout.write(line
45
46
  stdout(fail(`Missing payload file listed in MANAGED_PATHS.txt: ${missing[0]}`));
46
47
  return { exitCode: 1 };
47
48
  }
49
+ // ADR 0176 §4.3: split consumption modes. Scaffold-source paths and
50
+ // the registry file extract to the bundle cache only; everything
51
+ // else materialises into the repo tree as before.
52
+ const { materialise: materialisePaths, cacheOnly: cacheOnlyPaths } = splitConsumptionModes(bundle.managedPaths, bundle.manifest.scaffoldSourcePaths);
48
53
  const protectedIncoming = findProtectedPaths(bundle.managedPaths);
49
54
  if (protectedIncoming.length > 0) {
50
55
  stdout(fail(`Incoming bundle attempts to manage protected product-owned path: ${protectedIncoming[0]}`));
51
56
  stdout(fail(`Method baseline bundles must not manage files under: ${PROTECTED_PREFIXES.join(" ")}`));
52
57
  return { exitCode: 1 };
53
58
  }
54
- const conflicts = await findConflicts(bundle.managedPaths, bundle.payloadDir, opts.targetRepo);
59
+ const conflicts = await findConflicts(materialisePaths, bundle.payloadDir, opts.targetRepo);
55
60
  if (conflicts.length > 0 && !opts.allowOverwrite) {
56
61
  stdout(fail(`Found ${conflicts.length} conflicting managed file(s). Re-run with --allow-overwrite to apply.`));
57
62
  for (const p of conflicts)
@@ -112,11 +117,14 @@ export async function runSync(opts, stdout = (line) => process.stdout.write(line
112
117
  // Removals are scoped to THIS bundle's previously-managed paths.
113
118
  const prevBundleEntry = findBundle(prevLock, incomingBundleName);
114
119
  const prevPaths = prevBundleEntry?.managedPaths ?? [];
115
- const removals = computeRemovals(prevPaths, bundle.managedPaths, {
120
+ const removals = computeRemovals(prevPaths, materialisePaths, {
116
121
  noDelete: !opts.delete,
117
122
  });
118
123
  stdout(info(`Bundle: ${incomingBundleName} (version: ${bundle.manifest.bundleVersion})`));
119
124
  stdout(info(`Incoming managed files: ${bundle.managedPaths.length}`));
125
+ if (cacheOnlyPaths.length > 0) {
126
+ stdout(info(`Cache-only (scaffold-source/registry) files: ${cacheOnlyPaths.length} — extracted to .sdd/bundle-cache, not materialised`));
127
+ }
120
128
  stdout(info(`Conflicts: ${conflicts.length}`));
121
129
  stdout(info(`Removals from previous sync of this bundle: ${removals.length}`));
122
130
  stdout(info(`Protected ownership prefixes: ${PROTECTED_PREFIXES.join(" ")}`));
@@ -134,8 +142,22 @@ export async function runSync(opts, stdout = (line) => process.stdout.write(line
134
142
  }
135
143
  return { exitCode: 0 };
136
144
  }
137
- await copyManagedFiles(bundle.managedPaths, bundle.payloadDir, opts.targetRepo);
145
+ await copyManagedFiles(materialisePaths, bundle.payloadDir, opts.targetRepo);
138
146
  await removeManagedFiles(removals, opts.targetRepo);
147
+ // ADR 0176 §4.2: cache non-forward-base bundles (the resolution
148
+ // substrate for scaffold --archetype). Forward bases are not cached
149
+ // — they carry no archetype registries and are large.
150
+ if (!bundle.manifest.isForwardBase) {
151
+ await cacheBundle({
152
+ targetRepo: opts.targetRepo,
153
+ bundleName: bundle.manifest.bundleName,
154
+ bundleVersion: bundle.manifest.bundleVersion,
155
+ managedPaths: bundle.managedPaths,
156
+ payloadDir: bundle.payloadDir,
157
+ });
158
+ const ignored = await ensureCacheGitignored(opts.targetRepo);
159
+ stdout(info(`Bundle cached: .sdd/bundle-cache/${bundle.manifest.bundleName}@${bundle.manifest.bundleVersion}${ignored ? " (.gitignore updated)" : ""}`));
160
+ }
139
161
  const bundleSha = await sha256File(opts.bundle);
140
162
  const newEntry = {
141
163
  name: incomingBundleName,