@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/vite.js ADDED
@@ -0,0 +1,4281 @@
1
+ import { createRequire } from "node:module";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { parseSync } from "vite";
5
+ import { existsSync, readFileSync, statSync } from "node:fs";
6
+ //#region ../glue/src/detect.ts
7
+ /**
8
+ * Which framework a module renders with (machine PLAN §5.5), the way vicss
9
+ * decides it: `.vue` and `.svelte` (with `.svelte.js`/`.svelte.ts` rune
10
+ * modules) by extension; JSX by its `@jsxImportSource` pragma, else the
11
+ * project's JSX framework ({@link jsxFrameworkOf}); plain scripts by the
12
+ * plugin's `framework` option, else the framework the root `package.json`
13
+ * depends on, else none (vanilla). Plain data in and out, so it is
14
+ * unit-tested without Vite.
15
+ *
16
+ * vi18n has a detection of its own (its `vite/glue/detect.ts`, by plugin
17
+ * names); it is published, so moving it onto this module waits for its
18
+ * next minor (machine PLAN §11).
19
+ */
20
+ /** Root dependencies that name a project's framework, the first found winning. */
21
+ const DEPENDENCIES = [
22
+ ["vue", "vue"],
23
+ ["svelte", "svelte"],
24
+ ["solid-js", "solid"],
25
+ ["preact", "preact"],
26
+ ["react", "react"]
27
+ ];
28
+ /** `preact`, `solid-js` and `vue` (and their subpaths) by name; any other JSX source takes React's. */
29
+ function jsxFramework(source) {
30
+ const name = source.split("/")[0];
31
+ if (name === "preact") return "preact";
32
+ if (name === "solid-js") return "solid";
33
+ if (name === "vue") return "vue";
34
+ return "react";
35
+ }
36
+ /** The framework of module `id` with source `code`. */
37
+ function frameworkOf(id, code, settings) {
38
+ const file = id.replace(/[?#].*$/, "").toLowerCase();
39
+ if (file.endsWith(".vue")) return "vue";
40
+ if (file.endsWith(".svelte") || /\.svelte\.[cm]?[jt]s$/.test(file)) return "svelte";
41
+ if (/\.[jt]sx$/.test(file)) {
42
+ const pragma = /@jsxImportSource\s+([^\s*]+)/.exec(code)?.[1];
43
+ return pragma ? jsxFramework(pragma) : settings.jsx;
44
+ }
45
+ return settings.scripts;
46
+ }
47
+ /** The names a `package.json` depends on, if it can be read. */
48
+ function dependencies(root) {
49
+ const file = join(root, "package.json");
50
+ if (!existsSync(file)) return void 0;
51
+ try {
52
+ const pkg = JSON.parse(readFileSync(file, "utf8"));
53
+ return /* @__PURE__ */ new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]);
54
+ } catch {
55
+ return;
56
+ }
57
+ }
58
+ /** The framework the `package.json` in `root` depends on, if a known one. */
59
+ function rootFramework(root) {
60
+ const names = dependencies(root);
61
+ return names && DEPENDENCIES.find(([name]) => names.has(name))?.[1];
62
+ }
63
+ /**
64
+ * JSX frameworks by the names of the Vite plugins that compile them, as
65
+ * vi18n recognises them: a prefix up to a separator, or the exact name.
66
+ */
67
+ const JSX_PLUGINS = [
68
+ ["react", /^(?:vite:react|@vitejs\/plugin-react)(?:[:/-]|$)/],
69
+ ["preact", /^(?:preact:|vite:preact(?:[:/-]|$)|@preact\/|prefresh(?:[:/-]|$)|preact$)/],
70
+ ["solid", /^(?:solid:|vite-plugin-solid(?:[:/-]|$)|solid-start(?:[:/-]|$)|solid$)/],
71
+ ["vue", /^(?:vite:vue-jsx|@vitejs\/plugin-vue-jsx)(?:[:/-]|$)/]
72
+ ];
73
+ /** The one JSX framework whose Vite plugin is in `plugins`; none when there are none or several. */
74
+ function pluginFramework(plugins) {
75
+ const found = /* @__PURE__ */ new Set();
76
+ for (const plugin of plugins) {
77
+ const name = plugin?.name;
78
+ if (typeof name !== "string") continue;
79
+ for (const [framework, pattern] of JSX_PLUGINS) if (pattern.test(name)) found.add(framework);
80
+ }
81
+ return found.size === 1 ? [...found][0] : void 0;
82
+ }
83
+ /** tsconfig's JSON with comments and trailing commas, as TypeScript reads it. */
84
+ function parseJsonc(text) {
85
+ let out = "";
86
+ for (let i = 0; i < text.length; i++) {
87
+ const c = text[i];
88
+ if (c === "\"") {
89
+ const end = /"(?:[^"\\]|\\.)*"/y;
90
+ end.lastIndex = i;
91
+ const string = end.exec(text)?.[0] ?? c;
92
+ out += string;
93
+ i += string.length - 1;
94
+ } else if (c === "/" && text[i + 1] === "/") {
95
+ while (i < text.length && text[i] !== "\n") i++;
96
+ out += "\n";
97
+ } else if (c === "/" && text[i + 1] === "*") {
98
+ const end = text.indexOf("*/", i + 2);
99
+ i = end < 0 ? text.length : end + 1;
100
+ } else out += c;
101
+ }
102
+ return JSON.parse(out.replace(/,(\s*[}\]])/g, "$1"));
103
+ }
104
+ /** A tsconfig named by `extends` or a reference: a file, a directory's `tsconfig.json`, or a package's. */
105
+ function tsconfigPath(from, name) {
106
+ const local = name.startsWith(".") || name.startsWith("/");
107
+ let path;
108
+ try {
109
+ path = local ? resolve(dirname(from), name) : createRequire(from).resolve(name);
110
+ } catch {
111
+ return;
112
+ }
113
+ if (existsSync(path) && statSync(path).isDirectory()) path = join(path, "tsconfig.json");
114
+ else if (!existsSync(path) && existsSync(`${path}.json`)) path = `${path}.json`;
115
+ return existsSync(path) ? path : void 0;
116
+ }
117
+ /**
118
+ * `compilerOptions.jsxImportSource` as TypeScript resolves it for `file`:
119
+ * its own, else its `extends`' (the last one first); then, for a solution
120
+ * file (`references`, as create-vite writes), the first referenced project
121
+ * that sets one.
122
+ */
123
+ function tsconfigJsxSource(file, seen = /* @__PURE__ */ new Set()) {
124
+ if (seen.has(file) || !existsSync(file)) return void 0;
125
+ seen.add(file);
126
+ let config;
127
+ try {
128
+ config = parseJsonc(readFileSync(file, "utf8"));
129
+ } catch {
130
+ return;
131
+ }
132
+ const own = config.compilerOptions?.jsxImportSource;
133
+ if (typeof own === "string") return own;
134
+ const bases = typeof config.extends === "string" ? [config.extends] : config.extends ?? [];
135
+ for (const base of [...bases].reverse()) {
136
+ const path = tsconfigPath(file, base);
137
+ const found = path && tsconfigJsxSource(path, seen);
138
+ if (found) return found;
139
+ }
140
+ for (const reference of config.references ?? []) {
141
+ const path = tsconfigPath(file, reference.path);
142
+ const found = path && tsconfigJsxSource(path, seen);
143
+ if (found) return found;
144
+ }
145
+ }
146
+ /**
147
+ * The framework of JSX without a pragma: the JSX import source the Vite
148
+ * config gives Oxc or esbuild; else the one JSX framework whose Vite plugin
149
+ * is installed (`vite-plugin-solid`, the Preact preset); else the root
150
+ * tsconfig's `jsxImportSource`, through `extends` and `references`; else
151
+ * Preact, Solid or Vue when the root `package.json` depends on it; else React.
152
+ */
153
+ function jsxFrameworkOf(config, root) {
154
+ const oxc = config.oxc;
155
+ const esbuild = config.esbuild;
156
+ const fromVite = (oxc && oxc.jsx?.importSource) ?? (esbuild && esbuild.jsxImportSource);
157
+ if (typeof fromVite === "string") return jsxFramework(fromVite);
158
+ const plugin = pluginFramework(config.plugins ?? []);
159
+ if (plugin) return plugin;
160
+ const fromTsconfig = tsconfigJsxSource(join(root, "tsconfig.json"));
161
+ if (fromTsconfig) return jsxFramework(fromTsconfig);
162
+ const names = dependencies(root);
163
+ if (names?.has("preact")) return "preact";
164
+ if (names?.has("solid-js")) return "solid";
165
+ if (names?.has("vue") && !names.has("react")) return "vue";
166
+ return "react";
167
+ }
168
+ //#endregion
169
+ //#region ../glue/src/vite.ts
170
+ /**
171
+ * The build side of the shared glue (machine PLAN §5.1, §5.5): a Vite
172
+ * plugin that resolves a package's hooks (`useMachine` from
173
+ * `@menapo/machine`) per module to that module's framework, so one import
174
+ * works in React, Preact, Vue, Svelte, Solid and plain scripts, and no
175
+ * package has a `/react` or `/vue` entry (repo rule: entries by role).
176
+ *
177
+ * - **Per module:** each module importing a hook gets the hook from
178
+ * `virtual:<package>/glue/<framework>`, which resolves to the package's
179
+ * glue module for that framework; the rest of the import stays. Every
180
+ * form that reaches a hook is rewritten: named imports (string names
181
+ * too), a namespace import's members, re-exports, `export *` and
182
+ * `import()`.
183
+ * - **The app's framework:** a glue module and the adapters it uses import
184
+ * `react`, `vue`… by name. Those imports resolve from the project root, as
185
+ * the app's own do, so the app's single copy is used wherever the glue
186
+ * module lives (a workspace package, `node_modules`); one that does not
187
+ * resolve there fails the build, naming the fix.
188
+ */
189
+ /**
190
+ * A path as Vite writes ids, with forward slashes on every platform:
191
+ * `fileURLToPath` gives backslashes on Windows, and a prefix test of one
192
+ * against the other never matches.
193
+ */
194
+ const posix = (path) => path.replace(/\\/g, "/");
195
+ /** Whether `id` is inside one of `dirs`, both compared as Vite writes them. */
196
+ const within = (dirs, id) => dirs.some((dir) => posix(id).startsWith(posix(dir)));
197
+ /** The adapters' own directory: their framework imports resolve from the project root too. */
198
+ const OWN = dirname(fileURLToPath(import.meta.url));
199
+ /** Packages a glue module imports by name: resolved from the project root. */
200
+ const FRAMEWORK_IMPORT = /^(?:react|react-dom|preact|vue|svelte|solid-js)(?:\/|$)/;
201
+ /** The modules the rewrite reads: scripts and framework files, never `?raw` text or an asset's URL. */
202
+ const MODULE = /\.(?:[cm]?[jt]sx?|vue|svelte)(?:\?|$)/;
203
+ const NOT_CODE = /[?&](?:raw|url|inline|worker|sharedworker)(?:[&=]|$)/;
204
+ /** The scripts of a module: the whole of it, or a `.vue`/`.svelte` file's `<script>` blocks. */
205
+ function scripts(code, file) {
206
+ const framework = /\.(?:vue|svelte)$/.test(file);
207
+ if (!framework || !code.includes("<script")) return [{
208
+ offset: 0,
209
+ source: code,
210
+ name: framework ? "x.ts" : file
211
+ }];
212
+ const out = [];
213
+ for (const match of code.matchAll(/(<script\b([^>]*)>)([\s\S]*?)<\/script>/g)) {
214
+ const lang = /\blang\s*=\s*["']([jt]sx?)["']/.exec(match[2])?.[1] ?? "js";
215
+ out.push({
216
+ offset: match.index + match[1].length,
217
+ source: match[3],
218
+ name: `x.${lang}`
219
+ });
220
+ }
221
+ return out;
222
+ }
223
+ /** Every node below `node`, depth first. */
224
+ function walk(node, visit) {
225
+ if (Array.isArray(node)) for (const child of node) walk(child, visit);
226
+ else if (node && typeof node === "object" && typeof node.type === "string") {
227
+ visit(node);
228
+ for (const key of Object.keys(node)) if (key !== "parent") walk(node[key], visit);
229
+ }
230
+ }
231
+ const nameOf$2 = (node) => node.type === "Identifier" ? node.name : String(node.value);
232
+ /** A name as a specifier writes it: bare when it is an identifier, else quoted (`"a-b"`). */
233
+ const spell = (name) => /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
234
+ /**
235
+ * Rewrites every way module `code` reaches `hooks` of `pkg` so they come
236
+ * from `target`; type-only imports and specifiers stay. Replacements keep
237
+ * the lines they replace, so every other position in the module stands.
238
+ * Null when the module reaches no hook.
239
+ */
240
+ function rewrite(code, file, pkg, hooks, target) {
241
+ const edits = [];
242
+ const to = JSON.stringify(target);
243
+ const from = JSON.stringify(pkg);
244
+ const lines = (start, end) => "\n".repeat(code.slice(start, end).split("\n").length - 1);
245
+ for (const { offset, source, name } of scripts(code, file)) {
246
+ const { program, errors } = parseSync(name, source);
247
+ if (errors.length) continue;
248
+ const at = (node) => [offset + node.start, offset + node.end];
249
+ /** Namespace imports of the package: their hook members are rewritten to `__glue_<hook>`. */
250
+ const namespaces = /* @__PURE__ */ new Set();
251
+ /** Where the hooks those members read are imported: before the namespace's import, on its line. */
252
+ let hidden;
253
+ let used = false;
254
+ for (const statement of program.body) if (statement.type === "ImportDeclaration" && statement.source.value === pkg) {
255
+ if (statement.importKind === "type") continue;
256
+ const mine = [];
257
+ const rest = [];
258
+ let namespace;
259
+ for (const specifier of statement.specifiers) if (specifier.type === "ImportNamespaceSpecifier") namespace = specifier.local.name;
260
+ else if (specifier.type === "ImportSpecifier") {
261
+ const imported = nameOf$2(specifier.imported);
262
+ const local = specifier.local.name;
263
+ const text = imported === local ? local : `${spell(imported)} as ${local}`;
264
+ if (specifier.importKind !== "type" && hooks.includes(imported)) mine.push(text);
265
+ else rest.push(specifier.importKind === "type" ? `type ${text}` : text);
266
+ }
267
+ if (namespace !== void 0) {
268
+ namespaces.add(namespace);
269
+ hidden ??= offset + statement.start;
270
+ }
271
+ if (!mine.length) continue;
272
+ const [start, end] = at(statement);
273
+ const kept = rest.length ? `import { ${rest.join(", ")} } from ${from}; ` : "";
274
+ edits.push([
275
+ start,
276
+ end,
277
+ `${kept}import { ${mine.join(", ")} } from ${to};${lines(start, end)}`
278
+ ]);
279
+ } else if (statement.type === "ExportNamedDeclaration" && statement.source?.value === pkg && statement.exportKind !== "type") {
280
+ const mine = [];
281
+ const rest = [];
282
+ for (const specifier of statement.specifiers) {
283
+ const local = nameOf$2(specifier.local);
284
+ const exported = nameOf$2(specifier.exported);
285
+ const text = local === exported ? spell(local) : `${spell(local)} as ${spell(exported)}`;
286
+ if (specifier.exportKind !== "type" && hooks.includes(local)) mine.push(text);
287
+ else rest.push(specifier.exportKind === "type" ? `type ${text}` : text);
288
+ }
289
+ if (!mine.length) continue;
290
+ const [start, end] = at(statement);
291
+ const kept = rest.length ? `export { ${rest.join(", ")} } from ${from}; ` : "";
292
+ edits.push([
293
+ start,
294
+ end,
295
+ `${kept}export { ${mine.join(", ")} } from ${to};${lines(start, end)}`
296
+ ]);
297
+ } else if (statement.type === "ExportAllDeclaration" && statement.source.value === pkg && !statement.exported && statement.exportKind !== "type") {
298
+ const [, end] = at(statement);
299
+ edits.push([
300
+ end,
301
+ end,
302
+ ` export { ${hooks.join(", ")} } from ${to};`
303
+ ]);
304
+ }
305
+ walk(program.body, (node) => {
306
+ if (node.type === "ImportExpression" && node.source.type === "Literal" && node.source.value === pkg) {
307
+ const [start, end] = at(node);
308
+ edits.push([
309
+ start,
310
+ end,
311
+ `Promise.all([import(${from}), import(${to})]).then(([m, g]) => ({ ...m, ...g }))`
312
+ ]);
313
+ } else if (node.type === "MemberExpression" && node.object.type === "Identifier" && namespaces.has(node.object.name)) {
314
+ const key = node.computed ? node.property.type === "Literal" && typeof node.property.value === "string" ? node.property.value : void 0 : nameOf$2(node.property);
315
+ if (key === void 0 || !hooks.includes(key)) return;
316
+ const [start, end] = at(node);
317
+ edits.push([
318
+ start,
319
+ end,
320
+ `__glue_${key}`
321
+ ]);
322
+ used = true;
323
+ }
324
+ });
325
+ if (used && hidden !== void 0) {
326
+ const names = hooks.map((hook) => `${hook} as __glue_${hook}`).join(", ");
327
+ edits.push([
328
+ hidden,
329
+ hidden,
330
+ `import { ${names} } from ${to}; `
331
+ ]);
332
+ }
333
+ }
334
+ if (!edits.length) return null;
335
+ let out = code;
336
+ for (const [start, end, text] of edits.sort((a, b) => b[0] - a[0] || b[1] - a[1])) out = out.slice(0, start) + text + out.slice(end);
337
+ return out;
338
+ }
339
+ /** The plugin resolving `options.hooks` per framework. */
340
+ function gluePlugin(options) {
341
+ const prefix = `virtual:${options.package}/glue/`;
342
+ const dirs = [OWN, ...options.dirs].map(posix);
343
+ let settings = {
344
+ scripts: "vanilla",
345
+ jsx: "react"
346
+ };
347
+ let root = process.cwd();
348
+ return {
349
+ name: options.name,
350
+ enforce: "pre",
351
+ configResolved(config) {
352
+ root = config.root;
353
+ settings = {
354
+ scripts: options.framework ?? rootFramework(root) ?? "vanilla",
355
+ jsx: jsxFrameworkOf(config, root)
356
+ };
357
+ },
358
+ async resolveId(source, importer) {
359
+ if (source.startsWith(prefix)) return posix(options.module(source.slice(prefix.length)));
360
+ if (importer && FRAMEWORK_IMPORT.test(source) && within(dirs, importer)) {
361
+ const found = await this.resolve(source, join(root, "index.html"), { skipSelf: true });
362
+ if (found) return found;
363
+ this.error(`[${options.name}] the ${options.package} glue imports "${source}", which does not resolve from the project root (${root}): install it there, or tell the plugin the framework (its \`framework\` option for plain scripts, a \`@jsxImportSource\` pragma or tsconfig's \`jsxImportSource\` for JSX).`);
364
+ }
365
+ return null;
366
+ },
367
+ transform: {
368
+ filter: {
369
+ id: {
370
+ include: MODULE,
371
+ exclude: NOT_CODE
372
+ },
373
+ code: options.package
374
+ },
375
+ handler(code, id) {
376
+ if (id.startsWith("\0") || within(dirs, id)) return null;
377
+ const framework = frameworkOf(id, code, settings);
378
+ const out = rewrite(code, posix(id).replace(/[?#].*$/, ""), options.package, options.hooks, prefix + framework);
379
+ return out === null ? null : {
380
+ code: out,
381
+ map: null
382
+ };
383
+ }
384
+ }
385
+ };
386
+ }
387
+ //#endregion
388
+ //#region ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
389
+ var comma = ",".charCodeAt(0);
390
+ var semicolon = ";".charCodeAt(0);
391
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
392
+ var intToChar$1 = /* @__PURE__ */ new Uint8Array(64);
393
+ var charToInt = /* @__PURE__ */ new Uint8Array(128);
394
+ for (let i = 0; i < chars.length; i++) {
395
+ const c = chars.charCodeAt(i);
396
+ intToChar$1[i] = c;
397
+ charToInt[c] = i;
398
+ }
399
+ function encodeInteger(builder, num) {
400
+ do {
401
+ let clamped = num & 31;
402
+ num >>>= 5;
403
+ if (num > 0) clamped |= 32;
404
+ builder.write(intToChar$1[clamped]);
405
+ } while (num > 0);
406
+ }
407
+ function encodeSign(num) {
408
+ return num < 0 ? -num << 1 | 1 : num << 1;
409
+ }
410
+ var bufLength = 16384;
411
+ var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? { decode(buf) {
412
+ return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString();
413
+ } } : { decode(buf) {
414
+ let out = "";
415
+ for (let i = 0; i < buf.length; i++) out += String.fromCharCode(buf[i]);
416
+ return out;
417
+ } };
418
+ var StringWriter = class {
419
+ constructor() {
420
+ this.pos = 0;
421
+ this.out = "";
422
+ this.buffer = new Uint8Array(bufLength);
423
+ }
424
+ write(v) {
425
+ const { buffer } = this;
426
+ buffer[this.pos++] = v;
427
+ if (this.pos === bufLength) {
428
+ this.out += td.decode(buffer);
429
+ this.pos = 0;
430
+ }
431
+ }
432
+ flush() {
433
+ const { buffer, out, pos } = this;
434
+ return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
435
+ }
436
+ };
437
+ function encodeRangeMappings(decoded) {
438
+ if (decoded.length === 0) return "";
439
+ const writer = new StringWriter();
440
+ for (let i = 0; i < decoded.length; i++) {
441
+ const indices = decoded[i];
442
+ if (i > 0) writer.write(semicolon);
443
+ let index = 0;
444
+ for (let j = 0; j < indices.length; j++) {
445
+ const offset = indices[j];
446
+ encodeInteger(writer, offset - index);
447
+ index = offset;
448
+ }
449
+ }
450
+ return writer.flush();
451
+ }
452
+ function encode(decoded) {
453
+ const writer = new StringWriter();
454
+ let sourcesIndex = 0;
455
+ let sourceLine = 0;
456
+ let sourceColumn = 0;
457
+ let namesIndex = 0;
458
+ for (let i = 0; i < decoded.length; i++) {
459
+ const line = decoded[i];
460
+ if (i > 0) writer.write(semicolon);
461
+ if (line.length === 0) continue;
462
+ let genColumn = 0;
463
+ for (let j = 0; j < line.length; j++) {
464
+ const segment = line[j];
465
+ if (j > 0) writer.write(comma);
466
+ encodeInteger(writer, encodeSign(segment[0] - genColumn));
467
+ genColumn = segment[0];
468
+ if (segment.length === 1) continue;
469
+ encodeInteger(writer, encodeSign(segment[1] - sourcesIndex));
470
+ encodeInteger(writer, encodeSign(segment[2] - sourceLine));
471
+ encodeInteger(writer, encodeSign(segment[3] - sourceColumn));
472
+ sourcesIndex = segment[1];
473
+ sourceLine = segment[2];
474
+ sourceColumn = segment[3];
475
+ if (segment.length === 4) continue;
476
+ encodeInteger(writer, encodeSign(segment[4] - namesIndex));
477
+ namesIndex = segment[4];
478
+ }
479
+ }
480
+ return writer.flush();
481
+ }
482
+ //#endregion
483
+ //#region ../../node_modules/.pnpm/magic-string@1.4.3/node_modules/magic-string/dist/index.mjs
484
+ var BitSet = class BitSet {
485
+ constructor(arg) {
486
+ this.bits = arg instanceof BitSet ? arg.bits.slice() : [];
487
+ }
488
+ add(n) {
489
+ this.bits[n >> 5] |= 1 << (n & 31);
490
+ }
491
+ has(n) {
492
+ return !!(this.bits[n >> 5] & 1 << (n & 31));
493
+ }
494
+ };
495
+ var Chunk = class Chunk {
496
+ constructor(start, end, content) {
497
+ this.start = start;
498
+ this.end = end;
499
+ this.original = content;
500
+ this.intro = "";
501
+ this.outro = "";
502
+ this.content = content;
503
+ this.storeName = false;
504
+ this.edited = false;
505
+ this.previous = null;
506
+ this.next = null;
507
+ }
508
+ appendLeft(content) {
509
+ this.outro += content;
510
+ }
511
+ appendRight(content) {
512
+ this.intro = this.intro + content;
513
+ }
514
+ clone() {
515
+ const chunk = new Chunk(this.start, this.end, this.original);
516
+ chunk.intro = this.intro;
517
+ chunk.outro = this.outro;
518
+ chunk.content = this.content;
519
+ chunk.storeName = this.storeName;
520
+ chunk.edited = this.edited;
521
+ return chunk;
522
+ }
523
+ contains(index) {
524
+ return this.start < index && index < this.end;
525
+ }
526
+ eachNext(fn) {
527
+ fn(this);
528
+ let chunk = this.next;
529
+ while (chunk) {
530
+ fn(chunk);
531
+ chunk = chunk.next;
532
+ }
533
+ }
534
+ eachPrevious(fn) {
535
+ fn(this);
536
+ let chunk = this.previous;
537
+ while (chunk) {
538
+ fn(chunk);
539
+ chunk = chunk.previous;
540
+ }
541
+ }
542
+ edit(content, storeName, contentOnly) {
543
+ this.content = content;
544
+ if (!contentOnly) {
545
+ this.intro = "";
546
+ this.outro = "";
547
+ }
548
+ this.storeName = storeName;
549
+ this.edited = true;
550
+ return this;
551
+ }
552
+ prependLeft(content) {
553
+ this.outro = content + this.outro;
554
+ }
555
+ prependRight(content) {
556
+ this.intro = content + this.intro;
557
+ }
558
+ reset() {
559
+ this.intro = "";
560
+ this.outro = "";
561
+ if (this.edited) {
562
+ this.content = this.original;
563
+ this.storeName = false;
564
+ this.edited = false;
565
+ }
566
+ }
567
+ split(index) {
568
+ const sliceIndex = index - this.start;
569
+ const originalBefore = this.original.slice(0, sliceIndex);
570
+ const originalAfter = this.original.slice(sliceIndex);
571
+ this.original = originalBefore;
572
+ const newChunk = new Chunk(index, this.end, originalAfter);
573
+ newChunk.outro = this.outro;
574
+ this.outro = "";
575
+ this.end = index;
576
+ if (this.edited) {
577
+ newChunk.edit("", false, true);
578
+ this.content = "";
579
+ } else this.content = originalBefore;
580
+ newChunk.next = this.next;
581
+ if (newChunk.next) newChunk.next.previous = newChunk;
582
+ newChunk.previous = this;
583
+ this.next = newChunk;
584
+ return newChunk;
585
+ }
586
+ toString() {
587
+ return this.intro + this.content + this.outro;
588
+ }
589
+ trimEnd(rx) {
590
+ this.outro = this.outro.replace(rx, "");
591
+ if (this.outro.length) return true;
592
+ const trimmed = this.content.replace(rx, "");
593
+ if (trimmed.length) {
594
+ if (trimmed !== this.content) {
595
+ if (this.edited) this.edit(trimmed, this.storeName, true);
596
+ else this.split(this.start + trimmed.length).edit("", void 0, true);
597
+ }
598
+ return true;
599
+ } else {
600
+ this.edit("", void 0, true);
601
+ this.intro = this.intro.replace(rx, "");
602
+ if (this.intro.length) return true;
603
+ }
604
+ }
605
+ trimStart(rx) {
606
+ this.intro = this.intro.replace(rx, "");
607
+ if (this.intro.length) return true;
608
+ const trimmed = this.content.replace(rx, "");
609
+ if (trimmed.length) {
610
+ if (trimmed !== this.content) {
611
+ if (this.edited) this.edit(trimmed, this.storeName, true);
612
+ else {
613
+ this.split(this.end - trimmed.length);
614
+ this.edit("", void 0, true);
615
+ }
616
+ }
617
+ return true;
618
+ } else {
619
+ this.edit("", void 0, true);
620
+ this.outro = this.outro.replace(rx, "");
621
+ if (this.outro.length) return true;
622
+ }
623
+ }
624
+ };
625
+ /**
626
+ * The single error type thrown by MagicString.
627
+ *
628
+ * Every message is prefixed with `[MagicString]` so its source is obvious at a
629
+ * glance, and is kept short and consistent in tone.
630
+ */
631
+ var MagicStringError = class extends Error {
632
+ name = "MagicStringError";
633
+ constructor(message, options) {
634
+ super(`[MagicString] ${message}`, options);
635
+ }
636
+ };
637
+ function getBtoa() {
638
+ /* v8 ignore next -- environment fallback, unreachable when `btoa` is present */
639
+ if (typeof globalThis !== "undefined" && typeof globalThis.btoa === "function") return (str) => globalThis.btoa(unescape(encodeURIComponent(str)));
640
+ const buffer = globalThis["Buffer"];
641
+ if (buffer) return (str) => buffer.from(str, "utf-8").toString("base64");
642
+ return () => {
643
+ throw new MagicStringError("unsupported environment: `btoa` or `Buffer` is required");
644
+ };
645
+ /* v8 ignore stop */
646
+ }
647
+ const btoa = /* #__PURE__ */ getBtoa();
648
+ var SourceMap = class {
649
+ constructor(properties) {
650
+ this.version = 3;
651
+ this.file = properties.file;
652
+ this.sources = properties.sources;
653
+ this.sourcesContent = properties.sourcesContent;
654
+ this.names = properties.names;
655
+ this.mappings = typeof properties.mappings === "string" ? properties.mappings : encode(properties.mappings);
656
+ if (typeof properties.x_google_ignoreList !== "undefined") this.x_google_ignoreList = properties.x_google_ignoreList;
657
+ if (typeof properties.debugId !== "undefined") this.debugId = properties.debugId;
658
+ if (typeof properties.rangeMappings !== "undefined") {
659
+ let shouldOutputRangeMapping = false;
660
+ for (const line of properties.rangeMappings) if (line.length !== 0) {
661
+ shouldOutputRangeMapping = true;
662
+ break;
663
+ }
664
+ if (shouldOutputRangeMapping) this.rangeMappings = encodeRangeMappings(properties.rangeMappings);
665
+ }
666
+ }
667
+ /**
668
+ * Returns the equivalent of `JSON.stringify(map)`
669
+ */
670
+ toString() {
671
+ return JSON.stringify(this);
672
+ }
673
+ /**
674
+ * Returns a DataURI containing the sourcemap. Useful for doing this sort of thing:
675
+ * `generateMap(options?: SourceMapOptions): SourceMap;`
676
+ */
677
+ toUrl() {
678
+ return `data:application/json;charset=utf-8;base64,${btoa(this.toString())}`;
679
+ }
680
+ };
681
+ function getLocator(source) {
682
+ const lineOffsets = [0];
683
+ for (let i = source.indexOf("\n"); i !== -1; i = source.indexOf("\n", i + 1)) lineOffsets.push(i + 1);
684
+ return function locate(index) {
685
+ let i = 0;
686
+ let j = lineOffsets.length;
687
+ while (i < j) {
688
+ const m = i + j >> 1;
689
+ if (index < lineOffsets[m]) j = m;
690
+ else i = m + 1;
691
+ }
692
+ const line = i - 1;
693
+ return {
694
+ line,
695
+ column: index - lineOffsets[line]
696
+ };
697
+ };
698
+ }
699
+ function getRelativePath(from, to) {
700
+ const fromParts = from.split(/[/\\]/);
701
+ const toParts = to.split(/[/\\]/);
702
+ fromParts.pop();
703
+ while (fromParts[0] === toParts[0]) {
704
+ fromParts.shift();
705
+ toParts.shift();
706
+ }
707
+ if (fromParts.length) {
708
+ let i = fromParts.length;
709
+ while (i--) fromParts[i] = "..";
710
+ }
711
+ return fromParts.concat(toParts).join("/");
712
+ }
713
+ function guessIndent(code) {
714
+ const lines = code.split("\n");
715
+ const tabbed = lines.filter((line) => /^\t+/.test(line));
716
+ const spaced = lines.filter((line) => /^ {2,}/.test(line));
717
+ if (tabbed.length === 0 && spaced.length === 0) return null;
718
+ if (tabbed.length >= spaced.length) return " ";
719
+ const min = spaced.reduce((previous, current) => {
720
+ const numSpaces = /^ +/.exec(current)[0].length;
721
+ return Math.min(numSpaces, previous);
722
+ }, Infinity);
723
+ return " ".repeat(min);
724
+ }
725
+ const toString = Object.prototype.toString;
726
+ function isObject(thing) {
727
+ return toString.call(thing) === "[object Object]";
728
+ }
729
+ const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
730
+ const intToChar = /* #__PURE__ */ (() => {
731
+ const chars = /* @__PURE__ */ new Uint8Array(64);
732
+ for (let i = 0; i < 64; i++) chars[i] = BASE64_CHARS.charCodeAt(i);
733
+ return chars;
734
+ })();
735
+ const COMMA = 44;
736
+ const SEMICOLON = 59;
737
+ const BUFFER_SIZE = 16384;
738
+ const FLUSH_THRESHOLD = 16348;
739
+ const scratch = /* #__PURE__ */ new Uint8Array(BUFFER_SIZE);
740
+ function writeVlq(pos, num) {
741
+ num = num < 0 ? -num << 1 | 1 : num << 1;
742
+ do {
743
+ let clamped = num & 31;
744
+ num >>>= 5;
745
+ if (num > 0) clamped |= 32;
746
+ scratch[pos++] = intToChar[clamped];
747
+ } while (num > 0);
748
+ return pos;
749
+ }
750
+ const decoder = /* #__PURE__ */ new TextDecoder();
751
+ var MappingsEncoder = class {
752
+ constructor() {
753
+ this.out = "";
754
+ this.pos = 0;
755
+ this.lines = [];
756
+ this.buffered = 0;
757
+ this.needsComma = false;
758
+ this.prevGenColumn = 0;
759
+ this.prevSourceIndex = 0;
760
+ this.prevSourceLine = 0;
761
+ this.prevSourceColumn = 0;
762
+ this.prevNameIndex = 0;
763
+ }
764
+ endLine(segments) {
765
+ this.lines.push(segments);
766
+ this.buffered += segments.length + 1;
767
+ if (this.buffered >= 4096) this.drain(null);
768
+ }
769
+ segments(segments) {
770
+ this.drain(segments);
771
+ }
772
+ finish(segments) {
773
+ this.drain(segments);
774
+ this.flush();
775
+ return this.out;
776
+ }
777
+ drain(trailing) {
778
+ const lines = this.lines;
779
+ const lineCount = lines.length;
780
+ for (let l = 0; l <= lineCount; l++) {
781
+ const line = l < lineCount ? lines[l] : trailing;
782
+ if (line === null) break;
783
+ for (let i = 0; i < line.length; i++) {
784
+ if (this.pos > FLUSH_THRESHOLD) this.flush();
785
+ const segment = line[i];
786
+ if (this.needsComma) scratch[this.pos++] = COMMA;
787
+ this.needsComma = true;
788
+ this.pos = writeVlq(this.pos, segment[0] - this.prevGenColumn);
789
+ this.prevGenColumn = segment[0];
790
+ this.pos = writeVlq(this.pos, segment[1] - this.prevSourceIndex);
791
+ this.prevSourceIndex = segment[1];
792
+ this.pos = writeVlq(this.pos, segment[2] - this.prevSourceLine);
793
+ this.prevSourceLine = segment[2];
794
+ this.pos = writeVlq(this.pos, segment[3] - this.prevSourceColumn);
795
+ this.prevSourceColumn = segment[3];
796
+ if (segment.length === 5) {
797
+ this.pos = writeVlq(this.pos, segment[4] - this.prevNameIndex);
798
+ this.prevNameIndex = segment[4];
799
+ }
800
+ }
801
+ if (l < lineCount) {
802
+ if (this.pos > FLUSH_THRESHOLD) this.flush();
803
+ scratch[this.pos++] = SEMICOLON;
804
+ this.needsComma = false;
805
+ this.prevGenColumn = 0;
806
+ }
807
+ }
808
+ lines.length = 0;
809
+ this.buffered = 0;
810
+ this.flush();
811
+ }
812
+ flush() {
813
+ this.out += decoder.decode(scratch.subarray(0, this.pos));
814
+ this.pos = 0;
815
+ }
816
+ };
817
+ const NEWLINE_CHAR$1 = 10;
818
+ function isWordCode(code) {
819
+ return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 95;
820
+ }
821
+ var Mappings = class {
822
+ constructor(hires, encoder = null) {
823
+ this.hires = hires;
824
+ this.generatedCodeLine = 0;
825
+ this.generatedCodeColumn = 0;
826
+ this.raw = [];
827
+ this.rawSegments = this.raw[this.generatedCodeLine] = [];
828
+ this.rawRangeMappings = [];
829
+ this.rawRangeMappingsIndices = this.rawRangeMappings[this.generatedCodeLine] = [];
830
+ this.encoder = encoder;
831
+ }
832
+ nextLine() {
833
+ if (this.encoder === null) {
834
+ this.generatedCodeLine += 1;
835
+ this.raw[this.generatedCodeLine] = this.rawSegments = [];
836
+ } else {
837
+ this.encoder.endLine(this.rawSegments);
838
+ this.rawSegments = [];
839
+ this.generatedCodeLine += 1;
840
+ }
841
+ this.generatedCodeColumn = 0;
842
+ this.rawRangeMappings[this.generatedCodeLine] = this.rawRangeMappingsIndices = [];
843
+ }
844
+ addEdit(sourceIndex, content, loc, nameIndex) {
845
+ if (content.length) {
846
+ const contentLengthMinusOne = content.length - 1;
847
+ let contentLineEnd = content.indexOf("\n", 0);
848
+ let previousContentLineEnd = -1;
849
+ while (contentLineEnd >= 0 && contentLengthMinusOne > contentLineEnd) {
850
+ const segment = [
851
+ this.generatedCodeColumn,
852
+ sourceIndex,
853
+ loc.line,
854
+ loc.column
855
+ ];
856
+ if (nameIndex >= 0) segment.push(nameIndex);
857
+ this.rawSegments.push(segment);
858
+ this.nextLine();
859
+ previousContentLineEnd = contentLineEnd;
860
+ contentLineEnd = content.indexOf("\n", contentLineEnd + 1);
861
+ }
862
+ const segment = [
863
+ this.generatedCodeColumn,
864
+ sourceIndex,
865
+ loc.line,
866
+ loc.column
867
+ ];
868
+ if (nameIndex >= 0) segment.push(nameIndex);
869
+ this.rawSegments.push(segment);
870
+ this.advance(content.slice(previousContentLineEnd + 1));
871
+ }
872
+ }
873
+ addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
874
+ const end = chunk.end;
875
+ let i = chunk.start;
876
+ if (this.hires) {
877
+ const boundary = this.hires === "boundary";
878
+ const experimentalRange = this.hires === "experimental-range";
879
+ const encoder = experimentalRange ? null : this.encoder;
880
+ let charInHiresBoundary = false;
881
+ while (i < end) {
882
+ if (encoder !== null && this.rawSegments.length >= 4096) {
883
+ encoder.segments(this.rawSegments);
884
+ this.rawSegments.length = 0;
885
+ }
886
+ if (experimentalRange && i + 1 >= end) this.rawSegments.push([
887
+ this.generatedCodeColumn,
888
+ sourceIndex,
889
+ loc.line,
890
+ loc.column
891
+ ]);
892
+ const code = original.charCodeAt(i);
893
+ if (code === NEWLINE_CHAR$1) {
894
+ loc.line += 1;
895
+ loc.column = 0;
896
+ this.nextLine();
897
+ charInHiresBoundary = false;
898
+ } else {
899
+ if (boundary) {
900
+ if (isWordCode(code)) {
901
+ if (!charInHiresBoundary) {
902
+ this.rawSegments.push([
903
+ this.generatedCodeColumn,
904
+ sourceIndex,
905
+ loc.line,
906
+ loc.column
907
+ ]);
908
+ charInHiresBoundary = true;
909
+ }
910
+ } else {
911
+ this.rawSegments.push([
912
+ this.generatedCodeColumn,
913
+ sourceIndex,
914
+ loc.line,
915
+ loc.column
916
+ ]);
917
+ charInHiresBoundary = false;
918
+ }
919
+ } else if (experimentalRange) {
920
+ if (i === chunk.start) {
921
+ this.rawRangeMappingsIndices.push(this.rawSegments.length);
922
+ this.rawSegments.push([
923
+ this.generatedCodeColumn,
924
+ sourceIndex,
925
+ loc.line,
926
+ loc.column
927
+ ]);
928
+ }
929
+ } else this.rawSegments.push([
930
+ this.generatedCodeColumn,
931
+ sourceIndex,
932
+ loc.line,
933
+ loc.column
934
+ ]);
935
+ loc.column += 1;
936
+ this.generatedCodeColumn += 1;
937
+ }
938
+ i += 1;
939
+ }
940
+ } else {
941
+ const bits = sourcemapLocations.bits;
942
+ while (i < end) {
943
+ let newline = original.indexOf("\n", i);
944
+ if (newline === -1 || newline > end) newline = end;
945
+ if (newline > i) {
946
+ this.rawSegments.push([
947
+ this.generatedCodeColumn,
948
+ sourceIndex,
949
+ loc.line,
950
+ loc.column
951
+ ]);
952
+ for (let w = i + 1 >> 5, last = newline - 1 >> 5; w <= last; w++) {
953
+ let word = bits[w];
954
+ if (!word) continue;
955
+ const base = w << 5;
956
+ while (word) {
957
+ const lowest = word & -word;
958
+ const index = base + 31 - Math.clz32(lowest);
959
+ if (index > i && index < newline) {
960
+ const offset = index - i;
961
+ this.rawSegments.push([
962
+ this.generatedCodeColumn + offset,
963
+ sourceIndex,
964
+ loc.line,
965
+ loc.column + offset
966
+ ]);
967
+ }
968
+ word ^= lowest;
969
+ }
970
+ }
971
+ loc.column += newline - i;
972
+ this.generatedCodeColumn += newline - i;
973
+ }
974
+ if (newline === end) break;
975
+ loc.line += 1;
976
+ loc.column = 0;
977
+ this.nextLine();
978
+ i = newline + 1;
979
+ }
980
+ }
981
+ }
982
+ advance(str) {
983
+ if (!str) return;
984
+ const lastNewline = str.lastIndexOf("\n");
985
+ for (let i = str.indexOf("\n"); i !== -1; i = str.indexOf("\n", i + 1)) this.nextLine();
986
+ this.generatedCodeColumn += str.length - lastNewline - 1;
987
+ }
988
+ };
989
+ const n = "\n";
990
+ const NEWLINE_CHAR = "\n".charCodeAt(0);
991
+ const CR_CHAR = "\r".charCodeAt(0);
992
+ const warned = {
993
+ insertLeft: false,
994
+ insertRight: false,
995
+ storeName: false
996
+ };
997
+ /**
998
+ * Expands the `$` substitution patterns that `String.prototype.replace` accepts
999
+ * in a string replacement.
1000
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter
1001
+ *
1002
+ * `captures` holds the capture groups in order, so `captures[0]` is `$1`.
1003
+ * `namedCaptures` is `undefined` when the pattern has no named groups, and
1004
+ * `$<name>` is then literal text - which is also the case for a string search
1005
+ * value, where there are no groups of either kind.
1006
+ */
1007
+ function expandReplacement(replacement, matched, position, str, captures, namedCaptures) {
1008
+ if (!replacement.includes("$")) return replacement;
1009
+ let result = "";
1010
+ let index = 0;
1011
+ while (index < replacement.length) {
1012
+ const dollar = replacement.indexOf("$", index);
1013
+ if (dollar === -1) {
1014
+ result += replacement.slice(index);
1015
+ break;
1016
+ }
1017
+ result += replacement.slice(index, dollar);
1018
+ const char = replacement[dollar + 1];
1019
+ let expansion = "$";
1020
+ let consumed = 1;
1021
+ if (char === "$") consumed = 2;
1022
+ else if (char === "&") {
1023
+ expansion = matched;
1024
+ consumed = 2;
1025
+ } else if (char === "`") {
1026
+ expansion = str.slice(0, position);
1027
+ consumed = 2;
1028
+ } else if (char === "'") {
1029
+ expansion = str.slice(position + matched.length);
1030
+ consumed = 2;
1031
+ } else if (char === "<" && namedCaptures !== void 0) {
1032
+ const close = replacement.indexOf(">", dollar + 2);
1033
+ if (close !== -1) {
1034
+ expansion = namedCaptures[replacement.slice(dollar + 2, close)] ?? "";
1035
+ consumed = close + 1 - dollar;
1036
+ }
1037
+ } else if (char >= "0" && char <= "9") {
1038
+ const second = replacement[dollar + 2];
1039
+ const double = second >= "0" && second <= "9" ? Number(char + second) : NaN;
1040
+ const single = Number(char);
1041
+ if (double >= 1 && double <= captures.length) {
1042
+ expansion = captures[double - 1] ?? "";
1043
+ consumed = 3;
1044
+ } else if (single >= 1 && single <= captures.length) {
1045
+ expansion = captures[single - 1] ?? "";
1046
+ consumed = 2;
1047
+ }
1048
+ }
1049
+ result += expansion;
1050
+ index = dollar + consumed;
1051
+ }
1052
+ return result;
1053
+ }
1054
+ var MagicString = class MagicString {
1055
+ constructor(string, options = {}) {
1056
+ const chunk = new Chunk(0, string.length, string);
1057
+ Object.defineProperties(this, {
1058
+ original: {
1059
+ writable: true,
1060
+ value: string
1061
+ },
1062
+ outro: {
1063
+ writable: true,
1064
+ value: ""
1065
+ },
1066
+ intro: {
1067
+ writable: true,
1068
+ value: ""
1069
+ },
1070
+ firstChunk: {
1071
+ writable: true,
1072
+ value: chunk
1073
+ },
1074
+ lastChunk: {
1075
+ writable: true,
1076
+ value: chunk
1077
+ },
1078
+ lastSearchedChunk: {
1079
+ writable: true,
1080
+ value: chunk
1081
+ },
1082
+ byStart: {
1083
+ writable: true,
1084
+ value: /* @__PURE__ */ new Map([[0, chunk]])
1085
+ },
1086
+ byEnd: {
1087
+ writable: true,
1088
+ value: /* @__PURE__ */ new Map([[string.length, chunk]])
1089
+ },
1090
+ filename: {
1091
+ writable: true,
1092
+ value: options.filename
1093
+ },
1094
+ indentExclusionRanges: {
1095
+ writable: true,
1096
+ value: options.indentExclusionRanges
1097
+ },
1098
+ sourcemapLocations: {
1099
+ writable: true,
1100
+ value: new BitSet()
1101
+ },
1102
+ storedNames: {
1103
+ writable: true,
1104
+ value: {}
1105
+ },
1106
+ indentStr: {
1107
+ writable: true,
1108
+ value: void 0
1109
+ },
1110
+ ignoreList: {
1111
+ writable: true,
1112
+ value: options.ignoreList
1113
+ },
1114
+ offset: {
1115
+ writable: true,
1116
+ value: options.offset || 0
1117
+ },
1118
+ hasMovedChunks: {
1119
+ writable: true,
1120
+ value: false
1121
+ }
1122
+ });
1123
+ }
1124
+ /**
1125
+ * Adds the specified character index (with respect to the original string) to sourcemap mappings, if `hires` is false.
1126
+ */
1127
+ addSourcemapLocation(char) {
1128
+ this.sourcemapLocations.add(char);
1129
+ }
1130
+ /**
1131
+ * Appends the specified content to the end of the string.
1132
+ */
1133
+ append(content) {
1134
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1135
+ this.outro += content;
1136
+ return this;
1137
+ }
1138
+ /**
1139
+ * Appends the specified content at the index in the original string.
1140
+ * If a range *ending* with index is subsequently moved, the insert will be moved with it.
1141
+ * See also `s.prependLeft(...)`.
1142
+ */
1143
+ appendLeft(index, content) {
1144
+ index = index + this.offset;
1145
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1146
+ this._split(index);
1147
+ const chunk = this.byEnd.get(index);
1148
+ if (chunk) chunk.appendLeft(content);
1149
+ else this.intro += content;
1150
+ return this;
1151
+ }
1152
+ /**
1153
+ * Appends the specified content at the index in the original string.
1154
+ * If a range *starting* with index is subsequently moved, the insert will be moved with it.
1155
+ * See also `s.prependRight(...)`.
1156
+ */
1157
+ appendRight(index, content) {
1158
+ index = index + this.offset;
1159
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1160
+ this._split(index);
1161
+ const chunk = this.byStart.get(index);
1162
+ if (chunk) chunk.appendRight(content);
1163
+ else this.outro += content;
1164
+ return this;
1165
+ }
1166
+ /**
1167
+ * Does what you'd expect.
1168
+ */
1169
+ clone() {
1170
+ const cloned = new MagicString(this.original, {
1171
+ filename: this.filename,
1172
+ ignoreList: this.ignoreList,
1173
+ offset: this.offset
1174
+ });
1175
+ let originalChunk = this.firstChunk;
1176
+ let clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
1177
+ while (originalChunk) {
1178
+ cloned.byStart.set(clonedChunk.start, clonedChunk);
1179
+ cloned.byEnd.set(clonedChunk.end, clonedChunk);
1180
+ const nextOriginalChunk = originalChunk.next;
1181
+ const nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
1182
+ if (nextClonedChunk) {
1183
+ clonedChunk.next = nextClonedChunk;
1184
+ nextClonedChunk.previous = clonedChunk;
1185
+ clonedChunk = nextClonedChunk;
1186
+ }
1187
+ originalChunk = nextOriginalChunk;
1188
+ }
1189
+ cloned.lastChunk = clonedChunk;
1190
+ if (this.indentExclusionRanges) cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
1191
+ cloned.sourcemapLocations = new BitSet(this.sourcemapLocations);
1192
+ cloned.storedNames = { ...this.storedNames };
1193
+ cloned.intro = this.intro;
1194
+ cloned.outro = this.outro;
1195
+ cloned.hasMovedChunks = this.hasMovedChunks;
1196
+ return cloned;
1197
+ }
1198
+ /**
1199
+ * Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
1200
+ * Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
1201
+ */
1202
+ generateDecodedMap(options) {
1203
+ options = options || {};
1204
+ const mappings = new Mappings(options.hires);
1205
+ const names = this._generateMappings(mappings);
1206
+ return {
1207
+ ...this._mapProperties(options, names),
1208
+ mappings: mappings.raw,
1209
+ rangeMappings: mappings.rawRangeMappings
1210
+ };
1211
+ }
1212
+ /**
1213
+ * Generates a version 3 sourcemap.
1214
+ */
1215
+ generateMap(options) {
1216
+ options = options || {};
1217
+ const encoder = new MappingsEncoder();
1218
+ const mappings = new Mappings(options.hires, encoder);
1219
+ const names = this._generateMappings(mappings);
1220
+ return new SourceMap({
1221
+ ...this._mapProperties(options, names),
1222
+ mappings: encoder.finish(mappings.rawSegments),
1223
+ rangeMappings: mappings.rawRangeMappings
1224
+ });
1225
+ }
1226
+ /** @internal */
1227
+ _generateMappings(mappings) {
1228
+ const sourceIndex = 0;
1229
+ const names = Object.keys(this.storedNames);
1230
+ const locate = getLocator(this.original);
1231
+ if (this.intro) mappings.advance(this.intro);
1232
+ this.firstChunk.eachNext((chunk) => {
1233
+ const loc = locate(chunk.start);
1234
+ if (chunk.intro.length) mappings.advance(chunk.intro);
1235
+ if (chunk.edited) mappings.addEdit(sourceIndex, chunk.content, loc, chunk.storeName ? names.indexOf(chunk.original) : -1);
1236
+ else mappings.addUneditedChunk(sourceIndex, chunk, this.original, loc, this.sourcemapLocations);
1237
+ if (chunk.outro.length) mappings.advance(chunk.outro);
1238
+ });
1239
+ if (this.outro) mappings.advance(this.outro);
1240
+ return names;
1241
+ }
1242
+ /** @internal */
1243
+ _mapProperties(options, names) {
1244
+ return {
1245
+ file: options.file ? options.file.split(/[/\\]/).pop() : void 0,
1246
+ sources: [options.source ? getRelativePath(options.file || "", options.source) : options.file || ""],
1247
+ sourcesContent: options.includeContent ? [this.original] : void 0,
1248
+ names,
1249
+ x_google_ignoreList: this.ignoreList ? [0] : void 0
1250
+ };
1251
+ }
1252
+ /** @internal */
1253
+ _ensureindentStr() {
1254
+ if (this.indentStr === void 0) this.indentStr = guessIndent(this.original);
1255
+ }
1256
+ /** @internal */
1257
+ _getRawIndentString() {
1258
+ this._ensureindentStr();
1259
+ return this.indentStr;
1260
+ }
1261
+ getIndentString() {
1262
+ this._ensureindentStr();
1263
+ return this.indentStr === null ? " " : this.indentStr;
1264
+ }
1265
+ indent(indentStr, options) {
1266
+ const pattern = /^[^\r\n]/gm;
1267
+ if (isObject(indentStr)) {
1268
+ options = indentStr;
1269
+ indentStr = void 0;
1270
+ }
1271
+ if (indentStr === void 0) {
1272
+ this._ensureindentStr();
1273
+ indentStr = this.indentStr || " ";
1274
+ }
1275
+ if (indentStr === "") return this;
1276
+ const resolvedIndentStr = indentStr;
1277
+ options = options || {};
1278
+ const isExcluded = {};
1279
+ if (options.exclude) (typeof options.exclude[0] === "number" ? [options.exclude] : options.exclude).forEach((exclusion) => {
1280
+ for (let i = exclusion[0]; i < exclusion[1]; i += 1) isExcluded[i] = true;
1281
+ });
1282
+ let shouldIndentNextCharacter = options.indentStart !== false;
1283
+ const indentPiece = (str) => {
1284
+ if (str === "") return str;
1285
+ const indented = str.replace(pattern, (match, offset) => offset > 0 || shouldIndentNextCharacter ? `${resolvedIndentStr}${match}` : match);
1286
+ shouldIndentNextCharacter = str[str.length - 1] === "\n";
1287
+ return indented;
1288
+ };
1289
+ this.intro = indentPiece(this.intro);
1290
+ let charIndex = 0;
1291
+ let chunk = this.firstChunk;
1292
+ const indentAt = (index) => {
1293
+ shouldIndentNextCharacter = false;
1294
+ if (index === chunk.start) chunk.appendRight(resolvedIndentStr);
1295
+ else {
1296
+ this._splitChunk(chunk, index);
1297
+ chunk = chunk.next;
1298
+ chunk.prependRight(resolvedIndentStr);
1299
+ }
1300
+ };
1301
+ while (chunk) {
1302
+ const end = chunk.end;
1303
+ if (!isExcluded[chunk.start]) chunk.intro = indentPiece(chunk.intro);
1304
+ if (chunk.edited) {
1305
+ if (!isExcluded[charIndex]) chunk.content = indentPiece(chunk.content);
1306
+ } else if (options.exclude) {
1307
+ charIndex = chunk.start;
1308
+ while (charIndex < end) {
1309
+ if (!isExcluded[charIndex]) {
1310
+ const char = this.original.charCodeAt(charIndex);
1311
+ if (char === NEWLINE_CHAR) shouldIndentNextCharacter = true;
1312
+ else if (char !== CR_CHAR && shouldIndentNextCharacter) indentAt(charIndex);
1313
+ }
1314
+ charIndex += 1;
1315
+ }
1316
+ } else {
1317
+ charIndex = chunk.start;
1318
+ while (charIndex < end) {
1319
+ if (!shouldIndentNextCharacter) {
1320
+ const nextLine = this.original.indexOf(n, charIndex);
1321
+ if (nextLine === -1 || nextLine >= end) break;
1322
+ shouldIndentNextCharacter = true;
1323
+ charIndex = nextLine + 1;
1324
+ continue;
1325
+ }
1326
+ const char = this.original.charCodeAt(charIndex);
1327
+ if (char === NEWLINE_CHAR || char === CR_CHAR) {
1328
+ charIndex += 1;
1329
+ continue;
1330
+ }
1331
+ indentAt(charIndex);
1332
+ charIndex += 1;
1333
+ }
1334
+ }
1335
+ if (!isExcluded[chunk.end - 1]) chunk.outro = indentPiece(chunk.outro);
1336
+ charIndex = chunk.end;
1337
+ chunk = chunk.next;
1338
+ }
1339
+ this.outro = indentPiece(this.outro);
1340
+ return this;
1341
+ }
1342
+ /** @internal */
1343
+ insert() {
1344
+ throw new MagicStringError("insert() is deprecated, use appendLeft() or prependRight()");
1345
+ }
1346
+ /** @internal */
1347
+ insertLeft(index, content) {
1348
+ if (!warned.insertLeft) {
1349
+ console.warn("magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead");
1350
+ warned.insertLeft = true;
1351
+ }
1352
+ return this.appendLeft(index, content);
1353
+ }
1354
+ /** @internal */
1355
+ insertRight(index, content) {
1356
+ if (!warned.insertRight) {
1357
+ console.warn("magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead");
1358
+ warned.insertRight = true;
1359
+ }
1360
+ return this.prependRight(index, content);
1361
+ }
1362
+ /**
1363
+ * Moves the characters from `start` and `end` to `index`.
1364
+ *
1365
+ * `affinity` controls where the range is anchored at `index`. With the
1366
+ * default `'right'`, it is inserted before the content that starts at `index`;
1367
+ * with `'left'`, it is inserted after the content that ends at `index`. The
1368
+ * two differ only when other content has already been moved to that boundary,
1369
+ * mirroring the `appendLeft`/`appendRight` distinction.
1370
+ */
1371
+ move(start, end, index, affinity = "right") {
1372
+ start = start + this.offset;
1373
+ end = end + this.offset;
1374
+ index = index + this.offset;
1375
+ if (start === end) return this;
1376
+ if (index >= start && index <= end) throw new MagicStringError("cannot move a selection inside itself");
1377
+ this._split(start);
1378
+ this._split(end);
1379
+ this._split(index);
1380
+ const first = this.byStart.get(start);
1381
+ const last = this.byEnd.get(end);
1382
+ if (this.hasMovedChunks) {
1383
+ let cursor = first;
1384
+ while (cursor !== last) {
1385
+ cursor = cursor.next;
1386
+ if (!cursor || cursor.start < start || cursor.end > end) throw new MagicStringError(`cannot move ${start} to ${end} because an earlier move split that range`);
1387
+ }
1388
+ }
1389
+ const oldLeft = first.previous;
1390
+ const oldRight = last.next;
1391
+ let newLeft;
1392
+ let newRight;
1393
+ if (affinity === "left") {
1394
+ newLeft = this.byEnd.get(index) ?? null;
1395
+ if (!newLeft) {
1396
+ if (first === this.firstChunk) return this;
1397
+ newRight = this.firstChunk;
1398
+ } else {
1399
+ if (newLeft.next === first) return this;
1400
+ newRight = newLeft.next;
1401
+ }
1402
+ } else {
1403
+ newRight = this.byStart.get(index) ?? null;
1404
+ if (!newRight) {
1405
+ if (last === this.lastChunk) return this;
1406
+ newLeft = this.lastChunk;
1407
+ } else {
1408
+ if (newRight.previous === last) return this;
1409
+ newLeft = newRight.previous;
1410
+ }
1411
+ }
1412
+ if (oldLeft) oldLeft.next = oldRight;
1413
+ if (oldRight) oldRight.previous = oldLeft;
1414
+ if (newLeft) newLeft.next = first;
1415
+ if (newRight) newRight.previous = last;
1416
+ if (!first.previous) this.firstChunk = last.next;
1417
+ if (!last.next) {
1418
+ this.lastChunk = first.previous;
1419
+ this.lastChunk.next = null;
1420
+ }
1421
+ first.previous = newLeft;
1422
+ last.next = newRight || null;
1423
+ if (!newLeft) this.firstChunk = first;
1424
+ if (!newRight) this.lastChunk = last;
1425
+ this.hasMovedChunks = true;
1426
+ return this;
1427
+ }
1428
+ /**
1429
+ * Replaces the characters from `start` to `end` with `content`, along with the appended/prepended content in
1430
+ * that range. The same restrictions as `s.remove()` apply.
1431
+ *
1432
+ * The fourth argument is optional. It can have a storeName property - if true, the original name will be stored
1433
+ * for later inclusion in a sourcemap's names array - and a contentOnly property which determines whether only
1434
+ * the content is overwritten, or anything that was appended/prepended to the range as well.
1435
+ *
1436
+ * It may be preferred to use `s.update(...)` instead if you wish to avoid overwriting the appended/prepended content.
1437
+ */
1438
+ overwrite(start, end, content, options) {
1439
+ const optionObject = typeof options === "object" && options ? options : {};
1440
+ return this.update(start, end, content, {
1441
+ ...optionObject,
1442
+ overwrite: !optionObject.contentOnly
1443
+ });
1444
+ }
1445
+ /**
1446
+ * Replaces the characters from `start` to `end` with `content`. The same restrictions as `s.remove()` apply.
1447
+ *
1448
+ * The fourth argument is optional. It can have a storeName property - if true, the original name will be stored
1449
+ * for later inclusion in a sourcemap's names array - and an overwrite property which determines whether only
1450
+ * the content is overwritten, or anything that was appended/prepended to the range as well.
1451
+ */
1452
+ update(start, end, content, options) {
1453
+ start = start + this.offset;
1454
+ end = end + this.offset;
1455
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1456
+ if (this.original.length !== 0) {
1457
+ if (start < 0) start = Math.max(0, start + this.original.length);
1458
+ if (end < 0) end = Math.max(0, end + this.original.length);
1459
+ }
1460
+ if (start < 0) throw new MagicStringError(`start ${start} is out of bounds`);
1461
+ if (end > this.original.length) throw new MagicStringError(`end ${end} is out of bounds`);
1462
+ if (start === end) throw new MagicStringError(`cannot overwrite a zero-length range at ${start}, use appendLeft() or prependRight()`);
1463
+ if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
1464
+ this._split(start);
1465
+ this._split(end);
1466
+ if (options === true) {
1467
+ if (!warned.storeName) {
1468
+ console.warn("The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string");
1469
+ warned.storeName = true;
1470
+ }
1471
+ options = { storeName: true };
1472
+ }
1473
+ const optionObject = typeof options === "object" && options ? options : {};
1474
+ const storeName = optionObject.storeName || false;
1475
+ const overwrite = optionObject.overwrite || false;
1476
+ if (storeName) {
1477
+ const original = this.original.slice(start, end);
1478
+ Object.defineProperty(this.storedNames, original, {
1479
+ writable: true,
1480
+ value: true,
1481
+ enumerable: true
1482
+ });
1483
+ }
1484
+ const first = this.byStart.get(start);
1485
+ const last = this.byEnd.get(end);
1486
+ /* v8 ignore else -- unreachable: a valid start/end always yields a `first` chunk */
1487
+ if (first) {
1488
+ let chunk = first;
1489
+ while (chunk !== last) {
1490
+ if (chunk.next !== this.byStart.get(chunk.end)) throw new MagicStringError("cannot overwrite across a split point");
1491
+ chunk = chunk.next;
1492
+ chunk.edit("", false, !overwrite);
1493
+ }
1494
+ first.edit(content, storeName, !overwrite);
1495
+ } else {
1496
+ const newChunk = new Chunk(start, end, "").edit(content, storeName);
1497
+ last.next = newChunk;
1498
+ newChunk.previous = last;
1499
+ }
1500
+ return this;
1501
+ }
1502
+ /**
1503
+ * Prepends the string with the specified content.
1504
+ */
1505
+ prepend(content) {
1506
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1507
+ this.intro = content + this.intro;
1508
+ return this;
1509
+ }
1510
+ /**
1511
+ * Same as `s.appendLeft(...)`, except that the inserted content will go *before* any previous appends or prepends at index
1512
+ */
1513
+ prependLeft(index, content) {
1514
+ index = index + this.offset;
1515
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1516
+ this._split(index);
1517
+ const chunk = this.byEnd.get(index);
1518
+ if (chunk) chunk.prependLeft(content);
1519
+ else this.intro = content + this.intro;
1520
+ return this;
1521
+ }
1522
+ /**
1523
+ * Same as `s.appendRight(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`
1524
+ */
1525
+ prependRight(index, content) {
1526
+ index = index + this.offset;
1527
+ if (typeof content !== "string") throw new MagicStringError(`content must be a string, got ${typeof content}`);
1528
+ this._split(index);
1529
+ const chunk = this.byStart.get(index);
1530
+ if (chunk) chunk.prependRight(content);
1531
+ else this.outro = content + this.outro;
1532
+ return this;
1533
+ }
1534
+ /**
1535
+ * Removes the characters from `start` to `end` (of the original string, **not** the generated string).
1536
+ * Content appended or prepended at positions strictly inside the range is removed with it, while
1537
+ * content attached at `start` or `end` is preserved — use `s.overwrite(start, end, '')` to remove
1538
+ * the range including its edge inserts.
1539
+ * Repeated removals and removals with partially overlapping ranges are allowed.
1540
+ */
1541
+ remove(start, end) {
1542
+ start = start + this.offset;
1543
+ end = end + this.offset;
1544
+ if (this.original.length !== 0) {
1545
+ if (start < 0) start = Math.max(0, start + this.original.length);
1546
+ if (end < 0) end = Math.max(0, end + this.original.length);
1547
+ }
1548
+ if (start === end) return this;
1549
+ if (start < 0 || end > this.original.length) throw new MagicStringError(`range ${start}–${end} is out of bounds`);
1550
+ if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
1551
+ this._split(start);
1552
+ this._split(end);
1553
+ let chunk = this.byStart.get(start);
1554
+ while (chunk) {
1555
+ if (chunk.start > start) chunk.intro = "";
1556
+ if (chunk.end < end) chunk.outro = "";
1557
+ chunk.edit("", false, true);
1558
+ chunk = end > chunk.end ? this.byStart.get(chunk.end) : null;
1559
+ }
1560
+ return this;
1561
+ }
1562
+ /**
1563
+ * Reset the modified characters from `start` to `end` (of the original string, **not** the generated string).
1564
+ */
1565
+ reset(start, end) {
1566
+ start = start + this.offset;
1567
+ end = end + this.offset;
1568
+ if (this.original.length !== 0) {
1569
+ if (start < 0) start = Math.max(0, start + this.original.length);
1570
+ if (end < 0) end = Math.max(0, end + this.original.length);
1571
+ }
1572
+ if (start === end) return this;
1573
+ if (start < 0 || end > this.original.length) throw new MagicStringError(`range ${start}–${end} is out of bounds`);
1574
+ if (start > end) throw new MagicStringError(`end must be greater than start (start: ${start}, end: ${end})`);
1575
+ this._split(start);
1576
+ this._split(end);
1577
+ let chunk = this.byStart.get(start);
1578
+ while (chunk) {
1579
+ chunk.reset();
1580
+ chunk = end > chunk.end ? this.byStart.get(chunk.end) : null;
1581
+ }
1582
+ return this;
1583
+ }
1584
+ lastChar() {
1585
+ if (this.outro.length) return this.outro[this.outro.length - 1];
1586
+ let chunk = this.lastChunk;
1587
+ while (chunk) {
1588
+ if (chunk.outro.length) return chunk.outro[chunk.outro.length - 1];
1589
+ if (chunk.content.length) return chunk.content[chunk.content.length - 1];
1590
+ if (chunk.intro.length) return chunk.intro[chunk.intro.length - 1];
1591
+ chunk = chunk.previous;
1592
+ }
1593
+ if (this.intro.length) return this.intro[this.intro.length - 1];
1594
+ return "";
1595
+ }
1596
+ lastLine() {
1597
+ let lineIndex = this.outro.lastIndexOf(n);
1598
+ if (lineIndex !== -1) return this.outro.substr(lineIndex + 1);
1599
+ let lineStr = this.outro;
1600
+ let chunk = this.lastChunk;
1601
+ while (chunk) {
1602
+ if (chunk.outro.length > 0) {
1603
+ lineIndex = chunk.outro.lastIndexOf(n);
1604
+ if (lineIndex !== -1) return chunk.outro.substr(lineIndex + 1) + lineStr;
1605
+ lineStr = chunk.outro + lineStr;
1606
+ }
1607
+ if (chunk.content.length > 0) {
1608
+ lineIndex = chunk.content.lastIndexOf(n);
1609
+ if (lineIndex !== -1) return chunk.content.substr(lineIndex + 1) + lineStr;
1610
+ lineStr = chunk.content + lineStr;
1611
+ }
1612
+ if (chunk.intro.length > 0) {
1613
+ lineIndex = chunk.intro.lastIndexOf(n);
1614
+ if (lineIndex !== -1) return chunk.intro.substr(lineIndex + 1) + lineStr;
1615
+ lineStr = chunk.intro + lineStr;
1616
+ }
1617
+ chunk = chunk.previous;
1618
+ }
1619
+ lineIndex = this.intro.lastIndexOf(n);
1620
+ if (lineIndex !== -1) return this.intro.substr(lineIndex + 1) + lineStr;
1621
+ return this.intro + lineStr;
1622
+ }
1623
+ /**
1624
+ * Returns the content of the generated string that corresponds to the slice between `start` and `end` of the original string.
1625
+ * Throws error if the indices are for characters that were already removed.
1626
+ */
1627
+ slice(start = 0, end = this.original.length - this.offset) {
1628
+ start = start + this.offset;
1629
+ end = end + this.offset;
1630
+ if (this.original.length !== 0) {
1631
+ if (start < 0) start = Math.max(0, start + this.original.length);
1632
+ if (end < 0) end = Math.max(0, end + this.original.length);
1633
+ }
1634
+ let result = "";
1635
+ let chunk = this.firstChunk;
1636
+ while (chunk && (chunk.start > start || chunk.end <= start)) {
1637
+ if (chunk.start < end && chunk.end >= end || end === 0 && chunk.start === 0) return result;
1638
+ chunk = chunk.next;
1639
+ }
1640
+ if (chunk && chunk.edited && chunk.start !== start) throw new MagicStringError(`cannot use edited character ${start} as slice start anchor`);
1641
+ const startChunk = chunk;
1642
+ while (chunk) {
1643
+ if (end === 0 && chunk.start === 0) break;
1644
+ if (chunk.intro && (startChunk !== chunk || chunk.start === start)) result += chunk.intro;
1645
+ const containsEnd = chunk.start < end && chunk.end >= end;
1646
+ if (containsEnd && chunk.edited && chunk.end !== end) throw new MagicStringError(`cannot use edited character ${end} as slice end anchor`);
1647
+ const sliceStart = startChunk === chunk ? start - chunk.start : 0;
1648
+ const sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
1649
+ result += chunk.content.slice(sliceStart, sliceEnd);
1650
+ if (chunk.outro && (!containsEnd || chunk.end === end)) result += chunk.outro;
1651
+ if (containsEnd) break;
1652
+ chunk = chunk.next;
1653
+ }
1654
+ return result;
1655
+ }
1656
+ /**
1657
+ * Returns a clone of `s`, with all content before the `start` and `end` characters of the original string removed.
1658
+ */
1659
+ snip(start, end) {
1660
+ const clone = this.clone();
1661
+ clone.remove(0, start);
1662
+ clone.remove(end, clone.original.length);
1663
+ return clone;
1664
+ }
1665
+ /** @internal */
1666
+ _split(index) {
1667
+ if (this.byStart.get(index) || this.byEnd.get(index)) return;
1668
+ let chunk = this.lastSearchedChunk;
1669
+ let previousChunk = chunk;
1670
+ const searchForward = index > chunk.end;
1671
+ while (chunk) {
1672
+ if (chunk.contains(index)) return this._splitChunk(chunk, index);
1673
+ chunk = searchForward ? this.byStart.get(chunk.end) : this.byEnd.get(chunk.start);
1674
+ if (chunk === previousChunk) return;
1675
+ previousChunk = chunk;
1676
+ }
1677
+ }
1678
+ /** @internal */
1679
+ _splitChunk(chunk, index) {
1680
+ if (chunk.edited && chunk.content.length) {
1681
+ const loc = getLocator(this.original)(index);
1682
+ throw new MagicStringError(`cannot split a chunk that has already been edited (${loc.line}:${loc.column} – "${chunk.original}")`);
1683
+ }
1684
+ const newChunk = chunk.split(index);
1685
+ this.byEnd.set(index, chunk);
1686
+ this.byStart.set(index, newChunk);
1687
+ this.byEnd.set(newChunk.end, newChunk);
1688
+ if (chunk === this.lastChunk) this.lastChunk = newChunk;
1689
+ this.lastSearchedChunk = chunk;
1690
+ return true;
1691
+ }
1692
+ /**
1693
+ * Returns the generated string.
1694
+ */
1695
+ toString() {
1696
+ let str = this.intro;
1697
+ let chunk = this.firstChunk;
1698
+ while (chunk) {
1699
+ str += chunk.toString();
1700
+ chunk = chunk.next;
1701
+ }
1702
+ return str + this.outro;
1703
+ }
1704
+ /**
1705
+ * Returns true if the resulting source is empty (disregarding white space).
1706
+ */
1707
+ isEmpty() {
1708
+ if (this.intro.length && this.intro.trim()) return false;
1709
+ let chunk = this.firstChunk;
1710
+ while (chunk) {
1711
+ if (chunk.intro.length && chunk.intro.trim() || chunk.content.length && chunk.content.trim() || chunk.outro.length && chunk.outro.trim()) return false;
1712
+ chunk = chunk.next;
1713
+ }
1714
+ if (this.outro.length && this.outro.trim()) return false;
1715
+ return true;
1716
+ }
1717
+ length() {
1718
+ let chunk = this.firstChunk;
1719
+ let length = 0;
1720
+ while (chunk) {
1721
+ length += chunk.intro.length + chunk.content.length + chunk.outro.length;
1722
+ chunk = chunk.next;
1723
+ }
1724
+ return length;
1725
+ }
1726
+ /**
1727
+ * Removes empty lines from the start and end.
1728
+ */
1729
+ trimLines() {
1730
+ return this.trim("[\\r\\n]");
1731
+ }
1732
+ /**
1733
+ * Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the start and end.
1734
+ */
1735
+ trim(charType) {
1736
+ return this.trimStart(charType).trimEnd(charType);
1737
+ }
1738
+ /** @internal */
1739
+ trimEndAborted(charType) {
1740
+ const rx = new RegExp(`${charType || "\\s"}+$`);
1741
+ this.outro = this.outro.replace(rx, "");
1742
+ if (this.outro.length) return true;
1743
+ let chunk = this.lastChunk;
1744
+ do {
1745
+ const end = chunk.end;
1746
+ const aborted = chunk.trimEnd(rx);
1747
+ if (chunk.end !== end) {
1748
+ if (this.lastChunk === chunk) this.lastChunk = chunk.next;
1749
+ this.byEnd.set(chunk.end, chunk);
1750
+ this.byStart.set(chunk.next.start, chunk.next);
1751
+ this.byEnd.set(chunk.next.end, chunk.next);
1752
+ }
1753
+ if (aborted) return true;
1754
+ chunk = chunk.previous;
1755
+ } while (chunk);
1756
+ this.intro = this.intro.replace(rx, "");
1757
+ return this.intro.length > 0;
1758
+ }
1759
+ /**
1760
+ * Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the end.
1761
+ */
1762
+ trimEnd(charType) {
1763
+ this.trimEndAborted(charType);
1764
+ return this;
1765
+ }
1766
+ /** @internal */
1767
+ trimStartAborted(charType) {
1768
+ const rx = new RegExp(`^${charType || "\\s"}+`);
1769
+ this.intro = this.intro.replace(rx, "");
1770
+ if (this.intro.length) return true;
1771
+ let chunk = this.firstChunk;
1772
+ do {
1773
+ const end = chunk.end;
1774
+ const aborted = chunk.trimStart(rx);
1775
+ if (chunk.end !== end) {
1776
+ if (chunk === this.lastChunk) this.lastChunk = chunk.next;
1777
+ this.byEnd.set(chunk.end, chunk);
1778
+ this.byStart.set(chunk.next.start, chunk.next);
1779
+ this.byEnd.set(chunk.next.end, chunk.next);
1780
+ }
1781
+ if (aborted) return true;
1782
+ chunk = chunk.next;
1783
+ } while (chunk);
1784
+ this.outro = this.outro.replace(rx, "");
1785
+ return this.outro.length > 0;
1786
+ }
1787
+ /**
1788
+ * Trims content matching `charType` (defaults to `\s`, i.e. whitespace) from the start.
1789
+ */
1790
+ trimStart(charType) {
1791
+ this.trimStartAborted(charType);
1792
+ return this;
1793
+ }
1794
+ /**
1795
+ * Indicates if the string has been changed.
1796
+ */
1797
+ hasChanged() {
1798
+ if (this.intro || this.outro) return true;
1799
+ let outputIndex = 0;
1800
+ let chunk = this.firstChunk;
1801
+ while (chunk) {
1802
+ if (chunk.intro || chunk.outro) return true;
1803
+ if (!chunk.edited && chunk.start === outputIndex) outputIndex = chunk.end;
1804
+ else {
1805
+ if (!this.original.startsWith(chunk.content, outputIndex)) return true;
1806
+ outputIndex += chunk.content.length;
1807
+ }
1808
+ chunk = chunk.next;
1809
+ }
1810
+ return outputIndex !== this.original.length;
1811
+ }
1812
+ /**
1813
+ * Whether the original range [start, end) has had any of its characters
1814
+ * removed. `replace`/`replaceAll` search `original`, so a match can land on
1815
+ * text that is no longer in the output - overwriting it would resurrect the
1816
+ * removed characters, so such matches are skipped instead.
1817
+ *
1818
+ * @internal
1819
+ */
1820
+ _hasRemovedContent(start, end) {
1821
+ let chunk = this.byStart.get(start);
1822
+ if (!chunk) {
1823
+ chunk = this.lastSearchedChunk;
1824
+ const searchForward = start >= chunk.end;
1825
+ while (chunk && !chunk.contains(start)) chunk = searchForward ? this.byStart.get(chunk.end) : this.byEnd.get(chunk.start);
1826
+ if (chunk) this.lastSearchedChunk = chunk;
1827
+ }
1828
+ while (chunk && chunk.start < end) {
1829
+ if (chunk.content === "") return true;
1830
+ chunk = this.byStart.get(chunk.end);
1831
+ }
1832
+ return false;
1833
+ }
1834
+ /** @internal */
1835
+ _replaceRegexp(searchValue, replacement) {
1836
+ function getReplacement(match, str) {
1837
+ if (typeof replacement === "string") return expandReplacement(replacement, match[0], match.index, str, match.slice(1), match.groups);
1838
+ else return match.groups === void 0 ? replacement(match[0], ...match.slice(1), match.index, str) : replacement(match[0], ...match.slice(1), match.index, str, match.groups);
1839
+ }
1840
+ const replaceMatch = (match) => {
1841
+ /* v8 ignore next 2 -- `match.index` is always defined for matches from `matchAll` */
1842
+ if (match.index == null) return false;
1843
+ if (this._hasRemovedContent(match.index, match.index + match[0].length)) return false;
1844
+ const replacement = getReplacement(match, this.original);
1845
+ if (replacement === match[0]) return true;
1846
+ if (match[0].length === 0) this.appendRight(match.index - this.offset, replacement);
1847
+ else this.overwrite(match.index - this.offset, match.index + match[0].length - this.offset, replacement);
1848
+ return true;
1849
+ };
1850
+ if (searchValue.global) {
1851
+ searchValue.lastIndex = 0;
1852
+ for (const match of this.original.matchAll(searchValue)) replaceMatch(match);
1853
+ } else {
1854
+ const match = this.original.match(searchValue);
1855
+ if (match && !replaceMatch(match)) {
1856
+ const global = new RegExp(searchValue.source, `${searchValue.flags}g`);
1857
+ for (const next of this.original.matchAll(global)) if (replaceMatch(next)) break;
1858
+ }
1859
+ }
1860
+ return this;
1861
+ }
1862
+ /** @internal */
1863
+ _replaceString(string, replacement) {
1864
+ const { original } = this;
1865
+ let index = original.indexOf(string);
1866
+ while (index !== -1) {
1867
+ if (this._hasRemovedContent(index, index + string.length)) {
1868
+ index = original.indexOf(string, index + string.length);
1869
+ continue;
1870
+ }
1871
+ if (typeof replacement === "function") replacement = replacement(string, index, original);
1872
+ else replacement = expandReplacement(replacement, string, index, original, [], void 0);
1873
+ if (string !== replacement) {
1874
+ if (string.length === 0) this.appendRight(index - this.offset, replacement);
1875
+ else this.overwrite(index - this.offset, index + string.length - this.offset, replacement);
1876
+ }
1877
+ break;
1878
+ }
1879
+ return this;
1880
+ }
1881
+ /**
1882
+ * String replacement with RegExp or string.
1883
+ */
1884
+ replace(searchValue, replacement) {
1885
+ if (typeof searchValue === "string") return this._replaceString(searchValue, replacement);
1886
+ return this._replaceRegexp(searchValue, replacement);
1887
+ }
1888
+ /** @internal */
1889
+ _replaceAllString(string, replacement) {
1890
+ const { original } = this;
1891
+ const stringLength = string.length;
1892
+ if (stringLength === 0) {
1893
+ for (let index = 0; index <= original.length; index += 1) {
1894
+ if (this._hasRemovedContent(index, index)) continue;
1895
+ const _replacement = typeof replacement === "function" ? replacement("", index, original) : expandReplacement(replacement, "", index, original, [], void 0);
1896
+ if (_replacement !== "") this.appendRight(index - this.offset, _replacement);
1897
+ }
1898
+ return this;
1899
+ }
1900
+ for (let index = original.indexOf(string); index !== -1; index = original.indexOf(string, index + stringLength)) {
1901
+ if (this._hasRemovedContent(index, index + stringLength)) continue;
1902
+ const previous = original.slice(index, index + stringLength);
1903
+ const _replacement = typeof replacement === "function" ? replacement(previous, index, original) : expandReplacement(replacement, previous, index, original, [], void 0);
1904
+ if (previous !== _replacement) this.overwrite(index - this.offset, index + stringLength - this.offset, _replacement);
1905
+ }
1906
+ return this;
1907
+ }
1908
+ /**
1909
+ * Same as `s.replace`, but replace all matched strings instead of just one.
1910
+ */
1911
+ replaceAll(searchValue, replacement) {
1912
+ if (typeof searchValue === "string") return this._replaceAllString(searchValue, replacement);
1913
+ if (!searchValue.global) throw new MagicStringError("replaceAll() requires a global RegExp");
1914
+ return this._replaceRegexp(searchValue, replacement);
1915
+ }
1916
+ };
1917
+ //#endregion
1918
+ //#region src/core/env.ts
1919
+ /**
1920
+ * Whether dev checks and messages run (PLAN §1, principle 4). Vite replaces
1921
+ * `import.meta.env.DEV` with a literal, so a production build drops every
1922
+ * `if (DEV)` branch and the messages in it (tests/production.test.ts checks
1923
+ * the bundle). Without Vite (plain Node, wrangler's esbuild, a CDN) `env` is
1924
+ * undefined, and the checks are off instead of throwing.
1925
+ */
1926
+ const DEV = !!import.meta.env?.DEV;
1927
+ /**
1928
+ * Whether normalising a machine checks its config: in development, and in
1929
+ * the plugin's build-time compilation (M7), which runs this normaliser in
1930
+ * Node, where `import.meta.env` is undefined, and turns its errors into
1931
+ * build errors. Only {@link checkAlways} assigns it, and only the plugin
1932
+ * imports that, so an app's bundle never does and the bundler reads it as
1933
+ * `DEV`, dropping the checks from production builds as before.
1934
+ */
1935
+ let CHECK = DEV;
1936
+ /** Turns the normaliser's checks on in this process: the plugin's build side only. */
1937
+ const checkAlways = () => void (CHECK = true);
1938
+ /**
1939
+ * Dev-only bookkeeping: how deep the running code is inside actions, so a
1940
+ * context write elsewhere can be named (D5). Only read and written behind
1941
+ * `DEV`, so production builds drop it.
1942
+ */
1943
+ const dev = { acting: 0 };
1944
+ //#endregion
1945
+ //#region src/core/dev.ts
1946
+ /**
1947
+ * Dev errors and warnings (PLAN §4.11): each names the machine, what went
1948
+ * wrong and the fix, behind a code. Every caller sits behind `if (DEV)`, so
1949
+ * none of this reaches a production bundle.
1950
+ */
1951
+ /** `[machine:CODE] name: message`. */
1952
+ const message = (code, machine, text) => `[machine:${code}] ${machine}: ${text}`;
1953
+ function fail(code, machine, text) {
1954
+ throw new Error(message(code, machine, text));
1955
+ }
1956
+ /** Edit distance, for "did you mean". */
1957
+ function distance(a, b) {
1958
+ let row = Array.from({ length: b.length + 1 }, (_, i) => i);
1959
+ for (let i = 1; i <= a.length; i++) {
1960
+ const next = [i];
1961
+ for (let j = 1; j <= b.length; j++) next[j] = Math.min(row[j] + 1, next[j - 1] + 1, row[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
1962
+ row = next;
1963
+ }
1964
+ return row[b.length];
1965
+ }
1966
+ /** `; did you mean "closed"?` for the nearest of `options`, or "" when none is close. */
1967
+ function suggest(name, options) {
1968
+ let best = "";
1969
+ let score = Math.max(2, Math.floor(name.length / 3)) + 1;
1970
+ for (const option of options) {
1971
+ const d = distance(name.toLowerCase(), option.toLowerCase());
1972
+ if (d < score) [best, score] = [option, d];
1973
+ }
1974
+ return best ? `; did you mean "${best}"?` : "";
1975
+ }
1976
+ /** Keys users bring from XState and Zag, with what they are called here. */
1977
+ const RENAMED = {
1978
+ target: "to",
1979
+ guard: "if",
1980
+ cond: "if",
1981
+ actions: "do",
1982
+ effects: "effect",
1983
+ onEntry: "entry",
1984
+ onExit: "exit"
1985
+ };
1986
+ /** Fails on a key `config` should not have, naming the right one. */
1987
+ function checkKeys(config, allowed, machine, where) {
1988
+ for (const key of Object.keys(config)) {
1989
+ if (allowed.includes(key)) continue;
1990
+ fail("UNKNOWN_KEY", machine, `${where} has an unknown key "${key}"${RENAMED[key] ? `; use "${RENAMED[key]}".` : suggest(key, allowed) || "."} Allowed: ${allowed.join(", ")}.`);
1991
+ }
1992
+ }
1993
+ /** The DOM's spellings a part may not use, and React's, which every glue maps to its framework's. */
1994
+ const SPELLED = {
1995
+ class: "className",
1996
+ for: "htmlFor"
1997
+ };
1998
+ /**
1999
+ * Dev only: each part's handlers send an event some state handles, or are
2000
+ * functions; its keys are spelled as React spells them (`@menapo/glue/naming`).
2001
+ * Checked by the normaliser, so the plugin's compilation fails the build on
2002
+ * them, and when a compiled machine loads in development.
2003
+ */
2004
+ function checkParts(config, name, handled) {
2005
+ const parts = config.parts ?? {};
2006
+ for (const part of Object.keys(parts)) for (const key of Object.keys(parts[part])) {
2007
+ const value = parts[part][key];
2008
+ if (/^on[A-Z]/.test(key)) {
2009
+ if (typeof value !== "string" && typeof value !== "function") fail("BAD_HANDLER", name, `part "${part}" has ${key}: ${String(value)}; an on* key takes an event name or a function of (view, event).`);
2010
+ if (typeof value === "string" && !handled.has(value)) fail("UNKNOWN_EVENT", name, `part "${part}" sends "${value}" from ${key}, which no state handles${suggest(value, handled) || "."} Handle it in a state's \`on\`, or send another event.`);
2011
+ } else if (key in SPELLED || /^on[a-z]/.test(key)) fail("PART_KEY", name, `part "${part}" has "${key}"; parts use React's spellings, which each framework's glue maps to its own, so write "${SPELLED[key] ?? `on${key[2].toUpperCase()}${key.slice(3)}`}"${key in SPELLED ? "" : " with the event's words capitalised (onKeyDown, onDoubleClick)"}.`);
2012
+ }
2013
+ }
2014
+ //#endregion
2015
+ //#region src/core/effects.ts
2016
+ const AFTER = { type: "machine.after" };
2017
+ /** An `after` timer: setTimeout's extra arguments spare a closure per timer. */
2018
+ function fire(instance, rules) {
2019
+ if (!instance.b) instance.go(rules, AFTER);
2020
+ }
2021
+ /** `[state, cleanup or timer, …]`: what an instance keeps while its states are active. */
2022
+ const keep = (instance, at, item) => void (instance.r ??= []).push(at, item);
2023
+ /** `instance.b` once stopped (the interpreter's STOPPED). */
2024
+ const STOPPED = 2;
2025
+ /**
2026
+ * Sets the timers and starts the effects of `states`, entered in the event
2027
+ * that just settled and still active, while the instance is busy: what an
2028
+ * effect sends runs once all have started. The glue's component calls it
2029
+ * only once the framework has committed the DOM, and never on a server.
2030
+ *
2031
+ * An effect that stops the instance ends it: nothing after it starts, and
2032
+ * the cleanup it returns runs at once. An effect that throws does not keep
2033
+ * the others from starting: the event runs to completion and tells its
2034
+ * subscribers, then the first error is thrown.
2035
+ */
2036
+ function activate(instance, states) {
2037
+ let thrown;
2038
+ for (const at of states) {
2039
+ if (instance.b === STOPPED) break;
2040
+ if (at.af) for (const [ms, rules] of at.af) keep(instance, at, setTimeout(fire, ms, instance, rules));
2041
+ if (at.ef) for (const effect of at.ef) {
2042
+ if (instance.b === STOPPED) break;
2043
+ try {
2044
+ const cleanup = effect(instance);
2045
+ if (typeof cleanup === "function") {
2046
+ if (instance.b === STOPPED) cleanup();
2047
+ else keep(instance, at, cleanup);
2048
+ }
2049
+ } catch (error) {
2050
+ thrown ??= [error];
2051
+ }
2052
+ }
2053
+ }
2054
+ if (thrown) {
2055
+ instance.settle();
2056
+ throw thrown[0];
2057
+ }
2058
+ }
2059
+ /**
2060
+ * Stops what `instance` keeps for state `at`, latest first, and forgets it
2061
+ * if it waits to start; without `at` (the instance stops), everything. A
2062
+ * cleanup that throws does not keep the others from running: the first
2063
+ * error is thrown once all have.
2064
+ */
2065
+ function release(instance, at) {
2066
+ const { r, x } = instance;
2067
+ let thrown;
2068
+ if (!at) instance.r = instance.x = void 0;
2069
+ else x?.splice(x.indexOf(at) >>> 0, 1);
2070
+ if (r) {
2071
+ for (let k = r.length - 2; k >= 0; k -= 2) if (!at || r[k] === at) {
2072
+ const item = r.splice(k, 2)[1];
2073
+ try {
2074
+ if (typeof item === "function") item();
2075
+ else clearTimeout(item);
2076
+ } catch (error) {
2077
+ thrown ??= [error];
2078
+ }
2079
+ }
2080
+ }
2081
+ if (thrown) throw thrown[0];
2082
+ }
2083
+ //#endregion
2084
+ //#region ../glue/src/record.ts
2085
+ /**
2086
+ * The record kit's state lives in (machine PLAN D5, spike S1; store PLAN
2087
+ * D3): values in an array behind one accessor per field, defined once per
2088
+ * key set on a class's prototype, and a change mask with one bit per field.
2089
+ * A read is a few ns, and no proxy or snapshot is involved. `@menapo/machine`
2090
+ * lays out context and props by `bit`, `@menapo/store` its stores by `bit`
2091
+ * and `defineAccessors`, so the bit layout exists once. Machine writes its
2092
+ * accessor loop out: the call cost its toggle's size gate 12 B.
2093
+ *
2094
+ * Read tracking stays with each owner: machine records one mask per
2095
+ * derived value, the store one mask per store a derived value read.
2096
+ */
2097
+ /** The bit of field `n`: fields past the 31st share the last bit. */
2098
+ const bit = (n) => 1 << Math.min(n, 31);
2099
+ //#endregion
2100
+ //#region src/core/props.ts
2101
+ /** Marks a `bindable()` value and carries its {@link Control}. */
2102
+ const BOUND = Symbol();
2103
+ /**
2104
+ * Records each declared prop's default, and hands a `bindable()` one to its
2105
+ * {@link Control}: what the normaliser does, and a compiled machine at load.
2106
+ */
2107
+ function bind(tables, props) {
2108
+ tables.pn.forEach((prop, j) => {
2109
+ const value = props[prop];
2110
+ const control = value?.[BOUND];
2111
+ tables.pd.push(control ? value.value : value);
2112
+ if (control) (tables.k = control).bind(tables, j, prop, value, tables.Q.prototype);
2113
+ else tables.pl.push(j);
2114
+ });
2115
+ }
2116
+ /**
2117
+ * Takes the owner's props; a missing plain prop falls back to its default.
2118
+ * From `setProps` (`queue`): taken once the event being handled is done,
2119
+ * as one event of their own when none is, never once stopped.
2120
+ */
2121
+ function own(instance, owner, queue) {
2122
+ if (queue) {
2123
+ if (instance.b !== 2) {
2124
+ instance.n = owner;
2125
+ if (!instance.b) instance.go(void 0, instance.event);
2126
+ }
2127
+ return;
2128
+ }
2129
+ const { M, props } = instance;
2130
+ const previous = instance.o;
2131
+ instance.o = owner;
2132
+ for (const j of M.pl) {
2133
+ const given = owner[M.pn[j]];
2134
+ const value = given === void 0 ? M.pd[j] : given;
2135
+ if (props.v[j] !== value) {
2136
+ props.v[j] = value;
2137
+ props.m |= bit(1 + M.nm.length - M.pn.length + j);
2138
+ }
2139
+ }
2140
+ if (instance.s) M.k?.own(instance, previous);
2141
+ }
2142
+ //#endregion
2143
+ //#region src/core/store.ts
2144
+ /**
2145
+ * Context and props as objects with one accessor per field, defined once per
2146
+ * machine on a prototype (D5, spike S1), with the bit layout of
2147
+ * `@menapo/glue`'s record, which the store shares: a read is a few ns, a write that
2148
+ * changes the value sets the field's bit in a change mask, and no proxy or
2149
+ * snapshot is involved. Reads also record their bits while a derived value
2150
+ * is being computed, so it is recomputed only when what it read changes.
2151
+ */
2152
+ /** Bits read since the innermost derived value started computing. */
2153
+ let reads = 0;
2154
+ const track = (bits) => {
2155
+ reads |= bits;
2156
+ };
2157
+ /** Dev only: the name of the machine a store belongs to, for messages. */
2158
+ const nameOf$1 = (store) => store.i.M.name;
2159
+ /** A class with an accessor per name; `get(k)` and `set(k)` make each one's pair. */
2160
+ function accessors(names, get, set) {
2161
+ class Values {
2162
+ v;
2163
+ m = 0;
2164
+ i;
2165
+ constructor(values, instance) {
2166
+ this.v = values;
2167
+ this.i = instance;
2168
+ }
2169
+ }
2170
+ names.forEach((name, k) => {
2171
+ Object.defineProperty(Values.prototype, name, {
2172
+ configurable: true,
2173
+ get: get(k),
2174
+ set: set?.(k, name)
2175
+ });
2176
+ });
2177
+ return Values;
2178
+ }
2179
+ /**
2180
+ * Context (`writable`) or props, over `this.v[k]`. Context is written by
2181
+ * actions only (D5: anywhere else throws in dev); props are the owner's,
2182
+ * and only bindable ones (which `bindable` redefines) can be written.
2183
+ */
2184
+ function storeClass(names, first, writable) {
2185
+ const Values = accessors(names, (k) => {
2186
+ const mask = bit(first + k);
2187
+ return function() {
2188
+ reads |= mask;
2189
+ return this.v[k];
2190
+ };
2191
+ }, (k, name) => {
2192
+ const mask = bit(first + k);
2193
+ return writable ? function(value) {
2194
+ if (DEV && !dev.acting) fail("CONTEXT_WRITE", nameOf$1(this), `context.${name} was written outside an action. Write context in a transition's \`do\`, \`entry\` or \`exit\`, and send an event from here instead.`);
2195
+ if (this.v[k] !== value) {
2196
+ this.v[k] = value;
2197
+ this.m |= mask;
2198
+ }
2199
+ } : DEV ? function() {
2200
+ fail("PROPS_WRITE", nameOf$1(this), `props.${name} belongs to the owner and is read-only. Declare it \`${name}: bindable(…)\` to let actions write it.`);
2201
+ } : void 0;
2202
+ });
2203
+ if (writable) Values.s = (machine, given) => {
2204
+ const values = machine.df.slice();
2205
+ if (given) for (const key of Object.keys(given)) {
2206
+ const at = machine.nm.indexOf(key);
2207
+ if (DEV && !(at >= 0 && at < values.length)) fail("UNKNOWN_FIELD", machine.name, `start() was given context.${key}, which the machine does not declare; add it to \`context\`.`);
2208
+ values[at] = given[key];
2209
+ }
2210
+ return values;
2211
+ };
2212
+ return Values;
2213
+ }
2214
+ /**
2215
+ * The derived values' class: each getter returns the cached value while no
2216
+ * field it read has changed, else recomputes it with its reads recorded.
2217
+ * A value computed while a field it read is changed but not yet told is
2218
+ * recomputed on every read until the notification forgets it.
2219
+ */
2220
+ function derivedClass(names, fns) {
2221
+ const Derived = accessors(names, (k) => function() {
2222
+ const c = this.v;
2223
+ const instance = this.i;
2224
+ const read = c[2 * k];
2225
+ if (read === void 0 || read & instance.live()) {
2226
+ const outer = reads;
2227
+ reads = 0;
2228
+ c[2 * k + 1] = fns[k](instance);
2229
+ c[2 * k] = reads;
2230
+ reads |= outer;
2231
+ } else reads |= read;
2232
+ return c[2 * k + 1];
2233
+ });
2234
+ /** Forgets the values that read what `changed` says changed: the interpreter calls it as it notifies. */
2235
+ Derived.prototype.f = function(changed) {
2236
+ const c = this.v;
2237
+ for (let k = 0; k < c.length; k += 2) if (c[k] & changed) c[k] = void 0;
2238
+ };
2239
+ return Derived;
2240
+ }
2241
+ //#endregion
2242
+ //#region src/core/machine.ts
2243
+ /**
2244
+ * `defineMachine`: the config normalised once into tables (PLAN §5.2).
2245
+ * Each state becomes a record linked to its parent, numbered depth first
2246
+ * (so an ancestor's index is below its descendants'), with the leaf it
2247
+ * settles in when entered, its entry/exit/effect lists, `after` timers,
2248
+ * `always` rules and one `Map` from event name to what it does, its
2249
+ * ancestors' rules folded in after its own (D20).
2250
+ */
2251
+ const STATE_KEYS = [
2252
+ "initial",
2253
+ "states",
2254
+ "on",
2255
+ "entry",
2256
+ "exit",
2257
+ "effect",
2258
+ "after",
2259
+ "always",
2260
+ "type",
2261
+ "done"
2262
+ ];
2263
+ const MACHINE_KEYS = [
2264
+ "name",
2265
+ "context",
2266
+ "props",
2267
+ "events",
2268
+ "derived",
2269
+ "actions",
2270
+ "guards",
2271
+ "effects",
2272
+ "initial",
2273
+ "states",
2274
+ "on",
2275
+ "parts",
2276
+ "expose"
2277
+ ];
2278
+ const RULE_KEYS = [
2279
+ "to",
2280
+ "if",
2281
+ "do"
2282
+ ];
2283
+ const list = (value) => value === void 0 || Array.isArray(value) ? value : [value];
2284
+ const NOTHING = /* @__PURE__ */ new Map();
2285
+ /**
2286
+ * False while the plugin's compilation (M7) normalises at build time, so the
2287
+ * tables it emits hold data only and the features' runtimes wire them when
2288
+ * the compiled machine loads. Only {@link prepared} assigns it, and only the
2289
+ * plugin imports that, so an app's bundle reads it as `true`.
2290
+ */
2291
+ let wiring = true;
2292
+ /** `normalise(config)` without the features' wiring: the plugin's build side only. */
2293
+ function prepared(config) {
2294
+ wiring = false;
2295
+ try {
2296
+ return normalise(config);
2297
+ } finally {
2298
+ wiring = true;
2299
+ }
2300
+ }
2301
+ /** Config → tables: what `defineMachine` does at runtime, and the plugin at build time ({@link prepared}). */
2302
+ function normalise(config) {
2303
+ const name = CHECK ? config.name ?? "machine" : "";
2304
+ const byPath = /* @__PURE__ */ new Map();
2305
+ const states = [];
2306
+ const root = {
2307
+ i: -1,
2308
+ path: ""
2309
+ };
2310
+ /** The raw config of `root`, then of each state. */
2311
+ const raws = [config];
2312
+ if (CHECK) checkKeys(config, MACHINE_KEYS, name, "the machine");
2313
+ const where = (from) => from === root ? "the machine" : `"${from.path}"`;
2314
+ const below = (up, key) => up === root ? key : `${up.path}.${key}`;
2315
+ /** Dev only: the state and target being resolved, for `find`'s message; set before each call. */
2316
+ let asked = [root, ""];
2317
+ const find = (path) => {
2318
+ const found = byPath.get(path);
2319
+ if (CHECK && !found && asked[1] !== void 0) {
2320
+ const [from, target] = asked;
2321
+ const last = (p) => p.slice(p.lastIndexOf(".") + 1);
2322
+ 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(", ")}.`}`);
2323
+ }
2324
+ return found;
2325
+ };
2326
+ /**
2327
+ * Named implementations (D8), in one map: the types keep each kind's
2328
+ * names apart, and development rejects a name used in the wrong kind or
2329
+ * declared in two.
2330
+ */
2331
+ const named = {
2332
+ ...config.actions,
2333
+ ...config.guards,
2334
+ ...config.effects
2335
+ };
2336
+ if (CHECK) {
2337
+ const seen = /* @__PURE__ */ new Set();
2338
+ for (const kind of [
2339
+ "actions",
2340
+ "guards",
2341
+ "effects"
2342
+ ]) for (const key of Object.keys(config[kind] ?? {})) {
2343
+ 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.`);
2344
+ seen.add(key);
2345
+ }
2346
+ }
2347
+ /** Functions as written, or the ones they name. */
2348
+ const fns = (value) => value && list(value).map((f) => typeof f === "string" ? named[f] : f);
2349
+ /** Dev only: each name in `value` is one of `config[kind]`. */
2350
+ const known = (value, kind, from) => {
2351
+ 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.`);
2352
+ };
2353
+ /** Numbers `up`'s children depth first, then sets the leaf entering `up` settles in. */
2354
+ const walk = (up, raw) => {
2355
+ const children = raw.states;
2356
+ if (children) {
2357
+ for (const key of Object.keys(children)) {
2358
+ const child = children[key];
2359
+ const state = {
2360
+ i: states.length,
2361
+ path: below(up, key),
2362
+ up,
2363
+ fi: child.type === "final"
2364
+ };
2365
+ if (CHECK) {
2366
+ if (/[.#]/.test(key)) fail("STATE_NAME", name, `state "${key}" has a "." or "#" in its name; targets use them as separators, so rename it.`);
2367
+ checkKeys(child, STATE_KEYS, name, `state "${state.path}"`);
2368
+ const type = child.type;
2369
+ 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\`).`);
2370
+ }
2371
+ byPath.set(state.path, state);
2372
+ states.push(state);
2373
+ raws.push(child);
2374
+ walk(state, child);
2375
+ }
2376
+ 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(", ")}.`);
2377
+ }
2378
+ if (CHECK) asked = [up, raw.initial];
2379
+ up.lf = children ? find(below(up, raw.initial))?.lf : up;
2380
+ };
2381
+ walk(root, config);
2382
+ /** `#path` from the root, `.child` below `from`, else a sibling of `from` or of its nearest ancestor that has one. */
2383
+ const resolve = (target, from) => {
2384
+ if (CHECK) asked = [from, target];
2385
+ if (target[0] === "#") return find(target.slice(1));
2386
+ if (target[0] === ".") return find(below(from, target.slice(1)));
2387
+ for (let up = from.up; up && up !== root; up = up.up) {
2388
+ const found = byPath.get(`${up.path}.${target}`);
2389
+ if (found) return found;
2390
+ }
2391
+ return find(target);
2392
+ };
2393
+ const rules = (transitions, from) => list(transitions).map((item) => {
2394
+ 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 }.`);
2395
+ const rule = typeof item === "string" ? { to: item } : typeof item === "function" ? { do: item } : item;
2396
+ if (CHECK) {
2397
+ checkKeys(rule, RULE_KEYS, name, `a transition in ${where(from)}`);
2398
+ known(rule.if, "guards", from);
2399
+ known(rule.do, "actions", from);
2400
+ if (rule.to === "") resolve("", from);
2401
+ }
2402
+ return {
2403
+ to: rule.to && resolve(rule.to, from),
2404
+ if: rule.if && fns(rule.if)[0],
2405
+ do: fns(rule.do)
2406
+ };
2407
+ });
2408
+ const handled = /* @__PURE__ */ new Set();
2409
+ [root, ...states].forEach((state, k) => {
2410
+ const raw = raws[k];
2411
+ const up = state.up;
2412
+ const inherited = up?.on ?? NOTHING;
2413
+ let on = state.fi ? NOTHING : inherited;
2414
+ if (raw.on && !state.fi) {
2415
+ const folded = on = new Map(inherited);
2416
+ for (const type of Object.keys(raw.on)) {
2417
+ if (CHECK) {
2418
+ const declared = config.events;
2419
+ 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\`.`);
2420
+ handled.add(type);
2421
+ }
2422
+ const mine = rules(raw.on[type], state);
2423
+ const outer = inherited.get(type);
2424
+ folded.set(type, {
2425
+ ev: outer?.ev ?? { type },
2426
+ ru: outer ? mine.concat(outer.ru) : mine
2427
+ });
2428
+ }
2429
+ }
2430
+ if (CHECK) {
2431
+ 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\`.`);
2432
+ }
2433
+ if (CHECK && state.fi) {
2434
+ const region = up !== root && (raws[up.up.i + 1]?.type)?.kind;
2435
+ 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"\`.`);
2436
+ for (const key of [
2437
+ "states",
2438
+ "on",
2439
+ "effect",
2440
+ "after",
2441
+ "always"
2442
+ ]) if (raw[key]) fail("FINAL_STATE", name, `final state "${state.path}" has \`${key}\`, but the machine stops there; move it to another state.`);
2443
+ }
2444
+ const always = raw.always && rules(raw.always, state);
2445
+ const after = raw.after;
2446
+ state.on = on;
2447
+ if (CHECK) {
2448
+ known(raw.entry, "actions", state);
2449
+ known(raw.exit, "actions", state);
2450
+ known(raw.effect, "effects", state);
2451
+ }
2452
+ state.en = fns(raw.entry);
2453
+ state.ex = fns(raw.exit);
2454
+ state.ef = fns(raw.effect);
2455
+ state.af = after && Object.keys(after).map((ms) => {
2456
+ if (CHECK && !(Number(ms) >= 0)) fail("BAD_DELAY", name, `"${state.path}" has \`after: { ${ms}: … }\`; a delay is a number of milliseconds.`);
2457
+ return [+ms, rules(after[ms], state)];
2458
+ });
2459
+ state.al = always && up?.al ? always.concat(up.al) : always || up?.al;
2460
+ });
2461
+ if (CHECK) checkParts(config, name, handled);
2462
+ const context = config.context ?? {};
2463
+ const fields = Object.keys(context);
2464
+ const defaults = Object.values(context);
2465
+ if (CHECK) {
2466
+ for (const value of defaults) if (Array.isArray(value) || Object.getPrototypeOf(value ?? 0) === Object.prototype) Object.freeze(value);
2467
+ }
2468
+ const props = config.props ?? {};
2469
+ const propNames = Object.keys(props);
2470
+ const Props = propNames.length ? storeClass(propNames, 1 + fields.length, false) : void 0;
2471
+ const derived = config.derived;
2472
+ const tables = {
2473
+ ss: states,
2474
+ i0: root.lf,
2475
+ df: defaults,
2476
+ C: fields.length ? storeClass(fields, 1, true) : void 0,
2477
+ pn: propNames,
2478
+ pd: [],
2479
+ Q: Props,
2480
+ pl: [],
2481
+ nm: fields.concat(propNames),
2482
+ D: derived && derivedClass(Object.keys(derived), Object.values(derived)),
2483
+ k: void 0,
2484
+ bd: [],
2485
+ c: config,
2486
+ r: normalise,
2487
+ A: activate,
2488
+ X: release,
2489
+ P: own
2490
+ };
2491
+ if (CHECK) Object.assign(tables, {
2492
+ name,
2493
+ handled
2494
+ });
2495
+ bind(tables, props);
2496
+ for (const raw of raws) raw.type?.f?.(tables, raws, rules);
2497
+ return tables;
2498
+ }
2499
+ Object.freeze({});
2500
+ function run(fns, view) {
2501
+ if (fns) {
2502
+ if (DEV) dev.acting++;
2503
+ for (const fn of fns) fn(view);
2504
+ if (DEV) dev.acting--;
2505
+ }
2506
+ }
2507
+ //#endregion
2508
+ //#region src/core/bindable.ts
2509
+ /**
2510
+ * `bindable`: a prop the owner may control or leave to the machine (D9, as
2511
+ * spike S2 refined it). One transition serves both modes:
2512
+ *
2513
+ * - uncontrolled, the machine moves or stores the value and tells the owner
2514
+ * through `on…Change`;
2515
+ * - controlled, a transition that would change the value runs its actions
2516
+ * when the event is handled and asks the owner; only the move waits. When
2517
+ * the owner's new value arrives, the machine takes the waiting transition's
2518
+ * target, and the target's entry sees the event that asked. Props that do
2519
+ * not grant it drop it (a veto); an owner that changes the value unasked
2520
+ * moves the machine to the matching state.
2521
+ *
2522
+ * All of it lives here and reaches the core only through the
2523
+ * {@link Control} a `bindable()` value carries, so an app that never imports
2524
+ * `bindable` ships none of it.
2525
+ */
2526
+ /** What entry actions see when the owner moves a bound boolean unasked. */
2527
+ const PROPS = { type: "machine.props" };
2528
+ /**
2529
+ * A controlled-or-uncontrolled prop: `open: bindable(false, { states: {
2530
+ * true: "open", false: "closed" } })` gives the owner `open`, `defaultOpen`
2531
+ * and `onOpenChange`; `value: bindable("")` is data that actions write as
2532
+ * `props.value = next`.
2533
+ */
2534
+ const bindable = (value, options) => ({
2535
+ [BOUND]: control,
2536
+ value,
2537
+ ...options
2538
+ });
2539
+ const capital = (name) => name[0].toUpperCase() + name.slice(1);
2540
+ /** True when leaf `at` is inside `state`. */
2541
+ function inside$1(at, state) {
2542
+ for (; at; at = at.up) if (at === state) return true;
2543
+ return false;
2544
+ }
2545
+ const bounds = (machine) => machine.bd;
2546
+ const control = {
2547
+ bind(machine, prop, name, bindable, proto) {
2548
+ const { states, changed } = bindable;
2549
+ const find = (path) => {
2550
+ const state = machine.ss.find((s) => s.path === path);
2551
+ 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(", ")}.`);
2552
+ return state;
2553
+ };
2554
+ if (CHECK && changed !== void 0 && !machine.handled.has(changed)) {
2555
+ const handled = [...machine.handled];
2556
+ 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.`);
2557
+ }
2558
+ const b = {
2559
+ prop,
2560
+ name,
2561
+ bit: bit(1 + machine.nm.length - machine.pn.length + prop),
2562
+ default: `default${capital(name)}`,
2563
+ change: `on${capital(name)}Change`,
2564
+ yes: states && find(states.true),
2565
+ no: states && find(states.false),
2566
+ changed
2567
+ };
2568
+ bounds(machine).push(b);
2569
+ Object.defineProperty(proto, name, {
2570
+ get() {
2571
+ const instance = this.i;
2572
+ track(b.bit);
2573
+ const given = instance.o[name];
2574
+ if (given !== void 0) return given;
2575
+ if (!b.yes) return this.v[prop];
2576
+ track(1);
2577
+ return inside$1(instance.s, b.yes);
2578
+ },
2579
+ set(value) {
2580
+ const owner = this.i.o;
2581
+ if (DEV) {
2582
+ 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.`);
2583
+ 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.`);
2584
+ }
2585
+ const controlled = owner[name] !== void 0;
2586
+ if ((controlled ? owner[name] : this.v[prop]) === value) return;
2587
+ if (!controlled) {
2588
+ this.v[prop] = value;
2589
+ this.m |= b.bit;
2590
+ }
2591
+ owner[b.change]?.(value);
2592
+ }
2593
+ });
2594
+ },
2595
+ initial(instance, leaf) {
2596
+ const owner = instance.o;
2597
+ for (const b of bounds(instance.M)) {
2598
+ const given = owner[b.name] !== void 0 ? owner[b.name] : owner[b.default];
2599
+ if (given === void 0) continue;
2600
+ if (!b.yes) instance.props.v[b.prop] = given;
2601
+ else if (!!given !== inside$1(leaf, b.yes)) leaf = (given ? b.yes : b.no).lf;
2602
+ }
2603
+ return leaf;
2604
+ },
2605
+ hold(instance, rule) {
2606
+ for (const b of bounds(instance.M)) {
2607
+ if (!b.yes || instance.o[b.name] === void 0) continue;
2608
+ const next = inside$1(rule.to.lf, b.yes);
2609
+ if (inside$1(instance.s, b.yes) === next) continue;
2610
+ run(rule.do, instance);
2611
+ instance.p = {
2612
+ to: rule.to,
2613
+ event: instance.event,
2614
+ b
2615
+ };
2616
+ instance.o[b.change]?.(next);
2617
+ return true;
2618
+ }
2619
+ return false;
2620
+ },
2621
+ moved(instance, from) {
2622
+ for (const b of bounds(instance.M)) {
2623
+ if (!b.yes || instance.o[b.name] !== void 0) continue;
2624
+ const now = inside$1(instance.s, b.yes);
2625
+ if (now !== inside$1(from, b.yes)) {
2626
+ instance.props.m |= b.bit;
2627
+ instance.o[b.change]?.(now);
2628
+ }
2629
+ }
2630
+ },
2631
+ own(instance, previous) {
2632
+ for (const b of bounds(instance.M)) {
2633
+ const value = instance.o[b.name];
2634
+ if (value !== previous[b.name]) {
2635
+ instance.props.m |= b.bit;
2636
+ if (b.changed && value !== void 0) instance.dispatch({
2637
+ type: b.changed,
2638
+ value
2639
+ });
2640
+ }
2641
+ if (!b.yes) continue;
2642
+ const pending = instance.p?.b === b ? instance.p : void 0;
2643
+ if (pending) instance.p = void 0;
2644
+ if (value === void 0 || !!value === inside$1(instance.s, b.yes)) continue;
2645
+ if (pending && inside$1(pending.to.lf, b.yes) === !!value) {
2646
+ instance.event = pending.event;
2647
+ instance.move(pending.to, void 0);
2648
+ } else {
2649
+ instance.event = PROPS;
2650
+ instance.move(value ? b.yes : b.no, void 0);
2651
+ }
2652
+ }
2653
+ }
2654
+ };
2655
+ //#endregion
2656
+ //#region src/features/marks.ts
2657
+ /** The feature's mark on a raw state config, if it has one. */
2658
+ const markOf = (raw) => typeof raw?.type === "object" ? raw.type : void 0;
2659
+ //#endregion
2660
+ //#region src/features/records.ts
2661
+ const records = /* @__PURE__ */ new WeakMap();
2662
+ const recorded = (instance, history) => records.get(instance)?.get(history);
2663
+ function record(instance, history, states) {
2664
+ let map = records.get(instance);
2665
+ if (!map) records.set(instance, map = /* @__PURE__ */ new Map());
2666
+ map.set(history, states);
2667
+ }
2668
+ //#endregion
2669
+ //#region src/features/resume.ts
2670
+ function wireHistory(tables) {
2671
+ const states = tables.ss;
2672
+ const histories = states.filter((s) => s.h !== void 0);
2673
+ /** History state → the state its parent enters when nothing is recorded yet: its initial child. */
2674
+ const initial = /* @__PURE__ */ new Map();
2675
+ for (const h of histories) {
2676
+ const parent = h.up;
2677
+ let child = parent.lf;
2678
+ while (child.up !== parent) child = child.up;
2679
+ initial.set(h, child);
2680
+ const deep = h.h;
2681
+ const keep = (view) => {
2682
+ const leaf = view.s;
2683
+ let child = leaf;
2684
+ while (child.up !== parent) child = child.up;
2685
+ record(view, h, [deep ? leaf : child]);
2686
+ };
2687
+ parent.ex = [keep, ...parent.ex ?? []];
2688
+ }
2689
+ const all = /* @__PURE__ */ new Set();
2690
+ for (const state of [states[0].up, ...states]) {
2691
+ for (const handler of state.on.values()) for (const rule of handler.ru) all.add(rule);
2692
+ for (const rule of state.al ?? []) all.add(rule);
2693
+ for (const [, rules] of state.af ?? []) for (const rule of rules) all.add(rule);
2694
+ }
2695
+ for (const rule of all) {
2696
+ const h = rule.to;
2697
+ if (!h || !initial.has(h)) continue;
2698
+ const guard = rule.if;
2699
+ const routed = rule;
2700
+ routed.h = h;
2701
+ routed.if = (view) => (!guard || guard(view)) && !!(routed.to = recorded(view, h)?.[0] ?? initial.get(h));
2702
+ }
2703
+ }
2704
+ //#endregion
2705
+ //#region src/features/history.ts
2706
+ /**
2707
+ * `history()` (PLAN §4.9): a pseudo-state that stands for where its parent
2708
+ * was when last exited. A transition to it enters that again: the parent's
2709
+ * active child (shallow, its own initial states below it) or its active
2710
+ * leaves (`history("deep")`); before the parent was ever exited, the
2711
+ * parent's initial state. It is never active itself.
2712
+ *
2713
+ * Its mark carries what prepares a machine's tables, which the normaliser
2714
+ * calls when it meets the mark: each history state is marked (`h`: deep or
2715
+ * not) and checked; `resume.ts` wires them. Importing it has no effect. A
2716
+ * machine with parallel states leaves both to `parallel`'s configurations,
2717
+ * which read the same marker and the same records. A compiled machine (M7)
2718
+ * has its tables prepared at build time, so it ships `resume.ts` alone.
2719
+ */
2720
+ /** The machines whose history states are prepared. */
2721
+ const done = /* @__PURE__ */ new WeakSet();
2722
+ /** Prepares a machine's history states, then wires them: what the marks carry. */
2723
+ const prepare$1 = (tables, raws) => {
2724
+ if (done.has(tables) || raws.some((raw) => markOf(raw)?.kind === "parallel")) return;
2725
+ done.add(tables);
2726
+ tables.ss.forEach((state, k) => {
2727
+ const mark = markOf(raws[k + 1]);
2728
+ if (mark?.kind !== "history") return;
2729
+ const parent = state.up;
2730
+ if (CHECK) {
2731
+ 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.`);
2732
+ 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\`.`);
2733
+ }
2734
+ state.h = !!mark.deep;
2735
+ });
2736
+ if (wiring) wireHistory(tables);
2737
+ };
2738
+ const SHALLOW = /* @__PURE__ */ Object.freeze({
2739
+ kind: "history",
2740
+ deep: false,
2741
+ f: prepare$1
2742
+ });
2743
+ const DEEP = /* @__PURE__ */ Object.freeze({
2744
+ kind: "history",
2745
+ deep: true,
2746
+ f: prepare$1
2747
+ });
2748
+ /**
2749
+ * A history pseudo-state: `resume: history()` returns to the child its
2750
+ * parent was last in, `history("deep")` to the very states inside it.
2751
+ */
2752
+ const history = (type) => ({ type: type === "deep" ? DEEP : SHALLOW });
2753
+ //#endregion
2754
+ //#region src/features/chart.ts
2755
+ /**
2756
+ * The runtime of `type: parallel` (PLAN §4.9, D12): what a machine with
2757
+ * parallel states runs on, wired by {@link wireParallel} once its tables are
2758
+ * prepared (`parallel.ts`, which the normaliser runs, or the plugin at build
2759
+ * time for a compiled machine, M7). Only a machine that declares a parallel
2760
+ * state ships this module.
2761
+ *
2762
+ * The core keeps one leaf (`instance.s`). In a machine with parallel
2763
+ * states that leaf is a configuration: an object shaped like a state, one
2764
+ * per set of active states, made once per machine and reused, whose `path`
2765
+ * lists the active leaves, whose `on` holds every event an active state
2766
+ * handles, and whose `up` chain names every active state for `is()`. Each
2767
+ * of its handlers is one rule whose guard selects the transitions (SCXML's
2768
+ * selection: per active leaf in document order, its own handler first,
2769
+ * then its ancestors'; of two whose exits overlap, the one declared deeper,
2770
+ * else the earlier) and whose action takes them all as one microstep:
2771
+ * exits in reverse document order, the transitions' actions, entries in
2772
+ * document order. The core's own `move` is never used; it runs the queue,
2773
+ * `always`, timers, effects and notifications as for any machine.
2774
+ *
2775
+ * Where a transition's exits stop is the core's rule: at the nearest common
2776
+ * ancestor of the leaf it was found from and the target's parent, so a
2777
+ * transition into a state's own subtree never exits that state; when that
2778
+ * ancestor is parallel, the next one up, so every region is left together.
2779
+ *
2780
+ * A region whose active child is a `type: "final"` state is done; when
2781
+ * every region of a parallel state is, the state's `done` transition is
2782
+ * taken after the event that finished it (an internal `machine.done`).
2783
+ * History states inside parallel machines are handled here too, with the
2784
+ * records `history` keeps.
2785
+ */
2786
+ const DONE = { type: "machine.done" };
2787
+ /** Each parallel machine's chart, by its tables. */
2788
+ const charts = /* @__PURE__ */ new WeakMap();
2789
+ /** `at` strictly inside `up`: depth-first numbering puts a subtree in one range. */
2790
+ const inside = (at, up) => at.i > up.i && at.i <= up.e;
2791
+ /** What a leaf takes events through: a final state in a region has none of its own, its region's apply. */
2792
+ const handlersOf = (chart, leaf) => leaf.fi && leaf.up !== chart.r ? leaf.up.on : leaf.on;
2793
+ const configOf = (instance) => instance.s;
2794
+ /** The configuration of these active states (document order), made once per machine. */
2795
+ function configuration(chart, a) {
2796
+ const key = a.map((s) => s.i).join();
2797
+ let made = chart.m.get(key);
2798
+ if (!made) {
2799
+ const l = a.filter((s, k) => !(a[k + 1] && inside(a[k + 1], s)));
2800
+ let up;
2801
+ for (const s of a) up = {
2802
+ path: s.path,
2803
+ up
2804
+ };
2805
+ const on = /* @__PURE__ */ new Map();
2806
+ for (const leaf of l) for (const [type, handler] of handlersOf(chart, leaf)) if (!on.has(type)) on.set(type, {
2807
+ ev: handler.ev,
2808
+ ru: chart.e
2809
+ });
2810
+ if (a.some((s) => s.dn)) on.set(DONE.type, {
2811
+ ev: DONE,
2812
+ ru: chart.f
2813
+ });
2814
+ chart.m.set(key, made = {
2815
+ i: -2,
2816
+ path: l.map((s) => s.path).join(" "),
2817
+ up,
2818
+ fi: l.length === 1 && l[0].fi && l[0].up === chart.r,
2819
+ on,
2820
+ al: l.some((s) => s.al) ? chart.a : void 0,
2821
+ a,
2822
+ l
2823
+ });
2824
+ }
2825
+ return made;
2826
+ }
2827
+ /**
2828
+ * The transitions a guard selected, read by the action right after it: the
2829
+ * core runs a rule's action as soon as its guard passed (`take`), so one
2830
+ * module binding is enough.
2831
+ */
2832
+ let picked = [];
2833
+ /**
2834
+ * Keeps the transitions found (rule, leaf, …) that SCXML would: one per
2835
+ * rule, and of two whose exits overlap the one declared deeper, else the
2836
+ * earlier. True when any is left, kept in {@link picked}.
2837
+ */
2838
+ function choose(instance, found) {
2839
+ const taken = [];
2840
+ next: for (let k = 0; k < found.length; k += 2) {
2841
+ const rule = found[k];
2842
+ const leaf = found[k + 1];
2843
+ if (taken.some((t) => t[0] === rule)) continue;
2844
+ let domain;
2845
+ if (rule.to) {
2846
+ domain = leaf;
2847
+ let b = rule.to.up;
2848
+ while (domain !== b) if (domain.i > b.i) domain = domain.up;
2849
+ else b = b.up;
2850
+ while (domain.p) domain = domain.up;
2851
+ }
2852
+ const exits = domain ? configOf(instance).a.filter((s) => inside(s, domain)) : [];
2853
+ for (let j = 0; j < taken.length; j++) {
2854
+ const other = taken[j];
2855
+ if (!exits.some((s) => other[2].includes(s))) continue;
2856
+ if (inside(rule.s ?? leaf, other[0].s ?? leaf)) taken.splice(j--, 1);
2857
+ else continue next;
2858
+ }
2859
+ taken.push([
2860
+ rule,
2861
+ domain,
2862
+ exits
2863
+ ]);
2864
+ }
2865
+ picked = taken;
2866
+ return taken.length > 0;
2867
+ }
2868
+ /** Pushes the first rule of `rules` whose guard passes, found from `leaf`. */
2869
+ function first(instance, rules, leaf, found) {
2870
+ for (const rule of rules) if (!rule.if || rule.if(instance)) return found.push(rule, leaf);
2871
+ }
2872
+ /** Per active leaf, its first transition for the event being handled, or (`always`) its first eventless one. */
2873
+ function pick(chart, instance, always) {
2874
+ const found = [];
2875
+ for (const leaf of configOf(instance).l) {
2876
+ const rules = always ? leaf.al : handlersOf(chart, leaf).get(instance.event.type)?.ru;
2877
+ if (rules) first(instance, rules, leaf, found);
2878
+ }
2879
+ return choose(instance, found);
2880
+ }
2881
+ /** A state's own rules (a timer's, a `done`): taken from the first active leaf inside it. */
2882
+ function pickFrom(instance, rules, state) {
2883
+ const { a, l } = configOf(instance);
2884
+ const found = [];
2885
+ if (a.includes(state)) first(instance, rules, l.find((s) => s === state || inside(s, state)), found);
2886
+ return choose(instance, found);
2887
+ }
2888
+ /** A rule whose guard picks the transitions and whose action takes them. */
2889
+ const routed = (pick) => ({
2890
+ to: void 0,
2891
+ if: pick,
2892
+ do: [step]
2893
+ });
2894
+ /** The action of every configuration rule: the microstep of what its guard picked. */
2895
+ const step = (view) => {
2896
+ const instance = view;
2897
+ const taken = picked;
2898
+ const { a, l } = configOf(instance);
2899
+ const exits = /* @__PURE__ */ new Set();
2900
+ for (const t of taken) for (const s of t[2]) exits.add(s);
2901
+ for (const s of exits) for (const h of s.hs) record(instance, h, h.h ? l.filter((leaf) => inside(leaf, s)) : a.filter((c) => c.up === s));
2902
+ for (const s of [...exits].sort((x, y) => y.i - x.i)) instance.exit(s);
2903
+ for (const t of taken) run(t[0].do, instance);
2904
+ const enter = /* @__PURE__ */ new Set();
2905
+ for (const [rule, domain] of taken) if (rule.to) {
2906
+ descend(instance, rule.to, enter);
2907
+ ascend(instance, rule.to, domain, enter);
2908
+ }
2909
+ settle(instance, a.filter((s) => !exits.has(s)), enter);
2910
+ };
2911
+ /** Adds `state` and what entering it enters (SCXML's addDescendantStatesToEnter). */
2912
+ function descend(instance, state, enter) {
2913
+ if (state.h !== void 0) {
2914
+ const parent = state.up;
2915
+ const states = recorded(instance, state) ?? (parent.p ? parent.k : [parent.n]);
2916
+ for (const s of states) descend(instance, s, enter);
2917
+ for (const s of states) ascend(instance, s, parent, enter);
2918
+ } else {
2919
+ enter.add(state);
2920
+ if (state.p) for (const region of state.k) regions(instance, region, enter);
2921
+ else if (state.n) descend(instance, state.n, enter);
2922
+ }
2923
+ }
2924
+ /** Enters `region` by default unless a state in it is entered already. */
2925
+ function regions(instance, region, enter) {
2926
+ for (const s of enter) if (s === region || inside(s, region)) return;
2927
+ descend(instance, region, enter);
2928
+ }
2929
+ /** Adds the ancestors of `state` below `stop`, and the other regions of the parallel ones. */
2930
+ function ascend(instance, state, stop, enter) {
2931
+ for (let up = state.up; up !== stop && up.i >= 0; up = up.up) {
2932
+ enter.add(up);
2933
+ if (up.p) for (const region of up.k) regions(instance, region, enter);
2934
+ }
2935
+ }
2936
+ /**
2937
+ * Enters the new states in document order, each added to the configuration
2938
+ * just before its entry actions run (so `is()` there sees it and its
2939
+ * ancestors, not what is entered after it, as in the core): their timers
2940
+ * and effects start once the event settles, and a final state that
2941
+ * completes a parallel state queues that state's `done`.
2942
+ */
2943
+ function settle(instance, kept, enter) {
2944
+ const chart = charts.get(instance.M);
2945
+ let active = kept;
2946
+ instance.s = configuration(chart, active);
2947
+ for (const s of [...enter].sort((x, y) => x.i - y.i)) {
2948
+ active = [...active, s].sort((x, y) => x.i - y.i);
2949
+ instance.s = configuration(chart, active);
2950
+ instance.d = 1;
2951
+ run(s.en, instance);
2952
+ if (s.af || s.ef) (instance.x ??= []).push(s);
2953
+ const done = s.fi && s.up.up?.dn;
2954
+ if (done && !instance.q?.includes(done.event) && complete(instance, s.up.up)) {
2955
+ const q = instance.q ??= [];
2956
+ let k = 0;
2957
+ while (k < q.length && chart.d.has(q[k])) k++;
2958
+ q.splice(k, 0, done.event);
2959
+ }
2960
+ }
2961
+ }
2962
+ /** SCXML's isInFinalState: a compound state whose active child is final, a parallel one whose regions all are. */
2963
+ const complete = (instance, state) => state.p ? state.k.every((region) => complete(instance, region)) : configOf(instance).a.some((s) => s.up === state && s.fi);
2964
+ /** The runtime of a machine whose tables `parallel.ts` prepared: its chart, its timers' rules, its first configuration. */
2965
+ function wireParallel(tables) {
2966
+ const states = tables.ss;
2967
+ const root = states[0].up;
2968
+ const chart = {
2969
+ r: root,
2970
+ m: /* @__PURE__ */ new Map(),
2971
+ d: /* @__PURE__ */ new Map(),
2972
+ e: [routed((view) => pick(chart, view))],
2973
+ a: [routed((view) => pick(chart, view, true))],
2974
+ f: [routed((view) => {
2975
+ const state = chart.d.get(view.event);
2976
+ return !!state && complete(view, state) && pickFrom(view, state.dn.rules, state);
2977
+ })]
2978
+ };
2979
+ charts.set(tables, chart);
2980
+ for (const s of [root, ...states]) {
2981
+ if (s.af) s.af = s.af.map(([ms, list]) => [ms, [{
2982
+ ...routed((view) => pickFrom(view, list, s)),
2983
+ w: list
2984
+ }]]);
2985
+ if (s.dn) chart.d.set(s.dn.event, s);
2986
+ }
2987
+ tables.i0 = {
2988
+ i: -2,
2989
+ up: root,
2990
+ en: [(view) => {
2991
+ const enter = /* @__PURE__ */ new Set();
2992
+ descend(view, root.n, enter);
2993
+ settle(view, [], enter);
2994
+ }]
2995
+ };
2996
+ }
2997
+ //#endregion
2998
+ //#region src/features/parallel.ts
2999
+ /**
3000
+ * `type: parallel` (PLAN §4.9, D12): a state whose children, its regions,
3001
+ * are all active at once. Only a machine that declares one ships this
3002
+ * module: `parallel` is imported to be written, and the mark carries what
3003
+ * prepares the machine's tables for the configurations of `chart.ts` (the
3004
+ * regions, initial children and subtrees of each state, the state that
3005
+ * declares each rule, a parallel state's `done`) and wires them, which the
3006
+ * normaliser calls when it meets the mark. Importing it has no effect.
3007
+ * A compiled machine (M7) has its tables prepared at build time, so it ships
3008
+ * `chart.ts` without this module.
3009
+ */
3010
+ /** Prepares a machine with parallel states, then wires it: what the mark carries. */
3011
+ const prepare = (tables, raws, rules) => {
3012
+ const states = tables.ss;
3013
+ const root = states[0].up;
3014
+ if (root.k) return;
3015
+ const all = [root, ...states];
3016
+ const byPath = new Map(states.map((s) => [s.path, s]));
3017
+ for (const s of all) {
3018
+ const k = s.i + 1;
3019
+ const mark = markOf(raws[k]);
3020
+ const first = raws[k].initial;
3021
+ s.k = [];
3022
+ s.hs = [];
3023
+ s.p = mark?.kind === "parallel";
3024
+ s.h = mark?.kind === "history" ? !!mark.deep : void 0;
3025
+ s.n = first === void 0 ? void 0 : byPath.get(s === root ? first : `${s.path}.${first}`);
3026
+ for (let up = s; up; up = up.up) up.e = s.i;
3027
+ if (s.up) (s.h === void 0 ? s.up.k : s.up.hs).push(s);
3028
+ }
3029
+ if (CHECK) {
3030
+ 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\`.`);
3031
+ 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.");
3032
+ }
3033
+ /** Marks the first `count` rules of `list` as declared by `s`. */
3034
+ const own = (list, count, s) => {
3035
+ for (let k = 0; k < count; k++) list[k].s = s;
3036
+ };
3037
+ for (const s of all) {
3038
+ for (const [type, handler] of s.on) {
3039
+ const outer = s.up?.on.get(type);
3040
+ if (outer !== handler) own(handler.ru, handler.ru.length - (outer?.ru.length ?? 0), s);
3041
+ }
3042
+ if (s.al && s.al !== s.up?.al) own(s.al, s.al.length - (s.up?.al?.length ?? 0), s);
3043
+ for (const [, list] of s.af ?? []) own(list, list.length, s);
3044
+ const done = raws[s.i + 1]?.done;
3045
+ if (done && s.p) {
3046
+ const list = rules(done, s);
3047
+ own(list, list.length, s);
3048
+ s.dn = {
3049
+ rules: list,
3050
+ event: { type: DONE.type }
3051
+ };
3052
+ }
3053
+ }
3054
+ if (wiring) wireParallel(tables);
3055
+ };
3056
+ /** Marks a state whose regions are all active at once: `editor: { type: parallel, states: { … } }`. */
3057
+ const parallel = /* @__PURE__ */ Object.freeze({
3058
+ kind: "parallel",
3059
+ f: prepare
3060
+ });
3061
+ //#endregion
3062
+ //#region src/vite/compile/analyse.ts
3063
+ /** Why a config cannot be compiled, and where. */
3064
+ var NotLiteral = class extends Error {
3065
+ node;
3066
+ /** What to write instead, when it is not "write the structure out". */
3067
+ fix;
3068
+ constructor(node, reason, fix) {
3069
+ super(reason);
3070
+ this.node = node;
3071
+ this.fix = fix;
3072
+ }
3073
+ };
3074
+ /** The fix for a target or a name the build cannot read. */
3075
+ const WRITE_IT = "Write the target or name as a string literal in the config, or the function in place, to compile it.";
3076
+ /** An identifier's or a private name's text. */
3077
+ const nameOf = (node) => node.type === "Identifier" || node.type === "PrivateIdentifier" ? node.name : "…";
3078
+ /** Strips what TypeScript adds around an expression (`as const`, `!`, parentheses). */
3079
+ function bare(node) {
3080
+ for (;;) switch (node.type) {
3081
+ case "TSAsExpression":
3082
+ case "TSSatisfiesExpression":
3083
+ case "TSNonNullExpression":
3084
+ case "TSTypeAssertion":
3085
+ case "ParenthesizedExpression":
3086
+ node = node.expression;
3087
+ break;
3088
+ default: return node;
3089
+ }
3090
+ }
3091
+ /** A property's key as the runtime sees it, or undefined when computed. */
3092
+ function keyOf(property) {
3093
+ if (property.computed) return void 0;
3094
+ const { key } = property;
3095
+ if (key.type === "Identifier") return key.name;
3096
+ if (key.type === "Literal" && (typeof key.value === "string" || typeof key.value === "number")) return String(key.value);
3097
+ }
3098
+ /** A literal object's properties in order; NotLiteral on a spread, a computed key or a method. */
3099
+ function entries(node, what) {
3100
+ const object = bare(node);
3101
+ if (object.type !== "ObjectExpression") throw new NotLiteral(node, `${what} is not an object literal`);
3102
+ return object.properties.map((property) => {
3103
+ if (property.type !== "Property") throw new NotLiteral(property, `${what} has a spread`);
3104
+ const key = keyOf(property);
3105
+ if (key === void 0) throw new NotLiteral(property, `${what} has a computed key`);
3106
+ if (property.kind !== "init") throw new NotLiteral(property, `${what}.${key} is an accessor`);
3107
+ if (property.method) throw new NotLiteral(property, `${what}.${key} is a method`, `Write it as \`${key}: (view) => …\` to compile it.`);
3108
+ return [key, property];
3109
+ });
3110
+ }
3111
+ const PLAIN_KEY$1 = /^[A-Za-z_$][\w$]*$/;
3112
+ const member = (object, key) => PLAIN_KEY$1.test(key) ? `${object}.${key}` : `${object}[${JSON.stringify(key)}]`;
3113
+ /**
3114
+ * Reads the config `node` of a `defineMachine` call; NotLiteral when its
3115
+ * structure is not written out.
3116
+ */
3117
+ function analyse(node, scope) {
3118
+ const kept = [];
3119
+ const refs = /* @__PURE__ */ new Map();
3120
+ const unseen = /* @__PURE__ */ new Map();
3121
+ const c = /* @__PURE__ */ new Map();
3122
+ const context = [];
3123
+ const props = [];
3124
+ const derived = [];
3125
+ const written = /* @__PURE__ */ new Map();
3126
+ const keep = (value) => kept.push(value) - 1;
3127
+ const placeholder = (ref) => {
3128
+ const fn = function() {};
3129
+ refs.set(fn, ref);
3130
+ return fn;
3131
+ };
3132
+ /** The imported name `local` stands for, when it comes from this package. */
3133
+ const imported = (local) => {
3134
+ const from = scope.imports.get(local);
3135
+ return from && scope.isPackage(from[0]) ? from[1] : void 0;
3136
+ };
3137
+ /** A string written in place or held by a module `const`. */
3138
+ const string = (value) => {
3139
+ const n = bare(value);
3140
+ if (n.type === "Literal" && typeof n.value === "string") return n.value;
3141
+ if (n.type === "TemplateLiteral" && n.expressions.length === 0) return n.quasis[0]?.value.cooked ?? void 0;
3142
+ if (n.type === "Identifier" && !scope.imports.has(n.name)) {
3143
+ const init = scope.values.get(n.name);
3144
+ if (init && init !== "function") return string(init);
3145
+ }
3146
+ };
3147
+ /** A literal the runtime would reject (`null`, a number), passed through for its error. */
3148
+ const other = (value) => {
3149
+ const n = bare(value);
3150
+ if (n.type === "Literal") return n.value;
3151
+ if (n.type === "Identifier" && n.name === "undefined") return void 0;
3152
+ return NaN;
3153
+ };
3154
+ /**
3155
+ * A function: written in place or declared in the module. Where a target
3156
+ * or a name may stand, an identifier or a member whose value the build
3157
+ * cannot see (an import, a \`let\`, an enum's member) may be a string as
3158
+ * well as a function, so the machine is left to the runtime normaliser,
3159
+ * which reads the value (D14: both forms behave the same). A call's
3160
+ * result, in place or in a module \`const\`, is taken for a function, and
3161
+ * checked when the machine loads in development (\`unseen\`).
3162
+ */
3163
+ const fn = (value, where) => {
3164
+ const n = bare(value);
3165
+ const ph = placeholder(`$[${keep(value)}]`);
3166
+ if (n.type === "ArrowFunctionExpression" || n.type === "FunctionExpression") return ph;
3167
+ if (n.type === "Identifier") {
3168
+ const init = scope.imports.has(n.name) ? void 0 : scope.values.get(n.name);
3169
+ if (init === "function") return ph;
3170
+ const i = init && bare(init);
3171
+ if (i?.type === "ArrowFunctionExpression" || i?.type === "FunctionExpression") return ph;
3172
+ if (i?.type === "ObjectExpression" || i?.type === "ArrayExpression") throw new NotLiteral(value, `${where} is \`${n.name}\`, written outside the config`);
3173
+ if (!init) {
3174
+ const from = scope.imports.get(n.name)?.[0];
3175
+ throw new NotLiteral(value, `${where} is \`${n.name}\`, ${from === void 0 ? "not a module `const`" : `imported from "${from}"`}, which may be a target or a name as well as a function`, WRITE_IT);
3176
+ }
3177
+ }
3178
+ if (n.type === "MemberExpression") throw new NotLiteral(value, `${where} is \`${n.object.type === "Identifier" ? n.object.name : "…"}${n.computed ? "[…]" : `.${nameOf(n.property)}`}\`, a member the build cannot read, which may be a target or a name as well as a function`, WRITE_IT);
3179
+ unseen.set(ph, where);
3180
+ return ph;
3181
+ };
3182
+ /** One function or name, or a list of them (`entry`, `do`). */
3183
+ const fns = (value, where) => {
3184
+ const n = bare(value);
3185
+ if (n.type === "ArrayExpression") return n.elements.map((element, k) => {
3186
+ if (!element || element.type === "SpreadElement") throw new NotLiteral(value, `${where} has a spread or a hole`);
3187
+ return string(element) ?? fn(element, `${where}[${k}]`);
3188
+ });
3189
+ return string(value) ?? fn(value, where);
3190
+ };
3191
+ /** A transition: a target, a function, `{ to, if, do }`, or a list of them. */
3192
+ const transition = (value, where) => {
3193
+ const n = bare(value);
3194
+ const target = string(value);
3195
+ if (target !== void 0) return target;
3196
+ if (n.type === "ArrayExpression") return n.elements.map((element, k) => {
3197
+ if (!element || element.type === "SpreadElement") throw new NotLiteral(value, `${where} has a spread or a hole`);
3198
+ return transition(element, `${where}[${k}]`);
3199
+ });
3200
+ if (n.type === "ObjectExpression") {
3201
+ const rule = {};
3202
+ for (const [key, property] of entries(n, where)) if (key === "to") {
3203
+ const to = string(property.value);
3204
+ if (to === void 0 && bare(property.value).type !== "Literal") throw new NotLiteral(property.value, `${where}.to is not a string literal`);
3205
+ rule.to = to ?? other(property.value);
3206
+ } else if (key === "if") rule.if = fns(property.value, `${where}.if`);
3207
+ else if (key === "do") rule.do = fns(property.value, `${where}.do`);
3208
+ else rule[key] = other(property.value);
3209
+ return rule;
3210
+ }
3211
+ if (n.type === "Literal") return n.value;
3212
+ return fn(value, where);
3213
+ };
3214
+ const transitions = (value, where) => {
3215
+ const out = {};
3216
+ for (const [key, property] of entries(value, where)) out[key] = transition(property.value, member(where, key));
3217
+ return out;
3218
+ };
3219
+ const state = (value, path, where, at = value.start) => {
3220
+ const n = bare(value);
3221
+ if (n.type === "CallExpression" && n.callee.type === "Identifier" && imported(n.callee.name) === "history") {
3222
+ const [arg] = n.arguments;
3223
+ if (arg && arg.type !== "SpreadElement" && string(arg) !== "deep") throw new NotLiteral(arg, `${where}: history() takes nothing or "deep"`);
3224
+ written.set(path, {
3225
+ node: value,
3226
+ at,
3227
+ keys: /* @__PURE__ */ new Map()
3228
+ });
3229
+ return history(arg ? "deep" : void 0);
3230
+ }
3231
+ const keys = /* @__PURE__ */ new Map();
3232
+ const out = {};
3233
+ for (const [key, property] of entries(value, where)) {
3234
+ keys.set(key, property);
3235
+ const at = member(where, key);
3236
+ const v = property.value;
3237
+ switch (key) {
3238
+ case "initial": {
3239
+ const initial = string(v);
3240
+ if (initial === void 0) throw new NotLiteral(v, `${at} is not a string literal`);
3241
+ out.initial = initial;
3242
+ break;
3243
+ }
3244
+ case "states": {
3245
+ const states = {};
3246
+ for (const [name, child] of entries(v, at)) states[name] = state(child.value, path ? `${path}.${name}` : name, member(at, name), child.start);
3247
+ out.states = states;
3248
+ break;
3249
+ }
3250
+ case "on":
3251
+ case "after":
3252
+ out[key] = transitions(v, at);
3253
+ break;
3254
+ case "entry":
3255
+ case "exit":
3256
+ case "effect":
3257
+ out[key] = fns(v, at);
3258
+ break;
3259
+ case "always":
3260
+ case "done":
3261
+ out[key] = transition(v, at);
3262
+ break;
3263
+ case "type": {
3264
+ const b = bare(v);
3265
+ if (b.type === "Identifier" && imported(b.name) === "parallel") out.type = parallel;
3266
+ else if (string(v) !== void 0) out.type = string(v);
3267
+ else if (b.type === "Literal") out.type = b.value;
3268
+ else throw new NotLiteral(v, `${at} is not "final" or \`parallel\``);
3269
+ break;
3270
+ }
3271
+ default: out[key] = machineKey(key, v, at) ?? other(v);
3272
+ }
3273
+ }
3274
+ written.set(path, {
3275
+ node: value,
3276
+ at,
3277
+ keys
3278
+ });
3279
+ return out;
3280
+ };
3281
+ /** The keys only the machine has; undefined for any other. */
3282
+ const machineKey = (key, v, at) => {
3283
+ switch (key) {
3284
+ case "name":
3285
+ case "expose":
3286
+ c.set(key, keep(v));
3287
+ return key === "name" ? string(v) : {};
3288
+ case "parts":
3289
+ c.set(key, keep(v));
3290
+ return parts(v);
3291
+ case "events": {
3292
+ const b = bare(v);
3293
+ if (b.type !== "ObjectExpression") return void 0;
3294
+ try {
3295
+ return Object.fromEntries(entries(b, at).map(([event]) => [event, null]));
3296
+ } catch {
3297
+ return;
3298
+ }
3299
+ }
3300
+ case "context":
3301
+ case "props":
3302
+ case "derived": {
3303
+ const list = key === "context" ? context : key === "props" ? props : derived;
3304
+ const out = {};
3305
+ for (const [field, property] of entries(v, at)) {
3306
+ list.push([field, keep(property.value)]);
3307
+ out[field] = key === "derived" ? function() {} : null;
3308
+ if (key === "props") out[field] = bound(property.value) ?? null;
3309
+ }
3310
+ return out;
3311
+ }
3312
+ case "actions":
3313
+ case "guards":
3314
+ case "effects": {
3315
+ const named = entries(v, at);
3316
+ c.set(key, keep(v));
3317
+ return Object.fromEntries(named.map(([name]) => [name, placeholder(member(member("c", key), name))]));
3318
+ }
3319
+ default: return;
3320
+ }
3321
+ };
3322
+ /**
3323
+ * What the normaliser checks of `parts` (each handler's event, each key's
3324
+ * spelling), read where written; parts built elsewhere are checked when
3325
+ * the compiled machine loads in development. The code itself stays in place.
3326
+ */
3327
+ const parts = (value) => {
3328
+ const out = {};
3329
+ try {
3330
+ for (const [name, part] of entries(value, "parts")) {
3331
+ const read = {};
3332
+ for (const [key, property] of entries(part.value, name)) {
3333
+ const n = bare(property.value);
3334
+ read[key] = string(property.value) ?? (n.type === "Literal" ? n.value : () => {});
3335
+ }
3336
+ out[name] = read;
3337
+ }
3338
+ } catch {
3339
+ return {};
3340
+ }
3341
+ return out;
3342
+ };
3343
+ /** `bindable(…, { states, changed })` with literal options, so the build checks its states. */
3344
+ const bound = (value) => {
3345
+ const n = bare(value);
3346
+ if (n.type !== "CallExpression" || n.callee.type !== "Identifier" || imported(n.callee.name) !== "bindable") return void 0;
3347
+ const options = n.arguments[1];
3348
+ if (!options) return bindable(null);
3349
+ try {
3350
+ const read = {};
3351
+ for (const [key, property] of entries(options, "bindable")) if (key === "states") {
3352
+ const states = {};
3353
+ for (const [side, s] of entries(property.value, "states")) states[side] = string(s.value);
3354
+ read.states = states;
3355
+ } else read[key] = string(property.value);
3356
+ return bindable(null, read);
3357
+ } catch {
3358
+ return;
3359
+ }
3360
+ };
3361
+ return {
3362
+ raw: state(node, "", "the config"),
3363
+ kept,
3364
+ refs,
3365
+ unseen,
3366
+ c,
3367
+ context,
3368
+ props,
3369
+ derived,
3370
+ written
3371
+ };
3372
+ }
3373
+ //#endregion
3374
+ //#region src/vite/compile/emit.ts
3375
+ const PLAIN_KEY = /^[A-Za-z_$][\w$]*$/;
3376
+ const key = (k) => PLAIN_KEY.test(k) ? k : JSON.stringify(k);
3377
+ /** `function <name>($, c = {…}) { … }`: builds `analysis`'s tables. */
3378
+ function emit(tables, analysis, options) {
3379
+ const { runtime: use } = options;
3380
+ const states = tables.ss;
3381
+ const all = [states[0]?.up ?? tables.i0.up, ...states];
3382
+ const names = new Map(all.map((s, k) => [s, k ? `s${k - 1}` : "r"]));
3383
+ const count = /* @__PURE__ */ new Map();
3384
+ const visit = (value) => {
3385
+ if (typeof value !== "object" || value === null || names.has(value)) return;
3386
+ const n = (count.get(value) ?? 0) + 1;
3387
+ count.set(value, n);
3388
+ if (n > 1) return;
3389
+ if (value instanceof Map) for (const [, v] of value) visit(v);
3390
+ else if (Array.isArray(value)) for (const v of value) visit(v);
3391
+ else for (const v of Object.values(value)) visit(v);
3392
+ };
3393
+ for (const s of all) for (const v of Object.values(s)) visit(v);
3394
+ const lines = [];
3395
+ let shared = 0;
3396
+ const declared = /* @__PURE__ */ new Map();
3397
+ const val = (value, where) => {
3398
+ if (value === void 0) return "void 0";
3399
+ if (value === null) return "null";
3400
+ if (typeof value === "boolean") return value ? "!0" : "!1";
3401
+ if (typeof value === "number" || typeof value === "string") return JSON.stringify(value);
3402
+ if (typeof value === "function") {
3403
+ const ref = analysis.refs.get(value);
3404
+ if (ref === void 0) throw new Error(`[machine:COMPILE] ${where}: a function the build did not place`);
3405
+ const unseen = analysis.unseen.get(value);
3406
+ return unseen !== void 0 && options.dev ? `${use("fn")}(${ref}, ${JSON.stringify(unseen)}, ${JSON.stringify(options.machine)})` : ref;
3407
+ }
3408
+ if (typeof value !== "object") throw new Error(`[machine:COMPILE] ${where}: ${typeof value}`);
3409
+ const named = names.get(value) ?? declared.get(value);
3410
+ if (named) return named;
3411
+ let out;
3412
+ if (value instanceof Map) out = `new Map(${value.size ? `[${[...value].map(([k, v]) => `[${JSON.stringify(k)},${val(v, `${where}.${k}`)}]`).join(",")}]` : ""})`;
3413
+ else if (Array.isArray(value)) out = `[${value.map((v, k) => val(v, `${where}[${k}]`)).join(",")}]`;
3414
+ else out = `{${Object.entries(value).map(([k, v]) => `${key(k)}:${val(v, `${where}.${k}`)}`).join(",")}}`;
3415
+ if ((count.get(value) ?? 0) > 1) {
3416
+ const name = `o${shared++}`;
3417
+ lines.push(`const ${name}=${out};`);
3418
+ declared.set(value, name);
3419
+ return name;
3420
+ }
3421
+ return out;
3422
+ };
3423
+ const later = [];
3424
+ const done = /* @__PURE__ */ new Set();
3425
+ /** Whether `value` reaches a state not declared yet. */
3426
+ const ahead = (value, seen = /* @__PURE__ */ new Set()) => {
3427
+ if (typeof value !== "object" || value === null || seen.has(value)) return false;
3428
+ if (names.has(value)) return !done.has(value);
3429
+ seen.add(value);
3430
+ return (value instanceof Map ? [...value.values()] : Object.values(value)).some((v) => ahead(v, seen));
3431
+ };
3432
+ for (const s of all) {
3433
+ const fields = Object.entries(s).map(([k, v]) => {
3434
+ if (typeof v !== "object" || v === null) return `${key(k)}:${val(v, `${s.path}.${k}`)}`;
3435
+ if (done.has(v) || !ahead(v)) return `${key(k)}:${val(v, `${s.path}.${k}`)}`;
3436
+ later.push([s, k]);
3437
+ return `${key(k)}:void 0`;
3438
+ });
3439
+ lines.push(`const ${names.get(s)}={${fields.join(",")}};`);
3440
+ done.add(s);
3441
+ }
3442
+ for (const [s, k] of later) {
3443
+ const value = val(s[k], `${s.path}.${k}`);
3444
+ lines.push(`${names.get(s)}.${k}=${value};`);
3445
+ }
3446
+ const fields = analysis.context.map(([f]) => f);
3447
+ const propNames = analysis.props.map(([p]) => p);
3448
+ const effects = states.some((s) => s.af || s.ef);
3449
+ const ref = (k) => `$[${k}]`;
3450
+ const list = (items) => `[${items.map((i) => JSON.stringify(i)).join(",")}]`;
3451
+ if (propNames.length) lines.push(`const p=${list(propNames)};`);
3452
+ const props = propNames.length > 0;
3453
+ const t = [
3454
+ `ss:[${states.map((s) => names.get(s)).join(",")}]`,
3455
+ `i0:${val(tables.i0, "initial")}`,
3456
+ fields.length && `df:[${analysis.context.map(([, k]) => ref(k)).join(",")}]`,
3457
+ fields.length && `C:${use("storeClass")}(${list(fields)},1,!0)`,
3458
+ `pn:${props ? "p" : "[]"}`,
3459
+ props && "pd:[]",
3460
+ props && `Q:${use("storeClass")}(p,${1 + fields.length},!1)`,
3461
+ props && "pl:[]",
3462
+ `nm:${list([...fields, ...propNames])}`,
3463
+ analysis.derived.length && `D:${use("derivedClass")}(${list(analysis.derived.map(([d]) => d))},[${analysis.derived.map(([, k]) => ref(k)).join(",")}])`,
3464
+ "k:void 0",
3465
+ props && "bd:[]",
3466
+ "c",
3467
+ `r:(c)=>${options.name}($,c)`,
3468
+ effects && `A:${use("activate")}`,
3469
+ effects && `X:${use("release")}`,
3470
+ props && `P:${use("own")}`
3471
+ ].filter(Boolean);
3472
+ lines.push(`const t={${t.join(",")}};`);
3473
+ if (options.dev) lines.push(`${use("devTables")}(t);`);
3474
+ if (propNames.length) lines.push(`${use("bind")}(t,{${analysis.props.map(([p, k]) => `${key(p)}:${ref(k)}`).join(",")}});`);
3475
+ if (states.some((s) => s.p)) lines.push(`${use("wireParallel")}(t);`);
3476
+ else if (states.some((s) => s.h !== void 0)) lines.push(`${use("wireHistory")}(t);`);
3477
+ lines.push("return t;");
3478
+ const c = [...analysis.c].map(([k, index]) => `${k}:${ref(index)}`).join(",");
3479
+ return `function ${options.name}($,c={${c}}){${lines.join("")}}`;
3480
+ }
3481
+ //#endregion
3482
+ //#region src/vite/compile/module.ts
3483
+ /**
3484
+ * Compiling the machines of one module (PLAN §5.6, D14, M7). Each top-level
3485
+ * `const x = defineMachine({ … })` whose structure is literal is checked by
3486
+ * the runtime normaliser at build time (its dev errors become build errors,
3487
+ * with the file, line and column) and replaced by a call that builds its
3488
+ * tables: `__machine0([…])`, the array holding the config's code where it
3489
+ * is written (functions, context and props values, parts…), so every other
3490
+ * line and column stays. The builders and their imports are appended to the
3491
+ * module. A config that is not literal stays a `defineMachine` call,
3492
+ * normalised at runtime, with a warning that says why.
3493
+ */
3494
+ checkAlways();
3495
+ /** A machine's config the normaliser rejects: the build fails with it. */
3496
+ var CompileError = class extends Error {
3497
+ file;
3498
+ offset;
3499
+ line;
3500
+ column;
3501
+ constructor(file, at) {
3502
+ super(`${at.message}\n at ${file}:${at.line}:${at.column + 1}`);
3503
+ this.file = file;
3504
+ this.offset = at.offset;
3505
+ this.line = at.line;
3506
+ this.column = at.column;
3507
+ }
3508
+ };
3509
+ /** `code`'s machines compiled, or null when it has none to compile or warn about. */
3510
+ async function compileModule(code, file, options) {
3511
+ if (!code.includes("defineMachine")) return null;
3512
+ const { program, errors } = parseSync(file, code);
3513
+ if (errors.length) return null;
3514
+ const imports = /* @__PURE__ */ new Map();
3515
+ const values = /* @__PURE__ */ new Map();
3516
+ const sources = /* @__PURE__ */ new Set();
3517
+ for (const node of program.body) {
3518
+ if (node.type === "ImportDeclaration") {
3519
+ if (node.importKind === "type") continue;
3520
+ for (const specifier of node.specifiers) {
3521
+ if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") continue;
3522
+ const name = specifier.imported.type === "Identifier" ? specifier.imported.name : String(specifier.imported.value);
3523
+ imports.set(specifier.local.name, [node.source.value, name]);
3524
+ sources.add(node.source.value);
3525
+ }
3526
+ continue;
3527
+ }
3528
+ const declaration = node.type === "ExportNamedDeclaration" ? node.declaration : node;
3529
+ if (declaration?.type === "FunctionDeclaration" && declaration.id) values.set(declaration.id.name, "function");
3530
+ if (declaration?.type === "VariableDeclaration" && declaration.kind === "const") {
3531
+ for (const { id, init } of declaration.declarations) if (id.type === "Identifier" && init) values.set(id.name, init);
3532
+ }
3533
+ }
3534
+ const ours = /* @__PURE__ */ new Set();
3535
+ for (const source of sources) if (await options.isPackage(source)) ours.add(source);
3536
+ const scope = {
3537
+ imports,
3538
+ values,
3539
+ isPackage: (source) => ours.has(source)
3540
+ };
3541
+ const define = [...imports].filter(([, [s, n]]) => ours.has(s) && n === "defineMachine");
3542
+ if (!define.length) return null;
3543
+ const defines = new Set(define.map(([local]) => local));
3544
+ const lines = [0];
3545
+ for (let i = code.indexOf("\n"); i >= 0; i = code.indexOf("\n", i + 1)) lines.push(i + 1);
3546
+ const at = (offset, message) => {
3547
+ let low = 0;
3548
+ let high = lines.length - 1;
3549
+ while (low < high) {
3550
+ const mid = low + high + 1 >> 1;
3551
+ if (lines[mid] <= offset) low = mid;
3552
+ else high = mid - 1;
3553
+ }
3554
+ return {
3555
+ message,
3556
+ offset,
3557
+ line: low + 1,
3558
+ column: offset - lines[low]
3559
+ };
3560
+ };
3561
+ let prefix = "__machine";
3562
+ while (code.includes(prefix)) prefix += "$";
3563
+ const used = /* @__PURE__ */ new Set();
3564
+ const runtime = (name) => (used.add(name), `${prefix}_${name}`);
3565
+ const s = new MagicString(code);
3566
+ const builders = [];
3567
+ const compiled = [];
3568
+ const warnings = [];
3569
+ for (const node of program.body) {
3570
+ const declaration = node.type === "ExportNamedDeclaration" ? node.declaration : node;
3571
+ if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const") continue;
3572
+ for (const { id, init } of declaration.declarations) {
3573
+ const call = init && bare(init);
3574
+ if (id.type !== "Identifier" || call?.type !== "CallExpression" || call.callee.type !== "Identifier" || !defines.has(call.callee.name)) continue;
3575
+ const [config] = call.arguments;
3576
+ if (!config || config.type === "SpreadElement" || call.arguments.length !== 1) continue;
3577
+ let analysis;
3578
+ try {
3579
+ analysis = analyse(config, scope);
3580
+ } catch (error) {
3581
+ if (!(error instanceof NotLiteral)) throw error;
3582
+ warnings.push(at(error.node.start, `[machine:NOT_COMPILED] ${id.name}: ${error.message}, so this machine is normalised at runtime, as without the plugin. ${error.fix ?? "Write its states, targets and names out in the config to compile it."}`));
3583
+ continue;
3584
+ }
3585
+ let tables;
3586
+ try {
3587
+ tables = prepared(analysis.raw);
3588
+ } catch (error) {
3589
+ const message = error instanceof Error ? error.message : "";
3590
+ if (!message.startsWith("[machine:")) throw error;
3591
+ throw new CompileError(file, at(locate(message, analysis, config), message));
3592
+ }
3593
+ const name = `${prefix}${compiled.length}`;
3594
+ builders.push(emit(tables, analysis, {
3595
+ name,
3596
+ runtime,
3597
+ dev: options.dev,
3598
+ machine: analysis.raw.name ?? id.name
3599
+ }));
3600
+ replace(s, code, call, analysis.kept, `/* @__PURE__ */ ${name}([`, "])");
3601
+ compiled.push(id.name);
3602
+ }
3603
+ }
3604
+ if (!compiled.length && !warnings.length) return null;
3605
+ if (compiled.length) {
3606
+ const names = [...used].sort().map((n) => `${n} as ${prefix}_${n}`);
3607
+ s.append(`\n${builders.join("\n")}\n`);
3608
+ if (names.length) s.append(`import { ${names.join(", ")} } from ${JSON.stringify(options.runtime)};\n`);
3609
+ }
3610
+ return {
3611
+ code: s.toString(),
3612
+ map: s.generateMap({
3613
+ hires: "boundary",
3614
+ source: file,
3615
+ includeContent: true
3616
+ }),
3617
+ compiled,
3618
+ warnings
3619
+ };
3620
+ }
3621
+ /**
3622
+ * Overwrites `call` around the nodes it keeps, so each stays where it is
3623
+ * written: `open` before the first, a comma between two, `close` after the
3624
+ * last. Each gap keeps its line breaks, so no line moves.
3625
+ */
3626
+ function replace(s, code, call, kept, open, close) {
3627
+ const breaks = (from, to) => code.slice(from, to).replace(/[^\n]/g, "");
3628
+ let from = call.start;
3629
+ let text = open;
3630
+ for (const node of kept) {
3631
+ if (node.start < from) throw new Error("[machine:COMPILE] kept code out of order");
3632
+ s.overwrite(from, node.start, text + breaks(from, node.start));
3633
+ from = node.end;
3634
+ text = ",";
3635
+ }
3636
+ s.overwrite(from, call.end, (kept.length ? "" : open) + breaks(from, call.end) + close);
3637
+ }
3638
+ /**
3639
+ * Where a normaliser's message points in the config: the state it names
3640
+ * (its first quoted path, or the machine), then, inside that state's own
3641
+ * keys, a key or string it quotes after it.
3642
+ */
3643
+ function locate(message, analysis, config) {
3644
+ const quoted = [...message.matchAll(/"([^"]*)"|`([^`]*)`/g)].flatMap((m) => m[1] !== void 0 ? [m[1]] : m[2].match(/[\w$]+/g) ?? []);
3645
+ const k = quoted.findIndex((q) => analysis.written.has(q));
3646
+ const state = k >= 0 ? quoted[k] : "";
3647
+ const written = analysis.written.get(state);
3648
+ const tokens = new Set(quoted.slice(k + 1));
3649
+ if (!written) return config.start;
3650
+ let found;
3651
+ const search = (node, top) => {
3652
+ if (!node || found !== void 0) return;
3653
+ const n = bare(node);
3654
+ if (n.type === "ObjectExpression") for (const property of n.properties) {
3655
+ if (property.type !== "Property") continue;
3656
+ const name = keyOf(property);
3657
+ if (top && name === "states") continue;
3658
+ if (name !== void 0 && tokens.has(name)) return void (found = property.start);
3659
+ search(property.value, false);
3660
+ if (found !== void 0) return;
3661
+ }
3662
+ else if (n.type === "ArrayExpression") {
3663
+ for (const element of n.elements) if (element && element.type !== "SpreadElement") search(element, false);
3664
+ } else if (n.type === "Literal" && typeof n.value === "string" && tokens.has(n.value)) found = n.start;
3665
+ };
3666
+ search(written.node, true);
3667
+ if (found === void 0 && state === "") for (const key of [
3668
+ "props",
3669
+ "actions",
3670
+ "guards",
3671
+ "effects"
3672
+ ]) search(written.keys.get(key)?.value, false);
3673
+ return found ?? written.at;
3674
+ }
3675
+ //#endregion
3676
+ //#region src/vite/compile/plugin.ts
3677
+ function compilePlugin(options) {
3678
+ let dev = true;
3679
+ return {
3680
+ name: "machine:compile",
3681
+ enforce: "pre",
3682
+ configResolved(config) {
3683
+ dev = !config.isProduction;
3684
+ },
3685
+ transform: {
3686
+ filter: {
3687
+ id: {
3688
+ include: /\.[cm]?[jt]sx?(?:\?|$)/,
3689
+ exclude: /\/node_modules\//
3690
+ },
3691
+ code: "defineMachine"
3692
+ },
3693
+ async handler(code, id) {
3694
+ if (id.startsWith("\0") || options.include && !options.include(id)) return null;
3695
+ const isPackage = async (source) => source === "@menapo/machine" || source.startsWith(".") && (await this.resolve(source, id))?.id === options.index;
3696
+ try {
3697
+ const out = await compileModule(code, id.split("?")[0], {
3698
+ runtime: options.runtime,
3699
+ dev,
3700
+ isPackage
3701
+ });
3702
+ if (!out) return null;
3703
+ for (const warning of out.warnings) this.warn(warning.message, warning.offset);
3704
+ return out.compiled.length ? {
3705
+ code: out.code,
3706
+ map: out.map
3707
+ } : null;
3708
+ } catch (error) {
3709
+ if (error instanceof CompileError) this.error(error.message, error.offset);
3710
+ throw error;
3711
+ }
3712
+ }
3713
+ }
3714
+ };
3715
+ }
3716
+ //#endregion
3717
+ //#region src/vite/config.ts
3718
+ /** The package's entries an app's code imports (`/vite` runs in Vite itself). */
3719
+ const ENTRIES = [
3720
+ "@menapo/machine",
3721
+ "@menapo/machine/compiled",
3722
+ "@menapo/machine/dom",
3723
+ "@menapo/machine/test",
3724
+ "@menapo/machine/vicss"
3725
+ ];
3726
+ /**
3727
+ * `machine:config`: every entry served as its own files, never pre-bundled,
3728
+ * and bundled into server builds, never left to Node. The glue modules the
3729
+ * plugin resolves `useMachine` to import the runtime's files directly; a
3730
+ * pre-bundled or external copy of the entries beside them would be a
3731
+ * second runtime, whose instances the first does not know (a context write
3732
+ * would throw, parts would stop tracking, `inspect` would see nothing).
3733
+ */
3734
+ const configPlugin = () => ({
3735
+ name: "machine:config",
3736
+ config: () => ({ optimizeDeps: { exclude: ENTRIES } }),
3737
+ configEnvironment(name, config) {
3738
+ return (config.consumer ?? (name === "client" ? "client" : "server")) === "server" ? { resolve: { noExternal: ["@menapo/machine"] } } : void 0;
3739
+ }
3740
+ });
3741
+ //#endregion
3742
+ //#region ../glue/src/devtools.ts
3743
+ /** A panel: an iframe of the page served at `<base><path>`. */
3744
+ const dockEntry$1 = (panel, base, path) => ({
3745
+ ...panel,
3746
+ category: "framework",
3747
+ type: "iframe",
3748
+ url: base + path
3749
+ });
3750
+ /** Serves `page()` at `<anything>/<path>` while `on()` holds; other requests pass. */
3751
+ function serveDock(server, path, on, page) {
3752
+ server.middlewares.use((req, res, next) => {
3753
+ const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
3754
+ if (!on() || req.method !== "GET" || !pathname.endsWith(`/${path}`)) return next();
3755
+ res.setHeader("content-type", "text/html; charset=utf-8");
3756
+ res.setHeader("cache-control", "no-store");
3757
+ res.end(page());
3758
+ });
3759
+ }
3760
+ /**
3761
+ * The page's bridge as the panel finds it: the global `name` on the nearest
3762
+ * ancestor window that has it (Vite DevTools may nest the iframe). A panel
3763
+ * page runs from its own source text, so it is handed this function's
3764
+ * source (`${findBridge}`) rather than importing it.
3765
+ */
3766
+ function findBridge(name) {
3767
+ for (let w = window.parent;; w = w.parent) {
3768
+ try {
3769
+ const found = w[name];
3770
+ if (found) return found;
3771
+ } catch {}
3772
+ if (w === w.parent) return void 0;
3773
+ }
3774
+ }
3775
+ //#endregion
3776
+ //#region src/vite/devtools/protocol.ts
3777
+ /** The dock page Vite DevTools embeds, under the dev server's `base`. */
3778
+ const DOCK_PATH = "__machine/dock";
3779
+ /** The page module the plugin adds to every client module that imports `@menapo/machine`. */
3780
+ const PAGE_ID = "virtual:machine/devtools";
3781
+ /**
3782
+ * The page global through which the dock, a same-origin iframe, reaches the
3783
+ * page's machines: it is a document of its own, with no module graph.
3784
+ */
3785
+ const BRIDGE = "__machineDevtools";
3786
+ //#endregion
3787
+ //#region src/vite/devtools/dock.ts
3788
+ /**
3789
+ * The Vite DevTools dock panel (PLAN §5.8): one self-contained document the
3790
+ * dev server serves at `<base>__machine/dock`, embedded by Vite DevTools as
3791
+ * a same-origin iframe of the app's page, as vi18n's dock is. It finds the
3792
+ * page's {@link Bridge} on an ancestor window and shows, for the instance
3793
+ * chosen:
3794
+ *
3795
+ * - its current state and its context and props;
3796
+ * - the events it took, newest first, each with the fields it changed;
3797
+ * - a form that sends it one of the events its state handles;
3798
+ * - its states as a read-only tree: the active ones marked, each state's own
3799
+ * transitions, `after` timers and `always` rules, each linked to where it
3800
+ * is written when the plugin found its source.
3801
+ *
3802
+ * `dock` runs from its own source text in the page, so it uses nothing but
3803
+ * its arguments and globals (the bridge lookup is `@menapo/glue`'s, handed
3804
+ * in as source); it reads the core's tables, typed by the core.
3805
+ */
3806
+ function dock(global, find) {
3807
+ const bridge = find(global);
3808
+ const $ = (id) => document.getElementById(id);
3809
+ const h = (tag, props = {}, ...children) => {
3810
+ const element = Object.assign(document.createElement(tag), props);
3811
+ for (const child of children) if (child) element.append(child);
3812
+ return element;
3813
+ };
3814
+ const format = (value) => {
3815
+ let text;
3816
+ try {
3817
+ text = typeof value === "function" ? "ƒ" : JSON.stringify(value) ?? String(value);
3818
+ } catch {
3819
+ text = String(value);
3820
+ }
3821
+ return text.length > 80 ? `${text.slice(0, 79)}…` : text;
3822
+ };
3823
+ const link = (source, at) => {
3824
+ if (!source || !at) return null;
3825
+ const where = `${source.file}:${at[0]}:${at[1]}`;
3826
+ return h("a", {
3827
+ className: "source",
3828
+ textContent: "source",
3829
+ title: where,
3830
+ href: `../__open-in-editor?file=${encodeURIComponent(where)}`,
3831
+ onclick(event) {
3832
+ event.preventDefault();
3833
+ fetch(this.href);
3834
+ }
3835
+ });
3836
+ };
3837
+ const nameOf = ({ instance, source }) => instance.M.name !== "machine" ? instance.M.name : source?.binding ?? "machine";
3838
+ if (!bridge) {
3839
+ $("empty").textContent = "No machines here: open this panel from the app's page, where Vite DevTools shows it.";
3840
+ return;
3841
+ }
3842
+ let chosen = 0;
3843
+ let current;
3844
+ const instances = $("instances");
3845
+ const type = $("type");
3846
+ const data = $("payload");
3847
+ const error = $("error");
3848
+ /** A state's own transitions, `after` timers and `always` rules, as list items. */
3849
+ const transitions = (state, up, source) => {
3850
+ const own = source?.states[state.path];
3851
+ const items = [];
3852
+ const add = (label, rules, at) => {
3853
+ for (const rule of rules.flatMap((r) => r.w ?? [r])) {
3854
+ const to = rule.h ?? rule.to;
3855
+ const target = to ? to.path : "(stays)";
3856
+ items.push(h("li", {}, h("code", { textContent: label }), ` → ${target}`, rule.if ? " if …" : "", " ", link(source, at)));
3857
+ }
3858
+ };
3859
+ for (const [event, handler] of state.on) {
3860
+ if (up?.on.get(event) === handler) continue;
3861
+ const inherited = up?.on.get(event)?.ru.length ?? 0;
3862
+ add(event, handler.ru.slice(0, handler.ru.length - inherited), own?.on[event]);
3863
+ }
3864
+ for (const [ms, rules] of state.af ?? []) add(`after ${ms} ms`, rules, own?.after[ms]);
3865
+ if (state.al && state.al !== up?.al) {
3866
+ const inherited = up?.al?.length ?? 0;
3867
+ add("always", state.al.slice(0, state.al.length - inherited), own?.always);
3868
+ }
3869
+ return items.length ? h("ul", { className: "transitions" }, ...items) : null;
3870
+ };
3871
+ const graph = ({ instance, source }) => {
3872
+ const tree = h("ul");
3873
+ const lists = /* @__PURE__ */ new Map();
3874
+ const root = instance.M.ss[0]?.up;
3875
+ const anywhere = root && transitions(root, void 0, source);
3876
+ const chain = [];
3877
+ for (let at = instance.s; at; at = at.up) chain.push(at);
3878
+ const actives = new Set(instance.s.a ?? chain);
3879
+ for (const state of instance.M.ss) {
3880
+ const active = actives.has(state);
3881
+ const leaf = active && !instance.M.ss.some((s) => s.up === state && actives.has(s));
3882
+ const parent = state.up === root ? tree : lists.get(state.up);
3883
+ const name = state.path.slice(state.path.lastIndexOf(".") + 1);
3884
+ const item = h("li", {}, h("span", {
3885
+ className: active ? "state active" : "state",
3886
+ textContent: name + (state.fi ? " (final)" : ""),
3887
+ ariaCurrent: leaf ? "true" : null
3888
+ }), " ", link(source, source?.states[state.path]?.at), transitions(state, state.up, source));
3889
+ parent.append(item);
3890
+ const children = h("ul");
3891
+ lists.set(state, children);
3892
+ item.append(children);
3893
+ }
3894
+ for (const list of lists.values()) if (!list.childElementCount) list.remove();
3895
+ return h("div", {}, anywhere && h("div", {}, h("span", {
3896
+ className: "muted",
3897
+ textContent: "In any state"
3898
+ }), anywhere), tree);
3899
+ };
3900
+ const render = () => {
3901
+ queued = false;
3902
+ const live = bridge.live();
3903
+ current = live.find((l) => l.n === chosen) ?? live[0];
3904
+ chosen = current?.n ?? 0;
3905
+ instances.replaceChildren(...live.map((l) => h("option", {
3906
+ value: String(l.n),
3907
+ textContent: `${nameOf(l)} #${l.n}`,
3908
+ selected: l === current
3909
+ })));
3910
+ $("main").hidden = !current;
3911
+ $("empty").textContent = current ? "" : "No machine is running on this page.";
3912
+ if (!current) return;
3913
+ const { instance, steps } = current;
3914
+ $("state").textContent = instance.s.path;
3915
+ const { nm: names, pn: propNames } = instance.M;
3916
+ const fields = names.length - propNames.length;
3917
+ $("data").replaceChildren(...names.map((name, n) => {
3918
+ const store = n < fields ? instance.context : instance.props;
3919
+ return h("tr", {}, h("th", {
3920
+ scope: "row",
3921
+ textContent: name
3922
+ }), h("td", {
3923
+ className: "muted",
3924
+ textContent: n < fields ? "context" : "prop"
3925
+ }), h("td", {}, h("code", { textContent: format(store[name]) })));
3926
+ }));
3927
+ const events = [...instance.s.on.keys()];
3928
+ if (events.join("\0") !== [...type.options].map((o) => o.value).join("\0")) type.replaceChildren(...events.map((e) => h("option", {
3929
+ value: e,
3930
+ textContent: e
3931
+ })));
3932
+ $("submit").disabled = !events.length;
3933
+ $("log").replaceChildren(...steps.slice().reverse().map(({ event, state, changed }) => {
3934
+ const { type: name, ...payload } = event;
3935
+ return h("tr", {}, h("td", {}, h("code", { textContent: name }), Object.keys(payload).length ? ` ${format(payload)}` : ""), h("td", { textContent: state }), h("td", {}, ...Object.entries(changed).map(([field, value]) => h("div", {}, h("code", { textContent: `${field} = ${format(value)}` })))));
3936
+ }));
3937
+ $("graph").replaceChildren(graph(current));
3938
+ };
3939
+ let queued = false;
3940
+ const schedule = () => {
3941
+ if (!queued) {
3942
+ queued = true;
3943
+ setTimeout(render, 0);
3944
+ }
3945
+ };
3946
+ instances.onchange = () => {
3947
+ chosen = Number(instances.value);
3948
+ error.value = "";
3949
+ render();
3950
+ };
3951
+ $("send").onsubmit = (event) => {
3952
+ event.preventDefault();
3953
+ error.value = "";
3954
+ try {
3955
+ const payload = data.value.trim() ? JSON.parse(data.value) : void 0;
3956
+ if (payload !== void 0 && (typeof payload !== "object" || !payload || Array.isArray(payload))) throw new Error("the data is an object of the event's fields, like {\"by\": 2}");
3957
+ current?.instance.send(type.value, payload);
3958
+ } catch (thrown) {
3959
+ error.value = thrown instanceof Error ? thrown.message : String(thrown);
3960
+ }
3961
+ render();
3962
+ };
3963
+ const stop = bridge.subscribe(schedule);
3964
+ addEventListener("pagehide", stop);
3965
+ render();
3966
+ }
3967
+ const STYLE = `
3968
+ :root { color-scheme: light dark; font: 13px/1.4 system-ui, sans-serif; }
3969
+ body { margin: 0; padding: 8px 12px; background: Canvas; color: CanvasText; }
3970
+ header, form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
3971
+ h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; margin: 16px 0 4px; opacity: .8; }
3972
+ table { width: 100%; border-collapse: collapse; }
3973
+ th, td { padding: 3px 6px; border-top: 1px solid #8883; text-align: left; vertical-align: top; }
3974
+ th[scope=row] { font-weight: 500; }
3975
+ code { font: 12px ui-monospace, monospace; }
3976
+ ul { margin: 0; padding-left: 16px; list-style: none; }
3977
+ .transitions { padding-left: 12px; opacity: .85; }
3978
+ .state.active { font-weight: 700; }
3979
+ .state.active::after { content: " ●"; }
3980
+ #data td:last-child { width: 100%; }
3981
+ .muted, .source { opacity: .7; }
3982
+ input { flex: 1; min-width: 120px; font: inherit; }
3983
+ output { color: #d33; }
3984
+ `;
3985
+ /** The dock's page: one self-contained document. */
3986
+ function dockPage() {
3987
+ return `<!doctype html>
3988
+ <html lang="en">
3989
+ <head><meta charset="utf-8"><title>Machines</title><style>${STYLE}</style></head>
3990
+ <body>
3991
+ <p id="empty" role="status"></p>
3992
+ <main id="main" hidden>
3993
+ <header>
3994
+ <label>Machine <select id="instances"></select></label>
3995
+ <span>State <b id="state"></b></span>
3996
+ </header>
3997
+ <h2 id="h-data">Context and props</h2>
3998
+ <table aria-labelledby="h-data"><tbody id="data"></tbody></table>
3999
+ <h2 id="h-send">Send</h2>
4000
+ <form id="send" aria-labelledby="h-send">
4001
+ <select id="type" aria-label="Event"></select>
4002
+ <input id="payload" aria-label="Event data, as JSON" placeholder='{"by": 2}' autocomplete="off">
4003
+ <button id="submit">Send</button>
4004
+ <output id="error" role="alert"></output>
4005
+ </form>
4006
+ <h2 id="h-log">Events</h2>
4007
+ <table aria-labelledby="h-log">
4008
+ <thead><tr><th scope="col">Event</th><th scope="col">State after</th><th scope="col">Changed</th></tr></thead>
4009
+ <tbody id="log"></tbody>
4010
+ </table>
4011
+ <h2 id="h-graph">States</h2>
4012
+ <div id="graph" role="region" aria-labelledby="h-graph"></div>
4013
+ </main>
4014
+ <script type="module">(${dock.toString()})(${JSON.stringify(BRIDGE)}, ${findBridge.toString()});<\/script>
4015
+ </body>
4016
+ </html>
4017
+ `;
4018
+ }
4019
+ //#endregion
4020
+ //#region src/vite/devtools/page.ts
4021
+ /** Returns the function the plugin's transform registers each machine's source with. */
4022
+ function page(inspect, global, keep, limit) {
4023
+ const entries = [];
4024
+ const byInstance = /* @__PURE__ */ new WeakMap();
4025
+ const sources = /* @__PURE__ */ new WeakMap();
4026
+ const listeners = /* @__PURE__ */ new Set();
4027
+ let count = 0;
4028
+ /** Drops stopped (2) and collected instances: gone from the page, so from the panel. */
4029
+ const prune = () => {
4030
+ for (let k = 0; k < entries.length; k++) {
4031
+ const instance = entries[k].ref.deref();
4032
+ if (!instance || instance.b === 2) entries.splice(k--, 1);
4033
+ }
4034
+ };
4035
+ inspect(({ instance, event, state, changed }) => {
4036
+ let entry = byInstance.get(instance);
4037
+ if (!entry) {
4038
+ entry = {
4039
+ n: ++count,
4040
+ ref: new WeakRef(instance),
4041
+ steps: []
4042
+ };
4043
+ byInstance.set(instance, entry);
4044
+ if (entries.push(entry) > limit) {
4045
+ prune();
4046
+ if (entries.length > limit) entries.splice(0, entries.length - limit);
4047
+ }
4048
+ }
4049
+ if (entry.steps.push({
4050
+ event,
4051
+ state,
4052
+ changed
4053
+ }) > keep) entry.steps.shift();
4054
+ for (const listener of listeners) listener();
4055
+ });
4056
+ globalThis[global] = {
4057
+ live() {
4058
+ const live = [];
4059
+ prune();
4060
+ for (const { n, ref, steps } of entries) {
4061
+ const instance = ref.deref();
4062
+ live.push({
4063
+ n,
4064
+ instance,
4065
+ steps,
4066
+ source: sources.get(instance.M)
4067
+ });
4068
+ }
4069
+ return live;
4070
+ },
4071
+ subscribe(listener) {
4072
+ listeners.add(listener);
4073
+ return () => void listeners.delete(listener);
4074
+ }
4075
+ };
4076
+ return (machine, source) => void sources.set(machine, source);
4077
+ }
4078
+ //#endregion
4079
+ //#region src/vite/devtools/sources.ts
4080
+ /**
4081
+ * Where machines are written, for the panel's links to each state and
4082
+ * transition (PLAN §5.8). In dev with Vite DevTools on, every client module
4083
+ * that imports `@menapo/machine` gets two things appended, so no line or
4084
+ * column of its own code moves:
4085
+ *
4086
+ * - an import of the page module, which then runs before the module does,
4087
+ * so the panel sees every instance from the first one started;
4088
+ * - for each `const x = defineMachine({ … })` at the top level with a
4089
+ * literal config, a call that registers `x`'s source: the file, and per
4090
+ * state path where the state, each `on` event, each `after` delay and its
4091
+ * `always` are written.
4092
+ *
4093
+ * A config built by helpers is registered as far as it is literal, as M7's
4094
+ * compilation will take it (D14).
4095
+ */
4096
+ const PACKAGE = "@menapo/machine";
4097
+ /** A literal object's properties by key; computed keys and spreads are skipped. */
4098
+ function properties(node) {
4099
+ const map = /* @__PURE__ */ new Map();
4100
+ if (node?.type !== "ObjectExpression") return map;
4101
+ for (const property of node.properties) {
4102
+ if (property.type !== "Property" || property.computed) continue;
4103
+ const { key } = property;
4104
+ if (key.type === "Identifier") map.set(key.name, property);
4105
+ else if (key.type === "Literal" && (typeof key.value === "string" || typeof key.value === "number")) map.set(String(key.value), property);
4106
+ }
4107
+ return map;
4108
+ }
4109
+ /** `code` with the page module's import and its machines' sources appended, or null when it imports nothing from the package. */
4110
+ function withSources(code, file) {
4111
+ const { program, errors } = parseSync(file, code);
4112
+ if (errors.length) return null;
4113
+ const define = /* @__PURE__ */ new Set();
4114
+ let imports = false;
4115
+ for (const node of program.body) {
4116
+ if (node.type !== "ImportDeclaration" || node.source.value !== PACKAGE) continue;
4117
+ imports = true;
4118
+ for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier" && (specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value) === "defineMachine") define.add(specifier.local.name);
4119
+ }
4120
+ if (!imports) return null;
4121
+ const lines = [0];
4122
+ for (let i = code.indexOf("\n"); i >= 0; i = code.indexOf("\n", i + 1)) lines.push(i + 1);
4123
+ const loc = (offset) => {
4124
+ let low = 0;
4125
+ let high = lines.length - 1;
4126
+ while (low < high) {
4127
+ const mid = low + high + 1 >> 1;
4128
+ if (lines[mid] <= offset) low = mid;
4129
+ else high = mid - 1;
4130
+ }
4131
+ return [low + 1, offset - lines[low] + 1];
4132
+ };
4133
+ const keys = (node) => {
4134
+ const out = {};
4135
+ for (const [key, property] of properties(node)) out[key] = loc(property.start);
4136
+ return out;
4137
+ };
4138
+ let out = `\nimport { source as __machineSource } from ${JSON.stringify(PAGE_ID)};\n`;
4139
+ for (const statement of program.body) {
4140
+ const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
4141
+ if (declaration?.type !== "VariableDeclaration") continue;
4142
+ for (const { id, init } of declaration.declarations) {
4143
+ if (id.type !== "Identifier" || init?.type !== "CallExpression" || init.callee.type !== "Identifier" || !define.has(init.callee.name) || init.arguments[0]?.type !== "ObjectExpression") continue;
4144
+ const config = properties(init.arguments[0]);
4145
+ const states = { "": {
4146
+ at: loc(init.start),
4147
+ on: keys(config.get("on")?.value),
4148
+ after: {}
4149
+ } };
4150
+ const walk = (node, prefix) => {
4151
+ for (const [key, property] of properties(node)) {
4152
+ const path = prefix ? `${prefix}.${key}` : key;
4153
+ const state = properties(property.value);
4154
+ const always = state.get("always");
4155
+ states[path] = {
4156
+ at: loc(property.start),
4157
+ on: keys(state.get("on")?.value),
4158
+ after: keys(state.get("after")?.value),
4159
+ ...always ? { always: loc(always.start) } : {}
4160
+ };
4161
+ walk(state.get("states")?.value, path);
4162
+ }
4163
+ };
4164
+ walk(config.get("states")?.value, "");
4165
+ const source = {
4166
+ file,
4167
+ binding: id.name,
4168
+ states
4169
+ };
4170
+ out += `__machineSource(${id.name}, ${JSON.stringify(source)});\n`;
4171
+ }
4172
+ }
4173
+ return code + out;
4174
+ }
4175
+ //#endregion
4176
+ //#region src/vite/devtools/plugin.ts
4177
+ /**
4178
+ * `machine:devtools` (PLAN §5.8): the panel in the Vite DevTools dock, by
4179
+ * the mechanism vi18n's dock uses. Vite DevTools calls a plugin's
4180
+ * `devtools.setup` only when the user turned it on and installed
4181
+ * `@vitejs/devtools`, so the plugin imports nothing from it and, until
4182
+ * `setup` runs, does nothing: no page module, no transform, no route.
4183
+ *
4184
+ * Once on, in dev only:
4185
+ *
4186
+ * - `setup` registers the dock: an iframe of `<base>__machine/dock`, served
4187
+ * here as one self-contained document (`dock.ts`);
4188
+ * - every client module that imports `@menapo/machine` imports the page
4189
+ * module (`virtual:machine/devtools`), which watches instances through
4190
+ * `inspect` (D17) and puts the bridge the dock reads on the page, and
4191
+ * registers its machines' sources (`sources.ts`).
4192
+ *
4193
+ * A build never runs this plugin, so production bundles import neither the
4194
+ * page module nor `inspect`, and the core's hook calls drop out with them.
4195
+ */
4196
+ const dockEntry = (base) => dockEntry$1({
4197
+ id: "machine",
4198
+ title: "Machines",
4199
+ icon: "ph:graph-duotone"
4200
+ }, base, DOCK_PATH);
4201
+ /** The page module: the app's own `inspect`, handed to {@link page}'s source. */
4202
+ const pageModule = () => `import { inspect } from "@menapo/machine";\nexport const source = (${page.toString()})(inspect, ${JSON.stringify(BRIDGE)}, 100, 500);\n`;
4203
+ const RESOLVED = "\0" + PAGE_ID;
4204
+ function devtoolsPlugin() {
4205
+ let on = false;
4206
+ return {
4207
+ name: "machine:devtools",
4208
+ apply: "serve",
4209
+ enforce: "pre",
4210
+ devtools: { setup(context) {
4211
+ on = true;
4212
+ context.docks.register(dockEntry(context.viteConfig.base));
4213
+ } },
4214
+ configureServer(server) {
4215
+ serveDock(server, DOCK_PATH, () => on, dockPage);
4216
+ },
4217
+ resolveId: {
4218
+ filter: { id: /^virtual:machine\/devtools$/ },
4219
+ handler: (id) => "\0" + id
4220
+ },
4221
+ load: {
4222
+ filter: { id: /^\0virtual:machine\/devtools$/ },
4223
+ handler: (id) => id === RESOLVED ? {
4224
+ code: pageModule(),
4225
+ moduleType: "js"
4226
+ } : null
4227
+ },
4228
+ transform: {
4229
+ filter: {
4230
+ id: {
4231
+ include: /\.[cm]?[jt]sx?(?:\?|$)/,
4232
+ exclude: /\/node_modules\//
4233
+ },
4234
+ code: "@menapo/machine"
4235
+ },
4236
+ handler(code, id) {
4237
+ if (!on || this.environment.config.consumer !== "client") return null;
4238
+ const out = withSources(code, id.split("?")[0]);
4239
+ return out === null ? null : {
4240
+ code: out,
4241
+ map: null
4242
+ };
4243
+ }
4244
+ }
4245
+ };
4246
+ }
4247
+ //#endregion
4248
+ //#region src/vite.ts
4249
+ /**
4250
+ * `@menapo/machine/vite`: the plugin that resolves `useMachine` per
4251
+ * framework, validates and precompiles machines, and adds the DevTools panel
4252
+ * (PLAN §5.5, §5.6, §5.8). The only entry that may import from the build side.
4253
+ *
4254
+ * M3 brought the resolution: each module's `useMachine` comes from the glue
4255
+ * of its framework (`src/glue/`), on `@menapo/glue`'s adapters. M6 brought
4256
+ * the DevTools panel, M7 the compilation of literal machines (`compile/`).
4257
+ */
4258
+ const GLUE = posix(fileURLToPath(new URL("./glue/", import.meta.url)));
4259
+ const EXT = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
4260
+ const INDEX = posix(fileURLToPath(new URL("./index" + EXT, import.meta.url)));
4261
+ /** `plugins: [machine()]` in a Vite config. */
4262
+ function machine(options = {}) {
4263
+ return [
4264
+ configPlugin(),
4265
+ devtoolsPlugin(),
4266
+ ...options.compile === false ? [] : [compilePlugin({
4267
+ runtime: "@menapo/machine/compiled",
4268
+ index: INDEX
4269
+ })],
4270
+ gluePlugin({
4271
+ name: "machine:glue",
4272
+ package: "@menapo/machine",
4273
+ hooks: ["useMachine"],
4274
+ module: (framework) => GLUE + (framework === "svelte" ? "svelte.svelte" : framework) + EXT,
4275
+ dirs: [GLUE],
4276
+ framework: options.framework
4277
+ })
4278
+ ];
4279
+ }
4280
+ //#endregion
4281
+ export { machine };