@fanfare-io/fanfare-sdk-core 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- const e={"experience:entered":"fanfare_experience_entered","experience:left":"fanfare_experience_left","experience:distribution-entered":"fanfare_distribution_entered","queue:entered":"fanfare_queue_entered","queue:granted":"fanfare_queue_granted","queue:left":"fanfare_queue_left","queue:expired":"fanfare_queue_expired","draw:entered":"fanfare_draw_entered","draw:won":"fanfare_draw_won","draw:lost":"fanfare_draw_lost","auction:entered":"fanfare_auction_entered","auction:bid-placed":"fanfare_auction_bid_placed","auction:outbid":"fanfare_auction_outbid","auction:won":"fanfare_auction_won","auction:lost":"fanfare_auction_lost","waitlist:entered":"fanfare_waitlist_entered","waitlist:left":"fanfare_waitlist_left","timed_release:entered":"fanfare_timed_release_entered","timed_release:completed":"fanfare_timed_release_completed","auth:authenticated":"fanfare_auth_authenticated"},t=Object.keys(e),i={"experience:entered":e=>({experience_id:String(e.experienceId??"")}),"experience:left":e=>({experience_id:String(e.experienceId??"")}),"experience:distribution-entered":e=>({distribution_type:String(e.distributionType??""),distribution_id:String(e.distributionId??"")}),"queue:entered":e=>({queue_id:String(e.queueId??""),position:Number(e.position??0),...null!=e.estimatedWaitTime?{estimated_wait_time:Number(e.estimatedWaitTime)}:{}}),"queue:granted":e=>({queue_id:String(e.queueId??"")}),"queue:left":e=>({queue_id:String(e.queueId??""),...e.reason?{reason:String(e.reason)}:{}}),"queue:expired":e=>({queue_id:String(e.queueId??"")}),"draw:entered":e=>({draw_id:String(e.drawId??""),...e.entryNumber?{entry_number:String(e.entryNumber)}:{}}),"draw:won":e=>({draw_id:String(e.drawId??"")}),"draw:lost":e=>({draw_id:String(e.drawId??"")}),"auction:entered":e=>({auction_id:String(e.auctionId??"")}),"auction:bid-placed":e=>({auction_id:String(e.auctionId??""),amount:String(e.amount??""),status:String(e.status??"")}),"auction:outbid":e=>({auction_id:String(e.auctionId??""),your_bid:String(e.yourBid??""),highest_bid:String(e.highestBid??"")}),"auction:won":e=>({auction_id:String(e.auctionId??""),winning_bid:String(e.winningBid??"")}),"auction:lost":e=>({auction_id:String(e.auctionId??""),highest_bid:String(e.highestBid??"")}),"waitlist:entered":e=>({waitlist_id:String(e.waitlistId??""),sequence_id:String(e.sequenceId??"")}),"waitlist:left":e=>({waitlist_id:String(e.waitlistId??"")}),"timed_release:entered":e=>({timed_release_id:String(e.timedReleaseId??"")}),"timed_release:completed":e=>({timed_release_id:String(e.timedReleaseId??"")}),"auth:authenticated":e=>({is_new:Boolean(e.isNew)})};function n(n){const r=[];return{id:"google-analytics",init(a,d){if(r.length>0)throw new Error("GA adapter already initialized. Create a new adapter instance per SDK via createGAAdapter().");const u=[...new Set(d?.events??n?.events??t)];for(const t of u){const d=e[t];if(!d)continue;const u=a.on(t,e=>{if("undefined"==typeof window||"function"!=typeof window.gtag)return;const r=i[t],a={fanfare_event:t,...r?r(e):{},...n?.measurementId?{send_to:n.measurementId}:{}};window.gtag("event",d,a)});r.push(u)}},destroy(){for(const e of r)e();r.length=0}}}export{n as createGAAdapter};
1
+ const e={"experience:entered":"fanfare_experience_entered","experience:left":"fanfare_experience_left","experience:distribution-entered":"fanfare_distribution_entered","queue:entered":"fanfare_queue_entered","queue:granted":"fanfare_queue_granted","queue:left":"fanfare_queue_left","queue:expired":"fanfare_queue_expired","draw:entered":"fanfare_draw_entered","draw:won":"fanfare_draw_won","draw:lost":"fanfare_draw_lost","auction:entered":"fanfare_auction_entered","auction:bid-placed":"fanfare_auction_bid_placed","auction:outbid":"fanfare_auction_outbid","auction:won":"fanfare_auction_won","auction:lost":"fanfare_auction_lost","waitlist:entered":"fanfare_waitlist_entered","waitlist:left":"fanfare_waitlist_left","timed_release:entered":"fanfare_timed_release_entered","timed_release:completed":"fanfare_timed_release_completed","auth:authenticated":"fanfare_auth_authenticated"},t=Object.keys(e),i={"experience:entered":e=>({experience_id:String(e.experienceId??"")}),"experience:left":e=>({experience_id:String(e.experienceId??"")}),"experience:distribution-entered":e=>({distribution_type:String(e.distributionType??""),distribution_id:String(e.distributionId??"")}),"queue:entered":e=>({queue_id:String(e.queueId??""),...null!=e.position?{position:Number(e.position)}:{},...null!=e.estimatedWaitTime?{estimated_wait_time:Number(e.estimatedWaitTime)}:{}}),"queue:granted":e=>({queue_id:String(e.queueId??"")}),"queue:left":e=>({queue_id:String(e.queueId??""),...e.reason?{reason:String(e.reason)}:{}}),"queue:expired":e=>({queue_id:String(e.queueId??"")}),"draw:entered":e=>({draw_id:String(e.drawId??""),...e.entryNumber?{entry_number:String(e.entryNumber)}:{}}),"draw:won":e=>({draw_id:String(e.drawId??"")}),"draw:lost":e=>({draw_id:String(e.drawId??"")}),"auction:entered":e=>({auction_id:String(e.auctionId??"")}),"auction:bid-placed":e=>({auction_id:String(e.auctionId??""),amount:String(e.amount??""),status:String(e.status??"")}),"auction:outbid":e=>({auction_id:String(e.auctionId??""),your_bid:String(e.yourBid??""),highest_bid:String(e.highestBid??"")}),"auction:won":e=>({auction_id:String(e.auctionId??""),winning_bid:String(e.winningBid??"")}),"auction:lost":e=>({auction_id:String(e.auctionId??""),highest_bid:String(e.highestBid??"")}),"waitlist:entered":e=>({waitlist_id:String(e.waitlistId??""),sequence_id:String(e.sequenceId??"")}),"waitlist:left":e=>({waitlist_id:String(e.waitlistId??"")}),"timed_release:entered":e=>({timed_release_id:String(e.timedReleaseId??"")}),"timed_release:completed":e=>({timed_release_id:String(e.timedReleaseId??"")}),"auth:authenticated":e=>({is_new:Boolean(e.isNew)})};function n(n){const r=[];return{id:"google-analytics",init(a,d){if(r.length>0)throw new Error("GA adapter already initialized. Create a new adapter instance per SDK via createGAAdapter().");const u=[...new Set(d?.events??n?.events??t)];for(const t of u){const d=e[t];if(!d)continue;const u=a.on(t,e=>{if("undefined"==typeof window||"function"!=typeof window.gtag)return;const r=i[t],a={fanfare_event:t,...r?r(e):{},...n?.measurementId?{send_to:n.measurementId}:{}};window.gtag("event",d,a)});r.push(u)}},destroy(){for(const e of r)e();r.length=0}}}export{n as createGAAdapter};
@@ -1 +1 @@
1
- import{AuctionDistributionResults as t}from"@fanfare-io/fanfare-sdk-contracts";import{createError as e}from"../core/errors.js";import{readEntrySelectionRequirement as i}from"../experiences/selection-requirement.js";import{buildGrantPaymentAffordance as n}from"../experiences/sequence-driver.js";import{isGrantPendingWin as s}from"./auction-status.js";import{isUnresolvedAuctionConsumer as o}from"./auction.sequence.js";function r(t){return!t||("lost"===t.type||"closed"===t.type)&&!t.reason}function a(t){return new d(t)}function c(i){const{distribution:n,consumer:s,grant:a,settlement:c,selection:u,outcome:d}=i;if(!n)return{phase:"unavailable"};const l=function(t){if("auction"!==t.type)throw e.validationError("Auction driver received a non-auction distribution");return t}(n),h=s&&"auction"===s.mechanism?function(t){const{mechanism:e,...i}=t;return i}(s):p(n);if(a)return"completed"===d?.type?m(l,h,d):a.expiresAt&&Date.now()>new Date(a.expiresAt).getTime()?"open"===l.lifecycle?{phase:"enterable",mechanism:"auction",distribution:l,consumer:p(l)}:m(l,h,{type:"expired"}):c&&u?{phase:"granted",mechanism:"auction",distribution:l,consumer:h,grant:a,settlement:c,selection:u}:{phase:"unavailable"};if("closed"===l.lifecycle){if("not_bid"===h.status&&l.result===t.FLOOR_UNSOLD&&r(d))return m(l,h,{type:"lost",reason:t.FLOOR_UNSOLD});if("auction"===s?.mechanism&&!d)return m(l,h,{type:"lost"})}const b=d??function(t){return"lost"===t.status?{type:"lost"}:void 0}(h);return b?m(l,h,b):"settling"===l.lifecycle?{phase:"settling",mechanism:"auction",distribution:l,consumer:h}:o(h)?{phase:"participating",mechanism:"auction",distribution:l,consumer:h}:"scheduled"===l.lifecycle?{phase:"scheduled",mechanism:"auction",distribution:l}:"open"===l.lifecycle?{phase:"enterable",mechanism:"auction",distribution:l,consumer:h}:m(l,h,{type:"closed"})}function u(t,e,i){return t&&"auction"===t.mechanism&&e?"won"===e.type?{...t,status:"won"}:!i?.finalize&&"not_bid"===t.status&&r(e)?t:{...t,status:"lost"}:t}class d{constructor(t){this.deps=t,this.mechanism="auction"}project(t){return c(t)}buildView(t,s){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const o=function(t){if(!("mechanism"in t)||"auction"!==t.mechanism)throw e.validationError("Auction driver received a non-auction sequence");return t}(t);switch(o.phase){case"scheduled":return{phase:"scheduled",mechanism:"auction",distribution:o.distribution,startsAt:o.distribution.startsAt??o.distribution.opensAt};case"enterable":{const t={phase:"enterable",mechanism:"auction",distribution:o.distribution,consumer:o.consumer,display$:s.displayAtom};return i(o.distribution.details)?{...t,selection:{required:!0},loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)}:{...t,selection:{required:!1},bid:t=>this.bid(t)}}case"participating":return{phase:"participating",mechanism:"auction",consumer:o.consumer,display$:s.displayAtom,loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)};case"settling":return{phase:"settling",mechanism:"auction",distribution:o.distribution,consumer:o.consumer};case"granted":{const t={phase:"granted",mechanism:"auction",distribution:o.distribution,consumer:o.consumer,grant:o.grant},{settlement:e}=o,i=o.selection;if("awaiting_assignment"===i.status)return{...t,settlement:e,selection:i};if("required"===i.status)return{...t,settlement:e,selection:i,select:s.select,loadSelectionOptions:s.loadSelectionOptions};if("internal"===e.processor&&"at_end"===e.mode){const r=n(e,o.pendingAction,s);return r?{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim(),payment:r}:{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}}return"external"===e.processor?{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}:{phase:"unavailable",reason:"checkout_misconfigured"}}case"ended":return{phase:"ended",mechanism:"auction",consumer:o.consumer,outcome:o.outcome}}}startMonitoring(t){this.activeMonitorId=t.participation.id,this.deps.auctions.startMonitoring(t.participation.id,{displayAtom:t.displayAtom},t.onUpdate,t.onDegradedChange)}stopMonitoring(){this.activeMonitorId&&(this.deps.auctions.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async bid(t){await this.deps.runSerializedOperation(async()=>{const n=this.currentAuctionSequenceForBid(),o=n.consumer.distributionId??n.consumer.id??n.distribution.id,r="auction"===n.distribution.details?.type?n.distribution.details:void 0,a=n.consumer.selection,u=i(n.distribution.details),d="enterable"===n.phase;if(d&&u&&!t.selection)throw e.validationError("A product selection is required to enter");const p=d?u?t.selection:void 0:a,{selection:m,...h}=t,b=p?{...h,selection:p}:h;l(b,r?.auctionType);const f=await this.deps.auctions.bid(o,b),g=this.deps.getCurrentSequence(),y=g&&("enterable"===g.phase||"participating"===g.phase||"settling"===g.phase)&&"mechanism"in g&&"auction"===g.mechanism&&g.distribution.id===o;if("won"===f.status||"lost"===f.status)return void(y&&("won"===f.status&&f.admissionGrant?this.deps.applyMonitorUpdate({type:"granted",token:f.admissionGrant}):"lost"===f.status?this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"lost"}}):s(f)||this.deps.applyMonitorUpdate({type:"resync"})));if(!y)return;const v=function(t){switch(t){case"not_bid":case"winning":case"outbid":return t;case"won":case"lost":return}}(f.status);if(void 0===v)return;const w=void 0!==f.selection?.variantId?{productId:f.selection.productId,variantId:f.selection.variantId}:p,S=a??w;this.deps.commit(c({distribution:n.distribution,consumer:{..."participating"===n.phase?n.consumer:{},mechanism:"auction",status:v,id:o,distributionId:o,...S?{selection:S}:{},currentBid:f.amount,highestBid:f.highestBid}}))})}currentAuctionSequenceForBid(){const t=this.deps.getCurrentSequence();if(!t||"enterable"!==t.phase&&"participating"!==t.phase||!("mechanism"in t)||"auction"!==t.mechanism)throw e.validationError("No auction is available to bid on");return t}}function l(t,i){if("dutch"!==i){if(void 0===t.amount)throw e.validationError("A bid amount is required for an English auction")}else if(void 0!==t.amount)throw e.validationError("A Dutch auction clinches at the current price; omit the bid amount")}function p(t){return{status:"not_bid",distributionId:t.id}}function m(t,e,i){return{phase:"ended",mechanism:"auction",distribution:t,consumer:e,outcome:{...i,distributionType:i.distributionType??t.type,distributionId:i.distributionId??t.id,at:i.at??/* @__PURE__ */(new Date).toISOString()}}}export{l as assertBidInputMatchesMechanism,a as createAuctionDriver,r as isUpgradeableGenericLoss,c as projectAuctionSequence,u as withAuctionTerminalConsumer};
1
+ import{AuctionDistributionResults as t}from"@fanfare-io/fanfare-sdk-contracts";import{createError as e}from"../core/errors.js";import{readEntrySelectionRequirement as i}from"../experiences/selection-requirement.js";import{buildGrantPaymentAffordance as n}from"../experiences/sequence-driver.js";import{isGrantPendingWin as s}from"./auction-status.js";import{isUnresolvedAuctionConsumer as o}from"./auction.sequence.js";function r(t){return!t||("lost"===t.type||"closed"===t.type)&&!t.reason}function a(t){return new d(t)}function c(i){const{distribution:n,consumer:s,grant:a,settlement:c,selection:u,outcome:d}=i;if(!n)return{phase:"unavailable"};const l=function(t){if("auction"!==t.type)throw e.validationError("Auction driver received a non-auction distribution");return t}(n),h=s&&"auction"===s.mechanism?function(t){const{mechanism:e,...i}=t;return i}(s):p(n);if(a)return"completed"===d?.type?m(l,h,d):a.expiresAt&&Date.now()>new Date(a.expiresAt).getTime()?"open"===l.lifecycle?{phase:"enterable",mechanism:"auction",distribution:l,consumer:p(l)}:m(l,h,{type:"expired"}):c&&u?{phase:"granted",mechanism:"auction",distribution:l,consumer:h,grant:a,settlement:c,selection:u}:{phase:"unavailable"};if("closed"===l.lifecycle){if("not_bid"===h.status&&l.result===t.FLOOR_UNSOLD&&r(d))return m(l,h,{type:"lost",reason:t.FLOOR_UNSOLD});if("auction"===s?.mechanism&&!d)return m(l,h,{type:"lost"})}const b=d??function(t){return"lost"===t.status?{type:"lost"}:void 0}(h);return b?m(l,h,b):"settling"===l.lifecycle?{phase:"settling",mechanism:"auction",distribution:l,consumer:h}:o(h)?{phase:"participating",mechanism:"auction",distribution:l,consumer:h}:"scheduled"===l.lifecycle?{phase:"scheduled",mechanism:"auction",distribution:l}:"open"===l.lifecycle?{phase:"enterable",mechanism:"auction",distribution:l,consumer:h}:m(l,h,{type:"closed"})}function u(t,e,i){return t&&"auction"===t.mechanism&&e?"won"===e.type?{...t,status:"won"}:!i?.finalize&&"not_bid"===t.status&&r(e)?t:{...t,status:"lost"}:t}class d{constructor(t){this.deps=t,this.mechanism="auction",this.settlementCanStartWhileActive=!0}project(t){return c(t)}buildView(t,s){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const o=function(t){if(!("mechanism"in t)||"auction"!==t.mechanism)throw e.validationError("Auction driver received a non-auction sequence");return t}(t);switch(o.phase){case"scheduled":return{phase:"scheduled",mechanism:"auction",distribution:o.distribution,startsAt:o.distribution.startsAt??o.distribution.opensAt};case"enterable":{const t={phase:"enterable",mechanism:"auction",distribution:o.distribution,consumer:o.consumer,display$:s.displayAtom};return i(o.distribution.details)?{...t,selection:{required:!0},loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)}:{...t,selection:{required:!1},bid:t=>this.bid(t)}}case"participating":return{phase:"participating",mechanism:"auction",consumer:o.consumer,display$:s.displayAtom,loadSelectionOptions:s.loadSelectionOptions,bid:t=>this.bid(t)};case"settling":return{phase:"settling",mechanism:"auction",distribution:o.distribution,consumer:o.consumer};case"granted":{const t={phase:"granted",mechanism:"auction",distribution:o.distribution,consumer:o.consumer,grant:o.grant},{settlement:e}=o,i=o.selection;if("awaiting_assignment"===i.status)return{...t,settlement:e,selection:i};if("required"===i.status)return{...t,settlement:e,selection:i,select:s.select,loadSelectionOptions:s.loadSelectionOptions};if("internal"===e.processor&&"at_end"===e.mode){const r=n(e,o.pendingAction,s);return r?{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim(),payment:r}:{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}}return"external"===e.processor?{...t,settlement:e,selection:i,loadSelectionOptions:s.loadSelectionOptions,select:s.select,claim:()=>s.claim()}:{phase:"unavailable",reason:"checkout_misconfigured"}}case"ended":return{phase:"ended",mechanism:"auction",consumer:o.consumer,outcome:o.outcome}}}startMonitoring(t){this.activeMonitorId=t.participation.id,this.deps.auctions.startMonitoring(t.participation.id,{displayAtom:t.displayAtom},t.onUpdate,t.onDegradedChange)}stopMonitoring(){this.activeMonitorId&&(this.deps.auctions.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async bid(t){await this.deps.runSerializedOperation(async()=>{const n=this.currentAuctionSequenceForBid(),o=n.consumer.distributionId??n.consumer.id??n.distribution.id,r="auction"===n.distribution.details?.type?n.distribution.details:void 0,a=n.consumer.selection,u=i(n.distribution.details),d="enterable"===n.phase;if(d&&u&&!t.selection)throw e.validationError("A product selection is required to enter");const p=d?u?t.selection:void 0:a,{selection:m,...h}=t,b=p?{...h,selection:p}:h;l(b,r?.auctionType);const f=await this.deps.auctions.bid(o,b),g=this.deps.getCurrentSequence(),y=g&&("enterable"===g.phase||"participating"===g.phase||"settling"===g.phase)&&"mechanism"in g&&"auction"===g.mechanism&&g.distribution.id===o;if("won"===f.status||"lost"===f.status)return void(y&&("won"===f.status&&f.admissionGrant?this.deps.applyMonitorUpdate({type:"granted",token:f.admissionGrant}):"lost"===f.status?this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"lost"}}):s(f)||this.deps.applyMonitorUpdate({type:"resync"})));if(!y)return;const v=function(t){switch(t){case"not_bid":case"winning":case"outbid":return t;case"won":case"lost":return}}(f.status);if(void 0===v)return;const w=void 0!==f.selection?.variantId?{productId:f.selection.productId,variantId:f.selection.variantId}:p,S=a??w;this.deps.commit(c({distribution:n.distribution,consumer:{..."participating"===n.phase?n.consumer:{},mechanism:"auction",status:v,id:o,distributionId:o,...S?{selection:S}:{},currentBid:f.amount,highestBid:f.highestBid}}))})}currentAuctionSequenceForBid(){const t=this.deps.getCurrentSequence();if(!t||"enterable"!==t.phase&&"participating"!==t.phase||!("mechanism"in t)||"auction"!==t.mechanism)throw e.validationError("No auction is available to bid on");return t}}function l(t,i){if("dutch"!==i){if(void 0===t.amount)throw e.validationError("A bid amount is required for an English auction")}else if(void 0!==t.amount)throw e.validationError("A Dutch auction clinches at the current price; omit the bid amount")}function p(t){return{status:"not_bid",distributionId:t.id}}function m(t,e,i){return{phase:"ended",mechanism:"auction",distribution:t,consumer:e,outcome:{...i,distributionType:i.distributionType??t.type,distributionId:i.distributionId??t.id,at:i.at??/* @__PURE__ */(new Date).toISOString()}}}export{l as assertBidInputMatchesMechanism,a as createAuctionDriver,r as isUpgradeableGenericLoss,c as projectAuctionSequence,u as withAuctionTerminalConsumer};
@@ -1 +1 @@
1
- import*as e from"valibot";import{FanfareError as t}from"../core/errors.js";import{getLogger as s}from"../core/logger.js";import{parseResponse as r}from"../core/parse-response.js";import{loadRefreshToken as o,clearRefreshToken as i,clearLegacySessionTokens as n,buildAdmissionProofHeaders as a,persistRefreshToken as h}from"../security/admission-proof.js";import{getEventBus as c}from"../state/events.js";import{getSDKStore as g}from"../state/store.js";import{BroadcastChannelTransport as f}from"../sync/broadcast-transport.js";const l=e.object({accessToken:e.pipe(e.string(),e.minLength(1)),refreshToken:e.optional(e.pipe(e.string(),e.minLength(1))),beaconToken:e.pipe(e.string(),e.minLength(1)),accessTokenExpiresAt:e.optional(e.string()),refreshTokenTtlSeconds:e.optional(e.number())});function u(e){const t={};for(const[s,r]of Object.entries(e))"string"==typeof r&&(t[s]=r);return t}class p{constructor(e,t){this.logger=s(),this.store=g(),this.events=c(),this.lastBroadcastTokens=null,this.http=e,this.beaconHttp=t,this.setupTokenChannel(),this.http.updateConfig({...this.http.getConfig(),onUnauthorized:async()=>{await this.handleUnauthorized()}});const r=this.store.beaconToken;r&&this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":r}})}setupTokenChannel(){const e=new f("fanfare-auth-tokens");e.isAvailable()&&(this.authChannel=e,e.onMessage(e=>{const t=e.data;t&&"string"==typeof t.accessToken&&"string"==typeof t.beaconToken&&"string"==typeof t.refreshToken&&(this.lastBroadcastTokens={accessToken:t.accessToken,beaconToken:t.beaconToken,refreshToken:t.refreshToken})}))}broadcastSessionTokens(e){this.authChannel?.send(e)}check(){const e=this.store.session;return e?{isAuthenticated:!0,session:e,isGuest:"guest"===e.type,identityExpiresAt:e.identityExpiresAt}:{isAuthenticated:!1}}async guest(){try{this.logger.info("Creating guest session");const e=await this.sessionMintProofHeaders("/auth/guest"),{session:t,accessToken:s,refreshToken:r,beaconToken:o,refreshTokenTtlSeconds:i}=await this.http.post("/auth/guest",void 0,{headers:e});return this.attachIdentityExpiresAt(t,i),this.store.setSession(t),await this.setRefreshTokenDurable(r??null),this.store.setBeaconToken(o),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${s}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":o}}),this.scheduleTokenRefresh(t.expiresAt),this.events.emit("auth:authenticated",{session:t,isNew:!0}),t}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Failed to create guest session",t),this.events.emit("auth:error",{error:t,context:"guest"}),t}}async requestOtp(e){try{this.logger.info("Requesting OTP");const t="string"==typeof e?{email:e}:e;await this.http.post("/auth/otp/request",t)}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to request OTP",e),this.events.emit("auth:error",{error:e,context:"requestOtp"}),e}}async verifyOtp(e){try{const t=e;this.logger.info("Verifying OTP",{email:t.email,phone:t.phone});const s=await this.sessionMintProofHeaders("/auth/otp/verify"),{session:r,accessToken:o,refreshToken:i,beaconToken:n,refreshTokenTtlSeconds:a}=await this.http.post("/auth/otp/verify",t,{headers:s});return this.attachIdentityExpiresAt(r,a),this.store.setSession(r),await this.setRefreshTokenDurable(i??null),this.store.setBeaconToken(n),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${o}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":n}}),this.scheduleTokenRefresh(r.expiresAt),this.events.emit("auth:authenticated",{session:r,isNew:!0}),r}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to verify OTP",e),this.events.emit("auth:error",{error:e,context:"verifyOtp"}),e}}async exchangeExternal(e){try{const t="string"==typeof e?{exchangeCode:e}:e;this.logger.info("Exchanging external auth code");const s=await this.sessionMintProofHeaders("/auth/external/exchange"),{session:r,accessToken:o,refreshToken:i,beaconToken:n,refreshTokenTtlSeconds:a}=await this.http.post("/auth/external/exchange",t,{headers:s});return this.attachIdentityExpiresAt(r,a),this.store.setSession(r),await this.setRefreshTokenDurable(i??null),this.store.setBeaconToken(n),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${o}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":n}}),this.scheduleTokenRefresh(r.expiresAt),this.events.emit("auth:authenticated",{session:r,isNew:!0}),r}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to exchange external auth code",e),this.events.emit("auth:error",{error:e,context:"exchangeExternal"}),e}}async login(e){await this.requestOtp({email:e.email})}async logout(){try{if(!this.store.session)return;const e=this.store.refreshToken??await o();await this.http.post("/auth/logout",e?{refreshToken:e}:{},{skipUnauthorizedHandler:!0}).catch(()=>{}),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"user"})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Error during logout",t),this.store.clearAll(),this.lastBroadcastTokens=null,await i(),t}}async clearAuthState(){this.cancelTokenRefresh(),this.store.clearAll(),this.lastBroadcastTokens=null,await i();const e=this.http.getConfig(),{Authorization:t,...s}=e.headers||{};this.http.updateConfig({...e,headers:s});const r=this.beaconHttp.getConfig(),{"X-Beacon-Token":o,...n}=r.headers||{};this.beaconHttp.updateConfig({...r,headers:n})}getSession(){return this.store.session}async rehydrate(){const e=await o();if(n(),!e)return;if(this.store.setRefreshToken(e),!this.hasBearer()){try{await this.refresh()}catch(s){this.logger.debug("Eager rehydrate refresh failed; falling back to reactive 401",{error:s instanceof Error?s.message:String(s)})}return}const t=this.store.session;t?.expiresAt&&this.scheduleTokenRefresh(t.expiresAt)}hasBearer(){return Boolean(this.http.getConfig().headers?.Authorization)}async refresh(){if("undefined"!=typeof navigator&&"locks"in navigator){const e=await o(),t=e??this.store.refreshToken;return navigator.locks.request("fanfare-refresh",()=>this._doRefresh(t,e))}return this.refreshPromise?(this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise):(this.refreshPromise=this._doRefresh().finally(()=>{this.refreshPromise=void 0}),this.refreshPromise)}absoluteAuthUrl(e){return new URL(e,this.http.getConfig().baseUrl).toString()}async sessionMintProofHeaders(e){const t=await a({url:this.absoluteAuthUrl(e),method:"POST"});return t?u(t):{}}computeIdentityExpiresAt(e){if("number"==typeof e&&Number.isFinite(e))return new Date(Date.now()+1e3*e).toISOString()}attachIdentityExpiresAt(e,t){const s=this.computeIdentityExpiresAt(t);s&&(e.identityExpiresAt=s)}async setRefreshTokenDurable(e){this.store.setRefreshToken(e),e?await h(e):await i()}applySessionTokens(e){this.store.setBeaconToken(e.beaconToken),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${e.accessToken}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":e.beaconToken}});const t=this.store.session;t?.expiresAt&&this.scheduleTokenRefresh(t.expiresAt),this.store.session&&this.events.emit("auth:refreshed",{session:this.store.session})}async _doRefresh(e,s){const i=await o(),n=i??this.store.refreshToken;if(null!==i&&i!==this.store.refreshToken&&this.store.setRefreshToken(i),!n)throw this.logger.info("No refresh token available; clearing auth state"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("No refresh token available");if(s&&null===i)throw this.logger.debug("Durable refresh token cleared by a concurrent caller; skipping re-POST"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("Refresh token cleared by a concurrent caller");if(void 0!==e&&null!==i&&i!==e){this.logger.debug("Refresh already completed by a concurrent caller; adopting rotated session");const e=this.lastBroadcastTokens;return void(e&&e.refreshToken===i?this.applySessionTokens(e):this.store.session&&this.events.emit("auth:refreshed",{session:this.store.session}))}const h=await a({url:this.absoluteAuthUrl("/auth/refresh"),method:"POST"});if(!h)throw this.logger.info("No DPoP proof available for refresh; clearing auth state"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("DPoP proof unavailable for refresh");try{this.logger.info("Refreshing access token");const e=await this.http.post("/auth/refresh",{refreshToken:n},{headers:u(h),skipUnauthorizedHandler:!0}),t=r(l,e,{endpoint:"/auth/refresh"},{throwOnFailure:!0});if(!t.ok)throw t.error;const{accessToken:s,refreshToken:o,beaconToken:i,accessTokenExpiresAt:a,refreshTokenTtlSeconds:c}=t.value;if(await this.setRefreshTokenDurable(o??null),o&&this.broadcastSessionTokens({accessToken:s,beaconToken:i,refreshToken:o}),a){const e=this.computeIdentityExpiresAt(c);e?this.store.updateSessionExpiry(a,e):this.store.updateSessionExpiry(a)}this.applySessionTokens({accessToken:s,beaconToken:i})}catch(c){const e=c instanceof Error?c:new Error(String(c)),s=c instanceof t?c.status:void 0;throw 401===s?(this.logger.error("Refresh rejected as unauthorized; clearing auth state",e),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"})):this.logger.warn("Refresh failed transiently; preserving durable refresh token",{status:s,error:e.message}),e}}async handleUnauthorized(){this.logger.warn("Received 401, attempting to refresh token");try{await this.refresh()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.logger.error("Failed to handle 401",t),this.events.emit("auth:error",{error:new Error("Session expired"),context:"unauthorized"})}}scheduleTokenRefresh(e){if(this.cancelTokenRefresh(),!this.store.refreshToken)return;const t=Date.now(),s=new Date(e).getTime(),r=Number.isFinite(s),o=r?Math.max(s-3e5-t,3e4):3e4;this.logger.debug("Scheduling token refresh",{expiresAt:r?new Date(s).toISOString():"invalid",delayMs:o}),this.refreshTimer=setTimeout(()=>{this.refresh().catch(e=>{this.logger.error("Scheduled token refresh failed",e)})},o)}cancelTokenRefresh(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=void 0,this.logger.debug("Cancelled token refresh timer"))}destroy(){this.cancelTokenRefresh(),this.refreshPromise=void 0,this.authChannel?.close(),this.authChannel=void 0}}export{p as AuthenticationModule};
1
+ import*as e from"valibot";import{FanfareError as t}from"../core/errors.js";import{getLogger as s}from"../core/logger.js";import{parseResponse as r}from"../core/parse-response.js";import{loadRefreshToken as o,clearRefreshToken as i,clearLegacySessionTokens as n,buildAdmissionProofHeaders as a,persistRefreshToken as h}from"../security/admission-proof.js";import{getEventBus as c}from"../state/events.js";import{getSDKStore as g}from"../state/store.js";import{BroadcastChannelTransport as f}from"../sync/broadcast-transport.js";const l=e.object({accessToken:e.pipe(e.string(),e.minLength(1)),refreshToken:e.optional(e.pipe(e.string(),e.minLength(1))),beaconToken:e.pipe(e.string(),e.minLength(1)),accessTokenExpiresAt:e.optional(e.string()),refreshTokenTtlSeconds:e.optional(e.number())});function u(e){const t={};for(const[s,r]of Object.entries(e))"string"==typeof r&&(t[s]=r);return t}class d{constructor(e,t){this.logger=s(),this.store=g(),this.events=c(),this.lastBroadcastTokens=null,this.http=e,this.beaconHttp=t,this.setupTokenChannel(),this.http.updateConfig({...this.http.getConfig(),onUnauthorized:async()=>{await this.handleUnauthorized()}});const r=this.store.beaconToken;r&&this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":r}})}setupTokenChannel(){const e=new f("fanfare-auth-tokens");e.isAvailable()&&(this.authChannel=e,e.onMessage(e=>{const t=e.data;t&&"string"==typeof t.accessToken&&"string"==typeof t.beaconToken&&"string"==typeof t.refreshToken&&(this.lastBroadcastTokens={accessToken:t.accessToken,beaconToken:t.beaconToken,refreshToken:t.refreshToken})}))}broadcastSessionTokens(e){this.authChannel?.send(e)}check(){const e=this.store.session;return e?{isAuthenticated:!0,session:e,isGuest:"guest"===e.type,identityExpiresAt:e.identityExpiresAt}:{isAuthenticated:!1}}async guest(){try{this.logger.info("Creating guest session");const e=await this.sessionMintProofHeaders("/auth/guest"),{session:t,accessToken:s,refreshToken:r,beaconToken:o,refreshTokenTtlSeconds:i}=await this.http.post("/auth/guest",void 0,{headers:e});return this.attachIdentityExpiresAt(t,i),this.store.setSession(t),await this.setRefreshTokenDurable(r??null),this.store.setBeaconToken(o),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${s}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":o}}),this.scheduleTokenRefresh(t.expiresAt),this.events.emit("auth:authenticated",{session:t,isNew:!0}),t}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Failed to create guest session",t),this.events.emit("auth:error",{error:t,context:"guest"}),t}}async requestOtp(e){try{this.logger.info("Requesting OTP");const t="string"==typeof e?{email:e}:e;await this.http.post("/auth/otp/request",t)}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to request OTP",e),this.events.emit("auth:error",{error:e,context:"requestOtp"}),e}}async verifyOtp(e){try{const t=e;this.logger.info("Verifying OTP",{credential:t.email?"email":"phone"});const s=await this.sessionMintProofHeaders("/auth/otp/verify"),{session:r,accessToken:o,refreshToken:i,beaconToken:n,refreshTokenTtlSeconds:a}=await this.http.post("/auth/otp/verify",t,{headers:s});return this.attachIdentityExpiresAt(r,a),this.store.setSession(r),await this.setRefreshTokenDurable(i??null),this.store.setBeaconToken(n),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${o}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":n}}),this.scheduleTokenRefresh(r.expiresAt),this.events.emit("auth:authenticated",{session:r,isNew:!0}),r}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to verify OTP",e),this.events.emit("auth:error",{error:e,context:"verifyOtp"}),e}}async exchangeExternal(e){try{const t="string"==typeof e?{exchangeCode:e}:e;this.logger.info("Exchanging external auth code");const s=await this.sessionMintProofHeaders("/auth/external/exchange"),{session:r,accessToken:o,refreshToken:i,beaconToken:n,refreshTokenTtlSeconds:a}=await this.http.post("/auth/external/exchange",t,{headers:s});return this.attachIdentityExpiresAt(r,a),this.store.setSession(r),await this.setRefreshTokenDurable(i??null),this.store.setBeaconToken(n),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${o}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":n}}),this.scheduleTokenRefresh(r.expiresAt),this.events.emit("auth:authenticated",{session:r,isNew:!0}),r}catch(t){const e=t instanceof Error?t:new Error(String(t));throw this.logger.error("Failed to exchange external auth code",e),this.events.emit("auth:error",{error:e,context:"exchangeExternal"}),e}}async login(e){await this.requestOtp({email:e.email})}async logout(){try{if(!this.store.session)return;const e=this.store.refreshToken??await o();await this.http.post("/auth/logout",e?{refreshToken:e}:{},{skipUnauthorizedHandler:!0}).catch(()=>{}),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"user"})}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.logger.error("Error during logout",t),this.store.clearAll(),this.lastBroadcastTokens=null,await i(),t}}async clearAuthState(){this.cancelTokenRefresh(),this.store.clearAll(),this.lastBroadcastTokens=null,await i();const e=this.http.getConfig(),{Authorization:t,...s}=e.headers||{};this.http.updateConfig({...e,headers:s});const r=this.beaconHttp.getConfig(),{"X-Beacon-Token":o,...n}=r.headers||{};this.beaconHttp.updateConfig({...r,headers:n})}getSession(){return this.store.session}async rehydrate(){const e=await o();if(n(),!e)return;if(this.store.setRefreshToken(e),!this.hasBearer()){try{await this.refresh()}catch(s){this.logger.debug("Eager rehydrate refresh failed; falling back to reactive 401",{error:s instanceof Error?s.message:String(s)})}return}const t=this.store.session;t?.expiresAt&&this.scheduleTokenRefresh(t.expiresAt)}hasBearer(){return Boolean(this.http.getConfig().headers?.Authorization)}async refresh(){if("undefined"!=typeof navigator&&"locks"in navigator){const e=await o(),t=e??this.store.refreshToken;return navigator.locks.request("fanfare-refresh",()=>this._doRefresh(t,e))}return this.refreshPromise?(this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise):(this.refreshPromise=this._doRefresh().finally(()=>{this.refreshPromise=void 0}),this.refreshPromise)}absoluteAuthUrl(e){return new URL(e,this.http.getConfig().baseUrl).toString()}async sessionMintProofHeaders(e){const t=await a({url:this.absoluteAuthUrl(e),method:"POST"});return t?u(t):{}}computeIdentityExpiresAt(e){if("number"==typeof e&&Number.isFinite(e))return new Date(Date.now()+1e3*e).toISOString()}attachIdentityExpiresAt(e,t){const s=this.computeIdentityExpiresAt(t);s&&(e.identityExpiresAt=s)}async setRefreshTokenDurable(e){this.store.setRefreshToken(e),e?await h(e):await i()}applySessionTokens(e){this.store.setBeaconToken(e.beaconToken),this.http.updateConfig({...this.http.getConfig(),headers:{...this.http.getConfig().headers,Authorization:`Bearer ${e.accessToken}`}}),this.beaconHttp.updateConfig({...this.beaconHttp.getConfig(),headers:{...this.beaconHttp.getConfig().headers,"X-Beacon-Token":e.beaconToken}});const t=this.store.session;t?.expiresAt&&this.scheduleTokenRefresh(t.expiresAt),this.store.session&&this.events.emit("auth:refreshed",{session:this.store.session})}async _doRefresh(e,s){const i=await o(),n=i??this.store.refreshToken;if(null!==i&&i!==this.store.refreshToken&&this.store.setRefreshToken(i),!n)throw this.logger.info("No refresh token available; clearing auth state"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("No refresh token available");if(s&&null===i)throw this.logger.debug("Durable refresh token cleared by a concurrent caller; skipping re-POST"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("Refresh token cleared by a concurrent caller");if(void 0!==e&&null!==i&&i!==e){this.logger.debug("Refresh already completed by a concurrent caller; adopting rotated session");const e=this.lastBroadcastTokens;return void(e&&e.refreshToken===i?this.applySessionTokens(e):this.store.session&&this.events.emit("auth:refreshed",{session:this.store.session}))}const h=await a({url:this.absoluteAuthUrl("/auth/refresh"),method:"POST"});if(!h)throw this.logger.info("No DPoP proof available for refresh; clearing auth state"),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"}),new Error("DPoP proof unavailable for refresh");try{this.logger.info("Refreshing access token");const e=await this.http.post("/auth/refresh",{refreshToken:n},{headers:u(h),skipUnauthorizedHandler:!0}),t=r(l,e,{endpoint:"/auth/refresh"},{throwOnFailure:!0});if(!t.ok)throw t.error;const{accessToken:s,refreshToken:o,beaconToken:i,accessTokenExpiresAt:a,refreshTokenTtlSeconds:c}=t.value;if(await this.setRefreshTokenDurable(o??null),o&&this.broadcastSessionTokens({accessToken:s,beaconToken:i,refreshToken:o}),a){const e=this.computeIdentityExpiresAt(c);e?this.store.updateSessionExpiry(a,e):this.store.updateSessionExpiry(a)}this.applySessionTokens({accessToken:s,beaconToken:i})}catch(c){const e=c instanceof Error?c:new Error(String(c)),s=c instanceof t?c.status:void 0;throw 401===s?(this.logger.error("Refresh rejected as unauthorized; clearing auth state",e),await this.clearAuthState(),this.events.emit("auth:logout",{reason:"expired"})):this.logger.warn("Refresh failed transiently; preserving durable refresh token",{status:s,error:e.message}),e}}async handleUnauthorized(){this.logger.warn("Received 401, attempting to refresh token");try{await this.refresh()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.logger.error("Failed to handle 401",t),this.events.emit("auth:error",{error:new Error("Session expired"),context:"unauthorized"})}}scheduleTokenRefresh(e){if(this.cancelTokenRefresh(),!this.store.refreshToken)return;const t=Date.now(),s=new Date(e).getTime(),r=Number.isFinite(s),o=r?Math.max(s-3e5-t,3e4):3e4;this.logger.debug("Scheduling token refresh",{expiresAt:r?new Date(s).toISOString():"invalid",delayMs:o}),this.refreshTimer=setTimeout(()=>{this.refresh().catch(e=>{this.logger.error("Scheduled token refresh failed",e)})},o)}cancelTokenRefresh(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=void 0,this.logger.debug("Cancelled token refresh timer"))}destroy(){this.cancelTokenRefresh(),this.refreshPromise=void 0,this.authChannel?.close(),this.authChannel=void 0}}export{d as AuthenticationModule};
@@ -67,6 +67,20 @@ export interface OtpVerify {
67
67
  phone?: string;
68
68
  code: string;
69
69
  defaultCountry?: string;
70
+ /**
71
+ * Marketing SMS consent, recorded only on a phone verify, against the number the
72
+ * code just proved; ignored on an email verify. Send true only when the consumer
73
+ * explicitly agreed to the marketing offer for this attempt.
74
+ */
75
+ marketingSmsConsent?: boolean;
76
+ /** Exact localized marketing offer accepted for this attempt, including its policy link URLs. */
77
+ marketingSmsConsentText?: string;
78
+ /**
79
+ * The experience the consumer is verifying on, validated against the caller's
80
+ * organization server-side. It records where consent was given; it does not
81
+ * restrict the session to that experience.
82
+ */
83
+ experienceId?: string;
70
84
  }
71
85
  export interface ExternalExchange {
72
86
  exchangeCode: string;
@@ -1,2 +1,2 @@
1
- import{createBeaconLifecycleAdapter as e}from"../adapters/beacon-lifecycle.js";import{AppointmentManagementModule as t}from"../appointments/appointment.module.js";import{AuctionManagementModule as r}from"../auctions/auction.module.js";import{AuthenticationModule as n}from"../auth/auth.module.js";import{BeaconTrackingModule as i}from"../beacon/beacon.module.js";import{ChallengeManagementModule as o}from"../challenges/challenge.module.js";import{Config as s}from"../config/index.js";import{DrawManagementModule as a}from"../draws/draw.module.js";import{ExperienceManagementModule as d}from"../experiences/experience.module.js";import{ExperienceJourney as l}from"../experiences/journey.js";import{PaymentMethodManagementModule as c}from"../payment/payment.module.js";import{QueueManagementModule as u}from"../queues/queue.module.js";import{SelectionModule as g}from"../selections/selection.module.js";import{resolveCapabilityOptions as m,resolveCapabilityAttribution as y}from"../state/capability-token-registry.js";import{getEventBus as p}from"../state/events.js";import{getSDKStore as f}from"../state/store.js";import{CrossTabCoordinator as b}from"../sync/cross-tab-coordinator.js";import{TimedReleaseManagementModule as w}from"../timed-releases/timed-release.module.js";import{version as I}from"../version.js";import{WaitlistManagementModule as h}from"../waitlists/waitlist.module.js";import{FanfareError as v,ErrorCodes as M}from"./errors.js";import{createHttpClient as j}from"./http.js";import{createLogger as S}from"./logger.js";let k=null;async function x(x){const D=new s(x),E=D.get(),A=S({enabled:E.debug||!1,level:E.debug?"debug":"warn",prefix:"Fanfare"});if(A.info("Initializing Fanfare SDK",{organizationId:x.organizationId,environment:E.environment}),k)throw new v("Fanfare SDK is already initialized. The SDK uses a shared event bus and store; multiple concurrent instances will corrupt state and analytics. Call destroy() on the existing instance before initializing a new one.",M.ALREADY_INITIALIZED);const q=/* @__PURE__ */Symbol("fanfare-sdk"),C=j({baseUrl:D.apiUrl,credentials:E.credentials,headers:{"X-Organization-Id":x.organizationId,"X-Publishable-Key":x.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":I,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},E),z=j({baseUrl:D.beaconUrl,credentials:"omit",headers:{"X-Organization-Id":x.organizationId,"X-Publishable-Key":x.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":I,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},E),O=new n(C,z),F=new o(C),K=new u(C),X=new a(C),R=new r(C),P=new t(C),T=new h(C),$=new g(C),J=new w(C),N=new d(C,K,X,T,J),V=new c(C),L=p(),U=f();L.on("auth:logout",()=>V.destroy());const B=/* @__PURE__ */new Map,G=/* @__PURE__ */new Map,_=new i(z,E.beacon||{},e=>{const t=G.get(e)?.snapshot$.get();return"routed"===t?.journeyStage?t.sequenceId:void 0}),Y=/* @__PURE__ */new Map;let Z,H;const Q=e=>{const t=G.get(e);if(t)return A.debug("Returning existing journey handle",{experienceId:e}),t;A.info("Creating new journey",{experienceId:e});const r=new l(e,Z);B.set(e,r),H?.registerJourney(r);const n={view$:r.view$,events$:r.events$,latestEvent$:r.latestEvent$,snapshot$:r.state,ackEvent:e=>r.ackEvent(e),ackAllEvents:()=>r.ackAllEvents(),destroy:()=>{r.destroy(),H?.unregisterJourney(e),B.delete(e),G.delete(e)}};return G.set(e,n),n},W=async e=>{const t=e??await N.getMe(),r=/* @__PURE__ */new Set,n=new Set(t.active.journeys.map(e=>e.experienceId));for(const i of n)Q(i);for(const i of n){const e=B.get(i);e&&await e.resumeFromMe(t)&&r.add(i)}return[...r]},ee=async e=>{const t=/* @__PURE__ */new Set;for(const r of Object.keys(U.activeJourneys))Q(r),t.add(r);if(!e&&!O.getSession())return[...t];for(const r of await W(e))t.add(r);return[...t]},te={check:()=>O.check(),guest:()=>O.guest(),requestOtp:e=>O.requestOtp(e),verifyOtp:e=>O.verifyOtp(e),exchangeExternal:e=>O.exchangeExternal(e),login:e=>O.login(e),logout:()=>O.logout(),getSession:()=>O.getSession(),refresh:()=>O.refresh()},re={initiate:e=>F.initiate(e),verify:e=>F.verify(e)},ne={get:e=>Q(e),list:()=>Array.from(
2
- /* @__PURE__ */new Set([...G.keys(),...B.keys(),...Object.keys(U.activeJourneys)])),resumeAll:e=>ee(e)},ie={bind:e=>_.bind(e),track:e=>_.track(e),trackBatch:e=>_.trackBatch(e),flush:()=>_.flush()},oe=(e,t)=>{if(Y.has(e.id))A.warn("CDP adapter already registered, skipping",{adapterId:e.id});else{try{e.init(L,t)}catch(r){return void A.error("CDP adapter failed to initialize",{adapterId:e.id,error:r})}Y.set(e.id,e),A.info("CDP adapter registered",{adapterId:e.id})}},se=(e,t)=>L.on(e,t),ae=async()=>{A.info("Destroying SDK instance");for(const t of Y.values())try{t.destroy()}catch(e){A.error("CDP adapter failed to destroy",{adapterId:t.id,error:e})}Y.clear();for(const t of Array.from(G.values()))try{t.destroy()}catch(e){A.error("Journey handle failed to destroy",{error:e})}B.clear(),G.clear(),"destroy"in O&&"function"==typeof O.destroy&&O.destroy(),K.destroy(),X.destroy(),R.destroy(),P.destroy(),T.destroy(),J.destroy(),N.destroy(),_.destroy(),V.destroy(),H?.close(),L.off(),k===q&&(k=null)};Z={auth:te,challenges:re,journeys:ne,queues:{get:e=>K.get(e),enter:(e,t,r,n)=>K.enter(e,t,m({distributionId:e},r),n,y({distributionId:e})),leave:(e,t)=>K.leave(e,m({distributionId:e},t)),status:e=>K.status(e),startMonitoring:(e,t,r)=>K.startMonitoring(e,t,r),stopMonitoring:e=>K.stopMonitoring(e),isMonitoring:e=>K.isMonitoring(e)},draws:{get:e=>X.get(e),enter:(e,t,r,n,i)=>X.enter(e,t,m({distributionId:e},r),n,i,y({distributionId:e})),leave:(e,t)=>X.leave(e,m({distributionId:e},t)),status:e=>X.status(e),startMonitoring:(e,t,r)=>X.startMonitoring(e,t,r),stopMonitoring:e=>X.stopMonitoring(e),isMonitoring:e=>X.isMonitoring(e)},auctions:{get:e=>R.get(e),bid:(e,t,r)=>R.bid(e,t,m({distributionId:e},r),y({distributionId:e})),status:e=>R.status(e),startMonitoring:(e,t,r)=>R.startMonitoring(e,t,r),stopMonitoring:e=>R.stopMonitoring(e),isMonitoring:e=>R.isMonitoring(e),destroy:()=>R.destroy()},experiences:{get:e=>N.get(e),enter:e=>N.enter(e),leave:e=>N.leave(e),getMe:()=>N.getMe(),confirmCheckout:(e,t,r)=>N.confirmCheckout(e,t,r),findSequence:(e,t)=>N.findSequence(e,t),validateSequenceAccess:(e,t)=>N.validateSequenceAccess(e,t),selectSequence:e=>N.selectSequence(e),getCurrentDistributions:(e,t)=>N.getCurrentDistributions(e,t),enterDistribution:e=>{if(!e)throw new v("Distribution is required",M.VALIDATION_ERROR);return N.enterDistribution(e)},getActiveSession:()=>N.getActiveSession(),isInExperience:e=>N.isInExperience(e),getSelectedSequence:()=>N.getSelectedSequence(),createJourney:e=>Q(e),resumeJourneysFromMe:e=>W(e),destroy:()=>N.destroy()},selections:$,waitlists:{enter:(e,t)=>T.enter(e,m({waitlistId:e},t),y({waitlistId:e})),leave:(e,t)=>T.leave(e,m({waitlistId:e},t),y({waitlistId:e})),getStatus:e=>T.getStatus(e),destroy:()=>T.destroy()},timedReleases:{get:e=>J.get(e),enter:(e,t,r)=>J.enter(e,m({distributionId:e},t),r,y({distributionId:e})),leave:(e,t)=>J.leave(e,m({distributionId:e},t)),complete:e=>J.complete(e),status:e=>J.status(e),startMonitoring:(e,t,r)=>J.startMonitoring(e,t,r),stopMonitoring:e=>J.stopMonitoring(e),isMonitoring:e=>J.isMonitoring(e)},appointments:{get:(e,t)=>P.get(e,m({distributionId:e},t)),getSlots:(e,t,r)=>P.getSlots(e,t,m({distributionId:e},r)),getMe:e=>P.getMe(e),book:(e,t,r,n)=>P.book(e,t,r,m({distributionId:e},n),y({distributionId:e})),cancel:(e,t,r,n)=>P.cancel(e,t,r,m({distributionId:e},n)),reschedule:(e,t,r,n)=>P.reschedule(e,t,r,m({distributionId:e},n)),startMonitoring:(e,t,r,n)=>P.startMonitoring(e,t,r,n),stopMonitoring:e=>P.stopMonitoring(e),isMonitoring:e=>P.isMonitoring(e),bumpMonitorGeneration:e=>P.bumpMonitorGeneration(e),destroy:()=>P.destroy()},beacon:ie,payment:V,use:oe,on:se,destroy:ae};const de=!1!==x.sync&&"object"==typeof x.sync?x.sync:{};H=new b({enabled:!1!==x.sync&&!1!==de.enabled,channelName:de.channelName}),A.info("Inter-tab sync initialized",{tabId:H.getTabId(),enabled:H.isEnabled()});!E.beacon?.disableLifecycleEvents&&oe(e(e=>_.track(e)));const le={auth:te,challenges:re,journeys:ne,beacon:ie,appointments:Z.appointments,payment:V,use:oe,on:se,destroy:ae};if(await O.rehydrate(),E.autoResume&&O.getSession())try{await ee()}catch(ce){A.warn("Auto-resume on init failed; journeys can be resumed manually",{error:ce instanceof Error?ce.message:String(ce)})}return k=q,A.info("SDK initialized successfully"),le}export{x as init};
1
+ import{createBeaconLifecycleAdapter as e}from"../adapters/beacon-lifecycle.js";import{AppointmentManagementModule as t}from"../appointments/appointment.module.js";import{AuctionManagementModule as r}from"../auctions/auction.module.js";import{AuthenticationModule as n}from"../auth/auth.module.js";import{BeaconTrackingModule as i}from"../beacon/beacon.module.js";import{ChallengeManagementModule as o}from"../challenges/challenge.module.js";import{Config as s}from"../config/index.js";import{DrawManagementModule as a}from"../draws/draw.module.js";import{ExperienceManagementModule as d}from"../experiences/experience.module.js";import{ExperienceJourney as l}from"../experiences/journey.js";import{PaymentMethodManagementModule as c}from"../payment/payment.module.js";import{QueueManagementModule as u}from"../queues/queue.module.js";import{SelectionModule as g}from"../selections/selection.module.js";import{resolveCapabilityOptions as m,resolveCapabilityAttribution as y}from"../state/capability-token-registry.js";import{getEventBus as p}from"../state/events.js";import{getSDKStore as f}from"../state/store.js";import{CrossTabCoordinator as b}from"../sync/cross-tab-coordinator.js";import{TimedReleaseManagementModule as w}from"../timed-releases/timed-release.module.js";import{version as I}from"../version.js";import{WaitlistManagementModule as h}from"../waitlists/waitlist.module.js";import{FanfareError as v,ErrorCodes as M}from"./errors.js";import{createHttpClient as j}from"./http.js";import{registerInternalSdk as S,unregisterInternalSdk as k}from"./internal-sdk-registry.js";import{createLogger as x}from"./logger.js";let D=null;async function E(E){const A=new s(E),q=A.get(),C=x({enabled:q.debug||!1,level:q.debug?"debug":"warn",prefix:"Fanfare"});if(C.info("Initializing Fanfare SDK",{organizationId:E.organizationId,environment:q.environment}),D)throw new v("Fanfare SDK is already initialized. The SDK uses a shared event bus and store; multiple concurrent instances will corrupt state and analytics. Call destroy() on the existing instance before initializing a new one.",M.ALREADY_INITIALIZED);const z=/* @__PURE__ */Symbol("fanfare-sdk"),O=j({baseUrl:A.apiUrl,credentials:q.credentials,headers:{"X-Organization-Id":E.organizationId,"X-Publishable-Key":E.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":I,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},q),F=j({baseUrl:A.beaconUrl,credentials:"omit",headers:{"X-Organization-Id":E.organizationId,"X-Publishable-Key":E.publishableKey,"X-Fanfare-API-Version":"2025-01-15","X-Fanfare-SDK-Version":I,"X-Fanfare-Client-Type":"browser"},timeout:3e4,retryConfig:{maxRetries:3,delayTimer:1e3,retryOnNetworkError:!0}},q),K=new n(O,F),X=new o(O),R=new u(O),P=new a(O),T=new r(O),$=new t(O),J=new h(O),N=new g(O),V=new w(O),L=new d(O,R,P,J,V),U=new c(O),B=p(),G=f();B.on("auth:logout",()=>U.destroy());const _=/* @__PURE__ */new Map,Y=/* @__PURE__ */new Map,Z=new i(F,q.beacon||{},e=>{const t=Y.get(e)?.snapshot$.get();return"routed"===t?.journeyStage?t.sequenceId:void 0}),H=/* @__PURE__ */new Map;let Q,W;const ee=e=>{const t=Y.get(e);if(t)return C.debug("Returning existing journey handle",{experienceId:e}),t;C.info("Creating new journey",{experienceId:e});const r=new l(e,Q);_.set(e,r),W?.registerJourney(r);const n={view$:r.view$,events$:r.events$,latestEvent$:r.latestEvent$,snapshot$:r.state,ackEvent:e=>r.ackEvent(e),ackAllEvents:()=>r.ackAllEvents(),destroy:()=>{r.destroy(),W?.unregisterJourney(e),_.delete(e),Y.delete(e)}};return Y.set(e,n),n},te=async e=>{const t=e??await L.getMe(),r=/* @__PURE__ */new Set,n=new Set(t.active.journeys.map(e=>e.experienceId));for(const i of n)ee(i);for(const i of n){const e=_.get(i);e&&await e.resumeFromMe(t)&&r.add(i)}return[...r]},re=async e=>{const t=/* @__PURE__ */new Set;for(const r of Object.keys(G.activeJourneys))ee(r),t.add(r);if(!e&&!K.getSession())return[...t];for(const r of await te(e))t.add(r);return[...t]},ne={check:()=>K.check(),guest:()=>K.guest(),requestOtp:e=>K.requestOtp(e),verifyOtp:e=>K.verifyOtp(e),exchangeExternal:e=>K.exchangeExternal(e),login:e=>K.login(e),logout:()=>K.logout(),getSession:()=>K.getSession(),refresh:()=>K.refresh()},ie={initiate:e=>X.initiate(e),verify:e=>X.verify(e)},oe={get:e=>ee(e),list:()=>Array.from(
2
+ /* @__PURE__ */new Set([...Y.keys(),..._.keys(),...Object.keys(G.activeJourneys)])),resumeAll:e=>re(e)},se={bind:e=>Z.bind(e),track:e=>Z.track(e),trackBatch:e=>Z.trackBatch(e),flush:()=>Z.flush()},ae=(e,t)=>{if(H.has(e.id))C.warn("CDP adapter already registered, skipping",{adapterId:e.id});else{try{e.init(B,t)}catch(r){return void C.error("CDP adapter failed to initialize",{adapterId:e.id,error:r})}H.set(e.id,e),C.info("CDP adapter registered",{adapterId:e.id})}},de=(e,t)=>B.on(e,t),le=async()=>{C.info("Destroying SDK instance");for(const t of H.values())try{t.destroy()}catch(e){C.error("CDP adapter failed to destroy",{adapterId:t.id,error:e})}H.clear();for(const t of Array.from(Y.values()))try{t.destroy()}catch(e){C.error("Journey handle failed to destroy",{error:e})}_.clear(),Y.clear(),"destroy"in K&&"function"==typeof K.destroy&&K.destroy(),R.destroy(),P.destroy(),T.destroy(),$.destroy(),J.destroy(),V.destroy(),L.destroy(),Z.destroy(),U.destroy(),W?.close(),B.off(),k(ue),D===z&&(D=null)};Q={auth:ne,challenges:ie,journeys:oe,queues:{get:e=>R.get(e),enter:(e,t,r,n)=>R.enter(e,t,m({distributionId:e},r),n,y({distributionId:e})),leave:(e,t)=>R.leave(e,m({distributionId:e},t)),status:e=>R.status(e),startMonitoring:(e,t,r)=>R.startMonitoring(e,t,r),stopMonitoring:e=>R.stopMonitoring(e),isMonitoring:e=>R.isMonitoring(e)},draws:{get:e=>P.get(e),enter:(e,t,r,n,i)=>P.enter(e,t,m({distributionId:e},r),n,i,y({distributionId:e})),leave:(e,t)=>P.leave(e,m({distributionId:e},t)),status:e=>P.status(e),startMonitoring:(e,t,r)=>P.startMonitoring(e,t,r),stopMonitoring:e=>P.stopMonitoring(e),isMonitoring:e=>P.isMonitoring(e)},auctions:{get:e=>T.get(e),bid:(e,t,r)=>T.bid(e,t,m({distributionId:e},r),y({distributionId:e})),status:e=>T.status(e),startMonitoring:(e,t,r)=>T.startMonitoring(e,t,r),stopMonitoring:e=>T.stopMonitoring(e),isMonitoring:e=>T.isMonitoring(e),destroy:()=>T.destroy()},experiences:{get:e=>L.get(e),enter:e=>L.enter(e),leave:e=>L.leave(e),getMe:()=>L.getMe(),confirmCheckout:(e,t,r)=>L.confirmCheckout(e,t,r),findSequence:(e,t)=>L.findSequence(e,t),validateSequenceAccess:(e,t)=>L.validateSequenceAccess(e,t),selectSequence:e=>L.selectSequence(e),getCurrentDistributions:(e,t)=>L.getCurrentDistributions(e,t),enterDistribution:e=>{if(!e)throw new v("Distribution is required",M.VALIDATION_ERROR);return L.enterDistribution(e)},getActiveSession:()=>L.getActiveSession(),isInExperience:e=>L.isInExperience(e),getSelectedSequence:()=>L.getSelectedSequence(),createJourney:e=>ee(e),resumeJourneysFromMe:e=>te(e),destroy:()=>L.destroy()},selections:N,waitlists:{enter:(e,t)=>J.enter(e,m({waitlistId:e},t),y({waitlistId:e})),leave:(e,t)=>J.leave(e,m({waitlistId:e},t),y({waitlistId:e})),getStatus:e=>J.getStatus(e),destroy:()=>J.destroy()},timedReleases:{get:e=>V.get(e),enter:(e,t,r)=>V.enter(e,m({distributionId:e},t),r,y({distributionId:e})),leave:(e,t)=>V.leave(e,m({distributionId:e},t)),complete:e=>V.complete(e),status:e=>V.status(e),startMonitoring:(e,t,r)=>V.startMonitoring(e,t,r),stopMonitoring:e=>V.stopMonitoring(e),isMonitoring:e=>V.isMonitoring(e)},appointments:{get:(e,t)=>$.get(e,m({distributionId:e},t)),getSlots:(e,t,r)=>$.getSlots(e,t,m({distributionId:e},r)),getMe:e=>$.getMe(e),book:(e,t,r,n)=>$.book(e,t,r,m({distributionId:e},n),y({distributionId:e})),cancel:(e,t,r,n)=>$.cancel(e,t,r,m({distributionId:e},n)),reschedule:(e,t,r,n)=>$.reschedule(e,t,r,m({distributionId:e},n)),startMonitoring:(e,t,r,n)=>$.startMonitoring(e,t,r,n),stopMonitoring:e=>$.stopMonitoring(e),isMonitoring:e=>$.isMonitoring(e),bumpMonitorGeneration:e=>$.bumpMonitorGeneration(e),destroy:()=>$.destroy()},beacon:se,payment:U,use:ae,on:de,destroy:le};const ce=!1!==E.sync&&"object"==typeof E.sync?E.sync:{};W=new b({enabled:!1!==E.sync&&!1!==ce.enabled,channelName:ce.channelName}),C.info("Inter-tab sync initialized",{tabId:W.getTabId(),enabled:W.isEnabled()});!q.beacon?.disableLifecycleEvents&&ae(e(e=>Z.track(e)));const ue={auth:ne,challenges:ie,journeys:oe,beacon:se,appointments:Q.appointments,payment:U,use:ae,on:de,destroy:le};if(S(ue,Q),await K.rehydrate(),q.autoResume&&K.getSession())try{await re()}catch(ge){C.warn("Auto-resume on init failed; journeys can be resumed manually",{error:ge instanceof Error?ge.message:String(ge)})}return D=z,C.info("SDK initialized successfully"),ue}export{E as init};
@@ -0,0 +1,14 @@
1
+ import { ExperienceDetails } from '../experiences/types';
2
+ import { FanfareSDK, InternalFanfareSDK } from '../types';
3
+ /** Binds a public SDK object to its runtime surface for the lifetime of the instance. */
4
+ export declare function registerInternalSdk(sdk: FanfareSDK, internal: InternalFanfareSDK): void;
5
+ /** Unbinds a destroyed SDK object. */
6
+ export declare function unregisterInternalSdk(sdk: FanfareSDK): void;
7
+ /**
8
+ * Reads an experience's details through the instance's own experience read, so
9
+ * concurrent reads of one experience on one instance share a single request.
10
+ *
11
+ * Rejects with `NOT_INITIALIZED` for an object `init` did not create, or one
12
+ * that has been destroyed.
13
+ */
14
+ export declare function readExperienceDetails(sdk: FanfareSDK, experienceId: string): Promise<ExperienceDetails>;
@@ -0,0 +1 @@
1
+ import{createError as e}from"./errors.js";const n=/* @__PURE__ */new WeakMap;function t(e,t){n.set(e,t)}function i(e){n.delete(e)}async function o(t,i){const o=n.get(t);if(!o)throw e.notInitialized("SDK instance is not initialized or has been destroyed");return o.experiences.get(i)}export{o as readExperienceDetails,t as registerInternalSdk,i as unregisterInternalSdk};
@@ -1 +1 @@
1
- import t from"big.js";const n=/^\d{1,12}(\.\d{1,8})?$/;function r(t){return"string"==typeof t&&n.test(t)}function u(t,n="0"){return t&&r(t)?t:n}function e(t){return t.split(".")[1]?.length??0}function i(t,n){return Math.max(e(t),e(n))}function o(n,r){return t(n).plus(r).toFixed(i(n,r))}function f(n,r){return t(n).minus(r).toFixed(i(n,r))}function c(n,r){return t(n).gt(r)}function s(n,r){return t(n).gte(r)}function g(n,r){return t(n).lte(r)}export{n as MONEY_REGEX,o as addMoney,c as isMoneyGreaterThan,s as isMoneyGreaterThanOrEqualTo,g as isMoneyLessThanOrEqualTo,r as isMoneyString,u as normalizeMoney,f as subtractMoney};
1
+ const n=8,t=/^\d{1,12}(\.\d{1,8})?$/;function r(n){return"string"==typeof n&&t.test(n)}function u(n,t="0"){return n&&r(n)?n:t}function e(n){return n.split(".")[1]?.length??0}function i(n,t){return Math.max(e(n),e(t))}function o(n){const[t="0",r=""]=n.split(".");return BigInt(t+r.padEnd(8,"0"))}function c(n,t){const r=n<0n,u=(r?-n:n).toString().padStart(9,"0"),e=u.length-8,i=u.slice(e,e+t);return`${r?"-":""}${u.slice(0,e)}${i?`.${i}`:""}`}function f(n,t){return c(o(n)+o(t),i(n,t))}function s(n,t){return c(o(n)-o(t),i(n,t))}function l(n,t){return o(n)>o(t)}function p(n,t){return o(n)>=o(t)}function a(n,t){return o(n)<=o(t)}export{n as MONEY_FRACTIONAL_DIGITS,t as MONEY_REGEX,f as addMoney,l as isMoneyGreaterThan,p as isMoneyGreaterThanOrEqualTo,a as isMoneyLessThanOrEqualTo,r as isMoneyString,u as normalizeMoney,s as subtractMoney};
@@ -102,6 +102,8 @@ export interface DrawModule {
102
102
  */
103
103
  export interface Draw {
104
104
  id: string;
105
+ /** Whether loyalty points weight the randomized entry order. */
106
+ loyaltyBiasEnabled?: boolean;
105
107
  openAt?: string | null;
106
108
  closeAt?: string | null;
107
109
  timeZone: string;
@@ -1 +1 @@
1
- import{BRIDGE_EVENT_VERSION as e,FANFARE_STATE_CHANGE_EVENT as t}from"@fanfare-io/fanfare-sdk-contracts/bridge";import{describeJourneyState as n}from"./journey-display.js";const r="data-fanfare-experience-id",i="data-fanfare-stage",s="data-fanfare-phase",o="data-fanfare-mechanism",a="data-fanfare-expires-at",c=[r,i,s,o,a];function u(e,t){const r=n(e);let i=r.position;if(t&&"position"in t&&"number"==typeof t.position&&(i=t.position),"routed"!==e.journeyStage||!("sequence"in e))return{stage:e.journeyStage};const s=e.sequence;let o;"grant"in s&&(o=function(e){if(void 0===e)return;const t=Date.parse(e);return Number.isNaN(t)?void 0:t}(s.grant.expiresAt));const a={...void 0===r.mechanism?{}:{mechanism:r.mechanism},...void 0===i?{}:{position:i},...void 0===o?{}:{expiresAt:o}};return"ended"===s.phase?{stage:"routed",phase:"ended",outcomeType:s.outcome.type,...a}:{stage:"routed",phase:s.phase,...a}}function d(t,n){const r=e;switch(t.stage){case"ready":return{version:r,experienceId:n,stage:"ready"};case"routing":return{version:r,experienceId:n,stage:"routing"};case"gated":return{version:r,experienceId:n,stage:"gated"};case"routed":{const e=void 0===t.mechanism?{}:{mechanism:t.mechanism},i=void 0===t.expiresAt?{}:{expiresAt:t.expiresAt};switch(t.phase){case"unavailable":return{version:r,experienceId:n,stage:"routed",phase:"unavailable"};case"scheduled":return{version:r,experienceId:n,stage:"routed",phase:"scheduled",...e};case"enterable":return{version:r,experienceId:n,stage:"routed",phase:"enterable",...e};case"participating":if("queue"===t.mechanism){return{version:r,experienceId:n,stage:"routed",phase:"participating",mechanism:"queue",...void 0===t.position?{}:{position:t.position}}}return{version:r,experienceId:n,stage:"routed",phase:"participating",...void 0===t.mechanism?{}:{mechanism:t.mechanism}};case"settling":return{version:r,experienceId:n,stage:"routed",phase:"settling",...e};case"granted":return{version:r,experienceId:n,stage:"routed",phase:"granted",...e,...i};case"ended":return{version:r,experienceId:n,stage:"routed",phase:"ended",...e,outcome:{type:t.outcomeType}}}return null}}}function p(e,n){const p=n?.target??("undefined"!=typeof document?document.documentElement:null);if(!p){const t=d(u(e.view$.get()),e.snapshot$.get().experienceId);return null!==t&&("outcome"in t&&Object.freeze(t.outcome),Object.freeze(t)),{getState(){if(null===t)throw new Error("DOM bridge projection has no event detail");return t},dispose(){}}}return function(e,n){const p=n,m=e.snapshot$.get().experienceId,l=/* @__PURE__ */new Set,g=/* @__PURE__ */new Set,h=new Map(c.map(e=>[e,p.getAttribute(e)]));let f,v=e.view$.get(),x=null,b=!1;function y(e){const t=/* @__PURE__ */new Map([[r,m],[i,e.stage]]);void 0!==e.phase&&t.set(s,e.phase),void 0!==e.mechanism&&t.set(o,e.mechanism),void 0!==e.expiresAt&&t.set(a,String(e.expiresAt));for(const n of l)t.has(n)||p.removeAttribute(n);l.clear();for(const[n,r]of t)p.getAttribute(n)!==r&&p.setAttribute(n,r),l.add(n),g.add(n)}function A(e){if(r=e,(n=I).stage===r.stage&&n.phase===r.phase&&n.mechanism===r.mechanism&&n.position===r.position&&n.expiresAt===r.expiresAt&&n.outcomeType===r.outcomeType)return;var n,r;const i=d(e,m);null!==i&&(I=e,y(e),"outcome"in i&&Object.freeze(i.outcome),Object.freeze(i),O=i,p.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,detail:i})))}function w(e){const t="routed"===e.journeyStage&&"sequence"in e?e.sequence:null,n="participating"===t?.phase&&"queue"===t.mechanism?t.display$:null;return n===x?x:(f?.(),f=void 0,x=null,null==n?null:(x=n,f=n.listen(e=>{A(u(v,e))}),n))}const j=w(v);let I=u(v,j?.get()),O=d(I,m);null!==O&&("outcome"in O&&Object.freeze(O.outcome),Object.freeze(O)),y(I);const S=e.view$.listen(e=>{v=e,w(e),A(u(e,x?.get()))});return{getState(){if(null===O)throw new Error("DOM bridge projection has no event detail");return O},dispose(){if(!b){b=!0,S(),f?.(),f=void 0;for(const e of g){const t=h.get(e)??null;null===t?p.removeAttribute(e):p.setAttribute(e,t)}g.clear(),l.clear()}}}}(e,p)}export{p as createDomBridge};
1
+ import{BRIDGE_EVENT_VERSION as e,FANFARE_STATE_CHANGE_EVENT as t}from"@fanfare-io/fanfare-sdk-contracts/bridge";import{describeJourneyState as n}from"./journey-display.js";const r="data-fanfare-experience-id",i="data-fanfare-stage",s="data-fanfare-phase",o="data-fanfare-mechanism",a="data-fanfare-expires-at",c=[r,i,s,o,a];function u(e,t){const r=n(e),i=void 0===t?r.position:"number"==typeof t.position?t.position:void 0;if("routed"!==e.journeyStage||!("sequence"in e))return{stage:e.journeyStage};const s=e.sequence;let o;"grant"in s&&(o=function(e){if(void 0===e)return;const t=Date.parse(e);return Number.isNaN(t)?void 0:t}(s.grant.expiresAt));const a={...void 0===r.mechanism?{}:{mechanism:r.mechanism},...void 0===i?{}:{position:i},...void 0===o?{}:{expiresAt:o}};return"ended"===s.phase?{stage:"routed",phase:"ended",outcomeType:s.outcome.type,...a}:{stage:"routed",phase:s.phase,...a}}function d(t,n){const r=e;switch(t.stage){case"ready":return{version:r,experienceId:n,stage:"ready"};case"routing":return{version:r,experienceId:n,stage:"routing"};case"gated":return{version:r,experienceId:n,stage:"gated"};case"routed":{const e=void 0===t.mechanism?{}:{mechanism:t.mechanism},i=void 0===t.expiresAt?{}:{expiresAt:t.expiresAt};switch(t.phase){case"unavailable":return{version:r,experienceId:n,stage:"routed",phase:"unavailable"};case"scheduled":return{version:r,experienceId:n,stage:"routed",phase:"scheduled",...e};case"enterable":return{version:r,experienceId:n,stage:"routed",phase:"enterable",...e};case"participating":if("queue"===t.mechanism){return{version:r,experienceId:n,stage:"routed",phase:"participating",mechanism:"queue",...void 0===t.position?{}:{position:t.position}}}return{version:r,experienceId:n,stage:"routed",phase:"participating",...void 0===t.mechanism?{}:{mechanism:t.mechanism}};case"settling":return{version:r,experienceId:n,stage:"routed",phase:"settling",...e};case"granted":return{version:r,experienceId:n,stage:"routed",phase:"granted",...e,...i};case"ended":return{version:r,experienceId:n,stage:"routed",phase:"ended",...e,outcome:{type:t.outcomeType}}}return null}}}function p(e,n){const p=n?.target??("undefined"!=typeof document?document.documentElement:null);if(!p){const t=d(u(e.view$.get()),e.snapshot$.get().experienceId);return null!==t&&("outcome"in t&&Object.freeze(t.outcome),Object.freeze(t)),{getState(){if(null===t)throw new Error("DOM bridge projection has no event detail");return t},dispose(){}}}return function(e,n){const p=n,m=e.snapshot$.get().experienceId,l=/* @__PURE__ */new Set,g=/* @__PURE__ */new Set,h=new Map(c.map(e=>[e,p.getAttribute(e)]));let f,v=e.view$.get(),x=null,b=!1;function y(e){const t=/* @__PURE__ */new Map([[r,m],[i,e.stage]]);void 0!==e.phase&&t.set(s,e.phase),void 0!==e.mechanism&&t.set(o,e.mechanism),void 0!==e.expiresAt&&t.set(a,String(e.expiresAt));for(const n of l)t.has(n)||p.removeAttribute(n);l.clear();for(const[n,r]of t)p.getAttribute(n)!==r&&p.setAttribute(n,r),l.add(n),g.add(n)}function A(e){if(r=e,(n=I).stage===r.stage&&n.phase===r.phase&&n.mechanism===r.mechanism&&n.position===r.position&&n.expiresAt===r.expiresAt&&n.outcomeType===r.outcomeType)return;var n,r;const i=d(e,m);null!==i&&(I=e,y(e),"outcome"in i&&Object.freeze(i.outcome),Object.freeze(i),O=i,p.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,detail:i})))}function w(e){const t="routed"===e.journeyStage&&"sequence"in e?e.sequence:null,n="participating"===t?.phase&&"queue"===t.mechanism?t.display$:null;return n===x?x:(f?.(),f=void 0,x=null,null==n?null:(x=n,f=n.listen(e=>{A(u(v,e))}),n))}const j=w(v);let I=u(v,j?.get()),O=d(I,m);null!==O&&("outcome"in O&&Object.freeze(O.outcome),Object.freeze(O)),y(I);const S=e.view$.listen(e=>{v=e,w(e),A(u(e,x?.get()))});return{getState(){if(null===O)throw new Error("DOM bridge projection has no event detail");return O},dispose(){if(!b){b=!0,S(),f?.(),f=void 0;for(const e of g){const t=h.get(e)??null;null===t?p.removeAttribute(e):p.setAttribute(e,t)}g.clear(),l.clear()}}}}(e,p)}export{p as createDomBridge};
@@ -1 +1 @@
1
- import{AuthenticationRoutingGateV1Schema as e,AccessCodeRoutingGateV1Schema as t,BotCheckRoutingGateV1Schema as i,RoutingSequenceAvailabilityV1Schema as s}from"@fanfare-io/fanfare-sdk-contracts/routing";import{GrantedSelectionSchema as r,SelectionPinRequestSchema as n}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as o,computed as a}from"nanostores";import*as c from"valibot";import{createAppointmentDriver as d,withAppointmentTerminalConsumer as u}from"../appointments/appointment.driver.js";import{createAuctionDriver as h,isUpgradeableGenericLoss as l,withAuctionTerminalConsumer as p}from"../auctions/auction.driver.js";import{createError as m,isFanfareError as g,ErrorCodes as y}from"../core/errors.js";import{createLogger as f}from"../core/logger.js";import{generateId as b}from"../core/utils.js";import{createDrawDriver as S,withDrawTerminalConsumer as I}from"../draws/draw.driver.js";import{createQueueDriver as A,withQueueTerminalConsumer as v}from"../queues/queue.driver.js";import{clearExperienceContext as q,clearSequenceContext as w,resolveCapabilityOptions as C,bindDistributionContext as k,bindWaitlist as j,upsertSequenceToken as x,peekSequenceToken as T,getSequenceTokenExpiryMs as E}from"../state/capability-token-registry.js";import{getEventBus as D}from"../state/events.js";import{getSDKStore as R}from"../state/store.js";import{createTimedReleaseDriver as O,withTimedReleaseTerminalConsumer as M}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as G}from"./experience.module.js";import{buildJourneyView as F}from"./journey-view.js";import{createInitialSnapshot as _,getSequencePhase as P,getDistribution as H,normalizeSequenceState as N,buildSnapshot as U,getSequenceId as z,getConsumer as B,getWaitlistAttachment as L,getOutcome as J,getGrant as W,getSequenceState as V,getCheckout as Q,isPreEntryJourney as $,getGates as K,isRoutedState as X,getAdmissionGrantExpiresAt as Z,getParticipation as Y,validateSequenceState as ee}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as te}from"./settlement.js";const ie="Cannot commit a stale sequence transition",se="Cannot commit a sequence transition before routing",re=3e4,ne=6e5,oe=/* @__PURE__ */new Set([y.RATE_LIMITED,y.OVERLOADED,y.SERVICE_UNAVAILABLE,y.TIMEOUT,y.NETWORK_ERROR]),ae=/* @__PURE__ */new Set([y.NETWORK_ERROR,y.TIMEOUT,y.UNAUTHORIZED]),ce=[2e3,5e3,1e4],de=3*ce.length,ue=[2e3,5e3,1e4];function he(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function le(e){const t=Y(e);if(t)return t;if(!X(e))return;const i=e.sequence;return"enterable"===i.phase&&"mechanism"in i&&"auction"===i.mechanism?{id:i.distribution.id,type:"auction"}:void 0}function pe(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase&&"awaiting_assignment"!==e.sequence.selection.status||"ended"===e.sequence.phase)}function me(e){const{grant:t}=e;return t.issuedAt?`${t.sequenceId}|${t.distributionId}|${t.issuedAt}`:t.token}class ge{constructor(e,t){this.experienceId=e,this.sdk=t,this.store=R(),this.routingSession=this.store.session,this.logger=f(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.consecutiveGatedRestores=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredSnapshotNeedsPersist=!1,this.automaticAttemptAtom=o(void 0),this.refusedDistributionAtom=o(/* @__PURE__ */new Set),this.automaticEntry=!0,this.EVENT_BUFFER_LIMIT=20;const i=this.restoreFromStore(),s=i??_(this.experienceId),r=i?le(i):void 0,n=P(s);!r||"participating"!==n&&"enterable"!==n||this.ensureDisplayAtom(r),this.stateAtom=o(s),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=a([this.stateAtom,this.automaticAttemptAtom,this.refusedDistributionAtom],(e,t,i)=>{const s="routed"===e.journeyStage?H(e)?.id:void 0,r=void 0!==s&&i.has(s);return this.buildView(e,t??(r?"refused":void 0))}),this.events$=a(this.stateAtom,e=>e.events),this.latestEvent$=a(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),i?(this.restoredSnapshotNeedsPersist&&this.persist(),this.rearmTimersIfNeeded()):this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{void 0!==e?.automaticEntry&&(this.automaticEntry=e.automaticEntry),e?.reset&&(this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRoutedRuntimeState(),this.setSnapshot(_(this.experienceId))),"accessCode"in(e??{})&&this.patchJourneyBase({accessCode:e?.accessCode??null}),await this.routeAndLoadDistribution(this.stateAtom.get().accessCode)})}async reroute(e={}){await this.runSerializedOperation(async()=>{"accessCode"in e&&this.patchJourneyBase({accessCode:e.accessCode??null}),await this.routeAndLoadDistribution(e.accessCode??this.stateAtom.get().accessCode)})}async submitAccessCode(e){return await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage){await this.routeAndLoadDistribution(e,{transient:!0});const t=this.stateAtom.get();return"routed"!==t.journeyStage?"gated"===t.journeyStage?{outcome:"gated"}:{outcome:"unchanged"}:(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:t.sequenceId})}const i=t.sequenceId,s=t.accessCode,r=new Set(t.offers.map(e=>e.sequence.id));await this.routeAndLoadDistribution(e,{transient:!0});let n=this.stateAtom.get();return"routed"===n.journeyStage&&n.sequenceId===i?{outcome:"unchanged"}:"routed"===n.journeyStage&&r.has(n.sequenceId)?(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:n.sequenceId}):("string"==typeof s&&s!==e&&(await this.routeAndLoadDistribution(s,{carryFrom:t,transient:!0}),n=this.stateAtom.get()),"gated"===n.journeyStage?{outcome:"gated"}:{outcome:"unchanged"})})}async retry(){await this.runSerializedOperation(()=>this.routeAndLoadDistribution(this.stateAtom.get().accessCode))}destroy(){this.destroyed||(this.destroyed=!0,this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.stopPolling(),q(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(_(this.experienceId)))}async resumeFromMe(e){const t=e.active.journeys.find(e=>e.experienceId===this.experienceId&&"routed"===e.journeyStage);if(!t||"routed"!==t.journeyStage)return!1;const i=this.stateAtom.get(),s=N({sequence:We(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s,waitlist:this.buildWaitlistAttachmentFromMe(e,t.sequenceId)});this.setSnapshot(n),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&w(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return"granted"!==s.phase&&"ended"!==s.phase||(this.stopRuntimeMonitoring(!0),this.resetDistributionSelfCheck()),r&&"granted"===s.phase&&(this.clearGrantExpiryTimer(),this.checkoutStarted=!1),this.rearmTimersIfNeeded(),r&&"granted"===s.phase&&"granted"===P(this.stateAtom.get())&&(this.emitEvent("sequence_change","success","user","Grant issued",{grant:s.grant.token}),D().emit("journey:granted",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:s.grant.token,distributionType:s.grant.distributionType})),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get();try{const t=z(e);if(!t)throw m.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,C({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(z(s)!==t)return;const r=B(s),n=this.buildDistributionSummary(i,t,{consumer:r,settlingDistributionId:"routed"===s.journeyStage&&"settling"===s.sequence.phase?s.sequence.distribution.id:void 0}),o=this.buildWaitlistAttachmentFromContext(i,L(s)),a=H(s);n&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&j({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,J(s)),d=this.withTerminalConsumer(r,c),u=!n&&"routed"===s.journeyStage&&("granted"===s.sequence.phase||"ended"===s.sequence.phase)?s.sequence:this.projectSequenceState({distribution:n,consumer:d,grant:W(s),settlement:Qe(s),selection:$e(s),checkout:Ve(s),outcome:c});this.commit(u,{waitlist:o,lastSyncedAt:Date.now(),degraded:!1}),this.emitIfDistributionChanged(a,n),"granted"===u.phase||"ended"===u.phase?this.resetDistributionSelfCheck():this.evaluateDistributionSelfCheck(n),await this.attemptAutomaticEntry()}catch(t){const i=this.stateAtom.get();if(z(i)===z(e)&&"routed"===i.journeyStage&&this.isQuietableDegradationError(t))throw this.markDegraded(),this.markErrorQuietable(t);throw t}})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw m.validationError("Waitlist is not available for this sequence");const r=await this.sdk.waitlists.enter(s);this.commit(t,{waitlist:{waitlistId:s,status:"waitlisted",entryId:r.id}}),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:s})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=V(e),i=H(e),s=L(e)?.waitlistId;"scheduled"===t?.phase&&i&&s&&(await this.sdk.waitlists.leave(s),this.commit(t,{waitlist:{waitlistId:s,status:"not_waitlisted"}}),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:s}))})}async loadSelectionOptions(){const e=z(this.stateAtom.get());if(!e)throw m.validationError("No sequence is available to load selection options");return this.sdk.selections.loadOptions(e,C({experienceId:this.experienceId,sequenceId:e}))}async select(e){await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage)throw m.validationError("No sequence is available to select against");const i=t.sequence;if(!("participating"===i.phase&&"draw"===i.mechanism||"granted"===i.phase&&"timed_release"!==i.mechanism))throw m.validationError("Selection is not available in the current phase");const s=t.sequenceId,r=await this.sdk.selections.select(s,e,C({experienceId:this.experienceId,sequenceId:s})),n=this.stateAtom.get();if("routed"!==n.journeyStage||n.sequenceId!==s)return;const o=n.sequence;if("participating"===i.phase){if("participating"!==o.phase||"draw"!==o.mechanism||o.distribution.id!==i.distribution.id||o.consumer.id!==i.consumer.id)return;if("recorded"!==r.status)return;this.commit({...o,consumer:{...o.consumer,selection:{productId:r.productId,variantId:r.variantId}}});const e=this.displayAtom?.get();"draw"===e?.type&&this.displayAtom?.set({...e,selectionAvailability:void 0})}else{if("granted"!==o.phase||o.mechanism!==i.mechanism||me(o)!==me(i))return;if("recorded"===r.status)return;this.commit({...o,selection:r})}})}claim(){const e=this.stateAtom.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase&&"resolved"!==e.sequence.selection.status)throw m.validationError("Selection must be resolved before claiming the grant");const t=W(e);if(!t)throw m.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:t.distributionType,distributionId:t.distributionId,sequenceId:t.sequenceId}),D().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:t.token}),t}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw m.validationError("No paused checkout to resume");await this.submitCheckout(e)})}async runCheckoutOperation(e){try{await this.runSerializedOperation(e)}catch(t){if(await this.reconcileMissingCheckoutAdmission(t))return void(this.pendingCheckoutPaymentInput=void 0);throw t}}async reconcileMissingCheckoutAdmission(e){if(!g(e)||e.code!==y.ADMISSION_NOT_FOUND)return!1;const t=z(this.stateAtom.get());try{const e=await this.sdk.experiences.getMe();if(this.destroyed||z(this.stateAtom.get())!==t)return!1;const i=await this.resumeFromMe(e),s=this.stateAtom.get();return i&&z(s)===t&&"ended"===P(s)&&void 0!==Q(s)}catch{return!1}}async submitCheckout(e){const t=this.stateAtom.get(),i=V(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw m.validationError("No grant awaiting embedded checkout");const s=z(t),r=i.grant.token,n=me(i);this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(o){throw g(o)&&o.code===y.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),o}const a=this.stateAtom.get(),c=V(a);if(z(a)!==s||"granted"!==c?.phase||me(c)!==n)throw m.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...c,pendingAction:t.nextAction}),void this.emitEvent("sequence_change","info","user","Checkout awaiting verification",{processor:t.nextAction.processor});this.pendingCheckoutPaymentInput=void 0,this.transitionToEnded({checkout:t.checkout})}finally{const e=V(this.stateAtom.get());"granted"!==e?.phase||e.pendingAction||this.rearmTimersIfNeeded()}}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){let i=t?.carryFrom??this.stateAtom.get();const s=this.store.session,r=s?.consumerId!==this.routingSession?.consumerId||s?.type!==this.routingSession?.type;r&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),r&&"routed"===i.journeyStage&&$(i)&&(this.clearRoutedRuntimeState(),w(this.experienceId,i.sequenceId),i=U({base:this.baseFromSnapshot(i),journeyStage:"ready"}),this.setSnapshot(i)),this.routingSession=s;const n=i,o=z(n);let a,c=!1;try{const i=await this.resolveRouting(e,{transient:t?.transient});if(!i||this.destroyed)return;if(a=this.stateAtom.get().revision,c=o===i.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const s=await this.sdk.experiences.getCurrentDistributions(i.sequenceId,C({experienceId:this.experienceId,sequenceId:i.sequenceId}));if(this.destroyed)return;const r=c?B(n):void 0,d=this.buildDistributionSummary(s,i.sequenceId,{consumer:r,settlingDistributionId:c&&"routed"===n.journeyStage&&"settling"===n.sequence.phase?n.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(s,c?L(n):void 0);d&&k({experienceId:this.experienceId,sequenceId:i.sequenceId,distributionContext:d}),u&&j({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:i.sequenceId});const h=c?W(n):void 0,l=c?Ve(n):void 0,p=c?Qe(n):void 0,m=c?this.deriveTerminalOutcomeForClosedDistribution(r,d,J(n)):void 0,g=this.withTerminalConsumer(r,m),y=!d&&c&&"routed"===n.journeyStage&&("granted"===n.sequence.phase||"ended"===n.sequence.phase)?n.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:c?$e(n):void 0,checkout:l,outcome:m});c||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),b=U({base:this.baseFromSnapshot(f,{lastSyncedAt:Date.now(),degraded:!1}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:y,waitlist:u,sequenceRef:i.selectedSequence});this.setSnapshot(b),this.resumeDeferredGrantHydration(),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const S=c?H(n):void 0;this.emitIfDistributionChanged(S,d),this.evaluateDistributionSelfCheck(d),t?.transient||await this.attemptAutomaticEntry()}catch(d){if("routed"===n.journeyStage&&c&&this.isQuietableDegradationError(d))throw this.restoreQuietSnapshot(n),this.markErrorQuietable(d);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===o&&this.restoreClearedSequenceContext(n,o),void 0!==a&&this.stateAtom.get().revision===a)this.setSnapshot(n);else{const e=this.stateAtom.get();"routed"===n.journeyStage?this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:n.sequenceId,offers:n.offers,sequence:n.sequence,waitlist:n.waitlist,sequenceRef:n.sequenceRef})):this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw d}}clearRefusedDistributionOnStanding(e){const t=this.refusedDistributionAtom.get();if(0===t.size||"routed"!==e.journeyStage)return;const i=H(e)?.id;if(void 0===i||!t.has(i))return;const s=e.sequence.phase;"participating"!==s&&"granted"!==s||this.refusedDistributionAtom.set(new Set([...t].filter(e=>e!==i)))}clearAutomaticAttemptIfSuperseded(e){const t=this.automaticAttemptTarget;if(!t||"routing"===e.journeyStage)return;"routed"===e.journeyStage&&e.sequenceId===t.sequenceId&&H(e)?.id===t.distributionId&&"enterable"===e.sequence.phase||this.clearAutomaticAttempt()}clearAutomaticAttempt(){this.automaticAttemptTarget=void 0,this.automaticAttemptAtom.set(void 0)}async attemptAutomaticEntry(){if(!this.automaticEntry||this.destroyed)return;const e=this.buildView(this.stateAtom.get());if("routed"!==e.journeyStage)return;const t=H(this.stateAtom.get())?.id;if(void 0!==t&&this.refusedDistributionAtom.get().has(t))return;const i=this.automaticAttemptAtom.get()?this.automaticAttemptTarget:void 0;if(i&&i.distributionId===t)return;const s=e.sequence;if("enterable"!==s.phase||!("entry"in s)||!s.entry.automatic)return;const r=this.sequenceDrivers[s.mechanism];if(!r?.enterWithinOperation)return;const n=e.sequenceId;this.automaticAttemptTarget={sequenceId:n,distributionId:t},this.automaticAttemptAtom.set("entering");try{await r.enterWithinOperation()}catch(o){if(this.destroyed)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage||e.sequenceId!==n||"enterable"!==e.sequence.phase)return void this.clearAutomaticAttempt();this.clearAutomaticAttempt(),void 0===t||g(o)&&ae.has(o.code)||this.refusedDistributionAtom.set(/* @__PURE__ */new Set([...this.refusedDistributionAtom.get(),t])),this.emitEvent("error","error","user","Automatic entry was refused",{action:"enter",...t?{distributionId:t}:{},...g(o)?{code:o.code}:{}})}}restoreClearedSequenceContext(e,t){const i=H(e);i&&k({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=L(e);s&&j({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&x({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}restoreUndisplacedJourney(e,t){const i=this.stateAtom.get();return i.revision===t?(this.setSnapshot(e),!0):"routing"===i.journeyStage&&(this.setSnapshot(U({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})),!0)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=z(i);this.transitionToRouting();const r=this.stateAtom.get().revision;let n;try{n=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(a){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),a}if(this.destroyed)return null;if("gated"===n.outcome&&n.selected){const e=n.selected.gates;return"routed"!==i.journeyStage||$(i)?("routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:n.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null):(this.restoreUndisplacedJourney(i,r)&&(this.rearmTimersIfNeeded(),"ended"!==P(this.stateAtom.get())&&(this.scheduleGatedCapabilityRefresh(),this.emitEvent("requirement","info","user","Gates required",{gates:e}))),null)}if("no_access"===n.outcome||!n.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new G("No access available for this experience");const o=n.selected.sequence.id;if(s&&s!==o&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=T(this.experienceId,s),w(this.experienceId,s)),this.consecutiveGatedRestores=0,n.selected.capabilityGrant&&x({experienceId:this.experienceId,sequenceId:o,token:n.selected.capabilityGrant,expiresAt:n.selected.capabilityGrantExpiresAt}),s!==o){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,r=n.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,r);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:o,...void 0===e?{}:{previousPriority:e},priority:r,direction:a})}return{sequenceId:o,selectedSequence:n.selected.sequence,offers:n.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(K(e).length>0)return;if(e.experience)return;const t=await this.sdk.experiences.enter(this.experienceId);this.destroyed||this.patchJourneyBase({experience:t})}createSequenceDrivers(){return{appointment:d({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),getDisplayState:()=>this.displayAtom?.get()??null,commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>this.ensureDisplayAtom(e),stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),auction:h({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e)}),queue:A({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),timed_release:O({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),draw:S({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>V(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)})}}buildView(e,t){return F(e,{start:e=>this.start(e),reroute:e=>this.reroute(e),submitAccessCode:e=>this.submitAccessCode(e),retry:()=>this.retry(),joinWaitlist:()=>this.joinWaitlistAttachment(),exitWaitlist:()=>this.exitWaitlistAttachment(),claim:()=>this.claim(),select:e=>this.select(e),loadSelectionOptions:()=>this.loadSelectionOptions(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers,t)}projectSequenceState(e){if(!e.distribution)return{phase:"unavailable"};const t=this.sequenceDrivers[e.distribution.type];if(!t)throw new Error(`${e.distribution.type} sequence driver is required`);return t.project(e)}patchJourneyBase(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=this.baseFromSnapshot(t,e);switch(t.journeyStage){case"ready":return void this.setSnapshot(U({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(U({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(U({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(U({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,waitlist:t.waitlist,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(U({base:this.baseFromSnapshot(t),journeyStage:"gated",gates:e.gates,selected:e.selected}))}baseFromSnapshot(e,t={}){const i=Object.prototype.hasOwnProperty.call(t,"accessCode")?t.accessCode??void 0:e.accessCode,s=Object.prototype.hasOwnProperty.call(t,"degraded")?t.degraded||void 0:e.degraded,r=Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")?t.lastSyncedAt:e.lastSyncedAt;return{revision:e.revision,updatedAt:e.updatedAt,experienceId:e.experienceId,accessCode:i,experience:t.experience??e.experience,events:t.events??e.events,lastSeenEventId:t.lastSeenEventId??e.lastSeenEventId,degraded:s,lastSyncedAt:r}}commit(e,t={}){if(this.destroyed)throw m.notInitialized("Journey has been destroyed");const i=this.stateAtom.get();if("routed"!==i.journeyStage)throw m.validationError(se);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,i.sequenceId))throw m.validationError(ie);const s=Object.prototype.hasOwnProperty.call(t,"waitlist")?t.waitlist??void 0:i.waitlist,r={};Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")&&(r.lastSyncedAt=t.lastSyncedAt),Object.prototype.hasOwnProperty.call(t,"degraded")&&(r.degraded=t.degraded);const n=U({base:this.baseFromSnapshot(i,{...r,events:t.events??i.events}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:e,waitlist:s,sequenceRef:i.sequenceRef});this.setSnapshot(n)}markDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded||this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}clearDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded&&this.setSnapshot(this.rebuildSnapshot(e,{degraded:!1}))}restoreQuietSnapshot(e){this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}isQuietableDegradationError(e){return g(e)&&(e.retryable||oe.has(e.code))}markErrorQuietable(e){return e&&"object"==typeof e&&(e.quietable=!0),e}rebuildSnapshot(e,t={}){const i=this.baseFromSnapshot(e,t);switch(e.journeyStage){case"ready":return U({base:i,journeyStage:"ready"});case"routing":return U({base:i,journeyStage:"routing"});case"gated":return U({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return U({base:i,journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})}}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:b(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t,i){if(e.active){const s=this.sequenceDrivers[e.active.type],r=!0===s?.settlementCanStartWhileActive&&i?.settlingDistributionId===e.active.id?"settling":"open";return{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:r,details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}}if(e.upcoming)return{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",...e.upcoming.details?{details:e.upcoming.details,closesAt:e.upcoming.details.closeAt}:{},startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,updatedAt:Date.now()};if(e.recentPast){const s=i?.settlingDistributionId===e.recentPast.id||te(i?.consumer,e.recentPast.type,e.recentPast.id)?"settling":"closed";return{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:s,result:e.recentPast.result,closesAt:e.recentPast.endedAt,updatedAt:Date.now()}}}buildWaitlistAttachmentFromContext(e,t){const i=e.waitlist?.id;if(i)return t?.waitlistId===i?t:{waitlistId:i,status:"not_waitlisted"}}buildWaitlistAttachmentFromMe(e,t){const i=e.waitlists?.find(e=>e.experienceId===this.experienceId&&e.sequenceId===t);if(i)return{waitlistId:i.waitlistId,status:"waitlisted"}}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=Je(e[this.experienceId]);return t?"routing"===t.journeyStage||"routed"===t.journeyStage&&$(t)?("routed"===t.journeyStage&&w(this.experienceId,t.sequenceId),this.restoredSnapshotNeedsPersist=!0,U({base:this.baseFromSnapshot(t),journeyStage:"ready"})):t:(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=le(e);"auction"===s?.type&&"auction"!==this.displayAtom?.get().type&&(this.resetDisplayAtom("auction"),this.displayParticipationId=s.id),"routed"!==e.journeyStage&&"routing"!==e.journeyStage&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRefusedDistributionOnStanding(e),this.clearAutomaticAttemptIfSuperseded(e),this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?P(e):void 0,r=t.journeyStage,n="routed"===r?P(t):void 0;i===r&&s===n||D().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw m.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToEnded(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=z(i);if(!s)throw m.validationError(se);const r=H(i),n="checkout"in e?e.checkout:void 0,o="checkout"in e?{type:"completed"}:e,a=this.reinterpretLapsedGrantOutcome(i,o),c={...a,distributionType:a.distributionType??r?.type,distributionId:a.distributionId??r?.id,at:a.at??/* @__PURE__ */(new Date).toISOString()},d=this.withTerminalConsumer(B(i),c,{finalize:!0}),u=this.projectSequenceState({distribution:r,consumer:d,checkout:n,outcome:c});this.commit(u,t),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.emitEvent("sequence_change","expired"===c.type?"warning":"info","user","Sequence ended",{outcome:c}),D().emit("journey:ended",{experienceId:this.experienceId,sequenceId:s,outcome:c})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==P(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){if(!this.destroyed){if("settling"===e.type){const t=this.stateAtom.get();if(!X(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:B(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?"resync"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration():this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(pe(i,i.sequenceId)||this.pendingGrantHydration?.sequenceId!==i.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:i.sequenceId,token:e,expiresAt:t,attempt:0},this.hydrateGrantFromMe(e,t))):this.pendingGrantObservation={token:e,expiresAt:t}}resumeDeferredGrantHydration(){const e=this.pendingGrantObservation;e&&(this.pendingGrantObservation=void 0,this.beginGrantHydration(e.token,e.expiresAt))}async hydrateGrantFromMe(e,t){if(this.destroyed)return;const i=this.pendingGrantHydration,s=this.stateAtom.get();if(i)if("routing"!==s.journeyStage)if(pe(s,i.sequenceId))this.clearGrantHydration();else{if(void 0!==t&&Date.now()>=t)return this.clearAutomaticAttempt(),void this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});try{const e=await this.sdk.experiences.getMe();if(this.destroyed)return;await this.resumeFromMe(e)}catch(r){this.emitEvent("error","warning","system","Could not resolve an observed grant from /me",{grant:e,error:r instanceof Error?r.message:String(r)})}this.scheduleGrantHydrationRetry()}else this.scheduleGrantHydrationRetry()}scheduleGrantHydrationRetry(){const e=this.pendingGrantHydration;if(!e||this.destroyed)return;if(pe(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=ue[e.attempt]??1e4;e.attempt+=1,e.timer=setTimeout(()=>{this.pendingGrantHydration===e&&(e.timer=void 0,this.hydrateGrantFromMe(e.token,e.expiresAt))},t)}clearGrantHydration(){const e=this.pendingGrantHydration;e?.timer&&clearTimeout(e.timer),this.pendingGrantHydration=void 0,this.pendingGrantObservation=void 0}applyMonitorUpdateFromCallback(e){try{this.applyMonitorUpdate(e)}catch(t){if(this.dropStaleCallbackCommit(t,"monitor"))return;throw t}}startMonitorForParticipation(e){const t=this.ensureDisplayAtom(e),i=e=>this.applyMonitorUpdateFromCallback(e),s=e=>{e?this.markDegraded():this.clearDegraded()},r=this.sequenceDrivers[e.type];if(r?.startMonitoring)return r.startMonitoring({participation:e,displayAtom:t,onUpdate:i,onDegradedChange:s}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>r.stopMonitoring?.()});throw new Error(`Unknown distribution type: ${e.type}`)}stopRuntimeMonitoring(e=!1){this.activeMonitor&&(this.activeMonitor.stop(),this.activeMonitor=null),e&&this.clearRuntimeDisplayState()}clearGrantExpiryTimer(){this.grantExpiryTimer&&(clearTimeout(this.grantExpiryTimer),this.grantExpiryTimer=void 0)}clearCapabilityRefreshTimer(){this.capabilityRefreshTimer&&(clearTimeout(this.capabilityRefreshTimer),this.capabilityRefreshTimer=void 0)}clearDistributionSelfCheck(){this.selfCheckTimer&&(clearTimeout(this.selfCheckTimer),this.selfCheckTimer=void 0),this.selfCheckKey=void 0,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0}resetDistributionSelfCheck(){this.clearDistributionSelfCheck(),this.selfCheckTotalAttempts=0}evaluateDistributionSelfCheck(e){if(this.destroyed)return;const t=Date.now();if(!he(e,t))return void this.clearDistributionSelfCheck();const i=`${e?.sequenceId??""}:${e?.id??""}:${e?.opensAt??""}`;if(this.selfCheckKey!==i&&(this.clearDistributionSelfCheck(),this.selfCheckKey=i,this.selfCheckStartedAtMs=t),this.selfCheckTimer)return;if(this.selfCheckAttempts>=ce.length)return;if(this.selfCheckTotalAttempts>=de)return;const s=this.selfCheckStartedAtMs+ce[this.selfCheckAttempts];this.selfCheckAttempts+=1,this.selfCheckTotalAttempts+=1,this.selfCheckTimer=setTimeout(()=>{this.selfCheckFromTimer()},Math.max(0,s-t))}async selfCheckFromTimer(){if(this.selfCheckTimer=void 0,!this.destroyed)try{await this.refreshDistribution()}catch(e){if(this.destroyed||"routed"!==this.stateAtom.get().journeyStage)return;this.emitEvent("error","warning","system","Distribution self-check failed",{error:e instanceof Error?e.message:String(e)})}}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=E(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleGatedCapabilityRefresh(){const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=Math.min(re*2**this.consecutiveGatedRestores,ne);this.consecutiveGatedRestores+=1;const i=E(this.experienceId,e.sequenceId),s=void 0===i?t:Math.max(i-Date.now()-6e4,t);this.scheduleCapabilityRefresh(s)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();"routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),this.pendingGrantHydration&&pe(e,this.pendingGrantHydration.sequenceId)&&this.scheduleGrantHydrationRetry(),"routed"===e.journeyStage&&"granted"===e.sequence.phase&&"awaiting_assignment"===e.sequence.selection.status&&this.pendingGrantHydration?.sequenceId!==e.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:e.sequenceId,token:e.sequence.grant.token,expiresAt:Z(e),attempt:0},this.scheduleGrantHydrationRetry());const t=V(e);if("granted"===t?.phase&&(t.pendingAction||"internal"===t.settlement.processor&&"at_end"===t.settlement.mode&&"in_progress"===t.settlement.state))this.clearGrantExpiryTimer();else if("granted"===t?.phase&&!this.grantExpiryTimer){const t=Z(e);if(t){const e=t-Date.now();e<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),e)}}}async refreshCapabilityFromTimer(){this.capabilityRefreshTimer=void 0;try{if("routed"!==this.stateAtom.get().journeyStage)return void this.clearCapabilityRefreshTimer();if(this.checkoutStarted)return;const e=Date.now();if(void 0!==this.lastCapabilityRefreshAtMs&&e-this.lastCapabilityRefreshAtMs<re)return void this.scheduleCapabilityRefresh(re);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(re*2**this.consecutiveRefreshFailures,ne);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){this.grantExpiryTimer=void 0;try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!g(e)||e.code!==y.VALIDATION_ERROR||e.message!==ie&&e.message!==se)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=le(e),i=z(e);return t&&i?{experienceId:this.experienceId,sequenceId:i,participationId:t.id,participationType:t.type}:null}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),void this.setSnapshot(_(this.experienceId),{persist:!1});const t=Je(e);if(!t)return;const i="routing"===t.journeyStage?U({base:this.baseFromSnapshot(t),journeyStage:"ready"}):t,s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=le(i);r||(this.displayAtom=null,this.displayParticipationId=void 0),this.setSnapshot(i,{persist:!1}),r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){const t=this.displayAtom;if(t?.get().type===e.type&&this.displayParticipationId===e.id)return t;const i=this.resetDisplayAtom(e.type);return this.displayParticipationId=e.id,i}resetDisplayAtom(e){const t=this.displayAtom;if(!t||t.get().type!==e){const t=o({type:e});return this.displayAtom=t,t}return t.set({type:e}),t}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();if(!e)return this.displayAtom=null,void(this.displayParticipationId=void 0);this.displayParticipationId=void 0,this.resetDisplayAtom(e.participationType)}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i&&!l(i))return i;if(!t||"closed"!==t.lifecycle||!e)return i;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:i??{type:"closed",distributionType:t.type,distributionId:t.id}}withTerminalConsumer(e,t,i){switch(e?.mechanism){case"appointment":return u(e,t);case"auction":return p(e,t,i);case"draw":return I(e,t);case"queue":return v(e,t);case"timed_release":return M(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const ye=c.picklist(["queue","draw","auction","timed_release","appointment"]),fe=ye,be=c.picklist(["queue","draw","auction","timed_release"]),Se=c.picklist(["draw","auction"]),Ie=c.strictObject({id:c.string(),ts:c.number(),kind:c.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:c.picklist(["info","success","warning","error"]),audience:c.picklist(["user","system","analytics"]),message:c.string(),detail:c.optional(c.record(c.string(),c.unknown()))}),Ae=c.object({id:c.string(),name:c.string(),experienceId:c.string(),priority:c.number(),color:c.nullable(c.string()),availability:c.optional(s)}),ve=c.strictObject({type:ye,id:c.string(),sequenceId:c.optional(c.string()),lifecycle:c.string(),startsAt:c.optional(c.string()),opensAt:c.optional(c.string()),closesAt:c.optional(c.string()),settlesAt:c.optional(c.string()),result:c.optional(c.string()),details:c.optional(c.unknown()),updatedAt:c.optional(c.number())}),qe=c.strictObject({status:c.string(),selection:c.optional(n),id:c.optional(c.string()),distributionId:c.optional(c.string()),waitlistId:c.optional(c.string()),enteredAt:c.optional(c.string()),admissionGrant:c.optional(c.string()),grantedAt:c.optional(c.string()),deniedReason:c.optional(c.string()),position:c.optional(c.number()),highestBid:c.optional(c.string()),currentBid:c.optional(c.string()),slotId:c.optional(c.string()),locationId:c.optional(c.string()),bookedAt:c.optional(c.string()),checkedInAt:c.optional(c.string()),completedAt:c.optional(c.string()),cancelledAt:c.optional(c.string()),noShowAt:c.optional(c.string())}),we=c.strictObject({token:c.string(),distributionType:be,distributionId:c.string(),sequenceId:c.string(),experienceId:c.string(),issuedAt:c.optional(c.string()),expiresAt:c.optional(c.string())}),Ce=c.strictObject({orderId:c.string(),amount:c.string(),currencyCode:c.string(),paymentMethodLast4:c.optional(c.string()),paymentMethodBrand:c.optional(c.string())}),ke=c.strictObject({publishableKey:c.string(),stripeAccount:c.optional(c.string())}),je=c.strictObject({type:c.string(),reason:c.optional(c.string()),at:c.optional(c.string()),distributionType:c.optional(ye),distributionId:c.optional(c.string())}),xe=c.strictObject({phase:c.literal("unavailable"),reason:c.optional(c.string())}),Te=c.strictObject({waitlistId:c.string(),status:c.picklist(["not_waitlisted","waitlisted"]),entryId:c.optional(c.string())}),Ee=c.strictObject({phase:c.literal("scheduled"),mechanism:fe,distribution:ve}),De=c.strictObject({phase:c.literal("enterable"),mechanism:fe,distribution:ve,consumer:qe,paymentProvider:c.optional(ke)}),Re=c.strictObject({phase:c.literal("participating"),mechanism:fe,distribution:ve,consumer:qe}),Oe=c.strictObject({phase:c.literal("settling"),mechanism:Se,distribution:ve,consumer:qe}),Me=c.variant("processor",[c.strictObject({processor:c.literal("external")}),c.strictObject({processor:c.literal("internal"),mode:c.literal("at_end"),state:c.picklist(["awaiting","in_progress"]),paymentProvider:c.optional(ke)}),c.strictObject({processor:c.literal("internal"),mode:c.literal("pre_auth")})]),Ge=c.strictObject({phase:c.literal("granted"),mechanism:be,distribution:ve,consumer:qe,grant:we,settlement:Me,selection:r}),Fe=c.strictObject({phase:c.literal("ended"),mechanism:fe,distribution:ve,consumer:qe,outcome:je,checkout:c.optional(Ce)}),_e=c.union([xe,Ee,De,Re,Oe,Ge,Fe]),Pe={schemaVersion:c.literal(6),revision:c.number(),updatedAt:c.number(),experienceId:c.string(),accessCode:c.optional(c.string()),experience:c.optional(c.unknown()),events:c.array(Ie),lastSeenEventId:c.optional(c.string()),lastSyncedAt:c.optional(c.number())},He=c.strictObject({...Pe,journeyStage:c.literal("ready")}),Ne=c.strictObject({...Pe,journeyStage:c.literal("routing")}),Ue=c.variant("type",[c.object({...c.omit(e,["reason"]).entries,requirement:c.optional(c.picklist(["session","authenticated"]),"authenticated")}),c.omit(t,["reason"]),c.omit(i,["reason"])]),ze=c.strictObject({...Pe,journeyStage:c.literal("gated"),gates:c.array(Ue),selected:c.optional(c.strictObject({sequence:c.unknown(),status:c.literal("gated")}))}),Be=c.strictObject({...Pe,journeyStage:c.literal("routed"),sequenceId:c.string(),offers:c.array(c.object({sequence:c.unknown(),status:c.literal("gated"),gates:c.array(Ue)})),sequence:_e,waitlist:c.optional(Te),sequenceRef:c.optional(Ae)}),Le=c.union([He,Ne,ze,Be]);function Je(e){const t=c.safeParse(Le,e);if(!t.success)return null;const{schemaVersion:i,...s}=t.output;if("routed"===s.journeyStage)try{ee(s.sequence)}catch{return null}return s}function We(e){if("participating"!==e.phase&&"settling"!==e.phase)return e;if(!("selection"in e))return e;const{selection:t,...i}=e;return void 0===t?i:{...i,consumer:{...i.consumer,selection:t}}}function Ve(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?Q(e):e.sequence.checkout}function Qe(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function $e(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{ge as ExperienceJourney,he as isSelfContradictingDistribution};
1
+ import{AuthenticationRoutingGateV1Schema as e,AccessCodeRoutingGateV1Schema as t,BotCheckRoutingGateV1Schema as i,RoutingSequenceAvailabilityV1Schema as s}from"@fanfare-io/fanfare-sdk-contracts/routing";import{GrantedSelectionSchema as r,SelectionPinRequestSchema as n}from"@fanfare-io/fanfare-sdk-contracts/selection";import{atom as o,computed as a}from"nanostores";import*as c from"valibot";import{createAppointmentDriver as d,withAppointmentTerminalConsumer as u}from"../appointments/appointment.driver.js";import{createAuctionDriver as h,isUpgradeableGenericLoss as l,withAuctionTerminalConsumer as p}from"../auctions/auction.driver.js";import{createError as m,isFanfareError as g,ErrorCodes as y}from"../core/errors.js";import{createLogger as f}from"../core/logger.js";import{generateId as S}from"../core/utils.js";import{createDrawDriver as b,withDrawTerminalConsumer as A}from"../draws/draw.driver.js";import{createQueueDriver as I,withQueueTerminalConsumer as v}from"../queues/queue.driver.js";import{advanceQueuePrivacyEpoch as q}from"../queues/queue.module.js";import{clearExperienceContext as w,clearSequenceContext as C,resolveCapabilityOptions as k,bindDistributionContext as j,bindWaitlist as x,upsertSequenceToken as T,peekSequenceToken as E,getSequenceTokenExpiryMs as D}from"../state/capability-token-registry.js";import{getEventBus as R}from"../state/events.js";import{getSDKStore as O}from"../state/store.js";import{createTimedReleaseDriver as M,withTimedReleaseTerminalConsumer as G}from"../timed-releases/timed-release.driver.js";import{NoSequenceAvailableError as F}from"./experience.module.js";import{buildJourneyView as _}from"./journey-view.js";import{createInitialSnapshot as P,getSequencePhase as H,getDistribution as N,normalizeSequenceState as U,buildSnapshot as z,getSequenceId as B,getConsumer as L,getWaitlistAttachment as W,getOutcome as J,getGrant as V,getSequenceState as Q,getCheckout as $,isPreEntryJourney as K,getGates as X,isRoutedState as Z,getAdmissionGrantExpiresAt as Y,getParticipation as ee,validateSequenceState as te}from"./journey.machine.js";import{isUnresolvedSettlementConsumer as ie}from"./settlement.js";const se="Cannot commit a stale sequence transition",re="Cannot commit a sequence transition before routing",ne=3e4,oe=6e5,ae=/* @__PURE__ */new Set([y.RATE_LIMITED,y.OVERLOADED,y.SERVICE_UNAVAILABLE,y.TIMEOUT,y.NETWORK_ERROR]),ce=/* @__PURE__ */new Set([y.NETWORK_ERROR,y.TIMEOUT,y.UNAUTHORIZED]),de=[2e3,5e3,1e4],ue=3*de.length,he=[2e3,5e3,1e4];function le(e,t){if("scheduled"!==e?.lifecycle||!e.opensAt)return!1;const i=Date.parse(e.opensAt);return Number.isFinite(i)&&i<=t}function pe(e){const t=ee(e);if(t)return t;if(!Z(e))return;const i=e.sequence;return"enterable"===i.phase&&"mechanism"in i&&"auction"===i.mechanism?{id:i.distribution.id,type:"auction"}:void 0}function me(e){const t="queue"===e?.type&&"queue"===e.details?.type?e.details.openAt:null;return"string"==typeof t&&t?t:null}function ge(e,t){return"routing"!==e.journeyStage&&("routed"!==e.journeyStage||e.sequenceId!==t||"granted"===e.sequence.phase&&"awaiting_assignment"!==e.sequence.selection.status||"ended"===e.sequence.phase)}function ye(e){const{grant:t}=e;return t.issuedAt?`${t.sequenceId}|${t.distributionId}|${t.issuedAt}`:t.token}class fe{constructor(e,t){this.experienceId=e,this.sdk=t,this.store=O(),this.routingSession=this.store.session,this.logger=f(),this.activeMonitor=null,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0,this.selfCheckTotalAttempts=0,this.consecutiveRefreshFailures=0,this.consecutiveGatedRestores=0,this.checkoutStarted=!1,this.displayAtom=null,this.destroyed=!1,this.operationQueue=Promise.resolve(),this.lastClearedSequenceId=null,this.restoredSnapshotNeedsPersist=!1,this.automaticAttemptAtom=o(void 0),this.refusedDistributionAtom=o(/* @__PURE__ */new Set),this.automaticEntry=!0,this.EVENT_BUFFER_LIMIT=20;const i=this.restoreFromStore(),s=i??P(this.experienceId),r=i?pe(i):void 0,n=H(s);!r||"participating"!==n&&"enterable"!==n||this.ensureDisplayAtom(r),this.stateAtom=o(s),this.state=this.stateAtom,this.sequenceDrivers=this.createSequenceDrivers(),this.view$=a([this.stateAtom,this.automaticAttemptAtom,this.refusedDistributionAtom],(e,t,i)=>{const s="routed"===e.journeyStage?N(e)?.id:void 0,r=void 0!==s&&i.has(s);return this.buildView(e,t??(r?"refused":void 0))}),this.events$=a(this.stateAtom,e=>e.events),this.latestEvent$=a(this.stateAtom,e=>{if(!e.events.length)return null;const t=e.lastSeenEventId??"",i=e.events.filter(e=>e.id>t);return i.length?i[i.length-1]:null}),i?(this.restoredSnapshotNeedsPersist&&this.persist(),this.rearmTimersIfNeeded()):this.persist()}getExperienceId(){return this.experienceId}async start(e){await this.runSerializedOperation(async()=>{void 0!==e?.automaticEntry&&(this.automaticEntry=e.automaticEntry),e?.reset&&(this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRoutedRuntimeState(),this.setSnapshot(P(this.experienceId))),"accessCode"in(e??{})&&this.patchJourneyBase({accessCode:e?.accessCode??null}),await this.routeAndLoadDistribution(this.stateAtom.get().accessCode)})}async reroute(e={}){await this.runSerializedOperation(async()=>{"accessCode"in e&&this.patchJourneyBase({accessCode:e.accessCode??null}),await this.routeAndLoadDistribution(e.accessCode??this.stateAtom.get().accessCode)})}async submitAccessCode(e){return await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage){await this.routeAndLoadDistribution(e,{transient:!0});const t=this.stateAtom.get();return"routed"!==t.journeyStage?"gated"===t.journeyStage?{outcome:"gated"}:{outcome:"unchanged"}:(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:t.sequenceId})}const i=t.sequenceId,s=t.accessCode,r=new Set(t.offers.map(e=>e.sequence.id));await this.routeAndLoadDistribution(e,{transient:!0});let n=this.stateAtom.get();return"routed"===n.journeyStage&&n.sequenceId===i?{outcome:"unchanged"}:"routed"===n.journeyStage&&r.has(n.sequenceId)?(this.patchJourneyBase({accessCode:e}),await this.attemptAutomaticEntry(),{outcome:"advanced",sequenceId:n.sequenceId}):("string"==typeof s&&s!==e&&(await this.routeAndLoadDistribution(s,{carryFrom:t,transient:!0}),n=this.stateAtom.get()),"gated"===n.journeyStage?{outcome:"gated"}:{outcome:"unchanged"})})}async retry(){await this.runSerializedOperation(()=>this.routeAndLoadDistribution(this.stateAtom.get().accessCode))}destroy(){this.destroyed||(this.destroyed=!0,this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.stopPolling(),w(this.experienceId),this.store.removeJourney(this.experienceId),this.stateAtom.set(P(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=U({sequence:Qe(t.sequence)}),r="granted"===s.phase&&!("routed"===i.journeyStage&&i.sequenceId===t.sequenceId&&"granted"===i.sequence.phase),n=z({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:t.sequenceId,offers:this.normalizeOffers(t.offers),sequence:s,waitlist:this.buildWaitlistAttachmentFromMe(e,t.sequenceId)});this.setSnapshot(n),"routed"===i.journeyStage&&i.sequenceId!==t.sequenceId&&C(this.experienceId,i.sequenceId);try{await this.reroute()}catch{}return"granted"!==s.phase&&"ended"!==s.phase||(this.stopRuntimeMonitoring(!0),this.resetDistributionSelfCheck()),r&&"granted"===s.phase&&(this.clearGrantExpiryTimer(),this.checkoutStarted=!1),this.rearmTimersIfNeeded(),r&&"granted"===s.phase&&"granted"===H(this.stateAtom.get())&&(this.emitEvent("sequence_change","success","user","Grant issued",{grant:s.grant.token}),R().emit("journey:granted",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:s.grant.token,distributionType:s.grant.distributionType})),!0}async refreshDistribution(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get();try{const t=B(e);if(!t)throw m.validationError("Cannot refresh distribution before routing to a sequence");const i=await this.sdk.experiences.getCurrentDistributions(t,k({experienceId:this.experienceId,sequenceId:t})),s=this.stateAtom.get();if(B(s)!==t)return;const r=L(s),n=this.buildDistributionSummary(i,t,{consumer:r,settlingDistributionId:"routed"===s.journeyStage&&"settling"===s.sequence.phase?s.sequence.distribution.id:void 0}),o=this.buildWaitlistAttachmentFromContext(i,W(s)),a=N(s);n&&j({experienceId:this.experienceId,sequenceId:t,distributionContext:n}),o&&x({waitlistId:o.waitlistId,experienceId:this.experienceId,sequenceId:t});const c=this.deriveTerminalOutcomeForClosedDistribution(r,n,J(s)),d=this.withTerminalConsumer(r,c),u=!n&&"routed"===s.journeyStage&&("granted"===s.sequence.phase||"ended"===s.sequence.phase)?s.sequence:this.projectSequenceState({distribution:n,consumer:d,grant:V(s),settlement:Ke(s),selection:Xe(s),checkout:$e(s),outcome:c});this.commit(u,{waitlist:o,lastSyncedAt:Date.now(),degraded:!1},!0),this.emitIfDistributionChanged(a,n),"granted"===u.phase||"ended"===u.phase?this.resetDistributionSelfCheck():this.evaluateDistributionSelfCheck(n),await this.attemptAutomaticEntry()}catch(t){const i=this.stateAtom.get();if(B(i)===B(e)&&"routed"===i.journeyStage&&this.isQuietableDegradationError(t))throw this.markDegraded(),this.markErrorQuietable(t);throw t}})}async joinWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=Q(e),i=N(e),s=W(e)?.waitlistId;if("scheduled"!==t?.phase||!i||!s)throw m.validationError("Waitlist is not available for this sequence");const r=await this.sdk.waitlists.enter(s);this.commit(t,{waitlist:{waitlistId:s,status:"waitlisted",entryId:r.id}}),this.emitEvent("sequence_change","info","user","Entered waitlist",{waitlistId:s})})}async exitWaitlistAttachment(){await this.runSerializedOperation(async()=>{const e=this.stateAtom.get(),t=Q(e),i=N(e),s=W(e)?.waitlistId;"scheduled"===t?.phase&&i&&s&&(await this.sdk.waitlists.leave(s),this.commit(t,{waitlist:{waitlistId:s,status:"not_waitlisted"}}),this.emitEvent("sequence_change","info","user","Left waitlist",{waitlistId:s}))})}async loadSelectionOptions(){const e=B(this.stateAtom.get());if(!e)throw m.validationError("No sequence is available to load selection options");return this.sdk.selections.loadOptions(e,k({experienceId:this.experienceId,sequenceId:e}))}async select(e){await this.runSerializedOperation(async()=>{const t=this.stateAtom.get();if("routed"!==t.journeyStage)throw m.validationError("No sequence is available to select against");const i=t.sequence;if(!("participating"===i.phase&&"draw"===i.mechanism||"granted"===i.phase&&"timed_release"!==i.mechanism))throw m.validationError("Selection is not available in the current phase");const s=t.sequenceId,r=await this.sdk.selections.select(s,e,k({experienceId:this.experienceId,sequenceId:s})),n=this.stateAtom.get();if("routed"!==n.journeyStage||n.sequenceId!==s)return;const o=n.sequence;if("participating"===i.phase){if("participating"!==o.phase||"draw"!==o.mechanism||o.distribution.id!==i.distribution.id||o.consumer.id!==i.consumer.id)return;if("recorded"!==r.status)return;this.commit({...o,consumer:{...o.consumer,selection:{productId:r.productId,variantId:r.variantId}}});const e=this.displayAtom?.get();"draw"===e?.type&&this.displayAtom?.set({...e,selectionAvailability:void 0})}else{if("granted"!==o.phase||o.mechanism!==i.mechanism||ye(o)!==ye(i))return;if("recorded"===r.status)return;this.commit({...o,selection:r})}})}claim(){const e=this.stateAtom.get();if("routed"===e.journeyStage&&"granted"===e.sequence.phase&&"resolved"!==e.sequence.selection.status)throw m.validationError("Selection must be resolved before claiming the grant");const t=V(e);if(!t)throw m.validationError("No grant is available to claim");return this.checkoutStarted=!0,this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.emitEvent("sequence_change","success","user","Grant claimed",{distributionType:t.distributionType,distributionId:t.distributionId,sequenceId:t.sequenceId}),R().emit("journey:grant-claimed",{experienceId:this.experienceId,sequenceId:t.sequenceId,grant:t.token}),t}async confirmCheckout(e){await this.runCheckoutOperation(()=>this.submitCheckout(e))}async resumeCheckout(){await this.runCheckoutOperation(async()=>{const e=this.pendingCheckoutPaymentInput;if(!e)throw m.validationError("No paused checkout to resume");await this.submitCheckout(e)})}async runCheckoutOperation(e){try{await this.runSerializedOperation(e)}catch(t){if(await this.reconcileMissingCheckoutAdmission(t))return void(this.pendingCheckoutPaymentInput=void 0);throw t}}async reconcileMissingCheckoutAdmission(e){if(!g(e)||e.code!==y.ADMISSION_NOT_FOUND)return!1;const t=B(this.stateAtom.get());try{const e=await this.sdk.experiences.getMe();if(this.destroyed||B(this.stateAtom.get())!==t)return!1;const i=await this.resumeFromMe(e),s=this.stateAtom.get();return i&&B(s)===t&&"ended"===H(s)&&void 0!==$(s)}catch{return!1}}async submitCheckout(e){const t=this.stateAtom.get(),i=Q(t);if("granted"!==i?.phase||"internal"!==i.settlement.processor||"at_end"!==i.settlement.mode||"resolved"!==i.selection.status||this.checkoutStarted)throw m.validationError("No grant awaiting embedded checkout");const s=B(t),r=i.grant.token,n=ye(i);this.clearGrantExpiryTimer();try{let t;try{t=await this.sdk.experiences.confirmCheckout(r,i.grant.distributionId,e)}catch(o){throw g(o)&&o.code===y.ADMISSION_EXPIRED&&this.transitionToEnded({type:"expired"}),o}const a=this.stateAtom.get(),c=Q(a);if(B(a)!==s||"granted"!==c?.phase||ye(c)!==n)throw m.validationError("Grant changed before checkout could be confirmed");if("requires_action"===t.outcome)return this.pendingCheckoutPaymentInput=e,this.commit({...c,pendingAction:t.nextAction}),void this.emitEvent("sequence_change","info","user","Checkout awaiting verification",{processor:t.nextAction.processor});this.pendingCheckoutPaymentInput=void 0,this.transitionToEnded({checkout:t.checkout})}finally{const e=Q(this.stateAtom.get());"granted"!==e?.phase||e.pendingAction||this.rearmTimersIfNeeded()}}ackEvent(e){this.stateAtom.get().events.some(t=>t.id===e)&&this.patchJourneyBase({lastSeenEventId:e})}ackAllEvents(){const e=this.stateAtom.get();if(!e.events.length)return;const t=e.events[e.events.length-1];this.patchJourneyBase({lastSeenEventId:t.id})}async routeAndLoadDistribution(e,t){let i=t?.carryFrom??this.stateAtom.get();const s=this.store.session,r=s?.consumerId!==this.routingSession?.consumerId||s?.type!==this.routingSession?.type;r&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),r&&"routed"===i.journeyStage&&K(i)&&(this.clearRoutedRuntimeState(),C(this.experienceId,i.sequenceId),i=z({base:this.baseFromSnapshot(i),journeyStage:"ready"}),this.setSnapshot(i)),this.routingSession=s;const n=i,o=B(n);let a,c=!1;try{const i=await this.resolveRouting(e,{transient:t?.transient});if(!i||this.destroyed)return;if(a=this.stateAtom.get().revision,c=o===i.sequenceId,await this.enterExperienceIfReady(),this.destroyed)return;const s=await this.sdk.experiences.getCurrentDistributions(i.sequenceId,k({experienceId:this.experienceId,sequenceId:i.sequenceId}));if(this.destroyed)return;const r=c?L(n):void 0,d=this.buildDistributionSummary(s,i.sequenceId,{consumer:r,settlingDistributionId:c&&"routed"===n.journeyStage&&"settling"===n.sequence.phase?n.sequence.distribution.id:void 0}),u=this.buildWaitlistAttachmentFromContext(s,c?W(n):void 0);d&&j({experienceId:this.experienceId,sequenceId:i.sequenceId,distributionContext:d}),u&&x({waitlistId:u.waitlistId,experienceId:this.experienceId,sequenceId:i.sequenceId});const h=c?V(n):void 0,l=c?$e(n):void 0,p=c?Ke(n):void 0,m=c?this.deriveTerminalOutcomeForClosedDistribution(r,d,J(n)):void 0,g=this.withTerminalConsumer(r,m),y=!d&&c&&"routed"===n.journeyStage&&("granted"===n.sequence.phase||"ended"===n.sequence.phase)?n.sequence:this.projectSequenceState({distribution:d,consumer:g,grant:h,settlement:p,selection:c?Xe(n):void 0,checkout:l,outcome:m});c||(this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck());const f=this.stateAtom.get(),S=z({base:this.baseFromSnapshot(f,{lastSyncedAt:Date.now(),degraded:!1}),journeyStage:"routed",sequenceId:i.sequenceId,offers:i.offers,sequence:y,waitlist:u,sequenceRef:i.selectedSequence});this.setServerSnapshot(S,n),this.resumeDeferredGrantHydration(),this.consecutiveRefreshFailures=0,this.rearmTimersIfNeeded();const b=c?N(n):void 0;this.emitIfDistributionChanged(b,d),this.evaluateDistributionSelfCheck(d),t?.transient||await this.attemptAutomaticEntry()}catch(d){if("routed"===n.journeyStage&&c&&this.isQuietableDegradationError(d))throw this.restoreQuietSnapshot(n),this.markErrorQuietable(d);if("routing"===this.stateAtom.get().journeyStage)if(this.lastClearedSequenceId===o&&this.restoreClearedSequenceContext(n,o),void 0!==a&&this.stateAtom.get().revision===a)this.setSnapshot(n);else{const e=this.stateAtom.get();"routed"===n.journeyStage?this.setSnapshot(z({base:this.baseFromSnapshot(e),journeyStage:"routed",sequenceId:n.sequenceId,offers:n.offers,sequence:n.sequence,waitlist:n.waitlist,sequenceRef:n.sequenceRef})):this.setSnapshot(z({base:this.baseFromSnapshot(e),journeyStage:"ready"}))}throw d}}clearRefusedDistributionOnStanding(e){const t=this.refusedDistributionAtom.get();if(0===t.size||"routed"!==e.journeyStage)return;const i=N(e)?.id;if(void 0===i||!t.has(i))return;const s=e.sequence.phase;"participating"!==s&&"granted"!==s||this.refusedDistributionAtom.set(new Set([...t].filter(e=>e!==i)))}clearAutomaticAttemptIfSuperseded(e){const t=this.automaticAttemptTarget;if(!t||"routing"===e.journeyStage)return;"routed"===e.journeyStage&&e.sequenceId===t.sequenceId&&N(e)?.id===t.distributionId&&"enterable"===e.sequence.phase||this.clearAutomaticAttempt()}clearAutomaticAttempt(){this.automaticAttemptTarget=void 0,this.automaticAttemptAtom.set(void 0)}async attemptAutomaticEntry(){if(!this.automaticEntry||this.destroyed)return;const e=this.buildView(this.stateAtom.get());if("routed"!==e.journeyStage)return;const t=N(this.stateAtom.get())?.id;if(void 0!==t&&this.refusedDistributionAtom.get().has(t))return;const i=this.automaticAttemptAtom.get()?this.automaticAttemptTarget:void 0;if(i&&i.distributionId===t)return;const s=e.sequence;if("enterable"!==s.phase||!("entry"in s)||!s.entry.automatic)return;const r=this.sequenceDrivers[s.mechanism];if(!r?.enterWithinOperation)return;const n=e.sequenceId;this.automaticAttemptTarget={sequenceId:n,distributionId:t},this.automaticAttemptAtom.set("entering");try{await r.enterWithinOperation()}catch(o){if(this.destroyed)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage||e.sequenceId!==n||"enterable"!==e.sequence.phase)return void this.clearAutomaticAttempt();this.clearAutomaticAttempt(),void 0===t||g(o)&&ce.has(o.code)||this.refusedDistributionAtom.set(/* @__PURE__ */new Set([...this.refusedDistributionAtom.get(),t])),this.emitEvent("error","error","user","Automatic entry was refused",{action:"enter",...t?{distributionId:t}:{},...g(o)?{code:o.code}:{}})}}restoreClearedSequenceContext(e,t){const i=N(e);i&&j({experienceId:this.experienceId,sequenceId:t,distributionContext:i});const s=W(e);s&&x({waitlistId:s.waitlistId,experienceId:this.experienceId,sequenceId:t}),this.lastClearedSequenceToken&&T({experienceId:this.experienceId,sequenceId:t,token:this.lastClearedSequenceToken.token,expiresAt:this.lastClearedSequenceToken.expiresAt}),this.lastClearedSequenceId=null,this.lastClearedSequenceToken=void 0}restoreUndisplacedJourney(e,t){const i=this.stateAtom.get();return i.revision===t?(this.setSnapshot(e),!0):"routing"===i.journeyStage&&(this.setSnapshot(z({base:this.baseFromSnapshot(i),journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})),!0)}async resolveRouting(e,t){const i=this.stateAtom.get(),s=B(i);this.transitionToRouting();const r=this.stateAtom.get().revision;let n;try{n=await this.sdk.experiences.findSequence(this.experienceId,e)}catch(a){throw this.setSnapshot(i),"routed"===i.journeyStage&&this.armCapabilityRefreshTimer(),this.emitEvent("error","error","user","Failed to route experience"),a}if(this.destroyed)return null;if("gated"===n.outcome&&n.selected){const e=n.selected.gates;return"routed"!==i.journeyStage||K(i)?("routed"===i.journeyStage&&this.clearRoutedRuntimeState(),this.transitionToGated({gates:e,selected:{sequence:n.selected.sequence,status:"gated"}}),this.emitEvent("requirement","info","user","Gates required",{gates:e}),null):(this.restoreUndisplacedJourney(i,r)&&(this.rearmTimersIfNeeded(),"ended"!==H(this.stateAtom.get())&&(this.scheduleGatedCapabilityRefresh(),this.emitEvent("requirement","info","user","Gates required",{gates:e}))),null)}if("no_access"===n.outcome||!n.selected)throw this.setSnapshot(i),this.emitEvent("error","error","user","No access available for this experience"),new F("No access available for this experience");const o=n.selected.sequence.id;if(s&&s!==o&&(this.lastClearedSequenceId=s,this.lastClearedSequenceToken=E(this.experienceId,s),C(this.experienceId,s)),this.consecutiveGatedRestores=0,n.selected.capabilityGrant&&T({experienceId:this.experienceId,sequenceId:o,token:n.selected.capabilityGrant,expiresAt:n.selected.capabilityGrantExpiresAt}),s!==o){const e="routed"===i.journeyStage?i.sequenceRef?.priority:void 0,r=n.selected.sequence.priority,a=function(e,t){return void 0===e?"unknown":t>e?"upgrade":t<e?"downgrade":"lateral"}(e,r);this.emitEvent("reroute","downgrade"===a?"warning":"info",t?.transient?"system":"user","downgrade"===a?"Sequence downgraded":"Sequence changed",{previousSequenceId:s,sequenceId:o,...void 0===e?{}:{previousPriority:e},priority:r,direction:a})}return{sequenceId:o,selectedSequence:n.selected.sequence,offers:n.offers}}async enterExperienceIfReady(){const e=this.stateAtom.get();if(X(e).length>0)return;if(e.experience)return;const t=await this.sdk.experiences.enter(this.experienceId);this.destroyed||this.patchJourneyBase({experience:t})}createSequenceDrivers(){return{appointment:d({appointments:this.sdk.appointments,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>Q(this.stateAtom.get()),getDisplayState:()=>this.displayAtom?.get()??null,commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>this.ensureDisplayAtom(e),stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),auction:h({auctions:this.sdk.auctions,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>Q(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e)}),queue:I({queues:this.sdk.queues,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>Q(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),timed_release:M({timedReleases:this.sdk.timedReleases,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>Q(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)}),draw:b({draws:this.sdk.draws,runSerializedOperation:e=>this.runSerializedOperation(e),getCurrentSequence:()=>Q(this.stateAtom.get()),commit:(e,t)=>this.commit(e,t),applyMonitorUpdate:e=>this.applyMonitorUpdate(e),ensureDisplayAtom:e=>{this.ensureDisplayAtom(e)},stopRuntimeMonitoring:e=>this.stopRuntimeMonitoring(e),emitEvent:(e,t,i,s,r)=>this.emitEvent(e,t,i,s,r)})}}buildView(e,t){return _(e,{start:e=>this.start(e),reroute:e=>this.reroute(e),submitAccessCode:e=>this.submitAccessCode(e),retry:()=>this.retry(),joinWaitlist:()=>this.joinWaitlistAttachment(),exitWaitlist:()=>this.exitWaitlistAttachment(),claim:()=>this.claim(),select:e=>this.select(e),loadSelectionOptions:()=>this.loadSelectionOptions(),canConfirmCheckout:()=>!this.checkoutStarted,confirmCheckout:e=>this.confirmCheckout(e),resumeCheckout:()=>this.resumeCheckout()},this.displayAtom,this.sequenceDrivers,t)}projectSequenceState(e){if(!e.distribution)return{phase:"unavailable"};const t=this.sequenceDrivers[e.distribution.type];if(!t)throw new Error(`${e.distribution.type} sequence driver is required`);return t.project(e)}patchJourneyBase(e){if(this.destroyed)return;const t=this.stateAtom.get(),i=this.baseFromSnapshot(t,e);switch(t.journeyStage){case"ready":return void this.setSnapshot(z({base:i,journeyStage:"ready"}));case"routing":return void this.setSnapshot(z({base:i,journeyStage:"routing"}));case"gated":return void this.setSnapshot(z({base:i,journeyStage:"gated",gates:t.gates,selected:t.selected}));case"routed":return void this.setSnapshot(z({base:i,journeyStage:"routed",sequenceId:t.sequenceId,offers:t.offers,sequence:t.sequence,waitlist:t.waitlist,sequenceRef:t.sequenceRef}))}}transitionToRouting(){if(this.destroyed)return;const e=this.stateAtom.get();this.setSnapshot(z({base:this.baseFromSnapshot(e),journeyStage:"routing"}))}transitionToGated(e){if(this.destroyed)return;const t=this.stateAtom.get();this.setSnapshot(z({base:this.baseFromSnapshot(t),journeyStage:"gated",gates:e.gates,selected:e.selected}))}baseFromSnapshot(e,t={}){const i=Object.prototype.hasOwnProperty.call(t,"accessCode")?t.accessCode??void 0:e.accessCode,s=Object.prototype.hasOwnProperty.call(t,"degraded")?t.degraded||void 0:e.degraded,r=Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")?t.lastSyncedAt:e.lastSyncedAt;return{revision:e.revision,updatedAt:e.updatedAt,experienceId:e.experienceId,accessCode:i,experience:t.experience??e.experience,events:t.events??e.events,lastSeenEventId:t.lastSeenEventId??e.lastSeenEventId,degraded:s,lastSyncedAt:r}}commit(e,t={},i=!1){if(this.destroyed)throw m.notInitialized("Journey has been destroyed");const s=this.stateAtom.get();if("routed"!==s.journeyStage)throw m.validationError(re);if(!function(e,t){if("distribution"in e&&e.distribution.sequenceId&&e.distribution.sequenceId!==t)return!1;return!0}(e,s.sequenceId))throw m.validationError(se);const r=Object.prototype.hasOwnProperty.call(t,"waitlist")?t.waitlist??void 0:s.waitlist,n={};Object.prototype.hasOwnProperty.call(t,"lastSyncedAt")&&(n.lastSyncedAt=t.lastSyncedAt),Object.prototype.hasOwnProperty.call(t,"degraded")&&(n.degraded=t.degraded);const o=z({base:this.baseFromSnapshot(s,{...n,events:t.events??s.events}),journeyStage:"routed",sequenceId:s.sequenceId,offers:s.offers,sequence:e,waitlist:r,sequenceRef:s.sequenceRef});i?this.setServerSnapshot(o):this.setSnapshot(o)}markDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded||this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}clearDegraded(){if(this.destroyed)return;const e=this.stateAtom.get();e.degraded&&this.setSnapshot(this.rebuildSnapshot(e,{degraded:!1}))}restoreQuietSnapshot(e){this.setSnapshot(this.rebuildSnapshot(e,{degraded:!0}))}isQuietableDegradationError(e){return g(e)&&(e.retryable||ae.has(e.code))}markErrorQuietable(e){return e&&"object"==typeof e&&(e.quietable=!0),e}rebuildSnapshot(e,t={}){const i=this.baseFromSnapshot(e,t);switch(e.journeyStage){case"ready":return z({base:i,journeyStage:"ready"});case"routing":return z({base:i,journeyStage:"routing"});case"gated":return z({base:i,journeyStage:"gated",gates:e.gates,selected:e.selected});case"routed":return z({base:i,journeyStage:"routed",sequenceId:e.sequenceId,offers:e.offers,sequence:e.sequence,waitlist:e.waitlist,sequenceRef:e.sequenceRef})}}emitEvent(e,t,i,s,r){const n=this.stateAtom.get(),o={id:S(),ts:Date.now(),kind:e,severity:t,audience:i,message:s,detail:r},a=[...n.events,o],c=a.length>this.EVENT_BUFFER_LIMIT?a.slice(a.length-this.EVENT_BUFFER_LIMIT):a;this.patchJourneyBase({events:c})}emitIfDistributionChanged(e,t){e?.id===t?.id&&e?.lifecycle===t?.lifecycle||this.emitEvent("distribution_change","info","user","Distribution context changed",{previous:e,next:t})}buildDistributionSummary(e,t,i){if(e.active){const s=this.sequenceDrivers[e.active.type],r=!0===s?.settlementCanStartWhileActive&&i?.settlingDistributionId===e.active.id?"settling":"open";return{type:e.active.type,id:e.active.id,sequenceId:t,lifecycle:r,details:e.active.details,opensAt:e.active.details.openAt,closesAt:e.active.details.closeAt,updatedAt:Date.now()}}if(e.upcoming){const i=void 0===e.upcoming.details&&"queue"===e.upcoming.type?{id:e.upcoming.id,type:"queue",openAt:e.upcoming.startsAt}:e.upcoming.details;return{type:e.upcoming.type,id:e.upcoming.id,sequenceId:t,lifecycle:"scheduled",...i?{details:i,closesAt:i.closeAt}:{},startsAt:e.upcoming.startsAt,opensAt:e.upcoming.startsAt,updatedAt:Date.now()}}if(e.recentPast){const s=i?.settlingDistributionId===e.recentPast.id||ie(i?.consumer,e.recentPast.type,e.recentPast.id)?"settling":"closed";return{type:e.recentPast.type,id:e.recentPast.id,sequenceId:t,lifecycle:s,result:e.recentPast.result,closesAt:e.recentPast.endedAt,updatedAt:Date.now()}}}buildWaitlistAttachmentFromContext(e,t){const i=e.waitlist?.id;if(i)return t?.waitlistId===i?t:{waitlistId:i,status:"not_waitlisted"}}buildWaitlistAttachmentFromMe(e,t){const i=e.waitlists?.find(e=>e.experienceId===this.experienceId&&e.sequenceId===t);if(i)return{waitlistId:i.waitlistId,status:"waitlisted"}}normalizeOffers(e){return Array.isArray(e)?e.filter(Boolean):[]}restoreFromStore(){const e=this.store.activeJourneys;if(!e||!e[this.experienceId])return null;const t=Ve(e[this.experienceId]);return t?"routing"===t.journeyStage||"routed"===t.journeyStage&&K(t)?("routed"===t.journeyStage&&C(this.experienceId,t.sequenceId),this.restoredSnapshotNeedsPersist=!0,z({base:this.baseFromSnapshot(t),journeyStage:"ready"})):t:(this.store.removeJourney(this.experienceId),null)}persist(){this.store.setJourneyState(this.experienceId,this.stateAtom.get())}setSnapshot(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=pe(e);"auction"===s?.type&&"auction"!==this.displayAtom?.get().type&&(this.resetDisplayAtom("auction"),this.displayParticipationId=s.id),"routed"!==e.journeyStage&&"routing"!==e.journeyStage&&this.refusedDistributionAtom.set(/* @__PURE__ */new Set),this.clearRefusedDistributionOnStanding(e),this.clearAutomaticAttemptIfSuperseded(e),this.stateAtom.set(e),!1!==t.persist&&this.persist(),this.emitPhaseChanged(i,e)}setServerSnapshot(e,t=this.stateAtom.get(),i=!0){const s=function(e,t){const i=N(e),s=N(t),r=me(s);return"queue"!==i?.type||"queue"!==s?.type||i.id!==s.id||"scheduled"!==s.lifecycle||null===r||me(i)===r?null:s.id}(t,e);if(!s)return void this.setSnapshot(e,{persist:i});q(this.sdk.queues,s);const r=this.displayParticipationId===s&&"queue"===this.displayAtom?.get().type?this.displayAtom:null,{position:n,estimatedWaitTime:o,...a}=r?.get()??{type:"queue"},c="queue"===this.activeMonitor?.type&&this.activeMonitor.id===s;r?.set(a),this.setSnapshot(e,{persist:i}),r?.set({...a}),c&&(this.activeMonitor?.stop(),this.activeMonitor=null,this.startMonitorForParticipation({id:s,type:"queue"}))}emitPhaseChanged(e,t){const i=e.journeyStage,s="routed"===i?H(e):void 0,r=t.journeyStage,n="routed"===r?H(t):void 0;i===r&&s===n||R().emit("journey:phase-changed",{experienceId:this.experienceId,journeyStage:r,sequencePhase:n})}runSerializedOperation(e){const t=this.operationQueue.then(async()=>{if(this.destroyed)throw m.notInitialized("Journey has been destroyed");return e()});return this.operationQueue=t.then(()=>{},()=>{}),t}transitionToEnded(e,t={}){if(this.destroyed)return;const i=this.stateAtom.get(),s=B(i);if(!s)throw m.validationError(re);const r=N(i),n="checkout"in e?e.checkout:void 0,o="checkout"in e?{type:"completed"}:e,a=this.reinterpretLapsedGrantOutcome(i,o),c={...a,distributionType:a.distributionType??r?.type,distributionId:a.distributionId??r?.id,at:a.at??/* @__PURE__ */(new Date).toISOString()},d=this.withTerminalConsumer(L(i),c,{finalize:!0}),u=this.projectSequenceState({distribution:r,consumer:d,checkout:n,outcome:c});this.commit(u,t),this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.emitEvent("sequence_change","expired"===c.type?"warning":"info","user","Sequence ended",{outcome:c}),R().emit("journey:ended",{experienceId:this.experienceId,sequenceId:s,outcome:c})}reinterpretLapsedGrantOutcome(e,t){if("granted"!==H(e))return t;return"closed"===t.type&&("not_queued"===t.reason||"not_entered"===t.reason)?{type:"expired",at:t.at,distributionType:t.distributionType,distributionId:t.distributionId}:t}applyMonitorUpdate(e){if(!this.destroyed){if("settling"===e.type){const t=this.stateAtom.get();if(!Z(t)||"participating"!==t.sequence.phase)return;const i={...t.sequence.distribution,lifecycle:"settling",settlesAt:e.at},s=this.projectSequenceState({distribution:i,consumer:L(t)});if("settling"!==s.phase)return;return void this.commit(s,{lastSyncedAt:Date.now(),degraded:!1})}"granted"!==e.type?"resync"!==e.type?this.transitionToEnded(e.outcome,{lastSyncedAt:Date.now(),degraded:!1}):this.beginGrantHydration():this.beginGrantHydration(e.token,e.expiresAt)}}beginGrantHydration(e,t){const i=this.stateAtom.get();"routing"!==i.journeyStage?"routed"===i.journeyStage&&(ge(i,i.sequenceId)||this.pendingGrantHydration?.sequenceId!==i.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:i.sequenceId,token:e,expiresAt:t,attempt:0},this.hydrateGrantFromMe(e,t))):this.pendingGrantObservation={token:e,expiresAt:t}}resumeDeferredGrantHydration(){const e=this.pendingGrantObservation;e&&(this.pendingGrantObservation=void 0,this.beginGrantHydration(e.token,e.expiresAt))}async hydrateGrantFromMe(e,t){if(this.destroyed)return;const i=this.pendingGrantHydration,s=this.stateAtom.get();if(i)if("routing"!==s.journeyStage)if(ge(s,i.sequenceId))this.clearGrantHydration();else{if(void 0!==t&&Date.now()>=t)return this.clearAutomaticAttempt(),void this.transitionToEnded({type:"expired"},{lastSyncedAt:Date.now(),degraded:!1});try{const e=await this.sdk.experiences.getMe();if(this.destroyed)return;await this.resumeFromMe(e)}catch(r){this.emitEvent("error","warning","system","Could not resolve an observed grant from /me",{grant:e,error:r instanceof Error?r.message:String(r)})}this.scheduleGrantHydrationRetry()}else this.scheduleGrantHydrationRetry()}scheduleGrantHydrationRetry(){const e=this.pendingGrantHydration;if(!e||this.destroyed)return;if(ge(this.stateAtom.get(),e.sequenceId))return void this.clearGrantHydration();const t=he[e.attempt]??1e4;e.attempt+=1,e.timer=setTimeout(()=>{this.pendingGrantHydration===e&&(e.timer=void 0,this.hydrateGrantFromMe(e.token,e.expiresAt))},t)}clearGrantHydration(){const e=this.pendingGrantHydration;e?.timer&&clearTimeout(e.timer),this.pendingGrantHydration=void 0,this.pendingGrantObservation=void 0}applyMonitorUpdateFromCallback(e){try{this.applyMonitorUpdate(e)}catch(t){if(this.dropStaleCallbackCommit(t,"monitor"))return;throw t}}startMonitorForParticipation(e){const t=this.ensureDisplayAtom(e),i=e=>this.applyMonitorUpdateFromCallback(e),s=e=>{e?this.markDegraded():this.clearDegraded()},r=this.sequenceDrivers[e.type];if(r?.startMonitoring)return r.startMonitoring({participation:e,displayAtom:t,onUpdate:i,onDegradedChange:s}),void(this.activeMonitor={type:e.type,id:e.id,stop:()=>r.stopMonitoring?.()});throw new Error(`Unknown distribution type: ${e.type}`)}stopRuntimeMonitoring(e=!1){this.activeMonitor&&(this.activeMonitor.stop(),this.activeMonitor=null),e&&this.clearRuntimeDisplayState()}clearGrantExpiryTimer(){this.grantExpiryTimer&&(clearTimeout(this.grantExpiryTimer),this.grantExpiryTimer=void 0)}clearCapabilityRefreshTimer(){this.capabilityRefreshTimer&&(clearTimeout(this.capabilityRefreshTimer),this.capabilityRefreshTimer=void 0)}clearDistributionSelfCheck(){this.selfCheckTimer&&(clearTimeout(this.selfCheckTimer),this.selfCheckTimer=void 0),this.selfCheckKey=void 0,this.selfCheckStartedAtMs=0,this.selfCheckAttempts=0}resetDistributionSelfCheck(){this.clearDistributionSelfCheck(),this.selfCheckTotalAttempts=0}evaluateDistributionSelfCheck(e){if(this.destroyed)return;const t=Date.now();if(!le(e,t))return void this.clearDistributionSelfCheck();const i=`${e?.sequenceId??""}:${e?.id??""}:${e?.opensAt??""}`;if(this.selfCheckKey!==i&&(this.clearDistributionSelfCheck(),this.selfCheckKey=i,this.selfCheckStartedAtMs=t),this.selfCheckTimer)return;if(this.selfCheckAttempts>=de.length)return;if(this.selfCheckTotalAttempts>=ue)return;const s=this.selfCheckStartedAtMs+de[this.selfCheckAttempts];this.selfCheckAttempts+=1,this.selfCheckTotalAttempts+=1,this.selfCheckTimer=setTimeout(()=>{this.selfCheckFromTimer()},Math.max(0,s-t))}async selfCheckFromTimer(){if(this.selfCheckTimer=void 0,!this.destroyed)try{await this.refreshDistribution()}catch(e){if(this.destroyed||"routed"!==this.stateAtom.get().journeyStage)return;this.emitEvent("error","warning","system","Distribution self-check failed",{error:e instanceof Error?e.message:String(e)})}}armCapabilityRefreshTimer(){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=D(this.experienceId,e.sequenceId);if(void 0===t)return;const i=Math.max(t-Date.now()-6e4,5e3);this.scheduleCapabilityRefresh(i)}scheduleGatedCapabilityRefresh(){const e=this.stateAtom.get();if("routed"!==e.journeyStage)return;const t=Math.min(ne*2**this.consecutiveGatedRestores,oe);this.consecutiveGatedRestores+=1;const i=D(this.experienceId,e.sequenceId),s=void 0===i?t:Math.max(i-Date.now()-6e4,t);this.scheduleCapabilityRefresh(s)}scheduleCapabilityRefresh(e){if(this.clearCapabilityRefreshTimer(),this.destroyed||this.checkoutStarted)return;"routed"===this.stateAtom.get().journeyStage&&(this.capabilityRefreshTimer=setTimeout(()=>this.refreshCapabilityFromTimer(),e))}rearmTimersIfNeeded(){if(this.destroyed||this.checkoutStarted)return;const e=this.stateAtom.get();"routed"===e.journeyStage&&this.armCapabilityRefreshTimer(),this.pendingGrantHydration&&ge(e,this.pendingGrantHydration.sequenceId)&&this.scheduleGrantHydrationRetry(),"routed"===e.journeyStage&&"granted"===e.sequence.phase&&"awaiting_assignment"===e.sequence.selection.status&&this.pendingGrantHydration?.sequenceId!==e.sequenceId&&(this.clearGrantHydration(),this.pendingGrantHydration={sequenceId:e.sequenceId,token:e.sequence.grant.token,expiresAt:Y(e),attempt:0},this.scheduleGrantHydrationRetry());const t=Q(e);if("granted"===t?.phase&&(t.pendingAction||"internal"===t.settlement.processor&&"at_end"===t.settlement.mode&&"in_progress"===t.settlement.state))this.clearGrantExpiryTimer();else if("granted"===t?.phase&&!this.grantExpiryTimer){const t=Y(e);if(t){const e=t-Date.now();e<=0?this.transitionToEnded({type:"expired"}):this.grantExpiryTimer=setTimeout(()=>this.expireGrantFromTimer(),e)}}}async refreshCapabilityFromTimer(){this.capabilityRefreshTimer=void 0;try{if("routed"!==this.stateAtom.get().journeyStage)return void this.clearCapabilityRefreshTimer();if(this.checkoutStarted)return;const e=Date.now();if(void 0!==this.lastCapabilityRefreshAtMs&&e-this.lastCapabilityRefreshAtMs<ne)return void this.scheduleCapabilityRefresh(ne);this.lastCapabilityRefreshAtMs=e,await this.retry(),this.consecutiveRefreshFailures=0}catch(e){if(this.dropStaleCallbackCommit(e,"capability refresh timer"))return;const t=Math.min(ne*2**this.consecutiveRefreshFailures,oe);this.consecutiveRefreshFailures+=1,this.emitEvent("error","warning","system","Capability grant refresh failed",{error:e instanceof Error?e.message:String(e),consecutiveFailures:this.consecutiveRefreshFailures}),this.scheduleCapabilityRefresh(t)}}expireGrantFromTimer(){this.grantExpiryTimer=void 0;try{this.transitionToEnded({type:"expired"})}catch(e){if(this.dropStaleCallbackCommit(e,"grant-expiry timer"))return;throw e}}dropStaleCallbackCommit(e,t){return!(!g(e)||e.code!==y.VALIDATION_ERROR||e.message!==se&&e.message!==re)&&(this.logger.debug("Dropped stale journey callback",{experienceId:this.experienceId,source:t,message:e.message}),!0)}getRuntimeBinding(){const e=this.stateAtom.get(),t=pe(e),i=B(e);if(!t||!i)return null;const s="queue"===t.type?me(N(e)):null;return{experienceId:this.experienceId,sequenceId:i,participationId:t.id,participationType:t.type,...s?{runtimeContextToken:s}:{}}}startRuntimeMonitoring(){const e=this.getRuntimeBinding();if(!e)return;const t=this.activeMonitor;t&&t.id===e.participationId&&t.type===e.participationType&&this.displayAtom||(this.stopRuntimeMonitoring(!1),this.startMonitorForParticipation({id:e.participationId,type:e.participationType}))}getRuntimeDisplayAtom(){return this.displayAtom}applyRuntimeState(e){if(!e)return void this.clearRuntimeDisplayState();const t=this.getRuntimeBinding();if(!t)return;this.ensureDisplayAtom({id:t.participationId,type:t.participationType}).set(e)}reconcileSyncedSnapshot(e){if(this.destroyed)return;if(!e)return this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearGrantHydration(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),void this.setSnapshot(P(this.experienceId),{persist:!1});const t=Ve(e);if(!t)return;const i="routing"===t.journeyStage?z({base:this.baseFromSnapshot(t),journeyStage:"ready"}):t,s=this.stateAtom.get();if(s.revision===i.revision&&s.updatedAt===i.updatedAt)return;const r=pe(i);r||(this.displayAtom=null,this.displayParticipationId=void 0),this.setServerSnapshot(i,void 0,!1),r?this.ensureDisplayAtom(r):this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.rearmTimersIfNeeded()}ensureDisplayAtom(e){const t=this.displayAtom;if(t?.get().type===e.type&&this.displayParticipationId===e.id)return t;const i=this.resetDisplayAtom(e.type);return this.displayParticipationId=e.id,i}resetDisplayAtom(e){const t=this.displayAtom;if(!t||t.get().type!==e){const t=o({type:e});return this.displayAtom=t,t}return t.set({type:e}),t}clearRuntimeDisplayState(){const e=this.getRuntimeBinding();if(!e)return this.displayAtom=null,void(this.displayParticipationId=void 0);this.displayParticipationId=void 0,this.resetDisplayAtom(e.participationType)}clearRoutedRuntimeState(){this.stopRuntimeMonitoring(!0),this.clearGrantExpiryTimer(),this.clearCapabilityRefreshTimer(),this.resetDistributionSelfCheck(),this.checkoutStarted=!1}deriveTerminalOutcomeForClosedDistribution(e,t,i){if(i&&!l(i))return i;if(!t||"closed"!==t.lifecycle||!e)return i;const s=this.projectSequenceState({distribution:t,consumer:e});return"ended"===s.phase&&"lost"===s.outcome.type?s.outcome:i??{type:"closed",distributionType:t.type,distributionId:t.id}}withTerminalConsumer(e,t,i){switch(e?.mechanism){case"appointment":return u(e,t);case"auction":return p(e,t,i);case"draw":return A(e,t);case"queue":return v(e,t);case"timed_release":return G(e,t);default:return e}}stopPolling(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=void 0)}}const Se=c.picklist(["queue","draw","auction","timed_release","appointment"]),be=Se,Ae=c.picklist(["queue","draw","auction","timed_release"]),Ie=c.picklist(["draw","auction"]),ve=c.strictObject({id:c.string(),ts:c.number(),kind:c.picklist(["reroute","sequence_change","distribution_change","requirement","error","info"]),severity:c.picklist(["info","success","warning","error"]),audience:c.picklist(["user","system","analytics"]),message:c.string(),detail:c.optional(c.record(c.string(),c.unknown()))}),qe=c.object({id:c.string(),name:c.string(),experienceId:c.string(),priority:c.number(),color:c.nullable(c.string()),availability:c.optional(s)}),we=c.strictObject({type:Se,id:c.string(),sequenceId:c.optional(c.string()),lifecycle:c.string(),startsAt:c.optional(c.string()),opensAt:c.optional(c.string()),closesAt:c.optional(c.string()),settlesAt:c.optional(c.string()),result:c.optional(c.string()),details:c.optional(c.unknown()),updatedAt:c.optional(c.number())}),Ce=c.strictObject({status:c.string(),selection:c.optional(n),id:c.optional(c.string()),distributionId:c.optional(c.string()),waitlistId:c.optional(c.string()),enteredAt:c.optional(c.string()),admissionGrant:c.optional(c.string()),grantedAt:c.optional(c.string()),deniedReason:c.optional(c.string()),position:c.optional(c.number()),highestBid:c.optional(c.string()),currentBid:c.optional(c.string()),slotId:c.optional(c.string()),locationId:c.optional(c.string()),bookedAt:c.optional(c.string()),checkedInAt:c.optional(c.string()),completedAt:c.optional(c.string()),cancelledAt:c.optional(c.string()),noShowAt:c.optional(c.string())}),ke=c.strictObject({token:c.string(),distributionType:Ae,distributionId:c.string(),sequenceId:c.string(),experienceId:c.string(),issuedAt:c.optional(c.string()),expiresAt:c.optional(c.string())}),je=c.strictObject({orderId:c.string(),amount:c.string(),currencyCode:c.string(),paymentMethodLast4:c.optional(c.string()),paymentMethodBrand:c.optional(c.string())}),xe=c.strictObject({publishableKey:c.string(),stripeAccount:c.optional(c.string())}),Te=c.strictObject({type:c.string(),reason:c.optional(c.string()),at:c.optional(c.string()),distributionType:c.optional(Se),distributionId:c.optional(c.string())}),Ee=c.strictObject({phase:c.literal("unavailable"),reason:c.optional(c.string())}),De=c.strictObject({waitlistId:c.string(),status:c.picklist(["not_waitlisted","waitlisted"]),entryId:c.optional(c.string())}),Re=c.strictObject({phase:c.literal("scheduled"),mechanism:be,distribution:we}),Oe=c.strictObject({phase:c.literal("enterable"),mechanism:be,distribution:we,consumer:Ce,paymentProvider:c.optional(xe)}),Me=c.strictObject({phase:c.literal("participating"),mechanism:be,distribution:we,consumer:Ce}),Ge=c.strictObject({phase:c.literal("settling"),mechanism:Ie,distribution:we,consumer:Ce}),Fe=c.variant("processor",[c.strictObject({processor:c.literal("external")}),c.strictObject({processor:c.literal("internal"),mode:c.literal("at_end"),state:c.picklist(["awaiting","in_progress"]),paymentProvider:c.optional(xe)}),c.strictObject({processor:c.literal("internal"),mode:c.literal("pre_auth")})]),_e=c.strictObject({phase:c.literal("granted"),mechanism:Ae,distribution:we,consumer:Ce,grant:ke,settlement:Fe,selection:r}),Pe=c.strictObject({phase:c.literal("ended"),mechanism:be,distribution:we,consumer:Ce,outcome:Te,checkout:c.optional(je)}),He=c.union([Ee,Re,Oe,Me,Ge,_e,Pe]),Ne={schemaVersion:c.literal(6),revision:c.number(),updatedAt:c.number(),experienceId:c.string(),accessCode:c.optional(c.string()),experience:c.optional(c.unknown()),events:c.array(ve),lastSeenEventId:c.optional(c.string()),lastSyncedAt:c.optional(c.number())},Ue=c.strictObject({...Ne,journeyStage:c.literal("ready")}),ze=c.strictObject({...Ne,journeyStage:c.literal("routing")}),Be=c.variant("type",[c.object({...c.omit(e,["reason"]).entries,requirement:c.optional(c.picklist(["session","authenticated"]),"authenticated")}),c.omit(t,["reason"]),c.omit(i,["reason"])]),Le=c.strictObject({...Ne,journeyStage:c.literal("gated"),gates:c.array(Be),selected:c.optional(c.strictObject({sequence:c.unknown(),status:c.literal("gated")}))}),We=c.strictObject({...Ne,journeyStage:c.literal("routed"),sequenceId:c.string(),offers:c.array(c.object({sequence:c.unknown(),status:c.literal("gated"),gates:c.array(Be)})),sequence:He,waitlist:c.optional(De),sequenceRef:c.optional(qe)}),Je=c.union([Ue,ze,Le,We]);function Ve(e){const t=c.safeParse(Je,e);if(!t.success)return null;const{schemaVersion:i,...s}=t.output;if("routed"===s.journeyStage)try{te(s.sequence)}catch{return null}return s}function Qe(e){if("participating"!==e.phase&&"settling"!==e.phase)return e;if(!("selection"in e))return e;const{selection:t,...i}=e;return void 0===t?i:{...i,consumer:{...i.consumer,selection:t}}}function $e(e){return"routed"!==e.journeyStage||"granted"!==e.sequence.phase&&"ended"!==e.sequence.phase||!("checkout"in e.sequence)?$(e):e.sequence.checkout}function Ke(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.settlement}function Xe(e){if("routed"===e.journeyStage&&"granted"===e.sequence.phase)return e.sequence.selection}export{fe as ExperienceJourney,le as isSelfContradictingDistribution};
@@ -23,4 +23,4 @@ export type { DomBridge, DomBridgeOptions } from './dom-bridge';
23
23
  export { describeJourneyState, selectJourneyDisplay } from './journey-display';
24
24
  export type { JourneyDisplaySelection, JourneyStateDescription } from './journey-display';
25
25
  export type { AccessCodeSubmitResult, AdmissionGrant, AppointmentConsumerStatus, AppointmentEnded, AppointmentEndedView, AppointmentEnterable, AppointmentEnterableView, AppointmentParticipating, AppointmentParticipatingView, AppointmentSequence, AppointmentSequenceView, AuctionConsumerStatus, AuctionEnded, AuctionEndedView, AuctionEnterable, AuctionEnterableView, AuctionGranted, AuctionGrantedAwaitingAssignmentView, AuctionGrantedSelectionRequiredView, AuctionGrantedView, AuctionParticipating, AuctionParticipatingView, AuctionScheduled, AuctionScheduledView, AuctionSequence, AuctionSequenceView, AuctionSettling, AuctionSettlingView, ConsumerJourneyState, ConsumerSequenceState, DistributionLifecycle, DistributionSummary, DistributionType, DrawConsumerStatus, DrawEnded, DrawEndedView, DrawEnterable, DrawEnterableView, DrawGranted, DrawGrantedAwaitingAssignmentView, DrawGrantedSelectionRequiredView, DrawGrantedView, DrawParticipating, DrawParticipatingView, DrawScheduled, DrawScheduledView, DrawSequence, DrawSequenceView, DrawSettling, DrawSettlingView, EnterableSequenceView, GatedSnapshot, GatedView, JourneyEvent, JourneyEventAudience, JourneyEventKind, JourneyEventSeverity, JourneyHandle, JourneySnapshot, JourneyStage, JourneyView, ParticipatingSequenceView, Participation, PrimarySequenceMechanism, QueueConsumerStatus, QueueEnded, QueueEndedView, QueueEnterable, QueueEnterableView, QueueGranted, QueueGrantedAwaitingAssignmentView, QueueGrantedSelectionRequiredView, QueueGrantedView, QueueParticipating, QueueParticipatingView, QueueScheduled, QueueScheduledView, QueueSequence, QueueSequenceView, ReadySnapshot, ReadyView, RoutedSnapshot, RoutedView, RoutingGate, RoutingOffer, RoutingSnapshot, RoutingView, SequenceMechanism, SequenceOutcome, SequenceOutcomeType, SequencePhase, SequenceState, SequenceView, ServiceJourneyStage, ServiceJourneyState, TimedReleaseConsumerStatus, TimedReleaseEnded, TimedReleaseEndedView, TimedReleaseEnterable, TimedReleaseEnterableView, TimedReleaseGranted, TimedReleaseGrantedAwaitingAssignmentView, TimedReleaseGrantedView, TimedReleaseParticipating, TimedReleaseParticipatingView, TimedReleaseScheduled, TimedReleaseScheduledView, TimedReleaseSequence, TimedReleaseSequenceView, WaitlistAttachment, WaitlistAttachmentView, WaitlistConsumerStatus, } from './journey.types';
26
- export type { ConsumerMe, Distribution, DistributionContext, Experience, ExperienceDetails, ExperienceSession, JourneyDistributionType, JourneyParticipationType, Sequence, } from './types';
26
+ export type { ConsumerMe, Distribution, DistributionContext, Experience, ExperienceDetails, ExperienceSession, ExperienceTiming, JourneyDistributionType, JourneyParticipationType, Sequence, } from './types';
@@ -24,6 +24,8 @@ export interface Experience {
24
24
  description?: string;
25
25
  /** Optional image URL suitable for experience display. */
26
26
  imageUrl?: string;
27
+ /** Merchant identity shown in the optional marketing SMS offer. */
28
+ marketingSmsMerchantName?: string;
27
29
  /** Organization-level currency code (ISO 4217), when available */
28
30
  currencyCode?: string;
29
31
  /** ISO 8601 timestamp when the experience was created. */
@@ -31,6 +33,17 @@ export interface Experience {
31
33
  /** ISO 8601 timestamp when the experience was last updated. */
32
34
  updatedAt: string;
33
35
  }
36
+ /**
37
+ * Public timing of an experience: whether it is upcoming, open or closed, and
38
+ * the boundaries around that status.
39
+ */
40
+ export interface ExperienceTiming {
41
+ status?: "upcoming" | "open" | "closed" | null;
42
+ nextOpensAt?: string | null;
43
+ lastClosedAt?: string | null;
44
+ /** Set only while open: the instant the drop stops being open; null when the open run has no end. */
45
+ closesAt?: string | null;
46
+ }
34
47
  /**
35
48
  * Experience metadata with embeddable configuration and sequence context.
36
49
  *
@@ -46,6 +59,8 @@ export interface ExperienceDetails extends Experience {
46
59
  i18n?: Record<string, Record<string, string>>;
47
60
  /** Brand theme applied by SDK UI adapters. */
48
61
  theme?: BrandTheme;
62
+ /** Public timing; absent or null when the experience publishes none. */
63
+ timing?: ExperienceTiming | null;
49
64
  }
50
65
  /**
51
66
  * Consumer session for an entered experience.
@@ -182,6 +197,8 @@ export interface BaseDistributionDetails {
182
197
  export interface QueueDetails extends BaseDistributionDetails {
183
198
  /** Mechanism discriminator for queue details. */
184
199
  type: "queue";
200
+ /** Whether loyalty points weight this distribution's randomized entry order. */
201
+ loyaltyBiasEnabled?: boolean;
185
202
  /** Estimated wait time in seconds, when available. */
186
203
  estimatedWaitTime?: number;
187
204
  /** Current queue length, when available. */
@@ -196,6 +213,8 @@ export interface QueueDetails extends BaseDistributionDetails {
196
213
  export interface DrawDetails extends BaseDistributionDetails {
197
214
  /** Mechanism discriminator for draw details. */
198
215
  type: "draw";
216
+ /** Whether loyalty points weight this distribution's randomized entry order. */
217
+ loyaltyBiasEnabled?: boolean;
199
218
  /** ISO 8601 timestamp when the draw is expected to run. */
200
219
  drawTime?: string;
201
220
  /** ISO 8601 timestamp when entry closes. */
@@ -5,6 +5,7 @@
5
5
  * release. Customer-facing API lives on the domain subpaths (see
6
6
  * docs/PUBLIC_SURFACE.md).
7
7
  */
8
+ export { readExperienceDetails } from './core/internal-sdk-registry';
8
9
  export type { DistributionMonitor, MonitorUpdate } from './experiences/distribution-monitor.types';
9
10
  export { createDomBridge } from './experiences/dom-bridge';
10
11
  export { describeJourneyState, selectJourneyDisplay } from './experiences/journey-display';
@@ -14,3 +15,6 @@ export type { AppointmentDisplayState, AuctionDisplayState, DistributionDisplayS
14
15
  export { getEventBus } from './state/events';
15
16
  export { generateFingerprint, generateFingerprintSync } from './utils/fingerprint.module';
16
17
  export type { BrowserFingerprint } from './utils/fingerprint.module';
18
+ export { restoreFocus } from './utils/focus-recovery';
19
+ export { MARKETING_SMS_PRIVACY_URL, MARKETING_SMS_TERMS_URL, formatMarketingSmsConsentText, } from './utils/marketing-sms-consent';
20
+ export type { MarketingSmsCopy } from './utils/marketing-sms-consent';
package/dist/internals.js CHANGED
@@ -1 +1 @@
1
- import{createDomBridge as e}from"./experiences/dom-bridge.js";import{describeJourneyState as r,selectJourneyDisplay as o}from"./experiences/journey-display.js";import{SnapshotInvariantError as i,getAdmissionGrant as m,getAdmissionGrantExpiresAt as s,getDistribution as p,getGates as t,getOffers as n,getParticipation as j,getSelected as f,getSequenceId as c,getSequencePhase as d,getSequenceState as u,isGateState as x,isPreEntryJourney as a,isRoutedState as l}from"./experiences/journey.machine.js";import{getEventBus as y}from"./state/events.js";import{generateFingerprint as g,generateFingerprintSync as b}from"./utils/fingerprint.module.js";export{i as SnapshotInvariantError,e as createDomBridge,r as describeJourneyState,g as generateFingerprint,b as generateFingerprintSync,m as getAdmissionGrant,s as getAdmissionGrantExpiresAt,p as getDistribution,y as getEventBus,t as getGates,n as getOffers,j as getParticipation,f as getSelected,c as getSequenceId,d as getSequencePhase,u as getSequenceState,x as isGateState,a as isPreEntryJourney,l as isRoutedState,o as selectJourneyDisplay};
1
+ import{readExperienceDetails as r}from"./core/internal-sdk-registry.js";import{createDomBridge as e}from"./experiences/dom-bridge.js";import{describeJourneyState as o,selectJourneyDisplay as s}from"./experiences/journey-display.js";import{SnapshotInvariantError as i,getAdmissionGrant as m,getAdmissionGrantExpiresAt as t,getDistribution as n,getGates as p,getOffers as f,getParticipation as j,getSelected as c,getSequenceId as u,getSequencePhase as l,getSequenceState as a,isGateState as d,isPreEntryJourney as y,isRoutedState as g}from"./experiences/journey.machine.js";import{getEventBus as x}from"./state/events.js";import{generateFingerprint as k,generateFingerprintSync as v}from"./utils/fingerprint.module.js";import{restoreFocus as b}from"./utils/focus-recovery.js";import{MARKETING_SMS_PRIVACY_URL as h,MARKETING_SMS_TERMS_URL as q,formatMarketingSmsConsentText as w}from"./utils/marketing-sms-consent.js";export{h as MARKETING_SMS_PRIVACY_URL,q as MARKETING_SMS_TERMS_URL,i as SnapshotInvariantError,e as createDomBridge,o as describeJourneyState,w as formatMarketingSmsConsentText,k as generateFingerprint,v as generateFingerprintSync,m as getAdmissionGrant,t as getAdmissionGrantExpiresAt,n as getDistribution,x as getEventBus,p as getGates,f as getOffers,j as getParticipation,c as getSelected,u as getSequenceId,l as getSequencePhase,a as getSequenceState,d as isGateState,y as isPreEntryJourney,g as isRoutedState,r as readExperienceDetails,b as restoreFocus,s as selectJourneyDisplay};
@@ -1 +1 @@
1
- import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{createPollScheduler as s}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as o}from"../payment/checkout-contract.js";import{getEventBus as n}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class a{constructor(t){this.logger=e(),this.store=r(),this.events=n(),this.trackedParticipations=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.monitorRuntime=new i,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastQueueStatus=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}async get(t){try{return await this.http.get(`/queues/${t}`)}catch(e){throw this.logger.error("Failed to get queue",{queueId:t,error:e}),e}}async enter(e,s,i,n,r){const a=this.lifecycleGeneration;let u;try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");u=this.bumpOperationGeneration(e),this.logger.info("Entering queue",{queueId:e});const p={metadata:s};n&&(p.paymentInput=n);const l=await this.http.post(`/queues/${e}/enter`,p,i);if(o(l))return l;const c=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:s});return"QUEUED"!==l.status&&"GRANTED"!==l.status&&"COMPLETED"!==l.status||c.events.push({type:"experience:distribution-entered",payload:{distributionType:"queue",distributionId:e,...r}}),this.canApplyAsyncResult(e,a,void 0,u)?(this.applyTransition(e,c),l):l}catch(p){if(!this.canApplyAsyncResult(e,a,void 0,u))throw p;throw this.logger.error("Failed to enter queue",{queueId:e,error:p}),this.events.emit("queue:error",{queueId:e,error:p}),p}}async leave(t,e){const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t);try{if(this.logger.info("Leaving queue",{queueId:t}),await this.http.post(`/queues/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,s,void 0,i))return;this.trackedParticipations.delete(t),this.stopStatusPolling(t),this.events.emit("queue:left",{queueId:t,reason:"user"})}catch(o){if(!this.canApplyAsyncResult(t,s,void 0,i))throw o;throw this.logger.error("Failed to leave queue",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{queueId:t}),e;const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t),o=this._doStatus(t,s,void 0,i).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,o),o}async _doStatus(t,e,s,i){try{const o=await this.http.get(`/queues/${t}/status`);if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}catch(o){if(o&&"object"==typeof o&&"status"in o&&404===o.status){const o={status:"NOT_QUEUED"};if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}if(!this.canApplyAsyncResult(t,e,s,i))return{status:"NOT_QUEUED"};throw this.logger.error("Failed to get queue status",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}startMonitoring(t,e,s,i){this.monitorRuntime.start(t,e,s),this.startStatusPolling(t,1e4,i)}stopMonitoring(t){this.stopStatusPolling(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.isStatusPolling(t)||this.monitorRuntime.has(t)}startStatusPolling(t,e=1e4,i){this.stopStatusPolling(t);const o=this.lifecycleGeneration,n=this.bumpMonitorGeneration(t);this.hasSuccessfulReadThisSession.set(t,!1),this.lastQueueStatus.delete(t),this.logger.debug("Starting queue polling",{queueId:t,intervalMs:e});const r=s({baseMs:e,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:i,shouldContinue:()=>this.pollSchedulers.has(t)&&this.canApplyAsyncResult(t,o,n),run:async()=>{const e=this.getOperationGeneration(t);try{const s=await this._doStatus(t,o,n,e);this.canApplyAsyncResult(t,o,n,e)&&(this.hasSuccessfulReadThisSession.set(t,!0),this.lastQueueStatus.set(t,s))}catch(s){if(!this.canApplyAsyncResult(t,o,n,e))return;throw this.logger.error("Polling error",{queueId:t,error:s}),s}}});this.pollSchedulers.set(t,r),r.start()}stopStatusPolling(t){this.bumpMonitorGeneration(t);const e=this.pollSchedulers.get(t);e&&(e.stop(),this.pollSchedulers.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastQueueStatus.delete(t),this.logger.debug("Stopped queue polling",{queueId:t}))}isStatusPolling(t){return this.pollSchedulers.has(t)}shouldSuppressBackoff(t){if(!this.hasSuccessfulReadThisSession.get(t))return!0;const e=this.lastQueueStatus.get(t);return"QUEUED"===e?.status&&e.position<=5||!("GRANTED"!==e?.status||!("expiresAt"in e)||!e.expiresAt)&&Date.now()<new Date(e.expiresAt).getTime()}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,s,i){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===s||this.monitorGenerations.get(t)===s)&&(void 0===i||this.getOperationGeneration(t)===i))}buildTransition(t,e,{source:s,previousParticipation:i,enteredAt:o,metadata:n}){switch(e.status){case"QUEUED":return{trackedParticipation:{queueId:t,enteredAt:i?.enteredAt??o??/* @__PURE__ */(new Date).toISOString(),position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,metadata:i?.metadata??n},displayState:{position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds,positionDisplayMode:e.positionDisplayMode,admissionsPaused:e.admissionsPaused},events:"enter"===s?[{type:"queue:entered",payload:{queueId:t,position:e.position,estimatedWaitTime:e.estimatedWaitTimeInSeconds}}]:void 0!==i?.position&&i.position!==e.position?[{type:"queue:position-changed",payload:{queueId:t,position:e.position,previousPosition:i.position}}]:[],monitorUpdate:null,stopMonitoring:!1};case"GRANTED":return{trackedParticipation:null,events:[{type:"queue:granted",payload:{queueId:t,grant:e.admissionGrant}}],monitorUpdate:"status"===s?{type:"granted",token:e.admissionGrant,expiresAt:"expiresAt"in e&&e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===s};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===s};case"EXPIRED":return{trackedParticipation:null,events:[{type:"queue:expired",payload:{queueId:t}}],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"expired"}}:null,stopMonitoring:"status"===s};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===s};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===s};case"NOT_QUEUED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"closed",reason:"not_queued"}}:null,stopMonitoring:"status"===s}}}applyTransition(t,e){if(e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t),e.displayState){const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"queue",...e.displayState})}for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopStatusPolling(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1;for(const t of this.pollSchedulers.values())t.stop();this.pollSchedulers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastQueueStatus.clear(),this.monitorRuntime.clear()}}export{a as QueueManagementModule};
1
+ import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{createPollScheduler as s}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as o}from"../payment/checkout-contract.js";import{getEventBus as n}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class a{constructor(t){this.logger=e(),this.store=r(),this.events=n(),this.trackedParticipations=/* @__PURE__ */new Map,this.pollSchedulers=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.monitorRuntime=new i,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.queuedDisplayPrivacyEpochs=/* @__PURE__ */new Map,this.hasSuccessfulReadThisSession=/* @__PURE__ */new Map,this.lastQueueStatus=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}async get(t){try{return await this.http.get(`/queues/${t}`)}catch(e){throw this.logger.error("Failed to get queue",{queueId:t,error:e}),e}}async enter(e,s,i,n,r){const a=this.lifecycleGeneration;let u,p=0;try{if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");u=this.bumpOperationGeneration(e),p=this.queuedDisplayPrivacyEpochs.get(e)??0,this.logger.info("Entering queue",{queueId:e});const c={metadata:s};n&&(c.paymentInput=n);const l=await this.http.post(`/queues/${e}/enter`,c,i);if(o(l))return l;const h=this.canApplyAsyncResult(e,a,void 0,u),d=this.projectResponse(e,l,p,h);if(!h)return d;const y=this.buildTransition(e,d,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:s});return"QUEUED"!==l.status&&"GRANTED"!==l.status&&"COMPLETED"!==l.status||y.events.push({type:"experience:distribution-entered",payload:{distributionType:"queue",distributionId:e,...r}}),this.applyTransition(e,y),d}catch(c){if(!this.canApplyAsyncResult(e,a,void 0,u))throw c;throw this.logger.error("Failed to enter queue",{queueId:e,error:c}),this.events.emit("queue:error",{queueId:e,error:c}),c}}async leave(t,e){const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t);try{if(this.logger.info("Leaving queue",{queueId:t}),await this.http.post(`/queues/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,s,void 0,i))return;this.trackedParticipations.delete(t),this.stopStatusPolling(t),this.events.emit("queue:left",{queueId:t,reason:"user"})}catch(o){if(!this.canApplyAsyncResult(t,s,void 0,i))throw o;throw this.logger.error("Failed to leave queue",{queueId:t,error:o}),this.events.emit("queue:error",{queueId:t,error:o}),o}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{queueId:t}),e;const s=this.lifecycleGeneration,i=this.bumpOperationGeneration(t),o=this._doStatus(t,s,void 0,i).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,o),o}async _doStatus(t,e,s,i){const o=this.queuedDisplayPrivacyEpochs.get(t)??0;try{const n=await this.http.get(`/queues/${t}/status`),r=this.canApplyAsyncResult(t,e,s,i),a=this.projectResponse(t,n,o,r);if(!r)return a;const u=this.buildTransition(t,a,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,u),this.events.emit("queue:status-updated",{queueId:t,status:a}),a}catch(n){if(n&&"object"==typeof n&&"status"in n&&404===n.status){const o={status:"NOT_QUEUED"};if(!this.canApplyAsyncResult(t,e,s,i))return o;const n=this.buildTransition(t,o,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("queue:status-updated",{queueId:t,status:o}),o}if(!this.canApplyAsyncResult(t,e,s,i)){return{status:"NOT_QUEUED"}}throw this.logger.error("Failed to get queue status",{queueId:t,error:n}),this.events.emit("queue:error",{queueId:t,error:n}),n}}startMonitoring(t,e,s,i){this.monitorRuntime.start(t,e,s),this.startStatusPolling(t,1e4,i)}stopMonitoring(t){this.stopStatusPolling(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.isStatusPolling(t)||this.monitorRuntime.has(t)}startStatusPolling(t,e=1e4,i){this.stopStatusPolling(t);const o=this.lifecycleGeneration,n=this.bumpMonitorGeneration(t);this.hasSuccessfulReadThisSession.set(t,!1),this.lastQueueStatus.delete(t),this.logger.debug("Starting queue polling",{queueId:t,intervalMs:e});const r=s({baseMs:e,suppressBackoff:()=>this.shouldSuppressBackoff(t),onDegradedChange:i,shouldContinue:()=>this.pollSchedulers.has(t)&&this.canApplyAsyncResult(t,o,n),run:async()=>{const e=this.getOperationGeneration(t);try{const s=await this._doStatus(t,o,n,e);this.canApplyAsyncResult(t,o,n,e)&&(this.hasSuccessfulReadThisSession.set(t,!0),this.lastQueueStatus.set(t,s))}catch(s){if(!this.canApplyAsyncResult(t,o,n,e))return;throw this.logger.error("Polling error",{queueId:t,error:s}),s}}});this.pollSchedulers.set(t,r),r.start()}stopStatusPolling(t){this.bumpMonitorGeneration(t);const e=this.pollSchedulers.get(t);e&&(e.stop(),this.pollSchedulers.delete(t),this.hasSuccessfulReadThisSession.delete(t),this.lastQueueStatus.delete(t),this.logger.debug("Stopped queue polling",{queueId:t}))}isStatusPolling(t){return this.pollSchedulers.has(t)}shouldSuppressBackoff(t){if(!this.hasSuccessfulReadThisSession.get(t))return!0;const e=this.lastQueueStatus.get(t);return"QUEUED"===e?.status&&void 0!==e.position&&e.position<=5||!("GRANTED"!==e?.status||!("expiresAt"in e)||!e.expiresAt)&&Date.now()<new Date(e.expiresAt).getTime()}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}advancePrivacyEpoch(t){this.queuedDisplayPrivacyEpochs.set(t,(this.queuedDisplayPrivacyEpochs.get(t)??0)+1)}projectResponse(t,e,s,i){if("QUEUED"!==e.status)return e;const o=this.queuedDisplayPrivacyEpochs.get(t)??0;if(void 0===e.position&&void 0===e.estimatedWaitTimeInSeconds&&i)return this.queuedDisplayPrivacyEpochs.set(t,Math.max(o,s+1)),e;if(s>=o)return e;const{position:n,estimatedWaitTimeInSeconds:r,...a}=e;return a}canApplyAsyncResult(t,e,s,i){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===s||this.monitorGenerations.get(t)===s)&&(void 0===i||this.getOperationGeneration(t)===i))}buildTransition(t,e,{source:s,previousParticipation:i,enteredAt:o,metadata:n}){switch(e.status){case"QUEUED":{const r={...void 0===e.position?{}:{position:e.position},...void 0===e.estimatedWaitTimeInSeconds?{}:{estimatedWaitTime:e.estimatedWaitTimeInSeconds}};return{trackedParticipation:{queueId:t,enteredAt:i?.enteredAt??o??/* @__PURE__ */(new Date).toISOString(),...r,positionDisplayMode:e.positionDisplayMode,metadata:i?.metadata??n},displayState:{...r,positionDisplayMode:e.positionDisplayMode,admissionsPaused:e.admissionsPaused},events:"enter"===s?[{type:"queue:entered",payload:{queueId:t,...r}}]:void 0!==e.position&&void 0!==i?.position&&i.position!==e.position?[{type:"queue:position-changed",payload:{queueId:t,position:e.position,previousPosition:i.position}}]:[],monitorUpdate:null,stopMonitoring:!1}}case"GRANTED":return{trackedParticipation:null,events:[{type:"queue:granted",payload:{queueId:t,grant:e.admissionGrant}}],monitorUpdate:"status"===s?{type:"granted",token:e.admissionGrant,expiresAt:"expiresAt"in e&&e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===s};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===s};case"EXPIRED":return{trackedParticipation:null,events:[{type:"queue:expired",payload:{queueId:t}}],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"expired"}}:null,stopMonitoring:"status"===s};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===s};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===s};case"NOT_QUEUED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===s?{type:"ended",outcome:{type:"closed",reason:"not_queued"}}:null,stopMonitoring:"status"===s}}}applyTransition(t,e){if(e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t),e.displayState){const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"queue",...e.displayState})}for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopStatusPolling(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1;for(const t of this.pollSchedulers.values())t.stop();this.pollSchedulers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.queuedDisplayPrivacyEpochs.clear(),this.hasSuccessfulReadThisSession.clear(),this.lastQueueStatus.clear(),this.monitorRuntime.clear()}}function u(t,e){t instanceof a&&t.advancePrivacyEpoch(e)}export{a as QueueManagementModule,u as advanceQueuePrivacyEpoch};
@@ -83,8 +83,9 @@ export type QueueConsumerState = QueuedConsumerState | GrantedConsumerState | Co
83
83
  export interface QueuedConsumerState {
84
84
  status: "QUEUED";
85
85
  admissionsPaused?: boolean;
86
- position: number;
87
- /** Absent when the queue reports no wait estimate (e.g. a paused queue). */
86
+ /** Absent until the queue opens. */
87
+ position?: number;
88
+ /** Absent before opening or when the queue reports no finite wait estimate. */
88
89
  estimatedWaitTimeInSeconds?: number;
89
90
  positionDisplayMode?: "precise" | "qualitative";
90
91
  }
@@ -25,7 +25,7 @@ export interface AuthEvents {
25
25
  export interface QueueEvents {
26
26
  "queue:entered": {
27
27
  queueId: string;
28
- position: number;
28
+ position?: number;
29
29
  estimatedWaitTime?: number;
30
30
  };
31
31
  "queue:left": {
@@ -1 +1 @@
1
- import{getLogger as e}from"../core/logger.js";import{deepClone as t}from"../core/utils.js";import{useSDKStore as i,toPersistedSnapshot as s}from"../state/store.js";import{isHeartbeatMessage as a,isDurableBootstrapRequestMessage as n,isDurableBootstrapResponseMessage as r,isSessionUpdateMessage as o,isCapabilityTokensUpdateMessage as p,isDistributionCapabilityIndexUpdateMessage as d,isWaitlistCapabilityIndexUpdateMessage as l,isJourneyUpsertMessage as h,isJourneyRemoveMessage as u,isRuntimeStateMessage as b}from"./protocol.js";import{ProtocolTransport as c}from"./protocol-transport.js";class y{constructor(y){if(this.logger=e(),this.liveJourneys=/* @__PURE__ */new Map,this.activeTabs=/* @__PURE__ */new Map,this.runtimeOwners=/* @__PURE__ */new Map,this.runtimeCursors=/* @__PURE__ */new Map,this.unsubscribers=[],this.isApplyingRemoteUpdate=!1,this.leaderId=null,this.bootstrapSettled=!1,this.handleMessage=e=>{if(a(e))return this.recordHeartbeat(e.source,e.payload.active),void this.markBootstrapSettled();if(this.recordHeartbeat(e.source,!0),this.markBootstrapSettled(),n(e)){if(this.isLeader()){const e=i.getState();this.transport.sendDurableBootstrapResponse({session:t(e.session),sessionVersion:e.versionMap.session||0,capabilityTokens:t(e.capabilityTokens),capabilityTokensVersion:e.versionMap.capabilityTokens||0,distributionCapabilityIndex:t(e.distributionCapabilityIndex),distributionCapabilityIndexVersion:e.versionMap.distributionCapabilityIndex||0,waitlistCapabilityIndex:t(e.waitlistCapabilityIndex),waitlistCapabilityIndexVersion:e.versionMap.waitlistCapabilityIndex||0,journeys:Object.fromEntries(Object.entries(e.activeJourneys).map(([e,i])=>[e,{snapshot:t(s(i)),version:i.revision}]))})}}else r(e)?this.applyBootstrapResponse(e.payload):o(e)?this.applyVersionedStoreField("session",e.payload.value,e.payload.version):p(e)?this.applyVersionedStoreField("capabilityTokens",e.payload.value,e.payload.version):d(e)?this.applyVersionedStoreField("distributionCapabilityIndex",e.payload.value,e.payload.version):l(e)?this.applyVersionedStoreField("waitlistCapabilityIndex",e.payload.value,e.payload.version):h(e)?this.applyJourneyUpsert(e.payload.experienceId,e.payload.snapshot,e.payload.version):u(e)?this.applyJourneyRemove(e.payload.experienceId,e.payload.version):b(e)&&this.applyRuntimeState(e.payload)},this.transport=new c(y.channelName),this.enabled=(y.enabled??!0)&&this.transport.isAvailable(),this.localOnly=!this.enabled,this.unsubscribers.push(i.subscribe((e,t)=>{this.isApplyingRemoteUpdate||this.handleLocalStateChange(e,t)})),this.enabled)return this.unsubscribers.push(this.transport.onMessage(this.handleMessage)),this.sendHeartbeat(!0),this.heartbeatTimer=setInterval(()=>{this.sendHeartbeat(!0),this.pruneDeadTabs()},2e3),this.requestBootstrap(),void(this.bootstrapTimer=setTimeout(()=>{this.markBootstrapSettled()},100));this.leaderId=this.transport.getTabId(),this.activeTabs.set(this.transport.getTabId(),Date.now()),this.bootstrapSettled=!0}registerJourney(e){this.liveJourneys.set(e.getExperienceId(),e),this.syncRuntimeOwnership()}unregisterJourney(e){this.stopRuntimeOwner(e,!1),this.liveJourneys.delete(e),this.runtimeCursors.delete(e)}close(){this.enabled&&this.sendHeartbeat(!1),this.bootstrapTimer&&(clearTimeout(this.bootstrapTimer),this.bootstrapTimer=void 0),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0);for(const e of Array.from(this.runtimeOwners.keys()))this.stopRuntimeOwner(e,!1);for(const e of this.unsubscribers)e();this.unsubscribers.length=0,this.transport.close()}isEnabled(){return this.enabled}getTabId(){return this.transport.getTabId()}isLeader(){return this.leaderId===this.transport.getTabId()}handleLocalStateChange(e,i){this.enabled&&e.session!==i.session&&this.transport.sendSessionUpdate(t(e.session),e.versionMap.session||0),this.enabled&&e.capabilityTokens!==i.capabilityTokens&&this.transport.sendCapabilityTokensUpdate(t(e.capabilityTokens),e.versionMap.capabilityTokens||0),this.enabled&&e.distributionCapabilityIndex!==i.distributionCapabilityIndex&&this.transport.sendDistributionCapabilityIndexUpdate(t(e.distributionCapabilityIndex),e.versionMap.distributionCapabilityIndex||0),this.enabled&&e.waitlistCapabilityIndex!==i.waitlistCapabilityIndex&&this.transport.sendWaitlistCapabilityIndexUpdate(t(e.waitlistCapabilityIndex),e.versionMap.waitlistCapabilityIndex||0);const a=e.activeJourneys,n=i.activeJourneys,r=/* @__PURE__ */new Set([...Object.keys(a),...Object.keys(n)]);for(const o of r){const e=a[o],i=n[o];if(!this.enabled||!e||i&&e.revision===i.revision)this.enabled&&!e&&i&&this.transport.sendJourneyRemove(o,i.revision+1);else{const i=s(e);this.transport.sendJourneyUpsert(o,t(i),i.revision)}}a!==n&&this.syncRuntimeOwnership()}applyBootstrapResponse(e){this.applyVersionedStoreField("session",e.session,e.sessionVersion),this.applyVersionedStoreField("capabilityTokens",e.capabilityTokens,e.capabilityTokensVersion),this.applyVersionedStoreField("distributionCapabilityIndex",e.distributionCapabilityIndex,e.distributionCapabilityIndexVersion),this.applyVersionedStoreField("waitlistCapabilityIndex",e.waitlistCapabilityIndex,e.waitlistCapabilityIndexVersion);for(const[t,i]of Object.entries(e.journeys))this.applyJourneyUpsert(t,i.snapshot,i.version)}applyVersionedStoreField(e,t,s){const a=i.getState();s<=(a.versionMap[e]||0)||(this.isApplyingRemoteUpdate=!0,i.setState({[e]:t,versionMap:{...a.versionMap,[e]:s}}),this.isApplyingRemoteUpdate=!1)}applyJourneyUpsert(e,t,a){const n=i.getState(),r=n.activeJourneys[e];if(r&&r.revision>=a)return;this.isApplyingRemoteUpdate=!0;const o=s(t);i.setState({activeJourneys:{...n.activeJourneys,[e]:o}}),this.isApplyingRemoteUpdate=!1,this.liveJourneys.get(e)?.reconcileSyncedSnapshot(o),this.syncRuntimeOwnership()}applyJourneyRemove(e,t){const s=i.getState(),a=s.activeJourneys[e];if(a&&a.revision>=t)return;const{[e]:n,...r}=s.activeJourneys;this.isApplyingRemoteUpdate=!0,i.setState({activeJourneys:r}),this.isApplyingRemoteUpdate=!1,this.liveJourneys.get(e)?.reconcileSyncedSnapshot(null),this.syncRuntimeOwnership()}applyRuntimeState(e){if(e.leaderId!==this.leaderId)return;const t=this.liveJourneys.get(e.experienceId);if(!t||this.isLeader())return;const i=t.getRuntimeBinding();if(!i||i.sequenceId!==e.sequenceId||i.participationId!==e.participationId||i.participationType!==e.participationType)return;const s=this.runtimeCursors.get(e.experienceId);s&&s.leaderId===e.leaderId&&s.runtimeVersion>=e.runtimeVersion||(this.runtimeCursors.set(e.experienceId,{leaderId:e.leaderId,runtimeVersion:e.runtimeVersion}),t.applyRuntimeState(e.state??null))}syncRuntimeOwnership(){if(this.bootstrapSettled){for(const[e,i]of this.liveJourneys.entries()){const s=i.getRuntimeBinding(),a=this.runtimeOwners.get(e);if(!this.isLeader()||!s){a?this.stopRuntimeOwner(e,!0):s||i.applyRuntimeState(null);continue}const n=`${s.sequenceId}:${s.participationType}:${s.participationId}`;if(a?.bindingKey===n)continue;a&&this.stopRuntimeOwner(e,!0),i.startRuntimeMonitoring();const r=i.getRuntimeDisplayAtom();if(!r)continue;let o=0;const p=i=>{o+=1,this.enabled&&this.transport.sendRuntimeState({experienceId:e,sequenceId:s.sequenceId,participationId:s.participationId,participationType:s.participationType,leaderId:this.transport.getTabId(),runtimeVersion:o,state:t(i.get())})},d=r.listen(()=>{p(r)});p(r),this.runtimeOwners.set(e,{bindingKey:n,sequenceId:s.sequenceId,participationId:s.participationId,participationType:s.participationType,unsubscribe:d,runtimeVersion:o})}for(const e of Array.from(this.runtimeOwners.keys()))this.liveJourneys.has(e)||this.stopRuntimeOwner(e,!0)}}stopRuntimeOwner(e,t){const i=this.runtimeOwners.get(e),s=this.liveJourneys.get(e);i?(i.unsubscribe(),t&&this.enabled&&this.isLeader()&&this.transport.sendRuntimeState({experienceId:e,sequenceId:i.sequenceId,participationId:i.participationId,participationType:i.participationType,leaderId:this.transport.getTabId(),runtimeVersion:i.runtimeVersion+1,state:null}),s?.stopRuntimeMonitoring(!0),this.runtimeOwners.delete(e)):this.runtimeOwners.delete(e)}sendHeartbeat(e){this.enabled&&(this.transport.sendHeartbeat(e),this.recordHeartbeat(this.transport.getTabId(),e))}requestBootstrap(){this.enabled&&this.transport.sendDurableBootstrapRequest()}markBootstrapSettled(){this.bootstrapSettled||(this.bootstrapSettled=!0,this.bootstrapTimer&&(clearTimeout(this.bootstrapTimer),this.bootstrapTimer=void 0),this.syncRuntimeOwnership())}recordHeartbeat(e,t){t||e===this.transport.getTabId()?this.activeTabs.set(e,Date.now()):this.activeTabs.delete(e),this.updateLeader()}pruneDeadTabs(){const e=Date.now();let t=!1;for(const[i,s]of this.activeTabs.entries())i!==this.transport.getTabId()&&e-s>6e3&&(this.activeTabs.delete(i),t=!0);t&&this.updateLeader()}updateLeader(){if(this.localOnly)return void(this.leaderId=this.transport.getTabId());const e=[this.transport.getTabId(),...this.activeTabs.keys()].sort()[0]??this.transport.getTabId();e!==this.leaderId&&(this.leaderId=e,this.runtimeCursors.clear(),this.logger.info("Cross-tab leader changed",{leaderId:e,tabId:this.transport.getTabId(),isLeader:this.isLeader()}),this.isLeader()||this.requestBootstrap(),this.syncRuntimeOwnership())}}export{y as CrossTabCoordinator};
1
+ import{getLogger as e}from"../core/logger.js";import{deepClone as t}from"../core/utils.js";import{useSDKStore as i,toPersistedSnapshot as s}from"../state/store.js";import{isHeartbeatMessage as n,isDurableBootstrapRequestMessage as a,isDurableBootstrapResponseMessage as r,isSessionUpdateMessage as o,isCapabilityTokensUpdateMessage as p,isDistributionCapabilityIndexUpdateMessage as d,isWaitlistCapabilityIndexUpdateMessage as l,isJourneyUpsertMessage as h,isJourneyRemoveMessage as u,isRuntimeStateMessage as c}from"./protocol.js";import{ProtocolTransport as b}from"./protocol-transport.js";class y{constructor(y){if(this.logger=e(),this.liveJourneys=/* @__PURE__ */new Map,this.activeTabs=/* @__PURE__ */new Map,this.runtimeOwners=/* @__PURE__ */new Map,this.runtimeCursors=/* @__PURE__ */new Map,this.unsubscribers=[],this.isApplyingRemoteUpdate=!1,this.leaderId=null,this.bootstrapSettled=!1,this.handleMessage=e=>{if(n(e))return this.recordHeartbeat(e.source,e.payload.active),void this.markBootstrapSettled();if(this.recordHeartbeat(e.source,!0),this.markBootstrapSettled(),a(e)){if(this.isLeader()){const e=i.getState();this.transport.sendDurableBootstrapResponse({session:t(e.session),sessionVersion:e.versionMap.session||0,capabilityTokens:t(e.capabilityTokens),capabilityTokensVersion:e.versionMap.capabilityTokens||0,distributionCapabilityIndex:t(e.distributionCapabilityIndex),distributionCapabilityIndexVersion:e.versionMap.distributionCapabilityIndex||0,waitlistCapabilityIndex:t(e.waitlistCapabilityIndex),waitlistCapabilityIndexVersion:e.versionMap.waitlistCapabilityIndex||0,journeys:Object.fromEntries(Object.entries(e.activeJourneys).map(([e,i])=>[e,{snapshot:t(s(i)),version:i.revision}]))})}}else r(e)?this.applyBootstrapResponse(e.payload):o(e)?this.applyVersionedStoreField("session",e.payload.value,e.payload.version):p(e)?this.applyVersionedStoreField("capabilityTokens",e.payload.value,e.payload.version):d(e)?this.applyVersionedStoreField("distributionCapabilityIndex",e.payload.value,e.payload.version):l(e)?this.applyVersionedStoreField("waitlistCapabilityIndex",e.payload.value,e.payload.version):h(e)?this.applyJourneyUpsert(e.payload.experienceId,e.payload.snapshot,e.payload.version):u(e)?this.applyJourneyRemove(e.payload.experienceId,e.payload.version):c(e)&&this.applyRuntimeState(e.payload)},this.transport=new b(y.channelName),this.enabled=(y.enabled??!0)&&this.transport.isAvailable(),this.localOnly=!this.enabled,this.unsubscribers.push(i.subscribe((e,t)=>{this.isApplyingRemoteUpdate||this.handleLocalStateChange(e,t)})),this.enabled)return this.unsubscribers.push(this.transport.onMessage(this.handleMessage)),this.sendHeartbeat(!0),this.heartbeatTimer=setInterval(()=>{this.sendHeartbeat(!0),this.pruneDeadTabs()},2e3),this.requestBootstrap(),void(this.bootstrapTimer=setTimeout(()=>{this.markBootstrapSettled()},100));this.leaderId=this.transport.getTabId(),this.activeTabs.set(this.transport.getTabId(),Date.now()),this.bootstrapSettled=!0}registerJourney(e){this.liveJourneys.set(e.getExperienceId(),e),this.syncRuntimeOwnership()}unregisterJourney(e){this.stopRuntimeOwner(e,!1),this.liveJourneys.delete(e),this.runtimeCursors.delete(e)}close(){this.enabled&&this.sendHeartbeat(!1),this.bootstrapTimer&&(clearTimeout(this.bootstrapTimer),this.bootstrapTimer=void 0),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=void 0);for(const e of Array.from(this.runtimeOwners.keys()))this.stopRuntimeOwner(e,!1);for(const e of this.unsubscribers)e();this.unsubscribers.length=0,this.transport.close()}isEnabled(){return this.enabled}getTabId(){return this.transport.getTabId()}isLeader(){return this.leaderId===this.transport.getTabId()}handleLocalStateChange(e,i){this.enabled&&e.session!==i.session&&this.transport.sendSessionUpdate(t(e.session),e.versionMap.session||0),this.enabled&&e.capabilityTokens!==i.capabilityTokens&&this.transport.sendCapabilityTokensUpdate(t(e.capabilityTokens),e.versionMap.capabilityTokens||0),this.enabled&&e.distributionCapabilityIndex!==i.distributionCapabilityIndex&&this.transport.sendDistributionCapabilityIndexUpdate(t(e.distributionCapabilityIndex),e.versionMap.distributionCapabilityIndex||0),this.enabled&&e.waitlistCapabilityIndex!==i.waitlistCapabilityIndex&&this.transport.sendWaitlistCapabilityIndexUpdate(t(e.waitlistCapabilityIndex),e.versionMap.waitlistCapabilityIndex||0);const n=e.activeJourneys,a=i.activeJourneys,r=/* @__PURE__ */new Set([...Object.keys(n),...Object.keys(a)]);for(const o of r){const e=n[o],i=a[o];if(!this.enabled||!e||i&&e.revision===i.revision)this.enabled&&!e&&i&&this.transport.sendJourneyRemove(o,i.revision+1);else{const i=s(e);this.transport.sendJourneyUpsert(o,t(i),i.revision)}}n!==a&&this.syncRuntimeOwnership()}applyBootstrapResponse(e){this.applyVersionedStoreField("session",e.session,e.sessionVersion),this.applyVersionedStoreField("capabilityTokens",e.capabilityTokens,e.capabilityTokensVersion),this.applyVersionedStoreField("distributionCapabilityIndex",e.distributionCapabilityIndex,e.distributionCapabilityIndexVersion),this.applyVersionedStoreField("waitlistCapabilityIndex",e.waitlistCapabilityIndex,e.waitlistCapabilityIndexVersion);for(const[t,i]of Object.entries(e.journeys))this.applyJourneyUpsert(t,i.snapshot,i.version)}applyVersionedStoreField(e,t,s){const n=i.getState();s<=(n.versionMap[e]||0)||(this.isApplyingRemoteUpdate=!0,i.setState({[e]:t,versionMap:{...n.versionMap,[e]:s}}),this.isApplyingRemoteUpdate=!1)}applyJourneyUpsert(e,t,n){const a=i.getState(),r=a.activeJourneys[e];if(r&&r.revision>=n)return;this.isApplyingRemoteUpdate=!0;const o=s(t);i.setState({activeJourneys:{...a.activeJourneys,[e]:o}}),this.isApplyingRemoteUpdate=!1,this.liveJourneys.get(e)?.reconcileSyncedSnapshot(o),this.syncRuntimeOwnership()}applyJourneyRemove(e,t){const s=i.getState(),n=s.activeJourneys[e];if(n&&n.revision>=t)return;const{[e]:a,...r}=s.activeJourneys;this.isApplyingRemoteUpdate=!0,i.setState({activeJourneys:r}),this.isApplyingRemoteUpdate=!1,this.liveJourneys.get(e)?.reconcileSyncedSnapshot(null),this.syncRuntimeOwnership()}applyRuntimeState(e){if(e.leaderId!==this.leaderId)return;const t=this.liveJourneys.get(e.experienceId);if(!t||this.isLeader())return;const i=t.getRuntimeBinding();if(!i||i.sequenceId!==e.sequenceId||i.participationId!==e.participationId||i.participationType!==e.participationType||"queue"===i.participationType&&i.runtimeContextToken!==e.runtimeContextToken)return;const s=this.runtimeCursors.get(e.experienceId);s&&s.leaderId===e.leaderId&&s.runtimeVersion>=e.runtimeVersion||(this.runtimeCursors.set(e.experienceId,{leaderId:e.leaderId,runtimeVersion:e.runtimeVersion}),t.applyRuntimeState(e.state??null))}syncRuntimeOwnership(){if(this.bootstrapSettled){for(const[e,i]of this.liveJourneys.entries()){const s=i.getRuntimeBinding(),n=this.runtimeOwners.get(e);if(!this.isLeader()||!s){n?this.stopRuntimeOwner(e,!0):s||i.applyRuntimeState(null);continue}const a=`${s.sequenceId}:${s.participationType}:${s.participationId}`;if(n?.bindingKey===a)continue;n&&this.stopRuntimeOwner(e,!0),i.startRuntimeMonitoring();const r=i.getRuntimeDisplayAtom();if(!r)continue;const o={bindingKey:a,sequenceId:s.sequenceId,participationId:s.participationId,participationType:s.participationType,runtimeContextToken:s.runtimeContextToken,unsubscribe:()=>{},runtimeVersion:0},p=s=>{const n=i.getRuntimeBinding();if(!n)return;`${n.sequenceId}:${n.participationType}:${n.participationId}`===a&&(o.runtimeVersion+=1,o.runtimeContextToken=n.runtimeContextToken,this.enabled&&this.transport.sendRuntimeState({experienceId:e,sequenceId:n.sequenceId,participationId:n.participationId,participationType:n.participationType,runtimeContextToken:n.runtimeContextToken,leaderId:this.transport.getTabId(),runtimeVersion:o.runtimeVersion,state:t(s.get())}))};o.unsubscribe=r.listen(()=>{p(r)}),p(r),this.runtimeOwners.set(e,o)}for(const e of Array.from(this.runtimeOwners.keys()))this.liveJourneys.has(e)||this.stopRuntimeOwner(e,!0)}}stopRuntimeOwner(e,t){const i=this.runtimeOwners.get(e),s=this.liveJourneys.get(e);i?(i.unsubscribe(),t&&this.enabled&&this.isLeader()&&this.transport.sendRuntimeState({experienceId:e,sequenceId:i.sequenceId,participationId:i.participationId,participationType:i.participationType,runtimeContextToken:i.runtimeContextToken,leaderId:this.transport.getTabId(),runtimeVersion:i.runtimeVersion+1,state:null}),s?.stopRuntimeMonitoring(!0),this.runtimeOwners.delete(e)):this.runtimeOwners.delete(e)}sendHeartbeat(e){this.enabled&&(this.transport.sendHeartbeat(e),this.recordHeartbeat(this.transport.getTabId(),e))}requestBootstrap(){this.enabled&&this.transport.sendDurableBootstrapRequest()}markBootstrapSettled(){this.bootstrapSettled||(this.bootstrapSettled=!0,this.bootstrapTimer&&(clearTimeout(this.bootstrapTimer),this.bootstrapTimer=void 0),this.syncRuntimeOwnership())}recordHeartbeat(e,t){t||e===this.transport.getTabId()?this.activeTabs.set(e,Date.now()):this.activeTabs.delete(e),this.updateLeader()}pruneDeadTabs(){const e=Date.now();let t=!1;for(const[i,s]of this.activeTabs.entries())i!==this.transport.getTabId()&&e-s>6e3&&(this.activeTabs.delete(i),t=!0);t&&this.updateLeader()}updateLeader(){if(this.localOnly)return void(this.leaderId=this.transport.getTabId());const e=[this.transport.getTabId(),...this.activeTabs.keys()].sort()[0]??this.transport.getTabId();e!==this.leaderId&&(this.leaderId=e,this.runtimeCursors.clear(),this.logger.info("Cross-tab leader changed",{leaderId:e,tabId:this.transport.getTabId(),isLeader:this.isLeader()}),this.isLeader()||this.requestBootstrap(),this.syncRuntimeOwnership())}}export{y as CrossTabCoordinator};
@@ -1 +1 @@
1
- import*as e from"valibot";const t=e.object({id:e.string(),timestamp:e.number(),source:e.string(),version:e.number()}),n=e.intersect([t,e.object({type:e.literal("durable:bootstrap:request"),payload:e.object({})})]),i=e.object({snapshot:e.unknown(),version:e.number()}),r=e.intersect([t,e.object({type:e.literal("durable:bootstrap:response"),payload:e.object({session:e.unknown(),sessionVersion:e.number(),capabilityTokens:e.unknown(),capabilityTokensVersion:e.number(),distributionCapabilityIndex:e.unknown(),distributionCapabilityIndexVersion:e.number(),waitlistCapabilityIndex:e.unknown(),waitlistCapabilityIndexVersion:e.number(),journeys:e.record(e.string(),i)})})]),o=e.intersect([t,e.object({type:e.literal("session:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),a=e.intersect([t,e.object({type:e.literal("capability-tokens:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),s=e.intersect([t,e.object({type:e.literal("distribution-capability-index:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),u=e.intersect([t,e.object({type:e.literal("waitlist-capability-index:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),p=e.intersect([t,e.object({type:e.literal("journey:upsert"),payload:e.object({experienceId:e.string(),snapshot:e.unknown(),version:e.number()})})]),c=e.intersect([t,e.object({type:e.literal("journey:remove"),payload:e.object({experienceId:e.string(),version:e.number()})})]),b=e.intersect([t,e.object({type:e.literal("runtime:state"),payload:e.object({experienceId:e.string(),sequenceId:e.string(),participationId:e.string(),participationType:e.picklist(["queue","draw","auction","timed_release","appointment"]),leaderId:e.string(),runtimeVersion:e.number(),state:e.nullable(e.unknown())})})]),l=e.intersect([t,e.object({type:e.literal("heartbeat"),payload:e.object({active:e.boolean()})})]),y=e.union([n,r,o,a,s,u,p,c,b,l]),d=new Set(y.options.map(e=>e.options[1].entries.type.literal));function j(e){const t=null!==e&&"object"==typeof e&&"type"in e?e.type:void 0;return"string"==typeof t&&d.has(t)?t:"<unknown>"}function m(e){return"durable:bootstrap:request"===e.type}function k(e){return"durable:bootstrap:response"===e.type}function w(e){return"session:update"===e.type}function v(e){return"capability-tokens:update"===e.type}function f(e){return"distribution-capability-index:update"===e.type}function x(e){return"waitlist-capability-index:update"===e.type}function g(e){return"journey:upsert"===e.type}function I(e){return"journey:remove"===e.type}function h(e){return"runtime:state"===e.type}function V(e){return"heartbeat"===e.type}const q=3,C="fanfare-sdk-sync";export{C as DEFAULT_CHANNEL_NAME,q as PROTOCOL_VERSION,y as SyncMessageSchema,v as isCapabilityTokensUpdateMessage,f as isDistributionCapabilityIndexUpdateMessage,m as isDurableBootstrapRequestMessage,k as isDurableBootstrapResponseMessage,V as isHeartbeatMessage,I as isJourneyRemoveMessage,g as isJourneyUpsertMessage,h as isRuntimeStateMessage,w as isSessionUpdateMessage,x as isWaitlistCapabilityIndexUpdateMessage,j as messageType};
1
+ import*as e from"valibot";const t=e.object({id:e.string(),timestamp:e.number(),source:e.string(),version:e.number()}),n=e.intersect([t,e.object({type:e.literal("durable:bootstrap:request"),payload:e.object({})})]),i=e.object({snapshot:e.unknown(),version:e.number()}),r=e.intersect([t,e.object({type:e.literal("durable:bootstrap:response"),payload:e.object({session:e.unknown(),sessionVersion:e.number(),capabilityTokens:e.unknown(),capabilityTokensVersion:e.number(),distributionCapabilityIndex:e.unknown(),distributionCapabilityIndexVersion:e.number(),waitlistCapabilityIndex:e.unknown(),waitlistCapabilityIndexVersion:e.number(),journeys:e.record(e.string(),i)})})]),o=e.intersect([t,e.object({type:e.literal("session:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),a=e.intersect([t,e.object({type:e.literal("capability-tokens:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),s=e.intersect([t,e.object({type:e.literal("distribution-capability-index:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),u=e.intersect([t,e.object({type:e.literal("waitlist-capability-index:update"),payload:e.object({value:e.unknown(),version:e.number()})})]),p=e.intersect([t,e.object({type:e.literal("journey:upsert"),payload:e.object({experienceId:e.string(),snapshot:e.unknown(),version:e.number()})})]),c=e.intersect([t,e.object({type:e.literal("journey:remove"),payload:e.object({experienceId:e.string(),version:e.number()})})]),l=e.intersect([t,e.object({type:e.literal("runtime:state"),payload:e.object({experienceId:e.string(),sequenceId:e.string(),participationId:e.string(),participationType:e.picklist(["queue","draw","auction","timed_release","appointment"]),runtimeContextToken:e.optional(e.string()),leaderId:e.string(),runtimeVersion:e.number(),state:e.nullable(e.unknown())})})]),b=e.intersect([t,e.object({type:e.literal("heartbeat"),payload:e.object({active:e.boolean()})})]),y=e.union([n,r,o,a,s,u,p,c,l,b]),d=new Set(y.options.map(e=>e.options[1].entries.type.literal));function j(e){const t=null!==e&&"object"==typeof e&&"type"in e?e.type:void 0;return"string"==typeof t&&d.has(t)?t:"<unknown>"}function m(e){return"durable:bootstrap:request"===e.type}function k(e){return"durable:bootstrap:response"===e.type}function w(e){return"session:update"===e.type}function v(e){return"capability-tokens:update"===e.type}function f(e){return"distribution-capability-index:update"===e.type}function x(e){return"waitlist-capability-index:update"===e.type}function g(e){return"journey:upsert"===e.type}function I(e){return"journey:remove"===e.type}function h(e){return"runtime:state"===e.type}function C(e){return"heartbeat"===e.type}const V=3,q="fanfare-sdk-sync";export{q as DEFAULT_CHANNEL_NAME,V as PROTOCOL_VERSION,y as SyncMessageSchema,v as isCapabilityTokensUpdateMessage,f as isDistributionCapabilityIndexUpdateMessage,m as isDurableBootstrapRequestMessage,k as isDurableBootstrapResponseMessage,C as isHeartbeatMessage,I as isJourneyRemoveMessage,g as isJourneyUpsertMessage,h as isRuntimeStateMessage,w as isSessionUpdateMessage,x as isWaitlistCapabilityIndexUpdateMessage,j as messageType};
@@ -0,0 +1,2 @@
1
+ /** Restore focus lost when a native control is disabled or replaced, preserving deliberate focus moves. */
2
+ export declare function restoreFocus(target?: HTMLButtonElement | HTMLInputElement | null): void;
@@ -0,0 +1 @@
1
+ function e(e){if(!e?.isConnected||e.disabled)return;const t=e.ownerDocument,o=e.getRootNode().activeElement??t.activeElement;o&&o!==e&&o!==t.body||e.focus()}export{e as restoreFocus};
@@ -0,0 +1,13 @@
1
+ /** Shared first-party offer destinations and evidence format. */
2
+ export declare const MARKETING_SMS_TERMS_URL = "https://www.fanfare.io/terms-of-service#sms";
3
+ export declare const MARKETING_SMS_PRIVACY_URL = "https://www.fanfare.io/privacy-policy#text-messages";
4
+ export type MarketingSmsCopy = readonly [
5
+ title: string,
6
+ disclosure: string,
7
+ legalLine: string,
8
+ termsLabel: string,
9
+ privacyLabel: string,
10
+ agreeLabel: string
11
+ ];
12
+ /** Formats already-resolved displayed copy; callers freeze this only on affirmative consent. */
13
+ export declare function formatMarketingSmsConsentText([title, disclosure, legalLine, termsLabel, privacyLabel, agreeLabel,]: MarketingSmsCopy): string;
@@ -0,0 +1 @@
1
+ const t="https://www.fanfare.io/terms-of-service#sms",e="https://www.fanfare.io/privacy-policy#text-messages";function s([s,n,o,r,i,a]){return[s,n,o,`${r}: ${t}`,`${i}: ${e}`,a].join("\n")}export{e as MARKETING_SMS_PRIVACY_URL,t as MARKETING_SMS_TERMS_URL,s as formatMarketingSmsConsentText};
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.23.0";
5
+ export declare const version = "0.25.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- const o="0.23.0";export{o as version};
1
+ const o="0.25.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.23.0",
3
+ "version": "0.25.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",
@@ -110,24 +110,20 @@
110
110
  }
111
111
  },
112
112
  "dependencies": {
113
- "big.js": "^7.0.1",
114
113
  "nanostores": "^1.1.0",
115
114
  "uuid": "^14.0.0",
116
115
  "wretch": "^2.11.0",
117
116
  "zustand": "^5.0.6",
118
- "@fanfare-io/fanfare-sdk-contracts": "0.23.0"
117
+ "@fanfare-io/fanfare-sdk-contracts": "0.25.0"
119
118
  },
120
119
  "peerDependencies": {
121
120
  "valibot": "^1.1.0"
122
121
  },
123
122
  "devDependencies": {
124
- "@chromatic-com/storybook": "^5.0.1",
125
123
  "@size-limit/preset-small-lib": "^11.1.8",
126
- "@storybook/addon-docs": "^10.2.16",
127
- "@storybook/addon-onboarding": "^10.2.16",
128
- "@storybook/addon-vitest": "^10.2.16",
129
- "@storybook/react-vite": "^10.2.16",
130
- "@types/big.js": "^6.2.2",
124
+ "@storybook/addon-docs": "~10.4.6",
125
+ "@storybook/addon-vitest": "~10.4.6",
126
+ "@storybook/react-vite": "~10.4.6",
131
127
  "@types/node": "^24.1.0",
132
128
  "@types/react": "^19.1.12",
133
129
  "@types/react-dom": "^19.1.9",
@@ -144,7 +140,7 @@
144
140
  "rimraf": "^6.0.1",
145
141
  "rollup-plugin-visualizer": "^5.14.0",
146
142
  "size-limit": "^11.1.8",
147
- "storybook": "^10.2.16",
143
+ "storybook": "~10.4.6",
148
144
  "terser": "^5.46.1",
149
145
  "typescript": "^5.9.2",
150
146
  "typescript-native": "npm:typescript@^7.0.2",
@@ -154,7 +150,8 @@
154
150
  "vite-plugin-dts": "^4.5.4",
155
151
  "vite-tsconfig-paths": "^5.1.4",
156
152
  "vitest": "^5.0.0",
157
- "@fanfare-io/shared-models": "0.23.0"
153
+ "@fanfare-io/shared-models": "0.25.0",
154
+ "@fanfare-io/storybook-preset": "0.25.0"
158
155
  },
159
156
  "sideEffects": false,
160
157
  "keywords": [
@@ -171,7 +168,7 @@
171
168
  {
172
169
  "name": "Core + Queues",
173
170
  "path": "dist/index.js",
174
- "limit": "56 KB"
171
+ "limit": "56.5 KB"
175
172
  }
176
173
  ],
177
174
  "scripts": {