@kotao/storefront 0.2.1 → 0.2.2

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.
@@ -163,6 +163,7 @@ function buildRobotsTxt(options) {
163
163
  return "User-agent: *\nDisallow: /\n";
164
164
  }
165
165
  const lines = ["User-agent: *", "Allow: /"];
166
+ for (const path of options.disallow ?? []) lines.push(`Disallow: ${path}`);
166
167
  if (options.sitemapUrl !== void 0) lines.push(`Sitemap: ${options.sitemapUrl}`);
167
168
  return `${lines.join("\n")}
168
169
  `;
@@ -210,5 +211,5 @@ async function storefrontRedirect(options) {
210
211
  }
211
212
 
212
213
  export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd };
213
- //# sourceMappingURL=chunk-ISAFEOR2.js.map
214
- //# sourceMappingURL=chunk-ISAFEOR2.js.map
214
+ //# sourceMappingURL=chunk-PXVDVUFX.js.map
215
+ //# sourceMappingURL=chunk-PXVDVUFX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/seo/json-ld/common.ts","../src/seo/json-ld/product.ts","../src/seo/json-ld/local-business.ts","../src/seo/sitemap.ts","../src/seo/robots.ts","../src/seo/redirects.ts"],"names":[],"mappings":"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","file":"chunk-PXVDVUFX.js","sourcesContent":["import type { JsonLd } from '../types'\n\n/** Drop undefined / null / '' / empty-array fields so JSON-LD never renders empty or null values. */\nexport function compact(obj: Record<string, unknown>): JsonLd {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj)) {\n if (value === undefined || value === null || value === '') continue\n if (Array.isArray(value) && value.length === 0) continue\n out[key] = value\n }\n return out\n}\n\nconst SCHEMA = 'https://schema.org'\n\nexport interface BreadcrumbItem {\n readonly name: string\n readonly url: string\n}\n\nexport function breadcrumbListJsonLd(items: readonly BreadcrumbItem[]): JsonLd {\n return {\n '@context': SCHEMA,\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n item: item.url,\n })),\n }\n}\n\nexport interface OrganizationInput {\n readonly name: string\n readonly url?: string\n readonly logo?: string\n readonly sameAs?: readonly string[]\n}\n\nexport function organizationJsonLd(input: OrganizationInput): JsonLd {\n return compact({\n '@context': SCHEMA,\n '@type': 'Organization',\n name: input.name,\n url: input.url,\n logo: input.logo,\n sameAs: input.sameAs,\n })\n}\n\nexport interface WebSiteInput {\n readonly name: string\n readonly url: string\n /** A URL template with `{search_term_string}` to enable the sitelinks search box. */\n readonly searchUrlTemplate?: string\n}\n\nexport function websiteJsonLd(input: WebSiteInput): JsonLd {\n return compact({\n '@context': SCHEMA,\n '@type': 'WebSite',\n name: input.name,\n url: input.url,\n potentialAction:\n input.searchUrlTemplate !== undefined\n ? {\n '@type': 'SearchAction',\n target: { '@type': 'EntryPoint', urlTemplate: input.searchUrlTemplate },\n 'query-input': 'required name=search_term_string',\n }\n : undefined,\n })\n}\n","import type { JsonLd } from '../types'\nimport { compact } from './common'\n\nexport interface OfferInput {\n readonly price: string | number\n readonly currency: string\n /** schema.org ItemAvailability short name, e.g. 'InStock' | 'OutOfStock' | 'PreOrder'. */\n readonly availability?: string\n readonly url?: string\n readonly sku?: string\n}\n\nexport interface ProductJsonLdInput {\n readonly name: string\n readonly description?: string\n readonly sku?: string\n readonly image?: string | readonly string[]\n readonly brand?: string\n readonly url?: string\n readonly offer?: OfferInput\n}\n\nfunction offerJsonLd(offer: OfferInput): JsonLd {\n return compact({\n '@type': 'Offer',\n price: String(offer.price),\n priceCurrency: offer.currency,\n availability:\n offer.availability !== undefined ? `https://schema.org/${offer.availability}` : undefined,\n url: offer.url,\n sku: offer.sku,\n })\n}\n\nexport function productJsonLd(input: ProductJsonLdInput): JsonLd {\n return compact({\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: input.name,\n description: input.description,\n sku: input.sku,\n image: input.image,\n brand: input.brand !== undefined ? { '@type': 'Brand', name: input.brand } : undefined,\n url: input.url,\n offers: input.offer !== undefined ? offerJsonLd(input.offer) : undefined,\n })\n}\n","import type { JsonLd } from '../types'\nimport { compact } from './common'\n\nexport interface AddressInput {\n readonly streetAddress?: string\n readonly locality?: string\n readonly region?: string\n readonly postalCode?: string\n readonly country?: string\n}\n\nexport interface GeoInput {\n readonly latitude: number\n readonly longitude: number\n}\n\nexport interface LocalBusinessInput {\n readonly name: string\n /** schema.org type, e.g. 'LocalBusiness' | 'Restaurant' | 'Store'. Defaults to 'LocalBusiness'. */\n readonly type?: string\n readonly url?: string\n readonly telephone?: string\n readonly priceRange?: string\n readonly image?: string | readonly string[]\n readonly address?: AddressInput\n readonly geo?: GeoInput\n /** schema.org openingHours strings, e.g. ['Mo-Fr 09:00-17:00', 'Sa 10:00-14:00']. */\n readonly openingHours?: readonly string[]\n /** Restaurant fields (used when type is 'Restaurant'). */\n readonly servesCuisine?: string | readonly string[]\n readonly menu?: string\n readonly acceptsReservations?: boolean\n}\n\nfunction addressJsonLd(address: AddressInput): JsonLd {\n return compact({\n '@type': 'PostalAddress',\n streetAddress: address.streetAddress,\n addressLocality: address.locality,\n addressRegion: address.region,\n postalCode: address.postalCode,\n addressCountry: address.country,\n })\n}\n\n/**\n * LocalBusiness JSON-LD — the brick-and-mortar / restaurant differentiator beyond Shopify's defaults\n * (the SDK table calls this out). `restaurantJsonLd` is the same builder pinned to `Restaurant`.\n */\nexport function localBusinessJsonLd(input: LocalBusinessInput): JsonLd {\n return compact({\n '@context': 'https://schema.org',\n '@type': input.type ?? 'LocalBusiness',\n name: input.name,\n url: input.url,\n telephone: input.telephone,\n priceRange: input.priceRange,\n image: input.image,\n address: input.address !== undefined ? addressJsonLd(input.address) : undefined,\n geo:\n input.geo !== undefined\n ? {\n '@type': 'GeoCoordinates',\n latitude: input.geo.latitude,\n longitude: input.geo.longitude,\n }\n : undefined,\n openingHours: input.openingHours,\n servesCuisine: input.servesCuisine,\n hasMenu: input.menu,\n acceptsReservations: input.acceptsReservations,\n })\n}\n\nexport function restaurantJsonLd(input: Omit<LocalBusinessInput, 'type'>): JsonLd {\n return localBusinessJsonLd({ ...input, type: 'Restaurant' })\n}\n","/**\n * sitemap.xml + sitemap-index factories. The entry source is injected (the caller resolves it from the\n * Storefront API or a search index — an open question, NOT decided here): build the entry list, then call\n * these. The index shape exists so large catalogs split into child sitemaps rather than one document.\n */\n\nexport type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n\nexport interface SitemapEntry {\n readonly loc: string\n readonly lastmod?: string\n readonly changefreq?: ChangeFreq\n /** 0.0–1.0 (clamped). */\n readonly priority?: number\n}\n\nexport interface SitemapIndexEntry {\n readonly loc: string\n readonly lastmod?: string\n}\n\nconst XML_CONTENT_TYPE = 'application/xml; charset=utf-8'\n\nfunction escapeXml(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, '&apos;')\n}\n\nexport function buildSitemapXml(entries: readonly SitemapEntry[]): string {\n const urls = entries\n .map((entry) => {\n const parts = [` <loc>${escapeXml(entry.loc)}</loc>`]\n if (entry.lastmod !== undefined)\n parts.push(` <lastmod>${escapeXml(entry.lastmod)}</lastmod>`)\n if (entry.changefreq !== undefined)\n parts.push(` <changefreq>${entry.changefreq}</changefreq>`)\n if (entry.priority !== undefined) {\n parts.push(\n ` <priority>${Math.max(0, Math.min(1, entry.priority)).toFixed(1)}</priority>`,\n )\n }\n return ` <url>\\n${parts.join('\\n')}\\n </url>`\n })\n .join('\\n')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${urls}\\n</urlset>\\n`\n}\n\nexport function buildSitemapIndexXml(sitemaps: readonly SitemapIndexEntry[]): string {\n const items = sitemaps\n .map((sitemap) => {\n const parts = [` <loc>${escapeXml(sitemap.loc)}</loc>`]\n if (sitemap.lastmod !== undefined)\n parts.push(` <lastmod>${escapeXml(sitemap.lastmod)}</lastmod>`)\n return ` <sitemap>\\n${parts.join('\\n')}\\n </sitemap>`\n })\n .join('\\n')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${items}\\n</sitemapindex>\\n`\n}\n\nexport function sitemapResponse(entries: readonly SitemapEntry[]): Response {\n return new Response(buildSitemapXml(entries), { headers: { 'content-type': XML_CONTENT_TYPE } })\n}\n\nexport function sitemapIndexResponse(sitemaps: readonly SitemapIndexEntry[]): Response {\n return new Response(buildSitemapIndexXml(sitemaps), {\n headers: { 'content-type': XML_CONTENT_TYPE },\n })\n}\n","/**\n * robots.txt factory. Non-production / preview deployments auto-disallow ALL crawling; production allows\n * and references the sitemap. `production` comes from the SDK's stage signal (context.env), NEVER the\n * request Host/URL (stage-pinned-config rule, AGENTS.md §Environments).\n */\nexport interface RobotsOptions {\n readonly production: boolean\n readonly sitemapUrl?: string\n /** Path prefixes to Disallow in production (e.g. /cart, /checkout — Shopify parity). */\n readonly disallow?: readonly string[]\n}\n\nexport function buildRobotsTxt(options: RobotsOptions): string {\n if (!options.production) {\n return 'User-agent: *\\nDisallow: /\\n'\n }\n const lines = ['User-agent: *', 'Allow: /']\n for (const path of options.disallow ?? []) lines.push(`Disallow: ${path}`)\n if (options.sitemapUrl !== undefined) lines.push(`Sitemap: ${options.sitemapUrl}`)\n return `${lines.join('\\n')}\\n`\n}\n\nexport function robotsResponse(options: RobotsOptions): Response {\n return new Response(buildRobotsTxt(options), {\n headers: { 'content-type': 'text/plain; charset=utf-8' },\n })\n}\n","import type { StorefrontClient } from '../storefront-client/client'\n\n/**\n * Slug-change 301 handler. The redirect store is owned elsewhere (catalog/storefront-API work); this\n * owns only the request-handling shape. The lookup is injected: given the current path, it returns the\n * canonical target (or null). A self-redirect (target === path) returns null to avoid an immediate loop.\n */\nexport type RedirectLookup = (path: string) => Promise<string | null> | string | null\n\nexport interface RedirectOptions {\n /** 301 (permanent, default) or 302 (temporary). */\n readonly status?: 301 | 302\n}\n\nexport interface StorefrontRedirectOptions extends RedirectOptions {\n readonly request: Request\n readonly response: Response\n readonly storefront: StorefrontClient\n}\n\nexport async function resolveRedirect(\n lookup: RedirectLookup,\n path: string,\n options: RedirectOptions = {},\n): Promise<Response | null> {\n const target = await lookup(path)\n if (target === null || target === '' || target === path) return null\n return new Response(null, {\n status: options.status ?? 301,\n headers: { location: target },\n })\n}\n\nexport const URL_REDIRECT_QUERY = `#graphql\n query UrlRedirect($path: String!) {\n urlRedirect(path: $path) {\n target\n }\n }\n`\n\n/**\n * Look up `path` in the storefront-api's url_redirects and, if found, return a 301 Response.\n * Returns null when there is no redirect, a self-redirect, or the lookup query fails (so the caller\n * falls through to its normal 404 — a lookup failure must never become a 500).\n */\nexport async function resolveStorefrontRedirect(\n storefront: StorefrontClient,\n path: string,\n options: RedirectOptions = {},\n): Promise<Response | null> {\n let target: string | null = null\n try {\n const data = await storefront.query<{ urlRedirect: { target: string } | null }>(\n URL_REDIRECT_QUERY,\n { variables: { path } },\n )\n target = data.urlRedirect?.target ?? null\n } catch {\n return null\n }\n return resolveRedirect(() => target, path, options)\n}\n\n/**\n * Hydrogen-compatible 404 redirect helper. It preserves the original response when there is no\n * matching URL redirect, mirroring the server.ts pattern used by Hydrogen demo stores.\n */\nexport async function storefrontRedirect(options: StorefrontRedirectOptions): Promise<Response> {\n if (options.response.status !== 404) return options.response\n const path = new URL(options.request.url).pathname\n const redirectOptions = options.status === undefined ? {} : { status: options.status }\n return (\n (await resolveStorefrontRedirect(options.storefront, path, redirectOptions)) ?? options.response\n )\n}\n"]}
@@ -156,4 +156,4 @@ interface HydrogenStorefrontClientOptions<I18n extends object = StorefrontI18n>
156
156
  declare function createStorefrontClient(opts: StorefrontClientOptions): StorefrontClientResult;
157
157
  declare function createStorefrontClient<I18n extends object = StorefrontI18n>(opts: HydrogenStorefrontClientOptions<I18n>): StorefrontClientResult<StorefrontI18n & I18n>;
158
158
 
159
- export { type CacheStrategy as C, type HydrogenStorefrontClientOptions as H, type KotaoCache as K, type StorefrontClient as S, CacheCustom as a, CacheLong as b, CacheNone as c, CacheShort as d, type Storefront as e, type StorefrontAuthHeader as f, type StorefrontClientOptions as g, type StorefrontClientResult as h, StorefrontDefaultCache as i, type StorefrontQueryOptions as j, createCache as k, createStorefrontClient as l, generateCacheControlHeader as m };
159
+ export { type CacheStrategy as C, type HydrogenStorefrontClientOptions as H, type KotaoCache as K, type StorefrontAuthHeader as S, type StorefrontClient as a, CacheCustom as b, CacheLong as c, CacheNone as d, CacheShort as e, type Storefront as f, type StorefrontClientOptions as g, type StorefrontClientResult as h, StorefrontDefaultCache as i, type StorefrontQueryOptions as j, createCache as k, createStorefrontClient as l, generateCacheControlHeader as m };
@@ -0,0 +1,47 @@
1
+ import { SectionInstance, ValuesDocument } from '@kotao/storefront-contracts/theme';
2
+ import { S as StorefrontAuthHeader } from './client-DiPdlvPu.js';
3
+
4
+ /**
5
+ * The stored settings a theme hydrates from (#93): the layout document's ordered section instances +
6
+ * the global theme tokens. Pass to {@link resolveThemeSettings} together with the theme's bundled
7
+ * section schemas + presets to produce the merged {@link ThemeSettingsSnapshot}.
8
+ */
9
+ interface StoredThemeSettings {
10
+ readonly shopId: string;
11
+ readonly sections: readonly SectionInstance[];
12
+ readonly global: Record<string, unknown>;
13
+ readonly templates?: Readonly<Partial<Record<string, ValuesDocument>>>;
14
+ readonly sectionGroups?: Readonly<Partial<Record<string, ValuesDocument>>>;
15
+ }
16
+ interface FetchThemeSettingsOptions {
17
+ /** Injectable for tests; defaults to the global `fetch`. */
18
+ readonly fetch?: typeof fetch;
19
+ /** Same-origin settings endpoint (default `/api/storefront/settings`). */
20
+ readonly endpoint?: string;
21
+ /**
22
+ * Explicit local-preview escape hatch for server-side theme workers that call the Kotao API directly.
23
+ * Production themes should keep the default same-origin path.
24
+ */
25
+ readonly allowCrossOrigin?: boolean;
26
+ /** Optional server-side auth headers for local previews. Never pass browser-visible tokens here. */
27
+ readonly auth?: {
28
+ readonly token: string;
29
+ readonly tokenHeader?: StorefrontAuthHeader;
30
+ readonly siteId?: string;
31
+ };
32
+ /**
33
+ * Gateway-signed shop-context headers (`x-kotao-shop` + `x-kotao-shop-signature`) to forward to the
34
+ * upstream, mirroring the storefront GraphQL client. Required for the SSR worker path: a Worker
35
+ * subrequest to its own zone bypasses the Gateway, so the shop context has to be re-attached
36
+ * explicitly and the request sent cross-origin to the API. `context.loadThemeSettings()` wires this
37
+ * automatically from the inbound request; pass it by hand only when calling `fetchThemeSettings` raw.
38
+ */
39
+ readonly shopContextHeaders?: Readonly<Record<string, string>>;
40
+ }
41
+ /**
42
+ * Fetch the shop's stored settings document via the same-origin `/api/storefront/settings` carve-out.
43
+ * The shop is resolved server-side from the Gateway-signed context — the theme sends no id and no token.
44
+ */
45
+ declare function fetchThemeSettings(options?: FetchThemeSettingsOptions): Promise<StoredThemeSettings>;
46
+
47
+ export { type FetchThemeSettingsOptions as F, type StoredThemeSettings as S, fetchThemeSettings as f };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,17 @@
1
- export { a as CacheCustom, b as CacheLong, c as CacheNone, d as CacheShort, C as CacheStrategy, H as HydrogenStorefrontClientOptions, K as KotaoCache, e as Storefront, f as StorefrontAuthHeader, S as StorefrontClient, g as StorefrontClientOptions, h as StorefrontClientResult, i as StorefrontDefaultCache, j as StorefrontQueryOptions, k as createCache, l as createStorefrontClient, m as generateCacheControlHeader } from './client-D9YYA2w6.js';
1
+ export { b as CacheCustom, c as CacheLong, d as CacheNone, e as CacheShort, C as CacheStrategy, H as HydrogenStorefrontClientOptions, K as KotaoCache, f as Storefront, S as StorefrontAuthHeader, a as StorefrontClient, g as StorefrontClientOptions, h as StorefrontClientResult, i as StorefrontDefaultCache, j as StorefrontQueryOptions, k as createCache, l as createStorefrontClient, m as generateCacheControlHeader } from './client-DiPdlvPu.js';
2
2
  import { a as Cart } from './types-Df3bGbKU.js';
3
3
  export { k as CART_FORM_ACTIONS, c as CartAttribute, l as CartBuyerIdentity, d as CartBuyerIdentityInput, i as CartDeliveryAddress, C as CartFormAction, m as CartLine, b as CartLineInput, f as CartLineUpdateInput, g as CartMetafield, h as CartMetafieldIdentifier, e as CartMutationResult, j as CartSelectedDeliveryOption, n as CartUserError, o as HydrogenCart, p as HydrogenCartCreateInput, q as HydrogenCartMutationResult, H as HydrogenSession, r as HydrogenSessionData } from './types-Df3bGbKU.js';
4
4
  import { C as ConsentBucket, a as ConsentState, B as BoundNonceProvider } from './shop-DkQmro-Y.js';
5
5
  export { A as ANALYTICS_EVENTS, b as AnalyticsBus, c as AnalyticsBusOptions, d as AnalyticsContext, e as AnalyticsEmitter, f as AnalyticsEventMap, g as AnalyticsEventName, h as AnalyticsGeo, i as CONSENT_BUCKETS, j as CONSENT_COOKIE, k as CartForm, l as CartFormProps, m as CartViewedPayload, n as CollectionViewedPayload, D as DENIED_CONSENT, E as EventHandler, G as GRANTED_CONSENT, o as GetShopAnalyticsOptions, P as PageViewedPayload, p as PixelEvent, q as PixelSend, r as PixelTransport, s as PixelTransportOptions, t as ProductAddedToCartPayload, u as ProductViewedPayload, S as SearchViewedPayload, v as ShopAnalytics, w as buildConsentCookie, x as createAnalyticsBus, y as createAnalyticsEmitter, z as createPixelTransport, F as getShopAnalytics, H as isGranted, I as mergeConsent, J as parseConsent, K as readConsentCookie, L as serializeConsent } from './shop-DkQmro-Y.js';
6
- import { C as CartDispatchAction, c as CustomerAccountClient } from './routing-BfUp49o8.js';
7
- export { A as Address, d as AddressConnection, e as CartGetId, f as CartHandler, g as CartSetId, h as CreateCartHandlerOptions, i as CustomerAccount, j as CustomerAccountError, k as CustomerAccountErrorCode, G as GEO_HEADER, l as GEO_SIGNATURE_HEADER, m as Geo, H as HydrogenCartHandlerOptions, a as KotaoContext, K as KotaoEnv, b as KotaoSession, L as LocalizePathOptions, M as Money, O as Order, n as OrderConnection, o as OrderPageInfo, p as OrdersOptions, R as ROUTING_LOCALE_HEADER, q as ROUTING_ORIGINAL_PATHNAME_HEADER, r as RequestCartHandlerOptions, t as RequestLoginCodeInput, u as RequestLoginCodeResult, S as StrippedLocalePath, V as VerifyLoginCodeInput, W as WithCache, v as cartGetIdDefault, w as cartSetIdDefault, x as createCartHandler, y as createKotaoContext, z as createWithCache, B as isLocalePathSegment, D as localeFromAcceptLanguage, E as localeFromPathname, F as localeFromRequest, I as localizePath, J as originalPathnameFromRequest, N as signGeo, P as stripLocalePrefixFromPathname, s as stripLocalePrefixFromRequest, Q as verifyGeo } from './routing-BfUp49o8.js';
6
+ import { C as CartDispatchAction, c as CustomerAccountClient } from './routing-BxRlep_-.js';
7
+ export { A as Address, d as AddressConnection, e as CartGetId, f as CartHandler, g as CartSetId, h as CreateCartHandlerOptions, i as CustomerAccount, j as CustomerAccountError, k as CustomerAccountErrorCode, G as GEO_HEADER, l as GEO_SIGNATURE_HEADER, m as Geo, H as HydrogenCartHandlerOptions, a as KotaoContext, K as KotaoEnv, b as KotaoSession, L as LocalizePathOptions, M as Money, O as Order, n as OrderConnection, o as OrderPageInfo, p as OrdersOptions, R as ROUTING_LOCALE_HEADER, q as ROUTING_ORIGINAL_PATHNAME_HEADER, r as RequestCartHandlerOptions, t as RequestLoginCodeInput, u as RequestLoginCodeResult, S as StrippedLocalePath, V as VerifyLoginCodeInput, W as WithCache, v as cartGetIdDefault, w as cartSetIdDefault, x as createCartHandler, y as createKotaoContext, z as createWithCache, B as isLocalePathSegment, D as localeFromAcceptLanguage, E as localeFromPathname, F as localeFromRequest, I as localizePath, J as originalPathnameFromRequest, N as signGeo, P as stripLocalePrefixFromPathname, s as stripLocalePrefixFromRequest, Q as verifyGeo } from './routing-BxRlep_-.js';
8
8
  import { T as ThemeSettingsSnapshot } from './store-DQK1nhob.js';
9
9
  export { R as ResolvedBlock, a as ResolvedSection, b as ResolvedSettings, c as ThemeSettingsStore, d as createThemeSettingsStore } from './store-DQK1nhob.js';
10
10
  export { ImageAttrs, ImageAttrsOptions, ImageCrop, ImageData, ImageFit, ImageFormat, ImageSrcSetOptions, ImageUrlOptions, buildImageAttrs, imageSrcSet, imageUrl } from './image.js';
11
11
  export { C as Connection, a as ConnectionEdge, F as FlattenableConnection, P as PageInfo, b as PaginationInfo, c as PaginationOptions, d as PaginationVariables, f as flattenConnection, g as getPaginationInfo, e as getPaginationVariables } from './pagination-CPgH3fAa.js';
12
12
  export { MappedProductOptions, ProductLike, ProductOptionDef, ProductOptionState, ProductOptionValueState, SelectedOption, SelectedOptionsMap, VariantLike, VariantSelectionLike, getAdjacentAndFirstAvailableVariants, getProductOptions, getSelectedProductOptions, getSelectedVariant, mapSelectedProductOptionToObject, useOptimisticVariant, useSelectedOptionInUrlParam } from './product.js';
13
- export { FetchThemeSettingsOptions, ResolveThemeSettingsArgs, StoredThemeSettings, fetchThemeSettings, mergeSection, mergeSettings, resolveThemeSettings } from './settings.js';
13
+ export { ResolveThemeSettingsArgs, mergeSection, mergeSettings, resolveThemeSettings } from './settings.js';
14
+ export { F as FetchThemeSettingsOptions, S as StoredThemeSettings, f as fetchThemeSettings } from './fetch-B6TIc6lI.js';
14
15
  export { AddressInput, BreadcrumbItem, ChangeFreq, GeoInput, LocalBusinessInput, OfferInput, OrganizationInput, ProductJsonLdInput, RedirectLookup, RedirectOptions, RobotsOptions, SitemapEntry, SitemapIndexEntry, StorefrontRedirectOptions, URL_REDIRECT_QUERY, WebSiteInput, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, getSeoMeta, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, serializeJsonLd, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './seo.js';
15
16
  export { A as Alternate, J as JsonLd, O as OpenGraph, a as OpenGraphImage, S as SeoConfig, b as SeoDescriptor, T as TwitterCard } from './types-BDhHWZsP.js';
16
17
  export { CreateStorefrontEditorBridgeArgs, CreateStorefrontEditorHostBridgeArgs, StorefrontEditorBlockSchema, StorefrontEditorBridge, StorefrontEditorHostBridge, StorefrontEditorMessageEvent, StorefrontEditorMessageSource, StorefrontEditorMessageTarget, StorefrontEditorReady, StorefrontEditorSectionPreset, StorefrontEditorSectionPresetBlock, StorefrontEditorSectionSchema, StorefrontEditorSelection, StorefrontEditorSettingInput, StorefrontEditorSettingOption, createStorefrontEditorBridge, createStorefrontEditorHostBridge } from './editor.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './chunk-ISAFEOR2.js';
2
- export { createThemeSettingsStore, fetchThemeSettings, mergeSection, mergeSettings, resolveThemeSettings } from './chunk-GCQI6OKH.js';
1
+ export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './chunk-PXVDVUFX.js';
2
+ export { createThemeSettingsStore, mergeSection, mergeSettings, resolveThemeSettings } from './chunk-H3D7BAOL.js';
3
3
  export { createStorefrontEditorBridge, createStorefrontEditorHostBridge } from './chunk-PXZ737N3.js';
4
4
  export { getAdjacentAndFirstAvailableVariants, getProductOptions, getSelectedProductOptions, getSelectedVariant, mapSelectedProductOptionToObject, useOptimisticVariant, useSelectedOptionInUrlParam } from './chunk-QOEEUTHQ.js';
5
5
  import { createNonceProvider } from './chunk-XMTWFJCZ.js';
@@ -7,8 +7,9 @@ export { CartForm, createAnalyticsBus, createAnalyticsEmitter, createPixelTransp
7
7
  export { getSeoMeta, serializeJsonLd } from './chunk-MYDKIJRH.js';
8
8
  export { buildImageAttrs, imageSrcSet, imageUrl } from './chunk-3YQXJNR7.js';
9
9
  export { flattenConnection, getPaginationInfo, getPaginationVariables } from './chunk-5QOFKGKD.js';
10
- import { localizePath, buildLocalizationCookie } from './chunk-U5677AEA.js';
11
- export { CART_ID_COOKIE, CHECKOUT_CONSENT_CATEGORIES, CHECKOUT_CONSENT_PARAM_PREFIX, CHECKOUT_DESIGN_TOKEN_INPUTS, CUSTOMER_SESSION_COOKIE, CacheCustom, CacheLong, CacheNone, CacheShort, CustomerAccountError, DEFAULT_SESSION_MAX_AGE, GEO_HEADER, GEO_SIGNATURE_HEADER, LOCALIZATION_COOKIE, ROUTING_LOCALE_HEADER, ROUTING_ORIGINAL_PATHNAME_HEADER, StorefrontDefaultCache, buildCartIdCookie, buildCustomerSessionCookie, buildLocalizationCookie, cartGetIdDefault, cartSetIdDefault, clearCustomerSessionCookie, createCache, createCartHandler, createCustomerAccountClient, createKotaoContext, createStorefrontClient, createWithCache, decodeCheckoutConsent, encodeCheckoutConsent, extractCheckoutDesignTokens, generateCacheControlHeader, isLocalePathSegment, localeFromAcceptLanguage, localeFromPathname, localeFromRequest, localizePath, originalPathnameFromRequest, readCartIdCookie, readCustomerSessionToken, readLocalizationCookie, resolveCheckoutUrl, shopHostname, signGeo, stripLocalePrefixFromPathname, stripLocalePrefixFromRequest, verifyGeo } from './chunk-U5677AEA.js';
10
+ import { localizePath, buildLocalizationCookie } from './chunk-7XYWHWT4.js';
11
+ export { CART_ID_COOKIE, CHECKOUT_CONSENT_CATEGORIES, CHECKOUT_CONSENT_PARAM_PREFIX, CHECKOUT_DESIGN_TOKEN_INPUTS, CUSTOMER_SESSION_COOKIE, CacheCustom, CacheLong, CacheNone, CacheShort, CustomerAccountError, DEFAULT_SESSION_MAX_AGE, GEO_HEADER, GEO_SIGNATURE_HEADER, LOCALIZATION_COOKIE, ROUTING_LOCALE_HEADER, ROUTING_ORIGINAL_PATHNAME_HEADER, StorefrontDefaultCache, buildCartIdCookie, buildCustomerSessionCookie, buildLocalizationCookie, cartGetIdDefault, cartSetIdDefault, clearCustomerSessionCookie, createCache, createCartHandler, createCustomerAccountClient, createKotaoContext, createStorefrontClient, createWithCache, decodeCheckoutConsent, encodeCheckoutConsent, extractCheckoutDesignTokens, generateCacheControlHeader, isLocalePathSegment, localeFromAcceptLanguage, localeFromPathname, localeFromRequest, localizePath, originalPathnameFromRequest, readCartIdCookie, readCustomerSessionToken, readLocalizationCookie, resolveCheckoutUrl, shopHostname, signGeo, stripLocalePrefixFromPathname, stripLocalePrefixFromRequest, verifyGeo } from './chunk-7XYWHWT4.js';
12
+ export { fetchThemeSettings } from './chunk-M7BPQ54D.js';
12
13
  import { CART_FORM_ACTIONS, CONSENT_BUCKETS } from './chunk-OGWYTYYS.js';
13
14
  export { CART_FORM_ACTIONS, CONSENT_BUCKETS, CONSENT_COOKIE, DENIED_CONSENT, GRANTED_CONSENT, buildConsentCookie, isGranted, mergeConsent, parseConsent, readConsentCookie, serializeConsent } from './chunk-OGWYTYYS.js';
14
15
 
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"],"names":[],"mappings":";;;;;;;;;;;;;;;AAIA,IAAM,WAAA,GAAc,CAAC,IAAA,EAAY,KAAA,MAAsC;AAAA,EACrE,GAAG,IAAA;AAAA,EACH,KAAA;AAAA,EACA,aAAA,EAAe,MAAM,MAAA,CAAO,CAAC,KAAK,IAAA,KAAS,GAAA,GAAM,IAAA,CAAK,QAAA,EAAU,CAAC;AACnE,CAAA,CAAA;AAEO,SAAS,yBAAA,CAA0B,MAAY,MAAA,EAAkC;AACtF,EAAA,QAAQ,OAAO,MAAA;AAAQ,IACrB,KAAK,kBAAkB,QAAA,EAAU;AAC/B,MAAA,MAAM,UAAA,GAAc,MAAA,CAAO,MAAA,CAAO,KAAA,IAAS,EAAC;AAI5C,MAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAc,CAAC,IAAA,MAAU;AAAA,QACpD,EAAA,EAAI,CAAA,WAAA,EAAc,IAAA,CAAK,aAAa,CAAA,CAAA;AAAA,QACpC,QAAA,EAAU,KAAK,QAAA,IAAY,CAAA;AAAA,QAC3B,WAAA,EAAa,EAAE,EAAA,EAAI,IAAA,CAAK,aAAA,EAAc;AAAA,QACtC,YAAY;AAAC,OACf,CAAE,CAAA;AACF,MAAA,OAAO,WAAA,CAAY,MAAM,CAAC,GAAG,KAAK,KAAA,EAAO,GAAG,SAAS,CAAC,CAAA;AAAA,IACxD;AAAA,IACA,KAAK,kBAAkB,WAAA,EAAa;AAClC,MAAA,MAAM,UAAU,IAAI,GAAA;AAAA,QAAA,CAChB,MAAA,CAAO,MAAA,CAAO,KAAA,IAAS,EAAC,EAAuD,GAAA;AAAA,UAC/E,CAAC,IAAA,KAAS,CAAC,IAAA,CAAK,IAAI,IAAI;AAAA;AAC1B,OACF;AACA,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAChB,GAAA,CAAI,CAAC,IAAA,KAAS;AACb,QAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AAClC,QAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,QAAA,IAAI,MAAA,CAAO,QAAA,KAAa,CAAA,EAAG,OAAO,IAAA;AAClC,QAAA,OAAO;AAAA,UACL,GAAG,IAAA;AAAA,UACH,QAAA,EAAU,MAAA,CAAO,QAAA,IAAY,IAAA,CAAK;AAAA,SACpC;AAAA,MACF,CAAC,CAAA,CACA,MAAA,CAAO,CAAC,IAAA,KAA2B,SAAS,IAAI,CAAA;AACnD,MAAA,OAAO,WAAA,CAAY,MAAM,KAAK,CAAA;AAAA,IAChC;AAAA,IACA,KAAK,kBAAkB,WAAA,EAAa;AAClC,MAAA,MAAM,SAAS,IAAI,GAAA,CAAK,OAAO,MAAA,CAAO,OAAA,IAAW,EAAe,CAAA;AAChE,MAAA,OAAO,WAAA;AAAA,QACL,IAAA;AAAA,QACA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,CAAC,IAAA,KAAS,CAAC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,EAAE,CAAC;AAAA,OAClD;AAAA,IACF;AAAA,IACA;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAMO,SAAS,iBAAA,CAAkB,MAAY,MAAA,EAAyC;AACrF,EAAA,OAAO,MAAA,GAAS,yBAAA,CAA0B,IAAA,EAAM,MAAM,CAAA,GAAI,IAAA;AAC5D;;;ACzCA,IAAM,aAAA,uBAAoB,GAAA,CAAI;AAAA,EAC5B,MAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAEM,SAAS,2BAAA,CACd,OAAA,GAA8C,EAAC,EAClB;AAC7B,EAAA,MAAM,QAAQ,WAAA,EAAY;AAC1B,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,OAAA,CAAQ,IAAI,CAAA;AAC5C,EAAA,MAAM,YAAY,aAAA,CAAc;AAAA,IAC9B,GAAG,gBAAA,CAAiB,OAAA,CAAQ,SAAA,IAAa,CAAC,MAAM,CAAC,CAAA;AAAA,IACjD,UAAU,KAAK,CAAA,CAAA;AAAA,GAChB,CAAA;AACD,EAAA,MAAM,aAAa,aAAA,CAAc;AAAA,IAC/B,MAAA;AAAA,IACA,GAAG,WAAA;AAAA,IACH,GAAG,gBAAA,CAAiB,OAAA,CAAQ,UAAU;AAAA,GACvC,CAAA;AACD,EAAA,MAAM,SAAS,aAAA,CAAc;AAAA,IAC3B,MAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG,WAAA;AAAA,IACH,GAAG,gBAAA,CAAiB,OAAA,CAAQ,MAAM;AAAA,GACnC,CAAA;AACD,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,CAAC,MAAA,EAAQ,eAAA,EAAiB,GAAG,gBAAA,CAAiB,OAAA,CAAQ,QAAQ,CAAC,CAAC,CAAA;AAC/F,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,CAAC,UAAA,EAAY,CAAC,QAAQ,CAAC,CAAA;AAAA,IACvB,CAAC,aAAA,EAAe,CAAC,QAAQ,CAAC,CAAA;AAAA,IAC1B,CAAC,eAAe,UAAU,CAAA;AAAA,IAC1B,CAAC,WAAW,MAAM,CAAA;AAAA,IAClB,CAAC,cAAc,SAAS,CAAA;AAAA,IACxB,CAAC,aAAa,QAAQ;AAAA,GACxB;AACA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,QAAQ,UAAA,CACL,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,MAAM,CAAA,KAAM,CAAA,EAAG,IAAI,CAAA,CAAA,EAAK,OAA6B,IAAA,CAAK,GAAG,CAAC,CAAA,CAAE,CAAA,CAC5E,KAAK,IAAI,CAAA;AAAA,IACZ,aAAA,EAAe,oBAAoB,KAAK;AAAA,GAC1C;AACF;AAEA,SAAS,WAAA,GAAsB;AAC7B,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,EAAE,CAAA;AAC/B,EAAA,MAAM,gBAAiB,UAAA,CAAuD,MAAA;AAC9E,EAAA,IAAI,aAAA,EAAe,oBAAoB,MAAA,EAAW;AAChD,IAAA,aAAA,CAAc,gBAAgB,KAAK,CAAA;AAAA,EACrC,CAAA,MAAO;AACL,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA,EAAG;AACxC,MAAA,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,IAC3C;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,CAAC,SAAS,IAAA,CAAK,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAChF;AAEA,SAAS,YAAY,IAAA,EAAgE;AACnF,EAAA,IAAI,IAAA,KAAS,MAAA,EAAW,OAAO,EAAC;AAChC,EAAA,OAAO,CAAC,IAAA,CAAK,WAAA,EAAa,KAAK,cAAc,CAAA,CAC1C,OAAO,CAAC,MAAA,KAA6B,MAAA,KAAW,MAAA,IAAa,OAAO,IAAA,EAAK,KAAM,EAAE,CAAA,CACjF,GAAA,CAAI,CAAC,MAAA,KAAW;AACf,IAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAK;AAC5B,IAAA,OAAO,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,GAAI,IAAI,IAAI,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA;AAAA,EACrF,CAAC,CAAA;AACL;AAEA,SAAS,iBAAiB,OAAA,EAA2D;AACnF,EAAA,OAAA,CAAQ,OAAA,IAAW,EAAC,EAAG,GAAA,CAAI,CAAC,MAAA,KAAW;AACrC,IAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAK;AAC5B,IAAA,OAAO,cAAc,GAAA,CAAI,OAAO,CAAA,GAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAA,GAAM,OAAA;AAAA,EACvD,CAAC,CAAA;AACH;AAEA,SAAS,cAAc,OAAA,EAA+C;AACpE,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAI,GAAA,CAAI,OAAA,CAAQ,MAAA,CAAO,CAAC,MAAA,KAAW,MAAA,CAAO,MAAA,GAAS,CAAC,CAAC,CAAC,CAAA;AAC1E;;;AClGA,SAAS,aAAa,GAAA,EAAqD;AACzE,EAAA,IAAI,GAAA,KAAQ,QAAQ,GAAA,KAAQ,EAAA,SAAW,EAAE,IAAA,EAAM,GAAA,EAAK,IAAA,EAAM,IAAA,EAAK;AAC/D,EAAA,OAAO,cAAA,CAAe,IAAA,CAAK,GAAG,CAAA,GAAI,EAAE,IAAA,EAAM,GAAA,EAAK,IAAA,EAAM,IAAA,EAAK,GAAI,EAAE,IAAA,EAAM,GAAA,EAAK,MAAM,KAAA,EAAM;AACzF;AAEA,eAAsB,uBAAuB,OAAA,EAAqC;AAChF,EAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,QAAA,EAAS;AACpC,EAAA,MAAM,OAAA,GAAA,CAAW,IAAA,CAAK,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA,EAAI,QAAA,EAAS,CAAE,IAAA,EAAK,CAAE,WAAA,EAAY,IAAK,IAAA;AACpF,EAAA,MAAM,MAAA,GAAA,CAAU,KAAK,GAAA,CAAI,aAAa,KAAK,EAAA,EAAI,QAAA,EAAS,CAAE,IAAA,EAAK,IAAK,IAAA;AACpE,EAAA,MAAM,aAAA,GAAA,CAAiB,KAAK,GAAA,CAAI,gBAAgB,KAAK,EAAA,EAAI,QAAA,EAAS,CAAE,IAAA,EAAK,IAAK,IAAA;AAC9E,EAAA,MAAM,QAAA,GAAW,cAAc,IAAA,CAAK,GAAA,CAAI,WAAW,CAAA,IAAK,EAAA,EAAI,UAAU,CAAA;AACtE,EAAA,MAAM,QAAA,GACJ,QAAA,CAAS,IAAA,IAAQ,aAAA,KAAkB,IAAA,GAC/B,YAAA,CAAa,QAAA,CAAS,IAAA,EAAM,MAAA,EAAQ,EAAE,aAAA,EAAe,IACrD,QAAA,CAAS,IAAA;AACf,EAAA,OAAO,IAAI,SAAS,IAAA,EAAM;AAAA,IACxB,MAAA,EAAQ,GAAA;AAAA,IACR,OAAA,EAAS,EAAE,QAAA,EAAU,YAAA,EAAc,wBAAwB,EAAE,OAAA,EAAS,MAAA,EAAQ,CAAA;AAAE,GACjF,CAAA;AACH;;;ACTO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,UAAA,EAAY,aAAA;AAAA,EACZ,aAAA,EAAe,gBAAA;AAAA,EACf,gBAAA,EAAkB,mBAAA;AAAA,EAClB,UAAA,EAAY,aAAA;AAAA,EACZ,YAAA,EAAc,eAAA;AAAA,EACd,kBAAA,EAAoB;AACtB;;;ACFO,SAAS,iBAAiB,OAAA,EAAiD;AAChF,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,UAAA,EAAY,IAAA,EAAM,QAAA,IAAY,IAAA;AACvD,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,UAAA,EAAY,IAAA,EAAM,OAAA,IAAW,IAAA;AACrD,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,UAAA,EAAY,IAAA,EAAM,QAAA,IAAY,IAAA;AACvD,EAAA,MAAM,gBAAA,GACJ,QAAA,IAAY,IAAA,IAAQ,OAAA,IAAW,IAAA,GAAO,GAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,GAAM,QAAA,IAAY,OAAA;AAEhF,EAAA,OAAO;AAAA,IACL,GAAI,gBAAA,IAAoB,IAAA,GAAO,EAAE,gBAAA,KAAqB,EAAC;AAAA,IACvD,GAAI,QAAA,IAAY,IAAA,GAAO,EAAE,QAAA,KAAa,EAAC;AAAA,IACvC,GAAI,QAAQ,kBAAA,IAAsB,IAAA,GAAO,EAAE,MAAA,EAAQ,OAAA,CAAQ,kBAAA,EAAmB,GAAI,EAAC;AAAA,IACnF,GAAI,QAAQ,kBAAA,IAAsB,IAAA,GAAO,EAAE,YAAA,EAAc,OAAA,CAAQ,kBAAA,EAAmB,GAAI;AAAC,GAC3F;AACF;;;ACxBA,IAAM,YAAA,GAAe,UAAA;AACrB,IAAM,aAAA,GAAgB,uBAAA;AAEf,SAAS,cAAc,KAAA,EAAsC;AAClE,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,KAAA,MAAW,MAAA,IAAU,eAAA;AACnB,IAAA,MAAA,CAAO,GAAA,CAAI,CAAA,EAAG,YAAY,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,MAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA;AAClE,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,cAAc,MAAA,EAAuC;AACnE,EAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KAAmC,MAAA,CAAO,GAAA,CAAI,GAAG,YAAY,CAAA,EAAG,MAAM,CAAA,CAAE,CAAA,KAAM,GAAA;AAC5F,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,KAAK,WAAW,CAAA;AAAA,IAC3B,SAAA,EAAW,KAAK,WAAW,CAAA;AAAA,IAC3B,WAAA,EAAa,KAAK,aAAa;AAAA,GACjC;AACF;AAEO,SAAS,0BAAA,CAA2B,aAAqB,KAAA,EAA6B;AAG3F,EAAA,IAAI,CAAC,WAAA,CAAY,IAAA,CAAK,WAAW,CAAA,EAAG;AAClC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,+DAAA,EAAkE,WAAW,CAAA,CAAE,CAAA;AAAA,EACjG;AACA,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,WAAA,EAAa,aAAa,CAAA;AAC9C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,aAAA,CAAc,KAAK,CAAA,EAAG,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAChF,EAAA,OAAO,CAAA,EAAG,IAAI,QAAQ,CAAA,EAAG,IAAI,MAAM,CAAA,EAAG,IAAI,IAAI,CAAA,CAAA;AAChD","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 { 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 const country = (form.get('country_code') ?? '').toString().trim().toUpperCase() || null\n const locale = (form.get('locale_code') ?? '').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, { primaryLocale })\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\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}\n\nexport interface PageViewedPayload {\n readonly title?: string\n}\nexport interface ProductViewedPayload {\n readonly productId: string\n readonly handle?: string\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}\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"]}
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"],"names":[],"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 { 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 const country = (form.get('country_code') ?? '').toString().trim().toUpperCase() || null\n const locale = (form.get('locale_code') ?? '').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, { primaryLocale })\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\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}\n\nexport interface PageViewedPayload {\n readonly title?: string\n}\nexport interface ProductViewedPayload {\n readonly productId: string\n readonly handle?: string\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}\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"]}
@@ -1,7 +1,9 @@
1
- import { K as KotaoEnv, a as KotaoContext } from './routing-BfUp49o8.js';
2
- export { b as KotaoSession, s as stripLocalePrefixFromRequest } from './routing-BfUp49o8.js';
1
+ import { K as KotaoEnv, a as KotaoContext } from './routing-BxRlep_-.js';
2
+ export { b as KotaoSession, s as stripLocalePrefixFromRequest } from './routing-BxRlep_-.js';
3
3
  import { H as HydrogenSession } from './types-Df3bGbKU.js';
4
- import './client-D9YYA2w6.js';
4
+ import './fetch-B6TIc6lI.js';
5
+ import '@kotao/storefront-contracts/theme';
6
+ import './client-DiPdlvPu.js';
5
7
 
6
8
  type JsonInit = ResponseInit | number;
7
9
  interface JsonResponse<T> {
@@ -1,5 +1,6 @@
1
- import { createKotaoContext } from './chunk-U5677AEA.js';
2
- export { stripLocalePrefixFromRequest } from './chunk-U5677AEA.js';
1
+ import { createKotaoContext } from './chunk-7XYWHWT4.js';
2
+ export { stripLocalePrefixFromRequest } from './chunk-7XYWHWT4.js';
3
+ import './chunk-M7BPQ54D.js';
3
4
  import './chunk-OGWYTYYS.js';
4
5
 
5
6
  // src/react-router/compat.ts
@@ -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;;;ACrBO,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 { createKotaoContext, type KotaoContext, type KotaoEnv } from '../context/context'\nexport { stripLocalePrefixFromRequest } from '../localization/routing'\n\nexport interface KotaoLoadContextDeps {\n readonly openCache?: (name: string) => Promise<Cache>\n readonly session?: HydrogenSession\n}\n\n/**\n * Build the React Router 7 load context for a storefront request. Call from the RR7 server\n * entry's `getLoadContext`. Isolated here so the SDK core 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: ExecutionContext,\n deps?: KotaoLoadContextDeps,\n): Promise<KotaoContext> => createKotaoContext(request, env, executionCtx, deps)\n"]}
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;;;ACrBO,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 { createKotaoContext, type KotaoContext, type KotaoEnv } from '../context/context'\nexport { stripLocalePrefixFromRequest } from '../localization/routing'\n\nexport interface KotaoLoadContextDeps {\n readonly openCache?: (name: string) => Promise<Cache>\n readonly session?: HydrogenSession\n}\n\n/**\n * Build the React Router 7 load context for a storefront request. Call from the RR7 server\n * entry's `getLoadContext`. Isolated here so the SDK core 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: ExecutionContext,\n deps?: KotaoLoadContextDeps,\n): Promise<KotaoContext> => createKotaoContext(request, env, executionCtx, deps)\n"]}