@fanfare-io/fanfare-sdk-core 0.8.0 → 0.9.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.
@@ -14,6 +14,8 @@ export interface AuctionDriverDependencies {
14
14
  runSerializedOperation(operation: () => Promise<void>): Promise<void>;
15
15
  /** Read the current routed sequence before validating an auction action. */
16
16
  getCurrentSequence(): SequenceState | undefined;
17
+ /** Read the live display state before ensureDisplayAtom resets it (settleAt lives here). */
18
+ getDisplayState(): DistributionDisplayState | null;
17
19
  /** Commit a concrete next auction sequence into the shell. */
18
20
  commit(next: SequenceState, opts?: {
19
21
  events?: JourneyEvent[];
@@ -1 +1 @@
1
- import{createError as i}from"../core/errors.js";import{isMoneyString as t,addMoney as e}from"../core/money.js";function n(i){return new r(i)}function s(t){const{distribution:e,consumer:n,waitlist:s,grant:a,outcome:o}=t;if(!e)return{phase:"unavailable"};const r=function(t){if("auction"!==t.type)throw i.validationError("Auction driver received a non-auction distribution");return t}(e),d=n&&"auction"===n.mechanism?function(i){const{mechanism:t,...e}=i;return e}(n):u(e);if(a)return"completed"===o?.type?c(r,d,o):a.expiresAt&&Date.now()>new Date(a.expiresAt).getTime()?"open"===r.lifecycle?{phase:"enterable",mechanism:"auction",distribution:r,consumer:u(r)}:c(r,d,{type:"expired"}):{phase:"granted",mechanism:"auction",distribution:r,consumer:d,grant:a};if(!o&&"auction"===n?.mechanism&&"closed"===r.lifecycle)return c(r,d,{type:"lost"});const h=o??function(i){return"lost"===i.status?{type:"lost"}:void 0}(d);if(h)return c(r,d,h);if(function(i){return"bidding"===i.status||"winning"===i.status||"outbid"===i.status}(d))return{phase:"participating",mechanism:"auction",distribution:r,consumer:d};if("scheduled"===r.lifecycle){const i=s&&s.waitlistId===r.waitlistId?s:r.waitlistId?{waitlistId:r.waitlistId,status:"not_waitlisted"}:void 0;return{phase:"scheduled",mechanism:"auction",distribution:r,...i?{waitlist:i}:{}}}return"open"===r.lifecycle?{phase:"enterable",mechanism:"auction",distribution:r,consumer:d}:"settling"===r.lifecycle?{phase:"settling",mechanism:"auction",distribution:r,consumer:d}:c(r,d,{type:"closed"})}function a(i,t){return"auction"===i?.mechanism?{...i,status:"won"}:{mechanism:"auction",status:"won",id:t.id,distributionId:t.id}}function o(i,t){return i&&"auction"===i.mechanism&&t?"won"===t.type?{...i,status:"won"}:{...i,status:"lost"}:i}class r{constructor(i){this.deps=i,this.mechanism="auction"}project(i){return s(i)}buildView(t,e){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const n=function(t){if(!("mechanism"in t)||"auction"!==t.mechanism)throw i.validationError("Auction driver received a non-auction sequence");return t}(t);switch(n.phase){case"scheduled":return{phase:"scheduled",mechanism:"auction",distribution:n.distribution,startsAt:n.distribution.startsAt??n.distribution.opensAt,waitlist:n.waitlist?{status:n.waitlist.status,join:()=>e.joinWaitlist(),leave:()=>e.exitWaitlist()}:void 0};case"enterable":return{phase:"enterable",mechanism:"auction",distribution:n.distribution,consumer:n.consumer,bid:i=>this.bid(i)};case"participating":return{phase:"participating",mechanism:"auction",consumer:n.consumer,display$:e.displayAtom,bid:i=>this.bid(i),leave:()=>this.leave()};case"settling":return{phase:"settling",mechanism:"auction",distribution:n.distribution,consumer:n.consumer};case"granted":return{phase:"granted",mechanism:"auction",distribution:n.distribution,consumer:n.consumer,grant:n.grant,claim:()=>e.claim()};case"ended":return{phase:"ended",mechanism:"auction",consumer:n.consumer,outcome:n.outcome}}}startMonitoring(i){this.activeMonitorId=i.participation.id,this.deps.auctions.startMonitoring(i.participation.id,{displayAtom:i.displayAtom},i.onUpdate)}stopMonitoring(){this.activeMonitorId&&(this.deps.auctions.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async enter(){await this.deps.runSerializedOperation(async()=>{await this.enterWithinOperation()})}async enterWithinOperation(){const i=this.currentAuctionSequence("enterable","No open auction to enter").distribution;await this.deps.auctions.enter(i.id,void 0),await this.deps.auctions.status(i.id);const t={mechanism:"auction",status:"bidding",id:e=i.id,distributionId:e};var e;this.deps.ensureDisplayAtom({id:i.id,type:"auction"}),this.deps.commit(s({distribution:i,consumer:t})),this.deps.emitEvent("sequence_change","success","user","Entered auction",{distributionId:i.id})}async leave(){await this.deps.runSerializedOperation(async()=>{await this.leaveWithinOperation()})}async leaveWithinOperation(){const i=this.currentAuctionSequence("participating"),t=i.consumer.distributionId??i.consumer.id??i.distribution.id;await this.deps.auctions.leave(t),this.deps.stopRuntimeMonitoring(!0),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"left"}}),this.deps.emitEvent("sequence_change","info","user","Left participation",{type:"auction"})}async bid(i){await this.deps.runSerializedOperation(async()=>{const n=this.currentAuctionSequenceForBid(),a=n.consumer.distributionId??n.consumer.id??n.distribution.id,o=await this.deps.auctions.bid(a,i),r="winning"===o.status?"winning":"outbid"===o.status?"outbid":"bidding",u="auction"===n.distribution.details?.type?n.distribution.details:void 0,c=u?.bidIncrement,d=o.highestBid&&c&&t(o.highestBid)&&t(c)?e(o.highestBid,c):void 0,h=this.deps.ensureDisplayAtom({id:a,type:"auction"});"enterable"===n.phase&&this.deps.commit(s({distribution:n.distribution,consumer:{mechanism:"auction",status:r,id:a,distributionId:a,currentBid:i,highestBid:o.highestBid}})),h.set({type:"auction",currentBid:i,highestBid:o.highestBid,bidCount:o.bidCount,status:r,currencyCode:u?.currencyCode,bidIncrement:c,minNextBid:d,closeAt:u?.closeAt??u?.endsAt})})}currentAuctionSequence(t,e="Not participating in an auction"){const n=this.deps.getCurrentSequence();if(!n||n.phase!==t||!("mechanism"in n)||"auction"!==n.mechanism)throw i.validationError(e);return n}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 u(i){return{status:"not_bid",distributionId:i.id}}function c(i,t,e){return{phase:"ended",mechanism:"auction",distribution:i,consumer:t,outcome:{...e,distributionType:e.distributionType??i.type,distributionId:e.distributionId??i.id,at:e.at??/* @__PURE__ */(new Date).toISOString()}}}export{n as createAuctionDriver,s as projectAuctionSequence,a as withAuctionGrantedConsumer,o as withAuctionTerminalConsumer};
1
+ import{createError as i}from"../core/errors.js";import{isMoneyString as t,addMoney as e}from"../core/money.js";function n(i){return new r(i)}function s(t){const{distribution:e,consumer:n,waitlist:s,grant:a,outcome:o}=t;if(!e)return{phase:"unavailable"};const r=function(t){if("auction"!==t.type)throw i.validationError("Auction driver received a non-auction distribution");return t}(e),d=n&&"auction"===n.mechanism?function(i){const{mechanism:t,...e}=i;return e}(n):u(e);if(a)return"completed"===o?.type?c(r,d,o):a.expiresAt&&Date.now()>new Date(a.expiresAt).getTime()?"open"===r.lifecycle?{phase:"enterable",mechanism:"auction",distribution:r,consumer:u(r)}:c(r,d,{type:"expired"}):{phase:"granted",mechanism:"auction",distribution:r,consumer:d,grant:a};if(!o&&"auction"===n?.mechanism&&"closed"===r.lifecycle)return c(r,d,{type:"lost"});const h=o??function(i){return"lost"===i.status?{type:"lost"}:void 0}(d);if(h)return c(r,d,h);if(function(i){return"bidding"===i.status||"winning"===i.status||"outbid"===i.status}(d))return{phase:"participating",mechanism:"auction",distribution:r,consumer:d};if("scheduled"===r.lifecycle){const i=s&&s.waitlistId===r.waitlistId?s:r.waitlistId?{waitlistId:r.waitlistId,status:"not_waitlisted"}:void 0;return{phase:"scheduled",mechanism:"auction",distribution:r,...i?{waitlist:i}:{}}}return"open"===r.lifecycle?{phase:"enterable",mechanism:"auction",distribution:r,consumer:d}:"settling"===r.lifecycle?{phase:"settling",mechanism:"auction",distribution:r,consumer:d}:c(r,d,{type:"closed"})}function a(i,t){return"auction"===i?.mechanism?{...i,status:"won"}:{mechanism:"auction",status:"won",id:t.id,distributionId:t.id}}function o(i,t){return i&&"auction"===i.mechanism&&t?"won"===t.type?{...i,status:"won"}:{...i,status:"lost"}:i}class r{constructor(i){this.deps=i,this.mechanism="auction"}project(i){return s(i)}buildView(t,e){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const n=function(t){if(!("mechanism"in t)||"auction"!==t.mechanism)throw i.validationError("Auction driver received a non-auction sequence");return t}(t);switch(n.phase){case"scheduled":return{phase:"scheduled",mechanism:"auction",distribution:n.distribution,startsAt:n.distribution.startsAt??n.distribution.opensAt,waitlist:n.waitlist?{status:n.waitlist.status,join:()=>e.joinWaitlist(),leave:()=>e.exitWaitlist()}:void 0};case"enterable":return{phase:"enterable",mechanism:"auction",distribution:n.distribution,consumer:n.consumer,bid:i=>this.bid(i)};case"participating":return{phase:"participating",mechanism:"auction",consumer:n.consumer,display$:e.displayAtom,bid:i=>this.bid(i),leave:()=>this.leave()};case"settling":return{phase:"settling",mechanism:"auction",distribution:n.distribution,consumer:n.consumer};case"granted":return{phase:"granted",mechanism:"auction",distribution:n.distribution,consumer:n.consumer,grant:n.grant,claim:()=>e.claim()};case"ended":return{phase:"ended",mechanism:"auction",consumer:n.consumer,outcome:n.outcome}}}startMonitoring(i){this.activeMonitorId=i.participation.id,this.deps.auctions.startMonitoring(i.participation.id,{displayAtom:i.displayAtom},i.onUpdate)}stopMonitoring(){this.activeMonitorId&&(this.deps.auctions.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async enter(){await this.deps.runSerializedOperation(async()=>{await this.enterWithinOperation()})}async enterWithinOperation(){const i=this.currentAuctionSequence("enterable","No open auction to enter").distribution;await this.deps.auctions.enter(i.id,void 0),await this.deps.auctions.status(i.id);const t={mechanism:"auction",status:"bidding",id:e=i.id,distributionId:e};var e;this.deps.ensureDisplayAtom({id:i.id,type:"auction"}),this.deps.commit(s({distribution:i,consumer:t})),this.deps.emitEvent("sequence_change","success","user","Entered auction",{distributionId:i.id})}async leave(){await this.deps.runSerializedOperation(async()=>{await this.leaveWithinOperation()})}async leaveWithinOperation(){const i=this.currentAuctionSequence("participating"),t=i.consumer.distributionId??i.consumer.id??i.distribution.id;await this.deps.auctions.leave(t),this.deps.stopRuntimeMonitoring(!0),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"left"}}),this.deps.emitEvent("sequence_change","info","user","Left participation",{type:"auction"})}async bid(i){await this.deps.runSerializedOperation(async()=>{const n=this.currentAuctionSequenceForBid(),a=n.consumer.distributionId??n.consumer.id??n.distribution.id,o=await this.deps.auctions.bid(a,i),r="winning"===o.status?"winning":"outbid"===o.status?"outbid":"bidding",u="auction"===n.distribution.details?.type?n.distribution.details:void 0,c=u?.bidIncrement,d=o.highestBid&&c&&t(o.highestBid)&&t(c)?e(o.highestBid,c):void 0,h=this.deps.getDisplayState(),p="auction"===h?.type?h.settleAt:void 0,m=this.deps.ensureDisplayAtom({id:a,type:"auction"});"enterable"===n.phase&&this.deps.commit(s({distribution:n.distribution,consumer:{mechanism:"auction",status:r,id:a,distributionId:a,currentBid:i,highestBid:o.highestBid}})),m.set({type:"auction",currentBid:i,highestBid:o.highestBid,bidCount:o.bidCount,status:r,currencyCode:u?.currencyCode,bidIncrement:c,minNextBid:d,closeAt:u?.closeAt??u?.endsAt,settleAt:p})})}currentAuctionSequence(t,e="Not participating in an auction"){const n=this.deps.getCurrentSequence();if(!n||n.phase!==t||!("mechanism"in n)||"auction"!==n.mechanism)throw i.validationError(e);return n}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 u(i){return{status:"not_bid",distributionId:i.id}}function c(i,t,e){return{phase:"ended",mechanism:"auction",distribution:i,consumer:t,outcome:{...e,distributionType:e.distributionType??i.type,distributionId:e.distributionId??i.id,at:e.at??/* @__PURE__ */(new Date).toISOString()}}}export{n as createAuctionDriver,s as projectAuctionSequence,a as withAuctionGrantedConsumer,o as withAuctionTerminalConsumer};
@@ -15,9 +15,9 @@ export declare class AuctionManagementModule implements AuctionModule {
15
15
  private readonly auctionDetailsCache;
16
16
  private readonly monitorGenerations;
17
17
  private readonly operationGenerations;
18
- private readonly diagnosticEmissionIds;
19
18
  private readonly BID_HISTORY_CACHE_TTL;
20
19
  private readonly AUCTION_DETAILS_CACHE_TTL;
20
+ private readonly NO_BID_END_GRACE_MS;
21
21
  private destroyed;
22
22
  private lifecycleGeneration;
23
23
  private get store();
@@ -26,6 +26,13 @@ export declare class AuctionManagementModule implements AuctionModule {
26
26
  private getAuctionDetailsCached;
27
27
  private getTrackedParticipation;
28
28
  private getAuctionCloseAt;
29
+ /**
30
+ * The bidding-close (and winner-resolution) instant: the server settles the auction at
31
+ * settleAt, so the monitor's terminal clock keys off it — winners are processed (and
32
+ * granted) at settle, not at the later closeAt "done" edge. Falls back to closeAt when
33
+ * settleAt is absent.
34
+ */
35
+ private getAuctionSettleAt;
29
36
  private buildDisplayState;
30
37
  private emitTransitionEvents;
31
38
  private applyTransition;
@@ -1 +1 @@
1
- import{AuctionBidderStateResponseSchema as t,AuctionHighestBidResponseSchema as i,AuctionBidHistoryResponseSchema as e}from"@fanfare-io/fanfare-sdk-contracts/auction";import{createError as n}from"../core/errors.js";import{getLogger as a}from"../core/logger.js";import{isMoneyString as s,addMoney as o,isMoneyGreaterThan as r,normalizeMoney as d,isMoneyLessThanOrEqualTo as u,subtractMoney as c}from"../core/money.js";import{parseResponse as h}from"../core/parse-response.js";import{DistributionMonitorRuntime as l}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as g}from"../state/events.js";import{getSDKStore as p}from"../state/store.js";class m{constructor(t){this.logger=a(),this.events=g(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.pollingIntervals=/* @__PURE__ */new Map,this.lastStatusCache=/* @__PURE__ */new Map,this.autoRebidConfigs=/* @__PURE__ */new Map,this.monitorRuntime=new l,this.bidHistoryCache=/* @__PURE__ */new Map,this.auctionDetailsCache=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.diagnosticEmissionIds=/* @__PURE__ */new Set,this.BID_HISTORY_CACHE_TTL=5e3,this.AUCTION_DETAILS_CACHE_TTL=3e4,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return p()}computeMinNextBid(t,i){if(i&&s(t)&&s(i))return o(t,i)}async getAuctionDetailsCached(t){const i=this.auctionDetailsCache.get(t);if(i&&Date.now()-i.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL)return i.details;const e=await this.http.get(`/auctions/${t}`);return this.auctionDetailsCache.set(t,{details:e,fetchedAt:Date.now()}),e}getTrackedParticipation(t){return this.trackedParticipations.get(t)}getAuctionCloseAt(t){return t.closeAt??t.settleAt}buildDisplayState(t,{status:i,highestBid:e,currentBid:n,bidCount:a}){return{highestBid:e,currentBid:n,bidCount:a,closeAt:this.getAuctionCloseAt(t),status:i,currencyCode:t.currencyCode,minNextBid:e?this.computeMinNextBid(e,t.minBidIncrement):void 0,bidIncrement:t.minBidIncrement??void 0}}emitTransitionEvents(t){for(const i of t)this.events.emit(i.type,i.payload)}applyTransition(t,i){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.diagnosticError&&!this.diagnosticEmissionIds.has(t)?(this.diagnosticEmissionIds.add(t),this.logger.error("Auction monitoring diagnostic",{auctionId:t,error:i.diagnosticError}),this.events.emit("auction:error",{auctionId:t,error:i.diagnosticError})):i.diagnosticError||this.diagnosticEmissionIds.delete(t),i.monitorUpdate&&this.monitorRuntime.notify(t,i.monitorUpdate),i.stopWatching&&this.stopWatching(t),i.autoRebidStatus&&this.handleAutoRebid(t,i.autoRebidStatus)}checkOutbidConditions(t,i,e){if("winning"===t.status&&"outbid"===i.status)return!0;if(e?.currentBid&&i.highestBid){const n=e.currentBid,a=i.highestBid,o=t.highestBid||"0";if(s(n)&&s(a)&&s(o))return r(a,o)&&r(a,n)&&"winning"!==i.status}return"outbid"!==t.status&&"outbid"===i.status}buildBidTransition(t,i,e,n,a){const s="winning"===i.status?"winning":"outbid"===i.status?"outbid":"bidding";return{trackedParticipation:{auctionId:t,enteredAt:a?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:s,currentBid:e,highestBid:i.highestBid,bidCount:i.bidCount,lastBidAt:/* @__PURE__ */(new Date).toISOString(),metadata:a?.metadata,fingerprint:a?.fingerprint},displayState:this.buildDisplayState(n,{status:i.status,highestBid:i.highestBid,currentBid:e,bidCount:i.bidCount}),events:[{type:"auction:bid-placed",payload:{auctionId:t,amount:e,status:i.status}},..."winning"===i.status?[{type:"auction:winning",payload:{auctionId:t,amount:e,highestBid:i.highestBid}}]:[],..."outbid"===i.status?[{type:"auction:outbid",payload:{auctionId:t,yourBid:e,highestBid:i.highestBid}}]:[]],monitorUpdate:null,stopWatching:!1,autoRebidStatus:null}}buildStatusTransition(t,i,e,{previousParticipation:n,previousStatus:a}){const s=i.currentBid??n?.currentBid,o=i.bidCount??n?.bidCount;if(i.timeRemaining<=0){const a="winning"===i.status;return a&&!i.admissionGrant?{trackedParticipation:{auctionId:t,enteredAt:n?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:"winning",currentBid:s,highestBid:i.highestBid,bidCount:o,metadata:n?.metadata,fingerprint:n?.fingerprint},displayState:this.buildDisplayState(e,{status:"winning",highestBid:i.highestBid,currentBid:s,bidCount:o}),events:[],monitorUpdate:null,stopWatching:!1,autoRebidStatus:null,diagnosticError:new Error("Auction resolved to terminal winning without admissionGrant")}:{trackedParticipation:null,displayState:this.buildDisplayState(e,{status:a?"won":"lost",highestBid:i.highestBid,currentBid:s,bidCount:o}),events:a?[{type:"auction:won",payload:{auctionId:t,winningBid:s??""}}]:[{type:"auction:lost",payload:{auctionId:t,highestBid:i.highestBid}}],monitorUpdate:a&&i.admissionGrant?{type:"granted",token:i.admissionGrant}:a?null:{type:"ended",outcome:{type:"lost"}},stopWatching:!0,autoRebidStatus:null}}const r={auctionId:t,enteredAt:n?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:s,highestBid:i.highestBid,bidCount:o,metadata:n?.metadata,fingerprint:n?.fingerprint},d=[];let u=null;return a&&(this.checkOutbidConditions(a,i,n)&&(d.push({type:"auction:outbid",payload:{auctionId:t,yourBid:a.currentBid||n?.currentBid||"",highestBid:i.highestBid}}),u=i),"winning"!==a.status&&"winning"===i.status&&d.push({type:"auction:winning",payload:{auctionId:t,amount:s||"",highestBid:i.highestBid}}),a.highestBid!==i.highestBid&&"winning"!==i.status&&d.push({type:"auction:bid-updated",payload:{auctionId:t,highestBid:i.highestBid,bidCount:i.bidCount||0}})),{trackedParticipation:r,displayState:this.buildDisplayState(e,{status:i.status,highestBid:i.highestBid,currentBid:s,bidCount:o}),events:d,monitorUpdate:null,stopWatching:!1,autoRebidStatus:u}}buildMissingTransition(){return{trackedParticipation:null,displayState:{status:"lost"},events:[],monitorUpdate:{type:"ended",outcome:{type:"closed",reason:"not_participating"}},stopWatching:!0,autoRebidStatus:null}}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(e,a,o){try{const r=this.lifecycleGeneration;if(!s(a))throw n.validationError("Invalid bid amount format. Must be a numeric string (e.g., '100.00')");if(!this.store.session)throw n.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");let u=this.bumpOperationGeneration(e),c=this.getTrackedParticipation(e);c||(this.logger.warn("Attempting to bid without entering auction first",{auctionId:e}),await this.enter(e,void 0,o),c=this.getTrackedParticipation(e),u=this.bumpOperationGeneration(e)),this.logger.info("Placing bid",{auctionId:e,amount:a});const l=`/auctions/${e}/bid`,g=`/auctions/${e}/highest-bid`,[p,m,b]=await Promise.all([this.http.post(l,{amount:a},o),this.http.get(g),this.getAuctionDetailsCached(e)]),y=h(t,p,{endpoint:l},{throwOnFailure:!0});if(!y.ok)throw y.error;const I=h(i,m,{endpoint:g},{throwOnFailure:!0});if(!I.ok)throw I.error;const f=y.value,B=I.value,w=d(B.amount),v={status:"WINNING"===f.status||"WON"===f.status?"winning":"OUTBID"===f.status||"BIDDING"===f.status||"LOST"===f.status?"outbid":"accepted",amount:a,highestBid:w,bidCount:f.bidCount??(c?.bidCount||0)+1};return this.canApplyAsyncResult(e,r,void 0,u)?(this.applyTransition(e,this.buildBidTransition(e,v,a,b,c)),v):v}catch(r){throw this.logger.error("Failed to place bid",{auctionId:e,amount:a,error:r}),this.events.emit("auction:error",{auctionId:e,error:r}),r}}async enter(t,i,e){try{const a=this.lifecycleGeneration,s=this.store.session;if(!s)throw n.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const o=this.bumpOperationGeneration(t);if(this.logger.info("Entering auction",{auctionId:t}),await this.http.post(`/auctions/${t}/enter`,{metadata:i},e),!this.canApplyAsyncResult(t,a,void 0,o))return;this.trackedParticipations.set(t,{auctionId:t,enteredAt:/* @__PURE__ */(new Date).toISOString(),status:"watching",metadata:i,fingerprint:s.deviceFingerprint}),this.emitTransitionEvents([{type:"auction:entered",payload:{auctionId:t}}])}catch(a){throw this.logger.error("Failed to enter auction",{auctionId:t,error:a}),this.events.emit("auction:error",{auctionId:t,error:a}),a}}async leave(t,i){try{const n=this.lifecycleGeneration,a=this.bumpOperationGeneration(t);this.logger.info("Leaving auction",{auctionId:t});try{await this.http.post(`/auctions/${t}/leave`,void 0,i)}catch(e){if(404!==e?.status)throw e;this.logger.debug("Leave endpoint not available",{auctionId:t})}if(!this.canApplyAsyncResult(t,n,void 0,a))return;this.stopMonitoring(t),this.trackedParticipations.delete(t),this.emitTransitionEvents([{type:"auction:left",payload:{auctionId:t}}])}catch(e){throw this.logger.error("Failed to leave auction",{auctionId:t,error:e}),this.events.emit("auction:error",{auctionId:t,error:e}),e}}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,n=this.bumpOperationGeneration(t),a=this.doStatus(t,e,void 0,n).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,a),a}async doStatus(e,n,a,s){try{const o=`/auctions/${e}/status`,r=`/auctions/${e}/highest-bid`,[u,c,l]=await Promise.all([this.http.get(o),this.http.get(r),this.getAuctionDetailsCached(e)]),g=h(t,u,{endpoint:o},{throwOnFailure:!0});if(!g.ok)throw g.error;const p=h(i,c,{endpoint:r},{throwOnFailure:!0});if(!p.ok)throw p.error;const m=g.value,b=p.value,y=this.getAuctionCloseAt(l),I=y?new Date(y).getTime():Number.NaN,f=d(b.amount),B=this.getTrackedParticipation(e),w=m.lastBidAmount??m.winningBidAmount;let v="watching";"WINNING"===m.status||"WON"===m.status?v="winning":"OUTBID"!==m.status&&"LOST"!==m.status&&"BIDDING"!==m.status||(v="outbid");const A={auctionId:e,status:v,currentBid:B?.currentBid??(w?d(w):void 0),highestBid:f,timeRemaining:Number.isFinite(I)?Math.max(0,I-Date.now()):0,bidCount:m.bidCount,admissionGrant:m.admissionGrant};if(!this.canApplyAsyncResult(e,n,a,s))return A;const C=this.buildStatusTransition(e,A,l,{previousParticipation:B,previousStatus:this.lastStatusCache.get(e)});return this.applyTransition(e,C),C.stopWatching?this.lastStatusCache.delete(e):this.lastStatusCache.set(e,A),this.events.emit("auction:status-updated",{auctionId:e,status:A}),A}catch(o){if(o&&"object"==typeof o&&"status"in o&&404===o.status){const t={auctionId:e,status:"watching",highestBid:"0.00",timeRemaining:0};if(!this.canApplyAsyncResult(e,n,a,s))return t;const i=this.buildMissingTransition();return this.applyTransition(e,i),this.lastStatusCache.delete(e),this.events.emit("auction:status-updated",{auctionId:e,status:t}),t}if(!this.canApplyAsyncResult(e,n,a,s))return this.lastStatusCache.get(e)??{auctionId:e,status:"watching",highestBid:"0.00",timeRemaining:0};throw this.logger.error("Failed to get auction status",{auctionId:e,error:o}),this.events.emit("auction:error",{auctionId:e,error:o}),o}}async getBidHistory(t){try{const i=this.bidHistoryCache.get(t);if(i&&Date.now()-i.fetchedAt<this.BID_HISTORY_CACHE_TTL)return this.logger.debug("Returning cached bid history",{auctionId:t}),i.bids;this.logger.info("Fetching bid history",{auctionId:t});const n=`/auctions/${t}/bids/history`,a=await this.http.get(n),s=h(e,a,{endpoint:n},{throwOnFailure:!0});if(!s.ok)throw s.error;const o=s.value.map(t=>({amount:d(t.amount),timestamp:t.timestamp,isWinning:t.isHighest,isYours:!0}));return this.bidHistoryCache.set(t,{bids:o,fetchedAt:Date.now()}),this.events.emit("auction:history-fetched",{auctionId:t,bidCount:o.length}),o}catch(i){throw this.logger.error("Failed to get bid history",{auctionId:t,error:i}),this.events.emit("auction:error",{auctionId:t,error:i}),i}}startWatching(t,i=5e3){this.stopWatching(t);const e=this.bumpMonitorGeneration(t);this.logger.info("Starting auction status polling",{auctionId:t,intervalMs:i});const n=this.getOperationGeneration(t);this.doStatus(t,this.lifecycleGeneration,e,n).catch(i=>{this.canApplyAsyncResult(t,this.lifecycleGeneration,e,n)&&this.logger.error("Initial status check failed",{auctionId:t,error:i})});const a=setInterval(()=>{const i=this.getOperationGeneration(t);this.doStatus(t,this.lifecycleGeneration,e,i).catch(n=>{this.canApplyAsyncResult(t,this.lifecycleGeneration,e,i)&&this.logger.error("Polling failed",{auctionId:t,error:n})})},i);this.pollingIntervals.set(t,a),this.events.emit("auction:watching-started",{auctionId:t,intervalMs:i})}stopWatching(t){this.bumpMonitorGeneration(t);const i=this.pollingIntervals.get(t);i&&(clearInterval(i),this.pollingIntervals.delete(t),this.lastStatusCache.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.pollingIntervals.keys()))this.stopWatching(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,n){return!this.destroyed&&this.lifecycleGeneration===i&&((void 0===e||this.monitorGenerations.get(t)===e)&&(void 0===n||this.getOperationGeneration(t)===n))}enableAutoRebid(t,i,e){if(!s(i)||!s(e))throw n.validationError("Invalid monetary format. Must be numeric strings (e.g., '100.00')");if(u(i,"0")||u(e,"0"))throw n.validationError("Max bid and increment must be positive");if(r(e,i))throw n.validationError("Increment cannot be greater than max bid");const a=this.getTrackedParticipation(t);if(!a)throw n.validationError("Must enter auction before enabling auto-rebid");const o=d(a.currentBid),h=c(i,o);this.autoRebidConfigs.set(t,{enabled:!0,maxBid:i,increment:e,remainingBudget:h,rebidCount:0}),this.logger.info("Auto-rebid enabled",{auctionId:t,maxBid:i,increment:e,remainingBudget:h}),this.events.emit("auction:auto-rebid-enabled",{auctionId:t,maxBid:i,increment:e})}disableAutoRebid(t){const i=this.autoRebidConfigs.get(t);i&&(i.enabled=!1,this.logger.info("Auto-rebid disabled",{auctionId:t}),this.events.emit("auction:auto-rebid-disabled",{auctionId:t,finalRebidCount:i.rebidCount||0}))}getAutoRebidConfig(t){return this.autoRebidConfigs.get(t)}async handleAutoRebid(t,i){const e=this.autoRebidConfigs.get(t);if(e&&e.enabled)try{if(!s(i.highestBid)||!s(e.maxBid)||!s(e.increment))return void this.logger.error("Invalid auto-rebid amounts",{auctionId:t,config:e});const n=o(i.highestBid,e.increment);if(r(n,e.maxBid))return this.logger.info("Auto-rebid would exceed max bid, disabling",{auctionId:t,wouldBid:n,maxBid:e.maxBid}),this.disableAutoRebid(t),void this.events.emit("auction:auto-rebid-max-reached",{auctionId:t,maxBid:e.maxBid,wouldNeedToBid:n});this.logger.info("Placing auto-rebid",{auctionId:t,amount:n});const a=await this.bid(t,n);e.lastRebidAt=/* @__PURE__ */(new Date).toISOString(),e.rebidCount=(e.rebidCount||0)+1,e.remainingBudget=c(e.maxBid,n),this.events.emit("auction:auto-rebid-placed",{auctionId:t,amount:n,remainingBudget:e.remainingBudget,status:a.status})}catch(n){this.logger.error("Auto-rebid failed",{auctionId:t,error:n}),this.events.emit("auction:auto-rebid-failed",{auctionId:t,error:n})}}startMonitoring(t,i,e){this.monitorRuntime.start(t,i,e),this.trackedParticipations.has(t)||this.trackedParticipations.set(t,{auctionId:t,enteredAt:/* @__PURE__ */(new Date).toISOString(),status:"watching"}),this.startWatching(t)}stopMonitoring(t){this.stopWatching(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollingIntervals.has(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.stopAllWatching(),this.inFlightRequests.clear(),this.lastStatusCache.clear(),this.diagnosticEmissionIds.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.bidHistoryCache.clear(),this.auctionDetailsCache.clear(),this.autoRebidConfigs.clear(),this.monitorRuntime.clear()}}export{m as AuctionManagementModule};
1
+ import{AuctionBidderStateResponseSchema as t,AuctionHighestBidResponseSchema as i,AuctionBidHistoryResponseSchema as e}from"@fanfare-io/fanfare-sdk-contracts/auction";import{createError as n}from"../core/errors.js";import{getLogger as a}from"../core/logger.js";import{isMoneyString as s,addMoney as o,isMoneyGreaterThan as r,normalizeMoney as u,isMoneyLessThanOrEqualTo as d,subtractMoney as c}from"../core/money.js";import{parseResponse as h}from"../core/parse-response.js";import{DistributionMonitorRuntime as l}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as g}from"../state/events.js";import{getSDKStore as p}from"../state/store.js";class m{constructor(t){this.logger=a(),this.events=g(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.pollingIntervals=/* @__PURE__ */new Map,this.lastStatusCache=/* @__PURE__ */new Map,this.autoRebidConfigs=/* @__PURE__ */new Map,this.monitorRuntime=new l,this.bidHistoryCache=/* @__PURE__ */new Map,this.auctionDetailsCache=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.BID_HISTORY_CACHE_TTL=5e3,this.AUCTION_DETAILS_CACHE_TTL=3e4,this.NO_BID_END_GRACE_MS=5e3,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return p()}computeMinNextBid(t,i){if(i&&s(t)&&s(i))return o(t,i)}async getAuctionDetailsCached(t){const i=this.auctionDetailsCache.get(t);if(i&&Date.now()-i.fetchedAt<this.AUCTION_DETAILS_CACHE_TTL)return i.details;const e=await this.http.get(`/auctions/${t}`);return this.auctionDetailsCache.set(t,{details:e,fetchedAt:Date.now()}),e}getTrackedParticipation(t){return this.trackedParticipations.get(t)}getAuctionCloseAt(t){return t.closeAt??t.settleAt}getAuctionSettleAt(t){return t.settleAt??t.closeAt}buildDisplayState(t,{status:i,highestBid:e,currentBid:n,bidCount:a}){return{highestBid:e,currentBid:n,bidCount:a,closeAt:this.getAuctionCloseAt(t),settleAt:t.settleAt,status:i,currencyCode:t.currencyCode,minNextBid:e?this.computeMinNextBid(e,t.minBidIncrement):void 0,bidIncrement:t.minBidIncrement??void 0}}emitTransitionEvents(t){for(const i of t)this.events.emit(i.type,i.payload)}applyTransition(t,i){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.autoRebidStatus&&this.handleAutoRebid(t,i.autoRebidStatus)}checkOutbidConditions(t,i,e){if("winning"===t.status&&"outbid"===i.status)return!0;if(e?.currentBid&&i.highestBid){const n=e.currentBid,a=i.highestBid,o=t.highestBid||"0";if(s(n)&&s(a)&&s(o))return r(a,o)&&r(a,n)&&"winning"!==i.status}return"outbid"!==t.status&&"outbid"===i.status}buildBidTransition(t,i,e,n,a){const s="winning"===i.status?"winning":"outbid"===i.status?"outbid":"bidding";return{trackedParticipation:{auctionId:t,enteredAt:a?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:s,currentBid:e,highestBid:i.highestBid,bidCount:i.bidCount,lastBidAt:/* @__PURE__ */(new Date).toISOString(),metadata:a?.metadata,fingerprint:a?.fingerprint},displayState:this.buildDisplayState(n,{status:i.status,highestBid:i.highestBid,currentBid:e,bidCount:i.bidCount}),events:[{type:"auction:bid-placed",payload:{auctionId:t,amount:e,status:i.status}},..."winning"===i.status?[{type:"auction:winning",payload:{auctionId:t,amount:e,highestBid:i.highestBid}}]:[],..."outbid"===i.status?[{type:"auction:outbid",payload:{auctionId:t,yourBid:e,highestBid:i.highestBid}}]:[]],monitorUpdate:null,stopWatching:!1,autoRebidStatus:null}}buildStatusTransition(t,i,e,{previousParticipation:n,previousStatus:a}){const s=i.currentBid??n?.currentBid,o=i.bidCount??n?.bidCount;if("won"===i.terminal)return{trackedParticipation:null,displayState:this.buildDisplayState(e,{status:"won",highestBid:i.highestBid,currentBid:s,bidCount:o}),events:[{type:"auction:won",payload:{auctionId:t,winningBid:s??""}}],monitorUpdate:i.admissionGrant?{type:"granted",token:i.admissionGrant}:{type:"ended",outcome:{type:"won",distributionType:"auction",distributionId:t}},stopWatching:!0,autoRebidStatus:null};if("lost"===i.terminal)return{trackedParticipation:null,displayState:this.buildDisplayState(e,{status:"lost",highestBid:i.highestBid,currentBid:s,bidCount:o}),events:[{type:"auction:lost",payload:{auctionId:t,highestBid:i.highestBid}}],monitorUpdate:{type:"ended",outcome:{type:"lost"}},stopWatching:!0,autoRebidStatus:null};const r=e.closeAt?new Date(e.closeAt).getTime():void 0;if("watching"===i.status&&void 0!==r&&Date.now()>=r+this.NO_BID_END_GRACE_MS)return{trackedParticipation:null,displayState:this.buildDisplayState(e,{status:"ended",highestBid:i.highestBid,currentBid:s,bidCount:o}),events:[],monitorUpdate:{type:"ended",outcome:{type:"closed",reason:"ended"}},stopWatching:!0,autoRebidStatus:null};const u={auctionId:t,enteredAt:n?.enteredAt??/* @__PURE__ */(new Date).toISOString(),status:i.status,currentBid:s,highestBid:i.highestBid,bidCount:o,metadata:n?.metadata,fingerprint:n?.fingerprint},d=[];let c=null;return a&&(this.checkOutbidConditions(a,i,n)&&(d.push({type:"auction:outbid",payload:{auctionId:t,yourBid:a.currentBid||n?.currentBid||"",highestBid:i.highestBid}}),c=i),"winning"!==a.status&&"winning"===i.status&&d.push({type:"auction:winning",payload:{auctionId:t,amount:s||"",highestBid:i.highestBid}}),a.highestBid!==i.highestBid&&"winning"!==i.status&&d.push({type:"auction:bid-updated",payload:{auctionId:t,highestBid:i.highestBid,bidCount:i.bidCount||0}})),{trackedParticipation:u,displayState:this.buildDisplayState(e,{status:i.status,highestBid:i.highestBid,currentBid:s,bidCount:o}),events:d,monitorUpdate:null,stopWatching:!1,autoRebidStatus:c}}buildMissingTransition(){return{trackedParticipation:null,displayState:{status:"lost"},events:[],monitorUpdate:{type:"ended",outcome:{type:"closed",reason:"not_participating"}},stopWatching:!0,autoRebidStatus:null}}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(e,a,o){try{const r=this.lifecycleGeneration;if(!s(a))throw n.validationError("Invalid bid amount format. Must be a numeric string (e.g., '100.00')");if(!this.store.session)throw n.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");let d=this.bumpOperationGeneration(e),c=this.getTrackedParticipation(e);c||(this.logger.warn("Attempting to bid without entering auction first",{auctionId:e}),await this.enter(e,void 0,o),c=this.getTrackedParticipation(e),d=this.bumpOperationGeneration(e)),this.logger.info("Placing bid",{auctionId:e,amount:a});const l=`/auctions/${e}/bid`,g=`/auctions/${e}/highest-bid`,[p,m,b]=await Promise.all([this.http.post(l,{amount:a},o),this.http.get(g),this.getAuctionDetailsCached(e)]),y=h(t,p,{endpoint:l},{throwOnFailure:!0});if(!y.ok)throw y.error;const f=h(i,m,{endpoint:g},{throwOnFailure:!0});if(!f.ok)throw f.error;const I=y.value,B=f.value,w=u(B.amount),v={status:"WINNING"===I.status||"WON"===I.status?"winning":"OUTBID"===I.status||"BIDDING"===I.status||"LOST"===I.status?"outbid":"accepted",amount:a,highestBid:w,bidCount:I.bidCount??(c?.bidCount||0)+1};return this.canApplyAsyncResult(e,r,void 0,d)?(this.applyTransition(e,this.buildBidTransition(e,v,a,b,c)),v):v}catch(r){throw this.logger.error("Failed to place bid",{auctionId:e,amount:a,error:r}),this.events.emit("auction:error",{auctionId:e,error:r}),r}}async enter(t,i,e){try{const a=this.lifecycleGeneration,s=this.store.session;if(!s)throw n.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const o=this.bumpOperationGeneration(t);if(this.logger.info("Entering auction",{auctionId:t}),await this.http.post(`/auctions/${t}/enter`,{metadata:i},e),!this.canApplyAsyncResult(t,a,void 0,o))return;this.trackedParticipations.set(t,{auctionId:t,enteredAt:/* @__PURE__ */(new Date).toISOString(),status:"watching",metadata:i,fingerprint:s.deviceFingerprint}),this.emitTransitionEvents([{type:"auction:entered",payload:{auctionId:t}}])}catch(a){throw this.logger.error("Failed to enter auction",{auctionId:t,error:a}),this.events.emit("auction:error",{auctionId:t,error:a}),a}}async leave(t,i){try{const n=this.lifecycleGeneration,a=this.bumpOperationGeneration(t);this.logger.info("Leaving auction",{auctionId:t});try{await this.http.post(`/auctions/${t}/leave`,void 0,i)}catch(e){if(404!==e?.status)throw e;this.logger.debug("Leave endpoint not available",{auctionId:t})}if(!this.canApplyAsyncResult(t,n,void 0,a))return;this.stopMonitoring(t),this.trackedParticipations.delete(t),this.emitTransitionEvents([{type:"auction:left",payload:{auctionId:t}}])}catch(e){throw this.logger.error("Failed to leave auction",{auctionId:t,error:e}),this.events.emit("auction:error",{auctionId:t,error:e}),e}}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,n=this.bumpOperationGeneration(t),a=this.doStatus(t,e,void 0,n).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,a),a}async doStatus(e,n,a,s){try{const o=`/auctions/${e}/status`,r=`/auctions/${e}/highest-bid`,[d,c,l]=await Promise.all([this.http.get(o),this.http.get(r),this.getAuctionDetailsCached(e)]),g=h(t,d,{endpoint:o},{throwOnFailure:!0});if(!g.ok)throw g.error;const p=h(i,c,{endpoint:r},{throwOnFailure:!0});if(!p.ok)throw p.error;const m=g.value,b=p.value,y=this.getAuctionSettleAt(l),f=y?new Date(y).getTime():Number.NaN,I=u(b.amount),B=this.getTrackedParticipation(e),w=m.lastBidAmount??m.winningBidAmount;let v="watching";"WINNING"===m.status||"WON"===m.status?v="winning":"OUTBID"!==m.status&&"LOST"!==m.status&&"BIDDING"!==m.status||(v="outbid");const A="WON"===m.status?"won":"LOST"===m.status?"lost":void 0,C={auctionId:e,status:v,...A?{terminal:A}:{},currentBid:B?.currentBid??(w?u(w):void 0),highestBid:I,timeRemaining:Number.isFinite(f)?Math.max(0,f-Date.now()):0,bidCount:m.bidCount,admissionGrant:m.admissionGrant};if(!this.canApplyAsyncResult(e,n,a,s))return C;const S=this.buildStatusTransition(e,C,l,{previousParticipation:B,previousStatus:this.lastStatusCache.get(e)});return this.applyTransition(e,S),S.stopWatching?this.lastStatusCache.delete(e):this.lastStatusCache.set(e,C),this.events.emit("auction:status-updated",{auctionId:e,status:C}),C}catch(o){if(o&&"object"==typeof o&&"status"in o&&404===o.status){const t={auctionId:e,status:"watching",highestBid:"0.00",timeRemaining:0};if(!this.canApplyAsyncResult(e,n,a,s))return t;const i=this.buildMissingTransition();return this.applyTransition(e,i),this.lastStatusCache.delete(e),this.events.emit("auction:status-updated",{auctionId:e,status:t}),t}if(!this.canApplyAsyncResult(e,n,a,s))return this.lastStatusCache.get(e)??{auctionId:e,status:"watching",highestBid:"0.00",timeRemaining:0};throw this.logger.error("Failed to get auction status",{auctionId:e,error:o}),this.events.emit("auction:error",{auctionId:e,error:o}),o}}async getBidHistory(t){try{const i=this.bidHistoryCache.get(t);if(i&&Date.now()-i.fetchedAt<this.BID_HISTORY_CACHE_TTL)return this.logger.debug("Returning cached bid history",{auctionId:t}),i.bids;this.logger.info("Fetching bid history",{auctionId:t});const n=`/auctions/${t}/bids/history`,a=await this.http.get(n),s=h(e,a,{endpoint:n},{throwOnFailure:!0});if(!s.ok)throw s.error;const o=s.value.map(t=>({amount:u(t.amount),timestamp:t.timestamp,isWinning:t.isHighest,isYours:!0}));return this.bidHistoryCache.set(t,{bids:o,fetchedAt:Date.now()}),this.events.emit("auction:history-fetched",{auctionId:t,bidCount:o.length}),o}catch(i){throw this.logger.error("Failed to get bid history",{auctionId:t,error:i}),this.events.emit("auction:error",{auctionId:t,error:i}),i}}startWatching(t,i=5e3){this.stopWatching(t);const e=this.bumpMonitorGeneration(t);this.logger.info("Starting auction status polling",{auctionId:t,intervalMs:i});const n=this.getOperationGeneration(t);this.doStatus(t,this.lifecycleGeneration,e,n).catch(i=>{this.canApplyAsyncResult(t,this.lifecycleGeneration,e,n)&&this.logger.error("Initial status check failed",{auctionId:t,error:i})});const a=setInterval(()=>{const i=this.getOperationGeneration(t);this.doStatus(t,this.lifecycleGeneration,e,i).catch(n=>{this.canApplyAsyncResult(t,this.lifecycleGeneration,e,i)&&this.logger.error("Polling failed",{auctionId:t,error:n})})},i);this.pollingIntervals.set(t,a),this.events.emit("auction:watching-started",{auctionId:t,intervalMs:i})}stopWatching(t){this.bumpMonitorGeneration(t);const i=this.pollingIntervals.get(t);i&&(clearInterval(i),this.pollingIntervals.delete(t),this.lastStatusCache.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.pollingIntervals.keys()))this.stopWatching(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,n){return!this.destroyed&&this.lifecycleGeneration===i&&((void 0===e||this.monitorGenerations.get(t)===e)&&(void 0===n||this.getOperationGeneration(t)===n))}enableAutoRebid(t,i,e){if(!s(i)||!s(e))throw n.validationError("Invalid monetary format. Must be numeric strings (e.g., '100.00')");if(d(i,"0")||d(e,"0"))throw n.validationError("Max bid and increment must be positive");if(r(e,i))throw n.validationError("Increment cannot be greater than max bid");const a=this.getTrackedParticipation(t);if(!a)throw n.validationError("Must enter auction before enabling auto-rebid");const o=u(a.currentBid),h=c(i,o);this.autoRebidConfigs.set(t,{enabled:!0,maxBid:i,increment:e,remainingBudget:h,rebidCount:0}),this.logger.info("Auto-rebid enabled",{auctionId:t,maxBid:i,increment:e,remainingBudget:h}),this.events.emit("auction:auto-rebid-enabled",{auctionId:t,maxBid:i,increment:e})}disableAutoRebid(t){const i=this.autoRebidConfigs.get(t);i&&(i.enabled=!1,this.logger.info("Auto-rebid disabled",{auctionId:t}),this.events.emit("auction:auto-rebid-disabled",{auctionId:t,finalRebidCount:i.rebidCount||0}))}getAutoRebidConfig(t){return this.autoRebidConfigs.get(t)}async handleAutoRebid(t,i){const e=this.autoRebidConfigs.get(t);if(e&&e.enabled)try{if(!s(i.highestBid)||!s(e.maxBid)||!s(e.increment))return void this.logger.error("Invalid auto-rebid amounts",{auctionId:t,config:e});const n=o(i.highestBid,e.increment);if(r(n,e.maxBid))return this.logger.info("Auto-rebid would exceed max bid, disabling",{auctionId:t,wouldBid:n,maxBid:e.maxBid}),this.disableAutoRebid(t),void this.events.emit("auction:auto-rebid-max-reached",{auctionId:t,maxBid:e.maxBid,wouldNeedToBid:n});this.logger.info("Placing auto-rebid",{auctionId:t,amount:n});const a=await this.bid(t,n);e.lastRebidAt=/* @__PURE__ */(new Date).toISOString(),e.rebidCount=(e.rebidCount||0)+1,e.remainingBudget=c(e.maxBid,n),this.events.emit("auction:auto-rebid-placed",{auctionId:t,amount:n,remainingBudget:e.remainingBudget,status:a.status})}catch(n){this.logger.error("Auto-rebid failed",{auctionId:t,error:n}),this.events.emit("auction:auto-rebid-failed",{auctionId:t,error:n})}}startMonitoring(t,i,e){this.monitorRuntime.start(t,i,e),this.trackedParticipations.has(t)||this.trackedParticipations.set(t,{auctionId:t,enteredAt:/* @__PURE__ */(new Date).toISOString(),status:"watching"}),this.startWatching(t)}stopMonitoring(t){this.stopWatching(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollingIntervals.has(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.stopAllWatching(),this.inFlightRequests.clear(),this.lastStatusCache.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.bidHistoryCache.clear(),this.auctionDetailsCache.clear(),this.autoRebidConfigs.clear(),this.monitorRuntime.clear()}}export{m as AuctionManagementModule};
@@ -40,6 +40,12 @@ export interface BidResult {
40
40
  export interface AuctionStatus {
41
41
  auctionId: string;
42
42
  status: "winning" | "outbid" | "watching";
43
+ /**
44
+ * The server-resolved terminal outcome for this bidder, or absent while the auction is
45
+ * still in progress. The watch loop stops and finalizes on this alone — independent of the
46
+ * client clock and the admission grant.
47
+ */
48
+ terminal?: "won" | "lost";
43
49
  currentBid?: string;
44
50
  highestBid: string;
45
51
  timeRemaining: number;
@@ -90,7 +90,6 @@ export interface Session {
90
90
  */
91
91
  export interface GuestSession extends Session {
92
92
  type: "guest";
93
- guestId: string;
94
93
  email: undefined;
95
94
  }
96
95
  /**
package/dist/core/http.js CHANGED
@@ -1 +1 @@
1
- import e from"wretch";import{retry as t}from"wretch/middlewares";import{getAdmissionKeyThumbprint as r,buildAdmissionProofHeaders as i}from"../security/admission-proof.js";import{generateFingerprint as s}from"../utils/fingerprint.module.js";import{FanfareError as o,ErrorCodes as n}from"./errors.js";import{getLogger as a}from"./logger.js";import{isStorageAvailable as c,generateId as l}from"./utils.js";class g{constructor(e,t){this.logger=a(),this.fingerprint=null,this.config=e,this.fingerprintingEnabled=!1!==t?.features?.fingerprinting;const r="fanfare_client_session_id";let i=null;if(c("localStorage"))try{i=window.localStorage.getItem(r)}catch{i=null}if(this.clientSessionId=i||l(),!i&&c("localStorage"))try{window.localStorage.setItem(r,this.clientSessionId)}catch{}this.fingerprintingEnabled?this.initializeFingerprint():this.logger.debug("Browser fingerprinting disabled by configuration")}async initializeFingerprint(){try{const e=await s();this.fingerprint=e.hash,this.logger.debug("Browser fingerprint generated successfully",{fingerprintHash:this.fingerprint})}catch(e){this.logger.debug("Failed to generate browser fingerprint",{error:e instanceof Error?e.message:"Unknown error"}),this.fingerprint=null}}getClient(r){const{retryConfig:i={}}=this.config,s={"Content-Type":"application/json","X-Client-Session-Id":this.clientSessionId,...this.config.headers};return this.fingerprint&&(s["X-Fingerprint"]=this.fingerprint),e(this.config.baseUrl).polyfills({fetch:(e,t)=>globalThis.fetch(e,t)}).options({credentials:this.config.credentials??"include",mode:"cors"}).headers(s).middlewares([t({delayTimer:i.delayTimer||1e3,maxAttempts:i.maxRetries||3,until:(e,t)=>e?!!e.ok||e.status>=400&&e.status<500&&429!==e.status:(t&&i.retryOnNetworkError,!1),onRetry:e=>{this.logger.debug("Retrying request",{status:e.response?.status,error:e.error?.message,retryAfter:e.response?.headers?.get("Retry-After")??void 0})}})]).catcher(401,async e=>{throw!r?.skipUnauthorizedHandler&&this.config.onUnauthorized&&(this.logger.debug("Received 401, triggering re-authentication"),await this.config.onUnauthorized()),await this.toFanfareError(e)}).catcherFallback(async e=>{throw await this.toFanfareError(e)})}async toFanfareError(e){if(e&&"object"==typeof e&&"response"in e&&e.response instanceof Response){let t=null;const r=await e.response.text().catch(()=>null),i=e;if(r)try{t=JSON.parse(r)}catch{t={message:r}}else if(null!==i.json&&void 0!==i.json&&"object"==typeof i.json)t=i.json;else if("string"==typeof i.text&&i.text.length>0)try{t=JSON.parse(i.text)}catch{t={message:i.text}}const s=("correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0)||void 0;return o.fromResponse(e.response,t,s)}return e&&"object"==typeof e&&"name"in e&&"AbortError"===e.name?new o("Request timed out",n.TIMEOUT,void 0,void 0,"correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0):new o(e&&"object"==typeof e&&"message"in e&&"string"==typeof e.message?e.message:"Network error occurred",n.NETWORK_ERROR,void 0,{originalError:e},e&&"object"==typeof e&&"correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0)}async request(e,t,s,a){const c=l(),g=l(),p=this.config.timeout||3e4,d=await r(),h=function(e){return/^\/(queues|draws)\/[^/]+\/enter$/.test(e)}(t),f=new URL(t,this.config.baseUrl).toString(),u=new AbortController,m=setTimeout(()=>u.abort(),p);try{const r={"X-Correlation-Id":c,"X-Request-Id":g,...a?.headers};if(h&&!d&&!r["X-Admission-Key-Thumbprint"])throw new o("Admission proof requires persistent browser crypto support",n.ADMISSION_PROOF_UNAVAILABLE,void 0,{path:t});if(h){const s=await i({url:f,method:e.toUpperCase()});if(!s)throw new o("Admission proof requires persistent browser crypto support",n.ADMISSION_PROOF_UNAVAILABLE,void 0,{path:t});Object.assign(r,s)}d&&!r["X-Admission-Key-Thumbprint"]&&(r["X-Admission-Key-Thumbprint"]=d);const l=this.getClient(a).url(t).headers(r).options({signal:a?.signal||u.signal});let p;switch(e){case"get":p=await l.get().json();break;case"post":p=await l.post(s).json();break;case"put":p=await l.put(s).json();break;case"delete":p=await l.delete().json();break;case"patch":p=await l.patch(s).json();break;default:throw new Error(`Unsupported HTTP method: ${e}`)}return p}finally{clearTimeout(m)}}async get(e,t){return this.request("get",e,void 0,t)}async post(e,t,r){return this.request("post",e,t,r)}async put(e,t,r){return this.request("put",e,t,r)}async delete(e,t){return this.request("delete",e,void 0,t)}async patch(e,t,r){return this.request("patch",e,t,r)}updateConfig(e){this.config={...this.config,...e}}getConfig(){return{...this.config}}}function p(e,t){return new g(e,t)}export{g as DefaultHttpClient,p as createHttpClient};
1
+ import e from"wretch";import{retry as t}from"wretch/middlewares";import{buildAdmissionProofHeaders as r,getAdmissionKeyThumbprint as i}from"../security/admission-proof.js";import{generateFingerprint as s}from"../utils/fingerprint.module.js";import{FanfareError as o,ErrorCodes as n}from"./errors.js";import{getLogger as a}from"./logger.js";import{isStorageAvailable as c,generateId as l}from"./utils.js";class g{constructor(e,t){this.logger=a(),this.fingerprint=null,this.config=e,this.fingerprintingEnabled=!1!==t?.features?.fingerprinting;const r="fanfare_client_session_id";let i=null;if(c("localStorage"))try{i=window.localStorage.getItem(r)}catch{i=null}if(this.clientSessionId=i||l(),!i&&c("localStorage"))try{window.localStorage.setItem(r,this.clientSessionId)}catch{}this.fingerprintingEnabled?this.initializeFingerprint():this.logger.debug("Browser fingerprinting disabled by configuration")}async initializeFingerprint(){try{const e=await s();this.fingerprint=e.hash,this.logger.debug("Browser fingerprint generated successfully",{fingerprintHash:this.fingerprint})}catch(e){this.logger.debug("Failed to generate browser fingerprint",{error:e instanceof Error?e.message:"Unknown error"}),this.fingerprint=null}}getClient(i){const{retryConfig:s={}}=this.config,o={"Content-Type":"application/json","X-Client-Session-Id":this.clientSessionId,...this.config.headers};return this.fingerprint&&(o["X-Fingerprint"]=this.fingerprint),e(this.config.baseUrl).polyfills({fetch:(e,t)=>globalThis.fetch(e,t)}).options({credentials:this.config.credentials??"include",mode:"cors"}).headers(o).middlewares([t({delayTimer:s.delayTimer||1e3,maxAttempts:s.maxRetries||3,until:(e,t)=>e?!!e.ok||e.status>=400&&e.status<500&&429!==e.status:(t&&s.retryOnNetworkError,!1),onRetry:async e=>{this.logger.debug("Retrying request",{status:e.response?.status,error:e.error?.message,retryAfter:e.response?.headers?.get("Retry-After")??void 0});const t=e.options.headers&&"object"==typeof e.options.headers&&!Array.isArray(e.options.headers)?e.options.headers:{};if(!Object.keys(t).some(e=>"dpop"===e.toLowerCase()))return;const i="string"==typeof e.options.method?e.options.method:"POST",s=this.config.baseUrl,o=e.url.startsWith(s)?new URL(e.url.slice(s.length),s).toString():e.url,n=await r({url:o,method:i});return n?{options:{...e.options,headers:{...t,...n}}}:void 0}})]).catcher(401,async e=>{throw!i?.skipUnauthorizedHandler&&this.config.onUnauthorized&&(this.logger.debug("Received 401, triggering re-authentication"),await this.config.onUnauthorized()),await this.toFanfareError(e)}).catcherFallback(async e=>{throw await this.toFanfareError(e)})}async toFanfareError(e){if(e&&"object"==typeof e&&"response"in e&&e.response instanceof Response){let t=null;const r=await e.response.text().catch(()=>null),i=e;if(r)try{t=JSON.parse(r)}catch{t={message:r}}else if(null!==i.json&&void 0!==i.json&&"object"==typeof i.json)t=i.json;else if("string"==typeof i.text&&i.text.length>0)try{t=JSON.parse(i.text)}catch{t={message:i.text}}const s=("correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0)||void 0;return o.fromResponse(e.response,t,s)}return e&&"object"==typeof e&&"name"in e&&"AbortError"===e.name?new o("Request timed out",n.TIMEOUT,void 0,void 0,"correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0):new o(e&&"object"==typeof e&&"message"in e&&"string"==typeof e.message?e.message:"Network error occurred",n.NETWORK_ERROR,void 0,{originalError:e},e&&"object"==typeof e&&"correlationId"in e&&"string"==typeof e.correlationId?e.correlationId:void 0)}async request(e,t,s,a){const c=l(),g=l(),p=this.config.timeout||3e4,d=await i(),h=function(e){return/^\/(queues|draws|timed-releases|waitlists)\/[^/]+\/enter$/.test(e)}(t),u=new URL(t,this.config.baseUrl).toString(),f=new AbortController,m=setTimeout(()=>f.abort(),p);try{const i={"X-Correlation-Id":c,"X-Request-Id":g,...a?.headers};if(h&&!d&&!i["X-Admission-Key-Thumbprint"])throw new o("Admission proof requires persistent browser crypto support",n.ADMISSION_PROOF_UNAVAILABLE,void 0,{path:t});if(h){const s=await r({url:u,method:e.toUpperCase()});if(!s)throw new o("Admission proof requires persistent browser crypto support",n.ADMISSION_PROOF_UNAVAILABLE,void 0,{path:t});Object.assign(i,s)}if(!h&&function(e){return/^\/auctions\/[^/]+\/bid$/.test(e)}(t)&&d){const t=await r({url:u,method:e.toUpperCase()});t&&Object.assign(i,t)}d&&!i["X-Admission-Key-Thumbprint"]&&(i["X-Admission-Key-Thumbprint"]=d);const l=this.getClient(a).url(t).headers(i).options({signal:a?.signal||f.signal});let p;switch(e){case"get":p=await l.get().json();break;case"post":p=await l.post(s).json();break;case"put":p=await l.put(s).json();break;case"delete":p=await l.delete().json();break;case"patch":p=await l.patch(s).json();break;default:throw new Error(`Unsupported HTTP method: ${e}`)}return p}finally{clearTimeout(m)}}async get(e,t){return this.request("get",e,void 0,t)}async post(e,t,r){return this.request("post",e,t,r)}async put(e,t,r){return this.request("put",e,t,r)}async delete(e,t){return this.request("delete",e,void 0,t)}async patch(e,t,r){return this.request("patch",e,t,r)}updateConfig(e){this.config={...this.config,...e}}getConfig(){return{...this.config}}}function p(e,t){return new g(e,t)}export{g as DefaultHttpClient,p as createHttpClient};
@@ -80,8 +80,10 @@ export interface AuctionDisplayState {
80
80
  currentBid?: string;
81
81
  /** Number of bids reported by the auction service. */
82
82
  bidCount?: number;
83
- /** ISO 8601 timestamp when bidding closes, when known. */
83
+ /** ISO 8601 timestamp when the distribution is done (terminal edge), when known. */
84
84
  closeAt?: string;
85
+ /** ISO 8601 timestamp when bidding closes (the within-window settle point); the bid countdown target. */
86
+ settleAt?: string;
85
87
  /** Auction display status from the auction service. */
86
88
  status?: string;
87
89
  /** ISO 4217 currency code for auction money fields. */
@@ -1 +1 @@
1
- import{atom as e,computed as t}from"nanostores";import*as i from"valibot";import{createAppointmentDriver as s,withAppointmentTerminalConsumer as r}from"../appointments/appointment.driver.js";import{createAuctionDriver as n,withAuctionGrantedConsumer as o,withAuctionTerminalConsumer as a}from"../auctions/auction.driver.js";import{createError as c,isFanfareError as d,ErrorCodes as u}from"../core/errors.js";import{createLogger as h}from"../core/logger.js";import{generateId as p}from"../core/utils.js";import{createDrawDriver as l,withDrawGrantedConsumer as m,withDrawTerminalConsumer as y}from"../draws/draw.driver.js";import{createQueueDriver as g,withQueueGrantedConsumer as f,withQueueTerminalConsumer as b}from"../queues/queue.driver.js";import{clearExperienceContext as I,clearSequenceContext as v,bindDistributionContext as S,upsertSequenceToken as A,getSequenceTokenExpiryMs as q}from"../state/capability-token-registry.js";import{getEventBus as w}from"../state/events.js";import{getSDKStore as x}from"../state/store.js";import{createTimedReleaseDriver as T,withTimedReleaseGrantedConsumer as R,withTimedReleaseTerminalConsumer as j}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as C}from"./experience.module.js";import{buildJourneyView as E}from"./journey-view.js";import{createInitialSnapshot as D,getParticipation as k,getSequencePhase as M,normalizeSequenceState as O,buildSnapshot as F,getSequenceId as G,getDistribution as _,getConsumer as z,getWaitlistAttachment as B,getOutcome as U,getGrant as J,getSequenceState as P,getGates as L,createGrant as N,getAdmissionGrantExpiresAt as V,validateSequenceState as W}from"./journey.machine.js";const $="Cannot commit a stale sequence transition",Q="Cannot commit a sequence transition before routing",H=3e4;class K{constructor(i,s){this.experienceId=i,this.sdk=s,this.store=x(),this.logger=h(),this.activeMonitor=null,this.consecutiveRefreshFailures=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.EVENT_BUFFER_LIMIT=20;const r=this.restoreFromStore(),n=r??D(this.experienceId),o=r?k(r):void 0;o&&"participating"===M(n)&&this.ensureDisplayAtom(o),this.stateAtom=e(n),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=t(this.stateAtom,e=>this.buildView(e)),this.events$=t(this.stateAtom,e=>e.events),this.latestEvent$=t(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),r?this.rearmTimersIfNeeded():this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{e?.reset&&(this.clearRoutedRuntimeState(),this.setSnapshot(D(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.clearCapabilityRefreshTimer(),this.stopPolling(),I(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(D(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=O({sequence:t.sequence}),r=F({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s});this.setSnapshot(r),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&v(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return this.rearmTimersIfNeeded(),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=G(e);if(!t)throw c.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t),s=this.buildDistributionSummary(i,t),r=_(e);s&&S({experienceId:this.experienceId,sequenceId:t,distributionContext:s});const n=z(e),o="scheduled"===s?.lifecycle?X(B(e),s):void 0,a=this.deriveTerminalOutcomeForClosedDistribution(n,s,U(e)),d=this.withTerminalConsumer(n,a),u=this.projectSequenceState({distribution:s,consumer:d,waitlist:o,grant:J(e),outcome:a});this.commit(u),this.emitIfDistributionChanged(r,s)})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=P(e),i=_(e),s="scheduled"===t?.phase&&"waitlist"in t?t.waitlist?.waitlistId:i?.waitlistId,r="string"==typeof s?s:void 0;if("scheduled"!==t?.phase||!i||!r)throw c.validationError("Waitlist is not available for this sequence");const n=await this.sdk.waitlists.enter(r),o=this.projectSequenceState({distribution:i,waitlist:{waitlistId:r,status:"waitlisted",entryId:n.id}});this.commit(o),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:r})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=P(e),i=_(e),s="scheduled"===t?.phase&&"waitlist"in t?t.waitlist:void 0,r=s?.waitlistId??i?.waitlistId;if("scheduled"!==t?.phase||!i||!r)return;await this.sdk.waitlists.leave(r);const n=this.projectSequenceState({distribution:i,waitlist:{waitlistId:r,status:"not_waitlisted"}});this.commit(n),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:r})})}claim(){const e=J(this.stateAtom.get());if(!e)throw c.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:e.distributionType,distributionId:e.distributionId,sequenceId:e.sequenceId}),w().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:e.sequenceId,grant:e.token}),e}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){const i=t?.carryFrom??this.stateAtom.get(),s=G(i),r=await this.resolveRouting(e,{transient:t?.transient});if(!r||this.destroyed)return;if(await this.enterExperienceIfReady(),this.destroyed)return;const n=await this.sdk.experiences.getCurrentDistributions(r.sequenceId);if(this.destroyed)return;const o=this.buildDistributionSummary(n,r.sequenceId);o&&S({experienceId:this.experienceId,sequenceId:r.sequenceId,distributionContext:o});const a=s===r.sequenceId,c=a?z(i):void 0,d=a&&"scheduled"===o?.lifecycle?X(B(i),o):void 0,u=a?J(i):void 0,h=a?this.deriveTerminalOutcomeForClosedDistribution(c,o,U(i)):void 0,p=this.withTerminalConsumer(c,h),l=!o&&a&&"routed"===i.journeyStage&&("granted"===i.sequence.phase||"ended"===i.sequence.phase)?i.sequence:this.projectSequenceState({distribution:o,consumer:p,waitlist:d,grant:u,outcome:h});a||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer());const m=this.stateAtom.get(),y=F({base:this.baseFromSnapshot(m),journeyStage:"routed",sequenceId:r.sequenceId,offers:r.offers,sequence:l,sequenceRef:r.selectedSequence});this.setSnapshot(y),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const g=a?_(i):void 0;this.emitIfDistributionChanged(g,o)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=G(i);let r;this.transitionToRouting();try{r=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(o){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),o}if(this.destroyed)return null;if("gated"===r.outcome&&r.selected){const e=r.selected.gates;return"routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:r.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null}if("no_access"===r.outcome||!r.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new C("No access available for this experience");const n=r.selected.sequence.id;if(s&&s!==n&&v(this.experienceId,s),r.selected.capabilityGrant&&A({experienceId:this.experienceId,sequenceId:n,token:r.selected.capabilityGrant,expiresAt:r.selected.capabilityGrantExpiresAt}),s!==n){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,o=r.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,o);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:n,...void 0===e?{}:{previousPriority:e},priority:o,direction:a})}return{sequenceId:n,selectedSequence:r.selected.sequence,offers:r.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(L(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:s({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:n({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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)}),queue:g({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:T({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:l({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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 E(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()},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(F({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(F({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(F({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(F({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(F({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(F({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;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}}commit(e,t={}){if(this.destroyed)throw c.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw c.validationError(Q);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw c.validationError($);const s=F({base:{revision:i.revision,updatedAt:i.updatedAt,experienceId:i.experienceId,accessCode:i.accessCode,experience:i.experience,events:t.events??i.events,lastSeenEventId:i.lastSeenEventId},journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:e,sequenceRef:i.sequenceRef});this.setSnapshot(s)}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:p(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t){return e.active?{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:"open",details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}:e.upcoming?{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,waitlistId:e.upcoming.waitlistId,updatedAt:Date.now()}:e.recentPast?{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:"closed",closesAt:e.recentPast.endedAt,updatedAt:Date.now()}:void 0}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=qe(e[this.experienceId]);return t||(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get();this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?M(e):void 0,r=t.journeyStage,n="routed"===r?M(t):void 0;i===r&&s===n||w().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw c.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToGranted(e,t){if(this.destroyed)return;const i=this.stateAtom.get(),s=_(i),r=G(i);if(!s||!r)return void this.transitionToEnded({type:"closed",reason:"missing_distribution"});if(void 0!==t&&Date.now()>=t)return void this.transitionToEnded({type:"expired"});const n=N({token:e,distribution:s,sequenceId:r,experienceId:this.experienceId,expiresAt:t}),o=this.withGrantedConsumer(z(i),s),a=this.projectSequenceState({distribution:s,consumer:o,grant:n});this.commit(a),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.checkoutStarted=!1,void 0!==t&&(this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),t-Date.now())),this.emitEvent("sequence_change","success","user","Grant issued",{grant:e}),w().emit("journey:granted",{experienceId:this.experienceId,sequenceId:r,grant:e})}transitionToEnded(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=_(t),s=this.reinterpretLapsedGrantOutcome(t,e),r={...s,distributionType:s.distributionType??i?.type,distributionId:s.distributionId??i?.id,at:s.at??/* @__PURE__ */(new Date).toISOString()},n=this.withTerminalConsumer(z(t),r),o=this.projectSequenceState({distribution:i,consumer:n,outcome:r});this.commit(o),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","expired"===r.type?"warning":"info","user","Sequence ended",{outcome:r}),w().emit("journey:ended",{experienceId:this.experienceId,sequenceId:G(this.stateAtom.get()),outcome:r})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==M(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){this.destroyed||("granted"!==e.type?this.transitionToEnded(e.outcome):this.transitionToGranted(e.token,e.expiresAt))}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=this.sequenceDrivers[e.type];if(s?.startMonitoring)return s.startMonitoring({participation:e,displayAtom:t,onUpdate:i}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>s.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)}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=q(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),"granted"!==M(e)||this.grantExpiryTimer)return;const t=V(e);if(!t)return;const i=t-Date.now();i<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),i)}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<H)return void this.scheduleCapabilityRefresh(H);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(H*2**this.consecutiveRefreshFailures,6e5);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!d(e)||e.code!==u.VALIDATION_ERROR||e.message!==$&&e.message!==Q)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=M(e),i=k(e),s=G(e);return"participating"===t&&i&&s?{experienceId:this.experienceId,sequenceId:s,participationId:i.id,participationType:i.type}:null}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;const t=this.getRuntimeBinding();if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),void this.setSnapshot(D(this.experienceId),{persist:!1});const i=qe(e);if(!i)return;const s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=k(i),n=M(i),o="participating"===n&&r?{sequenceId:G(i),participationId:r.id,participationType:r.type}:null;o?t&&t.sequenceId===o.sequenceId&&t.participationId===o.participationId&&t.participationType===o.participationType||this.resetDisplayAtom(o.participationType):this.displayAtom=null,this.setSnapshot(i,{persist:!1}),"participating"===n&&r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){return this.resetDisplayAtom(e.type),this.displayAtom}resetDisplayAtom(t){this.displayAtom&&this.displayAtom.get().type===t?this.displayAtom.set({type:t}):this.displayAtom=e({type:t})}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();e?this.resetDisplayAtom(e.participationType):this.displayAtom=null}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i)return i;if(!t||"closed"!==t.lifecycle||!e)return;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:{type:"closed",distributionType:t.type,distributionId:t.id}}withGrantedConsumer(e,t){switch(e?.mechanism){case"auction":return o(e,t);case"draw":return m(e,t);case"queue":return f(e,t);case"timed_release":return R(e,t);case void 0:switch(t.type){case"auction":return o(e,t);case"draw":return m(e,t);case"queue":return f(e,t);case"timed_release":return R(e,t);default:throw c.validationError("Distribution does not produce grants")}default:if(!e)throw c.validationError("Grant requires a consumer state");return e}}withTerminalConsumer(e,t){switch(e?.mechanism){case"appointment":return r(e,t);case"auction":return a(e,t);case"draw":return y(e,t);case"queue":return b(e,t);case"timed_release":return j(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}function X(e,t){if(e&&e.waitlistId===t.waitlistId)return e}const Y=i.picklist(["queue","draw","auction","timed_release","appointment"]),Z=Y,ee=i.picklist(["queue","draw","auction","timed_release"]),te=i.picklist(["draw","auction"]),ie=i.strictObject({id:i.string(),ts:i.number(),kind:i.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:i.picklist(["info","success","warning","error"]),audience:i.picklist(["user","system","analytics"]),message:i.string(),detail:i.optional(i.record(i.string(),i.unknown()))}),se=i.strictObject({id:i.string(),name:i.string(),experienceId:i.string(),priority:i.number(),color:i.string()}),re=i.strictObject({type:Y,id:i.string(),sequenceId:i.optional(i.string()),lifecycle:i.string(),startsAt:i.optional(i.string()),opensAt:i.optional(i.string()),closesAt:i.optional(i.string()),settlesAt:i.optional(i.string()),waitlistId:i.optional(i.string()),details:i.optional(i.unknown()),updatedAt:i.optional(i.number())}),ne=i.strictObject({status:i.string(),id:i.optional(i.string()),distributionId:i.optional(i.string()),waitlistId:i.optional(i.string()),enteredAt:i.optional(i.string()),admissionGrant:i.optional(i.string()),grantedAt:i.optional(i.string()),deniedReason:i.optional(i.string()),position:i.optional(i.number()),highestBid:i.optional(i.string()),currentBid:i.optional(i.string()),slotId:i.optional(i.string()),locationId:i.optional(i.string()),bookedAt:i.optional(i.string()),checkedInAt:i.optional(i.string()),completedAt:i.optional(i.string()),cancelledAt:i.optional(i.string()),noShowAt:i.optional(i.string()),notifiedAt:i.optional(i.string())}),oe=i.strictObject({token:i.string(),distributionType:ee,distributionId:i.string(),sequenceId:i.string(),experienceId:i.string(),issuedAt:i.optional(i.string()),expiresAt:i.optional(i.string()),handoffUrl:i.optional(i.string())}),ae=i.strictObject({type:i.string(),reason:i.optional(i.string()),at:i.optional(i.string()),distributionType:i.optional(Y),distributionId:i.optional(i.string())}),ce=i.strictObject({phase:i.literal("unavailable"),reason:i.optional(i.string())}),de=i.strictObject({waitlistId:i.string(),status:i.picklist(["not_waitlisted","waitlisted","notified"]),entryId:i.optional(i.string())}),ue=i.strictObject({phase:i.literal("scheduled"),mechanism:Z,distribution:re,waitlist:i.optional(de)}),he=i.strictObject({phase:i.literal("enterable"),mechanism:Z,distribution:re,consumer:ne}),pe=i.strictObject({phase:i.literal("participating"),mechanism:Z,distribution:re,consumer:ne}),le=i.strictObject({phase:i.literal("settling"),mechanism:te,distribution:re,consumer:ne}),me=i.strictObject({phase:i.literal("granted"),mechanism:ee,distribution:re,consumer:ne,grant:oe}),ye=i.strictObject({phase:i.literal("ended"),mechanism:Z,distribution:re,consumer:ne,outcome:ae}),ge=i.union([ce,ue,he,pe,le,me,ye]),fe={schemaVersion:i.literal(2),revision:i.number(),updatedAt:i.number(),experienceId:i.string(),accessCode:i.optional(i.string()),experience:i.optional(i.unknown()),events:i.array(ie),lastSeenEventId:i.optional(i.string())},be=i.strictObject({...fe,journeyStage:i.literal("ready")}),Ie=i.strictObject({...fe,journeyStage:i.literal("routing")}),ve=i.strictObject({...fe,journeyStage:i.literal("gated"),gates:i.array(i.unknown()),selected:i.optional(i.strictObject({sequence:i.unknown(),status:i.literal("gated")}))}),Se=i.strictObject({...fe,journeyStage:i.literal("routed"),sequenceId:i.string(),offers:i.array(i.unknown()),sequence:ge,sequenceRef:i.optional(se)}),Ae=i.union([be,Ie,ve,Se]);function qe(e){const t=i.safeParse(Ae,e);if(!t.success)return null;const{schemaVersion:s,...r}=t.output;if("routed"===r.journeyStage)try{W(r.sequence)}catch{return null}return r}export{K as ExperienceJourney};
1
+ import{atom as e,computed as t}from"nanostores";import*as i from"valibot";import{createAppointmentDriver as s,withAppointmentTerminalConsumer as r}from"../appointments/appointment.driver.js";import{createAuctionDriver as n,withAuctionGrantedConsumer as o,withAuctionTerminalConsumer as a}from"../auctions/auction.driver.js";import{createError as c,isFanfareError as d,ErrorCodes as u}from"../core/errors.js";import{createLogger as h}from"../core/logger.js";import{generateId as p}from"../core/utils.js";import{createDrawDriver as l,withDrawGrantedConsumer as m,withDrawTerminalConsumer as y}from"../draws/draw.driver.js";import{createQueueDriver as g,withQueueGrantedConsumer as f,withQueueTerminalConsumer as b}from"../queues/queue.driver.js";import{clearExperienceContext as I,clearSequenceContext as v,bindDistributionContext as S,upsertSequenceToken as A,getSequenceTokenExpiryMs as q}from"../state/capability-token-registry.js";import{getEventBus as w}from"../state/events.js";import{getSDKStore as x}from"../state/store.js";import{createTimedReleaseDriver as T,withTimedReleaseGrantedConsumer as R,withTimedReleaseTerminalConsumer as j}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as C}from"./experience.module.js";import{buildJourneyView as E}from"./journey-view.js";import{createInitialSnapshot as D,getParticipation as k,getSequencePhase as M,normalizeSequenceState as O,buildSnapshot as F,getSequenceId as G,getDistribution as _,getConsumer as z,getWaitlistAttachment as B,getOutcome as U,getGrant as J,getSequenceState as P,getGates as L,createGrant as N,getAdmissionGrantExpiresAt as V,validateSequenceState as W}from"./journey.machine.js";const $="Cannot commit a stale sequence transition",Q="Cannot commit a sequence transition before routing",H=3e4;class K{constructor(i,s){this.experienceId=i,this.sdk=s,this.store=x(),this.logger=h(),this.activeMonitor=null,this.consecutiveRefreshFailures=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.EVENT_BUFFER_LIMIT=20;const r=this.restoreFromStore(),n=r??D(this.experienceId),o=r?k(r):void 0;o&&"participating"===M(n)&&this.ensureDisplayAtom(o),this.stateAtom=e(n),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=t(this.stateAtom,e=>this.buildView(e)),this.events$=t(this.stateAtom,e=>e.events),this.latestEvent$=t(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),r?this.rearmTimersIfNeeded():this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{e?.reset&&(this.clearRoutedRuntimeState(),this.setSnapshot(D(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.clearCapabilityRefreshTimer(),this.stopPolling(),I(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(D(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=O({sequence:t.sequence}),r=F({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s});this.setSnapshot(r),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&v(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return this.rearmTimersIfNeeded(),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=G(e);if(!t)throw c.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t),s=this.buildDistributionSummary(i,t),r=_(e);s&&S({experienceId:this.experienceId,sequenceId:t,distributionContext:s});const n=z(e),o="scheduled"===s?.lifecycle?X(B(e),s):void 0,a=this.deriveTerminalOutcomeForClosedDistribution(n,s,U(e)),d=this.withTerminalConsumer(n,a),u=this.projectSequenceState({distribution:s,consumer:d,waitlist:o,grant:J(e),outcome:a});this.commit(u),this.emitIfDistributionChanged(r,s)})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=P(e),i=_(e),s="scheduled"===t?.phase&&"waitlist"in t?t.waitlist?.waitlistId:i?.waitlistId,r="string"==typeof s?s:void 0;if("scheduled"!==t?.phase||!i||!r)throw c.validationError("Waitlist is not available for this sequence");const n=await this.sdk.waitlists.enter(r),o=this.projectSequenceState({distribution:i,waitlist:{waitlistId:r,status:"waitlisted",entryId:n.id}});this.commit(o),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:r})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=P(e),i=_(e),s="scheduled"===t?.phase&&"waitlist"in t?t.waitlist:void 0,r=s?.waitlistId??i?.waitlistId;if("scheduled"!==t?.phase||!i||!r)return;await this.sdk.waitlists.leave(r);const n=this.projectSequenceState({distribution:i,waitlist:{waitlistId:r,status:"not_waitlisted"}});this.commit(n),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:r})})}claim(){const e=J(this.stateAtom.get());if(!e)throw c.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:e.distributionType,distributionId:e.distributionId,sequenceId:e.sequenceId}),w().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:e.sequenceId,grant:e.token}),e}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){const i=t?.carryFrom??this.stateAtom.get(),s=G(i),r=await this.resolveRouting(e,{transient:t?.transient});if(!r||this.destroyed)return;if(await this.enterExperienceIfReady(),this.destroyed)return;const n=await this.sdk.experiences.getCurrentDistributions(r.sequenceId);if(this.destroyed)return;const o=this.buildDistributionSummary(n,r.sequenceId);o&&S({experienceId:this.experienceId,sequenceId:r.sequenceId,distributionContext:o});const a=s===r.sequenceId,c=a?z(i):void 0,d=a&&"scheduled"===o?.lifecycle?X(B(i),o):void 0,u=a?J(i):void 0,h=a?this.deriveTerminalOutcomeForClosedDistribution(c,o,U(i)):void 0,p=this.withTerminalConsumer(c,h),l=!o&&a&&"routed"===i.journeyStage&&("granted"===i.sequence.phase||"ended"===i.sequence.phase)?i.sequence:this.projectSequenceState({distribution:o,consumer:p,waitlist:d,grant:u,outcome:h});a||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer());const m=this.stateAtom.get(),y=F({base:this.baseFromSnapshot(m),journeyStage:"routed",sequenceId:r.sequenceId,offers:r.offers,sequence:l,sequenceRef:r.selectedSequence});this.setSnapshot(y),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const g=a?_(i):void 0;this.emitIfDistributionChanged(g,o)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=G(i);let r;this.transitionToRouting();try{r=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(o){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),o}if(this.destroyed)return null;if("gated"===r.outcome&&r.selected){const e=r.selected.gates;return"routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:r.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null}if("no_access"===r.outcome||!r.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new C("No access available for this experience");const n=r.selected.sequence.id;if(s&&s!==n&&v(this.experienceId,s),r.selected.capabilityGrant&&A({experienceId:this.experienceId,sequenceId:n,token:r.selected.capabilityGrant,expiresAt:r.selected.capabilityGrantExpiresAt}),s!==n){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,o=r.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,o);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:n,...void 0===e?{}:{previousPriority:e},priority:o,direction:a})}return{sequenceId:n,selectedSequence:r.selected.sequence,offers:r.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(L(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:s({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:n({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(this.stateAtom.get()),getDisplayState:()=>this.displayAtom?.get()??null,commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>this.ensureDisplayAtom(e),stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),queue:g({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:T({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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:l({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>P(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 E(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()},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(F({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(F({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(F({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(F({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(F({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(F({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;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}}commit(e,t={}){if(this.destroyed)throw c.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw c.validationError(Q);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw c.validationError($);const s=F({base:{revision:i.revision,updatedAt:i.updatedAt,experienceId:i.experienceId,accessCode:i.accessCode,experience:i.experience,events:t.events??i.events,lastSeenEventId:i.lastSeenEventId},journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:e,sequenceRef:i.sequenceRef});this.setSnapshot(s)}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:p(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t){return e.active?{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:"open",details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}:e.upcoming?{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,waitlistId:e.upcoming.waitlistId,updatedAt:Date.now()}:e.recentPast?{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:"closed",closesAt:e.recentPast.endedAt,updatedAt:Date.now()}:void 0}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=qe(e[this.experienceId]);return t||(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get();this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?M(e):void 0,r=t.journeyStage,n="routed"===r?M(t):void 0;i===r&&s===n||w().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw c.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToGranted(e,t){if(this.destroyed)return;const i=this.stateAtom.get(),s=_(i),r=G(i);if(!s||!r)return void this.transitionToEnded({type:"closed",reason:"missing_distribution"});if(void 0!==t&&Date.now()>=t)return void this.transitionToEnded({type:"expired"});const n=N({token:e,distribution:s,sequenceId:r,experienceId:this.experienceId,expiresAt:t}),o=this.withGrantedConsumer(z(i),s),a=this.projectSequenceState({distribution:s,consumer:o,grant:n});this.commit(a),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.checkoutStarted=!1,void 0!==t&&(this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),t-Date.now())),this.emitEvent("sequence_change","success","user","Grant issued",{grant:e}),w().emit("journey:granted",{experienceId:this.experienceId,sequenceId:r,grant:e})}transitionToEnded(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=_(t),s=this.reinterpretLapsedGrantOutcome(t,e),r={...s,distributionType:s.distributionType??i?.type,distributionId:s.distributionId??i?.id,at:s.at??/* @__PURE__ */(new Date).toISOString()},n=this.withTerminalConsumer(z(t),r),o=this.projectSequenceState({distribution:i,consumer:n,outcome:r});this.commit(o),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","expired"===r.type?"warning":"info","user","Sequence ended",{outcome:r}),w().emit("journey:ended",{experienceId:this.experienceId,sequenceId:G(this.stateAtom.get()),outcome:r})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==M(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){this.destroyed||("granted"!==e.type?this.transitionToEnded(e.outcome):this.transitionToGranted(e.token,e.expiresAt))}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=this.sequenceDrivers[e.type];if(s?.startMonitoring)return s.startMonitoring({participation:e,displayAtom:t,onUpdate:i}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>s.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)}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=q(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),"granted"!==M(e)||this.grantExpiryTimer)return;const t=V(e);if(!t)return;const i=t-Date.now();i<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),i)}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<H)return void this.scheduleCapabilityRefresh(H);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(H*2**this.consecutiveRefreshFailures,6e5);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!d(e)||e.code!==u.VALIDATION_ERROR||e.message!==$&&e.message!==Q)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=M(e),i=k(e),s=G(e);return"participating"===t&&i&&s?{experienceId:this.experienceId,sequenceId:s,participationId:i.id,participationType:i.type}:null}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;const t=this.getRuntimeBinding();if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),void this.setSnapshot(D(this.experienceId),{persist:!1});const i=qe(e);if(!i)return;const s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=k(i),n=M(i),o="participating"===n&&r?{sequenceId:G(i),participationId:r.id,participationType:r.type}:null;o?t&&t.sequenceId===o.sequenceId&&t.participationId===o.participationId&&t.participationType===o.participationType||this.resetDisplayAtom(o.participationType):this.displayAtom=null,this.setSnapshot(i,{persist:!1}),"participating"===n&&r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){return this.resetDisplayAtom(e.type),this.displayAtom}resetDisplayAtom(t){this.displayAtom&&this.displayAtom.get().type===t?this.displayAtom.set({type:t}):this.displayAtom=e({type:t})}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();e?this.resetDisplayAtom(e.participationType):this.displayAtom=null}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i)return i;if(!t||"closed"!==t.lifecycle||!e)return;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:{type:"closed",distributionType:t.type,distributionId:t.id}}withGrantedConsumer(e,t){switch(e?.mechanism){case"auction":return o(e,t);case"draw":return m(e,t);case"queue":return f(e,t);case"timed_release":return R(e,t);case void 0:switch(t.type){case"auction":return o(e,t);case"draw":return m(e,t);case"queue":return f(e,t);case"timed_release":return R(e,t);default:throw c.validationError("Distribution does not produce grants")}default:if(!e)throw c.validationError("Grant requires a consumer state");return e}}withTerminalConsumer(e,t){switch(e?.mechanism){case"appointment":return r(e,t);case"auction":return a(e,t);case"draw":return y(e,t);case"queue":return b(e,t);case"timed_release":return j(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}function X(e,t){if(e&&e.waitlistId===t.waitlistId)return e}const Y=i.picklist(["queue","draw","auction","timed_release","appointment"]),Z=Y,ee=i.picklist(["queue","draw","auction","timed_release"]),te=i.picklist(["draw","auction"]),ie=i.strictObject({id:i.string(),ts:i.number(),kind:i.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:i.picklist(["info","success","warning","error"]),audience:i.picklist(["user","system","analytics"]),message:i.string(),detail:i.optional(i.record(i.string(),i.unknown()))}),se=i.strictObject({id:i.string(),name:i.string(),experienceId:i.string(),priority:i.number(),color:i.string()}),re=i.strictObject({type:Y,id:i.string(),sequenceId:i.optional(i.string()),lifecycle:i.string(),startsAt:i.optional(i.string()),opensAt:i.optional(i.string()),closesAt:i.optional(i.string()),settlesAt:i.optional(i.string()),waitlistId:i.optional(i.string()),details:i.optional(i.unknown()),updatedAt:i.optional(i.number())}),ne=i.strictObject({status:i.string(),id:i.optional(i.string()),distributionId:i.optional(i.string()),waitlistId:i.optional(i.string()),enteredAt:i.optional(i.string()),admissionGrant:i.optional(i.string()),grantedAt:i.optional(i.string()),deniedReason:i.optional(i.string()),position:i.optional(i.number()),highestBid:i.optional(i.string()),currentBid:i.optional(i.string()),slotId:i.optional(i.string()),locationId:i.optional(i.string()),bookedAt:i.optional(i.string()),checkedInAt:i.optional(i.string()),completedAt:i.optional(i.string()),cancelledAt:i.optional(i.string()),noShowAt:i.optional(i.string()),notifiedAt:i.optional(i.string())}),oe=i.strictObject({token:i.string(),distributionType:ee,distributionId:i.string(),sequenceId:i.string(),experienceId:i.string(),issuedAt:i.optional(i.string()),expiresAt:i.optional(i.string()),handoffUrl:i.optional(i.string())}),ae=i.strictObject({type:i.string(),reason:i.optional(i.string()),at:i.optional(i.string()),distributionType:i.optional(Y),distributionId:i.optional(i.string())}),ce=i.strictObject({phase:i.literal("unavailable"),reason:i.optional(i.string())}),de=i.strictObject({waitlistId:i.string(),status:i.picklist(["not_waitlisted","waitlisted","notified"]),entryId:i.optional(i.string())}),ue=i.strictObject({phase:i.literal("scheduled"),mechanism:Z,distribution:re,waitlist:i.optional(de)}),he=i.strictObject({phase:i.literal("enterable"),mechanism:Z,distribution:re,consumer:ne}),pe=i.strictObject({phase:i.literal("participating"),mechanism:Z,distribution:re,consumer:ne}),le=i.strictObject({phase:i.literal("settling"),mechanism:te,distribution:re,consumer:ne}),me=i.strictObject({phase:i.literal("granted"),mechanism:ee,distribution:re,consumer:ne,grant:oe}),ye=i.strictObject({phase:i.literal("ended"),mechanism:Z,distribution:re,consumer:ne,outcome:ae}),ge=i.union([ce,ue,he,pe,le,me,ye]),fe={schemaVersion:i.literal(2),revision:i.number(),updatedAt:i.number(),experienceId:i.string(),accessCode:i.optional(i.string()),experience:i.optional(i.unknown()),events:i.array(ie),lastSeenEventId:i.optional(i.string())},be=i.strictObject({...fe,journeyStage:i.literal("ready")}),Ie=i.strictObject({...fe,journeyStage:i.literal("routing")}),ve=i.strictObject({...fe,journeyStage:i.literal("gated"),gates:i.array(i.unknown()),selected:i.optional(i.strictObject({sequence:i.unknown(),status:i.literal("gated")}))}),Se=i.strictObject({...fe,journeyStage:i.literal("routed"),sequenceId:i.string(),offers:i.array(i.unknown()),sequence:ge,sequenceRef:i.optional(se)}),Ae=i.union([be,Ie,ve,Se]);function qe(e){const t=i.safeParse(Ae,e);if(!t.success)return null;const{schemaVersion:s,...r}=t.output;if("routed"===r.journeyStage)try{W(r.sequence)}catch{return null}return r}export{K as ExperienceJourney};
@@ -1 +1 @@
1
- import{isMoneyString as e,isMoneyGreaterThan as t,isMoneyGreaterThanOrEqualTo as n}from"../core/money.js";import{MockServer as s,ResponseBuilder as o}from"./mock-server.js";const i="http://mock.local/api",a="http://mock.local/beacon",r="424242";function u(){return{isEntered:!1,enteredAt:null,status:"not_waitlisted",isOpen:!1}}function d(e,t){window.localStorage.setItem(e,JSON.stringify(t))}const c={queue:{key:"queue",label:"Queue Journey",experienceId:"experience_queue_story_1",sequenceId:"sequence_queue_story_1",queueId:"queue_queue_story_1"},"waitlist-queue":{key:"waitlist-queue",label:"Waitlist to Queue Journey",experienceId:"experience_waitlist_queue_story_1",sequenceId:"sequence_waitlist_queue_story_1",queueId:"queue_waitlist_queue_story_1",waitlistId:"waitlist_waitlist_queue_story_1"},"waitlist-lifecycle":{key:"waitlist-lifecycle",label:"Waitlist Lifecycle Journey",experienceId:"experience_waitlist_lifecycle_story_1",sequenceId:"sequence_waitlist_lifecycle_story_1",queueId:"queue_waitlist_lifecycle_story_1",waitlistId:"waitlist_waitlist_lifecycle_story_1"},"auth-queue":{key:"auth-queue",label:"Auth-Gated Queue Journey",experienceId:"experience_auth_queue_story_1",sequenceId:"sequence_auth_queue_story_1",queueId:"queue_auth_queue_story_1"},"access-code-queue":{key:"access-code-queue",label:"Access-Code Queue Journey",experienceId:"experience_access_code_queue_story_1",sequenceId:"sequence_access_code_queue_story_1",queueId:"queue_access_code_queue_story_1",accessCode:"VIP123"},"upgrade-offer-queue":{key:"upgrade-offer-queue",label:"Upgrade Offer Journey",experienceId:"experience_upgrade_offer_story_1",sequenceId:"sequence_upgrade_offer_base_story_1",upgradeSequenceId:"sequence_upgrade_offer_vip_story_1",queueId:"queue_upgrade_offer_story_1",accessCode:"VIP123"},"upgrade-offer-multi-queue":{key:"upgrade-offer-multi-queue",label:"Upgrade Offer Journey (multi-tier)",experienceId:"experience_upgrade_offer_multi_story_1",sequenceId:"sequence_upgrade_offer_multi_base_story_1",upgradeSequenceId:"sequence_upgrade_offer_multi_vip_story_1",superUpgradeSequenceId:"sequence_upgrade_offer_multi_super_story_1",queueId:"queue_upgrade_offer_multi_story_1",accessCode:"VIP123"},draw:{key:"draw",label:"Draw Journey",experienceId:"experience_draw_story_1",sequenceId:"sequence_draw_story_1",drawId:"draw_draw_story_1"},"timed-release":{key:"timed-release",label:"Timed Release Journey",experienceId:"experience_timed_release_story_1",sequenceId:"sequence_timed_release_story_1",timedReleaseId:"timed_release_story_1"},"timed-release-completion":{key:"timed-release-completion",label:"Timed Release Completion Journey",experienceId:"experience_timed_release_completion_story_1",sequenceId:"sequence_timed_release_completion_story_1",timedReleaseId:"timed_release_completion_story_1"},appointment:{key:"appointment",label:"Appointment Journey",experienceId:"experience_appointment_story_1",sequenceId:"sequence_appointment_story_1",appointmentId:"appointment_story_1"},auction:{key:"auction",label:"Auction Journey",experienceId:"experience_auction_story_1",sequenceId:"sequence_auction_story_1",auctionId:"auction_auction_story_1"},"auth-draw":{key:"auth-draw",label:"Auth-Gated Draw Journey",experienceId:"experience_auth_draw_story_1",sequenceId:"sequence_auth_draw_story_1",drawId:"draw_auth_draw_story_1"},"admission-cycle":{key:"admission-cycle",label:"Admission Cycle Journey",experienceId:"experience_admission_cycle_story_1",sequenceId:"sequence_admission_cycle_story_1",queueId:"queue_admission_cycle_story_1"},"queue-denied":{key:"queue-denied",label:"Queue Denied (not selected)",experienceId:"experience_queue_denied_story_1",sequenceId:"sequence_queue_denied_story_1",queueId:"queue_queue_denied_story_1"},"draw-denied":{key:"draw-denied",label:"Draw Denied (not selected)",experienceId:"experience_draw_denied_story_1",sequenceId:"sequence_draw_denied_story_1",drawId:"draw_draw_denied_story_1"}};function l(e){return e.startsWith("http://")||e.startsWith("https://")?e:new URL(e,window.location.origin).toString()}function _(e){if(!e)return c.queue;if("string"==typeof e)return c[e]??c.queue;return{...c[e.key]??c.queue,...e}}async function p(e){try{const t=await e.clone().json();return"object"==typeof t&&null!==t?t:void 0}catch{return}}function I(e,t){return{baseUrl:e,defaultDelay:t.defaultDelay??30,logRequests:t.logRequests??!1,fallbackToNetwork:!1}}function m(e){return function(e){const t=e.headers.get("DPoP"),n=e.headers.get("X-Admission-Key-Thumbprint");if(!t||!n)return!1;const s=t.split(".");return 3===s.length&&!s.some(e=>0===e.length)&&/^[A-Za-z0-9_-]{16,}$/.test(n)}(e)?null:o.badRequest("Missing admission proof headers")}function y(e={}){const t=e.scenarios?.length?e.scenarios.map(e=>_(e)):[_(e.scenario)],n=t[0],d=l(e.apiBaseUrl??i),c=l(e.beaconBaseUrl??a),m=e.otpCode??r,y=new s(I(d,e)),A=new s(I(c,e)),E=/* @__PURE__ */new Date,x=E.toISOString(),h=new Date(E.getTime()+864e5).toISOString(),b=new Date(E.getTime()+(e.grantExpiresInMs??864e5)).toISOString(),O={sessionType:null};!function(e,t){const n={id:"session_mock_demo",consumerId:"consumer_mock_demo",guestId:"guest_mock_demo",type:"guest",email:null,phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso};e.post("/auth/guest",()=>(t.authState.sessionType="guest",o.ok({session:n,accessToken:"guest_access_token_mock_demo",refreshToken:"guest_refresh_token_mock_demo",beaconToken:"guest_beacon_token_mock_demo"}))),e.post("/auth/refresh",()=>o.ok({accessToken:`access_token_mock_demo_${Date.now()}`,refreshToken:"guest_refresh_token_mock_demo",beaconToken:"authenticated"===t.authState.sessionType?"authenticated_beacon_token_mock_demo":"guest_beacon_token_mock_demo"})),e.post("/auth/otp/request",o.ok({ok:!0})),e.post("/auth/otp/verify",async e=>{const n=await p(e);return("string"==typeof n?.code?n.code:void 0)!==t.otpCode?o.error("Invalid verification code",401):(t.authState.sessionType="authenticated",o.ok({session:{id:"session_authenticated_mock_demo",consumerId:"consumer_authenticated_mock_demo",type:"authenticated",email:"string"==typeof n?.email?n.email:"sdk-harness@example.com",phone:"string"==typeof n?.phone?n.phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso},accessToken:"authenticated_access_token_mock_demo",refreshToken:"authenticated_refresh_token_mock_demo",beaconToken:"authenticated_beacon_token_mock_demo"}))}),e.post("/auth/external/exchange",async e=>{const n=await p(e);return("string"==typeof n?.exchangeCode?n.exchangeCode:void 0)?(t.authState.sessionType="authenticated",o.ok({session:{id:"session_external_mock_demo",consumerId:"consumer_external_mock_demo",type:"authenticated",email:"external-auth@example.com",phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso},accessToken:"external_access_token_mock_demo",refreshToken:"external_refresh_token_mock_demo",beaconToken:"authenticated_beacon_token_mock_demo"})):o.error("Invalid exchange code",401)})}(y,{nowIso:x,expiresAtIso:h,otpCode:m,authState:O}),function(e){const t=()=>o.ok({ok:!0});e.post("/events",t),e.post("/events/batch",t)}(A);const T=t.map(e=>{const t=e.waitlistId?`__fanfare_harness_waitlist__:${e.key}:${e.experienceId}`:void 0;return{scenario:e,waitlistStorageKey:t,waitlistState:e.waitlistId?function(e){const t={isEntered:!1,enteredAt:null,status:"not_waitlisted",isOpen:!1};try{const n=window.localStorage.getItem(e);if(!n)return t;const s=JSON.parse(n),o=!0===s.isEntered,i="string"==typeof s.status?s.status:void 0,a="notified"===i||"waitlisted"===i||"not_waitlisted"===i?i:o?"waitlisted":"not_waitlisted";return{isEntered:"not_waitlisted"!==a||o,enteredAt:"string"==typeof s.enteredAt?s.enteredAt:null,status:a,isOpen:!0===s.isOpen}}catch{return t}}(t??""):void 0}});var v;v=T,y.get("/consumers/me",()=>{const e=/* @__PURE__ */(new Date).toISOString(),t=v.flatMap(({scenario:t,waitlistState:n})=>{if(!t.waitlistId||!n?.isEntered||!t.queueId)return[];if("waitlist-lifecycle"===t.key&&n.isOpen)return[];const s=new Date(Date.now()+6e4).toISOString(),o="notified"===n.status?"notified":"waitlisted";return[{experienceId:t.experienceId,updatedAt:e,journeyStage:"routed",sequenceId:t.sequenceId,offers:[],sequence:{phase:"scheduled",mechanism:"queue",distribution:{type:"queue",id:t.queueId,sequenceId:t.sequenceId,lifecycle:"scheduled",startsAt:s,opensAt:s},waitlist:{waitlistId:t.waitlistId,status:o,entryId:`waitlist_entry_${t.waitlistId}`}}}]});return o.ok({active:{journeys:t}})});for(const{scenario:s,waitlistStorageKey:o,waitlistState:i}of T){if(g(y,s,{authState:O,expiresAtIso:b,waitlistState:i}),s.queueId){const e=[s.queueId,...s.upgradeSequenceId?[`${s.queueId}_vip`]:[],...s.superUpgradeSequenceId?[`${s.queueId}_super`]:[]];for(const t of e)q(y,{queueId:t,experienceId:s.experienceId,name:"Journey Queue",initialPosition:"admission-cycle"===s.key?2:5,initialEstimatedWaitSeconds:90,autoExpireAfterGrant:"admission-cycle"===s.key,forceDenyOnEnter:"queue-denied"===s.key})}s.waitlistId&&k(y,{waitlistId:s.waitlistId,sequenceId:s.sequenceId,state:i??u(),storageKey:o??""}),s.drawId&&w(y,{drawId:s.drawId,forceDenyOnEnter:"draw-denied"===s.key}),s.timedReleaseId&&S(y,{timedReleaseId:s.timedReleaseId,closeAt:new Date(Date.now()+9e5).toISOString(),grantOnEnter:"timed-release-completion"!==s.key}),s.appointmentId&&f(y,{appointmentId:s.appointmentId}),s.auctionId&&D(y,{auctionId:s.auctionId})}return y.start(),A.start(),{apiBaseUrl:d,beaconBaseUrl:c,scenario:n,scenarios:t,stop(){A.stop(),y.stop()},getRequests:()=>({api:y.getRequests(),beacon:A.getRequests()})}}function g(e,t,n){const s=(/* @__PURE__ */new Date).toISOString();e.get(`/experiences/${t.experienceId}`,o.ok({id:t.experienceId,name:t.label,createdAt:s,updatedAt:s,i18n:{en:{"start.title":t.label,"start.description":"Mock data served from the harness mock server."}}})),e.post(`/experiences/${t.experienceId}/enter`,o.ok({experienceId:t.experienceId,enteredAt:s}));const i=e=>({id:t.sequenceId,name:e,experienceId:t.experienceId,priority:1,color:"#000000"}),a=(e,n,s)=>({schemaVersion:1,experienceId:t.experienceId,outcome:"gated",selected:{sequence:e,status:"gated",gates:[n],reasons:s},offers:[],outcomeReasons:s}),r=e=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:e,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${t.sequenceId}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[],outcomeReasons:[]});if("auth-queue"===t.key||"auth-draw"===t.key)e.post(`/experiences/${t.experienceId}/route`,()=>"authenticated"!==n.authState.sessionType?o.ok(a(i("auth-draw"===t.key?"Auth-Gated Draw Sequence":"Auth-Gated Queue Sequence"),{type:"authentication",status:"required",reason:{code:"AUTH_REQUIRED"}},[{code:"AUTH_REQUIRED"}])):o.ok(r(i("auth-draw"===t.key?"Auth-Gated Draw Sequence":"Auth-Gated Queue Sequence"))));else if("access-code-queue"===t.key)e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),s="string"==typeof n?.accessCode?n.accessCode:void 0;return s?s!==t.accessCode?o.ok(a(i("Access-Code Queue Sequence"),{type:"access_code",status:"invalid",reason:{code:"ACCESS_CODE_INVALID"}},[{code:"ACCESS_CODE_INVALID"}])):o.ok(r(i("Access-Code Queue Sequence"))):o.ok(a(i("Access-Code Queue Sequence"),{type:"access_code",status:"missing",reason:{code:"ACCESS_CODE_REQUIRED"}},[{code:"ACCESS_CODE_REQUIRED"}]))});else if("upgrade-offer-queue"===t.key){const s={id:t.sequenceId,name:"General Admission",experienceId:t.experienceId,priority:1,color:"#000000"},i={id:t.upgradeSequenceId??`${t.sequenceId}_vip`,name:"VIP",experienceId:t.experienceId,priority:10,color:"#7c3aed"},a=e=>{const t="missing"===e?"ACCESS_CODE_REQUIRED":"ACCESS_CODE_INVALID";return{sequence:i,status:"gated",gates:[{type:"access_code",status:e,reason:{code:t}}],reasons:[{code:t}]}},r=e=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:s,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${t.sequenceId}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[a(e)],outcomeReasons:[]}),u=()=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:i,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${i.id}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[],outcomeReasons:[]});e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),s="string"==typeof n?.accessCode?n.accessCode:void 0;return s?s!==t.accessCode?o.ok(r("invalid")):o.ok(u()):o.ok(r("missing"))})}else if("upgrade-offer-multi-queue"===t.key){const s={id:t.sequenceId,name:"General Admission",experienceId:t.experienceId,priority:1,color:"#000000"},i={id:t.upgradeSequenceId??`${t.sequenceId}_vip`,name:"VIP",experienceId:t.experienceId,priority:10,color:"#7c3aed"},a={id:t.superUpgradeSequenceId??`${t.sequenceId}_super`,name:"Super VIP",experienceId:t.experienceId,priority:20,color:"#f59e0b"},r=(e,t)=>{const n="missing"===t?"ACCESS_CODE_REQUIRED":"ACCESS_CODE_INVALID";return{sequence:e,status:"gated",gates:[{type:"access_code",status:t,reason:{code:n}}],reasons:[{code:n}]}},u=(e,s)=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:e,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${e.id}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:s,outcomeReasons:[]}),d=()=>({schemaVersion:1,experienceId:t.experienceId,outcome:"gated",selected:{sequence:s,status:"gated",gates:[{type:"access_code",status:"invalid",reason:{code:"ACCESS_CODE_INVALID"}}],reasons:[{code:"ACCESS_CODE_INVALID"}]},offers:[],outcomeReasons:[{code:"ACCESS_CODE_INVALID"}]});e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),c="string"==typeof n?.accessCode?n.accessCode:void 0;if("LOCKDOWN"===c)return o.ok(d());if("SUPER123"===c)return o.ok(u(a,[]));if(c===t.accessCode)return o.ok(u(i,[r(a,"invalid")]));const l=c?"invalid":"missing";return o.ok(u(s,[r(a,l),r(i,l)]))})}else e.post(`/experiences/${t.experienceId}/route`,o.ok(r(i(`${t.label} Sequence`))));if("waitlist-queue"!==t.key&&"waitlist-lifecycle"!==t.key)if("draw"!==t.key&&"auth-draw"!==t.key&&"draw-denied"!==t.key)if("timed-release"!==t.key&&"timed-release-completion"!==t.key)if("appointment"!==t.key)if("auction"!==t.key){if("upgrade-offer-queue"===t.key||"upgrade-offer-multi-queue"===t.key){const n=(e,t)=>()=>{const n=/* @__PURE__ */(new Date).toISOString();return o.ok({recentPast:void 0,active:{type:"queue",id:e,status:"active",details:{id:e,type:"queue",openAt:n,metadata:{label:t},estimatedWaitTime:300,queueLength:10}},upcoming:void 0})};return e.get(`/sequences/${t.sequenceId}/distributions`,n(t.queueId??"queue_upgrade_base","General Admission Queue")),e.get(`/sequences/${t.upgradeSequenceId??`${t.sequenceId}_vip`}/distributions`,n(`${t.queueId}_vip`,"VIP Queue")),void("upgrade-offer-multi-queue"===t.key&&e.get(`/sequences/${t.superUpgradeSequenceId??`${t.sequenceId}_super`}/distributions`,n(`${t.queueId}_super`,"Super VIP Queue")))}e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=/* @__PURE__ */(new Date).toISOString();return o.ok({recentPast:void 0,active:{type:"queue",id:t.queueId,status:"active",details:{id:t.queueId,type:"queue",openAt:e,metadata:{label:"Journey Queue"},estimatedWaitTime:300,queueLength:10}},upcoming:void 0})})}else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now(),n=new Date(e).toISOString();return o.ok({recentPast:void 0,active:{type:"auction",id:t.auctionId,status:"active",details:{id:t.auctionId,type:"auction",openAt:n,endsAt:new Date(e+36e5).toISOString(),startsAt:n,currencyCode:"USD",reservePrice:"50.00",bidIncrement:"10.00"}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=t.appointmentId??"appointment_mock",n=Date.now(),s=new Date(n-6e4).toISOString(),i=new Date(n+6048e5).toISOString();return o.ok({recentPast:void 0,active:{type:"appointment",id:e,status:"active",details:{id:e,type:"appointment",openAt:s,closeAt:i,bookingWindowOpenAt:s,bookingWindowCloseAt:i,slotDurationMinutes:30,timezone:"UTC",locationId:"loc_harness_1",totalSlots:3,availableSlots:3}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now();return o.ok({recentPast:void 0,active:{type:"timed_release",id:t.timedReleaseId,status:"active",details:{id:t.timedReleaseId,type:"timed_release",openAt:new Date(e).toISOString(),closeAt:new Date(e+9e5).toISOString()}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now(),n=new Date(e+(t.drawDelayMs??72e5)).toISOString();return o.ok({recentPast:void 0,active:{type:"draw",id:t.drawId,status:"active",details:{id:t.drawId,type:"draw",openAt:new Date(e).toISOString(),drawTime:n,entryDeadline:n}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=/* @__PURE__ */(new Date).toISOString();return("waitlist-lifecycle"===t.key?!0===n.waitlistState?.isOpen:n.waitlistState?.isEntered)?o.ok({recentPast:void 0,active:{type:"queue",id:t.queueId,status:"active",details:{id:t.queueId,type:"queue",openAt:e,closeAt:new Date(Date.now()+18e5).toISOString(),metadata:{label:"Journey Queue (Window Open)"},estimatedWaitTime:300,queueLength:10}},upcoming:void 0}):o.ok({recentPast:void 0,active:void 0,upcoming:{type:"queue",id:t.queueId,startsAt:new Date(Date.now()+6e4).toISOString(),waitlistId:t.waitlistId}})})}function q(e,t){const{queueId:n,experienceId:s="experience_mock",name:i="Mock Queue",initialPosition:a=12,initialEstimatedWaitSeconds:r=300,autoExpireAfterGrant:u=!1,forceDenyOnEnter:d=!1}=t;let c=!1,l=!1,_=a,p=r,I=0;e.get(`/queues/${n}`,o.ok({id:n,experienceId:s,name:i,status:"open",capacity:500,currentSize:247,estimatedWaitTime:p,openAt:new Date(Date.now()-3e5).toISOString(),closeAt:new Date(Date.now()+36e5).toISOString()})),e.post(`/queues/${n}/enter`,e=>{const t=m(e);return t||(d?o.ok({status:"DENIED"}):(c=!0,l=!1,I=0,_=a,p=r,o.ok({position:_,estimatedWaitTimeInSeconds:p,status:"QUEUED"})))}),e.post(`/queues/${n}/leave`,()=>(c=!1,l=!1,I=0,o.ok({}))),e.get(`/queues/${n}/status`,()=>c?l?(I+=1,u&&I>1?(c=!1,l=!1,o.ok({status:"EXPIRED"})):o.ok({status:"GRANTED",admissionGrant:`admission_grant_${n}`,expiresAt:new Date(Date.now()+6e5).toISOString()})):(_=Math.max(1,_-1),p=Math.max(0,p-30),_<=1?(l=!0,I=1,o.ok({status:"GRANTED",admissionGrant:`admission_grant_${n}`,expiresAt:new Date(Date.now()+6e5).toISOString()})):o.ok({status:"QUEUED",position:_,estimatedWaitTimeInSeconds:p})):o.ok({status:"NOT_QUEUED"}))}function w(e,t){const{drawId:n,drawAtOffsetMs:s=72e5,forceDenyOnEnter:i=!1}=t;let a="NOT_ENTERED",r=0;e.get(`/draws/${n}`,()=>o.ok({id:n,openAt:new Date(Date.now()-6e4).toISOString(),closeAt:null,timeZone:"UTC",drawAt:new Date(Date.now()+s).toISOString(),capacity:null,continueSelectingUntilCompleted:null})),e.get(`/draws/${n}/status`,()=>"NOT_ENTERED"===a?o.ok({status:"NOT_ENTERED"}):(r+=1,r>=2?o.ok({status:"WON",wonAt:/* @__PURE__ */(new Date).toISOString(),admissionGrant:`admission_grant_${n}`}):o.ok({status:"ENTERED"}))),e.post(`/draws/${n}/enter`,e=>{const t=m(e);return t||(i?o.ok({status:"DENIED"}):(a="ENTERED",r=0,o.ok({status:"ENTERED"})))}),e.post(`/draws/${n}/leave`,()=>(a="NOT_ENTERED",r=0,o.ok({})))}function k(e,t){const{waitlistId:n,sequenceId:s,state:i,storageKey:a}=t;e.get(`/waitlists/${n}/status`,()=>o.ok({waitlistId:n,isEntered:i.isEntered,enteredAt:i.enteredAt??void 0})),e.post(`/waitlists/${n}/enter`,()=>(i.isEntered=!0,i.enteredAt=/* @__PURE__ */(new Date).toISOString(),i.status="waitlisted",d(a,i),o.ok({id:`waitlist_entry_${n}`,waitlistId:n,sequenceId:s,consumerId:"consumer_mock_demo",enteredAt:i.enteredAt})));const r=()=>(i.isEntered=!1,i.enteredAt=null,i.status="not_waitlisted",i.isOpen=!1,d(a,i),o.ok({}));e.delete(`/waitlists/${n}/leave`,r),e.post(`/waitlists/${n}/leave`,r)}function S(e,t){const{timedReleaseId:n,closeAt:s,grantOnEnter:i=!0}=t;let a="NOT_ENTERED",r=null,u=null,d=0;const c=()=>`admission_grant_${n}_${d+=1}`;e.get(`/timed-releases/${n}`,o.ok({id:n,openAt:new Date(Date.now()-6e4).toISOString(),closeAt:s,timeZone:"America/Los_Angeles",supportsGuest:!0})),e.get(`/timed-releases/${n}/status`,()=>"ENTERED"===a?o.ok({status:"ENTERED",enteredAt:r??/* @__PURE__ */(new Date).toISOString(),...i?{admissionGrant:c()}:{},expiresAt:u??new Date(Date.now()+6e4).toISOString()}):"LEFT"===a?o.ok({status:"LEFT",leftAt:/* @__PURE__ */(new Date).toISOString()}):o.ok({status:"NOT_ENTERED"})),e.post(`/timed-releases/${n}/enter`,async e=>{const t=await p(e),n="string"==typeof t?.variantId?t.variantId:void 0;return a="ENTERED",r=/* @__PURE__ */(new Date).toISOString(),u=new Date(Date.now()+6e4).toISOString(),o.ok({status:"ENTERED",enteredAt:r,selectedVariantId:n,...i?{admissionGrant:c()}:{},expiresAt:u})}),e.post(`/timed-releases/${n}/leave`,()=>"ENTERED"===a?o.error("Participant holds a live admission; it lapses when its window ends",409):(a="LEFT",o.ok({})))}function f(e,t){const{appointmentId:n}=t,s="loc_harness_1";let i=null;const a=(e,t,n)=>{const s=/* @__PURE__ */new Date;return new Date(Date.UTC(s.getUTCFullYear(),s.getUTCMonth(),s.getUTCDate()+e,t,n))},r=(e,t)=>({id:e,startTime:t.toISOString(),endTime:new Date(t.getTime()+18e5).toISOString(),locationId:s,locationName:"Suite 100",timezone:"UTC",capacity:4,available:3}),u=[r("slot_0900",a(1,9,0)),r("slot_1030",a(1,10,30)),r("slot_1100",a(2,11,0))],d=(e,t)=>{const s=(e=>u.find(t=>t.id===e)??u[0])(e);return{bookingId:`booking_${n}`,appointmentId:n,consumerId:"consumer_mock_demo",slotId:s.id,locationId:s.locationId,locationName:s.locationName,startTime:s.startTime,endTime:s.endTime,confirmationCode:t,status:"booked",createdAt:/* @__PURE__ */(new Date).toISOString()}};e.get(`/appointments/${n}`,o.ok({id:n,type:"appointment",openAt:new Date(Date.now()-6e4).toISOString(),closeAt:new Date(Date.now()+6048e5).toISOString(),slotDurationMinutes:30,timeZone:"UTC",locationId:s,totalSlots:u.length,availableSlots:u.length})),e.get(`/appointments/${n}/slots`,()=>o.ok({slots:u})),e.get(`/appointments/${n}/me`,()=>o.ok({booking:i})),e.post(`/appointments/${n}/book`,async e=>{const t=await p(e),n="string"==typeof t?.slotId?t.slotId:u[0].id;return i=d(n,"FF-1001"),o.ok(i)}),e.post(`/appointments/${n}/reschedule`,async e=>{const t=await p(e),n=i?.slotId??u[0].id,s="string"==typeof t?.newSlotId?t.newSlotId:u[1].id;return i=d(s,"FF-2001"),o.ok({bookingId:i.bookingId,previousSlotId:n,newSlotId:i.slotId,confirmationCode:i.confirmationCode,status:i.status})}),e.post(`/appointments/${n}/cancel`,()=>{const e=i?.bookingId??`booking_${n}`;return i=null,o.ok({message:"ok",bookingId:e})})}function D(s,i){const{auctionId:a}=i,r=/* @__PURE__ */new Date,u=r.toISOString(),d=new Date(r.getTime()+6e5).toISOString();let c="100.00",l=null,_=12,I=!1,m=[{bidId:"bid_1",consumerId:"consumer_mock_demo",amount:"80.00",timestamp:u},{bidId:"bid_2",consumerId:"consumer_mock_demo",amount:"90.00",timestamp:u}];s.get(`/auctions/${a}`,o.ok({id:a,openAt:u,closeAt:d,settleAt:d,currencyCode:"USD",reservePrice:null,minBidIncrement:"10.00",autoExtendSeconds:null,timeZone:"UTC"})),s.get(`/auctions/${a}/highest-bid`,()=>o.ok({amount:c})),s.get(`/auctions/${a}/status`,()=>{const e=null===l?"NOT_BID":n(l,c)?"WINNING":I?"OUTBID":"NOT_BID";return o.ok({status:e,lastBidAmount:l??void 0,bidCount:_,auctionEndTime:d})}),s.post(`/auctions/${a}/enter`,()=>(I=!0,o.ok({}))),s.post(`/auctions/${a}/leave`,()=>(I=!1,l=null,o.ok({}))),s.post(`/auctions/${a}/bid`,async s=>{const i=await p(s),a=i?.amount,r="string"==typeof a?a:"";return e(r)?(l=r,_+=1,t(r,c)&&(c=r),m=[...m,{bidId:`bid_${m.length+1}`,consumerId:"consumer_mock_demo",amount:r,timestamp:/* @__PURE__ */(new Date).toISOString()}],o.ok({status:n(r,c)?"WINNING":"OUTBID",lastBidAmount:r,bidCount:_,winningBidAmount:c,winningBidTime:/* @__PURE__ */(new Date).toISOString(),auctionEndTime:d,paymentToken:null})):o.badRequest("Invalid bid amount")}),s.get(`/auctions/${a}/bids/history`,()=>o.ok(m.map(e=>({...e,isHighest:e.amount===c}))))}export{i as DEFAULT_HARNESS_MOCK_API_BASE_URL,a as DEFAULT_HARNESS_MOCK_BEACON_BASE_URL,r as DEFAULT_TEST_OTP_CODE,c as HARNESS_SCENARIOS,y as installHarnessMockServer,_ as normalizeHarnessScenario};
1
+ import{isMoneyString as e,isMoneyGreaterThan as t,isMoneyGreaterThanOrEqualTo as n}from"../core/money.js";import{MockServer as s,ResponseBuilder as o}from"./mock-server.js";const i="http://mock.local/api",a="http://mock.local/beacon",r="424242";function u(){return{isEntered:!1,enteredAt:null,status:"not_waitlisted",isOpen:!1}}function d(e,t){window.localStorage.setItem(e,JSON.stringify(t))}const c={queue:{key:"queue",label:"Queue Journey",experienceId:"experience_queue_story_1",sequenceId:"sequence_queue_story_1",queueId:"queue_queue_story_1"},"waitlist-queue":{key:"waitlist-queue",label:"Waitlist to Queue Journey",experienceId:"experience_waitlist_queue_story_1",sequenceId:"sequence_waitlist_queue_story_1",queueId:"queue_waitlist_queue_story_1",waitlistId:"waitlist_waitlist_queue_story_1"},"waitlist-lifecycle":{key:"waitlist-lifecycle",label:"Waitlist Lifecycle Journey",experienceId:"experience_waitlist_lifecycle_story_1",sequenceId:"sequence_waitlist_lifecycle_story_1",queueId:"queue_waitlist_lifecycle_story_1",waitlistId:"waitlist_waitlist_lifecycle_story_1"},"auth-queue":{key:"auth-queue",label:"Auth-Gated Queue Journey",experienceId:"experience_auth_queue_story_1",sequenceId:"sequence_auth_queue_story_1",queueId:"queue_auth_queue_story_1"},"access-code-queue":{key:"access-code-queue",label:"Access-Code Queue Journey",experienceId:"experience_access_code_queue_story_1",sequenceId:"sequence_access_code_queue_story_1",queueId:"queue_access_code_queue_story_1",accessCode:"VIP123"},"upgrade-offer-queue":{key:"upgrade-offer-queue",label:"Upgrade Offer Journey",experienceId:"experience_upgrade_offer_story_1",sequenceId:"sequence_upgrade_offer_base_story_1",upgradeSequenceId:"sequence_upgrade_offer_vip_story_1",queueId:"queue_upgrade_offer_story_1",accessCode:"VIP123"},"upgrade-offer-multi-queue":{key:"upgrade-offer-multi-queue",label:"Upgrade Offer Journey (multi-tier)",experienceId:"experience_upgrade_offer_multi_story_1",sequenceId:"sequence_upgrade_offer_multi_base_story_1",upgradeSequenceId:"sequence_upgrade_offer_multi_vip_story_1",superUpgradeSequenceId:"sequence_upgrade_offer_multi_super_story_1",queueId:"queue_upgrade_offer_multi_story_1",accessCode:"VIP123"},draw:{key:"draw",label:"Draw Journey",experienceId:"experience_draw_story_1",sequenceId:"sequence_draw_story_1",drawId:"draw_draw_story_1"},"timed-release":{key:"timed-release",label:"Timed Release Journey",experienceId:"experience_timed_release_story_1",sequenceId:"sequence_timed_release_story_1",timedReleaseId:"timed_release_story_1"},"timed-release-completion":{key:"timed-release-completion",label:"Timed Release Completion Journey",experienceId:"experience_timed_release_completion_story_1",sequenceId:"sequence_timed_release_completion_story_1",timedReleaseId:"timed_release_completion_story_1"},appointment:{key:"appointment",label:"Appointment Journey",experienceId:"experience_appointment_story_1",sequenceId:"sequence_appointment_story_1",appointmentId:"appointment_story_1"},auction:{key:"auction",label:"Auction Journey",experienceId:"experience_auction_story_1",sequenceId:"sequence_auction_story_1",auctionId:"auction_auction_story_1"},"auth-draw":{key:"auth-draw",label:"Auth-Gated Draw Journey",experienceId:"experience_auth_draw_story_1",sequenceId:"sequence_auth_draw_story_1",drawId:"draw_auth_draw_story_1"},"admission-cycle":{key:"admission-cycle",label:"Admission Cycle Journey",experienceId:"experience_admission_cycle_story_1",sequenceId:"sequence_admission_cycle_story_1",queueId:"queue_admission_cycle_story_1"},"queue-denied":{key:"queue-denied",label:"Queue Denied (not selected)",experienceId:"experience_queue_denied_story_1",sequenceId:"sequence_queue_denied_story_1",queueId:"queue_queue_denied_story_1"},"draw-denied":{key:"draw-denied",label:"Draw Denied (not selected)",experienceId:"experience_draw_denied_story_1",sequenceId:"sequence_draw_denied_story_1",drawId:"draw_draw_denied_story_1"}};function l(e){return e.startsWith("http://")||e.startsWith("https://")?e:new URL(e,window.location.origin).toString()}function _(e){if(!e)return c.queue;if("string"==typeof e)return c[e]??c.queue;return{...c[e.key]??c.queue,...e}}async function p(e){try{const t=await e.clone().json();return"object"==typeof t&&null!==t?t:void 0}catch{return}}function I(e,t){return{baseUrl:e,defaultDelay:t.defaultDelay??30,logRequests:t.logRequests??!1,fallbackToNetwork:!1}}function m(e){return function(e){const t=e.headers.get("DPoP"),n=e.headers.get("X-Admission-Key-Thumbprint");if(!t||!n)return!1;const s=t.split(".");return 3===s.length&&!s.some(e=>0===e.length)&&/^[A-Za-z0-9_-]{16,}$/.test(n)}(e)?null:o.badRequest("Missing admission proof headers")}function y(e={}){const t=e.scenarios?.length?e.scenarios.map(e=>_(e)):[_(e.scenario)],n=t[0],d=l(e.apiBaseUrl??i),c=l(e.beaconBaseUrl??a),m=e.otpCode??r,y=new s(I(d,e)),A=new s(I(c,e)),E=/* @__PURE__ */new Date,x=E.toISOString(),h=new Date(E.getTime()+864e5).toISOString(),b=new Date(E.getTime()+(e.grantExpiresInMs??864e5)).toISOString(),O={sessionType:null};!function(e,t){const n={id:"session_mock_demo",consumerId:"consumer_mock_demo",type:"guest",email:null,phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso};e.post("/auth/guest",()=>(t.authState.sessionType="guest",o.ok({session:n,accessToken:"guest_access_token_mock_demo",refreshToken:"guest_refresh_token_mock_demo",beaconToken:"guest_beacon_token_mock_demo"}))),e.post("/auth/refresh",()=>o.ok({accessToken:`access_token_mock_demo_${Date.now()}`,refreshToken:"guest_refresh_token_mock_demo",beaconToken:"authenticated"===t.authState.sessionType?"authenticated_beacon_token_mock_demo":"guest_beacon_token_mock_demo"})),e.post("/auth/otp/request",o.ok({ok:!0})),e.post("/auth/otp/verify",async e=>{const n=await p(e);return("string"==typeof n?.code?n.code:void 0)!==t.otpCode?o.error("Invalid verification code",401):(t.authState.sessionType="authenticated",o.ok({session:{id:"session_authenticated_mock_demo",consumerId:"consumer_authenticated_mock_demo",type:"authenticated",email:"string"==typeof n?.email?n.email:"sdk-harness@example.com",phone:"string"==typeof n?.phone?n.phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso},accessToken:"authenticated_access_token_mock_demo",refreshToken:"authenticated_refresh_token_mock_demo",beaconToken:"authenticated_beacon_token_mock_demo"}))}),e.post("/auth/external/exchange",async e=>{const n=await p(e);return("string"==typeof n?.exchangeCode?n.exchangeCode:void 0)?(t.authState.sessionType="authenticated",o.ok({session:{id:"session_external_mock_demo",consumerId:"consumer_external_mock_demo",type:"authenticated",email:"external-auth@example.com",phone:null,createdAt:t.nowIso,expiresAt:t.expiresAtIso},accessToken:"external_access_token_mock_demo",refreshToken:"external_refresh_token_mock_demo",beaconToken:"authenticated_beacon_token_mock_demo"})):o.error("Invalid exchange code",401)})}(y,{nowIso:x,expiresAtIso:h,otpCode:m,authState:O}),function(e){const t=()=>o.ok({ok:!0});e.post("/events",t),e.post("/events/batch",t)}(A);const T=t.map(e=>{const t=e.waitlistId?`__fanfare_harness_waitlist__:${e.key}:${e.experienceId}`:void 0;return{scenario:e,waitlistStorageKey:t,waitlistState:e.waitlistId?function(e){const t={isEntered:!1,enteredAt:null,status:"not_waitlisted",isOpen:!1};try{const n=window.localStorage.getItem(e);if(!n)return t;const s=JSON.parse(n),o=!0===s.isEntered,i="string"==typeof s.status?s.status:void 0,a="notified"===i||"waitlisted"===i||"not_waitlisted"===i?i:o?"waitlisted":"not_waitlisted";return{isEntered:"not_waitlisted"!==a||o,enteredAt:"string"==typeof s.enteredAt?s.enteredAt:null,status:a,isOpen:!0===s.isOpen}}catch{return t}}(t??""):void 0}});var v;v=T,y.get("/consumers/me",()=>{const e=/* @__PURE__ */(new Date).toISOString(),t=v.flatMap(({scenario:t,waitlistState:n})=>{if(!t.waitlistId||!n?.isEntered||!t.queueId)return[];if("waitlist-lifecycle"===t.key&&n.isOpen)return[];const s=new Date(Date.now()+6e4).toISOString(),o="notified"===n.status?"notified":"waitlisted";return[{experienceId:t.experienceId,updatedAt:e,journeyStage:"routed",sequenceId:t.sequenceId,offers:[],sequence:{phase:"scheduled",mechanism:"queue",distribution:{type:"queue",id:t.queueId,sequenceId:t.sequenceId,lifecycle:"scheduled",startsAt:s,opensAt:s},waitlist:{waitlistId:t.waitlistId,status:o,entryId:`waitlist_entry_${t.waitlistId}`}}}]});return o.ok({active:{journeys:t}})});for(const{scenario:s,waitlistStorageKey:o,waitlistState:i}of T){if(g(y,s,{authState:O,expiresAtIso:b,waitlistState:i}),s.queueId){const e=[s.queueId,...s.upgradeSequenceId?[`${s.queueId}_vip`]:[],...s.superUpgradeSequenceId?[`${s.queueId}_super`]:[]];for(const t of e)q(y,{queueId:t,experienceId:s.experienceId,name:"Journey Queue",initialPosition:"admission-cycle"===s.key?2:5,initialEstimatedWaitSeconds:90,autoExpireAfterGrant:"admission-cycle"===s.key,forceDenyOnEnter:"queue-denied"===s.key})}s.waitlistId&&k(y,{waitlistId:s.waitlistId,sequenceId:s.sequenceId,state:i??u(),storageKey:o??""}),s.drawId&&w(y,{drawId:s.drawId,forceDenyOnEnter:"draw-denied"===s.key}),s.timedReleaseId&&S(y,{timedReleaseId:s.timedReleaseId,closeAt:new Date(Date.now()+9e5).toISOString(),grantOnEnter:"timed-release-completion"!==s.key}),s.appointmentId&&f(y,{appointmentId:s.appointmentId}),s.auctionId&&D(y,{auctionId:s.auctionId})}return y.start(),A.start(),{apiBaseUrl:d,beaconBaseUrl:c,scenario:n,scenarios:t,stop(){A.stop(),y.stop()},getRequests:()=>({api:y.getRequests(),beacon:A.getRequests()})}}function g(e,t,n){const s=(/* @__PURE__ */new Date).toISOString();e.get(`/experiences/${t.experienceId}`,o.ok({id:t.experienceId,name:t.label,createdAt:s,updatedAt:s,i18n:{en:{"start.title":t.label,"start.description":"Mock data served from the harness mock server."}}})),e.post(`/experiences/${t.experienceId}/enter`,o.ok({experienceId:t.experienceId,enteredAt:s}));const i=e=>({id:t.sequenceId,name:e,experienceId:t.experienceId,priority:1,color:"#000000"}),a=(e,n,s)=>({schemaVersion:1,experienceId:t.experienceId,outcome:"gated",selected:{sequence:e,status:"gated",gates:[n],reasons:s},offers:[],outcomeReasons:s}),r=e=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:e,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${t.sequenceId}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[],outcomeReasons:[]});if("auth-queue"===t.key||"auth-draw"===t.key)e.post(`/experiences/${t.experienceId}/route`,()=>"authenticated"!==n.authState.sessionType?o.ok(a(i("auth-draw"===t.key?"Auth-Gated Draw Sequence":"Auth-Gated Queue Sequence"),{type:"authentication",status:"required",reason:{code:"AUTH_REQUIRED"}},[{code:"AUTH_REQUIRED"}])):o.ok(r(i("auth-draw"===t.key?"Auth-Gated Draw Sequence":"Auth-Gated Queue Sequence"))));else if("access-code-queue"===t.key)e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),s="string"==typeof n?.accessCode?n.accessCode:void 0;return s?s!==t.accessCode?o.ok(a(i("Access-Code Queue Sequence"),{type:"access_code",status:"invalid",reason:{code:"ACCESS_CODE_INVALID"}},[{code:"ACCESS_CODE_INVALID"}])):o.ok(r(i("Access-Code Queue Sequence"))):o.ok(a(i("Access-Code Queue Sequence"),{type:"access_code",status:"missing",reason:{code:"ACCESS_CODE_REQUIRED"}},[{code:"ACCESS_CODE_REQUIRED"}]))});else if("upgrade-offer-queue"===t.key){const s={id:t.sequenceId,name:"General Admission",experienceId:t.experienceId,priority:1,color:"#000000"},i={id:t.upgradeSequenceId??`${t.sequenceId}_vip`,name:"VIP",experienceId:t.experienceId,priority:10,color:"#7c3aed"},a=e=>{const t="missing"===e?"ACCESS_CODE_REQUIRED":"ACCESS_CODE_INVALID";return{sequence:i,status:"gated",gates:[{type:"access_code",status:e,reason:{code:t}}],reasons:[{code:t}]}},r=e=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:s,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${t.sequenceId}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[a(e)],outcomeReasons:[]}),u=()=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:i,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${i.id}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:[],outcomeReasons:[]});e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),s="string"==typeof n?.accessCode?n.accessCode:void 0;return s?s!==t.accessCode?o.ok(r("invalid")):o.ok(u()):o.ok(r("missing"))})}else if("upgrade-offer-multi-queue"===t.key){const s={id:t.sequenceId,name:"General Admission",experienceId:t.experienceId,priority:1,color:"#000000"},i={id:t.upgradeSequenceId??`${t.sequenceId}_vip`,name:"VIP",experienceId:t.experienceId,priority:10,color:"#7c3aed"},a={id:t.superUpgradeSequenceId??`${t.sequenceId}_super`,name:"Super VIP",experienceId:t.experienceId,priority:20,color:"#f59e0b"},r=(e,t)=>{const n="missing"===t?"ACCESS_CODE_REQUIRED":"ACCESS_CODE_INVALID";return{sequence:e,status:"gated",gates:[{type:"access_code",status:t,reason:{code:n}}],reasons:[{code:n}]}},u=(e,s)=>({schemaVersion:1,experienceId:t.experienceId,outcome:"routed",selected:{sequence:e,status:"admissible",gates:[],reasons:[],sequenceCapabilityGrant:`mock_sequence_capability_grant_${e.id}`,sequenceCapabilityGrantExpiresAt:n.expiresAtIso},offers:s,outcomeReasons:[]}),d=()=>({schemaVersion:1,experienceId:t.experienceId,outcome:"gated",selected:{sequence:s,status:"gated",gates:[{type:"access_code",status:"invalid",reason:{code:"ACCESS_CODE_INVALID"}}],reasons:[{code:"ACCESS_CODE_INVALID"}]},offers:[],outcomeReasons:[{code:"ACCESS_CODE_INVALID"}]});e.post(`/experiences/${t.experienceId}/route`,async e=>{const n=await p(e),c="string"==typeof n?.accessCode?n.accessCode:void 0;if("LOCKDOWN"===c)return o.ok(d());if("SUPER123"===c)return o.ok(u(a,[]));if(c===t.accessCode)return o.ok(u(i,[r(a,"invalid")]));const l=c?"invalid":"missing";return o.ok(u(s,[r(a,l),r(i,l)]))})}else e.post(`/experiences/${t.experienceId}/route`,o.ok(r(i(`${t.label} Sequence`))));if("waitlist-queue"!==t.key&&"waitlist-lifecycle"!==t.key)if("draw"!==t.key&&"auth-draw"!==t.key&&"draw-denied"!==t.key)if("timed-release"!==t.key&&"timed-release-completion"!==t.key)if("appointment"!==t.key)if("auction"!==t.key){if("upgrade-offer-queue"===t.key||"upgrade-offer-multi-queue"===t.key){const n=(e,t)=>()=>{const n=/* @__PURE__ */(new Date).toISOString();return o.ok({recentPast:void 0,active:{type:"queue",id:e,status:"active",details:{id:e,type:"queue",openAt:n,metadata:{label:t},estimatedWaitTime:300,queueLength:10}},upcoming:void 0})};return e.get(`/sequences/${t.sequenceId}/distributions`,n(t.queueId??"queue_upgrade_base","General Admission Queue")),e.get(`/sequences/${t.upgradeSequenceId??`${t.sequenceId}_vip`}/distributions`,n(`${t.queueId}_vip`,"VIP Queue")),void("upgrade-offer-multi-queue"===t.key&&e.get(`/sequences/${t.superUpgradeSequenceId??`${t.sequenceId}_super`}/distributions`,n(`${t.queueId}_super`,"Super VIP Queue")))}e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=/* @__PURE__ */(new Date).toISOString();return o.ok({recentPast:void 0,active:{type:"queue",id:t.queueId,status:"active",details:{id:t.queueId,type:"queue",openAt:e,metadata:{label:"Journey Queue"},estimatedWaitTime:300,queueLength:10}},upcoming:void 0})})}else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now(),n=new Date(e).toISOString();return o.ok({recentPast:void 0,active:{type:"auction",id:t.auctionId,status:"active",details:{id:t.auctionId,type:"auction",openAt:n,endsAt:new Date(e+36e5).toISOString(),startsAt:n,currencyCode:"USD",reservePrice:"50.00",bidIncrement:"10.00"}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=t.appointmentId??"appointment_mock",n=Date.now(),s=new Date(n-6e4).toISOString(),i=new Date(n+6048e5).toISOString();return o.ok({recentPast:void 0,active:{type:"appointment",id:e,status:"active",details:{id:e,type:"appointment",openAt:s,closeAt:i,bookingWindowOpenAt:s,bookingWindowCloseAt:i,slotDurationMinutes:30,timezone:"UTC",locationId:"loc_harness_1",totalSlots:3,availableSlots:3}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now();return o.ok({recentPast:void 0,active:{type:"timed_release",id:t.timedReleaseId,status:"active",details:{id:t.timedReleaseId,type:"timed_release",openAt:new Date(e).toISOString(),closeAt:new Date(e+9e5).toISOString()}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=Date.now(),n=new Date(e+(t.drawDelayMs??72e5)).toISOString();return o.ok({recentPast:void 0,active:{type:"draw",id:t.drawId,status:"active",details:{id:t.drawId,type:"draw",openAt:new Date(e).toISOString(),drawTime:n,entryDeadline:n}},upcoming:void 0})});else e.get(`/sequences/${t.sequenceId}/distributions`,()=>{const e=/* @__PURE__ */(new Date).toISOString();return("waitlist-lifecycle"===t.key?!0===n.waitlistState?.isOpen:n.waitlistState?.isEntered)?o.ok({recentPast:void 0,active:{type:"queue",id:t.queueId,status:"active",details:{id:t.queueId,type:"queue",openAt:e,closeAt:new Date(Date.now()+18e5).toISOString(),metadata:{label:"Journey Queue (Window Open)"},estimatedWaitTime:300,queueLength:10}},upcoming:void 0}):o.ok({recentPast:void 0,active:void 0,upcoming:{type:"queue",id:t.queueId,startsAt:new Date(Date.now()+6e4).toISOString(),waitlistId:t.waitlistId}})})}function q(e,t){const{queueId:n,experienceId:s="experience_mock",name:i="Mock Queue",initialPosition:a=12,initialEstimatedWaitSeconds:r=300,autoExpireAfterGrant:u=!1,forceDenyOnEnter:d=!1}=t;let c=!1,l=!1,_=a,p=r,I=0;e.get(`/queues/${n}`,o.ok({id:n,experienceId:s,name:i,status:"open",capacity:500,currentSize:247,estimatedWaitTime:p,openAt:new Date(Date.now()-3e5).toISOString(),closeAt:new Date(Date.now()+36e5).toISOString()})),e.post(`/queues/${n}/enter`,e=>{const t=m(e);return t||(d?o.ok({status:"DENIED"}):(c=!0,l=!1,I=0,_=a,p=r,o.ok({position:_,estimatedWaitTimeInSeconds:p,status:"QUEUED"})))}),e.post(`/queues/${n}/leave`,()=>(c=!1,l=!1,I=0,o.ok({}))),e.get(`/queues/${n}/status`,()=>c?l?(I+=1,u&&I>1?(c=!1,l=!1,o.ok({status:"EXPIRED"})):o.ok({status:"GRANTED",admissionGrant:`admission_grant_${n}`,expiresAt:new Date(Date.now()+6e5).toISOString()})):(_=Math.max(1,_-1),p=Math.max(0,p-30),_<=1?(l=!0,I=1,o.ok({status:"GRANTED",admissionGrant:`admission_grant_${n}`,expiresAt:new Date(Date.now()+6e5).toISOString()})):o.ok({status:"QUEUED",position:_,estimatedWaitTimeInSeconds:p})):o.ok({status:"NOT_QUEUED"}))}function w(e,t){const{drawId:n,drawAtOffsetMs:s=72e5,forceDenyOnEnter:i=!1}=t;let a="NOT_ENTERED",r=0;e.get(`/draws/${n}`,()=>o.ok({id:n,openAt:new Date(Date.now()-6e4).toISOString(),closeAt:null,timeZone:"UTC",drawAt:new Date(Date.now()+s).toISOString(),capacity:null,continueSelectingUntilCompleted:null})),e.get(`/draws/${n}/status`,()=>"NOT_ENTERED"===a?o.ok({status:"NOT_ENTERED"}):(r+=1,r>=2?o.ok({status:"WON",wonAt:/* @__PURE__ */(new Date).toISOString(),admissionGrant:`admission_grant_${n}`}):o.ok({status:"ENTERED"}))),e.post(`/draws/${n}/enter`,e=>{const t=m(e);return t||(i?o.ok({status:"DENIED"}):(a="ENTERED",r=0,o.ok({status:"ENTERED"})))}),e.post(`/draws/${n}/leave`,()=>(a="NOT_ENTERED",r=0,o.ok({})))}function k(e,t){const{waitlistId:n,sequenceId:s,state:i,storageKey:a}=t;e.get(`/waitlists/${n}/status`,()=>o.ok({waitlistId:n,isEntered:i.isEntered,enteredAt:i.enteredAt??void 0})),e.post(`/waitlists/${n}/enter`,()=>(i.isEntered=!0,i.enteredAt=/* @__PURE__ */(new Date).toISOString(),i.status="waitlisted",d(a,i),o.ok({id:`waitlist_entry_${n}`,waitlistId:n,sequenceId:s,consumerId:"consumer_mock_demo",enteredAt:i.enteredAt})));const r=()=>(i.isEntered=!1,i.enteredAt=null,i.status="not_waitlisted",i.isOpen=!1,d(a,i),o.ok({}));e.delete(`/waitlists/${n}/leave`,r),e.post(`/waitlists/${n}/leave`,r)}function S(e,t){const{timedReleaseId:n,closeAt:s,grantOnEnter:i=!0}=t;let a="NOT_ENTERED",r=null,u=null,d=0;const c=()=>`admission_grant_${n}_${d+=1}`;e.get(`/timed-releases/${n}`,o.ok({id:n,openAt:new Date(Date.now()-6e4).toISOString(),closeAt:s,timeZone:"America/Los_Angeles",supportsGuest:!0})),e.get(`/timed-releases/${n}/status`,()=>"ENTERED"===a?o.ok({status:"ENTERED",enteredAt:r??/* @__PURE__ */(new Date).toISOString(),...i?{admissionGrant:c()}:{},expiresAt:u??new Date(Date.now()+6e4).toISOString()}):"LEFT"===a?o.ok({status:"LEFT",leftAt:/* @__PURE__ */(new Date).toISOString()}):o.ok({status:"NOT_ENTERED"})),e.post(`/timed-releases/${n}/enter`,async e=>{const t=await p(e),n="string"==typeof t?.variantId?t.variantId:void 0;return a="ENTERED",r=/* @__PURE__ */(new Date).toISOString(),u=new Date(Date.now()+6e4).toISOString(),o.ok({status:"ENTERED",enteredAt:r,selectedVariantId:n,...i?{admissionGrant:c()}:{},expiresAt:u})}),e.post(`/timed-releases/${n}/leave`,()=>"ENTERED"===a?o.error("Participant holds a live admission; it lapses when its window ends",409):(a="LEFT",o.ok({})))}function f(e,t){const{appointmentId:n}=t,s="loc_harness_1";let i=null;const a=(e,t,n)=>{const s=/* @__PURE__ */new Date;return new Date(Date.UTC(s.getUTCFullYear(),s.getUTCMonth(),s.getUTCDate()+e,t,n))},r=(e,t)=>({id:e,startTime:t.toISOString(),endTime:new Date(t.getTime()+18e5).toISOString(),locationId:s,locationName:"Suite 100",timezone:"UTC",capacity:4,available:3}),u=[r("slot_0900",a(1,9,0)),r("slot_1030",a(1,10,30)),r("slot_1100",a(2,11,0))],d=(e,t)=>{const s=(e=>u.find(t=>t.id===e)??u[0])(e);return{bookingId:`booking_${n}`,appointmentId:n,consumerId:"consumer_mock_demo",slotId:s.id,locationId:s.locationId,locationName:s.locationName,startTime:s.startTime,endTime:s.endTime,confirmationCode:t,status:"booked",createdAt:/* @__PURE__ */(new Date).toISOString()}};e.get(`/appointments/${n}`,o.ok({id:n,type:"appointment",openAt:new Date(Date.now()-6e4).toISOString(),closeAt:new Date(Date.now()+6048e5).toISOString(),slotDurationMinutes:30,timeZone:"UTC",locationId:s,totalSlots:u.length,availableSlots:u.length})),e.get(`/appointments/${n}/slots`,()=>o.ok({slots:u})),e.get(`/appointments/${n}/me`,()=>o.ok({booking:i})),e.post(`/appointments/${n}/book`,async e=>{const t=await p(e),n="string"==typeof t?.slotId?t.slotId:u[0].id;return i=d(n,"FF-1001"),o.ok(i)}),e.post(`/appointments/${n}/reschedule`,async e=>{const t=await p(e),n=i?.slotId??u[0].id,s="string"==typeof t?.newSlotId?t.newSlotId:u[1].id;return i=d(s,"FF-2001"),o.ok({bookingId:i.bookingId,previousSlotId:n,newSlotId:i.slotId,confirmationCode:i.confirmationCode,status:i.status})}),e.post(`/appointments/${n}/cancel`,()=>{const e=i?.bookingId??`booking_${n}`;return i=null,o.ok({message:"ok",bookingId:e})})}function D(s,i){const{auctionId:a}=i,r=/* @__PURE__ */new Date,u=r.toISOString(),d=new Date(r.getTime()+6e5).toISOString();let c="100.00",l=null,_=12,I=!1,m=[{bidId:"bid_1",consumerId:"consumer_mock_demo",amount:"80.00",timestamp:u},{bidId:"bid_2",consumerId:"consumer_mock_demo",amount:"90.00",timestamp:u}];s.get(`/auctions/${a}`,o.ok({id:a,openAt:u,closeAt:d,settleAt:d,currencyCode:"USD",reservePrice:null,minBidIncrement:"10.00",autoExtendSeconds:null,timeZone:"UTC"})),s.get(`/auctions/${a}/highest-bid`,()=>o.ok({amount:c})),s.get(`/auctions/${a}/status`,()=>{const e=null===l?"NOT_BID":n(l,c)?"WINNING":I?"OUTBID":"NOT_BID";return o.ok({status:e,lastBidAmount:l??void 0,bidCount:_,auctionEndTime:d})}),s.post(`/auctions/${a}/enter`,()=>(I=!0,o.ok({}))),s.post(`/auctions/${a}/leave`,()=>(I=!1,l=null,o.ok({}))),s.post(`/auctions/${a}/bid`,async s=>{const i=await p(s),a=i?.amount,r="string"==typeof a?a:"";return e(r)?(l=r,_+=1,t(r,c)&&(c=r),m=[...m,{bidId:`bid_${m.length+1}`,consumerId:"consumer_mock_demo",amount:r,timestamp:/* @__PURE__ */(new Date).toISOString()}],o.ok({status:n(r,c)?"WINNING":"OUTBID",lastBidAmount:r,bidCount:_,winningBidAmount:c,winningBidTime:/* @__PURE__ */(new Date).toISOString(),auctionEndTime:d,paymentToken:null})):o.badRequest("Invalid bid amount")}),s.get(`/auctions/${a}/bids/history`,()=>o.ok(m.map(e=>({...e,isHighest:e.amount===c}))))}export{i as DEFAULT_HARNESS_MOCK_API_BASE_URL,a as DEFAULT_HARNESS_MOCK_BEACON_BASE_URL,r as DEFAULT_TEST_OTP_CODE,c as HARNESS_SCENARIOS,y as installHarnessMockServer,_ as normalizeHarnessScenario};
@@ -1 +1 @@
1
- import{atom as e,computed as t}from"nanostores";import{projectAppointmentSequence as n,createAppointmentDriver as i}from"../appointments/appointment.driver.js";import{projectAuctionSequence as a,createAuctionDriver as r}from"../auctions/auction.driver.js";import{projectDrawSequence as s,createDrawDriver as o}from"../draws/draw.driver.js";import{buildJourneyView as c}from"../experiences/journey-view.js";import{projectQueueSequence as u,createQueueDriver as p}from"../queues/queue.driver.js";import{projectTimedReleaseSequence as m,createTimedReleaseDriver as d}from"../timed-releases/timed-release.driver.js";function l(e){if("routed"!==e.journeyStage||"participating"!==e.sequence.phase)return{type:"queue"};switch(e.sequence.mechanism){case"queue":return{type:"queue"};case"draw":return{type:"draw"};case"auction":return{type:"auction"};case"timed_release":return{type:"timed_release"};case"appointment":return{type:"appointment"}}}function v(e="exp_123",t){return{revision:1,updatedAt:Date.now(),journeyStage:"ready",experienceId:e,experience:void 0,events:[],lastSeenEventId:void 0,...t}}function g(e){return{...v(e?.experienceId),journeyStage:"gated",gates:e?.gates??[],selected:e?.selected,...e?.overrides}}function y(e){const t=e.sequence&&"mechanism"in e.sequence?e.sequence:function(e){if(!e.distribution)return{phase:"unavailable"};switch(e.distribution.type){case"appointment":return n(e);case"auction":return a(e);case"draw":return s(e);case"queue":return u(e);case"timed_release":return m(e)}}({distribution:e.distribution,consumer:e.consumer??e.overrides?.consumer,grant:e.grant??e.overrides?.grant,outcome:e.outcome??e.overrides?.outcome}),{consumer:i,grant:r,outcome:o,actions:c,sequence:p,...d}=e.overrides??{};return{...v(e.experienceId),journeyStage:"routed",sequenceId:e.sequenceId??"seq_1",offers:e.offers??[],sequence:t,...d}}function h(t){const n=e(t??v());let i=0;return{atom:n,get:()=>n.get(),listen:e=>{i+=1;const t=n.listen(e);return()=>{i-=1,t()}},subscribe:e=>{i+=1;const t=n.subscribe(e);return()=>{i-=1,t()}},setValue:e=>n.set(e),getListenerCount:()=>i}}function b(n){const a=n?.initialSnapshot??v(),s=h(a),u=e(l(s.get())),m=n?.actions??{},g={start:async()=>{},reroute:async()=>{},submitAccessCode:async()=>({outcome:"unchanged"}),retry:async()=>{},enter:async()=>{},leave:async()=>{},joinWaitlist:async()=>{},exitWaitlist:async()=>{},bid:async()=>{},complete:async()=>{},book:async()=>{},cancel:async()=>{},reschedule:async()=>{},claim:()=>{const e=s.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.grant;throw new Error("No grant is available to claim")},...n?.handlers,...m},y=function(t){const n=p({queues:{enter:async()=>({status:"QUEUED",position:0,estimatedWaitTimeInSeconds:0}),leave:async()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),a=d({timedReleases:{enter:async()=>({status:"NOT_ENTERED"}),leave:async()=>{},complete:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),s=o({draws:{enter:async()=>({status:"ENTERED"}),leave:async()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),c=r({auctions:{enter:async()=>{},leave:async()=>{},status:async()=>({auctionId:"auction_1",status:"watching",highestBid:"0.00",timeRemaining:0}),bid:async(e,t)=>({status:"accepted",amount:t,highestBid:t,bidCount:1}),startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>e({type:"auction"}),stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),u=i({appointments:{book:async(e,t,n)=>({bookingId:"booking_1",appointmentId:e,slotId:t,locationId:n,startTime:new Date(Date.now()+6e4).toISOString(),endTime:new Date(Date.now()+12e4).toISOString(),consumerStatus:"booked"}),cancel:async()=>{},reschedule:async(e,t,n)=>({bookingId:"booking_1",appointmentId:e,slotId:t,locationId:n,startTime:new Date(Date.now()+6e4).toISOString(),endTime:new Date(Date.now()+12e4).toISOString(),consumerStatus:"booked"}),bumpMonitorGeneration:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},getDisplayState:()=>({type:"appointment"}),commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>e({type:"appointment"}),stopRuntimeMonitoring:()=>{},emitEvent:()=>{}});return{appointment:{mechanism:"appointment",project:u.project.bind(u),buildView:(e,n)=>{const i=u.buildView(e,n);return"enterable"===i.phase&&"appointment"===i.mechanism?{...i,book:(e,n)=>t.book(e,n)}:"participating"===i.phase&&"appointment"===i.mechanism?{...i,cancel:e=>t.cancel(e),reschedule:(e,n)=>t.reschedule(e,n)}:i}},auction:{mechanism:"auction",project:c.project.bind(c),buildView:(e,n)=>{const i=c.buildView(e,n);return"enterable"===i.phase&&"auction"===i.mechanism?{...i,bid:e=>t.bid(e)}:"participating"===i.phase&&"auction"===i.mechanism?{...i,bid:e=>t.bid(e),leave:()=>t.leave()}:i},enter:()=>t.enter(),leave:()=>t.leave()},queue:{mechanism:"queue",project:n.project.bind(n),buildView:(e,i)=>{const a=n.buildView(e,i);return"enterable"===a.phase&&"queue"===a.mechanism?{...a,enter:()=>t.enter()}:"participating"===a.phase&&"queue"===a.mechanism?{...a,leave:()=>t.leave()}:a},enter:()=>t.enter(),leave:()=>t.leave()},timed_release:{mechanism:"timed_release",project:a.project.bind(a),buildView:(e,n)=>{const i=a.buildView(e,n);return"enterable"===i.phase&&"timed_release"===i.mechanism?{...i,enter:()=>t.enter()}:"participating"===i.phase&&"timed_release"===i.mechanism?{...i,leave:()=>t.leave(),complete:()=>t.complete()}:i},enter:()=>t.enter(),leave:()=>t.leave()},draw:{mechanism:"draw",project:s.project.bind(s),buildView:(e,n)=>{const i=s.buildView(e,n);return"enterable"===i.phase&&"draw"===i.mechanism?{...i,enter:()=>t.enter()}:"participating"===i.phase&&"draw"===i.mechanism?{...i,leave:()=>t.leave()}:i},enter:()=>t.enter(),leave:()=>t.leave()}}}(g),b=t(s.atom,e=>c(e,g,u,y)),w=t(s.atom,e=>e.events),S=t(s.atom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",n=e.events.filter(e=>e.id>t);return n.length?n[n.length-1]:null}),q=e=>{if(m.ackEvent)return void m.ackEvent(e);const t=s.get();t.events.some(t=>t.id===e)&&s.setValue({...t,lastSeenEventId:e})},E=()=>{if(m.ackAllEvents)return void m.ackAllEvents();const e=s.get(),t=e.events[e.events.length-1];t&&s.setValue({...e,lastSeenEventId:t.id})},f=()=>{if(m.destroy)return void m.destroy();const e=s.get().experienceId||a.experienceId;s.setValue(v(e)),u.set(l(v(e)))};return{handle:{view$:b,snapshot$:s.atom,events$:w,latestEvent$:S,ackEvent:q,ackAllEvents:E,destroy:f},snapshot$:s,view$:b,events$:w,latestEvent$:S,handlers:g,setSnapshot:e=>{s.setValue(e)},updateSnapshot:e=>{const t=e(s.get());s.setValue(t)},setDisplayState:e=>u.set(e),ackEvent:q,ackAllEvents:E,destroy:f}}export{b as createMockJourneyHandle,h as createMockJourneyStore,g as makeGatedSnapshot,v as makeReadySnapshot,y as makeRoutedSnapshot};
1
+ import{atom as e,computed as t}from"nanostores";import{projectAppointmentSequence as n,createAppointmentDriver as i}from"../appointments/appointment.driver.js";import{projectAuctionSequence as a,createAuctionDriver as r}from"../auctions/auction.driver.js";import{projectDrawSequence as s,createDrawDriver as o}from"../draws/draw.driver.js";import{buildJourneyView as c}from"../experiences/journey-view.js";import{projectQueueSequence as u,createQueueDriver as p}from"../queues/queue.driver.js";import{projectTimedReleaseSequence as m,createTimedReleaseDriver as l}from"../timed-releases/timed-release.driver.js";function d(e){if("routed"!==e.journeyStage||"participating"!==e.sequence.phase)return{type:"queue"};switch(e.sequence.mechanism){case"queue":return{type:"queue"};case"draw":return{type:"draw"};case"auction":return{type:"auction"};case"timed_release":return{type:"timed_release"};case"appointment":return{type:"appointment"}}}function v(e="exp_123",t){return{revision:1,updatedAt:Date.now(),journeyStage:"ready",experienceId:e,experience:void 0,events:[],lastSeenEventId:void 0,...t}}function g(e){return{...v(e?.experienceId),journeyStage:"gated",gates:e?.gates??[],selected:e?.selected,...e?.overrides}}function y(e){const t=e.sequence&&"mechanism"in e.sequence?e.sequence:function(e){if(!e.distribution)return{phase:"unavailable"};switch(e.distribution.type){case"appointment":return n(e);case"auction":return a(e);case"draw":return s(e);case"queue":return u(e);case"timed_release":return m(e)}}({distribution:e.distribution,consumer:e.consumer??e.overrides?.consumer,grant:e.grant??e.overrides?.grant,outcome:e.outcome??e.overrides?.outcome}),{consumer:i,grant:r,outcome:o,actions:c,sequence:p,...l}=e.overrides??{};return{...v(e.experienceId),journeyStage:"routed",sequenceId:e.sequenceId??"seq_1",offers:e.offers??[],sequence:t,...l}}function h(t){const n=e(t??v());let i=0;return{atom:n,get:()=>n.get(),listen:e=>{i+=1;const t=n.listen(e);return()=>{i-=1,t()}},subscribe:e=>{i+=1;const t=n.subscribe(e);return()=>{i-=1,t()}},setValue:e=>n.set(e),getListenerCount:()=>i}}function b(n){const a=n?.initialSnapshot??v(),s=h(a),u=e(d(s.get())),m=n?.actions??{},g={start:async()=>{},reroute:async()=>{},submitAccessCode:async()=>({outcome:"unchanged"}),retry:async()=>{},enter:async()=>{},leave:async()=>{},joinWaitlist:async()=>{},exitWaitlist:async()=>{},bid:async()=>{},complete:async()=>{},book:async()=>{},cancel:async()=>{},reschedule:async()=>{},claim:()=>{const e=s.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.grant;throw new Error("No grant is available to claim")},...n?.handlers,...m},y=function(t){const n=p({queues:{enter:async()=>({status:"QUEUED",position:0,estimatedWaitTimeInSeconds:0}),leave:async()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),a=l({timedReleases:{enter:async()=>({status:"NOT_ENTERED"}),leave:async()=>{},complete:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),s=o({draws:{enter:async()=>({status:"ENTERED"}),leave:async()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>{},stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),c=r({auctions:{enter:async()=>{},leave:async()=>{},status:async()=>({auctionId:"auction_1",status:"watching",highestBid:"0.00",timeRemaining:0}),bid:async(e,t)=>({status:"accepted",amount:t,highestBid:t,bidCount:1}),startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},getDisplayState:()=>null,commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>e({type:"auction"}),stopRuntimeMonitoring:()=>{},emitEvent:()=>{}}),u=i({appointments:{book:async(e,t,n)=>({bookingId:"booking_1",appointmentId:e,slotId:t,locationId:n,startTime:new Date(Date.now()+6e4).toISOString(),endTime:new Date(Date.now()+12e4).toISOString(),consumerStatus:"booked"}),cancel:async()=>{},reschedule:async(e,t,n)=>({bookingId:"booking_1",appointmentId:e,slotId:t,locationId:n,startTime:new Date(Date.now()+6e4).toISOString(),endTime:new Date(Date.now()+12e4).toISOString(),consumerStatus:"booked"}),bumpMonitorGeneration:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{}},runSerializedOperation:async e=>e(),getCurrentSequence:()=>{},getDisplayState:()=>({type:"appointment"}),commit:()=>{},applyMonitorUpdate:()=>{},ensureDisplayAtom:()=>e({type:"appointment"}),stopRuntimeMonitoring:()=>{},emitEvent:()=>{}});return{appointment:{mechanism:"appointment",project:u.project.bind(u),buildView:(e,n)=>{const i=u.buildView(e,n);return"enterable"===i.phase&&"appointment"===i.mechanism?{...i,book:(e,n)=>t.book(e,n)}:"participating"===i.phase&&"appointment"===i.mechanism?{...i,cancel:e=>t.cancel(e),reschedule:(e,n)=>t.reschedule(e,n)}:i}},auction:{mechanism:"auction",project:c.project.bind(c),buildView:(e,n)=>{const i=c.buildView(e,n);return"enterable"===i.phase&&"auction"===i.mechanism?{...i,bid:e=>t.bid(e)}:"participating"===i.phase&&"auction"===i.mechanism?{...i,bid:e=>t.bid(e),leave:()=>t.leave()}:i},enter:()=>t.enter(),leave:()=>t.leave()},queue:{mechanism:"queue",project:n.project.bind(n),buildView:(e,i)=>{const a=n.buildView(e,i);return"enterable"===a.phase&&"queue"===a.mechanism?{...a,enter:()=>t.enter()}:"participating"===a.phase&&"queue"===a.mechanism?{...a,leave:()=>t.leave()}:a},enter:()=>t.enter(),leave:()=>t.leave()},timed_release:{mechanism:"timed_release",project:a.project.bind(a),buildView:(e,n)=>{const i=a.buildView(e,n);return"enterable"===i.phase&&"timed_release"===i.mechanism?{...i,enter:()=>t.enter()}:"participating"===i.phase&&"timed_release"===i.mechanism?{...i,leave:()=>t.leave(),complete:()=>t.complete()}:i},enter:()=>t.enter(),leave:()=>t.leave()},draw:{mechanism:"draw",project:s.project.bind(s),buildView:(e,n)=>{const i=s.buildView(e,n);return"enterable"===i.phase&&"draw"===i.mechanism?{...i,enter:()=>t.enter()}:"participating"===i.phase&&"draw"===i.mechanism?{...i,leave:()=>t.leave()}:i},enter:()=>t.enter(),leave:()=>t.leave()}}}(g),b=t(s.atom,e=>c(e,g,u,y)),w=t(s.atom,e=>e.events),S=t(s.atom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",n=e.events.filter(e=>e.id>t);return n.length?n[n.length-1]:null}),q=e=>{if(m.ackEvent)return void m.ackEvent(e);const t=s.get();t.events.some(t=>t.id===e)&&s.setValue({...t,lastSeenEventId:e})},E=()=>{if(m.ackAllEvents)return void m.ackAllEvents();const e=s.get(),t=e.events[e.events.length-1];t&&s.setValue({...e,lastSeenEventId:t.id})},f=()=>{if(m.destroy)return void m.destroy();const e=s.get().experienceId||a.experienceId;s.setValue(v(e)),u.set(d(v(e)))};return{handle:{view$:b,snapshot$:s.atom,events$:w,latestEvent$:S,ackEvent:q,ackAllEvents:E,destroy:f},snapshot$:s,view$:b,events$:w,latestEvent$:S,handlers:g,setSnapshot:e=>{s.setValue(e)},updateSnapshot:e=>{const t=e(s.get());s.setValue(t)},setDisplayState:e=>u.set(e),ackEvent:q,ackAllEvents:E,destroy:f}}export{b as createMockJourneyHandle,h as createMockJourneyStore,g as makeGatedSnapshot,v as makeReadySnapshot,y as makeRoutedSnapshot};
@@ -1 +1 @@
1
- import{createMockJourneyHandle as e,makeReadySnapshot as t}from"./mock-journey.js";const n={type:"guest",consumerId:"consumer_guest_123",guestId:"guest_123",email:void 0,expiresAt:new Date(Date.now()+36e5).toISOString()},s={type:"authenticated",consumerId:"consumer_auth_123",email:"test@example.com",expiresAt:new Date(Date.now()+36e5).toISOString()},i={id:"queue_test_123",experienceId:"experience_test_123",name:"Test Queue",status:"open",currentSize:0},a={position:1,estimatedWaitTimeInSeconds:60,status:"QUEUED"},o={status:"NOT_QUEUED"},c={id:"auction_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:new Date(Date.now()+6e4).toISOString(),settleAt:new Date(Date.now()+6e4).toISOString(),currencyCode:"USD",reservePrice:null,minBidIncrement:"1.00",autoExtendSeconds:null,timeZone:"UTC"},r={auctionId:"auction_test_123",status:"watching",highestBid:"0.00",timeRemaining:6e4},u={id:"draw_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:null,timeZone:"UTC",drawAt:new Date(Date.now()+6e4).toISOString(),capacity:null,continueSelectingUntilCompleted:null},d={status:"NOT_ENTERED"},g={id:"waitlist_entry_123",waitlistId:"waitlist_test_123",sequenceId:"sequence_test_123",consumerId:"consumer_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()},l={waitlistId:"waitlist_test_123",isEntered:!1},y={id:"timed_release_test_123",timeZone:"UTC",supportsGuest:!1},m={status:"NOT_ENTERED"},S={id:"experience_test_123",name:"Test Experience",createdAt:/* @__PURE__ */(new Date).toISOString(),updatedAt:/* @__PURE__ */(new Date).toISOString()},p={experienceId:"experience_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()};function _(){const i=e({initialSnapshot:t(S.id)}).handle;return{auth:{check:()=>({isAuthenticated:!1}),guest:async()=>n,requestOtp:async()=>{},verifyOtp:async()=>s,exchangeExternal:async()=>s,login:async()=>{},logout:async()=>{},getSession:()=>null,refresh:async()=>{}},challenges:{initiate:async()=>({}),verify:async()=>({status:"failed",attemptsRemaining:0})},journeys:{get:()=>i,list:()=>[],resumeAll:async()=>[]},beacon:{track:async()=>{},trackBatch:async()=>{},flush:async()=>{}},appointments:{get:async()=>({id:"appt_mock",type:"appointment"}),getSlots:async()=>[],getMe:async()=>null,book:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),cancel:async()=>{},reschedule:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,bumpMonitorGeneration:()=>{},destroy:()=>{}},on:()=>()=>{},use:()=>{},destroy:async()=>{}}}function I(e={}){return{..._(),...e}}function w(n={}){return{...{..._(),queues:{get:async()=>i,enter:async()=>a,leave:async()=>{},status:async()=>o,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},auctions:{get:async()=>c,bid:async()=>({status:"accepted",amount:"10.00",highestBid:"10.00",bidCount:1}),enter:async()=>{},leave:async()=>{},status:async()=>r,getBidHistory:async()=>[],enableAutoRebid:()=>{},disableAutoRebid:()=>{},getAutoRebidConfig:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,destroy:()=>{}},draws:{get:async()=>u,enter:async()=>({status:"ENTERED"}),leave:async()=>{},status:async()=>d,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},waitlists:{enter:async()=>g,leave:async()=>{},getStatus:async()=>l,destroy:()=>{}},timedReleases:{get:async()=>y,enter:async()=>({status:"ENTERED",enteredAt:/* @__PURE__ */(new Date).toISOString(),admissionGrant:"mock_tr_grant",expiresAt:new Date(Date.now()+6e4).toISOString()}),leave:async()=>{},complete:()=>"mock_tr_grant",status:async()=>m,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},experiences:{get:async()=>S,enter:async()=>p,leave:async()=>{},findSequence:async()=>({outcome:"routed",selected:{sequence:{id:"sequence_test_123",name:"Test Sequence",experienceId:"experience_test_123",priority:1,color:"#000000"},status:"admissible",gates:[]},offers:[]}),validateSequenceAccess:async()=>({sequenceId:"sequence_test_123",hasAccess:!0,accessReasons:[],deniedReasons:[]}),selectSequence:async()=>{},getCurrentDistributions:async()=>({}),enterDistribution:async()=>{},getActiveSession:()=>null,isInExperience:()=>!1,getSelectedSequence:()=>null,getMe:async()=>({active:{journeys:[]}}),destroy:()=>{},createJourney:n=>e({initialSnapshot:t(n)}).handle,resumeJourneysFromMe:async()=>[]}},...n}}export{I as createMockFanfareSDK,w as createMockInternalFanfareSDK,c as mockAuction,r as mockAuctionStatus,u as mockDraw,d as mockDrawStatus,S as mockExperience,p as mockExperienceSession,i as mockQueue,a as mockQueueEnterResult,o as mockQueueStatus,y as mockTimedRelease,m as mockTimedReleaseStatus,g as mockWaitlistEntry,l as mockWaitlistStatus};
1
+ import{createMockJourneyHandle as e,makeReadySnapshot as t}from"./mock-journey.js";const n={type:"guest",consumerId:"consumer_guest_123",email:void 0,expiresAt:new Date(Date.now()+36e5).toISOString()},s={type:"authenticated",consumerId:"consumer_auth_123",email:"test@example.com",expiresAt:new Date(Date.now()+36e5).toISOString()},i={id:"queue_test_123",experienceId:"experience_test_123",name:"Test Queue",status:"open",currentSize:0},a={position:1,estimatedWaitTimeInSeconds:60,status:"QUEUED"},o={status:"NOT_QUEUED"},c={id:"auction_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:new Date(Date.now()+6e4).toISOString(),settleAt:new Date(Date.now()+6e4).toISOString(),currencyCode:"USD",reservePrice:null,minBidIncrement:"1.00",autoExtendSeconds:null,timeZone:"UTC"},r={auctionId:"auction_test_123",status:"watching",highestBid:"0.00",timeRemaining:6e4},u={id:"draw_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:null,timeZone:"UTC",drawAt:new Date(Date.now()+6e4).toISOString(),capacity:null,continueSelectingUntilCompleted:null},d={status:"NOT_ENTERED"},l={id:"waitlist_entry_123",waitlistId:"waitlist_test_123",sequenceId:"sequence_test_123",consumerId:"consumer_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()},y={waitlistId:"waitlist_test_123",isEntered:!1},g={id:"timed_release_test_123",timeZone:"UTC",supportsGuest:!1},m={status:"NOT_ENTERED"},S={id:"experience_test_123",name:"Test Experience",createdAt:/* @__PURE__ */(new Date).toISOString(),updatedAt:/* @__PURE__ */(new Date).toISOString()},p={experienceId:"experience_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()};function _(){const i=e({initialSnapshot:t(S.id)}).handle;return{auth:{check:()=>({isAuthenticated:!1}),guest:async()=>n,requestOtp:async()=>{},verifyOtp:async()=>s,exchangeExternal:async()=>s,login:async()=>{},logout:async()=>{},getSession:()=>null,refresh:async()=>{}},challenges:{initiate:async()=>({}),verify:async()=>({status:"failed",attemptsRemaining:0})},journeys:{get:()=>i,list:()=>[],resumeAll:async()=>[]},beacon:{track:async()=>{},trackBatch:async()=>{},flush:async()=>{}},appointments:{get:async()=>({id:"appt_mock",type:"appointment"}),getSlots:async()=>[],getMe:async()=>null,book:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),cancel:async()=>{},reschedule:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,bumpMonitorGeneration:()=>{},destroy:()=>{}},on:()=>()=>{},use:()=>{},destroy:async()=>{}}}function I(e={}){return{..._(),...e}}function w(n={}){return{...{..._(),queues:{get:async()=>i,enter:async()=>a,leave:async()=>{},status:async()=>o,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},auctions:{get:async()=>c,bid:async()=>({status:"accepted",amount:"10.00",highestBid:"10.00",bidCount:1}),enter:async()=>{},leave:async()=>{},status:async()=>r,getBidHistory:async()=>[],enableAutoRebid:()=>{},disableAutoRebid:()=>{},getAutoRebidConfig:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,destroy:()=>{}},draws:{get:async()=>u,enter:async()=>({status:"ENTERED"}),leave:async()=>{},status:async()=>d,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},waitlists:{enter:async()=>l,leave:async()=>{},getStatus:async()=>y,destroy:()=>{}},timedReleases:{get:async()=>g,enter:async()=>({status:"ENTERED",enteredAt:/* @__PURE__ */(new Date).toISOString(),admissionGrant:"mock_tr_grant",expiresAt:new Date(Date.now()+6e4).toISOString()}),leave:async()=>{},complete:()=>"mock_tr_grant",status:async()=>m,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},experiences:{get:async()=>S,enter:async()=>p,leave:async()=>{},findSequence:async()=>({outcome:"routed",selected:{sequence:{id:"sequence_test_123",name:"Test Sequence",experienceId:"experience_test_123",priority:1,color:"#000000"},status:"admissible",gates:[]},offers:[]}),validateSequenceAccess:async()=>({sequenceId:"sequence_test_123",hasAccess:!0,accessReasons:[],deniedReasons:[]}),selectSequence:async()=>{},getCurrentDistributions:async()=>({}),enterDistribution:async()=>{},getActiveSession:()=>null,isInExperience:()=>!1,getSelectedSequence:()=>null,getMe:async()=>({active:{journeys:[]}}),destroy:()=>{},createJourney:n=>e({initialSnapshot:t(n)}).handle,resumeJourneysFromMe:async()=>[]}},...n}}export{I as createMockFanfareSDK,w as createMockInternalFanfareSDK,c as mockAuction,r as mockAuctionStatus,u as mockDraw,d as mockDrawStatus,S as mockExperience,p as mockExperienceSession,i as mockQueue,a as mockQueueEnterResult,o as mockQueueStatus,g as mockTimedRelease,m as mockTimedReleaseStatus,l as mockWaitlistEntry,y as mockWaitlistStatus};
package/dist/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * SDK version
3
3
  * This is automatically updated during the build process
4
4
  */
5
- export declare const version = "0.8.0";
5
+ export declare const version = "0.9.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- const o="0.8.0";export{o as version};
1
+ const o="0.9.0";export{o as version};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanfare-io/fanfare-sdk-core",
3
- "version": "0.8.0",
3
+ "version": "0.9.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",
@@ -122,7 +122,7 @@
122
122
  "uuid": "^14.0.0",
123
123
  "wretch": "^2.11.0",
124
124
  "zustand": "^5.0.6",
125
- "@fanfare-io/fanfare-sdk-contracts": "0.8.0"
125
+ "@fanfare-io/fanfare-sdk-contracts": "0.9.0"
126
126
  },
127
127
  "peerDependencies": {
128
128
  "valibot": "^1.1.0",
@@ -165,7 +165,7 @@
165
165
  "vite-plugin-dts": "^4.5.4",
166
166
  "vite-tsconfig-paths": "^5.1.4",
167
167
  "vitest": "^3.2.4",
168
- "@fanfare-io/shared-models": "0.8.0"
168
+ "@fanfare-io/shared-models": "0.9.0"
169
169
  },
170
170
  "sideEffects": false,
171
171
  "keywords": [