@vielzeug/clockwork 1.0.2
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 +220 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +9 -0
- package/dist/_dev.js.map +1 -0
- package/dist/_trace.cjs +2 -0
- package/dist/_trace.cjs.map +1 -0
- package/dist/_trace.d.ts +7 -0
- package/dist/_trace.d.ts.map +1 -0
- package/dist/_trace.js +17 -0
- package/dist/_trace.js.map +1 -0
- package/dist/clockwork.cjs +2 -0
- package/dist/clockwork.cjs.map +1 -0
- package/dist/clockwork.iife.js +2 -0
- package/dist/clockwork.iife.js.map +1 -0
- package/dist/clockwork.js +2 -0
- package/dist/clockwork.js.map +1 -0
- package/dist/definition.cjs +2 -0
- package/dist/definition.cjs.map +1 -0
- package/dist/definition.d.ts +16 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +49 -0
- package/dist/definition.js.map +1 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +29 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +36 -0
- package/dist/devtools.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +57 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +64 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/interpret.cjs +2 -0
- package/dist/interpret.cjs.map +1 -0
- package/dist/interpret.d.ts +25 -0
- package/dist/interpret.d.ts.map +1 -0
- package/dist/interpret.js +298 -0
- package/dist/interpret.js.map +1 -0
- package/dist/types.d.ts +277 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interpret.cjs","names":[],"sources":["../src/interpret.ts"],"sourcesContent":["import { batch, readonly, signal } from '@vielzeug/ripple';\n\nimport type {\n DebugEvent,\n EventByType,\n EventType,\n InterpretOptions,\n LifecycleEvent,\n MachineConfig,\n MachineDefinition,\n MachineEvent,\n MachineInstance,\n MachineSnapshot,\n SendResult,\n StateNode,\n TransitionDef,\n TransitionTraceEntry,\n} from './types.js';\n\nimport { warn } from './_dev';\nimport { createTraceBuffer } from './_trace.js';\nimport { getAncestorPaths, getNodeAtPath, resolveLeaf, validateDefinition } from './definition.js';\nimport {\n ClockworkInvalidMaxTransitionsError,\n ClockworkInvalidSnapshotStateError,\n ClockworkInvalidValidateContextError,\n ClockworkTransitionLoopGuardError,\n} from './errors.js';\n\n// ── Internal constants ────────────────────────────────────────────────────────\n\nconst INIT_EVENT = { type: '$init' } as const;\nconst HYDRATE_EVENT = { type: '$hydrate' } as const;\n\n// ── Pure resolver ─────────────────────────────────────────────────────────────\n\n/**\n * Resolves which transition should be taken for a given state + event.\n * Pure function — no side effects; useful for testing transition logic independently.\n *\n * Optionally calls `onGuard` for each guard evaluation.\n */\nconst resolveTransition = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n definition: Readonly<MachineConfig<State, Ctx, Ev>>,\n input: {\n context: Readonly<Ctx>;\n event: NoInfer<Ev>;\n state: State;\n },\n onGuard?: (info: { context: Readonly<Ctx>; event: Ev; from: State; passed: boolean; target: State }) => void,\n): TransitionDef<State, Ctx, Ev> | undefined => {\n const { context, event, state } = input;\n\n // Defensive: callers piping untyped external data through an `Ev`-typed cast (e.g. a\n // deserialized network message passed to `send()`) can violate the MachineEvent contract\n // at runtime. Treat a missing/non-string `type` as \"no matching transition\" rather than\n // crashing with a raw TypeError.\n if (!event || typeof (event as unknown as { type?: unknown }).type !== 'string') return undefined;\n\n const ancestors = getAncestorPaths(state);\n\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const node = getNodeAtPath(definition.states, ancestors[i]) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.on) continue;\n\n // Own-property check — an event `type` of \"__proto__\"/\"constructor\"/etc. must resolve\n // to \"no matching transition\", not an inherited Object.prototype member.\n const eventType = event.type as EventType<Ev>;\n const raw = Object.hasOwn(node.on, eventType) ? node.on[eventType] : undefined;\n\n if (!raw) continue;\n\n const defs = Array.isArray(raw) ? raw : [raw];\n\n for (const def of defs) {\n const passed = !def.guard || def.guard({ context, event: event as EventByType<Ev, EventType<Ev>> });\n\n onGuard?.({ context, event, from: state, passed, target: def.target as State });\n\n if (passed) return def as TransitionDef<State, Ctx, Ev>;\n }\n }\n\n return undefined;\n};\n\n// ── SendResult helpers ─────────────────────────────────────────────────────────\n\nconst RESULT_TRANSITIONED: SendResult = Object.freeze({ status: 'transitioned' });\nconst RESULT_QUEUED: SendResult = Object.freeze({ status: 'queued' });\nconst RESULT_REJECTED: SendResult = Object.freeze({ status: 'rejected' });\n\n// ── Core interpreter ──────────────────────────────────────────────────────────\n\nconst _interpret = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n definition: Readonly<MachineConfig<State, Ctx, Ev>>,\n options: InterpretOptions<State, Ctx, Ev> = {},\n): MachineInstance<State, Ctx, Ev> => {\n // R8: unified onDebug — no more separate onTransition\n const onDebug = options.onDebug;\n // Auto-enable trace (default 50) when onDebug is set but traceLimit not explicit\n const traceLimit = options.traceLimit ?? (onDebug ? 50 : 0);\n\n if (options.maxTransitionsPerFlush !== undefined && options.maxTransitionsPerFlush < 1) {\n throw new ClockworkInvalidMaxTransitionsError(\n 'maxTransitionsPerFlush must be greater than 0',\n options.maxTransitionsPerFlush,\n );\n }\n\n const maxTransitionsPerFlush = options.maxTransitionsPerFlush ?? 1_000;\n const clone = options.clone ?? structuredClone;\n // Widen State keys to string so getNodeAtPath / getAncestorPaths work with plain string paths.\n const states = definition.states as unknown as Record<string, StateNode<string, Ctx, Ev>>;\n const interceptors = options.interceptors ?? [];\n const persistedSnapshot = options.snapshot ?? options.persistence?.load();\n\n if (persistedSnapshot) {\n const snapshotRoot = persistedSnapshot.state.split('.')[0];\n const snapshotValid =\n Object.hasOwn(definition.states, snapshotRoot) &&\n (!persistedSnapshot.state.includes('.') ||\n !!getNodeAtPath(definition.states as Record<string, StateNode<string, Ctx, Ev>>, persistedSnapshot.state));\n\n if (!snapshotValid) {\n throw new ClockworkInvalidSnapshotStateError(\n `snapshot state \"${persistedSnapshot.state}\" not found in states`,\n persistedSnapshot.state,\n );\n }\n }\n\n // R9: validateContext returns true | string — string is the failure reason\n const assertContext = (context: Ctx, phase: 'init' | 'transition'): void => {\n const validator = definition.validateContext;\n\n if (!validator) return;\n\n const result = validator(context);\n\n if (result !== true) {\n throw new ClockworkInvalidValidateContextError(\n `context failed validation during ${phase}${result ? `: ${result}` : ''}`,\n phase,\n result,\n );\n }\n };\n\n const resolvedInitial = persistedSnapshot\n ? persistedSnapshot.state\n : (resolveLeaf(states, definition.initial) as State);\n\n const initialContext = persistedSnapshot\n ? clone(persistedSnapshot.context)\n : clone(('context' in definition ? definition.context : ({} as Ctx)) as Ctx);\n\n if (!persistedSnapshot) assertContext(initialContext, 'init');\n\n // ── Section: State & context signals ──────────────────────────────────────\n\n const state_ = signal(resolvedInitial);\n const context_ = signal(initialContext);\n\n // ── Section: Trace ring buffer ─────────────────────────────────────────────\n\n const trace = createTraceBuffer<State, Ev>(traceLimit);\n\n // ── Section: Lifecycle ─────────────────────────────────────────────────────\n\n const disposeController = new AbortController();\n let disposed = false;\n\n // ── Section: Invoke scheduler ──────────────────────────────────────────────\n\n let invokeCounter = 0;\n\n const activeInvokes = new Set<{\n controller: AbortController;\n event: Ev | LifecycleEvent;\n id: string;\n /** Path of the state node that owns this invoke — scopes abort-on-exit to that subtree. */\n path: string;\n state: State;\n }>();\n\n /**\n * Aborts active invokes. With `paths`, only invokes owned by one of those state paths are\n * stopped (used when exiting part of the hierarchy); omit `paths` to stop everything (dispose).\n */\n const stopInvokes = (paths?: readonly string[]): void => {\n for (const invoke of activeInvokes) {\n if (paths && !paths.includes(invoke.path)) continue;\n\n invoke.controller.abort();\n onDebug?.({\n context: context_.value,\n event: invoke.event,\n invokeId: invoke.id,\n state: invoke.state,\n type: 'invoke-abort',\n });\n activeInvokes.delete(invoke);\n }\n };\n\n // ── Section: After-timer scheduler ────────────────────────────────────────\n\n const activeTimers = new Set<{ path: string; timer: ReturnType<typeof setTimeout> }>();\n\n /**\n * Clears active after-timers. With `paths`, only timers owned by one of those state paths are\n * cleared (used when exiting part of the hierarchy); omit `paths` to clear everything (dispose).\n */\n const clearTimers = (paths?: readonly string[]): void => {\n for (const entry of activeTimers) {\n if (paths && !paths.includes(entry.path)) continue;\n\n clearTimeout(entry.timer);\n activeTimers.delete(entry);\n }\n };\n\n // ── Section: Persistence ───────────────────────────────────────────────────\n\n const saveSnapshot = (): void => {\n options.persistence?.save({ context: clone(context_.value), state: state_.value });\n };\n\n // ── Section: Subscribe ─────────────────────────────────────────────────────\n\n const subscribers = new Set<(snapshot: MachineSnapshot<State, Ctx>) => void>();\n\n const notifySubscribers = (): void => {\n if (subscribers.size === 0) return;\n\n const snap: MachineSnapshot<State, Ctx> = Object.freeze({ context: context_.value, state: state_.value });\n\n for (const fn of subscribers) fn(snap);\n };\n\n // ── Section: Event queue (hoisted so executeTransition closures can reference) ──\n\n type EventQueueItem = { readonly event: Ev; readonly tag: 'event' };\n type AfterQueueItem = {\n readonly actions: Array<(args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void>;\n readonly afterEvent: { readonly delay: number; readonly type: '$after' };\n readonly from: State;\n readonly tag: 'after';\n readonly target: State;\n };\n\n type QueueItem = AfterQueueItem | EventQueueItem;\n\n const queue: QueueItem[] = [];\n let draining = false;\n\n // ── Section: Hierarchy / transition execution ──────────────────────────────\n\n const computeTransitionPaths = (from: string, to: string): { entryPaths: string[]; exitPaths: string[] } => {\n if (!from.includes('.') && !to.includes('.')) {\n return { entryPaths: [to], exitPaths: [from] };\n }\n\n if (from === to) {\n return { entryPaths: [to], exitPaths: [from] };\n }\n\n const fromAncestors = getAncestorPaths(from);\n const toAncestors = getAncestorPaths(to);\n\n // Advance while both paths share a common prefix — lcaIndex lands at\n // the first segment that diverges (the deepest common ancestor's depth + 1).\n let lcaIndex = 0;\n\n while (\n lcaIndex < fromAncestors.length &&\n lcaIndex < toAncestors.length &&\n fromAncestors[lcaIndex] === toAncestors[lcaIndex]\n ) {\n lcaIndex++;\n }\n\n return {\n entryPaths: toAncestors.slice(lcaIndex),\n exitPaths: fromAncestors.slice(lcaIndex).reverse(),\n };\n };\n\n const executeTransition = (\n from: State,\n resolvedTarget: State,\n actions: Array<(args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void>,\n event: Ev | LifecycleEvent,\n ): void => {\n const { entryPaths, exitPaths } = computeTransitionPaths(from, resolvedTarget);\n const draft = clone(context_.value);\n\n for (const path of exitPaths) {\n getNodeAtPath(states, path)?.exit?.({ context: draft, event });\n }\n\n for (const fn of actions) fn({ context: draft, event });\n\n for (const path of entryPaths) {\n getNodeAtPath(states, path)?.entry?.({ context: draft, event });\n }\n\n // Freeze before assertContext so validateContext always receives a read-only context\n Object.freeze(draft);\n assertContext(draft, 'transition');\n\n batch(() => {\n stopInvokes(exitPaths);\n clearTimers(exitPaths);\n state_.value = resolvedTarget;\n context_.value = draft;\n });\n\n // R8: emit unified 'transition' debug event (replaces onTransition callback)\n onDebug?.({ event, from, to: resolvedTarget, type: 'transition' } as DebugEvent<State, Ctx, Ev>);\n trace?.push({ event, from, timestamp: Date.now(), to: resolvedTarget });\n notifySubscribers();\n saveSnapshot();\n // Only (re)start invokes/timers for newly-entered paths — ancestors that remain active\n // across the transition (e.g. a sibling-to-sibling move under the same compound parent)\n // keep their invokes/timers running uninterrupted.\n runInvokes(event, entryPaths);\n scheduleAfterTransitions(entryPaths);\n };\n\n // ── Section: Invoke scheduling ─────────────────────────────────────────────\n\n const runInvokes = (triggerEvent: Ev | LifecycleEvent, paths: readonly string[]): void => {\n for (const path of paths) {\n const node = getNodeAtPath(states, path) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.invoke?.length) continue;\n\n for (const invokeDef of node.invoke) {\n const controller = new AbortController();\n const invokeId = invokeDef.id ?? String(++invokeCounter);\n const capturedContext = context_.value;\n const invokeInfo = { controller, event: triggerEvent, id: invokeId, path, state: state_.value };\n\n activeInvokes.add(invokeInfo);\n onDebug?.({\n context: capturedContext,\n event: triggerEvent,\n invokeId,\n state: state_.value,\n type: 'invoke-start',\n });\n\n void invokeDef\n .src({ context: capturedContext, entryEvent: triggerEvent, signal: controller.signal })\n .then((result) => {\n activeInvokes.delete(invokeInfo);\n\n if (disposed || controller.signal.aborted || !invokeDef.onDone) return;\n\n onDebug?.({\n context: capturedContext,\n event: triggerEvent,\n invokeId,\n result,\n state: state_.value,\n type: 'invoke-done',\n });\n\n queue.push({ event: invokeDef.onDone(result, capturedContext), tag: 'event' });\n\n if (!draining) drainQueue();\n })\n .catch((error: unknown) => {\n activeInvokes.delete(invokeInfo);\n\n if (disposed || controller.signal.aborted || !invokeDef.onError) return;\n\n onDebug?.({\n context: capturedContext,\n error,\n event: triggerEvent,\n invokeId,\n state: state_.value,\n type: 'invoke-error',\n });\n\n queue.push({ event: invokeDef.onError(error, capturedContext), tag: 'event' });\n\n if (!draining) drainQueue();\n });\n }\n }\n };\n\n // ── Section: After (delayed) transitions ───────────────────────────────────\n\n const scheduleAfterTransitions = (paths: readonly string[]): void => {\n for (const path of paths) {\n const node = getNodeAtPath(states, path) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.after?.length) continue;\n\n for (const afterDef of node.after) {\n const timer = setTimeout(() => {\n activeTimers.delete(entry);\n\n // A sibling-to-sibling transition elsewhere in the tree may have moved the leaf\n // while this timer's owning path stayed active — fire from wherever we are now,\n // and only if that path is still part of the live ancestor chain.\n if (disposed || !getAncestorPaths(state_.value).includes(path)) return;\n\n const from = state_.value;\n // R10: after.guard unified — receives { context, event } like all other guards\n const afterEvent = { delay: afterDef.delay, type: '$after' } as const;\n\n if (afterDef.guard && !afterDef.guard({ context: context_.value, event: afterEvent })) return;\n\n const resolvedTarget = resolveLeaf(states, afterDef.target) as State;\n\n queue.push({\n actions: (afterDef.actions ?? []) as Array<\n (args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void\n >,\n afterEvent,\n from,\n tag: 'after',\n target: resolvedTarget,\n });\n drainQueue();\n }, afterDef.delay);\n const entry = { path, timer };\n\n activeTimers.add(entry);\n }\n }\n };\n\n // ── Section: Event queue processing ─────────────────────────────────────────\n\n const processEvent = (item: QueueItem): boolean => {\n if (disposed) return false;\n\n if (item.tag === 'after') {\n executeTransition(item.from, item.target, item.actions, item.afterEvent);\n\n return true;\n }\n\n const from = state_.value;\n\n const transition = resolveTransition(\n definition,\n { context: context_.value, event: item.event, state: from },\n onDebug ? (info) => onDebug({ ...info, type: 'guard' }) : undefined,\n );\n\n if (!transition) {\n onDebug?.({ event: item.event, from, type: 'transition-skipped' });\n\n return false;\n }\n\n const resolvedTarget = resolveLeaf(states, transition.target) as State;\n const actions = (transition.actions ?? []) as Array<\n (args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void\n >;\n\n executeTransition(from, resolvedTarget, actions, item.event);\n\n return true;\n };\n\n const drainQueueInner = (): void => {\n let processed = 0;\n\n while (queue.length > 0) {\n if (++processed > maxTransitionsPerFlush) {\n throw new ClockworkTransitionLoopGuardError(\n 'transition queue exceeded maxTransitionsPerFlush',\n maxTransitionsPerFlush,\n );\n }\n\n processEvent(queue.shift()!);\n }\n };\n\n const drainQueue = (): void => {\n if (draining) return;\n\n draining = true;\n\n try {\n drainQueueInner();\n } catch (err) {\n queue.length = 0;\n throw err;\n } finally {\n draining = false;\n }\n };\n\n // ── Section: Public API ────────────────────────────────────────────────────\n\n const can = (event: Ev): boolean => {\n if (disposed) return false;\n\n return !!resolveTransition(definition, { context: context_.value, event, state: state_.value });\n };\n\n const send = (event: Ev): SendResult => {\n if (disposed) {\n warn('send() called on a disposed machine — event ignored');\n\n return RESULT_REJECTED;\n }\n\n // Run interceptors left-to-right — first null wins\n let intercepted: Ev | null = event;\n\n for (const fn of interceptors) {\n intercepted = fn(intercepted, { context: context_.value, state: state_.value });\n\n if (intercepted === null) return RESULT_REJECTED;\n }\n\n const interceptedEvent = intercepted;\n\n if (draining) {\n queue.push({ event: interceptedEvent, tag: 'event' });\n\n return RESULT_QUEUED;\n }\n\n draining = true;\n\n try {\n const transitioned = processEvent({ event: interceptedEvent, tag: 'event' });\n\n drainQueueInner();\n\n return transitioned ? RESULT_TRANSITIONED : RESULT_REJECTED;\n } catch (err) {\n queue.length = 0;\n throw err;\n } finally {\n draining = false;\n }\n };\n\n const getSnapshot = (): MachineSnapshot<State, Ctx> =>\n Object.freeze({ context: clone(context_.value), state: state_.value });\n\n const getTrace = (): readonly TransitionTraceEntry<State, Ev>[] => trace?.get() ?? [];\n\n const matches = (...stateArgs: string[]): boolean => {\n if (disposed) return false;\n\n const current = state_.value;\n\n return stateArgs.some((s) => current === s || current.startsWith(`${s}.`));\n };\n\n // R5: subscribe via plain Set — no ripple effect(), no prevState/prevCtx tracking\n const subscribe = (fn: (snapshot: MachineSnapshot<State, Ctx>) => void): (() => void) => {\n subscribers.add(fn);\n\n return () => subscribers.delete(fn);\n };\n\n // ── Section: Initialization ────────────────────────────────────────────────\n\n const initialPaths = getAncestorPaths(resolvedInitial);\n\n if (persistedSnapshot) {\n runInvokes(HYDRATE_EVENT, initialPaths);\n scheduleAfterTransitions(initialPaths);\n } else {\n const hasEntry = initialPaths.some((p) => getNodeAtPath(states, p)?.entry);\n\n if (hasEntry) {\n const initDraft = clone(context_.value);\n\n for (const p of initialPaths) {\n getNodeAtPath(states, p)?.entry?.({ context: initDraft, event: INIT_EVENT });\n }\n\n assertContext(initDraft, 'init');\n context_.value = initDraft;\n }\n\n runInvokes(INIT_EVENT, initialPaths);\n scheduleAfterTransitions(initialPaths);\n\n if (options.persistence) saveSnapshot();\n }\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n stopInvokes();\n clearTimers();\n state_.dispose();\n context_.dispose();\n };\n\n return {\n can,\n context: readonly(context_),\n get disposalSignal() {\n return disposeController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot,\n getTrace,\n matches,\n send,\n state: readonly(state_),\n subscribe,\n [Symbol.dispose]: dispose,\n };\n};\n\n// ── Public entry point ────────────────────────────────────────────────────────\n\n/**\n * Validates a machine configuration and returns a reusable definition handle.\n *\n * Call `.start(options?)` to create a running instance.\n * Call `.resolve(input, options?)` to inspect transitions without starting a machine.\n *\n * @example\n * const counterDef = createMachine({\n * context: { count: 0 },\n * initial: 'idle',\n * states: { idle: { on: { INC: { actions: [({ context }) => { context.count += 1 }], target: 'idle' } } } },\n * });\n *\n * const m1 = counterDef.start();\n * const m2 = counterDef.start({ snapshot: { context: { count: 10 }, state: 'idle' } });\n *\n * // Test transitions without a running machine:\n * counterDef.resolve({ context: { count: 0 }, event: { type: 'INC' }, state: 'idle' });\n *\n * // Or start immediately (one-shot):\n * const m3 = createMachine(config).start();\n */\nexport const createMachine = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n config: MachineConfig<State, Ctx, Ev>,\n): MachineDefinition<State, Ctx, Ev> => {\n validateDefinition(config);\n\n return {\n resolve(input, options?) {\n return resolveTransition(config, input, options?.onGuard);\n },\n start(options?) {\n return _interpret(config, options);\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MachineConfig, MachineDefinition, MachineEvent } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates a machine configuration and returns a reusable definition handle.
|
|
4
|
+
*
|
|
5
|
+
* Call `.start(options?)` to create a running instance.
|
|
6
|
+
* Call `.resolve(input, options?)` to inspect transitions without starting a machine.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const counterDef = createMachine({
|
|
10
|
+
* context: { count: 0 },
|
|
11
|
+
* initial: 'idle',
|
|
12
|
+
* states: { idle: { on: { INC: { actions: [({ context }) => { context.count += 1 }], target: 'idle' } } } },
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* const m1 = counterDef.start();
|
|
16
|
+
* const m2 = counterDef.start({ snapshot: { context: { count: 10 }, state: 'idle' } });
|
|
17
|
+
*
|
|
18
|
+
* // Test transitions without a running machine:
|
|
19
|
+
* counterDef.resolve({ context: { count: 0 }, event: { type: 'INC' }, state: 'idle' });
|
|
20
|
+
*
|
|
21
|
+
* // Or start immediately (one-shot):
|
|
22
|
+
* const m3 = createMachine(config).start();
|
|
23
|
+
*/
|
|
24
|
+
export declare const createMachine: <State extends string, Ctx extends object, Ev extends MachineEvent>(config: MachineConfig<State, Ctx, Ev>) => MachineDefinition<State, Ctx, Ev>;
|
|
25
|
+
//# sourceMappingURL=interpret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interpret.d.ts","sourceRoot":"","sources":["../src/interpret.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,aAAa,EACb,iBAAiB,EACjB,YAAY,EAOb,MAAM,YAAY,CAAC;AAwmBpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,EAAE,SAAS,YAAY,EAC7F,QAAQ,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,KACpC,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAWlC,CAAC"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { warn as e } from "./_dev.js";
|
|
2
|
+
import { createTraceBuffer as t } from "./_trace.js";
|
|
3
|
+
import { ClockworkInvalidMaxTransitionsError as n, ClockworkInvalidSnapshotStateError as r, ClockworkInvalidValidateContextError as i, ClockworkTransitionLoopGuardError as a } from "./errors.js";
|
|
4
|
+
import { getAncestorPaths as o, getNodeAtPath as s, resolveLeaf as c, validateDefinition as l } from "./definition.js";
|
|
5
|
+
import { batch as u, readonly as d, signal as f } from "@vielzeug/ripple";
|
|
6
|
+
//#region src/interpret.ts
|
|
7
|
+
var p = { type: "$init" }, m = { type: "$hydrate" }, h = (e, t, n) => {
|
|
8
|
+
let { context: r, event: i, state: a } = t;
|
|
9
|
+
if (!i || typeof i.type != "string") return;
|
|
10
|
+
let c = o(a);
|
|
11
|
+
for (let t = c.length - 1; t >= 0; t--) {
|
|
12
|
+
let o = s(e.states, c[t]);
|
|
13
|
+
if (!o?.on) continue;
|
|
14
|
+
let l = i.type, u = Object.hasOwn(o.on, l) ? o.on[l] : void 0;
|
|
15
|
+
if (!u) continue;
|
|
16
|
+
let d = Array.isArray(u) ? u : [u];
|
|
17
|
+
for (let e of d) {
|
|
18
|
+
let t = !e.guard || e.guard({
|
|
19
|
+
context: r,
|
|
20
|
+
event: i
|
|
21
|
+
});
|
|
22
|
+
if (n?.({
|
|
23
|
+
context: r,
|
|
24
|
+
event: i,
|
|
25
|
+
from: a,
|
|
26
|
+
passed: t,
|
|
27
|
+
target: e.target
|
|
28
|
+
}), t) return e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, g = Object.freeze({ status: "transitioned" }), _ = Object.freeze({ status: "queued" }), v = Object.freeze({ status: "rejected" }), y = (l, y = {}) => {
|
|
32
|
+
let b = y.onDebug, x = y.traceLimit ?? (b ? 50 : 0);
|
|
33
|
+
if (y.maxTransitionsPerFlush !== void 0 && y.maxTransitionsPerFlush < 1) throw new n("maxTransitionsPerFlush must be greater than 0", y.maxTransitionsPerFlush);
|
|
34
|
+
let S = y.maxTransitionsPerFlush ?? 1e3, C = y.clone ?? structuredClone, w = l.states, T = y.interceptors ?? [], E = y.snapshot ?? y.persistence?.load();
|
|
35
|
+
if (E) {
|
|
36
|
+
let e = E.state.split(".")[0];
|
|
37
|
+
if (!(Object.hasOwn(l.states, e) && (!E.state.includes(".") || s(l.states, E.state)))) throw new r(`snapshot state "${E.state}" not found in states`, E.state);
|
|
38
|
+
}
|
|
39
|
+
let D = (e, t) => {
|
|
40
|
+
let n = l.validateContext;
|
|
41
|
+
if (!n) return;
|
|
42
|
+
let r = n(e);
|
|
43
|
+
if (r !== !0) throw new i(`context failed validation during ${t}${r ? `: ${r}` : ""}`, t, r);
|
|
44
|
+
}, O = E ? E.state : c(w, l.initial), k = C(E ? E.context : "context" in l ? l.context : {});
|
|
45
|
+
E || D(k, "init");
|
|
46
|
+
let A = f(O), j = f(k), M = t(x), N = new AbortController(), P = !1, F = 0, I = /* @__PURE__ */ new Set(), L = (e) => {
|
|
47
|
+
for (let t of I) e && !e.includes(t.path) || (t.controller.abort(), b?.({
|
|
48
|
+
context: j.value,
|
|
49
|
+
event: t.event,
|
|
50
|
+
invokeId: t.id,
|
|
51
|
+
state: t.state,
|
|
52
|
+
type: "invoke-abort"
|
|
53
|
+
}), I.delete(t));
|
|
54
|
+
}, R = /* @__PURE__ */ new Set(), z = (e) => {
|
|
55
|
+
for (let t of R) e && !e.includes(t.path) || (clearTimeout(t.timer), R.delete(t));
|
|
56
|
+
}, B = () => {
|
|
57
|
+
y.persistence?.save({
|
|
58
|
+
context: C(j.value),
|
|
59
|
+
state: A.value
|
|
60
|
+
});
|
|
61
|
+
}, V = /* @__PURE__ */ new Set(), H = () => {
|
|
62
|
+
if (V.size === 0) return;
|
|
63
|
+
let e = Object.freeze({
|
|
64
|
+
context: j.value,
|
|
65
|
+
state: A.value
|
|
66
|
+
});
|
|
67
|
+
for (let t of V) t(e);
|
|
68
|
+
}, U = [], W = !1, G = (e, t) => {
|
|
69
|
+
if (!e.includes(".") && !t.includes(".") || e === t) return {
|
|
70
|
+
entryPaths: [t],
|
|
71
|
+
exitPaths: [e]
|
|
72
|
+
};
|
|
73
|
+
let n = o(e), r = o(t), i = 0;
|
|
74
|
+
for (; i < n.length && i < r.length && n[i] === r[i];) i++;
|
|
75
|
+
return {
|
|
76
|
+
entryPaths: r.slice(i),
|
|
77
|
+
exitPaths: n.slice(i).reverse()
|
|
78
|
+
};
|
|
79
|
+
}, K = (e, t, n, r) => {
|
|
80
|
+
let { entryPaths: i, exitPaths: a } = G(e, t), o = C(j.value);
|
|
81
|
+
for (let e of a) s(w, e)?.exit?.({
|
|
82
|
+
context: o,
|
|
83
|
+
event: r
|
|
84
|
+
});
|
|
85
|
+
for (let e of n) e({
|
|
86
|
+
context: o,
|
|
87
|
+
event: r
|
|
88
|
+
});
|
|
89
|
+
for (let e of i) s(w, e)?.entry?.({
|
|
90
|
+
context: o,
|
|
91
|
+
event: r
|
|
92
|
+
});
|
|
93
|
+
Object.freeze(o), D(o, "transition"), u(() => {
|
|
94
|
+
L(a), z(a), A.value = t, j.value = o;
|
|
95
|
+
}), b?.({
|
|
96
|
+
event: r,
|
|
97
|
+
from: e,
|
|
98
|
+
to: t,
|
|
99
|
+
type: "transition"
|
|
100
|
+
}), M?.push({
|
|
101
|
+
event: r,
|
|
102
|
+
from: e,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
to: t
|
|
105
|
+
}), H(), B(), q(r, i), J(i);
|
|
106
|
+
}, q = (e, t) => {
|
|
107
|
+
for (let n of t) {
|
|
108
|
+
let t = s(w, n);
|
|
109
|
+
if (t?.invoke?.length) for (let r of t.invoke) {
|
|
110
|
+
let t = new AbortController(), i = r.id ?? String(++F), a = j.value, o = {
|
|
111
|
+
controller: t,
|
|
112
|
+
event: e,
|
|
113
|
+
id: i,
|
|
114
|
+
path: n,
|
|
115
|
+
state: A.value
|
|
116
|
+
};
|
|
117
|
+
I.add(o), b?.({
|
|
118
|
+
context: a,
|
|
119
|
+
event: e,
|
|
120
|
+
invokeId: i,
|
|
121
|
+
state: A.value,
|
|
122
|
+
type: "invoke-start"
|
|
123
|
+
}), r.src({
|
|
124
|
+
context: a,
|
|
125
|
+
entryEvent: e,
|
|
126
|
+
signal: t.signal
|
|
127
|
+
}).then((n) => {
|
|
128
|
+
I.delete(o), !(P || t.signal.aborted || !r.onDone) && (b?.({
|
|
129
|
+
context: a,
|
|
130
|
+
event: e,
|
|
131
|
+
invokeId: i,
|
|
132
|
+
result: n,
|
|
133
|
+
state: A.value,
|
|
134
|
+
type: "invoke-done"
|
|
135
|
+
}), U.push({
|
|
136
|
+
event: r.onDone(n, a),
|
|
137
|
+
tag: "event"
|
|
138
|
+
}), W || Z());
|
|
139
|
+
}).catch((n) => {
|
|
140
|
+
I.delete(o), !(P || t.signal.aborted || !r.onError) && (b?.({
|
|
141
|
+
context: a,
|
|
142
|
+
error: n,
|
|
143
|
+
event: e,
|
|
144
|
+
invokeId: i,
|
|
145
|
+
state: A.value,
|
|
146
|
+
type: "invoke-error"
|
|
147
|
+
}), U.push({
|
|
148
|
+
event: r.onError(n, a),
|
|
149
|
+
tag: "event"
|
|
150
|
+
}), W || Z());
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}, J = (e) => {
|
|
155
|
+
for (let t of e) {
|
|
156
|
+
let e = s(w, t);
|
|
157
|
+
if (e?.after?.length) for (let n of e.after) {
|
|
158
|
+
let e = {
|
|
159
|
+
path: t,
|
|
160
|
+
timer: setTimeout(() => {
|
|
161
|
+
if (R.delete(e), P || !o(A.value).includes(t)) return;
|
|
162
|
+
let r = A.value, i = {
|
|
163
|
+
delay: n.delay,
|
|
164
|
+
type: "$after"
|
|
165
|
+
};
|
|
166
|
+
if (n.guard && !n.guard({
|
|
167
|
+
context: j.value,
|
|
168
|
+
event: i
|
|
169
|
+
})) return;
|
|
170
|
+
let a = c(w, n.target);
|
|
171
|
+
U.push({
|
|
172
|
+
actions: n.actions ?? [],
|
|
173
|
+
afterEvent: i,
|
|
174
|
+
from: r,
|
|
175
|
+
tag: "after",
|
|
176
|
+
target: a
|
|
177
|
+
}), Z();
|
|
178
|
+
}, n.delay)
|
|
179
|
+
};
|
|
180
|
+
R.add(e);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}, Y = (e) => {
|
|
184
|
+
if (P) return !1;
|
|
185
|
+
if (e.tag === "after") return K(e.from, e.target, e.actions, e.afterEvent), !0;
|
|
186
|
+
let t = A.value, n = h(l, {
|
|
187
|
+
context: j.value,
|
|
188
|
+
event: e.event,
|
|
189
|
+
state: t
|
|
190
|
+
}, b ? (e) => b({
|
|
191
|
+
...e,
|
|
192
|
+
type: "guard"
|
|
193
|
+
}) : void 0);
|
|
194
|
+
if (!n) return b?.({
|
|
195
|
+
event: e.event,
|
|
196
|
+
from: t,
|
|
197
|
+
type: "transition-skipped"
|
|
198
|
+
}), !1;
|
|
199
|
+
let r = c(w, n.target), i = n.actions ?? [];
|
|
200
|
+
return K(t, r, i, e.event), !0;
|
|
201
|
+
}, X = () => {
|
|
202
|
+
let e = 0;
|
|
203
|
+
for (; U.length > 0;) {
|
|
204
|
+
if (++e > S) throw new a("transition queue exceeded maxTransitionsPerFlush", S);
|
|
205
|
+
Y(U.shift());
|
|
206
|
+
}
|
|
207
|
+
}, Z = () => {
|
|
208
|
+
if (!W) {
|
|
209
|
+
W = !0;
|
|
210
|
+
try {
|
|
211
|
+
X();
|
|
212
|
+
} catch (e) {
|
|
213
|
+
throw U.length = 0, e;
|
|
214
|
+
} finally {
|
|
215
|
+
W = !1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}, ee = (e) => P ? !1 : !!h(l, {
|
|
219
|
+
context: j.value,
|
|
220
|
+
event: e,
|
|
221
|
+
state: A.value
|
|
222
|
+
}), te = (t) => {
|
|
223
|
+
if (P) return e("send() called on a disposed machine — event ignored"), v;
|
|
224
|
+
let n = t;
|
|
225
|
+
for (let e of T) if (n = e(n, {
|
|
226
|
+
context: j.value,
|
|
227
|
+
state: A.value
|
|
228
|
+
}), n === null) return v;
|
|
229
|
+
let r = n;
|
|
230
|
+
if (W) return U.push({
|
|
231
|
+
event: r,
|
|
232
|
+
tag: "event"
|
|
233
|
+
}), _;
|
|
234
|
+
W = !0;
|
|
235
|
+
try {
|
|
236
|
+
let e = Y({
|
|
237
|
+
event: r,
|
|
238
|
+
tag: "event"
|
|
239
|
+
});
|
|
240
|
+
return X(), e ? g : v;
|
|
241
|
+
} catch (e) {
|
|
242
|
+
throw U.length = 0, e;
|
|
243
|
+
} finally {
|
|
244
|
+
W = !1;
|
|
245
|
+
}
|
|
246
|
+
}, ne = () => Object.freeze({
|
|
247
|
+
context: C(j.value),
|
|
248
|
+
state: A.value
|
|
249
|
+
}), re = () => M?.get() ?? [], ie = (...e) => {
|
|
250
|
+
if (P) return !1;
|
|
251
|
+
let t = A.value;
|
|
252
|
+
return e.some((e) => t === e || t.startsWith(`${e}.`));
|
|
253
|
+
}, ae = (e) => (V.add(e), () => V.delete(e)), Q = o(O);
|
|
254
|
+
if (E) q(m, Q), J(Q);
|
|
255
|
+
else {
|
|
256
|
+
if (Q.some((e) => s(w, e)?.entry)) {
|
|
257
|
+
let e = C(j.value);
|
|
258
|
+
for (let t of Q) s(w, t)?.entry?.({
|
|
259
|
+
context: e,
|
|
260
|
+
event: p
|
|
261
|
+
});
|
|
262
|
+
D(e, "init"), j.value = e;
|
|
263
|
+
}
|
|
264
|
+
q(p, Q), J(Q), y.persistence && B();
|
|
265
|
+
}
|
|
266
|
+
let $ = () => {
|
|
267
|
+
P || (P = !0, N.abort(), L(), z(), A.dispose(), j.dispose());
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
can: ee,
|
|
271
|
+
context: d(j),
|
|
272
|
+
get disposalSignal() {
|
|
273
|
+
return N.signal;
|
|
274
|
+
},
|
|
275
|
+
dispose: $,
|
|
276
|
+
get disposed() {
|
|
277
|
+
return P;
|
|
278
|
+
},
|
|
279
|
+
getSnapshot: ne,
|
|
280
|
+
getTrace: re,
|
|
281
|
+
matches: ie,
|
|
282
|
+
send: te,
|
|
283
|
+
state: d(A),
|
|
284
|
+
subscribe: ae,
|
|
285
|
+
[Symbol.dispose]: $
|
|
286
|
+
};
|
|
287
|
+
}, b = (e) => (l(e), {
|
|
288
|
+
resolve(t, n) {
|
|
289
|
+
return h(e, t, n?.onGuard);
|
|
290
|
+
},
|
|
291
|
+
start(t) {
|
|
292
|
+
return y(e, t);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
//#endregion
|
|
296
|
+
export { b as createMachine };
|
|
297
|
+
|
|
298
|
+
//# sourceMappingURL=interpret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interpret.js","names":[],"sources":["../src/interpret.ts"],"sourcesContent":["import { batch, readonly, signal } from '@vielzeug/ripple';\n\nimport type {\n DebugEvent,\n EventByType,\n EventType,\n InterpretOptions,\n LifecycleEvent,\n MachineConfig,\n MachineDefinition,\n MachineEvent,\n MachineInstance,\n MachineSnapshot,\n SendResult,\n StateNode,\n TransitionDef,\n TransitionTraceEntry,\n} from './types.js';\n\nimport { warn } from './_dev';\nimport { createTraceBuffer } from './_trace.js';\nimport { getAncestorPaths, getNodeAtPath, resolveLeaf, validateDefinition } from './definition.js';\nimport {\n ClockworkInvalidMaxTransitionsError,\n ClockworkInvalidSnapshotStateError,\n ClockworkInvalidValidateContextError,\n ClockworkTransitionLoopGuardError,\n} from './errors.js';\n\n// ── Internal constants ────────────────────────────────────────────────────────\n\nconst INIT_EVENT = { type: '$init' } as const;\nconst HYDRATE_EVENT = { type: '$hydrate' } as const;\n\n// ── Pure resolver ─────────────────────────────────────────────────────────────\n\n/**\n * Resolves which transition should be taken for a given state + event.\n * Pure function — no side effects; useful for testing transition logic independently.\n *\n * Optionally calls `onGuard` for each guard evaluation.\n */\nconst resolveTransition = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n definition: Readonly<MachineConfig<State, Ctx, Ev>>,\n input: {\n context: Readonly<Ctx>;\n event: NoInfer<Ev>;\n state: State;\n },\n onGuard?: (info: { context: Readonly<Ctx>; event: Ev; from: State; passed: boolean; target: State }) => void,\n): TransitionDef<State, Ctx, Ev> | undefined => {\n const { context, event, state } = input;\n\n // Defensive: callers piping untyped external data through an `Ev`-typed cast (e.g. a\n // deserialized network message passed to `send()`) can violate the MachineEvent contract\n // at runtime. Treat a missing/non-string `type` as \"no matching transition\" rather than\n // crashing with a raw TypeError.\n if (!event || typeof (event as unknown as { type?: unknown }).type !== 'string') return undefined;\n\n const ancestors = getAncestorPaths(state);\n\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const node = getNodeAtPath(definition.states, ancestors[i]) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.on) continue;\n\n // Own-property check — an event `type` of \"__proto__\"/\"constructor\"/etc. must resolve\n // to \"no matching transition\", not an inherited Object.prototype member.\n const eventType = event.type as EventType<Ev>;\n const raw = Object.hasOwn(node.on, eventType) ? node.on[eventType] : undefined;\n\n if (!raw) continue;\n\n const defs = Array.isArray(raw) ? raw : [raw];\n\n for (const def of defs) {\n const passed = !def.guard || def.guard({ context, event: event as EventByType<Ev, EventType<Ev>> });\n\n onGuard?.({ context, event, from: state, passed, target: def.target as State });\n\n if (passed) return def as TransitionDef<State, Ctx, Ev>;\n }\n }\n\n return undefined;\n};\n\n// ── SendResult helpers ─────────────────────────────────────────────────────────\n\nconst RESULT_TRANSITIONED: SendResult = Object.freeze({ status: 'transitioned' });\nconst RESULT_QUEUED: SendResult = Object.freeze({ status: 'queued' });\nconst RESULT_REJECTED: SendResult = Object.freeze({ status: 'rejected' });\n\n// ── Core interpreter ──────────────────────────────────────────────────────────\n\nconst _interpret = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n definition: Readonly<MachineConfig<State, Ctx, Ev>>,\n options: InterpretOptions<State, Ctx, Ev> = {},\n): MachineInstance<State, Ctx, Ev> => {\n // R8: unified onDebug — no more separate onTransition\n const onDebug = options.onDebug;\n // Auto-enable trace (default 50) when onDebug is set but traceLimit not explicit\n const traceLimit = options.traceLimit ?? (onDebug ? 50 : 0);\n\n if (options.maxTransitionsPerFlush !== undefined && options.maxTransitionsPerFlush < 1) {\n throw new ClockworkInvalidMaxTransitionsError(\n 'maxTransitionsPerFlush must be greater than 0',\n options.maxTransitionsPerFlush,\n );\n }\n\n const maxTransitionsPerFlush = options.maxTransitionsPerFlush ?? 1_000;\n const clone = options.clone ?? structuredClone;\n // Widen State keys to string so getNodeAtPath / getAncestorPaths work with plain string paths.\n const states = definition.states as unknown as Record<string, StateNode<string, Ctx, Ev>>;\n const interceptors = options.interceptors ?? [];\n const persistedSnapshot = options.snapshot ?? options.persistence?.load();\n\n if (persistedSnapshot) {\n const snapshotRoot = persistedSnapshot.state.split('.')[0];\n const snapshotValid =\n Object.hasOwn(definition.states, snapshotRoot) &&\n (!persistedSnapshot.state.includes('.') ||\n !!getNodeAtPath(definition.states as Record<string, StateNode<string, Ctx, Ev>>, persistedSnapshot.state));\n\n if (!snapshotValid) {\n throw new ClockworkInvalidSnapshotStateError(\n `snapshot state \"${persistedSnapshot.state}\" not found in states`,\n persistedSnapshot.state,\n );\n }\n }\n\n // R9: validateContext returns true | string — string is the failure reason\n const assertContext = (context: Ctx, phase: 'init' | 'transition'): void => {\n const validator = definition.validateContext;\n\n if (!validator) return;\n\n const result = validator(context);\n\n if (result !== true) {\n throw new ClockworkInvalidValidateContextError(\n `context failed validation during ${phase}${result ? `: ${result}` : ''}`,\n phase,\n result,\n );\n }\n };\n\n const resolvedInitial = persistedSnapshot\n ? persistedSnapshot.state\n : (resolveLeaf(states, definition.initial) as State);\n\n const initialContext = persistedSnapshot\n ? clone(persistedSnapshot.context)\n : clone(('context' in definition ? definition.context : ({} as Ctx)) as Ctx);\n\n if (!persistedSnapshot) assertContext(initialContext, 'init');\n\n // ── Section: State & context signals ──────────────────────────────────────\n\n const state_ = signal(resolvedInitial);\n const context_ = signal(initialContext);\n\n // ── Section: Trace ring buffer ─────────────────────────────────────────────\n\n const trace = createTraceBuffer<State, Ev>(traceLimit);\n\n // ── Section: Lifecycle ─────────────────────────────────────────────────────\n\n const disposeController = new AbortController();\n let disposed = false;\n\n // ── Section: Invoke scheduler ──────────────────────────────────────────────\n\n let invokeCounter = 0;\n\n const activeInvokes = new Set<{\n controller: AbortController;\n event: Ev | LifecycleEvent;\n id: string;\n /** Path of the state node that owns this invoke — scopes abort-on-exit to that subtree. */\n path: string;\n state: State;\n }>();\n\n /**\n * Aborts active invokes. With `paths`, only invokes owned by one of those state paths are\n * stopped (used when exiting part of the hierarchy); omit `paths` to stop everything (dispose).\n */\n const stopInvokes = (paths?: readonly string[]): void => {\n for (const invoke of activeInvokes) {\n if (paths && !paths.includes(invoke.path)) continue;\n\n invoke.controller.abort();\n onDebug?.({\n context: context_.value,\n event: invoke.event,\n invokeId: invoke.id,\n state: invoke.state,\n type: 'invoke-abort',\n });\n activeInvokes.delete(invoke);\n }\n };\n\n // ── Section: After-timer scheduler ────────────────────────────────────────\n\n const activeTimers = new Set<{ path: string; timer: ReturnType<typeof setTimeout> }>();\n\n /**\n * Clears active after-timers. With `paths`, only timers owned by one of those state paths are\n * cleared (used when exiting part of the hierarchy); omit `paths` to clear everything (dispose).\n */\n const clearTimers = (paths?: readonly string[]): void => {\n for (const entry of activeTimers) {\n if (paths && !paths.includes(entry.path)) continue;\n\n clearTimeout(entry.timer);\n activeTimers.delete(entry);\n }\n };\n\n // ── Section: Persistence ───────────────────────────────────────────────────\n\n const saveSnapshot = (): void => {\n options.persistence?.save({ context: clone(context_.value), state: state_.value });\n };\n\n // ── Section: Subscribe ─────────────────────────────────────────────────────\n\n const subscribers = new Set<(snapshot: MachineSnapshot<State, Ctx>) => void>();\n\n const notifySubscribers = (): void => {\n if (subscribers.size === 0) return;\n\n const snap: MachineSnapshot<State, Ctx> = Object.freeze({ context: context_.value, state: state_.value });\n\n for (const fn of subscribers) fn(snap);\n };\n\n // ── Section: Event queue (hoisted so executeTransition closures can reference) ──\n\n type EventQueueItem = { readonly event: Ev; readonly tag: 'event' };\n type AfterQueueItem = {\n readonly actions: Array<(args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void>;\n readonly afterEvent: { readonly delay: number; readonly type: '$after' };\n readonly from: State;\n readonly tag: 'after';\n readonly target: State;\n };\n\n type QueueItem = AfterQueueItem | EventQueueItem;\n\n const queue: QueueItem[] = [];\n let draining = false;\n\n // ── Section: Hierarchy / transition execution ──────────────────────────────\n\n const computeTransitionPaths = (from: string, to: string): { entryPaths: string[]; exitPaths: string[] } => {\n if (!from.includes('.') && !to.includes('.')) {\n return { entryPaths: [to], exitPaths: [from] };\n }\n\n if (from === to) {\n return { entryPaths: [to], exitPaths: [from] };\n }\n\n const fromAncestors = getAncestorPaths(from);\n const toAncestors = getAncestorPaths(to);\n\n // Advance while both paths share a common prefix — lcaIndex lands at\n // the first segment that diverges (the deepest common ancestor's depth + 1).\n let lcaIndex = 0;\n\n while (\n lcaIndex < fromAncestors.length &&\n lcaIndex < toAncestors.length &&\n fromAncestors[lcaIndex] === toAncestors[lcaIndex]\n ) {\n lcaIndex++;\n }\n\n return {\n entryPaths: toAncestors.slice(lcaIndex),\n exitPaths: fromAncestors.slice(lcaIndex).reverse(),\n };\n };\n\n const executeTransition = (\n from: State,\n resolvedTarget: State,\n actions: Array<(args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void>,\n event: Ev | LifecycleEvent,\n ): void => {\n const { entryPaths, exitPaths } = computeTransitionPaths(from, resolvedTarget);\n const draft = clone(context_.value);\n\n for (const path of exitPaths) {\n getNodeAtPath(states, path)?.exit?.({ context: draft, event });\n }\n\n for (const fn of actions) fn({ context: draft, event });\n\n for (const path of entryPaths) {\n getNodeAtPath(states, path)?.entry?.({ context: draft, event });\n }\n\n // Freeze before assertContext so validateContext always receives a read-only context\n Object.freeze(draft);\n assertContext(draft, 'transition');\n\n batch(() => {\n stopInvokes(exitPaths);\n clearTimers(exitPaths);\n state_.value = resolvedTarget;\n context_.value = draft;\n });\n\n // R8: emit unified 'transition' debug event (replaces onTransition callback)\n onDebug?.({ event, from, to: resolvedTarget, type: 'transition' } as DebugEvent<State, Ctx, Ev>);\n trace?.push({ event, from, timestamp: Date.now(), to: resolvedTarget });\n notifySubscribers();\n saveSnapshot();\n // Only (re)start invokes/timers for newly-entered paths — ancestors that remain active\n // across the transition (e.g. a sibling-to-sibling move under the same compound parent)\n // keep their invokes/timers running uninterrupted.\n runInvokes(event, entryPaths);\n scheduleAfterTransitions(entryPaths);\n };\n\n // ── Section: Invoke scheduling ─────────────────────────────────────────────\n\n const runInvokes = (triggerEvent: Ev | LifecycleEvent, paths: readonly string[]): void => {\n for (const path of paths) {\n const node = getNodeAtPath(states, path) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.invoke?.length) continue;\n\n for (const invokeDef of node.invoke) {\n const controller = new AbortController();\n const invokeId = invokeDef.id ?? String(++invokeCounter);\n const capturedContext = context_.value;\n const invokeInfo = { controller, event: triggerEvent, id: invokeId, path, state: state_.value };\n\n activeInvokes.add(invokeInfo);\n onDebug?.({\n context: capturedContext,\n event: triggerEvent,\n invokeId,\n state: state_.value,\n type: 'invoke-start',\n });\n\n void invokeDef\n .src({ context: capturedContext, entryEvent: triggerEvent, signal: controller.signal })\n .then((result) => {\n activeInvokes.delete(invokeInfo);\n\n if (disposed || controller.signal.aborted || !invokeDef.onDone) return;\n\n onDebug?.({\n context: capturedContext,\n event: triggerEvent,\n invokeId,\n result,\n state: state_.value,\n type: 'invoke-done',\n });\n\n queue.push({ event: invokeDef.onDone(result, capturedContext), tag: 'event' });\n\n if (!draining) drainQueue();\n })\n .catch((error: unknown) => {\n activeInvokes.delete(invokeInfo);\n\n if (disposed || controller.signal.aborted || !invokeDef.onError) return;\n\n onDebug?.({\n context: capturedContext,\n error,\n event: triggerEvent,\n invokeId,\n state: state_.value,\n type: 'invoke-error',\n });\n\n queue.push({ event: invokeDef.onError(error, capturedContext), tag: 'event' });\n\n if (!draining) drainQueue();\n });\n }\n }\n };\n\n // ── Section: After (delayed) transitions ───────────────────────────────────\n\n const scheduleAfterTransitions = (paths: readonly string[]): void => {\n for (const path of paths) {\n const node = getNodeAtPath(states, path) as StateNode<State, Ctx, Ev> | undefined;\n\n if (!node?.after?.length) continue;\n\n for (const afterDef of node.after) {\n const timer = setTimeout(() => {\n activeTimers.delete(entry);\n\n // A sibling-to-sibling transition elsewhere in the tree may have moved the leaf\n // while this timer's owning path stayed active — fire from wherever we are now,\n // and only if that path is still part of the live ancestor chain.\n if (disposed || !getAncestorPaths(state_.value).includes(path)) return;\n\n const from = state_.value;\n // R10: after.guard unified — receives { context, event } like all other guards\n const afterEvent = { delay: afterDef.delay, type: '$after' } as const;\n\n if (afterDef.guard && !afterDef.guard({ context: context_.value, event: afterEvent })) return;\n\n const resolvedTarget = resolveLeaf(states, afterDef.target) as State;\n\n queue.push({\n actions: (afterDef.actions ?? []) as Array<\n (args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void\n >,\n afterEvent,\n from,\n tag: 'after',\n target: resolvedTarget,\n });\n drainQueue();\n }, afterDef.delay);\n const entry = { path, timer };\n\n activeTimers.add(entry);\n }\n }\n };\n\n // ── Section: Event queue processing ─────────────────────────────────────────\n\n const processEvent = (item: QueueItem): boolean => {\n if (disposed) return false;\n\n if (item.tag === 'after') {\n executeTransition(item.from, item.target, item.actions, item.afterEvent);\n\n return true;\n }\n\n const from = state_.value;\n\n const transition = resolveTransition(\n definition,\n { context: context_.value, event: item.event, state: from },\n onDebug ? (info) => onDebug({ ...info, type: 'guard' }) : undefined,\n );\n\n if (!transition) {\n onDebug?.({ event: item.event, from, type: 'transition-skipped' });\n\n return false;\n }\n\n const resolvedTarget = resolveLeaf(states, transition.target) as State;\n const actions = (transition.actions ?? []) as Array<\n (args: { context: Ctx; readonly event: Ev | LifecycleEvent }) => void\n >;\n\n executeTransition(from, resolvedTarget, actions, item.event);\n\n return true;\n };\n\n const drainQueueInner = (): void => {\n let processed = 0;\n\n while (queue.length > 0) {\n if (++processed > maxTransitionsPerFlush) {\n throw new ClockworkTransitionLoopGuardError(\n 'transition queue exceeded maxTransitionsPerFlush',\n maxTransitionsPerFlush,\n );\n }\n\n processEvent(queue.shift()!);\n }\n };\n\n const drainQueue = (): void => {\n if (draining) return;\n\n draining = true;\n\n try {\n drainQueueInner();\n } catch (err) {\n queue.length = 0;\n throw err;\n } finally {\n draining = false;\n }\n };\n\n // ── Section: Public API ────────────────────────────────────────────────────\n\n const can = (event: Ev): boolean => {\n if (disposed) return false;\n\n return !!resolveTransition(definition, { context: context_.value, event, state: state_.value });\n };\n\n const send = (event: Ev): SendResult => {\n if (disposed) {\n warn('send() called on a disposed machine — event ignored');\n\n return RESULT_REJECTED;\n }\n\n // Run interceptors left-to-right — first null wins\n let intercepted: Ev | null = event;\n\n for (const fn of interceptors) {\n intercepted = fn(intercepted, { context: context_.value, state: state_.value });\n\n if (intercepted === null) return RESULT_REJECTED;\n }\n\n const interceptedEvent = intercepted;\n\n if (draining) {\n queue.push({ event: interceptedEvent, tag: 'event' });\n\n return RESULT_QUEUED;\n }\n\n draining = true;\n\n try {\n const transitioned = processEvent({ event: interceptedEvent, tag: 'event' });\n\n drainQueueInner();\n\n return transitioned ? RESULT_TRANSITIONED : RESULT_REJECTED;\n } catch (err) {\n queue.length = 0;\n throw err;\n } finally {\n draining = false;\n }\n };\n\n const getSnapshot = (): MachineSnapshot<State, Ctx> =>\n Object.freeze({ context: clone(context_.value), state: state_.value });\n\n const getTrace = (): readonly TransitionTraceEntry<State, Ev>[] => trace?.get() ?? [];\n\n const matches = (...stateArgs: string[]): boolean => {\n if (disposed) return false;\n\n const current = state_.value;\n\n return stateArgs.some((s) => current === s || current.startsWith(`${s}.`));\n };\n\n // R5: subscribe via plain Set — no ripple effect(), no prevState/prevCtx tracking\n const subscribe = (fn: (snapshot: MachineSnapshot<State, Ctx>) => void): (() => void) => {\n subscribers.add(fn);\n\n return () => subscribers.delete(fn);\n };\n\n // ── Section: Initialization ────────────────────────────────────────────────\n\n const initialPaths = getAncestorPaths(resolvedInitial);\n\n if (persistedSnapshot) {\n runInvokes(HYDRATE_EVENT, initialPaths);\n scheduleAfterTransitions(initialPaths);\n } else {\n const hasEntry = initialPaths.some((p) => getNodeAtPath(states, p)?.entry);\n\n if (hasEntry) {\n const initDraft = clone(context_.value);\n\n for (const p of initialPaths) {\n getNodeAtPath(states, p)?.entry?.({ context: initDraft, event: INIT_EVENT });\n }\n\n assertContext(initDraft, 'init');\n context_.value = initDraft;\n }\n\n runInvokes(INIT_EVENT, initialPaths);\n scheduleAfterTransitions(initialPaths);\n\n if (options.persistence) saveSnapshot();\n }\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n stopInvokes();\n clearTimers();\n state_.dispose();\n context_.dispose();\n };\n\n return {\n can,\n context: readonly(context_),\n get disposalSignal() {\n return disposeController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot,\n getTrace,\n matches,\n send,\n state: readonly(state_),\n subscribe,\n [Symbol.dispose]: dispose,\n };\n};\n\n// ── Public entry point ────────────────────────────────────────────────────────\n\n/**\n * Validates a machine configuration and returns a reusable definition handle.\n *\n * Call `.start(options?)` to create a running instance.\n * Call `.resolve(input, options?)` to inspect transitions without starting a machine.\n *\n * @example\n * const counterDef = createMachine({\n * context: { count: 0 },\n * initial: 'idle',\n * states: { idle: { on: { INC: { actions: [({ context }) => { context.count += 1 }], target: 'idle' } } } },\n * });\n *\n * const m1 = counterDef.start();\n * const m2 = counterDef.start({ snapshot: { context: { count: 10 }, state: 'idle' } });\n *\n * // Test transitions without a running machine:\n * counterDef.resolve({ context: { count: 0 }, event: { type: 'INC' }, state: 'idle' });\n *\n * // Or start immediately (one-shot):\n * const m3 = createMachine(config).start();\n */\nexport const createMachine = <State extends string, Ctx extends object, Ev extends MachineEvent>(\n config: MachineConfig<State, Ctx, Ev>,\n): MachineDefinition<State, Ctx, Ev> => {\n validateDefinition(config);\n\n return {\n resolve(input, options?) {\n return resolveTransition(config, input, options?.onGuard);\n },\n start(options?) {\n return _interpret(config, options);\n },\n };\n};\n"],"mappings":"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"}
|