@milehimikey/em 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +121 -30
  2. package/.claude/skills/event-modeling/reference/conform.md +36 -7
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +210 -48
  4. package/.claude/skills/event-modeling/reference/extract.md +24 -7
  5. package/.claude/skills/event-modeling/reference/methodology.md +129 -32
  6. package/.claude/skills/event-modeling/templates/conformance-report.md +9 -7
  7. package/.claude/skills/event-modeling/templates/model-readme.md +9 -0
  8. package/.claude/skills/event-modeling/templates/slice.md +20 -1
  9. package/.claude/skills/event-modeling/templates/state.md +8 -0
  10. package/README.md +1 -0
  11. package/dist/catalog/build.d.ts +33 -0
  12. package/dist/catalog/build.js +117 -0
  13. package/dist/catalog/build.js.map +1 -0
  14. package/dist/catalog/classify.d.ts +14 -0
  15. package/dist/catalog/classify.js +44 -0
  16. package/dist/catalog/classify.js.map +1 -0
  17. package/dist/catalog/pages.d.ts +45 -0
  18. package/dist/catalog/pages.js +174 -0
  19. package/dist/catalog/pages.js.map +1 -0
  20. package/dist/catalog/sliceDoc.d.ts +10 -0
  21. package/dist/catalog/sliceDoc.js +20 -0
  22. package/dist/catalog/sliceDoc.js.map +1 -0
  23. package/dist/cli/catalog-inputs.d.ts +16 -0
  24. package/dist/cli/catalog-inputs.js +18 -0
  25. package/dist/cli/catalog-inputs.js.map +1 -0
  26. package/dist/cli/glossary-inputs.d.ts +15 -0
  27. package/dist/cli/glossary-inputs.js +17 -0
  28. package/dist/cli/glossary-inputs.js.map +1 -0
  29. package/dist/cli/render-inputs.d.ts +27 -0
  30. package/dist/cli/render-inputs.js +41 -0
  31. package/dist/cli/render-inputs.js.map +1 -0
  32. package/dist/cli.js +188 -10
  33. package/dist/cli.js.map +1 -1
  34. package/dist/emit/diffJson.d.ts +1 -1
  35. package/dist/emit/diffJson.js +8 -1
  36. package/dist/emit/diffJson.js.map +1 -1
  37. package/dist/emit/dot.d.ts +7 -0
  38. package/dist/emit/dot.js +10 -4
  39. package/dist/emit/dot.js.map +1 -1
  40. package/dist/emit/glossaryJson.d.ts +13 -0
  41. package/dist/emit/glossaryJson.js +36 -0
  42. package/dist/emit/glossaryJson.js.map +1 -0
  43. package/dist/emit/json.d.ts +16 -2
  44. package/dist/emit/json.js +57 -6
  45. package/dist/emit/json.js.map +1 -1
  46. package/dist/emit/theme.d.ts +3 -0
  47. package/dist/emit/theme.js +19 -0
  48. package/dist/emit/theme.js.map +1 -1
  49. package/dist/model/diff.d.ts +36 -1
  50. package/dist/model/diff.js +198 -35
  51. package/dist/model/diff.js.map +1 -1
  52. package/dist/model/edges.d.ts +12 -1
  53. package/dist/model/edges.js +26 -24
  54. package/dist/model/edges.js.map +1 -1
  55. package/dist/model/glossary.d.ts +68 -0
  56. package/dist/model/glossary.js +138 -0
  57. package/dist/model/glossary.js.map +1 -0
  58. package/dist/model/model.d.ts +35 -0
  59. package/dist/model/model.js +41 -0
  60. package/dist/model/model.js.map +1 -1
  61. package/dist/model/validate.js +252 -1
  62. package/dist/model/validate.js.map +1 -1
  63. package/dist/parser/ast.d.ts +20 -0
  64. package/dist/parser/parser.js +158 -29
  65. package/dist/parser/parser.js.map +1 -1
  66. package/dist/render/drawEdges.d.ts +3 -1
  67. package/dist/render/drawEdges.js +119 -7
  68. package/dist/render/drawEdges.js.map +1 -1
  69. package/dist/render/drawNotes.d.ts +7 -3
  70. package/dist/render/drawNotes.js +75 -28
  71. package/dist/render/drawNotes.js.map +1 -1
  72. package/dist/render/render.d.ts +20 -1
  73. package/dist/render/render.js +57 -17
  74. package/dist/render/render.js.map +1 -1
  75. package/dist/render/serve.js +155 -0
  76. package/dist/render/serve.js.map +1 -1
  77. package/dist/render/sliceDiagram.d.ts +11 -0
  78. package/dist/render/sliceDiagram.js +198 -0
  79. package/dist/render/sliceDiagram.js.map +1 -0
  80. package/dist/render/sliceOverlay.d.ts +21 -0
  81. package/dist/render/sliceOverlay.js +111 -0
  82. package/dist/render/sliceOverlay.js.map +1 -0
  83. package/dist/render/sliceStatus.d.ts +5 -0
  84. package/dist/render/sliceStatus.js +26 -0
  85. package/dist/render/sliceStatus.js.map +1 -0
  86. package/dist/render/statusOverlay.d.ts +16 -0
  87. package/dist/render/statusOverlay.js +110 -0
  88. package/dist/render/statusOverlay.js.map +1 -0
  89. package/dist/render/svgGeometry.d.ts +43 -0
  90. package/dist/render/svgGeometry.js +69 -3
  91. package/dist/render/svgGeometry.js.map +1 -1
  92. package/dist/render/watch.d.ts +1 -1
  93. package/dist/render/watch.js +5 -2
  94. package/dist/render/watch.js.map +1 -1
  95. package/package.json +3 -2
@@ -0,0 +1,174 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // HTML-string builders for `em catalog`'s static site: an index page and one
3
+ // per-slice detail page. Hand-written template literals, no templating
4
+ // library — same convention as the live-viewer page in
5
+ // src/render/serve.ts's VIEWER_HTML, just as functions instead of one static
6
+ // template, since these pages vary per model/slice.
7
+ import { slicePatternLabel } from "./classify.js";
8
+ export function escapeHtml(s) {
9
+ return s
10
+ .replace(/&/g, "&")
11
+ .replace(/</g, "&lt;")
12
+ .replace(/>/g, "&gt;")
13
+ .replace(/"/g, "&quot;")
14
+ .replace(/'/g, "&#39;");
15
+ }
16
+ /** Human-readable status cell: "no doc" (nothing found), "unknown" (doc found, no Status
17
+ * line), or the doc's own Status value verbatim. Shared by the index and slice pages so the
18
+ * two never drift out of sync on how a missing/freeform doc reads. */
19
+ function statusLabel(hasDoc, status) {
20
+ if (!hasDoc)
21
+ return "no doc";
22
+ if (!status)
23
+ return "unknown";
24
+ return status;
25
+ }
26
+ /** MIME type for the diagram `<object>` — must match the actual file `buildCatalog` wrote
27
+ * (`diagram.svg` or `diagram.png`), or the browser won't render it. */
28
+ function diagramMimeType(format) {
29
+ return format === "png" ? "image/png" : "image/svg+xml";
30
+ }
31
+ /** `homeHref` is relative to the page being built (`index.html` from the index page itself,
32
+ * `../../index.html` from a slice page two levels down) so the site works opened straight
33
+ * off disk via `file://`, with no server — an absolute `href="/"` would jump to the
34
+ * filesystem root instead. */
35
+ function layout(title, bodyHtml, homeHref) {
36
+ return `<!doctype html>
37
+ <html lang="en">
38
+ <head>
39
+ <meta charset="utf-8" />
40
+ <title>${escapeHtml(title)}</title>
41
+ <style>
42
+ :root { color-scheme: light dark; }
43
+ body {
44
+ margin: 0; font-family: system-ui, sans-serif; line-height: 1.5;
45
+ background: #fafafa; color: #1f2933;
46
+ }
47
+ header {
48
+ padding: .75rem 1.25rem; background: #1f2933; color: #fff;
49
+ }
50
+ header a { color: #fff; text-decoration: none; }
51
+ main { max-width: 960px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
52
+ table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
53
+ th, td { text-align: left; padding: .4rem .6rem; border-bottom: 1px solid #ddd; font-size: 14px; }
54
+ th { font-weight: 600; background: #f0f2f5; }
55
+ .status { display: inline-block; padding: .1rem .5rem; border-radius: 999px; font-size: 12px; background: #e4e7eb; }
56
+ .pattern { font-size: 12px; color: #52606d; }
57
+ /* Fixed-height frame, not a width-driven one: a slice diagram is often
58
+ narrow and tall relative to the full model's wide/short diagram, so
59
+ sizing by width alone would blow a narrow one's height up hugely.
60
+ object-fit doesn't apply to <object> the way it does <img> (needed here,
61
+ not <img>, so the SVG's clickable note links keep working) — giving it a
62
+ DEFINITE width AND height together stretches it non-uniformly, distorting
63
+ the aspect ratio instead of preserving it. width:auto + height:100% +
64
+ max-width:100% instead sizes it from its own intrinsic aspect ratio
65
+ (capped, never overflowing the frame); the flex centers it horizontally. */
66
+ .diagram-frame {
67
+ height: 420px; border: 1px solid #ddd; margin: 1rem 0; overflow: hidden; background: #fff;
68
+ display: flex; align-items: flex-start; justify-content: center;
69
+ }
70
+ object.diagram { width: auto; height: 100%; max-width: 100%; }
71
+ .full-diagram-link { margin: 0 0 1rem; font-size: 13px; }
72
+ .doc { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid #ddd; }
73
+ .doc h1 { font-size: 1.3rem; }
74
+ .doc h2 { font-size: 1.05rem; }
75
+ .no-doc { color: #7b8794; font-style: italic; }
76
+ code { background: #eee; padding: .05rem .3rem; border-radius: 3px; }
77
+ </style>
78
+ </head>
79
+ <body>
80
+ <header><a href="${escapeHtml(homeHref)}">Event Model Catalog</a></header>
81
+ <main>
82
+ ${bodyHtml}
83
+ </main>
84
+ </body>
85
+ </html>
86
+ `;
87
+ }
88
+ export function renderIndexPage(models, title, format) {
89
+ const sections = models
90
+ .map((m) => {
91
+ const rows = m.slices
92
+ .map((s) => ` <tr>
93
+ <td><a href="${escapeHtml(m.key)}/slices/${escapeHtml(s.key)}.html">${escapeHtml(s.name)}</a></td>
94
+ <td class="pattern">${escapeHtml(slicePatternLabel(s.pattern))}</td>
95
+ <td><span class="status">${escapeHtml(statusLabel(s.hasDoc, s.status))}</span></td>
96
+ </tr>`)
97
+ .join("\n");
98
+ const diagramHref = `${escapeHtml(m.key)}/${escapeHtml(m.diagramFile)}`;
99
+ return ` <section>
100
+ <h2>${escapeHtml(m.name)}</h2>
101
+ <p>${m.slices.length} slice${m.slices.length === 1 ? "" : "s"} · <code>${escapeHtml(m.file)}</code> · <a href="${diagramHref}">open diagram full-size</a></p>
102
+ <div class="diagram-frame"><object class="diagram" type="${diagramMimeType(format)}" data="${diagramHref}"></object></div>
103
+ <table>
104
+ <thead><tr><th>Slice</th><th>Pattern</th><th>Status</th></tr></thead>
105
+ <tbody>
106
+ ${rows}
107
+ </tbody>
108
+ </table>
109
+ </section>`;
110
+ })
111
+ .join("\n");
112
+ return layout(title, ` <h1>${escapeHtml(title)}</h1>
113
+ ${sections}`, "index.html");
114
+ }
115
+ function formatFields(el) {
116
+ if (!el.fields || el.fields.length === 0)
117
+ return "";
118
+ return el.fields.map((f) => (f.type ? `${f.name}: ${f.type}` : f.name)).join(", ");
119
+ }
120
+ function elementDetail(el) {
121
+ const parts = [];
122
+ if (el.persona)
123
+ parts.push(`@${el.persona}`);
124
+ if (el.context)
125
+ parts.push(`@${el.context}`);
126
+ if (el.from && el.from.length > 0)
127
+ parts.push(`from ${el.from.join(", ")}`);
128
+ if (el.again)
129
+ parts.push("again");
130
+ if (el.public)
131
+ parts.push("public");
132
+ const fields = formatFields(el);
133
+ if (fields)
134
+ parts.push(`{ ${fields} }`);
135
+ return parts.map(escapeHtml).join(" &middot; ");
136
+ }
137
+ function renderElementsTable(slice, elementRefs) {
138
+ const rows = slice.elements
139
+ .map((el) => {
140
+ const ref = elementRefs.get(el.id);
141
+ const anchor = ref ? ` id="${escapeHtml(ref)}"` : "";
142
+ const annotations = [];
143
+ if (el.issue)
144
+ annotations.push(`<div><span class="status">issue</span> ${escapeHtml(el.issue)}</div>`);
145
+ if (el.divergence)
146
+ annotations.push(`<div><span class="status">divergence</span> ${escapeHtml(el.divergence)}</div>`);
147
+ return ` <tr${anchor}>
148
+ <td>${escapeHtml(el.kind)}</td>
149
+ <td>${escapeHtml(el.name)}</td>
150
+ <td>${elementDetail(el)}${annotations.join("")}</td>
151
+ </tr>`;
152
+ })
153
+ .join("\n");
154
+ return ` <table>
155
+ <thead><tr><th>Kind</th><th>Name</th><th>Detail</th></tr></thead>
156
+ <tbody>
157
+ ${rows}
158
+ </tbody>
159
+ </table>`;
160
+ }
161
+ export function renderSlicePage(args) {
162
+ const { modelName, diagramFile, sliceDiagramFile, slice, pattern, elementRefs, doc, docExpectedPath } = args;
163
+ const docSection = doc
164
+ ? ` <div class="doc">${doc.html}</div>`
165
+ : ` <p class="no-doc">No slice doc found at <code>${escapeHtml(docExpectedPath)}</code>.</p>`;
166
+ return layout(`${slice.name} — ${modelName}`, ` <p><a href="../../index.html">&larr; ${escapeHtml(modelName)}</a></p>
167
+ <h1>${escapeHtml(slice.name)}</h1>
168
+ <p class="pattern">${escapeHtml(slicePatternLabel(pattern))} · <span class="status">${escapeHtml(statusLabel(!!doc, doc?.status ?? null))}</span></p>
169
+ <div class="diagram-frame"><object class="diagram" type="image/svg+xml" data="${escapeHtml(sliceDiagramFile)}"></object></div>
170
+ <p class="full-diagram-link"><a href="${escapeHtml(diagramFile)}">View full model diagram &rarr;</a></p>
171
+ ${renderElementsTable(slice, elementRefs)}
172
+ ${docSection}`, "../../index.html");
173
+ }
174
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/catalog/pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface SliceDoc {
2
+ /** The doc's raw markdown, as read from disk. */
3
+ raw: string;
4
+ /** Lowercased value of the doc's `- **Status:** ...` line, or null if no such line is found
5
+ * (a freeform doc that doesn't follow the slice.md template). */
6
+ status: string | null;
7
+ /** The whole doc rendered to HTML. */
8
+ html: string;
9
+ }
10
+ export declare function parseSliceDoc(markdown: string): SliceDoc;
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Parses a slice design doc (slices/<slice-name>.md, see
3
+ // .claude/skills/event-modeling/templates/slice.md) for `em catalog`. `em`
4
+ // has never parsed this markdown structurally before — it only checked the
5
+ // file exists (cli.ts's warnMissingNotes). This is deliberately shallow: pull
6
+ // the one bold-label line the index page needs (`- **Status:** ...`) with a
7
+ // regex rather than a full frontmatter parser, and render the whole doc body
8
+ // as HTML for the per-slice detail page. Pure — no fs access; the caller
9
+ // reads the file and hands us its text.
10
+ import { marked } from "marked";
11
+ const STATUS_LINE = /^-\s*\*\*Status:\*\*\s*(.+?)\s*$/im;
12
+ export function parseSliceDoc(markdown) {
13
+ const match = markdown.match(STATUS_LINE);
14
+ return {
15
+ raw: markdown,
16
+ status: match ? match[1].trim().toLowerCase() : null,
17
+ html: marked.parse(markdown, { async: false }),
18
+ };
19
+ }
20
+ //# sourceMappingURL=sliceDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sliceDoc.js","sourceRoot":"","sources":["../../src/catalog/sliceDoc.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,yDAAyD;AACzD,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,yEAAyE;AACzE,wCAAwC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAYhC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAEzD,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,OAAO;QACL,GAAG,EAAE,QAAQ;QACb,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;QACpD,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAW;KACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** Resolved `em catalog` flag validation, or a user-facing `error`. */
2
+ export type CatalogPlan = {
3
+ error: string;
4
+ } | {
5
+ ok: true;
6
+ format: "svg" | "png";
7
+ };
8
+ /**
9
+ * Validate the `em catalog` `--format` flag. Pure — no I/O. Only svg/png are
10
+ * accepted: the per-slice page embeds the diagram in an `<object>`/`<img>`,
11
+ * which needs a format a browser can render directly — pdf (and any other
12
+ * rsvg-convert-only format) is out of scope for a static browsable site.
13
+ */
14
+ export declare function planCatalogArgs(opts: {
15
+ format?: string;
16
+ }): CatalogPlan;
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Testable input handling for `em catalog`: --format validation, factored
3
+ // out of cli.ts so it's unit-testable without spawning a subprocess (mirrors
4
+ // src/cli/glossary-inputs.ts's planGlossaryArgs shape).
5
+ /**
6
+ * Validate the `em catalog` `--format` flag. Pure — no I/O. Only svg/png are
7
+ * accepted: the per-slice page embeds the diagram in an `<object>`/`<img>`,
8
+ * which needs a format a browser can render directly — pdf (and any other
9
+ * rsvg-convert-only format) is out of scope for a static browsable site.
10
+ */
11
+ export function planCatalogArgs(opts) {
12
+ const format = opts.format ?? "svg";
13
+ if (format !== "svg" && format !== "png") {
14
+ return { error: `em catalog: unsupported --format '${format}' (catalog diagrams support svg or png only)` };
15
+ }
16
+ return { ok: true, format };
17
+ }
18
+ //# sourceMappingURL=catalog-inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-inputs.js","sourceRoot":"","sources":["../../src/cli/catalog-inputs.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0EAA0E;AAC1E,6EAA6E;AAC7E,wDAAwD;AAKxD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAyB;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;IACpC,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACzC,OAAO,EAAE,KAAK,EAAE,qCAAqC,MAAM,8CAA8C,EAAE,CAAC;IAC9G,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,15 @@
1
+ /** Resolved `em glossary` flag validation, or a user-facing `error`. */
2
+ export type GlossaryPlan = {
3
+ error: string;
4
+ } | {
5
+ ok: true;
6
+ };
7
+ /**
8
+ * Validate the `em glossary` flag combination. Pure — no I/O. `-o` only makes
9
+ * sense alongside `--json`: there's no JSON document to write from the text
10
+ * report, so the combination is a usage error rather than a silent no-op.
11
+ */
12
+ export declare function planGlossaryArgs(opts: {
13
+ json?: boolean;
14
+ out?: string;
15
+ }): GlossaryPlan;
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Testable input handling for `em glossary`: flag-combination validation,
3
+ // factored out of cli.ts so it's unit-testable without spawning a subprocess
4
+ // (cli.ts keeps the process.exit/console wiring; see test/glossary-inputs.test.ts).
5
+ // Mirrors src/cli/diff-inputs.ts's planDiffArgs shape for the same reason.
6
+ /**
7
+ * Validate the `em glossary` flag combination. Pure — no I/O. `-o` only makes
8
+ * sense alongside `--json`: there's no JSON document to write from the text
9
+ * report, so the combination is a usage error rather than a silent no-op.
10
+ */
11
+ export function planGlossaryArgs(opts) {
12
+ if (opts.out && !opts.json) {
13
+ return { error: "em glossary: -o requires --json (there's no file to write from the text report)" };
14
+ }
15
+ return { ok: true };
16
+ }
17
+ //# sourceMappingURL=glossary-inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary-inputs.js","sourceRoot":"","sources":["../../src/cli/glossary-inputs.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0EAA0E;AAC1E,6EAA6E;AAC7E,oFAAoF;AACpF,2EAA2E;AAK3E;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAsC;IACrE,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,iFAAiF,EAAE,CAAC;IACtG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Slice } from "../model/model.js";
2
+ export type SliceLookup = {
3
+ index: number;
4
+ } | {
5
+ error: string;
6
+ };
7
+ /**
8
+ * Resolve `--slice <name>` against the compiled model's slices: an exact,
9
+ * case-sensitive match against `Slice.name` — not a fuzzy namespace. On no
10
+ * match, lists every valid slice name in declaration order so a typo is
11
+ * correctable without opening the .em file.
12
+ *
13
+ * Note this is a *different* identity than `em export`/`em catalog`'s
14
+ * deduped `sliceKey` (kebab-slug + `~2`/`~3` suffix for repeated names) —
15
+ * `findIndex` here always returns the *first* slice with a given name, so two
16
+ * identically-named slices can't be disambiguated via `--slice` at all today.
17
+ * Rare in practice (duplicate slice names are themselves a validation
18
+ * warning), and not worth a key-based `--slice` syntax until it's a real
19
+ * complaint — flagging here rather than silently.
20
+ */
21
+ export declare function resolveSliceArg(name: string, slices: Slice[]): SliceLookup;
22
+ /**
23
+ * Default output path for a slice diagram: slices/<kebab-slug>.svg next to the
24
+ * .em file — the same slices/ + kebabSlug convention em catalog's sibling-file
25
+ * lookup and the event-modeling skill's slice-doc path already use.
26
+ */
27
+ export declare function defaultSliceOut(file: string, sliceName: string): string;
@@ -0,0 +1,41 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Testable input handling for `em render --slice`: slice-name resolution and
3
+ // its default output path, factored out of cli.ts so they're unit-testable
4
+ // without spawning a subprocess (mirrors src/cli/catalog-inputs.ts's shape).
5
+ // Unlike planCatalogArgs's pre-file --format check, resolving a slice name
6
+ // needs the compiled model, so this isn't a pre-file plan*Args gate — it's
7
+ // called from the render action after compileFile().
8
+ import { dirname, join } from "node:path";
9
+ import { kebabSlug } from "../util/slug.js";
10
+ /**
11
+ * Resolve `--slice <name>` against the compiled model's slices: an exact,
12
+ * case-sensitive match against `Slice.name` — not a fuzzy namespace. On no
13
+ * match, lists every valid slice name in declaration order so a typo is
14
+ * correctable without opening the .em file.
15
+ *
16
+ * Note this is a *different* identity than `em export`/`em catalog`'s
17
+ * deduped `sliceKey` (kebab-slug + `~2`/`~3` suffix for repeated names) —
18
+ * `findIndex` here always returns the *first* slice with a given name, so two
19
+ * identically-named slices can't be disambiguated via `--slice` at all today.
20
+ * Rare in practice (duplicate slice names are themselves a validation
21
+ * warning), and not worth a key-based `--slice` syntax until it's a real
22
+ * complaint — flagging here rather than silently.
23
+ */
24
+ export function resolveSliceArg(name, slices) {
25
+ const index = slices.findIndex((s) => s.name === name);
26
+ if (index >= 0)
27
+ return { index };
28
+ const names = slices.map((s) => `"${s.name}"`).join(", ");
29
+ return {
30
+ error: `em render: no slice named "${name}" — valid slices: ${names || "(model has no slices)"}`,
31
+ };
32
+ }
33
+ /**
34
+ * Default output path for a slice diagram: slices/<kebab-slug>.svg next to the
35
+ * .em file — the same slices/ + kebabSlug convention em catalog's sibling-file
36
+ * lookup and the event-modeling skill's slice-doc path already use.
37
+ */
38
+ export function defaultSliceOut(file, sliceName) {
39
+ return join(dirname(file), "slices", `${kebabSlug(sliceName)}.svg`);
40
+ }
41
+ //# sourceMappingURL=render-inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-inputs.js","sourceRoot":"","sources":["../../src/cli/render-inputs.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,qDAAqD;AAErD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,MAAe;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACvD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO;QACL,KAAK,EAAE,8BAA8B,IAAI,qBAAqB,KAAK,IAAI,uBAAuB,EAAE;KACjG,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,SAAiB;IAC7D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AACtE,CAAC"}