@fanfare-io/fanfare-sdk-react 0.11.0 → 0.13.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.
Files changed (33) hide show
  1. package/dist/components/checkout/checkout-processing-panel.d.ts +10 -0
  2. package/dist/components/checkout/index.d.ts +4 -0
  3. package/dist/components/checkout/payment-collection-panel.d.ts +18 -0
  4. package/dist/components/checkout/receipt-panel.d.ts +10 -0
  5. package/dist/components/checkout/reservation-checkout-panel.d.ts +18 -0
  6. package/dist/components/checkout/stripe-card-field.d.ts +29 -0
  7. package/dist/components/checkout/stripe-client.d.ts +71 -0
  8. package/dist/components/checkout/use-payment-challenge.d.ts +32 -0
  9. package/dist/components/checkout/use-payment-method-selection.d.ts +8 -0
  10. package/dist/components/compositions/index.d.ts +1 -1
  11. package/dist/components/compositions/outcome-panel.d.ts +12 -1
  12. package/dist/components/module/index.d.ts +2 -0
  13. package/dist/components/module/stage-steps.d.ts +26 -0
  14. package/dist/components/widgets/experience-widget.d.ts +86 -3
  15. package/dist/components/widgets/internal/auction-module.d.ts +4 -1
  16. package/dist/components/widgets/internal/draw-module.d.ts +4 -1
  17. package/dist/components/widgets/internal/queue-module.d.ts +4 -1
  18. package/dist/components/widgets/internal/timed-release-module.d.ts +4 -1
  19. package/dist/hooks/use-fanfare-dom-bridge.d.ts +57 -0
  20. package/dist/hooks/use-sdk-event.d.ts +20 -0
  21. package/dist/index-DSJ9q_Zs.js +6157 -0
  22. package/dist/index.d.ts +6 -3
  23. package/dist/index.js +60 -5192
  24. package/dist/lib/currency.d.ts +10 -0
  25. package/dist/stripe-card-field-wp_QQxeN.js +170 -0
  26. package/dist/styles/base.css +1 -1
  27. package/package.json +19 -16
  28. package/dist/components/widgets/draw/draw-actions.d.ts +0 -40
  29. package/dist/components/widgets/draw/index.d.ts +0 -6
  30. package/dist/components/widgets/queue/index.d.ts +0 -6
  31. package/dist/components/widgets/queue/queue-actions.d.ts +0 -36
  32. package/dist/components/widgets/timed-release/index.d.ts +0 -6
  33. package/dist/components/widgets/timed-release/timed-release-actions.d.ts +0 -38
@@ -0,0 +1,10 @@
1
+ import { CheckoutProcessingSlotProps } from '../widgets/experience-widget';
2
+ import * as React from "react";
3
+ /**
4
+ * Default processing panel shown for the gap between a submitted reservation checkout and the
5
+ * server's confirmation.
6
+ *
7
+ * The consumer must not navigate away while the payment settles, so the wait is announced rather
8
+ * than shown only as motion.
9
+ */
10
+ export declare function CheckoutProcessingPanel(_props: CheckoutProcessingSlotProps): React.ReactElement;
@@ -0,0 +1,4 @@
1
+ export { CheckoutProcessingPanel } from './checkout-processing-panel';
2
+ export { PaymentCollectionPanel } from './payment-collection-panel';
3
+ export { ReceiptPanel } from './receipt-panel';
4
+ export { ReservationCheckoutPanel } from './reservation-checkout-panel';
@@ -0,0 +1,18 @@
1
+ import { PaymentCollectionSlotProps } from '../widgets/experience-widget';
2
+ import * as React from "react";
3
+ /**
4
+ * Default payment-collection panel for pre-auth distributions, rendered both when the consumer is
5
+ * about to enter and when a failed authorization leaves a retry open.
6
+ *
7
+ * The hold amount is rendered from the view's own figure — the client never derives one.
8
+ *
9
+ * The configuration block reaches this panel on the entry mount only. A retry is offered from an
10
+ * ended view, which carries none, so that mount renders payment as unavailable and a consumer with
11
+ * a saved method submits without needing Stripe.js at all.
12
+ *
13
+ * Entry has no server-side resume: a paused authorization is carried forward by submitting the
14
+ * identical payment input again, held here from the first submit, so exactly one payment method is
15
+ * minted across the whole flow. An ended view carries no pending action, so the retry mount reaches
16
+ * none of this.
17
+ */
18
+ export declare function PaymentCollectionPanel({ preAuthAmount, preAuthCurrencyCode, savedPaymentMethods, paymentProvider, onEnter, isEntering, intent, pendingAction, onRetryPaymentConfig, ...slotProps }: PaymentCollectionSlotProps): React.ReactElement;
@@ -0,0 +1,10 @@
1
+ import { ReceiptSlotProps } from '../widgets/experience-widget';
2
+ import * as React from "react";
3
+ /**
4
+ * Default panel for a completed Fanfare-managed payment.
5
+ *
6
+ * The order reference is the outcome. An admission credential appears only on the arm that carries
7
+ * one, and stays subordinate to the purchase: payment is terminal, so a receipt must never read as
8
+ * the thing that admits its holder.
9
+ */
10
+ export declare function ReceiptPanel(props: ReceiptSlotProps): React.ReactElement;
@@ -0,0 +1,18 @@
1
+ import { ReservationCheckoutSlotProps } from '../widgets/experience-widget';
2
+ import * as React from "react";
3
+ /**
4
+ * Default reservation-checkout panel for post-win checkout flows.
5
+ *
6
+ * `Countdown`'s `onComplete` is a no-op: the journey machine owns the reservation's expiry (it
7
+ * arms its own timer and transitions the sequence to `ended` when the deadline passes), so the
8
+ * panel never disables itself locally — it simply stops being rendered once the phase changes.
9
+ *
10
+ * Elements renders only on the internal-processor arm. The discriminator is `processor`; an absent
11
+ * configuration block on that arm means the configuration read failed, which is a retryable outage
12
+ * inside a ticking window — never a signal that another processor is in play.
13
+ *
14
+ * A pending action adds visible substates over the same screen rather than replacing it: the card
15
+ * field stays mounted through the whole challenge, because a consumer whose bank prompt failed may
16
+ * need to submit again with the card they already typed.
17
+ */
18
+ export declare function ReservationCheckoutPanel({ reservation, savedPaymentMethods, processor, paymentProvider, pendingAction, onConfirm, onResume, isConfirming, onRetryPaymentConfig, ...slotProps }: ReservationCheckoutSlotProps): React.ReactElement;
@@ -0,0 +1,29 @@
1
+ import { StripeClientConfig } from './stripe-client';
2
+ /**
3
+ * The SDK's card-entry field: a Stripe Card Element and the controller that turns it into a
4
+ * payment-method id. It knows nothing about reservations, pre-auth, or amounts.
5
+ *
6
+ * `@stripe/stripe-js` is a type-only dependency across the SDK, so no vendor bytes ship in any
7
+ * chunk. This module is the one lazy split: it is reached only through a dynamic `import()` from
8
+ * the panel that renders new-card entry, so the Card Element wiring stays out of the adapter's
9
+ * entry chunk. Nothing statically imported from `src/index.ts` may reach it.
10
+ *
11
+ * Stripe.js is unsupported inside a shadow root, and the Solid adapter's web components attach one
12
+ * unconditionally, so the element mounts into a container this file appends to the light DOM and
13
+ * positions over its in-panel anchor. The theme's custom properties do not cross the shadow
14
+ * boundary, so the resolved values the card style needs are copied onto that container.
15
+ */
16
+ import * as React from "react";
17
+ export interface StripeCardController {
18
+ /** Resolves the `pm_…` id, or rejects with an `Error` carrying already-localized copy. */
19
+ createPaymentMethod: () => Promise<string>;
20
+ }
21
+ export interface StripeCardFieldProps {
22
+ config: StripeClientConfig;
23
+ onController: (controller: StripeCardController | null) => void;
24
+ onChange?: (state: {
25
+ complete: boolean;
26
+ errorMessage: string | null;
27
+ }) => void;
28
+ }
29
+ export declare function StripeCardField({ config, onController, onChange }: StripeCardFieldProps): React.ReactElement;
@@ -0,0 +1,71 @@
1
+ import { ReservationPaymentAffordance } from '@fanfare-io/fanfare-sdk-core/experiences';
2
+ import { Stripe, StripeConstructor, StripeElementStyle } from '@stripe/stripe-js';
3
+ /**
4
+ * The wire block a checkout-capable view carries, reached through the adapter's declared
5
+ * dependency on `fanfare-sdk-core` rather than the contracts package: core re-exports the contract
6
+ * types so adapter modules never resolve `@fanfare-io/fanfare-sdk-contracts` directly.
7
+ */
8
+ type PaymentProviderConfig = NonNullable<ReservationPaymentAffordance["paymentProvider"]>;
9
+ declare global {
10
+ interface Window {
11
+ Stripe?: StripeConstructor;
12
+ }
13
+ }
14
+ export interface StripeClientConfig {
15
+ publishableKey: string;
16
+ stripeAccount?: string;
17
+ }
18
+ /**
19
+ * Loads Stripe.js once per page. A failure is never retained: a memoized rejection would strand
20
+ * every subsequent payer on the page, not just the one whose load failed. Retry is the caller's
21
+ * decision — this module never schedules one.
22
+ */
23
+ export declare function loadStripeJs(): Promise<StripeConstructor>;
24
+ /**
25
+ * One Stripe client per (publishable key, connected account). A construction that rejects drops
26
+ * its cache entry for the same reason the loader does.
27
+ */
28
+ export declare function getStripe(config: StripeClientConfig): Promise<Stripe>;
29
+ /**
30
+ * Re-serializes a theme color into the spelling a CSS color parser produces.
31
+ *
32
+ * Custom properties are untyped, so a theme color arrives from `getComputedStyle` as whatever token
33
+ * stream the stylesheet declared — and a minifier is free to rewrite `oklch(0.63 0.24 25)` as
34
+ * `oklch(63% .24 25)`. Stripe's element options go through a stricter parser than CSS's: a spelling
35
+ * every browser accepts can still be rejected there, and the rejection is silent — the element
36
+ * mounts a frame that never renders its input. The invariant this restores is that colors reach
37
+ * Stripe in the serialization its parser accepts.
38
+ *
39
+ * `null` means the value is not a color the parser recognizes, and the caller must omit the key
40
+ * rather than guess: assigning an unparsable value leaves `fillStyle` on its previous one, so
41
+ * emitting the read-back would emit some other color entirely. Where no parser exists — server
42
+ * rendering, or a DOM without canvas — the value passes through: the mismatch only arises where a
43
+ * real browser serializes computed styles.
44
+ */
45
+ export declare function normalizeCssColor(value: string): string | null;
46
+ /**
47
+ * Maps the SDK theme's resolved custom properties onto the Card Element's `style` option. Stripe's
48
+ * `Appearance` API governs the Elements group Payment Element consumes and does not reach the
49
+ * legacy Card Element, so `style` is the only theming path — there is deliberately no second one.
50
+ *
51
+ * Property names are the values of `THEME_CSS_VARS` (the adapter's `theme/theme.types.ts`), spelled
52
+ * literally here because this file must stay identical across adapters. `--ff-color-primary` and
53
+ * `--ff-color-background` have no Card Element equivalent: the element's background is the host
54
+ * page's, supplied by the panel around the iframe.
55
+ *
56
+ * Every color is re-serialized on the way out; a color the parser rejects is omitted, which leaves
57
+ * Stripe on its own default for that slot. The font stack is not a color and is forwarded as read.
58
+ */
59
+ export declare function deriveStripeCardStyle(root: HTMLElement): StripeElementStyle;
60
+ /**
61
+ * The one binding between a view's wire configuration and a Stripe client.
62
+ *
63
+ * `null` means no usable Stripe client configuration reached this view. On a checkout-capable arm
64
+ * that has exactly one cause — the configuration read failed — because the processor discriminator
65
+ * is the `processor` field (reserved) or `checkoutMode` (enterable), never the presence of this
66
+ * block. The condition is transient and retryable.
67
+ */
68
+ export declare function resolveStripeClientConfig(source: PaymentProviderConfig | undefined): StripeClientConfig | null;
69
+ /** Test-only seam over every memo this module holds. Never exported from an adapter's public entry. */
70
+ export declare function resetStripeClientCache(): void;
71
+ export {};
@@ -0,0 +1,32 @@
1
+ import { StripeClientConfig } from './stripe-client';
2
+ /** `idle` covers both "no challenge yet" and "this attempt has settled". */
3
+ export type PaymentChallengePhase = "idle" | "challenging" | "resuming";
4
+ /**
5
+ * How the last attempt ended, when it ended badly — the fact a panel needs to offer the right way
6
+ * out, which the error copy alone cannot carry.
7
+ *
8
+ * - `cancelled` — the prompt ended without the card being refused: the consumer dismissed it, or the
9
+ * authentication behind it failed. Reopening that same prompt is the recovery.
10
+ * - `declined` — the card was refused, or the prompt could not be opened at all. That secret is
11
+ * spent; only a fresh submission can produce another.
12
+ * - `resume_failed` — the prompt succeeded and the operation behind it did not. The challenge is
13
+ * answered, so the recovery is to run that operation again, never to reopen the prompt.
14
+ */
15
+ export type PaymentChallengeRecovery = "cancelled" | "declined" | "resume_failed";
16
+ export interface PaymentChallenge {
17
+ phase: PaymentChallengePhase;
18
+ /** Localized copy for the attempt that produced it; terminal until another attempt starts. */
19
+ error: string | null;
20
+ /** Set together with `error`, and cleared when the next attempt starts. */
21
+ recovery: PaymentChallengeRecovery | null;
22
+ /**
23
+ * Open the bank prompt for `clientSecret`. Once per secret per instance: a repeat call carrying a
24
+ * secret this instance already launched is a no-op, and a genuinely new secret launches again.
25
+ */
26
+ launch: (clientSecret: string, resume: () => Promise<void>) => void;
27
+ /** Reopen the same prompt on the consumer's own instruction, then run the same resume. */
28
+ retry: (clientSecret: string, resume: () => Promise<void>) => void;
29
+ }
30
+ /** Test-only seam. Never exported from the adapter's public entry. */
31
+ export declare function resetPaymentChallengeState(): void;
32
+ export declare function usePaymentChallenge(config: StripeClientConfig | null): PaymentChallenge;
@@ -0,0 +1,8 @@
1
+ import { PaymentMethodSummary } from '@fanfare-io/fanfare-sdk-core/experiences';
2
+ /**
3
+ * Tracks the selected saved payment method for a checkout panel. The list can still be loading on
4
+ * mount (nothing selected yet) or a previously selected card can be removed out from under the
5
+ * panel — falls back to the default method, else the first, else `undefined`, both on initial
6
+ * mount and whenever the list changes, so submit never carries a stale/deleted `paymentMethodId`.
7
+ */
8
+ export declare function usePaymentMethodSelection(savedPaymentMethods: PaymentMethodSummary[]): [string | undefined, (id: string) => void];
@@ -10,6 +10,6 @@ export { JourneyGate, type JourneyGateProps, type JourneyGateStage } from './jou
10
10
  export { ChallengeGate, type ChallengeGateProps } from './challenge-gate';
11
11
  export { AccessCodeForm, type AccessCodeFormProps } from './access-code-form';
12
12
  export { GrantedPanel, type GrantedPanelProps } from './granted-panel';
13
- export { OutcomePanel, type OutcomePanelProps } from './outcome-panel';
13
+ export { OutcomePanel, type OutcomeAction, type OutcomePanelProps } from './outcome-panel';
14
14
  export { EndedModule, type EndedModuleProps } from './ended-module';
15
15
  export { ErrorView, type ErrorViewProps } from './error-view';
@@ -4,6 +4,15 @@ import { OutcomeReason } from '../module';
4
4
  * (e.g. the expired re-enter button label) so each module shows terminology
5
5
  * appropriate to its mechanism rather than queue-specific copy.
6
6
  */
7
+ /**
8
+ * A host-provided action for a terminal panel, such as "See what's still
9
+ * available" after a loss. The widget renders it as a secondary button; the
10
+ * host decides where it leads.
11
+ */
12
+ export interface OutcomeAction {
13
+ label: string;
14
+ onClick: () => void;
15
+ }
7
16
  export type ParticipationType = "queue" | "draw" | "auction" | "timed_release" | "appointment";
8
17
  export interface OutcomePanelProps {
9
18
  outcomeReason: OutcomeReason;
@@ -14,9 +23,11 @@ export interface OutcomePanelProps {
14
23
  participationType?: ParticipationType;
15
24
  onReenter?: () => Promise<void>;
16
25
  isReentering?: boolean;
26
+ /** Secondary action offered once the outcome is final. */
27
+ action?: OutcomeAction;
17
28
  className?: string;
18
29
  }
19
- export declare function OutcomePanel({ outcomeReason, participationType, onReenter, isReentering, className, }: OutcomePanelProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function OutcomePanel({ outcomeReason, participationType, onReenter, isReentering, action, className, }: OutcomePanelProps): import("react/jsx-runtime").JSX.Element;
20
31
  export declare namespace OutcomePanel {
21
32
  var displayName: string;
22
33
  }
@@ -13,6 +13,8 @@ export { PanelHeading } from './panel-heading';
13
13
  export type { PanelHeadingProps, PanelHeadingVariant } from './panel-heading';
14
14
  export { InfoPanel } from './info-panel';
15
15
  export type { InfoPanelProps } from './info-panel';
16
+ export { StageSteps } from './stage-steps';
17
+ export type { StageStep, StageStepStatus, StageStepsProps } from './stage-steps';
16
18
  export { AlertPanel } from './alert-panel';
17
19
  export type { AlertPanelProps } from './alert-panel';
18
20
  export { normalizeOutcomeReason, normalizeSequenceOutcomeReason } from './outcome-reason';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * StageSteps
3
+ *
4
+ * A row of named stages for a phase whose end time is unknown. Completed
5
+ * stages read solid, the active stage pulses and its connector flows toward
6
+ * the next, and pending stages stay outlined. It communicates where the
7
+ * consumer is in a process without implying a percentage or a deadline.
8
+ *
9
+ * <StageSteps
10
+ * steps={[
11
+ * { label: "You're in", status: "done" },
12
+ * { label: "Drawing", status: "active" },
13
+ * { label: "Results", status: "pending" },
14
+ * ]}
15
+ * />
16
+ */
17
+ import * as React from "react";
18
+ export type StageStepStatus = "done" | "active" | "pending";
19
+ export interface StageStep {
20
+ label: string;
21
+ status: StageStepStatus;
22
+ }
23
+ export interface StageStepsProps extends React.HTMLAttributes<HTMLDivElement> {
24
+ steps: readonly StageStep[];
25
+ }
26
+ export declare const StageSteps: React.ForwardRefExoticComponent<StageStepsProps & React.RefAttributes<HTMLDivElement>>;
@@ -1,7 +1,8 @@
1
1
  import { BotMitigationState, RoutingChallenge } from '@fanfare-io/fanfare-sdk-core/challenges';
2
- import { JourneyHandle, JourneySnapshot, JourneyView, SequenceView } from '@fanfare-io/fanfare-sdk-core/experiences';
2
+ import { CheckoutNextAction, CheckoutProcessor, FanfareCheckoutResult, JourneyHandle, JourneySnapshot, JourneyView, PaymentInput, PaymentMethodSummary, ReservationPaymentAffordance, SequenceView } from '@fanfare-io/fanfare-sdk-core/experiences';
3
3
  import { BrandTheme, WidgetVariant } from '../../theme';
4
4
  import { AuthInputMode } from '../auth/auth-input';
5
+ import { OutcomeAction } from '../compositions/outcome-panel';
5
6
  import * as React from "react";
6
7
  type ParticipationType = "queue" | "draw" | "auction" | "timed_release" | "appointment";
7
8
  type EnterableSlotSequence = Extract<SequenceView, {
@@ -59,6 +60,74 @@ export interface GrantedSlotProps extends SlotProps {
59
60
  grant?: string;
60
61
  expiresAt?: number;
61
62
  }
63
+ export interface PaymentCollectionSlotProps extends SlotProps {
64
+ preAuthAmount: string;
65
+ preAuthCurrencyCode: string;
66
+ savedPaymentMethods: PaymentMethodSummary[];
67
+ /** Rejects on a recoverable payment failure (e.g. a declined card) — the panel owns recovery UI for its own submit. */
68
+ onEnter: (paymentInput: PaymentInput) => Promise<void>;
69
+ isEntering: boolean;
70
+ /** "enter" collects payment before first entry; "reenter" collects payment to retry after a failed or expired authorization hold. */
71
+ intent: "enter" | "reenter";
72
+ /**
73
+ * Client configuration for the processor authorizing this entry, as the server supplied it.
74
+ * Carried by the enterable arm only — a retry is offered from an ended view, which holds no
75
+ * configuration.
76
+ */
77
+ paymentProvider?: PaymentProviderConfig;
78
+ /** Present while the processor is waiting on an out-of-band action; re-submitting entry resolves it. */
79
+ pendingAction?: CheckoutNextAction;
80
+ /**
81
+ * Re-reads the consumer's journeys from the server so a provider configuration that was missing
82
+ * or unreadable at render can arrive. This is the panel's only recovery when `paymentProvider` is
83
+ * absent — the value is a prop, so nothing the panel does to its own state can produce it.
84
+ */
85
+ onRetryPaymentConfig?: () => Promise<void>;
86
+ }
87
+ /**
88
+ * The processor's client configuration block, named through the affordance that carries it: core
89
+ * re-exports the affordance but not the contract's own name for this shape.
90
+ */
91
+ type PaymentProviderConfig = NonNullable<ReservationPaymentAffordance["paymentProvider"]>;
92
+ export interface ReservationCheckoutSlotProps extends SlotProps {
93
+ reservation: {
94
+ token: string;
95
+ expiresAt: number;
96
+ };
97
+ savedPaymentMethods: PaymentMethodSummary[];
98
+ /** Which processor executes this reservation's payment leg; narrow before rendering processor-specific UI. */
99
+ processor: CheckoutProcessor;
100
+ /** Client configuration for the processor settling this reservation, as the server supplied it. */
101
+ paymentProvider?: PaymentProviderConfig;
102
+ /** Present while the processor is waiting on an out-of-band action; complete it, then call `onResume`. */
103
+ pendingAction?: CheckoutNextAction;
104
+ /** Rejects on a recoverable payment failure (e.g. a declined card) — the panel owns recovery UI for its own submit. */
105
+ onConfirm: (paymentInput: PaymentInput) => Promise<void>;
106
+ /** Re-submits the paused checkout after `pendingAction` has been completed. Idempotent server-side. */
107
+ onResume: () => Promise<void>;
108
+ isConfirming: boolean;
109
+ /**
110
+ * Re-reads the consumer's journeys from the server so a provider configuration that was missing
111
+ * or unreadable at render can arrive. This is the panel's only recovery when `paymentProvider` is
112
+ * absent — the value is a prop, so nothing the panel does to its own state can produce it.
113
+ */
114
+ onRetryPaymentConfig?: () => Promise<void>;
115
+ }
116
+ export type CheckoutProcessingSlotProps = SlotProps;
117
+ /**
118
+ * A Fanfare-managed payment is terminal: it produces a receipt, never an admission credential. The
119
+ * `grant` arm carries the credential the mechanism itself issued alongside the payment; a purchase
120
+ * that ends the journey has no credential to carry, so the arms are discriminated rather than
121
+ * making the grant optional — a slot implementation can never read one that does not exist.
122
+ */
123
+ export type ReceiptSlotProps = SlotProps & ({
124
+ kind: "grant";
125
+ admissionGrant: string;
126
+ checkout: FanfareCheckoutResult;
127
+ } | {
128
+ kind: "purchase";
129
+ checkout: FanfareCheckoutResult;
130
+ });
62
131
  export interface ExpiredSlotProps extends SlotProps {
63
132
  reason: string;
64
133
  endedAt?: Date;
@@ -69,7 +138,8 @@ export interface EndedSlotProps extends SlotProps {
69
138
  }
70
139
  export interface ErrorSlotProps extends SlotProps {
71
140
  error: string;
72
- onRetry: () => void;
141
+ /** Absent when the refusal cannot be cleared by retrying — render no retry affordance. */
142
+ onRetry?: () => void;
73
143
  }
74
144
  export interface LoadingSlotProps extends SlotProps {
75
145
  /** Stage-specific message (e.g. routing). `undefined` for generic loading — show just a spinner. */
@@ -93,6 +163,10 @@ export interface ExperienceWidgetSlots {
93
163
  enterable?: (props: EnterableSlotProps) => React.ReactNode;
94
164
  participating?: (props: ParticipatingSlotProps) => React.ReactNode;
95
165
  granted?: (props: GrantedSlotProps) => React.ReactNode;
166
+ paymentCollection?: (props: PaymentCollectionSlotProps) => React.ReactNode;
167
+ reservationCheckout?: (props: ReservationCheckoutSlotProps) => React.ReactNode;
168
+ checkoutProcessing?: (props: CheckoutProcessingSlotProps) => React.ReactNode;
169
+ receipt?: (props: ReceiptSlotProps) => React.ReactNode;
96
170
  expired?: (props: ExpiredSlotProps) => React.ReactNode;
97
171
  ended?: (props: EndedSlotProps) => React.ReactNode;
98
172
  error?: (props: ErrorSlotProps) => React.ReactNode;
@@ -125,16 +199,25 @@ export interface ExperienceWidgetProps {
125
199
  children?: (props: ExperienceRenderProps) => React.ReactNode;
126
200
  /** Checkout URL for granted state */
127
201
  checkoutUrl?: string;
202
+ /**
203
+ * Action offered on terminal panels (not selected, sold out, closed), such as
204
+ * a link back to what is still available. Omit to show none.
205
+ */
206
+ endedAction?: OutcomeAction;
128
207
  /** Called when journey state changes */
129
208
  onJourneyChange?: (snapshot: JourneySnapshot) => void;
130
209
  /** Called when the journey receives a grant */
131
210
  onGranted?: (grant: string) => void;
211
+ /** Called when a Fanfare-managed checkout completes (pre-auth capture or post-win reservation checkout) */
212
+ onFanfareCheckout?: (result: FanfareCheckoutResult) => void;
213
+ /** Called when the consumer wins a `post_win` distribution and must complete checkout before `expiresAt` */
214
+ onReserved?: (expiresAt: number) => void;
132
215
  /** Called when an error occurs */
133
216
  onError?: (error: Error) => void;
134
217
  /** Additional class name */
135
218
  className?: string;
136
219
  }
137
- export declare function ExperienceWidget({ experienceId, autoStart, accessCode, autoEnterWaitlist, theme, variant, slots, children, checkoutUrl, onJourneyChange, onGranted, onError, className, }: ExperienceWidgetProps): import("react/jsx-runtime").JSX.Element;
220
+ export declare function ExperienceWidget({ experienceId, autoStart, accessCode, autoEnterWaitlist, theme, variant, slots, children, checkoutUrl, endedAction, onJourneyChange, onGranted, onFanfareCheckout, onReserved, onError, className, }: ExperienceWidgetProps): import("react/jsx-runtime").JSX.Element;
138
221
  export declare namespace ExperienceWidget {
139
222
  var displayName: string;
140
223
  }
@@ -1,5 +1,6 @@
1
1
  import { AuctionDisplayState } from '@fanfare-io/fanfare-sdk-core/internals';
2
2
  import { ReadableAtom } from 'nanostores';
3
+ import { OutcomeAction } from '../../compositions/outcome-panel';
3
4
  import { OutcomeReason } from '../../module';
4
5
  export interface AuctionModuleProps {
5
6
  display$?: ReadableAtom<AuctionDisplayState>;
@@ -23,11 +24,13 @@ export interface AuctionModuleProps {
23
24
  checkoutUrl?: string;
24
25
  onCheckoutClick?: () => void;
25
26
  outcomeReason?: OutcomeReason;
27
+ /** Host-provided action shown on the terminal panel. */
28
+ outcomeAction?: OutcomeAction;
26
29
  onReenter?: () => Promise<void>;
27
30
  isReentering?: boolean;
28
31
  className?: string;
29
32
  }
30
- export declare function AuctionModule({ display$, fallbackState, sequencePhase, canEnter, canLeave, onEnter, onLeave, onBid, isEntering, isLeaving, countdownTo, defaultBidAmount, currencyCode, locale, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, onReenter, isReentering, className, }: AuctionModuleProps): import("react/jsx-runtime").JSX.Element | null;
33
+ export declare function AuctionModule({ display$, fallbackState, sequencePhase, canEnter, canLeave, onEnter, onLeave, onBid, isEntering, isLeaving, countdownTo, defaultBidAmount, currencyCode, locale, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, outcomeAction, onReenter, isReentering, className, }: AuctionModuleProps): import("react/jsx-runtime").JSX.Element | null;
31
34
  export declare namespace AuctionModule {
32
35
  var displayName: string;
33
36
  }
@@ -1,5 +1,6 @@
1
1
  import { DrawDisplayState } from '@fanfare-io/fanfare-sdk-core/internals';
2
2
  import { ReadableAtom } from 'nanostores';
3
+ import { OutcomeAction } from '../../compositions/outcome-panel';
3
4
  import { OutcomeReason } from '../../module';
4
5
  export interface DrawModuleProps {
5
6
  /** Reactive display state atom from the journey */
@@ -30,6 +31,8 @@ export interface DrawModuleProps {
30
31
  onCheckoutClick?: () => void;
31
32
  /** Denial reason (denied) */
32
33
  outcomeReason?: OutcomeReason;
34
+ /** Host-provided action shown on the terminal panel. */
35
+ outcomeAction?: OutcomeAction;
33
36
  /** Re-enter handler (denied with reason="expired") */
34
37
  onReenter?: () => Promise<void>;
35
38
  /** Whether re-entering is in progress */
@@ -37,7 +40,7 @@ export interface DrawModuleProps {
37
40
  /** Additional class name */
38
41
  className?: string;
39
42
  }
40
- export declare function DrawModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, isEntering, isLeaving, countdownTo, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, onReenter, isReentering, className, }: DrawModuleProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function DrawModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, isEntering, isLeaving, countdownTo, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, outcomeAction, onReenter, isReentering, className, }: DrawModuleProps): import("react/jsx-runtime").JSX.Element | null;
41
44
  export declare namespace DrawModule {
42
45
  var displayName: string;
43
46
  }
@@ -1,5 +1,6 @@
1
1
  import { QueueDisplayState } from '@fanfare-io/fanfare-sdk-core/internals';
2
2
  import { ReadableAtom } from 'nanostores';
3
+ import { OutcomeAction } from '../../compositions/outcome-panel';
3
4
  import { OutcomeReason } from '../../module';
4
5
  export interface QueueModuleProps {
5
6
  display$?: ReadableAtom<QueueDisplayState>;
@@ -17,11 +18,13 @@ export interface QueueModuleProps {
17
18
  checkoutUrl?: string;
18
19
  onCheckoutClick?: () => void;
19
20
  outcomeReason?: OutcomeReason;
21
+ /** Host-provided action shown on the terminal panel. */
22
+ outcomeAction?: OutcomeAction;
20
23
  onReenter?: () => Promise<void>;
21
24
  isReentering?: boolean;
22
25
  className?: string;
23
26
  }
24
- export declare function QueueModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, isEntering, isLeaving, closeAt, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, onReenter, isReentering, className, }: QueueModuleProps): import("react/jsx-runtime").JSX.Element | null;
27
+ export declare function QueueModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, isEntering, isLeaving, closeAt, grant: _grant, expiresAt, checkoutUrl, onCheckoutClick, outcomeReason, outcomeAction, onReenter, isReentering, className, }: QueueModuleProps): import("react/jsx-runtime").JSX.Element | null;
25
28
  export declare namespace QueueModule {
26
29
  var displayName: string;
27
30
  }
@@ -1,5 +1,6 @@
1
1
  import { TimedReleaseDisplayState } from '@fanfare-io/fanfare-sdk-core/internals';
2
2
  import { ReadableAtom } from 'nanostores';
3
+ import { OutcomeAction } from '../../compositions/outcome-panel';
3
4
  import { OutcomeReason } from '../../module';
4
5
  export interface TimedReleaseModuleProps {
5
6
  display$?: ReadableAtom<TimedReleaseDisplayState>;
@@ -17,11 +18,13 @@ export interface TimedReleaseModuleProps {
17
18
  grant?: string;
18
19
  expiresAt?: number | Date;
19
20
  outcomeReason?: OutcomeReason;
21
+ /** Host-provided action shown on the terminal panel. */
22
+ outcomeAction?: OutcomeAction;
20
23
  onReenter?: () => Promise<void>;
21
24
  isReentering?: boolean;
22
25
  className?: string;
23
26
  }
24
- export declare function TimedReleaseModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, onComplete, isEntering, isLeaving, countdownTo, checkoutUrl, onCheckoutClick, grant: _grant, expiresAt, outcomeReason, onReenter, isReentering, className, }: TimedReleaseModuleProps): import("react/jsx-runtime").JSX.Element | null;
27
+ export declare function TimedReleaseModule({ display$, sequencePhase, canEnter, canLeave, onEnter, onLeave, onComplete, isEntering, isLeaving, countdownTo, checkoutUrl, onCheckoutClick, grant: _grant, expiresAt, outcomeReason, outcomeAction, onReenter, isReentering, className, }: TimedReleaseModuleProps): import("react/jsx-runtime").JSX.Element | null;
25
28
  export declare namespace TimedReleaseModule {
26
29
  var displayName: string;
27
30
  }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Mirrors one journey into a stable DOM target.
3
+ *
4
+ * The bridge latches `data-fanfare-version`, `data-fanfare-experience-id`,
5
+ * `data-fanfare-stage`, `data-fanfare-phase`, `data-fanfare-mechanism`,
6
+ * `data-fanfare-expires-at`, and `data-fanfare-outcome` on `opts.target`,
7
+ * defaulting to `document.documentElement`, and dispatches `fanfare:state-change`
8
+ * on that same element. Queue position is event-only, and a terminal outcome
9
+ * crosses as its type alone — the free-text qualifier that may accompany one
10
+ * stays on the typed SDK surface.
11
+ * Installation happens in an effect, so the first paint can be unbridged;
12
+ * vanilla `createDomBridge` stamps synchronously.
13
+ *
14
+ * Install stamps the current state and emits nothing; events are
15
+ * transition-only, so a consumer waiting for one may wait arbitrarily long — or
16
+ * forever, when the journey has ended or its queue is stable — and should read
17
+ * the latched attributes instead of expecting an initial event.
18
+ *
19
+ * Unmounting or changing `experienceId` disposes the bridge, which returns the
20
+ * bridge attributes to their pre-install state. Changing `opts.target` does not
21
+ * reinstall it. The target must be an
22
+ * already-mounted, stable element; a `ref.current` read in the render that
23
+ * creates the ref is null. Use this hook from a keyed subtree to move targets.
24
+ * `{ target: ref.current ?? undefined }` — the natural React spelling —
25
+ * silently bridges `document.documentElement`, because a nullish target is
26
+ * indistinguishable from "no target wanted".
27
+ *
28
+ * Calling this hook twice for the same experience and target is unsupported:
29
+ * it duplicates events, creates conflicting stamps, and allows one owner to
30
+ * remove the other owner's stamps during teardown. Distinct experiences alone
31
+ * do not separate two hooks: two calls with no `opts.target` both stamp
32
+ * `document.documentElement` and contend for the same attribute names, so give
33
+ * each call its own target when a page bridges more than one experience. The
34
+ * hook also creates the journey on read when one does not already exist, which
35
+ * arms its pollers.
36
+ *
37
+ * A reinstall emits nothing. When the context SDK identity changes — the `ssr`
38
+ * read-only SDK swapping to the live SDK on first paint, or a provider
39
+ * re-init — the hook disposes and reinstalls; install stamps without emitting,
40
+ * so an events-only consumer never learns of a delta it did not cause. The
41
+ * attributes remain correct.
42
+ *
43
+ * Dispose the bridge before destroying its handle or SDK. Destroying either
44
+ * while the bridge is live emits one final ready-projection event and restamps
45
+ * ready attributes — a reset the bridge cannot distinguish from a legitimate
46
+ * restart. Under `FanfareProvider`, unmount is safe: the provider defers SDK
47
+ * destruction through its lifecycle queue, so this hook's dispose runs first
48
+ * and that path emits no such event. When the provider replaces its SDK
49
+ * without unmounting the bridge — a change to a config value, or `ssr` mode
50
+ * dropping `publishableKey` — that ordering is not guaranteed and a live
51
+ * bridge may emit one final ready event and restamp. A caller supplying its
52
+ * own SDK through `FanfareContext` gets no deferral and is a direct-destroy
53
+ * caller.
54
+ */
55
+ export declare function useFanfareDomBridge(experienceId: string | undefined, opts?: {
56
+ target?: Element;
57
+ }): void;
@@ -0,0 +1,20 @@
1
+ import { SDKEvents } from '@fanfare-io/fanfare-sdk-core/events';
2
+ type SDKEventKey = keyof SDKEvents;
3
+ /**
4
+ * Subscribe to a typed Fanfare SDK event for the lifetime of the component.
5
+ *
6
+ * The handler is read from a ref refreshed on every render, so passing an inline
7
+ * closure never re-subscribes. The subscription is re-established when the SDK
8
+ * instance identity changes (the provider swapping its first-paint SDK for the
9
+ * live one) or when `event` changes; events emitted during such a swap are not
10
+ * delivered.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * useSdkEvent("journey:granted", (data) => {
15
+ * analytics.track("granted", { experienceId: data.experienceId });
16
+ * });
17
+ * ```
18
+ */
19
+ export declare function useSdkEvent<K extends SDKEventKey>(event: K, handler: (data: SDKEvents[K]) => void): void;
20
+ export {};