@modular-react/journeys 0.1.0 → 1.0.1

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-DyU_PmaC.js","names":[],"sources":["../src/validation.ts","../src/runtime.ts"],"sourcesContent":["import type { ModuleDescriptor } from \"@modular-react/core\";\nimport type { AnyJourneyDefinition, RegisteredJourney } from \"./types.js\";\n\n/**\n * Aggregated error thrown when one or more registered journeys reference\n * module ids, entry names, or exit names that do not exist (or that\n * disagree on `allowBack`). Mirrors the style of core's\n * `validateDependencies` — accumulate all issues, throw once.\n */\nexport class JourneyValidationError extends Error {\n readonly issues: readonly string[];\n constructor(issues: readonly string[]) {\n super(`[@modular-react/journeys] Invalid journey registration:\\n - ${issues.join(\"\\n - \")}`);\n this.name = \"JourneyValidationError\";\n this.issues = issues;\n }\n}\n\nexport class JourneyHydrationError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(`[@modular-react/journeys] ${message}`, options);\n this.name = \"JourneyHydrationError\";\n }\n}\n\n/**\n * Thrown when `runtime.start()` / `runtime.hydrate()` is called with a\n * journey id that is not registered. Distinct class so shells can\n * discriminate \"this journey is gone after an upgrade, drop the tab\"\n * from transient or validation failures.\n */\nexport class UnknownJourneyError extends Error {\n readonly journeyId: string;\n constructor(journeyId: string, registered: readonly string[]) {\n super(\n `[@modular-react/journeys] Unknown journey id \"${journeyId}\". Registered: ${\n registered.join(\", \") || \"(none)\"\n }`,\n );\n this.name = \"UnknownJourneyError\";\n this.journeyId = journeyId;\n }\n}\n\nexport function validateJourneyContracts(\n journeys: readonly RegisteredJourney[],\n modules: readonly ModuleDescriptor<any, any, any, any>[],\n): void {\n const issues: string[] = [];\n const moduleById = new Map<string, ModuleDescriptor<any, any, any, any>>();\n for (const mod of modules) moduleById.set(mod.id, mod);\n\n // Guard against a module declaring an exit literally named `allowBack`.\n // Per-entry transitions on a journey use `allowBack: boolean` as a\n // control key and an exit of the same name would be silently skipped by\n // the per-exit iteration below. Fail loudly at registration time instead.\n for (const mod of modules) {\n if (mod.exitPoints && Object.prototype.hasOwnProperty.call(mod.exitPoints, \"allowBack\")) {\n issues.push(\n `module \"${mod.id}\" declares an exit named \"allowBack\", which collides with the reserved ` +\n `per-entry transition control key. Rename the exit (e.g. \"allowBackExit\").`,\n );\n }\n }\n\n const seenIds = new Set<string>();\n for (const reg of journeys) {\n const def = reg.definition;\n if (seenIds.has(def.id)) {\n issues.push(`journey \"${def.id}\" is registered more than once`);\n }\n seenIds.add(def.id);\n\n // Validate transitions map. The inner objects must be non-null — we\n // accept `AnyJourneyDefinition`, so a caller that sidesteps the typed\n // `defineJourney` helper can hand us `{ transitions: { foo: null } }`\n // or `{ bar: { baz: null } }`; we want those to become an accumulated\n // issue instead of a TypeError that short-circuits the loop.\n const transitions = (def.transitions ?? {}) as Record<string, unknown>;\n for (const [moduleId, perModule] of Object.entries(transitions)) {\n const mod = moduleById.get(moduleId);\n if (!mod) {\n issues.push(\n `journey \"${def.id}\" references unknown module id \"${moduleId}\" in transitions`,\n );\n continue;\n }\n if (!perModule || typeof perModule !== \"object\") {\n issues.push(\n `journey \"${def.id}\" has malformed transitions for module \"${moduleId}\" (expected an object)`,\n );\n continue;\n }\n for (const [entryName, perEntry] of Object.entries(perModule as Record<string, unknown>)) {\n const entry = mod.entryPoints?.[entryName];\n if (!entry) {\n issues.push(`journey \"${def.id}\" references unknown entry \"${moduleId}.${entryName}\"`);\n continue;\n }\n if (!perEntry || typeof perEntry !== \"object\") {\n issues.push(\n `journey \"${def.id}\" has malformed transitions for entry \"${moduleId}.${entryName}\" (expected an object)`,\n );\n continue;\n }\n const perEntryObj = perEntry as Record<string, unknown>;\n for (const exitName of Object.keys(perEntryObj)) {\n if (exitName === \"allowBack\") continue;\n if (!mod.exitPoints || !(exitName in mod.exitPoints)) {\n issues.push(\n `journey \"${def.id}\" references unknown exit \"${moduleId}.${entryName}.${exitName}\"`,\n );\n }\n }\n if (perEntryObj.allowBack === true) {\n const descriptorAllowBack = entry.allowBack;\n if (descriptorAllowBack !== \"preserve-state\" && descriptorAllowBack !== \"rollback\") {\n issues.push(\n `journey \"${def.id}\" sets allowBack on \"${moduleId}.${entryName}\" but the module entry does not declare allowBack`,\n );\n }\n }\n }\n }\n }\n\n if (issues.length > 0) throw new JourneyValidationError(issues);\n}\n\n/**\n * Shallow sanity check on a journey definition's own shape. Use this for\n * authoring ergonomics; structural contract checks live in\n * {@link validateJourneyContracts}.\n */\nexport function validateJourneyDefinition(def: AnyJourneyDefinition): readonly string[] {\n const issues: string[] = [];\n if (!def.id || typeof def.id !== \"string\") issues.push(\"journey is missing a string id\");\n if (!def.version || typeof def.version !== \"string\")\n issues.push(`journey \"${def.id ?? \"(unknown)\"}\" is missing a string version`);\n if (typeof def.initialState !== \"function\")\n issues.push(`journey \"${def.id}\" must declare initialState as a function`);\n if (typeof def.start !== \"function\")\n issues.push(`journey \"${def.id}\" must declare start as a function`);\n if (!def.transitions || typeof def.transitions !== \"object\")\n issues.push(`journey \"${def.id}\" must declare transitions`);\n return issues;\n}\n","import type { JourneyHandleRef, ModuleDescriptor } from \"@modular-react/core\";\nimport type {\n AnyJourneyDefinition,\n InstanceId,\n JourneyDefinition,\n JourneyDefinitionSummary,\n JourneyInstance,\n JourneyPersistence,\n JourneyRuntime,\n JourneyStatus,\n JourneyStep,\n ModuleTypeMap,\n RegisteredJourney,\n SerializedJourney,\n StepSpec,\n TransitionEvent,\n TransitionResult,\n} from \"./types.js\";\nimport { JourneyHydrationError, UnknownJourneyError } from \"./validation.js\";\n\nexport interface InstanceRecord<TState = unknown> {\n id: InstanceId;\n journeyId: string;\n status: JourneyStatus;\n step: JourneyStep | null;\n history: JourneyStep[];\n /** Snapshots captured per history entry — indexed alongside history. */\n rollbackSnapshots: (TState | undefined)[];\n /** True when any entry in `rollbackSnapshots` holds a real snapshot. */\n hasRollbackSnapshot: boolean;\n state: TState;\n terminalPayload: unknown;\n startedAt: string;\n updatedAt: string;\n /** Monotonically increasing token used to invalidate stale exit/goBack calls. */\n stepToken: number;\n /** Persistence key computed on start. Stable for the instance's lifetime. */\n persistenceKey: string | null;\n terminalFired: boolean;\n /** Total retries the outlet has consumed for this instance (across all steps). */\n retryCount: number;\n listeners: Set<() => void>;\n pendingSave: SerializedJourney<TState> | null;\n saveInFlight: boolean;\n /**\n * True when `removePersisted` fires while `saveInFlight` is still set:\n * the remove is deferred until the save settles so adapters that don't\n * serialize their own ops can't see remove→save reordering and leave\n * an orphaned blob in storage.\n */\n pendingRemove: boolean;\n /**\n * Monotonically incrementing revision bumped when an observable field\n * changes (status/step/state/history/terminalPayload). Used to memoize\n * the public `JourneyInstance` snapshot so that `getInstance(id)` returns\n * a stable reference between changes — a requirement of\n * `useSyncExternalStore`.\n */\n revision: number;\n /** Cached snapshot keyed by `revision`; rebuilt on the next read if stale. */\n cachedSnapshot: { revision: number; instance: JourneyInstance } | null;\n /** Cached exit/goBack closures keyed by stepToken. */\n cachedCallbacks: {\n stepToken: number;\n exit: (name: string, output?: unknown) => void;\n goBack: (() => void) | undefined;\n } | null;\n}\n\nexport interface JourneyRuntimeOptions {\n readonly debug?: boolean;\n /**\n * Module descriptors keyed by id — the runtime needs them to resolve\n * `allowBack` mode ('preserve-state' | 'rollback' | false) at goBack time.\n * When omitted, `goBack` falls back to 'preserve-state' for any journey\n * transition that opts in via `allowBack: true`.\n */\n readonly modules?: Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>;\n}\n\n/**\n * Module-private store of runtime internals. Keeps `__bindStepCallbacks`,\n * `__getRecord`, `__getRegistered`, and the bound module descriptor map off\n * the public `JourneyRuntime` surface (which would otherwise show up in\n * autocomplete, `Object.keys`, etc.). Access via {@link getInternals}.\n */\nconst INTERNALS = new WeakMap<JourneyRuntime, JourneyRuntimeInternals>();\n\n/**\n * Create a journey runtime bound to a set of registered journeys. The\n * registry integration assembles this once at resolve time; the runtime is\n * owned by the manifest and exposed as `manifest.journeys`.\n *\n * Passing an empty `registered` array yields a no-op runtime: every public\n * method is safe to call, and `start()` will throw \"unknown journey id\" —\n * matching the normal \"not registered\" failure mode and letting shells skip\n * null-guards on `manifest.journeys`.\n */\nexport function createJourneyRuntime(\n registered: readonly RegisteredJourney[],\n options: JourneyRuntimeOptions = {},\n): JourneyRuntime {\n const debug = options.debug ?? defaultDebug();\n const moduleMap = options.modules ?? {};\n const definitions = new Map<string, RegisteredJourney>();\n for (const entry of registered) definitions.set(entry.definition.id, entry);\n const instances = new Map<InstanceId, InstanceRecord>();\n // keyIndex is namespaced internally by journeyId so two journeys that\n // happen to return the same `keyFor` string do not alias onto the same\n // instance. The adapter sees only the user-defined portion; the prefix is\n // applied inside the runtime.\n const keyIndex = new Map<string, InstanceId>();\n\n function indexKey(journeyId: string, userKey: string): string {\n return `${journeyId}::${userKey}`;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function notify(record: InstanceRecord) {\n record.revision += 1;\n record.cachedSnapshot = null;\n for (const listener of record.listeners) {\n try {\n listener();\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] listener threw\", err);\n }\n }\n }\n\n function nowIso(): string {\n return new Date().toISOString();\n }\n\n function mintInstanceId(): InstanceId {\n try {\n const cryptoObj = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (cryptoObj?.randomUUID) return `ji_${cryptoObj.randomUUID()}`;\n } catch {\n // Fall through to the Math.random fallback.\n }\n const rand = Math.random().toString(36).slice(2, 10);\n return `ji_${Date.now().toString(36)}_${rand}`;\n }\n\n function summarize(reg: RegisteredJourney): JourneyDefinitionSummary {\n return {\n id: reg.definition.id,\n version: reg.definition.version,\n meta: reg.definition.meta,\n };\n }\n\n function assertKnown(journeyId: string): RegisteredJourney {\n const reg = definitions.get(journeyId);\n if (!reg) {\n throw new UnknownJourneyError(journeyId, [...definitions.keys()]);\n }\n return reg;\n }\n\n function stepFromSpec(spec: StepSpec<ModuleTypeMap>): JourneyStep {\n return { moduleId: spec.module, entry: spec.entry, input: spec.input };\n }\n\n function entryAllowBackMode(step: JourneyStep | null): \"preserve-state\" | \"rollback\" | false {\n if (!step) return false;\n const mod = moduleMap[step.moduleId];\n const entry = mod?.entryPoints?.[step.entry];\n const raw = entry?.allowBack;\n if (raw === \"rollback\" || raw === \"preserve-state\") return raw;\n return false;\n }\n\n function journeyAllowsBack(definition: AnyJourneyDefinition, step: JourneyStep | null): boolean {\n if (!step) return false;\n const perModule = (definition.transitions as Record<string, any> | undefined)?.[step.moduleId];\n const perEntry = perModule?.[step.entry];\n return perEntry?.allowBack === true;\n }\n\n function cloneSnapshot(state: unknown): unknown {\n if (state === null || typeof state !== \"object\") return state;\n const cloned: unknown = Array.isArray(state) ? [...state] : { ...(state as object) };\n // Dev-mode probe: freeze the snapshot so a transition that mutates\n // rolled-back state in place fails loudly instead of silently corrupting\n // the history. The freeze is shallow — deep mutation still slips through\n // (documented limitation L8), but catches the most common footgun.\n if (debug) {\n try {\n Object.freeze(cloned);\n } catch {\n // Some engines reject freezing exotic objects; swallow.\n }\n }\n return cloned;\n }\n\n function trimHistory(record: InstanceRecord, reg: RegisteredJourney) {\n const cap = reg.options?.maxHistory;\n // `undefined`, zero, and negative all mean \"unbounded\" — zero is treated\n // as the same escape hatch as a negative cap so a misconfigured `0`\n // cannot silently disable `goBack` by trimming history on every transition.\n if (cap === undefined || cap <= 0) return;\n while (record.history.length > cap) {\n record.history.shift();\n record.rollbackSnapshots.shift();\n }\n record.hasRollbackSnapshot = record.rollbackSnapshots.some((s) => s !== undefined);\n }\n\n // ---------------------------------------------------------------------------\n // Persistence save pipeline (§10.2)\n // ---------------------------------------------------------------------------\n\n function schedulePersist<TState>(\n record: InstanceRecord<TState>,\n persistence: JourneyPersistence<TState>,\n ) {\n const blob = serialize(record);\n if (record.saveInFlight) {\n record.pendingSave = blob;\n return;\n }\n void runSave(record, persistence, blob);\n }\n\n async function runSave<TState>(\n record: InstanceRecord<TState>,\n persistence: JourneyPersistence<TState>,\n blob: SerializedJourney<TState>,\n ) {\n record.saveInFlight = true;\n try {\n if (!record.persistenceKey) return;\n await persistence.save(record.persistenceKey, blob);\n } catch (err) {\n if (debug) {\n console.error(\n `[@modular-react/journeys] Failed to persist \"${record.journeyId}\" instance ${record.id}`,\n err,\n );\n }\n } finally {\n record.saveInFlight = false;\n // A terminal transition arrived while the save was in flight. The\n // remove was deferred to this point so adapters that do not serialize\n // their own ops don't see remove → save reordering. Skip the pending\n // save — its blob is about to be obsolete anyway.\n if (record.pendingRemove) {\n record.pendingRemove = false;\n record.pendingSave = null;\n if (record.persistenceKey) fireAndForgetRemove(persistence, record.persistenceKey);\n } else if (record.pendingSave) {\n const next = record.pendingSave;\n record.pendingSave = null;\n void runSave(record, persistence, next);\n }\n }\n }\n\n function removePersisted<TState>(\n record: InstanceRecord<TState>,\n persistence: JourneyPersistence<TState>,\n ) {\n if (!record.persistenceKey) return;\n record.pendingSave = null;\n const key = record.persistenceKey;\n keyIndex.delete(indexKey(record.journeyId, key));\n if (record.saveInFlight) {\n // Defer the remove until the save settles. `runSave`'s finally block\n // picks this up and fires the remove with the same key.\n record.pendingRemove = true;\n return;\n }\n fireAndForgetRemove(persistence, key);\n }\n\n /**\n * Delete a blob we've decided to discard (terminal, corrupt, unmigrateable)\n * without mutating any live instance record. Used from the `start()` paths\n * where we've probed the adapter and then chosen to mint a fresh instance.\n */\n function discardBlob<TState>(persistence: JourneyPersistence<TState>, key: string) {\n fireAndForgetRemove(persistence, key);\n }\n\n function fireAndForgetRemove<TState>(persistence: JourneyPersistence<TState>, key: string) {\n try {\n const maybe = persistence.remove(key);\n if (maybe && typeof (maybe as Promise<void>).catch === \"function\") {\n (maybe as Promise<void>).catch((err) => {\n if (debug) console.error(\"[@modular-react/journeys] persistence.remove rejected\", err);\n });\n }\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] persistence.remove threw\", err);\n }\n }\n\n function serialize<TState>(record: InstanceRecord<TState>): SerializedJourney<TState> {\n return {\n definitionId: record.journeyId,\n version: definitions.get(record.journeyId)!.definition.version,\n instanceId: record.id,\n status:\n record.status === \"loading\" ? \"active\" : (record.status as SerializedJourney[\"status\"]),\n step: record.step,\n history: [...record.history],\n // Preserve alignment with `history` — map `undefined` to `null` so the\n // shape survives JSON. Only emit when we actually hold snapshots.\n rollbackSnapshots: record.hasRollbackSnapshot\n ? record.rollbackSnapshots.map((s) => (s === undefined ? null : s))\n : undefined,\n terminalPayload:\n record.status === \"completed\" || record.status === \"aborted\"\n ? record.terminalPayload\n : undefined,\n state: record.state,\n startedAt: record.startedAt,\n updatedAt: record.updatedAt,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Hook firing\n // ---------------------------------------------------------------------------\n\n function fireOnTransition(\n reg: RegisteredJourney,\n record: InstanceRecord,\n from: JourneyStep | null,\n to: JourneyStep | null,\n exit: string | null,\n ) {\n const ev: TransitionEvent = {\n journeyId: record.journeyId,\n instanceId: record.id,\n from,\n to,\n exit,\n state: record.state,\n // Defensive copy — `record.history` is mutated in place on every\n // transition, and async consumers (analytics batchers, deferred\n // telemetry) would otherwise observe later mutations when they\n // finally inspect the event.\n history: [...record.history],\n };\n try {\n reg.definition.onTransition?.(ev);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onTransition (definition) threw\", err);\n }\n try {\n reg.options?.onTransition?.(ev);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onTransition (registration) threw\", err);\n }\n }\n\n function fireOnComplete(reg: RegisteredJourney, record: InstanceRecord, result: unknown) {\n if (record.terminalFired) return;\n record.terminalFired = true;\n const ctx = {\n journeyId: record.journeyId,\n instanceId: record.id,\n state: record.state,\n history: record.history,\n };\n try {\n reg.definition.onComplete?.(ctx, result);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onComplete (definition) threw\", err);\n }\n try {\n reg.options?.onComplete?.(ctx, result);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onComplete (registration) threw\", err);\n }\n }\n\n function fireOnAbort(reg: RegisteredJourney, record: InstanceRecord, reason: unknown) {\n if (record.terminalFired) return;\n record.terminalFired = true;\n const ctx = {\n journeyId: record.journeyId,\n instanceId: record.id,\n state: record.state,\n history: record.history,\n };\n try {\n reg.definition.onAbort?.(ctx, reason);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onAbort (definition) threw\", err);\n }\n try {\n reg.options?.onAbort?.(ctx, reason);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onAbort (registration) threw\", err);\n }\n }\n\n function fireOnError(\n reg: RegisteredJourney,\n record: InstanceRecord,\n err: unknown,\n step: JourneyStep | null,\n ) {\n try {\n reg.options?.onError?.(err, { step });\n } catch (hookErr) {\n if (debug) console.error(\"[@modular-react/journeys] onError (registration) threw\", hookErr);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Transition application\n // ---------------------------------------------------------------------------\n\n function applyTransition(\n record: InstanceRecord,\n reg: RegisteredJourney,\n result: TransitionResult<ModuleTypeMap, unknown>,\n exitName: string | null,\n ) {\n const previousStep = record.step;\n // Snapshot the *pre-transition* state (before any state update) — this\n // is what goBack should restore into the step we're about to leave.\n // \"state\" in result signals an explicit write, even if the new value is\n // `undefined` (legitimate for state types that allow it).\n const preState = record.state;\n if (\"state\" in result) {\n record.state = result.state;\n }\n\n if (\"next\" in result) {\n const nextStep = stepFromSpec(result.next);\n if (debug) {\n // Validation at resolveManifest() catches static misconfiguration,\n // but transition handlers branch at runtime and can return a\n // dynamically-built `next` that points at a module or entry that\n // isn't registered. The outlet would then render its generic\n // \"no entry on the registered modules\" message with no hint about\n // which transition was responsible. Warn here so the authoring loop\n // surfaces the source.\n if (Object.keys(moduleMap).length > 0) {\n const mod = moduleMap[nextStep.moduleId];\n if (!mod) {\n console.warn(\n `[@modular-react/journeys] Transition on \"${previousStep?.moduleId}.${previousStep?.entry}\" returned next.module=\"${nextStep.moduleId}\" which is not in the runtime's module map — the outlet will render a \"no entry\" error.`,\n );\n } else if (!mod.entryPoints?.[nextStep.entry]) {\n console.warn(\n `[@modular-react/journeys] Transition on \"${previousStep?.moduleId}.${previousStep?.entry}\" returned next.entry=\"${nextStep.moduleId}.${nextStep.entry}\" which is not a declared entry on that module.`,\n );\n }\n }\n }\n if (previousStep) {\n record.history.push(previousStep);\n // Clone the pre-state snapshot only when the step we're entering\n // opts in to rollback — avoids unnecessary work for preserve-state\n // / no-back entries. Shallow clone keeps the snapshot stable against\n // accidental top-level mutation.\n const nextMode = entryAllowBackModeForStep(nextStep);\n if (nextMode === \"rollback\") {\n record.rollbackSnapshots.push(cloneSnapshot(preState));\n record.hasRollbackSnapshot = true;\n } else {\n record.rollbackSnapshots.push(undefined);\n }\n }\n record.step = nextStep;\n record.status = \"active\";\n record.stepToken += 1;\n record.updatedAt = nowIso();\n record.cachedCallbacks = null;\n trimHistory(record, reg);\n fireOnTransition(reg, record, previousStep, nextStep, exitName);\n } else if (\"complete\" in result) {\n if (previousStep) {\n record.history.push(previousStep);\n record.rollbackSnapshots.push(undefined);\n }\n record.step = null;\n record.status = \"completed\";\n record.terminalPayload = result.complete;\n record.stepToken += 1;\n record.updatedAt = nowIso();\n record.cachedCallbacks = null;\n trimHistory(record, reg);\n fireOnTransition(reg, record, previousStep, null, exitName);\n fireOnComplete(reg, record, result.complete);\n } else if (\"abort\" in result) {\n if (previousStep) {\n record.history.push(previousStep);\n record.rollbackSnapshots.push(undefined);\n }\n record.step = null;\n record.status = \"aborted\";\n record.terminalPayload = result.abort;\n record.stepToken += 1;\n record.updatedAt = nowIso();\n record.cachedCallbacks = null;\n trimHistory(record, reg);\n fireOnTransition(reg, record, previousStep, null, exitName);\n fireOnAbort(reg, record, result.abort);\n }\n\n const persistence = reg.options?.persistence;\n if (persistence) {\n if (record.status === \"active\") schedulePersist(record, persistence);\n else removePersisted(record, persistence);\n }\n\n notify(record);\n }\n\n function entryAllowBackModeForStep(\n step: JourneyStep | null,\n ): \"preserve-state\" | \"rollback\" | false {\n return entryAllowBackMode(step);\n }\n\n function dispatchExit(\n record: InstanceRecord,\n reg: RegisteredJourney,\n stepToken: number,\n exitName: string,\n output: unknown,\n ) {\n if (record.status !== \"active\") {\n if (debug) {\n console.warn(\n `[@modular-react/journeys] Exit(\"${exitName}\") dropped on instance ${record.id} — status=${record.status}. ` +\n `(This is the expected no-op when an exit fires before the initial async load settles; ` +\n `await the load or subscribe for status changes before dispatching.)`,\n );\n }\n return;\n }\n if (record.stepToken !== stepToken) {\n if (debug) {\n console.warn(\n `[@modular-react/journeys] Stale exit(\"${exitName}\") dropped on instance ${record.id}`,\n );\n }\n return;\n }\n const step = record.step;\n if (!step) return;\n const perModule = (reg.definition.transitions as Record<string, any> | undefined)?.[\n step.moduleId\n ];\n const perEntry = perModule?.[step.entry];\n const handler = perEntry?.[exitName] as\n | ((ctx: {\n state: unknown;\n input: unknown;\n output: unknown;\n }) => TransitionResult<ModuleTypeMap, unknown>)\n | undefined;\n if (typeof handler !== \"function\") {\n if (debug) {\n console.warn(\n `[@modular-react/journeys] No transition for exit(\"${exitName}\") on ${step.moduleId}.${step.entry} — ignoring.`,\n );\n }\n return;\n }\n let result: TransitionResult<ModuleTypeMap, unknown>;\n try {\n result = handler({ state: record.state, input: step.input, output });\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] transition handler threw\", err);\n fireOnError(reg, record, err, step);\n applyTransition(\n record,\n reg,\n { abort: { reason: \"transition-error\", exit: exitName, error: err } },\n exitName,\n );\n return;\n }\n // Transitions must be pure and synchronous. A handler that returns a\n // thenable almost certainly forgot to put the async work inside a loading\n // entry point — applying the thenable as the transition result would be\n // a silent no-op (it is not `{ next | complete | abort }`), so warn and\n // treat it as an abort.\n if (result && typeof (result as { then?: unknown }).then === \"function\") {\n if (debug) {\n console.error(\n `[@modular-react/journeys] Transition handler for ${step.moduleId}.${step.entry}.\"${exitName}\" returned a Promise. Transitions must be synchronous and pure — put async work inside a loading entry point on a module.`,\n );\n }\n applyTransition(\n record,\n reg,\n { abort: { reason: \"transition-returned-promise\", exit: exitName } },\n exitName,\n );\n return;\n }\n applyTransition(record, reg, result, exitName);\n }\n\n function dispatchGoBack(record: InstanceRecord, reg: RegisteredJourney, stepToken: number) {\n if (record.status !== \"active\") return;\n if (record.stepToken !== stepToken) return;\n if (record.history.length === 0) return;\n\n const step = record.step;\n if (!step) return;\n // Journey-side opt-in\n if (!journeyAllowsBack(reg.definition, step)) return;\n\n const previousStep = record.history.pop()!;\n const snapshot = record.rollbackSnapshots.pop();\n const mode = entryAllowBackMode(step);\n if (mode === \"rollback\" && snapshot !== undefined) {\n record.state = snapshot;\n }\n record.hasRollbackSnapshot = record.rollbackSnapshots.some((s) => s !== undefined);\n record.step = previousStep;\n record.stepToken += 1;\n record.updatedAt = nowIso();\n record.cachedCallbacks = null;\n fireOnTransition(reg, record, step, previousStep, null);\n const persistence = reg.options?.persistence;\n if (persistence) schedulePersist(record, persistence);\n notify(record);\n }\n\n function bindStepCallbacks(record: InstanceRecord, reg: RegisteredJourney) {\n if (record.cachedCallbacks && record.cachedCallbacks.stepToken === record.stepToken) {\n return record.cachedCallbacks;\n }\n const token = record.stepToken;\n const exit = (name: string, output?: unknown) => {\n dispatchExit(record, reg, token, name, output);\n };\n let mode = entryAllowBackMode(record.step);\n // Documented fallback (see `JourneyRuntimeOptions.modules`): when the\n // runtime is built without a module descriptor for this step but the\n // journey's transition opts in via `allowBack: true`, treat the mode as\n // 'preserve-state' so `goBack` stays wired. Without this fallback the\n // headless simulator (which never passes a moduleMap) and any runtime\n // created without module descriptors would see `goBack` silently\n // disappear, contradicting the documented behavior.\n if (\n mode === false &&\n record.step &&\n journeyAllowsBack(reg.definition, record.step) &&\n !moduleMap[record.step.moduleId]\n ) {\n mode = \"preserve-state\";\n }\n const canGoBack =\n mode !== false && journeyAllowsBack(reg.definition, record.step) && record.history.length > 0;\n const goBack = canGoBack\n ? () => {\n dispatchGoBack(record, reg, token);\n }\n : undefined;\n record.cachedCallbacks = { stepToken: token, exit, goBack };\n return record.cachedCallbacks;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n function buildInstance(record: InstanceRecord): JourneyInstance {\n if (record.cachedSnapshot && record.cachedSnapshot.revision === record.revision) {\n return record.cachedSnapshot.instance;\n }\n // Copy-on-build: history is mutated in place on every transition, so\n // consumers that diff against a prior `instance.history` reference\n // (React deps, effect closures, useMemo) need a frozen-per-revision\n // snapshot. Cheap — bounded by the history cap, and only rebuilt when\n // the revision bumps.\n const historySnapshot: readonly JourneyStep[] = [...record.history];\n const instance: JourneyInstance = {\n id: record.id,\n journeyId: record.journeyId,\n status: record.status,\n step: record.step,\n history: historySnapshot,\n state: record.state,\n terminalPayload:\n record.status === \"completed\" || record.status === \"aborted\"\n ? record.terminalPayload\n : undefined,\n startedAt: record.startedAt,\n updatedAt: record.updatedAt,\n serialize: () => serialize(record),\n };\n record.cachedSnapshot = { revision: record.revision, instance };\n return instance;\n }\n\n function createRecord(\n reg: RegisteredJourney,\n instanceId: InstanceId,\n persistenceKey: string | null,\n initialState: unknown,\n ): InstanceRecord {\n const startedAt = nowIso();\n return {\n id: instanceId,\n journeyId: reg.definition.id,\n status: \"loading\",\n step: null,\n history: [],\n rollbackSnapshots: [],\n hasRollbackSnapshot: false,\n state: initialState,\n terminalPayload: undefined,\n startedAt,\n updatedAt: startedAt,\n stepToken: 0,\n persistenceKey,\n terminalFired: false,\n retryCount: 0,\n listeners: new Set(),\n pendingSave: null,\n saveInFlight: false,\n pendingRemove: false,\n revision: 0,\n cachedSnapshot: null,\n cachedCallbacks: null,\n };\n }\n\n function startFresh(\n reg: RegisteredJourney,\n input: unknown,\n existingRecord?: InstanceRecord,\n ): InstanceId {\n const def = reg.definition as JourneyDefinition<any, any, unknown>;\n const record =\n existingRecord ?? createRecord(reg, mintInstanceId(), null, def.initialState(input));\n if (!existingRecord) {\n instances.set(record.id, record);\n } else {\n // Recycling a record — typically because an async probe failed or a\n // partial hydrate threw. Reset every field that could carry stale\n // state from the record's previous life. Without this, a hydrate that\n // populated `history` / `rollbackSnapshots` before throwing would\n // leak those entries into the \"fresh\" instance.\n record.state = def.initialState(input);\n record.history = [];\n record.rollbackSnapshots = [];\n record.hasRollbackSnapshot = false;\n }\n const startStep = stepFromSpec(def.start(record.state, input));\n record.step = startStep;\n record.status = \"active\";\n record.stepToken += 1;\n record.terminalFired = false;\n record.terminalPayload = undefined;\n // A recycled record can carry a retry count from its previous life (an\n // async-load failure that fell through to `startFresh`, for example).\n // The new run is a fresh journey — reset the budget.\n record.retryCount = 0;\n record.updatedAt = nowIso();\n record.cachedCallbacks = null;\n fireOnTransition(reg, record, null, startStep, null);\n const persistence = reg.options?.persistence;\n if (persistence) schedulePersist(record, persistence);\n notify(record);\n return record.id;\n }\n\n function hydrateInto(record: InstanceRecord, blob: SerializedJourney<unknown>) {\n const historyLen = blob.history.length;\n // Align rollbackSnapshots with history — mismatched lengths corrupt\n // `goBack` (pop() would take the wrong pair). Reject upfront instead of\n // silently misbehaving later.\n if (blob.rollbackSnapshots && blob.rollbackSnapshots.length !== historyLen) {\n throw new JourneyHydrationError(\n `Blob for journey \"${record.journeyId}\" has rollbackSnapshots.length=${blob.rollbackSnapshots.length} but history.length=${historyLen}. Fix the persisted blob (pad rollbackSnapshots with null for non-rollback entries) or provide onHydrate to migrate.`,\n );\n }\n record.state = blob.state;\n record.step = blob.step;\n record.history = [...blob.history];\n if (blob.rollbackSnapshots) {\n record.rollbackSnapshots = blob.rollbackSnapshots.map((s) =>\n s === null ? undefined : s,\n ) as (unknown | undefined)[];\n record.hasRollbackSnapshot = record.rollbackSnapshots.some((s) => s !== undefined);\n } else {\n // Legacy blobs without rollbackSnapshots — treat as if every history\n // entry had no snapshot. Keeps the two arrays length-aligned.\n record.rollbackSnapshots = Array.from({ length: historyLen }, () => undefined);\n record.hasRollbackSnapshot = false;\n }\n record.status = blob.status;\n record.terminalPayload = blob.terminalPayload;\n record.startedAt = blob.startedAt;\n record.updatedAt = blob.updatedAt;\n record.stepToken += 1;\n record.terminalFired = blob.status !== \"active\";\n record.cachedCallbacks = null;\n }\n\n function probeLoad(\n reg: RegisteredJourney,\n persistence: JourneyPersistence<unknown>,\n key: string,\n ): SerializedJourney<unknown> | null | Promise<SerializedJourney<unknown> | null> {\n let loaded: SerializedJourney<unknown> | null | Promise<SerializedJourney<unknown> | null>;\n try {\n loaded = persistence.load(key) as\n | SerializedJourney<unknown>\n | null\n | Promise<SerializedJourney<unknown> | null>;\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] persistence.load threw\", err);\n return null;\n }\n if (loaded && typeof (loaded as Promise<unknown>).then === \"function\") {\n return loaded as Promise<SerializedJourney<unknown> | null>;\n }\n return loaded as SerializedJourney<unknown> | null;\n }\n\n type MigrateResult =\n | { ok: true; blob: SerializedJourney<unknown> }\n | { ok: false; reason: \"version-mismatch\" }\n | { ok: false; reason: \"on-hydrate-threw\"; cause: unknown };\n\n function migrateBlob(reg: RegisteredJourney, blob: SerializedJourney<unknown>): MigrateResult {\n let migrated: SerializedJourney<unknown> = blob;\n let ranAny = false;\n if (reg.definition.onHydrate) {\n ranAny = true;\n try {\n migrated = reg.definition.onHydrate(migrated) as SerializedJourney<unknown>;\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onHydrate (definition) threw\", err);\n return { ok: false, reason: \"on-hydrate-threw\", cause: err };\n }\n }\n // Registration-level `onHydrate` runs after the definition's — shells can\n // layer environment-specific post-migration tweaks (redaction, id\n // rewriting) without touching journey authoring code.\n const regHydrate = reg.options?.onHydrate as\n | ((b: SerializedJourney<unknown>) => SerializedJourney<unknown>)\n | undefined;\n if (regHydrate) {\n ranAny = true;\n try {\n migrated = regHydrate(migrated);\n } catch (err) {\n if (debug) console.error(\"[@modular-react/journeys] onHydrate (registration) threw\", err);\n return { ok: false, reason: \"on-hydrate-threw\", cause: err };\n }\n }\n if (ranAny) {\n return { ok: true, blob: migrated };\n }\n if (blob.version !== reg.definition.version) {\n return { ok: false, reason: \"version-mismatch\" };\n }\n return { ok: true, blob };\n }\n\n // ---------------------------------------------------------------------------\n // Runtime surface\n // ---------------------------------------------------------------------------\n\n const runtime: JourneyRuntime = {\n start<TInput>(\n journeyIdOrHandle: string | JourneyHandleRef<string, TInput>,\n ...rest: [input?: TInput]\n ): InstanceId {\n const input = (rest.length > 0 ? rest[0] : undefined) as TInput;\n // Accept either a bare id or a `JourneyHandle`-shaped object. The\n // handle form is the `start<TId, TInput>(handle, input)` overload; it\n // only exists to type-check `input` — the runtime behaviour is\n // identical either way.\n const journeyId =\n typeof journeyIdOrHandle === \"string\" ? journeyIdOrHandle : journeyIdOrHandle.id;\n const reg = assertKnown(journeyId);\n const persistence = reg.options?.persistence;\n\n if (persistence) {\n const key = persistence.keyFor({\n journeyId: reg.definition.id,\n input,\n });\n const indexed = indexKey(reg.definition.id, key);\n // Idempotency: return the existing instance for this key whenever it\n // is still in flight — \"active\" OR \"loading\". Returning a fresh id\n // while a load is pending would orphan the loading instance and\n // trigger a second `load()`.\n const existingId = keyIndex.get(indexed);\n const existing = existingId ? instances.get(existingId) : null;\n if (existing && (existing.status === \"active\" || existing.status === \"loading\")) {\n return existing.id;\n }\n\n const def = reg.definition as JourneyDefinition<any, any, unknown>;\n const loaded = probeLoad(reg, persistence as JourneyPersistence<unknown>, key);\n\n if (loaded && typeof (loaded as Promise<unknown>).then === \"function\") {\n // Async probe — mint a placeholder instance in `loading` status,\n // but initialize `state` from `initialState(input)` immediately so\n // consumers reading state during loading never see `undefined`.\n // If the blob later hydrates, state is overwritten.\n const instanceId = mintInstanceId();\n const record = createRecord(reg, instanceId, key, def.initialState(input));\n instances.set(instanceId, record);\n keyIndex.set(indexed, instanceId);\n notify(record);\n\n void (loaded as Promise<SerializedJourney<unknown> | null>).then(\n (blob) => {\n // The caller may have ended the instance before the load\n // settled (tab closed, navigation, explicit `runtime.end`).\n // In that case the record is already terminal and we must\n // not resurrect it with startFresh or a hydrate.\n if (record.status !== \"loading\") return;\n if (!blob || blob.status !== \"active\") {\n // Discard terminal/missing blob and mint a fresh instance\n // under the same key. A terminal blob left in storage would\n // be re-fetched on every subsequent start().\n if (blob) discardBlob(persistence as JourneyPersistence<unknown>, key);\n startFresh(reg, input, record);\n return;\n }\n const migrated = migrateBlob(reg, blob);\n if (!migrated.ok) {\n discardBlob(persistence as JourneyPersistence<unknown>, key);\n startFresh(reg, input, record);\n return;\n }\n try {\n hydrateInto(record, migrated.blob);\n } catch (err) {\n if (debug)\n console.error(\"[@modular-react/journeys] hydrate after async load failed\", err);\n discardBlob(persistence as JourneyPersistence<unknown>, key);\n startFresh(reg, input, record);\n return;\n }\n notify(record);\n },\n (err) => {\n if (debug) console.error(\"[@modular-react/journeys] persistence.load rejected\", err);\n if (record.status !== \"loading\") return;\n startFresh(reg, input, record);\n },\n );\n return instanceId;\n }\n\n const blob = loaded as SerializedJourney<unknown> | null;\n if (blob && blob.status === \"active\") {\n const migrated = migrateBlob(reg, blob);\n if (migrated.ok) {\n // Guard against a blob whose recorded id collides with a live\n // instance (corrupted / hand-edited blob, or two journeys sharing\n // a persistence keyspace). Mint a fresh id instead of clobbering\n // the existing entry — matches `hydrate()`'s existing rejection\n // of re-hydrate over an existing id.\n const instanceId =\n migrated.blob.instanceId && !instances.has(migrated.blob.instanceId)\n ? migrated.blob.instanceId\n : mintInstanceId();\n const record = createRecord(reg, instanceId, key, def.initialState(input));\n instances.set(instanceId, record);\n keyIndex.set(indexed, instanceId);\n try {\n hydrateInto(record, migrated.blob);\n } catch (err) {\n if (debug)\n console.error(\"[@modular-react/journeys] hydrate during start failed\", err);\n // Cleanup the half-built record and fall through to startFresh\n // under the same key.\n instances.delete(instanceId);\n keyIndex.delete(indexed);\n discardBlob(persistence as JourneyPersistence<unknown>, key);\n const freshId = mintInstanceId();\n const freshRecord = createRecord(reg, freshId, key, def.initialState(input));\n instances.set(freshId, freshRecord);\n keyIndex.set(indexed, freshId);\n return startFresh(reg, input, freshRecord);\n }\n notify(record);\n return instanceId;\n }\n // Migration failed: discard the stale blob so it doesn't get\n // re-fetched forever.\n discardBlob(persistence as JourneyPersistence<unknown>, key);\n } else if (blob) {\n // Terminal blob — drop it before reusing the key for a fresh run.\n discardBlob(persistence as JourneyPersistence<unknown>, key);\n }\n\n // No blob / terminal blob / migration failed — mint a fresh instance\n // that still owns the key, so subsequent `start()` calls are\n // idempotent.\n const instanceId = mintInstanceId();\n const record = createRecord(reg, instanceId, key, def.initialState(input));\n instances.set(instanceId, record);\n keyIndex.set(indexed, instanceId);\n return startFresh(reg, input, record);\n }\n\n return startFresh(reg, input);\n },\n\n hydrate<TState>(journeyId: string, blob: SerializedJourney<TState>): InstanceId {\n const reg = assertKnown(journeyId);\n const migrated = migrateBlob(reg, blob as SerializedJourney<unknown>);\n if (!migrated.ok) {\n if (migrated.reason === \"on-hydrate-threw\") {\n // Surface the original throw via `.cause` so callers can\n // distinguish a migrator bug from a true version mismatch and\n // log the underlying error without losing the stack.\n throw new JourneyHydrationError(\n `onHydrate threw while migrating blob for \"${journeyId}\" (blob=${blob.version} def=${reg.definition.version}).`,\n { cause: migrated.cause },\n );\n }\n throw new JourneyHydrationError(\n `Hydrate version mismatch for \"${journeyId}\": blob=${blob.version} def=${reg.definition.version}. Provide onHydrate to migrate.`,\n );\n }\n const instanceId = migrated.blob.instanceId || mintInstanceId();\n // Guard against silent overwrite — two hydrates of the same blob would\n // otherwise clobber live state and orphan existing listeners.\n if (instances.has(instanceId)) {\n throw new JourneyHydrationError(\n `Cannot hydrate journey \"${journeyId}\" with instance id \"${instanceId}\" — an instance with the same id is already in memory. Call forget(id) first if you intend to replace it.`,\n );\n }\n\n // If the migrated blob has an input we can use to compute the key, we\n // could re-index — but `SerializedJourney` doesn't carry the original\n // `input`, so explicit hydrate stays persistence-unlinked. Callers\n // that want round-trip persistence should use `start()` which owns the\n // key lifecycle. Document this on the API.\n const record = createRecord(reg, instanceId, null, migrated.blob.state);\n instances.set(instanceId, record);\n try {\n hydrateInto(record, migrated.blob);\n } catch (err) {\n // Don't leak the half-built loading placeholder — otherwise\n // subsequent getInstance(id) returns partial state, a retry hits\n // the \"already in memory\" guard, and forget(id) is a no-op\n // (status never reached terminal). Mirror the sync-start path\n // which already cleans up in the same situation.\n instances.delete(instanceId);\n throw err;\n }\n notify(record);\n return instanceId;\n },\n\n getInstance(id) {\n const record = instances.get(id);\n return record ? buildInstance(record) : null;\n },\n\n listInstances() {\n return [...instances.keys()];\n },\n\n listDefinitions() {\n return [...definitions.values()].map(summarize);\n },\n\n isRegistered(journeyId) {\n return definitions.has(journeyId);\n },\n\n subscribe(id, listener) {\n const record = instances.get(id);\n if (!record) return () => {};\n record.listeners.add(listener);\n return () => {\n record.listeners.delete(listener);\n };\n },\n\n end(id, reason) {\n const record = instances.get(id);\n if (!record) return;\n if (record.status === \"completed\" || record.status === \"aborted\") return;\n const reg = definitions.get(record.journeyId);\n if (!reg) return;\n // An outlet that unmounts mid-load should still be able to tear the\n // placeholder instance down. The journey never \"started\" as far as the\n // author is concerned, so skip `onAbandon` (it would see a null step)\n // and transition straight to `aborted` with the supplied reason.\n if (record.status === \"loading\") {\n applyTransition(record, reg, { abort: { reason: reason ?? \"abandoned\" } }, null);\n return;\n }\n const defaultAbort: TransitionResult<ModuleTypeMap, unknown> = {\n abort: { reason: reason ?? \"abandoned\" },\n };\n let result: TransitionResult<ModuleTypeMap, unknown> = defaultAbort;\n // Registration-level `onAbandon` overrides the definition's — shells\n // can swap the abandon outcome without modifying journey authoring code\n // (e.g. complete instead of abort on tab close). If absent, fall back\n // to the definition's handler.\n const abandonHandler = reg.options?.onAbandon ?? reg.definition.onAbandon;\n if (abandonHandler) {\n try {\n result = abandonHandler({\n journeyId: record.journeyId,\n instanceId: record.id,\n step: record.step,\n state: record.state,\n reason: reason ?? \"abandoned\",\n }) as TransitionResult<ModuleTypeMap, unknown>;\n } catch (err) {\n // Surface the handler crash through the registration-level onError\n // hook before falling back to the default abort. Preserve the\n // caller-supplied `reason` (and surface `onAbandon`'s own error as\n // `cause`) so a throw in a shell's onAbandon doesn't silently\n // erase the original abort context.\n if (debug) console.error(\"[@modular-react/journeys] onAbandon threw\", err);\n fireOnError(reg, record, err, record.step);\n result = {\n abort: {\n reason: reason ?? \"abandoned\",\n cause: \"onAbandon-threw\",\n error: err,\n },\n };\n }\n }\n applyTransition(record, reg, result, null);\n },\n\n forget(id) {\n const record = instances.get(id);\n if (!record) return;\n if (record.status !== \"completed\" && record.status !== \"aborted\") return;\n if (record.persistenceKey) keyIndex.delete(indexKey(record.journeyId, record.persistenceKey));\n record.listeners.clear();\n instances.delete(id);\n },\n\n forgetTerminal() {\n let removed = 0;\n for (const [id, record] of instances) {\n if (record.status === \"completed\" || record.status === \"aborted\") {\n if (record.persistenceKey) {\n keyIndex.delete(indexKey(record.journeyId, record.persistenceKey));\n }\n record.listeners.clear();\n instances.delete(id);\n removed += 1;\n }\n }\n return removed;\n },\n };\n\n function dispatchComponentError(id: InstanceId, err: unknown, step: JourneyStep): void {\n const record = instances.get(id);\n if (!record) return;\n const reg = definitions.get(record.journeyId);\n if (!reg) return;\n fireOnError(reg, record, err, step);\n }\n\n // Internals used by the outlet and testing helpers — kept on a WeakMap\n // rather than on the runtime object to keep the public surface clean.\n const internals: JourneyRuntimeInternals = {\n __bindStepCallbacks: bindStepCallbacks,\n __getRecord: (id: InstanceId) => instances.get(id),\n __getRegistered: (id: string) => definitions.get(id),\n __moduleMap: moduleMap,\n __debug: debug,\n __fireComponentError: dispatchComponentError,\n };\n INTERNALS.set(runtime, internals);\n\n return runtime;\n}\n\nfunction defaultDebug(): boolean {\n const g = globalThis as { process?: { env?: { NODE_ENV?: string } } };\n return !!g.process && g.process.env?.NODE_ENV !== \"production\";\n}\n\nexport interface JourneyRuntimeInternals {\n __bindStepCallbacks(\n record: InstanceRecord,\n reg: RegisteredJourney,\n ): {\n exit: (name: string, output?: unknown) => void;\n goBack?: () => void;\n stepToken: number;\n };\n __getRecord(id: InstanceId): InstanceRecord | undefined;\n __getRegistered(id: string): RegisteredJourney | undefined;\n /** Module descriptors bound to this runtime — the `<JourneyOutlet>` reads\n * this to resolve step components without the caller threading `modules`\n * through as a prop. */\n __moduleMap: Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>;\n /** Runtime's resolved debug flag — useful for dev-mode probes in the\n * outlet / module-tab. */\n __debug: boolean;\n /**\n * Fires the registration-level `onError` hook for a component-level throw\n * caught by the outlet's error boundary. Routed through the runtime so\n * the outlet never has to reach into `reg.options.onError` directly —\n * keeps the runtime the single owner of hook firing.\n */\n __fireComponentError(id: InstanceId, err: unknown, step: JourneyStep): void;\n}\n\nexport function getInternals(runtime: JourneyRuntime): JourneyRuntimeInternals {\n const internals = INTERNALS.get(runtime);\n if (!internals) {\n throw new Error(\n \"[@modular-react/journeys] getInternals() called on a runtime that was not produced by createJourneyRuntime().\",\n );\n }\n return internals;\n}\n"],"mappings":"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"}