@fanfare-io/fanfare-sdk-core 0.1.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/LICENSE +202 -0
- package/README.md +44 -0
- package/dist/adapters/google-analytics.d.ts +12 -0
- package/dist/adapters/google-analytics.js +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/types.d.ts +9 -0
- package/dist/appointments/appointment.module.d.ts +32 -0
- package/dist/appointments/appointment.module.js +1 -0
- package/dist/appointments/index.d.ts +2 -0
- package/dist/appointments/public.d.ts +1 -0
- package/dist/appointments/public.js +1 -0
- package/dist/appointments/types.d.ts +47 -0
- package/dist/auctions/auction.module.d.ts +58 -0
- package/dist/auctions/auction.module.js +1 -0
- package/dist/auctions/index.d.ts +5 -0
- package/dist/auctions/public.d.ts +5 -0
- package/dist/auctions/public.js +1 -0
- package/dist/auctions/types.d.ts +97 -0
- package/dist/auth/auth.module.d.ts +71 -0
- package/dist/auth/auth.module.js +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/types.d.ts +112 -0
- package/dist/beacon/batching.d.ts +52 -0
- package/dist/beacon/batching.js +1 -0
- package/dist/beacon/beacon.module.d.ts +58 -0
- package/dist/beacon/beacon.module.js +1 -0
- package/dist/beacon/enrichment.d.ts +16 -0
- package/dist/beacon/enrichment.js +1 -0
- package/dist/beacon/index.d.ts +12 -0
- package/dist/beacon/public.d.ts +9 -0
- package/dist/beacon/public.js +1 -0
- package/dist/beacon/types.d.ts +103 -0
- package/dist/beacon/validation.d.ts +24 -0
- package/dist/beacon/validation.js +1 -0
- package/dist/challenges/challenge.module.d.ts +9 -0
- package/dist/challenges/challenge.module.js +1 -0
- package/dist/challenges/index.d.ts +3 -0
- package/dist/challenges/public.d.ts +9 -0
- package/dist/challenges/public.js +1 -0
- package/dist/challenges/types.d.ts +33 -0
- package/dist/config/index.d.ts +44 -0
- package/dist/config/index.js +1 -0
- package/dist/core/client.d.ts +15 -0
- package/dist/core/client.js +2 -0
- package/dist/core/errors.d.ts +175 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/http.d.ts +87 -0
- package/dist/core/http.js +1 -0
- package/dist/core/logger.d.ts +46 -0
- package/dist/core/logger.js +1 -0
- package/dist/core/money.d.ts +10 -0
- package/dist/core/money.js +1 -0
- package/dist/core/parse-response.d.ts +62 -0
- package/dist/core/parse-response.js +1 -0
- package/dist/core/utils.d.ts +67 -0
- package/dist/core/utils.js +1 -0
- package/dist/draws/draw.module.d.ts +40 -0
- package/dist/draws/draw.module.js +1 -0
- package/dist/draws/index.d.ts +5 -0
- package/dist/draws/public.d.ts +6 -0
- package/dist/draws/public.js +1 -0
- package/dist/draws/types.d.ts +90 -0
- package/dist/draws/types.js +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +1 -0
- package/dist/events.d.ts +5 -0
- package/dist/events.js +1 -0
- package/dist/experiences/distribution-monitor.runtime.d.ts +12 -0
- package/dist/experiences/distribution-monitor.runtime.js +1 -0
- package/dist/experiences/distribution-monitor.types.d.ts +62 -0
- package/dist/experiences/experience.module.d.ts +88 -0
- package/dist/experiences/experience.module.js +1 -0
- package/dist/experiences/index.d.ts +3 -0
- package/dist/experiences/index.js +1 -0
- package/dist/experiences/journey-contract.fixtures.d.ts +9 -0
- package/dist/experiences/journey-view.d.ts +22 -0
- package/dist/experiences/journey-view.js +1 -0
- package/dist/experiences/journey.d.ts +89 -0
- package/dist/experiences/journey.js +1 -0
- package/dist/experiences/journey.machine.d.ts +79 -0
- package/dist/experiences/journey.machine.js +1 -0
- package/dist/experiences/journey.types.d.ts +395 -0
- package/dist/experiences/public.d.ts +13 -0
- package/dist/experiences/public.js +1 -0
- package/dist/experiences/types.d.ts +161 -0
- package/dist/handoff/handoff.module.d.ts +184 -0
- package/dist/handoff/handoff.module.js +1 -0
- package/dist/handoff/index.d.ts +5 -0
- package/dist/handoff/index.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -0
- package/dist/internals.d.ts +11 -0
- package/dist/internals.js +1 -0
- package/dist/queues/index.d.ts +5 -0
- package/dist/queues/index.js +1 -0
- package/dist/queues/qualitative-bucket.d.ts +12 -0
- package/dist/queues/qualitative-bucket.js +1 -0
- package/dist/queues/queue.module.d.ts +42 -0
- package/dist/queues/queue.module.js +1 -0
- package/dist/queues/types.d.ts +112 -0
- package/dist/queues/types.js +1 -0
- package/dist/security/admission-proof.d.ts +15 -0
- package/dist/security/admission-proof.js +1 -0
- package/dist/state/capability-token-registry.d.ts +44 -0
- package/dist/state/capability-token-registry.js +1 -0
- package/dist/state/events.d.ts +342 -0
- package/dist/state/events.js +1 -0
- package/dist/state/store.d.ts +48 -0
- package/dist/state/store.js +1 -0
- package/dist/sync/broadcast-transport.d.ts +19 -0
- package/dist/sync/broadcast-transport.js +1 -0
- package/dist/sync/cross-tab-coordinator.d.ts +44 -0
- package/dist/sync/cross-tab-coordinator.js +1 -0
- package/dist/sync/index.d.ts +8 -0
- package/dist/sync/localstorage-transport.d.ts +32 -0
- package/dist/sync/localstorage-transport.js +1 -0
- package/dist/sync/protocol-transport.d.ts +51 -0
- package/dist/sync/protocol-transport.js +1 -0
- package/dist/sync/protocol.d.ts +254 -0
- package/dist/sync/protocol.js +1 -0
- package/dist/sync/recovery.d.ts +68 -0
- package/dist/sync/transport-factory.d.ts +16 -0
- package/dist/sync/transport-factory.js +1 -0
- package/dist/sync/transport-interface.d.ts +33 -0
- package/dist/sync/transport.d.ts +49 -0
- package/dist/sync/transport.js +1 -0
- package/dist/test-utils/harness-scenarios.d.ts +71 -0
- package/dist/test-utils/harness-scenarios.js +1 -0
- package/dist/test-utils/index.d.ts +12 -0
- package/dist/test-utils/index.js +1 -0
- package/dist/test-utils/mock-journey.d.ts +63 -0
- package/dist/test-utils/mock-journey.js +1 -0
- package/dist/test-utils/mock-sdk-vitest.d.ts +80 -0
- package/dist/test-utils/mock-sdk-vitest.js +1 -0
- package/dist/test-utils/mock-sdk.d.ts +22 -0
- package/dist/test-utils/mock-sdk.js +1 -0
- package/dist/test-utils/mock-server.d.ts +105 -0
- package/dist/test-utils/mock-server.js +1 -0
- package/dist/test-utils/sdk-factory.d.ts +3 -0
- package/dist/test-utils/sdk-factory.js +1 -0
- package/dist/test-utils/verification-scenarios.d.ts +43 -0
- package/dist/test-utils/verification-scenarios.js +1 -0
- package/dist/test-utils/verification-state.d.ts +1 -0
- package/dist/test-utils/verification-state.js +1 -0
- package/dist/theme.d.ts +8 -0
- package/dist/theme.js +1 -0
- package/dist/timed-releases/index.d.ts +6 -0
- package/dist/timed-releases/public.d.ts +6 -0
- package/dist/timed-releases/public.js +1 -0
- package/dist/timed-releases/timed-release.module.d.ts +31 -0
- package/dist/timed-releases/timed-release.module.js +1 -0
- package/dist/timed-releases/types.d.ts +70 -0
- package/dist/timed-releases/types.js +1 -0
- package/dist/types/distribution-type.d.ts +45 -0
- package/dist/types/index.d.ts +178 -0
- package/dist/utils/fingerprint.module.d.ts +27 -0
- package/dist/utils/fingerprint.module.js +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +1 -0
- package/dist/waitlists/index.d.ts +2 -0
- package/dist/waitlists/public.d.ts +6 -0
- package/dist/waitlists/public.js +1 -0
- package/dist/waitlists/types.d.ts +50 -0
- package/dist/waitlists/types.js +1 -0
- package/dist/waitlists/waitlist.module.d.ts +17 -0
- package/dist/waitlists/waitlist.module.js +1 -0
- package/package.json +205 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { JourneyHandle } from '../experiences/journey.types';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical vi.fn()-wrapped SDK object for adapter test files (react/solid).
|
|
4
|
+
*
|
|
5
|
+
* Lives in this dedicated subpath (not the main test-utils entry) so that
|
|
6
|
+
* `@fanfare-io/fanfare-sdk-core/test-utils` consumers without Vitest in their
|
|
7
|
+
* runtime (e.g. Playwright-only sdk-harness) don't take a hard dependency on
|
|
8
|
+
* vitest at import time. Import directly from
|
|
9
|
+
* `@fanfare-io/fanfare-sdk-core/test-utils/vitest`.
|
|
10
|
+
*
|
|
11
|
+
* All 4 use-experience-journey and experience-widget test files share this
|
|
12
|
+
* shape. The caller provides `journeyHandle` (from `createMockJourneyHandle`)
|
|
13
|
+
* so that `experiences.createJourney` and `journeys.get` return the same
|
|
14
|
+
* controllable handle.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { createMockJourneyHandle, makeReadySnapshot } from "@fanfare-io/fanfare-sdk-core/test-utils";
|
|
19
|
+
* import { createMockSDKObject } from "@fanfare-io/fanfare-sdk-core/test-utils/vitest";
|
|
20
|
+
*
|
|
21
|
+
* const mockJourney = createMockJourneyHandle({ initialSnapshot: makeReadySnapshot("exp_123") });
|
|
22
|
+
* const { sdk, events } = createMockSDKObject(mockJourney.handle);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function createMockSDKObject(journeyHandle: JourneyHandle): {
|
|
26
|
+
sdk: {
|
|
27
|
+
auth: {
|
|
28
|
+
check: import('vitest').Mock<() => {
|
|
29
|
+
isAuthenticated: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
guest: import('vitest').Mock<(...args: any[]) => any>;
|
|
32
|
+
requestOtp: import('vitest').Mock<(...args: any[]) => any>;
|
|
33
|
+
verifyOtp: import('vitest').Mock<(...args: any[]) => any>;
|
|
34
|
+
login: import('vitest').Mock<(...args: any[]) => any>;
|
|
35
|
+
logout: import('vitest').Mock<(...args: any[]) => any>;
|
|
36
|
+
refresh: import('vitest').Mock<(...args: any[]) => any>;
|
|
37
|
+
getSession: import('vitest').Mock<() => null>;
|
|
38
|
+
};
|
|
39
|
+
challenges: {
|
|
40
|
+
initiate: import('vitest').Mock<(...args: any[]) => any>;
|
|
41
|
+
verify: import('vitest').Mock<(...args: any[]) => any>;
|
|
42
|
+
};
|
|
43
|
+
queues: {
|
|
44
|
+
startPolling: import('vitest').Mock<(...args: any[]) => any>;
|
|
45
|
+
stopPolling: import('vitest').Mock<(...args: any[]) => any>;
|
|
46
|
+
};
|
|
47
|
+
auctions: {
|
|
48
|
+
startWatching: import('vitest').Mock<(...args: any[]) => any>;
|
|
49
|
+
stopWatching: import('vitest').Mock<(...args: any[]) => any>;
|
|
50
|
+
};
|
|
51
|
+
draws: {
|
|
52
|
+
scheduleResultCheck: import('vitest').Mock<(...args: any[]) => any>;
|
|
53
|
+
cancelScheduledCheck: import('vitest').Mock<(...args: any[]) => any>;
|
|
54
|
+
};
|
|
55
|
+
waitlists: {};
|
|
56
|
+
timedReleases: {};
|
|
57
|
+
experiences: {
|
|
58
|
+
get: import('vitest').Mock<(...args: any[]) => any>;
|
|
59
|
+
createJourney: import('vitest').Mock<(...args: any[]) => any>;
|
|
60
|
+
getMe: import('vitest').Mock<(...args: any[]) => any>;
|
|
61
|
+
destroy: import('vitest').Mock<(...args: any[]) => any>;
|
|
62
|
+
};
|
|
63
|
+
journeys: {
|
|
64
|
+
get: import('vitest').Mock<(...args: any[]) => any>;
|
|
65
|
+
list: import('vitest').Mock<(...args: any[]) => any>;
|
|
66
|
+
resumeAll: import('vitest').Mock<(...args: any[]) => any>;
|
|
67
|
+
};
|
|
68
|
+
beacon: {
|
|
69
|
+
track: import('vitest').Mock<(...args: any[]) => any>;
|
|
70
|
+
trackBatch: import('vitest').Mock<(...args: any[]) => any>;
|
|
71
|
+
flush: import('vitest').Mock<(...args: any[]) => any>;
|
|
72
|
+
};
|
|
73
|
+
on: import('vitest').Mock<(event: string, handler: (data: Record<string, unknown>) => void) => () => void>;
|
|
74
|
+
destroy: import('vitest').Mock<(...args: any[]) => any>;
|
|
75
|
+
};
|
|
76
|
+
events: {
|
|
77
|
+
on: import('vitest').Mock<(event: string, handler: (data: Record<string, unknown>) => void) => () => void>;
|
|
78
|
+
emit: (event: string, data: Record<string, unknown>) => void;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{vi as e}from"vitest";function n(n){const o={},t=e.fn((e,n)=>(o[e]||(o[e]=[]),o[e].push(n),()=>{const t=o[e].indexOf(n);t>-1&&o[e].splice(t,1)}));return{sdk:{auth:{check:e.fn(()=>({isAuthenticated:!1})),guest:e.fn().mockResolvedValue({type:"guest",consumerId:"c_1"}),requestOtp:e.fn().mockResolvedValue(void 0),verifyOtp:e.fn().mockResolvedValue({session:"verified"}),login:e.fn().mockResolvedValue(void 0),logout:e.fn().mockResolvedValue(void 0),refresh:e.fn().mockResolvedValue(void 0),getSession:e.fn(()=>null)},challenges:{initiate:e.fn().mockResolvedValue({}),verify:e.fn().mockResolvedValue(void 0)},queues:{startPolling:e.fn(),stopPolling:e.fn()},auctions:{startWatching:e.fn(),stopWatching:e.fn()},draws:{scheduleResultCheck:e.fn(),cancelScheduledCheck:e.fn()},waitlists:{},timedReleases:{},experiences:{get:e.fn().mockResolvedValue({id:"exp_123",name:"Test"}),createJourney:e.fn().mockReturnValue(n),getMe:e.fn(),destroy:e.fn()},journeys:{get:e.fn().mockReturnValue(n),list:e.fn().mockReturnValue([]),resumeAll:e.fn().mockResolvedValue([])},beacon:{track:e.fn(),trackBatch:e.fn(),flush:e.fn()},on:t,destroy:e.fn()},events:{on:t,emit:(e,n)=>{o[e]?.forEach(e=>e(n))}}}}export{n as createMockSDKObject};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AuctionDetails, AuctionStatus } from '../auctions/types';
|
|
2
|
+
import { Draw, DrawConsumerState } from '../draws/types';
|
|
3
|
+
import { ExperienceDetails, ExperienceSession } from '../experiences/types';
|
|
4
|
+
import { Queue, QueueConsumerState } from '../queues/types';
|
|
5
|
+
import { TimedRelease, TimedReleaseConsumerState } from '../timed-releases/types';
|
|
6
|
+
import { FanfareSDK, InternalFanfareSDK } from '../types';
|
|
7
|
+
import { WaitlistEntry, WaitlistStatus } from '../waitlists/types';
|
|
8
|
+
export declare const mockQueue: Queue;
|
|
9
|
+
export declare const mockQueueEnterResult: QueueConsumerState;
|
|
10
|
+
export declare const mockQueueStatus: QueueConsumerState;
|
|
11
|
+
export declare const mockAuction: AuctionDetails;
|
|
12
|
+
export declare const mockAuctionStatus: AuctionStatus;
|
|
13
|
+
export declare const mockDraw: Draw;
|
|
14
|
+
export declare const mockDrawStatus: DrawConsumerState;
|
|
15
|
+
export declare const mockWaitlistEntry: WaitlistEntry;
|
|
16
|
+
export declare const mockWaitlistStatus: WaitlistStatus;
|
|
17
|
+
export declare const mockTimedRelease: TimedRelease;
|
|
18
|
+
export declare const mockTimedReleaseStatus: TimedReleaseConsumerState;
|
|
19
|
+
export declare const mockExperience: ExperienceDetails;
|
|
20
|
+
export declare const mockExperienceSession: ExperienceSession;
|
|
21
|
+
export declare function createMockFanfareSDK(overrides?: Partial<FanfareSDK>): FanfareSDK;
|
|
22
|
+
export declare function createMockInternalFanfareSDK(overrides?: Partial<InternalFanfareSDK>): InternalFanfareSDK;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createMockJourneyHandle as e,makeReadySnapshot as t}from"./mock-journey.js";const n={type:"guest",consumerId:"consumer_guest_123",guestId:"guest_123",email:void 0,expiresAt:new Date(Date.now()+36e5).toISOString()},s={type:"authenticated",consumerId:"consumer_auth_123",email:"test@example.com",expiresAt:new Date(Date.now()+36e5).toISOString()},i={id:"queue_test_123",experienceId:"experience_test_123",name:"Test Queue",status:"open",currentSize:0},a={position:1,estimatedWaitTimeInSeconds:60,status:"QUEUED"},o={status:"NOT_QUEUED"},c={id:"auction_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:new Date(Date.now()+6e4).toISOString(),settleAt:new Date(Date.now()+6e4).toISOString(),currencyCode:"USD",reservePrice:null,minBidIncrement:"1.00",autoExtendSeconds:null,timeZone:"UTC"},r={auctionId:"auction_test_123",status:"watching",highestBid:"0.00",timeRemaining:6e4},u={id:"draw_test_123",openAt:/* @__PURE__ */(new Date).toISOString(),closeAt:null,timeZone:"UTC",drawAt:new Date(Date.now()+6e4).toISOString(),capacity:null,continueSelectingUntilCompleted:null},d={status:"NOT_ENTERED"},y={id:"waitlist_entry_123",waitlistId:"waitlist_test_123",sequenceId:"sequence_test_123",consumerId:"consumer_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()},l={waitlistId:"waitlist_test_123",isEntered:!1},g={id:"timed_release_test_123",timeZone:"UTC",supportsGuest:!1},m={status:"NOT_ENTERED"},p={id:"experience_test_123",name:"Test Experience",createdAt:/* @__PURE__ */(new Date).toISOString(),updatedAt:/* @__PURE__ */(new Date).toISOString()},S={experienceId:"experience_test_123",enteredAt:/* @__PURE__ */(new Date).toISOString()};function _(){const i=e({initialSnapshot:t(p.id)}).handle;return{auth:{check:()=>({isAuthenticated:!1}),guest:async()=>n,requestOtp:async()=>{},verifyOtp:async()=>s,exchangeExternal:async()=>s,login:async()=>{},logout:async()=>{},getSession:()=>null,refresh:async()=>{}},challenges:{initiate:async()=>({}),verify:async()=>({status:"failed",attemptsRemaining:0})},journeys:{get:()=>i,list:()=>[],resumeAll:async()=>[]},beacon:{track:async()=>{},trackBatch:async()=>{},flush:async()=>{}},appointments:{get:async()=>({id:"appt_mock",type:"appointment"}),getSlots:async()=>[],getMe:async()=>null,book:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),cancel:async()=>{},reschedule:async(e,t,n)=>({bookingId:"booking_mock",appointmentId:e,slotId:t,locationId:n,startTime:/* @__PURE__ */(new Date).toISOString(),endTime:/* @__PURE__ */(new Date).toISOString(),confirmationCode:"MOCK",consumerStatus:"booked"}),startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,bumpMonitorGeneration:()=>{},destroy:()=>{}},on:()=>()=>{},use:()=>{},destroy:async()=>{}}}function I(e={}){return{..._(),...e}}function w(n={}){return{...{..._(),queues:{get:async()=>i,enter:async()=>a,leave:async()=>{},status:async()=>o,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},auctions:{get:async()=>c,bid:async()=>({status:"accepted",amount:"10.00",highestBid:"10.00",bidCount:1}),enter:async()=>{},leave:async()=>{},status:async()=>r,getBidHistory:async()=>[],enableAutoRebid:()=>{},disableAutoRebid:()=>{},getAutoRebidConfig:()=>{},startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1,destroy:()=>{}},draws:{get:async()=>u,enter:async()=>({status:"ENTERED"}),leave:async()=>{},status:async()=>d,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},waitlists:{enter:async()=>y,leave:async()=>{},getStatus:async()=>l,destroy:()=>{}},timedReleases:{get:async()=>g,enter:async()=>({status:"ENTERED",enteredAt:/* @__PURE__ */(new Date).toISOString()}),leave:async()=>{},complete:async()=>{},status:async()=>m,startMonitoring:()=>{},stopMonitoring:()=>{},isMonitoring:()=>!1},experiences:{get:async()=>p,enter:async()=>S,leave:async()=>{},findSequence:async()=>({outcome:"routed",selected:{sequence:{id:"sequence_test_123",name:"Test Sequence",experienceId:"experience_test_123",priority:1,color:"#000000"},status:"admissible",gates:[]},offers:[]}),validateSequenceAccess:async()=>({sequenceId:"sequence_test_123",hasAccess:!0,accessReasons:[],deniedReasons:[]}),selectSequence:async()=>{},getCurrentDistributions:async()=>({}),enterDistribution:async()=>{},getActiveSession:()=>null,isInExperience:()=>!1,getSelectedSequence:()=>null,getMe:async()=>({active:{journeys:[]}}),destroy:()=>{},createJourney:n=>e({initialSnapshot:t(n)}).handle,resumeJourneysFromMe:async()=>[]}},...n}}export{I as createMockFanfareSDK,w as createMockInternalFanfareSDK,c as mockAuction,r as mockAuctionStatus,u as mockDraw,d as mockDrawStatus,p as mockExperience,S as mockExperienceSession,i as mockQueue,a as mockQueueEnterResult,o as mockQueueStatus,g as mockTimedRelease,m as mockTimedReleaseStatus,y as mockWaitlistEntry,l as mockWaitlistStatus};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Server for API interception and testing
|
|
3
|
+
*
|
|
4
|
+
* This class intercepts fetch requests and provides controlled responses
|
|
5
|
+
* for testing the Fanfare SDK without hitting real endpoints.
|
|
6
|
+
*/
|
|
7
|
+
export interface MockResponse {
|
|
8
|
+
status?: number;
|
|
9
|
+
statusText?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
body?: any;
|
|
12
|
+
delay?: number;
|
|
13
|
+
error?: Error | string;
|
|
14
|
+
}
|
|
15
|
+
export interface MockEndpoint {
|
|
16
|
+
method: string;
|
|
17
|
+
pathPattern: string | RegExp;
|
|
18
|
+
response: MockResponse | ((request: Request) => MockResponse | Promise<MockResponse>);
|
|
19
|
+
}
|
|
20
|
+
export interface MockServerConfig {
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
defaultDelay?: number;
|
|
23
|
+
logRequests?: boolean;
|
|
24
|
+
fallbackToNetwork?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare class MockServer {
|
|
27
|
+
private endpoints;
|
|
28
|
+
private interceptedRequests;
|
|
29
|
+
private isActive;
|
|
30
|
+
private config;
|
|
31
|
+
constructor(config: MockServerConfig);
|
|
32
|
+
/**
|
|
33
|
+
* Register a mock endpoint
|
|
34
|
+
*/
|
|
35
|
+
mock(method: string, pathPattern: string | RegExp, response: MockResponse | ((request: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
36
|
+
/**
|
|
37
|
+
* Convenience methods for common HTTP methods
|
|
38
|
+
*/
|
|
39
|
+
get(pathPattern: string | RegExp, response: MockResponse | ((req: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
40
|
+
post(pathPattern: string | RegExp, response: MockResponse | ((req: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
41
|
+
put(pathPattern: string | RegExp, response: MockResponse | ((req: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
42
|
+
patch(pathPattern: string | RegExp, response: MockResponse | ((req: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
43
|
+
delete(pathPattern: string | RegExp, response: MockResponse | ((req: Request) => MockResponse | Promise<MockResponse>)): this;
|
|
44
|
+
/**
|
|
45
|
+
* Start intercepting requests
|
|
46
|
+
*/
|
|
47
|
+
start(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Stop intercepting requests
|
|
50
|
+
*/
|
|
51
|
+
stop(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Reset all mock endpoints and clear request history
|
|
54
|
+
*/
|
|
55
|
+
reset(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get all intercepted requests
|
|
58
|
+
*/
|
|
59
|
+
getRequests(): typeof this.interceptedRequests;
|
|
60
|
+
/**
|
|
61
|
+
* Get requests matching a specific pattern
|
|
62
|
+
*/
|
|
63
|
+
getRequestsMatching(pathPattern: string | RegExp): typeof this.interceptedRequests;
|
|
64
|
+
/**
|
|
65
|
+
* Clear request history
|
|
66
|
+
*/
|
|
67
|
+
clearHistory(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Create the fetch interceptor
|
|
70
|
+
*/
|
|
71
|
+
handle(input: RequestInfo | URL, init?: RequestInit): Promise<Response | null>;
|
|
72
|
+
/**
|
|
73
|
+
* Load a preset scenario
|
|
74
|
+
*/
|
|
75
|
+
loadPreset(preset: MockPreset): void;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Mock preset for common scenarios
|
|
79
|
+
*/
|
|
80
|
+
export interface MockPreset {
|
|
81
|
+
name: string;
|
|
82
|
+
description: string;
|
|
83
|
+
endpoints: MockEndpoint[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Helper to create dynamic responses
|
|
87
|
+
*/
|
|
88
|
+
export declare class ResponseBuilder {
|
|
89
|
+
private response;
|
|
90
|
+
static ok(body?: any): MockResponse;
|
|
91
|
+
static created(body?: any): MockResponse;
|
|
92
|
+
static noContent(): MockResponse;
|
|
93
|
+
static badRequest(message?: string): MockResponse;
|
|
94
|
+
static unauthorized(message?: string): MockResponse;
|
|
95
|
+
static forbidden(message?: string): MockResponse;
|
|
96
|
+
static notFound(message?: string): MockResponse;
|
|
97
|
+
static serverError(message?: string): MockResponse;
|
|
98
|
+
static error(message: string, status?: number): MockResponse;
|
|
99
|
+
static networkError(): MockResponse;
|
|
100
|
+
status(code: number, text?: string): ResponseBuilder;
|
|
101
|
+
body(data: any): ResponseBuilder;
|
|
102
|
+
headers(headers: Record<string, string>): ResponseBuilder;
|
|
103
|
+
delay(ms: number): ResponseBuilder;
|
|
104
|
+
build(): MockResponse;
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=/* @__PURE__ */new Set;let e=null;function s(t){window.fetch=t,globalThis.fetch=t,"undefined"!=typeof self&&(self.fetch=t)}class r{constructor(t){this.endpoints=[],this.interceptedRequests=[],this.isActive=!1,this.config=t}mock(t,e,s){return this.endpoints.push({method:t.toUpperCase(),pathPattern:e,response:s}),this}get(t,e){return this.mock("GET",t,e)}post(t,e){return this.mock("POST",t,e)}put(t,e){return this.mock("PUT",t,e)}patch(t,e){return this.mock("PATCH",t,e)}delete(t,e){return this.mock("DELETE",t,e)}start(){this.isActive||(this.isActive=!0,t.add(this),function(){if(e)return;e=window.fetch.bind(window),s(async(s,r)=>{for(const e of t){const t=await e.handle(s,r);if(t)return t}if(!e)throw new Error("MockServer dispatcher is missing original fetch.");return e(s,r)})}())}stop(){this.isActive&&(this.isActive=!1,t.delete(this),t.size>0||!e||(s(e),e=null))}reset(){this.endpoints=[],this.interceptedRequests=[]}getRequests(){return[...this.interceptedRequests]}getRequestsMatching(t){return this.interceptedRequests.filter(e=>{const s=new URL(e.url).pathname;return"string"==typeof t?s===t:t.test(s)})}clearHistory(){this.interceptedRequests=[]}async handle(t,s){const r=function(t){if(t instanceof Request)return new URL(t.url);if(t instanceof URL)return t;const e="undefined"!=typeof window?window.location.href:"http://localhost";return new URL(String(t),e)}(t);if(!r.href.startsWith(this.config.baseUrl))return null;const n=new Request(t,(t=>{if(!t)return;const{signal:e,...s}=t;return s})(s)),o=n.method.toUpperCase(),i=r.pathname;this.config.logRequests;const a={};let u;if(n.headers.forEach((t,e)=>{a[e]=t}),n.body)try{const t=n.clone(),e=await t.text();if(e)try{u=JSON.parse(e)}catch{u=e}}catch{u=void 0}this.interceptedRequests.push({timestamp:/* @__PURE__ */new Date,method:o,url:r.href,headers:a,body:u});const h=new URL(this.config.baseUrl).pathname.replace(/\/$/,""),c=h&&i.startsWith(h)?i.slice(h.length):i,d=this.endpoints.find(t=>t.method===o&&("string"==typeof t.pathPattern?c===t.pathPattern:t.pathPattern.test(c)));if(!d){if(this.config.fallbackToNetwork){if(!e)throw new Error("MockServer fallback requested without an original fetch.");return e(n)}return new Response(JSON.stringify({error:"Mock not found"}),{status:404,statusText:"Not Found",headers:{"Content-Type":"application/json"}})}const p="function"==typeof d.response?await d.response(n):d.response,f=p.delay??this.config.defaultDelay??0;if(f>0&&await new Promise(t=>setTimeout(t,f)),p.error){if("string"==typeof p.error)throw new Error(p.error);throw p.error}const l=void 0!==p.body?JSON.stringify(p.body):"";return new Response(l,{status:p.status??200,statusText:p.statusText??"OK",headers:{"Content-Type":"application/json",...p.headers}})}loadPreset(t){this.reset(),t.endpoints.forEach(t=>{this.mock(t.method,t.pathPattern,t.response)})}}class n{constructor(){this.response={}}static ok(t){return{status:200,statusText:"OK",body:t}}static created(t){return{status:201,statusText:"Created",body:t}}static noContent(){return{status:204,statusText:"No Content"}}static badRequest(t="Bad Request"){return{status:400,statusText:"Bad Request",body:{error:t,message:t}}}static unauthorized(t="Unauthorized"){return{status:401,statusText:"Unauthorized",body:{error:t,message:t}}}static forbidden(t="Forbidden"){return{status:403,statusText:"Forbidden",body:{error:t,message:t}}}static notFound(t="Not Found"){return{status:404,statusText:"Not Found",body:{error:t,message:t}}}static serverError(t="Internal Server Error"){return{status:500,statusText:"Internal Server Error",body:{error:t,message:t}}}static error(t,e=500){return{status:e,statusText:(s=e,{400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"}[s]||"Error"),body:{error:t,message:t}};var s}static networkError(){return{error:new Error("Network request failed")}}status(t,e){return this.response.status=t,e&&(this.response.statusText=e),this}body(t){return this.response.body=t,this}headers(t){return this.response.headers={...this.response.headers,...t},this}delay(t){return this.response.delay=t,this}build(){return this.response}}export{r as MockServer,n as ResponseBuilder};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createMockFanfareSDK as o}from"./mock-sdk.js";async function r(r){return o()}export{r as createMockSDK};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { HarnessScenarioKey } from './harness-scenarios';
|
|
2
|
+
export type VerificationScenarioKey = "queue" | "admission-cycle" | "auth-queue" | "access-code-queue" | "draw" | "auth-draw" | "timed-release" | "auction";
|
|
3
|
+
export type VerificationAction = {
|
|
4
|
+
type: "clickButton";
|
|
5
|
+
label: string;
|
|
6
|
+
} | {
|
|
7
|
+
type: "fillCredential";
|
|
8
|
+
value: string;
|
|
9
|
+
} | {
|
|
10
|
+
type: "fillOtp";
|
|
11
|
+
value: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: "fillAccessCode";
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
16
|
+
export interface VerificationCheckpoint {
|
|
17
|
+
journeyStage: string;
|
|
18
|
+
sequencePhase?: string;
|
|
19
|
+
visibleTexts?: string[];
|
|
20
|
+
hiddenTexts?: string[];
|
|
21
|
+
visibleActions?: string[];
|
|
22
|
+
hiddenActions?: string[];
|
|
23
|
+
timerVisible?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface VerificationStep {
|
|
26
|
+
key: string;
|
|
27
|
+
actions: VerificationAction[];
|
|
28
|
+
checkpoint: VerificationCheckpoint;
|
|
29
|
+
}
|
|
30
|
+
export interface VerificationScenarioDefinition {
|
|
31
|
+
key: VerificationScenarioKey;
|
|
32
|
+
initial: VerificationCheckpoint;
|
|
33
|
+
steps: VerificationStep[];
|
|
34
|
+
}
|
|
35
|
+
export declare const DEFAULT_VERIFICATION_EMAIL = "sdk-harness@example.com";
|
|
36
|
+
export declare const DEFAULT_VERIFICATION_OTP = "424242";
|
|
37
|
+
export declare const DEFAULT_INVALID_ACCESS_CODE = "WRONG1";
|
|
38
|
+
export declare const DEFAULT_VALID_ACCESS_CODE = "VIP123";
|
|
39
|
+
export declare const SHARED_HARNESS_VERIFICATION_SCENARIOS: VerificationScenarioKey[];
|
|
40
|
+
export declare const ADAPTER_SMOKE_SCENARIOS: Array<Exclude<VerificationScenarioKey, "auth-draw">>;
|
|
41
|
+
export declare const SDK_VERIFICATION_SCENARIOS: Record<VerificationScenarioKey, VerificationScenarioDefinition>;
|
|
42
|
+
export declare function isVerificationScenarioKey(value: HarnessScenarioKey): value is VerificationScenarioKey;
|
|
43
|
+
export declare function getVerificationScenario(key: VerificationScenarioKey): VerificationScenarioDefinition;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e="sdk-harness@example.com",t="424242",i="WRONG1",n="VIP123",s=["queue","admission-cycle","auth-queue","access-code-queue","draw","auth-draw","timed-release","auction"],u=["queue","admission-cycle","auth-queue","access-code-queue","draw","timed-release","auction"],o={queue:{key:"queue",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Queue"]},steps:[{key:"entered-queue",actions:[{type:"clickButton",label:"Enter Queue"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're in the Queue","Estimated wait"],hiddenTexts:["Continue to Shop"],visibleActions:["Leave Queue"]}}]},"admission-cycle":{key:"admission-cycle",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Queue"]},steps:[{key:"granted",actions:[{type:"clickButton",label:"Enter Queue"}],checkpoint:{journeyStage:"routed",sequencePhase:"granted",visibleTexts:["You're in!","Continue to Shop"],visibleActions:["Continue to Shop"]}}]},"auth-queue":{key:"auth-queue",initial:{journeyStage:"gated",visibleTexts:["Sign In","Enter your email or phone to continue"],visibleActions:["Continue"]},steps:[{key:"submitted-auth-credential",actions:[{type:"fillCredential",value:e},{type:"clickButton",label:"Continue"}],checkpoint:{journeyStage:"gated",visibleTexts:["Enter your code"],visibleActions:["Verify"]}},{key:"verified-auth",actions:[{type:"fillOtp",value:t}],checkpoint:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Queue"]}},{key:"entered-queue",actions:[{type:"clickButton",label:"Enter Queue"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're in the Queue","Estimated wait"],visibleActions:["Leave Queue"]}}]},"access-code-queue":{key:"access-code-queue",initial:{journeyStage:"gated",visibleTexts:["Enter Access Code","Enter your access code to continue"],visibleActions:["Submit"],hiddenActions:["Enter Queue"]},steps:[{key:"rejected-access-code",actions:[{type:"fillAccessCode",value:i},{type:"clickButton",label:"Submit"}],checkpoint:{journeyStage:"gated",hiddenActions:["Enter Queue"],visibleActions:["Submit"]}},{key:"unlocked-queue",actions:[{type:"fillAccessCode",value:n},{type:"clickButton",label:"Submit"}],checkpoint:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Queue"]}},{key:"entered-queue",actions:[{type:"clickButton",label:"Enter Queue"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're in the Queue","Estimated wait"],visibleActions:["Leave Queue"]}}]},draw:{key:"draw",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Draw"]},steps:[{key:"entered-draw",actions:[{type:"clickButton",label:"Enter Draw"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You've Entered","Good luck in the draw","Drawing in"],visibleActions:["Withdraw Entry"],timerVisible:!0}}]},"auth-draw":{key:"auth-draw",initial:{journeyStage:"gated",visibleTexts:["Sign In","Enter your email or phone to continue"],visibleActions:["Continue"]},steps:[{key:"submitted-auth-credential",actions:[{type:"fillCredential",value:e},{type:"clickButton",label:"Continue"}],checkpoint:{journeyStage:"gated",visibleTexts:["Enter your code"],visibleActions:["Verify"]}},{key:"verified-auth",actions:[{type:"fillOtp",value:t}],checkpoint:{journeyStage:"routed",sequencePhase:"enterable",visibleActions:["Enter Draw"]}},{key:"entered-draw",actions:[{type:"clickButton",label:"Enter Draw"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You've Entered","Good luck in the draw","Drawing in"],visibleActions:["Withdraw Entry"],timerVisible:!0}}]},"timed-release":{key:"timed-release",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleTexts:["Flash Sale","Limited time access","Window closes in"],visibleActions:["Enter Sale"],timerVisible:!0},steps:[{key:"entered-sale",actions:[{type:"clickButton",label:"Enter Sale"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're in!","Complete purchase in","Browse and complete your purchase before your session expires"],visibleActions:["Complete Purchase","Exit Sale"],timerVisible:!0}},{key:"completed-sale",actions:[{type:"clickButton",label:"Complete Purchase"}],checkpoint:{journeyStage:"routed",sequencePhase:"granted",visibleTexts:["You're in!","Continue to Shop"],visibleActions:["Continue to Shop"]}}]},auction:{key:"auction",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleTexts:["Auction","Bid for exclusive access","Auction ends in"],visibleActions:["Bid"],timerVisible:!0},steps:[{key:"bid-auction",actions:[{type:"clickButton",label:"Bid"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["Auction","Current bid","Auction ends in"],visibleActions:["Bid","Leave"],timerVisible:!0}},{key:"placed-bid",actions:[{type:"clickButton",label:"Bid"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're Winning!","Your winning bid","$110.00"],visibleActions:["Bid"]}}]}};function a(e){return e in o}function c(e){return o[e]}export{u as ADAPTER_SMOKE_SCENARIOS,i as DEFAULT_INVALID_ACCESS_CODE,n as DEFAULT_VALID_ACCESS_CODE,e as DEFAULT_VERIFICATION_EMAIL,t as DEFAULT_VERIFICATION_OTP,o as SDK_VERIFICATION_SCENARIOS,s as SHARED_HARNESS_VERIFICATION_SCENARIOS,c as getVerificationScenario,a as isVerificationScenarioKey};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resetVerificationState(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resetSDKStore as t}from"../state/store.js";function o(){t()}export{o as resetVerificationState};
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme — single source of truth via `@fanfare-io/fanfare-sdk-contracts`.
|
|
3
|
+
*
|
|
4
|
+
* The schema, defaults, and merge helper live in `@fanfare-io/fanfare-sdk-contracts`
|
|
5
|
+
* to keep one definition shared between admin/server (`shared-models`) and the
|
|
6
|
+
* SDK family (sdk-core, sdk-react, sdk-solid).
|
|
7
|
+
*/
|
|
8
|
+
export { BrandThemeSchema, DefaultBrandTheme, WidgetVariantSchema, WidgetVariants, mergeBrandTheme, type BrandTheme, type ResolvedBrandTheme, type WidgetVariant, } from '@fanfare-io/fanfare-sdk-contracts/theme';
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{BrandThemeSchema as r,DefaultBrandTheme as a,WidgetVariantSchema as e,WidgetVariants as f,mergeBrandTheme as o}from"@fanfare-io/fanfare-sdk-contracts/theme";export{r as BrandThemeSchema,a as DefaultBrandTheme,e as WidgetVariantSchema,f as WidgetVariants,o as mergeBrandTheme};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timed Release module exports
|
|
3
|
+
*/
|
|
4
|
+
export { TimedReleaseManagementModule } from './timed-release.module';
|
|
5
|
+
export { TimedReleaseDenyReason } from './types';
|
|
6
|
+
export type { TimedRelease, TimedReleaseCompletedState, TimedReleaseConsumerState, TimedReleaseDeniedState, TimedReleaseEnteredState, TimedReleaseLeftState, TimedReleaseModule, TimedReleaseNotEnteredState, TimedReleaseParticipation, } from './types';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public timed-release surface. The runtime `TimedReleaseManagementModule`/`TimedReleaseModule`/`TimedReleaseParticipation`
|
|
3
|
+
* symbols from `./types` are internal and not exported here.
|
|
4
|
+
*/
|
|
5
|
+
export { TimedReleaseDenyReason } from './types';
|
|
6
|
+
export type { TimedRelease, TimedReleaseCompletedState, TimedReleaseConsumerState, TimedReleaseDeniedState, TimedReleaseEnteredState, TimedReleaseLeftState, TimedReleaseNotEnteredState, } from './types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{TimedReleaseDenyReason as o}from"./types.js";export{o as TimedReleaseDenyReason};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpClient, RequestOptions } from '../core/http';
|
|
2
|
+
import { MonitorUpdate } from '../experiences/distribution-monitor.types';
|
|
3
|
+
import { TimedRelease, TimedReleaseConsumerState, TimedReleaseModule } from './types';
|
|
4
|
+
export declare class TimedReleaseManagementModule implements TimedReleaseModule {
|
|
5
|
+
private readonly http;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly events;
|
|
8
|
+
private readonly monitorRuntime;
|
|
9
|
+
private readonly trackedParticipations;
|
|
10
|
+
private readonly monitorGenerations;
|
|
11
|
+
private readonly operationGenerations;
|
|
12
|
+
private destroyed;
|
|
13
|
+
private lifecycleGeneration;
|
|
14
|
+
private get store();
|
|
15
|
+
constructor(http: HttpClient);
|
|
16
|
+
get(timedReleaseId: string): Promise<TimedRelease>;
|
|
17
|
+
enter(timedReleaseId: string, variantId?: string, options?: RequestOptions): Promise<TimedReleaseConsumerState>;
|
|
18
|
+
leave(timedReleaseId: string, options?: RequestOptions): Promise<void>;
|
|
19
|
+
complete(timedReleaseId: string, options?: RequestOptions): Promise<string | undefined>;
|
|
20
|
+
status(timedReleaseId: string): Promise<TimedReleaseConsumerState>;
|
|
21
|
+
startMonitoring(id: string, context?: Record<string, unknown>, onUpdate?: (update: MonitorUpdate) => void): void;
|
|
22
|
+
stopMonitoring(id: string): void;
|
|
23
|
+
isMonitoring(id: string): boolean;
|
|
24
|
+
private bumpMonitorGeneration;
|
|
25
|
+
private getOperationGeneration;
|
|
26
|
+
private bumpOperationGeneration;
|
|
27
|
+
private canApplyAsyncResult;
|
|
28
|
+
private buildTransition;
|
|
29
|
+
private applyTransition;
|
|
30
|
+
destroy(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createError as t}from"../core/errors.js";import{getLogger as e}from"../core/logger.js";import{DistributionMonitorRuntime as i}from"../experiences/distribution-monitor.runtime.js";import{getEventBus as s}from"../state/events.js";import{getSDKStore as r}from"../state/store.js";class n{constructor(t){this.logger=e(),this.events=s(),this.monitorRuntime=new i,this.trackedParticipations=/* @__PURE__ */new Map,this.monitorGenerations=/* @__PURE__ */new Map,this.operationGenerations=/* @__PURE__ */new Map,this.destroyed=!1,this.lifecycleGeneration=0,this.http=t}get store(){return r()}async get(t){try{return await this.http.get(`/timed-releases/${t}`)}catch(e){throw this.logger.error("Failed to get timed release",{timedReleaseId:t,error:e}),e}}async enter(e,i,s){const r=this.lifecycleGeneration,n=this.monitorGenerations.get(e);if(!this.store.session)throw t.unauthorized("Not authenticated. Call auth.guest() or auth.login() first");const o=this.bumpOperationGeneration(e);try{this.logger.info("Entering timed release",{timedReleaseId:e});const t=await this.http.post(`/timed-releases/${e}/enter`,{variantId:i},s),a=this.buildTransition(e,t,{source:"enter"});return this.canApplyAsyncResult(e,r,n,o)?(this.applyTransition(e,a),t):t}catch(a){if(!this.canApplyAsyncResult(e,r,n,o))throw a;throw this.logger.error("Failed to enter timed release",{timedReleaseId:e,error:a}),this.events.emit("timed_release:error",{timedReleaseId:e,error:a}),a}}async leave(t,e){const i=this.lifecycleGeneration,s=this.monitorGenerations.get(t),r=this.bumpOperationGeneration(t);try{this.logger.info("Leaving timed release",{timedReleaseId:t}),await this.http.post(`/timed-releases/${t}/leave`,{},e);const n=this.buildTransition(t,{status:"LEFT",leftAt:/* @__PURE__ */(new Date).toISOString()},{source:"leave"});if(!this.canApplyAsyncResult(t,i,s,r))return;this.applyTransition(t,n)}catch(n){if(!this.canApplyAsyncResult(t,i,s,r))throw n;throw this.logger.error("Failed to leave timed release",{timedReleaseId:t,error:n}),this.events.emit("timed_release:error",{timedReleaseId:t,error:n}),n}}async complete(t,e){const i=this.lifecycleGeneration,s=this.monitorGenerations.get(t),r=this.bumpOperationGeneration(t);try{this.logger.info("Completing timed release",{timedReleaseId:t});const n=(await this.http.post(`/timed-releases/${t}/complete`,{},e)).admissionGrant,o=this.buildTransition(t,{status:"COMPLETED",completedAt:/* @__PURE__ */(new Date).toISOString()},{source:"complete",grant:n});return this.canApplyAsyncResult(t,i,s,r)?(this.applyTransition(t,o),n):n}catch(n){if(!this.canApplyAsyncResult(t,i,s,r))throw n;throw this.logger.error("Failed to complete timed release",{timedReleaseId:t,error:n}),this.events.emit("timed_release:error",{timedReleaseId:t,error:n}),n}}async status(t){const e=this.lifecycleGeneration,i=this.monitorGenerations.get(t),s=this.bumpOperationGeneration(t);try{const r=await this.http.get(`/timed-releases/${t}/status`),n=this.buildTransition(t,r,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,n),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:r.status}),r):r}catch(r){if(r&&"object"==typeof r&&"status"in r&&404===r.status){const r={status:"NOT_ENTERED"},n=this.buildTransition(t,r,{source:"status"});return this.canApplyAsyncResult(t,e,i,s)?(this.applyTransition(t,n),this.events.emit("timed_release:status-updated",{timedReleaseId:t,status:r.status}),r):r}if(!this.canApplyAsyncResult(t,e,i,s))return{status:"NOT_ENTERED"};throw this.logger.error("Failed to get timed release status",{timedReleaseId:t,error:r}),this.events.emit("timed_release:error",{timedReleaseId:t,error:r}),r}}startMonitoring(t,e,i){this.bumpMonitorGeneration(t),this.monitorRuntime.start(t,e,i);const s=this.monitorRuntime.getDisplayAtom(t);s&&s.set({type:"timed_release",enteredAt:this.trackedParticipations.get(t)?.enteredAt})}stopMonitoring(t){this.bumpMonitorGeneration(t),this.monitorRuntime.stop(t)}isMonitoring(t){return this.monitorRuntime.has(t)}bumpMonitorGeneration(t){const e=(this.monitorGenerations.get(t)??0)+1;return this.monitorGenerations.set(t,e),e}getOperationGeneration(t){return this.operationGenerations.get(t)??0}bumpOperationGeneration(t){const e=this.getOperationGeneration(t)+1;return this.operationGenerations.set(t,e),e}canApplyAsyncResult(t,e,i,s){return!this.destroyed&&this.lifecycleGeneration===e&&((void 0===i||this.monitorGenerations.get(t)===i)&&(void 0===s||this.getOperationGeneration(t)===s))}buildTransition(t,e,{source:i,grant:s}){switch(e.status){case"ENTERED":return{trackedParticipation:{timedReleaseId:t,enteredAt:e.enteredAt,selectedVariantId:e.selectedVariantId},events:"enter"===i?[{type:"timed_release:entered",payload:{timedReleaseId:t,enteredAt:e.enteredAt}}]:[],monitorUpdate:null,stopMonitoring:!1};case"COMPLETED":return{trackedParticipation:null,events:[{type:"timed_release:completed",payload:{timedReleaseId:t,completedAt:e.completedAt}}],monitorUpdate:"complete"===i&&s?{type:"granted",token:s}:null,stopMonitoring:"complete"===i||"status"===i};case"LEFT":return{trackedParticipation:null,events:[{type:"timed_release:left",payload:{timedReleaseId:t}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"left"}}:null,stopMonitoring:"leave"===i||"status"===i};case"DENIED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"denied",reason:e.reason}}:null,stopMonitoring:"enter"===i||"status"===i};case"NOT_ENTERED":return{trackedParticipation:null,events:[],monitorUpdate:"enter"===i||"status"===i?{type:"ended",outcome:{type:"closed",reason:"not_entered"}}:null,stopMonitoring:"enter"===i||"status"===i}}}applyTransition(t,e){e.trackedParticipation?this.trackedParticipations.set(t,e.trackedParticipation):this.trackedParticipations.delete(t);const i=this.monitorRuntime.getDisplayAtom(t);i&&(e.trackedParticipation?i.set({type:"timed_release",enteredAt:e.trackedParticipation.enteredAt}):i.set({type:"timed_release"}));for(const s of e.events)this.events.emit(s.type,s.payload);e.monitorUpdate&&this.monitorRuntime.notify(t,e.monitorUpdate),e.stopMonitoring&&this.stopMonitoring(t)}destroy(){this.destroyed=!0,this.lifecycleGeneration+=1,this.trackedParticipations.clear(),this.monitorGenerations.clear(),this.operationGenerations.clear(),this.monitorRuntime.clear()}}export{n as TimedReleaseManagementModule};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { RequestOptions } from '../core/http';
|
|
2
|
+
/**
|
|
3
|
+
* Known timed release deny reasons.
|
|
4
|
+
*
|
|
5
|
+
* When a consumer is denied entry to a timed release, the `reason` field
|
|
6
|
+
* will contain one of these values (or a custom reason).
|
|
7
|
+
*/
|
|
8
|
+
export declare const TimedReleaseDenyReason: {
|
|
9
|
+
/** Consumer has exceeded their order limit for this experience */
|
|
10
|
+
readonly ORDER_LIMIT_EXCEEDED: "ORDER_LIMIT_EXCEEDED";
|
|
11
|
+
/** Timed release is closed */
|
|
12
|
+
readonly RELEASE_CLOSED: "RELEASE_CLOSED";
|
|
13
|
+
/** Bot detection triggered */
|
|
14
|
+
readonly BOT_DETECTED: "BOT_DETECTED";
|
|
15
|
+
};
|
|
16
|
+
export type TimedReleaseDenyReason = (typeof TimedReleaseDenyReason)[keyof typeof TimedReleaseDenyReason];
|
|
17
|
+
/**
|
|
18
|
+
* Timed Release participation stored in state
|
|
19
|
+
*/
|
|
20
|
+
export interface TimedReleaseParticipation {
|
|
21
|
+
timedReleaseId: string;
|
|
22
|
+
enteredAt: string;
|
|
23
|
+
selectedVariantId?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Timed Release consumer states (from backend)
|
|
27
|
+
*/
|
|
28
|
+
export type TimedReleaseConsumerState = TimedReleaseNotEnteredState | TimedReleaseEnteredState | TimedReleaseCompletedState | TimedReleaseLeftState | TimedReleaseDeniedState;
|
|
29
|
+
export interface TimedReleaseNotEnteredState {
|
|
30
|
+
status: "NOT_ENTERED";
|
|
31
|
+
}
|
|
32
|
+
export interface TimedReleaseEnteredState {
|
|
33
|
+
status: "ENTERED";
|
|
34
|
+
enteredAt: string;
|
|
35
|
+
selectedVariantId?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface TimedReleaseCompletedState {
|
|
38
|
+
status: "COMPLETED";
|
|
39
|
+
completedAt: string;
|
|
40
|
+
selectedVariantId?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface TimedReleaseLeftState {
|
|
43
|
+
status: "LEFT";
|
|
44
|
+
leftAt: string;
|
|
45
|
+
}
|
|
46
|
+
export interface TimedReleaseDeniedState {
|
|
47
|
+
status: "DENIED";
|
|
48
|
+
reason: string;
|
|
49
|
+
deniedAt: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Timed Release module interface
|
|
53
|
+
*/
|
|
54
|
+
export interface TimedReleaseModule {
|
|
55
|
+
get(timedReleaseId: string): Promise<TimedRelease>;
|
|
56
|
+
enter(timedReleaseId: string, variantId?: string, options?: RequestOptions): Promise<TimedReleaseConsumerState>;
|
|
57
|
+
leave(timedReleaseId: string, options?: RequestOptions): Promise<void>;
|
|
58
|
+
complete(timedReleaseId: string, options?: RequestOptions): Promise<string | undefined>;
|
|
59
|
+
status(timedReleaseId: string): Promise<TimedReleaseConsumerState>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Timed Release entity
|
|
63
|
+
*/
|
|
64
|
+
export interface TimedRelease {
|
|
65
|
+
id: string;
|
|
66
|
+
openAt?: string;
|
|
67
|
+
closeAt?: string;
|
|
68
|
+
timeZone: string;
|
|
69
|
+
supportsGuest: boolean;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const E={ORDER_LIMIT_EXCEEDED:"ORDER_LIMIT_EXCEEDED",RELEASE_CLOSED:"RELEASE_CLOSED",BOT_DETECTED:"BOT_DETECTED"};export{E as TimedReleaseDenyReason};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical SDK distribution-type union.
|
|
3
|
+
*
|
|
4
|
+
* Previously, the SDK spelled out `"queue" | "draw" | ... | "appointment"` in
|
|
5
|
+
* 8+ places across `experiences/types.ts`, `experiences/journey.types.ts`,
|
|
6
|
+
* `handoff/handoff.module.ts`, `state/capability-token-registry.ts`, and
|
|
7
|
+
* `state/events.ts`. Each variant included or excluded `appointment`,
|
|
8
|
+
* `waitlist`, and `timed_release` differently, and adding a new distribution
|
|
9
|
+
* type required a coordinated 8-place edit. Most divergence was unintended.
|
|
10
|
+
*
|
|
11
|
+
* Rather than keep redefining the same set, sites import {@link
|
|
12
|
+
* SdkDistributionType} (the full set) and narrow with `Exclude` /
|
|
13
|
+
* `Extract` when they legitimately need a subset (e.g. journey participation
|
|
14
|
+
* excludes `appointment`; handoff payload excludes `timed_release` because
|
|
15
|
+
* those distributions don't produce handoff tokens).
|
|
16
|
+
*
|
|
17
|
+
* The lowercase string values are an SDK-public contract — they appear in
|
|
18
|
+
* `JourneySnapshot.participation.type`, `Distribution.type`, etc. Server
|
|
19
|
+
* payloads use these lowercase public values at SDK boundaries.
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
export type SdkDistributionType = "queue" | "draw" | "auction" | "appointment" | "timed_release" | "waitlist";
|
|
23
|
+
/**
|
|
24
|
+
* The five "distribution-proper" types that have an open/active state and
|
|
25
|
+
* appear under `Distribution.type`, `DistributionContext.active.type`, etc.
|
|
26
|
+
* Excludes `waitlist` because waitlists are pre-distribution membership
|
|
27
|
+
* containers, not distributions themselves.
|
|
28
|
+
*/
|
|
29
|
+
export type SdkDistributionEntryType = Exclude<SdkDistributionType, "waitlist">;
|
|
30
|
+
/**
|
|
31
|
+
* Distribution types that produce a handoff token on admission.
|
|
32
|
+
* Excludes `timed_release` (no admission handoff) and `waitlist` (pre-distribution).
|
|
33
|
+
*/
|
|
34
|
+
export type SdkHandoffDistributionType = Exclude<SdkDistributionType, "timed_release" | "waitlist">;
|
|
35
|
+
/**
|
|
36
|
+
* Distribution types that participate in journey resume / participation tracking.
|
|
37
|
+
*/
|
|
38
|
+
export type SdkJourneyParticipationType = SdkDistributionType;
|
|
39
|
+
/**
|
|
40
|
+
* Distribution types that produce admissions for journey resume.
|
|
41
|
+
* Excludes `appointment` (booking is the participation; there is no admission
|
|
42
|
+
* handoff — completion surfaces via display state) and `waitlist` (waitlists
|
|
43
|
+
* graduate into other distributions; the admission belongs to the downstream type).
|
|
44
|
+
*/
|
|
45
|
+
export type SdkJourneyAdmissionType = Exclude<SdkDistributionType, "appointment" | "waitlist">;
|