@rxova/journey-core 1.0.0-rc.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +182 -186
  2. package/dist/connectors/immer/immer.cjs +2 -0
  3. package/dist/connectors/immer/immer.cjs.map +7 -0
  4. package/dist/connectors/immer/immer.d.cts +11 -0
  5. package/dist/connectors/immer/immer.d.ts +11 -0
  6. package/dist/connectors/immer/immer.js +2 -0
  7. package/dist/connectors/immer/immer.js.map +7 -0
  8. package/dist/connectors/immer/immer.types.d.cts +9 -0
  9. package/dist/connectors/immer/immer.types.d.ts +9 -0
  10. package/dist/core/errors.d.cts +57 -0
  11. package/dist/core/errors.d.ts +57 -0
  12. package/dist/core/helpers.d.cts +46 -0
  13. package/dist/core/helpers.d.ts +46 -0
  14. package/dist/core/machine.d.cts +9 -0
  15. package/dist/core/machine.d.ts +9 -0
  16. package/dist/core/runtime.d.cts +115 -0
  17. package/dist/core/runtime.d.ts +115 -0
  18. package/dist/core/runtime.types.d.cts +112 -0
  19. package/dist/core/runtime.types.d.ts +112 -0
  20. package/dist/core/store.d.cts +31 -0
  21. package/dist/core/store.d.ts +31 -0
  22. package/dist/core/types.d.cts +481 -0
  23. package/dist/core/types.d.ts +481 -0
  24. package/dist/diagnostics/diagnostics.helpers.d.cts +17 -0
  25. package/dist/diagnostics/diagnostics.helpers.d.ts +17 -0
  26. package/dist/diagnostics/diagnostics.types.d.cts +22 -0
  27. package/dist/diagnostics/diagnostics.types.d.ts +22 -0
  28. package/dist/graph/bag.types.d.cts +91 -0
  29. package/dist/graph/bag.types.d.ts +91 -0
  30. package/dist/graph/graph.d.cts +63 -0
  31. package/dist/graph/graph.d.ts +63 -0
  32. package/dist/graph/graph.types.d.cts +212 -0
  33. package/dist/graph/graph.types.d.ts +212 -0
  34. package/dist/graph/work.d.cts +81 -0
  35. package/dist/graph/work.d.ts +81 -0
  36. package/dist/index.cjs +1 -1
  37. package/dist/index.cjs.map +4 -4
  38. package/dist/index.d.cts +13 -5
  39. package/dist/index.d.ts +13 -5
  40. package/dist/index.js +1 -1
  41. package/dist/index.js.map +4 -4
  42. package/dist/linear/linear.d.cts +33 -0
  43. package/dist/linear/linear.d.ts +33 -0
  44. package/dist/linear/linear.types.d.cts +28 -0
  45. package/dist/linear/linear.types.d.ts +28 -0
  46. package/dist/plugins/analytics/analytics.d.cts +8 -0
  47. package/dist/plugins/analytics/analytics.d.ts +8 -0
  48. package/dist/plugins/analytics/analytics.helpers.d.cts +2 -0
  49. package/dist/plugins/analytics/analytics.helpers.d.ts +2 -0
  50. package/dist/plugins/analytics/analytics.types.d.cts +27 -0
  51. package/dist/plugins/analytics/analytics.types.d.ts +27 -0
  52. package/dist/plugins/execution-paths/execution-paths.d.cts +7 -4
  53. package/dist/plugins/execution-paths/execution-paths.d.ts +7 -4
  54. package/dist/plugins/execution-paths/execution-paths.types.d.cts +21 -0
  55. package/dist/plugins/execution-paths/execution-paths.types.d.ts +21 -0
  56. package/dist/plugins/index.cjs +2 -0
  57. package/dist/plugins/index.cjs.map +7 -0
  58. package/dist/plugins/index.d.cts +19 -0
  59. package/dist/plugins/index.d.ts +19 -0
  60. package/dist/plugins/index.js +2 -0
  61. package/dist/plugins/index.js.map +7 -0
  62. package/dist/plugins/persistence/persistence.d.cts +16 -0
  63. package/dist/plugins/persistence/persistence.d.ts +16 -0
  64. package/dist/plugins/persistence/persistence.helpers.d.cts +34 -0
  65. package/dist/plugins/persistence/persistence.helpers.d.ts +34 -0
  66. package/dist/plugins/persistence/persistence.types.d.cts +63 -0
  67. package/dist/plugins/persistence/persistence.types.d.ts +63 -0
  68. package/dist/plugins/replay/replay.d.cts +7 -0
  69. package/dist/plugins/replay/replay.d.ts +7 -0
  70. package/dist/plugins/replay/replay.helpers.d.cts +22 -0
  71. package/dist/plugins/replay/replay.helpers.d.ts +22 -0
  72. package/dist/plugins/replay/replay.types.d.cts +28 -0
  73. package/dist/plugins/replay/replay.types.d.ts +28 -0
  74. package/package.json +61 -79
  75. package/dist/journey-builder/index.d.cts +0 -5
  76. package/dist/journey-builder/index.d.ts +0 -5
  77. package/dist/journey-builder/types.d.cts +0 -126
  78. package/dist/journey-builder/types.d.ts +0 -126
  79. package/dist/journey-machine/async-state.d.cts +0 -12
  80. package/dist/journey-machine/async-state.d.ts +0 -12
  81. package/dist/journey-machine/computed.d.cts +0 -2
  82. package/dist/journey-machine/computed.d.ts +0 -2
  83. package/dist/journey-machine/controls.d.cts +0 -17
  84. package/dist/journey-machine/controls.d.ts +0 -17
  85. package/dist/journey-machine/helpers.d.cts +0 -52
  86. package/dist/journey-machine/helpers.d.ts +0 -52
  87. package/dist/journey-machine/index.d.cts +0 -3
  88. package/dist/journey-machine/index.d.ts +0 -3
  89. package/dist/journey-machine/navigation.d.cts +0 -32
  90. package/dist/journey-machine/navigation.d.ts +0 -32
  91. package/dist/journey-machine/plugin-controller.d.cts +0 -15
  92. package/dist/journey-machine/plugin-controller.d.ts +0 -15
  93. package/dist/journey-machine/resolve-journey-definition.d.cts +0 -2
  94. package/dist/journey-machine/resolve-journey-definition.d.ts +0 -2
  95. package/dist/journey-machine/runtime.d.cts +0 -33
  96. package/dist/journey-machine/runtime.d.ts +0 -33
  97. package/dist/journey-machine/send.d.cts +0 -9
  98. package/dist/journey-machine/send.d.ts +0 -9
  99. package/dist/plugins/analytics/index.cjs +0 -2
  100. package/dist/plugins/analytics/index.cjs.map +0 -7
  101. package/dist/plugins/analytics/index.d.cts +0 -25
  102. package/dist/plugins/analytics/index.d.ts +0 -25
  103. package/dist/plugins/analytics/index.js +0 -2
  104. package/dist/plugins/analytics/index.js.map +0 -7
  105. package/dist/plugins/autosave/index.cjs +0 -2
  106. package/dist/plugins/autosave/index.cjs.map +0 -7
  107. package/dist/plugins/autosave/index.d.cts +0 -17
  108. package/dist/plugins/autosave/index.d.ts +0 -17
  109. package/dist/plugins/autosave/index.js +0 -2
  110. package/dist/plugins/autosave/index.js.map +0 -7
  111. package/dist/plugins/diagnostics/diagnostics.d.cts +0 -3
  112. package/dist/plugins/diagnostics/diagnostics.d.ts +0 -3
  113. package/dist/plugins/diagnostics/index.cjs +0 -2
  114. package/dist/plugins/diagnostics/index.cjs.map +0 -7
  115. package/dist/plugins/diagnostics/index.d.cts +0 -15
  116. package/dist/plugins/diagnostics/index.d.ts +0 -15
  117. package/dist/plugins/diagnostics/index.js +0 -2
  118. package/dist/plugins/diagnostics/index.js.map +0 -7
  119. package/dist/plugins/execution-paths/index.cjs +0 -2
  120. package/dist/plugins/execution-paths/index.cjs.map +0 -7
  121. package/dist/plugins/execution-paths/index.d.cts +0 -18
  122. package/dist/plugins/execution-paths/index.d.ts +0 -18
  123. package/dist/plugins/execution-paths/index.js +0 -2
  124. package/dist/plugins/execution-paths/index.js.map +0 -7
  125. package/dist/plugins/persistence/controller.d.cts +0 -17
  126. package/dist/plugins/persistence/controller.d.ts +0 -17
  127. package/dist/plugins/persistence/index.cjs +0 -2
  128. package/dist/plugins/persistence/index.cjs.map +0 -7
  129. package/dist/plugins/persistence/index.d.cts +0 -12
  130. package/dist/plugins/persistence/index.d.ts +0 -12
  131. package/dist/plugins/persistence/index.js +0 -2
  132. package/dist/plugins/persistence/index.js.map +0 -7
  133. package/dist/plugins/replay/index.cjs +0 -2
  134. package/dist/plugins/replay/index.cjs.map +0 -7
  135. package/dist/plugins/replay/index.d.cts +0 -27
  136. package/dist/plugins/replay/index.d.ts +0 -27
  137. package/dist/plugins/replay/index.js +0 -2
  138. package/dist/plugins/replay/index.js.map +0 -7
  139. package/dist/types/analytics.types.d.cts +0 -33
  140. package/dist/types/analytics.types.d.ts +0 -33
  141. package/dist/types/autosave.types.d.cts +0 -23
  142. package/dist/types/autosave.types.d.ts +0 -23
  143. package/dist/types/diagnostics.types.d.cts +0 -38
  144. package/dist/types/diagnostics.types.d.ts +0 -38
  145. package/dist/types/index.d.cts +0 -9
  146. package/dist/types/index.d.ts +0 -9
  147. package/dist/types/journey.types.d.cts +0 -191
  148. package/dist/types/journey.types.d.ts +0 -191
  149. package/dist/types/machine.types.d.cts +0 -104
  150. package/dist/types/machine.types.d.ts +0 -104
  151. package/dist/types/observation.types.d.cts +0 -83
  152. package/dist/types/observation.types.d.ts +0 -83
  153. package/dist/types/persistence.types.d.cts +0 -34
  154. package/dist/types/persistence.types.d.ts +0 -34
  155. package/dist/types/replay.types.d.cts +0 -35
  156. package/dist/types/replay.types.d.ts +0 -35
  157. package/dist/types/transitions.types.d.cts +0 -122
  158. package/dist/types/transitions.types.d.ts +0 -122
@@ -0,0 +1,33 @@
1
+ import type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, TerminatePayloadOf } from "./linear.types.js";
2
+ import type { AnyJourneyPlugin, JourneyRuntimeOptions } from "../core/types.js";
3
+ import type { Bag, MetaOf } from "../graph/bag.types.js";
4
+ /**
5
+ * Creates a linear journey runtime from a pure-data definition.
6
+ *
7
+ * Declared order drives `goToNextStep`'s fallback at the timeline tip and the
8
+ * order-based snapshot fields (`index`, `isFirstStep`, `isLastStep`,
9
+ * `stepOrder`). `goToNextStep` on the last step never auto-completes.
10
+ */
11
+ export declare function createLinearJourney<const TStepId extends string, TContext, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads, const TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TMeta = Record<string, unknown>>(definition: LinearJourneyDefinition<TStepId, TContext, TTerminationPayloads, TMeta>, options?: JourneyRuntimeOptions<TPlugins, NoInfer<TStepId>>): LinearJourneyMachine<TContext, TStepId, TMeta, TPlugins, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>;
12
+ /**
13
+ * Returns a `createLinearJourney` with its types pinned up front instead of
14
+ * inferred.
15
+ *
16
+ * The linear tier infers almost everything from the definition already — step
17
+ * ids from the `steps` tuple, context from its value — so reach for this only
18
+ * to pin a `meta` shape, or to share one bag with steps authored in separate
19
+ * files.
20
+ *
21
+ * A standalone export rather than a property on the factory: attaching one
22
+ * would be a module-level side effect, and that defeats tree-shaking badly
23
+ * enough that importing only `createLinearJourney` pulled the whole graph tier
24
+ * into the bundle.
25
+ *
26
+ * ```ts
27
+ * const checkout = withLinearTypes<CheckoutBag>()({
28
+ * steps: ["account", "shipping", "review"],
29
+ * context: initialContext
30
+ * });
31
+ * ```
32
+ */
33
+ export declare const withLinearTypes: <TBag extends Bag>() => <const TPlugins extends readonly AnyJourneyPlugin[] = readonly []>(definition: LinearJourneyDefinition<TBag["stepId"], TBag["context"], JourneyTerminationPayloads, MetaOf<TBag>>, options?: JourneyRuntimeOptions<TPlugins, TBag["stepId"]>) => LinearJourneyMachine<TBag["context"], TBag["stepId"], MetaOf<TBag>, TPlugins>;
@@ -0,0 +1,33 @@
1
+ import type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, TerminatePayloadOf } from "./linear.types.js";
2
+ import type { AnyJourneyPlugin, JourneyRuntimeOptions } from "../core/types.js";
3
+ import type { Bag, MetaOf } from "../graph/bag.types.js";
4
+ /**
5
+ * Creates a linear journey runtime from a pure-data definition.
6
+ *
7
+ * Declared order drives `goToNextStep`'s fallback at the timeline tip and the
8
+ * order-based snapshot fields (`index`, `isFirstStep`, `isLastStep`,
9
+ * `stepOrder`). `goToNextStep` on the last step never auto-completes.
10
+ */
11
+ export declare function createLinearJourney<const TStepId extends string, TContext, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads, const TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TMeta = Record<string, unknown>>(definition: LinearJourneyDefinition<TStepId, TContext, TTerminationPayloads, TMeta>, options?: JourneyRuntimeOptions<TPlugins, NoInfer<TStepId>>): LinearJourneyMachine<TContext, TStepId, TMeta, TPlugins, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>;
12
+ /**
13
+ * Returns a `createLinearJourney` with its types pinned up front instead of
14
+ * inferred.
15
+ *
16
+ * The linear tier infers almost everything from the definition already — step
17
+ * ids from the `steps` tuple, context from its value — so reach for this only
18
+ * to pin a `meta` shape, or to share one bag with steps authored in separate
19
+ * files.
20
+ *
21
+ * A standalone export rather than a property on the factory: attaching one
22
+ * would be a module-level side effect, and that defeats tree-shaking badly
23
+ * enough that importing only `createLinearJourney` pulled the whole graph tier
24
+ * into the bundle.
25
+ *
26
+ * ```ts
27
+ * const checkout = withLinearTypes<CheckoutBag>()({
28
+ * steps: ["account", "shipping", "review"],
29
+ * context: initialContext
30
+ * });
31
+ * ```
32
+ */
33
+ export declare const withLinearTypes: <TBag extends Bag>() => <const TPlugins extends readonly AnyJourneyPlugin[] = readonly []>(definition: LinearJourneyDefinition<TBag["stepId"], TBag["context"], JourneyTerminationPayloads, MetaOf<TBag>>, options?: JourneyRuntimeOptions<TPlugins, TBag["stepId"]>) => LinearJourneyMachine<TBag["context"], TBag["stepId"], MetaOf<TBag>, TPlugins>;
@@ -0,0 +1,28 @@
1
+ import type { AnyJourneyPlugin, CompletePayloadOf, JourneyMachineBase, JourneyTerminationPayloads, LinearSnapshot, NavigationResult, OnEnterHook, OnLeaveHook, PluginApis, TerminatePayloadOf } from "../core/types.js";
2
+ export type { CompletePayloadOf, JourneyTerminationPayloads, TerminatePayloadOf } from "../core/types.js";
3
+ /** Full linear step config; a bare string is shorthand for `{ id, metadata: {} }`. */
4
+ export type LinearStepConfig<TContext = unknown, TStepId extends string = string, TMeta = Record<string, unknown>, TCompletePayload = unknown, TTerminatePayload = unknown> = {
5
+ readonly id: TStepId;
6
+ readonly metadata?: TMeta;
7
+ readonly onEnter?: OnEnterHook<TContext, TStepId, never, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>>;
8
+ readonly onLeave?: OnLeaveHook<TContext, TStepId, never, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>>;
9
+ };
10
+ export type LinearStepInput<TContext, TMeta, TStepId extends string = string, TCompletePayload = unknown, TTerminatePayload = unknown> = TStepId | LinearStepConfig<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>;
11
+ /** Pure-data linear definition: testable and reusable. */
12
+ export type LinearJourneyDefinition<TStepId extends string = string, TContext = unknown, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads, TMeta = Record<string, unknown>> = {
13
+ readonly steps: readonly LinearStepInput<TContext, TMeta, TStepId, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>[];
14
+ readonly context: TContext;
15
+ };
16
+ /** Step ids inferred as a literal union from the steps tuple. */
17
+ export type LinearStepIdOf<TSteps extends readonly (string | {
18
+ readonly id: string;
19
+ })[]> = TSteps[number] extends infer TStep ? TStep extends string ? TStep : TStep extends {
20
+ readonly id: infer TId extends string;
21
+ } ? TId : never : never;
22
+ export type LinearJourneyMachine<TContext, TStepId extends string, TMeta = Record<string, unknown>, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TCompletePayload = unknown, TTerminatePayload = unknown> = Omit<JourneyMachineBase<TContext, TStepId, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload>, "navigate"> & {
23
+ readonly navigate: JourneyMachineBase<TContext, TStepId, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload>["navigate"] & {
24
+ /** Declared-order index navigation; out-of-range indexes reject with "invalid-target". */
25
+ goToStepByIndex(index: number): Promise<NavigationResult<TStepId>>;
26
+ };
27
+ readonly plugins: PluginApis<TPlugins>;
28
+ };
@@ -0,0 +1,28 @@
1
+ import type { AnyJourneyPlugin, CompletePayloadOf, JourneyMachineBase, JourneyTerminationPayloads, LinearSnapshot, NavigationResult, OnEnterHook, OnLeaveHook, PluginApis, TerminatePayloadOf } from "../core/types.js";
2
+ export type { CompletePayloadOf, JourneyTerminationPayloads, TerminatePayloadOf } from "../core/types.js";
3
+ /** Full linear step config; a bare string is shorthand for `{ id, metadata: {} }`. */
4
+ export type LinearStepConfig<TContext = unknown, TStepId extends string = string, TMeta = Record<string, unknown>, TCompletePayload = unknown, TTerminatePayload = unknown> = {
5
+ readonly id: TStepId;
6
+ readonly metadata?: TMeta;
7
+ readonly onEnter?: OnEnterHook<TContext, TStepId, never, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>>;
8
+ readonly onLeave?: OnLeaveHook<TContext, TStepId, never, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>>;
9
+ };
10
+ export type LinearStepInput<TContext, TMeta, TStepId extends string = string, TCompletePayload = unknown, TTerminatePayload = unknown> = TStepId | LinearStepConfig<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>;
11
+ /** Pure-data linear definition: testable and reusable. */
12
+ export type LinearJourneyDefinition<TStepId extends string = string, TContext = unknown, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads, TMeta = Record<string, unknown>> = {
13
+ readonly steps: readonly LinearStepInput<TContext, TMeta, TStepId, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>[];
14
+ readonly context: TContext;
15
+ };
16
+ /** Step ids inferred as a literal union from the steps tuple. */
17
+ export type LinearStepIdOf<TSteps extends readonly (string | {
18
+ readonly id: string;
19
+ })[]> = TSteps[number] extends infer TStep ? TStep extends string ? TStep : TStep extends {
20
+ readonly id: infer TId extends string;
21
+ } ? TId : never : never;
22
+ export type LinearJourneyMachine<TContext, TStepId extends string, TMeta = Record<string, unknown>, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TCompletePayload = unknown, TTerminatePayload = unknown> = Omit<JourneyMachineBase<TContext, TStepId, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload>, "navigate"> & {
23
+ readonly navigate: JourneyMachineBase<TContext, TStepId, LinearSnapshot<TContext, TStepId, TMeta, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload>["navigate"] & {
24
+ /** Declared-order index navigation; out-of-range indexes reject with "invalid-target". */
25
+ goToStepByIndex(index: number): Promise<NavigationResult<TStepId>>;
26
+ };
27
+ readonly plugins: PluginApis<TPlugins>;
28
+ };
@@ -0,0 +1,8 @@
1
+ export { RECENT_EVENT_CAPACITY } from "./analytics.helpers.js";
2
+ import type { AnalyticsApi, AnalyticsPluginOptions } from "./analytics.types.js";
3
+ import type { JourneyPlugin } from "../../core/types.js";
4
+ /**
5
+ * Converts journey observations (transitions, lifecycle, blocked navigations,
6
+ * errors) into analytics envelopes delivered to the configured sink.
7
+ */
8
+ export declare function createAnalyticsPlugin(options: AnalyticsPluginOptions): JourneyPlugin<"analytics", AnalyticsApi, never>;
@@ -0,0 +1,8 @@
1
+ export { RECENT_EVENT_CAPACITY } from "./analytics.helpers.js";
2
+ import type { AnalyticsApi, AnalyticsPluginOptions } from "./analytics.types.js";
3
+ import type { JourneyPlugin } from "../../core/types.js";
4
+ /**
5
+ * Converts journey observations (transitions, lifecycle, blocked navigations,
6
+ * errors) into analytics envelopes delivered to the configured sink.
7
+ */
8
+ export declare function createAnalyticsPlugin(options: AnalyticsPluginOptions): JourneyPlugin<"analytics", AnalyticsApi, never>;
@@ -0,0 +1,2 @@
1
+ /** Ring-buffer capacity for the recent-events buffer. */
2
+ export declare const RECENT_EVENT_CAPACITY = 100;
@@ -0,0 +1,2 @@
1
+ /** Ring-buffer capacity for the recent-events buffer. */
2
+ export declare const RECENT_EVENT_CAPACITY = 100;
@@ -0,0 +1,27 @@
1
+ export type AnalyticsTrackedEvent = {
2
+ readonly name: string;
3
+ readonly timestamp: number;
4
+ readonly stepId: string | null;
5
+ readonly payload: Readonly<Record<string, unknown>>;
6
+ };
7
+ export type AnalyticsRecentEvent = {
8
+ readonly source: "lifecycle" | "custom";
9
+ readonly tracked: AnalyticsTrackedEvent;
10
+ readonly success: boolean;
11
+ readonly error?: unknown;
12
+ };
13
+ export type AnalyticsPluginOptions = {
14
+ /** The analytics sink. Exceptions are captured, never rethrown. */
15
+ track(event: AnalyticsTrackedEvent): void;
16
+ /** Called when `track` throws; without it failures only land in the buffer. */
17
+ onError?(error: unknown, event: AnalyticsTrackedEvent): void;
18
+ /** Injectable clock, mainly for tests. */
19
+ now?: () => number;
20
+ };
21
+ export type AnalyticsApi = {
22
+ /** Tracks a custom event through the same safe pipeline. */
23
+ trackAnalyticsEvent(name: string, payload?: Record<string, unknown>): AnalyticsTrackedEvent;
24
+ /** The last 100 tracked events (successes and failures). */
25
+ getRecentEvents(): readonly AnalyticsRecentEvent[];
26
+ clearRecentEvents(): void;
27
+ };
@@ -0,0 +1,27 @@
1
+ export type AnalyticsTrackedEvent = {
2
+ readonly name: string;
3
+ readonly timestamp: number;
4
+ readonly stepId: string | null;
5
+ readonly payload: Readonly<Record<string, unknown>>;
6
+ };
7
+ export type AnalyticsRecentEvent = {
8
+ readonly source: "lifecycle" | "custom";
9
+ readonly tracked: AnalyticsTrackedEvent;
10
+ readonly success: boolean;
11
+ readonly error?: unknown;
12
+ };
13
+ export type AnalyticsPluginOptions = {
14
+ /** The analytics sink. Exceptions are captured, never rethrown. */
15
+ track(event: AnalyticsTrackedEvent): void;
16
+ /** Called when `track` throws; without it failures only land in the buffer. */
17
+ onError?(error: unknown, event: AnalyticsTrackedEvent): void;
18
+ /** Injectable clock, mainly for tests. */
19
+ now?: () => number;
20
+ };
21
+ export type AnalyticsApi = {
22
+ /** Tracks a custom event through the same safe pipeline. */
23
+ trackAnalyticsEvent(name: string, payload?: Record<string, unknown>): AnalyticsTrackedEvent;
24
+ /** The last 100 tracked events (successes and failures). */
25
+ getRecentEvents(): readonly AnalyticsRecentEvent[];
26
+ clearRecentEvents(): void;
27
+ };
@@ -1,6 +1,9 @@
1
- import type { JourneyDefinition, JourneyExecutionPathOptions, JourneyExecutionPathsResult, JourneyFullEventType, JourneyJsonObject, JourneyResolvedDefinition } from "../../types";
1
+ import type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths.types.js";
2
+ import type { JourneyPlugin } from "../../core/types.js";
3
+ export type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths.types.js";
2
4
  /**
3
- * Enumerates structural execution paths from a resolved journey definition.
5
+ * First-party plugin: tracks execution paths through the journey. Observes
6
+ * transitions via the plugin host and contributes
7
+ * `machine.plugins["execution-paths"]` and `snapshot.plugins["execution-paths"]`.
4
8
  */
5
- export declare function getExecutionPaths<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>(journey: JourneyDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>, options?: JourneyExecutionPathOptions): JourneyExecutionPathsResult<TStepId, JourneyFullEventType<TEventMap>>;
6
- export declare function getExecutionPaths<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>(journey: JourneyResolvedDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>, options?: JourneyExecutionPathOptions): JourneyExecutionPathsResult<TStepId, JourneyFullEventType<TEventMap>>;
9
+ export declare function createExecutionPathsPlugin(options?: ExecutionPathsPluginOptions): JourneyPlugin<"execution-paths", ExecutionPathsApi, ExecutionPathsSnapshot>;
@@ -1,6 +1,9 @@
1
- import type { JourneyDefinition, JourneyExecutionPathOptions, JourneyExecutionPathsResult, JourneyFullEventType, JourneyJsonObject, JourneyResolvedDefinition } from "../../types";
1
+ import type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths.types.js";
2
+ import type { JourneyPlugin } from "../../core/types.js";
3
+ export type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths.types.js";
2
4
  /**
3
- * Enumerates structural execution paths from a resolved journey definition.
5
+ * First-party plugin: tracks execution paths through the journey. Observes
6
+ * transitions via the plugin host and contributes
7
+ * `machine.plugins["execution-paths"]` and `snapshot.plugins["execution-paths"]`.
4
8
  */
5
- export declare function getExecutionPaths<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>(journey: JourneyDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>, options?: JourneyExecutionPathOptions): JourneyExecutionPathsResult<TStepId, JourneyFullEventType<TEventMap>>;
6
- export declare function getExecutionPaths<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>(journey: JourneyResolvedDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>, options?: JourneyExecutionPathOptions): JourneyExecutionPathsResult<TStepId, JourneyFullEventType<TEventMap>>;
9
+ export declare function createExecutionPathsPlugin(options?: ExecutionPathsPluginOptions): JourneyPlugin<"execution-paths", ExecutionPathsApi, ExecutionPathsSnapshot>;
@@ -0,0 +1,21 @@
1
+ export type ExecutionPathsPluginOptions = {
2
+ /**
3
+ * How many finished runs to retain, newest kept. Defaults to 50. Older paths
4
+ * are dropped once the bound is reached — this history is diagnostic, and a
5
+ * long-lived machine that completes and restarts repeatedly would otherwise
6
+ * grow it for the lifetime of the process.
7
+ */
8
+ maxPaths?: number;
9
+ };
10
+ export type ExecutionPathsApi = {
11
+ /** The committed step sequence of the current run. */
12
+ getCurrentPath(): readonly string[];
13
+ /** Paths of finished runs (a run ends on complete/terminate/restart). */
14
+ getCompletedPaths(): readonly (readonly string[])[];
15
+ /** Drops every retained completed path. The current run is untouched. */
16
+ clearCompletedPaths(): void;
17
+ };
18
+ export type ExecutionPathsSnapshot = {
19
+ readonly currentPath: readonly string[];
20
+ readonly completedPaths: readonly (readonly string[])[];
21
+ };
@@ -0,0 +1,21 @@
1
+ export type ExecutionPathsPluginOptions = {
2
+ /**
3
+ * How many finished runs to retain, newest kept. Defaults to 50. Older paths
4
+ * are dropped once the bound is reached — this history is diagnostic, and a
5
+ * long-lived machine that completes and restarts repeatedly would otherwise
6
+ * grow it for the lifetime of the process.
7
+ */
8
+ maxPaths?: number;
9
+ };
10
+ export type ExecutionPathsApi = {
11
+ /** The committed step sequence of the current run. */
12
+ getCurrentPath(): readonly string[];
13
+ /** Paths of finished runs (a run ends on complete/terminate/restart). */
14
+ getCompletedPaths(): readonly (readonly string[])[];
15
+ /** Drops every retained completed path. The current run is untouched. */
16
+ clearCompletedPaths(): void;
17
+ };
18
+ export type ExecutionPathsSnapshot = {
19
+ readonly currentPath: readonly string[];
20
+ readonly completedPaths: readonly (readonly string[])[];
21
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";var w=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var F=(e,t)=>{for(var n in t)w(e,n,{get:t[n],enumerable:!0})},B=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!U.call(e,o)&&o!==n&&w(e,o,{get:()=>t[o],enumerable:!(r=V(t,o))||r.enumerable});return e};var Y=e=>B(w({},"__esModule",{value:!0}),e);var q={};F(q,{DEFAULT_SAVE_REASONS:()=>E,RECENT_EVENT_CAPACITY:()=>g,buildPersistedState:()=>h,createAnalyticsPlugin:()=>N,createExecutionPathsPlugin:()=>D,createPersistencePlugin:()=>_,createReplayPlugin:()=>z,normalizeDebounceMs:()=>S,normalizeMaxEntries:()=>A,parsePersistedState:()=>x,persistOptionToPlugin:()=>j,serializeReplaySession:()=>b,toSerializable:()=>y});module.exports=Y(q);var g=100;function N(e){let t=e.now??Date.now;return{name:"analytics",setup(n){let r=[],o=s=>{r.push(s),r.length>100&&(r=r.slice(r.length-100))},a=(s,i,u)=>{let l={name:i,timestamp:t(),stepId:n.getSnapshot().currentStep?.id??null,payload:u};try{e.track(l),o({source:s,tracked:l,success:!0})}catch(p){o({source:s,tracked:l,success:!1,error:p});try{e.onError?.(p,l)}catch(d){n.reportError(d)}}return l};return n.onTransition(({from:s,to:i})=>{a("lifecycle","journey.transition",{from:s,to:i})}),n.onStatusChange(({previous:s,current:i})=>{a("lifecycle",`journey.${i}`,{previous:s})}),n.onNavigationBlocked(({reason:s,from:i,to:u})=>{a("lifecycle","journey.navigationBlocked",{reason:s,from:i,to:u})}),n.onError(({phase:s,stepId:i,error:u})=>{a("lifecycle","journey.error",{phase:s,stepId:i,error:u})}),{api:{trackAnalyticsEvent:(s,i={})=>a("custom",s,i),getRecentEvents:()=>[...r],clearRecentEvents:()=>{r=[]}}}}}}function D(e={}){let t=e.maxPaths??50;return{name:"execution-paths",setup(n){let r=[],o=[];return n.onTransition(({to:a})=>{r.push(a)}),n.onStatusChange(({current:a})=>{(a==="completed"||a==="terminated")&&r.length>0&&(o.push(Object.freeze([...r])),o.length>t&&(o=o.slice(o.length-t)),r=[])}),{api:{getCurrentPath:()=>Object.freeze([...r]),getCompletedPaths:()=>Object.freeze([...o]),clearCompletedPaths:()=>{o=[]}},deriveSnapshot:(a,s)=>s&&s.currentPath.length===r.length&&s.completedPaths.length===o.length?s:Object.freeze({currentPath:Object.freeze([...r]),completedPaths:Object.freeze([...o])})}}}}var $=()=>{let e=globalThis;if(typeof e.__DEV__=="boolean")return e.__DEV__;let t=e.process?.env?.NODE_ENV;return t===void 0||t==="development"},C=(e,t)=>{if(!(!$()||typeof console>"u")){if(t===void 0){console.warn(e);return}console.warn("%s",e,t)}};var P=class extends Error{constructor(t,n,r={},o){super(`journey: ${n}`,o===void 0?void 0:{cause:o}),this.name="JourneyError",this.code=t,this.stepId=r.stepId,this.event=r.event,this.pluginName=r.pluginName}};var E=["context","transition","status"];function S(e){return typeof e!="number"||!Number.isFinite(e)?0:Math.max(0,Math.trunc(e))}function h(e,t){return{status:e.status,context:e.context,timeline:e.history.timeline,currentIndex:e.history.currentIndex,savedAt:t}}var H=new Set(["idle","running","paused","completed","terminated"]),K=["__proto__","constructor","prototype"];function O(e,t=0){if(t>50||e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(r=>O(r,t+1));let n={};for(let[r,o]of Object.entries(e))K.includes(r)||(n[r]=O(o,t+1));return n}function x(e){if(e===null)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!="object"||t===null)return null;let n=t;return typeof n.status!="string"||!H.has(n.status)||!Array.isArray(n.timeline)||!n.timeline.every(r=>typeof r=="string")||typeof n.currentIndex!="number"||!Number.isInteger(n.currentIndex)||typeof n.savedAt!="number"||!Number.isFinite(n.savedAt)?null:{status:n.status,context:O(n.context),timeline:n.timeline,currentIndex:n.currentIndex,savedAt:n.savedAt}}function I(e){if(e.storage)return e.storage;let t;try{t=globalThis.localStorage}catch(n){throw new P("storage-unavailable","localStorage access was blocked by the environment; pass persist.storage explicitly",{},n)}if(!t)throw new P("storage-unavailable","persist.storage is required when localStorage is unavailable");return t}var T={status:"idle",lastSavedAt:null,error:null};function _(e){let t=e.now??Date.now,n=S(e.debounceMs),r=new Set(e.saveOn??E),o=0;return{name:"persistence",setup(a){o+=1,o>1&&C(`journey: persistence plugin instance shared by ${o} machines; they overwrite key "${e.key}". Create one per machine.`);let s=null,i=T,u=null,l=!1,p=c=>{s=c,i={status:"saved",lastSavedAt:c.savedAt,error:null}},d=c=>{i={...i,status:"error",error:c},a.reportError(c)},R=()=>{let c=h(a.getSnapshot(),t());i={...i,status:"saving"};let m;try{m=e.storage.setItem(e.key,JSON.stringify(c))}catch(J){throw i={...i,status:"error",error:J},J}if(m!==void 0)return m.then(()=>p(c),d);p(c)},f=()=>{u!==null&&(clearTimeout(u),u=null)},M=()=>{u=null;try{let c=R()}catch(c){d(c)}},k=c=>{if(!(l||!r.has(c))){if(n===0){R();return}f(),i={...i,status:"pending"},u=setTimeout(M,n)}};return a.onContextChange(()=>k("context")),a.onTransition(()=>k("transition")),a.onStatusChange(({current:c})=>{if(c==="terminated"&&e.clearOnTerminate){f(),s=null,i=T,e.storage.removeItem(e.key);return}k("status")}),a.onDispose(()=>{l=!0,f()}),{api:{inspectPersistedState:()=>s,getPersistenceState:()=>i,readPersisted:()=>x(e.storage.getItem(e.key)),clearPersisted:()=>{f(),s=null,i=T;try{e.storage.removeItem(e.key)}catch(c){d(c)}},flushPersisted:async()=>{f();try{await R()}catch(c){d(c)}}},deriveSnapshot:(c,m)=>m===i?m:i}}}}function j(e){return _({key:e.key,storage:I(e)})}function A(e){return typeof e!="number"||!Number.isFinite(e)?500:Math.max(1,Math.trunc(e))}var G=100,W="[circular]",X="[max-depth]";function v(e,t,n){if(e===null||typeof e=="string"||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="bigint")return e.toString();if(typeof e>"u")return null;if(typeof e=="function"||typeof e=="symbol")return`[unsupported:${typeof e}]`;if(e instanceof Error)return{name:e.name,message:e.message,...e.stack?{stack:e.stack}:{}};if(e instanceof Date)return e.toISOString();if(typeof e!="object")return String(e);let r=e;if(t.memo.has(r))return t.memo.get(r);if(t.inProgress.has(r))return W;if(n>=t.maxDepth)return X;t.inProgress.add(r);let o;if(Array.isArray(e))o=e.map(a=>v(a,t,n+1));else{let a={};for(let[s,i]of Object.entries(e))a[s]=v(i,t,n+1);o=a}return t.inProgress.delete(r),t.memo.set(r,o),o}function y(e,t={}){return v(e,{memo:new Map,inProgress:new Set,maxDepth:t.maxDepth??G},0)}function b(e,t){return JSON.stringify(y(e),null,t?.pretty?2:void 0)}function z(e={}){let t=A(e.maxEntries),n=e.captureSnapshots??!0,r=e.now??Date.now;return{name:"replay",setup(o){let a=r(),s=[],i=(u,l)=>{let p={at:r(),kind:u,data:y(l),...n?{snapshot:y(o.getSnapshot())}:{}};s.push(p),s.length>t&&(s=s.slice(s.length-t))};return o.onTransition(({from:u,to:l})=>i("transition",{from:u,to:l})),o.onStatusChange(({previous:u,current:l})=>i("status",{previous:u,current:l})),o.onContextChange(({previous:u,current:l})=>i("context",{previous:u,current:l})),o.onNavigationBlocked(({reason:u,from:l,to:p})=>i("navigationBlocked",{reason:u,from:l,to:p})),o.onError(({phase:u,stepId:l,error:p})=>i("error",{phase:u,stepId:l,error:p})),{api:{getReplaySession:()=>({startedAt:a,entries:[...s]}),clearReplaySession:()=>{a=r(),s=[]},exportReplaySession:u=>b({startedAt:a,entries:[...s]},u)},deriveSnapshot:(u,l)=>l?.entryCount===s.length?l:{entryCount:s.length}}}}}0&&(module.exports={DEFAULT_SAVE_REASONS,RECENT_EVENT_CAPACITY,buildPersistedState,createAnalyticsPlugin,createExecutionPathsPlugin,createPersistencePlugin,createReplayPlugin,normalizeDebounceMs,normalizeMaxEntries,parsePersistedState,persistOptionToPlugin,serializeReplaySession,toSerializable});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/plugins/index.ts", "../../src/plugins/analytics/analytics.helpers.ts", "../../src/plugins/analytics/analytics.ts", "../../src/plugins/execution-paths/execution-paths.ts", "../../../common/src/dev.ts", "../../src/core/errors.ts", "../../src/plugins/persistence/persistence.helpers.ts", "../../src/plugins/persistence/persistence.ts", "../../src/plugins/replay/replay.helpers.ts", "../../src/plugins/replay/replay.ts"],
4
+ "sourcesContent": ["/**\n * Every bundled plugin, behind one subpath.\n *\n * They used to have a subpath each, which made the import path a decision:\n * one path for analytics, another for replay, another for persistence, another\n * for execution paths \u2014 four ways to spell adding a plugin. Each\n * is still its own module internally and every factory is a named export, so\n * tree-shaking is unchanged: importing `createReplayPlugin` from here pulls in\n * replay and nothing else.\n */\n// Re-exported here, not only from the root entry: every factory below returns a\n// `JourneyPlugin`, and a consumer whose own inferred type mentions one needs to\n// be able to name it from the same module it imported the factory from.\nexport type { AnyJourneyPlugin, JourneyPlugin, PluginApis, PluginHost } from \"../core/types\";\n\nexport { createAnalyticsPlugin, RECENT_EVENT_CAPACITY } from \"./analytics/analytics\";\nexport type {\n AnalyticsApi,\n AnalyticsPluginOptions,\n AnalyticsRecentEvent,\n AnalyticsTrackedEvent\n} from \"./analytics/analytics.types\";\n\nexport { createExecutionPathsPlugin } from \"./execution-paths/execution-paths\";\nexport type {\n ExecutionPathsApi,\n ExecutionPathsPluginOptions,\n ExecutionPathsSnapshot\n} from \"./execution-paths/execution-paths.types\";\n\nexport {\n buildPersistedState,\n createPersistencePlugin,\n DEFAULT_SAVE_REASONS,\n normalizeDebounceMs,\n parsePersistedState,\n persistOptionToPlugin\n} from \"./persistence/persistence\";\nexport type {\n JourneyPersistedState,\n JourneyStorage,\n PersistenceApi,\n PersistencePluginOptions,\n PersistenceReason,\n PersistenceState\n} from \"./persistence/persistence.types\";\n\nexport {\n createReplayPlugin,\n normalizeMaxEntries,\n serializeReplaySession,\n toSerializable\n} from \"./replay/replay\";\nexport type {\n ReplayApi,\n ReplayEntry,\n ReplayEntryKind,\n ReplayExportOptions,\n ReplayPluginOptions,\n ReplaySession\n} from \"./replay/replay.types\";\n", "/** Ring-buffer capacity for the recent-events buffer. */\nexport const RECENT_EVENT_CAPACITY = 100;\n", "import { RECENT_EVENT_CAPACITY } from \"./analytics.helpers\";\n\nexport { RECENT_EVENT_CAPACITY } from \"./analytics.helpers\";\nimport type {\n AnalyticsApi,\n AnalyticsPluginOptions,\n AnalyticsRecentEvent,\n AnalyticsTrackedEvent\n} from \"./analytics.types\";\nimport type { JourneyPlugin } from \"../../core/types\";\n\n/**\n * Converts journey observations (transitions, lifecycle, blocked navigations,\n * errors) into analytics envelopes delivered to the configured sink.\n */\nexport function createAnalyticsPlugin(\n options: AnalyticsPluginOptions\n): JourneyPlugin<\"analytics\", AnalyticsApi, never> {\n const now = options.now ?? Date.now;\n\n return {\n name: \"analytics\",\n // Per-instance state lives inside `setup()` (called once per machine) so a\n // single plugin instance reused across machines never shares its buffer.\n setup(host) {\n let recent: AnalyticsRecentEvent[] = [];\n\n const record = (entry: AnalyticsRecentEvent) => {\n recent.push(entry);\n if (recent.length > RECENT_EVENT_CAPACITY) {\n recent = recent.slice(recent.length - RECENT_EVENT_CAPACITY);\n }\n };\n\n const trackSafely = (\n source: AnalyticsRecentEvent[\"source\"],\n name: string,\n payload: Record<string, unknown>\n ): AnalyticsTrackedEvent => {\n const tracked: AnalyticsTrackedEvent = {\n name,\n timestamp: now(),\n stepId: host.getSnapshot().currentStep?.id ?? null,\n payload\n };\n try {\n options.track(tracked);\n record({ source, tracked, success: true });\n } catch (error) {\n record({ source, tracked, success: false, error });\n // The error handler sits inside the guard: a throwing onError used to\n // escape trackSafely, which made \"the sink failed\" indistinguishable\n // from \"your error handler failed\" at the isolation boundary.\n try {\n options.onError?.(error, tracked);\n } catch (handlerError) {\n host.reportError(handlerError);\n }\n }\n return tracked;\n };\n\n host.onTransition(({ from, to }) => {\n trackSafely(\"lifecycle\", \"journey.transition\", { from, to });\n });\n host.onStatusChange(({ previous, current }) => {\n trackSafely(\"lifecycle\", `journey.${current}`, { previous });\n });\n host.onNavigationBlocked(({ reason, from, to }) => {\n trackSafely(\"lifecycle\", \"journey.navigationBlocked\", { reason, from, to });\n });\n host.onError(({ phase, stepId, error }) => {\n trackSafely(\"lifecycle\", \"journey.error\", { phase, stepId, error });\n });\n\n return {\n api: {\n trackAnalyticsEvent: (name, payload = {}) => trackSafely(\"custom\", name, payload),\n getRecentEvents: () => [...recent],\n clearRecentEvents: () => {\n recent = [];\n }\n }\n };\n }\n };\n}\n", "import type {\n ExecutionPathsApi,\n ExecutionPathsPluginOptions,\n ExecutionPathsSnapshot\n} from \"./execution-paths.types\";\nimport type { JourneyPlugin } from \"../../core/types\";\n\nexport type {\n ExecutionPathsApi,\n ExecutionPathsPluginOptions,\n ExecutionPathsSnapshot\n} from \"./execution-paths.types\";\n\n/** Retained finished runs. Diagnostic history, so a modest bound is enough. */\nconst DEFAULT_MAX_PATHS = 50;\n\n/**\n * First-party plugin: tracks execution paths through the journey. Observes\n * transitions via the plugin host and contributes\n * `machine.plugins[\"execution-paths\"]` and `snapshot.plugins[\"execution-paths\"]`.\n */\nexport function createExecutionPathsPlugin(\n options: ExecutionPathsPluginOptions = {}\n): JourneyPlugin<\"execution-paths\", ExecutionPathsApi, ExecutionPathsSnapshot> {\n const maxPaths = options.maxPaths ?? DEFAULT_MAX_PATHS;\n return {\n name: \"execution-paths\",\n setup(host) {\n let currentPath: string[] = [];\n let completedPaths: (readonly string[])[] = [];\n\n host.onTransition(({ to }) => {\n currentPath.push(to);\n });\n host.onStatusChange(({ current }) => {\n if ((current === \"completed\" || current === \"terminated\") && currentPath.length > 0) {\n completedPaths.push(Object.freeze([...currentPath]));\n // Newest kept: a machine that completes and restarts on a loop would\n // otherwise retain one frozen array per run, forever.\n if (completedPaths.length > maxPaths) {\n completedPaths = completedPaths.slice(completedPaths.length - maxPaths);\n }\n currentPath = [];\n }\n });\n\n return {\n api: {\n getCurrentPath: () => Object.freeze([...currentPath]),\n getCompletedPaths: () => Object.freeze([...completedPaths]),\n clearCompletedPaths: () => {\n completedPaths = [];\n }\n },\n deriveSnapshot: (_snapshot, previous) => {\n if (\n previous &&\n previous.currentPath.length === currentPath.length &&\n previous.completedPaths.length === completedPaths.length\n ) {\n return previous;\n }\n return Object.freeze({\n currentPath: Object.freeze([...currentPath]),\n completedPaths: Object.freeze([...completedPaths])\n });\n }\n };\n }\n };\n}\n", "/**\n * Environment detection for diagnostics that must not survive into production.\n *\n * Two resolvers live here and they answer deliberately different questions.\n * They are not interchangeable, and the difference is the point:\n *\n * - {@link isDevelopmentEnvironment} asks *\"should I print a developer\n * warning?\"* and is **permissive** \u2014 an unset `NODE_ENV` counts as\n * development, because an unconfigured environment is someone's machine and\n * the cost of a stray warning there is far lower than the cost of a silently\n * swallowed one.\n * - {@link resolveNonProductionEnvironment} asks *\"should I switch on a feature\n * that has a runtime cost?\"* and is **conservative** \u2014 an unset environment\n * counts as production, because auto-enabling a devtools bridge in an\n * unconfigured deployment leaks internal state to anything listening.\n *\n * Permissive for output, conservative for behaviour. Changing either default to\n * match the other would break one of those two properties.\n */\n\nimport { isRecord } from \"./predicates\";\n\ntype DiagnosticGlobal = typeof globalThis & {\n __DEV__?: unknown;\n process?: {\n env?: {\n NODE_ENV?: string;\n };\n };\n};\n\n/**\n * Reports whether developer-facing diagnostics should be emitted.\n *\n * A boolean `__DEV__` global wins outright \u2014 bundlers inline it, and test\n * suites set it to exercise warning paths that `NODE_ENV=test` would otherwise\n * suppress. Otherwise an unset or `\"development\"` `NODE_ENV` is development,\n * and anything else (including `\"test\"` and `\"production\"`) is not.\n *\n * @returns `true` when warnings and errors should reach the console.\n */\nexport const isDevelopmentEnvironment = (): boolean => {\n const diagnosticGlobal = globalThis as DiagnosticGlobal;\n if (typeof diagnosticGlobal.__DEV__ === \"boolean\") {\n return diagnosticGlobal.__DEV__;\n }\n\n const nodeEnv = diagnosticGlobal.process?.env?.NODE_ENV;\n return nodeEnv === undefined || nodeEnv === \"development\";\n};\n\n/**\n * Logs a warning, but only where {@link isDevelopmentEnvironment} holds.\n *\n * The `detail` argument is forwarded as a second console argument rather than\n * interpolated, so objects stay inspectable in devtools instead of collapsing\n * to `[object Object]`. Omitting it logs the message alone, avoiding a trailing\n * `undefined` in the output.\n *\n * @param message - The warning text.\n * @param detail - Optional structured context to log alongside the message.\n */\nexport const warnInDevelopment = (message: string, detail?: unknown): void => {\n if (!isDevelopmentEnvironment() || typeof console === \"undefined\") {\n return;\n }\n\n if (detail === undefined) {\n console.warn(message);\n return;\n }\n\n // `message` goes through a constant `%s` rather than standing as the format\n // string itself: callers build it by interpolation, so a `%` in an\n // interpolated value would otherwise be read as a directive and consume\n // `detail`.\n console.warn(\"%s\", message, detail);\n};\n\n/**\n * The subset of a bundler's `import.meta.env` this module reads.\n *\n * Callers pass `import.meta.env` in rather than this module reading it,\n * because `import.meta` is a syntax error in a CommonJS build \u2014 keeping it at\n * the call site lets the bundler that understands it do the substitution.\n */\nexport type NonProductionBundlerEnv = {\n /** Vite-style development flag. */\n DEV?: unknown;\n /** Vite-style production flag. Takes precedence over {@link NonProductionBundlerEnv.DEV}. */\n PROD?: unknown;\n};\n\ntype ProcessLike = { env?: { NODE_ENV?: string } };\n\nconst resolveImportMetaEnvironment = (\n bundlerEnv: NonProductionBundlerEnv | null | undefined\n): boolean | null => {\n if (!isRecord(bundlerEnv)) {\n return null;\n }\n if (bundlerEnv.PROD === true) {\n return false;\n }\n if (bundlerEnv.DEV === true) {\n return true;\n }\n return null;\n};\n\nconst resolveNodeEnvironment = (nodeEnv: string | undefined): boolean | null => {\n if (typeof nodeEnv !== \"string\") {\n return null;\n }\n return nodeEnv !== \"production\";\n};\n\nconst readAmbientNodeEnv = (): string | undefined => {\n const processLike = (globalThis as typeof globalThis & { process?: ProcessLike }).process;\n return processLike?.env?.NODE_ENV;\n};\n\n/**\n * Decides whether the current environment is something other than production,\n * for features that should not switch themselves on in a shipped app.\n *\n * Sources are consulted in order \u2014 bundler flags, then `NODE_ENV` \u2014 and the\n * first that gives a definite answer wins. When none does, the answer is\n * `false`: an environment that never said it was safe is treated as production.\n *\n * Passing `nodeEnv` explicitly, *including as `undefined`*, opts out of reading\n * the ambient `process.env.NODE_ENV`. That lets a caller say \"I have looked and\n * there is no value\" and get the conservative answer, rather than having this\n * function reach for a global the caller deliberately ignored.\n *\n * @param options - Environment sources. Omit entirely to read ambient `NODE_ENV`.\n * @param options.bundlerEnv - A bundler's `import.meta.env`, if available.\n * @param options.nodeEnv - An explicit `NODE_ENV`; presence of the key disables the ambient read.\n * @returns `true` only when a source positively indicates a non-production environment.\n */\nexport const resolveNonProductionEnvironment = (\n options: {\n bundlerEnv?: NonProductionBundlerEnv | null | undefined;\n nodeEnv?: string | undefined;\n } = {}\n): boolean => {\n const resolvedNodeEnv = \"nodeEnv\" in options ? options.nodeEnv : readAmbientNodeEnv();\n\n return (\n resolveImportMetaEnvironment(options.bundlerEnv ?? null) ??\n resolveNodeEnvironment(resolvedNodeEnv) ??\n false\n );\n};\n", "/**\n * Machine-readable reasons a journey can refuse to build or operate.\n *\n * Closed on purpose: consumers should be able to `switch` over it exhaustively.\n * Adding a member is a minor change; removing or repurposing one is not done\n * within a major.\n */\nexport type JourneyErrorCode =\n /** A definition declared no steps. */\n | \"empty-definition\"\n /** The same step id was declared twice. */\n | \"duplicate-step-id\"\n /** A step id was referenced that the definition does not declare. */\n | \"unknown-step\"\n /** `initial` names a step the definition does not declare. */\n | \"unknown-initial-step\"\n /** A transition's `from` or `to` names a step that does not exist. */\n | \"dangling-transition\"\n /** Two plugins claimed the same `name`. */\n | \"duplicate-plugin-name\"\n /** Persistence was requested but no usable storage could be resolved. */\n | \"storage-unavailable\"\n /** Navigation work's `commit` returned a promise; it must be synchronous. */\n | \"async-commit\"\n /** A transition or work entry declared `label` as something other than a non-empty string. */\n | \"invalid-label\"\n /** A transition or work entry declared `timeoutMs` as something other than a positive finite number. */\n | \"invalid-timeout\";\n\nexport type JourneyErrorDetails = {\n /** The offending step id, where one caused the failure. */\n readonly stepId?: string;\n /** The offending event name, for transition failures. */\n readonly event?: string;\n /** The offending plugin name, for plugin failures. */\n readonly pluginName?: string;\n};\n\n/**\n * Every error core throws itself.\n *\n * Before this existed, distinguishing \"duplicate plugin name\" from \"unknown step\n * in transition\" meant matching on the message text \u2014 which quietly made every\n * message a compatibility promise. `code` is the stable contract; `message` is\n * for humans and may be reworded in any release.\n *\n * Errors surfaced through `NavigationResult.error` and the `error` subscription\n * event stay `unknown`: those carry whatever the caller's own work threw, which\n * core cannot constrain.\n */\nexport class JourneyError extends Error {\n readonly code: JourneyErrorCode;\n readonly stepId: string | undefined;\n readonly event: string | undefined;\n readonly pluginName: string | undefined;\n\n constructor(\n code: JourneyErrorCode,\n message: string,\n details: JourneyErrorDetails = {},\n cause?: unknown\n ) {\n super(`journey: ${message}`, cause === undefined ? undefined : { cause });\n this.name = \"JourneyError\";\n this.code = code;\n this.stepId = details.stepId;\n this.event = details.event;\n this.pluginName = details.pluginName;\n }\n}\n\n/** Narrowing helper, so consumers need not import the class to test for it. */\nexport const isJourneyError = (value: unknown): value is JourneyError =>\n value instanceof JourneyError;\n", "import { JourneyError } from \"../../core/errors\";\nimport type { JourneyPersistedState, JourneyStorage, PersistenceReason } from \"./persistence.types\";\nimport type { JourneyPersistOption, JourneySnapshot } from \"../../core/types\";\n\n/** Every observation kind a save can be scheduled on. */\nexport const DEFAULT_SAVE_REASONS: readonly PersistenceReason[] = [\n \"context\",\n \"transition\",\n \"status\"\n];\n\n/** Omitted means \"write immediately\"; anything else is clamped to a whole >= 0. */\nexport function normalizeDebounceMs(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 0;\n return Math.max(0, Math.trunc(value));\n}\n\nexport function buildPersistedState(snapshot: JourneySnapshot, now: number): JourneyPersistedState {\n return {\n status: snapshot.status,\n context: snapshot.context,\n timeline: snapshot.history.timeline,\n currentIndex: snapshot.history.currentIndex,\n savedAt: now\n };\n}\n\nconst JOURNEY_STATUSES = new Set([\n \"idle\",\n \"running\",\n \"paused\",\n \"completed\",\n \"terminated\"\n]) as ReadonlySet<string>;\n\n/**\n * Keys that must never survive `JSON.parse` into machine state.\n *\n * `JSON.parse` creates `__proto__` as an ordinary own property rather than\n * reassigning the prototype, so the parsed object is safe in isolation. It stops\n * being safe the moment application code spreads or `Object.assign`s it into a\n * fresh object \u2014 the own key is then copied as a *prototype assignment*. Storage\n * is attacker-reachable, so the shape is scrubbed before it can reach a context.\n */\nconst UNSAFE_KEYS = [\"__proto__\", \"constructor\", \"prototype\"] as const;\n\nfunction scrub(value: unknown, depth = 0): unknown {\n if (depth > 50 || value === null || typeof value !== \"object\") return value;\n if (Array.isArray(value)) return value.map((item) => scrub(item, depth + 1));\n\n const output: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n if ((UNSAFE_KEYS as readonly string[]).includes(key)) continue;\n output[key] = scrub(item, depth + 1);\n }\n return output;\n}\n\n/**\n * Parses a stored value; malformed or foreign payloads yield `null`.\n *\n * Validation is deliberately total: this is the only gate between an untrusted\n * storage entry and machine state, and `readPersisted()` hands the result\n * straight to application code typed as `JourneyPersistedState`.\n */\nexport function parsePersistedState(raw: string | null): JourneyPersistedState | null {\n if (raw === null) return null;\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as Record<string, unknown>;\n if (\n typeof candidate.status !== \"string\" ||\n !JOURNEY_STATUSES.has(candidate.status) ||\n !Array.isArray(candidate.timeline) ||\n !candidate.timeline.every((id) => typeof id === \"string\") ||\n typeof candidate.currentIndex !== \"number\" ||\n !Number.isInteger(candidate.currentIndex) ||\n typeof candidate.savedAt !== \"number\" ||\n !Number.isFinite(candidate.savedAt)\n ) {\n return null;\n }\n return {\n status: candidate.status,\n context: scrub(candidate.context),\n timeline: candidate.timeline,\n currentIndex: candidate.currentIndex,\n savedAt: candidate.savedAt\n } as JourneyPersistedState;\n}\n\n/**\n * Resolves the `persist` option's storage; throws when none is available.\n *\n * Reading `globalThis.localStorage` can itself throw \u2014 a third-party iframe\n * with storage blocked, or Safari's Lockdown Mode \u2014 rather than returning\n * undefined. Unguarded, that surfaced as a raw `SecurityError` out of\n * `createLinearJourney`, which reads as a library crash rather than an\n * environment that cannot persist. The failure stays loud (silently disabling\n * persistence loses data with no signal) but is now a `journey:` error naming\n * the fix, with the original kept as `cause`.\n */\nexport function resolvePersistStorage(option: JourneyPersistOption): JourneyStorage {\n if (option.storage) return option.storage;\n\n let ambient: JourneyStorage | undefined;\n try {\n ambient = globalThis.localStorage as JourneyStorage | undefined;\n } catch (error) {\n throw new JourneyError(\n \"storage-unavailable\",\n \"localStorage access was blocked by the environment; pass persist.storage explicitly\",\n {},\n error\n );\n }\n\n if (!ambient) {\n throw new JourneyError(\n \"storage-unavailable\",\n \"persist.storage is required when localStorage is unavailable\"\n );\n }\n return ambient;\n}\n\n/**\n * Reads a persisted record that can seed this machine, or `null`. Restore is\n * best-effort by design: terminal-status records, records whose timeline\n * mentions a step the current definition no longer declares (definition\n * drift), and malformed/foreign payloads are all ignored rather than thrown.\n */\nexport function readRestorableState(\n option: JourneyPersistOption,\n isDeclaredStep: (id: string) => boolean\n): JourneyPersistedState | null {\n let raw: string | null;\n try {\n raw = resolvePersistStorage(option).getItem(option.key);\n } catch {\n return null;\n }\n const record = parsePersistedState(raw);\n if (!record) return null;\n if (record.status !== \"running\" && record.status !== \"paused\") return null;\n if (!Number.isInteger(record.currentIndex)) return null;\n if (record.currentIndex < 0 || record.currentIndex >= record.timeline.length) return null;\n for (const id of record.timeline) {\n if (typeof id !== \"string\" || !isDeclaredStep(id)) return null;\n }\n return record;\n}\n", "import { warnInDevelopment } from \"@rxova/journey-common/dev\";\nimport {\n buildPersistedState,\n DEFAULT_SAVE_REASONS,\n normalizeDebounceMs,\n parsePersistedState,\n resolvePersistStorage\n} from \"./persistence.helpers\";\nimport type {\n JourneyPersistedState,\n PersistenceApi,\n PersistencePluginOptions,\n PersistenceReason,\n PersistenceState\n} from \"./persistence.types\";\nimport type { JourneyPersistOption, JourneyPlugin } from \"../../core/types\";\n\nexport {\n buildPersistedState,\n DEFAULT_SAVE_REASONS,\n normalizeDebounceMs,\n parsePersistedState\n} from \"./persistence.helpers\";\n\nconst IDLE_STATE: PersistenceState = { status: \"idle\", lastSavedAt: null, error: null };\n\n/**\n * Persists a serializable slice of machine state (status, context, timeline) on\n * every transition, status change, and context change. `saveOn` narrows which\n * of those trigger a write; `debounceMs` collapses a burst of them into one.\n *\n * The plugin itself is save-side only (plugins observe, they cannot seed the\n * runtime). Restore happens through the creation-time `persist` option, which\n * reads a restorable record before the machine is built; `readPersisted()`\n * exposes the saved state for callers wiring this plugin explicitly.\n */\nexport function createPersistencePlugin(\n options: PersistencePluginOptions\n): JourneyPlugin<\"persistence\", PersistenceApi, PersistenceState> {\n const now = options.now ?? Date.now;\n const debounceMs = normalizeDebounceMs(options.debounceMs);\n const saveReasons = new Set<PersistenceReason>(options.saveOn ?? DEFAULT_SAVE_REASONS);\n // Per plugin *instance*, not per machine: mutable state is scoped to setup(),\n // but `options` is not \u2014 two machines sharing one instance write the same key\n // and silently clobber each other.\n let setupCount = 0;\n return {\n name: \"persistence\",\n setup(host) {\n setupCount += 1;\n if (setupCount > 1) {\n warnInDevelopment(\n `journey: persistence plugin instance shared by ${setupCount} machines; they overwrite key \"${options.key}\". Create one per machine.`\n );\n }\n let lastWritten: JourneyPersistedState | null = null;\n let state: PersistenceState = IDLE_STATE;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let disposed = false;\n\n const succeeded = (written: JourneyPersistedState) => {\n lastWritten = written;\n state = { status: \"saved\", lastSavedAt: written.savedAt, error: null };\n };\n\n const failed = (error: unknown) => {\n state = { ...state, status: \"error\", error };\n host.reportError(error);\n };\n\n /**\n * Writes now. Returns a promise only so `flushPersisted` can be awaited;\n * the immediate path stays synchronous up to the `setItem` call, which is\n * what lets a throwing adapter surface through the runtime's listener\n * isolation exactly as it always did.\n */\n const save = (): void | Promise<void> => {\n const written = buildPersistedState(host.getSnapshot(), now());\n state = { ...state, status: \"saving\" };\n let result: void | Promise<void>;\n try {\n result = options.storage.setItem(options.key, JSON.stringify(written));\n } catch (error) {\n // Recorded here, but rethrown so the runtime's listener isolation\n // reports it exactly as it always did.\n state = { ...state, status: \"error\", error };\n throw error;\n }\n // `setItem` may be async. Discarding that promise turned a rejecting\n // adapter into an unhandled rejection, which terminates the process\n // under Node's default `--unhandled-rejections=throw`.\n if (result !== undefined) {\n return result.then(() => succeeded(written), failed);\n }\n succeeded(written);\n };\n\n const cancelTimer = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n /**\n * A debounced write happens on a timer, so there is no synchronous caller\n * left to throw to \u2014 its failures land in `state` and `reportError`\n * instead. The immediate path keeps throwing.\n */\n const runDebounced = () => {\n timer = null;\n try {\n const result = save();\n if (result !== undefined) void result;\n } catch (error) {\n failed(error);\n }\n };\n\n const onObservation = (reason: PersistenceReason) => {\n if (disposed || !saveReasons.has(reason)) return;\n if (debounceMs === 0) {\n void save();\n return;\n }\n cancelTimer();\n state = { ...state, status: \"pending\" };\n timer = setTimeout(runDebounced, debounceMs);\n };\n\n host.onContextChange(() => onObservation(\"context\"));\n host.onTransition(() => onObservation(\"transition\"));\n host.onStatusChange(({ current }) => {\n if (current === \"terminated\" && options.clearOnTerminate) {\n cancelTimer();\n lastWritten = null;\n state = IDLE_STATE;\n options.storage.removeItem(options.key);\n return;\n }\n onObservation(\"status\");\n });\n host.onDispose(() => {\n disposed = true;\n cancelTimer();\n });\n\n return {\n api: {\n inspectPersistedState: () => lastWritten,\n getPersistenceState: () => state,\n readPersisted: () => parsePersistedState(options.storage.getItem(options.key)),\n clearPersisted: () => {\n cancelTimer();\n lastWritten = null;\n state = IDLE_STATE;\n // Called directly by user code; record the failure here rather than\n // throwing out of an API whose sibling writes are all contained.\n try {\n options.storage.removeItem(options.key);\n } catch (error) {\n failed(error);\n }\n },\n flushPersisted: async () => {\n cancelTimer();\n try {\n await save();\n } catch (error) {\n failed(error);\n }\n }\n },\n deriveSnapshot: (_snapshot, previous) => (previous === state ? previous : state)\n };\n }\n };\n}\n\n/** Expands the creation-time `persist` option into the persistence plugin. */\nexport function persistOptionToPlugin(\n option: JourneyPersistOption\n): JourneyPlugin<\"persistence\", PersistenceApi, PersistenceState> {\n return createPersistencePlugin({ key: option.key, storage: resolvePersistStorage(option) });\n}\n", "import type { ReplayExportOptions, ReplaySession } from \"./replay.types\";\n\nexport function normalizeMaxEntries(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 500;\n return Math.max(1, Math.trunc(value));\n}\n\n/**\n * Depth past which nesting is truncated instead of walked. Guards the call\n * stack: a long parent/child chain used to overflow it, and the resulting\n * RangeError was swallowed by listener isolation, silently dropping the entry.\n */\nconst DEFAULT_MAX_DEPTH = 100;\n\nconst CIRCULAR = \"[circular]\";\nconst TRUNCATED = \"[max-depth]\";\n\ntype WalkState = {\n /** Completed conversions, so a shared subtree is walked exactly once. */\n readonly memo: Map<object, unknown>;\n /** Objects on the current path \u2014 these, and only these, are cycles. */\n readonly inProgress: Set<object>;\n readonly maxDepth: number;\n};\n\nfunction walk(value: unknown, state: WalkState, depth: number): unknown {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n if (typeof value === \"bigint\") return value.toString();\n if (typeof value === \"undefined\") return null;\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n return `[unsupported:${typeof value}]`;\n }\n if (value instanceof Error) {\n return {\n name: value.name,\n message: value.message,\n ...(value.stack ? { stack: value.stack } : {})\n };\n }\n if (value instanceof Date) return value.toISOString();\n if (typeof value !== \"object\") return String(value);\n\n const object = value as object;\n if (state.memo.has(object)) return state.memo.get(object);\n if (state.inProgress.has(object)) return CIRCULAR;\n if (depth >= state.maxDepth) return TRUNCATED;\n\n state.inProgress.add(object);\n let result: unknown;\n if (Array.isArray(value)) {\n result = value.map((item) => walk(item, state, depth + 1));\n } else {\n const output: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n output[key] = walk(item, state, depth + 1);\n }\n result = output;\n }\n state.inProgress.delete(object);\n state.memo.set(object, result);\n return result;\n}\n\n/**\n * Converts any value into a JSON-safe structure (circular refs, errors, dates).\n *\n * Memoized by reference, which is what keeps it linear in the number of\n * distinct objects. The previous walk removed each node from its `seen` set on\n * the way back up \u2014 correct for cycles, but it meant a shared subtree was\n * re-traversed once per path reaching it, so a context with diamond-shaped\n * sharing cost 2^N. Since the replay plugin serializes the whole snapshot on\n * every transition by default, that ran on the hot path: measured 21 ms at\n * depth 14, 241 ms at 18, 932 ms at 20, and no completion at 26.\n *\n * Arrays are now cycle-tracked too. They were checked before the object branch\n * and never entered `seen`, so a self-referencing array recursed until the\n * stack gave out.\n */\nexport function toSerializable(value: unknown, options: { maxDepth?: number } = {}): unknown {\n return walk(\n value,\n {\n memo: new Map<object, unknown>(),\n inProgress: new Set<object>(),\n maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH\n },\n 0\n );\n}\n\n/** Serializes a replay session into a JSON string safe for logging or export. */\nexport function serializeReplaySession(\n session: ReplaySession,\n options?: ReplayExportOptions\n): string {\n return JSON.stringify(toSerializable(session), null, options?.pretty ? 2 : undefined);\n}\n", "import { normalizeMaxEntries, serializeReplaySession, toSerializable } from \"./replay.helpers\";\nimport type { ReplayApi, ReplayEntry, ReplayEntryKind, ReplayPluginOptions } from \"./replay.types\";\nimport type { JourneyPlugin } from \"../../core/types\";\n\nexport { normalizeMaxEntries, serializeReplaySession, toSerializable } from \"./replay.helpers\";\n\n/** Records snapshot and lifecycle activity into an exportable replay session. */\nexport function createReplayPlugin(\n options: ReplayPluginOptions = {}\n): JourneyPlugin<\"replay\", ReplayApi, { entryCount: number }> {\n const maxEntries = normalizeMaxEntries(options.maxEntries);\n const captureSnapshots = options.captureSnapshots ?? true;\n const now = options.now ?? Date.now;\n\n return {\n name: \"replay\",\n setup(host) {\n let startedAt = now();\n let entries: ReplayEntry[] = [];\n\n const record = (kind: ReplayEntryKind, data: unknown) => {\n const entry: ReplayEntry = {\n at: now(),\n kind,\n data: toSerializable(data),\n ...(captureSnapshots ? { snapshot: toSerializable(host.getSnapshot()) } : {})\n };\n entries.push(entry);\n if (entries.length > maxEntries) {\n entries = entries.slice(entries.length - maxEntries);\n }\n };\n\n host.onTransition(({ from, to }) => record(\"transition\", { from, to }));\n host.onStatusChange(({ previous, current }) => record(\"status\", { previous, current }));\n host.onContextChange(({ previous, current }) => record(\"context\", { previous, current }));\n host.onNavigationBlocked(({ reason, from, to }) =>\n record(\"navigationBlocked\", { reason, from, to })\n );\n host.onError(({ phase, stepId, error }) => record(\"error\", { phase, stepId, error }));\n\n return {\n api: {\n getReplaySession: () => ({ startedAt, entries: [...entries] }),\n clearReplaySession: () => {\n startedAt = now();\n entries = [];\n },\n exportReplaySession: (exportOptions) =>\n serializeReplaySession({ startedAt, entries: [...entries] }, exportOptions)\n },\n deriveSnapshot: (_snapshot, previous) =>\n previous?.entryCount === entries.length ? previous : { entryCount: entries.length }\n };\n }\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["plugins_exports", "__export", "DEFAULT_SAVE_REASONS", "RECENT_EVENT_CAPACITY", "buildPersistedState", "createAnalyticsPlugin", "createExecutionPathsPlugin", "createPersistencePlugin", "createReplayPlugin", "normalizeDebounceMs", "normalizeMaxEntries", "parsePersistedState", "persistOptionToPlugin", "serializeReplaySession", "toSerializable", "__toCommonJS", "RECENT_EVENT_CAPACITY", "createAnalyticsPlugin", "options", "now", "host", "recent", "record", "entry", "trackSafely", "source", "name", "payload", "tracked", "error", "handlerError", "from", "to", "previous", "current", "reason", "phase", "stepId", "createExecutionPathsPlugin", "options", "maxPaths", "host", "currentPath", "completedPaths", "to", "current", "_snapshot", "previous", "isDevelopmentEnvironment", "diagnosticGlobal", "nodeEnv", "warnInDevelopment", "message", "detail", "JourneyError", "code", "message", "details", "cause", "DEFAULT_SAVE_REASONS", "normalizeDebounceMs", "value", "buildPersistedState", "snapshot", "now", "JOURNEY_STATUSES", "UNSAFE_KEYS", "scrub", "depth", "item", "output", "key", "parsePersistedState", "raw", "candidate", "id", "resolvePersistStorage", "option", "ambient", "error", "JourneyError", "IDLE_STATE", "createPersistencePlugin", "options", "now", "debounceMs", "normalizeDebounceMs", "saveReasons", "DEFAULT_SAVE_REASONS", "setupCount", "host", "warnInDevelopment", "lastWritten", "state", "timer", "disposed", "succeeded", "written", "failed", "error", "save", "buildPersistedState", "result", "cancelTimer", "runDebounced", "onObservation", "reason", "current", "parsePersistedState", "_snapshot", "previous", "persistOptionToPlugin", "option", "resolvePersistStorage", "normalizeMaxEntries", "value", "DEFAULT_MAX_DEPTH", "CIRCULAR", "TRUNCATED", "walk", "state", "depth", "object", "result", "item", "output", "key", "toSerializable", "options", "serializeReplaySession", "session", "createReplayPlugin", "options", "maxEntries", "normalizeMaxEntries", "captureSnapshots", "now", "host", "startedAt", "entries", "record", "kind", "data", "entry", "toSerializable", "from", "to", "previous", "current", "reason", "phase", "stepId", "error", "exportOptions", "serializeReplaySession", "_snapshot"]
7
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Every bundled plugin, behind one subpath.
3
+ *
4
+ * They used to have a subpath each, which made the import path a decision:
5
+ * one path for analytics, another for replay, another for persistence, another
6
+ * for execution paths — four ways to spell adding a plugin. Each
7
+ * is still its own module internally and every factory is a named export, so
8
+ * tree-shaking is unchanged: importing `createReplayPlugin` from here pulls in
9
+ * replay and nothing else.
10
+ */
11
+ export type { AnyJourneyPlugin, JourneyPlugin, PluginApis, PluginHost } from "../core/types.js";
12
+ export { createAnalyticsPlugin, RECENT_EVENT_CAPACITY } from "./analytics/analytics.js";
13
+ export type { AnalyticsApi, AnalyticsPluginOptions, AnalyticsRecentEvent, AnalyticsTrackedEvent } from "./analytics/analytics.types.js";
14
+ export { createExecutionPathsPlugin } from "./execution-paths/execution-paths.js";
15
+ export type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths/execution-paths.types.js";
16
+ export { buildPersistedState, createPersistencePlugin, DEFAULT_SAVE_REASONS, normalizeDebounceMs, parsePersistedState, persistOptionToPlugin } from "./persistence/persistence.js";
17
+ export type { JourneyPersistedState, JourneyStorage, PersistenceApi, PersistencePluginOptions, PersistenceReason, PersistenceState } from "./persistence/persistence.types.js";
18
+ export { createReplayPlugin, normalizeMaxEntries, serializeReplaySession, toSerializable } from "./replay/replay.js";
19
+ export type { ReplayApi, ReplayEntry, ReplayEntryKind, ReplayExportOptions, ReplayPluginOptions, ReplaySession } from "./replay/replay.types.js";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Every bundled plugin, behind one subpath.
3
+ *
4
+ * They used to have a subpath each, which made the import path a decision:
5
+ * one path for analytics, another for replay, another for persistence, another
6
+ * for execution paths — four ways to spell adding a plugin. Each
7
+ * is still its own module internally and every factory is a named export, so
8
+ * tree-shaking is unchanged: importing `createReplayPlugin` from here pulls in
9
+ * replay and nothing else.
10
+ */
11
+ export type { AnyJourneyPlugin, JourneyPlugin, PluginApis, PluginHost } from "../core/types.js";
12
+ export { createAnalyticsPlugin, RECENT_EVENT_CAPACITY } from "./analytics/analytics.js";
13
+ export type { AnalyticsApi, AnalyticsPluginOptions, AnalyticsRecentEvent, AnalyticsTrackedEvent } from "./analytics/analytics.types.js";
14
+ export { createExecutionPathsPlugin } from "./execution-paths/execution-paths.js";
15
+ export type { ExecutionPathsApi, ExecutionPathsPluginOptions, ExecutionPathsSnapshot } from "./execution-paths/execution-paths.types.js";
16
+ export { buildPersistedState, createPersistencePlugin, DEFAULT_SAVE_REASONS, normalizeDebounceMs, parsePersistedState, persistOptionToPlugin } from "./persistence/persistence.js";
17
+ export type { JourneyPersistedState, JourneyStorage, PersistenceApi, PersistencePluginOptions, PersistenceReason, PersistenceState } from "./persistence/persistence.types.js";
18
+ export { createReplayPlugin, normalizeMaxEntries, serializeReplaySession, toSerializable } from "./replay/replay.js";
19
+ export type { ReplayApi, ReplayEntry, ReplayEntryKind, ReplayExportOptions, ReplayPluginOptions, ReplaySession } from "./replay/replay.types.js";
@@ -0,0 +1,2 @@
1
+ var P=100;function C(e){let t=e.now??Date.now;return{name:"analytics",setup(n){let r=[],i=o=>{r.push(o),r.length>100&&(r=r.slice(r.length-100))},a=(o,s,u)=>{let l={name:s,timestamp:t(),stepId:n.getSnapshot().currentStep?.id??null,payload:u};try{e.track(l),i({source:o,tracked:l,success:!0})}catch(p){i({source:o,tracked:l,success:!1,error:p});try{e.onError?.(p,l)}catch(y){n.reportError(y)}}return l};return n.onTransition(({from:o,to:s})=>{a("lifecycle","journey.transition",{from:o,to:s})}),n.onStatusChange(({previous:o,current:s})=>{a("lifecycle",`journey.${s}`,{previous:o})}),n.onNavigationBlocked(({reason:o,from:s,to:u})=>{a("lifecycle","journey.navigationBlocked",{reason:o,from:s,to:u})}),n.onError(({phase:o,stepId:s,error:u})=>{a("lifecycle","journey.error",{phase:o,stepId:s,error:u})}),{api:{trackAnalyticsEvent:(o,s={})=>a("custom",o,s),getRecentEvents:()=>[...r],clearRecentEvents:()=>{r=[]}}}}}}function I(e={}){let t=e.maxPaths??50;return{name:"execution-paths",setup(n){let r=[],i=[];return n.onTransition(({to:a})=>{r.push(a)}),n.onStatusChange(({current:a})=>{(a==="completed"||a==="terminated")&&r.length>0&&(i.push(Object.freeze([...r])),i.length>t&&(i=i.slice(i.length-t)),r=[])}),{api:{getCurrentPath:()=>Object.freeze([...r]),getCompletedPaths:()=>Object.freeze([...i]),clearCompletedPaths:()=>{i=[]}},deriveSnapshot:(a,o)=>o&&o.currentPath.length===r.length&&o.completedPaths.length===i.length?o:Object.freeze({currentPath:Object.freeze([...r]),completedPaths:Object.freeze([...i])})}}}}var j=()=>{let e=globalThis;if(typeof e.__DEV__=="boolean")return e.__DEV__;let t=e.process?.env?.NODE_ENV;return t===void 0||t==="development"},v=(e,t)=>{if(!(!j()||typeof console>"u")){if(t===void 0){console.warn(e);return}console.warn("%s",e,t)}};var g=class extends Error{constructor(t,n,r={},i){super(`journey: ${n}`,i===void 0?void 0:{cause:i}),this.name="JourneyError",this.code=t,this.stepId=r.stepId,this.event=r.event,this.pluginName=r.pluginName}};var E=["context","transition","status"];function S(e){return typeof e!="number"||!Number.isFinite(e)?0:Math.max(0,Math.trunc(e))}function h(e,t){return{status:e.status,context:e.context,timeline:e.history.timeline,currentIndex:e.history.currentIndex,savedAt:t}}var z=new Set(["idle","running","paused","completed","terminated"]),M=["__proto__","constructor","prototype"];function w(e,t=0){if(t>50||e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(r=>w(r,t+1));let n={};for(let[r,i]of Object.entries(e))M.includes(r)||(n[r]=w(i,t+1));return n}function x(e){if(e===null)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!="object"||t===null)return null;let n=t;return typeof n.status!="string"||!z.has(n.status)||!Array.isArray(n.timeline)||!n.timeline.every(r=>typeof r=="string")||typeof n.currentIndex!="number"||!Number.isInteger(n.currentIndex)||typeof n.savedAt!="number"||!Number.isFinite(n.savedAt)?null:{status:n.status,context:w(n.context),timeline:n.timeline,currentIndex:n.currentIndex,savedAt:n.savedAt}}function J(e){if(e.storage)return e.storage;let t;try{t=globalThis.localStorage}catch(n){throw new g("storage-unavailable","localStorage access was blocked by the environment; pass persist.storage explicitly",{},n)}if(!t)throw new g("storage-unavailable","persist.storage is required when localStorage is unavailable");return t}var O={status:"idle",lastSavedAt:null,error:null};function N(e){let t=e.now??Date.now,n=S(e.debounceMs),r=new Set(e.saveOn??E),i=0;return{name:"persistence",setup(a){i+=1,i>1&&v(`journey: persistence plugin instance shared by ${i} machines; they overwrite key "${e.key}". Create one per machine.`);let o=null,s=O,u=null,l=!1,p=c=>{o=c,s={status:"saved",lastSavedAt:c.savedAt,error:null}},y=c=>{s={...s,status:"error",error:c},a.reportError(c)},R=()=>{let c=h(a.getSnapshot(),t());s={...s,status:"saving"};let d;try{d=e.storage.setItem(e.key,JSON.stringify(c))}catch(_){throw s={...s,status:"error",error:_},_}if(d!==void 0)return d.then(()=>p(c),y);p(c)},f=()=>{u!==null&&(clearTimeout(u),u=null)},D=()=>{u=null;try{let c=R()}catch(c){y(c)}},k=c=>{if(!(l||!r.has(c))){if(n===0){R();return}f(),s={...s,status:"pending"},u=setTimeout(D,n)}};return a.onContextChange(()=>k("context")),a.onTransition(()=>k("transition")),a.onStatusChange(({current:c})=>{if(c==="terminated"&&e.clearOnTerminate){f(),o=null,s=O,e.storage.removeItem(e.key);return}k("status")}),a.onDispose(()=>{l=!0,f()}),{api:{inspectPersistedState:()=>o,getPersistenceState:()=>s,readPersisted:()=>x(e.storage.getItem(e.key)),clearPersisted:()=>{f(),o=null,s=O;try{e.storage.removeItem(e.key)}catch(c){y(c)}},flushPersisted:async()=>{f();try{await R()}catch(c){y(c)}}},deriveSnapshot:(c,d)=>d===s?d:s}}}}function V(e){return N({key:e.key,storage:J(e)})}function A(e){return typeof e!="number"||!Number.isFinite(e)?500:Math.max(1,Math.trunc(e))}var L=100,U="[circular]",F="[max-depth]";function T(e,t,n){if(e===null||typeof e=="string"||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="bigint")return e.toString();if(typeof e>"u")return null;if(typeof e=="function"||typeof e=="symbol")return`[unsupported:${typeof e}]`;if(e instanceof Error)return{name:e.name,message:e.message,...e.stack?{stack:e.stack}:{}};if(e instanceof Date)return e.toISOString();if(typeof e!="object")return String(e);let r=e;if(t.memo.has(r))return t.memo.get(r);if(t.inProgress.has(r))return U;if(n>=t.maxDepth)return F;t.inProgress.add(r);let i;if(Array.isArray(e))i=e.map(a=>T(a,t,n+1));else{let a={};for(let[o,s]of Object.entries(e))a[o]=T(s,t,n+1);i=a}return t.inProgress.delete(r),t.memo.set(r,i),i}function m(e,t={}){return T(e,{memo:new Map,inProgress:new Set,maxDepth:t.maxDepth??L},0)}function b(e,t){return JSON.stringify(m(e),null,t?.pretty?2:void 0)}function B(e={}){let t=A(e.maxEntries),n=e.captureSnapshots??!0,r=e.now??Date.now;return{name:"replay",setup(i){let a=r(),o=[],s=(u,l)=>{let p={at:r(),kind:u,data:m(l),...n?{snapshot:m(i.getSnapshot())}:{}};o.push(p),o.length>t&&(o=o.slice(o.length-t))};return i.onTransition(({from:u,to:l})=>s("transition",{from:u,to:l})),i.onStatusChange(({previous:u,current:l})=>s("status",{previous:u,current:l})),i.onContextChange(({previous:u,current:l})=>s("context",{previous:u,current:l})),i.onNavigationBlocked(({reason:u,from:l,to:p})=>s("navigationBlocked",{reason:u,from:l,to:p})),i.onError(({phase:u,stepId:l,error:p})=>s("error",{phase:u,stepId:l,error:p})),{api:{getReplaySession:()=>({startedAt:a,entries:[...o]}),clearReplaySession:()=>{a=r(),o=[]},exportReplaySession:u=>b({startedAt:a,entries:[...o]},u)},deriveSnapshot:(u,l)=>l?.entryCount===o.length?l:{entryCount:o.length}}}}}export{E as DEFAULT_SAVE_REASONS,P as RECENT_EVENT_CAPACITY,h as buildPersistedState,C as createAnalyticsPlugin,I as createExecutionPathsPlugin,N as createPersistencePlugin,B as createReplayPlugin,S as normalizeDebounceMs,A as normalizeMaxEntries,x as parsePersistedState,V as persistOptionToPlugin,b as serializeReplaySession,m as toSerializable};
2
+ //# sourceMappingURL=index.js.map