@kotao/storefront 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/{chunk-RFM27BKZ.js → chunk-BV4OLXV4.js} +7 -3
  2. package/dist/chunk-BV4OLXV4.js.map +1 -0
  3. package/dist/{chunk-OGWYTYYS.js → chunk-GR4QJ7EC.js} +14 -22
  4. package/dist/chunk-GR4QJ7EC.js.map +1 -0
  5. package/dist/{chunk-3YQXJNR7.js → chunk-PWVXOMDE.js} +18 -9
  6. package/dist/chunk-PWVXOMDE.js.map +1 -0
  7. package/dist/{chunk-DTKLAOKR.js → chunk-XZVCUAZU.js} +14 -4
  8. package/dist/chunk-XZVCUAZU.js.map +1 -0
  9. package/dist/{client-CwOPPpFF.d.ts → client-D-M3Y1ul.d.ts} +2 -0
  10. package/dist/{createCartHandler-7DdKB-GF.d.ts → createCartHandler-DJEZW4o2.d.ts} +1 -1
  11. package/dist/editor.d.ts +1 -1
  12. package/dist/editor.js +1 -1
  13. package/dist/events-react.d.ts +1 -1
  14. package/dist/events-react.js +20 -9
  15. package/dist/events-react.js.map +1 -1
  16. package/dist/events.d.ts +3 -3
  17. package/dist/image.d.ts +1 -1
  18. package/dist/image.js +1 -1
  19. package/dist/index.d.ts +19 -12
  20. package/dist/index.js +29 -12
  21. package/dist/index.js.map +1 -1
  22. package/dist/react-router.d.ts +19 -7
  23. package/dist/react-router.js +43 -4
  24. package/dist/react-router.js.map +1 -1
  25. package/dist/react.d.ts +4 -5
  26. package/dist/react.js +144 -21
  27. package/dist/react.js.map +1 -1
  28. package/dist/{routing-Bz3Cg0BW.d.ts → routing-D2GFXAa0.d.ts} +8 -3
  29. package/dist/{shop-CnrnSAJw.d.ts → shop-4y-4z3oZ.d.ts} +5 -4
  30. package/dist/{types-DZX8ScsJ.d.ts → types-C4yXcret.d.ts} +2 -1
  31. package/package.json +2 -2
  32. package/dist/chunk-3YQXJNR7.js.map +0 -1
  33. package/dist/chunk-DTKLAOKR.js.map +0 -1
  34. package/dist/chunk-OGWYTYYS.js.map +0 -1
  35. package/dist/chunk-RFM27BKZ.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cart/useOptimisticCart.ts","../src/content-security-policy.ts","../src/localization/handler.ts","../src/analytics/events.ts","../src/analytics/shop.ts","../src/analytics/consent-encoding.ts","../src/hosted-surface/branding.ts","../src/hosted-surface/localization.ts"],"names":["MARKET_PATH_PREFIX_HEADER"],"mappings":"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","file":"index.js","sourcesContent":["import { CART_FORM_ACTIONS } from './actions'\nimport type { CartDispatchAction } from './createCartHandler'\nimport type { Cart, CartLine } from './types'\n\nconst recalculate = (cart: Cart, lines: readonly CartLine[]): Cart => ({\n ...cart,\n lines,\n totalQuantity: lines.reduce((sum, line) => sum + line.quantity, 0),\n})\n\nexport function applyOptimisticCartAction(cart: Cart, action: CartDispatchAction): Cart {\n switch (action.action) {\n case CART_FORM_ACTIONS.LinesAdd: {\n const inputLines = (action.inputs.lines ?? []) as Array<{\n merchandiseId: string\n quantity?: number | null\n }>\n const additions = inputLines.map<CartLine>((line) => ({\n id: `optimistic:${line.merchandiseId}`,\n quantity: line.quantity ?? 1,\n merchandise: { id: line.merchandiseId },\n attributes: [],\n }))\n return recalculate(cart, [...cart.lines, ...additions])\n }\n case CART_FORM_ACTIONS.LinesUpdate: {\n const updates = new Map(\n ((action.inputs.lines ?? []) as Array<{ id: string; quantity?: number | null }>).map(\n (line) => [line.id, line],\n ),\n )\n const lines = cart.lines\n .map((line) => {\n const update = updates.get(line.id)\n if (!update) return line\n if (update.quantity === 0) return null\n return {\n ...line,\n quantity: update.quantity ?? line.quantity,\n }\n })\n .filter((line): line is CartLine => line !== null)\n return recalculate(cart, lines)\n }\n case CART_FORM_ACTIONS.LinesRemove: {\n const remove = new Set((action.inputs.lineIds ?? []) as string[])\n return recalculate(\n cart,\n cart.lines.filter((line) => !remove.has(line.id)),\n )\n }\n default:\n return cart\n }\n}\n\n/**\n * Pure optimistic helper for React integrations. A React wrapper can call this from `useOptimistic`;\n * keeping the reducer pure lets non-React tests exercise the exact overlay behavior.\n */\nexport function useOptimisticCart(cart: Cart, action: CartDispatchAction | null): Cart {\n return action ? applyOptimisticCartAction(cart, action) : cart\n}\n","import { createNonceProvider, type BoundNonceProvider } from './react/nonce'\n\nexport interface ContentSecurityPolicyShop {\n readonly checkoutDomain?: string\n readonly storeDomain?: string\n}\n\nexport interface CreateContentSecurityPolicyOptions {\n readonly shop?: ContentSecurityPolicyShop\n readonly scriptSrc?: readonly string[]\n readonly connectSrc?: readonly string[]\n readonly imgSrc?: readonly string[]\n readonly styleSrc?: readonly string[]\n}\n\nexport interface ContentSecurityPolicyResult {\n readonly nonce: string\n readonly header: string\n readonly NonceProvider: BoundNonceProvider\n}\n\nconst QUOTED_TOKENS = new Set([\n 'self',\n 'none',\n 'unsafe-eval',\n 'unsafe-hashes',\n 'unsafe-inline',\n 'strict-dynamic',\n 'wasm-unsafe-eval',\n])\n\nexport function createContentSecurityPolicy(\n options: CreateContentSecurityPolicyOptions = {},\n): ContentSecurityPolicyResult {\n const nonce = createNonce()\n const shopDomains = shopSources(options.shop)\n const scriptSrc = uniqueSources([\n ...normalizeSources(options.scriptSrc ?? ['self']),\n `'nonce-${nonce}'`,\n ])\n const connectSrc = uniqueSources([\n 'self',\n ...shopDomains,\n ...normalizeSources(options.connectSrc),\n ])\n const imgSrc = uniqueSources([\n 'self',\n 'data:',\n 'https:',\n ...shopDomains,\n ...normalizeSources(options.imgSrc),\n ])\n const styleSrc = uniqueSources(['self', 'unsafe-inline', ...normalizeSources(options.styleSrc)])\n const directives = [\n ['base-uri', [\"'self'\"]],\n ['default-src', [\"'self'\"]],\n ['connect-src', connectSrc],\n ['img-src', imgSrc],\n ['script-src', scriptSrc],\n ['style-src', styleSrc],\n ]\n return {\n nonce,\n header: directives\n .map(([name, values]) => `${name} ${(values as readonly string[]).join(' ')}`)\n .join('; '),\n NonceProvider: createNonceProvider(nonce),\n }\n}\n\nfunction createNonce(): string {\n const bytes = new Uint8Array(18)\n const runtimeCrypto = (globalThis as typeof globalThis & { crypto?: Crypto }).crypto\n if (runtimeCrypto?.getRandomValues !== undefined) {\n runtimeCrypto.getRandomValues(bytes)\n } else {\n for (let i = 0; i < bytes.length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256)\n }\n }\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\nfunction shopSources(shop: ContentSecurityPolicyShop | undefined): readonly string[] {\n if (shop === undefined) return []\n return [shop.storeDomain, shop.checkoutDomain]\n .filter((domain): domain is string => domain !== undefined && domain.trim() !== '')\n .map((domain) => {\n const trimmed = domain.trim()\n return /^https?:\\/\\//i.test(trimmed) ? new URL(trimmed).origin : `https://${trimmed}`\n })\n}\n\nfunction normalizeSources(sources: readonly string[] | undefined): readonly string[] {\n return (sources ?? []).map((source) => {\n const trimmed = source.trim()\n return QUOTED_TOKENS.has(trimmed) ? `'${trimmed}'` : trimmed\n })\n}\n\nfunction uniqueSources(sources: readonly string[]): readonly string[] {\n return Array.from(new Set(sources.filter((source) => source.length > 0)))\n}\n","import { MARKET_PATH_PREFIX_HEADER } from '@kotao/storefront-contracts'\nimport { buildLocalizationCookie } from './cookie'\nimport { localizePath } from './routing'\n\n/** Same-origin path only: must start with a single '/', not '//' or '/\\'. */\nfunction safeReturnTo(raw: string | null): { path: string; safe: boolean } {\n if (raw === null || raw === '') return { path: '/', safe: true }\n return /^\\/(?![/\\\\])/.test(raw) ? { path: raw, safe: true } : { path: '/', safe: false }\n}\n\nexport async function handleLocalizationPost(request: Request): Promise<Response> {\n const form = await request.formData()\n // Shopify localization forms may include the current value as a hidden input before the clicked\n // submit button with the same name. FormData preserves DOM order, so the successful submitter is\n // the final value and must win over the stale hidden default.\n const country =\n (form.getAll('country_code').at(-1) ?? '').toString().trim().toUpperCase() || null\n const locale = (form.getAll('locale_code').at(-1) ?? '').toString().trim() || null\n const primaryLocale = (form.get('primary_locale') ?? '').toString().trim() || null\n const returnTo = safeReturnTo((form.get('return_to') ?? '').toString())\n const location =\n returnTo.safe && primaryLocale !== null\n ? localizePath(returnTo.path, locale, {\n primaryLocale,\n marketPathPrefix: request.headers.get(MARKET_PATH_PREFIX_HEADER),\n })\n : returnTo.path\n return new Response(null, {\n status: 302,\n headers: { location, 'set-cookie': buildLocalizationCookie({ country, locale }) },\n })\n}\n","/**\n * Coarse visitor geo attached to pixel events — structurally the SDK's `context.geo` (a `Geo` value is\n * assignable here). Declared locally so the analytics surface (and its DOM/React preset) need not pull\n * in the geo crypto module.\n */\nexport interface AnalyticsGeo {\n readonly country: string | null\n readonly city: string | null\n readonly colo: string | null\n readonly botScore: number | null\n readonly clientIp: string | null\n}\n\n/** The standard storefront analytics events (Hydrogen-shaped names). */\nexport const ANALYTICS_EVENTS = {\n pageViewed: 'page_viewed',\n productViewed: 'product_viewed',\n collectionViewed: 'collection_viewed',\n cartViewed: 'cart_viewed',\n searchViewed: 'search_viewed',\n productAddedToCart: 'product_added_to_cart',\n} as const\n\nexport type AnalyticsEventName = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS]\n\nexport type AnalyticsDeviceClass = 'mobile' | 'tablet' | 'desktop'\n\n/** Ambient context the provider attaches to every event before it reaches the pixel. */\nexport interface AnalyticsContext {\n readonly shopId?: string\n readonly url?: string\n readonly geo?: AnalyticsGeo\n readonly referrer?: string\n readonly utmSource?: string\n readonly utmMedium?: string\n readonly utmCampaign?: string\n readonly deviceClass?: AnalyticsDeviceClass\n}\n\nexport interface PageViewedPayload {\n readonly title?: string\n}\nexport interface ProductViewedPayload {\n readonly productId: string\n /** Stable offer id; Google Merchant Center and Google ecommerce events use this as item_id. */\n readonly variantId?: string\n readonly handle?: string\n readonly title?: string\n readonly variantTitle?: string\n readonly vendor?: string\n readonly sku?: string\n /** Major-unit price, e.g. 24.95. */\n readonly price?: number\n readonly currency?: string\n readonly quantity?: number\n}\nexport interface CollectionViewedPayload {\n readonly collectionId: string\n readonly handle?: string\n}\nexport interface CartViewedPayload {\n readonly cartId?: string\n}\nexport interface SearchViewedPayload {\n readonly query: string\n readonly resultCount?: number\n}\nexport interface ProductAddedToCartPayload {\n readonly productId: string\n readonly variantId: string\n readonly quantity: number\n readonly title?: string\n readonly variantTitle?: string\n readonly vendor?: string\n readonly sku?: string\n /** Major-unit price, e.g. 24.95. */\n readonly price?: number\n readonly currency?: string\n}\n\n/** Event name → payload (the typed pub/sub surface). */\nexport interface AnalyticsEventMap {\n page_viewed: PageViewedPayload\n product_viewed: ProductViewedPayload\n collection_viewed: CollectionViewedPayload\n cart_viewed: CartViewedPayload\n search_viewed: SearchViewedPayload\n product_added_to_cart: ProductAddedToCartPayload\n}\n\n/** A pixel-bound event: the name, its payload, the ambient context, a timestamp, and consent state. */\nexport interface PixelEvent<E extends AnalyticsEventName = AnalyticsEventName> {\n readonly event: E\n readonly payload: AnalyticsEventMap[E]\n readonly context: AnalyticsContext\n readonly timestamp: string\n /** Whether the analytics-consent bucket was granted when this event was recorded. */\n readonly consent: boolean\n}\n","export interface ShopAnalytics {\n readonly acceptedLanguage?: string\n readonly currency?: string\n readonly shopId?: string\n readonly storefrontId?: string\n}\n\nexport interface GetShopAnalyticsOptions {\n readonly publicStorefrontId?: string\n readonly storefront?: {\n readonly i18n?: {\n readonly country?: string | null\n readonly currency?: string | null\n readonly language?: string | null\n readonly pathPrefix?: string | null\n } | null\n } | null\n}\n\nexport function getShopAnalytics(options: GetShopAnalyticsOptions): ShopAnalytics {\n const language = options.storefront?.i18n?.language ?? null\n const country = options.storefront?.i18n?.country ?? null\n const currency = options.storefront?.i18n?.currency ?? null\n const acceptedLanguage =\n language != null && country != null ? `${language}-${country}` : (language ?? country)\n\n return {\n ...(acceptedLanguage != null ? { acceptedLanguage } : {}),\n ...(currency != null ? { currency } : {}),\n ...(options.publicStorefrontId != null ? { shopId: options.publicStorefrontId } : {}),\n ...(options.publicStorefrontId != null ? { storefrontId: options.publicStorefrontId } : {}),\n }\n}\n","import { CONSENT_BUCKETS, type ConsentBucket, type ConsentState } from './consent'\n\n/**\n * Encode/decode consent into the `cart.checkoutUrl` handoff (query params), mirroring Shopify's\n * server-set-consent direction (design §Checkout handoff). The handoff target is the Gateway-owned\n * same-origin `/checkout/*` path — `appendConsentToCheckoutUrl` preserves a relative URL as relative\n * so it never points at a separate subdomain.\n */\nconst PARAM_PREFIX = 'consent_'\nconst RELATIVE_BASE = 'https://kotao.invalid'\n\nexport function encodeConsent(state: ConsentState): URLSearchParams {\n const params = new URLSearchParams()\n for (const bucket of CONSENT_BUCKETS)\n params.set(`${PARAM_PREFIX}${bucket}`, state[bucket] ? '1' : '0')\n return params\n}\n\nexport function decodeConsent(params: URLSearchParams): ConsentState {\n const read = (bucket: ConsentBucket): boolean => params.get(`${PARAM_PREFIX}${bucket}`) === '1'\n return {\n analytics: read('analytics'),\n marketing: read('marketing'),\n preferences: read('preferences'),\n }\n}\n\nexport function appendConsentToCheckoutUrl(checkoutUrl: string, state: ConsentState): string {\n // The checkout handoff is the same-origin `/checkout/*` path (cart.checkoutUrl is relative). Reject\n // absolute or protocol-relative URLs so consent params never ride to another origin.\n if (!/^\\/(?!\\/)/.test(checkoutUrl)) {\n throw new Error(`checkoutUrl must be a same-origin path starting with '/'; got: ${checkoutUrl}`)\n }\n const url = new URL(checkoutUrl, RELATIVE_BASE)\n for (const [key, value] of encodeConsent(state)) url.searchParams.set(key, value)\n return `${url.pathname}${url.search}${url.hash}`\n}\n","import {\n CHECKOUT_DESIGN_TOKEN_INPUTS,\n extractCheckoutDesignTokens,\n type CheckoutDesignTokens,\n} from '../checkout/design-tokens'\nimport type { ResolvedSettings } from '../settings/types'\n\nconst CHECKOUT_TOKEN_DEFAULTS: Readonly<Record<string, string>> = Object.fromEntries(\n CHECKOUT_DESIGN_TOKEN_INPUTS.flatMap((input) =>\n 'defaultValue' in input && typeof input.defaultValue === 'string' && input.defaultValue !== ''\n ? [[input.name, input.defaultValue]]\n : [],\n ),\n)\n\n/** Resolve the token contract shared by trusted checkout and customer-account surfaces. */\nexport function resolveHostedBranding(global: ResolvedSettings): CheckoutDesignTokens {\n return extractCheckoutDesignTokens({ global: { ...CHECKOUT_TOKEN_DEFAULTS, ...global } })\n}\n\n/** Escape merchant-controlled text for an HTML text node or double-quoted attribute. */\nexport function escapeHostedHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n}\n\n/**\n * Strip characters that can terminate an inline CSS declaration, block, quote, or style element.\n * Trusted renderers must apply this at the CSS-value sink; the resolved token values themselves\n * remain byte-for-byte compatible with checkout's existing contract.\n */\nexport function sanitizeHostedCssValue(value: string): string {\n return value\n .replace(/[<>\"'`;{}\\\\]/g, '')\n .replace(/[\\r\\n\\t]+/g, ' ')\n .trim()\n}\n","import { readLocalizationCookie } from '../localization/cookie'\n\nexport interface HostedLocaleInput {\n readonly configuredLocales: readonly string[]\n readonly localizationCookie: string | null\n readonly customerLocale: string | null\n readonly primaryLocale: string\n}\n\n/**\n * Resolve display language within the verified shop's configured locale allowlist. Payment\n * jurisdiction and legal-copy decisions deliberately stay outside this display-only resolver.\n */\nexport function resolveHostedLocale(input: HostedLocaleInput): string {\n const configured = input.configuredLocales.map((locale) => locale.trim()).filter(Boolean)\n if (configured.length === 0) throw new Error('hosted surfaces require a configured locale')\n\n const configuredByKey = new Map(configured.map((locale) => [localeKey(locale), locale] as const))\n const cookieLocale = safelyReadCookieLocale(input.localizationCookie)\n\n for (const candidate of [cookieLocale, input.customerLocale, input.primaryLocale]) {\n if (candidate === null || candidate.trim() === '') continue\n const match = configuredByKey.get(localeKey(candidate))\n if (match !== undefined) return match\n }\n\n return configured[0]!\n}\n\nfunction localeKey(locale: string): string {\n return locale.trim().replace(/_/g, '-').toLowerCase()\n}\n\nfunction safelyReadCookieLocale(cookieHeader: string | null): string | null {\n if (cookieHeader === null) return null\n if (!cookieHeader.includes('=')) return cookieHeader.trim() || null\n try {\n return readLocalizationCookie(cookieHeader).locale\n } catch {\n return null\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/cart/useOptimisticCart.ts","../src/content-security-policy.ts","../src/localization/handler.ts","../src/analytics/events.ts","../src/analytics/shop.ts","../src/analytics/consent-encoding.ts","../src/hosted-surface/branding.ts","../src/hosted-surface/localization.ts"],"names":["MARKET_PATH_PREFIX_HEADER"],"mappings":"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","file":"index.js","sourcesContent":["import { CART_FORM_ACTIONS } from './actions'\nimport type { CartDispatchAction } from './createCartHandler'\nimport type { Cart, CartLine } from './types'\n\nconst recalculate = (cart: Cart, lines: readonly CartLine[]): Cart => ({\n ...cart,\n lines,\n totalQuantity: lines.reduce((sum, line) => sum + line.quantity, 0),\n})\n\nexport function applyOptimisticCartAction(cart: Cart, action: CartDispatchAction): Cart {\n switch (action.action) {\n case CART_FORM_ACTIONS.LinesAdd: {\n const inputLines = (action.inputs.lines ?? []) as Array<{\n merchandiseId: string\n quantity?: number | null\n }>\n const additions = inputLines.map<CartLine>((line) => ({\n id: `optimistic:${line.merchandiseId}`,\n quantity: line.quantity ?? 1,\n merchandise: { id: line.merchandiseId },\n attributes: [],\n }))\n return recalculate(cart, [...cart.lines, ...additions])\n }\n case CART_FORM_ACTIONS.LinesUpdate: {\n const updates = new Map(\n ((action.inputs.lines ?? []) as Array<{ id: string; quantity?: number | null }>).map(\n (line) => [line.id, line],\n ),\n )\n const lines = cart.lines\n .map((line) => {\n const update = updates.get(line.id)\n if (!update) return line\n if (update.quantity === 0) return null\n return {\n ...line,\n quantity: update.quantity ?? line.quantity,\n }\n })\n .filter((line): line is CartLine => line !== null)\n return recalculate(cart, lines)\n }\n case CART_FORM_ACTIONS.LinesRemove: {\n const remove = new Set((action.inputs.lineIds ?? []) as string[])\n return recalculate(\n cart,\n cart.lines.filter((line) => !remove.has(line.id)),\n )\n }\n default:\n return cart\n }\n}\n\n/**\n * Pure optimistic helper for React integrations. A React wrapper can call this from `useOptimistic`;\n * keeping the reducer pure lets non-React tests exercise the exact overlay behavior.\n */\nexport function useOptimisticCart(cart: Cart, action: CartDispatchAction | null): Cart {\n return action ? applyOptimisticCartAction(cart, action) : cart\n}\n","import { createNonceProvider, type BoundNonceProvider } from './react/nonce'\n\nexport interface ContentSecurityPolicyShop {\n readonly checkoutDomain?: string\n readonly storeDomain?: string\n}\n\nexport interface CreateContentSecurityPolicyOptions {\n readonly shop?: ContentSecurityPolicyShop\n readonly scriptSrc?: readonly string[]\n readonly connectSrc?: readonly string[]\n readonly imgSrc?: readonly string[]\n readonly styleSrc?: readonly string[]\n}\n\nexport interface ContentSecurityPolicyResult {\n readonly nonce: string\n readonly header: string\n readonly NonceProvider: BoundNonceProvider\n}\n\nconst QUOTED_TOKENS = new Set([\n 'self',\n 'none',\n 'unsafe-eval',\n 'unsafe-hashes',\n 'unsafe-inline',\n 'strict-dynamic',\n 'wasm-unsafe-eval',\n])\n\nexport function createContentSecurityPolicy(\n options: CreateContentSecurityPolicyOptions = {},\n): ContentSecurityPolicyResult {\n const nonce = createNonce()\n const shopDomains = shopSources(options.shop)\n const scriptSrc = uniqueSources([\n ...normalizeSources(options.scriptSrc ?? ['self']),\n `'nonce-${nonce}'`,\n ])\n const connectSrc = uniqueSources([\n 'self',\n ...shopDomains,\n ...normalizeSources(options.connectSrc),\n ])\n const imgSrc = uniqueSources([\n 'self',\n 'data:',\n 'https:',\n ...shopDomains,\n ...normalizeSources(options.imgSrc),\n ])\n const styleSrc = uniqueSources(['self', 'unsafe-inline', ...normalizeSources(options.styleSrc)])\n const directives = [\n ['base-uri', [\"'self'\"]],\n ['default-src', [\"'self'\"]],\n ['connect-src', connectSrc],\n ['img-src', imgSrc],\n ['script-src', scriptSrc],\n ['style-src', styleSrc],\n ]\n return {\n nonce,\n header: directives\n .map(([name, values]) => `${name} ${(values as readonly string[]).join(' ')}`)\n .join('; '),\n NonceProvider: createNonceProvider(nonce),\n }\n}\n\nfunction createNonce(): string {\n const bytes = new Uint8Array(18)\n const runtimeCrypto = (globalThis as typeof globalThis & { crypto?: Crypto }).crypto\n if (runtimeCrypto?.getRandomValues !== undefined) {\n runtimeCrypto.getRandomValues(bytes)\n } else {\n for (let i = 0; i < bytes.length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256)\n }\n }\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\nfunction shopSources(shop: ContentSecurityPolicyShop | undefined): readonly string[] {\n if (shop === undefined) return []\n return [shop.storeDomain, shop.checkoutDomain]\n .filter((domain): domain is string => domain !== undefined && domain.trim() !== '')\n .map((domain) => {\n const trimmed = domain.trim()\n return /^https?:\\/\\//i.test(trimmed) ? new URL(trimmed).origin : `https://${trimmed}`\n })\n}\n\nfunction normalizeSources(sources: readonly string[] | undefined): readonly string[] {\n return (sources ?? []).map((source) => {\n const trimmed = source.trim()\n return QUOTED_TOKENS.has(trimmed) ? `'${trimmed}'` : trimmed\n })\n}\n\nfunction uniqueSources(sources: readonly string[]): readonly string[] {\n return Array.from(new Set(sources.filter((source) => source.length > 0)))\n}\n","import { MARKET_PATH_PREFIX_HEADER } from '@kotao/storefront-contracts'\nimport type { CartHandler } from '../cart/createCartHandler'\nimport { buildLocalizationCookie, readLocalizationCookie } from './cookie'\nimport { localizePath } from './routing'\n\n/** Same-origin path only: must start with a single '/', not '//' or '/\\'. */\nfunction safeReturnTo(raw: string | null): { path: string; safe: boolean } {\n if (raw === null || raw === '') return { path: '/', safe: true }\n return /^\\/(?![/\\\\])/.test(raw) ? { path: raw, safe: true } : { path: '/', safe: false }\n}\n\nexport async function handleLocalizationPost(\n request: Request,\n context?: { readonly cart?: Pick<CartHandler, 'get' | 'updateBuyerIdentity'> },\n): Promise<Response> {\n const form = await request.formData()\n // Shopify localization forms may include the current value as a hidden input before the clicked\n // submit button with the same name. FormData preserves DOM order, so the successful submitter is\n // the final value and must win over the stale hidden default.\n const previous = readLocalizationCookie(request.headers.get('cookie'))\n const country = form.has('country_code')\n ? (form.getAll('country_code').at(-1) ?? '').toString().trim().toUpperCase() || null\n : previous.country\n const locale = form.has('locale_code')\n ? (form.getAll('locale_code').at(-1) ?? '').toString().trim() || null\n : previous.locale\n if ((form.has('country_code') && country === null) || (country !== null && !/^[A-Z]{2}$/.test(country))) {\n return new Response('Invalid country', { status: 400 })\n }\n const primaryLocale = (form.get('primary_locale') ?? '').toString().trim() || null\n const returnTo = safeReturnTo((form.get('return_to') ?? '').toString())\n const location =\n returnTo.safe && primaryLocale !== null\n ? localizePath(returnTo.path, locale, {\n primaryLocale,\n marketPathPrefix: request.headers.get(MARKET_PATH_PREFIX_HEADER),\n })\n : returnTo.path\n const headers = new Headers({ location })\n // Do not create a cart just to change language/country. When one exists, update it before\n // committing the browsing preference so the following checkout uses the same destination.\n if (country !== null && form.has('country_code') && context?.cart !== undefined) {\n const cart = await context.cart.get()\n if (cart !== null && cart.buyerIdentity.countryCode !== country) {\n const result = await context.cart.updateBuyerIdentity({ countryCode: country })\n if (result.cart === null || result.userErrors.length > 0) {\n return new Response('Unable to update cart country', { status: 409 })\n }\n const cartCookie = result.headers.get('set-cookie')\n if (cartCookie !== null) headers.append('set-cookie', cartCookie)\n }\n }\n headers.append('set-cookie', buildLocalizationCookie({ country, locale }))\n return new Response(null, {\n status: 302,\n headers,\n })\n}\n","/**\n * Coarse visitor geo attached to pixel events — structurally the SDK's `context.geo` (a `Geo` value is\n * assignable here). Declared locally so the analytics surface (and its DOM/React preset) need not pull\n * in the geo crypto module.\n */\nexport interface AnalyticsGeo {\n readonly country: string | null\n readonly city: string | null\n readonly colo: string | null\n readonly botScore: number | null\n readonly clientIp: string | null\n}\n\n/** The standard storefront analytics events (Hydrogen-shaped names). */\nexport const ANALYTICS_EVENTS = {\n pageViewed: 'page_viewed',\n productViewed: 'product_viewed',\n collectionViewed: 'collection_viewed',\n cartViewed: 'cart_viewed',\n searchViewed: 'search_viewed',\n productAddedToCart: 'product_added_to_cart',\n} as const\n\nexport type AnalyticsEventName = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS]\n\nexport type AnalyticsDeviceClass = 'mobile' | 'tablet' | 'desktop'\n\n/** Ambient context the provider attaches to every event before it reaches the pixel. */\nexport interface AnalyticsContext {\n readonly shopId?: string\n readonly url?: string\n readonly geo?: AnalyticsGeo\n readonly referrer?: string\n readonly utmSource?: string\n readonly utmMedium?: string\n readonly utmCampaign?: string\n readonly deviceClass?: AnalyticsDeviceClass\n}\n\nexport interface PageViewedPayload {\n readonly title?: string\n}\nexport interface ProductViewedPayload {\n readonly productId: string\n /** Stable offer id; Google Merchant Center and Google ecommerce events use this as item_id. */\n readonly variantId?: string\n readonly handle?: string\n readonly title?: string\n readonly variantTitle?: string\n readonly vendor?: string\n readonly sku?: string\n /** Major-unit price, e.g. 24.95. */\n readonly price?: number\n readonly currency?: string\n readonly quantity?: number\n}\nexport interface CollectionViewedPayload {\n readonly collectionId: string\n readonly handle?: string\n}\nexport interface CartViewedPayload {\n readonly cartId?: string\n}\nexport interface SearchViewedPayload {\n readonly query: string\n readonly resultCount?: number\n}\nexport interface ProductAddedToCartPayload {\n readonly productId: string\n readonly variantId: string\n readonly quantity: number\n readonly title?: string\n readonly variantTitle?: string\n readonly vendor?: string\n readonly sku?: string\n /** Major-unit price, e.g. 24.95. */\n readonly price?: number\n readonly currency?: string\n}\n\n/** Event name → payload (the typed pub/sub surface). */\nexport interface AnalyticsEventMap {\n page_viewed: PageViewedPayload\n product_viewed: ProductViewedPayload\n collection_viewed: CollectionViewedPayload\n cart_viewed: CartViewedPayload\n search_viewed: SearchViewedPayload\n product_added_to_cart: ProductAddedToCartPayload\n}\n\n/** A pixel-bound event: the name, its payload, the ambient context, a timestamp, and consent state. */\nexport interface PixelEvent<E extends AnalyticsEventName = AnalyticsEventName> {\n readonly event: E\n readonly payload: AnalyticsEventMap[E]\n readonly context: AnalyticsContext\n readonly timestamp: string\n /** Whether the analytics-consent bucket was granted when this event was recorded. */\n readonly consent: boolean\n}\n","export interface ShopAnalytics {\n readonly acceptedLanguage?: string\n readonly currency?: string\n readonly shopId?: string\n readonly storefrontId?: string\n}\n\nexport interface GetShopAnalyticsOptions {\n readonly publicStorefrontId?: string\n readonly storefront?: {\n readonly i18n?: {\n readonly country?: string | null\n readonly currency?: string | null\n readonly language?: string | null\n readonly pathPrefix?: string | null\n } | null\n } | null\n}\n\nexport function getShopAnalytics(options: GetShopAnalyticsOptions): ShopAnalytics {\n const language = options.storefront?.i18n?.language ?? null\n const country = options.storefront?.i18n?.country ?? null\n const currency = options.storefront?.i18n?.currency ?? null\n const acceptedLanguage =\n language != null && country != null ? `${language}-${country}` : (language ?? country)\n\n return {\n ...(acceptedLanguage != null ? { acceptedLanguage } : {}),\n ...(currency != null ? { currency } : {}),\n ...(options.publicStorefrontId != null ? { shopId: options.publicStorefrontId } : {}),\n ...(options.publicStorefrontId != null ? { storefrontId: options.publicStorefrontId } : {}),\n }\n}\n","import { CONSENT_BUCKETS, type ConsentBucket, type ConsentState } from './consent'\n\n/**\n * Encode/decode consent into the `cart.checkoutUrl` handoff (query params), mirroring Shopify's\n * server-set-consent direction (design §Checkout handoff). The handoff target is the Gateway-owned\n * same-origin `/checkout/*` path — `appendConsentToCheckoutUrl` preserves a relative URL as relative\n * so it never points at a separate subdomain.\n */\nconst PARAM_PREFIX = 'consent_'\nconst RELATIVE_BASE = 'https://kotao.invalid'\n\nexport function encodeConsent(state: ConsentState): URLSearchParams {\n const params = new URLSearchParams()\n for (const bucket of CONSENT_BUCKETS)\n params.set(`${PARAM_PREFIX}${bucket}`, state[bucket] ? '1' : '0')\n return params\n}\n\nexport function decodeConsent(params: URLSearchParams): ConsentState {\n const read = (bucket: ConsentBucket): boolean => params.get(`${PARAM_PREFIX}${bucket}`) === '1'\n return {\n analytics: read('analytics'),\n marketing: read('marketing'),\n preferences: read('preferences'),\n }\n}\n\nexport function appendConsentToCheckoutUrl(checkoutUrl: string, state: ConsentState): string {\n // The checkout handoff is the same-origin `/checkout/*` path (cart.checkoutUrl is relative). Reject\n // absolute or protocol-relative URLs so consent params never ride to another origin.\n if (!/^\\/(?!\\/)/.test(checkoutUrl)) {\n throw new Error(`checkoutUrl must be a same-origin path starting with '/'; got: ${checkoutUrl}`)\n }\n const url = new URL(checkoutUrl, RELATIVE_BASE)\n for (const [key, value] of encodeConsent(state)) url.searchParams.set(key, value)\n return `${url.pathname}${url.search}${url.hash}`\n}\n","import {\n CHECKOUT_DESIGN_TOKEN_INPUTS,\n extractCheckoutDesignTokens,\n type CheckoutDesignTokens,\n} from '../checkout/design-tokens'\nimport type { ResolvedSettings } from '../settings/types'\n\nconst CHECKOUT_TOKEN_DEFAULTS: Readonly<Record<string, string>> = Object.fromEntries(\n CHECKOUT_DESIGN_TOKEN_INPUTS.flatMap((input) =>\n 'defaultValue' in input && typeof input.defaultValue === 'string' && input.defaultValue !== ''\n ? [[input.name, input.defaultValue]]\n : [],\n ),\n)\n\n/** Resolve the token contract shared by trusted checkout and customer-account surfaces. */\nexport function resolveHostedBranding(global: ResolvedSettings): CheckoutDesignTokens {\n return extractCheckoutDesignTokens({ global: { ...CHECKOUT_TOKEN_DEFAULTS, ...global } })\n}\n\n/** Escape merchant-controlled text for an HTML text node or double-quoted attribute. */\nexport function escapeHostedHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n}\n\n/**\n * Strip characters that can terminate an inline CSS declaration, block, quote, or style element.\n * Trusted renderers must apply this at the CSS-value sink; the resolved token values themselves\n * remain byte-for-byte compatible with checkout's existing contract.\n */\nexport function sanitizeHostedCssValue(value: string): string {\n return value\n .replace(/[<>\"'`;{}\\\\]/g, '')\n .replace(/[\\r\\n\\t]+/g, ' ')\n .trim()\n}\n","import { readLocalizationCookie } from '../localization/cookie'\n\nexport interface HostedLocaleInput {\n readonly configuredLocales: readonly string[]\n readonly localizationCookie: string | null\n readonly customerLocale: string | null\n readonly primaryLocale: string\n}\n\n/**\n * Resolve display language within the verified shop's configured locale allowlist. Payment\n * jurisdiction and legal-copy decisions deliberately stay outside this display-only resolver.\n */\nexport function resolveHostedLocale(input: HostedLocaleInput): string {\n const configured = input.configuredLocales.map((locale) => locale.trim()).filter(Boolean)\n if (configured.length === 0) throw new Error('hosted surfaces require a configured locale')\n\n const configuredByKey = new Map(configured.map((locale) => [localeKey(locale), locale] as const))\n const cookieLocale = safelyReadCookieLocale(input.localizationCookie)\n\n for (const candidate of [cookieLocale, input.customerLocale, input.primaryLocale]) {\n if (candidate === null || candidate.trim() === '') continue\n const match = configuredByKey.get(localeKey(candidate))\n if (match !== undefined) return match\n }\n\n return configured[0]!\n}\n\nfunction localeKey(locale: string): string {\n return locale.trim().replace(/_/g, '-').toLowerCase()\n}\n\nfunction safelyReadCookieLocale(cookieHeader: string | null): string | null {\n if (cookieHeader === null) return null\n if (!cookieHeader.includes('=')) return cookieHeader.trim() || null\n try {\n return readLocalizationCookie(cookieHeader).locale\n } catch {\n return null\n }\n}\n"]}
@@ -1,16 +1,19 @@
1
- import { K as KotaoContext, a as KotaoEnv, S as StorefrontExecutionContext } from './routing-Bz3Cg0BW.js';
2
- export { b as KotaoSession, s as stripLocalePrefixFromRequest } from './routing-Bz3Cg0BW.js';
1
+ import { K as KotaoContext, a as KotaoEnv, S as StorefrontExecutionContext } from './routing-D2GFXAa0.js';
2
+ export { b as KotaoSession, s as stripLocalePrefixFromRequest } from './routing-D2GFXAa0.js';
3
3
  import * as react_router from 'react-router';
4
- import { RouterContextProvider } from 'react-router';
5
- import { H as HydrogenSession } from './types-DZX8ScsJ.js';
4
+ import { EntryContext, RouterContextProvider } from 'react-router';
5
+ import { H as HydrogenSession } from './types-C4yXcret.js';
6
6
  import { b as StorefrontCacheInput } from './client-D51WdvYR.js';
7
7
  import './fetch-Bq_cc8_G.js';
8
8
  import '@kotao/storefront-contracts/theme';
9
- import './createCartHandler-7DdKB-GF.js';
10
- import './client-CwOPPpFF.js';
9
+ import './createCartHandler-DJEZW4o2.js';
10
+ import './client-D-M3Y1ul.js';
11
11
  import './types-CSNXbGFW.js';
12
12
  import './http-types-CGh8WGHB.js';
13
13
 
14
+ /** Static assets and SSR must use the same content-addressed tree, including dynamic Liquid URLs. */
15
+ declare function versionAssetResponse(response: Response, base?: string): Promise<Response>;
16
+
14
17
  type JsonInit = ResponseInit | number;
15
18
  interface JsonResponse<T> {
16
19
  readonly type: string;
@@ -23,6 +26,15 @@ type AwaitedDeferred<T> = T extends Record<string, unknown> ? {
23
26
  declare function json<T>(data: T, init?: JsonInit): JsonResponse<T>;
24
27
  declare function defer<T>(data: T, _init?: JsonInit): Promise<AwaitedDeferred<T>>;
25
28
 
29
+ /** Internal loader-to-document contract; request headers never opt into this behavior. */
30
+ declare const ROUTE_DOCUMENT_RESPONSE_HEADER = "x-kotao-route-document";
31
+ /**
32
+ * UI-route loaders are data loaders: React Router unwraps even HTML Responses and then calls
33
+ * entry.server to render the application. Preserve explicitly marked HTML at that final boundary
34
+ * for Shopify section fragments and Liquid-native documents instead of rendering the app shell.
35
+ */
36
+ declare function routeDocumentResponse(request: Request, status: number, responseHeaders: Headers, context: EntryContext): Response | null;
37
+
26
38
  interface KotaoLoadContextDeps {
27
39
  readonly openCache?: (name: string) => Promise<StorefrontCacheInput>;
28
40
  readonly session?: HydrogenSession;
@@ -63,4 +75,4 @@ declare function getKotaoContext(context: KotaoRouterContextReader): KotaoContex
63
75
  */
64
76
  declare const getKotaoLoadContext: (request: Request, env: KotaoEnv, executionCtx: StorefrontExecutionContext, deps?: KotaoLoadContextDeps) => Promise<KotaoContext>;
65
77
 
66
- export { KotaoContext, KotaoEnv, createKotaoRouterContext, defer, getKotaoContext, getKotaoLoadContext, json, kotaoContext };
78
+ export { KotaoContext, KotaoEnv, ROUTE_DOCUMENT_RESPONSE_HEADER, createKotaoRouterContext, defer, getKotaoContext, getKotaoLoadContext, json, kotaoContext, routeDocumentResponse, versionAssetResponse };
@@ -1,11 +1,28 @@
1
- import { createKotaoContext } from './chunk-DTKLAOKR.js';
2
- export { stripLocalePrefixFromRequest } from './chunk-DTKLAOKR.js';
3
- import './chunk-OGWYTYYS.js';
1
+ import { createKotaoContext } from './chunk-XZVCUAZU.js';
2
+ export { stripLocalePrefixFromRequest } from './chunk-XZVCUAZU.js';
3
+ import './chunk-GR4QJ7EC.js';
4
4
  import './chunk-2MRB6SMP.js';
5
5
  import './chunk-CKGI3Y7L.js';
6
6
  import './chunk-R4N565GX.js';
7
7
  import { createContext, RouterContextProvider } from 'react-router';
8
8
 
9
+ // src/react-router/asset-response.ts
10
+ function versionAssetReferences(source, base) {
11
+ if (!/^\/assets\/v\/[a-f0-9]{32}\/$/.test(base)) return source;
12
+ return source.replace(/(^|[\s"'`(=>])\/assets\/(?!v\/)/g, (_, before) => before + base);
13
+ }
14
+ async function versionAssetResponse(response, base) {
15
+ if (!base || !response.headers.get("content-type")?.includes("text/html") || !response.body) return response;
16
+ const html = await response.text();
17
+ const headers = new Headers(response.headers);
18
+ headers.delete("content-length");
19
+ return new Response(versionAssetReferences(html, base), {
20
+ status: response.status,
21
+ statusText: response.statusText,
22
+ headers
23
+ });
24
+ }
25
+
9
26
  // src/react-router/compat.ts
10
27
  function normalizeJsonInit(init) {
11
28
  return typeof init === "number" ? { status: init } : init;
@@ -28,6 +45,28 @@ async function defer(data, _init) {
28
45
  }
29
46
  return await data;
30
47
  }
48
+
49
+ // src/react-router/document-response.ts
50
+ var ROUTE_DOCUMENT_RESPONSE_HEADER = "x-kotao-route-document";
51
+ function routeDocumentResponse(request, status, responseHeaders, context) {
52
+ const state = context.staticHandlerContext;
53
+ if (state.errors || !["GET", "HEAD"].includes(request.method)) return null;
54
+ const leaf = state.matches.at(-1);
55
+ if (!leaf) return null;
56
+ const headers = state.loaderHeaders[leaf.route.id];
57
+ if (headers?.get(ROUTE_DOCUMENT_RESPONSE_HEADER) !== "html") return null;
58
+ const body = state.loaderData[leaf.route.id];
59
+ if (typeof body !== "string") return null;
60
+ const merged = new Headers(responseHeaders);
61
+ headers.forEach((value, key) => {
62
+ if (key !== ROUTE_DOCUMENT_RESPONSE_HEADER && key !== "content-length" && key !== "set-cookie") {
63
+ merged.set(key, value);
64
+ }
65
+ });
66
+ merged.delete(ROUTE_DOCUMENT_RESPONSE_HEADER);
67
+ merged.delete("content-length");
68
+ return new Response(request.method === "HEAD" ? null : body, { status, headers: merged });
69
+ }
31
70
  var kotaoContext = createContext();
32
71
  function createKotaoRouterContext(context) {
33
72
  const provider = Object.assign(new RouterContextProvider(), context);
@@ -39,6 +78,6 @@ function getKotaoContext(context) {
39
78
  }
40
79
  var getKotaoLoadContext = (request, env, executionCtx, deps) => createKotaoContext(request, env, executionCtx, deps);
41
80
 
42
- export { createKotaoRouterContext, defer, getKotaoContext, getKotaoLoadContext, json, kotaoContext };
81
+ export { ROUTE_DOCUMENT_RESPONSE_HEADER, createKotaoRouterContext, defer, getKotaoContext, getKotaoLoadContext, json, kotaoContext, routeDocumentResponse, versionAssetResponse };
43
82
  //# sourceMappingURL=react-router.js.map
44
83
  //# sourceMappingURL=react-router.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-router/compat.ts","../src/react-router/binding.ts"],"names":[],"mappings":";;;;;;;;;AAUA,SAAS,kBAAkB,IAAA,EAA2C;AACpE,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,EAAE,MAAA,EAAQ,MAAK,GAAI,IAAA;AACvD;AAEA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAEA,SAAS,cAAc,KAAA,EAA+C;AACpE,EAAA,OAAO,QAAA,CAAS,KAAK,CAAA,IAAK,OAAO,MAAM,IAAA,KAAS,UAAA;AAClD;AAEO,SAAS,IAAA,CAAQ,MAAS,IAAA,EAAkC;AACjE,EAAA,OAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,iBAAA,CAAkB,IAAI,CAAC,CAAA;AACpD;AAEA,eAAsB,KAAA,CAAS,MAAS,KAAA,EAA+C;AACrF,EAAA,IAAI,SAAS,IAAI,CAAA,IAAK,CAAC,aAAA,CAAc,IAAI,CAAA,EAAG;AAC1C,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC5B,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,IAAI,OAAO,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM,CAAC,GAAA,EAAK,MAAM,KAAK,CAAU;AAAA,KAC9E;AACA,IAAA,OAAO,MAAA,CAAO,YAAY,OAAO,CAAA;AAAA,EACnC;AAEA,EAAA,OAAQ,MAAM,IAAA;AAChB;ACFO,IAAM,eAAe,aAAA;AAWrB,SAAS,yBAAyB,OAAA,EAA8C;AACrF,EAAA,MAAM,WAAW,MAAA,CAAO,MAAA,CAAO,IAAI,qBAAA,IAAyB,OAAO,CAAA;AACnE,EAAA,QAAA,CAAS,GAAA,CAAI,cAAc,OAAO,CAAA;AAClC,EAAA,OAAO,QAAA;AACT;AAEO,SAAS,gBAAgB,OAAA,EAAiD;AAC/E,EAAA,OAAO,OAAA,CAAQ,IAAI,YAAY,CAAA;AACjC;AAQO,IAAM,mBAAA,GAAsB,CACjC,OAAA,EACA,GAAA,EACA,YAAA,EACA,SAC0B,kBAAA,CAAmB,OAAA,EAAS,GAAA,EAAK,YAAA,EAAc,IAAI","file":"react-router.js","sourcesContent":["type JsonInit = ResponseInit | number\nexport interface JsonResponse<T> {\n readonly type: string\n readonly data: T\n readonly init: ResponseInit | null\n}\n\ntype AwaitedDeferred<T> =\n T extends Record<string, unknown> ? { [K in keyof T]: Awaited<T[K]> } : Awaited<T>\n\nfunction normalizeJsonInit(init?: JsonInit): ResponseInit | undefined {\n return typeof init === 'number' ? { status: init } : init\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return isRecord(value) && typeof value.then === 'function'\n}\n\nexport function json<T>(data: T, init?: JsonInit): JsonResponse<T> {\n return Response.json(data, normalizeJsonInit(init)) as unknown as JsonResponse<T>\n}\n\nexport async function defer<T>(data: T, _init?: JsonInit): Promise<AwaitedDeferred<T>> {\n if (isRecord(data) && !isPromiseLike(data)) {\n const entries = await Promise.all(\n Object.entries(data).map(async ([key, value]) => [key, await value] as const),\n )\n return Object.fromEntries(entries) as AwaitedDeferred<T>\n }\n\n return (await data) as AwaitedDeferred<T>\n}\n","import type { HydrogenSession } from '../cart/types'\nimport type { StorefrontCacheInput } from '../cache/cache'\nimport { createContext, RouterContextProvider } from 'react-router'\nimport {\n createKotaoContext,\n type KotaoContext,\n type KotaoEnv,\n type StorefrontExecutionContext,\n} from '../context/context'\nexport { stripLocalePrefixFromRequest } from '../localization/routing'\n\nexport interface KotaoLoadContextDeps {\n readonly openCache?: (name: string) => Promise<StorefrontCacheInput>\n readonly session?: HydrogenSession\n}\n\ndeclare module 'react-router' {\n /**\n * Compatibility projection for Hydrogen routes that read `context.storefront` directly.\n * React Router 8 requires the provider instance; the canonical typed value remains available\n * through `context.get(kotaoContext)`.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface RouterContextProvider extends KotaoContext {}\n\n /**\n * Source-import compatibility for Hydrogen projects that still name their application context.\n * React Router 8 itself receives `RouterContextProvider`; generated Kotao entries use that type.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface AppLoadContext extends KotaoContext {}\n}\n\nexport const kotaoContext = createContext<KotaoContext>()\n\n/**\n * Structural reader shared by React Router's development and production declaration trees.\n * React Router 8 publishes the provider class from both conditional entry points; their private\n * fields make the two class types nominally incompatible even though the runtime API is identical.\n */\nexport interface KotaoRouterContextReader {\n get<T>(context: { readonly defaultValue?: T }): T\n}\n\nexport function createKotaoRouterContext(context: KotaoContext): RouterContextProvider {\n const provider = Object.assign(new RouterContextProvider(), context)\n provider.set(kotaoContext, context)\n return provider\n}\n\nexport function getKotaoContext(context: KotaoRouterContextReader): KotaoContext {\n return context.get(kotaoContext)\n}\n\n/**\n * Build the Kotao storefront context for a React Router request. Wrap the result with\n * `createKotaoRouterContext` before handing it to React Router 8. Isolated here so the SDK core\n * stays framework-agnostic (a TanStack\n * Start binding can be added alongside without touching the core).\n */\nexport const getKotaoLoadContext = (\n request: Request,\n env: KotaoEnv,\n executionCtx: StorefrontExecutionContext,\n deps?: KotaoLoadContextDeps,\n): Promise<KotaoContext> => createKotaoContext(request, env, executionCtx, deps)\n"]}
1
+ {"version":3,"sources":["../src/react-router/asset-response.ts","../src/react-router/compat.ts","../src/react-router/document-response.ts","../src/react-router/binding.ts"],"names":[],"mappings":";;;;;;;;;AACO,SAAS,sBAAA,CAAuB,QAAgB,IAAA,EAAsB;AAC3E,EAAA,IAAI,CAAC,+BAAA,CAAgC,IAAA,CAAK,IAAI,GAAG,OAAO,MAAA;AACxD,EAAA,OAAO,OAAO,OAAA,CAAQ,kCAAA,EAAoC,CAAC,CAAA,EAAG,MAAA,KAAmB,SAAS,IAAI,CAAA;AAChG;AAGA,eAAsB,oBAAA,CAAqB,UAAoB,IAAA,EAAkC;AAC/F,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,QAAA,CAAS,QAAQ,GAAA,CAAI,cAAc,CAAA,EAAG,QAAA,CAAS,WAAW,CAAA,IAAK,CAAC,QAAA,CAAS,MAAM,OAAO,QAAA;AACpG,EAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAC5C,EAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,EAAA,OAAO,IAAI,QAAA,CAAS,sBAAA,CAAuB,IAAA,EAAM,IAAI,CAAA,EAAG;AAAA,IACtD,QAAQ,QAAA,CAAS,MAAA;AAAA,IAAQ,YAAY,QAAA,CAAS,UAAA;AAAA,IAAY;AAAA,GAC3D,CAAA;AACH;;;ACLA,SAAS,kBAAkB,IAAA,EAA2C;AACpE,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,EAAE,MAAA,EAAQ,MAAK,GAAI,IAAA;AACvD;AAEA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAEA,SAAS,cAAc,KAAA,EAA+C;AACpE,EAAA,OAAO,QAAA,CAAS,KAAK,CAAA,IAAK,OAAO,MAAM,IAAA,KAAS,UAAA;AAClD;AAEO,SAAS,IAAA,CAAQ,MAAS,IAAA,EAAkC;AACjE,EAAA,OAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,iBAAA,CAAkB,IAAI,CAAC,CAAA;AACpD;AAEA,eAAsB,KAAA,CAAS,MAAS,KAAA,EAA+C;AACrF,EAAA,IAAI,SAAS,IAAI,CAAA,IAAK,CAAC,aAAA,CAAc,IAAI,CAAA,EAAG;AAC1C,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC5B,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,IAAI,OAAO,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM,CAAC,GAAA,EAAK,MAAM,KAAK,CAAU;AAAA,KAC9E;AACA,IAAA,OAAO,MAAA,CAAO,YAAY,OAAO,CAAA;AAAA,EACnC;AAEA,EAAA,OAAQ,MAAM,IAAA;AAChB;;;AChCO,IAAM,8BAAA,GAAiC;AAOvC,SAAS,qBAAA,CACd,OAAA,EACA,MAAA,EACA,eAAA,EACA,OAAA,EACiB;AACjB,EAAA,MAAM,QAAQ,OAAA,CAAQ,oBAAA;AACtB,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,CAAC,CAAC,KAAA,EAAO,MAAM,CAAA,CAAE,QAAA,CAAS,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,IAAA;AACtE,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,EAAA,CAAG,EAAE,CAAA;AAChC,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,MAAM,EAAE,CAAA;AACjD,EAAA,IAAI,OAAA,EAAS,GAAA,CAAI,8BAA8B,CAAA,KAAM,QAAQ,OAAO,IAAA;AACpE,EAAA,MAAM,IAAA,GAAgB,KAAA,CAAM,UAAA,CAAW,IAAA,CAAK,MAAM,EAAE,CAAA;AACpD,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AACrC,EAAA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,eAAe,CAAA;AAC1C,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAA,KAAQ;AAC9B,IAAA,IAAI,GAAA,KAAQ,8BAAA,IAAkC,GAAA,KAAQ,gBAAA,IAAoB,QAAQ,YAAA,EAAc;AAC9F,MAAA,MAAA,CAAO,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,IACvB;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,OAAO,8BAA8B,CAAA;AAC5C,EAAA,MAAA,CAAO,OAAO,gBAAgB,CAAA;AAC9B,EAAA,OAAO,IAAI,QAAA,CAAS,OAAA,CAAQ,MAAA,KAAW,MAAA,GAAS,IAAA,GAAO,IAAA,EAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,MAAA,EAAQ,CAAA;AAC1F;ACDO,IAAM,eAAe,aAAA;AAWrB,SAAS,yBAAyB,OAAA,EAA8C;AACrF,EAAA,MAAM,WAAW,MAAA,CAAO,MAAA,CAAO,IAAI,qBAAA,IAAyB,OAAO,CAAA;AACnE,EAAA,QAAA,CAAS,GAAA,CAAI,cAAc,OAAO,CAAA;AAClC,EAAA,OAAO,QAAA;AACT;AAEO,SAAS,gBAAgB,OAAA,EAAiD;AAC/E,EAAA,OAAO,OAAA,CAAQ,IAAI,YAAY,CAAA;AACjC;AAQO,IAAM,mBAAA,GAAsB,CACjC,OAAA,EACA,GAAA,EACA,YAAA,EACA,SAC0B,kBAAA,CAAmB,OAAA,EAAS,GAAA,EAAK,YAAA,EAAc,IAAI","file":"react-router.js","sourcesContent":["/** Rewrite root-relative asset URLs, including Liquid/JSON strings, but never external origins. */\nexport function versionAssetReferences(source: string, base: string): string {\n if (!/^\\/assets\\/v\\/[a-f0-9]{32}\\/$/.test(base)) return source\n return source.replace(/(^|[\\s\"'`(=>])\\/assets\\/(?!v\\/)/g, (_, before: string) => before + base)\n}\n\n/** Static assets and SSR must use the same content-addressed tree, including dynamic Liquid URLs. */\nexport async function versionAssetResponse(response: Response, base?: string): Promise<Response> {\n if (!base || !response.headers.get('content-type')?.includes('text/html') || !response.body) return response\n const html = await response.text()\n const headers = new Headers(response.headers)\n headers.delete('content-length')\n return new Response(versionAssetReferences(html, base), {\n status: response.status, statusText: response.statusText, headers,\n })\n}\n","type JsonInit = ResponseInit | number\nexport interface JsonResponse<T> {\n readonly type: string\n readonly data: T\n readonly init: ResponseInit | null\n}\n\ntype AwaitedDeferred<T> =\n T extends Record<string, unknown> ? { [K in keyof T]: Awaited<T[K]> } : Awaited<T>\n\nfunction normalizeJsonInit(init?: JsonInit): ResponseInit | undefined {\n return typeof init === 'number' ? { status: init } : init\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return isRecord(value) && typeof value.then === 'function'\n}\n\nexport function json<T>(data: T, init?: JsonInit): JsonResponse<T> {\n return Response.json(data, normalizeJsonInit(init)) as unknown as JsonResponse<T>\n}\n\nexport async function defer<T>(data: T, _init?: JsonInit): Promise<AwaitedDeferred<T>> {\n if (isRecord(data) && !isPromiseLike(data)) {\n const entries = await Promise.all(\n Object.entries(data).map(async ([key, value]) => [key, await value] as const),\n )\n return Object.fromEntries(entries) as AwaitedDeferred<T>\n }\n\n return (await data) as AwaitedDeferred<T>\n}\n","import type { EntryContext } from 'react-router'\n\n/** Internal loader-to-document contract; request headers never opt into this behavior. */\nexport const ROUTE_DOCUMENT_RESPONSE_HEADER = 'x-kotao-route-document'\n\n/**\n * UI-route loaders are data loaders: React Router unwraps even HTML Responses and then calls\n * entry.server to render the application. Preserve explicitly marked HTML at that final boundary\n * for Shopify section fragments and Liquid-native documents instead of rendering the app shell.\n */\nexport function routeDocumentResponse(\n request: Request,\n status: number,\n responseHeaders: Headers,\n context: EntryContext,\n): Response | null {\n const state = context.staticHandlerContext\n if (state.errors || !['GET', 'HEAD'].includes(request.method)) return null\n const leaf = state.matches.at(-1)\n if (!leaf) return null\n const headers = state.loaderHeaders[leaf.route.id]\n if (headers?.get(ROUTE_DOCUMENT_RESPONSE_HEADER) !== 'html') return null\n const body: unknown = state.loaderData[leaf.route.id]\n if (typeof body !== 'string') return null\n const merged = new Headers(responseHeaders)\n headers.forEach((value, key) => {\n if (key !== ROUTE_DOCUMENT_RESPONSE_HEADER && key !== 'content-length' && key !== 'set-cookie') {\n merged.set(key, value)\n }\n })\n // React Router already merges loader Set-Cookie headers into the document response.\n merged.delete(ROUTE_DOCUMENT_RESPONSE_HEADER)\n merged.delete('content-length')\n return new Response(request.method === 'HEAD' ? null : body, { status, headers: merged })\n}\n","import type { HydrogenSession } from '../cart/types'\nimport type { StorefrontCacheInput } from '../cache/cache'\nimport { createContext, RouterContextProvider } from 'react-router'\nimport {\n createKotaoContext,\n type KotaoContext,\n type KotaoEnv,\n type StorefrontExecutionContext,\n} from '../context/context'\nexport { stripLocalePrefixFromRequest } from '../localization/routing'\n\nexport interface KotaoLoadContextDeps {\n readonly openCache?: (name: string) => Promise<StorefrontCacheInput>\n readonly session?: HydrogenSession\n}\n\ndeclare module 'react-router' {\n /**\n * Compatibility projection for Hydrogen routes that read `context.storefront` directly.\n * React Router 8 requires the provider instance; the canonical typed value remains available\n * through `context.get(kotaoContext)`.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface RouterContextProvider extends KotaoContext {}\n\n /**\n * Source-import compatibility for Hydrogen projects that still name their application context.\n * React Router 8 itself receives `RouterContextProvider`; generated Kotao entries use that type.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface AppLoadContext extends KotaoContext {}\n}\n\nexport const kotaoContext = createContext<KotaoContext>()\n\n/**\n * Structural reader shared by React Router's development and production declaration trees.\n * React Router 8 publishes the provider class from both conditional entry points; their private\n * fields make the two class types nominally incompatible even though the runtime API is identical.\n */\nexport interface KotaoRouterContextReader {\n get<T>(context: { readonly defaultValue?: T }): T\n}\n\nexport function createKotaoRouterContext(context: KotaoContext): RouterContextProvider {\n const provider = Object.assign(new RouterContextProvider(), context)\n provider.set(kotaoContext, context)\n return provider\n}\n\nexport function getKotaoContext(context: KotaoRouterContextReader): KotaoContext {\n return context.get(kotaoContext)\n}\n\n/**\n * Build the Kotao storefront context for a React Router request. Wrap the result with\n * `createKotaoRouterContext` before handing it to React Router 8. Isolated here so the SDK core\n * stays framework-agnostic (a TanStack\n * Start binding can be added alongside without touching the core).\n */\nexport const getKotaoLoadContext = (\n request: Request,\n env: KotaoEnv,\n executionCtx: StorefrontExecutionContext,\n deps?: KotaoLoadContextDeps,\n): Promise<KotaoContext> => createKotaoContext(request, env, executionCtx, deps)\n"]}
package/dist/react.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import react__default, { ReactNode, ReactElement, ImgHTMLAttributes, VideoHTMLAttributes, ElementType, AnchorHTMLAttributes, CSSProperties, ComponentPropsWithoutRef, ComponentType } from 'react';
2
2
  import { FetcherWithComponents } from 'react-router';
3
- import { j as CartFormAction, a as CartLineInput } from './types-DZX8ScsJ.js';
4
- import { g as getCartFormInput, S as ShopAnalytics, A as AnalyticsContext, C as ConsentState, a as AnalyticsEmitter, b as AnalyticsBus } from './shop-CnrnSAJw.js';
5
- export { u as useNonce } from './shop-CnrnSAJw.js';
3
+ import { j as CartFormAction, a as CartLineInput } from './types-C4yXcret.js';
4
+ import { g as getCartFormInput, S as ShopAnalytics, A as AnalyticsContext, C as ConsentState, a as AnalyticsEmitter, b as AnalyticsBus } from './shop-4y-4z3oZ.js';
5
+ export { u as useNonce } from './shop-4y-4z3oZ.js';
6
6
  import { Cart } from './storefront-api-types.js';
7
7
  import { ImageAttrsOptions, ImageData, ImageCrop, ImageFormat } from './image.js';
8
8
  import { C as Connection, P as PaginationInfo } from './pagination-DCrQk02Q.js';
@@ -82,8 +82,7 @@ interface AnalyticsProviderProps {
82
82
  }
83
83
  /**
84
84
  * `<AnalyticsProvider>` (Hydrogen-shaped) — wires the consent-gated analytics bus and exposes it via
85
- * `useAnalytics`. The visible consent UI is disabled for now; analytics starts granted while the public
86
- * API remains in place for the replacement flow.
85
+ * `useAnalytics`. Optional tracking follows the native CMP and its persisted shopper decision.
87
86
  */
88
87
  declare function AnalyticsProvider(props: AnalyticsProviderProps): ReactElement;
89
88
  declare function useAnalytics(): AnalyticsContextValue;
package/dist/react.js CHANGED
@@ -1,13 +1,13 @@
1
- import { getCartFormInput, createAnalyticsBus, collectAnalyticsAttribution, createPixelTransport } from './chunk-RFM27BKZ.js';
2
- export { useNonce } from './chunk-RFM27BKZ.js';
1
+ import { getCartFormInput, createAnalyticsBus, collectAnalyticsAttribution, createPixelTransport } from './chunk-BV4OLXV4.js';
2
+ export { useNonce } from './chunk-BV4OLXV4.js';
3
3
  import { getPaginationInfo } from './chunk-5QOFKGKD.js';
4
- import { CART_FORM_ACTIONS, GRANTED_CONSENT, CONSENT_COOKIE, readConsentCookie, mergeConsent, buildConsentCookie } from './chunk-OGWYTYYS.js';
4
+ import { CART_FORM_ACTIONS, DENIED_CONSENT, mergeConsent, buildConsentCookie, readConsentCookie, CONSENT_COOKIE } from './chunk-GR4QJ7EC.js';
5
5
  import { getSeoMeta, serializeJsonLd } from './chunk-SVMYFYIA.js';
6
6
  export { REACT_SEAM_CART_DOCUMENTS, createStorefrontApiCartAdapter } from './chunk-WNXI3MPM.js';
7
7
  import { resolveHydrogenEditorStyles } from './chunk-XXNCFWXT.js';
8
8
  export { StorefrontForm, StorefrontFormFields } from './chunk-VV653S52.js';
9
9
  import './chunk-IIOHENCG.js';
10
- import { buildImageAttrs } from './chunk-3YQXJNR7.js';
10
+ import { buildImageAttrs } from './chunk-PWVXOMDE.js';
11
11
  import { createContext, useState, useRef, useMemo, useEffect, useCallback, useContext, useSyncExternalStore, Children, isValidElement, cloneElement, createElement } from 'react';
12
12
  import { useFetcher } from 'react-router';
13
13
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -39,6 +39,93 @@ function useOptimisticData(_id) {
39
39
  return void 0;
40
40
  }
41
41
 
42
+ // src/react/presence-tracker.ts
43
+ function startPresenceTracking(options) {
44
+ let tabId;
45
+ let sequence = 0;
46
+ let lastActivity = Date.now();
47
+ let lastUrl = "";
48
+ let stage = "browsing";
49
+ let interval;
50
+ let stopped = false;
51
+ const activity = (nextStage) => {
52
+ if (document.visibilityState !== "visible") return;
53
+ lastActivity = Date.now();
54
+ if (nextStage) stage = nextStage;
55
+ };
56
+ const onInteraction = () => activity();
57
+ const send = (visible) => {
58
+ if (!options.consent()) return;
59
+ if (!tabId) {
60
+ if (typeof globalThis.crypto?.randomUUID !== "function") return;
61
+ tabId = globalThis.crypto.randomUUID();
62
+ }
63
+ const context = options.context();
64
+ if (context.url !== lastUrl) {
65
+ lastUrl = context.url ?? "";
66
+ const path = new URL(lastUrl, window.location.href).pathname;
67
+ stage = /^\/checkout(?:\/|$)/.test(path) ? "checkout" : /^\/cart(?:\/|$)/.test(path) ? "cart" : /\/products\//.test(path) ? "product" : "browsing";
68
+ lastActivity = Date.now();
69
+ }
70
+ options.send(
71
+ options.endpoint,
72
+ JSON.stringify({
73
+ events: [
74
+ {
75
+ event: "visitor_presence",
76
+ context,
77
+ consent: true,
78
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
79
+ payload: {
80
+ tabId,
81
+ sequence: ++sequence,
82
+ visible,
83
+ stage,
84
+ idleSeconds: Math.min(
85
+ 86400,
86
+ Math.max(0, Math.floor((Date.now() - lastActivity) / 1e3))
87
+ )
88
+ }
89
+ }
90
+ ]
91
+ })
92
+ );
93
+ };
94
+ const visibility = () => {
95
+ if (interval !== void 0) clearInterval(interval);
96
+ interval = void 0;
97
+ const visible = document.visibilityState === "visible";
98
+ send(visible);
99
+ if (visible) interval = setInterval(() => send(true), 2e4);
100
+ };
101
+ const hide = () => {
102
+ if (interval !== void 0) clearInterval(interval);
103
+ interval = void 0;
104
+ send(false);
105
+ };
106
+ const resume = (event) => {
107
+ if (event.persisted) visibility();
108
+ };
109
+ const events = ["pointerdown", "keydown", "scroll"];
110
+ for (const event of events) document.addEventListener(event, onInteraction, { passive: true });
111
+ document.addEventListener("visibilitychange", visibility);
112
+ window.addEventListener("pagehide", hide);
113
+ window.addEventListener("pageshow", resume);
114
+ visibility();
115
+ return {
116
+ activity,
117
+ stop() {
118
+ if (stopped) return;
119
+ stopped = true;
120
+ hide();
121
+ for (const event of events) document.removeEventListener(event, onInteraction);
122
+ document.removeEventListener("visibilitychange", visibility);
123
+ window.removeEventListener("pagehide", hide);
124
+ window.removeEventListener("pageshow", resume);
125
+ }
126
+ };
127
+ }
128
+
42
129
  // src/analytics/anonymous-context.ts
43
130
  function anonymousPageContext(candidateUrl, currentUrl) {
44
131
  if (!candidateUrl || !currentUrl) return {};
@@ -56,7 +143,10 @@ function anonymousPageContext(candidateUrl, currentUrl) {
56
143
  }
57
144
  var beaconSend = (endpoint, body) => {
58
145
  if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
59
- if (navigator.sendBeacon(endpoint, new Blob([body], { type: "application/json" }))) return;
146
+ try {
147
+ if (navigator.sendBeacon(endpoint, new Blob([body], { type: "application/json" }))) return;
148
+ } catch {
149
+ }
60
150
  }
61
151
  if (typeof fetch === "function") {
62
152
  void fetch(endpoint, {
@@ -64,13 +154,18 @@ var beaconSend = (endpoint, body) => {
64
154
  body,
65
155
  keepalive: true,
66
156
  headers: { "content-type": "application/json" }
157
+ }).catch(() => {
67
158
  });
68
159
  }
69
160
  };
70
161
  var AnalyticsReactContext = createContext(null);
162
+ function nativeConsent() {
163
+ const api = typeof window === "undefined" ? void 0 : window.KotaoConsent;
164
+ return typeof api?.get === "function" && typeof api.hasDecision === "function" && typeof api.set === "function" ? api : void 0;
165
+ }
71
166
  function AnalyticsProvider(props) {
72
- const [consent, setConsentState] = useState(GRANTED_CONSENT);
73
- const [decided, setDecided] = useState(true);
167
+ const [consent, setConsentState] = useState(DENIED_CONSENT);
168
+ const [decided, setDecided] = useState(false);
74
169
  const [mounted, setMounted] = useState(false);
75
170
  const consentRef = useRef(consent);
76
171
  consentRef.current = consent;
@@ -122,26 +217,54 @@ function AnalyticsProvider(props) {
122
217
  );
123
218
  useEffect(() => {
124
219
  setMounted(true);
125
- const cookie = typeof document === "undefined" ? "" : document.cookie;
126
- if (cookie.includes(`${CONSENT_COOKIE}=`)) {
127
- const existing = readConsentCookie(cookie);
128
- consentRef.current = existing;
129
- setConsentState(existing);
130
- setDecided(true);
131
- }
220
+ const sync = () => {
221
+ const api = nativeConsent();
222
+ const next = api?.get() ?? readConsentCookie(document.cookie);
223
+ consentRef.current = next;
224
+ setConsentState(next);
225
+ setDecided(api?.hasDecision() ?? document.cookie.includes(`${CONSENT_COOKIE}=v1.`));
226
+ };
227
+ sync();
228
+ document.addEventListener("kotao:consent-changed", sync);
229
+ return () => document.removeEventListener("kotao:consent-changed", sync);
132
230
  }, []);
133
231
  const setConsent = useCallback((patch) => {
134
- setConsentState((prev) => {
135
- const next = mergeConsent(prev, patch);
136
- consentRef.current = next;
137
- if (typeof document !== "undefined") document.cookie = buildConsentCookie(next);
138
- return next;
139
- });
232
+ const next = mergeConsent(consentRef.current, patch);
233
+ consentRef.current = next;
234
+ const api = nativeConsent();
235
+ if (api !== void 0) api.set(next);
236
+ else if (typeof document !== "undefined") document.cookie = buildConsentCookie(next);
237
+ setConsentState(next);
140
238
  setDecided(true);
141
239
  }, []);
142
240
  useEffect(() => {
143
241
  bus.publish("page_viewed", {});
144
242
  }, [bus, props.url]);
243
+ useEffect(() => {
244
+ const tracker = startPresenceTracking({
245
+ endpoint: endpointRef.current ?? "/api/storefront/pixel",
246
+ send: beaconSend,
247
+ consent: () => consentRef.current.analytics,
248
+ context: () => ({
249
+ ...collectAnalyticsAttribution({
250
+ referrer: document.referrer,
251
+ search: window.location.search,
252
+ ...typeof window.matchMedia === "function" ? { matchMedia: (query) => window.matchMedia(query) } : {}
253
+ }),
254
+ ...contextRef.current
255
+ })
256
+ });
257
+ const unsubscribers = [
258
+ bus.emitter.subscribe("product_added_to_cart", () => tracker.activity("cart")),
259
+ bus.emitter.subscribe("cart_viewed", () => tracker.activity("cart")),
260
+ bus.emitter.subscribe("product_viewed", () => tracker.activity("product"))
261
+ ];
262
+ return () => {
263
+ tracker.stop();
264
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
265
+ bus.flush();
266
+ };
267
+ }, [bus]);
145
268
  useEffect(() => {
146
269
  if (typeof document === "undefined") return;
147
270
  const onHide = () => bus.flush();
@@ -1035,7 +1158,7 @@ var PAYMENT_ICON_SVGS = {
1035
1158
 
1036
1159
  // ../smart-cart/src/widgets/trust-badges.ts
1037
1160
  var KNOWN_KEYS = new Set(Object.keys(PAYMENT_ICON_SVGS));
1038
- var DEFAULT_BADGES = ["visa", "mastercard", "amex", "paypal"];
1161
+ var DEFAULT_BADGES = ["visa", "mastercard", "amex"];
1039
1162
  function readBadges(config) {
1040
1163
  const raw = config.badges;
1041
1164
  if (!Array.isArray(raw)) return null;