@fanfare-io/fanfare-sdk-core 0.2.0 → 0.4.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.driver.d.ts +44 -0
- package/dist/appointments/appointment.driver.js +1 -0
- package/dist/appointments/appointment.sequence.d.ts +194 -0
- package/dist/auctions/auction.driver.d.ts +46 -0
- package/dist/auctions/auction.driver.js +1 -0
- package/dist/auctions/auction.module.js +1 -1
- package/dist/auctions/auction.sequence.d.ts +263 -0
- package/dist/core/client.js +1 -1
- package/dist/draws/draw.driver.d.ts +45 -0
- package/dist/draws/draw.driver.js +1 -0
- package/dist/draws/draw.module.js +1 -1
- package/dist/draws/draw.sequence.d.ts +249 -0
- package/dist/experiences/distribution-monitor.types.d.ts +92 -2
- package/dist/experiences/experience.module.js +1 -1
- package/dist/experiences/journey-view.d.ts +8 -10
- package/dist/experiences/journey-view.js +1 -1
- package/dist/experiences/journey.d.ts +45 -20
- package/dist/experiences/journey.js +1 -1
- package/dist/experiences/journey.machine.d.ts +3 -20
- package/dist/experiences/journey.machine.js +1 -1
- package/dist/experiences/journey.types.d.ts +422 -217
- package/dist/experiences/public.d.ts +1 -1
- package/dist/experiences/sequence-driver.d.ts +93 -0
- package/dist/experiences/types.d.ts +185 -23
- package/dist/internals.d.ts +1 -1
- package/dist/internals.js +1 -1
- package/dist/queues/queue.driver.d.ts +45 -0
- package/dist/queues/queue.driver.js +1 -0
- package/dist/queues/queue.module.js +1 -1
- package/dist/queues/queue.sequence.d.ts +222 -0
- package/dist/state/capability-token-registry.d.ts +1 -0
- package/dist/state/capability-token-registry.js +1 -1
- package/dist/state/store.d.ts +5 -1
- package/dist/state/store.js +1 -1
- package/dist/storefront.d.ts +14 -24
- package/dist/storefront.js +1 -1
- package/dist/test-utils/harness-scenarios.d.ts +8 -1
- package/dist/test-utils/harness-scenarios.js +1 -1
- package/dist/test-utils/mock-journey.d.ts +15 -4
- package/dist/test-utils/mock-journey.js +1 -1
- package/dist/test-utils/mock-sdk.js +1 -1
- package/dist/test-utils/verification-scenarios.d.ts +9 -1
- package/dist/test-utils/verification-scenarios.js +1 -1
- package/dist/timed-releases/timed-release.driver.d.ts +45 -0
- package/dist/timed-releases/timed-release.driver.js +1 -0
- package/dist/timed-releases/timed-release.module.d.ts +6 -1
- package/dist/timed-releases/timed-release.module.js +1 -1
- package/dist/timed-releases/timed-release.sequence.d.ts +227 -0
- package/dist/timed-releases/types.d.ts +16 -1
- package/dist/types/distribution-type.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { SequenceOutcomeType } from '@fanfare-io/fanfare-sdk-contracts/consumer-me';
|
|
1
2
|
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 VerificationScenarioKey = "queue" | "admission-cycle" | "appointment" | "auth-queue" | "access-code-queue" | "draw" | "auth-draw" | "timed-release" | "timed-release-completion" | "waitlist-lifecycle" | "auction";
|
|
3
4
|
export type VerificationAction = {
|
|
4
5
|
type: "clickButton";
|
|
5
6
|
label: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "clickGridcell";
|
|
9
|
+
label: string;
|
|
6
10
|
} | {
|
|
7
11
|
type: "fillCredential";
|
|
8
12
|
value: string;
|
|
@@ -21,6 +25,10 @@ export interface VerificationCheckpoint {
|
|
|
21
25
|
visibleActions?: string[];
|
|
22
26
|
hiddenActions?: string[];
|
|
23
27
|
timerVisible?: boolean;
|
|
28
|
+
waitlistStatus?: "not_waitlisted" | "waitlisted" | "notified";
|
|
29
|
+
waitlistAbsent?: boolean;
|
|
30
|
+
/** Pin the ended sequence's outcome type from the bridge snapshot — ended-state copy is shared across most outcomes, so text checkpoints alone cannot distinguish them. */
|
|
31
|
+
outcomeType?: SequenceOutcomeType;
|
|
24
32
|
}
|
|
25
33
|
export interface VerificationStep {
|
|
26
34
|
key: string;
|
|
@@ -1 +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"],
|
|
1
|
+
const e="sdk-harness@example.com",t="424242",i="WRONG1",n="VIP123",s=["queue","admission-cycle","appointment","auth-queue","access-code-queue","draw","auth-draw","timed-release","timed-release-completion","auction"],o=["queue","admission-cycle","auth-queue","access-code-queue","draw","timed-release","auction"],a={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"]}}]},appointment:{key:"appointment",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleTexts:["Book an Appointment"],visibleActions:["Select a time"]},steps:[{key:"selected-slot",actions:[{type:"clickGridcell",label:"9:00 AM"}],checkpoint:{journeyStage:"routed",sequencePhase:"enterable",visibleTexts:["Book an Appointment"],visibleActions:["Continue · 9:00 AM"]}},{key:"booked-appointment",actions:[{type:"clickButton",label:"Continue · 9:00 AM"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["Appointment Booked","Suite 100","9:00 AM"],visibleActions:["Reschedule","Cancel"]}},{key:"started-reschedule",actions:[{type:"clickButton",label:"Reschedule"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["Reschedule Appointment"],visibleActions:["Select a time"]}},{key:"selected-reschedule-slot",actions:[{type:"clickGridcell",label:"10:30 AM"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["Reschedule Appointment"],visibleActions:["Continue · 10:30 AM"]}},{key:"rescheduled-appointment",actions:[{type:"clickButton",label:"Continue · 10:30 AM"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["Appointment Booked","Suite 100","10:30 AM"],visibleActions:["Reschedule","Cancel"]}}]},"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:"granted",visibleTexts:["You're in!","Continue to Shop"],visibleActions:["Continue to Shop"],timerVisible:!0}}]},"timed-release-completion":{key:"timed-release-completion",initial:{journeyStage:"routed",sequencePhase:"enterable",visibleTexts:["Flash Sale","Limited time access","Window closes in"],visibleActions:["Enter Sale"],timerVisible:!0},steps:[{key:"entered-grantless-sale",actions:[{type:"clickButton",label:"Enter Sale"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",visibleTexts:["You're in!","Complete purchase in","Browse and complete your purchase"],visibleActions:["Complete Purchase"],hiddenActions:["Continue to Shop"],timerVisible:!0}},{key:"completed-sale",actions:[{type:"clickButton",label:"Complete Purchase"}],checkpoint:{journeyStage:"routed",sequencePhase:"ended",outcomeType:"completed",visibleTexts:["Purchase complete","Thank you for your purchase"],hiddenActions:["Complete Purchase","Continue to Shop"]}}]},"waitlist-lifecycle":{key:"waitlist-lifecycle",initial:{journeyStage:"routed",sequencePhase:"scheduled",waitlistStatus:"not_waitlisted",visibleActions:["Enter Waitlist"],hiddenActions:["Enter Queue"]},steps:[{key:"joined-waitlist",actions:[{type:"clickButton",label:"Enter Waitlist"}],checkpoint:{journeyStage:"routed",sequencePhase:"scheduled",waitlistStatus:"waitlisted",visibleTexts:["You're on the Waitlist!"],visibleActions:["Leave Waitlist"],hiddenActions:["Enter Queue"]}},{key:"notified-waitlist",actions:[],checkpoint:{journeyStage:"routed",sequencePhase:"scheduled",waitlistStatus:"notified",visibleActions:["Leave Waitlist"],hiddenActions:["Enter Queue"]}},{key:"opened-queue",actions:[],checkpoint:{journeyStage:"routed",sequencePhase:"enterable",waitlistAbsent:!0,visibleActions:["Enter Queue"],hiddenActions:["Enter Waitlist","Leave Waitlist"]}},{key:"entered-queue",actions:[{type:"clickButton",label:"Enter Queue"}],checkpoint:{journeyStage:"routed",sequencePhase:"participating",waitlistAbsent:!0,visibleTexts:["You're in the Queue","Estimated wait"],visibleActions:["Leave Queue"]}}]},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 c(e){return e in a}function u(e){return a[e]}export{o 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,a as SDK_VERIFICATION_SCENARIOS,s as SHARED_HARNESS_VERIFICATION_SCENARIOS,u as getVerificationScenario,c as isVerificationScenarioKey};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DistributionMonitor, MonitorUpdate } from '../experiences/distribution-monitor.types';
|
|
2
|
+
import { DistributionSummary, JourneyEvent, JourneyEventAudience, JourneyEventKind, JourneyEventSeverity, MechanismConsumerState, Participation, SequenceOutcome, SequenceState } from '../experiences/journey.types';
|
|
3
|
+
import { SequenceDriver, SequenceProjectionFacts } from '../experiences/sequence-driver';
|
|
4
|
+
import { TimedReleaseModule } from './types';
|
|
5
|
+
type TimedReleaseMonitorModule = TimedReleaseModule & Pick<DistributionMonitor, "startMonitoring" | "stopMonitoring">;
|
|
6
|
+
/**
|
|
7
|
+
* Narrow shell and module capabilities required by the timed-release driver.
|
|
8
|
+
*/
|
|
9
|
+
export interface TimedReleaseDriverDependencies {
|
|
10
|
+
/** Timed-release module functions used by timed-release actions and monitoring. */
|
|
11
|
+
timedReleases: Pick<TimedReleaseMonitorModule, "enter" | "leave" | "complete" | "startMonitoring" | "stopMonitoring">;
|
|
12
|
+
/** Serialize a timed-release action with the shell's operation queue. */
|
|
13
|
+
runSerializedOperation(operation: () => Promise<void>): Promise<void>;
|
|
14
|
+
/** Read the current routed sequence before validating a timed-release action. */
|
|
15
|
+
getCurrentSequence(): SequenceState | undefined;
|
|
16
|
+
/** Commit a concrete next timed-release sequence into the shell. */
|
|
17
|
+
commit(next: SequenceState, opts?: {
|
|
18
|
+
events?: JourneyEvent[];
|
|
19
|
+
}): void;
|
|
20
|
+
/** Apply shell-owned grant and terminal lifecycle side effects. */
|
|
21
|
+
applyMonitorUpdate(update: MonitorUpdate): void;
|
|
22
|
+
/** Ensure the shell-owned display atom exists for this timed-release participation. */
|
|
23
|
+
ensureDisplayAtom(participation: Participation): void;
|
|
24
|
+
/** Stop shell runtime monitoring and optionally clear display state. */
|
|
25
|
+
stopRuntimeMonitoring(clearDisplay?: boolean): void;
|
|
26
|
+
/** Append a shell-visible journey event to the snapshot. */
|
|
27
|
+
emitEvent(kind: JourneyEventKind, severity: JourneyEventSeverity, audience: JourneyEventAudience, message: string, detail?: Record<string, unknown>): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create the timed-release sequence driver for one journey instance.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createTimedReleaseDriver(deps: TimedReleaseDriverDependencies): SequenceDriver;
|
|
33
|
+
/**
|
|
34
|
+
* Project timed-release server facts into a timed-release sequence state.
|
|
35
|
+
*/
|
|
36
|
+
export declare function projectTimedReleaseSequence(facts: SequenceProjectionFacts): SequenceState;
|
|
37
|
+
/**
|
|
38
|
+
* Return the timed-release consumer state that represents an issued grant.
|
|
39
|
+
*/
|
|
40
|
+
export declare function withTimedReleaseGrantedConsumer(consumer: MechanismConsumerState | undefined, distribution: DistributionSummary): MechanismConsumerState;
|
|
41
|
+
/**
|
|
42
|
+
* Return the timed-release consumer state that represents a terminal outcome.
|
|
43
|
+
*/
|
|
44
|
+
export declare function withTimedReleaseTerminalConsumer(consumer: MechanismConsumerState | undefined, outcome?: SequenceOutcome): MechanismConsumerState | undefined;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createError as e}from"../core/errors.js";function t(e){return new r(e)}function i(t){const{distribution:i,consumer:s,waitlist:n,grant:r,outcome:o}=t;if(!i)return{phase:"unavailable"};const c=function(t){if("timed_release"!==t.type)throw e.validationError("Timed-release driver received a non-timed-release distribution");return t}(i),u=s&&"timed_release"===s.mechanism?function(e){const{mechanism:t,...i}=e;return i}(s):a(i);if(r)return"completed"===o?.type?d(c,u,o):r.expiresAt&&Date.now()>new Date(r.expiresAt).getTime()?"open"===c.lifecycle?{phase:"enterable",mechanism:"timed_release",distribution:c,consumer:a(c)}:d(c,u,{type:"expired"}):{phase:"granted",mechanism:"timed_release",distribution:c,consumer:u,grant:r};const m=o??function(e){return"completed"===e.status?{type:"completed"}:"denied"===e.status?{type:"denied",reason:e.deniedReason}:"left"===e.status?{type:"left"}:void 0}(u);if(m)return d(c,u,m);if(function(e){return"entered"===e.status}(u))return{phase:"participating",mechanism:"timed_release",distribution:c,consumer:u};if("scheduled"===c.lifecycle){const e=n&&n.waitlistId===c.waitlistId?n:c.waitlistId?{waitlistId:c.waitlistId,status:"not_waitlisted"}:void 0;return{phase:"scheduled",mechanism:"timed_release",distribution:c,...e?{waitlist:e}:{}}}return"open"===c.lifecycle?{phase:"enterable",mechanism:"timed_release",distribution:c,consumer:u}:d(c,u,{type:"closed"})}function s(e,t){return"timed_release"===e?.mechanism?{...e,status:"completed"}:{mechanism:"timed_release",status:"completed",id:t.id,distributionId:t.id}}function n(e,t){return e&&"timed_release"===e.mechanism&&t?"left"===t.type?{...e,status:"left"}:"denied"===t.type?{...e,status:"denied",deniedReason:t.reason}:"completed"===t.type?{...e,status:"completed"}:{...e,status:"not_entered"}:e}class r{constructor(e){this.deps=e,this.mechanism="timed_release"}project(e){return i(e)}buildView(t,i){if("unavailable"===t.phase)return{phase:"unavailable",reason:t.reason};const s=function(t){if(!("mechanism"in t)||"timed_release"!==t.mechanism)throw e.validationError("Timed-release driver received a non-timed-release sequence");return t}(t);switch(s.phase){case"scheduled":return{phase:"scheduled",mechanism:"timed_release",distribution:s.distribution,startsAt:s.distribution.startsAt??s.distribution.opensAt,waitlist:s.waitlist?{status:s.waitlist.status,join:()=>i.joinWaitlist(),leave:()=>i.exitWaitlist()}:void 0};case"enterable":return{phase:"enterable",mechanism:"timed_release",distribution:s.distribution,consumer:s.consumer,enter:()=>this.enter()};case"participating":return{phase:"participating",mechanism:"timed_release",consumer:s.consumer,display$:i.displayAtom,leave:()=>this.leave(),complete:()=>this.complete()};case"granted":return{phase:"granted",mechanism:"timed_release",distribution:s.distribution,consumer:s.consumer,grant:s.grant,claim:()=>i.claim()};case"ended":return{phase:"ended",mechanism:"timed_release",consumer:s.consumer,outcome:s.outcome}}}startMonitoring(e){this.activeMonitorId=e.participation.id,this.deps.timedReleases.startMonitoring(e.participation.id,{displayAtom:e.displayAtom},e.onUpdate)}stopMonitoring(){this.activeMonitorId&&(this.deps.timedReleases.stopMonitoring(this.activeMonitorId),this.activeMonitorId=void 0)}async enter(){await this.deps.runSerializedOperation(async()=>{await this.enterWithinOperation()})}async enterWithinOperation(){const e=this.currentTimedReleaseSequence("enterable","No open timed release to enter").distribution,t=function(e){if("ENTERED"===e.status)return e.admissionGrant?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null;if("COMPLETED"===e.status)return{type:"ended",outcome:{type:"completed"}};return{type:"ended",outcome:{type:"LEFT"===e.status?"left":"denied",reason:"reason"in e?e.reason:void 0}}}(await this.deps.timedReleases.enter(e.id,void 0));if(t)return void this.deps.applyMonitorUpdate(t);const s={mechanism:"timed_release",status:"entered",id:n=e.id,distributionId:n};var n;this.deps.ensureDisplayAtom({id:e.id,type:"timed_release"}),this.deps.commit(i({distribution:e,consumer:s})),this.deps.emitEvent("sequence_change","success","user","Entered timed release",{distributionId:e.id})}async leave(){await this.deps.runSerializedOperation(async()=>{await this.leaveWithinOperation()})}async leaveWithinOperation(){const e=this.currentTimedReleaseSequence("participating"),t=e.consumer.distributionId??e.consumer.id??e.distribution.id;this.deps.stopRuntimeMonitoring(!0),await this.deps.timedReleases.leave(t),this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"left"}}),this.deps.emitEvent("sequence_change","info","user","Left participation",{type:"timed_release"})}async complete(){await this.deps.runSerializedOperation(async()=>{const e=this.currentTimedReleaseSequence("participating"),t=e.consumer.distributionId??e.consumer.id??e.distribution.id,i=this.deps.timedReleases.complete(t);i?this.deps.applyMonitorUpdate({type:"granted",token:i}):this.deps.applyMonitorUpdate({type:"ended",outcome:{type:"completed"}}),this.deps.emitEvent("sequence_change","success","user","Completed timed release",{timedReleaseId:t})})}currentTimedReleaseSequence(t,i="Not participating in a timed release"){const s=this.deps.getCurrentSequence();if(!s||s.phase!==t||!("mechanism"in s)||"timed_release"!==s.mechanism)throw e.validationError(i);return s}}function a(e){return{status:"not_entered",distributionId:e.id}}function d(e,t,i){return{phase:"ended",mechanism:"timed_release",distribution:e,consumer:t,outcome:{...i,distributionType:i.distributionType??e.type,distributionId:i.distributionId??e.id,at:i.at??/* @__PURE__ */(new Date).toISOString()}}}export{t as createTimedReleaseDriver,i as projectTimedReleaseSequence,s as withTimedReleaseGrantedConsumer,n as withTimedReleaseTerminalConsumer};
|
|
@@ -16,7 +16,12 @@ export declare class TimedReleaseManagementModule implements TimedReleaseModule
|
|
|
16
16
|
get(timedReleaseId: string): Promise<TimedRelease>;
|
|
17
17
|
enter(timedReleaseId: string, variantId?: string, options?: RequestOptions): Promise<TimedReleaseConsumerState>;
|
|
18
18
|
leave(timedReleaseId: string, options?: RequestOptions): Promise<void>;
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Admit = enter: the admission credential is minted and returned at entry, so
|
|
21
|
+
* finishing makes no network call — it resolves the credential held from entry
|
|
22
|
+
* (and refreshed by status re-mints) for the checkout claim.
|
|
23
|
+
*/
|
|
24
|
+
complete(timedReleaseId: string): string | undefined;
|
|
20
25
|
status(timedReleaseId: string): Promise<TimedReleaseConsumerState>;
|
|
21
26
|
startMonitoring(id: string, context?: Record<string, unknown>, onUpdate?: (update: MonitorUpdate) => void): void;
|
|
22
27
|
stopMonitoring(id: string): void;
|
|
@@ -1 +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}}
|
|
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}}complete(t){return this.trackedParticipations.get(t)?.admissionGrant}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}){switch(e.status){case"ENTERED":return{trackedParticipation:{timedReleaseId:t,enteredAt:e.enteredAt,selectedVariantId:e.selectedVariantId,admissionGrant:e.admissionGrant,expiresAt:e.expiresAt},events:"enter"===i?[{type:"timed_release:entered",payload:{timedReleaseId:t,enteredAt:e.enteredAt}}]:[],monitorUpdate:e.admissionGrant?{type:"granted",token:e.admissionGrant,expiresAt:e.expiresAt?new Date(e.expiresAt).getTime():void 0}:null,stopMonitoring:!1};case"COMPLETED":return{trackedParticipation:null,events:[{type:"timed_release:completed",payload:{timedReleaseId:t,completedAt:e.completedAt}}],monitorUpdate:"status"===i?{type:"ended",outcome:{type:"completed"}}:null,stopMonitoring:"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,227 @@
|
|
|
1
|
+
import { AdmissionGrant, SequenceOutcome, TimedReleaseConsumerStatus } from '@fanfare-io/fanfare-sdk-contracts/consumer-me';
|
|
2
|
+
import { ReadableAtom } from 'nanostores';
|
|
3
|
+
import { TimedReleaseDisplayState } from '../experiences/distribution-monitor.types';
|
|
4
|
+
import { DistributionSummary, WaitlistAttachment, WaitlistAttachmentView } from '../experiences/journey.types';
|
|
5
|
+
/**
|
|
6
|
+
* Timed release participation facts for one consumer.
|
|
7
|
+
*
|
|
8
|
+
* The server is authoritative for every field. Timestamps are ISO 8601 strings.
|
|
9
|
+
* Optional fields are present only after the matching timed release lifecycle
|
|
10
|
+
* event has occurred.
|
|
11
|
+
*/
|
|
12
|
+
export interface TimedReleaseSequenceConsumerState {
|
|
13
|
+
/** Server timed release status for this consumer. */
|
|
14
|
+
status: TimedReleaseConsumerStatus;
|
|
15
|
+
/** Timed release participation id, when the server has created one. */
|
|
16
|
+
id?: string;
|
|
17
|
+
/** Distribution id this timed release participation belongs to. */
|
|
18
|
+
distributionId?: string;
|
|
19
|
+
/** ISO 8601 timestamp for when the consumer entered the timed release. */
|
|
20
|
+
enteredAt?: string;
|
|
21
|
+
/** Opaque admission credential token, when one has been issued inline. */
|
|
22
|
+
admissionGrant?: string;
|
|
23
|
+
/** ISO 8601 timestamp for when the consumer was granted admission. */
|
|
24
|
+
grantedAt?: string;
|
|
25
|
+
/** Server-provided denial reason for denied timed release outcomes. */
|
|
26
|
+
deniedReason?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A timed release sequence as seen by one consumer.
|
|
30
|
+
*
|
|
31
|
+
* Lifecycle: scheduled -> enterable -> participating -> granted -> ended. Entry
|
|
32
|
+
* reserves the timed release position, the participating display tracks the
|
|
33
|
+
* live release window, and completion may surface the held admission grant.
|
|
34
|
+
*/
|
|
35
|
+
export type TimedReleaseSequence = TimedReleaseScheduled | TimedReleaseEnterable | TimedReleaseParticipating | TimedReleaseGranted | TimedReleaseEnded;
|
|
36
|
+
/**
|
|
37
|
+
* The timed release exists but entry has not opened yet.
|
|
38
|
+
*/
|
|
39
|
+
export interface TimedReleaseScheduled {
|
|
40
|
+
/** Mechanism discriminator for timed release narrowing. */
|
|
41
|
+
mechanism: "timed_release";
|
|
42
|
+
/** Phase discriminator; scheduled timed releases cannot be entered yet. */
|
|
43
|
+
phase: "scheduled";
|
|
44
|
+
/** Timed release distribution facts from the journey projector. */
|
|
45
|
+
distribution: DistributionSummary & {
|
|
46
|
+
type: "timed_release";
|
|
47
|
+
};
|
|
48
|
+
/** Scheduled-only waitlist attachment, cleared when the timed release opens. */
|
|
49
|
+
waitlist?: WaitlistAttachment;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The timed release is open and this consumer has not entered it.
|
|
53
|
+
*/
|
|
54
|
+
export interface TimedReleaseEnterable {
|
|
55
|
+
/** Mechanism discriminator for timed release narrowing. */
|
|
56
|
+
mechanism: "timed_release";
|
|
57
|
+
/** Phase discriminator; `enter()` is legal on the matching view. */
|
|
58
|
+
phase: "enterable";
|
|
59
|
+
/** Open timed release distribution facts from the journey projector. */
|
|
60
|
+
distribution: DistributionSummary & {
|
|
61
|
+
type: "timed_release";
|
|
62
|
+
};
|
|
63
|
+
/** Consumer timed release facts before entry. */
|
|
64
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The consumer has entered the timed release.
|
|
68
|
+
*/
|
|
69
|
+
export interface TimedReleaseParticipating {
|
|
70
|
+
/** Mechanism discriminator for timed release narrowing. */
|
|
71
|
+
mechanism: "timed_release";
|
|
72
|
+
/** Phase discriminator; timed release display is live while participating. */
|
|
73
|
+
phase: "participating";
|
|
74
|
+
/** Timed release distribution facts from the journey projector. */
|
|
75
|
+
distribution: DistributionSummary & {
|
|
76
|
+
type: "timed_release";
|
|
77
|
+
};
|
|
78
|
+
/** Consumer timed release participation facts. */
|
|
79
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The timed release has issued an admission grant to this consumer.
|
|
83
|
+
*/
|
|
84
|
+
export interface TimedReleaseGranted {
|
|
85
|
+
/** Mechanism discriminator for timed release narrowing. */
|
|
86
|
+
mechanism: "timed_release";
|
|
87
|
+
/** Phase discriminator; `claim()` is legal on the matching view. */
|
|
88
|
+
phase: "granted";
|
|
89
|
+
/** Timed release distribution facts from the journey projector. */
|
|
90
|
+
distribution: DistributionSummary & {
|
|
91
|
+
type: "timed_release";
|
|
92
|
+
};
|
|
93
|
+
/** Consumer timed release facts associated with the grant. */
|
|
94
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
95
|
+
/** Admission credential issued by the server for this timed release. */
|
|
96
|
+
grant: AdmissionGrant;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The timed release journey is terminal for this consumer.
|
|
100
|
+
*/
|
|
101
|
+
export interface TimedReleaseEnded {
|
|
102
|
+
/** Mechanism discriminator for timed release narrowing. */
|
|
103
|
+
mechanism: "timed_release";
|
|
104
|
+
/** Phase discriminator; no timed release actions are legal after this phase. */
|
|
105
|
+
phase: "ended";
|
|
106
|
+
/** Timed release distribution facts from the journey projector. */
|
|
107
|
+
distribution: DistributionSummary & {
|
|
108
|
+
type: "timed_release";
|
|
109
|
+
};
|
|
110
|
+
/** Consumer timed release facts at the terminal outcome. */
|
|
111
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
112
|
+
/** Server-derived terminal outcome for the timed release participation. */
|
|
113
|
+
outcome: SequenceOutcome;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Timed release views exposed by `JourneyView.sequence`.
|
|
117
|
+
*
|
|
118
|
+
* The view lifecycle mirrors the timed release sequence lifecycle. Actions exist
|
|
119
|
+
* only on the phases where they are legal, and each action may reject if the
|
|
120
|
+
* server has moved the sequence before the request is handled.
|
|
121
|
+
*/
|
|
122
|
+
export type TimedReleaseSequenceView = TimedReleaseScheduledView | TimedReleaseEnterableView | TimedReleaseParticipatingView | TimedReleaseGrantedView | TimedReleaseEndedView;
|
|
123
|
+
/**
|
|
124
|
+
* Scheduled timed release view rendered before entry opens.
|
|
125
|
+
*/
|
|
126
|
+
export interface TimedReleaseScheduledView {
|
|
127
|
+
/** Phase discriminator; no timed release action is legal yet. */
|
|
128
|
+
phase: "scheduled";
|
|
129
|
+
/** Mechanism discriminator for timed release rendering. */
|
|
130
|
+
mechanism: "timed_release";
|
|
131
|
+
/** Timed release distribution facts from the journey projector. */
|
|
132
|
+
distribution: DistributionSummary & {
|
|
133
|
+
type: "timed_release";
|
|
134
|
+
};
|
|
135
|
+
/** ISO 8601 opening timestamp chosen from the distribution start/open time. */
|
|
136
|
+
startsAt?: string;
|
|
137
|
+
/** Scheduled-only waitlist actions; this attachment never grants access. */
|
|
138
|
+
waitlist?: WaitlistAttachmentView;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Enterable timed release view rendered when the consumer can enter.
|
|
142
|
+
*/
|
|
143
|
+
export interface TimedReleaseEnterableView {
|
|
144
|
+
/** Phase discriminator; `enter()` is legal on this view. */
|
|
145
|
+
phase: "enterable";
|
|
146
|
+
/** Mechanism discriminator for timed release rendering. */
|
|
147
|
+
mechanism: "timed_release";
|
|
148
|
+
/** Open timed release distribution facts from the journey projector. */
|
|
149
|
+
distribution: DistributionSummary & {
|
|
150
|
+
type: "timed_release";
|
|
151
|
+
};
|
|
152
|
+
/** Consumer timed release facts before entry. */
|
|
153
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
154
|
+
/**
|
|
155
|
+
* Enter the timed release.
|
|
156
|
+
*
|
|
157
|
+
* Legal only while this view is current. The promise rejects if entry is no
|
|
158
|
+
* longer allowed or the server rejects the participation request.
|
|
159
|
+
*/
|
|
160
|
+
enter(): Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Participating timed release view rendered after entry.
|
|
164
|
+
*/
|
|
165
|
+
export interface TimedReleaseParticipatingView {
|
|
166
|
+
/** Phase discriminator; `leave()` and `complete()` are legal on this view. */
|
|
167
|
+
phase: "participating";
|
|
168
|
+
/** Mechanism discriminator for timed release rendering. */
|
|
169
|
+
mechanism: "timed_release";
|
|
170
|
+
/** Consumer timed release participation facts. */
|
|
171
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
172
|
+
/** Live timed release display facts, such as release timing and availability. */
|
|
173
|
+
display$: ReadableAtom<TimedReleaseDisplayState>;
|
|
174
|
+
/**
|
|
175
|
+
* Leave the timed release participation.
|
|
176
|
+
*
|
|
177
|
+
* Legal only while this view is current. The promise rejects if the consumer
|
|
178
|
+
* is no longer participating or the server rejects the leave request.
|
|
179
|
+
*/
|
|
180
|
+
leave(): Promise<void>;
|
|
181
|
+
/**
|
|
182
|
+
* Complete the timed release participation.
|
|
183
|
+
*
|
|
184
|
+
* Legal only while this view is current. The promise rejects if completion is
|
|
185
|
+
* no longer allowed; success moves the journey to granted or ended depending
|
|
186
|
+
* on the server-held credential.
|
|
187
|
+
*/
|
|
188
|
+
complete(): Promise<void>;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Granted timed release view rendered after completion exposes a credential.
|
|
192
|
+
*/
|
|
193
|
+
export interface TimedReleaseGrantedView {
|
|
194
|
+
/** Phase discriminator; `claim()` is legal on this view. */
|
|
195
|
+
phase: "granted";
|
|
196
|
+
/** Mechanism discriminator for timed release rendering. */
|
|
197
|
+
mechanism: "timed_release";
|
|
198
|
+
/** Timed release distribution facts from the journey projector. */
|
|
199
|
+
distribution: DistributionSummary & {
|
|
200
|
+
type: "timed_release";
|
|
201
|
+
};
|
|
202
|
+
/** Consumer timed release facts associated with the grant. */
|
|
203
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
204
|
+
/** Admission credential issued by the server for this timed release. */
|
|
205
|
+
grant: AdmissionGrant;
|
|
206
|
+
/**
|
|
207
|
+
* Claim the timed release admission credential for handoff.
|
|
208
|
+
*
|
|
209
|
+
* Legal only while this view is current. It returns the credential
|
|
210
|
+
* synchronously and may throw if the journey has already left the granted
|
|
211
|
+
* phase.
|
|
212
|
+
*/
|
|
213
|
+
claim(): AdmissionGrant;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Ended timed release view rendered once the journey is terminal.
|
|
217
|
+
*/
|
|
218
|
+
export interface TimedReleaseEndedView {
|
|
219
|
+
/** Phase discriminator; no timed release action is legal after this phase. */
|
|
220
|
+
phase: "ended";
|
|
221
|
+
/** Mechanism discriminator for timed release rendering. */
|
|
222
|
+
mechanism: "timed_release";
|
|
223
|
+
/** Consumer timed release facts at the terminal outcome. */
|
|
224
|
+
consumer: TimedReleaseSequenceConsumerState;
|
|
225
|
+
/** Server-derived terminal outcome for the timed release participation. */
|
|
226
|
+
outcome: SequenceOutcome;
|
|
227
|
+
}
|
|
@@ -21,6 +21,9 @@ export interface TimedReleaseParticipation {
|
|
|
21
21
|
timedReleaseId: string;
|
|
22
22
|
enteredAt: string;
|
|
23
23
|
selectedVariantId?: string;
|
|
24
|
+
/** Admission credential held from entry; refreshed on every status re-mint. */
|
|
25
|
+
admissionGrant?: string;
|
|
26
|
+
expiresAt?: string;
|
|
24
27
|
}
|
|
25
28
|
/**
|
|
26
29
|
* Timed Release consumer states (from backend)
|
|
@@ -33,6 +36,13 @@ export interface TimedReleaseEnteredState {
|
|
|
33
36
|
status: "ENTERED";
|
|
34
37
|
enteredAt: string;
|
|
35
38
|
selectedVariantId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Admit = enter: the admission credential is minted at entry and held from
|
|
41
|
+
* then on. Status reads re-mint it statelessly (a byte-different token with the
|
|
42
|
+
* same standing).
|
|
43
|
+
*/
|
|
44
|
+
admissionGrant?: string;
|
|
45
|
+
expiresAt?: string;
|
|
36
46
|
}
|
|
37
47
|
export interface TimedReleaseCompletedState {
|
|
38
48
|
status: "COMPLETED";
|
|
@@ -55,7 +65,12 @@ export interface TimedReleaseModule {
|
|
|
55
65
|
get(timedReleaseId: string): Promise<TimedRelease>;
|
|
56
66
|
enter(timedReleaseId: string, variantId?: string, options?: RequestOptions): Promise<TimedReleaseConsumerState>;
|
|
57
67
|
leave(timedReleaseId: string, options?: RequestOptions): Promise<void>;
|
|
58
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Admit = enter: resolves the admission credential held from entry for the
|
|
70
|
+
* checkout claim. No network call — the credential was minted and returned at
|
|
71
|
+
* entry and refreshed by status re-mints.
|
|
72
|
+
*/
|
|
73
|
+
complete(timedReleaseId: string): string | undefined;
|
|
59
74
|
status(timedReleaseId: string): Promise<TimedReleaseConsumerState>;
|
|
60
75
|
}
|
|
61
76
|
/**
|
|
@@ -35,7 +35,7 @@ export type SdkHandoffDistributionType = Exclude<SdkDistributionType, "timed_rel
|
|
|
35
35
|
/**
|
|
36
36
|
* Distribution types that participate in journey resume / participation tracking.
|
|
37
37
|
*/
|
|
38
|
-
export type SdkJourneyParticipationType =
|
|
38
|
+
export type SdkJourneyParticipationType = SdkDistributionEntryType;
|
|
39
39
|
/**
|
|
40
40
|
* Distribution types that produce admissions for journey resume.
|
|
41
41
|
* Excludes `appointment` (booking is the participation; there is no admission
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.
|
|
1
|
+
const o="0.4.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.4.0",
|
|
4
4
|
"description": "Official Fanfare Browser SDK for queue, draw, auction, and appointment experiences",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"uuid": "^14.0.0",
|
|
123
123
|
"wretch": "^2.11.0",
|
|
124
124
|
"zustand": "^5.0.6",
|
|
125
|
-
"@fanfare-io/fanfare-sdk-contracts": "0.
|
|
125
|
+
"@fanfare-io/fanfare-sdk-contracts": "0.4.0"
|
|
126
126
|
},
|
|
127
127
|
"peerDependencies": {
|
|
128
128
|
"valibot": "^1.1.0",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"vite-plugin-dts": "^4.5.4",
|
|
166
166
|
"vite-tsconfig-paths": "^5.1.4",
|
|
167
167
|
"vitest": "^3.2.4",
|
|
168
|
-
"@fanfare-io/shared-models": "0.
|
|
168
|
+
"@fanfare-io/shared-models": "0.4.0"
|
|
169
169
|
},
|
|
170
170
|
"sideEffects": false,
|
|
171
171
|
"keywords": [
|