@fanfare-io/fanfare-sdk-core 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appointments/appointment.driver.js +1 -1
- package/dist/appointments/appointment.module.js +1 -1
- package/dist/appointments/appointment.sequence.d.ts +5 -4
- package/dist/appointments/types.d.ts +3 -5
- package/dist/core/client.js +1 -1
- package/dist/core/error-display.js +1 -1
- package/dist/core/errors.d.ts +2 -0
- package/dist/experiences/distribution-monitor.types.d.ts +3 -3
- package/dist/experiences/experience.module.js +1 -1
- package/dist/experiences/journey.js +1 -1
- package/dist/experiences/journey.machine.d.ts +2 -0
- package/dist/experiences/journey.machine.js +1 -1
- package/dist/experiences/journey.types.d.ts +2 -1
- package/dist/internals.d.ts +1 -1
- package/dist/internals.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -7
|
@@ -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"
|
|
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 +1 @@
|
|
|
1
|
-
import*as t from"valibot";import{isFanfareError as o,createError as
|
|
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)}),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,status:e}),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 r,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(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,status:e}),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){try{const o=await this.http.post(`/appointments/${t}/book`,{slotId:n,locationId:i??null},s),e=b(l(u,o,`/appointments/${t}/book`));return a().emit("experience:distribution-entered",{distributionType:"appointment",distributionId:t,...r}),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,s){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){try{const o=await this.http.post(`/appointments/${t}/reschedule`,{newSlotId:n,newLocationId:i??null},s),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(r){if(this.logger.error("Failed to reschedule appointment",{appointmentId:t,newSlotId:n,newLocationId:i,error:r}),o(r))throw r;throw e.networkError("Failed to reschedule appointment",{error:r})}}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)}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};
|
|
@@ -16,6 +16,8 @@ export interface AppointmentSequenceConsumerState {
|
|
|
16
16
|
id?: string;
|
|
17
17
|
/** Distribution id this appointment booking belongs to. */
|
|
18
18
|
distributionId?: string;
|
|
19
|
+
/** Who or what ended the booking. */
|
|
20
|
+
cancelledCause?: "consumer" | "admin" | "killed";
|
|
19
21
|
/** Booked slot id from the appointment service. */
|
|
20
22
|
slotId?: string;
|
|
21
23
|
/** Booked location id from the appointment service. */
|
|
@@ -156,16 +158,15 @@ export interface AppointmentParticipatingView {
|
|
|
156
158
|
mechanism: "appointment";
|
|
157
159
|
/** Consumer appointment booking facts. */
|
|
158
160
|
consumer: AppointmentSequenceConsumerState;
|
|
159
|
-
/** Live appointment display facts, including slot, location, and
|
|
161
|
+
/** Live appointment display facts, including slot, location, and booking status. */
|
|
160
162
|
display$: ReadableAtom<AppointmentDisplayState>;
|
|
161
163
|
/**
|
|
162
164
|
* Cancel the booked appointment.
|
|
163
165
|
*
|
|
164
|
-
* Legal only while this view is current. The
|
|
165
|
-
* the server. The promise rejects if the booking cannot be cancelled or the
|
|
166
|
+
* Legal only while this view is current. The promise rejects if the booking cannot be cancelled or the
|
|
166
167
|
* server rejects the cancellation.
|
|
167
168
|
*/
|
|
168
|
-
cancel(
|
|
169
|
+
cancel(): Promise<void>;
|
|
169
170
|
/**
|
|
170
171
|
* Move the booking to another slot and optional location.
|
|
171
172
|
*
|
|
@@ -15,21 +15,19 @@ export interface AppointmentBooking {
|
|
|
15
15
|
bookingId: string;
|
|
16
16
|
appointmentId: string;
|
|
17
17
|
slotId: string;
|
|
18
|
+
cancelledCause?: "consumer" | "admin" | "killed";
|
|
18
19
|
locationId?: string;
|
|
19
20
|
locationName?: string;
|
|
20
|
-
/** Join URL for online appointments. Absent for in-person bookings. */
|
|
21
|
-
joinUrl?: string;
|
|
22
21
|
startTime: string;
|
|
23
22
|
endTime: string;
|
|
24
23
|
confirmationCode?: string;
|
|
25
|
-
consumerStatus: "booked" | "
|
|
24
|
+
consumerStatus: "booked" | "completed" | "cancelled";
|
|
26
25
|
}
|
|
27
26
|
export interface AppointmentModule {
|
|
28
27
|
get(appointmentId: string, options?: RequestOptions): Promise<AppointmentDetails>;
|
|
29
28
|
getSlots(appointmentId: string, range: {
|
|
30
29
|
startDate: string;
|
|
31
30
|
endDate: string;
|
|
32
|
-
timezone?: string;
|
|
33
31
|
}, options?: RequestOptions): Promise<AppointmentSlot[]>;
|
|
34
32
|
/**
|
|
35
33
|
* Own-booking-scoped read. Ungated server-side, so it carries no capability
|
|
@@ -37,7 +35,7 @@ export interface AppointmentModule {
|
|
|
37
35
|
*/
|
|
38
36
|
getMe(appointmentId: string): Promise<AppointmentBooking | null>;
|
|
39
37
|
book(appointmentId: string, slotId: string, locationId?: string, options?: RequestOptions): Promise<AppointmentBooking>;
|
|
40
|
-
cancel(appointmentId: string, slotId: string, locationId?: string,
|
|
38
|
+
cancel(appointmentId: string, slotId: string, locationId?: string, options?: RequestOptions): Promise<void>;
|
|
41
39
|
reschedule(appointmentId: string, newSlotId: string, newLocationId?: string, options?: RequestOptions): Promise<AppointmentBooking>;
|
|
42
40
|
startMonitoring(id: string, context?: Record<string, unknown>, onUpdate?: (update: MonitorUpdate) => void, onDegradedChange?: (degraded: boolean) => void): void;
|
|
43
41
|
stopMonitoring(id: string): void;
|
package/dist/core/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{createBeaconLifecycleAdapter as e}from"../adapters/beacon-lifecycle.js";import{AppointmentManagementModule as t}from"../appointments/appointment.module.js";import{AuctionManagementModule as i}from"../auctions/auction.module.js";import{AuthenticationModule as r}from"../auth/auth.module.js";import{BeaconTrackingModule as n}from"../beacon/beacon.module.js";import{ChallengeManagementModule as o}from"../challenges/challenge.module.js";import{Config as s}from"../config/index.js";import{DrawManagementModule as a}from"../draws/draw.module.js";import{ExperienceManagementModule as d}from"../experiences/experience.module.js";import{ExperienceJourney as u}from"../experiences/journey.js";import{PaymentMethodManagementModule as l}from"../payment/payment.module.js";import{QueueManagementModule as c}from"../queues/queue.module.js";import{resolveCapabilityOptions as g,resolveCapabilityAttribution as y}from"../state/capability-token-registry.js";import{getEventBus as m}from"../state/events.js";import{getSDKStore as p}from"../state/store.js";import{CrossTabCoordinator as f}from"../sync/cross-tab-coordinator.js";import{TimedReleaseManagementModule as b}from"../timed-releases/timed-release.module.js";import{version as w}from"../version.js";import{WaitlistManagementModule as I}from"../waitlists/waitlist.module.js";import{FanfareError as h,ErrorCodes as v}from"./errors.js";import{createHttpClient as M}from"./http.js";import{createLogger as S}from"./logger.js";let j=null;async function k(k){const A=new s(k),x=A.get(),D=S({enabled:x.debug||!1,level:x.debug?"debug":"warn",prefix:"Fanfare"});if(D.info("Initializing Fanfare SDK",{organizationId:k.organizationId,environment:x.environment}),j)throw new h("Fanfare SDK is already initialized. The SDK uses a shared event bus and store; multiple concurrent instances will corrupt state and analytics. Call destroy() on the existing instance before initializing a new one.",v.ALREADY_INITIALIZED);const E=/* @__PURE__ */Symbol("fanfare-sdk"),C=M({baseUrl:A.apiUrl,credentials:x.credentials,headers:{"X-Organization-Id":k.organizationId,"X-Publishable-Key":k.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":w,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},x),q=M({baseUrl:A.beaconUrl,credentials:"omit",headers:{"X-Organization-Id":k.organizationId,"X-Publishable-Key":k.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":w,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},x),R=new r(C,q),z=new o(C),O=new c(C),F=new a(C),K=new i(C),X=new t(C),P=new I(C),T=new b(C),$=new d(C,O,F,K,P,T),J=new l(C),N=m(),V=p();N.on("auth:logout",()=>J.destroy());const B=/* @__PURE__ */new Map,L=/* @__PURE__ */new Map,U=new n(q,x.beacon||{},e=>{const t=L.get(e)?.snapshot$.get();return"routed"===t?.journeyStage?t.sequenceId:void 0}),G=/* @__PURE__ */new Map;let H,_;const Y=e=>{const t=L.get(e);if(t)return D.debug("Returning existing journey handle",{experienceId:e}),t;D.info("Creating new journey",{experienceId:e});const i=new u(e,H);B.set(e,i),_?.registerJourney(i);const r={view$:i.view$,events$:i.events$,latestEvent$:i.latestEvent$,snapshot$:i.state,ackEvent:e=>i.ackEvent(e),ackAllEvents:()=>i.ackAllEvents(),destroy:()=>{i.destroy(),_?.unregisterJourney(e),B.delete(e),L.delete(e)}};return L.set(e,r),r},Z=async e=>{const t=e??await $.getMe(),i=/* @__PURE__ */new Set,r=new Set(t.active.journeys.map(e=>e.experienceId));for(const n of r)Y(n);for(const n of r){const e=B.get(n);e&&await e.resumeFromMe(t)&&i.add(n)}return[...i]},Q=async e=>{const t=/* @__PURE__ */new Set;for(const i of Object.keys(V.activeJourneys))Y(i),t.add(i);if(!e&&!R.getSession())return[...t];for(const i of await Z(e))t.add(i);return[...t]},W={check:()=>R.check(),guest:()=>R.guest(),requestOtp:e=>R.requestOtp(e),verifyOtp:e=>R.verifyOtp(e),exchangeExternal:e=>R.exchangeExternal(e),login:e=>R.login(e),logout:()=>R.logout(),getSession:()=>R.getSession(),refresh:()=>R.refresh()},ee={initiate:e=>z.initiate(e),verify:e=>z.verify(e)},te={get:e=>Y(e),list:()=>Array.from(
|
|
2
|
-
/* @__PURE__ */new Set([...L.keys(),...B.keys(),...Object.keys(V.activeJourneys)])),resumeAll:e=>Q(e)},ie={bind:e=>U.bind(e),track:e=>U.track(e),trackBatch:e=>U.trackBatch(e),flush:()=>U.flush()},re=(e,t)=>{if(G.has(e.id))D.warn("CDP adapter already registered, skipping",{adapterId:e.id});else{try{e.init(N,t)}catch(i){return void D.error("CDP adapter failed to initialize",{adapterId:e.id,error:i})}G.set(e.id,e),D.info("CDP adapter registered",{adapterId:e.id})}},ne=(e,t)=>N.on(e,t),oe=async()=>{D.info("Destroying SDK instance");for(const t of G.values())try{t.destroy()}catch(e){D.error("CDP adapter failed to destroy",{adapterId:t.id,error:e})}G.clear();for(const t of Array.from(L.values()))try{t.destroy()}catch(e){D.error("Journey handle failed to destroy",{error:e})}B.clear(),L.clear(),"destroy"in R&&"function"==typeof R.destroy&&R.destroy(),O.destroy(),F.destroy(),K.destroy(),X.destroy(),P.destroy(),T.destroy(),$.destroy(),U.destroy(),J.destroy(),_?.close(),N.off(),j===E&&(j=null)};H={auth:W,challenges:ee,journeys:te,queues:{get:e=>O.get(e),enter:(e,t,i,r)=>O.enter(e,t,g({distributionId:e},i),r,y({distributionId:e})),leave:(e,t)=>O.leave(e,g({distributionId:e},t)),status:e=>O.status(e),startMonitoring:(e,t,i)=>O.startMonitoring(e,t,i),stopMonitoring:e=>O.stopMonitoring(e),isMonitoring:e=>O.isMonitoring(e)},draws:{get:e=>F.get(e),enter:(e,t,i,r)=>F.enter(e,t,g({distributionId:e},i),r,y({distributionId:e})),leave:(e,t)=>F.leave(e,g({distributionId:e},t)),status:e=>F.status(e),startMonitoring:(e,t,i)=>F.startMonitoring(e,t,i),stopMonitoring:e=>F.stopMonitoring(e),isMonitoring:e=>F.isMonitoring(e)},auctions:{get:e=>K.get(e),bid:(e,t,i)=>K.bid(e,t,g({distributionId:e},i),y({distributionId:e})),enter:(e,t,i)=>K.enter(e,t,g({distributionId:e},i),y({distributionId:e})),leave:(e,t)=>K.leave(e,g({distributionId:e},t)),status:e=>K.status(e),getBidHistory:e=>K.getBidHistory(e),enableAutoRebid:(e,t,i)=>K.enableAutoRebid(e,t,i),disableAutoRebid:e=>K.disableAutoRebid(e),getAutoRebidConfig:e=>K.getAutoRebidConfig(e),startMonitoring:(e,t,i)=>K.startMonitoring(e,t,i),stopMonitoring:e=>K.stopMonitoring(e),isMonitoring:e=>K.isMonitoring(e),destroy:()=>K.destroy()},experiences:{get:e=>$.get(e),enter:e=>$.enter(e),leave:e=>$.leave(e),getMe:()=>$.getMe(),confirmCheckout:(e,t,i)=>$.confirmCheckout(e,t,i),findSequence:(e,t)=>$.findSequence(e,t),validateSequenceAccess:(e,t)=>$.validateSequenceAccess(e,t),selectSequence:e=>$.selectSequence(e),getCurrentDistributions:(e,t)=>$.getCurrentDistributions(e,t),enterDistribution:e=>{if(!e)throw new h("Distribution is required",v.VALIDATION_ERROR);return $.enterDistribution(e)},getActiveSession:()=>$.getActiveSession(),isInExperience:e=>$.isInExperience(e),getSelectedSequence:()=>$.getSelectedSequence(),createJourney:e=>Y(e),resumeJourneysFromMe:e=>Z(e),destroy:()=>$.destroy()},waitlists:{enter:(e,t)=>P.enter(e,g({waitlistId:e},t),y({waitlistId:e})),leave:(e,t)=>P.leave(e,g({waitlistId:e},t),y({waitlistId:e})),getStatus:e=>P.getStatus(e),destroy:()=>P.destroy()},timedReleases:{get:e=>T.get(e),enter:(e,t,i)=>T.enter(e,t,g({distributionId:e},i),y({distributionId:e})),leave:(e,t)=>T.leave(e,g({distributionId:e},t)),complete:e=>T.complete(e),status:e=>T.status(e),startMonitoring:(e,t,i)=>T.startMonitoring(e,t,i),stopMonitoring:e=>T.stopMonitoring(e),isMonitoring:e=>T.isMonitoring(e)},appointments:{get:(e,t)=>X.get(e,g({distributionId:e},t)),getSlots:(e,t,i)=>X.getSlots(e,t,g({distributionId:e},i)),getMe:e=>X.getMe(e),book:(e,t,i,r)=>X.book(e,t,i,g({distributionId:e},r),y({distributionId:e})),cancel:(e,t,i,r
|
|
2
|
+
/* @__PURE__ */new Set([...L.keys(),...B.keys(),...Object.keys(V.activeJourneys)])),resumeAll:e=>Q(e)},ie={bind:e=>U.bind(e),track:e=>U.track(e),trackBatch:e=>U.trackBatch(e),flush:()=>U.flush()},re=(e,t)=>{if(G.has(e.id))D.warn("CDP adapter already registered, skipping",{adapterId:e.id});else{try{e.init(N,t)}catch(i){return void D.error("CDP adapter failed to initialize",{adapterId:e.id,error:i})}G.set(e.id,e),D.info("CDP adapter registered",{adapterId:e.id})}},ne=(e,t)=>N.on(e,t),oe=async()=>{D.info("Destroying SDK instance");for(const t of G.values())try{t.destroy()}catch(e){D.error("CDP adapter failed to destroy",{adapterId:t.id,error:e})}G.clear();for(const t of Array.from(L.values()))try{t.destroy()}catch(e){D.error("Journey handle failed to destroy",{error:e})}B.clear(),L.clear(),"destroy"in R&&"function"==typeof R.destroy&&R.destroy(),O.destroy(),F.destroy(),K.destroy(),X.destroy(),P.destroy(),T.destroy(),$.destroy(),U.destroy(),J.destroy(),_?.close(),N.off(),j===E&&(j=null)};H={auth:W,challenges:ee,journeys:te,queues:{get:e=>O.get(e),enter:(e,t,i,r)=>O.enter(e,t,g({distributionId:e},i),r,y({distributionId:e})),leave:(e,t)=>O.leave(e,g({distributionId:e},t)),status:e=>O.status(e),startMonitoring:(e,t,i)=>O.startMonitoring(e,t,i),stopMonitoring:e=>O.stopMonitoring(e),isMonitoring:e=>O.isMonitoring(e)},draws:{get:e=>F.get(e),enter:(e,t,i,r)=>F.enter(e,t,g({distributionId:e},i),r,y({distributionId:e})),leave:(e,t)=>F.leave(e,g({distributionId:e},t)),status:e=>F.status(e),startMonitoring:(e,t,i)=>F.startMonitoring(e,t,i),stopMonitoring:e=>F.stopMonitoring(e),isMonitoring:e=>F.isMonitoring(e)},auctions:{get:e=>K.get(e),bid:(e,t,i)=>K.bid(e,t,g({distributionId:e},i),y({distributionId:e})),enter:(e,t,i)=>K.enter(e,t,g({distributionId:e},i),y({distributionId:e})),leave:(e,t)=>K.leave(e,g({distributionId:e},t)),status:e=>K.status(e),getBidHistory:e=>K.getBidHistory(e),enableAutoRebid:(e,t,i)=>K.enableAutoRebid(e,t,i),disableAutoRebid:e=>K.disableAutoRebid(e),getAutoRebidConfig:e=>K.getAutoRebidConfig(e),startMonitoring:(e,t,i)=>K.startMonitoring(e,t,i),stopMonitoring:e=>K.stopMonitoring(e),isMonitoring:e=>K.isMonitoring(e),destroy:()=>K.destroy()},experiences:{get:e=>$.get(e),enter:e=>$.enter(e),leave:e=>$.leave(e),getMe:()=>$.getMe(),confirmCheckout:(e,t,i)=>$.confirmCheckout(e,t,i),findSequence:(e,t)=>$.findSequence(e,t),validateSequenceAccess:(e,t)=>$.validateSequenceAccess(e,t),selectSequence:e=>$.selectSequence(e),getCurrentDistributions:(e,t)=>$.getCurrentDistributions(e,t),enterDistribution:e=>{if(!e)throw new h("Distribution is required",v.VALIDATION_ERROR);return $.enterDistribution(e)},getActiveSession:()=>$.getActiveSession(),isInExperience:e=>$.isInExperience(e),getSelectedSequence:()=>$.getSelectedSequence(),createJourney:e=>Y(e),resumeJourneysFromMe:e=>Z(e),destroy:()=>$.destroy()},waitlists:{enter:(e,t)=>P.enter(e,g({waitlistId:e},t),y({waitlistId:e})),leave:(e,t)=>P.leave(e,g({waitlistId:e},t),y({waitlistId:e})),getStatus:e=>P.getStatus(e),destroy:()=>P.destroy()},timedReleases:{get:e=>T.get(e),enter:(e,t,i)=>T.enter(e,t,g({distributionId:e},i),y({distributionId:e})),leave:(e,t)=>T.leave(e,g({distributionId:e},t)),complete:e=>T.complete(e),status:e=>T.status(e),startMonitoring:(e,t,i)=>T.startMonitoring(e,t,i),stopMonitoring:e=>T.stopMonitoring(e),isMonitoring:e=>T.isMonitoring(e)},appointments:{get:(e,t)=>X.get(e,g({distributionId:e},t)),getSlots:(e,t,i)=>X.getSlots(e,t,g({distributionId:e},i)),getMe:e=>X.getMe(e),book:(e,t,i,r)=>X.book(e,t,i,g({distributionId:e},r),y({distributionId:e})),cancel:(e,t,i,r)=>X.cancel(e,t,i,g({distributionId:e},r)),reschedule:(e,t,i,r)=>X.reschedule(e,t,i,g({distributionId:e},r)),startMonitoring:(e,t,i,r)=>X.startMonitoring(e,t,i,r),stopMonitoring:e=>X.stopMonitoring(e),isMonitoring:e=>X.isMonitoring(e),bumpMonitorGeneration:e=>X.bumpMonitorGeneration(e),destroy:()=>X.destroy()},beacon:ie,payment:J,use:re,on:ne,destroy:oe};const se=!1!==k.sync&&"object"==typeof k.sync?k.sync:{};_=new f({enabled:!1!==k.sync&&!1!==se.enabled,channelName:se.channelName}),D.info("Inter-tab sync initialized",{tabId:_.getTabId(),enabled:_.isEnabled()});!x.beacon?.disableLifecycleEvents&&re(e(e=>U.track(e)));const ae={auth:W,challenges:ee,journeys:te,beacon:ie,appointments:H.appointments,payment:J,use:re,on:ne,destroy:oe};if(await R.rehydrate(),x.autoResume&&R.getSession())try{await Q()}catch(de){D.warn("Auto-resume on init failed; journeys can be resumed manually",{error:de instanceof Error?de.message:String(de)})}return j=E,D.info("SDK initialized successfully"),ae}export{k as init};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{classify as r}from"./error-disposition.js";import{FanfareError as e,ErrorCodes as o}from"./errors.js";const t={[o.OTP_INVALID]:"auth.invalidCode",[o.OTP_EXPIRED]:"auth.codeExpired",[o.INVALID_SESSION]:"common.sessionExpired",[o.INVALID_REFRESH_TOKEN]:"common.sessionExpired",[o.SERVICE_UNAVAILABLE]:"error.overloadedGeneric",[o.TIMEOUT]:"error.overloadedGeneric",[o.NETWORK_ERROR]:"error.overloadedGeneric",[o.FEATURE_DISABLED]:"error.featureDisabled",[o.NO_ACCESS]:"widget.noAccess.message",[o.FORBIDDEN]:"error.forbidden",[o.DISTRIBUTION_NOT_OPEN]:"error.notOpen",[o.DISTRIBUTION_DEACTIVATED]:"error.deactivated",[o.ALREADY_BOOKED]:"appointment.error.alreadyBooked",[o.ACTIVE_BOOKING_EXISTS]:"appointment.error.activeBookingExists",[o.SLOT_NOT_AVAILABLE]:"appointment.error.slotNotAvailable",[o.SLOT_EXPIRED]:"appointment.error.slotExpired",[o.SLOT_OUT_OF_WINDOW]:"appointment.error.slotOutOfWindow",[o.CANNOT_CANCEL_ONGOING_OR_PAST]:"appointment.error.cannotCancel",[o.NEW_SLOT_FULL]:"appointment.error.newSlotFull",[o.NO_EXISTING_BOOKING]:"appointment.error.noExistingBooking",[o.INVALID_SLOT]:"appointment.error.invalidSlot"},i=/* @__PURE__ */new Set([o.DISTRIBUTION_DEACTIVATED,o.DISTRIBUTION_CLOSED,o.DISTRIBUTION_ORDER_LIMIT,o.SOLD_OUT,o.CHECKOUT_MISCONFIGURED]);function n(r){return"INLINE"===r||"STEP_RESET"===r?"error.inlineGeneric":"error.description"}function s(e,s){const{disposition:
|
|
1
|
+
import{classify as r}from"./error-disposition.js";import{FanfareError as e,ErrorCodes as o}from"./errors.js";const t={[o.OTP_INVALID]:"auth.invalidCode",[o.OTP_EXPIRED]:"auth.codeExpired",[o.INVALID_SESSION]:"common.sessionExpired",[o.INVALID_REFRESH_TOKEN]:"common.sessionExpired",[o.SERVICE_UNAVAILABLE]:"error.overloadedGeneric",[o.TIMEOUT]:"error.overloadedGeneric",[o.NETWORK_ERROR]:"error.overloadedGeneric",[o.FEATURE_DISABLED]:"error.featureDisabled",[o.NO_ACCESS]:"widget.noAccess.message",[o.FORBIDDEN]:"error.forbidden",[o.DISTRIBUTION_NOT_OPEN]:"error.notOpen",[o.DISTRIBUTION_DEACTIVATED]:"error.deactivated",[o.ALREADY_BOOKED]:"appointment.error.alreadyBooked",[o.ACTIVE_BOOKING_EXISTS]:"appointment.error.activeBookingExists",[o.SLOT_NOT_AVAILABLE]:"appointment.error.slotNotAvailable",[o.SLOT_EXPIRED]:"appointment.error.slotExpired",[o.SLOT_OUT_OF_WINDOW]:"appointment.error.slotOutOfWindow",[o.CANNOT_CANCEL_ONGOING_OR_PAST]:"appointment.error.cannotCancel",[o.NEW_SLOT_FULL]:"appointment.error.newSlotFull",[o.BOOKING_COMPLETED]:"appointment.error.bookingCompleted",[o.NO_EXISTING_BOOKING]:"appointment.error.noExistingBooking",[o.INVALID_SLOT]:"appointment.error.invalidSlot"},i=/* @__PURE__ */new Set([o.DISTRIBUTION_DEACTIVATED,o.DISTRIBUTION_CLOSED,o.DISTRIBUTION_ORDER_LIMIT,o.SOLD_OUT,o.CHECKOUT_MISCONFIGURED]);function n(r){return"INLINE"===r||"STEP_RESET"===r?"error.inlineGeneric":"error.description"}function s(e,s){const{disposition:E}=r(e.code,e.status,e.action),I="number"==typeof e.retryAfter&&Number.isFinite(e.retryAfter)?e.retryAfter:void 0,d=function(r,e,i){return r===o.RATE_LIMITED?i?"error.rateLimited":"error.rateLimitedGeneric":r===o.OVERLOADED?i?"error.overloaded":"error.overloadedGeneric":t[r]??n(e)}(e.code,E,void 0!==I);let O=s(d,void 0!==I?{values:{seconds:I}}:void 0);O.includes("{{")&&(O=s(n(E)));const a="PANEL"===E?s("error.title"):void 0;return{code:e.code,title:a,description:O,disposition:E,retryAfter:e.retryAfter,support:e.support,allowRetry:!i.has(e.code)}}function E(r){if(r instanceof e)return r;const t=r instanceof Error?r.message:String(r);return new e(t,o.INTERNAL_ERROR)}function I(r){return{code:r.code,status:r.status,requestId:r.requestId,correlationId:r.correlationId,details:r.details,issues:r.issues}}export{E as ensureFanfareError,I as toDebug,s as toDisplay};
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export declare const ErrorCodes: {
|
|
|
58
58
|
readonly SELECTION_MISMATCH: "SELECTION_MISMATCH";
|
|
59
59
|
readonly SELECTION_UNAVAILABLE: "SELECTION_UNAVAILABLE";
|
|
60
60
|
readonly SELECTION_LIMIT_REACHED: "SELECTION_LIMIT_REACHED";
|
|
61
|
+
readonly SELECTION_ASSIGNMENT_PENDING: "SELECTION_ASSIGNMENT_PENDING";
|
|
61
62
|
readonly ADMISSION_SELECTION_UNRESOLVED: "ADMISSION_SELECTION_UNRESOLVED";
|
|
62
63
|
readonly SOLD_OUT: "SOLD_OUT";
|
|
63
64
|
readonly STOCK_UNSEEDED: "STOCK_UNSEEDED";
|
|
@@ -91,6 +92,7 @@ export declare const ErrorCodes: {
|
|
|
91
92
|
readonly INVALID_SLOT: "INVALID_SLOT";
|
|
92
93
|
readonly NEW_SLOT_FULL: "NEW_SLOT_FULL";
|
|
93
94
|
readonly CANNOT_CANCEL_ONGOING_OR_PAST: "CANNOT_CANCEL_ONGOING_OR_PAST";
|
|
95
|
+
readonly BOOKING_COMPLETED: "BOOKING_COMPLETED";
|
|
94
96
|
readonly ENTRY_TOKEN_REQUIRED: "ENTRY_TOKEN_REQUIRED";
|
|
95
97
|
readonly ENTRY_TOKEN_INVALID: "ENTRY_TOKEN_INVALID";
|
|
96
98
|
readonly ENTRY_TOKEN_EXPIRED: "ENTRY_TOKEN_EXPIRED";
|
|
@@ -137,8 +137,6 @@ export interface AppointmentDisplayState {
|
|
|
137
137
|
locationId?: string;
|
|
138
138
|
/** Human-readable location name, when supplied by the server. */
|
|
139
139
|
locationName?: string;
|
|
140
|
-
/** Join URL for online appointments. Absent for in-person bookings. */
|
|
141
|
-
joinUrl?: string;
|
|
142
140
|
/** ISO 8601 appointment start timestamp. */
|
|
143
141
|
startTime: string;
|
|
144
142
|
/** ISO 8601 appointment end timestamp. */
|
|
@@ -146,7 +144,9 @@ export interface AppointmentDisplayState {
|
|
|
146
144
|
/** Server confirmation code for the booking, when one is issued. */
|
|
147
145
|
confirmationCode?: string;
|
|
148
146
|
/** Server booking status for this consumer. */
|
|
149
|
-
consumerStatus: "booked" | "
|
|
147
|
+
consumerStatus: "booked" | "completed" | "cancelled";
|
|
148
|
+
/** Who or what cancelled the booking. */
|
|
149
|
+
cancelledCause?: "consumer" | "admin" | "killed";
|
|
150
150
|
};
|
|
151
151
|
/** Milliseconds until the appointment starts, computed by the monitor. */
|
|
152
152
|
msUntilStart?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ConsumerMeResponseSchema as e}from"@fanfare-io/fanfare-sdk-contracts/consumer-me";import{RoutingDecisionV1Schema as t}from"@fanfare-io/fanfare-sdk-contracts/routing";import{SequenceDistributionContextSchema as r}from"@fanfare-io/fanfare-sdk-contracts/sequence-distribution-context";import{createError as i,FanfareError as s,ErrorCodes as n}from"../core/errors.js";import{getLogger as o}from"../core/logger.js";import{parseResponse as c}from"../core/parse-response.js";import{isEntryActionRequired as a}from"../payment/checkout-contract.js";import{getEventBus as u}from"../state/events.js";import{getSDKStore as d}from"../state/store.js";class p extends s{constructor(e){super(e,n.NO_ACCESS),this.name="NoSequenceAvailableError"}}class h extends Error{constructor(e){super(e),this.name="SequenceFullError"}}class l{constructor(e,t,r,i,s,n){this.logger=o(),this.events=u(),this.http=e,this.queues=t,this.draws=r,this.auctions=i,this.waitlists=s,this.timedReleases=n}get store(){return d()}async get(e){try{return this.logger.info("Getting experience details",{experienceId:e}),await this.http.get(`/experiences/${e}`)}catch(t){throw this.logger.error("Failed to get experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}async enter(e){try{const t=this.store.activeExperiences[e];if(t)return this.logger.info("Already in experience",{experienceId:e}),t;if(!this.store.session)throw i.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");this.logger.info("Entering experience",{experienceId:e});const r=await this.http.post(`/experiences/${e}/enter`);return this.store.addActiveExperience(e,r),this.events.emit("experience:entered",{experienceId:e,enteredAt:r.enteredAt}),r}catch(t){if(t&&"object"==typeof t&&"message"in t){const r=t.message.toLowerCase();if(r.includes("already entered")||r.includes("already in experience"))return this.logger.info("Already in experience (from API)",{experienceId:e}),this.store.activeExperiences[e]||{experienceId:e,enteredAt:/* @__PURE__ */(new Date).toISOString()}}throw this.logger.error("Failed to enter experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}async leave(e){try{const r=this.store.activeExperiences[e];if(!r)return void this.logger.warn("Not in experience",{experienceId:e});if(this.logger.info("Leaving experience",{experienceId:e}),r.currentDistribution){const{type:e,id:i}=r.currentDistribution;try{switch(e){case"queue":await this.queues.leave(i);break;case"draw":await this.draws.leave(i)}}catch(t){this.logger.warn("Failed to leave distribution during experience leave",{type:e,id:i,error:t})}}if(r.waitlistId)try{await this.waitlists.leave(r.waitlistId)}catch(t){this.logger.warn("Failed to leave waitlist during experience leave",{waitlistId:r.waitlistId,error:t})}await this.http.delete(`/experiences/${e}/leave`),this.store.removeActiveExperience(e),this.events.emit("experience:left",{experienceId:e})}catch(t){throw this.logger.error("Failed to leave experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}mapGate(e){switch(e.type){case"authentication":return{type:"authentication",status:"required"};case"access_code":return{type:"access_code",status:e.status};case"bot_check":return{type:"bot_check",status:e.status,challenge:e.challenge,mitigation:e.mitigation}}}mapSequence(e){return{id:e.id,name:e.name,experienceId:e.experienceId,priority:e.priority,color:e.color??""}}mapRoutingResult(e){const t=e.offers.filter(e=>"gated"===e.status).map(e=>({sequence:this.mapSequence(e.sequence),status:"gated",gates:e.gates.map(e=>this.mapGate(e))}));if("no_access"===e.outcome)return{outcome:"no_access",offers:t};const r=e.selected;return{outcome:e.outcome,selected:{sequence:this.mapSequence(r.sequence),status:r.status,gates:r.gates.map(e=>this.mapGate(e)),capabilityGrant:r.sequenceCapabilityGrant,capabilityGrantExpiresAt:r.sequenceCapabilityGrantExpiresAt},offers:t}}async findSequence(e,r){try{this.logger.info("Finding sequence",{experienceId:e,hasAccessCode:!!r});const i=await this.http.post(`/experiences/${e}/route`,r?{accessCode:r}:{}),s=c(t,i,{endpoint:`/experiences/${e}/route`},{throwOnFailure:!0});if(!s.ok)throw s.error;return this.mapRoutingResult(s.value)}catch(i){throw this.logger.error("Failed to find sequence",{experienceId:e,error:i}),i}}async validateSequenceAccess(e,t){try{this.logger.info("Validating sequence access",{sequenceId:e,hasAccessCode:!!t});const r=await this.http.post(`/sequences/${e}/validate`,t?{accessCode:t}:{});return{sequenceId:e,hasAccess:r.hasAccess,accessReasons:r.accessReasons,deniedReasons:r.deniedReasons}}catch(r){throw this.logger.error("Failed to validate sequence access",{sequenceId:e,error:r}),r}}async selectSequence(e){try{this.logger.info("Selecting sequence",{sequenceId:e});const t=Object.keys(this.store.activeExperiences)[0];if(!t)throw i.internalError("No active experience found for sequence selection");this.store.updateExperienceSession(t,{assignedSequenceId:e}),this.events.emit("experience:sequence-selected",{sequenceId:e})}catch(t){if(t&&"object"==typeof t&&"message"in t){const e=t.message.toLowerCase();if(e.includes("sequence full")||e.includes("capacity reached"))throw new h("Sequence is full")}throw this.logger.error("Failed to select sequence",{sequenceId:e,error:t}),t}}async getCurrentDistributions(e,t){try{this.logger.info("Getting distribution context",{sequenceId:e});const i=await this.http.get(`/sequences/${e}/distributions`,t),s=c(r,i,{endpoint:`/sequences/${e}/distributions`},{throwOnFailure:!0});if(!s.ok)throw s.error;return s.value}catch(i){throw this.logger.error("Failed to get distribution context",{sequenceId:e,error:i}),i}}async enterDistribution(e){try{this.logger.info("Entering distribution",{type:e.type,id:e.id});const t=d(),r=Object.keys(t.activeExperiences)[0],i=r?t.activeExperiences[r]:null;let s;switch(e.type){case"queue":{const t=await this.queues.enter(e.id);s=a(t)||"DENIED"===t.status||"EXPIRED"===t.status||"LEFT"===t.status?"ended":"active";break}case"draw":{const t=await this.draws.enter(e.id);s=a(t)||"DENIED"===t.status||"NOT_ENTERED"===t.status?"ended":"active";break}case"auction":await this.auctions.enter(e.id),s="active";break;case"appointment":this.logger.warn("Appointment distribution not yet implemented",{id:e.id});break;case"timed_release":s="ENTERED"===(await this.timedReleases.enter(e.id)).status?"active":"ended";break;default:this.logger.warn("Unknown distribution type",{type:e.type})}i&&r&&t.updateExperienceSession(r,{currentDistribution:s?{type:e.type,id:e.id,status:s}:void 0})}catch(t){throw this.logger.error("Failed to enter distribution",{type:e.type,id:e.id,error:t}),t}}getActiveSession(){const e=Object.values(this.store.activeExperiences);return e.length>0?e[0]:null}isInExperience(e){return!!this.store.activeExperiences[e]}getSelectedSequence(){const e=this.getActiveSession();return e&&e.assignedSequenceId?{id:e.assignedSequenceId,name:"",experienceId:e.experienceId,priority:0,color:"#000000"}:null}async getMe(){const t=await this.http.get("/consumers/me"),r=c(e,t,{endpoint:"/consumers/me"},{throwOnFailure:!0});if(!r.ok)throw r.error;return r.value}async confirmCheckout(e,t,r){return this.http.post("/checkout/confirm",{admissionGrant:e,distributionId:t,paymentInput:r})}destroy(){this.logger.debug("ExperienceManagementModule destroyed")}}export{l as ExperienceManagementModule,p as NoSequenceAvailableError,h as SequenceFullError};
|
|
1
|
+
import{ConsumerMeResponseSchema as e}from"@fanfare-io/fanfare-sdk-contracts/consumer-me";import{RoutingDecisionV1Schema as t}from"@fanfare-io/fanfare-sdk-contracts/routing";import{SequenceDistributionContextSchema as r}from"@fanfare-io/fanfare-sdk-contracts/sequence-distribution-context";import{createError as i,FanfareError as s,ErrorCodes as n}from"../core/errors.js";import{getLogger as o}from"../core/logger.js";import{parseResponse as c}from"../core/parse-response.js";import{isEntryActionRequired as a}from"../payment/checkout-contract.js";import{getEventBus as u}from"../state/events.js";import{getSDKStore as d}from"../state/store.js";class p extends s{constructor(e){super(e,n.NO_ACCESS),this.name="NoSequenceAvailableError"}}class h extends Error{constructor(e){super(e),this.name="SequenceFullError"}}class l{constructor(e,t,r,i,s,n){this.logger=o(),this.events=u(),this.http=e,this.queues=t,this.draws=r,this.auctions=i,this.waitlists=s,this.timedReleases=n}get store(){return d()}async get(e){try{return this.logger.info("Getting experience details",{experienceId:e}),await this.http.get(`/experiences/${e}`)}catch(t){throw this.logger.error("Failed to get experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}async enter(e){try{const t=this.store.activeExperiences[e];if(t)return this.logger.info("Already in experience",{experienceId:e}),t;if(!this.store.session)throw i.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");this.logger.info("Entering experience",{experienceId:e});const r=await this.http.post(`/experiences/${e}/enter`);return this.store.addActiveExperience(e,r),this.events.emit("experience:entered",{experienceId:e,enteredAt:r.enteredAt}),r}catch(t){if(t&&"object"==typeof t&&"message"in t){const r=t.message.toLowerCase();if(r.includes("already entered")||r.includes("already in experience"))return this.logger.info("Already in experience (from API)",{experienceId:e}),this.store.activeExperiences[e]||{experienceId:e,enteredAt:/* @__PURE__ */(new Date).toISOString()}}throw this.logger.error("Failed to enter experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}async leave(e){try{const r=this.store.activeExperiences[e];if(!r)return void this.logger.warn("Not in experience",{experienceId:e});if(this.logger.info("Leaving experience",{experienceId:e}),r.currentDistribution){const{type:e,id:i}=r.currentDistribution;try{switch(e){case"queue":await this.queues.leave(i);break;case"draw":await this.draws.leave(i)}}catch(t){this.logger.warn("Failed to leave distribution during experience leave",{type:e,id:i,error:t})}}if(r.waitlistId)try{await this.waitlists.leave(r.waitlistId)}catch(t){this.logger.warn("Failed to leave waitlist during experience leave",{waitlistId:r.waitlistId,error:t})}await this.http.delete(`/experiences/${e}/leave`),this.store.removeActiveExperience(e),this.events.emit("experience:left",{experienceId:e})}catch(t){throw this.logger.error("Failed to leave experience",{experienceId:e,error:t}),this.events.emit("experience:error",{experienceId:e,error:t instanceof Error?t.message:"Unknown error"}),t}}mapGate(e){switch(e.type){case"authentication":return{type:"authentication",status:"required",requirement:e.requirement??"authenticated"};case"access_code":return{type:"access_code",status:e.status};case"bot_check":return{type:"bot_check",status:e.status,challenge:e.challenge,mitigation:e.mitigation}}}mapSequence(e){return{id:e.id,name:e.name,experienceId:e.experienceId,priority:e.priority,color:e.color??""}}mapRoutingResult(e){const t=e.offers.filter(e=>"gated"===e.status).map(e=>({sequence:this.mapSequence(e.sequence),status:"gated",gates:e.gates.map(e=>this.mapGate(e))}));if("no_access"===e.outcome)return{outcome:"no_access",offers:t};const r=e.selected;return{outcome:e.outcome,selected:{sequence:this.mapSequence(r.sequence),status:r.status,gates:r.gates.map(e=>this.mapGate(e)),capabilityGrant:r.sequenceCapabilityGrant,capabilityGrantExpiresAt:r.sequenceCapabilityGrantExpiresAt},offers:t}}async findSequence(e,r){try{this.logger.info("Finding sequence",{experienceId:e,hasAccessCode:!!r});const i=await this.http.post(`/experiences/${e}/route`,r?{accessCode:r}:{}),s=c(t,i,{endpoint:`/experiences/${e}/route`},{throwOnFailure:!0});if(!s.ok)throw s.error;return this.mapRoutingResult(s.value)}catch(i){throw this.logger.error("Failed to find sequence",{experienceId:e,error:i}),i}}async validateSequenceAccess(e,t){try{this.logger.info("Validating sequence access",{sequenceId:e,hasAccessCode:!!t});const r=await this.http.post(`/sequences/${e}/validate`,t?{accessCode:t}:{});return{sequenceId:e,hasAccess:r.hasAccess,accessReasons:r.accessReasons,deniedReasons:r.deniedReasons}}catch(r){throw this.logger.error("Failed to validate sequence access",{sequenceId:e,error:r}),r}}async selectSequence(e){try{this.logger.info("Selecting sequence",{sequenceId:e});const t=Object.keys(this.store.activeExperiences)[0];if(!t)throw i.internalError("No active experience found for sequence selection");this.store.updateExperienceSession(t,{assignedSequenceId:e}),this.events.emit("experience:sequence-selected",{sequenceId:e})}catch(t){if(t&&"object"==typeof t&&"message"in t){const e=t.message.toLowerCase();if(e.includes("sequence full")||e.includes("capacity reached"))throw new h("Sequence is full")}throw this.logger.error("Failed to select sequence",{sequenceId:e,error:t}),t}}async getCurrentDistributions(e,t){try{this.logger.info("Getting distribution context",{sequenceId:e});const i=await this.http.get(`/sequences/${e}/distributions`,t),s=c(r,i,{endpoint:`/sequences/${e}/distributions`},{throwOnFailure:!0});if(!s.ok)throw s.error;return s.value}catch(i){throw this.logger.error("Failed to get distribution context",{sequenceId:e,error:i}),i}}async enterDistribution(e){try{this.logger.info("Entering distribution",{type:e.type,id:e.id});const t=d(),r=Object.keys(t.activeExperiences)[0],i=r?t.activeExperiences[r]:null;let s;switch(e.type){case"queue":{const t=await this.queues.enter(e.id);s=a(t)||"DENIED"===t.status||"EXPIRED"===t.status||"LEFT"===t.status?"ended":"active";break}case"draw":{const t=await this.draws.enter(e.id);s=a(t)||"DENIED"===t.status||"NOT_ENTERED"===t.status?"ended":"active";break}case"auction":await this.auctions.enter(e.id),s="active";break;case"appointment":this.logger.warn("Appointment distribution not yet implemented",{id:e.id});break;case"timed_release":s="ENTERED"===(await this.timedReleases.enter(e.id)).status?"active":"ended";break;default:this.logger.warn("Unknown distribution type",{type:e.type})}i&&r&&t.updateExperienceSession(r,{currentDistribution:s?{type:e.type,id:e.id,status:s}:void 0})}catch(t){throw this.logger.error("Failed to enter distribution",{type:e.type,id:e.id,error:t}),t}}getActiveSession(){const e=Object.values(this.store.activeExperiences);return e.length>0?e[0]:null}isInExperience(e){return!!this.store.activeExperiences[e]}getSelectedSequence(){const e=this.getActiveSession();return e&&e.assignedSequenceId?{id:e.assignedSequenceId,name:"",experienceId:e.experienceId,priority:0,color:"#000000"}:null}async getMe(){const t=await this.http.get("/consumers/me"),r=c(e,t,{endpoint:"/consumers/me"},{throwOnFailure:!0});if(!r.ok)throw r.error;return r.value}async confirmCheckout(e,t,r){return this.http.post("/checkout/confirm",{admissionGrant:e,distributionId:t,paymentInput:r})}destroy(){this.logger.debug("ExperienceManagementModule destroyed")}}export{l as ExperienceManagementModule,p as NoSequenceAvailableError,h as SequenceFullError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{GrantedSelectionSchema as e}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as t,computed as i}from"nanostores";import*as s from"valibot";import{createAppointmentDriver as r,withAppointmentTerminalConsumer as n}from"../appointments/appointment.driver.js";import{createAuctionDriver as o,isUpgradeableGenericLoss as a,withAuctionTerminalConsumer as c}from"../auctions/auction.driver.js";import{createError as d,isFanfareError as u,ErrorCodes as h}from"../core/errors.js";import{createLogger as l}from"../core/logger.js";import{generateId as p}from"../core/utils.js";import{createDrawDriver as m,withDrawTerminalConsumer as g}from"../draws/draw.driver.js";import{createQueueDriver as y,withQueueTerminalConsumer as f}from"../queues/queue.driver.js";import{clearExperienceContext as b,clearSequenceContext as S,resolveCapabilityOptions as I,bindDistributionContext as A,bindWaitlist as v,upsertSequenceToken as q,peekSequenceToken as w,getSequenceTokenExpiryMs as C}from"../state/capability-token-registry.js";import{getEventBus as k}from"../state/events.js";import{getSDKStore as x}from"../state/store.js";import{createTimedReleaseDriver as j,withTimedReleaseTerminalConsumer as T}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as D}from"./experience.module.js";import{buildJourneyView as R}from"./journey-view.js";import{createInitialSnapshot as E,getParticipation as O,getSequencePhase as M,normalizeSequenceState as F,buildSnapshot as G,getSequenceId as _,getConsumer as P,getWaitlistAttachment as z,getDistribution as H,getOutcome as B,getGrant as N,getSequenceState as U,getCheckout as L,getGates as J,isRoutedState as W,getAdmissionGrantExpiresAt as Q,validateSequenceState as V}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as $}from"./settlement.js";const K="Cannot commit a stale sequence transition",X="Cannot commit a sequence transition before routing",Y=3e4,Z=/* @__PURE__ */new Set([h.RATE_LIMITED,h.OVERLOADED,h.SERVICE_UNAVAILABLE,h.TIMEOUT,h.NETWORK_ERROR]),ee=[2e3,5e3,1e4],te=3*ee.length,ie=[2e3,5e3,1e4];function se(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function re(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase||"ended"===e.sequence.phase)}class ne{constructor(e,s){this.experienceId=e,this.sdk=s,this.store=x(),this.logger=l(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredFromPoisonedRoutingSnapshot=!1,this.EVENT_BUFFER_LIMIT=20;const r=this.restoreFromStore(),n=r??E(this.experienceId),o=r?O(r):void 0;o&&"participating"===M(n)&&this.ensureDisplayAtom(o),this.stateAtom=t(n),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=i(this.stateAtom,e=>this.buildView(e)),this.events$=i(this.stateAtom,e=>e.events),this.latestEvent$=i(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}),r?(this.restoredFromPoisonedRoutingSnapshot&&this.persist(),this.rearmTimersIfNeeded()):this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{e?.reset&&(this.clearRoutedRuntimeState(),this.setSnapshot(E(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}),{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}),{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(),b(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(E(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=F({sequence:Fe(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=G({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&&S(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"===M(this.stateAtom.get())&&(this.emitEvent("sequence_change","success","user","Grant issued",{grant:s.grant.token}),k().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=_(e);if(!t)throw d.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,I({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(_(s)!==t)return;const r=P(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,z(s)),a=H(s);n&&A({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&v({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,B(s)),u=this.withTerminalConsumer(r,c),h=!n&&"routed"===s.journeyStage&&("granted"===s.sequence.phase||"ended"===s.sequence.phase)?s.sequence:this.projectSequenceState({distribution:n,consumer:u,grant:N(s),settlement:_e(s),selection:Pe(s),checkout:Ge(s),outcome:c});this.commit(h,{waitlist:o,lastSyncedAt:Date.now(),degraded:!1}),this.emitIfDistributionChanged(a,n),"granted"===h.phase||"ended"===h.phase?this.resetDistributionSelfCheck():this.evaluateDistributionSelfCheck(n)}catch(t){const i=this.stateAtom.get();if(_(i)===_(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=U(e),i=H(e),s=z(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw d.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=U(e),i=H(e),s=z(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}))})}claim(){const e=N(this.stateAtom.get());if(!e)throw d.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:e.distributionType,distributionId:e.distributionId,sequenceId:e.sequenceId}),k().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:e.sequenceId,grant:e.token}),e}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw d.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(!u(e)||e.code!==h.ADMISSION_NOT_FOUND)return!1;const t=_(this.stateAtom.get());try{const e=await this.sdk.experiences.getMe();if(this.destroyed||_(this.stateAtom.get())!==t)return!1;const i=await this.resumeFromMe(e),s=this.stateAtom.get();return i&&_(s)===t&&"ended"===M(s)&&void 0!==L(s)}catch{return!1}}async submitCheckout(e){const t=this.stateAtom.get(),i=U(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw d.validationError("No grant awaiting embedded checkout");const s=_(t),r=i.grant.token;this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(n){throw u(n)&&n.code===h.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),n}const o=this.stateAtom.get(),a=U(o);if(_(o)!==s||"granted"!==a?.phase||a.grant.token!==r)throw d.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...a,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=U(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){const i=t?.carryFrom??this.stateAtom.get(),s=_(i);let r,n=!1;try{const o=await this.resolveRouting(e,{transient:t?.transient});if(!o||this.destroyed)return;if(r=this.stateAtom.get().revision,n=s===o.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const a=await this.sdk.experiences.getCurrentDistributions(o.sequenceId,I({experienceId:this.experienceId,sequenceId:o.sequenceId}));if(this.destroyed)return;const c=n?P(i):void 0,d=this.buildDistributionSummary(a,o.sequenceId,{consumer:c,settlingDistributionId:n&&"routed"===i.journeyStage&&"settling"===i.sequence.phase?i.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(a,n?z(i):void 0);d&&A({experienceId:this.experienceId,sequenceId:o.sequenceId,distributionContext:d}),u&&v({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:o.sequenceId});const h=n?N(i):void 0,l=n?Ge(i):void 0,p=n?_e(i):void 0,m=n?this.deriveTerminalOutcomeForClosedDistribution(c,d,B(i)):void 0,g=this.withTerminalConsumer(c,m),y=!d&&n&&"routed"===i.journeyStage&&("granted"===i.sequence.phase||"ended"===i.sequence.phase)?i.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:n?Pe(i):void 0,checkout:l,outcome:m});n||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),b=G({base:this.baseFromSnapshot(f,{lastSyncedAt:Date.now(),degraded:!1}),journeyStage:"routed",sequenceId:o.sequenceId,offers:o.offers,sequence:y,waitlist:u,sequenceRef:o.selectedSequence});this.setSnapshot(b),this.resumeDeferredGrantHydration(),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const S=n?H(i):void 0;this.emitIfDistributionChanged(S,d),this.evaluateDistributionSelfCheck(d)}catch(o){if("routed"===i.journeyStage&&n&&this.isQuietableDegradationError(o))throw this.restoreQuietSnapshot(i),this.markErrorQuietable(o);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===s&&this.restoreClearedSequenceContext(i,s),void 0!==r&&this.stateAtom.get().revision===r)this.setSnapshot(i);else{const e=this.stateAtom.get();"routed"===i.journeyStage?this.setSnapshot(G({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:i.sequence,waitlist:i.waitlist,sequenceRef:i.sequenceRef})):this.setSnapshot(G({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw o}}restoreClearedSequenceContext(e,t){const i=H(e);i&&A({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=z(e);s&&v({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&q({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}async resolveRouting(e,t){const i=this.stateAtom.get(),s=_(i);let r;this.transitionToRouting();try{r=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(o){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),o}if(this.destroyed)return null;if("gated"===r.outcome&&r.selected){const e=r.selected.gates;return"routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:r.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null}if("no_access"===r.outcome||!r.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new D("No access available for this experience");const n=r.selected.sequence.id;if(s&&s!==n&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=w(this.experienceId,s),S(this.experienceId,s)),r.selected.capabilityGrant&&q({experienceId:this.experienceId,sequenceId:n,token:r.selected.capabilityGrant,expiresAt:r.selected.capabilityGrantExpiresAt}),s!==n){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,o=r.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,o);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:n,...void 0===e?{}:{previousPriority:e},priority:o,direction:a})}return{sequenceId:n,selectedSequence:r.selected.sequence,offers:r.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(J(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:r({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>U(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:o({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>U(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)}),queue:y({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>U(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:j({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>U(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:m({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>U(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){return R(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(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers)}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(G({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(G({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(G({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(G({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(G({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(G({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 d.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw d.validationError(X);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw d.validationError(K);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=G({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 u(e)&&(e.retryable||Z.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 G({base:i,journeyStage:"ready"});case"routing":return G({base:i,journeyStage:"routing"});case"gated":return G({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return G({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:p(),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||$(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=Me(e[this.experienceId]);return t?"routing"===t.journeyStage?(this.restoredFromPoisonedRoutingSnapshot=!0,G({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();this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?M(e):void 0,r=t.journeyStage,n="routed"===r?M(t):void 0;i===r&&s===n||k().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw d.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=_(i);if(!s)throw d.validationError(X);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()},u=this.withTerminalConsumer(P(i),c,{finalize:!0}),h=this.projectSequenceState({distribution:r,consumer:u,checkout:n,outcome:c});this.commit(h,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}),k().emit("journey:ended",{experienceId:this.experienceId,sequenceId:s,outcome:c})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==M(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(!W(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:P(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(re(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(re(s,i.sequenceId))this.clearGrantHydration();else if(void 0!==t&&Date.now()>=t)this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});else{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(re(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=ie[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(!se(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>=ee.length)return;if(this.selfCheckTotalAttempts>=te)return;const s=this.selfCheckStartedAtMs+ee[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=C(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}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();const t=U(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=Q(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<Y)return void this.scheduleCapabilityRefresh(Y);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(Y*2**this.consecutiveRefreshFailures,6e5);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(){try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!u(e)||e.code!==h.VALIDATION_ERROR||e.message!==K&&e.message!==X)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=O(e),i=_(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;const t=this.getRuntimeBinding();if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),void this.setSnapshot(E(this.experienceId),{persist:!1});const i=Me(e);if(!i)return;const s="routing"===i.journeyStage?G({base:this.baseFromSnapshot(i),journeyStage:"ready"}):i,r=this.stateAtom.get();if(r.revision===s.revision&&r.updatedAt===s.updatedAt)return;const n=O(s),o=_(s),a=n&&o?{sequenceId:o,participationId:n.id,participationType:n.type}:null;a?t&&t.sequenceId===a.sequenceId&&t.participationId===a.participationId&&t.participationType===a.participationType||this.resetDisplayAtom(a.participationType):this.displayAtom=null,this.setSnapshot(s,{persist:!1}),n?this.ensureDisplayAtom(n):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){return this.resetDisplayAtom(e.type)}resetDisplayAtom(e){const i=this.displayAtom;if(!i||i.get().type!==e){const i=t({type:e});return this.displayAtom=i,i}return i.set({type:e}),i}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();e?this.resetDisplayAtom(e.participationType):this.displayAtom=null}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i&&!a(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 n(e,t);case"auction":return c(e,t,i);case"draw":return g(e,t);case"queue":return f(e,t);case"timed_release":return T(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const oe=s.picklist(["queue","draw","auction","timed_release","appointment"]),ae=oe,ce=s.picklist(["queue","draw","auction","timed_release"]),de=s.picklist(["draw","auction"]),ue=s.strictObject({id:s.string(),ts:s.number(),kind:s.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:s.picklist(["info","success","warning","error"]),audience:s.picklist(["user","system","analytics"]),message:s.string(),detail:s.optional(s.record(s.string(),s.unknown()))}),he=s.strictObject({id:s.string(),name:s.string(),experienceId:s.string(),priority:s.number(),color:s.string()}),le=s.strictObject({type:oe,id:s.string(),sequenceId:s.optional(s.string()),lifecycle:s.string(),startsAt:s.optional(s.string()),opensAt:s.optional(s.string()),closesAt:s.optional(s.string()),settlesAt:s.optional(s.string()),result:s.optional(s.string()),details:s.optional(s.unknown()),updatedAt:s.optional(s.number())}),pe=s.strictObject({status:s.string(),id:s.optional(s.string()),distributionId:s.optional(s.string()),waitlistId:s.optional(s.string()),enteredAt:s.optional(s.string()),admissionGrant:s.optional(s.string()),grantedAt:s.optional(s.string()),deniedReason:s.optional(s.string()),position:s.optional(s.number()),highestBid:s.optional(s.string()),currentBid:s.optional(s.string()),slotId:s.optional(s.string()),locationId:s.optional(s.string()),bookedAt:s.optional(s.string()),checkedInAt:s.optional(s.string()),completedAt:s.optional(s.string()),cancelledAt:s.optional(s.string()),noShowAt:s.optional(s.string())}),me=s.strictObject({token:s.string(),distributionType:ce,distributionId:s.string(),sequenceId:s.string(),experienceId:s.string(),issuedAt:s.optional(s.string()),expiresAt:s.optional(s.string())}),ge=s.strictObject({orderId:s.string(),amount:s.string(),currencyCode:s.string(),paymentMethodLast4:s.optional(s.string()),paymentMethodBrand:s.optional(s.string())}),ye=s.strictObject({publishableKey:s.string(),stripeAccount:s.optional(s.string())}),fe=s.strictObject({type:s.string(),reason:s.optional(s.string()),at:s.optional(s.string()),distributionType:s.optional(oe),distributionId:s.optional(s.string())}),be=s.strictObject({phase:s.literal("unavailable"),reason:s.optional(s.string())}),Se=s.strictObject({waitlistId:s.string(),status:s.picklist(["not_waitlisted","waitlisted"]),entryId:s.optional(s.string())}),Ie=s.strictObject({phase:s.literal("scheduled"),mechanism:ae,distribution:le}),Ae=s.strictObject({phase:s.literal("enterable"),mechanism:ae,distribution:le,consumer:pe,paymentProvider:s.optional(ye)}),ve=s.strictObject({phase:s.literal("participating"),mechanism:ae,distribution:le,consumer:pe}),qe=s.strictObject({phase:s.literal("settling"),mechanism:de,distribution:le,consumer:pe}),we=s.variant("processor",[s.strictObject({processor:s.literal("external")}),s.strictObject({processor:s.literal("internal"),mode:s.literal("at_end"),state:s.picklist(["awaiting","in_progress"]),paymentProvider:s.optional(ye)}),s.strictObject({processor:s.literal("internal"),mode:s.literal("pre_auth")})]),Ce=s.strictObject({phase:s.literal("granted"),mechanism:ce,distribution:le,consumer:pe,grant:me,settlement:we,selection:e}),ke=s.strictObject({phase:s.literal("ended"),mechanism:ae,distribution:le,consumer:pe,outcome:fe,checkout:s.optional(ge)}),xe=s.union([be,Ie,Ae,ve,qe,Ce,ke]),je={schemaVersion:s.literal(5),revision:s.number(),updatedAt:s.number(),experienceId:s.string(),accessCode:s.optional(s.string()),experience:s.optional(s.unknown()),events:s.array(ue),lastSeenEventId:s.optional(s.string()),lastSyncedAt:s.optional(s.number())},Te=s.strictObject({...je,journeyStage:s.literal("ready")}),De=s.strictObject({...je,journeyStage:s.literal("routing")}),Re=s.strictObject({...je,journeyStage:s.literal("gated"),gates:s.array(s.unknown()),selected:s.optional(s.strictObject({sequence:s.unknown(),status:s.literal("gated")}))}),Ee=s.strictObject({...je,journeyStage:s.literal("routed"),sequenceId:s.string(),offers:s.array(s.unknown()),sequence:xe,waitlist:s.optional(Se),sequenceRef:s.optional(he)}),Oe=s.union([Te,De,Re,Ee]);function Me(e){const t=s.safeParse(Oe,e);if(!t.success)return null;const{schemaVersion:i,...r}=t.output;if("routed"===r.journeyStage)try{V(r.sequence)}catch{return null}return r}function Fe(e){if(("participating"===e.phase||"settling"===e.phase)&&"selection"in e){const{selection:t,...i}=e;return i}return e}function Ge(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?L(e):e.sequence.checkout}function _e(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function Pe(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{ne as ExperienceJourney,se as isSelfContradictingDistribution};
|
|
1
|
+
import{AuthenticationRoutingGateV1Schema as e,AccessCodeRoutingGateV1Schema as t,BotCheckRoutingGateV1Schema as i}from"@fanfare-io/fanfare-sdk-contracts/routing";import{GrantedSelectionSchema as s}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as r,computed as n}from"nanostores";import*as o from"valibot";import{createAppointmentDriver as a,withAppointmentTerminalConsumer as c}from"../appointments/appointment.driver.js";import{createAuctionDriver as d,isUpgradeableGenericLoss as u,withAuctionTerminalConsumer as h}from"../auctions/auction.driver.js";import{createError as l,isFanfareError as p,ErrorCodes as m}from"../core/errors.js";import{createLogger as g}from"../core/logger.js";import{generateId as y}from"../core/utils.js";import{createDrawDriver as f,withDrawTerminalConsumer as b}from"../draws/draw.driver.js";import{createQueueDriver as S,withQueueTerminalConsumer as I}from"../queues/queue.driver.js";import{clearExperienceContext as A,clearSequenceContext as v,resolveCapabilityOptions as q,bindDistributionContext as w,bindWaitlist as C,upsertSequenceToken as k,peekSequenceToken as x,getSequenceTokenExpiryMs as j}from"../state/capability-token-registry.js";import{getEventBus as T}from"../state/events.js";import{getSDKStore as D}from"../state/store.js";import{createTimedReleaseDriver as R,withTimedReleaseTerminalConsumer as E}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as O}from"./experience.module.js";import{buildJourneyView as M}from"./journey-view.js";import{createInitialSnapshot as F,getParticipation as G,getSequencePhase as P,normalizeSequenceState as _,buildSnapshot as N,getSequenceId as z,getConsumer as H,getWaitlistAttachment as U,getDistribution as B,getOutcome as L,getGrant as J,getSequenceState as W,getCheckout as Q,isPreEntryJourney as V,getGates as $,isRoutedState as K,getAdmissionGrantExpiresAt as X,validateSequenceState as Y}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as Z}from"./settlement.js";const ee="Cannot commit a stale sequence transition",te="Cannot commit a sequence transition before routing",ie=3e4,se=/* @__PURE__ */new Set([m.RATE_LIMITED,m.OVERLOADED,m.SERVICE_UNAVAILABLE,m.TIMEOUT,m.NETWORK_ERROR]),re=[2e3,5e3,1e4],ne=3*re.length,oe=[2e3,5e3,1e4];function ae(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function ce(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase||"ended"===e.sequence.phase)}class de{constructor(e,t){this.experienceId=e,this.sdk=t,this.store=D(),this.routingSession=this.store.session,this.logger=g(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredSnapshotNeedsPersist=!1,this.EVENT_BUFFER_LIMIT=20;const i=this.restoreFromStore(),s=i??F(this.experienceId),o=i?G(i):void 0;o&&"participating"===P(s)&&this.ensureDisplayAtom(o),this.stateAtom=r(s),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=n(this.stateAtom,e=>this.buildView(e)),this.events$=n(this.stateAtom,e=>e.events),this.latestEvent$=n(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()=>{e?.reset&&(this.clearRoutedRuntimeState(),this.setSnapshot(F(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}),{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}),{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(),A(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(F(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=_({sequence:ze(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=N({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&&v(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}),T().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 l.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,q({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(z(s)!==t)return;const r=H(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,U(s)),a=B(s);n&&w({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&C({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,L(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:J(s),settlement:Ue(s),selection:Be(s),checkout:He(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)}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=W(e),i=B(e),s=U(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw l.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=W(e),i=B(e),s=U(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}))})}claim(){const e=J(this.stateAtom.get());if(!e)throw l.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:e.distributionType,distributionId:e.distributionId,sequenceId:e.sequenceId}),T().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:e.sequenceId,grant:e.token}),e}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw l.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(!p(e)||e.code!==m.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=W(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw l.validationError("No grant awaiting embedded checkout");const s=z(t),r=i.grant.token;this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(n){throw p(n)&&n.code===m.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),n}const o=this.stateAtom.get(),a=W(o);if(z(o)!==s||"granted"!==a?.phase||a.grant.token!==r)throw l.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...a,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=W(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;(s?.consumerId!==this.routingSession?.consumerId||s?.type!==this.routingSession?.type)&&"routed"===i.journeyStage&&V(i)&&(this.clearRoutedRuntimeState(),v(this.experienceId,i.sequenceId),i=N({base:this.baseFromSnapshot(i),journeyStage:"ready"}),this.setSnapshot(i)),this.routingSession=s;const r=i,n=z(r);let o,a=!1;try{const i=await this.resolveRouting(e,{transient:t?.transient});if(!i||this.destroyed)return;if(o=this.stateAtom.get().revision,a=n===i.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const s=await this.sdk.experiences.getCurrentDistributions(i.sequenceId,q({experienceId:this.experienceId,sequenceId:i.sequenceId}));if(this.destroyed)return;const c=a?H(r):void 0,d=this.buildDistributionSummary(s,i.sequenceId,{consumer:c,settlingDistributionId:a&&"routed"===r.journeyStage&&"settling"===r.sequence.phase?r.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(s,a?U(r):void 0);d&&w({experienceId:this.experienceId,sequenceId:i.sequenceId,distributionContext:d}),u&&C({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:i.sequenceId});const h=a?J(r):void 0,l=a?He(r):void 0,p=a?Ue(r):void 0,m=a?this.deriveTerminalOutcomeForClosedDistribution(c,d,L(r)):void 0,g=this.withTerminalConsumer(c,m),y=!d&&a&&"routed"===r.journeyStage&&("granted"===r.sequence.phase||"ended"===r.sequence.phase)?r.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:a?Be(r):void 0,checkout:l,outcome:m});a||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),b=N({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=a?B(r):void 0;this.emitIfDistributionChanged(S,d),this.evaluateDistributionSelfCheck(d)}catch(c){if("routed"===r.journeyStage&&a&&this.isQuietableDegradationError(c))throw this.restoreQuietSnapshot(r),this.markErrorQuietable(c);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===n&&this.restoreClearedSequenceContext(r,n),void 0!==o&&this.stateAtom.get().revision===o)this.setSnapshot(r);else{const e=this.stateAtom.get();"routed"===r.journeyStage?this.setSnapshot(N({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:r.sequenceId,offers:r.offers,sequence:r.sequence,waitlist:r.waitlist,sequenceRef:r.sequenceRef})):this.setSnapshot(N({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw c}}restoreClearedSequenceContext(e,t){const i=B(e);i&&w({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=U(e);s&&C({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&k({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}async resolveRouting(e,t){const i=this.stateAtom.get(),s=z(i);let r;this.transitionToRouting();try{r=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(o){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),o}if(this.destroyed)return null;if("gated"===r.outcome&&r.selected){const e=r.selected.gates;return"routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:r.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null}if("no_access"===r.outcome||!r.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new O("No access available for this experience");const n=r.selected.sequence.id;if(s&&s!==n&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=x(this.experienceId,s),v(this.experienceId,s)),r.selected.capabilityGrant&&k({experienceId:this.experienceId,sequenceId:n,token:r.selected.capabilityGrant,expiresAt:r.selected.capabilityGrantExpiresAt}),s!==n){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,o=r.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,o);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:n,...void 0===e?{}:{previousPriority:e},priority:o,direction:a})}return{sequenceId:n,selectedSequence:r.selected.sequence,offers:r.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if($(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:a({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>W(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:d({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>W(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)}),queue:S({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>W(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:R({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>W(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:f({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>W(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){return M(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(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers)}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(N({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(N({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(N({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(N({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(N({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(N({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 l.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw l.validationError(te);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw l.validationError(ee);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=N({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 p(e)&&(e.retryable||se.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 N({base:i,journeyStage:"ready"});case"routing":return N({base:i,journeyStage:"routing"});case"gated":return N({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return N({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:y(),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||Z(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=Ne(e[this.experienceId]);return t?"routing"===t.journeyStage||"routed"===t.journeyStage&&V(t)?("routed"===t.journeyStage&&v(this.experienceId,t.sequenceId),this.restoredSnapshotNeedsPersist=!0,N({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();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||T().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw l.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 l.validationError(te);const r=B(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(H(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}),T().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(!K(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:H(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(ce(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(ce(s,i.sequenceId))this.clearGrantHydration();else if(void 0!==t&&Date.now()>=t)this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});else{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(ce(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=oe[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(!ae(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>=re.length)return;if(this.selfCheckTotalAttempts>=ne)return;const s=this.selfCheckStartedAtMs+re[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=j(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}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();const t=W(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=X(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<ie)return void this.scheduleCapabilityRefresh(ie);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(ie*2**this.consecutiveRefreshFailures,6e5);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(){try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!p(e)||e.code!==m.VALIDATION_ERROR||e.message!==ee&&e.message!==te)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=G(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(F(this.experienceId),{persist:!1});const t=Ne(e);if(!t)return;const i="routing"===t.journeyStage?N({base:this.baseFromSnapshot(t),journeyStage:"ready"}):t,s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=G(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=r({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&&!u(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 c(e,t);case"auction":return h(e,t,i);case"draw":return b(e,t);case"queue":return I(e,t);case"timed_release":return E(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const ue=o.picklist(["queue","draw","auction","timed_release","appointment"]),he=ue,le=o.picklist(["queue","draw","auction","timed_release"]),pe=o.picklist(["draw","auction"]),me=o.strictObject({id:o.string(),ts:o.number(),kind:o.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:o.picklist(["info","success","warning","error"]),audience:o.picklist(["user","system","analytics"]),message:o.string(),detail:o.optional(o.record(o.string(),o.unknown()))}),ge=o.strictObject({id:o.string(),name:o.string(),experienceId:o.string(),priority:o.number(),color:o.string()}),ye=o.strictObject({type:ue,id:o.string(),sequenceId:o.optional(o.string()),lifecycle:o.string(),startsAt:o.optional(o.string()),opensAt:o.optional(o.string()),closesAt:o.optional(o.string()),settlesAt:o.optional(o.string()),result:o.optional(o.string()),details:o.optional(o.unknown()),updatedAt:o.optional(o.number())}),fe=o.strictObject({status:o.string(),id:o.optional(o.string()),distributionId:o.optional(o.string()),waitlistId:o.optional(o.string()),enteredAt:o.optional(o.string()),admissionGrant:o.optional(o.string()),grantedAt:o.optional(o.string()),deniedReason:o.optional(o.string()),position:o.optional(o.number()),highestBid:o.optional(o.string()),currentBid:o.optional(o.string()),slotId:o.optional(o.string()),locationId:o.optional(o.string()),bookedAt:o.optional(o.string()),checkedInAt:o.optional(o.string()),completedAt:o.optional(o.string()),cancelledAt:o.optional(o.string()),noShowAt:o.optional(o.string())}),be=o.strictObject({token:o.string(),distributionType:le,distributionId:o.string(),sequenceId:o.string(),experienceId:o.string(),issuedAt:o.optional(o.string()),expiresAt:o.optional(o.string())}),Se=o.strictObject({orderId:o.string(),amount:o.string(),currencyCode:o.string(),paymentMethodLast4:o.optional(o.string()),paymentMethodBrand:o.optional(o.string())}),Ie=o.strictObject({publishableKey:o.string(),stripeAccount:o.optional(o.string())}),Ae=o.strictObject({type:o.string(),reason:o.optional(o.string()),at:o.optional(o.string()),distributionType:o.optional(ue),distributionId:o.optional(o.string())}),ve=o.strictObject({phase:o.literal("unavailable"),reason:o.optional(o.string())}),qe=o.strictObject({waitlistId:o.string(),status:o.picklist(["not_waitlisted","waitlisted"]),entryId:o.optional(o.string())}),we=o.strictObject({phase:o.literal("scheduled"),mechanism:he,distribution:ye}),Ce=o.strictObject({phase:o.literal("enterable"),mechanism:he,distribution:ye,consumer:fe,paymentProvider:o.optional(Ie)}),ke=o.strictObject({phase:o.literal("participating"),mechanism:he,distribution:ye,consumer:fe}),xe=o.strictObject({phase:o.literal("settling"),mechanism:pe,distribution:ye,consumer:fe}),je=o.variant("processor",[o.strictObject({processor:o.literal("external")}),o.strictObject({processor:o.literal("internal"),mode:o.literal("at_end"),state:o.picklist(["awaiting","in_progress"]),paymentProvider:o.optional(Ie)}),o.strictObject({processor:o.literal("internal"),mode:o.literal("pre_auth")})]),Te=o.strictObject({phase:o.literal("granted"),mechanism:le,distribution:ye,consumer:fe,grant:be,settlement:je,selection:s}),De=o.strictObject({phase:o.literal("ended"),mechanism:he,distribution:ye,consumer:fe,outcome:Ae,checkout:o.optional(Se)}),Re=o.union([ve,we,Ce,ke,xe,Te,De]),Ee={schemaVersion:o.literal(5),revision:o.number(),updatedAt:o.number(),experienceId:o.string(),accessCode:o.optional(o.string()),experience:o.optional(o.unknown()),events:o.array(me),lastSeenEventId:o.optional(o.string()),lastSyncedAt:o.optional(o.number())},Oe=o.strictObject({...Ee,journeyStage:o.literal("ready")}),Me=o.strictObject({...Ee,journeyStage:o.literal("routing")}),Fe=o.variant("type",[o.object({...o.omit(e,["reason"]).entries,requirement:o.optional(o.picklist(["session","authenticated"]),"authenticated")}),o.omit(t,["reason"]),o.omit(i,["reason"])]),Ge=o.strictObject({...Ee,journeyStage:o.literal("gated"),gates:o.array(Fe),selected:o.optional(o.strictObject({sequence:o.unknown(),status:o.literal("gated")}))}),Pe=o.strictObject({...Ee,journeyStage:o.literal("routed"),sequenceId:o.string(),offers:o.array(o.object({sequence:o.unknown(),status:o.literal("gated"),gates:o.array(Fe)})),sequence:Re,waitlist:o.optional(qe),sequenceRef:o.optional(ge)}),_e=o.union([Oe,Me,Ge,Pe]);function Ne(e){const t=o.safeParse(_e,e);if(!t.success)return null;const{schemaVersion:i,...s}=t.output;if("routed"===s.journeyStage)try{Y(s.sequence)}catch{return null}return s}function ze(e){if(("participating"===e.phase||"settling"===e.phase)&&"selection"in e){const{selection:t,...i}=e;return i}return e}function He(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?Q(e):e.sequence.checkout}function Ue(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function Be(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{de as ExperienceJourney,ae as isSelfContradictingDistribution};
|
|
@@ -6,6 +6,8 @@ export declare class SnapshotInvariantError extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
export declare function isGateState(state: JourneySnapshot): state is GatedSnapshot;
|
|
8
8
|
export declare function isRoutedState(state: JourneySnapshot): state is RoutedSnapshot;
|
|
9
|
+
/** Identity may change only before participation or an outstanding payment action. */
|
|
10
|
+
export declare function isPreEntryJourney(state: JourneySnapshot | null | undefined): boolean;
|
|
9
11
|
export declare function getGates(state: JourneySnapshot): RoutingGate[];
|
|
10
12
|
export declare function getSequenceId(state: JourneySnapshot): string | undefined;
|
|
11
13
|
export declare function getDistribution(state: JourneySnapshot): DistributionSummary | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isUnresolvedSettlementConsumer as e}from"./settlement.js";class n extends Error{constructor(e,n){super(`SnapshotInvariantError: phase '${e}' requires ${n}`),this.name="SnapshotInvariantError"}}function t(e){return"gated"===e.journeyStage}function i(e){return"routed"===e.journeyStage}function r(e){return t(e)?e.gates:[]}function
|
|
1
|
+
import{isUnresolvedSettlementConsumer as e}from"./settlement.js";class n extends Error{constructor(e,n){super(`SnapshotInvariantError: phase '${e}' requires ${n}`),this.name="SnapshotInvariantError"}}function t(e){return"gated"===e.journeyStage}function i(e){return"routed"===e.journeyStage}function r(e){if("gated"===e?.journeyStage)return!0;if("routed"!==e?.journeyStage)return!1;if("waitlisted"===e.waitlist?.status)return!1;const n=e.sequence;return!("scheduled"!==n.phase&&"enterable"!==n.phase||"pendingAction"in n&&n.pendingAction)}function s(e){return t(e)?e.gates:[]}function u(e){return i(e)?e.sequenceId:void 0}function o(e){if(!i(e))return;const n=e.sequence;return"distribution"in n?n.distribution:void 0}function c(e){return i(e)?e.waitlist:void 0}function a(e){if(!i(e))return;const n=e.sequence;return"mechanism"in n&&"consumer"in n?I(n.mechanism,n.consumer):void 0}function d(n){if(!i(n))return;if("participating"!==n.sequence.phase&&"settling"!==n.sequence.phase)return;const t=n.sequence;if("settling"===t.phase&&!e(I(t.mechanism,t.consumer),t.mechanism,t.distribution.id))return;const r=t.consumer,s=A(r.distributionId)??A(r.id)??o(n)?.id;return s?{id:s,type:t.mechanism}:void 0}function h(e){return f(e)?.token}function f(e){return i(e)&&"granted"===e.sequence.phase?e.sequence.grant:void 0}function w(e){const n=f(e);if(n?.expiresAt)return new Date(n.expiresAt).getTime()}function m(e){if(i(e)&&"ended"===e.sequence.phase)return"checkout"in e.sequence?e.sequence.checkout:void 0}function g(e){return i(e)&&"ended"===e.sequence.phase?e.sequence.outcome:void 0}function l(e){return t(e)?e.selected:void 0}function p(e){return i(e)?e.offers:[]}function q(e){return i(e)?e.sequence.phase:"unavailable"}function v(e){return i(e)?e.sequence:void 0}function b(e){return{revision:1,updatedAt:Date.now(),journeyStage:"ready",experienceId:e,events:[],lastSeenEventId:void 0}}function y(e){const t=function(e){return{...e,revision:e.revision+1,updatedAt:Date.now()}}(e.base);switch(e.journeyStage){case"ready":return{...t,journeyStage:"ready"};case"routing":return{...t,journeyStage:"routing"};case"gated":return{...t,journeyStage:"gated",gates:e.gates??[],selected:e.selected};case"routed":{if(!e.sequenceId)throw new n("routed","sequenceId");if(!e.sequence)throw new n("routed","sequence");const i=e.sequence;return S(i),{...t,journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers??[],sequence:i,...e.waitlist?{waitlist:e.waitlist}:{},sequenceRef:e.sequenceRef}}}throw new n(String(e.journeyStage),"known journeyStage")}function S(e){const t=e;switch(e.phase){case"unavailable":default:return e;case"scheduled":if(!("mechanism"in t))throw new n("scheduled","mechanism");if(!("distribution"in t))throw new n("scheduled","distribution");if("consumer"in t)throw new n("scheduled","no consumer");if("waitlist"in t)throw new n("scheduled","no waitlist attachment");return e;case"enterable":case"participating":if(!("mechanism"in t))throw new n(e.phase,"mechanism");if(!("distribution"in t))throw new n(e.phase,"distribution");if(!("consumer"in t))throw new n(e.phase,"consumer");if("waitlist"in t)throw new n(e.phase,"no waitlist attachment");return e;case"settling":if(!("mechanism"in t))throw new n("settling","mechanism");if(!("distribution"in t))throw new n("settling","distribution");if(!("consumer"in t))throw new n("settling","consumer");return e;case"granted":if(!("mechanism"in t))throw new n("granted","mechanism");if(!("distribution"in t))throw new n("granted","distribution");if(!("consumer"in t))throw new n("granted","consumer");if(!("grant"in t))throw new n("granted","grant");if(!("settlement"in t))throw new n("granted","settlement");return e;case"ended":if(!("mechanism"in t))throw new n("ended","mechanism");if(!("distribution"in t))throw new n("ended","distribution");if(!("consumer"in t))throw new n("ended","consumer");if(!e.outcome)throw new n("ended","outcome");return e}}function j(e){try{return S(e.sequence)}catch{return{phase:"unavailable"}}}function I(e,n){return{...n,mechanism:e}}function A(e){return"string"==typeof e&&e.length>0?e:void 0}export{n as SnapshotInvariantError,y as buildSnapshot,b as createInitialSnapshot,h as getAdmissionGrant,w as getAdmissionGrantExpiresAt,m as getCheckout,a as getConsumer,o as getDistribution,s as getGates,f as getGrant,p as getOffers,g as getOutcome,d as getParticipation,l as getSelected,u as getSequenceId,q as getSequencePhase,v as getSequenceState,c as getWaitlistAttachment,t as isGateState,r as isPreEntryJourney,i as isRoutedState,j as normalizeSequenceState,S as validateSequenceState,I as withMechanism};
|
|
@@ -249,10 +249,11 @@ export type ServiceJourneyStage = ServiceJourneyState["journeyStage"];
|
|
|
249
249
|
* satisfied.
|
|
250
250
|
*/
|
|
251
251
|
export type RoutingGate =
|
|
252
|
-
/** The
|
|
252
|
+
/** The minimum identity needed to continue; a guest satisfies `session`. */
|
|
253
253
|
{
|
|
254
254
|
type: "authentication";
|
|
255
255
|
status: "required";
|
|
256
|
+
requirement: "session" | "authenticated";
|
|
256
257
|
}
|
|
257
258
|
/** The route requires an access code or rejected the submitted one. */
|
|
258
259
|
| {
|
package/dist/internals.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { DistributionMonitor, MonitorUpdate } from './experiences/distribut
|
|
|
9
9
|
export { createDomBridge } from './experiences/dom-bridge';
|
|
10
10
|
export { describeJourneyState, selectJourneyDisplay } from './experiences/journey-display';
|
|
11
11
|
export type { JourneyDisplaySelection, JourneyStateDescription } from './experiences/journey-display';
|
|
12
|
-
export { SnapshotInvariantError, getAdmissionGrant, getAdmissionGrantExpiresAt, getDistribution, getGates, getOffers, getParticipation, getSelected, getSequenceId, getSequencePhase, getSequenceState, isGateState, isRoutedState, } from './experiences/journey.machine';
|
|
12
|
+
export { SnapshotInvariantError, getAdmissionGrant, getAdmissionGrantExpiresAt, getDistribution, getGates, getOffers, getParticipation, getSelected, getSequenceId, getSequencePhase, getSequenceState, isGateState, isPreEntryJourney, isRoutedState, } from './experiences/journey.machine';
|
|
13
13
|
export type { AppointmentDisplayState, AuctionDisplayState, DistributionDisplayState, DrawDisplayState, QueueDisplayState, TimedReleaseDisplayState, } from './experiences/public';
|
|
14
14
|
export { getEventBus } from './state/events';
|
|
15
15
|
export { generateFingerprint, generateFingerprintSync } from './utils/fingerprint.module';
|
package/dist/internals.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createDomBridge as e}from"./experiences/dom-bridge.js";import{describeJourneyState as r,selectJourneyDisplay as o}from"./experiences/journey-display.js";import{SnapshotInvariantError as i,getAdmissionGrant as m,getAdmissionGrantExpiresAt as s,getDistribution as p,getGates as t,getOffers as n,getParticipation as j,getSelected as f,getSequenceId as c,getSequencePhase as d,getSequenceState as u,isGateState as x,isRoutedState as
|
|
1
|
+
import{createDomBridge as e}from"./experiences/dom-bridge.js";import{describeJourneyState as r,selectJourneyDisplay as o}from"./experiences/journey-display.js";import{SnapshotInvariantError as i,getAdmissionGrant as m,getAdmissionGrantExpiresAt as s,getDistribution as p,getGates as t,getOffers as n,getParticipation as j,getSelected as f,getSequenceId as c,getSequencePhase as d,getSequenceState as u,isGateState as x,isPreEntryJourney as a,isRoutedState as l}from"./experiences/journey.machine.js";import{getEventBus as y}from"./state/events.js";import{generateFingerprint as g,generateFingerprintSync as b}from"./utils/fingerprint.module.js";export{i as SnapshotInvariantError,e as createDomBridge,r as describeJourneyState,g as generateFingerprint,b as generateFingerprintSync,m as getAdmissionGrant,s as getAdmissionGrantExpiresAt,p as getDistribution,y as getEventBus,t as getGates,n as getOffers,j as getParticipation,f as getSelected,c as getSequenceId,d as getSequencePhase,u as getSequenceState,x as isGateState,a as isPreEntryJourney,l as isRoutedState,o as selectJourneyDisplay};
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.
|
|
1
|
+
const o="0.15.0";export{o as version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanfare-io/fanfare-sdk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"uuid": "^14.0.0",
|
|
112
112
|
"wretch": "^2.11.0",
|
|
113
113
|
"zustand": "^5.0.6",
|
|
114
|
-
"@fanfare-io/fanfare-sdk-contracts": "0.
|
|
114
|
+
"@fanfare-io/fanfare-sdk-contracts": "0.15.0"
|
|
115
115
|
},
|
|
116
116
|
"peerDependencies": {
|
|
117
117
|
"valibot": "^1.1.0"
|
|
@@ -128,9 +128,9 @@
|
|
|
128
128
|
"@types/react": "^19.1.12",
|
|
129
129
|
"@types/react-dom": "^19.1.9",
|
|
130
130
|
"@types/uuid": "^10.0.0",
|
|
131
|
-
"@vitest/browser": "^
|
|
132
|
-
"@vitest/browser-playwright": "^
|
|
133
|
-
"@vitest/ui": "^
|
|
131
|
+
"@vitest/browser": "^5.0.0",
|
|
132
|
+
"@vitest/browser-playwright": "^5.0.0",
|
|
133
|
+
"@vitest/ui": "^5.0.0",
|
|
134
134
|
"jsdom": "^26.1.0",
|
|
135
135
|
"playwright": "1.58.2",
|
|
136
136
|
"prettier": "^3.5.3",
|
|
@@ -149,8 +149,8 @@
|
|
|
149
149
|
"vite-bundle-visualizer": "^1.0.0",
|
|
150
150
|
"vite-plugin-dts": "^4.5.4",
|
|
151
151
|
"vite-tsconfig-paths": "^5.1.4",
|
|
152
|
-
"vitest": "^
|
|
153
|
-
"@fanfare-io/shared-models": "0.
|
|
152
|
+
"vitest": "^5.0.0",
|
|
153
|
+
"@fanfare-io/shared-models": "0.15.0"
|
|
154
154
|
},
|
|
155
155
|
"sideEffects": false,
|
|
156
156
|
"keywords": [
|