@fanfare-io/fanfare-sdk-core 0.16.0 → 0.18.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.module.js +1 -1
- package/dist/auctions/auction.driver.js +1 -1
- package/dist/auctions/auction.sequence.d.ts +15 -1
- package/dist/auth/types.d.ts +5 -0
- package/dist/core/error-display.js +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/core/parse-response.js +1 -1
- package/dist/core/utils.d.ts +4 -5
- package/dist/core/utils.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 +18 -1
- package/dist/draws/types.d.ts +6 -1
- package/dist/experiences/journey.js +1 -1
- package/dist/experiences/sequence-driver.js +1 -1
- package/dist/queues/queue.driver.js +1 -1
- package/dist/queues/queue.sequence.d.ts +16 -1
- package/dist/state/store.js +1 -1
- package/dist/sync/broadcast-transport.js +1 -1
- package/dist/sync/localstorage-transport.js +1 -1
- package/dist/sync/protocol-transport.js +1 -1
- package/dist/sync/protocol.js +1 -1
- package/dist/sync/transport.js +1 -1
- package/dist/timed-releases/timed-release.driver.js +1 -1
- package/dist/timed-releases/timed-release.sequence.d.ts +12 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -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 r}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as s}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as a}from"../state/events.js";function l(t,o,e){const n=i(t,o,{endpoint:e},{throwOnFailure:!0});if(!n.ok)throw n.error;return n.value}const d=t.object({id:t.string(),startTime:t.string(),endTime:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),timezone:t.nullish(t.string()),capacity:t.number(),available:t.number()}),c=t.object({slots:t.array(d)}),m=t.object({id:t.string(),type:t.literal("appointment"),openAt:t.nullish(t.string()),closeAt:t.nullish(t.string()),slotDurationMinutes:t.nullish(t.number()),timeZone:t.nullish(t.string()),locationId:t.nullish(t.string()),totalSlots:t.nullish(t.number()),availableSlots:t.nullish(t.number())}),p=t.string(),h=t.picklist(["consumer","admin","killed"]),u=t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.string(),endTime:t.string(),confirmationCode:t.string(),status:p,cancelledCause:t.nullish(h),createdAt:t.string()}),g=t.object({booking:t.nullish(t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.nullish(t.string()),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.nullish(t.string()),endTime:t.nullish(t.string()),confirmationCode:t.nullish(t.string()),status:p,cancelledCause:t.nullish(h)}))}),I=t.object({booking:u,bookingId:t.string(),previousSlotId:t.string(),newSlotId:t.string(),confirmationCode:t.string(),status:t.string(),cancelledCause:t.nullish(h)});function b(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId,locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime,endTime:t.endTime,confirmationCode:t.confirmationCode,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}function k(t){return{id:t.id,startTime:t.startTime,endTime:t.endTime,locationId:t.locationId??void 0,locationName:t.locationName??void 0,timezone:t.timezone??void 0,capacity:t.capacity,available:t.available}}class w{constructor(t,o){this.logger=n(),this.monitorRuntime=new s,this.pollSchedulers=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.observedBooking=/* @__PURE__ */new Map,this.destroyed=!1,this.http=t,this.pollingIntervalMs=o?.pollingIntervalMs??3e4}async get(t,n){try{const o=await this.http.get(`/appointments/${t}`,n),e=l(m,o,`/appointments/${t}`);return{id:e.id,type:"appointment",openAt:e.openAt??void 0,closeAt:e.closeAt??void 0,slotDurationMinutes:e.slotDurationMinutes??void 0,bookingWindowOpenAt:e.openAt??void 0,bookingWindowCloseAt:e.closeAt??void 0,timezone:e.timeZone??void 0,locationId:e.locationId??void 0,totalSlots:e.totalSlots??void 0,availableSlots:e.availableSlots??void 0}}catch(i){if(this.logger.error("Failed to get appointment",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get appointment",{error:i})}}async getSlots(t,n,i){try{const o=new URLSearchParams({startDate:n.startDate,endDate:n.endDate}),e=await this.http.get(`/appointments/${t}/slots?${o.toString()}`,i);return l(c,e,`/appointments/${t}/slots`).slots.map(k)}catch(r){if(this.logger.error("Failed to get appointment slots",{appointmentId:t,error:r}),o(r))throw r;throw e.networkError("Failed to get appointment slots",{error:r})}}async getMe(t){try{const o=await this.http.get(`/appointments/${t}/me`),e=l(g,o,`/appointments/${t}/me`);return e.booking?function(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId??"",locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime??"",endTime:t.endTime??"",confirmationCode:t.confirmationCode??void 0,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}(e.booking):null}catch(i){if(this.logger.error("Failed to get current booking",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get current booking",{error:i})}}async book(t,n,i,r,s){try{const o=await this.http.post(`/appointments/${t}/book`,{slotId:n,locationId:i??null},r),e=b(l(u,o,`/appointments/${t}/book`));return a().emit("experience:distribution-entered",{distributionType:"appointment",distributionId:t,...s}),e}catch(d){if(this.logger.error("Failed to book appointment",{appointmentId:t,slotId:n,locationId:i,error:d}),o(d))throw d;throw e.networkError("Failed to book appointment",{error:d})}}async cancel(t,n,i,r){try{await this.http.post(`/appointments/${t}/cancel`,{slotId:n,locationId:i??null},r)}catch(s){if(this.logger.error("Failed to cancel appointment",{appointmentId:t,slotId:n,locationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to cancel appointment",{error:s})}}async reschedule(t,n,i,r){try{const o=await this.http.post(`/appointments/${t}/reschedule`,{newSlotId:n,newLocationId:i??null},r),e=b(l(I,o,`/appointments/${t}/reschedule`).booking);if(this.isMonitoring(t)){const o=this.monitorRuntime.getDisplayAtom(t);o&&o.set({type:"appointment",booking:e,msUntilStart:Math.max(0,Date.parse(e.startTime)-Date.now())}),this.startMonitor(t,o?{displayAtom:o}:void 0,void 0,this.pollSchedulers.get(t)?.onDegradedChange,e)}return e}catch(s){if(this.logger.error("Failed to reschedule appointment",{appointmentId:t,newSlotId:n,newLocationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to reschedule appointment",{error:s})}}startMonitoring(t,o,e,n){this.startMonitor(t,o,e,n)}startMonitor(t,o,e,n,i){if(this.destroyed)return;this.monitorRuntime.start(t,o,e),this.stopPolling(t);const s=this.monitorRuntime.getDisplayAtom(t)?.get();this.observedBooking.set(t,"appointment"===s?.type&&void 0!==s.booking);const a=(this.monitorGenerations.get(t)??0)+1;this.monitorGenerations.set(t,a);const l=r({baseMs:this.pollingIntervalMs,suppressBackoff:()=>!1,onDegradedChange:n,shouldContinue:()=>!this.destroyed&&this.monitorGenerations.get(t)===a&&this.pollSchedulers.has(t),run:async()=>{if(!this.destroyed&&this.monitorGenerations.get(t)===a)try{const o=i??await this.getMe(t);if(i=void 0,this.destroyed||this.monitorGenerations.get(t)!==a)return;const e=this.monitorRuntime.getDisplayAtom(t);if(!o)return void(this.observedBooking.get(t)&&(this.monitorRuntime.notify(t,{type:"ended",outcome:{type:"closed"}}),this.stopMonitoring(t)));if(this.observedBooking.set(t,!0),"cancelled"===o.consumerStatus||"completed"===o.consumerStatus)return this.monitorRuntime.notify(t,{type:"ended",outcome:{type:o.consumerStatus,..."cancelled"===o.consumerStatus&&"killed"===o.cancelledCause?{reason:"killed"}:{}}}),void this.stopMonitoring(t);const n=Math.max(0,Date.parse(o.startTime)-Date.now());if(!e)return;e.set({type:"appointment",booking:{slotId:o.slotId,locationId:o.locationId,locationName:o.locationName,startTime:o.startTime,endTime:o.endTime,confirmationCode:o.confirmationCode,consumerStatus:o.consumerStatus,cancelledCause:o.cancelledCause},msUntilStart:n})}catch(o){throw this.logger.error("Appointment polling tick failed",{id:t,error:o}),o}}});this.pollSchedulers.set(t,{scheduler:l,onDegradedChange:n}),l.start()}stopMonitoring(t){this.stopPolling(t),this.monitorRuntime.stop(t),this.observedBooking.delete(t),this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}bumpMonitorGeneration(t){this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}destroy(){this.destroyed=!0,this.monitorGenerations.clear(),this.observedBooking.clear();for(const t of Array.from(this.pollSchedulers.keys()))this.stopPolling(t);this.monitorRuntime.clear()}stopPolling(t){const o=this.pollSchedulers.get(t);void 0!==o&&(o.scheduler.stop(),this.pollSchedulers.delete(t))}}export{w as AppointmentManagementModule};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AuctionDistributionResults as t}from"@fanfare-io/fanfare-sdk-contracts";import{createError as i}from"../core/errors.js";import{buildGrantPaymentAffordance as
|
|
1
|
+
import{AuctionDistributionResults as t}from"@fanfare-io/fanfare-sdk-contracts";import{createError as i}from"../core/errors.js";import{selectionModeFields as e,buildGrantPaymentAffordance as n}from"../experiences/sequence-driver.js";import{isGrantPendingWin as s}from"./auction-status.js";import{isUnresolvedAuctionConsumer as o}from"./auction.sequence.js";function a(t){return!t||("lost"===t.type||"closed"===t.type)&&!t.reason}function r(t){return new d(t)}function c(n){const s=function(e){const{distribution:n,consumer:s,grant:r,settlement:c,selection:u,outcome:d}=e;if(!n)return{phase:"unavailable"};const l=function(t){if("auction"!==t.type)throw i.validationError("Auction driver received a non-auction distribution");return t}(n),h=s&&"auction"===s.mechanism?function(t){const{mechanism:i,...e}=t;return e}(s):p(n);if(r)return"completed"===d?.type?m(l,h,d):r.expiresAt&&Date.now()>new Date(r.expiresAt).getTime()?"open"===l.lifecycle?{phase:"enterable",mechanism:"auction",distribution:l,consumer:p(l)}:m(l,h,{type:"expired"}):c&&u?{phase:"granted",mechanism:"auction",distribution:l,consumer:h,grant:r,settlement:c,selection:u}:{phase:"unavailable"};if("closed"===l.lifecycle){if("not_bid"===h.status&&l.result===t.FLOOR_UNSOLD&&a(d))return m(l,h,{type:"lost",reason:t.FLOOR_UNSOLD});if("auction"===s?.mechanism&&!d)return m(l,h,{type:"lost"})}const b=d??function(t){return"lost"===t.status?{type:"lost"}:void 0}(h);if(b)return m(l,h,b);if("settling"===l.lifecycle)return{phase:"settling",mechanism:"auction",distribution:l,consumer:h};if(o(h))return{phase:"participating",mechanism:"auction",distribution:l,consumer:h};if("scheduled"===l.lifecycle)return{phase:"scheduled",mechanism:"auction",distribution:l};if("open"===l.lifecycle)return{phase:"enterable",mechanism:"auction",distribution:l,consumer:h};return m(l,h,{type:"closed"})}(n);return"unavailable"===s.phase?s:{...s,...e(n)}}function u(t,i,e){return t&&"auction"===t.mechanism&&i?"won"===i.type?{...t,status:"won"}:!e?.finalize&&"not_bid"===t.status&&a(i)?t:{...t,status:"lost"}:t}class d{constructor(t){this.deps=t,this.mechanism="auction"}project(t){return c(t)}buildView(t,s){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const o=function(t){if(!("mechanism"in t)||"auction"!==t.mechanism)throw i.validationError("Auction driver received a non-auction sequence");return t}(t);switch(o.phase){case"scheduled":return{phase:"scheduled",mechanism:"auction",...e(o),distribution:o.distribution,startsAt:o.distribution.startsAt??o.distribution.opensAt};case"enterable":return{phase:"enterable",mechanism:"auction",...e(o),distribution:o.distribution,consumer:o.consumer,display$:s.displayAtom,loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)};case"participating":return{phase:"participating",mechanism:"auction",...e(o),consumer:o.consumer,display$:s.displayAtom,loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)};case"settling":return{phase:"settling",mechanism:"auction",...e(o),distribution:o.distribution,consumer:o.consumer};case"granted":{const t={phase:"granted",mechanism:"auction",...e(o),distribution:o.distribution,consumer:o.consumer,grant:o.grant},{settlement:i}=o,a=o.selection;if("awaiting_assignment"===a.status)return{...t,settlement:i,selection:a};if("required"===a.status)return{...t,settlement:i,selection:a,select:s.select,loadSelectionOptions:s.loadSelectionOptions};if("internal"===i.processor&&"at_end"===i.mode){const e=n(i,o.pendingAction,s);return e?{...t,settlement:i,selection:a,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim(),payment:e}:{...t,settlement:i,selection:a,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}}return"external"===i.processor?{...t,settlement:i,selection:a,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}:{phase:"unavailable",reason:"checkout_misconfigured"}}case"ended":return{phase:"ended",mechanism:"auction",...e(o),consumer:o.consumer,outcome:o.outcome}}}startMonitoring(t){this.activeMonitorId=t.participation.id,this.deps.auctions.startMonitoring(t.participation.id,{displayAtom:t.displayAtom},t.onUpdate,t.onDegradedChange)}stopMonitoring(){this.activeMonitorId&&(this.deps.auctions.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async bid(t){await this.deps.runSerializedOperation(async()=>{const i=this.currentAuctionSequenceForBid(),e=i.consumer.distributionId??i.consumer.id??i.distribution.id,n=i.consumer.selection,o="auction"===i.distribution.details?.type?i.distribution.details:void 0;l(t,o?.auctionType);const a=await this.deps.auctions.bid(e,t),r=this.deps.getCurrentSequence(),u=r&&("enterable"===r.phase||"participating"===r.phase||"settling"===r.phase)&&"mechanism"in r&&"auction"===r.mechanism&&r.distribution.id===e;if("won"===a.status||"lost"===a.status)return void(u&&("won"===a.status&&a.admissionGrant?this.deps.applyMonitorUpdate({type:"granted",token:a.admissionGrant}):"lost"===a.status?this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"lost"}}):s(a)||this.deps.applyMonitorUpdate({type:"resync"})));if(!u)return;const d=function(t){switch(t){case"not_bid":case"winning":case"outbid":return t;case"won":case"lost":return}}(a.status);void 0!==d&&this.deps.commit(c({distribution:i.distribution,consumer:{..."participating"===i.phase?i.consumer:{},mechanism:"auction",status:d,id:e,distributionId:e,...n?{selection:n}:{},currentBid:a.amount,highestBid:a.highestBid}}))})}currentAuctionSequenceForBid(){const t=this.deps.getCurrentSequence();if(!t||"enterable"!==t.phase&&"participating"!==t.phase||!("mechanism"in t)||"auction"!==t.mechanism)throw i.validationError("No auction is available to bid on");return t}}function l(t,e){if("dutch"!==e){if(void 0===t.amount)throw i.validationError("A bid amount is required for an English auction")}else if(void 0!==t.amount)throw i.validationError("A Dutch auction clinches at the current price; omit the bid amount")}function p(t){return{status:"not_bid",distributionId:t.id}}function m(t,i,e){return{phase:"ended",mechanism:"auction",distribution:t,consumer:i,outcome:{...e,distributionType:e.distributionType??t.type,distributionId:e.distributionId??t.id,at:e.at??/* @__PURE__ */(new Date).toISOString()}}}export{l as assertBidInputMatchesMechanism,r as createAuctionDriver,a as isUpgradeableGenericLoss,c as projectAuctionSequence,u as withAuctionTerminalConsumer};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AdmissionGrant, AuctionConsumerStatus, GrantedSettlement, SequenceOutcome } from '@fanfare-io/fanfare-sdk-contracts/consumer-me';
|
|
2
|
-
import { GrantedSelection } from '@fanfare-io/fanfare-sdk-contracts/selection';
|
|
2
|
+
import { GrantedSelection, SelectionMode } from '@fanfare-io/fanfare-sdk-contracts/selection';
|
|
3
3
|
import { ReadableAtom } from 'nanostores';
|
|
4
4
|
import { AuctionDisplayState } from '../experiences/distribution-monitor.types';
|
|
5
5
|
import { DistributionSummary } from '../experiences/journey.types';
|
|
@@ -52,6 +52,7 @@ export type AuctionSequence = AuctionScheduled | AuctionEnterable | AuctionParti
|
|
|
52
52
|
export interface AuctionScheduled {
|
|
53
53
|
/** Mechanism discriminator for auction narrowing. */
|
|
54
54
|
mechanism: "auction";
|
|
55
|
+
selectionMode?: SelectionMode;
|
|
55
56
|
/** Phase discriminator; scheduled auctions cannot be bid on yet. */
|
|
56
57
|
phase: "scheduled";
|
|
57
58
|
/** Auction distribution facts from the journey projector. */
|
|
@@ -65,6 +66,7 @@ export interface AuctionScheduled {
|
|
|
65
66
|
export interface AuctionEnterable {
|
|
66
67
|
/** Mechanism discriminator for auction narrowing. */
|
|
67
68
|
mechanism: "auction";
|
|
69
|
+
selectionMode?: SelectionMode;
|
|
68
70
|
/** Phase discriminator; `bid()` is legal on the matching view. */
|
|
69
71
|
phase: "enterable";
|
|
70
72
|
/** Open auction distribution facts from the journey projector. */
|
|
@@ -80,6 +82,7 @@ export interface AuctionEnterable {
|
|
|
80
82
|
export interface AuctionParticipating {
|
|
81
83
|
/** Mechanism discriminator for auction narrowing. */
|
|
82
84
|
mechanism: "auction";
|
|
85
|
+
selectionMode?: SelectionMode;
|
|
83
86
|
/** Phase discriminator; auction display and bidding are live. */
|
|
84
87
|
phase: "participating";
|
|
85
88
|
/** Auction distribution facts from the journey projector. */
|
|
@@ -95,6 +98,7 @@ export interface AuctionParticipating {
|
|
|
95
98
|
export interface AuctionSettling {
|
|
96
99
|
/** Mechanism discriminator for auction narrowing. */
|
|
97
100
|
mechanism: "auction";
|
|
101
|
+
selectionMode?: SelectionMode;
|
|
98
102
|
/** Phase discriminator; no consumer action is legal while settling. */
|
|
99
103
|
phase: "settling";
|
|
100
104
|
/** Settling auction distribution facts from the journey projector. */
|
|
@@ -110,6 +114,7 @@ export interface AuctionSettling {
|
|
|
110
114
|
export interface AuctionGranted {
|
|
111
115
|
/** Mechanism discriminator for auction narrowing. */
|
|
112
116
|
mechanism: "auction";
|
|
117
|
+
selectionMode?: SelectionMode;
|
|
113
118
|
/** Phase discriminator; `claim()` is legal on the matching view. */
|
|
114
119
|
phase: "granted";
|
|
115
120
|
/** Auction distribution facts from the journey projector. */
|
|
@@ -135,6 +140,7 @@ export interface AuctionGranted {
|
|
|
135
140
|
export interface AuctionEnded {
|
|
136
141
|
/** Mechanism discriminator for auction narrowing. */
|
|
137
142
|
mechanism: "auction";
|
|
143
|
+
selectionMode?: SelectionMode;
|
|
138
144
|
/** Phase discriminator; no auction actions are legal after this phase. */
|
|
139
145
|
phase: "ended";
|
|
140
146
|
/** Auction distribution facts from the journey projector. */
|
|
@@ -162,6 +168,7 @@ export interface AuctionScheduledView {
|
|
|
162
168
|
phase: "scheduled";
|
|
163
169
|
/** Mechanism discriminator for auction rendering. */
|
|
164
170
|
mechanism: "auction";
|
|
171
|
+
selectionMode?: SelectionMode;
|
|
165
172
|
/** Auction distribution facts from the journey projector. */
|
|
166
173
|
distribution: DistributionSummary & {
|
|
167
174
|
type: "auction";
|
|
@@ -177,6 +184,7 @@ export interface AuctionEnterableView {
|
|
|
177
184
|
phase: "enterable";
|
|
178
185
|
/** Mechanism discriminator for auction rendering. */
|
|
179
186
|
mechanism: "auction";
|
|
187
|
+
selectionMode?: SelectionMode;
|
|
180
188
|
/** Open auction distribution facts from the journey projector. */
|
|
181
189
|
distribution: DistributionSummary & {
|
|
182
190
|
type: "auction";
|
|
@@ -208,6 +216,7 @@ export interface AuctionParticipatingView {
|
|
|
208
216
|
phase: "participating";
|
|
209
217
|
/** Mechanism discriminator for auction rendering. */
|
|
210
218
|
mechanism: "auction";
|
|
219
|
+
selectionMode?: SelectionMode;
|
|
211
220
|
/** Consumer auction participation facts. */
|
|
212
221
|
consumer: AuctionSequenceConsumerState;
|
|
213
222
|
/** Live auction display facts: the price model, currency, and close time. */
|
|
@@ -234,6 +243,7 @@ export interface AuctionSettlingView {
|
|
|
234
243
|
phase: "settling";
|
|
235
244
|
/** Mechanism discriminator for auction rendering. */
|
|
236
245
|
mechanism: "auction";
|
|
246
|
+
selectionMode?: SelectionMode;
|
|
237
247
|
/** Settling auction distribution facts from the journey projector. */
|
|
238
248
|
distribution: DistributionSummary & {
|
|
239
249
|
type: "auction";
|
|
@@ -249,6 +259,7 @@ interface AuctionGrantedViewBase {
|
|
|
249
259
|
phase: "granted";
|
|
250
260
|
/** Mechanism discriminator for auction rendering. */
|
|
251
261
|
mechanism: "auction";
|
|
262
|
+
selectionMode?: SelectionMode;
|
|
252
263
|
/** Auction distribution facts from the journey projector. */
|
|
253
264
|
distribution: DistributionSummary & {
|
|
254
265
|
type: "auction";
|
|
@@ -317,6 +328,7 @@ export interface AuctionEndedView {
|
|
|
317
328
|
phase: "ended";
|
|
318
329
|
/** Mechanism discriminator for auction rendering. */
|
|
319
330
|
mechanism: "auction";
|
|
331
|
+
selectionMode?: SelectionMode;
|
|
320
332
|
/** Consumer auction facts at the terminal outcome. */
|
|
321
333
|
consumer: AuctionSequenceConsumerState;
|
|
322
334
|
/** Server-derived terminal outcome for the auction participation. */
|
|
@@ -328,6 +340,7 @@ export interface AuctionGrantedAwaitingAssignmentView {
|
|
|
328
340
|
phase: "granted";
|
|
329
341
|
/** Mechanism discriminator. */
|
|
330
342
|
mechanism: "auction";
|
|
343
|
+
selectionMode?: SelectionMode;
|
|
331
344
|
/** Distribution associated with this grant. */
|
|
332
345
|
distribution: DistributionSummary & {
|
|
333
346
|
type: "auction";
|
|
@@ -349,6 +362,7 @@ export interface AuctionGrantedSelectionRequiredView {
|
|
|
349
362
|
phase: "granted";
|
|
350
363
|
/** Mechanism discriminator. */
|
|
351
364
|
mechanism: "auction";
|
|
365
|
+
selectionMode?: SelectionMode;
|
|
352
366
|
/** Distribution associated with this grant. */
|
|
353
367
|
distribution: DistributionSummary & {
|
|
354
368
|
type: "auction";
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export interface OtpRequest {
|
|
|
56
56
|
phone?: string;
|
|
57
57
|
defaultCountry?: string;
|
|
58
58
|
name?: string;
|
|
59
|
+
/**
|
|
60
|
+
* The experience the consumer is signing in to. The sign-in email then carries that experience's
|
|
61
|
+
* branding, matching the rest of its emails; without it the email uses the organization's.
|
|
62
|
+
*/
|
|
63
|
+
experienceId?: string;
|
|
59
64
|
}
|
|
60
65
|
export interface OtpVerify {
|
|
61
66
|
email?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{classify as
|
|
1
|
+
import{classify as e}from"./error-disposition.js";import{FanfareError as r,ErrorCodes as o}from"./errors.js";const t={[o.OTP_INVALID]:"auth.invalidCode",[o.OTP_EXPIRED]:"auth.codeExpired",[o.INVALID_SESSION]:"common.sessionExpired",[o.INVALID_REFRESH_TOKEN]:"common.sessionExpired",[o.SERVICE_UNAVAILABLE]:"error.overloadedGeneric",[o.TIMEOUT]:"error.overloadedGeneric",[o.NETWORK_ERROR]:"error.overloadedGeneric",[o.FEATURE_DISABLED]:"error.featureDisabled",[o.NO_ACCESS]:"widget.noAccess.message",[o.FORBIDDEN]:"error.forbidden",[o.DISTRIBUTION_NOT_OPEN]:"error.notOpen",[o.DISTRIBUTION_DEACTIVATED]:"error.deactivated",[o.ALREADY_BOOKED]:"appointment.error.alreadyBooked",[o.ACTIVE_BOOKING_EXISTS]:"appointment.error.activeBookingExists",[o.SLOT_NOT_AVAILABLE]:"appointment.error.slotNotAvailable",[o.SLOT_EXPIRED]:"appointment.error.slotExpired",[o.SLOT_OUT_OF_WINDOW]:"appointment.error.slotOutOfWindow",[o.CANNOT_CANCEL_ONGOING_OR_PAST]:"appointment.error.cannotCancel",[o.NEW_SLOT_FULL]:"appointment.error.newSlotFull",[o.BOOKING_COMPLETED]:"appointment.error.bookingCompleted",[o.NO_EXISTING_BOOKING]:"appointment.error.noExistingBooking",[o.INVALID_SLOT]:"appointment.error.invalidSlot",[o.SELECTION_REQUIRED]:"selection.error.required",[o.SELECTION_INVALID_CHOICE]:"selection.error.invalidChoice",[o.SELECTION_NOT_REQUIRED]:"selection.error.notRequired",[o.SELECTION_LOCKED]:"selection.locked.final",[o.SELECTION_MISMATCH]:"selection.error.mismatch",[o.SELECTION_UNAVAILABLE]:"selection.error.unavailable",[o.SELECTION_LIMIT_REACHED]:"selection.error.limitReached",[o.SELECTION_ASSIGNMENT_PENDING]:"selection.error.assignmentPending",[o.ADMISSION_SELECTION_UNRESOLVED]:"selection.error.admissionUnresolved",[o.SOLD_OUT]:"selection.error.soldOut",[o.STOCK_UNSEEDED]:"selection.error.unseeded"},i=/* @__PURE__ */new Set([o.DISTRIBUTION_DEACTIVATED,o.DISTRIBUTION_CLOSED,o.DISTRIBUTION_ORDER_LIMIT,o.SOLD_OUT,o.CHECKOUT_MISCONFIGURED]);function n(e){return"INLINE"===e||"STEP_RESET"===e?"error.inlineGeneric":"error.description"}function E(r,E){const{disposition:s}=e(r.code,r.status,r.action),I="number"==typeof r.retryAfter&&Number.isFinite(r.retryAfter)?r.retryAfter:void 0,O=function(e,r,i){return e===o.RATE_LIMITED?i?"error.rateLimited":"error.rateLimitedGeneric":e===o.OVERLOADED?i?"error.overloaded":"error.overloadedGeneric":t[e]??n(r)}(r.code,s,void 0!==I);let d=E(O,void 0!==I?{values:{seconds:I}}:void 0);d.includes("{{")&&(d=E(n(s)));const T="PANEL"===s?E("error.title"):void 0;return{code:r.code,title:T,description:d,disposition:s,retryAfter:r.retryAfter,support:r.support,allowRetry:!i.has(r.code)}}function s(e){if(e instanceof r)return e;const t=e instanceof Error?e.message:String(e);return new r(t,o.INTERNAL_ERROR)}function I(e){return{code:e.code,status:e.status,requestId:e.requestId,correlationId:e.correlationId,details:e.details,issues:e.issues}}export{s as ensureFanfareError,I as toDebug,E as toDisplay};
|
package/dist/core/logger.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createError as e}from"./errors.js";import{maskPII as
|
|
1
|
+
import{createError as e}from"./errors.js";import{maskPII as r}from"./utils.js";class o{constructor(e){this.levels={debug:0,info:1,warn:2,error:3},this.config=e}shouldLog(e){return!!this.config.enabled&&this.levels[e]>=this.levels[this.config.level]}format(e,o,i){const t=[`${this.config.prefix?`[${this.config.prefix}] `:""}${/* @__PURE__ */(new Date).toISOString()} [${e.toUpperCase()}] ${o}`];if(void 0!==i){const e=r(i);void 0!==e&&t.push(e)}switch(e){case"debug":case"info":break;case"warn":console.warn(...t);break;case"error":console.error(...t)}}debug(e,r){this.shouldLog("debug")&&this.format("debug",e,r)}info(e,r){this.shouldLog("info")&&this.format("info",e,r)}warn(e,r){this.shouldLog("warn")&&this.format("warn",e,r)}error(e,r){this.shouldLog("error")&&this.format("error",e,r)}setLevel(e){this.config.level=e}setEnabled(e){this.config.enabled=e}}let i=null;function t(e){return i&&!e||(i=new o(e||{enabled:!1,level:"warn",prefix:"Fanfare"})),i}function n(){if(!i)throw e.notInitialized("Logger not initialized. Call createLogger first.");return i}function s(){return i}export{t as createLogger,n as getLogger,s as peekLogger};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"valibot";import{getEventBus as
|
|
1
|
+
import*as e from"valibot";import{getEventBus as t}from"../state/events.js";import{ProtocolError as r}from"./errors.js";import{getLogger as o}from"./logger.js";function s(s,i,p,u={}){const d=e.safeParse(s,i);if(d.success)return{ok:!0,value:d.output};const a=function(e){const t=e[0];if(!t)return"no issues reported";const r=n(t),o=r?` at "${r}"`:"",s=e.length>1?` (and ${e.length-1} more)`:"";return`expected ${t.expected??t.type}${o}${s}`}(d.issues),c=`Response from ${p.endpoint} failed schema validation: ${a}`,m=new r(c,p.endpoint,d.issues,p.requestId);var f;if(o().error("Protocol error: response failed schema validation",{endpoint:p.endpoint,requestId:p.requestId,issues:(f=d.issues,f.map(e=>({path:n(e),expected:e.expected??e.type})))}),t().emit("journey:error",{kind:"protocolError",endpoint:p.endpoint,message:c,requestId:p.requestId,issues:d.issues}),u.throwOnFailure)throw m;return{ok:!1,error:m}}function n(e){const t=e.path?.map(e=>String(e.key))??[];return"never"===e.expected&&t.length>0&&(t[t.length-1]="<unexpected>"),t.join(".")}export{s as parseResponse};
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -46,11 +46,10 @@ export declare function isStorageAvailable(type: "localStorage" | "sessionStorag
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function safeJsonParse<T>(json: string, fallback?: T): T | null;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* Mask sensitive PII data in objects
|
|
49
|
+
* Project arbitrary log data onto what is safe to print: errors reduced to their identity, and
|
|
50
|
+
* string, number and boolean values held under a safe key. A top-level primitive carries no key to
|
|
51
|
+
* judge it by and is dropped, as is every key outside the set — dropped rather than masked, so that
|
|
52
|
+
* neither the value nor its presence can be inferred from the console.
|
|
54
53
|
*/
|
|
55
54
|
export declare function maskPII(data: unknown): unknown;
|
|
56
55
|
/**
|
package/dist/core/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{v7 as
|
|
1
|
+
import{v7 as r}from"uuid";import{ErrorCodes as t}from"./errors.js";function e(){return r()}const n=(r,t=.2,e=Math.random)=>Math.round(r*(1-t+2*e()*t));function o(r,t=/* @__PURE__ */new WeakSet){if(null===r||"object"!=typeof r)return r;if(t.has(r))return r;if(r instanceof Date)return new Date(r.getTime());if(r instanceof Array)return t.add(r),r.map(r=>o(r,t));if(r instanceof Object){t.add(r);const e={};for(const n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=o(r[n],t));return e}return r}function i(){return"undefined"!=typeof window&&void 0!==window.document}function a(r){if(!i())return!1;try{const t=window[r],e="__storage_test__";return t.setItem(e,e),t.removeItem(e),!0}catch{return!1}}const s=new Set("adapterId appointmentId attempt auctionId bytes code correlationId distributionId drawId endpoint expected experienceId id issues journeyId kind locationId organizationId path reason requestId retryAfterMs slotId status statusCode type waitlistId".split(" ")),u=Object.values(t),c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function d(r){const t={error:"|AbortError|DOMException|Error|FanfareError|NoSequenceAvailableError|ProtocolError|RangeError|SequenceFullError|SnapshotInvariantError|SyntaxError|TypeError|ValiError|ValidationError|WaitlistClosedError|".includes(`|${r.name}|`)?r.name:"<unknown>"},{code:e,endpoint:n,status:o,requestId:i,correlationId:a}=r;return"string"==typeof e&&u.includes(e)&&(t.code=e),"string"==typeof n&&(t.endpoint=n),Number.isInteger(o)&&(t.status=o),"string"==typeof i&&c.test(i)&&(t.requestId=i),"string"==typeof a&&c.test(a)&&(t.correlationId=a),t}function f(r){if(r instanceof Error)return d(r);if(Array.isArray(r))return r.map(r=>f(r));if(null!==r&&"object"==typeof r){const t={};for(const[e,n]of Object.entries(r)){if(n instanceof Error){t[e]=d(n);continue}if(!s.has(e))continue;if("string"==typeof n||"number"==typeof n||"boolean"==typeof n){t[e]=n;continue}const r=f(n);void 0!==r&&(t[e]=r)}return t}}export{o as deepClone,e as generateId,i as isBrowser,a as isStorageAvailable,n as jitteredIntervalMs,f as maskPII};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{DrawConsumerStatuses as e}from"@fanfare-io/fanfare-sdk-contracts/consumer-me";import{createError as t}from"../core/errors.js";import{selectionModeFields as i,buildGrantPaymentAffordance as n}from"../experiences/sequence-driver.js";import{resolveCheckoutContract as s,checkoutViewFields as r,isEntryActionRequired as o,canReenter as a,isPreAuthMisconfigured as d}from"../payment/checkout-contract.js";import{isClaimableSettlement as c}from"../payment/settlement.js";import{isUnresolvedDrawConsumer as u}from"./draw.sequence.js";import{drawSelectionFromResponse as p}from"./types.js";function l(e){return d(s(e.details))}function m(e){return a(e.distribution.lifecycle,s(e.distribution.details))}function h(e){return new y(e)}const w=new Set(e);function f(e){const n=function(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),p=n&&"draw"===n.mechanism?function(e){const{mechanism:t,...i}=e;return i}(n):v(i);if(s)return"completed"===d?.type?g(c,p,d,r):s.expiresAt&&Date.now()>new Date(s.expiresAt).getTime()?"open"===c.lifecycle?l(c)?{phase:"unavailable",reason:"checkout_misconfigured"}:{phase:"enterable",mechanism:"draw",distribution:c,consumer:v(c)}:g(c,p,{type:"expired"}):o&&a?{phase:"granted",mechanism:"draw",distribution:c,consumer:p,grant:s,settlement:o,selection:a}:{phase:"unavailable"};if(!function(e){return w.has(e.status)}(p))return{phase:"unavailable",reason:"unrecognized_standing"};if(!d&&"draw"===n?.mechanism&&"closed"===c.lifecycle)return g(c,p,{type:"lost"});const m=d??function(e){return"completed"===e.status?{type:"completed"}:"denied"===e.status?{type:"denied",reason:e.deniedReason}:void 0}(p);if(m)return g(c,p,m,r);if("settling"===c.lifecycle)return{phase:"settling",mechanism:"draw",distribution:c,consumer:p};if(u(p))return{phase:"participating",mechanism:"draw",distribution:c,consumer:p};if("scheduled"===c.lifecycle)return{phase:"scheduled",mechanism:"draw",distribution:c};if("open"===c.lifecycle)return l(c)?{phase:"unavailable",reason:"checkout_misconfigured"}:{phase:"enterable",mechanism:"draw",distribution:c,consumer:p};return g(c,p,{type:"closed"})}(e);return"unavailable"===n.phase?n:{...n,...i(e)}}function b(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 y{constructor(e){this.deps=e,this.mechanism="draw",this.settlementCanStartWhileActive=!0}project(e){return f(e)}buildView(e,s){if("unavailable"===e.phase)return{phase:"unavailable",reason:e.reason};const r=function(e){if(!("mechanism"in e)||"draw"!==e.mechanism)throw t.validationError("Draw driver received a non-draw sequence");return e}(e);switch(r.phase){case"scheduled":return{phase:"scheduled",mechanism:"draw",...i(r),distribution:r.distribution,startsAt:r.distribution.startsAt??r.distribution.opensAt};case"enterable":return this.buildEnterableView(r,s);case"participating":return{phase:"participating",mechanism:"draw",...i(r),consumer:r.consumer,display$:s.displayAtom,loadSelectionOptions:s.loadSelectionOptions,select:s.select,leave:()=>this.leave()};case"settling":return{phase:"settling",mechanism:"draw",...i(r),distribution:r.distribution,consumer:r.consumer};case"granted":{const e={phase:"granted",mechanism:"draw",...i(r),distribution:r.distribution,consumer:r.consumer,grant:r.grant},{settlement:t}=r,o=r.selection;if("awaiting_assignment"===o.status)return{...e,settlement:t,selection:o};if("required"===o.status)return{...e,settlement:t,selection:o,select:s.select,loadSelectionOptions:s.loadSelectionOptions};if("internal"===t.processor&&"at_end"===t.mode){const i=n(t,r.pendingAction,s);return i?{...e,settlement:t,selection:o,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim(),payment:i}:{...e,settlement:t,selection:o,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}}return c(t)?{...e,settlement:t,selection:o,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}:{...e,settlement:t,selection:o}}case"ended":return this.buildEndedView(r)}}buildEnterableView(e,t){const n=s(e.distribution.details),o=r(n);return"pre_auth"===o.checkoutMode?{phase:"enterable",mechanism:"draw",...i(e),...o,distribution:e.distribution,consumer:e.consumer,...e.pendingAction?{pendingAction:e.pendingAction}:{},...e.paymentProvider?{paymentProvider:e.paymentProvider}:{},loadSelectionOptions:t.loadSelectionOptions,enter:(e,t)=>this.enter({paymentInput:e,selection:t})}:{phase:"enterable",mechanism:"draw",...i(e),...o,distribution:e.distribution,consumer:e.consumer,loadSelectionOptions:t.loadSelectionOptions,enter:e=>this.enter({selection:e})}}buildEndedView(e){const t={phase:"ended",mechanism:"draw",...i(e),consumer:e.consumer,outcome:e.outcome,...e.checkout?{checkout:e.checkout}:{}};if(!m(e))return t;const n=s(e.distribution.details),o=r(n);return"pre_auth"===o.checkoutMode?{...t,...o,reenter:(e,t)=>this.enter({paymentInput:e,selection:t})}:{...t,...o,reenter:e=>this.enter({selection:e})}}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"),n=i.distribution,r=s(n.details),a=e?.paymentInput;if("pre_auth"===r.mode&&!a)throw t.validationError("Payment input is required for pre-auth distributions");const d=e?.selection?await this.deps.draws.enter(n.id,void 0,void 0,a,e.selection):a?await this.deps.draws.enter(n.id,void 0,void 0,a):await this.deps.draws.enter(n.id,void 0);if(o(d))return this.deps.commit({...i,pendingAction:d.nextAction}),void this.deps.emitEvent("sequence_change","info","user","Entry awaiting verification",{distributionId:n.id,processor:d.nextAction.processor});const c=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}}}(d);if(c)return void this.deps.applyMonitorUpdate(c);const u=p(d),l={...(m=n.id,{mechanism:"draw",status:"entered",id:m,distributionId:m}),...u?{selection:u}:{}};var m;this.deps.ensureDisplayAtom({id:n.id,type:"draw"}),this.deps.commit(f({distribution:n,consumer:l})),this.deps.emitEvent("sequence_change","success","user","Entered draw",{distributionId:n.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&&m(n))return{phase:"enterable",mechanism:"draw",distribution:n.distribution,consumer:v(n.distribution)};if(n.phase!==e)throw t.validationError(i);return n}}function v(e){return{status:"not_entered",distributionId:e.id}}function g(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{h as createDrawDriver,f as projectDrawSequence,b 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"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}}}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){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,5 +1,5 @@
|
|
|
1
1
|
import { AdmissionGrant, DrawConsumerStatus, GrantedSettlement, SequenceOutcome } from '@fanfare-io/fanfare-sdk-contracts/consumer-me';
|
|
2
|
-
import { GrantedSelection } from '@fanfare-io/fanfare-sdk-contracts/selection';
|
|
2
|
+
import { GrantedSelection, SelectionMode } from '@fanfare-io/fanfare-sdk-contracts/selection';
|
|
3
3
|
import { ReadableAtom } from 'nanostores';
|
|
4
4
|
import { DrawDisplayState } from '../experiences/distribution-monitor.types';
|
|
5
5
|
import { DistributionSummary } from '../experiences/journey.types';
|
|
@@ -48,6 +48,7 @@ export type DrawSequence = DrawScheduled | DrawEnterable | DrawParticipating | D
|
|
|
48
48
|
export interface DrawScheduled {
|
|
49
49
|
/** Mechanism discriminator for draw narrowing. */
|
|
50
50
|
mechanism: "draw";
|
|
51
|
+
selectionMode?: SelectionMode;
|
|
51
52
|
/** Phase discriminator; scheduled draws cannot be entered yet. */
|
|
52
53
|
phase: "scheduled";
|
|
53
54
|
/** Draw distribution facts from the journey projector. */
|
|
@@ -61,6 +62,7 @@ export interface DrawScheduled {
|
|
|
61
62
|
export interface DrawEnterable {
|
|
62
63
|
/** Mechanism discriminator for draw narrowing. */
|
|
63
64
|
mechanism: "draw";
|
|
65
|
+
selectionMode?: SelectionMode;
|
|
64
66
|
/** Phase discriminator; `enter()` is legal on the matching view. */
|
|
65
67
|
phase: "enterable";
|
|
66
68
|
/** Open draw distribution facts from the journey projector. */
|
|
@@ -87,6 +89,7 @@ export interface DrawEnterable {
|
|
|
87
89
|
export interface DrawParticipating {
|
|
88
90
|
/** Mechanism discriminator for draw narrowing. */
|
|
89
91
|
mechanism: "draw";
|
|
92
|
+
selectionMode?: SelectionMode;
|
|
90
93
|
/** Phase discriminator; the draw display is live while participating. */
|
|
91
94
|
phase: "participating";
|
|
92
95
|
/** Draw distribution facts from the journey projector. */
|
|
@@ -102,6 +105,7 @@ export interface DrawParticipating {
|
|
|
102
105
|
export interface DrawSettling {
|
|
103
106
|
/** Mechanism discriminator for draw narrowing. */
|
|
104
107
|
mechanism: "draw";
|
|
108
|
+
selectionMode?: SelectionMode;
|
|
105
109
|
/** Phase discriminator; no consumer action is legal while settling. */
|
|
106
110
|
phase: "settling";
|
|
107
111
|
/** Settling draw distribution facts from the journey projector. */
|
|
@@ -117,6 +121,7 @@ export interface DrawSettling {
|
|
|
117
121
|
export interface DrawGranted {
|
|
118
122
|
/** Mechanism discriminator for draw narrowing. */
|
|
119
123
|
mechanism: "draw";
|
|
124
|
+
selectionMode?: SelectionMode;
|
|
120
125
|
/** Phase discriminator; `claim()` is legal on the matching view. */
|
|
121
126
|
phase: "granted";
|
|
122
127
|
/** Draw distribution facts from the journey projector. */
|
|
@@ -143,6 +148,7 @@ export interface DrawGranted {
|
|
|
143
148
|
export interface DrawEnded {
|
|
144
149
|
/** Mechanism discriminator for draw narrowing. */
|
|
145
150
|
mechanism: "draw";
|
|
151
|
+
selectionMode?: SelectionMode;
|
|
146
152
|
/** Phase discriminator; no draw actions are legal after this phase. */
|
|
147
153
|
phase: "ended";
|
|
148
154
|
/** Draw distribution facts from the journey projector. */
|
|
@@ -172,6 +178,7 @@ export interface DrawScheduledView {
|
|
|
172
178
|
phase: "scheduled";
|
|
173
179
|
/** Mechanism discriminator for draw rendering. */
|
|
174
180
|
mechanism: "draw";
|
|
181
|
+
selectionMode?: SelectionMode;
|
|
175
182
|
/** Draw distribution facts from the journey projector. */
|
|
176
183
|
distribution: DistributionSummary & {
|
|
177
184
|
type: "draw";
|
|
@@ -193,6 +200,7 @@ export type DrawEnterableView = {
|
|
|
193
200
|
phase: "enterable";
|
|
194
201
|
/** Mechanism discriminator for draw rendering. */
|
|
195
202
|
mechanism: "draw";
|
|
203
|
+
selectionMode?: SelectionMode;
|
|
196
204
|
/** Money moves after the win; entry places no hold. */
|
|
197
205
|
checkoutMode: "at_end";
|
|
198
206
|
/** Open draw distribution facts from the journey projector. */
|
|
@@ -216,6 +224,7 @@ export type DrawEnterableView = {
|
|
|
216
224
|
phase: "enterable";
|
|
217
225
|
/** Mechanism discriminator for draw rendering. */
|
|
218
226
|
mechanism: "draw";
|
|
227
|
+
selectionMode?: SelectionMode;
|
|
219
228
|
/** Fanfare places a payment authorization hold at entry and captures it on win. */
|
|
220
229
|
checkoutMode: "pre_auth";
|
|
221
230
|
/** Open draw distribution facts from the journey projector. */
|
|
@@ -258,6 +267,7 @@ export interface DrawParticipatingView {
|
|
|
258
267
|
phase: "participating";
|
|
259
268
|
/** Mechanism discriminator for draw rendering. */
|
|
260
269
|
mechanism: "draw";
|
|
270
|
+
selectionMode?: SelectionMode;
|
|
261
271
|
/** Consumer draw participation facts. */
|
|
262
272
|
consumer: DrawSequenceConsumerState;
|
|
263
273
|
/** Live draw display facts, such as participant counts and draw timing. */
|
|
@@ -282,6 +292,7 @@ export interface DrawSettlingView {
|
|
|
282
292
|
phase: "settling";
|
|
283
293
|
/** Mechanism discriminator for draw rendering. */
|
|
284
294
|
mechanism: "draw";
|
|
295
|
+
selectionMode?: SelectionMode;
|
|
285
296
|
/** Settling draw distribution facts from the journey projector. */
|
|
286
297
|
distribution: DistributionSummary & {
|
|
287
298
|
type: "draw";
|
|
@@ -295,6 +306,7 @@ interface DrawGrantedViewBase {
|
|
|
295
306
|
phase: "granted";
|
|
296
307
|
/** Mechanism discriminator for draw rendering. */
|
|
297
308
|
mechanism: "draw";
|
|
309
|
+
selectionMode?: SelectionMode;
|
|
298
310
|
/** Draw distribution facts from the journey projector. */
|
|
299
311
|
distribution: DistributionSummary & {
|
|
300
312
|
type: "draw";
|
|
@@ -375,6 +387,7 @@ export type DrawEndedView = {
|
|
|
375
387
|
phase: "ended";
|
|
376
388
|
/** Mechanism discriminator for draw rendering. */
|
|
377
389
|
mechanism: "draw";
|
|
390
|
+
selectionMode?: SelectionMode;
|
|
378
391
|
/** Consumer draw facts at the terminal outcome. */
|
|
379
392
|
consumer: DrawSequenceConsumerState;
|
|
380
393
|
/** Server-derived terminal outcome for the draw participation. */
|
|
@@ -390,6 +403,7 @@ export type DrawEndedView = {
|
|
|
390
403
|
phase: "ended";
|
|
391
404
|
/** Mechanism discriminator for draw rendering. */
|
|
392
405
|
mechanism: "draw";
|
|
406
|
+
selectionMode?: SelectionMode;
|
|
393
407
|
/** Consumer draw facts at the terminal outcome. */
|
|
394
408
|
consumer: DrawSequenceConsumerState;
|
|
395
409
|
/** Server-derived terminal outcome for the draw participation. */
|
|
@@ -411,6 +425,7 @@ export type DrawEndedView = {
|
|
|
411
425
|
phase: "ended";
|
|
412
426
|
/** Mechanism discriminator for draw rendering. */
|
|
413
427
|
mechanism: "draw";
|
|
428
|
+
selectionMode?: SelectionMode;
|
|
414
429
|
/** Consumer draw facts at the terminal outcome. */
|
|
415
430
|
consumer: DrawSequenceConsumerState;
|
|
416
431
|
/** Server-derived terminal outcome for the draw participation. */
|
|
@@ -445,6 +460,7 @@ export interface DrawGrantedAwaitingAssignmentView {
|
|
|
445
460
|
phase: "granted";
|
|
446
461
|
/** Mechanism discriminator. */
|
|
447
462
|
mechanism: "draw";
|
|
463
|
+
selectionMode?: SelectionMode;
|
|
448
464
|
/** Distribution associated with this grant. */
|
|
449
465
|
distribution: DistributionSummary & {
|
|
450
466
|
type: "draw";
|
|
@@ -466,6 +482,7 @@ export interface DrawGrantedSelectionRequiredView {
|
|
|
466
482
|
phase: "granted";
|
|
467
483
|
/** Mechanism discriminator. */
|
|
468
484
|
mechanism: "draw";
|
|
485
|
+
selectionMode?: SelectionMode;
|
|
469
486
|
/** Distribution associated with this grant. */
|
|
470
487
|
distribution: DistributionSummary & {
|
|
471
488
|
type: "draw";
|
package/dist/draws/types.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface DrawResult {
|
|
|
44
44
|
/**
|
|
45
45
|
* Draw consumer states (from backend)
|
|
46
46
|
*/
|
|
47
|
-
export type DrawConsumerState = DrawNotEnteredState | DrawEnteredState | DrawWonState | DrawCompletedState | DrawDeniedState;
|
|
47
|
+
export type DrawConsumerState = DrawNotEnteredState | DrawEnteredState | DrawWonState | DrawCompletedState | DrawDeniedState | DrawLeftState;
|
|
48
48
|
export interface DrawNotEnteredState {
|
|
49
49
|
status: "NOT_ENTERED";
|
|
50
50
|
}
|
|
@@ -70,6 +70,11 @@ export interface DrawCompletedState {
|
|
|
70
70
|
fingerprint?: string;
|
|
71
71
|
expiresAt?: string;
|
|
72
72
|
}
|
|
73
|
+
/** A voluntary exit. Nothing refused the consumer, so there is no reason to carry. */
|
|
74
|
+
export interface DrawLeftState {
|
|
75
|
+
status: "LEFT";
|
|
76
|
+
leftAt: string;
|
|
77
|
+
}
|
|
73
78
|
export interface DrawDeniedState {
|
|
74
79
|
status: "DENIED";
|
|
75
80
|
reason: string;
|