@rxova/journey-core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,13 +1,13 @@
1
1
  export { createLinearJourney, withLinearTypes } from "./linear/linear.js";
2
- export type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, LinearStepConfig, LinearStepIdOf, LinearStepInput, TerminatePayloadOf } from "./linear/linear.types.js";
2
+ export type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, LinearStepConfig, LinearStepIdOf, LinearStepInput, TerminatePayloadOf, } from "./linear/linear.types.js";
3
3
  export { createGraphJourney, withGraphTypes } from "./graph/graph.js";
4
- export type { GraphHookArgs, GraphJourneyDefinition, GraphJourneyMachine, GraphJourneyOptions, GraphOnEntry, GraphStepConfig, GraphTransition, SendArgs, SendVerb, SendWork, SendWorkArgs, TransitionGuard } from "./graph/graph.types.js";
4
+ export type { GraphHookArgs, GraphJourneyDefinition, GraphJourneyMachine, GraphJourneyOptions, GraphOnEntry, GraphStepConfig, GraphTransition, SendArgs, SendVerb, SendWork, SendWorkArgs, TransitionGuard, } from "./graph/graph.types.js";
5
5
  export { defineWork } from "./graph/work.js";
6
6
  export type { WorkConfig } from "./graph/work.js";
7
- export type { Bag, GraphDefinition, GraphStep, HandlersOf, MetaOf, ResultOf, ResultsOf } from "./graph/bag.types.js";
7
+ export type { Bag, GraphDefinition, GraphStep, HandlersOf, MetaOf, ResultOf, ResultsOf, } from "./graph/bag.types.js";
8
8
  export { analyzeStructure } from "./diagnostics/diagnostics.helpers.js";
9
9
  export type { DiagnosticsIssue, DiagnosticsResult } from "./diagnostics/diagnostics.types.js";
10
10
  export { JourneyError, isJourneyError } from "./core/errors.js";
11
11
  export type { JourneyErrorCode, JourneyErrorDetails } from "./core/errors.js";
12
- export type { AnyJourneyPlugin, ContextUpdater, CurrentStepBase, GraphGuardState, GraphSnapshot, GraphTransitionSnapshot, JourneyControls, JourneyEventObject, JourneyEventPayload, JourneyEventPayloads, JourneyHistoryState, JourneyMachineBase, JourneyNavigation, JourneyOutcome, JourneyPersistOption, JourneyPlugin, JourneyRuntimeOptions, JourneySnapshot, JourneySnapshotBase, JourneyStatus, JourneyStructure, JourneySubscriptionEvent, JourneySubscriptions, LinearSnapshot, MachineState, NavigationFailureReason, NavigationDirection, NavigationResult, StepEnterDirection, NavigationWork, NavigationWorkArgs, OnEnterHook, OnLeaveHook, PluginApis, PluginHost, StepAsyncState, StepHookArgs, TransitionInfo, TransitionState, Unsubscribe } from "./core/types.js";
12
+ export type { AnyJourneyPlugin, ContextUpdater, CurrentStepBase, GraphGuardState, GraphSnapshot, GraphTransitionSnapshot, JourneyControls, JourneyEventObject, JourneyEventPayload, JourneyEventPayloads, JourneyHistoryState, JourneyMachineBase, JourneyNavigation, JourneyOutcome, JourneyPersistOption, JourneyPlugin, JourneyRuntimeOptions, JourneySnapshot, JourneySnapshotBase, JourneyStatus, JourneyStructure, JourneySubscriptionEvent, JourneySubscriptions, LinearSnapshot, MachineState, NavigationFailureReason, NavigationDirection, NavigationResult, StepEnterDirection, NavigationWork, NavigationWorkArgs, OnEnterHook, OnLeaveHook, PluginApis, PluginHost, StepAsyncState, StepHookArgs, TransitionInfo, TransitionState, Unsubscribe, } from "./core/types.js";
13
13
  export type { JourneyStorage } from "./plugins/persistence/persistence.types.js";
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  export { createLinearJourney, withLinearTypes } from "./linear/linear.js";
2
- export type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, LinearStepConfig, LinearStepIdOf, LinearStepInput, TerminatePayloadOf } from "./linear/linear.types.js";
2
+ export type { CompletePayloadOf, JourneyTerminationPayloads, LinearJourneyDefinition, LinearJourneyMachine, LinearStepConfig, LinearStepIdOf, LinearStepInput, TerminatePayloadOf, } from "./linear/linear.types.js";
3
3
  export { createGraphJourney, withGraphTypes } from "./graph/graph.js";
4
- export type { GraphHookArgs, GraphJourneyDefinition, GraphJourneyMachine, GraphJourneyOptions, GraphOnEntry, GraphStepConfig, GraphTransition, SendArgs, SendVerb, SendWork, SendWorkArgs, TransitionGuard } from "./graph/graph.types.js";
4
+ export type { GraphHookArgs, GraphJourneyDefinition, GraphJourneyMachine, GraphJourneyOptions, GraphOnEntry, GraphStepConfig, GraphTransition, SendArgs, SendVerb, SendWork, SendWorkArgs, TransitionGuard, } from "./graph/graph.types.js";
5
5
  export { defineWork } from "./graph/work.js";
6
6
  export type { WorkConfig } from "./graph/work.js";
7
- export type { Bag, GraphDefinition, GraphStep, HandlersOf, MetaOf, ResultOf, ResultsOf } from "./graph/bag.types.js";
7
+ export type { Bag, GraphDefinition, GraphStep, HandlersOf, MetaOf, ResultOf, ResultsOf, } from "./graph/bag.types.js";
8
8
  export { analyzeStructure } from "./diagnostics/diagnostics.helpers.js";
9
9
  export type { DiagnosticsIssue, DiagnosticsResult } from "./diagnostics/diagnostics.types.js";
10
10
  export { JourneyError, isJourneyError } from "./core/errors.js";
11
11
  export type { JourneyErrorCode, JourneyErrorDetails } from "./core/errors.js";
12
- export type { AnyJourneyPlugin, ContextUpdater, CurrentStepBase, GraphGuardState, GraphSnapshot, GraphTransitionSnapshot, JourneyControls, JourneyEventObject, JourneyEventPayload, JourneyEventPayloads, JourneyHistoryState, JourneyMachineBase, JourneyNavigation, JourneyOutcome, JourneyPersistOption, JourneyPlugin, JourneyRuntimeOptions, JourneySnapshot, JourneySnapshotBase, JourneyStatus, JourneyStructure, JourneySubscriptionEvent, JourneySubscriptions, LinearSnapshot, MachineState, NavigationFailureReason, NavigationDirection, NavigationResult, StepEnterDirection, NavigationWork, NavigationWorkArgs, OnEnterHook, OnLeaveHook, PluginApis, PluginHost, StepAsyncState, StepHookArgs, TransitionInfo, TransitionState, Unsubscribe } from "./core/types.js";
12
+ export type { AnyJourneyPlugin, ContextUpdater, CurrentStepBase, GraphGuardState, GraphSnapshot, GraphTransitionSnapshot, JourneyControls, JourneyEventObject, JourneyEventPayload, JourneyEventPayloads, JourneyHistoryState, JourneyMachineBase, JourneyNavigation, JourneyOutcome, JourneyPersistOption, JourneyPlugin, JourneyRuntimeOptions, JourneySnapshot, JourneySnapshotBase, JourneyStatus, JourneyStructure, JourneySubscriptionEvent, JourneySubscriptions, LinearSnapshot, MachineState, NavigationFailureReason, NavigationDirection, NavigationResult, StepEnterDirection, NavigationWork, NavigationWorkArgs, OnEnterHook, OnLeaveHook, PluginApis, PluginHost, StepAsyncState, StepHookArgs, TransitionInfo, TransitionState, Unsubscribe, } from "./core/types.js";
13
13
  export type { JourneyStorage } from "./plugins/persistence/persistence.types.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var m=class extends Error{constructor(e,t,r={},s){super(`journey: ${t}`,s===void 0?void 0:{cause:s}),this.name="JourneyError",this.code=e,this.stepId=r.stepId,this.event=r.event,this.pluginName=r.pluginName}},re=n=>n instanceof m;var R=(n,e)=>`${n.length}:${n}${e}`,H=n=>n.label??`${n.event}[${n.index}] (${n.from} -> ${n.to})`,F=n=>n===null?null:{event:n.event,from:n.from,to:n.to,label:n.label??null,index:n.index},x=Object.freeze({isLoading:!1,isSuccess:!0,isError:!1,error:null}),A=Object.freeze({isLoading:!0,isSuccess:!1,isError:!1,error:null}),S=(n,e)=>Object.hasOwn(n,e);function j(n){console.error("[journey] subscriber threw:",n)}var E=(n,e)=>{let t=Object.keys(n);return t.length!==Object.keys(e).length?!1:t.every(r=>S(e,r)&&Object.is(n[r],e[r]))};function C(n){return{getSnapshot:()=>n.store.getSnapshot(),controls:{start:()=>n.start(),pause:()=>n.pause(),resume:()=>n.resume(),complete:e=>n.complete(e),terminate:e=>n.terminate(e),restart:()=>n.restart()},dispose:()=>n.dispose(),navigate:{goToStepById:e=>n.goToStepById(e),goToPreviousStep:e=>n.goToPreviousStep(e),goToNextStep:e=>n.goToNextStep(e),goToLastVisitedStep:()=>n.goToLastVisitedStep()},subscriptions:{subscribe:e=>n.store.subscribe(e),subscribeEvent:(e,t)=>n.store.subscribeEvent(e,t)},context:{update:e=>n.updateContext(e)},async:{clearError:()=>n.clearAsyncError()},plugins:n.pluginApis}}var D=()=>{let n=globalThis;if(typeof n.__DEV__=="boolean")return n.__DEV__;let e=n.process?.env?.NODE_ENV;return e===void 0||e==="development"},V=(n,e)=>{if(!(!D()||typeof console>"u")){if(e===void 0){console.warn(n);return}console.warn("%s",n,e)}};var N=class{constructor(e,t){this.listeners=new Set;this.eventListeners=new Map;this.disposed=!1;this.snapshot=e,this.onListenerError=t}report(e){if(!this.onListenerError){j(e);return}try{this.onListenerError(e)}catch(t){j(t)}}getSnapshot(){return this.snapshot}publish(e){if(!Object.is(this.snapshot,e)){this.snapshot=e;for(let t of[...this.listeners])try{t()}catch(r){this.report(r)}}}subscribe(e){return this.disposed?()=>{}:(this.listeners.add(e),()=>{this.listeners.delete(e)})}subscribeEvent(e,t){if(this.disposed)return()=>{};let r=this.eventListeners.get(e);return r||(r=new Set,this.eventListeners.set(e,r)),r.add(t),()=>{r.delete(t)}}emit(e,t){let r=this.eventListeners.get(e);if(r)for(let s of[...r])try{s(t)}catch(a){this.report(a)}}dispose(){this.disposed=!0,this.listeners.clear(),this.eventListeners.clear()}};var B=n=>typeof n=="object"&&n!==null&&D()?Object.freeze(n):n,se=Object.freeze({}),Y=Object.freeze({}),w=(n,e)=>e!=null&&E(n,e)?e:Object.freeze(n),I=(n,e,t=Object.is)=>e!==void 0&&e.length===n.length&&n.every((r,s)=>t(r,e[s]))?e:Object.freeze([...n]),ie=(n,e)=>n===void 0?"none":e?"passed":"failed",P=class{constructor(e){this.pluginApis={};this.status="idle";this.timeline=[];this.currentIndex=-1;this.outcome=null;this.visitCounts=new Map;this.entryAsync=x;this.pending=null;this.disposed=!1;this.restoreSeed=null;this.generation=0;this.raiseQueue=[];this.processingRaised=!1;this.lastPluginExtensions={};this.lastSnapshot=null;this.transitionListeners=new Set;this.disposeCallbacks=[];this.snapshotDerivers=new Map;this.config=e,this.frozenStepOrder=Object.freeze([...e.stepIds]),this.restoreSeed=e.restore??null,this.context=B(e.restore?e.restore.context:e.initialContext),this.store=new N(this.buildSnapshot(),e.onListenerError),this.setupPlugins(),this.store.publish(this.buildSnapshot()),e.autoStart&&this.start()}start(){return this.disposed||this.status!=="idle"?!1:(this.setStatus("running"),this.enterInitialStep(),!0)}pause(){return this.disposed||this.status!=="running"||this.pending?!1:(this.setStatus("paused"),!0)}resume(){return this.disposed||this.status!=="paused"?!1:(this.setStatus("running"),!0)}complete(e){return this.disposed||this.status!=="running"||this.pending?!1:(this.outcome=Object.freeze({type:"completed",payload:e}),this.setStatus("completed"),!0)}terminate(e){return this.disposed||this.status==="terminated"?!1:(this.generation+=1,this.pending=null,this.entryAsync=x,this.raiseQueue=[],this.outcome=Object.freeze({type:"terminated",payload:e}),this.setStatus("terminated"),!0)}restart(){return this.disposed||this.status!=="completed"&&this.status!=="terminated"?!1:(this.generation+=1,this.pending=null,this.raiseQueue=[],this.timeline=[],this.currentIndex=-1,this.context=B(this.config.initialContext),this.outcome=null,this.visitCounts=new Map,this.entryAsync=x,this.setStatus("running"),this.enterInitialStep(),!0)}dispose(){if(!this.disposed){this.disposed=!0,this.generation+=1,this.pending=null,this.raiseQueue=[];for(let e of this.disposeCallbacks.splice(0))try{e()}catch{}this.transitionListeners.clear(),this.store.dispose()}}updateContext(e){if(this.disposed)return;let t=this.context;this.context=B(e(t));let r=this.publish();this.store.emit("contextChange",{snapshot:r,previous:t,current:this.context})}clearAsyncError(){this.disposed||!this.entryAsync.isError||(this.entryAsync=x,this.publish())}goToStepById(e){let t=this.checkNavigable();if(t)return this.blocked(t,e);if(!S(this.config.steps,e))return this.blocked({ok:!1,reason:"invalid-target"},e);if(e===this.currentStepId())return this.blocked({ok:!1,reason:"no-op"},e);if(this.config.kind==="graph"){let r=this.config.transitions.find(s=>s.from===this.currentStepId()&&s.to===e&&this.isEnabled(s));return r?this.runNavigation(e,{kind:"append"},this.eventFor(r),r):this.blocked({ok:!1,reason:"invalid-target"},e)}return this.runNavigation(e,{kind:"append"},null,null)}goToPreviousStep(e=1){let t=this.checkNavigable();if(t)return this.blocked(t,null);if(this.currentIndex<=0)return this.blocked({ok:!1,reason:"out-of-bounds"},null);let r=Math.max(0,this.currentIndex-Math.max(1,Math.floor(e))),s=this.timeline[r];return this.runNavigation(s,{kind:"pointer",index:r},null,null,void 0,"backward")}goToNextStep(e){let t=this.checkNavigable();if(t)return this.blocked(t,null);if(this.currentIndex<this.timeline.length-1){let r=this.currentIndex+1,s=this.timeline[r];return this.runNavigation(s,{kind:"pointer",index:r},null,null,e,"forward")}if(this.config.kind==="linear"){let r=this.config.stepIds.indexOf(this.currentStepId()??""),s=this.config.stepIds[r+1];return s===void 0?this.blocked({ok:!1,reason:"out-of-bounds"},null):this.runNavigation(s,{kind:"append"},null,null,e,"forward")}return this.blocked({ok:!1,reason:"out-of-bounds"},null)}goToLastVisitedStep(){let e=this.checkNavigable();if(e)return this.blocked(e,null);let t=this.timeline.length-1;if(this.currentIndex>=t)return this.blocked({ok:!1,reason:"no-op"},null);let r=this.timeline[t];return this.runNavigation(r,{kind:"pointer",index:t},null,null)}goToStepByIndex(e){let t=Number.isInteger(e)?this.config.stepIds[e]:void 0;return t===void 0?this.blocked({ok:!1,reason:"invalid-target"},null):this.goToStepById(t)}send(e,t,r){let s=this.checkNavigable();if(s)return this.blocked(s,null);let a=t===void 0?{type:e}:{type:e,payload:t},u=this.currentStepId(),o=r??(u===null?void 0:this.config.eventWork?.[R(u,e)]);if(o)return this.sendWithWork(e,a,o);let i=this.resolveTransition(e);return i?this.runNavigation(i.to,{kind:"append"},a,i):this.blocked({ok:!1,reason:"no-enabled-transition"},null)}async sendWithWork(e,t,r){let s=this.generation,a=this.currentStepId();if(a===null)return this.blocked({ok:!1,reason:"not-running"},null);let u=this.context,o=!1,i;this.pending={phase:"working",from:a,to:null},this.entryAsync=A,this.publish();try{let d={snapshot:this.store.getSnapshot(),from:a,event:t,handlers:this.config.handlers};if(i=await this.withTimeout(Promise.resolve(r.run(d)),`send work(${r.label??`${e} from ${a}`})`,r.timeoutMs),!this.isCurrent(s))return this.staleResult();let c=r.commit?.({...d,result:i,updateContext:h=>{u=h(u),o=!0}});if(typeof c=="object"&&c!==null&&"then"in c)throw new m("async-commit","send work commit must be synchronous")}catch(d){if(!this.isCurrent(s))return this.staleResult();this.pending=null,this.entryAsync=Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:d});let c=this.publish();return this.store.emit("error",{snapshot:c,error:d,phase:"work",stepId:a}),this.blocked({ok:!1,reason:"error",error:d},null)}if(!this.isCurrent(s))return this.staleResult();let l=this.resolveTransition(e,u,i);return l?this.commitAndSettle(a,l.to,{kind:"append"},t,l,s,u,o,"jump"):(this.pending=null,this.entryAsync=x,this.publish(),this.blocked({ok:!1,reason:"no-enabled-transition"},null))}setupPlugins(){let e={getSnapshot:()=>this.store.getSnapshot(),structure:Object.freeze({kind:this.config.kind,stepIds:Object.freeze([...this.config.stepIds]),initial:this.config.initial,transitions:Object.freeze(this.config.transitions.map(t=>Object.freeze({event:t.event,from:t.from,to:t.to,guarded:t.when!==void 0,label:t.label??null,index:t.index})))}),onTransition:t=>(this.transitionListeners.add(t),()=>this.transitionListeners.delete(t)),onNavigationBlocked:t=>this.store.subscribeEvent("navigationBlocked",t),onStatusChange:t=>this.store.subscribeEvent("statusChange",t),onContextChange:t=>this.store.subscribeEvent("contextChange",t),onError:t=>this.store.subscribeEvent("error",t),onDispose:t=>{this.disposeCallbacks.push(t)},reportError:t=>this.store.report(t)};try{for(let t of this.config.plugins){if(S(this.pluginApis,t.name))throw new m("duplicate-plugin-name",`duplicate plugin name "${t.name}"`,{pluginName:t.name});let r=t.setup(e);this.pluginApis[t.name]=r.api,r.deriveSnapshot&&this.snapshotDerivers.set(t.name,r.deriveSnapshot)}}catch(t){throw this.dispose(),t}}currentStepId(){return this.currentIndex>=0?this.timeline[this.currentIndex]:null}eventFor(e){return{type:e.event}}isEnabled(e,t=this.context,r){if(!e.when)return!0;try{return e.when({context:t,handlers:this.config.handlers,result:r})}catch{return!1}}resolveTransition(e,t=this.context,r){return this.config.transitions.find(s=>s.event===e&&s.from===this.currentStepId()&&this.isEnabled(s,t,r))}checkNavigable(){return this.disposed?{ok:!1,reason:"disposed"}:this.status!=="running"?{ok:!1,reason:"not-running"}:this.pending?{ok:!1,reason:"transitioning"}:null}setStatus(e){let t=this.status;this.status=e;let r=this.publish();this.store.emit("statusChange",{snapshot:r,previous:t,current:e}),this.refreshPluginSnapshot()}refreshPluginSnapshot(){this.snapshotDerivers.size>0&&this.publish()}blocked(e,t){if(e.reason!=="disposed"){let r={snapshot:this.store.getSnapshot(),reason:e.reason,from:this.currentStepId(),to:t};"error"in e&&(r.error=e.error),this.store.emit("navigationBlocked",r)}return Promise.resolve({...e,from:this.currentStepId(),to:t})}hookArgs(e,t,r,s){return{snapshot:this.store.getSnapshot(),from:e,to:t,event:r,transition:s,updateContext:a=>this.updateContext(a),raise:a=>{this.config.kind!=="graph"||this.disposed||this.raiseQueue.push(a)}}}async invokeEffect(e,t,r,s){if(!e)return null;try{return await this.withTimeout(Promise.resolve(e(t)),r,s),null}catch(a){return{error:a}}}withTimeout(e,t,r){let s=r??this.config.defaultTimeoutMs;return s===void 0?e:new Promise((a,u)=>{let o=setTimeout(()=>u(new Error(`journey: ${t} timed out after ${s}ms`)),s);e.then(i=>{clearTimeout(o),a(i)},i=>{clearTimeout(o),u(i)})})}async enterInitialStep(){let e=this.generation,t=this.restoreSeed;if(this.restoreSeed=null,t){let s=t.timeline[t.currentIndex];this.pending={phase:"entering",from:null,to:s},this.timeline=[...t.timeline],this.currentIndex=t.currentIndex;for(let a of t.timeline)this.visitCounts.set(a,(this.visitCounts.get(a)??0)+1);this.commitEntry(null,s,"jump"),await this.runEntryEffects(null,s,null,null,e);return}let r=this.config.startAt??this.config.initial;this.pending={phase:"entering",from:null,to:r},this.timeline=[r],this.currentIndex=0,this.commitEntry(null,r,"jump"),await this.runEntryEffects(null,r,null,null,e)}async runNavigation(e,t,r,s,a,u){let o=this.generation,i=this.currentStepId(),l=this.context,d=!1;if(a&&i!==null&&u){this.pending={phase:"working",from:i,to:e},this.entryAsync=A,this.publish();try{let c={snapshot:this.store.getSnapshot(),from:i,to:e,direction:u},h=await this.withTimeout(Promise.resolve(a.run(c)),`${u} navigation work(${i} -> ${e})`,a.timeoutMs);if(!this.isCurrent(o))return this.staleResult();let f=a.commit?.({...c,result:h,updateContext:y=>{l=y(l),d=!0}});if(typeof f=="object"&&f!==null&&"then"in f)throw new m("async-commit","navigation work commit must be synchronous")}catch(c){if(!this.isCurrent(o))return this.staleResult();this.pending=null,this.entryAsync=Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:c});let h=this.publish();return this.store.emit("error",{snapshot:h,error:c,phase:"work",stepId:i}),this.blocked({ok:!1,reason:"error",error:c},e)}}return this.isCurrent(o)?this.commitAndSettle(i,e,t,r,s,o,l,d,u??"jump"):this.staleResult()}async commitAndSettle(e,t,r,s,a,u,o,i,l){let d=this.context;r.kind==="pointer"?this.currentIndex=r.index:(this.timeline=[...this.timeline.slice(0,this.currentIndex+1),t],this.currentIndex=this.timeline.length-1),i&&(this.context=B(o));let c=e===null?void 0:this.config.steps[e];return this.pending={phase:c?.onLeave?"leaving":"entering",from:e,to:t},this.commitEntry(e,t,l,i?{previous:d,current:o}:void 0,c?.onLeave!==void 0||a?.onTransition!==void 0),await this.runEntryEffects(e,t,s,a,u),{ok:!0,from:e,to:t}}commitEntry(e,t,r,s,a=!1){this.visitCounts.set(t,(this.visitCounts.get(t)??0)+1);let u=this.config.steps[t];this.entryAsync=a||u?.onEnter?A:x;let o=this.publish();s&&this.store.emit("contextChange",{snapshot:o,...s}),e!==null&&this.store.emit("stepLeave",{snapshot:o,from:e,to:t}),this.store.emit("stepEnter",{snapshot:o,from:e,to:t,direction:r})}async runEntryEffects(e,t,r,s,a){let u=e===null?void 0:this.config.steps[e],o=this.config.steps[t],i=F(s),l=[],d=await this.invokeEffect(u?.onLeave,this.hookArgs(e,t,r,i),`onLeave(${e??""})`);if(!this.isCurrent(a))return;d&&e!==null&&l.push({error:d.error,phase:"leave",stepId:e}),this.pending={phase:"entering",from:e,to:t},this.publish();let c=await this.invokeEffect(s?.onTransition,this.hookArgs(e,t,r,i),`onTransition(${s?H(s):r?.type??""})`,s?.timeoutMs);if(!this.isCurrent(a))return;c&&l.push({error:c.error,phase:"transition",stepId:t});let h=await this.invokeEffect(o?.onEnter,this.hookArgs(e,t,r,i),`onEnter(${t})`);if(!this.isCurrent(a))return;h&&l.push({error:h.error,phase:"enter",stepId:t}),this.entryAsync=l[0]?Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:l[0].error}):x,this.pending=null;let f=this.publish();for(let y of l)this.store.emit("error",{snapshot:f,error:y.error,phase:y.phase,stepId:y.stepId});for(let y of[...this.transitionListeners])try{y({from:e,to:t,snapshot:f})}catch{}this.refreshPluginSnapshot(),this.processRaisedEvents(a)}async processRaisedEvents(e){if(this.processingRaised||!this.isCurrent(e))return;this.processingRaised=!0;let t=0;try{for(;this.raiseQueue.length>0&&this.isCurrent(e);){if(this.status!=="running"){this.raiseQueue=[];return}if(t+=1,t>25){this.raiseQueue=[],this.store.emit("error",{snapshot:this.store.getSnapshot(),error:new Error(`journey: raised-event cascade exceeded ${25} events; queue dropped`),phase:"raise",stepId:this.currentStepId()});return}let r=this.raiseQueue.shift();await this.send(r.type,r.payload)}}finally{this.processingRaised=!1}}isCurrent(e){return!this.disposed&&e===this.generation}staleResult(){let e=this.currentStepId();return this.disposed?{ok:!1,reason:"disposed",from:e,to:null}:{ok:!1,reason:"not-running",from:e,to:null}}publish(){let e=this.buildSnapshot();return this.store.publish(e),e}buildSnapshot(){let e=this.lastSnapshot,t=this.currentStepId(),r={},s=0;for(let h of this.config.stepIds){let f=(this.visitCounts.get(h)??0)>0;r[h]=f,f&&(s+=1)}let a=e?.history,u=a!=null&&E(a.visited,r)?a.visited:Object.freeze(r),o={status:this.status,context:this.context,transition:w({pending:this.pending!==null,phase:this.pending?.phase??null,from:this.pending?.from??null,to:this.pending?.to??null},e?.transition),history:w({timeline:I(this.timeline,a?.timeline),currentIndex:this.currentIndex,visited:u,canGoBack:this.currentIndex>0,canGoForward:this.currentIndex>=0&&this.currentIndex<this.timeline.length-1},a),machine:w({outcome:this.outcome},e?.machine)},i=t===null?null:{id:t,metadata:this.config.steps[t]?.metadata??se,isFirstTimeVisit:(this.visitCounts.get(t)??0)===1,async:this.entryAsync},l;if(this.config.kind==="linear"){let h=e?.type==="linear"?e:null,f=t===null?-1:this.config.stepIds.indexOf(t);l={...o,type:"linear",currentStep:i===null?null:w({...i,index:f,isFirstStep:f===0,isLastStep:f===this.config.stepIds.length-1},h?.currentStep),steps:w({totalSteps:this.config.stepIds.length,stepOrder:this.frozenStepOrder,visitedStepCount:s},h?.steps),plugins:Y}}else{let h=[],f=[],y=[],g=[],p=new Map,T=new Set,b=!1;if(t!==null)for(let v of this.config.transitions){if(v.from!==t)continue;b=!0,h.includes(v.event)||h.push(v.event);let J=p.get(v.event)??0;p.set(v.event,J+1);let O=this.isEnabled(v),U=O&&!T.has(v.event);U&&T.add(v.event),g.push(Object.freeze({event:v.event,to:v.to,priority:J,guard:ie(v.when,O),enabled:O,selected:U})),O&&(f.includes(v.event)||f.push(v.event),y.includes(v.to)||y.push(v.to))}let k=e?.type==="graph"?e:null;l={...o,type:"graph",currentStep:i===null?null:w({...i,isTerminal:!b},k?.currentStep),steps:w({totalSteps:this.config.stepIds.length,visitedStepCount:s},k?.steps),declaredEvents:I(h,k?.declaredEvents),availableEvents:I(f,k?.availableEvents),availableSteps:I(y,k?.availableSteps),outgoingTransitions:I(g,k?.outgoingTransitions,(v,J)=>E(v,J)),plugins:Y}}if(this.snapshotDerivers.size>0){let h={};for(let[y,g]of this.snapshotDerivers)try{h[y]=g(l,this.lastPluginExtensions[y])}catch(p){this.store.report(p),S(this.lastPluginExtensions,y)&&(h[y]=this.lastPluginExtensions[y])}this.lastPluginExtensions=h;let f=e?.plugins;l={...l,plugins:f!=null&&E(f,h)?f:Object.freeze(h)}}let d=Object.freeze(l),c=e!==null&&E(d,e)?e:d;return this.lastSnapshot=c,c}};var K=["context","transition","status"];function q(n){return typeof n!="number"||!Number.isFinite(n)?0:Math.max(0,Math.trunc(n))}function X(n,e){return{status:n.status,context:n.context,timeline:n.history.timeline,currentIndex:n.history.currentIndex,savedAt:e}}var oe=new Set(["idle","running","paused","completed","terminated"]),ae=["__proto__","constructor","prototype"];function G(n,e=0){if(e>50||n===null||typeof n!="object")return n;if(Array.isArray(n))return n.map(r=>G(r,e+1));let t={};for(let[r,s]of Object.entries(n))ae.includes(r)||(t[r]=G(s,e+1));return t}function W(n){if(n===null)return null;let e;try{e=JSON.parse(n)}catch{return null}if(typeof e!="object"||e===null)return null;let t=e;return typeof t.status!="string"||!oe.has(t.status)||!Array.isArray(t.timeline)||!t.timeline.every(r=>typeof r=="string")||typeof t.currentIndex!="number"||!Number.isInteger(t.currentIndex)||typeof t.savedAt!="number"||!Number.isFinite(t.savedAt)?null:{status:t.status,context:G(t.context),timeline:t.timeline,currentIndex:t.currentIndex,savedAt:t.savedAt}}function $(n){if(n.storage)return n.storage;let e;try{e=globalThis.localStorage}catch(t){throw new m("storage-unavailable","localStorage access was blocked by the environment; pass persist.storage explicitly",{},t)}if(!e)throw new m("storage-unavailable","persist.storage is required when localStorage is unavailable");return e}function L(n,e){let t;try{t=$(n).getItem(n.key)}catch{return null}let r=W(t);if(!r||r.status!=="running"&&r.status!=="paused"||!Number.isInteger(r.currentIndex)||r.currentIndex<0||r.currentIndex>=r.timeline.length)return null;for(let s of r.timeline)if(typeof s!="string"||!e(s))return null;return r}var z={status:"idle",lastSavedAt:null,error:null};function ue(n){let e=n.now??Date.now,t=q(n.debounceMs),r=new Set(n.saveOn??K),s=0;return{name:"persistence",setup(a){s+=1,s>1&&V(`journey: persistence plugin instance shared by ${s} machines; they overwrite key "${n.key}". Create one per machine.`);let u=null,o=z,i=null,l=!1,d=p=>{u=p,o={status:"saved",lastSavedAt:p.savedAt,error:null}},c=p=>{o={...o,status:"error",error:p},a.reportError(p)},h=()=>{let p=X(a.getSnapshot(),e());o={...o,status:"saving"};let T;try{T=n.storage.setItem(n.key,JSON.stringify(p))}catch(b){throw o={...o,status:"error",error:b},b}if(T!==void 0)return T.then(()=>d(p),c);d(p)},f=()=>{i!==null&&(clearTimeout(i),i=null)},y=()=>{i=null;try{let p=h()}catch(p){c(p)}},g=p=>{if(!(l||!r.has(p))){if(t===0){h();return}f(),o={...o,status:"pending"},i=setTimeout(y,t)}};return a.onContextChange(()=>g("context")),a.onTransition(()=>g("transition")),a.onStatusChange(({current:p})=>{if(p==="terminated"&&n.clearOnTerminate){f(),u=null,o=z,n.storage.removeItem(n.key);return}g("status")}),a.onDispose(()=>{l=!0,f()}),{api:{inspectPersistedState:()=>u,getPersistenceState:()=>o,readPersisted:()=>W(n.storage.getItem(n.key)),clearPersisted:()=>{f(),u=null,o=z;try{n.storage.removeItem(n.key)}catch(p){c(p)}},flushPersisted:async()=>{f();try{await h()}catch(p){c(p)}}},deriveSnapshot:(p,T)=>T===o?T:o}}}}function M(n){return ue({key:n.key,storage:$(n)})}function Z(n,e={}){if(n.steps.length===0)throw new m("empty-definition","a linear journey needs at least one step");let t=[],r={};for(let o of n.steps){let i=typeof o=="string"?{id:o}:o;if(S(r,i.id))throw new m("duplicate-step-id",`duplicate step id "${i.id}"`,{stepId:i.id});t.push(i.id);let l={metadata:i.metadata??{}};i.onEnter&&(l.onEnter=i.onEnter),i.onLeave&&(l.onLeave=i.onLeave),r[i.id]=l}if(e.startAt!==void 0&&!S(r,e.startAt))throw new m("unknown-step",`startAt references unknown step "${e.startAt}"`,{stepId:e.startAt});let s=e.persist&&e.startAt===void 0?L(e.persist,o=>S(r,o)):null,a=new P({kind:"linear",stepIds:t,steps:r,initial:t[0],...e.startAt!==void 0?{startAt:e.startAt}:{},initialContext:n.context,...s?{restore:{context:s.context,timeline:s.timeline,currentIndex:s.currentIndex}}:{},transitions:[],handlers:void 0,autoStart:e.autoStart??!0,defaultTimeoutMs:e.defaultTimeoutMs,...e.onListenerError!==void 0?{onListenerError:e.onListenerError}:{},plugins:[...e.persist?[M(e.persist)]:[],...e.plugins??[]]}),u=C(a);return{...u,navigate:{...u.navigate,goToStepByIndex:o=>a.goToStepByIndex(o)}}}var le=()=>(n,e={})=>Z(n,e);var ee=(n,e,t)=>{if(typeof n!="string"||n.length===0)throw new m("invalid-label",`${e} must declare "label" as a non-empty string`,t);return n},te=(n,e,t)=>{if(typeof n!="number"||!Number.isFinite(n)||n<=0)throw new m("invalid-timeout",`${e} must declare "timeoutMs" as a finite number greater than 0`,t);return n};function _(n){let e=Object.keys(n.steps);if(e.length===0)throw new m("empty-definition","a graph journey needs at least one step");if(!e.includes(n.initial))throw new m("unknown-initial-step",`initial step "${n.initial}" is not a declared step`,{stepId:n.initial});let t={};for(let u of e){let o=n.steps[u],i={metadata:o.metadata??{}};o.onEnter&&(i.onEnter=o.onEnter),o.onLeave&&(i.onLeave=o.onLeave),t[u]=i}let r=[],s={},a=(u,o,i,l)=>{if(!e.includes(i.to))throw new m("dangling-transition",`transition "${u}" references unknown step "${i.to}"`,{event:u,stepId:i.to});let d=`transition "${u}"[${l}] on step "${o}"`,c={event:u,from:o,to:i.to,index:l};i.when&&(c.when=i.when),i.onTransition&&(c.onTransition=i.onTransition),i.label!==void 0&&(c.label=ee(i.label,d,{event:u,stepId:o})),i.timeoutMs!==void 0&&(c.timeoutMs=te(i.timeoutMs,d,{event:u,stepId:o})),r.push(c)};for(let u of e){let o=n.steps[u]?.on;if(o)for(let[i,l]of Object.entries(o)){if(l===void 0)continue;if(typeof l=="string"){a(i,u,{to:l},0);continue}if(Array.isArray(l)){l.forEach((f,y)=>a(i,u,f,y));continue}let d=l,c=`work on "${i}" on step "${u}"`,h={run:d.run};d.commit&&(h.commit=d.commit),d.label!==void 0&&(h.label=ee(d.label,c,{event:i,stepId:u})),d.timeoutMs!==void 0&&(h.timeoutMs=te(d.timeoutMs,c,{event:i,stepId:u})),s[R(u,i)]=h,d.candidates.forEach((f,y)=>a(i,u,f,y))}}return{stepIds:e,steps:t,transitions:r,eventWork:s}}function ne(n,e={}){let{stepIds:t,steps:r,transitions:s,eventWork:a}=_(n);if(e.startAt!==void 0&&!S(r,e.startAt))throw new m("unknown-step",`startAt references unknown step "${e.startAt}"`,{stepId:e.startAt});let u=e.persist&&e.startAt===void 0?L(e.persist,l=>S(r,l)):null,o=new P({kind:"graph",stepIds:t,steps:r,initial:n.initial,...e.startAt!==void 0?{startAt:e.startAt}:{},initialContext:n.context,...u?{restore:{context:u.context,timeline:u.timeline,currentIndex:u.currentIndex}}:{},transitions:s,...Object.keys(a).length>0?{eventWork:a}:{},handlers:e.handlers??n.handlers,autoStart:e.autoStart??!0,defaultTimeoutMs:e.defaultTimeoutMs,...e.onListenerError!==void 0?{onListenerError:e.onListenerError}:{},plugins:[...e.persist?[M(e.persist)]:[],...e.plugins??[]]});return{...C(o),send:(l,d)=>o.send(l,d)}}var pe=()=>(n,e={})=>ne(n,e);var de=()=>n=>n;function ce(n){let e=[],t=[...n.stepIds],r=new Set,s=new Set;n.transitions.forEach((g,p)=>{let T=`${g.from} ${g.event}`;if(s.has(T)){r.add(p),e.push({code:"shadowed-transition",severity:"warning",from:g.from,event:g.event,message:`Transition "${g.from}.${g.event}" is shadowed by an earlier unconditional candidate.`});return}g.guarded||s.add(T)});let a=n.transitions.filter((g,p)=>!r.has(p)),u=g=>a.filter(p=>p.from===g),o=new Set,i=[n.initial];for(;i.length>0;){let g=i.pop();if(!o.has(g)){o.add(g);for(let p of u(g))i.push(p.to)}}for(let g of t)o.has(g)||e.push({code:"unreachable-step",severity:"warning",stepId:g,message:`Step "${g}" is unreachable from the initial step "${n.initial}".`});let l=t.filter(g=>u(g).length===0),d=new Set,c=new Set,h=[],f=g=>{c.add(g),h.push(g);for(let p of u(g)){let T=h.indexOf(p.to);if(T>=0){let b=[...h.slice(T),p.to],k=b.join(" ");d.has(k)||(d.add(k),e.push({code:"cycle-detected",severity:"warning",from:p.from,event:p.event,steps:b,message:`Cycle detected: ${b.join(" -> ")}.`}));continue}c.has(p.to)||f(p.to)}h.pop()};f(n.initial);let y=l.some(g=>o.has(g));return y||e.push({code:"no-terminal-path",severity:"warning",stepId:n.initial,message:`No terminal step is reachable from the initial step "${n.initial}".`}),{issues:e,summary:{stepCount:t.length,reachableStepCount:o.size,unreachableStepCount:t.length-o.size,terminalStepIds:l,cycleCount:d.size,shadowedTransitionCount:r.size,terminalPathExists:y}}}function he(n){let{stepIds:e,transitions:t}=_(n),r={kind:"graph",stepIds:e,initial:n.initial,transitions:t.map(s=>({event:s.event,from:s.from,to:s.to,guarded:s.when!==void 0,label:s.label??null,index:s.index}))};return ce(r)}export{m as JourneyError,he as analyzeStructure,ne as createGraphJourney,Z as createLinearJourney,de as defineWork,re as isJourneyError,pe as withGraphTypes,le as withLinearTypes};
1
+ var m=class extends Error{constructor(e,t,r={},s){super(`journey: ${t}`,s===void 0?void 0:{cause:s}),this.name="JourneyError",this.code=e,this.stepId=r.stepId,this.event=r.event,this.pluginName=r.pluginName}},re=n=>n instanceof m;var A=(n,e)=>`${n.length}:${n}${e}`,U=n=>n.label??`${n.event}[${n.index}] (${n.from} -> ${n.to})`,F=n=>n===null?null:{event:n.event,from:n.from,to:n.to,label:n.label??null,index:n.index},x=Object.freeze({isLoading:!1,isSuccess:!0,isError:!1,error:null}),R=Object.freeze({isLoading:!0,isSuccess:!1,isError:!1,error:null}),S=(n,e)=>Object.hasOwn(n,e);function j(n){console.error("[journey] subscriber threw:",n)}function C(n){return{getSnapshot:()=>n.store.getSnapshot(),controls:{start:()=>n.start(),pause:()=>n.pause(),resume:()=>n.resume(),complete:e=>n.complete(e),terminate:e=>n.terminate(e),restart:()=>n.restart()},dispose:()=>n.dispose(),navigate:{goToStepById:e=>n.goToStepById(e),goToPreviousStep:e=>n.goToPreviousStep(e),goToNextStep:e=>n.goToNextStep(e),goToLastVisitedStep:()=>n.goToLastVisitedStep()},subscriptions:{subscribe:e=>n.store.subscribe(e),subscribeEvent:(e,t)=>n.store.subscribeEvent(e,t)},context:{update:e=>n.updateContext(e)},async:{clearError:()=>n.clearAsyncError()},plugins:n.pluginApis}}var D=()=>{let n=globalThis;if(typeof n.__DEV__=="boolean")return n.__DEV__;let e=n.process?.env?.NODE_ENV;return e===void 0||e==="development"},V=(n,e)=>{if(!(!D()||typeof console>"u")){if(e===void 0){console.warn(n);return}console.warn("%s",n,e)}};var w=(n,e)=>{if(Object.is(n,e))return!0;if(typeof n!="object"||n===null||typeof e!="object"||e===null||Array.isArray(n)!==Array.isArray(e))return!1;let t=n,r=e,s=Object.keys(t);return s.length!==Object.keys(r).length?!1:s.every(a=>Object.hasOwn(r,a)&&Object.is(t[a],r[a]))};var N=class{constructor(e,t){this.listeners=new Set;this.eventListeners=new Map;this.disposed=!1;this.snapshot=e,this.onListenerError=t}report(e){if(!this.onListenerError){j(e);return}try{this.onListenerError(e)}catch(t){j(t)}}getSnapshot(){return this.snapshot}publish(e){if(!Object.is(this.snapshot,e)){this.snapshot=e;for(let t of[...this.listeners])try{t()}catch(r){this.report(r)}}}subscribe(e){return this.disposed?()=>{}:(this.listeners.add(e),()=>{this.listeners.delete(e)})}subscribeEvent(e,t){if(this.disposed)return()=>{};let r=this.eventListeners.get(e);return r||(r=new Set,this.eventListeners.set(e,r)),r.add(t),()=>{r.delete(t)}}emit(e,t){let r=this.eventListeners.get(e);if(r)for(let s of[...r])try{s(t)}catch(a){this.report(a)}}dispose(){this.disposed=!0,this.listeners.clear(),this.eventListeners.clear()}};var B=n=>typeof n=="object"&&n!==null&&D()?Object.freeze(n):n,se=Object.freeze({}),Y=Object.freeze({}),E=(n,e)=>e!=null&&w(n,e)?e:Object.freeze(n),P=(n,e,t=Object.is)=>e!==void 0&&e.length===n.length&&n.every((r,s)=>t(r,e[s]))?e:Object.freeze([...n]),ie=(n,e)=>n===void 0?"none":e?"passed":"failed",I=class{constructor(e){this.pluginApis={};this.status="idle";this.timeline=[];this.currentIndex=-1;this.outcome=null;this.visitCounts=new Map;this.entryAsync=x;this.pending=null;this.disposed=!1;this.restoreSeed=null;this.generation=0;this.raiseQueue=[];this.processingRaised=!1;this.lastPluginExtensions={};this.lastSnapshot=null;this.transitionListeners=new Set;this.disposeCallbacks=[];this.snapshotDerivers=new Map;this.config=e,this.frozenStepOrder=Object.freeze([...e.stepIds]),this.restoreSeed=e.restore??null,this.context=B(e.restore?e.restore.context:e.initialContext),this.store=new N(this.buildSnapshot(),e.onListenerError),this.setupPlugins(),this.store.publish(this.buildSnapshot()),e.autoStart&&this.start()}start(){return this.disposed||this.status!=="idle"?!1:(this.setStatus("running"),this.enterInitialStep(),!0)}pause(){return this.disposed||this.status!=="running"||this.pending?!1:(this.setStatus("paused"),!0)}resume(){return this.disposed||this.status!=="paused"?!1:(this.setStatus("running"),!0)}complete(e){return this.disposed||this.status!=="running"||this.pending?!1:(this.outcome=Object.freeze({type:"completed",payload:e}),this.setStatus("completed"),!0)}terminate(e){return this.disposed||this.status==="terminated"?!1:(this.generation+=1,this.pending=null,this.entryAsync=x,this.raiseQueue=[],this.outcome=Object.freeze({type:"terminated",payload:e}),this.setStatus("terminated"),!0)}restart(){return this.disposed||this.status!=="completed"&&this.status!=="terminated"?!1:(this.generation+=1,this.pending=null,this.raiseQueue=[],this.timeline=[],this.currentIndex=-1,this.context=B(this.config.initialContext),this.outcome=null,this.visitCounts=new Map,this.entryAsync=x,this.setStatus("running"),this.enterInitialStep(),!0)}dispose(){if(!this.disposed){this.disposed=!0,this.generation+=1,this.pending=null,this.raiseQueue=[];for(let e of this.disposeCallbacks.splice(0))try{e()}catch{}this.transitionListeners.clear(),this.store.dispose()}}updateContext(e){if(this.disposed)return;let t=this.context;this.context=B(e(t));let r=this.publish();this.store.emit("contextChange",{snapshot:r,previous:t,current:this.context})}clearAsyncError(){this.disposed||!this.entryAsync.isError||(this.entryAsync=x,this.publish())}goToStepById(e){let t=this.checkNavigable();if(t)return this.blocked(t,e);if(!S(this.config.steps,e))return this.blocked({ok:!1,reason:"invalid-target"},e);if(e===this.currentStepId())return this.blocked({ok:!1,reason:"no-op"},e);if(this.config.kind==="graph"){let r=this.config.transitions.find(s=>s.from===this.currentStepId()&&s.to===e&&this.isEnabled(s));return r?this.runNavigation(e,{kind:"append"},this.eventFor(r),r):this.blocked({ok:!1,reason:"invalid-target"},e)}return this.runNavigation(e,{kind:"append"},null,null)}goToPreviousStep(e=1){let t=this.checkNavigable();if(t)return this.blocked(t,null);if(this.currentIndex<=0)return this.blocked({ok:!1,reason:"out-of-bounds"},null);let r=Math.max(0,this.currentIndex-Math.max(1,Math.floor(e))),s=this.timeline[r];return this.runNavigation(s,{kind:"pointer",index:r},null,null,void 0,"backward")}goToNextStep(e){let t=this.checkNavigable();if(t)return this.blocked(t,null);if(this.currentIndex<this.timeline.length-1){let r=this.currentIndex+1,s=this.timeline[r];return this.runNavigation(s,{kind:"pointer",index:r},null,null,e,"forward")}if(this.config.kind==="linear"){let r=this.config.stepIds.indexOf(this.currentStepId()??""),s=this.config.stepIds[r+1];return s===void 0?this.blocked({ok:!1,reason:"out-of-bounds"},null):this.runNavigation(s,{kind:"append"},null,null,e,"forward")}return this.blocked({ok:!1,reason:"out-of-bounds"},null)}goToLastVisitedStep(){let e=this.checkNavigable();if(e)return this.blocked(e,null);let t=this.timeline.length-1;if(this.currentIndex>=t)return this.blocked({ok:!1,reason:"no-op"},null);let r=this.timeline[t];return this.runNavigation(r,{kind:"pointer",index:t},null,null)}goToStepByIndex(e){let t=Number.isInteger(e)?this.config.stepIds[e]:void 0;return t===void 0?this.blocked({ok:!1,reason:"invalid-target"},null):this.goToStepById(t)}send(e,t,r){let s=this.checkNavigable();if(s)return this.blocked(s,null);let a=t===void 0?{type:e}:{type:e,payload:t},u=this.currentStepId(),o=r??(u===null?void 0:this.config.eventWork?.[A(u,e)]);if(o)return this.sendWithWork(e,a,o);let i=this.resolveTransition(e);return i?this.runNavigation(i.to,{kind:"append"},a,i):this.blocked({ok:!1,reason:"no-enabled-transition"},null)}async sendWithWork(e,t,r){let s=this.generation,a=this.currentStepId();if(a===null)return this.blocked({ok:!1,reason:"not-running"},null);let u=this.context,o=!1,i;this.pending={phase:"working",from:a,to:null},this.entryAsync=R,this.publish();try{let c={snapshot:this.store.getSnapshot(),from:a,event:t,handlers:this.config.handlers};if(i=await this.withTimeout(Promise.resolve(r.run(c)),`send work(${r.label??`${e} from ${a}`})`,r.timeoutMs),!this.isCurrent(s))return this.staleResult();let d=r.commit?.({...c,result:i,updateContext:h=>{u=h(u),o=!0}});if(typeof d=="object"&&d!==null&&"then"in d)throw new m("async-commit","send work commit must be synchronous")}catch(c){if(!this.isCurrent(s))return this.staleResult();this.pending=null,this.entryAsync=Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:c});let d=this.publish();return this.store.emit("error",{snapshot:d,error:c,phase:"work",stepId:a}),this.blocked({ok:!1,reason:"error",error:c},null)}if(!this.isCurrent(s))return this.staleResult();let l=this.resolveTransition(e,u,i);return l?this.commitAndSettle(a,l.to,{kind:"append"},t,l,s,u,o,"jump"):(this.pending=null,this.entryAsync=x,this.publish(),this.blocked({ok:!1,reason:"no-enabled-transition"},null))}setupPlugins(){let e={getSnapshot:()=>this.store.getSnapshot(),structure:Object.freeze({kind:this.config.kind,stepIds:Object.freeze([...this.config.stepIds]),initial:this.config.initial,transitions:Object.freeze(this.config.transitions.map(t=>Object.freeze({event:t.event,from:t.from,to:t.to,guarded:t.when!==void 0,label:t.label??null,index:t.index})))}),onTransition:t=>(this.transitionListeners.add(t),()=>this.transitionListeners.delete(t)),onNavigationBlocked:t=>this.store.subscribeEvent("navigationBlocked",t),onStatusChange:t=>this.store.subscribeEvent("statusChange",t),onContextChange:t=>this.store.subscribeEvent("contextChange",t),onError:t=>this.store.subscribeEvent("error",t),onDispose:t=>{this.disposeCallbacks.push(t)},reportError:t=>this.store.report(t)};try{for(let t of this.config.plugins){if(S(this.pluginApis,t.name))throw new m("duplicate-plugin-name",`duplicate plugin name "${t.name}"`,{pluginName:t.name});let r=t.setup(e);this.pluginApis[t.name]=r.api,r.deriveSnapshot&&this.snapshotDerivers.set(t.name,r.deriveSnapshot)}}catch(t){throw this.dispose(),t}}currentStepId(){return this.currentIndex>=0?this.timeline[this.currentIndex]:null}eventFor(e){return{type:e.event}}isEnabled(e,t=this.context,r){if(!e.when)return!0;try{return e.when({context:t,handlers:this.config.handlers,result:r})}catch{return!1}}resolveTransition(e,t=this.context,r){return this.config.transitions.find(s=>s.event===e&&s.from===this.currentStepId()&&this.isEnabled(s,t,r))}checkNavigable(){return this.disposed?{ok:!1,reason:"disposed"}:this.status!=="running"?{ok:!1,reason:"not-running"}:this.pending?{ok:!1,reason:"transitioning"}:null}setStatus(e){let t=this.status;this.status=e;let r=this.publish();this.store.emit("statusChange",{snapshot:r,previous:t,current:e}),this.refreshPluginSnapshot()}refreshPluginSnapshot(){this.snapshotDerivers.size>0&&this.publish()}blocked(e,t){if(e.reason!=="disposed"){let r={snapshot:this.store.getSnapshot(),reason:e.reason,from:this.currentStepId(),to:t};"error"in e&&(r.error=e.error),this.store.emit("navigationBlocked",r)}return Promise.resolve({...e,from:this.currentStepId(),to:t})}hookArgs(e,t,r,s){return{snapshot:this.store.getSnapshot(),from:e,to:t,event:r,transition:s,updateContext:a=>this.updateContext(a),raise:a=>{this.config.kind!=="graph"||this.disposed||this.raiseQueue.push(a)}}}async invokeEffect(e,t,r,s){if(!e)return null;try{return await this.withTimeout(Promise.resolve(e(t)),r,s),null}catch(a){return{error:a}}}withTimeout(e,t,r){let s=r??this.config.defaultTimeoutMs;return s===void 0?e:new Promise((a,u)=>{let o=setTimeout(()=>u(new Error(`journey: ${t} timed out after ${s}ms`)),s);e.then(i=>{clearTimeout(o),a(i)},i=>{clearTimeout(o),u(i)})})}async enterInitialStep(){let e=this.generation,t=this.restoreSeed;if(this.restoreSeed=null,t){let s=t.timeline[t.currentIndex];this.pending={phase:"entering",from:null,to:s},this.timeline=[...t.timeline],this.currentIndex=t.currentIndex;for(let a of t.timeline)this.visitCounts.set(a,(this.visitCounts.get(a)??0)+1);this.commitEntry(null,s,"jump"),await this.runEntryEffects(null,s,null,null,e);return}let r=this.config.startAt??this.config.initial;this.pending={phase:"entering",from:null,to:r},this.timeline=[r],this.currentIndex=0,this.commitEntry(null,r,"jump"),await this.runEntryEffects(null,r,null,null,e)}async runNavigation(e,t,r,s,a,u){let o=this.generation,i=this.currentStepId(),l=this.context,c=!1;if(a&&i!==null&&u){this.pending={phase:"working",from:i,to:e},this.entryAsync=R,this.publish();try{let d={snapshot:this.store.getSnapshot(),from:i,to:e,direction:u},h=await this.withTimeout(Promise.resolve(a.run(d)),`${u} navigation work(${i} -> ${e})`,a.timeoutMs);if(!this.isCurrent(o))return this.staleResult();let g=a.commit?.({...d,result:h,updateContext:y=>{l=y(l),c=!0}});if(typeof g=="object"&&g!==null&&"then"in g)throw new m("async-commit","navigation work commit must be synchronous")}catch(d){if(!this.isCurrent(o))return this.staleResult();this.pending=null,this.entryAsync=Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:d});let h=this.publish();return this.store.emit("error",{snapshot:h,error:d,phase:"work",stepId:i}),this.blocked({ok:!1,reason:"error",error:d},e)}}return this.isCurrent(o)?this.commitAndSettle(i,e,t,r,s,o,l,c,u??"jump"):this.staleResult()}async commitAndSettle(e,t,r,s,a,u,o,i,l){let c=this.context;r.kind==="pointer"?this.currentIndex=r.index:(this.timeline=[...this.timeline.slice(0,this.currentIndex+1),t],this.currentIndex=this.timeline.length-1),i&&(this.context=B(o));let d=e===null?void 0:this.config.steps[e];return this.pending={phase:d?.onLeave?"leaving":"entering",from:e,to:t},this.commitEntry(e,t,l,i?{previous:c,current:o}:void 0,d?.onLeave!==void 0||a?.onTransition!==void 0),await this.runEntryEffects(e,t,s,a,u),{ok:!0,from:e,to:t}}commitEntry(e,t,r,s,a=!1){this.visitCounts.set(t,(this.visitCounts.get(t)??0)+1);let u=this.config.steps[t];this.entryAsync=a||u?.onEnter?R:x;let o=this.publish();s&&this.store.emit("contextChange",{snapshot:o,...s}),e!==null&&this.store.emit("stepLeave",{snapshot:o,from:e,to:t}),this.store.emit("stepEnter",{snapshot:o,from:e,to:t,direction:r})}async runEntryEffects(e,t,r,s,a){let u=e===null?void 0:this.config.steps[e],o=this.config.steps[t],i=F(s),l=[],c=await this.invokeEffect(u?.onLeave,this.hookArgs(e,t,r,i),`onLeave(${e??""})`);if(!this.isCurrent(a))return;c&&e!==null&&l.push({error:c.error,phase:"leave",stepId:e}),this.pending={phase:"entering",from:e,to:t},this.publish();let d=await this.invokeEffect(s?.onTransition,this.hookArgs(e,t,r,i),`onTransition(${s?U(s):r?.type??""})`,s?.timeoutMs);if(!this.isCurrent(a))return;d&&l.push({error:d.error,phase:"transition",stepId:t});let h=await this.invokeEffect(o?.onEnter,this.hookArgs(e,t,r,i),`onEnter(${t})`);if(!this.isCurrent(a))return;h&&l.push({error:h.error,phase:"enter",stepId:t}),this.entryAsync=l[0]?Object.freeze({isLoading:!1,isSuccess:!1,isError:!0,error:l[0].error}):x,this.pending=null;let g=this.publish();for(let y of l)this.store.emit("error",{snapshot:g,error:y.error,phase:y.phase,stepId:y.stepId});for(let y of[...this.transitionListeners])try{y({from:e,to:t,snapshot:g})}catch{}this.refreshPluginSnapshot(),this.processRaisedEvents(a)}async processRaisedEvents(e){if(this.processingRaised||!this.isCurrent(e))return;this.processingRaised=!0;let t=0;try{for(;this.raiseQueue.length>0&&this.isCurrent(e);){if(this.status!=="running"){this.raiseQueue=[];return}if(t+=1,t>25){this.raiseQueue=[],this.store.emit("error",{snapshot:this.store.getSnapshot(),error:new Error(`journey: raised-event cascade exceeded ${25} events; queue dropped`),phase:"raise",stepId:this.currentStepId()});return}let r=this.raiseQueue.shift();await this.send(r.type,r.payload)}}finally{this.processingRaised=!1}}isCurrent(e){return!this.disposed&&e===this.generation}staleResult(){let e=this.currentStepId();return this.disposed?{ok:!1,reason:"disposed",from:e,to:null}:{ok:!1,reason:"not-running",from:e,to:null}}publish(){let e=this.buildSnapshot();return this.store.publish(e),e}buildSnapshot(){let e=this.lastSnapshot,t=this.currentStepId(),r={},s=0;for(let h of this.config.stepIds){let g=(this.visitCounts.get(h)??0)>0;r[h]=g,g&&(s+=1)}let a=e?.history,u=a!=null&&w(a.visited,r)?a.visited:Object.freeze(r),o={status:this.status,context:this.context,transition:E({pending:this.pending!==null,phase:this.pending?.phase??null,from:this.pending?.from??null,to:this.pending?.to??null},e?.transition),history:E({timeline:P(this.timeline,a?.timeline),currentIndex:this.currentIndex,visited:u,canGoBack:this.currentIndex>0,canGoForward:this.currentIndex>=0&&this.currentIndex<this.timeline.length-1},a),machine:E({outcome:this.outcome},e?.machine)},i=t===null?null:{id:t,metadata:this.config.steps[t]?.metadata??se,isFirstTimeVisit:(this.visitCounts.get(t)??0)===1,async:this.entryAsync},l;if(this.config.kind==="linear"){let h=e?.type==="linear"?e:null,g=t===null?-1:this.config.stepIds.indexOf(t);l={...o,type:"linear",currentStep:i===null?null:E({...i,index:g,isFirstStep:g===0,isLastStep:g===this.config.stepIds.length-1},h?.currentStep),steps:E({totalSteps:this.config.stepIds.length,stepOrder:this.frozenStepOrder,visitedStepCount:s},h?.steps),plugins:Y}}else{let h=[],g=[],y=[],f=[],p=new Map,v=new Set,b=!1;if(t!==null)for(let T of this.config.transitions){if(T.from!==t)continue;b=!0,h.includes(T.event)||h.push(T.event);let O=p.get(T.event)??0;p.set(T.event,O+1);let J=this.isEnabled(T),H=J&&!v.has(T.event);H&&v.add(T.event),f.push(Object.freeze({event:T.event,to:T.to,priority:O,guard:ie(T.when,J),enabled:J,selected:H})),J&&(g.includes(T.event)||g.push(T.event),y.includes(T.to)||y.push(T.to))}let k=e?.type==="graph"?e:null;l={...o,type:"graph",currentStep:i===null?null:E({...i,isTerminal:!b},k?.currentStep),steps:E({totalSteps:this.config.stepIds.length,visitedStepCount:s},k?.steps),declaredEvents:P(h,k?.declaredEvents),availableEvents:P(g,k?.availableEvents),availableSteps:P(y,k?.availableSteps),outgoingTransitions:P(f,k?.outgoingTransitions,(T,O)=>w(T,O)),plugins:Y}}if(this.snapshotDerivers.size>0){let h={};for(let[y,f]of this.snapshotDerivers)try{h[y]=f(l,this.lastPluginExtensions[y])}catch(p){this.store.report(p),S(this.lastPluginExtensions,y)&&(h[y]=this.lastPluginExtensions[y])}this.lastPluginExtensions=h;let g=e?.plugins;l={...l,plugins:g!=null&&w(g,h)?g:Object.freeze(h)}}let c=Object.freeze(l),d=e!==null&&w(c,e)?e:c;return this.lastSnapshot=d,d}};var K=["context","transition","status"];function q(n){return typeof n!="number"||!Number.isFinite(n)?0:Math.max(0,Math.trunc(n))}function X(n,e){return{status:n.status,context:n.context,timeline:n.history.timeline,currentIndex:n.history.currentIndex,savedAt:e}}var oe=new Set(["idle","running","paused","completed","terminated"]),ae=["__proto__","constructor","prototype"];function G(n,e=0){if(e>50||n===null||typeof n!="object")return n;if(Array.isArray(n))return n.map(r=>G(r,e+1));let t={};for(let[r,s]of Object.entries(n))ae.includes(r)||(t[r]=G(s,e+1));return t}function $(n){if(n===null)return null;let e;try{e=JSON.parse(n)}catch{return null}if(typeof e!="object"||e===null)return null;let t=e;return typeof t.status!="string"||!oe.has(t.status)||!Array.isArray(t.timeline)||!t.timeline.every(r=>typeof r=="string")||typeof t.currentIndex!="number"||!Number.isInteger(t.currentIndex)||typeof t.savedAt!="number"||!Number.isFinite(t.savedAt)?null:{status:t.status,context:G(t.context),timeline:t.timeline,currentIndex:t.currentIndex,savedAt:t.savedAt}}function W(n){if(n.storage)return n.storage;let e;try{e=globalThis.localStorage}catch(t){throw new m("storage-unavailable","localStorage access was blocked by the environment; pass persist.storage explicitly",{},t)}if(!e)throw new m("storage-unavailable","persist.storage is required when localStorage is unavailable");return e}function L(n,e){let t;try{t=W(n).getItem(n.key)}catch{return null}let r=$(t);if(!r||r.status!=="running"&&r.status!=="paused"||!Number.isInteger(r.currentIndex)||r.currentIndex<0||r.currentIndex>=r.timeline.length)return null;for(let s of r.timeline)if(typeof s!="string"||!e(s))return null;return r}var z={status:"idle",lastSavedAt:null,error:null};function ue(n){let e=n.now??Date.now,t=q(n.debounceMs),r=new Set(n.saveOn??K),s=0;return{name:"persistence",setup(a){s+=1,s>1&&V(`journey: persistence plugin instance shared by ${s} machines; they overwrite key "${n.key}". Create one per machine.`);let u=null,o=z,i=null,l=!1,c=p=>{u=p,o={status:"saved",lastSavedAt:p.savedAt,error:null}},d=p=>{o={...o,status:"error",error:p},a.reportError(p)},h=()=>{let p=X(a.getSnapshot(),e());o={...o,status:"saving"};let v;try{v=n.storage.setItem(n.key,JSON.stringify(p))}catch(b){throw o={...o,status:"error",error:b},b}if(v!==void 0)return v.then(()=>c(p),d);c(p)},g=()=>{i!==null&&(clearTimeout(i),i=null)},y=()=>{i=null;try{let p=h()}catch(p){d(p)}},f=p=>{if(!(l||!r.has(p))){if(t===0){h();return}g(),o={...o,status:"pending"},i=setTimeout(y,t)}};return a.onContextChange(()=>f("context")),a.onTransition(()=>f("transition")),a.onStatusChange(({current:p})=>{if(p==="terminated"&&n.clearOnTerminate){g(),u=null,o=z,n.storage.removeItem(n.key);return}f("status")}),a.onDispose(()=>{l=!0,g()}),{api:{inspectPersistedState:()=>u,getPersistenceState:()=>o,readPersisted:()=>$(n.storage.getItem(n.key)),clearPersisted:()=>{g(),u=null,o=z;try{n.storage.removeItem(n.key)}catch(p){d(p)}},flushPersisted:async()=>{g();try{await h()}catch(p){d(p)}}},deriveSnapshot:(p,v)=>v===o?v:o}}}}function M(n){return ue({key:n.key,storage:W(n)})}function Z(n,e={}){if(n.steps.length===0)throw new m("empty-definition","a linear journey needs at least one step");let t=[],r={};for(let o of n.steps){let i=typeof o=="string"?{id:o}:o;if(S(r,i.id))throw new m("duplicate-step-id",`duplicate step id "${i.id}"`,{stepId:i.id});t.push(i.id);let l={metadata:i.metadata??{}};i.onEnter&&(l.onEnter=i.onEnter),i.onLeave&&(l.onLeave=i.onLeave),r[i.id]=l}if(e.startAt!==void 0&&!S(r,e.startAt))throw new m("unknown-step",`startAt references unknown step "${e.startAt}"`,{stepId:e.startAt});let s=e.persist&&e.startAt===void 0?L(e.persist,o=>S(r,o)):null,a=new I({kind:"linear",stepIds:t,steps:r,initial:t[0],...e.startAt!==void 0?{startAt:e.startAt}:{},initialContext:n.context,...s?{restore:{context:s.context,timeline:s.timeline,currentIndex:s.currentIndex}}:{},transitions:[],handlers:void 0,autoStart:e.autoStart??!0,defaultTimeoutMs:e.defaultTimeoutMs,...e.onListenerError!==void 0?{onListenerError:e.onListenerError}:{},plugins:[...e.persist?[M(e.persist)]:[],...e.plugins??[]]}),u=C(a);return{...u,navigate:{...u.navigate,goToStepByIndex:o=>a.goToStepByIndex(o)}}}var le=()=>(n,e={})=>Z(n,e);var ee=(n,e,t)=>{if(typeof n!="string"||n.length===0)throw new m("invalid-label",`${e} must declare "label" as a non-empty string`,t);return n},te=(n,e,t)=>{if(typeof n!="number"||!Number.isFinite(n)||n<=0)throw new m("invalid-timeout",`${e} must declare "timeoutMs" as a finite number greater than 0`,t);return n};function _(n){let e=Object.keys(n.steps);if(e.length===0)throw new m("empty-definition","a graph journey needs at least one step");if(!e.includes(n.initial))throw new m("unknown-initial-step",`initial step "${n.initial}" is not a declared step`,{stepId:n.initial});let t={};for(let u of e){let o=n.steps[u],i={metadata:o.metadata??{}};o.onEnter&&(i.onEnter=o.onEnter),o.onLeave&&(i.onLeave=o.onLeave),t[u]=i}let r=[],s={},a=(u,o,i,l)=>{if(!e.includes(i.to))throw new m("dangling-transition",`transition "${u}" references unknown step "${i.to}"`,{event:u,stepId:i.to});let c=`transition "${u}"[${l}] on step "${o}"`,d={event:u,from:o,to:i.to,index:l};i.when&&(d.when=i.when),i.onTransition&&(d.onTransition=i.onTransition),i.label!==void 0&&(d.label=ee(i.label,c,{event:u,stepId:o})),i.timeoutMs!==void 0&&(d.timeoutMs=te(i.timeoutMs,c,{event:u,stepId:o})),r.push(d)};for(let u of e){let o=n.steps[u]?.on;if(o)for(let[i,l]of Object.entries(o)){if(l===void 0)continue;if(typeof l=="string"){a(i,u,{to:l},0);continue}if(Array.isArray(l)){l.forEach((g,y)=>a(i,u,g,y));continue}let c=l,d=`work on "${i}" on step "${u}"`,h={run:c.run};c.commit&&(h.commit=c.commit),c.label!==void 0&&(h.label=ee(c.label,d,{event:i,stepId:u})),c.timeoutMs!==void 0&&(h.timeoutMs=te(c.timeoutMs,d,{event:i,stepId:u})),s[A(u,i)]=h,c.candidates.forEach((g,y)=>a(i,u,g,y))}}return{stepIds:e,steps:t,transitions:r,eventWork:s}}function ne(n,e={}){let{stepIds:t,steps:r,transitions:s,eventWork:a}=_(n);if(e.startAt!==void 0&&!S(r,e.startAt))throw new m("unknown-step",`startAt references unknown step "${e.startAt}"`,{stepId:e.startAt});let u=e.persist&&e.startAt===void 0?L(e.persist,l=>S(r,l)):null,o=new I({kind:"graph",stepIds:t,steps:r,initial:n.initial,...e.startAt!==void 0?{startAt:e.startAt}:{},initialContext:n.context,...u?{restore:{context:u.context,timeline:u.timeline,currentIndex:u.currentIndex}}:{},transitions:s,...Object.keys(a).length>0?{eventWork:a}:{},handlers:e.handlers??n.handlers,autoStart:e.autoStart??!0,defaultTimeoutMs:e.defaultTimeoutMs,...e.onListenerError!==void 0?{onListenerError:e.onListenerError}:{},plugins:[...e.persist?[M(e.persist)]:[],...e.plugins??[]]});return{...C(o),send:(l,c)=>o.send(l,c)}}var pe=()=>(n,e={})=>ne(n,e);var ce=()=>n=>n;function de(n){let e=[],t=[...n.stepIds],r=new Set,s=new Set;n.transitions.forEach((f,p)=>{let v=`${f.from} ${f.event}`;if(s.has(v)){r.add(p),e.push({code:"shadowed-transition",severity:"warning",from:f.from,event:f.event,message:`Transition "${f.from}.${f.event}" is shadowed by an earlier unconditional candidate.`});return}f.guarded||s.add(v)});let a=n.transitions.filter((f,p)=>!r.has(p)),u=f=>a.filter(p=>p.from===f),o=new Set,i=[n.initial];for(;i.length>0;){let f=i.pop();if(!o.has(f)){o.add(f);for(let p of u(f))i.push(p.to)}}for(let f of t)o.has(f)||e.push({code:"unreachable-step",severity:"warning",stepId:f,message:`Step "${f}" is unreachable from the initial step "${n.initial}".`});let l=t.filter(f=>u(f).length===0),c=new Set,d=new Set,h=[],g=f=>{d.add(f),h.push(f);for(let p of u(f)){let v=h.indexOf(p.to);if(v>=0){let b=[...h.slice(v),p.to],k=b.join(" ");c.has(k)||(c.add(k),e.push({code:"cycle-detected",severity:"warning",from:p.from,event:p.event,steps:b,message:`Cycle detected: ${b.join(" -> ")}.`}));continue}d.has(p.to)||g(p.to)}h.pop()};g(n.initial);let y=l.some(f=>o.has(f));return y||e.push({code:"no-terminal-path",severity:"warning",stepId:n.initial,message:`No terminal step is reachable from the initial step "${n.initial}".`}),{issues:e,summary:{stepCount:t.length,reachableStepCount:o.size,unreachableStepCount:t.length-o.size,terminalStepIds:l,cycleCount:c.size,shadowedTransitionCount:r.size,terminalPathExists:y}}}function he(n){let{stepIds:e,transitions:t}=_(n),r={kind:"graph",stepIds:e,initial:n.initial,transitions:t.map(s=>({event:s.event,from:s.from,to:s.to,guarded:s.when!==void 0,label:s.label??null,index:s.index}))};return de(r)}export{m as JourneyError,he as analyzeStructure,ne as createGraphJourney,Z as createLinearJourney,ce as defineWork,re as isJourneyError,pe as withGraphTypes,le as withLinearTypes};
2
2
  //# sourceMappingURL=index.js.map