@menapo/machine 0.0.0-stage → 0.1.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/dist/index.js ADDED
@@ -0,0 +1,582 @@
1
+ import { i as suggest, n as checkParts, r as fail, t as checkKeys } from "./dev-BAz6e3W-.js";
2
+ import { a as start, d as CHECK, f as DEV, l as track, o as derivedClass, p as dev, r as run, s as storeClass, u as bit } from "./instance-BM2UqeXW.js";
3
+ import { a as release, c as own, i as activate, n as wireParallel, o as BOUND, r as wireHistory, s as bind, t as DONE } from "./chart-BWjU2VF4.js";
4
+ import { t as inspect } from "./inspect-BmyLUhcy.js";
5
+ //#region src/core/bindable.ts
6
+ /**
7
+ * `bindable`: a prop the owner may control or leave to the machine (D9, as
8
+ * spike S2 refined it). One transition serves both modes:
9
+ *
10
+ * - uncontrolled, the machine moves or stores the value and tells the owner
11
+ * through `on…Change`;
12
+ * - controlled, a transition that would change the value runs its actions
13
+ * when the event is handled and asks the owner; only the move waits. When
14
+ * the owner's new value arrives, the machine takes the waiting transition's
15
+ * target, and the target's entry sees the event that asked. Props that do
16
+ * not grant it drop it (a veto); an owner that changes the value unasked
17
+ * moves the machine to the matching state.
18
+ *
19
+ * All of it lives here and reaches the core only through the
20
+ * {@link Control} a `bindable()` value carries, so an app that never imports
21
+ * `bindable` ships none of it.
22
+ */
23
+ /** What entry actions see when the owner moves a bound boolean unasked. */
24
+ const PROPS = { type: "machine.props" };
25
+ /**
26
+ * A controlled-or-uncontrolled prop: `open: bindable(false, { states: {
27
+ * true: "open", false: "closed" } })` gives the owner `open`, `defaultOpen`
28
+ * and `onOpenChange`; `value: bindable("")` is data that actions write as
29
+ * `props.value = next`.
30
+ */
31
+ const bindable = (value, options) => ({
32
+ [BOUND]: control,
33
+ value,
34
+ ...options
35
+ });
36
+ const capital = (name) => name[0].toUpperCase() + name.slice(1);
37
+ /** True when leaf `at` is inside `state`. */
38
+ function inside(at, state) {
39
+ for (; at; at = at.up) if (at === state) return true;
40
+ return false;
41
+ }
42
+ const bounds = (machine) => machine.bd;
43
+ const control = {
44
+ bind(machine, prop, name, bindable, proto) {
45
+ const { states, changed } = bindable;
46
+ const find = (path) => {
47
+ const state = machine.ss.find((s) => s.path === path);
48
+ if (CHECK && !state) fail("UNKNOWN_TARGET", machine.name, `bindable "${name}" names state "${path}", which does not exist; use a path from the root: ${machine.ss.map((s) => `"${s.path}"`).join(", ")}.`);
49
+ return state;
50
+ };
51
+ if (CHECK && changed !== void 0 && !machine.handled.has(changed)) {
52
+ const handled = [...machine.handled];
53
+ fail("UNKNOWN_EVENT", machine.name, `bindable "${name}" sends "${changed}" when the owner changes it, but no state handles it${suggest(changed, handled) || "."} Handle it in a state's \`on\`, or name an event one does.`);
54
+ }
55
+ const b = {
56
+ prop,
57
+ name,
58
+ bit: bit(1 + machine.nm.length - machine.pn.length + prop),
59
+ default: `default${capital(name)}`,
60
+ change: `on${capital(name)}Change`,
61
+ yes: states && find(states.true),
62
+ no: states && find(states.false),
63
+ changed
64
+ };
65
+ bounds(machine).push(b);
66
+ Object.defineProperty(proto, name, {
67
+ get() {
68
+ const instance = this.i;
69
+ track(b.bit);
70
+ const given = instance.o[name];
71
+ if (given !== void 0) return given;
72
+ if (!b.yes) return this.v[prop];
73
+ track(1);
74
+ return inside(instance.s, b.yes);
75
+ },
76
+ set(value) {
77
+ const owner = this.i.o;
78
+ if (DEV) {
79
+ if (b.yes) fail("BOUND_WRITE", machine.name, `props.${name} follows the state; move to "${b.yes.path}" or "${b.no.path}" instead of writing it.`);
80
+ if (!dev.acting) fail("CONTEXT_WRITE", machine.name, `props.${name} was written outside an action. Write it in a transition's \`do\`, \`entry\` or \`exit\`, and send an event from here instead.`);
81
+ }
82
+ const controlled = owner[name] !== void 0;
83
+ if ((controlled ? owner[name] : this.v[prop]) === value) return;
84
+ if (!controlled) {
85
+ this.v[prop] = value;
86
+ this.m |= b.bit;
87
+ }
88
+ owner[b.change]?.(value);
89
+ }
90
+ });
91
+ },
92
+ initial(instance, leaf) {
93
+ const owner = instance.o;
94
+ for (const b of bounds(instance.M)) {
95
+ const given = owner[b.name] !== void 0 ? owner[b.name] : owner[b.default];
96
+ if (given === void 0) continue;
97
+ if (!b.yes) instance.props.v[b.prop] = given;
98
+ else if (!!given !== inside(leaf, b.yes)) leaf = (given ? b.yes : b.no).lf;
99
+ }
100
+ return leaf;
101
+ },
102
+ hold(instance, rule) {
103
+ for (const b of bounds(instance.M)) {
104
+ if (!b.yes || instance.o[b.name] === void 0) continue;
105
+ const next = inside(rule.to.lf, b.yes);
106
+ if (inside(instance.s, b.yes) === next) continue;
107
+ run(rule.do, instance);
108
+ instance.p = {
109
+ to: rule.to,
110
+ event: instance.event,
111
+ b
112
+ };
113
+ instance.o[b.change]?.(next);
114
+ return true;
115
+ }
116
+ return false;
117
+ },
118
+ moved(instance, from) {
119
+ for (const b of bounds(instance.M)) {
120
+ if (!b.yes || instance.o[b.name] !== void 0) continue;
121
+ const now = inside(instance.s, b.yes);
122
+ if (now !== inside(from, b.yes)) {
123
+ instance.props.m |= b.bit;
124
+ instance.o[b.change]?.(now);
125
+ }
126
+ }
127
+ },
128
+ own(instance, previous) {
129
+ for (const b of bounds(instance.M)) {
130
+ const value = instance.o[b.name];
131
+ if (value !== previous[b.name]) {
132
+ instance.props.m |= b.bit;
133
+ if (b.changed && value !== void 0) instance.dispatch({
134
+ type: b.changed,
135
+ value
136
+ });
137
+ }
138
+ if (!b.yes) continue;
139
+ const pending = instance.p?.b === b ? instance.p : void 0;
140
+ if (pending) instance.p = void 0;
141
+ if (value === void 0 || !!value === inside(instance.s, b.yes)) continue;
142
+ if (pending && inside(pending.to.lf, b.yes) === !!value) {
143
+ instance.event = pending.event;
144
+ instance.move(pending.to, void 0);
145
+ } else {
146
+ instance.event = PROPS;
147
+ instance.move(value ? b.yes : b.no, void 0);
148
+ }
149
+ }
150
+ }
151
+ };
152
+ //#endregion
153
+ //#region src/core/machine.ts
154
+ /**
155
+ * `defineMachine`: the config normalised once into tables (PLAN §5.2).
156
+ * Each state becomes a record linked to its parent, numbered depth first
157
+ * (so an ancestor's index is below its descendants'), with the leaf it
158
+ * settles in when entered, its entry/exit/effect lists, `after` timers,
159
+ * `always` rules and one `Map` from event name to what it does, its
160
+ * ancestors' rules folded in after its own (D20).
161
+ */
162
+ const STATE_KEYS = [
163
+ "initial",
164
+ "states",
165
+ "on",
166
+ "entry",
167
+ "exit",
168
+ "effect",
169
+ "after",
170
+ "always",
171
+ "type",
172
+ "done"
173
+ ];
174
+ const MACHINE_KEYS = [
175
+ "name",
176
+ "context",
177
+ "props",
178
+ "events",
179
+ "derived",
180
+ "actions",
181
+ "guards",
182
+ "effects",
183
+ "initial",
184
+ "states",
185
+ "on",
186
+ "parts",
187
+ "expose"
188
+ ];
189
+ const RULE_KEYS = [
190
+ "to",
191
+ "if",
192
+ "do"
193
+ ];
194
+ const list = (value) => value === void 0 || Array.isArray(value) ? value : [value];
195
+ const NOTHING = /* @__PURE__ */ new Map();
196
+ /** Config → tables: what `defineMachine` does at runtime, and the plugin at build time ({@link prepared}). */
197
+ function normalise(config) {
198
+ const name = CHECK ? config.name ?? "machine" : "";
199
+ const byPath = /* @__PURE__ */ new Map();
200
+ const states = [];
201
+ const root = {
202
+ i: -1,
203
+ path: ""
204
+ };
205
+ /** The raw config of `root`, then of each state. */
206
+ const raws = [config];
207
+ if (CHECK) checkKeys(config, MACHINE_KEYS, name, "the machine");
208
+ const where = (from) => from === root ? "the machine" : `"${from.path}"`;
209
+ const below = (up, key) => up === root ? key : `${up.path}.${key}`;
210
+ /** Dev only: the state and target being resolved, for `find`'s message; set before each call. */
211
+ let asked = [root, ""];
212
+ const find = (path) => {
213
+ const found = byPath.get(path);
214
+ if (CHECK && !found && asked[1] !== void 0) {
215
+ const [from, target] = asked;
216
+ const last = (p) => p.slice(p.lastIndexOf(".") + 1);
217
+ fail("UNKNOWN_TARGET", name, `${where(from)} → "${target}" does not exist${suggest(last(path), [...byPath.keys()].map(last)) || `; the states are ${[...byPath.keys()].map((p) => `"${p}"`).join(", ")}.`}`);
218
+ }
219
+ return found;
220
+ };
221
+ /**
222
+ * Named implementations (D8), in one map: the types keep each kind's
223
+ * names apart, and development rejects a name used in the wrong kind or
224
+ * declared in two.
225
+ */
226
+ const named = {
227
+ ...config.actions,
228
+ ...config.guards,
229
+ ...config.effects
230
+ };
231
+ if (CHECK) {
232
+ const seen = /* @__PURE__ */ new Set();
233
+ for (const kind of [
234
+ "actions",
235
+ "guards",
236
+ "effects"
237
+ ]) for (const key of Object.keys(config[kind] ?? {})) {
238
+ if (seen.has(key)) fail("NAME_CLASH", name, `"${key}" is declared in \`${kind}\` and in another of \`actions\`, \`guards\` and \`effects\`; give each its own name.`);
239
+ seen.add(key);
240
+ }
241
+ }
242
+ /** Functions as written, or the ones they name. */
243
+ const fns = (value) => value && list(value).map((f) => typeof f === "string" ? named[f] : f);
244
+ /** Dev only: each name in `value` is one of `config[kind]`. */
245
+ const known = (value, kind, from) => {
246
+ for (const f of list(value) ?? []) if (typeof f !== "function" && !config[kind]?.[f]) fail("UNKNOWN_NAME", name, `${where(from)} names "${String(f)}", which is not in \`${kind}\`${suggest(String(f), Object.keys(config[kind] ?? {})) || "."} Declare it in \`${kind}\`, or write the function in place.`);
247
+ };
248
+ /** Numbers `up`'s children depth first, then sets the leaf entering `up` settles in. */
249
+ const walk = (up, raw) => {
250
+ const children = raw.states;
251
+ if (children) {
252
+ for (const key of Object.keys(children)) {
253
+ const child = children[key];
254
+ const state = {
255
+ i: states.length,
256
+ path: below(up, key),
257
+ up,
258
+ fi: child.type === "final"
259
+ };
260
+ if (CHECK) {
261
+ if (/[.#]/.test(key)) fail("STATE_NAME", name, `state "${key}" has a "." or "#" in its name; targets use them as separators, so rename it.`);
262
+ checkKeys(child, STATE_KEYS, name, `state "${state.path}"`);
263
+ const type = child.type;
264
+ if (type !== void 0 && type !== "final" && typeof type !== "object") fail("BAD_TYPE", name, `"${state.path}" has \`type: ${JSON.stringify(type)}\`; a state's type is "final", or \`parallel\` imported from @menapo/machine (\`type: parallel\`).`);
265
+ }
266
+ byPath.set(state.path, state);
267
+ states.push(state);
268
+ raws.push(child);
269
+ walk(state, child);
270
+ }
271
+ if (CHECK && raw.initial === void 0 && typeof raw.type !== "object") fail("NO_INITIAL", name, `${up === root ? "the machine has no" : `"${up.path}" has child states but no`} \`initial\`; name one of: ${Object.keys(children).join(", ")}.`);
272
+ }
273
+ if (CHECK) asked = [up, raw.initial];
274
+ up.lf = children ? find(below(up, raw.initial))?.lf : up;
275
+ };
276
+ walk(root, config);
277
+ /** `#path` from the root, `.child` below `from`, else a sibling of `from` or of its nearest ancestor that has one. */
278
+ const resolve = (target, from) => {
279
+ if (CHECK) asked = [from, target];
280
+ if (target[0] === "#") return find(target.slice(1));
281
+ if (target[0] === ".") return find(below(from, target.slice(1)));
282
+ for (let up = from.up; up && up !== root; up = up.up) {
283
+ const found = byPath.get(`${up.path}.${target}`);
284
+ if (found) return found;
285
+ }
286
+ return find(target);
287
+ };
288
+ const rules = (transitions, from) => list(transitions).map((item) => {
289
+ if (CHECK && (typeof item !== "object" || item === null) && typeof item !== "string" && typeof item !== "function") fail("BAD_TRANSITION", name, `${where(from)} has a transition that is ${String(item)}; write a target name, an action, or { to, if, do }.`);
290
+ const rule = typeof item === "string" ? { to: item } : typeof item === "function" ? { do: item } : item;
291
+ if (CHECK) {
292
+ checkKeys(rule, RULE_KEYS, name, `a transition in ${where(from)}`);
293
+ known(rule.if, "guards", from);
294
+ known(rule.do, "actions", from);
295
+ if (rule.to === "") resolve("", from);
296
+ }
297
+ return {
298
+ to: rule.to && resolve(rule.to, from),
299
+ if: rule.if && fns(rule.if)[0],
300
+ do: fns(rule.do)
301
+ };
302
+ });
303
+ const handled = /* @__PURE__ */ new Set();
304
+ [root, ...states].forEach((state, k) => {
305
+ const raw = raws[k];
306
+ const up = state.up;
307
+ const inherited = up?.on ?? NOTHING;
308
+ let on = state.fi ? NOTHING : inherited;
309
+ if (raw.on && !state.fi) {
310
+ const folded = on = new Map(inherited);
311
+ for (const type of Object.keys(raw.on)) {
312
+ if (CHECK) {
313
+ const declared = config.events;
314
+ if (declared && !(type in declared)) fail("UNKNOWN_EVENT", name, `${where(state)} handles "${type}", which is not in \`events\`${suggest(type, Object.keys(declared)) || "."} Declare it in \`events\`.`);
315
+ handled.add(type);
316
+ }
317
+ const mine = rules(raw.on[type], state);
318
+ const outer = inherited.get(type);
319
+ folded.set(type, {
320
+ ev: outer?.ev ?? { type },
321
+ ru: outer ? mine.concat(outer.ru) : mine
322
+ });
323
+ }
324
+ }
325
+ if (CHECK) {
326
+ if (raw.done && raw.type?.kind !== "parallel") fail("DONE_KEY", name, `${where(state)} has \`done\`, which is taken when every region of a parallel state is final; give the state \`type: parallel\`, or use \`always\`.`);
327
+ }
328
+ if (CHECK && state.fi) {
329
+ const region = up !== root && (raws[up.up.i + 1]?.type)?.kind;
330
+ if (up !== root && region !== "parallel") fail("FINAL_STATE", name, `"${state.path}" is final but not top-level; a final state ends the machine at the top level, or a region of a parallel state, so move it or drop \`type: "final"\`.`);
331
+ for (const key of [
332
+ "states",
333
+ "on",
334
+ "effect",
335
+ "after",
336
+ "always"
337
+ ]) if (raw[key]) fail("FINAL_STATE", name, `final state "${state.path}" has \`${key}\`, but the machine stops there; move it to another state.`);
338
+ }
339
+ const always = raw.always && rules(raw.always, state);
340
+ const after = raw.after;
341
+ state.on = on;
342
+ if (CHECK) {
343
+ known(raw.entry, "actions", state);
344
+ known(raw.exit, "actions", state);
345
+ known(raw.effect, "effects", state);
346
+ }
347
+ state.en = fns(raw.entry);
348
+ state.ex = fns(raw.exit);
349
+ state.ef = fns(raw.effect);
350
+ state.af = after && Object.keys(after).map((ms) => {
351
+ if (CHECK && !(Number(ms) >= 0)) fail("BAD_DELAY", name, `"${state.path}" has \`after: { ${ms}: … }\`; a delay is a number of milliseconds.`);
352
+ return [+ms, rules(after[ms], state)];
353
+ });
354
+ state.al = always && up?.al ? always.concat(up.al) : always || up?.al;
355
+ });
356
+ if (CHECK) checkParts(config, name, handled);
357
+ const context = config.context ?? {};
358
+ const fields = Object.keys(context);
359
+ const defaults = Object.values(context);
360
+ if (CHECK) {
361
+ for (const value of defaults) if (Array.isArray(value) || Object.getPrototypeOf(value ?? 0) === Object.prototype) Object.freeze(value);
362
+ }
363
+ const props = config.props ?? {};
364
+ const propNames = Object.keys(props);
365
+ const Props = propNames.length ? storeClass(propNames, 1 + fields.length, false) : void 0;
366
+ const derived = config.derived;
367
+ const tables = {
368
+ ss: states,
369
+ i0: root.lf,
370
+ df: defaults,
371
+ C: fields.length ? storeClass(fields, 1, true) : void 0,
372
+ pn: propNames,
373
+ pd: [],
374
+ Q: Props,
375
+ pl: [],
376
+ nm: fields.concat(propNames),
377
+ D: derived && derivedClass(Object.keys(derived), Object.values(derived)),
378
+ k: void 0,
379
+ bd: [],
380
+ c: config,
381
+ r: normalise,
382
+ A: activate,
383
+ X: release,
384
+ P: own
385
+ };
386
+ if (CHECK) Object.assign(tables, {
387
+ name,
388
+ handled
389
+ });
390
+ bind(tables, props);
391
+ for (const raw of raws) raw.type?.f?.(tables, raws, rules);
392
+ return tables;
393
+ }
394
+ /**
395
+ * Defines a machine (PLAN §4.1): states, transitions, context, props and
396
+ * events in one object, normalised once into tables. Context, props,
397
+ * events, derived values, named implementations and the state tree are
398
+ * inferred from it.
399
+ */
400
+ const defineMachine = normalise;
401
+ /**
402
+ * The machine with some of its named `actions`, `guards` and `effects`
403
+ * replaced (D8), for tests or reuse; `machine` itself is unchanged. Each
404
+ * function is typed from the machine, as the ones it replaces.
405
+ */
406
+ function provide(machine, implementations) {
407
+ const tables = machine;
408
+ const config = { ...tables.c };
409
+ for (const kind of [
410
+ "actions",
411
+ "guards",
412
+ "effects"
413
+ ]) {
414
+ const given = implementations[kind];
415
+ if (DEV && given) {
416
+ for (const key of Object.keys(given)) if (!(key in (config[kind] ?? {}))) fail("UNKNOWN_NAME", config.name ?? "machine", `provide() was given ${kind}.${key}, which the machine does not declare${suggest(key, Object.keys(config[kind] ?? {})) || "."} Declare it in the machine's \`${kind}\` first.`);
417
+ }
418
+ config[kind] = {
419
+ ...config[kind],
420
+ ...given
421
+ };
422
+ }
423
+ return tables.r(config);
424
+ }
425
+ /** Declares an event's data in `events` (D7): `ADD: payload<{ by: number }>()`. */
426
+ const payload = () => null;
427
+ //#endregion
428
+ //#region src/features/marks.ts
429
+ /** The feature's mark on a raw state config, if it has one. */
430
+ const markOf = (raw) => typeof raw?.type === "object" ? raw.type : void 0;
431
+ //#endregion
432
+ //#region src/features/history.ts
433
+ /**
434
+ * `history()` (PLAN §4.9): a pseudo-state that stands for where its parent
435
+ * was when last exited. A transition to it enters that again: the parent's
436
+ * active child (shallow, its own initial states below it) or its active
437
+ * leaves (`history("deep")`); before the parent was ever exited, the
438
+ * parent's initial state. It is never active itself.
439
+ *
440
+ * Its mark carries what prepares a machine's tables, which the normaliser
441
+ * calls when it meets the mark: each history state is marked (`h`: deep or
442
+ * not) and checked; `resume.ts` wires them. Importing it has no effect. A
443
+ * machine with parallel states leaves both to `parallel`'s configurations,
444
+ * which read the same marker and the same records. A compiled machine (M7)
445
+ * has its tables prepared at build time, so it ships `resume.ts` alone.
446
+ */
447
+ /** The machines whose history states are prepared. */
448
+ const done = /* @__PURE__ */ new WeakSet();
449
+ /** Prepares a machine's history states, then wires them: what the marks carry. */
450
+ const prepare$1 = (tables, raws) => {
451
+ if (done.has(tables) || raws.some((raw) => markOf(raw)?.kind === "parallel")) return;
452
+ done.add(tables);
453
+ tables.ss.forEach((state, k) => {
454
+ const mark = markOf(raws[k + 1]);
455
+ if (mark?.kind !== "history") return;
456
+ const parent = state.up;
457
+ if (CHECK) {
458
+ if (parent.i < 0) fail("HISTORY", tables.name, `"${state.path}" is a history state of the machine itself, which is never exited; put it inside the state whose history it keeps.`);
459
+ if (`${parent.path}.${raws[parent.i + 1].initial}` === state.path) fail("HISTORY", tables.name, `"${parent.path}" starts in its history state "${state.path}"; make another state its \`initial\`.`);
460
+ }
461
+ state.h = !!mark.deep;
462
+ });
463
+ wireHistory(tables);
464
+ };
465
+ const SHALLOW = /* @__PURE__ */ Object.freeze({
466
+ kind: "history",
467
+ deep: false,
468
+ f: prepare$1
469
+ });
470
+ const DEEP = /* @__PURE__ */ Object.freeze({
471
+ kind: "history",
472
+ deep: true,
473
+ f: prepare$1
474
+ });
475
+ /**
476
+ * A history pseudo-state: `resume: history()` returns to the child its
477
+ * parent was last in, `history("deep")` to the very states inside it.
478
+ */
479
+ const history = (type) => ({ type: type === "deep" ? DEEP : SHALLOW });
480
+ //#endregion
481
+ //#region src/features/job.ts
482
+ /**
483
+ * Starts `run(signal)` for the state whose effect calls it and returns the
484
+ * cleanup that cancels it. `view` is the effect's view; `done` and
485
+ * `error` name events the machine declares.
486
+ */
487
+ function job(view, run, events) {
488
+ const controller = new AbortController();
489
+ let live = true;
490
+ const send = view.send;
491
+ const tell = (type, payload) => {
492
+ if (live && type) send(type, payload);
493
+ };
494
+ const work = run(controller.signal);
495
+ let stop;
496
+ if (typeof work === "function") stop = work;
497
+ else if (work && typeof work.stop === "function") {
498
+ const child = work;
499
+ stop = () => child.stop();
500
+ child.done.then(() => tell(events?.done, { data: child.context }));
501
+ } else if (work) work.then((data) => tell(events?.done, { data }), (error) => {
502
+ if (!live) return;
503
+ if (!events?.error) throw error;
504
+ tell(events.error, { error });
505
+ });
506
+ return (() => {
507
+ live = false;
508
+ controller.abort();
509
+ stop?.();
510
+ });
511
+ }
512
+ //#endregion
513
+ //#region src/features/parallel.ts
514
+ /**
515
+ * `type: parallel` (PLAN §4.9, D12): a state whose children, its regions,
516
+ * are all active at once. Only a machine that declares one ships this
517
+ * module: `parallel` is imported to be written, and the mark carries what
518
+ * prepares the machine's tables for the configurations of `chart.ts` (the
519
+ * regions, initial children and subtrees of each state, the state that
520
+ * declares each rule, a parallel state's `done`) and wires them, which the
521
+ * normaliser calls when it meets the mark. Importing it has no effect.
522
+ * A compiled machine (M7) has its tables prepared at build time, so it ships
523
+ * `chart.ts` without this module.
524
+ */
525
+ /** Prepares a machine with parallel states, then wires it: what the mark carries. */
526
+ const prepare = (tables, raws, rules) => {
527
+ const states = tables.ss;
528
+ const root = states[0].up;
529
+ if (root.k) return;
530
+ const all = [root, ...states];
531
+ const byPath = new Map(states.map((s) => [s.path, s]));
532
+ for (const s of all) {
533
+ const k = s.i + 1;
534
+ const mark = markOf(raws[k]);
535
+ const first = raws[k].initial;
536
+ s.k = [];
537
+ s.hs = [];
538
+ s.p = mark?.kind === "parallel";
539
+ s.h = mark?.kind === "history" ? !!mark.deep : void 0;
540
+ s.n = first === void 0 ? void 0 : byPath.get(s === root ? first : `${s.path}.${first}`);
541
+ for (let up = s; up; up = up.up) up.e = s.i;
542
+ if (s.up) (s.h === void 0 ? s.up.k : s.up.hs).push(s);
543
+ }
544
+ if (CHECK) {
545
+ for (const s of states) if (s.h !== void 0 && s.up.n === s) fail("HISTORY", tables.name, `"${s.up.path}" starts in its history state "${s.path}"; make another state its \`initial\`.`);
546
+ if (tables.bd.some((b) => b.yes)) fail("PARALLEL", tables.name, "a bindable prop follows states (`bindable(…, { states })`) in a machine with parallel states, which is not supported yet; bind the value to data and move with events.");
547
+ }
548
+ /** Marks the first `count` rules of `list` as declared by `s`. */
549
+ const own = (list, count, s) => {
550
+ for (let k = 0; k < count; k++) list[k].s = s;
551
+ };
552
+ for (const s of all) {
553
+ for (const [type, handler] of s.on) {
554
+ const outer = s.up?.on.get(type);
555
+ if (outer !== handler) own(handler.ru, handler.ru.length - (outer?.ru.length ?? 0), s);
556
+ }
557
+ if (s.al && s.al !== s.up?.al) own(s.al, s.al.length - (s.up?.al?.length ?? 0), s);
558
+ for (const [, list] of s.af ?? []) own(list, list.length, s);
559
+ const done = raws[s.i + 1]?.done;
560
+ if (done && s.p) {
561
+ const list = rules(done, s);
562
+ own(list, list.length, s);
563
+ s.dn = {
564
+ rules: list,
565
+ event: { type: DONE.type }
566
+ };
567
+ }
568
+ }
569
+ wireParallel(tables);
570
+ };
571
+ /** Marks a state whose regions are all active at once: `editor: { type: parallel, states: { … } }`. */
572
+ const parallel = /* @__PURE__ */ Object.freeze({
573
+ kind: "parallel",
574
+ f: prepare
575
+ });
576
+ //#endregion
577
+ //#region src/parts/use.ts
578
+ function useMachine() {
579
+ throw new Error("[machine:NO_PLUGIN] useMachine is resolved per framework by the Vite plugin; add machine() from @menapo/machine/vite to the Vite config's plugins. With it, reach useMachine from a module the plugin transforms (a script, a .vue or .svelte file), by import, re-export or import(), not through an object passed around.");
580
+ }
581
+ //#endregion
582
+ export { bindable, defineMachine, history, inspect, job, parallel, payload, provide, start, useMachine };
@@ -0,0 +1,74 @@
1
+ import { i as setHook } from "./instance-BM2UqeXW.js";
2
+ //#region src/inspect.ts
3
+ /**
4
+ * `inspect` (PLAN D17, §5.8): how DevTools and `/test` watch machines. Each
5
+ * event an instance takes becomes one step once it is handled: the event,
6
+ * the state after it, and the context fields and props it changed, with
7
+ * their new values.
8
+ *
9
+ * A step's changes are found by comparing each field with its value after
10
+ * the previous step, so a queued event that changes a field again reports it
11
+ * too. Only the last values are kept, never a snapshot per event (§9): with
12
+ * context changed in place, snapshots would cost memory on every event and
13
+ * still not make time travel safe.
14
+ *
15
+ * This module is the only one that sets the core's hook, so a bundle that
16
+ * never imports `inspect` (every production build: DevTools add it in dev
17
+ * only) drops the hook's calls along with it.
18
+ */
19
+ const PROPS = { type: "machine.props" };
20
+ let open = /* @__PURE__ */ new WeakMap();
21
+ const watchers = /* @__PURE__ */ new Set();
22
+ /** Context fields, then props, as the instance reads them (a controlled prop is the owner's). */
23
+ function values(instance) {
24
+ const { nm: names, pn: propNames } = instance.M;
25
+ const fields = names.length - propNames.length;
26
+ const context = instance.context;
27
+ const props = instance.props;
28
+ return names.map((name, n) => (n < fields ? context : props)[name]);
29
+ }
30
+ function close(instance, record) {
31
+ const event = record.event;
32
+ if (!event) return;
33
+ record.event = void 0;
34
+ const now = values(instance);
35
+ const changed = {};
36
+ now.forEach((value, n) => {
37
+ if (!Object.is(value, record.values[n])) changed[instance.M.nm[n]] = value;
38
+ });
39
+ record.values = now;
40
+ const step = {
41
+ instance,
42
+ event,
43
+ state: instance.s.path,
44
+ changed
45
+ };
46
+ for (const watch of watchers) watch(step);
47
+ }
48
+ /** The hook: an event begins (`null`: owner props), or, with none, the instance is done. */
49
+ function observe(instance, event) {
50
+ let record = open.get(instance);
51
+ if (!record) open.set(instance, record = {
52
+ values: values(instance),
53
+ event: void 0
54
+ });
55
+ else close(instance, record);
56
+ if (event !== void 0) record.event = event ?? PROPS;
57
+ }
58
+ /**
59
+ * Calls `watch` with a {@link Step} for every event any instance takes from
60
+ * now on, in order. Returns the function that stops watching.
61
+ */
62
+ function inspect(watch) {
63
+ watchers.add(watch);
64
+ setHook(observe);
65
+ return () => {
66
+ watchers.delete(watch);
67
+ if (!watchers.size) {
68
+ setHook(void 0);
69
+ open = /* @__PURE__ */ new WeakMap();
70
+ }
71
+ };
72
+ }
73
+ //#endregion
74
+ export { values as n, inspect as t };
@@ -0,0 +1,22 @@
1
+ import { g as Instance, n as AnyEvent } from "./types-Bk8svtO6.js";
2
+ //#region src/inspect.d.ts
3
+ /** One event an instance took, once handled. */
4
+ interface Step {
5
+ readonly instance: Instance;
6
+ /**
7
+ * `machine.start` first, `machine.after` for a timer, `machine.props` for
8
+ * new owner props; otherwise the event as sent.
9
+ */
10
+ readonly event: AnyEvent;
11
+ /** The active path after it. */
12
+ readonly state: string;
13
+ /** The context fields and props it changed, with their new values. */
14
+ readonly changed: Readonly<Record<string, unknown>>;
15
+ }
16
+ /**
17
+ * Calls `watch` with a {@link Step} for every event any instance takes from
18
+ * now on, in order. Returns the function that stops watching.
19
+ */
20
+ declare function inspect(watch: (step: Step) => void): () => void;
21
+ //#endregion
22
+ export { inspect as n, Step as t };