@lime-bundles/widget 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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/styles/widget-styles.ts","../src/thankyou/index.ts","../src/index.ts"],"sourcesContent":["/**\n * <lime-bundle> Web Component — renders Lime Bundles on headless storefronts.\n *\n * Usage:\n * <lime-bundle\n * shop-domain=\"my-store.myshopify.com\"\n * storefront-token=\"abc123\"\n * bundle-gid=\"gid://shopify/Metaobject/12345\"\n * ></lime-bundle>\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundle,\n detectCartApi,\n createAjaxCartApi,\n createStorefrontCartApi,\n observeImpression,\n reportImpression,\n reportAddToCart,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n type CartLineItem,\n} from \"@lime-bundles/core\";\nimport { renderFixedBundle } from \"./renderers/fixed\";\nimport { renderMixMatchBundle } from \"./renderers/mix-match\";\nimport { renderVolumeBundle } from \"./renderers/volume\";\nimport { WIDGET_STYLES } from \"./styles/widget-styles\";\n\nexport class LimeBundleElement extends HTMLElement {\n static observedAttributes = [\n \"shop-domain\",\n \"storefront-token\",\n \"bundle-gid\",\n \"cart-id\",\n \"app-url\",\n \"analytics\",\n \"locale\",\n ];\n\n private shadow: ShadowRoot;\n private bundle: ParsedBundle | null = null;\n private abortController: AbortController | null = null;\n private impressionCleanup: (() => void) | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n connectedCallback() {\n this.render();\n this.fetchBundle();\n }\n\n disconnectedCallback() {\n this.abortController?.abort();\n this.teardownImpression();\n }\n\n /** Clean up stale bundle state and active impression observer. */\n private teardownImpression() {\n this.impressionCleanup?.();\n this.impressionCleanup = null;\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n // Ignore no-op writes (same value re-set) and pre-connect attribute parsing\n if (oldValue === newValue || !this.isConnected) return;\n\n if (name === \"bundle-gid\" || name === \"shop-domain\" || name === \"storefront-token\") {\n // Only refetch when all required attributes are present — prevents\n // partial-attribute races during element initialization or programmatic updates\n if (this.shopDomain && this.storefrontToken && this.bundleGid) {\n this.fetchBundle();\n }\n }\n }\n\n private get shopDomain(): string {\n return this.getAttribute(\"shop-domain\") ?? \"\";\n }\n\n private get storefrontToken(): string {\n return this.getAttribute(\"storefront-token\") ?? \"\";\n }\n\n private get bundleGid(): string {\n return this.getAttribute(\"bundle-gid\") ?? \"\";\n }\n\n private get cartId(): string | undefined {\n return this.getAttribute(\"cart-id\") ?? undefined;\n }\n\n private get appUrl(): string {\n return this.getAttribute(\"app-url\") ?? \"\";\n }\n\n private get analyticsEnabled(): boolean {\n return this.getAttribute(\"analytics\") !== \"false\";\n }\n\n private async fetchBundle() {\n if (!this.shopDomain || !this.storefrontToken || !this.bundleGid) {\n this.renderError(\"Missing required attributes: shop-domain, storefront-token, bundle-gid\");\n return;\n }\n\n // Abort any previous fetch and create a fresh controller.\n // Capture in a local const so post-await checks reference the controller\n // for THIS invocation, not a later one created by a concurrent attributeChangedCallback.\n this.abortController?.abort();\n const controller = new AbortController();\n this.abortController = controller;\n\n this.renderLoading();\n\n try {\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: this.bundleGid },\n );\n\n // Check the LOCAL controller — if a newer fetch replaced this.abortController,\n // this invocation's controller will be aborted.\n if (controller.signal.aborted) return;\n\n if (!data.metaobject) {\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\"Bundle not found\");\n return;\n }\n\n this.bundle = parseMetaobjectBundle(\n data.metaobject.id,\n data.metaobject.fields,\n );\n\n if (!this.bundle) {\n this.teardownImpression();\n this.renderError(\"Bundle is not active or has expired\");\n return;\n }\n\n this.renderBundle();\n this.setupImpression();\n } catch (err) {\n if (controller.signal.aborted) return;\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\n err instanceof Error ? err.message : \"Failed to load bundle\",\n );\n }\n }\n\n private renderBundle() {\n if (!this.bundle) return;\n\n const container = document.createElement(\"div\");\n container.className = \"lb-bundle\";\n container.setAttribute(\"role\", \"region\");\n container.setAttribute(\"aria-label\", this.bundle.title);\n\n const addToCart = async (items: CartLineItem[]) => {\n const apiType = detectCartApi();\n const cart =\n apiType === \"ajax\"\n ? createAjaxCartApi(this.bundleGid, this.bundle!.bundleType)\n : createStorefrontCartApi(\n createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n }),\n this.bundleGid,\n this.bundle!.bundleType,\n this.cartId,\n );\n\n let result: Awaited<ReturnType<typeof cart.addLines>>;\n try {\n result = await cart.addLines(items);\n } catch (err) {\n result = {\n success: false,\n error: err instanceof Error ? err.message : \"Cart add failed\",\n };\n }\n\n if (result.success) {\n // Always dispatch the CustomEvent so consumers can react to add-to-cart\n // regardless of analytics configuration\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:add-to-cart\", {\n detail: { items, cartId: result.cartId },\n bubbles: true,\n composed: true,\n }),\n );\n\n // Analytics reporting is separate — only fires when enabled and appUrl is set\n if (this.analyticsEnabled && this.appUrl) {\n const quantity = items.reduce((sum, i) => sum + i.quantity, 0);\n const totalPrice = items.reduce((sum, item) => {\n const product = this.bundle!.products.find((p) =>\n p.variants.nodes.some((v) => v.id === item.variantId),\n );\n const variant = product?.variants.nodes.find((v) => v.id === item.variantId);\n const price = variant ? parseFloat(variant.price.amount) : 0;\n return sum + price * item.quantity;\n }, 0);\n\n reportAddToCart(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle!.bundleType,\n productId: this.bundle!.products[0]?.id ?? \"\",\n quantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n },\n );\n }\n }\n\n return result;\n };\n\n switch (this.bundle.bundleType) {\n case \"fixed\":\n renderFixedBundle(container, this.bundle, addToCart);\n break;\n case \"mix_match\":\n renderMixMatchBundle(container, this.bundle, addToCart);\n break;\n case \"volume\":\n renderVolumeBundle(container, this.bundle, addToCart);\n break;\n }\n\n this.shadow.innerHTML = \"\";\n const style = document.createElement(\"style\");\n style.textContent = WIDGET_STYLES;\n this.shadow.appendChild(style);\n this.shadow.appendChild(container);\n\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:loaded\", {\n detail: {\n bundleType: this.bundle.bundleType,\n title: this.bundle.title,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private setupImpression() {\n if (!this.analyticsEnabled || !this.bundle || !this.appUrl) return;\n\n this.impressionCleanup?.();\n this.impressionCleanup = observeImpression(this, () => {\n reportImpression(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle!.bundleType,\n },\n );\n });\n }\n\n private renderLoading() {\n this.shadow.innerHTML = `\n <style>${WIDGET_STYLES}</style>\n <div class=\"lb-bundle lb-bundle--loading\">\n <div class=\"lb-skeleton lb-skeleton--title\"></div>\n <div class=\"lb-skeleton lb-skeleton--products\"></div>\n </div>\n `;\n }\n\n private renderError(message: string) {\n this.shadow.innerHTML = \"\";\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message, code: \"LOAD_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private render() {\n // Initial empty state\n this.shadow.innerHTML = `<style>${WIDGET_STYLES}</style>`;\n }\n}\n","/**\n * DOM renderer for fixed bundles.\n */\nimport { formatMoney, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderFixedBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Discount badge\n if (bundle.discountLabel) {\n const badge = document.createElement(\"span\");\n badge.className = \"lb-bundle__discount-badge\";\n badge.textContent = bundle.discountLabel;\n container.appendChild(badge);\n }\n\n // Products\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products\";\n\n for (const product of bundle.products) {\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product\";\n productEl.setAttribute(\"part\", \"product\");\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(product.priceRange.minVariantPrice.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n // CTA button\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.textContent = bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\";\n button.setAttribute(\"part\", \"button\");\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n button.textContent = \"Adding...\";\n\n const items: CartLineItem[] = bundle.products\n .filter((p) => p.variants.nodes.some((v) => v.availableForSale))\n .map((p) => {\n const variant = p.variants.nodes.find((v) => v.availableForSale)!;\n return { variantId: variant.id, quantity: 1 };\n });\n\n const result = await addToCart(items);\n\n button.disabled = false;\n button.textContent = bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\";\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n\n container.appendChild(button);\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for mix-and-match bundles.\n */\nimport { formatMoney, validateQuantity, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderMixMatchBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const selections = new Map<string, { variantId: string; quantity: number }>();\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Instructions\n const instructions = document.createElement(\"p\");\n instructions.className = \"lb-bundle__instructions\";\n instructions.textContent = bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : \"Select your items\";\n container.appendChild(instructions);\n\n // Products\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products lb-bundle__products--selectable\";\n\n for (const product of bundle.products) {\n const variant = product.variants.nodes.find((v) => v.availableForSale) ?? product.variants.nodes[0];\n if (!variant) continue;\n\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--selectable\";\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(variant.price.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n\n const selectBtn = document.createElement(\"button\");\n selectBtn.className = \"lb-bundle__select-btn\";\n selectBtn.textContent = variant.availableForSale ? \"Select\" : \"Sold out\";\n selectBtn.disabled = !variant.availableForSale;\n\n selectBtn.addEventListener(\"click\", () => {\n const key = product.id;\n if (selections.has(key)) {\n selections.delete(key);\n productEl.classList.remove(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Select\";\n } else {\n selections.set(key, { variantId: variant.id, quantity: 1 });\n productEl.classList.add(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Selected\";\n }\n updateCta();\n });\n\n productEl.appendChild(selectBtn);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n // Validation message\n const validationEl = document.createElement(\"p\");\n validationEl.className = \"lb-bundle__validation\";\n container.appendChild(validationEl);\n\n // CTA\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n button.disabled = true;\n container.appendChild(button);\n\n function updateCta() {\n const total = Array.from(selections.values()).reduce((s, v) => s + v.quantity, 0);\n const validation = validateQuantity(total, bundle.minQuantity, bundle.maxQuantity);\n button.disabled = !validation.valid;\n button.textContent = bundle.widgetConfig.ctaText ?? `Add ${total} Items to Cart`;\n validationEl.textContent = validation.message ?? \"\";\n }\n\n updateCta();\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n button.textContent = \"Adding...\";\n\n const items: CartLineItem[] = Array.from(selections.entries()).map(([, s]) => ({\n variantId: s.variantId,\n quantity: s.quantity,\n }));\n\n const result = await addToCart(items);\n updateCta();\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for volume bundles.\n */\nimport { formatMoney, calculateTierSavings, type ParsedBundle, type CartLineItem, type AddToCartResult } from \"@lime-bundles/core\";\n\nexport function renderVolumeBundle(\n container: HTMLElement,\n bundle: ParsedBundle,\n addToCart: (items: CartLineItem[]) => Promise<AddToCartResult>,\n) {\n const product = bundle.products[0];\n if (!product) return;\n\n const basePrice = parseFloat(product.priceRange.minVariantPrice.amount);\n const currency = product.priceRange.minVariantPrice.currencyCode;\n let quantity = 1;\n\n // Title\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n // Product\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--volume\";\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(basePrice, currency))} each</p>\n `;\n productEl.appendChild(info);\n container.appendChild(productEl);\n\n // Tier table\n const tiersDiv = document.createElement(\"div\");\n tiersDiv.className = \"lb-bundle__tiers\";\n tiersDiv.setAttribute(\"role\", \"table\");\n tiersDiv.setAttribute(\"aria-label\", \"Volume discounts\");\n container.appendChild(tiersDiv);\n\n // Quantity selector\n const qtyWrapper = document.createElement(\"div\");\n qtyWrapper.className = \"lb-bundle__quantity-selector\";\n const label = document.createElement(\"label\");\n label.textContent = \"Quantity\";\n qtyWrapper.appendChild(label);\n\n const qtyControl = document.createElement(\"div\");\n qtyControl.className = \"lb-bundle__quantity-control\";\n\n const minusBtn = document.createElement(\"button\");\n minusBtn.textContent = \"−\";\n minusBtn.setAttribute(\"aria-label\", \"Decrease quantity\");\n\n const qtyInput = document.createElement(\"input\");\n qtyInput.type = \"number\";\n qtyInput.min = \"1\";\n qtyInput.value = \"1\";\n qtyInput.className = \"lb-bundle__quantity-input\";\n\n const plusBtn = document.createElement(\"button\");\n plusBtn.textContent = \"+\";\n plusBtn.setAttribute(\"aria-label\", \"Increase quantity\");\n\n qtyControl.append(minusBtn, qtyInput, plusBtn);\n qtyWrapper.appendChild(qtyControl);\n container.appendChild(qtyWrapper);\n\n // CTA\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n container.appendChild(button);\n\n function updateTiers() {\n const savings = calculateTierSavings(bundle.volumeTiers, basePrice, quantity);\n tiersDiv.innerHTML = \"\";\n for (const ts of savings) {\n const row = document.createElement(\"div\");\n row.className = `lb-bundle__tier${ts.isActive ? \" lb-bundle__tier--active\" : \"\"}`;\n row.setAttribute(\"role\", \"row\");\n row.innerHTML = `\n <span class=\"lb-bundle__tier-quantity\" role=\"cell\">${ts.tier.minQuantity}+ items</span>\n <span class=\"lb-bundle__tier-price\" role=\"cell\">${escapeHtml(formatMoney(ts.unitPrice, currency))} each</span>\n <span class=\"lb-bundle__tier-savings\" role=\"cell\">Save ${ts.savingsPercent.toFixed(0)}%</span>\n ${ts.tier.label ? `<span class=\"lb-bundle__tier-label\" role=\"cell\">${escapeHtml(ts.tier.label)}</span>` : \"\"}\n `;\n tiersDiv.appendChild(row);\n }\n button.textContent = bundle.widgetConfig.ctaText ?? `Add ${quantity} to Cart`;\n }\n\n updateTiers();\n\n minusBtn.addEventListener(\"click\", () => {\n if (quantity > 1) { quantity--; qtyInput.value = String(quantity); updateTiers(); }\n });\n plusBtn.addEventListener(\"click\", () => {\n quantity++; qtyInput.value = String(quantity); updateTiers();\n });\n qtyInput.addEventListener(\"change\", () => {\n const val = parseInt(qtyInput.value, 10);\n if (!isNaN(val) && val > 0) { quantity = val; updateTiers(); }\n });\n\n button.addEventListener(\"click\", async () => {\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n button.disabled = true;\n button.textContent = \"Adding...\";\n\n const items: CartLineItem[] = [{ variantId: variant.id, quantity }];\n const result = await addToCart(items);\n\n button.disabled = false;\n updateTiers();\n\n if (!result.success) {\n const error = document.createElement(\"p\");\n error.className = \"lb-bundle__error\";\n error.textContent = result.error ?? \"Failed to add to cart\";\n container.appendChild(error);\n setTimeout(() => error.remove(), 5000);\n }\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * CSS styles inlined into Shadow DOM.\n * Uses CSS custom properties that pierce the shadow boundary for theming.\n */\nexport const WIDGET_STYLES = `\n:host {\n display: block;\n --lb-primary-color: #000;\n --lb-secondary-color: #666;\n --lb-accent-color: #2563eb;\n --lb-background: #fff;\n --lb-border-color: #e5e7eb;\n --lb-border-radius: 8px;\n --lb-font-family: inherit;\n --lb-font-size: 14px;\n --lb-spacing-sm: 8px;\n --lb-spacing-md: 16px;\n --lb-spacing-lg: 24px;\n --lb-button-bg: var(--lb-accent-color);\n --lb-button-text: #fff;\n --lb-button-radius: var(--lb-border-radius);\n --lb-savings-color: #16a34a;\n --lb-error-color: #dc2626;\n}\n\n.lb-bundle {\n font-family: var(--lb-font-family);\n font-size: var(--lb-font-size);\n color: var(--lb-primary-color);\n background: var(--lb-background);\n border: 1px solid var(--lb-border-color);\n border-radius: var(--lb-border-radius);\n padding: var(--lb-spacing-lg);\n}\n\n.lb-bundle__title { margin: 0 0 var(--lb-spacing-md); font-size: 1.25em; font-weight: 600; }\n.lb-bundle__discount-badge { display: inline-block; background: var(--lb-savings-color); color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 600; margin-bottom: var(--lb-spacing-md); }\n.lb-bundle__products { display: grid; gap: var(--lb-spacing-md); margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__product { display: flex; gap: var(--lb-spacing-md); align-items: center; padding: var(--lb-spacing-sm); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__product--selected { border-color: var(--lb-accent-color); }\n.lb-bundle__product-image { width: 64px; height: 64px; object-fit: cover; border-radius: calc(var(--lb-border-radius) - 2px); flex-shrink: 0; }\n.lb-bundle__product-info { flex: 1; min-width: 0; }\n.lb-bundle__product-title { margin: 0; font-weight: 500; }\n.lb-bundle__product-price { margin: 4px 0 0; color: var(--lb-secondary-color); }\n.lb-bundle__instructions { color: var(--lb-secondary-color); margin: 0 0 var(--lb-spacing-md); }\n.lb-bundle__tiers { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__tier { display: flex; align-items: center; gap: var(--lb-spacing-md); padding: var(--lb-spacing-sm) var(--lb-spacing-md); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); margin-bottom: var(--lb-spacing-sm); }\n.lb-bundle__tier--active { border-color: var(--lb-savings-color); }\n.lb-bundle__tier-savings { color: var(--lb-savings-color); font-weight: 600; }\n.lb-bundle__quantity-selector { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__quantity-selector label { display: block; margin-bottom: var(--lb-spacing-sm); font-weight: 500; }\n.lb-bundle__quantity-control { display: inline-flex; align-items: center; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__quantity-control button { width: 32px; height: 32px; border: none; background: transparent; cursor: pointer; font-size: 1.1em; display: flex; align-items: center; justify-content: center; }\n.lb-bundle__quantity-input { width: 40px; text-align: center; border: none; border-left: 1px solid var(--lb-border-color); border-right: 1px solid var(--lb-border-color); height: 32px; font-size: var(--lb-font-size); -moz-appearance: textfield; }\n.lb-bundle__quantity-input::-webkit-outer-spin-button, .lb-bundle__quantity-input::-webkit-inner-spin-button { -webkit-appearance: none; }\n.lb-bundle__select-btn { padding: 6px 12px; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); background: transparent; cursor: pointer; }\n.lb-bundle__select-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__cta { width: 100%; padding: 12px 24px; border: none; border-radius: var(--lb-button-radius); background: var(--lb-button-bg); color: var(--lb-button-text); font-size: 1em; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }\n.lb-bundle__cta:hover:not(:disabled) { opacity: 0.9; }\n.lb-bundle__cta:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__error { color: var(--lb-error-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n.lb-bundle__validation { color: var(--lb-secondary-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n\n.lb-skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: lb-shimmer 1.5s infinite; border-radius: var(--lb-border-radius); }\n.lb-skeleton--title { height: 24px; width: 60%; margin-bottom: var(--lb-spacing-md); }\n.lb-skeleton--products { height: 200px; }\n@keyframes lb-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n`;\n","/**\n * Thank-you page snippet — fires purchase events for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-thankyou.js\"></script>\n * <script>\n * LimeBundles.trackPurchase({\n * shopDomain: 'my-store.myshopify.com',\n * orderId: 'gid://shopify/Order/12345',\n * lineItems: [\n * { bundleGid: '...', bundleType: 'fixed', price: 29.99, quantity: 1 }\n * ]\n * });\n * </script>\n */\n\ninterface PurchaseLineItem {\n bundleGid: string;\n bundleType: string;\n price: number;\n quantity: number;\n}\n\ninterface TrackPurchaseInput {\n shopDomain: string;\n orderId: string;\n lineItems: PurchaseLineItem[];\n appUrl?: string;\n}\n\n// Deduplication: track order+bundle combos we've already reported\nconst reportedPurchases = new Set<string>();\n\nexport function trackPurchase(input: TrackPurchaseInput): void {\n const appUrl = input.appUrl ?? `https://${input.shopDomain}`;\n\n // Group line items by bundleGid (merge quantities and prices)\n const bundleMap = new Map<string, { bundleType: string; revenue: number; lineItemCount: number }>();\n\n for (const item of input.lineItems) {\n if (!item.bundleGid) continue;\n\n const existing = bundleMap.get(item.bundleGid);\n if (existing) {\n existing.revenue += item.price * item.quantity;\n existing.lineItemCount += 1;\n } else {\n bundleMap.set(item.bundleGid, {\n bundleType: item.bundleType,\n revenue: item.price * item.quantity,\n lineItemCount: 1,\n });\n }\n }\n\n // Fire purchase events for each unique bundle\n for (const [bundleGid, data] of bundleMap) {\n const dedupKey = `${input.orderId}:${bundleGid}`;\n if (reportedPurchases.has(dedupKey)) continue;\n reportedPurchases.add(dedupKey);\n\n const payload = {\n shopDomain: input.shopDomain,\n eventType: \"bundle_purchased\",\n bundleGid,\n bundleType: data.bundleType,\n orderId: input.orderId,\n revenue: Math.round(data.revenue * 100) / 100,\n lineItemCount: data.lineItemCount,\n occurredAt: new Date().toISOString(),\n };\n\n // Fire-and-forget with sendBeacon fallback\n const url = `${appUrl}/api/analytics`;\n const body = JSON.stringify(payload);\n\n try {\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n }).catch(() => {\n // Fallback to sendBeacon\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon) {\n navigator.sendBeacon(url, body);\n }\n });\n } catch {\n if (typeof navigator !== \"undefined\" && navigator.sendBeacon) {\n navigator.sendBeacon(url, body);\n }\n }\n }\n}\n","/**\n * @lime-bundles/widget — Vanilla JS Web Component for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-bundle.js\"></script>\n * <lime-bundle shop-domain=\"...\" storefront-token=\"...\" bundle-gid=\"...\"></lime-bundle>\n */\nimport { LimeBundleElement } from \"./lime-bundle\";\n\n// Register custom element\nif (typeof customElements !== \"undefined\" && !customElements.get(\"lime-bundle\")) {\n customElements.define(\"lime-bundle\", LimeBundleElement);\n}\n\nexport { LimeBundleElement };\nexport { trackPurchase } from \"./thankyou/index\";\n"],"mappings":"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","names":["formatMoney","escapeHtml","formatMoney","escapeHtml"]}
1
+ {"version":3,"sources":["../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/styles/widget-styles.ts","../src/index.ts"],"sourcesContent":["/**\n * <lime-bundle> Web Component — renders Lime Bundles on any storefront.\n *\n * Usage:\n * <lime-bundle\n * shop-domain=\"my-store.myshopify.com\"\n * storefront-token=\"abc123\"\n * bundle-gid=\"gid://shopify/Metaobject/12345\"\n * app-url=\"https://bundles.example.com\"\n * ></lime-bundle>\n *\n * document.querySelector(\"lime-bundle\").addEventListener(\n * \"lime-bundle:add-to-cart\",\n * (ev) => { cart.linesAdd(ev.detail.lines); }\n * );\n *\n * BYO-cart model: the widget fires `lime-bundle:add-to-cart` with a\n * `CartLineInput[]` payload in the `detail.lines` field. Merchants wire\n * this to their cart system (Hydrogen's useCart, Storefront Cart API,\n * ajax cart — whatever). The widget does not perform the cart add\n * itself; it optimistically reports success to the UI after dispatch.\n *\n * Analytics: the widget calls `reportImpression` / `reportAddToCart`\n * against the app URL if the `analytics` attribute is not \"false\" and\n * `app-url` is set. These fire-and-forget.\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n SHOP_CUSTOM_CSS_QUERY,\n parseMetaobjectBundle,\n observeImpression,\n reportImpression,\n reportAddToCart,\n injectCustomCss,\n type ParsedBundle,\n type FixedBundleData,\n type VolumeBundleData,\n type MixMatchBundleData,\n type BundleMetaobjectResponse,\n type ShopCustomCssResponse,\n type CartLineInput,\n} from \"@lime-bundles/core\";\nimport { renderFixedBundle } from \"./renderers/fixed\";\nimport { renderMixMatchBundle } from \"./renderers/mix-match\";\nimport { renderVolumeBundle } from \"./renderers/volume\";\nimport { WIDGET_STYLES } from \"./styles/widget-styles\";\n\nexport class LimeBundleElement extends HTMLElement {\n static observedAttributes = [\n \"shop-domain\",\n \"storefront-token\",\n \"bundle-gid\",\n \"app-url\",\n \"analytics\",\n \"locale\",\n ];\n\n private shadow: ShadowRoot;\n private bundle: ParsedBundle | null = null;\n private abortController: AbortController | null = null;\n private impressionCleanup: (() => void) | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n connectedCallback() {\n this.render();\n this.fetchBundle();\n }\n\n disconnectedCallback() {\n this.abortController?.abort();\n this.teardownImpression();\n }\n\n private teardownImpression() {\n this.impressionCleanup?.();\n this.impressionCleanup = null;\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue || !this.isConnected) return;\n\n if (\n name === \"bundle-gid\" ||\n name === \"shop-domain\" ||\n name === \"storefront-token\"\n ) {\n if (this.shopDomain && this.storefrontToken && this.bundleGid) {\n this.fetchBundle();\n }\n }\n }\n\n private get shopDomain(): string {\n return this.getAttribute(\"shop-domain\") ?? \"\";\n }\n\n private get storefrontToken(): string {\n return this.getAttribute(\"storefront-token\") ?? \"\";\n }\n\n private get bundleGid(): string {\n return this.getAttribute(\"bundle-gid\") ?? \"\";\n }\n\n private get appUrl(): string {\n return this.getAttribute(\"app-url\") ?? \"\";\n }\n\n private get analyticsEnabled(): boolean {\n return this.getAttribute(\"analytics\") !== \"false\";\n }\n\n private async fetchBundle() {\n if (!this.shopDomain || !this.storefrontToken || !this.bundleGid) {\n this.renderError(\n \"Missing required attributes: shop-domain, storefront-token, bundle-gid\",\n );\n return;\n }\n\n this.abortController?.abort();\n const controller = new AbortController();\n this.abortController = controller;\n\n this.renderLoading();\n\n try {\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n\n // Fetch bundle data and custom CSS in parallel. Custom CSS is best-effort;\n // if it fails we still render the bundle without merchant styling.\n const [bundleData, cssData] = await Promise.all([\n client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: this.bundleGid },\n { signal: controller.signal },\n ),\n client\n .query<ShopCustomCssResponse>(\n SHOP_CUSTOM_CSS_QUERY,\n undefined,\n { signal: controller.signal },\n )\n .catch(() => null),\n ]);\n\n if (controller.signal.aborted) return;\n\n if (!bundleData.metaobject) {\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\"Bundle not found\");\n return;\n }\n\n this.bundle = parseMetaobjectBundle(\n bundleData.metaobject.id,\n bundleData.metaobject.fields,\n );\n\n if (!this.bundle) {\n this.teardownImpression();\n this.renderError(\"Bundle is not active or has expired\");\n return;\n }\n\n // Best-effort custom CSS injection. No-op in jsdom/SSR contexts.\n if (cssData?.shop?.metafield?.value) {\n injectCustomCss(this.shopDomain, cssData.shop.metafield.value);\n }\n\n this.renderBundle();\n this.setupImpression();\n } catch (err) {\n if (controller.signal.aborted) return;\n this.bundle = null;\n this.teardownImpression();\n this.renderError(\n err instanceof Error ? err.message : \"Failed to load bundle\",\n );\n }\n }\n\n /**\n * Dispatch add-to-cart for merchant handling. Returns true — the widget\n * reports success optimistically. If the merchant's cart mutation fails,\n * they're responsible for surfacing that error in their own UI.\n */\n private dispatchAddToCart = (lines: CartLineInput[]): void => {\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:add-to-cart\", {\n detail: { lines },\n bubbles: true,\n composed: true,\n }),\n );\n\n if (this.analyticsEnabled && this.appUrl && this.bundle) {\n const quantity = lines.reduce((sum, l) => sum + l.quantity, 0);\n const totalPrice = lines.reduce((sum, line) => {\n const product = this.bundle!.products.find((p) =>\n p.variants.nodes.some((v) => v.id === line.merchandiseId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === line.merchandiseId,\n );\n const price = variant ? parseFloat(variant.price.amount) : 0;\n return sum + price * line.quantity;\n }, 0);\n\n reportAddToCart(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle.bundleType,\n productId: this.bundle.products[0]?.id ?? \"\",\n quantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n },\n );\n }\n };\n\n private renderBundle() {\n if (!this.bundle) return;\n\n const container = document.createElement(\"div\");\n container.className = \"lb-bundle\";\n container.setAttribute(\"role\", \"region\");\n container.setAttribute(\"aria-label\", this.bundle.title);\n\n switch (this.bundle.bundleType) {\n case \"fixed\":\n renderFixedBundle(\n container,\n this.bundle as FixedBundleData,\n this.dispatchAddToCart,\n );\n break;\n case \"mix_match\":\n renderMixMatchBundle(\n container,\n this.bundle as MixMatchBundleData,\n this.dispatchAddToCart,\n );\n break;\n case \"volume\":\n renderVolumeBundle(\n container,\n this.bundle as VolumeBundleData,\n this.dispatchAddToCart,\n );\n break;\n }\n\n this.shadow.innerHTML = \"\";\n const style = document.createElement(\"style\");\n style.textContent = WIDGET_STYLES;\n this.shadow.appendChild(style);\n this.shadow.appendChild(container);\n\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:loaded\", {\n detail: {\n bundleType: this.bundle.bundleType,\n title: this.bundle.title,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private setupImpression() {\n if (!this.analyticsEnabled || !this.bundle || !this.appUrl) return;\n\n this.impressionCleanup?.();\n this.impressionCleanup = observeImpression(this, () => {\n reportImpression(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: this.bundleGid,\n bundleType: this.bundle!.bundleType,\n },\n );\n });\n }\n\n private renderLoading() {\n this.shadow.innerHTML = `\n <style>${WIDGET_STYLES}</style>\n <div class=\"lb-bundle lb-bundle--loading\">\n <div class=\"lb-skeleton lb-skeleton--title\"></div>\n <div class=\"lb-skeleton lb-skeleton--products\"></div>\n </div>\n `;\n }\n\n private renderError(message: string) {\n this.shadow.innerHTML = \"\";\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message, code: \"LOAD_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private render() {\n this.shadow.innerHTML = `<style>${WIDGET_STYLES}</style>`;\n }\n}\n","/**\n * DOM renderer for fixed bundles.\n *\n * Takes the narrowed `FixedBundleData` variant so we get TS errors if any\n * caller passes a non-fixed bundle.\n *\n * `onAddToCart` is a BYO-cart dispatch: the widget owner listens for\n * `lime-bundle:add-to-cart` (CustomEvent) and performs the actual cart\n * mutation. This renderer only builds the DOM and invokes the dispatch\n * — it does not know how or whether the add succeeds.\n */\nimport {\n formatMoney,\n type FixedBundleData,\n type CartLineInput,\n} from \"@lime-bundles/core\";\n\nexport function renderFixedBundle(\n container: HTMLElement,\n bundle: FixedBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n) {\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n if (bundle.discountLabel) {\n const badge = document.createElement(\"span\");\n badge.className = \"lb-bundle__discount-badge\";\n badge.textContent = bundle.discountLabel;\n container.appendChild(badge);\n }\n\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products\";\n\n for (const product of bundle.products) {\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product\";\n productEl.setAttribute(\"part\", \"product\");\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(product.priceRange.minVariantPrice.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.textContent = bundle.widgetConfig.ctaText ?? \"Add Bundle to Cart\";\n button.setAttribute(\"part\", \"button\");\n\n button.addEventListener(\"click\", () => {\n const lines: CartLineInput[] = bundle.products\n .filter((p) => p.variants.nodes.some((v) => v.availableForSale))\n .map((p) => {\n const variant = p.variants.nodes.find((v) => v.availableForSale)!;\n return {\n merchandiseId: variant.id,\n quantity: 1,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n };\n });\n\n if (lines.length === 0) return;\n onAddToCart(lines);\n });\n\n container.appendChild(button);\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for mix-and-match bundles. See fixed.ts for the BYO-cart\n * contract.\n */\nimport {\n formatMoney,\n validateQuantity,\n type MixMatchBundleData,\n type CartLineInput,\n} from \"@lime-bundles/core\";\n\nexport function renderMixMatchBundle(\n container: HTMLElement,\n bundle: MixMatchBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n) {\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const selections = new Map<string, { variantId: string; quantity: number }>();\n\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n const instructions = document.createElement(\"p\");\n instructions.className = \"lb-bundle__instructions\";\n instructions.textContent =\n bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : \"Select your items\";\n container.appendChild(instructions);\n\n const productsDiv = document.createElement(\"div\");\n productsDiv.className = \"lb-bundle__products lb-bundle__products--selectable\";\n\n for (const product of bundle.products) {\n const variant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n if (!variant) continue;\n\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--selectable\";\n\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(variant.price.amount, currency))}</p>\n `;\n productEl.appendChild(info);\n\n const selectBtn = document.createElement(\"button\");\n selectBtn.className = \"lb-bundle__select-btn\";\n selectBtn.textContent = variant.availableForSale ? \"Select\" : \"Sold out\";\n selectBtn.disabled = !variant.availableForSale;\n\n selectBtn.addEventListener(\"click\", () => {\n const key = product.id;\n if (selections.has(key)) {\n selections.delete(key);\n productEl.classList.remove(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Select\";\n } else {\n selections.set(key, { variantId: variant.id, quantity: 1 });\n productEl.classList.add(\"lb-bundle__product--selected\");\n selectBtn.textContent = \"Selected\";\n }\n updateCta();\n });\n\n productEl.appendChild(selectBtn);\n productsDiv.appendChild(productEl);\n }\n container.appendChild(productsDiv);\n\n const validationEl = document.createElement(\"p\");\n validationEl.className = \"lb-bundle__validation\";\n container.appendChild(validationEl);\n\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n button.disabled = true;\n container.appendChild(button);\n\n function updateCta() {\n const total = Array.from(selections.values()).reduce(\n (s, v) => s + v.quantity,\n 0,\n );\n const validation = validateQuantity(\n total,\n bundle.minQuantity,\n bundle.maxQuantity,\n );\n button.disabled = !validation.valid;\n button.textContent =\n bundle.widgetConfig.ctaText ?? `Add ${total} Items to Cart`;\n validationEl.textContent = validation.message ?? \"\";\n }\n\n updateCta();\n\n button.addEventListener(\"click\", () => {\n const lines: CartLineInput[] = Array.from(selections.values()).map((s) => ({\n merchandiseId: s.variantId,\n quantity: s.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n if (lines.length === 0) return;\n onAddToCart(lines);\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * DOM renderer for volume bundles. See fixed.ts for the BYO-cart contract.\n */\nimport {\n formatMoney,\n calculateTierSavings,\n type VolumeBundleData,\n type CartLineInput,\n} from \"@lime-bundles/core\";\n\nexport function renderVolumeBundle(\n container: HTMLElement,\n bundle: VolumeBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n) {\n const product = bundle.products[0];\n if (!product) return;\n\n const basePrice = parseFloat(product.priceRange.minVariantPrice.amount);\n const currency = product.priceRange.minVariantPrice.currencyCode;\n let quantity = 1;\n\n const title = document.createElement(\"h3\");\n title.className = \"lb-bundle__title\";\n title.textContent = bundle.title;\n title.setAttribute(\"part\", \"title\");\n container.appendChild(title);\n\n const productEl = document.createElement(\"div\");\n productEl.className = \"lb-bundle__product lb-bundle__product--volume\";\n if (product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = product.featuredImage.url;\n img.alt = product.featuredImage.altText ?? product.title;\n img.className = \"lb-bundle__product-image\";\n img.loading = \"lazy\";\n productEl.appendChild(img);\n }\n const info = document.createElement(\"div\");\n info.className = \"lb-bundle__product-info\";\n info.innerHTML = `\n <p class=\"lb-bundle__product-title\">${escapeHtml(product.title)}</p>\n <p class=\"lb-bundle__product-price\">${escapeHtml(formatMoney(basePrice, currency))} each</p>\n `;\n productEl.appendChild(info);\n container.appendChild(productEl);\n\n const tiersDiv = document.createElement(\"div\");\n tiersDiv.className = \"lb-bundle__tiers\";\n tiersDiv.setAttribute(\"role\", \"table\");\n tiersDiv.setAttribute(\"aria-label\", \"Volume discounts\");\n container.appendChild(tiersDiv);\n\n const qtyWrapper = document.createElement(\"div\");\n qtyWrapper.className = \"lb-bundle__quantity-selector\";\n const label = document.createElement(\"label\");\n label.textContent = \"Quantity\";\n qtyWrapper.appendChild(label);\n\n const qtyControl = document.createElement(\"div\");\n qtyControl.className = \"lb-bundle__quantity-control\";\n\n const minusBtn = document.createElement(\"button\");\n minusBtn.textContent = \"−\";\n minusBtn.setAttribute(\"aria-label\", \"Decrease quantity\");\n\n const qtyInput = document.createElement(\"input\");\n qtyInput.type = \"number\";\n qtyInput.min = \"1\";\n qtyInput.value = \"1\";\n qtyInput.className = \"lb-bundle__quantity-input\";\n\n const plusBtn = document.createElement(\"button\");\n plusBtn.textContent = \"+\";\n plusBtn.setAttribute(\"aria-label\", \"Increase quantity\");\n\n qtyControl.append(minusBtn, qtyInput, plusBtn);\n qtyWrapper.appendChild(qtyControl);\n container.appendChild(qtyWrapper);\n\n const button = document.createElement(\"button\");\n button.className = \"lb-bundle__cta\";\n button.setAttribute(\"part\", \"button\");\n container.appendChild(button);\n\n function updateTiers() {\n const savings = calculateTierSavings(\n bundle.volumeTiers,\n basePrice,\n quantity,\n );\n tiersDiv.innerHTML = \"\";\n for (const ts of savings) {\n const row = document.createElement(\"div\");\n row.className = `lb-bundle__tier${ts.isActive ? \" lb-bundle__tier--active\" : \"\"}`;\n row.setAttribute(\"role\", \"row\");\n row.innerHTML = `\n <span class=\"lb-bundle__tier-quantity\" role=\"cell\">${ts.tier.minQuantity}+ items</span>\n <span class=\"lb-bundle__tier-price\" role=\"cell\">${escapeHtml(formatMoney(ts.unitPrice, currency))} each</span>\n <span class=\"lb-bundle__tier-savings\" role=\"cell\">Save ${ts.savingsPercent.toFixed(0)}%</span>\n ${ts.tier.label ? `<span class=\"lb-bundle__tier-label\" role=\"cell\">${escapeHtml(ts.tier.label)}</span>` : \"\"}\n `;\n tiersDiv.appendChild(row);\n }\n button.textContent = bundle.widgetConfig.ctaText ?? `Add ${quantity} to Cart`;\n }\n\n updateTiers();\n\n minusBtn.addEventListener(\"click\", () => {\n if (quantity > 1) {\n quantity--;\n qtyInput.value = String(quantity);\n updateTiers();\n }\n });\n plusBtn.addEventListener(\"click\", () => {\n quantity++;\n qtyInput.value = String(quantity);\n updateTiers();\n });\n qtyInput.addEventListener(\"change\", () => {\n const val = parseInt(qtyInput.value, 10);\n if (!isNaN(val) && val > 0) {\n quantity = val;\n updateTiers();\n }\n });\n\n button.addEventListener(\"click\", () => {\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n onAddToCart([\n {\n merchandiseId: variant.id,\n quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n },\n ]);\n });\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement(\"div\");\n div.textContent = str;\n return div.innerHTML;\n}\n","/**\n * CSS styles inlined into Shadow DOM.\n * Uses CSS custom properties that pierce the shadow boundary for theming.\n */\nexport const WIDGET_STYLES = `\n:host {\n display: block;\n --lb-primary-color: #000;\n --lb-secondary-color: #666;\n --lb-accent-color: #2563eb;\n --lb-background: #fff;\n --lb-border-color: #e5e7eb;\n --lb-border-radius: 8px;\n --lb-font-family: inherit;\n --lb-font-size: 14px;\n --lb-spacing-sm: 8px;\n --lb-spacing-md: 16px;\n --lb-spacing-lg: 24px;\n --lb-button-bg: var(--lb-accent-color);\n --lb-button-text: #fff;\n --lb-button-radius: var(--lb-border-radius);\n --lb-savings-color: #16a34a;\n --lb-error-color: #dc2626;\n}\n\n.lb-bundle {\n font-family: var(--lb-font-family);\n font-size: var(--lb-font-size);\n color: var(--lb-primary-color);\n background: var(--lb-background);\n border: 1px solid var(--lb-border-color);\n border-radius: var(--lb-border-radius);\n padding: var(--lb-spacing-lg);\n}\n\n.lb-bundle__title { margin: 0 0 var(--lb-spacing-md); font-size: 1.25em; font-weight: 600; }\n.lb-bundle__discount-badge { display: inline-block; background: var(--lb-savings-color); color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 600; margin-bottom: var(--lb-spacing-md); }\n.lb-bundle__products { display: grid; gap: var(--lb-spacing-md); margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__product { display: flex; gap: var(--lb-spacing-md); align-items: center; padding: var(--lb-spacing-sm); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__product--selected { border-color: var(--lb-accent-color); }\n.lb-bundle__product-image { width: 64px; height: 64px; object-fit: cover; border-radius: calc(var(--lb-border-radius) - 2px); flex-shrink: 0; }\n.lb-bundle__product-info { flex: 1; min-width: 0; }\n.lb-bundle__product-title { margin: 0; font-weight: 500; }\n.lb-bundle__product-price { margin: 4px 0 0; color: var(--lb-secondary-color); }\n.lb-bundle__instructions { color: var(--lb-secondary-color); margin: 0 0 var(--lb-spacing-md); }\n.lb-bundle__tiers { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__tier { display: flex; align-items: center; gap: var(--lb-spacing-md); padding: var(--lb-spacing-sm) var(--lb-spacing-md); border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); margin-bottom: var(--lb-spacing-sm); }\n.lb-bundle__tier--active { border-color: var(--lb-savings-color); }\n.lb-bundle__tier-savings { color: var(--lb-savings-color); font-weight: 600; }\n.lb-bundle__quantity-selector { margin-bottom: var(--lb-spacing-lg); }\n.lb-bundle__quantity-selector label { display: block; margin-bottom: var(--lb-spacing-sm); font-weight: 500; }\n.lb-bundle__quantity-control { display: inline-flex; align-items: center; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); }\n.lb-bundle__quantity-control button { width: 32px; height: 32px; border: none; background: transparent; cursor: pointer; font-size: 1.1em; display: flex; align-items: center; justify-content: center; }\n.lb-bundle__quantity-input { width: 40px; text-align: center; border: none; border-left: 1px solid var(--lb-border-color); border-right: 1px solid var(--lb-border-color); height: 32px; font-size: var(--lb-font-size); -moz-appearance: textfield; }\n.lb-bundle__quantity-input::-webkit-outer-spin-button, .lb-bundle__quantity-input::-webkit-inner-spin-button { -webkit-appearance: none; }\n.lb-bundle__select-btn { padding: 6px 12px; border: 1px solid var(--lb-border-color); border-radius: var(--lb-border-radius); background: transparent; cursor: pointer; }\n.lb-bundle__select-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__cta { width: 100%; padding: 12px 24px; border: none; border-radius: var(--lb-button-radius); background: var(--lb-button-bg); color: var(--lb-button-text); font-size: 1em; font-weight: 600; cursor: pointer; transition: opacity 0.15s; }\n.lb-bundle__cta:hover:not(:disabled) { opacity: 0.9; }\n.lb-bundle__cta:disabled { opacity: 0.5; cursor: not-allowed; }\n.lb-bundle__error { color: var(--lb-error-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n.lb-bundle__validation { color: var(--lb-secondary-color); margin: var(--lb-spacing-sm) 0; font-size: 0.9em; }\n\n.lb-skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: lb-shimmer 1.5s infinite; border-radius: var(--lb-border-radius); }\n.lb-skeleton--title { height: 24px; width: 60%; margin-bottom: var(--lb-spacing-md); }\n.lb-skeleton--products { height: 200px; }\n@keyframes lb-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n`;\n","/**\n * @lime-bundles/widget — Vanilla JS Web Component for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-bundle.js\"></script>\n * <lime-bundle shop-domain=\"...\" storefront-token=\"...\" bundle-gid=\"...\"></lime-bundle>\n */\nimport { LimeBundleElement } from \"./lime-bundle\";\n\n// Register custom element\nif (\n typeof customElements !== \"undefined\" &&\n !customElements.get(\"lime-bundle\")\n) {\n customElements.define(\"lime-bundle\", LimeBundleElement);\n}\n\nexport { LimeBundleElement };\n"],"mappings":"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","names":["formatMoney","escapeHtml","formatMoney","escapeHtml"]}
@@ -1,4 +1,4 @@
1
- "use strict";var LimeBundles=(()=>{var A=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(t,e)=>{for(var n in e)A(t,n,{get:e[n],enumerable:!0})},te=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of X(e))!Z.call(t,r)&&r!==n&&A(t,r,{get:()=>e[r],enumerable:!(a=K(e,r))||a.enumerable});return t};var ne=t=>te(A({},"__esModule",{value:!0}),t);var be={};ee(be,{LimeBundleElement:()=>E,trackPurchase:()=>Y});var L=class extends Error{constructor(t){super(t.map(e=>e.message).join("; ")),this.errors=t,this.name="StorefrontApiError"}},re="2025-10";function S(t){let e=t.apiVersion??re,n=`https://${t.shopDomain}/api/${e}/graphql.json`;return{async query(a,r){let o=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","X-Shopify-Storefront-Access-Token":t.accessToken},body:JSON.stringify({query:a,variables:r})});if(!o.ok)throw new L([{message:`Storefront API error: ${o.status} ${o.statusText}`}]);let i=await o.json();if(i.errors?.length)throw new L(i.errors);return i.data}}}var D=`#graphql
1
+ "use strict";var LimeBundles=(()=>{var I=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var X=(n,e)=>{for(var t in e)I(n,t,{get:e[t],enumerable:!0})},K=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Y(e))!J.call(n,r)&&r!==t&&I(n,r,{get:()=>e[r],enumerable:!(i=W(e,r))||i.enumerable});return n};var Z=n=>K(I({},"__esModule",{value:!0}),n);var be={};X(be,{LimeBundleElement:()=>C});var A=class extends Error{constructor(n){super(n.map(e=>e.message).join("; ")),this.errors=n,this.name="StorefrontApiError"}},ee="2025-10";function L(n){let e=n.apiVersion??ee,t=`https://${n.shopDomain}/api/${e}/graphql.json`;return{async query(i,r,o){let s={"Content-Type":"application/json","X-Shopify-Storefront-Access-Token":n.accessToken};n.buyerIp&&(s["Shopify-Storefront-Buyer-IP"]=n.buyerIp);let a=await fetch(t,{method:"POST",headers:s,body:JSON.stringify({query:i,variables:r}),signal:o?.signal});if(!a.ok)throw new A([{message:`Storefront API error: ${a.status} ${a.statusText}`}]);let l=await a.json();if(l.errors?.length)throw new A(l.errors);return l.data}}}var M=`#graphql
2
2
  query BundleMetaobject($id: ID!) {
3
3
  metaobject(id: $id) {
4
4
  id
@@ -82,45 +82,29 @@
82
82
  }
83
83
  }
84
84
  }
85
- `,ae=new Set(["fixed","mix_match","volume"]),ie=new Set(["active"]);function $(t,e){let n=new Map(e.map(u=>[u.key,u])),a=n.get("title")?.value??"Bundle",r=n.get("bundle_type")?.value,o=n.get("status")?.value;if(!r||!ae.has(r)||!o||!ie.has(o))return null;let i=n.get("starts_at")?.value??null,s=n.get("ends_at")?.value??null,l=new Date;if(i&&new Date(i)>l||s&&new Date(s)<l)return null;let d=oe(n),p=se(n),c=le(n),f=de(n);return{id:t,title:a,bundleType:r,status:o,products:d,discountConfig:p,widgetConfig:c,volumeTiers:f,minQuantity:N(n,"min_quantity"),maxQuantity:N(n,"max_quantity"),startsAt:i,endsAt:s,discountLabel:n.get("discount_label")?.value??null,abTestId:n.get("ab_test_id")?.value??null,abTestConfig:k(n,"ab_test_config")}}function oe(t){let e=[],n=t.get("products");if(n?.reference&&"variants"in n.reference&&e.push(n.reference),n?.references?.nodes)for(let r of n.references.nodes)"variants"in r?e.push(r):"products"in r&&r.products?.nodes&&e.push(...r.products.nodes);let a=t.get("collection");if(a?.references?.nodes)for(let r of a.references.nodes)"products"in r&&r.products?.nodes&&e.push(...r.products.nodes);return e}function se(t){return{discountType:t.get("discount_type")?.value??"percentage",discountValue:parseFloat(t.get("discount_value")?.value??"0"),allowStacking:t.get("allow_stacking")?.value==="true"}}function le(t){let e=k(t,"widget_config");if(e&&typeof e=="object"){let n=e;return{primaryColor:n.primaryColor??null,ctaText:n.ctaText??null,outOfStockBehavior:n.outOfStockBehavior??"hide"}}return{primaryColor:null,ctaText:null,outOfStockBehavior:"hide"}}function de(t){let e=k(t,"volume_tiers");return Array.isArray(e)?e.filter(n=>typeof n=="object"&&n!==null).map(n=>({minQuantity:Number(n.minQuantity??0),discountType:n.discountType??"percentage",discountValue:Number(n.discountValue??0),label:n.label??null})):[]}function k(t,e){let n=t.get(e)?.value;if(!n)return null;try{return JSON.parse(n)}catch{return null}}function N(t,e){let n=t.get(e)?.value;if(!n)return null;let a=parseInt(n,10);return isNaN(a)?null:a}function B(t,e,n){return[...t].sort((r,o)=>r.minQuantity-o.minQuantity).map(r=>{let o=r.discountType==="percentage"?e*(r.discountValue/100):r.discountValue,i=Math.max(0,e-o),s=e-i,l=e>0?s/e*100:0,d=n>=r.minQuantity;return{tier:r,unitPrice:i,savings:s,savingsPercent:l,isActive:d}})}function q(t,e,n){return e!==null&&t<e?{valid:!1,totalQuantity:t,message:`Select at least ${e} item${e!==1?"s":""}`}:n!==null&&t>n?{valid:!1,totalQuantity:t,message:`Select at most ${n} item${n!==1?"s":""}`}:{valid:!0,totalQuantity:t,message:null}}function M(){return typeof window<"u"&&typeof window.Shopify=="object"?"ajax":"storefront"}var P=Promise.resolve();function O(t,e){return{async addLines(n){return new Promise(a=>{P=P.catch(()=>{}).then(async()=>{try{let r=n.map(i=>({id:ce(i.variantId),quantity:i.quantity,properties:{...i.properties,_lime_bundle_gid:t,_lime_bundle_type:e}})),o=await fetch("/cart/add.js",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({items:r})});if(!o.ok){let i=await o.json().catch(()=>({}));a({success:!1,error:i?.description?.toString()??`Cart error: ${o.status}`});return}a({success:!0})}catch(r){a({success:!1,error:r instanceof Error?r.message:"Cart add failed"})}})})}}}function ce(t){let e=t.match(/\/(\d+)$/);return e?parseInt(e[1],10):0}var ue=`#graphql
86
- mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
87
- cartLinesAdd(cartId: $cartId, lines: $lines) {
88
- cart {
89
- id
90
- }
91
- userErrors {
92
- field
93
- message
94
- }
95
- }
96
- }
97
- `,pe=`#graphql
98
- mutation CartCreate($input: CartInput!) {
99
- cartCreate(input: $input) {
100
- cart {
101
- id
102
- }
103
- userErrors {
104
- field
105
- message
85
+ `,N=`#graphql
86
+ query ShopCustomCss {
87
+ shop {
88
+ metafield(namespace: "$app", key: "custom_css") {
89
+ value
106
90
  }
107
91
  }
108
92
  }
109
- `;function j(t,e,n,a){return{async addLines(r){try{let o=r.map(i=>({merchandiseId:i.variantId,quantity:i.quantity,attributes:[{key:"_lime_bundle_gid",value:e},{key:"_lime_bundle_type",value:n},...Object.entries(i.properties??{}).map(([s,l])=>({key:s,value:l}))]}));if(a){let i=await t.query(ue,{cartId:a,lines:o});return i.cartLinesAdd.userErrors.length>0?{success:!1,error:i.cartLinesAdd.userErrors[0].message}:{success:!0,cartId:a}}else{let i=await t.query(pe,{input:{lines:o}});return i.cartCreate.userErrors.length>0?{success:!1,error:i.cartCreate.userErrors[0].message}:{success:!0,cartId:i.cartCreate.cart?.id}}}catch(o){return{success:!1,error:o instanceof Error?o.message:"Cart add failed"}}}}}async function U(t,e){await V(t,{shopDomain:t.shopDomain,eventType:"bundle_impression",bundleGid:e.bundleGid,bundleType:e.bundleType,productId:e.productId,abTestId:e.abTestId,abVariant:e.abVariant,occurredAt:new Date().toISOString()})}async function F(t,e){await V(t,{shopDomain:t.shopDomain,eventType:"bundle_add_to_cart",...e,occurredAt:new Date().toISOString()})}function R(t,e){if(typeof IntersectionObserver>"u")return e(),()=>{};let n=new IntersectionObserver(a=>{for(let r of a)r.isIntersecting&&(e(),n.unobserve(r.target))},{threshold:.5});return n.observe(t),()=>n.disconnect()}async function V(t,e){let n=`${t.appUrl}/api/analytics`,a=JSON.stringify(e);try{(await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:a})).ok||(await new Promise(o=>setTimeout(o,2e3)),await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:a}))}catch{try{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(n,a)}catch{}}}var fe=720*60*60;function C(t,e){let n=typeof t=="string"?parseFloat(t):t;try{return new Intl.NumberFormat(void 0,{style:"currency",currency:e}).format(n)}catch{return`${e} ${n.toFixed(2)}`}}function H(t,e,n){let a=e.products[0]?.priceRange.minVariantPrice.currencyCode??"USD",r=document.createElement("h3");if(r.className="lb-bundle__title",r.textContent=e.title,r.setAttribute("part","title"),t.appendChild(r),e.discountLabel){let s=document.createElement("span");s.className="lb-bundle__discount-badge",s.textContent=e.discountLabel,t.appendChild(s)}let o=document.createElement("div");o.className="lb-bundle__products";for(let s of e.products){let l=document.createElement("div");if(l.className="lb-bundle__product",l.setAttribute("part","product"),s.featuredImage){let p=document.createElement("img");p.src=s.featuredImage.url,p.alt=s.featuredImage.altText??s.title,p.className="lb-bundle__product-image",p.loading="lazy",l.appendChild(p)}let d=document.createElement("div");d.className="lb-bundle__product-info",d.innerHTML=`
110
- <p class="lb-bundle__product-title">${G(s.title)}</p>
111
- <p class="lb-bundle__product-price">${G(C(s.priceRange.minVariantPrice.amount,a))}</p>
112
- `,l.appendChild(d),o.appendChild(l)}t.appendChild(o);let i=document.createElement("button");i.className="lb-bundle__cta",i.textContent=e.widgetConfig.ctaText??"Add Bundle to Cart",i.setAttribute("part","button"),i.addEventListener("click",async()=>{i.disabled=!0,i.textContent="Adding...";let s=e.products.filter(d=>d.variants.nodes.some(p=>p.availableForSale)).map(d=>({variantId:d.variants.nodes.find(c=>c.availableForSale).id,quantity:1})),l=await n(s);if(i.disabled=!1,i.textContent=e.widgetConfig.ctaText??"Add Bundle to Cart",!l.success){let d=document.createElement("p");d.className="lb-bundle__error",d.textContent=l.error??"Failed to add to cart",t.appendChild(d),setTimeout(()=>d.remove(),5e3)}}),t.appendChild(i)}function G(t){let e=document.createElement("div");return e.textContent=t,e.innerHTML}function Q(t,e,n){let a=e.products[0]?.priceRange.minVariantPrice.currencyCode??"USD",r=new Map,o=document.createElement("h3");o.className="lb-bundle__title",o.textContent=e.title,o.setAttribute("part","title"),t.appendChild(o);let i=document.createElement("p");i.className="lb-bundle__instructions",i.textContent=e.minQuantity&&e.maxQuantity?`Select ${e.minQuantity}\u2013${e.maxQuantity} items`:e.minQuantity?`Select at least ${e.minQuantity} items`:"Select your items",t.appendChild(i);let s=document.createElement("div");s.className="lb-bundle__products lb-bundle__products--selectable";for(let c of e.products){let f=c.variants.nodes.find(m=>m.availableForSale)??c.variants.nodes[0];if(!f)continue;let u=document.createElement("div");if(u.className="lb-bundle__product lb-bundle__product--selectable",c.featuredImage){let m=document.createElement("img");m.src=c.featuredImage.url,m.alt=c.featuredImage.altText??c.title,m.className="lb-bundle__product-image",m.loading="lazy",u.appendChild(m)}let g=document.createElement("div");g.className="lb-bundle__product-info",g.innerHTML=`
113
- <p class="lb-bundle__product-title">${z(c.title)}</p>
114
- <p class="lb-bundle__product-price">${z(C(f.price.amount,a))}</p>
115
- `,u.appendChild(g);let b=document.createElement("button");b.className="lb-bundle__select-btn",b.textContent=f.availableForSale?"Select":"Sold out",b.disabled=!f.availableForSale,b.addEventListener("click",()=>{let m=c.id;r.has(m)?(r.delete(m),u.classList.remove("lb-bundle__product--selected"),b.textContent="Select"):(r.set(m,{variantId:f.id,quantity:1}),u.classList.add("lb-bundle__product--selected"),b.textContent="Selected"),p()}),u.appendChild(b),s.appendChild(u)}t.appendChild(s);let l=document.createElement("p");l.className="lb-bundle__validation",t.appendChild(l);let d=document.createElement("button");d.className="lb-bundle__cta",d.setAttribute("part","button"),d.disabled=!0,t.appendChild(d);function p(){let c=Array.from(r.values()).reduce((u,g)=>u+g.quantity,0),f=q(c,e.minQuantity,e.maxQuantity);d.disabled=!f.valid,d.textContent=e.widgetConfig.ctaText??`Add ${c} Items to Cart`,l.textContent=f.message??""}p(),d.addEventListener("click",async()=>{d.disabled=!0,d.textContent="Adding...";let c=Array.from(r.entries()).map(([,u])=>({variantId:u.variantId,quantity:u.quantity})),f=await n(c);if(p(),!f.success){let u=document.createElement("p");u.className="lb-bundle__error",u.textContent=f.error??"Failed to add to cart",t.appendChild(u),setTimeout(()=>u.remove(),5e3)}})}function z(t){let e=document.createElement("div");return e.textContent=t,e.innerHTML}function J(t,e,n){let a=e.products[0];if(!a)return;let r=parseFloat(a.priceRange.minVariantPrice.amount),o=a.priceRange.minVariantPrice.currencyCode,i=1,s=document.createElement("h3");s.className="lb-bundle__title",s.textContent=e.title,s.setAttribute("part","title"),t.appendChild(s);let l=document.createElement("div");if(l.className="lb-bundle__product lb-bundle__product--volume",a.featuredImage){let h=document.createElement("img");h.src=a.featuredImage.url,h.alt=a.featuredImage.altText??a.title,h.className="lb-bundle__product-image",h.loading="lazy",l.appendChild(h)}let d=document.createElement("div");d.className="lb-bundle__product-info",d.innerHTML=`
116
- <p class="lb-bundle__product-title">${I(a.title)}</p>
117
- <p class="lb-bundle__product-price">${I(C(r,o))} each</p>
118
- `,l.appendChild(d),t.appendChild(l);let p=document.createElement("div");p.className="lb-bundle__tiers",p.setAttribute("role","table"),p.setAttribute("aria-label","Volume discounts"),t.appendChild(p);let c=document.createElement("div");c.className="lb-bundle__quantity-selector";let f=document.createElement("label");f.textContent="Quantity",c.appendChild(f);let u=document.createElement("div");u.className="lb-bundle__quantity-control";let g=document.createElement("button");g.textContent="\u2212",g.setAttribute("aria-label","Decrease quantity");let b=document.createElement("input");b.type="number",b.min="1",b.value="1",b.className="lb-bundle__quantity-input";let m=document.createElement("button");m.textContent="+",m.setAttribute("aria-label","Increase quantity"),u.append(g,b,m),c.appendChild(u),t.appendChild(c);let v=document.createElement("button");v.className="lb-bundle__cta",v.setAttribute("part","button"),t.appendChild(v);function T(){let h=B(e.volumeTiers,r,i);p.innerHTML="";for(let y of h){let _=document.createElement("div");_.className=`lb-bundle__tier${y.isActive?" lb-bundle__tier--active":""}`,_.setAttribute("role","row"),_.innerHTML=`
119
- <span class="lb-bundle__tier-quantity" role="cell">${y.tier.minQuantity}+ items</span>
120
- <span class="lb-bundle__tier-price" role="cell">${I(C(y.unitPrice,o))} each</span>
121
- <span class="lb-bundle__tier-savings" role="cell">Save ${y.savingsPercent.toFixed(0)}%</span>
122
- ${y.tier.label?`<span class="lb-bundle__tier-label" role="cell">${I(y.tier.label)}</span>`:""}
123
- `,p.appendChild(_)}v.textContent=e.widgetConfig.ctaText??`Add ${i} to Cart`}T(),g.addEventListener("click",()=>{i>1&&(i--,b.value=String(i),T())}),m.addEventListener("click",()=>{i++,b.value=String(i),T()}),b.addEventListener("change",()=>{let h=parseInt(b.value,10);!isNaN(h)&&h>0&&(i=h,T())}),v.addEventListener("click",async()=>{let h=a.variants.nodes.find(x=>x.availableForSale);if(!h)return;v.disabled=!0,v.textContent="Adding...";let y=[{variantId:h.id,quantity:i}],_=await n(y);if(v.disabled=!1,T(),!_.success){let x=document.createElement("p");x.className="lb-bundle__error",x.textContent=_.error??"Failed to add to cart",t.appendChild(x),setTimeout(()=>x.remove(),5e3)}})}function I(t){let e=document.createElement("div");return e.textContent=t,e.innerHTML}var w=`
93
+ `,y=class extends Error{constructor(n,e){super(n),this.reason=e,this.name="BundleParseError"}},te=new Set(["fixed","mix_match","volume"]),ne=new Set(["active"]);function $(n,e){try{return re(n,e)}catch(t){if(t instanceof y)return null;throw t}}function re(n,e){let t=new Map(e.map(c=>[c.key,c])),i=t.get("title")?.value??"Bundle",r=t.get("bundle_type")?.value,o=t.get("status")?.value;if(!r||!te.has(r))throw new y(`Invalid or missing bundle_type: ${r??"null"}`,"invalid_type");let s=r;if(!o||!ne.has(o))throw new y(`Bundle is not active: status=${o??"null"}`,"inactive");let a=o,l=t.get("starts_at")?.value??null,d=t.get("ends_at")?.value??null,u=new Date;if(l){let c=new Date(l);if(Number.isNaN(c.getTime()))throw new y(`Invalid starts_at: ${l}`,"invalid_type");if(c>u)throw new y(`Bundle not yet started: starts_at=${l}`,"not_started")}if(d){let c=new Date(d);if(Number.isNaN(c.getTime()))throw new y(`Invalid ends_at: ${d}`,"invalid_type");if(c<u)throw new y(`Bundle has expired: ends_at=${d}`,"expired")}let p=ie(t),b=ae(t),f=oe(t),h={id:n,title:i,status:a,products:p,discountConfig:b,widgetConfig:f,startsAt:l,endsAt:d,discountLabel:t.get("discount_label")?.value??null,abTestId:t.get("ab_test_id")?.value??null,abTestConfig:k(t,"ab_test_config")};switch(s){case"fixed":return{...h,bundleType:"fixed"};case"volume":return{...h,bundleType:"volume",volumeTiers:se(t)};case"mix_match":return{...h,bundleType:"mix_match",minQuantity:B(t,"min_quantity"),maxQuantity:B(t,"max_quantity")}}}function ie(n){let e=[],t=n.get("products");if(t?.reference&&"variants"in t.reference&&e.push(t.reference),t?.references?.nodes)for(let r of t.references.nodes)"variants"in r?e.push(r):"products"in r&&r.products?.nodes&&e.push(...r.products.nodes);let i=n.get("collection");if(i?.references?.nodes)for(let r of i.references.nodes)"products"in r&&r.products?.nodes&&e.push(...r.products.nodes);return e}function ae(n){return{discountType:n.get("discount_type")?.value??"percentage",discountValue:parseFloat(n.get("discount_value")?.value??"0"),allowStacking:n.get("allow_stacking")?.value==="true"}}function oe(n){let e=k(n,"widget_config");if(e&&typeof e=="object"){let t=e;return{primaryColor:t.primaryColor??null,ctaText:t.ctaText??null,outOfStockBehavior:t.outOfStockBehavior??"hide"}}return{primaryColor:null,ctaText:null,outOfStockBehavior:"hide"}}function se(n){let e=k(n,"volume_tiers");return Array.isArray(e)?e.filter(t=>typeof t=="object"&&t!==null).map(t=>({minQuantity:Number(t.minQuantity??0),discountType:t.discountType??"percentage",discountValue:Number(t.discountValue??0),label:t.label??null})):[]}function k(n,e){let t=n.get(e)?.value;if(!t)return null;try{return JSON.parse(t)}catch{return null}}function B(n,e){let t=n.get(e)?.value;if(!t)return null;let i=parseInt(t,10);return isNaN(i)?null:i}function P(n,e,t){return[...n].sort((r,o)=>r.minQuantity-o.minQuantity).map(r=>{let o=r.discountType==="percentage"?e*(r.discountValue/100):r.discountValue,s=Math.max(0,e-o),a=e-s,l=e>0?a/e*100:0,d=t>=r.minQuantity;return{tier:r,unitPrice:s,savings:a,savingsPercent:l,isActive:d}})}function q(n,e,t){return e!==null&&n<e?{valid:!1,totalQuantity:n,message:`Select at least ${e} item${e!==1?"s":""}`}:t!==null&&n>t?{valid:!1,totalQuantity:n,message:`Select at most ${t} item${t!==1?"s":""}`}:{valid:!0,totalQuantity:n,message:null}}async function F(n,e){await U(n,{shopDomain:n.shopDomain,eventType:"bundle_impression",bundleGid:e.bundleGid,bundleType:e.bundleType,productId:e.productId,abTestId:e.abTestId,abVariant:e.abVariant,occurredAt:new Date().toISOString()})}async function O(n,e){await U(n,{shopDomain:n.shopDomain,eventType:"bundle_add_to_cart",...e,occurredAt:new Date().toISOString()})}function V(n,e){if(typeof IntersectionObserver>"u")return e(),()=>{};let t=new IntersectionObserver(i=>{for(let r of i)r.isIntersecting&&(e(),t.unobserve(r.target))},{threshold:.5});return t.observe(n),()=>t.disconnect()}async function U(n,e){let t=`${n.appUrl}/api/analytics`,i=JSON.stringify(e);try{(await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:i})).ok||(await new Promise(o=>setTimeout(o,2e3)),await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:i}))}catch{try{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(t,i)}catch{}}}var fe=720*60*60;var D=1e4,le=[[/@import/i,"@import rules"],[/@charset/i,"@charset declarations"],[/expression\s*\(/i,"CSS expressions"],[/-moz-binding/i,"-moz-binding"],[/-webkit-binding/i,"-webkit-binding"],[/behavior\s*:/i,"behavior property"]],de=/^(https:|\/[^/]|\.\/|\.\.\/|#)/;function ce(n){if(n.length>D)return{ok:!1,error:`CSS exceeds ${D.toLocaleString("en-US")} character limit`};let e=n.replace(/\/\*[\s\S]*?\*\//g,"");try{e=e.replace(/\\([0-9a-fA-F]{1,6})[ \t\n\f]?/g,(r,o)=>{let s=parseInt(o,16);return s<0||s>1114111?"\uFFFD":String.fromCodePoint(s)}),e=e.replace(/\\\r?\n/g,""),e=e.replace(/\\([^\n\r\f0-9a-fA-F])/g,"$1")}catch{return{ok:!1,error:"CSS contains invalid unicode escape sequences"}}e=e.replace(/</g,"").replace(/>/g,"");for(let[r,o]of le)if(r.test(e))return{ok:!1,error:`CSS contains blocked pattern: ${o}`};let t=/url\s*\(\s*([\s\S]*?)\s*\)/gi,i;for(;(i=t.exec(e))!==null;){let r=i[1].trim();if((r.startsWith("'")||r.startsWith('"'))&&(r=r.slice(1)),(r.endsWith("'")||r.endsWith('"'))&&(r=r.slice(0,-1)),r=r.trim(),r&&!de.test(r))return{ok:!1,error:"CSS url() values must use https:// or relative paths"}}return{ok:!0,css:e}}var ue="lb-custom-css-";function j(n,e){if(typeof document>"u"||!e)return!1;let t=ce(e);if(!t.ok||!t.css.trim())return!1;let i=ue+pe(n),r=document.getElementById(i);return r||(r=document.createElement("style"),r.id=i,r.setAttribute("data-lime-bundles","custom-css"),document.head.appendChild(r)),r.textContent!==t.css&&(r.textContent=t.css),!0}function pe(n){let e=2166136261;for(let t=0;t<n.length;t++)e^=n.charCodeAt(t),e=Math.imul(e,16777619);return(e>>>0).toString(16)}function _(n,e){let t=typeof n=="string"?parseFloat(n):n;try{return new Intl.NumberFormat(void 0,{style:"currency",currency:e}).format(t)}catch{return`${e} ${t.toFixed(2)}`}}function H(n,e,t){let i=e.products[0]?.priceRange.minVariantPrice.currencyCode??"USD",r=document.createElement("h3");if(r.className="lb-bundle__title",r.textContent=e.title,r.setAttribute("part","title"),n.appendChild(r),e.discountLabel){let a=document.createElement("span");a.className="lb-bundle__discount-badge",a.textContent=e.discountLabel,n.appendChild(a)}let o=document.createElement("div");o.className="lb-bundle__products";for(let a of e.products){let l=document.createElement("div");if(l.className="lb-bundle__product",l.setAttribute("part","product"),a.featuredImage){let u=document.createElement("img");u.src=a.featuredImage.url,u.alt=a.featuredImage.altText??a.title,u.className="lb-bundle__product-image",u.loading="lazy",l.appendChild(u)}let d=document.createElement("div");d.className="lb-bundle__product-info",d.innerHTML=`
94
+ <p class="lb-bundle__product-title">${R(a.title)}</p>
95
+ <p class="lb-bundle__product-price">${R(_(a.priceRange.minVariantPrice.amount,i))}</p>
96
+ `,l.appendChild(d),o.appendChild(l)}n.appendChild(o);let s=document.createElement("button");s.className="lb-bundle__cta",s.textContent=e.widgetConfig.ctaText??"Add Bundle to Cart",s.setAttribute("part","button"),s.addEventListener("click",()=>{let a=e.products.filter(l=>l.variants.nodes.some(d=>d.availableForSale)).map(l=>({merchandiseId:l.variants.nodes.find(u=>u.availableForSale).id,quantity:1,attributes:[{key:"_lime_bundle_gid",value:e.id},{key:"_lime_bundle_type",value:e.bundleType}]}));a.length!==0&&t(a)}),n.appendChild(s)}function R(n){let e=document.createElement("div");return e.textContent=n,e.innerHTML}function Q(n,e,t){let i=e.products[0]?.priceRange.minVariantPrice.currencyCode??"USD",r=new Map,o=document.createElement("h3");o.className="lb-bundle__title",o.textContent=e.title,o.setAttribute("part","title"),n.appendChild(o);let s=document.createElement("p");s.className="lb-bundle__instructions",s.textContent=e.minQuantity&&e.maxQuantity?`Select ${e.minQuantity}\u2013${e.maxQuantity} items`:e.minQuantity?`Select at least ${e.minQuantity} items`:"Select your items",n.appendChild(s);let a=document.createElement("div");a.className="lb-bundle__products lb-bundle__products--selectable";for(let p of e.products){let b=p.variants.nodes.find(m=>m.availableForSale)??p.variants.nodes[0];if(!b)continue;let f=document.createElement("div");if(f.className="lb-bundle__product lb-bundle__product--selectable",p.featuredImage){let m=document.createElement("img");m.src=p.featuredImage.url,m.alt=p.featuredImage.altText??p.title,m.className="lb-bundle__product-image",m.loading="lazy",f.appendChild(m)}let h=document.createElement("div");h.className="lb-bundle__product-info",h.innerHTML=`
97
+ <p class="lb-bundle__product-title">${z(p.title)}</p>
98
+ <p class="lb-bundle__product-price">${z(_(b.price.amount,i))}</p>
99
+ `,f.appendChild(h);let c=document.createElement("button");c.className="lb-bundle__select-btn",c.textContent=b.availableForSale?"Select":"Sold out",c.disabled=!b.availableForSale,c.addEventListener("click",()=>{let m=p.id;r.has(m)?(r.delete(m),f.classList.remove("lb-bundle__product--selected"),c.textContent="Select"):(r.set(m,{variantId:b.id,quantity:1}),f.classList.add("lb-bundle__product--selected"),c.textContent="Selected"),u()}),f.appendChild(c),a.appendChild(f)}n.appendChild(a);let l=document.createElement("p");l.className="lb-bundle__validation",n.appendChild(l);let d=document.createElement("button");d.className="lb-bundle__cta",d.setAttribute("part","button"),d.disabled=!0,n.appendChild(d);function u(){let p=Array.from(r.values()).reduce((f,h)=>f+h.quantity,0),b=q(p,e.minQuantity,e.maxQuantity);d.disabled=!b.valid,d.textContent=e.widgetConfig.ctaText??`Add ${p} Items to Cart`,l.textContent=b.message??""}u(),d.addEventListener("click",()=>{let p=Array.from(r.values()).map(b=>({merchandiseId:b.variantId,quantity:b.quantity,attributes:[{key:"_lime_bundle_gid",value:e.id},{key:"_lime_bundle_type",value:e.bundleType}]}));p.length!==0&&t(p)})}function z(n){let e=document.createElement("div");return e.textContent=n,e.innerHTML}function G(n,e,t){let i=e.products[0];if(!i)return;let r=parseFloat(i.priceRange.minVariantPrice.amount),o=i.priceRange.minVariantPrice.currencyCode,s=1,a=document.createElement("h3");a.className="lb-bundle__title",a.textContent=e.title,a.setAttribute("part","title"),n.appendChild(a);let l=document.createElement("div");if(l.className="lb-bundle__product lb-bundle__product--volume",i.featuredImage){let g=document.createElement("img");g.src=i.featuredImage.url,g.alt=i.featuredImage.altText??i.title,g.className="lb-bundle__product-image",g.loading="lazy",l.appendChild(g)}let d=document.createElement("div");d.className="lb-bundle__product-info",d.innerHTML=`
100
+ <p class="lb-bundle__product-title">${T(i.title)}</p>
101
+ <p class="lb-bundle__product-price">${T(_(r,o))} each</p>
102
+ `,l.appendChild(d),n.appendChild(l);let u=document.createElement("div");u.className="lb-bundle__tiers",u.setAttribute("role","table"),u.setAttribute("aria-label","Volume discounts"),n.appendChild(u);let p=document.createElement("div");p.className="lb-bundle__quantity-selector";let b=document.createElement("label");b.textContent="Quantity",p.appendChild(b);let f=document.createElement("div");f.className="lb-bundle__quantity-control";let h=document.createElement("button");h.textContent="\u2212",h.setAttribute("aria-label","Decrease quantity");let c=document.createElement("input");c.type="number",c.min="1",c.value="1",c.className="lb-bundle__quantity-input";let m=document.createElement("button");m.textContent="+",m.setAttribute("aria-label","Increase quantity"),f.append(h,c,m),p.appendChild(f),n.appendChild(p);let x=document.createElement("button");x.className="lb-bundle__cta",x.setAttribute("part","button"),n.appendChild(x);function E(){let g=P(e.volumeTiers,r,s);u.innerHTML="";for(let v of g){let S=document.createElement("div");S.className=`lb-bundle__tier${v.isActive?" lb-bundle__tier--active":""}`,S.setAttribute("role","row"),S.innerHTML=`
103
+ <span class="lb-bundle__tier-quantity" role="cell">${v.tier.minQuantity}+ items</span>
104
+ <span class="lb-bundle__tier-price" role="cell">${T(_(v.unitPrice,o))} each</span>
105
+ <span class="lb-bundle__tier-savings" role="cell">Save ${v.savingsPercent.toFixed(0)}%</span>
106
+ ${v.tier.label?`<span class="lb-bundle__tier-label" role="cell">${T(v.tier.label)}</span>`:""}
107
+ `,u.appendChild(S)}x.textContent=e.widgetConfig.ctaText??`Add ${s} to Cart`}E(),h.addEventListener("click",()=>{s>1&&(s--,c.value=String(s),E())}),m.addEventListener("click",()=>{s++,c.value=String(s),E()}),c.addEventListener("change",()=>{let g=parseInt(c.value,10);!isNaN(g)&&g>0&&(s=g,E())}),x.addEventListener("click",()=>{let g=i.variants.nodes.find(v=>v.availableForSale);g&&t([{merchandiseId:g.id,quantity:s,attributes:[{key:"_lime_bundle_gid",value:e.id},{key:"_lime_bundle_type",value:e.bundleType}]}])})}function T(n){let e=document.createElement("div");return e.textContent=n,e.innerHTML}var w=`
124
108
  :host {
125
109
  display: block;
126
110
  --lb-primary-color: #000;
@@ -183,11 +167,11 @@
183
167
  .lb-skeleton--title { height: 24px; width: 60%; margin-bottom: var(--lb-spacing-md); }
184
168
  .lb-skeleton--products { height: 200px; }
185
169
  @keyframes lb-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
186
- `;var E=class extends HTMLElement{static observedAttributes=["shop-domain","storefront-token","bundle-gid","cart-id","app-url","analytics","locale"];shadow;bundle=null;abortController=null;impressionCleanup=null;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.fetchBundle()}disconnectedCallback(){this.abortController?.abort(),this.teardownImpression()}teardownImpression(){this.impressionCleanup?.(),this.impressionCleanup=null}attributeChangedCallback(e,n,a){n===a||!this.isConnected||(e==="bundle-gid"||e==="shop-domain"||e==="storefront-token")&&this.shopDomain&&this.storefrontToken&&this.bundleGid&&this.fetchBundle()}get shopDomain(){return this.getAttribute("shop-domain")??""}get storefrontToken(){return this.getAttribute("storefront-token")??""}get bundleGid(){return this.getAttribute("bundle-gid")??""}get cartId(){return this.getAttribute("cart-id")??void 0}get appUrl(){return this.getAttribute("app-url")??""}get analyticsEnabled(){return this.getAttribute("analytics")!=="false"}async fetchBundle(){if(!this.shopDomain||!this.storefrontToken||!this.bundleGid){this.renderError("Missing required attributes: shop-domain, storefront-token, bundle-gid");return}this.abortController?.abort();let e=new AbortController;this.abortController=e,this.renderLoading();try{let a=await S({shopDomain:this.shopDomain,accessToken:this.storefrontToken}).query(D,{id:this.bundleGid});if(e.signal.aborted)return;if(!a.metaobject){this.bundle=null,this.teardownImpression(),this.renderError("Bundle not found");return}if(this.bundle=$(a.metaobject.id,a.metaobject.fields),!this.bundle){this.teardownImpression(),this.renderError("Bundle is not active or has expired");return}this.renderBundle(),this.setupImpression()}catch(n){if(e.signal.aborted)return;this.bundle=null,this.teardownImpression(),this.renderError(n instanceof Error?n.message:"Failed to load bundle")}}renderBundle(){if(!this.bundle)return;let e=document.createElement("div");e.className="lb-bundle",e.setAttribute("role","region"),e.setAttribute("aria-label",this.bundle.title);let n=async r=>{let i=M()==="ajax"?O(this.bundleGid,this.bundle.bundleType):j(S({shopDomain:this.shopDomain,accessToken:this.storefrontToken}),this.bundleGid,this.bundle.bundleType,this.cartId),s;try{s=await i.addLines(r)}catch(l){s={success:!1,error:l instanceof Error?l.message:"Cart add failed"}}if(s.success&&(this.dispatchEvent(new CustomEvent("lime-bundle:add-to-cart",{detail:{items:r,cartId:s.cartId},bubbles:!0,composed:!0})),this.analyticsEnabled&&this.appUrl)){let l=r.reduce((p,c)=>p+c.quantity,0),d=r.reduce((p,c)=>{let u=this.bundle.products.find(b=>b.variants.nodes.some(m=>m.id===c.variantId))?.variants.nodes.find(b=>b.id===c.variantId),g=u?parseFloat(u.price.amount):0;return p+g*c.quantity},0);F({shopDomain:this.shopDomain,appUrl:this.appUrl},{bundleGid:this.bundleGid,bundleType:this.bundle.bundleType,productId:this.bundle.products[0]?.id??"",quantity:l,totalPrice:Math.round(d*100)/100})}return s};switch(this.bundle.bundleType){case"fixed":H(e,this.bundle,n);break;case"mix_match":Q(e,this.bundle,n);break;case"volume":J(e,this.bundle,n);break}this.shadow.innerHTML="";let a=document.createElement("style");a.textContent=w,this.shadow.appendChild(a),this.shadow.appendChild(e),this.dispatchEvent(new CustomEvent("lime-bundle:loaded",{detail:{bundleType:this.bundle.bundleType,title:this.bundle.title},bubbles:!0,composed:!0}))}setupImpression(){!this.analyticsEnabled||!this.bundle||!this.appUrl||(this.impressionCleanup?.(),this.impressionCleanup=R(this,()=>{U({shopDomain:this.shopDomain,appUrl:this.appUrl},{bundleGid:this.bundleGid,bundleType:this.bundle.bundleType})}))}renderLoading(){this.shadow.innerHTML=`
170
+ `;var C=class extends HTMLElement{static observedAttributes=["shop-domain","storefront-token","bundle-gid","app-url","analytics","locale"];shadow;bundle=null;abortController=null;impressionCleanup=null;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.fetchBundle()}disconnectedCallback(){this.abortController?.abort(),this.teardownImpression()}teardownImpression(){this.impressionCleanup?.(),this.impressionCleanup=null}attributeChangedCallback(e,t,i){t===i||!this.isConnected||(e==="bundle-gid"||e==="shop-domain"||e==="storefront-token")&&this.shopDomain&&this.storefrontToken&&this.bundleGid&&this.fetchBundle()}get shopDomain(){return this.getAttribute("shop-domain")??""}get storefrontToken(){return this.getAttribute("storefront-token")??""}get bundleGid(){return this.getAttribute("bundle-gid")??""}get appUrl(){return this.getAttribute("app-url")??""}get analyticsEnabled(){return this.getAttribute("analytics")!=="false"}async fetchBundle(){if(!this.shopDomain||!this.storefrontToken||!this.bundleGid){this.renderError("Missing required attributes: shop-domain, storefront-token, bundle-gid");return}this.abortController?.abort();let e=new AbortController;this.abortController=e,this.renderLoading();try{let t=L({shopDomain:this.shopDomain,accessToken:this.storefrontToken}),[i,r]=await Promise.all([t.query(M,{id:this.bundleGid},{signal:e.signal}),t.query(N,void 0,{signal:e.signal}).catch(()=>null)]);if(e.signal.aborted)return;if(!i.metaobject){this.bundle=null,this.teardownImpression(),this.renderError("Bundle not found");return}if(this.bundle=$(i.metaobject.id,i.metaobject.fields),!this.bundle){this.teardownImpression(),this.renderError("Bundle is not active or has expired");return}r?.shop?.metafield?.value&&j(this.shopDomain,r.shop.metafield.value),this.renderBundle(),this.setupImpression()}catch(t){if(e.signal.aborted)return;this.bundle=null,this.teardownImpression(),this.renderError(t instanceof Error?t.message:"Failed to load bundle")}}dispatchAddToCart=e=>{if(this.dispatchEvent(new CustomEvent("lime-bundle:add-to-cart",{detail:{lines:e},bubbles:!0,composed:!0})),this.analyticsEnabled&&this.appUrl&&this.bundle){let t=e.reduce((r,o)=>r+o.quantity,0),i=e.reduce((r,o)=>{let a=this.bundle.products.find(d=>d.variants.nodes.some(u=>u.id===o.merchandiseId))?.variants.nodes.find(d=>d.id===o.merchandiseId),l=a?parseFloat(a.price.amount):0;return r+l*o.quantity},0);O({shopDomain:this.shopDomain,appUrl:this.appUrl},{bundleGid:this.bundleGid,bundleType:this.bundle.bundleType,productId:this.bundle.products[0]?.id??"",quantity:t,totalPrice:Math.round(i*100)/100})}};renderBundle(){if(!this.bundle)return;let e=document.createElement("div");switch(e.className="lb-bundle",e.setAttribute("role","region"),e.setAttribute("aria-label",this.bundle.title),this.bundle.bundleType){case"fixed":H(e,this.bundle,this.dispatchAddToCart);break;case"mix_match":Q(e,this.bundle,this.dispatchAddToCart);break;case"volume":G(e,this.bundle,this.dispatchAddToCart);break}this.shadow.innerHTML="";let t=document.createElement("style");t.textContent=w,this.shadow.appendChild(t),this.shadow.appendChild(e),this.dispatchEvent(new CustomEvent("lime-bundle:loaded",{detail:{bundleType:this.bundle.bundleType,title:this.bundle.title},bubbles:!0,composed:!0}))}setupImpression(){!this.analyticsEnabled||!this.bundle||!this.appUrl||(this.impressionCleanup?.(),this.impressionCleanup=V(this,()=>{F({shopDomain:this.shopDomain,appUrl:this.appUrl},{bundleGid:this.bundleGid,bundleType:this.bundle.bundleType})}))}renderLoading(){this.shadow.innerHTML=`
187
171
  <style>${w}</style>
188
172
  <div class="lb-bundle lb-bundle--loading">
189
173
  <div class="lb-skeleton lb-skeleton--title"></div>
190
174
  <div class="lb-skeleton lb-skeleton--products"></div>
191
175
  </div>
192
- `}renderError(e){this.shadow.innerHTML="",this.dispatchEvent(new CustomEvent("lime-bundle:error",{detail:{message:e,code:"LOAD_ERROR"},bubbles:!0,composed:!0}))}render(){this.shadow.innerHTML=`<style>${w}</style>`}};var W=new Set;function Y(t){let e=t.appUrl??`https://${t.shopDomain}`,n=new Map;for(let a of t.lineItems){if(!a.bundleGid)continue;let r=n.get(a.bundleGid);r?(r.revenue+=a.price*a.quantity,r.lineItemCount+=1):n.set(a.bundleGid,{bundleType:a.bundleType,revenue:a.price*a.quantity,lineItemCount:1})}for(let[a,r]of n){let o=`${t.orderId}:${a}`;if(W.has(o))continue;W.add(o);let i={shopDomain:t.shopDomain,eventType:"bundle_purchased",bundleGid:a,bundleType:r.bundleType,orderId:t.orderId,revenue:Math.round(r.revenue*100)/100,lineItemCount:r.lineItemCount,occurredAt:new Date().toISOString()},s=`${e}/api/analytics`,l=JSON.stringify(i);try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:l}).catch(()=>{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(s,l)})}catch{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(s,l)}}}typeof customElements<"u"&&!customElements.get("lime-bundle")&&customElements.define("lime-bundle",E);return ne(be);})();
176
+ `}renderError(e){this.shadow.innerHTML="",this.dispatchEvent(new CustomEvent("lime-bundle:error",{detail:{message:e,code:"LOAD_ERROR"},bubbles:!0,composed:!0}))}render(){this.shadow.innerHTML=`<style>${w}</style>`}};typeof customElements<"u"&&!customElements.get("lime-bundle")&&customElements.define("lime-bundle",C);return Z(be);})();
193
177
  //# sourceMappingURL=lime-bundle.global.js.map