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