@occluze/tether 0.7.2 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +4 -4
  2. package/dist/esm/astro.js +1 -1
  3. package/dist/esm/chunks/{account-W4FBRKXA.js → account-BS5MCC2R.js} +1 -1
  4. package/dist/esm/chunks/{account-pages-IM3OQTOP.js → account-pages-XR6QZ3DX.js} +1 -1
  5. package/dist/esm/chunks/{analytics-QDVOLZY3.js → analytics-BYN3QHBA.js} +1 -1
  6. package/dist/esm/chunks/{chunk-YC4JXWRG.js → chunk-2IVIZDT5.js} +1 -4
  7. package/dist/esm/chunks/{chunk-NYDZPTLP.js → chunk-A2PCQK2F.js} +1 -1
  8. package/dist/esm/chunks/{chunk-BMDYNMRC.js → chunk-A3GXCQJ4.js} +1 -1
  9. package/dist/esm/chunks/{chunk-GF6JTI65.js → chunk-COQ6ATC6.js} +1 -1
  10. package/dist/esm/chunks/chunk-EGIDFV3T.js +1 -0
  11. package/dist/esm/chunks/{chunk-CUL66KRA.js → chunk-EW3GKRT4.js} +1 -1
  12. package/dist/esm/chunks/{chunk-CXHPBT7N.js → chunk-FM7ZLLVP.js} +1 -1
  13. package/dist/esm/chunks/{chunk-AUVAN3KZ.js → chunk-FPJGQ7Z5.js} +1 -1
  14. package/dist/esm/chunks/chunk-GXCCTPDF.js +1 -0
  15. package/dist/esm/chunks/{chunk-RPI5C3EJ.js → chunk-HXZSNZOV.js} +1 -1
  16. package/dist/esm/chunks/{chunk-RJY76ESL.js → chunk-MJFKAMFM.js} +1 -1
  17. package/dist/esm/chunks/{chunk-QUECUXHD.js → chunk-TE5PNI76.js} +1 -1
  18. package/dist/esm/chunks/{chunk-CVNRODH3.js → chunk-X2JZPXLF.js} +1 -1
  19. package/dist/esm/chunks/{diagnose-3XHPZZNP.js → diagnose-IFRNCEYG.js} +1 -1
  20. package/dist/esm/chunks/{extras-C724KKG6.js → extras-3477M6Y7.js} +1 -1
  21. package/dist/esm/chunks/{markets-ui-WBWWQAGU.js → markets-ui-3L2UZ7PK.js} +1 -1
  22. package/dist/esm/chunks/{search-5NFIZBPJ.js → search-YR73GIWY.js} +1 -1
  23. package/dist/esm/index.js +1 -1
  24. package/dist/esm/server.js +2 -2
  25. package/dist/tether.js +26 -25
  26. package/dist/tether.min.js +1 -4
  27. package/dist/tether.min.js.map +4 -4
  28. package/dist/types/src/account/page-answers.d.ts +3 -1
  29. package/dist/types/src/analytics.d.ts +3 -1
  30. package/dist/types/src/cart/store.d.ts +8 -2
  31. package/dist/types/src/client.d.ts +8 -3
  32. package/dist/types/src/controls/navigation.d.ts +3 -1
  33. package/dist/types/src/dom.d.ts +3 -1
  34. package/dist/types/src/index.d.ts +3 -3
  35. package/dist/types/src/markup.d.ts +7 -5
  36. package/dist/types/src/presets/barba.d.ts +3 -1
  37. package/dist/types/src/product-state.d.ts +3 -1
  38. package/dist/types/src/server.d.ts +4 -4
  39. package/dist/types/src/store-html.d.ts +4 -4
  40. package/dist/types/src/version.d.ts +1 -0
  41. package/package.json +4 -1
  42. package/dist/esm/chunks/chunk-MRSDJPBC.js +0 -1
  43. package/dist/esm/chunks/chunk-QDHLXXAZ.js +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../server/src/shared/api-versions.ts", "../src/api-version.ts", "../server/src/shared/json.ts", "../src/config-sections.ts", "../server/src/shared/market-codes.ts", "../src/config.ts", "../server/src/shared/error-message.ts", "../src/client.ts", "../src/markup.ts", "../src/store-html.ts", "../src/shopify-values.ts", "../src/images.ts", "../src/repeaters.ts", "../src/dom.ts", "../src/fragments.ts", "../src/selling-plans.ts", "../src/amounts.ts", "../src/product-shape.ts", "../src/cart/cart-shape.ts", "../src/cart/drawer.ts", "../src/loader.ts", "../src/product-render.ts", "../src/cart/render.ts", "../src/storage.ts", "../server/src/shared/cart-id.ts", "../src/cart/store.ts", "../src/cart/mutations.ts", "../src/cart/checkout.ts", "../src/cart.ts", "../src/product-state.ts", "../src/uri.ts", "../src/status.ts", "../src/variant-render.ts", "../src/product-events.ts", "../src/product.ts", "../src/collection.ts", "../src/mounting.ts", "../src/markets.ts", "../src/page-market.ts", "../src/part-integrity.ts", "../src/part-registry.ts", "../src/lazy-iife.ts", "../src/account-marks.ts", "../src/account-api.ts", "../src/controls/cart-controls.ts", "../src/controls/discount.ts", "../src/controls/extras.ts", "../src/controls/navigation.ts", "../src/index.ts"],
4
- "sourcesContent": ["/**\n * The Shopify API versions Tether is built and smoke-tested against, for the browser script and the Worker alike. The\n * three APIs are versioned separately, so each has its own constant even while they hold the same quarter. A new\n * version must pass `npm run smoke` on both the pinned version and the next one.\n */\n\n/** A dated, quarterly version such as `2026-07`. */\nexport type ApiVersion = `${number}-${'01' | '04' | '07' | '10'}`;\n\n/** The Storefront API: products, carts, cart buyer identity. */\nexport const STOREFRONT_API_VERSION: ApiVersion = '2026-07';\n\n/** The Customer Account API: the logged-in customer's orders, addresses and subscriptions. */\nexport const CUSTOMER_ACCOUNT_API_VERSION: ApiVersion = '2026-07';\n\n/** The Admin API the sync reads products with. */\nexport const ADMIN_API_VERSION: ApiVersion = '2026-07';\n", "/**\n * Shopify API versions are quarterly (YYYY-01, -04, -07, -10) and supported for 12 months. Tether pins a dated version\n * and never targets `unstable` (it changes without notice). Used by the config, the diagnose part and the release\n * scripts.\n */\n\nimport type { ApiVersion } from '../server/src/shared/api-versions.ts';\n\nconst QUARTERLY_VERSION = /^(\\d{4})-(01|04|07|10)$/;\nconst MONTHS_PER_QUARTER = 3;\nconst QUARTERS_PER_YEAR = 4;\nconst MONTHS_SUPPORTED = 12;\n/** Months left at which the status starts asking for an upgrade. */\nconst UPGRADE_WARNING_MONTHS = 3;\nconst AVERAGE_MONTH_MS = 30.44 * 24 * 3600 * 1000;\n\nexport interface ApiVersionStatus {\n version: string;\n /** Released and still supported. */\n ok: boolean;\n /** What to do about it, for people. */\n note: string;\n /** Months of support left, rounded; negative once support ended. */\n monthsLeft?: number;\n /** The version after this one. */\n next?: string;\n}\n\nfunction parseVersion(version: unknown): { year: number; month: number } | null {\n const match = QUARTERLY_VERSION.exec(typeof version === 'string' ? version : '');\n if (!match) return null;\n return { year: Number(match[1]), month: Number(match[2]) };\n}\n\n/** @returns True for a dated version such as `2026-07`. */\nexport const isQuarterlyVersion = (version: unknown): version is ApiVersion => parseVersion(version) !== null;\n\n/** @param month 1 to 12. */\nconst formatVersion = (year: number, month: number) => `${year}-${String(month).padStart(2, '0')}`;\n\n/**\n * `'2026-07'` \u2192 `'2026-10'`, `'2026-10'` \u2192 `'2027-01'`.\n *\n * @param steps Quarters ahead.\n * @throws {Error} When `version` isn't a quarterly version.\n */\nexport function nextApiVersion(version: string, steps = 1): string {\n const parsed = parseVersion(version);\n if (!parsed) throw new Error(`not a quarterly Shopify API version: ${version}`);\n const quarter = parsed.year * QUARTERS_PER_YEAR + (parsed.month - 1) / MONTHS_PER_QUARTER + steps;\n return formatVersion(Math.floor(quarter / QUARTERS_PER_YEAR), (quarter % QUARTERS_PER_YEAR) * MONTHS_PER_QUARTER + 1);\n}\n\n/** The support window of `version`, relative to `now`. */\nexport function apiVersionStatus(version: string, now = new Date()): ApiVersionStatus {\n if (version === 'unstable') {\n return { version, ok: false, note: '\"unstable\" changes without notice: pin a dated version such as 2026-07' };\n }\n const parsed = parseVersion(version);\n if (!parsed) return { version, ok: false, note: 'not a quarterly version (YYYY-01, -04, -07 or -10)' };\n const released = new Date(Date.UTC(parsed.year, parsed.month - 1, 1));\n const supportedUntil = new Date(Date.UTC(parsed.year, parsed.month - 1 + MONTHS_SUPPORTED, 1));\n const monthsLeft = (supportedUntil.getTime() - now.getTime()) / AVERAGE_MONTH_MS;\n const isReleased = released <= now;\n let note = `supported until about ${supportedUntil.toISOString().slice(0, 7)}`;\n if (!isReleased) note = 'not released yet (release candidate at best)';\n else if (monthsLeft <= 0) {\n note = 'no longer supported: Shopify falls back to the oldest supported version. Upgrade (see docs/reference.md)';\n } else if (monthsLeft < UPGRADE_WARNING_MONTHS) {\n note = `${note}: plan the upgrade to ${nextApiVersion(version)} or later (see docs/reference.md)`;\n }\n return {\n version,\n ok: isReleased && monthsLeft > 0,\n monthsLeft: Math.round(monthsLeft),\n next: nextApiVersion(version),\n note,\n };\n}\n", "/**\n * Reading JSON that arrives from outside (Shopify's answers, a Worker request body) field by field, for the browser\n * script and the Worker alike. Every reader takes `unknown`, so a missing or mistyped field is `undefined`, never a\n * crash.\n */\n\n/** A plain object, as parsed JSON holds it. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** The field `key` of `value`, or `undefined` when `value` is not a plain object. */\nexport function field(value: unknown, key: string): unknown {\n return isRecord(value) ? value[key] : undefined;\n}\n\n/** The value when it is an array, else an empty list. */\nexport function asList(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\n/** A non-empty string, or `null`: Shopify sends `\"\"` and `null` alike for an unset text field. */\nexport function textOrNull(value: unknown): string | null {\n return typeof value === 'string' && value !== '' ? value : null;\n}\n\nexport const numberOrNull = (value: unknown): number | null =>\n typeof value === 'number' && Number.isFinite(value) ? value : null;\n\n/** A count Shopify always sends; 0 for an answer without it. */\nexport const countOrZero = (value: unknown): number => numberOrNull(value) ?? 0;\n\nexport const listOf = <T>(value: unknown, read: (item: unknown) => T): T[] => asList(value).map((item) => read(item));\n\n/**\n * @param connection A GraphQL connection: `{ nodes: [...] }`.\n */\nexport const nodesOf = <T>(connection: unknown, read: (item: unknown) => T): { nodes: T[] } => ({\n nodes: listOf(field(connection, 'nodes'), read),\n});\n\n/** The non-empty strings of a list. */\nexport const textList = (value: unknown): string[] => listOf(value, textOrNull).filter((line) => line !== null);\n", "/**\n * Parsers for the options with structure, whose values become URLs, credentials, storage names or query text: the\n * store's domain, the metafields, the embed origins and the nested `account`, `markets` and `analytics` objects.\n * `account.server` becomes the base of credentialed requests, so a value of the wrong kind is dropped (with a warning\n * through `invalid`), never used. `config.ts` resolves the rest.\n */\nimport { isRecord } from '../server/src/shared/json.ts';\nimport type { AccountConfig, AnalyticsConfig, MarketsConfig, MetafieldIdentifier, MetafieldRef } from './config.ts';\n\n/** Warns that an option was dropped. */\ntype Invalid = (name: string, value: unknown) => void;\n\n/** A host name, without protocol or path: what `domainOption` keeps of the `domain` option. */\nexport type ShopDomain = string & { readonly __brand: 'ShopDomain' };\ntype LocaleMarket = NonNullable<MarketsConfig['locales']>[string];\ntype ShopifyAnalyticsOptions = Exclude<NonNullable<AnalyticsConfig['shopify']>, boolean>;\n\n/**\n * Metafield identifiers are written into the product fragment's text: a GraphQL fragment can't declare variables, so\n * they can't be sent as variables. Only these characters are let through, which covers Shopify's namespaces and keys.\n */\nconst METAFIELD_PART = /^[A-Za-z0-9_.-]+$/;\n/** A namespace is the same, or an app-reserved one: `$app` or `$app:<name>`. */\nconst METAFIELD_NAMESPACE = /^(\\$app(:[A-Za-z0-9_-]+)?|[A-Za-z0-9_.-]+)$/;\n/** A DNS name: labels of letters, digits and inner hyphens. No port, user or path. */\nconst HOSTNAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;\n/** A cookie name, as RFC 6265 allows it. */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n/** A global variable's name, which `analytics.dataLayer` names. */\nconst GLOBAL_NAME = /^[A-Za-z_$][\\w$]*$/;\n/** Stands for the site's own origin, to tell a path on the site from a URL elsewhere. */\nconst SITE_ORIGIN = 'https://site.invalid';\n/** Hosts a developer's own machine answers on, where plain http is the norm (`wrangler dev`). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);\n/** Every source `markets.detect` reads. */\nexport const MARKET_SOURCES: readonly ('saved' | 'server' | 'webflow')[] = ['saved', 'server', 'webflow'];\nconst CONSENT_MODES: readonly NonNullable<ShopifyAnalyticsOptions['consent']>[] = [\n 'no-banner',\n 'default-banner',\n 'custom-banner',\n];\n\nconst isText = (value: unknown): value is string => typeof value === 'string' && value !== '';\n\n/** A hook the site passes. What it is called with is up to Tether, and what it returns is not used. */\nconst isFunction = (value: unknown): value is (...args: unknown[]) => unknown => typeof value === 'function';\n\n/**\n * @returns The store's domain without protocol or path (`https://shop.example/x` \u2192 `shop.example`), `''` for anything\n * that isn't a host name.\n */\nexport function domainOption(value: unknown, invalid: Invalid): ShopDomain | '' {\n if (typeof value !== 'string' || !value) return '';\n const host = value.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');\n if (isHostname(host)) return host;\n invalid('domain', value);\n return '';\n}\n\n/** @returns True for a path on this site, an https URL, or an http one on the developer's machine. */\nfunction isSiteOrHttpsUrl(value: unknown): value is string {\n if (!isText(value)) return false;\n try {\n const url = new URL(value, `${SITE_ORIGIN}/`);\n if (url.origin === SITE_ORIGIN) return true;\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname));\n } catch {\n // Not a URL at all.\n return false;\n }\n}\n\n/**\n * The folder of the optional parts. It must be https or on this site, which keeps parts off plain http (mixed content,\n * rewritable on the way); it doesn't vouch for the host, which the site chose.\n *\n * @returns The folder as written, `null` for none or one elsewhere (the parts then load beside the core).\n */\nexport function partsUrlOption(value: unknown, invalid: Invalid): string | null {\n if (value === undefined || value === null || value === '') return null;\n if (typeof value === 'string' && (isSiteOrHttpsUrl(value) || isPageOrigin(value))) return value;\n invalid('partsUrl', value);\n return null;\n}\n\n/** @returns True for an absolute URL on the page's own origin (a site served over plain http). */\nfunction isPageOrigin(value: string): boolean {\n if (typeof location === 'undefined') return false;\n try {\n return new URL(value).origin === location.origin;\n } catch {\n // Not an absolute URL: `isSiteOrHttpsUrl` has judged it.\n return false;\n }\n}\n\n/** @returns An https origin written as just that (`https://video.example`), `null` for anything else. */\nfunction toHttpsOrigin(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && url.href === new URL(url.origin).href ? url.origin : null;\n } catch {\n // Not an absolute URL.\n return null;\n }\n}\n\n/**\n * The origins a site lets description iframes load from. Each one is trusted to show only what the site expects in a\n * description, so only a bare https origin is taken: a path would suggest a narrower trust than is given.\n *\n * @returns The valid origins; each other entry is skipped with a warning.\n */\nexport function embedOriginsOption(value: unknown, invalid: Invalid): string[] {\n if (!Array.isArray(value)) {\n invalid('embedOrigins', value);\n return [];\n }\n const origins: string[] = [];\n const entries: unknown[] = value;\n for (const entry of entries) {\n const origin = toHttpsOrigin(entry);\n if (origin) origins.push(origin);\n else invalid('embedOrigin', entry);\n }\n return origins;\n}\n\n/** @returns The identifier of `'namespace.key'` or `{ namespace, key }`, when both parts are valid identifiers. */\nexport function toMetafieldIdentifier(ref: unknown): MetafieldIdentifier | null {\n let namespace: unknown;\n let key: unknown;\n if (typeof ref === 'string') {\n const dot = ref.indexOf('.');\n namespace = dot > 0 ? ref.slice(0, dot) : '';\n key = ref.slice(dot + 1);\n } else if (typeof ref === 'object' && ref !== null) {\n namespace = Reflect.get(ref, 'namespace');\n key = Reflect.get(ref, 'key');\n }\n if (typeof namespace !== 'string' || typeof key !== 'string') return null;\n return METAFIELD_NAMESPACE.test(namespace) && METAFIELD_PART.test(key) ? { namespace, key } : null;\n}\n\n/** @returns The refs that name a valid identifier, as written; each other one is skipped with a warning. */\nexport function metafieldsOption(value: unknown, invalid: Invalid): MetafieldRef[] {\n if (!Array.isArray(value)) {\n invalid('metafields', value);\n return [];\n }\n const refs: MetafieldRef[] = [];\n const entries: unknown[] = value;\n for (const ref of entries) {\n const identifier = toMetafieldIdentifier(ref);\n if (identifier) refs.push(typeof ref === 'string' ? ref : identifier);\n else invalid('metafield', ref);\n }\n return refs;\n}\n\n/** How each field of a nested option is read: the field's value when it is of the right kind, else `undefined`. */\ntype Schema<Section extends object> = { [Key in keyof Section]-?: (value: unknown) => Section[Key] | undefined };\n\nconst when =\n <T>(check: (value: unknown) => value is T) =>\n (value: unknown): T | undefined =>\n check(value) ? value : undefined;\n\n/** The pattern is what makes a string a `ShopDomain`. */\nconst isHostname = (value: unknown): value is ShopDomain => isText(value) && HOSTNAME.test(value);\n\nconst isCookieName = (value: unknown): value is string => isText(value) && COOKIE_NAME.test(value);\n\n/** `true` for `window.dataLayer`, or the name of another global array. */\nconst isDataLayer = (value: unknown): value is boolean | string =>\n typeof value === 'boolean' || (isText(value) && GLOBAL_NAME.test(value));\n\nconst url = when(isSiteOrHttpsUrl);\nconst text = when(isText);\nconst flag = when((value): value is boolean => typeof value === 'boolean');\nconst hook = when(isFunction);\n\n/** @returns The fields of the right kind; each other one is dropped with a warning. */\nfunction sectionOption<Section extends object>(\n name: string,\n value: unknown,\n schema: Schema<Section>,\n invalid: Invalid,\n): Section | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value)) {\n invalid(name, value);\n return null;\n }\n const section: Record<string, unknown> = {};\n const readers: Readonly<Record<string, (value: unknown) => unknown>> = schema;\n for (const [key, read] of Object.entries(readers)) {\n const field = value[key];\n if (field === undefined) continue;\n const parsed = read(field);\n if (parsed === undefined) invalid(`${name}.${key}`, field);\n else section[key] = parsed;\n }\n // Each field went through the reader its schema entry gives it, which returns that field's type in `Section`.\n return section as Section;\n}\n\nconst ACCOUNT: Schema<Partial<AccountConfig>> = {\n server: url,\n orderUrl: url,\n subscriptionsUrl: url,\n paymentUrl: url,\n logoutUrl: url,\n locale: text,\n navigate: hook,\n};\n\n/** Customer tokens are never kept in the browser, so an `account` without the Worker is off. */\nconst ACCOUNT_NEEDS_SERVER = 'customer accounts need the Worker since 0.7.0: set account.server';\n\nconst LOCALE_MARKET: Schema<LocaleMarket> = { country: text, language: text, locale: text };\n\nconst MARKETS: Schema<MarketsConfig> = {\n // A single source name counts as a list of one.\n detect: (value) => {\n if (typeof value === 'boolean') return value;\n const names: unknown[] = Array.isArray(value) ? value : [value];\n return typeof value === 'string' || Array.isArray(value)\n ? MARKET_SOURCES.filter((source) => names.includes(source))\n : undefined;\n },\n locales: (value) => {\n if (!isRecord(value)) return undefined;\n const locales: Record<string, LocaleMarket> = {};\n for (const [locale, entry] of Object.entries(value)) {\n const market = sectionOption(`markets.locales.${locale}`, entry, LOCALE_MARKET, () => undefined);\n if (market) locales[locale] = market;\n }\n return locales;\n },\n cookie: when(isCookieName),\n remember: flag,\n strict: flag,\n};\n\n/**\n * `proxy` says the site serves the Storefront API on its own domain. Shopify's script only needs to know that it\n * does, not where, so any truthy value (including a path such as `'/api'`) turns it on.\n */\nconst SHOPIFY_ANALYTICS: Schema<ShopifyAnalyticsOptions> = {\n consent: (value) => CONSENT_MODES.find((mode) => mode === value),\n proxy: (value) => value !== undefined && Boolean(value),\n};\n\nconst ANALYTICS: Schema<AnalyticsConfig> = {\n dataLayer: when(isDataLayer),\n shopify: (value) =>\n typeof value === 'boolean'\n ? value\n : (sectionOption('analytics.shopify', value, SHOPIFY_ANALYTICS, () => undefined) ?? undefined),\n onEvent: hook,\n};\n\n/**\n * @param warn Says why an `account` without a usable `server` is off.\n * @returns `null` without a usable `account.server`: nothing of the account part loads.\n */\nexport function accountOption(value: unknown, invalid: Invalid, warn: (message: string) => void): AccountConfig | null {\n const account = sectionOption('account', value, ACCOUNT, invalid);\n if (account === null) return null;\n const { server } = account;\n if (server === undefined) {\n warn(ACCOUNT_NEEDS_SERVER);\n return null;\n }\n return { ...account, server };\n}\n\nexport const marketsOption = (value: unknown, invalid: Invalid): MarketsConfig | null =>\n sectionOption('markets', value, MARKETS, invalid);\n\nexport const analyticsOption = (value: unknown, invalid: Invalid): AnalyticsConfig | null =>\n sectionOption('analytics', value, ANALYTICS, invalid);\n", "/**\n * Country and language codes as Shopify takes them, parsed the same way in the browser and the Worker: anything that\n * isn't a code is dropped rather than repaired.\n */\n\n/** An ISO 3166-1 alpha-2 code, upper case. */\nexport type CountryCode = string & { readonly __brand: 'CountryCode' };\n/** A Shopify `LanguageCode` value. */\nexport type LanguageCode = string & { readonly __brand: 'LanguageCode' };\n\nconst COUNTRY = /^[A-Z]{2}$/;\n/** Cloudflare's codes for an unknown country (XX) and Tor (T1), which a `cf-ipcountry` cookie can carry. */\nconst NOT_A_COUNTRY = new Set(['XX', 'T1']);\n/** Shopify `LanguageCode` values with a region. Every other language is its plain ISO 639 code. */\nconst REGIONAL_LANGUAGES = new Set(['PT_BR', 'PT_PT', 'ZH_CN', 'ZH_TW']);\nconst LANGUAGE = /^[A-Z]{2,3}$/;\n\n/** @returns `'de'` \u2192 `'DE'`; `null` for anything that is not a country code. */\nexport function toCountry(value: unknown): CountryCode | null {\n const code = (typeof value === 'string' ? value : '').trim().toUpperCase();\n if (!COUNTRY.test(code) || NOT_A_COUNTRY.has(code)) return null;\n // The checks above are what makes a string a CountryCode.\n return code as CountryCode;\n}\n\n/** @returns `'de-DE'` \u2192 `'DE'`, `'pt-BR'` \u2192 `'PT_BR'`, `'en'` \u2192 `'EN'`; `null` otherwise. */\nexport function toLanguage(value: unknown): LanguageCode | null {\n const raw = (typeof value === 'string' ? value : '').trim().replace('-', '_').toUpperCase();\n // The set and the pattern are what makes a string a LanguageCode.\n if (REGIONAL_LANGUAGES.has(raw)) return raw as LanguageCode;\n const primary = raw.split('_')[0] ?? '';\n return LANGUAGE.test(primary) ? (primary as LanguageCode) : null;\n}\n", "/**\n * The runtime configuration. `cfg` is one shared object that every browser module imports; `configure()` replaces its\n * contents in place, so those imports see the new values, and `setCountry()` changes its market keys the same way.\n * Every option is validated here, once, where it arrives: values that end up in query text, URLs, attribute names or\n * storage keys never reach the code that uses them unchecked.\n */\nimport { STOREFRONT_API_VERSION } from '../server/src/shared/api-versions.ts';\nimport { isQuarterlyVersion } from './api-version.ts';\nimport {\n accountOption,\n analyticsOption,\n domainOption,\n embedOriginsOption,\n marketsOption,\n metafieldsOption,\n partsUrlOption,\n} from './config-sections.ts';\n\nexport { MARKET_SOURCES } from './config-sections.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport type { ApiVersion } from '../server/src/shared/api-versions.ts';\nimport type { ShopDomain } from './config-sections.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** Where the visitor's market comes from (`markets.ts`). */\nexport interface MarketsConfig {\n /** True: every source; a list: only those. */\n detect?: boolean | ('saved' | 'server' | 'webflow')[];\n /** Webflow locale (subdirectory or html lang) to market. */\n locales?: Record<string, { country?: string; language?: string; locale?: string }>;\n /** The cookie a server sets with the visitor's country. */\n cookie?: string;\n /** Remember the visitor's choice (default true). */\n remember?: boolean;\n /** With `locales`: ignore an html lang that isn't in the map. */\n strict?: boolean;\n}\n\n/** The optional analytics part. */\nexport interface AnalyticsConfig {\n /** GA4/GTM events, into `window.dataLayer` or the named array. */\n dataLayer?: boolean | string;\n /** Shopify's own analytics. `proxy`: the site serves the Storefront API on its own domain. */\n shopify?: boolean | { consent?: 'no-banner' | 'default-banner' | 'custom-banner'; proxy?: boolean };\n onEvent?: (name: string, data: Record<string, unknown>) => void;\n}\n\n/** The optional customer accounts part, which runs through the site's Worker. */\nexport interface AccountConfig {\n /** The Worker's account routes, a URL or a path on this site. */\n server: string;\n /** The site's own order page, with `{id}` for the numeric order id. */\n orderUrl?: string;\n /** Shopify's subscription management page. */\n subscriptionsUrl?: string;\n /** A separate page for payment methods (default `subscriptionsUrl`). */\n paymentUrl?: string;\n /** Where Shopify sends the visitor after logout (default the home page). */\n logoutUrl?: string;\n /** The language of Shopify's login pages. */\n locale?: string;\n /** Replaces `location.assign` for the login redirect (popups, tests). */\n navigate?: (url: string) => void;\n}\n\nexport interface MetafieldIdentifier {\n namespace: string;\n key: string;\n}\n/** `'namespace.key'` or the identifier itself. */\nexport type MetafieldRef = string | MetafieldIdentifier;\n/** Shopify's note on a cart change. */\nexport interface CartWarning {\n code: string;\n message: string;\n target?: string;\n}\nexport type CheckoutTarget = '_self' | '_blank';\ntype PurchaseKind = 'one-time' | 'subscription';\n\n/** Every option, with its default filled in. */\nexport interface TetherConfig {\n /** The store's domain, without protocol or path; `''` until one is set. */\n domain: ShopDomain | '';\n /** The public Storefront API access token. */\n token: string;\n /** A dated Shopify API version (or `unstable`, with a warning). */\n apiVersion: ApiVersion | 'unstable';\n /** The market's country, for `@inContext(country:)`. */\n country: CountryCode | undefined;\n /** The market's language, for `@inContext(language:)`. */\n language: LanguageCode | undefined;\n /** An `Intl` locale for money and dates. */\n locale: string | undefined;\n markets: MarketsConfig | null;\n /** The attribute prefix: `tether` reads `tether-product`, `tether-bind`. */\n prefix: string;\n /** A folder (`/product`) or a template with `{handle}` / `{id}`. */\n productPath: string;\n collectionPath: string;\n /** The product metafields to fetch. */\n metafields: MetafieldRef[];\n /** Origins (`https://video.example`) a description's iframes may load from, besides the built-in video players. */\n embedOrigins: string[];\n imagesFirst: number;\n variantsFirst: number;\n sellingPlanGroupsFirst: number;\n sellingPlansFirst: number;\n collectionFirst: number;\n /**\n * How many times a failed request is sent again. A query is retried after a network error (no answer, or a body that\n * stopped arriving), a 429, a 5xx or Shopify's `THROTTLED`. A mutation, a cart change included, is retried only\n * after a 429 or `THROTTLED`, which Shopify sends without running it: after a network error or a 5xx it may\n * already have applied the change.\n */\n retries: number;\n /** Refresh server-rendered products once in the background. */\n revalidate: boolean;\n /** The localStorage key of the cart id (default `<prefix>:cart:<domain>`). */\n storageKey: string;\n cartDrawerOpenClass: string;\n openCartOnAdd: boolean;\n /** Move focus into the open cart, keep Tab inside it, restore it on close. */\n cartFocus: boolean;\n checkoutTarget: CheckoutTarget;\n defaultPurchase: PurchaseKind;\n /** `?variant=` follows the visitor's choice on the page's main product. */\n variantUrl: boolean;\n /** Seconds a Shopify answer may be cached; 0 leaves it to Shopify, which can be hours. */\n maxAge: number;\n /** `tether-show=\"low-stock\"` at this many or fewer (needs inventory access). */\n lowStock: number;\n /** Milliseconds the search part waits after the last keystroke. */\n searchDelay: number;\n /** Mount the document on DOMContentLoaded. */\n autoMount: boolean;\n /** Mount elements added later (CMS load more, filters). */\n observe: boolean;\n /** Mount on Astro's `astro:page-load` and close the cart on navigation. */\n astro: boolean;\n /** The folder of the optional parts, for the `<script>` build. */\n partsUrl: string | null;\n analytics: AnalyticsConfig | null;\n account: AccountConfig | null;\n /** The site's text for a cart warning; nothing keeps Shopify's. */\n warningText: ((warning: CartWarning) => string | null | undefined) | null;\n formatMoney: ((money: Money, locale: string | undefined) => string) | null;\n /** Default: `ScrollTrigger.refresh()` when present. */\n onLayoutChange: ((reason: string) => void) | null;\n onCartOpen: ((drawers: HTMLElement[]) => void) | null;\n onCartClose: ((drawers: HTMLElement[]) => void) | null;\n onLineEnter: ((node: HTMLElement) => void) | null;\n /** Animates a line out, then calls `done()`. */\n onLineLeave: ((node: HTMLElement, done: () => void) => void) | null;\n onValueChange: ((element: HTMLElement, previousText: string) => void) | null;\n /** Every failure that reaches the visitor. */\n onError: ((error: unknown, context: string) => void) | null;\n debug: boolean;\n}\n\n/** The options a site writes as plain text, which `resolveConfig` checks into their typed form. */\ntype ParsedFromText = 'domain' | 'apiVersion' | 'country' | 'language' | 'storageKey';\n\n/**\n * What a site passes: any option, the market as the codes people write (`'de'`, `'pt-BR'`), and `null` for the\n * default storage key.\n */\nexport type TetherOptions = {\n [Key in Exclude<keyof TetherConfig, ParsedFromText>]?: TetherConfig[Key];\n} & Partial<Record<Exclude<ParsedFromText, 'storageKey'>, string | undefined>> & {\n storageKey?: string | null | undefined;\n };\n\nexport const DEFAULTS: Readonly<TetherConfig> = {\n domain: '',\n token: '',\n apiVersion: STOREFRONT_API_VERSION,\n country: undefined,\n language: undefined,\n locale: undefined,\n markets: null,\n prefix: 'tether',\n productPath: '/product',\n collectionPath: '/collection',\n metafields: [],\n embedOrigins: [],\n imagesFirst: 25,\n variantsFirst: 100,\n sellingPlanGroupsFirst: 10,\n sellingPlansFirst: 25,\n collectionFirst: 24,\n retries: 2,\n revalidate: true,\n storageKey: '',\n cartDrawerOpenClass: 'is-open',\n openCartOnAdd: true,\n cartFocus: true,\n checkoutTarget: '_self',\n defaultPurchase: 'one-time',\n variantUrl: false,\n maxAge: 60,\n lowStock: 5,\n searchDelay: 150,\n autoMount: true,\n observe: false,\n astro: true,\n partsUrl: null,\n analytics: null,\n account: null,\n warningText: null,\n formatMoney: null,\n onLayoutChange: null,\n onCartOpen: null,\n onCartClose: null,\n onLineEnter: null,\n onLineLeave: null,\n onValueChange: null,\n onError: null,\n debug: false,\n};\n\nexport const cfg: TetherConfig = { ...DEFAULTS };\n\n/**\n * @returns A view of `target` and the objects in it that reads the live values and refuses every change: a write\n * throws in strict-mode code (as on a frozen object) and warns in any code.\n */\nfunction readOnlyView<T extends object>(target: T): T {\n const refuse = (key: string | symbol) => {\n warn(`Tether.config is read-only (${String(key)}): pass options to Tether.init() instead`);\n return false;\n };\n return new Proxy(target, {\n get(object, key) {\n const value: unknown = Reflect.get(object, key);\n // The config's objects are its own copies (`resolveConfig`), never frozen, so a view of each is allowed.\n return typeof value === 'object' && value !== null ? readOnlyView(value) : value;\n },\n set: (_object, key) => refuse(key),\n deleteProperty: (_object, key) => refuse(key),\n defineProperty: (_object, key) => refuse(key),\n });\n}\n\n/**\n * `cfg` as `Tether.config` shows it. A value written there would skip the validation `configure()` does, so the view\n * is read-only; `Tether.init(options)` is how a site changes the config.\n */\nexport const configView = readOnlyView(cfg);\n\n/** The prefix becomes part of attribute names, CSS selectors and event names, so it is kept to those characters. */\nconst PREFIX = /^[a-z][a-z0-9-]*$/;\n\ntype CountOption =\n | 'imagesFirst'\n | 'variantsFirst'\n | 'sellingPlanGroupsFirst'\n | 'sellingPlansFirst'\n | 'collectionFirst'\n | 'retries'\n | 'maxAge'\n | 'lowStock'\n | 'searchDelay';\ntype HookOption =\n | 'warningText'\n | 'formatMoney'\n | 'onLayoutChange'\n | 'onCartOpen'\n | 'onCartClose'\n | 'onLineEnter'\n | 'onLineLeave'\n | 'onValueChange'\n | 'onError';\ntype SwitchOption =\n 'revalidate' | 'openCartOnAdd' | 'cartFocus' | 'variantUrl' | 'autoMount' | 'observe' | 'astro' | 'debug';\ntype TextOption = 'token' | 'productPath' | 'collectionPath' | 'cartDrawerOpenClass';\n\nconst SWITCH_OPTIONS: readonly SwitchOption[] = [\n 'revalidate',\n 'openCartOnAdd',\n 'cartFocus',\n 'variantUrl',\n 'autoMount',\n 'observe',\n 'astro',\n 'debug',\n];\nconst TEXT_OPTIONS: readonly TextOption[] = ['token', 'productPath', 'collectionPath', 'cartDrawerOpenClass'];\n\n/** Options that are written into queries as list sizes or used as counts. */\nconst COUNT_OPTIONS: readonly CountOption[] = [\n 'imagesFirst',\n 'variantsFirst',\n 'sellingPlanGroupsFirst',\n 'sellingPlansFirst',\n 'collectionFirst',\n 'retries',\n 'maxAge',\n 'lowStock',\n 'searchDelay',\n];\nconst HOOK_OPTIONS: readonly HookOption[] = [\n 'warningText',\n 'formatMoney',\n 'onLayoutChange',\n 'onCartOpen',\n 'onCartClose',\n 'onLineEnter',\n 'onLineLeave',\n 'onValueChange',\n 'onError',\n];\n\n/** @returns `fallback`, with a warning unless `value` was left out. */\nfunction invalidOption<T>(name: string, value: unknown, fallback: T): T {\n if (value !== undefined) warn(`invalid ${name} ${JSON.stringify(value)}: using ${JSON.stringify(fallback)}`);\n return fallback;\n}\n\n/** Warns about an option without a default to fall back to, which is left out (`config-sections.ts` too). */\nfunction dropped(name: string, value: unknown): void {\n warn(`invalid ${name} ${JSON.stringify(value)}: ignored`);\n}\n\n/**\n * Warns about each key that is no option, so a misspelt one (`openCartOnAd`) is seen; none of them reaches `cfg`.\n */\nfunction warnUnknownKeys(options: Readonly<Record<string, unknown>>): void {\n for (const key of Object.keys(options)) {\n if (!(key in DEFAULTS)) warn(`unknown option ${JSON.stringify(key)}: ignored`);\n }\n}\n\n/** @returns A site's hook, `null` for anything that isn't a function. */\nfunction hookOption(value: unknown): ((...args: never[]) => unknown) | null {\n return typeof value === 'function' ? (value as (...args: never[]) => unknown) : null;\n}\n\nfunction hookOptions(options: Readonly<Record<string, unknown>>): Pick<TetherConfig, HookOption> {\n const hooks = Object.fromEntries(HOOK_OPTIONS.map((name) => [name, hookOption(options[name])]));\n // A function from the site can only be checked for being callable; how it is called is the documented signature\n // of its option.\n return hooks as Pick<TetherConfig, HookOption>;\n}\n\n/** @returns `value` when it is a non-negative integer, else the default. */\nfunction countOption(name: CountOption, value: unknown): number {\n if (typeof value === 'number' && Number.isInteger(value) && value >= 0) return value;\n return invalidOption(name, value, DEFAULTS[name]);\n}\n\nfunction switchOption(name: SwitchOption, value: unknown): boolean {\n return typeof value === 'boolean' ? value : invalidOption(name, value, DEFAULTS[name]);\n}\n\nfunction textOption(name: TextOption, value: unknown): string {\n return typeof value === 'string' ? value : invalidOption(name, value, DEFAULTS[name]);\n}\n\n/** @returns An `Intl` locale as written, `undefined` for one `Intl` doesn't accept. */\nfunction localeOption(value: unknown): string | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n try {\n if (typeof value === 'string' && Intl.getCanonicalLocales(value).length) return value;\n } catch {\n // RangeError: not a locale (falls through to the warning).\n }\n dropped('locale', value);\n return undefined;\n}\n\n/** @param choices The first one is the default. */\nfunction choiceOption<Choice extends string>(\n name: string,\n value: unknown,\n choices: readonly [Choice, ...Choice[]],\n): Choice {\n return choices.find((choice) => choice === value) ?? invalidOption(name, value, choices[0]);\n}\n\n/**\n * @returns A quarterly version, or `unstable` with a warning; the default for anything else, which would be written\n * into the endpoint's path.\n */\nfunction apiVersionOption(value: unknown): ApiVersion | 'unstable' {\n if (value === 'unstable') {\n warn('apiVersion \"unstable\" changes without notice. Pin a dated version (e.g. 2026-07).');\n return value;\n }\n return isQuarterlyVersion(value) ? value : invalidOption('apiVersion', value, DEFAULTS.apiVersion);\n}\n\nfunction codeOption<Code>(name: string, value: unknown, parse: (value: unknown) => Code | null): Code | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n const code = parse(value);\n if (code !== null) return code;\n dropped(name, value);\n return undefined;\n}\n\n/**\n * The configuration `options` describe: defaults filled in, every value validated (an invalid one warns and falls\n * back to its default), hooks that aren't functions dropped so callers only check for null.\n *\n * @param options What the site set, or any object (the server helpers take their options from code and `.env`).\n */\nexport function resolveConfig(options: TetherOptions | Readonly<Record<string, unknown>>): TetherConfig {\n warnUnknownKeys(options);\n const domain = domainOption(options.domain, dropped);\n const prefix =\n typeof options.prefix === 'string' && PREFIX.test(options.prefix)\n ? options.prefix\n : invalidOption('prefix', options.prefix, DEFAULTS.prefix);\n const resolved: TetherConfig = {\n ...DEFAULTS,\n ...hookOptions(options),\n domain,\n apiVersion: apiVersionOption(options.apiVersion),\n country: codeOption('country', options.country, toCountry),\n locale: localeOption(options.locale),\n partsUrl: partsUrlOption(options.partsUrl, dropped),\n language: codeOption('language', options.language, toLanguage),\n markets: marketsOption(options.markets, dropped),\n analytics: analyticsOption(options.analytics, dropped),\n account: accountOption(options.account, dropped, warn),\n prefix,\n storageKey:\n typeof options.storageKey === 'string' && options.storageKey ? options.storageKey : `${prefix}:cart:${domain}`,\n metafields: metafieldsOption(options.metafields ?? [], dropped),\n embedOrigins: embedOriginsOption(options.embedOrigins ?? [], dropped),\n checkoutTarget: choiceOption('checkoutTarget', options.checkoutTarget, ['_self', '_blank']),\n defaultPurchase: choiceOption('defaultPurchase', options.defaultPurchase, ['one-time', 'subscription']),\n };\n for (const name of COUNT_OPTIONS) resolved[name] = countOption(name, options[name]);\n for (const name of SWITCH_OPTIONS) resolved[name] = switchOption(name, options[name]);\n for (const name of TEXT_OPTIONS) resolved[name] = textOption(name, options[name]);\n return resolved;\n}\n\n/**\n * @param value A `markets` option from code other than the config (`Tether.detectMarket(markets)`).\n * @returns It validated as the config's own is, with the same warnings.\n */\nexport const parseMarkets = (value: unknown): MarketsConfig | null => marketsOption(value, dropped);\n\n/**\n * Replaces the shared configuration with `options` over the defaults (see `resolveConfig`).\n *\n * @returns The shared `cfg`.\n */\nexport function configure(options: TetherOptions | Readonly<Record<string, unknown>> = {}): TetherConfig {\n const resolved = resolveConfig(options);\n for (const key of Object.keys(cfg)) {\n if (!(key in resolved)) Reflect.deleteProperty(cfg, key);\n }\n return Object.assign(cfg, resolved);\n}\n\n/** @returns True once a domain and a token are set. */\nexport const isConfigured = (): boolean => Boolean(cfg.domain && cfg.token);\n\nexport function log(...args: unknown[]): void {\n if (cfg.debug) console.log(`[${cfg.prefix}]`, ...args);\n}\n\nexport function warn(...args: unknown[]): void {\n console.warn(`[${cfg.prefix}]`, ...args);\n}\n\n/**\n * Logs a failure that reaches the visitor and hands it to `onError`. A throwing `onError` is logged, never rethrown:\n * the site's hook must not break the render that reported the failure.\n *\n * @param context What failed, e.g. `product coffee-box`.\n */\nexport function reportError(error: unknown, context: string): void {\n warn(context, error instanceof Error && error.message ? error.message : error);\n if (!cfg.onError) return;\n try {\n cfg.onError(error, context);\n } catch (hookError) {\n warn('onError threw', hookError);\n }\n}\n", "/** The text of anything thrown: an `Error`'s message, or the value itself written as text. */\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n", "/**\n * The one Storefront API client: a fetch wrapper that adds the market (`@inContext`), retries what is safe to send\n * again (`Operation`), and turns every failure into a `TetherError`, an answer whose `data` lacks what its caller reads included\n * (`DataCheck`). The browser (`gql`, and `query` for `Tether.query()`) and the server helpers (`request` with their own\n * config) share it.\n */\nimport { cfg, log } from './config.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { field, isRecord } from '../server/src/shared/json.ts';\nimport { messageOf } from '../server/src/shared/error-message.ts';\nimport type { TetherConfig } from './config.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\nexport type TetherErrorCode =\n | 'NOT_FOUND'\n | 'NETWORK'\n | 'NOT_CONFIGURED'\n | 'NOT_LOGGED_IN'\n | 'INVALID'\n | 'INVALID_COUNTRY'\n | 'INVALID_DISCOUNT'\n | 'INVALID_RESPONSE'\n | 'SERVER_ERROR'\n | 'ERROR'\n | `HTTP_${number}`;\nexport interface GraphQLError {\n message: string;\n extensions?: { code?: string };\n}\n/** The response shape the GraphQL spec sets. */\ninterface GraphQLResponse {\n data?: unknown;\n errors?: GraphQLError[];\n}\nexport interface UserError {\n message: string;\n field?: string[] | null;\n code?: string | null;\n}\ninterface TetherErrorDetails {\n /** Default `HTTP_<status>` with a status, else `ERROR`. */\n code?: TetherErrorCode | undefined;\n /** The HTTP status of the response that failed. */\n status?: number;\n /** The GraphQL errors of that response. */\n errors?: readonly GraphQLError[];\n /** A mutation's user errors. */\n userErrors?: readonly UserError[];\n}\n\nexport class TetherError extends Error {\n override readonly name = 'TetherError' as const;\n readonly code: TetherErrorCode;\n readonly status: number | undefined;\n readonly errors: readonly GraphQLError[] | undefined;\n readonly userErrors: readonly UserError[] | undefined;\n\n constructor(message: string, { status, errors, userErrors, code }: TetherErrorDetails = {}) {\n super(message);\n this.code = code ?? (status ? `HTTP_${status}` : 'ERROR');\n this.status = status;\n this.errors = errors;\n this.userErrors = userErrors;\n }\n}\n\n/** What a request needs from a config: the browser's `cfg`, or a config the server helpers build. */\nexport type StorefrontConfig = Pick<\n TetherConfig,\n 'domain' | 'token' | 'apiVersion' | 'country' | 'language' | 'maxAge' | 'retries'\n> & { fetch?: typeof fetch };\n/** Checks a query's `data` before its caller reads it as a `T`. */\nexport type DataCheck<T> = (data: unknown) => data is T;\n/**\n * The structure of JSON a check accepts: a `typeof` name, `'object'` for a plain object, `[item]` for a list of\n * items, an object's fields, or a check of its own. Fields it doesn't name aren't checked, so a shape names only what\n * its reader walks without a fallback. A field's `undefined` is the type of one `ShapeOf` leaves out, and checks\n * nothing.\n */\nexport type Shape =\n | 'string'\n | 'number'\n | 'boolean'\n | 'object'\n | ((value: unknown) => boolean)\n | readonly [Shape]\n | { readonly [key: string]: Shape | undefined };\n\n/** @returns True when `value` has the structure `shape` describes. */\nexport function matches(value: unknown, shape: Shape): boolean {\n if (typeof shape === 'function') return shape(value);\n if (typeof shape === 'string') return shape === 'object' ? isRecord(value) : typeof value === shape;\n if (isShapeList(shape)) return Array.isArray(value) && value.every((item) => matches(item, shape[0]));\n return (\n isRecord(value) && Object.entries(shape).every(([key, inner]) => inner === undefined || matches(value[key], inner))\n );\n}\n\n/** `Array.isArray` doesn't narrow a readonly tuple out of a union. */\nconst isShapeList = (shape: Shape): shape is readonly [Shape] => Array.isArray(shape);\n\n/**\n * The shapes that describe a `T`: every structure such a shape names is one `T` declares at that place, and a check\n * in it is a type guard for a type `T` accepts there. A shape for `T` therefore can't name a field `T` lacks or claim\n * a type `T` contradicts, so a change to `T` that its shape no longer agrees with fails to compile. Fields of `T` the\n * shape doesn't name stay unchecked (`Shape`).\n */\nexport type ShapeOf<T> = unknown extends T ? Shape : KnownShapeOf<T>;\ntype KnownShapeOf<T> =\n | DataCheck<T>\n | (string extends T ? 'string' : never)\n | (number extends T ? 'number' : never)\n | (boolean extends T ? 'boolean' : never)\n | ListShapeOf<T>\n | FieldsShapeOf<T>;\ntype ListShapeOf<T> = T extends readonly (infer Item)[] ? readonly [ShapeOf<Item>] : never;\ntype FieldsShapeOf<T> = T extends readonly unknown[]\n ? never\n : T extends object\n ? 'object' | { readonly [Key in keyof T]?: ShapeOf<T[Key]> }\n : never;\n\n/** @returns A check that `value` is `null` or has the structure `shape` describes. */\nexport const orNull =\n <T>(shape: ShapeOf<T>): DataCheck<T | null> =>\n (value): value is T | null =>\n value === null || matches(value, shape);\n\n/**\n * @returns A check that `data` has the structure `shape` describes. `ShapeOf` ties `shape` to `T`; fields `shape`\n * doesn't name are `T`'s claim.\n */\nexport const shaped =\n <T>(shape: ShapeOf<T>): DataCheck<T> =>\n (data): data is T =>\n matches(data, shape);\n/**\n * What an operation does to the store, which decides what is safe to retry. A query only reads, so a failure that may\n * pass is retried: the network (no answer, or a body that stopped arriving), a 429, a 5xx, `THROTTLED` and\n * `INTERNAL_SERVER_ERROR`. Shopify may have applied a mutation before a server error or a dropped connection, so a\n * mutation is retried only when Shopify answers that it didn't run it (429 or `THROTTLED`): sending it again could add\n * a line twice. Other failures (a 4xx, a 430, an answer that isn't valid, other GraphQL errors) are never retried.\n */\nexport type Operation = 'query' | 'mutation';\n\ninterface RequestOptions {\n /** `false` sends the query without `@inContext`. */\n context?: boolean;\n /** Default `'query'`. */\n operation?: Operation;\n}\n/** What the store's token may read beyond the defaults. */\nexport interface StoreAccess {\n inventory: boolean;\n}\n/**\n * A query's text, or a function that writes it for what the token may read: Tether's own queries, built from\n * `fragments.ts`, leave out inventory for a token that can't read it. Documents Tether didn't write are strings.\n */\nexport type QueryText = string | ((access: StoreAccess) => string);\n\n/** @returns The Storefront GraphQL endpoint of `conf`'s store. */\nexport function endpoint(conf: Pick<TetherConfig, 'domain' | 'apiVersion'> = cfg): string {\n return `https://${conf.domain}/api/${conf.apiVersion}/graphql.json`;\n}\n\n/** GraphQL strings and `#` comments: text in them is never part of an operation's header. */\nconst STRINGS_AND_COMMENTS = /\"(?:\\\\.|[^\"\\\\\\n])*\"|#[^\\n]*/g;\n/** An operation's keyword, name and variable definitions: named (query Name($x: T)) and anonymous (query { \u2026 }). */\nconst OPERATION_START = /^\\s*(?:query|mutation)(?:\\s+\\w+)?\\s*(?:\\([^)]*\\))?/;\n\n/**\n * The operation of a document Tether didn't write (`Tether.query()`, the server's `query()`). Outside strings and\n * comments, any `mutation` keyword makes it a mutation: a document that starts with a fragment still has its\n * operation found, and a misread errs on the side that is never sent twice.\n */\nexport function operationOf(document: string): Operation {\n return /\\bmutation\\b/.test(document.replace(STRINGS_AND_COMMENTS, '')) ? 'mutation' : 'query';\n}\n\n/**\n * Adds @inContext(country, language) to the first operation in the document, so prices, currency and translations\n * follow the market. The header is found in a copy of the document whose strings and comments are blanked out, so a\n * `)` in a default value or an `@inContext` in a comment or the selection set doesn't move or stop it; an operation\n * that names its own `@inContext` keeps it. Tether's own documents start with their operation, and so do the ones\n * `Tether.query()` is given in practice: a document that starts with a fragment is sent as written.\n */\nfunction inContext(\n query: string,\n { country, language }: { country: CountryCode | undefined; language: LanguageCode | undefined },\n): string {\n const args: string[] = [];\n if (country) args.push(`country: ${country}`);\n if (language) args.push(`language: ${language}`);\n if (!args.length) return query;\n const tag = `@inContext(${args.join(', ')})`;\n const blanked = query.replace(STRINGS_AND_COMMENTS, (text) => ' '.repeat(text.length));\n const selectionStart = blanked.indexOf('{');\n if (blanked.slice(0, selectionStart).includes('@inContext')) return query;\n // Shorthand: a document that starts with its selection set is a query without a keyword.\n if (blanked.trimStart().startsWith('{')) return `query ${tag} ${query.slice(selectionStart)}`;\n const header = OPERATION_START.exec(blanked);\n if (!header) return query;\n const end = header[0].length;\n return `${query.slice(0, end).trimEnd()} ${tag} ${query.slice(end).trimStart()}`;\n}\n\n/**\n * `inContext` for any country and language, as the public `Tether.withContext` takes them: one that isn't a valid\n * code is left out, so page input never becomes query text.\n */\nexport function withContext(\n query: string,\n ctx: { country?: string | undefined; language?: string | undefined } = cfg,\n): string {\n const country = toCountry(ctx.country) ?? undefined;\n const language = toLanguage(ctx.language) ?? undefined;\n return inContext(query, { country, language });\n}\n\n/** Backoff after a network error, a 429 or a 5xx: this times 2^attempt. */\nconst RETRY_BACKOFF_MS = 300;\n/** Backoff after Shopify's `THROTTLED` error, which asks for a longer wait: this times 2^attempt. */\nconst THROTTLE_BACKOFF_MS = 500;\n/** Shopify's status for a request its bot protection rejected; retrying doesn't help. */\nconst SECURITY_REJECTION = 430;\nconst MS_PER_SECOND = 1000;\n\n/**\n * `quantityAvailable` needs the token's `unauthenticated_read_product_inventory` scope; without it Shopify refuses the\n * whole query with an error naming the field. That refusal is learned once per page and store: Tether's later queries\n * to the store are written without the field (`StoreAccess`), so stock levels are unknown and everything else works,\n * for one extra request.\n */\nconst INVENTORY_FIELD = 'quantityAvailable';\n\n/** The stores (domain, version and token) whose token can't read inventory. */\nconst storesWithoutInventory = new Set<string>();\n\nconst storeOf = (conf: StorefrontConfig) => `${conf.domain} ${conf.apiVersion} ${conf.token}`;\n\n/** @returns The text sent: with the market, and the cache's time bucket for a query. */\nfunction queryText(\n conf: StorefrontConfig,\n query: QueryText,\n access: StoreAccess,\n { context, operation }: Required<RequestOptions>,\n): string {\n const written = typeof query === 'string' ? query : query(access);\n const text = context ? inContext(written, conf) : written;\n if (conf.maxAge <= 0 || operation === 'mutation') return text;\n return `${text} #${Math.floor(Date.now() / (conf.maxAge * MS_PER_SECOND))}`;\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** What a status means, for errors Shopify sends without a message (401/403 have none). */\nfunction statusText(status: number, conf: StorefrontConfig): string {\n if (status === 401 || status === 403) {\n const adminHint = conf.token.startsWith('shpat_') ? ', not an Admin API token (shpat_\u2026)' : '';\n return `Shopify rejected the Storefront token for ${conf.domain} (${status}): use the public Storefront API token (Headless channel)${adminHint}`;\n }\n if (status === 404) {\n return `Store or API version not found (404): check domain ${conf.domain} and apiVersion ${conf.apiVersion}`;\n }\n return `HTTP ${status} from ${conf.domain}`;\n}\n\n/**\n * @param value One entry of a response's `errors`.\n * @returns The error, with an empty message when it has none.\n */\nfunction toGraphQLError(value: unknown): GraphQLError {\n const message = field(value, 'message');\n const code = field(field(value, 'extensions'), 'code');\n const text = typeof message === 'string' ? message : '';\n return typeof code === 'string' ? { message: text, extensions: { code } } : { message: text };\n}\n\n/**\n * @param json A response body.\n * @returns The body in the shape the GraphQL spec sets, `null` when it has another one.\n */\nfunction toGraphQLResponse(json: unknown): GraphQLResponse | null {\n if (!isRecord(json)) return null;\n const { data, errors } = json;\n if (errors === undefined || errors === null) return { data };\n if (!Array.isArray(errors)) return null;\n return { data, errors: errors.map(toGraphQLError) };\n}\n\n/**\n * For a body that isn't JSON, isn't in the GraphQL shape or has `data` its caller's check refuses: `INVALID_RESPONSE`\n * for a 2xx, after which Shopify may have run a mutation, else `HTTP_<status>`.\n */\nfunction invalidResponse(res: Response, conf: StorefrontConfig): TetherError {\n return res.ok\n ? new TetherError(`Invalid response (${res.status})`, { code: 'INVALID_RESPONSE', status: res.status })\n : new TetherError(statusText(res.status, conf), { status: res.status });\n}\n\nfunction graphQLFailure(\n res: Response,\n errors: GraphQLError[],\n conf: StorefrontConfig,\n code?: TetherErrorCode,\n): TetherError {\n const message = errors\n .map((error) => error.message)\n .filter(Boolean)\n .join(' | ');\n let text = message || 'Storefront API error';\n if (!res.ok) text = res.status === 401 || res.status === 403 || !message ? statusText(res.status, conf) : message;\n return new TetherError(text, { status: res.status, errors, code });\n}\n\ntype Attempt<T> =\n { kind: 'data'; data: T } | { kind: 'retry'; backoffMs: number } | { kind: 'inventory-refused'; error: TetherError };\n\n/**\n * Which failures an attempt may answer with a retry instead of throwing: none on the last attempt, only Shopify's\n * \"not run\" answers for a mutation (`Operation`).\n */\ntype RetryPolicy = 'none' | 'not-run' | 'any';\n\n/** No answer arrived, or its body stopped arriving: retried when `retry` allows any retry, else `NETWORK`. */\nfunction networkFailure(error: unknown, retry: RetryPolicy): Attempt<never> {\n if (retry === 'any') return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n throw new TetherError(`Network error: ${messageOf(error)}`, { code: 'NETWORK' });\n}\n\n/**\n * What an answer with GraphQL errors means: an inventory refusal, a retry `retry` allows, or else the failure it\n * throws. Shopify answers a failure on its own side with a 200 and a top-level `INTERNAL_SERVER_ERROR`, which means\n * what a 5xx does; a 2xx mutation answer with errors and no `data` doesn't say whether Shopify applied it either.\n * Both are `SERVER_ERROR`.\n */\nfunction graphQLOutcome(\n res: Response,\n conf: StorefrontConfig,\n query: string,\n errors: GraphQLError[],\n data: unknown,\n retry: RetryPolicy,\n operation: Operation,\n): Attempt<never> {\n if (query.includes(INVENTORY_FIELD) && errors.some((error) => error.message.includes(INVENTORY_FIELD))) {\n return { kind: 'inventory-refused', error: graphQLFailure(res, errors, conf) };\n }\n const codes = errors.map((error) => error.extensions?.code);\n if (retry !== 'none' && codes.includes('THROTTLED')) return { kind: 'retry', backoffMs: THROTTLE_BACKOFF_MS };\n const serverError = codes.includes('INTERNAL_SERVER_ERROR');\n if (retry === 'any' && serverError) return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n const unclear = res.ok && (serverError || (operation === 'mutation' && data == null));\n throw graphQLFailure(res, errors, conf, unclear ? 'SERVER_ERROR' : undefined);\n}\n\n/** One request. Failures that retrying can't fix, or that `retry` doesn't allow retrying, throw. */\nasync function attempt<T>(\n conf: StorefrontConfig,\n query: string,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n retry: RetryPolicy,\n operation: Operation,\n): Promise<Attempt<T>> {\n let res;\n try {\n res = await (conf.fetch ?? fetch)(endpoint(conf), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Shopify-Storefront-Access-Token': conf.token },\n body: JSON.stringify({ query, variables }),\n });\n } catch (error) {\n return networkFailure(error, retry);\n }\n if (res.status === SECURITY_REJECTION) {\n throw new TetherError('Shopify security rejection (430)', { status: SECURITY_REJECTION });\n }\n if ((retry !== 'none' && res.status === 429) || (retry === 'any' && res.status >= 500)) {\n return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n // `json()` refuses a body that isn't JSON with a SyntaxError; it rejects with another error when the connection\n // drops while the body streams in.\n if (error instanceof SyntaxError) throw invalidResponse(res, conf);\n return networkFailure(error, retry);\n }\n const body = toGraphQLResponse(json);\n if (!body) throw invalidResponse(res, conf);\n const { data, errors = [] } = body;\n if (errors.length) return graphQLOutcome(res, conf, query, errors, data, retry, operation);\n if (!res.ok) throw new TetherError(statusText(res.status, conf), { status: res.status });\n if (!isData(data)) throw invalidResponse(res, conf);\n return { kind: 'data', data };\n}\n\n/**\n * One Storefront API request, for any config: the browser's shared one (`gql`) or one the server helpers build.\n * Queries carry a time bucket so Shopify's cache of an identical query text is at most `maxAge` seconds old. A query\n * written for the store's access leaves out inventory once the token turns out not to read it (`INVENTORY_FIELD`);\n * a fixed query text is the caller's own, and that refusal is its error, as is a refusal of a text written without\n * inventory, so the refusal costs at most one extra request. How often a failure is retried is\n * `conf.retries`; which failures are retried is the operation's (`Operation`).\n *\n * @param isData Checks that `data` has what the caller then reads, so a malformed answer fails here.\n * @throws {TetherError} `NOT_CONFIGURED` without a domain or token, `NETWORK` when the store can't be reached or\n * the answer's body stops arriving, `INVALID_RESPONSE` for a 2xx whose body isn't valid (not JSON, not in the\n * GraphQL shape, or `data` that `isData` refuses), `SERVER_ERROR` for a 2xx whose GraphQL errors leave open\n * whether Shopify ran it (a top-level `INTERNAL_SERVER_ERROR`, or a mutation's errors without `data`),\n * `HTTP_<status>` for any other HTTP or GraphQL failure.\n */\nexport async function request<T>(\n conf: StorefrontConfig,\n query: QueryText,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n { context = true, operation = 'query' }: RequestOptions = {},\n): Promise<T> {\n if (!conf.domain || !conf.token) {\n throw new TetherError('Tether is not configured: domain and token are required', { code: 'NOT_CONFIGURED' });\n }\n const store = storeOf(conf);\n const retryWhileLeft: RetryPolicy = operation === 'mutation' ? 'not-run' : 'any';\n let retries = 0;\n for (;;) {\n const access = { inventory: !storesWithoutInventory.has(store) };\n const sent = queryText(conf, query, access, { context, operation });\n const retry = retries < conf.retries ? retryWhileLeft : 'none';\n const outcome = await attempt(conf, sent, variables, isData, retry, operation);\n switch (outcome.kind) {\n case 'data':\n return outcome.data;\n case 'inventory-refused':\n if (typeof query === 'string' || !access.inventory) throw outcome.error;\n log(`the token can't read inventory: ${INVENTORY_FIELD} is left out from now on`);\n storesWithoutInventory.add(store);\n break;\n case 'retry':\n retries += 1;\n log('retry', retries);\n await sleep(outcome.backoffMs * 2 ** retries);\n break;\n }\n }\n}\n\n/** `request` with the browser's shared config. */\nexport function gql<T>(\n query: QueryText,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n options?: RequestOptions,\n): Promise<T> {\n return request(cfg, query, variables, isData, options);\n}\n\n/** The check of `Tether.query()` and the server's `query()`, whose callers read `data` themselves. */\nexport const hasData = (data: unknown): data is object => typeof data === 'object' && data !== null;\n\n/** `Tether.query()` and the server's `query()`: a document Tether didn't write, sent with `conf`. */\nexport function untypedQuery<T>(\n conf: StorefrontConfig,\n text: string,\n variables: Record<string, unknown> = {},\n options?: Omit<RequestOptions, 'operation'>,\n): Promise<T> {\n // The caller's own query text decides the shape of `data`, and the caller's `T` states it: nothing checks `data`\n // beyond `hasData`.\n return request(conf, text, variables, hasData, { ...options, operation: operationOf(text) }) as Promise<T>;\n}\n\n/** `Tether.query()`: `untypedQuery` with the browser's shared config. */\nexport function query<T>(\n text: string,\n variables?: Record<string, unknown>,\n options?: Omit<RequestOptions, 'operation'>,\n): Promise<T> {\n return untypedQuery(cfg, text, variables, options);\n}\n\n/**\n * @param payload A mutation's payload.\n * @param name The mutation, for the error message.\n * @throws {TetherError} `ERROR` on an empty payload or on user errors (in `userErrors`).\n */\nexport function unwrap<Payload extends { userErrors?: readonly UserError[] | null }>(\n payload: Payload | null | undefined,\n name: string,\n): Payload {\n if (!payload) throw new TetherError(`${name}: empty response`);\n const userErrors = payload.userErrors ?? [];\n if (userErrors.length) {\n throw new TetherError(userErrors.map((error) => error.message).join(' | '), { userErrors });\n }\n return payload;\n}\n", "/**\n * How Tether finds its markup: attribute names carry the configured prefix (`tether-product`, or `wfs-product` with\n * `prefix: 'wfs'`), and so do the selectors and the DOM events built from them.\n */\nimport { cfg } from './config.ts';\nimport type { ShopifyCart } from './cart/store.ts';\nimport type { CartLineInput } from './cart/mutations.ts';\nimport type { CartWarning } from './config.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { Collection } from './loader.ts';\nimport type { ProductScope } from './product.ts';\nimport type { Purchase } from './product-state.ts';\nimport type { SearchResults } from './search.ts';\n\n/** @returns The prefixed attribute name: `product` \u2192 `tether-product`. */\nexport const attr = (name: string): string => `${cfg.prefix}-${name}`;\n\n/** @returns A selector for the attribute, with that exact value when given. */\nexport const sel = (name: string, value?: string): string =>\n value === undefined ? `[${attr(name)}]` : `[${attr(name)}=\"${value}\"]`;\n\nexport const get = (el: Element, name: string): string | null => el.getAttribute(attr(name));\n\nexport const has = (el: Element, name: string): boolean => el.hasAttribute(attr(name));\n\n/**\n * Tether's attributes go on HTML elements, so matches are typed as such. An SVG element that carries one still has\n * the `style`, `dataset` and attribute methods Tether uses.\n *\n * @returns The matches in `root`, none without a root.\n */\nexport function qsa(root: ParentNode | null | undefined, selector: string): HTMLElement[] {\n if (!root) return [];\n return [...root.querySelectorAll<HTMLElement>(selector)];\n}\n\n/** @returns Matches in `root` whose nearest `scopeSelector` ancestor is `root` itself, not a nested scope. */\nexport const scoped = (root: Element | null | undefined, selector: string, scopeSelector: string): HTMLElement[] =>\n qsa(root, selector).filter((el) => el.closest(scopeSelector) === root);\n\n/**\n * The events Tether dispatches, as `<prefix>:<name>` (`tether:cart-updated`), bubbling up to `document`, with the\n * detail each carries. `emit` takes only these, and `Tether.on()` passes on only the events `emit` dispatched, so a listener gets the\n * detail its event really has.\n */\nexport interface TetherEvents {\n ready: { cart: ShopifyCart | null };\n 'product-ready': { product: Product; scope: ProductScope };\n 'product-updated': { product: Product; scope: ProductScope };\n /** `key` is null when the scope names no product; `source` is what it named. */\n 'product-not-found': { key: string | null; source: string };\n 'variant-changed': {\n product: Product;\n variant: ProductVariant;\n selected: Record<string, string>;\n purchase: Purchase['kind'];\n sellingPlanId: string | null;\n };\n 'product-added': { product: Product; variant: ProductVariant; line: CartLineInput };\n added: { cart: ShopifyCart; lines: CartLineInput[] };\n 'group-added': { group: string | null; lines: CartLineInput[] };\n 'cart-warning': { warnings: readonly CartWarning[] };\n 'cart-updated': { cart: ShopifyCart | null; reason: string };\n 'cart-toggled': { open: boolean };\n 'cart-error': { error: unknown };\n checkout: { url: string; cart: ShopifyCart | null };\n 'list-ready': { products: Product[]; collection: Collection | null };\n /** A list's cards were drawn (again): `container` holds them. */\n 'list-rendered': { container: Element };\n /** The search part's suggestions for `term`. */\n search: SearchResults & { term: string };\n 'country-changed': { country: string; language: string | undefined };\n 'discount-applied': { code: string };\n 'discount-invalid': { code: string };\n 'layout-changed': { reason: string };\n error: { error: unknown; context: string; key?: string | null };\n 'account-login': Record<string, never>;\n 'account-logout': Record<string, never>;\n /** A login that failed: `expired` (it took too long, or came back to another browser) or `error`. */\n 'account-error': { error: 'expired' | 'error' };\n 'account-updated': { what: 'profile' | 'addresses' | 'subscriptions' | 'orders' };\n}\n\n/** The events the analytics part reports, which the core buffers from `init()` until the part has loaded. */\nexport const ANALYTICS_EVENTS = [\n 'product-ready',\n 'list-ready',\n 'cart-updated',\n 'cart-toggled',\n 'checkout',\n 'search',\n] as const satisfies readonly (keyof TetherEvents)[];\n\nexport type AnalyticsEventName = (typeof ANALYTICS_EVENTS)[number];\n\n/**\n * The events Tether created, with the name each was created under. Any script can dispatch a `<prefix>:<name>` event\n * with any detail; only an event in here carries the detail `TetherEvents` gives its name.\n */\nconst ownEvents = new WeakMap<Event, keyof TetherEvents>();\n\n/** @returns The bubbling event `<prefix>:<name>`, known to `isOwnEvent`. */\nexport function ownEvent<Name extends keyof TetherEvents>(\n name: Name,\n detail: TetherEvents[Name],\n): CustomEvent<TetherEvents[Name]> {\n const event = new CustomEvent(`${cfg.prefix}:${name}`, { detail, bubbles: true });\n ownEvents.set(event, name);\n return event;\n}\n\n/** @returns True for an event `ownEvent` created as `name`, false for any other, such as one another script dispatched. */\nexport function isOwnEvent<Name extends keyof TetherEvents>(\n event: Event,\n name: Name,\n): event is CustomEvent<TetherEvents[Name]> {\n return ownEvents.get(event) === name;\n}\n\n/** Dispatches the bubbling event `<prefix>:<name>`. */\nexport function emit<Name extends keyof TetherEvents>(\n name: Name,\n detail: TetherEvents[Name],\n target: EventTarget = document,\n): void {\n target.dispatchEvent(ownEvent(name, detail));\n}\n", "/**\n * HTML written in the Shopify admin (product and collection descriptions), made safe to insert into the site. Store\n * staff and apps that can edit products are not trusted to act on the storefront, where customer sessions live. Only\n * elements and attributes that show content are kept (an allow-list, so markup nobody reviewed is never let in):\n * text formatting, lists, tables, images, links and video embeds look as written, inline `style` included, because\n * the Shopify editor writes its formatting there. Script, page-wide `<style>` and forms (which could post the\n * visitor's input anywhere) are removed, and embeds run sandboxed.\n *\n * A description stays where it is written, so it can't be laid over the page as a fake login (a phishing form on the\n * shop's own origin). Removed for that: `id` (it would clobber `window.<id>` and the site's own ids); `class`, which\n * would let a description borrow the site's own overlay or modal classes; and every style that takes content out of\n * the flow (`position`, `inset`, `z-index`), moves it from where the flow put it (transforms, motion paths, negative\n * margins, and negative indents and spacing beyond the small values that tighten text, `NEGATIVE_LIMITS`), copies it\n * elsewhere (reflections) or sizes it to the window (viewport and container-query units).\n * Outlines, shadows, filters, border-image and mask-border outsets, text decoration and text stroke paint beyond their\n * element, so they are kept only while every number they give is a pixel length of a few dozen pixels at most\n * (`MAX_PAINT_PX`): enough to set off an image or a callout, too little to cover the page.\n * Styles that fetch anything go too.\n *\n * A frame shows whatever its origin serves, sized as the description likes, so a frame from a page store staff can\n * write would be that fake login. Frames are kept only from the video players in `EMBED_ORIGINS` and the origins the\n * site adds (`embedOrigins`), never from the site's own origin, where a sandbox can be lifted from inside. What an\n * allowed frame shows is up to its host: the host's player, and whatever that player lets its users add (a call to\n * action, an email gate). An origin a site adds is trusted the same way.\n *\n * Not prevented: content wider than the description (a table or embed on a narrow screen, or a box given a large\n * width in pixels) overflows beside it, as in any HTML, and a capped shadow or outline still paints just past it. Tether\n * doesn't clip the element it fills (`contain: paint`, `overflow`), because that would cut off those wide tables and\n * embeds, stop margins collapsing through it and make it contain floats, changing how ordinary descriptions look. A\n * site that wants the clip sets it on that element in its own CSS.\n */\n\nconst HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n\n/** The elements kept, by local name. Any other element is replaced by its content, which is cleaned the same way. */\nconst ALLOWED_ELEMENTS = new Set([\n ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div', 'br', 'hr', 'blockquote', 'code', 'pre'],\n ...['strong', 'em', 'b', 'i', 'u', 's', 'small', 'sub', 'sup'],\n ...['ul', 'ol', 'li', 'dl', 'dt', 'dd'],\n ...['table', 'caption', 'colgroup', 'col', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td'],\n ...['a', 'img', 'picture', 'source', 'video', 'figure', 'figcaption', 'iframe'],\n]);\n/** Elements whose content is code, styles or form state rather than text to show: removed with their content. */\nconst REMOVED_WITH_CONTENT = new Set(['script', 'style', 'template', 'noscript', 'textarea', 'select', 'title']);\n\n/** Attributes any kept element may have, and every `aria-*` one (`isAllowedAttribute`). */\nconst GLOBAL_ATTRIBUTES = new Set(['style', 'title', 'lang', 'dir', 'role', 'align', 'width', 'height']);\n/** Further attributes per element. */\nconst ELEMENT_ATTRIBUTES = new Map([\n ['a', new Set(['href', 'target', 'rel', 'hreflang'])],\n ['img', new Set(['src', 'srcset', 'sizes', 'alt', 'loading', 'decoding'])],\n ['source', new Set(['src', 'srcset', 'sizes', 'type', 'media'])],\n ['video', new Set(['src', 'poster', 'controls', 'autoplay', 'muted', 'loop', 'playsinline', 'preload'])],\n ['iframe', new Set(['src', 'allow', 'allowfullscreen', 'frameborder', 'loading'])],\n ['blockquote', new Set(['cite'])],\n ['ol', new Set(['start', 'type', 'reversed'])],\n ['li', new Set(['value'])],\n ['table', new Set(['border', 'cellpadding', 'cellspacing', 'summary'])],\n ['col', new Set(['span'])],\n ['colgroup', new Set(['span'])],\n ['th', new Set(['colspan', 'rowspan', 'headers', 'scope', 'valign'])],\n ['td', new Set(['colspan', 'rowspan', 'headers', 'valign'])],\n]);\n/** Attributes whose value is fetched or navigated to. */\nconst URL_ATTRIBUTES = new Set(['href', 'src', 'poster', 'cite']);\n/** Schemes that run code when navigated to. */\nconst SCRIPT_URL = /^(javascript|vbscript|data):/i;\n/** `data:` stays allowed for images, which only decode pixels. */\nconst DATA_IMAGE = /^data:image\\/(png|gif|jpeg|webp|avif);/i;\n\n/**\n * Style properties that take content out of the flow or move it from where the flow put it. `inset` and its logical\n * forms, the transform properties and the motion path (`offset-*`) are matched by prefix (`isOutOfPlace`). A\n * reflection (`-webkit-box-reflect`) paints a copy of the element beside it, as far away as the element is large.\n */\nconst OUT_OF_PLACE_PROPERTIES = new Set([\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'z-index',\n '-webkit-box-reflect',\n]);\nconst OUT_OF_PLACE_PREFIXES = ['inset', 'transform', 'translate', 'rotate', 'scale', 'offset'];\n/**\n * A length in viewport units (`100vw`, `50dvh`, `10vmax`) or container-query units (`100cqw`), which sizes content to\n * the window, not the description: outside a size container, a container-query unit is a small-viewport unit.\n */\nconst VIEWPORT_LENGTH = /\\d(?:[sld]?v|cq)(?:w|h|i|b|min|max)\\b/i;\n/**\n * Style properties, without a `-webkit-` prefix, that paint beyond their element's box by as far as their numbers\n * say: outlines and their offset, shadows, `filter` (`drop-shadow()`, `blur()`), text decoration and its offset, text\n * stroke, and the outset of a border image or mask border, which a unitless number multiplies by the border width.\n * Browsers list a shorthand's longhands; the border image and mask border shorthands are checked whole where a\n * parser keeps them.\n */\nconst PAINTS_BEYOND =\n /^(?:outline|box-shadow|text-shadow|filter|text-decoration|text-underline-offset|text-stroke)|^(?:border-image|mask-border|mask-box-image)(?:-outset)?$/;\n/** The longest length, in pixels, those properties may give. */\nconst MAX_PAINT_PX = 40;\n/**\n * Properties on which a small negative value only tightens text, with the most a negative value may be in each unit;\n * one in another unit, or beyond the limit, is removed. Spacing stops at -0.25em (-4px at a 16px font), less than the\n * advance of a letter or a space, so text tightens without running back over what precedes it. An indent stops at\n * `MAX_PAINT_PX`, the reach allowed to anything that paints beyond its element.\n */\nconst NEGATIVE_LIMITS = new Map<string, Partial<Record<string, number>>>([\n ['letter-spacing', { px: 4, em: 0.25 }],\n ['word-spacing', { px: 4, em: 0.25 }],\n ['text-indent', { px: MAX_PAINT_PX }],\n]);\n/**\n * A number with its unit, if any, captured (`5000px`, `1.5em`, `5e3px`, `100`, `5000%`). Digits inside a word or a\n * hex colour (`#5000ff`) are not one.\n */\nconst NUMBER = /(?<![\\w#.-])[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?(%|[a-z]+)?/gi;\n/**\n * A function other than `drop-shadow()` and `blur()`, with no function inside it: colours, a filter's colour and\n * opacity changes and a border image's gradient, whose numbers paint nothing beyond the box. They are left out before\n * the numbers are checked.\n */\nconst NON_LENGTH_FUNCTION = /(?<![\\w-])(?!drop-shadow\\(|blur\\()[\\w-]+\\([^()]*\\)/g;\n/**\n * Values whose lengths depend on more than the declaration (the page's custom properties, arithmetic, the\n * environment), so they can't be checked against `MAX_PAINT_PX`.\n */\nconst COMPUTED_VALUE = /(?:var|calc|min|max|clamp|env|attr)\\(/i;\n/** Style values that fetch a resource or run code (`expression()` in old Internet Explorer). */\nconst FETCHING_VALUE = /url\\(|expression\\(|@import/i;\n\n/** Browsers ignore control characters and spaces in a URL's scheme (`java^Iscript:`), so the check does too. */\n// eslint-disable-next-line no-control-regex -- control characters are exactly what browsers skip in a scheme\nconst IGNORED_IN_SCHEME = /[\\u0000- ]/g;\n\n/**\n * An embed runs its own player script on its own origin, but can't navigate this page, open dialogs over it or submit\n * forms. `allow-same-origin` keeps the player's own storage and cookies, which YouTube and Vimeo need.\n */\nconst IFRAME_SANDBOX = 'allow-scripts allow-same-origin allow-popups allow-presentation';\n/**\n * The permissions an embed may ask the page to delegate: the ones video players and social embeds use. Camera,\n * microphone, location, payment and the rest are never delegated to store content.\n */\nconst IFRAME_FEATURES = new Set([\n ...['autoplay', 'encrypted-media', 'picture-in-picture', 'fullscreen', 'clipboard-write'],\n ...['accelerometer', 'gyroscope', 'web-share'],\n]);\n/** Players check which site embeds them (YouTube refuses to play without it): the origin is sent, never the path. */\nconst IFRAME_REFERRER_POLICY = 'strict-origin-when-cross-origin';\n/**\n * The video hosts product descriptions embed, by the origin of their embed code: YouTube (and its cookie-less\n * domain), Vimeo, Loom and Wistia. Store staff choose the video these hosts play, not the page around it.\n */\nconst EMBED_ORIGINS = [\n 'https://www.youtube.com',\n 'https://www.youtube-nocookie.com',\n 'https://player.vimeo.com',\n 'https://www.loom.com',\n 'https://fast.wistia.net',\n];\n\n/** @param name An attribute name, lower case. */\nfunction isAllowedAttribute(tag: string, name: string): boolean {\n return GLOBAL_ATTRIBUTES.has(name) || name.startsWith('aria-') || (ELEMENT_ATTRIBUTES.get(tag)?.has(name) ?? false);\n}\n\n/** @returns False for a URL that would run script. */\nfunction isSafeUrl(tag: string, name: string, value: string): boolean {\n const url = value.replace(IGNORED_IN_SCHEME, '');\n if (!SCRIPT_URL.test(url)) return true;\n return tag === 'img' && name === 'src' && DATA_IMAGE.test(url);\n}\n\n/** @returns Null for text that is not a URL, which no check below lets through. */\nfunction urlOrNull(value: string, base?: string): URL | null {\n try {\n return new URL(value, base);\n } catch {\n return null;\n }\n}\n\n/** What cleaning needs to know about the page the fragment is for. */\ninterface Page {\n /** The page's origin, `null` when it has none (`about:blank`). */\n origin: string | null;\n /** Relative URLs resolve against it, as they will once the fragment is inserted. */\n baseUrl: string;\n /** An element whose `style` parses declarations the way the browser applies them. */\n styleParser: HTMLElement;\n /** The origins an iframe may load from. */\n embedOrigins: ReadonlySet<string>;\n}\n\nfunction pageOf(document: Document, styleParser: HTMLElement, embedOrigins: readonly string[]): Page {\n const baseUrl = document.baseURI;\n const origin = urlOrNull(baseUrl)?.origin ?? 'null';\n return {\n origin: origin === 'null' ? null : origin,\n baseUrl,\n styleParser,\n embedOrigins: new Set([...EMBED_ORIGINS, ...embedOrigins]),\n };\n}\n\n/**\n * @param value An iframe's `src`.\n * @returns True for a URL on an allowed embed origin other than the page's: any other embed is removed.\n */\nfunction isAllowedEmbed(value: string, page: Page): boolean {\n const url = urlOrNull(value.trim(), page.baseUrl);\n return url !== null && url.origin !== page.origin && page.embedOrigins.has(url.origin);\n}\n\n/**\n * @param value An iframe's `allow` attribute: features separated by `;`, each with an optional allowlist.\n * @returns The features from `IFRAME_FEATURES` alone, each for the embed's own origin.\n */\nfunction allowedFeatures(value: string): string {\n const features = value.split(';').map((directive) => directive.trim().split(/\\s+/)[0]?.toLowerCase() ?? '');\n return [...new Set(features.filter((feature) => IFRAME_FEATURES.has(feature)))].join('; ');\n}\n\n/**\n * @param property A style property name, lower case.\n * @returns True for a declaration that would put content anywhere but where the description's flow puts it.\n */\nfunction isOutOfPlace(property: string, value: string): boolean {\n if (OUT_OF_PLACE_PROPERTIES.has(property) || OUT_OF_PLACE_PREFIXES.some((prefix) => property.startsWith(prefix))) {\n return true;\n }\n if (VIEWPORT_LENGTH.test(value)) return true;\n const limits = NEGATIVE_LIMITS.get(property);\n if (limits) return COMPUTED_VALUE.test(value) || movesBackTooFar(value, limits);\n // A negative margin moves content back over what comes before. A minus sign is a negative length, or a `calc()`\n // that can be one.\n return property.startsWith('margin') && value.includes('-');\n}\n\n/** @param limits The most a negative number may be, by its unit (`NEGATIVE_LIMITS`). */\nfunction movesBackTooFar(value: string, limits: Partial<Record<string, number>>): boolean {\n return [...value.matchAll(NUMBER)].some(([number = '', unit = '']) => {\n const amount = parseFloat(number);\n if (amount >= 0) return false;\n const limit = limits[unit.toLowerCase()];\n return limit === undefined || -amount > limit;\n });\n}\n\n/**\n * @param property A style property name, lower case.\n * @returns True for a declaration that would paint farther beyond its element than `MAX_PAINT_PX`. Only pixel\n * lengths can be checked: any other non-zero number counts as too far, whether it has another unit (`em` grows with\n * the font size, which a description sets too), a percentage or none (an outset's multiple of the border width).\n */\nfunction paintsTooFar(property: string, value: string): boolean {\n if (!PAINTS_BEYOND.test(property.replace(/^-webkit-/, ''))) return false;\n if (COMPUTED_VALUE.test(value)) return true;\n return [...value.replace(NON_LENGTH_FUNCTION, '').matchAll(NUMBER)].some(([number = '', unit = '']) => {\n const amount = Math.abs(parseFloat(number));\n return amount !== 0 && (unit.toLowerCase() !== 'px' || amount > MAX_PAINT_PX);\n });\n}\n\n/**\n * An inline style as the browser's own parser reads it, without out-of-place, far-painting or fetching declarations. The parsed\n * form is written back, so text the parser skipped (an unknown or malformed declaration) is gone too.\n */\nfunction cleanStyle(value: string, styleParser: HTMLElement): string {\n const style = styleParser.style;\n style.cssText = value;\n const properties = Array.from({ length: style.length }, (_, index) => style.item(index));\n for (const property of properties) {\n const value = style.getPropertyValue(property);\n const remove = isOutOfPlace(property, value) || paintsTooFar(property, value) || FETCHING_VALUE.test(value);\n if (remove) style.removeProperty(property);\n }\n const cleaned = style.cssText;\n styleParser.removeAttribute('style');\n return cleaned;\n}\n\n/** @param element An allowed element: its other attributes are removed. */\nfunction cleanAttributes(element: Element, page: Page): void {\n const tag = element.localName;\n for (const attribute of [...element.attributes]) {\n const name = attribute.name.toLowerCase();\n const isUrl = URL_ATTRIBUTES.has(name);\n const keep = isAllowedAttribute(tag, name) && (!isUrl || isSafeUrl(tag, name, attribute.value));\n if (!keep) element.removeAttribute(attribute.name);\n }\n const style = element.getAttribute('style');\n if (style === null) return;\n const cleaned = cleanStyle(style, page.styleParser);\n if (cleaned === '') element.removeAttribute('style');\n else element.setAttribute('style', cleaned);\n}\n\n/** @param element A kept element, its attributes cleaned. */\nfunction hardenEmbedsAndLinks(element: Element): void {\n if (element.localName === 'iframe') {\n const allow = allowedFeatures(element.getAttribute('allow') ?? '');\n if (allow === '') element.removeAttribute('allow');\n else element.setAttribute('allow', allow);\n element.setAttribute('sandbox', IFRAME_SANDBOX);\n element.setAttribute('referrerpolicy', IFRAME_REFERRER_POLICY);\n return;\n }\n if (element.localName !== 'a' || element.getAttribute('target')?.toLowerCase() !== '_blank') return;\n // The opened page gets no handle on this one (`window.opener`) and isn't told which page linked to it.\n const rel = new Set((element.getAttribute('rel') ?? '').split(/\\s+/).filter(Boolean));\n rel.add('noopener');\n rel.add('noreferrer');\n element.setAttribute('rel', [...rel].join(' '));\n}\n\nfunction cleanElement(element: Element, page: Page): void {\n const tag = element.localName;\n const isHtml = element.namespaceURI === HTML_NAMESPACE;\n if (isHtml && REMOVED_WITH_CONTENT.has(tag)) {\n element.remove();\n return;\n }\n if (!isHtml || !ALLOWED_ELEMENTS.has(tag)) {\n element.replaceWith(...element.childNodes);\n return;\n }\n if (tag === 'iframe' && !isAllowedEmbed(element.getAttribute('src') ?? '', page)) {\n element.remove();\n return;\n }\n cleanAttributes(element, page);\n hardenEmbedsAndLinks(element);\n}\n\n/**\n * Store HTML as a fragment of content elements only.\n *\n * @param document The document the fragment is for.\n * @param embedOrigins Origins iframes may load from besides the video players in `EMBED_ORIGINS`: the config's\n * `embedOrigins`, already checked to be https origins.\n */\nexport function storeHtmlFragment(\n html: string | null | undefined,\n document: Document = globalThis.document,\n embedOrigins: readonly string[] = [],\n): DocumentFragment {\n const template = document.createElement('template');\n // A <template>'s content is parsed inert: nothing in it loads or runs before it is cleaned.\n // eslint-disable-next-line no-restricted-syntax -- parsing into an inert template is the sanctioned way in\n template.innerHTML = html ?? '';\n // The template itself is never inserted, so its own `style` is a free parser for the content's declarations.\n const page = pageOf(document, template, embedOrigins);\n // Document order: a replaced element's content moves up and is visited after it; what a removed one held leaves\n // the fragment with it.\n for (const element of [...template.content.querySelectorAll('*')]) cleanElement(element, page);\n return template.content;\n}\n", "/**\n * Conversions of Shopify values that the browser script and the server helpers (`src/server.ts`, `src/astro.ts`)\n * share, so a server-rendered page and the script that takes it over agree. Pure: no config, no DOM.\n */\n\n/** A Storefront API `MoneyV2`. */\nexport interface Money {\n amount: string;\n currencyCode: string;\n}\n\n/** The `.env` names a store's domain and public Storefront token are read from, first match wins. */\nexport const STORE_ENV_NAMES = {\n domain: ['PUBLIC_SHOPIFY_DOMAIN', 'PUBLIC_SHOPIFY_STORE_DOMAIN', 'SHOPIFY_DOMAIN'],\n token: ['PUBLIC_SHOPIFY_STOREFRONT_TOKEN', 'PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN', 'SHOPIFY_STOREFRONT_TOKEN'],\n} as const;\n\n/**\n * True for an absolute https URL. Links and redirects that come from Shopify (checkout, status pages,\n * tracking) are only used when this holds.\n */\nexport function isHttpsUrl(url: unknown): url is string {\n if (typeof url !== 'string') return false;\n try {\n return new URL(url).protocol === 'https:';\n } catch {\n // Not an absolute URL, so not an https one.\n return false;\n }\n}\n\n/**\n * True for a token Shopify issues as a secret (`shpat_` Admin, `shpss_` shared secret, `shpca_` client credentials,\n * `shppa_` private app, `shpua_` user). The public Storefront token has no such prefix. A secret token must never\n * reach a page.\n */\nexport function isSecretShopifyToken(token: string): boolean {\n return /^shp[a-z]{2}_/.test(token.trim());\n}\n\n/**\n * Money as text in `locale`, e.g. `{ amount: '30.0', currencyCode: 'EUR' }` in `nl-NL` \u2192 `\u20AC 30,00`.\n *\n * @param locale An `Intl` locale; the runtime's own when absent.\n * @returns `''` without money or with an amount that isn't a number.\n */\nexport function formatMoney(money: Money | null | undefined, locale: string | undefined): string {\n if (!money) return '';\n const amount = parseFloat(money.amount);\n if (Number.isNaN(amount)) return '';\n try {\n return new Intl.NumberFormat(locale || undefined, { style: 'currency', currency: money.currencyCode }).format(\n amount,\n );\n } catch {\n // A currency code Intl doesn't know.\n return `${money.currencyCode} ${amount.toFixed(2)}`;\n }\n}\n\n/**\n * A product page's path. `productPath` is a folder (`/product` \u2192 `/product/<handle>`) or a template with `{handle}`\n * and `{id}` (the numeric product id), e.g. `/product?handle={handle}`.\n *\n * @param id A product gid or numeric id.\n * @returns `null` when the path needs a handle or an id the product lacks: no link beats a wrong one.\n */\nexport function productUrl(productPath: string, handle: string | null | undefined, id?: string | null): string | null {\n if (!/\\{(handle|id)\\}/.test(productPath)) {\n return handle ? `${productPath.replace(/\\/$/, '')}/${encodeURIComponent(handle)}` : null;\n }\n const numericId = id ? (id.split('/').at(-1) ?? '') : '';\n if (productPath.includes('{handle}') && !handle) return null;\n if (productPath.includes('{id}') && !numericId) return null;\n return productPath\n .replace(/\\{handle\\}/g, encodeURIComponent(handle ?? ''))\n .replace(/\\{id\\}/g, encodeURIComponent(numericId));\n}\n\n/**\n * The key Tether loads a product by: a handle, or a product gid.\n *\n * @param raw A handle, a numeric id, a gid or a product URL (`\u2026/products/<handle>`).\n */\nexport function productKey(raw: string | number | null | undefined): string | null {\n const value = String(raw ?? '').trim();\n if (!value) return null;\n if (value.startsWith('gid://')) return value;\n if (/^\\d+$/.test(value)) return `gid://shopify/Product/${value}`;\n return /\\/products\\/([^/?#]+)/.exec(value)?.[1] ?? value;\n}\n", "/**\n * Shopify images on the page: sized through the CDN's URL parameters, written to an `<img>` or as a CSS background,\n * and never downloaded twice when the server already rendered the same image.\n */\nimport { attr } from './markup.ts';\n\n/** Shopify CDN parameters. */\nexport interface ImageSize {\n width?: string | number | null;\n height?: string | number | null;\n /** `center`, `top`, `bottom`, `left` or `right`; only applies with a width or height. */\n crop?: string | null;\n}\n\n/**\n * Shopify CDN image at a size: `imageUrl(url, { width: 800 })`. Use the same width as the element's max-width\n * attribute, so the browser script keeps the image instead of loading it again.\n *\n * @returns The sized URL; `url` itself when it is empty or not absolute.\n */\nexport function imageUrl<Url extends string | null | undefined>(\n url: Url,\n { width, height, crop }: ImageSize = {},\n): Url {\n if (!url) return url;\n let parsed;\n try {\n parsed = new URL(url);\n } catch (error) {\n // A relative or malformed URL has no CDN parameters to set: it is used as written.\n if (error instanceof TypeError) return url;\n throw error;\n }\n if (width) parsed.searchParams.set('width', String(width));\n if (height) parsed.searchParams.set('height', String(height));\n if (crop) parsed.searchParams.set('crop', crop);\n // `url` is a non-empty string here, so `Url` is a string type.\n return parsed.toString() as Url;\n}\n\n/** @returns `name` as Webflow's custom attribute, or in valid HTML as `<prefix>-name` / `data-name`. */\nfunction sizeAttribute(el: Element, name: string): string | null {\n return el.getAttribute(name) ?? el.getAttribute(attr(name)) ?? el.getAttribute(`data-${name}`);\n}\n\n/** @param el The element whose max-width / max-height / crop attributes size the image. */\nexport function cdnUrl<Url extends string | null | undefined>(url: Url, el: Element | null | undefined): Url {\n if (!url || !el) return url;\n const width = sizeAttribute(el, 'max-width');\n const height = sizeAttribute(el, 'max-height');\n return imageUrl(url, { width, height, crop: width || height ? sizeAttribute(el, 'crop') : null });\n}\n\n/** @returns The URL without its query (the size parameters). */\nconst withoutQuery = (url: string) => url.replace(/\\?.*$/, '');\n\n/** @returns True for the same image at any size parameters: the server-rendered one is kept. */\nexport function sameImage(a: string | null | undefined, b: string | null | undefined): boolean {\n if (!a || !b) return false;\n return withoutQuery(a) === withoutQuery(b);\n}\n\n/**\n * A CSS `url()` for an image URL from Shopify. Characters that would end the string are escaped, so the URL can't\n * add declarations; only http(s), relative and data:image URLs are used.\n */\nfunction cssUrl(url: string): string {\n const hasScheme = /^\\s*[a-z][a-z0-9+.-]*:/i.test(url);\n if (hasScheme && !/^\\s*(https?:|data:image\\/)/i.test(url)) return 'none';\n const escaped = url.replace(/[\"\\\\\\n\\r\\f]/g, (char) => `\\\\${char.charCodeAt(0).toString(16)} `);\n return `url(\"${escaped}\")`;\n}\n\n/** Shows `url` in `el`: as the `src` of an `<img>` (lazy unless it says otherwise), else as its background image. */\nexport function setImage(el: HTMLElement, url: string | null | undefined, alt?: string | null): void {\n if (!url) return;\n const src = cdnUrl(url, el);\n if (el.tagName !== 'IMG') {\n el.style.backgroundImage = cssUrl(src);\n return;\n }\n if (sameImage(el.getAttribute('src'), src)) {\n if (alt != null && !el.getAttribute('alt')) el.setAttribute('alt', alt);\n return;\n }\n el.removeAttribute('srcset');\n el.removeAttribute('sizes');\n el.setAttribute('src', src);\n if (alt != null) el.setAttribute('alt', alt);\n if (!el.getAttribute('loading')) el.setAttribute('loading', 'lazy');\n}\n", "/**\n * Lists rendered from a template: a repeater's first child in the designer is the template, and every item gets a\n * copy of it. The other children are placeholder content and are removed.\n */\nimport { cfg, reportError, warn } from './config.ts';\n\n/** Each repeater's template, captured on first render. */\nconst templates = new WeakMap<Element, HTMLElement | null>();\n\n/** @returns True once the repeater's template has been captured (it may be empty after that). */\nexport const hasTemplate = (container: Element): boolean => Boolean(templates.get(container));\n\n/**\n * A deep copy of a template. `cloneNode` is typed as returning a `Node`; a copy of an element is an element of the\n * same kind.\n */\nconst copyOf = (template: HTMLElement) => template.cloneNode(true) as HTMLElement;\n\nfunction templateOf(container: Element): HTMLElement | null {\n const known = templates.get(container);\n if (known !== undefined) return known;\n const first = container.firstElementChild;\n // Repeaters are HTML lists in the designer; their first child is an HTML element.\n const template = first ? copyOf(first as HTMLElement) : null;\n templates.set(container, template);\n container.replaceChildren();\n return template;\n}\n\n/** Rebuilds the list on every render. Fine for images, tags, options. */\nexport function repeat<Item>(\n container: Element,\n items: readonly Item[],\n render: (node: HTMLElement, item: Item, index: number) => void,\n): void {\n const template = templateOf(container);\n if (!template) {\n if (items.length) warn('repeater without a template child', container);\n return;\n }\n container.replaceChildren();\n items.forEach((item, index) => {\n const node = copyOf(template);\n render(node, item, index);\n container.appendChild(node);\n });\n}\n\ninterface KeyedHooks {\n /** Runs on a node inserted for a new item. */\n onEnter?: ((node: HTMLElement) => void) | null;\n /** Runs on a node whose item is gone; `done()` removes it. */\n onLeave?: ((node: HTMLElement, done: () => void) => void) | null;\n}\n\nfunction leave(node: HTMLElement, onLeave: KeyedHooks['onLeave']): void {\n const done = () => {\n node.remove();\n };\n if (!onLeave) {\n done();\n return;\n }\n try {\n onLeave(node, done);\n } catch (error) {\n reportError(error, 'repeater onLeave');\n done();\n }\n}\n\n/**\n * Inserts the nodes of new items next to the item before them, leaving the existing nodes where they are.\n *\n * @param wanted Every item's node, in order.\n * @param fresh The nodes not yet in the container.\n */\nfunction insertFresh(\n container: Element,\n wanted: HTMLElement[],\n fresh: Set<HTMLElement>,\n onEnter: KeyedHooks['onEnter'],\n): void {\n wanted.forEach((node, index) => {\n if (!fresh.has(node)) return;\n const previous = wanted[index - 1];\n if (previous?.parentNode === container) previous.after(node);\n else container.prepend(node);\n if (!onEnter) return;\n try {\n onEnter(node);\n } catch (error) {\n reportError(error, 'repeater onEnter');\n }\n });\n}\n\n/**\n * Keyed repeater: keeps existing nodes, inserts new ones, removes old ones. Used for cart lines, so values can\n * animate and lines can slide in and out.\n */\nexport function repeatKeyed<Item>(\n container: Element,\n items: readonly Item[],\n keyOf: (item: Item, index: number) => string | number,\n render: (node: HTMLElement, item: Item, index: number, isNew: boolean) => void,\n { onEnter, onLeave }: KeyedHooks = {},\n): void {\n const template = templateOf(container);\n if (!template) {\n warn('repeater without a template child', container);\n return;\n }\n\n const keyAttribute = `data-${cfg.prefix}-key`;\n const leavingAttribute = `data-${cfg.prefix}-leaving`;\n // The list's children are copies of its template, HTML elements like it.\n const live = () =>\n [...(container.children as HTMLCollectionOf<HTMLElement>)].filter(\n (node) => node.hasAttribute(keyAttribute) && !node.hasAttribute(leavingAttribute),\n );\n\n const existing = new Map(live().map((node) => [node.getAttribute(keyAttribute), node]));\n const wanted: HTMLElement[] = [];\n const fresh = new Set<HTMLElement>();\n\n items.forEach((item, index) => {\n const key = String(keyOf(item, index));\n let node = existing.get(key);\n if (node) existing.delete(key);\n else {\n node = copyOf(template);\n node.setAttribute(keyAttribute, key);\n fresh.add(node);\n }\n render(node, item, index, fresh.has(node));\n wanted.push(node);\n });\n\n const staying = wanted.filter((node) => !fresh.has(node));\n const current = live().filter((node) => staying.includes(node));\n const inOrder = current.length === staying.length && current.every((node, index) => node === staying[index]);\n\n if (!inOrder) {\n existing.forEach((node) => {\n node.remove();\n });\n wanted.forEach((node) => container.appendChild(node));\n return;\n }\n\n insertFresh(container, wanted, fresh, onEnter);\n\n existing.forEach((node) => {\n node.setAttribute(leavingAttribute, '');\n node.removeAttribute(keyAttribute);\n node.style.pointerEvents = 'none';\n node.inert = true;\n leave(node, onLeave);\n });\n}\n", "/**\n * The browser modules' DOM toolkit: what Shopify values look like on the page (money, dates, text, images, lists)\n * and how elements show, hide and report layout changes. Attribute names go through `attr()` so the prefix is\n * configurable. The optional parts import all of it from here (`window.Tether.__core['dom.ts']`).\n */\nimport { cfg, reportError, warn } from './config.ts';\nimport { attr, emit, get } from './markup.ts';\nimport { storeHtmlFragment } from './store-html.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { formatMoney as formatMoneyIn, productUrl as productUrlFor } from './shopify-values.ts';\nimport type { Money } from './shopify-values.ts';\n\nexport { attr, sel, get, has, qsa, scoped, emit, ownEvent, isOwnEvent, ANALYTICS_EVENTS } from './markup.ts';\nexport { imageUrl, cdnUrl, sameImage, setImage } from './images.ts';\nexport { hasTemplate, repeat, repeatKeyed } from './repeaters.ts';\n\n/**\n * State Tether keeps on elements, as `data-tether-*` attributes whatever the prefix: sites style and test against\n * these names, so they stay fixed where the `tether-*` markup attributes follow `prefix`.\n */\nconst SEEN = 'data-tether-seen';\nconst SHOWN = 'data-tether-shown';\nconst PREVIOUS_DISPLAY = 'data-tether-prev-display';\n\n/** Async renders change the page height in bursts; one notification per burst. */\nconst LAYOUT_DEBOUNCE_MS = 120;\n\n/**\n * The product page of a product (`productPath`).\n *\n * @returns `null` without the handle or id the path needs.\n */\nexport function productUrl(handle: string | null | undefined, id?: string | null): string | null {\n return productUrlFor(cfg.productPath || '/product', handle, id);\n}\n\n/** `Intl` throws a RangeError for a locale or currency it doesn't know; the caller then shows a plain fallback. */\nfunction formatOr<T>(format: () => T, fallback: T): T {\n try {\n return format();\n } catch (error) {\n if (error instanceof RangeError) return fallback;\n throw error;\n }\n}\n\n/**\n * `30` \u2192 `30%` (en, nl) or `30 %` (de, fr), in the configured locale.\n *\n * @returns `''` for no or a zero percentage.\n */\nexport function formatPercent(percentage: number | null | undefined, locale: string | undefined = cfg.locale): string {\n if (!percentage) return '';\n const format = () =>\n new Intl.NumberFormat(locale || undefined, { style: 'percent', maximumFractionDigits: 0 }).format(percentage / 100);\n return formatOr(format, `${percentage}%`);\n}\n\n/** Money in the configured locale, through the site's `formatMoney` when it has one. */\nexport function formatMoney(money: Money | null | undefined): string {\n if (!money) return '';\n if (cfg.formatMoney) return cfg.formatMoney(money, cfg.locale);\n return formatMoneyIn(money, cfg.locale);\n}\n\n/** @returns The date in the configured locale, `''` without one. */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return '';\n return formatOr(() => new Date(iso).toLocaleDateString(cfg.locale || undefined), '');\n}\n\n/**\n * Writes HTML from the Shopify admin (product and collection descriptions) into `el`, with script removed\n * (`store-html.ts`). Everything else from Shopify is written as text (`setText`).\n */\nexport function setStoreHtml(el: Element, html: string | null | undefined): void {\n el.replaceChildren(storeHtmlFragment(html, el.ownerDocument, cfg.embedOrigins));\n}\n\nconst isTextField = (el: Element): el is HTMLInputElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'TEXTAREA';\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\n/**\n * Sets the text of `el`, or its value when it is a form field.\n *\n * @param value `null` and `undefined` clear it.\n */\nexport function setText(el: Element, value: string | number | boolean | null | undefined): void {\n const text = value == null ? '' : String(value);\n if (isTextField(el)) el.value = text;\n else el.textContent = text;\n}\n\nconst readText = (el: Element) => (isTextField(el) ? el.value : el.textContent);\n\n/** `setText` that hands a change after the first render to `onValueChange`, for scramble and roll effects. */\nexport function setTextTracked(el: HTMLElement, value: string | number | boolean | null | undefined): void {\n const before = readText(el);\n setText(el, value);\n if (before !== readText(el) && el.hasAttribute(SEEN) && cfg.onValueChange) {\n try {\n cfg.onValueChange(el, before);\n } catch (error) {\n reportError(error, 'onValueChange');\n }\n }\n el.setAttribute(SEEN, '1');\n}\n\n/**\n * Shows an element hidden before the script ran (a class, an inline `display: none`, a `hidden` attribute), as\n * `block` or the value of `tether-display`.\n */\nfunction reveal(el: HTMLElement): void {\n if (el.hasAttribute(SHOWN)) return;\n el.setAttribute(SHOWN, '');\n el.removeAttribute('hidden');\n const previous = el.getAttribute(PREVIOUS_DISPLAY);\n if (previous !== null) {\n el.style.display = previous;\n el.removeAttribute(PREVIOUS_DISPLAY);\n } else if (el.style.display === 'none') {\n el.style.display = '';\n }\n if (typeof getComputedStyle === 'function' && getComputedStyle(el).display === 'none') {\n el.style.display = el.getAttribute(attr('display')) || 'block';\n }\n}\n\n/** Shows or hides `el`, restoring its own display value when shown again. */\nexport function show(el: HTMLElement, visible: boolean): void {\n if (visible) {\n reveal(el);\n return;\n }\n el.removeAttribute(SHOWN);\n // The display value to restore is a real one, never 'none'.\n if (!el.hasAttribute(PREVIOUS_DISPLAY) && el.style.display !== 'none') {\n el.setAttribute(PREVIOUS_DISPLAY, el.style.display);\n }\n el.style.display = 'none';\n}\n\n/** Marks an option button or input as chosen: `is-selected`, `aria-pressed`, and its own or inner radio/checkbox. */\nexport function setSelected(el: Element, selected: boolean): void {\n el.classList.toggle('is-selected', selected);\n el.setAttribute('aria-pressed', selected ? 'true' : 'false');\n if (isInput(el) && (el.type === 'radio' || el.type === 'checkbox')) el.checked = selected;\n const input = el.querySelector('input[type=\"radio\"],input[type=\"checkbox\"]');\n if (input && isInput(input)) input.checked = selected;\n}\n\nlet layoutTimer: ReturnType<typeof setTimeout> | undefined;\n\n/**\n * A global the page's own scripts set on `window`: `TETHER_CONFIG`, the site's config, `TETHER_INTEGRATION`, set by a\n * framework integration (Astro) that starts Tether itself, and GSAP's `ScrollTrigger`. Another script wrote it, so it\n * is read as unknown and checked where it is used.\n */\nexport const pageGlobal = (name: 'TETHER_CONFIG' | 'TETHER_INTEGRATION' | 'ScrollTrigger'): unknown =>\n Reflect.get(window, name);\n\n/** Refreshes GSAP's ScrollTrigger, when the site has it: it measures positions that a height change makes stale. */\nfunction refreshScrollTrigger(): void {\n // ScrollTrigger is a class, and `refresh` a static method of it.\n const scrollTrigger = pageGlobal('ScrollTrigger');\n if (typeof scrollTrigger !== 'function') return;\n const refresh: unknown = Reflect.get(scrollTrigger, 'refresh');\n if (typeof refresh === 'function') Reflect.apply(refresh, scrollTrigger, []);\n}\n\n/**\n * Async renders change the page height after the site's page-transition hooks ran. After a burst of them this calls\n * `onLayoutChange` (else refreshes ScrollTrigger) and emits `tether:layout-changed`, also when either of those throws.\n */\nexport function notifyLayoutChanged(reason: string): void {\n clearTimeout(layoutTimer);\n layoutTimer = setTimeout(() => {\n try {\n if (cfg.onLayoutChange) cfg.onLayoutChange(reason);\n else refreshScrollTrigger();\n } catch (error) {\n reportError(error, 'layout-changed');\n }\n emit('layout-changed', { reason });\n }, LAYOUT_DEBOUNCE_MS);\n}\n\n/** Notifies a layout change (`<reason>:images`) once every image in `root` that is still loading has loaded or failed. */\nexport function watchImages(root: ParentNode, reason: string): void {\n const loading = [...root.querySelectorAll('img')].filter((img) => !img.complete);\n let left = loading.length;\n const done = () => {\n left -= 1;\n if (left === 0) notifyLayoutChanged(`${reason}:images`);\n };\n loading.forEach((img) => {\n img.addEventListener('load', done, { once: true });\n img.addEventListener('error', done, { once: true });\n });\n}\n\n/**\n * Runs `task` with `aria-busy` on `el`; does nothing while `el` is already busy.\n *\n * @returns The task's result, `undefined` when it didn't run.\n */\nexport function busy<T>(el: Element, task: () => T | Promise<T>): Promise<T | undefined> {\n if (el.getAttribute('aria-busy') === 'true') return Promise.resolve(undefined);\n el.setAttribute('aria-busy', 'true');\n return Promise.resolve()\n .then(task)\n .finally(() => {\n el.removeAttribute('aria-busy');\n });\n}\n\n/** @returns The attribute's JSON, `null` when it is missing or not JSON (with a warning). */\nexport function parseJSONAttr(el: Element | null | undefined, name: string): unknown {\n const raw = el ? get(el, name) : null;\n if (!raw) return null;\n try {\n const json: unknown = JSON.parse(raw);\n return json;\n } catch {\n warn(`invalid JSON in ${attr(name)}`, el);\n return null;\n }\n}\n\nexport type AttributeValue = string | number | boolean | null | undefined;\n\n/** @returns A JSON value as attribute text: `''` for none, an object or array as its JSON. */\nfunction attributeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n // What else parsed JSON holds: objects and arrays.\n return JSON.stringify(value);\n}\n\n/**\n * `tether-line-attributes`, JSON the site writes: `{ a: 1 }` or `[{ key, value }]`. An array entry that isn't an\n * object is skipped.\n */\nexport function attributesFromJson(json: unknown): { key: string; value: string }[] {\n if (Array.isArray(json)) {\n const entries: unknown[] = json;\n return entries\n .filter(isRecord)\n .map((entry) => ({ key: attributeText(entry['key']), value: attributeText(entry['value']) }));\n }\n if (!isRecord(json)) return [];\n return Object.entries(json).map(([key, value]) => ({ key, value: attributeText(value) }));\n}\n\n/** `{ a: 1 }` or `[{ key, value }]` \u2192 `[{ key, value }]` as Shopify wants cart and line attributes. */\nexport function toAttributes(\n attributes: Record<string, AttributeValue> | { key: string | number; value?: AttributeValue }[] | null | undefined,\n): { key: string; value: string }[] {\n if (!attributes) return [];\n if (Array.isArray(attributes)) {\n return attributes.map((attribute) => ({ key: String(attribute.key), value: String(attribute.value ?? '') }));\n }\n return Object.entries(attributes).map(([key, value]) => ({ key, value: String(value ?? '') }));\n}\n", "/**\n * The GraphQL fragments of what Tether renders. They are built at call time because the list sizes and metafields come\n * from the config; the browser passes its validated `cfg`, the server helpers their own. Operations put these after\n * the operation, where `withContext` expects them.\n */\nimport { cfg } from './config.ts';\nimport { toMetafieldIdentifier } from './config-sections.ts';\nimport type { TetherConfig, MetafieldIdentifier } from './config.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { StoreAccess } from './client.ts';\n\nexport interface Image {\n url: string;\n altText: string | null;\n width: number | null;\n height: number | null;\n}\n\n/** An option value's swatch: a colour, an image, or both, as the merchant set them in the admin. */\ninterface Swatch {\n color: string | null;\n image: { previewImage: Image | null } | null;\n}\n\nexport type SellingPlanAdjustmentValue =\n | { __typename: 'SellingPlanPercentagePriceAdjustment'; adjustmentPercentage: number }\n | { __typename: 'SellingPlanFixedAmountPriceAdjustment'; adjustmentAmount: Money }\n | { __typename: 'SellingPlanFixedPriceAdjustment'; price: Money };\n\nexport interface SellingPlan {\n id: string;\n name: string;\n description: string | null;\n recurringDeliveries: boolean;\n options: { name: string; value: string }[];\n priceAdjustments: { orderCount: number | null; adjustmentValue: SellingPlanAdjustmentValue }[];\n}\n\nexport interface SellingPlanGroup {\n name: string;\n appName: string | null;\n options: { name: string; values: string[] }[];\n sellingPlans: { nodes: SellingPlan[] };\n}\n\n/** A variant's price on one plan. */\nexport interface SellingPlanAllocation {\n sellingPlan: { id: string };\n /** What the customer pays at checkout (more for a prepaid plan). */\n checkoutChargeAmount: Money;\n remainingBalanceChargeAmount: Money;\n priceAdjustments: { price: Money; compareAtPrice: Money; perDeliveryPrice: Money; unitPrice: Money | null }[];\n}\n\nexport interface ProductVariant {\n id: string;\n title: string;\n availableForSale: boolean;\n currentlyNotInStock: boolean;\n /** Absent when the store's token can't read inventory. */\n quantityAvailable?: number | null;\n sku: string | null;\n barcode: string | null;\n selectedOptions: { name: string; value: string }[];\n image: Image | null;\n price: Money;\n compareAtPrice: Money | null;\n unitPrice: Money | null;\n sellingPlanAllocations: { nodes: SellingPlanAllocation[] };\n}\n\nexport interface Metafield {\n namespace: string;\n key: string;\n type: string;\n value: string;\n /** A file reference's image. */\n reference: { image?: Image } | null;\n}\n\n/** The fields of `fragment TetherProduct`. */\nexport interface Product {\n id: string;\n handle: string;\n title: string;\n description: string;\n descriptionHtml: string;\n productType: string;\n vendor: string;\n tags: string[];\n availableForSale: boolean;\n publishedAt: string | null;\n onlineStoreUrl: string | null;\n requiresSellingPlan: boolean;\n seo: { title: string | null; description: string | null };\n featuredImage: Image | null;\n images: { nodes: Image[] };\n options: { name: string; optionValues: { name: string; swatch: Swatch | null }[] }[];\n priceRange: { minVariantPrice: Money; maxVariantPrice: Money };\n compareAtPriceRange: { minVariantPrice: Money; maxVariantPrice: Money };\n /** Present when metafields are configured; `null` for one that isn't set. */\n metafields?: (Metafield | null)[];\n sellingPlanGroups: { nodes: SellingPlanGroup[] };\n variants: { nodes: ProductVariant[] };\n}\n\nexport type ProductFragmentConfig = Pick<\n TetherConfig,\n 'imagesFirst' | 'variantsFirst' | 'sellingPlanGroupsFirst' | 'sellingPlansFirst' | 'metafields'\n>;\n\nconst MONEY = 'amount currencyCode';\n/** What a token reads unless the store turned out to refuse more (client.ts). */\nconst FULL_ACCESS: StoreAccess = { inventory: true };\n\n/** The stock field, for a token that may read inventory. */\nconst inventoryField = (access: StoreAccess): string => (access.inventory ? 'quantityAvailable' : '');\nconst IMAGE = 'url altText width height';\n\n/**\n * The `metafields(identifiers:)` field. A fragment can't declare variables, so the identifiers are written into the\n * text: only valid ones (`toMetafieldIdentifier`), each quoted as a string literal.\n */\nfunction metafieldsField(metafields: ProductFragmentConfig['metafields']) {\n const identifiers: MetafieldIdentifier[] = [];\n for (const ref of metafields) {\n const identifier = toMetafieldIdentifier(ref);\n if (identifier) identifiers.push(identifier);\n }\n if (!identifiers.length) return '';\n const list = identifiers\n .map(({ namespace, key }) => `{namespace: ${JSON.stringify(namespace)}, key: ${JSON.stringify(key)}}`)\n .join(', ');\n return `metafields(identifiers:[${list}]){namespace key type value reference{... on MediaImage{image{${IMAGE}}}}}`;\n}\n\nconst SELLING_PLAN_FIELDS = ` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${MONEY}}}... on SellingPlanFixedPriceAdjustment{price{${MONEY}}}}}`;\n\nconst ALLOCATION_FIELDS = ` sellingPlan{id}checkoutChargeAmount{${MONEY}}remainingBalanceChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}unitPrice{${MONEY}}}`;\n\n/**\n * @param o The list sizes and metafields.\n * @param access What the token may read.\n * @returns `fragment TetherProduct on Product`.\n */\nexport function productFragment(o: ProductFragmentConfig = cfg, access: StoreAccess = FULL_ACCESS): string {\n return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${o.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField(o.metafields)} sellingPlanGroups(first:${o.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${o.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${o.variantsFirst}){nodes{id title availableForSale currentlyNotInStock ${inventoryField(access)} sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${o.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;\n}\n\n/**\n * @param access What the token may read.\n * @returns `fragment TetherCart on Cart`.\n */\nexport function cartFragment(access: StoreAccess = FULL_ACCESS): string {\n return ` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${MONEY}}totalAmount{${MONEY}}checkoutChargeAmount{${MONEY}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${MONEY}}subtotalAmount{${MONEY}}amountPerQuantity{${MONEY}}compareAtAmountPerQuantity{${MONEY}}}discountAllocations{discountedAmount{${MONEY}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping ${inventoryField(access)} image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}selectedOptions{name value}product{id title handle vendor featuredImage{${IMAGE}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`;\n}\n\n/** The collection fields Tether renders, for inside a `collection { }` selection. */\nexport function collectionFields(): string {\n return `\n id handle title description descriptionHtml\n image { ${IMAGE} }\n seo { title description }`;\n}\n", "/**\n * Shopify selling plans (subscriptions): which plans a product and a variant have, and what each costs. Pure, no DOM,\n * shared by the browser script and the server helpers.\n */\nimport { fromMinorUnits, toMinorUnits } from './amounts.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { SellingPlan, SellingPlanGroup, SellingPlanAllocation } from './fragments.ts';\n\nconst PERCENT = 100;\n\n/** A plan with the group it belongs to. */\nexport type PlanInGroup = SellingPlan & { group: SellingPlanGroup };\nexport interface ProductWithPlans {\n sellingPlanGroups?: { nodes: SellingPlanGroup[] } | null;\n}\n/** A variant as far as plans go. A cart line's variant has no allocations. */\nexport interface VariantWithPlans {\n price: Money;\n sellingPlanAllocations?: { nodes: SellingPlanAllocation[] } | null;\n}\nexport type PlanAdjustment =\n { type: 'percentage'; value: number } | { type: 'amount'; value: Money } | { type: 'price'; value: Money };\n\n/**\n * @param value A gid, a numeric id, or anything else.\n * @param type The Shopify type, e.g. `ProductVariant`.\n * @returns The gid of a numeric id; a gid or other value as written; `null` when empty.\n */\nexport function toGid(value: unknown, type: string): string | null {\n const text = typeof value === 'string' || typeof value === 'number' ? String(value).trim() : '';\n if (!text) return null;\n if (text.startsWith('gid://')) return text;\n if (/^\\d+$/.test(text)) return `gid://shopify/${type}/${text}`;\n return text;\n}\n\nexport const GID = {\n product: (value: unknown) => toGid(value, 'Product'),\n variant: (value: unknown) => toGid(value, 'ProductVariant'),\n sellingPlan: (value: unknown) => toGid(value, 'SellingPlan'),\n};\n\n/** @returns All plans of the product, flattened. */\nexport function allPlans(product: ProductWithPlans | null | undefined): PlanInGroup[] {\n const groups = product?.sellingPlanGroups?.nodes ?? [];\n return groups.flatMap((group) => group.sellingPlans.nodes.map((plan) => ({ ...plan, group })));\n}\n\nexport function hasPlans(product: ProductWithPlans | null | undefined): boolean {\n return allPlans(product).length > 0;\n}\n\n/** @returns The variant's allocation on the plan; `null` when the variant isn't in it. */\nexport function allocationFor(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): SellingPlanAllocation | null {\n if (!variant || !planId) return null;\n const allocations = variant.sellingPlanAllocations?.nodes ?? [];\n return allocations.find((allocation) => allocation.sellingPlan.id === planId) ?? null;\n}\n\n/** @returns The plans this variant can be bought on. */\nexport function plansForVariant(\n product: ProductWithPlans | null | undefined,\n variant: VariantWithPlans | null | undefined,\n): PlanInGroup[] {\n return allPlans(product).filter((plan) => allocationFor(variant, plan.id));\n}\n\n/** @returns The price per delivery on the plan; the one-time price when the variant isn't on it. */\nexport function planPrice(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): Money | null {\n const adjustment = allocationFor(variant, planId)?.priceAdjustments[0];\n return adjustment?.perDeliveryPrice ?? adjustment?.price ?? variant?.price ?? null;\n}\n\n/** @returns What the customer pays at checkout on the plan (prepaid plans charge more up front). */\nexport function planCheckoutCharge(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): Money | null {\n const allocation = allocationFor(variant, planId);\n return allocation?.checkoutChargeAmount ?? allocation?.priceAdjustments[0]?.price ?? variant?.price ?? null;\n}\n\n/** @returns What the plan saves on the one-time price; no amount and 0% when it saves nothing. */\nexport function planSavings(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): { amount: Money | null; percentage: number } {\n const onPlanPrice = planPrice(variant, planId);\n if (!variant || !onPlanPrice) return { amount: null, percentage: 0 };\n const oneTime = parseFloat(variant.price.amount);\n const onPlan = parseFloat(onPlanPrice.amount);\n if (!Number.isFinite(oneTime) || !Number.isFinite(onPlan) || oneTime <= 0 || onPlan >= oneTime) {\n return { amount: null, percentage: 0 };\n }\n const saved = toMinorUnits(variant.price) - toMinorUnits(onPlanPrice);\n return {\n amount: fromMinorUnits(saved, variant.price.currencyCode),\n percentage: Math.round((1 - onPlan / oneTime) * PERCENT),\n };\n}\n\n/**\n * @returns The plan's own discount, whatever the variant: a percentage, a fixed amount off or a fixed price.\n */\nexport function planAdjustment(plan: Pick<SellingPlan, 'priceAdjustments'> | null | undefined): PlanAdjustment | null {\n const value = plan?.priceAdjustments[0]?.adjustmentValue;\n if (!value) return null;\n switch (value.__typename) {\n case 'SellingPlanPercentagePriceAdjustment':\n return { type: 'percentage', value: value.adjustmentPercentage };\n case 'SellingPlanFixedAmountPriceAdjustment':\n return { type: 'amount', value: value.adjustmentAmount };\n case 'SellingPlanFixedPriceAdjustment':\n return { type: 'price', value: value.price };\n default:\n // An adjustment type newer than the API version Tether was written for.\n return null;\n }\n}\n\n/**\n * Finds a plan by id, numeric id, name or option value (case-insensitive), so Webflow editors can write\n * `tether-default-plan=\"Every month\"`.\n */\nexport function findPlan(\n product: ProductWithPlans | null | undefined,\n ref: string | null | undefined,\n): PlanInGroup | null {\n if (!ref) return null;\n const plans = allPlans(product);\n const gid = GID.sellingPlan(ref);\n const needle = ref.trim().toLowerCase();\n return (\n plans.find((plan) => plan.id === gid) ??\n plans.find((plan) => plan.name.toLowerCase() === needle) ??\n plans.find((plan) => plan.options.some((option) => option.value.toLowerCase() === needle)) ??\n null\n );\n}\n", "/**\n * Arithmetic on Shopify's money amounts, which are decimal strings (`'27.50'`). Comparisons go digit by digit, as\n * integers of the smallest unit present, so two amounts that differ only past float precision aren't misjudged. Sums\n * and differences are taken in the currency's own smallest unit, so JPY stays whole and KWD keeps its three digits.\n */\nimport type { Money } from './shopify-values.ts';\n\nconst DECIMAL = /^(-?)(\\d+)(?:\\.(\\d+))?$/;\n\n/**\n * @param fractionDigits Digits after the point to scale to.\n * @returns The amount in units of 10^-fractionDigits, `null` when it isn't a decimal number.\n */\nfunction toScaledInteger(amount: string, fractionDigits: number): bigint | null {\n const match = DECIMAL.exec(amount.trim());\n if (!match) return null;\n const [, sign = '', whole = '0', fraction = ''] = match;\n const digits = BigInt(`${whole}${fraction.padEnd(fractionDigits, '0')}`);\n return sign === '-' ? -digits : digits;\n}\n\nconst fractionLength = (amount: string) => amount.split('.')[1]?.length ?? 0;\n\n/** @returns True when both are decimal numbers and `amount` is the larger one. */\nexport function isGreaterAmount(amount: string | null | undefined, other: string | null | undefined): boolean {\n if (amount == null || other == null) return false;\n const fractionDigits = Math.max(fractionLength(amount.trim()), fractionLength(other.trim()));\n const scaled = toScaledInteger(amount, fractionDigits);\n const scaledOther = toScaledInteger(other, fractionDigits);\n if (scaled === null || scaledOther === null) return false;\n return scaled > scaledOther;\n}\n\n/** For a currency code Intl doesn't know: most currencies have two digits, and `formatMoney` then shows two too. */\nconst DEFAULT_FRACTION_DIGITS = 2;\n\n/** Currency code \u2192 its fraction digits, which never change on a page. */\nconst fractionDigitsByCurrency = new Map<string, number>();\n\n/** @returns The digits after the point the currency has: 0 for JPY, 2 for EUR, 3 for KWD. */\nfunction currencyFractionDigits(currencyCode: string): number {\n const known = fractionDigitsByCurrency.get(currencyCode);\n if (known !== undefined) return known;\n let digits = DEFAULT_FRACTION_DIGITS;\n try {\n const format = new Intl.NumberFormat('en', { style: 'currency', currency: currencyCode });\n digits = format.resolvedOptions().maximumFractionDigits ?? DEFAULT_FRACTION_DIGITS;\n } catch {\n // A code Intl doesn't know (RangeError): see DEFAULT_FRACTION_DIGITS.\n }\n fractionDigitsByCurrency.set(currencyCode, digits);\n return digits;\n}\n\n/** @returns The amount in the currency's smallest unit (cents, yen, fils), rounded to a whole one. */\nexport function toMinorUnits(money: Money): number {\n return Math.round(parseFloat(money.amount) * 10 ** currencyFractionDigits(money.currencyCode));\n}\n\n/** @returns The amount written with the currency's own number of digits. */\nexport function fromMinorUnits(minorUnits: number, currencyCode: string): Money {\n const digits = currencyFractionDigits(currencyCode);\n return { amount: (minorUnits / 10 ** digits).toFixed(digits), currencyCode };\n}\n", "/**\n * The structure of Tether's product fragment, checked on a value read from the page or from a Storefront answer\n * before it is used as a `Product`. Rendering walks the product's lists and prices without checking each step, so a\n * truncated or hand-written entry, or an answer missing fields, would otherwise fail deep inside a render instead of\n * being skipped or refused.\n */\nimport { matches } from './client.ts';\nimport { field, isRecord } from '../server/src/shared/json.ts';\nimport type { ShapeOf } from './client.ts';\nimport type { Product } from './fragments.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { PageInfo } from './loader.ts';\n\n/** A page of a GraphQL connection of products. */\nexport interface ProductConnection {\n nodes: Product[];\n pageInfo: PageInfo;\n}\n\n/** A Storefront API `MoneyV2`. */\nexport const MONEY = { amount: 'string', currencyCode: 'string' } as const satisfies ShapeOf<Money>;\n\n/**\n * A product's id, handle and title, and every list and price a render walks without a fallback: images, options,\n * variants (each with an id, a price and its options) and the price range. Text fields are written as text, and the\n * selling plan lists are read with a fallback, so they need no check.\n */\nexport const PRODUCT = {\n id: 'string',\n handle: 'string',\n title: 'string',\n options: ['object'],\n images: { nodes: ['object'] },\n priceRange: { minVariantPrice: MONEY, maxVariantPrice: MONEY },\n variants: { nodes: [{ id: 'string', price: MONEY, selectedOptions: ['object'] }] },\n} as const satisfies ShapeOf<Product>;\n\n/** A page of products, with the `pageInfo` paging reads. */\nexport const PRODUCT_CONNECTION = {\n nodes: [PRODUCT],\n pageInfo: { hasNextPage: 'boolean' },\n} as const satisfies ShapeOf<ProductConnection>;\n\n/** True for a value with the structure of `PRODUCT` and a non-empty id and handle. */\nexport const isProductShape = (value: unknown): value is Product =>\n field(value, 'id') !== '' && field(value, 'handle') !== '' && matches(value, PRODUCT);\n\n/**\n * A node a lookup by gid answers: a product, or a node of another type, for which `... on Product` selects no field\n * (no `id`).\n */\nexport const PRODUCT_NODE = (node: unknown): node is Product | { id?: undefined } =>\n isRecord(node) && (node['id'] === undefined || matches(node, PRODUCT));\n", "/**\n * The structure of Tether's cart fragment, checked on a Storefront answer before it is used as a `ShopifyCart`. The\n * cart's render, its totals and the cart API walk its lines and prices without checking each step. Text fields are\n * written as text, so they need no check.\n */\nimport { MONEY } from '../product-shape.ts';\nimport type { ShapeOf } from '../client.ts';\nimport type { ShopifyCart, ShopifyCartLine } from './store.ts';\n\nconst LINE = {\n id: 'string',\n quantity: 'number',\n attributes: ['object'],\n cost: { totalAmount: MONEY, subtotalAmount: MONEY, amountPerQuantity: MONEY },\n merchandise: { id: 'string', selectedOptions: ['object'], product: 'object' },\n} as const satisfies ShapeOf<ShopifyCartLine>;\n\nexport const CART = {\n id: 'string',\n checkoutUrl: 'string',\n totalQuantity: 'number',\n attributes: ['object'],\n discountCodes: ['object'],\n cost: { totalAmount: MONEY, subtotalAmount: MONEY },\n lines: { nodes: [LINE] },\n} as const satisfies ShapeOf<ShopifyCart>;\n", "/**\n * The cart drawer: every `[tether-cart-drawer]` on the page opens and closes together, as a modal dialog. Focus moves\n * into the open cart, Tab stays inside it and focus returns on close (`cartFocus`). A page without a drawer (a cart\n * built in React, Svelte or Vue) still has an open state, for `Tether.cart.subscribe`.\n */\nimport { cfg } from '../config.ts';\nimport { sel, get, qsa, emit } from '../dom.ts';\n\nconst FOCUSABLE =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/** Long enough for the drawer's open transition to make its content visible: hidden elements can't take focus. */\nconst FOCUS_AFTER_OPEN_MS = 60;\n\n/** The open state of a page without a drawer element. */\nlet openWithoutDrawer = false;\n\n/** Where focus was before the cart opened. */\nlet returnFocus: HTMLElement | null = null;\n\nconst isFocusable = (el: Element | null): el is HTMLElement => el !== null && 'focus' in el;\n\n/** @returns The visible focusable elements inside `drawer`, in document order. */\nconst focusables = (drawer: ParentNode): HTMLElement[] =>\n qsa(drawer, FOCUSABLE).filter((el) => el.offsetParent !== null || el === document.activeElement);\n\n/** Every cart drawer on the page. */\nexport const drawers = (): HTMLElement[] => qsa(document, sel('cart-drawer'));\n\nconst isOpenDrawer = (drawer: HTMLElement) => drawer.classList.contains(cfg.cartDrawerOpenClass);\n\n/** True while a drawer, or the drawer-less cart, is open. */\nexport function isOpen(): boolean {\n const all = drawers();\n return all.length ? all.some(isOpenDrawer) : openWithoutDrawer;\n}\n\n/**\n * `aria-expanded` on the open buttons, and dialog semantics on the drawers unless their markup has its own. A closed\n * drawer is hidden from screen readers and, with `cartFocus`, out of the Tab order (inert), like a closed dialog.\n */\nexport function syncDrawerAria(open: boolean): void {\n qsa(document, sel('cart-open')).forEach((button) => {\n button.setAttribute('aria-expanded', String(open));\n });\n drawers().forEach((drawer) => {\n if (!drawer.hasAttribute('role')) drawer.setAttribute('role', 'dialog');\n if (!drawer.hasAttribute('aria-label') && !drawer.hasAttribute('aria-labelledby')) {\n drawer.setAttribute('aria-label', get(drawer, 'cart-label') || 'Cart');\n }\n drawer.setAttribute('aria-modal', String(open));\n if (isOpenDrawer(drawer)) return;\n drawer.setAttribute('aria-hidden', 'true');\n if (cfg.cartFocus) drawer.inert = true;\n });\n}\n\nfunction focusInto(drawer: HTMLElement) {\n const target = focusables(drawer)[0] ?? drawer;\n if (target === drawer && !drawer.hasAttribute('tabindex')) drawer.tabIndex = -1;\n target.focus({ preventScroll: true });\n}\n\n/** Opens every drawer except `tether-cart-drawer=\"manual\"` ones, which the site opens itself. */\nexport function openCart(): void {\n if (isOpen()) return;\n openWithoutDrawer = true;\n const opened = drawers().filter((drawer) => get(drawer, 'cart-drawer') !== 'manual');\n opened.forEach((drawer) => {\n drawer.inert = false;\n drawer.classList.add(cfg.cartDrawerOpenClass);\n drawer.setAttribute('aria-hidden', 'false');\n });\n syncDrawerAria(true);\n const [first] = opened;\n if (cfg.cartFocus && first) {\n returnFocus = isFocusable(document.activeElement) ? document.activeElement : null;\n setTimeout(() => {\n focusInto(first);\n }, FOCUS_AFTER_OPEN_MS);\n }\n cfg.onCartOpen?.(opened);\n emit('cart-toggled', { open: true });\n}\n\nexport function closeCart(): void {\n if (!isOpen()) return;\n openWithoutDrawer = false;\n const all = drawers();\n const hadFocus = all.some((drawer) => drawer.contains(document.activeElement));\n all.forEach((drawer) => {\n drawer.classList.remove(cfg.cartDrawerOpenClass);\n drawer.setAttribute('aria-hidden', 'true');\n if (cfg.cartFocus) drawer.inert = true;\n });\n syncDrawerAria(false);\n if (hadFocus && returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });\n returnFocus = null;\n cfg.onCartClose?.(all);\n emit('cart-toggled', { open: false });\n}\n\nexport function toggleCart(): void {\n if (isOpen()) closeCart();\n else openCart();\n}\n\n/**\n * Keeps Tab inside the open drawer (`cartFocus`): from the last focusable element it wraps to the first, with Shift\n * from the first to the last, and from outside the drawer it goes in. For a document `keydown` listener.\n */\nexport function trapFocus(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || !cfg.cartFocus || !isOpen()) return;\n const drawer = drawers().find(isOpenDrawer);\n if (!drawer) return;\n const inside = focusables(drawer);\n const first = inside[0];\n const last = inside.at(-1);\n if (!first || !last) return;\n const active = document.activeElement;\n const leaving = event.shiftKey ? active === first : active === last;\n if (drawer.contains(active) && !leaving) return;\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n}\n", "/**\n * Product data for the browser script. Products asked for in the same tick go out as one request, and every product\n * is cached for the life of the page, which survives Barba/Swup page swaps. Products the server rendered\n * (`tether/server` `hydrate()`) are used at once and refreshed once in the background.\n */\nimport { cfg, log } from './config.ts';\nimport { gql, matches, orNull, shaped, TetherError } from './client.ts';\nimport { productFragment, collectionFields } from './fragments.ts';\nimport { productKey } from './shopify-values.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { PRODUCT, PRODUCT_CONNECTION, PRODUCT_NODE } from './product-shape.ts';\nimport type { Product, Image } from './fragments.ts';\nimport type { QueryText } from './client.ts';\nimport type { ProductConnection } from './product-shape.ts';\n\n/**\n * A product request that missed is remembered this long, so one page asking twice sends one request, and a product\n * published in Shopify meanwhile shows up on the next visit.\n */\nconst MISS_CACHE_MS = 10_000;\n/** A product loaded live this recently is newer than a server-rendered copy of it, which is then ignored. */\nconst FRESH_MS = 60_000;\n\ninterface Waiter {\n resolve: (product: Product) => void;\n reject: (error: unknown) => void;\n}\n\n/** Handle or gid to the product, loaded or loading. */\nconst productCache = new Map<string, Promise<Product>>();\nconst waiting = new Map<string, Waiter>();\n/** Keys of products that came from the server and weren't refreshed yet. */\nconst stale = new Set<string>();\n/** Handle or gid to when it last loaded live, in ms since the epoch. */\nconst freshAt = new Map<string, number>();\n/** Keys for the next batch. */\nconst pending: string[] = [];\nlet flushScheduled = false;\n/**\n * Bumped by `clearProductCache()`: responses to requests sent before that (in the old market, after a country\n * switch) still reach their callers but never refill the cache.\n */\nlet generation = 0;\n\nconst isGid = (key: string) => key.startsWith('gid://');\n\n/** Accepts a handle, numeric id, gid or a full product URL. */\nexport const normalizeProductKey = productKey;\n\n/** Puts a product the page already has (from a collection query) in the cache, under its handle and its id. */\nexport function primeProduct(product: Product | null | undefined): void {\n if (!product) return;\n const loaded = Promise.resolve(product);\n productCache.set(product.handle, loaded);\n productCache.set(product.id, loaded);\n}\n\n/**\n * @param rawKey A handle, numeric id, gid or product URL.\n * @throws {TetherError} `NOT_FOUND` (rejected) for an empty key or a product the store doesn't have.\n */\nexport function getProduct(rawKey: string | number | null | undefined): Promise<Product> {\n const key = normalizeProductKey(rawKey);\n if (!key) return Promise.reject(new TetherError('empty product key', { code: 'NOT_FOUND' }));\n const cached = productCache.get(key);\n if (cached) return cached;\n\n const loading = new Promise<Product>((resolve, reject) => {\n waiting.set(key, { resolve, reject });\n pending.push(key);\n if (!flushScheduled) {\n flushScheduled = true;\n setTimeout(() => void flush(), 0);\n }\n });\n productCache.set(key, loading);\n return loading;\n}\n\n/**\n * @param operation An operation that spreads `...TetherProduct`.\n * @returns The operation with the product fragment (which starts on its own line), written for what the token may\n * read.\n */\nconst withProductFragment =\n (operation: string): QueryText =>\n (access) =>\n `${operation}${productFragment(cfg, access)}`;\n\n/**\n * @param keys Handles and gids.\n * @returns One query for all of them: gids through `nodes(ids:)` as `byId`, each handle as its own alias `h<index>`.\n */\nfunction batchQuery(keys: string[]): { query: QueryText; variables: Record<string, unknown>; handles: string[] } {\n const ids = keys.filter(isGid);\n const handles = keys.filter((key) => !isGid(key));\n const definitions: string[] = [];\n const selections: string[] = [];\n const variables: Record<string, unknown> = {};\n if (ids.length) {\n definitions.push('$ids: [ID!]!');\n variables['ids'] = ids;\n selections.push('byId: nodes(ids: $ids) { ... on Product { ...TetherProduct } }');\n }\n handles.forEach((handle, index) => {\n definitions.push(`$h${index}: String!`);\n variables[`h${index}`] = handle;\n selections.push(`h${index}: product(handle: $h${index}) { ...TetherProduct }`);\n });\n const query = withProductFragment(`query TetherProducts(${definitions.join(',')}){${selections.join(' ')}}`);\n return { query, variables, handles };\n}\n\nfunction settleWaiting(key: string, settle: (waiter: Waiter) => void): void {\n const waiter = waiting.get(key);\n waiting.delete(key);\n if (waiter) settle(waiter);\n}\n\nfunction forgetMissLater(key: string, batchGeneration: number): void {\n if (batchGeneration !== generation) return;\n const miss = productCache.get(key);\n setTimeout(() => {\n if (productCache.get(key) === miss) productCache.delete(key);\n }, MISS_CACHE_MS);\n}\n\n/** A node of a type other than Product, for which `... on Product` selects no field. */\ninterface OtherNode {\n id?: undefined;\n}\n/** The answer to `batchQuery`: `byId` for the gids, `h<index>` for each handle. */\ntype BatchData = Partial<Record<`h${number}`, Product | null>> & { byId?: (Product | OtherNode | null)[] };\n\nfunction isBatchData(data: unknown): data is BatchData {\n if (!isRecord(data)) return false;\n const { byId = [], ...byHandle } = data;\n return matches(byId, [orNull(PRODUCT_NODE)]) && Object.values(byHandle).every(orNull(PRODUCT));\n}\n\nasync function flush() {\n flushScheduled = false;\n const batchGeneration = generation;\n const keys = pending.splice(0);\n if (!keys.length) return;\n const { query, variables, handles } = batchQuery(keys);\n log('fetch products', keys);\n\n let data;\n try {\n data = await gql(query, variables, isBatchData);\n } catch (error) {\n keys.forEach((key) => {\n settleWaiting(key, (waiter) => {\n if (batchGeneration === generation) productCache.delete(key);\n waiter.reject(error);\n });\n });\n return;\n }\n\n const found = new Map<string, Product>();\n for (const product of data.byId ?? []) if (product?.id) found.set(product.id, product);\n handles.forEach((handle, index) => {\n const product = data[`h${index}`];\n if (product) found.set(handle, product);\n });\n\n const now = Date.now();\n keys.forEach((key) => {\n settleWaiting(key, (waiter) => {\n const product = found.get(key);\n if (!product) {\n forgetMissLater(key, batchGeneration);\n waiter.reject(new TetherError(`Product not found: ${key}`, { code: 'NOT_FOUND' }));\n return;\n }\n if (batchGeneration === generation) {\n primeProduct(product);\n freshAt.set(product.handle, now);\n freshAt.set(product.id, now);\n }\n waiter.resolve(product);\n });\n });\n}\n\n/** Forgets every product, for a new market: requests already sent no longer fill the cache. */\nexport function clearProductCache(): void {\n productCache.clear();\n generation += 1;\n stale.clear();\n freshAt.clear();\n}\n\n/**\n * Uses products the server rendered, unless the page loaded the same product live in the last `FRESH_MS` (an earlier\n * page of a View Transition visit), whose copy is newer.\n */\nexport function hydrateProducts(products: readonly Product[] | null | undefined): void {\n for (const product of products ?? []) {\n if (!product.id || !product.handle) continue;\n const loadedAgo = Date.now() - (freshAt.get(product.handle) ?? 0);\n if (loadedAgo < FRESH_MS && productCache.has(product.handle)) continue;\n primeProduct(product);\n stale.add(product.handle);\n stale.add(product.id);\n }\n}\n\n/** @returns True once per server-rendered product: the caller then refreshes it. */\nexport function takeStale(rawKey: string | number | null | undefined): boolean {\n const key = normalizeProductKey(rawKey);\n if (!key || !stale.has(key)) return false;\n stale.delete(key);\n // The handle and the id point to the same product: one refresh covers both.\n void productCache.get(key)?.then((product) => {\n stale.delete(product.handle);\n stale.delete(product.id);\n }, failureIsReportedByTheRender);\n return true;\n}\n\n/**\n * The cached promise is the one the product's render awaits, and the render reports its failure: the stale cleanup\n * above has nothing left to do for a product that did not load.\n */\nfunction failureIsReportedByTheRender() {\n // Nothing to clean: see above.\n}\n\n/** Loads this product again, past the cache. Products refreshed in the same tick go out as one request. */\nexport function refreshProduct(key: string | number): Promise<Product> {\n const normalized = normalizeProductKey(key);\n if (normalized) productCache.delete(normalized);\n return getProduct(key);\n}\n\nexport interface PageInfo {\n hasNextPage: boolean;\n endCursor: string | null;\n}\nexport interface Collection {\n id: string;\n handle: string;\n title: string;\n description: string;\n descriptionHtml: string;\n image: Image | null;\n seo: { title: string | null; description: string | null };\n}\nexport interface ProductPage {\n collection: Collection | null;\n products: Product[];\n pageInfo: PageInfo;\n}\nexport interface ProductQuery {\n /** A collection handle; without one, a search. */\n collection?: string;\n /** Storefront search syntax, for a search. */\n query?: string;\n first?: number;\n /** The cursor of the page before. */\n after?: string | null;\n /** `ProductCollectionSortKeys` for a collection, `ProductSortKeys` for a search. */\n sortKey?: string;\n reverse?: boolean;\n /** `ProductFilter` values, for a collection. */\n filters?: unknown[];\n}\n\n/** A collection's answer: `null` for a handle the store doesn't have. Its other fields are written as text. */\nexport const isCollectionAnswer = shaped<{ collection: (Collection & { products: ProductConnection }) | null }>({\n collection: orNull({ handle: 'string', products: PRODUCT_CONNECTION }),\n});\nexport const isSearchAnswer = shaped<{ products: ProductConnection }>({ products: PRODUCT_CONNECTION });\nexport const isRecommendationsAnswer = shaped<{ productRecommendations: Product[] | null }>({\n productRecommendations: orNull([PRODUCT]),\n});\n\nconst COLLECTION_QUERY = `query TetherCollection($handle:String!,$first:Int!,$after:String,$sortKey:ProductCollectionSortKeys,$reverse:Boolean,$filters:[ProductFilter!]){collection(handle:$handle){${collectionFields()} products(first:$first,after:$after,sortKey:$sortKey,reverse:$reverse,filters:$filters){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}}`;\n\nconst SEARCH_QUERY = `query TetherSearch($first:Int!,$after:String,$query:String,$sortKey:ProductSortKeys,$reverse:Boolean){products(first:$first,after:$after,query:$query,sortKey:$sortKey,reverse:$reverse){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}`;\n\n/**\n * A page of products from a collection (by handle) or from a search. Every product on it is cached.\n *\n * @throws {TetherError} `NOT_FOUND` for a collection the store doesn't have.\n */\nexport async function getProducts({\n collection,\n query,\n first = cfg.collectionFirst,\n after = null,\n sortKey,\n reverse = false,\n filters,\n}: ProductQuery = {}): Promise<ProductPage> {\n if (collection) {\n const data = await gql(\n withProductFragment(COLLECTION_QUERY),\n {\n handle: collection,\n first,\n after,\n sortKey: sortKey || undefined,\n reverse,\n filters: filters ?? undefined,\n },\n isCollectionAnswer,\n );\n if (!data.collection) throw new TetherError(`Collection not found: ${collection}`, { code: 'NOT_FOUND' });\n const { products, ...rest } = data.collection;\n products.nodes.forEach(primeProduct);\n return { collection: rest, products: products.nodes, pageInfo: products.pageInfo };\n }\n\n const data = await gql(\n withProductFragment(SEARCH_QUERY),\n { first, after, query: query || undefined, sortKey: sortKey || undefined, reverse },\n isSearchAnswer,\n );\n data.products.nodes.forEach(primeProduct);\n return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };\n}\n\n/**\n * Shopify's recommendations for a product. Every product in them is cached.\n *\n * @param product A product, or its handle, id or gid.\n */\nexport async function getRecommendations(\n product: string | number | { id: string } | null | undefined,\n intent: 'RELATED' | 'COMPLEMENTARY' = 'RELATED',\n): Promise<Product[]> {\n const key = normalizeProductKey(typeof product === 'object' ? product?.id : product);\n const byId = key?.startsWith('gid://') ?? false;\n const data = await gql(\n withProductFragment(\n `query TetherRecs(${byId ? '$id: ID!' : '$handle: String!'},$intent:ProductRecommendationIntent){productRecommendations(${byId ? 'productId: $id' : 'productHandle: $handle'},intent:$intent){...TetherProduct}}`,\n ),\n byId ? { id: key, intent } : { handle: key, intent },\n isRecommendationsAnswer,\n );\n const list = data.productRecommendations ?? [];\n list.forEach(primeProduct);\n return list;\n}\n", "/**\n * The DOM writes of a product scope that depend on the product alone, once per product: its binds, the option and\n * plan lists and the options of its selects. `variant-render.ts` writes what follows the visitor's choice.\n */\nimport {\n attr,\n sel,\n get,\n has,\n qsa,\n formatMoney,\n formatDate,\n setText,\n setImage,\n setStoreHtml,\n show,\n repeat,\n hasTemplate,\n sameImage,\n productUrl,\n} from './dom.ts';\nimport type { Product } from './fragments.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\n\n/** The product scope as its renders see it. */\nexport interface RenderScope {\n readonly root: HTMLElement;\n readonly product: Product;\n readonly plans: PlanInGroup[];\n /** Matches in this scope, the root included. */\n $: (selector: string) => HTMLElement[];\n /** The product's option name for one written in the markup. */\n optName: (authored: string | null) => string;\n}\n\n/** `tether-bind=\"image:2\"`: the second product image. */\nconst NTH_IMAGE_BIND = /^image:(\\d+)$/;\nconst METAFIELD_BIND = 'metafield:';\nconst MULTI_LINE_METAFIELD = /^(rich_text|multi_line_text)_field$/;\n/** `tether-plan-list` values that list every plan, not one group: Webflow's Designer can't always leave it empty. */\nconst ALL_PLAN_GROUPS = ['', 'true', 'all', '*'];\n\nexport const isSelect = (el: Element): el is HTMLSelectElement => el.tagName === 'SELECT';\n\n/** @returns The `<select>` elements in the scope that match `selector`. */\nexport const selectsIn = (scope: RenderScope, selector: string): HTMLSelectElement[] =>\n scope.$(`select${selector}`).filter(isSelect);\n\n/** Leaves a server-rendered gallery with the same images as it is. */\nfunction renderGallery(el: HTMLElement, product: Product): void {\n const images = product.images.nodes;\n const rendered = [...el.children].map((node) => (node.tagName === 'IMG' ? node : node.querySelector('img')));\n const same =\n !hasTemplate(el) &&\n rendered.length === images.length &&\n rendered.every((img, index) => sameImage(img?.getAttribute('src'), images[index]?.url));\n if (same) return;\n repeat(el, images, (node, image) => {\n const target = node.matches(`img,${sel('bind', 'image')}`) ? node : (qsa(node, 'img')[0] ?? node);\n setImage(target, image.url, image.altText || product.title);\n });\n}\n\n/** @param ref `namespace.key` */\nfunction renderMetafield(el: HTMLElement, product: Product, ref: string): void {\n const dot = ref.indexOf('.');\n const namespace = ref.slice(0, dot);\n const key = ref.slice(dot + 1);\n const metafield = (product.metafields ?? []).find((field) => field?.namespace === namespace && field.key === key);\n if (!metafield) {\n setText(el, '');\n return;\n }\n const image = metafield.reference?.image;\n if (image) {\n setImage(el, image.url, image.altText);\n return;\n }\n if (MULTI_LINE_METAFIELD.test(metafield.type)) el.style.whiteSpace = 'pre-line';\n setText(el, metafield.value);\n}\n\n/** The product binds written as text. */\nconst PRODUCT_TEXTS = new Map<string, (product: Product) => string>(\n Object.entries({\n title: (product) => product.title,\n 'description-text': (product) => product.description,\n vendor: (product) => product.vendor,\n type: (product) => product.productType,\n handle: (product) => product.handle,\n 'published-at': (product) => formatDate(product.publishedAt),\n 'min-price': (product) => formatMoney(product.priceRange.minVariantPrice),\n 'max-price': (product) => formatMoney(product.priceRange.maxVariantPrice),\n }),\n);\n\nfunction renderTags(el: HTMLElement, product: Product): void {\n const exclude = (el.getAttribute('exclude') ?? '')\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean);\n const tags = product.tags.filter((tag) => !exclude.includes(tag));\n repeat(el, tags, (node, tag) => {\n setText(node, tag);\n });\n}\n\n/**\n * A `tether-bind` of the product itself; binds of the variant are left to `writeVariant`.\n */\nfunction renderProductBind(el: HTMLElement, key: string, product: Product): void {\n const text = PRODUCT_TEXTS.get(key);\n if (text) {\n setText(el, text(product));\n return;\n }\n switch (key) {\n case 'description':\n setStoreHtml(el, product.descriptionHtml);\n break;\n case 'featured-image':\n setImage(el, product.featuredImage?.url, product.featuredImage?.altText || product.title);\n break;\n case 'url':\n case 'link': {\n const url = productUrl(product.handle, product.id);\n if (url) el.setAttribute('href', url);\n if (key === 'url' && !el.firstElementChild && !el.textContent.trim()) setText(el, product.title);\n break;\n }\n case 'images':\n renderGallery(el, product);\n break;\n case 'tags':\n renderTags(el, product);\n break;\n default:\n renderIndexedBind(el, key, product);\n }\n}\n\n/** `image:<n>` (falling back to the featured image) and `metafield:<namespace.key>`. */\nfunction renderIndexedBind(el: HTMLElement, key: string, product: Product): void {\n const nth = NTH_IMAGE_BIND.exec(key);\n if (nth) {\n const image = product.images.nodes[Number(nth[1]) - 1] ?? product.featuredImage;\n setImage(el, image?.url, image?.altText || product.title);\n } else if (key.startsWith(METAFIELD_BIND)) {\n renderMetafield(el, product, key.slice(METAFIELD_BIND.length));\n }\n}\n\nfunction renderOptionLists(scope: RenderScope): void {\n const { product } = scope;\n scope.$(sel('option-list')).forEach((container) => {\n const name = scope.optName(get(container, 'option-list'));\n const option = product.options.find((candidate) => candidate.name === name);\n if (!option || (option.optionValues.length <= 1 && has(container, 'hide-single'))) {\n show(container, false);\n return;\n }\n repeat(container, option.optionValues, (node, value) => {\n node.setAttribute(attr('option-value'), value.name);\n const text = qsa(node, sel('bind', 'option-value'))[0];\n if (text) setText(text, value.name);\n else if (!node.firstElementChild) setText(node, value.name);\n const swatchEl = qsa(node, sel('bind', 'swatch'))[0];\n const { swatch } = value;\n if (!swatchEl || !swatch) return;\n if (swatch.color) swatchEl.style.backgroundColor = swatch.color;\n if (swatch.image?.previewImage) setImage(swatchEl, swatch.image.previewImage.url, value.name);\n });\n });\n}\n\nfunction renderSelects(scope: RenderScope): void {\n const { product } = scope;\n selectsIn(scope, sel('option')).forEach((select) => {\n const option = product.options.find((candidate) => candidate.name === scope.optName(get(select, 'option')));\n if (!option) {\n show(select, false);\n return;\n }\n select.replaceChildren(...option.optionValues.map((value) => new Option(value.name, value.name)));\n });\n selectsIn(scope, sel('variant-select')).forEach((select) => {\n select.replaceChildren(...product.variants.nodes.map((variant) => new Option(variant.title, variant.id)));\n });\n selectsIn(scope, sel('plan')).forEach((select) => {\n const plans = scope.plans.map((plan) => new Option(plan.name, plan.id));\n const oneTime = !product.requiresSellingPlan && get(select, 'plan') !== 'subscription-only';\n const oneTimeLabel = select.getAttribute(attr('one-time-label')) || 'One-time purchase';\n select.replaceChildren(...(oneTime ? [new Option(oneTimeLabel, '')] : []), ...plans);\n });\n}\n\nfunction renderPlanLists(scope: RenderScope): void {\n scope.$(sel('plan-list')).forEach((container) => {\n // The value is a selling plan group's name, or one of ALL_PLAN_GROUPS.\n const raw = (get(container, 'plan-list') ?? '').trim();\n const group = ALL_PLAN_GROUPS.includes(raw.toLowerCase()) ? null : raw;\n const plans = scope.plans.filter((plan) => !group || plan.group.name === group);\n repeat(container, plans, (node, plan) => {\n node.setAttribute(attr('plan-value'), plan.id);\n if (!node.firstElementChild && !node.querySelector(sel('plan-bind'))) setText(node, plan.name);\n });\n });\n}\n\n/** Writes what depends on the product alone: its binds, option and plan lists and the selects' options. */\nexport function writeProduct(scope: RenderScope): void {\n scope.$(sel('bind')).forEach((el) => {\n renderProductBind(el, get(el, 'bind') ?? '', scope.product);\n });\n renderOptionLists(scope);\n renderSelects(scope);\n renderPlanLists(scope);\n}\n", "/**\n * Shows the cart everywhere on the page: counts, totals, `tether-show=\"cart-*\"` conditions, the note, the discount\n * codes and the `[tether-cart-lines]` repeaters. It renders what it is given (a `CartView`) and holds no state, so\n * every render shows the cart as the store has it at that moment.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { getProduct } from '../loader.ts';\nimport { allPlans } from '../selling-plans.ts';\nimport { isSelect } from '../product-render.ts';\nimport { isGreaterAmount } from '../amounts.ts';\nimport {\n attr,\n sel,\n get,\n qsa,\n formatMoney,\n setText,\n setTextTracked,\n setImage,\n show,\n repeat,\n repeatKeyed,\n productUrl,\n notifyLayoutChanged,\n} from '../dom.ts';\nimport { isOpen, syncDrawerAria } from './drawer.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { Product } from '../fragments.ts';\nimport type { ShopifyCart, ShopifyCartLine, PendingQuantity } from './store.ts';\n\n/** What a render shows. */\ninterface CartView {\n cart: ShopifyCart | null;\n warnings: readonly CartWarning[];\n /** Line id \u2192 the quantity quick clicks asked for. */\n pending: ReadonlyMap<string, PendingQuantity>;\n /** The last load or change failed (`tether-show=\"cart-error\"`). */\n failed: boolean;\n}\n\n/** Shopify's title of a variant of a product without options. */\nconst DEFAULT_TITLE = 'Default Title';\n\n/** @returns The quantity to show: the one asked for while an update is on its way. */\nexport const quantityOf = (line: ShopifyCartLine, pending: ReadonlyMap<string, PendingQuantity>): number =>\n pending.get(line.id)?.quantity ?? line.quantity;\n\nconst numericId = (gid: string) => gid.split('/').pop() ?? gid;\n\n/** @returns The product page, with the line's variant and plan selected. */\nfunction lineUrl(line: ShopifyCartLine): string | null {\n const variant = line.merchandise;\n const base = productUrl(variant.product.handle, variant.product.id);\n if (!base) return null;\n const query = new URLSearchParams();\n query.set('variant', numericId(variant.id));\n const planId = line.sellingPlanAllocation?.sellingPlan.id;\n if (planId) query.set('selling_plan', numericId(planId));\n return `${base}${base.includes('?') ? '&' : '?'}${query.toString()}`;\n}\n\ntype LineText = (line: ShopifyCartLine, view: CartView) => string | number;\n\nconst productTitle: LineText = (line) => line.merchandise.product.title;\n\n/** The text of each `tether-line` key. */\nconst LINE_TEXT: Record<string, LineText> = {\n title: productTitle,\n 'product-title': productTitle,\n 'variant-title': (line) => (line.merchandise.title === DEFAULT_TITLE ? '' : line.merchandise.title),\n options: (line) =>\n line.merchandise.selectedOptions\n .filter((option) => option.value !== DEFAULT_TITLE)\n .map((option) => `${option.name}: ${option.value}`)\n .join(', '),\n vendor: (line) => line.merchandise.product.vendor,\n sku: (line) => line.merchandise.sku ?? '',\n price: (line) => formatMoney(line.cost.amountPerQuantity),\n 'compare-at-price': (line) => formatMoney(line.cost.compareAtAmountPerQuantity ?? line.merchandise.compareAtPrice),\n 'line-total': (line) => formatMoney(line.cost.totalAmount),\n 'line-subtotal': (line) => formatMoney(line.cost.subtotalAmount),\n quantity: (line, view) => quantityOf(line, view.pending),\n plan: (line) => line.sellingPlanAllocation?.sellingPlan.name ?? '',\n 'plan-description': (line) => line.sellingPlanAllocation?.sellingPlan.description ?? '',\n components: (line) =>\n (line.lineComponents ?? [])\n .map((component) => `${component.quantity} \u00D7 ${component.merchandise.product.title}`)\n .join(', '),\n};\n\nconst ATTRIBUTE_KEY = 'attribute:';\n\n/**\n * @param key The `tether-line` value.\n * @returns The text for `key`, `undefined` for a key Tether doesn't know.\n */\nfunction lineValue(line: ShopifyCartLine, key: string, view: CartView): string | number | undefined {\n if (Object.hasOwn(LINE_TEXT, key)) return LINE_TEXT[key]?.(line, view);\n if (!key.startsWith(ATTRIBUTE_KEY)) return undefined;\n const name = key.slice(ATTRIBUTE_KEY.length);\n return line.attributes.find((attribute) => attribute.key === name)?.value ?? '';\n}\n\n/**\n * Fills a `<select tether-line-plan>` with the product's plans (and one-time purchase, when the product allows it and\n * the select isn't `subscription-only`), keeping the options when they haven't changed.\n */\nfunction fillPlanSelect(select: HTMLSelectElement, line: ShopifyCartLine, product: Product) {\n const plans = allPlans(product);\n const allowOneTime = !product.requiresSellingPlan && get(select, 'line-plan') !== 'subscription-only';\n const oneTime = { value: '', label: select.getAttribute(attr('one-time-label')) || 'One-time purchase' };\n const choices = [...(allowOneTime ? [oneTime] : []), ...plans.map((plan) => ({ value: plan.id, label: plan.name }))];\n const signature = choices.map((choice) => choice.value).join('|');\n if (select.dataset['tetherSig'] !== signature) {\n select.replaceChildren(\n ...choices.map((choice) => {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n return option;\n }),\n );\n select.dataset['tetherSig'] = signature;\n }\n select.value = line.sellingPlanAllocation?.sellingPlan.id ?? '';\n show(select, plans.length > 0);\n}\n\nfunction renderLineState(node: HTMLElement, line: ShopifyCartLine, view: CartView) {\n const isSubscription = Boolean(line.sellingPlanAllocation);\n node.classList.toggle('is-subscription', isSubscription);\n // Shopify's note about this line, such as less stock than asked.\n const warning = view.warnings.find((candidate) => candidate.target === line.id);\n node.classList.toggle('is-warning', Boolean(warning));\n const pending = view.pending.has(line.id);\n node.classList.toggle('is-busy', pending);\n if (pending) node.setAttribute('aria-busy', 'true');\n else node.removeAttribute('aria-busy');\n qsa(node, sel('line', 'warning')).forEach((el) => {\n setTextTracked(el, warning?.message ?? '');\n });\n const conditions = new Map([\n ['subscription', isSubscription],\n ['one-time', !isSubscription],\n ['on-sale', isGreaterAmount(line.cost.compareAtAmountPerQuantity?.amount, line.cost.amountPerQuantity.amount)],\n ['has-components', (line.lineComponents ?? []).length > 0],\n ['warning', Boolean(warning)],\n ]);\n qsa(node, sel('line-show')).forEach((el) => {\n const key = get(el, 'line-show');\n const visible = key === null ? undefined : conditions.get(key);\n if (visible !== undefined) show(el, visible);\n });\n}\n\nfunction renderLine(node: HTMLElement, line: ShopifyCartLine, view: CartView) {\n const variant = line.merchandise;\n node.setAttribute(attr('line-id'), line.id);\n qsa(node, sel('line')).forEach((el) => {\n const key = get(el, 'line') ?? '';\n if (key === 'image') {\n setImage(el, variant.image?.url ?? variant.product.featuredImage?.url, variant.product.title);\n return;\n }\n if (key === 'url') {\n const url = lineUrl(line);\n if (url) el.setAttribute('href', url);\n return;\n }\n const value = lineValue(line, key, view);\n if (value !== undefined) setTextTracked(el, value);\n });\n renderLineState(node, line, view);\n const planSelects = qsa(node, sel('line-plan')).filter(isSelect);\n planSelects.forEach((select) => {\n getProduct(variant.product.handle)\n .then((product) => {\n fillPlanSelect(select, line, product);\n })\n .catch((error: unknown) => {\n reportError(error, 'line plan options');\n });\n });\n}\n\nfunction renderConditions(cart: ShopifyCart | null, view: CartView) {\n const count = cart?.totalQuantity ?? 0;\n const hasSubscription = (cart?.lines.nodes ?? []).some((line) => line.sellingPlanAllocation);\n const conditions = new Map([\n ['cart-empty', count === 0],\n ['cart-filled', count > 0],\n ['cart-has-subscription', hasSubscription],\n ['cart-no-subscription', !hasSubscription],\n ['cart-has-discount', (cart?.discountCodes ?? []).some((code) => code.applicable)],\n ['cart-error', view.failed],\n ['cart-warning', view.warnings.length > 0],\n ]);\n qsa(document, sel('show')).forEach((el) => {\n const key = get(el, 'show');\n const visible = key === null ? undefined : conditions.get(key);\n if (visible === undefined || el.closest(sel('product'))) return;\n show(el, visible);\n });\n qsa(document, sel('checkout')).forEach((el) => {\n el.classList.toggle('is-disabled', !count);\n el.setAttribute('aria-disabled', String(!count));\n });\n}\n\nfunction renderFields(cart: ShopifyCart | null) {\n qsa(document, `textarea${sel('cart-note')},input${sel('cart-note')}`).forEach((el) => {\n // The visitor may be typing in it.\n if (document.activeElement !== el) setText(el, cart?.note ?? '');\n });\n qsa(document, sel('cart-discounts')).forEach((container) => {\n repeat(container, cart?.discountCodes ?? [], (node, discount) => {\n node.setAttribute(attr('discount-code'), discount.code);\n const target = node.querySelector(sel('discount', 'code')) ?? (node.firstElementChild ? null : node);\n if (target) setText(target, discount.code);\n node.classList.toggle('is-inapplicable', !discount.applicable);\n });\n });\n}\n\nexport function renderCart(view: CartView): void {\n const { cart } = view;\n const money = (key: 'subtotalAmount' | 'totalAmount' | 'checkoutChargeAmount') =>\n cart ? formatMoney(cart.cost[key]) : '';\n\n syncDrawerAria(isOpen());\n qsa(document, sel('cart-count')).forEach((el) => {\n setTextTracked(el, cart?.totalQuantity ?? 0);\n });\n qsa(document, sel('cart-subtotal')).forEach((el) => {\n setTextTracked(el, money('subtotalAmount'));\n });\n qsa(document, sel('cart-total')).forEach((el) => {\n setTextTracked(el, money('totalAmount'));\n });\n qsa(document, sel('cart-checkout-charge')).forEach((el) => {\n setTextTracked(el, money('checkoutChargeAmount'));\n });\n renderConditions(cart, view);\n qsa(document, sel('cart-warning')).forEach((el) => {\n setTextTracked(el, [...new Set(view.warnings.map((warning) => warning.message))].join(' '));\n });\n renderFields(cart);\n qsa(document, sel('cart-lines')).forEach((container) => {\n repeatKeyed(\n container,\n cart?.lines.nodes ?? [],\n (line) => line.id,\n (node, line) => {\n renderLine(node, line, view);\n },\n { onEnter: cfg.onLineEnter, onLeave: cfg.onLineLeave },\n );\n });\n if (cart) notifyLayoutChanged('cart');\n}\n\n/** Shows the count a saved cart had on the last page in the cart counts the page left at 0, until it has loaded. */\nexport function showSavedCount(count: number): void {\n qsa(document, sel('cart-count')).forEach((el) => {\n if (/^\\s*0?\\s*$/.test(el.textContent)) el.textContent = String(count);\n });\n}\n", "/**\n * The browser's `localStorage` and `sessionStorage`, which may be blocked: accessing them throws in private mode, in\n * sandboxed frames and where the visitor blocks site data, and writing throws when they are full. Tether then keeps\n * nothing between pages: a read finds nothing, and a write or removal does nothing. Each call catches on its own, so a\n * blocked area never stops a call on the other.\n */\n\ntype StorageArea = 'localStorage' | 'sessionStorage';\n\n/** @returns `null` when nothing is stored under `key`, or the storage is blocked. */\nexport function getStored(key: string, area: StorageArea = 'localStorage'): string | null {\n try {\n return window[area].getItem(key);\n } catch {\n // Blocked storage holds nothing: see the file's comment.\n return null;\n }\n}\n\nexport function setStored(key: string, value: string, area: StorageArea = 'localStorage'): void {\n try {\n window[area].setItem(key, value);\n } catch {\n // Blocked or full storage keeps nothing: see the file's comment.\n }\n}\n\nexport function removeStored(key: string, area: StorageArea = 'localStorage'): void {\n try {\n window[area].removeItem(key);\n } catch {\n // Blocked storage has nothing to remove: see the file's comment.\n }\n}\n", "/** A Storefront cart id: what the browser keeps for the visitor's cart and sends the Worker to link or unlink it. */\nexport const CART_ID = /^gid:\\/\\/shopify\\/Cart\\/[\\w-]+(\\?key=[\\w-]+)?$/;\n", "/**\n * The cart on this page: what Shopify last said about it (`CartStatus`), the quantities quick clicks asked for and\n * the one queue every change goes through. A change of what the page shows goes through\n * here, so the document (`renderCart`) and the listeners (`tether:cart-updated`) always see the store's cart.\n */\nimport { cfg } from '../config.ts';\nimport { emit } from '../dom.ts';\nimport { isOpen } from './drawer.ts';\nimport { renderCart, quantityOf, showSavedCount } from './render.ts';\nimport { getStored, removeStored, setStored } from '../storage.ts';\nimport { CART_ID } from '../../server/src/shared/cart-id.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { Image } from '../fragments.ts';\nimport type { Money } from '../shopify-values.ts';\n\nexport interface Attribute {\n key: string;\n value: string;\n}\n\n/** A line's `merchandise`: Shopify's `Merchandise` union has only `ProductVariant`. */\nexport interface CartVariant {\n id: string;\n title: string;\n sku: string | null;\n availableForSale: boolean;\n requiresShipping: boolean;\n /** Absent when the store's token can't read inventory. */\n quantityAvailable?: number | null;\n image: Image | null;\n price: Money;\n compareAtPrice: Money | null;\n selectedOptions: { name: string; value: string }[];\n product: { id: string; title: string; handle: string; vendor: string; featuredImage: Image | null };\n}\n\ninterface LinePlanAllocation {\n sellingPlan: {\n id: string;\n name: string;\n description: string | null;\n recurringDeliveries: boolean;\n options: { name: string; value: string }[];\n };\n checkoutChargeAmount: Money;\n priceAdjustments: { price: Money; compareAtPrice: Money; perDeliveryPrice: Money }[];\n}\n\nexport interface ShopifyCartLine {\n id: string;\n quantity: number;\n attributes: Attribute[];\n cost: {\n totalAmount: Money;\n subtotalAmount: Money;\n amountPerQuantity: Money;\n compareAtAmountPerQuantity: Money | null;\n };\n discountAllocations: { discountedAmount: Money }[];\n sellingPlanAllocation: LinePlanAllocation | null;\n merchandise: CartVariant;\n /** Only on a bundle (`ComponentizableCartLine`). */\n lineComponents?: {\n id: string;\n quantity: number;\n merchandise: { id: string; title: string; product: { title: string; handle: string } };\n }[];\n}\n\n/** The fields of `fragment TetherCart`. */\nexport interface ShopifyCart {\n id: string;\n checkoutUrl: string;\n totalQuantity: number;\n note: string | null;\n createdAt: string;\n updatedAt: string;\n attributes: Attribute[];\n buyerIdentity: { countryCode: string | null; email: string | null };\n discountCodes: { code: string; applicable: boolean }[];\n cost: { subtotalAmount: Money; totalAmount: Money; checkoutChargeAmount: Money };\n lines: { nodes: ShopifyCartLine[] };\n}\n\n/**\n * `unknown` until the saved cart has loaded (the page's own markup shows); `empty` without a cart (none saved, or\n * Shopify no longer has it); `error` after a failed load or change, with the cart as Shopify last sent it.\n */\nexport type CartStatus =\n | { kind: 'unknown' }\n | { kind: 'empty' }\n | { kind: 'ready'; cart: ShopifyCart }\n | { kind: 'error'; error: unknown; cart: ShopifyCart | null };\n\n/** What one quick +/- click asked for; a newer click replaces it. */\nexport interface PendingQuantity {\n quantity: number;\n}\n\n/** The second argument of a `subscribe` listener. */\ninterface CartMeta {\n open: boolean;\n count: number;\n loaded: boolean;\n /** A copy of Shopify's notes on the last change. */\n warnings: CartWarning[];\n}\n\nlet status: CartStatus = { kind: 'unknown' };\n\n/** Shopify's notes on the last change. */\nlet warnings: readonly CartWarning[] = [];\n\n/** Line id \u2192 what quick +/- clicks asked for, until Shopify has answered. */\nexport const pendingQuantities = new Map<string, PendingQuantity>();\n\nexport const cartStatus = (): CartStatus => status;\n\n/** The cart as Shopify last sent it. */\nexport function currentCart(): ShopifyCart | null {\n switch (status.kind) {\n case 'ready':\n case 'error':\n return status.cart;\n case 'unknown':\n case 'empty':\n return null;\n }\n}\n\nexport const currentWarnings = (): readonly CartWarning[] => warnings;\n\nconst statusOf = (cart: ShopifyCart | null): CartStatus => (cart ? { kind: 'ready', cart } : { kind: 'empty' });\n\nconst seenKey = () => `${cfg.storageKey}:seen`;\n\n/**\n * The id of the visitor's cart, kept across pages. A stored value that isn't a cart id (written by another script,\n * or edited by hand) is forgotten, so it is never sent to Shopify or the Worker and the next change starts a new cart.\n */\nexport function savedCartId(): string | null {\n const stored = getStored(cfg.storageKey);\n if (!stored) return null;\n if (CART_ID.test(stored)) return stored;\n forgetSavedCart();\n return null;\n}\n\n/** Forgets the saved cart, which Shopify no longer has. */\nexport function forgetSavedCart(): void {\n removeStored(cfg.storageKey);\n}\n\n/** Keeps a cart Shopify sent without showing it, for an answer a newer change will replace. */\nexport function keepQuietly(cart: ShopifyCart | null): void {\n status = statusOf(cart);\n if (cart) setStored(cfg.storageKey, cart.id);\n}\n\n/** Renders the document with the store's cart. */\nexport function render(): void {\n renderCart({\n cart: currentCart(),\n warnings,\n pending: pendingQuantities,\n failed: status.kind === 'error',\n });\n}\n\n/** @param reason The `reason` of `tether:cart-updated`. */\nexport function announce(reason: string): void {\n render();\n emit('cart-updated', { cart: currentCart(), reason });\n}\n\n/**\n * Keeps a cart Shopify sent after a change, shows it, and tells this page and the visitor's other tabs.\n *\n * @returns `cart`.\n */\nexport function commit(cart: ShopifyCart | null, reason: string): ShopifyCart | null {\n keepQuietly(cart);\n // Other tabs reload the cart on this storage event; the next page shows the count before its cart has loaded.\n setStored(seenKey(), `${Date.now()}|${cart?.totalQuantity ?? 0}`);\n announce(reason);\n return cart;\n}\n\n/** The outcome of loading the saved cart: the cart, `null` when there is none, or the failure. */\nexport function settleLoad(\n outcome: { kind: 'loaded'; cart: ShopifyCart | null } | { kind: 'failed'; error: unknown },\n): void {\n status = outcome.kind === 'loaded' ? statusOf(outcome.cart) : { kind: 'error', error: outcome.error, cart: null };\n announce('load');\n}\n\n/**\n * The cart's count on the last page, from `<storageKey>:seen` (`<time>|<count>`).\n *\n * @returns `null` unless the count is a whole number: other scripts and other Tether versions write storage too.\n */\nfunction lastKnownCount(): number | null {\n const text = getStored(seenKey())?.split('|')[1] ?? '';\n const count = Number(text);\n return /^\\d+$/.test(text) && Number.isSafeInteger(count) ? count : null;\n}\n\n/** Until the saved cart has loaded, the counts show the count it had on the last page instead of the page's 0. */\nexport function showLastKnownCount(): void {\n if (status.kind !== 'unknown') return;\n const count = lastKnownCount();\n if (count !== null) showSavedCount(count);\n}\n\n/** Sets Shopify's notes on the last change: each message once, in the site's words (`warningText`). */\nexport function setWarnings(notes: readonly CartWarning[]): void {\n warnings = notes\n .filter((note, index) => notes.findIndex((other) => other.message === note.message) === index)\n .map((note) => ({ ...note, message: cfg.warningText?.(note) || note.message }));\n if (warnings.length) emit('cart-warning', { warnings });\n}\n\n/** The queue only waits for a task: its result, or failure, reaches the caller through the promise `run` returns. */\nconst waitOnly = () => undefined;\n\n/** @returns A runner that starts each task once the one before has settled, whether it succeeded or failed. */\nexport function serial(): <T>(task: () => Promise<T>) => Promise<T> {\n let previous: Promise<unknown> = Promise.resolve();\n return <T>(task: () => Promise<T>) => {\n const run = previous.then(task);\n previous = run.then(waitOnly, waitOnly);\n return run;\n };\n}\n\nconst runInOrder = serial();\n\n/**\n * Runs a cart change after the ones before it, so quick clicks never race. A failure sets the `error` status (shown by\n * `tether-show=\"cart-error\"` until the next change succeeds), emits `tether:cart-error` and rejects.\n */\nexport function enqueue<T>(task: () => Promise<T>): Promise<T> {\n return runInOrder(task).then(\n (value) => {\n if (status.kind === 'error') {\n status = statusOf(status.cart);\n render();\n }\n return value;\n },\n (error: unknown) => {\n status = { kind: 'error', error, cart: currentCart() };\n render();\n emit('cart-error', { error });\n throw error;\n },\n );\n}\n\n/** @returns The quantity to show for `line`. */\nexport const pendingQuantityOf = (line: ShopifyCartLine): number => quantityOf(line, pendingQuantities);\n\nconst meta = (): CartMeta => ({\n open: isOpen(),\n count: currentCart()?.totalQuantity ?? 0,\n loaded: status.kind !== 'unknown',\n warnings: [...warnings],\n});\n\n/**\n * Calls `listener` now and after every change of the cart or the drawer.\n *\n * @returns Stops listening.\n */\nexport function subscribe(listener: (cart: ShopifyCart | null, meta: CartMeta) => void): () => void {\n const events = ['cart-updated', 'cart-toggled'].map((name) => `${cfg.prefix}:${name}`);\n const call = () => {\n listener(currentCart(), meta());\n };\n events.forEach((name) => {\n document.addEventListener(name, call);\n });\n call();\n return () => {\n events.forEach((name) => {\n document.removeEventListener(name, call);\n });\n };\n}\n", "/**\n * The Storefront Cart API: loading the saved cart, creating one, and the mutations that change it. Shopify forgets a\n * cart some days after its last change and once it has been checked out; a change that needs a cart then creates a\n * new one with the same input, so the visitor's click still lands.\n */\nimport { cfg, log } from '../config.ts';\nimport { gql, orNull, shaped, unwrap, TetherError } from '../client.ts';\nimport { cartFragment } from '../fragments.ts';\nimport { GID } from '../selling-plans.ts';\nimport { toAttributes } from '../dom.ts';\nimport { CART } from './cart-shape.ts';\nimport { savedCartId, forgetSavedCart, setWarnings } from './store.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { QueryText, UserError } from '../client.ts';\nimport type { AttributeValue } from '../dom.ts';\nimport type { ShopifyCart, Attribute } from './store.ts';\n\nexport type Attributes = Record<string, AttributeValue> | { key: string | number; value?: AttributeValue }[];\n\n/** A line to add, in any of the shapes `Cart.add` takes. */\nexport interface LineSpec {\n variantId?: string | number;\n merchandiseId?: string | number;\n id?: string | number;\n quantity?: number | string;\n sellingPlanId?: string | number | null;\n sellingPlan?: string | number | null;\n attributes?: Attributes | null;\n}\n\n/** Shopify's `CartLineInput`. */\nexport interface CartLineInput {\n merchandiseId: string;\n quantity: number;\n sellingPlanId?: string;\n attributes?: Attribute[];\n}\n\n/** What every cart mutation answers. Its lists are read with a fallback, so `isPayloadOf` checks the cart alone. */\ninterface CartPayload {\n cart: ShopifyCart | null;\n userErrors?: UserError[] | null;\n warnings?: CartWarning[];\n}\n\n/**\n * Shopify answers a cart id it can't resolve, in a mutation, with a top-level GraphQL error whose only detail is\n * this message (no `extensions.code`). A user error on the `cartId` field is recognised by its shape instead.\n */\nconst UNKNOWN_CART_MESSAGE = /does not exist|not found|invalid id/i;\n\n/** @returns A check of a mutation's answer: a `null` payload (which `unwrap` refuses), or one with a cart or `null`. */\nconst isPayloadOf = (name: string) =>\n shaped<Record<string, CartPayload | null>>({ [name]: orNull({ cart: orNull(CART) }) });\n\n/**\n * @returns True when `error` says Shopify doesn't have the cart, false for any other failure (the network, the token,\n * a line it refused), after which the saved cart is still good.\n */\nexport function isCartGone(error: unknown): boolean {\n if (!(error instanceof TetherError)) return false;\n if (error.userErrors?.some((userError) => userError.field?.[0] === 'cartId')) return true;\n return (error.errors ?? []).some((graphQLError) => UNKNOWN_CART_MESSAGE.test(graphQLError.message));\n}\n\n/**\n * @returns True when a change failed without saying whether Shopify applied it: no answer or no whole body came (the\n * network), a 2xx whose body isn't valid, or a server error (a 5xx, or a 2xx with `SERVER_ERROR`), which can come\n * after the change was committed. Shopify's user errors and a 4xx are clear.\n */\nexport function isUnclearFailure(error: unknown): boolean {\n if (!(error instanceof TetherError)) return false;\n const { code, status = 0 } = error;\n return code === 'NETWORK' || code === 'INVALID_RESPONSE' || code === 'SERVER_ERROR' || status >= 500;\n}\n\n/**\n * @param input A variant id, or a line.\n * @param quantity For a variant id.\n * @param options For a variant id.\n * @throws {TetherError} `ERROR` without a variant id.\n */\nexport function toLineInput(\n input: string | number | LineSpec | null | undefined,\n quantity = 1,\n options: Omit<LineSpec, 'variantId' | 'quantity'> = {},\n): CartLineInput {\n const spec: LineSpec =\n typeof input === 'object' && input !== null\n ? input\n : { ...options, quantity, ...(input === null || input === undefined ? {} : { variantId: input }) };\n const merchandiseId = GID.variant(spec.merchandiseId || spec.variantId || spec.id);\n if (!merchandiseId) throw new TetherError('add(): variantId is required');\n const asked = parseInt(String(spec.quantity ?? 1), 10);\n const line: CartLineInput = { merchandiseId, quantity: Math.max(1, asked || 1) };\n const planId = GID.sellingPlan(spec.sellingPlanId || spec.sellingPlan);\n if (planId) line.sellingPlanId = planId;\n const attributes = toAttributes(spec.attributes);\n if (attributes.length) line.attributes = attributes;\n return line;\n}\n\n/**\n * @param name The mutation, which is also its payload's field.\n * @param args The operation's variable definitions.\n * @param inner The mutation field's arguments.\n */\nfunction mutationText(name: string, args: string, inner: string): QueryText {\n return (access) => `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message target}}}${cartFragment(access)}`;\n}\n\n/**\n * @returns The cart in Shopify's answer, after keeping its warnings.\n * @throws {TetherError} For a failed request, or the mutation's user errors.\n */\nasync function send(\n name: string,\n args: string,\n inner: string,\n variables: Record<string, unknown>,\n): Promise<ShopifyCart | null> {\n const data = await gql(mutationText(name, args, inner), variables, isPayloadOf(name), { operation: 'mutation' });\n const payload = unwrap(data[name], name);\n setWarnings(payload.warnings ?? []);\n return payload.cart;\n}\n\n/**\n * A new cart in the visitor's market.\n *\n * @param input More of Shopify's `CartInput`, e.g. the first lines.\n */\nfunction createCart(input: Record<string, unknown>): Promise<ShopifyCart | null> {\n const withIdentity = cfg.country ? { buyerIdentity: { countryCode: cfg.country }, ...input } : input;\n return send('cartCreate', '$input: CartInput!', 'input: $input', { input: withIdentity });\n}\n\n/** A mutation on the saved cart. */\ninterface CartMutation {\n /** The mutation, e.g. `cartLinesAdd`. */\n name: string;\n /** Its variable definitions besides `$cartId`. */\n args: string;\n /** Its arguments besides `cartId`. */\n inner: string;\n variables: Record<string, unknown>;\n /**\n * The `CartInput` of a new cart that does what the mutation asks, when there is no cart (or Shopify no longer has\n * it); `null` leaves the visitor without a cart instead.\n */\n createInput: Record<string, unknown> | null;\n}\n\n/**\n * Runs a mutation on the saved cart, or creates a cart from `createInput` when there is none to change.\n *\n * @returns The changed cart; `null` without a cart and without a `createInput`.\n * @throws {TetherError} For a failed request or Shopify's user errors; also when the cart is gone and there is no\n * `createInput` (the saved cart is forgotten first).\n */\nexport async function mutateCart({\n name,\n args,\n inner,\n variables,\n createInput,\n}: CartMutation): Promise<ShopifyCart | null> {\n const cartId = savedCartId();\n if (!cartId) return createInput ? createCart(createInput) : null;\n try {\n return await send(name, `$cartId: ID!, ${args}`, `cartId: $cartId, ${inner}`, { ...variables, cartId });\n } catch (error) {\n if (!isCartGone(error)) throw error;\n forgetSavedCart();\n if (!createInput) throw error;\n log('cart expired, creating a new one');\n return createCart(createInput);\n }\n}\n\n/** @returns The cart, `null` when Shopify doesn't have it (expired or checked out). */\nexport async function fetchCart(id: string): Promise<ShopifyCart | null> {\n const data = await gql(\n (access) => `query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${cartFragment(access)}`,\n { id },\n shaped<{ cart: ShopifyCart | null }>({ cart: orNull(CART) }),\n );\n return data.cart;\n}\n\n/** @returns The cart's current checkout URL, `null` when Shopify doesn't have the cart. */\nexport async function fetchCheckoutUrl(id: string): Promise<string | null> {\n const data = await gql(\n 'query TetherCheckout($id: ID!) { cart(id: $id) { checkoutUrl } }',\n { id },\n shaped<{ cart: { checkoutUrl: string } | null }>({ cart: orNull({ checkoutUrl: 'string' }) }),\n );\n return data.cart?.checkoutUrl ?? null;\n}\n\n/** A change to one line, for `updateLine`. */\nexport interface LinePatch {\n /** 0 or less removes the line. */\n quantity?: number;\n /** `null` switches the line back to one-time purchase. */\n sellingPlanId?: string | number | null;\n attributes?: Attributes;\n merchandiseId?: string | number;\n variantId?: string | number;\n}\n\n/** @returns Shopify's `CartLineUpdateInput`. */\nexport function toLineUpdate(lineId: string, patch: number | LinePatch): Record<string, unknown> {\n const change: LinePatch = typeof patch === 'number' ? { quantity: patch } : patch;\n const line: Record<string, unknown> = { id: lineId };\n if (change.quantity !== undefined) line['quantity'] = change.quantity;\n if ('sellingPlanId' in change)\n line['sellingPlanId'] = change.sellingPlanId ? GID.sellingPlan(change.sellingPlanId) : null;\n if (change.attributes) line['attributes'] = toAttributes(change.attributes);\n const variantId = change.merchandiseId || change.variantId;\n if (variantId) line['merchandiseId'] = GID.variant(variantId);\n return line;\n}\n\nexport const updateLines = (lines: Record<string, unknown>[]): Promise<ShopifyCart | null> =>\n mutateCart({\n name: 'cartLinesUpdate',\n args: '$lines: [CartLineUpdateInput!]!',\n inner: 'lines: $lines',\n variables: { lines },\n createInput: null,\n });\n\nexport const removeLines = (lineIds: string[]): Promise<ShopifyCart | null> =>\n mutateCart({\n name: 'cartLinesRemove',\n args: '$lineIds: [ID!]!',\n inner: 'lineIds: $lineIds',\n variables: { lineIds },\n createInput: null,\n });\n\n/** @returns The cart once `lineId` has `target` (0 removes it). */\nexport const setLineQuantity = (lineId: string, target: number): Promise<ShopifyCart | null> =>\n target > 0 ? updateLines([{ id: lineId, quantity: target }]) : removeLines([lineId]);\n", "/**\n * Sending the visitor to Shopify's checkout. The URL comes from Shopify at click time, because the cart's checkout URL\n * changes with its market and customer; the account part resolves a logged-in customer's own (`setCheckoutResolver`).\n */\nimport { cfg, reportError } from '../config.ts';\nimport { TetherError } from '../client.ts';\nimport { isHttpsUrl } from '../shopify-values.ts';\nimport { emit } from '../dom.ts';\nimport { fetchCheckoutUrl } from './mutations.ts';\nimport { currentCart, savedCartId } from './store.ts';\nimport type { CheckoutTarget } from '../config.ts';\n\ntype CheckoutUrlAnswer = { kind: 'url'; url: string | null } | { kind: 'failed'; error: unknown };\n\n/**\n * The logged-in customer's checkout URL for the cart; `null` when nobody is logged in, so the cart's own checkout URL\n * is used.\n */\ntype CheckoutResolver = () => Promise<string | null>;\n\nlet resolveAccountCheckout: CheckoutResolver | null = null;\n\n/** Sets the account part's checkout URL, or clears it with `null`. A URL it answers opens checkout logged in. */\nexport function setCheckoutResolver(resolver: CheckoutResolver | null): void {\n resolveAccountCheckout = resolver;\n}\n\n/**\n * @returns Shopify's answer as a value, so the request can run alongside the account's and its failure is read only\n * when its URL is the one used.\n */\nconst askCheckoutUrl = (cartId: string): Promise<CheckoutUrlAnswer> =>\n fetchCheckoutUrl(cartId).then(\n (url) => ({ kind: 'url', url }),\n (error: unknown) => ({ kind: 'failed', error }),\n );\n\n/**\n * @returns The logged-in customer's checkout URL. When the account can't give one, the failure is reported and the\n * cart's own checkout URL is used, which still checks out (without the login).\n */\nasync function accountCheckoutUrl(): Promise<string | null> {\n if (!resolveAccountCheckout) return null;\n try {\n const url = await resolveAccountCheckout();\n // Shopify's checkout shows the customer as logged in only with this parameter.\n return url && `${url}${url.includes('?') ? '&' : '?'}logged_in=true`;\n } catch (error) {\n reportError(error, 'account checkout');\n return null;\n }\n}\n\n/** @throws {TetherError} `ERROR` without a checkout URL (the cart is empty or Shopify no longer has it). */\nasync function checkoutUrl(cartId: string): Promise<string> {\n const cartAnswer = askCheckoutUrl(cartId);\n const accountUrl = await accountCheckoutUrl();\n if (accountUrl) return accountUrl;\n const answer = await cartAnswer;\n if (answer.kind === 'failed') throw answer.error;\n if (!answer.url) throw new TetherError('No checkoutUrl (cart empty or expired)');\n return answer.url;\n}\n\n/**\n * A new tab's `opener` gives the checkout page a handle on this one; a browser that doesn't let a script clear it\n * leaves the tab as it opened.\n */\nfunction detachOpenerIfAllowed(tab: Window) {\n try {\n tab.opener = null;\n } catch {\n // The browser keeps `opener` read-only: see the function's comment.\n }\n}\n\n/**\n * Goes to Shopify's checkout. With `_blank` the tab opens inside the click (Safari blocks `window.open` after an\n * await) and gets its URL once there is one.\n *\n * @returns The checkout URL, `null` with nothing to check out or after a reported failure.\n */\nexport async function checkout(target: CheckoutTarget = cfg.checkoutTarget): Promise<string | null> {\n const cartId = savedCartId();\n const cart = currentCart();\n if (!cartId || (cart && !cart.totalQuantity)) return null;\n const tab = target === '_blank' ? window.open('', '_blank') : null;\n if (tab) detachOpenerIfAllowed(tab);\n try {\n const url = await checkoutUrl(cartId);\n if (!isHttpsUrl(url)) throw new TetherError('The checkout URL is not https: not opened');\n emit('checkout', { url, cart: currentCart() });\n if (tab && !tab.closed) tab.location.href = url;\n else window.location.href = url;\n return url;\n } catch (error) {\n tab?.close();\n reportError(error, 'checkout failed');\n return null;\n }\n}\n", "/**\n * The cart, as one object: `Tether.cart`. Every change goes through one queue (fast clicks never race) and resolves\n * with the cart Shopify sent back. The state lives in `cart/store.ts`, the Cart API calls in `cart/mutations.ts`, the\n * drawer in `cart/drawer.ts`, the document rendering in `cart/render.ts` and the checkout in `cart/checkout.ts`.\n */\nimport { cfg, reportError } from './config.ts';\nimport { TetherError } from './client.ts';\nimport { emit, toAttributes } from './dom.ts';\nimport {\n fetchCart,\n isCartGone,\n isUnclearFailure,\n mutateCart,\n removeLines,\n setLineQuantity,\n toLineInput,\n toLineUpdate,\n updateLines,\n} from './cart/mutations.ts';\nimport { checkout, setCheckoutResolver } from './cart/checkout.ts';\nimport { drawers, isOpen, openCart, closeCart, toggleCart, trapFocus } from './cart/drawer.ts';\nimport {\n announce,\n cartStatus,\n commit,\n currentCart,\n currentWarnings,\n enqueue,\n forgetSavedCart,\n keepQuietly,\n pendingQuantities,\n pendingQuantityOf,\n render,\n savedCartId,\n settleLoad,\n showLastKnownCount,\n subscribe,\n} from './cart/store.ts';\nimport type { CartWarning } from './config.ts';\nimport type { Attributes, LineSpec, CartLineInput, LinePatch } from './cart/mutations.ts';\nimport type { ShopifyCart, ShopifyCartLine, CartStatus } from './cart/store.ts';\n\nexport { trapFocus };\n\ntype DiscountCodes = string[] | string | ((current: string[]) => string[]);\n\nconst currentLines = (): readonly ShopifyCartLine[] => currentCart()?.lines.nodes ?? [];\n\n/**\n * Loads the saved cart again after a change whose outcome is unclear, so this page and the visitor's other tabs show\n * what Shopify has. A failure here is reported; the change's own failure is what its caller hears.\n */\nasync function reloadAfterUnclearFailure(): Promise<void> {\n const id = savedCartId();\n if (!id) return;\n try {\n const cart = await fetchCart(id);\n if (!cart) forgetSavedCart();\n commit(cart, 'load');\n } catch (error) {\n reportError(error, 'cart reload after a failed change');\n }\n}\n\n/**\n * Runs a cart change in the queue (`enqueue`). After a failure that doesn't say whether Shopify applied the change,\n * the cart is loaded again before the next change runs; the change still rejects.\n */\nfunction runChange<T>(task: () => Promise<T>): Promise<T> {\n return enqueue(() =>\n task().catch(async (error: unknown) => {\n if (isUnclearFailure(error)) await reloadAfterUnclearFailure();\n throw error;\n }),\n );\n}\n\n/**\n * +1 / -1 for a line. Quick clicks add up: each builds on the quantity the click before asked for, which shows at once\n * (`tether-line=\"quantity\"`, `quantityOf`) until Shopify has answered. Only the latest target is sent; the ones it\n * replaced are skipped.\n */\nfunction changeQuantity(lineId: string, delta: number): Promise<ShopifyCart | null> {\n const line = currentLines().find((candidate) => candidate.id === lineId);\n if (!line) return Promise.resolve(currentCart());\n const target = Math.max(0, pendingQuantityOf(line) + delta);\n const job = { quantity: target };\n pendingQuantities.set(lineId, job);\n announce('pending');\n const isLatest = () => pendingQuantities.get(lineId) === job;\n const sent = runChange(async () => {\n if (!isLatest()) return currentCart();\n const now = currentLines().find((candidate) => candidate.id === lineId);\n // A + then a -: Shopify already has the quantity.\n if (now?.quantity === target) return currentCart();\n const cart = await setLineQuantity(lineId, target);\n // A newer click is on its way: keep this answer, but show totals and count once that one is in, instead of\n // stepping through every reply.\n if (!isLatest()) {\n keepQuietly(cart);\n return cart;\n }\n return commit(cart, target > 0 ? 'update' : 'remove');\n });\n return sent.finally(() => {\n if (!isLatest()) return;\n pendingQuantities.delete(lineId);\n announce('settled');\n });\n}\n\n/**\n * The codes the cart has once a change is its turn in the queue.\n *\n * @param codes A function gets the codes as they are by then.\n */\nfunction discountCodesFrom(codes: DiscountCodes): string[] {\n const current = (currentCart()?.discountCodes ?? []).map((discount) => discount.code);\n const list = typeof codes === 'function' ? codes(current) : codes;\n return (Array.isArray(list) ? list : [list]).map((code) => code.trim()).filter(Boolean);\n}\n\n/** @returns The cart's attributes with `attributes` merged in; `''` removes a key. */\nfunction mergedAttributes(attributes: Attributes): { key: string; value: string }[] {\n const merged = new Map((currentCart()?.attributes ?? []).map((attribute) => [attribute.key, attribute.value]));\n toAttributes(attributes).forEach((attribute) => merged.set(attribute.key, attribute.value));\n return [...merged].filter(([, value]) => value !== '').map(([key, value]) => ({ key, value }));\n}\n\n/**\n * Loads the saved cart. A cart Shopify no longer has is forgotten; one it can't be asked about (the network, the\n * token) is kept, because it is probably fine.\n */\nasync function init(): Promise<ShopifyCart | null> {\n const id = savedCartId();\n if (!id) {\n settleLoad({ kind: 'loaded', cart: null });\n return null;\n }\n showLastKnownCount();\n try {\n const cart = await fetchCart(id);\n if (!cart) forgetSavedCart();\n settleLoad({ kind: 'loaded', cart });\n } catch (error) {\n reportError(error, 'cart load failed');\n if (isCartGone(error)) forgetSavedCart();\n settleLoad({ kind: 'failed', error });\n }\n return currentCart();\n}\n\nexport const Cart = {\n init,\n /** The saved cart's id. */\n id: savedCartId,\n\n get status(): CartStatus {\n return cartStatus();\n },\n get state(): ShopifyCart | null {\n return currentCart();\n },\n /** The cart's lines, a copy: changing it changes nothing in the cart. */\n get lines(): ShopifyCartLine[] {\n return [...currentLines()];\n },\n get count(): number {\n return currentCart()?.totalQuantity ?? 0;\n },\n /** True once the saved cart, or that there is none, is known. */\n get loaded(): boolean {\n return cartStatus().kind !== 'unknown';\n },\n /**\n * The failure of the last load or change, until a change succeeds. Requests fail with an `Error`; anything else\n * thrown is wrapped, so the public type holds.\n */\n get lastError(): Error | null {\n const current = cartStatus();\n if (current.kind !== 'error') return null;\n return current.error instanceof Error ? current.error : new Error(String(current.error));\n },\n /** Shopify's notes on the last change, a copy. */\n get warnings(): CartWarning[] {\n return [...currentWarnings()];\n },\n /** Line id \u2192 the quantity quick clicks asked for. */\n get pendingQty(): ReadonlyMap<string, { quantity: number }> {\n return pendingQuantities;\n },\n\n /**\n * Sets where a logged-in customer checks out, or clears it with `null`: the account part's resolver answers the\n * customer's checkout URL for the cart (`null` uses the cart's own).\n */\n setCheckoutResolver,\n\n /**\n * Forgets the saved cart on this device, as if there were none: the page shows an empty cart and\n * `tether:cart-updated` fires with reason `load`. The next add starts a new cart.\n */\n forgetSaved(): void {\n forgetSavedCart();\n settleLoad({ kind: 'loaded', cart: null });\n },\n\n render,\n subscribe,\n\n /**\n * `add(variantId, 2)`, `add({ variantId, quantity, sellingPlanId, attributes })`, or `add([line, line])` for\n * bundles in one request. Creates the cart when there is none.\n *\n * @throws {TetherError} (rejected) When the add fails, or Shopify answers it without a cart.\n */\n add(\n input: string | number | LineSpec | (string | number | LineSpec)[],\n quantity = 1,\n options: Omit<LineSpec, 'variantId' | 'quantity'> = {},\n ): Promise<ShopifyCart> {\n const lines: CartLineInput[] = (Array.isArray(input) ? input : [input]).map((line) =>\n toLineInput(line, quantity, options),\n );\n return runChange(async () => {\n const cart = await mutateCart({\n name: 'cartLinesAdd',\n args: '$lines: [CartLineInput!]!',\n inner: 'lines: $lines',\n variables: { lines },\n createInput: { lines },\n });\n // A mutation answers without a cart only beside user errors, which reject above; an add always has one.\n if (!cart) throw new TetherError('Shopify answered the add without a cart');\n commit(cart, 'add');\n emit('added', { cart, lines });\n if (cfg.openCartOnAdd) openCart();\n return cart;\n });\n },\n\n /**\n * `updateLine(lineId, 3)` or `updateLine(lineId, { quantity, sellingPlanId, attributes })`.\n *\n * @param patch A quantity of 0 or less removes the line.\n */\n updateLine(lineId: string, patch: number | LinePatch): Promise<ShopifyCart | null> {\n const quantity = typeof patch === 'number' ? patch : patch.quantity;\n if (quantity !== undefined && quantity <= 0) return Cart.removeLine(lineId);\n const line = toLineUpdate(lineId, patch);\n return runChange(async () => commit(await updateLines([line]), 'update'));\n },\n\n changeQuantity,\n\n /** The quantity to show: the one asked for while an update is on its way. */\n quantityOf: pendingQuantityOf,\n\n removeLine(lineIds: string | string[]): Promise<ShopifyCart | null> {\n const ids = Array.isArray(lineIds) ? lineIds : [lineIds];\n return runChange(async () => commit(await removeLines(ids), 'remove'));\n },\n\n /** Removes the lines the cart has when the change runs, so one queued just before it is cleared too. */\n clear(): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const ids = currentLines().map((line) => line.id);\n if (!ids.length) return currentCart();\n return commit(await removeLines(ids), 'remove');\n });\n },\n\n setNote(note: string | null | undefined): Promise<ShopifyCart | null> {\n const variables = { note: note ?? '' };\n return runChange(async () =>\n commit(\n await mutateCart({\n name: 'cartNoteUpdate',\n args: '$note: String!',\n inner: 'note: $note',\n variables,\n createInput: variables,\n }),\n 'note',\n ),\n );\n },\n\n /**\n * Cart attributes, merged with the cart's (as it is when the change runs, so quick calls don't drop each other).\n *\n * @param attributes The value `''` removes a key.\n */\n setAttributes(attributes: Attributes): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const variables = { attributes: mergedAttributes(attributes) };\n const cart = await mutateCart({\n name: 'cartAttributesUpdate',\n args: '$attributes: [AttributeInput!]!',\n inner: 'attributes: $attributes',\n variables,\n createInput: variables,\n });\n return commit(cart, 'attributes');\n });\n },\n\n /** Replaces the discount codes; `[]` removes them all. */\n setDiscountCodes(codes: DiscountCodes): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const variables = { discountCodes: discountCodesFrom(codes) };\n const cart = await mutateCart({\n name: 'cartDiscountCodesUpdate',\n args: '$discountCodes: [String!]!',\n inner: 'discountCodes: $discountCodes',\n variables,\n createInput: variables,\n });\n return commit(cart, 'discount');\n });\n },\n\n /**\n * Adds a code. A code Shopify doesn't accept is removed again, so it doesn't warn on every later change.\n *\n * @throws {TetherError} `INVALID_DISCOUNT` (rejected) when Shopify doesn't accept the code.\n */\n async applyDiscount(code: string): Promise<ShopifyCart | null> {\n const cart = await Cart.setDiscountCodes((current) => [...new Set([...current, code])]);\n const wanted = code.toLowerCase();\n const applied = (cart?.discountCodes ?? []).find((discount) => discount.code.toLowerCase() === wanted);\n if (applied?.applicable) return cart;\n try {\n await Cart.removeDiscount(code);\n } catch (error) {\n reportError(error, 'removing a discount code Shopify refused');\n }\n throw new TetherError(`Discount code not valid: ${code}`, { code: 'INVALID_DISCOUNT' });\n },\n\n removeDiscount(code: string): Promise<ShopifyCart | null> {\n const unwanted = code.toLowerCase();\n return Cart.setDiscountCodes((current) => current.filter((existing) => existing.toLowerCase() !== unwanted));\n },\n\n /**\n * Sets who is buying; a country changes the cart's currency and prices to that market's.\n *\n * @param options `create: false` forgets an expired cart instead of creating an empty one.\n */\n setBuyerIdentity(\n buyerIdentity: { countryCode?: string; email?: string; phone?: string },\n { create = true }: { create?: boolean } = {},\n ): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const cart = await mutateCart({\n name: 'cartBuyerIdentityUpdate',\n args: '$buyerIdentity: CartBuyerIdentityInput!',\n inner: 'buyerIdentity: $buyerIdentity',\n variables: { buyerIdentity },\n createInput: create ? { buyerIdentity } : null,\n }).catch((error: unknown) => {\n // mutateCart has forgotten the cart Shopify no longer has; without `create` that leaves no cart.\n if (create || !isCartGone(error)) throw error;\n return null;\n });\n return commit(cart, 'buyer-identity');\n });\n },\n\n checkout,\n\n drawers,\n isOpen,\n open: openCart,\n close: closeCart,\n toggle: toggleCart,\n};\n", "/**\n * What a product scope shows for the visitor's choice: the variant, the purchase it is bought as, its prices, the\n * text of every `tether-bind` and every `tether-show` condition. Pure, no DOM and no config: the browser scope\n * (`product.ts`) and the server's `initialState()` (`initial.ts`) derive their view here, so a server-rendered page\n * shows what the script shows when it takes over.\n */\nimport {\n GID,\n allPlans,\n allocationFor,\n findPlan,\n planAdjustment,\n planCheckoutCharge,\n planPrice,\n planSavings,\n} from './selling-plans.ts';\nimport { fromMinorUnits, toMinorUnits } from './amounts.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\n\n/** Option name \u2192 chosen value. */\nexport type Selection = Record<string, string>;\n/**\n * How the visitor wants to buy. Buying once keeps the plan offered next to the one-time price (the subscription\n * binds `subscription-price` and `plan-name` show it, and subscribing switches to it); `null` for a product without\n * plans.\n */\nexport type Purchase = { kind: 'one-time'; offeredPlanId: string | null } | { kind: 'subscription'; planId: string };\n/** How the line is bought: on a plan only when the visitor subscribes and the variant is on that plan. */\ntype LinePurchase = { kind: 'one-time' } | { kind: 'subscription'; planId: string };\n/** What the visitor chose, before it is checked against the product. */\ninterface ProductChoice {\n selected: Selection;\n purchase: Purchase;\n quantity: number;\n}\nexport type ShowCondition =\n | 'in-stock'\n | 'out-of-stock'\n | 'backorder'\n | 'on-sale'\n | 'not-on-sale'\n | 'has-variants'\n | 'single-variant'\n | 'low-stock'\n | 'plenty-in-stock'\n | 'subscription-available'\n | 'subscription-unavailable'\n | 'subscription-only'\n | 'one-time-available'\n | 'subscription-selected'\n | 'one-time-selected'\n | 'has-savings';\nexport interface ProductView {\n variant: ProductVariant;\n /** The selection, matching `variant`. */\n selected: Selection;\n /** The choice, made valid for the product and `variant`. */\n purchase: Purchase;\n /** The purchase's plan, offered or chosen. */\n plan: PlanInGroup | null;\n /** How the line is bought. */\n line: LinePurchase;\n /** The variant is on at least one plan. */\n subscriptionAvailable: boolean;\n quantity: number;\n /** What one item costs as bought. */\n price: Money;\n /** Only when on sale. */\n compareAtPrice: Money | null;\n /** What `plan` saves on the one-time price. */\n savings: { amount: Money | null; percentage: number };\n show: Record<ShowCondition, boolean>;\n}\n/** How the caller writes values as text. */\nexport interface Formatters {\n money: (money: Money | null) => string;\n percent: (percentage: number) => string;\n}\n/** What a bind writes; `null` and `undefined` clear it. */\ntype BindText = string | number | null | undefined;\n\nconst PERCENT = 100;\n\nconst timesQuantity = (money: Money, quantity: number): Money =>\n fromMinorUnits(toMinorUnits(money) * quantity, money.currencyCode);\n\nexport const selectionOf = (variant: ProductVariant): Selection =>\n Object.fromEntries(variant.selectedOptions.map((option) => [option.name, option.value]));\n\n/** @returns The variant with exactly these option values. */\nfunction findVariant(product: Product, selected: Selection): ProductVariant | null {\n return (\n product.variants.nodes.find((variant) =>\n variant.selectedOptions.every((option) => selected[option.name] === option.value),\n ) ?? null\n );\n}\n\n/**\n * @param name An option name.\n * @returns True when a variant for sale has this value and the other selected values.\n */\nexport function isValueAvailable(product: Product, selected: Selection, name: string, value: string): boolean {\n return product.variants.nodes.some(\n (variant) =>\n variant.availableForSale &&\n variant.selectedOptions.every((option) =>\n option.name === name ? option.value === value : selected[option.name] === option.value,\n ),\n );\n}\n\n/** @returns The plan subscribed on, or offered while buying once. */\nexport const purchasePlanId = (purchase: Purchase): string | null =>\n purchase.kind === 'subscription' ? purchase.planId : purchase.offeredPlanId;\n\n/** @returns On the plan when subscribing and the variant is on it, else once. */\nexport function linePurchase(purchase: Purchase, variant: ProductVariant | null): LinePurchase {\n if (purchase.kind === 'subscription' && allocationFor(variant, purchase.planId)) return purchase;\n return { kind: 'one-time' };\n}\n\n/**\n * The purchase the visitor wants, made valid for the product and variant: one-time without plans, a subscription when\n * the product requires one, and a plan the variant is on when it is on any (else a plan of the product, for the binds\n * to offer).\n *\n * @param kind The way of buying the visitor asked for.\n * @param preferredPlanId The plan to keep when it is the product's: the visitor's last one.\n */\nexport function normalisePurchase(\n product: Product,\n variant: ProductVariant | null,\n kind: Purchase['kind'],\n preferredPlanId: string | null,\n): Purchase {\n const plans = allPlans(product);\n const known = plans.find((plan) => plan.id === preferredPlanId) ?? plans[0];\n if (!known) return { kind: 'one-time', offeredPlanId: null };\n const onVariant = allocationFor(variant, known.id)\n ? known\n : (plans.find((plan) => allocationFor(variant, plan.id)) ?? known);\n if (product.requiresSellingPlan || kind === 'subscription') return { kind: 'subscription', planId: onVariant.id };\n return { kind: 'one-time', offeredPlanId: onVariant.id };\n}\n\ninterface InitialChoiceOptions {\n /** A variant id or gid (`?variant=`); the first variant for sale without. */\n variant?: string | null | undefined;\n /** A plan to subscribe on (`?selling_plan=`). */\n sellingPlan?: string | null | undefined;\n /** The plan offered first, by id, name or option value. */\n defaultPlan?: string | null | undefined;\n /** `subscription` subscribes; anything else buys once. */\n defaultPurchase?: string | null | undefined;\n}\n\n/** What a product starts with before the visitor chooses anything. */\nexport function initialChoice(\n product: Product,\n { variant, sellingPlan, defaultPlan, defaultPurchase }: InitialChoiceOptions,\n): Omit<ProductChoice, 'quantity'> {\n const variants = product.variants.nodes;\n const wantedId = GID.variant(variant);\n const start =\n variants.find((candidate) => candidate.id === wantedId) ??\n variants.find((candidate) => candidate.availableForSale) ??\n variants[0] ??\n null;\n const fromUrl = findPlan(product, sellingPlan);\n const offered = fromUrl ?? findPlan(product, defaultPlan);\n const kind = fromUrl || defaultPurchase === 'subscription' ? 'subscription' : 'one-time';\n return {\n selected: start ? selectionOf(start) : {},\n purchase: normalisePurchase(product, start, kind, offered?.id ?? null),\n };\n}\n\nfunction showConditions(\n variant: ProductVariant,\n lowStock: number,\n variantCount: number,\n sale: Pick<ProductView, 'line' | 'subscriptionAvailable' | 'compareAtPrice' | 'savings'>,\n requiresSellingPlan: boolean,\n): Record<ShowCondition, boolean> {\n const stock = variant.quantityAvailable;\n const low = stock != null && stock > 0 && stock <= lowStock;\n const onSale = sale.compareAtPrice !== null;\n const onPlan = sale.line.kind === 'subscription';\n return {\n 'in-stock': variant.availableForSale,\n 'out-of-stock': !variant.availableForSale,\n backorder: variant.availableForSale && variant.currentlyNotInStock,\n 'on-sale': onSale,\n 'not-on-sale': !onSale,\n 'has-variants': variantCount > 1,\n 'single-variant': variantCount === 1,\n 'low-stock': low,\n 'plenty-in-stock': variant.availableForSale && !low,\n 'subscription-available': sale.subscriptionAvailable,\n 'subscription-unavailable': !sale.subscriptionAvailable,\n 'subscription-only': requiresSellingPlan,\n 'one-time-available': !requiresSellingPlan,\n 'subscription-selected': onPlan,\n 'one-time-selected': !onPlan,\n 'has-savings': onPlan ? sale.savings.percentage > 0 : onSale,\n };\n}\n\n/**\n * The view for the visitor's choice. A selection no variant has falls back to the first variant.\n *\n * @param lowStock `low-stock` at this many or fewer.\n * @returns `null` for a product without variants.\n */\nexport function productView(product: Product, choice: ProductChoice, lowStock: number): ProductView | null {\n const exact = findVariant(product, choice.selected);\n const variant = exact ?? product.variants.nodes[0];\n if (!variant) return null;\n const purchase = normalisePurchase(product, variant, choice.purchase.kind, purchasePlanId(choice.purchase));\n const planId = purchasePlanId(purchase);\n const plans = allPlans(product);\n const line = linePurchase(purchase, variant);\n const allocation = line.kind === 'subscription' ? allocationFor(variant, line.planId) : null;\n // The plan helpers fall back to the one-time price only without a variant, which `variant` rules out.\n const price = allocation ? (planPrice(variant, planId) ?? variant.price) : variant.price;\n const compareAt = allocation?.priceAdjustments[0]?.compareAtPrice ?? variant.compareAtPrice;\n const sale = {\n line,\n subscriptionAvailable: plans.some((plan) => allocationFor(variant, plan.id)),\n compareAtPrice: compareAt && parseFloat(compareAt.amount) > parseFloat(price.amount) ? compareAt : null,\n savings: planSavings(variant, planId),\n };\n return {\n ...sale,\n variant,\n selected: exact ? choice.selected : selectionOf(variant),\n purchase,\n plan: plans.find((plan) => plan.id === planId) ?? null,\n quantity: choice.quantity,\n price,\n show: showConditions(variant, lowStock, product.variants.nodes.length, sale, product.requiresSellingPlan),\n };\n}\n\n/** The `tether-bind` values that follow the variant and purchase. Thunks, so only the binds on the page are computed. */\nexport function variantBindTexts(view: ProductView, format: Formatters): Map<string, () => BindText> {\n const { variant, plan, price, compareAtPrice, quantity } = view;\n const planId = purchasePlanId(view.purchase);\n // Prices for the quantity count one item when it is 0 (an optional bundle item).\n const forQuantity = (money: Money) => timesQuantity(money, Math.max(1, quantity));\n const checkoutCharge = () => {\n const charge = view.line.kind === 'subscription' ? (planCheckoutCharge(variant, planId) ?? price) : price;\n return format.money(forQuantity(charge));\n };\n const discountPercentage = () => {\n if (!compareAtPrice) return '0%';\n return `${Math.round((1 - parseFloat(price.amount) / parseFloat(compareAtPrice.amount)) * PERCENT)}%`;\n };\n const discountAmount = () => {\n if (!compareAtPrice) return '';\n return format.money(fromMinorUnits(toMinorUnits(compareAtPrice) - toMinorUnits(price), price.currencyCode));\n };\n const texts: Record<string, () => BindText> = {\n price: () => format.money(price),\n 'total-price': () => format.money(forQuantity(price)),\n 'one-time-price': () => format.money(variant.price),\n 'subscription-price': () => format.money(planId ? planPrice(variant, planId) : null),\n 'subscription-savings': () => format.percent(view.savings.percentage),\n 'subscription-savings-amount': () => format.money(view.savings.amount),\n 'checkout-charge': checkoutCharge,\n 'plan-name': () => plan?.name,\n 'plan-description': () => plan?.description,\n 'plan-group': () => plan?.group.name,\n 'compare-at-price': () => format.money(compareAtPrice),\n 'unit-price': () => format.money(variant.unitPrice),\n 'discount-percentage': discountPercentage,\n 'discount-amount': discountAmount,\n 'variant-title': () => variant.title,\n 'variant-id': () => variant.id,\n sku: () => variant.sku,\n barcode: () => variant.barcode,\n stock: () => variant.quantityAvailable,\n quantity: () => quantity,\n };\n return new Map(Object.entries(texts));\n}\n/**\n * The `tether-plan-bind` values of one plan in a plan list, for the variant.\n *\n * @param planId The list item's `tether-plan-value`.\n * @param plan The product's plan with that id.\n */\nexport function planBindTexts(\n variant: ProductVariant,\n planId: string,\n plan: PlanInGroup | undefined,\n format: Formatters,\n): Map<string, () => BindText> {\n const adjustmentText = () => {\n const adjustment = planAdjustment(plan);\n if (!adjustment) return '';\n return adjustment.type === 'percentage' ? `${adjustment.value}%` : format.money(adjustment.value);\n };\n const texts: Record<string, () => BindText> = {\n name: () => plan?.name,\n // The option values alone (\"1 month\"), without the price text a store may put in the plan name.\n option: () => (plan?.options ?? []).map((option) => option.value).join(', '),\n description: () => plan?.description,\n price: () => format.money(planPrice(variant, planId)),\n savings: () => format.percent(planSavings(variant, planId).percentage),\n 'savings-amount': () => format.money(planSavings(variant, planId).amount),\n adjustment: adjustmentText,\n };\n return new Map(Object.entries(texts));\n}\n", "/**\n * Decoding of percent-escaped text the page hands Tether (a path segment, a cookie value), which nothing guarantees\n * is well formed.\n */\n\n/** @returns `text` decoded, or as written when it has a malformed escape (`%E0%A4%A`). */\nexport function decodeOrKeep(text: string): string {\n try {\n return decodeURIComponent(text);\n } catch (error) {\n if (error instanceof URIError) return text;\n throw error;\n }\n}\n", "/**\n * Where a product scope gets its key from, and the state of a scope (loading, ready, error, not found), so a failing\n * API never leaves a broken page.\n */\nimport { attr, sel, get, scoped, show } from './dom.ts';\nimport { decodeOrKeep } from './uri.ts';\n\ninterface ProductKeySource {\n /** The product to load; `null` when the attribute or the page URL has none. */\n key: string | null;\n /** The key comes from the page URL (`url:\u2026`). */\n fromUrl: boolean;\n /** The attribute's value as written. */\n raw: string;\n}\n\ntype ScopeStatus = 'loading' | 'ready' | 'error' | 'not-found';\n\nconst URL_SOURCE = 'url:';\n\n/**\n * Reads a `tether-product` value:\n * - `coffee-box`: a handle, numeric id, gid or product URL;\n * - `url:handle`: `?handle=` from the page URL (any parameter: `url:p` reads `?p=`);\n * - `url:path`: the last segment of the page path (`/shop/coffee-box` \u2192 `coffee-box`).\n *\n * @param name The attribute, without prefix.\n */\nexport function resolveProductKey(\n el: Element,\n location: Pick<Location, 'pathname' | 'search'> = window.location,\n name = 'product',\n): ProductKeySource {\n const raw = (get(el, name) ?? '').trim();\n if (!raw.toLowerCase().startsWith(URL_SOURCE)) return { key: raw || null, fromUrl: false, raw };\n const source = raw.slice(URL_SOURCE.length).trim();\n let key;\n if (source.toLowerCase() === 'path') {\n const segments = location.pathname.split('/').filter(Boolean);\n const segment = segments[segments.length - 1];\n // A malformed escape is kept as written, which is how the browser shows it.\n key = segment === undefined ? null : decodeOrKeep(segment);\n } else {\n key = new URLSearchParams(location.search).get(source || 'handle');\n }\n return { key: key?.trim() || null, fromUrl: true, raw };\n}\n\n/** The values of `tether-show` that follow the scope's state. */\nexport const STATUS_KEYS: readonly ScopeStatus[] = ['loading', 'ready', 'error', 'not-found'];\n\nconst isScopeStatus = (value: unknown): value is ScopeStatus => STATUS_KEYS.some((status) => status === value);\n\n/** @returns The error's `code` (a `TetherError` has one). */\nconst codeOf = (error: unknown): unknown =>\n typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n\n/** @returns True for a `TetherError` with code `NOT_FOUND`. */\nexport const isNotFound = (error: unknown): boolean => codeOf(error) === 'NOT_FOUND';\n\n/** @returns The selector of every scope that owns its state elements; the nearest one wins. */\nexport const boundary = (): string =>\n ['product', 'collection', 'products', 'recommendations'].map((name) => sel(name)).join(',');\n\n/** @returns True when `el` belongs to the scope `root`, not to a scope nested in it. */\nexport const ownedBy = (el: Element, root: Element): boolean =>\n el === root || el.parentElement?.closest(boundary()) === root;\n\n/** @returns The error's code for `tether-error`, `error` without one. */\nfunction errorCode(error: unknown): string {\n const code = codeOf(error);\n return typeof code === 'string' && code ? code : 'error';\n}\n\n/**\n * Sets `tether-state` (loading | ready | error) and `tether-error` (not-found or an error code) on `root`, toggles\n * `tether-show=\"loading|ready|error|not-found\"` inside it, and disables its add-to-cart buttons when there's no\n * product.\n */\nexport function setScopeStatus(root: Element, status: ScopeStatus, error?: unknown): void {\n const notFound = status === 'not-found';\n const failed = status === 'error' || notFound;\n root.setAttribute(attr('state'), notFound ? 'error' : status);\n if (failed) root.setAttribute(attr('error'), notFound ? 'not-found' : errorCode(error));\n else root.removeAttribute(attr('error'));\n\n scoped(root, sel('show'), boundary()).forEach((el) => {\n const shows = get(el, 'show');\n if (isScopeStatus(shows)) show(el, shows === status);\n });\n\n if (!failed) return;\n scoped(root, sel('add-to-cart'), boundary()).forEach((button) => {\n button.classList.add('is-disabled');\n button.setAttribute('aria-disabled', 'true');\n if ('disabled' in button) button.disabled = true;\n });\n}\n", "/**\n * The DOM writes of a product scope that follow the visitor's choice, on every change: `product-state.ts`'s view as\n * text, selected and unavailable states, `tether-show` conditions and the add-to-cart buttons.\n */\nimport { attr, sel, get, qsa, formatMoney, formatPercent, setText, setImage, setSelected, show } from './dom.ts';\nimport { STATUS_KEYS, ownedBy } from './status.ts';\nimport { allocationFor } from './selling-plans.ts';\nimport { isValueAvailable, planBindTexts, variantBindTexts } from './product-state.ts';\nimport { isSelect, selectsIn } from './product-render.ts';\nimport type { Formatters, ProductView } from './product-state.ts';\nimport type { RenderScope } from './product-render.ts';\n\nconst FORMAT: Formatters = { money: formatMoney, percent: formatPercent };\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\n/**\n * The element of an add-to-cart button that holds its label, with the label as written kept in\n * `data-tether-label`, so mode-dependent texts (`tether-subscribe-text`, `tether-soldout-text`) can be undone.\n */\nexport function addButtonLabel(button: HTMLElement): { label: HTMLElement; original: string } {\n const label = qsa(button, sel('add-label'))[0] ?? button;\n const original = label.dataset['tetherLabel'] ?? (isInput(label) ? label.value : label.textContent);\n label.dataset['tetherLabel'] = original;\n return { label, original };\n}\n\nfunction renderOptionStates(scope: RenderScope, view: ProductView): void {\n const { product } = scope;\n const selectedValue = (authored: string | null) => view.selected[scope.optName(authored)] ?? '';\n scope.$(sel('option-selected')).forEach((el) => {\n setText(el, selectedValue(get(el, 'option-selected')));\n });\n scope.$(sel('option-list')).forEach((container) => {\n const name = scope.optName(get(container, 'option-list'));\n qsa(container, sel('option-value')).forEach((node) => {\n const value = get(node, 'option-value') ?? '';\n setSelected(node, view.selected[name] === value);\n const unavailable = !isValueAvailable(product, view.selected, name, value);\n node.classList.toggle('is-unavailable', unavailable);\n node.setAttribute('data-unavailable', unavailable ? 'true' : 'false');\n });\n });\n selectsIn(scope, sel('option')).forEach((select) => {\n select.value = selectedValue(get(select, 'option'));\n });\n selectsIn(scope, sel('variant-select')).forEach((select) => {\n select.value = view.variant.id;\n });\n}\n\n/**\n * The purchase toggles and the plan list and select.\n */\nfunction renderPurchaseStates(scope: RenderScope, view: ProductView): void {\n const { variant, purchase, line } = view;\n scope.$(sel('purchase')).forEach((el) => {\n if (isSelect(el)) {\n el.value = purchase.kind;\n return;\n }\n const mode = get(el, 'purchase');\n setSelected(el, mode === purchase.kind);\n const unavailable = mode === 'subscription' ? !view.subscriptionAvailable : scope.product.requiresSellingPlan;\n el.classList.toggle('is-unavailable', unavailable);\n });\n scope.$(sel('plan-value')).forEach((node) => {\n const planId = get(node, 'plan-value') ?? '';\n setSelected(node, line.kind === 'subscription' && planId === line.planId);\n node.classList.toggle('is-unavailable', !allocationFor(variant, planId));\n const plan = scope.plans.find((candidate) => candidate.id === planId);\n const texts = planBindTexts(variant, planId, plan, FORMAT);\n qsa(node, sel('plan-bind')).forEach((el) => {\n const text = texts.get(get(el, 'plan-bind') ?? '');\n if (text) setText(el, text());\n });\n });\n selectsIn(scope, sel('plan')).forEach((select) => {\n select.value = line.kind === 'subscription' ? line.planId : '';\n });\n}\n\n/**\n * The `tether-show` conditions, with the scope's own status: a product scope that renders is ready.\n */\nfunction renderConditions(scope: RenderScope, view: ProductView): void {\n const conditions = new Map<string, boolean>(Object.entries(view.show));\n STATUS_KEYS.forEach((status) => conditions.set(status, status === 'ready'));\n scope.$(sel('show')).forEach((el) => {\n const key = get(el, 'show') ?? '';\n const visible = conditions.get(key);\n if (visible === undefined) return;\n // State blocks of a list nested in this product belong to that list.\n if (STATUS_KEYS.some((status) => status === key) && !ownedBy(el, scope.root)) return;\n show(el, visible);\n });\n}\n\nfunction renderAddButtons(scope: RenderScope, view: ProductView): void {\n const disabled = !view.variant.availableForSale;\n scope.$(sel('add-to-cart')).forEach((button) => {\n button.classList.toggle('is-disabled', disabled);\n button.setAttribute('aria-disabled', String(disabled));\n // A <button> or <input> is disabled; a link or div carries only aria-disabled.\n if ('disabled' in button) button.disabled = disabled;\n const soldOutText = get(button, 'soldout-text');\n const subscribeText = get(button, 'subscribe-text');\n if (!soldOutText && !subscribeText) return;\n const { label, original } = addButtonLabel(button);\n let text = original;\n if (view.line.kind === 'subscription' && subscribeText) text = subscribeText;\n if (disabled && soldOutText) text = soldOutText;\n if (button.getAttribute('aria-busy') !== 'true') setText(label, text);\n });\n}\n\n/**\n * Writes what follows the variant and purchase.\n */\nexport function writeVariant(scope: RenderScope, view: ProductView): void {\n const { variant } = view;\n const { product } = scope;\n const texts = variantBindTexts(view, FORMAT);\n scope.$(sel('bind')).forEach((el) => {\n const key = get(el, 'bind') ?? '';\n if (key === 'variant-image') {\n setImage(el, variant.image?.url || product.featuredImage?.url, variant.image?.altText || product.title);\n return;\n }\n const text = texts.get(key);\n if (text) setText(el, text());\n });\n renderOptionStates(scope, view);\n renderPurchaseStates(scope, view);\n renderConditions(scope, view);\n scope.root.classList.toggle('is-subscription', view.line.kind === 'subscription');\n scope.root.setAttribute(attr('purchase-mode'), view.line.kind);\n renderAddButtons(scope, view);\n}\n", "/**\n * A product scope's clicks and changes: option and plan buttons, purchase toggles, quantity steppers, the selects and\n * add to cart. One delegated listener of each kind on the root, so cards a list re-renders need no new listeners.\n */\nimport { reportError } from './config.ts';\nimport { sel, get, emit, setText, busy } from './dom.ts';\nimport { addButtonLabel } from './variant-render.ts';\nimport type { ProductScope } from './product.ts';\nimport type { Purchase } from './product-state.ts';\n\nconst productSelector = () => sel('product');\n\nconst isField = (el: Element): el is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA';\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/**\n * @returns The nearest `selector` around `target` that belongs to the scope, not to a nested one. Typed as an HTML\n * element, as `qsa` types its matches.\n */\nfunction closestOwn(scope: ProductScope, target: Element, selector: string): HTMLElement | null {\n const el = target.closest<HTMLElement>(selector);\n return el?.closest(productSelector()) === scope.root ? el : null;\n}\n\n/** Adds to the cart with `aria-busy` on the button and `tether-adding-text` as its label meanwhile. */\nfunction onAddClick(scope: ProductScope, button: HTMLElement): void {\n const { label, original } = addButtonLabel(button);\n const addingText = get(button, 'adding-text');\n busy(button, async () => {\n if (addingText) setText(label, addingText);\n try {\n await scope.addToCart();\n } catch (error) {\n reportError(error, 'add to cart failed');\n emit('error', { error, context: 'add-to-cart' }, scope.root);\n } finally {\n if (addingText) setText(label, original);\n }\n })\n .then(() => {\n // The mode-dependent labels (tether-subscribe-text, tether-soldout-text) wait for aria-busy to clear.\n scope.renderVariant();\n })\n .catch((error: unknown) => {\n reportError(error, 'add to cart render');\n });\n}\n\nfunction onClick(scope: ProductScope, event: Event, target: Element): void {\n const optionValue = closestOwn(scope, target, sel('option-value'));\n const optionList = optionValue?.closest(sel('option-list'));\n if (optionValue && optionList) {\n event.preventDefault();\n scope.select(scope.optName(get(optionList, 'option-list')), get(optionValue, 'option-value') ?? '');\n return;\n }\n const plan = closestOwn(scope, target, sel('plan-value'));\n if (plan) {\n event.preventDefault();\n scope.selectPlan(get(plan, 'plan-value'));\n return;\n }\n const purchase = closestOwn(scope, target, `${sel('purchase')}:not(select)`);\n if (purchase) {\n // A radio or checkbox input keeps its own click, so it shows checked.\n if (purchase.tagName !== 'INPUT') event.preventDefault();\n scope.setPurchase(purchaseKind(get(purchase, 'purchase')));\n return;\n }\n const plus = closestOwn(scope, target, sel('qty-plus'));\n const minus = plus ? null : closestOwn(scope, target, sel('qty-minus'));\n if (plus || minus) {\n event.preventDefault();\n scope.setQuantity(scope.quantity + (plus ? 1 : -1));\n return;\n }\n const add = closestOwn(scope, target, sel('add-to-cart'));\n if (add) {\n event.preventDefault();\n onAddClick(scope, add);\n }\n}\n\n/** @returns The kind a `tether-purchase` value asks for: `subscription`, else buying once. */\nconst purchaseKind = (value: string | null): Purchase['kind'] =>\n value === 'subscription' ? 'subscription' : 'one-time';\n\n/** @param target A form field in the scope. */\nfunction onChange(scope: ProductScope, target: Element): void {\n const value = isField(target) ? target.value : '';\n if (target.matches(`select${sel('option')}`)) {\n scope.select(scope.optName(get(target, 'option')), value);\n } else if (target.matches(`select${sel('variant-select')}`)) {\n scope.selectVariant(value);\n } else if (target.matches(`select${sel('plan')}`)) {\n if (value) scope.selectPlan(value);\n else scope.setPurchase('one-time');\n } else if (target.matches(`select${sel('purchase')}`)) {\n scope.setPurchase(purchaseKind(value));\n } else if (target.matches(sel('qty'))) {\n scope.setQuantity(value);\n }\n}\n\nexport function bindProductEvents(scope: ProductScope): void {\n scope.root.addEventListener('click', (event) => {\n if (isElement(event.target)) onClick(scope, event, event.target);\n });\n scope.root.addEventListener('change', (event) => {\n const { target } = event;\n if (isElement(target) && target.closest(productSelector()) === scope.root) onChange(scope, target);\n });\n}\n", "/**\n * ProductScope: everything inside [tether-product=\"<handle>\"] binds to one Shopify product: options, variants,\n * quantity, subscriptions and add to cart. The scope holds the visitor's choice and the actions on it;\n * `product-state.ts` derives what the choice shows, `product-render.ts` and `variant-render.ts` write it, and\n * `product-events.ts` turns clicks and changes into actions.\n */\nimport { cfg } from './config.ts';\nimport {\n attr,\n sel,\n get,\n has,\n scoped,\n emit,\n setText,\n notifyLayoutChanged,\n watchImages,\n parseJSONAttr,\n attributesFromJson,\n} from './dom.ts';\nimport { Cart } from './cart.ts';\nimport { allPlans, allocationFor, findPlan, GID } from './selling-plans.ts';\nimport {\n initialChoice,\n linePurchase,\n normalisePurchase,\n productView,\n purchasePlanId,\n selectionOf,\n} from './product-state.ts';\nimport { writeProduct } from './product-render.ts';\nimport { writeVariant } from './variant-render.ts';\nimport { bindProductEvents } from './product-events.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { ShopifyCart } from './cart/store.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\nimport type { Purchase, Selection } from './product-state.ts';\n\n/** What a scope adds to the cart. */\ninterface CartLineInput {\n merchandiseId: string;\n quantity: number;\n sellingPlanId?: string;\n attributes?: { key: string; value: string }[];\n}\n\nexport const scopes = new WeakMap<Element, ProductScope>();\n\nconst productSelector = () => sel('product');\n\n/**\n * Option names as written in the markup (the build's language), by product handle. In another language Shopify\n * translates them (Size \u2192 Gr\u00F6\u00DFe), so markup names that aren't the product's are matched by position.\n */\nconst knownOptionNames = new Map<string, string[]>();\n\nexport function rememberOptionNames(products: readonly Pick<Product, 'handle' | 'options'>[]): void {\n products.forEach((product) => {\n if (!knownOptionNames.has(product.handle)) {\n knownOptionNames.set(\n product.handle,\n product.options.map((option) => option.name),\n );\n }\n });\n}\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\nconst isField = (el: Element): el is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA';\n\n/**\n * @param el A `tether-line-attribute` field.\n * @returns Its value for the cart line; `null` for an unchecked or empty field.\n */\nfunction lineAttributeValue(el: HTMLElement): string | null {\n if (isInput(el) && el.type === 'checkbox') {\n if (!el.checked) return null;\n return el.value && el.value !== 'on' ? el.value : 'Yes';\n }\n if (isInput(el) && el.type === 'radio') return el.checked ? el.value : null;\n return (isField(el) && el.value) || null;\n}\n\nexport class ProductScope {\n readonly root: HTMLElement;\n product: Product;\n plans: PlanInGroup[];\n selected: Selection;\n /** How the visitor buys; `setPurchase` and `selectPlan` change it. */\n purchase: Purchase;\n variant: ProductVariant | null;\n quantity: number;\n\n constructor(root: HTMLElement, product: Product) {\n this.root = root;\n this.product = product;\n this.plans = allPlans(product);\n const params = new URLSearchParams(window.location.search);\n const choice = initialChoice(product, {\n variant: params.get('variant') || get(root, 'variant'),\n sellingPlan: params.get('selling_plan'),\n defaultPlan: get(root, 'default-plan'),\n defaultPurchase: get(root, 'default-purchase') || cfg.defaultPurchase,\n });\n this.selected = choice.selected;\n this.purchase = choice.purchase;\n this.variant = null;\n this.quantity = has(root, 'quantity') ? Math.max(0, parseInt(get(root, 'quantity') ?? '', 10) || 0) : 1;\n\n scopes.set(root, this);\n root.setAttribute(attr('state'), 'ready');\n root.removeAttribute(attr('error'));\n this.renderProduct();\n bindProductEvents(this);\n this.renderVariant();\n\n watchImages(root, 'product');\n notifyLayoutChanged('product');\n emit('product-ready', { product, scope: this }, root);\n }\n\n /** New data for the same product (a country switch). Keeps the visitor's choice. */\n update(product: Product): void {\n const variantId = this.variant?.id;\n this.product = product;\n this.plans = allPlans(product);\n const same = product.variants.nodes.find((variant) => variant.id === variantId);\n if (same) this.selected = selectionOf(same);\n this.root.setAttribute(attr('state'), 'ready');\n this.root.removeAttribute(attr('error'));\n this.renderProduct();\n this.renderVariant();\n notifyLayoutChanged('product');\n emit('product-updated', { product, scope: this }, this.root);\n }\n\n /** @returns Matches in this scope, including the root itself (a card link with tether-bind=\"link\"). */\n $(selector: string): HTMLElement[] {\n const inner = scoped(this.root, selector, productSelector());\n return this.root.matches(selector) ? [this.root, ...inner] : inner;\n }\n\n /**\n * @param authored An option name in the markup.\n * @returns The product's option of that name, or at that position in the build's language.\n */\n optName(authored: string | null): string {\n if (!authored) return '';\n const { options } = this.product;\n if (options.some((option) => option.name === authored)) return authored;\n const index = (knownOptionNames.get(this.product.handle) ?? []).indexOf(authored);\n return options[index]?.name ?? authored;\n }\n\n get plan(): PlanInGroup | null {\n const planId = purchasePlanId(this.purchase);\n return this.plans.find((plan) => plan.id === planId) ?? null;\n }\n\n /** True when the current variant can be bought on a plan. */\n get subscriptionAvailable(): boolean {\n return this.plans.some((plan) => allocationFor(this.variant, plan.id));\n }\n\n /** True when the line is bought on a plan: subscribing, and the variant is on the plan. */\n get isSubscription(): boolean {\n return linePurchase(this.purchase, this.variant).kind === 'subscription';\n }\n\n /** @returns What would be added to the cart right now. */\n getLine(): CartLineInput | null {\n if (!this.variant) return null;\n const line: CartLineInput = { merchandiseId: this.variant.id, quantity: this.quantity };\n const bought = linePurchase(this.purchase, this.variant);\n if (bought.kind === 'subscription') line.sellingPlanId = bought.planId;\n const attributes = this.collectAttributes();\n if (attributes.length) line.attributes = attributes;\n return line;\n }\n\n /** @returns `tether-line-attributes` and the filled `tether-line-attribute` fields. */\n collectAttributes(): { key: string; value: string }[] {\n const fixed = attributesFromJson(parseJSONAttr(this.root, 'line-attributes'));\n const fields = this.$(sel('line-attribute')).flatMap((el) => {\n const value = lineAttributeValue(el);\n return value === null ? [] : [{ key: get(el, 'line-attribute') ?? '', value }];\n });\n return [...fixed, ...fields];\n }\n\n /**\n * Selects an option value. A combination no variant has moves to the closest variant with the value: the most other\n * choices kept, then one for sale.\n */\n select(name: string, value: string): void {\n if (this.selected[name] === value) return;\n const withValue = this.product.variants.nodes.filter((variant) =>\n variant.selectedOptions.some((option) => option.name === name && option.value === value),\n );\n const [firstWithValue] = withValue;\n // A value no variant has: nothing to select, the visitor's choice stays.\n if (!firstWithValue) return;\n const next = { ...this.selected, [name]: value };\n if (!withValue.some((variant) => variant.selectedOptions.every((option) => next[option.name] === option.value))) {\n const score = (variant: ProductVariant) => {\n const kept = variant.selectedOptions.filter(\n (option) => option.name !== name && this.selected[option.name] === option.value,\n ).length;\n // One kept choice outweighs being for sale.\n return kept * 2 + (variant.availableForSale ? 1 : 0);\n };\n const closest = withValue.reduce(\n (best, variant) => (score(variant) > score(best) ? variant : best),\n firstWithValue,\n );\n Object.assign(next, selectionOf(closest));\n }\n this.selected = next;\n this.renderVariant();\n }\n\n /** @param id A variant gid or numeric id. */\n selectVariant(id: string): void {\n const variant = this.product.variants.nodes.find((candidate) => candidate.id === GID.variant(id));\n if (!variant) return;\n this.selected = selectionOf(variant);\n this.renderVariant();\n }\n\n setPurchase(kind: Purchase['kind']): void {\n const next = normalisePurchase(this.product, this.variant, kind, purchasePlanId(this.purchase));\n // The product decides when the wanted kind isn't possible (no plans, or a plan required).\n if (next.kind === this.purchase.kind) return;\n this.purchase = next;\n this.renderVariant();\n }\n\n /** @param ref A plan id, numeric id, name or option value. */\n selectPlan(ref: string | null): void {\n const plan = findPlan(this.product, ref);\n if (!plan) return;\n this.purchase = { kind: 'subscription', planId: plan.id };\n this.renderVariant();\n }\n\n setQuantity(quantity: number | string): void {\n const minimum = has(this.root, 'quantity') ? 0 : 1;\n this.quantity = Math.max(minimum, parseInt(String(quantity), 10) || 0);\n this.$(sel('qty')).forEach((el) => {\n setText(el, this.quantity);\n });\n this.renderVariant();\n }\n\n /** @returns The cart after adding; `null` when the variant can't be added. */\n async addToCart(): Promise<ShopifyCart | null> {\n if (!this.variant?.availableForSale) return null;\n const line = this.getLine();\n if (!line || line.quantity < 1) return null;\n const cart = await Cart.add(line);\n emit('product-added', { product: this.product, variant: this.variant, line }, this.root);\n return cart;\n }\n\n /** Learns the markup's option names when they are the product's, then writes the product. */\n renderProduct(): void {\n const { product } = this;\n if (!knownOptionNames.has(product.handle)) {\n const authored = this.$(`${sel('option-list')},select${sel('option')}`).map(\n (el) => get(el, 'option-list') || get(el, 'option'),\n );\n if (authored.some((name) => product.options.some((option) => option.name === name))) {\n rememberOptionNames([product]);\n }\n }\n writeProduct(this);\n }\n\n /** Derives the view of the current choice, keeps its variant, selection and purchase, and writes it. */\n renderVariant(): void {\n const view = productView(\n this.product,\n { selected: this.selected, purchase: this.purchase, quantity: this.quantity },\n cfg.lowStock,\n );\n if (!view) {\n this.variant = null;\n return;\n }\n this.variant = view.variant;\n this.selected = view.selected;\n this.purchase = view.purchase;\n writeVariant(this, view);\n emit(\n 'variant-changed',\n {\n product: this.product,\n variant: view.variant,\n selected: { ...view.selected },\n purchase: view.purchase.kind,\n sellingPlanId: view.line.kind === 'subscription' ? view.line.planId : null,\n },\n this.root,\n );\n }\n}\n", "/**\n * Product lists: [tether-collection=\"handle\"], [tether-products=\"search query\"] and [tether-recommendations]. The\n * first child of [tether-list] is the card template; every card becomes a product scope, so all product attributes\n * work inside cards (including quick-add with a subscription toggle).\n */\nimport { reportError } from './config.ts';\nimport { TetherError } from './client.ts';\nimport {\n attr,\n sel,\n get,\n has,\n scoped,\n emit,\n setText,\n setStoreHtml,\n setImage,\n repeat,\n notifyLayoutChanged,\n busy,\n show,\n} from './dom.ts';\nimport { getProducts, getRecommendations } from './loader.ts';\nimport { setScopeStatus, isNotFound, resolveProductKey } from './status.ts';\nimport type { Product } from './fragments.ts';\nimport type { Collection, PageInfo, ProductQuery } from './loader.ts';\n\n/** `tether-products=\"url:q\"`: the search in the page's `?q=`, for a results page on a static site. */\nconst URL_QUERY_SOURCE = 'url:';\n\nexport const listSelector = () => `${sel('collection')},${sel('products')},${sel('recommendations')}`;\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/**\n * @param raw A `tether-limit`.\n * @returns The limit; `undefined` for none, zero or not a number.\n */\nconst toLimit = (raw: string | null): number | undefined => parseInt(raw ?? '', 10) || undefined;\n\n/**\n * @param raw A `tether-intent`.\n * @returns Shopify's recommendation intent, `RELATED` unless `COMPLEMENTARY` is written.\n */\nconst toIntent = (raw: string | null): 'RELATED' | 'COMPLEMENTARY' =>\n raw === 'COMPLEMENTARY' ? 'COMPLEMENTARY' : 'RELATED';\n\nexport class ListScope {\n readonly root: HTMLElement;\n products: Product[] = [];\n pageInfo: PageInfo | null = null;\n collection: Collection | null = null;\n /** Counts loads, so only the latest one renders. */\n loadToken = 0;\n\n constructor(root: HTMLElement) {\n this.root = root;\n setScopeStatus(root, 'loading');\n root.addEventListener('click', (event) => {\n if (!isElement(event.target)) return;\n const more = event.target.closest(sel('load-more'));\n if (more?.closest(listSelector()) !== root) return;\n event.preventDefault();\n void busy(more, () => this.load(true));\n });\n }\n\n $(selector: string) {\n return scoped(this.root, selector, listSelector());\n }\n\n /** The collection or search query of the list's attributes. */\n options(): ProductQuery {\n const { root } = this;\n const query: ProductQuery = { reverse: get(root, 'reverse') === 'true' };\n const first = toLimit(get(root, 'limit'));\n if (first) query.first = first;\n const sortKey = get(root, 'sort');\n if (sortKey) query.sortKey = sortKey;\n if (has(root, 'collection')) {\n query.collection = get(root, 'collection') ?? '';\n return query;\n }\n const raw = get(root, 'products') ?? '';\n query.query = raw.startsWith(URL_QUERY_SOURCE)\n ? new URL(window.location.href).searchParams.get(raw.slice(URL_QUERY_SOURCE.length)) || ''\n : raw;\n return query;\n }\n\n /**\n * The product of a recommendations list: a handle, `url:\u2026` (one dynamic product page), or empty inside a product\n * scope for that product.\n *\n * @throws {TetherError} `NOT_FOUND` when there is no product to recommend for.\n */\n async loadRecommendations(): Promise<Product[]> {\n let { key } = resolveProductKey(this.root, window.location, 'recommendations');\n const scope = this.root.closest(sel('product'));\n if (!key && scope) ({ key } = resolveProductKey(scope));\n if (!key) throw new TetherError('No product for recommendations', { code: 'NOT_FOUND' });\n return getRecommendations(key, toIntent(get(this.root, 'intent')));\n }\n\n /** @param more Appends the next page instead of replacing the products. */\n async load(more = false): Promise<void> {\n this.loadToken += 1;\n const token = this.loadToken;\n const stale = () => token !== this.loadToken;\n try {\n if (has(this.root, 'recommendations')) {\n const recommendations = await this.loadRecommendations();\n if (stale()) return;\n const limit = toLimit(get(this.root, 'limit'));\n this.products = limit ? recommendations.slice(0, limit) : recommendations;\n } else {\n const page = await getProducts({ ...this.options(), after: more ? (this.pageInfo?.endCursor ?? null) : null });\n if (stale()) return;\n this.collection = page.collection;\n this.pageInfo = page.pageInfo;\n this.products = more ? [...this.products, ...page.products] : page.products;\n }\n this.render();\n setScopeStatus(this.root, 'ready');\n emit('list-ready', { products: this.products, collection: this.collection }, this.root);\n } catch (error) {\n if (stale()) return;\n // A failed \"load more\" keeps what is rendered; otherwise the list shows its error state.\n if (!more) setScopeStatus(this.root, isNotFound(error) ? 'not-found' : 'error', error);\n // A missing product for recommendations is a page detail, not an error to report.\n if (!(isNotFound(error) && has(this.root, 'recommendations'))) reportError(error, 'list load failed');\n emit('error', { error, context: 'list' }, this.root);\n }\n }\n\n render() {\n const { collection } = this;\n this.$(sel('collection-bind')).forEach((el) => {\n switch (get(el, 'collection-bind') ?? '') {\n case 'title':\n setText(el, collection?.title ?? '');\n break;\n case 'description':\n setStoreHtml(el, collection?.descriptionHtml);\n break;\n case 'image':\n setImage(el, collection?.image?.url, collection?.image?.altText || collection?.title);\n break;\n case 'count':\n setText(el, this.products.length);\n break;\n }\n });\n\n this.$(sel('list')).forEach((container) => {\n repeat(container, this.products, (node, product) => {\n node.setAttribute(attr('product'), product.handle);\n node.removeAttribute(attr('state'));\n });\n // Cards are new elements: mounting them uses the product data already cached.\n emit('list-rendered', { container }, this.root);\n });\n\n const empty = this.products.length === 0;\n this.$(sel('show')).forEach((el) => {\n const key = get(el, 'show');\n if (key === 'list-empty') show(el, empty);\n if (key === 'list-filled') show(el, !empty);\n });\n this.$(sel('load-more')).forEach((el) => {\n show(el, Boolean(this.pageInfo?.hasNextPage));\n });\n notifyLayoutChanged('list');\n }\n}\n", "/**\n * The product scopes and lists on the page: mounting a product root, the live roots a country switch re-renders, and\n * the products the server rendered into the page. Shared by `index.ts` and the optional markets part.\n */\nimport { cfg, log, warn, reportError } from './config.ts';\nimport { ProductScope, scopes, rememberOptionNames } from './product.ts';\nimport { listSelector } from './collection.ts';\nimport { getProduct, takeStale, refreshProduct, hydrateProducts } from './loader.ts';\nimport { resolveProductKey, setScopeStatus, isNotFound } from './status.ts';\nimport { attr, get, qsa, emit } from './dom.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { isProductShape } from './product-shape.ts';\nimport type { Product } from './fragments.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** A mounted `ListScope`. */\ninterface MountedList {\n root: Element;\n load: (more?: boolean) => Promise<unknown>;\n}\n\ntype KeySource = Pick<ReturnType<typeof resolveProductKey>, 'fromUrl' | 'raw'>;\n\n/**\n * The marker on the `<script type=\"application/json\">` tags `tether/server`'s `hydrate()` writes. It is fixed, not\n * `<prefix>-hydrate`, because the server writes it without knowing the browser's prefix.\n */\nconst HYDRATE_SELECTOR = 'script[type=\"application/json\"][data-tether-hydrate]';\n\nexport const mountedProducts = new WeakSet<Element>();\nexport const mountedLists = new WeakSet<Element>();\n/** Product roots to re-render after a country switch; pruned once disconnected. */\nexport const liveProducts = new Set<HTMLElement>();\n/** Lists to reload after a country switch; pruned once disconnected. */\nexport const liveLists = new Set<MountedList>();\n/** Each product root's latest request, so a slow older response can't win. */\nconst latestRequest = new WeakMap<Element, object>();\n\n/** Forgets roots a page transition removed, so they aren't kept alive. */\nexport function pruneLive(): void {\n liveProducts.forEach((el) => {\n if (!el.isConnected) liveProducts.delete(el);\n });\n liveLists.forEach((list) => {\n if (!list.root.isConnected) liveLists.delete(list);\n });\n}\n\n/** @returns `root` itself when it matches, then the matches inside it. */\nexport function within(root: ParentNode & Partial<Pick<Element, 'matches'>>, selector: string): HTMLElement[] {\n // A root that matches carries Tether's markup, which goes on HTML elements (see `qsa`).\n const self = root.matches?.(selector) ? [root as HTMLElement] : [];\n return [...self, ...qsa(root, selector)];\n}\n\n/** `tether-page-title=\"{title} | My shop\"` on a product root sets `document.title`, for dynamic product pages. */\nfunction applyPageTitle(el: Element, product: Product): void {\n const template = get(el, 'page-title');\n if (!template || typeof document === 'undefined') return;\n document.title = template\n .replace(/\\{title\\}/g, product.title)\n .replace(/\\{vendor\\}/g, product.vendor || '')\n .replace(/\\{type\\}/g, product.productType || '');\n}\n\n/**\n * A product root without a key: on a dynamic page opened without `?handle=` (or with an empty path) it is a product\n * that isn't there; inside a list it gets its handle when the list renders.\n */\nfunction mountWithoutKey(el: HTMLElement, { fromUrl, raw }: KeySource): void {\n if (fromUrl) {\n mountedProducts.add(el);\n setScopeStatus(el, 'not-found');\n emit('product-not-found', { key: null, source: raw }, el);\n } else if (!el.closest(listSelector())) {\n warn(`[${attr('product')}] without a value. Bind it to the Shopify handle field, or use \"url:handle\".`, el);\n }\n}\n\nfunction showLoadFailure(el: HTMLElement, key: string, { fromUrl, raw }: KeySource, error: unknown): void {\n const notFound = isNotFound(error);\n setScopeStatus(el, notFound ? 'not-found' : 'error', error);\n if (!notFound) {\n reportError(error, `product ${key}`);\n emit('error', { error, context: 'product', key }, el);\n return;\n }\n emit('product-not-found', { key, source: raw }, el);\n // A typo in a URL isn't worth an alert; a CMS item pointing at a deleted product is.\n if (fromUrl) log(`product not found: ${key}`);\n else reportError(error, `product ${key}`);\n}\n\n/**\n * Mounts one product root.\n *\n * @returns True when a product request was started.\n */\nexport function mountProduct(el: HTMLElement): boolean {\n if (mountedProducts.has(el)) return false;\n const { key, ...source } = resolveProductKey(el);\n if (!key) {\n mountWithoutKey(el, source);\n return false;\n }\n mountedProducts.add(el);\n liveProducts.add(el);\n const request = {};\n latestRequest.set(el, request);\n const isLatest = () => latestRequest.get(el) === request;\n // A rendered scope keeps showing its content while it refreshes (a country switch).\n if (scopes.get(el)) el.setAttribute(attr('state'), 'updating');\n else setScopeStatus(el, 'loading');\n const loading = trackMarket(getProduct(key));\n loading\n .then((product) => {\n if (!isLatest()) return;\n const existing = scopes.get(el);\n if (existing) existing.update(product);\n else new ProductScope(el, product);\n applyPageTitle(el, product);\n if (cfg.revalidate && takeStale(key)) queueRefresh(key);\n })\n .catch((error: unknown) => {\n if (isLatest()) showLoadFailure(el, key, source, error);\n });\n return true;\n}\n\n/** Loads again the product scopes that failed on a network or API error, for when the browser is back online. */\nexport function retryFailed(): void {\n pruneLive();\n liveProducts.forEach((el) => {\n if (el.getAttribute(attr('state')) !== 'error' || el.getAttribute(attr('error')) === 'not-found') return;\n mountedProducts.delete(el);\n mountProduct(el);\n });\n}\n\n/** Server-rendered products to refresh in the next batch. */\nlet refreshQueue = new Set<string>();\n\nfunction queueRefresh(key: string): void {\n if (!refreshQueue.size) setTimeout(flushRefresh, 0);\n refreshQueue.add(key);\n}\n\n/** @param key The key the refresh was asked for. */\nfunction applyRefresh(key: string, fresh: Product): void {\n liveProducts.forEach((el) => {\n const scope = scopes.get(el);\n if (!scope || !el.isConnected) return;\n const rootKey = resolveProductKey(el).key;\n if (rootKey !== key && rootKey !== fresh.handle && rootKey !== fresh.id) return;\n if (JSON.stringify(scope.product) !== JSON.stringify(fresh)) scope.update(fresh);\n });\n}\n\n/** One batched background request refreshes every server-rendered product queued. */\nfunction flushRefresh() {\n const keys = [...refreshQueue];\n refreshQueue = new Set();\n keys.forEach((key) => {\n refreshProduct(key)\n .then((fresh) => {\n applyRefresh(key, fresh);\n })\n .catch((error: unknown) => {\n reportError(error, `refresh ${key}`);\n });\n });\n}\n\n/**\n * While the page's server-rendered prices are another market's: the loads to wait for before\n * `html.tether-market-pending` goes. `null` when no market is pending.\n */\nlet marketPending: Set<Promise<unknown>> | null = null;\n\n/**\n * Lists, and anything else that loads prices, hold `html.tether-market-pending` until they are in.\n *\n * @returns `loading` itself.\n */\nexport const trackMarket = <T>(loading: Promise<T>): Promise<T> => {\n marketPending?.add(loading);\n return loading;\n};\n\n/** After a mount: drops `html.tether-market-pending` once this page's products are live. */\nexport function settleMarket(): void {\n if (!marketPending) return;\n const waits = [...marketPending];\n void Promise.allSettled(waits).then(() => {\n if (!marketPending || waits.some((wait) => !marketPending?.has(wait))) return;\n if (waits.length !== marketPending.size) {\n settleMarket(); // more loads came in meanwhile\n return;\n }\n marketPending = null;\n document.documentElement.classList.remove(`${cfg.prefix}-market-pending`);\n });\n}\n\n/** What `tether/server`'s `hydrate()` writes: the market it fetched in, and the products. */\ninterface HydratePayload {\n country: CountryCode | undefined;\n language: LanguageCode | undefined;\n products: Product[];\n}\n\n/** @param json A hydrate tag's JSON. */\nfunction toHydratePayload(json: unknown): HydratePayload {\n if (!isRecord(json)) return { country: undefined, language: undefined, products: [] };\n const { products } = json;\n const entries: unknown[] = Array.isArray(products) ? products : [];\n return {\n country: toCountry(json['country']) ?? undefined,\n language: toLanguage(json['language']) ?? undefined,\n products: entries.filter(isProductShape),\n };\n}\n\n/** Hydrate tags already read. */\nconst readScripts = new WeakSet<Element>();\n/**\n * Uses the products in the page's hydrate tags (`tether/server` `hydrate()`) without a request. A tag made for another\n * market is skipped, and marks the page (`html.tether-market-pending`) until the live prices are in, so CSS can hide\n * prices meanwhile.\n *\n * Trust: a hydrate tag is part of the page, written by the site's own build like the rest of its HTML and scripts, so\n * its prices and products are shown until the background refresh replaces them. Whoever can write a tag into the page\n * can already write a script there; the shape check (`isProductShape`) is for a truncated or hand-written tag, which\n * is skipped instead of failing a render.\n *\n * @param root Where to look: the document for a mount (a tag may sit in `<head>` or anywhere else on the page), the\n * added elements for `observe`.\n */\nexport function hydrateFromPage(root: Document | Element = document): void {\n within(root, HYDRATE_SELECTOR).forEach((script) => {\n if (readScripts.has(script)) return;\n readScripts.add(script);\n let json: unknown;\n try {\n json = JSON.parse(script.textContent);\n } catch (error) {\n warn('bad data-tether-hydrate', error);\n return;\n }\n const { country, language, products } = toHydratePayload(json);\n // The build's option names, also for other languages.\n rememberOptionNames(products);\n if (country === cfg.country && language === cfg.language) {\n hydrateProducts(products);\n return;\n }\n log('hydration skipped: other market');\n if (!marketPending) {\n marketPending = new Set();\n document.documentElement.classList.add(`${cfg.prefix}-market-pending`);\n }\n });\n}\n", "/**\n * Markets: which country and language prices, currency and translations follow.\n *\n * markets: {\n * detect: true, // or a list: ['saved', 'server', 'webflow']\n * locales: { // Webflow Localization: locale subdirectory or <html lang>\n * de: { country: 'DE', language: 'DE', locale: 'de-DE' },\n * 'fr-be': { country: 'BE', language: 'FR' },\n * },\n * cookie: 'tether_country', // a cookie your server or Worker sets (from cf-ipcountry)\n * remember: true, // keep the visitor's tether-country-select choice\n * }\n *\n * Sources, first match wins per field:\n * saved the visitor's own choice (localStorage), from tether-country-select / Tether.setCountry()\n * server window.TETHER_COUNTRY, <meta name=\"tether-country\" content=\"DE\">, or the cookie\n * webflow locale subdirectory (/de/...) or <html lang=\"de-DE\">, through `locales`\n * then the static `country` / `language` config. Without `detect`, only `saved` applies, so a country select keeps\n * working across pages; `detect: false` turns even that off. Tether never calls an IP lookup service: a country from\n * the visitor's IP only comes from the site's own server (docs/reference.md, Markets and localization).\n */\nimport { MARKET_SOURCES, cfg, parseMarkets } from './config.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { getStored, setStored } from './storage.ts';\nimport { decodeOrKeep } from './uri.ts';\nimport type { MarketsConfig } from './config.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** A market a visitor chose, as `setCountry()` stores it. */\nexport interface Market {\n country: CountryCode;\n language: LanguageCode | undefined;\n /** An `Intl` locale for money and dates. */\n locale: string | undefined;\n}\n\nexport type MarketSource = 'saved' | 'server' | 'webflow';\ntype MarketField = 'country' | 'language' | 'locale';\ntype MarketSources = Partial<Record<MarketField, MarketSource>>;\n\n/** The fields detection found, and the source each came from. */\nexport interface DetectedMarket {\n country?: CountryCode;\n language?: LanguageCode;\n locale?: string;\n /** Absent when `detect: false` turns detection off. */\n sources?: MarketSources;\n}\n\n/** What detection reads, passed in so it runs on any document. */\ninterface MarketEnv {\n document: Pick<Document, 'cookie' | 'querySelector' | 'documentElement'> | null;\n location: Pick<Location, 'pathname'> | null;\n /** `localStorage`, read through `getStored`, which finds nothing where the browser blocks it. */\n storage: Pick<Storage, 'getItem'>;\n /** Where `TETHER_COUNTRY` and `TETHER_LANGUAGE` are read from (`window`). */\n global: object;\n}\n\n/** A source's raw values. */\ninterface MarketHint {\n country?: unknown;\n language?: unknown;\n locale?: unknown;\n}\n\ninterface NamedCode {\n isoCode: string;\n name: string;\n}\n\ninterface Currency {\n isoCode: string;\n symbol: string;\n}\n\nexport type LocalizedCountry = NamedCode & { currency: Currency };\n\n/** The store's markets, from the Storefront API's `localization`. */\nexport interface Localization {\n /** The country of the current `@inContext`. */\n country: LocalizedCountry;\n language: NamedCode;\n availableCountries: (LocalizedCountry & { availableLanguages: NamedCode[] })[];\n}\n\nconst DEFAULT_COOKIE = 'tether_country';\n\n/** The localStorage key of the visitor's market: `<prefix>:market:<domain>`. */\nexport const storageKey = (): string => `${cfg.prefix}:market:${cfg.domain}`;\n\nconst pageStorage: Pick<Storage, 'getItem'> = { getItem: (key) => getStored(key) };\n\n/** The page this script runs on. */\nfunction pageEnv(): MarketEnv {\n const hasWindow = typeof window !== 'undefined';\n return {\n document: typeof document !== 'undefined' ? document : null,\n location: hasWindow ? window.location : null,\n storage: pageStorage,\n global: hasWindow ? window : {},\n };\n}\n\n/** The JSON value of `text`, `null` for text that isn't JSON (a key another script wrote). */\nfunction parseJsonOrNull(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch (error) {\n if (error instanceof SyntaxError) return null;\n throw error;\n }\n}\n\n/** `value` as a market, when it has a valid country. A language or locale that isn't valid is left out. */\nfunction toMarket(value: unknown): Market | null {\n if (typeof value !== 'object' || value === null) return null;\n const country = toCountry(Reflect.get(value, 'country'));\n if (!country) return null;\n const locale: unknown = Reflect.get(value, 'locale');\n return {\n country,\n language: toLanguage(Reflect.get(value, 'language')) ?? undefined,\n locale: typeof locale === 'string' && locale ? locale : undefined,\n };\n}\n\n/**\n * A market as `rememberMarket` stores it, validated: storage is written by other tabs, older versions and other\n * scripts too.\n *\n * @param text The value stored under `storageKey()`.\n * @returns `null` when it isn't a valid market.\n */\nexport const parseSavedMarket = (text: string): Market | null => toMarket(parseJsonOrNull(text));\n\n/**\n * The visitor's saved market (`setCountry()`, `tether-country-select`).\n *\n * @param storage Default `localStorage`.\n * @returns `null` when nothing valid is saved.\n */\nexport function readSavedMarket(storage: Pick<Storage, 'getItem'> = pageStorage): Market | null {\n const text = storage.getItem(storageKey());\n return text ? parseSavedMarket(text) : null;\n}\n\n/** @returns The cookie's value; one with a malformed escape is kept as written, which no country code is. */\nfunction readCookie(doc: Pick<Document, 'cookie'> | null, name: string): string | null {\n if (!doc || !name) return null;\n const cookie = doc.cookie\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(`${name}=`));\n return cookie ? decodeOrKeep(cookie.slice(name.length + 1)) : null;\n}\n\n/** The entry of `locales` for `key`, ignoring case. */\nfunction lookupLocale(locales: MarketsConfig['locales'], key: string | undefined): MarketHint | null {\n if (!locales || !key) return null;\n const wanted = key.toLowerCase();\n const found = Object.keys(locales).find((name) => name.toLowerCase() === wanted);\n return found ? (locales[found] ?? null) : null;\n}\n\nfunction readServerHint(markets: MarketsConfig, env: MarketEnv): MarketHint {\n const meta = (name: string) => env.document?.querySelector(`meta[name=\"${name}\"]`)?.getAttribute('content');\n const globalCountry: unknown = Reflect.get(env.global, 'TETHER_COUNTRY');\n const globalLanguage: unknown = Reflect.get(env.global, 'TETHER_LANGUAGE');\n const pageCountry = meta('tether-country') || readCookie(env.document, markets.cookie || DEFAULT_COOKIE);\n return {\n country: globalCountry || pageCountry,\n language: globalLanguage || meta('tether-language'),\n };\n}\n\nfunction readWebflowLocale(markets: MarketsConfig, env: MarketEnv): MarketHint | null {\n const segment = (env.location?.pathname ?? '/').split('/').find((part) => part !== '');\n const fromPath = lookupLocale(markets.locales, segment);\n if (fromPath) return fromPath;\n const lang = env.document?.documentElement.getAttribute('lang');\n if (!lang) return null;\n const mapped = lookupLocale(markets.locales, lang) ?? lookupLocale(markets.locales, lang.split('-')[0]);\n if (mapped) return { locale: lang, ...mapped };\n if (markets.locales && markets.strict) return null;\n // Unmapped: the language from <html lang>, a country only from a region subtag (de-CH -> CH).\n const region = lang.split('-')[1];\n return { language: lang, country: region?.length === 2 ? region : null, locale: lang };\n}\n\nfunction readSource(source: MarketSource, markets: MarketsConfig, env: MarketEnv): MarketHint | null {\n switch (source) {\n case 'saved':\n return markets.remember === false ? null : readSavedMarket(env.storage);\n case 'server':\n return readServerHint(markets, env);\n case 'webflow':\n return readWebflowLocale(markets, env);\n }\n}\n\n/**\n * @param detect Validated: a list holds only known sources.\n * @returns Without `detect`, only the visitor's own choice from a country select.\n */\nfunction sourcesFor(detect: MarketsConfig['detect']): readonly MarketSource[] {\n if (!detect) return ['saved'];\n return detect === true ? MARKET_SOURCES : detect;\n}\n\n/**\n * The market the page's sources point to. Reads the DOM and storage only, so it can run on any `env`.\n *\n * @returns Only the fields found; `{}` with `detect: false`.\n */\nexport function detectMarket(\n markets: MarketsConfig | null | undefined = cfg.markets,\n env: MarketEnv = pageEnv(),\n): DetectedMarket {\n // The config's own `markets` was validated by `configure()`; one passed in by the site's code is validated here.\n const config = (markets === cfg.markets ? markets : parseMarkets(markets)) ?? {};\n if (config.detect === false) return {};\n const sources: MarketSources = {};\n const found: DetectedMarket = { sources };\n for (const source of sourcesFor(config.detect)) {\n const hint = readSource(source, config, env);\n if (!hint) continue;\n const country = toCountry(hint.country);\n const language = toLanguage(hint.language);\n const locale = typeof hint.locale === 'string' && hint.locale ? hint.locale : null;\n if (country && !found.country) {\n found.country = country;\n sources.country = source;\n }\n if (language && !found.language) {\n found.language = language;\n sources.language = source;\n }\n if (locale && !found.locale) {\n found.locale = locale;\n sources.locale = source;\n }\n }\n return found;\n}\n\n/** Saves the visitor's choice for the next pages, unless `markets.remember` is false. */\nexport function rememberMarket(market: {\n country: CountryCode;\n language?: string | null | undefined;\n locale?: string | null | undefined;\n}): void {\n if (cfg.markets?.remember === false) return;\n const saved = { country: market.country, language: market.language || undefined, locale: market.locale || undefined };\n setStored(storageKey(), JSON.stringify(saved));\n}\n", "/**\n * The market the page uses: a page built for one market (`<meta name=\"tether-page-country\">`) wins, then the visitor's\n * saved or detected market, then the configured one. Init applies it to `cfg` once. Astro's ClientRouter keeps the\n * script running across pages, so each arrival asks again: the next page may be built for another market, or for none.\n */\nimport { cfg, log, reportError } from './config.ts';\nimport { loadPart } from './lazy.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { readSavedMarket } from './markets.ts';\nimport type { TetherOptions } from './config.ts';\nimport type { Market, DetectedMarket } from './markets.ts';\nimport type { Parts } from './lazy.ts';\nimport type { ShopifyCart } from './cart/store.ts';\n\nexport type SetCountryOptions = Parameters<Parts['markets']['setCountry']>[1];\n\n/** The market of the config, before a page's or visitor's market applied. */\ninterface ConfiguredMarket {\n country: string | undefined;\n language: string | undefined;\n locale: string | undefined;\n}\n\nlet configuredMarket: ConfiguredMarket = { country: undefined, language: undefined, locale: undefined };\n\n/** Whether the page arrived at last was built for its own market, which the next page without one switches back from. */\nlet lastPageHadOwnMarket = false;\n\n/**\n * `<meta name=\"tether-page-country\" content=\"DE\">`, with `tether-page-language` and `tether-page-locale`. Never\n * remembered: it belongs to the page, not to the visitor.\n *\n * @returns `null` when the page has no valid country of its own.\n */\nfunction pageMarket(): Market | null {\n const meta = (name: string) =>\n document.querySelector(`meta[name=\"${cfg.prefix}-page-${name}\"]`)?.getAttribute('content') || undefined;\n const country = toCountry(meta('country'));\n if (!country) return null;\n return { country, language: toLanguage(meta('language')) ?? undefined, locale: meta('locale') };\n}\n\n/**\n * Without `markets.detect` only the visitor's saved choice applies, read without loading the markets part.\n */\nasync function visitorMarket(): Promise<Market | DetectedMarket | null> {\n const markets = cfg.markets ?? {};\n if (markets.detect === false) return null;\n if (!markets.detect) return markets.remember === false ? null : readSavedMarket();\n try {\n return (await loadPart('markets')).detectMarket(markets);\n } catch (error) {\n reportError(error, 'markets');\n return null;\n }\n}\n\n/**\n * Sets `cfg`'s market for the first page, over the configured one.\n *\n * @param overrides What `init()` was called with: a `locale` there wins over the visitor's.\n */\nexport async function applyStartMarket(overrides: TetherOptions): Promise<void> {\n configuredMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };\n const page = pageMarket();\n lastPageHadOwnMarket = page !== null;\n if (page) {\n cfg.country = page.country;\n cfg.language = page.language;\n if (page.locale) cfg.locale = page.locale;\n return;\n }\n const market = await visitorMarket();\n if (!market) return;\n if (market.country) cfg.country = market.country;\n if (market.language) cfg.language = market.language;\n if (market.locale && !overrides.locale) cfg.locale = market.locale;\n log('market', market);\n}\n\nconst sameCode = (a: string | undefined, b: string | undefined) => (a ?? '').toUpperCase() === (b ?? '').toUpperCase();\n\n/**\n * The market to switch to on arriving at the current page. A page built for a market switches to it; the first page\n * without one after such a page switches back to the visitor's own choice, else to the configured market.\n *\n * @returns `null` when the page stays in the market in use.\n */\nexport function marketForArrival(): {\n country: string;\n language: string | undefined;\n locale: string | undefined;\n} | null {\n const page = pageMarket();\n const leftOwnMarket = page === null && lastPageHadOwnMarket;\n lastPageHadOwnMarket = page !== null;\n const target = page ?? (leftOwnMarket ? (readSavedMarket() ?? configuredMarket) : null);\n if (!target?.country) return null;\n if (sameCode(target.country, cfg.country) && sameCode(target.language, cfg.language)) return null;\n return { country: target.country, language: target.language, locale: target.locale };\n}\n\n/**\n * `Tether.setCountry('DE')`: loads the markets part on first use.\n *\n * @returns The cart once everything is in the new market.\n */\nexport function setCountry(code: string, options?: SetCountryOptions): Promise<ShopifyCart | null> {\n return loadPart('markets').then((part) => part.setCountry(code, options));\n}\n", "export const PART_INTEGRITY = {\"tether.diagnose.min.js\":\"sha384-GoSabxXrpEUWCpbWmyCt6Eby/SRGcvuJ1c8lQFVaLprF1MbfyZwlfundN9SJzHlS\",\"tether.markets.min.js\":\"sha384-ZCR9y6ze/DzdKq13UxtHqcuWrujwCRgkMkock5VjQXA6svPWzCbim2/jseMyhsji\",\"tether.search.min.js\":\"sha384-LdRf9V4s9Z9Jq0JASRj5LoGHLRYj98SnSKldeLxslwGFEuYTFVMYviMDVYORA9sN\",\"tether.extras.min.js\":\"sha384-xKG8mQaRI9O7rOHGPtEMFCjCa5GJt8v1Pg+aUi1DnKMMCcRhbtXeB66FkiIBValW\",\"tether.account.min.js\":\"sha384-bsEg4NX+L08ZQv6v4gRa9tvI5FBWjVBUPfgb7MAB9P0htKO+vWs0AetLGfVcHPWU\",\"tether.account-pages.min.js\":\"sha384-PmV835ZgO/6emk4R8FSrpmAbAL4l9uacnyY6qc4RPauLLwB3GCQzxIKjbhlvKhak\",\"tether.analytics.min.js\":\"sha384-PCfUr7l9kFJGvf6kowF32Q4j+PYp6VbtG+RAiqU7A1rXqHlSE3mf+NlhiOInGIC6\"};\n", "/**\n * Where the `<script>` build's parts meet the core: `window.Tether` as the core script set it up (`TetherInternals`).\n * Another script can replace that global after the core, with an object that has none of it; each side checks it.\n */\nimport { isRecord } from '../server/src/shared/json.ts';\nimport type { PartName, Parts, TetherInternals } from './lazy.ts';\n\n/** @returns True when `value` carries the core's modules and parts, as the core script's `window.Tether` does. */\nexport function isTetherInternals(value: unknown): value is TetherInternals {\n return isRecord(value) && isRecord(value['__core']) && isRecord(value['__parts']);\n}\n\n/**\n * Registers a part's module with the core that loads it.\n *\n * @throws {Error} When `window.Tether` isn't the core script's: the part ran without it, or another script replaced it.\n */\nexport function registerPart<Name extends PartName>(name: Name, part: Parts[Name]): void {\n const tether: unknown = Reflect.get(window, 'Tether');\n if (!isTetherInternals(tether)) {\n throw new Error(`tether.${name}.js runs after the Tether script, which loads it, and needs its window.Tether`);\n }\n tether.__parts[name] = part;\n}\n", "/**\n * `<script>` build: the optional parts are separate files next to tether(.min).js (tether.markets.min.js), loaded on\n * first use and checked against the hashes the core was built with, from that folder or from `partsUrl`, which must\n * serve the files of the same release. A part registers itself in `window.Tether.__parts` and uses the core modules in\n * `window.Tether.__core` instead of bundling copies.\n */\nimport { cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn } from './config.ts';\nimport { endpoint, gql, orNull, shaped, TetherError } from './client.ts';\nimport {\n ANALYTICS_EVENTS,\n attr,\n attributesFromJson,\n emit,\n formatDate,\n formatMoney,\n get,\n has,\n hasTemplate,\n isOwnEvent,\n parseJSONAttr,\n productUrl,\n qsa,\n repeat,\n sel,\n setImage,\n setText,\n show,\n} from './dom.ts';\nimport { clearProductCache, getProduct } from './loader.ts';\nimport { allPlans } from './selling-plans.ts';\nimport { boundary, resolveProductKey } from './status.ts';\nimport { Cart } from './cart.ts';\nimport { scopes } from './product.ts';\nimport { listSelector } from './collection.ts';\nimport { liveLists, liveProducts, mountedProducts, mountProduct, pruneLive, within } from './mounting.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { setCountry } from './page-market.ts';\nimport { isHttpsUrl } from './shopify-values.ts';\nimport { PART_INTEGRITY } from './part-integrity.ts';\nimport { isTetherInternals } from './part-registry.ts';\nimport type { Parts, PartName } from './lazy.ts';\n\n/**\n * The core exports the parts use, shared instead of bundled twice, by the file name scripts/build.ts maps a part's\n * imports to. Each file shares a list of values without their names: the build finds each value's export name in its\n * module and hands a part's import of that name the value at its position. The core so carries no export names and no\n * export a part doesn't use, and a part that imports one not listed here fails its build. A part reads its entries\n * when it loads, once every module has run; read while the core runs, an import cycle through this file could leave\n * a value unset.\n */\nexport const coreModules = {\n get 'config.ts'() {\n return [cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn];\n },\n get 'client.ts'() {\n return [endpoint, gql, orNull, shaped, TetherError];\n },\n get 'dom.ts'() {\n return [\n ANALYTICS_EVENTS,\n attr,\n attributesFromJson,\n emit,\n formatDate,\n formatMoney,\n get,\n has,\n hasTemplate,\n isOwnEvent,\n parseJSONAttr,\n productUrl,\n qsa,\n repeat,\n sel,\n setImage,\n setText,\n show,\n ];\n },\n get 'loader.ts'() {\n return [clearProductCache, getProduct];\n },\n get 'selling-plans.ts'() {\n return [allPlans];\n },\n get 'status.ts'() {\n return [boundary, resolveProductKey];\n },\n get 'cart.ts'() {\n return [Cart];\n },\n get 'product.ts'() {\n return [scopes];\n },\n get 'collection.ts'() {\n return [listSelector];\n },\n get 'mounting.ts'() {\n return [liveLists, liveProducts, mountedProducts, mountProduct, pruneLive, within];\n },\n get 'market-codes.ts'() {\n return [toCountry, toLanguage];\n },\n get 'page-market.ts'() {\n return [setCountry];\n },\n get 'shopify-values.ts'() {\n return [isHttpsUrl];\n },\n // Parts that load other parts (account \u2192 account-pages) use this same loader.\n get 'lazy.ts'() {\n return [loadPart];\n },\n};\n\nconst MINIFIED_SCRIPT = /\\.min\\.js(\\?|$)/;\nconst coreScript = typeof document === 'undefined' ? '' : (document.currentScript?.getAttribute('src') ?? '');\nconst coreScriptUrl = coreScript ? new URL(coreScript, document.baseURI).href : '';\n\n/** Parts loaded or loading. */\nconst parts = new Map<PartName, Promise<unknown>>();\n\n/**\n * The folder parts load from: the `partsUrl` (checked to be https or on this site when the config was resolved), else\n * the core script's own folder. `null` when the core was not loaded by a `<script src>` (a module, a loader) and no\n * `partsUrl` is set, so nothing says where parts are.\n */\nfunction partsFolder(): string | null {\n if (cfg.partsUrl) return new URL(cfg.partsUrl, document.baseURI).href;\n if (!coreScriptUrl) return null;\n return coreScriptUrl.replace(/[^/]*$/, '');\n}\n\n/**\n * Pins the script to the part file built with this core, wherever it loads from: a page that pins the core with\n * `integrity` then runs only parts of that build, and a `partsUrl` must serve the same release's files.\n */\nfunction applyIntegrity(script: HTMLScriptElement, integrity: string): void {\n script.integrity = integrity;\n // A cross-origin script is only checked when fetched with CORS (CDNs such as jsDelivr allow it).\n if (new URL(script.src).origin !== window.location.origin) script.crossOrigin = 'anonymous';\n}\n\n/**\n * Loads a part's file. A failed load leaves the cache, so the next use tries again (the network may be back).\n *\n * @param integrity The file's hash from this core's build.\n */\nfunction loadScript(name: PartName, folder: string, file: string, integrity: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const url = `${folder.replace(/\\/?$/, '/')}${file}`;\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n applyIntegrity(script, integrity);\n script.onload = () => {\n const tether: unknown = Reflect.get(window, 'Tether');\n const part = isTetherInternals(tether) ? tether.__parts[name] : undefined;\n if (part) {\n resolve(part);\n return;\n }\n parts.delete(name);\n const problem = isTetherInternals(tether)\n ? 'did not register'\n : \"can't register: window.Tether is no longer this Tether script's, another script replaced it\";\n reject(new Error(`${url} ${problem}`));\n };\n script.onerror = () => {\n parts.delete(name);\n reject(\n new Error(\n `Could not load ${url}, or it is not the file built with this Tether script. Host the same release's files ` +\n 'next to the Tether script or at partsUrl.',\n ),\n );\n };\n document.head.appendChild(script);\n });\n}\n\n/**\n * @throws {Error} (rejected) When the part's file can't be loaded, fails its integrity check or doesn't register.\n */\nexport function loadPart<Name extends PartName>(name: Name): Promise<Parts[Name]> {\n let loading = parts.get(name);\n if (!loading) {\n const folder = partsFolder();\n if (folder === null) {\n return Promise.reject(\n new Error(`The Tether script's own location is unknown, so its ${name} part can't be found: set partsUrl.`),\n );\n }\n const suffix = MINIFIED_SCRIPT.test(coreScriptUrl) || !coreScriptUrl ? '.min' : '';\n const file = `tether.${name}${suffix}.js`;\n const integrity = PART_INTEGRITY[file];\n if (integrity === undefined) {\n return Promise.reject(new Error(`This Tether script was built without ${file}, so it can't load that part.`));\n }\n loading = loadScript(name, folder, file, integrity);\n parts.set(name, loading);\n }\n // A part's file is built from the module `Parts[Name]` names (scripts/build.ts), and registers that module.\n return loading as Promise<Parts[Name]>;\n}\n", "/**\n * What tells a page that customer accounts are in use in this browser: the \"may be logged in\" hint the account part\n * keeps in `localStorage`, and the Worker's `?account=` mark after a login. The core reads them to decide whether a\n * page loads the part, and the part writes the hint, so both take them from here.\n */\nimport { cfg } from './config.ts';\nimport { removeStored } from './storage.ts';\n\n/**\n * What the Worker's login redirect says in `?account=`: `in` after a login, `expired` when the login took too long,\n * `error` when it failed.\n */\ntype LoginOutcome = 'in' | 'expired' | 'error';\n\n/** The \"may be logged in\" hint's key: it makes the core load the part on the next page, and holds no secret. */\nexport const hintKey = (): string => `${cfg.prefix}:account-hint:${cfg.domain}`;\n\n/** Removes a key from both storage areas, each on its own: a blocked one doesn't keep the key in the other. */\nfunction removeFromBothAreas(key: string): void {\n removeStored(key, 'sessionStorage');\n removeStored(key, 'localStorage');\n}\n\n/**\n * Removes the customer tokens and pending login that Tether 0.6's browser-mode accounts kept in this browser. The\n * session held a refresh token with the customer's full scope, so it must not stay behind once accounts run through\n * the Worker. TODO: remove in 0.8.0, when no visitor's browser still holds a 0.6 session.\n */\nexport function forgetBrowserModeTokens(): void {\n removeFromBothAreas(`${cfg.prefix}:account:${cfg.domain}`);\n removeFromBothAreas(`${cfg.prefix}:account-login:${cfg.domain}`);\n}\n\nexport const isLoginOutcome = (value: string | null): value is LoginOutcome =>\n value === 'in' || value === 'expired' || value === 'error';\n", "/**\n * The core's side of customer accounts, which are an optional part: whether a page needs the part, and\n * `Tether.account`, whose functions load it on first call. Every function returns a promise, the part's own sync ones\n * (`isLoggedIn`) too, because the part may still have to load.\n */\nimport { cfg, reportError } from './config.ts';\nimport { attr } from './dom.ts';\nimport { loadPart } from './lazy.ts';\nimport { within } from './mounting.ts';\nimport { hintKey, isLoginOutcome } from './account-marks.ts';\nimport { getStored } from './storage.ts';\nimport type { Parts } from './lazy.ts';\n\ntype AccountPart = Parts['account'];\ntype AccountResult<Name extends keyof AccountPart> = Awaited<ReturnType<AccountPart[Name]>>;\ntype AccountFunction<Name extends keyof AccountPart> = (\n ...args: Parameters<AccountPart[Name]>\n) => Promise<AccountResult<Name>>;\ntype AccountApi = { [Name in keyof AccountPart]: AccountFunction<Name> };\n\n/** The attributes of the account markup: a page with one of them loads the part. */\nconst ACCOUNT_ATTRIBUTES = [\n 'account-login',\n 'account-bind',\n 'account-link',\n 'orders',\n 'order',\n 'addresses',\n 'subscriptions',\n 'profile-form',\n 'address-form',\n];\n\n/** @returns A selector for every element the account part fills in or shows. */\nconst accountSelector = (): string =>\n [...ACCOUNT_ATTRIBUTES.map((name) => `[${attr(name)}]`), `[${attr('show')}^=\"logged-\"]`].join(',');\n\n/**\n * Whether this browser keeps the \"may be logged in\" hint, so checkout on a page without account markup still knows\n * the customer.\n *\n * @returns False where the browser blocks storage, which then holds no hint.\n */\nfunction hasLoginHint(): boolean {\n return Boolean(getStored(hintKey()));\n}\n\n/**\n * Loads the account part and mounts it on `root`, when accounts are configured and the page needs them: account\n * markup in `root`, a login coming back from the Worker (`?account=`), or the \"may be logged in\" hint.\n */\nexport function mountAccountIfUsed(root: ParentNode): void {\n if (!cfg.account) return;\n const needed =\n within(root, accountSelector()).length > 0 ||\n isLoginOutcome(new URLSearchParams(window.location.search).get('account')) ||\n hasLoginHint();\n if (!needed) return;\n loadPart('account')\n .then((part) => part.mountAccount(root))\n .catch((error: unknown) => {\n reportError(error, 'account');\n });\n}\n\n/** @returns `name` of the account part, called once the part has loaded. */\nfunction fromPart<Name extends keyof AccountPart>(name: Name): AccountFunction<Name> {\n return (...args) =>\n loadPart('account').then((part) => {\n // `Name` picks one function of the part, and `args` are that function's parameters.\n const call = part[name] as (\n ...callArgs: Parameters<AccountPart[Name]>\n ) => AccountResult<Name> | PromiseLike<AccountResult<Name>>;\n return call(...args);\n });\n}\n\n/**\n * `Tether.account`: the account part's functions, each loading the part on first call. A plain object, so `await\n * Tether.account` and `Object.keys(Tether.account)` behave as for any object.\n */\nexport const accountApi: AccountApi = {\n login: fromPart('login'),\n logout: fromPart('logout'),\n isLoggedIn: fromPart('isLoggedIn'),\n customer: fromPart('customer'),\n orders: fromPart('orders'),\n order: fromPart('order'),\n updateProfile: fromPart('updateProfile'),\n addresses: fromPart('addresses'),\n saveAddress: fromPart('saveAddress'),\n deleteAddress: fromPart('deleteAddress'),\n subscriptions: fromPart('subscriptions'),\n subscriptionAction: fromPart('subscriptionAction'),\n requestReturn: fromPart('requestReturn'),\n render: fromPart('render'),\n mountAccount: fromPart('mountAccount'),\n};\n", "/**\n * The cart's markup controls, delegated from the document so they keep working across page swaps: open, close,\n * clear and checkout buttons, a line's +, - and remove buttons, its quantity input and plan select, the cart note, and\n * the keyboard of the open drawer (Escape closes it, Tab stays inside it).\n */\nimport { cfg, reportError } from '../config.ts';\nimport { Cart, trapFocus } from '../cart.ts';\nimport { sel, busy } from '../dom.ts';\n\n/** @returns The attribute a rendered cart line carries its line id in. */\nconst lineKeyAttribute = (): string => `data-${cfg.prefix}-key`;\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/** @returns The rendered cart line `target` is in, with its line id. */\nfunction lineOf(target: Element): { line: Element; id: string } | null {\n const line = target.closest(`[${lineKeyAttribute()}]`);\n const id = line?.getAttribute(lineKeyAttribute());\n return line && id ? { line, id } : null;\n}\n\nfunction reportFailure(change: Promise<unknown>, context: string): void {\n change.catch((error: unknown) => {\n reportError(error, context);\n });\n}\n\n/** A line's remove, + and - buttons. */\nfunction onLineClick(event: MouseEvent, target: Element): void {\n const found = lineOf(target);\n if (!found?.line.closest(sel('cart-lines'))) return;\n const { line, id } = found;\n if (target.closest(sel('line-remove'))) {\n event.preventDefault();\n line.classList.add('is-busy');\n reportFailure(Cart.removeLine(id), 'cart line');\n return;\n }\n const delta = lineDelta(target);\n if (!delta || !Cart.lines.some((candidate) => candidate.id === id)) return;\n event.preventDefault();\n reportFailure(Cart.changeQuantity(id, delta), 'cart line');\n}\n\n/** @returns The quantity change of a + or - button, 0 for anything else. */\nfunction lineDelta(target: Element): 1 | -1 | 0 {\n if (target.closest(sel('line-plus'))) return 1;\n if (target.closest(sel('line-minus'))) return -1;\n return 0;\n}\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n if (!isElement(target)) return;\n if (target.closest(sel('cart-open'))) {\n event.preventDefault();\n Cart.toggle();\n return;\n }\n if (target.closest(sel('cart-close'))) {\n event.preventDefault();\n Cart.close();\n return;\n }\n const clear = target.closest(sel('cart-clear'));\n if (clear) {\n event.preventDefault();\n void busy(clear, () =>\n Cart.clear().catch((error: unknown) => {\n reportError(error, 'cart clear');\n }),\n );\n return;\n }\n const checkout = target.closest(sel('checkout'));\n if (checkout) {\n event.preventDefault();\n void busy(checkout, () => Cart.checkout());\n return;\n }\n onLineClick(event, target);\n}\n\n/** The line shows `is-busy` and `aria-busy` until Shopify has answered; the next render removes them. */\nfunction showLineBusy(line: Element, change: Promise<unknown>, context: string): void {\n line.classList.add('is-busy');\n line.setAttribute('aria-busy', 'true');\n reportFailure(change, context);\n}\n\nfunction onChange(event: Event): void {\n const { target } = event;\n if (!isElement(target)) return;\n const found = lineOf(target);\n if (found && target instanceof window.HTMLInputElement && target.matches(sel('line', 'quantity'))) {\n showLineBusy(found.line, Cart.updateLine(found.id, parseInt(target.value, 10) || 0), 'cart line');\n } else if (found && target instanceof window.HTMLSelectElement && target.matches(sel('line-plan'))) {\n showLineBusy(found.line, Cart.updateLine(found.id, { sellingPlanId: target.value || null }), 'cart line plan');\n } else if (hasValue(target) && target.matches(sel('cart-note'))) {\n reportFailure(Cart.setNote(target.value), 'cart note');\n }\n}\n\nconst hasValue = (target: Element): target is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n target instanceof window.HTMLInputElement ||\n target instanceof window.HTMLTextAreaElement ||\n target instanceof window.HTMLSelectElement;\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (!Cart.isOpen()) return;\n if (event.key === 'Escape') {\n Cart.close();\n return;\n }\n trapFocus(event);\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindCartControls(): void {\n document.addEventListener('click', onClick);\n document.addEventListener('change', onChange);\n document.addEventListener('keydown', onKeydown);\n}\n", "/**\n * The discount code form (`tether-discount-form` with `tether-discount-input`) and the remove buttons of applied codes\n * (`tether-discount-remove` inside `tether-discount-code`), delegated from the document. A code Shopify refuses marks\n * the form `is-invalid` (and the input `aria-invalid`) until the visitor types again.\n */\nimport { reportError } from '../config.ts';\nimport { TetherError } from '../client.ts';\nimport { Cart } from '../cart.ts';\nimport { attr, sel, busy, emit } from '../dom.ts';\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\nfunction codeInputOf(form: Element): HTMLInputElement | null {\n const input = form.querySelector(sel('discount-input')) ?? form.querySelector('input');\n return input instanceof window.HTMLInputElement ? input : null;\n}\n\nfunction markInvalid(form: Element, input: HTMLInputElement | null, invalid: boolean): void {\n form.classList.toggle('is-invalid', invalid);\n if (invalid) input?.setAttribute('aria-invalid', 'true');\n else input?.removeAttribute('aria-invalid');\n}\n\nasync function applyCode(form: Element, input: HTMLInputElement | null, code: string): Promise<void> {\n try {\n await Cart.applyDiscount(code);\n markInvalid(form, input, false);\n emit('discount-applied', { code }, form);\n if (input) input.value = '';\n } catch (error) {\n markInvalid(form, input, true);\n if (error instanceof TetherError && error.code === 'INVALID_DISCOUNT') emit('discount-invalid', { code }, form);\n else reportError(error, 'discount');\n }\n}\n\nfunction onSubmit(event: SubmitEvent): void {\n const { target } = event;\n const form = isElement(target) ? target.closest(sel('discount-form')) : null;\n if (!form) return;\n event.preventDefault();\n const input = codeInputOf(form);\n const code = input?.value.trim();\n if (!code) return;\n void busy(form, () => applyCode(form, input, code));\n}\n\nfunction onInput(event: Event): void {\n const { target } = event;\n if (!isElement(target)) return;\n const form = target.closest(sel('discount-form'));\n if (!form) return;\n form.classList.remove('is-invalid');\n target.removeAttribute('aria-invalid');\n}\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n const remove = isElement(target) ? target.closest(sel('discount-remove')) : null;\n if (!remove) return;\n event.preventDefault();\n const code = remove.closest(sel('discount-code'))?.getAttribute(attr('discount-code'));\n if (!code) return;\n Cart.removeDiscount(code).catch((error: unknown) => {\n reportError(error, 'discount');\n });\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindDiscountControls(): void {\n // Capture: the submit is handled before a router (Astro's ClientRouter) turns it into a navigation.\n document.addEventListener('submit', onSubmit, true);\n document.addEventListener('input', onInput);\n document.addEventListener('click', onClick);\n}\n", "/**\n * The document-wide listeners whose work is in the optional extras part, loaded when one of them first has work:\n * group add buttons, the cart or market changing in another tab, and shareable variant links. A part that can't load\n * (a self-hosted site without `tether.extras.min.js`) is reported, so the missing file shows.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { attr, sel, busy, isOwnEvent } from '../dom.ts';\nimport { loadPart } from '../lazy.ts';\nimport type { ShopifyCart } from '../cart/store.ts';\n\n/** `[tether-group]` with `[tether-group-add]`: adds every product scope inside the group in one request. */\nexport const addGroup = (button: Element): Promise<ShopifyCart | null> =>\n loadPart('extras').then((part) => part.addGroup(button));\n\nconst reportExtrasError = (error: unknown) => {\n reportError(error, 'extras');\n};\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n const button = target instanceof window.Element ? target.closest(sel('group-add')) : null;\n if (!button) return;\n event.preventDefault();\n busy(button, () => addGroup(button)).catch(reportExtrasError);\n}\n\nfunction onStorage(event: StorageEvent): void {\n if (!cfg.domain || !event.key?.startsWith(`${cfg.prefix}:`)) return;\n loadPart('extras')\n .then((part) => {\n part.onStorage(event);\n })\n .catch(reportExtrasError);\n}\n\n/** @param event `tether:variant-changed`, which a product scope emits; one another script dispatches is ignored. */\nfunction onVariantChanged(event: Event): void {\n if (!isOwnEvent(event, 'variant-changed')) return;\n const { target } = event;\n if (!(target instanceof window.Element)) return;\n if (!cfg.variantUrl && !target.hasAttribute(attr('variant-url'))) return;\n loadPart('extras')\n .then((part) => {\n part.writeVariantUrl(target, event.detail);\n })\n .catch(reportExtrasError);\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindExtrasControls(): void {\n document.addEventListener('click', onClick);\n window.addEventListener('storage', onStorage);\n document.addEventListener(`${cfg.prefix}:variant-changed`, onVariantChanged);\n}\n", "/**\n * How the page follows the visitor across pages and markets: Astro's ClientRouter (`astro:page-load`,\n * `astro:before-swap`) mounts each new page in its market and closes the cart while leaving; a country select\n * switches the market; back online, what failed to load loads again.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { attr, sel } from '../dom.ts';\nimport { liveLists, retryFailed } from '../mounting.ts';\nimport { marketForArrival, setCountry } from '../page-market.ts';\n\n/** What the entry module does for a page. */\ninterface PageHooks {\n /** Settles once `init()` has applied the visitor's market. */\n ready: () => Promise<unknown>;\n mount: (root: Document) => unknown;\n unmount: () => void;\n}\n\n/** The body the last full-document mount ran on: Astro's first `astro:page-load` comes after that mount. */\nlet mountedBody: HTMLElement | null = null;\n\n/** Records a mount of the whole document, so the `astro:page-load` of the same page sends no second round of requests. */\nexport function rememberDocumentMount(): void {\n mountedBody = document.body;\n}\n\n/** Mounts the page Astro swapped in. It rejects when `init()` failed or the mount threw; the listener reports that. */\nasync function onPageLoad(hooks: PageHooks): Promise<void> {\n await hooks.ready();\n const target = marketForArrival();\n if (!target) {\n if (mountedBody !== document.body) hooks.mount(document);\n return;\n }\n try {\n await setCountry(target.country, { language: target.language ?? null, locale: target.locale, remember: false });\n } catch (error) {\n reportError(error, 'country');\n }\n hooks.mount(document);\n}\n\nfunction bindAstroRouter(hooks: PageHooks): void {\n document.addEventListener('astro:page-load', () => {\n onPageLoad(hooks).catch((error: unknown) => {\n reportError(error, 'page mount');\n });\n });\n document.addEventListener('astro:before-swap', () => {\n mountedBody = null;\n hooks.unmount();\n });\n}\n\nfunction onChange(event: Event): void {\n const { target } = event;\n if (!(target instanceof window.HTMLSelectElement) || !target.matches(sel('country-select'))) return;\n if (!target.value) return;\n setCountry(target.value).catch((error: unknown) => {\n reportError(error, 'country');\n });\n}\n\n/** Loads the failed products and lists again. */\nfunction onOnline() {\n retryFailed();\n liveLists.forEach((list) => {\n if (list.root.getAttribute(attr('state')) === 'error') void list.load();\n });\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindNavigation(hooks: PageHooks): void {\n if (cfg.astro) bindAstroRouter(hooks);\n document.addEventListener('change', onChange);\n window.addEventListener('online', onOnline);\n}\n", "/**\n * Tether's entry: `window.Tether`, `init()`, `mount()` and the start of the plain `<script>` setup.\n *\n * <script>window.TETHER_CONFIG = { domain: 'x.myshopify.com', token: '...' }</script>\n * <script src=\"tether.min.js\" defer></script>\n *\n * With page transitions (Barba, Swup): set `autoMount: false`, call `Tether.init()` once and\n * `Tether.mount(container)` after every page enter.\n */\nimport { cfg, configView, configure, isConfigured, log, warn, reportError } from './config.ts';\nimport { query, TetherError, withContext } from './client.ts';\nimport { Cart } from './cart.ts';\nimport { scopes } from './product.ts';\nimport { ListScope, listSelector } from './collection.ts';\nimport {\n getProduct,\n getProducts,\n getRecommendations,\n clearProductCache,\n primeProduct,\n normalizeProductKey,\n} from './loader.ts';\nimport * as plans from './selling-plans.ts';\nimport { resolveProductKey } from './status.ts';\nimport { sel, emit, isOwnEvent, ANALYTICS_EVENTS, formatMoney, imageUrl, pageGlobal } from './dom.ts';\nimport { loadPart, coreModules } from './lazy.ts';\nimport {\n mountedLists,\n liveLists,\n pruneLive,\n within,\n mountProduct,\n hydrateFromPage,\n settleMarket,\n trackMarket,\n} from './mounting.ts';\nimport { applyStartMarket, setCountry } from './page-market.ts';\nimport { accountApi, mountAccountIfUsed } from './account-api.ts';\nimport { forgetBrowserModeTokens } from './account-marks.ts';\nimport { bindCartControls } from './controls/cart-controls.ts';\nimport { bindDiscountControls } from './controls/discount.ts';\nimport { addGroup, bindExtrasControls } from './controls/extras.ts';\nimport { bindNavigation, rememberDocumentMount } from './controls/navigation.ts';\nimport { field } from '../server/src/shared/json.ts';\nimport type { TetherConfig, TetherOptions, MarketsConfig } from './config.ts';\nimport type { ShopifyCart } from './cart/store.ts';\nimport type { ProductScope } from './product.ts';\nimport type { DiagnoseOptions } from './diagnose.ts';\nimport type { TetherEvents } from './markup.ts';\nimport type { TetherInternals } from './lazy.ts';\n\nexport { addGroup, setCountry };\n\n// The types a site uses by name, under the names the docs give them.\nexport type { TetherOptions as TetherConfig, TetherConfig as ResolvedTetherConfig } from './config.ts';\nexport type { MarketsConfig, CartWarning } from './config.ts';\nexport type { Money } from './shopify-values.ts';\nexport type {\n Image as ShopifyImage,\n Metafield as ShopifyMetafield,\n Product as ShopifyProduct,\n ProductVariant as ShopifyVariant,\n SellingPlan as ShopifySellingPlanNode,\n SellingPlanAdjustmentValue,\n SellingPlanAllocation as ShopifySellingPlanAllocation,\n SellingPlanGroup as ShopifySellingPlanGroup,\n} from './fragments.ts';\nexport type {\n PlanInGroup as ShopifySellingPlan,\n PlanAdjustment,\n ProductWithPlans,\n VariantWithPlans,\n} from './selling-plans.ts';\nexport type { CartStatus, CartVariant as ShopifyCartVariant, ShopifyCart, ShopifyCartLine } from './cart/store.ts';\nexport type { Attributes, LineSpec as LineInput } from './cart/mutations.ts';\nexport type { TetherError, TetherErrorCode, GraphQLError, UserError } from './client.ts';\nexport type { ProductScope } from './product.ts';\nexport type { Purchase } from './product-state.ts';\nexport type { ProductPage, ProductQuery } from './loader.ts';\nexport type { DetectedMarket, Localization, MarketSource } from './markets.ts';\nexport type { SetCountryOptions } from './page-market.ts';\nexport type { DiagnoseOptions, DiagnoseReport } from './diagnose.ts';\nexport type { TetherEvents } from './markup.ts';\nexport type { AccountCustomer } from './account/answers.ts';\nexport type { AccountImage as ImageRef, AccountOrder, AccountOrderLine } from '../server/src/shared/account-orders.ts';\nexport type { AddressFields as CustomerAddressInput } from './account/operations.ts';\nexport type {\n CustomerAddress,\n Fulfillment,\n MailingAddress,\n OrderDetail,\n OrderDetailLine,\n SubscriptionContract,\n SubscriptionLine,\n} from './account/page-answers.ts';\n/** The helpers for selling plans, `Tether.plans`. */\nexport type SellingPlanHelpers = typeof plans;\n\nexport const VERSION = '0.7.2';\n\n/** Milliseconds `observe` waits after the last added node, so a burst of additions mounts once. */\nconst OBSERVE_DEBOUNCE_MS = 50;\n\n/** @returns The keys of `window.TETHER_CONFIG`, none when it is no object. */\nfunction pageConfig(): Readonly<Record<string, unknown>> {\n const config = pageGlobal('TETHER_CONFIG');\n return typeof config === 'object' && config !== null ? Object.fromEntries(Object.entries(config)) : {};\n}\n\nlet initPromise: Promise<Tether | null> | null = null;\n\nlet observer: MutationObserver | null = null;\n\n/** The analytics part's queue, created once by the first `init()` with `analytics`. */\nlet analyticsQueue: Event[] | null = null;\n\n/** The document-wide listeners are bound once, by the first configured `init()`. */\nlet controlsBound = false;\n\n/**\n * Mounts the lists of `root` and the product scopes in them as they render.\n *\n * @returns The lists mounted.\n */\nfunction mountLists(root: ParentNode): number {\n let count = 0;\n within(root, listSelector()).forEach((el) => {\n if (mountedLists.has(el)) return;\n mountedLists.add(el);\n count++;\n const list = new ListScope(el);\n liveLists.add(list);\n el.addEventListener(`${cfg.prefix}:list-rendered`, (event) => {\n // The list emits this once its products are in the page, with the element they are in.\n if (isOwnEvent(event, 'list-rendered')) mount(event.detail.container);\n });\n void trackMarket(list.load());\n });\n return count;\n}\n\n/**\n * Loads each optional part `root` uses: the country select, customer accounts and search as you type.\n */\nfunction mountParts(root: ParentNode): void {\n if (within(root, `${sel('country-select')},${sel('country-bind')}`).length) {\n loadPart('markets')\n .then((part) => part.renderCountrySelects(root))\n .catch((error: unknown) => {\n reportError(error, 'markets');\n });\n }\n mountAccountIfUsed(root);\n if (within(root, sel('search')).length) {\n loadPart('search')\n .then((part) => {\n part.mountSearch(root);\n })\n .catch((error: unknown) => {\n reportError(error, 'search');\n });\n }\n}\n\n/**\n * Mounts the lists, product scopes and parts inside `root`, after its hydrate tags were read (products the server\n * rendered into the page need no request), without the page-wide work that follows a mount.\n *\n * @returns The scopes and lists newly mounted.\n */\nfunction mountWithin(root: Document | Element): number {\n let count = mountLists(root);\n within(root, sel('product')).forEach((el) => {\n if (mountProduct(el)) count++;\n });\n mountParts(root);\n return count;\n}\n\n/**\n * Mounts every product scope and product list inside `root`. Safe to call repeatedly.\n *\n * @returns The scopes and lists newly mounted.\n */\nexport function mount(root: Document | Element = document): number {\n if (!isConfigured()) return 0;\n if (root === document) rememberDocumentMount();\n // Page transitions detach old roots: they aren't kept alive.\n pruneLive();\n hydrateFromPage(root.ownerDocument ?? document);\n const count = mountWithin(root);\n settleMarket();\n Cart.render();\n log(`mounted ${count} scope(s)`, root);\n return count;\n}\n\n/**\n * `observe`: mounts a burst of added elements at once. Each root is scanned on its own (an element inside another\n * added one is covered by it), and the page-wide work (market state, the cart's markup) runs once for the burst.\n */\nfunction mountAdded(added: readonly Element[]): void {\n const roots = added.filter(\n (root) => root.isConnected && !added.some((other) => other !== root && other.contains(root)),\n );\n if (!roots.length || !isConfigured()) return;\n pruneLive();\n // Only the added elements are read for hydrate tags: the rest of the page was read when it was mounted. A load-more\n // fragment may bring its tag after the cards it is for, so every tag is read before any card mounts.\n roots.forEach((root) => {\n hydrateFromPage(root);\n });\n const count = roots.reduce((sum, root) => sum + mountWithin(root), 0);\n settleMarket();\n Cart.render();\n log(`mounted ${count} scope(s) in ${roots.length} added element(s)`);\n}\n\n/** Call before a page transition removes the page: closes the cart drawer. */\nexport function unmount(): void {\n Cart.close();\n}\n\n/** @returns The product scope `el` is in. */\nexport function getScope(el: Element | null | undefined): ProductScope | null {\n const root = el?.closest(sel('product'));\n return root ? (scopes.get(root) ?? null) : null;\n}\n\nfunction bindControls() {\n if (controlsBound) return;\n controlsBound = true;\n bindCartControls();\n bindDiscountControls();\n bindExtrasControls();\n bindNavigation({ ready: () => initPromise ?? Promise.resolve(null), mount, unmount });\n}\n\n/** Buffers Tether's events until the analytics part has loaded, so none are missed. */\nfunction startAnalyticsQueue() {\n const queue: Event[] = [];\n analyticsQueue = queue;\n ANALYTICS_EVENTS.forEach((name) => {\n document.addEventListener(`${cfg.prefix}:${name}`, (event) => queue.push(event));\n });\n document.addEventListener('astro:page-load', (event) => queue.push(event));\n loadPart('analytics')\n .then((part) => part.startAnalytics(queue))\n .catch((error: unknown) => {\n reportError(error, 'analytics');\n });\n}\n\n/**\n * A saved cart from another market moves to this one, so its prices match the page's.\n */\nasync function cartInPageMarket(cart: ShopifyCart | null): Promise<ShopifyCart | null> {\n const country = cfg.country;\n const cartCountry = cart?.buyerIdentity.countryCode;\n if (!cart || !country || !cartCountry || cartCountry === country) return cart;\n try {\n return await Cart.setBuyerIdentity({ countryCode: country }, { create: false });\n } catch (error) {\n reportError(error, 'cart country');\n return cart;\n }\n}\n\n/**\n * Configures Tether (`window.TETHER_CONFIG` with `overrides` over it), applies the visitor's market and loads the\n * cart. A second call without overrides returns the first one's promise.\n *\n * @returns `null` without a domain and token, when Tether stays idle.\n */\nexport function init(overrides: TetherOptions = {}): Promise<Tether | null> {\n if (initPromise && !Object.keys(overrides).length) return initPromise;\n configure({ ...pageConfig(), ...overrides });\n if (!isConfigured()) {\n warn('TETHER_CONFIG is missing domain or token. Tether is idle.');\n initPromise = Promise.resolve(null);\n return initPromise;\n }\n forgetBrowserModeTokens();\n bindControls();\n if (cfg.observe) startObserver();\n if (cfg.analytics && !analyticsQueue) startAnalyticsQueue();\n initPromise = applyStartMarket(overrides)\n .then(() => Cart.init())\n .then(cartInPageMarket)\n .then((cart) => {\n emit('ready', { cart });\n return api;\n });\n return initPromise;\n}\n\n/** `observe`: mounts the elements added to the page later. */\nfunction startObserver() {\n if (observer || typeof MutationObserver === 'undefined') return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const queued = new Set<Element>();\n observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof window.Element) queued.add(node);\n });\n });\n clearTimeout(timer);\n timer = setTimeout(() => {\n const added = [...queued];\n queued.clear();\n mountAdded(added);\n }, OBSERVE_DEBOUNCE_MS);\n });\n observer.observe(document.body, { childList: true, subtree: true });\n}\n\n/** @param markets Default `markets` of the config. */\nconst detectMarket = (markets?: MarketsConfig | null) =>\n loadPart('markets').then((part) => part.detectMarket(markets ?? cfg.markets));\n\nconst getLocalization = () => loadPart('markets').then((part) => part.getLocalization());\n\nconst diagnose = (options?: Omit<DiagnoseOptions, 'version'>) =>\n loadPart('diagnose').then((part) => part.diagnose({ ...options, version: VERSION }));\n\n/**\n * Listens to the Tether event `<prefix>:<name>`. Only the events Tether dispatched reach the listener: one another\n * script dispatches under the same name is ignored, so the detail is always the one `TetherEvents` gives.\n *\n * @returns Stops listening.\n */\nfunction on<Name extends keyof TetherEvents>(\n name: Name,\n listener: (detail: TetherEvents[Name], event: CustomEvent<TetherEvents[Name]>) => void,\n): () => void {\n const type = `${cfg.prefix}:${name}`;\n const handler = (event: Event) => {\n if (isOwnEvent(event, name)) listener(event.detail, event);\n };\n document.addEventListener(type, handler);\n return () => {\n document.removeEventListener(type, handler);\n };\n}\n\nconst publicApi = {\n VERSION,\n init,\n mount,\n unmount,\n /** The live config, read-only. */\n get config(): Readonly<TetherConfig> {\n return configView;\n },\n get ready(): Promise<Tether | null> {\n return initPromise ?? Promise.resolve(null);\n },\n query,\n withContext,\n cart: Cart,\n getProduct,\n getProducts,\n getRecommendations,\n getScope,\n addGroup,\n account: accountApi,\n primeProduct,\n normalizeProductKey,\n clearProductCache,\n formatMoney,\n imageUrl,\n plans,\n TetherError,\n resolveProductKey,\n setCountry,\n detectMarket,\n getLocalization,\n diagnose,\n on,\n};\n\n/** `window.Tether`, and what `import Tether from '@occluze/tether'` gives. */\nexport type Tether = typeof publicApi;\n\nconst internals: TetherInternals = { __core: coreModules, __parts: {} };\n\n/** `Tether`, also carrying `TetherInternals`, which the published type leaves out. */\nexport const api: Tether = Object.assign(publicApi, internals);\n\n/** @alias `api`, for `import Tether from '@occluze/tether'`. */\nexport default api;\n\n/**\n * The plain `<script>` setup starts by itself, unless `autoMount: false` or a framework integration (Astro), which\n * starts Tether itself.\n */\nfunction autoStart() {\n // `window.Tether` is declared by the published types, which describe this object to sites; the source doesn't\n // depend on them.\n Reflect.set(window, 'Tether', api);\n const config = pageGlobal('TETHER_CONFIG');\n if (!config || field(config, 'autoMount') === false || pageGlobal('TETHER_INTEGRATION')) return;\n const start = () => {\n init()\n .then(() => mount(document))\n .catch((error: unknown) => {\n reportError(error, 'init');\n });\n };\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });\n else start();\n}\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') autoStart();\n"],
5
- "mappings": "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",
6
- "names": ["STOREFRONT_API_VERSION", "QUARTERLY_VERSION", "parseVersion", "version", "match", "QUARTERLY_VERSION", "isQuarterlyVersion", "isRecord", "value", "field", "key", "METAFIELD_PART", "METAFIELD_NAMESPACE", "HOSTNAME", "COOKIE_NAME", "GLOBAL_NAME", "SITE_ORIGIN", "LOOPBACK_HOSTS", "MARKET_SOURCES", "CONSENT_MODES", "isText", "value", "isFunction", "domainOption", "invalid", "host", "isHostname", "isSiteOrHttpsUrl", "url", "partsUrlOption", "isPageOrigin", "toHttpsOrigin", "embedOriginsOption", "origins", "entries", "entry", "origin", "toMetafieldIdentifier", "ref", "namespace", "key", "dot", "metafieldsOption", "refs", "identifier", "when", "check", "isCookieName", "isDataLayer", "text", "flag", "hook", "sectionOption", "name", "schema", "isRecord", "section", "readers", "read", "field", "parsed", "ACCOUNT", "ACCOUNT_NEEDS_SERVER", "LOCALE_MARKET", "MARKETS", "names", "source", "locales", "locale", "market", "SHOPIFY_ANALYTICS", "mode", "ANALYTICS", "accountOption", "warn", "account", "server", "marketsOption", "analyticsOption", "COUNTRY", "NOT_A_COUNTRY", "REGIONAL_LANGUAGES", "LANGUAGE", "toCountry", "value", "code", "toLanguage", "raw", "primary", "DEFAULTS", "STOREFRONT_API_VERSION", "cfg", "readOnlyView", "target", "refuse", "key", "warn", "object", "value", "_object", "configView", "PREFIX", "SWITCH_OPTIONS", "TEXT_OPTIONS", "COUNT_OPTIONS", "HOOK_OPTIONS", "invalidOption", "name", "fallback", "dropped", "warnUnknownKeys", "options", "hookOption", "hookOptions", "countOption", "switchOption", "textOption", "localeOption", "choiceOption", "choices", "choice", "apiVersionOption", "isQuarterlyVersion", "codeOption", "parse", "code", "resolveConfig", "domain", "domainOption", "prefix", "resolved", "toCountry", "partsUrlOption", "toLanguage", "marketsOption", "analyticsOption", "accountOption", "metafieldsOption", "embedOriginsOption", "parseMarkets", "configure", "isConfigured", "log", "args", "reportError", "error", "context", "hookError", "messageOf", "error", "TetherError", "message", "status", "errors", "userErrors", "code", "__publicField", "matches", "value", "shape", "isRecord", "isShapeList", "item", "key", "inner", "orNull", "shaped", "data", "endpoint", "conf", "cfg", "STRINGS_AND_COMMENTS", "OPERATION_START", "operationOf", "document", "inContext", "query", "country", "language", "args", "tag", "blanked", "text", "selectionStart", "header", "end", "withContext", "ctx", "toCountry", "toLanguage", "RETRY_BACKOFF_MS", "THROTTLE_BACKOFF_MS", "SECURITY_REJECTION", "MS_PER_SECOND", "INVENTORY_FIELD", "storesWithoutInventory", "storeOf", "queryText", "access", "context", "operation", "written", "sleep", "ms", "resolve", "statusText", "adminHint", "toGraphQLError", "field", "toGraphQLResponse", "json", "invalidResponse", "res", "graphQLFailure", "error", "networkFailure", "retry", "messageOf", "graphQLOutcome", "codes", "serverError", "unclear", "attempt", "variables", "isData", "body", "request", "store", "retryWhileLeft", "retries", "sent", "outcome", "log", "gql", "options", "hasData", "untypedQuery", "unwrap", "payload", "name", "attr", "name", "cfg", "sel", "value", "get", "el", "has", "qsa", "root", "selector", "scoped", "scopeSelector", "ANALYTICS_EVENTS", "ownEvents", "ownEvent", "detail", "event", "isOwnEvent", "emit", "target", "HTML_NAMESPACE", "ALLOWED_ELEMENTS", "REMOVED_WITH_CONTENT", "GLOBAL_ATTRIBUTES", "ELEMENT_ATTRIBUTES", "URL_ATTRIBUTES", "SCRIPT_URL", "DATA_IMAGE", "OUT_OF_PLACE_PROPERTIES", "OUT_OF_PLACE_PREFIXES", "VIEWPORT_LENGTH", "PAINTS_BEYOND", "MAX_PAINT_PX", "NEGATIVE_LIMITS", "NUMBER", "NON_LENGTH_FUNCTION", "COMPUTED_VALUE", "FETCHING_VALUE", "IGNORED_IN_SCHEME", "IFRAME_SANDBOX", "IFRAME_FEATURES", "IFRAME_REFERRER_POLICY", "EMBED_ORIGINS", "isAllowedAttribute", "tag", "name", "isSafeUrl", "value", "url", "urlOrNull", "base", "pageOf", "document", "styleParser", "embedOrigins", "baseUrl", "origin", "isAllowedEmbed", "page", "allowedFeatures", "features", "directive", "feature", "isOutOfPlace", "property", "prefix", "limits", "movesBackTooFar", "number", "unit", "amount", "limit", "paintsTooFar", "cleanStyle", "style", "properties", "_", "index", "cleaned", "cleanAttributes", "element", "attribute", "isUrl", "hardenEmbedsAndLinks", "allow", "rel", "cleanElement", "isHtml", "storeHtmlFragment", "html", "template", "isHttpsUrl", "url", "formatMoney", "money", "locale", "amount", "productUrl", "productPath", "handle", "id", "numericId", "productKey", "raw", "value", "imageUrl", "url", "width", "height", "crop", "parsed", "error", "sizeAttribute", "el", "name", "attr", "cdnUrl", "withoutQuery", "sameImage", "a", "b", "cssUrl", "char", "setImage", "alt", "src", "templates", "hasTemplate", "container", "copyOf", "template", "templateOf", "known", "first", "repeat", "items", "render", "warn", "item", "index", "node", "leave", "onLeave", "done", "error", "reportError", "insertFresh", "wanted", "fresh", "onEnter", "previous", "repeatKeyed", "keyOf", "keyAttribute", "cfg", "leavingAttribute", "live", "existing", "key", "staying", "current", "SEEN", "SHOWN", "PREVIOUS_DISPLAY", "LAYOUT_DEBOUNCE_MS", "productUrl", "handle", "id", "cfg", "formatOr", "format", "fallback", "error", "formatPercent", "percentage", "locale", "formatMoney", "money", "formatDate", "iso", "setStoreHtml", "el", "html", "storeHtmlFragment", "isTextField", "isInput", "setText", "value", "text", "readText", "setTextTracked", "before", "reportError", "reveal", "previous", "attr", "show", "visible", "setSelected", "selected", "input", "layoutTimer", "pageGlobal", "name", "refreshScrollTrigger", "scrollTrigger", "refresh", "notifyLayoutChanged", "reason", "emit", "watchImages", "root", "loading", "img", "left", "done", "busy", "task", "parseJSONAttr", "raw", "get", "warn", "attributeText", "attributesFromJson", "json", "isRecord", "entry", "key", "toAttributes", "attributes", "attribute", "MONEY", "FULL_ACCESS", "inventoryField", "access", "IMAGE", "metafieldsField", "metafields", "identifiers", "ref", "identifier", "toMetafieldIdentifier", "namespace", "key", "SELLING_PLAN_FIELDS", "ALLOCATION_FIELDS", "productFragment", "o", "cfg", "cartFragment", "collectionFields", "selling_plans_exports", "__export", "GID", "allPlans", "allocationFor", "findPlan", "hasPlans", "planAdjustment", "planCheckoutCharge", "planPrice", "planSavings", "plansForVariant", "toGid", "DECIMAL", "toScaledInteger", "amount", "fractionDigits", "match", "sign", "whole", "fraction", "digits", "fractionLength", "isGreaterAmount", "other", "scaled", "scaledOther", "DEFAULT_FRACTION_DIGITS", "fractionDigitsByCurrency", "currencyFractionDigits", "currencyCode", "known", "toMinorUnits", "money", "fromMinorUnits", "minorUnits", "PERCENT", "toGid", "value", "type", "text", "GID", "allPlans", "product", "group", "plan", "hasPlans", "allocationFor", "variant", "planId", "allocation", "plansForVariant", "planPrice", "adjustment", "planCheckoutCharge", "planSavings", "onPlanPrice", "oneTime", "onPlan", "saved", "toMinorUnits", "fromMinorUnits", "planAdjustment", "findPlan", "ref", "plans", "gid", "needle", "option", "MONEY", "PRODUCT", "PRODUCT_CONNECTION", "isProductShape", "value", "field", "matches", "PRODUCT_NODE", "node", "isRecord", "LINE", "MONEY", "CART", "FOCUSABLE", "FOCUS_AFTER_OPEN_MS", "openWithoutDrawer", "returnFocus", "isFocusable", "el", "focusables", "drawer", "qsa", "drawers", "sel", "isOpenDrawer", "cfg", "isOpen", "all", "syncDrawerAria", "open", "button", "get", "focusInto", "target", "openCart", "opened", "first", "emit", "closeCart", "hadFocus", "toggleCart", "trapFocus", "event", "inside", "last", "active", "leaving", "MISS_CACHE_MS", "FRESH_MS", "productCache", "waiting", "stale", "freshAt", "pending", "flushScheduled", "generation", "isGid", "key", "normalizeProductKey", "productKey", "primeProduct", "product", "loaded", "getProduct", "rawKey", "TetherError", "cached", "loading", "resolve", "reject", "flush", "withProductFragment", "operation", "access", "productFragment", "cfg", "batchQuery", "keys", "ids", "handles", "definitions", "selections", "variables", "handle", "index", "settleWaiting", "settle", "waiter", "forgetMissLater", "batchGeneration", "miss", "isBatchData", "data", "isRecord", "byId", "byHandle", "matches", "orNull", "PRODUCT_NODE", "PRODUCT", "query", "log", "gql", "error", "found", "now", "clearProductCache", "hydrateProducts", "products", "takeStale", "failureIsReportedByTheRender", "refreshProduct", "normalized", "isCollectionAnswer", "shaped", "PRODUCT_CONNECTION", "isSearchAnswer", "isRecommendationsAnswer", "COLLECTION_QUERY", "collectionFields", "SEARCH_QUERY", "getProducts", "collection", "first", "after", "sortKey", "reverse", "filters", "rest", "getRecommendations", "intent", "list", "NTH_IMAGE_BIND", "METAFIELD_BIND", "MULTI_LINE_METAFIELD", "ALL_PLAN_GROUPS", "isSelect", "el", "selectsIn", "scope", "selector", "renderGallery", "product", "images", "rendered", "node", "hasTemplate", "img", "index", "sameImage", "repeat", "image", "target", "sel", "qsa", "setImage", "renderMetafield", "ref", "dot", "namespace", "key", "metafield", "field", "setText", "PRODUCT_TEXTS", "formatDate", "formatMoney", "renderTags", "exclude", "tag", "tags", "renderProductBind", "text", "setStoreHtml", "url", "productUrl", "renderIndexedBind", "nth", "renderOptionLists", "container", "name", "get", "option", "candidate", "has", "show", "value", "attr", "swatchEl", "swatch", "renderSelects", "select", "variant", "plans", "plan", "oneTime", "oneTimeLabel", "renderPlanLists", "raw", "group", "writeProduct", "DEFAULT_TITLE", "quantityOf", "line", "pending", "numericId", "gid", "lineUrl", "variant", "base", "productUrl", "query", "planId", "productTitle", "LINE_TEXT", "option", "formatMoney", "view", "component", "ATTRIBUTE_KEY", "lineValue", "key", "name", "attribute", "fillPlanSelect", "select", "product", "plans", "allPlans", "allowOneTime", "get", "oneTime", "attr", "choices", "plan", "signature", "choice", "show", "renderLineState", "node", "isSubscription", "warning", "candidate", "qsa", "sel", "el", "setTextTracked", "conditions", "isGreaterAmount", "visible", "renderLine", "setImage", "url", "value", "isSelect", "getProduct", "error", "reportError", "renderConditions", "cart", "count", "hasSubscription", "code", "renderFields", "setText", "container", "repeat", "discount", "target", "renderCart", "money", "syncDrawerAria", "isOpen", "repeatKeyed", "cfg", "notifyLayoutChanged", "showSavedCount", "getStored", "key", "area", "setStored", "value", "removeStored", "CART_ID", "status", "warnings", "pendingQuantities", "cartStatus", "currentCart", "currentWarnings", "statusOf", "cart", "seenKey", "cfg", "savedCartId", "stored", "getStored", "CART_ID", "forgetSavedCart", "removeStored", "keepQuietly", "setStored", "render", "renderCart", "announce", "reason", "emit", "commit", "settleLoad", "outcome", "lastKnownCount", "text", "count", "showLastKnownCount", "showSavedCount", "setWarnings", "notes", "note", "index", "other", "waitOnly", "serial", "previous", "task", "run", "runInOrder", "enqueue", "value", "error", "pendingQuantityOf", "line", "quantityOf", "meta", "isOpen", "subscribe", "listener", "events", "name", "call", "UNKNOWN_CART_MESSAGE", "isPayloadOf", "name", "shaped", "orNull", "CART", "isCartGone", "error", "TetherError", "userError", "graphQLError", "isUnclearFailure", "code", "status", "toLineInput", "input", "quantity", "options", "spec", "merchandiseId", "GID", "asked", "line", "planId", "attributes", "toAttributes", "mutationText", "args", "inner", "access", "cartFragment", "send", "variables", "data", "gql", "payload", "unwrap", "setWarnings", "createCart", "withIdentity", "cfg", "mutateCart", "createInput", "cartId", "savedCartId", "forgetSavedCart", "log", "fetchCart", "id", "fetchCheckoutUrl", "toLineUpdate", "lineId", "patch", "change", "variantId", "updateLines", "lines", "removeLines", "lineIds", "setLineQuantity", "target", "resolveAccountCheckout", "setCheckoutResolver", "resolver", "askCheckoutUrl", "cartId", "fetchCheckoutUrl", "url", "error", "accountCheckoutUrl", "reportError", "checkoutUrl", "cartAnswer", "accountUrl", "answer", "TetherError", "detachOpenerIfAllowed", "tab", "checkout", "target", "cfg", "savedCartId", "cart", "currentCart", "isHttpsUrl", "emit", "currentLines", "currentCart", "reloadAfterUnclearFailure", "id", "savedCartId", "cart", "fetchCart", "forgetSavedCart", "commit", "error", "reportError", "runChange", "task", "enqueue", "isUnclearFailure", "changeQuantity", "lineId", "delta", "line", "candidate", "target", "pendingQuantityOf", "job", "pendingQuantities", "announce", "isLatest", "setLineQuantity", "keepQuietly", "discountCodesFrom", "codes", "current", "discount", "list", "code", "mergedAttributes", "attributes", "merged", "attribute", "toAttributes", "value", "key", "init", "settleLoad", "showLastKnownCount", "isCartGone", "Cart", "cartStatus", "currentWarnings", "setCheckoutResolver", "render", "subscribe", "input", "quantity", "options", "lines", "toLineInput", "mutateCart", "TetherError", "emit", "cfg", "openCart", "patch", "toLineUpdate", "updateLines", "lineIds", "ids", "removeLines", "note", "variables", "wanted", "unwanted", "existing", "buyerIdentity", "create", "checkout", "drawers", "isOpen", "closeCart", "toggleCart", "PERCENT", "timesQuantity", "money", "quantity", "fromMinorUnits", "toMinorUnits", "selectionOf", "variant", "option", "findVariant", "product", "selected", "isValueAvailable", "name", "value", "purchasePlanId", "purchase", "linePurchase", "allocationFor", "normalisePurchase", "kind", "preferredPlanId", "plans", "allPlans", "known", "plan", "onVariant", "initialChoice", "sellingPlan", "defaultPlan", "defaultPurchase", "variants", "wantedId", "GID", "start", "candidate", "fromUrl", "findPlan", "offered", "showConditions", "lowStock", "variantCount", "sale", "requiresSellingPlan", "stock", "low", "onSale", "onPlan", "productView", "choice", "exact", "planId", "line", "allocation", "price", "planPrice", "compareAt", "planSavings", "variantBindTexts", "view", "format", "compareAtPrice", "forQuantity", "texts", "charge", "planCheckoutCharge", "planBindTexts", "adjustment", "planAdjustment", "decodeOrKeep", "text", "error", "URL_SOURCE", "resolveProductKey", "el", "location", "name", "raw", "get", "source", "key", "segments", "segment", "decodeOrKeep", "STATUS_KEYS", "isScopeStatus", "value", "status", "codeOf", "error", "isNotFound", "boundary", "sel", "ownedBy", "root", "errorCode", "code", "setScopeStatus", "notFound", "failed", "attr", "scoped", "shows", "show", "button", "FORMAT", "formatMoney", "formatPercent", "isInput", "el", "addButtonLabel", "button", "label", "qsa", "sel", "original", "renderOptionStates", "scope", "view", "product", "selectedValue", "authored", "setText", "get", "container", "name", "node", "value", "setSelected", "unavailable", "isValueAvailable", "selectsIn", "select", "renderPurchaseStates", "variant", "purchase", "line", "isSelect", "mode", "planId", "allocationFor", "plan", "candidate", "texts", "planBindTexts", "text", "renderConditions", "conditions", "STATUS_KEYS", "status", "key", "visible", "ownedBy", "show", "renderAddButtons", "disabled", "soldOutText", "subscribeText", "writeVariant", "variantBindTexts", "setImage", "attr", "productSelector", "sel", "isField", "el", "isElement", "target", "closestOwn", "scope", "selector", "onAddClick", "button", "label", "original", "addButtonLabel", "addingText", "get", "busy", "setText", "error", "reportError", "emit", "onClick", "event", "optionValue", "optionList", "plan", "purchase", "purchaseKind", "plus", "minus", "add", "value", "onChange", "bindProductEvents", "scopes", "productSelector", "sel", "knownOptionNames", "rememberOptionNames", "products", "product", "option", "isInput", "el", "isField", "lineAttributeValue", "ProductScope", "root", "__publicField", "allPlans", "params", "choice", "initialChoice", "get", "cfg", "has", "attr", "bindProductEvents", "watchImages", "notifyLayoutChanged", "emit", "variantId", "same", "variant", "selectionOf", "selector", "inner", "scoped", "authored", "options", "index", "planId", "purchasePlanId", "plan", "allocationFor", "linePurchase", "line", "bought", "attributes", "fixed", "attributesFromJson", "parseJSONAttr", "fields", "value", "name", "withValue", "firstWithValue", "next", "score", "closest", "best", "id", "candidate", "GID", "kind", "normalisePurchase", "ref", "findPlan", "quantity", "minimum", "setText", "cart", "Cart", "writeProduct", "view", "productView", "writeVariant", "URL_QUERY_SOURCE", "listSelector", "sel", "isElement", "target", "toLimit", "raw", "toIntent", "ListScope", "root", "__publicField", "setScopeStatus", "event", "more", "busy", "selector", "scoped", "query", "get", "first", "sortKey", "has", "key", "resolveProductKey", "scope", "TetherError", "getRecommendations", "token", "stale", "recommendations", "limit", "page", "getProducts", "emit", "error", "isNotFound", "reportError", "collection", "el", "setText", "setStoreHtml", "setImage", "container", "repeat", "node", "product", "attr", "empty", "show", "notifyLayoutChanged", "HYDRATE_SELECTOR", "mountedProducts", "mountedLists", "liveProducts", "liveLists", "latestRequest", "pruneLive", "el", "list", "within", "root", "selector", "qsa", "applyPageTitle", "product", "template", "get", "mountWithoutKey", "fromUrl", "raw", "setScopeStatus", "emit", "listSelector", "warn", "attr", "showLoadFailure", "key", "error", "notFound", "isNotFound", "reportError", "log", "mountProduct", "source", "resolveProductKey", "request", "isLatest", "scopes", "trackMarket", "getProduct", "existing", "ProductScope", "cfg", "takeStale", "queueRefresh", "retryFailed", "refreshQueue", "flushRefresh", "applyRefresh", "fresh", "scope", "rootKey", "keys", "refreshProduct", "marketPending", "loading", "settleMarket", "waits", "wait", "toHydratePayload", "json", "isRecord", "products", "entries", "toCountry", "toLanguage", "isProductShape", "readScripts", "hydrateFromPage", "script", "country", "language", "rememberOptionNames", "hydrateProducts", "storageKey", "cfg", "pageStorage", "key", "getStored", "parseJsonOrNull", "text", "error", "toMarket", "value", "country", "toCountry", "locale", "toLanguage", "parseSavedMarket", "readSavedMarket", "storage", "pageStorage", "storageKey", "configuredMarket", "lastPageHadOwnMarket", "pageMarket", "meta", "name", "cfg", "country", "toCountry", "toLanguage", "visitorMarket", "markets", "readSavedMarket", "loadPart", "error", "reportError", "applyStartMarket", "overrides", "page", "market", "log", "sameCode", "a", "b", "marketForArrival", "leftOwnMarket", "target", "setCountry", "code", "options", "part", "PART_INTEGRITY", "isTetherInternals", "value", "isRecord", "coreModules", "cfg", "isConfigured", "log", "MARKET_SOURCES", "parseMarkets", "reportError", "warn", "endpoint", "gql", "orNull", "shaped", "TetherError", "ANALYTICS_EVENTS", "attr", "attributesFromJson", "emit", "formatDate", "formatMoney", "get", "has", "hasTemplate", "isOwnEvent", "parseJSONAttr", "productUrl", "qsa", "repeat", "sel", "setImage", "setText", "show", "clearProductCache", "getProduct", "allPlans", "boundary", "resolveProductKey", "Cart", "scopes", "listSelector", "liveLists", "liveProducts", "mountedProducts", "mountProduct", "pruneLive", "within", "toCountry", "toLanguage", "setCountry", "isHttpsUrl", "loadPart", "MINIFIED_SCRIPT", "coreScript", "coreScriptUrl", "parts", "partsFolder", "applyIntegrity", "script", "integrity", "loadScript", "name", "folder", "file", "resolve", "reject", "url", "tether", "part", "isTetherInternals", "problem", "loading", "suffix", "PART_INTEGRITY", "hintKey", "cfg", "removeFromBothAreas", "key", "removeStored", "forgetBrowserModeTokens", "isLoginOutcome", "value", "ACCOUNT_ATTRIBUTES", "accountSelector", "name", "attr", "hasLoginHint", "getStored", "hintKey", "mountAccountIfUsed", "root", "cfg", "within", "isLoginOutcome", "loadPart", "part", "error", "reportError", "fromPart", "args", "call", "accountApi", "lineKeyAttribute", "cfg", "isElement", "target", "lineOf", "line", "id", "reportFailure", "change", "context", "error", "reportError", "onLineClick", "event", "found", "sel", "Cart", "delta", "lineDelta", "candidate", "onClick", "clear", "busy", "checkout", "showLineBusy", "onChange", "hasValue", "onKeydown", "trapFocus", "bindCartControls", "isElement", "target", "codeInputOf", "form", "input", "sel", "markInvalid", "invalid", "applyCode", "code", "Cart", "emit", "error", "TetherError", "reportError", "onSubmit", "event", "busy", "onInput", "onClick", "remove", "attr", "bindDiscountControls", "addGroup", "button", "loadPart", "part", "reportExtrasError", "error", "reportError", "onClick", "event", "target", "sel", "busy", "onStorage", "cfg", "onVariantChanged", "isOwnEvent", "attr", "bindExtrasControls", "mountedBody", "rememberDocumentMount", "onPageLoad", "hooks", "target", "marketForArrival", "setCountry", "error", "reportError", "bindAstroRouter", "onChange", "event", "sel", "onOnline", "retryFailed", "liveLists", "list", "attr", "bindNavigation", "cfg", "VERSION", "OBSERVE_DEBOUNCE_MS", "pageConfig", "config", "pageGlobal", "initPromise", "observer", "analyticsQueue", "controlsBound", "mountLists", "root", "count", "within", "listSelector", "el", "mountedLists", "list", "ListScope", "liveLists", "cfg", "event", "isOwnEvent", "mount", "trackMarket", "mountParts", "sel", "loadPart", "part", "error", "reportError", "mountAccountIfUsed", "mountWithin", "mountProduct", "isConfigured", "rememberDocumentMount", "pruneLive", "hydrateFromPage", "settleMarket", "Cart", "log", "mountAdded", "added", "roots", "other", "sum", "unmount", "getScope", "scopes", "bindControls", "bindCartControls", "bindDiscountControls", "bindExtrasControls", "bindNavigation", "startAnalyticsQueue", "queue", "ANALYTICS_EVENTS", "name", "cartInPageMarket", "cart", "country", "cartCountry", "init", "overrides", "configure", "forgetBrowserModeTokens", "startObserver", "applyStartMarket", "emit", "api", "warn", "timer", "queued", "mutations", "mutation", "node", "detectMarket", "markets", "getLocalization", "diagnose", "options", "on", "listener", "type", "handler", "publicApi", "configView", "query", "withContext", "getProduct", "getProducts", "getRecommendations", "addGroup", "accountApi", "primeProduct", "normalizeProductKey", "clearProductCache", "formatMoney", "imageUrl", "selling_plans_exports", "TetherError", "resolveProductKey", "setCountry", "internals", "coreModules", "index_default", "autoStart", "field", "start"]
3
+ "sources": ["../server/src/shared/api-versions.ts", "../src/api-version.ts", "../server/src/shared/json.ts", "../src/config-sections.ts", "../server/src/shared/market-codes.ts", "../src/config.ts", "../server/src/shared/error-message.ts", "../src/client.ts", "../src/markup.ts", "../src/store-html.ts", "../src/shopify-values.ts", "../src/images.ts", "../src/repeaters.ts", "../src/dom.ts", "../src/fragments.ts", "../src/selling-plans.ts", "../src/amounts.ts", "../src/product-shape.ts", "../src/cart/cart-shape.ts", "../src/cart/drawer.ts", "../src/loader.ts", "../src/product-render.ts", "../src/cart/render.ts", "../src/storage.ts", "../server/src/shared/cart-id.ts", "../src/cart/store.ts", "../src/cart/mutations.ts", "../src/cart/checkout.ts", "../src/cart.ts", "../src/product-state.ts", "../src/uri.ts", "../src/status.ts", "../src/variant-render.ts", "../src/product-events.ts", "../src/product.ts", "../src/collection.ts", "../src/mounting.ts", "../src/markets.ts", "../src/page-market.ts", "../src/part-integrity.ts", "../src/part-registry.ts", "../src/version.ts", "../src/lazy-iife.ts", "../src/account-marks.ts", "../src/account-api.ts", "../src/controls/cart-controls.ts", "../src/controls/discount.ts", "../src/controls/extras.ts", "../src/controls/navigation.ts", "../src/index.ts"],
4
+ "sourcesContent": ["/**\n * The Shopify API versions Tether is built and smoke-tested against, for the browser script and the Worker alike. The\n * three APIs are versioned separately, so each has its own constant even while they hold the same quarter. A new\n * version must pass `npm run smoke` on both the pinned version and the next one.\n */\n\n/** A dated, quarterly version such as `2026-07`. */\nexport type ApiVersion = `${number}-${'01' | '04' | '07' | '10'}`;\n\n/** The Storefront API: products, carts, cart buyer identity. */\nexport const STOREFRONT_API_VERSION: ApiVersion = '2026-07';\n\n/** The Customer Account API: the logged-in customer's orders, addresses and subscriptions. */\nexport const CUSTOMER_ACCOUNT_API_VERSION: ApiVersion = '2026-07';\n\n/** The Admin API the sync reads products with. */\nexport const ADMIN_API_VERSION: ApiVersion = '2026-07';\n", "/**\n * Shopify API versions are quarterly (YYYY-01, -04, -07, -10) and supported for 12 months. Tether pins a dated version\n * and never targets `unstable` (it changes without notice). Used by the config, the diagnose part and the release\n * scripts.\n */\n\nimport type { ApiVersion } from '../server/src/shared/api-versions.ts';\n\nconst QUARTERLY_VERSION = /^(\\d{4})-(01|04|07|10)$/;\nconst MONTHS_PER_QUARTER = 3;\nconst QUARTERS_PER_YEAR = 4;\nconst MONTHS_SUPPORTED = 12;\n/** Months left at which the status starts asking for an upgrade. */\nconst UPGRADE_WARNING_MONTHS = 3;\nconst AVERAGE_MONTH_MS = 30.44 * 24 * 3600 * 1000;\n\nexport interface ApiVersionStatus {\n version: string;\n /** Released and still supported. */\n ok: boolean;\n /** What to do about it, for people. */\n note: string;\n /** Months of support left, rounded; negative once support ended. */\n monthsLeft?: number;\n /** The version after this one. */\n next?: string;\n}\n\nfunction parseVersion(version: unknown): { year: number; month: number } | null {\n const match = QUARTERLY_VERSION.exec(typeof version === 'string' ? version : '');\n if (!match) return null;\n return { year: Number(match[1]), month: Number(match[2]) };\n}\n\n/** @returns True for a dated version such as `2026-07`. */\nexport const isQuarterlyVersion = (version: unknown): version is ApiVersion => parseVersion(version) !== null;\n\n/** @param month 1 to 12. */\nconst formatVersion = (year: number, month: number) => `${year}-${String(month).padStart(2, '0')}`;\n\n/**\n * `'2026-07'` \u2192 `'2026-10'`, `'2026-10'` \u2192 `'2027-01'`.\n *\n * @param steps Quarters ahead.\n * @throws {Error} When `version` isn't a quarterly version.\n */\nexport function nextApiVersion(version: string, steps = 1): string {\n const parsed = parseVersion(version);\n if (!parsed) throw new Error(`not a quarterly Shopify API version: ${version}`);\n const quarter = parsed.year * QUARTERS_PER_YEAR + (parsed.month - 1) / MONTHS_PER_QUARTER + steps;\n return formatVersion(Math.floor(quarter / QUARTERS_PER_YEAR), (quarter % QUARTERS_PER_YEAR) * MONTHS_PER_QUARTER + 1);\n}\n\n/** The support window of `version`, relative to `now`. */\nexport function apiVersionStatus(version: string, now = new Date()): ApiVersionStatus {\n if (version === 'unstable') {\n return { version, ok: false, note: '\"unstable\" changes without notice: pin a dated version such as 2026-07' };\n }\n const parsed = parseVersion(version);\n if (!parsed) return { version, ok: false, note: 'not a quarterly version (YYYY-01, -04, -07 or -10)' };\n const released = new Date(Date.UTC(parsed.year, parsed.month - 1, 1));\n const supportedUntil = new Date(Date.UTC(parsed.year, parsed.month - 1 + MONTHS_SUPPORTED, 1));\n const monthsLeft = (supportedUntil.getTime() - now.getTime()) / AVERAGE_MONTH_MS;\n const isReleased = released <= now;\n let note = `supported until about ${supportedUntil.toISOString().slice(0, 7)}`;\n if (!isReleased) note = 'not released yet (release candidate at best)';\n else if (monthsLeft <= 0) {\n note = 'no longer supported: Shopify falls back to the oldest supported version. Upgrade (see docs/reference.md)';\n } else if (monthsLeft < UPGRADE_WARNING_MONTHS) {\n note = `${note}: plan the upgrade to ${nextApiVersion(version)} or later (see docs/reference.md)`;\n }\n return {\n version,\n ok: isReleased && monthsLeft > 0,\n monthsLeft: Math.round(monthsLeft),\n next: nextApiVersion(version),\n note,\n };\n}\n", "/**\n * Reading JSON that arrives from outside (Shopify's answers, a Worker request body) field by field, for the browser\n * script and the Worker alike. Every reader takes `unknown`, so a missing or mistyped field is `undefined`, never a\n * crash.\n */\n\n/** A plain object, as parsed JSON holds it. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** The field `key` of `value`, or `undefined` when `value` is not a plain object. */\nexport function field(value: unknown, key: string): unknown {\n return isRecord(value) ? value[key] : undefined;\n}\n\n/** The value when it is an array, else an empty list. */\nexport function asList(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\n/** A non-empty string, or `null`: Shopify sends `\"\"` and `null` alike for an unset text field. */\nexport function textOrNull(value: unknown): string | null {\n return typeof value === 'string' && value !== '' ? value : null;\n}\n\nexport const numberOrNull = (value: unknown): number | null =>\n typeof value === 'number' && Number.isFinite(value) ? value : null;\n\n/** A count Shopify always sends; 0 for an answer without it. */\nexport const countOrZero = (value: unknown): number => numberOrNull(value) ?? 0;\n\nexport const listOf = <T>(value: unknown, read: (item: unknown) => T): T[] => asList(value).map((item) => read(item));\n\n/**\n * @param connection A GraphQL connection: `{ nodes: [...] }`.\n */\nexport const nodesOf = <T>(connection: unknown, read: (item: unknown) => T): { nodes: T[] } => ({\n nodes: listOf(field(connection, 'nodes'), read),\n});\n\n/** The non-empty strings of a list. */\nexport const textList = (value: unknown): string[] => listOf(value, textOrNull).filter((line) => line !== null);\n", "/**\n * Parsers for the options with structure, whose values become URLs, credentials, storage names or query text: the\n * store's domain, the metafields, the embed origins and the nested `account`, `markets` and `analytics` objects.\n * `account.server` becomes the base of credentialed requests, so a value of the wrong kind is dropped (with a warning\n * through `invalid`), never used. `config.ts` resolves the rest.\n */\nimport { isRecord } from '../server/src/shared/json.ts';\nimport type { AccountConfig, AnalyticsConfig, MarketsConfig, MetafieldIdentifier, MetafieldRef } from './config.ts';\n\n/** Warns that an option was dropped. */\ntype Invalid = (name: string, value: unknown) => void;\n\n/** A host name, without protocol or path: what `domainOption` keeps of the `domain` option. */\nexport type ShopDomain = string & { readonly __brand: 'ShopDomain' };\ntype LocaleMarket = NonNullable<MarketsConfig['locales']>[string];\ntype ShopifyAnalyticsOptions = Exclude<NonNullable<AnalyticsConfig['shopify']>, boolean>;\n\n/**\n * Metafield identifiers are written into the product fragment's text: a GraphQL fragment can't declare variables, so\n * they can't be sent as variables. Only these characters are let through, which covers Shopify's namespaces and keys.\n */\nconst METAFIELD_PART = /^[A-Za-z0-9_.-]+$/;\n/** A namespace is the same, or an app-reserved one: `$app` or `$app:<name>`. */\nconst METAFIELD_NAMESPACE = /^(\\$app(:[A-Za-z0-9_-]+)?|[A-Za-z0-9_.-]+)$/;\n/** A DNS name: labels of letters, digits and inner hyphens. No port, user or path. */\nconst HOSTNAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;\n/** A cookie name, as RFC 6265 allows it. */\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n/** A global variable's name, which `analytics.dataLayer` names. */\nconst GLOBAL_NAME = /^[A-Za-z_$][\\w$]*$/;\n/** Stands for the site's own origin, to tell a path on the site from a URL elsewhere. */\nconst SITE_ORIGIN = 'https://site.invalid';\n/** Hosts a developer's own machine answers on, where plain http is the norm (`wrangler dev`). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);\n/** Every source `markets.detect` reads. */\nexport const MARKET_SOURCES: readonly ('saved' | 'server' | 'webflow')[] = ['saved', 'server', 'webflow'];\nconst CONSENT_MODES: readonly NonNullable<ShopifyAnalyticsOptions['consent']>[] = [\n 'no-banner',\n 'default-banner',\n 'custom-banner',\n];\n\nconst isText = (value: unknown): value is string => typeof value === 'string' && value !== '';\n\n/** A hook the site passes. What it is called with is up to Tether, and what it returns is not used. */\nconst isFunction = (value: unknown): value is (...args: unknown[]) => unknown => typeof value === 'function';\n\n/**\n * @returns The store's domain without protocol or path (`https://shop.example/x` \u2192 `shop.example`), `''` for anything\n * that isn't a host name.\n */\nexport function domainOption(value: unknown, invalid: Invalid): ShopDomain | '' {\n if (typeof value !== 'string' || !value) return '';\n const host = value.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');\n if (isHostname(host)) return host;\n invalid('domain', value);\n return '';\n}\n\n/** @returns True for a path on this site, an https URL, or an http one on the developer's machine. */\nfunction isSiteOrHttpsUrl(value: unknown): value is string {\n if (!isText(value)) return false;\n try {\n const url = new URL(value, `${SITE_ORIGIN}/`);\n if (url.origin === SITE_ORIGIN) return true;\n return url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname));\n } catch {\n // Not a URL at all.\n return false;\n }\n}\n\n/**\n * The folder of the optional parts. It must be https or on this site, which keeps parts off plain http (mixed content,\n * rewritable on the way); it doesn't vouch for the host, which the site chose.\n *\n * @returns The folder as written, `null` for none or one elsewhere (the parts then load beside the core).\n */\nexport function partsUrlOption(value: unknown, invalid: Invalid): string | null {\n if (value === undefined || value === null || value === '') return null;\n if (typeof value === 'string' && (isSiteOrHttpsUrl(value) || isPageOrigin(value))) return value;\n invalid('partsUrl', value);\n return null;\n}\n\n/** @returns True for an absolute URL on the page's own origin (a site served over plain http). */\nfunction isPageOrigin(value: string): boolean {\n if (typeof location === 'undefined') return false;\n try {\n return new URL(value).origin === location.origin;\n } catch {\n // Not an absolute URL: `isSiteOrHttpsUrl` has judged it.\n return false;\n }\n}\n\n/** @returns An https origin written as just that (`https://video.example`), `null` for anything else. */\nfunction toHttpsOrigin(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && url.href === new URL(url.origin).href ? url.origin : null;\n } catch {\n // Not an absolute URL.\n return null;\n }\n}\n\n/**\n * The origins a site lets description iframes load from. Each one is trusted to show only what the site expects in a\n * description, so only a bare https origin is taken: a path would suggest a narrower trust than is given.\n *\n * @returns The valid origins; each other entry is skipped with a warning.\n */\nexport function embedOriginsOption(value: unknown, invalid: Invalid): string[] {\n if (!Array.isArray(value)) {\n invalid('embedOrigins', value);\n return [];\n }\n const origins: string[] = [];\n const entries: unknown[] = value;\n for (const entry of entries) {\n const origin = toHttpsOrigin(entry);\n if (origin) origins.push(origin);\n else invalid('embedOrigin', entry);\n }\n return origins;\n}\n\n/** @returns The identifier of `'namespace.key'` or `{ namespace, key }`, when both parts are valid identifiers. */\nexport function toMetafieldIdentifier(ref: unknown): MetafieldIdentifier | null {\n let namespace: unknown;\n let key: unknown;\n if (typeof ref === 'string') {\n const dot = ref.indexOf('.');\n namespace = dot > 0 ? ref.slice(0, dot) : '';\n key = ref.slice(dot + 1);\n } else if (typeof ref === 'object' && ref !== null) {\n namespace = Reflect.get(ref, 'namespace');\n key = Reflect.get(ref, 'key');\n }\n if (typeof namespace !== 'string' || typeof key !== 'string') return null;\n return METAFIELD_NAMESPACE.test(namespace) && METAFIELD_PART.test(key) ? { namespace, key } : null;\n}\n\n/** @returns The refs that name a valid identifier, as written; each other one is skipped with a warning. */\nexport function metafieldsOption(value: unknown, invalid: Invalid): MetafieldRef[] {\n if (!Array.isArray(value)) {\n invalid('metafields', value);\n return [];\n }\n const refs: MetafieldRef[] = [];\n const entries: unknown[] = value;\n for (const ref of entries) {\n const identifier = toMetafieldIdentifier(ref);\n if (identifier) refs.push(typeof ref === 'string' ? ref : identifier);\n else invalid('metafield', ref);\n }\n return refs;\n}\n\n/** How each field of a nested option is read: the field's value when it is of the right kind, else `undefined`. */\ntype Schema<Section extends object> = { [Key in keyof Section]-?: (value: unknown) => Section[Key] | undefined };\n\nconst when =\n <T>(check: (value: unknown) => value is T) =>\n (value: unknown): T | undefined =>\n check(value) ? value : undefined;\n\n/** The pattern is what makes a string a `ShopDomain`. */\nconst isHostname = (value: unknown): value is ShopDomain => isText(value) && HOSTNAME.test(value);\n\nconst isCookieName = (value: unknown): value is string => isText(value) && COOKIE_NAME.test(value);\n\n/** `true` for `window.dataLayer`, or the name of another global array. */\nconst isDataLayer = (value: unknown): value is boolean | string =>\n typeof value === 'boolean' || (isText(value) && GLOBAL_NAME.test(value));\n\nconst url = when(isSiteOrHttpsUrl);\nconst text = when(isText);\nconst flag = when((value): value is boolean => typeof value === 'boolean');\nconst hook = when(isFunction);\n\n/** @returns The fields of the right kind; each other one is dropped with a warning. */\nfunction sectionOption<Section extends object>(\n name: string,\n value: unknown,\n schema: Schema<Section>,\n invalid: Invalid,\n): Partial<Section> | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value)) {\n invalid(name, value);\n return null;\n }\n const section: Partial<Section> = {};\n for (const key in schema) {\n const field = value[key];\n if (field === undefined) continue;\n const parsed = schema[key](field);\n if (parsed === undefined) invalid(`${name}.${key}`, field);\n else section[key] = parsed;\n }\n return section;\n}\n\nconst ACCOUNT: Schema<Partial<AccountConfig>> = {\n server: url,\n orderUrl: url,\n subscriptionsUrl: url,\n paymentUrl: url,\n logoutUrl: url,\n locale: text,\n navigate: hook,\n};\n\n/** Customer tokens are never kept in the browser, so an `account` without the Worker is off. */\nconst ACCOUNT_NEEDS_SERVER = 'customer accounts need the Worker since 0.7.0: set account.server';\n\nconst LOCALE_MARKET: Schema<LocaleMarket> = { country: text, language: text, locale: text };\n\nconst MARKETS: Schema<MarketsConfig> = {\n // A single source name counts as a list of one.\n detect: (value) => {\n if (typeof value === 'boolean') return value;\n const names: unknown[] = Array.isArray(value) ? value : [value];\n return typeof value === 'string' || Array.isArray(value)\n ? MARKET_SOURCES.filter((source) => names.includes(source))\n : undefined;\n },\n locales: (value) => {\n if (!isRecord(value)) return undefined;\n const locales: Record<string, LocaleMarket> = {};\n for (const [locale, entry] of Object.entries(value)) {\n const market = sectionOption(`markets.locales.${locale}`, entry, LOCALE_MARKET, () => undefined);\n if (market) locales[locale] = market;\n }\n return locales;\n },\n cookie: when(isCookieName),\n remember: flag,\n strict: flag,\n};\n\n/**\n * `proxy` says the site serves the Storefront API on its own domain. Shopify's script only needs to know that it\n * does, not where, so any truthy value (including a path such as `'/api'`) turns it on.\n */\nconst SHOPIFY_ANALYTICS: Schema<ShopifyAnalyticsOptions> = {\n consent: (value) => CONSENT_MODES.find((mode) => mode === value),\n proxy: (value) => value !== undefined && Boolean(value),\n};\n\nconst ANALYTICS: Schema<AnalyticsConfig> = {\n dataLayer: when(isDataLayer),\n shopify: (value) =>\n typeof value === 'boolean'\n ? value\n : (sectionOption('analytics.shopify', value, SHOPIFY_ANALYTICS, () => undefined) ?? undefined),\n onEvent: hook,\n};\n\n/**\n * @param warn Says why an `account` without a usable `server` is off.\n * @returns `null` without a usable `account.server`: nothing of the account part loads.\n */\nexport function accountOption(value: unknown, invalid: Invalid, warn: (message: string) => void): AccountConfig | null {\n const account = sectionOption('account', value, ACCOUNT, invalid);\n if (account === null) return null;\n const { server } = account;\n if (server === undefined) {\n warn(ACCOUNT_NEEDS_SERVER);\n return null;\n }\n return { ...account, server };\n}\n\nexport const marketsOption = (value: unknown, invalid: Invalid): MarketsConfig | null =>\n sectionOption('markets', value, MARKETS, invalid);\n\nexport const analyticsOption = (value: unknown, invalid: Invalid): AnalyticsConfig | null =>\n sectionOption('analytics', value, ANALYTICS, invalid);\n", "/**\n * Country and language codes as Shopify takes them, parsed the same way in the browser and the Worker: anything that\n * isn't a code is dropped rather than repaired.\n */\n\n/** An ISO 3166-1 alpha-2 code, upper case. */\nexport type CountryCode = string & { readonly __brand: 'CountryCode' };\n/** A Shopify `LanguageCode` value. */\nexport type LanguageCode = string & { readonly __brand: 'LanguageCode' };\n\nconst COUNTRY = /^[A-Z]{2}$/;\n/** Cloudflare's codes for an unknown country (XX) and Tor (T1), which a `cf-ipcountry` cookie can carry. */\nconst NOT_A_COUNTRY = new Set(['XX', 'T1']);\n/** Shopify `LanguageCode` values with a region. Every other language is its plain ISO 639 code. */\nconst REGIONAL_LANGUAGES = new Set(['PT_BR', 'PT_PT', 'ZH_CN', 'ZH_TW']);\nconst LANGUAGE = /^[A-Z]{2,3}$/;\n\n/** @returns `'de'` \u2192 `'DE'`; `null` for anything that is not a country code. */\nexport function toCountry(value: unknown): CountryCode | null {\n const code = (typeof value === 'string' ? value : '').trim().toUpperCase();\n if (!COUNTRY.test(code) || NOT_A_COUNTRY.has(code)) return null;\n // The checks above are what makes a string a CountryCode.\n return code as CountryCode;\n}\n\n/** @returns `'de-DE'` \u2192 `'DE'`, `'pt-BR'` \u2192 `'PT_BR'`, `'en'` \u2192 `'EN'`; `null` otherwise. */\nexport function toLanguage(value: unknown): LanguageCode | null {\n const raw = (typeof value === 'string' ? value : '').trim().replace('-', '_').toUpperCase();\n // The set and the pattern are what makes a string a LanguageCode.\n if (REGIONAL_LANGUAGES.has(raw)) return raw as LanguageCode;\n const primary = raw.split('_')[0] ?? '';\n return LANGUAGE.test(primary) ? (primary as LanguageCode) : null;\n}\n", "/**\n * The runtime configuration. `cfg` is one shared object that every browser module imports; `configure()` replaces its\n * contents in place, so those imports see the new values, and `setCountry()` changes its market keys the same way.\n * Every option is validated here, once, where it arrives: values that end up in query text, URLs, attribute names or\n * storage keys never reach the code that uses them unchecked.\n */\nimport { STOREFRONT_API_VERSION } from '../server/src/shared/api-versions.ts';\nimport { isQuarterlyVersion } from './api-version.ts';\nimport {\n accountOption,\n analyticsOption,\n domainOption,\n embedOriginsOption,\n marketsOption,\n metafieldsOption,\n partsUrlOption,\n} from './config-sections.ts';\n\nexport { MARKET_SOURCES } from './config-sections.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport type { ApiVersion } from '../server/src/shared/api-versions.ts';\nimport type { ShopDomain } from './config-sections.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** Where the visitor's market comes from (`markets.ts`). */\nexport interface MarketsConfig {\n /** True: every source; a list: only those. */\n detect?: boolean | ('saved' | 'server' | 'webflow')[];\n /** Webflow locale (subdirectory or html lang) to market. */\n locales?: Record<string, { country?: string; language?: string; locale?: string }>;\n /** The cookie a server sets with the visitor's country. */\n cookie?: string;\n /** Remember the visitor's choice (default true). */\n remember?: boolean;\n /** With `locales`: ignore an html lang that isn't in the map. */\n strict?: boolean;\n}\n\n/** The optional analytics part. */\nexport interface AnalyticsConfig {\n /** GA4/GTM events, into `window.dataLayer` or the named array. */\n dataLayer?: boolean | string;\n /** Shopify's own analytics. `proxy`: the site serves the Storefront API on its own domain. */\n shopify?: boolean | { consent?: 'no-banner' | 'default-banner' | 'custom-banner'; proxy?: boolean };\n onEvent?: (name: string, data: Record<string, unknown>) => void;\n}\n\n/** The optional customer accounts part, which runs through the site's Worker. */\nexport interface AccountConfig {\n /** The Worker's account routes, a URL or a path on this site. */\n server: string;\n /** The site's own order page, with `{id}` for the numeric order id. */\n orderUrl?: string;\n /** Shopify's subscription management page. */\n subscriptionsUrl?: string;\n /** A separate page for payment methods (default `subscriptionsUrl`). */\n paymentUrl?: string;\n /** Where Shopify sends the visitor after logout (default the home page). */\n logoutUrl?: string;\n /** The language of Shopify's login pages. */\n locale?: string;\n /** Replaces `location.assign` for the login redirect (popups, tests). */\n navigate?: (url: string) => void;\n}\n\nexport interface MetafieldIdentifier {\n namespace: string;\n key: string;\n}\n/** `'namespace.key'` or the identifier itself. */\nexport type MetafieldRef = string | MetafieldIdentifier;\n/** Shopify's note on a cart change. */\nexport interface CartWarning {\n code: string;\n message: string;\n target?: string;\n}\nexport type CheckoutTarget = '_self' | '_blank';\ntype PurchaseKind = 'one-time' | 'subscription';\n\n/** Every option, with its default filled in. */\nexport interface TetherConfig {\n /** The store's domain, without protocol or path; `''` until one is set. */\n domain: ShopDomain | '';\n /** The public Storefront API access token. */\n token: string;\n /** A dated Shopify API version (or `unstable`, with a warning). */\n apiVersion: ApiVersion | 'unstable';\n /** The market's country, for `@inContext(country:)`. */\n country: CountryCode | undefined;\n /** The market's language, for `@inContext(language:)`. */\n language: LanguageCode | undefined;\n /** An `Intl` locale for money and dates. */\n locale: string | undefined;\n markets: MarketsConfig | null;\n /** The attribute prefix: `tether` reads `tether-product`, `tether-bind`. */\n prefix: string;\n /** A folder (`/product`) or a template with `{handle}` / `{id}`. */\n productPath: string;\n collectionPath: string;\n /** The product metafields to fetch. */\n metafields: MetafieldRef[];\n /** Origins (`https://video.example`) a description's iframes may load from, besides the built-in video players. */\n embedOrigins: string[];\n imagesFirst: number;\n variantsFirst: number;\n sellingPlanGroupsFirst: number;\n sellingPlansFirst: number;\n collectionFirst: number;\n /**\n * How many times a failed request is sent again. A query is retried after a network error (no answer, or a body that\n * stopped arriving), a 429, a 5xx or Shopify's `THROTTLED`. A mutation, a cart change included, is retried only\n * after a 429 or `THROTTLED`, which Shopify sends without running it: after a network error or a 5xx it may\n * already have applied the change.\n */\n retries: number;\n /** Refresh server-rendered products once in the background. */\n revalidate: boolean;\n /** The localStorage key of the cart id (default `<prefix>:cart:<domain>`). */\n storageKey: string;\n cartDrawerOpenClass: string;\n openCartOnAdd: boolean;\n /** Move focus into the open cart, keep Tab inside it, restore it on close. */\n cartFocus: boolean;\n checkoutTarget: CheckoutTarget;\n defaultPurchase: PurchaseKind;\n /** `?variant=` follows the visitor's choice on the page's main product. */\n variantUrl: boolean;\n /** Seconds a Shopify answer may be cached; 0 leaves it to Shopify, which can be hours. */\n maxAge: number;\n /** `tether-show=\"low-stock\"` at this many or fewer (needs inventory access). */\n lowStock: number;\n /** Milliseconds the search part waits after the last keystroke. */\n searchDelay: number;\n /** Mount the document on DOMContentLoaded. */\n autoMount: boolean;\n /** Mount elements added later (CMS load more, filters). */\n observe: boolean;\n /** Mount on Astro's `astro:page-load` and close the cart on navigation. */\n astro: boolean;\n /** The folder of the optional parts, for the `<script>` build. */\n partsUrl: string | null;\n analytics: AnalyticsConfig | null;\n account: AccountConfig | null;\n /** The site's text for a cart warning; nothing keeps Shopify's. */\n warningText: ((warning: CartWarning) => string | null | undefined) | null;\n formatMoney: ((money: Money, locale: string | undefined) => string) | null;\n /** Default: `ScrollTrigger.refresh()` when present. */\n onLayoutChange: ((reason: string) => void) | null;\n onCartOpen: ((drawers: HTMLElement[]) => void) | null;\n onCartClose: ((drawers: HTMLElement[]) => void) | null;\n onLineEnter: ((node: HTMLElement) => void) | null;\n /** Animates a line out, then calls `done()`. */\n onLineLeave: ((node: HTMLElement, done: () => void) => void) | null;\n onValueChange: ((element: HTMLElement, previousText: string) => void) | null;\n /** Every failure that reaches the visitor. */\n onError: ((error: unknown, context: string) => void) | null;\n debug: boolean;\n}\n\n/** The options a site writes as plain text, which `resolveConfig` checks into their typed form. */\ntype ParsedFromText = 'domain' | 'apiVersion' | 'country' | 'language' | 'storageKey';\n\n/**\n * What a site passes: any option, the market as the codes people write (`'de'`, `'pt-BR'`), and `null` for the\n * default storage key.\n */\nexport type TetherOptions = {\n [Key in Exclude<keyof TetherConfig, ParsedFromText>]?: TetherConfig[Key];\n} & Partial<Record<Exclude<ParsedFromText, 'storageKey'>, string | undefined>> & {\n storageKey?: string | null | undefined;\n };\n\nexport const DEFAULTS: Readonly<TetherConfig> = {\n domain: '',\n token: '',\n apiVersion: STOREFRONT_API_VERSION,\n country: undefined,\n language: undefined,\n locale: undefined,\n markets: null,\n prefix: 'tether',\n productPath: '/product',\n collectionPath: '/collection',\n metafields: [],\n embedOrigins: [],\n imagesFirst: 25,\n variantsFirst: 100,\n sellingPlanGroupsFirst: 10,\n sellingPlansFirst: 25,\n collectionFirst: 24,\n retries: 2,\n revalidate: true,\n storageKey: '',\n cartDrawerOpenClass: 'is-open',\n openCartOnAdd: true,\n cartFocus: true,\n checkoutTarget: '_self',\n defaultPurchase: 'one-time',\n variantUrl: false,\n maxAge: 60,\n lowStock: 5,\n searchDelay: 150,\n autoMount: true,\n observe: false,\n astro: true,\n partsUrl: null,\n analytics: null,\n account: null,\n warningText: null,\n formatMoney: null,\n onLayoutChange: null,\n onCartOpen: null,\n onCartClose: null,\n onLineEnter: null,\n onLineLeave: null,\n onValueChange: null,\n onError: null,\n debug: false,\n};\n\nexport const cfg: TetherConfig = { ...DEFAULTS };\n\n/**\n * @returns A view of `target` and the objects in it that reads the live values and refuses every change: a write\n * throws in strict-mode code (as on a frozen object) and warns in any code.\n */\nfunction readOnlyView<T extends object>(target: T): T {\n const refuse = (key: string | symbol) => {\n warn(`Tether.config is read-only (${String(key)}): pass options to Tether.init() instead`);\n return false;\n };\n return new Proxy(target, {\n get(object, key) {\n const value: unknown = Reflect.get(object, key);\n // The config's objects are its own copies (`resolveConfig`), never frozen, so a view of each is allowed.\n return typeof value === 'object' && value !== null ? readOnlyView(value) : value;\n },\n set: (_object, key) => refuse(key),\n deleteProperty: (_object, key) => refuse(key),\n defineProperty: (_object, key) => refuse(key),\n });\n}\n\n/**\n * `cfg` as `Tether.config` shows it. A value written there would skip the validation `configure()` does, so the view\n * is read-only; `Tether.init(options)` is how a site changes the config.\n */\nexport const configView = readOnlyView(cfg);\n\n/** The prefix becomes part of attribute names, CSS selectors and event names, so it is kept to those characters. */\nconst PREFIX = /^[a-z][a-z0-9-]*$/;\n\ntype CountOption =\n | 'imagesFirst'\n | 'variantsFirst'\n | 'sellingPlanGroupsFirst'\n | 'sellingPlansFirst'\n | 'collectionFirst'\n | 'retries'\n | 'maxAge'\n | 'lowStock'\n | 'searchDelay';\ntype HookOption =\n | 'warningText'\n | 'formatMoney'\n | 'onLayoutChange'\n | 'onCartOpen'\n | 'onCartClose'\n | 'onLineEnter'\n | 'onLineLeave'\n | 'onValueChange'\n | 'onError';\ntype SwitchOption =\n 'revalidate' | 'openCartOnAdd' | 'cartFocus' | 'variantUrl' | 'autoMount' | 'observe' | 'astro' | 'debug';\ntype TextOption = 'token' | 'productPath' | 'collectionPath' | 'cartDrawerOpenClass';\n\nconst SWITCH_OPTIONS: readonly SwitchOption[] = [\n 'revalidate',\n 'openCartOnAdd',\n 'cartFocus',\n 'variantUrl',\n 'autoMount',\n 'observe',\n 'astro',\n 'debug',\n];\nconst TEXT_OPTIONS: readonly TextOption[] = ['token', 'productPath', 'collectionPath', 'cartDrawerOpenClass'];\n\n/** Options that are written into queries as list sizes or used as counts. */\nconst COUNT_OPTIONS: readonly CountOption[] = [\n 'imagesFirst',\n 'variantsFirst',\n 'sellingPlanGroupsFirst',\n 'sellingPlansFirst',\n 'collectionFirst',\n 'retries',\n 'maxAge',\n 'lowStock',\n 'searchDelay',\n];\nconst HOOK_OPTIONS: readonly HookOption[] = [\n 'warningText',\n 'formatMoney',\n 'onLayoutChange',\n 'onCartOpen',\n 'onCartClose',\n 'onLineEnter',\n 'onLineLeave',\n 'onValueChange',\n 'onError',\n];\n\n/** @returns `fallback`, with a warning unless `value` was left out. */\nfunction invalidOption<T>(name: string, value: unknown, fallback: T): T {\n if (value !== undefined) warn(`invalid ${name} ${JSON.stringify(value)}: using ${JSON.stringify(fallback)}`);\n return fallback;\n}\n\n/** Warns about an option without a default to fall back to, which is left out (`config-sections.ts` too). */\nfunction dropped(name: string, value: unknown): void {\n warn(`invalid ${name} ${JSON.stringify(value)}: ignored`);\n}\n\n/**\n * Warns about each key that is no option, so a misspelt one (`openCartOnAd`) is seen; none of them reaches `cfg`.\n */\nfunction warnUnknownKeys(options: Readonly<Record<string, unknown>>): void {\n for (const key of Object.keys(options)) {\n if (!(key in DEFAULTS)) warn(`unknown option ${JSON.stringify(key)}: ignored`);\n }\n}\n\n/** Sets the site's function for option `name`; a value that isn't one is dropped with a warning, unless it is null. */\nfunction setHook<Name extends HookOption>(config: Pick<TetherConfig, Name>, name: Name, value: unknown): void {\n if (typeof value === 'function') {\n // A function from the site can only be checked for being callable; how it is called is the documented signature\n // of its option.\n config[name] = value as TetherConfig[Name];\n } else if (value !== undefined && value !== null) {\n dropped(name, value);\n }\n}\n\n/** @returns `value` when it is a non-negative integer, else the default. */\nfunction countOption(name: CountOption, value: unknown): number {\n if (typeof value === 'number' && Number.isInteger(value) && value >= 0) return value;\n return invalidOption(name, value, DEFAULTS[name]);\n}\n\nfunction switchOption(name: SwitchOption, value: unknown): boolean {\n return typeof value === 'boolean' ? value : invalidOption(name, value, DEFAULTS[name]);\n}\n\nfunction textOption(name: TextOption, value: unknown): string {\n return typeof value === 'string' ? value : invalidOption(name, value, DEFAULTS[name]);\n}\n\n/** @returns An `Intl` locale as written, `undefined` for one `Intl` doesn't accept. */\nfunction localeOption(value: unknown): string | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n try {\n if (typeof value === 'string' && Intl.getCanonicalLocales(value).length) return value;\n } catch {\n // RangeError: not a locale (falls through to the warning).\n }\n dropped('locale', value);\n return undefined;\n}\n\n/** @param choices The first one is the default. */\nfunction choiceOption<Choice extends string>(\n name: string,\n value: unknown,\n choices: readonly [Choice, ...Choice[]],\n): Choice {\n return choices.find((choice) => choice === value) ?? invalidOption(name, value, choices[0]);\n}\n\n/**\n * @returns A quarterly version, or `unstable` with a warning; the default for anything else, which would be written\n * into the endpoint's path.\n */\nfunction apiVersionOption(value: unknown): ApiVersion | 'unstable' {\n if (value === 'unstable') {\n warn('apiVersion \"unstable\" changes without notice. Pin a dated version (e.g. 2026-07).');\n return value;\n }\n return isQuarterlyVersion(value) ? value : invalidOption('apiVersion', value, DEFAULTS.apiVersion);\n}\n\nfunction codeOption<Code>(name: string, value: unknown, parse: (value: unknown) => Code | null): Code | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n const code = parse(value);\n if (code !== null) return code;\n dropped(name, value);\n return undefined;\n}\n\n/**\n * The configuration `options` describe: defaults filled in, every value validated (an invalid one warns and falls\n * back to its default), hooks that aren't functions dropped so callers only check for null.\n *\n * @param options What the site set, or any object (the server helpers take their options from code and `.env`).\n */\nexport function resolveConfig(options: TetherOptions | Readonly<Record<string, unknown>>): TetherConfig {\n warnUnknownKeys(options);\n const domain = domainOption(options.domain, dropped);\n const prefix =\n typeof options.prefix === 'string' && PREFIX.test(options.prefix)\n ? options.prefix\n : invalidOption('prefix', options.prefix, DEFAULTS.prefix);\n const resolved: TetherConfig = {\n ...DEFAULTS,\n domain,\n apiVersion: apiVersionOption(options.apiVersion),\n country: codeOption('country', options.country, toCountry),\n locale: localeOption(options.locale),\n partsUrl: partsUrlOption(options.partsUrl, dropped),\n language: codeOption('language', options.language, toLanguage),\n markets: marketsOption(options.markets, dropped),\n analytics: analyticsOption(options.analytics, dropped),\n account: accountOption(options.account, dropped, warn),\n prefix,\n storageKey:\n typeof options.storageKey === 'string' && options.storageKey ? options.storageKey : `${prefix}:cart:${domain}`,\n metafields: metafieldsOption(options.metafields ?? [], dropped),\n embedOrigins: embedOriginsOption(options.embedOrigins ?? [], dropped),\n checkoutTarget: choiceOption('checkoutTarget', options.checkoutTarget, ['_self', '_blank']),\n defaultPurchase: choiceOption('defaultPurchase', options.defaultPurchase, ['one-time', 'subscription']),\n };\n for (const name of COUNT_OPTIONS) resolved[name] = countOption(name, options[name]);\n for (const name of SWITCH_OPTIONS) resolved[name] = switchOption(name, options[name]);\n for (const name of TEXT_OPTIONS) resolved[name] = textOption(name, options[name]);\n for (const name of HOOK_OPTIONS) setHook(resolved, name, options[name]);\n return resolved;\n}\n\n/**\n * @param value A `markets` option from code other than the config (`Tether.detectMarket(markets)`).\n * @returns It validated as the config's own is, with the same warnings.\n */\nexport const parseMarkets = (value: unknown): MarketsConfig | null => marketsOption(value, dropped);\n\n/**\n * Replaces the shared configuration with `options` over the defaults (see `resolveConfig`).\n *\n * @returns The shared `cfg`.\n */\nexport function configure(options: TetherOptions | Readonly<Record<string, unknown>> = {}): TetherConfig {\n const resolved = resolveConfig(options);\n for (const key of Object.keys(cfg)) {\n if (!(key in resolved)) Reflect.deleteProperty(cfg, key);\n }\n return Object.assign(cfg, resolved);\n}\n\n/** @returns True once a domain and a token are set. */\nexport const isConfigured = (): boolean => Boolean(cfg.domain && cfg.token);\n\nexport function log(...args: unknown[]): void {\n if (cfg.debug) console.log(`[${cfg.prefix}]`, ...args);\n}\n\nexport function warn(...args: unknown[]): void {\n console.warn(`[${cfg.prefix}]`, ...args);\n}\n\n/**\n * Logs a failure that reaches the visitor and hands it to `onError`. A throwing `onError` is logged, never rethrown:\n * the site's hook must not break the render that reported the failure.\n *\n * @param context What failed, e.g. `product coffee-box`.\n */\nexport function reportError(error: unknown, context: string): void {\n warn(context, error instanceof Error && error.message ? error.message : error);\n if (!cfg.onError) return;\n try {\n cfg.onError(error, context);\n } catch (hookError) {\n warn('onError threw', hookError);\n }\n}\n", "/** The text of anything thrown: an `Error`'s message, or the value itself written as text. */\nexport function messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n", "/**\n * The one Storefront API client: a fetch wrapper that adds the market (`@inContext`), retries what is safe to send\n * again (`Operation`), and turns every failure, including an answer whose `data` lacks what its caller reads\n * (`DataCheck`), into a `TetherError`. The browser (`gql`, and `query` for `Tether.query()`) and the server helpers\n * (`request` with their own config) share it.\n */\nimport { cfg, log } from './config.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { field, isRecord } from '../server/src/shared/json.ts';\nimport { messageOf } from '../server/src/shared/error-message.ts';\nimport type { TetherConfig } from './config.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\nexport type TetherErrorCode =\n | 'NOT_FOUND'\n | 'NETWORK'\n | 'NOT_CONFIGURED'\n | 'NOT_LOGGED_IN'\n | 'INVALID'\n | 'INVALID_COUNTRY'\n | 'INVALID_DISCOUNT'\n | 'INVALID_RESPONSE'\n | 'SERVER_ERROR'\n | 'ERROR'\n | `HTTP_${number}`;\nexport interface GraphQLError {\n message: string;\n extensions?: { code?: string };\n}\n/** The response shape the GraphQL spec sets. */\ninterface GraphQLResponse {\n data?: unknown;\n errors?: GraphQLError[];\n}\nexport interface UserError {\n message: string;\n field?: string[] | null;\n code?: string | null;\n}\ninterface TetherErrorDetails {\n /** Default `HTTP_<status>` with a status, else `ERROR`. */\n code?: TetherErrorCode | undefined;\n /** The HTTP status of the response that failed. */\n status?: number;\n /** The GraphQL errors of that response. */\n errors?: readonly GraphQLError[];\n /** A mutation's user errors. */\n userErrors?: readonly UserError[];\n}\n\nexport class TetherError extends Error {\n override readonly name = 'TetherError' as const;\n readonly code: TetherErrorCode;\n readonly status: number | undefined;\n readonly errors: readonly GraphQLError[] | undefined;\n readonly userErrors: readonly UserError[] | undefined;\n\n constructor(message: string, { status, errors, userErrors, code }: TetherErrorDetails = {}) {\n super(message);\n this.code = code ?? (status ? `HTTP_${status}` : 'ERROR');\n this.status = status;\n this.errors = errors;\n this.userErrors = userErrors;\n }\n}\n\n/** What a request needs from a config: the browser's `cfg`, or a config the server helpers build. */\nexport type StorefrontConfig = Pick<\n TetherConfig,\n 'domain' | 'token' | 'apiVersion' | 'country' | 'language' | 'maxAge' | 'retries'\n> & { fetch?: typeof fetch };\n/** Checks a query's `data` before its caller reads it as a `T`. */\nexport type DataCheck<T> = (data: unknown) => data is T;\n/**\n * The structure of JSON a check accepts: a `typeof` name, `'object'` for a plain object, `[item]` for a list of\n * items, an object's fields, or a check of its own. Fields it doesn't name aren't checked, so a shape names only what\n * its reader walks without a fallback. A field's `undefined` is the type of one `ShapeOf` leaves out, and checks\n * nothing.\n */\nexport type Shape =\n | 'string'\n | 'number'\n | 'boolean'\n | 'object'\n | ((value: unknown) => boolean)\n | readonly [Shape]\n | { readonly [key: string]: Shape | undefined };\n\n/** @returns True when `value` has the structure `shape` describes. */\nexport function matches(value: unknown, shape: Shape): boolean {\n if (typeof shape === 'function') return shape(value);\n if (typeof shape === 'string') return shape === 'object' ? isRecord(value) : typeof value === shape;\n if (isShapeList(shape)) return Array.isArray(value) && value.every((item) => matches(item, shape[0]));\n return (\n isRecord(value) && Object.entries(shape).every(([key, inner]) => inner === undefined || matches(value[key], inner))\n );\n}\n\n/** `Array.isArray` doesn't narrow a readonly tuple out of a union. */\nconst isShapeList = (shape: Shape): shape is readonly [Shape] => Array.isArray(shape);\n\n/**\n * The shapes that describe a `T`: every structure such a shape names is one `T` declares at that place, and a check\n * in it is a type guard for a type `T` accepts there. A shape for `T` therefore can't name a field `T` lacks or claim\n * a type `T` contradicts, so a change to `T` that its shape no longer agrees with fails to compile. Fields of `T` the\n * shape doesn't name stay unchecked (`Shape`).\n */\nexport type ShapeOf<T> = unknown extends T ? Shape : KnownShapeOf<T>;\ntype KnownShapeOf<T> =\n | DataCheck<T>\n | (string extends T ? 'string' : never)\n | (number extends T ? 'number' : never)\n | (boolean extends T ? 'boolean' : never)\n | ListShapeOf<T>\n | FieldsShapeOf<T>;\ntype ListShapeOf<T> = T extends readonly (infer Item)[] ? readonly [ShapeOf<Item>] : never;\ntype FieldsShapeOf<T> = T extends readonly unknown[]\n ? never\n : T extends object\n ? 'object' | { readonly [Key in keyof T]?: ShapeOf<T[Key]> }\n : never;\n\n/** @returns A check that `value` is `null` or has the structure `shape` describes. */\nexport const orNull =\n <T>(shape: ShapeOf<T>): DataCheck<T | null> =>\n (value): value is T | null =>\n value === null || matches(value, shape);\n\n/**\n * @returns A check that `data` has the structure `shape` describes. `ShapeOf` ties `shape` to `T`; fields `shape`\n * doesn't name are `T`'s claim.\n */\nexport const shaped =\n <T>(shape: ShapeOf<T>): DataCheck<T> =>\n (data): data is T =>\n matches(data, shape);\n/**\n * What an operation does to the store, which decides what is safe to retry. A query only reads, so a failure that may\n * pass is retried: the network (no answer, or a body that stopped arriving), a 429, a 5xx, `THROTTLED` and\n * `INTERNAL_SERVER_ERROR`. Shopify may have applied a mutation before a server error or a dropped connection, so a\n * mutation is retried only when Shopify answers that it didn't run it (429 or `THROTTLED`): sending it again could add\n * a line twice. Other failures (a 4xx, a 430, an answer that isn't valid, other GraphQL errors) are never retried.\n */\nexport type Operation = 'query' | 'mutation';\n\ninterface RequestOptions {\n /** `false` sends the query without `@inContext`. */\n context?: boolean;\n /** Default `'query'`. */\n operation?: Operation;\n}\n/** What the store's token may read beyond the defaults. */\nexport interface StoreAccess {\n inventory: boolean;\n}\n/**\n * A query's text, or a function that writes it for what the token may read: Tether's own queries, built from\n * `fragments.ts`, leave out inventory for a token that can't read it. Documents Tether didn't write are strings.\n */\nexport type QueryText = string | ((access: StoreAccess) => string);\n\n/** @returns The Storefront GraphQL endpoint of `conf`'s store. */\nexport function endpoint(conf: Pick<TetherConfig, 'domain' | 'apiVersion'> = cfg): string {\n return `https://${conf.domain}/api/${conf.apiVersion}/graphql.json`;\n}\n\n/** GraphQL strings and `#` comments: text in them is never part of an operation's header. */\nconst STRINGS_AND_COMMENTS = /\"(?:\\\\.|[^\"\\\\\\n])*\"|#[^\\n]*/g;\n/** An operation's keyword, name and variable definitions: named (query Name($x: T)) and anonymous (query { \u2026 }). */\nconst OPERATION_START = /^\\s*(?:query|mutation)(?:\\s+\\w+)?\\s*(?:\\([^)]*\\))?/;\n\n/**\n * The operation of a document Tether didn't write (`Tether.query()`, the server's `query()`). Outside strings and\n * comments, any `mutation` keyword makes it a mutation: a document that starts with a fragment still has its\n * operation found, and a misread errs on the side that is never sent twice.\n */\nexport function operationOf(document: string): Operation {\n return /\\bmutation\\b/.test(document.replace(STRINGS_AND_COMMENTS, '')) ? 'mutation' : 'query';\n}\n\n/**\n * Adds @inContext(country, language) to the first operation in the document, so prices, currency and translations\n * follow the market. The header is found in a copy of the document whose strings and comments are blanked out, so a\n * `)` in a default value or an `@inContext` in a comment or the selection set doesn't move or stop it; an operation\n * that names its own `@inContext` keeps it. Tether's own documents start with their operation, and so do the ones\n * `Tether.query()` is given in practice: a document that starts with a fragment is sent as written.\n */\nfunction inContext(\n query: string,\n { country, language }: { country: CountryCode | undefined; language: LanguageCode | undefined },\n): string {\n const args: string[] = [];\n if (country) args.push(`country: ${country}`);\n if (language) args.push(`language: ${language}`);\n if (!args.length) return query;\n const tag = `@inContext(${args.join(', ')})`;\n const blanked = query.replace(STRINGS_AND_COMMENTS, (text) => ' '.repeat(text.length));\n const selectionStart = blanked.indexOf('{');\n if (blanked.slice(0, selectionStart).includes('@inContext')) return query;\n // Shorthand: a document that starts with its selection set is a query without a keyword.\n if (blanked.trimStart().startsWith('{')) return `query ${tag} ${query.slice(selectionStart)}`;\n const header = OPERATION_START.exec(blanked);\n if (!header) return query;\n const end = header[0].length;\n return `${query.slice(0, end).trimEnd()} ${tag} ${query.slice(end).trimStart()}`;\n}\n\n/**\n * `inContext` for any country and language, as the public `Tether.withContext` takes them: one that isn't a valid\n * code is left out, so page input never becomes query text.\n */\nexport function withContext(\n query: string,\n ctx: { country?: string | undefined; language?: string | undefined } = cfg,\n): string {\n const country = toCountry(ctx.country) ?? undefined;\n const language = toLanguage(ctx.language) ?? undefined;\n return inContext(query, { country, language });\n}\n\n/** Backoff after a network error, a 429 or a 5xx: this times 2^attempt. */\nconst RETRY_BACKOFF_MS = 300;\n/** Backoff after Shopify's `THROTTLED` error, which asks for a longer wait: this times 2^attempt. */\nconst THROTTLE_BACKOFF_MS = 500;\n/** Shopify's status for a request its bot protection rejected; retrying doesn't help. */\nconst SECURITY_REJECTION = 430;\nconst MS_PER_SECOND = 1000;\n\n/**\n * `quantityAvailable` needs the token's `unauthenticated_read_product_inventory` scope; without it Shopify refuses the\n * whole query with an error naming the field. That refusal is learned once per page and store: Tether's later queries\n * to the store are written without the field (`StoreAccess`), so stock levels are unknown and everything else works,\n * for one extra request.\n */\nconst INVENTORY_FIELD = 'quantityAvailable';\n\n/** The stores (domain, version and token) whose token can't read inventory. */\nconst storesWithoutInventory = new Set<string>();\n\nconst storeOf = (conf: StorefrontConfig) => `${conf.domain} ${conf.apiVersion} ${conf.token}`;\n\n/** @returns The text sent: with the market, and the cache's time bucket for a query. */\nfunction queryText(\n conf: StorefrontConfig,\n query: QueryText,\n access: StoreAccess,\n { context, operation }: Required<RequestOptions>,\n): string {\n const written = typeof query === 'string' ? query : query(access);\n const text = context ? inContext(written, conf) : written;\n if (conf.maxAge <= 0 || operation === 'mutation') return text;\n return `${text} #${Math.floor(Date.now() / (conf.maxAge * MS_PER_SECOND))}`;\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** What a status means, for errors Shopify sends without a message (401/403 have none). */\nfunction statusText(status: number, conf: StorefrontConfig): string {\n if (status === 401 || status === 403) {\n const adminHint = conf.token.startsWith('shpat_') ? ', not an Admin API token (shpat_\u2026)' : '';\n return `Shopify rejected the Storefront token for ${conf.domain} (${status}): use the public Storefront API token (Headless channel)${adminHint}`;\n }\n if (status === 404) {\n return `Store or API version not found (404): check domain ${conf.domain} and apiVersion ${conf.apiVersion}`;\n }\n return `HTTP ${status} from ${conf.domain}`;\n}\n\n/**\n * @param value One entry of a response's `errors`.\n * @returns The error, with an empty message when it has none.\n */\nfunction toGraphQLError(value: unknown): GraphQLError {\n const message = field(value, 'message');\n const code = field(field(value, 'extensions'), 'code');\n const text = typeof message === 'string' ? message : '';\n return typeof code === 'string' ? { message: text, extensions: { code } } : { message: text };\n}\n\n/**\n * @param json A response body.\n * @returns The body in the shape the GraphQL spec sets, `null` when it has another one.\n */\nfunction toGraphQLResponse(json: unknown): GraphQLResponse | null {\n if (!isRecord(json)) return null;\n const { data, errors } = json;\n if (errors === undefined || errors === null) return { data };\n if (!Array.isArray(errors)) return null;\n return { data, errors: errors.map(toGraphQLError) };\n}\n\n/**\n * For a body that isn't JSON, isn't in the GraphQL shape or has `data` its caller's check refuses: `INVALID_RESPONSE`\n * for a 2xx, after which Shopify may have run a mutation, else `HTTP_<status>`.\n */\nfunction invalidResponse(res: Response, conf: StorefrontConfig): TetherError {\n return res.ok\n ? new TetherError(`Invalid response (${res.status})`, { code: 'INVALID_RESPONSE', status: res.status })\n : new TetherError(statusText(res.status, conf), { status: res.status });\n}\n\nfunction graphQLFailure(\n res: Response,\n errors: GraphQLError[],\n conf: StorefrontConfig,\n code?: TetherErrorCode,\n): TetherError {\n const message = errors\n .map((error) => error.message)\n .filter(Boolean)\n .join(' | ');\n let text = message || 'Storefront API error';\n if (!res.ok) text = res.status === 401 || res.status === 403 || !message ? statusText(res.status, conf) : message;\n return new TetherError(text, { status: res.status, errors, code });\n}\n\ntype Attempt<T> =\n { kind: 'data'; data: T } | { kind: 'retry'; backoffMs: number } | { kind: 'inventory-refused'; error: TetherError };\n\n/**\n * Which failures an attempt may answer with a retry instead of throwing: none on the last attempt, only Shopify's\n * \"not run\" answers for a mutation (`Operation`).\n */\ntype RetryPolicy = 'none' | 'not-run' | 'any';\n\n/** No answer arrived, or its body stopped arriving: retried when `retry` allows any retry, else `NETWORK`. */\nfunction networkFailure(error: unknown, retry: RetryPolicy): Attempt<never> {\n if (retry === 'any') return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n throw new TetherError(`Network error: ${messageOf(error)}`, { code: 'NETWORK' });\n}\n\n/**\n * What an answer with GraphQL errors means: an inventory refusal, a retry `retry` allows, or else the failure it\n * throws. Shopify answers a failure on its own side with a 200 and a top-level `INTERNAL_SERVER_ERROR`, which means\n * what a 5xx does; a 2xx mutation answer with errors and no `data` doesn't say whether Shopify applied it either.\n * Both are `SERVER_ERROR`.\n */\nfunction graphQLOutcome(\n res: Response,\n conf: StorefrontConfig,\n query: string,\n errors: GraphQLError[],\n data: unknown,\n retry: RetryPolicy,\n operation: Operation,\n): Attempt<never> {\n if (query.includes(INVENTORY_FIELD) && errors.some((error) => error.message.includes(INVENTORY_FIELD))) {\n return { kind: 'inventory-refused', error: graphQLFailure(res, errors, conf) };\n }\n const codes = errors.map((error) => error.extensions?.code);\n if (retry !== 'none' && codes.includes('THROTTLED')) return { kind: 'retry', backoffMs: THROTTLE_BACKOFF_MS };\n const serverError = codes.includes('INTERNAL_SERVER_ERROR');\n if (retry === 'any' && serverError) return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n const unclear = res.ok && (serverError || (operation === 'mutation' && data == null));\n throw graphQLFailure(res, errors, conf, unclear ? 'SERVER_ERROR' : undefined);\n}\n\n/** One request. Failures that retrying can't fix, or that `retry` doesn't allow retrying, throw. */\nasync function attempt<T>(\n conf: StorefrontConfig,\n query: string,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n retry: RetryPolicy,\n operation: Operation,\n): Promise<Attempt<T>> {\n let res;\n try {\n res = await (conf.fetch ?? fetch)(endpoint(conf), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Shopify-Storefront-Access-Token': conf.token },\n body: JSON.stringify({ query, variables }),\n });\n } catch (error) {\n return networkFailure(error, retry);\n }\n if (res.status === SECURITY_REJECTION) {\n throw new TetherError('Shopify security rejection (430)', { status: SECURITY_REJECTION });\n }\n if ((retry !== 'none' && res.status === 429) || (retry === 'any' && res.status >= 500)) {\n return { kind: 'retry', backoffMs: RETRY_BACKOFF_MS };\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n // `json()` refuses a body that isn't JSON with a SyntaxError; it rejects with another error when the connection\n // drops while the body streams in.\n if (error instanceof SyntaxError) throw invalidResponse(res, conf);\n return networkFailure(error, retry);\n }\n const body = toGraphQLResponse(json);\n if (!body) throw invalidResponse(res, conf);\n const { data, errors = [] } = body;\n if (errors.length) return graphQLOutcome(res, conf, query, errors, data, retry, operation);\n if (!res.ok) throw new TetherError(statusText(res.status, conf), { status: res.status });\n if (!isData(data)) throw invalidResponse(res, conf);\n return { kind: 'data', data };\n}\n\n/**\n * One Storefront API request, for any config: the browser's shared one (`gql`) or one the server helpers build.\n * Queries carry a time bucket so Shopify's cache of an identical query text is at most `maxAge` seconds old. A query\n * written for the store's access leaves out inventory once the token turns out not to read it (`INVENTORY_FIELD`);\n * a fixed query text is the caller's own, and that refusal is its error, as is a refusal of a text written without\n * inventory, so the refusal costs at most one extra request. How often a failure is retried is\n * `conf.retries`; which failures are retried is the operation's (`Operation`).\n *\n * @param isData Checks that `data` has what the caller then reads, so a malformed answer fails here.\n * @throws {TetherError} `NOT_CONFIGURED` without a domain or token, `NETWORK` when the store can't be reached or\n * the answer's body stops arriving, `INVALID_RESPONSE` for a 2xx whose body isn't valid (not JSON, not in the\n * GraphQL shape, or `data` that `isData` refuses), `SERVER_ERROR` for a 2xx whose GraphQL errors leave open\n * whether Shopify ran it (a top-level `INTERNAL_SERVER_ERROR`, or a mutation's errors without `data`),\n * `HTTP_<status>` for any other HTTP or GraphQL failure.\n */\nexport async function request<T>(\n conf: StorefrontConfig,\n query: QueryText,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n { context = true, operation = 'query' }: RequestOptions = {},\n): Promise<T> {\n if (!conf.domain || !conf.token) {\n throw new TetherError('Tether is not configured: domain and token are required', { code: 'NOT_CONFIGURED' });\n }\n const store = storeOf(conf);\n const retryWhileLeft: RetryPolicy = operation === 'mutation' ? 'not-run' : 'any';\n let retries = 0;\n for (;;) {\n const access = { inventory: !storesWithoutInventory.has(store) };\n const sent = queryText(conf, query, access, { context, operation });\n const retry = retries < conf.retries ? retryWhileLeft : 'none';\n const outcome = await attempt(conf, sent, variables, isData, retry, operation);\n switch (outcome.kind) {\n case 'data':\n return outcome.data;\n case 'inventory-refused':\n if (typeof query === 'string' || !access.inventory) throw outcome.error;\n log(`the token can't read inventory: ${INVENTORY_FIELD} is left out from now on`);\n storesWithoutInventory.add(store);\n break;\n case 'retry':\n retries += 1;\n log('retry', retries);\n await sleep(outcome.backoffMs * 2 ** retries);\n break;\n }\n }\n}\n\n/** `request` with the browser's shared config. */\nexport function gql<T>(\n query: QueryText,\n variables: Record<string, unknown>,\n isData: DataCheck<T>,\n options?: RequestOptions,\n): Promise<T> {\n return request(cfg, query, variables, isData, options);\n}\n\n/** The check of `Tether.query()` and the server's `query()`, whose callers read `data` themselves. */\nexport const hasData = (data: unknown): data is object => typeof data === 'object' && data !== null;\n\n/**\n * `Tether.query()` and the server's `query()`: a document Tether didn't write, sent with `conf`.\n *\n * @returns Shopify's `data`, checked only for being an object: the caller's own query decides its shape, so the caller\n * checks the fields it reads.\n */\nexport function untypedQuery(\n conf: StorefrontConfig,\n text: string,\n variables: Record<string, unknown> = {},\n options?: Omit<RequestOptions, 'operation'>,\n): Promise<object> {\n return request(conf, text, variables, hasData, { ...options, operation: operationOf(text) });\n}\n\n/** `Tether.query()`: `untypedQuery` with the browser's shared config. */\nexport function query(\n text: string,\n variables?: Record<string, unknown>,\n options?: Omit<RequestOptions, 'operation'>,\n): Promise<object> {\n return untypedQuery(cfg, text, variables, options);\n}\n\n/**\n * @param payload A mutation's payload.\n * @param name The mutation, for the error message.\n * @throws {TetherError} `ERROR` on an empty payload or on user errors (in `userErrors`).\n */\nexport function unwrap<Payload extends { userErrors?: readonly UserError[] | null }>(\n payload: Payload | null | undefined,\n name: string,\n): Payload {\n if (!payload) throw new TetherError(`${name}: empty response`);\n const userErrors = payload.userErrors ?? [];\n if (userErrors.length) {\n throw new TetherError(userErrors.map((error) => error.message).join(' | '), { userErrors });\n }\n return payload;\n}\n", "/**\n * How Tether finds its markup: attribute names carry the configured prefix (`tether-product`, or `wfs-product` with\n * `prefix: 'wfs'`), and so do the selectors and the DOM events built from them.\n */\nimport { cfg } from './config.ts';\nimport type { CartUpdateReason, ShopifyCart } from './cart/store.ts';\nimport type { CartLineInput } from './cart/mutations.ts';\nimport type { CartWarning } from './config.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { Collection } from './loader.ts';\nimport type { ProductScope } from './product.ts';\nimport type { Purchase } from './product-state.ts';\nimport type { SearchResults } from './search.ts';\n\n/** @returns The prefixed attribute name: `product` \u2192 `tether-product`. */\nexport const attr = (name: string): string => `${cfg.prefix}-${name}`;\n\n/** @returns A selector for the attribute, with that exact value when given. */\nexport const sel = (name: string, value?: string): string =>\n value === undefined ? `[${attr(name)}]` : `[${attr(name)}=\"${value}\"]`;\n\nexport const get = (el: Element, name: string): string | null => el.getAttribute(attr(name));\n\nexport const has = (el: Element, name: string): boolean => el.hasAttribute(attr(name));\n\n/**\n * Tether's attributes go on HTML elements, so matches are typed as such. An SVG element that carries one still has\n * the `style`, `dataset` and attribute methods Tether uses.\n *\n * @returns The matches in `root`, none without a root.\n */\nexport function qsa(root: ParentNode | null | undefined, selector: string): HTMLElement[] {\n if (!root) return [];\n return [...root.querySelectorAll<HTMLElement>(selector)];\n}\n\n/** @returns Matches in `root` whose nearest `scopeSelector` ancestor is `root` itself, not a nested scope. */\nexport const scoped = (root: Element | null | undefined, selector: string, scopeSelector: string): HTMLElement[] =>\n qsa(root, selector).filter((el) => el.closest(scopeSelector) === root);\n\n/**\n * The events Tether dispatches, as `<prefix>:<name>` (`tether:cart-updated`), bubbling up to `document`, with the\n * detail each carries. `emit` takes only these, and `Tether.on()` passes on only the events `emit` dispatched, so a\n * listener gets the detail its event really has.\n */\nexport interface TetherEvents {\n ready: { cart: ShopifyCart | null };\n 'product-ready': { product: Product; scope: ProductScope };\n 'product-updated': { product: Product; scope: ProductScope };\n /** `key` is null when the scope names no product; `source` is what it named. */\n 'product-not-found': { key: string | null; source: string };\n 'variant-changed': {\n product: Product;\n variant: ProductVariant;\n selected: Record<string, string>;\n purchase: Purchase['kind'];\n sellingPlanId: string | null;\n };\n 'product-added': { product: Product; variant: ProductVariant; line: CartLineInput };\n added: { cart: ShopifyCart; lines: CartLineInput[] };\n 'group-added': { group: string | null; lines: CartLineInput[] };\n 'cart-warning': { warnings: readonly CartWarning[] };\n 'cart-updated': { cart: ShopifyCart | null; reason: CartUpdateReason };\n 'cart-toggled': { open: boolean };\n 'cart-error': { error: unknown };\n checkout: { url: string; cart: ShopifyCart | null };\n 'list-ready': { products: Product[]; collection: Collection | null };\n /** A list's cards were drawn (again): `container` holds them. */\n 'list-rendered': { container: Element };\n /** The search part's suggestions for `term`. */\n search: SearchResults & { term: string };\n 'country-changed': { country: string; language: string | undefined };\n 'discount-applied': { code: string };\n 'discount-invalid': { code: string };\n 'layout-changed': { reason: string };\n error: { error: unknown; context: string; key?: string | null };\n 'account-login': Record<string, never>;\n 'account-logout': Record<string, never>;\n /** A login that failed: `expired` (it took too long, or came back to another browser) or `error`. */\n 'account-error': { error: 'expired' | 'error' };\n 'account-updated': { what: 'profile' | 'addresses' | 'subscriptions' | 'orders' };\n}\n\n/** The events the analytics part reports, which the core buffers from `init()` until the part has loaded. */\nexport const ANALYTICS_EVENTS = [\n 'product-ready',\n 'list-ready',\n 'cart-updated',\n 'cart-toggled',\n 'checkout',\n 'search',\n] as const satisfies readonly (keyof TetherEvents)[];\n\nexport type AnalyticsEventName = (typeof ANALYTICS_EVENTS)[number];\n\n/**\n * The events Tether created, with the name each was created under. Any script can dispatch a `<prefix>:<name>` event\n * with any detail; only an event in here carries the detail `TetherEvents` gives its name.\n */\nconst ownEvents = new WeakMap<Event, keyof TetherEvents>();\n\n/** @returns The bubbling event `<prefix>:<name>`, known to `isOwnEvent`. */\nexport function ownEvent<Name extends keyof TetherEvents>(\n name: Name,\n detail: TetherEvents[Name],\n): CustomEvent<TetherEvents[Name]> {\n const event = new CustomEvent(`${cfg.prefix}:${name}`, { detail, bubbles: true });\n ownEvents.set(event, name);\n return event;\n}\n\n/**\n * @returns True for an event `ownEvent` created as `name`, false for any other, such as one another script dispatched.\n */\nexport function isOwnEvent<Name extends keyof TetherEvents>(\n event: Event,\n name: Name,\n): event is CustomEvent<TetherEvents[Name]> {\n return ownEvents.get(event) === name;\n}\n\n/** Dispatches the bubbling event `<prefix>:<name>`. */\nexport function emit<Name extends keyof TetherEvents>(\n name: Name,\n detail: TetherEvents[Name],\n target: EventTarget = document,\n): void {\n target.dispatchEvent(ownEvent(name, detail));\n}\n", "/**\n * HTML written in the Shopify admin (product and collection descriptions), made safe to insert into the site. Store\n * staff and apps that can edit products are not trusted to act on the storefront, where customer sessions live. Only\n * elements and attributes that show content are kept (an allow-list, so markup nobody reviewed is never let in):\n * text formatting, lists, tables, images, links and video embeds look as written, inline `style` included, because\n * the Shopify editor writes its formatting there. Script, page-wide `<style>` and forms (which could post the\n * visitor's input anywhere) are removed, and embeds run sandboxed.\n *\n * A description stays where it is written, so it can't be laid over the page as a fake login (a phishing form on the\n * shop's own origin). Removed for that: `id` (it would clobber `window.<id>` and the site's own ids); `class`, which\n * would let a description borrow the site's own overlay or modal classes; and every style that takes content out of\n * the flow (`position`, `inset`, `z-index`), moves it from where the flow put it (transforms, motion paths, negative\n * margins, and negative indents and spacing beyond the small values that tighten text, `NEGATIVE_LIMITS`), copies it\n * elsewhere (reflections) or sizes it to the window (viewport and container-query units).\n * Outlines, shadows, filters, border-image and mask-border outsets, text decoration and text stroke paint beyond their\n * element, so they are kept only while every number they give is a pixel length of a few dozen pixels at most\n * (`MAX_PAINT_PX`): enough to set off an image or a callout, too little to cover the page.\n * Styles that fetch anything go too.\n *\n * A frame shows whatever its origin serves, sized as the description likes, so a frame from a page store staff can\n * write would be that fake login. Frames are kept only from the video players in `EMBED_ORIGINS` and the origins the\n * site adds (`embedOrigins`), never from the site's own origin, where a sandbox can be lifted from inside. What an\n * allowed frame shows is up to its host: the host's player, and whatever that player lets its users add (a call to\n * action, an email gate). An origin a site adds is trusted the same way.\n *\n * Not prevented: content wider than the description (a table or embed on a narrow screen, or a box given a large\n * width in pixels) overflows beside it, as in any HTML, and a capped shadow or outline still paints just past it.\n * Tether doesn't clip the element it fills (`contain: paint`, `overflow`), because that would cut off those wide\n * tables and embeds, stop margins collapsing through it and make it contain floats, changing how ordinary\n * descriptions look. A site that wants the clip sets it on that element in its own CSS.\n */\n\nconst HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n\n/** The elements kept, by local name. Any other element is replaced by its content, which is cleaned the same way. */\nconst ALLOWED_ELEMENTS = new Set([\n ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div', 'br', 'hr', 'blockquote', 'code', 'pre'],\n ...['strong', 'em', 'b', 'i', 'u', 's', 'small', 'sub', 'sup'],\n ...['ul', 'ol', 'li', 'dl', 'dt', 'dd'],\n ...['table', 'caption', 'colgroup', 'col', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td'],\n ...['a', 'img', 'picture', 'source', 'video', 'figure', 'figcaption', 'iframe'],\n]);\n/** Elements whose content is code, styles or form state rather than text to show: removed with their content. */\nconst REMOVED_WITH_CONTENT = new Set(['script', 'style', 'template', 'noscript', 'textarea', 'select', 'title']);\n\n/** Attributes any kept element may have, and every `aria-*` one (`isAllowedAttribute`). */\nconst GLOBAL_ATTRIBUTES = new Set(['style', 'title', 'lang', 'dir', 'role', 'align', 'width', 'height']);\n/** Further attributes per element. */\nconst ELEMENT_ATTRIBUTES = new Map([\n ['a', new Set(['href', 'target', 'rel', 'hreflang'])],\n ['img', new Set(['src', 'srcset', 'sizes', 'alt', 'loading', 'decoding'])],\n ['source', new Set(['src', 'srcset', 'sizes', 'type', 'media'])],\n ['video', new Set(['src', 'poster', 'controls', 'autoplay', 'muted', 'loop', 'playsinline', 'preload'])],\n ['iframe', new Set(['src', 'allow', 'allowfullscreen', 'frameborder', 'loading'])],\n ['blockquote', new Set(['cite'])],\n ['ol', new Set(['start', 'type', 'reversed'])],\n ['li', new Set(['value'])],\n ['table', new Set(['border', 'cellpadding', 'cellspacing', 'summary'])],\n ['col', new Set(['span'])],\n ['colgroup', new Set(['span'])],\n ['th', new Set(['colspan', 'rowspan', 'headers', 'scope', 'valign'])],\n ['td', new Set(['colspan', 'rowspan', 'headers', 'valign'])],\n]);\n/** Attributes whose value is fetched or navigated to. */\nconst URL_ATTRIBUTES = new Set(['href', 'src', 'poster', 'cite']);\n/** Schemes that run code when navigated to. */\nconst SCRIPT_URL = /^(javascript|vbscript|data):/i;\n/** `data:` stays allowed for images, which only decode pixels. */\nconst DATA_IMAGE = /^data:image\\/(png|gif|jpeg|webp|avif);/i;\n\n/**\n * Style properties that take content out of the flow or move it from where the flow put it. `inset` and its logical\n * forms, the transform properties and the motion path (`offset-*`) are matched by prefix (`isOutOfPlace`). A\n * reflection (`-webkit-box-reflect`) paints a copy of the element beside it, as far away as the element is large.\n */\nconst OUT_OF_PLACE_PROPERTIES = new Set([\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'z-index',\n '-webkit-box-reflect',\n]);\nconst OUT_OF_PLACE_PREFIXES = ['inset', 'transform', 'translate', 'rotate', 'scale', 'offset'];\n/**\n * A length in viewport units (`100vw`, `50dvh`, `10vmax`) or container-query units (`100cqw`), which sizes content to\n * the window, not the description: outside a size container, a container-query unit is a small-viewport unit.\n */\nconst VIEWPORT_LENGTH = /\\d(?:[sld]?v|cq)(?:w|h|i|b|min|max)\\b/i;\n/**\n * Style properties, without a `-webkit-` prefix, that paint beyond their element's box by as far as their numbers\n * say: outlines and their offset, shadows, `filter` (`drop-shadow()`, `blur()`), text decoration and its offset, text\n * stroke, and the outset of a border image or mask border, which a unitless number multiplies by the border width.\n * Browsers list a shorthand's longhands; the border image and mask border shorthands are checked whole where a\n * parser keeps them.\n */\nconst PAINTS_BEYOND =\n /^(?:outline|box-shadow|text-shadow|filter|text-decoration|text-underline-offset|text-stroke)|^(?:border-image|mask-border|mask-box-image)(?:-outset)?$/;\n/** The longest length, in pixels, those properties may give. */\nconst MAX_PAINT_PX = 40;\n/**\n * Properties on which a small negative value only tightens text, with the most a negative value may be in each unit;\n * one in another unit, or beyond the limit, is removed. Spacing stops at -0.25em (-4px at a 16px font), less than the\n * advance of a letter or a space, so text tightens without running back over what precedes it. An indent stops at\n * `MAX_PAINT_PX`, the reach allowed to anything that paints beyond its element.\n */\nconst NEGATIVE_LIMITS = new Map<string, Partial<Record<string, number>>>([\n ['letter-spacing', { px: 4, em: 0.25 }],\n ['word-spacing', { px: 4, em: 0.25 }],\n ['text-indent', { px: MAX_PAINT_PX }],\n]);\n/**\n * A number with its unit, if any, captured (`5000px`, `1.5em`, `5e3px`, `100`, `5000%`). Digits inside a word or a\n * hex colour (`#5000ff`) are not one.\n */\nconst NUMBER = /(?<![\\w#.-])[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?(%|[a-z]+)?/gi;\n/**\n * A function other than `drop-shadow()` and `blur()`, with no function inside it: colours, a filter's colour and\n * opacity changes and a border image's gradient, whose numbers paint nothing beyond the box. They are left out before\n * the numbers are checked.\n */\nconst NON_LENGTH_FUNCTION = /(?<![\\w-])(?!drop-shadow\\(|blur\\()[\\w-]+\\([^()]*\\)/g;\n/**\n * Values whose lengths depend on more than the declaration (the page's custom properties, arithmetic, the\n * environment), so they can't be checked against `MAX_PAINT_PX`.\n */\nconst COMPUTED_VALUE = /(?:var|calc|min|max|clamp|env|attr)\\(/i;\n/** Style values that fetch a resource or run code (`expression()` in old Internet Explorer). */\nconst FETCHING_VALUE = /url\\(|expression\\(|@import/i;\n\n/** Browsers ignore control characters and spaces in a URL's scheme (`java^Iscript:`), so the check does too. */\n// eslint-disable-next-line no-control-regex -- control characters are exactly what browsers skip in a scheme\nconst IGNORED_IN_SCHEME = /[\\u0000- ]/g;\n\n/**\n * An embed runs its own player script on its own origin, but can't navigate this page, open dialogs over it or submit\n * forms. `allow-same-origin` keeps the player's own storage and cookies, which YouTube and Vimeo need.\n */\nconst IFRAME_SANDBOX = 'allow-scripts allow-same-origin allow-popups allow-presentation';\n/**\n * The permissions an embed may ask the page to delegate: the ones video players and social embeds use. Camera,\n * microphone, location, payment and the rest are never delegated to store content.\n */\nconst IFRAME_FEATURES = new Set([\n ...['autoplay', 'encrypted-media', 'picture-in-picture', 'fullscreen', 'clipboard-write'],\n ...['accelerometer', 'gyroscope', 'web-share'],\n]);\n/** Players check which site embeds them (YouTube refuses to play without it): the origin is sent, never the path. */\nconst IFRAME_REFERRER_POLICY = 'strict-origin-when-cross-origin';\n/**\n * The video hosts product descriptions embed, by the origin of their embed code: YouTube (and its cookie-less\n * domain), Vimeo, Loom and Wistia. Store staff choose the video these hosts play, not the page around it.\n */\nconst EMBED_ORIGINS = [\n 'https://www.youtube.com',\n 'https://www.youtube-nocookie.com',\n 'https://player.vimeo.com',\n 'https://www.loom.com',\n 'https://fast.wistia.net',\n];\n\n/** @param name An attribute name, lower case. */\nfunction isAllowedAttribute(tag: string, name: string): boolean {\n return GLOBAL_ATTRIBUTES.has(name) || name.startsWith('aria-') || (ELEMENT_ATTRIBUTES.get(tag)?.has(name) ?? false);\n}\n\n/** @returns False for a URL that would run script. */\nfunction isSafeUrl(tag: string, name: string, value: string): boolean {\n const url = value.replace(IGNORED_IN_SCHEME, '');\n if (!SCRIPT_URL.test(url)) return true;\n return tag === 'img' && name === 'src' && DATA_IMAGE.test(url);\n}\n\n/** @returns Null for text that is not a URL, which no check below lets through. */\nfunction urlOrNull(value: string, base?: string): URL | null {\n try {\n return new URL(value, base);\n } catch {\n return null;\n }\n}\n\n/** What cleaning needs to know about the page the fragment is for. */\ninterface Page {\n /** The page's origin, `null` when it has none (`about:blank`). */\n origin: string | null;\n /** Relative URLs resolve against it, as they will once the fragment is inserted. */\n baseUrl: string;\n /** An element whose `style` parses declarations the way the browser applies them. */\n styleParser: HTMLElement;\n /** The origins an iframe may load from. */\n embedOrigins: ReadonlySet<string>;\n}\n\nfunction pageOf(document: Document, styleParser: HTMLElement, embedOrigins: readonly string[]): Page {\n const baseUrl = document.baseURI;\n const origin = urlOrNull(baseUrl)?.origin ?? 'null';\n return {\n origin: origin === 'null' ? null : origin,\n baseUrl,\n styleParser,\n embedOrigins: new Set([...EMBED_ORIGINS, ...embedOrigins]),\n };\n}\n\n/**\n * @param value An iframe's `src`.\n * @returns True for a URL on an allowed embed origin other than the page's: any other embed is removed.\n */\nfunction isAllowedEmbed(value: string, page: Page): boolean {\n const url = urlOrNull(value.trim(), page.baseUrl);\n return url !== null && url.origin !== page.origin && page.embedOrigins.has(url.origin);\n}\n\n/**\n * @param value An iframe's `allow` attribute: features separated by `;`, each with an optional allowlist.\n * @returns The features from `IFRAME_FEATURES` alone, each for the embed's own origin.\n */\nfunction allowedFeatures(value: string): string {\n const features = value.split(';').map((directive) => directive.trim().split(/\\s+/)[0]?.toLowerCase() ?? '');\n return [...new Set(features.filter((feature) => IFRAME_FEATURES.has(feature)))].join('; ');\n}\n\n/**\n * @param property A style property name, lower case.\n * @returns True for a declaration that would put content anywhere but where the description's flow puts it.\n */\nfunction isOutOfPlace(property: string, value: string): boolean {\n if (OUT_OF_PLACE_PROPERTIES.has(property) || OUT_OF_PLACE_PREFIXES.some((prefix) => property.startsWith(prefix))) {\n return true;\n }\n if (VIEWPORT_LENGTH.test(value)) return true;\n const limits = NEGATIVE_LIMITS.get(property);\n if (limits) return COMPUTED_VALUE.test(value) || movesBackTooFar(value, limits);\n // A negative margin moves content back over what comes before. A minus sign is a negative length, or a `calc()`\n // that can be one.\n return property.startsWith('margin') && value.includes('-');\n}\n\n/** @param limits The most a negative number may be, by its unit (`NEGATIVE_LIMITS`). */\nfunction movesBackTooFar(value: string, limits: Partial<Record<string, number>>): boolean {\n return [...value.matchAll(NUMBER)].some(([number = '', unit = '']) => {\n const amount = parseFloat(number);\n if (amount >= 0) return false;\n const limit = limits[unit.toLowerCase()];\n return limit === undefined || -amount > limit;\n });\n}\n\n/**\n * @param property A style property name, lower case.\n * @returns True for a declaration that would paint farther beyond its element than `MAX_PAINT_PX`. Only pixel\n * lengths can be checked: any other non-zero number counts as too far, whether it has another unit (`em` grows with\n * the font size, which a description sets too), a percentage or none (an outset's multiple of the border width).\n */\nfunction paintsTooFar(property: string, value: string): boolean {\n if (!PAINTS_BEYOND.test(property.replace(/^-webkit-/, ''))) return false;\n if (COMPUTED_VALUE.test(value)) return true;\n return [...value.replace(NON_LENGTH_FUNCTION, '').matchAll(NUMBER)].some(([number = '', unit = '']) => {\n const amount = Math.abs(parseFloat(number));\n return amount !== 0 && (unit.toLowerCase() !== 'px' || amount > MAX_PAINT_PX);\n });\n}\n\n/**\n * An inline style as the browser's own parser reads it, without out-of-place, far-painting or fetching declarations.\n * The parsed form is written back, so text the parser skipped (an unknown or malformed declaration) is gone too.\n */\nfunction cleanStyle(value: string, styleParser: HTMLElement): string {\n const style = styleParser.style;\n style.cssText = value;\n const properties = Array.from({ length: style.length }, (_, index) => style.item(index));\n for (const property of properties) {\n const value = style.getPropertyValue(property);\n const remove = isOutOfPlace(property, value) || paintsTooFar(property, value) || FETCHING_VALUE.test(value);\n if (remove) style.removeProperty(property);\n }\n const cleaned = style.cssText;\n styleParser.removeAttribute('style');\n return cleaned;\n}\n\n/** @param element An allowed element: its other attributes are removed. */\nfunction cleanAttributes(element: Element, page: Page): void {\n const tag = element.localName;\n for (const attribute of [...element.attributes]) {\n const name = attribute.name.toLowerCase();\n const isUrl = URL_ATTRIBUTES.has(name);\n const keep = isAllowedAttribute(tag, name) && (!isUrl || isSafeUrl(tag, name, attribute.value));\n if (!keep) element.removeAttribute(attribute.name);\n }\n const style = element.getAttribute('style');\n if (style === null) return;\n const cleaned = cleanStyle(style, page.styleParser);\n if (cleaned === '') element.removeAttribute('style');\n else element.setAttribute('style', cleaned);\n}\n\n/** @param element A kept element, its attributes cleaned. */\nfunction hardenEmbedsAndLinks(element: Element): void {\n if (element.localName === 'iframe') {\n const allow = allowedFeatures(element.getAttribute('allow') ?? '');\n if (allow === '') element.removeAttribute('allow');\n else element.setAttribute('allow', allow);\n element.setAttribute('sandbox', IFRAME_SANDBOX);\n element.setAttribute('referrerpolicy', IFRAME_REFERRER_POLICY);\n return;\n }\n if (element.localName !== 'a' || element.getAttribute('target')?.toLowerCase() !== '_blank') return;\n // The opened page gets no handle on this one (`window.opener`) and isn't told which page linked to it.\n const rel = new Set((element.getAttribute('rel') ?? '').split(/\\s+/).filter(Boolean));\n rel.add('noopener');\n rel.add('noreferrer');\n element.setAttribute('rel', [...rel].join(' '));\n}\n\nfunction cleanElement(element: Element, page: Page): void {\n const tag = element.localName;\n const isHtml = element.namespaceURI === HTML_NAMESPACE;\n if (isHtml && REMOVED_WITH_CONTENT.has(tag)) {\n element.remove();\n return;\n }\n if (!isHtml || !ALLOWED_ELEMENTS.has(tag)) {\n element.replaceWith(...element.childNodes);\n return;\n }\n if (tag === 'iframe' && !isAllowedEmbed(element.getAttribute('src') ?? '', page)) {\n element.remove();\n return;\n }\n cleanAttributes(element, page);\n hardenEmbedsAndLinks(element);\n}\n\n/**\n * Store HTML as a fragment of content elements only.\n *\n * @param document The document the fragment is for.\n * @param embedOrigins Origins iframes may load from besides the video players in `EMBED_ORIGINS`: the config's\n * `embedOrigins`, already checked to be https origins.\n */\nexport function storeHtmlFragment(\n html: string | null | undefined,\n document: Document = globalThis.document,\n embedOrigins: readonly string[] = [],\n): DocumentFragment {\n const template = document.createElement('template');\n // A <template>'s content is parsed inert: nothing in it loads or runs before it is cleaned.\n // eslint-disable-next-line no-restricted-syntax -- parsing into an inert template is the sanctioned way in\n template.innerHTML = html ?? '';\n // The template itself is never inserted, so its own `style` is a free parser for the content's declarations.\n const page = pageOf(document, template, embedOrigins);\n // Document order: a replaced element's content moves up and is visited after it; what a removed one held leaves\n // the fragment with it.\n for (const element of [...template.content.querySelectorAll('*')]) cleanElement(element, page);\n return template.content;\n}\n", "/**\n * Conversions of Shopify values that the browser script and the server helpers (`src/server.ts`, `src/astro.ts`)\n * share, so a server-rendered page and the script that takes it over agree. Pure: no config, no DOM.\n */\n\n/** A Storefront API `MoneyV2`. */\nexport interface Money {\n amount: string;\n currencyCode: string;\n}\n\n/** The `.env` names a store's domain and public Storefront token are read from, first match wins. */\nexport const STORE_ENV_NAMES = {\n domain: ['PUBLIC_SHOPIFY_DOMAIN', 'PUBLIC_SHOPIFY_STORE_DOMAIN', 'SHOPIFY_DOMAIN'],\n token: ['PUBLIC_SHOPIFY_STOREFRONT_TOKEN', 'PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN', 'SHOPIFY_STOREFRONT_TOKEN'],\n} as const;\n\n/**\n * True for an absolute https URL. Links and redirects that come from Shopify (checkout, status pages,\n * tracking) are only used when this holds.\n */\nexport function isHttpsUrl(url: unknown): url is string {\n if (typeof url !== 'string') return false;\n try {\n return new URL(url).protocol === 'https:';\n } catch {\n // Not an absolute URL, so not an https one.\n return false;\n }\n}\n\n/**\n * True for a token Shopify issues as a secret (`shpat_` Admin, `shpss_` shared secret, `shpca_` client credentials,\n * `shppa_` private app, `shpua_` user). The public Storefront token has no such prefix. A secret token must never\n * reach a page.\n */\nexport function isSecretShopifyToken(token: string): boolean {\n return /^shp[a-z]{2}_/.test(token.trim());\n}\n\n/**\n * Money as text in `locale`, e.g. `{ amount: '30.0', currencyCode: 'EUR' }` in `nl-NL` \u2192 `\u20AC 30,00`.\n *\n * @param locale An `Intl` locale; the runtime's own when absent.\n * @returns `''` without money or with an amount that isn't a number.\n */\nexport function formatMoney(money: Money | null | undefined, locale: string | undefined): string {\n if (!money) return '';\n const amount = parseFloat(money.amount);\n if (Number.isNaN(amount)) return '';\n try {\n return new Intl.NumberFormat(locale || undefined, { style: 'currency', currency: money.currencyCode }).format(\n amount,\n );\n } catch {\n // A currency code Intl doesn't know.\n return `${money.currencyCode} ${amount.toFixed(2)}`;\n }\n}\n\n/**\n * A product page's path. `productPath` is a folder (`/product` \u2192 `/product/<handle>`) or a template with `{handle}`\n * and `{id}` (the numeric product id), e.g. `/product?handle={handle}`.\n *\n * @param id A product gid or numeric id.\n * @returns `null` when the path needs a handle or an id the product lacks: no link beats a wrong one.\n */\nexport function productUrl(productPath: string, handle: string | null | undefined, id?: string | null): string | null {\n if (!/\\{(handle|id)\\}/.test(productPath)) {\n return handle ? `${productPath.replace(/\\/$/, '')}/${encodeURIComponent(handle)}` : null;\n }\n const numericId = id ? (id.split('/').at(-1) ?? '') : '';\n if (productPath.includes('{handle}') && !handle) return null;\n if (productPath.includes('{id}') && !numericId) return null;\n return productPath\n .replace(/\\{handle\\}/g, encodeURIComponent(handle ?? ''))\n .replace(/\\{id\\}/g, encodeURIComponent(numericId));\n}\n\n/**\n * The key Tether loads a product by: a handle, or a product gid.\n *\n * @param raw A handle, a numeric id, a gid or a product URL (`\u2026/products/<handle>`).\n */\nexport function productKey(raw: string | number | null | undefined): string | null {\n const value = String(raw ?? '').trim();\n if (!value) return null;\n if (value.startsWith('gid://')) return value;\n if (/^\\d+$/.test(value)) return `gid://shopify/Product/${value}`;\n return /\\/products\\/([^/?#]+)/.exec(value)?.[1] ?? value;\n}\n", "/**\n * Shopify images on the page: sized through the CDN's URL parameters, written to an `<img>` or as a CSS background,\n * and never downloaded twice when the server already rendered the same image.\n */\nimport { attr } from './markup.ts';\n\n/** Shopify CDN parameters. */\nexport interface ImageSize {\n width?: string | number | null;\n height?: string | number | null;\n /** `center`, `top`, `bottom`, `left` or `right`; only applies with a width or height. */\n crop?: string | null;\n}\n\n/**\n * Shopify CDN image at a size: `imageUrl(url, { width: 800 })`. Use the same width as the element's max-width\n * attribute, so the browser script keeps the image instead of loading it again.\n *\n * @returns The sized URL; `url` itself when it is empty or not absolute.\n */\nexport function imageUrl<Url extends string | null | undefined>(\n url: Url,\n { width, height, crop }: ImageSize = {},\n): Url {\n if (!url) return url;\n let parsed;\n try {\n parsed = new URL(url);\n } catch (error) {\n // A relative or malformed URL has no CDN parameters to set: it is used as written.\n if (error instanceof TypeError) return url;\n throw error;\n }\n if (width) parsed.searchParams.set('width', String(width));\n if (height) parsed.searchParams.set('height', String(height));\n if (crop) parsed.searchParams.set('crop', crop);\n // `url` is a non-empty string here, so `Url` is a string type.\n return parsed.toString() as Url;\n}\n\n/** @returns `name` as Webflow's custom attribute, or in valid HTML as `<prefix>-name` / `data-name`. */\nfunction sizeAttribute(el: Element, name: string): string | null {\n return el.getAttribute(name) ?? el.getAttribute(attr(name)) ?? el.getAttribute(`data-${name}`);\n}\n\n/** @param el The element whose max-width / max-height / crop attributes size the image. */\nexport function cdnUrl<Url extends string | null | undefined>(url: Url, el: Element | null | undefined): Url {\n if (!url || !el) return url;\n const width = sizeAttribute(el, 'max-width');\n const height = sizeAttribute(el, 'max-height');\n return imageUrl(url, { width, height, crop: width || height ? sizeAttribute(el, 'crop') : null });\n}\n\n/** @returns The URL without its query (the size parameters). */\nconst withoutQuery = (url: string) => url.replace(/\\?.*$/, '');\n\n/** @returns True for the same image at any size parameters: the server-rendered one is kept. */\nexport function sameImage(a: string | null | undefined, b: string | null | undefined): boolean {\n if (!a || !b) return false;\n return withoutQuery(a) === withoutQuery(b);\n}\n\n/**\n * A CSS `url()` for an image URL from Shopify. Characters that would end the string are escaped, so the URL can't\n * add declarations; only http(s), relative and data:image URLs are used.\n */\nfunction cssUrl(url: string): string {\n const hasScheme = /^\\s*[a-z][a-z0-9+.-]*:/i.test(url);\n if (hasScheme && !/^\\s*(https?:|data:image\\/)/i.test(url)) return 'none';\n const escaped = url.replace(/[\"\\\\\\n\\r\\f]/g, (char) => `\\\\${char.charCodeAt(0).toString(16)} `);\n return `url(\"${escaped}\")`;\n}\n\n/** Shows `url` in `el`: as the `src` of an `<img>` (lazy unless it says otherwise), else as its background image. */\nexport function setImage(el: HTMLElement, url: string | null | undefined, alt?: string | null): void {\n if (!url) return;\n const src = cdnUrl(url, el);\n if (el.tagName !== 'IMG') {\n el.style.backgroundImage = cssUrl(src);\n return;\n }\n if (sameImage(el.getAttribute('src'), src)) {\n if (alt != null && !el.getAttribute('alt')) el.setAttribute('alt', alt);\n return;\n }\n el.removeAttribute('srcset');\n el.removeAttribute('sizes');\n el.setAttribute('src', src);\n if (alt != null) el.setAttribute('alt', alt);\n if (!el.getAttribute('loading')) el.setAttribute('loading', 'lazy');\n}\n", "/**\n * Lists rendered from a template: a repeater's first child in the designer is the template, and every item gets a\n * copy of it. The other children are placeholder content and are removed.\n */\nimport { cfg, reportError, warn } from './config.ts';\n\n/** Each repeater's template, captured on first render. */\nconst templates = new WeakMap<Element, HTMLElement | null>();\n\n/** @returns True once the repeater's template has been captured (it may be empty after that). */\nexport const hasTemplate = (container: Element): boolean => Boolean(templates.get(container));\n\n/**\n * A deep copy of a template. `cloneNode` is typed as returning a `Node`; a copy of an element is an element of the\n * same kind.\n */\nconst copyOf = (template: HTMLElement) => template.cloneNode(true) as HTMLElement;\n\nfunction templateOf(container: Element): HTMLElement | null {\n const known = templates.get(container);\n if (known !== undefined) return known;\n const first = container.firstElementChild;\n // Repeaters are HTML lists in the designer; their first child is an HTML element.\n const template = first ? copyOf(first as HTMLElement) : null;\n templates.set(container, template);\n container.replaceChildren();\n return template;\n}\n\n/** Rebuilds the list on every render. Fine for images, tags, options. */\nexport function repeat<Item>(\n container: Element,\n items: readonly Item[],\n render: (node: HTMLElement, item: Item, index: number) => void,\n): void {\n const template = templateOf(container);\n if (!template) {\n if (items.length) warn('repeater without a template child', container);\n return;\n }\n container.replaceChildren();\n items.forEach((item, index) => {\n const node = copyOf(template);\n render(node, item, index);\n container.appendChild(node);\n });\n}\n\ninterface KeyedHooks {\n /** Runs on a node inserted for a new item. */\n onEnter?: ((node: HTMLElement) => void) | null;\n /** Runs on a node whose item is gone; `done()` removes it. */\n onLeave?: ((node: HTMLElement, done: () => void) => void) | null;\n}\n\nfunction leave(node: HTMLElement, onLeave: KeyedHooks['onLeave']): void {\n const done = () => {\n node.remove();\n };\n if (!onLeave) {\n done();\n return;\n }\n try {\n onLeave(node, done);\n } catch (error) {\n reportError(error, 'repeater onLeave');\n done();\n }\n}\n\n/**\n * Inserts the nodes of new items next to the item before them, leaving the existing nodes where they are.\n *\n * @param wanted Every item's node, in order.\n * @param fresh The nodes not yet in the container.\n */\nfunction insertFresh(\n container: Element,\n wanted: HTMLElement[],\n fresh: Set<HTMLElement>,\n onEnter: KeyedHooks['onEnter'],\n): void {\n wanted.forEach((node, index) => {\n if (!fresh.has(node)) return;\n const previous = wanted[index - 1];\n if (previous?.parentNode === container) previous.after(node);\n else container.prepend(node);\n if (!onEnter) return;\n try {\n onEnter(node);\n } catch (error) {\n reportError(error, 'repeater onEnter');\n }\n });\n}\n\n/**\n * Keyed repeater: keeps existing nodes, inserts new ones, removes old ones. Used for cart lines, so values can\n * animate and lines can slide in and out.\n */\nexport function repeatKeyed<Item>(\n container: Element,\n items: readonly Item[],\n keyOf: (item: Item, index: number) => string | number,\n render: (node: HTMLElement, item: Item, index: number, isNew: boolean) => void,\n { onEnter, onLeave }: KeyedHooks = {},\n): void {\n const template = templateOf(container);\n if (!template) {\n warn('repeater without a template child', container);\n return;\n }\n\n const keyAttribute = `data-${cfg.prefix}-key`;\n const leavingAttribute = `data-${cfg.prefix}-leaving`;\n // The list's children are copies of its template, HTML elements like it.\n const live = () =>\n [...(container.children as HTMLCollectionOf<HTMLElement>)].filter(\n (node) => node.hasAttribute(keyAttribute) && !node.hasAttribute(leavingAttribute),\n );\n\n const existing = new Map(live().map((node) => [node.getAttribute(keyAttribute), node]));\n const wanted: HTMLElement[] = [];\n const fresh = new Set<HTMLElement>();\n\n items.forEach((item, index) => {\n const key = String(keyOf(item, index));\n let node = existing.get(key);\n if (node) existing.delete(key);\n else {\n node = copyOf(template);\n node.setAttribute(keyAttribute, key);\n fresh.add(node);\n }\n render(node, item, index, fresh.has(node));\n wanted.push(node);\n });\n\n const staying = wanted.filter((node) => !fresh.has(node));\n const current = live().filter((node) => staying.includes(node));\n const inOrder = current.length === staying.length && current.every((node, index) => node === staying[index]);\n\n if (!inOrder) {\n existing.forEach((node) => {\n node.remove();\n });\n wanted.forEach((node) => container.appendChild(node));\n return;\n }\n\n insertFresh(container, wanted, fresh, onEnter);\n\n existing.forEach((node) => {\n node.setAttribute(leavingAttribute, '');\n node.removeAttribute(keyAttribute);\n node.style.pointerEvents = 'none';\n node.inert = true;\n leave(node, onLeave);\n });\n}\n", "/**\n * The browser modules' DOM toolkit: what Shopify values look like on the page (money, dates, text, images, lists)\n * and how elements show, hide and report layout changes. Attribute names go through `attr()` so the prefix is\n * configurable. The optional parts import all of it from here (`window.Tether.__core['dom.ts']`).\n */\nimport { cfg, reportError, warn } from './config.ts';\nimport { attr, emit, get } from './markup.ts';\nimport { storeHtmlFragment } from './store-html.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { formatMoney as formatMoneyIn, productUrl as productUrlFor } from './shopify-values.ts';\nimport type { Money } from './shopify-values.ts';\n\nexport { attr, sel, get, has, qsa, scoped, emit, ownEvent, isOwnEvent, ANALYTICS_EVENTS } from './markup.ts';\nexport { imageUrl, cdnUrl, sameImage, setImage } from './images.ts';\nexport { hasTemplate, repeat, repeatKeyed } from './repeaters.ts';\n\n/**\n * State Tether keeps on elements, as `data-tether-*` attributes whatever the prefix: sites style and test against\n * these names, so they stay fixed where the `tether-*` markup attributes follow `prefix`.\n */\nconst SEEN = 'data-tether-seen';\nconst SHOWN = 'data-tether-shown';\nconst PREVIOUS_DISPLAY = 'data-tether-prev-display';\n\n/** Async renders change the page height in bursts; one notification per burst. */\nconst LAYOUT_DEBOUNCE_MS = 120;\n\n/**\n * The product page of a product (`productPath`).\n *\n * @returns `null` without the handle or id the path needs.\n */\nexport function productUrl(handle: string | null | undefined, id?: string | null): string | null {\n return productUrlFor(cfg.productPath || '/product', handle, id);\n}\n\n/** `Intl` throws a RangeError for a locale or currency it doesn't know; the caller then shows a plain fallback. */\nfunction formatOr<T>(format: () => T, fallback: T): T {\n try {\n return format();\n } catch (error) {\n if (error instanceof RangeError) return fallback;\n throw error;\n }\n}\n\n/**\n * `30` \u2192 `30%` (en, nl) or `30 %` (de, fr), in the configured locale.\n *\n * @returns `''` for no or a zero percentage.\n */\nexport function formatPercent(percentage: number | null | undefined, locale: string | undefined = cfg.locale): string {\n if (!percentage) return '';\n const format = () =>\n new Intl.NumberFormat(locale || undefined, { style: 'percent', maximumFractionDigits: 0 }).format(percentage / 100);\n return formatOr(format, `${percentage}%`);\n}\n\n/** Money in the configured locale, through the site's `formatMoney` when it has one. */\nexport function formatMoney(money: Money | null | undefined): string {\n if (!money) return '';\n if (cfg.formatMoney) return cfg.formatMoney(money, cfg.locale);\n return formatMoneyIn(money, cfg.locale);\n}\n\n/** @returns The date in the configured locale, `''` without one. */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return '';\n return formatOr(() => new Date(iso).toLocaleDateString(cfg.locale || undefined), '');\n}\n\n/**\n * Writes HTML from the Shopify admin (product and collection descriptions) into `el`, with script removed\n * (`store-html.ts`). Everything else from Shopify is written as text (`setText`).\n */\nexport function setStoreHtml(el: Element, html: string | null | undefined): void {\n el.replaceChildren(storeHtmlFragment(html, el.ownerDocument, cfg.embedOrigins));\n}\n\nconst isTextField = (el: Element): el is HTMLInputElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'TEXTAREA';\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\n/**\n * Sets the text of `el`, or its value when it is a form field.\n *\n * @param value `null` and `undefined` clear it.\n */\nexport function setText(el: Element, value: string | number | boolean | null | undefined): void {\n const text = value == null ? '' : String(value);\n if (isTextField(el)) el.value = text;\n else el.textContent = text;\n}\n\nconst readText = (el: Element) => (isTextField(el) ? el.value : el.textContent);\n\n/** `setText` that hands a change after the first render to `onValueChange`, for scramble and roll effects. */\nexport function setTextTracked(el: HTMLElement, value: string | number | boolean | null | undefined): void {\n const before = readText(el);\n setText(el, value);\n if (before !== readText(el) && el.hasAttribute(SEEN) && cfg.onValueChange) {\n try {\n cfg.onValueChange(el, before);\n } catch (error) {\n reportError(error, 'onValueChange');\n }\n }\n el.setAttribute(SEEN, '1');\n}\n\n/**\n * Shows an element hidden before the script ran (a class, an inline `display: none`, a `hidden` attribute), as\n * `block` or the value of `tether-display`.\n */\nfunction reveal(el: HTMLElement): void {\n if (el.hasAttribute(SHOWN)) return;\n el.setAttribute(SHOWN, '');\n el.removeAttribute('hidden');\n const previous = el.getAttribute(PREVIOUS_DISPLAY);\n if (previous !== null) {\n el.style.display = previous;\n el.removeAttribute(PREVIOUS_DISPLAY);\n } else if (el.style.display === 'none') {\n el.style.display = '';\n }\n if (typeof getComputedStyle === 'function' && getComputedStyle(el).display === 'none') {\n el.style.display = el.getAttribute(attr('display')) || 'block';\n }\n}\n\n/** Shows or hides `el`, restoring its own display value when shown again. */\nexport function show(el: HTMLElement, visible: boolean): void {\n if (visible) {\n reveal(el);\n return;\n }\n el.removeAttribute(SHOWN);\n // The display value to restore is a real one, never 'none'.\n if (!el.hasAttribute(PREVIOUS_DISPLAY) && el.style.display !== 'none') {\n el.setAttribute(PREVIOUS_DISPLAY, el.style.display);\n }\n el.style.display = 'none';\n}\n\n/** Marks an option button or input as chosen: `is-selected`, `aria-pressed`, and its own or inner radio/checkbox. */\nexport function setSelected(el: Element, selected: boolean): void {\n el.classList.toggle('is-selected', selected);\n el.setAttribute('aria-pressed', selected ? 'true' : 'false');\n if (isInput(el) && (el.type === 'radio' || el.type === 'checkbox')) el.checked = selected;\n const input = el.querySelector('input[type=\"radio\"],input[type=\"checkbox\"]');\n if (input && isInput(input)) input.checked = selected;\n}\n\nlet layoutTimer: ReturnType<typeof setTimeout> | undefined;\n\n/**\n * A global the page's own scripts set on `window`: `TETHER_CONFIG`, the site's config, `TETHER_INTEGRATION`, set by a\n * framework integration (Astro) that starts Tether itself, and GSAP's `ScrollTrigger`. Another script wrote it, so it\n * is read as unknown and checked where it is used.\n */\nexport const pageGlobal = (name: 'TETHER_CONFIG' | 'TETHER_INTEGRATION' | 'ScrollTrigger'): unknown =>\n Reflect.get(window, name);\n\n/** Refreshes GSAP's ScrollTrigger, when the site has it: it measures positions that a height change makes stale. */\nfunction refreshScrollTrigger(): void {\n // ScrollTrigger is a class, and `refresh` a static method of it.\n const scrollTrigger = pageGlobal('ScrollTrigger');\n if (typeof scrollTrigger !== 'function') return;\n const refresh: unknown = Reflect.get(scrollTrigger, 'refresh');\n if (typeof refresh === 'function') Reflect.apply(refresh, scrollTrigger, []);\n}\n\n/**\n * Async renders change the page height after the site's page-transition hooks ran. After a burst of them this calls\n * `onLayoutChange` (else refreshes ScrollTrigger) and emits `tether:layout-changed`, also when either of those throws.\n */\nexport function notifyLayoutChanged(reason: string): void {\n clearTimeout(layoutTimer);\n layoutTimer = setTimeout(() => {\n try {\n if (cfg.onLayoutChange) cfg.onLayoutChange(reason);\n else refreshScrollTrigger();\n } catch (error) {\n reportError(error, 'layout-changed');\n }\n emit('layout-changed', { reason });\n }, LAYOUT_DEBOUNCE_MS);\n}\n\n/**\n * Notifies a layout change (`<reason>:images`) once every image in `root` that is still loading has loaded or failed.\n */\nexport function watchImages(root: ParentNode, reason: string): void {\n const loading = [...root.querySelectorAll('img')].filter((img) => !img.complete);\n let left = loading.length;\n const done = () => {\n left -= 1;\n if (left === 0) notifyLayoutChanged(`${reason}:images`);\n };\n loading.forEach((img) => {\n img.addEventListener('load', done, { once: true });\n img.addEventListener('error', done, { once: true });\n });\n}\n\n/**\n * Runs `task` with `aria-busy` on `el`; does nothing while `el` is already busy.\n *\n * @returns The task's result, `undefined` when it didn't run.\n */\nexport function busy<T>(el: Element, task: () => T | Promise<T>): Promise<T | undefined> {\n if (el.getAttribute('aria-busy') === 'true') return Promise.resolve(undefined);\n el.setAttribute('aria-busy', 'true');\n return Promise.resolve()\n .then(task)\n .finally(() => {\n el.removeAttribute('aria-busy');\n });\n}\n\n/** @returns The attribute's JSON, `null` when it is missing or not JSON (with a warning). */\nexport function parseJSONAttr(el: Element | null | undefined, name: string): unknown {\n const raw = el ? get(el, name) : null;\n if (!raw) return null;\n try {\n const json: unknown = JSON.parse(raw);\n return json;\n } catch {\n warn(`invalid JSON in ${attr(name)}`, el);\n return null;\n }\n}\n\nexport type AttributeValue = string | number | boolean | null | undefined;\n\n/** @returns A JSON value as attribute text: `''` for none, an object or array as its JSON. */\nfunction attributeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n // What else parsed JSON holds: objects and arrays.\n return JSON.stringify(value);\n}\n\n/**\n * `tether-line-attributes`, JSON the site writes: `{ a: 1 }` or `[{ key, value }]`. An array entry that isn't an\n * object is skipped.\n */\nexport function attributesFromJson(json: unknown): { key: string; value: string }[] {\n if (Array.isArray(json)) {\n const entries: unknown[] = json;\n return entries\n .filter(isRecord)\n .map((entry) => ({ key: attributeText(entry['key']), value: attributeText(entry['value']) }));\n }\n if (!isRecord(json)) return [];\n return Object.entries(json).map(([key, value]) => ({ key, value: attributeText(value) }));\n}\n\n/** `{ a: 1 }` or `[{ key, value }]` \u2192 `[{ key, value }]` as Shopify wants cart and line attributes. */\nexport function toAttributes(\n attributes: Record<string, AttributeValue> | { key: string | number; value?: AttributeValue }[] | null | undefined,\n): { key: string; value: string }[] {\n if (!attributes) return [];\n if (Array.isArray(attributes)) {\n return attributes.map((attribute) => ({ key: String(attribute.key), value: String(attribute.value ?? '') }));\n }\n return Object.entries(attributes).map(([key, value]) => ({ key, value: String(value ?? '') }));\n}\n", "/**\n * The GraphQL fragments of what Tether renders. They are built at call time because the list sizes and metafields come\n * from the config; the browser passes its validated `cfg`, the server helpers their own. Operations put these after\n * the operation, where `withContext` expects them.\n */\nimport { cfg } from './config.ts';\nimport { toMetafieldIdentifier } from './config-sections.ts';\nimport type { TetherConfig, MetafieldIdentifier } from './config.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { StoreAccess } from './client.ts';\n\nexport interface Image {\n url: string;\n altText: string | null;\n width: number | null;\n height: number | null;\n}\n\n/** An option value's swatch: a colour, an image, or both, as the merchant set them in the admin. */\ninterface Swatch {\n color: string | null;\n image: { previewImage: Image | null } | null;\n}\n\nexport type SellingPlanAdjustmentValue =\n | { __typename: 'SellingPlanPercentagePriceAdjustment'; adjustmentPercentage: number }\n | { __typename: 'SellingPlanFixedAmountPriceAdjustment'; adjustmentAmount: Money }\n | { __typename: 'SellingPlanFixedPriceAdjustment'; price: Money };\n\nexport interface SellingPlan {\n id: string;\n name: string;\n description: string | null;\n recurringDeliveries: boolean;\n options: { name: string; value: string }[];\n priceAdjustments: { orderCount: number | null; adjustmentValue: SellingPlanAdjustmentValue }[];\n}\n\nexport interface SellingPlanGroup {\n name: string;\n appName: string | null;\n options: { name: string; values: string[] }[];\n sellingPlans: { nodes: SellingPlan[] };\n}\n\n/** A variant's price on one plan. */\nexport interface SellingPlanAllocation {\n sellingPlan: { id: string };\n /** What the customer pays at checkout (more for a prepaid plan). */\n checkoutChargeAmount: Money;\n remainingBalanceChargeAmount: Money;\n priceAdjustments: { price: Money; compareAtPrice: Money; perDeliveryPrice: Money; unitPrice: Money | null }[];\n}\n\nexport interface ProductVariant {\n id: string;\n title: string;\n availableForSale: boolean;\n currentlyNotInStock: boolean;\n /** Absent when the store's token can't read inventory. */\n quantityAvailable?: number | null;\n sku: string | null;\n barcode: string | null;\n selectedOptions: { name: string; value: string }[];\n image: Image | null;\n price: Money;\n compareAtPrice: Money | null;\n unitPrice: Money | null;\n sellingPlanAllocations: { nodes: SellingPlanAllocation[] };\n}\n\nexport interface Metafield {\n namespace: string;\n key: string;\n type: string;\n value: string;\n /** A file reference's image. */\n reference: { image?: Image } | null;\n}\n\n/** The fields of `fragment TetherProduct`. */\nexport interface Product {\n id: string;\n handle: string;\n title: string;\n description: string;\n descriptionHtml: string;\n productType: string;\n vendor: string;\n tags: string[];\n availableForSale: boolean;\n publishedAt: string | null;\n onlineStoreUrl: string | null;\n requiresSellingPlan: boolean;\n seo: { title: string | null; description: string | null };\n featuredImage: Image | null;\n images: { nodes: Image[] };\n options: { name: string; optionValues: { name: string; swatch: Swatch | null }[] }[];\n priceRange: { minVariantPrice: Money; maxVariantPrice: Money };\n compareAtPriceRange: { minVariantPrice: Money; maxVariantPrice: Money };\n /** Present when metafields are configured; `null` for one that isn't set. */\n metafields?: (Metafield | null)[];\n sellingPlanGroups: { nodes: SellingPlanGroup[] };\n variants: { nodes: ProductVariant[] };\n}\n\nexport type ProductFragmentConfig = Pick<\n TetherConfig,\n 'imagesFirst' | 'variantsFirst' | 'sellingPlanGroupsFirst' | 'sellingPlansFirst' | 'metafields'\n>;\n\nconst MONEY = 'amount currencyCode';\n/** What a token reads unless the store turned out to refuse more (client.ts). */\nconst FULL_ACCESS: StoreAccess = { inventory: true };\n\n/** The stock field, for a token that may read inventory. */\nconst inventoryField = (access: StoreAccess): string => (access.inventory ? 'quantityAvailable' : '');\nconst IMAGE = 'url altText width height';\n\n/**\n * The `metafields(identifiers:)` field. A fragment can't declare variables, so the identifiers are written into the\n * text: only valid ones (`toMetafieldIdentifier`), each quoted as a string literal.\n */\nfunction metafieldsField(metafields: ProductFragmentConfig['metafields']) {\n const identifiers: MetafieldIdentifier[] = [];\n for (const ref of metafields) {\n const identifier = toMetafieldIdentifier(ref);\n if (identifier) identifiers.push(identifier);\n }\n if (!identifiers.length) return '';\n const list = identifiers\n .map(({ namespace, key }) => `{namespace: ${JSON.stringify(namespace)}, key: ${JSON.stringify(key)}}`)\n .join(', ');\n return `metafields(identifiers:[${list}]){namespace key type value reference{... on MediaImage{image{${IMAGE}}}}}`;\n}\n\nconst SELLING_PLAN_FIELDS = ` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${MONEY}}}... on SellingPlanFixedPriceAdjustment{price{${MONEY}}}}}`;\n\nconst ALLOCATION_FIELDS = ` sellingPlan{id}checkoutChargeAmount{${MONEY}}remainingBalanceChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}unitPrice{${MONEY}}}`;\n\n/**\n * @param o The list sizes and metafields.\n * @param access What the token may read.\n * @returns `fragment TetherProduct on Product`.\n */\nexport function productFragment(o: ProductFragmentConfig = cfg, access: StoreAccess = FULL_ACCESS): string {\n return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${o.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField(o.metafields)} sellingPlanGroups(first:${o.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${o.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${o.variantsFirst}){nodes{id title availableForSale currentlyNotInStock ${inventoryField(access)} sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${o.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;\n}\n\n/**\n * @param access What the token may read.\n * @returns `fragment TetherCart on Cart`.\n */\nexport function cartFragment(access: StoreAccess = FULL_ACCESS): string {\n return ` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${MONEY}}totalAmount{${MONEY}}checkoutChargeAmount{${MONEY}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${MONEY}}subtotalAmount{${MONEY}}amountPerQuantity{${MONEY}}compareAtAmountPerQuantity{${MONEY}}}discountAllocations{discountedAmount{${MONEY}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping ${inventoryField(access)} image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}selectedOptions{name value}product{id title handle vendor featuredImage{${IMAGE}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`;\n}\n\n/** The collection fields Tether renders, for inside a `collection { }` selection. */\nexport function collectionFields(): string {\n return ` id handle title description descriptionHtml image{${IMAGE}}seo{title description}`;\n}\n", "/**\n * Shopify selling plans (subscriptions): which plans a product and a variant have, and what each costs. Pure, no DOM,\n * shared by the browser script and the server helpers.\n */\nimport { fromMinorUnits, toMinorUnits } from './amounts.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { SellingPlan, SellingPlanGroup, SellingPlanAllocation } from './fragments.ts';\n\nconst PERCENT = 100;\n\n/** A plan with the group it belongs to. */\nexport type PlanInGroup = SellingPlan & { group: SellingPlanGroup };\nexport interface ProductWithPlans {\n sellingPlanGroups?: { nodes: SellingPlanGroup[] } | null;\n}\n/** A variant as far as plans go. A cart line's variant has no allocations. */\nexport interface VariantWithPlans {\n price: Money;\n sellingPlanAllocations?: { nodes: SellingPlanAllocation[] } | null;\n}\nexport type PlanAdjustment =\n { type: 'percentage'; value: number } | { type: 'amount'; value: Money } | { type: 'price'; value: Money };\n\n/**\n * @param value A gid, a numeric id, or anything else.\n * @param type The Shopify type, e.g. `ProductVariant`.\n * @returns The gid of a numeric id; a gid or other value as written; `null` when empty.\n */\nexport function toGid(value: unknown, type: string): string | null {\n const text = typeof value === 'string' || typeof value === 'number' ? String(value).trim() : '';\n if (!text) return null;\n if (text.startsWith('gid://')) return text;\n if (/^\\d+$/.test(text)) return `gid://shopify/${type}/${text}`;\n return text;\n}\n\nexport const GID = {\n product: (value: unknown) => toGid(value, 'Product'),\n variant: (value: unknown) => toGid(value, 'ProductVariant'),\n sellingPlan: (value: unknown) => toGid(value, 'SellingPlan'),\n};\n\n/** @returns All plans of the product, flattened. */\nexport function allPlans(product: ProductWithPlans | null | undefined): PlanInGroup[] {\n const groups = product?.sellingPlanGroups?.nodes ?? [];\n return groups.flatMap((group) => group.sellingPlans.nodes.map((plan) => ({ ...plan, group })));\n}\n\nexport function hasPlans(product: ProductWithPlans | null | undefined): boolean {\n return allPlans(product).length > 0;\n}\n\n/** @returns The variant's allocation on the plan; `null` when the variant isn't in it. */\nexport function allocationFor(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): SellingPlanAllocation | null {\n if (!variant || !planId) return null;\n const allocations = variant.sellingPlanAllocations?.nodes ?? [];\n return allocations.find((allocation) => allocation.sellingPlan.id === planId) ?? null;\n}\n\n/** @returns The plans this variant can be bought on. */\nexport function plansForVariant(\n product: ProductWithPlans | null | undefined,\n variant: VariantWithPlans | null | undefined,\n): PlanInGroup[] {\n return allPlans(product).filter((plan) => allocationFor(variant, plan.id));\n}\n\n/** @returns The price per delivery on the plan; the one-time price when the variant isn't on it. */\nexport function planPrice(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): Money | null {\n const adjustment = allocationFor(variant, planId)?.priceAdjustments[0];\n return adjustment?.perDeliveryPrice ?? adjustment?.price ?? variant?.price ?? null;\n}\n\n/** @returns What the customer pays at checkout on the plan (prepaid plans charge more up front). */\nexport function planCheckoutCharge(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): Money | null {\n const allocation = allocationFor(variant, planId);\n return allocation?.checkoutChargeAmount ?? allocation?.priceAdjustments[0]?.price ?? variant?.price ?? null;\n}\n\n/** @returns What the plan saves on the one-time price; no amount and 0% when it saves nothing. */\nexport function planSavings(\n variant: VariantWithPlans | null | undefined,\n planId: string | null | undefined,\n): { amount: Money | null; percentage: number } {\n const onPlanPrice = planPrice(variant, planId);\n if (!variant || !onPlanPrice) return { amount: null, percentage: 0 };\n const oneTime = parseFloat(variant.price.amount);\n const onPlan = parseFloat(onPlanPrice.amount);\n if (!Number.isFinite(oneTime) || !Number.isFinite(onPlan) || oneTime <= 0 || onPlan >= oneTime) {\n return { amount: null, percentage: 0 };\n }\n const saved = toMinorUnits(variant.price) - toMinorUnits(onPlanPrice);\n return {\n amount: fromMinorUnits(saved, variant.price.currencyCode),\n percentage: Math.round((1 - onPlan / oneTime) * PERCENT),\n };\n}\n\n/**\n * @returns The plan's own discount, whatever the variant: a percentage, a fixed amount off or a fixed price.\n */\nexport function planAdjustment(plan: Pick<SellingPlan, 'priceAdjustments'> | null | undefined): PlanAdjustment | null {\n const value = plan?.priceAdjustments[0]?.adjustmentValue;\n if (!value) return null;\n switch (value.__typename) {\n case 'SellingPlanPercentagePriceAdjustment':\n return { type: 'percentage', value: value.adjustmentPercentage };\n case 'SellingPlanFixedAmountPriceAdjustment':\n return { type: 'amount', value: value.adjustmentAmount };\n case 'SellingPlanFixedPriceAdjustment':\n return { type: 'price', value: value.price };\n default:\n // An adjustment type newer than the API version Tether was written for.\n return null;\n }\n}\n\n/**\n * Finds a plan by id, numeric id, name or option value (case-insensitive), so Webflow editors can write\n * `tether-default-plan=\"Every month\"`.\n */\nexport function findPlan(\n product: ProductWithPlans | null | undefined,\n ref: string | null | undefined,\n): PlanInGroup | null {\n if (!ref) return null;\n const plans = allPlans(product);\n const gid = GID.sellingPlan(ref);\n const needle = ref.trim().toLowerCase();\n return (\n plans.find((plan) => plan.id === gid) ??\n plans.find((plan) => plan.name.toLowerCase() === needle) ??\n plans.find((plan) => plan.options.some((option) => option.value.toLowerCase() === needle)) ??\n null\n );\n}\n", "/**\n * Arithmetic on Shopify's money amounts, which are decimal strings (`'27.50'`). Comparisons go digit by digit, as\n * integers of the smallest unit present, so two amounts that differ only past float precision aren't misjudged. Sums\n * and differences are taken in the currency's own smallest unit, so JPY stays whole and KWD keeps its three digits.\n */\nimport type { Money } from './shopify-values.ts';\n\nconst DECIMAL = /^(-?)(\\d+)(?:\\.(\\d+))?$/;\n\n/**\n * @param fractionDigits Digits after the point to scale to.\n * @returns The amount in units of 10^-fractionDigits, `null` when it isn't a decimal number.\n */\nfunction toScaledInteger(amount: string, fractionDigits: number): bigint | null {\n const match = DECIMAL.exec(amount.trim());\n if (!match) return null;\n const [, sign = '', whole = '0', fraction = ''] = match;\n const digits = BigInt(`${whole}${fraction.padEnd(fractionDigits, '0')}`);\n return sign === '-' ? -digits : digits;\n}\n\nconst fractionLength = (amount: string) => amount.split('.')[1]?.length ?? 0;\n\n/** @returns True when both are decimal numbers and `amount` is the larger one. */\nexport function isGreaterAmount(amount: string | null | undefined, other: string | null | undefined): boolean {\n if (amount == null || other == null) return false;\n const fractionDigits = Math.max(fractionLength(amount.trim()), fractionLength(other.trim()));\n const scaled = toScaledInteger(amount, fractionDigits);\n const scaledOther = toScaledInteger(other, fractionDigits);\n if (scaled === null || scaledOther === null) return false;\n return scaled > scaledOther;\n}\n\n/** For a currency code Intl doesn't know: most currencies have two digits, and `formatMoney` then shows two too. */\nconst DEFAULT_FRACTION_DIGITS = 2;\n\n/** Currency code \u2192 its fraction digits, which never change on a page. */\nconst fractionDigitsByCurrency = new Map<string, number>();\n\n/** @returns The digits after the point the currency has: 0 for JPY, 2 for EUR, 3 for KWD. */\nfunction currencyFractionDigits(currencyCode: string): number {\n const known = fractionDigitsByCurrency.get(currencyCode);\n if (known !== undefined) return known;\n let digits = DEFAULT_FRACTION_DIGITS;\n try {\n const format = new Intl.NumberFormat('en', { style: 'currency', currency: currencyCode });\n digits = format.resolvedOptions().maximumFractionDigits ?? DEFAULT_FRACTION_DIGITS;\n } catch {\n // A code Intl doesn't know (RangeError): see DEFAULT_FRACTION_DIGITS.\n }\n fractionDigitsByCurrency.set(currencyCode, digits);\n return digits;\n}\n\n/** @returns The amount in the currency's smallest unit (cents, yen, fils), rounded to a whole one. */\nexport function toMinorUnits(money: Money): number {\n return Math.round(parseFloat(money.amount) * 10 ** currencyFractionDigits(money.currencyCode));\n}\n\n/** @returns The amount written with the currency's own number of digits. */\nexport function fromMinorUnits(minorUnits: number, currencyCode: string): Money {\n const digits = currencyFractionDigits(currencyCode);\n return { amount: (minorUnits / 10 ** digits).toFixed(digits), currencyCode };\n}\n", "/**\n * The structure of Tether's product fragment, checked on a value read from the page or from a Storefront answer\n * before it is used as a `Product`. Rendering walks the product's lists and prices without checking each step, so a\n * truncated or hand-written entry, or an answer missing fields, would otherwise fail deep inside a render instead of\n * being skipped or refused.\n */\nimport { matches } from './client.ts';\nimport { field, isRecord } from '../server/src/shared/json.ts';\nimport type { ShapeOf } from './client.ts';\nimport type { Product } from './fragments.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { PageInfo } from './loader.ts';\n\n/** A page of a GraphQL connection of products. */\nexport interface ProductConnection {\n nodes: Product[];\n pageInfo: PageInfo;\n}\n\n/** A Storefront API `MoneyV2`. */\nexport const MONEY = { amount: 'string', currencyCode: 'string' } as const satisfies ShapeOf<Money>;\n\n/**\n * A product's id, handle and title, and every list and price a render walks without a fallback: images, options,\n * variants (each with an id, a price and its options) and the price range. Text fields are written as text, and the\n * selling plan lists are read with a fallback, so they need no check.\n */\nexport const PRODUCT = {\n id: 'string',\n handle: 'string',\n title: 'string',\n options: ['object'],\n images: { nodes: ['object'] },\n priceRange: { minVariantPrice: MONEY, maxVariantPrice: MONEY },\n variants: { nodes: [{ id: 'string', price: MONEY, selectedOptions: ['object'] }] },\n} as const satisfies ShapeOf<Product>;\n\n/** A page of products, with the `pageInfo` paging reads. */\nexport const PRODUCT_CONNECTION = {\n nodes: [PRODUCT],\n pageInfo: { hasNextPage: 'boolean' },\n} as const satisfies ShapeOf<ProductConnection>;\n\n/** True for a value with the structure of `PRODUCT` and a non-empty id and handle. */\nexport const isProductShape = (value: unknown): value is Product =>\n field(value, 'id') !== '' && field(value, 'handle') !== '' && matches(value, PRODUCT);\n\n/**\n * A node a lookup by gid answers: a product, or a node of another type, for which `... on Product` selects no field\n * (no `id`).\n */\nexport const PRODUCT_NODE = (node: unknown): node is Product | { id?: undefined } =>\n isRecord(node) && (node['id'] === undefined || matches(node, PRODUCT));\n", "/**\n * The structure of Tether's cart fragment, checked on a Storefront answer before it is used as a `ShopifyCart`. The\n * cart's render, its totals and the cart API walk its lines and prices without checking each step. Text fields are\n * written as text, so they need no check.\n */\nimport { MONEY } from '../product-shape.ts';\nimport type { ShapeOf } from '../client.ts';\nimport type { ShopifyCart, ShopifyCartLine } from './store.ts';\n\nconst LINE = {\n id: 'string',\n quantity: 'number',\n attributes: ['object'],\n cost: { totalAmount: MONEY, subtotalAmount: MONEY, amountPerQuantity: MONEY },\n merchandise: { id: 'string', selectedOptions: ['object'], product: 'object' },\n} as const satisfies ShapeOf<ShopifyCartLine>;\n\nexport const CART = {\n id: 'string',\n checkoutUrl: 'string',\n totalQuantity: 'number',\n attributes: ['object'],\n discountCodes: ['object'],\n cost: { totalAmount: MONEY, subtotalAmount: MONEY },\n lines: { nodes: [LINE] },\n} as const satisfies ShapeOf<ShopifyCart>;\n", "/**\n * The cart drawer: every `[tether-cart-drawer]` on the page opens and closes together, as a modal dialog. Focus moves\n * into the open cart, Tab stays inside it and focus returns on close (`cartFocus`). A page without a drawer (a cart\n * built in React, Svelte or Vue) still has an open state, for `Tether.cart.subscribe`.\n */\nimport { cfg } from '../config.ts';\nimport { sel, get, qsa, emit } from '../dom.ts';\n\nconst FOCUSABLE =\n 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\n/** Long enough for the drawer's open transition to make its content visible: hidden elements can't take focus. */\nconst FOCUS_AFTER_OPEN_MS = 60;\n\n/** The open state of a page without a drawer element. */\nlet openWithoutDrawer = false;\n\n/** Where focus was before the cart opened. */\nlet returnFocus: HTMLElement | null = null;\n\nconst isFocusable = (el: Element | null): el is HTMLElement => el !== null && 'focus' in el;\n\n/** @returns The visible focusable elements inside `drawer`, in document order. */\nconst focusables = (drawer: ParentNode): HTMLElement[] =>\n qsa(drawer, FOCUSABLE).filter((el) => el.offsetParent !== null || el === document.activeElement);\n\n/** Every cart drawer on the page. */\nexport const drawers = (): HTMLElement[] => qsa(document, sel('cart-drawer'));\n\nconst isOpenDrawer = (drawer: HTMLElement) => drawer.classList.contains(cfg.cartDrawerOpenClass);\n\n/** True while a drawer, or the drawer-less cart, is open. */\nexport function isOpen(): boolean {\n const all = drawers();\n return all.length ? all.some(isOpenDrawer) : openWithoutDrawer;\n}\n\n/**\n * `aria-expanded` on the open buttons, and dialog semantics on the drawers unless their markup has its own. A closed\n * drawer is hidden from screen readers and, with `cartFocus`, out of the Tab order (inert), like a closed dialog.\n */\nexport function syncDrawerAria(open: boolean): void {\n qsa(document, sel('cart-open')).forEach((button) => {\n button.setAttribute('aria-expanded', String(open));\n });\n drawers().forEach((drawer) => {\n if (!drawer.hasAttribute('role')) drawer.setAttribute('role', 'dialog');\n if (!drawer.hasAttribute('aria-label') && !drawer.hasAttribute('aria-labelledby')) {\n drawer.setAttribute('aria-label', get(drawer, 'cart-label') || 'Cart');\n }\n drawer.setAttribute('aria-modal', String(open));\n if (isOpenDrawer(drawer)) return;\n drawer.setAttribute('aria-hidden', 'true');\n if (cfg.cartFocus) drawer.inert = true;\n });\n}\n\nfunction focusInto(drawer: HTMLElement) {\n const target = focusables(drawer)[0] ?? drawer;\n if (target === drawer && !drawer.hasAttribute('tabindex')) drawer.tabIndex = -1;\n target.focus({ preventScroll: true });\n}\n\n/** Opens every drawer except `tether-cart-drawer=\"manual\"` ones, which the site opens itself. */\nexport function openCart(): void {\n if (isOpen()) return;\n openWithoutDrawer = true;\n const opened = drawers().filter((drawer) => get(drawer, 'cart-drawer') !== 'manual');\n opened.forEach((drawer) => {\n drawer.inert = false;\n drawer.classList.add(cfg.cartDrawerOpenClass);\n drawer.setAttribute('aria-hidden', 'false');\n });\n syncDrawerAria(true);\n const [first] = opened;\n if (cfg.cartFocus && first) {\n returnFocus = isFocusable(document.activeElement) ? document.activeElement : null;\n setTimeout(() => {\n focusInto(first);\n }, FOCUS_AFTER_OPEN_MS);\n }\n cfg.onCartOpen?.(opened);\n emit('cart-toggled', { open: true });\n}\n\nexport function closeCart(): void {\n if (!isOpen()) return;\n openWithoutDrawer = false;\n const all = drawers();\n const hadFocus = all.some((drawer) => drawer.contains(document.activeElement));\n all.forEach((drawer) => {\n drawer.classList.remove(cfg.cartDrawerOpenClass);\n drawer.setAttribute('aria-hidden', 'true');\n if (cfg.cartFocus) drawer.inert = true;\n });\n syncDrawerAria(false);\n if (hadFocus && returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });\n returnFocus = null;\n cfg.onCartClose?.(all);\n emit('cart-toggled', { open: false });\n}\n\nexport function toggleCart(): void {\n if (isOpen()) closeCart();\n else openCart();\n}\n\n/**\n * Keeps Tab inside the open drawer (`cartFocus`): from the last focusable element it wraps to the first, with Shift\n * from the first to the last, and from outside the drawer it goes in. For a document `keydown` listener.\n */\nexport function trapFocus(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || !cfg.cartFocus || !isOpen()) return;\n const drawer = drawers().find(isOpenDrawer);\n if (!drawer) return;\n const inside = focusables(drawer);\n const first = inside[0];\n const last = inside.at(-1);\n if (!first || !last) return;\n const active = document.activeElement;\n const leaving = event.shiftKey ? active === first : active === last;\n if (drawer.contains(active) && !leaving) return;\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n}\n", "/**\n * Product data for the browser script. Products asked for in the same tick go out as one request, and every product\n * is cached for the life of the page, which survives Barba/Swup page swaps. Products the server rendered\n * (`tether/server` `hydrate()`) are used at once and refreshed once in the background.\n */\nimport { cfg, log } from './config.ts';\nimport { gql, matches, orNull, shaped, TetherError } from './client.ts';\nimport { productFragment, collectionFields } from './fragments.ts';\nimport { productKey } from './shopify-values.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { PRODUCT, PRODUCT_CONNECTION, PRODUCT_NODE } from './product-shape.ts';\nimport type { Product, Image } from './fragments.ts';\nimport type { QueryText } from './client.ts';\nimport type { ProductConnection } from './product-shape.ts';\n\n/**\n * A product request that missed is remembered this long, so one page asking twice sends one request, and a product\n * published in Shopify meanwhile shows up on the next visit.\n */\nconst MISS_CACHE_MS = 10_000;\n/** A product loaded live this recently is newer than a server-rendered copy of it, which is then ignored. */\nconst FRESH_MS = 60_000;\n\ninterface Waiter {\n resolve: (product: Product) => void;\n reject: (error: unknown) => void;\n}\n\n/** Handle or gid to the product, loaded or loading. */\nconst productCache = new Map<string, Promise<Product>>();\nconst waiting = new Map<string, Waiter>();\n/** Keys of products that came from the server and weren't refreshed yet. */\nconst stale = new Set<string>();\n/** Handle or gid to when it last loaded live, in ms since the epoch. */\nconst freshAt = new Map<string, number>();\n/** Keys for the next batch. */\nconst pending: string[] = [];\nlet flushScheduled = false;\n/**\n * Bumped by `clearProductCache()`: responses to requests sent before that (in the old market, after a country\n * switch) still reach their callers but never refill the cache.\n */\nlet generation = 0;\n\nconst isGid = (key: string) => key.startsWith('gid://');\n\n/** Accepts a handle, numeric id, gid or a full product URL. */\nexport const normalizeProductKey = productKey;\n\n/** Puts a product the page already has (from a collection query) in the cache, under its handle and its id. */\nexport function primeProduct(product: Product | null | undefined): void {\n if (!product) return;\n const loaded = Promise.resolve(product);\n productCache.set(product.handle, loaded);\n productCache.set(product.id, loaded);\n}\n\n/**\n * @param rawKey A handle, numeric id, gid or product URL.\n * @throws {TetherError} `NOT_FOUND` (rejected) for an empty key or a product the store doesn't have.\n */\nexport function getProduct(rawKey: string | number | null | undefined): Promise<Product> {\n const key = normalizeProductKey(rawKey);\n if (!key) return Promise.reject(new TetherError('empty product key', { code: 'NOT_FOUND' }));\n const cached = productCache.get(key);\n if (cached) return cached;\n\n const loading = new Promise<Product>((resolve, reject) => {\n waiting.set(key, { resolve, reject });\n pending.push(key);\n if (!flushScheduled) {\n flushScheduled = true;\n setTimeout(() => void flush(), 0);\n }\n });\n productCache.set(key, loading);\n return loading;\n}\n\n/**\n * @param operation An operation that spreads `...TetherProduct`.\n * @returns The operation with the product fragment (which starts on its own line), written for what the token may\n * read.\n */\nconst withProductFragment =\n (operation: string): QueryText =>\n (access) =>\n `${operation}${productFragment(cfg, access)}`;\n\n/**\n * @param keys Handles and gids.\n * @returns One query for all of them: gids through `nodes(ids:)` as `byId`, each handle as its own alias `h<index>`.\n */\nfunction batchQuery(keys: string[]): { query: QueryText; variables: Record<string, unknown>; handles: string[] } {\n const ids = keys.filter(isGid);\n const handles = keys.filter((key) => !isGid(key));\n const definitions: string[] = [];\n const selections: string[] = [];\n const variables: Record<string, unknown> = {};\n if (ids.length) {\n definitions.push('$ids: [ID!]!');\n variables['ids'] = ids;\n selections.push(`byId:nodes(ids:$ids){... on Product{...TetherProduct}}`);\n }\n handles.forEach((handle, index) => {\n definitions.push(`$h${index}: String!`);\n variables[`h${index}`] = handle;\n selections.push(`h${index}:product(handle:$h${index}){...TetherProduct}`);\n });\n const query = withProductFragment(`query TetherProducts(${definitions.join(',')}){${selections.join(' ')}}`);\n return { query, variables, handles };\n}\n\nfunction settleWaiting(key: string, settle: (waiter: Waiter) => void): void {\n const waiter = waiting.get(key);\n waiting.delete(key);\n if (waiter) settle(waiter);\n}\n\nfunction forgetMissLater(key: string, batchGeneration: number): void {\n if (batchGeneration !== generation) return;\n const miss = productCache.get(key);\n setTimeout(() => {\n if (productCache.get(key) === miss) productCache.delete(key);\n }, MISS_CACHE_MS);\n}\n\n/** A node of a type other than Product, for which `... on Product` selects no field. */\ninterface OtherNode {\n id?: undefined;\n}\n/** The answer to `batchQuery`: `byId` for the gids, `h<index>` for each handle. */\ntype BatchData = Partial<Record<`h${number}`, Product | null>> & { byId?: (Product | OtherNode | null)[] };\n\nfunction isBatchData(data: unknown): data is BatchData {\n if (!isRecord(data)) return false;\n const { byId = [], ...byHandle } = data;\n return matches(byId, [orNull(PRODUCT_NODE)]) && Object.values(byHandle).every(orNull(PRODUCT));\n}\n\nasync function flush() {\n flushScheduled = false;\n const batchGeneration = generation;\n const keys = pending.splice(0);\n if (!keys.length) return;\n const { query, variables, handles } = batchQuery(keys);\n log('fetch products', keys);\n\n let data;\n try {\n data = await gql(query, variables, isBatchData);\n } catch (error) {\n keys.forEach((key) => {\n settleWaiting(key, (waiter) => {\n if (batchGeneration === generation) productCache.delete(key);\n waiter.reject(error);\n });\n });\n return;\n }\n\n const found = new Map<string, Product>();\n for (const product of data.byId ?? []) if (product?.id) found.set(product.id, product);\n handles.forEach((handle, index) => {\n const product = data[`h${index}`];\n if (product) found.set(handle, product);\n });\n\n const now = Date.now();\n keys.forEach((key) => {\n settleWaiting(key, (waiter) => {\n const product = found.get(key);\n if (!product) {\n forgetMissLater(key, batchGeneration);\n waiter.reject(new TetherError(`Product not found: ${key}`, { code: 'NOT_FOUND' }));\n return;\n }\n if (batchGeneration === generation) {\n primeProduct(product);\n freshAt.set(product.handle, now);\n freshAt.set(product.id, now);\n }\n waiter.resolve(product);\n });\n });\n}\n\n/** Forgets every product, for a new market: requests already sent no longer fill the cache. */\nexport function clearProductCache(): void {\n productCache.clear();\n generation += 1;\n stale.clear();\n freshAt.clear();\n}\n\n/**\n * Uses products the server rendered, unless the page loaded the same product live in the last `FRESH_MS` (an earlier\n * page of a View Transition visit), whose copy is newer.\n */\nexport function hydrateProducts(products: readonly Product[] | null | undefined): void {\n for (const product of products ?? []) {\n if (!product.id || !product.handle) continue;\n const loadedAgo = Date.now() - (freshAt.get(product.handle) ?? 0);\n if (loadedAgo < FRESH_MS && productCache.has(product.handle)) continue;\n primeProduct(product);\n stale.add(product.handle);\n stale.add(product.id);\n }\n}\n\n/** @returns True once per server-rendered product: the caller then refreshes it. */\nexport function takeStale(rawKey: string | number | null | undefined): boolean {\n const key = normalizeProductKey(rawKey);\n if (!key || !stale.has(key)) return false;\n stale.delete(key);\n // The handle and the id point to the same product: one refresh covers both.\n void productCache.get(key)?.then((product) => {\n stale.delete(product.handle);\n stale.delete(product.id);\n }, failureIsReportedByTheRender);\n return true;\n}\n\n/**\n * The cached promise is the one the product's render awaits, and the render reports its failure: the stale cleanup\n * above has nothing left to do for a product that did not load.\n */\nfunction failureIsReportedByTheRender() {\n // Nothing to clean: see above.\n}\n\n/** Loads this product again, past the cache. Products refreshed in the same tick go out as one request. */\nexport function refreshProduct(key: string | number): Promise<Product> {\n const normalized = normalizeProductKey(key);\n if (normalized) productCache.delete(normalized);\n return getProduct(key);\n}\n\nexport interface PageInfo {\n hasNextPage: boolean;\n endCursor: string | null;\n}\nexport interface Collection {\n id: string;\n handle: string;\n title: string;\n description: string;\n descriptionHtml: string;\n image: Image | null;\n seo: { title: string | null; description: string | null };\n}\nexport interface ProductPage {\n collection: Collection | null;\n products: Product[];\n pageInfo: PageInfo;\n}\nexport interface ProductQuery {\n /** A collection handle; without one, a search. */\n collection?: string;\n /** Storefront search syntax, for a search. */\n query?: string;\n first?: number;\n /** The cursor of the page before. */\n after?: string | null;\n /** `ProductCollectionSortKeys` for a collection, `ProductSortKeys` for a search. */\n sortKey?: string;\n reverse?: boolean;\n /** `ProductFilter` values, for a collection. */\n filters?: unknown[];\n}\n\n/** A collection's answer: `null` for a handle the store doesn't have. Its other fields are written as text. */\nexport const isCollectionAnswer = shaped<{ collection: (Collection & { products: ProductConnection }) | null }>({\n collection: orNull({ handle: 'string', products: PRODUCT_CONNECTION }),\n});\nexport const isSearchAnswer = shaped<{ products: ProductConnection }>({ products: PRODUCT_CONNECTION });\nexport const isRecommendationsAnswer = shaped<{ productRecommendations: Product[] | null }>({\n productRecommendations: orNull([PRODUCT]),\n});\n\nconst COLLECTION_QUERY = `query TetherCollection($handle:String!,$first:Int!,$after:String,$sortKey:ProductCollectionSortKeys,$reverse:Boolean,$filters:[ProductFilter!]){collection(handle:$handle){${collectionFields()} products(first:$first,after:$after,sortKey:$sortKey,reverse:$reverse,filters:$filters){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}}`;\n\nconst SEARCH_QUERY = `query TetherSearch($first:Int!,$after:String,$query:String,$sortKey:ProductSortKeys,$reverse:Boolean){products(first:$first,after:$after,query:$query,sortKey:$sortKey,reverse:$reverse){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}`;\n\n/**\n * A page of products from a collection (by handle) or from a search. Every product on it is cached.\n *\n * @throws {TetherError} `NOT_FOUND` for a collection the store doesn't have.\n */\nexport async function getProducts({\n collection,\n query,\n first = cfg.collectionFirst,\n after = null,\n sortKey,\n reverse = false,\n filters,\n}: ProductQuery = {}): Promise<ProductPage> {\n if (collection) {\n const data = await gql(\n withProductFragment(COLLECTION_QUERY),\n {\n handle: collection,\n first,\n after,\n sortKey: sortKey || undefined,\n reverse,\n filters: filters ?? undefined,\n },\n isCollectionAnswer,\n );\n if (!data.collection) throw new TetherError(`Collection not found: ${collection}`, { code: 'NOT_FOUND' });\n const { products, ...rest } = data.collection;\n products.nodes.forEach(primeProduct);\n return { collection: rest, products: products.nodes, pageInfo: products.pageInfo };\n }\n\n const data = await gql(\n withProductFragment(SEARCH_QUERY),\n { first, after, query: query || undefined, sortKey: sortKey || undefined, reverse },\n isSearchAnswer,\n );\n data.products.nodes.forEach(primeProduct);\n return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };\n}\n\n/**\n * Shopify's recommendations for a product. Every product in them is cached.\n *\n * @param product A product, or its handle, id or gid.\n */\nexport async function getRecommendations(\n product: string | number | { id: string } | null | undefined,\n intent: 'RELATED' | 'COMPLEMENTARY' = 'RELATED',\n): Promise<Product[]> {\n const key = normalizeProductKey(typeof product === 'object' ? product?.id : product);\n const byId = key?.startsWith('gid://') ?? false;\n const data = await gql(\n withProductFragment(\n `query TetherRecs(${byId ? '$id: ID!' : '$handle: String!'},$intent:ProductRecommendationIntent){productRecommendations(${byId ? 'productId: $id' : 'productHandle: $handle'},intent:$intent){...TetherProduct}}`,\n ),\n byId ? { id: key, intent } : { handle: key, intent },\n isRecommendationsAnswer,\n );\n const list = data.productRecommendations ?? [];\n list.forEach(primeProduct);\n return list;\n}\n", "/**\n * The DOM writes of a product scope that depend on the product alone, once per product: its binds, the option and\n * plan lists and the options of its selects. `variant-render.ts` writes what follows the visitor's choice.\n */\nimport {\n attr,\n sel,\n get,\n has,\n qsa,\n formatMoney,\n formatDate,\n setText,\n setImage,\n setStoreHtml,\n show,\n repeat,\n hasTemplate,\n sameImage,\n productUrl,\n} from './dom.ts';\nimport type { Product } from './fragments.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\n\n/** The product scope as its renders see it. */\nexport interface RenderScope {\n readonly root: HTMLElement;\n readonly product: Product;\n readonly plans: PlanInGroup[];\n /** Matches in this scope, the root included. */\n $: (selector: string) => HTMLElement[];\n /** The product's option name for one written in the markup. */\n optName: (authored: string | null) => string;\n}\n\n/** `tether-bind=\"image:2\"`: the second product image. */\nconst NTH_IMAGE_BIND = /^image:(\\d+)$/;\nconst METAFIELD_BIND = 'metafield:';\nconst MULTI_LINE_METAFIELD = /^(rich_text|multi_line_text)_field$/;\n/** `tether-plan-list` values that list every plan, not one group: Webflow's Designer can't always leave it empty. */\nconst ALL_PLAN_GROUPS = ['', 'true', 'all', '*'];\n\nexport const isSelect = (el: Element): el is HTMLSelectElement => el.tagName === 'SELECT';\n\n/** @returns The `<select>` elements in the scope that match `selector`. */\nexport const selectsIn = (scope: RenderScope, selector: string): HTMLSelectElement[] =>\n scope.$(`select${selector}`).filter(isSelect);\n\n/** Leaves a server-rendered gallery with the same images as it is. */\nfunction renderGallery(el: HTMLElement, product: Product): void {\n const images = product.images.nodes;\n const rendered = [...el.children].map((node) => (node.tagName === 'IMG' ? node : node.querySelector('img')));\n const same =\n !hasTemplate(el) &&\n rendered.length === images.length &&\n rendered.every((img, index) => sameImage(img?.getAttribute('src'), images[index]?.url));\n if (same) return;\n repeat(el, images, (node, image) => {\n const target = node.matches(`img,${sel('bind', 'image')}`) ? node : (qsa(node, 'img')[0] ?? node);\n setImage(target, image.url, image.altText || product.title);\n });\n}\n\n/** @param ref `namespace.key` */\nfunction renderMetafield(el: HTMLElement, product: Product, ref: string): void {\n const dot = ref.indexOf('.');\n const namespace = ref.slice(0, dot);\n const key = ref.slice(dot + 1);\n const metafield = (product.metafields ?? []).find((field) => field?.namespace === namespace && field.key === key);\n if (!metafield) {\n setText(el, '');\n return;\n }\n const image = metafield.reference?.image;\n if (image) {\n setImage(el, image.url, image.altText);\n return;\n }\n if (MULTI_LINE_METAFIELD.test(metafield.type)) el.style.whiteSpace = 'pre-line';\n setText(el, metafield.value);\n}\n\n/** The product binds written as text. */\nconst PRODUCT_TEXTS = new Map<string, (product: Product) => string>(\n Object.entries({\n title: (product) => product.title,\n 'description-text': (product) => product.description,\n vendor: (product) => product.vendor,\n type: (product) => product.productType,\n handle: (product) => product.handle,\n 'published-at': (product) => formatDate(product.publishedAt),\n 'min-price': (product) => formatMoney(product.priceRange.minVariantPrice),\n 'max-price': (product) => formatMoney(product.priceRange.maxVariantPrice),\n }),\n);\n\nfunction renderTags(el: HTMLElement, product: Product): void {\n const exclude = (el.getAttribute('exclude') ?? '')\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean);\n const tags = product.tags.filter((tag) => !exclude.includes(tag));\n repeat(el, tags, (node, tag) => {\n setText(node, tag);\n });\n}\n\n/**\n * A `tether-bind` of the product itself; binds of the variant are left to `writeVariant`.\n */\nfunction renderProductBind(el: HTMLElement, key: string, product: Product): void {\n const text = PRODUCT_TEXTS.get(key);\n if (text) {\n setText(el, text(product));\n return;\n }\n switch (key) {\n case 'description':\n setStoreHtml(el, product.descriptionHtml);\n break;\n case 'featured-image':\n setImage(el, product.featuredImage?.url, product.featuredImage?.altText || product.title);\n break;\n case 'url':\n case 'link': {\n const url = productUrl(product.handle, product.id);\n if (url) el.setAttribute('href', url);\n if (key === 'url' && !el.firstElementChild && !el.textContent.trim()) setText(el, product.title);\n break;\n }\n case 'images':\n renderGallery(el, product);\n break;\n case 'tags':\n renderTags(el, product);\n break;\n default:\n renderIndexedBind(el, key, product);\n }\n}\n\n/** `image:<n>` (falling back to the featured image) and `metafield:<namespace.key>`. */\nfunction renderIndexedBind(el: HTMLElement, key: string, product: Product): void {\n const nth = NTH_IMAGE_BIND.exec(key);\n if (nth) {\n const image = product.images.nodes[Number(nth[1]) - 1] ?? product.featuredImage;\n setImage(el, image?.url, image?.altText || product.title);\n } else if (key.startsWith(METAFIELD_BIND)) {\n renderMetafield(el, product, key.slice(METAFIELD_BIND.length));\n }\n}\n\nfunction renderOptionLists(scope: RenderScope): void {\n const { product } = scope;\n scope.$(sel('option-list')).forEach((container) => {\n const name = scope.optName(get(container, 'option-list'));\n const option = product.options.find((candidate) => candidate.name === name);\n if (!option || (option.optionValues.length <= 1 && has(container, 'hide-single'))) {\n show(container, false);\n return;\n }\n repeat(container, option.optionValues, (node, value) => {\n node.setAttribute(attr('option-value'), value.name);\n const text = qsa(node, sel('bind', 'option-value'))[0];\n if (text) setText(text, value.name);\n else if (!node.firstElementChild) setText(node, value.name);\n const swatchEl = qsa(node, sel('bind', 'swatch'))[0];\n const { swatch } = value;\n if (!swatchEl || !swatch) return;\n if (swatch.color) swatchEl.style.backgroundColor = swatch.color;\n if (swatch.image?.previewImage) setImage(swatchEl, swatch.image.previewImage.url, value.name);\n });\n });\n}\n\nfunction renderSelects(scope: RenderScope): void {\n const { product } = scope;\n selectsIn(scope, sel('option')).forEach((select) => {\n const option = product.options.find((candidate) => candidate.name === scope.optName(get(select, 'option')));\n if (!option) {\n show(select, false);\n return;\n }\n select.replaceChildren(...option.optionValues.map((value) => new Option(value.name, value.name)));\n });\n selectsIn(scope, sel('variant-select')).forEach((select) => {\n select.replaceChildren(...product.variants.nodes.map((variant) => new Option(variant.title, variant.id)));\n });\n selectsIn(scope, sel('plan')).forEach((select) => {\n const plans = scope.plans.map((plan) => new Option(plan.name, plan.id));\n const oneTime = !product.requiresSellingPlan && get(select, 'plan') !== 'subscription-only';\n const oneTimeLabel = select.getAttribute(attr('one-time-label')) || 'One-time purchase';\n select.replaceChildren(...(oneTime ? [new Option(oneTimeLabel, '')] : []), ...plans);\n });\n}\n\nfunction renderPlanLists(scope: RenderScope): void {\n scope.$(sel('plan-list')).forEach((container) => {\n // The value is a selling plan group's name, or one of ALL_PLAN_GROUPS.\n const raw = (get(container, 'plan-list') ?? '').trim();\n const group = ALL_PLAN_GROUPS.includes(raw.toLowerCase()) ? null : raw;\n const plans = scope.plans.filter((plan) => !group || plan.group.name === group);\n repeat(container, plans, (node, plan) => {\n node.setAttribute(attr('plan-value'), plan.id);\n if (!node.firstElementChild && !node.querySelector(sel('plan-bind'))) setText(node, plan.name);\n });\n });\n}\n\n/** Writes what depends on the product alone: its binds, option and plan lists and the selects' options. */\nexport function writeProduct(scope: RenderScope): void {\n scope.$(sel('bind')).forEach((el) => {\n renderProductBind(el, get(el, 'bind') ?? '', scope.product);\n });\n renderOptionLists(scope);\n renderSelects(scope);\n renderPlanLists(scope);\n}\n", "/**\n * Shows the cart everywhere on the page: counts, totals, `tether-show=\"cart-*\"` conditions, the note, the discount\n * codes and the `[tether-cart-lines]` repeaters. It renders what it is given (a `CartView`) and holds no state, so\n * every render shows the cart as the store has it at that moment.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { getProduct } from '../loader.ts';\nimport { allPlans } from '../selling-plans.ts';\nimport { isSelect } from '../product-render.ts';\nimport { isGreaterAmount } from '../amounts.ts';\nimport {\n attr,\n sel,\n get,\n qsa,\n formatMoney,\n setText,\n setTextTracked,\n setImage,\n show,\n repeat,\n repeatKeyed,\n productUrl,\n notifyLayoutChanged,\n} from '../dom.ts';\nimport { isOpen, syncDrawerAria } from './drawer.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { Product } from '../fragments.ts';\nimport type { ShopifyCart, ShopifyCartLine, PendingQuantity } from './store.ts';\n\n/** What a render shows. */\ninterface CartView {\n cart: ShopifyCart | null;\n warnings: readonly CartWarning[];\n /** Line id \u2192 the quantity quick clicks asked for. */\n pending: ReadonlyMap<string, PendingQuantity>;\n /** The last load or change failed (`tether-show=\"cart-error\"`). */\n failed: boolean;\n}\n\n/** Shopify's title of a variant of a product without options. */\nconst DEFAULT_TITLE = 'Default Title';\n\n/** @returns The quantity to show: the one asked for while an update is on its way. */\nexport const quantityOf = (line: ShopifyCartLine, pending: ReadonlyMap<string, PendingQuantity>): number =>\n pending.get(line.id)?.quantity ?? line.quantity;\n\nconst numericId = (gid: string) => gid.split('/').pop() ?? gid;\n\n/** @returns The product page, with the line's variant and plan selected. */\nfunction lineUrl(line: ShopifyCartLine): string | null {\n const variant = line.merchandise;\n const base = productUrl(variant.product.handle, variant.product.id);\n if (!base) return null;\n const query = new URLSearchParams();\n query.set('variant', numericId(variant.id));\n const planId = line.sellingPlanAllocation?.sellingPlan.id;\n if (planId) query.set('selling_plan', numericId(planId));\n return `${base}${base.includes('?') ? '&' : '?'}${query.toString()}`;\n}\n\ntype LineText = (line: ShopifyCartLine, view: CartView) => string | number;\n\nconst productTitle: LineText = (line) => line.merchandise.product.title;\n\n/** The text of each `tether-line` key. */\nconst LINE_TEXT: Record<string, LineText> = {\n title: productTitle,\n 'product-title': productTitle,\n 'variant-title': (line) => (line.merchandise.title === DEFAULT_TITLE ? '' : line.merchandise.title),\n options: (line) =>\n line.merchandise.selectedOptions\n .filter((option) => option.value !== DEFAULT_TITLE)\n .map((option) => `${option.name}: ${option.value}`)\n .join(', '),\n vendor: (line) => line.merchandise.product.vendor,\n sku: (line) => line.merchandise.sku ?? '',\n price: (line) => formatMoney(line.cost.amountPerQuantity),\n 'compare-at-price': (line) => formatMoney(line.cost.compareAtAmountPerQuantity ?? line.merchandise.compareAtPrice),\n 'line-total': (line) => formatMoney(line.cost.totalAmount),\n 'line-subtotal': (line) => formatMoney(line.cost.subtotalAmount),\n quantity: (line, view) => quantityOf(line, view.pending),\n plan: (line) => line.sellingPlanAllocation?.sellingPlan.name ?? '',\n 'plan-description': (line) => line.sellingPlanAllocation?.sellingPlan.description ?? '',\n components: (line) =>\n (line.lineComponents ?? [])\n .map((component) => `${component.quantity} \u00D7 ${component.merchandise.product.title}`)\n .join(', '),\n};\n\nconst ATTRIBUTE_KEY = 'attribute:';\n\n/**\n * @param key The `tether-line` value.\n * @returns The text for `key`, `undefined` for a key Tether doesn't know.\n */\nfunction lineValue(line: ShopifyCartLine, key: string, view: CartView): string | number | undefined {\n if (Object.hasOwn(LINE_TEXT, key)) return LINE_TEXT[key]?.(line, view);\n if (!key.startsWith(ATTRIBUTE_KEY)) return undefined;\n const name = key.slice(ATTRIBUTE_KEY.length);\n return line.attributes.find((attribute) => attribute.key === name)?.value ?? '';\n}\n\n/**\n * Fills a `<select tether-line-plan>` with the product's plans (and one-time purchase, when the product allows it and\n * the select isn't `subscription-only`), keeping the options when they haven't changed.\n */\nfunction fillPlanSelect(select: HTMLSelectElement, line: ShopifyCartLine, product: Product) {\n const plans = allPlans(product);\n const allowOneTime = !product.requiresSellingPlan && get(select, 'line-plan') !== 'subscription-only';\n const oneTime = { value: '', label: select.getAttribute(attr('one-time-label')) || 'One-time purchase' };\n const choices = [...(allowOneTime ? [oneTime] : []), ...plans.map((plan) => ({ value: plan.id, label: plan.name }))];\n const signature = choices.map((choice) => choice.value).join('|');\n if (select.dataset['tetherSig'] !== signature) {\n select.replaceChildren(\n ...choices.map((choice) => {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n return option;\n }),\n );\n select.dataset['tetherSig'] = signature;\n }\n select.value = line.sellingPlanAllocation?.sellingPlan.id ?? '';\n show(select, plans.length > 0);\n}\n\nfunction renderLineState(node: HTMLElement, line: ShopifyCartLine, view: CartView) {\n const isSubscription = Boolean(line.sellingPlanAllocation);\n node.classList.toggle('is-subscription', isSubscription);\n // Shopify's note about this line, such as less stock than asked.\n const warning = view.warnings.find((candidate) => candidate.target === line.id);\n node.classList.toggle('is-warning', Boolean(warning));\n const pending = view.pending.has(line.id);\n node.classList.toggle('is-busy', pending);\n if (pending) node.setAttribute('aria-busy', 'true');\n else node.removeAttribute('aria-busy');\n qsa(node, sel('line', 'warning')).forEach((el) => {\n setTextTracked(el, warning?.message ?? '');\n });\n const conditions = new Map([\n ['subscription', isSubscription],\n ['one-time', !isSubscription],\n ['on-sale', isGreaterAmount(line.cost.compareAtAmountPerQuantity?.amount, line.cost.amountPerQuantity.amount)],\n ['has-components', (line.lineComponents ?? []).length > 0],\n ['warning', Boolean(warning)],\n ]);\n qsa(node, sel('line-show')).forEach((el) => {\n const key = get(el, 'line-show');\n const visible = key === null ? undefined : conditions.get(key);\n if (visible !== undefined) show(el, visible);\n });\n}\n\nfunction renderLine(node: HTMLElement, line: ShopifyCartLine, view: CartView) {\n const variant = line.merchandise;\n node.setAttribute(attr('line-id'), line.id);\n qsa(node, sel('line')).forEach((el) => {\n const key = get(el, 'line') ?? '';\n if (key === 'image') {\n setImage(el, variant.image?.url ?? variant.product.featuredImage?.url, variant.product.title);\n return;\n }\n if (key === 'url') {\n const url = lineUrl(line);\n if (url) el.setAttribute('href', url);\n return;\n }\n const value = lineValue(line, key, view);\n if (value !== undefined) setTextTracked(el, value);\n });\n renderLineState(node, line, view);\n const planSelects = qsa(node, sel('line-plan')).filter(isSelect);\n planSelects.forEach((select) => {\n getProduct(variant.product.handle)\n .then((product) => {\n fillPlanSelect(select, line, product);\n })\n .catch((error: unknown) => {\n reportError(error, 'line plan options');\n });\n });\n}\n\nfunction renderConditions(cart: ShopifyCart | null, view: CartView) {\n const count = cart?.totalQuantity ?? 0;\n const hasSubscription = (cart?.lines.nodes ?? []).some((line) => line.sellingPlanAllocation);\n const conditions = new Map([\n ['cart-empty', count === 0],\n ['cart-filled', count > 0],\n ['cart-has-subscription', hasSubscription],\n ['cart-no-subscription', !hasSubscription],\n ['cart-has-discount', (cart?.discountCodes ?? []).some((code) => code.applicable)],\n ['cart-error', view.failed],\n ['cart-warning', view.warnings.length > 0],\n ]);\n qsa(document, sel('show')).forEach((el) => {\n const key = get(el, 'show');\n const visible = key === null ? undefined : conditions.get(key);\n if (visible === undefined || el.closest(sel('product'))) return;\n show(el, visible);\n });\n qsa(document, sel('checkout')).forEach((el) => {\n el.classList.toggle('is-disabled', !count);\n el.setAttribute('aria-disabled', String(!count));\n });\n}\n\nfunction renderFields(cart: ShopifyCart | null) {\n qsa(document, `textarea${sel('cart-note')},input${sel('cart-note')}`).forEach((el) => {\n // The visitor may be typing in it.\n if (document.activeElement !== el) setText(el, cart?.note ?? '');\n });\n qsa(document, sel('cart-discounts')).forEach((container) => {\n repeat(container, cart?.discountCodes ?? [], (node, discount) => {\n node.setAttribute(attr('discount-code'), discount.code);\n const target = node.querySelector(sel('discount', 'code')) ?? (node.firstElementChild ? null : node);\n if (target) setText(target, discount.code);\n node.classList.toggle('is-inapplicable', !discount.applicable);\n });\n });\n}\n\nexport function renderCart(view: CartView): void {\n const { cart } = view;\n const money = (key: 'subtotalAmount' | 'totalAmount' | 'checkoutChargeAmount') =>\n cart ? formatMoney(cart.cost[key]) : '';\n\n syncDrawerAria(isOpen());\n qsa(document, sel('cart-count')).forEach((el) => {\n setTextTracked(el, cart?.totalQuantity ?? 0);\n });\n qsa(document, sel('cart-subtotal')).forEach((el) => {\n setTextTracked(el, money('subtotalAmount'));\n });\n qsa(document, sel('cart-total')).forEach((el) => {\n setTextTracked(el, money('totalAmount'));\n });\n qsa(document, sel('cart-checkout-charge')).forEach((el) => {\n setTextTracked(el, money('checkoutChargeAmount'));\n });\n renderConditions(cart, view);\n qsa(document, sel('cart-warning')).forEach((el) => {\n setTextTracked(el, [...new Set(view.warnings.map((warning) => warning.message))].join(' '));\n });\n renderFields(cart);\n qsa(document, sel('cart-lines')).forEach((container) => {\n repeatKeyed(\n container,\n cart?.lines.nodes ?? [],\n (line) => line.id,\n (node, line) => {\n renderLine(node, line, view);\n },\n { onEnter: cfg.onLineEnter, onLeave: cfg.onLineLeave },\n );\n });\n if (cart) notifyLayoutChanged('cart');\n}\n\n/** Shows the count a saved cart had on the last page in the cart counts the page left at 0, until it has loaded. */\nexport function showSavedCount(count: number): void {\n qsa(document, sel('cart-count')).forEach((el) => {\n if (/^\\s*0?\\s*$/.test(el.textContent)) el.textContent = String(count);\n });\n}\n", "/**\n * The browser's `localStorage` and `sessionStorage`, which may be blocked: accessing them throws in private mode, in\n * sandboxed frames and where the visitor blocks site data, and writing throws when they are full. Tether then keeps\n * nothing between pages: a read finds nothing, and a write or removal does nothing. Each call catches on its own, so a\n * blocked area never stops a call on the other.\n */\n\ntype StorageArea = 'localStorage' | 'sessionStorage';\n\n/** @returns `null` when nothing is stored under `key`, or the storage is blocked. */\nexport function getStored(key: string, area: StorageArea = 'localStorage'): string | null {\n try {\n return window[area].getItem(key);\n } catch {\n // Blocked storage holds nothing: see the file's comment.\n return null;\n }\n}\n\nexport function setStored(key: string, value: string, area: StorageArea = 'localStorage'): void {\n try {\n window[area].setItem(key, value);\n } catch {\n // Blocked or full storage keeps nothing: see the file's comment.\n }\n}\n\nexport function removeStored(key: string, area: StorageArea = 'localStorage'): void {\n try {\n window[area].removeItem(key);\n } catch {\n // Blocked storage has nothing to remove: see the file's comment.\n }\n}\n", "/** A Storefront cart id: what the browser keeps for the visitor's cart and sends the Worker to link or unlink it. */\nexport const CART_ID = /^gid:\\/\\/shopify\\/Cart\\/[\\w-]+(\\?key=[\\w-]+)?$/;\n", "/**\n * The cart on this page: what Shopify last said about it (`CartStatus`), the quantities quick clicks asked for and\n * the one queue every change goes through. A change of what the page shows goes through\n * here, so the document (`renderCart`) and the listeners (`tether:cart-updated`) always see the store's cart.\n */\nimport { cfg } from '../config.ts';\nimport { emit } from '../dom.ts';\nimport { isOpen } from './drawer.ts';\nimport { renderCart, quantityOf, showSavedCount } from './render.ts';\nimport { getStored, removeStored, setStored } from '../storage.ts';\nimport { CART_ID } from '../../server/src/shared/cart-id.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { Image } from '../fragments.ts';\nimport type { Money } from '../shopify-values.ts';\n\nexport interface Attribute {\n key: string;\n value: string;\n}\n\n/** A line's `merchandise`: Shopify's `Merchandise` union has only `ProductVariant`. */\nexport interface CartVariant {\n id: string;\n title: string;\n sku: string | null;\n availableForSale: boolean;\n requiresShipping: boolean;\n /** Absent when the store's token can't read inventory. */\n quantityAvailable?: number | null;\n image: Image | null;\n price: Money;\n compareAtPrice: Money | null;\n selectedOptions: { name: string; value: string }[];\n product: { id: string; title: string; handle: string; vendor: string; featuredImage: Image | null };\n}\n\ninterface LinePlanAllocation {\n sellingPlan: {\n id: string;\n name: string;\n description: string | null;\n recurringDeliveries: boolean;\n options: { name: string; value: string }[];\n };\n checkoutChargeAmount: Money;\n priceAdjustments: { price: Money; compareAtPrice: Money; perDeliveryPrice: Money }[];\n}\n\nexport interface ShopifyCartLine {\n id: string;\n quantity: number;\n attributes: Attribute[];\n cost: {\n totalAmount: Money;\n subtotalAmount: Money;\n amountPerQuantity: Money;\n compareAtAmountPerQuantity: Money | null;\n };\n discountAllocations: { discountedAmount: Money }[];\n sellingPlanAllocation: LinePlanAllocation | null;\n merchandise: CartVariant;\n /** Only on a bundle (`ComponentizableCartLine`). */\n lineComponents?: {\n id: string;\n quantity: number;\n merchandise: { id: string; title: string; product: { title: string; handle: string } };\n }[];\n}\n\n/** The fields of `fragment TetherCart`. */\nexport interface ShopifyCart {\n id: string;\n checkoutUrl: string;\n totalQuantity: number;\n note: string | null;\n createdAt: string;\n updatedAt: string;\n attributes: Attribute[];\n buyerIdentity: { countryCode: string | null; email: string | null };\n discountCodes: { code: string; applicable: boolean }[];\n cost: { subtotalAmount: Money; totalAmount: Money; checkoutChargeAmount: Money };\n lines: { nodes: ShopifyCartLine[] };\n}\n\n/**\n * `unknown` until the saved cart has loaded (the page's own markup shows); `empty` without a cart (none saved, or\n * Shopify no longer has it); `error` after a failed load or change, with the cart as Shopify last sent it.\n */\nexport type CartStatus =\n | { kind: 'unknown' }\n | { kind: 'empty' }\n | { kind: 'ready'; cart: ShopifyCart }\n | { kind: 'error'; error: unknown; cart: ShopifyCart | null };\n\n/** What one quick +/- click asked for; a newer click replaces it. */\nexport interface PendingQuantity {\n quantity: number;\n}\n\n/** The second argument of a `subscribe` listener. */\ninterface CartMeta {\n open: boolean;\n count: number;\n loaded: boolean;\n /** A copy of Shopify's notes on the last change. */\n warnings: CartWarning[];\n}\n\nlet status: CartStatus = { kind: 'unknown' };\n\n/** Shopify's notes on the last change. */\nlet warnings: readonly CartWarning[] = [];\n\n/** Line id \u2192 what quick +/- clicks asked for, until Shopify has answered. */\nexport const pendingQuantities = new Map<string, PendingQuantity>();\n\nexport const cartStatus = (): CartStatus => status;\n\n/** The cart as Shopify last sent it. */\nexport function currentCart(): ShopifyCart | null {\n switch (status.kind) {\n case 'ready':\n case 'error':\n return status.cart;\n case 'unknown':\n case 'empty':\n return null;\n }\n}\n\nexport const currentWarnings = (): readonly CartWarning[] => warnings;\n\nconst statusOf = (cart: ShopifyCart | null): CartStatus => (cart ? { kind: 'ready', cart } : { kind: 'empty' });\n\nconst seenKey = () => `${cfg.storageKey}:seen`;\n\n/**\n * The id of the visitor's cart, kept across pages. A stored value that isn't a cart id (written by another script,\n * or edited by hand) is forgotten, so it is never sent to Shopify or the Worker and the next change starts a new cart.\n */\nexport function savedCartId(): string | null {\n const stored = getStored(cfg.storageKey);\n if (!stored) return null;\n if (CART_ID.test(stored)) return stored;\n forgetSavedCart();\n return null;\n}\n\n/** Forgets the saved cart, which Shopify no longer has. */\nexport function forgetSavedCart(): void {\n removeStored(cfg.storageKey);\n}\n\n/** Keeps a cart Shopify sent without showing it, for an answer a newer change will replace. */\nexport function keepQuietly(cart: ShopifyCart | null): void {\n status = statusOf(cart);\n if (cart) setStored(cfg.storageKey, cart.id);\n}\n\n/** Renders the document with the store's cart. */\nexport function render(): void {\n renderCart({\n cart: currentCart(),\n warnings,\n pending: pendingQuantities,\n failed: status.kind === 'error',\n });\n}\n\n/**\n * Why `tether:cart-updated` fired: `load` when the saved cart was loaded, failed to load or was forgotten; `pending`\n * when quick clicks changed a quantity Shopify doesn't have yet, and `settled` once their last request is done; any\n * other value names the change Shopify just answered. `docs/reference.md` lists them.\n */\nexport type CartUpdateReason =\n 'load' | 'add' | 'update' | 'remove' | 'note' | 'attributes' | 'discount' | 'buyer-identity' | 'pending' | 'settled';\n\n/** @param reason The `reason` of `tether:cart-updated`. */\nexport function announce(reason: CartUpdateReason): void {\n render();\n emit('cart-updated', { cart: currentCart(), reason });\n}\n\n/**\n * Keeps a cart Shopify sent after a change, shows it, and tells this page and the visitor's other tabs.\n *\n * @returns `cart`.\n */\nexport function commit(cart: ShopifyCart | null, reason: CartUpdateReason): ShopifyCart | null {\n keepQuietly(cart);\n // Other tabs reload the cart on this storage event; the next page shows the count before its cart has loaded.\n setStored(seenKey(), `${Date.now()}|${cart?.totalQuantity ?? 0}`);\n announce(reason);\n return cart;\n}\n\n/** The outcome of loading the saved cart: the cart, `null` when there is none, or the failure. */\nexport function settleLoad(\n outcome: { kind: 'loaded'; cart: ShopifyCart | null } | { kind: 'failed'; error: unknown },\n): void {\n status = outcome.kind === 'loaded' ? statusOf(outcome.cart) : { kind: 'error', error: outcome.error, cart: null };\n announce('load');\n}\n\n/**\n * The cart's count on the last page, from `<storageKey>:seen` (`<time>|<count>`).\n *\n * @returns `null` unless the count is a whole number: other scripts and other Tether versions write storage too.\n */\nfunction lastKnownCount(): number | null {\n const text = getStored(seenKey())?.split('|')[1] ?? '';\n const count = Number(text);\n return /^\\d+$/.test(text) && Number.isSafeInteger(count) ? count : null;\n}\n\n/** Until the saved cart has loaded, the counts show the count it had on the last page instead of the page's 0. */\nexport function showLastKnownCount(): void {\n if (status.kind !== 'unknown') return;\n const count = lastKnownCount();\n if (count !== null) showSavedCount(count);\n}\n\n/** Sets Shopify's notes on the last change: each message once, in the site's words (`warningText`). */\nexport function setWarnings(notes: readonly CartWarning[]): void {\n warnings = notes\n .filter((note, index) => notes.findIndex((other) => other.message === note.message) === index)\n .map((note) => ({ ...note, message: cfg.warningText?.(note) || note.message }));\n if (warnings.length) emit('cart-warning', { warnings });\n}\n\n/** The queue only waits for a task: its result, or failure, reaches the caller through the promise `run` returns. */\nconst waitOnly = () => undefined;\n\n/** @returns A runner that starts each task once the one before has settled, whether it succeeded or failed. */\nexport function serial(): <T>(task: () => Promise<T>) => Promise<T> {\n let previous: Promise<unknown> = Promise.resolve();\n return <T>(task: () => Promise<T>) => {\n const run = previous.then(task);\n previous = run.then(waitOnly, waitOnly);\n return run;\n };\n}\n\nconst runInOrder = serial();\n\n/**\n * Runs a cart change after the ones before it, so quick clicks never race. A failure sets the `error` status (shown by\n * `tether-show=\"cart-error\"` until the next change succeeds), emits `tether:cart-error` and rejects.\n */\nexport function enqueue<T>(task: () => Promise<T>): Promise<T> {\n return runInOrder(task).then(\n (value) => {\n if (status.kind === 'error') {\n status = statusOf(status.cart);\n render();\n }\n return value;\n },\n (error: unknown) => {\n status = { kind: 'error', error, cart: currentCart() };\n render();\n emit('cart-error', { error });\n throw error;\n },\n );\n}\n\n/** @returns The quantity to show for `line`. */\nexport const pendingQuantityOf = (line: ShopifyCartLine): number => quantityOf(line, pendingQuantities);\n\nconst meta = (): CartMeta => ({\n open: isOpen(),\n count: currentCart()?.totalQuantity ?? 0,\n loaded: status.kind !== 'unknown',\n warnings: [...warnings],\n});\n\n/**\n * Calls `listener` now and after every change of the cart or the drawer.\n *\n * @returns Stops listening.\n */\nexport function subscribe(listener: (cart: ShopifyCart | null, meta: CartMeta) => void): () => void {\n const events = ['cart-updated', 'cart-toggled'].map((name) => `${cfg.prefix}:${name}`);\n const call = () => {\n listener(currentCart(), meta());\n };\n events.forEach((name) => {\n document.addEventListener(name, call);\n });\n call();\n return () => {\n events.forEach((name) => {\n document.removeEventListener(name, call);\n });\n };\n}\n", "/**\n * The Storefront Cart API: loading the saved cart, creating one, and the mutations that change it. Shopify forgets a\n * cart some days after its last change and once it has been checked out; a change that needs a cart then creates a\n * new one with the same input, so the visitor's click still lands.\n */\nimport { cfg, log } from '../config.ts';\nimport { gql, orNull, shaped, unwrap, TetherError } from '../client.ts';\nimport { cartFragment } from '../fragments.ts';\nimport { GID } from '../selling-plans.ts';\nimport { toAttributes } from '../dom.ts';\nimport { CART } from './cart-shape.ts';\nimport { savedCartId, forgetSavedCart, setWarnings } from './store.ts';\nimport type { CartWarning } from '../config.ts';\nimport type { QueryText, UserError } from '../client.ts';\nimport type { AttributeValue } from '../dom.ts';\nimport type { ShopifyCart, Attribute } from './store.ts';\n\nexport type Attributes = Record<string, AttributeValue> | { key: string | number; value?: AttributeValue }[];\n\n/** A line to add, in any of the shapes `Cart.add` takes. */\nexport interface LineSpec {\n variantId?: string | number;\n merchandiseId?: string | number;\n id?: string | number;\n quantity?: number | string;\n sellingPlanId?: string | number | null;\n sellingPlan?: string | number | null;\n attributes?: Attributes | null;\n}\n\n/** Shopify's `CartLineInput`. */\nexport interface CartLineInput {\n merchandiseId: string;\n quantity: number;\n sellingPlanId?: string;\n attributes?: Attribute[];\n}\n\n/** What every cart mutation answers. Its lists are read with a fallback, so `isPayloadOf` checks the cart alone. */\ninterface CartPayload {\n cart: ShopifyCart | null;\n userErrors?: UserError[] | null;\n warnings?: CartWarning[];\n}\n\n/**\n * Shopify answers a cart id it can't resolve, in a mutation, with a top-level GraphQL error whose only detail is\n * this message (no `extensions.code`). A user error on the `cartId` field is recognised by its shape instead.\n */\nconst UNKNOWN_CART_MESSAGE = /does not exist|not found|invalid id/i;\n\n/** @returns A check of a mutation's answer: a `null` payload (which `unwrap` refuses), or one with a cart or `null`. */\nconst isPayloadOf = (name: string) =>\n shaped<Record<string, CartPayload | null>>({ [name]: orNull({ cart: orNull(CART) }) });\n\n/**\n * @returns True when `error` says Shopify doesn't have the cart, false for any other failure (the network, the token,\n * a line it refused), after which the saved cart is still good.\n */\nexport function isCartGone(error: unknown): boolean {\n if (!(error instanceof TetherError)) return false;\n if (error.userErrors?.some((userError) => userError.field?.[0] === 'cartId')) return true;\n return (error.errors ?? []).some((graphQLError) => UNKNOWN_CART_MESSAGE.test(graphQLError.message));\n}\n\n/**\n * @returns True when a change failed without saying whether Shopify applied it: no answer or no whole body came (the\n * network), a 2xx whose body isn't valid, or a server error (a 5xx, or a 2xx with `SERVER_ERROR`), which can come\n * after the change was committed. Shopify's user errors and a 4xx are clear.\n */\nexport function isUnclearFailure(error: unknown): boolean {\n if (!(error instanceof TetherError)) return false;\n const { code, status = 0 } = error;\n return code === 'NETWORK' || code === 'INVALID_RESPONSE' || code === 'SERVER_ERROR' || status >= 500;\n}\n\n/**\n * @param input A variant id, or a line.\n * @param quantity For a variant id.\n * @param options For a variant id.\n * @throws {TetherError} `ERROR` without a variant id.\n */\nexport function toLineInput(\n input: string | number | LineSpec | null | undefined,\n quantity = 1,\n options: Omit<LineSpec, 'variantId' | 'quantity'> = {},\n): CartLineInput {\n const spec: LineSpec =\n typeof input === 'object' && input !== null\n ? input\n : { ...options, quantity, ...(input === null || input === undefined ? {} : { variantId: input }) };\n const merchandiseId = GID.variant(spec.merchandiseId || spec.variantId || spec.id);\n if (!merchandiseId) throw new TetherError('add(): variantId is required');\n const asked = parseInt(String(spec.quantity ?? 1), 10);\n const line: CartLineInput = { merchandiseId, quantity: Math.max(1, asked || 1) };\n const planId = GID.sellingPlan(spec.sellingPlanId || spec.sellingPlan);\n if (planId) line.sellingPlanId = planId;\n const attributes = toAttributes(spec.attributes);\n if (attributes.length) line.attributes = attributes;\n return line;\n}\n\n/**\n * @param name The mutation, which is also its payload's field.\n * @param args The operation's variable definitions.\n * @param inner The mutation field's arguments.\n */\nfunction mutationText(name: string, args: string, inner: string): QueryText {\n return (access) => `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message target}}}${cartFragment(access)}`;\n}\n\n/**\n * @returns The cart in Shopify's answer, after keeping its warnings.\n * @throws {TetherError} For a failed request, or the mutation's user errors.\n */\nasync function send(\n name: string,\n args: string,\n inner: string,\n variables: Record<string, unknown>,\n): Promise<ShopifyCart | null> {\n const data = await gql(mutationText(name, args, inner), variables, isPayloadOf(name), { operation: 'mutation' });\n const payload = unwrap(data[name], name);\n setWarnings(payload.warnings ?? []);\n return payload.cart;\n}\n\n/**\n * A new cart in the visitor's market.\n *\n * @param input More of Shopify's `CartInput`, e.g. the first lines.\n */\nfunction createCart(input: Record<string, unknown>): Promise<ShopifyCart | null> {\n const withIdentity = cfg.country ? { buyerIdentity: { countryCode: cfg.country }, ...input } : input;\n return send('cartCreate', '$input: CartInput!', 'input: $input', { input: withIdentity });\n}\n\n/** A mutation on the saved cart. */\ninterface CartMutation {\n /** The mutation, e.g. `cartLinesAdd`. */\n name: string;\n /** Its variable definitions besides `$cartId`. */\n args: string;\n /** Its arguments besides `cartId`. */\n inner: string;\n variables: Record<string, unknown>;\n /**\n * The `CartInput` of a new cart that does what the mutation asks, when there is no cart (or Shopify no longer has\n * it); `null` leaves the visitor without a cart instead.\n */\n createInput: Record<string, unknown> | null;\n}\n\n/**\n * Runs a mutation on the saved cart, or creates a cart from `createInput` when there is none to change.\n *\n * @returns The changed cart; `null` without a cart and without a `createInput`.\n * @throws {TetherError} For a failed request or Shopify's user errors; also when the cart is gone and there is no\n * `createInput` (the saved cart is forgotten first).\n */\nexport async function mutateCart({\n name,\n args,\n inner,\n variables,\n createInput,\n}: CartMutation): Promise<ShopifyCart | null> {\n const cartId = savedCartId();\n if (!cartId) return createInput ? createCart(createInput) : null;\n try {\n return await send(name, `$cartId: ID!, ${args}`, `cartId: $cartId, ${inner}`, { ...variables, cartId });\n } catch (error) {\n if (!isCartGone(error)) throw error;\n forgetSavedCart();\n if (!createInput) throw error;\n log('cart expired, creating a new one');\n return createCart(createInput);\n }\n}\n\n/** @returns The cart, `null` when Shopify doesn't have it (expired or checked out). */\nexport async function fetchCart(id: string): Promise<ShopifyCart | null> {\n const data = await gql(\n (access) => `query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${cartFragment(access)}`,\n { id },\n shaped<{ cart: ShopifyCart | null }>({ cart: orNull(CART) }),\n );\n return data.cart;\n}\n\n/** @returns The cart's current checkout URL, `null` when Shopify doesn't have the cart. */\nexport async function fetchCheckoutUrl(id: string): Promise<string | null> {\n const data = await gql(\n `query TetherCheckout($id:ID!){cart(id:$id){checkoutUrl}}`,\n { id },\n shaped<{ cart: { checkoutUrl: string } | null }>({ cart: orNull({ checkoutUrl: 'string' }) }),\n );\n return data.cart?.checkoutUrl ?? null;\n}\n\n/** A change to one line, for `updateLine`. */\nexport interface LinePatch {\n /** 0 or less removes the line. */\n quantity?: number;\n /** `null` switches the line back to one-time purchase. */\n sellingPlanId?: string | number | null;\n attributes?: Attributes;\n merchandiseId?: string | number;\n variantId?: string | number;\n}\n\n/** @returns Shopify's `CartLineUpdateInput`. */\nexport function toLineUpdate(lineId: string, patch: number | LinePatch): Record<string, unknown> {\n const change: LinePatch = typeof patch === 'number' ? { quantity: patch } : patch;\n const line: Record<string, unknown> = { id: lineId };\n if (change.quantity !== undefined) line['quantity'] = change.quantity;\n if ('sellingPlanId' in change)\n line['sellingPlanId'] = change.sellingPlanId ? GID.sellingPlan(change.sellingPlanId) : null;\n if (change.attributes) line['attributes'] = toAttributes(change.attributes);\n const variantId = change.merchandiseId || change.variantId;\n if (variantId) line['merchandiseId'] = GID.variant(variantId);\n return line;\n}\n\nexport const updateLines = (lines: Record<string, unknown>[]): Promise<ShopifyCart | null> =>\n mutateCart({\n name: 'cartLinesUpdate',\n args: '$lines: [CartLineUpdateInput!]!',\n inner: 'lines: $lines',\n variables: { lines },\n createInput: null,\n });\n\nexport const removeLines = (lineIds: string[]): Promise<ShopifyCart | null> =>\n mutateCart({\n name: 'cartLinesRemove',\n args: '$lineIds: [ID!]!',\n inner: 'lineIds: $lineIds',\n variables: { lineIds },\n createInput: null,\n });\n\n/** @returns The cart once `lineId` has `target` (0 removes it). */\nexport const setLineQuantity = (lineId: string, target: number): Promise<ShopifyCart | null> =>\n target > 0 ? updateLines([{ id: lineId, quantity: target }]) : removeLines([lineId]);\n", "/**\n * Sending the visitor to Shopify's checkout. The URL comes from Shopify at click time, because the cart's checkout URL\n * changes with its market and customer; the account part resolves a logged-in customer's own (`setCheckoutResolver`).\n */\nimport { cfg, reportError } from '../config.ts';\nimport { TetherError } from '../client.ts';\nimport { isHttpsUrl } from '../shopify-values.ts';\nimport { emit } from '../dom.ts';\nimport { fetchCheckoutUrl } from './mutations.ts';\nimport { currentCart, savedCartId } from './store.ts';\nimport type { CheckoutTarget } from '../config.ts';\n\ntype CheckoutUrlAnswer = { kind: 'url'; url: string | null } | { kind: 'failed'; error: unknown };\n\n/**\n * The logged-in customer's checkout URL for the cart; `null` when nobody is logged in, so the cart's own checkout URL\n * is used.\n */\ntype CheckoutResolver = () => Promise<string | null>;\n\nlet resolveAccountCheckout: CheckoutResolver | null = null;\n\n/** Sets the account part's checkout URL, or clears it with `null`. A URL it answers opens checkout logged in. */\nexport function setCheckoutResolver(resolver: CheckoutResolver | null): void {\n resolveAccountCheckout = resolver;\n}\n\n/**\n * @returns Shopify's answer as a value, so the request can run alongside the account's and its failure is read only\n * when its URL is the one used.\n */\nconst askCheckoutUrl = (cartId: string): Promise<CheckoutUrlAnswer> =>\n fetchCheckoutUrl(cartId).then(\n (url) => ({ kind: 'url', url }),\n (error: unknown) => ({ kind: 'failed', error }),\n );\n\n/**\n * @returns The logged-in customer's checkout URL. When the account can't give one, the failure is reported and the\n * cart's own checkout URL is used, which still checks out (without the login).\n */\nasync function accountCheckoutUrl(): Promise<string | null> {\n if (!resolveAccountCheckout) return null;\n try {\n const url = await resolveAccountCheckout();\n // Shopify's checkout shows the customer as logged in only with this parameter.\n return url && `${url}${url.includes('?') ? '&' : '?'}logged_in=true`;\n } catch (error) {\n reportError(error, 'account checkout');\n return null;\n }\n}\n\n/** @throws {TetherError} `ERROR` without a checkout URL (the cart is empty or Shopify no longer has it). */\nasync function checkoutUrl(cartId: string): Promise<string> {\n const cartAnswer = askCheckoutUrl(cartId);\n const accountUrl = await accountCheckoutUrl();\n if (accountUrl) return accountUrl;\n const answer = await cartAnswer;\n if (answer.kind === 'failed') throw answer.error;\n if (!answer.url) throw new TetherError('No checkoutUrl (cart empty or expired)');\n return answer.url;\n}\n\n/**\n * A new tab's `opener` gives the checkout page a handle on this one; a browser that doesn't let a script clear it\n * leaves the tab as it opened.\n */\nfunction detachOpenerIfAllowed(tab: Window) {\n try {\n tab.opener = null;\n } catch {\n // The browser keeps `opener` read-only: see the function's comment.\n }\n}\n\n/**\n * Goes to Shopify's checkout. With `_blank` the tab opens inside the click (Safari blocks `window.open` after an\n * await) and gets its URL once there is one.\n *\n * @returns The checkout URL, `null` with nothing to check out or after a reported failure.\n */\nexport async function checkout(target: CheckoutTarget = cfg.checkoutTarget): Promise<string | null> {\n const cartId = savedCartId();\n const cart = currentCart();\n if (!cartId || (cart && !cart.totalQuantity)) return null;\n const tab = target === '_blank' ? window.open('', '_blank') : null;\n if (tab) detachOpenerIfAllowed(tab);\n try {\n const url = await checkoutUrl(cartId);\n if (!isHttpsUrl(url)) throw new TetherError('The checkout URL is not https: not opened');\n emit('checkout', { url, cart: currentCart() });\n if (tab && !tab.closed) tab.location.href = url;\n else window.location.href = url;\n return url;\n } catch (error) {\n tab?.close();\n reportError(error, 'checkout failed');\n return null;\n }\n}\n", "/**\n * The cart, as one object: `Tether.cart`. Every change goes through one queue (fast clicks never race) and resolves\n * with the cart Shopify sent back. The state lives in `cart/store.ts`, the Cart API calls in `cart/mutations.ts`, the\n * drawer in `cart/drawer.ts`, the document rendering in `cart/render.ts` and the checkout in `cart/checkout.ts`.\n */\nimport { cfg, reportError } from './config.ts';\nimport { TetherError } from './client.ts';\nimport { emit, toAttributes } from './dom.ts';\nimport {\n fetchCart,\n isCartGone,\n isUnclearFailure,\n mutateCart,\n removeLines,\n setLineQuantity,\n toLineInput,\n toLineUpdate,\n updateLines,\n} from './cart/mutations.ts';\nimport { checkout, setCheckoutResolver } from './cart/checkout.ts';\nimport { drawers, isOpen, openCart, closeCart, toggleCart, trapFocus } from './cart/drawer.ts';\nimport {\n announce,\n cartStatus,\n commit,\n currentCart,\n currentWarnings,\n enqueue,\n forgetSavedCart,\n keepQuietly,\n pendingQuantities,\n pendingQuantityOf,\n render,\n savedCartId,\n settleLoad,\n showLastKnownCount,\n subscribe,\n} from './cart/store.ts';\nimport type { CartWarning } from './config.ts';\nimport type { Attributes, LineSpec, CartLineInput, LinePatch } from './cart/mutations.ts';\nimport type { ShopifyCart, ShopifyCartLine, CartStatus } from './cart/store.ts';\n\nexport { trapFocus };\n\ntype DiscountCodes = string[] | string | ((current: string[]) => string[]);\n\nconst currentLines = (): readonly ShopifyCartLine[] => currentCart()?.lines.nodes ?? [];\n\n/**\n * Loads the saved cart again after a change whose outcome is unclear, so this page and the visitor's other tabs show\n * what Shopify has. A failure here is reported; the change's own failure is what its caller hears.\n */\nasync function reloadAfterUnclearFailure(): Promise<void> {\n const id = savedCartId();\n if (!id) return;\n try {\n const cart = await fetchCart(id);\n if (!cart) forgetSavedCart();\n commit(cart, 'load');\n } catch (error) {\n reportError(error, 'cart reload after a failed change');\n }\n}\n\n/**\n * Runs a cart change in the queue (`enqueue`). After a failure that doesn't say whether Shopify applied the change,\n * the cart is loaded again before the next change runs; the change still rejects.\n */\nfunction runChange<T>(task: () => Promise<T>): Promise<T> {\n return enqueue(() =>\n task().catch(async (error: unknown) => {\n if (isUnclearFailure(error)) await reloadAfterUnclearFailure();\n throw error;\n }),\n );\n}\n\n/**\n * +1 / -1 for a line. Quick clicks add up: each builds on the quantity the click before asked for, which shows at once\n * (`tether-line=\"quantity\"`, `quantityOf`) until Shopify has answered. Only the latest target is sent; the ones it\n * replaced are skipped.\n */\nfunction changeQuantity(lineId: string, delta: number): Promise<ShopifyCart | null> {\n const line = currentLines().find((candidate) => candidate.id === lineId);\n if (!line) return Promise.resolve(currentCart());\n const target = Math.max(0, pendingQuantityOf(line) + delta);\n const job = { quantity: target };\n pendingQuantities.set(lineId, job);\n announce('pending');\n const isLatest = () => pendingQuantities.get(lineId) === job;\n const sent = runChange(async () => {\n if (!isLatest()) return currentCart();\n const now = currentLines().find((candidate) => candidate.id === lineId);\n // A + then a -: Shopify already has the quantity.\n if (now?.quantity === target) return currentCart();\n const cart = await setLineQuantity(lineId, target);\n // A newer click is on its way: keep this answer, but show totals and count once that one is in, instead of\n // stepping through every reply.\n if (!isLatest()) {\n keepQuietly(cart);\n return cart;\n }\n return commit(cart, target > 0 ? 'update' : 'remove');\n });\n return sent.finally(() => {\n if (!isLatest()) return;\n pendingQuantities.delete(lineId);\n announce('settled');\n });\n}\n\n/**\n * The codes the cart has once a change is its turn in the queue.\n *\n * @param codes A function gets the codes as they are by then.\n */\nfunction discountCodesFrom(codes: DiscountCodes): string[] {\n const current = (currentCart()?.discountCodes ?? []).map((discount) => discount.code);\n const list = typeof codes === 'function' ? codes(current) : codes;\n return (Array.isArray(list) ? list : [list]).map((code) => code.trim()).filter(Boolean);\n}\n\n/** @returns The cart's attributes with `attributes` merged in; `''` removes a key. */\nfunction mergedAttributes(attributes: Attributes): { key: string; value: string }[] {\n const merged = new Map((currentCart()?.attributes ?? []).map((attribute) => [attribute.key, attribute.value]));\n toAttributes(attributes).forEach((attribute) => merged.set(attribute.key, attribute.value));\n return [...merged].filter(([, value]) => value !== '').map(([key, value]) => ({ key, value }));\n}\n\n/**\n * Loads the saved cart. A cart Shopify no longer has is forgotten; one it can't be asked about (the network, the\n * token) is kept, because it is probably fine.\n */\nasync function init(): Promise<ShopifyCart | null> {\n const id = savedCartId();\n if (!id) {\n settleLoad({ kind: 'loaded', cart: null });\n return null;\n }\n showLastKnownCount();\n try {\n const cart = await fetchCart(id);\n if (!cart) forgetSavedCart();\n settleLoad({ kind: 'loaded', cart });\n } catch (error) {\n reportError(error, 'cart load failed');\n if (isCartGone(error)) forgetSavedCart();\n settleLoad({ kind: 'failed', error });\n }\n return currentCart();\n}\n\nexport const Cart = {\n init,\n /** The saved cart's id. */\n id: savedCartId,\n\n get status(): CartStatus {\n return cartStatus();\n },\n get state(): ShopifyCart | null {\n return currentCart();\n },\n /** The cart's lines, a copy: changing it changes nothing in the cart. */\n get lines(): ShopifyCartLine[] {\n return [...currentLines()];\n },\n get count(): number {\n return currentCart()?.totalQuantity ?? 0;\n },\n /** True once the saved cart, or that there is none, is known. */\n get loaded(): boolean {\n return cartStatus().kind !== 'unknown';\n },\n /**\n * The failure of the last load or change, until a change succeeds. Requests fail with an `Error`; anything else\n * thrown is wrapped, so the public type holds.\n */\n get lastError(): Error | null {\n const current = cartStatus();\n if (current.kind !== 'error') return null;\n return current.error instanceof Error ? current.error : new Error(String(current.error));\n },\n /** Shopify's notes on the last change, a copy. */\n get warnings(): CartWarning[] {\n return [...currentWarnings()];\n },\n /** Line id \u2192 the quantity quick clicks asked for. */\n get pendingQty(): ReadonlyMap<string, { quantity: number }> {\n return pendingQuantities;\n },\n\n /**\n * Sets where a logged-in customer checks out, or clears it with `null`: the account part's resolver answers the\n * customer's checkout URL for the cart (`null` uses the cart's own).\n */\n setCheckoutResolver,\n\n /**\n * Forgets the saved cart on this device, as if there were none: the page shows an empty cart and\n * `tether:cart-updated` fires with reason `load`. The next add starts a new cart.\n */\n forgetSaved(): void {\n forgetSavedCart();\n settleLoad({ kind: 'loaded', cart: null });\n },\n\n render,\n subscribe,\n\n /**\n * `add(variantId, 2)`, `add({ variantId, quantity, sellingPlanId, attributes })`, or `add([line, line])` for\n * bundles in one request. Creates the cart when there is none.\n *\n * @throws {TetherError} (rejected) When the add fails, or Shopify answers it without a cart.\n */\n add(\n input: string | number | LineSpec | (string | number | LineSpec)[],\n quantity = 1,\n options: Omit<LineSpec, 'variantId' | 'quantity'> = {},\n ): Promise<ShopifyCart> {\n const lines: CartLineInput[] = (Array.isArray(input) ? input : [input]).map((line) =>\n toLineInput(line, quantity, options),\n );\n return runChange(async () => {\n const cart = await mutateCart({\n name: 'cartLinesAdd',\n args: '$lines: [CartLineInput!]!',\n inner: 'lines: $lines',\n variables: { lines },\n createInput: { lines },\n });\n // A mutation answers without a cart only beside user errors, which reject above; an add always has one.\n if (!cart) throw new TetherError('Shopify answered the add without a cart');\n commit(cart, 'add');\n emit('added', { cart, lines });\n if (cfg.openCartOnAdd) openCart();\n return cart;\n });\n },\n\n /**\n * `updateLine(lineId, 3)` or `updateLine(lineId, { quantity, sellingPlanId, attributes })`.\n *\n * @param patch A quantity of 0 or less removes the line.\n */\n updateLine(lineId: string, patch: number | LinePatch): Promise<ShopifyCart | null> {\n const quantity = typeof patch === 'number' ? patch : patch.quantity;\n if (quantity !== undefined && quantity <= 0) return Cart.removeLine(lineId);\n const line = toLineUpdate(lineId, patch);\n return runChange(async () => commit(await updateLines([line]), 'update'));\n },\n\n changeQuantity,\n\n /** The quantity to show: the one asked for while an update is on its way. */\n quantityOf: pendingQuantityOf,\n\n removeLine(lineIds: string | string[]): Promise<ShopifyCart | null> {\n const ids = Array.isArray(lineIds) ? lineIds : [lineIds];\n return runChange(async () => commit(await removeLines(ids), 'remove'));\n },\n\n /** Removes the lines the cart has when the change runs, so one queued just before it is cleared too. */\n clear(): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const ids = currentLines().map((line) => line.id);\n if (!ids.length) return currentCart();\n return commit(await removeLines(ids), 'remove');\n });\n },\n\n setNote(note: string | null | undefined): Promise<ShopifyCart | null> {\n const variables = { note: note ?? '' };\n return runChange(async () =>\n commit(\n await mutateCart({\n name: 'cartNoteUpdate',\n args: '$note: String!',\n inner: 'note: $note',\n variables,\n createInput: variables,\n }),\n 'note',\n ),\n );\n },\n\n /**\n * Cart attributes, merged with the cart's (as it is when the change runs, so quick calls don't drop each other).\n *\n * @param attributes The value `''` removes a key.\n */\n setAttributes(attributes: Attributes): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const variables = { attributes: mergedAttributes(attributes) };\n const cart = await mutateCart({\n name: 'cartAttributesUpdate',\n args: '$attributes: [AttributeInput!]!',\n inner: 'attributes: $attributes',\n variables,\n createInput: variables,\n });\n return commit(cart, 'attributes');\n });\n },\n\n /** Replaces the discount codes; `[]` removes them all. */\n setDiscountCodes(codes: DiscountCodes): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const variables = { discountCodes: discountCodesFrom(codes) };\n const cart = await mutateCart({\n name: 'cartDiscountCodesUpdate',\n args: '$discountCodes: [String!]!',\n inner: 'discountCodes: $discountCodes',\n variables,\n createInput: variables,\n });\n return commit(cart, 'discount');\n });\n },\n\n /**\n * Adds a code. A code Shopify doesn't accept is removed again, so it doesn't warn on every later change.\n *\n * @throws {TetherError} `INVALID_DISCOUNT` (rejected) when Shopify doesn't accept the code.\n */\n async applyDiscount(code: string): Promise<ShopifyCart | null> {\n const cart = await Cart.setDiscountCodes((current) => [...new Set([...current, code])]);\n const wanted = code.toLowerCase();\n const applied = (cart?.discountCodes ?? []).find((discount) => discount.code.toLowerCase() === wanted);\n if (applied?.applicable) return cart;\n try {\n await Cart.removeDiscount(code);\n } catch (error) {\n reportError(error, 'removing a discount code Shopify refused');\n }\n throw new TetherError(`Discount code not valid: ${code}`, { code: 'INVALID_DISCOUNT' });\n },\n\n removeDiscount(code: string): Promise<ShopifyCart | null> {\n const unwanted = code.toLowerCase();\n return Cart.setDiscountCodes((current) => current.filter((existing) => existing.toLowerCase() !== unwanted));\n },\n\n /**\n * Sets who is buying; a country changes the cart's currency and prices to that market's.\n *\n * @param options `create: false` forgets an expired cart instead of creating an empty one.\n */\n setBuyerIdentity(\n buyerIdentity: { countryCode?: string; email?: string; phone?: string },\n { create = true }: { create?: boolean } = {},\n ): Promise<ShopifyCart | null> {\n return runChange(async () => {\n const cart = await mutateCart({\n name: 'cartBuyerIdentityUpdate',\n args: '$buyerIdentity: CartBuyerIdentityInput!',\n inner: 'buyerIdentity: $buyerIdentity',\n variables: { buyerIdentity },\n createInput: create ? { buyerIdentity } : null,\n }).catch((error: unknown) => {\n // mutateCart has forgotten the cart Shopify no longer has; without `create` that leaves no cart.\n if (create || !isCartGone(error)) throw error;\n return null;\n });\n return commit(cart, 'buyer-identity');\n });\n },\n\n checkout,\n\n drawers,\n isOpen,\n open: openCart,\n close: closeCart,\n toggle: toggleCart,\n};\n", "/**\n * What a product scope shows for the visitor's choice: the variant, the purchase it is bought as, its prices, the\n * text of every `tether-bind` and every `tether-show` condition. Pure, no DOM and no config: the browser scope\n * (`product.ts`) and the server's `initialState()` (`initial.ts`) derive their view here, so a server-rendered page\n * shows what the script shows when it takes over.\n */\nimport {\n GID,\n allPlans,\n allocationFor,\n findPlan,\n planAdjustment,\n planCheckoutCharge,\n planPrice,\n planSavings,\n} from './selling-plans.ts';\nimport { fromMinorUnits, toMinorUnits } from './amounts.ts';\nimport type { Money } from './shopify-values.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\n\n/** Option name \u2192 chosen value. */\nexport type Selection = Record<string, string>;\n/**\n * How the visitor wants to buy. Buying once keeps the plan offered next to the one-time price (the subscription\n * binds `subscription-price` and `plan-name` show it, and subscribing switches to it); `null` for a product without\n * plans.\n */\nexport type Purchase = { kind: 'one-time'; offeredPlanId: string | null } | { kind: 'subscription'; planId: string };\n/** How the line is bought: on a plan only when the visitor subscribes and the variant is on that plan. */\ntype LinePurchase = { kind: 'one-time' } | { kind: 'subscription'; planId: string };\n/** What the visitor chose, before it is checked against the product. */\ninterface ProductChoice {\n selected: Selection;\n purchase: Purchase;\n quantity: number;\n}\nexport type ShowCondition =\n | 'in-stock'\n | 'out-of-stock'\n | 'backorder'\n | 'on-sale'\n | 'not-on-sale'\n | 'has-variants'\n | 'single-variant'\n | 'low-stock'\n | 'plenty-in-stock'\n | 'subscription-available'\n | 'subscription-unavailable'\n | 'subscription-only'\n | 'one-time-available'\n | 'subscription-selected'\n | 'one-time-selected'\n | 'has-savings';\nexport interface ProductView {\n variant: ProductVariant;\n /** The selection, matching `variant`. */\n selected: Selection;\n /** The choice, made valid for the product and `variant`. */\n purchase: Purchase;\n /** The purchase's plan, offered or chosen. */\n plan: PlanInGroup | null;\n /** How the line is bought. */\n line: LinePurchase;\n /** The variant is on at least one plan. */\n subscriptionAvailable: boolean;\n quantity: number;\n /** What one item costs as bought. */\n price: Money;\n /** Only when on sale. */\n compareAtPrice: Money | null;\n /** What `plan` saves on the one-time price. */\n savings: { amount: Money | null; percentage: number };\n show: Record<ShowCondition, boolean>;\n}\n/** How the caller writes values as text. */\nexport interface Formatters {\n money: (money: Money | null) => string;\n percent: (percentage: number) => string;\n}\n/** What a bind writes; `null` and `undefined` clear it. */\ntype BindText = string | number | null | undefined;\n\nconst PERCENT = 100;\n\nconst timesQuantity = (money: Money, quantity: number): Money =>\n fromMinorUnits(toMinorUnits(money) * quantity, money.currencyCode);\n\nexport const selectionOf = (variant: ProductVariant): Selection =>\n Object.fromEntries(variant.selectedOptions.map((option) => [option.name, option.value]));\n\n/** @returns The variant with exactly these option values. */\nfunction findVariant(product: Product, selected: Selection): ProductVariant | null {\n return (\n product.variants.nodes.find((variant) =>\n variant.selectedOptions.every((option) => selected[option.name] === option.value),\n ) ?? null\n );\n}\n\n/**\n * @param name An option name.\n * @returns True when a variant for sale has this value and the other selected values.\n */\nexport function isValueAvailable(product: Product, selected: Selection, name: string, value: string): boolean {\n return product.variants.nodes.some(\n (variant) =>\n variant.availableForSale &&\n variant.selectedOptions.every((option) =>\n option.name === name ? option.value === value : selected[option.name] === option.value,\n ),\n );\n}\n\n/** @returns The plan subscribed on, or offered while buying once. */\nexport const purchasePlanId = (purchase: Purchase): string | null =>\n purchase.kind === 'subscription' ? purchase.planId : purchase.offeredPlanId;\n\n/** @returns On the plan when subscribing and the variant is on it, else once. */\nexport function linePurchase(purchase: Purchase, variant: ProductVariant | null): LinePurchase {\n if (purchase.kind === 'subscription' && allocationFor(variant, purchase.planId)) return purchase;\n return { kind: 'one-time' };\n}\n\n/**\n * The purchase the visitor wants, made valid for the product and variant: one-time without plans, a subscription when\n * the product requires one, and a plan the variant is on when it is on any (else a plan of the product, for the binds\n * to offer).\n *\n * @param kind The way of buying the visitor asked for.\n * @param preferredPlanId The plan to keep when it is the product's: the visitor's last one.\n */\nexport function normalisePurchase(\n product: Product,\n variant: ProductVariant | null,\n kind: Purchase['kind'],\n preferredPlanId: string | null,\n): Purchase {\n const plans = allPlans(product);\n const known = plans.find((plan) => plan.id === preferredPlanId) ?? plans[0];\n if (!known) return { kind: 'one-time', offeredPlanId: null };\n const onVariant = allocationFor(variant, known.id)\n ? known\n : (plans.find((plan) => allocationFor(variant, plan.id)) ?? known);\n if (product.requiresSellingPlan || kind === 'subscription') return { kind: 'subscription', planId: onVariant.id };\n return { kind: 'one-time', offeredPlanId: onVariant.id };\n}\n\ninterface InitialChoiceOptions {\n /** A variant id or gid (`?variant=`); the first variant for sale without. */\n variant?: string | null | undefined;\n /** A plan to subscribe on (`?selling_plan=`). */\n sellingPlan?: string | null | undefined;\n /** The plan offered first, by id, name or option value. */\n defaultPlan?: string | null | undefined;\n /** `subscription` subscribes; anything else buys once. */\n defaultPurchase?: string | null | undefined;\n}\n\n/** What a product starts with before the visitor chooses anything. */\nexport function initialChoice(\n product: Product,\n { variant, sellingPlan, defaultPlan, defaultPurchase }: InitialChoiceOptions,\n): Omit<ProductChoice, 'quantity'> {\n const variants = product.variants.nodes;\n const wantedId = GID.variant(variant);\n const start =\n variants.find((candidate) => candidate.id === wantedId) ??\n variants.find((candidate) => candidate.availableForSale) ??\n variants[0] ??\n null;\n const fromUrl = findPlan(product, sellingPlan);\n const offered = fromUrl ?? findPlan(product, defaultPlan);\n const kind = fromUrl || defaultPurchase === 'subscription' ? 'subscription' : 'one-time';\n return {\n selected: start ? selectionOf(start) : {},\n purchase: normalisePurchase(product, start, kind, offered?.id ?? null),\n };\n}\n\nfunction showConditions(\n variant: ProductVariant,\n lowStock: number,\n variantCount: number,\n sale: Pick<ProductView, 'line' | 'subscriptionAvailable' | 'compareAtPrice' | 'savings'>,\n requiresSellingPlan: boolean,\n): Record<ShowCondition, boolean> {\n const stock = variant.quantityAvailable;\n const low = stock != null && stock > 0 && stock <= lowStock;\n const onSale = sale.compareAtPrice !== null;\n const onPlan = sale.line.kind === 'subscription';\n return {\n 'in-stock': variant.availableForSale,\n 'out-of-stock': !variant.availableForSale,\n backorder: variant.availableForSale && variant.currentlyNotInStock,\n 'on-sale': onSale,\n 'not-on-sale': !onSale,\n 'has-variants': variantCount > 1,\n 'single-variant': variantCount === 1,\n 'low-stock': low,\n 'plenty-in-stock': variant.availableForSale && !low,\n 'subscription-available': sale.subscriptionAvailable,\n 'subscription-unavailable': !sale.subscriptionAvailable,\n 'subscription-only': requiresSellingPlan,\n 'one-time-available': !requiresSellingPlan,\n 'subscription-selected': onPlan,\n 'one-time-selected': !onPlan,\n 'has-savings': onPlan ? sale.savings.percentage > 0 : onSale,\n };\n}\n\n/**\n * The view for the visitor's choice. A selection no variant has falls back to the first variant.\n *\n * @param lowStock `low-stock` at this many or fewer.\n * @returns `null` for a product without variants.\n */\nexport function productView(product: Product, choice: ProductChoice, lowStock: number): ProductView | null {\n const exact = findVariant(product, choice.selected);\n const variant = exact ?? product.variants.nodes[0];\n if (!variant) return null;\n const purchase = normalisePurchase(product, variant, choice.purchase.kind, purchasePlanId(choice.purchase));\n const planId = purchasePlanId(purchase);\n const plans = allPlans(product);\n const line = linePurchase(purchase, variant);\n const allocation = line.kind === 'subscription' ? allocationFor(variant, line.planId) : null;\n // The plan helpers fall back to the one-time price only without a variant, which `variant` rules out.\n const price = allocation ? (planPrice(variant, planId) ?? variant.price) : variant.price;\n const compareAt = allocation?.priceAdjustments[0]?.compareAtPrice ?? variant.compareAtPrice;\n const sale = {\n line,\n subscriptionAvailable: plans.some((plan) => allocationFor(variant, plan.id)),\n compareAtPrice: compareAt && parseFloat(compareAt.amount) > parseFloat(price.amount) ? compareAt : null,\n savings: planSavings(variant, planId),\n };\n return {\n ...sale,\n variant,\n selected: exact ? choice.selected : selectionOf(variant),\n purchase,\n plan: plans.find((plan) => plan.id === planId) ?? null,\n quantity: choice.quantity,\n price,\n show: showConditions(variant, lowStock, product.variants.nodes.length, sale, product.requiresSellingPlan),\n };\n}\n\n/**\n * The `tether-bind` values that follow the variant and purchase. Thunks, so only the binds on the page are computed.\n */\nexport function variantBindTexts(view: ProductView, format: Formatters): Map<string, () => BindText> {\n const { variant, plan, price, compareAtPrice, quantity } = view;\n const planId = purchasePlanId(view.purchase);\n // Prices for the quantity count one item when it is 0 (an optional bundle item).\n const forQuantity = (money: Money) => timesQuantity(money, Math.max(1, quantity));\n const checkoutCharge = () => {\n const charge = view.line.kind === 'subscription' ? (planCheckoutCharge(variant, planId) ?? price) : price;\n return format.money(forQuantity(charge));\n };\n const discountPercentage = () => {\n if (!compareAtPrice) return '0%';\n return `${Math.round((1 - parseFloat(price.amount) / parseFloat(compareAtPrice.amount)) * PERCENT)}%`;\n };\n const discountAmount = () => {\n if (!compareAtPrice) return '';\n return format.money(fromMinorUnits(toMinorUnits(compareAtPrice) - toMinorUnits(price), price.currencyCode));\n };\n const texts: Record<string, () => BindText> = {\n price: () => format.money(price),\n 'total-price': () => format.money(forQuantity(price)),\n 'one-time-price': () => format.money(variant.price),\n 'subscription-price': () => format.money(planId ? planPrice(variant, planId) : null),\n 'subscription-savings': () => format.percent(view.savings.percentage),\n 'subscription-savings-amount': () => format.money(view.savings.amount),\n 'checkout-charge': checkoutCharge,\n 'plan-name': () => plan?.name,\n 'plan-description': () => plan?.description,\n 'plan-group': () => plan?.group.name,\n 'compare-at-price': () => format.money(compareAtPrice),\n 'unit-price': () => format.money(variant.unitPrice),\n 'discount-percentage': discountPercentage,\n 'discount-amount': discountAmount,\n 'variant-title': () => variant.title,\n 'variant-id': () => variant.id,\n sku: () => variant.sku,\n barcode: () => variant.barcode,\n stock: () => variant.quantityAvailable,\n quantity: () => quantity,\n };\n return new Map(Object.entries(texts));\n}\n/**\n * The `tether-plan-bind` values of one plan in a plan list, for the variant.\n *\n * @param planId The list item's `tether-plan-value`.\n * @param plan The product's plan with that id.\n */\nexport function planBindTexts(\n variant: ProductVariant,\n planId: string,\n plan: PlanInGroup | undefined,\n format: Formatters,\n): Map<string, () => BindText> {\n const adjustmentText = () => {\n const adjustment = planAdjustment(plan);\n if (!adjustment) return '';\n return adjustment.type === 'percentage' ? `${adjustment.value}%` : format.money(adjustment.value);\n };\n const texts: Record<string, () => BindText> = {\n name: () => plan?.name,\n // The option values alone (\"1 month\"), without the price text a store may put in the plan name.\n option: () => (plan?.options ?? []).map((option) => option.value).join(', '),\n description: () => plan?.description,\n price: () => format.money(planPrice(variant, planId)),\n savings: () => format.percent(planSavings(variant, planId).percentage),\n 'savings-amount': () => format.money(planSavings(variant, planId).amount),\n adjustment: adjustmentText,\n };\n return new Map(Object.entries(texts));\n}\n", "/**\n * Decoding of percent-escaped text the page hands Tether (a path segment, a cookie value), which nothing guarantees\n * is well formed.\n */\n\n/** @returns `text` decoded, or as written when it has a malformed escape (`%E0%A4%A`). */\nexport function decodeOrKeep(text: string): string {\n try {\n return decodeURIComponent(text);\n } catch (error) {\n if (error instanceof URIError) return text;\n throw error;\n }\n}\n", "/**\n * Where a product scope gets its key from, and the state of a scope (loading, ready, error, not found), so a failing\n * API never leaves a broken page.\n */\nimport { attr, sel, get, scoped, show } from './dom.ts';\nimport { decodeOrKeep } from './uri.ts';\n\ninterface ProductKeySource {\n /** The product to load; `null` when the attribute or the page URL has none. */\n key: string | null;\n /** The key comes from the page URL (`url:\u2026`). */\n fromUrl: boolean;\n /** The attribute's value as written. */\n raw: string;\n}\n\ntype ScopeStatus = 'loading' | 'ready' | 'error' | 'not-found';\n\nconst URL_SOURCE = 'url:';\n\n/**\n * Reads a `tether-product` value:\n * - `coffee-box`: a handle, numeric id, gid or product URL;\n * - `url:handle`: `?handle=` from the page URL (any parameter: `url:p` reads `?p=`);\n * - `url:path`: the last segment of the page path (`/shop/coffee-box` \u2192 `coffee-box`).\n *\n * @param name The attribute, without prefix.\n */\nexport function resolveProductKey(\n el: Element,\n location: Pick<Location, 'pathname' | 'search'> = window.location,\n name = 'product',\n): ProductKeySource {\n const raw = (get(el, name) ?? '').trim();\n if (!raw.toLowerCase().startsWith(URL_SOURCE)) return { key: raw || null, fromUrl: false, raw };\n const source = raw.slice(URL_SOURCE.length).trim();\n let key;\n if (source.toLowerCase() === 'path') {\n const segments = location.pathname.split('/').filter(Boolean);\n const segment = segments[segments.length - 1];\n // A malformed escape is kept as written, which is how the browser shows it.\n key = segment === undefined ? null : decodeOrKeep(segment);\n } else {\n key = new URLSearchParams(location.search).get(source || 'handle');\n }\n return { key: key?.trim() || null, fromUrl: true, raw };\n}\n\n/** The values of `tether-show` that follow the scope's state. */\nexport const STATUS_KEYS: readonly ScopeStatus[] = ['loading', 'ready', 'error', 'not-found'];\n\nconst isScopeStatus = (value: unknown): value is ScopeStatus => STATUS_KEYS.some((status) => status === value);\n\n/** @returns The error's `code` (a `TetherError` has one). */\nconst codeOf = (error: unknown): unknown =>\n typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n\n/** @returns True for a `TetherError` with code `NOT_FOUND`. */\nexport const isNotFound = (error: unknown): boolean => codeOf(error) === 'NOT_FOUND';\n\n/** @returns The selector of every scope that owns its state elements; the nearest one wins. */\nexport const boundary = (): string =>\n ['product', 'collection', 'products', 'recommendations'].map((name) => sel(name)).join(',');\n\n/** @returns True when `el` belongs to the scope `root`, not to a scope nested in it. */\nexport const ownedBy = (el: Element, root: Element): boolean =>\n el === root || el.parentElement?.closest(boundary()) === root;\n\n/** @returns The error's code for `tether-error`, `error` without one. */\nfunction errorCode(error: unknown): string {\n const code = codeOf(error);\n return typeof code === 'string' && code ? code : 'error';\n}\n\n/**\n * Sets `tether-state` (loading | ready | error) and `tether-error` (not-found or an error code) on `root`, toggles\n * `tether-show=\"loading|ready|error|not-found\"` inside it, and disables its add-to-cart buttons when there's no\n * product.\n */\nexport function setScopeStatus(root: Element, status: ScopeStatus, error?: unknown): void {\n const notFound = status === 'not-found';\n const failed = status === 'error' || notFound;\n root.setAttribute(attr('state'), notFound ? 'error' : status);\n if (failed) root.setAttribute(attr('error'), notFound ? 'not-found' : errorCode(error));\n else root.removeAttribute(attr('error'));\n\n scoped(root, sel('show'), boundary()).forEach((el) => {\n const shows = get(el, 'show');\n if (isScopeStatus(shows)) show(el, shows === status);\n });\n\n if (!failed) return;\n scoped(root, sel('add-to-cart'), boundary()).forEach((button) => {\n button.classList.add('is-disabled');\n button.setAttribute('aria-disabled', 'true');\n if ('disabled' in button) button.disabled = true;\n });\n}\n", "/**\n * The DOM writes of a product scope that follow the visitor's choice, on every change: `product-state.ts`'s view as\n * text, selected and unavailable states, `tether-show` conditions and the add-to-cart buttons.\n */\nimport { attr, sel, get, qsa, formatMoney, formatPercent, setText, setImage, setSelected, show } from './dom.ts';\nimport { STATUS_KEYS, ownedBy } from './status.ts';\nimport { allocationFor } from './selling-plans.ts';\nimport { isValueAvailable, planBindTexts, variantBindTexts } from './product-state.ts';\nimport { isSelect, selectsIn } from './product-render.ts';\nimport type { Formatters, ProductView } from './product-state.ts';\nimport type { RenderScope } from './product-render.ts';\n\nconst FORMAT: Formatters = { money: formatMoney, percent: formatPercent };\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\n/**\n * The element of an add-to-cart button that holds its label, with the label as written kept in\n * `data-tether-label`, so mode-dependent texts (`tether-subscribe-text`, `tether-soldout-text`) can be undone.\n */\nexport function addButtonLabel(button: HTMLElement): { label: HTMLElement; original: string } {\n const label = qsa(button, sel('add-label'))[0] ?? button;\n const original = label.dataset['tetherLabel'] ?? (isInput(label) ? label.value : label.textContent);\n label.dataset['tetherLabel'] = original;\n return { label, original };\n}\n\nfunction renderOptionStates(scope: RenderScope, view: ProductView): void {\n const { product } = scope;\n const selectedValue = (authored: string | null) => view.selected[scope.optName(authored)] ?? '';\n scope.$(sel('option-selected')).forEach((el) => {\n setText(el, selectedValue(get(el, 'option-selected')));\n });\n scope.$(sel('option-list')).forEach((container) => {\n const name = scope.optName(get(container, 'option-list'));\n qsa(container, sel('option-value')).forEach((node) => {\n const value = get(node, 'option-value') ?? '';\n setSelected(node, view.selected[name] === value);\n const unavailable = !isValueAvailable(product, view.selected, name, value);\n node.classList.toggle('is-unavailable', unavailable);\n node.setAttribute('data-unavailable', unavailable ? 'true' : 'false');\n });\n });\n selectsIn(scope, sel('option')).forEach((select) => {\n select.value = selectedValue(get(select, 'option'));\n });\n selectsIn(scope, sel('variant-select')).forEach((select) => {\n select.value = view.variant.id;\n });\n}\n\n/**\n * The purchase toggles and the plan list and select.\n */\nfunction renderPurchaseStates(scope: RenderScope, view: ProductView): void {\n const { variant, purchase, line } = view;\n scope.$(sel('purchase')).forEach((el) => {\n if (isSelect(el)) {\n el.value = purchase.kind;\n return;\n }\n const mode = get(el, 'purchase');\n setSelected(el, mode === purchase.kind);\n const unavailable = mode === 'subscription' ? !view.subscriptionAvailable : scope.product.requiresSellingPlan;\n el.classList.toggle('is-unavailable', unavailable);\n });\n scope.$(sel('plan-value')).forEach((node) => {\n const planId = get(node, 'plan-value') ?? '';\n setSelected(node, line.kind === 'subscription' && planId === line.planId);\n node.classList.toggle('is-unavailable', !allocationFor(variant, planId));\n const plan = scope.plans.find((candidate) => candidate.id === planId);\n const texts = planBindTexts(variant, planId, plan, FORMAT);\n qsa(node, sel('plan-bind')).forEach((el) => {\n const text = texts.get(get(el, 'plan-bind') ?? '');\n if (text) setText(el, text());\n });\n });\n selectsIn(scope, sel('plan')).forEach((select) => {\n select.value = line.kind === 'subscription' ? line.planId : '';\n });\n}\n\n/**\n * The `tether-show` conditions, with the scope's own status: a product scope that renders is ready.\n */\nfunction renderConditions(scope: RenderScope, view: ProductView): void {\n const conditions = new Map<string, boolean>(Object.entries(view.show));\n STATUS_KEYS.forEach((status) => conditions.set(status, status === 'ready'));\n scope.$(sel('show')).forEach((el) => {\n const key = get(el, 'show') ?? '';\n const visible = conditions.get(key);\n if (visible === undefined) return;\n // State blocks of a list nested in this product belong to that list.\n if (STATUS_KEYS.some((status) => status === key) && !ownedBy(el, scope.root)) return;\n show(el, visible);\n });\n}\n\nfunction renderAddButtons(scope: RenderScope, view: ProductView): void {\n const disabled = !view.variant.availableForSale;\n scope.$(sel('add-to-cart')).forEach((button) => {\n button.classList.toggle('is-disabled', disabled);\n button.setAttribute('aria-disabled', String(disabled));\n // A <button> or <input> is disabled; a link or div carries only aria-disabled.\n if ('disabled' in button) button.disabled = disabled;\n const soldOutText = get(button, 'soldout-text');\n const subscribeText = get(button, 'subscribe-text');\n if (!soldOutText && !subscribeText) return;\n const { label, original } = addButtonLabel(button);\n let text = original;\n if (view.line.kind === 'subscription' && subscribeText) text = subscribeText;\n if (disabled && soldOutText) text = soldOutText;\n if (button.getAttribute('aria-busy') !== 'true') setText(label, text);\n });\n}\n\n/**\n * Writes what follows the variant and purchase.\n */\nexport function writeVariant(scope: RenderScope, view: ProductView): void {\n const { variant } = view;\n const { product } = scope;\n const texts = variantBindTexts(view, FORMAT);\n scope.$(sel('bind')).forEach((el) => {\n const key = get(el, 'bind') ?? '';\n if (key === 'variant-image') {\n setImage(el, variant.image?.url || product.featuredImage?.url, variant.image?.altText || product.title);\n return;\n }\n const text = texts.get(key);\n if (text) setText(el, text());\n });\n renderOptionStates(scope, view);\n renderPurchaseStates(scope, view);\n renderConditions(scope, view);\n scope.root.classList.toggle('is-subscription', view.line.kind === 'subscription');\n scope.root.setAttribute(attr('purchase-mode'), view.line.kind);\n renderAddButtons(scope, view);\n}\n", "/**\n * A product scope's clicks and changes: option and plan buttons, purchase toggles, quantity steppers, the selects and\n * add to cart. One delegated listener of each kind on the root, so cards a list re-renders need no new listeners.\n */\nimport { reportError } from './config.ts';\nimport { sel, get, emit, setText, busy } from './dom.ts';\nimport { addButtonLabel } from './variant-render.ts';\nimport type { ProductScope } from './product.ts';\nimport type { Purchase } from './product-state.ts';\n\nconst productSelector = () => sel('product');\n\nconst isField = (el: Element): el is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA';\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/**\n * @returns The nearest `selector` around `target` that belongs to the scope, not to a nested one. Typed as an HTML\n * element, as `qsa` types its matches.\n */\nfunction closestOwn(scope: ProductScope, target: Element, selector: string): HTMLElement | null {\n const el = target.closest<HTMLElement>(selector);\n return el?.closest(productSelector()) === scope.root ? el : null;\n}\n\n/** Adds to the cart with `aria-busy` on the button and `tether-adding-text` as its label meanwhile. */\nfunction onAddClick(scope: ProductScope, button: HTMLElement): void {\n const { label, original } = addButtonLabel(button);\n const addingText = get(button, 'adding-text');\n busy(button, async () => {\n if (addingText) setText(label, addingText);\n try {\n await scope.addToCart();\n } catch (error) {\n reportError(error, 'add to cart failed');\n emit('error', { error, context: 'add-to-cart' }, scope.root);\n } finally {\n if (addingText) setText(label, original);\n }\n })\n .then(() => {\n // The mode-dependent labels (tether-subscribe-text, tether-soldout-text) wait for aria-busy to clear.\n scope.renderVariant();\n })\n .catch((error: unknown) => {\n reportError(error, 'add to cart render');\n });\n}\n\nfunction onClick(scope: ProductScope, event: Event, target: Element): void {\n const optionValue = closestOwn(scope, target, sel('option-value'));\n const optionList = optionValue?.closest(sel('option-list'));\n if (optionValue && optionList) {\n event.preventDefault();\n scope.select(scope.optName(get(optionList, 'option-list')), get(optionValue, 'option-value') ?? '');\n return;\n }\n const plan = closestOwn(scope, target, sel('plan-value'));\n if (plan) {\n event.preventDefault();\n scope.selectPlan(get(plan, 'plan-value'));\n return;\n }\n const purchase = closestOwn(scope, target, `${sel('purchase')}:not(select)`);\n if (purchase) {\n // A radio or checkbox input keeps its own click, so it shows checked.\n if (purchase.tagName !== 'INPUT') event.preventDefault();\n scope.setPurchase(purchaseKind(get(purchase, 'purchase')));\n return;\n }\n const plus = closestOwn(scope, target, sel('qty-plus'));\n const minus = plus ? null : closestOwn(scope, target, sel('qty-minus'));\n if (plus || minus) {\n event.preventDefault();\n scope.setQuantity(scope.quantity + (plus ? 1 : -1));\n return;\n }\n const add = closestOwn(scope, target, sel('add-to-cart'));\n if (add) {\n event.preventDefault();\n onAddClick(scope, add);\n }\n}\n\n/** @returns The kind a `tether-purchase` value asks for: `subscription`, else buying once. */\nconst purchaseKind = (value: string | null): Purchase['kind'] =>\n value === 'subscription' ? 'subscription' : 'one-time';\n\n/** @param target A form field in the scope. */\nfunction onChange(scope: ProductScope, target: Element): void {\n const value = isField(target) ? target.value : '';\n if (target.matches(`select${sel('option')}`)) {\n scope.select(scope.optName(get(target, 'option')), value);\n } else if (target.matches(`select${sel('variant-select')}`)) {\n scope.selectVariant(value);\n } else if (target.matches(`select${sel('plan')}`)) {\n if (value) scope.selectPlan(value);\n else scope.setPurchase('one-time');\n } else if (target.matches(`select${sel('purchase')}`)) {\n scope.setPurchase(purchaseKind(value));\n } else if (target.matches(sel('qty'))) {\n scope.setQuantity(value);\n }\n}\n\nexport function bindProductEvents(scope: ProductScope): void {\n scope.root.addEventListener('click', (event) => {\n if (isElement(event.target)) onClick(scope, event, event.target);\n });\n scope.root.addEventListener('change', (event) => {\n const { target } = event;\n if (isElement(target) && target.closest(productSelector()) === scope.root) onChange(scope, target);\n });\n}\n", "/**\n * ProductScope: everything inside [tether-product=\"<handle>\"] binds to one Shopify product: options, variants,\n * quantity, subscriptions and add to cart. The scope holds the visitor's choice and the actions on it;\n * `product-state.ts` derives what the choice shows, `product-render.ts` and `variant-render.ts` write it, and\n * `product-events.ts` turns clicks and changes into actions.\n */\nimport { cfg } from './config.ts';\nimport {\n attr,\n sel,\n get,\n has,\n scoped,\n emit,\n setText,\n notifyLayoutChanged,\n watchImages,\n parseJSONAttr,\n attributesFromJson,\n} from './dom.ts';\nimport { Cart } from './cart.ts';\nimport { allPlans, allocationFor, findPlan, GID } from './selling-plans.ts';\nimport {\n initialChoice,\n linePurchase,\n normalisePurchase,\n productView,\n purchasePlanId,\n selectionOf,\n} from './product-state.ts';\nimport { writeProduct } from './product-render.ts';\nimport { writeVariant } from './variant-render.ts';\nimport { bindProductEvents } from './product-events.ts';\nimport type { Product, ProductVariant } from './fragments.ts';\nimport type { ShopifyCart } from './cart/store.ts';\nimport type { PlanInGroup } from './selling-plans.ts';\nimport type { Purchase, Selection } from './product-state.ts';\n\n/** What a scope adds to the cart. */\ninterface CartLineInput {\n merchandiseId: string;\n quantity: number;\n sellingPlanId?: string;\n attributes?: { key: string; value: string }[];\n}\n\nexport const scopes = new WeakMap<Element, ProductScope>();\n\nconst productSelector = () => sel('product');\n\n/**\n * Option names as written in the markup (the build's language), by product handle. In another language Shopify\n * translates them (Size \u2192 Gr\u00F6\u00DFe), so markup names that aren't the product's are matched by position.\n */\nconst knownOptionNames = new Map<string, string[]>();\n\nexport function rememberOptionNames(products: readonly Pick<Product, 'handle' | 'options'>[]): void {\n products.forEach((product) => {\n if (!knownOptionNames.has(product.handle)) {\n knownOptionNames.set(\n product.handle,\n product.options.map((option) => option.name),\n );\n }\n });\n}\n\nconst isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';\n\nconst isField = (el: Element): el is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement =>\n el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA';\n\n/**\n * @param el A `tether-line-attribute` field.\n * @returns Its value for the cart line; `null` for an unchecked or empty field.\n */\nfunction lineAttributeValue(el: HTMLElement): string | null {\n if (isInput(el) && el.type === 'checkbox') {\n if (!el.checked) return null;\n return el.value && el.value !== 'on' ? el.value : 'Yes';\n }\n if (isInput(el) && el.type === 'radio') return el.checked ? el.value : null;\n return (isField(el) && el.value) || null;\n}\n\nexport class ProductScope {\n readonly root: HTMLElement;\n product: Product;\n plans: PlanInGroup[];\n selected: Selection;\n /** How the visitor buys; `setPurchase` and `selectPlan` change it. */\n purchase: Purchase;\n variant: ProductVariant | null;\n quantity: number;\n\n constructor(root: HTMLElement, product: Product) {\n this.root = root;\n this.product = product;\n this.plans = allPlans(product);\n const params = new URLSearchParams(window.location.search);\n const choice = initialChoice(product, {\n variant: params.get('variant') || get(root, 'variant'),\n sellingPlan: params.get('selling_plan'),\n defaultPlan: get(root, 'default-plan'),\n defaultPurchase: get(root, 'default-purchase') || cfg.defaultPurchase,\n });\n this.selected = choice.selected;\n this.purchase = choice.purchase;\n this.variant = null;\n this.quantity = has(root, 'quantity') ? Math.max(0, parseInt(get(root, 'quantity') ?? '', 10) || 0) : 1;\n\n scopes.set(root, this);\n root.setAttribute(attr('state'), 'ready');\n root.removeAttribute(attr('error'));\n this.renderProduct();\n bindProductEvents(this);\n this.renderVariant();\n\n watchImages(root, 'product');\n notifyLayoutChanged('product');\n emit('product-ready', { product, scope: this }, root);\n }\n\n /** New data for the same product (a country switch). Keeps the visitor's choice. */\n update(product: Product): void {\n const variantId = this.variant?.id;\n this.product = product;\n this.plans = allPlans(product);\n const same = product.variants.nodes.find((variant) => variant.id === variantId);\n if (same) this.selected = selectionOf(same);\n this.root.setAttribute(attr('state'), 'ready');\n this.root.removeAttribute(attr('error'));\n this.renderProduct();\n this.renderVariant();\n notifyLayoutChanged('product');\n emit('product-updated', { product, scope: this }, this.root);\n }\n\n /** @returns Matches in this scope, including the root itself (a card link with tether-bind=\"link\"). */\n $(selector: string): HTMLElement[] {\n const inner = scoped(this.root, selector, productSelector());\n return this.root.matches(selector) ? [this.root, ...inner] : inner;\n }\n\n /**\n * @param authored An option name in the markup.\n * @returns The product's option of that name, or at that position in the build's language.\n */\n optName(authored: string | null): string {\n if (!authored) return '';\n const { options } = this.product;\n if (options.some((option) => option.name === authored)) return authored;\n const index = (knownOptionNames.get(this.product.handle) ?? []).indexOf(authored);\n return options[index]?.name ?? authored;\n }\n\n get plan(): PlanInGroup | null {\n const planId = purchasePlanId(this.purchase);\n return this.plans.find((plan) => plan.id === planId) ?? null;\n }\n\n /** True when the current variant can be bought on a plan. */\n get subscriptionAvailable(): boolean {\n return this.plans.some((plan) => allocationFor(this.variant, plan.id));\n }\n\n /** True when the line is bought on a plan: subscribing, and the variant is on the plan. */\n get isSubscription(): boolean {\n return linePurchase(this.purchase, this.variant).kind === 'subscription';\n }\n\n /** @returns What would be added to the cart right now. */\n getLine(): CartLineInput | null {\n if (!this.variant) return null;\n const line: CartLineInput = { merchandiseId: this.variant.id, quantity: this.quantity };\n const bought = linePurchase(this.purchase, this.variant);\n if (bought.kind === 'subscription') line.sellingPlanId = bought.planId;\n const attributes = this.collectAttributes();\n if (attributes.length) line.attributes = attributes;\n return line;\n }\n\n /** @returns `tether-line-attributes` and the filled `tether-line-attribute` fields. */\n collectAttributes(): { key: string; value: string }[] {\n const fixed = attributesFromJson(parseJSONAttr(this.root, 'line-attributes'));\n const fields = this.$(sel('line-attribute')).flatMap((el) => {\n const value = lineAttributeValue(el);\n return value === null ? [] : [{ key: get(el, 'line-attribute') ?? '', value }];\n });\n return [...fixed, ...fields];\n }\n\n /**\n * Selects an option value. A combination no variant has moves to the closest variant with the value: the most other\n * choices kept, then one for sale.\n */\n select(name: string, value: string): void {\n if (this.selected[name] === value) return;\n const withValue = this.product.variants.nodes.filter((variant) =>\n variant.selectedOptions.some((option) => option.name === name && option.value === value),\n );\n const [firstWithValue] = withValue;\n // A value no variant has: nothing to select, the visitor's choice stays.\n if (!firstWithValue) return;\n const next = { ...this.selected, [name]: value };\n if (!withValue.some((variant) => variant.selectedOptions.every((option) => next[option.name] === option.value))) {\n const score = (variant: ProductVariant) => {\n const kept = variant.selectedOptions.filter(\n (option) => option.name !== name && this.selected[option.name] === option.value,\n ).length;\n // One kept choice outweighs being for sale.\n return kept * 2 + (variant.availableForSale ? 1 : 0);\n };\n const closest = withValue.reduce(\n (best, variant) => (score(variant) > score(best) ? variant : best),\n firstWithValue,\n );\n Object.assign(next, selectionOf(closest));\n }\n this.selected = next;\n this.renderVariant();\n }\n\n /** @param id A variant gid or numeric id. */\n selectVariant(id: string): void {\n const variant = this.product.variants.nodes.find((candidate) => candidate.id === GID.variant(id));\n if (!variant) return;\n this.selected = selectionOf(variant);\n this.renderVariant();\n }\n\n setPurchase(kind: Purchase['kind']): void {\n const next = normalisePurchase(this.product, this.variant, kind, purchasePlanId(this.purchase));\n // The product decides when the wanted kind isn't possible (no plans, or a plan required).\n if (next.kind === this.purchase.kind) return;\n this.purchase = next;\n this.renderVariant();\n }\n\n /** @param ref A plan id, numeric id, name or option value. */\n selectPlan(ref: string | null): void {\n const plan = findPlan(this.product, ref);\n if (!plan) return;\n this.purchase = { kind: 'subscription', planId: plan.id };\n this.renderVariant();\n }\n\n setQuantity(quantity: number | string): void {\n const minimum = has(this.root, 'quantity') ? 0 : 1;\n this.quantity = Math.max(minimum, parseInt(String(quantity), 10) || 0);\n this.$(sel('qty')).forEach((el) => {\n setText(el, this.quantity);\n });\n this.renderVariant();\n }\n\n /** @returns The cart after adding; `null` when the variant can't be added. */\n async addToCart(): Promise<ShopifyCart | null> {\n if (!this.variant?.availableForSale) return null;\n const line = this.getLine();\n if (!line || line.quantity < 1) return null;\n const cart = await Cart.add(line);\n emit('product-added', { product: this.product, variant: this.variant, line }, this.root);\n return cart;\n }\n\n /** Learns the markup's option names when they are the product's, then writes the product. */\n renderProduct(): void {\n const { product } = this;\n if (!knownOptionNames.has(product.handle)) {\n const authored = this.$(`${sel('option-list')},select${sel('option')}`).map(\n (el) => get(el, 'option-list') || get(el, 'option'),\n );\n if (authored.some((name) => product.options.some((option) => option.name === name))) {\n rememberOptionNames([product]);\n }\n }\n writeProduct(this);\n }\n\n /** Derives the view of the current choice, keeps its variant, selection and purchase, and writes it. */\n renderVariant(): void {\n const view = productView(\n this.product,\n { selected: this.selected, purchase: this.purchase, quantity: this.quantity },\n cfg.lowStock,\n );\n if (!view) {\n this.variant = null;\n return;\n }\n this.variant = view.variant;\n this.selected = view.selected;\n this.purchase = view.purchase;\n writeVariant(this, view);\n emit(\n 'variant-changed',\n {\n product: this.product,\n variant: view.variant,\n selected: { ...view.selected },\n purchase: view.purchase.kind,\n sellingPlanId: view.line.kind === 'subscription' ? view.line.planId : null,\n },\n this.root,\n );\n }\n}\n", "/**\n * Product lists: [tether-collection=\"handle\"], [tether-products=\"search query\"] and [tether-recommendations]. The\n * first child of [tether-list] is the card template; every card becomes a product scope, so all product attributes\n * work inside cards (including quick-add with a subscription toggle).\n */\nimport { reportError } from './config.ts';\nimport { TetherError } from './client.ts';\nimport {\n attr,\n sel,\n get,\n has,\n scoped,\n emit,\n setText,\n setStoreHtml,\n setImage,\n repeat,\n notifyLayoutChanged,\n busy,\n show,\n} from './dom.ts';\nimport { getProducts, getRecommendations } from './loader.ts';\nimport { setScopeStatus, isNotFound, resolveProductKey } from './status.ts';\nimport type { Product } from './fragments.ts';\nimport type { Collection, PageInfo, ProductQuery } from './loader.ts';\n\n/** `tether-products=\"url:q\"`: the search in the page's `?q=`, for a results page on a static site. */\nconst URL_QUERY_SOURCE = 'url:';\n\nexport const listSelector = () => `${sel('collection')},${sel('products')},${sel('recommendations')}`;\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/**\n * @param raw A `tether-limit`.\n * @returns The limit; `undefined` for none, zero or not a number.\n */\nconst toLimit = (raw: string | null): number | undefined => parseInt(raw ?? '', 10) || undefined;\n\n/**\n * @param raw A `tether-intent`.\n * @returns Shopify's recommendation intent, `RELATED` unless `COMPLEMENTARY` is written.\n */\nconst toIntent = (raw: string | null): 'RELATED' | 'COMPLEMENTARY' =>\n raw === 'COMPLEMENTARY' ? 'COMPLEMENTARY' : 'RELATED';\n\nexport class ListScope {\n readonly root: HTMLElement;\n products: Product[] = [];\n pageInfo: PageInfo | null = null;\n collection: Collection | null = null;\n /** Counts loads, so only the latest one renders. */\n loadToken = 0;\n\n constructor(root: HTMLElement) {\n this.root = root;\n setScopeStatus(root, 'loading');\n root.addEventListener('click', (event) => {\n if (!isElement(event.target)) return;\n const more = event.target.closest(sel('load-more'));\n if (more?.closest(listSelector()) !== root) return;\n event.preventDefault();\n void busy(more, () => this.load(true));\n });\n }\n\n $(selector: string) {\n return scoped(this.root, selector, listSelector());\n }\n\n /** The collection or search query of the list's attributes. */\n options(): ProductQuery {\n const { root } = this;\n const query: ProductQuery = { reverse: get(root, 'reverse') === 'true' };\n const first = toLimit(get(root, 'limit'));\n if (first) query.first = first;\n const sortKey = get(root, 'sort');\n if (sortKey) query.sortKey = sortKey;\n if (has(root, 'collection')) {\n query.collection = get(root, 'collection') ?? '';\n return query;\n }\n const raw = get(root, 'products') ?? '';\n query.query = raw.startsWith(URL_QUERY_SOURCE)\n ? new URL(window.location.href).searchParams.get(raw.slice(URL_QUERY_SOURCE.length)) || ''\n : raw;\n return query;\n }\n\n /**\n * The product of a recommendations list: a handle, `url:\u2026` (one dynamic product page), or empty inside a product\n * scope for that product.\n *\n * @throws {TetherError} `NOT_FOUND` when there is no product to recommend for.\n */\n async loadRecommendations(): Promise<Product[]> {\n let { key } = resolveProductKey(this.root, window.location, 'recommendations');\n const scope = this.root.closest(sel('product'));\n if (!key && scope) ({ key } = resolveProductKey(scope));\n if (!key) throw new TetherError('No product for recommendations', { code: 'NOT_FOUND' });\n return getRecommendations(key, toIntent(get(this.root, 'intent')));\n }\n\n /** @param more Appends the next page instead of replacing the products. */\n async load(more = false): Promise<void> {\n this.loadToken += 1;\n const token = this.loadToken;\n const stale = () => token !== this.loadToken;\n try {\n if (has(this.root, 'recommendations')) {\n const recommendations = await this.loadRecommendations();\n if (stale()) return;\n const limit = toLimit(get(this.root, 'limit'));\n this.products = limit ? recommendations.slice(0, limit) : recommendations;\n } else {\n const page = await getProducts({ ...this.options(), after: more ? (this.pageInfo?.endCursor ?? null) : null });\n if (stale()) return;\n this.collection = page.collection;\n this.pageInfo = page.pageInfo;\n this.products = more ? [...this.products, ...page.products] : page.products;\n }\n this.render();\n setScopeStatus(this.root, 'ready');\n emit('list-ready', { products: this.products, collection: this.collection }, this.root);\n } catch (error) {\n if (stale()) return;\n // A failed \"load more\" keeps what is rendered; otherwise the list shows its error state.\n if (!more) setScopeStatus(this.root, isNotFound(error) ? 'not-found' : 'error', error);\n // A missing product for recommendations is a page detail, not an error to report.\n if (!(isNotFound(error) && has(this.root, 'recommendations'))) reportError(error, 'list load failed');\n emit('error', { error, context: 'list' }, this.root);\n }\n }\n\n render() {\n const { collection } = this;\n this.$(sel('collection-bind')).forEach((el) => {\n switch (get(el, 'collection-bind') ?? '') {\n case 'title':\n setText(el, collection?.title ?? '');\n break;\n case 'description':\n setStoreHtml(el, collection?.descriptionHtml);\n break;\n case 'image':\n setImage(el, collection?.image?.url, collection?.image?.altText || collection?.title);\n break;\n case 'count':\n setText(el, this.products.length);\n break;\n }\n });\n\n this.$(sel('list')).forEach((container) => {\n repeat(container, this.products, (node, product) => {\n node.setAttribute(attr('product'), product.handle);\n node.removeAttribute(attr('state'));\n });\n // Cards are new elements: mounting them uses the product data already cached.\n emit('list-rendered', { container }, this.root);\n });\n\n const empty = this.products.length === 0;\n this.$(sel('show')).forEach((el) => {\n const key = get(el, 'show');\n if (key === 'list-empty') show(el, empty);\n if (key === 'list-filled') show(el, !empty);\n });\n this.$(sel('load-more')).forEach((el) => {\n show(el, Boolean(this.pageInfo?.hasNextPage));\n });\n notifyLayoutChanged('list');\n }\n}\n", "/**\n * The product scopes and lists on the page: mounting a product root, the live roots a country switch re-renders, and\n * the products the server rendered into the page. Shared by `index.ts` and the optional markets part.\n */\nimport { cfg, log, warn, reportError } from './config.ts';\nimport { ProductScope, scopes, rememberOptionNames } from './product.ts';\nimport { listSelector } from './collection.ts';\nimport { getProduct, takeStale, refreshProduct, hydrateProducts } from './loader.ts';\nimport { resolveProductKey, setScopeStatus, isNotFound } from './status.ts';\nimport { attr, get, qsa, emit } from './dom.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { isRecord } from '../server/src/shared/json.ts';\nimport { isProductShape } from './product-shape.ts';\nimport type { Product } from './fragments.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** A mounted `ListScope`. */\ninterface MountedList {\n root: Element;\n load: (more?: boolean) => Promise<unknown>;\n}\n\ntype KeySource = Pick<ReturnType<typeof resolveProductKey>, 'fromUrl' | 'raw'>;\n\n/**\n * The marker on the `<script type=\"application/json\">` tags `tether/server`'s `hydrate()` writes. It is fixed, not\n * `<prefix>-hydrate`, because the server writes it without knowing the browser's prefix.\n */\nconst HYDRATE_SELECTOR = 'script[type=\"application/json\"][data-tether-hydrate]';\n\nexport const mountedProducts = new WeakSet<Element>();\nexport const mountedLists = new WeakSet<Element>();\n/** Product roots to re-render after a country switch; pruned once disconnected. */\nexport const liveProducts = new Set<HTMLElement>();\n/** Lists to reload after a country switch; pruned once disconnected. */\nexport const liveLists = new Set<MountedList>();\n/** Each product root's latest request, so a slow older response can't win. */\nconst latestRequest = new WeakMap<Element, object>();\n\n/** Forgets roots a page transition removed, so they aren't kept alive. */\nexport function pruneLive(): void {\n liveProducts.forEach((el) => {\n if (!el.isConnected) liveProducts.delete(el);\n });\n liveLists.forEach((list) => {\n if (!list.root.isConnected) liveLists.delete(list);\n });\n}\n\n/** @returns `root` itself when it matches, then the matches inside it. */\nexport function within(root: ParentNode & Partial<Pick<Element, 'matches'>>, selector: string): HTMLElement[] {\n // A root that matches carries Tether's markup, which goes on HTML elements (see `qsa`).\n const self = root.matches?.(selector) ? [root as HTMLElement] : [];\n return [...self, ...qsa(root, selector)];\n}\n\n/** `tether-page-title=\"{title} | My shop\"` on a product root sets `document.title`, for dynamic product pages. */\nfunction applyPageTitle(el: Element, product: Product): void {\n const template = get(el, 'page-title');\n if (!template || typeof document === 'undefined') return;\n document.title = template\n .replace(/\\{title\\}/g, product.title)\n .replace(/\\{vendor\\}/g, product.vendor || '')\n .replace(/\\{type\\}/g, product.productType || '');\n}\n\n/**\n * A product root without a key: on a dynamic page opened without `?handle=` (or with an empty path) it is a product\n * that isn't there; inside a list it gets its handle when the list renders.\n */\nfunction mountWithoutKey(el: HTMLElement, { fromUrl, raw }: KeySource): void {\n if (fromUrl) {\n mountedProducts.add(el);\n setScopeStatus(el, 'not-found');\n emit('product-not-found', { key: null, source: raw }, el);\n } else if (!el.closest(listSelector())) {\n warn(`[${attr('product')}] without a value. Bind it to the Shopify handle field, or use \"url:handle\".`, el);\n }\n}\n\nfunction showLoadFailure(el: HTMLElement, key: string, { fromUrl, raw }: KeySource, error: unknown): void {\n const notFound = isNotFound(error);\n setScopeStatus(el, notFound ? 'not-found' : 'error', error);\n if (!notFound) {\n reportError(error, `product ${key}`);\n emit('error', { error, context: 'product', key }, el);\n return;\n }\n emit('product-not-found', { key, source: raw }, el);\n // A typo in a URL isn't worth an alert; a CMS item pointing at a deleted product is.\n if (fromUrl) log(`product not found: ${key}`);\n else reportError(error, `product ${key}`);\n}\n\n/**\n * Mounts one product root.\n *\n * @returns True when a product request was started.\n */\nexport function mountProduct(el: HTMLElement): boolean {\n if (mountedProducts.has(el)) return false;\n const { key, ...source } = resolveProductKey(el);\n if (!key) {\n mountWithoutKey(el, source);\n return false;\n }\n mountedProducts.add(el);\n liveProducts.add(el);\n const request = {};\n latestRequest.set(el, request);\n const isLatest = () => latestRequest.get(el) === request;\n // A rendered scope keeps showing its content while it refreshes (a country switch).\n if (scopes.get(el)) el.setAttribute(attr('state'), 'updating');\n else setScopeStatus(el, 'loading');\n const loading = trackMarket(getProduct(key));\n loading\n .then((product) => {\n if (!isLatest()) return;\n const existing = scopes.get(el);\n if (existing) existing.update(product);\n else new ProductScope(el, product);\n applyPageTitle(el, product);\n if (cfg.revalidate && takeStale(key)) queueRefresh(key);\n })\n .catch((error: unknown) => {\n if (isLatest()) showLoadFailure(el, key, source, error);\n });\n return true;\n}\n\n/** Loads again the product scopes that failed on a network or API error, for when the browser is back online. */\nexport function retryFailed(): void {\n pruneLive();\n liveProducts.forEach((el) => {\n if (el.getAttribute(attr('state')) !== 'error' || el.getAttribute(attr('error')) === 'not-found') return;\n mountedProducts.delete(el);\n mountProduct(el);\n });\n}\n\n/** Server-rendered products to refresh in the next batch. */\nlet refreshQueue = new Set<string>();\n\nfunction queueRefresh(key: string): void {\n if (!refreshQueue.size) setTimeout(flushRefresh, 0);\n refreshQueue.add(key);\n}\n\n/** @param key The key the refresh was asked for. */\nfunction applyRefresh(key: string, fresh: Product): void {\n liveProducts.forEach((el) => {\n const scope = scopes.get(el);\n if (!scope || !el.isConnected) return;\n const rootKey = resolveProductKey(el).key;\n if (rootKey !== key && rootKey !== fresh.handle && rootKey !== fresh.id) return;\n if (JSON.stringify(scope.product) !== JSON.stringify(fresh)) scope.update(fresh);\n });\n}\n\n/** One batched background request refreshes every server-rendered product queued. */\nfunction flushRefresh() {\n const keys = [...refreshQueue];\n refreshQueue = new Set();\n keys.forEach((key) => {\n refreshProduct(key)\n .then((fresh) => {\n applyRefresh(key, fresh);\n })\n .catch((error: unknown) => {\n reportError(error, `refresh ${key}`);\n });\n });\n}\n\n/**\n * While the page's server-rendered prices are another market's: the loads to wait for before\n * `html.tether-market-pending` goes. `null` when no market is pending.\n */\nlet marketPending: Set<Promise<unknown>> | null = null;\n\n/**\n * Lists, and anything else that loads prices, hold `html.tether-market-pending` until they are in.\n *\n * @returns `loading` itself.\n */\nexport const trackMarket = <T>(loading: Promise<T>): Promise<T> => {\n marketPending?.add(loading);\n return loading;\n};\n\n/** After a mount: drops `html.tether-market-pending` once this page's products are live. */\nexport function settleMarket(): void {\n if (!marketPending) return;\n const waits = [...marketPending];\n void Promise.allSettled(waits).then(() => {\n if (!marketPending || waits.some((wait) => !marketPending?.has(wait))) return;\n if (waits.length !== marketPending.size) {\n settleMarket(); // more loads came in meanwhile\n return;\n }\n marketPending = null;\n document.documentElement.classList.remove(`${cfg.prefix}-market-pending`);\n });\n}\n\n/** What `tether/server`'s `hydrate()` writes: the market it fetched in, and the products. */\ninterface HydratePayload {\n country: CountryCode | undefined;\n language: LanguageCode | undefined;\n products: Product[];\n}\n\n/** @param json A hydrate tag's JSON. */\nfunction toHydratePayload(json: unknown): HydratePayload {\n if (!isRecord(json)) return { country: undefined, language: undefined, products: [] };\n const { products } = json;\n const entries: unknown[] = Array.isArray(products) ? products : [];\n return {\n country: toCountry(json['country']) ?? undefined,\n language: toLanguage(json['language']) ?? undefined,\n products: entries.filter(isProductShape),\n };\n}\n\n/** Hydrate tags already read. */\nconst readScripts = new WeakSet<Element>();\n/**\n * Uses the products in the page's hydrate tags (`tether/server` `hydrate()`) without a request. A tag made for another\n * market is skipped, and marks the page (`html.tether-market-pending`) until the live prices are in, so CSS can hide\n * prices meanwhile.\n *\n * Trust: a hydrate tag is part of the page, written by the site's own build like the rest of its HTML and scripts, so\n * its prices and products are shown until the background refresh replaces them. Whoever can write a tag into the page\n * can already write a script there; the shape check (`isProductShape`) is for a truncated or hand-written tag, which\n * is skipped instead of failing a render.\n *\n * @param root Where to look: the document for a mount (a tag may sit in `<head>` or anywhere else on the page), the\n * added elements for `observe`.\n */\nexport function hydrateFromPage(root: Document | Element = document): void {\n within(root, HYDRATE_SELECTOR).forEach((script) => {\n if (readScripts.has(script)) return;\n readScripts.add(script);\n let json: unknown;\n try {\n json = JSON.parse(script.textContent);\n } catch (error) {\n warn('bad data-tether-hydrate', error);\n return;\n }\n const { country, language, products } = toHydratePayload(json);\n // The build's option names, also for other languages.\n rememberOptionNames(products);\n if (country === cfg.country && language === cfg.language) {\n hydrateProducts(products);\n return;\n }\n log('hydration skipped: other market');\n if (!marketPending) {\n marketPending = new Set();\n document.documentElement.classList.add(`${cfg.prefix}-market-pending`);\n }\n });\n}\n", "/**\n * Markets: which country and language prices, currency and translations follow.\n *\n * markets: {\n * detect: true, // or a list: ['saved', 'server', 'webflow']\n * locales: { // Webflow Localization: locale subdirectory or <html lang>\n * de: { country: 'DE', language: 'DE', locale: 'de-DE' },\n * 'fr-be': { country: 'BE', language: 'FR' },\n * },\n * cookie: 'tether_country', // a cookie your server or Worker sets (from cf-ipcountry)\n * remember: true, // keep the visitor's tether-country-select choice\n * }\n *\n * Sources, first match wins per field:\n * saved the visitor's own choice (localStorage), from tether-country-select / Tether.setCountry()\n * server window.TETHER_COUNTRY, <meta name=\"tether-country\" content=\"DE\">, or the cookie\n * webflow locale subdirectory (/de/...) or <html lang=\"de-DE\">, through `locales`\n * then the static `country` / `language` config. Without `detect`, only `saved` applies, so a country select keeps\n * working across pages; `detect: false` turns even that off. Tether never calls an IP lookup service: a country from\n * the visitor's IP only comes from the site's own server (docs/reference.md, Markets and localization).\n */\nimport { MARKET_SOURCES, cfg, parseMarkets } from './config.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { getStored, setStored } from './storage.ts';\nimport { decodeOrKeep } from './uri.ts';\nimport type { MarketsConfig } from './config.ts';\nimport type { CountryCode, LanguageCode } from './market-codes.ts';\n\n/** A market a visitor chose, as `setCountry()` stores it. */\nexport interface Market {\n country: CountryCode;\n language: LanguageCode | undefined;\n /** An `Intl` locale for money and dates. */\n locale: string | undefined;\n}\n\nexport type MarketSource = 'saved' | 'server' | 'webflow';\ntype MarketField = 'country' | 'language' | 'locale';\ntype MarketSources = Partial<Record<MarketField, MarketSource>>;\n\n/** The fields detection found, and the source each came from. */\nexport interface DetectedMarket {\n country?: CountryCode;\n language?: LanguageCode;\n locale?: string;\n /** Absent when `detect: false` turns detection off. */\n sources?: MarketSources;\n}\n\n/** What detection reads, passed in so it runs on any document. */\ninterface MarketEnv {\n document: Pick<Document, 'cookie' | 'querySelector' | 'documentElement'> | null;\n location: Pick<Location, 'pathname'> | null;\n /** `localStorage`, read through `getStored`, which finds nothing where the browser blocks it. */\n storage: Pick<Storage, 'getItem'>;\n /** Where `TETHER_COUNTRY` and `TETHER_LANGUAGE` are read from (`window`). */\n global: object;\n}\n\n/** A source's raw values. */\ninterface MarketHint {\n country?: unknown;\n language?: unknown;\n locale?: unknown;\n}\n\ninterface NamedCode {\n isoCode: string;\n name: string;\n}\n\ninterface Currency {\n isoCode: string;\n symbol: string;\n}\n\nexport type LocalizedCountry = NamedCode & { currency: Currency };\n\n/** The store's markets, from the Storefront API's `localization`. */\nexport interface Localization {\n /** The country of the current `@inContext`. */\n country: LocalizedCountry;\n language: NamedCode;\n availableCountries: (LocalizedCountry & { availableLanguages: NamedCode[] })[];\n}\n\nconst DEFAULT_COOKIE = 'tether_country';\n\n/** The localStorage key of the visitor's market: `<prefix>:market:<domain>`. */\nexport const storageKey = (): string => `${cfg.prefix}:market:${cfg.domain}`;\n\nconst pageStorage: Pick<Storage, 'getItem'> = { getItem: (key) => getStored(key) };\n\n/** The page this script runs on. */\nfunction pageEnv(): MarketEnv {\n const hasWindow = typeof window !== 'undefined';\n return {\n document: typeof document !== 'undefined' ? document : null,\n location: hasWindow ? window.location : null,\n storage: pageStorage,\n global: hasWindow ? window : {},\n };\n}\n\n/** The JSON value of `text`, `null` for text that isn't JSON (a key another script wrote). */\nfunction parseJsonOrNull(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch (error) {\n if (error instanceof SyntaxError) return null;\n throw error;\n }\n}\n\n/** `value` as a market, when it has a valid country. A language or locale that isn't valid is left out. */\nfunction toMarket(value: unknown): Market | null {\n if (typeof value !== 'object' || value === null) return null;\n const country = toCountry(Reflect.get(value, 'country'));\n if (!country) return null;\n const locale: unknown = Reflect.get(value, 'locale');\n return {\n country,\n language: toLanguage(Reflect.get(value, 'language')) ?? undefined,\n locale: typeof locale === 'string' && locale ? locale : undefined,\n };\n}\n\n/**\n * A market as `rememberMarket` stores it, validated: storage is written by other tabs, older versions and other\n * scripts too.\n *\n * @param text The value stored under `storageKey()`.\n * @returns `null` when it isn't a valid market.\n */\nexport const parseSavedMarket = (text: string): Market | null => toMarket(parseJsonOrNull(text));\n\n/**\n * The visitor's saved market (`setCountry()`, `tether-country-select`).\n *\n * @param storage Default `localStorage`.\n * @returns `null` when nothing valid is saved.\n */\nexport function readSavedMarket(storage: Pick<Storage, 'getItem'> = pageStorage): Market | null {\n const text = storage.getItem(storageKey());\n return text ? parseSavedMarket(text) : null;\n}\n\n/** @returns The cookie's value; one with a malformed escape is kept as written, which no country code is. */\nfunction readCookie(doc: Pick<Document, 'cookie'> | null, name: string): string | null {\n if (!doc || !name) return null;\n const cookie = doc.cookie\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(`${name}=`));\n return cookie ? decodeOrKeep(cookie.slice(name.length + 1)) : null;\n}\n\n/** The entry of `locales` for `key`, ignoring case. */\nfunction lookupLocale(locales: MarketsConfig['locales'], key: string | undefined): MarketHint | null {\n if (!locales || !key) return null;\n const wanted = key.toLowerCase();\n const found = Object.keys(locales).find((name) => name.toLowerCase() === wanted);\n return found ? (locales[found] ?? null) : null;\n}\n\nfunction readServerHint(markets: MarketsConfig, env: MarketEnv): MarketHint {\n const meta = (name: string) => env.document?.querySelector(`meta[name=\"${name}\"]`)?.getAttribute('content');\n const globalCountry: unknown = Reflect.get(env.global, 'TETHER_COUNTRY');\n const globalLanguage: unknown = Reflect.get(env.global, 'TETHER_LANGUAGE');\n const pageCountry = meta('tether-country') || readCookie(env.document, markets.cookie || DEFAULT_COOKIE);\n return {\n country: globalCountry || pageCountry,\n language: globalLanguage || meta('tether-language'),\n };\n}\n\nfunction readWebflowLocale(markets: MarketsConfig, env: MarketEnv): MarketHint | null {\n const segment = (env.location?.pathname ?? '/').split('/').find((part) => part !== '');\n const fromPath = lookupLocale(markets.locales, segment);\n if (fromPath) return fromPath;\n const lang = env.document?.documentElement.getAttribute('lang');\n if (!lang) return null;\n const mapped = lookupLocale(markets.locales, lang) ?? lookupLocale(markets.locales, lang.split('-')[0]);\n if (mapped) return { locale: lang, ...mapped };\n if (markets.locales && markets.strict) return null;\n // Unmapped: the language from <html lang>, a country only from a region subtag (de-CH -> CH).\n const region = lang.split('-')[1];\n return { language: lang, country: region?.length === 2 ? region : null, locale: lang };\n}\n\nfunction readSource(source: MarketSource, markets: MarketsConfig, env: MarketEnv): MarketHint | null {\n switch (source) {\n case 'saved':\n return markets.remember === false ? null : readSavedMarket(env.storage);\n case 'server':\n return readServerHint(markets, env);\n case 'webflow':\n return readWebflowLocale(markets, env);\n }\n}\n\n/**\n * @param detect Validated: a list holds only known sources.\n * @returns Without `detect`, only the visitor's own choice from a country select.\n */\nfunction sourcesFor(detect: MarketsConfig['detect']): readonly MarketSource[] {\n if (!detect) return ['saved'];\n return detect === true ? MARKET_SOURCES : detect;\n}\n\n/**\n * The market the page's sources point to. Reads the DOM and storage only, so it can run on any `env`.\n *\n * @returns Only the fields found; `{}` with `detect: false`.\n */\nexport function detectMarket(\n markets: MarketsConfig | null | undefined = cfg.markets,\n env: MarketEnv = pageEnv(),\n): DetectedMarket {\n // The config's own `markets` was validated by `configure()`; one passed in by the site's code is validated here.\n const config = (markets === cfg.markets ? markets : parseMarkets(markets)) ?? {};\n if (config.detect === false) return {};\n const sources: MarketSources = {};\n const found: DetectedMarket = { sources };\n for (const source of sourcesFor(config.detect)) {\n const hint = readSource(source, config, env);\n if (!hint) continue;\n const country = toCountry(hint.country);\n const language = toLanguage(hint.language);\n const locale = typeof hint.locale === 'string' && hint.locale ? hint.locale : null;\n if (country && !found.country) {\n found.country = country;\n sources.country = source;\n }\n if (language && !found.language) {\n found.language = language;\n sources.language = source;\n }\n if (locale && !found.locale) {\n found.locale = locale;\n sources.locale = source;\n }\n }\n return found;\n}\n\n/** Saves the visitor's choice for the next pages, unless `markets.remember` is false. */\nexport function rememberMarket(market: {\n country: CountryCode;\n language?: string | null | undefined;\n locale?: string | null | undefined;\n}): void {\n if (cfg.markets?.remember === false) return;\n const saved = { country: market.country, language: market.language || undefined, locale: market.locale || undefined };\n setStored(storageKey(), JSON.stringify(saved));\n}\n", "/**\n * The market the page uses: a page built for one market (`<meta name=\"tether-page-country\">`) wins, then the visitor's\n * saved or detected market, then the configured one. Init applies it to `cfg` once. Astro's ClientRouter keeps the\n * script running across pages, so each arrival asks again: the next page may be built for another market, or for none.\n */\nimport { cfg, log, reportError } from './config.ts';\nimport { loadPart } from './lazy.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { readSavedMarket } from './markets.ts';\nimport type { TetherOptions } from './config.ts';\nimport type { Market, DetectedMarket } from './markets.ts';\nimport type { Parts } from './lazy.ts';\nimport type { ShopifyCart } from './cart/store.ts';\n\nexport type SetCountryOptions = Parameters<Parts['markets']['setCountry']>[1];\n\n/** The market of the config, before a page's or visitor's market applied. */\ninterface ConfiguredMarket {\n country: string | undefined;\n language: string | undefined;\n locale: string | undefined;\n}\n\nlet configuredMarket: ConfiguredMarket = { country: undefined, language: undefined, locale: undefined };\n\n/**\n * Whether the page arrived at last was built for its own market, which the next page without one switches back from.\n */\nlet lastPageHadOwnMarket = false;\n\n/**\n * `<meta name=\"tether-page-country\" content=\"DE\">`, with `tether-page-language` and `tether-page-locale`. Never\n * remembered: it belongs to the page, not to the visitor.\n *\n * @returns `null` when the page has no valid country of its own.\n */\nfunction pageMarket(): Market | null {\n const meta = (name: string) =>\n document.querySelector(`meta[name=\"${cfg.prefix}-page-${name}\"]`)?.getAttribute('content') || undefined;\n const country = toCountry(meta('country'));\n if (!country) return null;\n return { country, language: toLanguage(meta('language')) ?? undefined, locale: meta('locale') };\n}\n\n/**\n * Without `markets.detect` only the visitor's saved choice applies, read without loading the markets part.\n */\nasync function visitorMarket(): Promise<Market | DetectedMarket | null> {\n const markets = cfg.markets ?? {};\n if (markets.detect === false) return null;\n if (!markets.detect) return markets.remember === false ? null : readSavedMarket();\n try {\n return (await loadPart('markets')).detectMarket(markets);\n } catch (error) {\n reportError(error, 'markets');\n return null;\n }\n}\n\n/**\n * Sets `cfg`'s market for the first page, over the configured one.\n *\n * @param overrides What `init()` was called with: a `locale` there wins over the visitor's.\n */\nexport async function applyStartMarket(overrides: TetherOptions): Promise<void> {\n configuredMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };\n const page = pageMarket();\n lastPageHadOwnMarket = page !== null;\n if (page) {\n cfg.country = page.country;\n cfg.language = page.language;\n if (page.locale) cfg.locale = page.locale;\n return;\n }\n const market = await visitorMarket();\n if (!market) return;\n if (market.country) cfg.country = market.country;\n if (market.language) cfg.language = market.language;\n if (market.locale && !overrides.locale) cfg.locale = market.locale;\n log('market', market);\n}\n\nconst sameCode = (a: string | undefined, b: string | undefined) => (a ?? '').toUpperCase() === (b ?? '').toUpperCase();\n\n/**\n * The market to switch to on arriving at the current page. A page built for a market switches to it; the first page\n * without one after such a page switches back to the visitor's own choice, else to the configured market.\n *\n * @returns `null` when the page stays in the market in use.\n */\nexport function marketForArrival(): {\n country: string;\n language: string | undefined;\n locale: string | undefined;\n} | null {\n const page = pageMarket();\n const leftOwnMarket = page === null && lastPageHadOwnMarket;\n lastPageHadOwnMarket = page !== null;\n const target = page ?? (leftOwnMarket ? (readSavedMarket() ?? configuredMarket) : null);\n if (!target?.country) return null;\n if (sameCode(target.country, cfg.country) && sameCode(target.language, cfg.language)) return null;\n return { country: target.country, language: target.language, locale: target.locale };\n}\n\n/**\n * `Tether.setCountry('DE')`: loads the markets part on first use.\n *\n * @returns The cart once everything is in the new market.\n */\nexport function setCountry(code: string, options?: SetCountryOptions): Promise<ShopifyCart | null> {\n return loadPart('markets').then((part) => part.setCountry(code, options));\n}\n", "export const PART_INTEGRITY = {\"tether.diagnose.min.js\":\"sha384-GoSabxXrpEUWCpbWmyCt6Eby/SRGcvuJ1c8lQFVaLprF1MbfyZwlfundN9SJzHlS\",\"tether.markets.min.js\":\"sha384-ZCR9y6ze/DzdKq13UxtHqcuWrujwCRgkMkock5VjQXA6svPWzCbim2/jseMyhsji\",\"tether.search.min.js\":\"sha384-LdRf9V4s9Z9Jq0JASRj5LoGHLRYj98SnSKldeLxslwGFEuYTFVMYviMDVYORA9sN\",\"tether.extras.min.js\":\"sha384-xKG8mQaRI9O7rOHGPtEMFCjCa5GJt8v1Pg+aUi1DnKMMCcRhbtXeB66FkiIBValW\",\"tether.account.min.js\":\"sha384-bsEg4NX+L08ZQv6v4gRa9tvI5FBWjVBUPfgb7MAB9P0htKO+vWs0AetLGfVcHPWU\",\"tether.account-pages.min.js\":\"sha384-PmV835ZgO/6emk4R8FSrpmAbAL4l9uacnyY6qc4RPauLLwB3GCQzxIKjbhlvKhak\",\"tether.analytics.min.js\":\"sha384-PCfUr7l9kFJGvf6kowF32Q4j+PYp6VbtG+RAiqU7A1rXqHlSE3mf+NlhiOInGIC6\"};\n", "/**\n * Where the `<script>` build's parts meet the core: `window.Tether` as the core script set it up (`TetherInternals`).\n * Another script can replace that global after the core, with an object that has none of it; each side checks it.\n */\nimport { isRecord } from '../server/src/shared/json.ts';\nimport type { PartName, Parts, TetherInternals } from './lazy.ts';\n\n/** @returns True when `value` carries the core's modules and parts, as the core script's `window.Tether` does. */\nexport function isTetherInternals(value: unknown): value is TetherInternals {\n return isRecord(value) && isRecord(value['__core']) && isRecord(value['__parts']);\n}\n\n/**\n * Registers a part's module with the core that loads it.\n *\n * @throws {Error} When `window.Tether` isn't the core script's: the part ran without it, or another script replaced it.\n */\nexport function registerPart<Name extends PartName>(name: Name, part: Parts[Name]): void {\n const tether: unknown = Reflect.get(window, 'Tether');\n if (!isTetherInternals(tether)) {\n throw new Error(`tether.${name}.js runs after the Tether script, which loads it, and needs its window.Tether`);\n }\n tether.__parts[name] = part;\n}\n", "// Set by each release (a test checks it matches package.json).\nexport const VERSION = '0.8.1';\n", "/**\n * `<script>` build: the optional parts are separate files next to tether(.min).js (tether.markets.min.js), loaded on\n * first use and checked against the hashes the core was built with, from that folder (on jsDelivr, the core's exact\n * release folder) or from `partsUrl`, which must serve the files of the same release. A part registers itself in\n * `window.Tether.__parts` and uses the core modules in `window.Tether.__core` instead of bundling copies.\n */\nimport { cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn } from './config.ts';\nimport { endpoint, gql, orNull, shaped, TetherError } from './client.ts';\nimport {\n ANALYTICS_EVENTS,\n attr,\n attributesFromJson,\n emit,\n formatDate,\n formatMoney,\n get,\n has,\n hasTemplate,\n isOwnEvent,\n parseJSONAttr,\n productUrl,\n qsa,\n repeat,\n sel,\n setImage,\n setText,\n show,\n} from './dom.ts';\nimport { clearProductCache, getProduct } from './loader.ts';\nimport { allPlans } from './selling-plans.ts';\nimport { boundary, resolveProductKey } from './status.ts';\nimport { Cart } from './cart.ts';\nimport { scopes } from './product.ts';\nimport { listSelector } from './collection.ts';\nimport { liveLists, liveProducts, mountedProducts, mountProduct, pruneLive, within } from './mounting.ts';\nimport { toCountry, toLanguage } from './market-codes.ts';\nimport { setCountry } from './page-market.ts';\nimport { isHttpsUrl } from './shopify-values.ts';\nimport { PART_INTEGRITY } from './part-integrity.ts';\nimport { isTetherInternals } from './part-registry.ts';\nimport { VERSION } from './version.ts';\nimport type { Parts, PartName } from './lazy.ts';\n\n/**\n * The core exports the parts use, shared instead of bundled twice, by the file name scripts/build.ts maps a part's\n * imports to. Each file shares a list of values without their names: the build finds each value's export name in its\n * module and hands a part's import of that name the value at its position. The core so carries no export names and no\n * export a part doesn't use, and a part that imports one not listed here fails its build. A part reads its entries\n * when it loads, once every module has run; read while the core runs, an import cycle through this file could leave\n * a value unset.\n */\nexport const coreModules = {\n get 'config.ts'() {\n return [cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn];\n },\n get 'client.ts'() {\n return [endpoint, gql, orNull, shaped, TetherError];\n },\n get 'dom.ts'() {\n return [\n ANALYTICS_EVENTS,\n attr,\n attributesFromJson,\n emit,\n formatDate,\n formatMoney,\n get,\n has,\n hasTemplate,\n isOwnEvent,\n parseJSONAttr,\n productUrl,\n qsa,\n repeat,\n sel,\n setImage,\n setText,\n show,\n ];\n },\n get 'loader.ts'() {\n return [clearProductCache, getProduct];\n },\n get 'selling-plans.ts'() {\n return [allPlans];\n },\n get 'status.ts'() {\n return [boundary, resolveProductKey];\n },\n get 'cart.ts'() {\n return [Cart];\n },\n get 'product.ts'() {\n return [scopes];\n },\n get 'collection.ts'() {\n return [listSelector];\n },\n get 'mounting.ts'() {\n return [liveLists, liveProducts, mountedProducts, mountProduct, pruneLive, within];\n },\n get 'market-codes.ts'() {\n return [toCountry, toLanguage];\n },\n get 'page-market.ts'() {\n return [setCountry];\n },\n get 'shopify-values.ts'() {\n return [isHttpsUrl];\n },\n // Parts that load other parts (account \u2192 account-pages) use this same loader.\n get 'lazy.ts'() {\n return [loadPart];\n },\n};\n\nconst MINIFIED_SCRIPT = /\\.min\\.js(\\?|$)/;\nconst coreScript = typeof document === 'undefined' ? '' : (document.currentScript?.getAttribute('src') ?? '');\nconst coreScriptUrl = coreScript ? new URL(coreScript, document.baseURI).href : '';\n\n/**\n * A jsDelivr folder of a GitHub repository (`gh/user/repo`) or a scoped npm package (`npm/@scope/name`) whose version\n * is an alias: none, `latest` or a range such as `0.8`. jsDelivr caches each alias URL on its own for up to 7 days, so\n * through an alias a release's core can meet another release's part, which then fails its integrity check.\n */\nconst JSDELIVR_ALIAS = /^(https:\\/\\/cdn\\.jsdelivr\\.net\\/(gh|npm)\\/@?[^/@]+\\/[^/@]+)(@(latest|\\d+(\\.\\d+)?))?\\//;\n\n/**\n * @returns The folder, with a jsDelivr version alias replaced by the release this core is (`@v<VERSION>` for a GitHub\n * repository, whose tags are `vX.Y.Z`; `@<VERSION>` for npm).\n */\nexport function exactReleaseFolder(folder: string): string {\n return folder.replace(\n JSDELIVR_ALIAS,\n (_alias, path: string, source: string) => `${path}@${source === 'gh' ? 'v' : ''}${VERSION}/`,\n );\n}\n\n/** Parts loaded or loading. */\nconst parts = new Map<PartName, Promise<unknown>>();\n\n/**\n * The folder parts load from: the `partsUrl` (checked to be https or on this site when the config was resolved), else\n * the core script's own folder, at its exact release on jsDelivr. `null` when the core was not loaded by a\n * `<script src>` (a module, a loader) and no `partsUrl` is set, so nothing says where parts are.\n */\nfunction partsFolder(): string | null {\n if (cfg.partsUrl) return new URL(cfg.partsUrl, document.baseURI).href;\n if (!coreScriptUrl) return null;\n return exactReleaseFolder(coreScriptUrl.replace(/[^/]*$/, ''));\n}\n\n/**\n * Pins the script to the part file built with this core, wherever it loads from: a page that pins the core with\n * `integrity` then runs only parts of that build, and a `partsUrl` must serve the same release's files.\n */\nfunction applyIntegrity(script: HTMLScriptElement, integrity: string): void {\n script.integrity = integrity;\n // A cross-origin script is only checked when fetched with CORS (CDNs such as jsDelivr allow it).\n if (new URL(script.src).origin !== window.location.origin) script.crossOrigin = 'anonymous';\n}\n\n/**\n * Loads a part's file. A failed load leaves the cache, so the next use tries again (the network may be back).\n *\n * @param integrity The file's hash from this core's build.\n */\nfunction loadScript(name: PartName, folder: string, file: string, integrity: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const url = `${folder.replace(/\\/?$/, '/')}${file}`;\n const script = document.createElement('script');\n script.src = url;\n script.async = true;\n applyIntegrity(script, integrity);\n script.onload = () => {\n const tether: unknown = Reflect.get(window, 'Tether');\n const part = isTetherInternals(tether) ? tether.__parts[name] : undefined;\n if (part) {\n resolve(part);\n return;\n }\n parts.delete(name);\n const problem = isTetherInternals(tether)\n ? 'did not register'\n : \"can't register: window.Tether is no longer this Tether script's\";\n reject(new Error(`${url} ${problem}`));\n };\n script.onerror = () => {\n parts.delete(name);\n reject(new Error(`Could not load ${url}, or it is not the file built with this Tether script`));\n };\n document.head.appendChild(script);\n });\n}\n\n/**\n * @throws {Error} (rejected) When the part's file can't be loaded, fails its integrity check or doesn't register.\n */\nexport function loadPart<Name extends PartName>(name: Name): Promise<Parts[Name]> {\n let loading = parts.get(name);\n if (!loading) {\n const folder = partsFolder();\n if (folder === null) {\n return Promise.reject(\n new Error(`The Tether script's own location is unknown, so its ${name} part can't be found: set partsUrl.`),\n );\n }\n const suffix = MINIFIED_SCRIPT.test(coreScriptUrl) || !coreScriptUrl ? '.min' : '';\n const file = `tether.${name}${suffix}.js`;\n const integrity = PART_INTEGRITY[file];\n if (integrity === undefined) {\n return Promise.reject(new Error(`This Tether script was built without ${file}, so it can't load that part.`));\n }\n loading = loadScript(name, folder, file, integrity);\n parts.set(name, loading);\n }\n // A part's file is built from the module `Parts[Name]` names (scripts/build.ts), and registers that module.\n return loading as Promise<Parts[Name]>;\n}\n", "/**\n * What tells a page that customer accounts are in use in this browser: the \"may be logged in\" hint the account part\n * keeps in `localStorage`, and the Worker's `?account=` mark after a login. The core reads them to decide whether a\n * page loads the part, and the part writes the hint, so both take them from here.\n */\nimport { cfg } from './config.ts';\nimport { removeStored } from './storage.ts';\n\n/**\n * What the Worker's login redirect says in `?account=`: `in` after a login, `expired` when the login took too long,\n * `error` when it failed.\n */\ntype LoginOutcome = 'in' | 'expired' | 'error';\n\n/** The \"may be logged in\" hint's key: it makes the core load the part on the next page, and holds no secret. */\nexport const hintKey = (): string => `${cfg.prefix}:account-hint:${cfg.domain}`;\n\n/** Removes a key from both storage areas, each on its own: a blocked one doesn't keep the key in the other. */\nfunction removeFromBothAreas(key: string): void {\n removeStored(key, 'sessionStorage');\n removeStored(key, 'localStorage');\n}\n\n/**\n * Removes the customer tokens and pending login that Tether 0.6's browser-mode accounts kept in this browser. The\n * session held a refresh token with the customer's full scope, so it must not stay behind once accounts run through\n * the Worker. TODO: remove in 0.8.0, when no visitor's browser still holds a 0.6 session.\n */\nexport function forgetBrowserModeTokens(): void {\n removeFromBothAreas(`${cfg.prefix}:account:${cfg.domain}`);\n removeFromBothAreas(`${cfg.prefix}:account-login:${cfg.domain}`);\n}\n\nexport const isLoginOutcome = (value: string | null): value is LoginOutcome =>\n value === 'in' || value === 'expired' || value === 'error';\n", "/**\n * The core's side of customer accounts, which are an optional part: whether a page needs the part, and\n * `Tether.account`, whose functions load it on first call. Every function returns a promise, the part's own sync ones\n * (`isLoggedIn`) too, because the part may still have to load.\n */\nimport { cfg, reportError } from './config.ts';\nimport { attr } from './dom.ts';\nimport { loadPart } from './lazy.ts';\nimport { within } from './mounting.ts';\nimport { hintKey, isLoginOutcome } from './account-marks.ts';\nimport { getStored } from './storage.ts';\nimport type { Parts } from './lazy.ts';\n\ntype AccountPart = Parts['account'];\ntype AccountResult<Name extends keyof AccountPart> = Awaited<ReturnType<AccountPart[Name]>>;\ntype AccountFunction<Name extends keyof AccountPart> = (\n ...args: Parameters<AccountPart[Name]>\n) => Promise<AccountResult<Name>>;\ntype AccountApi = { [Name in keyof AccountPart]: AccountFunction<Name> };\n\n/** The attributes of the account markup: a page with one of them loads the part. */\nconst ACCOUNT_ATTRIBUTES = [\n 'account-login',\n 'account-bind',\n 'account-link',\n 'orders',\n 'order',\n 'addresses',\n 'subscriptions',\n 'profile-form',\n 'address-form',\n];\n\n/** @returns A selector for every element the account part fills in or shows. */\nconst accountSelector = (): string =>\n [...ACCOUNT_ATTRIBUTES.map((name) => `[${attr(name)}]`), `[${attr('show')}^=\"logged-\"]`].join(',');\n\n/**\n * Whether this browser keeps the \"may be logged in\" hint, so checkout on a page without account markup still knows\n * the customer.\n *\n * @returns False where the browser blocks storage, which then holds no hint.\n */\nfunction hasLoginHint(): boolean {\n return Boolean(getStored(hintKey()));\n}\n\n/**\n * Loads the account part and mounts it on `root`, when accounts are configured and the page needs them: account\n * markup in `root`, a login coming back from the Worker (`?account=`), or the \"may be logged in\" hint.\n */\nexport function mountAccountIfUsed(root: ParentNode): void {\n if (!cfg.account) return;\n const needed =\n within(root, accountSelector()).length > 0 ||\n isLoginOutcome(new URLSearchParams(window.location.search).get('account')) ||\n hasLoginHint();\n if (!needed) return;\n loadPart('account')\n .then((part) => part.mountAccount(root))\n .catch((error: unknown) => {\n reportError(error, 'account');\n });\n}\n\n/** @returns `name` of the account part, called once the part has loaded. */\nfunction fromPart<Name extends keyof AccountPart>(name: Name): AccountFunction<Name> {\n return (...args) =>\n loadPart('account').then((part) => {\n // `Name` picks one function of the part, and `args` are that function's parameters.\n const call = part[name] as (\n ...callArgs: Parameters<AccountPart[Name]>\n ) => AccountResult<Name> | PromiseLike<AccountResult<Name>>;\n return call(...args);\n });\n}\n\n/**\n * `Tether.account`: the account part's functions, each loading the part on first call. A plain object, so `await\n * Tether.account` and `Object.keys(Tether.account)` behave as for any object.\n */\nexport const accountApi: AccountApi = {\n login: fromPart('login'),\n logout: fromPart('logout'),\n isLoggedIn: fromPart('isLoggedIn'),\n customer: fromPart('customer'),\n orders: fromPart('orders'),\n order: fromPart('order'),\n updateProfile: fromPart('updateProfile'),\n addresses: fromPart('addresses'),\n saveAddress: fromPart('saveAddress'),\n deleteAddress: fromPart('deleteAddress'),\n subscriptions: fromPart('subscriptions'),\n subscriptionAction: fromPart('subscriptionAction'),\n requestReturn: fromPart('requestReturn'),\n render: fromPart('render'),\n mountAccount: fromPart('mountAccount'),\n};\n", "/**\n * The cart's markup controls, delegated from the document so they keep working across page swaps: open, close,\n * clear and checkout buttons, a line's +, - and remove buttons, its quantity input and plan select, the cart note, and\n * the keyboard of the open drawer (Escape closes it, Tab stays inside it).\n */\nimport { cfg, reportError } from '../config.ts';\nimport { Cart, trapFocus } from '../cart.ts';\nimport { sel, busy } from '../dom.ts';\n\n/** @returns The attribute a rendered cart line carries its line id in. */\nconst lineKeyAttribute = (): string => `data-${cfg.prefix}-key`;\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\n/** @returns The rendered cart line `target` is in, with its line id. */\nfunction lineOf(target: Element): { line: Element; id: string } | null {\n const line = target.closest(`[${lineKeyAttribute()}]`);\n const id = line?.getAttribute(lineKeyAttribute());\n return line && id ? { line, id } : null;\n}\n\nfunction reportFailure(change: Promise<unknown>, context: string): void {\n change.catch((error: unknown) => {\n reportError(error, context);\n });\n}\n\n/** A line's remove, + and - buttons. */\nfunction onLineClick(event: MouseEvent, target: Element): void {\n const found = lineOf(target);\n if (!found?.line.closest(sel('cart-lines'))) return;\n const { line, id } = found;\n if (target.closest(sel('line-remove'))) {\n event.preventDefault();\n line.classList.add('is-busy');\n reportFailure(Cart.removeLine(id), 'cart line');\n return;\n }\n const delta = lineDelta(target);\n if (!delta || !Cart.lines.some((candidate) => candidate.id === id)) return;\n event.preventDefault();\n reportFailure(Cart.changeQuantity(id, delta), 'cart line');\n}\n\n/** @returns The quantity change of a + or - button, 0 for anything else. */\nfunction lineDelta(target: Element): 1 | -1 | 0 {\n if (target.closest(sel('line-plus'))) return 1;\n if (target.closest(sel('line-minus'))) return -1;\n return 0;\n}\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n if (!isElement(target)) return;\n if (target.closest(sel('cart-open'))) {\n event.preventDefault();\n Cart.toggle();\n return;\n }\n if (target.closest(sel('cart-close'))) {\n event.preventDefault();\n Cart.close();\n return;\n }\n const clear = target.closest(sel('cart-clear'));\n if (clear) {\n event.preventDefault();\n void busy(clear, () =>\n Cart.clear().catch((error: unknown) => {\n reportError(error, 'cart clear');\n }),\n );\n return;\n }\n const checkout = target.closest(sel('checkout'));\n if (checkout) {\n event.preventDefault();\n void busy(checkout, () => Cart.checkout());\n return;\n }\n onLineClick(event, target);\n}\n\n/** The line shows `is-busy` and `aria-busy` until Shopify has answered; the next render removes them. */\nfunction showLineBusy(line: Element, change: Promise<unknown>, context: string): void {\n line.classList.add('is-busy');\n line.setAttribute('aria-busy', 'true');\n reportFailure(change, context);\n}\n\nfunction onChange(event: Event): void {\n const { target } = event;\n if (!isElement(target)) return;\n const found = lineOf(target);\n if (found && target instanceof window.HTMLInputElement && target.matches(sel('line', 'quantity'))) {\n showLineBusy(found.line, Cart.updateLine(found.id, parseInt(target.value, 10) || 0), 'cart line');\n } else if (found && target instanceof window.HTMLSelectElement && target.matches(sel('line-plan'))) {\n showLineBusy(found.line, Cart.updateLine(found.id, { sellingPlanId: target.value || null }), 'cart line plan');\n } else if (hasValue(target) && target.matches(sel('cart-note'))) {\n reportFailure(Cart.setNote(target.value), 'cart note');\n }\n}\n\nconst hasValue = (target: Element): target is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement =>\n target instanceof window.HTMLInputElement ||\n target instanceof window.HTMLTextAreaElement ||\n target instanceof window.HTMLSelectElement;\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (!Cart.isOpen()) return;\n if (event.key === 'Escape') {\n Cart.close();\n return;\n }\n trapFocus(event);\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindCartControls(): void {\n document.addEventListener('click', onClick);\n document.addEventListener('change', onChange);\n document.addEventListener('keydown', onKeydown);\n}\n", "/**\n * The discount code form (`tether-discount-form` with `tether-discount-input`) and the remove buttons of applied codes\n * (`tether-discount-remove` inside `tether-discount-code`), delegated from the document. A code Shopify refuses marks\n * the form `is-invalid` (and the input `aria-invalid`) until the visitor types again.\n */\nimport { reportError } from '../config.ts';\nimport { TetherError } from '../client.ts';\nimport { Cart } from '../cart.ts';\nimport { attr, sel, busy, emit } from '../dom.ts';\n\nconst isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;\n\nfunction codeInputOf(form: Element): HTMLInputElement | null {\n const input = form.querySelector(sel('discount-input')) ?? form.querySelector('input');\n return input instanceof window.HTMLInputElement ? input : null;\n}\n\nfunction markInvalid(form: Element, input: HTMLInputElement | null, invalid: boolean): void {\n form.classList.toggle('is-invalid', invalid);\n if (invalid) input?.setAttribute('aria-invalid', 'true');\n else input?.removeAttribute('aria-invalid');\n}\n\nasync function applyCode(form: Element, input: HTMLInputElement | null, code: string): Promise<void> {\n try {\n await Cart.applyDiscount(code);\n markInvalid(form, input, false);\n emit('discount-applied', { code }, form);\n if (input) input.value = '';\n } catch (error) {\n markInvalid(form, input, true);\n if (error instanceof TetherError && error.code === 'INVALID_DISCOUNT') emit('discount-invalid', { code }, form);\n else reportError(error, 'discount');\n }\n}\n\nfunction onSubmit(event: SubmitEvent): void {\n const { target } = event;\n const form = isElement(target) ? target.closest(sel('discount-form')) : null;\n if (!form) return;\n event.preventDefault();\n const input = codeInputOf(form);\n const code = input?.value.trim();\n if (!code) return;\n void busy(form, () => applyCode(form, input, code));\n}\n\nfunction onInput(event: Event): void {\n const { target } = event;\n if (!isElement(target)) return;\n const form = target.closest(sel('discount-form'));\n if (!form) return;\n form.classList.remove('is-invalid');\n target.removeAttribute('aria-invalid');\n}\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n const remove = isElement(target) ? target.closest(sel('discount-remove')) : null;\n if (!remove) return;\n event.preventDefault();\n const code = remove.closest(sel('discount-code'))?.getAttribute(attr('discount-code'));\n if (!code) return;\n Cart.removeDiscount(code).catch((error: unknown) => {\n reportError(error, 'discount');\n });\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindDiscountControls(): void {\n // Capture: the submit is handled before a router (Astro's ClientRouter) turns it into a navigation.\n document.addEventListener('submit', onSubmit, true);\n document.addEventListener('input', onInput);\n document.addEventListener('click', onClick);\n}\n", "/**\n * The document-wide listeners whose work is in the optional extras part, loaded when one of them first has work:\n * group add buttons, the cart or market changing in another tab, and shareable variant links. A part that can't load\n * (a self-hosted site without `tether.extras.min.js`) is reported, so the missing file shows.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { attr, sel, busy, isOwnEvent } from '../dom.ts';\nimport { loadPart } from '../lazy.ts';\nimport type { ShopifyCart } from '../cart/store.ts';\n\n/** `[tether-group]` with `[tether-group-add]`: adds every product scope inside the group in one request. */\nexport const addGroup = (button: Element): Promise<ShopifyCart | null> =>\n loadPart('extras').then((part) => part.addGroup(button));\n\nconst reportExtrasError = (error: unknown) => {\n reportError(error, 'extras');\n};\n\nfunction onClick(event: MouseEvent): void {\n const { target } = event;\n const button = target instanceof window.Element ? target.closest(sel('group-add')) : null;\n if (!button) return;\n event.preventDefault();\n busy(button, () => addGroup(button)).catch(reportExtrasError);\n}\n\nfunction onStorage(event: StorageEvent): void {\n if (!cfg.domain || !event.key?.startsWith(`${cfg.prefix}:`)) return;\n loadPart('extras')\n .then((part) => {\n part.onStorage(event);\n })\n .catch(reportExtrasError);\n}\n\n/** @param event `tether:variant-changed`, which a product scope emits; one another script dispatches is ignored. */\nfunction onVariantChanged(event: Event): void {\n if (!isOwnEvent(event, 'variant-changed')) return;\n const { target } = event;\n if (!(target instanceof window.Element)) return;\n if (!cfg.variantUrl && !target.hasAttribute(attr('variant-url'))) return;\n loadPart('extras')\n .then((part) => {\n part.writeVariantUrl(target, event.detail);\n })\n .catch(reportExtrasError);\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindExtrasControls(): void {\n document.addEventListener('click', onClick);\n window.addEventListener('storage', onStorage);\n document.addEventListener(`${cfg.prefix}:variant-changed`, onVariantChanged);\n}\n", "/**\n * How the page follows the visitor across pages and markets: Astro's ClientRouter (`astro:page-load`,\n * `astro:before-swap`) mounts each new page in its market and closes the cart while leaving; a country select\n * switches the market; back online, what failed to load loads again.\n */\nimport { cfg, reportError } from '../config.ts';\nimport { attr, sel } from '../dom.ts';\nimport { liveLists, retryFailed } from '../mounting.ts';\nimport { marketForArrival, setCountry } from '../page-market.ts';\n\n/** What the entry module does for a page. */\ninterface PageHooks {\n /** Settles once `init()` has applied the visitor's market. */\n ready: () => Promise<unknown>;\n mount: (root: Document) => unknown;\n unmount: () => void;\n}\n\n/** The body the last full-document mount ran on: Astro's first `astro:page-load` comes after that mount. */\nlet mountedBody: HTMLElement | null = null;\n\n/**\n * Records a mount of the whole document, so the `astro:page-load` of the same page sends no second round of requests.\n */\nexport function rememberDocumentMount(): void {\n mountedBody = document.body;\n}\n\n/** Mounts the page Astro swapped in. It rejects when `init()` failed or the mount threw; the listener reports that. */\nasync function onPageLoad(hooks: PageHooks): Promise<void> {\n await hooks.ready();\n const target = marketForArrival();\n if (!target) {\n if (mountedBody !== document.body) hooks.mount(document);\n return;\n }\n try {\n await setCountry(target.country, { language: target.language ?? null, locale: target.locale, remember: false });\n } catch (error) {\n reportError(error, 'country');\n }\n hooks.mount(document);\n}\n\nfunction bindAstroRouter(hooks: PageHooks): void {\n document.addEventListener('astro:page-load', () => {\n onPageLoad(hooks).catch((error: unknown) => {\n reportError(error, 'page mount');\n });\n });\n document.addEventListener('astro:before-swap', () => {\n mountedBody = null;\n hooks.unmount();\n });\n}\n\nfunction onChange(event: Event): void {\n const { target } = event;\n if (!(target instanceof window.HTMLSelectElement) || !target.matches(sel('country-select'))) return;\n if (!target.value) return;\n setCountry(target.value).catch((error: unknown) => {\n reportError(error, 'country');\n });\n}\n\n/** Loads the failed products and lists again. */\nfunction onOnline() {\n retryFailed();\n liveLists.forEach((list) => {\n if (list.root.getAttribute(attr('state')) === 'error') void list.load();\n });\n}\n\n/** Binds the document-wide listeners, once per page load of the script. */\nexport function bindNavigation(hooks: PageHooks): void {\n if (cfg.astro) bindAstroRouter(hooks);\n document.addEventListener('change', onChange);\n window.addEventListener('online', onOnline);\n}\n", "/**\n * Tether's entry: `window.Tether`, `init()`, `mount()` and the start of the plain `<script>` setup.\n *\n * <script>window.TETHER_CONFIG = { domain: 'x.myshopify.com', token: '...' }</script>\n * <script src=\"tether.min.js\" defer></script>\n *\n * With page transitions (Barba, Swup): set `autoMount: false`, call `Tether.init()` once and\n * `Tether.mount(container)` after every page enter.\n */\nimport { cfg, configView, configure, isConfigured, log, warn, reportError } from './config.ts';\nimport { query, TetherError, withContext } from './client.ts';\nimport { Cart } from './cart.ts';\nimport { scopes } from './product.ts';\nimport { ListScope, listSelector } from './collection.ts';\nimport {\n getProduct,\n getProducts,\n getRecommendations,\n clearProductCache,\n primeProduct,\n normalizeProductKey,\n} from './loader.ts';\nimport * as plans from './selling-plans.ts';\nimport { resolveProductKey } from './status.ts';\nimport { sel, emit, isOwnEvent, ANALYTICS_EVENTS, formatMoney, imageUrl, pageGlobal } from './dom.ts';\nimport { loadPart, coreModules } from './lazy.ts';\nimport { VERSION } from './version.ts';\nimport {\n mountedLists,\n liveLists,\n pruneLive,\n within,\n mountProduct,\n hydrateFromPage,\n settleMarket,\n trackMarket,\n} from './mounting.ts';\nimport { applyStartMarket, setCountry } from './page-market.ts';\nimport { accountApi, mountAccountIfUsed } from './account-api.ts';\nimport { forgetBrowserModeTokens } from './account-marks.ts';\nimport { bindCartControls } from './controls/cart-controls.ts';\nimport { bindDiscountControls } from './controls/discount.ts';\nimport { addGroup, bindExtrasControls } from './controls/extras.ts';\nimport { bindNavigation, rememberDocumentMount } from './controls/navigation.ts';\nimport { field } from '../server/src/shared/json.ts';\nimport type { TetherConfig, TetherOptions, MarketsConfig } from './config.ts';\nimport type { ShopifyCart } from './cart/store.ts';\nimport type { ProductScope } from './product.ts';\nimport type { DiagnoseOptions } from './diagnose.ts';\nimport type { TetherEvents } from './markup.ts';\nimport type { TetherInternals } from './lazy.ts';\n\nexport { addGroup, setCountry, VERSION };\n\n// The types a site uses by name, under the names the docs give them.\nexport type { TetherOptions as TetherConfig, TetherConfig as ResolvedTetherConfig } from './config.ts';\nexport type { MarketsConfig, CartWarning } from './config.ts';\nexport type { Money } from './shopify-values.ts';\nexport type {\n Image as ShopifyImage,\n Metafield as ShopifyMetafield,\n Product as ShopifyProduct,\n ProductVariant as ShopifyVariant,\n SellingPlan as ShopifySellingPlanNode,\n SellingPlanAdjustmentValue,\n SellingPlanAllocation as ShopifySellingPlanAllocation,\n SellingPlanGroup as ShopifySellingPlanGroup,\n} from './fragments.ts';\nexport type {\n PlanInGroup as ShopifySellingPlan,\n PlanAdjustment,\n ProductWithPlans,\n VariantWithPlans,\n} from './selling-plans.ts';\nexport type {\n CartStatus,\n CartUpdateReason,\n CartVariant as ShopifyCartVariant,\n ShopifyCart,\n ShopifyCartLine,\n} from './cart/store.ts';\nexport type { Attributes, LineSpec as LineInput } from './cart/mutations.ts';\nexport type { TetherError, TetherErrorCode, GraphQLError, UserError } from './client.ts';\nexport type { ProductScope } from './product.ts';\nexport type { Purchase } from './product-state.ts';\nexport type { ProductPage, ProductQuery } from './loader.ts';\nexport type { DetectedMarket, Localization, MarketSource } from './markets.ts';\nexport type { SetCountryOptions } from './page-market.ts';\nexport type { DiagnoseOptions, DiagnoseReport } from './diagnose.ts';\nexport type { TetherEvents } from './markup.ts';\nexport type { AccountCustomer } from './account/answers.ts';\nexport type { AccountImage as ImageRef, AccountOrder, AccountOrderLine } from '../server/src/shared/account-orders.ts';\nexport type { AddressFields as CustomerAddressInput } from './account/operations.ts';\nexport type {\n CustomerAddress,\n Fulfillment,\n MailingAddress,\n OrderDetail,\n OrderDetailLine,\n SubscriptionContract,\n SubscriptionLine,\n} from './account/page-answers.ts';\n/** The helpers for selling plans, `Tether.plans`. */\nexport type SellingPlanHelpers = typeof plans;\n\n/** Milliseconds `observe` waits after the last added node, so a burst of additions mounts once. */\nconst OBSERVE_DEBOUNCE_MS = 50;\n\n/** @returns The keys of `window.TETHER_CONFIG`, none when it is no object. */\nfunction pageConfig(): Readonly<Record<string, unknown>> {\n const config = pageGlobal('TETHER_CONFIG');\n return typeof config === 'object' && config !== null ? Object.fromEntries(Object.entries(config)) : {};\n}\n\nlet initPromise: Promise<Tether | null> | null = null;\n\nlet observer: MutationObserver | null = null;\n\n/** The analytics part's queue, created once by the first `init()` with `analytics`. */\nlet analyticsQueue: Event[] | null = null;\n\n/** The document-wide listeners are bound once, by the first configured `init()`. */\nlet controlsBound = false;\n\n/**\n * Mounts the lists of `root` and the product scopes in them as they render.\n *\n * @returns The lists mounted.\n */\nfunction mountLists(root: ParentNode): number {\n let count = 0;\n within(root, listSelector()).forEach((el) => {\n if (mountedLists.has(el)) return;\n mountedLists.add(el);\n count++;\n const list = new ListScope(el);\n liveLists.add(list);\n el.addEventListener(`${cfg.prefix}:list-rendered`, (event) => {\n // The list emits this once its products are in the page, with the element they are in.\n if (isOwnEvent(event, 'list-rendered')) mount(event.detail.container);\n });\n void trackMarket(list.load());\n });\n return count;\n}\n\n/**\n * Loads each optional part `root` uses: the country select, customer accounts and search as you type.\n */\nfunction mountParts(root: ParentNode): void {\n if (within(root, `${sel('country-select')},${sel('country-bind')}`).length) {\n loadPart('markets')\n .then((part) => part.renderCountrySelects(root))\n .catch((error: unknown) => {\n reportError(error, 'markets');\n });\n }\n mountAccountIfUsed(root);\n if (within(root, sel('search')).length) {\n loadPart('search')\n .then((part) => {\n part.mountSearch(root);\n })\n .catch((error: unknown) => {\n reportError(error, 'search');\n });\n }\n}\n\n/**\n * Mounts the lists, product scopes and parts inside `root`, after its hydrate tags were read (products the server\n * rendered into the page need no request), without the page-wide work that follows a mount.\n *\n * @returns The scopes and lists newly mounted.\n */\nfunction mountWithin(root: Document | Element): number {\n let count = mountLists(root);\n within(root, sel('product')).forEach((el) => {\n if (mountProduct(el)) count++;\n });\n mountParts(root);\n return count;\n}\n\n/**\n * Mounts every product scope and product list inside `root`. Safe to call repeatedly.\n *\n * @returns The scopes and lists newly mounted.\n */\nexport function mount(root: Document | Element = document): number {\n if (!isConfigured()) return 0;\n if (root === document) rememberDocumentMount();\n // Page transitions detach old roots: they aren't kept alive.\n pruneLive();\n hydrateFromPage(root.ownerDocument ?? document);\n const count = mountWithin(root);\n settleMarket();\n Cart.render();\n log(`mounted ${count} scope(s)`, root);\n return count;\n}\n\n/**\n * `observe`: mounts a burst of added elements at once. Each root is scanned on its own (an element inside another\n * added one is covered by it), and the page-wide work (market state, the cart's markup) runs once for the burst.\n */\nfunction mountAdded(added: readonly Element[]): void {\n const roots = added.filter(\n (root) => root.isConnected && !added.some((other) => other !== root && other.contains(root)),\n );\n if (!roots.length || !isConfigured()) return;\n pruneLive();\n // Only the added elements are read for hydrate tags: the rest of the page was read when it was mounted. A load-more\n // fragment may bring its tag after the cards it is for, so every tag is read before any card mounts.\n roots.forEach((root) => {\n hydrateFromPage(root);\n });\n const count = roots.reduce((sum, root) => sum + mountWithin(root), 0);\n settleMarket();\n Cart.render();\n log(`mounted ${count} scope(s) in ${roots.length} added element(s)`);\n}\n\n/** Call before a page transition removes the page: closes the cart drawer. */\nexport function unmount(): void {\n Cart.close();\n}\n\n/** @returns The product scope `el` is in. */\nexport function getScope(el: Element | null | undefined): ProductScope | null {\n const root = el?.closest(sel('product'));\n return root ? (scopes.get(root) ?? null) : null;\n}\n\nfunction bindControls() {\n if (controlsBound) return;\n controlsBound = true;\n bindCartControls();\n bindDiscountControls();\n bindExtrasControls();\n bindNavigation({ ready: () => initPromise ?? Promise.resolve(null), mount, unmount });\n}\n\n/** Buffers Tether's events until the analytics part has loaded, so none are missed. */\nfunction startAnalyticsQueue() {\n const queue: Event[] = [];\n analyticsQueue = queue;\n ANALYTICS_EVENTS.forEach((name) => {\n document.addEventListener(`${cfg.prefix}:${name}`, (event) => queue.push(event));\n });\n document.addEventListener('astro:page-load', (event) => queue.push(event));\n loadPart('analytics')\n .then((part) => part.startAnalytics(queue))\n .catch((error: unknown) => {\n reportError(error, 'analytics');\n });\n}\n\n/**\n * A saved cart from another market moves to this one, so its prices match the page's.\n */\nasync function cartInPageMarket(cart: ShopifyCart | null): Promise<ShopifyCart | null> {\n const country = cfg.country;\n const cartCountry = cart?.buyerIdentity.countryCode;\n if (!cart || !country || !cartCountry || cartCountry === country) return cart;\n try {\n return await Cart.setBuyerIdentity({ countryCode: country }, { create: false });\n } catch (error) {\n reportError(error, 'cart country');\n return cart;\n }\n}\n\n/**\n * Configures Tether (`window.TETHER_CONFIG` with `overrides` over it), applies the visitor's market and loads the\n * cart. A second call without overrides returns the first one's promise.\n *\n * @returns `null` without a domain and token, when Tether stays idle.\n */\nexport function init(overrides: TetherOptions = {}): Promise<Tether | null> {\n if (initPromise && !Object.keys(overrides).length) return initPromise;\n configure({ ...pageConfig(), ...overrides });\n if (!isConfigured()) {\n warn('TETHER_CONFIG is missing domain or token. Tether is idle.');\n initPromise = Promise.resolve(null);\n return initPromise;\n }\n forgetBrowserModeTokens();\n bindControls();\n if (cfg.observe) startObserver();\n if (cfg.analytics && !analyticsQueue) startAnalyticsQueue();\n initPromise = applyStartMarket(overrides)\n .then(() => Cart.init())\n .then(cartInPageMarket)\n .then((cart) => {\n emit('ready', { cart });\n return api;\n });\n return initPromise;\n}\n\n/** `observe`: mounts the elements added to the page later. */\nfunction startObserver() {\n if (observer || typeof MutationObserver === 'undefined') return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const queued = new Set<Element>();\n observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof window.Element) queued.add(node);\n });\n });\n clearTimeout(timer);\n timer = setTimeout(() => {\n const added = [...queued];\n queued.clear();\n mountAdded(added);\n }, OBSERVE_DEBOUNCE_MS);\n });\n observer.observe(document.body, { childList: true, subtree: true });\n}\n\n/** @param markets Default `markets` of the config. */\nconst detectMarket = (markets?: MarketsConfig | null) =>\n loadPart('markets').then((part) => part.detectMarket(markets ?? cfg.markets));\n\nconst getLocalization = () => loadPart('markets').then((part) => part.getLocalization());\n\nconst diagnose = (options?: Omit<DiagnoseOptions, 'version'>) =>\n loadPart('diagnose').then((part) => part.diagnose({ ...options, version: VERSION }));\n\n/**\n * Listens to the Tether event `<prefix>:<name>`. Only the events Tether dispatched reach the listener: one another\n * script dispatches under the same name is ignored, so the detail is always the one `TetherEvents` gives.\n *\n * @returns Stops listening.\n */\nfunction on<Name extends keyof TetherEvents>(\n name: Name,\n listener: (detail: TetherEvents[Name], event: CustomEvent<TetherEvents[Name]>) => void,\n): () => void {\n const type = `${cfg.prefix}:${name}`;\n const handler = (event: Event) => {\n if (isOwnEvent(event, name)) listener(event.detail, event);\n };\n document.addEventListener(type, handler);\n return () => {\n document.removeEventListener(type, handler);\n };\n}\n\nconst publicApi = {\n VERSION,\n init,\n mount,\n unmount,\n /** The live config, read-only. */\n get config(): Readonly<TetherConfig> {\n return configView;\n },\n get ready(): Promise<Tether | null> {\n return initPromise ?? Promise.resolve(null);\n },\n query,\n withContext,\n cart: Cart,\n getProduct,\n getProducts,\n getRecommendations,\n getScope,\n addGroup,\n account: accountApi,\n primeProduct,\n normalizeProductKey,\n clearProductCache,\n formatMoney,\n imageUrl,\n plans,\n TetherError,\n resolveProductKey,\n setCountry,\n detectMarket,\n getLocalization,\n diagnose,\n on,\n};\n\n/** `window.Tether`, and what `import Tether from '@occluze/tether'` gives. */\nexport type Tether = typeof publicApi;\n\nconst internals: TetherInternals = { __core: coreModules, __parts: {} };\n\n/** `Tether`, also carrying `TetherInternals`, which the published type leaves out. */\nexport const api: Tether = Object.assign(publicApi, internals);\n\n/** @alias `api`, for `import Tether from '@occluze/tether'`. */\nexport default api;\n\n/**\n * The plain `<script>` setup starts by itself, unless `autoMount: false` or a framework integration (Astro), which\n * starts Tether itself.\n */\nfunction autoStart() {\n // `window.Tether` is declared by the published types, which describe this object to sites; the source doesn't\n // depend on them.\n Reflect.set(window, 'Tether', api);\n const config = pageGlobal('TETHER_CONFIG');\n if (!config || field(config, 'autoMount') === false || pageGlobal('TETHER_INTEGRATION')) return;\n const start = () => {\n init()\n .then(() => mount(document))\n .catch((error: unknown) => {\n reportError(error, 'init');\n });\n };\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });\n else start();\n}\n\nif (typeof window !== 'undefined' && typeof document !== 'undefined') autoStart();\n"],
5
+ "mappings": "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",
6
+ "names": ["STOREFRONT_API_VERSION", "QUARTERLY_VERSION", "parseVersion", "version", "match", "QUARTERLY_VERSION", "isQuarterlyVersion", "isRecord", "value", "field", "key", "METAFIELD_PART", "METAFIELD_NAMESPACE", "HOSTNAME", "COOKIE_NAME", "GLOBAL_NAME", "SITE_ORIGIN", "LOOPBACK_HOSTS", "MARKET_SOURCES", "CONSENT_MODES", "isText", "value", "isFunction", "domainOption", "invalid", "host", "isHostname", "isSiteOrHttpsUrl", "url", "partsUrlOption", "isPageOrigin", "toHttpsOrigin", "embedOriginsOption", "origins", "entries", "entry", "origin", "toMetafieldIdentifier", "ref", "namespace", "key", "dot", "metafieldsOption", "refs", "identifier", "when", "check", "isCookieName", "isDataLayer", "text", "flag", "hook", "sectionOption", "name", "schema", "isRecord", "section", "field", "parsed", "ACCOUNT", "ACCOUNT_NEEDS_SERVER", "LOCALE_MARKET", "MARKETS", "names", "source", "locales", "locale", "market", "SHOPIFY_ANALYTICS", "mode", "ANALYTICS", "accountOption", "warn", "account", "server", "marketsOption", "analyticsOption", "COUNTRY", "NOT_A_COUNTRY", "REGIONAL_LANGUAGES", "LANGUAGE", "toCountry", "value", "code", "toLanguage", "raw", "primary", "DEFAULTS", "STOREFRONT_API_VERSION", "cfg", "readOnlyView", "target", "refuse", "key", "warn", "object", "value", "_object", "configView", "PREFIX", "SWITCH_OPTIONS", "TEXT_OPTIONS", "COUNT_OPTIONS", "HOOK_OPTIONS", "invalidOption", "name", "fallback", "dropped", "warnUnknownKeys", "options", "setHook", "config", "countOption", "switchOption", "textOption", "localeOption", "choiceOption", "choices", "choice", "apiVersionOption", "isQuarterlyVersion", "codeOption", "parse", "code", "resolveConfig", "domain", "domainOption", "prefix", "resolved", "toCountry", "partsUrlOption", "toLanguage", "marketsOption", "analyticsOption", "accountOption", "metafieldsOption", "embedOriginsOption", "parseMarkets", "configure", "isConfigured", "log", "args", "reportError", "error", "context", "hookError", "messageOf", "error", "TetherError", "message", "status", "errors", "userErrors", "code", "__publicField", "matches", "value", "shape", "isRecord", "isShapeList", "item", "key", "inner", "orNull", "shaped", "data", "endpoint", "conf", "cfg", "STRINGS_AND_COMMENTS", "OPERATION_START", "operationOf", "document", "inContext", "query", "country", "language", "args", "tag", "blanked", "text", "selectionStart", "header", "end", "withContext", "ctx", "toCountry", "toLanguage", "RETRY_BACKOFF_MS", "THROTTLE_BACKOFF_MS", "SECURITY_REJECTION", "MS_PER_SECOND", "INVENTORY_FIELD", "storesWithoutInventory", "storeOf", "queryText", "access", "context", "operation", "written", "sleep", "ms", "resolve", "statusText", "adminHint", "toGraphQLError", "field", "toGraphQLResponse", "json", "invalidResponse", "res", "graphQLFailure", "error", "networkFailure", "retry", "messageOf", "graphQLOutcome", "codes", "serverError", "unclear", "attempt", "variables", "isData", "body", "request", "store", "retryWhileLeft", "retries", "sent", "outcome", "log", "gql", "options", "hasData", "untypedQuery", "unwrap", "payload", "name", "attr", "name", "cfg", "sel", "value", "get", "el", "has", "qsa", "root", "selector", "scoped", "scopeSelector", "ANALYTICS_EVENTS", "ownEvents", "ownEvent", "detail", "event", "isOwnEvent", "emit", "target", "HTML_NAMESPACE", "ALLOWED_ELEMENTS", "REMOVED_WITH_CONTENT", "GLOBAL_ATTRIBUTES", "ELEMENT_ATTRIBUTES", "URL_ATTRIBUTES", "SCRIPT_URL", "DATA_IMAGE", "OUT_OF_PLACE_PROPERTIES", "OUT_OF_PLACE_PREFIXES", "VIEWPORT_LENGTH", "PAINTS_BEYOND", "MAX_PAINT_PX", "NEGATIVE_LIMITS", "NUMBER", "NON_LENGTH_FUNCTION", "COMPUTED_VALUE", "FETCHING_VALUE", "IGNORED_IN_SCHEME", "IFRAME_SANDBOX", "IFRAME_FEATURES", "IFRAME_REFERRER_POLICY", "EMBED_ORIGINS", "isAllowedAttribute", "tag", "name", "isSafeUrl", "value", "url", "urlOrNull", "base", "pageOf", "document", "styleParser", "embedOrigins", "baseUrl", "origin", "isAllowedEmbed", "page", "allowedFeatures", "features", "directive", "feature", "isOutOfPlace", "property", "prefix", "limits", "movesBackTooFar", "number", "unit", "amount", "limit", "paintsTooFar", "cleanStyle", "style", "properties", "_", "index", "cleaned", "cleanAttributes", "element", "attribute", "isUrl", "hardenEmbedsAndLinks", "allow", "rel", "cleanElement", "isHtml", "storeHtmlFragment", "html", "template", "isHttpsUrl", "url", "formatMoney", "money", "locale", "amount", "productUrl", "productPath", "handle", "id", "numericId", "productKey", "raw", "value", "imageUrl", "url", "width", "height", "crop", "parsed", "error", "sizeAttribute", "el", "name", "attr", "cdnUrl", "withoutQuery", "sameImage", "a", "b", "cssUrl", "char", "setImage", "alt", "src", "templates", "hasTemplate", "container", "copyOf", "template", "templateOf", "known", "first", "repeat", "items", "render", "warn", "item", "index", "node", "leave", "onLeave", "done", "error", "reportError", "insertFresh", "wanted", "fresh", "onEnter", "previous", "repeatKeyed", "keyOf", "keyAttribute", "cfg", "leavingAttribute", "live", "existing", "key", "staying", "current", "SEEN", "SHOWN", "PREVIOUS_DISPLAY", "LAYOUT_DEBOUNCE_MS", "productUrl", "handle", "id", "cfg", "formatOr", "format", "fallback", "error", "formatPercent", "percentage", "locale", "formatMoney", "money", "formatDate", "iso", "setStoreHtml", "el", "html", "storeHtmlFragment", "isTextField", "isInput", "setText", "value", "text", "readText", "setTextTracked", "before", "reportError", "reveal", "previous", "attr", "show", "visible", "setSelected", "selected", "input", "layoutTimer", "pageGlobal", "name", "refreshScrollTrigger", "scrollTrigger", "refresh", "notifyLayoutChanged", "reason", "emit", "watchImages", "root", "loading", "img", "left", "done", "busy", "task", "parseJSONAttr", "raw", "get", "warn", "attributeText", "attributesFromJson", "json", "isRecord", "entry", "key", "toAttributes", "attributes", "attribute", "MONEY", "FULL_ACCESS", "inventoryField", "access", "IMAGE", "metafieldsField", "metafields", "identifiers", "ref", "identifier", "toMetafieldIdentifier", "namespace", "key", "SELLING_PLAN_FIELDS", "ALLOCATION_FIELDS", "productFragment", "o", "cfg", "cartFragment", "collectionFields", "selling_plans_exports", "__export", "GID", "allPlans", "allocationFor", "findPlan", "hasPlans", "planAdjustment", "planCheckoutCharge", "planPrice", "planSavings", "plansForVariant", "toGid", "DECIMAL", "toScaledInteger", "amount", "fractionDigits", "match", "sign", "whole", "fraction", "digits", "fractionLength", "isGreaterAmount", "other", "scaled", "scaledOther", "DEFAULT_FRACTION_DIGITS", "fractionDigitsByCurrency", "currencyFractionDigits", "currencyCode", "known", "toMinorUnits", "money", "fromMinorUnits", "minorUnits", "PERCENT", "toGid", "value", "type", "text", "GID", "allPlans", "product", "group", "plan", "hasPlans", "allocationFor", "variant", "planId", "allocation", "plansForVariant", "planPrice", "adjustment", "planCheckoutCharge", "planSavings", "onPlanPrice", "oneTime", "onPlan", "saved", "toMinorUnits", "fromMinorUnits", "planAdjustment", "findPlan", "ref", "plans", "gid", "needle", "option", "MONEY", "PRODUCT", "PRODUCT_CONNECTION", "isProductShape", "value", "field", "matches", "PRODUCT_NODE", "node", "isRecord", "LINE", "MONEY", "CART", "FOCUSABLE", "FOCUS_AFTER_OPEN_MS", "openWithoutDrawer", "returnFocus", "isFocusable", "el", "focusables", "drawer", "qsa", "drawers", "sel", "isOpenDrawer", "cfg", "isOpen", "all", "syncDrawerAria", "open", "button", "get", "focusInto", "target", "openCart", "opened", "first", "emit", "closeCart", "hadFocus", "toggleCart", "trapFocus", "event", "inside", "last", "active", "leaving", "MISS_CACHE_MS", "FRESH_MS", "productCache", "waiting", "stale", "freshAt", "pending", "flushScheduled", "generation", "isGid", "key", "normalizeProductKey", "productKey", "primeProduct", "product", "loaded", "getProduct", "rawKey", "TetherError", "cached", "loading", "resolve", "reject", "flush", "withProductFragment", "operation", "access", "productFragment", "cfg", "batchQuery", "keys", "ids", "handles", "definitions", "selections", "variables", "handle", "index", "settleWaiting", "settle", "waiter", "forgetMissLater", "batchGeneration", "miss", "isBatchData", "data", "isRecord", "byId", "byHandle", "matches", "orNull", "PRODUCT_NODE", "PRODUCT", "query", "log", "gql", "error", "found", "now", "clearProductCache", "hydrateProducts", "products", "takeStale", "failureIsReportedByTheRender", "refreshProduct", "normalized", "isCollectionAnswer", "shaped", "PRODUCT_CONNECTION", "isSearchAnswer", "isRecommendationsAnswer", "COLLECTION_QUERY", "collectionFields", "SEARCH_QUERY", "getProducts", "collection", "first", "after", "sortKey", "reverse", "filters", "rest", "getRecommendations", "intent", "list", "NTH_IMAGE_BIND", "METAFIELD_BIND", "MULTI_LINE_METAFIELD", "ALL_PLAN_GROUPS", "isSelect", "el", "selectsIn", "scope", "selector", "renderGallery", "product", "images", "rendered", "node", "hasTemplate", "img", "index", "sameImage", "repeat", "image", "target", "sel", "qsa", "setImage", "renderMetafield", "ref", "dot", "namespace", "key", "metafield", "field", "setText", "PRODUCT_TEXTS", "formatDate", "formatMoney", "renderTags", "exclude", "tag", "tags", "renderProductBind", "text", "setStoreHtml", "url", "productUrl", "renderIndexedBind", "nth", "renderOptionLists", "container", "name", "get", "option", "candidate", "has", "show", "value", "attr", "swatchEl", "swatch", "renderSelects", "select", "variant", "plans", "plan", "oneTime", "oneTimeLabel", "renderPlanLists", "raw", "group", "writeProduct", "DEFAULT_TITLE", "quantityOf", "line", "pending", "numericId", "gid", "lineUrl", "variant", "base", "productUrl", "query", "planId", "productTitle", "LINE_TEXT", "option", "formatMoney", "view", "component", "ATTRIBUTE_KEY", "lineValue", "key", "name", "attribute", "fillPlanSelect", "select", "product", "plans", "allPlans", "allowOneTime", "get", "oneTime", "attr", "choices", "plan", "signature", "choice", "show", "renderLineState", "node", "isSubscription", "warning", "candidate", "qsa", "sel", "el", "setTextTracked", "conditions", "isGreaterAmount", "visible", "renderLine", "setImage", "url", "value", "isSelect", "getProduct", "error", "reportError", "renderConditions", "cart", "count", "hasSubscription", "code", "renderFields", "setText", "container", "repeat", "discount", "target", "renderCart", "money", "syncDrawerAria", "isOpen", "repeatKeyed", "cfg", "notifyLayoutChanged", "showSavedCount", "getStored", "key", "area", "setStored", "value", "removeStored", "CART_ID", "status", "warnings", "pendingQuantities", "cartStatus", "currentCart", "currentWarnings", "statusOf", "cart", "seenKey", "cfg", "savedCartId", "stored", "getStored", "CART_ID", "forgetSavedCart", "removeStored", "keepQuietly", "setStored", "render", "renderCart", "announce", "reason", "emit", "commit", "settleLoad", "outcome", "lastKnownCount", "text", "count", "showLastKnownCount", "showSavedCount", "setWarnings", "notes", "note", "index", "other", "waitOnly", "serial", "previous", "task", "run", "runInOrder", "enqueue", "value", "error", "pendingQuantityOf", "line", "quantityOf", "meta", "isOpen", "subscribe", "listener", "events", "name", "call", "UNKNOWN_CART_MESSAGE", "isPayloadOf", "name", "shaped", "orNull", "CART", "isCartGone", "error", "TetherError", "userError", "graphQLError", "isUnclearFailure", "code", "status", "toLineInput", "input", "quantity", "options", "spec", "merchandiseId", "GID", "asked", "line", "planId", "attributes", "toAttributes", "mutationText", "args", "inner", "access", "cartFragment", "send", "variables", "data", "gql", "payload", "unwrap", "setWarnings", "createCart", "withIdentity", "cfg", "mutateCart", "createInput", "cartId", "savedCartId", "forgetSavedCart", "log", "fetchCart", "id", "fetchCheckoutUrl", "toLineUpdate", "lineId", "patch", "change", "variantId", "updateLines", "lines", "removeLines", "lineIds", "setLineQuantity", "target", "resolveAccountCheckout", "setCheckoutResolver", "resolver", "askCheckoutUrl", "cartId", "fetchCheckoutUrl", "url", "error", "accountCheckoutUrl", "reportError", "checkoutUrl", "cartAnswer", "accountUrl", "answer", "TetherError", "detachOpenerIfAllowed", "tab", "checkout", "target", "cfg", "savedCartId", "cart", "currentCart", "isHttpsUrl", "emit", "currentLines", "currentCart", "reloadAfterUnclearFailure", "id", "savedCartId", "cart", "fetchCart", "forgetSavedCart", "commit", "error", "reportError", "runChange", "task", "enqueue", "isUnclearFailure", "changeQuantity", "lineId", "delta", "line", "candidate", "target", "pendingQuantityOf", "job", "pendingQuantities", "announce", "isLatest", "setLineQuantity", "keepQuietly", "discountCodesFrom", "codes", "current", "discount", "list", "code", "mergedAttributes", "attributes", "merged", "attribute", "toAttributes", "value", "key", "init", "settleLoad", "showLastKnownCount", "isCartGone", "Cart", "cartStatus", "currentWarnings", "setCheckoutResolver", "render", "subscribe", "input", "quantity", "options", "lines", "toLineInput", "mutateCart", "TetherError", "emit", "cfg", "openCart", "patch", "toLineUpdate", "updateLines", "lineIds", "ids", "removeLines", "note", "variables", "wanted", "unwanted", "existing", "buyerIdentity", "create", "checkout", "drawers", "isOpen", "closeCart", "toggleCart", "PERCENT", "timesQuantity", "money", "quantity", "fromMinorUnits", "toMinorUnits", "selectionOf", "variant", "option", "findVariant", "product", "selected", "isValueAvailable", "name", "value", "purchasePlanId", "purchase", "linePurchase", "allocationFor", "normalisePurchase", "kind", "preferredPlanId", "plans", "allPlans", "known", "plan", "onVariant", "initialChoice", "sellingPlan", "defaultPlan", "defaultPurchase", "variants", "wantedId", "GID", "start", "candidate", "fromUrl", "findPlan", "offered", "showConditions", "lowStock", "variantCount", "sale", "requiresSellingPlan", "stock", "low", "onSale", "onPlan", "productView", "choice", "exact", "planId", "line", "allocation", "price", "planPrice", "compareAt", "planSavings", "variantBindTexts", "view", "format", "compareAtPrice", "forQuantity", "texts", "charge", "planCheckoutCharge", "planBindTexts", "adjustment", "planAdjustment", "decodeOrKeep", "text", "error", "URL_SOURCE", "resolveProductKey", "el", "location", "name", "raw", "get", "source", "key", "segments", "segment", "decodeOrKeep", "STATUS_KEYS", "isScopeStatus", "value", "status", "codeOf", "error", "isNotFound", "boundary", "sel", "ownedBy", "root", "errorCode", "code", "setScopeStatus", "notFound", "failed", "attr", "scoped", "shows", "show", "button", "FORMAT", "formatMoney", "formatPercent", "isInput", "el", "addButtonLabel", "button", "label", "qsa", "sel", "original", "renderOptionStates", "scope", "view", "product", "selectedValue", "authored", "setText", "get", "container", "name", "node", "value", "setSelected", "unavailable", "isValueAvailable", "selectsIn", "select", "renderPurchaseStates", "variant", "purchase", "line", "isSelect", "mode", "planId", "allocationFor", "plan", "candidate", "texts", "planBindTexts", "text", "renderConditions", "conditions", "STATUS_KEYS", "status", "key", "visible", "ownedBy", "show", "renderAddButtons", "disabled", "soldOutText", "subscribeText", "writeVariant", "variantBindTexts", "setImage", "attr", "productSelector", "sel", "isField", "el", "isElement", "target", "closestOwn", "scope", "selector", "onAddClick", "button", "label", "original", "addButtonLabel", "addingText", "get", "busy", "setText", "error", "reportError", "emit", "onClick", "event", "optionValue", "optionList", "plan", "purchase", "purchaseKind", "plus", "minus", "add", "value", "onChange", "bindProductEvents", "scopes", "productSelector", "sel", "knownOptionNames", "rememberOptionNames", "products", "product", "option", "isInput", "el", "isField", "lineAttributeValue", "ProductScope", "root", "__publicField", "allPlans", "params", "choice", "initialChoice", "get", "cfg", "has", "attr", "bindProductEvents", "watchImages", "notifyLayoutChanged", "emit", "variantId", "same", "variant", "selectionOf", "selector", "inner", "scoped", "authored", "options", "index", "planId", "purchasePlanId", "plan", "allocationFor", "linePurchase", "line", "bought", "attributes", "fixed", "attributesFromJson", "parseJSONAttr", "fields", "value", "name", "withValue", "firstWithValue", "next", "score", "closest", "best", "id", "candidate", "GID", "kind", "normalisePurchase", "ref", "findPlan", "quantity", "minimum", "setText", "cart", "Cart", "writeProduct", "view", "productView", "writeVariant", "URL_QUERY_SOURCE", "listSelector", "sel", "isElement", "target", "toLimit", "raw", "toIntent", "ListScope", "root", "__publicField", "setScopeStatus", "event", "more", "busy", "selector", "scoped", "query", "get", "first", "sortKey", "has", "key", "resolveProductKey", "scope", "TetherError", "getRecommendations", "token", "stale", "recommendations", "limit", "page", "getProducts", "emit", "error", "isNotFound", "reportError", "collection", "el", "setText", "setStoreHtml", "setImage", "container", "repeat", "node", "product", "attr", "empty", "show", "notifyLayoutChanged", "HYDRATE_SELECTOR", "mountedProducts", "mountedLists", "liveProducts", "liveLists", "latestRequest", "pruneLive", "el", "list", "within", "root", "selector", "qsa", "applyPageTitle", "product", "template", "get", "mountWithoutKey", "fromUrl", "raw", "setScopeStatus", "emit", "listSelector", "warn", "attr", "showLoadFailure", "key", "error", "notFound", "isNotFound", "reportError", "log", "mountProduct", "source", "resolveProductKey", "request", "isLatest", "scopes", "trackMarket", "getProduct", "existing", "ProductScope", "cfg", "takeStale", "queueRefresh", "retryFailed", "refreshQueue", "flushRefresh", "applyRefresh", "fresh", "scope", "rootKey", "keys", "refreshProduct", "marketPending", "loading", "settleMarket", "waits", "wait", "toHydratePayload", "json", "isRecord", "products", "entries", "toCountry", "toLanguage", "isProductShape", "readScripts", "hydrateFromPage", "script", "country", "language", "rememberOptionNames", "hydrateProducts", "storageKey", "cfg", "pageStorage", "key", "getStored", "parseJsonOrNull", "text", "error", "toMarket", "value", "country", "toCountry", "locale", "toLanguage", "parseSavedMarket", "readSavedMarket", "storage", "pageStorage", "storageKey", "configuredMarket", "lastPageHadOwnMarket", "pageMarket", "meta", "name", "cfg", "country", "toCountry", "toLanguage", "visitorMarket", "markets", "readSavedMarket", "loadPart", "error", "reportError", "applyStartMarket", "overrides", "page", "market", "log", "sameCode", "a", "b", "marketForArrival", "leftOwnMarket", "target", "setCountry", "code", "options", "part", "PART_INTEGRITY", "isTetherInternals", "value", "isRecord", "VERSION", "coreModules", "cfg", "isConfigured", "log", "MARKET_SOURCES", "parseMarkets", "reportError", "warn", "endpoint", "gql", "orNull", "shaped", "TetherError", "ANALYTICS_EVENTS", "attr", "attributesFromJson", "emit", "formatDate", "formatMoney", "get", "has", "hasTemplate", "isOwnEvent", "parseJSONAttr", "productUrl", "qsa", "repeat", "sel", "setImage", "setText", "show", "clearProductCache", "getProduct", "allPlans", "boundary", "resolveProductKey", "Cart", "scopes", "listSelector", "liveLists", "liveProducts", "mountedProducts", "mountProduct", "pruneLive", "within", "toCountry", "toLanguage", "setCountry", "isHttpsUrl", "loadPart", "MINIFIED_SCRIPT", "coreScript", "coreScriptUrl", "JSDELIVR_ALIAS", "exactReleaseFolder", "folder", "_alias", "path", "source", "VERSION", "parts", "partsFolder", "applyIntegrity", "script", "integrity", "loadScript", "name", "file", "resolve", "reject", "url", "tether", "part", "isTetherInternals", "problem", "loading", "suffix", "PART_INTEGRITY", "hintKey", "cfg", "removeFromBothAreas", "key", "removeStored", "forgetBrowserModeTokens", "isLoginOutcome", "value", "ACCOUNT_ATTRIBUTES", "accountSelector", "name", "attr", "hasLoginHint", "getStored", "hintKey", "mountAccountIfUsed", "root", "cfg", "within", "isLoginOutcome", "loadPart", "part", "error", "reportError", "fromPart", "args", "call", "accountApi", "lineKeyAttribute", "cfg", "isElement", "target", "lineOf", "line", "id", "reportFailure", "change", "context", "error", "reportError", "onLineClick", "event", "found", "sel", "Cart", "delta", "lineDelta", "candidate", "onClick", "clear", "busy", "checkout", "showLineBusy", "onChange", "hasValue", "onKeydown", "trapFocus", "bindCartControls", "isElement", "target", "codeInputOf", "form", "input", "sel", "markInvalid", "invalid", "applyCode", "code", "Cart", "emit", "error", "TetherError", "reportError", "onSubmit", "event", "busy", "onInput", "onClick", "remove", "attr", "bindDiscountControls", "addGroup", "button", "loadPart", "part", "reportExtrasError", "error", "reportError", "onClick", "event", "target", "sel", "busy", "onStorage", "cfg", "onVariantChanged", "isOwnEvent", "attr", "bindExtrasControls", "mountedBody", "rememberDocumentMount", "onPageLoad", "hooks", "target", "marketForArrival", "setCountry", "error", "reportError", "bindAstroRouter", "onChange", "event", "sel", "onOnline", "retryFailed", "liveLists", "list", "attr", "bindNavigation", "cfg", "OBSERVE_DEBOUNCE_MS", "pageConfig", "config", "pageGlobal", "initPromise", "observer", "analyticsQueue", "controlsBound", "mountLists", "root", "count", "within", "listSelector", "el", "mountedLists", "list", "ListScope", "liveLists", "cfg", "event", "isOwnEvent", "mount", "trackMarket", "mountParts", "sel", "loadPart", "part", "error", "reportError", "mountAccountIfUsed", "mountWithin", "mountProduct", "isConfigured", "rememberDocumentMount", "pruneLive", "hydrateFromPage", "settleMarket", "Cart", "log", "mountAdded", "added", "roots", "other", "sum", "unmount", "getScope", "scopes", "bindControls", "bindCartControls", "bindDiscountControls", "bindExtrasControls", "bindNavigation", "startAnalyticsQueue", "queue", "ANALYTICS_EVENTS", "name", "cartInPageMarket", "cart", "country", "cartCountry", "init", "overrides", "configure", "forgetBrowserModeTokens", "startObserver", "applyStartMarket", "emit", "api", "warn", "timer", "queued", "mutations", "mutation", "node", "detectMarket", "markets", "getLocalization", "diagnose", "options", "VERSION", "on", "listener", "type", "handler", "publicApi", "configView", "query", "withContext", "getProduct", "getProducts", "getRecommendations", "addGroup", "accountApi", "primeProduct", "normalizeProductKey", "clearProductCache", "formatMoney", "imageUrl", "selling_plans_exports", "TetherError", "resolveProductKey", "setCountry", "internals", "coreModules", "index_default", "autoStart", "field", "start"]
7
7
  }