domain-knowledge-kit 0.2.14 → 0.2.16
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/.github/skills/{flow-implementer → dkk-flow-implementer}/skill.md +17 -8
- package/.github/skills/{story-analyst → dkk-story-analyst}/skill.md +23 -13
- package/README.md +5 -0
- package/dist/cli.js +20 -0
- package/dist/cli.js.map +1 -1
- package/dist/features/agent/commands/init.d.ts.map +1 -1
- package/dist/features/agent/commands/init.js +141 -3
- package/dist/features/agent/commands/init.js.map +1 -1
- package/dist/features/agent/commands/prime.d.ts +11 -0
- package/dist/features/agent/commands/prime.d.ts.map +1 -1
- package/dist/features/agent/commands/prime.js +122 -10
- package/dist/features/agent/commands/prime.js.map +1 -1
- package/dist/features/federation/commands/consumers.d.ts +40 -0
- package/dist/features/federation/commands/consumers.d.ts.map +1 -0
- package/dist/features/federation/commands/consumers.js +126 -0
- package/dist/features/federation/commands/consumers.js.map +1 -0
- package/dist/features/federation/commands/peers-add.d.ts +14 -0
- package/dist/features/federation/commands/peers-add.d.ts.map +1 -0
- package/dist/features/federation/commands/peers-add.js +79 -0
- package/dist/features/federation/commands/peers-add.js.map +1 -0
- package/dist/features/federation/commands/peers-list.d.ts +8 -0
- package/dist/features/federation/commands/peers-list.d.ts.map +1 -0
- package/dist/features/federation/commands/peers-list.js +51 -0
- package/dist/features/federation/commands/peers-list.js.map +1 -0
- package/dist/features/federation/commands/peers-status.d.ts +8 -0
- package/dist/features/federation/commands/peers-status.d.ts.map +1 -0
- package/dist/features/federation/commands/peers-status.js +78 -0
- package/dist/features/federation/commands/peers-status.js.map +1 -0
- package/dist/features/federation/commands/pull.d.ts +18 -0
- package/dist/features/federation/commands/pull.d.ts.map +1 -0
- package/dist/features/federation/commands/pull.js +153 -0
- package/dist/features/federation/commands/pull.js.map +1 -0
- package/dist/features/federation/git-fetcher.d.ts +45 -0
- package/dist/features/federation/git-fetcher.d.ts.map +1 -0
- package/dist/features/federation/git-fetcher.js +70 -0
- package/dist/features/federation/git-fetcher.js.map +1 -0
- package/dist/features/federation/loader.d.ts +60 -0
- package/dist/features/federation/loader.d.ts.map +1 -0
- package/dist/features/federation/loader.js +193 -0
- package/dist/features/federation/loader.js.map +1 -0
- package/dist/features/federation/lock.d.ts +12 -0
- package/dist/features/federation/lock.d.ts.map +1 -0
- package/dist/features/federation/lock.js +48 -0
- package/dist/features/federation/lock.js.map +1 -0
- package/dist/features/federation/tests/git-fetcher.test.d.ts +2 -0
- package/dist/features/federation/tests/git-fetcher.test.d.ts.map +1 -0
- package/dist/features/federation/tests/git-fetcher.test.js +167 -0
- package/dist/features/federation/tests/git-fetcher.test.js.map +1 -0
- package/dist/features/federation/tests/loader.test.d.ts +2 -0
- package/dist/features/federation/tests/loader.test.d.ts.map +1 -0
- package/dist/features/federation/tests/loader.test.js +144 -0
- package/dist/features/federation/tests/loader.test.js.map +1 -0
- package/dist/features/federation/tests/phase5.test.d.ts +2 -0
- package/dist/features/federation/tests/phase5.test.d.ts.map +1 -0
- package/dist/features/federation/tests/phase5.test.js +137 -0
- package/dist/features/federation/tests/phase5.test.js.map +1 -0
- package/dist/features/federation/tests/schema-load.test.d.ts +2 -0
- package/dist/features/federation/tests/schema-load.test.d.ts.map +1 -0
- package/dist/features/federation/tests/schema-load.test.js +97 -0
- package/dist/features/federation/tests/schema-load.test.js.map +1 -0
- package/dist/features/federation/tests/validator.test.d.ts +2 -0
- package/dist/features/federation/tests/validator.test.d.ts.map +1 -0
- package/dist/features/federation/tests/validator.test.js +319 -0
- package/dist/features/federation/tests/validator.test.js.map +1 -0
- package/dist/features/mcp/commands/serve.d.ts +10 -0
- package/dist/features/mcp/commands/serve.d.ts.map +1 -0
- package/dist/features/mcp/commands/serve.js +12 -0
- package/dist/features/mcp/commands/serve.js.map +1 -0
- package/dist/features/mcp/server.d.ts +15 -0
- package/dist/features/mcp/server.d.ts.map +1 -0
- package/dist/features/mcp/server.js +438 -0
- package/dist/features/mcp/server.js.map +1 -0
- package/dist/features/pipeline/commands/validate.d.ts.map +1 -1
- package/dist/features/pipeline/commands/validate.js +7 -0
- package/dist/features/pipeline/commands/validate.js.map +1 -1
- package/dist/features/pipeline/indexer.d.ts +28 -2
- package/dist/features/pipeline/indexer.d.ts.map +1 -1
- package/dist/features/pipeline/indexer.js +84 -29
- package/dist/features/pipeline/indexer.js.map +1 -1
- package/dist/features/pipeline/validator.d.ts +10 -0
- package/dist/features/pipeline/validator.d.ts.map +1 -1
- package/dist/features/pipeline/validator.js +274 -27
- package/dist/features/pipeline/validator.js.map +1 -1
- package/dist/features/query/commands/list.d.ts +10 -0
- package/dist/features/query/commands/list.d.ts.map +1 -1
- package/dist/features/query/commands/list.js +1 -1
- package/dist/features/query/commands/list.js.map +1 -1
- package/dist/features/query/commands/locate.d.ts +1 -0
- package/dist/features/query/commands/locate.d.ts.map +1 -1
- package/dist/features/query/commands/locate.js +1 -1
- package/dist/features/query/commands/locate.js.map +1 -1
- package/dist/features/query/commands/search.d.ts.map +1 -1
- package/dist/features/query/commands/search.js +2 -0
- package/dist/features/query/commands/search.js.map +1 -1
- package/dist/features/query/commands/show.d.ts +15 -0
- package/dist/features/query/commands/show.d.ts.map +1 -1
- package/dist/features/query/commands/show.js +116 -58
- package/dist/features/query/commands/show.js.map +1 -1
- package/dist/features/query/commands/story.d.ts +70 -0
- package/dist/features/query/commands/story.d.ts.map +1 -1
- package/dist/features/query/commands/story.js +3 -2
- package/dist/features/query/commands/story.js.map +1 -1
- package/dist/features/query/commands/summary.d.ts +3 -0
- package/dist/features/query/commands/summary.d.ts.map +1 -1
- package/dist/features/query/commands/summary.js +1 -1
- package/dist/features/query/commands/summary.js.map +1 -1
- package/dist/features/query/searcher.d.ts +18 -1
- package/dist/features/query/searcher.d.ts.map +1 -1
- package/dist/features/query/searcher.js +11 -2
- package/dist/features/query/searcher.js.map +1 -1
- package/dist/features/scaffold/commands/service-init.d.ts +12 -0
- package/dist/features/scaffold/commands/service-init.d.ts.map +1 -0
- package/dist/features/scaffold/commands/service-init.js +69 -0
- package/dist/features/scaffold/commands/service-init.js.map +1 -0
- package/dist/shared/graph.d.ts +8 -0
- package/dist/shared/graph.d.ts.map +1 -1
- package/dist/shared/graph.js +180 -112
- package/dist/shared/graph.js.map +1 -1
- package/dist/shared/index.d.ts +4 -1
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +6 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/loader.d.ts +22 -0
- package/dist/shared/loader.d.ts.map +1 -1
- package/dist/shared/loader.js +31 -1
- package/dist/shared/loader.js.map +1 -1
- package/dist/shared/paths.d.ts +59 -7
- package/dist/shared/paths.d.ts.map +1 -1
- package/dist/shared/paths.js +93 -11
- package/dist/shared/paths.js.map +1 -1
- package/dist/shared/refs.d.ts +96 -0
- package/dist/shared/refs.d.ts.map +1 -0
- package/dist/shared/refs.js +182 -0
- package/dist/shared/refs.js.map +1 -0
- package/dist/shared/service-id.d.ts +11 -0
- package/dist/shared/service-id.d.ts.map +1 -0
- package/dist/shared/service-id.js +64 -0
- package/dist/shared/service-id.js.map +1 -0
- package/dist/shared/tests/paths.test.d.ts +2 -0
- package/dist/shared/tests/paths.test.d.ts.map +1 -0
- package/dist/shared/tests/paths.test.js +111 -0
- package/dist/shared/tests/paths.test.js.map +1 -0
- package/dist/shared/tests/refs.test.d.ts +2 -0
- package/dist/shared/tests/refs.test.d.ts.map +1 -0
- package/dist/shared/tests/refs.test.js +104 -0
- package/dist/shared/tests/refs.test.js.map +1 -0
- package/dist/shared/types/domain.d.ts +20 -0
- package/dist/shared/types/domain.d.ts.map +1 -1
- package/dist/shared/types/federation.d.ts +60 -0
- package/dist/shared/types/federation.d.ts.map +1 -0
- package/dist/shared/types/federation.js +12 -0
- package/dist/shared/types/federation.js.map +1 -0
- package/package.json +6 -3
- package/tools/dkk/claude/agents/dkk-domain-reviewer.md +69 -0
- package/tools/dkk/claude/commands/dkk-adr.md +11 -0
- package/tools/dkk/claude/commands/dkk-impact.md +34 -0
- package/tools/dkk/claude/commands/dkk-implement.md +12 -0
- package/tools/dkk/claude/commands/dkk-prime.md +6 -0
- package/tools/dkk/claude/commands/dkk-review.md +12 -0
- package/tools/dkk/claude/commands/dkk-story.md +12 -0
- package/tools/dkk/claude/hooks/post-edit-validate.mjs +51 -0
- package/tools/dkk/claude/hooks/pre-edit-block-generated.mjs +39 -0
- package/tools/dkk/claude/hooks/session-start-prime.mjs +33 -0
- package/tools/dkk/claude/hooks/stop-validate.mjs +48 -0
- package/tools/dkk/claude/settings.json +62 -0
- package/tools/dkk/claude/skills/dkk-adr-author/SKILL.md +54 -0
- package/tools/dkk/claude/skills/dkk-flow-implementer/SKILL.md +51 -0
- package/tools/dkk/claude/skills/dkk-story-analyst/SKILL.md +108 -0
- package/tools/dkk/schema/actors.schema.json +11 -1
- package/tools/dkk/schema/adr-frontmatter.schema.json +4 -4
- package/tools/dkk/schema/aggregate.schema.json +1 -1
- package/tools/dkk/schema/command.schema.json +1 -1
- package/tools/dkk/schema/event.schema.json +1 -1
- package/tools/dkk/schema/federation.schema.json +71 -0
- package/tools/dkk/schema/glossary.schema.json +1 -1
- package/tools/dkk/schema/index.schema.json +2 -2
- package/tools/dkk/schema/policy.schema.json +1 -1
- package/tools/dkk/schema/read-model.schema.json +15 -1
- package/tools/dkk/schema/service.schema.json +30 -0
- package/.github/skills/domain-knowledge/skill.md +0 -93
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { repoRoot } from "../../../shared/paths.js";
|
|
2
|
+
import { loadFederation, resolvePeerRoot, peerEnvKey, loadPeerModel } from "../loader.js";
|
|
3
|
+
export function registerPeersStatus(parent) {
|
|
4
|
+
parent
|
|
5
|
+
.command("status")
|
|
6
|
+
.description("Show detailed status for each configured federation peer")
|
|
7
|
+
.option("-r, --root <path>", "Override repository root")
|
|
8
|
+
.option("--json", "Output as JSON")
|
|
9
|
+
.option("--minify", "Minify JSON output")
|
|
10
|
+
.action((opts) => {
|
|
11
|
+
const root = repoRoot(opts.root);
|
|
12
|
+
const manifest = loadFederation(opts.root);
|
|
13
|
+
if (!manifest || manifest.peers.length === 0) {
|
|
14
|
+
if (opts.json) {
|
|
15
|
+
console.log(JSON.stringify({ peers: [] }, null, opts.minify ? 0 : 2));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log("No peers configured.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const rows = [];
|
|
22
|
+
for (const peer of manifest.peers) {
|
|
23
|
+
const resolution = resolvePeerRoot(peer, root);
|
|
24
|
+
const envKey = peerEnvKey(peer.name);
|
|
25
|
+
const envOverride = process.env[envKey] ?? null;
|
|
26
|
+
const row = {
|
|
27
|
+
name: peer.name,
|
|
28
|
+
kind: peer.source.type,
|
|
29
|
+
reachable: resolution.reachable,
|
|
30
|
+
peerRoot: resolution.peerRoot,
|
|
31
|
+
envOverride,
|
|
32
|
+
service: null,
|
|
33
|
+
exports: [],
|
|
34
|
+
contexts: [],
|
|
35
|
+
warning: resolution.reason ?? null,
|
|
36
|
+
};
|
|
37
|
+
if (resolution.reachable && resolution.peerRoot) {
|
|
38
|
+
try {
|
|
39
|
+
const model = loadPeerModel(resolution.peerRoot);
|
|
40
|
+
if (model.service) {
|
|
41
|
+
row.service = model.service.name;
|
|
42
|
+
row.exports = model.service.exports;
|
|
43
|
+
}
|
|
44
|
+
row.contexts = Array.from(model.contexts.keys()).sort();
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
row.warning = err instanceof Error ? err.message : String(err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
rows.push(row);
|
|
51
|
+
}
|
|
52
|
+
if (opts.json) {
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
const { name, ...rest } = row;
|
|
56
|
+
out[name] = rest;
|
|
57
|
+
}
|
|
58
|
+
console.log(JSON.stringify(out, null, opts.minify ? 0 : 2));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const row of rows) {
|
|
62
|
+
const status = row.reachable ? "reachable" : "unreachable";
|
|
63
|
+
console.log(`${row.name} [${row.kind}] ${status}`);
|
|
64
|
+
if (row.peerRoot)
|
|
65
|
+
console.log(` path: ${row.peerRoot}`);
|
|
66
|
+
if (row.envOverride)
|
|
67
|
+
console.log(` env: ${row.envOverride}`);
|
|
68
|
+
if (row.service) {
|
|
69
|
+
console.log(` service: ${row.service}`);
|
|
70
|
+
console.log(` exports: ${row.exports.join(", ") || "(none)"}`);
|
|
71
|
+
console.log(` contexts: ${row.contexts.join(", ") || "(none)"}`);
|
|
72
|
+
}
|
|
73
|
+
if (row.warning)
|
|
74
|
+
console.log(` warning: ${row.warning}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=peers-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peers-status.js","sourceRoot":"","sources":["../../../../src/features/federation/commands/peers-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dkk pull [<name>]` — fetch git-source peers into `.dkk/imports/`.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour:
|
|
5
|
+
* - Local-source peers are silently skipped (always live from disk).
|
|
6
|
+
* - For each git-source peer, sparse-checkout `.dkk/` into the cache
|
|
7
|
+
* at `.dkk/imports/<service>/`, then record the resolved SHA in
|
|
8
|
+
* `.dkk/federation.lock.json`.
|
|
9
|
+
* - `--refresh` forces a re-fetch even when the cache already exists.
|
|
10
|
+
* - `--offline` skips all git calls; a warning is emitted if the
|
|
11
|
+
* cache is missing for any git peer.
|
|
12
|
+
*
|
|
13
|
+
* The command exits 0 if every git peer either fetched successfully
|
|
14
|
+
* or was already up-to-date; exits 1 if any fetch errored.
|
|
15
|
+
*/
|
|
16
|
+
import type { Command as Cmd } from "commander";
|
|
17
|
+
export declare function registerPull(program: Cmd): void;
|
|
18
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../../src/features/federation/commands/pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,WAAW,CAAC;AA2BhD,wBAAgB,YAAY,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CAoI/C"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { repoRoot, importedServiceDir, importsDir, federationLockFile } from "../../../shared/paths.js";
|
|
3
|
+
import { loadFederation } from "../loader.js";
|
|
4
|
+
import { sparseFetch } from "../git-fetcher.js";
|
|
5
|
+
import { readLock, writeLock, makeEntry } from "../lock.js";
|
|
6
|
+
export function registerPull(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("pull [name]")
|
|
9
|
+
.description("Fetch git-source federation peers into .dkk/imports/")
|
|
10
|
+
.option("-r, --root <path>", "Override repository root")
|
|
11
|
+
.option("--refresh", "Re-fetch even if the cache is already populated")
|
|
12
|
+
.option("--offline", "Use existing cache only; do not contact any remote")
|
|
13
|
+
.option("--json", "Output as JSON")
|
|
14
|
+
.option("--minify", "Minify JSON output")
|
|
15
|
+
.action((name, opts) => {
|
|
16
|
+
const root = repoRoot(opts.root);
|
|
17
|
+
const manifest = loadFederation(opts.root);
|
|
18
|
+
if (!manifest || manifest.peers.length === 0) {
|
|
19
|
+
if (opts.json) {
|
|
20
|
+
console.log(JSON.stringify({ peers: [] }, null, opts.minify ? 0 : 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log("No peers configured.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Optional filter to a single peer name.
|
|
27
|
+
const targets = name
|
|
28
|
+
? manifest.peers.filter((p) => p.name === name)
|
|
29
|
+
: manifest.peers;
|
|
30
|
+
if (name && targets.length === 0) {
|
|
31
|
+
console.error(`Error: no peer named "${name}" in federation.yml.`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Ensure the imports cache directory is gitignored. We write a
|
|
35
|
+
// self-contained `.gitignore` containing `*` inside .dkk/imports/
|
|
36
|
+
// so the cache stays local to each developer regardless of what
|
|
37
|
+
// the project's root .gitignore says. Cheap (~10 bytes) and
|
|
38
|
+
// protects against accidental commits.
|
|
39
|
+
ensureImportsGitignore(root);
|
|
40
|
+
const lock = readLock(opts.root);
|
|
41
|
+
const reports = [];
|
|
42
|
+
let anyError = false;
|
|
43
|
+
for (const peer of targets) {
|
|
44
|
+
if (peer.source.type === "local") {
|
|
45
|
+
reports.push({ name: peer.name, outcome: "skipped-local" });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Git source.
|
|
49
|
+
const cacheDir = importedServiceDir(peer.name, root);
|
|
50
|
+
const cacheExists = existsSync(cacheDir + "/.dkk");
|
|
51
|
+
if (opts.offline) {
|
|
52
|
+
if (cacheExists) {
|
|
53
|
+
reports.push({
|
|
54
|
+
name: peer.name,
|
|
55
|
+
outcome: "skipped-offline-cached",
|
|
56
|
+
sha: lock[peer.name]?.sha,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
reports.push({
|
|
61
|
+
name: peer.name,
|
|
62
|
+
outcome: "missing-cache-offline",
|
|
63
|
+
message: `cache missing and --offline set; run \`dkk pull ${peer.name}\` without --offline`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Re-fetch only when forced, cache empty, or the recorded
|
|
69
|
+
// source has drifted from the manifest (url/branch/path change).
|
|
70
|
+
const recorded = lock[peer.name];
|
|
71
|
+
const sourceChanged = !sourceMatches(recorded?.source, peer.source);
|
|
72
|
+
const needFetch = opts.refresh || !cacheExists || sourceChanged;
|
|
73
|
+
if (!needFetch) {
|
|
74
|
+
reports.push({
|
|
75
|
+
name: peer.name,
|
|
76
|
+
outcome: "cached",
|
|
77
|
+
sha: recorded?.sha,
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = sparseFetch({
|
|
83
|
+
url: peer.source.url,
|
|
84
|
+
branch: peer.source.branch,
|
|
85
|
+
subpath: peer.source.path ?? "",
|
|
86
|
+
dest: cacheDir,
|
|
87
|
+
});
|
|
88
|
+
lock[peer.name] = makeEntry(peer.source, result.sha);
|
|
89
|
+
reports.push({
|
|
90
|
+
name: peer.name,
|
|
91
|
+
outcome: "fetched",
|
|
92
|
+
sha: result.sha,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
anyError = true;
|
|
97
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
98
|
+
reports.push({
|
|
99
|
+
name: peer.name,
|
|
100
|
+
outcome: "error",
|
|
101
|
+
message: msg,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Always re-serialize the lock so order stays stable, even when
|
|
106
|
+
// nothing changed.
|
|
107
|
+
writeLock(lock, opts.root);
|
|
108
|
+
if (opts.json) {
|
|
109
|
+
console.log(JSON.stringify({ lock: federationLockFile(opts.root), peers: reports }, null, opts.minify ? 0 : 2));
|
|
110
|
+
if (anyError)
|
|
111
|
+
process.exit(1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
for (const r of reports) {
|
|
115
|
+
const tag = `[${r.outcome}]`;
|
|
116
|
+
const sha = r.sha ? ` ${r.sha.slice(0, 12)}` : "";
|
|
117
|
+
const msg = r.message ? ` ${r.message}` : "";
|
|
118
|
+
console.log(`${r.name.padEnd(20)} ${tag}${sha}${msg}`);
|
|
119
|
+
}
|
|
120
|
+
if (anyError)
|
|
121
|
+
process.exit(1);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create `.dkk/imports/.gitignore` (containing `*`) if it doesn't
|
|
126
|
+
* exist. Keeps the cache directory off git regardless of what the
|
|
127
|
+
* project's root .gitignore says.
|
|
128
|
+
*/
|
|
129
|
+
function ensureImportsGitignore(root) {
|
|
130
|
+
const dir = importsDir(root);
|
|
131
|
+
if (!existsSync(dir)) {
|
|
132
|
+
mkdirSync(dir, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
const gi = `${dir}/.gitignore`;
|
|
135
|
+
if (!existsSync(gi)) {
|
|
136
|
+
writeFileSync(gi, "# Auto-generated by `dkk pull`. Keeps the federation peer cache\n# off git regardless of the project's root .gitignore.\n*\n", "utf-8");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Compare two source specs by value to detect manifest drift. */
|
|
140
|
+
function sourceMatches(a, b) {
|
|
141
|
+
if (!a)
|
|
142
|
+
return false;
|
|
143
|
+
if (a.type !== b.type)
|
|
144
|
+
return false;
|
|
145
|
+
if (a.type === "git" && b.type === "git") {
|
|
146
|
+
return a.url === b.url && a.branch === b.branch && (a.path ?? "") === (b.path ?? "");
|
|
147
|
+
}
|
|
148
|
+
if (a.type === "local" && b.type === "local") {
|
|
149
|
+
return a.path === b.path;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../../src/features/federation/commands/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link sparseFetch}.
|
|
3
|
+
*/
|
|
4
|
+
export interface SparseFetchOptions {
|
|
5
|
+
/** Git URL (https / ssh). */
|
|
6
|
+
url: string;
|
|
7
|
+
/** Branch to track. */
|
|
8
|
+
branch: string;
|
|
9
|
+
/**
|
|
10
|
+
* Sub-path inside the peer repo where the service lives (i.e. the
|
|
11
|
+
* directory whose child is `.dkk/`). Empty string for repos that
|
|
12
|
+
* have `.dkk/` at the root.
|
|
13
|
+
*/
|
|
14
|
+
subpath: string;
|
|
15
|
+
/**
|
|
16
|
+
* Absolute destination directory. Will be wiped if it exists.
|
|
17
|
+
* After fetch, the peer's repo content lives at `<dest>` and the
|
|
18
|
+
* service's `.dkk/` lives at `<dest>/<subpath>/.dkk` (or
|
|
19
|
+
* `<dest>/.dkk` when subpath is empty).
|
|
20
|
+
*/
|
|
21
|
+
dest: string;
|
|
22
|
+
}
|
|
23
|
+
/** Result of a successful sparse fetch. */
|
|
24
|
+
export interface SparseFetchResult {
|
|
25
|
+
/** Resolved commit SHA at HEAD of the fetched branch. */
|
|
26
|
+
sha: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Clone a peer repository into `dest` using a sparse-checkout of only
|
|
30
|
+
* the `.dkk/` subtree. The clone is depth-1 + blobless to keep it fast
|
|
31
|
+
* and small. Returns the resolved commit SHA.
|
|
32
|
+
*
|
|
33
|
+
* Steps:
|
|
34
|
+
* 1. Wipe `dest` if it exists (a stale partial clone is worse than no clone).
|
|
35
|
+
* 2. `git clone --filter=blob:none --depth 1 --no-checkout --branch <b> <url> <dest>`.
|
|
36
|
+
* 3. `git sparse-checkout init --no-cone` inside `dest`.
|
|
37
|
+
* 4. `git sparse-checkout set <subpath>/.dkk` (or `.dkk` when subpath empty).
|
|
38
|
+
* 5. `git checkout <branch>` to materialise the sparse set on disk.
|
|
39
|
+
* 6. `git rev-parse HEAD` to capture the SHA.
|
|
40
|
+
*
|
|
41
|
+
* Errors from `git` propagate as thrown exceptions with their stderr
|
|
42
|
+
* attached, so the caller can surface a meaningful message to the user.
|
|
43
|
+
*/
|
|
44
|
+
export declare function sparseFetch(opts: SparseFetchOptions): SparseFetchResult;
|
|
45
|
+
//# sourceMappingURL=git-fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-fetcher.d.ts","sourceRoot":"","sources":["../../../src/features/federation/git-fetcher.ts"],"names":[],"mappings":"AAcA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;CACb;AAcD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAoCvE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sparse-checkout fetcher for git-source peers.
|
|
3
|
+
*
|
|
4
|
+
* Clones a peer repository into a local cache, pulling only the
|
|
5
|
+
* subdirectory containing `.dkk/` to keep disk cost minimal — a
|
|
6
|
+
* typical peer is well under a megabyte.
|
|
7
|
+
*
|
|
8
|
+
* The user's existing git credential helper / SSH agent is what
|
|
9
|
+
* authenticates the clone. DKK never handles tokens directly.
|
|
10
|
+
*/
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
/**
|
|
15
|
+
* Run `git` with the given args inside `cwd`. Throws on non-zero exit.
|
|
16
|
+
* Captures stdout for callers that need it (e.g. `rev-parse`).
|
|
17
|
+
*/
|
|
18
|
+
function git(args, cwd) {
|
|
19
|
+
return execFileSync("git", args, {
|
|
20
|
+
cwd,
|
|
21
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
22
|
+
encoding: "utf-8",
|
|
23
|
+
}).trim();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Clone a peer repository into `dest` using a sparse-checkout of only
|
|
27
|
+
* the `.dkk/` subtree. The clone is depth-1 + blobless to keep it fast
|
|
28
|
+
* and small. Returns the resolved commit SHA.
|
|
29
|
+
*
|
|
30
|
+
* Steps:
|
|
31
|
+
* 1. Wipe `dest` if it exists (a stale partial clone is worse than no clone).
|
|
32
|
+
* 2. `git clone --filter=blob:none --depth 1 --no-checkout --branch <b> <url> <dest>`.
|
|
33
|
+
* 3. `git sparse-checkout init --no-cone` inside `dest`.
|
|
34
|
+
* 4. `git sparse-checkout set <subpath>/.dkk` (or `.dkk` when subpath empty).
|
|
35
|
+
* 5. `git checkout <branch>` to materialise the sparse set on disk.
|
|
36
|
+
* 6. `git rev-parse HEAD` to capture the SHA.
|
|
37
|
+
*
|
|
38
|
+
* Errors from `git` propagate as thrown exceptions with their stderr
|
|
39
|
+
* attached, so the caller can surface a meaningful message to the user.
|
|
40
|
+
*/
|
|
41
|
+
export function sparseFetch(opts) {
|
|
42
|
+
if (existsSync(opts.dest)) {
|
|
43
|
+
rmSync(opts.dest, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
mkdirSync(dirname(opts.dest), { recursive: true });
|
|
46
|
+
// Step 2: blobless, depth-1, no checkout yet.
|
|
47
|
+
git([
|
|
48
|
+
"clone",
|
|
49
|
+
"--filter=blob:none",
|
|
50
|
+
"--depth",
|
|
51
|
+
"1",
|
|
52
|
+
"--no-checkout",
|
|
53
|
+
"--branch",
|
|
54
|
+
opts.branch,
|
|
55
|
+
opts.url,
|
|
56
|
+
opts.dest,
|
|
57
|
+
], process.cwd());
|
|
58
|
+
// Steps 3-4: enable sparse-checkout, restrict to the .dkk/ subtree.
|
|
59
|
+
git(["sparse-checkout", "init", "--no-cone"], opts.dest);
|
|
60
|
+
const sparsePattern = opts.subpath
|
|
61
|
+
? `${opts.subpath.replace(/\/$/, "")}/.dkk/*`
|
|
62
|
+
: ".dkk/*";
|
|
63
|
+
git(["sparse-checkout", "set", sparsePattern], opts.dest);
|
|
64
|
+
// Step 5: materialise.
|
|
65
|
+
git(["checkout", opts.branch], opts.dest);
|
|
66
|
+
// Step 6: capture HEAD SHA.
|
|
67
|
+
const sha = git(["rev-parse", "HEAD"], opts.dest);
|
|
68
|
+
return { sha };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=git-fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-fetcher.js","sourceRoot":"","sources":["../../../src/features/federation/git-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC;;;GAGG;AACH,SAAS,GAAG,CAAC,IAAc,EAAE,GAAW;IACtC,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/B,GAAG;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,QAAQ,EAAE,OAAO;KAClB,CAAC,CAAC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CAAC,IAAwB;IAClD,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEnD,8CAA8C;IAC9C,GAAG,CACD;QACE,OAAO;QACP,oBAAoB;QACpB,SAAS;QACT,GAAG;QACH,eAAe;QACf,UAAU;QACV,IAAI,CAAC,MAAM;QACX,IAAI,CAAC,GAAG;QACR,IAAI,CAAC,IAAI;KACV,EACD,OAAO,CAAC,GAAG,EAAE,CACd,CAAC;IAEF,oEAAoE;IACpE,GAAG,CAAC,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO;QAChC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS;QAC7C,CAAC,CAAC,QAAQ,CAAC;IACb,GAAG,CAAC,CAAC,iBAAiB,EAAE,KAAK,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1D,uBAAuB;IACvB,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1C,4BAA4B;IAC5B,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAElD,OAAO,EAAE,GAAG,EAAE,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DomainModel } from "../../shared/types/domain.js";
|
|
2
|
+
import type { FederationManifest, PeerSpec } from "../../shared/types/federation.js";
|
|
3
|
+
/** Per-peer resolution outcome surfaced to the loader/caller. */
|
|
4
|
+
export interface PeerResolution {
|
|
5
|
+
/** The peer's repo root on disk (where its `.dkk/` lives), if reachable. */
|
|
6
|
+
peerRoot: string | null;
|
|
7
|
+
/** True when the peer's `.dkk/` directory exists on disk. */
|
|
8
|
+
reachable: boolean;
|
|
9
|
+
/** A short reason string used for warnings (e.g. "git cache empty"). */
|
|
10
|
+
reason?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Read `.dkk/federation.yml` (if present), validate it against
|
|
14
|
+
* `federation.schema.json`, and return the parsed manifest. Returns
|
|
15
|
+
* `null` for unfederated repos.
|
|
16
|
+
*
|
|
17
|
+
* Throws when the file exists but does not conform to the schema —
|
|
18
|
+
* the message includes Ajv's error list so the user can fix the
|
|
19
|
+
* manifest directly.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadFederation(root?: string): FederationManifest | null;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a single peer spec into an absolute repo-root path on disk.
|
|
24
|
+
*
|
|
25
|
+
* - `local` sources resolve relative to the local repo root (so
|
|
26
|
+
* `../order-svc` in `billing-svc/.dkk/federation.yml` points at the
|
|
27
|
+
* sibling directory regardless of `cwd`). Env-var override
|
|
28
|
+
* `DKK_PEER_<SERVICE_NAME_UPPER>` (uppercase, hyphens → underscores)
|
|
29
|
+
* takes precedence over the manifest's `source.path`.
|
|
30
|
+
* - `git` sources resolve to the cache directory
|
|
31
|
+
* `.dkk/imports/<service>/` populated by `dkk pull`.
|
|
32
|
+
*
|
|
33
|
+
* The returned `peerRoot` points at the peer's repository root (so
|
|
34
|
+
* `<peerRoot>/.dkk/` is where the peer's domain lives).
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolvePeerRoot(spec: PeerSpec, localRepoRoot: string): PeerResolution;
|
|
37
|
+
/**
|
|
38
|
+
* Load a peer's domain model in "peer mode": one level deep (peer's
|
|
39
|
+
* own `federation.yml` is skipped) and resilient to minor schema
|
|
40
|
+
* drift. The peer's model is structurally identical to a local model
|
|
41
|
+
* so the same `loadDomainModel` is reused; the federation pass is
|
|
42
|
+
* suppressed via the `followPeers: false` option.
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadPeerModel(peerRoot: string): DomainModel;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve and load every peer declared in the manifest. Unreachable
|
|
47
|
+
* peers are reported via `warnings` (each line is one peer) but never
|
|
48
|
+
* abort the load — the caller (typically the main loader) attaches
|
|
49
|
+
* the resulting map to `model.peers`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function loadAllPeers(localRepoRoot: string, manifest: FederationManifest): {
|
|
52
|
+
peers: Map<string, DomainModel>;
|
|
53
|
+
warnings: string[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Build the env-var key used to override a peer's source path.
|
|
57
|
+
* Exposed for tests and for the `peers status` command.
|
|
58
|
+
*/
|
|
59
|
+
export declare function peerEnvKey(serviceName: string): string;
|
|
60
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/features/federation/loader.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EACV,kBAAkB,EAClB,QAAQ,EACT,MAAM,kCAAkC,CAAC;AAuB1C,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAkBvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,cAAc,CAoDrF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,kBAAkB,GAC3B;IAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAoBzD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation loader — resolves peer services declared in
|
|
3
|
+
* `.dkk/federation.yml` and loads each peer's `.dkk/` as a read-only
|
|
4
|
+
* sub-model attached at `model.peers.get(serviceName)`.
|
|
5
|
+
*
|
|
6
|
+
* Peer loading is exactly one level deep: a peer's own `federation.yml`
|
|
7
|
+
* is intentionally NOT followed. Peers are loaded in lenient mode so
|
|
8
|
+
* that DKK schema drift across services (peer YAML using fields the
|
|
9
|
+
* local DKK version doesn't understand) degrades to a warning rather
|
|
10
|
+
* than failing the consumer's load.
|
|
11
|
+
*
|
|
12
|
+
* Two source types are supported:
|
|
13
|
+
* - `local`: a filesystem path (absolute or relative to repo root).
|
|
14
|
+
* - `git`: resolved against the cache at `.dkk/imports/<service>/`
|
|
15
|
+
* populated by `dkk pull` (Phase 3).
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
18
|
+
import { isAbsolute, resolve, join } from "node:path";
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
import { parseYaml } from "../../shared/yaml.js";
|
|
21
|
+
import { federationFile, importedServiceDir, schemaDir, repoRoot } from "../../shared/paths.js";
|
|
22
|
+
import { loadDomainModel, setFederationHook } from "../../shared/loader.js";
|
|
23
|
+
// ajv is a CJS package — use createRequire for ESM interop.
|
|
24
|
+
const require = createRequire(import.meta.url);
|
|
25
|
+
const Ajv = require("ajv").default;
|
|
26
|
+
const addFormats = require("ajv-formats").default;
|
|
27
|
+
/** Cached Ajv instance for federation.yml validation. */
|
|
28
|
+
let cachedAjv = null;
|
|
29
|
+
function getAjv() {
|
|
30
|
+
if (cachedAjv)
|
|
31
|
+
return cachedAjv;
|
|
32
|
+
const ajv = new Ajv({ allErrors: true, strict: true });
|
|
33
|
+
addFormats(ajv);
|
|
34
|
+
const dir = schemaDir();
|
|
35
|
+
for (const f of readdirSync(dir).filter((n) => n.endsWith(".schema.json"))) {
|
|
36
|
+
const schema = JSON.parse(readFileSync(join(dir, f), "utf-8"));
|
|
37
|
+
ajv.addSchema(schema, schema.$id);
|
|
38
|
+
}
|
|
39
|
+
cachedAjv = ajv;
|
|
40
|
+
return ajv;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read `.dkk/federation.yml` (if present), validate it against
|
|
44
|
+
* `federation.schema.json`, and return the parsed manifest. Returns
|
|
45
|
+
* `null` for unfederated repos.
|
|
46
|
+
*
|
|
47
|
+
* Throws when the file exists but does not conform to the schema —
|
|
48
|
+
* the message includes Ajv's error list so the user can fix the
|
|
49
|
+
* manifest directly.
|
|
50
|
+
*/
|
|
51
|
+
export function loadFederation(root) {
|
|
52
|
+
const path = federationFile(root);
|
|
53
|
+
if (!existsSync(path))
|
|
54
|
+
return null;
|
|
55
|
+
const text = readFileSync(path, "utf-8");
|
|
56
|
+
const parsed = parseYaml(text);
|
|
57
|
+
const ajv = getAjv();
|
|
58
|
+
const valid = ajv.validate("federation.schema.json", parsed);
|
|
59
|
+
if (!valid) {
|
|
60
|
+
const details = (ajv.errors ?? [])
|
|
61
|
+
.map((e) => ` - ${e.instancePath || "/"}: ${e.message ?? "invalid"}`)
|
|
62
|
+
.join("\n");
|
|
63
|
+
throw new Error(`Invalid ${path}:\n${details}\n\nExpected shape: { peers: [{ name, source: { type: "local" | "git", ... } }] }`);
|
|
64
|
+
}
|
|
65
|
+
return parsed;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve a single peer spec into an absolute repo-root path on disk.
|
|
69
|
+
*
|
|
70
|
+
* - `local` sources resolve relative to the local repo root (so
|
|
71
|
+
* `../order-svc` in `billing-svc/.dkk/federation.yml` points at the
|
|
72
|
+
* sibling directory regardless of `cwd`). Env-var override
|
|
73
|
+
* `DKK_PEER_<SERVICE_NAME_UPPER>` (uppercase, hyphens → underscores)
|
|
74
|
+
* takes precedence over the manifest's `source.path`.
|
|
75
|
+
* - `git` sources resolve to the cache directory
|
|
76
|
+
* `.dkk/imports/<service>/` populated by `dkk pull`.
|
|
77
|
+
*
|
|
78
|
+
* The returned `peerRoot` points at the peer's repository root (so
|
|
79
|
+
* `<peerRoot>/.dkk/` is where the peer's domain lives).
|
|
80
|
+
*/
|
|
81
|
+
export function resolvePeerRoot(spec, localRepoRoot) {
|
|
82
|
+
const source = spec.source;
|
|
83
|
+
// Env-var override: applies to any source type for the convenience of
|
|
84
|
+
// developers who want to point at a local checkout regardless of
|
|
85
|
+
// what the committed manifest says.
|
|
86
|
+
const envKey = `DKK_PEER_${spec.name.toUpperCase().replace(/-/g, "_")}`;
|
|
87
|
+
const envOverride = process.env[envKey];
|
|
88
|
+
if (envOverride && envOverride.length > 0) {
|
|
89
|
+
const peerRoot = isAbsolute(envOverride)
|
|
90
|
+
? envOverride
|
|
91
|
+
: resolve(localRepoRoot, envOverride);
|
|
92
|
+
const reachable = existsSync(peerRoot + "/.dkk");
|
|
93
|
+
return {
|
|
94
|
+
peerRoot,
|
|
95
|
+
reachable,
|
|
96
|
+
reason: reachable ? undefined : `env override ${envKey} points at ${peerRoot} but it has no .dkk/`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (source.type === "local") {
|
|
100
|
+
const peerRoot = isAbsolute(source.path)
|
|
101
|
+
? source.path
|
|
102
|
+
: resolve(localRepoRoot, source.path);
|
|
103
|
+
const reachable = existsSync(peerRoot + "/.dkk");
|
|
104
|
+
return {
|
|
105
|
+
peerRoot,
|
|
106
|
+
reachable,
|
|
107
|
+
reason: reachable ? undefined : `local path ${peerRoot} has no .dkk/`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (source.type === "git") {
|
|
111
|
+
const cacheRoot = importedServiceDir(spec.name, localRepoRoot);
|
|
112
|
+
// When the peer's `.dkk/` lives in a sub-directory of its repo
|
|
113
|
+
// (monorepo case), the manifest's `source.path` names that
|
|
114
|
+
// sub-directory; the sparse-checkout pulls it into the cache at
|
|
115
|
+
// the same relative location.
|
|
116
|
+
const peerRoot = source.path
|
|
117
|
+
? `${cacheRoot}/${source.path.replace(/\/$/, "")}`
|
|
118
|
+
: cacheRoot;
|
|
119
|
+
const reachable = existsSync(peerRoot + "/.dkk");
|
|
120
|
+
return {
|
|
121
|
+
peerRoot,
|
|
122
|
+
reachable,
|
|
123
|
+
reason: reachable ? undefined : `git cache empty for "${spec.name}" — run \`dkk pull ${spec.name}\``,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Exhaustiveness check — future source types should be added here.
|
|
127
|
+
const exhaustive = source;
|
|
128
|
+
return { peerRoot: null, reachable: false, reason: `unknown source type: ${JSON.stringify(exhaustive)}` };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Load a peer's domain model in "peer mode": one level deep (peer's
|
|
132
|
+
* own `federation.yml` is skipped) and resilient to minor schema
|
|
133
|
+
* drift. The peer's model is structurally identical to a local model
|
|
134
|
+
* so the same `loadDomainModel` is reused; the federation pass is
|
|
135
|
+
* suppressed via the `followPeers: false` option.
|
|
136
|
+
*/
|
|
137
|
+
export function loadPeerModel(peerRoot) {
|
|
138
|
+
return loadDomainModel({ root: peerRoot, followPeers: false });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Resolve and load every peer declared in the manifest. Unreachable
|
|
142
|
+
* peers are reported via `warnings` (each line is one peer) but never
|
|
143
|
+
* abort the load — the caller (typically the main loader) attaches
|
|
144
|
+
* the resulting map to `model.peers`.
|
|
145
|
+
*/
|
|
146
|
+
export function loadAllPeers(localRepoRoot, manifest) {
|
|
147
|
+
const peers = new Map();
|
|
148
|
+
const warnings = [];
|
|
149
|
+
for (const spec of manifest.peers ?? []) {
|
|
150
|
+
const resolution = resolvePeerRoot(spec, localRepoRoot);
|
|
151
|
+
if (!resolution.reachable || !resolution.peerRoot) {
|
|
152
|
+
warnings.push(`peer "${spec.name}" unreachable: ${resolution.reason ?? "unknown"}`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const model = loadPeerModel(resolution.peerRoot);
|
|
157
|
+
peers.set(spec.name, model);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
warnings.push(`peer "${spec.name}" failed to load: ${msg}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { peers, warnings };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Build the env-var key used to override a peer's source path.
|
|
168
|
+
* Exposed for tests and for the `peers status` command.
|
|
169
|
+
*/
|
|
170
|
+
export function peerEnvKey(serviceName) {
|
|
171
|
+
return `DKK_PEER_${serviceName.toUpperCase().replace(/-/g, "_")}`;
|
|
172
|
+
}
|
|
173
|
+
// ── Hook registration ────────────────────────────────────────────────
|
|
174
|
+
//
|
|
175
|
+
// Register the peer-hydration hook with the shared loader at module
|
|
176
|
+
// initialisation. Any CLI command that imports this slice (directly or
|
|
177
|
+
// transitively via the federation commands wired in cli.ts) will cause
|
|
178
|
+
// `loadDomainModel` to start populating `model.peers`. Scripts that
|
|
179
|
+
// import the shared loader without the federation slice get plain
|
|
180
|
+
// unfederated behaviour — no surprises, no cycles.
|
|
181
|
+
setFederationHook((root, model) => {
|
|
182
|
+
const manifest = loadFederation(root);
|
|
183
|
+
if (!manifest)
|
|
184
|
+
return;
|
|
185
|
+
const resolvedRoot = repoRoot(root);
|
|
186
|
+
const { peers, warnings } = loadAllPeers(resolvedRoot, manifest);
|
|
187
|
+
if (peers.size > 0)
|
|
188
|
+
model.peers = peers;
|
|
189
|
+
for (const w of warnings) {
|
|
190
|
+
console.warn(`dkk: ${w}`);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
//# sourceMappingURL=loader.js.map
|