@milehimikey/em 1.1.1 → 1.2.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.
@@ -5,8 +5,10 @@ description: >-
5
5
  the 7 steps of Event Modeling and the 4 patterns (State Change, State View, Automation,
6
6
  Translation), and produce implementation-ready slice design documents. Use when the user wants
7
7
  to event-model a business process or system, do event modeling / event storming, design slices,
8
- build or edit an `.em` model, or run the em tool. Works in resumable phases via an argument:
9
- `discover` (steps 1-4), `model` (steps 5-7), `slice` (deep slice specs), plus `watch` and
8
+ build or edit an `.em` model, run the em tool, or extract / reverse-engineer a current-state
9
+ event model from an existing system or codebase (event-driven or legacy/procedural). Works in
10
+ resumable phases via an argument: `discover` (steps 1-4, greenfield), `extract` (current-state
11
+ model of an existing system), `model` (steps 5-7), `slice` (deep slice specs), plus `watch` and
10
12
  `validate`. With no argument it resumes from the saved state file.
11
13
  ---
12
14
 
@@ -16,8 +18,8 @@ You are facilitating an Event Modeling session. Your job is to **extract an accu
16
18
  the user through Socratic questioning** — never to invent the domain. You drive the `em` CLI to
17
19
  render the model live, and you produce implementation-ready slice design docs.
18
20
 
19
- Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax
20
- + validation rules) before doing real work — they are the source of truth. Templates live in
21
+ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax and
22
+ validation rules) before doing real work — they are the source of truth. Templates live in
21
23
  `templates/`.
22
24
 
23
25
  ## Operating principles (every phase)
@@ -59,7 +61,8 @@ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl
59
61
  directory (or a `models/` subfolder). If found, read the state file. If not, and the phase
60
62
  needs one, ask the user for the model name and where to create it.
61
63
  3. Parse the argument (`$ARGUMENTS`) to pick the phase below. With **no argument**, read the
62
- state file and resume the recorded phase/step; if no model exists, propose starting `discover`.
64
+ state file and resume the recorded phase/step; if no model exists, propose starting `discover`
65
+ (greenfield) or `extract` (modeling an existing system).
63
66
 
64
67
  ## Project layout this skill creates
65
68
 
@@ -67,22 +70,24 @@ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl
67
70
  <model-name>/
68
71
  <model-name>.em # the model (slice docs linked via note "...")
69
72
  <model-name>.svg # render target for em watch
70
- live.html # auto-refresh viewer (copy of templates/live.html, SVG_FILE set)
73
+ live.html # file:// auto-refresh viewer (copy of templates/live.html, verbatim)
71
74
  README.md # overview + slice index (from templates/model-readme.md)
72
75
  .event-modeling.md # resumable state (from templates/state.md)
73
76
  slices/<slice-name>.md # one rich slice doc per slice (from templates/slice.md)
74
77
  ```
75
78
 
76
- When creating a new model, scaffold the directory, copy the templates in (filling the
77
- placeholders), and set `SVG_FILE` in the copied `live.html` to `<model-name>.svg`. You may use
78
- `em init` for a starter `.em`, but usually you'll build it up from the discovery conversation
79
- instead. Fill template placeholders — never leave `{{...}}` in delivered files.
79
+ When creating a new model, scaffold the directory and copy the templates in (filling the
80
+ placeholders). Copy `live.html` **verbatim** — it takes the SVG name from its URL query
81
+ (`live.html?svg=<model-name>.svg`), so there's nothing to edit. You may use `em init` for a
82
+ starter `.em`, but usually you'll build it up from the discovery conversation instead. Fill
83
+ template placeholders — never leave `{{...}}` in delivered files.
80
84
 
81
85
  ---
82
86
 
83
87
  ## Phase: `discover` — steps 1-4
84
88
 
85
89
  Goal: a draft model of events, storyboard, commands, and views. Loose is OK; structure comes next.
90
+ **Existing system?** Use `extract` instead (next section) — discover is for greenfield modeling.
86
91
 
87
92
  1. **Brainstorm events (step 1).** Ask the user to name everything that happens, as past-tense
88
93
  facts. Probe for missed state changes. For each candidate, apply the **"is it an event?"
@@ -107,6 +112,27 @@ End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (s
107
112
  decisions, open questions, slice inventory). Tell the user they can stop here and resume with
108
113
  `/event-modeling model`.
109
114
 
115
+ ## Phase: `extract` — current-state model of an existing system
116
+
117
+ Goal: a faithful **current-state** model extracted from an existing system — the as-is sibling
118
+ of `discover` (it replaces discover; the model then proceeds to `model` as usual).
119
+
120
+ **Read `reference/extract.md` before doing any extract work** — it carries a stance override
121
+ that inverts methodology step 1 (capture how the system behaves *today*; never model desired
122
+ state).
123
+
124
+ - Two source modes, detected from the system itself and confirmed with the user:
125
+ **event-driven** (an event vocabulary already exists — schemas, topics, handlers) and
126
+ **procedural/monolith** (no event vocabulary — synthesize candidate events from behavior
127
+ and docs).
128
+ - Converge via a ~7-round confirm-and-clarify loop (one concern per round; render from round 2,
129
+ validate from round 4 — see the playbook).
130
+ - **Current-state-only:** park unknowns as `# TBD` comments in the `.em`, mirrored in the state
131
+ file's Open Questions — never guess intended design.
132
+
133
+ End of phase: validated as-is model; state file updated (source mode, rounds, decisions, slice
134
+ inventory); then chain to `/event-modeling model` (steps 5-7).
135
+
110
136
  ## Phase: `model` — steps 5-7
111
137
 
112
138
  Goal: a structurally complete, **validated** model with correct patterns and swimlanes.
@@ -151,18 +177,21 @@ For each slice:
151
177
  3. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
152
178
  (the command for State Change, the view for State View, the processor for Automation, the
153
179
  translation for Translation).
154
- 4. Update `README.md`'s slice index and the state file's slice inventory (`draft` → `ready`).
180
+ 4. Update `README.md`'s slice index and the state file's slice inventory (`draft` → `ready-to-implement`).
155
181
  5. Re-render and `em validate`.
156
182
 
157
183
  ## Phase: `watch` — live team view
158
184
 
159
- 1. Ensure `live.html` exists in the model dir (copy from `templates/live.html`) with `SVG_FILE`
160
- set to `<model-name>.svg`.
161
- 2. Start the watcher in the background: `em watch <model-name>.em -o <model-name>.svg`
162
- (run_in_background). It re-renders the SVG on every save.
163
- 3. Tell the user to open `live.html` in a browser and share their screen — it auto-refreshes ~1s,
164
- so the team sees the model evolve as you edit during the session. (`em` has no built-in server;
165
- the HTML viewer provides the auto-reload.)
185
+ **Recommended (instant push, no polling):** start the watcher with `--serve` in the background:
186
+ `em watch <model-name>.em -o <model-name>.svg --serve` (run_in_background). It re-renders on every
187
+ save and pushes an instant reload to the browser over Server-Sent Events. Tell the user to open the
188
+ URL it prints (e.g. `http://localhost:5173/?svg=<model-name>.svg`) and share their screen — updates
189
+ appear the moment you save, with no idle churn between edits.
190
+
191
+ **No-server fallback (`file://`):** if the user prefers not to run a server, start the plain watcher
192
+ `em watch <model-name>.em -o <model-name>.svg` (run_in_background) and have them open the copied
193
+ `live.html?svg=<model-name>.svg` in a browser. It polls every ~2s and reloads without flicker. No
194
+ editing of `live.html` is needed — the `?svg=` query picks the model.
166
195
 
167
196
  ## Phase: `validate`
168
197
 
@@ -186,6 +215,7 @@ em validate <name>.em # check rules; exit 0 if clean/warnin
186
215
  em render <name>.em -o <name>.svg # render (svg/png/pdf by extension)
187
216
  em render <name>.em --emit-dot # inspect generated Graphviz DOT
188
217
  em watch <name>.em -o <name>.svg # re-render on save (run in background)
218
+ em watch <name>.em -o <name>.svg --serve # + live viewer with instant push-reload (--port N)
189
219
  ```
190
220
 
191
221
  Always finish a working session by: re-rendering, running `em validate`, and updating
@@ -1,3 +1,5 @@
1
+ <!-- DSL behavior change? Update BOTH docs/dsl.md and .claude/skills/event-modeling/reference/em-dsl.md -->
2
+
1
3
  # `em` DSL Reference & Cheatsheet
2
4
 
3
5
  The `em` tool (`@milehimikey/em`) is a slice-first text DSL rendered to a strict Graphviz
@@ -13,7 +15,8 @@ em init [file] # scaffold a starter model.em (default: model.em)
13
15
  em render <file> -o out.svg # render (extension picks format: svg, png, pdf)
14
16
  em render <file> --emit-dot # print the Graphviz DOT instead of rendering
15
17
  em render <file> --keep-empty-lanes # keep the API lane even when empty
16
- em watch <file> -o out.svg # re-render on every save (file-based; no HTTP server)
18
+ em watch <file> -o out.svg # re-render on every save (file-based)
19
+ em watch <file> -o out.svg --serve # + localhost live viewer, instant SSE push-reload (--port N)
17
20
  em validate <file> # check event-modeling rules; exit 0 if only warnings/clean
18
21
  ```
19
22
 
@@ -34,6 +37,7 @@ slice "Name" { # one vertical time step (a column)
34
37
  ui Free Text @Persona # screen; @Persona picks its row (defaults to first/"User")
35
38
  command Free Text # state-changing request (API band)
36
39
  view Free Text from "Event A", "Event B" # read model fed by event(s)
40
+ view Free Text again from "Event C" # later instance of an evolving read model (see Clauses)
37
41
  event Free Text @Context # recorded fact; @Context picks its row (defaults to "Domain")
38
42
  processor Free Text from "View" # automation; aliases: automation | saga | translation
39
43
  }
@@ -53,7 +57,16 @@ arrow From Element -> To Element # explicit cross-slice edge (overrides infer
53
57
  ### Clauses
54
58
  - **Tags:** `@Persona` only on `ui`; `@Context` only on `event`. Undeclared tags auto-create a row.
55
59
  - **`from "X"`** on views/automations declares the source(s); names are quoted, comma-separated.
56
- Matching is case-insensitive and whitespace-normalized.
60
+ Matching is case-insensitive and whitespace-normalized. A `from` may never point at an event or
61
+ view that first appears in a LATER slice (forward-only timeline — validation error).
62
+ - **`again`** (views only): `view <Name> again [from "Event", …]` declares a later instance of an
63
+ already-declared read model — the forward-only device for a view that evolves as later events
64
+ land. Instances are ONE logical view: the first declaration owns the `note` binding; each
65
+ instance's `from` lists only the NEW events landing at that point (not cumulative); instances are
66
+ linked left-to-right with a continuity arrow; a reaction (`from "View"`) reads the nearest
67
+ instance at-or-before its own slice. `again` with no earlier declaration is a validation error.
68
+ Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
69
+ `from`/`arrow` — plain repeats are only warning-free while unreferenced.
57
70
  - **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
58
71
  a clickable marker in SVG and a legend entry in PNG/PDF. **This is how slice docs attach.**
59
72
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
@@ -141,7 +154,9 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
141
154
  *referenced* duplicate, resolving to the first). **Wire each event to a read model exactly once:**
142
155
  a repeated instance's `from` lists only the **new** events since the previous instance (not
143
156
  cumulative), or the event draws a duplicate arrow to the same read model at every repeat. (An
144
- event may still feed several *different* read models, once each.)
157
+ event may still feed several *different* read models, once each.) When a repeated view **must be
158
+ referenced** — e.g. a reaction reads it via `from` — declare its later instances with
159
+ `view X again` instead (see Clauses); instances resolve properly and draw continuity arrows.
145
160
  - **Keep arrows span-1: put each repeat right after its feeding event.** Place a read-model instance
146
161
  immediately after the event that updates it, sourcing only that single adjacent event. A read
147
162
  model far from its source events draws long arrows that cross the read-model row and read as
@@ -170,6 +185,11 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
170
185
  2. **Unknown event source** — `view X from "Event"` where the event doesn't exist anywhere.
171
186
  3. **Unknown read-model source** — `processor X from "View"` where the view doesn't exist.
172
187
  4. **Arrow endpoint mismatch** — `arrow A -> B` where A or B matches no element name.
188
+ 5. **Backward timeline** ("time flows left to right") — an event feeding a view instance in an
189
+ EARLIER slice (fix: add a `view X again` instance where the event lands and move the source
190
+ there), a reaction reading a view before any instance of it exists, or an explicit backward
191
+ `arrow`.
192
+ 6. **`again` without an earlier declaration** — declare the view plainly the first time it appears.
173
193
 
174
194
  **Warnings (should fix):**
175
195
  1. **Automation/translation shares slice with its command** — both `automation`/`processor` and
@@ -0,0 +1,123 @@
1
+ # Extract Phase — Current-State Model of an Existing System
2
+
3
+ Read this **before doing any extract work**. It governs the `extract` phase and **overrides
4
+ `methodology.md` step 1's stance** for the duration of extraction: you are capturing how the
5
+ system behaves **today**, not modeling what the business needs. Everything else in the
6
+ methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the two-slice
7
+ reaction split — still applies.
8
+
9
+ ## Stance override — current-state-only
10
+
11
+ - Capture the system **as it is**, faithfully, warts included. Never invent future or desired
12
+ state. Improvement ideas that surface go to the state file's Open Questions or Decisions log
13
+ — never into the model.
14
+ - Methodology step 1's caution ("model the process the business **needs**, not how the current
15
+ system happens to work — existing system behavior is a common source of fake events") is
16
+ **deliberately suspended**: here the existing system *is* the subject.
17
+ - The **"is it an event?" filter still applies.** Derived values (totals, calculated fields)
18
+ and telemetry/activity (clicks, page views, logins) are still not events, even when the
19
+ current system stores or emits them as such. Park each rejection in the Decisions log with
20
+ why, so it isn't re-proposed.
21
+ - **Never guess.** Unknown or ambiguous current behavior is parked (see TBD convention below),
22
+ not filled in with what the design "probably" intends.
23
+
24
+ ## Setup
25
+
26
+ 1. Run the SKILL.md preconditions (tool check, model location) and scaffold the standard
27
+ project layout if no model exists yet.
28
+ 2. **Agree the scope line first**: which system, which part, what's out of bounds. Write it to
29
+ the state file's Session inputs.
30
+ 3. Record in the state file: `Source mode` (after detection, below) and
31
+ `Existing system refs` — the repo paths, event-schema/topic locations, and docs you will
32
+ extract from.
33
+
34
+ ## Mode detection
35
+
36
+ Inspect the system with your own tools (Grep/Read over the codebase and docs), then confirm
37
+ the mode with the user:
38
+
39
+ - **Event-driven** — the system already has an event vocabulary. Signals: event
40
+ schemas/classes, topics/streams/queues, message or CDC handlers, an event store, pub/sub
41
+ wiring. Extraction *lifts* the model from that vocabulary.
42
+ - **Procedural / monolith** — no event vocabulary. Signals: transaction scripts, service
43
+ methods mutating rows, CRUD controllers, procedural runbooks/docs. Extraction *synthesizes*
44
+ candidate events from the state changes the behavior implies.
45
+
46
+ Mixed systems happen: pick the dominant mode, note the exception areas in the state file.
47
+
48
+ ## Sourcing by mode
49
+
50
+ - **Event-driven:** inventory the emitted/consumed event types; take past-tense business facts
51
+ directly; then filter — technical/infrastructure messages (retries, heartbeats, envelope
52
+ wrappers) and derived/telemetry types are dropped (log why). Keep the system's own names
53
+ where they are honest past-tense facts; propose a cleaner past-tense name only when the
54
+ original is technical (record the mapping in the Decisions log).
55
+ - **Procedural:** for each significant operation, ask "what business fact has become true when
56
+ this completes?" — those are the candidate events. Screens and tables are *evidence* of
57
+ state changes, not events themselves; a table row's lifecycle (created/updated/closed) often
58
+ implies several events. Apply the event filter hard here — procedural systems are where
59
+ derived values and activity records masquerade as facts.
60
+
61
+ ## The confirm-and-clarify loop (~7 rounds)
62
+
63
+ One concern per round. After every round: mirror what you extracted back to the user, update
64
+ the `.em`, and (from R2) re-render so they see the model grow. Convergence beats coverage —
65
+ it is fine to finish a round with `# TBD`s still parked.
66
+
67
+ - **R1 — Candidate events.** Extract (event-driven) or synthesize (procedural) the past-tense
68
+ facts; apply the event test; user confirms/renames/rejects. Ambiguous candidates → `# TBD`.
69
+ - **R2 — Timeline order.** Order the confirmed events into the as-is narrative. Identify the
70
+ *actual* actors/callers and the *real* UI or API surfaces (headless systems: no
71
+ `ui`/`persona` — use write/read translations per `em-dsl.md`). **First `em render` here.**
72
+ - **R3 — Commands / inputs.** For each event, the request that actually produces it today
73
+ (imperative name). Unclear trigger → `# TBD`, not a plausible guess.
74
+ - **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
75
+ `from "Event"` (repeat read models span-1 per `em-dsl.md`). **Start running `em validate`**
76
+ each round from here.
77
+ - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
78
+ listeners, outbound syncs, inbound webhooks. Model each as a reaction with the two-slice
79
+ `reaction → command → event` split and the slice-ordering rules in `em-dsl.md` — even when
80
+ the current code wires it as one procedural step (note the as-built shape in the slice's
81
+ `# TBD`/Decisions if it differs).
82
+ - **R6 — Gap & TBD reconciliation.** Walk every parked `# TBD` with the user. Resolve only
83
+ what they can confirm **as a current-state fact**; everything else stays parked. Explicitly
84
+ refuse to resolve a TBD by inventing desired behavior.
85
+ - **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings; remember
86
+ the validator's blind spot — check the reaction split by hand). User confirms the model is a
87
+ faithful picture of today. Mark extraction complete in the state file and hand off.
88
+
89
+ Seven rounds is the default shape, not a quota — collapse rounds for a small system, repeat
90
+ one for a sprawling one. Record any deviation in the Decisions log.
91
+
92
+ ## TBD parking convention
93
+
94
+ - Inline `.em` comment on its own line directly above (or trailing) the element it concerns:
95
+ `# TBD: <specific question about current behavior>`. Comments are native DSL syntax — they
96
+ render and validate cleanly.
97
+ - Mirror every `# TBD` 1:1 as a checkbox in the state file's **Open questions / parking lot**,
98
+ and remove both together when resolved.
99
+ - A `# TBD` is only ever resolved by a confirmed current-state fact — never replaced by a
100
+ guessed element.
101
+
102
+ ## Completion & handoff
103
+
104
+ Extraction is complete when: the model renders; `em validate` is clean (or remaining warnings
105
+ are consciously accepted in the Decisions log); every unknown is a parked `# TBD`; and the
106
+ user has confirmed the model reflects today's behavior. Then:
107
+
108
+ 1. State file: set phase progress (extraction rounds checked), log the completion decision,
109
+ seed the **Slice inventory** (every slice, pattern, doc status `none`).
110
+ 2. The extracted model **replaces `discover`** — suggest `/event-modeling model` next
111
+ (steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
112
+ as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
113
+ extraction.
114
+
115
+ ## Anti-patterns
116
+
117
+ - **Inventing desired state** — the cardinal sin of extraction; park it instead.
118
+ - **Importing screens/tables as events** — they are evidence of state changes, not facts.
119
+ - **Guessing an unclear trigger or reaction** instead of parking a `# TBD`.
120
+ - **Cleaning up the domain while extracting** — renames and restructures beyond honest
121
+ past-tense naming are `model`-phase work, done with the user after the as-is picture exists.
122
+ - **Skipping render/validate until the end** — the loop depends on the user seeing the model
123
+ grow round by round.
@@ -121,6 +121,9 @@ state change anyone can think of. No commands, no UI yet — just facts.
121
121
  User Clicked Button, User Viewed Page, User Logged In.
122
122
  - Model the process the business **needs**, not how the current system happens to work. Existing
123
123
  system behavior is a common source of fake events — screens and tables leaking in as facts.
124
+ (**Extract mode inverts this**: in the skill's `extract` phase the existing system *is* the
125
+ subject and this caution is deliberately suspended — see the "Extract" section below and
126
+ `reference/extract.md`. The derived-value and telemetry filters still apply there.)
124
127
  - Output: a flat list of candidate `event` names.
125
128
 
126
129
  ### Step 2 — The Plot / Storyboard *(discover)*
@@ -178,6 +181,26 @@ Walk the whole model with stakeholders and check for loose ends:
178
181
 
179
182
  ---
180
183
 
184
+ ## Extract: current-state models of existing systems
185
+
186
+ The `extract` phase derives a model from an **existing** system instead of a greenfield
187
+ conversation. It is discover's as-is sibling: its confirm-and-clarify rounds converge the same
188
+ step 1–4 outputs (events, timeline, commands, read models) — *derived from the system*, then
189
+ confirmed with the user — after which the model enters `model` (steps 5–7) as usual.
190
+
191
+ The stance inversion, stated authoritatively: **extraction captures how the system behaves
192
+ today — faithfully, warts included — and never invents future or desired state.** Step 1's
193
+ anti-current-system caution is suspended for the duration; the "is it an event?" filter is
194
+ not — derived values and telemetry are still rejected even when the current system emits them.
195
+ Unknown or ambiguous current behavior is parked (`# TBD` in the `.em`, mirrored in the state
196
+ file's Open Questions), never guessed. Desired-state improvements belong to the `model` and
197
+ `slice` phases, after the as-is picture is agreed.
198
+
199
+ Procedure, source modes (event-driven vs. procedural synthesis), and the round-by-round loop:
200
+ `reference/extract.md`.
201
+
202
+ ---
203
+
181
204
  ## Socratic stance (applies throughout)
182
205
 
183
206
  - Ask **one focused question at a time**; never assume a domain fact — extract it.
@@ -3,10 +3,22 @@
3
3
  Live viewer for an em event model. Auto-reloads the rendered SVG so a team on a shared
4
4
  screen sees updates from `em watch` without manual refresh.
5
5
 
6
- Usage:
7
- 1) In one terminal: em watch MODEL.em -o MODEL.svg
8
- 2) Set SVG_FILE below to the SVG filename (same folder as this file).
9
- 3) Open this file in a browser (open live.html) and share the screen.
6
+ Two ways to use it:
7
+
8
+ A) Push (recommended, instant, no polling):
9
+ em watch MODEL.em -o MODEL.svg --serve
10
+ then open the URL it prints (http://localhost:5173/?svg=MODEL.svg). You don't need
11
+ this file at all in that mode — the server serves its own push-based viewer.
12
+
13
+ B) This file, over file:// (no server):
14
+ 1) In one terminal: em watch MODEL.em -o MODEL.svg
15
+ 2) Open this file with the SVG name in the query:
16
+ open "live.html?svg=MODEL.svg" (macOS)
17
+ No editing required — the ?svg= param picks the model.
18
+
19
+ Under file:// the browser gives no reliable "did the file change?" signal (fetch/HEAD is
20
+ blocked), so this fallback polls and reloads on a gentle interval. It double-buffers the
21
+ swap so a shared screen never flashes. For zero idle work and instant updates, use --serve.
10
22
 
11
23
  The SVG is embedded via <object> so note "..." links inside it stay clickable.
12
24
  -->
@@ -37,17 +49,57 @@
37
49
  <object id="svg" type="image/svg+xml"></object>
38
50
  </div>
39
51
  <script>
40
- // Change this to your model's SVG filename:
41
- const SVG_FILE = "model.svg";
42
- const obj = document.getElementById("svg");
52
+ // SVG filename from ?svg=<name>.svg (defaults to model.svg) — one file serves any model.
53
+ const SVG_FILE = new URLSearchParams(location.search).get("svg") || "model.svg";
54
+ // Poll cadence for the file:// fallback. Tune freely; --serve makes polling unnecessary.
55
+ const REFRESH_INTERVAL_MS = 2000;
56
+
57
+ const stage = document.getElementById("stage");
43
58
  const stamp = document.getElementById("stamp");
44
- function reload() {
45
- // cache-bust so the freshly written SVG is fetched each tick
46
- obj.setAttribute("data", SVG_FILE + "?t=" + Date.now());
47
- stamp.textContent = new Date().toLocaleTimeString();
59
+ let current = document.getElementById("svg");
60
+ let lastModified = null;
61
+
62
+ // Best-effort change check — only meaningful when served over http(s). Under file://
63
+ // fetch/HEAD is blocked, so this throws and we fall back to reloading every tick.
64
+ async function changed() {
65
+ if (!location.protocol.startsWith("http")) return true;
66
+ try {
67
+ const res = await fetch(SVG_FILE, { method: "HEAD", cache: "no-store" });
68
+ const tag = res.headers.get("last-modified") || res.headers.get("etag");
69
+ if (tag && tag === lastModified) return false;
70
+ lastModified = tag;
71
+ return true;
72
+ } catch {
73
+ return true; // can't tell — reload to be safe
74
+ }
48
75
  }
49
- reload();
50
- setInterval(reload, 1000);
76
+
77
+ // Double-buffer: load into a hidden <object>, swap on load, so the shared screen
78
+ // never flashes white during a reload.
79
+ function swap() {
80
+ const next = document.createElement("object");
81
+ next.type = "image/svg+xml";
82
+ next.style.cssText = current.style.cssText;
83
+ next.addEventListener("load", () => {
84
+ stage.replaceChild(next, current);
85
+ current = next;
86
+ stamp.textContent = new Date().toLocaleTimeString();
87
+ }, { once: true });
88
+ next.setAttribute("data", SVG_FILE + "?t=" + Date.now());
89
+ }
90
+
91
+ // Self-scheduling loop: schedule the next tick only after this one resolves, so a slow
92
+ // reload can never overlap the next tick.
93
+ async function tick() {
94
+ try {
95
+ if (await changed()) swap();
96
+ } finally {
97
+ setTimeout(tick, REFRESH_INTERVAL_MS);
98
+ }
99
+ }
100
+
101
+ swap();
102
+ setTimeout(tick, REFRESH_INTERVAL_MS);
51
103
  </script>
52
104
  </body>
53
105
  </html>
@@ -6,10 +6,13 @@
6
6
  While modeling, run the live view so the team can watch the diagram update:
7
7
 
8
8
  ```bash
9
- em watch {{model-name}}.em -o {{model-name}}.svg # re-renders on every save
10
- # then open live.html in a browser (auto-refreshes ~1s; share the screen)
9
+ em watch {{model-name}}.em -o {{model-name}}.svg --serve # re-render + instant push-reload
10
+ # then open the URL it prints (http://localhost:5173/?svg={{model-name}}.svg) and share the screen
11
11
  ```
12
12
 
13
+ No server? Run `em watch {{model-name}}.em -o {{model-name}}.svg` and open
14
+ `live.html?svg={{model-name}}.svg` in a browser (polls ~2s; share the screen).
15
+
13
16
  Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
14
17
 
15
18
  ## Patterns legend
@@ -7,14 +7,25 @@ Keep it current at the end of every working session.
7
7
  # Event Modeling Progress — {{Model Name}}
8
8
 
9
9
  - **Model file:** `{{model-name}}.em`
10
- - **Current phase:** {{discover | model | slice}}
11
- - **Current step:** {{1–7, see methodology}}
10
+ - **Current phase:** {{extract | discover | model | slice}}
11
+ - **Current step:** {{1–7, see methodology; or extraction round R1–R7}}
12
12
  - **Last updated:** {{YYYY-MM-DD}}
13
13
 
14
14
  ## Session inputs
15
15
  - **Scope line:** {{one-line description of what's in/out of bounds for this model}}
16
16
  - **PRD / spec reference:** {{path, link, or "none"}}
17
17
  - **Headless/API model:** {{yes | no}}
18
+ - **Source mode:** {{greenfield | extract-event-driven | extract-procedural}}
19
+ - **Existing system refs:** {{repo paths, event-schema/topic locations, docs — or "n/a"}}
20
+
21
+ ## Extraction progress (existing-system models only — delete for greenfield)
22
+ - [ ] R1. Candidate events (extracted/synthesized, filtered, confirmed)
23
+ - [ ] R2. Timeline order (as-is narrative, actors/callers, first render)
24
+ - [ ] R3. Commands / inputs
25
+ - [ ] R4. Read models / outputs (validate from here)
26
+ - [ ] R5. Boundaries & reactions (two-slice splits)
27
+ - [ ] R6. Gap & TBD reconciliation
28
+ - [ ] R7. Convergence (render + validate clean, user confirmed as-is)
18
29
 
19
30
  ## Steps completed
20
31
  - [ ] 1. Brainstorm events
@@ -36,4 +47,4 @@ Keep it current at the end of every working session.
36
47
  ## Slice inventory
37
48
  | Slice | Pattern | Doc status |
38
49
  |-------|---------|------------|
39
- | {{Slice Name}} | {{State Change/View/Automation/Translation}} | {{none / draft / ready}} |
50
+ | {{Slice Name}} | {{State Change/View/Automation/Translation}} | {{none / draft / reviewed / ready-to-implement}} |
package/README.md CHANGED
@@ -1,187 +1,85 @@
1
- # em — AI friendly event modeling
1
+ # em — event modeling in plain text
2
2
 
3
3
  `em` is a command-line tool for [Event Modeling](https://eventmodeling.org/). You write a
4
- model in a small, **plain-text, slice-first DSL** (`.em`) and `em` renders it to a clean,
5
- deterministic diagram (SVG by default, PNG on request). Because the source is just text —
6
- diff-able, reviewable, and unambiguous — it's as easy for an LLM to generate and edit as it
7
- is for a person to write by hand. Layout is a **strict grid**: swimlane rows and
8
- time-ordered slice columns stay perfectly aligned with no manual offsets.
4
+ model in a small, slice-first DSL and `em` renders it as a clean, deterministic diagram.
5
+ Because the source is plain text — diff-able, reviewable, unambiguous — it's as easy for an
6
+ AI to write and edit as it is for a person, and `em validate` keeps either one honest.
9
7
 
10
- ```
11
- examples/order-fulfillment.em -> em render -> order-fulfillment.svg
12
- ```
8
+ ![An order-fulfillment event model rendered by em](https://raw.githubusercontent.com/milehimikey/em/main/examples/order-fulfillment.svg)
13
9
 
14
- Rendering is **self-contained** — Graphviz runs as bundled WebAssembly and PNG is
15
- rasterized in-process, so there's nothing to `apt`/`brew` install for SVG or PNG output.
10
+ The source for that diagram is [about 70 lines of text](examples/order-fulfillment.em).
16
11
 
17
12
  ## Install
18
13
 
19
14
  ```bash
20
- npm install -g @milehimikey/em # then `em` is on your PATH
15
+ npm install -g @milehimikey/em
21
16
  ```
22
17
 
23
- Requires **Node ≥ 18**. SVG and PNG need no other tools. PDF (and other Graphviz raster
24
- formats) are optional and use a system `rsvg-convert` (librsvg) if one is installed.
25
-
26
- Run without installing:
27
-
28
- ```bash
29
- npx @milehimikey/em init model.em
30
- npx @milehimikey/em render model.em
31
- ```
18
+ Requires Node ≥ 18. SVG and PNG rendering are fully self-contained (Graphviz runs as
19
+ bundled WebAssembly); nothing else to install. PDF is optional — see
20
+ [docs/dependencies.md](docs/dependencies.md).
32
21
 
33
22
  ## Quickstart
34
23
 
35
24
  ```bash
36
25
  em init model.em # scaffold a starter model
37
26
  em render model.em # -> model.svg (open it in a browser)
38
- em watch model.em # re-render on every save
27
+ em watch model.em --serve # live browser view, re-renders on every save
39
28
  em validate model.em # check event-modeling rules
40
29
  ```
41
30
 
42
- A `.em` model is a list of **slices** (vertical time steps), each holding elements that fall
43
- into swimlane rows:
31
+ A model is a list of slices — vertical time steps, read left to right — whose elements land
32
+ in swimlane rows:
44
33
 
45
- ```
34
+ ```em
46
35
  model "Order Fulfillment"
47
36
 
48
- persona Customer # UI swimlanes (top), one row per persona
49
- context Order # event swimlanes (bottom), one row per context
50
- context Payment
51
-
52
- slice "Browse Catalog" { # each slice is one column (time, left -> right)
53
- ui Product Catalog @Customer # @Persona places the screen in a persona row
54
- command Place Order { # a `{ … }` block declares the element's fields
55
- customerId
56
- total: Money # optional `: Type` annotation
57
- }
58
- event Order Placed @Order note "notes/order-placed.md" { # note "…" links docs
59
- orderId
60
- total: Money
61
- }
37
+ persona Customer
38
+ context Order
39
+
40
+ slice "Browse Catalog" {
41
+ ui Product Catalog @Customer
42
+ command Place Order
43
+ event Order Placed @Order
62
44
  }
63
45
 
64
46
  slice "View Open Orders" {
65
- view Open Orders from "Order Placed" # from "<event>" wires the data flow
47
+ view Open Orders from "Order Placed"
66
48
  ui Order List @Customer
67
49
  }
68
50
  ```
69
51
 
70
- ## The four patterns
71
-
72
- Event Modeling is built from four patterns, all expressible as slices:
73
-
74
- | Pattern | Flow | DSL elements |
75
- |---|---|---|
76
- | **Input** (state change) | UI → command → event | `ui`, `command`, `event` |
77
- | **Output** (state view) | event → read model → UI | `event`, `view from "…"`, `ui` |
78
- | **Automation** | read model (todo) → processor → command → event | `view from "…"` + `processor`, then `command` + `event` |
79
- | **Translation** | external read model → translation → command → event | `view from "…"` + `translation`, then `command` + `event` |
52
+ The [tutorial](docs/tutorial.md) builds a complete model from an empty file in about twenty
53
+ minutes.
80
54
 
81
- Automation/translation slices are **not** triggered by a command — they react to a read
82
- model (a "todo list") or a timer and *issue* a command when done. The automation slice
83
- contains **only** the read model it reads and the processor/translation; the command it
84
- triggers (and that command's event) is the **next** slice.
55
+ ## Model with AI
85
56
 
86
- ## DSL reference
87
-
88
- ### Element keywords → swimlane bands (top → bottom)
89
-
90
- 0. *(header)* — each slice name is rendered as a title cell in the top row
91
- 1. `automation` / `processor` / `saga` / `translation` — **Automation** band (only shown if used)
92
- 2. `ui` — **persona** rows (one per `persona`)
93
- 3. `command` + `view` — the **API** row (commands and read models share one lane)
94
- 4. `event` — **context/concept** rows (one per `context`)
95
-
96
- Commands and read models share the **API** row, so a slice holds *either* a command *or* a
97
- read model. The empty API lane is dropped when a model has neither; pass `--keep-empty-lanes`
98
- to keep it.
99
-
100
- `@Persona` / `@Context` choose the row within a band (undeclared tags create a new row).
101
- `view <Name> again [from "Event", …]` declares a LATER INSTANCE of an already-declared read
102
- model — the Event Modeling device for keeping the timeline forward-only as a view evolves.
103
- Instances are one logical view: the first declaration owns the `note` doc; each instance lists
104
- the events that reach it there; instances are linked left-to-right with a continuity arrow, and
105
- reactions (`processor … from "View"`) read the nearest instance at-or-before their slice.
106
-
107
- `view … from "Event"[, "Event2"]` declares which events feed a read model and draws the
108
- data-flow arrow. Arrows within a slice are inferred from the pattern; use `arrow A -> B` for
109
- anything extra (e.g. a read model feeding a different screen).
110
-
111
- ### Fields
112
-
113
- Any element can declare **data fields** in a `{ … }` block — the data it accepts (command),
114
- records (event), projects (read model), or shows (UI). Each field is `name` with an optional
115
- `: Type`; write one per line, or inline and comma-separated:
116
-
117
- ```
118
- command Place Order { # one per line
119
- customerId
120
- items: List<LineItem>
121
- total: Money
122
- }
123
-
124
- event Payment Requested @Payment { orderId, amount: Money } # inline
125
- ```
126
-
127
- Fields render **in the box**, UML-style: the name, a divider rule, then the fields. The box
128
- grows to fit them (width stays fixed, so columns stay aligned), and arrows still anchor to
129
- the box edges so the lines stay stable. A field block coexists with `note`/`from` clauses on
130
- the same element. Fields are the foundation of the later event-modeling phases (the slicing /
131
- information-completeness process); field-level validation across slices is planned.
132
-
133
- ### Notes
134
-
135
- Any element can carry `note "path.md"` (valid on every keyword). The prose lives in the
136
- markdown file — keeping the diagram uncluttered — and the box gets a small **numbered
137
- folded-corner marker** in its top-right corner. A **legend** is appended below the diagram
138
- mapping each number to its element and note file, so even a static export tells you which
139
- note belongs to which box.
140
-
141
- In **SVG** output the markers and legend rows are links; clicking one opens the markdown
142
- file. Links resolve relative to the **output SVG's location**, so they keep working whether
143
- the SVG is rendered beside the model or into another folder (as long as the notes travel
144
- with it). Open the SVG in a **web browser** to use the links — image viewers like macOS
145
- Preview/Quick Look show the markers but ignore SVG hyperlinks. Raster output (PNG/PDF) can't
146
- carry links — that's what the footnote numbers + legend are for.
147
-
148
- ## Commands
57
+ `em` ships a Claude Code skill that runs a facilitated Event Modeling session: the AI asks
58
+ the questions, you supply the domain, and the model renders live as it grows.
149
59
 
150
60
  ```bash
151
- em init [file] # scaffold a starter model (default: model.em)
152
- em render <file> [-o out.svg] # render (format from -o extension or -T)
153
- em render <file> -o out.png # PNG (in-process, no system deps)
154
- em render <file> --emit-dot # print the generated DOT instead of rendering
155
- em render <file> --keep-empty-lanes # don't collapse the API lane when empty
156
- em watch <file> [-o out.svg] # re-render on every save
157
- em validate <file> # check event-modeling rules (non-zero exit on errors)
61
+ em skill install # copy the skill into .claude/skills/event-modeling/
158
62
  ```
159
63
 
160
- ## Validation rules (`em validate`)
161
-
162
- - two same-band elements in one slice (collision) → **error** (split into separate slices)
163
- - a read model whose `from "Event"` doesn't exist → **error**
164
- - **time flows left to right** (the Two Laws): an event feeding an EARLIER view instance,
165
- a reaction reading a view that only exists later, or an explicit backward `arrow` → **error**
166
- (fix: add `view X again` at the point on the timeline where the event lands)
167
- - `again` on a view with no earlier declaration → **error**; `again` on non-views → parse error
168
- - multi-event slices always render as a FAN from the command (never event→event); fanned
169
- arrows route around the column gutter so they cannot read as a chain
170
- - an automation/translation whose `from "<read model>"` doesn't exist → **error**
171
- - an `arrow` endpoint that matches no element → **error**
172
- - an automation/translation slice that also holds the command it triggers → **warning**
173
- - a command that records no event → **warning**
174
- - a read model with no source event → **warning**
175
- - a name defined more than once and referenced → **warning** (resolves to first)
64
+ Then run `/event-modeling` in Claude Code. See [docs/ai-workflow.md](docs/ai-workflow.md)
65
+ for the phases and what a session produces, and the
66
+ [em-with-ai repository](https://github.com/milehimikey/em-with-ai) for a ~50-slice model
67
+ built this way.
176
68
 
177
69
  ## Documentation
178
70
 
179
- - [docs/timeline-model.md](docs/timeline-model.md) — the Two Laws of the Timeline (forward-only
180
- validation, `view … again`)
181
- - [docs/why-dot-not-plantuml.md](docs/why-dot-not-plantuml.md) — why DOT + the strict-grid /
182
- self-drawn-edges architecture
183
- - [docs/features.md](docs/features.md) — full feature list and roadmap
184
- - [docs/dependencies.md](docs/dependencies.md) — how rendering works with no system deps
71
+ | Doc | What it answers |
72
+ |---|---|
73
+ | [docs/tutorial.md](docs/tutorial.md) | Learn the tool by building a model from scratch |
74
+ | [docs/patterns.md](docs/patterns.md) | The four Event Modeling patterns and their DSL shapes |
75
+ | [docs/dsl.md](docs/dsl.md) | Full DSL reference: keywords, `from`, `again`, fields, notes |
76
+ | [docs/cli.md](docs/cli.md) | Every command and flag |
77
+ | [docs/validation.md](docs/validation.md) | Every rule `em validate` checks, and the fixes |
78
+ | [docs/timeline.md](docs/timeline.md) | The Two Laws of the Timeline |
79
+ | [docs/ai-workflow.md](docs/ai-workflow.md) | The Claude Code skill: install, phases, artifacts |
80
+ | [docs/dependencies.md](docs/dependencies.md) | What's bundled vs. what needs a system install |
81
+ | [docs/architecture.md](docs/architecture.md) | How rendering works; why Graphviz, not PlantUML |
82
+ | [docs/roadmap.md](docs/roadmap.md) | What's planned |
185
83
 
186
84
  ## Development
187
85
 
@@ -192,32 +90,6 @@ npm test # vitest
192
90
  npx tsx src/cli.ts <command> ... # run straight from source
193
91
  ```
194
92
 
195
- ## AI Assistant (Claude Code)
196
-
197
- `em` ships a **Claude Code skill** that guides you and your team through Event Modeling using the 7-step process and 4 design patterns, producing implementation-ready slice specifications. The AI drives the model — asking focused questions, never guessing domain facts — while `em validate` keeps the diagram honest.
198
-
199
- **Install the skill into your project:**
200
-
201
- ```bash
202
- em skill install # copies the skill into .claude/skills/event-modeling/
203
- em skill install --force # overwrite an existing installation
204
- ```
205
-
206
- Then, in Claude Code, run:
207
-
208
- ```
209
- /event-modeling # start (or resume) a guided session
210
- /event-modeling discover # steps 1–4: brainstorm events, timeline, commands, read models
211
- /event-modeling model # steps 5–7: swimlanes, patterns, completeness check
212
- /event-modeling slice # deep implementation specs, one per slice
213
- /event-modeling watch # open a live browser view for team modeling sessions
214
- /event-modeling validate # run em validate and resolve all diagnostics
215
- ```
216
-
217
- Sessions are **resumable** — the skill checkpoints progress in `.event-modeling.md` so you can continue across conversations.
218
-
219
- For a complete worked example (a headless CPQ system with ~50 slices), see the [em-with-ai repository](https://github.com/milehimikey/em-with-ai).
220
-
221
93
  ## License
222
94
 
223
95
  [MIT](LICENSE) © milehimikey
package/dist/cli.js CHANGED
@@ -9,13 +9,14 @@ import { compile } from "./pipeline.js";
9
9
  import { ParseError } from "./parser/parser.js";
10
10
  import { renderDot, formatFromPath } from "./render/render.js";
11
11
  import { watchFile } from "./render/watch.js";
12
+ import { startLiveServer } from "./render/serve.js";
12
13
  import { formatDiagnostic, hasErrors } from "./model/validate.js";
13
14
  import { STARTER_EM } from "./templates.js";
14
15
  const program = new Command();
15
16
  program
16
17
  .name("em")
17
18
  .description("Event Modeling CLI — slice-first DSL rendered as a strict Graphviz grid")
18
- .version("1.1.1");
19
+ .version("1.2.0");
19
20
  program
20
21
  .command("init")
21
22
  .description("scaffold a starter .em model")
@@ -69,9 +70,12 @@ program
69
70
  .option("-o, --out <path>", "output path (extension picks the format)")
70
71
  .option("-T, --format <fmt>", "output format (svg, png, pdf, ...)")
71
72
  .option("--keep-empty-lanes", "keep the API lane even when empty")
73
+ .option("--serve", "serve a live viewer with instant push-reload (no polling)")
74
+ .option("--port <n>", "port for --serve (default 5173)", (v) => parseInt(v, 10))
72
75
  .action(async (file, opts) => {
73
76
  const out = opts.out ?? defaultOut(file, opts.format ?? "svg");
74
77
  const fmt = opts.format ?? formatFromPath(out);
78
+ let server;
75
79
  const build = async () => {
76
80
  const started = Date.now();
77
81
  try {
@@ -86,12 +90,25 @@ program
86
90
  }
87
91
  await renderDot(dot, model, out, fmt, dirname(file));
88
92
  console.log(`rendered ${out} (${Date.now() - started}ms)`);
93
+ server?.notify();
89
94
  }
90
95
  catch (e) {
91
96
  reportError(e);
92
97
  }
93
98
  };
94
99
  await build();
100
+ if (opts.serve) {
101
+ try {
102
+ // Serve the directory the SVG is written to — that's what the browser
103
+ // fetches, and note "..." links inside the SVG resolve relative to it.
104
+ server = await startLiveServer({ dir: dirname(resolve(out)), port: opts.port });
105
+ console.log(`→ live view: ${server.url}/?svg=${encodeURIComponent(basename(out))}`);
106
+ console.log(" open it in a browser and share your screen");
107
+ }
108
+ catch (e) {
109
+ reportError(e);
110
+ }
111
+ }
95
112
  watchFile(file, build);
96
113
  console.log(`watching ${file} … (ctrl-c to stop)`);
97
114
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface LiveServer {
2
+ /** Base URL the viewer is served at, e.g. http://localhost:5173 */
3
+ url: string;
4
+ /** The port actually bound (differs from the request if it was taken). */
5
+ port: number;
6
+ /** Push a reload to every connected browser (call after a successful render). */
7
+ notify: () => void;
8
+ /** Stop the server and drop all SSE connections. */
9
+ close: () => Promise<void>;
10
+ }
11
+ export interface StartOptions {
12
+ /** Directory to serve (the model dir); static files are resolved within it. */
13
+ dir: string;
14
+ /** Preferred port; falls forward to the next free one if taken. */
15
+ port?: number;
16
+ }
17
+ /**
18
+ * Start the live server for `dir`. Resolves once it is listening.
19
+ */
20
+ export declare function startLiveServer(opts: StartOptions): Promise<LiveServer>;
@@ -0,0 +1,202 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Optional localhost dev server for `em watch --serve`.
3
+ //
4
+ // Serves the model directory over HTTP and pushes an instant reload to the
5
+ // browser via Server-Sent Events (SSE) after each successful re-render. This
6
+ // replaces the poll-and-cache-bust loop of the static live.html viewer: no
7
+ // polling, instant updates, and zero idle work between edits.
8
+ //
9
+ // Uses only Node's built-in `http` — no extra dependencies. Binds to
10
+ // 127.0.0.1 (loopback) so the model is never exposed off the machine.
11
+ import { createServer } from "node:http";
12
+ import { createReadStream } from "node:fs";
13
+ import { stat } from "node:fs/promises";
14
+ import { extname, join, relative, resolve, sep } from "node:path";
15
+ const HOST = "127.0.0.1";
16
+ const DEFAULT_PORT = 5173;
17
+ // Number of sequential ports to try if the preferred one is taken.
18
+ const PORT_TRIES = 10;
19
+ // Keepalive comment cadence so proxies/browsers don't drop the idle SSE stream.
20
+ const SSE_KEEPALIVE_MS = 15000;
21
+ const CONTENT_TYPES = {
22
+ ".svg": "image/svg+xml; charset=utf-8",
23
+ ".png": "image/png",
24
+ ".html": "text/html; charset=utf-8",
25
+ ".css": "text/css; charset=utf-8",
26
+ ".js": "text/javascript; charset=utf-8",
27
+ ".json": "application/json; charset=utf-8",
28
+ ".pdf": "application/pdf",
29
+ };
30
+ /**
31
+ * Start the live server for `dir`. Resolves once it is listening.
32
+ */
33
+ export async function startLiveServer(opts) {
34
+ const root = resolve(opts.dir);
35
+ const clients = new Set();
36
+ const server = createServer((req, res) => handle(req, res, root, clients));
37
+ const port = await listen(server, opts.port ?? DEFAULT_PORT);
38
+ const keepalive = setInterval(() => {
39
+ for (const res of clients)
40
+ res.write(": keepalive\n\n");
41
+ }, SSE_KEEPALIVE_MS);
42
+ // Don't let the keepalive timer hold the process open on its own.
43
+ keepalive.unref?.();
44
+ return {
45
+ url: `http://localhost:${port}`,
46
+ port,
47
+ notify() {
48
+ for (const res of clients)
49
+ res.write("data: reload\n\n");
50
+ },
51
+ async close() {
52
+ clearInterval(keepalive);
53
+ for (const res of clients)
54
+ res.end();
55
+ clients.clear();
56
+ await new Promise((res) => server.close(() => res()));
57
+ },
58
+ };
59
+ }
60
+ /** Try to listen on `port`, falling forward a few times if it's in use. */
61
+ function listen(server, port) {
62
+ return new Promise((resolvePort, reject) => {
63
+ let attempt = 0;
64
+ const tryPort = (p) => {
65
+ const onError = (err) => {
66
+ if (err.code === "EADDRINUSE" && attempt < PORT_TRIES - 1) {
67
+ attempt += 1;
68
+ tryPort(p + 1);
69
+ }
70
+ else {
71
+ reject(err);
72
+ }
73
+ };
74
+ server.once("error", onError);
75
+ server.listen(p, HOST, () => {
76
+ server.removeListener("error", onError);
77
+ // With p === 0 the OS assigns a free port; read the real one back.
78
+ resolvePort(server.address().port);
79
+ });
80
+ };
81
+ tryPort(port);
82
+ });
83
+ }
84
+ function handle(req, res, root, clients) {
85
+ const url = new URL(req.url ?? "/", "http://localhost");
86
+ const pathname = decodeURIComponent(url.pathname);
87
+ if (pathname === "/__events") {
88
+ openEventStream(res, clients);
89
+ return;
90
+ }
91
+ if (pathname === "/") {
92
+ res.writeHead(200, {
93
+ "content-type": "text/html; charset=utf-8",
94
+ "cache-control": "no-store",
95
+ });
96
+ res.end(VIEWER_HTML);
97
+ return;
98
+ }
99
+ serveStatic(pathname, root, res);
100
+ }
101
+ function openEventStream(res, clients) {
102
+ res.writeHead(200, {
103
+ "content-type": "text/event-stream",
104
+ "cache-control": "no-store",
105
+ connection: "keep-alive",
106
+ });
107
+ res.write(": connected\n\n");
108
+ clients.add(res);
109
+ res.on("close", () => clients.delete(res));
110
+ }
111
+ async function serveStatic(pathname, root, res) {
112
+ // Resolve within root and reject anything that escapes it (path traversal).
113
+ const target = resolve(join(root, "." + pathname));
114
+ const rel = relative(root, target);
115
+ if (rel === ".." || rel.startsWith(".." + sep) || (rel === "" && pathname !== "/")) {
116
+ res.writeHead(403).end("forbidden");
117
+ return;
118
+ }
119
+ try {
120
+ const info = await stat(target);
121
+ if (!info.isFile()) {
122
+ res.writeHead(404).end("not found");
123
+ return;
124
+ }
125
+ res.writeHead(200, {
126
+ "content-type": CONTENT_TYPES[extname(target).toLowerCase()] ?? "application/octet-stream",
127
+ "cache-control": "no-store",
128
+ });
129
+ createReadStream(target).pipe(res);
130
+ }
131
+ catch {
132
+ res.writeHead(404).end("not found");
133
+ }
134
+ }
135
+ // Served viewer page. Same double-buffered, no-flicker swap as the static
136
+ // live.html fallback, but driven by SSE push instead of a poll loop — the
137
+ // browser reloads only when the server says the SVG actually changed.
138
+ const VIEWER_HTML = `<!doctype html>
139
+ <html lang="en">
140
+ <head>
141
+ <meta charset="utf-8" />
142
+ <title>Event Model — Live</title>
143
+ <style>
144
+ :root { color-scheme: light dark; }
145
+ body { margin: 0; font-family: system-ui, sans-serif; background: #fafafa; }
146
+ header {
147
+ display: flex; align-items: center; gap: .75rem;
148
+ padding: .5rem .9rem; background: #1f2933; color: #fff; font-size: 14px;
149
+ }
150
+ header .dot { width: 9px; height: 9px; border-radius: 50%; background: #34c759; transition: background .3s; }
151
+ header .dot.stale { background: #f0a020; }
152
+ header .stamp { margin-left: auto; opacity: .7; font-variant-numeric: tabular-nums; }
153
+ #stage { padding: 1rem; }
154
+ object { width: 100%; height: calc(100vh - 60px); border: 0; }
155
+ </style>
156
+ </head>
157
+ <body>
158
+ <header>
159
+ <span class="dot" id="dot"></span>
160
+ <span>Event Model — live (push)</span>
161
+ <span class="stamp" id="stamp">connecting…</span>
162
+ </header>
163
+ <div id="stage">
164
+ <object id="svg" type="image/svg+xml"></object>
165
+ </div>
166
+ <script>
167
+ // The SVG filename comes from ?svg=<name>.svg (defaults to model.svg), so one
168
+ // server serves any model without editing this page.
169
+ const SVG_FILE = new URLSearchParams(location.search).get("svg") || "model.svg";
170
+ const stage = document.getElementById("stage");
171
+ const stamp = document.getElementById("stamp");
172
+ const dot = document.getElementById("dot");
173
+ let current = document.getElementById("svg");
174
+
175
+ // Double-buffer: load into a hidden <object>, swap on load, so the shared
176
+ // screen never flashes white during a reload. Keep <object> (not <img>) so
177
+ // note "..." links inside the SVG stay clickable.
178
+ function reload() {
179
+ dot.classList.add("stale");
180
+ const next = document.createElement("object");
181
+ next.type = "image/svg+xml";
182
+ next.style.cssText = current.style.cssText;
183
+ next.addEventListener("load", () => {
184
+ stage.replaceChild(next, current);
185
+ current = next;
186
+ stamp.textContent = new Date().toLocaleTimeString();
187
+ dot.classList.remove("stale");
188
+ }, { once: true });
189
+ next.setAttribute("data", SVG_FILE + "?t=" + Date.now());
190
+ }
191
+
192
+ reload();
193
+
194
+ const es = new EventSource("/__events");
195
+ es.onopen = () => { stamp.textContent = new Date().toLocaleTimeString(); };
196
+ es.onmessage = reload;
197
+ es.onerror = () => { dot.classList.add("stale"); };
198
+ </script>
199
+ </body>
200
+ </html>
201
+ `;
202
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/render/serve.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,wDAAwD;AACxD,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,8DAA8D;AAC9D,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AAEtE,OAAO,EAAE,YAAY,EAAmC,MAAM,WAAW,CAAC;AAE1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,IAAI,GAAG,WAAW,CAAC;AACzB,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,mEAAmE;AACnE,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,gFAAgF;AAChF,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,8BAA8B;IACtC,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,0BAA0B;IACnC,MAAM,EAAE,yBAAyB;IACjC,KAAK,EAAE,gCAAgC;IACvC,OAAO,EAAE,iCAAiC;IAC1C,MAAM,EAAE,iBAAiB;CAC1B,CAAC;AAoBF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAkB;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAE3E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC,CAAC;IAE7D,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC1D,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACrB,kEAAkE;IAClE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IAEpB,OAAO;QACL,GAAG,EAAE,oBAAoB,IAAI,EAAE;QAC/B,IAAI;QACJ,MAAM;YACJ,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,CAAC,KAAK;YACT,aAAa,CAAC,SAAS,CAAC,CAAC;YACzB,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YACrC,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,SAAS,MAAM,CACb,MAAuC,EACvC,IAAY;IAEZ,OAAO,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE;QACzC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE;YAC5B,MAAM,OAAO,GAAG,CAAC,GAA0B,EAAE,EAAE;gBAC7C,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,GAAG,UAAU,GAAG,CAAC,EAAE,CAAC;oBAC1D,OAAO,IAAI,CAAC,CAAC;oBACb,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACjB,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC;YACF,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;gBAC1B,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACxC,mEAAmE;gBACnE,WAAW,CAAE,MAAM,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,MAAM,CACb,GAAoB,EACpB,GAAmB,EACnB,IAAY,EACZ,OAA4B;IAE5B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAElD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC7B,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACrB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,cAAc,EAAE,0BAA0B;YAC1C,eAAe,EAAE,UAAU;SAC5B,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,eAAe,CAAC,GAAmB,EAAE,OAA4B;IACxE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;QACjB,cAAc,EAAE,mBAAmB;QACnC,eAAe,EAAE,UAAU;QAC3B,UAAU,EAAE,YAAY;KACzB,CAAC,CAAC;IACH,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,QAAgB,EAChB,IAAY,EACZ,GAAmB;IAEnB,4EAA4E;IAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACpC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACpC,OAAO;QACT,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,cAAc,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,0BAA0B;YAC1F,eAAe,EAAE,UAAU;SAC5B,CAAC,CAAC;QACH,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,0EAA0E;AAC1E,sEAAsE;AACtE,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+DnB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@milehimikey/em",
3
- "version": "1.1.1",
4
- "description": "AI-friendly Event Modeling \u2014 a slice-first text DSL rendered to a strict, self-contained Graphviz grid (SVG/PNG)",
3
+ "version": "1.2.0",
4
+ "description": "AI-friendly Event Modeling — a slice-first text DSL rendered to a strict, self-contained Graphviz grid (SVG/PNG)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "em": "./dist/cli.js"
@@ -59,4 +59,4 @@
59
59
  "typescript": "^5.6.0",
60
60
  "vitest": "^2.1.0"
61
61
  }
62
- }
62
+ }