@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/LICENSE +21 -0
- package/README.md +135 -2
- package/dist/chart-BWjU2VF4.js +395 -0
- package/dist/compiled.d.ts +58 -0
- package/dist/compiled.js +43 -0
- package/dist/component-DDXOyYUI.d.ts +147 -0
- package/dist/dev-BAz6e3W-.js +79 -0
- package/dist/dom.d.ts +187 -0
- package/dist/dom.js +659 -0
- package/dist/glue/preact.d.ts +4 -0
- package/dist/glue/preact.js +54 -0
- package/dist/glue/react.d.ts +4 -0
- package/dist/glue/react.js +50 -0
- package/dist/glue/solid.d.ts +4 -0
- package/dist/glue/solid.js +42 -0
- package/dist/glue/svelte.svelte.d.ts +4 -0
- package/dist/glue/svelte.svelte.js +57 -0
- package/dist/glue/vanilla.d.ts +4 -0
- package/dist/glue/vanilla.js +83 -0
- package/dist/glue/vue.d.ts +4 -0
- package/dist/glue/vue.js +41 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +582 -0
- package/dist/inspect-BmyLUhcy.js +74 -0
- package/dist/inspect-C3qosuCm.d.ts +22 -0
- package/dist/instance-BM2UqeXW.js +418 -0
- package/dist/machine-BstWHtWL.d.ts +335 -0
- package/dist/naming-Csj5186j.js +492 -0
- package/dist/records-7FFepzo9.js +10 -0
- package/dist/source-DdFTPjID.js +15 -0
- package/dist/test.d.ts +63 -0
- package/dist/test.js +262 -0
- package/dist/types-Bk8svtO6.d.ts +580 -0
- package/dist/vicss.d.ts +19 -0
- package/dist/vicss.js +51 -0
- package/dist/vite.d.ts +27 -0
- package/dist/vite.js +4281 -0
- package/package.json +87 -3
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { r as fail } from "./dev-BAz6e3W-.js";
|
|
2
|
+
import { c as swap, f as DEV, n as hook, p as dev, t as Interpreter, u as bit } from "./instance-BM2UqeXW.js";
|
|
3
|
+
//#region src/parts/compile.ts
|
|
4
|
+
const HANDLER = /^on[A-Z]/;
|
|
5
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
/** Splits `machine.parts` for `naming`, once per machine and naming. */
|
|
7
|
+
function compile(machine, naming) {
|
|
8
|
+
let byNaming = cache.get(machine);
|
|
9
|
+
if (!byNaming) cache.set(machine, byNaming = /* @__PURE__ */ new Map());
|
|
10
|
+
let compiled = byNaming.get(naming);
|
|
11
|
+
if (compiled) return compiled;
|
|
12
|
+
const parts = [];
|
|
13
|
+
let size = 0;
|
|
14
|
+
const config = machine.c.parts ?? {};
|
|
15
|
+
for (const name of Object.keys(config)) {
|
|
16
|
+
const statics = [];
|
|
17
|
+
const keys = [];
|
|
18
|
+
const fns = [];
|
|
19
|
+
const on = [];
|
|
20
|
+
const part = config[name];
|
|
21
|
+
for (const key of Object.keys(part)) {
|
|
22
|
+
const value = part[key];
|
|
23
|
+
if (HANDLER.test(key)) on.push([naming(key), value]);
|
|
24
|
+
else if (typeof value === "function") {
|
|
25
|
+
keys.push(naming(key));
|
|
26
|
+
fns.push(value);
|
|
27
|
+
} else statics.push([naming(key), value]);
|
|
28
|
+
}
|
|
29
|
+
const template = Object.fromEntries(statics);
|
|
30
|
+
template.id = void 0;
|
|
31
|
+
template["data-scope"] = machine.c.name ?? "machine";
|
|
32
|
+
template["data-part"] = name;
|
|
33
|
+
template["data-state"] = void 0;
|
|
34
|
+
for (const key of keys) template[key] = void 0;
|
|
35
|
+
for (const [key] of on) template[key] = void 0;
|
|
36
|
+
parts.push({
|
|
37
|
+
name,
|
|
38
|
+
statics,
|
|
39
|
+
keys,
|
|
40
|
+
fns,
|
|
41
|
+
on,
|
|
42
|
+
at: size,
|
|
43
|
+
template
|
|
44
|
+
});
|
|
45
|
+
size += fns.length;
|
|
46
|
+
}
|
|
47
|
+
const record = Object.fromEntries(parts.map((part) => [part.name, void 0]));
|
|
48
|
+
byNaming.set(naming, compiled = {
|
|
49
|
+
parts,
|
|
50
|
+
size,
|
|
51
|
+
record
|
|
52
|
+
});
|
|
53
|
+
return compiled;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/parts/component.ts
|
|
57
|
+
/**
|
|
58
|
+
* A machine rendered by a framework (PLAN §4.4, §4.6, §5.4, §5.5): the
|
|
59
|
+
* interpreter plus its parts, ids, elements and `expose`, and the lifecycle
|
|
60
|
+
* the glue drives. Only the glue creates one, so a machine used without a
|
|
61
|
+
* framework (`start`) ships none of this.
|
|
62
|
+
*
|
|
63
|
+
* - **Parts** are rebuilt only when a field one of their attribute functions
|
|
64
|
+
* read has changed (the change mask, accumulated between renders), and
|
|
65
|
+
* only into a copy of the previous object, which keeps its shape, its
|
|
66
|
+
* static attributes and its handlers (S3: "a precompiled shape"). A part
|
|
67
|
+
* whose values came out the same returns the same object.
|
|
68
|
+
* - **Handlers** are made once per instance, so their identity never changes.
|
|
69
|
+
* - **The view version** (`snapshot`) moves only when a part, `expose` or
|
|
70
|
+
* what the component read through the view itself changed: React renders
|
|
71
|
+
* once per visible change and never for an unhandled event (S3).
|
|
72
|
+
* - **Effects and timers** start once the framework has committed the DOM
|
|
73
|
+
* (D13): never on a server, after the first commit, and after each commit
|
|
74
|
+
* that follows an event entering a state with effects.
|
|
75
|
+
*/
|
|
76
|
+
/** `instance.b`: the interpreter's IDLE, BUSY and STOPPED. */
|
|
77
|
+
const IDLE = 0;
|
|
78
|
+
const BUSY = 1;
|
|
79
|
+
const STOPPED = 2;
|
|
80
|
+
/**
|
|
81
|
+
* Elements by part, read from the document when asked: a class per machine.
|
|
82
|
+
* A part not in the document reads `undefined`, as does every part where
|
|
83
|
+
* there is no document (a server render).
|
|
84
|
+
*/
|
|
85
|
+
const elementClasses = /* @__PURE__ */ new WeakMap();
|
|
86
|
+
function elements(machine, ids) {
|
|
87
|
+
let Elements = elementClasses.get(machine);
|
|
88
|
+
if (!Elements) {
|
|
89
|
+
class Found {
|
|
90
|
+
$;
|
|
91
|
+
constructor(ids) {
|
|
92
|
+
this.$ = ids;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const part of Object.keys(machine.c.parts ?? {})) Object.defineProperty(Found.prototype, part, { get() {
|
|
96
|
+
return typeof document === "object" ? document.getElementById(this.$[part]) ?? void 0 : void 0;
|
|
97
|
+
} });
|
|
98
|
+
elementClasses.set(machine, Elements = Found);
|
|
99
|
+
}
|
|
100
|
+
return new Elements(ids);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A part's props object copied with its new state: the precompiled shape
|
|
104
|
+
* S3 asked for. Each of a machine's first eight parts is copied at a spread
|
|
105
|
+
* of its own, so V8 sees one shape at each and copies it as a block; one
|
|
106
|
+
* spread for every part sees them all and copies key by key, about three
|
|
107
|
+
* times slower (`bench/machine`'s `props.changed`).
|
|
108
|
+
*/
|
|
109
|
+
function copy(p, old, state) {
|
|
110
|
+
let next;
|
|
111
|
+
switch (p) {
|
|
112
|
+
case 0:
|
|
113
|
+
next = { ...old };
|
|
114
|
+
break;
|
|
115
|
+
case 1:
|
|
116
|
+
next = { ...old };
|
|
117
|
+
break;
|
|
118
|
+
case 2:
|
|
119
|
+
next = { ...old };
|
|
120
|
+
break;
|
|
121
|
+
case 3:
|
|
122
|
+
next = { ...old };
|
|
123
|
+
break;
|
|
124
|
+
case 4:
|
|
125
|
+
next = { ...old };
|
|
126
|
+
break;
|
|
127
|
+
case 5:
|
|
128
|
+
next = { ...old };
|
|
129
|
+
break;
|
|
130
|
+
case 6:
|
|
131
|
+
next = { ...old };
|
|
132
|
+
break;
|
|
133
|
+
case 7:
|
|
134
|
+
next = { ...old };
|
|
135
|
+
break;
|
|
136
|
+
default: next = { ...old };
|
|
137
|
+
}
|
|
138
|
+
next["data-state"] = state;
|
|
139
|
+
return next;
|
|
140
|
+
}
|
|
141
|
+
/** The first listener of every component: collects what changed between renders. */
|
|
142
|
+
function collect(instance) {
|
|
143
|
+
instance.cc |= instance.mask;
|
|
144
|
+
}
|
|
145
|
+
/** True when `at` is the leaf or one of its ancestors, or one of a parallel configuration's states. */
|
|
146
|
+
function active(leaf, at) {
|
|
147
|
+
if (leaf.a) return leaf.a.includes(at);
|
|
148
|
+
for (let s = leaf; s; s = s.up) if (s === at) return true;
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* What `useMachine` returns: `state`, `is`, `send`, `context`, `parts`, and
|
|
153
|
+
* a getter for each value `expose` returns (a class per machine adds them).
|
|
154
|
+
* Every getter reads the component's current snapshot, after the
|
|
155
|
+
* framework's tracker (`t`: Vue, Svelte and Solid record the read; React
|
|
156
|
+
* and Preact have none). React and Preact get a new one per view version,
|
|
157
|
+
* since they compare by identity (React's Compiler too); the others keep
|
|
158
|
+
* one for the component's life. Reading `state`, `is` or `context` marks
|
|
159
|
+
* them watched, so a change to them renders even when no part shows it.
|
|
160
|
+
*/
|
|
161
|
+
var View = class {
|
|
162
|
+
$;
|
|
163
|
+
t;
|
|
164
|
+
u;
|
|
165
|
+
constructor(component, track, listen) {
|
|
166
|
+
this.$ = component;
|
|
167
|
+
this.t = track;
|
|
168
|
+
this.u = listen;
|
|
169
|
+
}
|
|
170
|
+
get parts() {
|
|
171
|
+
this.t?.();
|
|
172
|
+
return this.$.record();
|
|
173
|
+
}
|
|
174
|
+
get state() {
|
|
175
|
+
this.t?.();
|
|
176
|
+
this.$.wa |= 1;
|
|
177
|
+
return this.$.s.path;
|
|
178
|
+
}
|
|
179
|
+
get is() {
|
|
180
|
+
this.t?.();
|
|
181
|
+
this.$.wa |= 1;
|
|
182
|
+
return this.$.is;
|
|
183
|
+
}
|
|
184
|
+
get context() {
|
|
185
|
+
this.t?.();
|
|
186
|
+
const { $ } = this;
|
|
187
|
+
$.wa |= bit($.M.nm.length - $.M.pn.length) - 1 << 1;
|
|
188
|
+
return $.context;
|
|
189
|
+
}
|
|
190
|
+
get send() {
|
|
191
|
+
return this.$.send;
|
|
192
|
+
}
|
|
193
|
+
setProps(props) {
|
|
194
|
+
this.$.setProps(props);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Stops the instance as an unmount does: its effects and timers, and in
|
|
198
|
+
* plain code its listeners. A framework's glue does it when the component
|
|
199
|
+
* goes; plain code calls it when the element goes.
|
|
200
|
+
*/
|
|
201
|
+
stop() {
|
|
202
|
+
if (this.u) this.u.stop();
|
|
203
|
+
else this.$.unmount();
|
|
204
|
+
}
|
|
205
|
+
/** Calls `listener` after each visible change. */
|
|
206
|
+
subscribe(listener) {
|
|
207
|
+
const { $, u } = this;
|
|
208
|
+
if (u) return u.subscribe(listener);
|
|
209
|
+
let seen = $.snapshot();
|
|
210
|
+
return $.subscribe(() => {
|
|
211
|
+
const now = $.snapshot();
|
|
212
|
+
if (now !== seen) {
|
|
213
|
+
seen = now;
|
|
214
|
+
listener();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const viewClasses = /* @__PURE__ */ new WeakMap();
|
|
220
|
+
/** A view of `component`: {@link View}, with a getter for each key `expose` returns, on a class made once per machine. */
|
|
221
|
+
function viewOf(component, track, listen) {
|
|
222
|
+
let Of = viewClasses.get(component.M);
|
|
223
|
+
if (!Of) {
|
|
224
|
+
Of = class extends View {};
|
|
225
|
+
for (const key of Object.keys(component.exposed() ?? {})) Object.defineProperty(Of.prototype, key, {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
get() {
|
|
228
|
+
this.t?.();
|
|
229
|
+
this.$.snapshot();
|
|
230
|
+
return this.$.exposed()[key];
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
viewClasses.set(component.M, Of);
|
|
234
|
+
}
|
|
235
|
+
return new Of(component, track, listen);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Dev only: props given as a function where the framework reads them as an
|
|
239
|
+
* object on each render (React, Preact, plain code), which would ignore them.
|
|
240
|
+
*/
|
|
241
|
+
function objectProps(machine, props, framework) {
|
|
242
|
+
if (typeof props === "function") fail("PROPS", machine.c.name ?? "machine", `useMachine was given props as a function, which only Vue, Svelte and Solid read; in ${framework} pass the props object itself, on each render: useMachine(machine, { … }).`);
|
|
243
|
+
}
|
|
244
|
+
/** What the glue's adapters create: a component of `machine`, named for the framework. */
|
|
245
|
+
const component = (machine, naming) => (id, owner) => new Component(machine, owner, id, naming);
|
|
246
|
+
var Component = class extends Interpreter {
|
|
247
|
+
/** The instance id: the framework's (`useId`), or the owner's `id`. */
|
|
248
|
+
id;
|
|
249
|
+
/** The framework's attribute names. */
|
|
250
|
+
nm;
|
|
251
|
+
/** Bits changed since the last snapshot. */
|
|
252
|
+
cc = 0;
|
|
253
|
+
/** Bits the component read through its view (`state`, `is`, `context`). */
|
|
254
|
+
wa = 0;
|
|
255
|
+
/** The view version. */
|
|
256
|
+
pv = 0;
|
|
257
|
+
/** 1 while mounted: effects may start. */
|
|
258
|
+
y = 0;
|
|
259
|
+
/** 1 when effects wait for a commit, which a render must bring. */
|
|
260
|
+
fx = 0;
|
|
261
|
+
/** Effects deferred until the next commit. */
|
|
262
|
+
w;
|
|
263
|
+
/** Parts, once rendered. */
|
|
264
|
+
rd;
|
|
265
|
+
/** `expose`'s last result and the bits it read. */
|
|
266
|
+
xo;
|
|
267
|
+
xr = 0;
|
|
268
|
+
ir;
|
|
269
|
+
er;
|
|
270
|
+
constructor(machine, owner, id, naming) {
|
|
271
|
+
super(machine, void 0, owner);
|
|
272
|
+
this.id = owner?.id ?? id;
|
|
273
|
+
this.nm = naming;
|
|
274
|
+
this.l = [collect];
|
|
275
|
+
}
|
|
276
|
+
/** `data-scope`: the machine's name. */
|
|
277
|
+
get scope() {
|
|
278
|
+
return this.M.c.name ?? "machine";
|
|
279
|
+
}
|
|
280
|
+
/** `ids.<part>`: `<name>:<id>:<part>`, stable for the instance and the same on server and client. */
|
|
281
|
+
get ids() {
|
|
282
|
+
if (!this.ir) {
|
|
283
|
+
const ids = {};
|
|
284
|
+
const prefix = `${this.scope}:${this.id}:`;
|
|
285
|
+
for (const part of Object.keys(this.M.c.parts ?? {})) ids[part] = prefix + part;
|
|
286
|
+
this.ir = ids;
|
|
287
|
+
}
|
|
288
|
+
return this.ir;
|
|
289
|
+
}
|
|
290
|
+
/** `el.<part>`: the part's element, found by its id when read. */
|
|
291
|
+
get el() {
|
|
292
|
+
return this.er ??= elements(this.M, this.ids);
|
|
293
|
+
}
|
|
294
|
+
activate(states) {
|
|
295
|
+
if (this.y) {
|
|
296
|
+
(this.w ??= []).push(...states);
|
|
297
|
+
this.fx = 1;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
exit(at) {
|
|
301
|
+
const w = this.w;
|
|
302
|
+
if (w) {
|
|
303
|
+
const k = w.indexOf(at);
|
|
304
|
+
if (k >= 0) w.splice(k, 1);
|
|
305
|
+
}
|
|
306
|
+
super.exit(at);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Starts what `states` run as one step (D3): what an effect sends waits
|
|
310
|
+
* until every effect has started, then runs to completion and is told,
|
|
311
|
+
* as when the core starts them. Inside a listener (the vanilla glue
|
|
312
|
+
* commits there) the event being handled does that.
|
|
313
|
+
*/
|
|
314
|
+
begin(states) {
|
|
315
|
+
if (!states.length || this.b === STOPPED) return;
|
|
316
|
+
if (this.b === BUSY) return super.activate(states);
|
|
317
|
+
this.b = BUSY;
|
|
318
|
+
try {
|
|
319
|
+
super.activate(states);
|
|
320
|
+
this.settle();
|
|
321
|
+
hook?.(this);
|
|
322
|
+
} finally {
|
|
323
|
+
if (this.b === BUSY) {
|
|
324
|
+
this.b = IDLE;
|
|
325
|
+
this.q = this.n = void 0;
|
|
326
|
+
if (DEV) dev.acting = 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/** After the first commit (and again after StrictMode's remount): starts what the active states run. */
|
|
331
|
+
mount() {
|
|
332
|
+
if (this.b === STOPPED && !this.s.fi) this.b = IDLE;
|
|
333
|
+
this.y = 1;
|
|
334
|
+
const chain = [];
|
|
335
|
+
const all = this.s.a;
|
|
336
|
+
if (all) {
|
|
337
|
+
for (const at of all) if (at.af || at.ef) chain.push(at);
|
|
338
|
+
} else for (let at = this.s; at; at = at.up) if (at.af || at.ef) chain.unshift(at);
|
|
339
|
+
this.w = void 0;
|
|
340
|
+
this.begin(chain);
|
|
341
|
+
}
|
|
342
|
+
unmount() {
|
|
343
|
+
this.y = 0;
|
|
344
|
+
this.stop();
|
|
345
|
+
}
|
|
346
|
+
/** After a commit: starts the timers and effects of states entered since, that are still active. */
|
|
347
|
+
commit() {
|
|
348
|
+
const w = this.w;
|
|
349
|
+
if (w && this.y) {
|
|
350
|
+
this.w = void 0;
|
|
351
|
+
this.begin(w.filter((at) => active(this.s, at)));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The view version: brings parts and `expose` up to date, and moves only
|
|
356
|
+
* when something shown changed. Before the first render it does nothing,
|
|
357
|
+
* so creating a component builds no parts.
|
|
358
|
+
*/
|
|
359
|
+
snapshot() {
|
|
360
|
+
const changed = this.cc;
|
|
361
|
+
if ((changed || this.fx) && (this.rd || this.xo)) {
|
|
362
|
+
this.cc = 0;
|
|
363
|
+
let moved = this.fx || changed & this.wa;
|
|
364
|
+
this.fx = 0;
|
|
365
|
+
const rd = this.rd;
|
|
366
|
+
if (rd) {
|
|
367
|
+
const { po, pr, pa } = rd;
|
|
368
|
+
const parts = rd.pc.parts;
|
|
369
|
+
const path = this.s.path;
|
|
370
|
+
for (let p = 0; p < parts.length; p++) {
|
|
371
|
+
if (!(pr[p] & changed)) continue;
|
|
372
|
+
const { keys, fns, at } = parts[p];
|
|
373
|
+
const outer = swap(0);
|
|
374
|
+
let dirty = false;
|
|
375
|
+
for (let k = 0; k < fns.length; k++) {
|
|
376
|
+
const value = fns[k](this);
|
|
377
|
+
if (value !== pa[at + k]) {
|
|
378
|
+
pa[at + k] = value;
|
|
379
|
+
dirty = true;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
pr[p] = swap(outer) | 1;
|
|
383
|
+
const old = po[p];
|
|
384
|
+
if (!dirty && old["data-state"] === path) continue;
|
|
385
|
+
const next = copy(p, old, path);
|
|
386
|
+
for (let k = 0; k < keys.length; k++) next[keys[k]] = pa[at + k];
|
|
387
|
+
po[p] = next;
|
|
388
|
+
moved = 1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (this.xr & changed) {
|
|
392
|
+
this.exposed(true);
|
|
393
|
+
moved = 1;
|
|
394
|
+
}
|
|
395
|
+
if (moved) this.pv++;
|
|
396
|
+
}
|
|
397
|
+
return this.pv;
|
|
398
|
+
}
|
|
399
|
+
/** Renders every part the first time: each from its compiled template, then the instance's values. */
|
|
400
|
+
render() {
|
|
401
|
+
this.cc = this.fx = 0;
|
|
402
|
+
const pc = compile(this.M, this.nm);
|
|
403
|
+
this.exposed(true);
|
|
404
|
+
const rd = {
|
|
405
|
+
pc,
|
|
406
|
+
po: Array.from({ length: pc.parts.length }),
|
|
407
|
+
pr: Array.from({ length: pc.parts.length }),
|
|
408
|
+
pa: Array.from({ length: pc.size }),
|
|
409
|
+
rec: void 0,
|
|
410
|
+
rv: -1,
|
|
411
|
+
vo: void 0,
|
|
412
|
+
vv: -1
|
|
413
|
+
};
|
|
414
|
+
const path = this.s.path;
|
|
415
|
+
const ids = this.ids;
|
|
416
|
+
for (let p = 0; p < pc.parts.length; p++) {
|
|
417
|
+
const { name, keys, fns, at, on, template } = pc.parts[p];
|
|
418
|
+
const outer = swap(0);
|
|
419
|
+
for (let k = 0; k < fns.length; k++) rd.pa[at + k] = fns[k](this);
|
|
420
|
+
rd.pr[p] = swap(outer) | 1;
|
|
421
|
+
const object = copy(p, template, path);
|
|
422
|
+
object.id = ids[name];
|
|
423
|
+
for (let k = 0; k < keys.length; k++) object[keys[k]] = rd.pa[at + k];
|
|
424
|
+
for (const [key, handler] of on) object[key] = typeof handler === "string" ? () => this.dispatch(handler) : (event) => handler(this, event);
|
|
425
|
+
rd.po[p] = object;
|
|
426
|
+
}
|
|
427
|
+
return this.rd = rd;
|
|
428
|
+
}
|
|
429
|
+
/** What `expose` returns, computed again when `again` or not yet. */
|
|
430
|
+
exposed(again) {
|
|
431
|
+
const expose = this.M.c.expose;
|
|
432
|
+
if (expose && (again || !this.xo)) {
|
|
433
|
+
const outer = swap(0);
|
|
434
|
+
this.xo = expose(this);
|
|
435
|
+
this.xr = swap(outer);
|
|
436
|
+
if (DEV) {
|
|
437
|
+
for (const key of Object.keys(this.xo ?? {})) if (key in View.prototype || key === "$" || key === "t" || key === "u") fail("EXPOSE_KEY", this.M.name, `expose returns "${key}", which useMachine's result has already; rename it.`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return this.xo;
|
|
441
|
+
}
|
|
442
|
+
/** Every part's props object, by part: the same record until one changes. */
|
|
443
|
+
record() {
|
|
444
|
+
const pv = this.snapshot();
|
|
445
|
+
const rd = this.rd ?? this.render();
|
|
446
|
+
if (rd.rv !== pv || !rd.rec) {
|
|
447
|
+
const { parts, record } = rd.pc;
|
|
448
|
+
const out = { ...record };
|
|
449
|
+
for (let p = 0; p < parts.length; p++) out[parts[p].name] = rd.po[p];
|
|
450
|
+
rd.rec = out;
|
|
451
|
+
rd.rv = pv;
|
|
452
|
+
}
|
|
453
|
+
return rd.rec;
|
|
454
|
+
}
|
|
455
|
+
/** The view for the current version, for frameworks that compare by identity: a new one only when something shown changed. */
|
|
456
|
+
view() {
|
|
457
|
+
const pv = this.snapshot();
|
|
458
|
+
const rd = this.rd ?? this.render();
|
|
459
|
+
if (rd.vv !== pv || !rd.vo) {
|
|
460
|
+
rd.vo = viewOf(this);
|
|
461
|
+
rd.vv = pv;
|
|
462
|
+
}
|
|
463
|
+
return rd.vo;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region ../glue/src/naming.ts
|
|
468
|
+
/** React's names whose DOM name is not their lowercase form. */
|
|
469
|
+
const DOM = {
|
|
470
|
+
htmlFor: "for",
|
|
471
|
+
className: "class",
|
|
472
|
+
onDoubleClick: "onDblClick"
|
|
473
|
+
};
|
|
474
|
+
/** React's name as the DOM spells it, still camel-cased: `htmlFor` is `for`, `onDoubleClick` `onDblClick`. */
|
|
475
|
+
const dom = (key) => DOM[key] ?? key;
|
|
476
|
+
/** React: names as written. */
|
|
477
|
+
const react = (key) => key;
|
|
478
|
+
/** Preact and Solid take React's names but the DOM's attributes, and lowercase an event's name themselves. */
|
|
479
|
+
const jsx = dom;
|
|
480
|
+
/** Svelte and the DOM itself: lowercase (`onclick`, `tabindex`, `for`). */
|
|
481
|
+
const lower = (key) => dom(key).toLowerCase();
|
|
482
|
+
/**
|
|
483
|
+
* Vue derives an event's name from the rest of an `on…` key, hyphenating
|
|
484
|
+
* capitals (`onKeyDown` would listen for `key-down`), so a handler is
|
|
485
|
+
* `onKeydown`; other names stay, but for the DOM's attributes.
|
|
486
|
+
*/
|
|
487
|
+
const vue = (key) => {
|
|
488
|
+
const name = dom(key);
|
|
489
|
+
return /^on[A-Z]/.test(name) ? name.slice(0, 3) + name.slice(3).toLowerCase() : name;
|
|
490
|
+
};
|
|
491
|
+
//#endregion
|
|
492
|
+
export { component as a, vue as i, lower as n, objectProps as o, react as r, viewOf as s, jsx as t };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/features/records.ts
|
|
2
|
+
const records = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
const recorded = (instance, history) => records.get(instance)?.get(history);
|
|
4
|
+
function record(instance, history, states) {
|
|
5
|
+
let map = records.get(instance);
|
|
6
|
+
if (!map) records.set(instance, map = /* @__PURE__ */ new Map());
|
|
7
|
+
map.set(history, states);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { recorded as n, records as r, record as t };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region ../glue/src/source.ts
|
|
2
|
+
/** Equal own values: a render passing equal props does not set them again. */
|
|
3
|
+
function same(a, b) {
|
|
4
|
+
if (a === b) return true;
|
|
5
|
+
if (!a || !b) return false;
|
|
6
|
+
const keys = Object.keys(a);
|
|
7
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
8
|
+
for (const key of keys) if (!Object.is(a[key], b[key])) return false;
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
let ids = 0;
|
|
12
|
+
/** An id for frameworks that give none (Svelte, vanilla): unique on the page, not across a server and its client. */
|
|
13
|
+
const nextId = () => `m${++ids}`;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { same as n, nextId as t };
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { _ as InstanceOf, i as AnyMachine, k as StartOptions, n as AnyEvent, u as EventOf } from "./types-Bk8svtO6.js";
|
|
2
|
+
import { t as Step } from "./inspect-C3qosuCm.js";
|
|
3
|
+
//#region src/test/machine.d.ts
|
|
4
|
+
/** What a test machine adds to the instance. */
|
|
5
|
+
interface TestControls {
|
|
6
|
+
/** Moves its clock forward by `ms`, firing the timers due, in time order. */
|
|
7
|
+
advance(ms: number): void;
|
|
8
|
+
/** Fires every pending timer, and those they set, in time order, until none is left. */
|
|
9
|
+
runAll(): void;
|
|
10
|
+
/** Timers set on its clock and not yet fired or cleared. */
|
|
11
|
+
readonly pending: number;
|
|
12
|
+
/** Every event it took, from `machine.start`, with the state after it and what it changed. */
|
|
13
|
+
readonly steps: readonly Step[];
|
|
14
|
+
/** The states it settled in, from the initial one; a state repeated in a row counts once. */
|
|
15
|
+
readonly path: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* Sends each event in turn; `machine.after` fires the next pending timer
|
|
18
|
+
* instead. A path from `paths()` replays this way.
|
|
19
|
+
*/
|
|
20
|
+
play(events: readonly AnyEvent[]): void;
|
|
21
|
+
}
|
|
22
|
+
type TestMachine<M extends AnyMachine> = InstanceOf<M> & TestControls;
|
|
23
|
+
/**
|
|
24
|
+
* Starts `machine` for a test: events run synchronously, timers wait for
|
|
25
|
+
* `advance` or `runAll`, and every event taken is recorded in `steps` and
|
|
26
|
+
* `path`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function testMachine<M extends AnyMachine>(machine: M, options?: StartOptions<M>): TestMachine<M>;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/test/paths.d.ts
|
|
31
|
+
type EventsOf<M extends AnyMachine> = NonNullable<M["types"]>["events"];
|
|
32
|
+
interface PathOptions<M extends AnyMachine> extends StartOptions<M> {
|
|
33
|
+
/** Events in a path at most; 6 when left out. */
|
|
34
|
+
readonly depth?: number;
|
|
35
|
+
/** Paths at most; finding more is an error that says what to narrow. 1000 when left out. */
|
|
36
|
+
readonly limit?: number;
|
|
37
|
+
/**
|
|
38
|
+
* The data to try for events that carry some: each entry is sent as one
|
|
39
|
+
* event (`ADD: [{ by: 1 }, { by: -1 }]`). An event not listed is sent
|
|
40
|
+
* without data.
|
|
41
|
+
*/
|
|
42
|
+
readonly events?: { readonly [K in keyof EventsOf<M> & string]?: readonly Omit<EventOf<EventsOf<M>, K>, "type">[]; };
|
|
43
|
+
}
|
|
44
|
+
interface Path {
|
|
45
|
+
/** The events, in order; `machine.after` stands for the next timer firing. */
|
|
46
|
+
readonly events: readonly AnyEvent[];
|
|
47
|
+
/** The state before the first event and after each one. */
|
|
48
|
+
readonly states: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Every path from the initial state that passes no state twice, up to
|
|
52
|
+
* `depth` events. A path ends when it reaches a final state, a state where
|
|
53
|
+
* nothing it can send does anything, `depth`, or a state with context it
|
|
54
|
+
* passed through before; the step back is part of the path, so every
|
|
55
|
+
* transition taken is covered. Events are those the current state handles,
|
|
56
|
+
* plus `machine.after` while a timer is pending. A state is its path and
|
|
57
|
+
* its context and props, compared as JSON, and what its history states
|
|
58
|
+
* recorded. In a parallel configuration the state is every active leaf, and
|
|
59
|
+
* the events tried are those any active state handles.
|
|
60
|
+
*/
|
|
61
|
+
export declare function paths<M extends AnyMachine>(machine: M, options?: PathOptions<M>): Path[];
|
|
62
|
+
//#endregion
|
|
63
|
+
export type { Path, PathOptions, Step, TestControls, TestMachine };
|