@milehimikey/em 0.2.0 → 1.0.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/.claude/skills/event-modeling/SKILL.md +80 -25
- package/.claude/skills/event-modeling/reference/em-dsl.md +18 -0
- package/.claude/skills/event-modeling/reference/frontmatter.md +129 -0
- package/.claude/skills/event-modeling/reference/methodology.md +10 -0
- package/.claude/skills/event-modeling/templates/model-readme.md +4 -4
- package/.claude/skills/event-modeling/templates/slice.md +4 -2
- package/.claude/skills/event-modeling/templates/state.md +14 -3
- package/README.md +31 -2
- package/dist/cli.js +227 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.js +60 -0
- package/dist/config.js.map +1 -0
- package/dist/migrate/migrate.d.ts +26 -0
- package/dist/migrate/migrate.js +276 -0
- package/dist/migrate/migrate.js.map +1 -0
- package/dist/model/deriveSliceFields.d.ts +21 -0
- package/dist/model/deriveSliceFields.js +60 -0
- package/dist/model/deriveSliceFields.js.map +1 -0
- package/dist/model/frontmatter.d.ts +21 -0
- package/dist/model/frontmatter.js +38 -0
- package/dist/model/frontmatter.js.map +1 -0
- package/dist/model/model.js +2 -17
- package/dist/model/model.js.map +1 -1
- package/dist/model/sliceDoc.d.ts +56 -0
- package/dist/model/sliceDoc.js +28 -0
- package/dist/model/sliceDoc.js.map +1 -0
- package/dist/model/validate.d.ts +2 -0
- package/dist/model/validate.js +1 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/model/validateSliceDocs.d.ts +7 -0
- package/dist/model/validateSliceDocs.js +195 -0
- package/dist/model/validateSliceDocs.js.map +1 -0
- package/dist/slice/generate.d.ts +17 -0
- package/dist/slice/generate.js +61 -0
- package/dist/slice/generate.js.map +1 -0
- package/dist/slice/query.d.ts +42 -0
- package/dist/slice/query.js +96 -0
- package/dist/slice/query.js.map +1 -0
- package/dist/slice/sync.d.ts +18 -0
- package/dist/slice/sync.js +104 -0
- package/dist/slice/sync.js.map +1 -0
- package/dist/util/findModel.d.ts +1 -0
- package/dist/util/findModel.js +27 -0
- package/dist/util/findModel.js.map +1 -0
- package/dist/util/slug.d.ts +4 -0
- package/dist/util/slug.js +32 -0
- package/dist/util/slug.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em migrate` — one-time upgrade from the pre-1.0 fixed-template format to
|
|
3
|
+
// the frontmatter standard (docs/1.0.0-spec.md §7). planMigration() is pure;
|
|
4
|
+
// applyMigration() is the only thing that writes, so --dry-run is just
|
|
5
|
+
// "compute the plan, don't call apply."
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
9
|
+
import { normalize } from "../model/model.js";
|
|
10
|
+
import { parse } from "../parser/parser.js";
|
|
11
|
+
import { buildDocIdByElementId, deriveGeneratedFields } from "../model/deriveSliceFields.js";
|
|
12
|
+
import { frontmatterFromData, parseFrontmatter, stringifyFrontmatter } from "../model/frontmatter.js";
|
|
13
|
+
import { loadSliceDocs } from "../model/sliceDoc.js";
|
|
14
|
+
import { CURRENT_SCHEMA_VERSION } from "../model/validateSliceDocs.js";
|
|
15
|
+
import { findModelFile } from "../util/findModel.js";
|
|
16
|
+
import { dedupe, kebabSlug } from "../util/slug.js";
|
|
17
|
+
const PATTERN_FROM_PROSE = {
|
|
18
|
+
"state change": "state-change",
|
|
19
|
+
"state view": "state-view",
|
|
20
|
+
automation: "automation",
|
|
21
|
+
translation: "translation",
|
|
22
|
+
};
|
|
23
|
+
const STATUS_FROM_PROSE = {
|
|
24
|
+
draft: "draft",
|
|
25
|
+
reviewed: "reviewed",
|
|
26
|
+
"ready-to-implement": "ready-to-implement",
|
|
27
|
+
};
|
|
28
|
+
export function planMigration(dir, opts = {}) {
|
|
29
|
+
const modelPath = findModelFile(dir, opts.modelPath);
|
|
30
|
+
const model = normalize(parse(readFileSync(modelPath, "utf8")));
|
|
31
|
+
const docs = loadSliceDocs(dir);
|
|
32
|
+
const notes = [];
|
|
33
|
+
const usedIds = new Set();
|
|
34
|
+
for (const doc of docs) {
|
|
35
|
+
if (typeof doc.frontmatter?.id === "string")
|
|
36
|
+
usedIds.add(doc.frontmatter.id);
|
|
37
|
+
}
|
|
38
|
+
const drafts = [];
|
|
39
|
+
for (const doc of docs) {
|
|
40
|
+
if (typeof doc.frontmatter?.schemaVersion === "number")
|
|
41
|
+
continue; // already migrated
|
|
42
|
+
const heading = doc.raw.match(/^#\s*Slice:\s*(.+)$/m);
|
|
43
|
+
const title = heading ? heading[1].trim() : doc.file.replace(/\.md$/, "");
|
|
44
|
+
const linkedElement = model.elements.find((el) => el.note && resolve(dirname(modelPath), el.note) === resolve(doc.path));
|
|
45
|
+
let pattern;
|
|
46
|
+
let sliceElementId;
|
|
47
|
+
if (linkedElement) {
|
|
48
|
+
sliceElementId = linkedElement.id;
|
|
49
|
+
pattern = patternFromElementKind(linkedElement.kind);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const proseMatch = doc.raw.match(/\*\*Pattern:\*\*\s*([A-Za-z ]+)/);
|
|
53
|
+
const key = proseMatch?.[1].trim().toLowerCase();
|
|
54
|
+
const resolved = key ? PATTERN_FROM_PROSE[key] : undefined;
|
|
55
|
+
pattern = resolved ?? "state-change";
|
|
56
|
+
notes.push({
|
|
57
|
+
file: doc.path,
|
|
58
|
+
message: resolved
|
|
59
|
+
? "no .em element links to this doc (orphan); pattern inferred from prose only — please wire a `note` and re-sync"
|
|
60
|
+
: "no .em element links to this doc and its pattern couldn't be inferred from prose; defaulted to state-change — please review",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const statusMatch = doc.raw.match(/\*\*Status:\*\*\s*([A-Za-z- ]+)/);
|
|
64
|
+
const statusKey = statusMatch?.[1].trim().toLowerCase();
|
|
65
|
+
const resolvedStatus = statusKey ? STATUS_FROM_PROSE[statusKey] : undefined;
|
|
66
|
+
const status = resolvedStatus ?? "draft";
|
|
67
|
+
if (!resolvedStatus) {
|
|
68
|
+
notes.push({
|
|
69
|
+
file: doc.path,
|
|
70
|
+
message: "status couldn't be confidently inferred from prose; defaulted to draft",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const id = dedupe(kebabSlug(title), usedIds, "-");
|
|
74
|
+
const upstreamMatch = doc.raw.match(/\*\*Upstream events this slice relies on:\*\*\s*(.+)/);
|
|
75
|
+
const upstreamEvents = upstreamMatch
|
|
76
|
+
? upstreamMatch[1]
|
|
77
|
+
.split(",")
|
|
78
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
79
|
+
.filter((s) => s.length > 0 && !/^\{\{.*\}\}$/.test(s))
|
|
80
|
+
: [];
|
|
81
|
+
if (upstreamMatch && upstreamEvents.length === 0) {
|
|
82
|
+
notes.push({ file: doc.path, message: "upstream events line found but unparseable; left empty" });
|
|
83
|
+
}
|
|
84
|
+
drafts.push({ doc, id, title, pattern, status, sliceElementId, upstreamEvents });
|
|
85
|
+
}
|
|
86
|
+
// Cross-doc id index (already-migrated docs + newly planned drafts) so
|
|
87
|
+
// triggers/triggeredBy resolve even between two docs migrated in the same run.
|
|
88
|
+
const docIdByElementId = buildDocIdByElementId(model, docs);
|
|
89
|
+
for (const d of drafts) {
|
|
90
|
+
if (d.sliceElementId)
|
|
91
|
+
docIdByElementId.set(d.sliceElementId, d.id);
|
|
92
|
+
}
|
|
93
|
+
const docPlans = docs.map((doc) => {
|
|
94
|
+
const draft = drafts.find((d) => d.doc === doc);
|
|
95
|
+
if (!draft)
|
|
96
|
+
return { path: doc.path, file: doc.file, alreadyMigrated: true };
|
|
97
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
98
|
+
const data = {
|
|
99
|
+
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
100
|
+
id: draft.id,
|
|
101
|
+
title: draft.title,
|
|
102
|
+
pattern: draft.pattern,
|
|
103
|
+
status: draft.status,
|
|
104
|
+
version: 1,
|
|
105
|
+
model: relative(dirname(doc.path), modelPath),
|
|
106
|
+
created: today,
|
|
107
|
+
updated: today,
|
|
108
|
+
};
|
|
109
|
+
if (draft.sliceElementId) {
|
|
110
|
+
data.sliceElement = draft.sliceElementId;
|
|
111
|
+
const derived = deriveGeneratedFields(model, draft.sliceElementId, docIdByElementId);
|
|
112
|
+
if (derived) {
|
|
113
|
+
data.commands = derived.commands;
|
|
114
|
+
data.events = derived.events;
|
|
115
|
+
data.readModels = derived.readModels;
|
|
116
|
+
data.contexts = derived.contexts;
|
|
117
|
+
data.personas = derived.personas;
|
|
118
|
+
if (derived.triggers)
|
|
119
|
+
data.triggers = derived.triggers;
|
|
120
|
+
if (derived.triggeredBy)
|
|
121
|
+
data.triggeredBy = derived.triggeredBy;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (draft.upstreamEvents.length > 0)
|
|
125
|
+
data.upstreamEvents = draft.upstreamEvents;
|
|
126
|
+
const content = stringifyFrontmatter(frontmatterFromData(data), stripLegacyBullets(doc.body));
|
|
127
|
+
return { path: doc.path, file: doc.file, alreadyMigrated: false, content };
|
|
128
|
+
});
|
|
129
|
+
// Every slice's final (id, pattern, status, version) — migrated-this-run or already-migrated —
|
|
130
|
+
// used to regenerate the state file's Slice inventory table below.
|
|
131
|
+
const summaries = docs.map((doc) => {
|
|
132
|
+
const draft = drafts.find((d) => d.doc === doc);
|
|
133
|
+
if (draft)
|
|
134
|
+
return { title: draft.title, id: draft.id, pattern: draft.pattern, status: draft.status, version: 1 };
|
|
135
|
+
// Already-migrated doc: its frontmatter is whatever's on disk, which may be
|
|
136
|
+
// malformed. Fall back rather than rendering `undefined` into the table —
|
|
137
|
+
// `em validate` is what reports the malformed field.
|
|
138
|
+
const fm = doc.frontmatter;
|
|
139
|
+
return {
|
|
140
|
+
title: typeof fm.title === "string" ? fm.title : String(fm.id),
|
|
141
|
+
id: String(fm.id),
|
|
142
|
+
pattern: typeof fm.pattern === "string" ? fm.pattern : "state-change",
|
|
143
|
+
status: typeof fm.status === "string" ? fm.status : "draft",
|
|
144
|
+
version: typeof fm.version === "number" ? fm.version : 1,
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
const stateFile = planStateFile(dir, modelPath, summaries, notes);
|
|
148
|
+
return { dir, modelPath, docs: docPlans, stateFile, notes };
|
|
149
|
+
}
|
|
150
|
+
function planStateFile(dir, modelPath, summaries, notes) {
|
|
151
|
+
const stateFilePath = join(dir, ".event-modeling.md");
|
|
152
|
+
if (!existsSync(stateFilePath))
|
|
153
|
+
return undefined;
|
|
154
|
+
const raw = readFileSync(stateFilePath, "utf8");
|
|
155
|
+
const { data, body } = parseFrontmatter(raw);
|
|
156
|
+
if (data && typeof data.schemaVersion === "number") {
|
|
157
|
+
return { path: stateFilePath, file: ".event-modeling.md", alreadyMigrated: true };
|
|
158
|
+
}
|
|
159
|
+
const fmData = { schemaVersion: CURRENT_SCHEMA_VERSION, model: relative(dir, modelPath) };
|
|
160
|
+
const { content: newBody, matchedAny, unmatchedRows } = regenerateSliceInventory(body, summaries);
|
|
161
|
+
if (summaries.length > 0 && !matchedAny) {
|
|
162
|
+
notes.push({
|
|
163
|
+
file: stateFilePath,
|
|
164
|
+
message: "Slice inventory table couldn't be matched to any migrated doc by name; review manually",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else if (unmatchedRows > 0) {
|
|
168
|
+
notes.push({
|
|
169
|
+
file: stateFilePath,
|
|
170
|
+
message: `${unmatchedRows} Slice inventory row(s) couldn't be matched to a migrated doc by name; review manually`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const content = stringifyFrontmatter(frontmatterFromData(fmData), newBody);
|
|
174
|
+
return { path: stateFilePath, file: ".event-modeling.md", alreadyMigrated: false, content };
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rewrite the "| Slice | Pattern | Doc status |" table to "| Slice | Id | Pattern | Doc status |",
|
|
178
|
+
* matching each row to a migrated slice by title (or by kebab-slugging the row's slice name) and
|
|
179
|
+
* filling in id/pattern/status/version. Rows that can't be matched (e.g. a still-unfilled
|
|
180
|
+
* `{{Slice Name}}` template row) are left untouched — never guessed.
|
|
181
|
+
*/
|
|
182
|
+
function regenerateSliceInventory(content, summaries) {
|
|
183
|
+
const lines = content.split("\n");
|
|
184
|
+
const headerIdx = lines.findIndex((l) => /^\|\s*Slice\s*\|/i.test(l.trim()));
|
|
185
|
+
const separatorOk = headerIdx !== -1 && /^\|[\s\-:|]+\|$/.test((lines[headerIdx + 1] ?? "").trim());
|
|
186
|
+
if (!separatorOk)
|
|
187
|
+
return { content, matchedAny: false, unmatchedRows: 0 };
|
|
188
|
+
const byTitle = new Map(summaries.map((s) => [s.title.toLowerCase(), s]));
|
|
189
|
+
const bySlug = new Map(summaries.map((s) => [kebabSlug(s.title), s]));
|
|
190
|
+
let rowEnd = headerIdx + 2;
|
|
191
|
+
while (rowEnd < lines.length && lines[rowEnd].trim().startsWith("|"))
|
|
192
|
+
rowEnd++;
|
|
193
|
+
let matchedAny = false;
|
|
194
|
+
let unmatchedRows = 0;
|
|
195
|
+
const newRows = [];
|
|
196
|
+
for (let i = headerIdx + 2; i < rowEnd; i++) {
|
|
197
|
+
const cells = lines[i].split("|").slice(1, -1).map((c) => c.trim());
|
|
198
|
+
const sliceName = cells[0] ?? "";
|
|
199
|
+
const summary = byTitle.get(sliceName.toLowerCase()) ?? bySlug.get(kebabSlug(sliceName));
|
|
200
|
+
if (summary) {
|
|
201
|
+
matchedAny = true;
|
|
202
|
+
newRows.push(`| ${summary.title} | ${summary.id} | ${summary.pattern} | ${summary.status} (v${summary.version}) |`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
unmatchedRows++;
|
|
206
|
+
newRows.push(lines[i]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!matchedAny)
|
|
210
|
+
return { content, matchedAny: false, unmatchedRows };
|
|
211
|
+
const rewritten = [
|
|
212
|
+
...lines.slice(0, headerIdx),
|
|
213
|
+
"| Slice | Id | Pattern | Doc status |",
|
|
214
|
+
"|-------|-----|---------|------------|",
|
|
215
|
+
...newRows,
|
|
216
|
+
...lines.slice(rowEnd),
|
|
217
|
+
];
|
|
218
|
+
return { content: rewritten.join("\n"), matchedAny: true, unmatchedRows };
|
|
219
|
+
}
|
|
220
|
+
function patternFromElementKind(kind) {
|
|
221
|
+
if (kind === "command")
|
|
222
|
+
return "state-change";
|
|
223
|
+
if (kind === "translation")
|
|
224
|
+
return "translation";
|
|
225
|
+
if (kind === "processor" || kind === "automation" || kind === "saga")
|
|
226
|
+
return "automation";
|
|
227
|
+
return "state-view";
|
|
228
|
+
}
|
|
229
|
+
function stripLegacyBullets(body) {
|
|
230
|
+
return body
|
|
231
|
+
.split("\n")
|
|
232
|
+
.filter((line) => !/^-\s*\*\*(Pattern|Status):\*\*/.test(line.trim()))
|
|
233
|
+
.join("\n");
|
|
234
|
+
}
|
|
235
|
+
export function applyMigration(plan) {
|
|
236
|
+
for (const doc of plan.docs) {
|
|
237
|
+
if (!doc.alreadyMigrated && doc.content !== undefined)
|
|
238
|
+
writeFileSync(doc.path, doc.content);
|
|
239
|
+
}
|
|
240
|
+
if (plan.stateFile && !plan.stateFile.alreadyMigrated && plan.stateFile.content !== undefined) {
|
|
241
|
+
writeFileSync(plan.stateFile.path, plan.stateFile.content);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
export function formatMigrationReport(plan) {
|
|
245
|
+
const changed = plan.docs.filter((d) => !d.alreadyMigrated);
|
|
246
|
+
const skipped = plan.docs.filter((d) => d.alreadyMigrated);
|
|
247
|
+
const lines = [`# em migrate report — ${plan.dir}`, "", `Model: ${plan.modelPath}`, ""];
|
|
248
|
+
lines.push(`## Migrated (${changed.length})`);
|
|
249
|
+
lines.push(...(changed.length ? changed.map((d) => `- ${d.file}`) : ["(none)"]));
|
|
250
|
+
lines.push("", `## Already migrated, skipped (${skipped.length})`);
|
|
251
|
+
lines.push(...(skipped.length ? skipped.map((d) => `- ${d.file}`) : ["(none)"]));
|
|
252
|
+
if (plan.stateFile) {
|
|
253
|
+
lines.push("", `## State file: ${plan.stateFile.alreadyMigrated ? "already migrated, skipped" : "migrated"}`);
|
|
254
|
+
}
|
|
255
|
+
lines.push("", `## Flagged for review (${plan.notes.length})`);
|
|
256
|
+
lines.push(...(plan.notes.length ? plan.notes.map((n) => `- ${n.file}: ${n.message}`) : ["(none)"]));
|
|
257
|
+
return lines.join("\n") + "\n";
|
|
258
|
+
}
|
|
259
|
+
/** Throws unless `dir` is a clean git working tree (or `force` is set). Git is the undo mechanism. */
|
|
260
|
+
export function assertCleanWorkingTree(dir, force) {
|
|
261
|
+
if (force)
|
|
262
|
+
return;
|
|
263
|
+
let output;
|
|
264
|
+
try {
|
|
265
|
+
output = execFileSync("git", ["-C", dir, "status", "--porcelain", "."], { encoding: "utf8" });
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
throw new Error(`${dir} doesn't look like it's inside a git repository — em migrate rewrites files in ` +
|
|
269
|
+
`place and relies on git as the undo mechanism; pass --force to proceed anyway`);
|
|
270
|
+
}
|
|
271
|
+
if (output.trim().length > 0) {
|
|
272
|
+
throw new Error(`${dir} has uncommitted changes — commit or stash them first (git is em migrate's undo ` +
|
|
273
|
+
`mechanism), or pass --force to proceed anyway`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/migrate/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NormalizedModel } from "./model.js";
|
|
2
|
+
import { SliceDoc } from "./sliceDoc.js";
|
|
3
|
+
export interface DerivedSliceFields {
|
|
4
|
+
commands: string[];
|
|
5
|
+
events: string[];
|
|
6
|
+
readModels: string[];
|
|
7
|
+
contexts: string[];
|
|
8
|
+
personas: string[];
|
|
9
|
+
triggers?: string;
|
|
10
|
+
triggeredBy?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Derive the generated fields for the slice that `sliceElementId` belongs to.
|
|
14
|
+
* `docIdByElementId` maps every *other* slice doc's linked element id to that
|
|
15
|
+
* doc's frontmatter `id`, so `triggers`/`triggeredBy` (the automation/translation
|
|
16
|
+
* two-slice split) can be resolved without a second .em pass per doc. Returns
|
|
17
|
+
* null if `sliceElementId` doesn't resolve to a real element.
|
|
18
|
+
*/
|
|
19
|
+
export declare function deriveGeneratedFields(model: NormalizedModel, sliceElementId: string, docIdByElementId: ReadonlyMap<string, string>): DerivedSliceFields | null;
|
|
20
|
+
/** Map every doc's linked .em element id -> that doc's frontmatter `id`, skipping unusable docs. */
|
|
21
|
+
export declare function buildDocIdByElementId(model: NormalizedModel, docs: readonly SliceDoc[]): Map<string, string>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Computes the "generated" slice-doc frontmatter fields (docs/1.0.0-spec.md §2.2)
|
|
3
|
+
// from the .em model. Shared by validateSliceDocs.ts (drift detection) and the
|
|
4
|
+
// future `em slice sync` (Phase C) so there's exactly one definition of
|
|
5
|
+
// "what these fields should be" — never duplicated between check and write.
|
|
6
|
+
import { AUTOMATION_KINDS } from "../parser/ast.js";
|
|
7
|
+
/**
|
|
8
|
+
* Derive the generated fields for the slice that `sliceElementId` belongs to.
|
|
9
|
+
* `docIdByElementId` maps every *other* slice doc's linked element id to that
|
|
10
|
+
* doc's frontmatter `id`, so `triggers`/`triggeredBy` (the automation/translation
|
|
11
|
+
* two-slice split) can be resolved without a second .em pass per doc. Returns
|
|
12
|
+
* null if `sliceElementId` doesn't resolve to a real element.
|
|
13
|
+
*/
|
|
14
|
+
export function deriveGeneratedFields(model, sliceElementId, docIdByElementId) {
|
|
15
|
+
const element = model.byId.get(sliceElementId);
|
|
16
|
+
if (!element)
|
|
17
|
+
return null;
|
|
18
|
+
const slice = model.slices[element.sliceIndex];
|
|
19
|
+
const commands = slice.elements.filter((e) => e.kind === "command").map((e) => e.name);
|
|
20
|
+
const events = slice.elements.filter((e) => e.kind === "event").map((e) => e.name);
|
|
21
|
+
const readModels = slice.elements.filter((e) => e.kind === "view").map((e) => e.name);
|
|
22
|
+
const contexts = dedupe(slice.elements.filter((e) => e.kind === "event" && e.context).map((e) => e.context));
|
|
23
|
+
const personas = dedupe(slice.elements.filter((e) => e.kind === "ui" && e.persona).map((e) => e.persona));
|
|
24
|
+
const fields = { commands, events, readModels, contexts, personas };
|
|
25
|
+
const isReaction = slice.elements.some((e) => AUTOMATION_KINDS.has(e.kind));
|
|
26
|
+
if (isReaction) {
|
|
27
|
+
const nextSlice = model.slices[slice.index + 1];
|
|
28
|
+
const triggeredCommand = nextSlice?.elements.find((e) => e.kind === "command");
|
|
29
|
+
const triggeredDocId = triggeredCommand && docIdByElementId.get(triggeredCommand.id);
|
|
30
|
+
if (triggeredDocId)
|
|
31
|
+
fields.triggers = triggeredDocId;
|
|
32
|
+
}
|
|
33
|
+
else if (commands.length > 0) {
|
|
34
|
+
const prevSlice = model.slices[slice.index - 1];
|
|
35
|
+
const reaction = prevSlice?.elements.find((e) => AUTOMATION_KINDS.has(e.kind));
|
|
36
|
+
const triggeringDocId = reaction && docIdByElementId.get(reaction.id);
|
|
37
|
+
if (triggeringDocId)
|
|
38
|
+
fields.triggeredBy = triggeringDocId;
|
|
39
|
+
}
|
|
40
|
+
return fields;
|
|
41
|
+
}
|
|
42
|
+
function dedupe(values) {
|
|
43
|
+
return Array.from(new Set(values));
|
|
44
|
+
}
|
|
45
|
+
/** Map every doc's linked .em element id -> that doc's frontmatter `id`, skipping unusable docs. */
|
|
46
|
+
export function buildDocIdByElementId(model, docs) {
|
|
47
|
+
const index = new Map();
|
|
48
|
+
for (const doc of docs) {
|
|
49
|
+
const data = doc.frontmatter;
|
|
50
|
+
if (!data)
|
|
51
|
+
continue;
|
|
52
|
+
const id = data.id;
|
|
53
|
+
const sliceElement = data.sliceElement;
|
|
54
|
+
if (typeof id === "string" && typeof sliceElement === "string" && model.byId.has(sliceElement)) {
|
|
55
|
+
index.set(sliceElement, id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return index;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=deriveSliceFields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deriveSliceFields.js","sourceRoot":"","sources":["../../src/model/deriveSliceFields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Document } from "yaml";
|
|
2
|
+
export interface ParsedFrontmatter {
|
|
3
|
+
/** Parsed YAML data, or null if the file has no frontmatter block. */
|
|
4
|
+
data: Record<string, unknown> | null;
|
|
5
|
+
/** The YAML Document, kept around so writes can preserve comments/formatting. Null if no block. */
|
|
6
|
+
document: Document | null;
|
|
7
|
+
/** Everything after the closing `---`. Equal to the whole input when there's no frontmatter block. */
|
|
8
|
+
body: string;
|
|
9
|
+
}
|
|
10
|
+
/** Parse a slice doc's leading frontmatter block, if it has one. */
|
|
11
|
+
export declare function parseFrontmatter(raw: string): ParsedFrontmatter;
|
|
12
|
+
/** Reassemble a `---\n...\n---\n<body>` string from a Document and body text. */
|
|
13
|
+
export declare function stringifyFrontmatter(document: Document, body: string): string;
|
|
14
|
+
/** Build a fresh Document from a plain object (used when creating a new slice doc). */
|
|
15
|
+
export declare function frontmatterFromData(data: Record<string, unknown>): Document;
|
|
16
|
+
/**
|
|
17
|
+
* Shallow-merge `patch` into `document` in place, preserving every other key
|
|
18
|
+
* (including ones `em` doesn't know about) and existing comments. Returns the
|
|
19
|
+
* same Document instance for convenience.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mergeFrontmatter(document: Document, patch: Record<string, unknown>): Document;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Split/parse/stringify a `---\n...\n---\n` YAML frontmatter block. Backed by
|
|
3
|
+
// the `yaml` package's Document API so incremental writes (slice sync/update,
|
|
4
|
+
// migrate) preserve comments and fields the CLI doesn't know about — external
|
|
5
|
+
// tools may own fields in this same block (see docs/1.0.0-spec.md §8).
|
|
6
|
+
import { Document, parseDocument } from "yaml";
|
|
7
|
+
const DELIMITER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
8
|
+
/** Parse a slice doc's leading frontmatter block, if it has one. */
|
|
9
|
+
export function parseFrontmatter(raw) {
|
|
10
|
+
const match = raw.match(DELIMITER);
|
|
11
|
+
if (!match)
|
|
12
|
+
return { data: null, document: null, body: raw };
|
|
13
|
+
const [, yamlText, body] = match;
|
|
14
|
+
const document = parseDocument(yamlText);
|
|
15
|
+
const data = (document.toJS() ?? {});
|
|
16
|
+
return { data, document, body };
|
|
17
|
+
}
|
|
18
|
+
/** Reassemble a `---\n...\n---\n<body>` string from a Document and body text. */
|
|
19
|
+
export function stringifyFrontmatter(document, body) {
|
|
20
|
+
const yamlText = document.toString().replace(/\n+$/, "");
|
|
21
|
+
return `---\n${yamlText}\n---\n${body}`;
|
|
22
|
+
}
|
|
23
|
+
/** Build a fresh Document from a plain object (used when creating a new slice doc). */
|
|
24
|
+
export function frontmatterFromData(data) {
|
|
25
|
+
return new Document(data);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Shallow-merge `patch` into `document` in place, preserving every other key
|
|
29
|
+
* (including ones `em` doesn't know about) and existing comments. Returns the
|
|
30
|
+
* same Document instance for convenience.
|
|
31
|
+
*/
|
|
32
|
+
export function mergeFrontmatter(document, patch) {
|
|
33
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
34
|
+
document.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
return document;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/model/frontmatter.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,uEAAuE;AAEvE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE/C,MAAM,SAAS,GAAG,6CAA6C,CAAC;AAWhE,oEAAoE;AACpE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAE7D,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;IACjC,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAA4B,CAAC;IAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAC,QAAkB,EAAE,IAAY;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,QAAQ,QAAQ,UAAU,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB,EAAE,KAA8B;IACjF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/model/model.js
CHANGED
|
@@ -2,19 +2,12 @@
|
|
|
2
2
|
// Normalizes a parsed AST into a resolved model: stable ids, resolved
|
|
3
3
|
// persona/context lanes, and lookup indexes used by layout and validation.
|
|
4
4
|
import { AUTOMATION_KINDS } from "../parser/ast.js";
|
|
5
|
+
import { dedupe, slug } from "../util/slug.js";
|
|
5
6
|
export const DEFAULT_PERSONA = "User";
|
|
6
7
|
export const DEFAULT_CONTEXT = "Domain";
|
|
7
8
|
export function normalizeName(name) {
|
|
8
9
|
return name.trim().toLowerCase().replace(/\s+/g, " ");
|
|
9
10
|
}
|
|
10
|
-
function slug(name) {
|
|
11
|
-
const s = name
|
|
12
|
-
.trim()
|
|
13
|
-
.toLowerCase()
|
|
14
|
-
.replace(/[^a-z0-9]+/g, "_")
|
|
15
|
-
.replace(/^_+|_+$/g, "");
|
|
16
|
-
return s || "n";
|
|
17
|
-
}
|
|
18
11
|
export function normalize(ast) {
|
|
19
12
|
const personas = [...ast.personas];
|
|
20
13
|
const contexts = [...ast.contexts];
|
|
@@ -24,15 +17,7 @@ export function normalize(ast) {
|
|
|
24
17
|
const byName = new Map();
|
|
25
18
|
const usedIds = new Set();
|
|
26
19
|
let hasAutomation = false;
|
|
27
|
-
const makeId = (name) =>
|
|
28
|
-
const base = slug(name);
|
|
29
|
-
let id = base;
|
|
30
|
-
let n = 2;
|
|
31
|
-
while (usedIds.has(id))
|
|
32
|
-
id = `${base}_${n++}`;
|
|
33
|
-
usedIds.add(id);
|
|
34
|
-
return id;
|
|
35
|
-
};
|
|
20
|
+
const makeId = (name) => dedupe(slug(name), usedIds, "_");
|
|
36
21
|
ast.slices.forEach((sliceNode, sliceIndex) => {
|
|
37
22
|
const slice = {
|
|
38
23
|
name: sliceNode.name,
|
package/dist/model/model.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/model/model.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,sEAAsE;AACtE,2EAA2E;AAE3E,OAAO,EAAE,gBAAgB,EAAiC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/model/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Document } from "yaml";
|
|
2
|
+
export type SlicePattern = "state-change" | "state-view" | "automation" | "translation";
|
|
3
|
+
export type SliceStatus = "draft" | "reviewed" | "ready-to-implement" | "in-progress" | "implemented" | "deprecated" | "superseded";
|
|
4
|
+
/**
|
|
5
|
+
* The frontmatter schema from docs/1.0.0-spec.md §2. This is the *expected*
|
|
6
|
+
* shape for a valid doc — loadSliceDocs() does not enforce it (a doc on disk
|
|
7
|
+
* may be missing fields, mistyped, or have no frontmatter at all; that's what
|
|
8
|
+
* validateSliceDocs() reports). Unknown/external fields always round-trip
|
|
9
|
+
* through SliceDoc.document even though they aren't listed here.
|
|
10
|
+
*/
|
|
11
|
+
export interface SliceFrontmatter {
|
|
12
|
+
schemaVersion: number;
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
pattern: SlicePattern;
|
|
16
|
+
status: SliceStatus;
|
|
17
|
+
version: number;
|
|
18
|
+
model: string;
|
|
19
|
+
sliceElement: string;
|
|
20
|
+
commands?: string[];
|
|
21
|
+
events?: string[];
|
|
22
|
+
readModels?: string[];
|
|
23
|
+
contexts?: string[];
|
|
24
|
+
personas?: string[];
|
|
25
|
+
triggers?: string;
|
|
26
|
+
triggeredBy?: string;
|
|
27
|
+
swimlane?: {
|
|
28
|
+
persona?: string;
|
|
29
|
+
context?: string;
|
|
30
|
+
};
|
|
31
|
+
upstreamEvents?: string[];
|
|
32
|
+
relatedSlices?: string[];
|
|
33
|
+
tags?: string[];
|
|
34
|
+
owner?: string;
|
|
35
|
+
created?: string;
|
|
36
|
+
updated?: string;
|
|
37
|
+
contentHash?: string;
|
|
38
|
+
supersededBy?: string;
|
|
39
|
+
implementedRef?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface SliceDoc {
|
|
42
|
+
/** Absolute path to the doc. */
|
|
43
|
+
path: string;
|
|
44
|
+
/** Filename without directory, e.g. "place-order.md". */
|
|
45
|
+
file: string;
|
|
46
|
+
/** Parsed frontmatter, or null if the doc has no `---` block at all. Not validated. */
|
|
47
|
+
frontmatter: Record<string, unknown> | null;
|
|
48
|
+
/** The YAML Document backing `frontmatter`, for round-trip-safe writes. Null alongside frontmatter. */
|
|
49
|
+
document: Document | null;
|
|
50
|
+
/** Everything after the closing `---` (or the whole file, if there's no frontmatter block). */
|
|
51
|
+
body: string;
|
|
52
|
+
/** Full file contents, as read from disk. */
|
|
53
|
+
raw: string;
|
|
54
|
+
}
|
|
55
|
+
/** Load every `slices/*.md` doc in a model directory. Returns [] if `slices/` doesn't exist. */
|
|
56
|
+
export declare function loadSliceDocs(modelDir: string): SliceDoc[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Loads slice design docs (slices/*.md) and their frontmatter. Parsing only —
|
|
3
|
+
// no validation here (see validateSliceDocs.ts) and no writing (see
|
|
4
|
+
// src/slice/*.ts). Kept separate so "what's on disk" stays cheap and pure.
|
|
5
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { parseFrontmatter } from "./frontmatter.js";
|
|
8
|
+
/** Load every `slices/*.md` doc in a model directory. Returns [] if `slices/` doesn't exist. */
|
|
9
|
+
export function loadSliceDocs(modelDir) {
|
|
10
|
+
const slicesDir = join(modelDir, "slices");
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = readdirSync(slicesDir);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return entries
|
|
19
|
+
.filter((f) => f.endsWith(".md"))
|
|
20
|
+
.sort()
|
|
21
|
+
.map((file) => {
|
|
22
|
+
const path = join(slicesDir, file);
|
|
23
|
+
const raw = readFileSync(path, "utf8");
|
|
24
|
+
const { data, document, body } = parseFrontmatter(raw);
|
|
25
|
+
return { path, file, frontmatter: data, document, body, raw };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=sliceDoc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sliceDoc.js","sourceRoot":"","sources":["../../src/model/sliceDoc.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,8EAA8E;AAC9E,oEAAoE;AACpE,2EAA2E;AAE3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAmEpD,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3C,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/model/validate.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface Diagnostic {
|
|
|
5
5
|
severity: Severity;
|
|
6
6
|
message: string;
|
|
7
7
|
line?: number;
|
|
8
|
+
/** Slice-doc path, for diagnostics that point at a Markdown doc rather than a .em line. */
|
|
9
|
+
file?: string;
|
|
8
10
|
}
|
|
9
11
|
export declare function validate(model: NormalizedModel, grid: Grid): Diagnostic[];
|
|
10
12
|
export declare function hasErrors(diags: Diagnostic[]): boolean;
|
package/dist/model/validate.js
CHANGED
|
@@ -120,7 +120,7 @@ export function hasErrors(diags) {
|
|
|
120
120
|
}
|
|
121
121
|
/** Pretty one-line diagnostic for terminal output. */
|
|
122
122
|
export function formatDiagnostic(d) {
|
|
123
|
-
const where = d.line ? `:${d.line}` : "";
|
|
123
|
+
const where = d.file ? ` ${d.file}` : d.line ? `:${d.line}` : "";
|
|
124
124
|
const tag = d.severity === "error" ? "error" : "warn ";
|
|
125
125
|
return ` ${tag}${where} ${d.message}`;
|
|
126
126
|
}
|