@mantaq/inspect 0.2.1 → 0.3.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/README.md CHANGED
@@ -10,7 +10,7 @@ It finds every exported actor, or function that builds one, from the file's type
10
10
 
11
11
  ## Three ways in
12
12
 
13
- **CLI**: `mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--config FILE] [--mode MODE] [--no-open] [--check]`. `--config` loads the files with that Vite config (env, aliases, plugins) and `--mode` picks its mode; without `--config` no project config is read. `--check` exits 1 if a warning was printed or fuzzing found a handler error (printed as `error:`), so CI can gate on design errors. `--json` writes the viz document instead of the page. Seed: `MANTAQ_SEED` (default from `@mantaq/pbt`). Same seed → same output.
13
+ **CLI**: `mantaq-inspect <file.ts>... [--out FILE] [--json] [--live] [--no-fuzz] [--iterations N] [--config FILE] [--mode MODE] [--no-open] [--check]`. `--config` loads the files with that Vite config (env, aliases, plugins) and `--mode` picks its mode; without `--config` no project config is read. `--check` exits 1 if a warning was printed or fuzzing found a handler error (printed as `error:`), so CI can gate on design errors. `--json` writes the viz document instead of the page. `--live` bundles your machine files into the page with Vite, so it is still one offline HTML file but its steps run the real actors (same rules as the Vite plugin's live page); it cannot be combined with `--json`. Seed: `MANTAQ_SEED` (default from `@mantaq/pbt`). Same seed → same output.
14
14
 
15
15
  **Vite plugin**: a live page on your dev server that redraws, keeping your walk, when you save.
16
16
 
@@ -50,6 +50,7 @@ const json = serializeViz(vizDocument([machine]));
50
50
  - **Exports.** An actor, or a function that returns one (or `{ actor }`). Functions are called with no arguments, with a `VirtualClock` when the first parameter is a clock, or with `{ clock }` when it is an options object with a `clock`. Functions with other required parameters are skipped. Discovery reads types only; nothing is called to find out.
51
51
  - **A fresh actor per call**, to fuzz. A file that only exports one actor instance is drawn, not fuzzed. When a file has factories, its actor instances are skipped.
52
52
  - **Exported factories.** Export a factory that takes ports and a clock, not only a built instance. See [Machines in an application](https://anderscan.github.io/mantaq/guides/machines-in-an-app/).
53
+ - **A factory, for `--live`.** The live page recreates the actor on every step, so it needs an exported factory; a file that only exports an instance stays a static walk, with the usual warning.
53
54
  - **A `VirtualClock`**, to fuzz. An actor on the real clock is drawn, with a warning.
54
55
 
55
56
  Every part that cannot run is skipped with a warning, never guessed.
package/dist/cli.mjs CHANGED
@@ -1,41 +1,87 @@
1
1
  #!/usr/bin/env node
2
- import { l as vizDocument, r as inspectFile } from "./load-DOwsxTyL.mjs";
3
- import { t as htmlPage } from "./page-rr9IQ-4k.mjs";
2
+ import { a as targetsSource, d as vizDocument, o as inlineConfig, r as TARGETS_RESOLVED, s as inspectFile } from "./vite-BBW0CmYr.mjs";
3
+ import { t as htmlPage } from "./page-CK7-IsLJ.mjs";
4
4
  import { serializeViz } from "@mantaq/viz";
5
5
  import { Either } from "@mantaq/utils";
6
6
  import { basename, resolve } from "node:path";
7
7
  import { SEED_ENV, parseSeed } from "@mantaq/pbt";
8
8
  import { writeFileSync } from "node:fs";
9
+ import { fileURLToPath } from "node:url";
10
+ import { build } from "vite";
9
11
  import { spawn } from "node:child_process";
10
12
  import { parseArgs } from "node:util";
13
+ // src/live-html.ts
14
+ /**
15
+ * The live page as one classic script: the viewer, the driver and every
16
+ * exported factory of `files`, built with the project's Vite config.
17
+ */
18
+ async function liveBundle(files, load) {
19
+ const entry = fileURLToPath(import.meta.resolve(import.meta.url.endsWith(".ts") ? "./live-embedded.ts" : "./live-embedded.mjs"));
20
+ const targets = {
21
+ name: "mantaq-live-targets",
22
+ resolveId: (id) => id === "virtual:mantaq/targets" ? TARGETS_RESOLVED : null,
23
+ load: (id) => id === "\0virtual:mantaq/targets" ? targetsSource(files) : null
24
+ };
25
+ const inline = await inlineConfig(load);
26
+ const output = await build({
27
+ ...inline,
28
+ logLevel: "silent",
29
+ plugins: [...inline.plugins ?? [], targets],
30
+ build: {
31
+ write: false,
32
+ minify: true,
33
+ cssCodeSplit: false,
34
+ lib: {
35
+ entry,
36
+ formats: ["iife"],
37
+ name: "MantaqLive",
38
+ fileName: () => "live.js"
39
+ }
40
+ }
41
+ });
42
+ return (Array.isArray(output) ? output : [output]).flatMap((each) => "output" in each ? each.output : []).flatMap((file) => file.type === "chunk" ? [file.code] : []).join("\n");
43
+ }
11
44
  // src/cli.ts
12
45
  /**
13
46
  * mantaq-inspect: draw the machines a file exports.
14
47
  *
15
- * mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--config FILE]
48
+ * mantaq-inspect <file.ts>... [--out FILE] [--json] [--live] [--no-fuzz] [--iterations N] [--config FILE]
16
49
  * [--mode MODE] [--no-open] [--check]
17
50
  *
18
51
  * Finds every exported actor or actor factory from the file's types, reads
19
52
  * handler explanations and sample payloads from the source, fuzzes each
20
53
  * machine that runs on a VirtualClock, and writes one self-contained HTML page
21
54
  * (default `mantaq-viz.html`) that opens without a server. `--json` writes the
22
- * viz document instead. `--config` loads the files with that Vite config (env,
55
+ * viz document instead. `--live` bundles the machine files into the page, so its
56
+ * steps run the real actors (factories on a VirtualClock) with no server. `--config` loads the files with that Vite config (env,
23
57
  * aliases, plugins) and `--mode` picks its mode. `--check` exits 1 if any warning was printed or
24
58
  * fuzzing found a handler error (each printed as `error:`). Seed: MANTAQ_SEED (default from @mantaq/pbt).
25
59
  */
26
- const USAGE = "usage: mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--config FILE] [--mode MODE] [--no-open] [--check]\n";
60
+ const USAGE = "usage: mantaq-inspect <file.ts>... [--out FILE] [--json] [--live] [--no-fuzz] [--iterations N] [--config FILE] [--mode MODE] [--no-open] [--check]\n";
27
61
  function loadOptions(config, mode) {
28
62
  return {
29
63
  ...config === void 0 ? {} : { config: resolve(config) },
30
64
  ...mode === void 0 ? {} : { mode }
31
65
  };
32
66
  }
67
+ function fuzzSettings(iterationsArg, off) {
68
+ const iterations = Number(iterationsArg ?? 2e3);
69
+ if (!Number.isInteger(iterations) || iterations < 0) return Either.left("bad --iterations");
70
+ const seed = parseSeed(process.env[SEED_ENV]);
71
+ if (Either.isLeft(seed)) return Either.left(seed[0].message);
72
+ const settings = off ? false : {
73
+ seed: seed[1],
74
+ iterations
75
+ };
76
+ return Either.right(settings);
77
+ }
33
78
  function parse() {
34
79
  const parsed = Either.from(() => parseArgs({
35
80
  allowPositionals: true,
36
81
  options: {
37
82
  out: { type: "string" },
38
83
  json: { type: "boolean" },
84
+ live: { type: "boolean" },
39
85
  iterations: { type: "string" },
40
86
  config: { type: "string" },
41
87
  mode: { type: "string" },
@@ -47,20 +93,17 @@ function parse() {
47
93
  if (Either.isLeft(parsed)) return Either.left(String(parsed[0]));
48
94
  const { positionals, values } = parsed[1];
49
95
  if (positionals.length === 0) return Either.left("no file given");
50
- const iterations = Number(values.iterations ?? 2e3);
51
- if (!Number.isInteger(iterations) || iterations < 0) return Either.left("bad --iterations");
52
- const seed = parseSeed(process.env[SEED_ENV]);
53
- if (Either.isLeft(seed)) return Either.left(seed[0].message);
54
96
  const json = values.json === true;
55
- const fuzz = values["no-fuzz"] === true ? false : {
56
- seed: seed[1],
57
- iterations
58
- };
97
+ const live = values.live === true;
98
+ if (live && json) return Either.left("--live and --json cannot be combined");
99
+ const fuzz = fuzzSettings(values.iterations, values["no-fuzz"] === true);
100
+ if (Either.isLeft(fuzz)) return Either.left(fuzz[0]);
59
101
  return Either.right({
60
102
  files: positionals.map((file) => resolve(file)),
61
103
  out: resolve(values.out ?? (json ? "mantaq-viz.json" : "mantaq-viz.html")),
62
104
  json,
63
- fuzz,
105
+ live,
106
+ fuzz: fuzz[1],
64
107
  check: values.check === true,
65
108
  load: loadOptions(values.config, values.mode),
66
109
  open: values["no-open"] !== true && !json && process.stdout.isTTY === true
@@ -85,7 +128,7 @@ async function main() {
85
128
  process.exitCode = 1;
86
129
  return;
87
130
  }
88
- const { files, out, json, fuzz, open, check, load } = options[1];
131
+ const { files, out, json, live, fuzz, open, check, load } = options[1];
89
132
  const results = [];
90
133
  for (const file of files) results.push(await inspectFile(file, { fuzz }, load));
91
134
  for (const warning of results.flatMap((result) => result.warnings)) process.stderr.write(`warning: ${warning}\n`);
@@ -100,7 +143,8 @@ async function main() {
100
143
  }
101
144
  const document = vizDocument(machines);
102
145
  const title = `${files.map((file) => basename(file)).join(", ")} · mantaq viz`;
103
- writeFileSync(out, json ? serializeViz(document) : await htmlPage(document, title));
146
+ const liveScript = live ? await liveBundle(files, load) : void 0;
147
+ writeFileSync(out, json ? serializeViz(document) : await htmlPage(document, title, liveScript));
104
148
  process.stdout.write(`wrote ${out} (${machines.map((machine) => machine.id).join(", ")})\n`);
105
149
  if (open) openInBrowser(out);
106
150
  }
package/dist/index.d.mts CHANGED
@@ -123,6 +123,7 @@ interface ModuleAnalysis {
123
123
  sites: HandlerSite[];
124
124
  stateDocs: ReadonlyMap<string, string>;
125
125
  eventDocs: ReadonlyMap<string, string>;
126
+ effectDocs: ReadonlyMap<string, ReadonlyArray<string>>;
126
127
  payloads: Record<string, VizJson[]>;
127
128
  machines: MachineExport[];
128
129
  }
@@ -147,6 +148,9 @@ interface LoadOptions {
147
148
  }
148
149
  /** Load `file` with Vite (TS, tsconfig paths, extensionless imports) and inspect its machines. */
149
150
  export declare function inspectFile(file: string, options?: FileOptions, load?: LoadOptions): Promise<LoadedFile>;
150
- /** A self-contained HTML page for `document`: open it from disk, attach it, commit it. */
151
- export declare function htmlPage(document: VizDocument, title: string): Promise<string>;
151
+ /**
152
+ * A self-contained HTML page for `document`: open it from disk, attach it, commit it.
153
+ * With `live` (a built script that drives the machines), that script replaces the static viewer.
154
+ */
155
+ export declare function htmlPage(document: VizDocument, title: string, live?: string): Promise<string>;
152
156
  export { type AnalysisSource, type CorpusEntry, type FileOptions, type FuzzOptions, type FuzzResult, type FuzzSettings, type FuzzTarget, type HandlerSite, type InspectTarget, type Inspected, type InspectedModule, type LiteralPool, type MachineExport, type ModuleAnalysis, type ModuleOptions, type Observation, type RunError, type Trace, type VizInput, eventCatalog, execute, inspect, inspectModule, machineId, vizDocument };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as toVizMachine, _ as fuzzedMachine, a as inspect, b as shrink, d as analyzeSource, f as annotateMachine, g as literalPool, h as guidance, l as vizDocument, m as branchDistanceGuide, o as inspectModule, p as branchDistance, r as inspectFile, s as machineId, u as analyzeModule, v as withTestCoverage, x as toViz, y as fuzz } from "./load-DOwsxTyL.mjs";
1
+ import { C as toViz, S as shrink, _ as guidance, b as withTestCoverage, c as inspect, d as vizDocument, f as analyzeModule, g as branchDistanceGuide, h as branchDistance, l as inspectModule, m as annotateMachine, p as analyzeSource, s as inspectFile, u as machineId, v as literalPool, w as toVizMachine, x as fuzz, y as fuzzedMachine } from "./vite-BBW0CmYr.mjs";
2
2
  import { i as execute, r as eventCatalog } from "./run-B5huYj_r.mjs";
3
- import { t as htmlPage } from "./page-rr9IQ-4k.mjs";
3
+ import { t as htmlPage } from "./page-CK7-IsLJ.mjs";
4
4
  export { analyzeModule, analyzeSource, annotateMachine, branchDistance, branchDistanceGuide, eventCatalog, execute, fuzz, fuzzedMachine, guidance, htmlPage, inspect, inspectFile, inspectModule, literalPool, machineId, shrink, toViz, toVizMachine, vizDocument, withTestCoverage };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,16 @@
1
+ import { t as mountDriven } from "./live-page-DEDl_Nio.mjs";
2
+ import { DOCUMENT_ELEMENT_ID } from "@mantaq/viz";
3
+ import { targets } from "virtual:mantaq/targets";
4
+ // src/live-embedded.ts
5
+ /**
6
+ * Entry of the standalone live page `mantaq-inspect --live` writes: the viz
7
+ * document sits in the page, the machines are bundled in, nothing is fetched.
8
+ */
9
+ const root = document.querySelector("#app");
10
+ const text = document.getElementById(DOCUMENT_ELEMENT_ID)?.textContent;
11
+ if (root && text) mountDriven(root, {
12
+ source: "this page",
13
+ text,
14
+ targets
15
+ });
16
+ export {};
@@ -1,88 +1,6 @@
1
- import { createDriver } from "./drive.mjs";
2
- import { UPDATE_EVENT, parseViz } from "@mantaq/viz";
3
- import { Either } from "@mantaq/utils";
1
+ import { n as mountLiveDriven } from "./live-page-DEDl_Nio.mjs";
2
+ import { UPDATE_EVENT } from "@mantaq/viz";
4
3
  import { targets } from "virtual:mantaq/targets";
5
- import { mountViewer, showIssues } from "@mantaq/viz/viewer";
6
- import { linkOptions } from "@mantaq/viz/page";
7
- // src/live-page.ts
8
- /**
9
- * The live page of the Vite plugin: the served document, with each machine
10
- * that has a target running for real behind the viewer. The walk lives in the
11
- * address bar (`?machine=&step=`), so a redraw after a save replays it on the
12
- * new code.
13
- */
14
- /** A document holding only `machine`, so the viewer shows the machine its driver runs. */
15
- function only(viz, machine) {
16
- const chosen = viz.machines.find((each) => each.id === machine) ?? viz.machines[0];
17
- return {
18
- ...viz,
19
- machines: chosen === void 0 ? [] : [chosen]
20
- };
21
- }
22
- /** Mount the document at `text`; machines with a target run live, the rest stay static walks. */
23
- function mountDriven(root, context) {
24
- const parsed = parseViz(context.text);
25
- if (Either.isLeft(parsed)) {
26
- showIssues(root, {
27
- source: context.source,
28
- issues: parsed[0]
29
- });
30
- return;
31
- }
32
- const options = linkOptions();
33
- const viz = parsed[1];
34
- const shown = only(viz, options.machine ?? null);
35
- const id = shown.machines[0]?.id ?? "";
36
- const target = context.targets[id];
37
- const create = target !== void 0 && typeof Reflect.get(target().clock, "advance") === "function" ? target : void 0;
38
- const picker = viz.machines.length > 1;
39
- const select = (machine) => {
40
- const next = new URLSearchParams(location.search);
41
- next.set("machine", machine);
42
- next.delete("step");
43
- history.replaceState(null, "", `?${next.toString()}`);
44
- mountDriven(root, context);
45
- };
46
- const doc = root.ownerDocument;
47
- const bar = doc.createElement("nav");
48
- bar.className = "machines";
49
- if (picker) for (const machine of viz.machines) {
50
- const button = doc.createElement("button");
51
- button.type = "button";
52
- button.textContent = machine.id;
53
- button.disabled = machine.id === id;
54
- button.addEventListener("click", () => select(machine.id));
55
- bar.append(button);
56
- }
57
- const viewer = doc.createElement("div");
58
- root.replaceChildren(...picker ? [bar] : [], viewer);
59
- mountViewer(viewer, shown, {
60
- ...options,
61
- ...create === void 0 ? {} : { driver: createDriver({
62
- id,
63
- create
64
- }) }
65
- });
66
- }
67
- /** Fetch the document from `source` and mount it; call again to redraw, keeping the walk. */
68
- async function mountLiveDriven(root, context) {
69
- const response = await fetch(context.source).catch(() => null);
70
- if (response === null || !response.ok) {
71
- const status = response === null ? "network error" : `HTTP ${response.status}`;
72
- showIssues(root, {
73
- source: context.source,
74
- issues: [{
75
- path: "$",
76
- message: status
77
- }]
78
- });
79
- return;
80
- }
81
- mountDriven(root, {
82
- ...context,
83
- text: await response.text()
84
- });
85
- }
86
4
  // src/live-entry.ts
87
5
  /**
88
6
  * Entry of the page `@mantaq/inspect/vite` serves. Redraws when the dev
@@ -0,0 +1,85 @@
1
+ import { createDriver } from "./drive.mjs";
2
+ import { parseViz } from "@mantaq/viz";
3
+ import { Either } from "@mantaq/utils";
4
+ import { mountViewer, showIssues } from "@mantaq/viz/viewer";
5
+ import { linkOptions } from "@mantaq/viz/page";
6
+ // src/live-page.ts
7
+ /**
8
+ * The live page of the Vite plugin: the served document, with each machine
9
+ * that has a target running for real behind the viewer. The walk lives in the
10
+ * address bar (`?machine=&step=`), so a redraw after a save replays it on the
11
+ * new code.
12
+ */
13
+ /** A document holding only `machine`, so the viewer shows the machine its driver runs. */
14
+ function only(viz, machine) {
15
+ const chosen = viz.machines.find((each) => each.id === machine) ?? viz.machines[0];
16
+ return {
17
+ ...viz,
18
+ machines: chosen === void 0 ? [] : [chosen]
19
+ };
20
+ }
21
+ /** Mount the document at `text`; machines with a target run live, the rest stay static walks. */
22
+ function mountDriven(root, context) {
23
+ const parsed = parseViz(context.text);
24
+ if (Either.isLeft(parsed)) {
25
+ showIssues(root, {
26
+ source: context.source,
27
+ issues: parsed[0]
28
+ });
29
+ return;
30
+ }
31
+ const options = linkOptions();
32
+ const viz = parsed[1];
33
+ const shown = only(viz, options.machine ?? null);
34
+ const id = shown.machines[0]?.id ?? "";
35
+ const target = context.targets[id];
36
+ const create = target !== void 0 && typeof Reflect.get(target().clock, "advance") === "function" ? target : void 0;
37
+ const picker = viz.machines.length > 1;
38
+ const select = (machine) => {
39
+ const next = new URLSearchParams(location.search);
40
+ next.set("machine", machine);
41
+ next.delete("step");
42
+ history.replaceState(null, "", `?${next.toString()}`);
43
+ mountDriven(root, context);
44
+ };
45
+ const doc = root.ownerDocument;
46
+ const bar = doc.createElement("nav");
47
+ bar.className = "machines";
48
+ if (picker) for (const machine of viz.machines) {
49
+ const button = doc.createElement("button");
50
+ button.type = "button";
51
+ button.textContent = machine.id;
52
+ button.disabled = machine.id === id;
53
+ button.addEventListener("click", () => select(machine.id));
54
+ bar.append(button);
55
+ }
56
+ const viewer = doc.createElement("div");
57
+ root.replaceChildren(...picker ? [bar] : [], viewer);
58
+ mountViewer(viewer, shown, {
59
+ ...options,
60
+ ...create === void 0 ? {} : { driver: createDriver({
61
+ id,
62
+ create
63
+ }) }
64
+ });
65
+ }
66
+ /** Fetch the document from `source` and mount it; call again to redraw, keeping the walk. */
67
+ async function mountLiveDriven(root, context) {
68
+ const response = await fetch(context.source).catch(() => null);
69
+ if (response === null || !response.ok) {
70
+ const status = response === null ? "network error" : `HTTP ${response.status}`;
71
+ showIssues(root, {
72
+ source: context.source,
73
+ issues: [{
74
+ path: "$",
75
+ message: status
76
+ }]
77
+ });
78
+ return;
79
+ }
80
+ mountDriven(root, {
81
+ ...context,
82
+ text: await response.text()
83
+ });
84
+ }
85
+ export { mountLiveDriven as n, mountDriven as t };
@@ -0,0 +1,41 @@
1
+ import { t as STYLESHEETS } from "./vite-BBW0CmYr.mjs";
2
+ import { serializeViz, standaloneHtml } from "@mantaq/viz";
3
+ import { readFileSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ import { build } from "vite";
6
+ // src/page.ts
7
+ /** The viewer as one classic script, built from `@mantaq/viz/standalone`. */
8
+ async function viewerScript() {
9
+ const entry = fileURLToPath(import.meta.resolve("@mantaq/viz/standalone"));
10
+ const output = await build({
11
+ configFile: false,
12
+ logLevel: "silent",
13
+ build: {
14
+ write: false,
15
+ minify: true,
16
+ cssCodeSplit: false,
17
+ lib: {
18
+ entry,
19
+ formats: ["iife"],
20
+ name: "MantaqViz",
21
+ fileName: () => "viz.js"
22
+ }
23
+ }
24
+ });
25
+ return (Array.isArray(output) ? output : [output]).flatMap((each) => "output" in each ? each.output : []).flatMap((file) => file.type === "chunk" ? [file.code] : []).join("\n");
26
+ }
27
+ /**
28
+ * A self-contained HTML page for `document`: open it from disk, attach it, commit it.
29
+ * With `live` (a built script that drives the machines), that script replaces the static viewer.
30
+ */
31
+ async function htmlPage(document, title, live) {
32
+ const css = STYLESHEETS.map((id) => readFileSync(fileURLToPath(import.meta.resolve(id)), "utf8")).join("\n");
33
+ const script = live ?? await viewerScript();
34
+ return standaloneHtml({
35
+ title,
36
+ script,
37
+ css,
38
+ document: serializeViz(document)
39
+ });
40
+ }
41
+ export { htmlPage as t };
@@ -1,14 +1,14 @@
1
1
  import { i as execute, r as eventCatalog } from "./run-B5huYj_r.mjs";
2
- import { ANY_STATE, VIZ_SCHEMA_VERSION, coveredByAny, isAnyState, routingWarnings, unhandledSends } from "@mantaq/viz";
2
+ import { ANY_STATE, UPDATE_EVENT, VIZ_SCHEMA_VERSION, coveredByAny, isAnyState, livePageHtml, routingWarnings, serializeViz, unhandledSends } from "@mantaq/viz";
3
3
  import { INITIAL_NODE_ID, buildGraph, parseInternalEventIds } from "@mantaq/traversal";
4
4
  import { Either } from "@mantaq/utils";
5
5
  import ts from "typescript";
6
- import { dirname, join, relative, resolve, sep } from "node:path";
6
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
7
  import { DEFAULT_SEED } from "@mantaq/pbt";
8
8
  import { VirtualClock } from "@mantaq/core";
9
- import { existsSync } from "node:fs";
9
+ import { existsSync, globSync, readFileSync } from "node:fs";
10
10
  import { fileURLToPath } from "node:url";
11
- import { loadConfigFromFile, loadEnv, runnerImport } from "vite";
11
+ import { loadConfigFromFile, loadEnv, runnerImport, searchForWorkspaceRoot } from "vite";
12
12
  // src/structure.ts
13
13
  function byId$1(left, right) {
14
14
  if (left.id < right.id) return -1;
@@ -793,6 +793,15 @@ function callDoc(call) {
793
793
  while (ts.isPropertyAccessExpression(top.parent) || ts.isCallExpression(top.parent)) top = top.parent;
794
794
  return docOf(ts.isExpressionStatement(top.parent) ? top.parent : call);
795
795
  }
796
+ function collectEffectDoc(checker, call, effectDocs) {
797
+ if (!ts.isPropertyAccessExpression(call.expression) || call.expression.name.text !== "effect") return;
798
+ const [stateArg] = call.arguments;
799
+ if (stateArg === void 0) return;
800
+ const state = literalProperty(checker, stateArg, "name");
801
+ const doc = callDoc(call);
802
+ if (state === null || doc === null) return;
803
+ effectDocs.set(state, [...effectDocs.get(state) ?? [], doc]);
804
+ }
796
805
  function compilerOptions(source) {
797
806
  const configPath = source.tsconfig ?? ts.findConfigFile(dirname(source.file), (path) => ts.sys.fileExists(path));
798
807
  if (configPath === void 0) return {
@@ -880,6 +889,7 @@ function loadSource(source) {
880
889
  const registrations = [];
881
890
  const stateDocs = /* @__PURE__ */ new Map();
882
891
  const eventDocs = /* @__PURE__ */ new Map();
892
+ const effectDocs = /* @__PURE__ */ new Map();
883
893
  const visit = (node) => {
884
894
  const found = ts.isCallExpression(node) ? toRegistration(checker, node) : null;
885
895
  if (found) registrations.push(found);
@@ -888,6 +898,7 @@ function loadSource(source) {
888
898
  eventDocs
889
899
  });
890
900
  ts.forEachChild(node, visit);
901
+ if (ts.isCallExpression(node)) collectEffectDoc(checker, node, effectDocs);
891
902
  };
892
903
  visit(sourceFile);
893
904
  return {
@@ -896,7 +907,8 @@ function loadSource(source) {
896
907
  root: source.root,
897
908
  registrations,
898
909
  stateDocs,
899
- eventDocs
910
+ eventDocs,
911
+ effectDocs
900
912
  };
901
913
  });
902
914
  if (Either.isLeft(loaded)) return Either.left(`compiler failed: ${String(loaded[0])}`);
@@ -1444,7 +1456,8 @@ function docsOf(loaded) {
1444
1456
  return {
1445
1457
  sites: sitesOf(loaded),
1446
1458
  stateDocs: loaded.stateDocs,
1447
- eventDocs: loaded.eventDocs
1459
+ eventDocs: loaded.eventDocs,
1460
+ effectDocs: loaded.effectDocs
1448
1461
  };
1449
1462
  }
1450
1463
  /** Handlers, payload samples and machine exports of a file, from one TypeScript program. */
@@ -1460,10 +1473,17 @@ function localState(id) {
1460
1473
  return id.slice(id.lastIndexOf(".") + 1);
1461
1474
  }
1462
1475
  function explain(edge, context) {
1463
- if (edge.kind === "initial" || edge.kind === "effect") return {
1476
+ if (edge.kind === "initial") return {
1464
1477
  explanation: null,
1465
1478
  doc: null
1466
1479
  };
1480
+ if (edge.kind === "effect") {
1481
+ const docs = context.effectDocs.get(localState(edge.from));
1482
+ return {
1483
+ explanation: null,
1484
+ doc: docs === void 0 ? null : docs.join("\n\n")
1485
+ };
1486
+ }
1467
1487
  const state = localState(edge.from);
1468
1488
  const site = context.sites.find((candidate) => candidate.state === state && candidate.event === edge.event) ?? context.sites.find((candidate) => candidate.state === null && candidate.event === edge.event);
1469
1489
  if (site) return {
@@ -1513,6 +1533,7 @@ function annotateWith(machine, context) {
1513
1533
  ...edge,
1514
1534
  ...explain(edge, {
1515
1535
  sites: analysis[1].sites,
1536
+ effectDocs: analysis[1].effectDocs,
1516
1537
  file
1517
1538
  })
1518
1539
  };
@@ -1837,4 +1858,155 @@ async function inspectFile(file, options = {}, load = {}) {
1837
1858
  failed: false
1838
1859
  };
1839
1860
  }
1840
- export { toVizMachine as S, fuzzedMachine as _, inspect as a, shrink as b, projectRoot as c, analyzeSource as d, annotateMachine as f, literalPool as g, guidance as h, runOf as i, vizDocument as l, branchDistanceGuide as m, inspectEntry as n, inspectModule as o, branchDistance as p, inspectFile as r, machineId as s, entryId as t, analyzeModule as u, withTestCoverage as v, toViz as x, fuzz as y };
1861
+ // src/vite.ts
1862
+ /**
1863
+ * Vite plugin: a live page of your machines on your own dev server.
1864
+ *
1865
+ * // vite.config.ts
1866
+ * import { mantaqViz } from "@mantaq/inspect/vite";
1867
+ * export default defineConfig({ plugins: [mantaqViz({ machines: "src/**\/*.machine.ts" })] });
1868
+ *
1869
+ * Open `/__mantaq/` while the dev server runs. Machines load through your own
1870
+ * Vite config (aliases, tsconfig paths), are inspected on the server, and the
1871
+ * page redraws, keeping the walk, when you save a file.
1872
+ */
1873
+ const STYLESHEETS = ["@mantaq/viz/viewer.css", "@mantaq/viz/page.css"];
1874
+ const DEV_ITERATIONS = 500;
1875
+ const TARGETS = "virtual:mantaq/targets";
1876
+ const TARGETS_RESOLVED = `\0${TARGETS}`;
1877
+ function createExpression(found, index) {
1878
+ const callee = `m${index}[${JSON.stringify(found.name)}]`;
1879
+ const call = found.call === "clock" ? `${callee}(VirtualClock())` : found.call === "options" ? `${callee}({ clock: VirtualClock() })` : `${callee}()`;
1880
+ return found.unwrap === "actor" ? `${call}.actor` : call;
1881
+ }
1882
+ /**
1883
+ * Source of `virtual:mantaq/targets`: one factory per exported actor factory,
1884
+ * shaped by its type (`call`, `clock`, `options`). A bare actor export cannot
1885
+ * be recreated, so it gets none. Like the server-side inspection, plain actors
1886
+ * are skipped when a file has factories.
1887
+ */
1888
+ function targetsSource(files) {
1889
+ const core = fileURLToPath(import.meta.resolve("@mantaq/core"));
1890
+ const imports = [];
1891
+ const entries = [];
1892
+ files.forEach((file, index) => {
1893
+ const analysis = analyzeModule({
1894
+ file,
1895
+ root: projectRoot(file)
1896
+ });
1897
+ if (Either.isLeft(analysis)) return;
1898
+ const factories = analysis[1].machines.filter((found) => found.call !== "instance");
1899
+ if (factories.length === 0) return;
1900
+ imports.push(`import * as m${index} from ${JSON.stringify(file)};`);
1901
+ for (const found of factories) entries.push(` ${JSON.stringify(machineId(found.name))}: () => { const actor = ${createExpression(found, index)}; return { actor, clock: actor.clock }; },`);
1902
+ });
1903
+ return [
1904
+ `import { VirtualClock } from ${JSON.stringify(core)};`,
1905
+ ...imports,
1906
+ "export const targets = {",
1907
+ ...entries,
1908
+ "};"
1909
+ ].join("\n");
1910
+ }
1911
+ function machineFiles(root, patterns) {
1912
+ const files = patterns.flatMap((pattern) => globSync(pattern, { cwd: root }));
1913
+ return [...new Set(files.map((file) => isAbsolute(file) ? file : resolve(root, file)))].sort();
1914
+ }
1915
+ async function documentText(server, context) {
1916
+ const results = [];
1917
+ for (const file of context.files) {
1918
+ const run = runOf(await server.ssrLoadModule(entryId(file)));
1919
+ results.push(run === null ? {
1920
+ machines: [],
1921
+ warnings: [`${file}: could not load the inspector`]
1922
+ } : await run({ fuzz: context.fuzz }));
1923
+ }
1924
+ return {
1925
+ text: serializeViz(vizDocument(results.flatMap((result) => result.machines))),
1926
+ warnings: results.flatMap((result) => result.warnings)
1927
+ };
1928
+ }
1929
+ function mantaqViz(options) {
1930
+ const base = (options.base ?? "/__mantaq/").replace(/\/?$/, "/");
1931
+ const patterns = Array.isArray(options.machines) ? options.machines : [options.machines];
1932
+ const fuzz = options.fuzz ?? { iterations: DEV_ITERATIONS };
1933
+ const live = fileURLToPath(import.meta.resolve(import.meta.url.endsWith(".ts") ? "./live-entry.ts" : "./live-entry.mjs"));
1934
+ const viz = dirname(dirname(fileURLToPath(import.meta.resolve("@mantaq/viz/live"))));
1935
+ let root = process.cwd();
1936
+ return [
1937
+ inspectEntry(),
1938
+ {
1939
+ name: "mantaq-viz-targets",
1940
+ apply: "serve",
1941
+ configResolved: (config) => {
1942
+ root = config.root;
1943
+ },
1944
+ resolveId: (id) => id === "virtual:mantaq/targets" ? TARGETS_RESOLVED : null,
1945
+ load: (id) => id === "\0virtual:mantaq/targets" ? targetsSource(machineFiles(root, patterns)) : null
1946
+ },
1947
+ {
1948
+ name: "mantaq-viz",
1949
+ apply: "serve",
1950
+ config: (config) => ({ server: { fs: { allow: [
1951
+ searchForWorkspaceRoot(config.root ?? process.cwd()),
1952
+ dirname(dirname(live)),
1953
+ viz
1954
+ ] } } }),
1955
+ configureServer(server) {
1956
+ let cached = null;
1957
+ const build = async () => {
1958
+ const { text, warnings } = await documentText(server, {
1959
+ files: machineFiles(server.config.root, patterns),
1960
+ fuzz
1961
+ });
1962
+ for (const warning of warnings) server.config.logger.warn(`[mantaq-viz] ${warning}`);
1963
+ return text;
1964
+ };
1965
+ server.watcher.on("all", (_event, path) => {
1966
+ if (!/\.[cm]?[jt]sx?$/.test(path)) return;
1967
+ cached = null;
1968
+ const generated = server.moduleGraph.getModuleById(TARGETS_RESOLVED);
1969
+ if (generated !== void 0) server.moduleGraph.invalidateModule(generated);
1970
+ server.environments.ssr.moduleGraph.invalidateAll();
1971
+ server.ws.send({
1972
+ type: "custom",
1973
+ event: UPDATE_EVENT
1974
+ });
1975
+ });
1976
+ server.middlewares.use((request, response, next) => {
1977
+ const path = request.url?.split("?")[0];
1978
+ if (path === `${base}viz.json`) {
1979
+ cached ??= build();
1980
+ cached.then((text) => {
1981
+ response.setHeader("content-type", "application/json");
1982
+ response.end(text);
1983
+ }, (error) => {
1984
+ cached = null;
1985
+ response.statusCode = 500;
1986
+ response.end(String(error));
1987
+ });
1988
+ return;
1989
+ }
1990
+ if (path !== base && path !== base.slice(0, -1)) {
1991
+ next();
1992
+ return;
1993
+ }
1994
+ const html = livePageHtml({
1995
+ title: "mantaq viz",
1996
+ source: `${base}viz.json`,
1997
+ entry: `/@fs${live}`,
1998
+ css: STYLESHEETS.map((id) => readFileSync(fileURLToPath(import.meta.resolve(id)), "utf8")).join("\n")
1999
+ });
2000
+ server.transformIndexHtml(request.url ?? base, html).then((page) => {
2001
+ response.setHeader("content-type", "text/html");
2002
+ response.end(page);
2003
+ });
2004
+ });
2005
+ server.httpServer?.once("listening", () => {
2006
+ server.config.logger.info(` mantaq viz: ${base}`);
2007
+ });
2008
+ }
2009
+ }
2010
+ ];
2011
+ }
2012
+ export { toViz as C, shrink as S, guidance as _, targetsSource as a, withTestCoverage as b, inspect as c, vizDocument as d, analyzeModule as f, branchDistanceGuide as g, branchDistance as h, mantaqViz as i, inspectModule as l, annotateMachine as m, TARGETS as n, inlineConfig as o, analyzeSource as p, TARGETS_RESOLVED as r, inspectFile as s, STYLESHEETS as t, machineId as u, literalPool as v, toVizMachine as w, fuzz as x, fuzzedMachine as y };
package/dist/vite.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { t as FuzzSettings } from "./inspect-DvQtAcwv.mjs";
2
2
  import { Plugin } from "vite";
3
+ export declare const STYLESHEETS: string[];
3
4
  export interface MantaqVizOptions {
4
5
  /** Files that export machines: paths or globs, relative to the Vite root. */
5
6
  machines: string | string[];
@@ -8,4 +9,13 @@ export interface MantaqVizOptions {
8
9
  /** `false` = structure only. Default: 500 iterations, so a save redraws quickly. */
9
10
  fuzz?: false | FuzzSettings;
10
11
  }
12
+ export declare const TARGETS = "virtual:mantaq/targets";
13
+ export declare const TARGETS_RESOLVED = "\0virtual:mantaq/targets";
14
+ /**
15
+ * Source of `virtual:mantaq/targets`: one factory per exported actor factory,
16
+ * shaped by its type (`call`, `clock`, `options`). A bare actor export cannot
17
+ * be recreated, so it gets none. Like the server-side inspection, plain actors
18
+ * are skipped when a file has factories.
19
+ */
20
+ export declare function targetsSource(files: ReadonlyArray<string>): string;
11
21
  export declare function mantaqViz(options: MantaqVizOptions): Plugin[];
package/dist/vite.mjs CHANGED
@@ -1,159 +1 @@
1
- import { c as projectRoot, i as runOf, l as vizDocument, n as inspectEntry, s as machineId, t as entryId, u as analyzeModule } from "./load-DOwsxTyL.mjs";
2
- import { UPDATE_EVENT, livePageHtml, serializeViz } from "@mantaq/viz";
3
- import { Either } from "@mantaq/utils";
4
- import { dirname, isAbsolute, resolve } from "node:path";
5
- import { globSync, readFileSync } from "node:fs";
6
- import { fileURLToPath } from "node:url";
7
- import { searchForWorkspaceRoot } from "vite";
8
- // src/vite.ts
9
- /**
10
- * Vite plugin: a live page of your machines on your own dev server.
11
- *
12
- * // vite.config.ts
13
- * import { mantaqViz } from "@mantaq/inspect/vite";
14
- * export default defineConfig({ plugins: [mantaqViz({ machines: "src/**\/*.machine.ts" })] });
15
- *
16
- * Open `/__mantaq/` while the dev server runs. Machines load through your own
17
- * Vite config (aliases, tsconfig paths), are inspected on the server, and the
18
- * page redraws, keeping the walk, when you save a file.
19
- */
20
- const STYLESHEETS = ["@mantaq/viz/viewer.css", "@mantaq/viz/page.css"];
21
- const DEV_ITERATIONS = 500;
22
- const TARGETS = "virtual:mantaq/targets";
23
- const TARGETS_RESOLVED = `\0${TARGETS}`;
24
- function createExpression(found, index) {
25
- const callee = `m${index}[${JSON.stringify(found.name)}]`;
26
- const call = found.call === "clock" ? `${callee}(VirtualClock())` : found.call === "options" ? `${callee}({ clock: VirtualClock() })` : `${callee}()`;
27
- return found.unwrap === "actor" ? `${call}.actor` : call;
28
- }
29
- /**
30
- * Source of `virtual:mantaq/targets`: one factory per exported actor factory,
31
- * shaped by its type (`call`, `clock`, `options`). A bare actor export cannot
32
- * be recreated, so it gets none. Like the server-side inspection, plain actors
33
- * are skipped when a file has factories.
34
- */
35
- function targetsSource(files) {
36
- const core = fileURLToPath(import.meta.resolve("@mantaq/core"));
37
- const imports = [];
38
- const entries = [];
39
- files.forEach((file, index) => {
40
- const analysis = analyzeModule({
41
- file,
42
- root: projectRoot(file)
43
- });
44
- if (Either.isLeft(analysis)) return;
45
- const factories = analysis[1].machines.filter((found) => found.call !== "instance");
46
- if (factories.length === 0) return;
47
- imports.push(`import * as m${index} from ${JSON.stringify(file)};`);
48
- for (const found of factories) entries.push(` ${JSON.stringify(machineId(found.name))}: () => { const actor = ${createExpression(found, index)}; return { actor, clock: actor.clock }; },`);
49
- });
50
- return [
51
- `import { VirtualClock } from ${JSON.stringify(core)};`,
52
- ...imports,
53
- "export const targets = {",
54
- ...entries,
55
- "};"
56
- ].join("\n");
57
- }
58
- function machineFiles(root, patterns) {
59
- const files = patterns.flatMap((pattern) => globSync(pattern, { cwd: root }));
60
- return [...new Set(files.map((file) => isAbsolute(file) ? file : resolve(root, file)))].sort();
61
- }
62
- async function documentText(server, context) {
63
- const results = [];
64
- for (const file of context.files) {
65
- const run = runOf(await server.ssrLoadModule(entryId(file)));
66
- results.push(run === null ? {
67
- machines: [],
68
- warnings: [`${file}: could not load the inspector`]
69
- } : await run({ fuzz: context.fuzz }));
70
- }
71
- return {
72
- text: serializeViz(vizDocument(results.flatMap((result) => result.machines))),
73
- warnings: results.flatMap((result) => result.warnings)
74
- };
75
- }
76
- function mantaqViz(options) {
77
- const base = (options.base ?? "/__mantaq/").replace(/\/?$/, "/");
78
- const patterns = Array.isArray(options.machines) ? options.machines : [options.machines];
79
- const fuzz = options.fuzz ?? { iterations: DEV_ITERATIONS };
80
- const live = fileURLToPath(import.meta.resolve(import.meta.url.endsWith(".ts") ? "./live-entry.ts" : "./live-entry.mjs"));
81
- const viz = dirname(dirname(fileURLToPath(import.meta.resolve("@mantaq/viz/live"))));
82
- let root = process.cwd();
83
- return [
84
- inspectEntry(),
85
- {
86
- name: "mantaq-viz-targets",
87
- apply: "serve",
88
- configResolved: (config) => {
89
- root = config.root;
90
- },
91
- resolveId: (id) => id === TARGETS ? TARGETS_RESOLVED : null,
92
- load: (id) => id === TARGETS_RESOLVED ? targetsSource(machineFiles(root, patterns)) : null
93
- },
94
- {
95
- name: "mantaq-viz",
96
- apply: "serve",
97
- config: (config) => ({ server: { fs: { allow: [
98
- searchForWorkspaceRoot(config.root ?? process.cwd()),
99
- dirname(dirname(live)),
100
- viz
101
- ] } } }),
102
- configureServer(server) {
103
- let cached = null;
104
- const build = async () => {
105
- const { text, warnings } = await documentText(server, {
106
- files: machineFiles(server.config.root, patterns),
107
- fuzz
108
- });
109
- for (const warning of warnings) server.config.logger.warn(`[mantaq-viz] ${warning}`);
110
- return text;
111
- };
112
- server.watcher.on("all", (_event, path) => {
113
- if (!/\.[cm]?[jt]sx?$/.test(path)) return;
114
- cached = null;
115
- const generated = server.moduleGraph.getModuleById(TARGETS_RESOLVED);
116
- if (generated !== void 0) server.moduleGraph.invalidateModule(generated);
117
- server.environments.ssr.moduleGraph.invalidateAll();
118
- server.ws.send({
119
- type: "custom",
120
- event: UPDATE_EVENT
121
- });
122
- });
123
- server.middlewares.use((request, response, next) => {
124
- const path = request.url?.split("?")[0];
125
- if (path === `${base}viz.json`) {
126
- cached ??= build();
127
- cached.then((text) => {
128
- response.setHeader("content-type", "application/json");
129
- response.end(text);
130
- }, (error) => {
131
- cached = null;
132
- response.statusCode = 500;
133
- response.end(String(error));
134
- });
135
- return;
136
- }
137
- if (path !== base && path !== base.slice(0, -1)) {
138
- next();
139
- return;
140
- }
141
- const html = livePageHtml({
142
- title: "mantaq viz",
143
- source: `${base}viz.json`,
144
- entry: `/@fs${live}`,
145
- css: STYLESHEETS.map((id) => readFileSync(fileURLToPath(import.meta.resolve(id)), "utf8")).join("\n")
146
- });
147
- server.transformIndexHtml(request.url ?? base, html).then((page) => {
148
- response.setHeader("content-type", "text/html");
149
- response.end(page);
150
- });
151
- });
152
- server.httpServer?.once("listening", () => {
153
- server.config.logger.info(` mantaq viz: ${base}`);
154
- });
155
- }
156
- }
157
- ];
158
- }
159
- export { mantaqViz };
1
+ export { t as STYLESHEETS, n as TARGETS, r as TARGETS_RESOLVED, i as mantaqViz, a as targetsSource } from "./vite-BBW0CmYr.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantaq/inspect",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Turn a Mantaq machine into a viz document: structure, handler explanations (AST), fuzz and test coverage. CLI and Vite plugin included.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,6 +18,7 @@
18
18
  "exports": {
19
19
  ".": "./dist/index.mjs",
20
20
  "./drive": "./dist/drive.mjs",
21
+ "./live-embedded": "./dist/live-embedded.mjs",
21
22
  "./live-entry": "./dist/live-entry.mjs",
22
23
  "./vite": "./dist/vite.mjs",
23
24
  "./package.json": "./package.json"
@@ -27,11 +28,11 @@
27
28
  },
28
29
  "dependencies": {
29
30
  "typescript": "6.0.3",
30
- "@mantaq/pbt": "0.0.3",
31
- "@mantaq/traversal": "0.3.2",
32
- "@mantaq/core": "0.5.2",
33
- "@mantaq/viz": "0.2.1",
34
- "@mantaq/utils": "0.0.4"
31
+ "@mantaq/core": "0.5.3",
32
+ "@mantaq/traversal": "0.3.3",
33
+ "@mantaq/viz": "0.2.3",
34
+ "@mantaq/pbt": "0.0.4",
35
+ "@mantaq/utils": "0.0.5"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@types/node": "^26.2.0",
@@ -39,7 +40,7 @@
39
40
  "happy-dom": "^21.0.4",
40
41
  "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
41
42
  "vite-plus": "1.4.0",
42
- "@mantaq/sugar": "2.0.2"
43
+ "@mantaq/sugar": "2.0.3"
43
44
  },
44
45
  "peerDependencies": {
45
46
  "vite": "*"
@@ -1,38 +0,0 @@
1
- import { serializeViz, standaloneHtml } from "@mantaq/viz";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { build } from "vite";
5
- // src/page.ts
6
- /** The viewer as one classic script, built from `@mantaq/viz/standalone`, plus its stylesheets. */
7
- async function viewerBundle() {
8
- const entry = fileURLToPath(import.meta.resolve("@mantaq/viz/standalone"));
9
- const output = await build({
10
- configFile: false,
11
- logLevel: "silent",
12
- build: {
13
- write: false,
14
- minify: true,
15
- cssCodeSplit: false,
16
- lib: {
17
- entry,
18
- formats: ["iife"],
19
- name: "MantaqViz",
20
- fileName: () => "viz.js"
21
- }
22
- }
23
- });
24
- return {
25
- script: (Array.isArray(output) ? output : [output]).flatMap((each) => "output" in each ? each.output : []).flatMap((file) => file.type === "chunk" ? [file.code] : []).join("\n"),
26
- css: ["@mantaq/viz/viewer.css", "@mantaq/viz/page.css"].map((id) => readFileSync(fileURLToPath(import.meta.resolve(id)), "utf8")).join("\n")
27
- };
28
- }
29
- /** A self-contained HTML page for `document`: open it from disk, attach it, commit it. */
30
- async function htmlPage(document, title) {
31
- const bundle = await viewerBundle();
32
- return standaloneHtml({
33
- title,
34
- ...bundle,
35
- document: serializeViz(document)
36
- });
37
- }
38
- export { htmlPage as t };