machina-inspect 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,589 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let machina = require("machina");
30
+ let acorn = require("acorn");
31
+ acorn = __toESM(acorn);
32
+
33
+ //#region src/handler-analyzer.ts
34
+ const CONDITIONAL_ANCESTORS = new Set([
35
+ "IfStatement",
36
+ "SwitchStatement",
37
+ "SwitchCase",
38
+ "ConditionalExpression",
39
+ "LogicalExpression",
40
+ "TryStatement"
41
+ ]);
42
+ const PARSE_OPTS = {
43
+ ecmaVersion: 2022,
44
+ sourceType: "script"
45
+ };
46
+ /**
47
+ * Try to parse a function's toString() output using several strategies.
48
+ *
49
+ * Function.prototype.toString() produces different formats:
50
+ * - Named function declaration: `function name() { ... }` → parses as-is
51
+ * - Anonymous function expression: `function () { ... }` → needs parens wrapper
52
+ * - Arrow function: `() => { ... }` → parses as-is
53
+ * - Method shorthand: `name() { ... }` → needs object literal wrapper
54
+ *
55
+ * Returns null if none of the strategies succeed (unusual syntax, etc.).
56
+ */
57
+ function tryParse(source) {
58
+ try {
59
+ return acorn.parse(source, PARSE_OPTS);
60
+ } catch {}
61
+ try {
62
+ return acorn.parse(`(${source})`, PARSE_OPTS);
63
+ } catch {}
64
+ try {
65
+ return acorn.parse(`({${source}})`, PARSE_OPTS);
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ /**
71
+ * Find the first ArrowFunctionExpression node in a parsed AST.
72
+ * Returns undefined if no arrow function exists at the top level.
73
+ */
74
+ function findArrowFunction(node) {
75
+ if (node.type === "ArrowFunctionExpression") return node;
76
+ if (node.type === "Program" || node.type === "ExpressionStatement") {
77
+ const children = getChildren(node);
78
+ for (const child of children) {
79
+ const found = findArrowFunction(child);
80
+ if (found) return found;
81
+ }
82
+ }
83
+ }
84
+ /**
85
+ * Handle concise arrow functions (`() => "state"` with no block body).
86
+ *
87
+ * Acorn represents these with `expression: true` on the ArrowFunctionExpression
88
+ * and the body as a Literal directly — there is no ReturnStatement, so the
89
+ * general walkForReturns() misses them entirely.
90
+ *
91
+ * Returns:
92
+ * - HandlerTarget[] with one definite entry if the body is a string Literal
93
+ * - [] if the body is a non-string expression (no false edges produced)
94
+ * - undefined if the AST doesn't match the concise arrow pattern at all
95
+ * (caller should fall through to the general walker)
96
+ */
97
+ function checkConciseArrow(node) {
98
+ if (node.type !== "ArrowFunctionExpression" || !node.expression) return;
99
+ const body = node.body;
100
+ if (body && body.type === "Literal" && typeof body.value === "string") return [{
101
+ target: body.value,
102
+ confidence: "definite"
103
+ }];
104
+ return [];
105
+ }
106
+ /**
107
+ * Handle concise arrow functions found inside a parsed Program/ExpressionStatement.
108
+ * Used by analyzeHandler after tryParse() wraps the source in a Program node.
109
+ */
110
+ function checkConciseArrowInAst(ast) {
111
+ const arrowNode = findArrowFunction(ast);
112
+ if (!arrowNode || !arrowNode.expression) return;
113
+ const body = arrowNode.body;
114
+ if (body && body.type === "Literal" && typeof body.value === "string") return [{
115
+ target: body.value,
116
+ confidence: "definite"
117
+ }];
118
+ return [];
119
+ }
120
+ /**
121
+ * Walk a pre-parsed handler function AST node and extract transition targets.
122
+ *
123
+ * Accepts a FunctionDeclaration, FunctionExpression, or ArrowFunctionExpression
124
+ * node (as produced by acorn or the ESLint rule engine). Returns an array of
125
+ * { target, confidence } pairs. Empty array means no statically-determinable
126
+ * string return targets were found.
127
+ *
128
+ * This is the shared core used by both:
129
+ * - analyzeHandler() — the runtime path (parses fn.toString() first)
130
+ * - ESLint rules — the AST path (ESLint already has the parsed node)
131
+ */
132
+ function walkHandlerAst(node) {
133
+ const conciseResult = checkConciseArrow(node);
134
+ if (conciseResult !== void 0) return conciseResult;
135
+ const returns = [];
136
+ walkForReturns(node, [], returns);
137
+ if (returns.length === 0) return [];
138
+ const allNonConditional = returns.every((r) => !r.isConditional);
139
+ const isSoleReturn = returns.length === 1;
140
+ return returns.map(({ target, isConditional }) => ({
141
+ target,
142
+ confidence: allNonConditional && isSoleReturn && !isConditional ? "definite" : "possible"
143
+ }));
144
+ }
145
+ /**
146
+ * Analyze a handler function and extract transition targets.
147
+ *
148
+ * Returns an array of { target, confidence } pairs. Empty array means
149
+ * no statically-determinable string return targets were found.
150
+ *
151
+ * Safe to call on any function — parse errors return empty (fail silently).
152
+ */
153
+ function analyzeHandler(fn) {
154
+ const source = fn.toString();
155
+ if (source.includes("[native code]")) return [];
156
+ const ast = tryParse(source);
157
+ if (!ast) return [];
158
+ const conciseResult = checkConciseArrowInAst(ast);
159
+ if (conciseResult !== void 0) return conciseResult;
160
+ const fnNode = findTopLevelFunctionNode(ast);
161
+ if (fnNode) return walkHandlerAst(fnNode);
162
+ const returns = [];
163
+ walkForReturns(ast, [], returns);
164
+ if (returns.length === 0) return [];
165
+ const allNonConditional = returns.every((r) => !r.isConditional);
166
+ const isSoleReturn = returns.length === 1;
167
+ return returns.map(({ target, isConditional }) => ({
168
+ target,
169
+ confidence: allNonConditional && isSoleReturn && !isConditional ? "definite" : "possible"
170
+ }));
171
+ }
172
+ /**
173
+ * Find the top-level function node in a parsed Program AST.
174
+ * Returns the first FunctionDeclaration, FunctionExpression, or
175
+ * ArrowFunctionExpression at the top level.
176
+ */
177
+ function findTopLevelFunctionNode(ast) {
178
+ const children = getChildren(ast);
179
+ for (const child of children) {
180
+ if (child.type === "FunctionDeclaration" || child.type === "FunctionExpression" || child.type === "ArrowFunctionExpression") return child;
181
+ if (child.type === "ExpressionStatement") {
182
+ const grandChildren = getChildren(child);
183
+ for (const gc of grandChildren) if (gc.type === "FunctionDeclaration" || gc.type === "FunctionExpression" || gc.type === "ArrowFunctionExpression") return gc;
184
+ }
185
+ }
186
+ }
187
+ /**
188
+ * Collect string literals from a conditional expression tree.
189
+ * Used when a ReturnStatement's argument is a ternary or logical expression.
190
+ * All findings are marked as "possible" since they're conditional.
191
+ */
192
+ function collectStringLiteralsFromExpression(node, results) {
193
+ if (!node || typeof node !== "object") return;
194
+ if (node.type === "Literal" && typeof node.value === "string") {
195
+ results.push({
196
+ target: node.value,
197
+ isConditional: true
198
+ });
199
+ return;
200
+ }
201
+ if (node.type === "ConditionalExpression") {
202
+ collectStringLiteralsFromExpression(node.consequent, results);
203
+ collectStringLiteralsFromExpression(node.alternate, results);
204
+ } else if (node.type === "LogicalExpression") {
205
+ collectStringLiteralsFromExpression(node.left, results);
206
+ collectStringLiteralsFromExpression(node.right, results);
207
+ }
208
+ }
209
+ /**
210
+ * Recursively walk the AST, collecting ReturnStatement nodes that
211
+ * return string literals. Tracks whether any CONDITIONAL_ANCESTOR
212
+ * node exists in the ancestor chain.
213
+ *
214
+ * We skip nested function bodies (arrow functions, regular functions,
215
+ * class methods) — their returns belong to a different function scope.
216
+ */
217
+ function walkForReturns(node, ancestors, results) {
218
+ if (!node || typeof node !== "object") return;
219
+ if (node.type === "ReturnStatement") {
220
+ const arg = node.argument;
221
+ if (!arg) return;
222
+ if (arg.type === "Literal" && typeof arg.value === "string") {
223
+ const isConditional = ancestors.some((a) => CONDITIONAL_ANCESTORS.has(a));
224
+ results.push({
225
+ target: arg.value,
226
+ isConditional
227
+ });
228
+ } else collectStringLiteralsFromExpression(arg, results);
229
+ return;
230
+ }
231
+ if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
232
+ if (ancestors.some((a) => a === "FunctionDeclaration" || a === "FunctionExpression" || a === "ArrowFunctionExpression")) return;
233
+ }
234
+ const nextAncestors = [...ancestors, node.type];
235
+ const children = getChildren(node);
236
+ for (const child of children) if (child && typeof child === "object") walkForReturns(child, nextAncestors, results);
237
+ }
238
+ /**
239
+ * Extract the child nodes we want to recurse into from an AST node.
240
+ * Returns a flat list of child nodes (some may be arrays in the AST).
241
+ */
242
+ function getChildren(node) {
243
+ const children = [];
244
+ const pushIfNode = (val) => {
245
+ if (val && typeof val === "object" && "type" in val) children.push(val);
246
+ };
247
+ const pushArray = (arr) => {
248
+ if (Array.isArray(arr)) for (const item of arr) pushIfNode(item);
249
+ };
250
+ switch (node.type) {
251
+ case "Program":
252
+ pushArray(node.body);
253
+ break;
254
+ case "ExpressionStatement":
255
+ pushIfNode(node.expression);
256
+ break;
257
+ case "FunctionDeclaration":
258
+ case "FunctionExpression":
259
+ case "ArrowFunctionExpression":
260
+ if (node.body) pushIfNode(node.body);
261
+ break;
262
+ case "BlockStatement":
263
+ pushArray(node.body);
264
+ break;
265
+ case "ReturnStatement":
266
+ pushIfNode(node.argument);
267
+ break;
268
+ case "IfStatement":
269
+ pushIfNode(node.consequent);
270
+ pushIfNode(node.alternate);
271
+ break;
272
+ case "SwitchStatement":
273
+ pushArray(node.cases);
274
+ break;
275
+ case "SwitchCase":
276
+ pushArray(node.consequent);
277
+ break;
278
+ case "TryStatement":
279
+ pushIfNode(node.block);
280
+ pushIfNode(node.handler);
281
+ pushIfNode(node.finalizer);
282
+ break;
283
+ case "CatchClause":
284
+ pushIfNode(node.body);
285
+ break;
286
+ case "ConditionalExpression":
287
+ pushIfNode(node.consequent);
288
+ pushIfNode(node.alternate);
289
+ break;
290
+ case "LogicalExpression":
291
+ pushIfNode(node.left);
292
+ pushIfNode(node.right);
293
+ break;
294
+ case "VariableDeclaration":
295
+ pushArray(node.declarations);
296
+ break;
297
+ case "VariableDeclarator":
298
+ pushIfNode(node.init);
299
+ break;
300
+ case "ForStatement":
301
+ pushIfNode(node.body);
302
+ break;
303
+ case "WhileStatement":
304
+ case "DoWhileStatement":
305
+ pushIfNode(node.body);
306
+ break;
307
+ default: for (const key of Object.keys(node)) {
308
+ if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "parent" || key === "range") continue;
309
+ const val = node[key];
310
+ if (Array.isArray(val)) pushArray(val);
311
+ else pushIfNode(val);
312
+ }
313
+ }
314
+ return children;
315
+ }
316
+
317
+ //#endregion
318
+ //#region src/graph-builder.ts
319
+ const SPECIAL_KEYS = new Set([
320
+ "_onEnter",
321
+ "_onExit",
322
+ "_child"
323
+ ]);
324
+ /**
325
+ * Normalize the input to a consistent internal shape.
326
+ * Config objects are passed through; live instances have their
327
+ * public fields extracted.
328
+ */
329
+ function normalizeInput(input) {
330
+ if (machina.MACHINA_TYPE in input) {
331
+ const instance = input;
332
+ return {
333
+ id: instance.id,
334
+ initialState: instance.initialState,
335
+ states: instance.states
336
+ };
337
+ }
338
+ return input;
339
+ }
340
+ /**
341
+ * Extract transition edges from a single handler value.
342
+ * String shorthands produce one definite edge. Function handlers
343
+ * are analyzed by the acorn-based handler-analyzer.
344
+ */
345
+ function extractEdges(fromState, inputName, handlerValue) {
346
+ if (typeof handlerValue === "string") return [{
347
+ inputName,
348
+ from: fromState,
349
+ to: handlerValue,
350
+ confidence: "definite"
351
+ }];
352
+ if (typeof handlerValue === "function") return analyzeHandler(handlerValue).map(({ target, confidence }) => ({
353
+ inputName,
354
+ from: fromState,
355
+ to: target,
356
+ confidence
357
+ }));
358
+ return [];
359
+ }
360
+ /**
361
+ * Detect whether a _child value is a ChildLink wrapper (post-construction)
362
+ * vs a raw FSM instance (pre-construction config path).
363
+ *
364
+ * ChildLink objects have a `canHandle` method and an `instance` field.
365
+ * Raw FSM instances have MACHINA_TYPE stamped on them.
366
+ */
367
+ function isChildLink(value) {
368
+ return typeof value === "object" && value !== null && "canHandle" in value && "instance" in value;
369
+ }
370
+ /**
371
+ * Detect a plain config object that looks like an FSM config (has `initialState`
372
+ * and `states`). This covers eval'd user input in the explorer, where _child is
373
+ * a bare object literal rather than a createFsm() instance.
374
+ */
375
+ function isConfigShaped(value) {
376
+ return typeof value === "object" && value !== null && "initialState" in value && "states" in value;
377
+ }
378
+ /**
379
+ * Build a StateGraph from an FSM config or live instance.
380
+ * Child FSMs declared via _child are recursively built into child graphs.
381
+ *
382
+ * @param input - A config object or a live Fsm/BehavioralFsm instance
383
+ */
384
+ function buildStateGraph(input) {
385
+ const fsm = normalizeInput(input);
386
+ const nodes = {};
387
+ const children = {};
388
+ for (const stateName of Object.keys(fsm.states)) {
389
+ const stateObj = fsm.states[stateName];
390
+ const edges = [];
391
+ for (const key of Object.keys(stateObj)) {
392
+ const value = stateObj[key];
393
+ if (key === "_child") {
394
+ if (isChildLink(value)) children[stateName] = buildStateGraph(value.instance);
395
+ else if (typeof value === "object" && value !== null && machina.MACHINA_TYPE in value) children[stateName] = buildStateGraph(value);
396
+ else if (isConfigShaped(value)) children[stateName] = buildStateGraph(value);
397
+ continue;
398
+ }
399
+ if (SPECIAL_KEYS.has(key)) {
400
+ if (key === "_onEnter" && typeof value === "function") {
401
+ const targets = analyzeHandler(value);
402
+ for (const { target, confidence } of targets) edges.push({
403
+ inputName: "_onEnter",
404
+ from: stateName,
405
+ to: target,
406
+ confidence
407
+ });
408
+ }
409
+ continue;
410
+ }
411
+ for (const edge of extractEdges(stateName, key, value)) edges.push(edge);
412
+ }
413
+ nodes[stateName] = {
414
+ name: stateName,
415
+ edges
416
+ };
417
+ }
418
+ return {
419
+ fsmId: fsm.id,
420
+ initialState: fsm.initialState,
421
+ nodes,
422
+ children
423
+ };
424
+ }
425
+
426
+ //#endregion
427
+ //#region src/checks/unreachable.ts
428
+ /**
429
+ * Check for unreachable states in the graph.
430
+ * BFS from initialState — any unvisited node is a finding.
431
+ * Recurses into child graphs independently.
432
+ */
433
+ function checkUnreachable(graph, parentState) {
434
+ const findings = [];
435
+ findings.push(...findUnreachableInGraph(graph, parentState));
436
+ for (const [childParentState, childGraph] of Object.entries(graph.children)) findings.push(...checkUnreachable(childGraph, childParentState));
437
+ return findings;
438
+ }
439
+ function findUnreachableInGraph(graph, parentState) {
440
+ const { fsmId, initialState, nodes } = graph;
441
+ const visited = /* @__PURE__ */ new Set();
442
+ const queue = [];
443
+ if (initialState in nodes) {
444
+ queue.push(initialState);
445
+ visited.add(initialState);
446
+ }
447
+ while (queue.length > 0) {
448
+ const node = nodes[queue.shift()];
449
+ if (!node) continue;
450
+ for (const edge of node.edges) if (!visited.has(edge.to) && edge.to in nodes) {
451
+ visited.add(edge.to);
452
+ queue.push(edge.to);
453
+ }
454
+ }
455
+ const findings = [];
456
+ for (const stateName of Object.keys(nodes)) if (!visited.has(stateName)) findings.push({
457
+ type: "unreachable-state",
458
+ message: `State "${stateName}" is unreachable from initialState "${initialState}" in FSM "${fsmId}".`,
459
+ fsmId,
460
+ states: [stateName],
461
+ parentState
462
+ });
463
+ return findings;
464
+ }
465
+
466
+ //#endregion
467
+ //#region src/checks/onenter-loop.ts
468
+ /**
469
+ * Check for _onEnter transition loops in the graph.
470
+ * Only reports cycles where every edge is "definite".
471
+ * Recurses into child graphs.
472
+ */
473
+ function checkOnEnterLoops(graph, parentState) {
474
+ const findings = [];
475
+ findings.push(...findOnEnterLoopsInGraph(graph, parentState));
476
+ for (const [childParentState, childGraph] of Object.entries(graph.children)) findings.push(...checkOnEnterLoops(childGraph, childParentState));
477
+ return findings;
478
+ }
479
+ function findOnEnterLoopsInGraph(graph, parentState) {
480
+ const { fsmId, nodes } = graph;
481
+ const onEnterEdges = {};
482
+ for (const [stateName, node] of Object.entries(nodes)) {
483
+ const definiteOnEnterTargets = node.edges.filter((e) => e.inputName === "_onEnter" && e.confidence === "definite" && e.to !== stateName).map((e) => e.to);
484
+ if (definiteOnEnterTargets.length > 0) onEnterEdges[stateName] = definiteOnEnterTargets;
485
+ }
486
+ const WHITE = 0, GRAY = 1, BLACK = 2;
487
+ const color = {};
488
+ for (const stateName of Object.keys(nodes)) color[stateName] = WHITE;
489
+ const findings = [];
490
+ const reportedCycles = /* @__PURE__ */ new Set();
491
+ const dfs = (node, stack) => {
492
+ color[node] = GRAY;
493
+ stack.push(node);
494
+ for (const neighbor of onEnterEdges[node] ?? []) if (color[neighbor] === GRAY) {
495
+ const cycleStart = stack.indexOf(neighbor);
496
+ const cycle = stack.slice(cycleStart);
497
+ const cycleKey = [...cycle].sort().join(",");
498
+ if (!reportedCycles.has(cycleKey)) {
499
+ reportedCycles.add(cycleKey);
500
+ findings.push({
501
+ type: "onenter-loop",
502
+ message: `Unconditional _onEnter loop detected in FSM "${fsmId}": ` + cycle.join(" → ") + ` → ${neighbor}`,
503
+ fsmId,
504
+ states: cycle,
505
+ parentState
506
+ });
507
+ }
508
+ } else if (color[neighbor] === WHITE) dfs(neighbor, stack);
509
+ stack.pop();
510
+ color[node] = BLACK;
511
+ };
512
+ for (const stateName of Object.keys(nodes)) if (color[stateName] === WHITE) dfs(stateName, []);
513
+ return findings;
514
+ }
515
+
516
+ //#endregion
517
+ //#region src/checks/missing-handler.ts
518
+ const EXCLUDED_INPUTS = new Set([
519
+ "_onEnter",
520
+ "_onExit",
521
+ "*"
522
+ ]);
523
+ /**
524
+ * Check for states with missing handlers relative to the FSM's full input set.
525
+ * States with a `*` catch-all edge are skipped entirely.
526
+ * Recurses into child graphs independently.
527
+ */
528
+ function checkMissingHandlers(graph, parentState) {
529
+ const findings = [];
530
+ findings.push(...findMissingHandlersInGraph(graph, parentState));
531
+ for (const [childParentState, childGraph] of Object.entries(graph.children)) findings.push(...checkMissingHandlers(childGraph, childParentState));
532
+ return findings;
533
+ }
534
+ function findMissingHandlersInGraph(graph, parentState) {
535
+ const { fsmId, nodes } = graph;
536
+ const allInputs = /* @__PURE__ */ new Set();
537
+ for (const node of Object.values(nodes)) for (const edge of node.edges) if (!EXCLUDED_INPUTS.has(edge.inputName)) allInputs.add(edge.inputName);
538
+ if (allInputs.size === 0) return [];
539
+ const findings = [];
540
+ for (const [stateName, node] of Object.entries(nodes)) {
541
+ if (node.edges.some((e) => e.inputName === "*")) continue;
542
+ const stateInputs = /* @__PURE__ */ new Set();
543
+ for (const edge of node.edges) if (!EXCLUDED_INPUTS.has(edge.inputName)) stateInputs.add(edge.inputName);
544
+ const missingInputs = [...allInputs].filter((input) => !stateInputs.has(input));
545
+ if (missingInputs.length > 0) findings.push({
546
+ type: "missing-handler",
547
+ message: `State "${stateName}" in FSM "${fsmId}" is missing handlers for: ` + missingInputs.map((i) => `"${i}"`).join(", ") + ".",
548
+ fsmId,
549
+ states: [stateName],
550
+ inputs: missingInputs,
551
+ parentState
552
+ });
553
+ }
554
+ return findings;
555
+ }
556
+
557
+ //#endregion
558
+ //#region src/inspect.ts
559
+ /**
560
+ * Build a state graph from the input and run all structural checks.
561
+ * Returns an array of findings — empty means no issues detected.
562
+ *
563
+ * @param input - A config object or a live Fsm/BehavioralFsm instance
564
+ */
565
+ function inspect(input) {
566
+ return inspectGraph(buildStateGraph(input));
567
+ }
568
+ /**
569
+ * Run all structural checks against a pre-built state graph.
570
+ * Useful when you've already called buildStateGraph() for another purpose
571
+ * (e.g., diagram export) and want to avoid building it twice.
572
+ *
573
+ * @param graph - A StateGraph produced by buildStateGraph()
574
+ */
575
+ function inspectGraph(graph) {
576
+ return [
577
+ ...checkUnreachable(graph),
578
+ ...checkOnEnterLoops(graph),
579
+ ...checkMissingHandlers(graph)
580
+ ];
581
+ }
582
+
583
+ //#endregion
584
+ exports.analyzeHandler = analyzeHandler;
585
+ exports.buildStateGraph = buildStateGraph;
586
+ exports.inspect = inspect;
587
+ exports.inspectGraph = inspectGraph;
588
+ exports.walkHandlerAst = walkHandlerAst;
589
+ //# sourceMappingURL=index.cjs.map