{"version":3,"file":"storefront.mjs","sources":["../src/env.ts","../src/cart-id-store.ts","../src/errors.ts","../src/transport.ts","../src/client.ts","../src/helpers.ts","../src/customization.ts"],"sourcesContent":["/**\n * The only place the SDK touches browser globals. Each is read through `globalThis` and guarded,\n * so the same file runs in a browser, Node 18+ and edge workers (where they are missing), and in\n * browsers that throw on access (`localStorage` with storage blocked, sandboxed frames).\n */\n\ninterface BrowserGlobals {\n  localStorage?: Storage;\n  location?: { assign?: (url: string) => void };\n}\n\nconst globals = globalThis as BrowserGlobals;\n\n/** `localStorage`, or null where it is missing or blocked. */\nexport function browserStorage(): Storage | null {\n  try {\n    return globals.localStorage ?? null;\n  } catch {\n    return null;\n  }\n}\n\n/** A way to navigate the page, or null outside a browser window. */\nexport function browserNavigate(): ((url: string) => void) | null {\n  try {\n    const location = globals.location;\n    return location && typeof location.assign === \"function\"\n      ? (url) => location.assign!(url)\n      : null;\n  } catch {\n    return null;\n  }\n}\n\n/** Surface a listener's exception without breaking the call that emitted the event. */\nexport function reportListenerError(error: unknown): void {\n  const report = (globalThis as { reportError?: (error: unknown) => void }).reportError;\n  if (typeof report === \"function\") report(error);\n  else console.error(\"[storefront.js] a cart:change listener threw\", error);\n}\n","import { browserStorage } from \"./env.js\";\n\n/** Where the cart id lives. `\"none\"` keeps it in memory only, for the client's lifetime (servers). */\nexport type CartStorage = \"localStorage\" | \"none\";\n\n/**\n * The `localStorage` key a token's cart id is kept under: `od:cart:{token}`. A public token names\n * exactly one storefront, so this is one cart per storefront per browser, and two shops embedded\n * on one site never share a cart. The value is the bare cart id.\n */\nexport function cartStorageKey(token: string): string {\n  return `od:cart:${token}`;\n}\n\nexport interface CartIdStore {\n  read(): string | null;\n  write(id: string): void;\n  /**\n   * Drop the stored id. With `id`, only if it is still the one stored, so dropping a cart that\n   * turned out to be over never wipes a newer cart another tab stored meanwhile.\n   */\n  forget(id?: string): void;\n}\n\n/**\n * The stored cart id. `localStorage` is read on every call, so a cart started, cleared or converted\n * in another tab is seen at once. When storage is off, missing, blocked or full, the id is kept in\n * memory instead: the cart then still works, it just doesn't survive a reload.\n */\nexport function createCartIdStore(key: string, mode: CartStorage): CartIdStore {\n  let memory: string | null = null;\n  // False once a write failed (Safari private mode, quota): from then on memory is the truth.\n  let persisted = true;\n  const enabled = () => (mode === \"localStorage\" ? browserStorage() : null);\n  const storage = () => (persisted ? enabled() : null);\n\n  const read = (): string | null => {\n    const store = storage();\n    if (store) {\n      try {\n        return store.getItem(key) || null;\n      } catch {\n        // Blocked between calls: fall back to memory.\n      }\n    }\n    return memory;\n  };\n\n  return {\n    read,\n    write(id) {\n      memory = id;\n      const store = storage();\n      if (!store) return;\n      try {\n        store.setItem(key, id);\n      } catch {\n        persisted = false;\n      }\n    },\n    forget(id) {\n      if (id !== undefined && read() !== id) return;\n      memory = null;\n      try {\n        // Even after a failed write, so a stale id can't come back on the next page load.\n        enabled()?.removeItem(key);\n      } catch {\n        // Nothing to undo.\n      }\n    },\n  };\n}\n","/**\n * The codes a {@link StorefrontError} carries. The first group comes from the Storefront API;\n * the second is raised by the SDK itself, always with `status: 0`.\n */\nexport type StorefrontErrorCode =\n  // 400\n  | \"INVALID\"\n  | \"CUSTOMIZATION_INVALID\"\n  | \"PURCHASE_LIMIT\"\n  // 401\n  | \"TOKEN_INVALID\"\n  // 404\n  | \"NOT_FOUND\"\n  // 409\n  | \"CART_LOCKED\"\n  | \"CART_CONVERTED\"\n  | \"OUT_OF_STOCK\"\n  | \"SALE_CLOSED\"\n  // 429\n  | \"RATE_LIMITED\"\n  // A response without a code of its own: 5xx, or another status.\n  | \"SERVER_ERROR\"\n  | \"HTTP_ERROR\"\n  // Raised by the SDK (status 0).\n  | \"NETWORK_ERROR\"\n  | \"INVALID_RESPONSE\"\n  | \"NO_CART\"\n  | \"UNSUPPORTED_ENVIRONMENT\"\n  | (string & {});\n\nexport interface StorefrontErrorInit {\n  code: StorefrontErrorCode;\n  message: string;\n  /** The HTTP status, or 0 when the SDK raised the error itself. */\n  status: number;\n  field?: string;\n  retryAfter?: number;\n  cause?: unknown;\n}\n\n/**\n * Every failure the client reports. `code` is stable and meant for branching; `message` is the\n * server's explanation. It's written for the buyer for `CUSTOMIZATION_INVALID`, `PURCHASE_LIMIT`,\n * `OUT_OF_STOCK`, `SALE_CLOSED`, `CART_LOCKED` and `CART_CONVERTED`, and for `INVALID` when\n * `field` is one the buyer fills in: `code`, `quantity`, `lines[n].quantity`, `email` or `name`.\n * Any other `INVALID` is for you, the developer: show the buyer your own words.\n */\nexport class StorefrontError extends Error {\n  declare readonly code: StorefrontErrorCode;\n  /** The HTTP status, or 0 when the SDK raised the error itself (no response). */\n  declare readonly status: number;\n  /** For `CUSTOMIZATION_INVALID`, the key of the answer that was refused; for `INVALID`, the request field it's about. */\n  declare readonly field?: string;\n  /** For `RATE_LIMITED`: seconds to wait, when the server said (`Retry-After`). */\n  declare readonly retryAfter?: number;\n\n  constructor(init: StorefrontErrorInit) {\n    super(init.message, init.cause === undefined ? undefined : { cause: init.cause });\n    this.name = \"StorefrontError\";\n    const self = this as { -readonly [K in keyof StorefrontError]: StorefrontError[K] };\n    self.code = init.code;\n    self.status = init.status;\n    if (init.field !== undefined) self.field = init.field;\n    if (init.retryAfter !== undefined) self.retryAfter = init.retryAfter;\n  }\n}\n\n/** Whether `error` is a {@link StorefrontError} with one of `codes`. */\nexport function hasCode(error: unknown, ...codes: StorefrontErrorCode[]): error is StorefrontError {\n  return error instanceof StorefrontError && codes.includes(error.code);\n}\n\n/** Raised by the SDK, not the server. */\nexport function clientError(\n  code: StorefrontErrorCode,\n  message: string,\n  cause?: unknown,\n): StorefrontError {\n  return new StorefrontError({ code, message, status: 0, cause });\n}\n\nfunction fallbackCode(status: number): StorefrontErrorCode {\n  switch (status) {\n    case 400:\n      return \"INVALID\";\n    case 401:\n      return \"TOKEN_INVALID\";\n    case 404:\n      return \"NOT_FOUND\";\n    case 429:\n      return \"RATE_LIMITED\";\n    default:\n      return status >= 500 ? \"SERVER_ERROR\" : \"HTTP_ERROR\";\n  }\n}\n\n/** `Retry-After` in seconds: either delta-seconds or an HTTP date. */\nexport function parseRetryAfter(\n  value: string | null,\n  now: number = Date.now(),\n): number | undefined {\n  const raw = value?.trim();\n  if (!raw) return undefined;\n  if (/^\\d+$/.test(raw)) return Number(raw);\n  const at = Date.parse(raw);\n  return Number.isNaN(at) ? undefined : Math.max(0, Math.ceil((at - now) / 1000));\n}\n\nfunction stringProp(body: unknown, key: string): string | undefined {\n  if (typeof body !== \"object\" || body === null) return undefined;\n  const value = (body as Record<string, unknown>)[key];\n  return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\n/** The error for a non-2xx response whose body (`{ code, message, field? }`) is `body`, if JSON. */\nexport function errorFromResponse(response: Response, body: unknown): StorefrontError {\n  const status = response.status;\n  if (status === 429) {\n    const retryAfter = parseRetryAfter(response.headers.get(\"Retry-After\"));\n    return new StorefrontError({\n      code: \"RATE_LIMITED\",\n      status,\n      message:\n        stringProp(body, \"message\") ??\n        (retryAfter === undefined\n          ? \"Too many requests. Try again shortly.\"\n          : `Too many requests. Try again in ${retryAfter} seconds.`),\n      retryAfter,\n    });\n  }\n  return new StorefrontError({\n    code: stringProp(body, \"code\") ?? fallbackCode(status),\n    status,\n    message: stringProp(body, \"message\") ?? `The Storefront API answered ${status}.`,\n    field: stringProp(body, \"field\"),\n  });\n}\n","import { StorefrontError, clientError, errorFromResponse } from \"./errors.js\";\n\nexport type Method = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\n/** One JSON request to the Storefront API. `path` starts with `/`, relative to the version base. */\nexport type Send = <T>(method: Method, path: string, body?: unknown) => Promise<T>;\n\n/** The token header, on every request. Never put the token in a query string. */\nconst TOKEN_HEADER = \"X-OD-Storefront-Token\";\n\n/**\n * A fetch-only transport. No cookies are involved (the API's CORS is `*` without credentials), and\n * no `credentials`/`mode` options are passed, since some edge runtimes reject them.\n */\nexport function createTransport(options: {\n  baseUrl: string;\n  token: string;\n  fetch?: typeof fetch;\n}): Send {\n  const { baseUrl, token } = options;\n  // Never call a detached `window.fetch`: browsers throw \"Illegal invocation\".\n  const doFetch: typeof fetch =\n    options.fetch ??\n    ((input, init) => {\n      if (typeof globalThis.fetch !== \"function\") {\n        throw clientError(\n          \"UNSUPPORTED_ENVIRONMENT\",\n          \"No fetch() here. Pass createStorefrontClient({ fetch }).\",\n        );\n      }\n      return globalThis.fetch(input, init);\n    });\n\n  return async <T>(method: Method, path: string, body?: unknown): Promise<T> => {\n    const headers: Record<string, string> = { Accept: \"application/json\", [TOKEN_HEADER]: token };\n    const init: RequestInit = { method, headers };\n    if (body !== undefined) {\n      headers[\"Content-Type\"] = \"application/json\";\n      init.body = JSON.stringify(body);\n    }\n\n    let response: Response;\n    try {\n      response = await doFetch(baseUrl + path, init);\n    } catch (cause) {\n      if (cause instanceof StorefrontError) throw cause;\n      throw clientError(\n        \"NETWORK_ERROR\",\n        `Could not reach the Storefront API${cause instanceof Error ? `: ${cause.message}` : \".\"}`,\n        cause,\n      );\n    }\n\n    let text = \"\";\n    try {\n      text = await response.text();\n    } catch {\n      // A body that can't be read is treated as empty.\n    }\n    let data: unknown;\n    let parsed = text === \"\";\n    if (!parsed) {\n      try {\n        data = JSON.parse(text);\n        parsed = true;\n      } catch {\n        // Not JSON: an HTML error page from a proxy, say.\n      }\n    }\n\n    if (!response.ok) throw errorFromResponse(response, data);\n    if (!parsed) {\n      throw clientError(\n        \"INVALID_RESPONSE\",\n        `The Storefront API sent a body that isn't JSON (${response.status}).`,\n      );\n    }\n    return data as T;\n  };\n}\n","import { cartStorageKey, createCartIdStore, type CartStorage } from \"./cart-id-store.js\";\nimport { browserNavigate, reportListenerError } from \"./env.js\";\nimport { StorefrontError, clientError, hasCode } from \"./errors.js\";\nimport { createTransport, type Method } from \"./transport.js\";\nimport type {\n  Answers,\n  ApiVersion,\n  Cart,\n  CartContact,\n  CartLineInput,\n  Product,\n  ProductList,\n  ProductListParams,\n  Shop,\n  TagList,\n} from \"./types.js\";\n\n/** The production API origin + `/api`. */\nconst DEFAULT_API_URL = \"https://api.organizationdepot.com/api\";\n/** The Storefront API version this build was written for. */\nconst DEFAULT_API_VERSION: ApiVersion = \"2026-12\";\n\nexport interface StorefrontClientOptions {\n  /**\n   * A token from the shop's Headless page: a public token (`odpk_…`) for your live site, safe to\n   * ship in a page, or a preview token (`odpv_…`) for local development, whose carts work while the\n   * shop is closed and pay only in test mode (Stripe's test cards, no order).\n   */\n  token: string;\n  /** The API origin + `/api`. Default: production. Local dev: `http://localhost:12001/api`. */\n  apiUrl?: string;\n  apiVersion?: ApiVersion;\n  /** `\"localStorage\"` (default) keeps the cart id across page loads; `\"none\"` keeps it in memory. */\n  cartStorage?: CartStorage;\n  /** A fetch to use instead of the global one (tests, proxies, older runtimes). */\n  fetch?: typeof fetch;\n}\n\n/** The events {@link StorefrontClient.on} can listen to, with what each passes its listener. */\nexport interface StorefrontEvents {\n  /** After every call that changes the cart, and on `clear()`. `null`: there is no cart now. */\n  \"cart:change\": Cart | null;\n}\n\nexport interface ShopClient {\n  get(): Promise<Shop>;\n}\n\nexport interface ProductsClient {\n  /** Products in the shop's display order, a page at a time. */\n  list(params?: ProductListParams): Promise<ProductList>;\n  /** One product by handle (its slug, or its id). `null` when there is no such product. */\n  get(handle: string): Promise<Product | null>;\n}\n\nexport interface TagsClient {\n  /** Every tag on the shop's products, with how many carry each, in the shop's display order. */\n  list(): Promise<TagList>;\n}\n\n/**\n * The buyer's cart. The client remembers its id (see `cartStorage`), so none of these take one.\n * Calls run one at a time, in order, so two quick \"Add to cart\" clicks never start two carts.\n */\nexport interface CartClient {\n  /** The stored cart; `null` when there is none, or it was converted, abandoned or is unknown (it is then forgotten). */\n  get(): Promise<Cart | null>;\n  /** Add one line. Starts the cart when there is none. */\n  addLine(line: CartLineInput): Promise<Cart>;\n  /** Add lines. Starts the cart when there is none. A line with the same variant and customizations is incremented. */\n  addLines(lines: CartLineInput[]): Promise<Cart>;\n  /** Set a line's quantity; 0 removes it. */\n  updateLine(lineId: string, quantity: number): Promise<Cart>;\n  removeLine(lineId: string): Promise<Cart>;\n  /** Answers to the shop's `checkoutQuestions`. Starts the cart when there is none. */\n  setAnswers(answers: Answers): Promise<Cart>;\n  /** Starts the cart when there is none. */\n  setContact(contact: CartContact): Promise<Cart>;\n  applyDiscountCode(code: string): Promise<Cart>;\n  removeDiscountCode(code: string): Promise<Cart>;\n  /** Send the buyer to the hosted checkout (`cart.checkoutUrl`). Browser only. */\n  checkout(): Promise<void>;\n  /** Forget the stored cart id (the cart itself is untouched). Emits `cart:change` with `null`. */\n  clear(): void;\n}\n\nexport interface StorefrontClient {\n  readonly shop: ShopClient;\n  readonly products: ProductsClient;\n  readonly tags: TagsClient;\n  readonly cart: CartClient;\n  /** Listen to an event. Returns a function that stops listening. */\n  on<E extends keyof StorefrontEvents>(\n    event: E,\n    listener: (payload: StorefrontEvents[E]) => void,\n  ): () => void;\n}\n\n/** A `POST /carts` body: what a change can start a new cart with. */\ninterface NewCart {\n  lines?: CartLineInput[];\n  contact?: CartContact;\n  answers?: Answers;\n}\n\nconst enc = encodeURIComponent;\n\nfunction query(params: ProductListParams): string {\n  const parts: string[] = [];\n  for (const key of [\"first\", \"after\", \"tag\"] as const) {\n    const value = params[key];\n    if (value !== undefined && value !== null && value !== \"\")\n      parts.push(`${key}=${enc(String(value))}`);\n  }\n  return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/** A cart that can still be bought: `OPEN`, or `LOCKED` mid-checkout. */\nfunction isLive(cart: Cart): boolean {\n  return cart.status === \"OPEN\" || cart.status === \"LOCKED\";\n}\n\nfunction noCart(): StorefrontError {\n  return clientError(\"NO_CART\", \"There is no cart yet. Add a line first.\");\n}\n\n/**\n * A client for one storefront, named by its token (public `odpk_…` or preview `odpv_…`):\n * `createStorefrontClient({ token: \"odpk_…\" })`.\n */\nexport function createStorefrontClient(options: StorefrontClientOptions): StorefrontClient {\n  const token = typeof options?.token === \"string\" ? options.token.trim() : \"\";\n  if (token === \"\") {\n    throw new TypeError(\n      \"createStorefrontClient: `token` is required (a public token, odpk_…, or a preview token, odpv_…).\",\n    );\n  }\n  const apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, \"\");\n  const apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n  const send = createTransport({\n    baseUrl: `${apiUrl}/storefront-api/${apiVersion}`,\n    token,\n    fetch: options.fetch,\n  });\n  const ids = createCartIdStore(cartStorageKey(token), options.cartStorage ?? \"localStorage\");\n  const listeners = new Set<(cart: Cart | null) => void>();\n\n  const emit = (cart: Cart | null): void => {\n    for (const listener of [...listeners]) {\n      try {\n        listener(cart);\n      } catch (error) {\n        reportListenerError(error);\n      }\n    }\n  };\n\n  // Cart calls run one after another (see CartClient).\n  let tail: Promise<unknown> = Promise.resolve();\n  const serial = <T>(task: () => Promise<T>): Promise<T> => {\n    const run = tail.then(task);\n    tail = run.catch(() => undefined);\n    return run;\n  };\n\n  const remember = (cart: Cart): Cart => {\n    ids.write(cart.id);\n    emit(cart);\n    return cart;\n  };\n\n  /** `GET /carts/{id}`; `null` on 404. */\n  const fetchCart = async (id: string): Promise<Cart | null> => {\n    try {\n      return await send<Cart>(\"GET\", `/carts/${enc(id)}`);\n    } catch (error) {\n      if (error instanceof StorefrontError && error.status === 404) return null;\n      throw error;\n    }\n  };\n\n  /** Whether the stored cart is definitely over (unknown, converted or abandoned). Unsure is no. */\n  const isGone = async (id: string): Promise<boolean> => {\n    try {\n      const cart = await fetchCart(id);\n      return cart === null || !isLive(cart);\n    } catch {\n      return false;\n    }\n  };\n\n  /**\n   * One change to the stored cart. `edit` gets the cart's path (`/carts/{id}`). A `CART_LOCKED`\n   * answer releases the cart (the buyer left checkout) and retries once. When the stored cart\n   * turns out to be over, it is forgotten and, if the change can start a cart (`create`), a new\n   * one is made with it; otherwise the error is thrown.\n   */\n  const change = (edit: (path: string) => Promise<Cart>, create?: NewCart): Promise<Cart> =>\n    serial(async () => {\n      const id = ids.read();\n      if (id !== null) {\n        const path = `/carts/${enc(id)}`;\n        try {\n          try {\n            return remember(await edit(path));\n          } catch (error) {\n            if (!hasCode(error, \"CART_LOCKED\")) throw error;\n            await send<Cart>(\"POST\", `${path}/release`);\n            return remember(await edit(path));\n          }\n        } catch (error) {\n          // A 404 can also mean \"no such variant or line\", so ask before dropping the cart.\n          if (!hasCode(error, \"NOT_FOUND\", \"CART_CONVERTED\") || !(await isGone(id))) throw error;\n          ids.forget(id);\n          if (!create) {\n            emit(null);\n            throw error;\n          }\n        }\n      } else if (!create) {\n        throw noCart();\n      }\n      try {\n        return remember(await send<Cart>(\"POST\", \"/carts\", create));\n      } catch (error) {\n        if (id !== null) emit(null);\n        throw error;\n      }\n    });\n\n  const json =\n    (method: Method, suffix: string, body?: unknown) =>\n    (path: string): Promise<Cart> =>\n      send<Cart>(method, path + suffix, body);\n\n  const getCart = (): Promise<Cart | null> =>\n    serial(async () => {\n      const id = ids.read();\n      if (id === null) return null;\n      const cart = await fetchCart(id);\n      if (cart && isLive(cart)) return cart;\n      ids.forget(id);\n      emit(null);\n      return null;\n    });\n\n  const cart: CartClient = {\n    get: getCart,\n    addLine: (line) => cart.addLines([line]),\n    addLines: (lines) => change(json(\"POST\", \"/lines\", { lines }), { lines }),\n    updateLine: (lineId, quantity) => change(json(\"PUT\", `/lines/${enc(lineId)}`, { quantity })),\n    removeLine: (lineId) => change(json(\"DELETE\", `/lines/${enc(lineId)}`)),\n    setAnswers: (answers) => change(json(\"PUT\", \"/answers\", { answers }), { answers }),\n    setContact: (contact) => change(json(\"PUT\", \"/contact\", contact), { contact }),\n    applyDiscountCode: (code) => change(json(\"PUT\", \"/discount-codes\", { code })),\n    removeDiscountCode: (code) => change(json(\"DELETE\", `/discount-codes/${enc(code)}`)),\n    async checkout() {\n      const navigate = browserNavigate();\n      if (!navigate) {\n        throw clientError(\n          \"UNSUPPORTED_ENVIRONMENT\",\n          \"cart.checkout() needs a browser page. Elsewhere, send the buyer to cart.checkoutUrl yourself.\",\n        );\n      }\n      const current = await getCart();\n      if (!current) throw noCart();\n      if (!/^https?:\\/\\//i.test(current.checkoutUrl ?? \"\")) {\n        throw clientError(\"INVALID_RESPONSE\", \"The cart has no usable checkoutUrl.\");\n      }\n      navigate(current.checkoutUrl);\n    },\n    clear() {\n      ids.forget();\n      emit(null);\n    },\n  };\n\n  return {\n    shop: {\n      get: () => send<Shop>(\"GET\", \"/shop\"),\n    },\n    products: {\n      list: (params = {}) => send<ProductList>(\"GET\", `/products${query(params)}`),\n      async get(handle) {\n        if (typeof handle !== \"string\" || handle === \"\") return null;\n        try {\n          return await send<Product>(\"GET\", `/products/${enc(handle)}`);\n        } catch (error) {\n          if (error instanceof StorefrontError && error.status === 404) return null;\n          throw error;\n        }\n      },\n    },\n    tags: {\n      list: () => send<TagList>(\"GET\", \"/tags\"),\n    },\n    cart,\n    on(event, listener) {\n      if (event !== \"cart:change\")\n        throw new TypeError(`storefront.js has no \"${String(event)}\" event.`);\n      const fn = listener as (cart: Cart | null) => void;\n      listeners.add(fn);\n      return () => {\n        listeners.delete(fn);\n      };\n    },\n  };\n}\n","import type { Image, ImageSize, Money, Product, Variant } from \"./types.js\";\n\nconst formatters = new Map<string, Intl.NumberFormat>();\n\n/**\n * Money as the buyer reads it: `formatMoney({ amount: \"38.00\", currencyCode: \"USD\" })` is\n * `\"$38.00\"`. `locale` defaults to the runtime's (the browser's language); pass one for a server\n * render that must match the client. An amount or currency `Intl` can't format comes back plain\n * (`\"38.00 XYZ\"`) rather than throwing.\n */\nexport function formatMoney(money: Money, locale?: string): string {\n  const amount = Number(money.amount);\n  if (Number.isFinite(amount)) {\n    const key = `${locale ?? \"\"}|${money.currencyCode}`;\n    try {\n      let formatter = formatters.get(key);\n      if (!formatter) {\n        formatter = new Intl.NumberFormat(locale, {\n          style: \"currency\",\n          currency: money.currencyCode,\n        });\n        formatters.set(key, formatter);\n      }\n      return formatter.format(amount);\n    } catch {\n      // An unknown currency or locale: fall through.\n    }\n  }\n  return `${money.amount} ${money.currencyCode}`;\n}\n\n/**\n * The variant for a choice of option values, by option name: `variantFor(board, { Wood: \"Walnut\" })`.\n * `undefined` until every option has a value, or when that combination isn't sold. A product\n * without options has one variant, which `variantFor(product, {})` returns. Names and values\n * match exactly, as the API spells them.\n */\nexport function variantFor(\n  product: Product,\n  selected: { [optionName: string]: string },\n): Variant | undefined {\n  const hasOptions = (product.options ?? []).length > 0;\n  return (product.variants ?? []).find((variant) => {\n    const options = variant.selectedOptions ?? [];\n    return options.length > 0\n      ? options.every((option) => selected[option.name] === option.value)\n      : !hasOptions;\n  });\n}\n\n/**\n * An image's URL at a size (`\"original\"` by default), falling back to its original URL.\n * `undefined` when there is no image, so `imageUrl(product.featuredImage, \"medium\")` is safe.\n */\nexport function imageUrl(\n  image: Image | null | undefined,\n  size: ImageSize = \"original\",\n): string | undefined {\n  if (!image) return undefined;\n  return image.sizes?.[size] || image.url || undefined;\n}\n","import type { Form, FormControl } from \"./types.js\";\n\n/** The input to render for a field. Shop forms only use these four. */\nexport type FieldType = \"TEXT\" | \"NUMBER\" | \"SELECT\" | \"CHECKBOX\";\n\nexport interface FieldOption {\n  value: string;\n  label: string;\n}\n\n/**\n * One field of a form, plain enough to render an input from. Send the answer under `key`\n * (`customizations[key]` on a cart line, `answers[key]` for checkout questions): a string for\n * `TEXT` and `SELECT` (one of the `options` values), a number for `NUMBER`, a boolean for\n * `CHECKBOX`. The server checks every answer again and explains a refusal in\n * `StorefrontError.message` (code `CUSTOMIZATION_INVALID`, with `field` set to the key).\n */\nexport interface FieldDescriptor {\n  key: string;\n  label: string;\n  type: FieldType;\n  required: boolean;\n  /** `SELECT` choices, in order. */\n  options?: FieldOption[];\n  /** `NUMBER` bounds, inclusive. */\n  min?: number;\n  max?: number;\n  /** `TEXT` length limit. */\n  maxLength?: number;\n  /** Help text to show under the input. */\n  description?: string;\n}\n\nfunction fieldType(type: FormControl[\"type\"]): FieldType {\n  return type === \"NUMBER\" || type === \"SELECT\" || type === \"CHECKBOX\" ? type : \"TEXT\";\n}\n\nconst isNumber = (value: unknown): value is number =>\n  typeof value === \"number\" && Number.isFinite(value);\n\nfunction describe(control: FormControl): FieldDescriptor | null {\n  const key = control.field ?? control.id ?? \"\";\n  if (key === \"\") return null;\n  const field: FieldDescriptor = {\n    key,\n    label: control.label ?? \"\",\n    type: fieldType(control.type),\n    required: false,\n  };\n  for (const validator of control.validators ?? []) {\n    switch (validator?.type) {\n      case \"REQUIRED\":\n        field.required = true;\n        break;\n      case \"MINIMUM\":\n        if (isNumber(validator.min)) field.min = validator.min;\n        break;\n      case \"MAXIMUM\":\n        if (isNumber(validator.max)) field.max = validator.max;\n        break;\n      case \"MAX_LENGTH\":\n        if (isNumber(validator.max)) field.maxLength = validator.max;\n        break;\n    }\n  }\n  if (field.type === \"SELECT\") {\n    field.options = (control.options ?? [])\n      .map((option) => {\n        const value = option?.value == null ? \"\" : String(option.value);\n        return { value, label: option?.label ?? value };\n      })\n      .filter((option) => option.value !== \"\");\n  }\n  const description = control.description || control.helpText;\n  if (description) field.description = description;\n  return field;\n}\n\nfunction walk(controls: FormControl[] | undefined, out: FieldDescriptor[]): void {\n  for (const control of controls ?? []) {\n    if (!control || control.hidden) continue;\n    if (control.kind === \"FIELD\") {\n      const field = describe(control);\n      if (field) out.push(field);\n    } else if (control.kind !== \"ARRAY\") {\n      // FORM, GROUP or an unknown container: its fields join the list where it stands.\n      walk(control.controls, out);\n    }\n  }\n}\n\n/**\n * A form's fields, in order, as plain descriptors: a product's `customizationForm` or the shop's\n * `checkoutQuestions`. Groups are flattened, hidden fields left out. No form is no fields.\n *\n * A field's key is its `field` (else its `id`); `required`, `min`, `max` and `maxLength` come from\n * its validators; its `description` is the field's `description`, else its `helpText`. `MIN_LENGTH`\n * and `PATTERN` rules aren't described: the server checks them and explains a refusal\n * (`CUSTOMIZATION_INVALID`).\n */\nexport function customizationFields(form?: Form | null): FieldDescriptor[] {\n  const out: FieldDescriptor[] = [];\n  if (!form) return out;\n  walk(form.controls, out);\n  if (out.length === 0 && form.kind === \"FIELD\" && !form.hidden) {\n    const field = describe(form);\n    if (field) out.push(field);\n  }\n  return out;\n}\n"],"names":["globals","browserStorage","browserNavigate","location","url","reportListenerError","error","report","cartStorageKey","token","createCartIdStore","key","mode","memory","persisted","enabled","storage","read","store","id","StorefrontError","init","self","hasCode","codes","clientError","code","message","cause","fallbackCode","status","parseRetryAfter","value","now","raw","at","stringProp","body","errorFromResponse","response","retryAfter","TOKEN_HEADER","createTransport","options","baseUrl","doFetch","input","method","path","headers","text","data","parsed","DEFAULT_API_URL","DEFAULT_API_VERSION","enc","query","params","parts","isLive","cart","noCart","createStorefrontClient","apiUrl","apiVersion","send","ids","listeners","emit","listener","tail","serial","task","run","remember","fetchCart","isGone","change","edit","create","json","suffix","getCart","line","lines","lineId","quantity","answers","contact","navigate","current","handle","event","fn","formatters","formatMoney","money","locale","amount","formatter","variantFor","product","selected","hasOptions","variant","option","imageUrl","image","size","fieldType","type","isNumber","describe","control","field","validator","description","walk","controls","out","customizationFields","form"],"mappings":"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"}