@usethrottle/checkout-react 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -6
- package/dist/index.d.ts +15 -6
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -68,6 +68,19 @@ export function CheckoutPage({ sessionId }: { sessionId: string }) {
|
|
|
68
68
|
}
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
### Choosing the hosted page: `route`
|
|
72
|
+
|
|
73
|
+
`<PaymentEmbed/>` and `<CheckoutEmbed/>` build their own iframe URL and load the
|
|
74
|
+
`/c` page by default. Pass `route="s"` to load the unified `/s` page instead:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<PaymentEmbed sessionId={sessionId} parentOrigin="https://shop.example.com" route="s" />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The component does not see a URL you built server-side, so if you call
|
|
81
|
+
`buildCheckoutEmbedUrl({ route: 's' })` and then mount a component, set the same
|
|
82
|
+
`route` here. Card-vault (add-a-card) sessions are only served by `/c`.
|
|
83
|
+
|
|
71
84
|
## `<CheckoutEmbed/>` — full checkout
|
|
72
85
|
|
|
73
86
|
```tsx
|
|
@@ -159,7 +172,7 @@ function Cart() {
|
|
|
159
172
|
|
|
160
173
|
Also exposes `updateItem`, `removeItem`, `selectShipping`, `clearShipping`,
|
|
161
174
|
`applyDiscount`, `removeDiscount`, `refresh`, and `reset`. See the
|
|
162
|
-
[cart sessions guide](https://
|
|
175
|
+
[cart sessions guide](https://usethrottle.dev/docs/developers/cart-sessions).
|
|
163
176
|
|
|
164
177
|
## Events
|
|
165
178
|
|
package/dist/index.cjs
CHANGED
|
@@ -83,6 +83,7 @@ function CheckoutEmbed({
|
|
|
83
83
|
sessionId,
|
|
84
84
|
parentOrigin,
|
|
85
85
|
baseUrl = DEFAULT_BASE_URL,
|
|
86
|
+
route = "c",
|
|
86
87
|
primary,
|
|
87
88
|
logo,
|
|
88
89
|
initialHeight = 600,
|
|
@@ -148,7 +149,7 @@ function CheckoutEmbed({
|
|
|
148
149
|
if (primary) params.set("primary", primary);
|
|
149
150
|
if (logo) params.set("logo", logo);
|
|
150
151
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
151
|
-
const src = `${cleanBase}
|
|
152
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
152
153
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
153
154
|
"iframe",
|
|
154
155
|
{
|
|
@@ -170,6 +171,7 @@ function PaymentEmbed({
|
|
|
170
171
|
sessionId,
|
|
171
172
|
parentOrigin,
|
|
172
173
|
baseUrl = DEFAULT_BASE_URL2,
|
|
174
|
+
route = "c",
|
|
173
175
|
primary,
|
|
174
176
|
logo,
|
|
175
177
|
initialHeight = 480,
|
|
@@ -231,7 +233,7 @@ function PaymentEmbed({
|
|
|
231
233
|
if (primary) params.set("primary", primary);
|
|
232
234
|
if (logo) params.set("logo", logo);
|
|
233
235
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
234
|
-
const src = `${cleanBase}
|
|
236
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
235
237
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
236
238
|
"iframe",
|
|
237
239
|
{
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 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}/c/${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 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}/c/${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 Cart,\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: Cart | 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<Cart | 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<Cart>, 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 Cart,\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: Cart | 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: Cart | 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<Cart | 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<Cart | 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<Cart> => {\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"],"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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { CSSProperties, RefObject } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { CartSessionCart, AddLineItemInput, UpdateLineItemInput, SelectShippingInput, CheckoutHandoffInput, CheckoutHandoff, SelectedShipping } from '@usethrottle/cart';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Envelope every Throttle iframe message carries. Lets parents
|
|
@@ -84,7 +84,7 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
|
|
|
84
84
|
*/
|
|
85
85
|
interface RecurringIntent {
|
|
86
86
|
plan: string;
|
|
87
|
-
interval: 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
87
|
+
interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
88
88
|
amount?: number;
|
|
89
89
|
trialDays?: number;
|
|
90
90
|
planName?: string;
|
|
@@ -102,6 +102,15 @@ interface ThrottleEmbedProps {
|
|
|
102
102
|
parentOrigin: string;
|
|
103
103
|
/** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
|
|
104
104
|
baseUrl?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Which hosted page the iframe loads: `'c'` (the original embed) or `'s'`
|
|
107
|
+
* (the unified embed). Defaults to `'c'`, so existing integrations do not
|
|
108
|
+
* move. Set it to match the `route` you pass to `buildCheckoutEmbedUrl` in
|
|
109
|
+
* `@usethrottle/checkout-sdk/server` — the component builds its own URL and
|
|
110
|
+
* does not see the one you built server-side. Card-vault (add-a-card)
|
|
111
|
+
* sessions are only served by `'c'`.
|
|
112
|
+
*/
|
|
113
|
+
route?: 'c' | 's';
|
|
105
114
|
/** Optional brand color override (#rrggbb). */
|
|
106
115
|
primary?: string;
|
|
107
116
|
/** Optional logo URL override (https). */
|
|
@@ -160,7 +169,7 @@ interface CheckoutEmbedExtraProps {
|
|
|
160
169
|
* (`throttle embed-config set --origins ...`). Mismatches render
|
|
161
170
|
* an in-iframe "Embed not authorized" panel.
|
|
162
171
|
*/
|
|
163
|
-
declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
|
|
172
|
+
declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, route, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
|
|
164
173
|
|
|
165
174
|
/**
|
|
166
175
|
* Payment-only iframe embed. Renders just the Gr4vy provider iframe
|
|
@@ -172,7 +181,7 @@ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo
|
|
|
172
181
|
* `parentOrigin` must match an entry in the merchant's allow-list.
|
|
173
182
|
* The iframe auto-resizes on `throttle.resize` events.
|
|
174
183
|
*/
|
|
175
|
-
declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
|
|
184
|
+
declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, route, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
|
|
176
185
|
|
|
177
186
|
interface UseThrottleEventsOptions {
|
|
178
187
|
iframeRef: RefObject<HTMLIFrameElement | null>;
|
|
@@ -217,7 +226,7 @@ interface UseCartSessionOptions {
|
|
|
217
226
|
}
|
|
218
227
|
interface UseCartSessionResult {
|
|
219
228
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
220
|
-
cart:
|
|
229
|
+
cart: CartSessionCart | null;
|
|
221
230
|
/** Current opaque session id, or null before the first item. */
|
|
222
231
|
cartSessionId: string | null;
|
|
223
232
|
/** True while a request is in flight. */
|
|
@@ -297,7 +306,7 @@ interface UseThrottleCheckoutOptions {
|
|
|
297
306
|
}
|
|
298
307
|
interface UseThrottleCheckoutResult {
|
|
299
308
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
300
|
-
cart:
|
|
309
|
+
cart: CartSessionCart | null;
|
|
301
310
|
/** Current cart session id, or null before the first item. */
|
|
302
311
|
cartSessionId: string | null;
|
|
303
312
|
/** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { CSSProperties, RefObject } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { CartSessionCart, AddLineItemInput, UpdateLineItemInput, SelectShippingInput, CheckoutHandoffInput, CheckoutHandoff, SelectedShipping } from '@usethrottle/cart';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Envelope every Throttle iframe message carries. Lets parents
|
|
@@ -84,7 +84,7 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
|
|
|
84
84
|
*/
|
|
85
85
|
interface RecurringIntent {
|
|
86
86
|
plan: string;
|
|
87
|
-
interval: 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
87
|
+
interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
88
88
|
amount?: number;
|
|
89
89
|
trialDays?: number;
|
|
90
90
|
planName?: string;
|
|
@@ -102,6 +102,15 @@ interface ThrottleEmbedProps {
|
|
|
102
102
|
parentOrigin: string;
|
|
103
103
|
/** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
|
|
104
104
|
baseUrl?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Which hosted page the iframe loads: `'c'` (the original embed) or `'s'`
|
|
107
|
+
* (the unified embed). Defaults to `'c'`, so existing integrations do not
|
|
108
|
+
* move. Set it to match the `route` you pass to `buildCheckoutEmbedUrl` in
|
|
109
|
+
* `@usethrottle/checkout-sdk/server` — the component builds its own URL and
|
|
110
|
+
* does not see the one you built server-side. Card-vault (add-a-card)
|
|
111
|
+
* sessions are only served by `'c'`.
|
|
112
|
+
*/
|
|
113
|
+
route?: 'c' | 's';
|
|
105
114
|
/** Optional brand color override (#rrggbb). */
|
|
106
115
|
primary?: string;
|
|
107
116
|
/** Optional logo URL override (https). */
|
|
@@ -160,7 +169,7 @@ interface CheckoutEmbedExtraProps {
|
|
|
160
169
|
* (`throttle embed-config set --origins ...`). Mismatches render
|
|
161
170
|
* an in-iframe "Embed not authorized" panel.
|
|
162
171
|
*/
|
|
163
|
-
declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
|
|
172
|
+
declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, route, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
|
|
164
173
|
|
|
165
174
|
/**
|
|
166
175
|
* Payment-only iframe embed. Renders just the Gr4vy provider iframe
|
|
@@ -172,7 +181,7 @@ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo
|
|
|
172
181
|
* `parentOrigin` must match an entry in the merchant's allow-list.
|
|
173
182
|
* The iframe auto-resizes on `throttle.resize` events.
|
|
174
183
|
*/
|
|
175
|
-
declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
|
|
184
|
+
declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, route, primary, logo, initialHeight, className, style, submitDisabled, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
|
|
176
185
|
|
|
177
186
|
interface UseThrottleEventsOptions {
|
|
178
187
|
iframeRef: RefObject<HTMLIFrameElement | null>;
|
|
@@ -217,7 +226,7 @@ interface UseCartSessionOptions {
|
|
|
217
226
|
}
|
|
218
227
|
interface UseCartSessionResult {
|
|
219
228
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
220
|
-
cart:
|
|
229
|
+
cart: CartSessionCart | null;
|
|
221
230
|
/** Current opaque session id, or null before the first item. */
|
|
222
231
|
cartSessionId: string | null;
|
|
223
232
|
/** True while a request is in flight. */
|
|
@@ -297,7 +306,7 @@ interface UseThrottleCheckoutOptions {
|
|
|
297
306
|
}
|
|
298
307
|
interface UseThrottleCheckoutResult {
|
|
299
308
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
300
|
-
cart:
|
|
309
|
+
cart: CartSessionCart | null;
|
|
301
310
|
/** Current cart session id, or null before the first item. */
|
|
302
311
|
cartSessionId: string | null;
|
|
303
312
|
/** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ function CheckoutEmbed({
|
|
|
52
52
|
sessionId,
|
|
53
53
|
parentOrigin,
|
|
54
54
|
baseUrl = DEFAULT_BASE_URL,
|
|
55
|
+
route = "c",
|
|
55
56
|
primary,
|
|
56
57
|
logo,
|
|
57
58
|
initialHeight = 600,
|
|
@@ -117,7 +118,7 @@ function CheckoutEmbed({
|
|
|
117
118
|
if (primary) params.set("primary", primary);
|
|
118
119
|
if (logo) params.set("logo", logo);
|
|
119
120
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
120
|
-
const src = `${cleanBase}
|
|
121
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
121
122
|
return /* @__PURE__ */ jsx(
|
|
122
123
|
"iframe",
|
|
123
124
|
{
|
|
@@ -139,6 +140,7 @@ function PaymentEmbed({
|
|
|
139
140
|
sessionId,
|
|
140
141
|
parentOrigin,
|
|
141
142
|
baseUrl = DEFAULT_BASE_URL2,
|
|
143
|
+
route = "c",
|
|
142
144
|
primary,
|
|
143
145
|
logo,
|
|
144
146
|
initialHeight = 480,
|
|
@@ -200,7 +202,7 @@ function PaymentEmbed({
|
|
|
200
202
|
if (primary) params.set("primary", primary);
|
|
201
203
|
if (logo) params.set("logo", logo);
|
|
202
204
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
203
|
-
const src = `${cleanBase}
|
|
205
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
204
206
|
return /* @__PURE__ */ jsx2(
|
|
205
207
|
"iframe",
|
|
206
208
|
{
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx"],"sourcesContent":["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 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}/c/${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 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}/c/${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 Cart,\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: Cart | 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<Cart | 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<Cart>, 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 Cart,\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: Cart | 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: Cart | 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<Cart | 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<Cart | 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<Cart> => {\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":";AAAA,SAAS,aAAa,QAAQ,YAAAA,iBAAgB;;;ACA9C,SAAS,iBAAiC;AA4BnC,SAAS,kBAAkB,EAAE,WAAW,gBAAgB,UAAU,GAA6B;AACpG,YAAU,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,SAAS,aAAAC,YAAW,gBAAgC;AAmC7C,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,UAAU,WAAW,IAAI,SAA8B,MAAS;AAIvE,EAAAA,WAAU,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;;;AFiCI;AAvFJ,IAAM,mBAAmB;AAYlB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;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,YAAY,OAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AAEpD,QAAM,WAAW;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,MAAM,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEhF,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;;;AGzGA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,YAAAC,iBAAgB;AAwF1C,gBAAAC,YAAA;AAnFJ,IAAMC,oBAAmB;AAYlB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,UAAUA;AAAA,EACV;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,YAAYC,QAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AAEpD,QAAM,WAAWC;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,MAAM,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEhF,SACE,gBAAAJ;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;;;ACjGA,SAAS,eAAAK,cAAa,aAAAC,YAAW,SAAS,YAAAC,iBAAgB;AAC1D;AAAA,EACE;AAAA,OAQK;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,IAAIA,UAAsB,IAAI;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAIrD,QAAM,EAAE,QAAQ,UAAU,IAAI,QAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,kBAAkB,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,EAAAD,WAAU,MAAM;AACd,QAAI,UAAW,UAAS,SAAS;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AAGd,EAAAA,WAAU,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,gBAAgBD;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,MAAMA;AAAA,IACV,OAAO,IAAyB,OAA6B,CAAC,MAAqB;AACjF,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,UAAUA;AAAA,IACd,CAAC,UAA4B,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IAC/E,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,aAAaA;AAAA,IACjB,CAAC,QAAgB,UAA+B,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,CAAC;AAAA,IAC3F,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,aAAaA,aAAY,CAAC,WAAmB,IAAI,MAAM,OAAQ,WAAW,MAAM,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACvG,QAAM,iBAAiBA;AAAA,IACrB,CAAC,UAA+B,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzF,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,gBAAgBA,aAAY,MAAM,IAAI,MAAM,OAAQ,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACzF,QAAM,gBAAgBA;AAAA,IACpB,CAAC,SAAiB,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzE,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAiBA,aAAY,MAAM,IAAI,MAAM,OAAQ,eAAe,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AAC3F,QAAM,UAAUA,aAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAAA,EACjD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,WAAWA;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,QAAQA,aAAY,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,SAAS,eAAAG,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAClE;AAAA,EACE,qBAAAC;AAAA,EACA;AAAA,EACA;AAAA,OASK;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,MAA0C;AAC1D,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,IAAIF,UAAsB,IAAI;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAiC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAIrD,QAAM,UAAUD,QAAoB,IAAI;AACxC,UAAQ,UAAU;AAElB,QAAM,EAAE,QAAQ,UAAU,IAAID,SAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAIG,mBAAkB,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,EAAAJ,WAAU,MAAM;AACd,QAAI,WAAW;AACb,eAAS,SAAS;AAClB,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQM,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,gBAAgBR;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,MAAAQ,aAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAOA,QAAM,UAAUR,aAAY,YAA2B;AACrD,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,IAAAQ,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,MAAMR;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,oBAAoB,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,iBAAiB;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,UAAUA;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,aAAaA;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,aAAaA;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,eAAeA;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,gBAAgBA,aAAY,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,gBAAgBA;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,iBAAiBA,aAAY,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,UAAUA,aAAY,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,gBAAgBA;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,QAAQA,aAAY,MAAM;AAC9B,IAAAQ,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;","names":["useState","useEffect","useState","useCallback","useRef","useState","jsx","DEFAULT_BASE_URL","useRef","useState","useCallback","useCallback","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","CartSessionClient","DEFAULT_STORAGE_KEY","readStored","writeStored"]}
|
|
1
|
+
{"version":3,"sources":["../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx"],"sourcesContent":["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":";AAAA,SAAS,aAAa,QAAQ,YAAAA,iBAAgB;;;ACA9C,SAAS,iBAAiC;AA4BnC,SAAS,kBAAkB,EAAE,WAAW,gBAAgB,UAAU,GAA6B;AACpG,YAAU,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,SAAS,aAAAC,YAAW,gBAAgC;AAmC7C,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,UAAU,WAAW,IAAI,SAA8B,MAAS;AAIvE,EAAAA,WAAU,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,YAAY,OAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AAEpD,QAAM,WAAW;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,SAAS,eAAAC,cAAa,UAAAC,SAAQ,YAAAC,iBAAgB;AAyF1C,gBAAAC,YAAA;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,YAAYC,QAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AAEpD,QAAM,WAAWC;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,gBAAAJ;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,SAAS,eAAAK,cAAa,aAAAC,YAAW,SAAS,YAAAC,iBAAgB;AAC1D;AAAA,EACE;AAAA,OAQK;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,IAAIA,UAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAIrD,QAAM,EAAE,QAAQ,UAAU,IAAI,QAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,kBAAkB,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,EAAAD,WAAU,MAAM;AACd,QAAI,UAAW,UAAS,SAAS;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AAGd,EAAAA,WAAU,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,gBAAgBD;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,MAAMA;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,UAAUA;AAAA,IACd,CAAC,UAA4B,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IAC/E,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,aAAaA;AAAA,IACjB,CAAC,QAAgB,UAA+B,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,CAAC;AAAA,IAC3F,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,aAAaA,aAAY,CAAC,WAAmB,IAAI,MAAM,OAAQ,WAAW,MAAM,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACvG,QAAM,iBAAiBA;AAAA,IACrB,CAAC,UAA+B,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzF,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,gBAAgBA,aAAY,MAAM,IAAI,MAAM,OAAQ,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACzF,QAAM,gBAAgBA;AAAA,IACpB,CAAC,SAAiB,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzE,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAiBA,aAAY,MAAM,IAAI,MAAM,OAAQ,eAAe,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AAC3F,QAAM,UAAUA,aAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAAA,EACjD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,WAAWA;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,QAAQA,aAAY,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,SAAS,eAAAG,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAClE;AAAA,EACE,qBAAAC;AAAA,EACA;AAAA,EACA;AAAA,OASK;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,IAAIF,UAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAiC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAIrD,QAAM,UAAUD,QAA+B,IAAI;AACnD,UAAQ,UAAU;AAElB,QAAM,EAAE,QAAQ,UAAU,IAAID,SAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAIG,mBAAkB,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,EAAAJ,WAAU,MAAM;AACd,QAAI,WAAW;AACb,eAAS,SAAS;AAClB,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQM,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,gBAAgBR;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,MAAAQ,aAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAOA,QAAM,UAAUR,aAAY,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,IAAAQ,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,MAAMR;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,oBAAoB,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,iBAAiB;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,UAAUA;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,aAAaA;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,aAAaA;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,eAAeA;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,gBAAgBA,aAAY,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,gBAAgBA;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,iBAAiBA,aAAY,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,UAAUA,aAAY,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,gBAAgBA;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,QAAQA,aAAY,MAAM;AAC9B,IAAAQ,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;","names":["useState","useEffect","useState","useCallback","useRef","useState","jsx","DEFAULT_BASE_URL","useRef","useState","useCallback","useCallback","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","CartSessionClient","DEFAULT_STORAGE_KEY","readStored","writeStored"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usethrottle/checkout-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "React component for embedded Throttle checkout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@usethrottle/cart": "^3.
|
|
38
|
+
"@usethrottle/cart": "^3.9.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|