domain-knowledge-kit 0.2.15 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/cli.js +24 -1
- package/dist/cli.js.map +1 -1
- package/dist/features/agent/commands/init.d.ts +90 -1
- package/dist/features/agent/commands/init.d.ts.map +1 -1
- package/dist/features/agent/commands/init.js +328 -32
- 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 +105 -8
- package/dist/features/agent/commands/prime.js.map +1 -1
- package/dist/features/agent/commands/update.d.ts +27 -0
- package/dist/features/agent/commands/update.d.ts.map +1 -0
- package/dist/features/agent/commands/update.js +316 -0
- package/dist/features/agent/commands/update.js.map +1 -0
- package/dist/features/agent/dkk-artifacts.d.ts +76 -0
- package/dist/features/agent/dkk-artifacts.d.ts.map +1 -0
- package/dist/features/agent/dkk-artifacts.js +328 -0
- package/dist/features/agent/dkk-artifacts.js.map +1 -0
- package/dist/features/agent/install-mode.d.ts +34 -0
- package/dist/features/agent/install-mode.d.ts.map +1 -0
- package/dist/features/agent/install-mode.js +78 -0
- package/dist/features/agent/install-mode.js.map +1 -0
- package/dist/features/agent/mcp-register.d.ts +20 -0
- package/dist/features/agent/mcp-register.d.ts.map +1 -0
- package/dist/features/agent/mcp-register.js +116 -0
- package/dist/features/agent/mcp-register.js.map +1 -0
- package/dist/features/agent/settings-prune.d.ts +29 -0
- package/dist/features/agent/settings-prune.d.ts.map +1 -0
- package/dist/features/agent/settings-prune.js +70 -0
- package/dist/features/agent/settings-prune.js.map +1 -0
- package/dist/features/agent/tests/settings-prune.test.d.ts +2 -0
- package/dist/features/agent/tests/settings-prune.test.d.ts.map +1 -0
- package/dist/features/agent/tests/settings-prune.test.js +118 -0
- package/dist/features/agent/tests/settings-prune.test.js.map +1 -0
- 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 +82 -27
- 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 +2 -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/new-domain.d.ts +22 -0
- package/dist/features/scaffold/commands/new-domain.d.ts.map +1 -1
- package/dist/features/scaffold/commands/new-domain.js +44 -28
- package/dist/features/scaffold/commands/new-domain.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 +14 -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/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +15 -0
- package/dist/version.js.map +1 -0
- package/package.json +8 -5
- 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 +68 -0
- package/tools/dkk/claude/hooks/pre-edit-block-generated.mjs +39 -0
- package/tools/dkk/claude/hooks/session-start-prime.mjs +20 -0
- package/tools/dkk/claude/hooks/stop-validate.mjs +67 -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 +1 -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 +1 -1
- package/tools/dkk/schema/service.schema.json +30 -0
|
@@ -8,7 +8,29 @@
|
|
|
8
8
|
* context.yml, events/, commands/, aggregates/, policies/, read-models/
|
|
9
9
|
*
|
|
10
10
|
* Errors if `.dkk/domain/` already exists (use `--force` to overwrite).
|
|
11
|
+
*
|
|
12
|
+
* The scaffold logic is also exported as `scaffoldDomain()` so `dkk init`
|
|
13
|
+
* can bootstrap the same structure during project initialization.
|
|
11
14
|
*/
|
|
12
15
|
import type { Command as Cmd } from "commander";
|
|
16
|
+
export type ScaffoldDomainStatus = "created" | "skipped" | "replaced";
|
|
17
|
+
export interface ScaffoldDomainResult {
|
|
18
|
+
status: ScaffoldDomainStatus;
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Scaffold `.dkk/domain/` with sample content.
|
|
23
|
+
*
|
|
24
|
+
* - If the directory does not exist, creates it (`status: "created"`).
|
|
25
|
+
* - If it exists and `force` is false, leaves it untouched (`status: "skipped"`).
|
|
26
|
+
* - If it exists and `force` is true, removes it entirely first, then
|
|
27
|
+
* creates a fresh scaffold (`status: "replaced"`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function scaffoldDomain(opts?: {
|
|
30
|
+
root?: string;
|
|
31
|
+
force?: boolean;
|
|
32
|
+
}): ScaffoldDomainResult;
|
|
33
|
+
/** Files created by a successful scaffold — used by callers that print their own report. */
|
|
34
|
+
export declare const SCAFFOLD_DOMAIN_FILES: readonly string[];
|
|
13
35
|
export declare function registerNewDomain(program: Cmd): void;
|
|
14
36
|
//# sourceMappingURL=new-domain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-domain.d.ts","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/new-domain.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"new-domain.d.ts","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/new-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,WAAW,CAAC;AA0DhD,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,oBAAoB,CA0BlG;AAED,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAOlD,CAAC;AAIF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CAmCpD"}
|
|
@@ -45,6 +45,45 @@ emits:
|
|
|
45
45
|
events:
|
|
46
46
|
- SampleCreated
|
|
47
47
|
`;
|
|
48
|
+
/**
|
|
49
|
+
* Scaffold `.dkk/domain/` with sample content.
|
|
50
|
+
*
|
|
51
|
+
* - If the directory does not exist, creates it (`status: "created"`).
|
|
52
|
+
* - If it exists and `force` is false, leaves it untouched (`status: "skipped"`).
|
|
53
|
+
* - If it exists and `force` is true, removes it entirely first, then
|
|
54
|
+
* creates a fresh scaffold (`status: "replaced"`).
|
|
55
|
+
*/
|
|
56
|
+
export function scaffoldDomain(opts = {}) {
|
|
57
|
+
const dir = domainDir(opts.root);
|
|
58
|
+
if (existsSync(dir) && !opts.force) {
|
|
59
|
+
return { status: "skipped", path: dir };
|
|
60
|
+
}
|
|
61
|
+
const replaced = existsSync(dir) && (opts.force ?? false);
|
|
62
|
+
if (replaced) {
|
|
63
|
+
rmSync(dir, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
const contextsBase = join(dir, "contexts", "sample");
|
|
66
|
+
const subDirs = ["events", "commands", "aggregates", "policies", "read-models"];
|
|
67
|
+
for (const sub of subDirs) {
|
|
68
|
+
mkdirSync(join(contextsBase, sub), { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
writeFileSync(join(dir, "index.yml"), INDEX_YML, "utf-8");
|
|
71
|
+
writeFileSync(join(dir, "actors.yml"), ACTORS_YML, "utf-8");
|
|
72
|
+
writeFileSync(join(contextsBase, "context.yml"), CONTEXT_YML, "utf-8");
|
|
73
|
+
writeFileSync(join(contextsBase, "events", "SampleCreated.yml"), SAMPLE_EVENT, "utf-8");
|
|
74
|
+
writeFileSync(join(contextsBase, "commands", "CreateSample.yml"), SAMPLE_COMMAND, "utf-8");
|
|
75
|
+
writeFileSync(join(contextsBase, "aggregates", "Sample.yml"), SAMPLE_AGGREGATE, "utf-8");
|
|
76
|
+
return { status: replaced ? "replaced" : "created", path: dir };
|
|
77
|
+
}
|
|
78
|
+
/** Files created by a successful scaffold — used by callers that print their own report. */
|
|
79
|
+
export const SCAFFOLD_DOMAIN_FILES = [
|
|
80
|
+
"index.yml",
|
|
81
|
+
"actors.yml",
|
|
82
|
+
"contexts/sample/context.yml",
|
|
83
|
+
"contexts/sample/events/SampleCreated.yml",
|
|
84
|
+
"contexts/sample/commands/CreateSample.yml",
|
|
85
|
+
"contexts/sample/aggregates/Sample.yml",
|
|
86
|
+
];
|
|
48
87
|
// ── Registration ──────────────────────────────────────────────────────
|
|
49
88
|
export function registerNewDomain(program) {
|
|
50
89
|
program
|
|
@@ -56,44 +95,21 @@ export function registerNewDomain(program) {
|
|
|
56
95
|
.option("--minify", "Minify JSON")
|
|
57
96
|
.action((opts) => {
|
|
58
97
|
const dir = domainDir(opts.root);
|
|
59
|
-
// Guard: refuse to overwrite unless --force
|
|
60
98
|
if (existsSync(dir) && !opts.force) {
|
|
61
99
|
console.error(`Error: ${dir} already exists. Use --force to replace it entirely.`);
|
|
62
100
|
process.exit(1);
|
|
63
101
|
}
|
|
64
|
-
|
|
65
|
-
// to a clean slate (no silent merging of pre-existing content).
|
|
66
|
-
if (existsSync(dir) && opts.force) {
|
|
67
|
-
rmSync(dir, { recursive: true, force: true });
|
|
68
|
-
}
|
|
69
|
-
// Create directory structure
|
|
70
|
-
const contextsBase = join(dir, "contexts", "sample");
|
|
71
|
-
const subDirs = ["events", "commands", "aggregates", "policies", "read-models"];
|
|
72
|
-
for (const sub of subDirs) {
|
|
73
|
-
mkdirSync(join(contextsBase, sub), { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
// Write files
|
|
76
|
-
writeFileSync(join(dir, "index.yml"), INDEX_YML, "utf-8");
|
|
77
|
-
writeFileSync(join(dir, "actors.yml"), ACTORS_YML, "utf-8");
|
|
78
|
-
writeFileSync(join(contextsBase, "context.yml"), CONTEXT_YML, "utf-8");
|
|
79
|
-
writeFileSync(join(contextsBase, "events", "SampleCreated.yml"), SAMPLE_EVENT, "utf-8");
|
|
80
|
-
writeFileSync(join(contextsBase, "commands", "CreateSample.yml"), SAMPLE_COMMAND, "utf-8");
|
|
81
|
-
writeFileSync(join(contextsBase, "aggregates", "Sample.yml"), SAMPLE_AGGREGATE, "utf-8");
|
|
102
|
+
const result = scaffoldDomain({ root: opts.root, force: opts.force });
|
|
82
103
|
if (opts.json) {
|
|
83
104
|
console.log(JSON.stringify({
|
|
84
|
-
path:
|
|
85
|
-
success: true
|
|
105
|
+
path: result.path,
|
|
106
|
+
success: true,
|
|
86
107
|
}, null, opts.minify ? 0 : 2));
|
|
87
108
|
return;
|
|
88
109
|
}
|
|
89
110
|
console.log("Created .dkk/domain/ with sample content:");
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(" contexts/sample/");
|
|
93
|
-
console.log(" context.yml");
|
|
94
|
-
console.log(" events/SampleCreated.yml");
|
|
95
|
-
console.log(" commands/CreateSample.yml");
|
|
96
|
-
console.log(" aggregates/Sample.yml");
|
|
111
|
+
for (const f of SCAFFOLD_DOMAIN_FILES)
|
|
112
|
+
console.log(` ${f}`);
|
|
97
113
|
console.log("\nRun `dkk render` to validate and generate documentation.");
|
|
98
114
|
});
|
|
99
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-domain.js","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/new-domain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"new-domain.js","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/new-domain.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,yEAAyE;AAEzE,MAAM,SAAS,GAAG;;;;;CAKjB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;CAKlB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;CAMnB,CAAC;AAEF,MAAM,YAAY,GAAG;;;;;;;CAOpB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;CAKtB,CAAC;AAEF,MAAM,gBAAgB,GAAG;;;;;;;;;CASxB,CAAC;AAWF;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,OAA2C,EAAE;IAC1E,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEjC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACnC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC;IAC1D,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IAChF,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1D,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC5D,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IACvE,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,mBAAmB,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACxF,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,kBAAkB,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAC3F,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAEzF,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AAClE,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,WAAW;IACX,YAAY;IACZ,6BAA6B;IAC7B,0CAA0C;IAC1C,2CAA2C;IAC3C,uCAAuC;CACxC,CAAC;AAEF,yEAAyE;AAEzE,MAAM,UAAU,iBAAiB,CAAC,OAAY;IAC5C,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gEAAgE,CAAC;SAC7E,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,CAAC;SACvD,MAAM,CACL,SAAS,EACT,sGAAsG,CACvG;SACA,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC;SACjC,MAAM,CAAC,CAAC,IAA0E,EAAE,EAAE;QACrF,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACnC,OAAO,CAAC,KAAK,CACX,UAAU,GAAG,sDAAsD,CACpE,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAEtE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,IAAI;aACf,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/B,OAAO;QACV,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,KAAK,MAAM,CAAC,IAAI,qBAAqB;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dkk service init` command — declare this repo as a federated service.
|
|
3
|
+
*
|
|
4
|
+
* Writes `.dkk/service.yml` with the given name and exported contexts.
|
|
5
|
+
* This is the foundation for cross-repo references: peer repos can
|
|
6
|
+
* now address items in this service as `<name>:<context>.<Item>`.
|
|
7
|
+
*
|
|
8
|
+
* Errors if `.dkk/service.yml` already exists (use `--force` to replace).
|
|
9
|
+
*/
|
|
10
|
+
import type { Command as Cmd } from "commander";
|
|
11
|
+
export declare function registerServiceInit(program: Cmd): void;
|
|
12
|
+
//# sourceMappingURL=service-init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-init.d.ts","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/service-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,WAAW,CAAC;AAYhD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CA+EtD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { serviceFile } from "../../../shared/paths.js";
|
|
4
|
+
import { stringifyYaml } from "../../../shared/yaml.js";
|
|
5
|
+
/** Validate name is kebab-case per schema: ^[a-z][a-z0-9-]*$ */
|
|
6
|
+
function isValidKebab(name) {
|
|
7
|
+
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
8
|
+
}
|
|
9
|
+
export function registerServiceInit(program) {
|
|
10
|
+
const service = program
|
|
11
|
+
.command("service")
|
|
12
|
+
.description("Service identity and federation commands");
|
|
13
|
+
service
|
|
14
|
+
.command("init")
|
|
15
|
+
.description("Declare this repo as a federated service (writes .dkk/service.yml)")
|
|
16
|
+
.requiredOption("--name <name>", "Kebab-case service name (e.g. ordering)")
|
|
17
|
+
.option("--export <context...>", "Bounded-context names to export for federation (repeat or comma-separate)")
|
|
18
|
+
.option("--description <text>", "Optional human-readable description")
|
|
19
|
+
.option("-r, --root <path>", "Override repository root")
|
|
20
|
+
.option("--force", "Overwrite an existing service.yml")
|
|
21
|
+
.option("--json", "Output as JSON")
|
|
22
|
+
.option("--minify", "Minify JSON output")
|
|
23
|
+
.action((opts) => {
|
|
24
|
+
// Validate name
|
|
25
|
+
if (!isValidKebab(opts.name)) {
|
|
26
|
+
console.error(`Error: Service name "${opts.name}" is invalid. Use kebab-case (e.g. "ordering" or "order-management").`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// Normalize --export: commander gives an array, but each entry may
|
|
30
|
+
// also be a comma-separated list (user-friendly: --export a,b,c).
|
|
31
|
+
const rawExports = opts.export ?? [];
|
|
32
|
+
const exports = [];
|
|
33
|
+
for (const entry of rawExports) {
|
|
34
|
+
for (const ctx of entry.split(",")) {
|
|
35
|
+
const trimmed = ctx.trim();
|
|
36
|
+
if (trimmed.length === 0)
|
|
37
|
+
continue;
|
|
38
|
+
if (!isValidKebab(trimmed)) {
|
|
39
|
+
console.error(`Error: Export "${trimmed}" is not kebab-case.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
if (!exports.includes(trimmed))
|
|
43
|
+
exports.push(trimmed);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const path = serviceFile(opts.root);
|
|
47
|
+
if (existsSync(path) && !opts.force) {
|
|
48
|
+
console.error(`Error: ${path} already exists. Use --force to overwrite.`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const manifest = {
|
|
52
|
+
name: opts.name,
|
|
53
|
+
exports,
|
|
54
|
+
};
|
|
55
|
+
if (opts.description)
|
|
56
|
+
manifest.description = opts.description;
|
|
57
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
58
|
+
const header = "# Service identity for federation. See `dkk service --help`.\n";
|
|
59
|
+
writeFileSync(path, header + stringifyYaml(manifest), "utf-8");
|
|
60
|
+
if (opts.json) {
|
|
61
|
+
console.log(JSON.stringify({ path, name: opts.name, exports }, null, opts.minify ? 0 : 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(`Created ${path}`);
|
|
65
|
+
console.log(` name: ${opts.name}`);
|
|
66
|
+
console.log(` exports: [${exports.join(", ")}]`);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=service-init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-init.js","sourceRoot":"","sources":["../../../../src/features/scaffold/commands/service-init.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,gEAAgE;AAChE,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAY;IAC9C,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,0CAA0C,CAAC,CAAC;IAE3D,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,oEAAoE,CAAC;SACjF,cAAc,CAAC,eAAe,EAAE,yCAAyC,CAAC;SAC1E,MAAM,CAAC,uBAAuB,EAAE,2EAA2E,CAAC;SAC5G,MAAM,CAAC,sBAAsB,EAAE,qCAAqC,CAAC;SACrE,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,CAAC;SACvD,MAAM,CAAC,SAAS,EAAE,mCAAmC,CAAC;SACtD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,UAAU,EAAE,oBAAoB,CAAC;SACxC,MAAM,CAAC,CAAC,IAQR,EAAE,EAAE;QACH,gBAAgB;QAChB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CACX,wBAAwB,IAAI,CAAC,IAAI,uEAAuE,CACzG,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,mEAAmE;QACnE,kEAAkE;QAClE,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS;gBACnC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,OAAO,CAAC,KAAK,CAAC,kBAAkB,OAAO,sBAAsB,CAAC,CAAC;oBAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;gBACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,4CAA4C,CAAC,CAAC;YAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,QAAQ,GAAoB;YAChC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO;SACR,CAAC;QACF,IAAI,IAAI,CAAC,WAAW;YAAE,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QAE9D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,gEAAgE,CAAC;QAChF,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAE/D,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAClC,IAAI,EACJ,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACpB,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/shared/graph.d.ts
CHANGED
|
@@ -47,6 +47,14 @@ export declare class DomainGraph {
|
|
|
47
47
|
private constructor();
|
|
48
48
|
/**
|
|
49
49
|
* Build a domain graph from a loaded {@link DomainModel}.
|
|
50
|
+
*
|
|
51
|
+
* Federation-aware: the local model is walked first, then each peer
|
|
52
|
+
* model in `model.peers` is walked with its service name as a prefix.
|
|
53
|
+
* Local node ids keep the bare grammar (`<ctx>.<Name>`, `actor.X`,
|
|
54
|
+
* `adr-NNNN`, `flow.X`, `context.X`); peer node ids are prefixed
|
|
55
|
+
* with `<peerService>:` so a local policy referencing
|
|
56
|
+
* `ordering:ordering.OrderCancelled` connects through to the peer's
|
|
57
|
+
* node by name.
|
|
50
58
|
*/
|
|
51
59
|
static from(model: DomainModel): DomainGraph;
|
|
52
60
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/shared/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAsD,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/shared/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAsD,MAAM,mBAAmB,CAAC;AAOzG,iDAAiD;AACjD,MAAM,MAAM,QAAQ,GAChB,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,OAAO,GACP,KAAK,GACL,UAAU,GACV,MAAM,CAAC;AAEX,yCAAyC;AACzC,MAAM,WAAW,SAAS;IACxB,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;CACf;AAqBD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/C,iBAAiB;IACjB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAErC,sDAAsD;IACtD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA2B;IAE/C,OAAO;IAYP;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW;IAyO5C;;;;;;;;OAQG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,GAAG,CAAC,MAAM,CAAC;IAwB3D;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAI1C;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAG7B"}
|
package/dist/shared/graph.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* performs a breadth-first traversal up to a specified depth.
|
|
11
11
|
*/
|
|
12
12
|
import { forEachItem, itemAdrRefs } from "./item-visitor.js";
|
|
13
|
+
import { parseRef, qualifyItemRef, qualifyActorRef } from "./refs.js";
|
|
13
14
|
// ── Helpers ───────────────────────────────────────────────────────────
|
|
14
15
|
/** Build scoped id for a context-local item. */
|
|
15
16
|
function scopedId(context, name) {
|
|
@@ -44,6 +45,14 @@ export class DomainGraph {
|
|
|
44
45
|
// ── Factory ───────────────────────────────────────────────────────
|
|
45
46
|
/**
|
|
46
47
|
* Build a domain graph from a loaded {@link DomainModel}.
|
|
48
|
+
*
|
|
49
|
+
* Federation-aware: the local model is walked first, then each peer
|
|
50
|
+
* model in `model.peers` is walked with its service name as a prefix.
|
|
51
|
+
* Local node ids keep the bare grammar (`<ctx>.<Name>`, `actor.X`,
|
|
52
|
+
* `adr-NNNN`, `flow.X`, `context.X`); peer node ids are prefixed
|
|
53
|
+
* with `<peerService>:` so a local policy referencing
|
|
54
|
+
* `ordering:ordering.OrderCancelled` connects through to the peer's
|
|
55
|
+
* node by name.
|
|
47
56
|
*/
|
|
48
57
|
static from(model) {
|
|
49
58
|
const nodes = new Map();
|
|
@@ -74,136 +83,195 @@ export class DomainGraph {
|
|
|
74
83
|
adj.get(from)?.add(to);
|
|
75
84
|
adj.get(to)?.add(from);
|
|
76
85
|
}
|
|
77
|
-
/** Wire adr_refs for any item. */
|
|
78
|
-
function wireAdrRefs(itemId, adrRefs) {
|
|
86
|
+
/** Wire adr_refs for any item. Adds the peer prefix when refs are bare. */
|
|
87
|
+
function wireAdrRefs(itemId, adrRefs, prefix) {
|
|
79
88
|
if (!adrRefs)
|
|
80
89
|
return;
|
|
81
90
|
for (const ref of adrRefs) {
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
// Author-qualified refs (already contain `:`) stay as-is; bare
|
|
92
|
+
// refs are prefixed with this walk's service prefix.
|
|
93
|
+
const nodeRefId = ref.includes(":") || !prefix ? ref : `${prefix}${ref}`;
|
|
94
|
+
ensureNode(nodeRefId, "adr", nodeRefId);
|
|
95
|
+
addEdge(itemId, nodeRefId, "adr_ref");
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Walk one model (local or peer). When `walkPrefix` is set to
|
|
100
|
+
* `"<peerName>:"`, every node id created inside this walk is
|
|
101
|
+
* prefixed accordingly, so local + peer namespaces stay disjoint.
|
|
102
|
+
*/
|
|
103
|
+
function walkOne(m, walkPrefix) {
|
|
104
|
+
// ── Actors ────────────────────────────────────────────────────
|
|
105
|
+
for (const actor of m.actors) {
|
|
106
|
+
const id = ensureNode(`${walkPrefix}${actorId(actor.name)}`, "actor", actor.name);
|
|
107
|
+
wireAdrRefs(id, actor.adr_refs, walkPrefix);
|
|
108
|
+
}
|
|
109
|
+
// ── Bounded contexts & their items ────────────────────────────
|
|
110
|
+
for (const [ctxName, ctx] of m.contexts) {
|
|
111
|
+
const ctxId = ensureNode(`${walkPrefix}context.${ctxName}`, "context", ctxName);
|
|
112
|
+
wireAdrRefs(ctxId, undefined, walkPrefix);
|
|
113
|
+
forEachItem(ctx, (type, name, item) => {
|
|
114
|
+
const nodeKind = type;
|
|
115
|
+
const id = ensureNode(`${walkPrefix}${scopedId(ctxName, name)}`, nodeKind, name, ctxName);
|
|
116
|
+
addEdge(ctxId, id, "contains");
|
|
117
|
+
wireAdrRefs(id, itemAdrRefs(item), walkPrefix);
|
|
118
|
+
switch (type) {
|
|
119
|
+
case "event": {
|
|
120
|
+
const evt = item;
|
|
121
|
+
if (evt.raised_by) {
|
|
122
|
+
const res = qualifyItemRef(evt.raised_by, walkPrefix, ctxName);
|
|
123
|
+
const aggId = ensureNode(res.id, "aggregate", res.name, res.context);
|
|
124
|
+
addEdge(aggId, id, "emits");
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
111
127
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
case "command": {
|
|
129
|
+
const cmd = item;
|
|
130
|
+
if (cmd.handled_by) {
|
|
131
|
+
const res = qualifyItemRef(cmd.handled_by, walkPrefix, ctxName);
|
|
132
|
+
const aggId = ensureNode(res.id, "aggregate", res.name, res.context);
|
|
133
|
+
addEdge(aggId, id, "handles");
|
|
134
|
+
}
|
|
135
|
+
if (cmd.actor) {
|
|
136
|
+
const a = qualifyActorRef(cmd.actor, walkPrefix);
|
|
137
|
+
const aId = ensureNode(a.id, "actor", a.name);
|
|
138
|
+
addEdge(aId, id, "initiates");
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
119
141
|
}
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
142
|
+
case "policy": {
|
|
143
|
+
const pol = item;
|
|
144
|
+
for (const trigger of pol.when?.events ?? []) {
|
|
145
|
+
const res = qualifyItemRef(trigger, walkPrefix, ctxName);
|
|
146
|
+
const evtId = ensureNode(res.id, "event", res.name, res.context);
|
|
147
|
+
addEdge(evtId, id, "triggers");
|
|
148
|
+
}
|
|
149
|
+
for (const emitted of pol.then?.commands ?? []) {
|
|
150
|
+
const res = qualifyItemRef(emitted, walkPrefix, ctxName);
|
|
151
|
+
const cmdId = ensureNode(res.id, "command", res.name, res.context);
|
|
152
|
+
addEdge(id, cmdId, "emits");
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
123
155
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
case "aggregate": {
|
|
157
|
+
const agg = item;
|
|
158
|
+
for (const h of agg.handles?.commands ?? []) {
|
|
159
|
+
const res = qualifyItemRef(h, walkPrefix, ctxName);
|
|
160
|
+
const cmdId = ensureNode(res.id, "command", res.name, res.context);
|
|
161
|
+
addEdge(id, cmdId, "handles");
|
|
162
|
+
}
|
|
163
|
+
for (const e of agg.emits?.events ?? []) {
|
|
164
|
+
const res = qualifyItemRef(e, walkPrefix, ctxName);
|
|
165
|
+
const evtId = ensureNode(res.id, "event", res.name, res.context);
|
|
166
|
+
addEdge(id, evtId, "emits");
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
131
169
|
}
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
170
|
+
case "read_model": {
|
|
171
|
+
const rm = item;
|
|
172
|
+
for (const sub of rm.subscribes_to ?? []) {
|
|
173
|
+
const res = qualifyItemRef(sub, walkPrefix, ctxName);
|
|
174
|
+
const evtId = ensureNode(res.id, "event", res.name, res.context);
|
|
175
|
+
addEdge(id, evtId, "subscribes_to");
|
|
176
|
+
}
|
|
177
|
+
for (const user of rm.used_by ?? []) {
|
|
178
|
+
const a = qualifyActorRef(user, walkPrefix);
|
|
179
|
+
const aId = ensureNode(a.id, "actor", a.name);
|
|
180
|
+
addEdge(id, aId, "used_by");
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
135
183
|
}
|
|
136
|
-
|
|
184
|
+
case "glossary":
|
|
185
|
+
// Glossary items have no type-specific relationship wiring.
|
|
186
|
+
break;
|
|
137
187
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// ── ADRs ──────────────────────────────────────────────────────
|
|
191
|
+
for (const [adrIdRaw, adr] of m.adrs) {
|
|
192
|
+
const adrNodeId = `${walkPrefix}${adrIdRaw}`;
|
|
193
|
+
ensureNode(adrNodeId, "adr", adr.title);
|
|
194
|
+
// domain_refs → domain items. Use parseRef to split correctly
|
|
195
|
+
// for both bare and service-prefixed forms.
|
|
196
|
+
for (const ref of adr.domain_refs ?? []) {
|
|
197
|
+
const parsed = parseRef(ref);
|
|
198
|
+
if (parsed?.kind === "item" && parsed.service) {
|
|
199
|
+
// Explicit peer ref — keep as-is.
|
|
200
|
+
const id = `${parsed.service}:${parsed.context}.${parsed.name}`;
|
|
201
|
+
ensureNode(id, "aggregate", parsed.name, parsed.context);
|
|
202
|
+
addEdge(adrNodeId, id, "domain_ref");
|
|
149
203
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
204
|
+
else if (parsed?.kind === "item") {
|
|
205
|
+
// Bare ref — prefix with this walk's service.
|
|
206
|
+
const id = `${walkPrefix}${parsed.context}.${parsed.name}`;
|
|
207
|
+
ensureNode(id, "aggregate", parsed.name, parsed.context);
|
|
208
|
+
addEdge(adrNodeId, id, "domain_ref");
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// Unparseable — record edge against the raw form so
|
|
212
|
+
// visibility is preserved even though the node is dangling.
|
|
213
|
+
ensureNode(ref, "aggregate", ref);
|
|
214
|
+
addEdge(adrNodeId, ref, "domain_ref");
|
|
161
215
|
}
|
|
162
|
-
case "glossary":
|
|
163
|
-
// Glossary items have no type-specific relationship wiring.
|
|
164
|
-
break;
|
|
165
216
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const ctx = ref.slice(0, dotIdx);
|
|
177
|
-
const name = ref.slice(dotIdx + 1);
|
|
178
|
-
// We don't know the item kind from the ref alone; default to
|
|
179
|
-
// a generic node that will be reconciled if it was already created.
|
|
180
|
-
ensureNode(ref, "aggregate", name, ctx);
|
|
217
|
+
// superseded_by → another ADR
|
|
218
|
+
if (adr.superseded_by) {
|
|
219
|
+
const parsed = parseRef(adr.superseded_by);
|
|
220
|
+
const targetId = parsed?.kind === "adr"
|
|
221
|
+
? parsed.service
|
|
222
|
+
? `${parsed.service}:${parsed.id}`
|
|
223
|
+
: `${walkPrefix}${parsed.id}`
|
|
224
|
+
: adr.superseded_by;
|
|
225
|
+
ensureNode(targetId, "adr", targetId);
|
|
226
|
+
addEdge(adrNodeId, targetId, "superseded_by");
|
|
181
227
|
}
|
|
182
|
-
addEdge(adrId, ref, "domain_ref");
|
|
183
228
|
}
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
ensureNode(
|
|
187
|
-
|
|
229
|
+
// ── Flows ─────────────────────────────────────────────────────
|
|
230
|
+
for (const flow of m.index.flows ?? []) {
|
|
231
|
+
const fId = ensureNode(`${walkPrefix}${flowId(flow.name)}`, "flow", flow.name);
|
|
232
|
+
let prevStepId;
|
|
233
|
+
for (const step of flow.steps) {
|
|
234
|
+
const raw = step.ref;
|
|
235
|
+
const parsed = parseRef(raw);
|
|
236
|
+
let stepId;
|
|
237
|
+
let ctx;
|
|
238
|
+
let name;
|
|
239
|
+
if (parsed?.kind === "item" && parsed.service) {
|
|
240
|
+
stepId = `${parsed.service}:${parsed.context}.${parsed.name}`;
|
|
241
|
+
ctx = parsed.context;
|
|
242
|
+
name = parsed.name;
|
|
243
|
+
}
|
|
244
|
+
else if (parsed?.kind === "item") {
|
|
245
|
+
stepId = `${walkPrefix}${parsed.context}.${parsed.name}`;
|
|
246
|
+
ctx = parsed.context;
|
|
247
|
+
name = parsed.name;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
stepId = raw;
|
|
251
|
+
const dot = raw.indexOf(".");
|
|
252
|
+
ctx = dot > 0 ? raw.slice(0, dot) : undefined;
|
|
253
|
+
name = dot > 0 ? raw.slice(dot + 1) : raw;
|
|
254
|
+
}
|
|
255
|
+
const kind = step.type === "read_model" ? "read_model" : step.type;
|
|
256
|
+
ensureNode(stepId, kind, name, ctx);
|
|
257
|
+
addEdge(fId, stepId, "flow_step");
|
|
258
|
+
if (prevStepId) {
|
|
259
|
+
addEdge(prevStepId, stepId, "flow_next");
|
|
260
|
+
}
|
|
261
|
+
prevStepId = stepId;
|
|
262
|
+
}
|
|
188
263
|
}
|
|
189
264
|
}
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
ensureNode(ref, kind, name, ctx);
|
|
201
|
-
addEdge(fId, ref, "flow_step");
|
|
202
|
-
// Link consecutive flow steps
|
|
203
|
-
if (prevStepId) {
|
|
204
|
-
addEdge(prevStepId, ref, "flow_next");
|
|
205
|
-
}
|
|
206
|
-
prevStepId = ref;
|
|
265
|
+
// Local walk (no prefix).
|
|
266
|
+
walkOne(model, "");
|
|
267
|
+
// Peer walks, one prefix per peer.
|
|
268
|
+
for (const [peerName, peerModel] of model.peers ?? []) {
|
|
269
|
+
try {
|
|
270
|
+
walkOne(peerModel, `${peerName}:`);
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
274
|
+
console.warn(`dkk: skipped graphing peer "${peerName}": ${msg}`);
|
|
207
275
|
}
|
|
208
276
|
}
|
|
209
277
|
return new DomainGraph(nodes, edges, adj);
|