@mantaq/inspect 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anders Can
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,95 @@
1
- # Temporary Holding Version
1
+ # @mantaq/inspect
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Turns your Mantaq machine into a picture you can walk: one command, no config.
4
+
5
+ ```sh
6
+ mantaq-inspect src/cart.ts # writes mantaq-viz.html and opens it
7
+ ```
8
+
9
+ It finds every exported actor, or function that builds one, from the file's types. It reads handler explanations and sample payloads from the source, fuzzes each machine that runs on a `VirtualClock`, and writes one self-contained HTML page that works offline: commit it, attach it to a PR. The viewer is [`@mantaq/viz`](../viz/README.md).
10
+
11
+ ## Three ways in
12
+
13
+ **CLI**: `mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--no-open]`. `--json` writes the viz document instead of the page. Seed: `MANTAQ_SEED` (default from `@mantaq/pbt`). Same seed → same output.
14
+
15
+ **Vite plugin**: a live page on your dev server that redraws, keeping your walk, when you save.
16
+
17
+ ```ts
18
+ // vite.config.ts
19
+ import { mantaqViz } from "@mantaq/inspect/vite";
20
+
21
+ export default defineConfig({
22
+ plugins: [mantaqViz({ machines: "src/**/*.machine.ts" })],
23
+ });
24
+ // open /__mantaq/ while `vp dev` runs
25
+ ```
26
+
27
+ Options: `base` (default `/__mantaq/`), `fuzz` (`false`, or `{ seed, iterations }`; default 500 iterations so a save redraws fast).
28
+
29
+ **Library**: one call per machine.
30
+
31
+ ```ts
32
+ import { inspect, vizDocument } from "@mantaq/inspect";
33
+ import { serializeViz } from "@mantaq/viz";
34
+
35
+ const { machine, warnings } = inspect({
36
+ id: "cart",
37
+ create: () => createCart(), // a fresh actor every call
38
+ source: { file: "/abs/src/cart.ts", root: "/abs" }, // optional: explanations + payloads from types
39
+ tests: [[{ kind: "event", event: "addItem", payload: { sku: "A1" } }]], // optional: "example tests" coverage
40
+ });
41
+ const json = serializeViz(vizDocument([machine]));
42
+ ```
43
+
44
+ `inspectFile(file)` / `inspectModule(file, { load })` do the discovery the CLI does. The building blocks (`toVizMachine`, `annotateMachine`, `fuzz`, `fuzzedMachine`, `withTestCoverage`, `guidance`) stay exported for custom pipelines.
45
+
46
+ ## What it needs from your file
47
+
48
+ - **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.
49
+ - **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.
50
+ - **A `VirtualClock`**, to fuzz. An actor on the real clock is drawn, with a warning.
51
+
52
+ Every part that cannot run is skipped with a warning, never guessed.
53
+
54
+ ## Coverage: two sources, two questions
55
+
56
+ The viewer's "Coverage from" picker dims elements with zero hits. What dimmed means depends on the source:
57
+
58
+ - **example tests** (`tested`): no test exercises this. Collected by replaying the event sequences you pass as `tests`, each on a fresh actor.
59
+ - **fuzz runs** (`coverage`): no fuzz run produced this. Fuzzing reaches almost everything, so little is dimmed. What does stay dimmed is usually a **declared but unreached edge**: the handler lists the target in `to`, but its guards may never allow it. Saga's `compensatingRefund -REFUND_DONE-> failed` is an example. The edge panel calls these out. Fuzzing is not proof, so check the handler.
60
+
61
+ ## How each tool works
62
+
63
+ **Structure** (`toViz`, Phase 1): states come from the actor's `states`, edges from each registration's `to` (`actor.options.targets`): one edge per declared target, a `stay` edge when `to` is absent. `onAny` registrations draw from one `*` source per machine or region, never once per state. Coverage of a `*` edge counts every covered state; a state's own handler for the same event is not told apart. Nothing runs to find them. `@mantaq/traversal` still calls each handler with payload `{}` and each sample context, but only to label edges with the contexts that reach them and what they emit.
64
+
65
+ **Fuzzer** (`fuzz`, Phase 3): a fresh actor and `VirtualClock` per run, so it is synchronous and deterministic. Mutations: insert, delete, swap, payload leaf tweak, time advance, and **loop**. Loop repeats, 2–13 times or a compared literal's count, a step that changed the actor's context, so counter guards like `if (attempts > 10)` become reachable. A step that changes nothing is never looped. A run is kept when it reaches a new feature (`state:x`, `edge:a>e>b`, `stay:a>e`, `error:a>e`), then shrunk to 1-minimal. Internal events are injected only when the current state handles them, as a live effect would (core rejects the rest). Payloads come from the per-machine samples and are mutated leaf by leaf, keeping each leaf's JSON type and never dropping keys. So a handler that throws is a real finding, not a type violation the compiler would reject.
66
+
67
+ **Payloads** (`analyzeModule`): the TypeScript checker reads each handled event's payload type (`event("x")<{ ... }>()`) and builds samples: `"sample"` for strings, `1` for numbers, every member of a literal union (varied one field at a time), one element per array. The fuzzer mutates from there. Samples you pass replace them per event.
68
+
69
+ **AST** (`annotateMachine`, Phase 4): one `ts.Program` per source file with the project tsconfig. It finds `m.on(state, { event, handler })` / `m.onAny({ event, handler })` and resolves names from the literal types of `state.name` / `event.type`. It walks inline handlers: if/else, early returns, ternaries, and a ternary inside `state:`. A specific `on` wins over `onAny`, as at runtime.
70
+
71
+ **Guidance** (`guidance`, Phase 5): literal seeding plus a branch-distance guide built from AST comparisons on payload and context. It helps when handlers compare payload fields against literals far from the samples, or count in context toward a threshold. See `docs/atlas/phase5-comparison.md`.
72
+
73
+ ## What it cannot show
74
+
75
+ - **Guards** are not evaluated. A declared edge says "the handler may return this", not "some input reaches it". Fuzz coverage and the AST's branch conditions are the evidence; neither is proof.
76
+ - **`onAny` routing on live actor state** (`actor.state.name`) cannot be drawn exactly: the `*` edges say "from any state, may go to X", not which state leads where. Register per-state `m.on` handlers instead, as checkout's `back` does. `routingWarnings(machine)` lists these handlers (the viewer and `inspect` warnings show them); it needs AST explanations.
77
+ - **Payload types** come from the source. Without `source`, the fuzzer knows only the shapes you sample. Loose types (`string`, `unknown`) give loose samples. The document's `payloadType` is always `null`.
78
+ - **Effects** are known by name and declared `sends` only. An effect without `sends` shows `sends not declared`, and its events fall under "other internal events". `inspect` warns when a declared send has no handler in the effect's state. Example fixtures stub effects, and the fuzzer stands in for them by injecting internal events. It can therefore produce event orders a real effect never would, but only in states that handle the event.
79
+ - **Regions**: the fuzzer observes the root actor only. Region transitions are probed but not fuzzed.
80
+ - **AST limits**: handlers passed by reference, returns inside `switch` / loops / `try`, and computed `event`s are `not_analyzed`, with a reason and snippet. Comparisons against actor state or computed values carry no guidance.
81
+ - **History** cannot distinguish an ignored event from a self-loop. The fuzzer can (`stay` vs `transition`), but probe-only coverage cannot.
82
+ - **Fuzzing is not proof.** An edge that is absent was not reached; it is not shown to be impossible.
83
+
84
+ ## Fixtures
85
+
86
+ `src/fixtures/{checkout,auth,saga}.ts` copy the handlers of `packages/examples/*.test.ts` verbatim, with effects stubbed. The examples live in test files and cannot be imported, so the copies can drift from them. `credit.ts` (conditional handlers, far literals), `retry.ts` (context counter, `attempts > 10`) and `switchy.ts` (unsupported control flow) exist for the AST, loop and guidance tests.
87
+
88
+ ## Repo scripts
89
+
90
+ ```sh
91
+ vp run viz:export # bundled examples -> packages/viz/public/examples/*.json (the viz dev page loads them)
92
+ vp run viz:compare # blind vs guided fuzzing report (Markdown, 30 seeds)
93
+ ```
94
+
95
+ `export` options: `[outDir] --iterations N --no-fuzz --no-ast`.
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cli.mjs ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ import { c as vizDocument, r as inspectFile } from "./load-C4rGWmGU.mjs";
3
+ import { t as htmlPage } from "./page-rr9IQ-4k.mjs";
4
+ import { serializeViz } from "@mantaq/viz";
5
+ import { Either } from "@mantaq/utils";
6
+ import { basename, resolve } from "node:path";
7
+ import { SEED_ENV, parseSeed } from "@mantaq/pbt";
8
+ import { writeFileSync } from "node:fs";
9
+ import { spawn } from "node:child_process";
10
+ import { parseArgs } from "node:util";
11
+ // src/cli.ts
12
+ /**
13
+ * mantaq-inspect: draw the machines a file exports.
14
+ *
15
+ * mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--no-open]
16
+ *
17
+ * Finds every exported actor or actor factory from the file's types, reads
18
+ * handler explanations and sample payloads from the source, fuzzes each
19
+ * machine that runs on a VirtualClock, and writes one self-contained HTML page
20
+ * (default `mantaq-viz.html`) that opens without a server. `--json` writes the
21
+ * viz document instead. Seed: MANTAQ_SEED (default from @mantaq/pbt).
22
+ */
23
+ const USAGE = "usage: mantaq-inspect <file.ts>... [--out FILE] [--json] [--no-fuzz] [--iterations N] [--no-open]\n";
24
+ function parse() {
25
+ const parsed = Either.from(() => parseArgs({
26
+ allowPositionals: true,
27
+ options: {
28
+ out: { type: "string" },
29
+ json: { type: "boolean" },
30
+ iterations: { type: "string" },
31
+ "no-fuzz": { type: "boolean" },
32
+ "no-open": { type: "boolean" }
33
+ }
34
+ }));
35
+ if (Either.isLeft(parsed)) return Either.left(String(parsed[0]));
36
+ const { positionals, values } = parsed[1];
37
+ if (positionals.length === 0) return Either.left("no file given");
38
+ const iterations = Number(values.iterations ?? 2e3);
39
+ if (!Number.isInteger(iterations) || iterations < 0) return Either.left("bad --iterations");
40
+ const seed = parseSeed(process.env[SEED_ENV]);
41
+ if (Either.isLeft(seed)) return Either.left(seed[0].message);
42
+ const json = values.json === true;
43
+ const fuzz = values["no-fuzz"] === true ? false : {
44
+ seed: seed[1],
45
+ iterations
46
+ };
47
+ return Either.right({
48
+ files: positionals.map((file) => resolve(file)),
49
+ out: resolve(values.out ?? (json ? "mantaq-viz.json" : "mantaq-viz.html")),
50
+ json,
51
+ fuzz,
52
+ open: values["no-open"] !== true && !json && process.stdout.isTTY === true
53
+ });
54
+ }
55
+ function openInBrowser(path) {
56
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
57
+ spawn(command, [path], {
58
+ stdio: "ignore",
59
+ detached: true
60
+ }).on("error", () => {}).unref();
61
+ }
62
+ async function main() {
63
+ const options = parse();
64
+ if (Either.isLeft(options)) {
65
+ process.stderr.write(`${options[0]}\n${USAGE}`);
66
+ process.exitCode = 1;
67
+ return;
68
+ }
69
+ const { files, out, json, fuzz, open } = options[1];
70
+ const results = [];
71
+ for (const file of files) results.push(await inspectFile(file, { fuzz }));
72
+ for (const warning of results.flatMap((result) => result.warnings)) process.stderr.write(`warning: ${warning}\n`);
73
+ const machines = results.flatMap((result) => result.machines);
74
+ if (machines.length === 0) {
75
+ process.stderr.write("no machines found\n");
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ const document = vizDocument(machines);
80
+ const title = `${files.map((file) => basename(file)).join(", ")} · mantaq viz`;
81
+ writeFileSync(out, json ? serializeViz(document) : await htmlPage(document, title));
82
+ process.stdout.write(`wrote ${out} (${machines.map((machine) => machine.id).join(", ")})\n`);
83
+ if (open) openInBrowser(out);
84
+ }
85
+ main();
86
+ export {};
@@ -0,0 +1,195 @@
1
+ import { a as ModuleOptions, c as machineId, i as InspectedModule, l as vizDocument, n as InspectTarget, o as inspect, r as Inspected, s as inspectModule, t as FuzzSettings, u as AnalysisSource } from "./inspect-DvQtAcwv.mjs";
2
+ import { VizComparison, VizDocument, VizExplanation, VizFuzzError, VizJson, VizMachine, VizStep } from "@mantaq/viz";
3
+ import { History } from "@mantaq/traversal";
4
+ import { Either } from "@mantaq/utils";
5
+ import { AnyActor, VirtualClock } from "@mantaq/core";
6
+ import { Plugin } from "vite";
7
+ interface VizInput {
8
+ id: string;
9
+ /**
10
+ * Fresh actor to probe for structure. Handlers that read live actor state
11
+ * see this actor's current state, so pass one that has not been driven.
12
+ */
13
+ actor: AnyActor<unknown>;
14
+ /** History from `instrument(actor)`; omit for a structure-only document. */
15
+ history?: History;
16
+ sampleContexts?: Record<string, Record<string, unknown>>;
17
+ }
18
+ /** One machine; `toViz` is this plus sorting into a document. */
19
+ export declare function toVizMachine(input: VizInput): VizMachine;
20
+ export declare function toViz(inputs: ReadonlyArray<VizInput>): VizDocument;
21
+ /** Extra values to try at payload leaves (Phase 5 literal seeding). Empty = blind mutation. */
22
+ interface LiteralPool {
23
+ numbers: number[];
24
+ strings: string[];
25
+ }
26
+ /** A machine the fuzzer can drive: a fresh actor plus the virtual clock it runs on. */
27
+ interface FuzzTarget {
28
+ id: string;
29
+ create(): {
30
+ actor: AnyActor<unknown>;
31
+ clock: VirtualClock;
32
+ };
33
+ /**
34
+ * Sample payloads per event type. Payload types are erased at runtime, so
35
+ * the fuzzer only mutates shapes it was given. Events without samples are
36
+ * sent without a payload.
37
+ */
38
+ payloads?: Readonly<Record<string, ReadonlyArray<VizJson>>>;
39
+ }
40
+ interface Observation {
41
+ from: string;
42
+ event: string;
43
+ to: string;
44
+ transitioned: boolean;
45
+ /** Names of the effects that ran on entry into `to`. */
46
+ effects: string[];
47
+ /** Index of the step that caused it. */
48
+ step: number;
49
+ /** Payload the handler received (for branch-distance guidance). */
50
+ payload: unknown;
51
+ /** JSON copy of the actor context right after the handler ran; null if not JSON. */
52
+ context: unknown;
53
+ }
54
+ interface RunError {
55
+ from: string;
56
+ event: string;
57
+ message: string;
58
+ /** Index of the step that triggered it. */
59
+ step: number;
60
+ }
61
+ interface Trace {
62
+ visited: string[];
63
+ observations: Observation[];
64
+ errors: RunError[];
65
+ /** Indices of steps after which the actor context differed: loop candidates. */
66
+ contextSteps: number[];
67
+ /**
68
+ * Coverage features, sorted: `state:x`, `edge:a>e>b` (state changed or
69
+ * re-entered), `stay:a>e` (handled, no change), `error:a>e` (handler threw).
70
+ */
71
+ features: string[];
72
+ }
73
+ /** Declared inputs and internal events, each sorted. */
74
+ export declare function eventCatalog(actor: AnyActor<unknown>): {
75
+ inputs: string[];
76
+ internal: string[];
77
+ };
78
+ /**
79
+ * Replay steps against a fresh actor. Synchronous and deterministic: effects
80
+ * only run via the virtual clock, and nothing here reads wall time.
81
+ */
82
+ export declare function execute(target: FuzzTarget, steps: ReadonlyArray<VizStep>): Trace;
83
+ interface FuzzOptions {
84
+ seed: number;
85
+ iterations: number;
86
+ /** Longest sequence the mutator will grow. Default 24. */
87
+ maxLength?: number;
88
+ literals?: LiteralPool;
89
+ /** Loop context-changing steps (default true). False reproduces plain Phase 3 mutation. */
90
+ repeat?: boolean;
91
+ /**
92
+ * Optional search guidance (Phase 5). Sees every run; returning true keeps
93
+ * the run in the corpus as a stepping stone even without new coverage.
94
+ */
95
+ guide?: (trace: Trace) => boolean;
96
+ }
97
+ interface CorpusEntry {
98
+ steps: VizStep[];
99
+ /** Features first reached by this entry. Empty for guide-only keeps. */
100
+ discovered: string[];
101
+ iteration: number;
102
+ }
103
+ interface FuzzResult {
104
+ seed: number;
105
+ iterations: number;
106
+ corpus: CorpusEntry[];
107
+ /** Feature -> iteration that first reached it. */
108
+ firstHit: Map<string, number>;
109
+ errors: VizFuzzError[];
110
+ }
111
+ /** Greedy one-step deletion, last to first, while `wanted` stays covered. Deterministic. */
112
+ export declare function shrink(target: FuzzTarget, steps: ReadonlyArray<VizStep>, wanted: ReadonlyArray<string>): VizStep[];
113
+ /**
114
+ * Coverage-guided fuzzing. Each iteration mutates a corpus entry, runs it on a
115
+ * fresh actor and keeps it if it reaches a feature (state, edge, stay, error)
116
+ * no earlier run reached. Same target + options => identical result.
117
+ */
118
+ export declare function fuzz(target: FuzzTarget, options: FuzzOptions): FuzzResult;
119
+ /**
120
+ * Viz machine with fuzz results written in: coverage from replaying the
121
+ * shrunk corpus, newly discovered edges (origin `fuzz`), and the corpus itself.
122
+ */
123
+ export declare function fuzzedMachine(target: FuzzTarget, result: FuzzResult): VizMachine;
124
+ /**
125
+ * Fill `tested` from replaying test sequences (each from a fresh actor).
126
+ * Lets the viewer show what the tests never exercise, next to what fuzzing reached.
127
+ */
128
+ export declare function withTestCoverage(target: FuzzTarget, machine: VizMachine, sequences: ReadonlyArray<ReadonlyArray<VizStep>>): VizMachine;
129
+ /**
130
+ * Literal seeding: every literal the handlers compare against, plus its
131
+ * neighbours (`L - 1`, `L + 1`) so boundary sides are both reachable.
132
+ */
133
+ export declare function literalPool(machine: VizMachine): LiteralPool;
134
+ /**
135
+ * Branch distance (Korel/Tracey): how far `value` is from making the
136
+ * comparison `outcome`. 0 = that side is taken. Undefined when incomparable.
137
+ */
138
+ export declare function branchDistance(comparison: VizComparison, context: {
139
+ value: unknown;
140
+ outcome: boolean;
141
+ }): number | undefined;
142
+ /**
143
+ * Stateful guide for `fuzz({ guide })`. For every comparison outcome not yet
144
+ * reached it remembers the smallest distance seen; a run that gets closer is
145
+ * kept as a stepping stone. Reached outcomes stop counting.
146
+ */
147
+ export declare function branchDistanceGuide(machine: VizMachine): (trace: Trace) => boolean;
148
+ /** Phase 5 search options for an AST-annotated machine: literal seeding + branch distance. */
149
+ export declare function guidance(machine: VizMachine): Pick<FuzzOptions, "literals" | "guide">;
150
+ /**
151
+ * How to get a fresh actor from an export, read from its type alone (nothing
152
+ * is called to find out):
153
+ * - `instance`: the export is an actor. It cannot be recreated, so it is
154
+ * drawn but not fuzzed.
155
+ * - `call`: call it with no arguments.
156
+ * - `clock`: call it with a VirtualClock.
157
+ * - `options`: call it with `{ clock: VirtualClock }`.
158
+ *
159
+ * `unwrap`: the result is the actor (`self`) or carries it as `.actor`.
160
+ */
161
+ interface MachineExport {
162
+ name: string;
163
+ call: "instance" | "call" | "clock" | "options";
164
+ unwrap: "self" | "actor";
165
+ }
166
+ /** One explained registration, keyed the way runtime edges are: state + event. */
167
+ interface HandlerSite {
168
+ /** null = onAny. */
169
+ state: string | null;
170
+ event: string;
171
+ explanation: VizExplanation;
172
+ }
173
+ /** Explain every handler registration in a file. Left = the file could not be analyzed at all. */
174
+ export declare function analyzeSource(source: AnalysisSource): Either<string, HandlerSite[]>;
175
+ /** Everything one type-check of a machine file yields. */
176
+ interface ModuleAnalysis {
177
+ sites: HandlerSite[];
178
+ payloads: Record<string, VizJson[]>;
179
+ machines: MachineExport[];
180
+ }
181
+ /** Handlers, payload samples and machine exports of a file, from one TypeScript program. */
182
+ export declare function analyzeModule(source: AnalysisSource): Either<string, ModuleAnalysis>;
183
+ /**
184
+ * Fill `explanation` on handler-driven edges. Never adds or removes edges:
185
+ * the runtime graph stays the source of truth. A source that cannot be
186
+ * analyzed marks every handler edge `not_analyzed` with the reason.
187
+ */
188
+ export declare function annotateMachine(machine: VizMachine, source: AnalysisSource): VizMachine;
189
+ /** Options `inspectFile` passes through to `inspectModule`; loading is its job. */
190
+ type FileOptions = Omit<ModuleOptions, "load">;
191
+ /** Load `file` with Vite (TS, tsconfig paths, extensionless imports) and inspect its machines. */
192
+ export declare function inspectFile(file: string, options?: FileOptions): Promise<InspectedModule>;
193
+ /** A self-contained HTML page for `document`: open it from disk, attach it, commit it. */
194
+ export declare function htmlPage(document: VizDocument, title: string): Promise<string>;
195
+ 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, inspect, inspectModule, machineId, vizDocument };
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { C as toVizMachine, S as toViz, _ as withTestCoverage, a as inspect, b as eventCatalog, c as vizDocument, d as annotateMachine, f as branchDistance, g as fuzzedMachine, h as literalPool, l as analyzeModule, m as guidance, o as inspectModule, p as branchDistanceGuide, r as inspectFile, s as machineId, u as analyzeSource, v as fuzz, x as execute, y as shrink } from "./load-C4rGWmGU.mjs";
2
+ import { t as htmlPage } from "./page-rr9IQ-4k.mjs";
3
+ 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,66 @@
1
+ import { VizDocument, VizJson, VizMachine, VizStep } from "@mantaq/viz";
2
+ import { Either } from "@mantaq/utils";
3
+ import ts from "typescript";
4
+ import { AnyActor } from "@mantaq/core";
5
+ interface AnalysisSource {
6
+ /** Absolute path of the file that registers the handlers (`m.on` / `m.onAny`). */
7
+ file: string;
8
+ /** Locations are reported relative to this directory. */
9
+ root: string;
10
+ /** tsconfig used to type-check the file. Default: nearest one above `file`. */
11
+ tsconfig?: string;
12
+ }
13
+ interface FuzzSettings {
14
+ seed?: number;
15
+ /** Default 2000. */
16
+ iterations?: number;
17
+ }
18
+ interface InspectTarget {
19
+ id: string;
20
+ /** A fresh actor on every call. Fuzzing needs it to run on a VirtualClock. */
21
+ create: () => AnyActor<unknown>;
22
+ /** File with the `m.on` calls: edge explanations, and payload samples from the event types. */
23
+ source?: AnalysisSource;
24
+ /** Sample payloads per event; replace the ones read from types. */
25
+ payloads?: Readonly<Record<string, ReadonlyArray<VizJson>>>;
26
+ /** Event sequences your tests send, for "Coverage from: example tests". */
27
+ tests?: ReadonlyArray<ReadonlyArray<VizStep>>;
28
+ /** `false` = structure only. */
29
+ fuzz?: false | FuzzSettings;
30
+ }
31
+ interface Inspected {
32
+ machine: VizMachine;
33
+ /** Things worth fixing or knowing: unreachable routing, unhandled sends, why fuzzing was skipped. */
34
+ warnings: string[];
35
+ }
36
+ /**
37
+ * One machine in, one viz machine out: structure, edge explanations and
38
+ * payloads from the source's types, fuzz coverage, test coverage. Each part
39
+ * that cannot run is skipped with a warning, never guessed.
40
+ */
41
+ declare function inspect(target: InspectTarget): Inspected;
42
+ /** `createCheckoutActor` → `checkout`; other names are kept. */
43
+ declare function machineId(exportName: string): string;
44
+ interface ModuleOptions {
45
+ /** Import the file and return its exports (e.g. Vite's `runnerImport`, `ssrLoadModule`). */
46
+ load: (file: string) => Promise<Record<string, unknown>>;
47
+ /** Locations are relative to this. Default: nearest package.json above the file. */
48
+ root?: string;
49
+ tsconfig?: string;
50
+ fuzz?: false | FuzzSettings;
51
+ }
52
+ interface InspectedModule {
53
+ machines: VizMachine[];
54
+ /** Prefixed with the machine id, or the file when it is about the file. */
55
+ warnings: string[];
56
+ }
57
+ /**
58
+ * Every machine a file exports, found from its types: actors, and functions
59
+ * that build one (optionally taking a clock or `{ clock }`). When a file has
60
+ * factories, plain actor exports are skipped: they are usually one of those
61
+ * factories' results, and cannot be fuzzed.
62
+ */
63
+ declare function inspectModule(file: string, options: ModuleOptions): Promise<InspectedModule>;
64
+ /** Machines as a document, ready for `serializeViz`. */
65
+ declare function vizDocument(machines: ReadonlyArray<VizMachine>): VizDocument;
66
+ export { ModuleOptions as a, machineId as c, InspectedModule as i, vizDocument as l, InspectTarget as n, inspect as o, Inspected as r, inspectModule as s, FuzzSettings as t, AnalysisSource as u };