@fanfare-io/fanfare-sdk-core 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appointments/appointment.module.js +1 -1
- package/dist/auth/types.d.ts +5 -0
- package/dist/core/logger.js +1 -1
- package/dist/core/parse-response.js +1 -1
- package/dist/core/utils.d.ts +4 -5
- package/dist/core/utils.js +1 -1
- package/dist/draws/draw.driver.js +1 -1
- package/dist/draws/draw.module.js +1 -1
- package/dist/draws/types.d.ts +6 -1
- package/dist/state/store.js +1 -1
- package/dist/sync/broadcast-transport.js +1 -1
- package/dist/sync/localstorage-transport.js +1 -1
- package/dist/sync/protocol-transport.js +1 -1
- package/dist/sync/protocol.js +1 -1
- package/dist/sync/transport.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as t from"valibot";import{isFanfareError as o,createError as e}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{parseResponse as i}from"../core/parse-response.js";import{createPollScheduler as
|
|
1
|
+
import*as t from"valibot";import{isFanfareError as o,createError as e}from"../core/errors.js";import{getLogger as n}from"../core/logger.js";import{parseResponse as i}from"../core/parse-response.js";import{createPollScheduler as r}from"../core/poll-scheduler.js";import{DistributionMonitorRuntime as s}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as a}from"../state/events.js";function l(t,o,e){const n=i(t,o,{endpoint:e},{throwOnFailure:!0});if(!n.ok)throw n.error;return n.value}const d=t.object({id:t.string(),startTime:t.string(),endTime:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),timezone:t.nullish(t.string()),capacity:t.number(),available:t.number()}),c=t.object({slots:t.array(d)}),m=t.object({id:t.string(),type:t.literal("appointment"),openAt:t.nullish(t.string()),closeAt:t.nullish(t.string()),slotDurationMinutes:t.nullish(t.number()),timeZone:t.nullish(t.string()),locationId:t.nullish(t.string()),totalSlots:t.nullish(t.number()),availableSlots:t.nullish(t.number())}),p=t.string(),h=t.picklist(["consumer","admin","killed"]),u=t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.string(),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.string(),endTime:t.string(),confirmationCode:t.string(),status:p,cancelledCause:t.nullish(h),createdAt:t.string()}),g=t.object({booking:t.nullish(t.object({bookingId:t.string(),appointmentId:t.string(),consumerId:t.string(),slotId:t.nullish(t.string()),locationId:t.nullish(t.string()),locationName:t.nullish(t.string()),startTime:t.nullish(t.string()),endTime:t.nullish(t.string()),confirmationCode:t.nullish(t.string()),status:p,cancelledCause:t.nullish(h)}))}),I=t.object({booking:u,bookingId:t.string(),previousSlotId:t.string(),newSlotId:t.string(),confirmationCode:t.string(),status:t.string(),cancelledCause:t.nullish(h)});function b(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId,locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime,endTime:t.endTime,confirmationCode:t.confirmationCode,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}function k(t){return{id:t.id,startTime:t.startTime,endTime:t.endTime,locationId:t.locationId??void 0,locationName:t.locationName??void 0,timezone:t.timezone??void 0,capacity:t.capacity,available:t.available}}class w{constructor(t,o){this.logger=n(),this.monitorRuntime=new s,this.pollSchedulers=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.observedBooking=/* @__PURE__ */new Map,this.destroyed=!1,this.http=t,this.pollingIntervalMs=o?.pollingIntervalMs??3e4}async get(t,n){try{const o=await this.http.get(`/appointments/${t}`,n),e=l(m,o,`/appointments/${t}`);return{id:e.id,type:"appointment",openAt:e.openAt??void 0,closeAt:e.closeAt??void 0,slotDurationMinutes:e.slotDurationMinutes??void 0,bookingWindowOpenAt:e.openAt??void 0,bookingWindowCloseAt:e.closeAt??void 0,timezone:e.timeZone??void 0,locationId:e.locationId??void 0,totalSlots:e.totalSlots??void 0,availableSlots:e.availableSlots??void 0}}catch(i){if(this.logger.error("Failed to get appointment",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get appointment",{error:i})}}async getSlots(t,n,i){try{const o=new URLSearchParams({startDate:n.startDate,endDate:n.endDate}),e=await this.http.get(`/appointments/${t}/slots?${o.toString()}`,i);return l(c,e,`/appointments/${t}/slots`).slots.map(k)}catch(r){if(this.logger.error("Failed to get appointment slots",{appointmentId:t,error:r}),o(r))throw r;throw e.networkError("Failed to get appointment slots",{error:r})}}async getMe(t){try{const o=await this.http.get(`/appointments/${t}/me`),e=l(g,o,`/appointments/${t}/me`);return e.booking?function(t){const{appointmentId:o,status:e}=t;let i;return"booked"===e||"completed"===e||"cancelled"===e?i=e:(n().warn("Unknown appointment consumerStatus; treating as booked",{appointmentId:o}),i="booked"),{bookingId:t.bookingId,appointmentId:t.appointmentId,slotId:t.slotId??"",locationId:t.locationId??void 0,locationName:t.locationName??void 0,startTime:t.startTime??"",endTime:t.endTime??"",confirmationCode:t.confirmationCode??void 0,consumerStatus:i,cancelledCause:t.cancelledCause??void 0}}(e.booking):null}catch(i){if(this.logger.error("Failed to get current booking",{appointmentId:t,error:i}),o(i))throw i;throw e.networkError("Failed to get current booking",{error:i})}}async book(t,n,i,r,s){try{const o=await this.http.post(`/appointments/${t}/book`,{slotId:n,locationId:i??null},r),e=b(l(u,o,`/appointments/${t}/book`));return a().emit("experience:distribution-entered",{distributionType:"appointment",distributionId:t,...s}),e}catch(d){if(this.logger.error("Failed to book appointment",{appointmentId:t,slotId:n,locationId:i,error:d}),o(d))throw d;throw e.networkError("Failed to book appointment",{error:d})}}async cancel(t,n,i,r){try{await this.http.post(`/appointments/${t}/cancel`,{slotId:n,locationId:i??null},r)}catch(s){if(this.logger.error("Failed to cancel appointment",{appointmentId:t,slotId:n,locationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to cancel appointment",{error:s})}}async reschedule(t,n,i,r){try{const o=await this.http.post(`/appointments/${t}/reschedule`,{newSlotId:n,newLocationId:i??null},r),e=b(l(I,o,`/appointments/${t}/reschedule`).booking);if(this.isMonitoring(t)){const o=this.monitorRuntime.getDisplayAtom(t);o&&o.set({type:"appointment",booking:e,msUntilStart:Math.max(0,Date.parse(e.startTime)-Date.now())}),this.startMonitor(t,o?{displayAtom:o}:void 0,void 0,this.pollSchedulers.get(t)?.onDegradedChange,e)}return e}catch(s){if(this.logger.error("Failed to reschedule appointment",{appointmentId:t,newSlotId:n,newLocationId:i,error:s}),o(s))throw s;throw e.networkError("Failed to reschedule appointment",{error:s})}}startMonitoring(t,o,e,n){this.startMonitor(t,o,e,n)}startMonitor(t,o,e,n,i){if(this.destroyed)return;this.monitorRuntime.start(t,o,e),this.stopPolling(t);const s=this.monitorRuntime.getDisplayAtom(t)?.get();this.observedBooking.set(t,"appointment"===s?.type&&void 0!==s.booking);const a=(this.monitorGenerations.get(t)??0)+1;this.monitorGenerations.set(t,a);const l=r({baseMs:this.pollingIntervalMs,suppressBackoff:()=>!1,onDegradedChange:n,shouldContinue:()=>!this.destroyed&&this.monitorGenerations.get(t)===a&&this.pollSchedulers.has(t),run:async()=>{if(!this.destroyed&&this.monitorGenerations.get(t)===a)try{const o=i??await this.getMe(t);if(i=void 0,this.destroyed||this.monitorGenerations.get(t)!==a)return;const e=this.monitorRuntime.getDisplayAtom(t);if(!o)return void(this.observedBooking.get(t)&&(this.monitorRuntime.notify(t,{type:"ended",outcome:{type:"closed"}}),this.stopMonitoring(t)));if(this.observedBooking.set(t,!0),"cancelled"===o.consumerStatus||"completed"===o.consumerStatus)return this.monitorRuntime.notify(t,{type:"ended",outcome:{type:o.consumerStatus,..."cancelled"===o.consumerStatus&&"killed"===o.cancelledCause?{reason:"killed"}:{}}}),void this.stopMonitoring(t);const n=Math.max(0,Date.parse(o.startTime)-Date.now());if(!e)return;e.set({type:"appointment",booking:{slotId:o.slotId,locationId:o.locationId,locationName:o.locationName,startTime:o.startTime,endTime:o.endTime,confirmationCode:o.confirmationCode,consumerStatus:o.consumerStatus,cancelledCause:o.cancelledCause},msUntilStart:n})}catch(o){throw this.logger.error("Appointment polling tick failed",{id:t,error:o}),o}}});this.pollSchedulers.set(t,{scheduler:l,onDegradedChange:n}),l.start()}stopMonitoring(t){this.stopPolling(t),this.monitorRuntime.stop(t),this.observedBooking.delete(t),this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}isMonitoring(t){return this.monitorRuntime.has(t)||this.pollSchedulers.has(t)}bumpMonitorGeneration(t){this.monitorGenerations.set(t,(this.monitorGenerations.get(t)??0)+1)}destroy(){this.destroyed=!0,this.monitorGenerations.clear(),this.observedBooking.clear();for(const t of Array.from(this.pollSchedulers.keys()))this.stopPolling(t);this.monitorRuntime.clear()}stopPolling(t){const o=this.pollSchedulers.get(t);void 0!==o&&(o.scheduler.stop(),this.pollSchedulers.delete(t))}}export{w as AppointmentManagementModule};
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export interface OtpRequest {
|
|
|
56
56
|
phone?: string;
|
|
57
57
|
defaultCountry?: string;
|
|
58
58
|
name?: string;
|
|
59
|
+
/**
|
|
60
|
+
* The experience the consumer is signing in to. The sign-in email then carries that experience's
|
|
61
|
+
* branding, matching the rest of its emails; without it the email uses the organization's.
|
|
62
|
+
*/
|
|
63
|
+
experienceId?: string;
|
|
59
64
|
}
|
|
60
65
|
export interface OtpVerify {
|
|
61
66
|
email?: string;
|
package/dist/core/logger.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createError as e}from"./errors.js";import{maskPII as
|
|
1
|
+
import{createError as e}from"./errors.js";import{maskPII as r}from"./utils.js";class o{constructor(e){this.levels={debug:0,info:1,warn:2,error:3},this.config=e}shouldLog(e){return!!this.config.enabled&&this.levels[e]>=this.levels[this.config.level]}format(e,o,i){const t=[`${this.config.prefix?`[${this.config.prefix}] `:""}${/* @__PURE__ */(new Date).toISOString()} [${e.toUpperCase()}] ${o}`];if(void 0!==i){const e=r(i);void 0!==e&&t.push(e)}switch(e){case"debug":case"info":break;case"warn":console.warn(...t);break;case"error":console.error(...t)}}debug(e,r){this.shouldLog("debug")&&this.format("debug",e,r)}info(e,r){this.shouldLog("info")&&this.format("info",e,r)}warn(e,r){this.shouldLog("warn")&&this.format("warn",e,r)}error(e,r){this.shouldLog("error")&&this.format("error",e,r)}setLevel(e){this.config.level=e}setEnabled(e){this.config.enabled=e}}let i=null;function t(e){return i&&!e||(i=new o(e||{enabled:!1,level:"warn",prefix:"Fanfare"})),i}function n(){if(!i)throw e.notInitialized("Logger not initialized. Call createLogger first.");return i}function s(){return i}export{t as createLogger,n as getLogger,s as peekLogger};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"valibot";import{getEventBus as
|
|
1
|
+
import*as e from"valibot";import{getEventBus as t}from"../state/events.js";import{ProtocolError as r}from"./errors.js";import{getLogger as o}from"./logger.js";function s(s,i,p,u={}){const d=e.safeParse(s,i);if(d.success)return{ok:!0,value:d.output};const a=function(e){const t=e[0];if(!t)return"no issues reported";const r=n(t),o=r?` at "${r}"`:"",s=e.length>1?` (and ${e.length-1} more)`:"";return`expected ${t.expected??t.type}${o}${s}`}(d.issues),c=`Response from ${p.endpoint} failed schema validation: ${a}`,m=new r(c,p.endpoint,d.issues,p.requestId);var f;if(o().error("Protocol error: response failed schema validation",{endpoint:p.endpoint,requestId:p.requestId,issues:(f=d.issues,f.map(e=>({path:n(e),expected:e.expected??e.type})))}),t().emit("journey:error",{kind:"protocolError",endpoint:p.endpoint,message:c,requestId:p.requestId,issues:d.issues}),u.throwOnFailure)throw m;return{ok:!1,error:m}}function n(e){const t=e.path?.map(e=>String(e.key))??[];return"never"===e.expected&&t.length>0&&(t[t.length-1]="<unexpected>"),t.join(".")}export{s as parseResponse};
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -46,11 +46,10 @@ export declare function isStorageAvailable(type: "localStorage" | "sessionStorag
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function safeJsonParse<T>(json: string, fallback?: T): T | null;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* Mask sensitive PII data in objects
|
|
49
|
+
* Project arbitrary log data onto what is safe to print: errors reduced to their identity, and
|
|
50
|
+
* string, number and boolean values held under a safe key. A top-level primitive carries no key to
|
|
51
|
+
* judge it by and is dropped, as is every key outside the set — dropped rather than masked, so that
|
|
52
|
+
* neither the value nor its presence can be inferred from the console.
|
|
54
53
|
*/
|
|
55
54
|
export declare function maskPII(data: unknown): unknown;
|
|
56
55
|
/**
|
package/dist/core/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{v7 as
|
|
1
|
+
import{v7 as r}from"uuid";import{ErrorCodes as t}from"./errors.js";function e(){return r()}const n=(r,t=.2,e=Math.random)=>Math.round(r*(1-t+2*e()*t));function o(r,t=/* @__PURE__ */new WeakSet){if(null===r||"object"!=typeof r)return r;if(t.has(r))return r;if(r instanceof Date)return new Date(r.getTime());if(r instanceof Array)return t.add(r),r.map(r=>o(r,t));if(r instanceof Object){t.add(r);const e={};for(const n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=o(r[n],t));return e}return r}function i(){return"undefined"!=typeof window&&void 0!==window.document}function a(r){if(!i())return!1;try{const t=window[r],e="__storage_test__";return t.setItem(e,e),t.removeItem(e),!0}catch{return!1}}const s=new Set("adapterId appointmentId attempt auctionId bytes code correlationId distributionId drawId endpoint expected experienceId id issues journeyId kind locationId organizationId path reason requestId retryAfterMs slotId status statusCode type waitlistId".split(" ")),u=Object.values(t),c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function d(r){const t={error:"|AbortError|DOMException|Error|FanfareError|NoSequenceAvailableError|ProtocolError|RangeError|SequenceFullError|SnapshotInvariantError|SyntaxError|TypeError|ValiError|ValidationError|WaitlistClosedError|".includes(`|${r.name}|`)?r.name:"<unknown>"},{code:e,endpoint:n,status:o,requestId:i,correlationId:a}=r;return"string"==typeof e&&u.includes(e)&&(t.code=e),"string"==typeof n&&(t.endpoint=n),Number.isInteger(o)&&(t.status=o),"string"==typeof i&&c.test(i)&&(t.requestId=i),"string"==typeof a&&c.test(a)&&(t.correlationId=a),t}function f(r){if(r instanceof Error)return d(r);if(Array.isArray(r))return r.map(r=>f(r));if(null!==r&&"object"==typeof r){const t={};for(const[e,n]of Object.entries(r)){if(n instanceof Error){t[e]=d(n);continue}if(!s.has(e))continue;if("string"==typeof n||"number"==typeof n||"boolean"==typeof n){t[e]=n;continue}const r=f(n);void 0!==r&&(t[e]=r)}return t}}export{o as deepClone,e as generateId,i as isBrowser,a as isStorageAvailable,n as jitteredIntervalMs,f as maskPII};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{DrawConsumerStatuses as e}from"@fanfare-io/fanfare-sdk-contracts/consumer-me";import{createError as t}from"../core/errors.js";import{buildGrantPaymentAffordance as i}from"../experiences/sequence-driver.js";import{resolveCheckoutContract as n,checkoutViewFields as s,isEntryActionRequired as r,canReenter as o,isPreAuthMisconfigured as a}from"../payment/checkout-contract.js";import{isClaimableSettlement as d}from"../payment/settlement.js";import{isUnresolvedDrawConsumer as c}from"./draw.sequence.js";import{drawSelectionFromResponse as u}from"./types.js";function p(e){return a(n(e.details))}function l(e){return o(e.distribution.lifecycle,n(e.distribution.details))}function m(e){return new f(e)}const h=new Set(e);function w(e){const{distribution:i,consumer:n,grant:s,checkout:r,settlement:o,selection:a,outcome:d}=e;if(!i)return{phase:"unavailable"};const u=function(e){if("draw"!==e.type)throw t.validationError("Draw driver received a non-draw distribution");return e}(i),l=n&&"draw"===n.mechanism?function(e){const{mechanism:t,...i}=e;return i}(n):y(i);if(s)return"completed"===d?.type?v(u,l,d,r):s.expiresAt&&Date.now()>new Date(s.expiresAt).getTime()?"open"===u.lifecycle?p(u)?{phase:"unavailable",reason:"checkout_misconfigured"}:{phase:"enterable",mechanism:"draw",distribution:u,consumer:y(u)}:v(u,l,{type:"expired"}):o&&a?{phase:"granted",mechanism:"draw",distribution:u,consumer:l,grant:s,settlement:o,selection:a}:{phase:"unavailable"};if(!function(e){return h.has(e.status)}(l))return{phase:"unavailable",reason:"unrecognized_standing"};if(!d&&"draw"===n?.mechanism&&"closed"===u.lifecycle)return v(u,l,{type:"lost"});const m=d??function(e){return"completed"===e.status?{type:"completed"}:"denied"===e.status?{type:"denied",reason:e.deniedReason}:void 0}(l);return m?v(u,l,m,r):"settling"===u.lifecycle?{phase:"settling",mechanism:"draw",distribution:u,consumer:l}:c(l)?{phase:"participating",mechanism:"draw",distribution:u,consumer:l}:"scheduled"===u.lifecycle?{phase:"scheduled",mechanism:"draw",distribution:u}:"open"===u.lifecycle?p(u)?{phase:"unavailable",reason:"checkout_misconfigured"}:{phase:"enterable",mechanism:"draw",distribution:u,consumer:l}:v(u,l,{type:"closed"})}function b(e,t){return e&&"draw"===e.mechanism&&t?"left"===t.type?{...e,status:"left"}:"denied"===t.type?{...e,status:"denied",deniedReason:t.reason}:"completed"===t.type?{...e,status:"completed"}:"won"===t.type?{...e,status:"won"}:{...e,status:"not_entered"}:e}class f{constructor(e){this.deps=e,this.mechanism="draw",this.settlementCanStartWhileActive=!0}project(e){return w(e)}buildView(e,n){if("unavailable"===e.phase)return{phase:"unavailable",reason:e.reason};const s=function(e){if(!("mechanism"in e)||"draw"!==e.mechanism)throw t.validationError("Draw driver received a non-draw sequence");return e}(e);switch(s.phase){case"scheduled":return{phase:"scheduled",mechanism:"draw",distribution:s.distribution,startsAt:s.distribution.startsAt??s.distribution.opensAt};case"enterable":return this.buildEnterableView(s,n);case"participating":return{phase:"participating",mechanism:"draw",consumer:s.consumer,display$:n.displayAtom,loadSelectionOptions:n.loadSelectionOptions,select:n.select,leave:()=>this.leave()};case"settling":return{phase:"settling",mechanism:"draw",distribution:s.distribution,consumer:s.consumer};case"granted":{const e={phase:"granted",mechanism:"draw",distribution:s.distribution,consumer:s.consumer,grant:s.grant},{settlement:t}=s,r=s.selection;if("awaiting_assignment"===r.status)return{...e,settlement:t,selection:r};if("required"===r.status)return{...e,settlement:t,selection:r,select:n.select,loadSelectionOptions:n.loadSelectionOptions};if("internal"===t.processor&&"at_end"===t.mode){const o=i(t,s.pendingAction,n);return o?{...e,settlement:t,selection:r,loadSelectionOptions:n.loadSelectionOptions,select:n.select,claim:()=>n.claim(),payment:o}:{...e,settlement:t,selection:r,loadSelectionOptions:n.loadSelectionOptions,select:n.select,claim:()=>n.claim()}}return d(t)?{...e,settlement:t,selection:r,loadSelectionOptions:n.loadSelectionOptions,select:n.select,claim:()=>n.claim()}:{...e,settlement:t,selection:r}}case"ended":return this.buildEndedView(s)}}buildEnterableView(e,t){const i=n(e.distribution.details),r=s(i);return"pre_auth"===r.checkoutMode?{phase:"enterable",mechanism:"draw",...r,distribution:e.distribution,consumer:e.consumer,...e.pendingAction?{pendingAction:e.pendingAction}:{},...e.paymentProvider?{paymentProvider:e.paymentProvider}:{},loadSelectionOptions:t.loadSelectionOptions,enter:(e,t)=>this.enter({paymentInput:e,selection:t})}:{phase:"enterable",mechanism:"draw",...r,distribution:e.distribution,consumer:e.consumer,loadSelectionOptions:t.loadSelectionOptions,enter:e=>this.enter({selection:e})}}buildEndedView(e){const t={phase:"ended",mechanism:"draw",consumer:e.consumer,outcome:e.outcome,...e.checkout?{checkout:e.checkout}:{}};if(!l(e))return t;const i=n(e.distribution.details),r=s(i);return"pre_auth"===r.checkoutMode?{...t,...r,reenter:(e,t)=>this.enter({paymentInput:e,selection:t})}:{...t,...r,reenter:e=>this.enter({selection:e})}}startMonitoring(e){this.activeMonitorId=e.participation.id;const t=this.deps.getCurrentSequence(),i=t&&("participating"===t.phase||"settling"===t.phase)&&"mechanism"in t&&"draw"===t.mechanism&&"draw"===t.distribution.details?.type?t.distribution.details.drawTime:void 0;this.deps.draws.startMonitoring(e.participation.id,{displayAtom:e.displayAtom,drawTime:i,phase:"settling"===t?.phase?"settling":"participating"},e.onUpdate)}stopMonitoring(){this.activeMonitorId&&(this.deps.draws.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async enter(e){await this.deps.runSerializedOperation(async()=>{await this.enterWithinOperation(e)})}async enterWithinOperation(e){const i=this.currentDrawSequence("enterable","No open draw to enter"),s=i.distribution,o=n(s.details),a=e?.paymentInput;if("pre_auth"===o.mode&&!a)throw t.validationError("Payment input is required for pre-auth distributions");const d=e?.selection?await this.deps.draws.enter(s.id,void 0,void 0,a,e.selection):a?await this.deps.draws.enter(s.id,void 0,void 0,a):await this.deps.draws.enter(s.id,void 0);if(r(d))return this.deps.commit({...i,pendingAction:d.nextAction}),void this.deps.emitEvent("sequence_change","info","user","Entry awaiting verification",{distributionId:s.id,processor:d.nextAction.processor});const c=function(e){if("ENTERED"===e.status)return null;if("COMPLETED"===e.status)return{type:"ended",outcome:{type:"completed"}};if("WON"===e.status)return"admissionGrant"in e?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null;if("LEFT"===e.status)return{type:"ended",outcome:{type:"left"}};return{type:"ended",outcome:{type:"DENIED"===e.status?"denied":"lost",reason:"reason"in e?e.reason:void 0}}}(d);if(c)return void this.deps.applyMonitorUpdate(c);const p=u(d),l={...(m=s.id,{mechanism:"draw",status:"entered",id:m,distributionId:m}),...p?{selection:p}:{}};var m;this.deps.ensureDisplayAtom({id:s.id,type:"draw"}),this.deps.commit(w({distribution:s,consumer:l})),this.deps.emitEvent("sequence_change","success","user","Entered draw",{distributionId:s.id})}async leave(){await this.deps.runSerializedOperation(async()=>{await this.leaveWithinOperation()})}async leaveWithinOperation(){const e=this.currentDrawSequence("participating"),t=e.consumer.distributionId??e.consumer.id??e.distribution.id;await this.deps.draws.leave(t),this.deps.stopRuntimeMonitoring(!0),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"left"}}),this.deps.emitEvent("sequence_change","info","user","Left participation",{type:"draw"})}currentDrawSequence(e,i="Not participating in a draw"){const n=this.deps.getCurrentSequence();if(!n||!("mechanism"in n)||"draw"!==n.mechanism)throw t.validationError(i);if("enterable"===e&&"ended"===n.phase&&l(n))return{phase:"enterable",mechanism:"draw",distribution:n.distribution,consumer:y(n.distribution)};if(n.phase!==e)throw t.validationError(i);return n}}function y(e){return{status:"not_entered",distributionId:e.id}}function v(e,t,i,n){return{phase:"ended",mechanism:"draw",distribution:e,consumer:t,outcome:{...i,distributionType:i.distributionType??e.type,distributionId:i.distributionId??e.id,at:i.at??/* @__PURE__ */(new Date).toISOString()},...n?{checkout:n}:{}}}export{m as createDrawDriver,w as projectDrawSequence,b as withDrawTerminalConsumer};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createError as t,isFanfareError as e}from"../core/errors.js";import{getLogger as i}from"../core/logger.js";import{jitteredIntervalMs as s}from"../core/utils.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as n}from"../payment/checkout-contract.js";import{getEventBus as a}from"../state/events.js";import{getSDKStore as o}from"../state/store.js";const h=2147483647;class c{constructor(t){this.logger=i(),this.events=a(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.scheduledChecks=/* @__PURE__ */new Map,this.settlingTimers=/* @__PURE__ */new Map,this.monitorRuntime=new r,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.drawTimeMs=/* @__PURE__ */new Map,this.initialCheckBufferMs=5e3,this.retryCheckDelayMs=5e3,this.retryAfterCapMs=6e4,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return o()}async get(t){try{return await this.http.get(`/draws/${t}`)}catch(e){throw this.logger.error("Failed to get draw",{drawId:t,error:e}),e}}async enter(e,i,s,r,a,o){try{const h=this.lifecycleGeneration;if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const c=this.bumpOperationGeneration(e);this.logger.info("Entering draw",{drawId:e});const d={metadata:i,selection:a};r&&(d.paymentInput=r);const l=await this.http.post(`/draws/${e}/enter`,d,s);if(n(l))return l;const u=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:i});return"ENTERED"!==l.status&&"WON"!==l.status&&"COMPLETED"!==l.status||u.events.push({type:"experience:distribution-entered",payload:{distributionType:"draw",distributionId:e,...o}}),this.canApplyAsyncResult(e,h,void 0,c)?(this.applyTransition(e,u),l):l}catch(h){throw this.logger.error("Failed to enter draw",{drawId:e,error:h}),this.events.emit("draw:error",{drawId:e,error:h}),h}}async leave(t,e){try{const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t);if(this.logger.info("Leaving draw",{drawId:t}),await this.http.post(`/draws/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,i,void 0,s))return;this.trackedParticipations.delete(t),this.stopMonitoring(t),this.events.emit("draw:left",{drawId:t})}catch(i){throw this.logger.error("Failed to leave draw",{drawId:t,error:i}),this.events.emit("draw:error",{drawId:t,error:i}),i}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{drawId:t}),e;const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),r=this.doStatus(t,i,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,r),r}async doStatus(t,e,i,s){try{const r=await this.http.get(`/draws/${t}/status`);if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"};if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}if(!this.canApplyAsyncResult(t,e,i,s))return this.trackedParticipations.has(t)?{status:"ENTERED"}:{status:"NOT_ENTERED"};throw this.logger.error("Failed to get draw status",{drawId:t,error:r}),this.events.emit("draw:error",{drawId:t,error:r}),r}}startMonitoring(t,e,i){this.monitorRuntime.start(t,e,i),this.bumpMonitorGeneration(t);const s=e?.drawTime;s?this.drawTimeMs.set(t,new Date(s).getTime()):this.drawTimeMs.delete(t);const r=this.monitorRuntime.getDisplayAtom(t);r&&r.set({type:"draw",drawTime:s,entryNumber:this.trackedParticipations.get(t)?.entryNumber,selectionAvailability:this.trackedParticipations.get(t)?.selectionAvailability}),s&&"settling"!==e?.phase?this.scheduleSettlingTransition(t,s):this.cancelSettlingTransition(t),"settling"===e?.phase?this.scheduleStatusCheck(t,0):s&&this.scheduleInitialStatusCheck(t,s)}stopMonitoring(t){this.bumpMonitorGeneration(t),this.cancelScheduledCheck(t),this.cancelSettlingTransition(t),this.drawTimeMs.delete(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.scheduledChecks.has(t)||this.settlingTimers.has(t)}buildTransition(t,e,{source:i,previousParticipation:s,enteredAt:r,metadata:n}){switch(e.status){case"ENTERED":return{selectionAvailability:e.selectionAvailability,trackedParticipation:{drawId:t,enteredAt:s?.enteredAt??r??/* @__PURE__ */(new Date).toISOString(),entryNumber:s?.entryNumber,selectionAvailability:e.selectionAvailability,metadata:s?.metadata??n},events:"enter"===i?[{type:"draw:entered",payload:{drawId:t}}]:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:"status"===i};case"WON":return{trackedParticipation:null,events:[{type:"draw:won",payload:{drawId:t,result:{won:!0,drawTime:e.wonAt}}}],monitorUpdate:"status"===i?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"DENIED":return{trackedParticipation:null,events:[{type:"draw:expired",payload:{drawId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"NOT_ENTERED":return{trackedParticipation:null,events:s?[{type:"draw:lost",payload:{drawId:t,result:this.buildLostResult()}}]:[],monitorUpdate:"status"===i?{type:"ended",outcome:s?{type:"lost"}:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1}}}applyTransition(t,e){const i=this.monitorRuntime.getDisplayAtom(t),s=i?.get();i&&"draw"===s?.type&&void 0!==e.selectionAvailability&&i.set({...s,selectionAvailability:e.selectionAvailability}),e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);for(const r of e.events)this.events.emit(r.type,r.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring?this.stopMonitoring(t):e.retryStatusCheck&&this.monitorRuntime.has(t)&&this.scheduleRetryStatusCheck(t)}scheduleInitialStatusCheck(t,e){const i=new Date(e).getTime(),s=Date.now(),r=i+this.initialCheckBufferMs,n=r<=s?s:r+Math.round(2e3*Math.random());this.scheduleStatusCheckAt(t,n)}scheduleSettlingTransition(t,e){const i=new Date(e).getTime(),s=Math.max(0,i-Date.now());this.cancelSettlingTransition(t);const r=this.monitorGenerations.get(t),n=setTimeout(()=>{this.settlingTimers.delete(t),void 0!==r&&this.monitorGenerations.get(t)===r&&(Date.now()<i?this.scheduleSettlingTransition(t,e):this.monitorRuntime.notify(t,{type:"settling",at:e},!1))},Math.min(s,h));this.settlingTimers.set(t,n)}cancelSettlingTransition(t){const e=this.settlingTimers.get(t);e&&clearTimeout(e),this.settlingTimers.delete(t)}scheduleRetryStatusCheck(t,i){const r=(e(i)&&"number"==typeof i.retryAfter&&Number.isFinite(i.retryAfter)?Math.min(1e3*i.retryAfter,this.retryAfterCapMs):void 0)??s(this.retryCheckDelayMs);this.scheduleStatusCheck(t,this.isInSettlingWindow(t)?Math.min(r,this.retryCheckDelayMs):r)}isInSettlingWindow(t){const e=this.drawTimeMs.get(t);return void 0!==e&&Date.now()>=e}scheduleStatusCheck(t,i){this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=this.getOperationGeneration(t),n=setTimeout(async()=>{this.scheduledChecks.delete(t);try{if(void 0===s)return;await this.doStatus(t,this.lifecycleGeneration,s,r)}catch(i){if(!this.canApplyAsyncResult(t,this.lifecycleGeneration,s,r))return;if(e(i)&&i.retryable&&this.monitorRuntime.has(t))return void this.scheduleRetryStatusCheck(t,i);this.stopMonitoring(t)}},i);this.scheduledChecks.set(t,n)}scheduleStatusCheckAt(t,e){const i=Math.max(0,e-Date.now());if(i<=h)return void this.scheduleStatusCheck(t,i);this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=setTimeout(()=>{this.scheduledChecks.delete(t),void 0!==s&&this.monitorGenerations.get(t)===s&&this.scheduleStatusCheckAt(t,e)},h);this.scheduledChecks.set(t,r)}cancelScheduledCheck(t){const e=this.scheduledChecks.get(t);e&&(clearTimeout(e),this.scheduledChecks.delete(t))}buildLostResult(){return{won:!1,drawTime:/* @__PURE__ */(new Date).toISOString()}}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.scheduledChecks.forEach(t=>clearTimeout(t)),this.scheduledChecks.clear(),this.settlingTimers.forEach(t=>clearTimeout(t)),this.settlingTimers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.drawTimeMs.clear(),this.monitorRuntime.clear()}}export{c as DrawManagementModule};
|
|
1
|
+
import{createError as t,isFanfareError as e}from"../core/errors.js";import{getLogger as i}from"../core/logger.js";import{jitteredIntervalMs as s}from"../core/utils.js";import{DistributionMonitorRuntime as r}from"../experiences/distribution-monitor.runtime.js";import{isEntryActionRequired as n}from"../payment/checkout-contract.js";import{getEventBus as a}from"../state/events.js";import{getSDKStore as o}from"../state/store.js";const h=2147483647;class c{constructor(t){this.logger=i(),this.events=a(),this.trackedParticipations=/* @__PURE__ */new Map,this.inFlightRequests=/* @__PURE__ */new Map,this.scheduledChecks=/* @__PURE__ */new Map,this.settlingTimers=/* @__PURE__ */new Map,this.monitorRuntime=new r,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.drawTimeMs=/* @__PURE__ */new Map,this.initialCheckBufferMs=5e3,this.retryCheckDelayMs=5e3,this.retryAfterCapMs=6e4,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return o()}async get(t){try{return await this.http.get(`/draws/${t}`)}catch(e){throw this.logger.error("Failed to get draw",{drawId:t,error:e}),e}}async enter(e,i,s,r,a,o){try{const h=this.lifecycleGeneration;if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const c=this.bumpOperationGeneration(e);this.logger.info("Entering draw",{drawId:e});const d={metadata:i,selection:a};r&&(d.paymentInput=r);const l=await this.http.post(`/draws/${e}/enter`,d,s);if(n(l))return l;const u=this.buildTransition(e,l,{source:"enter",enteredAt:/* @__PURE__ */(new Date).toISOString(),metadata:i});return"ENTERED"!==l.status&&"WON"!==l.status&&"COMPLETED"!==l.status||u.events.push({type:"experience:distribution-entered",payload:{distributionType:"draw",distributionId:e,...o}}),this.canApplyAsyncResult(e,h,void 0,c)?(this.applyTransition(e,u),l):l}catch(h){throw this.logger.error("Failed to enter draw",{drawId:e,error:h}),this.events.emit("draw:error",{drawId:e,error:h}),h}}async leave(t,e){try{const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t);if(this.logger.info("Leaving draw",{drawId:t}),await this.http.post(`/draws/${t}/leave`,void 0,e),!this.canApplyAsyncResult(t,i,void 0,s))return;this.trackedParticipations.delete(t),this.stopMonitoring(t),this.events.emit("draw:left",{drawId:t})}catch(i){throw this.logger.error("Failed to leave draw",{drawId:t,error:i}),this.events.emit("draw:error",{drawId:t,error:i}),i}}async status(t){const e=this.inFlightRequests.get(t);if(e)return this.logger.debug("Returning existing in-flight request",{drawId:t}),e;const i=this.lifecycleGeneration,s=this.bumpOperationGeneration(t),r=this.doStatus(t,i,void 0,s).finally(()=>{this.inFlightRequests.delete(t)});return this.inFlightRequests.set(t,r),r}async doStatus(t,e,i,s){try{const r=await this.http.get(`/draws/${t}/status`);if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"};if(!this.canApplyAsyncResult(t,e,i,s))return r;const n=this.buildTransition(t,r,{source:"status",previousParticipation:this.trackedParticipations.get(t)});return this.applyTransition(t,n),this.events.emit("draw:status-updated",{drawId:t,status:r.status}),r}if(!this.canApplyAsyncResult(t,e,i,s))return this.trackedParticipations.has(t)?{status:"ENTERED"}:{status:"NOT_ENTERED"};throw this.logger.error("Failed to get draw status",{drawId:t,error:r}),this.events.emit("draw:error",{drawId:t,error:r}),r}}startMonitoring(t,e,i){this.monitorRuntime.start(t,e,i),this.bumpMonitorGeneration(t);const s=e?.drawTime;s?this.drawTimeMs.set(t,new Date(s).getTime()):this.drawTimeMs.delete(t);const r=this.monitorRuntime.getDisplayAtom(t);r&&r.set({type:"draw",drawTime:s,entryNumber:this.trackedParticipations.get(t)?.entryNumber,selectionAvailability:this.trackedParticipations.get(t)?.selectionAvailability}),s&&"settling"!==e?.phase?this.scheduleSettlingTransition(t,s):this.cancelSettlingTransition(t),"settling"===e?.phase?this.scheduleStatusCheck(t,0):s&&this.scheduleInitialStatusCheck(t,s)}stopMonitoring(t){this.bumpMonitorGeneration(t),this.cancelScheduledCheck(t),this.cancelSettlingTransition(t),this.drawTimeMs.delete(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)||this.scheduledChecks.has(t)||this.settlingTimers.has(t)}buildTransition(t,e,{source:i,previousParticipation:s,enteredAt:r,metadata:n}){switch(e.status){case"ENTERED":return{selectionAvailability:e.selectionAvailability,trackedParticipation:{drawId:t,enteredAt:s?.enteredAt??r??/* @__PURE__ */(new Date).toISOString(),entryNumber:s?.entryNumber,selectionAvailability:e.selectionAvailability,metadata:s?.metadata??n},events:"enter"===i?[{type:"draw:entered",payload:{drawId:t}}]:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:"status"===i};case"WON":return{trackedParticipation:null,events:[{type:"draw:won",payload:{drawId:t,result:{won:!0,drawTime:e.wonAt}}}],monitorUpdate:"status"===i?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"COMPLETED":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"DENIED":return{trackedParticipation:null,events:[{type:"draw:expired",payload:{drawId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"LEFT":return{trackedParticipation:null,events:[],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};case"NOT_ENTERED":return{trackedParticipation:null,events:s?[{type:"draw:lost",payload:{drawId:t,result:this.buildLostResult()}}]:[],monitorUpdate:"status"===i?{type:"ended",outcome:s?{type:"lost"}:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"status"===i,retryStatusCheck:!1};default:return{trackedParticipation:s??null,events:[],monitorUpdate:null,stopMonitoring:!1,retryStatusCheck:!0}}}applyTransition(t,e){const i=this.monitorRuntime.getDisplayAtom(t),s=i?.get();i&&"draw"===s?.type&&void 0!==e.selectionAvailability&&i.set({...s,selectionAvailability:e.selectionAvailability}),e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);for(const r of e.events)this.events.emit(r.type,r.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring?this.stopMonitoring(t):e.retryStatusCheck&&this.monitorRuntime.has(t)&&this.scheduleRetryStatusCheck(t)}scheduleInitialStatusCheck(t,e){const i=new Date(e).getTime(),s=Date.now(),r=i+this.initialCheckBufferMs,n=r<=s?s:r+Math.round(2e3*Math.random());this.scheduleStatusCheckAt(t,n)}scheduleSettlingTransition(t,e){const i=new Date(e).getTime(),s=Math.max(0,i-Date.now());this.cancelSettlingTransition(t);const r=this.monitorGenerations.get(t),n=setTimeout(()=>{this.settlingTimers.delete(t),void 0!==r&&this.monitorGenerations.get(t)===r&&(Date.now()<i?this.scheduleSettlingTransition(t,e):this.monitorRuntime.notify(t,{type:"settling",at:e},!1))},Math.min(s,h));this.settlingTimers.set(t,n)}cancelSettlingTransition(t){const e=this.settlingTimers.get(t);e&&clearTimeout(e),this.settlingTimers.delete(t)}scheduleRetryStatusCheck(t,i){const r=(e(i)&&"number"==typeof i.retryAfter&&Number.isFinite(i.retryAfter)?Math.min(1e3*i.retryAfter,this.retryAfterCapMs):void 0)??s(this.retryCheckDelayMs);this.scheduleStatusCheck(t,this.isInSettlingWindow(t)?Math.min(r,this.retryCheckDelayMs):r)}isInSettlingWindow(t){const e=this.drawTimeMs.get(t);return void 0!==e&&Date.now()>=e}scheduleStatusCheck(t,i){this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=this.getOperationGeneration(t),n=setTimeout(async()=>{this.scheduledChecks.delete(t);try{if(void 0===s)return;await this.doStatus(t,this.lifecycleGeneration,s,r)}catch(i){if(!this.canApplyAsyncResult(t,this.lifecycleGeneration,s,r))return;if(e(i)&&i.retryable&&this.monitorRuntime.has(t))return void this.scheduleRetryStatusCheck(t,i);this.stopMonitoring(t)}},i);this.scheduledChecks.set(t,n)}scheduleStatusCheckAt(t,e){const i=Math.max(0,e-Date.now());if(i<=h)return void this.scheduleStatusCheck(t,i);this.cancelScheduledCheck(t);const s=this.monitorGenerations.get(t),r=setTimeout(()=>{this.scheduledChecks.delete(t),void 0!==s&&this.monitorGenerations.get(t)===s&&this.scheduleStatusCheckAt(t,e)},h);this.scheduledChecks.set(t,r)}cancelScheduledCheck(t){const e=this.scheduledChecks.get(t);e&&(clearTimeout(e),this.scheduledChecks.delete(t))}buildLostResult(){return{won:!1,drawTime:/* @__PURE__ */(new Date).toISOString()}}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.scheduledChecks.forEach(t=>clearTimeout(t)),this.scheduledChecks.clear(),this.settlingTimers.forEach(t=>clearTimeout(t)),this.settlingTimers.clear(),this.inFlightRequests.clear(),this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.drawTimeMs.clear(),this.monitorRuntime.clear()}}export{c as DrawManagementModule};
|
package/dist/draws/types.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface DrawResult {
|
|
|
44
44
|
/**
|
|
45
45
|
* Draw consumer states (from backend)
|
|
46
46
|
*/
|
|
47
|
-
export type DrawConsumerState = DrawNotEnteredState | DrawEnteredState | DrawWonState | DrawCompletedState | DrawDeniedState;
|
|
47
|
+
export type DrawConsumerState = DrawNotEnteredState | DrawEnteredState | DrawWonState | DrawCompletedState | DrawDeniedState | DrawLeftState;
|
|
48
48
|
export interface DrawNotEnteredState {
|
|
49
49
|
status: "NOT_ENTERED";
|
|
50
50
|
}
|
|
@@ -70,6 +70,11 @@ export interface DrawCompletedState {
|
|
|
70
70
|
fingerprint?: string;
|
|
71
71
|
expiresAt?: string;
|
|
72
72
|
}
|
|
73
|
+
/** A voluntary exit. Nothing refused the consumer, so there is no reason to carry. */
|
|
74
|
+
export interface DrawLeftState {
|
|
75
|
+
status: "LEFT";
|
|
76
|
+
leftAt: string;
|
|
77
|
+
}
|
|
73
78
|
export interface DrawDeniedState {
|
|
74
79
|
status: "DENIED";
|
|
75
80
|
reason: string;
|
package/dist/state/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{persist as e,createJSONStorage as i}from"zustand/middleware";import{createStore as t}from"zustand/vanilla";import{isBrowser as n,isStorageAvailable as s}from"../core/utils.js";import{getEventBus as a}from"./events.js";function o(e){const{pendingAction:i,...t}=e;return t}function r(e){const i="routed"===e.journeyStage&&"pendingAction"in e.sequence?{...e,sequence:o(e.sequence)}:e,{degraded:t,...n}=i;return{...n,schemaVersion:5}}const c=["session","activeJourneys","versionMap"],p=["session"];function l(e){if(!e||"object"!=typeof e)return{};const i=e,t={};for(const n of p)n in i&&(t[n]=i[n]);return t}function b(e){if(!e||"object"!=typeof e)return{};const i=e,t={};for(const n of c)n in i&&(t[n]="versionMap"===n?l(i[n]):i[n]);return t}const y=t()(e((e,i)=>({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{},setSession:t=>{e(e=>({session:t,versionMap:{...e.versionMap,session:(e.versionMap.session||0)+1}})),t&&a().emit("auth:authenticated",{session:t,isNew:!i().session})},updateSessionExpiry:(i,t)=>{e(e=>e.session?{session:{...e.session,expiresAt:i,...t?{identityExpiresAt:t}:{}},versionMap:{...e.versionMap,session:(e.versionMap.session||0)+1}}:{})},setRefreshToken:i=>{e(e=>({refreshToken:i,versionMap:{...e.versionMap,refreshToken:(e.versionMap.refreshToken||0)+1}}))},setBeaconToken:i=>{e({beaconToken:i})},addActiveExperience:(i,t)=>{e(e=>({activeExperiences:{...e.activeExperiences,[i]:t},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}))},removeActiveExperience:i=>{e(e=>{const{[i]:t,...n}=e.activeExperiences;return{activeExperiences:n,versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}})},updateExperienceSession:(i,t)=>{e(e=>{const n=e.activeExperiences[i];return n?{activeExperiences:{...e.activeExperiences,[i]:{...n,...t}},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}:e})},clearActiveExperiences:()=>{e(e=>({activeExperiences:{},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}))},setJourneyState:(i,t)=>{const n=r(t);e(e=>({activeJourneys:{...e.activeJourneys,[i]:n}}))},removeJourney:i=>{e(e=>{const{[i]:t,...n}=e.activeJourneys;return{activeJourneys:n}})},clearJourneys:()=>{e(()=>({activeJourneys:{}}))},upsertCapabilityToken:(i,t)=>{e(e=>({capabilityTokens:{...e.capabilityTokens,[i]:t},versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1}}))},bindDistribution:(i,t)=>{e(e=>({distributionCapabilityIndex:{...e.distributionCapabilityIndex,[i]:t},versionMap:{...e.versionMap,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1}}))},bindWaitlist:(i,t)=>{e(e=>({waitlistCapabilityIndex:{...e.waitlistCapabilityIndex,[i]:t},versionMap:{...e.versionMap,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}))},clearCapabilityContext:(i,t)=>{e(e=>{const n=`${i}:${t}`,{[n]:s,...a}=e.capabilityTokens,o={};for(const[i,t]of Object.entries(e.distributionCapabilityIndex))t.sequenceKey!==n&&(o[i]=t);const r={};for(const[i,t]of Object.entries(e.waitlistCapabilityIndex))t.sequenceKey!==n&&(r[i]=t);return{capabilityTokens:a,distributionCapabilityIndex:o,waitlistCapabilityIndex:r,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearExperienceCapabilityContext:i=>{e(e=>{const t={};for(const[a,o]of Object.entries(e.capabilityTokens))o.experienceId!==i&&(t[a]=o);const n={};for(const[a,o]of Object.entries(e.distributionCapabilityIndex))o.experienceId!==i&&(n[a]=o);const s={};for(const[a,o]of Object.entries(e.waitlistCapabilityIndex))o.experienceId!==i&&(s[a]=o);return{capabilityTokens:t,distributionCapabilityIndex:n,waitlistCapabilityIndex:s,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearExpiredCapabilityTokens:i=>{e(e=>{const t=/* @__PURE__ */new Set,n={};for(const[o,r]of Object.entries(e.capabilityTokens))r.expiresAtMs&&r.expiresAtMs<=i?t.add(o):n[o]=r;if(0===t.size)return e;const s={};for(const[i,o]of Object.entries(e.distributionCapabilityIndex))t.has(o.sequenceKey)||(s[i]=o);const a={};for(const[i,o]of Object.entries(e.waitlistCapabilityIndex))t.has(o.sequenceKey)||(a[i]=o);return{capabilityTokens:n,distributionCapabilityIndex:s,waitlistCapabilityIndex:a,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearAll:()=>{e({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{}}),a().emit("state:cleared",{})}}),{name:"fanfare-sdk-state",storage:i(()=>(()=>{if(n()&&s("localStorage"))return window.localStorage;const e={};return{getItem:i=>Object.prototype.hasOwnProperty.call(e,i)?e[i]:null,setItem:(i,t)=>{e[i]=t},removeItem:i=>{delete e[i]}}})()),partialize:e=>b(e),version:3,merge:(e,i)=>({...i,...b(e)}),onRehydrateStorage:()=>e=>{if(e){const i=e.activeJourneys??{};a().emit("state:restored",{experiences:Object.keys(i)})}}}));function d(){y.setState({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{}}),v=null}let v=null;const u=()=>v||(v=new Proxy({},{get:(e,i)=>Reflect.has(e,i)?e[i]:y.getState()[i],has:(e,i)=>Reflect.has(e,i)||i in y.getState(),ownKeys:e=>{const i=new Set(Reflect.ownKeys(y.getState()));for(const t of Reflect.ownKeys(e))i.add(t);return Array.from(i)},getOwnPropertyDescriptor:(e,i)=>{const t=Object.getOwnPropertyDescriptor(e,i);if(t)return t;const n=Object.getOwnPropertyDescriptor(y.getState(),i);return n?{...n,configurable:!0}:void 0}}),v);export{u as getSDKStore,d as resetSDKStore,r as toPersistedSnapshot,y as useSDKStore};
|
|
1
|
+
import{persist as e,createJSONStorage as i}from"zustand/middleware";import{createStore as t}from"zustand/vanilla";import{isBrowser as n,isStorageAvailable as s}from"../core/utils.js";import{getEventBus as a}from"./events.js";function o(e){const{pendingAction:i,...t}=e;return t}function r(e){const i="routed"===e.journeyStage&&"pendingAction"in e.sequence?{...e,sequence:o(e.sequence)}:e,{degraded:t,...n}=i;return{...n,schemaVersion:5}}const c=["session","activeJourneys","versionMap"],p=["session"];function l(e){if(!e||"object"!=typeof e)return{};const i=e,t={};for(const n of p)n in i&&(t[n]=i[n]);return t}function b(e){if(!e||"object"!=typeof e)return{};const i=e,t={};for(const n of c)n in i&&(t[n]="versionMap"===n?l(i[n]):i[n]);return t}const y=t()(e((e,i)=>({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{},setSession:t=>{e(e=>({session:t,versionMap:{...e.versionMap,session:(e.versionMap.session||0)+1}})),t&&a().emit("auth:authenticated",{session:t,isNew:!i().session})},updateSessionExpiry:(i,t)=>{e(e=>e.session?{session:{...e.session,expiresAt:i,...t?{identityExpiresAt:t}:{}},versionMap:{...e.versionMap,session:(e.versionMap.session||0)+1}}:{})},setRefreshToken:i=>{e(e=>({refreshToken:i,versionMap:{...e.versionMap,refreshToken:(e.versionMap.refreshToken||0)+1}}))},setBeaconToken:i=>{e({beaconToken:i})},addActiveExperience:(i,t)=>{e(e=>({activeExperiences:{...e.activeExperiences,[i]:t},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}))},removeActiveExperience:i=>{e(e=>{const{[i]:t,...n}=e.activeExperiences;return{activeExperiences:n,versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}})},updateExperienceSession:(i,t)=>{e(e=>{const n=e.activeExperiences[i];return n?{activeExperiences:{...e.activeExperiences,[i]:{...n,...t}},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}:(console.warn(`Cannot update non-existent experience: ${i}`),e)})},clearActiveExperiences:()=>{e(e=>({activeExperiences:{},versionMap:{...e.versionMap,activeExperiences:(e.versionMap.activeExperiences||0)+1}}))},setJourneyState:(i,t)=>{const n=r(t);e(e=>({activeJourneys:{...e.activeJourneys,[i]:n}}))},removeJourney:i=>{e(e=>{const{[i]:t,...n}=e.activeJourneys;return{activeJourneys:n}})},clearJourneys:()=>{e(()=>({activeJourneys:{}}))},upsertCapabilityToken:(i,t)=>{e(e=>({capabilityTokens:{...e.capabilityTokens,[i]:t},versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1}}))},bindDistribution:(i,t)=>{e(e=>({distributionCapabilityIndex:{...e.distributionCapabilityIndex,[i]:t},versionMap:{...e.versionMap,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1}}))},bindWaitlist:(i,t)=>{e(e=>({waitlistCapabilityIndex:{...e.waitlistCapabilityIndex,[i]:t},versionMap:{...e.versionMap,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}))},clearCapabilityContext:(i,t)=>{e(e=>{const n=`${i}:${t}`,{[n]:s,...a}=e.capabilityTokens,o={};for(const[i,t]of Object.entries(e.distributionCapabilityIndex))t.sequenceKey!==n&&(o[i]=t);const r={};for(const[i,t]of Object.entries(e.waitlistCapabilityIndex))t.sequenceKey!==n&&(r[i]=t);return{capabilityTokens:a,distributionCapabilityIndex:o,waitlistCapabilityIndex:r,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearExperienceCapabilityContext:i=>{e(e=>{const t={};for(const[a,o]of Object.entries(e.capabilityTokens))o.experienceId!==i&&(t[a]=o);const n={};for(const[a,o]of Object.entries(e.distributionCapabilityIndex))o.experienceId!==i&&(n[a]=o);const s={};for(const[a,o]of Object.entries(e.waitlistCapabilityIndex))o.experienceId!==i&&(s[a]=o);return{capabilityTokens:t,distributionCapabilityIndex:n,waitlistCapabilityIndex:s,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearExpiredCapabilityTokens:i=>{e(e=>{const t=/* @__PURE__ */new Set,n={};for(const[o,r]of Object.entries(e.capabilityTokens))r.expiresAtMs&&r.expiresAtMs<=i?t.add(o):n[o]=r;if(0===t.size)return e;const s={};for(const[i,o]of Object.entries(e.distributionCapabilityIndex))t.has(o.sequenceKey)||(s[i]=o);const a={};for(const[i,o]of Object.entries(e.waitlistCapabilityIndex))t.has(o.sequenceKey)||(a[i]=o);return{capabilityTokens:n,distributionCapabilityIndex:s,waitlistCapabilityIndex:a,versionMap:{...e.versionMap,capabilityTokens:(e.versionMap.capabilityTokens||0)+1,distributionCapabilityIndex:(e.versionMap.distributionCapabilityIndex||0)+1,waitlistCapabilityIndex:(e.versionMap.waitlistCapabilityIndex||0)+1}}})},clearAll:()=>{e({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{}}),a().emit("state:cleared",{})}}),{name:"fanfare-sdk-state",storage:i(()=>(()=>{if(n()&&s("localStorage"))return window.localStorage;const e={};return{getItem:i=>Object.prototype.hasOwnProperty.call(e,i)?e[i]:null,setItem:(i,t)=>{e[i]=t},removeItem:i=>{delete e[i]}}})()),partialize:e=>b(e),version:3,merge:(e,i)=>({...i,...b(e)}),onRehydrateStorage:()=>e=>{if(e){const i=e.activeJourneys??{};a().emit("state:restored",{experiences:Object.keys(i)})}}}));function d(){y.setState({session:null,refreshToken:null,beaconToken:null,activeExperiences:{},activeJourneys:{},capabilityTokens:{},distributionCapabilityIndex:{},waitlistCapabilityIndex:{},versionMap:{}}),v=null}let v=null;const u=()=>v||(v=new Proxy({},{get:(e,i)=>Reflect.has(e,i)?e[i]:y.getState()[i],has:(e,i)=>Reflect.has(e,i)||i in y.getState(),ownKeys:e=>{const i=new Set(Reflect.ownKeys(y.getState()));for(const t of Reflect.ownKeys(e))i.add(t);return Array.from(i)},getOwnPropertyDescriptor:(e,i)=>{const t=Object.getOwnPropertyDescriptor(e,i);if(t)return t;const n=Object.getOwnPropertyDescriptor(y.getState(),i);return n?{...n,configurable:!0}:void 0}}),v);export{u as getSDKStore,d as resetSDKStore,r as toPersistedSnapshot,y as useSDKStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getLogger as e}from"../core/logger.js";import{isBrowser as a}from"../core/utils.js";function
|
|
1
|
+
import{getLogger as e}from"../core/logger.js";import{isBrowser as a}from"../core/utils.js";import{messageType as n}from"./protocol.js";function s(){if(!a())return!1;try{return"undefined"!=typeof window&&"BroadcastChannel"in window&&"function"==typeof window.BroadcastChannel}catch{return!1}}class t{constructor(a){if(this.channelName=a,this.channel=null,this.logger=e(),s())try{this.channel=new BroadcastChannel(a),this.logger.debug("BroadcastChannelTransport initialized",{channelName:a})}catch(n){this.logger.warn("Failed to create BroadcastChannel",{error:n,channelName:a}),this.channel=null}else this.logger.debug("BroadcastChannel not supported")}send(e){if(this.channel)try{this.channel.postMessage(e),this.logger.debug("Message sent via BroadcastChannel",{message:e})}catch(a){this.logger.warn("Failed to send message via BroadcastChannel",{error:a,type:n(e)})}else this.logger.debug("BroadcastChannel not available, message not sent",{message:e})}onMessage(e){return this.channel?(this.messageHandler=e,this.channel.onmessage=e=>{this.logger.debug("Message received via BroadcastChannel",{data:e.data}),this.messageHandler&&this.messageHandler(e)},()=>{this.channel&&(this.channel.onmessage=null),this.messageHandler=void 0,this.logger.debug("BroadcastChannel listener removed")}):(this.logger.debug("BroadcastChannel not available, listener not added"),()=>{})}close(){if(this.channel){try{this.channel.close(),this.logger.debug("BroadcastChannel closed",{channelName:this.channelName})}catch(e){this.logger.warn("Error closing BroadcastChannel",{error:e})}this.channel=null}this.messageHandler=void 0}isAvailable(){return null!==this.channel}}export{t as BroadcastChannelTransport,s as isBroadcastChannelSupported};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getLogger as e}from"../core/logger.js";import{isStorageAvailable as t}from"../core/utils.js";function
|
|
1
|
+
import{getLogger as e}from"../core/logger.js";import{isStorageAvailable as t}from"../core/utils.js";import{messageType as a}from"./protocol.js";function r(){return t("localStorage")}class s{constructor(t){this.lastProcessedCounter=0,this.logger=e(),this.channel=t,this.tabId=this.generateTabId(),r()?(this.startCleanupInterval(),this.initializeCounter(),this.logger.debug("LocalStorageTransport initialized",{channel:t,tabId:this.tabId})):this.logger.warn("localStorage not supported")}generateTabId(){return`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}getCounterKey(){return`fanfare-sdk:${this.channel}:counter`}getMessageKey(e){return`fanfare-sdk:${this.channel}:msg:${e}`}initializeCounter(){try{const e=localStorage.getItem(this.getCounterKey());e?this.lastProcessedCounter=parseInt(e,10):localStorage.setItem(this.getCounterKey(),"0")}catch(e){this.logger.warn("Failed to initialize counter",{error:e})}}getNextCounter(){try{const e=this.getCounterKey(),t=parseInt(localStorage.getItem(e)||"0",10)+1;return localStorage.setItem(e,t.toString()),t}catch(e){return this.logger.warn("Failed to get next counter",{error:e}),Date.now()}}startCleanupInterval(){"undefined"!=typeof window&&window.setInterval?this.cleanupInterval=window.setInterval(()=>{this.cleanupOldMessages()},2e3):"undefined"!=typeof setInterval&&(this.cleanupInterval=setInterval(()=>{this.cleanupOldMessages()},2e3))}cleanupOldMessages(){if(r())try{const e=Date.now(),t=5e3,a=[];for(let r=0;r<localStorage.length;r++){const s=localStorage.key(r);if(s&&s.startsWith(`fanfare-sdk:${this.channel}:msg:`))try{const r=localStorage.getItem(s);if(!r)continue;e-JSON.parse(r).timestamp>t&&a.push(s)}catch{a.push(s)}}a.forEach(e=>{try{localStorage.removeItem(e),this.logger.debug("Removed old message",{key:e})}catch(t){this.logger.warn("Failed to remove old message",{key:e,error:t})}})}catch(e){this.logger.warn("Cleanup failed",{error:e})}}send(e){if(!r())return void this.logger.debug("localStorage not available, message not sent");let t;try{const a=this.getNextCounter(),r={tabId:this.tabId,timestamp:Date.now(),data:e},s=this.getMessageKey(a),o=JSON.stringify(r);t=o.length,localStorage.setItem(s,o),this.logger.debug("Message sent via localStorage",{key:s,bytes:t})}catch(s){if(this.logger.warn("Failed to send message via localStorage",{type:a(e),bytes:t,error:s}),s instanceof Error&&"QuotaExceededError"===s.name){this.cleanupOldMessages();try{const t=this.getNextCounter(),a={tabId:this.tabId,timestamp:Date.now(),data:e},r=this.getMessageKey(t);localStorage.setItem(r,JSON.stringify(a))}catch{}}}}onMessage(e){return r()?(this.messageHandler=e,this.storageHandler=e=>{if(e.key&&e.newValue&&e.key.startsWith(`fanfare-sdk:${this.channel}:msg:`))try{const t=JSON.parse(e.newValue);if(t.tabId===this.tabId)return;const a=e.key.match(/msg:(\d+)$/);if(a){const r=parseInt(a[1],10);if(r>this.lastProcessedCounter){this.lastProcessedCounter=r,this.logger.debug("Message received via localStorage",{key:e.key,data:t.data,fromTab:t.tabId});const a=new MessageEvent("message",{data:t.data,origin:window.location.origin});this.messageHandler&&this.messageHandler(a)}}}catch(t){this.logger.warn("Failed to process storage event",{error:t,key:e.key})}},window.addEventListener("storage",this.storageHandler),()=>{this.storageHandler&&window.removeEventListener("storage",this.storageHandler),this.messageHandler=void 0,this.storageHandler=void 0,this.logger.debug("localStorage listener removed")}):(this.logger.debug("localStorage not available, listener not added"),()=>{})}close(){this.storageHandler&&(window.removeEventListener("storage",this.storageHandler),this.storageHandler=void 0),this.messageHandler=void 0,this.cleanupInterval&&("undefined"!=typeof window&&window.clearInterval?window.clearInterval(this.cleanupInterval):"undefined"!=typeof clearInterval&&clearInterval(this.cleanupInterval),this.cleanupInterval=void 0),this.cleanupOldMessages(),this.logger.debug("LocalStorageTransport closed",{channel:this.channel})}isAvailable(){return r()}}export{s as LocalStorageTransport,r as isLocalStorageSupported};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"valibot";import{getLogger as
|
|
1
|
+
import*as e from"valibot";import{getLogger as t}from"../core/logger.js";import{generateId as s}from"../core/utils.js";import{DEFAULT_CHANNEL_NAME as a,SyncMessageSchema as r,PROTOCOL_VERSION as o,messageType as i}from"./protocol.js";import{InterTabTransport as n}from"./transport.js";class d{constructor(e=a){this.logger=t(),this.transport=new n(e),this.tabId=s(),this.logger.debug("ProtocolTransport initialized",{tabId:this.tabId,channelName:e})}sendMessage(t){const a={...t,id:s(),timestamp:Date.now(),source:this.tabId,version:o};try{const s=e.parse(r,a);this.transport.send(s),this.logger.debug("Protocol message sent",{type:t.type,id:a.id,source:this.tabId})}catch(n){this.logger.error("Invalid message format",{error:n,type:i(t)})}}onMessage(t){return this.transport.on(s=>{try{const a=e.parse(r,s);if(a.source===this.tabId)return void this.logger.debug("Ignoring message from self",{type:a.type,id:a.id});this.logger.debug("Protocol message received",{type:a.type,id:a.id,source:a.source}),t(a)}catch(a){this.logger.warn("Received invalid message",{error:a,type:i(s)})}})}close(){this.logger.debug("Closing ProtocolTransport",{tabId:this.tabId}),this.transport.close()}isAvailable(){return this.transport.isAvailable()}getTabId(){return this.tabId}getListenerCount(){return this.transport.getListenerCount()}sendDurableBootstrapRequest(){this.sendMessage({type:"durable:bootstrap:request",payload:{}})}sendDurableBootstrapResponse(e){this.sendMessage({type:"durable:bootstrap:response",payload:e})}sendSessionUpdate(e,t){this.sendMessage({type:"session:update",payload:{value:e,version:t}})}sendCapabilityTokensUpdate(e,t){this.sendMessage({type:"capability-tokens:update",payload:{value:e,version:t}})}sendDistributionCapabilityIndexUpdate(e,t){this.sendMessage({type:"distribution-capability-index:update",payload:{value:e,version:t}})}sendWaitlistCapabilityIndexUpdate(e,t){this.sendMessage({type:"waitlist-capability-index:update",payload:{value:e,version:t}})}sendJourneyUpsert(e,t,s){this.sendMessage({type:"journey:upsert",payload:{experienceId:e,snapshot:t,version:s}})}sendJourneyRemove(e,t){this.sendMessage({type:"journey:remove",payload:{experienceId:e,version:t}})}sendRuntimeState(e){this.sendMessage({type:"runtime:state",payload:e})}sendHeartbeat(e){this.sendMessage({type:"heartbeat",payload:{active:e}})}}export{d as ProtocolTransport};
|
package/dist/sync/protocol.js
CHANGED
|
@@ -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()})})]),
|
|
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};
|
package/dist/sync/transport.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getLogger as s}from"../core/logger.js";import{
|
|
1
|
+
import{getLogger as s}from"../core/logger.js";import{messageType as e}from"./protocol.js";import{createTransport as t}from"./transport-factory.js";class r{constructor(e="fanfare-sdk-sync"){this.transport=null,this.listeners=/* @__PURE__ */new Set,this.logger=s(),this.channelName=e,this.transport=t(e),this.transport?(this.unsubscribe=this.transport.onMessage(s=>{this.handleMessage(s.data)}),this.logger.debug("InterTabTransport initialized",{channelName:e,transportAvailable:this.transport.isAvailable()})):this.logger.debug("No transport available, inter-tab communication disabled")}send(s){if(this.transport)try{this.transport.send(s),this.logger.debug("Message sent via transport",{message:s})}catch(t){this.logger.warn("Failed to send message",{error:t,type:e(s)})}else this.logger.debug("Transport not available, message not sent",{message:s})}on(s){return this.listeners.add(s),this.logger.debug("Transport listener added",{listenerCount:this.listeners.size}),()=>{this.listeners.delete(s),this.logger.debug("Transport listener removed",{listenerCount:this.listeners.size})}}close(){if(this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0),this.transport){try{this.transport.close(),this.logger.debug("Transport closed",{channelName:this.channelName})}catch(s){this.logger.warn("Error closing transport",{error:s})}this.transport=null}this.listeners.clear(),this.logger.debug("Transport closed and listeners cleared")}isAvailable(){return null!==this.transport&&this.transport.isAvailable()}getListenerCount(){return this.listeners.size}handleMessage(s){this.logger.debug("Message received via transport",{message:s,listenerCount:this.listeners.size}),this.listeners.forEach(t=>{try{t(s)}catch(r){this.logger.error("Error in transport listener",{error:r,type:e(s)})}})}}export{r as InterTabTransport};
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.
|
|
1
|
+
const o="0.17.0";export{o as version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanfare-io/fanfare-sdk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Official Fanfare Browser SDK for queue, draw, auction, and appointment experiences",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"uuid": "^14.0.0",
|
|
116
116
|
"wretch": "^2.11.0",
|
|
117
117
|
"zustand": "^5.0.6",
|
|
118
|
-
"@fanfare-io/fanfare-sdk-contracts": "0.
|
|
118
|
+
"@fanfare-io/fanfare-sdk-contracts": "0.17.0"
|
|
119
119
|
},
|
|
120
120
|
"peerDependencies": {
|
|
121
121
|
"valibot": "^1.1.0"
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
"vite-plugin-dts": "^4.5.4",
|
|
155
155
|
"vite-tsconfig-paths": "^5.1.4",
|
|
156
156
|
"vitest": "^5.0.0",
|
|
157
|
-
"@fanfare-io/shared-models": "0.
|
|
157
|
+
"@fanfare-io/shared-models": "0.17.0"
|
|
158
158
|
},
|
|
159
159
|
"sideEffects": false,
|
|
160
160
|
"keywords": [
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
{
|
|
172
172
|
"name": "Core + Queues",
|
|
173
173
|
"path": "dist/index.js",
|
|
174
|
-
"limit": "
|
|
174
|
+
"limit": "55 KB"
|
|
175
175
|
}
|
|
176
176
|
],
|
|
177
177
|
"scripts": {
|