@umxr/bagsy 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../core/src/ids.ts","../../core/src/operations.ts","../../core/src/errors.ts","../../core/src/transport.ts","../../core/src/account.ts","../../core/src/relay.ts","../../core/src/client.ts","../../core/src/visitor.ts"],"sourcesContent":["/**\n * Shopify IDs arrive in two shapes: numeric (Liquid's `product.id`) and GIDs\n * (Storefront and Customer Account APIs). Bagsy accepts either and always\n * sends GIDs on the wire.\n */\nexport type ProductRef = string | number;\nexport type VariantRef = string | number;\nexport type CustomerRef = string | number;\n\ntype Resource = 'Product' | 'ProductVariant' | 'Customer';\n\nfunction toGid(resource: Resource, ref: string | number): string {\n const value = String(ref).trim();\n const prefix = `gid://shopify/${resource}/`;\n if (value.startsWith(prefix)) {\n const id = value.slice(prefix.length);\n if (/^\\d+$/.test(id)) return value;\n } else if (/^\\d+$/.test(value)) {\n return `${prefix}${value}`;\n }\n throw new TypeError(`Expected a numeric ID or ${prefix}<id>, received \"${value}\"`);\n}\n\nexport const productGid = (ref: ProductRef) => toGid('Product', ref);\nexport const variantGid = (ref: VariantRef) => toGid('ProductVariant', ref);\nexport const customerGid = (ref: CustomerRef) => toGid('Customer', ref);\n","// Every API call as a standalone function over a Transport. `createBagsy()`\n// groups these into a client; import them directly when bundle size matters\n// (bundlers drop the ones you don't use).\nimport { productGid, variantGid, type ProductRef, type VariantRef } from './ids.js';\nimport type { Transport } from './transport.js';\nimport type {\n Item,\n ItemOptions,\n ItemPage,\n List,\n ListRef,\n MergeResult,\n PageOptions,\n SavedEntry,\n SavedLookup,\n Schemas,\n SharedList,\n TopProduct,\n} from './types.js';\n\nconst MAX_LOOKUP = 100;\nconst listPath = (listId: ListRef) => `/lists/${encodeURIComponent(listId)}`;\nconst itemPath = (listId: ListRef, itemId: string) => `${listPath(listId)}/items/${encodeURIComponent(itemId)}`;\n\n// Lists ------------------------------------------------------------------\n\n/** The owner's lists, default first. */\nexport const listLists = async (t: Transport): Promise<List[]> =>\n (await t.request<{ data: List[] }>('GET', '/lists')).data;\n\nexport const getList = (t: Transport, listId: ListRef = 'default', { includeItems = false } = {}): Promise<List> =>\n t.request('GET', listPath(listId), { query: { include: includeItems ? 'items' : undefined } });\n\nexport const createList = (t: Transport, name: string): Promise<List> =>\n t.request('POST', '/lists', { body: { name } satisfies Schemas['ListCreate'] });\n\nexport const renameList = (t: Transport, listId: ListRef, name: string): Promise<List> =>\n t.request('PATCH', listPath(listId), { body: { name } satisfies Schemas['ListUpdate'] });\n\nexport const deleteList = async (t: Transport, listId: ListRef): Promise<void> => {\n await t.request('DELETE', listPath(listId));\n};\n\n/**\n * The list's share token, creating it on first share. Stable across calls, so\n * links already sent keep working; `rotate: true` issues a new one.\n */\nexport const shareList = async (\n t: Transport,\n listId: ListRef = 'default',\n { rotate = false }: { rotate?: boolean } = {},\n): Promise<string> =>\n (await t.request<{ shareToken: string }>('POST', `${listPath(listId)}/share`, { query: { rotate: rotate ? 'true' : undefined } }))\n .shareToken;\n\nexport const unshareList = async (t: Transport, listId: ListRef = 'default'): Promise<void> => {\n await t.request('DELETE', `${listPath(listId)}/share`);\n};\n\n// Items ------------------------------------------------------------------\n\nconst itemBody = (product: ProductRef, variantId?: VariantRef, extra: { quantity?: number; note?: string } = {}) =>\n ({\n productId: productGid(product),\n ...(variantId !== undefined && { variantId: variantGid(variantId) }),\n ...(extra.quantity !== undefined && { quantity: extra.quantity }),\n ...(extra.note !== undefined && { note: extra.note }),\n }) satisfies Schemas['ItemCreate'];\n\nexport const listItems = (t: Transport, listId: ListRef = 'default', page: PageOptions = {}): Promise<ItemPage> =>\n t.request('GET', `${listPath(listId)}/items`, { query: { ...page } });\n\n/** Every item in a list, fetched page by page. */\nexport async function* iterateItems(\n t: Transport,\n listId: ListRef = 'default',\n { limit = 100 }: { limit?: number } = {},\n): AsyncGenerator<Item> {\n let after: string | undefined;\n do {\n const page = await listItems(t, listId, { limit, after });\n yield* page.data;\n after = page.pageInfo.hasNextPage ? (page.pageInfo.endCursor ?? undefined) : undefined;\n } while (after);\n}\n\n/** Save a product. Saving it again returns the existing item. */\nexport const addItem = (\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId, quantity, note }: ItemOptions & { quantity?: number; note?: string } = {},\n): Promise<Item> => t.request('POST', `${listPath(listId)}/items`, { body: itemBody(product, variantId, { quantity, note }) });\n\n/** Unsave by product, without knowing the item id. No-op if not saved. */\nexport const removeProduct = async (\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId }: ItemOptions = {},\n): Promise<void> => {\n await t.request('DELETE', `${listPath(listId)}/items`, {\n query: { productId: productGid(product), variantId: variantId === undefined ? undefined : variantGid(variantId) },\n });\n};\n\n/** Save if unsaved, unsave if saved. Built for heart buttons. */\nexport async function toggleItem(\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId }: ItemOptions = {},\n): Promise<{ saved: boolean }> {\n const { status } = await t.send('POST', `${listPath(listId)}/items`, { body: itemBody(product, variantId) });\n // 201 = newly saved. 200 = it was already saved, so this tap unsaves it.\n if (status === 201) return { saved: true };\n await removeProduct(t, product, { listId, variantId });\n return { saved: false };\n}\n\nexport const updateItem = (\n t: Transport,\n itemId: string,\n patch: { variantId?: VariantRef | null; quantity?: number; note?: string | null },\n { listId = 'default' }: { listId?: ListRef } = {},\n): Promise<Item> => {\n const body: Schemas['ItemUpdate'] = {\n ...(patch.quantity !== undefined && { quantity: patch.quantity }),\n ...(patch.note !== undefined && { note: patch.note }),\n ...(patch.variantId !== undefined && { variantId: patch.variantId === null ? null : variantGid(patch.variantId) }),\n };\n return t.request('PATCH', itemPath(listId, itemId), { body });\n};\n\nexport const removeItem = async (t: Transport, itemId: string, { listId = 'default' }: { listId?: ListRef } = {}) => {\n await t.request('DELETE', itemPath(listId, itemId));\n};\n\n// Lookup -----------------------------------------------------------------\n\n/**\n * Saved state for a set of products across all of the owner's lists.\n * Accepts any number of products; batches of 100 are fetched in parallel.\n */\nexport async function lookupSaved(t: Transport, products: Iterable<ProductRef>): Promise<SavedLookup> {\n const ids = [...new Set([...products].map(productGid))];\n const chunks: string[][] = [];\n for (let i = 0; i < ids.length; i += MAX_LOOKUP) chunks.push(ids.slice(i, i + MAX_LOOKUP));\n\n const pages = await Promise.all(\n chunks.map((productIds) => t.request<{ data: Record<string, SavedEntry[]> }>('GET', '/saved', { query: { productIds } })),\n );\n const byProduct: Record<string, SavedEntry[]> = {};\n for (const id of ids) byProduct[id] = [];\n for (const page of pages) Object.assign(byProduct, page.data);\n\n return {\n byProduct,\n get: (product) => byProduct[productGid(product)] ?? [],\n has(product, variant) {\n const entries = byProduct[productGid(product)] ?? [];\n if (variant === undefined) return entries.length > 0;\n const gid = variantGid(variant);\n return entries.some((entry) => entry.variantId === gid || entry.variantId == null);\n },\n };\n}\n\n// Identity, sharing, insights ------------------------------------------\n\n/**\n * Fold a guest's lists into the logged-in customer's. Call once after\n * login; safe to retry. Defaults to the transport's `visitorId`.\n */\nexport function mergeVisitor(t: Transport, visitorId: string | undefined = t.options.visitorId): Promise<MergeResult> {\n if (!visitorId) throw new TypeError('identity.merge: no visitorId to merge.');\n if (t.options.mode === 'server' && t.options.customerId === undefined) {\n throw new TypeError('identity.merge: set `customerId` (e.g. via bagsy.as({ customerId })) first.');\n }\n return t.request('POST', '/identity/merge', { body: { visitorId } });\n}\n\n/** Public, read-only view of a shared list. */\nexport const getSharedList = (t: Transport, shareToken: string, page: PageOptions = {}): Promise<SharedList> =>\n t.request('GET', `/shared/${encodeURIComponent(shareToken)}`, { query: { ...page } });\n\n/** Most-saved products. Server key only. */\nexport async function topProducts(\n t: Transport,\n query: { since?: Date | string; limit?: number } = {},\n): Promise<TopProduct[]> {\n if (t.options.mode !== 'server') {\n throw new Error('bagsy.insights.topProducts() needs a server key; call it from your server.');\n }\n const since = query.since instanceof Date ? query.since.toISOString() : query.since;\n return (await t.request<{ data: TopProduct[] }>('GET', '/insights/top-products', { query: { since, limit: query.limit } }))\n .data;\n}\n","import type { components } from './generated/schema.js';\n\nexport type Problem = components['schemas']['Problem'];\nexport type BagsyErrorCode = Problem['code'] | 'unexpected_response';\n\n/** Thrown for any non-2xx response. Switch on `code`, never on `message`. */\nexport class BagsyError extends Error {\n readonly name = 'BagsyError';\n readonly status: number;\n readonly code: BagsyErrorCode;\n readonly problem?: Problem;\n\n constructor(status: number, problem: unknown) {\n const parsed = isProblem(problem) ? problem : undefined;\n super(parsed?.detail ?? parsed?.title ?? `Bagsy request failed with status ${status}`);\n this.status = status;\n this.code = parsed?.code ?? 'unexpected_response';\n this.problem = parsed;\n }\n}\n\nfunction isProblem(value: unknown): value is Problem {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Problem).code === 'string' &&\n typeof (value as Problem).status === 'number'\n );\n}\n","import { BagsyError } from './errors.js';\nimport { customerGid } from './ids.js';\nimport type { BagsyOptions, RelayOptions, RequestOptions } from './types.js';\n\n/** How operations reach the API: base URL, identity headers and fetch. */\nexport interface Transport {\n readonly options: BagsyOptions;\n send<T>(method: string, path: string, init?: RequestOptions): Promise<{ status: number; data: T }>;\n request<T>(method: string, path: string, init?: RequestOptions): Promise<T>;\n}\n\n/** Proxy, server and account modes. Relay mode has its own: `createRelayTransport()`. */\nexport function createTransport(options: Exclude<BagsyOptions, RelayOptions>): Transport {\n if (options.mode === 'server') {\n if (!options.apiKey) throw new TypeError('createBagsy: `apiKey` is required in server mode.');\n const browser = globalThis as { window?: unknown; document?: unknown };\n if (browser.window !== undefined && browser.document !== undefined) {\n throw new Error(\n 'createBagsy: server mode exposes your secret key and must not run in a browser. ' +\n \"Use mode: 'proxy' in themes, or call Bagsy from your Hydrogen loaders and actions.\",\n );\n }\n }\n\n const baseUrl = (\n options.baseUrl ??\n (options.mode === 'proxy' ? `${(options.proxyPath ?? '/apps/bagsy').replace(/\\/$/, '')}/v1` : 'https://api.bagsy.dev/v1')\n ).replace(/\\/$/, '');\n\n const headers: Record<string, string> = { Accept: 'application/json' };\n if (options.visitorId) headers['Bagsy-Visitor-Id'] = options.visitorId;\n if (options.mode === 'server') {\n headers.Authorization = `Bearer ${options.apiKey}`;\n if (options.customerId !== undefined) headers['Bagsy-Customer-Id'] = customerGid(options.customerId);\n }\n\n /** One request. Query arrays are comma-joined; undefined values are dropped. */\n async function send<T>(method: string, path: string, { query = {}, body }: RequestOptions = {}) {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) params.set(key, Array.isArray(value) ? value.join(',') : String(value));\n }\n // Shopify's app proxy only guarantees query parameters reach the app (and\n // signs them), so the visitor travels there too.\n if (options.mode === 'proxy' && options.visitorId) params.set('visitor_id', options.visitorId);\n\n const search = params.toString();\n const response = await (options.fetch ?? fetch)(`${baseUrl}${path}${search ? `?${search}` : ''}`, {\n method,\n headers: body === undefined ? headers : { ...headers, 'Content-Type': 'application/json' },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n const text = await response.text();\n let data: unknown;\n try {\n data = text ? JSON.parse(text) : undefined;\n } catch {\n data = text;\n }\n if (!response.ok) throw new BagsyError(response.status, data);\n return { status: response.status, data: data as T };\n }\n\n return {\n options,\n send,\n request: async (method, path, init) => (await send(method, path, init)).data as never,\n };\n}\n","import { createTransport, type Transport } from './transport.js';\nimport type { AccountOptions } from './types.js';\n\n/**\n * Transport for customer account UI extensions: every request carries a\n * current Shopify session token. Kept separate from createTransport so\n * storefront bundles that never use it don't pay for it.\n */\nexport function createAccountTransport({\n sessionToken,\n baseUrl = 'https://api.bagsy.dev/account/v1',\n fetch: fetchImpl,\n ...rest\n}: AccountOptions): Transport {\n return createTransport({\n ...rest,\n mode: 'account',\n sessionToken,\n baseUrl,\n // Session tokens last five minutes and Shopify caches them, so ask every time.\n fetch: async (input, init) => {\n const headers = new Headers(init?.headers);\n headers.set('Authorization', `Bearer ${await sessionToken()}`);\n return (fetchImpl ?? fetch)(input, { ...init, headers });\n },\n });\n}\n","import { BagsyError } from './errors.js';\nimport type { Transport } from './transport.js';\nimport type { RelayOptions, RequestOptions } from './types.js';\n\n/** One Bagsy API call, as the browser sends it to your server's relay endpoint. */\nexport interface RelayRequest {\n method: string;\n /** API path below `/v1`, e.g. `/lists/default/items`. */\n path: string;\n query?: Record<string, string>;\n body?: unknown;\n}\n\n/** The API's answer, passed back unchanged so errors keep their status and problem. */\nexport interface RelayResponse<T = unknown> {\n status: number;\n data: T;\n}\n\n/**\n * Transport for browsers on headless storefronts (Hydrogen and other React\n * apps). Each call goes to one endpoint on your own server, which adds the\n * secret key and the shopper's identity from its session, then calls Bagsy.\n * The browser never holds a key, a customer id or a guest id.\n */\nexport function createRelayTransport(options: RelayOptions): Transport {\n const endpoint = options.endpoint ?? '/api/bagsy';\n\n async function send<T>(method: string, path: string, { query = {}, body }: RequestOptions = {}) {\n const params: Record<string, string> = {};\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) params[key] = Array.isArray(value) ? value.join(',') : String(value);\n }\n const call: RelayRequest = { method, path, ...(Object.keys(params).length && { query: params }), ...(body !== undefined && { body }) };\n\n const response = await (options.fetch ?? fetch)(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n // Same-origin fetches send the session cookie by default.\n body: JSON.stringify(call),\n });\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n payload = undefined;\n }\n if (!response.ok || !isRelayResponse(payload)) {\n // The relay itself failed (not wired up, crashed, or rejected the call).\n throw new BagsyError(response.ok ? 502 : response.status, payload);\n }\n if (payload.status >= 400) throw new BagsyError(payload.status, payload.data);\n return payload as RelayResponse<T>;\n }\n\n return {\n options,\n send,\n request: async (method, path, init) => (await send(method, path, init)).data as never,\n };\n}\n\nfunction isRelayResponse(value: unknown): value is RelayResponse {\n return typeof value === 'object' && value !== null && typeof (value as RelayResponse).status === 'number';\n}\n","import * as op from './operations.js';\nimport { createAccountTransport } from './account.js';\nimport { createRelayTransport } from './relay.js';\nimport { createTransport } from './transport.js';\nimport type { BagsyOptions, ItemOptions, ListRef, Owner, PageOptions } from './types.js';\nimport type { ProductRef, VariantRef } from './ids.js';\n\n/**\n * The Bagsy client. Every method is a thin wrapper over a standalone function\n * in `operations` — use those directly if you want only what you import.\n */\nexport function createBagsy(options: BagsyOptions) {\n const t =\n options.mode === 'account'\n ? createAccountTransport(options)\n : options.mode === 'relay'\n ? createRelayTransport(options)\n : createTransport(options);\n\n const items = {\n list: (listId?: ListRef, page?: PageOptions) => op.listItems(t, listId, page),\n /** Every item in a list, fetched page by page. */\n iterate: (listId?: ListRef, opts?: { limit?: number }) => op.iterateItems(t, listId, opts),\n /** Save a product. Saving it again returns the existing item. */\n add: (product: ProductRef, opts?: ItemOptions & { quantity?: number; note?: string }) => op.addItem(t, product, opts),\n /** Save if unsaved, unsave if saved. Built for heart buttons. */\n toggle: (product: ProductRef, opts?: ItemOptions) => op.toggleItem(t, product, opts),\n update: (\n itemId: string,\n patch: { variantId?: VariantRef | null; quantity?: number; note?: string | null },\n opts?: { listId?: ListRef },\n ) => op.updateItem(t, itemId, patch, opts),\n remove: (itemId: string, opts?: { listId?: ListRef }) => op.removeItem(t, itemId, opts),\n /** Unsave by product, without knowing the item id. No-op if not saved. */\n removeProduct: (product: ProductRef, opts?: ItemOptions) => op.removeProduct(t, product, opts),\n };\n\n return {\n lists: {\n /** The owner's lists, default first. */\n list: () => op.listLists(t),\n get: (listId?: ListRef, opts?: { includeItems?: boolean }) => op.getList(t, listId, opts),\n create: (name: string) => op.createList(t, name),\n rename: (listId: ListRef, name: string) => op.renameList(t, listId, name),\n delete: (listId: ListRef) => op.deleteList(t, listId),\n /** The list's share token, created on first share. `rotate: true` issues a new one. */\n share: (listId?: ListRef, opts?: { rotate?: boolean }) => op.shareList(t, listId, opts),\n unshare: (listId?: ListRef) => op.unshareList(t, listId),\n },\n items,\n /**\n * Saved state for a set of products across all of the owner's lists.\n * Accepts any number of products; batches of 100 are fetched in parallel.\n */\n saved: (products: Iterable<ProductRef>) => op.lookupSaved(t, products),\n identity: {\n /** Fold a guest's lists into the logged-in customer's. Safe to retry. */\n merge: (visitorId?: string) => op.mergeVisitor(t, visitorId),\n },\n shared: {\n /** Public, read-only view of a shared list. */\n get: (shareToken: string, page?: PageOptions) => op.getSharedList(t, shareToken, page),\n },\n insights: {\n /** Most-saved products. Server key only. */\n topProducts: (query?: { since?: Date | string; limit?: number }) => op.topProducts(t, query),\n },\n\n /**\n * A copy of this client acting for a specific shopper. In Hydrogen, call\n * once per request with the customer from your Customer Account session.\n */\n as(owner: Owner) {\n if (options.mode === 'account') {\n throw new TypeError('bagsy.as: in customer accounts the session token identifies the customer.');\n }\n if (options.mode === 'relay') {\n throw new TypeError(\"bagsy.as: through a relay, your server's session identifies the shopper.\");\n }\n if (options.mode === 'proxy') {\n if (owner.customerId !== undefined) {\n throw new TypeError('bagsy.as: on the app proxy the customer comes from Shopify; pass only `visitorId`.');\n }\n return createBagsy({ ...options, visitorId: owner.visitorId ?? options.visitorId });\n }\n return createBagsy({ ...options, ...owner });\n },\n };\n}\n\nexport type Bagsy = ReturnType<typeof createBagsy>;\n","const STORAGE_KEY = 'bagsy:visitor-id';\n\n/** The slice of Web Storage this module uses; `localStorage` satisfies it. */\nexport interface VisitorStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nlet memoryFallback: string | undefined;\n\n/**\n * Returns this browser's guest identity, creating it on first call.\n *\n * Persisted in localStorage. Falls back to an in-memory ID (for the page's\n * lifetime) when storage is blocked, e.g. in private windows or iframes.\n */\nexport function getVisitorId(storage: Pick<VisitorStorage, 'getItem' | 'setItem'> | undefined = safeLocalStorage()): string {\n try {\n const existing = storage?.getItem(STORAGE_KEY);\n if (existing) return existing;\n } catch {\n // Storage unreadable; fall through to a fresh ID.\n }\n\n const id = memoryFallback ?? crypto.randomUUID();\n memoryFallback = id;\n try {\n storage?.setItem(STORAGE_KEY, id);\n } catch {\n // Storage unwritable; the in-memory ID still works for this page.\n }\n return id;\n}\n\n/** The guest identity if one exists, without creating it. Use to decide whether to merge on login. */\nexport function peekVisitorId(storage: Pick<VisitorStorage, 'getItem'> | undefined = safeLocalStorage()): string | undefined {\n try {\n return storage?.getItem(STORAGE_KEY) ?? memoryFallback;\n } catch {\n return memoryFallback;\n }\n}\n\n/** Forget the guest identity, e.g. after a successful merge on login. */\nexport function clearVisitorId(storage: Pick<VisitorStorage, 'removeItem'> | undefined = safeLocalStorage()): void {\n memoryFallback = undefined;\n try {\n storage?.removeItem(STORAGE_KEY);\n } catch {\n // Nothing to clear.\n }\n}\n\nfunction safeLocalStorage(): VisitorStorage | undefined {\n try {\n // Read via globalThis so this compiles without the DOM lib (Workers, Node).\n return (globalThis as { localStorage?: VisitorStorage }).localStorage;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../core/src/ids.ts","../../core/src/operations.ts","../../core/src/errors.ts","../../core/src/transport.ts","../../core/src/account.ts","../../core/src/relay.ts","../../core/src/client.ts","../../core/src/visitor.ts"],"sourcesContent":["/**\n * Shopify IDs arrive in two shapes: numeric (Liquid's `product.id`) and GIDs\n * (Storefront and Customer Account APIs). Bagsy accepts either and always\n * sends GIDs on the wire.\n */\nexport type ProductRef = string | number;\nexport type VariantRef = string | number;\nexport type CustomerRef = string | number;\n\ntype Resource = 'Product' | 'ProductVariant' | 'Customer';\n\nfunction toGid(resource: Resource, ref: string | number): string {\n const value = String(ref).trim();\n const prefix = `gid://shopify/${resource}/`;\n if (value.startsWith(prefix)) {\n const id = value.slice(prefix.length);\n if (/^\\d+$/.test(id)) return value;\n } else if (/^\\d+$/.test(value)) {\n return `${prefix}${value}`;\n }\n throw new TypeError(`Expected a numeric ID or ${prefix}<id>, received \"${value}\"`);\n}\n\nexport const productGid = (ref: ProductRef) => toGid('Product', ref);\nexport const variantGid = (ref: VariantRef) => toGid('ProductVariant', ref);\nexport const customerGid = (ref: CustomerRef) => toGid('Customer', ref);\n","// Every API call as a standalone function over a Transport. `createBagsy()`\n// groups these into a client; import them directly when bundle size matters\n// (bundlers drop the ones you don't use).\nimport { productGid, variantGid, type ProductRef, type VariantRef } from './ids.js';\nimport type { Transport } from './transport.js';\nimport type {\n Item,\n ItemOptions,\n ItemPage,\n List,\n ListRef,\n MergeResult,\n PageOptions,\n SavedEntry,\n SavedLookup,\n Schemas,\n SharedList,\n TopProduct,\n} from './types.js';\n\nconst MAX_LOOKUP = 100;\nconst listPath = (listId: ListRef) => `/lists/${encodeURIComponent(listId)}`;\nconst itemPath = (listId: ListRef, itemId: string) => `${listPath(listId)}/items/${encodeURIComponent(itemId)}`;\n\n// Lists ------------------------------------------------------------------\n\n/** The owner's lists, default first. */\nexport const listLists = async (t: Transport): Promise<List[]> =>\n (await t.request<{ data: List[] }>('GET', '/lists')).data;\n\nexport const getList = (t: Transport, listId: ListRef = 'default', { includeItems = false } = {}): Promise<List> =>\n t.request('GET', listPath(listId), { query: { include: includeItems ? 'items' : undefined } });\n\nexport const createList = (t: Transport, name: string): Promise<List> =>\n t.request('POST', '/lists', { body: { name } satisfies Schemas['ListCreate'] });\n\nexport const renameList = (t: Transport, listId: ListRef, name: string): Promise<List> =>\n t.request('PATCH', listPath(listId), { body: { name } satisfies Schemas['ListUpdate'] });\n\nexport const deleteList = async (t: Transport, listId: ListRef): Promise<void> => {\n await t.request('DELETE', listPath(listId));\n};\n\n/**\n * The list's share token, creating it on first share. Stable across calls, so\n * links already sent keep working; `rotate: true` issues a new one.\n */\nexport const shareList = async (\n t: Transport,\n listId: ListRef = 'default',\n { rotate = false }: { rotate?: boolean } = {},\n): Promise<string> =>\n (await t.request<{ shareToken: string }>('POST', `${listPath(listId)}/share`, { query: { rotate: rotate ? 'true' : undefined } }))\n .shareToken;\n\nexport const unshareList = async (t: Transport, listId: ListRef = 'default'): Promise<void> => {\n await t.request('DELETE', `${listPath(listId)}/share`);\n};\n\n// Items ------------------------------------------------------------------\n\nconst itemBody = (product: ProductRef, variantId?: VariantRef, extra: { quantity?: number; note?: string } = {}) =>\n ({\n productId: productGid(product),\n ...(variantId !== undefined && { variantId: variantGid(variantId) }),\n ...(extra.quantity !== undefined && { quantity: extra.quantity }),\n ...(extra.note !== undefined && { note: extra.note }),\n }) satisfies Schemas['ItemCreate'];\n\nexport const listItems = (t: Transport, listId: ListRef = 'default', page: PageOptions = {}): Promise<ItemPage> =>\n t.request('GET', `${listPath(listId)}/items`, { query: { ...page } });\n\n/** Every item in a list, fetched page by page. */\nexport async function* iterateItems(\n t: Transport,\n listId: ListRef = 'default',\n { limit = 100 }: { limit?: number } = {},\n): AsyncGenerator<Item> {\n let after: string | undefined;\n do {\n const page = await listItems(t, listId, { limit, after });\n yield* page.data;\n after = page.pageInfo.hasNextPage ? (page.pageInfo.endCursor ?? undefined) : undefined;\n } while (after);\n}\n\n/** Save a product. Saving it again returns the existing item. */\nexport const addItem = (\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId, quantity, note }: ItemOptions & { quantity?: number; note?: string } = {},\n): Promise<Item> => t.request('POST', `${listPath(listId)}/items`, { body: itemBody(product, variantId, { quantity, note }) });\n\n/** Unsave by product, without knowing the item id. No-op if not saved. */\nexport const removeProduct = async (\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId }: ItemOptions = {},\n): Promise<void> => {\n await t.request('DELETE', `${listPath(listId)}/items`, {\n query: { productId: productGid(product), variantId: variantId === undefined ? undefined : variantGid(variantId) },\n });\n};\n\n/** Save if unsaved, unsave if saved. Built for heart buttons. */\nexport async function toggleItem(\n t: Transport,\n product: ProductRef,\n { listId = 'default', variantId }: ItemOptions = {},\n): Promise<{ saved: boolean }> {\n const { status } = await t.send('POST', `${listPath(listId)}/items`, { body: itemBody(product, variantId) });\n // 201 = newly saved. 200 = it was already saved, so this tap unsaves it.\n if (status === 201) return { saved: true };\n await removeProduct(t, product, { listId, variantId });\n return { saved: false };\n}\n\nexport const updateItem = (\n t: Transport,\n itemId: string,\n patch: { variantId?: VariantRef | null; quantity?: number; note?: string | null },\n { listId = 'default' }: { listId?: ListRef } = {},\n): Promise<Item> => {\n const body: Schemas['ItemUpdate'] = {\n ...(patch.quantity !== undefined && { quantity: patch.quantity }),\n ...(patch.note !== undefined && { note: patch.note }),\n ...(patch.variantId !== undefined && { variantId: patch.variantId === null ? null : variantGid(patch.variantId) }),\n };\n return t.request('PATCH', itemPath(listId, itemId), { body });\n};\n\nexport const removeItem = async (t: Transport, itemId: string, { listId = 'default' }: { listId?: ListRef } = {}) => {\n await t.request('DELETE', itemPath(listId, itemId));\n};\n\n// Lookup -----------------------------------------------------------------\n\n/**\n * Saved state for a set of products across all of the owner's lists.\n * Accepts any number of products; batches of 100 are fetched in parallel.\n */\nexport async function lookupSaved(t: Transport, products: Iterable<ProductRef>): Promise<SavedLookup> {\n const ids = [...new Set([...products].map(productGid))];\n const chunks: string[][] = [];\n for (let i = 0; i < ids.length; i += MAX_LOOKUP) chunks.push(ids.slice(i, i + MAX_LOOKUP));\n\n const pages = await Promise.all(\n chunks.map((productIds) => t.request<{ data: Record<string, SavedEntry[]> }>('GET', '/saved', { query: { productIds } })),\n );\n const byProduct: Record<string, SavedEntry[]> = {};\n for (const id of ids) byProduct[id] = [];\n for (const page of pages) Object.assign(byProduct, page.data);\n\n return {\n byProduct,\n get: (product) => byProduct[productGid(product)] ?? [],\n has(product, variant) {\n const entries = byProduct[productGid(product)] ?? [];\n if (variant === undefined) return entries.length > 0;\n const gid = variantGid(variant);\n return entries.some((entry) => entry.variantId === gid || entry.variantId == null);\n },\n };\n}\n\n// Identity, sharing, insights ------------------------------------------\n\n/**\n * Fold a guest's lists into the logged-in customer's. Call once after\n * login; safe to retry. Defaults to the transport's `visitorId`.\n */\nexport function mergeVisitor(t: Transport, visitorId: string | undefined = t.options.visitorId): Promise<MergeResult> {\n if (!visitorId) throw new TypeError('identity.merge: no visitorId to merge.');\n if (t.options.mode === 'server' && t.options.customerId === undefined) {\n throw new TypeError('identity.merge: set `customerId` (e.g. via bagsy.as({ customerId })) first.');\n }\n return t.request('POST', '/identity/merge', { body: { visitorId } });\n}\n\n/** Public, read-only view of a shared list. */\nexport const getSharedList = (t: Transport, shareToken: string, page: PageOptions = {}): Promise<SharedList> =>\n t.request('GET', `/shared/${encodeURIComponent(shareToken)}`, { query: { ...page } });\n\n/** Most-saved products. Server key only. */\nexport async function topProducts(\n t: Transport,\n query: { since?: Date | string; limit?: number } = {},\n): Promise<TopProduct[]> {\n if (t.options.mode !== 'server') {\n throw new Error('bagsy.insights.topProducts() needs a server key; call it from your server.');\n }\n const since = query.since instanceof Date ? query.since.toISOString() : query.since;\n return (await t.request<{ data: TopProduct[] }>('GET', '/insights/top-products', { query: { since, limit: query.limit } }))\n .data;\n}\n","import type { components } from './generated/schema.js';\n\nexport type Problem = components['schemas']['Problem'];\nexport type BagsyErrorCode = Problem['code'] | 'unexpected_response';\n\n/** Thrown for any non-2xx response. Switch on `code`, never on `message`. */\nexport class BagsyError extends Error {\n readonly name = 'BagsyError';\n readonly status: number;\n readonly code: BagsyErrorCode;\n readonly problem?: Problem;\n\n constructor(status: number, problem: unknown) {\n const parsed = isProblem(problem) ? problem : undefined;\n super(parsed?.detail ?? parsed?.title ?? `Bagsy request failed with status ${status}`);\n this.status = status;\n this.code = parsed?.code ?? 'unexpected_response';\n this.problem = parsed;\n }\n}\n\nfunction isProblem(value: unknown): value is Problem {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Problem).code === 'string' &&\n typeof (value as Problem).status === 'number'\n );\n}\n","import { BagsyError } from './errors.js';\nimport { customerGid } from './ids.js';\nimport type { BagsyOptions, RelayOptions, RequestOptions } from './types.js';\n\n/** How operations reach the API: base URL, identity headers and fetch. */\nexport interface Transport {\n readonly options: BagsyOptions;\n send<T>(method: string, path: string, init?: RequestOptions): Promise<{ status: number; data: T }>;\n request<T>(method: string, path: string, init?: RequestOptions): Promise<T>;\n}\n\n/** Proxy, server and account modes. Relay mode has its own: `createRelayTransport()`. */\nexport function createTransport(options: Exclude<BagsyOptions, RelayOptions>): Transport {\n if (options.mode === 'server') {\n if (!options.apiKey) throw new TypeError('createBagsy: `apiKey` is required in server mode.');\n const browser = globalThis as { window?: unknown; document?: unknown };\n if (browser.window !== undefined && browser.document !== undefined) {\n throw new Error(\n 'createBagsy: server mode exposes your secret key and must not run in a browser. ' +\n \"Use mode: 'proxy' in themes, or call Bagsy from your Hydrogen loaders and actions.\",\n );\n }\n }\n\n const baseUrl = (\n options.baseUrl ??\n (options.mode === 'proxy' ? `${(options.proxyPath ?? '/apps/bagsy').replace(/\\/$/, '')}/v1` : 'https://api.bagsy.dev/v1')\n ).replace(/\\/$/, '');\n\n const headers: Record<string, string> = { Accept: 'application/json' };\n if (options.visitorId) headers['Bagsy-Visitor-Id'] = options.visitorId;\n if (options.mode === 'server') {\n headers.Authorization = `Bearer ${options.apiKey}`;\n if (options.customerId !== undefined) headers['Bagsy-Customer-Id'] = customerGid(options.customerId);\n }\n\n /** One request. Query arrays are comma-joined; undefined values are dropped. */\n async function send<T>(method: string, path: string, { query = {}, body }: RequestOptions = {}) {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) params.set(key, Array.isArray(value) ? value.join(',') : String(value));\n }\n // Shopify's app proxy only guarantees query parameters reach the app (and\n // signs them), so the visitor travels there too.\n if (options.mode === 'proxy' && options.visitorId) params.set('visitor_id', options.visitorId);\n\n const search = params.toString();\n const response = await (options.fetch ?? fetch)(`${baseUrl}${path}${search ? `?${search}` : ''}`, {\n method,\n headers: body === undefined ? headers : { ...headers, 'Content-Type': 'application/json' },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n const text = await response.text();\n let data: unknown;\n try {\n data = text ? JSON.parse(text) : undefined;\n } catch {\n data = text;\n }\n if (!response.ok) throw new BagsyError(response.status, data);\n return { status: response.status, data: data as T };\n }\n\n return {\n options,\n send,\n request: async (method, path, init) => (await send(method, path, init)).data as never,\n };\n}\n","import { createTransport, type Transport } from './transport.js';\nimport type { AccountOptions } from './types.js';\n\n/**\n * Transport for customer account UI extensions: every request carries a\n * current Shopify session token. Kept separate from createTransport so\n * storefront bundles that never use it don't pay for it.\n */\nexport function createAccountTransport({\n sessionToken,\n baseUrl = 'https://api.bagsy.dev/account/v1',\n fetch: fetchImpl,\n ...rest\n}: AccountOptions): Transport {\n return createTransport({\n ...rest,\n mode: 'account',\n sessionToken,\n baseUrl,\n // Session tokens last five minutes and Shopify caches them, so ask every time.\n fetch: async (input, init) => {\n const headers = new Headers(init?.headers);\n headers.set('Authorization', `Bearer ${await sessionToken()}`);\n return (fetchImpl ?? fetch)(input, { ...init, headers });\n },\n });\n}\n","import { BagsyError } from './errors.js';\nimport type { Transport } from './transport.js';\nimport type { RelayOptions, RequestOptions } from './types.js';\n\n/** One Bagsy API call, as the browser sends it to your server's relay endpoint. */\nexport interface RelayRequest {\n method: string;\n /** API path below `/v1`, e.g. `/lists/default/items`. */\n path: string;\n query?: Record<string, string>;\n body?: unknown;\n}\n\n/** The API's answer, passed back unchanged so errors keep their status and problem. */\nexport interface RelayResponse<T = unknown> {\n status: number;\n data: T;\n}\n\n/**\n * Transport for browsers on headless storefronts (Hydrogen and other React\n * apps). Each call goes to one endpoint on your own server, which adds the\n * secret key and the shopper's identity from its session, then calls Bagsy.\n * The browser never holds a key, a customer id or a guest id.\n */\nexport function createRelayTransport(options: RelayOptions): Transport {\n const endpoint = options.endpoint ?? '/api/bagsy';\n\n async function send<T>(method: string, path: string, { query = {}, body }: RequestOptions = {}) {\n const params: Record<string, string> = {};\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) params[key] = Array.isArray(value) ? value.join(',') : String(value);\n }\n const call: RelayRequest = { method, path, ...(Object.keys(params).length && { query: params }), ...(body !== undefined && { body }) };\n\n const response = await (options.fetch ?? fetch)(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n // Same-origin fetches send the session cookie by default.\n body: JSON.stringify(call),\n });\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n payload = undefined;\n }\n if (!response.ok || !isRelayResponse(payload)) {\n // The relay itself failed (not wired up, crashed, or rejected the call).\n throw new BagsyError(response.ok ? 502 : response.status, payload);\n }\n if (payload.status >= 400) throw new BagsyError(payload.status, payload.data);\n return payload as RelayResponse<T>;\n }\n\n return {\n options,\n send,\n request: async (method, path, init) => (await send(method, path, init)).data as never,\n };\n}\n\nfunction isRelayResponse(value: unknown): value is RelayResponse {\n return typeof value === 'object' && value !== null && typeof (value as RelayResponse).status === 'number';\n}\n","import * as op from './operations.js';\nimport { createAccountTransport } from './account.js';\nimport { createRelayTransport } from './relay.js';\nimport { createTransport } from './transport.js';\nimport type { Transport } from './transport.js';\nimport type { BagsyOptions, ItemOptions, ListRef, Owner, PageOptions, RelayOptions } from './types.js';\nimport type { ProductRef, VariantRef } from './ids.js';\n\n/**\n * The Bagsy client. Every method is a thin wrapper over a standalone function\n * in `operations` — use those directly if you want only what you import.\n */\nexport function createBagsy(options: BagsyOptions): Bagsy {\n if (options.mode === 'relay') return createRelayClient(options);\n const t = options.mode === 'account' ? createAccountTransport(options) : createTransport(options);\n return clientFor(t, (owner) => {\n if (options.mode === 'account') {\n throw new TypeError('bagsy.as: in customer accounts the session token identifies the customer.');\n }\n if (options.mode === 'proxy') {\n if (owner.customerId !== undefined) {\n throw new TypeError('bagsy.as: on the app proxy the customer comes from Shopify; pass only `visitorId`.');\n }\n return createBagsy({ ...options, visitorId: owner.visitorId ?? options.visitorId });\n }\n return createBagsy({ ...options, ...owner });\n });\n}\n\n/**\n * A client for browsers on headless storefronts, calling your server's relay\n * endpoint. Same API as `createBagsy({ mode: 'relay' })`, without bundling the\n * other transports.\n */\nexport function createRelayClient(options: Omit<RelayOptions, 'mode'> = {}): Bagsy {\n return clientFor(createRelayTransport({ ...options, mode: 'relay' }), () => {\n throw new TypeError(\"bagsy.as: through a relay, your server's session identifies the shopper.\");\n });\n}\n\nfunction clientFor(t: Transport, as: (owner: Owner) => Bagsy) {\n\n const items = {\n list: (listId?: ListRef, page?: PageOptions) => op.listItems(t, listId, page),\n /** Every item in a list, fetched page by page. */\n iterate: (listId?: ListRef, opts?: { limit?: number }) => op.iterateItems(t, listId, opts),\n /** Save a product. Saving it again returns the existing item. */\n add: (product: ProductRef, opts?: ItemOptions & { quantity?: number; note?: string }) => op.addItem(t, product, opts),\n /** Save if unsaved, unsave if saved. Built for heart buttons. */\n toggle: (product: ProductRef, opts?: ItemOptions) => op.toggleItem(t, product, opts),\n update: (\n itemId: string,\n patch: { variantId?: VariantRef | null; quantity?: number; note?: string | null },\n opts?: { listId?: ListRef },\n ) => op.updateItem(t, itemId, patch, opts),\n remove: (itemId: string, opts?: { listId?: ListRef }) => op.removeItem(t, itemId, opts),\n /** Unsave by product, without knowing the item id. No-op if not saved. */\n removeProduct: (product: ProductRef, opts?: ItemOptions) => op.removeProduct(t, product, opts),\n };\n\n return {\n lists: {\n /** The owner's lists, default first. */\n list: () => op.listLists(t),\n get: (listId?: ListRef, opts?: { includeItems?: boolean }) => op.getList(t, listId, opts),\n create: (name: string) => op.createList(t, name),\n rename: (listId: ListRef, name: string) => op.renameList(t, listId, name),\n delete: (listId: ListRef) => op.deleteList(t, listId),\n /** The list's share token, created on first share. `rotate: true` issues a new one. */\n share: (listId?: ListRef, opts?: { rotate?: boolean }) => op.shareList(t, listId, opts),\n unshare: (listId?: ListRef) => op.unshareList(t, listId),\n },\n items,\n /**\n * Saved state for a set of products across all of the owner's lists.\n * Accepts any number of products; batches of 100 are fetched in parallel.\n */\n saved: (products: Iterable<ProductRef>) => op.lookupSaved(t, products),\n identity: {\n /** Fold a guest's lists into the logged-in customer's. Safe to retry. */\n merge: (visitorId?: string) => op.mergeVisitor(t, visitorId),\n },\n shared: {\n /** Public, read-only view of a shared list. */\n get: (shareToken: string, page?: PageOptions) => op.getSharedList(t, shareToken, page),\n },\n insights: {\n /** Most-saved products. Server key only. */\n topProducts: (query?: { since?: Date | string; limit?: number }) => op.topProducts(t, query),\n },\n\n /**\n * A copy of this client acting for a specific shopper. In Hydrogen, call\n * once per request with the customer from your Customer Account session.\n */\n as,\n };\n}\n\nexport type Bagsy = ReturnType<typeof clientFor>;\n","const STORAGE_KEY = 'bagsy:visitor-id';\n\n/** The slice of Web Storage this module uses; `localStorage` satisfies it. */\nexport interface VisitorStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nlet memoryFallback: string | undefined;\n\n/**\n * Returns this browser's guest identity, creating it on first call.\n *\n * Persisted in localStorage. Falls back to an in-memory ID (for the page's\n * lifetime) when storage is blocked, e.g. in private windows or iframes.\n */\nexport function getVisitorId(storage: Pick<VisitorStorage, 'getItem' | 'setItem'> | undefined = safeLocalStorage()): string {\n try {\n const existing = storage?.getItem(STORAGE_KEY);\n if (existing) return existing;\n } catch {\n // Storage unreadable; fall through to a fresh ID.\n }\n\n const id = memoryFallback ?? crypto.randomUUID();\n memoryFallback = id;\n try {\n storage?.setItem(STORAGE_KEY, id);\n } catch {\n // Storage unwritable; the in-memory ID still works for this page.\n }\n return id;\n}\n\n/** The guest identity if one exists, without creating it. Use to decide whether to merge on login. */\nexport function peekVisitorId(storage: Pick<VisitorStorage, 'getItem'> | undefined = safeLocalStorage()): string | undefined {\n try {\n return storage?.getItem(STORAGE_KEY) ?? memoryFallback;\n } catch {\n return memoryFallback;\n }\n}\n\n/** Forget the guest identity, e.g. after a successful merge on login. */\nexport function clearVisitorId(storage: Pick<VisitorStorage, 'removeItem'> | undefined = safeLocalStorage()): void {\n memoryFallback = undefined;\n try {\n storage?.removeItem(STORAGE_KEY);\n } catch {\n // Nothing to clear.\n }\n}\n\nfunction safeLocalStorage(): VisitorStorage | undefined {\n try {\n // Read via globalThis so this compiles without the DOM lib (Workers, Node).\n return (globalThis as { localStorage?: VisitorStorage }).localStorage;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,169 @@
1
+ import { T as Transport, O as Owner, c as components, L as ListRef, P as PageOptions, a as ProductRef, I as ItemOptions, V as VariantRef, S as SavedLookup, B as BagsyOptions, R as RelayOptions } from './transport-Chh09xIP.js';
2
+
3
+ /**
4
+ * The Bagsy client. Every method is a thin wrapper over a standalone function
5
+ * in `operations` — use those directly if you want only what you import.
6
+ */
7
+ declare function createBagsy(options: BagsyOptions): Bagsy;
8
+ /**
9
+ * A client for browsers on headless storefronts, calling your server's relay
10
+ * endpoint. Same API as `createBagsy({ mode: 'relay' })`, without bundling the
11
+ * other transports.
12
+ */
13
+ declare function createRelayClient(options?: Omit<RelayOptions, 'mode'>): Bagsy;
14
+ declare function clientFor(t: Transport, as: (owner: Owner) => Bagsy): {
15
+ lists: {
16
+ /** The owner's lists, default first. */
17
+ list: () => Promise<{
18
+ id: string;
19
+ name: string;
20
+ isDefault: boolean;
21
+ visibility: "private" | "shared";
22
+ itemCount: number;
23
+ shareToken?: string | null;
24
+ createdAt: string;
25
+ updatedAt: string;
26
+ items?: components["schemas"]["ItemPage"];
27
+ }[]>;
28
+ get: (listId?: ListRef, opts?: {
29
+ includeItems?: boolean;
30
+ }) => Promise<{
31
+ id: string;
32
+ name: string;
33
+ isDefault: boolean;
34
+ visibility: "private" | "shared";
35
+ itemCount: number;
36
+ shareToken?: string | null;
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ items?: components["schemas"]["ItemPage"];
40
+ }>;
41
+ create: (name: string) => Promise<{
42
+ id: string;
43
+ name: string;
44
+ isDefault: boolean;
45
+ visibility: "private" | "shared";
46
+ itemCount: number;
47
+ shareToken?: string | null;
48
+ createdAt: string;
49
+ updatedAt: string;
50
+ items?: components["schemas"]["ItemPage"];
51
+ }>;
52
+ rename: (listId: ListRef, name: string) => Promise<{
53
+ id: string;
54
+ name: string;
55
+ isDefault: boolean;
56
+ visibility: "private" | "shared";
57
+ itemCount: number;
58
+ shareToken?: string | null;
59
+ createdAt: string;
60
+ updatedAt: string;
61
+ items?: components["schemas"]["ItemPage"];
62
+ }>;
63
+ delete: (listId: ListRef) => Promise<void>;
64
+ /** The list's share token, created on first share. `rotate: true` issues a new one. */
65
+ share: (listId?: ListRef, opts?: {
66
+ rotate?: boolean;
67
+ }) => Promise<string>;
68
+ unshare: (listId?: ListRef) => Promise<void>;
69
+ };
70
+ items: {
71
+ list: (listId?: ListRef, page?: PageOptions) => Promise<{
72
+ data: components["schemas"]["Item"][];
73
+ pageInfo: {
74
+ hasNextPage: boolean;
75
+ endCursor?: string | null;
76
+ };
77
+ }>;
78
+ /** Every item in a list, fetched page by page. */
79
+ iterate: (listId?: ListRef, opts?: {
80
+ limit?: number;
81
+ }) => AsyncGenerator<{
82
+ id: string;
83
+ listId: string;
84
+ productId: components["schemas"]["ProductGid"];
85
+ variantId?: components["schemas"]["VariantGid"] | null;
86
+ quantity: number;
87
+ note?: string | null;
88
+ addedAt: string;
89
+ }, any, any>;
90
+ /** Save a product. Saving it again returns the existing item. */
91
+ add: (product: ProductRef, opts?: ItemOptions & {
92
+ quantity?: number;
93
+ note?: string;
94
+ }) => Promise<{
95
+ id: string;
96
+ listId: string;
97
+ productId: components["schemas"]["ProductGid"];
98
+ variantId?: components["schemas"]["VariantGid"] | null;
99
+ quantity: number;
100
+ note?: string | null;
101
+ addedAt: string;
102
+ }>;
103
+ /** Save if unsaved, unsave if saved. Built for heart buttons. */
104
+ toggle: (product: ProductRef, opts?: ItemOptions) => Promise<{
105
+ saved: boolean;
106
+ }>;
107
+ update: (itemId: string, patch: {
108
+ variantId?: VariantRef | null;
109
+ quantity?: number;
110
+ note?: string | null;
111
+ }, opts?: {
112
+ listId?: ListRef;
113
+ }) => Promise<{
114
+ id: string;
115
+ listId: string;
116
+ productId: components["schemas"]["ProductGid"];
117
+ variantId?: components["schemas"]["VariantGid"] | null;
118
+ quantity: number;
119
+ note?: string | null;
120
+ addedAt: string;
121
+ }>;
122
+ remove: (itemId: string, opts?: {
123
+ listId?: ListRef;
124
+ }) => Promise<void>;
125
+ /** Unsave by product, without knowing the item id. No-op if not saved. */
126
+ removeProduct: (product: ProductRef, opts?: ItemOptions) => Promise<void>;
127
+ };
128
+ /**
129
+ * Saved state for a set of products across all of the owner's lists.
130
+ * Accepts any number of products; batches of 100 are fetched in parallel.
131
+ */
132
+ saved: (products: Iterable<ProductRef>) => Promise<SavedLookup>;
133
+ identity: {
134
+ /** Fold a guest's lists into the logged-in customer's. Safe to retry. */
135
+ merge: (visitorId?: string) => Promise<{
136
+ mergedItems: number;
137
+ lists: components["schemas"]["List"][];
138
+ }>;
139
+ };
140
+ shared: {
141
+ /** Public, read-only view of a shared list. */
142
+ get: (shareToken: string, page?: PageOptions) => Promise<{
143
+ list: {
144
+ name: string;
145
+ itemCount: number;
146
+ };
147
+ items: components["schemas"]["ItemPage"];
148
+ }>;
149
+ };
150
+ insights: {
151
+ /** Most-saved products. Server key only. */
152
+ topProducts: (query?: {
153
+ since?: Date | string;
154
+ limit?: number;
155
+ }) => Promise<{
156
+ productId: components["schemas"]["ProductGid"];
157
+ saves: number;
158
+ }[]>;
159
+ };
160
+ /**
161
+ * A copy of this client acting for a specific shopper. In Hydrogen, call
162
+ * once per request with the customer from your Customer Account session.
163
+ */
164
+ as: (owner: Owner) => Bagsy;
165
+ };
166
+ type Bagsy = ReturnType<typeof clientFor>;
167
+
168
+ export { createRelayClient as a, createBagsy as c };
169
+ export type { Bagsy as B };
package/dist/element.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { components } from './index.js';
2
- import { Transport } from './index.js';
1
+ import { Transport, components } from './index.js';
3
2
 
4
3
  /**
5
4
  * `<bagsy-button product-id="123">` — a save/unsave toggle.
package/dist/element.js CHANGED
@@ -10,11 +10,12 @@ import {
10
10
  mergeVisitor,
11
11
  peekVisitorId,
12
12
  productGid,
13
+ removeProduct,
13
14
  shareList,
14
15
  toggleItem,
15
16
  unshareList,
16
17
  variantGid
17
- } from "./chunk-OM72NJDX.js";
18
+ } from "./chunk-QV3JPND6.js";
18
19
 
19
20
  // ../element/src/store.ts
20
21
  var BagsyStore = class extends EventTarget {
@@ -71,7 +72,7 @@ var BagsyStore = class extends EventTarget {
71
72
  if (!entries) return void 0;
72
73
  if (variantId === void 0) return entries.length > 0;
73
74
  const gid = variantGid(variantId);
74
- return entries.some((entry) => entry.variantId === gid);
75
+ return entries.some((entry) => !entry.variantId || entry.variantId === gid);
75
76
  }
76
77
  /** Optimistic: listeners see the new state immediately and revert on failure. */
77
78
  async toggle(productId, variantId) {
@@ -80,14 +81,15 @@ var BagsyStore = class extends EventTarget {
80
81
  const previous = this.saved.get(id) ?? [];
81
82
  const wasSaved = this.isSaved(id, variantId) ?? false;
82
83
  const variant = variantId === void 0 ? void 0 : variantGid(variantId);
84
+ const whole = wasSaved && variant && previous.some((entry) => !entry.variantId);
83
85
  this.saved.set(
84
86
  id,
85
- wasSaved ? previous.filter((entry) => variant ? entry.variantId !== variant : false) : [...previous, { listId: "default", itemId: "pending", ...variant && { variantId: variant } }]
87
+ wasSaved ? previous.filter((entry) => variant && !whole ? entry.variantId !== variant : false) : [...previous, { listId: "default", itemId: "pending", ...variant && { variantId: variant } }]
86
88
  );
87
89
  this.adjustCount(wasSaved ? -1 : 1);
88
90
  this.notify(id);
89
91
  try {
90
- const { saved } = await toggleItem(this.transport, id, variant ? { variantId: variant } : {});
92
+ const { saved } = whole ? (await removeProduct(this.transport, id), { saved: false }) : await toggleItem(this.transport, id, variant ? { variantId: variant } : {});
91
93
  if (saved === wasSaved) {
92
94
  this.queue(id);
93
95
  void this.loadCount().catch(() => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../element/src/store.ts","../../element/src/button.ts","../../element/src/count.ts","../../element/src/wishlist.ts","../../element/src/index.ts"],"sourcesContent":["import {\n clearVisitorId,\n createTransport,\n getVisitorId,\n iterateItems,\n listLists,\n lookupSaved,\n mergeVisitor,\n peekVisitorId,\n productGid,\n toggleItem,\n variantGid,\n type SavedEntry,\n type Transport,\n} from '@bagsy/core';\n\nexport interface BagsyConfig {\n /** Shopify `routes.root_url`, e.g. `/` or `/en-gb`. */\n root?: string;\n /** Whether Shopify has a logged-in customer for this page. */\n customer?: boolean;\n /** Override the proxy base URL entirely (tests, custom setups). */\n baseUrl?: string;\n fetch?: typeof fetch;\n}\n\nexport interface ChangeDetail {\n productId: string;\n variantId?: string;\n saved: boolean;\n}\n\ntype Listener = () => void;\n\n/**\n * One per page: a single client, one batched `/saved` lookup for every button\n * on the page, optimistic toggles, and the guest → customer merge on login.\n */\nexport class BagsyStore extends EventTarget {\n /** Use with any `@umxr/bagsy` operation, e.g. `listLists(Bagsy.transport)`. */\n readonly transport: Transport;\n private saved = new Map<string, SavedEntry[]>();\n private pending = new Set<string>();\n private flush?: Promise<void>;\n private listeners = new Map<string, Set<Listener>>();\n /** Resolves once any pending guest merge has finished. */\n readonly ready: Promise<void>;\n count?: number;\n /** Storefront root without a trailing slash: '' or '/en-gb'. */\n readonly root: string;\n\n constructor(private config: BagsyConfig = {}) {\n super();\n const root = (this.root = (config.root ?? '/').replace(/\\/$/, ''));\n // Signed-in customers are identified by Shopify; the guest id only matters before login.\n const visitorId = config.customer ? undefined : getVisitorId();\n this.transport = createTransport({\n mode: 'proxy',\n proxyPath: `${root}/apps/bagsy`,\n ...(config.baseUrl && { baseUrl: config.baseUrl }),\n ...(visitorId && { visitorId }),\n ...(config.fetch && { fetch: config.fetch }),\n });\n this.ready = this.mergeGuestIfNeeded();\n }\n\n /** Fold the pre-login guest list into the customer's, once. */\n private async mergeGuestIfNeeded() {\n if (!this.config.customer) return;\n const guest = peekVisitorId();\n if (!guest) return;\n try {\n await mergeVisitor(this.transport, guest);\n clearVisitorId();\n } catch (error) {\n this.emit('bagsy:error', { error });\n }\n }\n\n /** Subscribe to a product's saved state; triggers a (batched) lookup. */\n watch(productId: string, listener: Listener): () => void {\n const id = productGid(productId);\n let set = this.listeners.get(id);\n if (!set) this.listeners.set(id, (set = new Set()));\n set.add(listener);\n if (!this.saved.has(id)) this.queue(id);\n else listener();\n return () => set!.delete(listener);\n }\n\n isSaved(productId: string, variantId?: string): boolean | undefined {\n const entries = this.saved.get(productGid(productId));\n if (!entries) return undefined;\n if (variantId === undefined) return entries.length > 0;\n // A variant button reflects exactly that variant, matching what toggle() will change.\n const gid = variantGid(variantId);\n return entries.some((entry) => entry.variantId === gid);\n }\n\n /** Optimistic: listeners see the new state immediately and revert on failure. */\n async toggle(productId: string, variantId?: string): Promise<boolean> {\n await this.ready;\n const id = productGid(productId);\n const previous = this.saved.get(id) ?? [];\n const wasSaved = this.isSaved(id, variantId) ?? false;\n const variant = variantId === undefined ? undefined : variantGid(variantId);\n\n this.saved.set(\n id,\n wasSaved\n ? previous.filter((entry) => (variant ? entry.variantId !== variant : false))\n : [...previous, { listId: 'default', itemId: 'pending', ...(variant && { variantId: variant }) }],\n );\n this.adjustCount(wasSaved ? -1 : 1);\n this.notify(id);\n\n try {\n const { saved } = await toggleItem(this.transport, id, variant ? { variantId: variant } : {});\n if (saved === wasSaved) {\n // Changed in another tab or list; take the server's word for it.\n this.queue(id);\n void this.loadCount().catch(() => {});\n }\n this.emit('bagsy:change', { productId: id, ...(variant && { variantId: variant }), saved });\n return saved;\n } catch (error) {\n this.saved.set(id, previous);\n this.adjustCount(wasSaved ? 1 : -1);\n this.notify(id);\n this.queue(id); // re-sync from the server\n this.emit('bagsy:error', { error, productId: id });\n throw error;\n }\n }\n\n /** Items in the default list, newest first. */\n async *items() {\n await this.ready;\n yield* iterateItems(this.transport, 'default');\n }\n\n private countRequest?: Promise<number>;\n\n /** Load the count once per page; later calls share the first request. */\n ensureCount(): Promise<number> {\n return (this.countRequest ??= this.loadCount());\n }\n\n /** Number of items in the default list, for header badges. */\n async loadCount(): Promise<number> {\n await this.ready;\n const lists = await listLists(this.transport);\n this.count = lists.find((list) => list.isDefault)?.itemCount ?? 0;\n this.emit('bagsy:count', { count: this.count });\n return this.count;\n }\n\n private adjustCount(delta: number) {\n if (this.count === undefined) return;\n this.count = Math.max(0, this.count + delta);\n this.emit('bagsy:count', { count: this.count });\n }\n\n private queue(id: string) {\n this.pending.add(id);\n // Every button that connects in the same task shares one request.\n this.flush ??= Promise.resolve().then(async () => {\n const ids = [...this.pending];\n this.pending.clear();\n this.flush = undefined;\n try {\n await this.ready;\n const lookup = await lookupSaved(this.transport, ids);\n for (const pid of ids) {\n this.saved.set(pid, lookup.get(pid));\n this.notify(pid);\n }\n } catch (error) {\n this.emit('bagsy:error', { error });\n }\n });\n }\n\n private notify(id: string) {\n for (const listener of this.listeners.get(id) ?? []) listener();\n }\n\n private emit(type: string, detail: unknown) {\n const event = new CustomEvent(type, { detail });\n this.dispatchEvent(event);\n // Also on document, so themes can listen without a reference to the store.\n globalThis.document?.dispatchEvent(new CustomEvent(type, { detail }));\n }\n}\n\nlet shared: BagsyStore | undefined;\n\n/** The page's store, configured from the app embed's JSON on first use. */\nexport function getStore(): BagsyStore {\n if (shared) return shared;\n const script = globalThis.document?.getElementById('bagsy-config');\n let config: BagsyConfig = {};\n try {\n config = script?.textContent ? JSON.parse(script.textContent) : {};\n } catch {\n // Malformed config: fall back to defaults.\n }\n return (shared = new BagsyStore(config));\n}\n\n/** Replace the page store (tests, or custom setups before elements connect). */\nexport function setStore(store: BagsyStore | undefined) {\n shared = store;\n}\n","import { getStore } from './store.js';\n\n// The product form's variant input, and the events themes fire when it changes.\nconst VARIANT_INPUT = 'form[action*=\"/cart/add\"] [name=id]';\nconst VARIANT_EVENTS = ['change', 'variant:change', 'variant:update'];\n\n// Presentation attributes give a usable default even without styles.css.\nconst HEART =\n '<svg part=\"icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><path d=\"M12 20.5 10.6 19.2C5.6 14.7 2.5 11.9 2.5 8.4 2.5 5.6 4.7 3.5 7.4 3.5c1.6 0 3.2.8 4.1 2 .9-1.2 2.5-2 4.1-2 2.7 0 4.9 2.1 4.9 4.9 0 3.5-3.1 6.3-8.1 10.8z\"/></svg>';\n\n\n/**\n * `<bagsy-button product-id=\"123\">` — a save/unsave toggle.\n *\n * Attributes: `product-id` (required, numeric or GID), `variant-id` (save a\n * specific variant), `variant-source` (follow the selected variant in the\n * product form on the page), `label` / `label-saved` (text, also used as the\n * accessible name), `icon-only`. Reflects `saved` for styling.\n * Default look: `@umxr/bagsy/styles.css`. Style with `--bagsy-accent`,\n * `--bagsy-color`, `--bagsy-background`, `--bagsy-border`, `--bagsy-radius`,\n * `--bagsy-padding`, or `bagsy-button::part(button | icon | label)`.\n * Put your own markup inside to replace the default icon and label.\n */\nexport class BagsyButton extends HTMLElement {\n static observedAttributes = ['product-id', 'variant-id', 'label', 'label-saved'];\n\n private button: HTMLButtonElement;\n private label: HTMLSpanElement;\n private unwatch?: () => void;\n private busy = false;\n // Themes update the variant input after their own change handlers run.\n private onVariantChange = () => setTimeout(() => this.syncVariant());\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: 'open' });\n root.innerHTML = `<button part=\"button\" type=\"button\" aria-pressed=\"false\" aria-busy=\"true\"><slot>${HEART}<span part=\"label\"></span></slot></button>`;\n this.button = root.querySelector('button')!;\n this.label = root.querySelector('[part=label]')!;\n this.button.addEventListener('click', (event) => this.onClick(event));\n }\n\n connectedCallback() {\n if (this.hasAttribute('variant-source')) {\n this.syncVariant();\n for (const type of VARIANT_EVENTS) document.addEventListener(type, this.onVariantChange);\n }\n this.subscribe();\n }\n\n disconnectedCallback() {\n for (const type of VARIANT_EVENTS) document.removeEventListener(type, this.onVariantChange);\n this.unwatch?.();\n this.unwatch = undefined;\n }\n\n /** Follow the selected variant in the product form, preferring this section's form. */\n private syncVariant() {\n const input = (this.closest('.shopify-section')?.querySelector(VARIANT_INPUT) ??\n document.querySelector(VARIANT_INPUT)) as { value: string } | null;\n if (input?.value && input.value !== this.variantId) this.setAttribute('variant-id', input.value);\n }\n\n attributeChangedCallback(name: string, previous: string | null, next: string | null) {\n if (previous === next) return;\n if ((name === 'product-id' || name === 'variant-id') && this.isConnected) this.subscribe();\n else this.render();\n }\n\n get productId() {\n return this.getAttribute('product-id') ?? '';\n }\n\n get variantId() {\n return this.getAttribute('variant-id') || undefined;\n }\n\n private subscribe() {\n this.unwatch?.();\n if (!this.productId) return;\n try {\n this.unwatch = getStore().watch(this.productId, () => this.render());\n } catch (error) {\n console.warn('<bagsy-button>:', error);\n }\n this.render();\n }\n\n private render() {\n const saved = this.productId ? getStore().isSaved(this.productId, this.variantId) : undefined;\n const text = saved ? (this.getAttribute('label-saved') ?? 'Saved') : (this.getAttribute('label') ?? 'Save');\n this.toggleAttribute('saved', saved === true);\n this.button.setAttribute('aria-pressed', String(saved === true));\n const busy = String(saved === undefined || this.busy);\n this.button.setAttribute('aria-busy', busy);\n this.setAttribute('aria-busy', busy);\n this.button.setAttribute('aria-label', text);\n this.label.textContent = text;\n }\n\n private async onClick(event: Event) {\n // Buttons often sit inside product-card links; don't navigate.\n event.preventDefault();\n event.stopPropagation();\n if (this.busy || !this.productId || getStore().isSaved(this.productId, this.variantId) === undefined) return;\n this.busy = true;\n this.render();\n try {\n await getStore().toggle(this.productId, this.variantId);\n } catch {\n // The store reverted the state and dispatched `bagsy:error`.\n } finally {\n this.busy = false;\n this.render();\n }\n }\n}\n","import { getStore } from './store.js';\n\n/**\n * `<bagsy-count>` — how many items are in the shopper's wishlist, e.g. for a\n * header link. Renders plain text so it inherits theme styles. Add\n * `hide-empty` to hide it at zero.\n */\nexport class BagsyCount extends HTMLElement {\n private onCount = () => this.render();\n\n connectedCallback() {\n const store = getStore();\n store.addEventListener('bagsy:count', this.onCount);\n if (store.count === undefined) store.ensureCount().catch(() => {});\n this.render();\n }\n\n disconnectedCallback() {\n getStore().removeEventListener('bagsy:count', this.onCount);\n }\n\n private render() {\n const count = getStore().count;\n this.textContent = count === undefined ? '' : String(count);\n this.toggleAttribute('hidden', this.hasAttribute('hide-empty') && !count);\n }\n}\n","import { getList, getSharedList, shareList, unshareList, type Item } from '@bagsy/core';\nimport type { ChangeDetail } from './store.js';\nimport { getStore } from './store.js';\n\nconst STOREFRONT_API_VERSION = '2026-10';\nconst MAX_ITEMS = 250;\nconst BATCH = 50; // keeps each tokenless query well under the 1,000 complexity limit\nexport const SHARE_PARAM = 'bagsy_share';\n\nexport const PRODUCTS_QUERY = `query BagsyWishlist($ids: [ID!]!, $country: CountryCode, $language: LanguageCode)\n@inContext(country: $country, language: $language) {\n nodes(ids: $ids) {\n ... on Product {\n id\n title\n handle\n availableForSale\n featuredImage { url altText width height }\n priceRange { minVariantPrice { amount currencyCode } }\n }\n ... on ProductVariant {\n id\n title\n availableForSale\n image { url altText width height }\n price { amount currencyCode }\n product { id title handle featuredImage { url altText width height } }\n }\n }\n}`;\n\ntype Image = { url: string; altText: string | null; width: number | null; height: number | null } | null;\ntype Money = { amount: string; currencyCode: string };\ntype ProductNode = {\n id: string;\n title: string;\n handle: string;\n availableForSale: boolean;\n featuredImage: Image;\n priceRange: { minVariantPrice: Money };\n};\ntype VariantNode = {\n id: string;\n title: string;\n availableForSale: boolean;\n image: Image;\n price: Money;\n product: { id: string; title: string; handle: string; featuredImage: Image };\n};\n\n/** One card: a saved product, or a saved variant of one. */\nexport interface Card {\n productId: string;\n variantId?: string;\n title: string;\n variantTitle?: string;\n handle: string;\n available: boolean;\n image: Image;\n price: Money;\n}\n\nconst toCard = (node: ProductNode | VariantNode): Card =>\n 'product' in node\n ? {\n productId: node.product.id,\n variantId: node.id,\n title: node.product.title,\n // Shopify names the only variant of a single-variant product \"Default Title\".\n variantTitle: node.title === 'Default Title' ? undefined : node.title,\n handle: node.product.handle,\n available: node.availableForSale,\n image: node.image ?? node.product.featuredImage,\n price: node.price,\n }\n : {\n productId: node.id,\n title: node.title,\n handle: node.handle,\n available: node.availableForSale,\n image: node.featuredImage,\n price: node.priceRange.minVariantPrice,\n };\n\nconst keyOf = (item: { productId: string; variantId?: string | null }) => item.variantId ?? item.productId;\n\n/**\n * `<bagsy-wishlist>`: the shopper's saved products as a grid of cards, or a\n * shared list when the URL has `?bagsy_share=<token>`.\n *\n * Product data comes live from the store's Storefront API (tokenless, same\n * origin), so titles, prices and availability match the shopper's market.\n * Attributes: `country`, `language`, `share` (show share controls),\n * `empty-text`, `remove-label`, `save-label`, `saved-label`, `sold-out-label`,\n * `share-label`, `unshare-label`, `copied-label`, `unshared-label`,\n * `shared-heading`, `shared-missing-text`. Renders light DOM with\n * `bagsy-wishlist__*` classes, styled by `@umxr/bagsy/styles.css`.\n */\nexport class BagsyWishlist extends HTMLElement {\n private shared = false;\n\n private onChange = (event: Event) => {\n const { productId, variantId, saved } = (event as CustomEvent<ChangeDetail>).detail;\n if (saved || this.shared) return;\n // Unsaving a product (no variant) removes every saved variant of it too, as the API does.\n for (const card of this.querySelectorAll<HTMLElement>('.bagsy-wishlist__item')) {\n if (variantId ? card.dataset.key === variantId : card.dataset.productId === productId) card.remove();\n }\n if (!this.querySelector('.bagsy-wishlist__item')) this.renderEmpty(this.getAttribute('empty-text') ?? 'Your wishlist is empty.');\n };\n\n private text = (name: string, fallback: string) => this.getAttribute(name) ?? fallback;\n\n connectedCallback() {\n getStore().addEventListener('bagsy:change', this.onChange);\n this.setAttribute('aria-busy', 'true');\n const token = new URLSearchParams(location.search).get(SHARE_PARAM);\n (token ? this.loadShared(token) : this.loadOwn())\n .catch((error) => {\n console.warn('<bagsy-wishlist>:', error);\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n })\n .finally(() => this.removeAttribute('aria-busy'));\n }\n\n disconnectedCallback() {\n getStore().removeEventListener('bagsy:change', this.onChange);\n }\n\n private async loadOwn() {\n const store = getStore();\n const items: Item[] = [];\n for await (const item of store.items()) {\n items.push(item);\n if (items.length >= MAX_ITEMS) break;\n }\n const cards = items.length ? await this.cards(items) : [];\n if (!cards.length) return this.renderEmpty(this.text('empty-text', 'Your wishlist is empty.'));\n\n const nodes: Node[] = [];\n if (this.hasAttribute('share')) {\n const list = await getList(store.transport, 'default');\n nodes.push(this.shareControls(Boolean(list.shareToken)));\n }\n nodes.push(this.grid(cards, this.text('save-label', 'Save'), this.text('remove-label', 'Remove from wishlist')));\n this.replaceChildren(...nodes);\n }\n\n private async loadShared(token: string) {\n this.shared = true;\n const t = getStore().transport;\n const items: Item[] = [];\n let name = '';\n let after: string | undefined;\n try {\n do {\n const page = await getSharedList(t, token, { limit: 100, after });\n name = page.list.name;\n items.push(...page.items.data);\n after = page.items.pageInfo.hasNextPage ? (page.items.pageInfo.endCursor ?? undefined) : undefined;\n } while (after && items.length < MAX_ITEMS);\n } catch (error) {\n // Checked by shape: in the theme, errors come from the main asset's copy of BagsyError.\n if ((error as { status?: number } | null)?.status === 404) {\n return this.renderEmpty(this.text('shared-missing-text', 'This shared wishlist is no longer available.'));\n }\n throw error;\n }\n\n // Stands in for the page block's own heading, which styles.css hides in this view.\n this.toggleAttribute('shared-view', true);\n const heading = document.createElement('h2');\n heading.className = 'bagsy-wishlist__shared';\n heading.textContent = `${this.text('shared-heading', 'Shared wishlist')}: ${name}`;\n const cards = items.length ? await this.cards(items) : [];\n // Visitors save items to their own wishlist from here.\n this.replaceChildren(\n heading,\n cards.length\n ? this.grid(cards, this.text('save-label', 'Save'), this.text('saved-label', 'Saved'))\n : this.message(this.text('empty-text', 'Your wishlist is empty.')),\n );\n }\n\n /** Saved items → live cards, in saved order. Deleted or unpublished products drop out. */\n async cards(items: { productId: string; variantId?: string | null }[]): Promise<Card[]> {\n const ids = [...new Set(items.map(keyOf))];\n const variables = {\n country: this.getAttribute('country') || undefined,\n language: this.getAttribute('language')?.toUpperCase().replace('-', '_') || undefined,\n };\n const batches: string[][] = [];\n for (let i = 0; i < ids.length; i += BATCH) batches.push(ids.slice(i, i + BATCH));\n\n const results = await Promise.all(\n batches.map(async (batch) => {\n const response = await fetch(`/api/${STOREFRONT_API_VERSION}/graphql.json`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ query: PRODUCTS_QUERY, variables: { ...variables, ids: batch } }),\n });\n if (!response.ok) throw new Error(`Storefront API responded ${response.status}`);\n const body = (await response.json()) as { data?: { nodes: (ProductNode | VariantNode | null)[] }; errors?: unknown };\n if (!body.data) throw new Error(`Storefront API error: ${JSON.stringify(body.errors)}`);\n return body.data.nodes;\n }),\n );\n const byId = new Map<string, Card>();\n for (const node of results.flat()) {\n if (node && typeof node === 'object' && 'title' in node) byId.set(node.id, toCard(node));\n }\n return ids.map((id) => byId.get(id)).filter((card): card is Card => card !== undefined);\n }\n\n private shareControls(isShared: boolean) {\n const store = getStore();\n const actions = document.createElement('div');\n actions.className = 'bagsy-wishlist__actions';\n const share = this.actionButton('bagsy-wishlist__share', this.text('share-label', 'Share wishlist'));\n const unshare = this.actionButton('bagsy-wishlist__unshare', this.text('unshare-label', 'Stop sharing'));\n const status = document.createElement('span');\n status.className = 'bagsy-wishlist__status';\n status.setAttribute('role', 'status');\n unshare.hidden = !isShared;\n\n share.addEventListener('click', async () => {\n try {\n const token = await shareList(store.transport, 'default');\n const url = new URL(location.href);\n url.search = '';\n url.hash = '';\n url.searchParams.set(SHARE_PARAM, token);\n unshare.hidden = false;\n if (navigator.share) {\n await navigator.share({ title: document.title, url: url.toString() }).catch(() => {});\n } else {\n await navigator.clipboard.writeText(url.toString());\n status.textContent = this.text('copied-label', 'Link copied');\n }\n } catch (error) {\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n }\n });\n unshare.addEventListener('click', async () => {\n try {\n await unshareList(store.transport, 'default');\n unshare.hidden = true;\n status.textContent = this.text('unshared-label', 'Sharing stopped');\n } catch (error) {\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n }\n });\n\n actions.append(share, unshare, status);\n return actions;\n }\n\n private actionButton(className: string, label: string) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = className;\n button.textContent = label;\n return button;\n }\n\n private message(text: string) {\n const message = document.createElement('p');\n message.className = 'bagsy-wishlist__empty';\n message.textContent = text;\n return message;\n }\n\n private renderEmpty(text: string) {\n this.replaceChildren(this.message(text));\n }\n\n private grid(cards: Card[], label: string, labelSaved: string) {\n const root = getStore().root;\n const locale = document.documentElement.lang || undefined;\n const list = document.createElement('ul');\n list.className = 'bagsy-wishlist__grid';\n list.setAttribute('role', 'list');\n\n for (const card of cards) {\n const item = document.createElement('li');\n item.className = 'bagsy-wishlist__item';\n item.dataset.key = keyOf(card);\n item.dataset.productId = card.productId;\n\n if (card.image) {\n const image = document.createElement('img');\n image.className = 'bagsy-wishlist__image';\n const src = new URL(card.image.url);\n src.searchParams.set('width', '600');\n image.src = src.toString();\n image.alt = card.image.altText ?? '';\n image.loading = 'lazy';\n image.decoding = 'async';\n if (card.image.width && card.image.height) {\n image.width = card.image.width;\n image.height = card.image.height;\n }\n item.append(image);\n }\n\n const title = document.createElement('a');\n title.className = 'bagsy-wishlist__title';\n const href = new URL(`${root}/products/${encodeURIComponent(card.handle)}`, location.origin);\n if (card.variantId) href.searchParams.set('variant', card.variantId.split('/').pop()!);\n title.href = href.pathname + href.search;\n title.textContent = card.title;\n item.append(title);\n\n if (card.variantTitle) {\n const variant = document.createElement('span');\n variant.className = 'bagsy-wishlist__variant';\n variant.textContent = card.variantTitle;\n item.append(variant);\n }\n\n const price = document.createElement('span');\n price.className = 'bagsy-wishlist__price';\n price.textContent = formatPrice(card.price.amount, card.price.currencyCode, locale);\n\n const button = document.createElement('bagsy-button');\n button.setAttribute('product-id', card.productId);\n if (card.variantId) button.setAttribute('variant-id', card.variantId);\n button.setAttribute('icon-only', '');\n button.setAttribute('label', label);\n button.setAttribute('label-saved', labelSaved);\n\n item.append(price, button);\n if (!card.available) {\n const soldOut = document.createElement('span');\n soldOut.className = 'bagsy-wishlist__sold-out';\n soldOut.textContent = this.text('sold-out-label', 'Sold out');\n item.append(soldOut);\n }\n list.append(item);\n }\n return list;\n }\n}\n\nfunction formatPrice(amount: string, currency: string, locale?: string) {\n try {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(Number(amount));\n } catch {\n return `${amount} ${currency}`;\n }\n}\n","import { BagsyButton } from './button.js';\nimport { BagsyCount } from './count.js';\nimport { BagsyWishlist } from './wishlist.js';\n\nexport { BagsyButton, BagsyCount, BagsyWishlist };\nexport { BagsyStore, getStore, setStore, type BagsyConfig, type ChangeDetail } from './store.js';\n\n/** Register the elements. Safe to call more than once. */\nexport function define() {\n if (typeof customElements === 'undefined') return;\n for (const [name, element] of [\n ['bagsy-button', BagsyButton],\n ['bagsy-count', BagsyCount],\n ['bagsy-wishlist', BagsyWishlist],\n ] as const) {\n if (!customElements.get(name)) customElements.define(name, element);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../element/src/store.ts","../../element/src/button.ts","../../element/src/count.ts","../../element/src/wishlist.ts","../../element/src/index.ts"],"sourcesContent":["import {\n clearVisitorId,\n createTransport,\n getVisitorId,\n iterateItems,\n listLists,\n lookupSaved,\n mergeVisitor,\n peekVisitorId,\n productGid,\n removeProduct,\n toggleItem,\n variantGid,\n type SavedEntry,\n type Transport,\n} from '@bagsy/core';\n\nexport interface BagsyConfig {\n /** Shopify `routes.root_url`, e.g. `/` or `/en-gb`. */\n root?: string;\n /** Whether Shopify has a logged-in customer for this page. */\n customer?: boolean;\n /** Override the proxy base URL entirely (tests, custom setups). */\n baseUrl?: string;\n fetch?: typeof fetch;\n}\n\nexport interface ChangeDetail {\n productId: string;\n variantId?: string;\n saved: boolean;\n}\n\ntype Listener = () => void;\n\n/**\n * One per page: a single client, one batched `/saved` lookup for every button\n * on the page, optimistic toggles, and the guest → customer merge on login.\n */\nexport class BagsyStore extends EventTarget {\n /** Use with any `@umxr/bagsy` operation, e.g. `listLists(Bagsy.transport)`. */\n readonly transport: Transport;\n private saved = new Map<string, SavedEntry[]>();\n private pending = new Set<string>();\n private flush?: Promise<void>;\n private listeners = new Map<string, Set<Listener>>();\n /** Resolves once any pending guest merge has finished. */\n readonly ready: Promise<void>;\n count?: number;\n /** Storefront root without a trailing slash: '' or '/en-gb'. */\n readonly root: string;\n\n constructor(private config: BagsyConfig = {}) {\n super();\n const root = (this.root = (config.root ?? '/').replace(/\\/$/, ''));\n // Signed-in customers are identified by Shopify; the guest id only matters before login.\n const visitorId = config.customer ? undefined : getVisitorId();\n this.transport = createTransport({\n mode: 'proxy',\n proxyPath: `${root}/apps/bagsy`,\n ...(config.baseUrl && { baseUrl: config.baseUrl }),\n ...(visitorId && { visitorId }),\n ...(config.fetch && { fetch: config.fetch }),\n });\n this.ready = this.mergeGuestIfNeeded();\n }\n\n /** Fold the pre-login guest list into the customer's, once. */\n private async mergeGuestIfNeeded() {\n if (!this.config.customer) return;\n const guest = peekVisitorId();\n if (!guest) return;\n try {\n await mergeVisitor(this.transport, guest);\n clearVisitorId();\n } catch (error) {\n this.emit('bagsy:error', { error });\n }\n }\n\n /** Subscribe to a product's saved state; triggers a (batched) lookup. */\n watch(productId: string, listener: Listener): () => void {\n const id = productGid(productId);\n let set = this.listeners.get(id);\n if (!set) this.listeners.set(id, (set = new Set()));\n set.add(listener);\n if (!this.saved.has(id)) this.queue(id);\n else listener();\n return () => set!.delete(listener);\n }\n\n isSaved(productId: string, variantId?: string): boolean | undefined {\n const entries = this.saved.get(productGid(productId));\n if (!entries) return undefined;\n if (variantId === undefined) return entries.length > 0;\n // That variant, or the product saved without one (which means any variant).\n const gid = variantGid(variantId);\n return entries.some((entry) => !entry.variantId || entry.variantId === gid);\n }\n\n /** Optimistic: listeners see the new state immediately and revert on failure. */\n async toggle(productId: string, variantId?: string): Promise<boolean> {\n await this.ready;\n const id = productGid(productId);\n const previous = this.saved.get(id) ?? [];\n const wasSaved = this.isSaved(id, variantId) ?? false;\n const variant = variantId === undefined ? undefined : variantGid(variantId);\n // Saved for the whole product: unsaving from a variant button removes the product.\n const whole = wasSaved && variant && previous.some((entry) => !entry.variantId);\n\n this.saved.set(\n id,\n wasSaved\n ? previous.filter((entry) => (variant && !whole ? entry.variantId !== variant : false))\n : [...previous, { listId: 'default', itemId: 'pending', ...(variant && { variantId: variant }) }],\n );\n this.adjustCount(wasSaved ? -1 : 1);\n this.notify(id);\n\n try {\n const { saved } = whole\n ? (await removeProduct(this.transport, id), { saved: false })\n : await toggleItem(this.transport, id, variant ? { variantId: variant } : {});\n if (saved === wasSaved) {\n // Changed in another tab or list; take the server's word for it.\n this.queue(id);\n void this.loadCount().catch(() => {});\n }\n this.emit('bagsy:change', { productId: id, ...(variant && { variantId: variant }), saved });\n return saved;\n } catch (error) {\n this.saved.set(id, previous);\n this.adjustCount(wasSaved ? 1 : -1);\n this.notify(id);\n this.queue(id); // re-sync from the server\n this.emit('bagsy:error', { error, productId: id });\n throw error;\n }\n }\n\n /** Items in the default list, newest first. */\n async *items() {\n await this.ready;\n yield* iterateItems(this.transport, 'default');\n }\n\n private countRequest?: Promise<number>;\n\n /** Load the count once per page; later calls share the first request. */\n ensureCount(): Promise<number> {\n return (this.countRequest ??= this.loadCount());\n }\n\n /** Number of items in the default list, for header badges. */\n async loadCount(): Promise<number> {\n await this.ready;\n const lists = await listLists(this.transport);\n this.count = lists.find((list) => list.isDefault)?.itemCount ?? 0;\n this.emit('bagsy:count', { count: this.count });\n return this.count;\n }\n\n private adjustCount(delta: number) {\n if (this.count === undefined) return;\n this.count = Math.max(0, this.count + delta);\n this.emit('bagsy:count', { count: this.count });\n }\n\n private queue(id: string) {\n this.pending.add(id);\n // Every button that connects in the same task shares one request.\n this.flush ??= Promise.resolve().then(async () => {\n const ids = [...this.pending];\n this.pending.clear();\n this.flush = undefined;\n try {\n await this.ready;\n const lookup = await lookupSaved(this.transport, ids);\n for (const pid of ids) {\n this.saved.set(pid, lookup.get(pid));\n this.notify(pid);\n }\n } catch (error) {\n this.emit('bagsy:error', { error });\n }\n });\n }\n\n private notify(id: string) {\n for (const listener of this.listeners.get(id) ?? []) listener();\n }\n\n private emit(type: string, detail: unknown) {\n const event = new CustomEvent(type, { detail });\n this.dispatchEvent(event);\n // Also on document, so themes can listen without a reference to the store.\n globalThis.document?.dispatchEvent(new CustomEvent(type, { detail }));\n }\n}\n\nlet shared: BagsyStore | undefined;\n\n/** The page's store, configured from the app embed's JSON on first use. */\nexport function getStore(): BagsyStore {\n if (shared) return shared;\n const script = globalThis.document?.getElementById('bagsy-config');\n let config: BagsyConfig = {};\n try {\n config = script?.textContent ? JSON.parse(script.textContent) : {};\n } catch {\n // Malformed config: fall back to defaults.\n }\n return (shared = new BagsyStore(config));\n}\n\n/** Replace the page store (tests, or custom setups before elements connect). */\nexport function setStore(store: BagsyStore | undefined) {\n shared = store;\n}\n","import { getStore } from './store.js';\n\n// The product form's variant input, and the events themes fire when it changes.\nconst VARIANT_INPUT = 'form[action*=\"/cart/add\"] [name=id]';\nconst VARIANT_EVENTS = ['change', 'variant:change', 'variant:update'];\n\n// Presentation attributes give a usable default even without styles.css.\nconst HEART =\n '<svg part=\"icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\"><path d=\"M12 20.5 10.6 19.2C5.6 14.7 2.5 11.9 2.5 8.4 2.5 5.6 4.7 3.5 7.4 3.5c1.6 0 3.2.8 4.1 2 .9-1.2 2.5-2 4.1-2 2.7 0 4.9 2.1 4.9 4.9 0 3.5-3.1 6.3-8.1 10.8z\"/></svg>';\n\n\n/**\n * `<bagsy-button product-id=\"123\">` — a save/unsave toggle.\n *\n * Attributes: `product-id` (required, numeric or GID), `variant-id` (save a\n * specific variant), `variant-source` (follow the selected variant in the\n * product form on the page), `label` / `label-saved` (text, also used as the\n * accessible name), `icon-only`. Reflects `saved` for styling.\n * Default look: `@umxr/bagsy/styles.css`. Style with `--bagsy-accent`,\n * `--bagsy-color`, `--bagsy-background`, `--bagsy-border`, `--bagsy-radius`,\n * `--bagsy-padding`, or `bagsy-button::part(button | icon | label)`.\n * Put your own markup inside to replace the default icon and label.\n */\nexport class BagsyButton extends HTMLElement {\n static observedAttributes = ['product-id', 'variant-id', 'label', 'label-saved'];\n\n private button: HTMLButtonElement;\n private label: HTMLSpanElement;\n private unwatch?: () => void;\n private busy = false;\n // Themes update the variant input after their own change handlers run.\n private onVariantChange = () => setTimeout(() => this.syncVariant());\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: 'open' });\n root.innerHTML = `<button part=\"button\" type=\"button\" aria-pressed=\"false\" aria-busy=\"true\"><slot>${HEART}<span part=\"label\"></span></slot></button>`;\n this.button = root.querySelector('button')!;\n this.label = root.querySelector('[part=label]')!;\n this.button.addEventListener('click', (event) => this.onClick(event));\n }\n\n connectedCallback() {\n if (this.hasAttribute('variant-source')) {\n this.syncVariant();\n for (const type of VARIANT_EVENTS) document.addEventListener(type, this.onVariantChange);\n }\n this.subscribe();\n }\n\n disconnectedCallback() {\n for (const type of VARIANT_EVENTS) document.removeEventListener(type, this.onVariantChange);\n this.unwatch?.();\n this.unwatch = undefined;\n }\n\n /** Follow the selected variant in the product form, preferring this section's form. */\n private syncVariant() {\n const input = (this.closest('.shopify-section')?.querySelector(VARIANT_INPUT) ??\n document.querySelector(VARIANT_INPUT)) as { value: string } | null;\n if (input?.value && input.value !== this.variantId) this.setAttribute('variant-id', input.value);\n }\n\n attributeChangedCallback(name: string, previous: string | null, next: string | null) {\n if (previous === next) return;\n if ((name === 'product-id' || name === 'variant-id') && this.isConnected) this.subscribe();\n else this.render();\n }\n\n get productId() {\n return this.getAttribute('product-id') ?? '';\n }\n\n get variantId() {\n return this.getAttribute('variant-id') || undefined;\n }\n\n private subscribe() {\n this.unwatch?.();\n if (!this.productId) return;\n try {\n this.unwatch = getStore().watch(this.productId, () => this.render());\n } catch (error) {\n console.warn('<bagsy-button>:', error);\n }\n this.render();\n }\n\n private render() {\n const saved = this.productId ? getStore().isSaved(this.productId, this.variantId) : undefined;\n const text = saved ? (this.getAttribute('label-saved') ?? 'Saved') : (this.getAttribute('label') ?? 'Save');\n this.toggleAttribute('saved', saved === true);\n this.button.setAttribute('aria-pressed', String(saved === true));\n const busy = String(saved === undefined || this.busy);\n this.button.setAttribute('aria-busy', busy);\n this.setAttribute('aria-busy', busy);\n this.button.setAttribute('aria-label', text);\n this.label.textContent = text;\n }\n\n private async onClick(event: Event) {\n // Buttons often sit inside product-card links; don't navigate.\n event.preventDefault();\n event.stopPropagation();\n if (this.busy || !this.productId || getStore().isSaved(this.productId, this.variantId) === undefined) return;\n this.busy = true;\n this.render();\n try {\n await getStore().toggle(this.productId, this.variantId);\n } catch {\n // The store reverted the state and dispatched `bagsy:error`.\n } finally {\n this.busy = false;\n this.render();\n }\n }\n}\n","import { getStore } from './store.js';\n\n/**\n * `<bagsy-count>` — how many items are in the shopper's wishlist, e.g. for a\n * header link. Renders plain text so it inherits theme styles. Add\n * `hide-empty` to hide it at zero.\n */\nexport class BagsyCount extends HTMLElement {\n private onCount = () => this.render();\n\n connectedCallback() {\n const store = getStore();\n store.addEventListener('bagsy:count', this.onCount);\n if (store.count === undefined) store.ensureCount().catch(() => {});\n this.render();\n }\n\n disconnectedCallback() {\n getStore().removeEventListener('bagsy:count', this.onCount);\n }\n\n private render() {\n const count = getStore().count;\n this.textContent = count === undefined ? '' : String(count);\n this.toggleAttribute('hidden', this.hasAttribute('hide-empty') && !count);\n }\n}\n","import { getList, getSharedList, shareList, unshareList, type Item } from '@bagsy/core';\nimport type { ChangeDetail } from './store.js';\nimport { getStore } from './store.js';\n\nconst STOREFRONT_API_VERSION = '2026-10';\nconst MAX_ITEMS = 250;\nconst BATCH = 50; // keeps each tokenless query well under the 1,000 complexity limit\nexport const SHARE_PARAM = 'bagsy_share';\n\nexport const PRODUCTS_QUERY = `query BagsyWishlist($ids: [ID!]!, $country: CountryCode, $language: LanguageCode)\n@inContext(country: $country, language: $language) {\n nodes(ids: $ids) {\n ... on Product {\n id\n title\n handle\n availableForSale\n featuredImage { url altText width height }\n priceRange { minVariantPrice { amount currencyCode } }\n }\n ... on ProductVariant {\n id\n title\n availableForSale\n image { url altText width height }\n price { amount currencyCode }\n product { id title handle featuredImage { url altText width height } }\n }\n }\n}`;\n\ntype Image = { url: string; altText: string | null; width: number | null; height: number | null } | null;\ntype Money = { amount: string; currencyCode: string };\ntype ProductNode = {\n id: string;\n title: string;\n handle: string;\n availableForSale: boolean;\n featuredImage: Image;\n priceRange: { minVariantPrice: Money };\n};\ntype VariantNode = {\n id: string;\n title: string;\n availableForSale: boolean;\n image: Image;\n price: Money;\n product: { id: string; title: string; handle: string; featuredImage: Image };\n};\n\n/** One card: a saved product, or a saved variant of one. */\nexport interface Card {\n productId: string;\n variantId?: string;\n title: string;\n variantTitle?: string;\n handle: string;\n available: boolean;\n image: Image;\n price: Money;\n}\n\nconst toCard = (node: ProductNode | VariantNode): Card =>\n 'product' in node\n ? {\n productId: node.product.id,\n variantId: node.id,\n title: node.product.title,\n // Shopify names the only variant of a single-variant product \"Default Title\".\n variantTitle: node.title === 'Default Title' ? undefined : node.title,\n handle: node.product.handle,\n available: node.availableForSale,\n image: node.image ?? node.product.featuredImage,\n price: node.price,\n }\n : {\n productId: node.id,\n title: node.title,\n handle: node.handle,\n available: node.availableForSale,\n image: node.featuredImage,\n price: node.priceRange.minVariantPrice,\n };\n\nconst keyOf = (item: { productId: string; variantId?: string | null }) => item.variantId ?? item.productId;\n\n/**\n * `<bagsy-wishlist>`: the shopper's saved products as a grid of cards, or a\n * shared list when the URL has `?bagsy_share=<token>`.\n *\n * Product data comes live from the store's Storefront API (tokenless, same\n * origin), so titles, prices and availability match the shopper's market.\n * Attributes: `country`, `language`, `share` (show share controls),\n * `empty-text`, `remove-label`, `save-label`, `saved-label`, `sold-out-label`,\n * `share-label`, `unshare-label`, `copied-label`, `unshared-label`,\n * `shared-heading`, `shared-missing-text`. Renders light DOM with\n * `bagsy-wishlist__*` classes, styled by `@umxr/bagsy/styles.css`.\n */\nexport class BagsyWishlist extends HTMLElement {\n private shared = false;\n\n private onChange = (event: Event) => {\n const { productId, variantId, saved } = (event as CustomEvent<ChangeDetail>).detail;\n if (saved || this.shared) return;\n // Unsaving a product (no variant) removes every saved variant of it too, as the API does.\n for (const card of this.querySelectorAll<HTMLElement>('.bagsy-wishlist__item')) {\n if (variantId ? card.dataset.key === variantId : card.dataset.productId === productId) card.remove();\n }\n if (!this.querySelector('.bagsy-wishlist__item')) this.renderEmpty(this.getAttribute('empty-text') ?? 'Your wishlist is empty.');\n };\n\n private text = (name: string, fallback: string) => this.getAttribute(name) ?? fallback;\n\n connectedCallback() {\n getStore().addEventListener('bagsy:change', this.onChange);\n this.setAttribute('aria-busy', 'true');\n const token = new URLSearchParams(location.search).get(SHARE_PARAM);\n (token ? this.loadShared(token) : this.loadOwn())\n .catch((error) => {\n console.warn('<bagsy-wishlist>:', error);\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n })\n .finally(() => this.removeAttribute('aria-busy'));\n }\n\n disconnectedCallback() {\n getStore().removeEventListener('bagsy:change', this.onChange);\n }\n\n private async loadOwn() {\n const store = getStore();\n const items: Item[] = [];\n for await (const item of store.items()) {\n items.push(item);\n if (items.length >= MAX_ITEMS) break;\n }\n const cards = items.length ? await this.cards(items) : [];\n if (!cards.length) return this.renderEmpty(this.text('empty-text', 'Your wishlist is empty.'));\n\n const nodes: Node[] = [];\n if (this.hasAttribute('share')) {\n const list = await getList(store.transport, 'default');\n nodes.push(this.shareControls(Boolean(list.shareToken)));\n }\n nodes.push(this.grid(cards, this.text('save-label', 'Save'), this.text('remove-label', 'Remove from wishlist')));\n this.replaceChildren(...nodes);\n }\n\n private async loadShared(token: string) {\n this.shared = true;\n const t = getStore().transport;\n const items: Item[] = [];\n let name = '';\n let after: string | undefined;\n try {\n do {\n const page = await getSharedList(t, token, { limit: 100, after });\n name = page.list.name;\n items.push(...page.items.data);\n after = page.items.pageInfo.hasNextPage ? (page.items.pageInfo.endCursor ?? undefined) : undefined;\n } while (after && items.length < MAX_ITEMS);\n } catch (error) {\n // Checked by shape: in the theme, errors come from the main asset's copy of BagsyError.\n if ((error as { status?: number } | null)?.status === 404) {\n return this.renderEmpty(this.text('shared-missing-text', 'This shared wishlist is no longer available.'));\n }\n throw error;\n }\n\n // Stands in for the page block's own heading, which styles.css hides in this view.\n this.toggleAttribute('shared-view', true);\n const heading = document.createElement('h2');\n heading.className = 'bagsy-wishlist__shared';\n heading.textContent = `${this.text('shared-heading', 'Shared wishlist')}: ${name}`;\n const cards = items.length ? await this.cards(items) : [];\n // Visitors save items to their own wishlist from here.\n this.replaceChildren(\n heading,\n cards.length\n ? this.grid(cards, this.text('save-label', 'Save'), this.text('saved-label', 'Saved'))\n : this.message(this.text('empty-text', 'Your wishlist is empty.')),\n );\n }\n\n /** Saved items → live cards, in saved order. Deleted or unpublished products drop out. */\n async cards(items: { productId: string; variantId?: string | null }[]): Promise<Card[]> {\n const ids = [...new Set(items.map(keyOf))];\n const variables = {\n country: this.getAttribute('country') || undefined,\n language: this.getAttribute('language')?.toUpperCase().replace('-', '_') || undefined,\n };\n const batches: string[][] = [];\n for (let i = 0; i < ids.length; i += BATCH) batches.push(ids.slice(i, i + BATCH));\n\n const results = await Promise.all(\n batches.map(async (batch) => {\n const response = await fetch(`/api/${STOREFRONT_API_VERSION}/graphql.json`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ query: PRODUCTS_QUERY, variables: { ...variables, ids: batch } }),\n });\n if (!response.ok) throw new Error(`Storefront API responded ${response.status}`);\n const body = (await response.json()) as { data?: { nodes: (ProductNode | VariantNode | null)[] }; errors?: unknown };\n if (!body.data) throw new Error(`Storefront API error: ${JSON.stringify(body.errors)}`);\n return body.data.nodes;\n }),\n );\n const byId = new Map<string, Card>();\n for (const node of results.flat()) {\n if (node && typeof node === 'object' && 'title' in node) byId.set(node.id, toCard(node));\n }\n return ids.map((id) => byId.get(id)).filter((card): card is Card => card !== undefined);\n }\n\n private shareControls(isShared: boolean) {\n const store = getStore();\n const actions = document.createElement('div');\n actions.className = 'bagsy-wishlist__actions';\n const share = this.actionButton('bagsy-wishlist__share', this.text('share-label', 'Share wishlist'));\n const unshare = this.actionButton('bagsy-wishlist__unshare', this.text('unshare-label', 'Stop sharing'));\n const status = document.createElement('span');\n status.className = 'bagsy-wishlist__status';\n status.setAttribute('role', 'status');\n unshare.hidden = !isShared;\n\n share.addEventListener('click', async () => {\n try {\n const token = await shareList(store.transport, 'default');\n const url = new URL(location.href);\n url.search = '';\n url.hash = '';\n url.searchParams.set(SHARE_PARAM, token);\n unshare.hidden = false;\n if (navigator.share) {\n await navigator.share({ title: document.title, url: url.toString() }).catch(() => {});\n } else {\n await navigator.clipboard.writeText(url.toString());\n status.textContent = this.text('copied-label', 'Link copied');\n }\n } catch (error) {\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n }\n });\n unshare.addEventListener('click', async () => {\n try {\n await unshareList(store.transport, 'default');\n unshare.hidden = true;\n status.textContent = this.text('unshared-label', 'Sharing stopped');\n } catch (error) {\n this.dispatchEvent(new CustomEvent('bagsy:error', { detail: { error }, bubbles: true }));\n }\n });\n\n actions.append(share, unshare, status);\n return actions;\n }\n\n private actionButton(className: string, label: string) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = className;\n button.textContent = label;\n return button;\n }\n\n private message(text: string) {\n const message = document.createElement('p');\n message.className = 'bagsy-wishlist__empty';\n message.textContent = text;\n return message;\n }\n\n private renderEmpty(text: string) {\n this.replaceChildren(this.message(text));\n }\n\n private grid(cards: Card[], label: string, labelSaved: string) {\n const root = getStore().root;\n const locale = document.documentElement.lang || undefined;\n const list = document.createElement('ul');\n list.className = 'bagsy-wishlist__grid';\n list.setAttribute('role', 'list');\n\n for (const card of cards) {\n const item = document.createElement('li');\n item.className = 'bagsy-wishlist__item';\n item.dataset.key = keyOf(card);\n item.dataset.productId = card.productId;\n\n if (card.image) {\n const image = document.createElement('img');\n image.className = 'bagsy-wishlist__image';\n const src = new URL(card.image.url);\n src.searchParams.set('width', '600');\n image.src = src.toString();\n image.alt = card.image.altText ?? '';\n image.loading = 'lazy';\n image.decoding = 'async';\n if (card.image.width && card.image.height) {\n image.width = card.image.width;\n image.height = card.image.height;\n }\n item.append(image);\n }\n\n const title = document.createElement('a');\n title.className = 'bagsy-wishlist__title';\n const href = new URL(`${root}/products/${encodeURIComponent(card.handle)}`, location.origin);\n if (card.variantId) href.searchParams.set('variant', card.variantId.split('/').pop()!);\n title.href = href.pathname + href.search;\n title.textContent = card.title;\n item.append(title);\n\n if (card.variantTitle) {\n const variant = document.createElement('span');\n variant.className = 'bagsy-wishlist__variant';\n variant.textContent = card.variantTitle;\n item.append(variant);\n }\n\n const price = document.createElement('span');\n price.className = 'bagsy-wishlist__price';\n price.textContent = formatPrice(card.price.amount, card.price.currencyCode, locale);\n\n const button = document.createElement('bagsy-button');\n button.setAttribute('product-id', card.productId);\n if (card.variantId) button.setAttribute('variant-id', card.variantId);\n button.setAttribute('icon-only', '');\n button.setAttribute('label', label);\n button.setAttribute('label-saved', labelSaved);\n\n item.append(price, button);\n if (!card.available) {\n const soldOut = document.createElement('span');\n soldOut.className = 'bagsy-wishlist__sold-out';\n soldOut.textContent = this.text('sold-out-label', 'Sold out');\n item.append(soldOut);\n }\n list.append(item);\n }\n return list;\n }\n}\n\nfunction formatPrice(amount: string, currency: string, locale?: string) {\n try {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(Number(amount));\n } catch {\n return `${amount} ${currency}`;\n }\n}\n","import { BagsyButton } from './button.js';\nimport { BagsyCount } from './count.js';\nimport { BagsyWishlist } from './wishlist.js';\n\nexport { BagsyButton, BagsyCount, BagsyWishlist };\nexport { BagsyStore, getStore, setStore, type BagsyConfig, type ChangeDetail } from './store.js';\n\n/** Register the elements. Safe to call more than once. */\nexport function define() {\n if (typeof customElements === 'undefined') return;\n for (const [name, element] of [\n ['bagsy-button', BagsyButton],\n ['bagsy-count', BagsyCount],\n ['bagsy-wishlist', BagsyWishlist],\n ] as const) {\n if (!customElements.get(name)) customElements.define(name, element);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuCO,IAAM,aAAN,cAAyB,YAAY;AAAA,EAa1C,YAAoB,SAAsB,CAAC,GAAG;AAC5C,UAAM;AADY;AAElB,UAAM,OAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,QAAQ,OAAO,EAAE;AAEhE,UAAM,YAAY,OAAO,WAAW,SAAY,aAAa;AAC7D,SAAK,YAAY,gBAAgB;AAAA,MAC/B,MAAM;AAAA,MACN,WAAW,GAAG,IAAI;AAAA,MAClB,GAAI,OAAO,WAAW,EAAE,SAAS,OAAO,QAAQ;AAAA,MAChD,GAAI,aAAa,EAAE,UAAU;AAAA,MAC7B,GAAI,OAAO,SAAS,EAAE,OAAO,OAAO,MAAM;AAAA,IAC5C,CAAC;AACD,SAAK,QAAQ,KAAK,mBAAmB;AAAA,EACvC;AAAA,EAboB;AAAA;AAAA,EAXX;AAAA,EACD,QAAQ,oBAAI,IAA0B;AAAA,EACtC,UAAU,oBAAI,IAAY;AAAA,EAC1B;AAAA,EACA,YAAY,oBAAI,IAA2B;AAAA;AAAA,EAE1C;AAAA,EACT;AAAA;AAAA,EAES;AAAA;AAAA,EAkBT,MAAc,qBAAqB;AACjC,QAAI,CAAC,KAAK,OAAO,SAAU;AAC3B,UAAM,QAAQ,cAAc;AAC5B,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,YAAM,aAAa,KAAK,WAAW,KAAK;AACxC,qBAAe;AAAA,IACjB,SAAS,OAAO;AACd,WAAK,KAAK,eAAe,EAAE,MAAM,CAAC;AAAA,IACpC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAAmB,UAAgC;AACvD,UAAM,KAAK,WAAW,SAAS;AAC/B,QAAI,MAAM,KAAK,UAAU,IAAI,EAAE;AAC/B,QAAI,CAAC,IAAK,MAAK,UAAU,IAAI,IAAK,MAAM,oBAAI,IAAI,CAAE;AAClD,QAAI,IAAI,QAAQ;AAChB,QAAI,CAAC,KAAK,MAAM,IAAI,EAAE,EAAG,MAAK,MAAM,EAAE;AAAA,QACjC,UAAS;AACd,WAAO,MAAM,IAAK,OAAO,QAAQ;AAAA,EACnC;AAAA,EAEA,QAAQ,WAAmB,WAAyC;AAClE,UAAM,UAAU,KAAK,MAAM,IAAI,WAAW,SAAS,CAAC;AACpD,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,cAAc,OAAW,QAAO,QAAQ,SAAS;AAErD,UAAM,MAAM,WAAW,SAAS;AAChC,WAAO,QAAQ,KAAK,CAAC,UAAU,CAAC,MAAM,aAAa,MAAM,cAAc,GAAG;AAAA,EAC5E;AAAA;AAAA,EAGA,MAAM,OAAO,WAAmB,WAAsC;AACpE,UAAM,KAAK;AACX,UAAM,KAAK,WAAW,SAAS;AAC/B,UAAM,WAAW,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC;AACxC,UAAM,WAAW,KAAK,QAAQ,IAAI,SAAS,KAAK;AAChD,UAAM,UAAU,cAAc,SAAY,SAAY,WAAW,SAAS;AAE1E,UAAM,QAAQ,YAAY,WAAW,SAAS,KAAK,CAAC,UAAU,CAAC,MAAM,SAAS;AAE9E,SAAK,MAAM;AAAA,MACT;AAAA,MACA,WACI,SAAS,OAAO,CAAC,UAAW,WAAW,CAAC,QAAQ,MAAM,cAAc,UAAU,KAAM,IACpF,CAAC,GAAG,UAAU,EAAE,QAAQ,WAAW,QAAQ,WAAW,GAAI,WAAW,EAAE,WAAW,QAAQ,EAAG,CAAC;AAAA,IACpG;AACA,SAAK,YAAY,WAAW,KAAK,CAAC;AAClC,SAAK,OAAO,EAAE;AAEd,QAAI;AACF,YAAM,EAAE,MAAM,IAAI,SACb,MAAM,cAAc,KAAK,WAAW,EAAE,GAAG,EAAE,OAAO,MAAM,KACzD,MAAM,WAAW,KAAK,WAAW,IAAI,UAAU,EAAE,WAAW,QAAQ,IAAI,CAAC,CAAC;AAC9E,UAAI,UAAU,UAAU;AAEtB,aAAK,MAAM,EAAE;AACb,aAAK,KAAK,UAAU,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACtC;AACA,WAAK,KAAK,gBAAgB,EAAE,WAAW,IAAI,GAAI,WAAW,EAAE,WAAW,QAAQ,GAAI,MAAM,CAAC;AAC1F,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,MAAM,IAAI,IAAI,QAAQ;AAC3B,WAAK,YAAY,WAAW,IAAI,EAAE;AAClC,WAAK,OAAO,EAAE;AACd,WAAK,MAAM,EAAE;AACb,WAAK,KAAK,eAAe,EAAE,OAAO,WAAW,GAAG,CAAC;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,QAAQ;AACb,UAAM,KAAK;AACX,WAAO,aAAa,KAAK,WAAW,SAAS;AAAA,EAC/C;AAAA,EAEQ;AAAA;AAAA,EAGR,cAA+B;AAC7B,WAAQ,KAAK,iBAAiB,KAAK,UAAU;AAAA,EAC/C;AAAA;AAAA,EAGA,MAAM,YAA6B;AACjC,UAAM,KAAK;AACX,UAAM,QAAQ,MAAM,UAAU,KAAK,SAAS;AAC5C,SAAK,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,GAAG,aAAa;AAChE,SAAK,KAAK,eAAe,EAAE,OAAO,KAAK,MAAM,CAAC;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,YAAY,OAAe;AACjC,QAAI,KAAK,UAAU,OAAW;AAC9B,SAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK;AAC3C,SAAK,KAAK,eAAe,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,EAChD;AAAA,EAEQ,MAAM,IAAY;AACxB,SAAK,QAAQ,IAAI,EAAE;AAEnB,SAAK,UAAU,QAAQ,QAAQ,EAAE,KAAK,YAAY;AAChD,YAAM,MAAM,CAAC,GAAG,KAAK,OAAO;AAC5B,WAAK,QAAQ,MAAM;AACnB,WAAK,QAAQ;AACb,UAAI;AACF,cAAM,KAAK;AACX,cAAM,SAAS,MAAM,YAAY,KAAK,WAAW,GAAG;AACpD,mBAAW,OAAO,KAAK;AACrB,eAAK,MAAM,IAAI,KAAK,OAAO,IAAI,GAAG,CAAC;AACnC,eAAK,OAAO,GAAG;AAAA,QACjB;AAAA,MACF,SAAS,OAAO;AACd,aAAK,KAAK,eAAe,EAAE,MAAM,CAAC;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,IAAY;AACzB,eAAW,YAAY,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC,EAAG,UAAS;AAAA,EAChE;AAAA,EAEQ,KAAK,MAAc,QAAiB;AAC1C,UAAM,QAAQ,IAAI,YAAY,MAAM,EAAE,OAAO,CAAC;AAC9C,SAAK,cAAc,KAAK;AAExB,eAAW,UAAU,cAAc,IAAI,YAAY,MAAM,EAAE,OAAO,CAAC,CAAC;AAAA,EACtE;AACF;AAEA,IAAI;AAGG,SAAS,WAAuB;AACrC,MAAI,OAAQ,QAAO;AACnB,QAAM,SAAS,WAAW,UAAU,eAAe,cAAc;AACjE,MAAI,SAAsB,CAAC;AAC3B,MAAI;AACF,aAAS,QAAQ,cAAc,KAAK,MAAM,OAAO,WAAW,IAAI,CAAC;AAAA,EACnE,QAAQ;AAAA,EAER;AACA,SAAQ,SAAS,IAAI,WAAW,MAAM;AACxC;AAGO,SAAS,SAAS,OAA+B;AACtD,WAAS;AACX;;;ACvNA,IAAM,gBAAgB;AACtB,IAAM,iBAAiB,CAAC,UAAU,kBAAkB,gBAAgB;AAGpE,IAAM,QACJ;AAeK,IAAM,cAAN,cAA0B,YAAY;AAAA,EAC3C,OAAO,qBAAqB,CAAC,cAAc,cAAc,SAAS,aAAa;AAAA,EAEvE;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA;AAAA,EAEP,kBAAkB,MAAM,WAAW,MAAM,KAAK,YAAY,CAAC;AAAA,EAEnE,cAAc;AACZ,UAAM;AACN,UAAM,OAAO,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAC/C,SAAK,YAAY,mFAAmF,KAAK;AACzG,SAAK,SAAS,KAAK,cAAc,QAAQ;AACzC,SAAK,QAAQ,KAAK,cAAc,cAAc;AAC9C,SAAK,OAAO,iBAAiB,SAAS,CAAC,UAAU,KAAK,QAAQ,KAAK,CAAC;AAAA,EACtE;AAAA,EAEA,oBAAoB;AAClB,QAAI,KAAK,aAAa,gBAAgB,GAAG;AACvC,WAAK,YAAY;AACjB,iBAAW,QAAQ,eAAgB,UAAS,iBAAiB,MAAM,KAAK,eAAe;AAAA,IACzF;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,uBAAuB;AACrB,eAAW,QAAQ,eAAgB,UAAS,oBAAoB,MAAM,KAAK,eAAe;AAC1F,SAAK,UAAU;AACf,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGQ,cAAc;AACpB,UAAM,QAAS,KAAK,QAAQ,kBAAkB,GAAG,cAAc,aAAa,KAC1E,SAAS,cAAc,aAAa;AACtC,QAAI,OAAO,SAAS,MAAM,UAAU,KAAK,UAAW,MAAK,aAAa,cAAc,MAAM,KAAK;AAAA,EACjG;AAAA,EAEA,yBAAyB,MAAc,UAAyB,MAAqB;AACnF,QAAI,aAAa,KAAM;AACvB,SAAK,SAAS,gBAAgB,SAAS,iBAAiB,KAAK,YAAa,MAAK,UAAU;AAAA,QACpF,MAAK,OAAO;AAAA,EACnB;AAAA,EAEA,IAAI,YAAY;AACd,WAAO,KAAK,aAAa,YAAY,KAAK;AAAA,EAC5C;AAAA,EAEA,IAAI,YAAY;AACd,WAAO,KAAK,aAAa,YAAY,KAAK;AAAA,EAC5C;AAAA,EAEQ,YAAY;AAClB,SAAK,UAAU;AACf,QAAI,CAAC,KAAK,UAAW;AACrB,QAAI;AACF,WAAK,UAAU,SAAS,EAAE,MAAM,KAAK,WAAW,MAAM,KAAK,OAAO,CAAC;AAAA,IACrE,SAAS,OAAO;AACd,cAAQ,KAAK,mBAAmB,KAAK;AAAA,IACvC;AACA,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,SAAS;AACf,UAAM,QAAQ,KAAK,YAAY,SAAS,EAAE,QAAQ,KAAK,WAAW,KAAK,SAAS,IAAI;AACpF,UAAM,OAAO,QAAS,KAAK,aAAa,aAAa,KAAK,UAAY,KAAK,aAAa,OAAO,KAAK;AACpG,SAAK,gBAAgB,SAAS,UAAU,IAAI;AAC5C,SAAK,OAAO,aAAa,gBAAgB,OAAO,UAAU,IAAI,CAAC;AAC/D,UAAM,OAAO,OAAO,UAAU,UAAa,KAAK,IAAI;AACpD,SAAK,OAAO,aAAa,aAAa,IAAI;AAC1C,SAAK,aAAa,aAAa,IAAI;AACnC,SAAK,OAAO,aAAa,cAAc,IAAI;AAC3C,SAAK,MAAM,cAAc;AAAA,EAC3B;AAAA,EAEA,MAAc,QAAQ,OAAc;AAElC,UAAM,eAAe;AACrB,UAAM,gBAAgB;AACtB,QAAI,KAAK,QAAQ,CAAC,KAAK,aAAa,SAAS,EAAE,QAAQ,KAAK,WAAW,KAAK,SAAS,MAAM,OAAW;AACtG,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,QAAI;AACF,YAAM,SAAS,EAAE,OAAO,KAAK,WAAW,KAAK,SAAS;AAAA,IACxD,QAAQ;AAAA,IAER,UAAE;AACA,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AACF;;;AC7GO,IAAM,aAAN,cAAyB,YAAY;AAAA,EAClC,UAAU,MAAM,KAAK,OAAO;AAAA,EAEpC,oBAAoB;AAClB,UAAM,QAAQ,SAAS;AACvB,UAAM,iBAAiB,eAAe,KAAK,OAAO;AAClD,QAAI,MAAM,UAAU,OAAW,OAAM,YAAY,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACjE,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,uBAAuB;AACrB,aAAS,EAAE,oBAAoB,eAAe,KAAK,OAAO;AAAA,EAC5D;AAAA,EAEQ,SAAS;AACf,UAAM,QAAQ,SAAS,EAAE;AACzB,SAAK,cAAc,UAAU,SAAY,KAAK,OAAO,KAAK;AAC1D,SAAK,gBAAgB,UAAU,KAAK,aAAa,YAAY,KAAK,CAAC,KAAK;AAAA,EAC1E;AACF;;;ACtBA,IAAM,yBAAyB;AAC/B,IAAM,YAAY;AAClB,IAAM,QAAQ;AACP,IAAM,cAAc;AAEpB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqD9B,IAAM,SAAS,CAAC,SACd,aAAa,OACT;AAAA,EACE,WAAW,KAAK,QAAQ;AAAA,EACxB,WAAW,KAAK;AAAA,EAChB,OAAO,KAAK,QAAQ;AAAA;AAAA,EAEpB,cAAc,KAAK,UAAU,kBAAkB,SAAY,KAAK;AAAA,EAChE,QAAQ,KAAK,QAAQ;AAAA,EACrB,WAAW,KAAK;AAAA,EAChB,OAAO,KAAK,SAAS,KAAK,QAAQ;AAAA,EAClC,OAAO,KAAK;AACd,IACA;AAAA,EACE,WAAW,KAAK;AAAA,EAChB,OAAO,KAAK;AAAA,EACZ,QAAQ,KAAK;AAAA,EACb,WAAW,KAAK;AAAA,EAChB,OAAO,KAAK;AAAA,EACZ,OAAO,KAAK,WAAW;AACzB;AAEN,IAAM,QAAQ,CAAC,SAA2D,KAAK,aAAa,KAAK;AAc1F,IAAM,gBAAN,cAA4B,YAAY;AAAA,EACrC,SAAS;AAAA,EAET,WAAW,CAAC,UAAiB;AACnC,UAAM,EAAE,WAAW,WAAW,MAAM,IAAK,MAAoC;AAC7E,QAAI,SAAS,KAAK,OAAQ;AAE1B,eAAW,QAAQ,KAAK,iBAA8B,uBAAuB,GAAG;AAC9E,UAAI,YAAY,KAAK,QAAQ,QAAQ,YAAY,KAAK,QAAQ,cAAc,UAAW,MAAK,OAAO;AAAA,IACrG;AACA,QAAI,CAAC,KAAK,cAAc,uBAAuB,EAAG,MAAK,YAAY,KAAK,aAAa,YAAY,KAAK,yBAAyB;AAAA,EACjI;AAAA,EAEQ,OAAO,CAAC,MAAc,aAAqB,KAAK,aAAa,IAAI,KAAK;AAAA,EAE9E,oBAAoB;AAClB,aAAS,EAAE,iBAAiB,gBAAgB,KAAK,QAAQ;AACzD,SAAK,aAAa,aAAa,MAAM;AACrC,UAAM,QAAQ,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,WAAW;AAClE,KAAC,QAAQ,KAAK,WAAW,KAAK,IAAI,KAAK,QAAQ,GAC5C,MAAM,CAAC,UAAU;AAChB,cAAQ,KAAK,qBAAqB,KAAK;AACvC,WAAK,cAAc,IAAI,YAAY,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,IACzF,CAAC,EACA,QAAQ,MAAM,KAAK,gBAAgB,WAAW,CAAC;AAAA,EACpD;AAAA,EAEA,uBAAuB;AACrB,aAAS,EAAE,oBAAoB,gBAAgB,KAAK,QAAQ;AAAA,EAC9D;AAAA,EAEA,MAAc,UAAU;AACtB,UAAM,QAAQ,SAAS;AACvB,UAAM,QAAgB,CAAC;AACvB,qBAAiB,QAAQ,MAAM,MAAM,GAAG;AACtC,YAAM,KAAK,IAAI;AACf,UAAI,MAAM,UAAU,UAAW;AAAA,IACjC;AACA,UAAM,QAAQ,MAAM,SAAS,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AACxD,QAAI,CAAC,MAAM,OAAQ,QAAO,KAAK,YAAY,KAAK,KAAK,cAAc,yBAAyB,CAAC;AAE7F,UAAM,QAAgB,CAAC;AACvB,QAAI,KAAK,aAAa,OAAO,GAAG;AAC9B,YAAM,OAAO,MAAM,QAAQ,MAAM,WAAW,SAAS;AACrD,YAAM,KAAK,KAAK,cAAc,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,IACzD;AACA,UAAM,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,cAAc,MAAM,GAAG,KAAK,KAAK,gBAAgB,sBAAsB,CAAC,CAAC;AAC/G,SAAK,gBAAgB,GAAG,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAc,WAAW,OAAe;AACtC,SAAK,SAAS;AACd,UAAM,IAAI,SAAS,EAAE;AACrB,UAAM,QAAgB,CAAC;AACvB,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACF,SAAG;AACD,cAAM,OAAO,MAAM,cAAc,GAAG,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;AAChE,eAAO,KAAK,KAAK;AACjB,cAAM,KAAK,GAAG,KAAK,MAAM,IAAI;AAC7B,gBAAQ,KAAK,MAAM,SAAS,cAAe,KAAK,MAAM,SAAS,aAAa,SAAa;AAAA,MAC3F,SAAS,SAAS,MAAM,SAAS;AAAA,IACnC,SAAS,OAAO;AAEd,UAAK,OAAsC,WAAW,KAAK;AACzD,eAAO,KAAK,YAAY,KAAK,KAAK,uBAAuB,8CAA8C,CAAC;AAAA,MAC1G;AACA,YAAM;AAAA,IACR;AAGA,SAAK,gBAAgB,eAAe,IAAI;AACxC,UAAM,UAAU,SAAS,cAAc,IAAI;AAC3C,YAAQ,YAAY;AACpB,YAAQ,cAAc,GAAG,KAAK,KAAK,kBAAkB,iBAAiB,CAAC,KAAK,IAAI;AAChF,UAAM,QAAQ,MAAM,SAAS,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAExD,SAAK;AAAA,MACH;AAAA,MACA,MAAM,SACF,KAAK,KAAK,OAAO,KAAK,KAAK,cAAc,MAAM,GAAG,KAAK,KAAK,eAAe,OAAO,CAAC,IACnF,KAAK,QAAQ,KAAK,KAAK,cAAc,yBAAyB,CAAC;AAAA,IACrE;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,MAAM,OAA4E;AACtF,UAAM,MAAM,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC;AACzC,UAAM,YAAY;AAAA,MAChB,SAAS,KAAK,aAAa,SAAS,KAAK;AAAA,MACzC,UAAU,KAAK,aAAa,UAAU,GAAG,YAAY,EAAE,QAAQ,KAAK,GAAG,KAAK;AAAA,IAC9E;AACA,UAAM,UAAsB,CAAC;AAC7B,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAO,SAAQ,KAAK,IAAI,MAAM,GAAG,IAAI,KAAK,CAAC;AAEhF,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,QAAQ,IAAI,OAAO,UAAU;AAC3B,cAAM,WAAW,MAAM,MAAM,QAAQ,sBAAsB,iBAAiB;AAAA,UAC1E,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,QAAQ,mBAAmB;AAAA,UAC1E,MAAM,KAAK,UAAU,EAAE,OAAO,gBAAgB,WAAW,EAAE,GAAG,WAAW,KAAK,MAAM,EAAE,CAAC;AAAA,QACzF,CAAC;AACD,YAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,EAAE;AAC/E,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAI,CAAC,KAAK,KAAM,OAAM,IAAI,MAAM,yBAAyB,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AACtF,eAAO,KAAK,KAAK;AAAA,MACnB,CAAC;AAAA,IACH;AACA,UAAM,OAAO,oBAAI,IAAkB;AACnC,eAAW,QAAQ,QAAQ,KAAK,GAAG;AACjC,UAAI,QAAQ,OAAO,SAAS,YAAY,WAAW,KAAM,MAAK,IAAI,KAAK,IAAI,OAAO,IAAI,CAAC;AAAA,IACzF;AACA,WAAO,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,SAAuB,SAAS,MAAS;AAAA,EACxF;AAAA,EAEQ,cAAc,UAAmB;AACvC,UAAM,QAAQ,SAAS;AACvB,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,UAAM,QAAQ,KAAK,aAAa,yBAAyB,KAAK,KAAK,eAAe,gBAAgB,CAAC;AACnG,UAAM,UAAU,KAAK,aAAa,2BAA2B,KAAK,KAAK,iBAAiB,cAAc,CAAC;AACvG,UAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,WAAO,YAAY;AACnB,WAAO,aAAa,QAAQ,QAAQ;AACpC,YAAQ,SAAS,CAAC;AAElB,UAAM,iBAAiB,SAAS,YAAY;AAC1C,UAAI;AACF,cAAM,QAAQ,MAAM,UAAU,MAAM,WAAW,SAAS;AACxD,cAAM,MAAM,IAAI,IAAI,SAAS,IAAI;AACjC,YAAI,SAAS;AACb,YAAI,OAAO;AACX,YAAI,aAAa,IAAI,aAAa,KAAK;AACvC,gBAAQ,SAAS;AACjB,YAAI,UAAU,OAAO;AACnB,gBAAM,UAAU,MAAM,EAAE,OAAO,SAAS,OAAO,KAAK,IAAI,SAAS,EAAE,CAAC,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACtF,OAAO;AACL,gBAAM,UAAU,UAAU,UAAU,IAAI,SAAS,CAAC;AAClD,iBAAO,cAAc,KAAK,KAAK,gBAAgB,aAAa;AAAA,QAC9D;AAAA,MACF,SAAS,OAAO;AACd,aAAK,cAAc,IAAI,YAAY,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,MACzF;AAAA,IACF,CAAC;AACD,YAAQ,iBAAiB,SAAS,YAAY;AAC5C,UAAI;AACF,cAAM,YAAY,MAAM,WAAW,SAAS;AAC5C,gBAAQ,SAAS;AACjB,eAAO,cAAc,KAAK,KAAK,kBAAkB,iBAAiB;AAAA,MACpE,SAAS,OAAO;AACd,aAAK,cAAc,IAAI,YAAY,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,MACzF;AAAA,IACF,CAAC;AAED,YAAQ,OAAO,OAAO,SAAS,MAAM;AACrC,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,WAAmB,OAAe;AACrD,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,OAAO;AACd,WAAO,YAAY;AACnB,WAAO,cAAc;AACrB,WAAO;AAAA,EACT;AAAA,EAEQ,QAAQ,MAAc;AAC5B,UAAM,UAAU,SAAS,cAAc,GAAG;AAC1C,YAAQ,YAAY;AACpB,YAAQ,cAAc;AACtB,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,MAAc;AAChC,SAAK,gBAAgB,KAAK,QAAQ,IAAI,CAAC;AAAA,EACzC;AAAA,EAEQ,KAAK,OAAe,OAAe,YAAoB;AAC7D,UAAM,OAAO,SAAS,EAAE;AACxB,UAAM,SAAS,SAAS,gBAAgB,QAAQ;AAChD,UAAM,OAAO,SAAS,cAAc,IAAI;AACxC,SAAK,YAAY;AACjB,SAAK,aAAa,QAAQ,MAAM;AAEhC,eAAW,QAAQ,OAAO;AACxB,YAAM,OAAO,SAAS,cAAc,IAAI;AACxC,WAAK,YAAY;AACjB,WAAK,QAAQ,MAAM,MAAM,IAAI;AAC7B,WAAK,QAAQ,YAAY,KAAK;AAE9B,UAAI,KAAK,OAAO;AACd,cAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,cAAM,YAAY;AAClB,cAAM,MAAM,IAAI,IAAI,KAAK,MAAM,GAAG;AAClC,YAAI,aAAa,IAAI,SAAS,KAAK;AACnC,cAAM,MAAM,IAAI,SAAS;AACzB,cAAM,MAAM,KAAK,MAAM,WAAW;AAClC,cAAM,UAAU;AAChB,cAAM,WAAW;AACjB,YAAI,KAAK,MAAM,SAAS,KAAK,MAAM,QAAQ;AACzC,gBAAM,QAAQ,KAAK,MAAM;AACzB,gBAAM,SAAS,KAAK,MAAM;AAAA,QAC5B;AACA,aAAK,OAAO,KAAK;AAAA,MACnB;AAEA,YAAM,QAAQ,SAAS,cAAc,GAAG;AACxC,YAAM,YAAY;AAClB,YAAM,OAAO,IAAI,IAAI,GAAG,IAAI,aAAa,mBAAmB,KAAK,MAAM,CAAC,IAAI,SAAS,MAAM;AAC3F,UAAI,KAAK,UAAW,MAAK,aAAa,IAAI,WAAW,KAAK,UAAU,MAAM,GAAG,EAAE,IAAI,CAAE;AACrF,YAAM,OAAO,KAAK,WAAW,KAAK;AAClC,YAAM,cAAc,KAAK;AACzB,WAAK,OAAO,KAAK;AAEjB,UAAI,KAAK,cAAc;AACrB,cAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,gBAAQ,YAAY;AACpB,gBAAQ,cAAc,KAAK;AAC3B,aAAK,OAAO,OAAO;AAAA,MACrB;AAEA,YAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,YAAM,YAAY;AAClB,YAAM,cAAc,YAAY,KAAK,MAAM,QAAQ,KAAK,MAAM,cAAc,MAAM;AAElF,YAAM,SAAS,SAAS,cAAc,cAAc;AACpD,aAAO,aAAa,cAAc,KAAK,SAAS;AAChD,UAAI,KAAK,UAAW,QAAO,aAAa,cAAc,KAAK,SAAS;AACpE,aAAO,aAAa,aAAa,EAAE;AACnC,aAAO,aAAa,SAAS,KAAK;AAClC,aAAO,aAAa,eAAe,UAAU;AAE7C,WAAK,OAAO,OAAO,MAAM;AACzB,UAAI,CAAC,KAAK,WAAW;AACnB,cAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,gBAAQ,YAAY;AACpB,gBAAQ,cAAc,KAAK,KAAK,kBAAkB,UAAU;AAC5D,aAAK,OAAO,OAAO;AAAA,MACrB;AACA,WAAK,OAAO,IAAI;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,QAAgB,UAAkB,QAAiB;AACtE,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,EAC7F,QAAQ;AACN,WAAO,GAAG,MAAM,IAAI,QAAQ;AAAA,EAC9B;AACF;;;ACtVO,SAAS,SAAS;AACvB,MAAI,OAAO,mBAAmB,YAAa;AAC3C,aAAW,CAAC,MAAM,OAAO,KAAK;AAAA,IAC5B,CAAC,gBAAgB,WAAW;AAAA,IAC5B,CAAC,eAAe,UAAU;AAAA,IAC1B,CAAC,kBAAkB,aAAa;AAAA,EAClC,GAAY;AACV,QAAI,CAAC,eAAe,IAAI,IAAI,EAAG,gBAAe,OAAO,MAAM,OAAO;AAAA,EACpE;AACF;","names":[]}
@@ -3,9 +3,8 @@ import { CreateBagsyServerOptions, BagsyServer, Shopper, WritableShopper, Custom
3
3
  import * as _shopify_hydrogen from '@shopify/hydrogen';
4
4
  import { ShopifyRequestContext } from '@shopify/hydrogen';
5
5
  import { ReadonlyCustomerSessionManager, WritableCustomerSessionManager, CustomerSession } from '@shopify/hydrogen/customer-account';
6
- import './transport-DTApDImd.js';
7
- import './types-C1ZgQf5w.js';
8
- import './client-2LxFO18p.js';
6
+ import './transport-Chh09xIP.js';
7
+ import './client-CXCgpFGa.js';
9
8
 
10
9
  /** For loaders and server components: a read-only session. */
11
10
  interface HydrogenReadContext {