@rxova/journey-core 0.7.0 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +236 -105
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.cjs.map +4 -4
  4. package/dist/index.d.cts +5 -4
  5. package/dist/index.d.ts +5 -4
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +4 -4
  8. package/dist/journey-builder/index.d.cts +5 -0
  9. package/dist/journey-builder/index.d.ts +5 -0
  10. package/dist/journey-builder/types.d.cts +126 -0
  11. package/dist/journey-builder/types.d.ts +126 -0
  12. package/dist/journey-machine/async-state.d.cts +12 -0
  13. package/dist/journey-machine/async-state.d.ts +12 -0
  14. package/dist/journey-machine/computed.d.cts +2 -0
  15. package/dist/journey-machine/computed.d.ts +2 -0
  16. package/dist/journey-machine/controls.d.cts +17 -0
  17. package/dist/journey-machine/controls.d.ts +17 -0
  18. package/dist/journey-machine/helpers.d.cts +52 -0
  19. package/dist/journey-machine/helpers.d.ts +52 -0
  20. package/dist/journey-machine/index.d.cts +3 -0
  21. package/dist/journey-machine/index.d.ts +3 -0
  22. package/dist/journey-machine/navigation.d.cts +32 -0
  23. package/dist/journey-machine/navigation.d.ts +32 -0
  24. package/dist/journey-machine/plugin-controller.d.cts +15 -0
  25. package/dist/journey-machine/plugin-controller.d.ts +15 -0
  26. package/dist/journey-machine/resolve-journey-definition.d.cts +2 -0
  27. package/dist/journey-machine/resolve-journey-definition.d.ts +2 -0
  28. package/dist/journey-machine/runtime.d.cts +33 -0
  29. package/dist/journey-machine/runtime.d.ts +33 -0
  30. package/dist/journey-machine/send.d.cts +9 -0
  31. package/dist/journey-machine/send.d.ts +9 -0
  32. package/dist/plugins/analytics/index.cjs +2 -0
  33. package/dist/plugins/analytics/index.cjs.map +7 -0
  34. package/dist/plugins/analytics/index.d.cts +25 -0
  35. package/dist/plugins/analytics/index.d.ts +25 -0
  36. package/dist/plugins/analytics/index.js +2 -0
  37. package/dist/plugins/analytics/index.js.map +7 -0
  38. package/dist/plugins/autosave/index.cjs +2 -0
  39. package/dist/plugins/autosave/index.cjs.map +7 -0
  40. package/dist/plugins/autosave/index.d.cts +17 -0
  41. package/dist/plugins/autosave/index.d.ts +17 -0
  42. package/dist/plugins/autosave/index.js +2 -0
  43. package/dist/plugins/autosave/index.js.map +7 -0
  44. package/dist/plugins/diagnostics/diagnostics.d.cts +3 -0
  45. package/dist/plugins/diagnostics/diagnostics.d.ts +3 -0
  46. package/dist/plugins/diagnostics/index.cjs +2 -0
  47. package/dist/plugins/diagnostics/index.cjs.map +7 -0
  48. package/dist/plugins/diagnostics/index.d.cts +15 -0
  49. package/dist/plugins/diagnostics/index.d.ts +15 -0
  50. package/dist/plugins/diagnostics/index.js +2 -0
  51. package/dist/plugins/diagnostics/index.js.map +7 -0
  52. package/dist/plugins/execution-paths/execution-paths.d.cts +6 -0
  53. package/dist/plugins/execution-paths/execution-paths.d.ts +6 -0
  54. package/dist/plugins/execution-paths/index.cjs +2 -0
  55. package/dist/plugins/execution-paths/index.cjs.map +7 -0
  56. package/dist/plugins/execution-paths/index.d.cts +18 -0
  57. package/dist/plugins/execution-paths/index.d.ts +18 -0
  58. package/dist/plugins/execution-paths/index.js +2 -0
  59. package/dist/plugins/execution-paths/index.js.map +7 -0
  60. package/dist/plugins/persistence/controller.d.cts +17 -0
  61. package/dist/plugins/persistence/controller.d.ts +17 -0
  62. package/dist/plugins/persistence/index.cjs +2 -0
  63. package/dist/plugins/persistence/index.cjs.map +7 -0
  64. package/dist/plugins/persistence/index.d.cts +12 -0
  65. package/dist/plugins/persistence/index.d.ts +12 -0
  66. package/dist/plugins/persistence/index.js +2 -0
  67. package/dist/plugins/persistence/index.js.map +7 -0
  68. package/dist/plugins/replay/index.cjs +2 -0
  69. package/dist/plugins/replay/index.cjs.map +7 -0
  70. package/dist/plugins/replay/index.d.cts +27 -0
  71. package/dist/plugins/replay/index.d.ts +27 -0
  72. package/dist/plugins/replay/index.js +2 -0
  73. package/dist/plugins/replay/index.js.map +7 -0
  74. package/dist/types/analytics.types.d.cts +33 -0
  75. package/dist/types/analytics.types.d.ts +33 -0
  76. package/dist/types/autosave.types.d.cts +23 -0
  77. package/dist/types/autosave.types.d.ts +23 -0
  78. package/dist/types/diagnostics.types.d.cts +38 -0
  79. package/dist/types/diagnostics.types.d.ts +38 -0
  80. package/dist/types/index.d.cts +9 -0
  81. package/dist/types/index.d.ts +9 -4
  82. package/dist/types/journey.types.d.cts +191 -0
  83. package/dist/types/journey.types.d.ts +140 -167
  84. package/dist/types/machine.types.d.cts +104 -0
  85. package/dist/types/machine.types.d.ts +104 -0
  86. package/dist/types/observation.types.d.cts +83 -0
  87. package/dist/types/observation.types.d.ts +83 -0
  88. package/dist/types/persistence.types.d.cts +34 -0
  89. package/dist/types/persistence.types.d.ts +13 -20
  90. package/dist/types/replay.types.d.cts +35 -0
  91. package/dist/types/replay.types.d.ts +35 -0
  92. package/dist/types/transitions.types.d.cts +122 -0
  93. package/dist/types/transitions.types.d.ts +100 -57
  94. package/package.json +92 -2
  95. package/dist/machine-helpers.d.ts +0 -23
  96. package/dist/machine.d.ts +0 -11
  97. package/dist/persistence.d.ts +0 -17
  98. package/dist/transitions.d.ts +0 -62
  99. package/dist/types.d.ts +0 -1
@@ -0,0 +1,2 @@
1
+ var p=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null),w=e=>Array.isArray(e)?e.map(t=>w(t)):p(e)?Object.fromEntries(Object.entries(e).map(([t,n])=>[t,w(n)])):e,R=e=>{if(e===null)return"null";if(Array.isArray(e))return"array";if(typeof e=="object"){let t=e.constructor?.name;return t&&t!=="Object"?t:"object"}return typeof e};function C(e,t,n,r=[]){if(e===null||typeof e=="string"||typeof e=="number"||typeof e=="boolean")return;let o=r.length===0?t:`${t}.${r.join(".")}`;if(e===void 0)throw new Error(`${o} must be JSON-serializable. Received undefined.`);if(typeof e=="bigint"||typeof e=="symbol"||typeof e=="function")throw new Error(`${o} must be JSON-serializable. Received ${typeof e}.`);if(typeof e!="object")throw new Error(`${o} must be JSON-serializable. Received ${R(e)}.`);if(n.seen.has(e))throw new Error(`${o} must be JSON-serializable. Circular references are not supported.`);if(n.seen.add(e),Array.isArray(e)){for(let[s,i]of e.entries())C(i,t,n,[...r,String(s)]);return}if(!p(e))throw new Error(`${o} must be JSON-serializable. Received non-plain ${R(e)}.`);for(let[s,i]of Object.entries(e))C(i,t,n,[...r,s])}function h(e,t="Journey context"){return C(e,t,{seen:new WeakSet}),e}var W=e=>w(h(e));var A=e=>[...new Set(e)],q=(e,t)=>Object.fromEntries(t.map(n=>[n,e[n]===!0])),J=(e,t)=>{let n=t??A(e),r=Object.fromEntries(n.map(o=>[o,!1]));for(let o of e)r[o]=!0;return r};var K=()=>{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(!(!K()||typeof console>"u")){if(t===void 0){console.warn(e);return}console.warn(e,t)}};var Q=()=>({phase:"idle",eventType:null,transitionId:null,error:null}),$=e=>({isLoading:!1,byStep:Object.fromEntries(Object.keys(e).map(n=>[n,Q()]))});var g=(e,t,n,r,o,s)=>{if(e.length===0)throw new Error("Journey timeline cannot be empty.");let i=Math.max(0,Math.min(Math.trunc(t),e.length-1)),u=e[i],d=A(s?[...Object.keys(s),...e]:e);return{status:r,currentStepId:u,history:{timeline:[...e],index:i},context:W(n),visited:s?q(s,d):J(e,d),async:w(o)}};var I=e=>typeof e=="object"&&e!==null,M=e=>e==="idled"||e==="running"||e==="completed"||e==="terminated",U=(e,t)=>{if(typeof e!="string")throw new Error(`Persistence ${t} entries must be strings.`);let n=e.trim();if(!n)throw new Error(`Persistence ${t} entries must not be empty.`);if(n.includes("[")||n.includes("]")||n.includes("*")||n.startsWith(".")||n.endsWith(".")||n.includes(".."))throw new Error(`Persistence ${t} entry "${n}" must use exact dot-separated object keys.`);let r=n.split(".");if(r.some(o=>!o||o.trim()!==o||/^[0-9]+$/.test(o)))throw new Error(`Persistence ${t} entry "${n}" must use exact dot-separated object keys.`);return r},V=(e,t)=>{if(e===void 0)return null;if(!Array.isArray(e))throw new Error(`Persistence ${t} must be an array of dot-separated strings.`);let n=new Map;for(let r of e){let o=U(r,t);n.set(o.join("."),o)}return[...n.values()]},D=(e,t)=>t.length<=e.length&&t.every((n,r)=>e[r]===n),L=(e,t)=>e.some(n=>D(t,n)),X=(e,t)=>e.some(n=>D(n,t)),F=(e,t,n,r=[])=>{if(L(n,r))return{value:void 0,changed:!0,included:!1};let o=t===null,s=o||L(t,r),i=o||X(t,r);if(!s&&!i)return{value:void 0,changed:!0,included:!1};if(!p(e))return{value:e,changed:!s,included:s};let u=!1,d={};for(let[y,x]of Object.entries(e)){let f=F(x,t,n,[...r,y]);if(!f.included){u=!0;continue}d[y]=f.value,f.changed&&(u=!0)}return{value:d,changed:u||Object.keys(d).length!==Object.keys(e).length,included:!0}},N=(e,t)=>{if(p(e)&&p(t)){let n=new Map;for(let[r,o]of Object.entries(e))n.set(r,o);for(let[r,o]of Object.entries(t))n.set(r,N(e[r],o));return Object.fromEntries(n)}return t===void 0?e??null:t},Y=(e,t)=>N(e,t),Z=()=>{let e=globalThis.localStorage;return!e||typeof e.getItem!="function"||typeof e.setItem!="function"||typeof e.removeItem!="function"?null:e},O=(e,t)=>{if(t){t(e);return}v("Journey persistence encountered an error without an onError handler.",e)},ee=e=>{if(!e)return null;let t=e.storage;if(t==null)try{t=Z()}catch(o){return O(o,e.onError),null}if(!t)return null;let n,r;try{let o=V(e.allowList,"allowList"),s=V(e.blockList,"blockList");n=o?o.map(i=>i.join(".")):void 0,r=s?s.map(i=>i.join(".")):[]}catch(o){return O(o,e.onError),null}return{key:e.key,storage:t,version:e.version??1,clearOnReset:e.clearOnReset??!0,...n?{allowList:n}:{},blockList:r,serialize:e.serialize??JSON.stringify,deserialize:e.deserialize??JSON.parse,...e.migrate?{migrate:e.migrate}:{},...e.onError?{onError:e.onError}:{}}},te=(e,t,n)=>{let r=J(t,n);if(I(e)){let o=!1,s={...r};for(let i of n){let u=e[i];if(u===!0){s[i]=!0;continue}if(u===!1){r[i]&&(o=!0);continue}o=!0}return{visited:s,needsRewrite:o}}if(Array.isArray(e)){let o=J(e.filter(i=>typeof i=="string"&&n.includes(i)),n);return{visited:Object.fromEntries(n.map(i=>[i,r[i]||o[i]])),needsRewrite:!0}}return{visited:r,needsRewrite:!0}},z=(e,t,n)=>{if(!I(e))return null;let r=!1,o=I(e.history)?e.history:null;o||(r=!0);let s=o?.timeline??e.timeline,i=Array.isArray(s)?s.filter(a=>typeof a=="string"&&a in t):[],u=typeof e.currentStepId=="string"&&e.currentStepId in t?e.currentStepId:typeof e.current=="string"&&e.current in t?e.current:null;if(i.length===0){if(!u)return null;i.push(u),r=!0}let d=i.length-1,y=o?.index??e.index;if(typeof y=="number"&&Number.isFinite(y))d=Math.max(0,Math.min(Math.trunc(y),i.length-1)),d!==y&&(r=!0);else if(u){let a=i.lastIndexOf(u);a>=0&&(d=a,r=!0)}let x=M(e.status)?e.status:"idled";M(e.status)||(r=!0);let f=Object.keys(t),{visited:E,needsRewrite:k}=te(e.visited,i,f);return k&&(r=!0),{snapshot:{currentStepId:i[d],history:{timeline:i,index:d},context:h("context"in e?e.context:n,"Persisted journey context"),status:x,visited:E},needsRewrite:r}},G=e=>{let{initial:t,context:n,steps:r,options:o}=e,s=ee(o),i=s?.allowList?s.allowList.map(a=>a.split(".")):null,u=s?s.blockList.map(a=>a.split(".")):[],d=a=>{O(a,s?.onError)},y=a=>{if(!s||i===null&&u.length===0)return{context:a,changed:!1};if(!p(a))return{context:a,changed:!1};let c=F(a,i,u);return{context:c.included&&p(c.value)?c.value:{},changed:c.changed}},x=a=>{if(s)try{let{context:c}=y(a.context),T={version:s.version,snapshot:{currentStepId:a.currentStepId,history:{timeline:[...a.history.timeline],index:a.history.index},context:c,status:a.status,visited:{...a.visited}}};s.storage.setItem(s.key,s.serialize(T))}catch(c){d(c)}},f=()=>{if(s)try{s.storage.removeItem(s.key)}catch(a){d(a)}},E=()=>g([t],0,n,"idled",$(r)),k=(a=E())=>{let c=g(a.history.timeline,a.history.index,a.context,a.status,a.async,a.visited);if(!s)return c;try{let T=s.storage.getItem(s.key);if(!T)return c;let S=s.deserialize(T);if(!I(S))return c;let m=S.version;if(typeof m!="number")return c;let l=null,P=!1;if(m===s.version){let b=z(S.snapshot,r,n);l=b?.snapshot??null,P=!!b?.needsRewrite}else if(s.migrate){let b=s.migrate(S.snapshot,m);l=z(b,r,n)?.snapshot??null,P=l!==null,l||d(new Error(`Journey persistence migrate() returned data that could not be coerced into a valid snapshot. Falling back to the initial snapshot (persisted version: ${m}, current version: ${s.version}).`))}if(!l)return c;let{context:_,changed:H}=y(l.context),B=Y(c.context,_),j=g(l.history.timeline,l.history.index,h(B,"Hydrated journey context"),l.status==="running"?"idled":l.status,c.async,l.visited);return(P||H)&&x(j),j}catch(T){return d(T),c}};return{clearOnReset:s?.clearOnReset??!0,hydrateSnapshot:k,persistSnapshot:x,removePersistedSnapshot:f}};var ae=e=>({name:"persistence",setup:(({resolvedJourney:n})=>{let r=G({initial:n.initial,context:n.context,steps:n.steps,options:e});return{hydrateSnapshot:o=>r.hydrateSnapshot(o),onSnapshotChange:({snapshot:o,reason:s})=>{if(s!=="async"){if(s==="reset"&&r.clearOnReset){r.removePersistedSnapshot();return}r.persistSnapshot(o)}}}})});export{G as createPersistenceController,ae as createPersistencePlugin};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/journey-machine/helpers.ts", "../../../src/plugins/persistence/controller.ts", "../../../src/plugins/persistence/index.ts"],
4
+ "sourcesContent": ["import type {\n JourneyAsyncState,\n JourneyEvent,\n JourneyGoToEvent,\n JourneyJsonObject,\n JourneyJsonValue,\n JourneyPayloadFor,\n JourneyResolvedTransition,\n JourneySendEvent,\n JourneySendResult,\n JourneySnapshot,\n JourneyStatus,\n JourneyStepAsyncState,\n JourneyTerminal,\n JourneyTransition,\n JourneyTransitionArgs\n} from \"../types\";\n\nexport const assertStepExists = <TStepId extends string>(\n steps: Record<TStepId, unknown>,\n stepId: TStepId,\n message: string\n) => {\n if (!(stepId in steps)) {\n throw new Error(message);\n }\n};\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);\n\nconst cloneSerializableValue = <T extends JourneyJsonValue>(value: T): T => {\n if (Array.isArray(value)) {\n return value.map((entry) => cloneSerializableValue(entry)) as T;\n }\n\n if (isPlainObject(value)) {\n return Object.fromEntries(\n Object.entries(value).map(([key, entryValue]) => [\n key,\n cloneSerializableValue(entryValue as JourneyJsonValue)\n ])\n ) as T;\n }\n\n return value;\n};\n\ntype SerializableCheckState = {\n readonly seen: WeakSet<object>;\n};\n\nconst describeValue = (value: unknown): string => {\n if (value === null) {\n return \"null\";\n }\n\n if (Array.isArray(value)) {\n return \"array\";\n }\n\n if (typeof value === \"object\") {\n const constructorName = (value as { constructor?: { name?: string } }).constructor?.name;\n return constructorName && constructorName !== \"Object\" ? constructorName : \"object\";\n }\n\n return typeof value;\n};\n\nfunction assertSerializableValue(\n value: unknown,\n label: string,\n state: SerializableCheckState,\n path: readonly string[] = []\n): asserts value is JourneyJsonValue {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return;\n }\n\n const pathLabel = path.length === 0 ? label : `${label}.${path.join(\".\")}`;\n\n if (value === undefined) {\n throw new Error(`${pathLabel} must be JSON-serializable. Received undefined.`);\n }\n\n if (typeof value === \"bigint\" || typeof value === \"symbol\" || typeof value === \"function\") {\n throw new Error(`${pathLabel} must be JSON-serializable. Received ${typeof value}.`);\n }\n\n if (typeof value !== \"object\") {\n throw new Error(`${pathLabel} must be JSON-serializable. Received ${describeValue(value)}.`);\n }\n\n if (state.seen.has(value)) {\n throw new Error(\n `${pathLabel} must be JSON-serializable. Circular references are not supported.`\n );\n }\n\n state.seen.add(value);\n\n if (Array.isArray(value)) {\n for (const [index, entryValue] of value.entries()) {\n assertSerializableValue(entryValue, label, state, [...path, String(index)]);\n }\n return;\n }\n\n if (!isPlainObject(value)) {\n throw new Error(\n `${pathLabel} must be JSON-serializable. Received non-plain ${describeValue(value)}.`\n );\n }\n\n for (const [key, entryValue] of Object.entries(value)) {\n assertSerializableValue(entryValue, label, state, [...path, key]);\n }\n}\n\nexport function assertSerializableContext<TContext extends JourneyJsonObject>(\n context: TContext,\n label = \"Journey context\"\n): TContext {\n assertSerializableValue(context, label, { seen: new WeakSet() });\n return context;\n}\n\nexport const cloneContext = <TContext extends JourneyJsonObject>(context: TContext): TContext =>\n cloneSerializableValue(assertSerializableContext(context));\n\nconst cloneDefinitionValueFallback = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n return value.map((entry) => cloneDefinitionValueFallback(entry));\n }\n\n if (value instanceof Date) {\n return new Date(value.getTime());\n }\n\n if (value instanceof Map) {\n return new Map(\n [...value.entries()].map(([key, entryValue]) => [\n cloneDefinitionValueFallback(key),\n cloneDefinitionValueFallback(entryValue)\n ])\n );\n }\n\n if (value instanceof Set) {\n return new Set([...value].map((entry) => cloneDefinitionValueFallback(entry)));\n }\n\n if (isPlainObject(value)) {\n return Object.fromEntries(\n Object.entries(value).map(([key, entryValue]) => [\n key,\n cloneDefinitionValueFallback(entryValue)\n ])\n );\n }\n\n return value;\n};\n\nexport const cloneMetaValue = <T>(value: T): T => {\n if ((typeof value !== \"object\" && typeof value !== \"function\") || value === null) {\n return value;\n }\n\n if (typeof structuredClone === \"function\") {\n try {\n return structuredClone(value);\n } catch {\n return cloneDefinitionValueFallback(value) as T;\n }\n } else {\n return cloneDefinitionValueFallback(value) as T;\n }\n};\n\nconst freezeSnapshotValue = (value: unknown) => {\n if (typeof value !== \"object\" || value === null || Object.isFrozen(value)) {\n return;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n freezeSnapshotValue(item);\n }\n Object.freeze(value);\n return;\n }\n\n if (!isPlainObject(value)) {\n return;\n }\n\n for (const entryValue of Object.values(value)) {\n freezeSnapshotValue(entryValue);\n }\n Object.freeze(value);\n};\n\nexport const normalizeStepCount = (steps?: number): number => {\n if (typeof steps !== \"number\" || !Number.isFinite(steps)) {\n return 1;\n }\n return Math.max(1, Math.trunc(steps));\n};\n\nexport const now = (): number => Date.now();\n\nconst unique = <T>(items: readonly T[]): T[] => [...new Set(items)];\n\nconst normalizeVisited = <TStepId extends string>(\n visited: Record<TStepId, boolean>,\n stepIds: readonly TStepId[]\n): Record<TStepId, boolean> =>\n Object.fromEntries(stepIds.map((stepId) => [stepId, visited[stepId] === true])) as Record<\n TStepId,\n boolean\n >;\n\nexport const buildVisitedFromTimeline = <TStepId extends string>(\n timeline: readonly TStepId[],\n stepIds?: readonly TStepId[]\n): Record<TStepId, boolean> => {\n const resolvedStepIds = stepIds ?? unique(timeline);\n const visited = Object.fromEntries(resolvedStepIds.map((stepId) => [stepId, false])) as Record<\n TStepId,\n boolean\n >;\n\n for (const stepId of timeline) {\n visited[stepId] = true;\n }\n\n return visited;\n};\n\nexport const appendVisited = <TStepId extends string>(\n visited: Record<TStepId, boolean>,\n current: TStepId\n): Record<TStepId, boolean> => ({\n ...visited,\n [current]: true\n});\n\nexport const isPromiseLike = <T>(value: T | PromiseLike<T>): value is PromiseLike<T> =>\n typeof value === \"object\" &&\n value !== null &&\n \"then\" in value &&\n typeof (value as { then: unknown }).then === \"function\";\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nexport const validateFiniteTimeout = (timeoutMs: unknown, label: string) => {\n if (timeoutMs !== undefined && !isFiniteNumber(timeoutMs)) {\n throw new Error(`${label} must define a finite numeric \"timeoutMs\" when provided.`);\n }\n};\n\nexport class JourneyTimeoutError extends Error {\n override name = \"JourneyTimeoutError\";\n\n constructor(message: string) {\n super(message);\n }\n}\n\nexport class JourneyDisposedError extends Error {\n override name = \"JourneyDisposedError\";\n readonly operation: string;\n\n constructor(operation: string) {\n super(`Journey machine has been disposed; \"${operation}\" can no longer be used.`);\n this.operation = operation;\n }\n}\n\ntype DiagnosticGlobal = typeof globalThis & {\n __DEV__?: unknown;\n process?: {\n env?: {\n NODE_ENV?: string;\n };\n };\n};\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\nexport const warnInDevelopment = (message: string, detail?: unknown) => {\n if (!isDevelopmentEnvironment() || typeof console === \"undefined\") {\n return;\n }\n\n if (detail === undefined) {\n console.warn(message);\n return;\n }\n\n console.warn(message, detail);\n};\n\nexport const errorInDevelopment = (message: string, detail?: unknown) => {\n if (!isDevelopmentEnvironment() || typeof console === \"undefined\") {\n return;\n }\n\n if (detail === undefined) {\n console.error(message);\n return;\n }\n\n console.error(message, detail);\n};\n\nexport const withTimeout = async <T>(\n promise: PromiseLike<T>,\n timeoutMs: number | undefined,\n buildError: () => Error\n): Promise<T> => {\n if (!isFiniteNumber(timeoutMs) || timeoutMs <= 0) {\n return await promise;\n }\n\n return await new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(buildError());\n }, timeoutMs);\n\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n }\n );\n });\n};\n\nexport const withAbortSignal = async <T>(\n promise: PromiseLike<T>,\n signal: AbortSignal\n): Promise<T> => {\n if (signal.aborted) {\n throw signal.reason;\n }\n\n return await new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(signal.reason);\n };\n\n signal.addEventListener(\"abort\", onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error);\n }\n );\n });\n};\n\nexport const buildIdleStepAsyncState = (): JourneyStepAsyncState => ({\n phase: \"idle\",\n eventType: null,\n transitionId: null,\n error: null\n});\n\nexport const buildInitialAsyncState = <TStepId extends string>(\n steps: Record<TStepId, unknown>\n): JourneyAsyncState<TStepId> => {\n const byStep = Object.fromEntries(\n Object.keys(steps).map((stepId) => [stepId, buildIdleStepAsyncState()])\n ) as Record<TStepId, JourneyStepAsyncState>;\n\n return {\n isLoading: false,\n byStep\n };\n};\n\nexport const isGoToStepByIdEvent = <\n TStepId extends string,\n TEventMap extends Record<string, unknown>\n>(\n event: JourneySendEvent<TStepId, TEventMap>\n): event is JourneyGoToEvent<TStepId, JourneyPayloadFor<TEventMap, \"goToStepById\">> =>\n event.type === \"goToStepById\" && \"stepId\" in event;\n\nexport const isTerminalTarget = <TStepId extends string>(\n target: TStepId | JourneyTerminal\n): target is JourneyTerminal => target === \"COMPLETE\" || target === \"TERMINATED\";\n\nexport const resolveTransitionTarget = <\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown>,\n THandlers extends Record<string, unknown>\n>(\n transition: JourneyTransition<TContext, TStepId, TEventMap, THandlers>\n): TStepId | JourneyTerminal =>\n transition.event === \"completeJourney\"\n ? \"COMPLETE\"\n : transition.event === \"terminateJourney\"\n ? \"TERMINATED\"\n : (transition.to as TStepId | JourneyTerminal);\n\nexport const validateJourneyTransitions = <\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown>,\n THandlers extends Record<string, unknown>\n>(\n transitions: readonly JourneyResolvedTransition<TContext, TStepId, TEventMap, THandlers>[],\n steps: Record<TStepId, unknown>\n) => {\n const stepRegistry = steps as Record<string, unknown>;\n const allowedKeys = new Set([\n \"from\",\n \"event\",\n \"to\",\n \"when\",\n \"updateContext\",\n \"onEnter\",\n \"onLeave\",\n \"id\",\n \"label\",\n \"timeoutMs\"\n ]);\n\n for (const [index, transition] of transitions.entries()) {\n if (!transition || typeof transition !== \"object\") {\n throw new Error(`Journey transition at index ${index} must be an object.`);\n }\n\n for (const key of Object.keys(transition)) {\n if (!allowedKeys.has(key)) {\n throw new Error(\n `Journey transition at index ${index} contains unsupported field \"${key}\".`\n );\n }\n }\n\n if (typeof transition.from !== \"string\" || typeof transition.event !== \"string\") {\n throw new Error(\n `Journey transition at index ${index} must define string \"from\" and \"event\".`\n );\n }\n\n if (transition.from !== \"*\" && !(transition.from in stepRegistry)) {\n throw new Error(\n `Journey transition at index ${index} references unknown from step \"${transition.from}\".`\n );\n }\n\n validateFiniteTimeout(transition.timeoutMs, `Journey transition at index ${index}`);\n\n if (transition.when !== undefined && typeof transition.when !== \"function\") {\n throw new Error(\n `Journey transition at index ${index} must define \"when\" as a function when provided.`\n );\n }\n\n if (transition.updateContext !== undefined && typeof transition.updateContext !== \"function\") {\n throw new Error(\n `Journey transition at index ${index} must define \"updateContext\" as a function when provided.`\n );\n }\n\n if (transition.onEnter !== undefined && typeof transition.onEnter !== \"function\") {\n throw new Error(\n `Journey transition at index ${index} must define \"onEnter\" as a function when provided.`\n );\n }\n\n if (transition.onLeave !== undefined && typeof transition.onLeave !== \"function\") {\n throw new Error(\n `Journey transition at index ${index} must define \"onLeave\" as a function when provided.`\n );\n }\n\n if (transition.id !== undefined && typeof transition.id !== \"string\") {\n throw new Error(\n `Journey transition at index ${index} must define \"id\" as a string when provided.`\n );\n }\n\n if (transition.label !== undefined && typeof transition.label !== \"string\") {\n throw new Error(\n `Journey transition at index ${index} must define \"label\" as a string when provided.`\n );\n }\n\n if (transition.event === \"completeJourney\" || transition.event === \"terminateJourney\") {\n if (\"to\" in transition && transition.to !== undefined) {\n throw new Error(\n `Journey transition at index ${index} with event \"${transition.event}\" cannot define \"to\".`\n );\n }\n continue;\n }\n\n if (typeof transition.to !== \"string\") {\n throw new Error(\n `Journey transition at index ${index} with event \"${transition.event}\" must define string \"to\".`\n );\n }\n\n if (!isTerminalTarget(transition.to) && !(transition.to in stepRegistry)) {\n throw new Error(\n `Journey transition at index ${index} points to unknown step \"${transition.to}\".`\n );\n }\n }\n};\n\nexport const buildSendResult = <TContext extends JourneyJsonObject, TStepId extends string>(\n snapshot: JourneySnapshot<TContext, TStepId>,\n transitioned: boolean,\n options: {\n transitionId?: string;\n label?: string;\n error?: unknown;\n } = {}\n): JourneySendResult<TContext, TStepId> => ({\n transitioned,\n ...(options.transitionId !== undefined ? { transitionId: options.transitionId } : {}),\n ...(options.label !== undefined ? { label: options.label } : {}),\n ...(\"error\" in options ? { error: options.error } : {}),\n snapshot\n});\n\nexport const buildSnapshot = <TContext extends JourneyJsonObject, TStepId extends string>(\n timeline: readonly TStepId[],\n index: number,\n context: TContext,\n status: JourneyStatus,\n asyncState: JourneyAsyncState<TStepId>,\n visited?: Record<TStepId, boolean>\n): JourneySnapshot<TContext, TStepId> => {\n if (timeline.length === 0) {\n throw new Error(\"Journey timeline cannot be empty.\");\n }\n\n const safeIndex = Math.max(0, Math.min(Math.trunc(index), timeline.length - 1));\n const currentStepId = timeline[safeIndex] as TStepId;\n const stepIds = unique(\n visited ? ([...(Object.keys(visited) as TStepId[]), ...timeline] as const) : timeline\n );\n\n return {\n status,\n currentStepId,\n history: {\n timeline: [...timeline],\n index: safeIndex\n },\n context: cloneContext(context),\n visited: visited\n ? normalizeVisited(visited, stepIds)\n : buildVisitedFromTimeline(timeline, stepIds),\n async: cloneSerializableValue(\n asyncState as unknown as JourneyJsonValue\n ) as JourneyAsyncState<TStepId>\n };\n};\n\nexport const stabilizeSnapshot = <TContext extends JourneyJsonObject, TStepId extends string>(\n snapshot: JourneySnapshot<TContext, TStepId>\n): JourneySnapshot<TContext, TStepId> => {\n const stableSnapshot: JourneySnapshot<TContext, TStepId> = {\n ...snapshot,\n history: {\n timeline: [...snapshot.history.timeline],\n index: snapshot.history.index\n },\n context: cloneContext(snapshot.context),\n visited: { ...snapshot.visited },\n async: cloneSerializableValue(\n snapshot.async as unknown as JourneyJsonValue\n ) as JourneyAsyncState<TStepId>\n };\n\n freezeSnapshotValue(stableSnapshot);\n return stableSnapshot;\n};\n\nexport const selectTransition = async <\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown>,\n THandlers extends Record<string, unknown>,\n TTransition extends {\n from: TStepId | \"*\";\n event: string;\n when?: (\n args: JourneyTransitionArgs<TContext, TStepId, TEventMap, THandlers>\n ) => boolean | Promise<boolean>;\n timeoutMs?: number;\n id?: string;\n label?: string;\n }\n>(\n transitions: readonly TTransition[],\n snapshot: JourneySnapshot<TContext, TStepId>,\n event: JourneyEvent<TStepId, TEventMap>,\n signal: AbortSignal,\n handlers: THandlers,\n hooks?: {\n onAsyncGuardStart?: (transition: TTransition) => void;\n onAsyncGuardSuccess?: (transition: TTransition) => void;\n onAsyncGuardError?: (transition: TTransition, error: unknown) => void;\n },\n defaultTimeoutMs?: number\n): Promise<TTransition | null> => {\n for (const transition of transitions) {\n const fromMatches = transition.from === \"*\" || transition.from === snapshot.currentStepId;\n const eventMatches = transition.event === event.type;\n\n if (!fromMatches || !eventMatches) {\n continue;\n }\n\n if (!transition.when) {\n return transition;\n }\n\n let guardResult: boolean | Promise<boolean>;\n if (signal.aborted) {\n throw signal.reason;\n }\n\n guardResult = (\n transition.when as (\n args: JourneyTransitionArgs<TContext, TStepId, TEventMap, THandlers>\n ) => boolean | Promise<boolean>\n )({\n snapshot,\n context: snapshot.context,\n from: snapshot.currentStepId,\n timeline: snapshot.history.timeline,\n index: snapshot.history.index,\n event,\n signal,\n handlers\n });\n\n const asyncGuard = isPromiseLike(guardResult);\n const asyncGuardPromise = asyncGuard ? (guardResult as PromiseLike<boolean>) : null;\n\n let allowed: boolean;\n try {\n if (asyncGuard) {\n hooks?.onAsyncGuardStart?.(transition);\n allowed = await withTimeout(\n withAbortSignal(asyncGuardPromise as PromiseLike<boolean>, signal),\n transition.timeoutMs ?? defaultTimeoutMs,\n () =>\n new JourneyTimeoutError(\n `Transition guard timed out after ${transition.timeoutMs ?? defaultTimeoutMs}ms (event: ${event.type}, transition: ${transition.id ?? \"<anonymous>\"}).`\n )\n );\n } else {\n allowed = guardResult as boolean;\n }\n } catch (error) {\n if (asyncGuard) {\n hooks?.onAsyncGuardError?.(transition, error);\n }\n throw error;\n }\n\n if (asyncGuard) {\n hooks?.onAsyncGuardSuccess?.(transition);\n }\n\n if (allowed) {\n return transition;\n }\n }\n\n return null;\n};\n\nexport const transitionSnapshot = <TContext extends JourneyJsonObject, TStepId extends string>(\n snapshot: JourneySnapshot<TContext, TStepId>,\n nextCurrent: TStepId,\n nextContext: TContext\n): JourneySnapshot<TContext, TStepId> => {\n const baseTimeline = snapshot.history.timeline.slice(0, snapshot.history.index + 1);\n let nextTimeline = baseTimeline;\n if (nextCurrent !== snapshot.currentStepId) {\n nextTimeline = [...baseTimeline, nextCurrent];\n }\n\n const nextIndex = nextTimeline.length - 1;\n const visited = appendVisited(snapshot.visited, nextCurrent);\n\n return buildSnapshot(\n nextTimeline,\n nextIndex,\n nextContext,\n snapshot.status,\n snapshot.async,\n visited\n );\n};\n", "import type {\n JourneyPersistedState,\n JourneyPersistenceOptions,\n JourneyStorage,\n ResolvedPersistence\n} from \"../../types/persistence.types\";\nimport type {\n JourneyJsonValue,\n JourneyJsonObject,\n JourneySnapshot,\n JourneySnapshotStateBase,\n JourneyStatus\n} from \"../../types/journey.types\";\nimport {\n assertSerializableContext,\n buildInitialAsyncState,\n buildSnapshot,\n buildVisitedFromTimeline,\n isPlainObject,\n warnInDevelopment\n} from \"../../journey-machine/helpers\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isStatusValue = (value: unknown): value is JourneyStatus =>\n value === \"idled\" || value === \"running\" || value === \"completed\" || value === \"terminated\";\n\ntype PersistencePath = readonly string[];\n\nconst normalizePersistencePath = (\n path: unknown,\n optionName: \"allowList\" | \"blockList\"\n): PersistencePath => {\n if (typeof path !== \"string\") {\n throw new Error(`Persistence ${optionName} entries must be strings.`);\n }\n\n const trimmedPath = path.trim();\n if (!trimmedPath) {\n throw new Error(`Persistence ${optionName} entries must not be empty.`);\n }\n\n if (\n trimmedPath.includes(\"[\") ||\n trimmedPath.includes(\"]\") ||\n trimmedPath.includes(\"*\") ||\n trimmedPath.startsWith(\".\") ||\n trimmedPath.endsWith(\".\") ||\n trimmedPath.includes(\"..\")\n ) {\n throw new Error(\n `Persistence ${optionName} entry \"${trimmedPath}\" must use exact dot-separated object keys.`\n );\n }\n\n const segments = trimmedPath.split(\".\");\n if (\n segments.some((segment) => !segment || segment.trim() !== segment || /^[0-9]+$/.test(segment))\n ) {\n throw new Error(\n `Persistence ${optionName} entry \"${trimmedPath}\" must use exact dot-separated object keys.`\n );\n }\n\n return segments;\n};\n\nconst normalizePersistencePaths = (\n paths: unknown,\n optionName: \"allowList\" | \"blockList\"\n): readonly PersistencePath[] | null => {\n if (paths === undefined) {\n return null;\n }\n\n if (!Array.isArray(paths)) {\n throw new Error(`Persistence ${optionName} must be an array of dot-separated strings.`);\n }\n\n const normalized = new Map<string, PersistencePath>();\n for (const path of paths) {\n const resolvedPath = normalizePersistencePath(path, optionName);\n normalized.set(resolvedPath.join(\".\"), resolvedPath);\n }\n\n return [...normalized.values()];\n};\n\nconst pathStartsWith = (path: readonly string[], prefix: readonly string[]): boolean =>\n prefix.length <= path.length && prefix.every((segment, index) => path[index] === segment);\n\nconst pathHasAncestorMatch = (\n candidates: readonly PersistencePath[],\n path: readonly string[]\n): boolean => candidates.some((candidate) => pathStartsWith(path, candidate));\n\nconst pathHasDescendantMatch = (\n candidates: readonly PersistencePath[],\n path: readonly string[]\n): boolean => candidates.some((candidate) => pathStartsWith(candidate, path));\n\nconst filterContextValue = (\n value: unknown,\n allowList: readonly PersistencePath[] | null,\n blockList: readonly PersistencePath[],\n path: readonly string[] = []\n): { value: unknown; changed: boolean; included: boolean } => {\n if (pathHasAncestorMatch(blockList, path)) {\n return { value: undefined, changed: true, included: false };\n }\n\n const allowAll = allowList === null;\n const pathIsAllowed = allowAll || pathHasAncestorMatch(allowList, path);\n const pathHasAllowedDescendants = allowAll || pathHasDescendantMatch(allowList, path);\n\n if (!pathIsAllowed && !pathHasAllowedDescendants) {\n return { value: undefined, changed: true, included: false };\n }\n\n if (!isPlainObject(value)) {\n return {\n value,\n changed: !pathIsAllowed,\n included: pathIsAllowed\n };\n }\n\n let changed = false;\n const nextValue: Record<string, unknown> = {};\n\n for (const [key, childValue] of Object.entries(value)) {\n const result = filterContextValue(childValue, allowList, blockList, [...path, key]);\n if (!result.included) {\n changed = true;\n continue;\n }\n\n nextValue[key] = result.value;\n if (result.changed) {\n changed = true;\n }\n }\n\n return {\n value: nextValue,\n changed: changed || Object.keys(nextValue).length !== Object.keys(value).length,\n included: true\n };\n};\n\nconst mergePersistedValue = (\n initialValue: JourneyJsonValue | undefined,\n persistedValue: unknown\n): JourneyJsonValue => {\n if (isPlainObject(initialValue) && isPlainObject(persistedValue)) {\n const mergedEntries = new Map<string, JourneyJsonValue>();\n\n for (const [key, value] of Object.entries(initialValue)) {\n mergedEntries.set(key, value as JourneyJsonValue);\n }\n\n for (const [key, value] of Object.entries(persistedValue)) {\n mergedEntries.set(\n key,\n mergePersistedValue(initialValue[key] as JourneyJsonValue | undefined, value)\n );\n }\n\n return Object.fromEntries(mergedEntries) as JourneyJsonValue;\n }\n\n return persistedValue === undefined\n ? (initialValue ?? null)\n : (persistedValue as JourneyJsonValue);\n};\n\nconst mergePersistedContext = <TContext extends JourneyJsonObject>(\n initialContext: TContext,\n persistedContext: unknown\n): TContext => mergePersistedValue(initialContext, persistedContext) as TContext;\n\nconst resolveDefaultStorage = (): JourneyStorage | null => {\n const localStorageCandidate = (globalThis as { localStorage?: Partial<JourneyStorage> })\n .localStorage;\n\n if (\n !localStorageCandidate ||\n typeof localStorageCandidate.getItem !== \"function\" ||\n typeof localStorageCandidate.setItem !== \"function\" ||\n typeof localStorageCandidate.removeItem !== \"function\"\n ) {\n return null;\n }\n\n return localStorageCandidate as JourneyStorage;\n};\n\nconst reportPersistenceError = (error: unknown, onError?: (error: unknown) => void) => {\n if (onError) {\n onError(error);\n return;\n }\n\n warnInDevelopment(\"Journey persistence encountered an error without an onError handler.\", error);\n};\n\nconst resolvePersistence = <TContext extends JourneyJsonObject, TStepId extends string>(\n options?: JourneyPersistenceOptions<TContext, TStepId>\n): ResolvedPersistence<TContext, TStepId> | null => {\n if (!options) {\n return null;\n }\n\n let storage: JourneyStorage | null | undefined = options.storage;\n if (storage == null) {\n try {\n storage = resolveDefaultStorage();\n } catch (error) {\n reportPersistenceError(error, options.onError);\n return null;\n }\n }\n\n if (!storage) {\n return null;\n }\n\n let allowList: readonly string[] | undefined;\n let blockList: readonly string[];\n\n try {\n const normalizedAllowList = normalizePersistencePaths(options.allowList, \"allowList\");\n const normalizedBlockList = normalizePersistencePaths(options.blockList, \"blockList\");\n\n allowList = normalizedAllowList ? normalizedAllowList.map((path) => path.join(\".\")) : undefined;\n blockList = normalizedBlockList ? normalizedBlockList.map((path) => path.join(\".\")) : [];\n } catch (error) {\n reportPersistenceError(error, options.onError);\n return null;\n }\n\n return {\n key: options.key,\n storage,\n version: options.version ?? 1,\n clearOnReset: options.clearOnReset ?? true,\n ...(allowList ? { allowList } : {}),\n blockList,\n serialize: options.serialize ?? JSON.stringify,\n deserialize: options.deserialize ?? JSON.parse,\n ...(options.migrate ? { migrate: options.migrate } : {}),\n ...(options.onError ? { onError: options.onError } : {})\n };\n};\n\nconst resolveVisitedFromPersistence = <TStepId extends string>(\n visitedSource: unknown,\n timeline: readonly TStepId[],\n stepIds: readonly TStepId[]\n): {\n visited: Record<TStepId, boolean>;\n needsRewrite: boolean;\n} => {\n const visitedFromTimeline = buildVisitedFromTimeline(timeline, stepIds);\n\n if (isRecord(visitedSource)) {\n let needsRewrite = false;\n const visited = { ...visitedFromTimeline };\n\n for (const stepId of stepIds) {\n const persistedValue = visitedSource[stepId];\n\n if (persistedValue === true) {\n visited[stepId] = true;\n continue;\n }\n\n if (persistedValue === false) {\n if (visitedFromTimeline[stepId]) {\n needsRewrite = true;\n }\n continue;\n }\n\n needsRewrite = true;\n }\n\n return { visited, needsRewrite };\n }\n\n if (Array.isArray(visitedSource)) {\n const visitedFromArray = buildVisitedFromTimeline(\n visitedSource.filter(\n (step): step is TStepId => typeof step === \"string\" && stepIds.includes(step as TStepId)\n ) as TStepId[],\n stepIds\n );\n const visited = Object.fromEntries(\n stepIds.map((stepId) => [stepId, visitedFromTimeline[stepId] || visitedFromArray[stepId]])\n ) as Record<TStepId, boolean>;\n\n return { visited, needsRewrite: true };\n }\n\n return { visited: visitedFromTimeline, needsRewrite: true };\n};\n\nconst coercePersistedSnapshot = <TContext extends JourneyJsonObject, TStepId extends string>(\n value: unknown,\n steps: Record<TStepId, unknown>,\n fallbackContext: TContext\n): {\n snapshot: JourneySnapshotStateBase<TContext, TStepId>;\n needsRewrite: boolean;\n} | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n let needsRewrite = false;\n\n const rawHistory = isRecord(value.history) ? value.history : null;\n if (!rawHistory) {\n needsRewrite = true;\n }\n\n const rawTimeline = rawHistory?.timeline ?? value.timeline;\n const timeline = Array.isArray(rawTimeline)\n ? (rawTimeline.filter(\n (step): step is TStepId => typeof step === \"string\" && step in steps\n ) as TStepId[])\n : [];\n\n const currentStepIdValue =\n typeof value.currentStepId === \"string\" && value.currentStepId in steps\n ? (value.currentStepId as TStepId)\n : typeof value.current === \"string\" && value.current in steps\n ? (value.current as TStepId)\n : null;\n\n if (timeline.length === 0) {\n if (!currentStepIdValue) {\n return null;\n }\n timeline.push(currentStepIdValue);\n needsRewrite = true;\n }\n\n let index = timeline.length - 1;\n const rawIndex = rawHistory?.index ?? value.index;\n if (typeof rawIndex === \"number\" && Number.isFinite(rawIndex)) {\n index = Math.max(0, Math.min(Math.trunc(rawIndex), timeline.length - 1));\n if (index !== rawIndex) {\n needsRewrite = true;\n }\n } else if (currentStepIdValue) {\n const inferredIndex = timeline.lastIndexOf(currentStepIdValue);\n if (inferredIndex >= 0) {\n index = inferredIndex;\n needsRewrite = true;\n }\n }\n\n const status = isStatusValue(value.status) ? value.status : \"idled\";\n if (!isStatusValue(value.status)) {\n needsRewrite = true;\n }\n\n const stepIds = Object.keys(steps) as TStepId[];\n const { visited, needsRewrite: visitedNeedsRewrite } = resolveVisitedFromPersistence(\n value.visited,\n timeline,\n stepIds\n );\n if (visitedNeedsRewrite) {\n needsRewrite = true;\n }\n\n return {\n snapshot: {\n currentStepId: timeline[index] as TStepId,\n history: {\n timeline,\n index\n },\n context: assertSerializableContext(\n (\"context\" in value ? value.context : fallbackContext) as TContext,\n \"Persisted journey context\"\n ),\n status,\n visited\n },\n needsRewrite\n };\n};\n\n/**\n * Creates a persistence controller for snapshots, including hydration,\n * serialization, and storage error handling.\n */\nexport const createPersistenceController = <\n TContext extends JourneyJsonObject,\n TStepId extends string\n>(args: {\n initial: TStepId;\n context: TContext;\n steps: Record<TStepId, unknown>;\n options?: JourneyPersistenceOptions<TContext, TStepId>;\n}) => {\n const { initial, context, steps, options } = args;\n const persistence = resolvePersistence(options);\n const allowList = persistence?.allowList\n ? persistence.allowList.map((path) => path.split(\".\"))\n : null;\n const blockList = persistence ? persistence.blockList.map((path) => path.split(\".\")) : [];\n\n const reportResolvedPersistenceError = (error: unknown) => {\n reportPersistenceError(error, persistence?.onError);\n };\n\n const filterPersistedContext = (value: TContext): { context: TContext; changed: boolean } => {\n if (!persistence || (allowList === null && blockList.length === 0)) {\n return { context: value, changed: false };\n }\n\n if (!isPlainObject(value)) {\n return { context: value, changed: false };\n }\n\n const filtered = filterContextValue(value, allowList, blockList);\n const filteredContext =\n filtered.included && isPlainObject(filtered.value) ? filtered.value : {};\n\n return {\n context: filteredContext as TContext,\n changed: filtered.changed\n };\n };\n\n const persistSnapshot = (snapshot: JourneySnapshot<TContext, TStepId>) => {\n if (!persistence) {\n return;\n }\n\n try {\n const { context: persistedContext } = filterPersistedContext(snapshot.context);\n const persistedState: JourneyPersistedState<TContext, TStepId> = {\n version: persistence.version,\n snapshot: {\n currentStepId: snapshot.currentStepId,\n history: {\n timeline: [...snapshot.history.timeline],\n index: snapshot.history.index\n },\n context: persistedContext,\n status: snapshot.status,\n visited: { ...snapshot.visited }\n }\n };\n persistence.storage.setItem(persistence.key, persistence.serialize(persistedState));\n } catch (error) {\n reportResolvedPersistenceError(error);\n }\n };\n\n const removePersistedSnapshot = () => {\n if (!persistence) {\n return;\n }\n\n try {\n persistence.storage.removeItem(persistence.key);\n } catch (error) {\n reportResolvedPersistenceError(error);\n }\n };\n\n const buildBaseSnapshot = () =>\n buildSnapshot([initial], 0, context, \"idled\", buildInitialAsyncState(steps));\n const hydrateSnapshot = (\n baseSnapshot: JourneySnapshot<TContext, TStepId> = buildBaseSnapshot()\n ): JourneySnapshot<TContext, TStepId> => {\n const resolvedBaseSnapshot = buildSnapshot(\n baseSnapshot.history.timeline,\n baseSnapshot.history.index,\n baseSnapshot.context,\n baseSnapshot.status,\n baseSnapshot.async,\n baseSnapshot.visited\n );\n if (!persistence) {\n return resolvedBaseSnapshot;\n }\n\n try {\n const rawPersisted = persistence.storage.getItem(persistence.key);\n if (!rawPersisted) {\n return resolvedBaseSnapshot;\n }\n\n const parsed = persistence.deserialize(rawPersisted);\n if (!isRecord(parsed)) {\n return resolvedBaseSnapshot;\n }\n\n const persistedVersion = parsed.version;\n if (typeof persistedVersion !== \"number\") {\n return resolvedBaseSnapshot;\n }\n\n let persistedSnapshot: JourneySnapshotStateBase<TContext, TStepId> | null = null;\n let shouldRewritePersisted = false;\n\n if (persistedVersion === persistence.version) {\n const coerced = coercePersistedSnapshot(parsed.snapshot, steps, context);\n persistedSnapshot = coerced?.snapshot ?? null;\n shouldRewritePersisted = Boolean(coerced?.needsRewrite);\n } else if (persistence.migrate) {\n const migrated = persistence.migrate(parsed.snapshot, persistedVersion);\n const coerced = coercePersistedSnapshot(migrated, steps, context);\n persistedSnapshot = coerced?.snapshot ?? null;\n shouldRewritePersisted = persistedSnapshot !== null;\n\n if (!persistedSnapshot) {\n reportResolvedPersistenceError(\n new Error(\n \"Journey persistence migrate() returned data that could not be coerced into a valid snapshot. \" +\n `Falling back to the initial snapshot (persisted version: ${persistedVersion}, current version: ${persistence.version}).`\n )\n );\n }\n }\n\n if (!persistedSnapshot) {\n return resolvedBaseSnapshot;\n }\n\n const { context: filteredContext, changed: contextWasFiltered } = filterPersistedContext(\n persistedSnapshot.context\n );\n const hydratedContext = mergePersistedContext(resolvedBaseSnapshot.context, filteredContext);\n\n const hydratedSnapshot = buildSnapshot(\n persistedSnapshot.history.timeline,\n persistedSnapshot.history.index,\n assertSerializableContext(hydratedContext, \"Hydrated journey context\"),\n persistedSnapshot.status === \"running\" ? \"idled\" : persistedSnapshot.status,\n resolvedBaseSnapshot.async,\n persistedSnapshot.visited\n );\n\n if (shouldRewritePersisted || contextWasFiltered) {\n persistSnapshot(hydratedSnapshot);\n }\n\n return hydratedSnapshot;\n } catch (error) {\n reportResolvedPersistenceError(error);\n return resolvedBaseSnapshot;\n }\n };\n\n return {\n clearOnReset: persistence?.clearOnReset ?? true,\n hydrateSnapshot,\n persistSnapshot,\n removePersistedSnapshot\n };\n};\n", "import { createPersistenceController } from \"./controller\";\n\nimport type {\n JourneyJsonObject,\n JourneyMachinePlugin,\n JourneyPersistenceOptions\n} from \"../../types\";\n\n/**\n * Creates a machine plugin that hydrates snapshots from storage and persists\n * state changes without pulling persistence code into the base entrypoint.\n */\nexport const createPersistencePlugin = <TContext extends JourneyJsonObject, TStepId extends string>(\n options: JourneyPersistenceOptions<TContext, TStepId>\n) => {\n const setup = (({ resolvedJourney }) => {\n const controller = createPersistenceController({\n initial: resolvedJourney.initial as unknown as TStepId,\n context: resolvedJourney.context as unknown as TContext,\n steps: resolvedJourney.steps as unknown as Record<TStepId, unknown>,\n options\n });\n\n return {\n hydrateSnapshot: (snapshot) => controller.hydrateSnapshot(snapshot as never),\n onSnapshotChange: ({ snapshot, reason }) => {\n if (reason === \"async\") {\n return;\n }\n\n if (reason === \"reset\" && controller.clearOnReset) {\n controller.removePersistedSnapshot();\n return;\n }\n\n controller.persistSnapshot(snapshot as never);\n }\n };\n }) as JourneyMachinePlugin[\"setup\"];\n\n return {\n name: \"persistence\",\n setup\n } satisfies JourneyMachinePlugin;\n};\n\nexport { createPersistenceController };\nexport type { JourneyPersistedState, JourneyPersistenceOptions, JourneyStorage } from \"../../types\";\n"],
5
+ "mappings": "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",
6
+ "names": ["isPlainObject", "value", "cloneSerializableValue", "entry", "key", "entryValue", "describeValue", "constructorName", "assertSerializableValue", "label", "state", "path", "pathLabel", "index", "assertSerializableContext", "context", "cloneContext", "unique", "items", "normalizeVisited", "visited", "stepIds", "stepId", "buildVisitedFromTimeline", "timeline", "resolvedStepIds", "isDevelopmentEnvironment", "diagnosticGlobal", "nodeEnv", "warnInDevelopment", "message", "detail", "buildIdleStepAsyncState", "buildInitialAsyncState", "steps", "stepId", "buildSnapshot", "timeline", "index", "context", "status", "asyncState", "visited", "safeIndex", "currentStepId", "stepIds", "unique", "cloneContext", "normalizeVisited", "buildVisitedFromTimeline", "cloneSerializableValue", "isRecord", "value", "isStatusValue", "normalizePersistencePath", "path", "optionName", "trimmedPath", "segments", "segment", "normalizePersistencePaths", "paths", "normalized", "resolvedPath", "pathStartsWith", "prefix", "index", "pathHasAncestorMatch", "candidates", "candidate", "pathHasDescendantMatch", "filterContextValue", "allowList", "blockList", "allowAll", "pathIsAllowed", "pathHasAllowedDescendants", "isPlainObject", "changed", "nextValue", "key", "childValue", "result", "mergePersistedValue", "initialValue", "persistedValue", "mergedEntries", "mergePersistedContext", "initialContext", "persistedContext", "resolveDefaultStorage", "localStorageCandidate", "reportPersistenceError", "error", "onError", "warnInDevelopment", "resolvePersistence", "options", "storage", "normalizedAllowList", "normalizedBlockList", "resolveVisitedFromPersistence", "visitedSource", "timeline", "stepIds", "visitedFromTimeline", "buildVisitedFromTimeline", "needsRewrite", "visited", "stepId", "visitedFromArray", "step", "coercePersistedSnapshot", "steps", "fallbackContext", "rawHistory", "rawTimeline", "currentStepIdValue", "rawIndex", "inferredIndex", "status", "visitedNeedsRewrite", "assertSerializableContext", "createPersistenceController", "args", "initial", "context", "persistence", "reportResolvedPersistenceError", "filterPersistedContext", "filtered", "persistSnapshot", "snapshot", "persistedState", "removePersistedSnapshot", "buildBaseSnapshot", "buildSnapshot", "buildInitialAsyncState", "hydrateSnapshot", "baseSnapshot", "resolvedBaseSnapshot", "rawPersisted", "parsed", "persistedVersion", "persistedSnapshot", "shouldRewritePersisted", "coerced", "migrated", "filteredContext", "contextWasFiltered", "hydratedContext", "hydratedSnapshot", "createPersistencePlugin", "options", "resolvedJourney", "controller", "createPersistenceController", "snapshot", "reason"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var a=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var l=(n,e)=>{for(var t in e)a(n,t,{get:e[t],enumerable:!0})},x=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of f(e))!R.call(n,r)&&r!==t&&a(n,r,{get:()=>e[r],enumerable:!(s=S(e,r))||s.enumerable});return n};var b=n=>x(a({},"__esModule",{value:!0}),n);var T={};l(T,{createReplayPlugin:()=>E,serializeReplaySession:()=>g});module.exports=b(T);var h=n=>typeof n!="number"||!Number.isFinite(n)?500:Math.max(1,Math.trunc(n)),u=(n,e=new WeakSet)=>{if(n===null||typeof n=="string"||typeof n=="number"||typeof n=="boolean")return n;if(typeof n=="bigint")return n.toString();if(typeof n>"u")return null;if(typeof n=="function"||typeof n=="symbol")return`[unsupported:${typeof n}]`;if(n instanceof Error)return{name:n.name,message:n.message,...n.stack?{stack:n.stack}:{}};if(n instanceof Date)return n.toISOString();if(Array.isArray(n))return n.map(t=>u(t,e));if(typeof n=="object"){if(e.has(n))return"[circular]";e.add(n);let t={};for(let[s,r]of Object.entries(n))t[s]=u(r,e);return e.delete(n),t}return String(n)},g=(n,e)=>JSON.stringify(u(n),null,e?.pretty?2:void 0),E=(n={})=>{let e=n.captureEvents??!0,t=n.captureSnapshots??!0,s=h(n.maxEntries),r=null,p=[],y=!1,c,d=o=>{p.length>=s&&(p.shift(),y=!0),p.push(o)},J=()=>({version:1,initialSnapshot:r,entries:[...p],truncated:y});return{name:"replay",setup:()=>({hydrateSnapshot:o=>(r=o,o),onSnapshotChange:({snapshot:o,reason:i})=>{t&&d({kind:"snapshot",timestamp:Date.now(),reason:i,snapshot:o})},augmentMachine:({machine:o})=>(e&&(c=o.subscribeEvent(i=>{d({kind:"event",timestamp:i.timestamp,event:i})})),{getReplaySession:()=>J(),clearReplaySession:()=>{r=o.getSnapshot(),p.length=0,y=!1},exportReplaySession:i=>g(J(),i)}),dispose:()=>{c?.()}})}};0&&(module.exports={createReplayPlugin,serializeReplaySession});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/plugins/replay/index.ts"],
4
+ "sourcesContent": ["import type {\n JourneyJsonObject,\n JourneyMachine,\n JourneyMachinePlugin,\n JourneyObservationEvent,\n JourneyReplayEntry,\n JourneyReplayExportOptions,\n JourneyReplayPluginOptions,\n JourneyReplaySession,\n JourneySnapshot\n} from \"../../types\";\n\nconst normalizeMaxEntries = (value: number | undefined) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n return 500;\n }\n\n return Math.max(1, Math.trunc(value));\n};\n\nconst toSerializable = (value: unknown, seen = new WeakSet<object>()): unknown => {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (typeof value === \"undefined\") {\n return null;\n }\n\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n return `[unsupported:${typeof value}]`;\n }\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\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => toSerializable(item, seen));\n }\n\n if (typeof value === \"object\") {\n if (seen.has(value)) {\n return \"[circular]\";\n }\n\n seen.add(value);\n const output: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n output[key] = toSerializable(item, seen);\n }\n seen.delete(value);\n return output;\n }\n\n return String(value);\n};\n\n/** Serializes a replay session into a JSON string safe for logging or export. */\nexport const serializeReplaySession = <\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>\n>(\n session: JourneyReplaySession<TContext, TStepId, TEventMap>,\n options?: JourneyReplayExportOptions\n) => JSON.stringify(toSerializable(session), null, options?.pretty ? 2 : undefined);\n\nexport type JourneyReplayMachineExtension<\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>\n> = {\n getReplaySession: () => JourneyReplaySession<TContext, TStepId, TEventMap>;\n clearReplaySession: () => void;\n exportReplaySession: (options?: JourneyReplayExportOptions) => string;\n};\n\nexport type JourneyReplayMachine<\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>,\n TStepMeta = unknown,\n THandlers extends Record<string, unknown> = Record<never, never>\n> = JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers> &\n JourneyReplayMachineExtension<TContext, TStepId, TEventMap>;\n\n/** Creates a plugin that records snapshot and lifecycle activity into a replay session. */\nexport const createReplayPlugin = (options: JourneyReplayPluginOptions = {}) => {\n const captureEvents = options.captureEvents ?? true;\n const captureSnapshots = options.captureSnapshots ?? true;\n const maxEntries = normalizeMaxEntries(options.maxEntries);\n\n let initialSnapshot: JourneySnapshot<JourneyJsonObject, string> | null = null;\n const entries: JourneyReplayEntry<JourneyJsonObject, string, Record<string, unknown>>[] = [];\n let truncated = false;\n let unsubscribeEvents: (() => void) | undefined;\n\n const pushEntry = (\n entry: JourneyReplayEntry<JourneyJsonObject, string, Record<string, unknown>>\n ) => {\n if (entries.length >= maxEntries) {\n entries.shift();\n truncated = true;\n }\n\n entries.push(entry);\n };\n\n const buildSession = () =>\n ({\n version: 1 as const,\n initialSnapshot,\n entries: [...entries],\n truncated\n }) satisfies JourneyReplaySession<JourneyJsonObject, string, Record<string, unknown>>;\n\n return {\n name: \"replay\",\n setup: () => ({\n hydrateSnapshot: (snapshot) => {\n initialSnapshot = snapshot as JourneySnapshot<JourneyJsonObject, string>;\n return snapshot;\n },\n onSnapshotChange: ({ snapshot, reason }) => {\n if (!captureSnapshots) {\n return;\n }\n\n pushEntry({\n kind: \"snapshot\",\n timestamp: Date.now(),\n reason,\n snapshot: snapshot as JourneySnapshot<JourneyJsonObject, string>\n });\n },\n augmentMachine: ({ machine }) => {\n if (captureEvents) {\n unsubscribeEvents = machine.subscribeEvent((event) => {\n pushEntry({\n kind: \"event\",\n timestamp: event.timestamp,\n event: event as JourneyObservationEvent<string, Record<string, unknown>>\n });\n });\n }\n\n return {\n getReplaySession: () =>\n buildSession() as JourneyReplaySession<\n JourneyJsonObject,\n string,\n Record<string, unknown>\n >,\n clearReplaySession: () => {\n initialSnapshot = machine.getSnapshot() as JourneySnapshot<JourneyJsonObject, string>;\n entries.length = 0;\n truncated = false;\n },\n exportReplaySession: (exportOptions?: JourneyReplayExportOptions) =>\n serializeReplaySession(buildSession(), exportOptions)\n };\n },\n dispose: () => {\n unsubscribeEvents?.();\n }\n })\n } satisfies JourneyMachinePlugin;\n};\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,wBAAAE,EAAA,2BAAAC,IAAA,eAAAC,EAAAJ,GAYA,IAAMK,EAAuBC,GACvB,OAAOA,GAAU,UAAY,CAAC,OAAO,SAASA,CAAK,EAC9C,IAGF,KAAK,IAAI,EAAG,KAAK,MAAMA,CAAK,CAAC,EAGhCC,EAAiB,CAACD,EAAgBE,EAAO,IAAI,UAA+B,CAChF,GACEF,IAAU,MACV,OAAOA,GAAU,UACjB,OAAOA,GAAU,UACjB,OAAOA,GAAU,UAEjB,OAAOA,EAGT,GAAI,OAAOA,GAAU,SACnB,OAAOA,EAAM,SAAS,EAGxB,GAAI,OAAOA,EAAU,IACnB,OAAO,KAGT,GAAI,OAAOA,GAAU,YAAc,OAAOA,GAAU,SAClD,MAAO,gBAAgB,OAAOA,CAAK,IAGrC,GAAIA,aAAiB,MACnB,MAAO,CACL,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,GAAIA,EAAM,MAAQ,CAAE,MAAOA,EAAM,KAAM,EAAI,CAAC,CAC9C,EAGF,GAAIA,aAAiB,KACnB,OAAOA,EAAM,YAAY,EAG3B,GAAI,MAAM,QAAQA,CAAK,EACrB,OAAOA,EAAM,IAAKG,GAASF,EAAeE,EAAMD,CAAI,CAAC,EAGvD,GAAI,OAAOF,GAAU,SAAU,CAC7B,GAAIE,EAAK,IAAIF,CAAK,EAChB,MAAO,aAGTE,EAAK,IAAIF,CAAK,EACd,IAAMI,EAAkC,CAAC,EACzC,OAAW,CAACC,EAAKF,CAAI,IAAK,OAAO,QAAQH,CAAK,EAC5CI,EAAOC,CAAG,EAAIJ,EAAeE,EAAMD,CAAI,EAEzC,OAAAA,EAAK,OAAOF,CAAK,EACVI,CACT,CAEA,OAAO,OAAOJ,CAAK,CACrB,EAGaH,EAAyB,CAKpCS,EACAC,IACG,KAAK,UAAUN,EAAeK,CAAO,EAAG,KAAMC,GAAS,OAAS,EAAI,MAAS,EAsBrEX,EAAqB,CAACW,EAAsC,CAAC,IAAM,CAC9E,IAAMC,EAAgBD,EAAQ,eAAiB,GACzCE,EAAmBF,EAAQ,kBAAoB,GAC/CG,EAAaX,EAAoBQ,EAAQ,UAAU,EAErDI,EAAqE,KACnEC,EAAoF,CAAC,EACvFC,EAAY,GACZC,EAEEC,EACJC,GACG,CACCJ,EAAQ,QAAUF,IACpBE,EAAQ,MAAM,EACdC,EAAY,IAGdD,EAAQ,KAAKI,CAAK,CACpB,EAEMC,EAAe,KAClB,CACC,QAAS,EACT,gBAAAN,EACA,QAAS,CAAC,GAAGC,CAAO,EACpB,UAAAC,CACF,GAEF,MAAO,CACL,KAAM,SACN,MAAO,KAAO,CACZ,gBAAkBK,IAChBP,EAAkBO,EACXA,GAET,iBAAkB,CAAC,CAAE,SAAAA,EAAU,OAAAC,CAAO,IAAM,CACrCV,GAILM,EAAU,CACR,KAAM,WACN,UAAW,KAAK,IAAI,EACpB,OAAAI,EACA,SAAUD,CACZ,CAAC,CACH,EACA,eAAgB,CAAC,CAAE,QAAAE,CAAQ,KACrBZ,IACFM,EAAoBM,EAAQ,eAAgBC,GAAU,CACpDN,EAAU,CACR,KAAM,QACN,UAAWM,EAAM,UACjB,MAAOA,CACT,CAAC,CACH,CAAC,GAGI,CACL,iBAAkB,IAChBJ,EAAa,EAKf,mBAAoB,IAAM,CACxBN,EAAkBS,EAAQ,YAAY,EACtCR,EAAQ,OAAS,EACjBC,EAAY,EACd,EACA,oBAAsBS,GACpBzB,EAAuBoB,EAAa,EAAGK,CAAa,CACxD,GAEF,QAAS,IAAM,CACbR,IAAoB,CACtB,CACF,EACF,CACF",
6
+ "names": ["replay_exports", "__export", "createReplayPlugin", "serializeReplaySession", "__toCommonJS", "normalizeMaxEntries", "value", "toSerializable", "seen", "item", "output", "key", "session", "options", "captureEvents", "captureSnapshots", "maxEntries", "initialSnapshot", "entries", "truncated", "unsubscribeEvents", "pushEntry", "entry", "buildSession", "snapshot", "reason", "machine", "event", "exportOptions"]
7
+ }
@@ -0,0 +1,27 @@
1
+ import type { JourneyJsonObject, JourneyMachine, JourneyReplayExportOptions, JourneyReplayPluginOptions, JourneyReplaySession, JourneySnapshot } from "../../types";
2
+ /** Serializes a replay session into a JSON string safe for logging or export. */
3
+ export declare const serializeReplaySession: <TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>>(session: JourneyReplaySession<TContext, TStepId, TEventMap>, options?: JourneyReplayExportOptions) => string;
4
+ export type JourneyReplayMachineExtension<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>> = {
5
+ getReplaySession: () => JourneyReplaySession<TContext, TStepId, TEventMap>;
6
+ clearReplaySession: () => void;
7
+ exportReplaySession: (options?: JourneyReplayExportOptions) => string;
8
+ };
9
+ export type JourneyReplayMachine<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>> = JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers> & JourneyReplayMachineExtension<TContext, TStepId, TEventMap>;
10
+ /** Creates a plugin that records snapshot and lifecycle activity into a replay session. */
11
+ export declare const createReplayPlugin: (options?: JourneyReplayPluginOptions) => {
12
+ name: string;
13
+ setup: <TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>() => {
14
+ hydrateSnapshot: (snapshot: JourneySnapshot<TContext, TStepId>) => JourneySnapshot<TContext, TStepId>;
15
+ onSnapshotChange: ({ snapshot, reason }: import("../../types").JourneyMachinePluginSnapshotChange<TContext, TStepId>) => void;
16
+ augmentMachine: ({ machine }: {
17
+ machine: JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
18
+ journey: import("../..").JourneyDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
19
+ resolvedJourney: import("../../types").JourneyResolvedDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
20
+ }) => {
21
+ getReplaySession: () => JourneyReplaySession<JourneyJsonObject, string, Record<string, unknown>>;
22
+ clearReplaySession: () => void;
23
+ exportReplaySession: (exportOptions?: JourneyReplayExportOptions) => string;
24
+ };
25
+ dispose: () => void;
26
+ };
27
+ };
@@ -0,0 +1,27 @@
1
+ import type { JourneyJsonObject, JourneyMachine, JourneyReplayExportOptions, JourneyReplayPluginOptions, JourneyReplaySession, JourneySnapshot } from "../../types";
2
+ /** Serializes a replay session into a JSON string safe for logging or export. */
3
+ export declare const serializeReplaySession: <TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>>(session: JourneyReplaySession<TContext, TStepId, TEventMap>, options?: JourneyReplayExportOptions) => string;
4
+ export type JourneyReplayMachineExtension<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>> = {
5
+ getReplaySession: () => JourneyReplaySession<TContext, TStepId, TEventMap>;
6
+ clearReplaySession: () => void;
7
+ exportReplaySession: (options?: JourneyReplayExportOptions) => string;
8
+ };
9
+ export type JourneyReplayMachine<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>> = JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers> & JourneyReplayMachineExtension<TContext, TStepId, TEventMap>;
10
+ /** Creates a plugin that records snapshot and lifecycle activity into a replay session. */
11
+ export declare const createReplayPlugin: (options?: JourneyReplayPluginOptions) => {
12
+ name: string;
13
+ setup: <TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown, THandlers extends Record<string, unknown> = Record<never, never>>() => {
14
+ hydrateSnapshot: (snapshot: JourneySnapshot<TContext, TStepId>) => JourneySnapshot<TContext, TStepId>;
15
+ onSnapshotChange: ({ snapshot, reason }: import("../../types").JourneyMachinePluginSnapshotChange<TContext, TStepId>) => void;
16
+ augmentMachine: ({ machine }: {
17
+ machine: JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
18
+ journey: import("../..").JourneyDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
19
+ resolvedJourney: import("../../types").JourneyResolvedDefinition<TContext, TStepId, TEventMap, TStepMeta, THandlers>;
20
+ }) => {
21
+ getReplaySession: () => JourneyReplaySession<JourneyJsonObject, string, Record<string, unknown>>;
22
+ clearReplaySession: () => void;
23
+ exportReplaySession: (exportOptions?: JourneyReplayExportOptions) => string;
24
+ };
25
+ dispose: () => void;
26
+ };
27
+ };
@@ -0,0 +1,2 @@
1
+ var J=n=>typeof n!="number"||!Number.isFinite(n)?500:Math.max(1,Math.trunc(n)),a=(n,e=new WeakSet)=>{if(n===null||typeof n=="string"||typeof n=="number"||typeof n=="boolean")return n;if(typeof n=="bigint")return n.toString();if(typeof n>"u")return null;if(typeof n=="function"||typeof n=="symbol")return`[unsupported:${typeof n}]`;if(n instanceof Error)return{name:n.name,message:n.message,...n.stack?{stack:n.stack}:{}};if(n instanceof Date)return n.toISOString();if(Array.isArray(n))return n.map(r=>a(r,e));if(typeof n=="object"){if(e.has(n))return"[circular]";e.add(n);let r={};for(let[p,s]of Object.entries(n))r[p]=a(s,e);return e.delete(n),r}return String(n)},g=(n,e)=>JSON.stringify(a(n),null,e?.pretty?2:void 0),S=(n={})=>{let e=n.captureEvents??!0,r=n.captureSnapshots??!0,p=J(n.maxEntries),s=null,i=[],y=!1,u,c=t=>{i.length>=p&&(i.shift(),y=!0),i.push(t)},d=()=>({version:1,initialSnapshot:s,entries:[...i],truncated:y});return{name:"replay",setup:()=>({hydrateSnapshot:t=>(s=t,t),onSnapshotChange:({snapshot:t,reason:o})=>{r&&c({kind:"snapshot",timestamp:Date.now(),reason:o,snapshot:t})},augmentMachine:({machine:t})=>(e&&(u=t.subscribeEvent(o=>{c({kind:"event",timestamp:o.timestamp,event:o})})),{getReplaySession:()=>d(),clearReplaySession:()=>{s=t.getSnapshot(),i.length=0,y=!1},exportReplaySession:o=>g(d(),o)}),dispose:()=>{u?.()}})}};export{S as createReplayPlugin,g as serializeReplaySession};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/plugins/replay/index.ts"],
4
+ "sourcesContent": ["import type {\n JourneyJsonObject,\n JourneyMachine,\n JourneyMachinePlugin,\n JourneyObservationEvent,\n JourneyReplayEntry,\n JourneyReplayExportOptions,\n JourneyReplayPluginOptions,\n JourneyReplaySession,\n JourneySnapshot\n} from \"../../types\";\n\nconst normalizeMaxEntries = (value: number | undefined) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n return 500;\n }\n\n return Math.max(1, Math.trunc(value));\n};\n\nconst toSerializable = (value: unknown, seen = new WeakSet<object>()): unknown => {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (typeof value === \"undefined\") {\n return null;\n }\n\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n return `[unsupported:${typeof value}]`;\n }\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\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => toSerializable(item, seen));\n }\n\n if (typeof value === \"object\") {\n if (seen.has(value)) {\n return \"[circular]\";\n }\n\n seen.add(value);\n const output: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n output[key] = toSerializable(item, seen);\n }\n seen.delete(value);\n return output;\n }\n\n return String(value);\n};\n\n/** Serializes a replay session into a JSON string safe for logging or export. */\nexport const serializeReplaySession = <\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>\n>(\n session: JourneyReplaySession<TContext, TStepId, TEventMap>,\n options?: JourneyReplayExportOptions\n) => JSON.stringify(toSerializable(session), null, options?.pretty ? 2 : undefined);\n\nexport type JourneyReplayMachineExtension<\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>\n> = {\n getReplaySession: () => JourneyReplaySession<TContext, TStepId, TEventMap>;\n clearReplaySession: () => void;\n exportReplaySession: (options?: JourneyReplayExportOptions) => string;\n};\n\nexport type JourneyReplayMachine<\n TContext extends JourneyJsonObject,\n TStepId extends string,\n TEventMap extends Record<string, unknown> = Record<never, never>,\n TStepMeta = unknown,\n THandlers extends Record<string, unknown> = Record<never, never>\n> = JourneyMachine<TContext, TStepId, TEventMap, TStepMeta, THandlers> &\n JourneyReplayMachineExtension<TContext, TStepId, TEventMap>;\n\n/** Creates a plugin that records snapshot and lifecycle activity into a replay session. */\nexport const createReplayPlugin = (options: JourneyReplayPluginOptions = {}) => {\n const captureEvents = options.captureEvents ?? true;\n const captureSnapshots = options.captureSnapshots ?? true;\n const maxEntries = normalizeMaxEntries(options.maxEntries);\n\n let initialSnapshot: JourneySnapshot<JourneyJsonObject, string> | null = null;\n const entries: JourneyReplayEntry<JourneyJsonObject, string, Record<string, unknown>>[] = [];\n let truncated = false;\n let unsubscribeEvents: (() => void) | undefined;\n\n const pushEntry = (\n entry: JourneyReplayEntry<JourneyJsonObject, string, Record<string, unknown>>\n ) => {\n if (entries.length >= maxEntries) {\n entries.shift();\n truncated = true;\n }\n\n entries.push(entry);\n };\n\n const buildSession = () =>\n ({\n version: 1 as const,\n initialSnapshot,\n entries: [...entries],\n truncated\n }) satisfies JourneyReplaySession<JourneyJsonObject, string, Record<string, unknown>>;\n\n return {\n name: \"replay\",\n setup: () => ({\n hydrateSnapshot: (snapshot) => {\n initialSnapshot = snapshot as JourneySnapshot<JourneyJsonObject, string>;\n return snapshot;\n },\n onSnapshotChange: ({ snapshot, reason }) => {\n if (!captureSnapshots) {\n return;\n }\n\n pushEntry({\n kind: \"snapshot\",\n timestamp: Date.now(),\n reason,\n snapshot: snapshot as JourneySnapshot<JourneyJsonObject, string>\n });\n },\n augmentMachine: ({ machine }) => {\n if (captureEvents) {\n unsubscribeEvents = machine.subscribeEvent((event) => {\n pushEntry({\n kind: \"event\",\n timestamp: event.timestamp,\n event: event as JourneyObservationEvent<string, Record<string, unknown>>\n });\n });\n }\n\n return {\n getReplaySession: () =>\n buildSession() as JourneyReplaySession<\n JourneyJsonObject,\n string,\n Record<string, unknown>\n >,\n clearReplaySession: () => {\n initialSnapshot = machine.getSnapshot() as JourneySnapshot<JourneyJsonObject, string>;\n entries.length = 0;\n truncated = false;\n },\n exportReplaySession: (exportOptions?: JourneyReplayExportOptions) =>\n serializeReplaySession(buildSession(), exportOptions)\n };\n },\n dispose: () => {\n unsubscribeEvents?.();\n }\n })\n } satisfies JourneyMachinePlugin;\n};\n"],
5
+ "mappings": "AAYA,IAAMA,EAAuBC,GACvB,OAAOA,GAAU,UAAY,CAAC,OAAO,SAASA,CAAK,EAC9C,IAGF,KAAK,IAAI,EAAG,KAAK,MAAMA,CAAK,CAAC,EAGhCC,EAAiB,CAACD,EAAgBE,EAAO,IAAI,UAA+B,CAChF,GACEF,IAAU,MACV,OAAOA,GAAU,UACjB,OAAOA,GAAU,UACjB,OAAOA,GAAU,UAEjB,OAAOA,EAGT,GAAI,OAAOA,GAAU,SACnB,OAAOA,EAAM,SAAS,EAGxB,GAAI,OAAOA,EAAU,IACnB,OAAO,KAGT,GAAI,OAAOA,GAAU,YAAc,OAAOA,GAAU,SAClD,MAAO,gBAAgB,OAAOA,CAAK,IAGrC,GAAIA,aAAiB,MACnB,MAAO,CACL,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,GAAIA,EAAM,MAAQ,CAAE,MAAOA,EAAM,KAAM,EAAI,CAAC,CAC9C,EAGF,GAAIA,aAAiB,KACnB,OAAOA,EAAM,YAAY,EAG3B,GAAI,MAAM,QAAQA,CAAK,EACrB,OAAOA,EAAM,IAAKG,GAASF,EAAeE,EAAMD,CAAI,CAAC,EAGvD,GAAI,OAAOF,GAAU,SAAU,CAC7B,GAAIE,EAAK,IAAIF,CAAK,EAChB,MAAO,aAGTE,EAAK,IAAIF,CAAK,EACd,IAAMI,EAAkC,CAAC,EACzC,OAAW,CAACC,EAAKF,CAAI,IAAK,OAAO,QAAQH,CAAK,EAC5CI,EAAOC,CAAG,EAAIJ,EAAeE,EAAMD,CAAI,EAEzC,OAAAA,EAAK,OAAOF,CAAK,EACVI,CACT,CAEA,OAAO,OAAOJ,CAAK,CACrB,EAGaM,EAAyB,CAKpCC,EACAC,IACG,KAAK,UAAUP,EAAeM,CAAO,EAAG,KAAMC,GAAS,OAAS,EAAI,MAAS,EAsBrEC,EAAqB,CAACD,EAAsC,CAAC,IAAM,CAC9E,IAAME,EAAgBF,EAAQ,eAAiB,GACzCG,EAAmBH,EAAQ,kBAAoB,GAC/CI,EAAab,EAAoBS,EAAQ,UAAU,EAErDK,EAAqE,KACnEC,EAAoF,CAAC,EACvFC,EAAY,GACZC,EAEEC,EACJC,GACG,CACCJ,EAAQ,QAAUF,IACpBE,EAAQ,MAAM,EACdC,EAAY,IAGdD,EAAQ,KAAKI,CAAK,CACpB,EAEMC,EAAe,KAClB,CACC,QAAS,EACT,gBAAAN,EACA,QAAS,CAAC,GAAGC,CAAO,EACpB,UAAAC,CACF,GAEF,MAAO,CACL,KAAM,SACN,MAAO,KAAO,CACZ,gBAAkBK,IAChBP,EAAkBO,EACXA,GAET,iBAAkB,CAAC,CAAE,SAAAA,EAAU,OAAAC,CAAO,IAAM,CACrCV,GAILM,EAAU,CACR,KAAM,WACN,UAAW,KAAK,IAAI,EACpB,OAAAI,EACA,SAAUD,CACZ,CAAC,CACH,EACA,eAAgB,CAAC,CAAE,QAAAE,CAAQ,KACrBZ,IACFM,EAAoBM,EAAQ,eAAgBC,GAAU,CACpDN,EAAU,CACR,KAAM,QACN,UAAWM,EAAM,UACjB,MAAOA,CACT,CAAC,CACH,CAAC,GAGI,CACL,iBAAkB,IAChBJ,EAAa,EAKf,mBAAoB,IAAM,CACxBN,EAAkBS,EAAQ,YAAY,EACtCR,EAAQ,OAAS,EACjBC,EAAY,EACd,EACA,oBAAsBS,GACpBlB,EAAuBa,EAAa,EAAGK,CAAa,CACxD,GAEF,QAAS,IAAM,CACbR,IAAoB,CACtB,CACF,EACF,CACF",
6
+ "names": ["normalizeMaxEntries", "value", "toSerializable", "seen", "item", "output", "key", "serializeReplaySession", "session", "options", "createReplayPlugin", "captureEvents", "captureSnapshots", "maxEntries", "initialSnapshot", "entries", "truncated", "unsubscribeEvents", "pushEntry", "entry", "buildSession", "snapshot", "reason", "machine", "event", "exportOptions"]
7
+ }
@@ -0,0 +1,33 @@
1
+ import type { JourneyObservationEvent, JourneyTransitionSuccessObservationEvent } from "./observation.types";
2
+ import type { JourneyJsonObject, JourneyTerminal } from "./journey.types";
3
+ /** Standard analytics event names emitted by the analytics plugin. */
4
+ export type JourneyAnalyticsEventName = "journey_started" | "step_viewed" | "step_exited" | "transition_started" | "transition_succeeded" | "transition_failed" | "journey_completed" | "journey_terminated" | "navigation_previous" | "navigation_last_visited" | (string & {});
5
+ /** Analytics payload emitted by the analytics plugin. */
6
+ export type JourneyAnalyticsEventPayload<TContext extends JourneyJsonObject, TStepId extends string, TStepMeta> = Record<string, unknown> & {
7
+ context?: TContext;
8
+ stepId?: TStepId;
9
+ stepMeta?: TStepMeta;
10
+ from?: TStepId;
11
+ to?: TStepId | JourneyTerminal;
12
+ fromStepMeta?: TStepMeta;
13
+ toStepMeta?: TStepMeta;
14
+ durationMs?: number;
15
+ dwellMs?: number;
16
+ eventType?: string;
17
+ transitionId?: JourneyTransitionSuccessObservationEvent<TStepId>["transitionId"];
18
+ label?: JourneyTransitionSuccessObservationEvent<TStepId>["label"];
19
+ };
20
+ /** Event envelope passed to analytics trackers. */
21
+ export type JourneyAnalyticsTrackedEvent<TContext extends JourneyJsonObject, TStepId extends string, TStepMeta> = {
22
+ name: JourneyAnalyticsEventName;
23
+ timestamp: number;
24
+ machineId?: string;
25
+ payload: JourneyAnalyticsEventPayload<TContext, TStepId, TStepMeta>;
26
+ };
27
+ /** Options for the analytics plugin. */
28
+ export type JourneyAnalyticsPluginOptions<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown> = {
29
+ track: (event: JourneyAnalyticsTrackedEvent<TContext, TStepId, TStepMeta>) => void;
30
+ machineId?: string;
31
+ includeStepMeta?: boolean;
32
+ onError?: (error: unknown, event?: JourneyObservationEvent<TStepId, TEventMap> | JourneyAnalyticsTrackedEvent<TContext, TStepId, TStepMeta>) => void;
33
+ };
@@ -0,0 +1,33 @@
1
+ import type { JourneyObservationEvent, JourneyTransitionSuccessObservationEvent } from "./observation.types";
2
+ import type { JourneyJsonObject, JourneyTerminal } from "./journey.types";
3
+ /** Standard analytics event names emitted by the analytics plugin. */
4
+ export type JourneyAnalyticsEventName = "journey_started" | "step_viewed" | "step_exited" | "transition_started" | "transition_succeeded" | "transition_failed" | "journey_completed" | "journey_terminated" | "navigation_previous" | "navigation_last_visited" | (string & {});
5
+ /** Analytics payload emitted by the analytics plugin. */
6
+ export type JourneyAnalyticsEventPayload<TContext extends JourneyJsonObject, TStepId extends string, TStepMeta> = Record<string, unknown> & {
7
+ context?: TContext;
8
+ stepId?: TStepId;
9
+ stepMeta?: TStepMeta;
10
+ from?: TStepId;
11
+ to?: TStepId | JourneyTerminal;
12
+ fromStepMeta?: TStepMeta;
13
+ toStepMeta?: TStepMeta;
14
+ durationMs?: number;
15
+ dwellMs?: number;
16
+ eventType?: string;
17
+ transitionId?: JourneyTransitionSuccessObservationEvent<TStepId>["transitionId"];
18
+ label?: JourneyTransitionSuccessObservationEvent<TStepId>["label"];
19
+ };
20
+ /** Event envelope passed to analytics trackers. */
21
+ export type JourneyAnalyticsTrackedEvent<TContext extends JourneyJsonObject, TStepId extends string, TStepMeta> = {
22
+ name: JourneyAnalyticsEventName;
23
+ timestamp: number;
24
+ machineId?: string;
25
+ payload: JourneyAnalyticsEventPayload<TContext, TStepId, TStepMeta>;
26
+ };
27
+ /** Options for the analytics plugin. */
28
+ export type JourneyAnalyticsPluginOptions<TContext extends JourneyJsonObject, TStepId extends string, TEventMap extends Record<string, unknown> = Record<never, never>, TStepMeta = unknown> = {
29
+ track: (event: JourneyAnalyticsTrackedEvent<TContext, TStepId, TStepMeta>) => void;
30
+ machineId?: string;
31
+ includeStepMeta?: boolean;
32
+ onError?: (error: unknown, event?: JourneyObservationEvent<TStepId, TEventMap> | JourneyAnalyticsTrackedEvent<TContext, TStepId, TStepMeta>) => void;
33
+ };
@@ -0,0 +1,23 @@
1
+ import type { JourneyMachineSnapshotReason } from "./machine.types";
2
+ import type { JourneyPersistenceOptions } from "./persistence.types";
3
+ import type { JourneyJsonObject, JourneySnapshot } from "./journey.types";
4
+ /** Autosave lifecycle status reported by the autosave plugin. */
5
+ export type JourneyAutosaveStatus = "idle" | "pending" | "saved" | "error";
6
+ /** Runtime autosave state exposed through the autosave plugin extension. */
7
+ export type JourneyAutosaveState = {
8
+ status: JourneyAutosaveStatus;
9
+ lastSavedAt?: number;
10
+ pendingReason?: JourneyMachineSnapshotReason;
11
+ error?: unknown;
12
+ };
13
+ /** Options for the autosave plugin. */
14
+ export type JourneyAutosavePluginOptions<TContext extends JourneyJsonObject, TStepId extends string> = JourneyPersistenceOptions<TContext, TStepId> & {
15
+ debounceMs?: number;
16
+ hydrate?: boolean;
17
+ saveOn?: readonly JourneyMachineSnapshotReason[];
18
+ onSaved?: (details: {
19
+ snapshot: JourneySnapshot<TContext, TStepId>;
20
+ reason: JourneyMachineSnapshotReason;
21
+ timestamp: number;
22
+ }) => void;
23
+ };
@@ -0,0 +1,23 @@
1
+ import type { JourneyMachineSnapshotReason } from "./machine.types";
2
+ import type { JourneyPersistenceOptions } from "./persistence.types";
3
+ import type { JourneyJsonObject, JourneySnapshot } from "./journey.types";
4
+ /** Autosave lifecycle status reported by the autosave plugin. */
5
+ export type JourneyAutosaveStatus = "idle" | "pending" | "saved" | "error";
6
+ /** Runtime autosave state exposed through the autosave plugin extension. */
7
+ export type JourneyAutosaveState = {
8
+ status: JourneyAutosaveStatus;
9
+ lastSavedAt?: number;
10
+ pendingReason?: JourneyMachineSnapshotReason;
11
+ error?: unknown;
12
+ };
13
+ /** Options for the autosave plugin. */
14
+ export type JourneyAutosavePluginOptions<TContext extends JourneyJsonObject, TStepId extends string> = JourneyPersistenceOptions<TContext, TStepId> & {
15
+ debounceMs?: number;
16
+ hydrate?: boolean;
17
+ saveOn?: readonly JourneyMachineSnapshotReason[];
18
+ onSaved?: (details: {
19
+ snapshot: JourneySnapshot<TContext, TStepId>;
20
+ reason: JourneyMachineSnapshotReason;
21
+ timestamp: number;
22
+ }) => void;
23
+ };
@@ -0,0 +1,38 @@
1
+ import type { JourneyBuiltInFrom, JourneyMode } from "./journey.types";
2
+ /** Structural diagnostic issue codes returned by journey diagnostics. */
3
+ export type JourneyDiagnosticsIssueCode = "cycle-detected" | "dead-end-step" | "no-terminal-path" | "shadowed-transition" | "unreachable-step";
4
+ /** Severity assigned to a diagnostics issue. */
5
+ export type JourneyDiagnosticsIssueSeverity = "warning" | "error";
6
+ /** Options that affect structural journey diagnostics. */
7
+ export type JourneyDiagnosticsOptions = {
8
+ requireExplicitCompletion?: boolean;
9
+ };
10
+ /** A single diagnostics issue discovered while analyzing a journey definition. */
11
+ export type JourneyDiagnosticsIssue<TStepId extends string, TEventType extends string> = {
12
+ code: JourneyDiagnosticsIssueCode;
13
+ severity: JourneyDiagnosticsIssueSeverity;
14
+ message: string;
15
+ stepId?: TStepId;
16
+ from?: TStepId | JourneyBuiltInFrom;
17
+ eventType?: TEventType;
18
+ transitionId?: string;
19
+ label?: string;
20
+ steps?: readonly TStepId[];
21
+ };
22
+ /** Aggregate diagnostics summary for a journey definition. */
23
+ export type JourneyDiagnosticsSummary = {
24
+ mode: JourneyMode;
25
+ stepCount: number;
26
+ reachableStepCount: number;
27
+ unreachableStepCount: number;
28
+ deadEndCount: number;
29
+ cycleCount: number;
30
+ shadowedTransitionCount: number;
31
+ graphChecksSkipped: boolean;
32
+ terminalPathExists: boolean;
33
+ };
34
+ /** Result returned by `getJourneyDiagnostics()`. */
35
+ export type JourneyDiagnosticsResult<TStepId extends string, TEventType extends string> = {
36
+ issues: JourneyDiagnosticsIssue<TStepId, TEventType>[];
37
+ summary: JourneyDiagnosticsSummary;
38
+ };
@@ -0,0 +1,38 @@
1
+ import type { JourneyBuiltInFrom, JourneyMode } from "./journey.types";
2
+ /** Structural diagnostic issue codes returned by journey diagnostics. */
3
+ export type JourneyDiagnosticsIssueCode = "cycle-detected" | "dead-end-step" | "no-terminal-path" | "shadowed-transition" | "unreachable-step";
4
+ /** Severity assigned to a diagnostics issue. */
5
+ export type JourneyDiagnosticsIssueSeverity = "warning" | "error";
6
+ /** Options that affect structural journey diagnostics. */
7
+ export type JourneyDiagnosticsOptions = {
8
+ requireExplicitCompletion?: boolean;
9
+ };
10
+ /** A single diagnostics issue discovered while analyzing a journey definition. */
11
+ export type JourneyDiagnosticsIssue<TStepId extends string, TEventType extends string> = {
12
+ code: JourneyDiagnosticsIssueCode;
13
+ severity: JourneyDiagnosticsIssueSeverity;
14
+ message: string;
15
+ stepId?: TStepId;
16
+ from?: TStepId | JourneyBuiltInFrom;
17
+ eventType?: TEventType;
18
+ transitionId?: string;
19
+ label?: string;
20
+ steps?: readonly TStepId[];
21
+ };
22
+ /** Aggregate diagnostics summary for a journey definition. */
23
+ export type JourneyDiagnosticsSummary = {
24
+ mode: JourneyMode;
25
+ stepCount: number;
26
+ reachableStepCount: number;
27
+ unreachableStepCount: number;
28
+ deadEndCount: number;
29
+ cycleCount: number;
30
+ shadowedTransitionCount: number;
31
+ graphChecksSkipped: boolean;
32
+ terminalPathExists: boolean;
33
+ };
34
+ /** Result returned by `getJourneyDiagnostics()`. */
35
+ export type JourneyDiagnosticsResult<TStepId extends string, TEventType extends string> = {
36
+ issues: JourneyDiagnosticsIssue<TStepId, TEventType>[];
37
+ summary: JourneyDiagnosticsSummary;
38
+ };
@@ -0,0 +1,9 @@
1
+ export type { JourneyAnalyticsEventName, JourneyAnalyticsEventPayload, JourneyAnalyticsPluginOptions, JourneyAnalyticsTrackedEvent } from "./analytics.types";
2
+ export type { JourneyAutosavePluginOptions, JourneyAutosaveState, JourneyAutosaveStatus } from "./autosave.types";
3
+ export type { JourneyDiagnosticsIssue, JourneyDiagnosticsIssueCode, JourneyDiagnosticsIssueSeverity, JourneyDiagnosticsOptions, JourneyDiagnosticsResult, JourneyDiagnosticsSummary } from "./diagnostics.types";
4
+ export type { JourneyAsyncPhase, JourneyAsyncState, JourneyBaseEvent, JourneyBuiltInFrom, JourneyBuiltInSendEvent, JourneyCustomSendEvent, JourneyComputed, JourneyComputedBase, JourneyDefaultEventType, JourneyDefinition, JourneyDefinitionBase, JourneyEqualityFn, JourneyEvent, JourneyJsonObject, JourneyJsonPrimitive, JourneyJsonValue, JourneyExecutionPath, JourneyExecutionPathEventType, JourneyExecutionPathOptions, JourneyExecutionPathsResult, JourneyFullEventType, JourneyGoToEvent, JourneyGraphComputed, JourneyHeadlessComputed, JourneyHistory, JourneyLinearComputed, JourneyMode, JourneyPayloadFor, JourneyResolvedDefinition, JourneySelector, JourneySendEvent, JourneySendResult, JourneySnapshot, JourneySnapshotStateBase, JourneyStatus, JourneyStepAsyncState, JourneyStepDefinition, JourneyTerminal } from "./journey.types";
5
+ export type { JourneyCompleteObservationEvent, JourneyLifecycleErrorObservationEvent, JourneyLifecycleErrorPhase, JourneyLastVisitedNavigationObservationEvent, JourneyObservationEvent, JourneyPreviousNavigationObservationEvent, JourneyResetObservationEvent, JourneyStartObservationEvent, JourneyStepEnterObservationEvent, JourneyStepExitObservationEvent, JourneyTerminateObservationEvent, JourneyTransitionErrorObservationEvent, JourneyTransitionStartObservationEvent, JourneyTransitionSuccessObservationEvent } from "./observation.types";
6
+ export type { JourneyLifecycleErrorContext, JourneyMachine, JourneyMachineOptions, JourneyMachinePlugin, JourneyMachinePluginHooks, JourneyMachinePluginSetupContext, JourneyMachinePluginSnapshotChange, JourneyMachineSnapshotReason, JourneyMachineWithPlugins } from "./machine.types";
7
+ export type { JourneyDispatch, JourneyGlobalKey, JourneyLifecycleArgs, JourneyGlobalTransition, JourneyGoToStepGraphEdge, JourneyTransitionArgsForEvent, JourneyTransitionUpdateContextArgsForEvent, JourneyGoToStepTransition, JourneyGraphEdge, JourneyLinearStep, JourneyLinearTransitions, JourneyStepEventGraphEdge, JourneyStepEventTransition, JourneyStepLifecycleCallback, JourneyStepTransitions, JourneyTerminalGraphEdge, JourneyTerminalTransition, JourneyResolvedTransition, JourneyTransition, JourneyTransitionArgs, JourneyTransitionGraph, JourneyTransitionTarget, JourneyTransitionsDefinition } from "./transitions.types";
8
+ export type { JourneyPersistedState, JourneyPersistenceOptions, JourneyStorage } from "./persistence.types";
9
+ export type { JourneyReplayEntry, JourneyReplayEventEntry, JourneyReplayExportOptions, JourneyReplayPluginOptions, JourneyReplaySession, JourneyReplaySnapshotEntry } from "./replay.types";
@@ -1,4 +1,9 @@
1
- export { JOURNEY_ASYNC_PHASE, JOURNEY_EVENT, JOURNEY_STATUS, JOURNEY_WILDCARD } from "./journey.types";
2
- export type { JourneyAsyncPhase, JourneyAsyncState, JourneyBaseEvent, JourneyBuiltInEvent, JourneyBuiltInFrom, JourneyDefaultEventType, JourneyDefinition, JourneyEvent, JourneyEventPayloadMap, JourneyGoToEvent, JourneyGoToStepByIdEventType, JourneyMachine, JourneyMachineEventType, JourneyMachinePayloadMap, JourneyMachineOptions, JourneyObservationEvent, JourneySelector, JourneyEqualityFn, JourneyPayloadFor, JourneySendEvent, JourneySendResult, JourneySnapshot, JourneyStatus, JourneyStepAsyncState, JourneyStepDefinition, JourneyTerminal } from "./journey.types";
3
- export type { JourneyEventTransition, JourneyGoToStepTransition, JourneyTransition, JourneyTransitionArgs, JourneyTransitionTarget } from "./transitions.types";
4
- export type { JourneyPersistedSnapshot, JourneyPersistedState, JourneyPersistenceOptions, JourneyStorage } from "./persistence.types";
1
+ export type { JourneyAnalyticsEventName, JourneyAnalyticsEventPayload, JourneyAnalyticsPluginOptions, JourneyAnalyticsTrackedEvent } from "./analytics.types";
2
+ export type { JourneyAutosavePluginOptions, JourneyAutosaveState, JourneyAutosaveStatus } from "./autosave.types";
3
+ export type { JourneyDiagnosticsIssue, JourneyDiagnosticsIssueCode, JourneyDiagnosticsIssueSeverity, JourneyDiagnosticsOptions, JourneyDiagnosticsResult, JourneyDiagnosticsSummary } from "./diagnostics.types";
4
+ export type { JourneyAsyncPhase, JourneyAsyncState, JourneyBaseEvent, JourneyBuiltInFrom, JourneyBuiltInSendEvent, JourneyCustomSendEvent, JourneyComputed, JourneyComputedBase, JourneyDefaultEventType, JourneyDefinition, JourneyDefinitionBase, JourneyEqualityFn, JourneyEvent, JourneyJsonObject, JourneyJsonPrimitive, JourneyJsonValue, JourneyExecutionPath, JourneyExecutionPathEventType, JourneyExecutionPathOptions, JourneyExecutionPathsResult, JourneyFullEventType, JourneyGoToEvent, JourneyGraphComputed, JourneyHeadlessComputed, JourneyHistory, JourneyLinearComputed, JourneyMode, JourneyPayloadFor, JourneyResolvedDefinition, JourneySelector, JourneySendEvent, JourneySendResult, JourneySnapshot, JourneySnapshotStateBase, JourneyStatus, JourneyStepAsyncState, JourneyStepDefinition, JourneyTerminal } from "./journey.types";
5
+ export type { JourneyCompleteObservationEvent, JourneyLifecycleErrorObservationEvent, JourneyLifecycleErrorPhase, JourneyLastVisitedNavigationObservationEvent, JourneyObservationEvent, JourneyPreviousNavigationObservationEvent, JourneyResetObservationEvent, JourneyStartObservationEvent, JourneyStepEnterObservationEvent, JourneyStepExitObservationEvent, JourneyTerminateObservationEvent, JourneyTransitionErrorObservationEvent, JourneyTransitionStartObservationEvent, JourneyTransitionSuccessObservationEvent } from "./observation.types";
6
+ export type { JourneyLifecycleErrorContext, JourneyMachine, JourneyMachineOptions, JourneyMachinePlugin, JourneyMachinePluginHooks, JourneyMachinePluginSetupContext, JourneyMachinePluginSnapshotChange, JourneyMachineSnapshotReason, JourneyMachineWithPlugins } from "./machine.types";
7
+ export type { JourneyDispatch, JourneyGlobalKey, JourneyLifecycleArgs, JourneyGlobalTransition, JourneyGoToStepGraphEdge, JourneyTransitionArgsForEvent, JourneyTransitionUpdateContextArgsForEvent, JourneyGoToStepTransition, JourneyGraphEdge, JourneyLinearStep, JourneyLinearTransitions, JourneyStepEventGraphEdge, JourneyStepEventTransition, JourneyStepLifecycleCallback, JourneyStepTransitions, JourneyTerminalGraphEdge, JourneyTerminalTransition, JourneyResolvedTransition, JourneyTransition, JourneyTransitionArgs, JourneyTransitionGraph, JourneyTransitionTarget, JourneyTransitionsDefinition } from "./transitions.types";
8
+ export type { JourneyPersistedState, JourneyPersistenceOptions, JourneyStorage } from "./persistence.types";
9
+ export type { JourneyReplayEntry, JourneyReplayEventEntry, JourneyReplayExportOptions, JourneyReplayPluginOptions, JourneyReplaySession, JourneyReplaySnapshotEntry } from "./replay.types";