@usethrottle/checkout-react 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx"],"sourcesContent":["export { CheckoutEmbed } from './CheckoutEmbed.js';\nexport { PaymentEmbed } from './PaymentEmbed.js';\nexport { useThrottleEvents } from './useThrottleEvents.js';\nexport { useCartSession } from './useCartSession.js';\nexport type { UseCartSessionOptions, UseCartSessionResult } from './useCartSession.js';\nexport { useThrottleCheckout } from './useThrottleCheckout.js';\nexport type {\n UseThrottleCheckoutOptions,\n UseThrottleCheckoutResult,\n ThrottleCheckoutStatus,\n CheckoutTotals,\n} from './useThrottleCheckout.js';\nexport type {\n ThrottleEvent,\n ThrottleMessage,\n ThrottleEnvelope,\n ThrottleCompletedLineItem,\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n RecurringIntent,\n ThrottleParentCommand,\n ThrottleParentEnvelope,\n ThrottleParentMessage,\n} from './types.js';\n\n// v0.1.x compat alias. v0.1's <ThrottleCheckout> shape (positional\n// onCompleted/onError args, optional baseUrl, no parentOrigin) is\n// gone in v0.2 — the alias resolves to <CheckoutEmbed> which now\n// requires `parentOrigin`. Update call sites accordingly.\nexport { CheckoutEmbed as ThrottleCheckout } from './CheckoutEmbed.js';\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","DEFAULT_BASE_URL","import_react","import_react","import_cart","DEFAULT_STORAGE_KEY","readStored","writeStored"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx","../src/useSecureCard.tsx","../src/threeDSecure.ts","../src/SecureCardFields.tsx"],"sourcesContent":["export { CheckoutEmbed } from './CheckoutEmbed.js';\nexport { PaymentEmbed } from './PaymentEmbed.js';\nexport { useThrottleEvents } from './useThrottleEvents.js';\nexport { useCartSession } from './useCartSession.js';\nexport type { UseCartSessionOptions, UseCartSessionResult } from './useCartSession.js';\nexport { useThrottleCheckout } from './useThrottleCheckout.js';\nexport type {\n UseThrottleCheckoutOptions,\n UseThrottleCheckoutResult,\n ThrottleCheckoutStatus,\n CheckoutTotals,\n} from './useThrottleCheckout.js';\nexport type {\n ThrottleEvent,\n ThrottleMessage,\n ThrottleEnvelope,\n ThrottleCompletedLineItem,\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n RecurringIntent,\n ThrottleParentCommand,\n ThrottleParentEnvelope,\n ThrottleParentMessage,\n} from './types.js';\n\n// v0.1.x compat alias. v0.1's <ThrottleCheckout> shape (positional\n// onCompleted/onError args, optional baseUrl, no parentOrigin) is\n// gone in v0.2 — the alias resolves to <CheckoutEmbed> which now\n// requires `parentOrigin`. Update call sites accordingly.\nexport { CheckoutEmbed as ThrottleCheckout } from './CheckoutEmbed.js';\n\n// Secure Fields surface (Tasks 4-6)\nexport {\n SecureCardProvider,\n useSecureCard,\n type SecureCardSession,\n type SecureCardStatus,\n type SecureCardError,\n type SecureCardProviderProps,\n type SecureCardCompleteResult,\n // Required to type what the public surface already hands out:\n // `SecureCardContextValue` is `useSecureCard()`'s return, `SecureCardFieldKind`\n // is `registerField`'s first argument, and `SecureFieldsInstance` is the\n // `instance` on both.\n type SecureCardContextValue,\n type SecureCardFieldKind,\n type SecureFieldsInstance,\n} from './useSecureCard.js';\n// `resolveChallengeFn` is a public prop, so a merchant writing one needs its\n// argument type — and the instance type that argument carries.\nexport type { ResolveChallengeArgs, ThreeDSecureInstance } from './threeDSecure.js';\nexport {\n CardNumberField, ExpiryField, SecurityCodeField, PostalCodeField,\n type SecureCardFieldProps,\n} from './SecureCardFields.js';\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { SecureFields } from '@gr4vy/secure-fields';\nimport { resolveChallenge, type ResolveChallengeArgs } from './threeDSecure.js';\n\n/**\n * Lifecycle status for the headless Secure Fields state machine.\n * - `idle` — provider mounted, fields not yet registered\n * - `mounting` — Secure Fields instance constructed, fields registering\n * - `ready` — all required fields registered, submit() may be called\n * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE\n * - `authorizing` — card vaulted, awaiting the merchant's complete() call\n * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS\n * iframe (addThreeDSecure) is mounted and running\n * - `complete` — order created\n * - `error` — vault, challenge, or completion failed (see `error`)\n */\nexport type SecureCardStatus =\n | 'idle'\n | 'mounting'\n | 'ready'\n | 'vaulting'\n | 'authorizing'\n | 'challenging'\n | 'complete'\n | 'error';\n\n/** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */\nexport interface SecureCardSession {\n sessionId: string;\n /**\n * The payment provider instance this session belongs to, straight from\n * `POST /checkout-sessions/:id/card-session`.\n *\n * Either name works. `providerId` is the documented one; `gr4vyId` is the\n * original and is still accepted, because the package shipped with it and\n * dropping it would break consumers already passing the response through.\n */\n providerId?: string;\n /** @deprecated Use `providerId`. Still honoured. */\n gr4vyId?: string;\n environment: 'sandbox' | 'production';\n}\n\nexport interface SecureCardError {\n code: string;\n message: string;\n}\n\nexport type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';\n\n/**\n * Everything this package uses off a `SecureFields` instance, declared\n * structurally instead of re-exporting the SDK's own class type.\n *\n * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming\n * its type in an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts` — which is a hard TS7016 for any consumer on\n * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent\n * degradation of `instance` to `any` with `skipLibCheck: true` (the Next\n * default). That hits every installer of this package, not only Secure Fields\n * users. The `paths` override in this package's tsconfig repairs our own\n * compile; it is monorepo-only and does not ship. Same approach as\n * `ThreeDSecureInstance` in `threeDSecure.ts`.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower option and event types stay\n * assignable to the looser ones declared here.\n */\nexport interface SecureFieldsInstance {\n /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */\n controller?: HTMLIFrameElement;\n addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n submit(): void;\n}\n\nexport interface SecureCardContextValue {\n /**\n * The underlying Secure Fields instance — Task 5's field primitives register\n * against it.\n *\n * `null` until the provider's mount effect has constructed it. It cannot be\n * built during render: `new SecureFields()` mutates the document (see the\n * effect in `SecureCardProvider`), and React does not promise a render-phase\n * factory runs exactly once. Consumers that reach for it directly must\n * null-check; the field primitives already wait for it.\n */\n instance: SecureFieldsInstance | null;\n registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;\n /**\n * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).\n * Register this once, alongside the card fields; it is only actually\n * mounted if `complete()` gets `requires_action`.\n */\n registerThreeDSecure: (\n element: HTMLElement | string,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ) => void;\n /**\n * Abandons an in-progress challenge (e.g. the buyer closed the challenge\n * UI). Only meaningful while `status === 'challenging'`; settles\n * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept\n * distinct from a processor decline code.\n */\n cancelChallenge: () => void;\n status: SecureCardStatus;\n error: SecureCardError | null;\n /** True once all required fields are registered and submit() may be called. */\n ready: boolean;\n /** vault() then complete(). */\n pay: () => Promise<unknown>;\n vault: () => Promise<void>;\n complete: () => Promise<unknown>;\n}\n\nconst SecureCardContext = createContext<SecureCardContextValue | null>(null);\n\n// number, expiry, cvv are required; postalCode is optional.\nconst FIELDS_REQUIRED_TO_BE_READY = 3;\n\nexport interface SecureCardProviderProps {\n session: SecureCardSession;\n /**\n * Throttle's own checkout session id — the `:id` already used to mint\n * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same\n * value as `session.sessionId`: that field is Gr4vy's own checkout session\n * id (required to construct the `SecureFields` instance — see\n * `packages/platform-api/src/routes/card-session.ts`, which returns\n * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,\n * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`\n * route looks `:id` up directly against Throttle's own `checkoutSessions`\n * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not\n * `session.sessionId`, to call the right URL.\n */\n checkoutSessionId: string;\n children: ReactNode;\n /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */\n completeFn?: (args: { sessionId: string; gr4vySessionId: string }) => Promise<any>;\n /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */\n resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;\n /**\n * Bounds how long `complete()` waits on an unfinished challenge before\n * settling to a terminal `error` (code `three_ds_timeout`) on its own — the\n * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit\n * `cancelChallenge()` call, neither of which is guaranteed to ever fire if\n * the buyer just abandons the tab. Not sourced from Gr4vy documentation\n * (no challenge-duration limit is published in the SDK); 5 minutes is a\n * conservative, overridable default.\n */\n challengeTimeoutMs?: number;\n /**\n * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).\n * Required — not optional-with-a-same-origin-default. Secure Fields is the\n * headless surface for merchants who mount the card form on THEIR OWN\n * origin, which by definition is not same-origin with the Throttle API, so\n * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s\n * against the merchant's own domain at payment time instead). Making this\n * required turns that into a compile error instead of a silent runtime\n * failure. Joined onto the request path regardless of a trailing slash.\n */\n apiBaseUrl: string;\n onSuccess?: (result: SecureCardCompleteResult) => void;\n}\n\n/**\n * The real `POST /checkout-sessions/:id/complete` success body for the\n * Secure Fields flow (`checkout-session-service.ts` `complete()`,\n * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at\n * ~line 1849). There is no `order` key.\n */\nexport interface SecureCardCompleteResult {\n orderId: string;\n orderNumber: string;\n paymentId: string;\n /** Minor units (cents). */\n total: number;\n currency: string;\n status: string;\n paymentStatus: string;\n}\n\nconst DEFAULT_CHALLENGE_TIMEOUT_MS = 5 * 60 * 1000;\n\nexport function SecureCardProvider({\n session,\n checkoutSessionId,\n children,\n completeFn,\n resolveChallengeFn,\n challengeTimeoutMs = DEFAULT_CHALLENGE_TIMEOUT_MS,\n apiBaseUrl,\n onSuccess,\n}: SecureCardProviderProps) {\n const [status, setStatus] = useState<SecureCardStatus>('idle');\n const [error, setError] = useState<SecureCardError | null>(null);\n const [mountedCount, setMountedCount] = useState(0);\n const vaultWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n const threeDSElement = useRef<HTMLElement | string | null>(null);\n const threeDSOptions = useRef<{ challengeWindowSize?: '01' | '02' | '03' | '04' | '05' } | undefined>(undefined);\n const challengeWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n\n const { environment, sessionId } = session;\n // Either name is accepted: `providerId` is documented, `gr4vyId` is the\n // original the package first shipped with. Resolving here means the rest of\n // the hook never has to care which one the caller passed through.\n const providerInstanceId = session.providerId ?? session.gr4vyId ?? '';\n const [instance, setInstance] = useState<SecureFieldsInstance | null>(null);\n /** Mount point for Gr4vy's challenge, as state so arming can be an effect. */\n const [threeDSMount, setThreeDSMount] = useState<HTMLElement | string | null>(null);\n\n /**\n * `new SecureFields()` is a side effect on the document, not a computation.\n * Its constructor removes whatever `iframe#controller[src*=secure-fields]`\n * is already on the page, creates its own, appends it to `document.body`,\n * and resets the SDK's module-level event bus — so it must run exactly once\n * per mounted provider.\n *\n * It used to sit in a `useMemo`. React invokes a memo factory twice under\n * Strict Mode and commits the FIRST value, so the second construction tore\n * the committed instance's own controller iframe off the page. That\n * instance's internal `controllerReady` promise then never resolved, Gr4vy\n * never inserted a single field iframe, and yet `status` still reported\n * `ready` — because `ready` counts our own `registerField` calls, not the\n * SDK's DOM. Every \"verified in a browser\" pass before Task 9a ran against\n * that false positive (Task 9a, Finding 1).\n *\n * Task 4 had already moved `setStatus('mounting')` out of this same memo for\n * exactly this reason and left the SDK construction behind. `useMemo` is not\n * a lifecycle hook; an effect is the only hook React pairs with a cleanup,\n * so a Strict-Mode double-invoke settles on one live, attached instance.\n */\n useEffect(() => {\n const created = new SecureFields({ gr4vyId: providerInstanceId, environment, sessionId });\n setInstance(created);\n // A rebuilt instance has no registered fields. Both the counter and the\n // status derived from it must go back, or a `session` prop change leaves\n // `ready === true` while every field iframe is still being re-created and\n // `submit()` would fire against nothing.\n setMountedCount(0);\n setStatus((current) => (current === 'idle' || current === 'ready' ? 'mounting' : current));\n return () => {\n // The SDK exposes no teardown at all, so do the two things that matter:\n // take its controller iframe off the page, and stop handing the instance\n // out so no field can ever register against a discarded one.\n created.controller?.remove();\n setInstance(null);\n };\n }, [providerInstanceId, environment, sessionId]);\n\n useEffect(() => {\n if (!instance) return;\n\n const handleSuccess = () => {\n vaultWaiter.current?.resolve();\n };\n const handleFailure = (data?: { code?: string; message?: string }) => {\n vaultWaiter.current?.reject({\n code: data?.code ?? 'card_vault_failed',\n message: data?.message ?? 'The card could not be saved.',\n });\n };\n\n // The challenge now happens INSIDE submit(), between `vaulting` and the\n // vault result, so its status is driven by Gr4vy's own events rather than\n // by anything the server says. Without these the UI sits on `vaulting`\n // while a challenge is visibly on screen.\n const handleThreeDSStart = () => setStatus('challenging');\n const handleThreeDSFinish = () => {\n // Only meaningful while a challenge is genuinely outstanding.\n //\n // The provider fires FINISH even for a challenge that was cancelled or\n // failed, and it arrives AFTER the cancellation has settled the flow.\n // Without this guard that late event moved the status to `vaulting` — a\n // state nothing can leave, because the vault already resolved before the\n // challenge began. A buyer who pressed \"cancel\" was left on a spinner\n // forever, with no error and no way back. Observed in a browser; the\n // state machine reads fine without running it.\n if (!challengeWaiter.current) return;\n // Back to waiting on CARD_VAULT_SUCCESS/FAILURE, which follows the\n // challenge. The authentication outcome is not read from here: the\n // server is the authority on whether the money moved.\n setStatus((current) => (current === 'challenging' ? 'vaulting' : current));\n };\n\n instance.addEventListener(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.addEventListener(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.addEventListener(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n\n return () => {\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n };\n }, [instance]);\n\n const registerField = useCallback(\n (kind: SecureCardFieldKind, element: HTMLElement, options: Record<string, unknown> = {}) => {\n // Fields render before the provider's mount effect has constructed the\n // instance (React runs child effects before parent effects). They\n // re-register themselves when `instance` appears — see SecureCardFields.\n if (!instance) return;\n if (kind === 'number') instance.addCardNumberField(element, options);\n else if (kind === 'expiry') instance.addExpiryDateField(element, options);\n else if (kind === 'cvv') instance.addSecurityCodeField(element, options);\n else instance.addField(element, { type: 'postalCode', ...options } as any);\n setMountedCount((n) => n + 1);\n },\n [instance],\n );\n\n useEffect(() => {\n if (mountedCount >= FIELDS_REQUIRED_TO_BE_READY) setStatus('ready');\n }, [mountedCount]);\n\n const registerThreeDSecure = useCallback(\n (element: HTMLElement | string, options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' }) => {\n threeDSElement.current = element;\n threeDSOptions.current = options;\n // State as well as the ref: arming 3DS is an effect that has to re-run\n // when the mount point appears, and a ref write does not re-render.\n setThreeDSMount(element);\n },\n [],\n );\n\n /**\n * Arm 3-D Secure as SETUP, before `submit()` — not as a reaction to the\n * server.\n *\n * Gr4vy runs the enrolment check \"at the time of submitting Secure Fields\",\n * so `addThreeDSecure` has to be registered before `submit()` or the\n * challenge can never be raised. The original build called it only after\n * `/complete` answered `requires_action`, which is after `submit()` has\n * already finished — so it never ran, and every sandbox transaction came\n * back `threeDSecure: undefined`.\n *\n * The SDK has no un-register, so this arms once per instance; a changed\n * mount element arrives with a new instance or not at all.\n */\n useEffect(() => {\n if (!instance || !threeDSMount) return;\n try {\n instance.addThreeDSecure(\n threeDSMount,\n threeDSOptions.current?.challengeWindowSize\n ? { challengeWindowSize: threeDSOptions.current.challengeWindowSize }\n : undefined,\n );\n } catch (err) {\n // Arming is best-effort: a card that never needs a challenge must still\n // be chargeable. A card that DOES need one will fail visibly at submit.\n setError({\n code: 'three_ds_arm_failed',\n message: (err as Error)?.message ?? 'Could not arm 3-D Secure.',\n });\n }\n }, [instance, threeDSMount]);\n\n const cancelChallenge = useCallback(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_abandoned',\n message: 'The 3-D Secure challenge was abandoned.',\n });\n }, []);\n\n const vault = useCallback(() => {\n // Nothing to submit to yet. Previously impossible (the instance was built\n // during render); now the provider's first commit has none, and calling\n // `submit()` on nothing would hang `vaulting` forever with no rejection.\n if (!instance) {\n return Promise.reject<void>({\n code: 'not_ready',\n message: 'The card fields are not ready yet.',\n } as SecureCardError);\n }\n\n // A second vault() while one is already in flight would otherwise silently\n // overwrite vaultWaiter.current, orphaning the first caller's promise forever\n // (no resolve, no reject). Reject the second call instead; the first keeps\n // waiting on its own vault event.\n if (vaultWaiter.current) {\n return Promise.reject<void>({\n code: 'vault_in_progress',\n message: 'A card vault is already in progress.',\n } as SecureCardError);\n }\n\n setError(null);\n setStatus('vaulting');\n return new Promise<void>((resolve, reject) => {\n vaultWaiter.current = {\n resolve: () => {\n vaultWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n vaultWaiter.current = null;\n setError(e);\n setStatus('error');\n reject(e);\n },\n };\n instance.submit();\n });\n }, [instance]);\n\n const complete = useCallback(async () => {\n setStatus('authorizing');\n const doComplete = completeFn ?? ((args: { sessionId: string; gr4vySessionId: string }) => defaultComplete({ ...args, apiBaseUrl }));\n const doResolveChallenge = resolveChallengeFn ?? resolveChallenge;\n try {\n let res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n\n if (res?.status === 'requires_action') {\n if (!threeDSElement.current) {\n throw Object.assign(new Error('No 3-D Secure mount element registered (call registerThreeDSecure).'), {\n code: 'three_ds_element_missing',\n });\n }\n if (!instance) {\n throw Object.assign(new Error('The card fields are not ready yet.'), { code: 'not_ready' });\n }\n setStatus('challenging');\n // Gr4vy's own 3DS iframe runs the challenge; this resolves once it\n // reports THREE_DS_FINISH, or rejects if cancelChallenge() is called\n // or the resolver itself fails, or rejects on its own after\n // challengeTimeoutMs so a buyer who just\n // abandons the tab (no finish, no cancel) can never strand complete()\n // forever. It does not tell us success/failure — re-query /complete\n // for that.\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_timeout',\n message: 'The 3-D Secure challenge timed out.',\n });\n }, challengeTimeoutMs);\n\n challengeWaiter.current = {\n resolve: () => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n reject(Object.assign(new Error(e.message), { code: e.code }));\n },\n };\n doResolveChallenge({\n instance,\n element: threeDSElement.current!,\n ...(threeDSOptions.current ?? {}),\n }).then(\n () => challengeWaiter.current?.resolve(),\n // `resolveChallenge` calls `instance.addThreeDSecure()` synchronously\n // inside its Promise executor, so an SDK throw rejects here; a\n // merchant-supplied `resolveChallengeFn` can reject too. Without this\n // arm the only exit was `challengeTimeoutMs` — five minutes stuck at\n // `status: 'challenging'`, plus an unhandled rejection. Same\n // code-passthrough convention as `complete()`'s catch: keep a `code`\n // the resolver supplied, otherwise report `three_ds_failed`.\n (err: unknown) =>\n challengeWaiter.current?.reject({\n code: (err as { code?: string })?.code ?? 'three_ds_failed',\n message: (err as Error)?.message ?? 'The 3-D Secure challenge could not be completed.',\n }),\n );\n });\n\n setStatus('authorizing');\n res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n if (res?.status === 'requires_action') {\n throw Object.assign(new Error('The payment still requires authentication after the 3-D Secure challenge.'), {\n code: 'three_ds_unresolved',\n });\n }\n }\n\n setStatus('complete');\n onSuccess?.(res as SecureCardCompleteResult);\n return res;\n } catch (err) {\n setError({\n code: (err as { code?: string })?.code ?? 'complete_failed',\n message: (err as Error)?.message ?? 'Payment could not be completed.',\n });\n setStatus('error');\n throw err;\n }\n }, [completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, instance, checkoutSessionId, session.sessionId, onSuccess]);\n\n const pay = useCallback(async () => {\n await vault();\n return complete();\n }, [vault, complete]);\n\n const value: SecureCardContextValue = {\n instance,\n registerField,\n registerThreeDSecure,\n cancelChallenge,\n status,\n error,\n // \"The fields are mounted and nothing is in flight\" — NOT `status ===\n // 'ready'`. Deriving it from the status alone made a decline terminal: the\n // only thing that ever sets `ready` is the mount-count effect, which does\n // not re-run after `setStatus('error')`, so `ready` stayed false and the\n // merchant's pay button stayed disabled. A declined buyer could not try\n // another card without reloading the page — and a decline is the single\n // most common failure at a checkout.\n ready:\n mountedCount >= FIELDS_REQUIRED_TO_BE_READY &&\n status !== 'vaulting' &&\n status !== 'authorizing' &&\n status !== 'challenging' &&\n status !== 'complete',\n pay,\n vault,\n complete,\n };\n\n return <SecureCardContext.Provider value={value}>{children}</SecureCardContext.Provider>;\n}\n\n/**\n * Default completion call — POSTs to the public, buyer-facing complete route.\n *\n * `completeSessionSchema` (`packages/platform-api/src/schemas/checkout-sessions.ts`)\n * is `.strict()` and requires `paymentMethod`. The handler\n * (`checkout-session-service.ts` `complete()`) rejects `paymentMethod: 'card'`\n * outright (\"Use 'embedded' ...\"), and for non-proxy-mode `'embedded'`\n * requires `processorCheckoutSessionId` or `processorTransactionId`. Secure\n * Fields vaults the card straight to the Gr4vy checkout session — no embed\n * transaction exists yet — so this is the server-authorize sub-branch: send\n * `processorCheckoutSessionId` = that same Gr4vy session id (`gr4vySessionId`,\n * i.e. `SecureCardSession.sessionId`), which the handler forwards into\n * `adapter.authorize({ token: JSON.stringify({ checkoutSessionId }) })`.\n *\n * `apiBaseUrl` is joined onto the path with `joinApiUrl` so a merchant-supplied\n * value with or without a trailing slash both resolve to the same URL —\n * without it, `https://api.example.com/` + `/api/v1/...` would double the slash.\n */\nasync function defaultComplete({\n sessionId,\n gr4vySessionId,\n apiBaseUrl,\n}: {\n sessionId: string;\n gr4vySessionId: string;\n apiBaseUrl: string;\n}) {\n const res = await fetch(joinApiUrl(apiBaseUrl, `/api/v1/checkout-sessions/${sessionId}/complete`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paymentMethod: 'embedded', processorCheckoutSessionId: gr4vySessionId }),\n });\n const body = await res.json();\n if (!res.ok) {\n throw Object.assign(new Error(body?.error?.message ?? 'Payment could not be completed.'), {\n code: body?.error?.code ?? 'payment_failed',\n });\n }\n return body?.data ?? body;\n}\n\n/** Joins a base URL and a path safely regardless of a trailing slash on `base`. */\nfunction joinApiUrl(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function useSecureCard(): SecureCardContextValue {\n const ctx = useContext(SecureCardContext);\n if (!ctx) throw new Error('useSecureCard must be used inside <SecureCardProvider>');\n return ctx;\n}\n\nexport { SecureCardContext };\n","import { SecureFields } from '@gr4vy/secure-fields';\n\n/**\n * Subset of the Secure Fields instance this module needs — real or mocked.\n *\n * Declared structurally rather than as `Pick<SecureFields, …>`: any reference\n * to the SDK's own type from an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts`, and that package's `exports` map carries no `types`\n * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under\n * `skipLibCheck: false`, or silently degrades the member to `any` under\n * `skipLibCheck: true` (the Next default). The `paths` override in this\n * package's tsconfig repairs our own compile only; it does not ship.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower `CombinedEvents` event parameter\n * stays assignable to the `string` declared here.\n */\nexport interface ThreeDSecureInstance {\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n}\n\nexport interface ResolveChallengeArgs {\n /** The `SecureFields` instance already constructed for this session. */\n instance: ThreeDSecureInstance;\n /** Mount point for Gr4vy's own 3DS challenge iframe. */\n element: HTMLElement | string;\n challengeWindowSize?: '01' | '02' | '03' | '04' | '05';\n}\n\n/**\n * Drives a Gr4vy-hosted 3-D Secure challenge to completion using Secure\n * Fields' own `addThreeDSecure`, instead of hand-rolling a modal around\n * `transaction.paymentMethod.approvalUrl`.\n *\n * Why this instead of a modal + approvalUrl + redirect fallback: `addThreeDSecure(element, options)`\n * takes no transaction id and no approval URL — its only inputs are a mount\n * element and an optional `challengeWindowSize`\n * (node_modules/@gr4vy/secure-fields lib/index.d.ts, lib/types.d.ts —\n * `ThreeDSecureOptions = { challengeWindowSize?: ... }`). The bundled\n * implementation (lib/index.js, `class P` / `ThreeDSecure.init`) shows the\n * iframe it creates is `${frameUrl}/threeds.html` with query params\n * `{sessionId, parentOrigin, gr4vyId, environment}` — the exact identifiers\n * `SecureFields` was already constructed with. The challenge is scoped to the\n * Secure Fields *session*, not to anything passed in per-call. Our server\n * creates the transaction against that same session\n * (`paymentMethod: { method: 'checkout-session', id: checkoutSessionId }` —\n * packages/commerce-payment-orchestrator/src/adapters/gr4vy.ts `authorize()`),\n * so this element renders the challenge for that transaction without ever\n * touching `approvalUrl`. `approvalUrl` does not appear anywhere in the SDK\n * bundle (checked via `grep -c approvalUrl lib/index.js` → 0).\n *\n * `THREE_DS_FINISH`'s payload shape is undocumented (absent from the SDK's\n * README; only the bare event name appears in the CHANGELOG and\n * lib/constants.d.ts), so this does not try to read a success/fail flag off\n * it — it only signals \"the challenge UI is done.\" The caller re-queries\n * `/complete` afterwards for the authoritative outcome, which is also the\n * documented re-entry path for this endpoint (`checkout-session-service.ts`'s\n * `complete()` already tolerates re-invocation on the same session — see its\n * \"processing lease\" / pending-order-reuse comments).\n */\nexport function resolveChallenge({ instance, element, challengeWindowSize }: ResolveChallengeArgs): Promise<void> {\n return new Promise((resolve) => {\n const handleFinish = () => {\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n resolve();\n };\n // Registered before addThreeDSecure() mounts the frame, so a finish that\n // arrives immediately after mount is never missed.\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n instance.addThreeDSecure(element, challengeWindowSize ? { challengeWindowSize } : undefined);\n });\n}\n","import { useContext, useEffect, useRef, type CSSProperties } from 'react';\nimport { SecureCardContext, type SecureCardFieldKind } from './useSecureCard.js';\n\nexport interface SecureCardFieldProps {\n className?: string;\n style?: CSSProperties;\n /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */\n options?: Record<string, unknown>;\n}\n\n/**\n * Each field is an empty element Gr4vy replaces with its own iframe. We render\n * the container and nothing else: the merchant chose Secure Fields precisely so\n * that the styling is theirs.\n *\n * The element we hand Gr4vy is created with `document.createElement`, NOT the\n * one React renders. Gr4vy's SDK mounts a field with\n * `element.parentNode.replaceChild(itsOwnWrapper, element)` — it *replaces* the\n * node it is given. Handing it the React-managed node left React holding a\n * reference to a node that was no longer in the tree, so unmounting the field\n * threw an uncaught `NotFoundError: Failed to execute 'removeChild'` and killed\n * the page on the first click (Task 9a, Finding 3 — 100% reproducible in a\n * production build). Giving Gr4vy a throwaway child instead means React only\n * ever removes the host `<div>` it rendered itself, and everything Gr4vy put\n * inside goes with it.\n *\n * The merchant's `className`/`style` stay on the host element, and the\n * throwaway child carries `height: 100%` so Gr4vy's wrapper (which copies the\n * `style` attribute off the node it replaces) still fills the merchant's box —\n * the field iframe is `height: 100%` and would otherwise collapse to an\n * iframe's 150px default.\n *\n * Known limitation: Gr4vy's SDK has no unregister (only `removeEventListener`),\n * so a field that unmounts stays registered on the instance. Unmounting an\n * optional field (postal code) and remounting it is safe — re-registering\n * replaces the SDK's own per-type listener and builds a fresh iframe.\n * Unmounting a REQUIRED field (number, expiry, cvv) and then calling `pay()`\n * without remounting it leaves the SDK submitting against a field whose iframe\n * is gone; remount it first.\n */\nfunction useField(kind: SecureCardFieldKind, options?: Record<string, unknown>) {\n const ctx = useContext(SecureCardContext);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const instance = ctx?.instance ?? null;\n\n useEffect(() => {\n const host = hostRef.current;\n // React runs child effects before parent effects, so on the very first\n // commit the provider has not constructed the instance yet. This effect\n // re-runs when it appears.\n if (!host || !ctx || !instance) return;\n\n const target = document.createElement('div');\n target.style.height = '100%';\n host.appendChild(target);\n ctx.registerField(kind, target, options ?? {});\n\n return () => {\n // Drop whatever Gr4vy put in the host (its wrapper replaced `target`),\n // so a remount starts from an empty container instead of stacking a\n // second, dead iframe under the same field.\n while (host.firstChild) host.removeChild(host.firstChild);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [instance]);\n\n return hostRef;\n}\n\nexport function CardNumberField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('number', options)} className={className} style={style} />;\n}\n\nexport function ExpiryField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('expiry', options)} className={className} style={style} />;\n}\n\nexport function SecurityCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('cvv', options)} className={className} style={style} />;\n}\n\nexport function PostalCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('postalCode', options)} className={className} style={style} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAA8C;;;ACA9C,mBAA0C;AA4BnC,SAAS,kBAAkB,EAAE,WAAW,gBAAgB,UAAU,GAA6B;AACpG,8BAAU,MAAM;AACd,aAAS,OAAO,IAAkB;AAChC,UAAI,GAAG,WAAW,eAAgB;AAClC,UAAI,UAAU,WAAW,GAAG,WAAW,UAAU,QAAQ,cAAe;AACxE,YAAM,OAAO,GAAG;AAChB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAI,KAAK,WAAW,cAAc,KAAK,YAAY,EAAG;AACtD,UAAI,OAAO,KAAK,SAAS,YAAY,CAAC,KAAK,KAAK,WAAW,WAAW,EAAG;AACzE,gBAAU,IAAuB;AAAA,IACnC;AACA,WAAO,iBAAiB,WAAW,MAAM;AACzC,WAAO,MAAM,OAAO,oBAAoB,WAAW,MAAM;AAAA,EAC3D,GAAG,CAAC,WAAW,gBAAgB,SAAS,CAAC;AAC3C;;;AC1CA,IAAAC,gBAAoD;AAmC7C,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA8B,MAAS;AAIvE,+BAAU,MAAM;AACd,QAAI,mBAAmB,OAAW;AAClC,UAAM,MAAM,UAAU,SAAS;AAC/B,QAAI,CAAC,IAAK;AAGV,QAAI,aAAa,kBAAkB,YAAa;AAChD,QAAI,CAAC,YAAa;AAElB,UAAM,UAAiC;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AACA,QAAI,YAAY,SAAS,YAAY;AACrC,gBAAY,cAAc;AAAA,EAC5B,GAAG,CAAC,WAAW,cAAc,gBAAgB,aAAa,QAAQ,CAAC;AACrE;;;AFkCI;AAxFJ,IAAM,mBAAmB;AAYlB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiD;AAC/C,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,YAGjF,GAAI,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,YACtD,GAAI,IAAI,aAAa,SAAY,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,YAC/D,GAAI,IAAI,cAAc,SAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,UACpE,CAAC;AACD;AAAA,QACF,KAAK;AACH,qBAAW,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AACnD;AAAA,QACF,KAAK;AACH,uBAAa;AACb;AAAA,QACF,KAAK;AACH,0BAAgB,EAAE,MAAM,IAAI,KAAK,CAAC;AAClC;AAAA,QACF,KAAK;AACH,cAAI,UAAU,QAAS,WAAU,QAAQ,MAAM,SAAS,GAAG,IAAI,MAAM;AACrE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,SAAS,cAAc,aAAa,UAAU,YAAY,aAAa;AAAA,EAC1E;AAEA,oBAAkB,EAAE,WAAW,gBAAgB,WAAW,SAAS,CAAC;AACpE,oBAAkB;AAAA,IAChB;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAS,IAAI,gBAAgB,EAAE,OAAO,KAAK,MAAM,iBAAiB,aAAa,CAAC;AACtF,MAAI,QAAS,QAAO,IAAI,WAAW,OAAO;AAC1C,MAAI,KAAM,QAAO,IAAI,QAAQ,IAAI;AACjC,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,IAAI,KAAK,IAAI,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;;;AG1GA,IAAAC,gBAA8C;AAyF1C,IAAAC,sBAAA;AApFJ,IAAMC,oBAAmB;AAYlB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,UAAUA;AAAA,EACV,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,YAGjF,GAAI,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,YACtD,GAAI,IAAI,aAAa,SAAY,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,YAC/D,GAAI,IAAI,cAAc,SAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,UACpE,CAAC;AACD;AAAA,QACF,KAAK;AACH,qBAAW,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AACnD;AAAA,QACF,KAAK;AACH,uBAAa;AACb;AAAA,QACF,KAAK;AACH,cAAI,UAAU,QAAS,WAAU,QAAQ,MAAM,SAAS,GAAG,IAAI,MAAM;AACrE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,SAAS,cAAc,aAAa,UAAU,UAAU;AAAA,EAC3D;AAEA,oBAAkB,EAAE,WAAW,gBAAgB,WAAW,SAAS,CAAC;AACpE,oBAAkB;AAAA,IAChB;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAS,IAAI,gBAAgB,EAAE,OAAO,KAAK,MAAM,gBAAgB,aAAa,CAAC;AACrF,MAAI,QAAS,QAAO,IAAI,WAAW,OAAO;AAC1C,MAAI,KAAM,QAAO,IAAI,QAAQ,IAAI;AACjC,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,IAAI,KAAK,IAAI,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;;;AClGA,IAAAC,gBAA0D;AAC1D,kBASO;AAEP,IAAM,sBAAsB;AA0C5B,SAAS,WAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAiBO,SAAS,eAAe,SAAsD;AACnF,QAAM,EAAE,eAAe,eAAe,YAAY,QAAQ,IAAI;AAC9D,QAAM,aAAa,QAAQ,eAAe,SAAY,sBAAsB,QAAQ;AAEpF,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,8BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,UAAW,UAAS,SAAS;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,WAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,eAAW,IAAI;AACf,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AAAA,IAChB,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,UAAW;AACf,kBAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AAAA,IACd,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,UAAW,YAAW,KAAK;AAAA,IAClC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,kBAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAGA,QAAM,UAAM;AAAA,IACV,OAAO,IAAoC,OAA6B,CAAC,MAAqB;AAC5F,UAAI,CAAC,QAAQ;AACX,iBAAS,aAAa,IAAI,MAAM,+BAA+B,CAAC;AAChE;AAAA,MACF;AACA,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,YAAI,KAAK,OAAQ,OAAM,cAAc;AACrC,cAAM,OAAO,MAAM,GAAG;AACtB,gBAAQ,IAAI;AAAA,MACd,SAAS,GAAG;AACV,iBAAS,CAAU;AACnB,cAAM;AAAA,MACR,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,SAAS;AAAA,EACnC;AAEA,QAAM,cAAU;AAAA,IACd,CAAC,UAA4B,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IAC/E,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,CAAC,QAAgB,UAA+B,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,CAAC;AAAA,IAC3F,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa,2BAAY,CAAC,WAAmB,IAAI,MAAM,OAAQ,WAAW,MAAM,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACvG,QAAM,qBAAiB;AAAA,IACrB,CAAC,UAA+B,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzF,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,MAAM,IAAI,MAAM,OAAQ,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACzF,QAAM,oBAAgB;AAAA,IACpB,CAAC,SAAiB,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzE,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,MAAM,IAAI,MAAM,OAAQ,eAAe,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AAC3F,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAAA,EACjD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,eAAW;AAAA,IACf,OAAO,UAA0D;AAC/D,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,cAAM,cAAc;AACpB,eAAO,MAAM,OAAO,SAAS,KAAK;AAAA,MACpC,SAAS,GAAG;AACV,iBAAS,CAAU;AACnB,cAAM;AAAA,MACR,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,SAAS;AAAA,EACnC;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,gBAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACtPA,IAAAC,gBAAkE;AAClE,IAAAC,eAYO;AAEP,IAAMC,uBAAsB;AA6E5B,SAASC,YAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASC,aAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,SAAS,MAAqD;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,eAAe,KAAK;AAAA,IACpB,eAAe,KAAK;AAAA,IACpB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,EACd;AACF;AA0BO,SAAS,oBACd,SAC2B;AAC3B,QAAM,EAAE,eAAe,eAAe,YAAY,QAAQ,IAAI;AAC9D,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,aAAa,QAAQ,eAAe,SAAYF,uBAAsB,QAAQ;AAEpF,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAiC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,cAAU,sBAA+B,IAAI;AACnD,UAAQ,UAAU;AAElB,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,+BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,WAAW;AACb,eAAS,SAAS;AAClB,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQC,YAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,cAAU,SAAS;AACnB,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AACd,gBAAU,OAAO;AAAA,IACnB,CAAC,EACA,MAAM,MAAM;AACX,UAAI,UAAW;AACf,MAAAC,aAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AACZ,gBAAU,MAAM;AAAA,IAClB,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,MAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAOA,QAAM,cAAU,2BAAY,YAAsC;AAChE,QAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAM,OAAO,QAAQ;AAErB,WAAO,OAAO,EAAE;AAChB,qBAAiB,IAAI;AACrB,UAAM,UAAU,MAAM,OAAO,OAAO;AAAA,MAClC,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM,QAAQ;AAAA,IACtB,CAAC;AACD,IAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,qBAAiB,QAAQ,aAAa;AACtC,QAAI,OAAO,QAAQ;AACnB,eAAW,QAAQ,MAAM,aAAa,CAAC,GAAG;AACxC,aAAO,MAAM,OAAO,QAAQ;AAAA,QAC1B,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK,YAAY;AAAA,QAC3B,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AACA,UAAM,OAAO,MAAM;AACnB,QAAI,MAAM;AACR,aAAO,MAAM,OAAO,eAAe;AAAA,QACjC,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,SAAS,KAAK,WAAW;AAAA,QACzB,aAAa,KAAK,eAAe;AAAA,QACjC,uBAAuB,KAAK,yBAAyB;AAAA,MACvD,CAAC;AAAA,IACH;AACA,YAAQ,IAAI;AACZ,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,WAAW,UAAU,CAAC;AAQlC,QAAM,UAAM;AAAA,IACV,OACE,IACA,OAA0D,CAAC,MAC5C;AACf,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,aAAa,IAAI,MAAM,+BAA+B;AAChE,iBAAS,CAAC;AACV,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AACA,gBAAU,SAAS;AACnB,eAAS,IAAI;AACb,UAAI;AACF,YAAI,KAAK,OAAQ,OAAM,cAAc;AACrC,cAAM,SAAS,MAAM,GAAG;AACxB,kBAAU,OAAO;AACjB,eAAO;AAAA,MACT,SAAS,GAAG;AACV,cAAM,QAAQ,aAAa,iCAAoB,aAAa;AAC5D,YAAI,SAAS,aAAa;AACxB,cAAI;AACF,sBAAU,YAAY;AACtB,kBAAM,QAAQ;AACd,gBAAI,KAAK,mBAAmB;AAC1B,oBAAM,UAAU,MAAM,GAAG;AACzB,wBAAU,OAAO;AACjB,qBAAO;AAAA,YACT;AAGA,sBAAU,OAAO;AACjB,kBAAM,SAAS,IAAI,8BAAiB;AAAA,cAClC,SAAS;AAAA,YACX,CAAC;AACD,qBAAS,MAAM;AACf,kBAAM;AAAA,UACR,SAAS,YAAY;AACnB,qBAAS,UAAmB;AAC5B,sBAAU,OAAO;AACjB,kBAAM;AAAA,UACR;AAAA,QACF;AACA,iBAAS,CAAU;AACnB,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,WAAW,aAAa,OAAO;AAAA,EACzD;AAEA,QAAM,cAAU;AAAA,IACd,OAAO,UAA4B;AACjC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAC9F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,QAAgB,UAA+B;AACpD,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,GAAG,EAAE,mBAAmB,MAAM,CAAC;AAC5F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,WAAmB;AACxB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,MAAM,GAAG,EAAE,mBAAmB,MAAM,CAAC;AACrF,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,mBAAe;AAAA,IACnB,OAAO,UAA+B;AACpC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACrG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,YAAY;AAC5C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,GAAG,EAAE,mBAAmB,KAAK,CAAC;AACjF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAChB,QAAM,oBAAgB;AAAA,IACpB,OAAO,SAAiB;AACtB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACnG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,YAAY;AAC7C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,GAAG,EAAE,mBAAmB,KAAK,CAAC;AAClF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,OAAO,MAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAC5D,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,oBAAgB;AAAA,IACpB,CAAC,UACC,IAAI,MAAM,OAAQ,SAAS,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAAA,IAC9E,CAAC,QAAQ,GAAG;AAAA,EACd;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,IAAAA,aAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM,oBAAoB;AAAA,IAC1C,QAAQ,SAAS,IAAI;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACzZA,IAAAC,gBAQO;AACP,IAAAC,wBAA6B;;;ACT7B,2BAA6B;AAkEtB,SAAS,iBAAiB,EAAE,UAAU,SAAS,oBAAoB,GAAwC;AAChH,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,eAAe,MAAM;AACzB,eAAS,sBAAsB,kCAAa,OAAO,iBAAiB,YAAY;AAChF,cAAQ;AAAA,IACV;AAGA,aAAS,iBAAiB,kCAAa,OAAO,iBAAiB,YAAY;AAC3E,aAAS,gBAAgB,SAAS,sBAAsB,EAAE,oBAAoB,IAAI,MAAS;AAAA,EAC7F,CAAC;AACH;;;ADidS,IAAAC,sBAAA;AA1ZT,IAAM,wBAAoB,6BAA6C,IAAI;AAG3E,IAAM,8BAA8B;AA+DpC,IAAM,+BAA+B,IAAI,KAAK;AAEvC,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA2B,MAAM;AAC7D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAiC,IAAI;AAC/D,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,CAAC;AAClD,QAAM,kBAAc,sBAA6E,IAAI;AACrG,QAAM,qBAAiB,sBAAoC,IAAI;AAC/D,QAAM,qBAAiB,sBAA+E,MAAS;AAC/G,QAAM,sBAAkB,sBAA6E,IAAI;AAEzG,QAAM,EAAE,aAAa,UAAU,IAAI;AAInC,QAAM,qBAAqB,QAAQ,cAAc,QAAQ,WAAW;AACpE,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAsC,IAAI;AAE1E,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAsC,IAAI;AAuBlF,+BAAU,MAAM;AACd,UAAM,UAAU,IAAI,mCAAa,EAAE,SAAS,oBAAoB,aAAa,UAAU,CAAC;AACxF,gBAAY,OAAO;AAKnB,oBAAgB,CAAC;AACjB,cAAU,CAAC,YAAa,YAAY,UAAU,YAAY,UAAU,aAAa,OAAQ;AACzF,WAAO,MAAM;AAIX,cAAQ,YAAY,OAAO;AAC3B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,oBAAoB,aAAa,SAAS,CAAC;AAE/C,+BAAU,MAAM;AACd,QAAI,CAAC,SAAU;AAEf,UAAM,gBAAgB,MAAM;AAC1B,kBAAY,SAAS,QAAQ;AAAA,IAC/B;AACA,UAAM,gBAAgB,CAAC,SAA+C;AACpE,kBAAY,SAAS,OAAO;AAAA,QAC1B,MAAM,MAAM,QAAQ;AAAA,QACpB,SAAS,MAAM,WAAW;AAAA,MAC5B,CAAC;AAAA,IACH;AAMA,UAAM,qBAAqB,MAAM,UAAU,aAAa;AACxD,UAAM,sBAAsB,MAAM;AAUhC,UAAI,CAAC,gBAAgB,QAAS;AAI9B,gBAAU,CAAC,YAAa,YAAY,gBAAgB,aAAa,OAAQ;AAAA,IAC3E;AAEA,aAAS,iBAAiB,mCAAa,OAAO,oBAAoB,aAAa;AAC/E,aAAS,iBAAiB,mCAAa,OAAO,oBAAoB,aAAa;AAC/E,aAAS,iBAAiB,mCAAa,OAAO,gBAAgB,kBAAkB;AAChF,aAAS,iBAAiB,mCAAa,OAAO,iBAAiB,mBAAmB;AAElF,WAAO,MAAM;AACX,eAAS,sBAAsB,mCAAa,OAAO,oBAAoB,aAAa;AACpF,eAAS,sBAAsB,mCAAa,OAAO,oBAAoB,aAAa;AACpF,eAAS,sBAAsB,mCAAa,OAAO,gBAAgB,kBAAkB;AACrF,eAAS,sBAAsB,mCAAa,OAAO,iBAAiB,mBAAmB;AAAA,IACzF;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,oBAAgB;AAAA,IACpB,CAAC,MAA2B,SAAsB,UAAmC,CAAC,MAAM;AAI1F,UAAI,CAAC,SAAU;AACf,UAAI,SAAS,SAAU,UAAS,mBAAmB,SAAS,OAAO;AAAA,eAC1D,SAAS,SAAU,UAAS,mBAAmB,SAAS,OAAO;AAAA,eAC/D,SAAS,MAAO,UAAS,qBAAqB,SAAS,OAAO;AAAA,UAClE,UAAS,SAAS,SAAS,EAAE,MAAM,cAAc,GAAG,QAAQ,CAAQ;AACzE,sBAAgB,CAAC,MAAM,IAAI,CAAC;AAAA,IAC9B;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,+BAAU,MAAM;AACd,QAAI,gBAAgB,4BAA6B,WAAU,OAAO;AAAA,EACpE,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,2BAAuB;AAAA,IAC3B,CAAC,SAA+B,YAAyE;AACvG,qBAAe,UAAU;AACzB,qBAAe,UAAU;AAGzB,sBAAgB,OAAO;AAAA,IACzB;AAAA,IACA,CAAC;AAAA,EACH;AAgBA,+BAAU,MAAM;AACd,QAAI,CAAC,YAAY,CAAC,aAAc;AAChC,QAAI;AACF,eAAS;AAAA,QACP;AAAA,QACA,eAAe,SAAS,sBACpB,EAAE,qBAAqB,eAAe,QAAQ,oBAAoB,IAClE;AAAA,MACN;AAAA,IACF,SAAS,KAAK;AAGZ,eAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAU,KAAe,WAAW;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,CAAC;AAE3B,QAAM,sBAAkB,2BAAY,MAAM;AACxC,oBAAgB,SAAS,OAAO;AAAA,MAC9B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,YAAQ,2BAAY,MAAM;AAI9B,QAAI,CAAC,UAAU;AACb,aAAO,QAAQ,OAAa;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAoB;AAAA,IACtB;AAMA,QAAI,YAAY,SAAS;AACvB,aAAO,QAAQ,OAAa;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAoB;AAAA,IACtB;AAEA,aAAS,IAAI;AACb,cAAU,UAAU;AACpB,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,kBAAY,UAAU;AAAA,QACpB,SAAS,MAAM;AACb,sBAAY,UAAU;AACtB,kBAAQ;AAAA,QACV;AAAA,QACA,QAAQ,CAAC,MAAM;AACb,sBAAY,UAAU;AACtB,mBAAS,CAAC;AACV,oBAAU,OAAO;AACjB,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AACA,eAAS,OAAO;AAAA,IAClB,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,eAAW,2BAAY,YAAY;AACvC,cAAU,aAAa;AACvB,UAAM,aAAa,eAAe,CAAC,SAAwD,gBAAgB,EAAE,GAAG,MAAM,WAAW,CAAC;AAClI,UAAM,qBAAqB,sBAAsB;AACjD,QAAI;AACF,UAAI,MAAM,MAAM,WAAW,EAAE,WAAW,mBAAmB,gBAAgB,QAAQ,UAAU,CAAC;AAE9F,UAAI,KAAK,WAAW,mBAAmB;AACrC,YAAI,CAAC,eAAe,SAAS;AAC3B,gBAAM,OAAO,OAAO,IAAI,MAAM,qEAAqE,GAAG;AAAA,YACpG,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,YAAI,CAAC,UAAU;AACb,gBAAM,OAAO,OAAO,IAAI,MAAM,oCAAoC,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,QAC5F;AACA,kBAAU,aAAa;AAQvB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,gBAAM,YAAY,WAAW,MAAM;AACjC,4BAAgB,SAAS,OAAO;AAAA,cAC9B,MAAM;AAAA,cACN,SAAS;AAAA,YACX,CAAC;AAAA,UACH,GAAG,kBAAkB;AAErB,0BAAgB,UAAU;AAAA,YACxB,SAAS,MAAM;AACb,2BAAa,SAAS;AACtB,8BAAgB,UAAU;AAC1B,sBAAQ;AAAA,YACV;AAAA,YACA,QAAQ,CAAC,MAAM;AACb,2BAAa,SAAS;AACtB,8BAAgB,UAAU;AAC1B,qBAAO,OAAO,OAAO,IAAI,MAAM,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAAA,YAC9D;AAAA,UACF;AACA,6BAAmB;AAAA,YACjB;AAAA,YACA,SAAS,eAAe;AAAA,YACxB,GAAI,eAAe,WAAW,CAAC;AAAA,UACjC,CAAC,EAAE;AAAA,YACD,MAAM,gBAAgB,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAQvC,CAAC,QACC,gBAAgB,SAAS,OAAO;AAAA,cAC9B,MAAO,KAA2B,QAAQ;AAAA,cAC1C,SAAU,KAAe,WAAW;AAAA,YACtC,CAAC;AAAA,UACL;AAAA,QACF,CAAC;AAED,kBAAU,aAAa;AACvB,cAAM,MAAM,WAAW,EAAE,WAAW,mBAAmB,gBAAgB,QAAQ,UAAU,CAAC;AAC1F,YAAI,KAAK,WAAW,mBAAmB;AACrC,gBAAM,OAAO,OAAO,IAAI,MAAM,2EAA2E,GAAG;AAAA,YAC1G,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAEA,gBAAU,UAAU;AACpB,kBAAY,GAA+B;AAC3C,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,MAAO,KAA2B,QAAQ;AAAA,QAC1C,SAAU,KAAe,WAAW;AAAA,MACtC,CAAC;AACD,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF,GAAG,CAAC,YAAY,oBAAoB,oBAAoB,YAAY,UAAU,mBAAmB,QAAQ,WAAW,SAAS,CAAC;AAE9H,QAAM,UAAM,2BAAY,YAAY;AAClC,UAAM,MAAM;AACZ,WAAO,SAAS;AAAA,EAClB,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,QAAM,QAAgC;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,OACE,gBAAgB,+BAChB,WAAW,cACX,WAAW,iBACX,WAAW,iBACX,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,6CAAC,kBAAkB,UAAlB,EAA2B,OAAe,UAAS;AAC7D;AAoBA,eAAe,gBAAgB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,MAAM,MAAM,MAAM,WAAW,YAAY,6BAA6B,SAAS,WAAW,GAAG;AAAA,IACjG,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,eAAe,YAAY,4BAA4B,eAAe,CAAC;AAAA,EAChG,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,OAAO,IAAI,MAAM,MAAM,OAAO,WAAW,iCAAiC,GAAG;AAAA,MACxF,MAAM,MAAM,OAAO,QAAQ;AAAA,IAC7B,CAAC;AAAA,EACH;AACA,SAAO,MAAM,QAAQ;AACvB;AAGA,SAAS,WAAW,MAAc,MAAsB;AACtD,SAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,CAAC,GAAG,IAAI;AAC3C;AAEO,SAAS,gBAAwC;AACtD,QAAM,UAAM,0BAAW,iBAAiB;AACxC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAClF,SAAO;AACT;;;AEnlBA,IAAAC,gBAAkE;AAsEzD,IAAAC,sBAAA;AA9BT,SAAS,SAAS,MAA2B,SAAmC;AAC9E,QAAM,UAAM,0BAAW,iBAAiB;AACxC,QAAM,cAAU,sBAA8B,IAAI;AAClD,QAAM,WAAW,KAAK,YAAY;AAElC,+BAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AAIrB,QAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAU;AAEhC,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,MAAM,SAAS;AACtB,SAAK,YAAY,MAAM;AACvB,QAAI,cAAc,MAAM,QAAQ,WAAW,CAAC,CAAC;AAE7C,WAAO,MAAM;AAIX,aAAO,KAAK,WAAY,MAAK,YAAY,KAAK,UAAU;AAAA,IAC1D;AAAA,EAEF,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAO;AACT;AAEO,SAAS,gBAAgB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACnF,SAAO,6CAAC,SAAI,KAAK,SAAS,UAAU,OAAO,GAAG,WAAsB,OAAc;AACpF;AAEO,SAAS,YAAY,EAAE,WAAW,OAAO,QAAQ,GAAyB;AAC/E,SAAO,6CAAC,SAAI,KAAK,SAAS,UAAU,OAAO,GAAG,WAAsB,OAAc;AACpF;AAEO,SAAS,kBAAkB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACrF,SAAO,6CAAC,SAAI,KAAK,SAAS,OAAO,OAAO,GAAG,WAAsB,OAAc;AACjF;AAEO,SAAS,gBAAgB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACnF,SAAO,6CAAC,SAAI,KAAK,SAAS,cAAc,OAAO,GAAG,WAAsB,OAAc;AACxF;","names":["import_react","import_react","import_react","import_jsx_runtime","DEFAULT_BASE_URL","import_react","import_react","import_cart","DEFAULT_STORAGE_KEY","readStored","writeStored","import_react","import_secure_fields","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { CSSProperties, RefObject } from 'react';
2
+ import { CSSProperties, RefObject, ReactNode } from 'react';
3
3
  import { CartSessionCart, AddLineItemInput, UpdateLineItemInput, SelectShippingInput, CheckoutHandoffInput, CheckoutHandoff, SelectedShipping } from '@usethrottle/cart';
4
4
 
5
5
  /**
@@ -84,7 +84,12 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
84
84
  */
85
85
  interface RecurringIntent {
86
86
  plan: string;
87
- interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
87
+ /** A named interval. Or send intervalUnit + intervalCount for any cadence from 1 hour to 1 year. */
88
+ interval?: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly' | 'custom';
89
+ intervalUnit?: 'hour' | 'day' | 'week' | 'month';
90
+ intervalCount?: number;
91
+ /** Makes the subscription an installment plan of exactly this many payments (2–60). Not combinable with trialDays. */
92
+ totalPayments?: number;
88
93
  amount?: number;
89
94
  trialDays?: number;
90
95
  planName?: string;
@@ -361,4 +366,215 @@ interface UseThrottleCheckoutResult {
361
366
  */
362
367
  declare function useThrottleCheckout(options: UseThrottleCheckoutOptions): UseThrottleCheckoutResult;
363
368
 
364
- export { CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, PaymentEmbed, type RecurringIntent, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleCompletedLineItem, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useThrottleCheckout, useThrottleEvents };
369
+ /**
370
+ * Subset of the Secure Fields instance this module needs — real or mocked.
371
+ *
372
+ * Declared structurally rather than as `Pick<SecureFields, …>`: any reference
373
+ * to the SDK's own type from an exported declaration makes tsup emit
374
+ * `import { SecureFields } from '@gr4vy/secure-fields'` into
375
+ * `dist/index.d.ts`, and that package's `exports` map carries no `types`
376
+ * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under
377
+ * `skipLibCheck: false`, or silently degrades the member to `any` under
378
+ * `skipLibCheck: true` (the Next default). The `paths` override in this
379
+ * package's tsconfig repairs our own compile only; it does not ship.
380
+ *
381
+ * Members use method syntax deliberately: TypeScript checks method parameters
382
+ * bivariantly, so the real class's narrower `CombinedEvents` event parameter
383
+ * stays assignable to the `string` declared here.
384
+ */
385
+ interface ThreeDSecureInstance {
386
+ addThreeDSecure(element: string | HTMLElement, options?: {
387
+ challengeWindowSize?: '01' | '02' | '03' | '04' | '05';
388
+ }): void;
389
+ addEventListener(event: string, callback: (...args: any[]) => void): void;
390
+ removeEventListener?(event: string, callback: (...args: any[]) => void): void;
391
+ }
392
+ interface ResolveChallengeArgs {
393
+ /** The `SecureFields` instance already constructed for this session. */
394
+ instance: ThreeDSecureInstance;
395
+ /** Mount point for Gr4vy's own 3DS challenge iframe. */
396
+ element: HTMLElement | string;
397
+ challengeWindowSize?: '01' | '02' | '03' | '04' | '05';
398
+ }
399
+
400
+ /**
401
+ * Lifecycle status for the headless Secure Fields state machine.
402
+ * - `idle` — provider mounted, fields not yet registered
403
+ * - `mounting` — Secure Fields instance constructed, fields registering
404
+ * - `ready` — all required fields registered, submit() may be called
405
+ * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE
406
+ * - `authorizing` — card vaulted, awaiting the merchant's complete() call
407
+ * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS
408
+ * iframe (addThreeDSecure) is mounted and running
409
+ * - `complete` — order created
410
+ * - `error` — vault, challenge, or completion failed (see `error`)
411
+ */
412
+ type SecureCardStatus = 'idle' | 'mounting' | 'ready' | 'vaulting' | 'authorizing' | 'challenging' | 'complete' | 'error';
413
+ /** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */
414
+ interface SecureCardSession {
415
+ sessionId: string;
416
+ /**
417
+ * The payment provider instance this session belongs to, straight from
418
+ * `POST /checkout-sessions/:id/card-session`.
419
+ *
420
+ * Either name works. `providerId` is the documented one; `gr4vyId` is the
421
+ * original and is still accepted, because the package shipped with it and
422
+ * dropping it would break consumers already passing the response through.
423
+ */
424
+ providerId?: string;
425
+ /** @deprecated Use `providerId`. Still honoured. */
426
+ gr4vyId?: string;
427
+ environment: 'sandbox' | 'production';
428
+ }
429
+ interface SecureCardError {
430
+ code: string;
431
+ message: string;
432
+ }
433
+ type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';
434
+ /**
435
+ * Everything this package uses off a `SecureFields` instance, declared
436
+ * structurally instead of re-exporting the SDK's own class type.
437
+ *
438
+ * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming
439
+ * its type in an exported declaration makes tsup emit
440
+ * `import { SecureFields } from '@gr4vy/secure-fields'` into
441
+ * `dist/index.d.ts` — which is a hard TS7016 for any consumer on
442
+ * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent
443
+ * degradation of `instance` to `any` with `skipLibCheck: true` (the Next
444
+ * default). That hits every installer of this package, not only Secure Fields
445
+ * users. The `paths` override in this package's tsconfig repairs our own
446
+ * compile; it is monorepo-only and does not ship. Same approach as
447
+ * `ThreeDSecureInstance` in `threeDSecure.ts`.
448
+ *
449
+ * Members use method syntax deliberately: TypeScript checks method parameters
450
+ * bivariantly, so the real class's narrower option and event types stay
451
+ * assignable to the looser ones declared here.
452
+ */
453
+ interface SecureFieldsInstance {
454
+ /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */
455
+ controller?: HTMLIFrameElement;
456
+ addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;
457
+ addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;
458
+ addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;
459
+ addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;
460
+ addThreeDSecure(element: string | HTMLElement, options?: {
461
+ challengeWindowSize?: '01' | '02' | '03' | '04' | '05';
462
+ }): void;
463
+ addEventListener(event: string, callback: (...args: any[]) => void): void;
464
+ removeEventListener?(event: string, callback: (...args: any[]) => void): void;
465
+ submit(): void;
466
+ }
467
+ interface SecureCardContextValue {
468
+ /**
469
+ * The underlying Secure Fields instance — Task 5's field primitives register
470
+ * against it.
471
+ *
472
+ * `null` until the provider's mount effect has constructed it. It cannot be
473
+ * built during render: `new SecureFields()` mutates the document (see the
474
+ * effect in `SecureCardProvider`), and React does not promise a render-phase
475
+ * factory runs exactly once. Consumers that reach for it directly must
476
+ * null-check; the field primitives already wait for it.
477
+ */
478
+ instance: SecureFieldsInstance | null;
479
+ registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;
480
+ /**
481
+ * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).
482
+ * Register this once, alongside the card fields; it is only actually
483
+ * mounted if `complete()` gets `requires_action`.
484
+ */
485
+ registerThreeDSecure: (element: HTMLElement | string, options?: {
486
+ challengeWindowSize?: '01' | '02' | '03' | '04' | '05';
487
+ }) => void;
488
+ /**
489
+ * Abandons an in-progress challenge (e.g. the buyer closed the challenge
490
+ * UI). Only meaningful while `status === 'challenging'`; settles
491
+ * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept
492
+ * distinct from a processor decline code.
493
+ */
494
+ cancelChallenge: () => void;
495
+ status: SecureCardStatus;
496
+ error: SecureCardError | null;
497
+ /** True once all required fields are registered and submit() may be called. */
498
+ ready: boolean;
499
+ /** vault() then complete(). */
500
+ pay: () => Promise<unknown>;
501
+ vault: () => Promise<void>;
502
+ complete: () => Promise<unknown>;
503
+ }
504
+ interface SecureCardProviderProps {
505
+ session: SecureCardSession;
506
+ /**
507
+ * Throttle's own checkout session id — the `:id` already used to mint
508
+ * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same
509
+ * value as `session.sessionId`: that field is Gr4vy's own checkout session
510
+ * id (required to construct the `SecureFields` instance — see
511
+ * `packages/platform-api/src/routes/card-session.ts`, which returns
512
+ * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,
513
+ * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`
514
+ * route looks `:id` up directly against Throttle's own `checkoutSessions`
515
+ * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not
516
+ * `session.sessionId`, to call the right URL.
517
+ */
518
+ checkoutSessionId: string;
519
+ children: ReactNode;
520
+ /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */
521
+ completeFn?: (args: {
522
+ sessionId: string;
523
+ gr4vySessionId: string;
524
+ }) => Promise<any>;
525
+ /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */
526
+ resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;
527
+ /**
528
+ * Bounds how long `complete()` waits on an unfinished challenge before
529
+ * settling to a terminal `error` (code `three_ds_timeout`) on its own — the
530
+ * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit
531
+ * `cancelChallenge()` call, neither of which is guaranteed to ever fire if
532
+ * the buyer just abandons the tab. Not sourced from Gr4vy documentation
533
+ * (no challenge-duration limit is published in the SDK); 5 minutes is a
534
+ * conservative, overridable default.
535
+ */
536
+ challengeTimeoutMs?: number;
537
+ /**
538
+ * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).
539
+ * Required — not optional-with-a-same-origin-default. Secure Fields is the
540
+ * headless surface for merchants who mount the card form on THEIR OWN
541
+ * origin, which by definition is not same-origin with the Throttle API, so
542
+ * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s
543
+ * against the merchant's own domain at payment time instead). Making this
544
+ * required turns that into a compile error instead of a silent runtime
545
+ * failure. Joined onto the request path regardless of a trailing slash.
546
+ */
547
+ apiBaseUrl: string;
548
+ onSuccess?: (result: SecureCardCompleteResult) => void;
549
+ }
550
+ /**
551
+ * The real `POST /checkout-sessions/:id/complete` success body for the
552
+ * Secure Fields flow (`checkout-session-service.ts` `complete()`,
553
+ * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at
554
+ * ~line 1849). There is no `order` key.
555
+ */
556
+ interface SecureCardCompleteResult {
557
+ orderId: string;
558
+ orderNumber: string;
559
+ paymentId: string;
560
+ /** Minor units (cents). */
561
+ total: number;
562
+ currency: string;
563
+ status: string;
564
+ paymentStatus: string;
565
+ }
566
+ declare function SecureCardProvider({ session, checkoutSessionId, children, completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, onSuccess, }: SecureCardProviderProps): react_jsx_runtime.JSX.Element;
567
+ declare function useSecureCard(): SecureCardContextValue;
568
+
569
+ interface SecureCardFieldProps {
570
+ className?: string;
571
+ style?: CSSProperties;
572
+ /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */
573
+ options?: Record<string, unknown>;
574
+ }
575
+ declare function CardNumberField({ className, style, options }: SecureCardFieldProps): react_jsx_runtime.JSX.Element;
576
+ declare function ExpiryField({ className, style, options }: SecureCardFieldProps): react_jsx_runtime.JSX.Element;
577
+ declare function SecurityCodeField({ className, style, options }: SecureCardFieldProps): react_jsx_runtime.JSX.Element;
578
+ declare function PostalCodeField({ className, style, options }: SecureCardFieldProps): react_jsx_runtime.JSX.Element;
579
+
580
+ export { CardNumberField, CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, ExpiryField, PaymentEmbed, PostalCodeField, type RecurringIntent, type ResolveChallengeArgs, type SecureCardCompleteResult, type SecureCardContextValue, type SecureCardError, type SecureCardFieldKind, type SecureCardFieldProps, SecureCardProvider, type SecureCardProviderProps, type SecureCardSession, type SecureCardStatus, type SecureFieldsInstance, SecurityCodeField, type ThreeDSecureInstance, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleCompletedLineItem, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useSecureCard, useThrottleCheckout, useThrottleEvents };