@usethrottle/checkout-react 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/index.cjs +16 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -7
- package/dist/index.d.ts +38 -7
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
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,
|
|
@@ -113,7 +114,12 @@ function CheckoutEmbed({
|
|
|
113
114
|
onSucceeded?.({
|
|
114
115
|
orderId: msg.orderId,
|
|
115
116
|
paymentId: msg.paymentId,
|
|
116
|
-
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {}
|
|
117
|
+
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
|
|
118
|
+
// v1.4 analytics enrichment — forwarded verbatim so the parent
|
|
119
|
+
// can fire purchase events without an extra fetch.
|
|
120
|
+
...msg.total !== void 0 ? { total: msg.total } : {},
|
|
121
|
+
...msg.currency !== void 0 ? { currency: msg.currency } : {},
|
|
122
|
+
...msg.lineItems !== void 0 ? { lineItems: msg.lineItems } : {}
|
|
117
123
|
});
|
|
118
124
|
break;
|
|
119
125
|
case "throttle.error":
|
|
@@ -143,7 +149,7 @@ function CheckoutEmbed({
|
|
|
143
149
|
if (primary) params.set("primary", primary);
|
|
144
150
|
if (logo) params.set("logo", logo);
|
|
145
151
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
146
|
-
const src = `${cleanBase}
|
|
152
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
147
153
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
148
154
|
"iframe",
|
|
149
155
|
{
|
|
@@ -165,6 +171,7 @@ function PaymentEmbed({
|
|
|
165
171
|
sessionId,
|
|
166
172
|
parentOrigin,
|
|
167
173
|
baseUrl = DEFAULT_BASE_URL2,
|
|
174
|
+
route = "c",
|
|
168
175
|
primary,
|
|
169
176
|
logo,
|
|
170
177
|
initialHeight = 480,
|
|
@@ -194,7 +201,12 @@ function PaymentEmbed({
|
|
|
194
201
|
onSucceeded?.({
|
|
195
202
|
orderId: msg.orderId,
|
|
196
203
|
paymentId: msg.paymentId,
|
|
197
|
-
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {}
|
|
204
|
+
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
|
|
205
|
+
// v1.4 analytics enrichment — forwarded verbatim so the parent
|
|
206
|
+
// can fire purchase events without an extra fetch.
|
|
207
|
+
...msg.total !== void 0 ? { total: msg.total } : {},
|
|
208
|
+
...msg.currency !== void 0 ? { currency: msg.currency } : {},
|
|
209
|
+
...msg.lineItems !== void 0 ? { lineItems: msg.lineItems } : {}
|
|
198
210
|
});
|
|
199
211
|
break;
|
|
200
212
|
case "throttle.error":
|
|
@@ -221,7 +233,7 @@ function PaymentEmbed({
|
|
|
221
233
|
if (primary) params.set("primary", primary);
|
|
222
234
|
if (logo) params.set("logo", logo);
|
|
223
235
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
224
|
-
const src = `${cleanBase}
|
|
236
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
225
237
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
226
238
|
"iframe",
|
|
227
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 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 });\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 });\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAA8C;;;ACA9C,mBAA0C;AA4BnC,SAAS,kBAAkB,EAAE,WAAW,gBAAgB,UAAU,GAA6B;AACpG,8BAAU,MAAM;AACd,aAAS,OAAO,IAAkB;AAChC,UAAI,GAAG,WAAW,eAAgB;AAClC,UAAI,UAAU,WAAW,GAAG,WAAW,UAAU,QAAQ,cAAe;AACxE,YAAM,OAAO,GAAG;AAChB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAI,KAAK,WAAW,cAAc,KAAK,YAAY,EAAG;AACtD,UAAI,OAAO,KAAK,SAAS,YAAY,CAAC,KAAK,KAAK,WAAW,WAAW,EAAG;AACzE,gBAAU,IAAuB;AAAA,IACnC;AACA,WAAO,iBAAiB,WAAW,MAAM;AACzC,WAAO,MAAM,OAAO,oBAAoB,WAAW,MAAM;AAAA,EAC3D,GAAG,CAAC,WAAW,gBAAgB,SAAS,CAAC;AAC3C;;;AC1CA,IAAAC,gBAAoD;AAmC7C,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA8B,MAAS;AAIvE,+BAAU,MAAM;AACd,QAAI,mBAAmB,OAAW;AAClC,UAAM,MAAM,UAAU,SAAS;AAC/B,QAAI,CAAC,IAAK;AAGV,QAAI,aAAa,kBAAkB,YAAa;AAChD,QAAI,CAAC,YAAa;AAElB,UAAM,UAAiC;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AACA,QAAI,YAAY,SAAS,YAAY;AACrC,gBAAY,cAAc;AAAA,EAC5B,GAAG,CAAC,WAAW,cAAc,gBAAgB,aAAa,QAAQ,CAAC;AACrE;;;AF4BI;AAlFJ,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,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA,UACnF,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;;;AGpGA,IAAAC,gBAA8C;AAmF1C,IAAAC,sBAAA;AA9EJ,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,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA,UACnF,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;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;;;AC5FA,IAAAC,gBAA0D;AAC1D,kBASO;AAEP,IAAM,sBAAsB;AA0C5B,SAAS,WAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAiBO,SAAS,eAAe,SAAsD;AACnF,QAAM,EAAE,eAAe,eAAe,YAAY,QAAQ,IAAI;AAC9D,QAAM,aAAa,QAAQ,eAAe,SAAY,sBAAsB,QAAQ;AAEpF,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAsB,IAAI;AAClD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,8BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,UAAW,UAAS,SAAS;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,WAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,eAAW,IAAI;AACf,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AAAA,IAChB,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,UAAW;AACf,kBAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AAAA,IACd,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,UAAW,YAAW,KAAK;AAAA,IAClC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,kBAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAGA,QAAM,UAAM;AAAA,IACV,OAAO,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,cAAU;AAAA,IACd,CAAC,UAA4B,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IAC/E,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,CAAC,QAAgB,UAA+B,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,CAAC;AAAA,IAC3F,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa,2BAAY,CAAC,WAAmB,IAAI,MAAM,OAAQ,WAAW,MAAM,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACvG,QAAM,qBAAiB;AAAA,IACrB,CAAC,UAA+B,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzF,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,MAAM,IAAI,MAAM,OAAQ,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACzF,QAAM,oBAAgB;AAAA,IACpB,CAAC,SAAiB,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzE,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,MAAM,IAAI,MAAM,OAAQ,eAAe,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AAC3F,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAAA,EACjD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,eAAW;AAAA,IACf,OAAO,UAA0D;AAC/D,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,cAAM,cAAc;AACpB,eAAO,MAAM,OAAO,SAAS,KAAK;AAAA,MACpC,SAAS,GAAG;AACV,iBAAS,CAAU;AACnB,cAAM;AAAA,MACR,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,SAAS;AAAA,EACnC;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,gBAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACtPA,IAAAC,gBAAkE;AAClE,IAAAC,eAYO;AAEP,IAAMC,uBAAsB;AA6E5B,SAASC,YAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASC,aAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,SAAS,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,QAAI,wBAAsB,IAAI;AAClD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAiC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,cAAU,sBAAoB,IAAI;AACxC,UAAQ,UAAU;AAElB,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,+BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,WAAW;AACb,eAAS,SAAS;AAClB,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQC,YAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,cAAU,SAAS;AACnB,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AACd,gBAAU,OAAO;AAAA,IACnB,CAAC,EACA,MAAM,MAAM;AACX,UAAI,UAAW;AACf,MAAAC,aAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AACZ,gBAAU,MAAM;AAAA,IAClB,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,MAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAOA,QAAM,cAAU,2BAAY,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,IAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,qBAAiB,QAAQ,aAAa;AACtC,QAAI,OAAO,QAAQ;AACnB,eAAW,QAAQ,MAAM,aAAa,CAAC,GAAG;AACxC,aAAO,MAAM,OAAO,QAAQ;AAAA,QAC1B,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK,YAAY;AAAA,QAC3B,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AACA,UAAM,OAAO,MAAM;AACnB,QAAI,MAAM;AACR,aAAO,MAAM,OAAO,eAAe;AAAA,QACjC,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,SAAS,KAAK,WAAW;AAAA,QACzB,aAAa,KAAK,eAAe;AAAA,QACjC,uBAAuB,KAAK,yBAAyB;AAAA,MACvD,CAAC;AAAA,IACH;AACA,YAAQ,IAAI;AACZ,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,WAAW,UAAU,CAAC;AAQlC,QAAM,UAAM;AAAA,IACV,OACE,IACA,OAA0D,CAAC,MAC5C;AACf,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,aAAa,IAAI,MAAM,+BAA+B;AAChE,iBAAS,CAAC;AACV,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AACA,gBAAU,SAAS;AACnB,eAAS,IAAI;AACb,UAAI;AACF,YAAI,KAAK,OAAQ,OAAM,cAAc;AACrC,cAAM,SAAS,MAAM,GAAG;AACxB,kBAAU,OAAO;AACjB,eAAO;AAAA,MACT,SAAS,GAAG;AACV,cAAM,QAAQ,aAAa,iCAAoB,aAAa;AAC5D,YAAI,SAAS,aAAa;AACxB,cAAI;AACF,sBAAU,YAAY;AACtB,kBAAM,QAAQ;AACd,gBAAI,KAAK,mBAAmB;AAC1B,oBAAM,UAAU,MAAM,GAAG;AACzB,wBAAU,OAAO;AACjB,qBAAO;AAAA,YACT;AAGA,sBAAU,OAAO;AACjB,kBAAM,SAAS,IAAI,8BAAiB;AAAA,cAClC,SAAS;AAAA,YACX,CAAC;AACD,qBAAS,MAAM;AACf,kBAAM;AAAA,UACR,SAAS,YAAY;AACnB,qBAAS,UAAmB;AAC5B,sBAAU,OAAO;AACjB,kBAAM;AAAA,UACR;AAAA,QACF;AACA,iBAAS,CAAU;AACnB,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,WAAW,aAAa,OAAO;AAAA,EACzD;AAEA,QAAM,cAAU;AAAA,IACd,OAAO,UAA4B;AACjC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAC9F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,QAAgB,UAA+B;AACpD,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,GAAG,EAAE,mBAAmB,MAAM,CAAC;AAC5F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,WAAmB;AACxB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,MAAM,GAAG,EAAE,mBAAmB,MAAM,CAAC;AACrF,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,mBAAe;AAAA,IACnB,OAAO,UAA+B;AACpC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACrG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,YAAY;AAC5C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,GAAG,EAAE,mBAAmB,KAAK,CAAC;AACjF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAChB,QAAM,oBAAgB;AAAA,IACpB,OAAO,SAAiB;AACtB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACnG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,YAAY;AAC7C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,GAAG,EAAE,mBAAmB,KAAK,CAAC;AAClF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,OAAO,MAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAC5D,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,oBAAgB;AAAA,IACpB,CAAC,UACC,IAAI,MAAM,OAAQ,SAAS,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAAA,IAC9E,CAAC,QAAQ,GAAG;AAAA,EACd;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,IAAAA,aAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM,oBAAoB;AAAA,IAC1C,QAAQ,SAAS,IAAI;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","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
|
|
@@ -11,6 +11,20 @@ interface ThrottleEnvelope {
|
|
|
11
11
|
source: 'throttle';
|
|
12
12
|
version: 1;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Line item snapshot on `throttle.completed` (protocol v1.4, additive).
|
|
16
|
+
* Prices are minor units (cents). `sku` is the merchant catalog id — use it
|
|
17
|
+
* as the analytics `content_ids`/`item_id` join key so purchase events match
|
|
18
|
+
* your product feed.
|
|
19
|
+
*/
|
|
20
|
+
interface ThrottleCompletedLineItem {
|
|
21
|
+
id?: string;
|
|
22
|
+
name: string;
|
|
23
|
+
sku?: string;
|
|
24
|
+
quantity: number;
|
|
25
|
+
unitPrice: number;
|
|
26
|
+
total: number;
|
|
27
|
+
}
|
|
14
28
|
type ThrottleEvent = {
|
|
15
29
|
type: 'throttle.ready';
|
|
16
30
|
} | {
|
|
@@ -24,6 +38,10 @@ type ThrottleEvent = {
|
|
|
24
38
|
paymentId: string;
|
|
25
39
|
paymentStatus?: string;
|
|
26
40
|
subscriptionId?: string;
|
|
41
|
+
/** v1.4 additive — order total in minor units, for analytics purchase events. */
|
|
42
|
+
total?: number;
|
|
43
|
+
currency?: string;
|
|
44
|
+
lineItems?: ThrottleCompletedLineItem[];
|
|
27
45
|
} | {
|
|
28
46
|
type: 'throttle.cancelled';
|
|
29
47
|
} | {
|
|
@@ -66,7 +84,7 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
|
|
|
66
84
|
*/
|
|
67
85
|
interface RecurringIntent {
|
|
68
86
|
plan: string;
|
|
69
|
-
interval: 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
87
|
+
interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
70
88
|
amount?: number;
|
|
71
89
|
trialDays?: number;
|
|
72
90
|
planName?: string;
|
|
@@ -84,6 +102,15 @@ interface ThrottleEmbedProps {
|
|
|
84
102
|
parentOrigin: string;
|
|
85
103
|
/** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
|
|
86
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';
|
|
87
114
|
/** Optional brand color override (#rrggbb). */
|
|
88
115
|
primary?: string;
|
|
89
116
|
/** Optional logo URL override (https). */
|
|
@@ -115,6 +142,10 @@ interface ThrottleEmbedProps {
|
|
|
115
142
|
orderId: string;
|
|
116
143
|
paymentId: string;
|
|
117
144
|
subscriptionId?: string;
|
|
145
|
+
/** Order total in minor units — fire `gtag`/`fbq` purchase events with value = total / 100. */
|
|
146
|
+
total?: number;
|
|
147
|
+
currency?: string;
|
|
148
|
+
lineItems?: ThrottleCompletedLineItem[];
|
|
118
149
|
}) => void;
|
|
119
150
|
onFailed?: (evt: {
|
|
120
151
|
code: string;
|
|
@@ -138,7 +169,7 @@ interface CheckoutEmbedExtraProps {
|
|
|
138
169
|
* (`throttle embed-config set --origins ...`). Mismatches render
|
|
139
170
|
* an in-iframe "Embed not authorized" panel.
|
|
140
171
|
*/
|
|
141
|
-
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;
|
|
142
173
|
|
|
143
174
|
/**
|
|
144
175
|
* Payment-only iframe embed. Renders just the Gr4vy provider iframe
|
|
@@ -150,7 +181,7 @@ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo
|
|
|
150
181
|
* `parentOrigin` must match an entry in the merchant's allow-list.
|
|
151
182
|
* The iframe auto-resizes on `throttle.resize` events.
|
|
152
183
|
*/
|
|
153
|
-
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;
|
|
154
185
|
|
|
155
186
|
interface UseThrottleEventsOptions {
|
|
156
187
|
iframeRef: RefObject<HTMLIFrameElement | null>;
|
|
@@ -195,7 +226,7 @@ interface UseCartSessionOptions {
|
|
|
195
226
|
}
|
|
196
227
|
interface UseCartSessionResult {
|
|
197
228
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
198
|
-
cart:
|
|
229
|
+
cart: CartSessionCart | null;
|
|
199
230
|
/** Current opaque session id, or null before the first item. */
|
|
200
231
|
cartSessionId: string | null;
|
|
201
232
|
/** True while a request is in flight. */
|
|
@@ -275,7 +306,7 @@ interface UseThrottleCheckoutOptions {
|
|
|
275
306
|
}
|
|
276
307
|
interface UseThrottleCheckoutResult {
|
|
277
308
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
278
|
-
cart:
|
|
309
|
+
cart: CartSessionCart | null;
|
|
279
310
|
/** Current cart session id, or null before the first item. */
|
|
280
311
|
cartSessionId: string | null;
|
|
281
312
|
/** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */
|
|
@@ -330,4 +361,4 @@ interface UseThrottleCheckoutResult {
|
|
|
330
361
|
*/
|
|
331
362
|
declare function useThrottleCheckout(options: UseThrottleCheckoutOptions): UseThrottleCheckoutResult;
|
|
332
363
|
|
|
333
|
-
export { CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, PaymentEmbed, type RecurringIntent, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useThrottleCheckout, useThrottleEvents };
|
|
364
|
+
export { CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, PaymentEmbed, type RecurringIntent, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleCompletedLineItem, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useThrottleCheckout, useThrottleEvents };
|
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
|
|
@@ -11,6 +11,20 @@ interface ThrottleEnvelope {
|
|
|
11
11
|
source: 'throttle';
|
|
12
12
|
version: 1;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Line item snapshot on `throttle.completed` (protocol v1.4, additive).
|
|
16
|
+
* Prices are minor units (cents). `sku` is the merchant catalog id — use it
|
|
17
|
+
* as the analytics `content_ids`/`item_id` join key so purchase events match
|
|
18
|
+
* your product feed.
|
|
19
|
+
*/
|
|
20
|
+
interface ThrottleCompletedLineItem {
|
|
21
|
+
id?: string;
|
|
22
|
+
name: string;
|
|
23
|
+
sku?: string;
|
|
24
|
+
quantity: number;
|
|
25
|
+
unitPrice: number;
|
|
26
|
+
total: number;
|
|
27
|
+
}
|
|
14
28
|
type ThrottleEvent = {
|
|
15
29
|
type: 'throttle.ready';
|
|
16
30
|
} | {
|
|
@@ -24,6 +38,10 @@ type ThrottleEvent = {
|
|
|
24
38
|
paymentId: string;
|
|
25
39
|
paymentStatus?: string;
|
|
26
40
|
subscriptionId?: string;
|
|
41
|
+
/** v1.4 additive — order total in minor units, for analytics purchase events. */
|
|
42
|
+
total?: number;
|
|
43
|
+
currency?: string;
|
|
44
|
+
lineItems?: ThrottleCompletedLineItem[];
|
|
27
45
|
} | {
|
|
28
46
|
type: 'throttle.cancelled';
|
|
29
47
|
} | {
|
|
@@ -66,7 +84,7 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
|
|
|
66
84
|
*/
|
|
67
85
|
interface RecurringIntent {
|
|
68
86
|
plan: string;
|
|
69
|
-
interval: 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
87
|
+
interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
70
88
|
amount?: number;
|
|
71
89
|
trialDays?: number;
|
|
72
90
|
planName?: string;
|
|
@@ -84,6 +102,15 @@ interface ThrottleEmbedProps {
|
|
|
84
102
|
parentOrigin: string;
|
|
85
103
|
/** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
|
|
86
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';
|
|
87
114
|
/** Optional brand color override (#rrggbb). */
|
|
88
115
|
primary?: string;
|
|
89
116
|
/** Optional logo URL override (https). */
|
|
@@ -115,6 +142,10 @@ interface ThrottleEmbedProps {
|
|
|
115
142
|
orderId: string;
|
|
116
143
|
paymentId: string;
|
|
117
144
|
subscriptionId?: string;
|
|
145
|
+
/** Order total in minor units — fire `gtag`/`fbq` purchase events with value = total / 100. */
|
|
146
|
+
total?: number;
|
|
147
|
+
currency?: string;
|
|
148
|
+
lineItems?: ThrottleCompletedLineItem[];
|
|
118
149
|
}) => void;
|
|
119
150
|
onFailed?: (evt: {
|
|
120
151
|
code: string;
|
|
@@ -138,7 +169,7 @@ interface CheckoutEmbedExtraProps {
|
|
|
138
169
|
* (`throttle embed-config set --origins ...`). Mismatches render
|
|
139
170
|
* an in-iframe "Embed not authorized" panel.
|
|
140
171
|
*/
|
|
141
|
-
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;
|
|
142
173
|
|
|
143
174
|
/**
|
|
144
175
|
* Payment-only iframe embed. Renders just the Gr4vy provider iframe
|
|
@@ -150,7 +181,7 @@ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo
|
|
|
150
181
|
* `parentOrigin` must match an entry in the merchant's allow-list.
|
|
151
182
|
* The iframe auto-resizes on `throttle.resize` events.
|
|
152
183
|
*/
|
|
153
|
-
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;
|
|
154
185
|
|
|
155
186
|
interface UseThrottleEventsOptions {
|
|
156
187
|
iframeRef: RefObject<HTMLIFrameElement | null>;
|
|
@@ -195,7 +226,7 @@ interface UseCartSessionOptions {
|
|
|
195
226
|
}
|
|
196
227
|
interface UseCartSessionResult {
|
|
197
228
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
198
|
-
cart:
|
|
229
|
+
cart: CartSessionCart | null;
|
|
199
230
|
/** Current opaque session id, or null before the first item. */
|
|
200
231
|
cartSessionId: string | null;
|
|
201
232
|
/** True while a request is in flight. */
|
|
@@ -275,7 +306,7 @@ interface UseThrottleCheckoutOptions {
|
|
|
275
306
|
}
|
|
276
307
|
interface UseThrottleCheckoutResult {
|
|
277
308
|
/** Current cart snapshot, or null before the first item / resume. */
|
|
278
|
-
cart:
|
|
309
|
+
cart: CartSessionCart | null;
|
|
279
310
|
/** Current cart session id, or null before the first item. */
|
|
280
311
|
cartSessionId: string | null;
|
|
281
312
|
/** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */
|
|
@@ -330,4 +361,4 @@ interface UseThrottleCheckoutResult {
|
|
|
330
361
|
*/
|
|
331
362
|
declare function useThrottleCheckout(options: UseThrottleCheckoutOptions): UseThrottleCheckoutResult;
|
|
332
363
|
|
|
333
|
-
export { CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, PaymentEmbed, type RecurringIntent, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useThrottleCheckout, useThrottleEvents };
|
|
364
|
+
export { CheckoutEmbed, type CheckoutEmbedExtraProps, type CheckoutTotals, PaymentEmbed, type RecurringIntent, CheckoutEmbed as ThrottleCheckout, type ThrottleCheckoutStatus, type ThrottleCompletedLineItem, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, type ThrottleParentCommand, type ThrottleParentEnvelope, type ThrottleParentMessage, type UseCartSessionOptions, type UseCartSessionResult, type UseThrottleCheckoutOptions, type UseThrottleCheckoutResult, useCartSession, useThrottleCheckout, useThrottleEvents };
|
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,
|
|
@@ -82,7 +83,12 @@ function CheckoutEmbed({
|
|
|
82
83
|
onSucceeded?.({
|
|
83
84
|
orderId: msg.orderId,
|
|
84
85
|
paymentId: msg.paymentId,
|
|
85
|
-
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {}
|
|
86
|
+
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
|
|
87
|
+
// v1.4 analytics enrichment — forwarded verbatim so the parent
|
|
88
|
+
// can fire purchase events without an extra fetch.
|
|
89
|
+
...msg.total !== void 0 ? { total: msg.total } : {},
|
|
90
|
+
...msg.currency !== void 0 ? { currency: msg.currency } : {},
|
|
91
|
+
...msg.lineItems !== void 0 ? { lineItems: msg.lineItems } : {}
|
|
86
92
|
});
|
|
87
93
|
break;
|
|
88
94
|
case "throttle.error":
|
|
@@ -112,7 +118,7 @@ function CheckoutEmbed({
|
|
|
112
118
|
if (primary) params.set("primary", primary);
|
|
113
119
|
if (logo) params.set("logo", logo);
|
|
114
120
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
115
|
-
const src = `${cleanBase}
|
|
121
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
116
122
|
return /* @__PURE__ */ jsx(
|
|
117
123
|
"iframe",
|
|
118
124
|
{
|
|
@@ -134,6 +140,7 @@ function PaymentEmbed({
|
|
|
134
140
|
sessionId,
|
|
135
141
|
parentOrigin,
|
|
136
142
|
baseUrl = DEFAULT_BASE_URL2,
|
|
143
|
+
route = "c",
|
|
137
144
|
primary,
|
|
138
145
|
logo,
|
|
139
146
|
initialHeight = 480,
|
|
@@ -163,7 +170,12 @@ function PaymentEmbed({
|
|
|
163
170
|
onSucceeded?.({
|
|
164
171
|
orderId: msg.orderId,
|
|
165
172
|
paymentId: msg.paymentId,
|
|
166
|
-
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {}
|
|
173
|
+
...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
|
|
174
|
+
// v1.4 analytics enrichment — forwarded verbatim so the parent
|
|
175
|
+
// can fire purchase events without an extra fetch.
|
|
176
|
+
...msg.total !== void 0 ? { total: msg.total } : {},
|
|
177
|
+
...msg.currency !== void 0 ? { currency: msg.currency } : {},
|
|
178
|
+
...msg.lineItems !== void 0 ? { lineItems: msg.lineItems } : {}
|
|
167
179
|
});
|
|
168
180
|
break;
|
|
169
181
|
case "throttle.error":
|
|
@@ -190,7 +202,7 @@ function PaymentEmbed({
|
|
|
190
202
|
if (primary) params.set("primary", primary);
|
|
191
203
|
if (logo) params.set("logo", logo);
|
|
192
204
|
const cleanBase = baseUrl.replace(/\/$/, "");
|
|
193
|
-
const src = `${cleanBase}
|
|
205
|
+
const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;
|
|
194
206
|
return /* @__PURE__ */ jsx2(
|
|
195
207
|
"iframe",
|
|
196
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 });\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 });\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":["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.0",
|
|
4
4
|
"description": "React component for embedded Throttle checkout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
"dist",
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest"
|
|
25
|
+
},
|
|
20
26
|
"peerDependencies": {
|
|
21
27
|
"react": ">=18"
|
|
22
28
|
},
|
|
@@ -35,12 +41,6 @@
|
|
|
35
41
|
"access": "public"
|
|
36
42
|
},
|
|
37
43
|
"dependencies": {
|
|
38
|
-
"@usethrottle/cart": "
|
|
39
|
-
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "tsup",
|
|
42
|
-
"dev": "tsup --watch",
|
|
43
|
-
"test": "vitest run",
|
|
44
|
-
"test:watch": "vitest"
|
|
44
|
+
"@usethrottle/cart": "workspace:^"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|