{"version":3,"file":"index.cjs","names":["MACHINA_TYPE"],"sources":["../src/handler-analyzer.ts","../src/graph-builder.ts","../src/checks/unreachable.ts","../src/checks/onenter-loop.ts","../src/checks/missing-handler.ts","../src/inspect.ts"],"sourcesContent":["// =============================================================================\n// handler-analyzer.ts — Acorn-based static analysis of function handlers\n//\n// Parses handler.toString() with acorn, walks the AST for ReturnStatement\n// nodes that return string literals, and determines confidence based on\n// whether the return is nested in a conditional or not.\n//\n// This is best-effort: we catch explicit `return \"stateName\"` patterns.\n// Dynamic returns (variables, template literals, computed expressions) are\n// silently skipped — we produce no false edges, just miss some.\n// =============================================================================\n\nimport * as acorn from \"acorn\";\n\nexport interface HandlerTarget {\n    target: string;\n    confidence: \"definite\" | \"possible\";\n}\n\n// acorn nodes have a `type` string field plus arbitrary children.\n// We use a loose type to keep the AST walking code simple.\n// Declared early so AstFunctionNode can reference it.\ntype AcornNode = {\n    type: string;\n    body?: AcornNode | AcornNode[];\n    argument?: AcornNode | null;\n    value?: unknown;\n    consequent?: AcornNode | AcornNode[];\n    alternate?: AcornNode | null;\n    cases?: AcornNode[];\n    block?: AcornNode;\n    handler?: AcornNode | null;\n    finalizer?: AcornNode | null;\n    left?: AcornNode;\n    right?: AcornNode;\n    expression?: AcornNode | boolean;\n    declarations?: AcornNode[];\n    init?: AcornNode | null;\n    test?: AcornNode | null;\n    update?: AcornNode | null;\n    params?: AcornNode[];\n    [key: string]: unknown;\n};\n\n/**\n * A union of ESTree node types that represent a callable handler function.\n * Structurally compatible with both acorn nodes and ESLint's @types/estree,\n * so walkHandlerAst can accept either without type juggling at the call site.\n *\n * The index signature `[key: string]: unknown` keeps this loose enough that\n * acorn's extra fields (start, end, loc) don't cause structural mismatches.\n */\nexport type AstFunctionNode = {\n    type: \"FunctionDeclaration\" | \"FunctionExpression\" | \"ArrowFunctionExpression\";\n    body: AcornNode;\n    expression?: boolean; // true for concise arrow functions (no block body)\n    params: AcornNode[];\n    [key: string]: unknown;\n};\n\n// Node types that make a return \"conditional\" — any string-returning\n// ReturnStatement nested inside one of these becomes \"possible\".\nconst CONDITIONAL_ANCESTORS = new Set([\n    \"IfStatement\",\n    \"SwitchStatement\",\n    \"SwitchCase\",\n    \"ConditionalExpression\",\n    \"LogicalExpression\",\n    \"TryStatement\",\n]);\n\ninterface ReturnInfo {\n    target: string;\n    isConditional: boolean;\n}\n\nconst PARSE_OPTS: acorn.Options = { ecmaVersion: 2022, sourceType: \"script\" };\n\n/**\n * Try to parse a function's toString() output using several strategies.\n *\n * Function.prototype.toString() produces different formats:\n *   - Named function declaration: `function name() { ... }` → parses as-is\n *   - Anonymous function expression: `function () { ... }` → needs parens wrapper\n *   - Arrow function: `() => { ... }` → parses as-is\n *   - Method shorthand: `name() { ... }` → needs object literal wrapper\n *\n * Returns null if none of the strategies succeed (unusual syntax, etc.).\n */\nfunction tryParse(source: string): acorn.Node | null {\n    // Strategy 1: parse as-is (named function declarations, arrow functions)\n    try {\n        return acorn.parse(source, PARSE_OPTS);\n    } catch {\n        // continue to next strategy\n    }\n\n    // Strategy 2: wrap in parens (anonymous function expressions)\n    try {\n        return acorn.parse(`(${source})`, PARSE_OPTS);\n    } catch {\n        // continue to next strategy\n    }\n\n    // Strategy 3: wrap as object method (method shorthand `name() { ... }`)\n    try {\n        return acorn.parse(`({${source}})`, PARSE_OPTS);\n    } catch {\n        // All strategies exhausted — fail silently\n        return null;\n    }\n}\n\n/**\n * Find the first ArrowFunctionExpression node in a parsed AST.\n * Returns undefined if no arrow function exists at the top level.\n */\nfunction findArrowFunction(node: AcornNode): AcornNode | undefined {\n    if (node.type === \"ArrowFunctionExpression\") {\n        return node;\n    }\n    if (node.type === \"Program\" || node.type === \"ExpressionStatement\") {\n        const children = getChildren(node);\n        for (const child of children) {\n            const found = findArrowFunction(child);\n            if (found) {\n                return found;\n            }\n        }\n    }\n    return undefined;\n}\n\n/**\n * Handle concise arrow functions (`() => \"state\"` with no block body).\n *\n * Acorn represents these with `expression: true` on the ArrowFunctionExpression\n * and the body as a Literal directly — there is no ReturnStatement, so the\n * general walkForReturns() misses them entirely.\n *\n * Returns:\n *   - HandlerTarget[] with one definite entry if the body is a string Literal\n *   - [] if the body is a non-string expression (no false edges produced)\n *   - undefined if the AST doesn't match the concise arrow pattern at all\n *     (caller should fall through to the general walker)\n */\nfunction checkConciseArrow(node: AstFunctionNode): HandlerTarget[] | undefined {\n    // A concise arrow has `expression: true` and the body is not a BlockStatement.\n    if (node.type !== \"ArrowFunctionExpression\" || !node.expression) {\n        // Not a concise arrow — let checkConciseArrow's caller also check\n        // the parsed Program wrapper for arrow functions embedded in Program nodes.\n        return undefined;\n    }\n    const body = node.body as AcornNode;\n    if (body && body.type === \"Literal\" && typeof body.value === \"string\") {\n        return [{ target: body.value, confidence: \"definite\" }];\n    }\n    // Non-string concise arrow (e.g., `() => someVar`) — no targets extractable\n    return [];\n}\n\n/**\n * Handle concise arrow functions found inside a parsed Program/ExpressionStatement.\n * Used by analyzeHandler after tryParse() wraps the source in a Program node.\n */\nfunction checkConciseArrowInAst(ast: AcornNode): HandlerTarget[] | undefined {\n    const arrowNode = findArrowFunction(ast);\n    if (!arrowNode || !arrowNode.expression) {\n        return undefined;\n    }\n    const body = arrowNode.body as AcornNode;\n    if (body && body.type === \"Literal\" && typeof body.value === \"string\") {\n        return [{ target: body.value, confidence: \"definite\" }];\n    }\n    return [];\n}\n\n/**\n * Walk a pre-parsed handler function AST node and extract transition targets.\n *\n * Accepts a FunctionDeclaration, FunctionExpression, or ArrowFunctionExpression\n * node (as produced by acorn or the ESLint rule engine). Returns an array of\n * { target, confidence } pairs. Empty array means no statically-determinable\n * string return targets were found.\n *\n * This is the shared core used by both:\n *   - analyzeHandler() — the runtime path (parses fn.toString() first)\n *   - ESLint rules — the AST path (ESLint already has the parsed node)\n */\nexport function walkHandlerAst(node: AstFunctionNode): HandlerTarget[] {\n    // Concise arrow functions (`() => \"state\"`) have no ReturnStatement.\n    // The body is the expression directly — handle before the general walker.\n    const conciseResult = checkConciseArrow(node);\n    if (conciseResult !== undefined) {\n        return conciseResult;\n    }\n\n    const returns: ReturnInfo[] = [];\n    walkForReturns(node as unknown as AcornNode, [], returns);\n\n    if (returns.length === 0) {\n        return [];\n    }\n\n    // Determine confidence:\n    // - A single non-conditional return that's the ONLY string return → \"definite\"\n    // - Multiple returns or conditional returns → all \"possible\"\n    const allNonConditional = returns.every(r => !r.isConditional);\n    const isSoleReturn = returns.length === 1;\n\n    return returns.map(({ target, isConditional }) => ({\n        target,\n        confidence: allNonConditional && isSoleReturn && !isConditional ? \"definite\" : \"possible\",\n    }));\n}\n\n/**\n * Analyze a handler function and extract transition targets.\n *\n * Returns an array of { target, confidence } pairs. Empty array means\n * no statically-determinable string return targets were found.\n *\n * Safe to call on any function — parse errors return empty (fail silently).\n */\nexport function analyzeHandler(fn: (...args: unknown[]) => unknown): HandlerTarget[] {\n    const source = fn.toString();\n\n    // Native/bound functions have no analyzable body — skip them.\n    if (source.includes(\"[native code]\")) {\n        return [];\n    }\n\n    const ast = tryParse(source);\n    if (!ast) {\n        return [];\n    }\n\n    // Concise arrow functions (`() => \"state\"`) have no ReturnStatement in their\n    // AST. Instead, the ArrowFunctionExpression node has `expression: true` and\n    // the body is a Literal directly. Handle this before the general walker.\n    const conciseResult = checkConciseArrowInAst(ast as unknown as AcornNode);\n    if (conciseResult !== undefined) {\n        return conciseResult;\n    }\n\n    // For block-body functions, find the top-level function node and delegate\n    // to the shared AST walker.\n    const fnNode = findTopLevelFunctionNode(ast as unknown as AcornNode);\n    if (fnNode) {\n        return walkHandlerAst(fnNode as unknown as AstFunctionNode);\n    }\n\n    // Fallback: walk the raw AST directly (method shorthand wrapper case)\n    const returns: ReturnInfo[] = [];\n    walkForReturns(ast as unknown as AcornNode, [], returns);\n\n    if (returns.length === 0) {\n        return [];\n    }\n\n    const allNonConditional = returns.every(r => !r.isConditional);\n    const isSoleReturn = returns.length === 1;\n\n    return returns.map(({ target, isConditional }) => ({\n        target,\n        confidence: allNonConditional && isSoleReturn && !isConditional ? \"definite\" : \"possible\",\n    }));\n}\n\n/**\n * Find the top-level function node in a parsed Program AST.\n * Returns the first FunctionDeclaration, FunctionExpression, or\n * ArrowFunctionExpression at the top level.\n */\nfunction findTopLevelFunctionNode(ast: AcornNode): AcornNode | undefined {\n    const children = getChildren(ast);\n    for (const child of children) {\n        if (\n            child.type === \"FunctionDeclaration\" ||\n            child.type === \"FunctionExpression\" ||\n            child.type === \"ArrowFunctionExpression\"\n        ) {\n            return child;\n        }\n        // ExpressionStatement wrapper (anonymous function expressions wrapped in parens)\n        if (child.type === \"ExpressionStatement\") {\n            const grandChildren = getChildren(child);\n            for (const gc of grandChildren) {\n                if (\n                    gc.type === \"FunctionDeclaration\" ||\n                    gc.type === \"FunctionExpression\" ||\n                    gc.type === \"ArrowFunctionExpression\"\n                ) {\n                    return gc;\n                }\n            }\n        }\n    }\n    return undefined;\n}\n\n/**\n * Collect string literals from a conditional expression tree.\n * Used when a ReturnStatement's argument is a ternary or logical expression.\n * All findings are marked as \"possible\" since they're conditional.\n */\nfunction collectStringLiteralsFromExpression(node: AcornNode, results: ReturnInfo[]): void {\n    if (!node || typeof node !== \"object\") {\n        return;\n    }\n    if (node.type === \"Literal\" && typeof node.value === \"string\") {\n        results.push({ target: node.value as string, isConditional: true });\n        return;\n    }\n    // Recurse into conditional/logical branches\n    if (node.type === \"ConditionalExpression\") {\n        collectStringLiteralsFromExpression(node.consequent as AcornNode, results);\n        collectStringLiteralsFromExpression(node.alternate as AcornNode, results);\n    } else if (node.type === \"LogicalExpression\") {\n        collectStringLiteralsFromExpression(node.left as AcornNode, results);\n        collectStringLiteralsFromExpression(node.right as AcornNode, results);\n    }\n    // For any other expression type, don't recurse further — we can't reliably\n    // determine whether the result will be a state name.\n}\n\n/**\n * Recursively walk the AST, collecting ReturnStatement nodes that\n * return string literals. Tracks whether any CONDITIONAL_ANCESTOR\n * node exists in the ancestor chain.\n *\n * We skip nested function bodies (arrow functions, regular functions,\n * class methods) — their returns belong to a different function scope.\n */\nfunction walkForReturns(node: AcornNode, ancestors: string[], results: ReturnInfo[]): void {\n    if (!node || typeof node !== \"object\") {\n        return;\n    }\n\n    if (node.type === \"ReturnStatement\") {\n        const arg = node.argument;\n        if (!arg) {\n            return;\n        }\n        if (arg.type === \"Literal\" && typeof arg.value === \"string\") {\n            const isConditional = ancestors.some(a => CONDITIONAL_ANCESTORS.has(a));\n            results.push({ target: arg.value, isConditional });\n        } else {\n            // Non-literal return value (ternary, logical, expression, etc.)\n            // Walk into the argument to find any string literals in conditional branches.\n            // All targets found this way are \"possible\" since they're behind some expression.\n            collectStringLiteralsFromExpression(arg, results);\n        }\n        return;\n    }\n\n    // Skip nested function scopes — their returns are not ours\n    if (\n        node.type === \"FunctionDeclaration\" ||\n        node.type === \"FunctionExpression\" ||\n        node.type === \"ArrowFunctionExpression\"\n    ) {\n        // The outermost function (index 0 in a Program body) IS our function,\n        // so we need to visit it. We detect \"outermost\" by checking if ancestors\n        // has any function-type ancestor already.\n        const hasFunctionAncestor = ancestors.some(\n            a =>\n                a === \"FunctionDeclaration\" ||\n                a === \"FunctionExpression\" ||\n                a === \"ArrowFunctionExpression\"\n        );\n        if (hasFunctionAncestor) {\n            // Nested function — skip its body entirely\n            return;\n        }\n        // Fall through to recurse into the outermost function body\n    }\n\n    const nextAncestors = [...ancestors, node.type];\n    const children = getChildren(node);\n    for (const child of children) {\n        if (child && typeof child === \"object\") {\n            walkForReturns(child as AcornNode, nextAncestors, results);\n        }\n    }\n}\n\n/**\n * Extract the child nodes we want to recurse into from an AST node.\n * Returns a flat list of child nodes (some may be arrays in the AST).\n */\nfunction getChildren(node: AcornNode): AcornNode[] {\n    const children: AcornNode[] = [];\n\n    const pushIfNode = (val: unknown) => {\n        if (val && typeof val === \"object\" && \"type\" in (val as object)) {\n            children.push(val as AcornNode);\n        }\n    };\n\n    const pushArray = (arr: unknown) => {\n        if (Array.isArray(arr)) {\n            for (const item of arr) {\n                pushIfNode(item);\n            }\n        }\n    };\n\n    switch (node.type) {\n        case \"Program\":\n            pushArray(node.body);\n            break;\n        case \"ExpressionStatement\":\n            pushIfNode(node.expression);\n            break;\n        case \"FunctionDeclaration\":\n        case \"FunctionExpression\":\n        case \"ArrowFunctionExpression\":\n            if (node.body) {\n                pushIfNode(node.body);\n            }\n            break;\n        case \"BlockStatement\":\n            pushArray(node.body);\n            break;\n        case \"ReturnStatement\":\n            pushIfNode(node.argument);\n            break;\n        case \"IfStatement\":\n            pushIfNode(node.consequent);\n            pushIfNode(node.alternate);\n            break;\n        case \"SwitchStatement\":\n            pushArray(node.cases);\n            break;\n        case \"SwitchCase\":\n            pushArray(node.consequent);\n            break;\n        case \"TryStatement\":\n            pushIfNode(node.block);\n            pushIfNode(node.handler);\n            pushIfNode(node.finalizer);\n            break;\n        case \"CatchClause\":\n            pushIfNode(node.body as AcornNode | undefined);\n            break;\n        case \"ConditionalExpression\":\n            pushIfNode(node.consequent as AcornNode | undefined);\n            pushIfNode(node.alternate as AcornNode | undefined);\n            break;\n        case \"LogicalExpression\":\n            pushIfNode(node.left);\n            pushIfNode(node.right);\n            break;\n        case \"VariableDeclaration\":\n            pushArray(node.declarations);\n            break;\n        case \"VariableDeclarator\":\n            pushIfNode(node.init);\n            break;\n        case \"ForStatement\":\n            pushIfNode(node.body as AcornNode | undefined);\n            break;\n        case \"WhileStatement\":\n        case \"DoWhileStatement\":\n            pushIfNode(node.body as AcornNode | undefined);\n            break;\n        default:\n            // For any node type we don't explicitly handle, try common child fields.\n            // This prevents us from silently missing returns in unusual constructs.\n            for (const key of Object.keys(node)) {\n                // ESLint AST nodes have circular `parent` pointers that Acorn nodes lack.\n                // Traversing into `parent` causes infinite recursion.\n                if (\n                    key === \"type\" ||\n                    key === \"start\" ||\n                    key === \"end\" ||\n                    key === \"loc\" ||\n                    key === \"parent\" ||\n                    key === \"range\"\n                ) {\n                    continue;\n                }\n                const val = node[key];\n                if (Array.isArray(val)) {\n                    pushArray(val);\n                } else {\n                    pushIfNode(val);\n                }\n            }\n    }\n\n    return children;\n}\n","// =============================================================================\n// graph-builder.ts — Build a StateGraph IR from an FSM config or live instance\n//\n// Two input shapes exist:\n//   1. Config object (pre-instantiation) — _child values are raw FSM instances\n//   2. Live instance (post-construction) — _child values are ChildLink wrappers\n//      (BehavioralFsm mutates config.states in-place during wrapChildLinks())\n//\n// The MACHINA_TYPE symbol discriminates between config and live instance inputs,\n// and between raw FSM instances and ChildLink wrappers on _child fields.\n// =============================================================================\n\nimport { MACHINA_TYPE, type ChildLink } from \"machina\";\nimport { analyzeHandler } from \"./handler-analyzer\";\nimport type { StateGraph, StateNode, TransitionEdge, InspectInput } from \"./types\";\n\n// Keys that are lifecycle/structural — not input handlers\nconst SPECIAL_KEYS = new Set([\"_onEnter\", \"_onExit\", \"_child\"]);\n\n// Shape of what we need from a live FSM instance\ninterface FsmLike {\n    id: string;\n    initialState: string;\n    states: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Normalize the input to a consistent internal shape.\n * Config objects are passed through; live instances have their\n * public fields extracted.\n */\nfunction normalizeInput(input: InspectInput): FsmLike {\n    // Live instance: has MACHINA_TYPE stamped on it\n    if (MACHINA_TYPE in (input as object)) {\n        const instance = input as unknown as FsmLike & { [key: symbol]: string };\n        return {\n            id: instance.id,\n            initialState: instance.initialState,\n            states: instance.states,\n        };\n    }\n    // Config object: read directly\n    return input as FsmLike;\n}\n\n/**\n * Extract transition edges from a single handler value.\n * String shorthands produce one definite edge. Function handlers\n * are analyzed by the acorn-based handler-analyzer.\n */\nfunction extractEdges(\n    fromState: string,\n    inputName: string,\n    handlerValue: unknown\n): TransitionEdge[] {\n    if (typeof handlerValue === \"string\") {\n        return [{ inputName, from: fromState, to: handlerValue, confidence: \"definite\" }];\n    }\n\n    if (typeof handlerValue === \"function\") {\n        const targets = analyzeHandler(handlerValue as (...args: unknown[]) => unknown);\n        return targets.map(({ target, confidence }) => ({\n            inputName,\n            from: fromState,\n            to: target,\n            confidence,\n        }));\n    }\n\n    return [];\n}\n\n/**\n * Detect whether a _child value is a ChildLink wrapper (post-construction)\n * vs a raw FSM instance (pre-construction config path).\n *\n * ChildLink objects have a `canHandle` method and an `instance` field.\n * Raw FSM instances have MACHINA_TYPE stamped on them.\n */\nfunction isChildLink(value: unknown): value is ChildLink {\n    return (\n        typeof value === \"object\" && value !== null && \"canHandle\" in value && \"instance\" in value\n    );\n}\n\n/**\n * Detect a plain config object that looks like an FSM config (has `initialState`\n * and `states`). This covers eval'd user input in the explorer, where _child is\n * a bare object literal rather than a createFsm() instance.\n */\nfunction isConfigShaped(value: unknown): value is FsmLike {\n    return (\n        typeof value === \"object\" && value !== null && \"initialState\" in value && \"states\" in value\n    );\n}\n\n/**\n * Build a StateGraph from an FSM config or live instance.\n * Child FSMs declared via _child are recursively built into child graphs.\n *\n * @param input - A config object or a live Fsm/BehavioralFsm instance\n */\nexport function buildStateGraph(input: InspectInput): StateGraph {\n    const fsm = normalizeInput(input);\n    const nodes: Record<string, StateNode> = {};\n    const children: Record<string, StateGraph> = {};\n\n    for (const stateName of Object.keys(fsm.states)) {\n        const stateObj = fsm.states[stateName];\n        const edges: TransitionEdge[] = [];\n\n        for (const key of Object.keys(stateObj)) {\n            const value = stateObj[key];\n\n            if (key === \"_child\") {\n                // _child values come in three shapes:\n                //   1. ChildLink wrapper (live instance post-construction)\n                //   2. Raw FSM instance with MACHINA_TYPE (config passed a createFsm() result)\n                //   3. Plain config object with initialState + states (e.g. from eval'd user input)\n                if (isChildLink(value)) {\n                    children[stateName] = buildStateGraph(\n                        value.instance as unknown as InspectInput\n                    );\n                } else if (typeof value === \"object\" && value !== null && MACHINA_TYPE in value) {\n                    children[stateName] = buildStateGraph(value as unknown as InspectInput);\n                } else if (isConfigShaped(value)) {\n                    children[stateName] = buildStateGraph(value as unknown as InspectInput);\n                }\n                continue;\n            }\n\n            if (SPECIAL_KEYS.has(key)) {\n                // _onEnter / _onExit: analyze function handlers for bounce edges,\n                // but label them with source \"_onEnter\" or \"_onExit\" so the\n                // loop detector can find them.\n                if (key === \"_onEnter\" && typeof value === \"function\") {\n                    const targets = analyzeHandler(value as (...args: unknown[]) => unknown);\n                    for (const { target, confidence } of targets) {\n                        edges.push({\n                            inputName: \"_onEnter\",\n                            from: stateName,\n                            to: target,\n                            confidence,\n                        });\n                    }\n                }\n                continue;\n            }\n\n            // Regular input handlers (including catch-all \"*\")\n            for (const edge of extractEdges(stateName, key, value)) {\n                edges.push(edge);\n            }\n        }\n\n        nodes[stateName] = { name: stateName, edges };\n    }\n\n    const graph: StateGraph = {\n        fsmId: fsm.id,\n        initialState: fsm.initialState,\n        nodes,\n        children,\n    };\n\n    return graph;\n}\n","// =============================================================================\n// unreachable.ts — Detect states with no inbound path from initialState\n//\n// BFS from initialState, following ALL edges (definite and possible).\n// Any node not reached is unreachable. Child graphs are analyzed independently\n// from their own initialState.\n// =============================================================================\n\nimport type { StateGraph, Finding } from \"../types\";\n\n/**\n * Check for unreachable states in the graph.\n * BFS from initialState — any unvisited node is a finding.\n * Recurses into child graphs independently.\n */\nexport function checkUnreachable(graph: StateGraph, parentState?: string): Finding[] {\n    const findings: Finding[] = [];\n\n    findings.push(...findUnreachableInGraph(graph, parentState));\n\n    for (const [childParentState, childGraph] of Object.entries(graph.children)) {\n        findings.push(...checkUnreachable(childGraph, childParentState));\n    }\n\n    return findings;\n}\n\nfunction findUnreachableInGraph(graph: StateGraph, parentState?: string): Finding[] {\n    const { fsmId, initialState, nodes } = graph;\n    const visited = new Set<string>();\n    const queue: string[] = [];\n\n    if (initialState in nodes) {\n        queue.push(initialState);\n        visited.add(initialState);\n    }\n\n    while (queue.length > 0) {\n        const current = queue.shift()!;\n        const node = nodes[current];\n        if (!node) {\n            continue;\n        }\n        for (const edge of node.edges) {\n            if (!visited.has(edge.to) && edge.to in nodes) {\n                visited.add(edge.to);\n                queue.push(edge.to);\n            }\n        }\n    }\n\n    const findings: Finding[] = [];\n    for (const stateName of Object.keys(nodes)) {\n        if (!visited.has(stateName)) {\n            findings.push({\n                type: \"unreachable-state\",\n                message: `State \"${stateName}\" is unreachable from initialState \"${initialState}\" in FSM \"${fsmId}\".`,\n                fsmId,\n                states: [stateName],\n                parentState,\n            });\n        }\n    }\n\n    return findings;\n}\n","// =============================================================================\n// onenter-loop.ts — Detect unconditional _onEnter transition cycles\n//\n// Only reports cycles where ALL edges are \"definite\" (unconditional).\n// Conditional _onEnter bounces are an intentional pattern — not bugs.\n// Self-loops are excluded: the runtime silently ignores same-state transitions.\n// =============================================================================\n\nimport type { StateGraph, Finding } from \"../types\";\n\n/**\n * Check for _onEnter transition loops in the graph.\n * Only reports cycles where every edge is \"definite\".\n * Recurses into child graphs.\n */\nexport function checkOnEnterLoops(graph: StateGraph, parentState?: string): Finding[] {\n    const findings: Finding[] = [];\n\n    findings.push(...findOnEnterLoopsInGraph(graph, parentState));\n\n    for (const [childParentState, childGraph] of Object.entries(graph.children)) {\n        findings.push(...checkOnEnterLoops(childGraph, childParentState));\n    }\n\n    return findings;\n}\n\nfunction findOnEnterLoopsInGraph(graph: StateGraph, parentState?: string): Finding[] {\n    const { fsmId, nodes } = graph;\n\n    // Build the _onEnter-only subgraph: edges where inputName === \"_onEnter\"\n    // and confidence === \"definite\", excluding self-loops (same-state transitions\n    // are no-ops in the runtime — they hit the same-state guard and return early).\n    const onEnterEdges: Record<string, string[]> = {};\n\n    for (const [stateName, node] of Object.entries(nodes)) {\n        const definiteOnEnterTargets = node.edges\n            .filter(\n                e => e.inputName === \"_onEnter\" && e.confidence === \"definite\" && e.to !== stateName // exclude self-loops\n            )\n            .map(e => e.to);\n\n        if (definiteOnEnterTargets.length > 0) {\n            onEnterEdges[stateName] = definiteOnEnterTargets;\n        }\n    }\n\n    // DFS cycle detection on the _onEnter subgraph.\n    // We use the \"white-gray-black\" algorithm: unvisited, in-stack, done.\n    const WHITE = 0,\n        GRAY = 1,\n        BLACK = 2;\n    const color: Record<string, number> = {};\n\n    for (const stateName of Object.keys(nodes)) {\n        color[stateName] = WHITE;\n    }\n\n    const findings: Finding[] = [];\n    const reportedCycles = new Set<string>();\n\n    const dfs = (node: string, stack: string[]): void => {\n        color[node] = GRAY;\n        stack.push(node);\n\n        for (const neighbor of onEnterEdges[node] ?? []) {\n            if (color[neighbor] === GRAY) {\n                // Found a back edge — extract the cycle from the stack\n                const cycleStart = stack.indexOf(neighbor);\n                const cycle = stack.slice(cycleStart);\n\n                // Deduplicate: sort cycle members to produce a canonical key\n                const cycleKey = [...cycle].sort().join(\",\");\n                if (!reportedCycles.has(cycleKey)) {\n                    reportedCycles.add(cycleKey);\n                    findings.push({\n                        type: \"onenter-loop\",\n                        message:\n                            `Unconditional _onEnter loop detected in FSM \"${fsmId}\": ` +\n                            cycle.join(\" → \") +\n                            ` → ${neighbor}`,\n                        fsmId,\n                        states: cycle,\n                        parentState,\n                    });\n                }\n            } else if (color[neighbor] === WHITE) {\n                dfs(neighbor, stack);\n            }\n        }\n\n        stack.pop();\n        color[node] = BLACK;\n    };\n\n    for (const stateName of Object.keys(nodes)) {\n        if (color[stateName] === WHITE) {\n            dfs(stateName, []);\n        }\n    }\n\n    return findings;\n}\n","// =============================================================================\n// missing-handler.ts — Detect states missing handlers for known inputs\n//\n// Collects the union of all input names across all edges in a graph\n// (excluding _onEnter, _onExit, and *), then flags any state that handles\n// fewer inputs than the global union — unless that state has a * catch-all,\n// which implicitly handles every input.\n//\n// Limitation: Only inputs visible as graph edges are included. Function\n// handlers that produce no statically-extractable return target are invisible\n// to the graph, so the input union may be incomplete. This check is\n// best-effort and operates on what the graph knows.\n//\n// Does NOT recurse into child graphs automatically — child graphs have their\n// own independent input sets. The top-level checkMissingHandlers recurses\n// into children the same way all other checks do.\n// =============================================================================\n\nimport type { StateGraph, Finding } from \"../types\";\n\n// These are structural keys in the state object, not real inputs.\n// Including them in the \"all inputs\" union would cause false positives\n// on every FSM.\nconst EXCLUDED_INPUTS = new Set([\"_onEnter\", \"_onExit\", \"*\"]);\n\n/**\n * Check for states with missing handlers relative to the FSM's full input set.\n * States with a `*` catch-all edge are skipped entirely.\n * Recurses into child graphs independently.\n */\nexport function checkMissingHandlers(graph: StateGraph, parentState?: string): Finding[] {\n    const findings: Finding[] = [];\n\n    findings.push(...findMissingHandlersInGraph(graph, parentState));\n\n    for (const [childParentState, childGraph] of Object.entries(graph.children)) {\n        findings.push(...checkMissingHandlers(childGraph, childParentState));\n    }\n\n    return findings;\n}\n\nfunction findMissingHandlersInGraph(graph: StateGraph, parentState?: string): Finding[] {\n    const { fsmId, nodes } = graph;\n\n    // Collect the global input union: every inputName across all edges in\n    // all states, minus the structural keys we never want to check.\n    const allInputs = new Set<string>();\n    for (const node of Object.values(nodes)) {\n        for (const edge of node.edges) {\n            if (!EXCLUDED_INPUTS.has(edge.inputName)) {\n                allInputs.add(edge.inputName);\n            }\n        }\n    }\n\n    // Nothing to compare if the FSM has no named inputs at all (e.g., single\n    // state with only _onEnter, or an empty FSM).\n    if (allInputs.size === 0) {\n        return [];\n    }\n\n    const findings: Finding[] = [];\n\n    for (const [stateName, node] of Object.entries(nodes)) {\n        // States with a * catch-all handle every input by definition.\n        // Reporting missing handlers here would always be a false positive.\n        const hasCatchAll = node.edges.some(e => e.inputName === \"*\");\n        if (hasCatchAll) {\n            continue;\n        }\n\n        // Collect the inputs this state explicitly handles (via edges).\n        const stateInputs = new Set<string>();\n        for (const edge of node.edges) {\n            if (!EXCLUDED_INPUTS.has(edge.inputName)) {\n                stateInputs.add(edge.inputName);\n            }\n        }\n\n        // Diff: inputs the FSM knows about that this state doesn't handle.\n        const missingInputs = [...allInputs].filter(input => !stateInputs.has(input));\n\n        if (missingInputs.length > 0) {\n            findings.push({\n                type: \"missing-handler\",\n                message:\n                    `State \"${stateName}\" in FSM \"${fsmId}\" is missing handlers for: ` +\n                    missingInputs.map(i => `\"${i}\"`).join(\", \") +\n                    \".\",\n                fsmId,\n                states: [stateName],\n                inputs: missingInputs,\n                parentState,\n            });\n        }\n    }\n\n    return findings;\n}\n","// =============================================================================\n// inspect.ts — Public API entry points\n//\n// inspect() is the convenience function: build graph + run all checks.\n// inspectGraph() is for callers that already have a graph (e.g., tools\n// that build the graph once and run multiple check passes).\n// =============================================================================\n\nimport { buildStateGraph } from \"./graph-builder\";\nimport { checkUnreachable } from \"./checks/unreachable\";\nimport { checkOnEnterLoops } from \"./checks/onenter-loop\";\nimport { checkMissingHandlers } from \"./checks/missing-handler\";\nimport type { StateGraph, Finding, InspectInput } from \"./types\";\n\n/**\n * Build a state graph from the input and run all structural checks.\n * Returns an array of findings — empty means no issues detected.\n *\n * @param input - A config object or a live Fsm/BehavioralFsm instance\n */\nexport function inspect(input: InspectInput): Finding[] {\n    const graph = buildStateGraph(input);\n    return inspectGraph(graph);\n}\n\n/**\n * Run all structural checks against a pre-built state graph.\n * Useful when you've already called buildStateGraph() for another purpose\n * (e.g., diagram export) and want to avoid building it twice.\n *\n * @param graph - A StateGraph produced by buildStateGraph()\n */\nexport function inspectGraph(graph: StateGraph): Finding[] {\n    return [\n        ...checkUnreachable(graph),\n        ...checkOnEnterLoops(graph),\n        ...checkMissingHandlers(graph),\n    ];\n}\n"],"mappings":"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"}