@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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bundle.d.ts +6 -0
- package/dist/lib/bundle.d.ts.map +1 -1
- package/dist/lib/bundle.js +5 -0
- package/dist/lib/bundle.js.map +1 -1
- package/dist/lib/pack/builder.d.ts.map +1 -1
- package/dist/lib/pack/builder.js +12 -4
- package/dist/lib/pack/builder.js.map +1 -1
- package/dist/lib/pack/index.d.ts.map +1 -1
- package/dist/lib/pack/index.js +9 -2
- package/dist/lib/pack/index.js.map +1 -1
- package/dist/lib/pack/leakage.d.ts.map +1 -1
- package/dist/lib/pack/leakage.js +21 -10
- package/dist/lib/pack/leakage.js.map +1 -1
- package/dist/lib/pack/schema.d.ts +3 -2
- package/dist/lib/pack/schema.d.ts.map +1 -1
- package/dist/lib/pack/schema.js +23 -3
- package/dist/lib/pack/schema.js.map +1 -1
- package/dist/lib/pack/seed.d.ts +14 -1
- package/dist/lib/pack/seed.d.ts.map +1 -1
- package/dist/lib/pack/seed.js +14 -7
- package/dist/lib/pack/seed.js.map +1 -1
- package/dist/lib/scaffold/domain.d.ts +30 -0
- package/dist/lib/scaffold/domain.d.ts.map +1 -0
- package/dist/lib/scaffold/domain.js +234 -0
- package/dist/lib/scaffold/domain.js.map +1 -0
- package/dist/lib/scaffold/registry.d.ts +43 -0
- package/dist/lib/scaffold/registry.d.ts.map +1 -0
- package/dist/lib/scaffold/registry.js +104 -0
- package/dist/lib/scaffold/registry.js.map +1 -0
- package/dist/lib/sync/bundle-cache.d.ts +45 -0
- package/dist/lib/sync/bundle-cache.d.ts.map +1 -0
- package/dist/lib/sync/bundle-cache.js +92 -0
- package/dist/lib/sync/bundle-cache.js.map +1 -0
- package/dist/lib/sync/index.d.ts.map +1 -1
- package/dist/lib/sync/index.js +25 -3
- package/dist/lib/sync/index.js.map +1 -1
- package/dist/verbs/scaffold.d.ts +3 -0
- package/dist/verbs/scaffold.d.ts.map +1 -0
- package/dist/verbs/scaffold.js +82 -0
- package/dist/verbs/scaffold.js.map +1 -0
- 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;
|
|
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"}
|
package/dist/lib/sync/index.js
CHANGED
|
@@ -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(
|
|
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,
|
|
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(
|
|
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,
|