@modular-vue/journeys 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.d.ts +36 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -10
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ npm install @modular-vue/journeys
|
|
|
33
33
|
starts it on mount, renders its step, ends + forgets the instance on unmount.
|
|
34
34
|
Outlet props pass through as attrs, in either spelling (`:on-finished` and
|
|
35
35
|
`:onFinished` both reach the outlet); the default scoped slot receives
|
|
36
|
-
`{ instanceId, instance, stepIndex, outlet }` for chrome.
|
|
36
|
+
`{ instanceId, instance, runtime, stepIndex, outlet }` for chrome. `outlet` is
|
|
37
|
+
a functional component — render it with `<component :is="outlet" />`, or spell
|
|
38
|
+
it yourself as `<JourneyOutlet :instance-id="instanceId" :runtime="runtime" />`.
|
|
37
39
|
- **`useJourneyHost(handle, input, options?)`** — the lifecycle without the
|
|
38
40
|
rendering. Returns `{ instanceId, instance, stepIndex }` as refs, plus the
|
|
39
41
|
plain `runtime` it resolved at setup — the one `instanceId` is valid on.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { App, Component, ComputedRef, InjectionKey, MaybeRefOrGetter, PropType, ShallowRef, VNode } from "vue";
|
|
1
|
+
import { App, Component, ComputedRef, FunctionalComponent, InjectionKey, MaybeRefOrGetter, PropType, ShallowRef, VNode } from "vue";
|
|
2
2
|
import { ModuleExitEvent, ModuleExitHandler, VueAppProvidingPlugin } from "@modular-vue/vue";
|
|
3
3
|
import { AbandonCtx, AnnotatedTransitionHandler, AnyJourneyDefinition, ChildOutcome, EntryExitWildcardMap, EntryInputOf, EntryNamesOf, EntryTransitions, ExitCtx, ExitNamesOf, ExitNamesPairedWithEntry, ExitOnlyWildcardMap, ExitOutputOf, InstanceId, InstanceId as InstanceId$1, InvokeSpec, JourneyDefinition, JourneyDefinition as JourneyDefinition$1, JourneyDefinitionSummary, JourneyHandle, JourneyHandle as JourneyHandle$1, JourneyHydrationError, JourneyInstance, JourneyInstance as JourneyInstance$1, JourneyNavContribution, JourneyNavContribution as JourneyNavContribution$1, JourneyPersistence, JourneyRegisterOptions, JourneyRegisterOptions as JourneyRegisterOptions$1, JourneyRuntime, JourneyRuntime as JourneyRuntime$1, JourneyRuntimeOptions, JourneyStatus, JourneyStep, JourneyStep as JourneyStep$1, JourneyStepFor, JourneySync, JourneySyncAction, JourneySyncCallbackCtx, JourneySyncOptions, JourneySyncOptions as JourneySyncOptions$1, JourneySyncPort, JourneySyncPort as JourneySyncPort$1, JourneySystemAbortReason, JourneySystemAbortReasonCode, JourneyValidationError, MaybePromise, MemoryPersistence, MemoryPersistenceOptions, ModuleTypeMap, ParentLink, PendingInvoke, RegisteredJourney, ResumeBounceCounter, ResumeHandler, ResumeMap, SelectModuleCases, SelectModuleCasesPartial, SerializedJourney, StepRef, StepSpec, SyncJourneyPersistence, SyncJourneyPersistence as SyncJourneyPersistence$1, TerminalCtx, TerminalOutcome, TerminalOutcome as TerminalOutcome$1, TerminalSentinel, TransitionEvent, TransitionMap, TransitionResult, UnknownJourneyError, WebStoragePersistenceOptions, WildcardEntryInputOf, WildcardEntryNamesOf, WildcardExitNamesOf, WildcardExitOutputForEntry, WildcardExitOutputOf, WildcardTransitionMap, createJourneyRuntime, createJourneySync, createMemoryJourneySyncPort, createMemoryPersistence, createWebStoragePersistence, defaultStepPath, defineJourney, defineJourneyHandle, defineJourneyPersistence, defineTransition, invoke, isAnnotatedTransition, isTerminalSentinel, journeyStepPath, resolveJourneySyncAction, selectModule, selectModuleOrDefault, validateJourneyContracts, validateJourneyDefinition, validateJourneyGraph } from "@modular-frontend/journeys-engine";
|
|
4
4
|
import { ExitFn, ExitPointMap, ExitPointSchema, JourneyRuntime as JourneyRuntime$2, ModuleDescriptor, ModuleTypeMap as ModuleTypeMap$1, NavigationItemBase, RegistryPlugin, RuntimeMountAdapter, SemverParseError, UiComponent, compareVersions, isJourneySystemAbort, satisfies } from "@modular-frontend/core";
|
|
@@ -502,10 +502,27 @@ interface JourneyHostSlotProps {
|
|
|
502
502
|
/** See {@link JourneyHostState.stepIndex}. */
|
|
503
503
|
readonly stepIndex: number;
|
|
504
504
|
/**
|
|
505
|
-
* The
|
|
506
|
-
*
|
|
505
|
+
* The runtime the host started the instance on — the same value
|
|
506
|
+
* {@link JourneyHostState.runtime} exposes. Pass it to a hand-placed
|
|
507
|
+
* `<JourneyOutlet :instance-id="instanceId" :runtime="runtime" />` when you'd
|
|
508
|
+
* rather render the outlet yourself than use {@link JourneyHostSlotProps.outlet}
|
|
509
|
+
* (the idiomatic-Vue path), so the outlet resolves the *same* runtime the host
|
|
510
|
+
* pinned rather than whatever a nearer provider hands out.
|
|
507
511
|
*/
|
|
508
|
-
readonly
|
|
512
|
+
readonly runtime: JourneyRuntime$1;
|
|
513
|
+
/**
|
|
514
|
+
* The `<JourneyOutlet>` for this instance as a **functional component**,
|
|
515
|
+
* already bound to the host's pinned runtime and every outlet attribute passed
|
|
516
|
+
* to `<JourneyHost>`. Render it with `<component :is="outlet" />` in a template
|
|
517
|
+
* (or `h(outlet)` in a render function) — it is a component, not a raw VNode,
|
|
518
|
+
* so `:is` mounts it cleanly. Its identity is stable across host re-renders, so
|
|
519
|
+
* `:is` patches the outlet in place instead of remounting it on every step.
|
|
520
|
+
*
|
|
521
|
+
* Equivalent to spelling `<JourneyOutlet :instance-id="instanceId"
|
|
522
|
+
* :runtime="runtime" />` yourself with the slot's `instanceId` / `runtime` —
|
|
523
|
+
* use whichever reads better in your chrome.
|
|
524
|
+
*/
|
|
525
|
+
readonly outlet: FunctionalComponent;
|
|
509
526
|
}
|
|
510
527
|
/**
|
|
511
528
|
* Mount a journey in one line:
|
|
@@ -523,7 +540,8 @@ interface JourneyHostSlotProps {
|
|
|
523
540
|
* reads itself, to cover the render before the instance exists — which is why
|
|
524
541
|
* it has to accept both spellings; see {@link readLoadingFallback}.
|
|
525
542
|
*
|
|
526
|
-
* For chrome around the step, use the default scoped slot
|
|
543
|
+
* For chrome around the step, use the default scoped slot. The slot's `outlet`
|
|
544
|
+
* is a functional component, so `<component :is="outlet" />` renders it:
|
|
527
545
|
*
|
|
528
546
|
* ```vue
|
|
529
547
|
* <JourneyHost :handle="checkoutHandle" :input="{ cartId }" @finished="goToReceipt">
|
|
@@ -535,6 +553,17 @@ interface JourneyHostSlotProps {
|
|
|
535
553
|
* </JourneyHost>
|
|
536
554
|
* ```
|
|
537
555
|
*
|
|
556
|
+
* Or render the outlet yourself from the slot's `instanceId` + `runtime` — the
|
|
557
|
+
* more idiomatic-Vue spelling, equivalent to the `outlet` above:
|
|
558
|
+
*
|
|
559
|
+
* ```vue
|
|
560
|
+
* <template #default="{ stepIndex, instanceId, runtime }">
|
|
561
|
+
* <Layout title="Checkout" :step="stepIndex">
|
|
562
|
+
* <JourneyOutlet :instance-id="instanceId" :runtime="runtime" />
|
|
563
|
+
* </Layout>
|
|
564
|
+
* </template>
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
538
567
|
* To deep-link the steps, call `useJourneySync` in the same component — the
|
|
539
568
|
* host owns the instance, the sync owns the URL, and neither knows about the
|
|
540
569
|
* other.
|
|
@@ -593,8 +622,8 @@ declare const JourneyHost: import("vue").DefineComponent<import("vue").ExtractPr
|
|
|
593
622
|
default: undefined;
|
|
594
623
|
};
|
|
595
624
|
}>> & Readonly<{}>, {
|
|
596
|
-
input: any;
|
|
597
625
|
runtime: JourneyRuntime$1;
|
|
626
|
+
input: any;
|
|
598
627
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
599
628
|
//#endregion
|
|
600
629
|
//#region src/use-journey-sync.d.ts
|
|
@@ -756,9 +785,9 @@ declare const ModuleTab: import("vue").DefineComponent<import("vue").ExtractProp
|
|
|
756
785
|
default: undefined;
|
|
757
786
|
};
|
|
758
787
|
}>> & Readonly<{}>, {
|
|
788
|
+
onExit: (event: ModuleTabExitEvent) => void;
|
|
759
789
|
entry: string;
|
|
760
790
|
tabId: string;
|
|
761
|
-
onExit: (event: ModuleTabExitEvent) => void;
|
|
762
791
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
763
792
|
//#endregion
|
|
764
793
|
//#region src/use-wait-for-exit.d.ts
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/provider.ts","../src/use-journey-state.ts","../src/instance-hooks.ts","../src/mount-adapter.ts","../src/outlet.ts","../src/journey-host.ts","../src/use-journey-sync.ts","../src/module-tab.ts","../src/use-wait-for-exit.ts","../src/provide-journey-runtime.ts","../src/pinia-persistence.ts","../src/plugin.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/provider.ts","../src/use-journey-state.ts","../src/instance-hooks.ts","../src/mount-adapter.ts","../src/outlet.ts","../src/journey-host.ts","../src/use-journey-sync.ts","../src/module-tab.ts","../src/use-wait-for-exit.ts","../src/provide-journey-runtime.ts","../src/pinia-persistence.ts","../src/plugin.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -434,24 +434,28 @@ var Oe = n({
|
|
|
434
434
|
}
|
|
435
435
|
},
|
|
436
436
|
setup(e, { slots: t, attrs: n }) {
|
|
437
|
-
let { instanceId: i, instance: a, runtime: o, stepIndex: s } = Z(e.handle, e.input, { runtime: e.runtime })
|
|
437
|
+
let { instanceId: i, instance: a, runtime: o, stepIndex: s } = Z(e.handle, e.input, { runtime: e.runtime }), c = () => {
|
|
438
|
+
let e = i.value;
|
|
439
|
+
return e ? r(q, {
|
|
440
|
+
...n,
|
|
441
|
+
runtime: o,
|
|
442
|
+
instanceId: e
|
|
443
|
+
}) : null;
|
|
444
|
+
}, l = () => c();
|
|
438
445
|
return () => {
|
|
439
|
-
let e = i.value,
|
|
440
|
-
if (!e || !
|
|
446
|
+
let e = i.value, r = a.value;
|
|
447
|
+
if (!e || !r) {
|
|
441
448
|
let e = De(n);
|
|
442
449
|
return e ? typeof e == "function" ? e() : e : null;
|
|
443
450
|
}
|
|
444
|
-
let
|
|
445
|
-
...n,
|
|
446
|
-
runtime: o,
|
|
447
|
-
instanceId: e
|
|
448
|
-
}), u = t.default;
|
|
451
|
+
let u = t.default;
|
|
449
452
|
return u ? u({
|
|
450
453
|
instanceId: e,
|
|
451
|
-
instance:
|
|
454
|
+
instance: r,
|
|
455
|
+
runtime: o,
|
|
452
456
|
stepIndex: s.value,
|
|
453
457
|
outlet: l
|
|
454
|
-
}) :
|
|
458
|
+
}) : c();
|
|
455
459
|
};
|
|
456
460
|
}
|
|
457
461
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/provider.ts","../src/instance-hooks.ts","../src/use-journey-state.ts","../src/outlet.ts","../src/mount-adapter.ts","../src/journey-host.ts","../src/use-journey-sync.ts","../src/module-tab.ts","../src/use-wait-for-exit.ts","../src/provide-journey-runtime.ts","../src/pinia-persistence.ts","../src/plugin.ts"],"sourcesContent":["import { defineComponent, h, inject, provide, type InjectionKey, type PropType } from \"vue\";\nimport { ModuleExitProvider } from \"@modular-vue/vue\";\nimport type { ModuleExitEvent, ModuleExitHandler } from \"@modular-vue/vue\";\n\nimport type { JourneyRuntime } from \"@modular-frontend/journeys-engine\";\n\n/**\n * Shell-level context read by `<JourneyOutlet>` (PR-31) and the instance\n * composables so callers don't have to thread `runtime` through every\n * container that hosts a journey. Analog of the React `JourneyContext` value.\n *\n * `onModuleExit` is still surfaced here for consumers that introspect the\n * provider value. The actual dispatch flows through `<ModuleExitProvider>`\n * from `@modular-vue/vue`, which `<JourneyProvider>` mounts automatically.\n * Prefer consuming `useModuleExit` / `useModuleExitDispatcher` from the vue\n * package directly in new code.\n */\nexport interface JourneyProviderValue {\n /** Journey runtime — usually `manifest.journeys`. */\n readonly runtime: JourneyRuntime;\n /**\n * Optional fallback invoked by module hosts (`<ModuleRoute>`, tabs) after\n * any local `onExit` prop has run. Wiring this at the provider level gives a\n * shell global telemetry / tab-close forwarding without threading the\n * callback through every host.\n */\n readonly onModuleExit?: (event: ModuleExitEvent) => void;\n}\n\n/**\n * Injection key holding the current {@link JourneyProviderValue}, or `null`\n * when no `<JourneyProvider>` is mounted. Exported so tests and advanced hosts\n * can provide the context directly.\n */\nexport const journeyKey: InjectionKey<JourneyProviderValue> = Symbol(\"modular-vue.journey\");\n\n/**\n * Provides the journey runtime to descendant journey hosts, and composes over\n * `<ModuleExitProvider>` so module hosts (`<ModuleRoute>`, anything using\n * `useModuleExit`) see the shell's `onModuleExit` dispatcher without needing a\n * second provider.\n *\n * Authored with `defineComponent` + a render function (no SFC compiler in the\n * package build; see decision D4). The `runtime` is provided by identity at\n * setup — matching the modules / navigation contexts (PR-10), it is resolved\n * once from the manifest and does not swap on the same mount, and it is left\n * un-proxied so identity checks against `manifest.journeys` hold. The\n * `onModuleExit` handler is forwarded through the live `props` into\n * `<ModuleExitProvider>` on every render, and the provided context value reads\n * it through a getter, so a swapped handler reaches both descendant hosts and\n * consumers that introspect `useJourneyContext().onModuleExit` (parity with the\n * React provider, which rebuilds its value object each render).\n *\n * Existing journey consumers do not need to change — `onModuleExit` keeps\n * firing for every module exit emitted outside a journey step.\n */\nexport const JourneyProvider = defineComponent({\n name: \"JourneyProvider\",\n props: {\n /** Journey runtime — usually `manifest.journeys`. */\n runtime: { type: Object as PropType<JourneyRuntime>, required: true },\n /** Shell-wide fallback dispatcher for module exits fired outside a step. */\n onModuleExit: { type: Function as PropType<ModuleExitHandler>, default: undefined },\n },\n setup(props, { slots }) {\n // Keep `runtime` a raw reference (identity-stable for the same mount) while\n // exposing `onModuleExit` live: a getter reads the current prop on each\n // access, so a swapped handler is visible to introspecting consumers\n // without proxying `runtime` through `reactive`.\n const value: JourneyProviderValue = {\n runtime: props.runtime,\n get onModuleExit() {\n return props.onModuleExit;\n },\n };\n provide(journeyKey, value);\n return () => h(ModuleExitProvider, { onExit: props.onModuleExit }, () => slots.default?.());\n },\n});\n\n/** Read the current provider value, or `null` when none is mounted. */\nexport function useJourneyContext(): JourneyProviderValue | null {\n return inject(journeyKey, null);\n}\n","import {\n computed,\n shallowRef,\n toValue,\n watchEffect,\n type ComputedRef,\n type MaybeRefOrGetter,\n type ShallowRef,\n} from \"vue\";\nimport type {\n InstanceId,\n JourneyInstance,\n JourneyRuntime,\n} from \"@modular-frontend/journeys-engine\";\n\n/**\n * Sanity bound to break a corrupted cycle in the activeChild graph.\n * Legitimate invoke nesting is not expected to approach this depth — if a\n * real product stacks deeper, surface a knob via `JourneyRuntimeOptions`\n * rather than raising the constant blindly. Intentionally not exported:\n * the cap is implementation detail, not a knob. Matches the React source.\n */\nconst MAX_CHAIN_DEPTH = 64;\n\nconst EMPTY_CHAIN: readonly InstanceId[] = Object.freeze([]);\n\n/**\n * Subscribe to a single instance and return a `ShallowRef` of its current\n * snapshot, or `null` when runtime / id is missing or the instance has been\n * forgotten. The Vue analog of the React `useInstanceSnapshot` (which uses\n * `useSyncExternalStore`): a `watchEffect` seeds the ref, subscribes, and\n * re-subscribes whenever `instanceId` changes.\n *\n * `instanceId` accepts a plain value, a ref, or a getter. Reactive ids are\n * the point of the leaf-walking composables: `useActiveLeafJourneyInstance`\n * feeds a `ComputedRef` leaf id here, so the snapshot re-subscribes as the\n * active chain grows (parent invokes a child) or shrinks (child terminates).\n * The subscribe callback reads the *current* id so an in-flight notification\n * that arrives after an id swap still resolves against the live instance.\n *\n * The `watchEffect` uses the default (pre) flush: the initial subscription is\n * established synchronously at setup, runtime events push snapshots into the\n * `shallowRef` synchronously (matching React's tearing-free reads), and only\n * re-subscription on an id change is deferred to the next tick — so a\n * changing-leaf test awaits `nextTick()` before the swap is observed.\n */\nexport function useInstanceSnapshot(\n runtime: JourneyRuntime | null,\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const snapshot = shallowRef<JourneyInstance | null>(null);\n\n const readCurrent = (): JourneyInstance | null => {\n const id = toValue(instanceId);\n if (!runtime || !id) return null;\n return runtime.getInstance(id);\n };\n\n watchEffect((onCleanup) => {\n const id = toValue(instanceId);\n if (!runtime || !id) {\n snapshot.value = null;\n return;\n }\n snapshot.value = runtime.getInstance(id);\n const unsubscribe = runtime.subscribe(id, () => {\n snapshot.value = readCurrent();\n });\n onCleanup(unsubscribe);\n });\n\n return snapshot;\n}\n\n/**\n * Walk `activeChildId` from `rootId` down to the deepest descendant, returning\n * a `ShallowRef` of the full chain. Subscribes to every instance in the chain\n * and re-subscribes as the chain grows / shrinks. Pass `enabled: false` to\n * short-circuit the walk (collapses to `[rootId]`) — used by the outlet's\n * `leafOnly` opt-out (PR-31). Null `runtime` / `rootId` yields an empty chain\n * so callers can invoke this composable unconditionally even before a runtime\n * is mounted.\n *\n * The dynamic per-instance subscriptions are managed by hand (as in the React\n * source) rather than through Vue reactivity, since they rewire on runtime\n * events, not on tracked dependencies. The `watchEffect` only re-runs — tearing\n * down and rebuilding the whole chain — when `rootId` or `enabled` change.\n */\nexport function useCallChain(\n runtime: JourneyRuntime | null,\n rootId: MaybeRefOrGetter<InstanceId | null>,\n enabled: MaybeRefOrGetter<boolean>,\n): ShallowRef<readonly InstanceId[]> {\n const chainRef = shallowRef<readonly InstanceId[]>(EMPTY_CHAIN);\n\n // Cache by the joined-id signature so the ref keeps identity when the chain\n // hasn't actually shifted — the Vue analog of the React `cacheRef` guard, so\n // an unrelated state change on a chain member doesn't churn the ref.\n let lastKey: string | null = null;\n const setChain = (chain: readonly InstanceId[]): void => {\n const key = chain.join(\">\");\n if (lastKey === key) return;\n lastKey = key;\n chainRef.value = chain.length === 0 ? EMPTY_CHAIN : chain;\n };\n\n watchEffect((onCleanup) => {\n const root = toValue(rootId);\n const enabledNow = toValue(enabled);\n if (!runtime || !root) {\n setChain(EMPTY_CHAIN);\n return;\n }\n\n const unsubs = new Map<InstanceId, () => void>();\n let stopped = false;\n\n const rewire = (): void => {\n if (stopped) return;\n const seen = new Set<InstanceId>();\n const chain: InstanceId[] = [];\n let id: InstanceId | null = root;\n let depth = 0;\n while (id && depth < MAX_CHAIN_DEPTH) {\n if (seen.has(id)) break;\n seen.add(id);\n chain.push(id);\n if (!unsubs.has(id)) {\n unsubs.set(id, runtime.subscribe(id, rewire));\n }\n const inst = runtime.getInstance(id);\n id = enabledNow && inst ? inst.activeChildId : null;\n depth += 1;\n }\n for (const [subscribedId, unsub] of unsubs) {\n if (!seen.has(subscribedId)) {\n unsub();\n unsubs.delete(subscribedId);\n }\n }\n setChain(chain);\n };\n\n rewire();\n onCleanup(() => {\n stopped = true;\n for (const unsub of unsubs.values()) unsub();\n unsubs.clear();\n });\n });\n\n return chainRef;\n}\n\n/**\n * Last id in the active chain — `rootId` when no child is in flight, `null`\n * when runtime / rootId are missing. Returns a `ComputedRef` that tracks the\n * chain, so feeding it to {@link useInstanceSnapshot} re-subscribes the\n * snapshot as the leaf moves.\n */\nexport function useLeafId(\n runtime: JourneyRuntime | null,\n rootId: MaybeRefOrGetter<InstanceId | null>,\n enabled: MaybeRefOrGetter<boolean>,\n): ComputedRef<InstanceId | null> {\n const chain = useCallChain(runtime, rootId, enabled);\n return computed(() => chain.value[chain.value.length - 1] ?? toValue(rootId));\n}\n","import { computed, type ComputedRef, type MaybeRefOrGetter, type ShallowRef } from \"vue\";\nimport type { InstanceId, JourneyInstance } from \"@modular-frontend/journeys-engine\";\n\nimport { useInstanceSnapshot, useLeafId } from \"./instance-hooks.js\";\nimport { useJourneyContext } from \"./provider.js\";\n\n/**\n * Sugar over {@link useJourneyInstance}: returns a `ComputedRef` of the\n * instance's `state` (or `null` when the runtime / id / instance is\n * unavailable). `TState` is the journey's state type — pass it at the call\n * site.\n *\n * The React analog returns a plain `TState | null`; the Vue port returns a\n * `ComputedRef` so it stays reactive in templates and `watch` (the PR-23\n * convention for reactive-source composables). Read `.value` at the call site.\n *\n * Prefer {@link useJourneyInstance} when the host needs more than `state`\n * (`step` / `status` / `terminalPayload`).\n */\nexport function useJourneyState<TState>(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ComputedRef<TState | null> {\n const inst = useJourneyInstance(instanceId);\n return computed(() => (inst.value ? (inst.value.state as TState) : null));\n}\n\n/**\n * Subscribe to a journey instance and return a `ShallowRef` of its full\n * snapshot (`status`, `step`, `state`, `terminalPayload`, …), or `null` when\n * no `<JourneyProvider>` is mounted or the id is unknown. Tearing-free via the\n * `shallowRef` bridge in {@link useInstanceSnapshot}. The primitive of which\n * {@link useJourneyState} is sugar; symmetric with\n * {@link useActiveLeafJourneyInstance} for the leaf-walking case.\n */\nexport function useJourneyInstance(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const ctx = useJourneyContext();\n return useInstanceSnapshot(ctx?.runtime ?? null, instanceId);\n}\n\n/**\n * Sugar over {@link useActiveLeafJourneyInstance}: returns a `ComputedRef` of\n * the leaf instance's `state` as `TState`. `.value` is `null` when no\n * provider, the root id is unknown, or the leaf has been forgotten.\n *\n * Prefer {@link useActiveLeafJourneyInstance} when the leaf can be any of\n * several journeys — typing this composable as `<ParentState | ChildState>`\n * leaves the caller without a discriminator, whereas the instance form gives a\n * typed `inst.journeyId` to switch on.\n */\nexport function useActiveLeafJourneyState<TState>(\n rootInstanceId: MaybeRefOrGetter<InstanceId | null>,\n): ComputedRef<TState | null> {\n const inst = useActiveLeafJourneyInstance(rootInstanceId);\n return computed(() => (inst.value ? (inst.value.state as TState) : null));\n}\n\n/**\n * Walks `activeChildId` from `rootInstanceId` down to the deepest descendant\n * and returns a `ShallowRef` of that leaf's full `JourneyInstance`. The\n * recommended primitive when the host doesn't know the leaf's depth (a parent\n * that may or may not be in an invoked sub-flow) — pair with `inst.journeyId`\n * as a discriminator instead of typing the state composable as a union and\n * asserting manually. Re-subscribes as the chain grows (parent invokes a\n * child, grandchild starts) or shrinks (child terminates and parent resumes).\n *\n * `.value` is `null` under the same conditions as {@link useJourneyInstance}.\n */\nexport function useActiveLeafJourneyInstance(\n rootInstanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const ctx = useJourneyContext();\n const runtime = ctx?.runtime ?? null;\n const leafId = useLeafId(runtime, rootInstanceId, true);\n return useInstanceSnapshot(runtime, leafId);\n}\n","import {\n computed,\n defineComponent,\n h,\n inject,\n onErrorCaptured,\n onMounted,\n onUnmounted,\n ref,\n Suspense,\n toRaw,\n watch,\n type Component,\n type PropType,\n type ShallowRef,\n type VNode,\n} from \"vue\";\nimport type { ModuleDescriptor, ModuleEntryPoint } from \"@modular-frontend/core\";\nimport { resolveEntryComponent } from \"@modular-vue/vue\";\n\nimport { getInternals, isAnnotatedTransition } from \"@modular-frontend/journeys-engine\";\nimport { journeyKey } from \"./provider.js\";\nimport { useCallChain, useInstanceSnapshot, useLeafId } from \"./instance-hooks.js\";\nimport type {\n AnyJourneyDefinition,\n InstanceId,\n JourneyRuntime,\n JourneyStep,\n TerminalOutcome,\n} from \"@modular-frontend/journeys-engine\";\n\nexport type JourneyStepErrorPolicy = \"abort\" | \"retry\" | \"ignore\";\n\n/** Maximum automatic retries before falling back to `abort`. */\nconst DEFAULT_RETRY_CAP = 2;\n\nexport interface JourneyOutletNotFoundProps {\n readonly moduleId: string;\n readonly entry: string;\n}\n\nexport interface JourneyOutletErrorProps {\n readonly moduleId: string;\n readonly error: unknown;\n}\n\n/**\n * Renders the current step of a journey instance. Host-agnostic — works in a\n * tab, modal, route element, or plain `<div>`. On unmount while active, the\n * instance is abandoned (deferred by a microtask so a same-tick handoff to a\n * sibling outlet — outlet A unmounts, outlet B mounts against the same instance\n * — does not tear the instance down: the deferred check consults the record's\n * listener count and skips `end()` when another outlet is still subscribed).\n *\n * The Vue analog of the React `<JourneyOutlet>`. Authored with `defineComponent`\n * + a render function (no SFC compiler in the package build; see decision D4).\n * The instance snapshots come from the reactive `useInstanceSnapshot` /\n * `useLeafId` composables (PR-30), so the outlet re-renders as the active call\n * chain shifts (parent invokes a child, child terminates) and as the leaf's own\n * step advances. Error-message prefixes are `[@modular-vue/journeys]`.\n */\nexport const JourneyOutlet = defineComponent({\n name: \"JourneyOutlet\",\n props: {\n /**\n * Runtime to drive the outlet against. Optional when a `<JourneyProvider>`\n * is mounted above — the outlet reads the runtime from context in that\n * case. Explicit prop overrides context, so one outlet can reach a\n * different runtime when needed.\n */\n runtime: { type: Object as PropType<JourneyRuntime>, default: undefined },\n instanceId: { type: String as PropType<InstanceId>, required: true },\n /**\n * Module descriptors the outlet resolves step components against. Optional —\n * when omitted, the outlet pulls the descriptors the runtime was constructed\n * with (the common case).\n */\n modules: {\n type: Object as PropType<Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>>,\n default: undefined,\n },\n /** Rendered while the instance is in `loading` status. */\n loadingFallback: {\n type: null as unknown as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n onFinished: {\n type: Function as PropType<(outcome: TerminalOutcome) => void>,\n default: undefined,\n },\n onStepError: {\n type: Function as PropType<\n (err: unknown, ctx: { step: JourneyStep }) => JourneyStepErrorPolicy\n >,\n default: undefined,\n },\n /**\n * When `false`, the outlet renders the instance you handed it directly, even\n * if it has a child journey in flight. Set this when you compose two outlets\n * to render parent and child side-by-side. When `true` (the default), the\n * outlet walks the active call chain down to the leaf and renders the leaf.\n */\n leafOnly: { type: Boolean, default: true },\n /**\n * Cap on `retry` responses before the outlet falls back to `abort`. The\n * counter increments on every retry and is never reset. Default: 2.\n */\n retryLimit: { type: Number, default: DEFAULT_RETRY_CAP },\n /**\n * Rendered when the current step points at a module/entry that is not\n * registered with the runtime. Defaults to a plain red notice.\n */\n notFoundComponent: {\n type: Object as PropType<Component>,\n default: undefined,\n },\n /**\n * Rendered when a step component throws. Defaults to a plain red notice with\n * the error message. Receives the raw error so shells can route it through\n * their own reporting.\n */\n errorComponent: { type: Object as PropType<Component>, default: undefined },\n /**\n * Speculatively prefetch the chunks for entries reachable from the current\n * step during idle time after mount. `\"precise\"` (default, alias `true`)\n * reads declared `targets` from annotated handlers; `\"aggressive\"` preloads\n * every entry referenced as a transition source or annotated target;\n * `false` opts out. No effect for eager entries; SSR is a no-op.\n */\n preload: {\n type: [Boolean, String] as PropType<boolean | \"precise\" | \"aggressive\">,\n default: \"precise\",\n },\n },\n setup(props) {\n const context = inject(journeyKey, null);\n // Unwrap the reactive prop proxy before touching runtime internals: Vue\n // deeply proxies prop objects, and `getInternals` keys its store on the raw\n // runtime identity produced by `createJourneyRuntime`. A proxied runtime\n // would miss that store. `context?.runtime` can also be proxied (the\n // provider stashes its own `runtime` prop), so unwrap both.\n const runtime = toRaw(props.runtime ?? context?.runtime ?? undefined) ?? null;\n if (!runtime) {\n throw new Error(\n \"[@modular-vue/journeys] <JourneyOutlet> needs a runtime. Either pass `runtime` or mount a <JourneyProvider>.\",\n );\n }\n const internals = getInternals(runtime);\n\n // Unwrap the reactive prop proxy: Vue deeply proxies prop objects, which\n // would change the entry-object identity that keys `resolveEntryComponent`'s\n // per-entry `WeakMap` cache — so a preloaded chunk (resolved off the raw\n // runtime map) would not be reused by the render, and the memoized async\n // wrapper would churn. `internals.__moduleMap` is already raw; `toRaw` is a\n // no-op there.\n const getModules = (): Readonly<Record<string, ModuleDescriptor<any, any, any, any>>> =>\n toRaw(props.modules ?? internals.__moduleMap);\n\n // Subscribe to the originally-supplied (root) instance so onFinished and\n // abandon-on-unmount resolve against THAT instance — even when leaf-walk is\n // rendering a different (child/grandchild) record. The leaf is also\n // subscribed-to via `useLeafId` so this component re-renders when the call\n // chain shifts (parent invokes a child, child terminates, etc.).\n const rootInstance = useInstanceSnapshot(runtime, () => props.instanceId);\n const leafId = useLeafId(\n runtime,\n () => props.instanceId,\n () => props.leafOnly,\n );\n // Subscribe to the leaf separately so leaf-internal transitions trigger\n // re-renders. When the leaf id equals the root, useInstanceSnapshot dedupes\n // naturally (same subscribe call to the same record).\n const leafInstance = useInstanceSnapshot(runtime, leafId);\n const instance = computed(() =>\n leafId.value === props.instanceId ? rootInstance.value : leafInstance.value,\n );\n\n const retryKey = ref(0);\n\n // Abandon on unmount while still active or still loading. Deferred one\n // microtask so a same-tick handoff to a sibling outlet keeps the instance\n // alive: if any subscriber is still attached to the record, another outlet\n // has taken over and we skip the `end()`. Targets the ROOT — `runtime.end`\n // cascades to any active child, so a single call cleans the whole chain.\n //\n // Read `props.instanceId` at unmount time (not a value captured at setup)\n // so a host that re-binds `instanceId` on a live outlet abandons the\n // instance it is currently rendering — matching the React effect, whose\n // cleanup closure holds the latest id after re-subscription.\n onUnmounted(() => {\n const instanceId = props.instanceId;\n queueMicrotask(() => {\n const record = internals.__getRecord(instanceId);\n if (!record) return;\n if (record.status !== \"active\" && record.status !== \"loading\") return;\n if (record.listeners.size > 0) return;\n // `force`: an unmounting outlet cannot host an `onAbandon` `{ next }`\n // (there is nothing left to render it), so guarantee the instance ends\n // rather than advancing to an orphaned live step. Matches the React\n // outlet.\n runtime.end(instanceId, { reason: \"unmounted\" }, { force: true });\n });\n });\n\n // Fire onFinished exactly once on terminal — bound to the ROOT, since the\n // caller asked to be notified when the journey they mounted finishes. Child\n // terminations are observed via the parent's resume handler and are not\n // reported through this hook.\n const finishedInstances = new Set<InstanceId>();\n watch(\n rootInstance,\n (root) => {\n if (!root) return;\n if (root.status !== \"completed\" && root.status !== \"aborted\") return;\n if (finishedInstances.has(root.id)) return;\n finishedInstances.add(root.id);\n props.onFinished?.({\n status: root.status,\n payload: root.terminalPayload,\n instanceId: root.id,\n journeyId: root.journeyId,\n });\n },\n { immediate: true },\n );\n\n // Speculative preload of reachable entries' chunks. Re-keyed on\n // (status, module, entry, journey) — not on the whole snapshot, which\n // changes reference on every bump (timestamps, child-id shifts). Only fires\n // after mount (browser), matching the React effect; SSR stays a no-op.\n onMounted(() => {\n watch(\n () => {\n const inst = instance.value;\n return [\n props.preload,\n inst?.status === \"active\",\n inst?.step?.moduleId,\n inst?.step?.entry,\n inst?.journeyId,\n ] as const;\n },\n ([preload, isActive, stepModuleId, stepEntryName, journeyId], _old, onCleanup) => {\n if (preload === false || !isActive) return;\n if (!stepModuleId || !stepEntryName || !journeyId) return;\n const reg = internals.__getRegistered(journeyId);\n if (!reg) return;\n const mode = preload === \"aggressive\" ? \"aggressive\" : \"precise\";\n const targets = collectPreloadTargets(\n reg.definition,\n getModules(),\n stepModuleId,\n stepEntryName,\n mode,\n );\n if (targets.length === 0) return;\n\n let cancelled = false;\n const run = (): void => {\n if (cancelled) return;\n for (const entry of targets) {\n try {\n // `.catch` swallows async rejection from the lazy import; the\n // surrounding try/catch only covers a synchronous throw from\n // `resolveEntryComponent`/`preload` itself. Both are best-effort:\n // a malformed entry would have failed validation upstream, so one\n // bad entry never hides the rest.\n void resolveEntryComponent(entry)\n .preload()\n .catch(() => {});\n } catch {\n // Synchronous failure — see above.\n }\n }\n };\n\n const ricFn = (\n globalThis as {\n requestIdleCallback?: (cb: () => void, opts?: { timeout?: number }) => number;\n }\n ).requestIdleCallback;\n const cicFn = (globalThis as { cancelIdleCallback?: (handle: number) => void })\n .cancelIdleCallback;\n let idleHandle: number | undefined;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n if (typeof ricFn === \"function\") {\n idleHandle = ricFn(run, { timeout: 2000 });\n } else {\n timeoutHandle = setTimeout(run, 0);\n }\n onCleanup(() => {\n cancelled = true;\n if (idleHandle !== undefined && typeof cicFn === \"function\") cicFn(idleHandle);\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);\n });\n },\n { immediate: true },\n );\n });\n\n return () => {\n const inst = instance.value;\n if (!inst) return null;\n if (inst.status === \"loading\") {\n const lf = props.loadingFallback;\n return (typeof lf === \"function\" ? lf() : lf) ?? null;\n }\n if (inst.status === \"completed\" || inst.status === \"aborted\") return null;\n\n const step = inst.step;\n if (!step) return null;\n\n const modules = getModules();\n const mod = modules[step.moduleId];\n const entry = mod?.entryPoints?.[step.entry];\n if (!mod || !entry) {\n const NotFound = props.notFoundComponent ?? DefaultNotFound;\n return h(NotFound, { moduleId: step.moduleId, entry: step.entry });\n }\n if (!entryAllowsJourneyMount(entry)) {\n // Type-level enforcement via `StepSpec`'s mountKinds filter catches this\n // for typed code. The runtime check is the belt-and-braces for\n // type-bypass paths: journey-only hosts must not mount entries declared\n // `mountKinds: [\"composition\"]`.\n const ErrorComp = props.errorComponent ?? DefaultError;\n return h(ErrorComp, {\n moduleId: step.moduleId,\n error: new Error(\n `[@modular-vue/journeys] Entry \"${step.moduleId}.${step.entry}\" declares ` +\n `mountKinds=${JSON.stringify(entry.mountKinds)} which does not include \"journey\". ` +\n `Journey steps cannot mount composition-only entries — either widen the entry's mountKinds, ` +\n `or pick a different module-entry in the transition.`,\n ),\n });\n }\n\n // Resolve the *leaf's* record and registration so the step callbacks\n // (`exit`, `goBack`) drive the leaf's transitions.\n const record = internals.__getRecord(inst.id);\n const reg = internals.__getRegistered(inst.journeyId);\n if (!record || !reg) return null;\n const { exit, goBack, goForward } = internals.__bindStepCallbacks(record, reg);\n\n const handleError = (err: unknown): void => {\n // Registration-level onError fires on every component throw. Route\n // through the runtime so `fireOnError` stays the single owner of hook\n // firing. Bound to the LEAF's instance id since the throw came from the\n // leaf step's component.\n internals.__fireComponentError(inst.id, err, step);\n let policy = props.onStepError?.(err, { step }) ?? \"abort\";\n if (policy === \"retry\") {\n if (!internals.__consumeRetry(inst.id, props.retryLimit)) {\n policy = \"abort\";\n }\n }\n if (policy === \"abort\") {\n // End the LEAF — its abort cascades via the parent's resume handler.\n runtime.end(inst.id, { reason: \"component-error\", error: err });\n return;\n }\n if (policy === \"retry\") {\n retryKey.value += 1;\n }\n // 'ignore' — leave the boundary UI in place until the user navigates away.\n };\n\n const { Component: StepComponent } = resolveEntryComponent(entry);\n const stepKey = `${record.stepToken}:${retryKey.value}`;\n const entryFallback = (entry as { fallback?: VNode | (() => VNode) }).fallback;\n const outletFallback = props.loadingFallback;\n\n return h(\n StepErrorBoundary,\n {\n key: stepKey,\n moduleId: step.moduleId,\n onError: handleError,\n errorComponent: props.errorComponent,\n },\n () =>\n h(Suspense, null, {\n default: () =>\n h(StepComponent as Component, {\n input: step.input,\n exit,\n goBack,\n goForward,\n }),\n fallback: () => {\n const fb = entryFallback ?? outletFallback;\n return (typeof fb === \"function\" ? fb() : fb) ?? null;\n },\n }),\n );\n };\n },\n});\n\n/**\n * Walk `definition.transitions` to assemble the set of entry-point descriptors\n * to preload. In `\"precise\"` mode we look at the current step's transitions only\n * and read each handler's declared `targets`; in `\"aggressive\"` mode we walk\n * every entry referenced anywhere in the map. Both skip the current step.\n */\nfunction collectPreloadTargets(\n definition: AnyJourneyDefinition,\n modules: Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>,\n currentModuleId: string,\n currentEntry: string,\n mode: \"precise\" | \"aggressive\",\n): readonly ModuleEntryPoint<any>[] {\n const seen = new Set<string>();\n const out: ModuleEntryPoint<any>[] = [];\n const transitions = definition.transitions as\n | Record<string, Record<string, Record<string, unknown>> | undefined>\n | undefined;\n if (!transitions) return out;\n\n const collectPair = (moduleId: string, entryName: string): void => {\n if (!moduleId || !entryName) return;\n if (moduleId === currentModuleId && entryName === currentEntry) return;\n const seenKey = `${moduleId} ${entryName}`;\n if (seen.has(seenKey)) return;\n seen.add(seenKey);\n const entry = modules[moduleId]?.entryPoints?.[entryName];\n if (entry) out.push(entry);\n };\n\n if (mode === \"precise\") {\n const perEntry = transitions[currentModuleId]?.[currentEntry];\n if (!perEntry) return out;\n for (const value of Object.values(perEntry)) {\n if (!isAnnotatedTransition(value)) continue;\n for (const target of value.targets) {\n // Sentinel targets (`\"complete\"` / `\"abort\"` / `\"invoke\"`) carry no\n // chunk to preload — skip them here.\n if (typeof target === \"string\") continue;\n collectPair(target.module, target.entry);\n }\n }\n return out;\n }\n\n // Aggressive — every (module, entry) the journey could plausibly navigate to:\n // source-side keys UNIONED with the destinations declared by every annotated\n // handler (covers terminal-only destination steps).\n for (const [moduleId, perModule] of Object.entries(transitions)) {\n if (!perModule) continue;\n for (const [entryName, perExit] of Object.entries(perModule)) {\n collectPair(moduleId, entryName);\n if (!perExit) continue;\n for (const value of Object.values(perExit)) {\n if (!isAnnotatedTransition(value)) continue;\n for (const target of value.targets) {\n if (typeof target === \"string\") continue;\n collectPair(target.module, target.entry);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Returns true iff the entry's `mountKinds` permits the `\"journey\"` host.\n * Omitted `mountKinds` defaults to \"every mount surface\", so existing modules\n * that never opted in still work. Mirrors the composition outlet's\n * `entryAllowsCompositionMount`.\n */\nfunction entryAllowsJourneyMount(entry: { readonly mountKinds?: readonly string[] }): boolean {\n if (!Array.isArray(entry.mountKinds)) return true;\n return entry.mountKinds.includes(\"journey\");\n}\n\nconst DefaultNotFound = defineComponent({\n name: \"JourneyOutletNotFound\",\n props: {\n moduleId: { type: String, required: true },\n entry: { type: String, required: true },\n },\n setup(props) {\n return () =>\n h(\n \"div\",\n { style: { padding: \"1rem\", color: \"#c53030\" } },\n `Journey outlet: no entry \"${props.moduleId}.${props.entry}\" on the registered modules.`,\n );\n },\n});\n\nconst DefaultError = defineComponent({\n name: \"JourneyOutletError\",\n props: {\n moduleId: { type: String, required: true },\n error: { type: null as unknown as PropType<unknown>, default: undefined },\n },\n setup(props) {\n return () => {\n const err: unknown = props.error;\n const message = err instanceof Error ? err.message : String(err);\n return h(\n \"div\",\n {\n style: {\n padding: \"1rem\",\n border: \"1px solid #e53e3e\",\n borderRadius: \"0.5rem\",\n margin: \"1rem\",\n },\n role: \"alert\",\n \"data-journey-step-error\": props.moduleId,\n },\n [\n h(\n \"h3\",\n { style: { color: \"#e53e3e\", margin: \"0 0 0.5rem 0\" } },\n `Module \"${props.moduleId}\" encountered an error`,\n ),\n h(\n \"pre\",\n { style: { fontSize: \"0.875rem\", color: \"#718096\", whiteSpace: \"pre-wrap\" } },\n message,\n ),\n ],\n );\n };\n },\n});\n\n/**\n * Internal boundary the outlet wraps each step in. Analog of the React class\n * `StepErrorBoundary`: `onErrorCaptured` plays the role of\n * `getDerivedStateFromError` + `componentDidCatch`, flipping a local `error`\n * ref that swaps the subtree for the fallback and forwarding the error to\n * `onError` (which runs the outlet's retry/abort/ignore policy). Returning\n * `false` stops propagation to ancestor boundaries.\n *\n * The outlet keys this boundary by `${stepToken}:${retryKey}`, so a retry\n * (which bumps `retryKey`) remounts a fresh boundary that renders the step\n * again — the Vue analog of the React `key`-driven boundary reset.\n */\nconst StepErrorBoundary = defineComponent({\n name: \"JourneyStepErrorBoundary\",\n props: {\n moduleId: { type: String, required: true },\n onError: { type: Function as PropType<(err: unknown) => void>, required: true },\n errorComponent: { type: Object as PropType<Component>, default: undefined },\n },\n setup(props, { slots }) {\n const error = ref<unknown>(null);\n onErrorCaptured((err) => {\n error.value = err;\n props.onError(err);\n // Handled — don't propagate to ancestor boundaries.\n return false;\n });\n return () => {\n if (error.value != null) {\n const ErrorFallback = props.errorComponent ?? DefaultError;\n return h(ErrorFallback, { moduleId: props.moduleId, error: error.value });\n }\n return slots.default?.();\n };\n },\n});\n\n/**\n * Returns a reactive `ShallowRef` of the call stack for an outlet's instance —\n * root at index 0, the active leaf at the end, intermediate parents in between.\n * Useful for shells that render layered presentations (parent underneath, child\n * in a modal): mount the parent outlet with `leafOnly={false}` and the child\n * outlet against `chain.value[chain.value.length - 1]`. Returns a ref (not a\n * plain array) per the PR-30 reactive-source convention.\n */\nexport function useJourneyCallStack(\n runtime: JourneyRuntime,\n rootId: InstanceId,\n): ShallowRef<readonly InstanceId[]> {\n return useCallChain(runtime, rootId, true);\n}\n","import { defineComponent, h, type PropType, type VNode } from \"vue\";\nimport type { RuntimeMountAdapter } from \"@modular-frontend/core\";\nimport type { InstanceId, JourneyRuntime } from \"@modular-frontend/journeys-engine\";\nimport { JourneyOutlet } from \"./outlet.js\";\n\n/**\n * Adapt a {@link JourneyRuntime} to the generic\n * {@link RuntimeMountAdapter} shape so other packages can embed journeys\n * without depending on this package directly. Today the only consumer is\n * `@modular-vue/compositions` (zones with `kind: \"journey\"`):\n *\n * ```ts\n * import { createJourneyMountAdapter } from \"@modular-vue/journeys\";\n *\n * const manifest = registry.resolveManifest();\n * manifest.extensions.compositions.registerMountAdapter(\n * \"journey\",\n * createJourneyMountAdapter(manifest.extensions.journeys),\n * );\n * ```\n *\n * The wiring happens once after `resolveManifest()` and before mounting the\n * app, so the composition outlet finds the adapter the first time a zone\n * returns a `kind: \"journey\"` resolution. If the wiring is omitted, the\n * zone renders its `errorComponent` with a clear \"no adapter registered\"\n * message instead of throwing (see `CompositionOutlet`).\n *\n * The Vue analog of `@modular-react/journeys`'s `createJourneyMountAdapter`.\n *\n * Deviation from the React source, and why: React returns the bare\n * `JourneyOutlet` as `Outlet` and lets it read the journey runtime from the\n * `<JourneyProvider>` context that the journeys plugin threads app-wide. The\n * Vue `<CompositionOutlet>` renders `adapter.Outlet` with only\n * `{ instanceId, loadingFallback }` (no `runtime`), so the Vue adapter binds\n * the runtime it was handed into a thin wrapper component. This makes the\n * adapter self-contained — it mounts instances against exactly the runtime\n * passed to `createJourneyMountAdapter`, whether or not a `<JourneyProvider>`\n * sits above the composition outlet — rather than depending on an ambient\n * context that must happen to hold the same runtime.\n */\nexport function createJourneyMountAdapter(runtime: JourneyRuntime): RuntimeMountAdapter {\n const Outlet = defineComponent({\n name: \"JourneyMountAdapterOutlet\",\n props: {\n instanceId: { type: String as PropType<InstanceId>, required: true },\n loadingFallback: {\n type: null as unknown as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n },\n setup(props) {\n // Bind the captured `runtime` explicitly so the embedded journey mounts\n // against the runtime the adapter was built for — not whatever a\n // `<JourneyProvider>` above the composition outlet may (or may not) hold.\n return () =>\n h(JourneyOutlet, {\n runtime,\n instanceId: props.instanceId,\n loadingFallback: props.loadingFallback,\n });\n },\n });\n\n return {\n start(definitionId, input) {\n // The runtime's `start(journeyId, input)` overload accepts a bare id\n // string for dynamic dispatch — exactly what the adapter receives from\n // the composition outlet (which dereferences `handle.id` before calling).\n return runtime.start(definitionId, input);\n },\n end(instanceId) {\n // Cooperative cleanup: when the composition's per-zone cache evicts a\n // journey instance it minted, route through `end()` so the journey\n // runtime cascades into its own teardown rather than leaking the record\n // until the host process exits.\n runtime.end(instanceId, { reason: \"adapter-end\" });\n },\n Outlet,\n };\n}\n","import {\n computed,\n defineComponent,\n h,\n onMounted,\n onUnmounted,\n shallowRef,\n toRaw,\n type ComputedRef,\n type PropType,\n type ShallowRef,\n type VNode,\n} from \"vue\";\n\nimport type {\n InstanceId,\n JourneyHandle,\n JourneyInstance,\n JourneyRuntime,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { useInstanceSnapshot } from \"./instance-hooks.js\";\nimport { JourneyOutlet } from \"./outlet.js\";\nimport { useJourneyContext } from \"./provider.js\";\n\n/**\n * Tracks which host mount currently owns each started instance, so an\n * unmounting host's deferred teardown does not end and forget an instance a\n * same-tick replacement host has already resumed. With persistence,\n * `runtime.start()` returns the same id for the same input, so a route change\n * that unmounts one host and mounts another for the same journey hands the\n * live instance across — and the outgoing host's queued `end`/`forget` must\n * not fire against it. Keyed by runtime (a `WeakMap`, so it drops when the\n * runtime is collected) then by instance id.\n *\n * The value is a **set** of owner tokens, not a single one: two hosts can be\n * mounted on the same persisted id at once (concurrently, or across a route\n * swap), and only when the last of them unmounts should the instance be ended\n * and forgotten. A single \"latest owner\" would let the newer host, unmounting\n * first, tear the instance down while the older one is still showing it.\n */\nconst hostOwners = new WeakMap<JourneyRuntime, Map<InstanceId, Set<symbol>>>();\n\nfunction ownersFor(runtime: JourneyRuntime): Map<InstanceId, Set<symbol>> {\n let owners = hostOwners.get(runtime);\n if (!owners) {\n owners = new Map();\n hostOwners.set(runtime, owners);\n }\n return owners;\n}\n\nexport interface UseJourneyHostOptions {\n /**\n * Runtime to start the journey on. Optional when a `<JourneyProvider>` is\n * mounted above — the composable reads the runtime from context in that case.\n */\n readonly runtime?: JourneyRuntime;\n}\n\nexport interface JourneyHostState {\n /**\n * The instance this host owns. `null` until the component is mounted — the\n * journey is started from `onMounted`, so it does not exist during the first\n * render. `<JourneyHost>` renders `loadingFallback` for that frame.\n */\n readonly instanceId: ShallowRef<InstanceId | null>;\n readonly instance: ShallowRef<JourneyInstance | null>;\n /**\n * The runtime this host resolved at setup — the one `instanceId` is\n * meaningful to, and the one the host's own `<JourneyOutlet>` is given.\n * Already `toRaw`-unwrapped.\n *\n * Normally identical to whatever a `<JourneyProvider>` above you would hand\n * out, so a hand-placed outlet can keep resolving from context. It is\n * exposed for the case where those two could differ: a shell that swaps its\n * provider value mid-flight. The host does not follow such a swap (see\n * \"read once, at setup\"), so this is the runtime to use if you want to be\n * certain you are talking about the same journey the host started.\n */\n readonly runtime: JourneyRuntime;\n /**\n * How many steps the user has completed — `history.length`, so `0` on the\n * first step. Rewinds when the journey does.\n *\n * There is deliberately no `stepCount` here. The total is not knowable from\n * a running instance: a journey's next step is computed by a transition\n * handler from live state, so nothing short of walking the transition graph\n * can count the steps ahead — and a hand-passed total is exactly the\n * duplicated flow encoding this API exists to remove. Deriving it from the\n * graph is tracked separately.\n */\n readonly stepIndex: ComputedRef<number>;\n}\n\n/**\n * Own a journey instance for the lifetime of a component: start it on mount,\n * end and forget it on unmount. The Vue analog of the React\n * `useJourneyHost`.\n *\n * This is the lifecycle half of \"mount a journey\"; `useJourneySync` is the URL\n * half, and `<JourneyOutlet>` is the rendering half. {@link JourneyHost}\n * packages the first and the last together.\n *\n * **`handle`, `input` and `runtime` are read once, at setup**, and are\n * therefore plain values rather than the `MaybeRefOrGetter` the reactive\n * composables in this package take. Changing them cannot restart the journey:\n * silently abandoning a half-finished flow because a prop changed is never\n * what the caller meant. To run a different journey — or to run it on a\n * different runtime — remount: `<JourneyHost :key=\"journeyId\" …>`. The React\n * binding pins its runtime the same way.\n *\n * **Start means resume, when persistence is configured.** `runtime.start()`\n * with a `persistence` adapter returns the in-flight instance for the same\n * `keyFor(input)` rather than minting a new one, so a host that remounts (a\n * route change, a tab switch) picks the journey back up where it was. Without\n * persistence every mount starts a fresh instance.\n */\nexport function useJourneyHost<TInput>(\n handle: JourneyHandle<string, TInput, unknown>,\n input: TInput,\n options: UseJourneyHostOptions = {},\n): JourneyHostState {\n const ctx = useJourneyContext();\n // `toRaw` for the same reason the outlet does it — `getInternals` keys its\n // store on the raw runtime identity, and a runtime that arrived through a\n // reactive prop is a proxy.\n const runtime = toRaw(options.runtime ?? ctx?.runtime ?? undefined) ?? null;\n if (!runtime) {\n throw new Error(\n \"[@modular-vue/journeys] useJourneyHost needs a runtime. Either pass `runtime` or mount a <JourneyProvider>.\",\n );\n }\n\n const instanceId = shallowRef<InstanceId | null>(null);\n // Unique per host mount — see `hostOwners`.\n const ownerToken = Symbol(\"journey-host\");\n\n onMounted(() => {\n // Started from `onMounted` rather than `setup` so the start is guaranteed\n // to be paired with an `onUnmounted` that ends it. A component whose\n // `setup` ran but whose mount never completed (an ancestor threw first,\n // an async boundary discarded the branch) would otherwise leak a live\n // instance nothing owns. Also keeps the observable contract identical to\n // the React binding's.\n //\n // The string-id overload: the handle form's `...rest` tuple cannot be\n // satisfied from a generic `TInput` without widening the call site.\n // `handle.id` is the same value the handle overload reads.\n const id = runtime.start(handle.id, input);\n instanceId.value = id;\n // Claim ownership of the id by adding this mount's token to the id's owner\n // set (creating it on first mount).\n const owners = ownersFor(runtime);\n let ownerSet = owners.get(id);\n if (!ownerSet) {\n ownerSet = new Set();\n owners.set(id, ownerSet);\n }\n ownerSet.add(ownerToken);\n });\n\n onUnmounted(() => {\n const id = instanceId.value;\n if (!id) return;\n instanceId.value = null;\n const owners = ownersFor(runtime);\n // Deferred one microtask, matching `<JourneyOutlet>`: an inner outlet\n // schedules its own abandon the same way, and reading the record after\n // both have settled keeps the two from racing.\n queueMicrotask(() => {\n const set = owners.get(id);\n if (!set) return;\n // Drop this host's claim. Only tear down when no host still owns the id —\n // another host may share it (a route swap resuming the same persisted\n // instance, or two concurrent hosts), and it would keep showing the\n // instance we would otherwise end and forget.\n set.delete(ownerToken);\n if (set.size > 0) return;\n owners.delete(id);\n // `end` first, then `forget`: `forget` is a no-op on an instance that is\n // still active, and `end` is synchronous, so this ordering both aborts\n // the run and drops the record. An inner `<JourneyOutlet>` may have\n // already ended it — `end` no-ops on a terminal instance, so the race is\n // harmless in either order.\n //\n // `force`: the host owns this instance's lifetime, so unmount must leave\n // it terminal. Without force, an `onAbandon` returning a non-terminal\n // `{ next }` would advance the flow to a step no host renders and\n // `forget` would then no-op, leaking the instance and its persistence\n // key. Forcing coerces that to an abort; a terminal `onAbandon` choice\n // (complete/abort) is still honoured. The inner outlet's teardown also\n // forces, so whichever runs first terminates and the other no-ops —\n // `onAbandon` runs once, not twice. Matches the React host.\n runtime.end(id, { reason: \"unmounted\" }, { force: true });\n runtime.forget(id);\n });\n });\n\n const instance = useInstanceSnapshot(runtime, instanceId);\n\n return {\n instanceId,\n instance,\n runtime,\n stepIndex: computed(() => (instance.value ? instance.value.history.length : 0)),\n };\n}\n\n/**\n * Pull `loadingFallback` out of the host's attrs, accepting either spelling.\n *\n * Vue camelizes kebab-case only for **declared** props; a fallthrough attr\n * keeps whatever key the caller wrote. So a template's\n * `:loading-fallback=\"…\"` arrives as `attrs[\"loading-fallback\"]` while a\n * render function's `h(JourneyHost, { loadingFallback })` arrives camelCased,\n * and the host — which reads this attr itself rather than declaring it — has\n * to look under both. The inner `<JourneyOutlet>` needs no such help: it\n * *declares* `loadingFallback`, so Vue normalizes the kebab key for it.\n *\n * Without this the template spelling renders nothing for the frame before the\n * instance exists, then silently starts working once the outlet mounts.\n */\nfunction readLoadingFallback(attrs: Record<string, unknown>): VNode | (() => VNode) | undefined {\n return (attrs.loadingFallback ?? attrs[\"loading-fallback\"]) as VNode | (() => VNode) | undefined;\n}\n\nexport interface JourneyHostSlotProps {\n readonly instanceId: InstanceId;\n readonly instance: JourneyInstance;\n /** See {@link JourneyHostState.stepIndex}. */\n readonly stepIndex: number;\n /**\n * The `<JourneyOutlet>` for this instance, already built with every outlet\n * attribute passed to `<JourneyHost>`. Place it inside your chrome.\n */\n readonly outlet: VNode;\n}\n\n/**\n * Mount a journey in one line:\n * `<JourneyHost :handle=\"checkoutHandle\" :input=\"{ cartId }\" />`.\n *\n * Starts the journey on mount, renders its current step, and ends + forgets\n * the instance on unmount — the wrapper every journey host ends up writing.\n * The Vue analog of the React `<JourneyHost>`.\n *\n * **Outlet props pass through as attributes.** `inheritAttrs: false` plus an\n * attrs spread onto the inner `<JourneyOutlet>` means every outlet prop\n * (`onFinished`, `onStepError`, `errorComponent`, `preload`, …) works on\n * `<JourneyHost>` without this component re-declaring — and drifting from —\n * the outlet's prop list. `loadingFallback` is the one attr the host also\n * reads itself, to cover the render before the instance exists — which is why\n * it has to accept both spellings; see {@link readLoadingFallback}.\n *\n * For chrome around the step, use the default scoped slot:\n *\n * ```vue\n * <JourneyHost :handle=\"checkoutHandle\" :input=\"{ cartId }\" @finished=\"goToReceipt\">\n * <template #default=\"{ stepIndex, outlet }\">\n * <Layout title=\"Checkout\" :step=\"stepIndex\">\n * <component :is=\"outlet\" />\n * </Layout>\n * </template>\n * </JourneyHost>\n * ```\n *\n * To deep-link the steps, call `useJourneySync` in the same component — the\n * host owns the instance, the sync owns the URL, and neither knows about the\n * other.\n *\n * See {@link useJourneyHost} for the lifecycle rules (the instance is fixed\n * for the host's lifetime; `start` resumes when persistence is configured).\n */\nexport const JourneyHost = defineComponent({\n name: \"JourneyHost\",\n inheritAttrs: false,\n props: {\n handle: {\n type: Object as PropType<JourneyHandle<string, any, any>>,\n required: true,\n },\n /**\n * The journey's `input`. Typed `any` because a `defineComponent` prop\n * list cannot carry the handle's `TInput` through — reach for\n * {@link useJourneyHost} in a `<script setup>` block when you want the\n * input checked against the handle.\n */\n input: { type: null as unknown as PropType<any>, default: undefined },\n /**\n * Runtime to start the journey on, forwarded to the outlet. Optional when\n * a `<JourneyProvider>` is mounted above.\n */\n runtime: { type: Object as PropType<JourneyRuntime>, default: undefined },\n },\n setup(props, { slots, attrs }) {\n const { instanceId, instance, runtime, stepIndex } = useJourneyHost(props.handle, props.input, {\n runtime: props.runtime,\n });\n\n return () => {\n const id = instanceId.value;\n const inst = instance.value;\n if (!id || !inst) {\n const fallback = readLoadingFallback(attrs);\n if (!fallback) return null;\n return typeof fallback === \"function\" ? fallback() : fallback;\n }\n\n // The resolved runtime, not `props.runtime`: `instanceId` only means\n // anything on the runtime the host started it on, so forwarding a later\n // prop value would point the outlet at a runtime that has never heard of\n // this instance.\n const outlet = h(JourneyOutlet, { ...attrs, runtime, instanceId: id });\n const slot = slots.default;\n if (!slot) return outlet;\n return slot({\n instanceId: id,\n instance: inst,\n stepIndex: stepIndex.value,\n outlet,\n } satisfies JourneyHostSlotProps);\n };\n },\n});\n","import { toRaw, toValue, watch, watchEffect, type MaybeRefOrGetter } from \"vue\";\n\nimport { createJourneySync, defaultStepPath } from \"@modular-frontend/journeys-engine\";\nimport type {\n InstanceId,\n JourneyRuntime,\n JourneySyncCallbackCtx,\n JourneySyncOptions,\n JourneySyncPort,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { useJourneyContext } from \"./provider.js\";\n\nexport interface UseJourneySyncOptions extends JourneySyncOptions {\n /**\n * Runtime to sync against. Optional when a `<JourneyProvider>` is mounted\n * above — the composable reads the runtime from context in that case. Read\n * once at setup, matching how the other composables in this package resolve\n * their runtime.\n */\n readonly runtime?: JourneyRuntime;\n}\n\n/**\n * Keep a journey instance and the browser URL in step, in both directions:\n * the journey advances and the URL follows; the user presses Back or Forward\n * and the journey follows.\n *\n * The reconciler itself is framework- and router-neutral and lives in\n * `@modular-frontend/journeys-engine`; this composable is the Vue lifetime\n * wrapper around it, and the exact analog of the React `useJourneySync`. See\n * `createJourneySync` in the engine for the full semantics (push-vs-replace\n * rules, what a location can and cannot select, why the design is\n * event-sourced rather than a diff).\n *\n * Supply a {@link JourneySyncPort} for vue-router. It is the only router-aware\n * code in the picture:\n *\n * ```ts\n * const router = useRouter();\n * const route = useRoute();\n * const port: JourneySyncPort = {\n * read: () => String(route.params.step ?? \"\"),\n * push: (path) => void router.push({ name: \"checkout\", params: { step: path } }),\n * replace: (path) => void router.replace({ name: \"checkout\", params: { step: path } }),\n * go: (delta) => router.go(delta),\n * subscribe: (listener) => router.afterEach(() => listener()),\n * };\n *\n * useJourneySync(instanceId, port, { stepToPath: (step) => step.entry });\n * ```\n *\n * Conventions:\n *\n * - **`instanceId` accepts a plain value, a ref, or a getter**, and may be\n * `null` — the composable no-ops until an id arrives, so it can be called at\n * setup before the instance exists (as it must be under `useJourneyHost`,\n * which starts the journey on mount). The sync re-attaches when the id\n * changes.\n * - **`port` and `options` may be plain, refs, or getters.** Both are read\n * through a live holder rather than re-creating the reconciler, because\n * re-creating it re-runs its initial reconcile — and that reconcile\n * navigates. Pass a getter when the port itself depends on reactive state.\n * Two things are read once rather than live: `port.subscribe`, called when\n * the sync is created (so the subscription belongs to the port current at\n * that moment), and `options.stepToPath`, captured then too (the engine\n * needs a stable step->path mapping to resolve already-stamped history\n * frames). Both are invisible for the normal case — a port over a stable\n * router with a fixed mapping — but a port that subscribes to a *different*\n * source later, or a mapping that changes, will not take effect until the\n * sync is re-created. `onUnresolved`/`onBlocked` remain live. Change\n * `instanceId` (or remount) to re-attach.\n * - **The composable never starts or ends the instance.** It only navigates\n * within a journey that is already running. Pair it with\n * {@link JourneyHost}, which owns the lifecycle.\n */\nexport function useJourneySync(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n port: MaybeRefOrGetter<JourneySyncPort>,\n options: MaybeRefOrGetter<UseJourneySyncOptions> = {},\n): void {\n const ctx = useJourneyContext();\n // `toRaw` for the same reason the outlet does it: `getInternals` keys its\n // store on the raw runtime identity handed out by `createJourneyRuntime`,\n // and a value that reached us through a reactive prop is a proxy.\n const runtime = toRaw(toValue(options).runtime ?? ctx?.runtime ?? undefined) ?? null;\n\n // Live holder — `setup` runs once, so a caller whose port or callbacks\n // depend on reactive state passes a getter and this keeps up. Mirrors the\n // `latest` pattern in `useWaitForExit`.\n const latest = {\n port: toValue(port),\n options: toValue(options),\n };\n watchEffect(() => {\n latest.port = toValue(port);\n latest.options = toValue(options);\n });\n\n // `go` is optional on the port and its *presence* is load-bearing — the\n // reconciler falls back to `replace` without it — so the proxy has to\n // present `undefined` when the current port has none, not a function that\n // silently does nothing.\n const stablePort: JourneySyncPort = {\n read: () => latest.port.read(),\n push: (path) => latest.port.push(path),\n replace: (path) => latest.port.replace(path),\n get go() {\n return latest.port.go ? (delta: number) => latest.port.go?.(delta) : undefined;\n },\n subscribe: (listener) => latest.port.subscribe(listener),\n };\n\n watch(\n () => toValue(instanceId),\n (id, _previous, onCleanup) => {\n if (!runtime || !id) return;\n // `stepToPath` is captured once per sync, not read live: the engine\n // relies on a stable step->path mapping to resolve the history frames the\n // port has already stamped, so swapping it mid-flight would strand\n // previously visited paths as unresolved. `onUnresolved`/`onBlocked` stay\n // live. Change `instanceId` (or remount) to adopt a new mapping.\n const stepToPath = latest.options.stepToPath ?? defaultStepPath;\n const sync = createJourneySync(runtime, id, stablePort, {\n stepToPath,\n onUnresolved: (syncCtx: JourneySyncCallbackCtx) => latest.options.onUnresolved?.(syncCtx),\n onBlocked: (syncCtx: JourneySyncCallbackCtx) => latest.options.onBlocked?.(syncCtx),\n });\n // Runs before the next id's sync is created, and again when the\n // watcher is stopped as the component's scope is disposed.\n onCleanup(() => sync.stop());\n },\n { immediate: true },\n );\n}\n","import {\n computed,\n defineComponent,\n h,\n Suspense,\n toRaw,\n type Component,\n type PropType,\n type VNode,\n} from \"vue\";\nimport type { ModuleDescriptor } from \"@modular-frontend/core\";\nimport {\n ModuleErrorBoundary,\n resolveEntryComponent,\n useModuleExit,\n type ModuleExitEvent,\n} from \"@modular-vue/vue\";\n\n/**\n * Exit event fired by a module rendered inside a `<ModuleTab>`.\n *\n * Alias for {@link ModuleExitEvent} from `@modular-vue/vue` — kept as a named\n * export for the workspace-tab entry point so existing imports keep compiling.\n * Both types have the same shape.\n */\nexport type ModuleTabExitEvent = ModuleExitEvent;\n\ninterface EntryResolution {\n readonly resolvedName: string | undefined;\n readonly missingEntryNotice: string | null;\n}\n\n/**\n * Resolve the entry name for a `<ModuleTab>` from its module + optional `entry`\n * prop, matching the React source's rules: auto-pick a lone entry, require\n * disambiguation for a multi-entry module, surface a notice for an unknown\n * entry, and refuse to fall through to the legacy `component` when an explicit\n * `entry` is passed to a module with no entry points.\n */\nfunction resolveEntryName(\n mod: ModuleDescriptor<any, any, any, any>,\n entry: string | undefined,\n): EntryResolution {\n const entryPoints = mod.entryPoints;\n const entryNames = entryPoints ? Object.keys(entryPoints) : [];\n let resolvedName: string | undefined = entry;\n let missingEntryNotice: string | null = null;\n if (entry === undefined) {\n if (entryNames.length === 1) {\n resolvedName = entryNames[0];\n } else if (entryNames.length > 1) {\n missingEntryNotice = `Module \"${mod.id}\" exposes multiple entries (${entryNames.join(\", \")}); pass the \\`entry\\` prop to disambiguate.`;\n }\n } else if (entryPoints && !Object.prototype.hasOwnProperty.call(entryPoints, entry)) {\n missingEntryNotice = `Module \"${mod.id}\" has no entry \"${entry}\". Registered: ${entryNames.join(\", \") || \"(none)\"}.`;\n } else if (!entryPoints) {\n // `entry` requested but module exposes no entry points at all — surface\n // the misconfiguration instead of silently falling through to the legacy\n // `component` path.\n resolvedName = undefined;\n missingEntryNotice = `Module \"${mod.id}\" has no entry points; \\`entry=\"${entry}\"\\` cannot be resolved.`;\n }\n return { resolvedName, missingEntryNotice };\n}\n\nfunction notice(message: string): VNode {\n return h(\"div\", { style: { padding: \"1rem\", color: \"#c53030\" } }, message);\n}\n\n/**\n * Host for a single module instance rendered outside any route — in a tab,\n * modal, or panel. Default exit behavior delegates to the `onExit` callback\n * provided by the shell; the module itself stays journey-unaware. The Vue\n * analog of the React `<ModuleTab>`.\n *\n * Authored with `defineComponent` + a render function (no SFC compiler in the\n * package build; see decision D4). The exit binding is created once at setup\n * via `useModuleExit` (Vue composables must run synchronously in `setup`),\n * reading the current module id / resolved entry through getters so a host that\n * swaps `module`/`entry` on the same instance still emits the right event.\n *\n * `inheritAttrs` is disabled and `input` is read from `attrs` so the component\n * can tell \"no `input` prop\" apart from an explicit `input={undefined}` — Vue\n * collapses both to `undefined` for a *declared* prop (an explicit `undefined`\n * triggers the prop default), so the presence check has to look at the raw\n * attrs bag, the analog of the React source's `\"input\" in props`.\n */\nexport const ModuleTab = defineComponent({\n name: \"ModuleTab\",\n inheritAttrs: false,\n props: {\n /** Full module descriptor — the shell looks this up by id. */\n module: {\n type: Object as PropType<ModuleDescriptor<any, any, any, any>>,\n required: true,\n },\n /**\n * Entry point name on the module. If omitted and the module exposes exactly\n * one entry, that entry is used automatically. If the module exposes several\n * entries, the name must be supplied — passing an unknown name renders an\n * error notice. If `entry` is omitted and the module has no entry points,\n * the component falls back to the legacy `component` field; passing `entry`\n * to such a module instead renders the error notice so misconfiguration is\n * surfaced.\n */\n entry: { type: String, default: undefined },\n /** Opaque tab id threaded through to `onExit` for the shell to close it. */\n tabId: { type: String, default: undefined },\n /**\n * Called when the module emits an exit. Runs *before* the provider's global\n * `onExit` dispatcher (via `<ModuleExitProvider>`, typically composed under\n * `<JourneyProvider>`), so the shell can close the tab first and let the\n * provider hook forward to analytics / routing.\n */\n onExit: { type: Function as PropType<(event: ModuleTabExitEvent) => void>, default: undefined },\n },\n setup(props, { attrs }) {\n // Unwrap the reactive prop proxy: Vue deeply proxies prop objects, which\n // would change the entry-object identity that keys `resolveEntryComponent`'s\n // per-entry `WeakMap` cache — breaking preload/render chunk sharing and the\n // memoized async wrapper. `toRaw` restores the descriptor the runtime and\n // preload paths hold.\n const rawModule = () => toRaw(props.module);\n const resolution = computed(() => resolveEntryName(rawModule(), props.entry));\n\n // Hook binding must be created once at setup — always bind, even when the\n // entry is missing. When missing, the returned `exit` is never invoked\n // because we render the error notice instead.\n const exit = useModuleExit(\n () => props.module.id,\n () => resolution.value.resolvedName ?? \"\",\n {\n tabId: () => props.tabId,\n localOnExit: () => props.onExit,\n },\n );\n\n return () => {\n const mod = rawModule();\n const { resolvedName, missingEntryNotice } = resolution.value;\n const entryPoint = resolvedName ? mod.entryPoints?.[resolvedName] : undefined;\n\n let content: VNode | null;\n if (missingEntryNotice) {\n content = notice(missingEntryNotice);\n } else if (entryPoint) {\n // The entry's declared input schema is the source of truth for whether\n // `input` is required. At runtime we don't reflect on the schema, but\n // we can still refuse to render with a visibly wrong `undefined` when\n // the caller forgot to pass it — that surfaces the misconfiguration\n // instead of letting the component throw deep inside its render.\n // Callers whose entry schema is `void` should pass `input={undefined}`\n // explicitly.\n const hasInput = \"input\" in attrs;\n const input = (attrs as Record<string, unknown>).input;\n if (input === undefined && !hasInput) {\n content = notice(\n `Module \"${mod.id}\" entry \"${resolvedName ?? \"\"}\" was rendered without an \\`input\\` prop. ` +\n `Pass \\`input={undefined}\\` explicitly if the entry accepts no input.`,\n );\n } else {\n const { Component } = resolveEntryComponent(entryPoint);\n const fallback = (entryPoint as { fallback?: VNode | (() => VNode) }).fallback;\n content = h(Suspense, null, {\n default: () => h(Component, { input, exit }),\n fallback: () => (typeof fallback === \"function\" ? fallback() : (fallback ?? null)),\n });\n }\n } else if (mod.component) {\n // Back-compat: render the legacy workspace component when the module\n // exposes no entry points. Entry contracts are opt-in.\n const Legacy = mod.component as Component;\n const input = (attrs as Record<string, unknown>).input;\n content = h(Legacy, { input, tabId: props.tabId });\n } else {\n content = notice(`Module \"${mod.id}\" has no entry points and no component.`);\n }\n\n return h(ModuleErrorBoundary, { moduleId: mod.id }, () => content);\n };\n },\n});\n","import { toValue, watch, watchEffect, type MaybeRefOrGetter } from \"vue\";\nimport {\n isDevEnv,\n type ExitFn,\n type ExitPointMap,\n type ExitPointSchema,\n} from \"@modular-frontend/core\";\n\n/**\n * Exit names whose schema declares a `void` output. The named-exit form\n * of {@link WaitForExitTimeoutChannel} narrows to these so a deadline\n * dispatching `resolve(name)` without an output is sound. Exits that\n * require a payload must use the function form\n * (`fire: (resolve) => resolve(\"name\", payload)`).\n */\ntype VoidExitName<TExits extends ExitPointMap> = {\n [K in keyof TExits & string]: TExits[K] extends ExitPointSchema<infer T>\n ? [T] extends [void]\n ? K\n : never\n : never;\n}[keyof TExits & string];\n\n/**\n * One channel that races against the others. `resolve` is the wrapped exit\n * dispatcher: calling it fires the named exit if no other channel has fired\n * yet, and is a no-op otherwise. The subscriber returns its own teardown,\n * which the composable calls **immediately** when any channel wins (not just on\n * unmount) so the losing channels stop doing wakeups between dispatch and\n * unmount.\n *\n * @example\n * ```ts\n * subscribe: (resolve) => {\n * const handler = (msg: WsFrame) => {\n * if (msg.type === \"process-ready\") resolve(\"ready\", { process: msg.payload });\n * };\n * ws.on(\"message\", handler);\n * return () => ws.off(\"message\", handler);\n * }\n * ```\n */\nexport type WaitForExitSubscribeChannel<TExits extends ExitPointMap> = (\n resolve: ExitFn<TExits>,\n) => () => void;\n\n/**\n * Periodic check that fires when the answer is ready. The interval is torn\n * down the moment any channel wins, so `check` is not invoked again after\n * settle. `check` itself should still cancel its own outstanding async\n * work on unmount where applicable (e.g. via `AbortController`).\n *\n * @example\n * ```ts\n * poll: {\n * intervalMs: 3000,\n * check: async (resolve) => {\n * const process = await api.getTranslationProcess(projectId);\n * if (process) resolve(\"ready\", { process });\n * },\n * }\n * ```\n */\nexport interface WaitForExitPollChannel<TExits extends ExitPointMap> {\n readonly intervalMs: number;\n readonly check: (resolve: ExitFn<TExits>) => void | Promise<void>;\n}\n\n/**\n * Deadline arm. The simple form fires a named exit; the function form runs\n * arbitrary code (e.g. dispatches a different exit based on `input`). Pass\n * `0` or a negative `ms` to disable the timeout for a render without\n * conditionally calling the composable.\n *\n * @example\n * ```ts\n * // Named form — exit must have a `void` output schema.\n * timeout: { ms: 60_000, fire: \"timedOut\" }\n *\n * // Function form — choose the exit (and its payload) at deadline time.\n * timeout: {\n * ms: 60_000,\n * fire: (resolve) => resolve(\"failed\", { reason: \"deadline-exceeded\" }),\n * }\n * ```\n */\nexport type WaitForExitTimeoutChannel<TExits extends ExitPointMap> = {\n readonly ms: number;\n} & (\n | { readonly fire: VoidExitName<TExits> }\n | { readonly fire: (resolve: ExitFn<TExits>) => void }\n);\n\nexport interface WaitForExitChannels<TExits extends ExitPointMap> {\n /** Push channel — websocket, SSE, push notification, server-sent intent. */\n readonly subscribe?: WaitForExitSubscribeChannel<TExits>;\n /** Polling fallback. Omit when the push channel is reliable. */\n readonly poll?: WaitForExitPollChannel<TExits>;\n /** Deadline arm. Omit when no fallback is needed. */\n readonly timeout?: WaitForExitTimeoutChannel<TExits>;\n}\n\n/**\n * Wait for one of several async channels to fire and dispatch a journey\n * exit when the first one resolves. Encapsulates the cancellation latch,\n * channel teardown, and first-wins coordination so step components stay\n * declarative. The Vue analog of the React `useWaitForExit` hook.\n *\n * Channel callbacks (`subscribe`, `poll.check`, `timeout.fire`) and the\n * `exit` dispatcher are captured live, so changing their identity between\n * renders does NOT restart the wait. The wait restarts on changes to scalar\n * configuration (`poll.intervalMs`, `timeout.ms`, the timeout's named-exit\n * form) so a deadline that genuinely shifts mid-wait is honored.\n *\n * Both `exit` and `channels` accept a plain value, a ref, or a getter\n * ({@link MaybeRefOrGetter}) — because `setup` runs once, a caller whose\n * channel configuration changes on the same instance should pass a getter (or\n * a ref) so the wait re-arms on scalar changes and picks up the latest\n * callbacks; plain values still work for hosts whose configuration is fixed for\n * the mount. This mirrors how the React hook re-reads its props on re-render.\n *\n * Conventions:\n *\n * - At least one of `subscribe`, `poll`, `timeout` should be present.\n * With none, the composable is a no-op until the component unmounts.\n *\n * - `resolve(name, output)` is the *latched* exit dispatcher. The latch\n * is local to one arm cycle; the runtime's step-token mechanism is the\n * global backstop (an exit dispatched after the step has advanced is\n * dropped by the runtime even if the local latch were missing).\n *\n * - The composable does not own the journey-level \"what does each exit mean\"\n * decision. That lives in the journey's `transitions` map. It only marshals\n * the channels.\n *\n * - **First-wins teardown is immediate.** When any channel calls `resolve`,\n * the *other* channels' teardowns run before the exit dispatches. A subscribe\n * that synchronously calls `resolve` during setup still has its `unsubscribe`\n * invoked; poll / timeout are not armed at all in that case.\n *\n * - The arm cycle is monolithic: changing any scalar dep (`pollInterval`,\n * `timeoutMs`, the timeout's named-exit form) tears down *all* live channels\n * and re-arms them. Callers that need independent per-channel lifecycles\n * should split into multiple `useWaitForExit` calls or manage that channel\n * themselves.\n */\nexport function useWaitForExit<TExits extends ExitPointMap>(\n exit: MaybeRefOrGetter<ExitFn<TExits>>,\n channels: MaybeRefOrGetter<WaitForExitChannels<TExits>>,\n): void {\n // Latest-callback holders so changing identities don't churn the arm cycle.\n // The `watchEffect` keeps them fresh on every reactive change to `exit` /\n // `channels`; the arm cycle reads them at arm time and inside the channel\n // callbacks — never the arguments directly.\n const latest: {\n exit: ExitFn<TExits> | undefined;\n subscribe: WaitForExitChannels<TExits>[\"subscribe\"];\n pollCheck: WaitForExitPollChannel<TExits>[\"check\"] | undefined;\n timeoutFire: WaitForExitTimeoutChannel<TExits>[\"fire\"] | undefined;\n } = {\n exit: undefined,\n subscribe: undefined,\n pollCheck: undefined,\n timeoutFire: undefined,\n };\n\n watchEffect(() => {\n const ch = toValue(channels);\n latest.exit = toValue(exit);\n latest.subscribe = ch.subscribe;\n latest.pollCheck = ch.poll?.check;\n latest.timeoutFire = ch.timeout?.fire;\n });\n\n const arm = (onCleanup: (fn: () => void) => void): void => {\n const ch = toValue(channels);\n const hasSubscribe = ch.subscribe != null;\n const hasPoll = ch.poll != null;\n const hasTimeout = ch.timeout != null;\n const pollInterval = ch.poll?.intervalMs;\n const timeoutMs = ch.timeout?.ms;\n\n let settled = false;\n const teardowns: Array<() => void> = [];\n\n const safeTeardown = (td: () => void) => {\n try {\n td();\n } catch (err) {\n if (isDevEnv()) {\n // A misbehaving channel teardown shouldn't block sibling teardowns\n // or prevent unmount — but it's almost always a bug worth flagging\n // in dev so the author sees it.\n console.warn(\"[useWaitForExit] channel teardown threw:\", err);\n }\n }\n };\n\n const drainTeardowns = () => {\n // Drain so a user-supplied teardown never runs twice — websocket\n // close / AbortController.abort / unsubscribe-from-emitter are not\n // always idempotent.\n const list = teardowns.splice(0);\n for (const td of list) safeTeardown(td);\n };\n\n // First-wins latched dispatcher. On the winning call we tear down every\n // *other* channel before dispatching, so the losing channels can't do\n // wakeups between dispatch and unmount.\n const resolve: ExitFn<TExits> = ((name: Parameters<ExitFn<TExits>>[0], ...args: unknown[]) => {\n if (settled) return;\n settled = true;\n drainTeardowns();\n (latest.exit as unknown as (n: any, ...a: unknown[]) => void)?.(name, ...args);\n }) as ExitFn<TExits>;\n\n const registerTeardown = (td: () => void) => {\n // A subscribe that synchronously called `resolve` will have drained\n // the (empty-at-that-moment) teardown list before its unsubscribe\n // was returned. We still need to release that listener, just not\n // via the list.\n if (settled) safeTeardown(td);\n else teardowns.push(td);\n };\n\n if (hasSubscribe) {\n const unsubscribe = latest.subscribe?.(resolve);\n if (typeof unsubscribe === \"function\") registerTeardown(unsubscribe);\n }\n\n // If a synchronous subscribe-resolve already won, don't arm the fallback\n // channels at all — the wait is already over.\n if (!settled && hasPoll && pollInterval !== undefined && pollInterval > 0) {\n const id = setInterval(() => {\n if (settled) return;\n try {\n const result = latest.pollCheck?.(resolve);\n if (result && typeof (result as PromiseLike<void>).then === \"function\") {\n // `Promise.resolve` adopts the thenable into a real Promise, so we\n // don't depend on the user's value having a `.catch`.\n Promise.resolve(result as PromiseLike<void>).catch(() => {\n // Polling failures are non-fatal: the next tick gets another\n // chance, and the push / timeout channels still race.\n });\n }\n } catch {\n // Sync-throw from `check` — same rationale as the async catch.\n }\n }, pollInterval);\n registerTeardown(() => clearInterval(id));\n }\n\n if (!settled && hasTimeout && timeoutMs !== undefined && timeoutMs > 0) {\n const id = setTimeout(() => {\n if (settled) return;\n const fire = latest.timeoutFire;\n if (typeof fire === \"string\") {\n // Sound because the named form's type is constrained to\n // `VoidExitName<TExits>` — exits whose schema is `void`.\n (resolve as unknown as (n: string) => void)(fire);\n } else if (typeof fire === \"function\") {\n fire(resolve);\n }\n }, timeoutMs);\n registerTeardown(() => clearTimeout(id));\n }\n\n onCleanup(() => {\n settled = true;\n drainTeardowns();\n });\n };\n\n // Re-arm only when the wait's *shape* changes. The key folds the presence of\n // each channel and the scalar values that change what the wait does; callback\n // identity is deliberately excluded (it lives in `latest`), so a re-render\n // that only hands over fresh closures does not restart. The named-exit form\n // of `timeout.fire` is part of the key because changing it changes what the\n // deadline fires; the function form is read live from `latest`.\n watch(\n () => {\n const ch = toValue(channels);\n const timeoutNamedExit =\n ch.timeout && typeof ch.timeout.fire === \"string\" ? ch.timeout.fire : undefined;\n return [\n ch.subscribe != null,\n ch.poll != null,\n ch.timeout != null,\n ch.poll?.intervalMs,\n ch.timeout?.ms,\n timeoutNamedExit,\n ].join(\"|\");\n },\n (_key, _old, onCleanup) => {\n arm(onCleanup);\n },\n { immediate: true },\n );\n}\n","import type { App } from \"vue\";\nimport type { ModuleExitEvent } from \"@modular-vue/vue\";\n\nimport type { JourneyRuntime } from \"@modular-frontend/journeys-engine\";\nimport { journeyKey, type JourneyProviderValue } from \"./provider.js\";\n\nexport interface ProvideJourneyRuntimeOptions {\n /**\n * Shell-wide fallback dispatcher for module exits fired outside a journey\n * step — the app-level equivalent of `<JourneyProvider :onModuleExit>`.\n */\n readonly onModuleExit?: (event: ModuleExitEvent) => void;\n}\n\n/**\n * App-level twin of `<JourneyProvider>`: install the journey runtime on a Vue\n * `App` via `app.provide` so `<JourneyOutlet>` and the instance composables\n * resolve it from context without a wrapping component in the render tree. The\n * journeys analog of `@modular-vue/vue`'s `provideNavigation` / `provideSlots`\n * standalone helpers.\n *\n * **You usually do not need to call this.** When the registry is built with\n * `journeysPlugin()`, the plugin's `appProvides` hook already threads the\n * runtime app-wide through `app.use(manifest)` (the router-owning `resolve()` /\n * `installModularApp` path) — `<JourneyOutlet>` resolves `journeyKey` with no\n * shell wiring. This helper is the explicit escape hatch for the cases that\n * bypass that:\n *\n * - a shell that constructs a `JourneyRuntime` by hand (no `journeysPlugin()`),\n * - installing the same runtime on a second app (SSR, multiple roots),\n * - overriding the app-wide runtime for a subtree that mounts its own app.\n *\n * ```ts\n * // Hand-wired runtime, no plugin:\n * const runtime = createJourneyRuntime(registered, { modules });\n * provideJourneyRuntime(app, runtime, { onModuleExit: (e) => reportExit(e) });\n * ```\n *\n * A local `<JourneyProvider :runtime>` mounted lower in the tree still wins for\n * its subtree (component-level `inject` shadows the app-level provide), so this\n * only supplies the default when no nearer provider is present.\n */\nexport function provideJourneyRuntime(\n app: App,\n runtime: JourneyRuntime,\n options?: ProvideJourneyRuntimeOptions,\n): void {\n const value: JourneyProviderValue = {\n runtime,\n onModuleExit: options?.onModuleExit,\n };\n app.provide(journeyKey, value);\n}\n","import type { SerializedJourney, SyncJourneyPersistence } from \"@modular-frontend/journeys-engine\";\n\n/**\n * The minimal structural slice of a Pinia store this adapter needs: read the\n * reactive `$state` and mutate it through `$patch`. A real Pinia store (option\n * or setup) satisfies this, but the shape is intentionally structural so this\n * package takes **no** `pinia` dependency — the caller brings their own store.\n *\n * This is the load-bearing design choice behind decision D3\n * (`docs/vue-support-tracker.md`): \"do not take a Pinia dependency in runtime\n * packages\". The adapter is a thin bridge over a store the consumer already\n * owns, not a reason for every `@modular-vue/journeys` consumer to pull Pinia.\n */\nexport interface PiniaJourneyPersistenceStore {\n /**\n * The store's reactive state object (`store.$state`). Typed as `object`\n * rather than `Record<string, unknown>` so a concrete Pinia store — whose\n * `$state` is an interface without an index signature — is structurally\n * assignable without the caller casting. The adapter narrows internally.\n */\n readonly $state: object;\n /** Apply a mutation to `$state` (`store.$patch((s) => { … })`). */\n $patch(mutator: (state: any) => void): void;\n}\n\nexport interface PiniaJourneyPersistenceOptions<TInput> {\n /**\n * Compute the persistence key from the journey id and starting input.\n * Must be deterministic — `runtime.start()` probes this key to find an\n * existing instance and achieve start-means-resume idempotency.\n */\n readonly keyFor: (ctx: { journeyId: string; input: TInput }) => string;\n /**\n * The Pinia store that owns the serialized-journey record, or a lazy getter\n * returning it (or `null`). The getter is invoked on every call, mirroring\n * `createWebStoragePersistence`'s `storage` option — pass a getter when the\n * store is created inside a Pinia scope resolved after this adapter is\n * constructed, or `null` to force the no-op (server / no-store) path.\n */\n readonly store: PiniaJourneyPersistenceStore | (() => PiniaJourneyPersistenceStore | null);\n /**\n * Property on the store's `$state` that holds the\n * `Record<string, SerializedJourney<TState>>` map. Defaults to `\"journeys\"`.\n * The property is created on first `save` if absent.\n */\n readonly stateKey?: string;\n /**\n * When true (default), stored blobs are deep-cloned on both `save` and\n * `load`, so `load` returns a plain object detached from the store: callers\n * mutating it cannot corrupt the reactive state, and a mutated source blob\n * cannot corrupt what was persisted. The clone is a structural JSON round-trip\n * (`JSON.parse(JSON.stringify(...))`), which matches the `SerializedJourney`\n * contract the engine persists — plain JSON — but, like any storage adapter,\n * drops `undefined` fields and cannot carry `Date` / `Map` / `Set`. Journey\n * blobs are already JSON by construction, so this only bites if a custom\n * persistence layer routes non-serializable state through here.\n *\n * When false, the clone is skipped. Note Pinia still wraps stored state in a\n * reactive proxy, so `load` never returns the exact reference passed to\n * `save`; instead it returns the **live** store entry (mutations to it are\n * visible on the next `load` and to Pinia devtools). Use only when you have\n * verified nobody mutates the loaded blob out from under the store.\n */\n readonly clone?: boolean;\n}\n\n/**\n * `JourneyPersistence` backed by a Pinia store — the Vue-ecosystem analog of\n * `createWebStoragePersistence`, keeping journey state inside the app's\n * existing Pinia store tree instead of a parallel storage mechanism.\n *\n * Because the record lives in Pinia, in-flight journeys participate in the\n * app's Pinia devtools / timeline, and a single `store.$reset()` (or clearing\n * the record) drops every persisted journey through one path the app already\n * owns.\n *\n * Semantics match the stock adapters exactly: `keyFor` derives the key,\n * `load` returns the stored blob (or `null`), `save` writes it, `remove`\n * deletes it — so `runtime.start()` resumes an in-flight instance for the same\n * `keyFor(input)` rather than minting a fresh one.\n *\n * Pure-client-safe like the web-storage adapter: pass `store` as a getter that\n * returns `null` under SSR and all four methods no-op, so the runtime mints a\n * fresh instance server-side.\n *\n * ```ts\n * // A tiny store whose only job is to hold serialized journeys.\n * const useJourneyStore = defineStore(\"journeys\", {\n * state: () => ({ journeys: {} as Record<string, SerializedJourney<WizardState>> }),\n * });\n *\n * export const wizardPersistence = createPiniaJourneyPersistence<{ frameId: string }, WizardState>({\n * keyFor: ({ journeyId, input }) => `journey:${input.frameId}:${journeyId}`,\n * store: () => useJourneyStore(),\n * });\n * ```\n */\nexport function createPiniaJourneyPersistence<TInput, TState>(\n options: PiniaJourneyPersistenceOptions<TInput>,\n): SyncJourneyPersistence<TState, TInput> {\n const { keyFor } = options;\n const stateKey = options.stateKey ?? \"journeys\";\n const shouldClone = options.clone !== false;\n\n const copy = (blob: SerializedJourney<TState>): SerializedJourney<TState> =>\n shouldClone ? (JSON.parse(JSON.stringify(blob)) as SerializedJourney<TState>) : blob;\n\n const resolve = (): PiniaJourneyPersistenceStore | null => {\n const s = typeof options.store === \"function\" ? options.store() : options.store;\n return s ?? null;\n };\n\n const readRecord = (\n s: PiniaJourneyPersistenceStore,\n ): Record<string, SerializedJourney<TState>> | undefined => {\n const rec = (s.$state as Record<string, unknown>)[stateKey];\n return rec && typeof rec === \"object\"\n ? (rec as Record<string, SerializedJourney<TState>>)\n : undefined;\n };\n\n return {\n keyFor,\n load: (key) => {\n const s = resolve();\n if (!s) return null;\n const blob = readRecord(s)?.[key];\n return blob ? copy(blob) : null;\n },\n save: (key, blob) => {\n const s = resolve();\n if (!s) return;\n const stored = copy(blob);\n s.$patch((state) => {\n const existing = state[stateKey];\n const rec = (\n existing && typeof existing === \"object\" ? existing : (state[stateKey] = {})\n ) as Record<string, SerializedJourney<TState>>;\n rec[key] = stored;\n });\n },\n remove: (key) => {\n const s = resolve();\n if (!s) return;\n s.$patch((state) => {\n const rec = state[stateKey] as Record<string, SerializedJourney<TState>> | undefined;\n if (rec) delete rec[key];\n });\n },\n };\n}\n","import { defineComponent, h } from \"vue\";\nimport {\n provideBinding,\n type ModuleExitHandler,\n type VueAppProvidingPlugin,\n} from \"@modular-vue/vue\";\nimport type {\n JourneyRuntime,\n ModuleTypeMap,\n NavigationItemBase,\n RegistryPlugin,\n UiComponent,\n UiNode,\n} from \"@modular-frontend/core\";\nimport {\n createJourneyRuntime,\n JourneyValidationError,\n validateJourneyContracts,\n validateJourneyDefinition,\n} from \"@modular-frontend/journeys-engine\";\nimport type {\n AnyJourneyDefinition,\n JourneyDefinition,\n JourneyNavContribution,\n JourneyRegisterOptions,\n RegisteredJourney,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { JourneyProvider, journeyKey, type JourneyProviderValue } from \"./provider.js\";\n\n/**\n * Methods the journeys plugin contributes to the registry. Registered plugins\n * type-intersect with the base registry so shells call\n * `registry.registerJourney(...)` with full type support. The neutral analog\n * of the React `JourneysPluginExtension`.\n */\nexport interface JourneysPluginExtension {\n /**\n * Register a journey definition. The structural shape is validated\n * immediately (missing `id` / `version` / `transitions` etc.); module-level\n * contracts are validated at `resolveManifest()` / `resolve()` time.\n *\n * `options.persistence` is typed against the journey's state, and\n * `options.nav.buildInput` is typed against the journey's input — pass a\n * typed definition and both are checked end-to-end.\n */\n registerJourney<TModules extends ModuleTypeMap, TState, TInput, TOutput = unknown>(\n definition: JourneyDefinition<TModules, TState, TInput, TOutput>,\n options?: JourneyRegisterOptions<TState, TInput>,\n ): void;\n}\n\n/**\n * Default shape the journeys plugin emits for each `nav`-carrying journey.\n * When {@link JourneysPluginOptions.buildNavItem} is provided, the plugin\n * hands this default (plus the journey's id and buildInput factory) to the\n * adapter so apps can reshape the item into their narrowed `TNavItem`.\n */\nexport interface JourneyDefaultNavItem extends NavigationItemBase {\n readonly label: string;\n /**\n * Always empty for a journey launcher — the dispatchable action lives in\n * {@link JourneyDefaultNavItem.action}, so there is no URL to follow. An\n * empty string keeps the structural `NavigationItemBase.to` satisfied\n * without suggesting the shell should treat this item as a link.\n */\n readonly to: \"\";\n readonly icon?: string | UiComponent<{ className?: string }>;\n readonly group?: string;\n readonly order?: number;\n readonly hidden?: boolean;\n readonly meta?: unknown;\n readonly action: {\n readonly kind: \"journey-start\";\n readonly journeyId: string;\n readonly buildInput?: (ctx?: unknown) => unknown;\n };\n}\n\n/**\n * Signature for the optional typed adapter that reshapes the plugin's default\n * nav item into the app's narrowed `TNavItem`. The adapter is called once per\n * `nav`-carrying journey at manifest time.\n */\nexport type JourneyNavItemBuilder<TNavItem extends NavigationItemBase> = (\n defaults: JourneyDefaultNavItem,\n raw: JourneyNavContribution<unknown> & { readonly journeyId: string },\n) => TNavItem;\n\nexport interface JourneysPluginOptions<\n TNavItem extends NavigationItemBase = JourneyDefaultNavItem,\n> {\n /**\n * Enable verbose transition / rollback logging in the runtime. Defaults to\n * `false`; plugins propagate the registry-level debug flag when set.\n */\n readonly debug?: boolean;\n /**\n * Forwarded onto `<JourneyProvider>` as the shell-wide `onModuleExit`\n * handler. Use it as a default place to close tabs / forward analytics when\n * a module exit isn't consumed by an explicit prop. Reuses the upstream\n * `ModuleExitHandler` so the full `ModuleExitEvent` (incl. `routeId`) stays\n * in sync with `@modular-vue/vue`.\n */\n readonly onModuleExit?: ModuleExitHandler;\n /**\n * Optional adapter that reshapes the plugin's default nav item into the\n * app's narrowed `TNavItem`. Apps that use a typed `NavigationItem` alias\n * (typed label union, typed action union, typed meta bag) should supply this\n * so contributed items land in `manifest.navigation` with the correct\n * narrowed type. When omitted, the plugin emits items as\n * {@link JourneyDefaultNavItem} and the framework widens them to `TNavItem`\n * at the assembly boundary.\n */\n readonly buildNavItem?: JourneyNavItemBuilder<TNavItem>;\n}\n\n/**\n * Creates the journeys plugin. Pass to `registry.use(journeysPlugin())` (or\n * `createRegistry({ plugins: [...] })`) to enable journey registration and\n * outlet rendering without the runtime packages depending on\n * `@modular-vue/journeys` directly. The Vue analog of the React\n * `journeysPlugin`; the only framework-specific piece is `providers()`, which\n * contributes a Vue `<JourneyProvider>` instead of a React one.\n *\n * The plugin:\n * - contributes `registerJourney(...)` onto the registry (type-safe)\n * - validates contracts against registered modules at resolve time\n * - produces a `JourneyRuntime` on `manifest.extensions.journeys` (also\n * surfaced as the `manifest.journeys` convenience alias)\n * - wraps the provider stack in `<JourneyProvider :runtime=\"…\" />`\n *\n * **Instantiate per registry.** The returned object closes over a\n * journey-registration list; passing the same instance to two\n * `createRegistry()` calls causes them to share that list. Call\n * `journeysPlugin()` once per registry.\n */\nexport function journeysPlugin<TNavItem extends NavigationItemBase = JourneyDefaultNavItem>(\n options: JourneysPluginOptions<TNavItem> = {},\n): RegistryPlugin<\"journeys\", JourneysPluginExtension, JourneyRuntime> &\n VueAppProvidingPlugin<JourneyRuntime> {\n const registered: RegisteredJourney[] = [];\n\n return {\n name: \"journeys\",\n\n extend() {\n return {\n registerJourney<TModules extends ModuleTypeMap, TState, TInput, TOutput = unknown>(\n definition: JourneyDefinition<TModules, TState, TInput, TOutput>,\n regOpts?: JourneyRegisterOptions<TState, TInput>,\n ): void {\n const def = definition as AnyJourneyDefinition;\n const issues = validateJourneyDefinition(def);\n if (issues.length > 0) {\n throw new JourneyValidationError(issues);\n }\n registered.push({\n definition: def,\n options: regOpts as JourneyRegisterOptions | undefined,\n });\n },\n };\n },\n\n validate({ modules }) {\n if (registered.length > 0) {\n validateJourneyContracts(registered, modules);\n }\n },\n\n onResolve({ moduleDescriptors, debug }) {\n return createJourneyRuntime(registered, {\n modules: moduleDescriptors,\n debug: options.debug ?? debug,\n });\n },\n\n contributeNavigation() {\n const items: NavigationItemBase[] = [];\n for (const reg of registered) {\n const nav = reg.options?.nav;\n if (!nav) continue;\n const defaults: JourneyDefaultNavItem = {\n label: nav.label,\n to: \"\",\n ...(nav.icon !== undefined ? { icon: nav.icon } : {}),\n ...(nav.group !== undefined ? { group: nav.group } : {}),\n ...(nav.order !== undefined ? { order: nav.order } : {}),\n ...(nav.hidden !== undefined ? { hidden: nav.hidden } : {}),\n ...(nav.meta !== undefined ? { meta: nav.meta } : {}),\n action: {\n kind: \"journey-start\",\n journeyId: reg.definition.id,\n ...(nav.buildInput ? { buildInput: nav.buildInput } : {}),\n },\n };\n if (options.buildNavItem) {\n items.push(\n options.buildNavItem(defaults, {\n ...(nav as JourneyNavContribution<unknown>),\n journeyId: reg.definition.id,\n }),\n );\n } else {\n items.push(defaults);\n }\n }\n return items;\n },\n\n providers({ runtime }) {\n const onModuleExit = options.onModuleExit;\n const BoundJourneyProvider = defineComponent({\n name: \"JourneysPluginProvider\",\n setup(_props, { slots }) {\n return () => h(JourneyProvider, { runtime, onModuleExit }, () => slots.default?.());\n },\n });\n return [BoundJourneyProvider as unknown as UiComponent<{ children: UiNode }>];\n },\n\n // Install-mode twin of `providers()`. The framework-mode component form\n // (`resolveManifest`) mounts the `<JourneyProvider>` above; the router-owning\n // form (`resolve()` / Nuxt) has no root component to wrap, so it threads the\n // journey runtime app-wide via `app.provide` from this binding instead —\n // exactly how `navigationKey` / `modulesKey` reach the app there. The runtime\n // never imports `journeyKey`; the plugin supplies key + value.\n //\n // Journey context ONLY is bound here (single responsibility). The component\n // form's `<JourneyProvider>` additionally composes `<ModuleExitProvider>`,\n // but module-exit dispatching is the module-hosting layer's concern, not the\n // journeys binding's: in the router-owning path, exits fired outside a step\n // are the shell's to wire (it can read `manifest.onModuleExit`). Binding a\n // module-exit dispatcher off the journeys plugin here would conflate the two\n // and compete with that channel. `onModuleExit` stays on the value only for\n // introspection parity with the component provider.\n appProvides({ runtime }) {\n const value: JourneyProviderValue = { runtime, onModuleExit: options.onModuleExit };\n return [provideBinding(journeyKey, value)];\n },\n };\n}\n"],"mappings":";;;;;AAkCA,IAAa,IAAiD,OAAO,qBAAqB,GAsB7E,IAAkB,EAAgB;CAC7C,MAAM;CACN,OAAO;EAEL,SAAS;GAAE,MAAM;GAAoC,UAAU;EAAK;EAEpE,cAAc;GAAE,MAAM;GAAyC,SAAS,KAAA;EAAU;CACpF;CACA,MAAM,GAAO,EAAE,YAAS;EAYtB,OADA,EAAQ,GAAY;GALlB,SAAS,EAAM;GACf,IAAI,eAAe;IACjB,OAAO,EAAM;GACf;EAEkB,CAAK,SACZ,EAAE,GAAoB,EAAE,QAAQ,EAAM,aAAa,SAAS,EAAM,UAAU,CAAC;CAC5F;AACF,CAAC;AAGD,SAAgB,IAAiD;CAC/D,OAAO,EAAO,GAAY,IAAI;AAChC;;;AC7DA,IAAM,KAAkB,IAElB,IAAqC,OAAO,OAAO,CAAC,CAAC;AAsB3D,SAAgB,EACd,GACA,GACoC;CACpC,IAAM,IAAW,EAAmC,IAAI,GAElD,UAA4C;EAChD,IAAM,IAAK,EAAQ,CAAU;EAE7B,OADI,CAAC,KAAW,CAAC,IAAW,OACrB,EAAQ,YAAY,CAAE;CAC/B;CAeA,OAbA,GAAa,MAAc;EACzB,IAAM,IAAK,EAAQ,CAAU;EAC7B,IAAI,CAAC,KAAW,CAAC,GAAI;GACnB,EAAS,QAAQ;GACjB;EACF;EAKA,AAJA,EAAS,QAAQ,EAAQ,YAAY,CAAE,GAIvC,EAHoB,EAAQ,UAAU,SAAU;GAC9C,EAAS,QAAQ,EAAY;EAC/B,CACU,CAAW;CACvB,CAAC,GAEM;AACT;AAgBA,SAAgB,EACd,GACA,GACA,GACmC;CACnC,IAAM,IAAW,EAAkC,CAAW,GAK1D,IAAyB,MACvB,KAAY,MAAuC;EACvD,IAAM,IAAM,EAAM,KAAK,GAAG;EACtB,MAAY,MAChB,IAAU,GACV,EAAS,QAAQ,EAAM,WAAW,IAAI,IAAc;CACtD;CA+CA,OA7CA,GAAa,MAAc;EACzB,IAAM,IAAO,EAAQ,CAAM,GACrB,IAAa,EAAQ,CAAO;EAClC,IAAI,CAAC,KAAW,CAAC,GAAM;GACrB,EAAS,CAAW;GACpB;EACF;EAEA,IAAM,oBAAS,IAAI,IAA4B,GAC3C,IAAU,IAER,UAAqB;GACzB,IAAI,GAAS;GACb,IAAM,oBAAO,IAAI,IAAgB,GAC3B,IAAsB,CAAC,GACzB,IAAwB,GACxB,IAAQ;GACZ,OAAO,KAAM,IAAQ,MACf,GAAK,IAAI,CAAE,IADqB;IAIpC,AAFA,EAAK,IAAI,CAAE,GACX,EAAM,KAAK,CAAE,GACR,EAAO,IAAI,CAAE,KAChB,EAAO,IAAI,GAAI,EAAQ,UAAU,GAAI,CAAM,CAAC;IAE9C,IAAM,IAAO,EAAQ,YAAY,CAAE;IAEnC,AADA,IAAK,KAAc,IAAO,EAAK,gBAAgB,MAC/C,KAAS;GACX;GACA,KAAK,IAAM,CAAC,GAAc,MAAU,GAClC,AAAK,EAAK,IAAI,CAAY,MACxB,EAAM,GACN,EAAO,OAAO,CAAY;GAG9B,EAAS,CAAK;EAChB;EAGA,AADA,EAAO,GACP,QAAgB;GACd,IAAU;GACV,KAAK,IAAM,KAAS,EAAO,OAAO,GAAG,EAAM;GAC3C,EAAO,MAAM;EACf,CAAC;CACH,CAAC,GAEM;AACT;AAQA,SAAgB,EACd,GACA,GACA,GACgC;CAChC,IAAM,IAAQ,EAAa,GAAS,GAAQ,CAAO;CACnD,OAAO,QAAe,EAAM,MAAM,EAAM,MAAM,SAAS,MAAM,EAAQ,CAAM,CAAC;AAC9E;;;ACpJA,SAAgB,GACd,GAC4B;CAC5B,IAAM,IAAO,EAAmB,CAAU;CAC1C,OAAO,QAAgB,EAAK,QAAS,EAAK,MAAM,QAAmB,IAAK;AAC1E;AAUA,SAAgB,EACd,GACoC;CAEpC,OAAO,EADK,EACe,CAAA,EAAK,WAAW,MAAM,CAAU;AAC7D;AAYA,SAAgB,GACd,GAC4B;CAC5B,IAAM,IAAO,EAA6B,CAAc;CACxD,OAAO,QAAgB,EAAK,QAAS,EAAK,MAAM,QAAmB,IAAK;AAC1E;AAaA,SAAgB,EACd,GACoC;CAEpC,IAAM,IADM,EACI,CAAA,EAAK,WAAW;CAEhC,OAAO,EAAoB,GADZ,EAAU,GAAS,GAAgB,EACd,CAAM;AAC5C;ACfA,IAAa,IAAgB,EAAgB;CAC3C,MAAM;CACN,OAAO;EAOL,SAAS;GAAE,MAAM;GAAoC,SAAS,KAAA;EAAU;EACxE,YAAY;GAAE,MAAM;GAAgC,UAAU;EAAK;EAMnE,SAAS;GACP,MAAM;GACN,SAAS,KAAA;EACX;EAEA,iBAAiB;GACf,MAAM;GACN,SAAS,KAAA;EACX;EACA,YAAY;GACV,MAAM;GACN,SAAS,KAAA;EACX;EACA,aAAa;GACX,MAAM;GAGN,SAAS,KAAA;EACX;EAOA,UAAU;GAAE,MAAM;GAAS,SAAS;EAAK;EAKzC,YAAY;GAAE,MAAM;GAAQ,SAAS;EAAkB;EAKvD,mBAAmB;GACjB,MAAM;GACN,SAAS,KAAA;EACX;EAMA,gBAAgB;GAAE,MAAM;GAA+B,SAAS,KAAA;EAAU;EAQ1E,SAAS;GACP,MAAM,CAAC,SAAS,MAAM;GACtB,SAAS;EACX;CACF;CACA,MAAM,GAAO;EACX,IAAM,IAAU,EAAO,GAAY,IAAI,GAMjC,IAAU,EAAM,EAAM,WAAW,GAAS,WAAW,KAAA,CAAS,KAAK;EACzE,IAAI,CAAC,GACH,MAAU,MACR,8GACF;EAEF,IAAM,IAAY,GAAa,CAAO,GAQhC,UACJ,EAAM,EAAM,WAAW,EAAU,WAAW,GAOxC,IAAe,EAAoB,SAAe,EAAM,UAAU,GAClE,IAAS,EACb,SACM,EAAM,kBACN,EAAM,QACd,GAIM,IAAe,EAAoB,GAAS,CAAM,GAClD,IAAW,QACf,EAAO,UAAU,EAAM,aAAa,EAAa,QAAQ,EAAa,KACxE,GAEM,IAAW,EAAI,CAAC;EAYtB,QAAkB;GAChB,IAAM,IAAa,EAAM;GACzB,qBAAqB;IACnB,IAAM,IAAS,EAAU,YAAY,CAAU;IAC1C,MACD,EAAO,WAAW,YAAY,EAAO,WAAW,aAChD,EAAO,UAAU,OAAO,KAK5B,EAAQ,IAAI,GAAY,EAAE,QAAQ,YAAY,GAAG,EAAE,OAAO,GAAK,CAAC;GAClE,CAAC;EACH,CAAC;EAMD,IAAM,oBAAoB,IAAI,IAAgB;EA4F9C,OA3FA,EACE,IACC,MAAS;GACH,MACD,EAAK,WAAW,eAAe,EAAK,WAAW,aAC/C,EAAkB,IAAI,EAAK,EAAE,MACjC,EAAkB,IAAI,EAAK,EAAE,GAC7B,EAAM,aAAa;IACjB,QAAQ,EAAK;IACb,SAAS,EAAK;IACd,YAAY,EAAK;IACjB,WAAW,EAAK;GAClB,CAAC;EACH,GACA,EAAE,WAAW,GAAK,CACpB,GAMA,QAAgB;GACd,QACQ;IACJ,IAAM,IAAO,EAAS;IACtB,OAAO;KACL,EAAM;KACN,GAAM,WAAW;KACjB,GAAM,MAAM;KACZ,GAAM,MAAM;KACZ,GAAM;IACR;GACF,IACC,CAAC,GAAS,GAAU,GAAc,GAAe,IAAY,GAAM,MAAc;IAEhF,IADI,MAAY,MAAS,CAAC,KACtB,CAAC,KAAgB,CAAC,KAAiB,CAAC,GAAW;IACnD,IAAM,IAAM,EAAU,gBAAgB,CAAS;IAC/C,IAAI,CAAC,GAAK;IACV,IAAM,IAAO,MAAY,eAAe,eAAe,WACjD,IAAU,GACd,EAAI,YACJ,EAAW,GACX,GACA,GACA,CACF;IACA,IAAI,EAAQ,WAAW,GAAG;IAE1B,IAAI,IAAY,IACV,UAAkB;KAClB,QACJ,KAAK,IAAM,KAAS,GAClB,IAAI;MAMF,EAA2B,CAAK,CAAC,CAC9B,QAAQ,CAAC,CACT,YAAY,CAAC,CAAC;KACnB,QAAQ,CAER;IAEJ,GAEM,IACJ,WAGA,qBACI,IAAS,WACZ,oBACC,GACA;IAMJ,AALI,OAAO,KAAU,aACnB,IAAa,EAAM,GAAK,EAAE,SAAS,IAAK,CAAC,IAEzC,IAAgB,WAAW,GAAK,CAAC,GAEnC,QAAgB;KAGd,AAFA,IAAY,IACR,MAAe,KAAA,KAAa,OAAO,KAAU,cAAY,EAAM,CAAU,GACzE,MAAkB,KAAA,KAAW,aAAa,CAAa;IAC7D,CAAC;GACH,GACA,EAAE,WAAW,GAAK,CACpB;EACF,CAAC,SAEY;GACX,IAAM,IAAO,EAAS;GACtB,IAAI,CAAC,GAAM,OAAO;GAClB,IAAI,EAAK,WAAW,WAAW;IAC7B,IAAM,IAAK,EAAM;IACjB,QAAQ,OAAO,KAAO,aAAa,EAAG,IAAI,MAAO;GACnD;GACA,IAAI,EAAK,WAAW,eAAe,EAAK,WAAW,WAAW,OAAO;GAErE,IAAM,IAAO,EAAK;GAClB,IAAI,CAAC,GAAM,OAAO;GAGlB,IAAM,IADU,EACJ,CAAA,CAAQ,EAAK,WACnB,IAAQ,GAAK,cAAc,EAAK;GACtC,IAAI,CAAC,KAAO,CAAC,GAEX,OAAO,EADU,EAAM,qBAAqB,IACzB;IAAE,UAAU,EAAK;IAAU,OAAO,EAAK;GAAM,CAAC;GAEnE,IAAI,CAAC,GAAwB,CAAK,GAMhC,OAAO,EADW,EAAM,kBAAkB,GACtB;IAClB,UAAU,EAAK;IACf,OAAO,gBAAI,MACT,kCAAkC,EAAK,SAAS,GAAG,EAAK,MAAM,wBAC9C,KAAK,UAAU,EAAM,UAAU,EAAE,kLAGnD;GACF,CAAC;GAKH,IAAM,IAAS,EAAU,YAAY,EAAK,EAAE,GACtC,IAAM,EAAU,gBAAgB,EAAK,SAAS;GACpD,IAAI,CAAC,KAAU,CAAC,GAAK,OAAO;GAC5B,IAAM,EAAE,SAAM,WAAQ,iBAAc,EAAU,oBAAoB,GAAQ,CAAG,GAEvE,KAAe,MAAuB;IAK1C,EAAU,qBAAqB,EAAK,IAAI,GAAK,CAAI;IACjD,IAAI,IAAS,EAAM,cAAc,GAAK,EAAE,QAAK,CAAC,KAAK;IAMnD,IALI,MAAW,YACR,EAAU,eAAe,EAAK,IAAI,EAAM,UAAU,MACrD,IAAS,WAGT,MAAW,SAAS;KAEtB,EAAQ,IAAI,EAAK,IAAI;MAAE,QAAQ;MAAmB,OAAO;KAAI,CAAC;KAC9D;IACF;IACA,AAAI,MAAW,YACb,EAAS,SAAS;GAGtB,GAEM,EAAE,WAAW,MAAkB,EAAsB,CAAK,GAC1D,IAAU,GAAG,EAAO,UAAU,GAAG,EAAS,SAC1C,IAAiB,EAA+C,UAChE,IAAiB,EAAM;GAE7B,OAAO,EACL,IACA;IACE,KAAK;IACL,UAAU,EAAK;IACf,SAAS;IACT,gBAAgB,EAAM;GACxB,SAEE,EAAE,GAAU,MAAM;IAChB,eACE,EAAE,GAA4B;KAC5B,OAAO,EAAK;KACZ;KACA;KACA;IACF,CAAC;IACH,gBAAgB;KACd,IAAM,IAAK,KAAiB;KAC5B,QAAQ,OAAO,KAAO,aAAa,EAAG,IAAI,MAAO;IACnD;GACF,CAAC,CACL;EACF;CACF;AACF,CAAC;AAQD,SAAS,GACP,GACA,GACA,GACA,GACA,GACkC;CAClC,IAAM,oBAAO,IAAI,IAAY,GACvB,IAA+B,CAAC,GAChC,IAAc,EAAW;CAG/B,IAAI,CAAC,GAAa,OAAO;CAEzB,IAAM,KAAe,GAAkB,MAA4B;EAEjE,IADI,CAAC,KAAY,CAAC,KACd,MAAa,KAAmB,MAAc,GAAc;EAChE,IAAM,IAAU,GAAG,EAAS,GAAG;EAC/B,IAAI,EAAK,IAAI,CAAO,GAAG;EACvB,EAAK,IAAI,CAAO;EAChB,IAAM,IAAQ,EAAQ,EAAS,EAAE,cAAc;EAC/C,AAAI,KAAO,EAAI,KAAK,CAAK;CAC3B;CAEA,IAAI,MAAS,WAAW;EACtB,IAAM,IAAW,EAAY,EAAgB,GAAG;EAChD,IAAI,CAAC,GAAU,OAAO;EACtB,KAAK,IAAM,KAAS,OAAO,OAAO,CAAQ,GACnC,MAAsB,CAAK,GAChC,KAAK,IAAM,KAAU,EAAM,SAGrB,OAAO,KAAW,YACtB,EAAY,EAAO,QAAQ,EAAO,KAAK;EAG3C,OAAO;CACT;CAKA,KAAK,IAAM,CAAC,GAAU,MAAc,OAAO,QAAQ,CAAW,GACvD,OACL;OAAK,IAAM,CAAC,GAAW,MAAY,OAAO,QAAQ,CAAS,GACzD,MAAY,GAAU,CAAS,GAC1B,GACL;QAAK,IAAM,KAAS,OAAO,OAAO,CAAO,GAClC,MAAsB,CAAK,GAChC,KAAK,IAAM,KAAU,EAAM,SACrB,OAAO,KAAW,YACtB,EAAY,EAAO,QAAQ,EAAO,KAAK;EAE3C;CACF;CAEF,OAAO;AACT;AAQA,SAAS,GAAwB,GAA6D;CAE5F,OADK,MAAM,QAAQ,EAAM,UAAU,IAC5B,EAAM,WAAW,SAAS,SAAS,IADG;AAE/C;AAEA,IAAM,KAAkB,EAAgB;CACtC,MAAM;CACN,OAAO;EACL,UAAU;GAAE,MAAM;GAAQ,UAAU;EAAK;EACzC,OAAO;GAAE,MAAM;GAAQ,UAAU;EAAK;CACxC;CACA,MAAM,GAAO;EACX,aACE,EACE,OACA,EAAE,OAAO;GAAE,SAAS;GAAQ,OAAO;EAAU,EAAE,GAC/C,6BAA6B,EAAM,SAAS,GAAG,EAAM,MAAM,6BAC7D;CACJ;AACF,CAAC,GAEK,IAAe,EAAgB;CACnC,MAAM;CACN,OAAO;EACL,UAAU;GAAE,MAAM;GAAQ,UAAU;EAAK;EACzC,OAAO;GAAE,MAAM;GAAsC,SAAS,KAAA;EAAU;CAC1E;CACA,MAAM,GAAO;EACX,aAAa;GACX,IAAM,IAAe,EAAM,OACrB,IAAU,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG;GAC/D,OAAO,EACL,OACA;IACE,OAAO;KACL,SAAS;KACT,QAAQ;KACR,cAAc;KACd,QAAQ;IACV;IACA,MAAM;IACN,2BAA2B,EAAM;GACnC,GACA,CACE,EACE,MACA,EAAE,OAAO;IAAE,OAAO;IAAW,QAAQ;GAAe,EAAE,GACtD,WAAW,EAAM,SAAS,uBAC5B,GACA,EACE,OACA,EAAE,OAAO;IAAE,UAAU;IAAY,OAAO;IAAW,YAAY;GAAW,EAAE,GAC5E,CACF,CACF,CACF;EACF;CACF;AACF,CAAC,GAcK,KAAoB,EAAgB;CACxC,MAAM;CACN,OAAO;EACL,UAAU;GAAE,MAAM;GAAQ,UAAU;EAAK;EACzC,SAAS;GAAE,MAAM;GAA8C,UAAU;EAAK;EAC9E,gBAAgB;GAAE,MAAM;GAA+B,SAAS,KAAA;EAAU;CAC5E;CACA,MAAM,GAAO,EAAE,YAAS;EACtB,IAAM,IAAQ,EAAa,IAAI;EAO/B,OANA,GAAiB,OACf,EAAM,QAAQ,GACd,EAAM,QAAQ,CAAG,GAEV,GACR,SAEK,EAAM,SAAS,OAIZ,EAAM,UAAU,IAFd,EADe,EAAM,kBAAkB,GACtB;GAAE,UAAU,EAAM;GAAU,OAAO,EAAM;EAAM,CAAC;CAI9E;AACF,CAAC;AAUD,SAAgB,GACd,GACA,GACmC;CACnC,OAAO,EAAa,GAAS,GAAQ,EAAI;AAC3C;;;AC3hBA,SAAgB,GAA0B,GAA8C;CAuBtF,OAAO;EACL,MAAM,GAAc,GAAO;GAIzB,OAAO,EAAQ,MAAM,GAAc,CAAK;EAC1C;EACA,IAAI,GAAY;GAKd,EAAQ,IAAI,GAAY,EAAE,QAAQ,cAAc,CAAC;EACnD;EACA,QApCa,EAAgB;GAC7B,MAAM;GACN,OAAO;IACL,YAAY;KAAE,MAAM;KAAgC,UAAU;IAAK;IACnE,iBAAiB;KACf,MAAM;KACN,SAAS,KAAA;IACX;GACF;GACA,MAAM,GAAO;IAIX,aACE,EAAE,GAAe;KACf;KACA,YAAY,EAAM;KAClB,iBAAiB,EAAM;IACzB,CAAC;GACL;EACF,CAgBE;CACF;AACF;;;ACtCA,IAAM,oBAAa,IAAI,QAAsD;AAE7E,SAAS,EAAU,GAAuD;CACxE,IAAI,IAAS,EAAW,IAAI,CAAO;CAKnC,OAJK,MACH,oBAAS,IAAI,IAAI,GACjB,EAAW,IAAI,GAAS,CAAM,IAEzB;AACT;AAoEA,SAAgB,EACd,GACA,GACA,IAAiC,CAAC,GAChB;CAClB,IAAM,IAAM,EAAkB,GAIxB,IAAU,EAAM,EAAQ,WAAW,GAAK,WAAW,KAAA,CAAS,KAAK;CACvE,IAAI,CAAC,GACH,MAAU,MACR,6GACF;CAGF,IAAM,IAAa,EAA8B,IAAI,GAE/C,IAAa,OAAO,cAAc;CA0BxC,AAxBA,QAAgB;EAWd,IAAM,IAAK,EAAQ,MAAM,EAAO,IAAI,CAAK;EACzC,EAAW,QAAQ;EAGnB,IAAM,IAAS,EAAU,CAAO,GAC5B,IAAW,EAAO,IAAI,CAAE;EAK5B,AAJK,MACH,oBAAW,IAAI,IAAI,GACnB,EAAO,IAAI,GAAI,CAAQ,IAEzB,EAAS,IAAI,CAAU;CACzB,CAAC,GAED,QAAkB;EAChB,IAAM,IAAK,EAAW;EACtB,IAAI,CAAC,GAAI;EACT,EAAW,QAAQ;EACnB,IAAM,IAAS,EAAU,CAAO;EAIhC,qBAAqB;GACnB,IAAM,IAAM,EAAO,IAAI,CAAE;GACpB,MAKL,EAAI,OAAO,CAAU,GACjB,IAAI,OAAO,OACf,EAAO,OAAO,CAAE,GAehB,EAAQ,IAAI,GAAI,EAAE,QAAQ,YAAY,GAAG,EAAE,OAAO,GAAK,CAAC,GACxD,EAAQ,OAAO,CAAE;EACnB,CAAC;CACH,CAAC;CAED,IAAM,IAAW,EAAoB,GAAS,CAAU;CAExD,OAAO;EACL;EACA;EACA;EACA,WAAW,QAAgB,EAAS,QAAQ,EAAS,MAAM,QAAQ,SAAS,CAAE;CAChF;AACF;AAgBA,SAAS,GAAoB,GAAmE;CAC9F,OAAQ,EAAM,mBAAmB,EAAM;AACzC;AAiDA,IAAa,KAAc,EAAgB;CACzC,MAAM;CACN,cAAc;CACd,OAAO;EACL,QAAQ;GACN,MAAM;GACN,UAAU;EACZ;EAOA,OAAO;GAAE,MAAM;GAAkC,SAAS,KAAA;EAAU;EAKpE,SAAS;GAAE,MAAM;GAAoC,SAAS,KAAA;EAAU;CAC1E;CACA,MAAM,GAAO,EAAE,UAAO,YAAS;EAC7B,IAAM,EAAE,eAAY,aAAU,YAAS,iBAAc,EAAe,EAAM,QAAQ,EAAM,OAAO,EAC7F,SAAS,EAAM,QACjB,CAAC;EAED,aAAa;GACX,IAAM,IAAK,EAAW,OAChB,IAAO,EAAS;GACtB,IAAI,CAAC,KAAM,CAAC,GAAM;IAChB,IAAM,IAAW,GAAoB,CAAK;IAE1C,OADK,IACE,OAAO,KAAa,aAAa,EAAS,IAAI,IAD/B;GAExB;GAMA,IAAM,IAAS,EAAE,GAAe;IAAE,GAAG;IAAO;IAAS,YAAY;GAAG,CAAC,GAC/D,IAAO,EAAM;GAEnB,OADK,IACE,EAAK;IACV,YAAY;IACZ,UAAU;IACV,WAAW,EAAU;IACrB;GACF,CAAgC,IANd;EAOpB;CACF;AACF,CAAC;;;ACxPD,SAAgB,GACd,GACA,GACA,IAAmD,CAAC,GAC9C;CACN,IAAM,IAAM,EAAkB,GAIxB,IAAU,EAAM,EAAQ,CAAO,CAAC,CAAC,WAAW,GAAK,WAAW,KAAA,CAAS,KAAK,MAK1E,IAAS;EACb,MAAM,EAAQ,CAAI;EAClB,SAAS,EAAQ,CAAO;CAC1B;CACA,QAAkB;EAEhB,AADA,EAAO,OAAO,EAAQ,CAAI,GAC1B,EAAO,UAAU,EAAQ,CAAO;CAClC,CAAC;CAMD,IAAM,IAA8B;EAClC,YAAY,EAAO,KAAK,KAAK;EAC7B,OAAO,MAAS,EAAO,KAAK,KAAK,CAAI;EACrC,UAAU,MAAS,EAAO,KAAK,QAAQ,CAAI;EAC3C,IAAI,KAAK;GACP,OAAO,EAAO,KAAK,MAAM,MAAkB,EAAO,KAAK,KAAK,CAAK,IAAI,KAAA;EACvE;EACA,YAAY,MAAa,EAAO,KAAK,UAAU,CAAQ;CACzD;CAEA,QACQ,EAAQ,CAAU,IACvB,GAAI,GAAW,MAAc;EAC5B,IAAI,CAAC,KAAW,CAAC,GAAI;EAMrB,IAAM,IAAa,EAAO,QAAQ,cAAc,IAC1C,IAAO,GAAkB,GAAS,GAAI,GAAY;GACtD;GACA,eAAe,MAAoC,EAAO,QAAQ,eAAe,CAAO;GACxF,YAAY,MAAoC,EAAO,QAAQ,YAAY,CAAO;EACpF,CAAC;EAGD,QAAgB,EAAK,KAAK,CAAC;CAC7B,GACA,EAAE,WAAW,GAAK,CACpB;AACF;;;AC/FA,SAAS,GACP,GACA,GACiB;CACjB,IAAM,IAAc,EAAI,aAClB,IAAa,IAAc,OAAO,KAAK,CAAW,IAAI,CAAC,GACzD,IAAmC,GACnC,IAAoC;CAgBxC,OAfI,MAAU,KAAA,IACR,EAAW,WAAW,IACxB,IAAe,EAAW,KACjB,EAAW,SAAS,MAC7B,IAAqB,WAAW,EAAI,GAAG,8BAA8B,EAAW,KAAK,IAAI,EAAE,gDAEpF,KAAe,CAAC,OAAO,UAAU,eAAe,KAAK,GAAa,CAAK,IAChF,IAAqB,WAAW,EAAI,GAAG,kBAAkB,EAAM,iBAAiB,EAAW,KAAK,IAAI,KAAK,SAAS,KACxG,MAIV,IAAe,KAAA,GACf,IAAqB,WAAW,EAAI,GAAG,kCAAkC,EAAM,2BAE1E;EAAE;EAAc;CAAmB;AAC5C;AAEA,SAAS,EAAO,GAAwB;CACtC,OAAO,EAAE,OAAO,EAAE,OAAO;EAAE,SAAS;EAAQ,OAAO;CAAU,EAAE,GAAG,CAAO;AAC3E;AAoBA,IAAa,IAAY,EAAgB;CACvC,MAAM;CACN,cAAc;CACd,OAAO;EAEL,QAAQ;GACN,MAAM;GACN,UAAU;EACZ;EAUA,OAAO;GAAE,MAAM;GAAQ,SAAS,KAAA;EAAU;EAE1C,OAAO;GAAE,MAAM;GAAQ,SAAS,KAAA;EAAU;EAO1C,QAAQ;GAAE,MAAM;GAA2D,SAAS,KAAA;EAAU;CAChG;CACA,MAAM,GAAO,EAAE,YAAS;EAMtB,IAAM,UAAkB,EAAM,EAAM,MAAM,GACpC,IAAa,QAAe,GAAiB,EAAU,GAAG,EAAM,KAAK,CAAC,GAKtE,IAAO,QACL,EAAM,OAAO,UACb,EAAW,MAAM,gBAAgB,IACvC;GACE,aAAa,EAAM;GACnB,mBAAmB,EAAM;EAC3B,CACF;EAEA,aAAa;GACX,IAAM,IAAM,EAAU,GAChB,EAAE,iBAAc,0BAAuB,EAAW,OAClD,IAAa,IAAe,EAAI,cAAc,KAAgB,KAAA,GAEhE;GACJ,IAAI,GACF,IAAU,EAAO,CAAkB;QAC9B,IAAI,GAAY;IAQrB,IAAM,IAAW,WAAW,GACtB,IAAS,EAAkC;IACjD,IAAI,MAAU,KAAA,KAAa,CAAC,GAC1B,IAAU,EACR,WAAW,EAAI,GAAG,WAAW,KAAgB,GAAG,+GAElD;SACK;KACL,IAAM,EAAE,iBAAc,EAAsB,CAAU,GAChD,IAAY,EAAoD;KACtE,IAAU,EAAE,GAAU,MAAM;MAC1B,eAAe,EAAE,GAAW;OAAE;OAAO;MAAK,CAAC;MAC3C,gBAAiB,OAAO,KAAa,aAAa,EAAS,IAAK,KAAY;KAC9E,CAAC;IACH;GACF,OAAO,IAAI,EAAI,WAAW;IAGxB,IAAM,IAAS,EAAI,WACb,IAAS,EAAkC;IACjD,IAAU,EAAE,GAAQ;KAAE;KAAO,OAAO,EAAM;IAAM,CAAC;GACnD,OACE,IAAU,EAAO,WAAW,EAAI,GAAG,wCAAwC;GAG7E,OAAO,EAAE,GAAqB,EAAE,UAAU,EAAI,GAAG,SAAS,CAAO;EACnE;CACF;AACF,CAAC;;;ACnCD,SAAgB,GACd,GACA,GACM;CAKN,IAAM,IAKF;EACF,MAAM,KAAA;EACN,WAAW,KAAA;EACX,WAAW,KAAA;EACX,aAAa,KAAA;CACf;CAEA,QAAkB;EAChB,IAAM,IAAK,EAAQ,CAAQ;EAI3B,AAHA,EAAO,OAAO,EAAQ,CAAI,GAC1B,EAAO,YAAY,EAAG,WACtB,EAAO,YAAY,EAAG,MAAM,OAC5B,EAAO,cAAc,EAAG,SAAS;CACnC,CAAC;CAED,IAAM,KAAO,MAA8C;EACzD,IAAM,IAAK,EAAQ,CAAQ,GACrB,IAAe,EAAG,aAAa,MAC/B,IAAU,EAAG,QAAQ,MACrB,IAAa,EAAG,WAAW,MAC3B,IAAe,EAAG,MAAM,YACxB,IAAY,EAAG,SAAS,IAE1B,IAAU,IACR,IAA+B,CAAC,GAEhC,KAAgB,MAAmB;GACvC,IAAI;IACF,EAAG;GACL,SAAS,GAAK;IACZ,AAAI,EAAS,KAIX,QAAQ,KAAK,4CAA4C,CAAG;GAEhE;EACF,GAEM,UAAuB;GAI3B,IAAM,IAAO,EAAU,OAAO,CAAC;GAC/B,KAAK,IAAM,KAAM,GAAM,EAAa,CAAE;EACxC,GAKM,MAA4B,GAAqC,GAAG,MAAoB;GACxF,MACJ,IAAU,IACV,EAAe,GACf,EAAQ,OAAwD,GAAM,GAAG,CAAI;EAC/E,IAEM,KAAoB,MAAmB;GAK3C,AAAI,IAAS,EAAa,CAAE,IACvB,EAAU,KAAK,CAAE;EACxB;EAEA,IAAI,GAAc;GAChB,IAAM,IAAc,EAAO,YAAY,CAAO;GAC9C,AAAI,OAAO,KAAgB,cAAY,EAAiB,CAAW;EACrE;EAIA,IAAI,CAAC,KAAW,KAAW,MAAiB,KAAA,KAAa,IAAe,GAAG;GACzE,IAAM,IAAK,kBAAkB;IACvB,QACJ,IAAI;KACF,IAAM,IAAS,EAAO,YAAY,CAAO;KACzC,AAAI,KAAU,OAAQ,EAA6B,QAAS,cAG1D,QAAQ,QAAQ,CAA2B,CAAC,CAAC,YAAY,CAGzD,CAAC;IAEL,QAAQ,CAER;GACF,GAAG,CAAY;GACf,QAAuB,cAAc,CAAE,CAAC;EAC1C;EAEA,IAAI,CAAC,KAAW,KAAc,MAAc,KAAA,KAAa,IAAY,GAAG;GACtE,IAAM,IAAK,iBAAiB;IAC1B,IAAI,GAAS;IACb,IAAM,IAAO,EAAO;IACpB,AAAI,OAAO,KAAS,WAGlB,EAA4C,CAAI,IACvC,OAAO,KAAS,cACzB,EAAK,CAAO;GAEhB,GAAG,CAAS;GACZ,QAAuB,aAAa,CAAE,CAAC;EACzC;EAEA,QAAgB;GAEd,AADA,IAAU,IACV,EAAe;EACjB,CAAC;CACH;CAQA,QACQ;EACJ,IAAM,IAAK,EAAQ,CAAQ,GACrB,IACJ,EAAG,WAAW,OAAO,EAAG,QAAQ,QAAS,WAAW,EAAG,QAAQ,OAAO,KAAA;EACxE,OAAO;GACL,EAAG,aAAa;GAChB,EAAG,QAAQ;GACX,EAAG,WAAW;GACd,EAAG,MAAM;GACT,EAAG,SAAS;GACZ;EACF,CAAC,CAAC,KAAK,GAAG;CACZ,IACC,GAAM,GAAM,MAAc;EACzB,EAAI,CAAS;CACf,GACA,EAAE,WAAW,GAAK,CACpB;AACF;;;AChQA,SAAgB,GACd,GACA,GACA,GACM;CACN,IAAM,IAA8B;EAClC;EACA,cAAc,GAAS;CACzB;CACA,EAAI,QAAQ,GAAY,CAAK;AAC/B;;;AC6CA,SAAgB,GACd,GACwC;CACxC,IAAM,EAAE,cAAW,GACb,IAAW,EAAQ,YAAY,YAC/B,IAAc,EAAQ,UAAU,IAEhC,KAAQ,MACZ,IAAe,KAAK,MAAM,KAAK,UAAU,CAAI,CAAC,IAAkC,GAE5E,WACM,OAAO,EAAQ,SAAU,aAAa,EAAQ,MAAM,IAAI,EAAQ,UAC9D,MAGR,KACJ,MAC0D;EAC1D,IAAM,IAAO,EAAE,OAAmC;EAClD,OAAO,KAAO,OAAO,KAAQ,WACxB,IACD,KAAA;CACN;CAEA,OAAO;EACL;EACA,OAAO,MAAQ;GACb,IAAM,IAAI,EAAQ;GAClB,IAAI,CAAC,GAAG,OAAO;GACf,IAAM,IAAO,EAAW,CAAC,CAAC,GAAG;GAC7B,OAAO,IAAO,EAAK,CAAI,IAAI;EAC7B;EACA,OAAO,GAAK,MAAS;GACnB,IAAM,IAAI,EAAQ;GAClB,IAAI,CAAC,GAAG;GACR,IAAM,IAAS,EAAK,CAAI;GACxB,EAAE,QAAQ,MAAU;IAClB,IAAM,IAAW,EAAM,IACjB,IACJ,KAAY,OAAO,KAAa,WAAW,IAAY,EAAM,KAAY,CAAC;IAE5E,EAAI,KAAO;GACb,CAAC;EACH;EACA,SAAS,MAAQ;GACf,IAAM,IAAI,EAAQ;GACb,KACL,EAAE,QAAQ,MAAU;IAClB,IAAM,IAAM,EAAM;IAClB,AAAI,KAAK,OAAO,EAAI;GACtB,CAAC;EACH;CACF;AACF;;;ACbA,SAAgB,GACd,IAA2C,CAAC,GAEN;CACtC,IAAM,IAAkC,CAAC;CAEzC,OAAO;EACL,MAAM;EAEN,SAAS;GACP,OAAO,EACL,gBACE,GACA,GACM;IACN,IAAM,IAAM,GACN,IAAS,EAA0B,CAAG;IAC5C,IAAI,EAAO,SAAS,GAClB,MAAM,IAAI,EAAuB,CAAM;IAEzC,EAAW,KAAK;KACd,YAAY;KACZ,SAAS;IACX,CAAC;GACH,EACF;EACF;EAEA,SAAS,EAAE,cAAW;GACpB,AAAI,EAAW,SAAS,KACtB,EAAyB,GAAY,CAAO;EAEhD;EAEA,UAAU,EAAE,sBAAmB,YAAS;GACtC,OAAO,GAAqB,GAAY;IACtC,SAAS;IACT,OAAO,EAAQ,SAAS;GAC1B,CAAC;EACH;EAEA,uBAAuB;GACrB,IAAM,IAA8B,CAAC;GACrC,KAAK,IAAM,KAAO,GAAY;IAC5B,IAAM,IAAM,EAAI,SAAS;IACzB,IAAI,CAAC,GAAK;IACV,IAAM,IAAkC;KACtC,OAAO,EAAI;KACX,IAAI;KACJ,GAAI,EAAI,SAAS,KAAA,IAAiC,CAAC,IAAtB,EAAE,MAAM,EAAI,KAAK;KAC9C,GAAI,EAAI,UAAU,KAAA,IAAmC,CAAC,IAAxB,EAAE,OAAO,EAAI,MAAM;KACjD,GAAI,EAAI,UAAU,KAAA,IAAmC,CAAC,IAAxB,EAAE,OAAO,EAAI,MAAM;KACjD,GAAI,EAAI,WAAW,KAAA,IAAqC,CAAC,IAA1B,EAAE,QAAQ,EAAI,OAAO;KACpD,GAAI,EAAI,SAAS,KAAA,IAAiC,CAAC,IAAtB,EAAE,MAAM,EAAI,KAAK;KAC9C,QAAQ;MACN,MAAM;MACN,WAAW,EAAI,WAAW;MAC1B,GAAI,EAAI,aAAa,EAAE,YAAY,EAAI,WAAW,IAAI,CAAC;KACzD;IACF;IACA,AAAI,EAAQ,eACV,EAAM,KACJ,EAAQ,aAAa,GAAU;KAC7B,GAAI;KACJ,WAAW,EAAI,WAAW;IAC5B,CAAC,CACH,IAEA,EAAM,KAAK,CAAQ;GAEvB;GACA,OAAO;EACT;EAEA,UAAU,EAAE,cAAW;GACrB,IAAM,IAAe,EAAQ;GAO7B,OAAO,CANsB,EAAgB;IAC3C,MAAM;IACN,MAAM,GAAQ,EAAE,YAAS;KACvB,aAAa,EAAE,GAAiB;MAAE;MAAS;KAAa,SAAS,EAAM,UAAU,CAAC;IACpF;GACF,CACQ,CAAoE;EAC9E;EAiBA,YAAY,EAAE,cAAW;GAEvB,OAAO,CAAC,EAAe,GAAY;IADG;IAAS,cAAc,EAAQ;GAClC,CAAK,CAAC;EAC3C;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/provider.ts","../src/instance-hooks.ts","../src/use-journey-state.ts","../src/outlet.ts","../src/mount-adapter.ts","../src/journey-host.ts","../src/use-journey-sync.ts","../src/module-tab.ts","../src/use-wait-for-exit.ts","../src/provide-journey-runtime.ts","../src/pinia-persistence.ts","../src/plugin.ts"],"sourcesContent":["import { defineComponent, h, inject, provide, type InjectionKey, type PropType } from \"vue\";\nimport { ModuleExitProvider } from \"@modular-vue/vue\";\nimport type { ModuleExitEvent, ModuleExitHandler } from \"@modular-vue/vue\";\n\nimport type { JourneyRuntime } from \"@modular-frontend/journeys-engine\";\n\n/**\n * Shell-level context read by `<JourneyOutlet>` (PR-31) and the instance\n * composables so callers don't have to thread `runtime` through every\n * container that hosts a journey. Analog of the React `JourneyContext` value.\n *\n * `onModuleExit` is still surfaced here for consumers that introspect the\n * provider value. The actual dispatch flows through `<ModuleExitProvider>`\n * from `@modular-vue/vue`, which `<JourneyProvider>` mounts automatically.\n * Prefer consuming `useModuleExit` / `useModuleExitDispatcher` from the vue\n * package directly in new code.\n */\nexport interface JourneyProviderValue {\n /** Journey runtime — usually `manifest.journeys`. */\n readonly runtime: JourneyRuntime;\n /**\n * Optional fallback invoked by module hosts (`<ModuleRoute>`, tabs) after\n * any local `onExit` prop has run. Wiring this at the provider level gives a\n * shell global telemetry / tab-close forwarding without threading the\n * callback through every host.\n */\n readonly onModuleExit?: (event: ModuleExitEvent) => void;\n}\n\n/**\n * Injection key holding the current {@link JourneyProviderValue}, or `null`\n * when no `<JourneyProvider>` is mounted. Exported so tests and advanced hosts\n * can provide the context directly.\n */\nexport const journeyKey: InjectionKey<JourneyProviderValue> = Symbol(\"modular-vue.journey\");\n\n/**\n * Provides the journey runtime to descendant journey hosts, and composes over\n * `<ModuleExitProvider>` so module hosts (`<ModuleRoute>`, anything using\n * `useModuleExit`) see the shell's `onModuleExit` dispatcher without needing a\n * second provider.\n *\n * Authored with `defineComponent` + a render function (no SFC compiler in the\n * package build; see decision D4). The `runtime` is provided by identity at\n * setup — matching the modules / navigation contexts (PR-10), it is resolved\n * once from the manifest and does not swap on the same mount, and it is left\n * un-proxied so identity checks against `manifest.journeys` hold. The\n * `onModuleExit` handler is forwarded through the live `props` into\n * `<ModuleExitProvider>` on every render, and the provided context value reads\n * it through a getter, so a swapped handler reaches both descendant hosts and\n * consumers that introspect `useJourneyContext().onModuleExit` (parity with the\n * React provider, which rebuilds its value object each render).\n *\n * Existing journey consumers do not need to change — `onModuleExit` keeps\n * firing for every module exit emitted outside a journey step.\n */\nexport const JourneyProvider = defineComponent({\n name: \"JourneyProvider\",\n props: {\n /** Journey runtime — usually `manifest.journeys`. */\n runtime: { type: Object as PropType<JourneyRuntime>, required: true },\n /** Shell-wide fallback dispatcher for module exits fired outside a step. */\n onModuleExit: { type: Function as PropType<ModuleExitHandler>, default: undefined },\n },\n setup(props, { slots }) {\n // Keep `runtime` a raw reference (identity-stable for the same mount) while\n // exposing `onModuleExit` live: a getter reads the current prop on each\n // access, so a swapped handler is visible to introspecting consumers\n // without proxying `runtime` through `reactive`.\n const value: JourneyProviderValue = {\n runtime: props.runtime,\n get onModuleExit() {\n return props.onModuleExit;\n },\n };\n provide(journeyKey, value);\n return () => h(ModuleExitProvider, { onExit: props.onModuleExit }, () => slots.default?.());\n },\n});\n\n/** Read the current provider value, or `null` when none is mounted. */\nexport function useJourneyContext(): JourneyProviderValue | null {\n return inject(journeyKey, null);\n}\n","import {\n computed,\n shallowRef,\n toValue,\n watchEffect,\n type ComputedRef,\n type MaybeRefOrGetter,\n type ShallowRef,\n} from \"vue\";\nimport type {\n InstanceId,\n JourneyInstance,\n JourneyRuntime,\n} from \"@modular-frontend/journeys-engine\";\n\n/**\n * Sanity bound to break a corrupted cycle in the activeChild graph.\n * Legitimate invoke nesting is not expected to approach this depth — if a\n * real product stacks deeper, surface a knob via `JourneyRuntimeOptions`\n * rather than raising the constant blindly. Intentionally not exported:\n * the cap is implementation detail, not a knob. Matches the React source.\n */\nconst MAX_CHAIN_DEPTH = 64;\n\nconst EMPTY_CHAIN: readonly InstanceId[] = Object.freeze([]);\n\n/**\n * Subscribe to a single instance and return a `ShallowRef` of its current\n * snapshot, or `null` when runtime / id is missing or the instance has been\n * forgotten. The Vue analog of the React `useInstanceSnapshot` (which uses\n * `useSyncExternalStore`): a `watchEffect` seeds the ref, subscribes, and\n * re-subscribes whenever `instanceId` changes.\n *\n * `instanceId` accepts a plain value, a ref, or a getter. Reactive ids are\n * the point of the leaf-walking composables: `useActiveLeafJourneyInstance`\n * feeds a `ComputedRef` leaf id here, so the snapshot re-subscribes as the\n * active chain grows (parent invokes a child) or shrinks (child terminates).\n * The subscribe callback reads the *current* id so an in-flight notification\n * that arrives after an id swap still resolves against the live instance.\n *\n * The `watchEffect` uses the default (pre) flush: the initial subscription is\n * established synchronously at setup, runtime events push snapshots into the\n * `shallowRef` synchronously (matching React's tearing-free reads), and only\n * re-subscription on an id change is deferred to the next tick — so a\n * changing-leaf test awaits `nextTick()` before the swap is observed.\n */\nexport function useInstanceSnapshot(\n runtime: JourneyRuntime | null,\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const snapshot = shallowRef<JourneyInstance | null>(null);\n\n const readCurrent = (): JourneyInstance | null => {\n const id = toValue(instanceId);\n if (!runtime || !id) return null;\n return runtime.getInstance(id);\n };\n\n watchEffect((onCleanup) => {\n const id = toValue(instanceId);\n if (!runtime || !id) {\n snapshot.value = null;\n return;\n }\n snapshot.value = runtime.getInstance(id);\n const unsubscribe = runtime.subscribe(id, () => {\n snapshot.value = readCurrent();\n });\n onCleanup(unsubscribe);\n });\n\n return snapshot;\n}\n\n/**\n * Walk `activeChildId` from `rootId` down to the deepest descendant, returning\n * a `ShallowRef` of the full chain. Subscribes to every instance in the chain\n * and re-subscribes as the chain grows / shrinks. Pass `enabled: false` to\n * short-circuit the walk (collapses to `[rootId]`) — used by the outlet's\n * `leafOnly` opt-out (PR-31). Null `runtime` / `rootId` yields an empty chain\n * so callers can invoke this composable unconditionally even before a runtime\n * is mounted.\n *\n * The dynamic per-instance subscriptions are managed by hand (as in the React\n * source) rather than through Vue reactivity, since they rewire on runtime\n * events, not on tracked dependencies. The `watchEffect` only re-runs — tearing\n * down and rebuilding the whole chain — when `rootId` or `enabled` change.\n */\nexport function useCallChain(\n runtime: JourneyRuntime | null,\n rootId: MaybeRefOrGetter<InstanceId | null>,\n enabled: MaybeRefOrGetter<boolean>,\n): ShallowRef<readonly InstanceId[]> {\n const chainRef = shallowRef<readonly InstanceId[]>(EMPTY_CHAIN);\n\n // Cache by the joined-id signature so the ref keeps identity when the chain\n // hasn't actually shifted — the Vue analog of the React `cacheRef` guard, so\n // an unrelated state change on a chain member doesn't churn the ref.\n let lastKey: string | null = null;\n const setChain = (chain: readonly InstanceId[]): void => {\n const key = chain.join(\">\");\n if (lastKey === key) return;\n lastKey = key;\n chainRef.value = chain.length === 0 ? EMPTY_CHAIN : chain;\n };\n\n watchEffect((onCleanup) => {\n const root = toValue(rootId);\n const enabledNow = toValue(enabled);\n if (!runtime || !root) {\n setChain(EMPTY_CHAIN);\n return;\n }\n\n const unsubs = new Map<InstanceId, () => void>();\n let stopped = false;\n\n const rewire = (): void => {\n if (stopped) return;\n const seen = new Set<InstanceId>();\n const chain: InstanceId[] = [];\n let id: InstanceId | null = root;\n let depth = 0;\n while (id && depth < MAX_CHAIN_DEPTH) {\n if (seen.has(id)) break;\n seen.add(id);\n chain.push(id);\n if (!unsubs.has(id)) {\n unsubs.set(id, runtime.subscribe(id, rewire));\n }\n const inst = runtime.getInstance(id);\n id = enabledNow && inst ? inst.activeChildId : null;\n depth += 1;\n }\n for (const [subscribedId, unsub] of unsubs) {\n if (!seen.has(subscribedId)) {\n unsub();\n unsubs.delete(subscribedId);\n }\n }\n setChain(chain);\n };\n\n rewire();\n onCleanup(() => {\n stopped = true;\n for (const unsub of unsubs.values()) unsub();\n unsubs.clear();\n });\n });\n\n return chainRef;\n}\n\n/**\n * Last id in the active chain — `rootId` when no child is in flight, `null`\n * when runtime / rootId are missing. Returns a `ComputedRef` that tracks the\n * chain, so feeding it to {@link useInstanceSnapshot} re-subscribes the\n * snapshot as the leaf moves.\n */\nexport function useLeafId(\n runtime: JourneyRuntime | null,\n rootId: MaybeRefOrGetter<InstanceId | null>,\n enabled: MaybeRefOrGetter<boolean>,\n): ComputedRef<InstanceId | null> {\n const chain = useCallChain(runtime, rootId, enabled);\n return computed(() => chain.value[chain.value.length - 1] ?? toValue(rootId));\n}\n","import { computed, type ComputedRef, type MaybeRefOrGetter, type ShallowRef } from \"vue\";\nimport type { InstanceId, JourneyInstance } from \"@modular-frontend/journeys-engine\";\n\nimport { useInstanceSnapshot, useLeafId } from \"./instance-hooks.js\";\nimport { useJourneyContext } from \"./provider.js\";\n\n/**\n * Sugar over {@link useJourneyInstance}: returns a `ComputedRef` of the\n * instance's `state` (or `null` when the runtime / id / instance is\n * unavailable). `TState` is the journey's state type — pass it at the call\n * site.\n *\n * The React analog returns a plain `TState | null`; the Vue port returns a\n * `ComputedRef` so it stays reactive in templates and `watch` (the PR-23\n * convention for reactive-source composables). Read `.value` at the call site.\n *\n * Prefer {@link useJourneyInstance} when the host needs more than `state`\n * (`step` / `status` / `terminalPayload`).\n */\nexport function useJourneyState<TState>(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ComputedRef<TState | null> {\n const inst = useJourneyInstance(instanceId);\n return computed(() => (inst.value ? (inst.value.state as TState) : null));\n}\n\n/**\n * Subscribe to a journey instance and return a `ShallowRef` of its full\n * snapshot (`status`, `step`, `state`, `terminalPayload`, …), or `null` when\n * no `<JourneyProvider>` is mounted or the id is unknown. Tearing-free via the\n * `shallowRef` bridge in {@link useInstanceSnapshot}. The primitive of which\n * {@link useJourneyState} is sugar; symmetric with\n * {@link useActiveLeafJourneyInstance} for the leaf-walking case.\n */\nexport function useJourneyInstance(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const ctx = useJourneyContext();\n return useInstanceSnapshot(ctx?.runtime ?? null, instanceId);\n}\n\n/**\n * Sugar over {@link useActiveLeafJourneyInstance}: returns a `ComputedRef` of\n * the leaf instance's `state` as `TState`. `.value` is `null` when no\n * provider, the root id is unknown, or the leaf has been forgotten.\n *\n * Prefer {@link useActiveLeafJourneyInstance} when the leaf can be any of\n * several journeys — typing this composable as `<ParentState | ChildState>`\n * leaves the caller without a discriminator, whereas the instance form gives a\n * typed `inst.journeyId` to switch on.\n */\nexport function useActiveLeafJourneyState<TState>(\n rootInstanceId: MaybeRefOrGetter<InstanceId | null>,\n): ComputedRef<TState | null> {\n const inst = useActiveLeafJourneyInstance(rootInstanceId);\n return computed(() => (inst.value ? (inst.value.state as TState) : null));\n}\n\n/**\n * Walks `activeChildId` from `rootInstanceId` down to the deepest descendant\n * and returns a `ShallowRef` of that leaf's full `JourneyInstance`. The\n * recommended primitive when the host doesn't know the leaf's depth (a parent\n * that may or may not be in an invoked sub-flow) — pair with `inst.journeyId`\n * as a discriminator instead of typing the state composable as a union and\n * asserting manually. Re-subscribes as the chain grows (parent invokes a\n * child, grandchild starts) or shrinks (child terminates and parent resumes).\n *\n * `.value` is `null` under the same conditions as {@link useJourneyInstance}.\n */\nexport function useActiveLeafJourneyInstance(\n rootInstanceId: MaybeRefOrGetter<InstanceId | null>,\n): ShallowRef<JourneyInstance | null> {\n const ctx = useJourneyContext();\n const runtime = ctx?.runtime ?? null;\n const leafId = useLeafId(runtime, rootInstanceId, true);\n return useInstanceSnapshot(runtime, leafId);\n}\n","import {\n computed,\n defineComponent,\n h,\n inject,\n onErrorCaptured,\n onMounted,\n onUnmounted,\n ref,\n Suspense,\n toRaw,\n watch,\n type Component,\n type PropType,\n type ShallowRef,\n type VNode,\n} from \"vue\";\nimport type { ModuleDescriptor, ModuleEntryPoint } from \"@modular-frontend/core\";\nimport { resolveEntryComponent } from \"@modular-vue/vue\";\n\nimport { getInternals, isAnnotatedTransition } from \"@modular-frontend/journeys-engine\";\nimport { journeyKey } from \"./provider.js\";\nimport { useCallChain, useInstanceSnapshot, useLeafId } from \"./instance-hooks.js\";\nimport type {\n AnyJourneyDefinition,\n InstanceId,\n JourneyRuntime,\n JourneyStep,\n TerminalOutcome,\n} from \"@modular-frontend/journeys-engine\";\n\nexport type JourneyStepErrorPolicy = \"abort\" | \"retry\" | \"ignore\";\n\n/** Maximum automatic retries before falling back to `abort`. */\nconst DEFAULT_RETRY_CAP = 2;\n\nexport interface JourneyOutletNotFoundProps {\n readonly moduleId: string;\n readonly entry: string;\n}\n\nexport interface JourneyOutletErrorProps {\n readonly moduleId: string;\n readonly error: unknown;\n}\n\n/**\n * Renders the current step of a journey instance. Host-agnostic — works in a\n * tab, modal, route element, or plain `<div>`. On unmount while active, the\n * instance is abandoned (deferred by a microtask so a same-tick handoff to a\n * sibling outlet — outlet A unmounts, outlet B mounts against the same instance\n * — does not tear the instance down: the deferred check consults the record's\n * listener count and skips `end()` when another outlet is still subscribed).\n *\n * The Vue analog of the React `<JourneyOutlet>`. Authored with `defineComponent`\n * + a render function (no SFC compiler in the package build; see decision D4).\n * The instance snapshots come from the reactive `useInstanceSnapshot` /\n * `useLeafId` composables (PR-30), so the outlet re-renders as the active call\n * chain shifts (parent invokes a child, child terminates) and as the leaf's own\n * step advances. Error-message prefixes are `[@modular-vue/journeys]`.\n */\nexport const JourneyOutlet = defineComponent({\n name: \"JourneyOutlet\",\n props: {\n /**\n * Runtime to drive the outlet against. Optional when a `<JourneyProvider>`\n * is mounted above — the outlet reads the runtime from context in that\n * case. Explicit prop overrides context, so one outlet can reach a\n * different runtime when needed.\n */\n runtime: { type: Object as PropType<JourneyRuntime>, default: undefined },\n instanceId: { type: String as PropType<InstanceId>, required: true },\n /**\n * Module descriptors the outlet resolves step components against. Optional —\n * when omitted, the outlet pulls the descriptors the runtime was constructed\n * with (the common case).\n */\n modules: {\n type: Object as PropType<Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>>,\n default: undefined,\n },\n /** Rendered while the instance is in `loading` status. */\n loadingFallback: {\n type: null as unknown as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n onFinished: {\n type: Function as PropType<(outcome: TerminalOutcome) => void>,\n default: undefined,\n },\n onStepError: {\n type: Function as PropType<\n (err: unknown, ctx: { step: JourneyStep }) => JourneyStepErrorPolicy\n >,\n default: undefined,\n },\n /**\n * When `false`, the outlet renders the instance you handed it directly, even\n * if it has a child journey in flight. Set this when you compose two outlets\n * to render parent and child side-by-side. When `true` (the default), the\n * outlet walks the active call chain down to the leaf and renders the leaf.\n */\n leafOnly: { type: Boolean, default: true },\n /**\n * Cap on `retry` responses before the outlet falls back to `abort`. The\n * counter increments on every retry and is never reset. Default: 2.\n */\n retryLimit: { type: Number, default: DEFAULT_RETRY_CAP },\n /**\n * Rendered when the current step points at a module/entry that is not\n * registered with the runtime. Defaults to a plain red notice.\n */\n notFoundComponent: {\n type: Object as PropType<Component>,\n default: undefined,\n },\n /**\n * Rendered when a step component throws. Defaults to a plain red notice with\n * the error message. Receives the raw error so shells can route it through\n * their own reporting.\n */\n errorComponent: { type: Object as PropType<Component>, default: undefined },\n /**\n * Speculatively prefetch the chunks for entries reachable from the current\n * step during idle time after mount. `\"precise\"` (default, alias `true`)\n * reads declared `targets` from annotated handlers; `\"aggressive\"` preloads\n * every entry referenced as a transition source or annotated target;\n * `false` opts out. No effect for eager entries; SSR is a no-op.\n */\n preload: {\n type: [Boolean, String] as PropType<boolean | \"precise\" | \"aggressive\">,\n default: \"precise\",\n },\n },\n setup(props) {\n const context = inject(journeyKey, null);\n // Unwrap the reactive prop proxy before touching runtime internals: Vue\n // deeply proxies prop objects, and `getInternals` keys its store on the raw\n // runtime identity produced by `createJourneyRuntime`. A proxied runtime\n // would miss that store. `context?.runtime` can also be proxied (the\n // provider stashes its own `runtime` prop), so unwrap both.\n const runtime = toRaw(props.runtime ?? context?.runtime ?? undefined) ?? null;\n if (!runtime) {\n throw new Error(\n \"[@modular-vue/journeys] <JourneyOutlet> needs a runtime. Either pass `runtime` or mount a <JourneyProvider>.\",\n );\n }\n const internals = getInternals(runtime);\n\n // Unwrap the reactive prop proxy: Vue deeply proxies prop objects, which\n // would change the entry-object identity that keys `resolveEntryComponent`'s\n // per-entry `WeakMap` cache — so a preloaded chunk (resolved off the raw\n // runtime map) would not be reused by the render, and the memoized async\n // wrapper would churn. `internals.__moduleMap` is already raw; `toRaw` is a\n // no-op there.\n const getModules = (): Readonly<Record<string, ModuleDescriptor<any, any, any, any>>> =>\n toRaw(props.modules ?? internals.__moduleMap);\n\n // Subscribe to the originally-supplied (root) instance so onFinished and\n // abandon-on-unmount resolve against THAT instance — even when leaf-walk is\n // rendering a different (child/grandchild) record. The leaf is also\n // subscribed-to via `useLeafId` so this component re-renders when the call\n // chain shifts (parent invokes a child, child terminates, etc.).\n const rootInstance = useInstanceSnapshot(runtime, () => props.instanceId);\n const leafId = useLeafId(\n runtime,\n () => props.instanceId,\n () => props.leafOnly,\n );\n // Subscribe to the leaf separately so leaf-internal transitions trigger\n // re-renders. When the leaf id equals the root, useInstanceSnapshot dedupes\n // naturally (same subscribe call to the same record).\n const leafInstance = useInstanceSnapshot(runtime, leafId);\n const instance = computed(() =>\n leafId.value === props.instanceId ? rootInstance.value : leafInstance.value,\n );\n\n const retryKey = ref(0);\n\n // Abandon on unmount while still active or still loading. Deferred one\n // microtask so a same-tick handoff to a sibling outlet keeps the instance\n // alive: if any subscriber is still attached to the record, another outlet\n // has taken over and we skip the `end()`. Targets the ROOT — `runtime.end`\n // cascades to any active child, so a single call cleans the whole chain.\n //\n // Read `props.instanceId` at unmount time (not a value captured at setup)\n // so a host that re-binds `instanceId` on a live outlet abandons the\n // instance it is currently rendering — matching the React effect, whose\n // cleanup closure holds the latest id after re-subscription.\n onUnmounted(() => {\n const instanceId = props.instanceId;\n queueMicrotask(() => {\n const record = internals.__getRecord(instanceId);\n if (!record) return;\n if (record.status !== \"active\" && record.status !== \"loading\") return;\n if (record.listeners.size > 0) return;\n // `force`: an unmounting outlet cannot host an `onAbandon` `{ next }`\n // (there is nothing left to render it), so guarantee the instance ends\n // rather than advancing to an orphaned live step. Matches the React\n // outlet.\n runtime.end(instanceId, { reason: \"unmounted\" }, { force: true });\n });\n });\n\n // Fire onFinished exactly once on terminal — bound to the ROOT, since the\n // caller asked to be notified when the journey they mounted finishes. Child\n // terminations are observed via the parent's resume handler and are not\n // reported through this hook.\n const finishedInstances = new Set<InstanceId>();\n watch(\n rootInstance,\n (root) => {\n if (!root) return;\n if (root.status !== \"completed\" && root.status !== \"aborted\") return;\n if (finishedInstances.has(root.id)) return;\n finishedInstances.add(root.id);\n props.onFinished?.({\n status: root.status,\n payload: root.terminalPayload,\n instanceId: root.id,\n journeyId: root.journeyId,\n });\n },\n { immediate: true },\n );\n\n // Speculative preload of reachable entries' chunks. Re-keyed on\n // (status, module, entry, journey) — not on the whole snapshot, which\n // changes reference on every bump (timestamps, child-id shifts). Only fires\n // after mount (browser), matching the React effect; SSR stays a no-op.\n onMounted(() => {\n watch(\n () => {\n const inst = instance.value;\n return [\n props.preload,\n inst?.status === \"active\",\n inst?.step?.moduleId,\n inst?.step?.entry,\n inst?.journeyId,\n ] as const;\n },\n ([preload, isActive, stepModuleId, stepEntryName, journeyId], _old, onCleanup) => {\n if (preload === false || !isActive) return;\n if (!stepModuleId || !stepEntryName || !journeyId) return;\n const reg = internals.__getRegistered(journeyId);\n if (!reg) return;\n const mode = preload === \"aggressive\" ? \"aggressive\" : \"precise\";\n const targets = collectPreloadTargets(\n reg.definition,\n getModules(),\n stepModuleId,\n stepEntryName,\n mode,\n );\n if (targets.length === 0) return;\n\n let cancelled = false;\n const run = (): void => {\n if (cancelled) return;\n for (const entry of targets) {\n try {\n // `.catch` swallows async rejection from the lazy import; the\n // surrounding try/catch only covers a synchronous throw from\n // `resolveEntryComponent`/`preload` itself. Both are best-effort:\n // a malformed entry would have failed validation upstream, so one\n // bad entry never hides the rest.\n void resolveEntryComponent(entry)\n .preload()\n .catch(() => {});\n } catch {\n // Synchronous failure — see above.\n }\n }\n };\n\n const ricFn = (\n globalThis as {\n requestIdleCallback?: (cb: () => void, opts?: { timeout?: number }) => number;\n }\n ).requestIdleCallback;\n const cicFn = (globalThis as { cancelIdleCallback?: (handle: number) => void })\n .cancelIdleCallback;\n let idleHandle: number | undefined;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n if (typeof ricFn === \"function\") {\n idleHandle = ricFn(run, { timeout: 2000 });\n } else {\n timeoutHandle = setTimeout(run, 0);\n }\n onCleanup(() => {\n cancelled = true;\n if (idleHandle !== undefined && typeof cicFn === \"function\") cicFn(idleHandle);\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);\n });\n },\n { immediate: true },\n );\n });\n\n return () => {\n const inst = instance.value;\n if (!inst) return null;\n if (inst.status === \"loading\") {\n const lf = props.loadingFallback;\n return (typeof lf === \"function\" ? lf() : lf) ?? null;\n }\n if (inst.status === \"completed\" || inst.status === \"aborted\") return null;\n\n const step = inst.step;\n if (!step) return null;\n\n const modules = getModules();\n const mod = modules[step.moduleId];\n const entry = mod?.entryPoints?.[step.entry];\n if (!mod || !entry) {\n const NotFound = props.notFoundComponent ?? DefaultNotFound;\n return h(NotFound, { moduleId: step.moduleId, entry: step.entry });\n }\n if (!entryAllowsJourneyMount(entry)) {\n // Type-level enforcement via `StepSpec`'s mountKinds filter catches this\n // for typed code. The runtime check is the belt-and-braces for\n // type-bypass paths: journey-only hosts must not mount entries declared\n // `mountKinds: [\"composition\"]`.\n const ErrorComp = props.errorComponent ?? DefaultError;\n return h(ErrorComp, {\n moduleId: step.moduleId,\n error: new Error(\n `[@modular-vue/journeys] Entry \"${step.moduleId}.${step.entry}\" declares ` +\n `mountKinds=${JSON.stringify(entry.mountKinds)} which does not include \"journey\". ` +\n `Journey steps cannot mount composition-only entries — either widen the entry's mountKinds, ` +\n `or pick a different module-entry in the transition.`,\n ),\n });\n }\n\n // Resolve the *leaf's* record and registration so the step callbacks\n // (`exit`, `goBack`) drive the leaf's transitions.\n const record = internals.__getRecord(inst.id);\n const reg = internals.__getRegistered(inst.journeyId);\n if (!record || !reg) return null;\n const { exit, goBack, goForward } = internals.__bindStepCallbacks(record, reg);\n\n const handleError = (err: unknown): void => {\n // Registration-level onError fires on every component throw. Route\n // through the runtime so `fireOnError` stays the single owner of hook\n // firing. Bound to the LEAF's instance id since the throw came from the\n // leaf step's component.\n internals.__fireComponentError(inst.id, err, step);\n let policy = props.onStepError?.(err, { step }) ?? \"abort\";\n if (policy === \"retry\") {\n if (!internals.__consumeRetry(inst.id, props.retryLimit)) {\n policy = \"abort\";\n }\n }\n if (policy === \"abort\") {\n // End the LEAF — its abort cascades via the parent's resume handler.\n runtime.end(inst.id, { reason: \"component-error\", error: err });\n return;\n }\n if (policy === \"retry\") {\n retryKey.value += 1;\n }\n // 'ignore' — leave the boundary UI in place until the user navigates away.\n };\n\n const { Component: StepComponent } = resolveEntryComponent(entry);\n const stepKey = `${record.stepToken}:${retryKey.value}`;\n const entryFallback = (entry as { fallback?: VNode | (() => VNode) }).fallback;\n const outletFallback = props.loadingFallback;\n\n return h(\n StepErrorBoundary,\n {\n key: stepKey,\n moduleId: step.moduleId,\n onError: handleError,\n errorComponent: props.errorComponent,\n },\n () =>\n h(Suspense, null, {\n default: () =>\n h(StepComponent as Component, {\n input: step.input,\n exit,\n goBack,\n goForward,\n }),\n fallback: () => {\n const fb = entryFallback ?? outletFallback;\n return (typeof fb === \"function\" ? fb() : fb) ?? null;\n },\n }),\n );\n };\n },\n});\n\n/**\n * Walk `definition.transitions` to assemble the set of entry-point descriptors\n * to preload. In `\"precise\"` mode we look at the current step's transitions only\n * and read each handler's declared `targets`; in `\"aggressive\"` mode we walk\n * every entry referenced anywhere in the map. Both skip the current step.\n */\nfunction collectPreloadTargets(\n definition: AnyJourneyDefinition,\n modules: Readonly<Record<string, ModuleDescriptor<any, any, any, any>>>,\n currentModuleId: string,\n currentEntry: string,\n mode: \"precise\" | \"aggressive\",\n): readonly ModuleEntryPoint<any>[] {\n const seen = new Set<string>();\n const out: ModuleEntryPoint<any>[] = [];\n const transitions = definition.transitions as\n | Record<string, Record<string, Record<string, unknown>> | undefined>\n | undefined;\n if (!transitions) return out;\n\n const collectPair = (moduleId: string, entryName: string): void => {\n if (!moduleId || !entryName) return;\n if (moduleId === currentModuleId && entryName === currentEntry) return;\n const seenKey = `${moduleId} ${entryName}`;\n if (seen.has(seenKey)) return;\n seen.add(seenKey);\n const entry = modules[moduleId]?.entryPoints?.[entryName];\n if (entry) out.push(entry);\n };\n\n if (mode === \"precise\") {\n const perEntry = transitions[currentModuleId]?.[currentEntry];\n if (!perEntry) return out;\n for (const value of Object.values(perEntry)) {\n if (!isAnnotatedTransition(value)) continue;\n for (const target of value.targets) {\n // Sentinel targets (`\"complete\"` / `\"abort\"` / `\"invoke\"`) carry no\n // chunk to preload — skip them here.\n if (typeof target === \"string\") continue;\n collectPair(target.module, target.entry);\n }\n }\n return out;\n }\n\n // Aggressive — every (module, entry) the journey could plausibly navigate to:\n // source-side keys UNIONED with the destinations declared by every annotated\n // handler (covers terminal-only destination steps).\n for (const [moduleId, perModule] of Object.entries(transitions)) {\n if (!perModule) continue;\n for (const [entryName, perExit] of Object.entries(perModule)) {\n collectPair(moduleId, entryName);\n if (!perExit) continue;\n for (const value of Object.values(perExit)) {\n if (!isAnnotatedTransition(value)) continue;\n for (const target of value.targets) {\n if (typeof target === \"string\") continue;\n collectPair(target.module, target.entry);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Returns true iff the entry's `mountKinds` permits the `\"journey\"` host.\n * Omitted `mountKinds` defaults to \"every mount surface\", so existing modules\n * that never opted in still work. Mirrors the composition outlet's\n * `entryAllowsCompositionMount`.\n */\nfunction entryAllowsJourneyMount(entry: { readonly mountKinds?: readonly string[] }): boolean {\n if (!Array.isArray(entry.mountKinds)) return true;\n return entry.mountKinds.includes(\"journey\");\n}\n\nconst DefaultNotFound = defineComponent({\n name: \"JourneyOutletNotFound\",\n props: {\n moduleId: { type: String, required: true },\n entry: { type: String, required: true },\n },\n setup(props) {\n return () =>\n h(\n \"div\",\n { style: { padding: \"1rem\", color: \"#c53030\" } },\n `Journey outlet: no entry \"${props.moduleId}.${props.entry}\" on the registered modules.`,\n );\n },\n});\n\nconst DefaultError = defineComponent({\n name: \"JourneyOutletError\",\n props: {\n moduleId: { type: String, required: true },\n error: { type: null as unknown as PropType<unknown>, default: undefined },\n },\n setup(props) {\n return () => {\n const err: unknown = props.error;\n const message = err instanceof Error ? err.message : String(err);\n return h(\n \"div\",\n {\n style: {\n padding: \"1rem\",\n border: \"1px solid #e53e3e\",\n borderRadius: \"0.5rem\",\n margin: \"1rem\",\n },\n role: \"alert\",\n \"data-journey-step-error\": props.moduleId,\n },\n [\n h(\n \"h3\",\n { style: { color: \"#e53e3e\", margin: \"0 0 0.5rem 0\" } },\n `Module \"${props.moduleId}\" encountered an error`,\n ),\n h(\n \"pre\",\n { style: { fontSize: \"0.875rem\", color: \"#718096\", whiteSpace: \"pre-wrap\" } },\n message,\n ),\n ],\n );\n };\n },\n});\n\n/**\n * Internal boundary the outlet wraps each step in. Analog of the React class\n * `StepErrorBoundary`: `onErrorCaptured` plays the role of\n * `getDerivedStateFromError` + `componentDidCatch`, flipping a local `error`\n * ref that swaps the subtree for the fallback and forwarding the error to\n * `onError` (which runs the outlet's retry/abort/ignore policy). Returning\n * `false` stops propagation to ancestor boundaries.\n *\n * The outlet keys this boundary by `${stepToken}:${retryKey}`, so a retry\n * (which bumps `retryKey`) remounts a fresh boundary that renders the step\n * again — the Vue analog of the React `key`-driven boundary reset.\n */\nconst StepErrorBoundary = defineComponent({\n name: \"JourneyStepErrorBoundary\",\n props: {\n moduleId: { type: String, required: true },\n onError: { type: Function as PropType<(err: unknown) => void>, required: true },\n errorComponent: { type: Object as PropType<Component>, default: undefined },\n },\n setup(props, { slots }) {\n const error = ref<unknown>(null);\n onErrorCaptured((err) => {\n error.value = err;\n props.onError(err);\n // Handled — don't propagate to ancestor boundaries.\n return false;\n });\n return () => {\n if (error.value != null) {\n const ErrorFallback = props.errorComponent ?? DefaultError;\n return h(ErrorFallback, { moduleId: props.moduleId, error: error.value });\n }\n return slots.default?.();\n };\n },\n});\n\n/**\n * Returns a reactive `ShallowRef` of the call stack for an outlet's instance —\n * root at index 0, the active leaf at the end, intermediate parents in between.\n * Useful for shells that render layered presentations (parent underneath, child\n * in a modal): mount the parent outlet with `leafOnly={false}` and the child\n * outlet against `chain.value[chain.value.length - 1]`. Returns a ref (not a\n * plain array) per the PR-30 reactive-source convention.\n */\nexport function useJourneyCallStack(\n runtime: JourneyRuntime,\n rootId: InstanceId,\n): ShallowRef<readonly InstanceId[]> {\n return useCallChain(runtime, rootId, true);\n}\n","import { defineComponent, h, type PropType, type VNode } from \"vue\";\nimport type { RuntimeMountAdapter } from \"@modular-frontend/core\";\nimport type { InstanceId, JourneyRuntime } from \"@modular-frontend/journeys-engine\";\nimport { JourneyOutlet } from \"./outlet.js\";\n\n/**\n * Adapt a {@link JourneyRuntime} to the generic\n * {@link RuntimeMountAdapter} shape so other packages can embed journeys\n * without depending on this package directly. Today the only consumer is\n * `@modular-vue/compositions` (zones with `kind: \"journey\"`):\n *\n * ```ts\n * import { createJourneyMountAdapter } from \"@modular-vue/journeys\";\n *\n * const manifest = registry.resolveManifest();\n * manifest.extensions.compositions.registerMountAdapter(\n * \"journey\",\n * createJourneyMountAdapter(manifest.extensions.journeys),\n * );\n * ```\n *\n * The wiring happens once after `resolveManifest()` and before mounting the\n * app, so the composition outlet finds the adapter the first time a zone\n * returns a `kind: \"journey\"` resolution. If the wiring is omitted, the\n * zone renders its `errorComponent` with a clear \"no adapter registered\"\n * message instead of throwing (see `CompositionOutlet`).\n *\n * The Vue analog of `@modular-react/journeys`'s `createJourneyMountAdapter`.\n *\n * Deviation from the React source, and why: React returns the bare\n * `JourneyOutlet` as `Outlet` and lets it read the journey runtime from the\n * `<JourneyProvider>` context that the journeys plugin threads app-wide. The\n * Vue `<CompositionOutlet>` renders `adapter.Outlet` with only\n * `{ instanceId, loadingFallback }` (no `runtime`), so the Vue adapter binds\n * the runtime it was handed into a thin wrapper component. This makes the\n * adapter self-contained — it mounts instances against exactly the runtime\n * passed to `createJourneyMountAdapter`, whether or not a `<JourneyProvider>`\n * sits above the composition outlet — rather than depending on an ambient\n * context that must happen to hold the same runtime.\n */\nexport function createJourneyMountAdapter(runtime: JourneyRuntime): RuntimeMountAdapter {\n const Outlet = defineComponent({\n name: \"JourneyMountAdapterOutlet\",\n props: {\n instanceId: { type: String as PropType<InstanceId>, required: true },\n loadingFallback: {\n type: null as unknown as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n },\n setup(props) {\n // Bind the captured `runtime` explicitly so the embedded journey mounts\n // against the runtime the adapter was built for — not whatever a\n // `<JourneyProvider>` above the composition outlet may (or may not) hold.\n return () =>\n h(JourneyOutlet, {\n runtime,\n instanceId: props.instanceId,\n loadingFallback: props.loadingFallback,\n });\n },\n });\n\n return {\n start(definitionId, input) {\n // The runtime's `start(journeyId, input)` overload accepts a bare id\n // string for dynamic dispatch — exactly what the adapter receives from\n // the composition outlet (which dereferences `handle.id` before calling).\n return runtime.start(definitionId, input);\n },\n end(instanceId) {\n // Cooperative cleanup: when the composition's per-zone cache evicts a\n // journey instance it minted, route through `end()` so the journey\n // runtime cascades into its own teardown rather than leaking the record\n // until the host process exits.\n runtime.end(instanceId, { reason: \"adapter-end\" });\n },\n Outlet,\n };\n}\n","import {\n computed,\n defineComponent,\n h,\n onMounted,\n onUnmounted,\n shallowRef,\n toRaw,\n type ComputedRef,\n type FunctionalComponent,\n type PropType,\n type ShallowRef,\n type VNode,\n} from \"vue\";\n\nimport type {\n InstanceId,\n JourneyHandle,\n JourneyInstance,\n JourneyRuntime,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { useInstanceSnapshot } from \"./instance-hooks.js\";\nimport { JourneyOutlet } from \"./outlet.js\";\nimport { useJourneyContext } from \"./provider.js\";\n\n/**\n * Tracks which host mount currently owns each started instance, so an\n * unmounting host's deferred teardown does not end and forget an instance a\n * same-tick replacement host has already resumed. With persistence,\n * `runtime.start()` returns the same id for the same input, so a route change\n * that unmounts one host and mounts another for the same journey hands the\n * live instance across — and the outgoing host's queued `end`/`forget` must\n * not fire against it. Keyed by runtime (a `WeakMap`, so it drops when the\n * runtime is collected) then by instance id.\n *\n * The value is a **set** of owner tokens, not a single one: two hosts can be\n * mounted on the same persisted id at once (concurrently, or across a route\n * swap), and only when the last of them unmounts should the instance be ended\n * and forgotten. A single \"latest owner\" would let the newer host, unmounting\n * first, tear the instance down while the older one is still showing it.\n */\nconst hostOwners = new WeakMap<JourneyRuntime, Map<InstanceId, Set<symbol>>>();\n\nfunction ownersFor(runtime: JourneyRuntime): Map<InstanceId, Set<symbol>> {\n let owners = hostOwners.get(runtime);\n if (!owners) {\n owners = new Map();\n hostOwners.set(runtime, owners);\n }\n return owners;\n}\n\nexport interface UseJourneyHostOptions {\n /**\n * Runtime to start the journey on. Optional when a `<JourneyProvider>` is\n * mounted above — the composable reads the runtime from context in that case.\n */\n readonly runtime?: JourneyRuntime;\n}\n\nexport interface JourneyHostState {\n /**\n * The instance this host owns. `null` until the component is mounted — the\n * journey is started from `onMounted`, so it does not exist during the first\n * render. `<JourneyHost>` renders `loadingFallback` for that frame.\n */\n readonly instanceId: ShallowRef<InstanceId | null>;\n readonly instance: ShallowRef<JourneyInstance | null>;\n /**\n * The runtime this host resolved at setup — the one `instanceId` is\n * meaningful to, and the one the host's own `<JourneyOutlet>` is given.\n * Already `toRaw`-unwrapped.\n *\n * Normally identical to whatever a `<JourneyProvider>` above you would hand\n * out, so a hand-placed outlet can keep resolving from context. It is\n * exposed for the case where those two could differ: a shell that swaps its\n * provider value mid-flight. The host does not follow such a swap (see\n * \"read once, at setup\"), so this is the runtime to use if you want to be\n * certain you are talking about the same journey the host started.\n */\n readonly runtime: JourneyRuntime;\n /**\n * How many steps the user has completed — `history.length`, so `0` on the\n * first step. Rewinds when the journey does.\n *\n * There is deliberately no `stepCount` here. The total is not knowable from\n * a running instance: a journey's next step is computed by a transition\n * handler from live state, so nothing short of walking the transition graph\n * can count the steps ahead — and a hand-passed total is exactly the\n * duplicated flow encoding this API exists to remove. Deriving it from the\n * graph is tracked separately.\n */\n readonly stepIndex: ComputedRef<number>;\n}\n\n/**\n * Own a journey instance for the lifetime of a component: start it on mount,\n * end and forget it on unmount. The Vue analog of the React\n * `useJourneyHost`.\n *\n * This is the lifecycle half of \"mount a journey\"; `useJourneySync` is the URL\n * half, and `<JourneyOutlet>` is the rendering half. {@link JourneyHost}\n * packages the first and the last together.\n *\n * **`handle`, `input` and `runtime` are read once, at setup**, and are\n * therefore plain values rather than the `MaybeRefOrGetter` the reactive\n * composables in this package take. Changing them cannot restart the journey:\n * silently abandoning a half-finished flow because a prop changed is never\n * what the caller meant. To run a different journey — or to run it on a\n * different runtime — remount: `<JourneyHost :key=\"journeyId\" …>`. The React\n * binding pins its runtime the same way.\n *\n * **Start means resume, when persistence is configured.** `runtime.start()`\n * with a `persistence` adapter returns the in-flight instance for the same\n * `keyFor(input)` rather than minting a new one, so a host that remounts (a\n * route change, a tab switch) picks the journey back up where it was. Without\n * persistence every mount starts a fresh instance.\n */\nexport function useJourneyHost<TInput>(\n handle: JourneyHandle<string, TInput, unknown>,\n input: TInput,\n options: UseJourneyHostOptions = {},\n): JourneyHostState {\n const ctx = useJourneyContext();\n // `toRaw` for the same reason the outlet does it — `getInternals` keys its\n // store on the raw runtime identity, and a runtime that arrived through a\n // reactive prop is a proxy.\n const runtime = toRaw(options.runtime ?? ctx?.runtime ?? undefined) ?? null;\n if (!runtime) {\n throw new Error(\n \"[@modular-vue/journeys] useJourneyHost needs a runtime. Either pass `runtime` or mount a <JourneyProvider>.\",\n );\n }\n\n const instanceId = shallowRef<InstanceId | null>(null);\n // Unique per host mount — see `hostOwners`.\n const ownerToken = Symbol(\"journey-host\");\n\n onMounted(() => {\n // Started from `onMounted` rather than `setup` so the start is guaranteed\n // to be paired with an `onUnmounted` that ends it. A component whose\n // `setup` ran but whose mount never completed (an ancestor threw first,\n // an async boundary discarded the branch) would otherwise leak a live\n // instance nothing owns. Also keeps the observable contract identical to\n // the React binding's.\n //\n // The string-id overload: the handle form's `...rest` tuple cannot be\n // satisfied from a generic `TInput` without widening the call site.\n // `handle.id` is the same value the handle overload reads.\n const id = runtime.start(handle.id, input);\n instanceId.value = id;\n // Claim ownership of the id by adding this mount's token to the id's owner\n // set (creating it on first mount).\n const owners = ownersFor(runtime);\n let ownerSet = owners.get(id);\n if (!ownerSet) {\n ownerSet = new Set();\n owners.set(id, ownerSet);\n }\n ownerSet.add(ownerToken);\n });\n\n onUnmounted(() => {\n const id = instanceId.value;\n if (!id) return;\n instanceId.value = null;\n const owners = ownersFor(runtime);\n // Deferred one microtask, matching `<JourneyOutlet>`: an inner outlet\n // schedules its own abandon the same way, and reading the record after\n // both have settled keeps the two from racing.\n queueMicrotask(() => {\n const set = owners.get(id);\n if (!set) return;\n // Drop this host's claim. Only tear down when no host still owns the id —\n // another host may share it (a route swap resuming the same persisted\n // instance, or two concurrent hosts), and it would keep showing the\n // instance we would otherwise end and forget.\n set.delete(ownerToken);\n if (set.size > 0) return;\n owners.delete(id);\n // `end` first, then `forget`: `forget` is a no-op on an instance that is\n // still active, and `end` is synchronous, so this ordering both aborts\n // the run and drops the record. An inner `<JourneyOutlet>` may have\n // already ended it — `end` no-ops on a terminal instance, so the race is\n // harmless in either order.\n //\n // `force`: the host owns this instance's lifetime, so unmount must leave\n // it terminal. Without force, an `onAbandon` returning a non-terminal\n // `{ next }` would advance the flow to a step no host renders and\n // `forget` would then no-op, leaking the instance and its persistence\n // key. Forcing coerces that to an abort; a terminal `onAbandon` choice\n // (complete/abort) is still honoured. The inner outlet's teardown also\n // forces, so whichever runs first terminates and the other no-ops —\n // `onAbandon` runs once, not twice. Matches the React host.\n runtime.end(id, { reason: \"unmounted\" }, { force: true });\n runtime.forget(id);\n });\n });\n\n const instance = useInstanceSnapshot(runtime, instanceId);\n\n return {\n instanceId,\n instance,\n runtime,\n stepIndex: computed(() => (instance.value ? instance.value.history.length : 0)),\n };\n}\n\n/**\n * Pull `loadingFallback` out of the host's attrs, accepting either spelling.\n *\n * Vue camelizes kebab-case only for **declared** props; a fallthrough attr\n * keeps whatever key the caller wrote. So a template's\n * `:loading-fallback=\"…\"` arrives as `attrs[\"loading-fallback\"]` while a\n * render function's `h(JourneyHost, { loadingFallback })` arrives camelCased,\n * and the host — which reads this attr itself rather than declaring it — has\n * to look under both. The inner `<JourneyOutlet>` needs no such help: it\n * *declares* `loadingFallback`, so Vue normalizes the kebab key for it.\n *\n * Without this the template spelling renders nothing for the frame before the\n * instance exists, then silently starts working once the outlet mounts.\n */\nfunction readLoadingFallback(attrs: Record<string, unknown>): VNode | (() => VNode) | undefined {\n return (attrs.loadingFallback ?? attrs[\"loading-fallback\"]) as VNode | (() => VNode) | undefined;\n}\n\nexport interface JourneyHostSlotProps {\n readonly instanceId: InstanceId;\n readonly instance: JourneyInstance;\n /** See {@link JourneyHostState.stepIndex}. */\n readonly stepIndex: number;\n /**\n * The runtime the host started the instance on — the same value\n * {@link JourneyHostState.runtime} exposes. Pass it to a hand-placed\n * `<JourneyOutlet :instance-id=\"instanceId\" :runtime=\"runtime\" />` when you'd\n * rather render the outlet yourself than use {@link JourneyHostSlotProps.outlet}\n * (the idiomatic-Vue path), so the outlet resolves the *same* runtime the host\n * pinned rather than whatever a nearer provider hands out.\n */\n readonly runtime: JourneyRuntime;\n /**\n * The `<JourneyOutlet>` for this instance as a **functional component**,\n * already bound to the host's pinned runtime and every outlet attribute passed\n * to `<JourneyHost>`. Render it with `<component :is=\"outlet\" />` in a template\n * (or `h(outlet)` in a render function) — it is a component, not a raw VNode,\n * so `:is` mounts it cleanly. Its identity is stable across host re-renders, so\n * `:is` patches the outlet in place instead of remounting it on every step.\n *\n * Equivalent to spelling `<JourneyOutlet :instance-id=\"instanceId\"\n * :runtime=\"runtime\" />` yourself with the slot's `instanceId` / `runtime` —\n * use whichever reads better in your chrome.\n */\n readonly outlet: FunctionalComponent;\n}\n\n/**\n * Mount a journey in one line:\n * `<JourneyHost :handle=\"checkoutHandle\" :input=\"{ cartId }\" />`.\n *\n * Starts the journey on mount, renders its current step, and ends + forgets\n * the instance on unmount — the wrapper every journey host ends up writing.\n * The Vue analog of the React `<JourneyHost>`.\n *\n * **Outlet props pass through as attributes.** `inheritAttrs: false` plus an\n * attrs spread onto the inner `<JourneyOutlet>` means every outlet prop\n * (`onFinished`, `onStepError`, `errorComponent`, `preload`, …) works on\n * `<JourneyHost>` without this component re-declaring — and drifting from —\n * the outlet's prop list. `loadingFallback` is the one attr the host also\n * reads itself, to cover the render before the instance exists — which is why\n * it has to accept both spellings; see {@link readLoadingFallback}.\n *\n * For chrome around the step, use the default scoped slot. The slot's `outlet`\n * is a functional component, so `<component :is=\"outlet\" />` renders it:\n *\n * ```vue\n * <JourneyHost :handle=\"checkoutHandle\" :input=\"{ cartId }\" @finished=\"goToReceipt\">\n * <template #default=\"{ stepIndex, outlet }\">\n * <Layout title=\"Checkout\" :step=\"stepIndex\">\n * <component :is=\"outlet\" />\n * </Layout>\n * </template>\n * </JourneyHost>\n * ```\n *\n * Or render the outlet yourself from the slot's `instanceId` + `runtime` — the\n * more idiomatic-Vue spelling, equivalent to the `outlet` above:\n *\n * ```vue\n * <template #default=\"{ stepIndex, instanceId, runtime }\">\n * <Layout title=\"Checkout\" :step=\"stepIndex\">\n * <JourneyOutlet :instance-id=\"instanceId\" :runtime=\"runtime\" />\n * </Layout>\n * </template>\n * ```\n *\n * To deep-link the steps, call `useJourneySync` in the same component — the\n * host owns the instance, the sync owns the URL, and neither knows about the\n * other.\n *\n * See {@link useJourneyHost} for the lifecycle rules (the instance is fixed\n * for the host's lifetime; `start` resumes when persistence is configured).\n */\nexport const JourneyHost = defineComponent({\n name: \"JourneyHost\",\n inheritAttrs: false,\n props: {\n handle: {\n type: Object as PropType<JourneyHandle<string, any, any>>,\n required: true,\n },\n /**\n * The journey's `input`. Typed `any` because a `defineComponent` prop\n * list cannot carry the handle's `TInput` through — reach for\n * {@link useJourneyHost} in a `<script setup>` block when you want the\n * input checked against the handle.\n */\n input: { type: null as unknown as PropType<any>, default: undefined },\n /**\n * Runtime to start the journey on, forwarded to the outlet. Optional when\n * a `<JourneyProvider>` is mounted above.\n */\n runtime: { type: Object as PropType<JourneyRuntime>, default: undefined },\n },\n setup(props, { slots, attrs }) {\n const { instanceId, instance, runtime, stepIndex } = useJourneyHost(props.handle, props.input, {\n runtime: props.runtime,\n });\n\n // Build the outlet vnode for the currently-owned instance. Reads\n // `instanceId.value` / `attrs` at call time, so it tracks the live step.\n // The resolved runtime, not `props.runtime`: `instanceId` only means\n // anything on the runtime the host started it on, so forwarding a later\n // prop value would point the outlet at a runtime that has never heard of\n // this instance.\n const renderOutlet = (): VNode | null => {\n const id = instanceId.value;\n if (!id) return null;\n return h(JourneyOutlet, { ...attrs, runtime, instanceId: id });\n };\n\n // A STABLE functional-component identity wrapping the outlet, handed to the\n // slot as `outlet`. Stable across host re-renders on purpose: a fresh\n // component identity each render would make `<component :is=\"outlet\" />`\n // remount the outlet (and restart the step component / start-on-mount) on\n // every step, instead of patching it in place. Exposing a component (rather\n // than a raw VNode) is what lets `:is` render it cleanly in a template.\n const OutletComponent: FunctionalComponent = () => renderOutlet();\n\n return () => {\n const id = instanceId.value;\n const inst = instance.value;\n if (!id || !inst) {\n const fallback = readLoadingFallback(attrs);\n if (!fallback) return null;\n return typeof fallback === \"function\" ? fallback() : fallback;\n }\n\n const slot = slots.default;\n if (!slot) return renderOutlet();\n return slot({\n instanceId: id,\n instance: inst,\n runtime,\n stepIndex: stepIndex.value,\n outlet: OutletComponent,\n } satisfies JourneyHostSlotProps);\n };\n },\n});\n","import { toRaw, toValue, watch, watchEffect, type MaybeRefOrGetter } from \"vue\";\n\nimport { createJourneySync, defaultStepPath } from \"@modular-frontend/journeys-engine\";\nimport type {\n InstanceId,\n JourneyRuntime,\n JourneySyncCallbackCtx,\n JourneySyncOptions,\n JourneySyncPort,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { useJourneyContext } from \"./provider.js\";\n\nexport interface UseJourneySyncOptions extends JourneySyncOptions {\n /**\n * Runtime to sync against. Optional when a `<JourneyProvider>` is mounted\n * above — the composable reads the runtime from context in that case. Read\n * once at setup, matching how the other composables in this package resolve\n * their runtime.\n */\n readonly runtime?: JourneyRuntime;\n}\n\n/**\n * Keep a journey instance and the browser URL in step, in both directions:\n * the journey advances and the URL follows; the user presses Back or Forward\n * and the journey follows.\n *\n * The reconciler itself is framework- and router-neutral and lives in\n * `@modular-frontend/journeys-engine`; this composable is the Vue lifetime\n * wrapper around it, and the exact analog of the React `useJourneySync`. See\n * `createJourneySync` in the engine for the full semantics (push-vs-replace\n * rules, what a location can and cannot select, why the design is\n * event-sourced rather than a diff).\n *\n * Supply a {@link JourneySyncPort} for vue-router. It is the only router-aware\n * code in the picture:\n *\n * ```ts\n * const router = useRouter();\n * const route = useRoute();\n * const port: JourneySyncPort = {\n * read: () => String(route.params.step ?? \"\"),\n * push: (path) => void router.push({ name: \"checkout\", params: { step: path } }),\n * replace: (path) => void router.replace({ name: \"checkout\", params: { step: path } }),\n * go: (delta) => router.go(delta),\n * subscribe: (listener) => router.afterEach(() => listener()),\n * };\n *\n * useJourneySync(instanceId, port, { stepToPath: (step) => step.entry });\n * ```\n *\n * Conventions:\n *\n * - **`instanceId` accepts a plain value, a ref, or a getter**, and may be\n * `null` — the composable no-ops until an id arrives, so it can be called at\n * setup before the instance exists (as it must be under `useJourneyHost`,\n * which starts the journey on mount). The sync re-attaches when the id\n * changes.\n * - **`port` and `options` may be plain, refs, or getters.** Both are read\n * through a live holder rather than re-creating the reconciler, because\n * re-creating it re-runs its initial reconcile — and that reconcile\n * navigates. Pass a getter when the port itself depends on reactive state.\n * Two things are read once rather than live: `port.subscribe`, called when\n * the sync is created (so the subscription belongs to the port current at\n * that moment), and `options.stepToPath`, captured then too (the engine\n * needs a stable step->path mapping to resolve already-stamped history\n * frames). Both are invisible for the normal case — a port over a stable\n * router with a fixed mapping — but a port that subscribes to a *different*\n * source later, or a mapping that changes, will not take effect until the\n * sync is re-created. `onUnresolved`/`onBlocked` remain live. Change\n * `instanceId` (or remount) to re-attach.\n * - **The composable never starts or ends the instance.** It only navigates\n * within a journey that is already running. Pair it with\n * {@link JourneyHost}, which owns the lifecycle.\n */\nexport function useJourneySync(\n instanceId: MaybeRefOrGetter<InstanceId | null>,\n port: MaybeRefOrGetter<JourneySyncPort>,\n options: MaybeRefOrGetter<UseJourneySyncOptions> = {},\n): void {\n const ctx = useJourneyContext();\n // `toRaw` for the same reason the outlet does it: `getInternals` keys its\n // store on the raw runtime identity handed out by `createJourneyRuntime`,\n // and a value that reached us through a reactive prop is a proxy.\n const runtime = toRaw(toValue(options).runtime ?? ctx?.runtime ?? undefined) ?? null;\n\n // Live holder — `setup` runs once, so a caller whose port or callbacks\n // depend on reactive state passes a getter and this keeps up. Mirrors the\n // `latest` pattern in `useWaitForExit`.\n const latest = {\n port: toValue(port),\n options: toValue(options),\n };\n watchEffect(() => {\n latest.port = toValue(port);\n latest.options = toValue(options);\n });\n\n // `go` is optional on the port and its *presence* is load-bearing — the\n // reconciler falls back to `replace` without it — so the proxy has to\n // present `undefined` when the current port has none, not a function that\n // silently does nothing.\n const stablePort: JourneySyncPort = {\n read: () => latest.port.read(),\n push: (path) => latest.port.push(path),\n replace: (path) => latest.port.replace(path),\n get go() {\n return latest.port.go ? (delta: number) => latest.port.go?.(delta) : undefined;\n },\n subscribe: (listener) => latest.port.subscribe(listener),\n };\n\n watch(\n () => toValue(instanceId),\n (id, _previous, onCleanup) => {\n if (!runtime || !id) return;\n // `stepToPath` is captured once per sync, not read live: the engine\n // relies on a stable step->path mapping to resolve the history frames the\n // port has already stamped, so swapping it mid-flight would strand\n // previously visited paths as unresolved. `onUnresolved`/`onBlocked` stay\n // live. Change `instanceId` (or remount) to adopt a new mapping.\n const stepToPath = latest.options.stepToPath ?? defaultStepPath;\n const sync = createJourneySync(runtime, id, stablePort, {\n stepToPath,\n onUnresolved: (syncCtx: JourneySyncCallbackCtx) => latest.options.onUnresolved?.(syncCtx),\n onBlocked: (syncCtx: JourneySyncCallbackCtx) => latest.options.onBlocked?.(syncCtx),\n });\n // Runs before the next id's sync is created, and again when the\n // watcher is stopped as the component's scope is disposed.\n onCleanup(() => sync.stop());\n },\n { immediate: true },\n );\n}\n","import {\n computed,\n defineComponent,\n h,\n Suspense,\n toRaw,\n type Component,\n type PropType,\n type VNode,\n} from \"vue\";\nimport type { ModuleDescriptor } from \"@modular-frontend/core\";\nimport {\n ModuleErrorBoundary,\n resolveEntryComponent,\n useModuleExit,\n type ModuleExitEvent,\n} from \"@modular-vue/vue\";\n\n/**\n * Exit event fired by a module rendered inside a `<ModuleTab>`.\n *\n * Alias for {@link ModuleExitEvent} from `@modular-vue/vue` — kept as a named\n * export for the workspace-tab entry point so existing imports keep compiling.\n * Both types have the same shape.\n */\nexport type ModuleTabExitEvent = ModuleExitEvent;\n\ninterface EntryResolution {\n readonly resolvedName: string | undefined;\n readonly missingEntryNotice: string | null;\n}\n\n/**\n * Resolve the entry name for a `<ModuleTab>` from its module + optional `entry`\n * prop, matching the React source's rules: auto-pick a lone entry, require\n * disambiguation for a multi-entry module, surface a notice for an unknown\n * entry, and refuse to fall through to the legacy `component` when an explicit\n * `entry` is passed to a module with no entry points.\n */\nfunction resolveEntryName(\n mod: ModuleDescriptor<any, any, any, any>,\n entry: string | undefined,\n): EntryResolution {\n const entryPoints = mod.entryPoints;\n const entryNames = entryPoints ? Object.keys(entryPoints) : [];\n let resolvedName: string | undefined = entry;\n let missingEntryNotice: string | null = null;\n if (entry === undefined) {\n if (entryNames.length === 1) {\n resolvedName = entryNames[0];\n } else if (entryNames.length > 1) {\n missingEntryNotice = `Module \"${mod.id}\" exposes multiple entries (${entryNames.join(\", \")}); pass the \\`entry\\` prop to disambiguate.`;\n }\n } else if (entryPoints && !Object.prototype.hasOwnProperty.call(entryPoints, entry)) {\n missingEntryNotice = `Module \"${mod.id}\" has no entry \"${entry}\". Registered: ${entryNames.join(\", \") || \"(none)\"}.`;\n } else if (!entryPoints) {\n // `entry` requested but module exposes no entry points at all — surface\n // the misconfiguration instead of silently falling through to the legacy\n // `component` path.\n resolvedName = undefined;\n missingEntryNotice = `Module \"${mod.id}\" has no entry points; \\`entry=\"${entry}\"\\` cannot be resolved.`;\n }\n return { resolvedName, missingEntryNotice };\n}\n\nfunction notice(message: string): VNode {\n return h(\"div\", { style: { padding: \"1rem\", color: \"#c53030\" } }, message);\n}\n\n/**\n * Host for a single module instance rendered outside any route — in a tab,\n * modal, or panel. Default exit behavior delegates to the `onExit` callback\n * provided by the shell; the module itself stays journey-unaware. The Vue\n * analog of the React `<ModuleTab>`.\n *\n * Authored with `defineComponent` + a render function (no SFC compiler in the\n * package build; see decision D4). The exit binding is created once at setup\n * via `useModuleExit` (Vue composables must run synchronously in `setup`),\n * reading the current module id / resolved entry through getters so a host that\n * swaps `module`/`entry` on the same instance still emits the right event.\n *\n * `inheritAttrs` is disabled and `input` is read from `attrs` so the component\n * can tell \"no `input` prop\" apart from an explicit `input={undefined}` — Vue\n * collapses both to `undefined` for a *declared* prop (an explicit `undefined`\n * triggers the prop default), so the presence check has to look at the raw\n * attrs bag, the analog of the React source's `\"input\" in props`.\n */\nexport const ModuleTab = defineComponent({\n name: \"ModuleTab\",\n inheritAttrs: false,\n props: {\n /** Full module descriptor — the shell looks this up by id. */\n module: {\n type: Object as PropType<ModuleDescriptor<any, any, any, any>>,\n required: true,\n },\n /**\n * Entry point name on the module. If omitted and the module exposes exactly\n * one entry, that entry is used automatically. If the module exposes several\n * entries, the name must be supplied — passing an unknown name renders an\n * error notice. If `entry` is omitted and the module has no entry points,\n * the component falls back to the legacy `component` field; passing `entry`\n * to such a module instead renders the error notice so misconfiguration is\n * surfaced.\n */\n entry: { type: String, default: undefined },\n /** Opaque tab id threaded through to `onExit` for the shell to close it. */\n tabId: { type: String, default: undefined },\n /**\n * Called when the module emits an exit. Runs *before* the provider's global\n * `onExit` dispatcher (via `<ModuleExitProvider>`, typically composed under\n * `<JourneyProvider>`), so the shell can close the tab first and let the\n * provider hook forward to analytics / routing.\n */\n onExit: { type: Function as PropType<(event: ModuleTabExitEvent) => void>, default: undefined },\n },\n setup(props, { attrs }) {\n // Unwrap the reactive prop proxy: Vue deeply proxies prop objects, which\n // would change the entry-object identity that keys `resolveEntryComponent`'s\n // per-entry `WeakMap` cache — breaking preload/render chunk sharing and the\n // memoized async wrapper. `toRaw` restores the descriptor the runtime and\n // preload paths hold.\n const rawModule = () => toRaw(props.module);\n const resolution = computed(() => resolveEntryName(rawModule(), props.entry));\n\n // Hook binding must be created once at setup — always bind, even when the\n // entry is missing. When missing, the returned `exit` is never invoked\n // because we render the error notice instead.\n const exit = useModuleExit(\n () => props.module.id,\n () => resolution.value.resolvedName ?? \"\",\n {\n tabId: () => props.tabId,\n localOnExit: () => props.onExit,\n },\n );\n\n return () => {\n const mod = rawModule();\n const { resolvedName, missingEntryNotice } = resolution.value;\n const entryPoint = resolvedName ? mod.entryPoints?.[resolvedName] : undefined;\n\n let content: VNode | null;\n if (missingEntryNotice) {\n content = notice(missingEntryNotice);\n } else if (entryPoint) {\n // The entry's declared input schema is the source of truth for whether\n // `input` is required. At runtime we don't reflect on the schema, but\n // we can still refuse to render with a visibly wrong `undefined` when\n // the caller forgot to pass it — that surfaces the misconfiguration\n // instead of letting the component throw deep inside its render.\n // Callers whose entry schema is `void` should pass `input={undefined}`\n // explicitly.\n const hasInput = \"input\" in attrs;\n const input = (attrs as Record<string, unknown>).input;\n if (input === undefined && !hasInput) {\n content = notice(\n `Module \"${mod.id}\" entry \"${resolvedName ?? \"\"}\" was rendered without an \\`input\\` prop. ` +\n `Pass \\`input={undefined}\\` explicitly if the entry accepts no input.`,\n );\n } else {\n const { Component } = resolveEntryComponent(entryPoint);\n const fallback = (entryPoint as { fallback?: VNode | (() => VNode) }).fallback;\n content = h(Suspense, null, {\n default: () => h(Component, { input, exit }),\n fallback: () => (typeof fallback === \"function\" ? fallback() : (fallback ?? null)),\n });\n }\n } else if (mod.component) {\n // Back-compat: render the legacy workspace component when the module\n // exposes no entry points. Entry contracts are opt-in.\n const Legacy = mod.component as Component;\n const input = (attrs as Record<string, unknown>).input;\n content = h(Legacy, { input, tabId: props.tabId });\n } else {\n content = notice(`Module \"${mod.id}\" has no entry points and no component.`);\n }\n\n return h(ModuleErrorBoundary, { moduleId: mod.id }, () => content);\n };\n },\n});\n","import { toValue, watch, watchEffect, type MaybeRefOrGetter } from \"vue\";\nimport {\n isDevEnv,\n type ExitFn,\n type ExitPointMap,\n type ExitPointSchema,\n} from \"@modular-frontend/core\";\n\n/**\n * Exit names whose schema declares a `void` output. The named-exit form\n * of {@link WaitForExitTimeoutChannel} narrows to these so a deadline\n * dispatching `resolve(name)` without an output is sound. Exits that\n * require a payload must use the function form\n * (`fire: (resolve) => resolve(\"name\", payload)`).\n */\ntype VoidExitName<TExits extends ExitPointMap> = {\n [K in keyof TExits & string]: TExits[K] extends ExitPointSchema<infer T>\n ? [T] extends [void]\n ? K\n : never\n : never;\n}[keyof TExits & string];\n\n/**\n * One channel that races against the others. `resolve` is the wrapped exit\n * dispatcher: calling it fires the named exit if no other channel has fired\n * yet, and is a no-op otherwise. The subscriber returns its own teardown,\n * which the composable calls **immediately** when any channel wins (not just on\n * unmount) so the losing channels stop doing wakeups between dispatch and\n * unmount.\n *\n * @example\n * ```ts\n * subscribe: (resolve) => {\n * const handler = (msg: WsFrame) => {\n * if (msg.type === \"process-ready\") resolve(\"ready\", { process: msg.payload });\n * };\n * ws.on(\"message\", handler);\n * return () => ws.off(\"message\", handler);\n * }\n * ```\n */\nexport type WaitForExitSubscribeChannel<TExits extends ExitPointMap> = (\n resolve: ExitFn<TExits>,\n) => () => void;\n\n/**\n * Periodic check that fires when the answer is ready. The interval is torn\n * down the moment any channel wins, so `check` is not invoked again after\n * settle. `check` itself should still cancel its own outstanding async\n * work on unmount where applicable (e.g. via `AbortController`).\n *\n * @example\n * ```ts\n * poll: {\n * intervalMs: 3000,\n * check: async (resolve) => {\n * const process = await api.getTranslationProcess(projectId);\n * if (process) resolve(\"ready\", { process });\n * },\n * }\n * ```\n */\nexport interface WaitForExitPollChannel<TExits extends ExitPointMap> {\n readonly intervalMs: number;\n readonly check: (resolve: ExitFn<TExits>) => void | Promise<void>;\n}\n\n/**\n * Deadline arm. The simple form fires a named exit; the function form runs\n * arbitrary code (e.g. dispatches a different exit based on `input`). Pass\n * `0` or a negative `ms` to disable the timeout for a render without\n * conditionally calling the composable.\n *\n * @example\n * ```ts\n * // Named form — exit must have a `void` output schema.\n * timeout: { ms: 60_000, fire: \"timedOut\" }\n *\n * // Function form — choose the exit (and its payload) at deadline time.\n * timeout: {\n * ms: 60_000,\n * fire: (resolve) => resolve(\"failed\", { reason: \"deadline-exceeded\" }),\n * }\n * ```\n */\nexport type WaitForExitTimeoutChannel<TExits extends ExitPointMap> = {\n readonly ms: number;\n} & (\n | { readonly fire: VoidExitName<TExits> }\n | { readonly fire: (resolve: ExitFn<TExits>) => void }\n);\n\nexport interface WaitForExitChannels<TExits extends ExitPointMap> {\n /** Push channel — websocket, SSE, push notification, server-sent intent. */\n readonly subscribe?: WaitForExitSubscribeChannel<TExits>;\n /** Polling fallback. Omit when the push channel is reliable. */\n readonly poll?: WaitForExitPollChannel<TExits>;\n /** Deadline arm. Omit when no fallback is needed. */\n readonly timeout?: WaitForExitTimeoutChannel<TExits>;\n}\n\n/**\n * Wait for one of several async channels to fire and dispatch a journey\n * exit when the first one resolves. Encapsulates the cancellation latch,\n * channel teardown, and first-wins coordination so step components stay\n * declarative. The Vue analog of the React `useWaitForExit` hook.\n *\n * Channel callbacks (`subscribe`, `poll.check`, `timeout.fire`) and the\n * `exit` dispatcher are captured live, so changing their identity between\n * renders does NOT restart the wait. The wait restarts on changes to scalar\n * configuration (`poll.intervalMs`, `timeout.ms`, the timeout's named-exit\n * form) so a deadline that genuinely shifts mid-wait is honored.\n *\n * Both `exit` and `channels` accept a plain value, a ref, or a getter\n * ({@link MaybeRefOrGetter}) — because `setup` runs once, a caller whose\n * channel configuration changes on the same instance should pass a getter (or\n * a ref) so the wait re-arms on scalar changes and picks up the latest\n * callbacks; plain values still work for hosts whose configuration is fixed for\n * the mount. This mirrors how the React hook re-reads its props on re-render.\n *\n * Conventions:\n *\n * - At least one of `subscribe`, `poll`, `timeout` should be present.\n * With none, the composable is a no-op until the component unmounts.\n *\n * - `resolve(name, output)` is the *latched* exit dispatcher. The latch\n * is local to one arm cycle; the runtime's step-token mechanism is the\n * global backstop (an exit dispatched after the step has advanced is\n * dropped by the runtime even if the local latch were missing).\n *\n * - The composable does not own the journey-level \"what does each exit mean\"\n * decision. That lives in the journey's `transitions` map. It only marshals\n * the channels.\n *\n * - **First-wins teardown is immediate.** When any channel calls `resolve`,\n * the *other* channels' teardowns run before the exit dispatches. A subscribe\n * that synchronously calls `resolve` during setup still has its `unsubscribe`\n * invoked; poll / timeout are not armed at all in that case.\n *\n * - The arm cycle is monolithic: changing any scalar dep (`pollInterval`,\n * `timeoutMs`, the timeout's named-exit form) tears down *all* live channels\n * and re-arms them. Callers that need independent per-channel lifecycles\n * should split into multiple `useWaitForExit` calls or manage that channel\n * themselves.\n */\nexport function useWaitForExit<TExits extends ExitPointMap>(\n exit: MaybeRefOrGetter<ExitFn<TExits>>,\n channels: MaybeRefOrGetter<WaitForExitChannels<TExits>>,\n): void {\n // Latest-callback holders so changing identities don't churn the arm cycle.\n // The `watchEffect` keeps them fresh on every reactive change to `exit` /\n // `channels`; the arm cycle reads them at arm time and inside the channel\n // callbacks — never the arguments directly.\n const latest: {\n exit: ExitFn<TExits> | undefined;\n subscribe: WaitForExitChannels<TExits>[\"subscribe\"];\n pollCheck: WaitForExitPollChannel<TExits>[\"check\"] | undefined;\n timeoutFire: WaitForExitTimeoutChannel<TExits>[\"fire\"] | undefined;\n } = {\n exit: undefined,\n subscribe: undefined,\n pollCheck: undefined,\n timeoutFire: undefined,\n };\n\n watchEffect(() => {\n const ch = toValue(channels);\n latest.exit = toValue(exit);\n latest.subscribe = ch.subscribe;\n latest.pollCheck = ch.poll?.check;\n latest.timeoutFire = ch.timeout?.fire;\n });\n\n const arm = (onCleanup: (fn: () => void) => void): void => {\n const ch = toValue(channels);\n const hasSubscribe = ch.subscribe != null;\n const hasPoll = ch.poll != null;\n const hasTimeout = ch.timeout != null;\n const pollInterval = ch.poll?.intervalMs;\n const timeoutMs = ch.timeout?.ms;\n\n let settled = false;\n const teardowns: Array<() => void> = [];\n\n const safeTeardown = (td: () => void) => {\n try {\n td();\n } catch (err) {\n if (isDevEnv()) {\n // A misbehaving channel teardown shouldn't block sibling teardowns\n // or prevent unmount — but it's almost always a bug worth flagging\n // in dev so the author sees it.\n console.warn(\"[useWaitForExit] channel teardown threw:\", err);\n }\n }\n };\n\n const drainTeardowns = () => {\n // Drain so a user-supplied teardown never runs twice — websocket\n // close / AbortController.abort / unsubscribe-from-emitter are not\n // always idempotent.\n const list = teardowns.splice(0);\n for (const td of list) safeTeardown(td);\n };\n\n // First-wins latched dispatcher. On the winning call we tear down every\n // *other* channel before dispatching, so the losing channels can't do\n // wakeups between dispatch and unmount.\n const resolve: ExitFn<TExits> = ((name: Parameters<ExitFn<TExits>>[0], ...args: unknown[]) => {\n if (settled) return;\n settled = true;\n drainTeardowns();\n (latest.exit as unknown as (n: any, ...a: unknown[]) => void)?.(name, ...args);\n }) as ExitFn<TExits>;\n\n const registerTeardown = (td: () => void) => {\n // A subscribe that synchronously called `resolve` will have drained\n // the (empty-at-that-moment) teardown list before its unsubscribe\n // was returned. We still need to release that listener, just not\n // via the list.\n if (settled) safeTeardown(td);\n else teardowns.push(td);\n };\n\n if (hasSubscribe) {\n const unsubscribe = latest.subscribe?.(resolve);\n if (typeof unsubscribe === \"function\") registerTeardown(unsubscribe);\n }\n\n // If a synchronous subscribe-resolve already won, don't arm the fallback\n // channels at all — the wait is already over.\n if (!settled && hasPoll && pollInterval !== undefined && pollInterval > 0) {\n const id = setInterval(() => {\n if (settled) return;\n try {\n const result = latest.pollCheck?.(resolve);\n if (result && typeof (result as PromiseLike<void>).then === \"function\") {\n // `Promise.resolve` adopts the thenable into a real Promise, so we\n // don't depend on the user's value having a `.catch`.\n Promise.resolve(result as PromiseLike<void>).catch(() => {\n // Polling failures are non-fatal: the next tick gets another\n // chance, and the push / timeout channels still race.\n });\n }\n } catch {\n // Sync-throw from `check` — same rationale as the async catch.\n }\n }, pollInterval);\n registerTeardown(() => clearInterval(id));\n }\n\n if (!settled && hasTimeout && timeoutMs !== undefined && timeoutMs > 0) {\n const id = setTimeout(() => {\n if (settled) return;\n const fire = latest.timeoutFire;\n if (typeof fire === \"string\") {\n // Sound because the named form's type is constrained to\n // `VoidExitName<TExits>` — exits whose schema is `void`.\n (resolve as unknown as (n: string) => void)(fire);\n } else if (typeof fire === \"function\") {\n fire(resolve);\n }\n }, timeoutMs);\n registerTeardown(() => clearTimeout(id));\n }\n\n onCleanup(() => {\n settled = true;\n drainTeardowns();\n });\n };\n\n // Re-arm only when the wait's *shape* changes. The key folds the presence of\n // each channel and the scalar values that change what the wait does; callback\n // identity is deliberately excluded (it lives in `latest`), so a re-render\n // that only hands over fresh closures does not restart. The named-exit form\n // of `timeout.fire` is part of the key because changing it changes what the\n // deadline fires; the function form is read live from `latest`.\n watch(\n () => {\n const ch = toValue(channels);\n const timeoutNamedExit =\n ch.timeout && typeof ch.timeout.fire === \"string\" ? ch.timeout.fire : undefined;\n return [\n ch.subscribe != null,\n ch.poll != null,\n ch.timeout != null,\n ch.poll?.intervalMs,\n ch.timeout?.ms,\n timeoutNamedExit,\n ].join(\"|\");\n },\n (_key, _old, onCleanup) => {\n arm(onCleanup);\n },\n { immediate: true },\n );\n}\n","import type { App } from \"vue\";\nimport type { ModuleExitEvent } from \"@modular-vue/vue\";\n\nimport type { JourneyRuntime } from \"@modular-frontend/journeys-engine\";\nimport { journeyKey, type JourneyProviderValue } from \"./provider.js\";\n\nexport interface ProvideJourneyRuntimeOptions {\n /**\n * Shell-wide fallback dispatcher for module exits fired outside a journey\n * step — the app-level equivalent of `<JourneyProvider :onModuleExit>`.\n */\n readonly onModuleExit?: (event: ModuleExitEvent) => void;\n}\n\n/**\n * App-level twin of `<JourneyProvider>`: install the journey runtime on a Vue\n * `App` via `app.provide` so `<JourneyOutlet>` and the instance composables\n * resolve it from context without a wrapping component in the render tree. The\n * journeys analog of `@modular-vue/vue`'s `provideNavigation` / `provideSlots`\n * standalone helpers.\n *\n * **You usually do not need to call this.** When the registry is built with\n * `journeysPlugin()`, the plugin's `appProvides` hook already threads the\n * runtime app-wide through `app.use(manifest)` (the router-owning `resolve()` /\n * `installModularApp` path) — `<JourneyOutlet>` resolves `journeyKey` with no\n * shell wiring. This helper is the explicit escape hatch for the cases that\n * bypass that:\n *\n * - a shell that constructs a `JourneyRuntime` by hand (no `journeysPlugin()`),\n * - installing the same runtime on a second app (SSR, multiple roots),\n * - overriding the app-wide runtime for a subtree that mounts its own app.\n *\n * ```ts\n * // Hand-wired runtime, no plugin:\n * const runtime = createJourneyRuntime(registered, { modules });\n * provideJourneyRuntime(app, runtime, { onModuleExit: (e) => reportExit(e) });\n * ```\n *\n * A local `<JourneyProvider :runtime>` mounted lower in the tree still wins for\n * its subtree (component-level `inject` shadows the app-level provide), so this\n * only supplies the default when no nearer provider is present.\n */\nexport function provideJourneyRuntime(\n app: App,\n runtime: JourneyRuntime,\n options?: ProvideJourneyRuntimeOptions,\n): void {\n const value: JourneyProviderValue = {\n runtime,\n onModuleExit: options?.onModuleExit,\n };\n app.provide(journeyKey, value);\n}\n","import type { SerializedJourney, SyncJourneyPersistence } from \"@modular-frontend/journeys-engine\";\n\n/**\n * The minimal structural slice of a Pinia store this adapter needs: read the\n * reactive `$state` and mutate it through `$patch`. A real Pinia store (option\n * or setup) satisfies this, but the shape is intentionally structural so this\n * package takes **no** `pinia` dependency — the caller brings their own store.\n *\n * This is the load-bearing design choice behind decision D3\n * (`docs/vue-support-tracker.md`): \"do not take a Pinia dependency in runtime\n * packages\". The adapter is a thin bridge over a store the consumer already\n * owns, not a reason for every `@modular-vue/journeys` consumer to pull Pinia.\n */\nexport interface PiniaJourneyPersistenceStore {\n /**\n * The store's reactive state object (`store.$state`). Typed as `object`\n * rather than `Record<string, unknown>` so a concrete Pinia store — whose\n * `$state` is an interface without an index signature — is structurally\n * assignable without the caller casting. The adapter narrows internally.\n */\n readonly $state: object;\n /** Apply a mutation to `$state` (`store.$patch((s) => { … })`). */\n $patch(mutator: (state: any) => void): void;\n}\n\nexport interface PiniaJourneyPersistenceOptions<TInput> {\n /**\n * Compute the persistence key from the journey id and starting input.\n * Must be deterministic — `runtime.start()` probes this key to find an\n * existing instance and achieve start-means-resume idempotency.\n */\n readonly keyFor: (ctx: { journeyId: string; input: TInput }) => string;\n /**\n * The Pinia store that owns the serialized-journey record, or a lazy getter\n * returning it (or `null`). The getter is invoked on every call, mirroring\n * `createWebStoragePersistence`'s `storage` option — pass a getter when the\n * store is created inside a Pinia scope resolved after this adapter is\n * constructed, or `null` to force the no-op (server / no-store) path.\n */\n readonly store: PiniaJourneyPersistenceStore | (() => PiniaJourneyPersistenceStore | null);\n /**\n * Property on the store's `$state` that holds the\n * `Record<string, SerializedJourney<TState>>` map. Defaults to `\"journeys\"`.\n * The property is created on first `save` if absent.\n */\n readonly stateKey?: string;\n /**\n * When true (default), stored blobs are deep-cloned on both `save` and\n * `load`, so `load` returns a plain object detached from the store: callers\n * mutating it cannot corrupt the reactive state, and a mutated source blob\n * cannot corrupt what was persisted. The clone is a structural JSON round-trip\n * (`JSON.parse(JSON.stringify(...))`), which matches the `SerializedJourney`\n * contract the engine persists — plain JSON — but, like any storage adapter,\n * drops `undefined` fields and cannot carry `Date` / `Map` / `Set`. Journey\n * blobs are already JSON by construction, so this only bites if a custom\n * persistence layer routes non-serializable state through here.\n *\n * When false, the clone is skipped. Note Pinia still wraps stored state in a\n * reactive proxy, so `load` never returns the exact reference passed to\n * `save`; instead it returns the **live** store entry (mutations to it are\n * visible on the next `load` and to Pinia devtools). Use only when you have\n * verified nobody mutates the loaded blob out from under the store.\n */\n readonly clone?: boolean;\n}\n\n/**\n * `JourneyPersistence` backed by a Pinia store — the Vue-ecosystem analog of\n * `createWebStoragePersistence`, keeping journey state inside the app's\n * existing Pinia store tree instead of a parallel storage mechanism.\n *\n * Because the record lives in Pinia, in-flight journeys participate in the\n * app's Pinia devtools / timeline, and a single `store.$reset()` (or clearing\n * the record) drops every persisted journey through one path the app already\n * owns.\n *\n * Semantics match the stock adapters exactly: `keyFor` derives the key,\n * `load` returns the stored blob (or `null`), `save` writes it, `remove`\n * deletes it — so `runtime.start()` resumes an in-flight instance for the same\n * `keyFor(input)` rather than minting a fresh one.\n *\n * Pure-client-safe like the web-storage adapter: pass `store` as a getter that\n * returns `null` under SSR and all four methods no-op, so the runtime mints a\n * fresh instance server-side.\n *\n * ```ts\n * // A tiny store whose only job is to hold serialized journeys.\n * const useJourneyStore = defineStore(\"journeys\", {\n * state: () => ({ journeys: {} as Record<string, SerializedJourney<WizardState>> }),\n * });\n *\n * export const wizardPersistence = createPiniaJourneyPersistence<{ frameId: string }, WizardState>({\n * keyFor: ({ journeyId, input }) => `journey:${input.frameId}:${journeyId}`,\n * store: () => useJourneyStore(),\n * });\n * ```\n */\nexport function createPiniaJourneyPersistence<TInput, TState>(\n options: PiniaJourneyPersistenceOptions<TInput>,\n): SyncJourneyPersistence<TState, TInput> {\n const { keyFor } = options;\n const stateKey = options.stateKey ?? \"journeys\";\n const shouldClone = options.clone !== false;\n\n const copy = (blob: SerializedJourney<TState>): SerializedJourney<TState> =>\n shouldClone ? (JSON.parse(JSON.stringify(blob)) as SerializedJourney<TState>) : blob;\n\n const resolve = (): PiniaJourneyPersistenceStore | null => {\n const s = typeof options.store === \"function\" ? options.store() : options.store;\n return s ?? null;\n };\n\n const readRecord = (\n s: PiniaJourneyPersistenceStore,\n ): Record<string, SerializedJourney<TState>> | undefined => {\n const rec = (s.$state as Record<string, unknown>)[stateKey];\n return rec && typeof rec === \"object\"\n ? (rec as Record<string, SerializedJourney<TState>>)\n : undefined;\n };\n\n return {\n keyFor,\n load: (key) => {\n const s = resolve();\n if (!s) return null;\n const blob = readRecord(s)?.[key];\n return blob ? copy(blob) : null;\n },\n save: (key, blob) => {\n const s = resolve();\n if (!s) return;\n const stored = copy(blob);\n s.$patch((state) => {\n const existing = state[stateKey];\n const rec = (\n existing && typeof existing === \"object\" ? existing : (state[stateKey] = {})\n ) as Record<string, SerializedJourney<TState>>;\n rec[key] = stored;\n });\n },\n remove: (key) => {\n const s = resolve();\n if (!s) return;\n s.$patch((state) => {\n const rec = state[stateKey] as Record<string, SerializedJourney<TState>> | undefined;\n if (rec) delete rec[key];\n });\n },\n };\n}\n","import { defineComponent, h } from \"vue\";\nimport {\n provideBinding,\n type ModuleExitHandler,\n type VueAppProvidingPlugin,\n} from \"@modular-vue/vue\";\nimport type {\n JourneyRuntime,\n ModuleTypeMap,\n NavigationItemBase,\n RegistryPlugin,\n UiComponent,\n UiNode,\n} from \"@modular-frontend/core\";\nimport {\n createJourneyRuntime,\n JourneyValidationError,\n validateJourneyContracts,\n validateJourneyDefinition,\n} from \"@modular-frontend/journeys-engine\";\nimport type {\n AnyJourneyDefinition,\n JourneyDefinition,\n JourneyNavContribution,\n JourneyRegisterOptions,\n RegisteredJourney,\n} from \"@modular-frontend/journeys-engine\";\n\nimport { JourneyProvider, journeyKey, type JourneyProviderValue } from \"./provider.js\";\n\n/**\n * Methods the journeys plugin contributes to the registry. Registered plugins\n * type-intersect with the base registry so shells call\n * `registry.registerJourney(...)` with full type support. The neutral analog\n * of the React `JourneysPluginExtension`.\n */\nexport interface JourneysPluginExtension {\n /**\n * Register a journey definition. The structural shape is validated\n * immediately (missing `id` / `version` / `transitions` etc.); module-level\n * contracts are validated at `resolveManifest()` / `resolve()` time.\n *\n * `options.persistence` is typed against the journey's state, and\n * `options.nav.buildInput` is typed against the journey's input — pass a\n * typed definition and both are checked end-to-end.\n */\n registerJourney<TModules extends ModuleTypeMap, TState, TInput, TOutput = unknown>(\n definition: JourneyDefinition<TModules, TState, TInput, TOutput>,\n options?: JourneyRegisterOptions<TState, TInput>,\n ): void;\n}\n\n/**\n * Default shape the journeys plugin emits for each `nav`-carrying journey.\n * When {@link JourneysPluginOptions.buildNavItem} is provided, the plugin\n * hands this default (plus the journey's id and buildInput factory) to the\n * adapter so apps can reshape the item into their narrowed `TNavItem`.\n */\nexport interface JourneyDefaultNavItem extends NavigationItemBase {\n readonly label: string;\n /**\n * Always empty for a journey launcher — the dispatchable action lives in\n * {@link JourneyDefaultNavItem.action}, so there is no URL to follow. An\n * empty string keeps the structural `NavigationItemBase.to` satisfied\n * without suggesting the shell should treat this item as a link.\n */\n readonly to: \"\";\n readonly icon?: string | UiComponent<{ className?: string }>;\n readonly group?: string;\n readonly order?: number;\n readonly hidden?: boolean;\n readonly meta?: unknown;\n readonly action: {\n readonly kind: \"journey-start\";\n readonly journeyId: string;\n readonly buildInput?: (ctx?: unknown) => unknown;\n };\n}\n\n/**\n * Signature for the optional typed adapter that reshapes the plugin's default\n * nav item into the app's narrowed `TNavItem`. The adapter is called once per\n * `nav`-carrying journey at manifest time.\n */\nexport type JourneyNavItemBuilder<TNavItem extends NavigationItemBase> = (\n defaults: JourneyDefaultNavItem,\n raw: JourneyNavContribution<unknown> & { readonly journeyId: string },\n) => TNavItem;\n\nexport interface JourneysPluginOptions<\n TNavItem extends NavigationItemBase = JourneyDefaultNavItem,\n> {\n /**\n * Enable verbose transition / rollback logging in the runtime. Defaults to\n * `false`; plugins propagate the registry-level debug flag when set.\n */\n readonly debug?: boolean;\n /**\n * Forwarded onto `<JourneyProvider>` as the shell-wide `onModuleExit`\n * handler. Use it as a default place to close tabs / forward analytics when\n * a module exit isn't consumed by an explicit prop. Reuses the upstream\n * `ModuleExitHandler` so the full `ModuleExitEvent` (incl. `routeId`) stays\n * in sync with `@modular-vue/vue`.\n */\n readonly onModuleExit?: ModuleExitHandler;\n /**\n * Optional adapter that reshapes the plugin's default nav item into the\n * app's narrowed `TNavItem`. Apps that use a typed `NavigationItem` alias\n * (typed label union, typed action union, typed meta bag) should supply this\n * so contributed items land in `manifest.navigation` with the correct\n * narrowed type. When omitted, the plugin emits items as\n * {@link JourneyDefaultNavItem} and the framework widens them to `TNavItem`\n * at the assembly boundary.\n */\n readonly buildNavItem?: JourneyNavItemBuilder<TNavItem>;\n}\n\n/**\n * Creates the journeys plugin. Pass to `registry.use(journeysPlugin())` (or\n * `createRegistry({ plugins: [...] })`) to enable journey registration and\n * outlet rendering without the runtime packages depending on\n * `@modular-vue/journeys` directly. The Vue analog of the React\n * `journeysPlugin`; the only framework-specific piece is `providers()`, which\n * contributes a Vue `<JourneyProvider>` instead of a React one.\n *\n * The plugin:\n * - contributes `registerJourney(...)` onto the registry (type-safe)\n * - validates contracts against registered modules at resolve time\n * - produces a `JourneyRuntime` on `manifest.extensions.journeys` (also\n * surfaced as the `manifest.journeys` convenience alias)\n * - wraps the provider stack in `<JourneyProvider :runtime=\"…\" />`\n *\n * **Instantiate per registry.** The returned object closes over a\n * journey-registration list; passing the same instance to two\n * `createRegistry()` calls causes them to share that list. Call\n * `journeysPlugin()` once per registry.\n */\nexport function journeysPlugin<TNavItem extends NavigationItemBase = JourneyDefaultNavItem>(\n options: JourneysPluginOptions<TNavItem> = {},\n): RegistryPlugin<\"journeys\", JourneysPluginExtension, JourneyRuntime> &\n VueAppProvidingPlugin<JourneyRuntime> {\n const registered: RegisteredJourney[] = [];\n\n return {\n name: \"journeys\",\n\n extend() {\n return {\n registerJourney<TModules extends ModuleTypeMap, TState, TInput, TOutput = unknown>(\n definition: JourneyDefinition<TModules, TState, TInput, TOutput>,\n regOpts?: JourneyRegisterOptions<TState, TInput>,\n ): void {\n const def = definition as AnyJourneyDefinition;\n const issues = validateJourneyDefinition(def);\n if (issues.length > 0) {\n throw new JourneyValidationError(issues);\n }\n registered.push({\n definition: def,\n options: regOpts as JourneyRegisterOptions | undefined,\n });\n },\n };\n },\n\n validate({ modules }) {\n if (registered.length > 0) {\n validateJourneyContracts(registered, modules);\n }\n },\n\n onResolve({ moduleDescriptors, debug }) {\n return createJourneyRuntime(registered, {\n modules: moduleDescriptors,\n debug: options.debug ?? debug,\n });\n },\n\n contributeNavigation() {\n const items: NavigationItemBase[] = [];\n for (const reg of registered) {\n const nav = reg.options?.nav;\n if (!nav) continue;\n const defaults: JourneyDefaultNavItem = {\n label: nav.label,\n to: \"\",\n ...(nav.icon !== undefined ? { icon: nav.icon } : {}),\n ...(nav.group !== undefined ? { group: nav.group } : {}),\n ...(nav.order !== undefined ? { order: nav.order } : {}),\n ...(nav.hidden !== undefined ? { hidden: nav.hidden } : {}),\n ...(nav.meta !== undefined ? { meta: nav.meta } : {}),\n action: {\n kind: \"journey-start\",\n journeyId: reg.definition.id,\n ...(nav.buildInput ? { buildInput: nav.buildInput } : {}),\n },\n };\n if (options.buildNavItem) {\n items.push(\n options.buildNavItem(defaults, {\n ...(nav as JourneyNavContribution<unknown>),\n journeyId: reg.definition.id,\n }),\n );\n } else {\n items.push(defaults);\n }\n }\n return items;\n },\n\n providers({ runtime }) {\n const onModuleExit = options.onModuleExit;\n const BoundJourneyProvider = defineComponent({\n name: \"JourneysPluginProvider\",\n setup(_props, { slots }) {\n return () => h(JourneyProvider, { runtime, onModuleExit }, () => slots.default?.());\n },\n });\n return [BoundJourneyProvider as unknown as UiComponent<{ children: UiNode }>];\n },\n\n // Install-mode twin of `providers()`. The framework-mode component form\n // (`resolveManifest`) mounts the `<JourneyProvider>` above; the router-owning\n // form (`resolve()` / Nuxt) has no root component to wrap, so it threads the\n // journey runtime app-wide via `app.provide` from this binding instead —\n // exactly how `navigationKey` / `modulesKey` reach the app there. The runtime\n // never imports `journeyKey`; the plugin supplies key + value.\n //\n // Journey context ONLY is bound here (single responsibility). The component\n // form's `<JourneyProvider>` additionally composes `<ModuleExitProvider>`,\n // but module-exit dispatching is the module-hosting layer's concern, not the\n // journeys binding's: in the router-owning path, exits fired outside a step\n // are the shell's to wire (it can read `manifest.onModuleExit`). Binding a\n // module-exit dispatcher off the journeys plugin here would conflate the two\n // and compete with that channel. `onModuleExit` stays on the value only for\n // introspection parity with the component provider.\n appProvides({ runtime }) {\n const value: JourneyProviderValue = { runtime, onModuleExit: options.onModuleExit };\n return [provideBinding(journeyKey, value)];\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modular-vue/journeys",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Vue 3 journeys for @modular-vue: JourneyProvider, instance composables (useJourneyState / useActiveLeafJourneyInstance), and the journeys registry plugin over @modular-frontend/journeys-engine.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@modular-frontend/journeys-engine": "1.
|
|
30
|
+
"@modular-frontend/journeys-engine": "1.8.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@vue/test-utils": "^2.4.6",
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"vite": "^8.1.3",
|
|
41
41
|
"vitest": "^4.1.10",
|
|
42
42
|
"vue": "^3.5.40",
|
|
43
|
-
"@modular-
|
|
44
|
-
"@modular-
|
|
43
|
+
"@modular-vue/vue": "1.3.0",
|
|
44
|
+
"@modular-frontend/core": "0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@modular-frontend/core": "^0.1.0 || ^0.2.0",
|
|
47
|
+
"@modular-frontend/core": "^0.1.0 || ^0.2.0 || ^0.3.0",
|
|
48
48
|
"@modular-vue/vue": "^1.0.0",
|
|
49
49
|
"vue": "^3.5.40"
|
|
50
50
|
},
|