@fanfare-io/fanfare-sdk-core 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{createError as t}from"../core/errors.js";function e(t){return new o(t)}function i(e){const{distribution:i,consumer:n,outcome:o}=e;if(!i)return{phase:"unavailable"};const a=function(e){if("appointment"!==e.type)throw t.validationError("Appointment driver received a non-appointment distribution");return e}(i),r=n&&"appointment"===n.mechanism?function(t){const{mechanism:e,...i}=t;return i}(n):function(t){return{status:"not_booked",distributionId:t.id}}(i),p=o??function(t){return"completed"===t.status?{type:"completed"}:"cancelled"===t.status?{type:"cancelled",..."killed"===t.cancelledCause?{reason:"killed"}:{}}:void 0}(r);return p?s(a,r,p):function(t){return"completed"!==t.status&&"cancelled"!==t.status&&"not_booked"!==t.status}(r)?{phase:"participating",mechanism:"appointment",distribution:a,consumer:r}:"scheduled"===a.lifecycle?{phase:"scheduled",mechanism:"appointment",distribution:a}:"open"===a.lifecycle?{phase:"enterable",mechanism:"appointment",distribution:a,consumer:r}:s(a,r,{type:"closed"})}function n(t,e){return t&&"appointment"===t.mechanism&&e?"cancelled"===e.type?{...t,status:"cancelled",cancelledCause:"killed"===e.reason?"killed":t.cancelledCause}:"completed"===e.type?{...t,status:"completed"}:t:t}class o{constructor(t){this.deps=t,this.mechanism="appointment"}project(t){return i(t)}buildView(e,i){if("unavailable"===e.phase)return{phase:"unavailable",reason:e.reason};const n=function(e){if(!("mechanism"in e)||"appointment"!==e.mechanism)throw t.validationError("Appointment driver received a non-appointment sequence");return e}(e);switch(n.phase){case"scheduled":return{phase:"scheduled",mechanism:"appointment",distribution:n.distribution,startsAt:n.distribution.startsAt??n.distribution.opensAt};case"enterable":return{phase:"enterable",mechanism:"appointment",distribution:n.distribution,consumer:n.consumer,book:(t,e)=>this.book(t,e)};case"participating":return{phase:"participating",mechanism:"appointment",consumer:n.consumer,display$:i.displayAtom,cancel:()=>this.cancel(),reschedule:(t,e)=>this.reschedule(t,e)};case"ended":return{phase:"ended",mechanism:"appointment",consumer:n.consumer,outcome:n.outcome}}}startMonitoring(t){this.activeMonitorId=t.participation.id,this.deps.appointments.startMonitoring(t.participation.id,{displayAtom:t.displayAtom},t.onUpdate,t.onDegradedChange)}stopMonitoring(){this.activeMonitorId&&(this.deps.appointments.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async book(t,e){await this.deps.runSerializedOperation(async()=>{const n=this.currentAppointmentSequence("enterable","No open appointment to book").distribution,o=await this.deps.appointments.book(n.id,t,e);this.deps.appointments.bumpMonitorGeneration(n.id),this.writeBookingDisplay(n.id,o),this.deps.commit(i({distribution:n,consumer:{mechanism:"appointment",status:"booked",id:n.id,distributionId:n.id,slotId:o.slotId,locationId:o.locationId}})),this.deps.emitEvent("sequence_change","success","user","Booked appointment slot",{appointmentId:n.id,slotId:t,locationId:e})})}async cancel(){await this.deps.runSerializedOperation(async()=>{const e=this.currentAppointmentSequence("participating"),i=e.consumer,n=this.deps.getDisplayState(),o=n&&"appointment"===n.type?n.booking:void 0;if(!o)throw t.validationError("No booking details available to cancel");const s=i.distributionId??i.id??e.distribution.id;await this.deps.appointments.cancel(s,o.slotId,o.locationId??""),this.deps.stopRuntimeMonitoring(!0),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"cancelled"}})})}async reschedule(t,e){await this.deps.runSerializedOperation(async()=>{const n=this.currentAppointmentSequence("participating"),o=n.consumer,s=o.distributionId??o.id??n.distribution.id,a=await this.deps.appointments.reschedule(s,t,e),r=this.deps.getCurrentSequence();r&&("participating"===r.phase&&"mechanism"in r&&"appointment"===r.mechanism&&r.distribution.id===n.distribution.id?(this.writeBookingDisplay(s,a),this.deps.commit(i({distribution:n.distribution,consumer:{...o,mechanism:"appointment",status:a.consumerStatus,slotId:a.slotId,locationId:a.locationId,cancelledCause:a.cancelledCause}})),this.deps.emitEvent("sequence_change","info","user","Rescheduled appointment",{appointmentId:s,newSlotId:t,newLocationId:e})):this.deps.emitEvent("info","info","system","Reschedule receipt superseded by a terminal state",{appointmentId:s,newSlotId:t}))})}writeBookingDisplay(t,e){const i=this.deps.ensureDisplayAtom({id:t,type:"appointment"}),n=Math.max(0,Date.parse(e.startTime)-Date.now());i.set({type:"appointment",booking:{slotId:e.slotId,locationId:e.locationId,locationName:e.locationName,startTime:e.startTime,endTime:e.endTime,confirmationCode:e.confirmationCode,consumerStatus:e.consumerStatus,cancelledCause:e.cancelledCause},msUntilStart:n})}currentAppointmentSequence(e,i="Not participating in an appointment"){const n=this.deps.getCurrentSequence();if(!n||n.phase!==e||!("mechanism"in n)||"appointment"!==n.mechanism)throw t.validationError(i);return n}}function s(t,e,i){return{phase:"ended",mechanism:"appointment",distribution:t,consumer:e,outcome:{...i,distributionType:i.distributionType??t.type,distributionId:i.distributionId??t.id,at:i.at??/* @__PURE__ */(new Date).toISOString()}}}export{e as createAppointmentDriver,i as projectAppointmentSequence,n as withAppointmentTerminalConsumer};
1
+ import{createError as t}from"../core/errors.js";function e(t){return new o(t)}function i(e){const{distribution:i,consumer:n,outcome:o}=e;if(!i)return{phase:"unavailable"};const a=function(e){if("appointment"!==e.type)throw t.validationError("Appointment driver received a non-appointment distribution");return e}(i),r=n&&"appointment"===n.mechanism?function(t){const{mechanism:e,...i}=t;return i}(n):function(t){return{status:"not_booked",distributionId:t.id}}(i),c=o??function(t){return"completed"===t.status?{type:"completed"}:"cancelled"===t.status?{type:"cancelled",..."killed"===t.cancelledCause?{reason:"killed"}:{}}:void 0}(r);return c?s(a,r,c):function(t){return"completed"!==t.status&&"cancelled"!==t.status&&"not_booked"!==t.status}(r)?{phase:"participating",mechanism:"appointment",distribution:a,consumer:r}:"scheduled"===a.lifecycle||"open"===a.lifecycle?{phase:"enterable",mechanism:"appointment",distribution:a,consumer:r}:s(a,r,{type:"closed"})}function n(t,e){return t&&"appointment"===t.mechanism&&e?"cancelled"===e.type?{...t,status:"cancelled",cancelledCause:"killed"===e.reason?"killed":t.cancelledCause}:"completed"===e.type?{...t,status:"completed"}:t:t}class o{constructor(t){this.deps=t,this.mechanism="appointment"}project(t){return i(t)}buildView(e,i){if("unavailable"===e.phase)return{phase:"unavailable",reason:e.reason};const n=function(e){if(!("mechanism"in e)||"appointment"!==e.mechanism)throw t.validationError("Appointment driver received a non-appointment sequence");return e}(e);switch(n.phase){case"enterable":return{phase:"enterable",mechanism:"appointment",distribution:n.distribution,consumer:n.consumer,book:(t,e)=>this.book(t,e)};case"participating":return{phase:"participating",mechanism:"appointment",consumer:n.consumer,display$:i.displayAtom,cancel:()=>this.cancel(),reschedule:(t,e)=>this.reschedule(t,e)};case"ended":return{phase:"ended",mechanism:"appointment",consumer:n.consumer,outcome:n.outcome}}}startMonitoring(t){this.activeMonitorId=t.participation.id,this.deps.appointments.startMonitoring(t.participation.id,{displayAtom:t.displayAtom},t.onUpdate,t.onDegradedChange)}stopMonitoring(){this.activeMonitorId&&(this.deps.appointments.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async book(t,e){await this.deps.runSerializedOperation(async()=>{const n=this.currentAppointmentSequence("enterable","No open appointment to book").distribution,o=await this.deps.appointments.book(n.id,t,e);this.deps.appointments.bumpMonitorGeneration(n.id),this.writeBookingDisplay(n.id,o),this.deps.commit(i({distribution:n,consumer:{mechanism:"appointment",status:"booked",id:n.id,distributionId:n.id,slotId:o.slotId,locationId:o.locationId}})),this.deps.emitEvent("sequence_change","success","user","Booked appointment slot",{appointmentId:n.id,slotId:t,locationId:e})})}async cancel(){await this.deps.runSerializedOperation(async()=>{const e=this.currentAppointmentSequence("participating"),i=e.consumer,n=this.deps.getDisplayState(),o=n&&"appointment"===n.type?n.booking:void 0;if(!o)throw t.validationError("No booking details available to cancel");const s=i.distributionId??i.id??e.distribution.id;await this.deps.appointments.cancel(s,o.slotId,o.locationId??""),this.deps.stopRuntimeMonitoring(!0),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"cancelled"}})})}async reschedule(t,e){await this.deps.runSerializedOperation(async()=>{const n=this.currentAppointmentSequence("participating"),o=n.consumer,s=o.distributionId??o.id??n.distribution.id,a=await this.deps.appointments.reschedule(s,t,e),r=this.deps.getCurrentSequence();r&&("participating"===r.phase&&"mechanism"in r&&"appointment"===r.mechanism&&r.distribution.id===n.distribution.id?(this.writeBookingDisplay(s,a),this.deps.commit(i({distribution:n.distribution,consumer:{...o,mechanism:"appointment",status:a.consumerStatus,slotId:a.slotId,locationId:a.locationId,cancelledCause:a.cancelledCause}})),this.deps.emitEvent("sequence_change","info","user","Rescheduled appointment",{appointmentId:s,newSlotId:t,newLocationId:e})):this.deps.emitEvent("info","info","system","Reschedule receipt superseded by a terminal state",{appointmentId:s,newSlotId:t}))})}writeBookingDisplay(t,e){const i=this.deps.ensureDisplayAtom({id:t,type:"appointment"}),n=Math.max(0,Date.parse(e.startTime)-Date.now());i.set({type:"appointment",booking:{slotId:e.slotId,locationId:e.locationId,locationName:e.locationName,startTime:e.startTime,endTime:e.endTime,confirmationCode:e.confirmationCode,consumerStatus:e.consumerStatus,cancelledCause:e.cancelledCause},msUntilStart:n})}currentAppointmentSequence(e,i="Not participating in an appointment"){const n=this.deps.getCurrentSequence();if(!n||n.phase!==e||!("mechanism"in n)||"appointment"!==n.mechanism)throw t.validationError(i);return n}}function s(t,e,i){return{phase:"ended",mechanism:"appointment",distribution:t,consumer:e,outcome:{...i,distributionType:i.distributionType??t.type,distributionId:i.distributionId??t.id,at:i.at??/* @__PURE__ */(new Date).toISOString()}}}export{e as createAppointmentDriver,i as projectAppointmentSequence,n as withAppointmentTerminalConsumer};
@@ -1 +1 @@
1
- import*as t from"valibot";import{isFanfareError as o,createError as e}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{parseResponse as i}from"../core/parse-response.js";import{createPollScheduler as r}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as s}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as a}from"../state/events.js";function l(t,o,e){const n=i(t,o,{endpoint:e},{throwOnFailure:!0});if(!n.ok)throw n.error;return n.value}const d=t.object({id:t.string(),startTime:t.string(),endTime:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),timezone:t.nullish(t.string()),capacity:t.number(),available:t.number()}),c=t.object({slots:t.array(d)}),m=t.object({id:t.string(),type:t.literal("appointment"),openAt:t.nullish(t.string()),closeAt:t.nullish(t.string()),slotDurationMinutes:t.nullish(t.number()),timeZone:t.nullish(t.string()),locationId:t.nullish(t.string()),totalSlots:t.nullish(t.number()),availableSlots:t.nullish(t.number())}),p=t.string(),h=t.picklist(["consumer","admin","killed"]),u=t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.string(),endTime:t.string(),confirmationCode:t.string(),status:p,cancelledCause:t.nullish(h),createdAt:t.string()}),g=t.object({booking:t.nullish(t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.nullish(t.string()),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.nullish(t.string()),endTime:t.nullish(t.string()),confirmationCode:t.nullish(t.string()),status:p,cancelledCause:t.nullish(h)}))}),I=t.object({booking:u,bookingId:t.string(),previousSlotId:t.string(),newSlotId:t.string(),confirmationCode:t.string(),status:t.string(),cancelledCause:t.nullish(h)});function b(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId,locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime,endTime:t.endTime,confirmationCode:t.confirmationCode,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}function k(t){return{id:t.id,startTime:t.startTime,endTime:t.endTime,locationId:t.locationId??void 0,locationName:t.locationName??void 0,timezone:t.timezone??void 0,capacity:t.capacity,available:t.available}}class w{constructor(t,o){this.logger=n(),this.monitorRuntime=new s,this.pollSchedulers=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.observedBooking=/* @__PURE__ */new Map,this.destroyed=!1,this.http=t,this.pollingIntervalMs=o?.pollingIntervalMs??3e4}async get(t,n){try{const o=await this.http.get(`/appointments/${t}`,n),e=l(m,o,`/appointments/${t}`);return{id:e.id,type:"appointment",openAt:e.openAt??void 0,closeAt:e.closeAt??void 0,slotDurationMinutes:e.slotDurationMinutes??void 0,bookingWindowOpenAt:e.openAt??void 0,bookingWindowCloseAt:e.closeAt??void 0,timezone:e.timeZone??void 0,locationId:e.locationId??void 0,totalSlots:e.totalSlots??void 0,availableSlots:e.availableSlots??void 0}}catch(i){if(this.logger.error("Failed to get appointment",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get appointment",{error:i})}}async getSlots(t,n,i){try{const o=new URLSearchParams({startDate:n.startDate,endDate:n.endDate}),e=await this.http.get(`/appointments/${t}/slots?${o.toString()}`,i);return l(c,e,`/appointments/${t}/slots`).slots.map(k)}catch(r){if(this.logger.error("Failed to get appointment slots",{appointmentId:t,error:r}),o(r))throw r;throw e.networkError("Failed to get appointment slots",{error:r})}}async getMe(t){try{const o=await this.http.get(`/appointments/${t}/me`),e=l(g,o,`/appointments/${t}/me`);return e.booking?function(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId??"",locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime??"",endTime:t.endTime??"",confirmationCode:t.confirmationCode??void 0,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}(e.booking):null}catch(i){if(this.logger.error("Failed to get current booking",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get current booking",{error:i})}}async book(t,n,i,r,s){try{const o=await this.http.post(`/appointments/${t}/book`,{slotId:n,locationId:i??null},r),e=b(l(u,o,`/appointments/${t}/book`));return a().emit("experience:distribution-entered",{distributionType:"appointment",distributionId:t,...s}),e}catch(d){if(this.logger.error("Failed to book appointment",{appointmentId:t,slotId:n,locationId:i,error:d}),o(d))throw d;throw e.networkError("Failed to book appointment",{error:d})}}async cancel(t,n,i,r){try{await this.http.post(`/appointments/${t}/cancel`,{slotId:n,locationId:i??null},r)}catch(s){if(this.logger.error("Failed to cancel appointment",{appointmentId:t,slotId:n,locationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to cancel appointment",{error:s})}}async reschedule(t,n,i,r){try{const o=await this.http.post(`/appointments/${t}/reschedule`,{newSlotId:n,newLocationId:i??null},r),e=b(l(I,o,`/appointments/${t}/reschedule`).booking);if(this.isMonitoring(t)){const o=this.monitorRuntime.getDisplayAtom(t);o&&o.set({type:"appointment",booking:e,msUntilStart:Math.max(0,Date.parse(e.startTime)-Date.now())}),this.startMonitor(t,o?{displayAtom:o}:void 0,void 0,this.pollSchedulers.get(t)?.onDegradedChange,e)}return e}catch(s){if(this.logger.error("Failed to reschedule appointment",{appointmentId:t,newSlotId:n,newLocationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to reschedule appointment",{error:s})}}startMonitoring(t,o,e,n){this.startMonitor(t,o,e,n)}startMonitor(t,o,e,n,i){if(this.destroyed)return;this.monitorRuntime.start(t,o,e),this.stopPolling(t);const s=this.monitorRuntime.getDisplayAtom(t)?.get();this.observedBooking.set(t,"appointment"===s?.type&&void 0!==s.booking);const a=(this.monitorGenerations.get(t)??0)+1;this.monitorGenerations.set(t,a);const l=r({baseMs:this.pollingIntervalMs,suppressBackoff:()=>!1,onDegradedChange:n,shouldContinue:()=>!this.destroyed&&this.monitorGenerations.get(t)===a&&this.pollSchedulers.has(t),run:async()=>{if(!this.destroyed&&this.monitorGenerations.get(t)===a)try{const o=i??await this.getMe(t);if(i=void 0,this.destroyed||this.monitorGenerations.get(t)!==a)return;const e=this.monitorRuntime.getDisplayAtom(t);if(!o)return void(this.observedBooking.get(t)&&(this.monitorRuntime.notify(t,{type:"ended",outcome:{type:"closed"}}),this.stopMonitoring(t)));if(this.observedBooking.set(t,!0),"cancelled"===o.consumerStatus||"completed"===o.consumerStatus)return this.monitorRuntime.notify(t,{type:"ended",outcome:{type:o.consumerStatus,..."cancelled"===o.consumerStatus&&"killed"===o.cancelledCause?{reason:"killed"}:{}}}),void this.stopMonitoring(t);const n=Math.max(0,Date.parse(o.startTime)-Date.now());if(!e)return;e.set({type:"appointment",booking:{slotId:o.slotId,locationId:o.locationId,locationName:o.locationName,startTime:o.startTime,endTime:o.endTime,confirmationCode:o.confirmationCode,consumerStatus:o.consumerStatus,cancelledCause:o.cancelledCause},msUntilStart:n})}catch(o){throw this.logger.error("Appointment polling tick failed",{id:t,error:o}),o}}});this.pollSchedulers.set(t,{scheduler:l,onDegradedChange:n}),l.start()}stopMonitoring(t){this.stopPolling(t),this.monitorRuntime.stop(t),this.observedBooking.delete(t),this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}bumpMonitorGeneration(t){this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}destroy(){this.destroyed=!0,this.monitorGenerations.clear(),this.observedBooking.clear();for(const t of Array.from(this.pollSchedulers.keys()))this.stopPolling(t);this.monitorRuntime.clear()}stopPolling(t){const o=this.pollSchedulers.get(t);void 0!==o&&(o.scheduler.stop(),this.pollSchedulers.delete(t))}}export{w as AppointmentManagementModule};
1
+ import*as t from"valibot";import{isFanfareError as o,createError as e}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{parseResponse as i}from"../core/parse-response.js";import{createPollScheduler as s}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as a}from"../state/events.js";function l(t,o,e){const n=i(t,o,{endpoint:e},{throwOnFailure:!0});if(!n.ok)throw n.error;return n.value}const d=t.object({id:t.string(),startTime:t.string(),endTime:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),timezone:t.nullish(t.string()),capacity:t.number(),available:t.number()}),c=t.object({slots:t.array(d)}),p=t.object({id:t.string(),type:t.literal("appointment"),openAt:t.nullish(t.string()),closeAt:t.nullish(t.string()),slotDurationMinutes:t.nullish(t.number()),timeZone:t.nullish(t.string()),locationId:t.nullish(t.string()),totalSlots:t.nullish(t.number()),availableSlots:t.nullish(t.number())}),m=t.string(),h=t.picklist(["consumer","admin","killed"]),u=t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.string(),endTime:t.string(),confirmationCode:t.string(),status:m,cancelledCause:t.nullish(h),createdAt:t.string()}),g=t.object({booking:t.nullish(t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.nullish(t.string()),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.nullish(t.string()),endTime:t.nullish(t.string()),confirmationCode:t.nullish(t.string()),status:m,cancelledCause:t.nullish(h)}))}),b=t.object({booking:u,bookingId:t.string(),previousSlotId:t.string(),newSlotId:t.string(),confirmationCode:t.string(),status:t.string(),cancelledCause:t.nullish(h)});function I(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId,locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime,endTime:t.endTime,confirmationCode:t.confirmationCode,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}function k(t){return{id:t.id,startTime:t.startTime,endTime:t.endTime,locationId:t.locationId??void 0,locationName:t.locationName??void 0,timezone:t.timezone??void 0,capacity:t.capacity,available:t.available}}class y{constructor(t,o){this.logger=n(),this.monitorRuntime=new r,this.pollSchedulers=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.observedBooking=/* @__PURE__ */new Map,this.destroyed=!1,this.http=t,this.pollingIntervalMs=o?.pollingIntervalMs??3e4}async get(t,n){try{const o=await this.http.get(`/appointments/${t}`,n),e=l(p,o,`/appointments/${t}`);return{id:e.id,type:"appointment",openAt:e.openAt??void 0,closeAt:e.closeAt??void 0,slotDurationMinutes:e.slotDurationMinutes??void 0,bookingWindowOpenAt:e.openAt??void 0,bookingWindowCloseAt:e.closeAt??void 0,timezone:e.timeZone??void 0,locationId:e.locationId??void 0,totalSlots:e.totalSlots??void 0,availableSlots:e.availableSlots??void 0}}catch(i){if(this.logger.error("Failed to get appointment",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get appointment",{error:i})}}async getSlots(t,n,i){try{const o=new URLSearchParams({startDate:n.startDate,endDate:n.endDate}),e=await this.http.get(`/appointments/${t}/slots?${o.toString()}`,i);return l(c,e,`/appointments/${t}/slots`).slots.map(k)}catch(s){if(this.logger.error("Failed to get appointment slots",{appointmentId:t,error:s}),o(s))throw s;throw e.networkError("Failed to get appointment slots",{error:s})}}async getMe(t){try{const o=await this.http.get(`/appointments/${t}/me`),e=l(g,o,`/appointments/${t}/me`);return e.booking?function(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId??"",locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime??"",endTime:t.endTime??"",confirmationCode:t.confirmationCode??void 0,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}(e.booking):null}catch(i){if(this.logger.error("Failed to get current booking",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get current booking",{error:i})}}async book(t,n,i,s,r){const d=this.bumpOperationGeneration(t);try{const o=await this.http.post(`/appointments/${t}/book`,{slotId:n,locationId:i??null},s),e=I(l(u,o,`/appointments/${t}/book`));return this.canApplyAsyncResult(t,d)&&a().emit("experience:distribution-entered",{distributionType:"appointment",distributionId:t,...r}),e}catch(c){if(this.logger.error("Failed to book appointment",{appointmentId:t,slotId:n,locationId:i,error:c}),o(c))throw c;throw e.networkError("Failed to book appointment",{error:c})}}async cancel(t,n,i,s){this.bumpOperationGeneration(t);try{await this.http.post(`/appointments/${t}/cancel`,{slotId:n,locationId:i??null},s)}catch(r){if(this.logger.error("Failed to cancel appointment",{appointmentId:t,slotId:n,locationId:i,error:r}),o(r))throw r;throw e.networkError("Failed to cancel appointment",{error:r})}}async reschedule(t,n,i,s){const r=this.bumpOperationGeneration(t),a=this.monitorGenerations.get(t)??0;try{const o=await this.http.post(`/appointments/${t}/reschedule`,{newSlotId:n,newLocationId:i??null},s),e=I(l(b,o,`/appointments/${t}/reschedule`).booking);if(this.canApplyAsyncResult(t,r,a)&&this.isMonitoring(t)){const o=this.monitorRuntime.getDisplayAtom(t);o&&o.set({type:"appointment",booking:e,msUntilStart:Math.max(0,Date.parse(e.startTime)-Date.now())}),this.startMonitor(t,o?{displayAtom:o}:void 0,void 0,this.pollSchedulers.get(t)?.onDegradedChange,e)}return e}catch(d){if(this.logger.error("Failed to reschedule appointment",{appointmentId:t,newSlotId:n,newLocationId:i,error:d}),o(d))throw d;throw e.networkError("Failed to reschedule appointment",{error:d})}}startMonitoring(t,o,e,n){this.startMonitor(t,o,e,n)}startMonitor(t,o,e,n,i){if(this.destroyed)return;this.monitorRuntime.start(t,o,e),this.stopPolling(t);const r=this.monitorRuntime.getDisplayAtom(t)?.get();this.observedBooking.set(t,"appointment"===r?.type&&void 0!==r.booking);const a=(this.monitorGenerations.get(t)??0)+1;this.monitorGenerations.set(t,a);const l=s({baseMs:this.pollingIntervalMs,suppressBackoff:()=>!1,onDegradedChange:n,shouldContinue:()=>!this.destroyed&&this.monitorGenerations.get(t)===a&&this.pollSchedulers.has(t),run:async()=>{if(!this.destroyed&&this.monitorGenerations.get(t)===a)try{const o=i??await this.getMe(t);if(i=void 0,this.destroyed||this.monitorGenerations.get(t)!==a)return;const e=this.monitorRuntime.getDisplayAtom(t);if(!o)return void(this.observedBooking.get(t)&&(this.monitorRuntime.notify(t,{type:"ended",outcome:{type:"closed"}}),this.stopMonitoring(t)));if(this.observedBooking.set(t,!0),"cancelled"===o.consumerStatus||"completed"===o.consumerStatus)return this.monitorRuntime.notify(t,{type:"ended",outcome:{type:o.consumerStatus,..."cancelled"===o.consumerStatus&&"killed"===o.cancelledCause?{reason:"killed"}:{}}}),void this.stopMonitoring(t);const n=Math.max(0,Date.parse(o.startTime)-Date.now());if(!e)return;e.set({type:"appointment",booking:{slotId:o.slotId,locationId:o.locationId,locationName:o.locationName,startTime:o.startTime,endTime:o.endTime,confirmationCode:o.confirmationCode,consumerStatus:o.consumerStatus,cancelledCause:o.cancelledCause},msUntilStart:n})}catch(o){throw this.logger.error("Appointment polling tick failed",{id:t,error:o}),o}}});this.pollSchedulers.set(t,{scheduler:l,onDegradedChange:n}),l.start()}stopMonitoring(t){this.stopPolling(t),this.monitorRuntime.stop(t),this.observedBooking.delete(t),this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}bumpMonitorGeneration(t){this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}bumpOperationGeneration(t){const o=(this.operationGenerations.get(t)??0)+1;return this.operationGenerations.set(t,o),o}canApplyAsyncResult(t,o,e){return!this.destroyed&&((void 0===o||this.operationGenerations.get(t)===o)&&(void 0===e||(this.monitorGenerations.get(t)??0)===e))}destroy(){this.destroyed=!0,this.monitorGenerations.clear(),this.operationGenerations.clear(),this.observedBooking.clear();for(const t of Array.from(this.pollSchedulers.keys()))this.stopPolling(t);this.monitorRuntime.clear()}stopPolling(t){const o=this.pollSchedulers.get(t);void 0!==o&&(o.scheduler.stop(),this.pollSchedulers.delete(t))}}export{y as AppointmentManagementModule};
@@ -36,33 +36,21 @@ export interface AppointmentSequenceConsumerState {
36
36
  /**
37
37
  * An appointment sequence as seen by one consumer.
38
38
  *
39
- * Lifecycle: scheduled -> enterable -> participating -> ended. Appointment
39
+ * Lifecycle: enterable -> participating -> ended. Appointment
40
40
  * sequences never have `settling` or `granted` phases; booking creates the
41
41
  * participation and terminal outcomes are observed directly.
42
42
  */
43
- export type AppointmentSequence = AppointmentScheduled | AppointmentEnterable | AppointmentParticipating | AppointmentEnded;
43
+ export type AppointmentSequence = AppointmentEnterable | AppointmentParticipating | AppointmentEnded;
44
44
  /**
45
- * The appointment exists but booking has not opened yet.
46
- */
47
- export interface AppointmentScheduled {
48
- /** Mechanism discriminator for appointment narrowing. */
49
- mechanism: "appointment";
50
- /** Phase discriminator; scheduled appointments cannot be booked yet. */
51
- phase: "scheduled";
52
- /** Appointment distribution facts from the journey projector. */
53
- distribution: DistributionSummary & {
54
- type: "appointment";
55
- };
56
- }
57
- /**
58
- * Booking is open and this consumer has not booked an appointment.
45
+ * Booking is available whenever the appointment is not ended and this consumer has not booked.
46
+ * Calendar semantics: the distribution window bounds slots, not when booking may happen.
59
47
  */
60
48
  export interface AppointmentEnterable {
61
49
  /** Mechanism discriminator for appointment narrowing. */
62
50
  mechanism: "appointment";
63
51
  /** Phase discriminator; `book()` is legal on the matching view. */
64
52
  phase: "enterable";
65
- /** Open appointment distribution facts from the journey projector. */
53
+ /** Appointment distribution facts from the journey projector. */
66
54
  distribution: DistributionSummary & {
67
55
  type: "appointment";
68
56
  };
@@ -108,22 +96,7 @@ export interface AppointmentEnded {
108
96
  * only on the phases where they are legal, and each action may reject if the
109
97
  * server has moved the sequence before the request is handled.
110
98
  */
111
- export type AppointmentSequenceView = AppointmentScheduledView | AppointmentEnterableView | AppointmentParticipatingView | AppointmentEndedView;
112
- /**
113
- * Scheduled appointment view rendered before booking opens.
114
- */
115
- export interface AppointmentScheduledView {
116
- /** Phase discriminator; no appointment action is legal yet. */
117
- phase: "scheduled";
118
- /** Mechanism discriminator for appointment rendering. */
119
- mechanism: "appointment";
120
- /** Appointment distribution facts from the journey projector. */
121
- distribution: DistributionSummary & {
122
- type: "appointment";
123
- };
124
- /** ISO 8601 opening timestamp chosen from the distribution start/open time. */
125
- startsAt?: string;
126
- }
99
+ export type AppointmentSequenceView = AppointmentEnterableView | AppointmentParticipatingView | AppointmentEndedView;
127
100
  /**
128
101
  * Enterable appointment view rendered when the consumer can book a slot.
129
102
  */
@@ -132,7 +105,7 @@ export interface AppointmentEnterableView {
132
105
  phase: "enterable";
133
106
  /** Mechanism discriminator for appointment rendering. */
134
107
  mechanism: "appointment";
135
- /** Open appointment distribution facts from the journey projector. */
108
+ /** Appointment distribution facts from the journey projector. */
136
109
  distribution: DistributionSummary & {
137
110
  type: "appointment";
138
111
  };
@@ -1 +1 @@
1
- import{AuctionDetailsResponseSchema as t,AuctionBidderStateResponseSchema as i}from"@fanfare-io/fanfare-sdk-contracts/auction";import*as e from"valibot";import{createError as s}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{isMoneyString as a,isMoneyGreaterThan as o,normalizeMoney as r}from"../core/money.js";import{parseResponse as d}from"../core/parse-response.js";import{createPollScheduler as c}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as u}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as l}from"../state/events.js";import{getSDKStore as h}from"../state/store.js";import{isGrantPendingWin as p,toAuctionConsumerStatus as g,isTerminalAuctionStatus as m}from"./auction-status.js";import{assertBidInputMatchesMechanism as y}from"./auction.driver.js";import{advanceDutchPricing as S}from"./dutch-pricing.js";const f=/* @__PURE__ */Symbol("auction-participation-missing");function v(t){return"won"===t.status&&!t.admissionGrant&&!t.outcome}function b(t){return void 0!==t&&m(t.status)&&!v(t)}function A(t){return"english"===t.auctionType?r(t.highestBid):r(t.currentPrice)}const w=e.object({productId:e.string(),variantId:e.optional(e.string())});class D{constructor(t){this.logger=n(),this.events=l(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.pollBaseMs=/* @__PURE__ */new Map,this.degradedHandlers=/* @__PURE__ */new Map,this.lastStatusCache=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastSettleAtMs=/* @__PURE__ */new Map,this.visibilityHandlers=/* @__PURE__ */new Map,this.dutchStepTimers=/* @__PURE__ */new Map,this.monitorRuntime=new u,this.auctionDetailsCache=/* @__PURE__ */new Map,this.watchDetails=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.AUCTION_DETAILS_CACHE_TTL=3e4,this.WATCHING_POLL_MS=15e3,this.PARTICIPATING_POLL_MS=5e3,this.NO_BID_END_GRACE_MS=5e3,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return h()}async getAuctionDetailsCached(i,e){const s=this.auctionDetailsCache.get(i),n=void 0!==s&&Date.now()-s.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL,a=void 0!==s&&"error"in s;if(s&&n&&(!e?.bypassCache||a)){if(void 0!==s.details)return s.details;if("error"in s)throw s.error}try{const e=await this.http.get(`/auctions/${i}`),s=d(t,e,{endpoint:`/auctions/${i}`},{throwOnFailure:!0});if(!s.ok)throw s.error;const n=s.value;return this.auctionDetailsCache.set(i,{details:n,fetchedAt:Date.now()}),n}catch(o){if(this.auctionDetailsCache.set(i,{...void 0!==s?.details?{details:s.details}:{},fetchedAt:Date.now(),error:o}),void 0!==s?.details)return s.details;throw o}}getTrackedParticipation(t){return this.trackedParticipations.get(t)}buildPricing(t,i){return"english"===t.auctionType?{model:"english",...null===t.highestBid?{}:{highestBid:t.highestBid},minNextBid:t.minNextBid,...i?.minBidIncrement?{bidIncrement:i.minBidIncrement}:{},bidCount:t.bidCount,effectiveSettleAt:t.effectiveSettleAt,autoExtended:t.autoExtended,reserveMet:t.reserveMet,ended:t.ended}:{model:"dutch",currentPrice:t.currentPrice,remainingQuantity:t.remainingQuantity,totalQuantity:t.totalQuantity,...null===t.nextDropAt?{}:{nextDropAt:t.nextDropAt},...null===t.floorPrice?{}:{floorPrice:t.floorPrice},settleAt:t.settleAt,...i?.startPrice?{startPrice:i.startPrice}:{},...i?.priceDropAmount?{priceDropAmount:i.priceDropAmount}:{},..."number"==typeof i?.priceDropIntervalSeconds?{priceDropIntervalSeconds:i.priceDropIntervalSeconds}:{},ended:t.ended}}buildDisplayState(t,i,e){const s=("english"===i.auctionType?i.closeAt:null)??e?.closeAt??void 0;return{status:t,...e?.currencyCode?{currencyCode:e.currencyCode}:{},...s?{closeAt:s}:{},pricing:this.buildPricing(i,e)}}emitTransitionEvents(t){for(const i of t)this.events.emit(i.type,i.payload)}applyTransition(t,i){void 0!==i.trackedParticipation&&(i.trackedParticipation?this.trackedParticipations.set(t,i.trackedParticipation):this.trackedParticipations.delete(t));const e=this.monitorRuntime.getDisplayAtom(t);e&&i.displayState&&e.set({type:"auction",...i.displayState}),this.emitTransitionEvents(i.events),i.monitorUpdate&&this.monitorRuntime.notify(t,i.monitorUpdate),i.stopWatching?this.stopWatching(t):i.displayState&&this.armDutchStep(t)}armDutchStep(t){this.clearDutchStep(t);const i=this.monitorRuntime.getDisplayAtom(t),e=i?.get();if(!i||"auction"!==e?.type)return;const s=e.pricing;if("dutch"!==s?.model||s.ended||!s.nextDropAt||void 0===s.priceDropAmount||void 0===s.priceDropIntervalSeconds)return;const n=Date.parse(s.nextDropAt);if(Number.isNaN(n))return;const a=Math.max(0,n-Date.now());if(a>2147483647)return;const o=setTimeout(()=>{this.dutchStepTimers.delete(t);const i=this.monitorRuntime.getDisplayAtom(t),e=i?.get();if(!i||"auction"!==e?.type||"dutch"!==e.pricing?.model)return;const s=S(e.pricing,Date.now());s!==e.pricing&&(i.set({...e,pricing:s}),this.armDutchStep(t))},a+1);this.dutchStepTimers.set(t,o)}clearDutchStep(t){const i=this.dutchStepTimers.get(t);void 0!==i&&(clearTimeout(i),this.dutchStepTimers.delete(t))}checkOutbidConditions(t,i,e){if("outbid"!==i.status)return!1;if("winning"===t.status&&"outbid"===i.status)return!0;if(e?.currentBid&&i.highestBid){const s=e.currentBid,n=i.highestBid,r=t.highestBid||"0";if(a(s)&&a(n)&&a(r))return o(n,r)&&o(n,s)&&"outbid"===i.status}return"outbid"!==t.status&&"outbid"===i.status}buildBidTransition(t,i,e,s,n){const a=i.amount,o=n.isFirstBid?[{type:"auction:entered",payload:{auctionId:t}},{type:"experience:distribution-entered",payload:{distributionType:"auction",distributionId:t,...n.attribution}}]:[],r={type:"auction:bid-placed",payload:{auctionId:t,amount:a,status:i.status}};if("won"===i.status&&!p(i)||"lost"===i.status){const s=this.buildTerminalTransition(t,i.status,i.live,e,{currentBid:a,admissionGrant:i.admissionGrant});return{...s,events:[...o,r,...s.events]}}return{trackedParticipation:{auctionId:t,enteredAt:s?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:a,highestBid:i.highestBid,bidCount:i.bidCount,lastBidAt:/* @__PURE__ */(new Date).toISOString(),metadata:s?.metadata,fingerprint:s?.fingerprint},displayState:this.buildDisplayState(i.status,i.live,e),events:[...o,r,..."winning"===i.status?[{type:"auction:winning",payload:{auctionId:t,amount:a,highestBid:i.highestBid}}]:[],..."outbid"===i.status?[{type:"auction:outbid",payload:{auctionId:t,yourBid:a,highestBid:i.highestBid}}]:[]],monitorUpdate:null,stopWatching:!1}}buildTerminalTransition(t,i,e,s,n){return"won"===i?{trackedParticipation:null,displayState:this.buildDisplayState(i,e,s),events:[{type:"auction:won",payload:{auctionId:t,winningBid:n.currentBid??"",admissionGrant:n.admissionGrant}}],monitorUpdate:n.admissionGrant?{type:"granted",token:n.admissionGrant}:{type:"resync"},stopWatching:!0}:{trackedParticipation:null,displayState:this.buildDisplayState(i,e,s),events:[{type:"auction:lost",payload:{auctionId:t,highestBid:A(e)}}],monitorUpdate:{type:"ended",outcome:{type:"lost"}},stopWatching:!0}}buildStatusTransition(t,i,e,s,{previousParticipation:n,previousStatus:a}){const o=i.currentBid??n?.currentBid,r=i.bidCount??n?.bidCount;if(b(a))return{displayState:null,events:[],monitorUpdate:null,stopWatching:!1};if("won"===i.status&&!v(i)||"lost"===i.status)return this.buildTerminalTransition(t,i.status,e,s,{currentBid:o,admissionGrant:i.admissionGrant});const d=("english"===e.auctionType?e.closeAt:e.settleAt)??s?.closeAt,c=d?new Date(d).getTime():void 0,u=void 0!==c&&Date.now()>=c+this.NO_BID_END_GRACE_MS;if("not_bid"===i.status&&(e.ended||u))return{trackedParticipation:null,displayState:this.buildDisplayState("ended",e,s),events:[],monitorUpdate:{type:"ended",outcome:{type:"closed"}},stopWatching:!0};const l={auctionId:t,enteredAt:n?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:o,highestBid:i.highestBid,bidCount:r,metadata:n?.metadata,fingerprint:n?.fingerprint},h=[];return a&&(this.checkOutbidConditions(a,i,n)&&h.push({type:"auction:outbid",payload:{auctionId:t,yourBid:a.currentBid||n?.currentBid||"",highestBid:i.highestBid}}),"winning"!==a.status&&"winning"===i.status&&h.push({type:"auction:winning",payload:{auctionId:t,amount:o||"",highestBid:i.highestBid}}),a.highestBid!==i.highestBid&&"winning"!==i.status&&h.push({type:"auction:bid-updated",payload:{auctionId:t,highestBid:i.highestBid,bidCount:i.bidCount||0}})),{trackedParticipation:l,displayState:this.buildDisplayState(i.status,e,s),events:h,monitorUpdate:null,stopWatching:!1}}buildMissingTransition(){return{trackedParticipation:null,displayState:{status:"lost"},events:[],monitorUpdate:{type:"ended",outcome:{type:"closed",reason:"not_participating"}},stopWatching:!0}}async get(t){try{return await this.getAuctionDetailsCached(t)}catch(i){throw this.logger.error("Failed to get auction",{auctionId:t,error:i}),i}}async bid(t,n,o,c){try{const u=this.lifecycleGeneration;if(void 0!==n.amount&&!a(n.amount))throw s.validationError("Invalid bid amount format. Must be a numeric string (e.g., '100.00')");if(!this.store.session)throw s.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const l=this.bumpOperationGeneration(t),h=this.pollSchedulers.has(t),m=this.getTrackedParticipation(t),S=!(m&&"not_bid"!==m.status||b(this.lastStatusCache.get(t)));this.logger.info("Placing bid",{auctionId:t,amount:n.amount});const f=`/auctions/${t}/bid`,v=await this.readDetailsForDisplay(t);v&&y(n,v.auctionType);const D=await this.http.post(f,{...void 0===n.amount?{}:{amount:n.amount},...void 0===n.selection?{}:{selection:n.selection}},o),B=d(i,D,{endpoint:f},{throwOnFailure:!0});if(!B.ok)throw B.error;const C=B.value;C.degraded&&this.logger.warn("Bid committed; the credential mint did not complete on this request",{auctionId:t});const I=g(C.status),T=r(C.winningBidAmount??C.lastBidAmount??n.amount,A(C.live)),M="WON"===C.status&&void 0!==C.selection?e.safeParse(w,C.selection):void 0,P=M?.success?M.output:void 0,G={...P?{selection:P}:{},status:I,amount:T,highestBid:A(C.live),bidCount:C.bidCount??(m?.bidCount||0)+1,admissionGrant:C.admissionGrant,...C.degraded?{degraded:!0}:{},live:C.live};if(!this.canApplyAsyncResult(t,u,void 0,l))return G;if(b(this.lastStatusCache.get(t))||h&&!this.pollSchedulers.has(t))return G;if(this.applyTransition(t,this.buildBidTransition(t,G,v,m,{isFirstBid:S,attribution:c})),this.syncPollCadence(t),p(G))return G;return b(this.lastStatusCache.get(t))||this.lastStatusCache.set(t,{auctionId:t,status:I,currentBid:T,highestBid:G.highestBid,timeRemaining:0,bidCount:G.bidCount,admissionGrant:C.admissionGrant,live:C.live}),G}catch(u){throw this.logger.error("Failed to place bid",{auctionId:t,amount:n.amount,error:u}),this.events.emit("auction:error",{auctionId:t,error:u}),u}}async status(t){const i=this.inFlightRequests.get(t);if(i)return this.logger.debug("Returning existing in-flight request",{auctionId:t}),i;const e=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),n=this.doStatus(t,e,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,n),n}async doStatus(t,e,s,n){try{const a=`/auctions/${t}/status`,o=await this.http.get(a).catch(t=>{if(function(t){return"object"==typeof t&&null!==t&&"status"in t&&404===t.status}(t))return f;throw t});if(o===f)return this.applyMissingStatus(t,e,s,n);const c=d(i,o,{endpoint:a},{throwOnFailure:!0});if(!c.ok)throw c.error;const u=c.value,l=await this.readDetailsForDisplay(t),h=u.live,p=new Date(function(t){return"english"===t.auctionType?t.effectiveSettleAt:t.settleAt}(h)).getTime(),m=A(h),y=this.getTrackedParticipation(t),S=u.lastBidAmount??u.winningBidAmount,v={auctionId:t,status:g(u.status),currentBid:void 0!==S?r(S):y?.currentBid,highestBid:m,timeRemaining:Number.isFinite(p)?Math.max(0,p-Date.now()):0,bidCount:u.bidCount,admissionGrant:u.admissionGrant,expiresAt:u.expiresAt,degraded:u.degraded,outcome:u.outcome,selection:u.selection,live:h};if(!this.canApplyAsyncResult(t,e,s,n))return v;const w=this.lastStatusCache.get(t),D=this.buildStatusTransition(t,v,h,l,{previousParticipation:y,previousStatus:w});return this.applyTransition(t,D),this.hasSuccessfulReadThisSession.set(t,!0),Number.isFinite(p)?this.lastSettleAtMs.set(t,p):this.lastSettleAtMs.delete(t),b(w)?this.lastStatusCache.set(t,w):b(v)?this.lastStatusCache.set(t,v):D.stopWatching?this.lastStatusCache.delete(t):this.lastStatusCache.set(t,v),this.syncPollCadence(t),this.events.emit("auction:status-updated",{auctionId:t,status:v}),v}catch(a){if(!this.canApplyAsyncResult(t,e,s,n))return this.lastStatusCache.get(t)??{auctionId:t,status:"not_bid",highestBid:"0.00",timeRemaining:0};throw this.logger.error("Failed to get auction status",{auctionId:t,error:a}),this.events.emit("auction:error",{auctionId:t,error:a}),a}}async readDetailsForDisplay(t){if(this.pollSchedulers.has(t)){const i=this.watchDetails.get(t);if(i)return i}try{const i=this.auctionDetailsCache.get(t),e=Date.now(),s=void 0!==i&&!("error"in i)&&e-i.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL&&(null==i.details.openAt||e>=new Date(i.details.openAt).getTime()),n=await this.getAuctionDetailsCached(t,{bypassCache:this.pollSchedulers.has(t)&&s}),a=this.auctionDetailsCache.get(t);return this.pollSchedulers.has(t)&&void 0!==a&&!("error"in a)&&(null==n.openAt||Date.now()>=new Date(n.openAt).getTime())&&this.watchDetails.set(t,n),n}catch(i){return void this.logger.warn("Auction configuration read failed; rendering the live block alone",{auctionId:t,error:i})}}applyMissingStatus(t,i,e,s){const n={auctionId:t,status:"not_bid",highestBid:"0.00",timeRemaining:0};if(!this.canApplyAsyncResult(t,i,e,s))return n;if(!b(this.lastStatusCache.get(t))){const i=this.buildMissingTransition();this.applyTransition(t,i),this.lastStatusCache.delete(t)}return this.hasSuccessfulReadThisSession.set(t,!0),this.lastSettleAtMs.delete(t),this.events.emit("auction:status-updated",{auctionId:t,status:n}),n}startWatching(t,i){this.stopWatching(t),this.hasSuccessfulReadThisSession.set(t,!1),this.lastStatusCache.delete(t),this.lastSettleAtMs.delete(t),i?this.degradedHandlers.set(t,i):this.degradedHandlers.delete(t);const e=this.desiredPollBaseMs(t);this.logger.info("Starting auction status polling",{auctionId:t,intervalMs:e}),this.installScheduler(t,e),this.events.emit("auction:watching-started",{auctionId:t,intervalMs:e})}installScheduler(t,i){const e=this.lifecycleGeneration,s=this.bumpMonitorGeneration(t),n=c({baseMs:i,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:this.degradedHandlers.get(t),shouldContinue:()=>this.pollSchedulers.get(t)===n&&this.canApplyAsyncResult(t,e,s),run:async()=>{const i=this.getOperationGeneration(t);try{await this.doStatus(t,e,s,i)}catch(n){if(!this.canApplyAsyncResult(t,e,s,i))return;throw this.logger.error("Polling failed",{auctionId:t,error:n}),n}}});this.pollSchedulers.set(t,n),this.pollBaseMs.set(t,i),this.registerVisibilityPoke(t,n),n.start()}desiredPollBaseMs(t){const i=this.getTrackedParticipation(t)?.status??this.lastStatusCache.get(t)?.status;return(void 0===i||"not_bid"===i)&&!this.isInSettlingWindow(t)?this.WATCHING_POLL_MS:this.PARTICIPATING_POLL_MS}syncPollCadence(t){const i=this.pollSchedulers.get(t);if(!i)return;const e=this.desiredPollBaseMs(t);this.pollBaseMs.get(t)!==e&&(i.stop(),this.unregisterVisibilityPoke(t),this.logger.debug("Re-cadencing auction status polling",{auctionId:t,intervalMs:e}),this.installScheduler(t,e))}stopWatching(t){this.bumpMonitorGeneration(t),this.watchDetails.delete(t);const i=this.pollSchedulers.get(t);i&&(i.stop(),this.clearDutchStep(t),this.pollSchedulers.delete(t),this.pollBaseMs.delete(t),this.degradedHandlers.delete(t),this.unregisterVisibilityPoke(t),this.lastStatusCache.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastSettleAtMs.delete(t),this.logger.info("Stopped watching auction",{auctionId:t}),this.events.emit("auction:watching-stopped",{auctionId:t}))}stopAllWatching(){for(const t of Array.from(this.pollSchedulers.keys()))this.stopWatching(t)}shouldSuppressBackoff(t){return!this.hasSuccessfulReadThisSession.get(t)||this.isInSettlingWindow(t)}isInSettlingWindow(t){const i=this.lastSettleAtMs.get(t);return void 0!==i&&(Date.now()>=i-12e4&&!b(this.lastStatusCache.get(t)))}registerVisibilityPoke(t,i){if("undefined"==typeof document)return;const e=()=>{"visible"===document.visibilityState&&this.isInSettlingWindow(t)&&i.poke()};document.addEventListener("visibilitychange",e),this.visibilityHandlers.set(t,e)}unregisterVisibilityPoke(t){if("undefined"==typeof document)return void this.visibilityHandlers.delete(t);const i=this.visibilityHandlers.get(t);i&&(document.removeEventListener("visibilitychange",i),this.visibilityHandlers.delete(t))}bumpMonitorGeneration(t){const i=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,i),i}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const i=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,i),i}canApplyAsyncResult(t,i,e,s){return!this.destroyed&&this.lifecycleGeneration===i&&((void 0===e||this.monitorGenerations.get(t)===e)&&(void 0===s||this.getOperationGeneration(t)===s))}startMonitoring(t,i,e,s){b(this.lastStatusCache.get(t))||(this.monitorRuntime.start(t,i,e),this.startWatching(t,s))}stopMonitoring(t){this.stopWatching(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.stopAllWatching(),this.inFlightRequests.clear(),this.lastStatusCache.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastSettleAtMs.clear(),this.visibilityHandlers.clear();for(const t of Array.from(this.dutchStepTimers.keys()))this.clearDutchStep(t);this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.auctionDetailsCache.clear(),this.watchDetails.clear(),this.pollBaseMs.clear(),this.degradedHandlers.clear(),this.monitorRuntime.clear()}}export{D as AuctionManagementModule};
1
+ import{AuctionDetailsResponseSchema as t,AuctionBidderStateResponseSchema as i}from"@fanfare-io/fanfare-sdk-contracts/auction";import*as e from"valibot";import{createError as s}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{isMoneyString as a,isMoneyGreaterThan as o,normalizeMoney as r}from"../core/money.js";import{parseResponse as d}from"../core/parse-response.js";import{createPollScheduler as c}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as u}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as l}from"../state/events.js";import{getSDKStore as h}from"../state/store.js";import{isGrantPendingWin as p,toAuctionConsumerStatus as g,isTerminalAuctionStatus as m}from"./auction-status.js";import{assertBidInputMatchesMechanism as y}from"./auction.driver.js";import{advanceDutchPricing as S}from"./dutch-pricing.js";const f=/* @__PURE__ */Symbol("auction-participation-missing");function v(t){return"won"===t.status&&!t.admissionGrant&&!t.outcome}function b(t){return void 0!==t&&m(t.status)&&!v(t)}function A(t){return"english"===t.auctionType?r(t.highestBid):r(t.currentPrice)}const w=e.object({productId:e.string(),variantId:e.optional(e.string())});class D{constructor(t){this.logger=n(),this.events=l(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.pollBaseMs=/* @__PURE__ */new Map,this.degradedHandlers=/* @__PURE__ */new Map,this.lastStatusCache=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastSettleAtMs=/* @__PURE__ */new Map,this.visibilityHandlers=/* @__PURE__ */new Map,this.dutchStepTimers=/* @__PURE__ */new Map,this.monitorRuntime=new u,this.auctionDetailsCache=/* @__PURE__ */new Map,this.watchDetails=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.AUCTION_DETAILS_CACHE_TTL=3e4,this.WATCHING_POLL_MS=15e3,this.PARTICIPATING_POLL_MS=5e3,this.NO_BID_END_GRACE_MS=5e3,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return h()}async getAuctionDetailsCached(i,e){const s=this.auctionDetailsCache.get(i),n=void 0!==s&&Date.now()-s.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL,a=void 0!==s&&"error"in s;if(s&&n&&(!e?.bypassCache||a)){if(void 0!==s.details)return s.details;if("error"in s)throw s.error}try{const e=await this.http.get(`/auctions/${i}`),s=d(t,e,{endpoint:`/auctions/${i}`},{throwOnFailure:!0});if(!s.ok)throw s.error;const n=s.value;return this.auctionDetailsCache.set(i,{details:n,fetchedAt:Date.now()}),n}catch(o){if(this.auctionDetailsCache.set(i,{...void 0!==s?.details?{details:s.details}:{},fetchedAt:Date.now(),error:o}),void 0!==s?.details)return s.details;throw o}}getTrackedParticipation(t){return this.trackedParticipations.get(t)}buildPricing(t,i){return"english"===t.auctionType?{model:"english",...null===t.highestBid?{}:{highestBid:t.highestBid},minNextBid:t.minNextBid,...i?.minBidIncrement?{bidIncrement:i.minBidIncrement}:{},bidCount:t.bidCount,effectiveSettleAt:t.effectiveSettleAt,autoExtended:t.autoExtended,reserveMet:t.reserveMet,ended:t.ended}:{model:"dutch",currentPrice:t.currentPrice,remainingQuantity:t.remainingQuantity,totalQuantity:t.totalQuantity,...null===t.nextDropAt?{}:{nextDropAt:t.nextDropAt},...null===t.floorPrice?{}:{floorPrice:t.floorPrice},settleAt:t.settleAt,...i?.startPrice?{startPrice:i.startPrice}:{},...i?.priceDropAmount?{priceDropAmount:i.priceDropAmount}:{},..."number"==typeof i?.priceDropIntervalSeconds?{priceDropIntervalSeconds:i.priceDropIntervalSeconds}:{},ended:t.ended}}buildDisplayState(t,i,e){const s=("english"===i.auctionType?i.closeAt:null)??e?.closeAt??void 0;return{status:t,...e?.currencyCode?{currencyCode:e.currencyCode}:{},...s?{closeAt:s}:{},pricing:this.buildPricing(i,e)}}emitTransitionEvents(t){for(const i of t)this.events.emit(i.type,i.payload)}applyTransition(t,i){void 0!==i.trackedParticipation&&(i.trackedParticipation?this.trackedParticipations.set(t,i.trackedParticipation):this.trackedParticipations.delete(t));const e=this.monitorRuntime.getDisplayAtom(t);e&&i.displayState&&e.set({type:"auction",...i.displayState}),this.emitTransitionEvents(i.events),i.monitorUpdate&&this.monitorRuntime.notify(t,i.monitorUpdate),i.stopWatching?this.stopWatching(t):i.displayState&&this.armDutchStep(t)}armDutchStep(t){this.clearDutchStep(t);const i=this.monitorRuntime.getDisplayAtom(t),e=i?.get();if(!i||"auction"!==e?.type)return;const s=e.pricing;if("dutch"!==s?.model||s.ended||!s.nextDropAt||void 0===s.priceDropAmount||void 0===s.priceDropIntervalSeconds)return;const n=Date.parse(s.nextDropAt);if(Number.isNaN(n))return;const a=Math.max(0,n-Date.now());if(a>2147483647)return;const o=setTimeout(()=>{this.dutchStepTimers.delete(t);const i=this.monitorRuntime.getDisplayAtom(t),e=i?.get();if(!i||"auction"!==e?.type||"dutch"!==e.pricing?.model)return;const s=S(e.pricing,Date.now());s!==e.pricing&&(i.set({...e,pricing:s}),this.armDutchStep(t))},a+1);this.dutchStepTimers.set(t,o)}clearDutchStep(t){const i=this.dutchStepTimers.get(t);void 0!==i&&(clearTimeout(i),this.dutchStepTimers.delete(t))}checkOutbidConditions(t,i,e){if("outbid"!==i.status)return!1;if("winning"===t.status&&"outbid"===i.status)return!0;if(e?.currentBid&&i.highestBid){const s=e.currentBid,n=i.highestBid,r=t.highestBid||"0";if(a(s)&&a(n)&&a(r))return o(n,r)&&o(n,s)&&"outbid"===i.status}return"outbid"!==t.status&&"outbid"===i.status}buildBidTransition(t,i,e,s,n){const a=i.amount,o=n.isFirstBid?[{type:"auction:entered",payload:{auctionId:t}},{type:"experience:distribution-entered",payload:{distributionType:"auction",distributionId:t,...n.attribution}}]:[],r={type:"auction:bid-placed",payload:{auctionId:t,amount:a,status:i.status}};if("won"===i.status&&!p(i)||"lost"===i.status){const s=this.buildTerminalTransition(t,i.status,i.live,e,{currentBid:a,admissionGrant:i.admissionGrant});return{...s,events:[...o,r,...s.events]}}return{trackedParticipation:{auctionId:t,enteredAt:s?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:a,highestBid:i.highestBid,bidCount:i.bidCount,lastBidAt:/* @__PURE__ */(new Date).toISOString(),metadata:s?.metadata,fingerprint:s?.fingerprint},displayState:this.buildDisplayState(i.status,i.live,e),events:[...o,r,..."winning"===i.status?[{type:"auction:winning",payload:{auctionId:t,amount:a,highestBid:i.highestBid}}]:[],..."outbid"===i.status?[{type:"auction:outbid",payload:{auctionId:t,yourBid:a,highestBid:i.highestBid}}]:[]],monitorUpdate:null,stopWatching:!1}}buildTerminalTransition(t,i,e,s,n){return"won"===i?{trackedParticipation:null,displayState:this.buildDisplayState(i,e,s),events:[{type:"auction:won",payload:{auctionId:t,winningBid:n.currentBid??"",admissionGrant:n.admissionGrant}}],monitorUpdate:n.admissionGrant?{type:"granted",token:n.admissionGrant}:{type:"resync"},stopWatching:!0}:{trackedParticipation:null,displayState:this.buildDisplayState(i,e,s),events:[{type:"auction:lost",payload:{auctionId:t,highestBid:A(e)}}],monitorUpdate:{type:"ended",outcome:{type:"lost"}},stopWatching:!0}}buildStatusTransition(t,i,e,s,{previousParticipation:n,previousStatus:a}){const o=i.currentBid??n?.currentBid,r=i.bidCount??n?.bidCount;if(b(a))return{displayState:null,events:[],monitorUpdate:null,stopWatching:!1};if("won"===i.status&&!v(i)||"lost"===i.status)return this.buildTerminalTransition(t,i.status,e,s,{currentBid:o,admissionGrant:i.admissionGrant});const d=("english"===e.auctionType?e.closeAt:e.settleAt)??s?.closeAt,c=d?new Date(d).getTime():void 0,u=void 0!==c&&Date.now()>=c+this.NO_BID_END_GRACE_MS;if("not_bid"===i.status&&(e.ended||u))return{trackedParticipation:null,displayState:this.buildDisplayState("ended",e,s),events:[],monitorUpdate:{type:"ended",outcome:{type:"closed"}},stopWatching:!0};const l={auctionId:t,enteredAt:n?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:o,highestBid:i.highestBid,bidCount:r,metadata:n?.metadata,fingerprint:n?.fingerprint},h=[];return a&&(this.checkOutbidConditions(a,i,n)&&h.push({type:"auction:outbid",payload:{auctionId:t,yourBid:a.currentBid||n?.currentBid||"",highestBid:i.highestBid}}),"winning"!==a.status&&"winning"===i.status&&h.push({type:"auction:winning",payload:{auctionId:t,amount:o||"",highestBid:i.highestBid}}),a.highestBid!==i.highestBid&&"winning"!==i.status&&h.push({type:"auction:bid-updated",payload:{auctionId:t,highestBid:i.highestBid,bidCount:i.bidCount||0}})),{trackedParticipation:l,displayState:this.buildDisplayState(i.status,e,s),events:h,monitorUpdate:null,stopWatching:!1}}buildMissingTransition(){return{trackedParticipation:null,displayState:{status:"lost"},events:[],monitorUpdate:{type:"ended",outcome:{type:"closed",reason:"not_participating"}},stopWatching:!0}}async get(t){try{return await this.getAuctionDetailsCached(t)}catch(i){throw this.logger.error("Failed to get auction",{auctionId:t,error:i}),i}}async bid(t,n,o,c){const u=this.lifecycleGeneration;let l;try{if(void 0!==n.amount&&!a(n.amount))throw s.validationError("Invalid bid amount format. Must be a numeric string (e.g., '100.00')");if(!this.store.session)throw s.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");l=this.bumpOperationGeneration(t);const h=this.pollSchedulers.has(t),m=this.getTrackedParticipation(t),S=!(m&&"not_bid"!==m.status||b(this.lastStatusCache.get(t)));this.logger.info("Placing bid",{auctionId:t,amount:n.amount});const f=`/auctions/${t}/bid`,v=await this.readDetailsForDisplay(t);v&&y(n,v.auctionType);const D=await this.http.post(f,{...void 0===n.amount?{}:{amount:n.amount},...void 0===n.selection?{}:{selection:n.selection}},o),B=d(i,D,{endpoint:f},{throwOnFailure:!0});if(!B.ok)throw B.error;const C=B.value;C.degraded&&this.logger.warn("Bid committed; the credential mint did not complete on this request",{auctionId:t});const I=g(C.status),T=r(C.winningBidAmount??C.lastBidAmount??n.amount,A(C.live)),M="WON"===C.status&&void 0!==C.selection?e.safeParse(w,C.selection):void 0,P=M?.success?M.output:void 0,G={...P?{selection:P}:{},status:I,amount:T,highestBid:A(C.live),bidCount:C.bidCount??(m?.bidCount||0)+1,admissionGrant:C.admissionGrant,...C.degraded?{degraded:!0}:{},live:C.live};if(!this.canApplyAsyncResult(t,u,void 0,l))return G;if(b(this.lastStatusCache.get(t))||h&&!this.pollSchedulers.has(t))return G;if(this.applyTransition(t,this.buildBidTransition(t,G,v,m,{isFirstBid:S,attribution:c})),this.syncPollCadence(t),p(G))return G;return b(this.lastStatusCache.get(t))||this.lastStatusCache.set(t,{auctionId:t,status:I,currentBid:T,highestBid:G.highestBid,timeRemaining:0,bidCount:G.bidCount,admissionGrant:C.admissionGrant,live:C.live}),G}catch(h){if(!this.canApplyAsyncResult(t,u,void 0,l))throw h;throw this.logger.error("Failed to place bid",{auctionId:t,amount:n.amount,error:h}),this.events.emit("auction:error",{auctionId:t,error:h}),h}}async status(t){const i=this.inFlightRequests.get(t);if(i)return this.logger.debug("Returning existing in-flight request",{auctionId:t}),i;const e=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),n=this.doStatus(t,e,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,n),n}async doStatus(t,e,s,n){try{const a=`/auctions/${t}/status`,o=await this.http.get(a).catch(t=>{if(function(t){return"object"==typeof t&&null!==t&&"status"in t&&404===t.status}(t))return f;throw t});if(o===f)return this.applyMissingStatus(t,e,s,n);const c=d(i,o,{endpoint:a},{throwOnFailure:!0});if(!c.ok)throw c.error;const u=c.value,l=await this.readDetailsForDisplay(t),h=u.live,p=new Date(function(t){return"english"===t.auctionType?t.effectiveSettleAt:t.settleAt}(h)).getTime(),m=A(h),y=this.getTrackedParticipation(t),S=u.lastBidAmount??u.winningBidAmount,v={auctionId:t,status:g(u.status),currentBid:void 0!==S?r(S):y?.currentBid,highestBid:m,timeRemaining:Number.isFinite(p)?Math.max(0,p-Date.now()):0,bidCount:u.bidCount,admissionGrant:u.admissionGrant,expiresAt:u.expiresAt,degraded:u.degraded,outcome:u.outcome,selection:u.selection,live:h};if(!this.canApplyAsyncResult(t,e,s,n))return v;const w=this.lastStatusCache.get(t),D=this.buildStatusTransition(t,v,h,l,{previousParticipation:y,previousStatus:w});return this.applyTransition(t,D),this.hasSuccessfulReadThisSession.set(t,!0),Number.isFinite(p)?this.lastSettleAtMs.set(t,p):this.lastSettleAtMs.delete(t),b(w)?this.lastStatusCache.set(t,w):b(v)?this.lastStatusCache.set(t,v):D.stopWatching?this.lastStatusCache.delete(t):this.lastStatusCache.set(t,v),this.syncPollCadence(t),this.events.emit("auction:status-updated",{auctionId:t,status:v}),v}catch(a){if(!this.canApplyAsyncResult(t,e,s,n))return this.lastStatusCache.get(t)??{auctionId:t,status:"not_bid",highestBid:"0.00",timeRemaining:0};throw this.logger.error("Failed to get auction status",{auctionId:t,error:a}),this.events.emit("auction:error",{auctionId:t,error:a}),a}}async readDetailsForDisplay(t){if(this.pollSchedulers.has(t)){const i=this.watchDetails.get(t);if(i)return i}try{const i=this.auctionDetailsCache.get(t),e=Date.now(),s=void 0!==i&&!("error"in i)&&e-i.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL&&(null==i.details.openAt||e>=new Date(i.details.openAt).getTime()),n=await this.getAuctionDetailsCached(t,{bypassCache:this.pollSchedulers.has(t)&&s}),a=this.auctionDetailsCache.get(t);return this.pollSchedulers.has(t)&&void 0!==a&&!("error"in a)&&(null==n.openAt||Date.now()>=new Date(n.openAt).getTime())&&this.watchDetails.set(t,n),n}catch(i){return void this.logger.warn("Auction configuration read failed; rendering the live block alone",{auctionId:t,error:i})}}applyMissingStatus(t,i,e,s){const n={auctionId:t,status:"not_bid",highestBid:"0.00",timeRemaining:0};if(!this.canApplyAsyncResult(t,i,e,s))return n;if(!b(this.lastStatusCache.get(t))){const i=this.buildMissingTransition();this.applyTransition(t,i),this.lastStatusCache.delete(t)}return this.hasSuccessfulReadThisSession.set(t,!0),this.lastSettleAtMs.delete(t),this.events.emit("auction:status-updated",{auctionId:t,status:n}),n}startWatching(t,i){this.stopWatching(t),this.hasSuccessfulReadThisSession.set(t,!1),this.lastStatusCache.delete(t),this.lastSettleAtMs.delete(t),i?this.degradedHandlers.set(t,i):this.degradedHandlers.delete(t);const e=this.desiredPollBaseMs(t);this.logger.info("Starting auction status polling",{auctionId:t,intervalMs:e}),this.installScheduler(t,e),this.events.emit("auction:watching-started",{auctionId:t,intervalMs:e})}installScheduler(t,i){const e=this.lifecycleGeneration,s=this.bumpMonitorGeneration(t),n=c({baseMs:i,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:this.degradedHandlers.get(t),shouldContinue:()=>this.pollSchedulers.get(t)===n&&this.canApplyAsyncResult(t,e,s),run:async()=>{const i=this.getOperationGeneration(t);try{await this.doStatus(t,e,s,i)}catch(n){if(!this.canApplyAsyncResult(t,e,s,i))return;throw this.logger.error("Polling failed",{auctionId:t,error:n}),n}}});this.pollSchedulers.set(t,n),this.pollBaseMs.set(t,i),this.registerVisibilityPoke(t,n),n.start()}desiredPollBaseMs(t){const i=this.getTrackedParticipation(t)?.status??this.lastStatusCache.get(t)?.status;return(void 0===i||"not_bid"===i)&&!this.isInSettlingWindow(t)?this.WATCHING_POLL_MS:this.PARTICIPATING_POLL_MS}syncPollCadence(t){const i=this.pollSchedulers.get(t);if(!i)return;const e=this.desiredPollBaseMs(t);this.pollBaseMs.get(t)!==e&&(i.stop(),this.unregisterVisibilityPoke(t),this.logger.debug("Re-cadencing auction status polling",{auctionId:t,intervalMs:e}),this.installScheduler(t,e))}stopWatching(t){this.bumpMonitorGeneration(t),this.watchDetails.delete(t);const i=this.pollSchedulers.get(t);i&&(i.stop(),this.clearDutchStep(t),this.pollSchedulers.delete(t),this.pollBaseMs.delete(t),this.degradedHandlers.delete(t),this.unregisterVisibilityPoke(t),this.lastStatusCache.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastSettleAtMs.delete(t),this.logger.info("Stopped watching auction",{auctionId:t}),this.events.emit("auction:watching-stopped",{auctionId:t}))}stopAllWatching(){for(const t of Array.from(this.pollSchedulers.keys()))this.stopWatching(t)}shouldSuppressBackoff(t){return!this.hasSuccessfulReadThisSession.get(t)||this.isInSettlingWindow(t)}isInSettlingWindow(t){const i=this.lastSettleAtMs.get(t);return void 0!==i&&(Date.now()>=i-12e4&&!b(this.lastStatusCache.get(t)))}registerVisibilityPoke(t,i){if("undefined"==typeof document)return;const e=()=>{"visible"===document.visibilityState&&this.isInSettlingWindow(t)&&i.poke()};document.addEventListener("visibilitychange",e),this.visibilityHandlers.set(t,e)}unregisterVisibilityPoke(t){if("undefined"==typeof document)return void this.visibilityHandlers.delete(t);const i=this.visibilityHandlers.get(t);i&&(document.removeEventListener("visibilitychange",i),this.visibilityHandlers.delete(t))}bumpMonitorGeneration(t){const i=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,i),i}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const i=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,i),i}canApplyAsyncResult(t,i,e,s){return!this.destroyed&&this.lifecycleGeneration===i&&((void 0===e||this.monitorGenerations.get(t)===e)&&(void 0===s||this.getOperationGeneration(t)===s))}startMonitoring(t,i,e,s){b(this.lastStatusCache.get(t))||(this.monitorRuntime.start(t,i,e),this.startWatching(t,s))}stopMonitoring(t){this.stopWatching(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.stopAllWatching(),this.inFlightRequests.clear(),this.lastStatusCache.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastSettleAtMs.clear(),this.visibilityHandlers.clear();for(const t of Array.from(this.dutchStepTimers.keys()))this.clearDutchStep(t);this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.auctionDetailsCache.clear(),this.watchDetails.clear(),this.pollBaseMs.clear(),this.degradedHandlers.clear(),this.monitorRuntime.clear()}}export{D as AuctionManagementModule};
@@ -1 +1 @@
1
- import{createError as t,isFanfareError as e}from"../core/errors.js";import{getLogger as i}from"../core/logger.js";import{jitteredIntervalMs as s}from"../core/utils.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as n}from"../payment/checkout-contract.js";import{getEventBus as a}from"../state/events.js";import{getSDKStore as o}from"../state/store.js";const h=2147483647;class c{constructor(t){this.logger=i(),this.events=a(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.scheduledChecks=/* @__PURE__ */new Map,this.settlingTimers=/* @__PURE__ */new Map,this.monitorRuntime=new r,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.drawTimeMs=/* @__PURE__ */new Map,this.initialCheckBufferMs=5e3,this.retryCheckDelayMs=5e3,this.retryAfterCapMs=6e4,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return o()}async get(t){try{return await this.http.get(`/draws/${t}`)}catch(e){throw this.logger.error("Failed to get draw",{drawId:t,error:e}),e}}async enter(e,i,s,r,a,o){try{const h=this.lifecycleGeneration;if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const c=this.bumpOperationGeneration(e);this.logger.info("Entering draw",{drawId:e});const d={metadata:i,selection:a};r&&(d.paymentInput=r);const l=await this.http.post(`/draws/${e}/enter`,d,s);if(n(l))return l;const u=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:i});return"ENTERED"!==l.status&&"WON"!==l.status&&"COMPLETED"!==l.status||u.events.push({type:"experience:distribution-entered",payload:{distributionType:"draw",distributionId:e,...o}}),this.canApplyAsyncResult(e,h,void 0,c)?(this.applyTransition(e,u),l):l}catch(h){throw this.logger.error("Failed to enter draw",{drawId:e,error:h}),this.events.emit("draw:error",{drawId:e,error:h}),h}}async leave(t,e){try{const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t);if(this.logger.info("Leaving draw",{drawId:t}),await this.http.post(`/draws/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,i,void 0,s))return;this.trackedParticipations.delete(t),this.stopMonitoring(t),this.events.emit("draw:left",{drawId:t})}catch(i){throw this.logger.error("Failed to leave draw",{drawId:t,error:i}),this.events.emit("draw:error",{drawId:t,error:i}),i}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{drawId:t}),e;const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),r=this.doStatus(t,i,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,r),r}async doStatus(t,e,i,s){try{const r=await this.http.get(`/draws/${t}/status`);if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"};if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}if(!this.canApplyAsyncResult(t,e,i,s))return this.trackedParticipations.has(t)?{status:"ENTERED"}:{status:"NOT_ENTERED"};throw this.logger.error("Failed to get draw status",{drawId:t,error:r}),this.events.emit("draw:error",{drawId:t,error:r}),r}}startMonitoring(t,e,i){this.monitorRuntime.start(t,e,i),this.bumpMonitorGeneration(t);const s=e?.drawTime;s?this.drawTimeMs.set(t,new Date(s).getTime()):this.drawTimeMs.delete(t);const r=this.monitorRuntime.getDisplayAtom(t);r&&r.set({type:"draw",drawTime:s,entryNumber:this.trackedParticipations.get(t)?.entryNumber,selectionAvailability:this.trackedParticipations.get(t)?.selectionAvailability}),s&&"settling"!==e?.phase?this.scheduleSettlingTransition(t,s):this.cancelSettlingTransition(t),"settling"===e?.phase?this.scheduleStatusCheck(t,0):s&&this.scheduleInitialStatusCheck(t,s)}stopMonitoring(t){this.bumpMonitorGeneration(t),this.cancelScheduledCheck(t),this.cancelSettlingTransition(t),this.drawTimeMs.delete(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.scheduledChecks.has(t)||this.settlingTimers.has(t)}buildTransition(t,e,{source:i,previousParticipation:s,enteredAt:r,metadata:n}){switch(e.status){case"ENTERED":return{selectionAvailability:e.selectionAvailability,trackedParticipation:{drawId:t,enteredAt:s?.enteredAt??r??/* @__PURE__ */(new Date).toISOString(),entryNumber:s?.entryNumber,selectionAvailability:e.selectionAvailability,metadata:s?.metadata??n},events:"enter"===i?[{type:"draw:entered",payload:{drawId:t}}]:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:"status"===i};case"WON":return{trackedParticipation:null,events:[{type:"draw:won",payload:{drawId:t,result:{won:!0,drawTime:e.wonAt}}}],monitorUpdate:"status"===i?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"DENIED":return{trackedParticipation:null,events:[{type:"draw:expired",payload:{drawId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"NOT_ENTERED":return{trackedParticipation:null,events:s?[{type:"draw:lost",payload:{drawId:t,result:this.buildLostResult()}}]:[],monitorUpdate:"status"===i?{type:"ended",outcome:s?{type:"lost"}:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};default:return{trackedParticipation:s??null,events:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:!0}}}applyTransition(t,e){const i=this.monitorRuntime.getDisplayAtom(t),s=i?.get();i&&"draw"===s?.type&&void 0!==e.selectionAvailability&&i.set({...s,selectionAvailability:e.selectionAvailability}),e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);for(const r of e.events)this.events.emit(r.type,r.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring?this.stopMonitoring(t):e.retryStatusCheck&&this.monitorRuntime.has(t)&&this.scheduleRetryStatusCheck(t)}scheduleInitialStatusCheck(t,e){const i=new Date(e).getTime(),s=Date.now(),r=i+this.initialCheckBufferMs,n=r<=s?s:r+Math.round(2e3*Math.random());this.scheduleStatusCheckAt(t,n)}scheduleSettlingTransition(t,e){const i=new Date(e).getTime(),s=Math.max(0,i-Date.now());this.cancelSettlingTransition(t);const r=this.monitorGenerations.get(t),n=setTimeout(()=>{this.settlingTimers.delete(t),void 0!==r&&this.monitorGenerations.get(t)===r&&(Date.now()<i?this.scheduleSettlingTransition(t,e):this.monitorRuntime.notify(t,{type:"settling",at:e},!1))},Math.min(s,h));this.settlingTimers.set(t,n)}cancelSettlingTransition(t){const e=this.settlingTimers.get(t);e&&clearTimeout(e),this.settlingTimers.delete(t)}scheduleRetryStatusCheck(t,i){const r=(e(i)&&"number"==typeof i.retryAfter&&Number.isFinite(i.retryAfter)?Math.min(1e3*i.retryAfter,this.retryAfterCapMs):void 0)??s(this.retryCheckDelayMs);this.scheduleStatusCheck(t,this.isInSettlingWindow(t)?Math.min(r,this.retryCheckDelayMs):r)}isInSettlingWindow(t){const e=this.drawTimeMs.get(t);return void 0!==e&&Date.now()>=e}scheduleStatusCheck(t,i){this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=this.getOperationGeneration(t),n=setTimeout(async()=>{this.scheduledChecks.delete(t);try{if(void 0===s)return;await this.doStatus(t,this.lifecycleGeneration,s,r)}catch(i){if(!this.canApplyAsyncResult(t,this.lifecycleGeneration,s,r))return;if(e(i)&&i.retryable&&this.monitorRuntime.has(t))return void this.scheduleRetryStatusCheck(t,i);this.stopMonitoring(t)}},i);this.scheduledChecks.set(t,n)}scheduleStatusCheckAt(t,e){const i=Math.max(0,e-Date.now());if(i<=h)return void this.scheduleStatusCheck(t,i);this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=setTimeout(()=>{this.scheduledChecks.delete(t),void 0!==s&&this.monitorGenerations.get(t)===s&&this.scheduleStatusCheckAt(t,e)},h);this.scheduledChecks.set(t,r)}cancelScheduledCheck(t){const e=this.scheduledChecks.get(t);e&&(clearTimeout(e),this.scheduledChecks.delete(t))}buildLostResult(){return{won:!1,drawTime:/* @__PURE__ */(new Date).toISOString()}}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.scheduledChecks.forEach(t=>clearTimeout(t)),this.scheduledChecks.clear(),this.settlingTimers.forEach(t=>clearTimeout(t)),this.settlingTimers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.drawTimeMs.clear(),this.monitorRuntime.clear()}}export{c as DrawManagementModule};
1
+ import{createError as t,isFanfareError as e}from"../core/errors.js";import{getLogger as i}from"../core/logger.js";import{jitteredIntervalMs as s}from"../core/utils.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as n}from"../payment/checkout-contract.js";import{getEventBus as a}from"../state/events.js";import{getSDKStore as o}from"../state/store.js";const h=2147483647;class c{constructor(t){this.logger=i(),this.events=a(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.scheduledChecks=/* @__PURE__ */new Map,this.settlingTimers=/* @__PURE__ */new Map,this.monitorRuntime=new r,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.drawTimeMs=/* @__PURE__ */new Map,this.initialCheckBufferMs=5e3,this.retryCheckDelayMs=5e3,this.retryAfterCapMs=6e4,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return o()}async get(t){try{return await this.http.get(`/draws/${t}`)}catch(e){throw this.logger.error("Failed to get draw",{drawId:t,error:e}),e}}async enter(e,i,s,r,a,o){const h=this.lifecycleGeneration;let c;try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");c=this.bumpOperationGeneration(e),this.logger.info("Entering draw",{drawId:e});const d={metadata:i,selection:a};r&&(d.paymentInput=r);const l=await this.http.post(`/draws/${e}/enter`,d,s);if(n(l))return l;const u=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:i});return"ENTERED"!==l.status&&"WON"!==l.status&&"COMPLETED"!==l.status||u.events.push({type:"experience:distribution-entered",payload:{distributionType:"draw",distributionId:e,...o}}),this.canApplyAsyncResult(e,h,void 0,c)?(this.applyTransition(e,u),l):l}catch(d){if(!this.canApplyAsyncResult(e,h,void 0,c))throw d;throw this.logger.error("Failed to enter draw",{drawId:e,error:d}),this.events.emit("draw:error",{drawId:e,error:d}),d}}async leave(t,e){const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t);try{if(this.logger.info("Leaving draw",{drawId:t}),await this.http.post(`/draws/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,i,void 0,s))return;this.trackedParticipations.delete(t),this.stopMonitoring(t),this.events.emit("draw:left",{drawId:t})}catch(r){if(!this.canApplyAsyncResult(t,i,void 0,s))throw r;throw this.logger.error("Failed to leave draw",{drawId:t,error:r}),this.events.emit("draw:error",{drawId:t,error:r}),r}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{drawId:t}),e;const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),r=this.doStatus(t,i,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,r),r}async doStatus(t,e,i,s){try{const r=await this.http.get(`/draws/${t}/status`);if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"};if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}if(!this.canApplyAsyncResult(t,e,i,s))return this.trackedParticipations.has(t)?{status:"ENTERED"}:{status:"NOT_ENTERED"};throw this.logger.error("Failed to get draw status",{drawId:t,error:r}),this.events.emit("draw:error",{drawId:t,error:r}),r}}startMonitoring(t,e,i){this.monitorRuntime.start(t,e,i),this.bumpMonitorGeneration(t);const s=e?.drawTime;s?this.drawTimeMs.set(t,new Date(s).getTime()):this.drawTimeMs.delete(t);const r=this.monitorRuntime.getDisplayAtom(t);r&&r.set({type:"draw",drawTime:s,entryNumber:this.trackedParticipations.get(t)?.entryNumber,selectionAvailability:this.trackedParticipations.get(t)?.selectionAvailability}),s&&"settling"!==e?.phase?this.scheduleSettlingTransition(t,s):this.cancelSettlingTransition(t),"settling"===e?.phase?this.scheduleStatusCheck(t,0):s&&this.scheduleInitialStatusCheck(t,s)}stopMonitoring(t){this.bumpMonitorGeneration(t),this.cancelScheduledCheck(t),this.cancelSettlingTransition(t),this.drawTimeMs.delete(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.scheduledChecks.has(t)||this.settlingTimers.has(t)}buildTransition(t,e,{source:i,previousParticipation:s,enteredAt:r,metadata:n}){switch(e.status){case"ENTERED":return{selectionAvailability:e.selectionAvailability,trackedParticipation:{drawId:t,enteredAt:s?.enteredAt??r??/* @__PURE__ */(new Date).toISOString(),entryNumber:s?.entryNumber,selectionAvailability:e.selectionAvailability,metadata:s?.metadata??n},events:"enter"===i?[{type:"draw:entered",payload:{drawId:t}}]:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:"status"===i};case"WON":return{trackedParticipation:null,events:[{type:"draw:won",payload:{drawId:t,result:{won:!0,drawTime:e.wonAt}}}],monitorUpdate:"status"===i?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"DENIED":return{trackedParticipation:null,events:[{type:"draw:expired",payload:{drawId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"NOT_ENTERED":return{trackedParticipation:null,events:s?[{type:"draw:lost",payload:{drawId:t,result:this.buildLostResult()}}]:[],monitorUpdate:"status"===i?{type:"ended",outcome:s?{type:"lost"}:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};default:return{trackedParticipation:s??null,events:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:!0}}}applyTransition(t,e){const i=this.monitorRuntime.getDisplayAtom(t),s=i?.get();i&&"draw"===s?.type&&void 0!==e.selectionAvailability&&i.set({...s,selectionAvailability:e.selectionAvailability}),e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);for(const r of e.events)this.events.emit(r.type,r.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring?this.stopMonitoring(t):e.retryStatusCheck&&this.monitorRuntime.has(t)&&this.scheduleRetryStatusCheck(t)}scheduleInitialStatusCheck(t,e){const i=new Date(e).getTime(),s=Date.now(),r=i+this.initialCheckBufferMs,n=r<=s?s:r+Math.round(2e3*Math.random());this.scheduleStatusCheckAt(t,n)}scheduleSettlingTransition(t,e){const i=new Date(e).getTime(),s=Math.max(0,i-Date.now());this.cancelSettlingTransition(t);const r=this.monitorGenerations.get(t),n=setTimeout(()=>{this.settlingTimers.delete(t),void 0!==r&&this.monitorGenerations.get(t)===r&&(Date.now()<i?this.scheduleSettlingTransition(t,e):this.monitorRuntime.notify(t,{type:"settling",at:e},!1))},Math.min(s,h));this.settlingTimers.set(t,n)}cancelSettlingTransition(t){const e=this.settlingTimers.get(t);e&&clearTimeout(e),this.settlingTimers.delete(t)}scheduleRetryStatusCheck(t,i){const r=(e(i)&&"number"==typeof i.retryAfter&&Number.isFinite(i.retryAfter)?Math.min(1e3*i.retryAfter,this.retryAfterCapMs):void 0)??s(this.retryCheckDelayMs);this.scheduleStatusCheck(t,this.isInSettlingWindow(t)?Math.min(r,this.retryCheckDelayMs):r)}isInSettlingWindow(t){const e=this.drawTimeMs.get(t);return void 0!==e&&Date.now()>=e}scheduleStatusCheck(t,i){this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=this.getOperationGeneration(t),n=setTimeout(async()=>{this.scheduledChecks.delete(t);try{if(void 0===s)return;await this.doStatus(t,this.lifecycleGeneration,s,r)}catch(i){if(!this.canApplyAsyncResult(t,this.lifecycleGeneration,s,r))return;if(e(i)&&i.retryable&&this.monitorRuntime.has(t))return void this.scheduleRetryStatusCheck(t,i);this.stopMonitoring(t)}},i);this.scheduledChecks.set(t,n)}scheduleStatusCheckAt(t,e){const i=Math.max(0,e-Date.now());if(i<=h)return void this.scheduleStatusCheck(t,i);this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=setTimeout(()=>{this.scheduledChecks.delete(t),void 0!==s&&this.monitorGenerations.get(t)===s&&this.scheduleStatusCheckAt(t,e)},h);this.scheduledChecks.set(t,r)}cancelScheduledCheck(t){const e=this.scheduledChecks.get(t);e&&(clearTimeout(e),this.scheduledChecks.delete(t))}buildLostResult(){return{won:!1,drawTime:/* @__PURE__ */(new Date).toISOString()}}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.scheduledChecks.forEach(t=>clearTimeout(t)),this.scheduledChecks.clear(),this.settlingTimers.forEach(t=>clearTimeout(t)),this.settlingTimers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.drawTimeMs.clear(),this.monitorRuntime.clear()}}export{c as DrawManagementModule};
@@ -1 +1 @@
1
- import{AuthenticationRoutingGateV1Schema as e,AccessCodeRoutingGateV1Schema as t,BotCheckRoutingGateV1Schema as i,RoutingSequenceAvailabilityV1Schema as s}from"@fanfare-io/fanfare-sdk-contracts/routing";import{GrantedSelectionSchema as r,SelectionPinRequestSchema as n}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as o,computed as a}from"nanostores";import*as c from"valibot";import{createAppointmentDriver as d,withAppointmentTerminalConsumer as u}from"../appointments/appointment.driver.js";import{createAuctionDriver as h,isUpgradeableGenericLoss as l,withAuctionTerminalConsumer as p}from"../auctions/auction.driver.js";import{createError as m,isFanfareError as g,ErrorCodes as y}from"../core/errors.js";import{createLogger as f}from"../core/logger.js";import{generateId as b}from"../core/utils.js";import{createDrawDriver as S,withDrawTerminalConsumer as I}from"../draws/draw.driver.js";import{createQueueDriver as A,withQueueTerminalConsumer as v}from"../queues/queue.driver.js";import{clearExperienceContext as q,clearSequenceContext as w,resolveCapabilityOptions as C,bindDistributionContext as k,bindWaitlist as j,upsertSequenceToken as x,peekSequenceToken as T,getSequenceTokenExpiryMs as E}from"../state/capability-token-registry.js";import{getEventBus as D}from"../state/events.js";import{getSDKStore as R}from"../state/store.js";import{createTimedReleaseDriver as O,withTimedReleaseTerminalConsumer as M}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as G}from"./experience.module.js";import{buildJourneyView as F}from"./journey-view.js";import{createInitialSnapshot as _,getSequencePhase as P,getDistribution as H,normalizeSequenceState as N,buildSnapshot as U,getSequenceId as z,getConsumer as B,getWaitlistAttachment as L,getOutcome as J,getGrant as W,getSequenceState as V,getCheckout as Q,isPreEntryJourney as $,getGates as K,isRoutedState as X,getAdmissionGrantExpiresAt as Z,getParticipation as Y,validateSequenceState as ee}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as te}from"./settlement.js";const ie="Cannot commit a stale sequence transition",se="Cannot commit a sequence transition before routing",re=3e4,ne=6e5,oe=/* @__PURE__ */new Set([y.RATE_LIMITED,y.OVERLOADED,y.SERVICE_UNAVAILABLE,y.TIMEOUT,y.NETWORK_ERROR]),ae=/* @__PURE__ */new Set([y.NETWORK_ERROR,y.TIMEOUT,y.UNAUTHORIZED]),ce=[2e3,5e3,1e4],de=3*ce.length,ue=[2e3,5e3,1e4];function he(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function le(e){const t=Y(e);if(t)return t;if(!X(e))return;const i=e.sequence;return"enterable"===i.phase&&"mechanism"in i&&"auction"===i.mechanism?{id:i.distribution.id,type:"auction"}:void 0}function pe(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase&&"awaiting_assignment"!==e.sequence.selection.status||"ended"===e.sequence.phase)}function me(e){const{grant:t}=e;return t.issuedAt?`${t.sequenceId}|${t.distributionId}|${t.issuedAt}`:t.token}class ge{constructor(e,t){this.experienceId=e,this.sdk=t,this.store=R(),this.routingSession=this.store.session,this.logger=f(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.consecutiveGatedRestores=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredSnapshotNeedsPersist=!1,this.automaticAttemptAtom=o(void 0),this.refusedDistributionAtom=o(/* @__PURE__ */new Set),this.automaticEntry=!0,this.EVENT_BUFFER_LIMIT=20;const i=this.restoreFromStore(),s=i??_(this.experienceId),r=i?le(i):void 0,n=P(s);!r||"participating"!==n&&"enterable"!==n||this.ensureDisplayAtom(r),this.stateAtom=o(s),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=a([this.stateAtom,this.automaticAttemptAtom,this.refusedDistributionAtom],(e,t,i)=>{const s="routed"===e.journeyStage?H(e)?.id:void 0,r=void 0!==s&&i.has(s);return this.buildView(e,t??(r?"refused":void 0))}),this.events$=a(this.stateAtom,e=>e.events),this.latestEvent$=a(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),i?(this.restoredSnapshotNeedsPersist&&this.persist(),this.rearmTimersIfNeeded()):this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{void 0!==e?.automaticEntry&&(this.automaticEntry=e.automaticEntry),e?.reset&&(this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRoutedRuntimeState(),this.setSnapshot(_(this.experienceId))),"accessCode"in(e??{})&&this.patchJourneyBase({accessCode:e?.accessCode??null}),await this.routeAndLoadDistribution(this.stateAtom.get().accessCode)})}async reroute(e={}){await this.runSerializedOperation(async()=>{"accessCode"in e&&this.patchJourneyBase({accessCode:e.accessCode??null}),await this.routeAndLoadDistribution(e.accessCode??this.stateAtom.get().accessCode)})}async submitAccessCode(e){return await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage){await this.routeAndLoadDistribution(e,{transient:!0});const t=this.stateAtom.get();return"routed"!==t.journeyStage?"gated"===t.journeyStage?{outcome:"gated"}:{outcome:"unchanged"}:(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:t.sequenceId})}const i=t.sequenceId,s=t.accessCode,r=new Set(t.offers.map(e=>e.sequence.id));await this.routeAndLoadDistribution(e,{transient:!0});let n=this.stateAtom.get();return"routed"===n.journeyStage&&n.sequenceId===i?{outcome:"unchanged"}:"routed"===n.journeyStage&&r.has(n.sequenceId)?(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:n.sequenceId}):("string"==typeof s&&s!==e&&(await this.routeAndLoadDistribution(s,{carryFrom:t,transient:!0}),n=this.stateAtom.get()),"gated"===n.journeyStage?{outcome:"gated"}:{outcome:"unchanged"})})}async retry(){await this.runSerializedOperation(()=>this.routeAndLoadDistribution(this.stateAtom.get().accessCode))}destroy(){this.destroyed||(this.destroyed=!0,this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.stopPolling(),q(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(_(this.experienceId)))}async resumeFromMe(e){const t=e.active.journeys.find(e=>e.experienceId===this.experienceId&&"routed"===e.journeyStage);if(!t||"routed"!==t.journeyStage)return!1;const i=this.stateAtom.get(),s=N({sequence:We(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s,waitlist:this.buildWaitlistAttachmentFromMe(e,t.sequenceId)});this.setSnapshot(n),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&w(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return"granted"!==s.phase&&"ended"!==s.phase||(this.stopRuntimeMonitoring(!0),this.resetDistributionSelfCheck()),r&&"granted"===s.phase&&(this.clearGrantExpiryTimer(),this.checkoutStarted=!1),this.rearmTimersIfNeeded(),r&&"granted"===s.phase&&"granted"===P(this.stateAtom.get())&&(this.emitEvent("sequence_change","success","user","Grant issued",{grant:s.grant.token}),D().emit("journey:granted",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:s.grant.token,distributionType:s.grant.distributionType})),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get();try{const t=z(e);if(!t)throw m.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,C({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(z(s)!==t)return;const r=B(s),n=this.buildDistributionSummary(i,t,{consumer:r,settlingDistributionId:"routed"===s.journeyStage&&"settling"===s.sequence.phase?s.sequence.distribution.id:void 0}),o=this.buildWaitlistAttachmentFromContext(i,L(s)),a=H(s);n&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&j({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,J(s)),d=this.withTerminalConsumer(r,c),u=!n&&"routed"===s.journeyStage&&("granted"===s.sequence.phase||"ended"===s.sequence.phase)?s.sequence:this.projectSequenceState({distribution:n,consumer:d,grant:W(s),settlement:Qe(s),selection:$e(s),checkout:Ve(s),outcome:c});this.commit(u,{waitlist:o,lastSyncedAt:Date.now(),degraded:!1}),this.emitIfDistributionChanged(a,n),"granted"===u.phase||"ended"===u.phase?this.resetDistributionSelfCheck():this.evaluateDistributionSelfCheck(n),await this.attemptAutomaticEntry()}catch(t){const i=this.stateAtom.get();if(z(i)===z(e)&&"routed"===i.journeyStage&&this.isQuietableDegradationError(t))throw this.markDegraded(),this.markErrorQuietable(t);throw t}})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw m.validationError("Waitlist is not available for this sequence");const r=await this.sdk.waitlists.enter(s);this.commit(t,{waitlist:{waitlistId:s,status:"waitlisted",entryId:r.id}}),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:s})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;"scheduled"===t?.phase&&i&&s&&(await this.sdk.waitlists.leave(s),this.commit(t,{waitlist:{waitlistId:s,status:"not_waitlisted"}}),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:s}))})}async loadSelectionOptions(){const e=z(this.stateAtom.get());if(!e)throw m.validationError("No sequence is available to load selection options");return this.sdk.selections.loadOptions(e,C({experienceId:this.experienceId,sequenceId:e}))}async select(e){await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage)throw m.validationError("No sequence is available to select against");const i=t.sequence;if(!("participating"===i.phase&&"draw"===i.mechanism||"granted"===i.phase&&"timed_release"!==i.mechanism))throw m.validationError("Selection is not available in the current phase");const s=t.sequenceId,r=await this.sdk.selections.select(s,e,C({experienceId:this.experienceId,sequenceId:s})),n=this.stateAtom.get();if("routed"!==n.journeyStage||n.sequenceId!==s)return;const o=n.sequence;if("participating"===i.phase){if("participating"!==o.phase||"draw"!==o.mechanism||o.distribution.id!==i.distribution.id||o.consumer.id!==i.consumer.id)return;if("recorded"!==r.status)return;this.commit({...o,consumer:{...o.consumer,selection:{productId:r.productId,variantId:r.variantId}}});const e=this.displayAtom?.get();"draw"===e?.type&&this.displayAtom?.set({...e,selectionAvailability:void 0})}else{if("granted"!==o.phase||o.mechanism!==i.mechanism||me(o)!==me(i))return;if("recorded"===r.status)return;this.commit({...o,selection:r})}})}claim(){const e=this.stateAtom.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase&&"resolved"!==e.sequence.selection.status)throw m.validationError("Selection must be resolved before claiming the grant");const t=W(e);if(!t)throw m.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:t.distributionType,distributionId:t.distributionId,sequenceId:t.sequenceId}),D().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:t.token}),t}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw m.validationError("No paused checkout to resume");await this.submitCheckout(e)})}async runCheckoutOperation(e){try{await this.runSerializedOperation(e)}catch(t){if(await this.reconcileMissingCheckoutAdmission(t))return void(this.pendingCheckoutPaymentInput=void 0);throw t}}async reconcileMissingCheckoutAdmission(e){if(!g(e)||e.code!==y.ADMISSION_NOT_FOUND)return!1;const t=z(this.stateAtom.get());try{const e=await this.sdk.experiences.getMe();if(this.destroyed||z(this.stateAtom.get())!==t)return!1;const i=await this.resumeFromMe(e),s=this.stateAtom.get();return i&&z(s)===t&&"ended"===P(s)&&void 0!==Q(s)}catch{return!1}}async submitCheckout(e){const t=this.stateAtom.get(),i=V(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw m.validationError("No grant awaiting embedded checkout");const s=z(t),r=i.grant.token,n=me(i);this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(o){throw g(o)&&o.code===y.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),o}const a=this.stateAtom.get(),c=V(a);if(z(a)!==s||"granted"!==c?.phase||me(c)!==n)throw m.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...c,pendingAction:t.nextAction}),void this.emitEvent("sequence_change","info","user","Checkout awaiting verification",{processor:t.nextAction.processor});this.pendingCheckoutPaymentInput=void 0,this.transitionToEnded({checkout:t.checkout})}finally{const e=V(this.stateAtom.get());"granted"!==e?.phase||e.pendingAction||this.rearmTimersIfNeeded()}}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){let i=t?.carryFrom??this.stateAtom.get();const s=this.store.session,r=s?.consumerId!==this.routingSession?.consumerId||s?.type!==this.routingSession?.type;r&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),r&&"routed"===i.journeyStage&&$(i)&&(this.clearRoutedRuntimeState(),w(this.experienceId,i.sequenceId),i=U({base:this.baseFromSnapshot(i),journeyStage:"ready"}),this.setSnapshot(i)),this.routingSession=s;const n=i,o=z(n);let a,c=!1;try{const i=await this.resolveRouting(e,{transient:t?.transient});if(!i||this.destroyed)return;if(a=this.stateAtom.get().revision,c=o===i.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const s=await this.sdk.experiences.getCurrentDistributions(i.sequenceId,C({experienceId:this.experienceId,sequenceId:i.sequenceId}));if(this.destroyed)return;const r=c?B(n):void 0,d=this.buildDistributionSummary(s,i.sequenceId,{consumer:r,settlingDistributionId:c&&"routed"===n.journeyStage&&"settling"===n.sequence.phase?n.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(s,c?L(n):void 0);d&&k({experienceId:this.experienceId,sequenceId:i.sequenceId,distributionContext:d}),u&&j({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:i.sequenceId});const h=c?W(n):void 0,l=c?Ve(n):void 0,p=c?Qe(n):void 0,m=c?this.deriveTerminalOutcomeForClosedDistribution(r,d,J(n)):void 0,g=this.withTerminalConsumer(r,m),y=!d&&c&&"routed"===n.journeyStage&&("granted"===n.sequence.phase||"ended"===n.sequence.phase)?n.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:c?$e(n):void 0,checkout:l,outcome:m});c||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),b=U({base:this.baseFromSnapshot(f,{lastSyncedAt:Date.now(),degraded:!1}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:y,waitlist:u,sequenceRef:i.selectedSequence});this.setSnapshot(b),this.resumeDeferredGrantHydration(),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const S=c?H(n):void 0;this.emitIfDistributionChanged(S,d),this.evaluateDistributionSelfCheck(d),t?.transient||await this.attemptAutomaticEntry()}catch(d){if("routed"===n.journeyStage&&c&&this.isQuietableDegradationError(d))throw this.restoreQuietSnapshot(n),this.markErrorQuietable(d);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===o&&this.restoreClearedSequenceContext(n,o),void 0!==a&&this.stateAtom.get().revision===a)this.setSnapshot(n);else{const e=this.stateAtom.get();"routed"===n.journeyStage?this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:n.sequenceId,offers:n.offers,sequence:n.sequence,waitlist:n.waitlist,sequenceRef:n.sequenceRef})):this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw d}}clearRefusedDistributionOnStanding(e){const t=this.refusedDistributionAtom.get();if(0===t.size||"routed"!==e.journeyStage)return;const i=H(e)?.id;if(void 0===i||!t.has(i))return;const s=e.sequence.phase;"participating"!==s&&"granted"!==s||this.refusedDistributionAtom.set(new Set([...t].filter(e=>e!==i)))}clearAutomaticAttemptIfSuperseded(e){const t=this.automaticAttemptTarget;if(!t||"routing"===e.journeyStage)return;"routed"===e.journeyStage&&e.sequenceId===t.sequenceId&&H(e)?.id===t.distributionId&&"enterable"===e.sequence.phase||this.clearAutomaticAttempt()}clearAutomaticAttempt(){this.automaticAttemptTarget=void 0,this.automaticAttemptAtom.set(void 0)}async attemptAutomaticEntry(){if(!this.automaticEntry||this.destroyed)return;const e=this.buildView(this.stateAtom.get());if("routed"!==e.journeyStage)return;const t=H(this.stateAtom.get())?.id;if(void 0!==t&&this.refusedDistributionAtom.get().has(t))return;const i=this.automaticAttemptAtom.get()?this.automaticAttemptTarget:void 0;if(i&&i.distributionId===t)return;const s=e.sequence;if("enterable"!==s.phase||!("entry"in s)||!s.entry.automatic)return;const r=this.sequenceDrivers[s.mechanism];if(!r?.enterWithinOperation)return;const n=e.sequenceId;this.automaticAttemptTarget={sequenceId:n,distributionId:t},this.automaticAttemptAtom.set("entering");try{await r.enterWithinOperation()}catch(o){if(this.destroyed)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage||e.sequenceId!==n||"enterable"!==e.sequence.phase)return void this.clearAutomaticAttempt();this.clearAutomaticAttempt(),void 0===t||g(o)&&ae.has(o.code)||this.refusedDistributionAtom.set(/* @__PURE__ */new Set([...this.refusedDistributionAtom.get(),t])),this.emitEvent("error","error","user","Automatic entry was refused",{action:"enter",...t?{distributionId:t}:{},...g(o)?{code:o.code}:{}})}}restoreClearedSequenceContext(e,t){const i=H(e);i&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=L(e);s&&j({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&x({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}restoreUndisplacedJourney(e,t){const i=this.stateAtom.get();return i.revision===t?(this.setSnapshot(e),!0):"routing"===i.journeyStage&&(this.setSnapshot(U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})),!0)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=z(i);this.transitionToRouting();const r=this.stateAtom.get().revision;let n;try{n=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(a){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),a}if(this.destroyed)return null;if("gated"===n.outcome&&n.selected){const e=n.selected.gates;return"routed"!==i.journeyStage||$(i)?("routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:n.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null):(this.restoreUndisplacedJourney(i,r)&&(this.rearmTimersIfNeeded(),"ended"!==P(this.stateAtom.get())&&(this.scheduleGatedCapabilityRefresh(),this.emitEvent("requirement","info","user","Gates required",{gates:e}))),null)}if("no_access"===n.outcome||!n.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new G("No access available for this experience");const o=n.selected.sequence.id;if(s&&s!==o&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=T(this.experienceId,s),w(this.experienceId,s)),this.consecutiveGatedRestores=0,n.selected.capabilityGrant&&x({experienceId:this.experienceId,sequenceId:o,token:n.selected.capabilityGrant,expiresAt:n.selected.capabilityGrantExpiresAt}),s!==o){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,r=n.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,r);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:o,...void 0===e?{}:{previousPriority:e},priority:r,direction:a})}return{sequenceId:o,selectedSequence:n.selected.sequence,offers:n.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(K(e).length>0)return;if(e.experience)return;const t=await this.sdk.experiences.enter(this.experienceId);this.destroyed||this.patchJourneyBase({experience:t})}createSequenceDrivers(){return{appointment:d({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),getDisplayState:()=>this.displayAtom?.get()??null,commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>this.ensureDisplayAtom(e),stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),auction:h({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e)}),queue:A({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),timed_release:O({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),draw:S({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)})}}buildView(e,t){return F(e,{start:e=>this.start(e),reroute:e=>this.reroute(e),submitAccessCode:e=>this.submitAccessCode(e),retry:()=>this.retry(),joinWaitlist:()=>this.joinWaitlistAttachment(),exitWaitlist:()=>this.exitWaitlistAttachment(),claim:()=>this.claim(),select:e=>this.select(e),loadSelectionOptions:()=>this.loadSelectionOptions(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers,t)}projectSequenceState(e){if(!e.distribution)return{phase:"unavailable"};const t=this.sequenceDrivers[e.distribution.type];if(!t)throw new Error(`${e.distribution.type} sequence driver is required`);return t.project(e)}patchJourneyBase(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=this.baseFromSnapshot(t,e);switch(t.journeyStage){case"ready":return void this.setSnapshot(U({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(U({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(U({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(U({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,waitlist:t.waitlist,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(t),journeyStage:"gated",gates:e.gates,selected:e.selected}))}baseFromSnapshot(e,t={}){const i=Object.prototype.hasOwnProperty.call(t,"accessCode")?t.accessCode??void 0:e.accessCode,s=Object.prototype.hasOwnProperty.call(t,"degraded")?t.degraded||void 0:e.degraded,r=Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")?t.lastSyncedAt:e.lastSyncedAt;return{revision:e.revision,updatedAt:e.updatedAt,experienceId:e.experienceId,accessCode:i,experience:t.experience??e.experience,events:t.events??e.events,lastSeenEventId:t.lastSeenEventId??e.lastSeenEventId,degraded:s,lastSyncedAt:r}}commit(e,t={}){if(this.destroyed)throw m.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw m.validationError(se);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw m.validationError(ie);const s=Object.prototype.hasOwnProperty.call(t,"waitlist")?t.waitlist??void 0:i.waitlist,r={};Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")&&(r.lastSyncedAt=t.lastSyncedAt),Object.prototype.hasOwnProperty.call(t,"degraded")&&(r.degraded=t.degraded);const n=U({base:this.baseFromSnapshot(i,{...r,events:t.events??i.events}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:e,waitlist:s,sequenceRef:i.sequenceRef});this.setSnapshot(n)}markDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded||this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}clearDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded&&this.setSnapshot(this.rebuildSnapshot(e,{degraded:!1}))}restoreQuietSnapshot(e){this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}isQuietableDegradationError(e){return g(e)&&(e.retryable||oe.has(e.code))}markErrorQuietable(e){return e&&"object"==typeof e&&(e.quietable=!0),e}rebuildSnapshot(e,t={}){const i=this.baseFromSnapshot(e,t);switch(e.journeyStage){case"ready":return U({base:i,journeyStage:"ready"});case"routing":return U({base:i,journeyStage:"routing"});case"gated":return U({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return U({base:i,journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})}}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:b(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t,i){if(e.active){const s=this.sequenceDrivers[e.active.type],r=!0===s?.settlementCanStartWhileActive&&i?.settlingDistributionId===e.active.id?"settling":"open";return{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:r,details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}}if(e.upcoming)return{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,updatedAt:Date.now()};if(e.recentPast){const s=i?.settlingDistributionId===e.recentPast.id||te(i?.consumer,e.recentPast.type,e.recentPast.id)?"settling":"closed";return{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:s,result:e.recentPast.result,closesAt:e.recentPast.endedAt,updatedAt:Date.now()}}}buildWaitlistAttachmentFromContext(e,t){const i=e.waitlist?.id;if(i)return t?.waitlistId===i?t:{waitlistId:i,status:"not_waitlisted"}}buildWaitlistAttachmentFromMe(e,t){const i=e.waitlists?.find(e=>e.experienceId===this.experienceId&&e.sequenceId===t);if(i)return{waitlistId:i.waitlistId,status:"waitlisted"}}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=Je(e[this.experienceId]);return t?"routing"===t.journeyStage||"routed"===t.journeyStage&&$(t)?("routed"===t.journeyStage&&w(this.experienceId,t.sequenceId),this.restoredSnapshotNeedsPersist=!0,U({base:this.baseFromSnapshot(t),journeyStage:"ready"})):t:(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=le(e);"auction"===s?.type&&"auction"!==this.displayAtom?.get().type&&(this.resetDisplayAtom("auction"),this.displayParticipationId=s.id),"routed"!==e.journeyStage&&"routing"!==e.journeyStage&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRefusedDistributionOnStanding(e),this.clearAutomaticAttemptIfSuperseded(e),this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?P(e):void 0,r=t.journeyStage,n="routed"===r?P(t):void 0;i===r&&s===n||D().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw m.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToEnded(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=z(i);if(!s)throw m.validationError(se);const r=H(i),n="checkout"in e?e.checkout:void 0,o="checkout"in e?{type:"completed"}:e,a=this.reinterpretLapsedGrantOutcome(i,o),c={...a,distributionType:a.distributionType??r?.type,distributionId:a.distributionId??r?.id,at:a.at??/* @__PURE__ */(new Date).toISOString()},d=this.withTerminalConsumer(B(i),c,{finalize:!0}),u=this.projectSequenceState({distribution:r,consumer:d,checkout:n,outcome:c});this.commit(u,t),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.emitEvent("sequence_change","expired"===c.type?"warning":"info","user","Sequence ended",{outcome:c}),D().emit("journey:ended",{experienceId:this.experienceId,sequenceId:s,outcome:c})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==P(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){if(!this.destroyed){if("settling"===e.type){const t=this.stateAtom.get();if(!X(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:B(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?"resync"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration():this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(pe(i,i.sequenceId)||this.pendingGrantHydration?.sequenceId!==i.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:i.sequenceId,token:e,expiresAt:t,attempt:0},this.hydrateGrantFromMe(e,t))):this.pendingGrantObservation={token:e,expiresAt:t}}resumeDeferredGrantHydration(){const e=this.pendingGrantObservation;e&&(this.pendingGrantObservation=void 0,this.beginGrantHydration(e.token,e.expiresAt))}async hydrateGrantFromMe(e,t){if(this.destroyed)return;const i=this.pendingGrantHydration,s=this.stateAtom.get();if(i)if("routing"!==s.journeyStage)if(pe(s,i.sequenceId))this.clearGrantHydration();else{if(void 0!==t&&Date.now()>=t)return this.clearAutomaticAttempt(),void this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});try{const e=await this.sdk.experiences.getMe();if(this.destroyed)return;await this.resumeFromMe(e)}catch(r){this.emitEvent("error","warning","system","Could not resolve an observed grant from /me",{grant:e,error:r instanceof Error?r.message:String(r)})}this.scheduleGrantHydrationRetry()}else this.scheduleGrantHydrationRetry()}scheduleGrantHydrationRetry(){const e=this.pendingGrantHydration;if(!e||this.destroyed)return;if(pe(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=ue[e.attempt]??1e4;e.attempt+=1,e.timer=setTimeout(()=>{this.pendingGrantHydration===e&&(e.timer=void 0,this.hydrateGrantFromMe(e.token,e.expiresAt))},t)}clearGrantHydration(){const e=this.pendingGrantHydration;e?.timer&&clearTimeout(e.timer),this.pendingGrantHydration=void 0,this.pendingGrantObservation=void 0}applyMonitorUpdateFromCallback(e){try{this.applyMonitorUpdate(e)}catch(t){if(this.dropStaleCallbackCommit(t,"monitor"))return;throw t}}startMonitorForParticipation(e){const t=this.ensureDisplayAtom(e),i=e=>this.applyMonitorUpdateFromCallback(e),s=e=>{e?this.markDegraded():this.clearDegraded()},r=this.sequenceDrivers[e.type];if(r?.startMonitoring)return r.startMonitoring({participation:e,displayAtom:t,onUpdate:i,onDegradedChange:s}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>r.stopMonitoring?.()});throw new Error(`Unknown distribution type: ${e.type}`)}stopRuntimeMonitoring(e=!1){this.activeMonitor&&(this.activeMonitor.stop(),this.activeMonitor=null),e&&this.clearRuntimeDisplayState()}clearGrantExpiryTimer(){this.grantExpiryTimer&&(clearTimeout(this.grantExpiryTimer),this.grantExpiryTimer=void 0)}clearCapabilityRefreshTimer(){this.capabilityRefreshTimer&&(clearTimeout(this.capabilityRefreshTimer),this.capabilityRefreshTimer=void 0)}clearDistributionSelfCheck(){this.selfCheckTimer&&(clearTimeout(this.selfCheckTimer),this.selfCheckTimer=void 0),this.selfCheckKey=void 0,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0}resetDistributionSelfCheck(){this.clearDistributionSelfCheck(),this.selfCheckTotalAttempts=0}evaluateDistributionSelfCheck(e){if(this.destroyed)return;const t=Date.now();if(!he(e,t))return void this.clearDistributionSelfCheck();const i=`${e?.sequenceId??""}:${e?.id??""}:${e?.opensAt??""}`;if(this.selfCheckKey!==i&&(this.clearDistributionSelfCheck(),this.selfCheckKey=i,this.selfCheckStartedAtMs=t),this.selfCheckTimer)return;if(this.selfCheckAttempts>=ce.length)return;if(this.selfCheckTotalAttempts>=de)return;const s=this.selfCheckStartedAtMs+ce[this.selfCheckAttempts];this.selfCheckAttempts+=1,this.selfCheckTotalAttempts+=1,this.selfCheckTimer=setTimeout(()=>{this.selfCheckFromTimer()},Math.max(0,s-t))}async selfCheckFromTimer(){if(this.selfCheckTimer=void 0,!this.destroyed)try{await this.refreshDistribution()}catch(e){if(this.destroyed||"routed"!==this.stateAtom.get().journeyStage)return;this.emitEvent("error","warning","system","Distribution self-check failed",{error:e instanceof Error?e.message:String(e)})}}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=E(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleGatedCapabilityRefresh(){const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=Math.min(re*2**this.consecutiveGatedRestores,ne);this.consecutiveGatedRestores+=1;const i=E(this.experienceId,e.sequenceId),s=void 0===i?t:Math.max(i-Date.now()-6e4,t);this.scheduleCapabilityRefresh(s)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();"routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),this.pendingGrantHydration&&pe(e,this.pendingGrantHydration.sequenceId)&&this.scheduleGrantHydrationRetry(),"routed"===e.journeyStage&&"granted"===e.sequence.phase&&"awaiting_assignment"===e.sequence.selection.status&&this.pendingGrantHydration?.sequenceId!==e.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:e.sequenceId,token:e.sequence.grant.token,expiresAt:Z(e),attempt:0},this.scheduleGrantHydrationRetry());const t=V(e);if("granted"===t?.phase&&(t.pendingAction||"internal"===t.settlement.processor&&"at_end"===t.settlement.mode&&"in_progress"===t.settlement.state))this.clearGrantExpiryTimer();else if("granted"===t?.phase&&!this.grantExpiryTimer){const t=Z(e);if(t){const e=t-Date.now();e<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),e)}}}async refreshCapabilityFromTimer(){this.capabilityRefreshTimer=void 0;try{if("routed"!==this.stateAtom.get().journeyStage)return void this.clearCapabilityRefreshTimer();if(this.checkoutStarted)return;const e=Date.now();if(void 0!==this.lastCapabilityRefreshAtMs&&e-this.lastCapabilityRefreshAtMs<re)return void this.scheduleCapabilityRefresh(re);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(re*2**this.consecutiveRefreshFailures,ne);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){this.grantExpiryTimer=void 0;try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!g(e)||e.code!==y.VALIDATION_ERROR||e.message!==ie&&e.message!==se)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=le(e),i=z(e);return t&&i?{experienceId:this.experienceId,sequenceId:i,participationId:t.id,participationType:t.type}:null}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),void this.setSnapshot(_(this.experienceId),{persist:!1});const t=Je(e);if(!t)return;const i="routing"===t.journeyStage?U({base:this.baseFromSnapshot(t),journeyStage:"ready"}):t,s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=le(i);r||(this.displayAtom=null,this.displayParticipationId=void 0),this.setSnapshot(i,{persist:!1}),r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){const t=this.displayAtom;if(t?.get().type===e.type&&this.displayParticipationId===e.id)return t;const i=this.resetDisplayAtom(e.type);return this.displayParticipationId=e.id,i}resetDisplayAtom(e){const t=this.displayAtom;if(!t||t.get().type!==e){const t=o({type:e});return this.displayAtom=t,t}return t.set({type:e}),t}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();if(!e)return this.displayAtom=null,void(this.displayParticipationId=void 0);this.displayParticipationId=void 0,this.resetDisplayAtom(e.participationType)}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i&&!l(i))return i;if(!t||"closed"!==t.lifecycle||!e)return i;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:i??{type:"closed",distributionType:t.type,distributionId:t.id}}withTerminalConsumer(e,t,i){switch(e?.mechanism){case"appointment":return u(e,t);case"auction":return p(e,t,i);case"draw":return I(e,t);case"queue":return v(e,t);case"timed_release":return M(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const ye=c.picklist(["queue","draw","auction","timed_release","appointment"]),fe=ye,be=c.picklist(["queue","draw","auction","timed_release"]),Se=c.picklist(["draw","auction"]),Ie=c.strictObject({id:c.string(),ts:c.number(),kind:c.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:c.picklist(["info","success","warning","error"]),audience:c.picklist(["user","system","analytics"]),message:c.string(),detail:c.optional(c.record(c.string(),c.unknown()))}),Ae=c.object({id:c.string(),name:c.string(),experienceId:c.string(),priority:c.number(),color:c.nullable(c.string()),availability:c.optional(s)}),ve=c.strictObject({type:ye,id:c.string(),sequenceId:c.optional(c.string()),lifecycle:c.string(),startsAt:c.optional(c.string()),opensAt:c.optional(c.string()),closesAt:c.optional(c.string()),settlesAt:c.optional(c.string()),result:c.optional(c.string()),details:c.optional(c.unknown()),updatedAt:c.optional(c.number())}),qe=c.strictObject({status:c.string(),selection:c.optional(n),id:c.optional(c.string()),distributionId:c.optional(c.string()),waitlistId:c.optional(c.string()),enteredAt:c.optional(c.string()),admissionGrant:c.optional(c.string()),grantedAt:c.optional(c.string()),deniedReason:c.optional(c.string()),position:c.optional(c.number()),highestBid:c.optional(c.string()),currentBid:c.optional(c.string()),slotId:c.optional(c.string()),locationId:c.optional(c.string()),bookedAt:c.optional(c.string()),checkedInAt:c.optional(c.string()),completedAt:c.optional(c.string()),cancelledAt:c.optional(c.string()),noShowAt:c.optional(c.string())}),we=c.strictObject({token:c.string(),distributionType:be,distributionId:c.string(),sequenceId:c.string(),experienceId:c.string(),issuedAt:c.optional(c.string()),expiresAt:c.optional(c.string())}),Ce=c.strictObject({orderId:c.string(),amount:c.string(),currencyCode:c.string(),paymentMethodLast4:c.optional(c.string()),paymentMethodBrand:c.optional(c.string())}),ke=c.strictObject({publishableKey:c.string(),stripeAccount:c.optional(c.string())}),je=c.strictObject({type:c.string(),reason:c.optional(c.string()),at:c.optional(c.string()),distributionType:c.optional(ye),distributionId:c.optional(c.string())}),xe=c.strictObject({phase:c.literal("unavailable"),reason:c.optional(c.string())}),Te=c.strictObject({waitlistId:c.string(),status:c.picklist(["not_waitlisted","waitlisted"]),entryId:c.optional(c.string())}),Ee=c.strictObject({phase:c.literal("scheduled"),mechanism:fe,distribution:ve}),De=c.strictObject({phase:c.literal("enterable"),mechanism:fe,distribution:ve,consumer:qe,paymentProvider:c.optional(ke)}),Re=c.strictObject({phase:c.literal("participating"),mechanism:fe,distribution:ve,consumer:qe}),Oe=c.strictObject({phase:c.literal("settling"),mechanism:Se,distribution:ve,consumer:qe}),Me=c.variant("processor",[c.strictObject({processor:c.literal("external")}),c.strictObject({processor:c.literal("internal"),mode:c.literal("at_end"),state:c.picklist(["awaiting","in_progress"]),paymentProvider:c.optional(ke)}),c.strictObject({processor:c.literal("internal"),mode:c.literal("pre_auth")})]),Ge=c.strictObject({phase:c.literal("granted"),mechanism:be,distribution:ve,consumer:qe,grant:we,settlement:Me,selection:r}),Fe=c.strictObject({phase:c.literal("ended"),mechanism:fe,distribution:ve,consumer:qe,outcome:je,checkout:c.optional(Ce)}),_e=c.union([xe,Ee,De,Re,Oe,Ge,Fe]),Pe={schemaVersion:c.literal(6),revision:c.number(),updatedAt:c.number(),experienceId:c.string(),accessCode:c.optional(c.string()),experience:c.optional(c.unknown()),events:c.array(Ie),lastSeenEventId:c.optional(c.string()),lastSyncedAt:c.optional(c.number())},He=c.strictObject({...Pe,journeyStage:c.literal("ready")}),Ne=c.strictObject({...Pe,journeyStage:c.literal("routing")}),Ue=c.variant("type",[c.object({...c.omit(e,["reason"]).entries,requirement:c.optional(c.picklist(["session","authenticated"]),"authenticated")}),c.omit(t,["reason"]),c.omit(i,["reason"])]),ze=c.strictObject({...Pe,journeyStage:c.literal("gated"),gates:c.array(Ue),selected:c.optional(c.strictObject({sequence:c.unknown(),status:c.literal("gated")}))}),Be=c.strictObject({...Pe,journeyStage:c.literal("routed"),sequenceId:c.string(),offers:c.array(c.object({sequence:c.unknown(),status:c.literal("gated"),gates:c.array(Ue)})),sequence:_e,waitlist:c.optional(Te),sequenceRef:c.optional(Ae)}),Le=c.union([He,Ne,ze,Be]);function Je(e){const t=c.safeParse(Le,e);if(!t.success)return null;const{schemaVersion:i,...s}=t.output;if("routed"===s.journeyStage)try{ee(s.sequence)}catch{return null}return s}function We(e){if("participating"!==e.phase&&"settling"!==e.phase)return e;if(!("selection"in e))return e;const{selection:t,...i}=e;return void 0===t?i:{...i,consumer:{...i.consumer,selection:t}}}function Ve(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?Q(e):e.sequence.checkout}function Qe(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function $e(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{ge as ExperienceJourney,he as isSelfContradictingDistribution};
1
+ import{AuthenticationRoutingGateV1Schema as e,AccessCodeRoutingGateV1Schema as t,BotCheckRoutingGateV1Schema as i,RoutingSequenceAvailabilityV1Schema as s}from"@fanfare-io/fanfare-sdk-contracts/routing";import{GrantedSelectionSchema as r,SelectionPinRequestSchema as n}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as o,computed as a}from"nanostores";import*as c from"valibot";import{createAppointmentDriver as d,withAppointmentTerminalConsumer as u}from"../appointments/appointment.driver.js";import{createAuctionDriver as h,isUpgradeableGenericLoss as l,withAuctionTerminalConsumer as p}from"../auctions/auction.driver.js";import{createError as m,isFanfareError as g,ErrorCodes as y}from"../core/errors.js";import{createLogger as f}from"../core/logger.js";import{generateId as b}from"../core/utils.js";import{createDrawDriver as S,withDrawTerminalConsumer as I}from"../draws/draw.driver.js";import{createQueueDriver as A,withQueueTerminalConsumer as v}from"../queues/queue.driver.js";import{clearExperienceContext as q,clearSequenceContext as w,resolveCapabilityOptions as C,bindDistributionContext as k,bindWaitlist as j,upsertSequenceToken as x,peekSequenceToken as T,getSequenceTokenExpiryMs as E}from"../state/capability-token-registry.js";import{getEventBus as D}from"../state/events.js";import{getSDKStore as R}from"../state/store.js";import{createTimedReleaseDriver as O,withTimedReleaseTerminalConsumer as M}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as G}from"./experience.module.js";import{buildJourneyView as F}from"./journey-view.js";import{createInitialSnapshot as _,getSequencePhase as P,getDistribution as H,normalizeSequenceState as N,buildSnapshot as U,getSequenceId as z,getConsumer as B,getWaitlistAttachment as L,getOutcome as J,getGrant as W,getSequenceState as V,getCheckout as Q,isPreEntryJourney as $,getGates as K,isRoutedState as X,getAdmissionGrantExpiresAt as Z,getParticipation as Y,validateSequenceState as ee}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as te}from"./settlement.js";const ie="Cannot commit a stale sequence transition",se="Cannot commit a sequence transition before routing",re=3e4,ne=6e5,oe=/* @__PURE__ */new Set([y.RATE_LIMITED,y.OVERLOADED,y.SERVICE_UNAVAILABLE,y.TIMEOUT,y.NETWORK_ERROR]),ae=/* @__PURE__ */new Set([y.NETWORK_ERROR,y.TIMEOUT,y.UNAUTHORIZED]),ce=[2e3,5e3,1e4],de=3*ce.length,ue=[2e3,5e3,1e4];function he(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function le(e){const t=Y(e);if(t)return t;if(!X(e))return;const i=e.sequence;return"enterable"===i.phase&&"mechanism"in i&&"auction"===i.mechanism?{id:i.distribution.id,type:"auction"}:void 0}function pe(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase&&"awaiting_assignment"!==e.sequence.selection.status||"ended"===e.sequence.phase)}function me(e){const{grant:t}=e;return t.issuedAt?`${t.sequenceId}|${t.distributionId}|${t.issuedAt}`:t.token}class ge{constructor(e,t){this.experienceId=e,this.sdk=t,this.store=R(),this.routingSession=this.store.session,this.logger=f(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.consecutiveGatedRestores=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredSnapshotNeedsPersist=!1,this.automaticAttemptAtom=o(void 0),this.refusedDistributionAtom=o(/* @__PURE__ */new Set),this.automaticEntry=!0,this.EVENT_BUFFER_LIMIT=20;const i=this.restoreFromStore(),s=i??_(this.experienceId),r=i?le(i):void 0,n=P(s);!r||"participating"!==n&&"enterable"!==n||this.ensureDisplayAtom(r),this.stateAtom=o(s),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=a([this.stateAtom,this.automaticAttemptAtom,this.refusedDistributionAtom],(e,t,i)=>{const s="routed"===e.journeyStage?H(e)?.id:void 0,r=void 0!==s&&i.has(s);return this.buildView(e,t??(r?"refused":void 0))}),this.events$=a(this.stateAtom,e=>e.events),this.latestEvent$=a(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),i?(this.restoredSnapshotNeedsPersist&&this.persist(),this.rearmTimersIfNeeded()):this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{void 0!==e?.automaticEntry&&(this.automaticEntry=e.automaticEntry),e?.reset&&(this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRoutedRuntimeState(),this.setSnapshot(_(this.experienceId))),"accessCode"in(e??{})&&this.patchJourneyBase({accessCode:e?.accessCode??null}),await this.routeAndLoadDistribution(this.stateAtom.get().accessCode)})}async reroute(e={}){await this.runSerializedOperation(async()=>{"accessCode"in e&&this.patchJourneyBase({accessCode:e.accessCode??null}),await this.routeAndLoadDistribution(e.accessCode??this.stateAtom.get().accessCode)})}async submitAccessCode(e){return await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage){await this.routeAndLoadDistribution(e,{transient:!0});const t=this.stateAtom.get();return"routed"!==t.journeyStage?"gated"===t.journeyStage?{outcome:"gated"}:{outcome:"unchanged"}:(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:t.sequenceId})}const i=t.sequenceId,s=t.accessCode,r=new Set(t.offers.map(e=>e.sequence.id));await this.routeAndLoadDistribution(e,{transient:!0});let n=this.stateAtom.get();return"routed"===n.journeyStage&&n.sequenceId===i?{outcome:"unchanged"}:"routed"===n.journeyStage&&r.has(n.sequenceId)?(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:n.sequenceId}):("string"==typeof s&&s!==e&&(await this.routeAndLoadDistribution(s,{carryFrom:t,transient:!0}),n=this.stateAtom.get()),"gated"===n.journeyStage?{outcome:"gated"}:{outcome:"unchanged"})})}async retry(){await this.runSerializedOperation(()=>this.routeAndLoadDistribution(this.stateAtom.get().accessCode))}destroy(){this.destroyed||(this.destroyed=!0,this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.stopPolling(),q(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(_(this.experienceId)))}async resumeFromMe(e){const t=e.active.journeys.find(e=>e.experienceId===this.experienceId&&"routed"===e.journeyStage);if(!t||"routed"!==t.journeyStage)return!1;const i=this.stateAtom.get(),s=N({sequence:We(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s,waitlist:this.buildWaitlistAttachmentFromMe(e,t.sequenceId)});this.setSnapshot(n),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&w(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return"granted"!==s.phase&&"ended"!==s.phase||(this.stopRuntimeMonitoring(!0),this.resetDistributionSelfCheck()),r&&"granted"===s.phase&&(this.clearGrantExpiryTimer(),this.checkoutStarted=!1),this.rearmTimersIfNeeded(),r&&"granted"===s.phase&&"granted"===P(this.stateAtom.get())&&(this.emitEvent("sequence_change","success","user","Grant issued",{grant:s.grant.token}),D().emit("journey:granted",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:s.grant.token,distributionType:s.grant.distributionType})),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get();try{const t=z(e);if(!t)throw m.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,C({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(z(s)!==t)return;const r=B(s),n=this.buildDistributionSummary(i,t,{consumer:r,settlingDistributionId:"routed"===s.journeyStage&&"settling"===s.sequence.phase?s.sequence.distribution.id:void 0}),o=this.buildWaitlistAttachmentFromContext(i,L(s)),a=H(s);n&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&j({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,J(s)),d=this.withTerminalConsumer(r,c),u=!n&&"routed"===s.journeyStage&&("granted"===s.sequence.phase||"ended"===s.sequence.phase)?s.sequence:this.projectSequenceState({distribution:n,consumer:d,grant:W(s),settlement:Qe(s),selection:$e(s),checkout:Ve(s),outcome:c});this.commit(u,{waitlist:o,lastSyncedAt:Date.now(),degraded:!1}),this.emitIfDistributionChanged(a,n),"granted"===u.phase||"ended"===u.phase?this.resetDistributionSelfCheck():this.evaluateDistributionSelfCheck(n),await this.attemptAutomaticEntry()}catch(t){const i=this.stateAtom.get();if(z(i)===z(e)&&"routed"===i.journeyStage&&this.isQuietableDegradationError(t))throw this.markDegraded(),this.markErrorQuietable(t);throw t}})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw m.validationError("Waitlist is not available for this sequence");const r=await this.sdk.waitlists.enter(s);this.commit(t,{waitlist:{waitlistId:s,status:"waitlisted",entryId:r.id}}),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:s})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;"scheduled"===t?.phase&&i&&s&&(await this.sdk.waitlists.leave(s),this.commit(t,{waitlist:{waitlistId:s,status:"not_waitlisted"}}),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:s}))})}async loadSelectionOptions(){const e=z(this.stateAtom.get());if(!e)throw m.validationError("No sequence is available to load selection options");return this.sdk.selections.loadOptions(e,C({experienceId:this.experienceId,sequenceId:e}))}async select(e){await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage)throw m.validationError("No sequence is available to select against");const i=t.sequence;if(!("participating"===i.phase&&"draw"===i.mechanism||"granted"===i.phase&&"timed_release"!==i.mechanism))throw m.validationError("Selection is not available in the current phase");const s=t.sequenceId,r=await this.sdk.selections.select(s,e,C({experienceId:this.experienceId,sequenceId:s})),n=this.stateAtom.get();if("routed"!==n.journeyStage||n.sequenceId!==s)return;const o=n.sequence;if("participating"===i.phase){if("participating"!==o.phase||"draw"!==o.mechanism||o.distribution.id!==i.distribution.id||o.consumer.id!==i.consumer.id)return;if("recorded"!==r.status)return;this.commit({...o,consumer:{...o.consumer,selection:{productId:r.productId,variantId:r.variantId}}});const e=this.displayAtom?.get();"draw"===e?.type&&this.displayAtom?.set({...e,selectionAvailability:void 0})}else{if("granted"!==o.phase||o.mechanism!==i.mechanism||me(o)!==me(i))return;if("recorded"===r.status)return;this.commit({...o,selection:r})}})}claim(){const e=this.stateAtom.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase&&"resolved"!==e.sequence.selection.status)throw m.validationError("Selection must be resolved before claiming the grant");const t=W(e);if(!t)throw m.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:t.distributionType,distributionId:t.distributionId,sequenceId:t.sequenceId}),D().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:t.token}),t}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw m.validationError("No paused checkout to resume");await this.submitCheckout(e)})}async runCheckoutOperation(e){try{await this.runSerializedOperation(e)}catch(t){if(await this.reconcileMissingCheckoutAdmission(t))return void(this.pendingCheckoutPaymentInput=void 0);throw t}}async reconcileMissingCheckoutAdmission(e){if(!g(e)||e.code!==y.ADMISSION_NOT_FOUND)return!1;const t=z(this.stateAtom.get());try{const e=await this.sdk.experiences.getMe();if(this.destroyed||z(this.stateAtom.get())!==t)return!1;const i=await this.resumeFromMe(e),s=this.stateAtom.get();return i&&z(s)===t&&"ended"===P(s)&&void 0!==Q(s)}catch{return!1}}async submitCheckout(e){const t=this.stateAtom.get(),i=V(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw m.validationError("No grant awaiting embedded checkout");const s=z(t),r=i.grant.token,n=me(i);this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(o){throw g(o)&&o.code===y.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),o}const a=this.stateAtom.get(),c=V(a);if(z(a)!==s||"granted"!==c?.phase||me(c)!==n)throw m.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...c,pendingAction:t.nextAction}),void this.emitEvent("sequence_change","info","user","Checkout awaiting verification",{processor:t.nextAction.processor});this.pendingCheckoutPaymentInput=void 0,this.transitionToEnded({checkout:t.checkout})}finally{const e=V(this.stateAtom.get());"granted"!==e?.phase||e.pendingAction||this.rearmTimersIfNeeded()}}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){let i=t?.carryFrom??this.stateAtom.get();const s=this.store.session,r=s?.consumerId!==this.routingSession?.consumerId||s?.type!==this.routingSession?.type;r&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),r&&"routed"===i.journeyStage&&$(i)&&(this.clearRoutedRuntimeState(),w(this.experienceId,i.sequenceId),i=U({base:this.baseFromSnapshot(i),journeyStage:"ready"}),this.setSnapshot(i)),this.routingSession=s;const n=i,o=z(n);let a,c=!1;try{const i=await this.resolveRouting(e,{transient:t?.transient});if(!i||this.destroyed)return;if(a=this.stateAtom.get().revision,c=o===i.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const s=await this.sdk.experiences.getCurrentDistributions(i.sequenceId,C({experienceId:this.experienceId,sequenceId:i.sequenceId}));if(this.destroyed)return;const r=c?B(n):void 0,d=this.buildDistributionSummary(s,i.sequenceId,{consumer:r,settlingDistributionId:c&&"routed"===n.journeyStage&&"settling"===n.sequence.phase?n.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(s,c?L(n):void 0);d&&k({experienceId:this.experienceId,sequenceId:i.sequenceId,distributionContext:d}),u&&j({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:i.sequenceId});const h=c?W(n):void 0,l=c?Ve(n):void 0,p=c?Qe(n):void 0,m=c?this.deriveTerminalOutcomeForClosedDistribution(r,d,J(n)):void 0,g=this.withTerminalConsumer(r,m),y=!d&&c&&"routed"===n.journeyStage&&("granted"===n.sequence.phase||"ended"===n.sequence.phase)?n.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:c?$e(n):void 0,checkout:l,outcome:m});c||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),b=U({base:this.baseFromSnapshot(f,{lastSyncedAt:Date.now(),degraded:!1}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:y,waitlist:u,sequenceRef:i.selectedSequence});this.setSnapshot(b),this.resumeDeferredGrantHydration(),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const S=c?H(n):void 0;this.emitIfDistributionChanged(S,d),this.evaluateDistributionSelfCheck(d),t?.transient||await this.attemptAutomaticEntry()}catch(d){if("routed"===n.journeyStage&&c&&this.isQuietableDegradationError(d))throw this.restoreQuietSnapshot(n),this.markErrorQuietable(d);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===o&&this.restoreClearedSequenceContext(n,o),void 0!==a&&this.stateAtom.get().revision===a)this.setSnapshot(n);else{const e=this.stateAtom.get();"routed"===n.journeyStage?this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:n.sequenceId,offers:n.offers,sequence:n.sequence,waitlist:n.waitlist,sequenceRef:n.sequenceRef})):this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw d}}clearRefusedDistributionOnStanding(e){const t=this.refusedDistributionAtom.get();if(0===t.size||"routed"!==e.journeyStage)return;const i=H(e)?.id;if(void 0===i||!t.has(i))return;const s=e.sequence.phase;"participating"!==s&&"granted"!==s||this.refusedDistributionAtom.set(new Set([...t].filter(e=>e!==i)))}clearAutomaticAttemptIfSuperseded(e){const t=this.automaticAttemptTarget;if(!t||"routing"===e.journeyStage)return;"routed"===e.journeyStage&&e.sequenceId===t.sequenceId&&H(e)?.id===t.distributionId&&"enterable"===e.sequence.phase||this.clearAutomaticAttempt()}clearAutomaticAttempt(){this.automaticAttemptTarget=void 0,this.automaticAttemptAtom.set(void 0)}async attemptAutomaticEntry(){if(!this.automaticEntry||this.destroyed)return;const e=this.buildView(this.stateAtom.get());if("routed"!==e.journeyStage)return;const t=H(this.stateAtom.get())?.id;if(void 0!==t&&this.refusedDistributionAtom.get().has(t))return;const i=this.automaticAttemptAtom.get()?this.automaticAttemptTarget:void 0;if(i&&i.distributionId===t)return;const s=e.sequence;if("enterable"!==s.phase||!("entry"in s)||!s.entry.automatic)return;const r=this.sequenceDrivers[s.mechanism];if(!r?.enterWithinOperation)return;const n=e.sequenceId;this.automaticAttemptTarget={sequenceId:n,distributionId:t},this.automaticAttemptAtom.set("entering");try{await r.enterWithinOperation()}catch(o){if(this.destroyed)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage||e.sequenceId!==n||"enterable"!==e.sequence.phase)return void this.clearAutomaticAttempt();this.clearAutomaticAttempt(),void 0===t||g(o)&&ae.has(o.code)||this.refusedDistributionAtom.set(/* @__PURE__ */new Set([...this.refusedDistributionAtom.get(),t])),this.emitEvent("error","error","user","Automatic entry was refused",{action:"enter",...t?{distributionId:t}:{},...g(o)?{code:o.code}:{}})}}restoreClearedSequenceContext(e,t){const i=H(e);i&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=L(e);s&&j({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&x({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}restoreUndisplacedJourney(e,t){const i=this.stateAtom.get();return i.revision===t?(this.setSnapshot(e),!0):"routing"===i.journeyStage&&(this.setSnapshot(U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})),!0)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=z(i);this.transitionToRouting();const r=this.stateAtom.get().revision;let n;try{n=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(a){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),a}if(this.destroyed)return null;if("gated"===n.outcome&&n.selected){const e=n.selected.gates;return"routed"!==i.journeyStage||$(i)?("routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:n.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null):(this.restoreUndisplacedJourney(i,r)&&(this.rearmTimersIfNeeded(),"ended"!==P(this.stateAtom.get())&&(this.scheduleGatedCapabilityRefresh(),this.emitEvent("requirement","info","user","Gates required",{gates:e}))),null)}if("no_access"===n.outcome||!n.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new G("No access available for this experience");const o=n.selected.sequence.id;if(s&&s!==o&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=T(this.experienceId,s),w(this.experienceId,s)),this.consecutiveGatedRestores=0,n.selected.capabilityGrant&&x({experienceId:this.experienceId,sequenceId:o,token:n.selected.capabilityGrant,expiresAt:n.selected.capabilityGrantExpiresAt}),s!==o){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,r=n.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,r);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:o,...void 0===e?{}:{previousPriority:e},priority:r,direction:a})}return{sequenceId:o,selectedSequence:n.selected.sequence,offers:n.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(K(e).length>0)return;if(e.experience)return;const t=await this.sdk.experiences.enter(this.experienceId);this.destroyed||this.patchJourneyBase({experience:t})}createSequenceDrivers(){return{appointment:d({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),getDisplayState:()=>this.displayAtom?.get()??null,commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>this.ensureDisplayAtom(e),stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),auction:h({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e)}),queue:A({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),timed_release:O({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),draw:S({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)})}}buildView(e,t){return F(e,{start:e=>this.start(e),reroute:e=>this.reroute(e),submitAccessCode:e=>this.submitAccessCode(e),retry:()=>this.retry(),joinWaitlist:()=>this.joinWaitlistAttachment(),exitWaitlist:()=>this.exitWaitlistAttachment(),claim:()=>this.claim(),select:e=>this.select(e),loadSelectionOptions:()=>this.loadSelectionOptions(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers,t)}projectSequenceState(e){if(!e.distribution)return{phase:"unavailable"};const t=this.sequenceDrivers[e.distribution.type];if(!t)throw new Error(`${e.distribution.type} sequence driver is required`);return t.project(e)}patchJourneyBase(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=this.baseFromSnapshot(t,e);switch(t.journeyStage){case"ready":return void this.setSnapshot(U({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(U({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(U({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(U({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,waitlist:t.waitlist,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(t),journeyStage:"gated",gates:e.gates,selected:e.selected}))}baseFromSnapshot(e,t={}){const i=Object.prototype.hasOwnProperty.call(t,"accessCode")?t.accessCode??void 0:e.accessCode,s=Object.prototype.hasOwnProperty.call(t,"degraded")?t.degraded||void 0:e.degraded,r=Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")?t.lastSyncedAt:e.lastSyncedAt;return{revision:e.revision,updatedAt:e.updatedAt,experienceId:e.experienceId,accessCode:i,experience:t.experience??e.experience,events:t.events??e.events,lastSeenEventId:t.lastSeenEventId??e.lastSeenEventId,degraded:s,lastSyncedAt:r}}commit(e,t={}){if(this.destroyed)throw m.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw m.validationError(se);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw m.validationError(ie);const s=Object.prototype.hasOwnProperty.call(t,"waitlist")?t.waitlist??void 0:i.waitlist,r={};Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")&&(r.lastSyncedAt=t.lastSyncedAt),Object.prototype.hasOwnProperty.call(t,"degraded")&&(r.degraded=t.degraded);const n=U({base:this.baseFromSnapshot(i,{...r,events:t.events??i.events}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:e,waitlist:s,sequenceRef:i.sequenceRef});this.setSnapshot(n)}markDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded||this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}clearDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded&&this.setSnapshot(this.rebuildSnapshot(e,{degraded:!1}))}restoreQuietSnapshot(e){this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}isQuietableDegradationError(e){return g(e)&&(e.retryable||oe.has(e.code))}markErrorQuietable(e){return e&&"object"==typeof e&&(e.quietable=!0),e}rebuildSnapshot(e,t={}){const i=this.baseFromSnapshot(e,t);switch(e.journeyStage){case"ready":return U({base:i,journeyStage:"ready"});case"routing":return U({base:i,journeyStage:"routing"});case"gated":return U({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return U({base:i,journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})}}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:b(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t,i){if(e.active){const s=this.sequenceDrivers[e.active.type],r=!0===s?.settlementCanStartWhileActive&&i?.settlingDistributionId===e.active.id?"settling":"open";return{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:r,details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}}if(e.upcoming)return{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",...e.upcoming.details?{details:e.upcoming.details,closesAt:e.upcoming.details.closeAt}:{},startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,updatedAt:Date.now()};if(e.recentPast){const s=i?.settlingDistributionId===e.recentPast.id||te(i?.consumer,e.recentPast.type,e.recentPast.id)?"settling":"closed";return{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:s,result:e.recentPast.result,closesAt:e.recentPast.endedAt,updatedAt:Date.now()}}}buildWaitlistAttachmentFromContext(e,t){const i=e.waitlist?.id;if(i)return t?.waitlistId===i?t:{waitlistId:i,status:"not_waitlisted"}}buildWaitlistAttachmentFromMe(e,t){const i=e.waitlists?.find(e=>e.experienceId===this.experienceId&&e.sequenceId===t);if(i)return{waitlistId:i.waitlistId,status:"waitlisted"}}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=Je(e[this.experienceId]);return t?"routing"===t.journeyStage||"routed"===t.journeyStage&&$(t)?("routed"===t.journeyStage&&w(this.experienceId,t.sequenceId),this.restoredSnapshotNeedsPersist=!0,U({base:this.baseFromSnapshot(t),journeyStage:"ready"})):t:(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=le(e);"auction"===s?.type&&"auction"!==this.displayAtom?.get().type&&(this.resetDisplayAtom("auction"),this.displayParticipationId=s.id),"routed"!==e.journeyStage&&"routing"!==e.journeyStage&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRefusedDistributionOnStanding(e),this.clearAutomaticAttemptIfSuperseded(e),this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?P(e):void 0,r=t.journeyStage,n="routed"===r?P(t):void 0;i===r&&s===n||D().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw m.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToEnded(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=z(i);if(!s)throw m.validationError(se);const r=H(i),n="checkout"in e?e.checkout:void 0,o="checkout"in e?{type:"completed"}:e,a=this.reinterpretLapsedGrantOutcome(i,o),c={...a,distributionType:a.distributionType??r?.type,distributionId:a.distributionId??r?.id,at:a.at??/* @__PURE__ */(new Date).toISOString()},d=this.withTerminalConsumer(B(i),c,{finalize:!0}),u=this.projectSequenceState({distribution:r,consumer:d,checkout:n,outcome:c});this.commit(u,t),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.emitEvent("sequence_change","expired"===c.type?"warning":"info","user","Sequence ended",{outcome:c}),D().emit("journey:ended",{experienceId:this.experienceId,sequenceId:s,outcome:c})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==P(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){if(!this.destroyed){if("settling"===e.type){const t=this.stateAtom.get();if(!X(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:B(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?"resync"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration():this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(pe(i,i.sequenceId)||this.pendingGrantHydration?.sequenceId!==i.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:i.sequenceId,token:e,expiresAt:t,attempt:0},this.hydrateGrantFromMe(e,t))):this.pendingGrantObservation={token:e,expiresAt:t}}resumeDeferredGrantHydration(){const e=this.pendingGrantObservation;e&&(this.pendingGrantObservation=void 0,this.beginGrantHydration(e.token,e.expiresAt))}async hydrateGrantFromMe(e,t){if(this.destroyed)return;const i=this.pendingGrantHydration,s=this.stateAtom.get();if(i)if("routing"!==s.journeyStage)if(pe(s,i.sequenceId))this.clearGrantHydration();else{if(void 0!==t&&Date.now()>=t)return this.clearAutomaticAttempt(),void this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});try{const e=await this.sdk.experiences.getMe();if(this.destroyed)return;await this.resumeFromMe(e)}catch(r){this.emitEvent("error","warning","system","Could not resolve an observed grant from /me",{grant:e,error:r instanceof Error?r.message:String(r)})}this.scheduleGrantHydrationRetry()}else this.scheduleGrantHydrationRetry()}scheduleGrantHydrationRetry(){const e=this.pendingGrantHydration;if(!e||this.destroyed)return;if(pe(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=ue[e.attempt]??1e4;e.attempt+=1,e.timer=setTimeout(()=>{this.pendingGrantHydration===e&&(e.timer=void 0,this.hydrateGrantFromMe(e.token,e.expiresAt))},t)}clearGrantHydration(){const e=this.pendingGrantHydration;e?.timer&&clearTimeout(e.timer),this.pendingGrantHydration=void 0,this.pendingGrantObservation=void 0}applyMonitorUpdateFromCallback(e){try{this.applyMonitorUpdate(e)}catch(t){if(this.dropStaleCallbackCommit(t,"monitor"))return;throw t}}startMonitorForParticipation(e){const t=this.ensureDisplayAtom(e),i=e=>this.applyMonitorUpdateFromCallback(e),s=e=>{e?this.markDegraded():this.clearDegraded()},r=this.sequenceDrivers[e.type];if(r?.startMonitoring)return r.startMonitoring({participation:e,displayAtom:t,onUpdate:i,onDegradedChange:s}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>r.stopMonitoring?.()});throw new Error(`Unknown distribution type: ${e.type}`)}stopRuntimeMonitoring(e=!1){this.activeMonitor&&(this.activeMonitor.stop(),this.activeMonitor=null),e&&this.clearRuntimeDisplayState()}clearGrantExpiryTimer(){this.grantExpiryTimer&&(clearTimeout(this.grantExpiryTimer),this.grantExpiryTimer=void 0)}clearCapabilityRefreshTimer(){this.capabilityRefreshTimer&&(clearTimeout(this.capabilityRefreshTimer),this.capabilityRefreshTimer=void 0)}clearDistributionSelfCheck(){this.selfCheckTimer&&(clearTimeout(this.selfCheckTimer),this.selfCheckTimer=void 0),this.selfCheckKey=void 0,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0}resetDistributionSelfCheck(){this.clearDistributionSelfCheck(),this.selfCheckTotalAttempts=0}evaluateDistributionSelfCheck(e){if(this.destroyed)return;const t=Date.now();if(!he(e,t))return void this.clearDistributionSelfCheck();const i=`${e?.sequenceId??""}:${e?.id??""}:${e?.opensAt??""}`;if(this.selfCheckKey!==i&&(this.clearDistributionSelfCheck(),this.selfCheckKey=i,this.selfCheckStartedAtMs=t),this.selfCheckTimer)return;if(this.selfCheckAttempts>=ce.length)return;if(this.selfCheckTotalAttempts>=de)return;const s=this.selfCheckStartedAtMs+ce[this.selfCheckAttempts];this.selfCheckAttempts+=1,this.selfCheckTotalAttempts+=1,this.selfCheckTimer=setTimeout(()=>{this.selfCheckFromTimer()},Math.max(0,s-t))}async selfCheckFromTimer(){if(this.selfCheckTimer=void 0,!this.destroyed)try{await this.refreshDistribution()}catch(e){if(this.destroyed||"routed"!==this.stateAtom.get().journeyStage)return;this.emitEvent("error","warning","system","Distribution self-check failed",{error:e instanceof Error?e.message:String(e)})}}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=E(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleGatedCapabilityRefresh(){const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=Math.min(re*2**this.consecutiveGatedRestores,ne);this.consecutiveGatedRestores+=1;const i=E(this.experienceId,e.sequenceId),s=void 0===i?t:Math.max(i-Date.now()-6e4,t);this.scheduleCapabilityRefresh(s)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();"routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),this.pendingGrantHydration&&pe(e,this.pendingGrantHydration.sequenceId)&&this.scheduleGrantHydrationRetry(),"routed"===e.journeyStage&&"granted"===e.sequence.phase&&"awaiting_assignment"===e.sequence.selection.status&&this.pendingGrantHydration?.sequenceId!==e.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:e.sequenceId,token:e.sequence.grant.token,expiresAt:Z(e),attempt:0},this.scheduleGrantHydrationRetry());const t=V(e);if("granted"===t?.phase&&(t.pendingAction||"internal"===t.settlement.processor&&"at_end"===t.settlement.mode&&"in_progress"===t.settlement.state))this.clearGrantExpiryTimer();else if("granted"===t?.phase&&!this.grantExpiryTimer){const t=Z(e);if(t){const e=t-Date.now();e<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),e)}}}async refreshCapabilityFromTimer(){this.capabilityRefreshTimer=void 0;try{if("routed"!==this.stateAtom.get().journeyStage)return void this.clearCapabilityRefreshTimer();if(this.checkoutStarted)return;const e=Date.now();if(void 0!==this.lastCapabilityRefreshAtMs&&e-this.lastCapabilityRefreshAtMs<re)return void this.scheduleCapabilityRefresh(re);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(re*2**this.consecutiveRefreshFailures,ne);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){this.grantExpiryTimer=void 0;try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!g(e)||e.code!==y.VALIDATION_ERROR||e.message!==ie&&e.message!==se)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=le(e),i=z(e);return t&&i?{experienceId:this.experienceId,sequenceId:i,participationId:t.id,participationType:t.type}:null}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),void this.setSnapshot(_(this.experienceId),{persist:!1});const t=Je(e);if(!t)return;const i="routing"===t.journeyStage?U({base:this.baseFromSnapshot(t),journeyStage:"ready"}):t,s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=le(i);r||(this.displayAtom=null,this.displayParticipationId=void 0),this.setSnapshot(i,{persist:!1}),r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){const t=this.displayAtom;if(t?.get().type===e.type&&this.displayParticipationId===e.id)return t;const i=this.resetDisplayAtom(e.type);return this.displayParticipationId=e.id,i}resetDisplayAtom(e){const t=this.displayAtom;if(!t||t.get().type!==e){const t=o({type:e});return this.displayAtom=t,t}return t.set({type:e}),t}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();if(!e)return this.displayAtom=null,void(this.displayParticipationId=void 0);this.displayParticipationId=void 0,this.resetDisplayAtom(e.participationType)}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i&&!l(i))return i;if(!t||"closed"!==t.lifecycle||!e)return i;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:i??{type:"closed",distributionType:t.type,distributionId:t.id}}withTerminalConsumer(e,t,i){switch(e?.mechanism){case"appointment":return u(e,t);case"auction":return p(e,t,i);case"draw":return I(e,t);case"queue":return v(e,t);case"timed_release":return M(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const ye=c.picklist(["queue","draw","auction","timed_release","appointment"]),fe=ye,be=c.picklist(["queue","draw","auction","timed_release"]),Se=c.picklist(["draw","auction"]),Ie=c.strictObject({id:c.string(),ts:c.number(),kind:c.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:c.picklist(["info","success","warning","error"]),audience:c.picklist(["user","system","analytics"]),message:c.string(),detail:c.optional(c.record(c.string(),c.unknown()))}),Ae=c.object({id:c.string(),name:c.string(),experienceId:c.string(),priority:c.number(),color:c.nullable(c.string()),availability:c.optional(s)}),ve=c.strictObject({type:ye,id:c.string(),sequenceId:c.optional(c.string()),lifecycle:c.string(),startsAt:c.optional(c.string()),opensAt:c.optional(c.string()),closesAt:c.optional(c.string()),settlesAt:c.optional(c.string()),result:c.optional(c.string()),details:c.optional(c.unknown()),updatedAt:c.optional(c.number())}),qe=c.strictObject({status:c.string(),selection:c.optional(n),id:c.optional(c.string()),distributionId:c.optional(c.string()),waitlistId:c.optional(c.string()),enteredAt:c.optional(c.string()),admissionGrant:c.optional(c.string()),grantedAt:c.optional(c.string()),deniedReason:c.optional(c.string()),position:c.optional(c.number()),highestBid:c.optional(c.string()),currentBid:c.optional(c.string()),slotId:c.optional(c.string()),locationId:c.optional(c.string()),bookedAt:c.optional(c.string()),checkedInAt:c.optional(c.string()),completedAt:c.optional(c.string()),cancelledAt:c.optional(c.string()),noShowAt:c.optional(c.string())}),we=c.strictObject({token:c.string(),distributionType:be,distributionId:c.string(),sequenceId:c.string(),experienceId:c.string(),issuedAt:c.optional(c.string()),expiresAt:c.optional(c.string())}),Ce=c.strictObject({orderId:c.string(),amount:c.string(),currencyCode:c.string(),paymentMethodLast4:c.optional(c.string()),paymentMethodBrand:c.optional(c.string())}),ke=c.strictObject({publishableKey:c.string(),stripeAccount:c.optional(c.string())}),je=c.strictObject({type:c.string(),reason:c.optional(c.string()),at:c.optional(c.string()),distributionType:c.optional(ye),distributionId:c.optional(c.string())}),xe=c.strictObject({phase:c.literal("unavailable"),reason:c.optional(c.string())}),Te=c.strictObject({waitlistId:c.string(),status:c.picklist(["not_waitlisted","waitlisted"]),entryId:c.optional(c.string())}),Ee=c.strictObject({phase:c.literal("scheduled"),mechanism:fe,distribution:ve}),De=c.strictObject({phase:c.literal("enterable"),mechanism:fe,distribution:ve,consumer:qe,paymentProvider:c.optional(ke)}),Re=c.strictObject({phase:c.literal("participating"),mechanism:fe,distribution:ve,consumer:qe}),Oe=c.strictObject({phase:c.literal("settling"),mechanism:Se,distribution:ve,consumer:qe}),Me=c.variant("processor",[c.strictObject({processor:c.literal("external")}),c.strictObject({processor:c.literal("internal"),mode:c.literal("at_end"),state:c.picklist(["awaiting","in_progress"]),paymentProvider:c.optional(ke)}),c.strictObject({processor:c.literal("internal"),mode:c.literal("pre_auth")})]),Ge=c.strictObject({phase:c.literal("granted"),mechanism:be,distribution:ve,consumer:qe,grant:we,settlement:Me,selection:r}),Fe=c.strictObject({phase:c.literal("ended"),mechanism:fe,distribution:ve,consumer:qe,outcome:je,checkout:c.optional(Ce)}),_e=c.union([xe,Ee,De,Re,Oe,Ge,Fe]),Pe={schemaVersion:c.literal(6),revision:c.number(),updatedAt:c.number(),experienceId:c.string(),accessCode:c.optional(c.string()),experience:c.optional(c.unknown()),events:c.array(Ie),lastSeenEventId:c.optional(c.string()),lastSyncedAt:c.optional(c.number())},He=c.strictObject({...Pe,journeyStage:c.literal("ready")}),Ne=c.strictObject({...Pe,journeyStage:c.literal("routing")}),Ue=c.variant("type",[c.object({...c.omit(e,["reason"]).entries,requirement:c.optional(c.picklist(["session","authenticated"]),"authenticated")}),c.omit(t,["reason"]),c.omit(i,["reason"])]),ze=c.strictObject({...Pe,journeyStage:c.literal("gated"),gates:c.array(Ue),selected:c.optional(c.strictObject({sequence:c.unknown(),status:c.literal("gated")}))}),Be=c.strictObject({...Pe,journeyStage:c.literal("routed"),sequenceId:c.string(),offers:c.array(c.object({sequence:c.unknown(),status:c.literal("gated"),gates:c.array(Ue)})),sequence:_e,waitlist:c.optional(Te),sequenceRef:c.optional(Ae)}),Le=c.union([He,Ne,ze,Be]);function Je(e){const t=c.safeParse(Le,e);if(!t.success)return null;const{schemaVersion:i,...s}=t.output;if("routed"===s.journeyStage)try{ee(s.sequence)}catch{return null}return s}function We(e){if("participating"!==e.phase&&"settling"!==e.phase)return e;if(!("selection"in e))return e;const{selection:t,...i}=e;return void 0===t?i:{...i,consumer:{...i.consumer,selection:t}}}function Ve(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?Q(e):e.sequence.checkout}function Qe(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function $e(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{ge as ExperienceJourney,he as isSelfContradictingDistribution};
@@ -1,6 +1,6 @@
1
1
  import { AdmissionGrant, AppointmentConsumerStatus, AuctionConsumerStatus, DistributionLifecycle, DistributionType, DrawConsumerStatus, JourneyStage, QueueConsumerStatus, SequenceOutcome, SequenceOutcomeType, SequencePhase, TimedReleaseConsumerStatus, WaitlistConsumerStatus } from '@fanfare-io/fanfare-sdk-contracts/consumer-me';
2
2
  import { ReadableAtom } from 'nanostores';
3
- import { AppointmentEnded, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentScheduled, AppointmentScheduledView, AppointmentSequence, AppointmentSequenceConsumerState, AppointmentSequenceView } from '../appointments/appointment.sequence';
3
+ import { AppointmentEnded, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentSequence, AppointmentSequenceConsumerState, AppointmentSequenceView } from '../appointments/appointment.sequence';
4
4
  import { AuctionEnded, AuctionEnterable, AuctionEnterableView, AuctionGranted, AuctionParticipating, AuctionParticipatingView, AuctionScheduled, AuctionScheduledView, AuctionSequence, AuctionSequenceConsumerState, AuctionSequenceView, AuctionSettling } from '../auctions/auction.sequence';
5
5
  import { BotMitigationState, RoutingChallenge } from '../challenges/types';
6
6
  import { DrawEnded, DrawEnterable, DrawEnterableView, DrawGranted, DrawParticipating, DrawParticipatingView, DrawScheduled, DrawScheduledView, DrawSequence, DrawSequenceConsumerState, DrawSequenceView, DrawSettling } from '../draws/draw.sequence';
@@ -8,7 +8,7 @@ import { QueueEnded, QueueEnterable, QueueEnterableView, QueueGranted, QueuePart
8
8
  import { TimedReleaseEnded, TimedReleaseEnterable, TimedReleaseEnterableView, TimedReleaseGranted, TimedReleaseParticipating, TimedReleaseParticipatingView, TimedReleaseScheduled, TimedReleaseScheduledView, TimedReleaseSequence, TimedReleaseSequenceConsumerState, TimedReleaseSequenceView } from '../timed-releases/timed-release.sequence';
9
9
  import { SdkJourneyParticipationType } from '../types/distribution-type';
10
10
  import { DistributionDetails, ExperienceSession, Sequence } from './types';
11
- export type { AppointmentEnded, AppointmentEndedView, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentScheduled, AppointmentScheduledView, AppointmentSequence, AppointmentSequenceConsumerState, AppointmentSequenceView, } from '../appointments/appointment.sequence';
11
+ export type { AppointmentEnded, AppointmentEndedView, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentSequence, AppointmentSequenceConsumerState, AppointmentSequenceView, } from '../appointments/appointment.sequence';
12
12
  export type { AuctionEnded, AuctionEndedView, AuctionEnterable, AuctionEnterableView, AuctionGranted, AuctionGrantedAwaitingAssignmentView, AuctionGrantedSelectionRequiredView, AuctionGrantedView, AuctionParticipating, AuctionParticipatingView, AuctionScheduled, AuctionScheduledView, AuctionSequence, AuctionSequenceConsumerState, AuctionSequenceView, AuctionSettling, AuctionSettlingView, } from '../auctions/auction.sequence';
13
13
  export type { DrawEnded, DrawEndedView, DrawEnterable, DrawEnterableView, DrawGranted, DrawGrantedAwaitingAssignmentView, DrawGrantedSelectionRequiredView, DrawGrantedView, DrawParticipating, DrawParticipatingView, DrawScheduled, DrawScheduledView, DrawSequence, DrawSequenceConsumerState, DrawSequenceView, DrawSettling, DrawSettlingView, } from '../draws/draw.sequence';
14
14
  export type { QueueEnded, QueueEndedView, QueueEnterable, QueueEnterableView, QueueGranted, QueueGrantedAwaitingAssignmentView, QueueGrantedSelectionRequiredView, QueueGrantedView, QueueParticipating, QueueParticipatingView, QueueScheduled, QueueScheduledView, QueueSequence, QueueSequenceConsumerState, QueueSequenceView, } from '../queues/queue.sequence';
@@ -139,7 +139,7 @@ export type UnavailableSequenceState = {
139
139
  /**
140
140
  * Sequence states that exist before a primary distribution opens.
141
141
  */
142
- export type ScheduledSequenceState = QueueScheduled | DrawScheduled | AuctionScheduled | TimedReleaseScheduled | AppointmentScheduled;
142
+ export type ScheduledSequenceState = QueueScheduled | DrawScheduled | AuctionScheduled | TimedReleaseScheduled;
143
143
  /**
144
144
  * Sequence states where the consumer can enter, bid, book, or join a waitlist.
145
145
  */
@@ -527,7 +527,7 @@ type PrimarySequenceView = QueueSequenceView | DrawSequenceView | AuctionSequenc
527
527
  /**
528
528
  * Scheduled sequence views for primary distribution mechanisms.
529
529
  */
530
- export type ScheduledSequenceView = QueueScheduledView | DrawScheduledView | AuctionScheduledView | TimedReleaseScheduledView | AppointmentScheduledView;
530
+ export type ScheduledSequenceView = QueueScheduledView | DrawScheduledView | AuctionScheduledView | TimedReleaseScheduledView;
531
531
  /**
532
532
  * Enterable sequence views. The action exposed by each variant is the only
533
533
  * legal entry action for that phase and mechanism.
@@ -22,5 +22,5 @@ export { createDomBridge } from './dom-bridge';
22
22
  export type { DomBridge, DomBridgeOptions } from './dom-bridge';
23
23
  export { describeJourneyState, selectJourneyDisplay } from './journey-display';
24
24
  export type { JourneyDisplaySelection, JourneyStateDescription } from './journey-display';
25
- export type { AccessCodeSubmitResult, AdmissionGrant, AppointmentConsumerStatus, AppointmentEnded, AppointmentEndedView, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentScheduled, AppointmentScheduledView, AppointmentSequence, AppointmentSequenceView, AuctionConsumerStatus, AuctionEnded, AuctionEndedView, AuctionEnterable, AuctionEnterableView, AuctionGranted, AuctionGrantedAwaitingAssignmentView, AuctionGrantedSelectionRequiredView, AuctionGrantedView, AuctionParticipating, AuctionParticipatingView, AuctionScheduled, AuctionScheduledView, AuctionSequence, AuctionSequenceView, AuctionSettling, AuctionSettlingView, ConsumerJourneyState, ConsumerSequenceState, DistributionLifecycle, DistributionSummary, DistributionType, DrawConsumerStatus, DrawEnded, DrawEndedView, DrawEnterable, DrawEnterableView, DrawGranted, DrawGrantedAwaitingAssignmentView, DrawGrantedSelectionRequiredView, DrawGrantedView, DrawParticipating, DrawParticipatingView, DrawScheduled, DrawScheduledView, DrawSequence, DrawSequenceView, DrawSettling, DrawSettlingView, EnterableSequenceView, GatedSnapshot, GatedView, JourneyEvent, JourneyEventAudience, JourneyEventKind, JourneyEventSeverity, JourneyHandle, JourneySnapshot, JourneyStage, JourneyView, ParticipatingSequenceView, Participation, PrimarySequenceMechanism, QueueConsumerStatus, QueueEnded, QueueEndedView, QueueEnterable, QueueEnterableView, QueueGranted, QueueGrantedAwaitingAssignmentView, QueueGrantedSelectionRequiredView, QueueGrantedView, QueueParticipating, QueueParticipatingView, QueueScheduled, QueueScheduledView, QueueSequence, QueueSequenceView, ReadySnapshot, ReadyView, RoutedSnapshot, RoutedView, RoutingGate, RoutingOffer, RoutingSnapshot, RoutingView, SequenceMechanism, SequenceOutcome, SequenceOutcomeType, SequencePhase, SequenceState, SequenceView, ServiceJourneyStage, ServiceJourneyState, TimedReleaseConsumerStatus, TimedReleaseEnded, TimedReleaseEndedView, TimedReleaseEnterable, TimedReleaseEnterableView, TimedReleaseGranted, TimedReleaseGrantedAwaitingAssignmentView, TimedReleaseGrantedView, TimedReleaseParticipating, TimedReleaseParticipatingView, TimedReleaseScheduled, TimedReleaseScheduledView, TimedReleaseSequence, TimedReleaseSequenceView, WaitlistAttachment, WaitlistAttachmentView, WaitlistConsumerStatus, } from './journey.types';
25
+ export type { AccessCodeSubmitResult, AdmissionGrant, AppointmentConsumerStatus, AppointmentEnded, AppointmentEndedView, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentSequence, AppointmentSequenceView, AuctionConsumerStatus, AuctionEnded, AuctionEndedView, AuctionEnterable, AuctionEnterableView, AuctionGranted, AuctionGrantedAwaitingAssignmentView, AuctionGrantedSelectionRequiredView, AuctionGrantedView, AuctionParticipating, AuctionParticipatingView, AuctionScheduled, AuctionScheduledView, AuctionSequence, AuctionSequenceView, AuctionSettling, AuctionSettlingView, ConsumerJourneyState, ConsumerSequenceState, DistributionLifecycle, DistributionSummary, DistributionType, DrawConsumerStatus, DrawEnded, DrawEndedView, DrawEnterable, DrawEnterableView, DrawGranted, DrawGrantedAwaitingAssignmentView, DrawGrantedSelectionRequiredView, DrawGrantedView, DrawParticipating, DrawParticipatingView, DrawScheduled, DrawScheduledView, DrawSequence, DrawSequenceView, DrawSettling, DrawSettlingView, EnterableSequenceView, GatedSnapshot, GatedView, JourneyEvent, JourneyEventAudience, JourneyEventKind, JourneyEventSeverity, JourneyHandle, JourneySnapshot, JourneyStage, JourneyView, ParticipatingSequenceView, Participation, PrimarySequenceMechanism, QueueConsumerStatus, QueueEnded, QueueEndedView, QueueEnterable, QueueEnterableView, QueueGranted, QueueGrantedAwaitingAssignmentView, QueueGrantedSelectionRequiredView, QueueGrantedView, QueueParticipating, QueueParticipatingView, QueueScheduled, QueueScheduledView, QueueSequence, QueueSequenceView, ReadySnapshot, ReadyView, RoutedSnapshot, RoutedView, RoutingGate, RoutingOffer, RoutingSnapshot, RoutingView, SequenceMechanism, SequenceOutcome, SequenceOutcomeType, SequencePhase, SequenceState, SequenceView, ServiceJourneyStage, ServiceJourneyState, TimedReleaseConsumerStatus, TimedReleaseEnded, TimedReleaseEndedView, TimedReleaseEnterable, TimedReleaseEnterableView, TimedReleaseGranted, TimedReleaseGrantedAwaitingAssignmentView, TimedReleaseGrantedView, TimedReleaseParticipating, TimedReleaseParticipatingView, TimedReleaseScheduled, TimedReleaseScheduledView, TimedReleaseSequence, TimedReleaseSequenceView, WaitlistAttachment, WaitlistAttachmentView, WaitlistConsumerStatus, } from './journey.types';
26
26
  export type { ConsumerMe, Distribution, DistributionContext, Experience, ExperienceDetails, ExperienceSession, JourneyDistributionType, JourneyParticipationType, Sequence, } from './types';
@@ -1 +1 @@
1
- import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{createPollScheduler as s}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as o}from"../payment/checkout-contract.js";import{getEventBus as n}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class a{constructor(t){this.logger=e(),this.store=r(),this.events=n(),this.trackedParticipations=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.monitorRuntime=new i,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastQueueStatus=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}async get(t){try{return await this.http.get(`/queues/${t}`)}catch(e){throw this.logger.error("Failed to get queue",{queueId:t,error:e}),e}}async enter(e,s,i,n,r){try{const a=this.lifecycleGeneration;if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const u=this.bumpOperationGeneration(e);this.logger.info("Entering queue",{queueId:e});const p={metadata:s};n&&(p.paymentInput=n);const l=await this.http.post(`/queues/${e}/enter`,p,i);if(o(l))return l;const c=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:s});return"QUEUED"!==l.status&&"GRANTED"!==l.status&&"COMPLETED"!==l.status||c.events.push({type:"experience:distribution-entered",payload:{distributionType:"queue",distributionId:e,...r}}),this.canApplyAsyncResult(e,a,void 0,u)?(this.applyTransition(e,c),l):l}catch(a){throw this.logger.error("Failed to enter queue",{queueId:e,error:a}),this.events.emit("queue:error",{queueId:e,error:a}),a}}async leave(t,e){try{const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t);if(this.logger.info("Leaving queue",{queueId:t}),await this.http.post(`/queues/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,s,void 0,i))return;this.trackedParticipations.delete(t),this.stopStatusPolling(t),this.events.emit("queue:left",{queueId:t,reason:"user"})}catch(s){throw this.logger.error("Failed to leave queue",{queueId:t,error:s}),this.events.emit("queue:error",{queueId:t,error:s}),s}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{queueId:t}),e;const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t),o=this._doStatus(t,s,void 0,i).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,o),o}async _doStatus(t,e,s,i){try{const o=await this.http.get(`/queues/${t}/status`);if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}catch(o){if(o&&"object"==typeof o&&"status"in o&&404===o.status){const o={status:"NOT_QUEUED"};if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}if(!this.canApplyAsyncResult(t,e,s,i))return{status:"NOT_QUEUED"};throw this.logger.error("Failed to get queue status",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}startMonitoring(t,e,s,i){this.monitorRuntime.start(t,e,s),this.startStatusPolling(t,1e4,i)}stopMonitoring(t){this.stopStatusPolling(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.isStatusPolling(t)||this.monitorRuntime.has(t)}startStatusPolling(t,e=1e4,i){this.stopStatusPolling(t);const o=this.lifecycleGeneration,n=this.bumpMonitorGeneration(t);this.hasSuccessfulReadThisSession.set(t,!1),this.lastQueueStatus.delete(t),this.logger.debug("Starting queue polling",{queueId:t,intervalMs:e});const r=s({baseMs:e,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:i,shouldContinue:()=>this.pollSchedulers.has(t)&&this.canApplyAsyncResult(t,o,n),run:async()=>{const e=this.getOperationGeneration(t);try{const s=await this._doStatus(t,o,n,e);this.canApplyAsyncResult(t,o,n,e)&&(this.hasSuccessfulReadThisSession.set(t,!0),this.lastQueueStatus.set(t,s))}catch(s){if(!this.canApplyAsyncResult(t,o,n,e))return;throw this.logger.error("Polling error",{queueId:t,error:s}),s}}});this.pollSchedulers.set(t,r),r.start()}stopStatusPolling(t){this.bumpMonitorGeneration(t);const e=this.pollSchedulers.get(t);e&&(e.stop(),this.pollSchedulers.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastQueueStatus.delete(t),this.logger.debug("Stopped queue polling",{queueId:t}))}isStatusPolling(t){return this.pollSchedulers.has(t)}shouldSuppressBackoff(t){if(!this.hasSuccessfulReadThisSession.get(t))return!0;const e=this.lastQueueStatus.get(t);return"QUEUED"===e?.status&&e.position<=5||!("GRANTED"!==e?.status||!("expiresAt"in e)||!e.expiresAt)&&Date.now()<new Date(e.expiresAt).getTime()}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,s,i){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===s||this.monitorGenerations.get(t)===s)&&(void 0===i||this.getOperationGeneration(t)===i))}buildTransition(t,e,{source:s,previousParticipation:i,enteredAt:o,metadata:n}){switch(e.status){case"QUEUED":return{trackedParticipation:{queueId:t,enteredAt:i?.enteredAt??o??/* @__PURE__ */(new Date).toISOString(),position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,metadata:i?.metadata??n},displayState:{position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,admissionsPaused:e.admissionsPaused},events:"enter"===s?[{type:"queue:entered",payload:{queueId:t,position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds}}]:void 0!==i?.position&&i.position!==e.position?[{type:"queue:position-changed",payload:{queueId:t,position:e.position,previousPosition:i.position}}]:[],monitorUpdate:null,stopMonitoring:!1};case"GRANTED":return{trackedParticipation:null,events:[{type:"queue:granted",payload:{queueId:t,grant:e.admissionGrant}}],monitorUpdate:"status"===s?{type:"granted",token:e.admissionGrant,expiresAt:"expiresAt"in e&&e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===s};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===s};case"EXPIRED":return{trackedParticipation:null,events:[{type:"queue:expired",payload:{queueId:t}}],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"expired"}}:null,stopMonitoring:"status"===s};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===s};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===s};case"NOT_QUEUED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"closed",reason:"not_queued"}}:null,stopMonitoring:"status"===s}}}applyTransition(t,e){if(e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t),e.displayState){const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"queue",...e.displayState})}for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopStatusPolling(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1;for(const t of this.pollSchedulers.values())t.stop();this.pollSchedulers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastQueueStatus.clear(),this.monitorRuntime.clear()}}export{a as QueueManagementModule};
1
+ import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{createPollScheduler as s}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as o}from"../payment/checkout-contract.js";import{getEventBus as n}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class a{constructor(t){this.logger=e(),this.store=r(),this.events=n(),this.trackedParticipations=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.monitorRuntime=new i,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastQueueStatus=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}async get(t){try{return await this.http.get(`/queues/${t}`)}catch(e){throw this.logger.error("Failed to get queue",{queueId:t,error:e}),e}}async enter(e,s,i,n,r){const a=this.lifecycleGeneration;let u;try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");u=this.bumpOperationGeneration(e),this.logger.info("Entering queue",{queueId:e});const p={metadata:s};n&&(p.paymentInput=n);const l=await this.http.post(`/queues/${e}/enter`,p,i);if(o(l))return l;const c=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:s});return"QUEUED"!==l.status&&"GRANTED"!==l.status&&"COMPLETED"!==l.status||c.events.push({type:"experience:distribution-entered",payload:{distributionType:"queue",distributionId:e,...r}}),this.canApplyAsyncResult(e,a,void 0,u)?(this.applyTransition(e,c),l):l}catch(p){if(!this.canApplyAsyncResult(e,a,void 0,u))throw p;throw this.logger.error("Failed to enter queue",{queueId:e,error:p}),this.events.emit("queue:error",{queueId:e,error:p}),p}}async leave(t,e){const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t);try{if(this.logger.info("Leaving queue",{queueId:t}),await this.http.post(`/queues/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,s,void 0,i))return;this.trackedParticipations.delete(t),this.stopStatusPolling(t),this.events.emit("queue:left",{queueId:t,reason:"user"})}catch(o){if(!this.canApplyAsyncResult(t,s,void 0,i))throw o;throw this.logger.error("Failed to leave queue",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{queueId:t}),e;const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t),o=this._doStatus(t,s,void 0,i).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,o),o}async _doStatus(t,e,s,i){try{const o=await this.http.get(`/queues/${t}/status`);if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}catch(o){if(o&&"object"==typeof o&&"status"in o&&404===o.status){const o={status:"NOT_QUEUED"};if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}if(!this.canApplyAsyncResult(t,e,s,i))return{status:"NOT_QUEUED"};throw this.logger.error("Failed to get queue status",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}startMonitoring(t,e,s,i){this.monitorRuntime.start(t,e,s),this.startStatusPolling(t,1e4,i)}stopMonitoring(t){this.stopStatusPolling(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.isStatusPolling(t)||this.monitorRuntime.has(t)}startStatusPolling(t,e=1e4,i){this.stopStatusPolling(t);const o=this.lifecycleGeneration,n=this.bumpMonitorGeneration(t);this.hasSuccessfulReadThisSession.set(t,!1),this.lastQueueStatus.delete(t),this.logger.debug("Starting queue polling",{queueId:t,intervalMs:e});const r=s({baseMs:e,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:i,shouldContinue:()=>this.pollSchedulers.has(t)&&this.canApplyAsyncResult(t,o,n),run:async()=>{const e=this.getOperationGeneration(t);try{const s=await this._doStatus(t,o,n,e);this.canApplyAsyncResult(t,o,n,e)&&(this.hasSuccessfulReadThisSession.set(t,!0),this.lastQueueStatus.set(t,s))}catch(s){if(!this.canApplyAsyncResult(t,o,n,e))return;throw this.logger.error("Polling error",{queueId:t,error:s}),s}}});this.pollSchedulers.set(t,r),r.start()}stopStatusPolling(t){this.bumpMonitorGeneration(t);const e=this.pollSchedulers.get(t);e&&(e.stop(),this.pollSchedulers.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastQueueStatus.delete(t),this.logger.debug("Stopped queue polling",{queueId:t}))}isStatusPolling(t){return this.pollSchedulers.has(t)}shouldSuppressBackoff(t){if(!this.hasSuccessfulReadThisSession.get(t))return!0;const e=this.lastQueueStatus.get(t);return"QUEUED"===e?.status&&e.position<=5||!("GRANTED"!==e?.status||!("expiresAt"in e)||!e.expiresAt)&&Date.now()<new Date(e.expiresAt).getTime()}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,s,i){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===s||this.monitorGenerations.get(t)===s)&&(void 0===i||this.getOperationGeneration(t)===i))}buildTransition(t,e,{source:s,previousParticipation:i,enteredAt:o,metadata:n}){switch(e.status){case"QUEUED":return{trackedParticipation:{queueId:t,enteredAt:i?.enteredAt??o??/* @__PURE__ */(new Date).toISOString(),position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,metadata:i?.metadata??n},displayState:{position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,admissionsPaused:e.admissionsPaused},events:"enter"===s?[{type:"queue:entered",payload:{queueId:t,position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds}}]:void 0!==i?.position&&i.position!==e.position?[{type:"queue:position-changed",payload:{queueId:t,position:e.position,previousPosition:i.position}}]:[],monitorUpdate:null,stopMonitoring:!1};case"GRANTED":return{trackedParticipation:null,events:[{type:"queue:granted",payload:{queueId:t,grant:e.admissionGrant}}],monitorUpdate:"status"===s?{type:"granted",token:e.admissionGrant,expiresAt:"expiresAt"in e&&e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===s};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===s};case"EXPIRED":return{trackedParticipation:null,events:[{type:"queue:expired",payload:{queueId:t}}],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"expired"}}:null,stopMonitoring:"status"===s};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===s};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===s};case"NOT_QUEUED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"closed",reason:"not_queued"}}:null,stopMonitoring:"status"===s}}}applyTransition(t,e){if(e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t),e.displayState){const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"queue",...e.displayState})}for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopStatusPolling(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1;for(const t of this.pollSchedulers.values())t.stop();this.pollSchedulers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastQueueStatus.clear(),this.monitorRuntime.clear()}}export{a as QueueManagementModule};
@@ -150,14 +150,14 @@ export interface WaitlistEvents {
150
150
  export interface TimedReleaseEvents {
151
151
  "timed_release:entered": {
152
152
  timedReleaseId: string;
153
- enteredAt: string;
153
+ enteredAt?: string;
154
154
  };
155
155
  "timed_release:left": {
156
156
  timedReleaseId: string;
157
157
  };
158
158
  "timed_release:completed": {
159
159
  timedReleaseId: string;
160
- completedAt: string;
160
+ completedAt?: string;
161
161
  };
162
162
  "timed_release:error": {
163
163
  timedReleaseId: string;
@@ -1 +1 @@
1
- import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as s}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";function n(t){return"ENTERED"!==t.status&&"COMPLETED"!==t.status||void 0===t.selectedProductId||void 0===t.selectedVariantId?t:{...t,selection:{productId:t.selectedProductId,variantId:t.selectedVariantId}}}class o{constructor(t){this.logger=e(),this.events=s(),this.monitorRuntime=new i,this.trackedParticipations=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return r()}async get(t){try{return await this.http.get(`/timed-releases/${t}`)}catch(e){throw this.logger.error("Failed to get timed release",{timedReleaseId:t,error:e}),e}}async enter(e,i,s,r){const o=this.lifecycleGeneration,a=this.monitorGenerations.get(e);if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const d=this.bumpOperationGeneration(e);try{this.logger.info("Entering timed release",{timedReleaseId:e});const t=n(await this.http.post(`/timed-releases/${e}/enter`,{selection:s},i)),l=this.buildTransition(e,t,{source:"enter"});return"ENTERED"!==t.status&&"COMPLETED"!==t.status||l.events.push({type:"experience:distribution-entered",payload:{distributionType:"timed_release",distributionId:e,...r}}),this.canApplyAsyncResult(e,o,a,d)?(this.applyTransition(e,l),t):t}catch(l){if(!this.canApplyAsyncResult(e,o,a,d))throw l;throw this.logger.error("Failed to enter timed release",{timedReleaseId:e,error:l}),this.events.emit("timed_release:error",{timedReleaseId:e,error:l}),l}}async leave(t,e){const i=this.lifecycleGeneration,s=this.monitorGenerations.get(t),r=this.bumpOperationGeneration(t);try{this.logger.info("Leaving timed release",{timedReleaseId:t}),await this.http.post(`/timed-releases/${t}/leave`,{},e);const n=this.buildTransition(t,{status:"LEFT",leftAt:/* @__PURE__ */(new Date).toISOString()},{source:"leave"});if(!this.canApplyAsyncResult(t,i,s,r))return;this.applyTransition(t,n)}catch(n){if(!this.canApplyAsyncResult(t,i,s,r))throw n;throw this.logger.error("Failed to leave timed release",{timedReleaseId:t,error:n}),this.events.emit("timed_release:error",{timedReleaseId:t,error:n}),n}}complete(t){return this.trackedParticipations.get(t)?.admissionGrant}async status(t){const e=this.lifecycleGeneration,i=this.monitorGenerations.get(t),s=this.bumpOperationGeneration(t);try{const r=n(await this.http.get(`/timed-releases/${t}/status`)),o=this.buildTransition(t,r,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,o),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:r.status}),r):r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"},n=this.buildTransition(t,r,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,n),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:r.status}),r):r}if(!this.canApplyAsyncResult(t,e,i,s))return{status:"NOT_ENTERED"};throw this.logger.error("Failed to get timed release status",{timedReleaseId:t,error:r}),this.events.emit("timed_release:error",{timedReleaseId:t,error:r}),r}}startMonitoring(t,e,i){this.bumpMonitorGeneration(t),this.monitorRuntime.start(t,e,i);const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"timed_release",enteredAt:this.trackedParticipations.get(t)?.enteredAt})}stopMonitoring(t){this.bumpMonitorGeneration(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}buildTransition(t,e,{source:i}){switch(e.status){case"ENTERED":return{trackedParticipation:{timedReleaseId:t,enteredAt:e.enteredAt,selectedVariantId:e.selectedVariantId,admissionGrant:e.admissionGrant,expiresAt:e.expiresAt},events:"enter"===i?[{type:"timed_release:entered",payload:{timedReleaseId:t,enteredAt:e.enteredAt}}]:[],monitorUpdate:e.admissionGrant?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:!1};case"COMPLETED":return{trackedParticipation:null,events:[{type:"timed_release:completed",payload:{timedReleaseId:t,completedAt:e.completedAt}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i};case"LEFT":return{trackedParticipation:null,events:[{type:"timed_release:left",payload:{timedReleaseId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"leave"===i||"status"===i};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"enter"===i||"status"===i};case"NOT_ENTERED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"enter"===i||"status"===i}}}applyTransition(t,e){e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);const i=this.monitorRuntime.getDisplayAtom(t);i&&(e.trackedParticipation?i.set({type:"timed_release",enteredAt:e.trackedParticipation.enteredAt}):i.set({type:"timed_release"}));for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopMonitoring(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.monitorRuntime.clear()}}export{o as TimedReleaseManagementModule};
1
+ import{TimedReleaseConsumerStateResponseSchema as t}from"@fanfare-io/fanfare-sdk-contracts/timed-release";import{createError as e}from"../core/errors.js";import{getLogger as i}from"../core/logger.js";import{parseResponse as s}from"../core/parse-response.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as n}from"../state/events.js";import{getSDKStore as o}from"../state/store.js";function a(t){return"ENTERED"!==t.status&&"COMPLETED"!==t.status||void 0===t.selectedProductId||void 0===t.selectedVariantId?t:{...t,selection:{productId:t.selectedProductId,variantId:t.selectedVariantId}}}function d(e,i){const r=s(t,e,{endpoint:i},{throwOnFailure:!0});if(!r.ok)throw r.error;return r.value}class l{constructor(t){this.logger=i(),this.events=n(),this.monitorRuntime=new r,this.trackedParticipations=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return o()}async get(t){try{return await this.http.get(`/timed-releases/${t}`)}catch(e){throw this.logger.error("Failed to get timed release",{timedReleaseId:t,error:e}),e}}async enter(t,i,s,r){const n=this.lifecycleGeneration,o=this.monitorGenerations.get(t);if(!this.store.session)throw e.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const l=this.bumpOperationGeneration(t);try{this.logger.info("Entering timed release",{timedReleaseId:t});const e=`/timed-releases/${t}/enter`,p=a(d(await this.http.post(e,{selection:s},i),e)),c=this.buildTransition(t,p,{source:"enter"});return"ENTERED"!==p.status&&"COMPLETED"!==p.status||c.events.push({type:"experience:distribution-entered",payload:{distributionType:"timed_release",distributionId:t,...r}}),this.canApplyAsyncResult(t,n,o,l)?(this.applyTransition(t,c),p):p}catch(p){if(!this.canApplyAsyncResult(t,n,o,l))throw p;throw this.logger.error("Failed to enter timed release",{timedReleaseId:t,error:p}),this.events.emit("timed_release:error",{timedReleaseId:t,error:p}),p}}async leave(t,e){const i=this.lifecycleGeneration,s=this.monitorGenerations.get(t),r=this.bumpOperationGeneration(t);try{this.logger.info("Leaving timed release",{timedReleaseId:t}),await this.http.post(`/timed-releases/${t}/leave`,{},e);const n=this.buildTransition(t,{status:"LEFT",leftAt:/* @__PURE__ */(new Date).toISOString()},{source:"leave"});if(!this.canApplyAsyncResult(t,i,s,r))return;this.applyTransition(t,n)}catch(n){if(!this.canApplyAsyncResult(t,i,s,r))throw n;throw this.logger.error("Failed to leave timed release",{timedReleaseId:t,error:n}),this.events.emit("timed_release:error",{timedReleaseId:t,error:n}),n}}complete(t){return this.trackedParticipations.get(t)?.admissionGrant}async status(t){const e=this.lifecycleGeneration,i=this.monitorGenerations.get(t),s=this.bumpOperationGeneration(t);try{const r=`/timed-releases/${t}/status`,n=a(d(await this.http.get(r),r)),o=this.buildTransition(t,n,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,o),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:n.status}),n):n}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"},n=this.buildTransition(t,r,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,n),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:r.status}),r):r}if(!this.canApplyAsyncResult(t,e,i,s))return{status:"NOT_ENTERED"};throw this.logger.error("Failed to get timed release status",{timedReleaseId:t,error:r}),this.events.emit("timed_release:error",{timedReleaseId:t,error:r}),r}}startMonitoring(t,e,i){this.bumpMonitorGeneration(t),this.monitorRuntime.start(t,e,i);const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"timed_release",enteredAt:this.trackedParticipations.get(t)?.enteredAt})}stopMonitoring(t){this.bumpMonitorGeneration(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}buildTransition(t,e,{source:i}){switch(e.status){case"ENTERED":return{trackedParticipation:{timedReleaseId:t,enteredAt:e.enteredAt,selectedVariantId:e.selectedVariantId,admissionGrant:e.admissionGrant,expiresAt:e.expiresAt},events:"enter"===i?[{type:"timed_release:entered",payload:{timedReleaseId:t,enteredAt:e.enteredAt}}]:[],monitorUpdate:e.admissionGrant?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:!1};case"COMPLETED":return{trackedParticipation:null,events:[{type:"timed_release:completed",payload:{timedReleaseId:t,completedAt:e.completedAt}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i};case"LEFT":return{trackedParticipation:null,events:[{type:"timed_release:left",payload:{timedReleaseId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"leave"===i||"status"===i};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"enter"===i||"status"===i};case"NOT_ENTERED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"enter"===i||"status"===i}}}applyTransition(t,e){e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);const i=this.monitorRuntime.getDisplayAtom(t);i&&(e.trackedParticipation?i.set({type:"timed_release",enteredAt:e.trackedParticipation.enteredAt}):i.set({type:"timed_release"}));for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopMonitoring(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.monitorRuntime.clear()}}export{l as TimedReleaseManagementModule};
@@ -20,7 +20,8 @@ export type TimedReleaseDenyReason = (typeof TimedReleaseDenyReason)[keyof typeo
20
20
  */
21
21
  export interface TimedReleaseParticipation {
22
22
  timedReleaseId: string;
23
- enteredAt: string;
23
+ /** Absent when the standing was rendered from the register rather than accepted on this call. */
24
+ enteredAt?: string;
24
25
  selectedVariantId?: string;
25
26
  /** Admission credential held from entry; refreshed on every status re-mint. */
26
27
  admissionGrant?: string;
@@ -35,7 +36,11 @@ export interface TimedReleaseNotEnteredState {
35
36
  }
36
37
  export interface TimedReleaseEnteredState {
37
38
  status: "ENTERED";
38
- enteredAt: string;
39
+ /**
40
+ * The instant the register accepted the entry. A re-entry against a live admission renders the
41
+ * resident standing, which carries no fresh entry instant.
42
+ */
43
+ enteredAt?: string;
39
44
  selectedProductId?: string;
40
45
  selectedVariantId?: string;
41
46
  selection?: SelectionChoice;
@@ -49,7 +54,9 @@ export interface TimedReleaseEnteredState {
49
54
  }
50
55
  export interface TimedReleaseCompletedState {
51
56
  status: "COMPLETED";
52
- completedAt: string;
57
+ enteredAt?: string;
58
+ /** Absent when the spend was recorded without a committed completion instant to render. */
59
+ completedAt?: string;
53
60
  selectedProductId?: string;
54
61
  selectedVariantId?: string;
55
62
  selection?: SelectionChoice;
package/dist/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * SDK version
3
3
  * This is automatically updated during the build process
4
4
  */
5
- export declare const version = "0.21.0";
5
+ export declare const version = "0.22.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- const o="0.21.0";export{o as version};
1
+ const o="0.22.0";export{o as version};
@@ -1 +1 @@
1
- import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{generateId as s}from"../core/utils.js";import{getEventBus as r}from"../state/events.js";import{getSDKStore as i}from"../state/store.js";class o{constructor(t){this.logger=e(),this.events=r(),this.http=t}get store(){return i()}async enter(e,r,i){try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");this.logger.info("Entering waitlist",{waitlistId:e});const s=await this.http.post(`/waitlists/${e}/enter`,void 0,r);return this.events.emit("waitlist:entered",{waitlistId:e,sequenceId:s.sequenceId,enteredAt:s.enteredAt,...i}),s}catch(o){if(o&&"object"==typeof o&&"message"in o){const t=o.message.toLowerCase();if(t.includes("already on list")||t.includes("already entered")||t.includes("already joined")){this.logger.info("Waitlist entry already exists",{waitlistId:e});const t=await this.getStatus(e);return{id:s(),waitlistId:e,sequenceId:t.sequenceId??"",consumerId:this.store.session?.consumerId||"unknown",enteredAt:t.enteredAt||/* @__PURE__ */(new Date).toISOString()}}}throw this.logger.error("Failed to enter waitlist",{waitlistId:e,error:o}),this.events.emit("waitlist:error",{waitlistId:e,error:o instanceof Error?o.message:"Unknown error"}),o}}async leave(t,e,s){try{this.logger.info("Leaving waitlist",{waitlistId:t}),await this.http.delete(`/waitlists/${t}/leave`,e),this.events.emit("waitlist:left",{waitlistId:t,...s})}catch(r){throw this.logger.error("Failed to leave waitlist",{waitlistId:t,error:r}),this.events.emit("waitlist:error",{waitlistId:t,error:r instanceof Error?r.message:"Unknown error"}),r}}async getStatus(t){try{return await this.http.get(`/waitlists/${t}/status`)}catch(e){if(e&&"object"==typeof e&&"status"in e&&404===e.status)return{waitlistId:t,isEntered:!1};throw this.logger.error("Failed to get waitlist status",{waitlistId:t,error:e}),this.events.emit("waitlist:error",{waitlistId:t,error:e instanceof Error?e.message:"Unknown error"}),e}}destroy(){this.logger.debug("WaitlistManagementModule destroyed")}}export{o as WaitlistManagementModule};
1
+ import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{generateId as i}from"../core/utils.js";import{getEventBus as s}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class n{constructor(t){this.logger=e(),this.events=s(),this.operationGenerations=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return r()}async enter(e,s,r){const n=this.lifecycleGeneration;let o;try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");o=this.bumpOperationGeneration(e),this.logger.info("Entering waitlist",{waitlistId:e});const i=await this.http.post(`/waitlists/${e}/enter`,void 0,s);return this.canApplyAsyncResult(e,n,o)?(this.events.emit("waitlist:entered",{waitlistId:e,sequenceId:i.sequenceId,enteredAt:i.enteredAt,...r}),i):i}catch(a){if(a&&"object"==typeof a&&"message"in a){const t=a.message.toLowerCase();if(t.includes("already on list")||t.includes("already entered")||t.includes("already joined")){this.logger.info("Waitlist entry already exists",{waitlistId:e});const t=await this.getStatus(e);return{id:i(),waitlistId:e,sequenceId:t.sequenceId??"",consumerId:this.store.session?.consumerId||"unknown",enteredAt:t.enteredAt||/* @__PURE__ */(new Date).toISOString()}}}if(!this.canApplyAsyncResult(e,n,o))throw a;throw this.logger.error("Failed to enter waitlist",{waitlistId:e,error:a}),this.events.emit("waitlist:error",{waitlistId:e,error:a instanceof Error?a.message:"Unknown error"}),a}}async leave(t,e,i){const s=this.lifecycleGeneration,r=this.bumpOperationGeneration(t);try{if(this.logger.info("Leaving waitlist",{waitlistId:t}),await this.http.delete(`/waitlists/${t}/leave`,e),!this.canApplyAsyncResult(t,s,r))return;this.events.emit("waitlist:left",{waitlistId:t,...i})}catch(n){if(!this.canApplyAsyncResult(t,s,r))throw n;throw this.logger.error("Failed to leave waitlist",{waitlistId:t,error:n}),this.events.emit("waitlist:error",{waitlistId:t,error:n instanceof Error?n.message:"Unknown error"}),n}}async getStatus(t){const e=this.lifecycleGeneration,i=this.getOperationGeneration(t);try{return await this.http.get(`/waitlists/${t}/status`)}catch(s){if(s&&"object"==typeof s&&"status"in s&&404===s.status)return{waitlistId:t,isEntered:!1};if(!this.canApplyAsyncResult(t,e,i))throw s;throw this.logger.error("Failed to get waitlist status",{waitlistId:t,error:s}),this.events.emit("waitlist:error",{waitlistId:t,error:s instanceof Error?s.message:"Unknown error"}),s}}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i){return!this.destroyed&&this.lifecycleGeneration===e&&(void 0===i||this.getOperationGeneration(t)===i)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.operationGenerations.clear(),this.logger.debug("WaitlistManagementModule destroyed")}}export{n as WaitlistManagementModule};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanfare-io/fanfare-sdk-core",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Official Fanfare Browser SDK for queue, draw, auction, and appointment experiences",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",
@@ -115,7 +115,7 @@
115
115
  "uuid": "^14.0.0",
116
116
  "wretch": "^2.11.0",
117
117
  "zustand": "^5.0.6",
118
- "@fanfare-io/fanfare-sdk-contracts": "0.21.0"
118
+ "@fanfare-io/fanfare-sdk-contracts": "0.22.0"
119
119
  },
120
120
  "peerDependencies": {
121
121
  "valibot": "^1.1.0"
@@ -154,7 +154,7 @@
154
154
  "vite-plugin-dts": "^4.5.4",
155
155
  "vite-tsconfig-paths": "^5.1.4",
156
156
  "vitest": "^5.0.0",
157
- "@fanfare-io/shared-models": "0.21.0"
157
+ "@fanfare-io/shared-models": "0.22.0"
158
158
  },
159
159
  "sideEffects": false,
160
160
  "keywords": [