@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.cjs CHANGED
@@ -20,8 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- LimeBundleElement: () => LimeBundleElement,
24
- trackPurchase: () => trackPurchase
23
+ LimeBundleElement: () => LimeBundleElement
25
24
  });
26
25
  module.exports = __toCommonJS(index_exports);
27
26
 
@@ -30,7 +29,7 @@ var import_core4 = require("@lime-bundles/core");
30
29
 
31
30
  // src/renderers/fixed.ts
32
31
  var import_core = require("@lime-bundles/core");
33
- function renderFixedBundle(container, bundle, addToCart) {
32
+ function renderFixedBundle(container, bundle, onAddToCart) {
34
33
  const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
35
34
  const title = document.createElement("h3");
36
35
  title.className = "lb-bundle__title";
@@ -71,23 +70,20 @@ function renderFixedBundle(container, bundle, addToCart) {
71
70
  button.className = "lb-bundle__cta";
72
71
  button.textContent = bundle.widgetConfig.ctaText ?? "Add Bundle to Cart";
73
72
  button.setAttribute("part", "button");
74
- button.addEventListener("click", async () => {
75
- button.disabled = true;
76
- button.textContent = "Adding...";
77
- const items = bundle.products.filter((p) => p.variants.nodes.some((v) => v.availableForSale)).map((p) => {
73
+ button.addEventListener("click", () => {
74
+ const lines = bundle.products.filter((p) => p.variants.nodes.some((v) => v.availableForSale)).map((p) => {
78
75
  const variant = p.variants.nodes.find((v) => v.availableForSale);
79
- return { variantId: variant.id, quantity: 1 };
76
+ return {
77
+ merchandiseId: variant.id,
78
+ quantity: 1,
79
+ attributes: [
80
+ { key: "_lime_bundle_gid", value: bundle.id },
81
+ { key: "_lime_bundle_type", value: bundle.bundleType }
82
+ ]
83
+ };
80
84
  });
81
- const result = await addToCart(items);
82
- button.disabled = false;
83
- button.textContent = bundle.widgetConfig.ctaText ?? "Add Bundle to Cart";
84
- if (!result.success) {
85
- const error = document.createElement("p");
86
- error.className = "lb-bundle__error";
87
- error.textContent = result.error ?? "Failed to add to cart";
88
- container.appendChild(error);
89
- setTimeout(() => error.remove(), 5e3);
90
- }
85
+ if (lines.length === 0) return;
86
+ onAddToCart(lines);
91
87
  });
92
88
  container.appendChild(button);
93
89
  }
@@ -99,7 +95,7 @@ function escapeHtml(str) {
99
95
 
100
96
  // src/renderers/mix-match.ts
101
97
  var import_core2 = require("@lime-bundles/core");
102
- function renderMixMatchBundle(container, bundle, addToCart) {
98
+ function renderMixMatchBundle(container, bundle, onAddToCart) {
103
99
  const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
104
100
  const selections = /* @__PURE__ */ new Map();
105
101
  const title = document.createElement("h3");
@@ -163,29 +159,31 @@ function renderMixMatchBundle(container, bundle, addToCart) {
163
159
  button.disabled = true;
164
160
  container.appendChild(button);
165
161
  function updateCta() {
166
- const total = Array.from(selections.values()).reduce((s, v) => s + v.quantity, 0);
167
- const validation = (0, import_core2.validateQuantity)(total, bundle.minQuantity, bundle.maxQuantity);
162
+ const total = Array.from(selections.values()).reduce(
163
+ (s, v) => s + v.quantity,
164
+ 0
165
+ );
166
+ const validation = (0, import_core2.validateQuantity)(
167
+ total,
168
+ bundle.minQuantity,
169
+ bundle.maxQuantity
170
+ );
168
171
  button.disabled = !validation.valid;
169
172
  button.textContent = bundle.widgetConfig.ctaText ?? `Add ${total} Items to Cart`;
170
173
  validationEl.textContent = validation.message ?? "";
171
174
  }
172
175
  updateCta();
173
- button.addEventListener("click", async () => {
174
- button.disabled = true;
175
- button.textContent = "Adding...";
176
- const items = Array.from(selections.entries()).map(([, s]) => ({
177
- variantId: s.variantId,
178
- quantity: s.quantity
176
+ button.addEventListener("click", () => {
177
+ const lines = Array.from(selections.values()).map((s) => ({
178
+ merchandiseId: s.variantId,
179
+ quantity: s.quantity,
180
+ attributes: [
181
+ { key: "_lime_bundle_gid", value: bundle.id },
182
+ { key: "_lime_bundle_type", value: bundle.bundleType }
183
+ ]
179
184
  }));
180
- const result = await addToCart(items);
181
- updateCta();
182
- if (!result.success) {
183
- const error = document.createElement("p");
184
- error.className = "lb-bundle__error";
185
- error.textContent = result.error ?? "Failed to add to cart";
186
- container.appendChild(error);
187
- setTimeout(() => error.remove(), 5e3);
188
- }
185
+ if (lines.length === 0) return;
186
+ onAddToCart(lines);
189
187
  });
190
188
  }
191
189
  function escapeHtml2(str) {
@@ -196,7 +194,7 @@ function escapeHtml2(str) {
196
194
 
197
195
  // src/renderers/volume.ts
198
196
  var import_core3 = require("@lime-bundles/core");
199
- function renderVolumeBundle(container, bundle, addToCart) {
197
+ function renderVolumeBundle(container, bundle, onAddToCart) {
200
198
  const product = bundle.products[0];
201
199
  if (!product) return;
202
200
  const basePrice = parseFloat(product.priceRange.minVariantPrice.amount);
@@ -256,7 +254,11 @@ function renderVolumeBundle(container, bundle, addToCart) {
256
254
  button.setAttribute("part", "button");
257
255
  container.appendChild(button);
258
256
  function updateTiers() {
259
- const savings = (0, import_core3.calculateTierSavings)(bundle.volumeTiers, basePrice, quantity);
257
+ const savings = (0, import_core3.calculateTierSavings)(
258
+ bundle.volumeTiers,
259
+ basePrice,
260
+ quantity
261
+ );
260
262
  tiersDiv.innerHTML = "";
261
263
  for (const ts of savings) {
262
264
  const row = document.createElement("div");
@@ -292,22 +294,19 @@ function renderVolumeBundle(container, bundle, addToCart) {
292
294
  updateTiers();
293
295
  }
294
296
  });
295
- button.addEventListener("click", async () => {
297
+ button.addEventListener("click", () => {
296
298
  const variant = product.variants.nodes.find((v) => v.availableForSale);
297
299
  if (!variant) return;
298
- button.disabled = true;
299
- button.textContent = "Adding...";
300
- const items = [{ variantId: variant.id, quantity }];
301
- const result = await addToCart(items);
302
- button.disabled = false;
303
- updateTiers();
304
- if (!result.success) {
305
- const error = document.createElement("p");
306
- error.className = "lb-bundle__error";
307
- error.textContent = result.error ?? "Failed to add to cart";
308
- container.appendChild(error);
309
- setTimeout(() => error.remove(), 5e3);
310
- }
300
+ onAddToCart([
301
+ {
302
+ merchandiseId: variant.id,
303
+ quantity,
304
+ attributes: [
305
+ { key: "_lime_bundle_gid", value: bundle.id },
306
+ { key: "_lime_bundle_type", value: bundle.bundleType }
307
+ ]
308
+ }
309
+ ]);
311
310
  });
312
311
  }
313
312
  function escapeHtml3(str) {
@@ -388,7 +387,6 @@ var LimeBundleElement = class extends HTMLElement {
388
387
  "shop-domain",
389
388
  "storefront-token",
390
389
  "bundle-gid",
391
- "cart-id",
392
390
  "app-url",
393
391
  "analytics",
394
392
  "locale"
@@ -409,7 +407,6 @@ var LimeBundleElement = class extends HTMLElement {
409
407
  this.abortController?.abort();
410
408
  this.teardownImpression();
411
409
  }
412
- /** Clean up stale bundle state and active impression observer. */
413
410
  teardownImpression() {
414
411
  this.impressionCleanup?.();
415
412
  this.impressionCleanup = null;
@@ -431,9 +428,6 @@ var LimeBundleElement = class extends HTMLElement {
431
428
  get bundleGid() {
432
429
  return this.getAttribute("bundle-gid") ?? "";
433
430
  }
434
- get cartId() {
435
- return this.getAttribute("cart-id") ?? void 0;
436
- }
437
431
  get appUrl() {
438
432
  return this.getAttribute("app-url") ?? "";
439
433
  }
@@ -442,7 +436,9 @@ var LimeBundleElement = class extends HTMLElement {
442
436
  }
443
437
  async fetchBundle() {
444
438
  if (!this.shopDomain || !this.storefrontToken || !this.bundleGid) {
445
- this.renderError("Missing required attributes: shop-domain, storefront-token, bundle-gid");
439
+ this.renderError(
440
+ "Missing required attributes: shop-domain, storefront-token, bundle-gid"
441
+ );
446
442
  return;
447
443
  }
448
444
  this.abortController?.abort();
@@ -454,26 +450,37 @@ var LimeBundleElement = class extends HTMLElement {
454
450
  shopDomain: this.shopDomain,
455
451
  accessToken: this.storefrontToken
456
452
  });
457
- const data = await client.query(
458
- import_core4.BUNDLE_METAOBJECT_QUERY,
459
- { id: this.bundleGid }
460
- );
453
+ const [bundleData, cssData] = await Promise.all([
454
+ client.query(
455
+ import_core4.BUNDLE_METAOBJECT_QUERY,
456
+ { id: this.bundleGid },
457
+ { signal: controller.signal }
458
+ ),
459
+ client.query(
460
+ import_core4.SHOP_CUSTOM_CSS_QUERY,
461
+ void 0,
462
+ { signal: controller.signal }
463
+ ).catch(() => null)
464
+ ]);
461
465
  if (controller.signal.aborted) return;
462
- if (!data.metaobject) {
466
+ if (!bundleData.metaobject) {
463
467
  this.bundle = null;
464
468
  this.teardownImpression();
465
469
  this.renderError("Bundle not found");
466
470
  return;
467
471
  }
468
472
  this.bundle = (0, import_core4.parseMetaobjectBundle)(
469
- data.metaobject.id,
470
- data.metaobject.fields
473
+ bundleData.metaobject.id,
474
+ bundleData.metaobject.fields
471
475
  );
472
476
  if (!this.bundle) {
473
477
  this.teardownImpression();
474
478
  this.renderError("Bundle is not active or has expired");
475
479
  return;
476
480
  }
481
+ if (cssData?.shop?.metafield?.value) {
482
+ (0, import_core4.injectCustomCss)(this.shopDomain, cssData.shop.metafield.value);
483
+ }
477
484
  this.renderBundle();
478
485
  this.setupImpression();
479
486
  } catch (err) {
@@ -485,73 +492,70 @@ var LimeBundleElement = class extends HTMLElement {
485
492
  );
486
493
  }
487
494
  }
495
+ /**
496
+ * Dispatch add-to-cart for merchant handling. Returns true — the widget
497
+ * reports success optimistically. If the merchant's cart mutation fails,
498
+ * they're responsible for surfacing that error in their own UI.
499
+ */
500
+ dispatchAddToCart = (lines) => {
501
+ this.dispatchEvent(
502
+ new CustomEvent("lime-bundle:add-to-cart", {
503
+ detail: { lines },
504
+ bubbles: true,
505
+ composed: true
506
+ })
507
+ );
508
+ if (this.analyticsEnabled && this.appUrl && this.bundle) {
509
+ const quantity = lines.reduce((sum, l) => sum + l.quantity, 0);
510
+ const totalPrice = lines.reduce((sum, line) => {
511
+ const product = this.bundle.products.find(
512
+ (p) => p.variants.nodes.some((v) => v.id === line.merchandiseId)
513
+ );
514
+ const variant = product?.variants.nodes.find(
515
+ (v) => v.id === line.merchandiseId
516
+ );
517
+ const price = variant ? parseFloat(variant.price.amount) : 0;
518
+ return sum + price * line.quantity;
519
+ }, 0);
520
+ (0, import_core4.reportAddToCart)(
521
+ { shopDomain: this.shopDomain, appUrl: this.appUrl },
522
+ {
523
+ bundleGid: this.bundleGid,
524
+ bundleType: this.bundle.bundleType,
525
+ productId: this.bundle.products[0]?.id ?? "",
526
+ quantity,
527
+ totalPrice: Math.round(totalPrice * 100) / 100
528
+ }
529
+ );
530
+ }
531
+ };
488
532
  renderBundle() {
489
533
  if (!this.bundle) return;
490
534
  const container = document.createElement("div");
491
535
  container.className = "lb-bundle";
492
536
  container.setAttribute("role", "region");
493
537
  container.setAttribute("aria-label", this.bundle.title);
494
- const addToCart = async (items) => {
495
- const apiType = (0, import_core4.detectCartApi)();
496
- const cart = apiType === "ajax" ? (0, import_core4.createAjaxCartApi)(this.bundleGid, this.bundle.bundleType) : (0, import_core4.createStorefrontCartApi)(
497
- (0, import_core4.createStorefrontClient)({
498
- shopDomain: this.shopDomain,
499
- accessToken: this.storefrontToken
500
- }),
501
- this.bundleGid,
502
- this.bundle.bundleType,
503
- this.cartId
504
- );
505
- let result;
506
- try {
507
- result = await cart.addLines(items);
508
- } catch (err) {
509
- result = {
510
- success: false,
511
- error: err instanceof Error ? err.message : "Cart add failed"
512
- };
513
- }
514
- if (result.success) {
515
- this.dispatchEvent(
516
- new CustomEvent("lime-bundle:add-to-cart", {
517
- detail: { items, cartId: result.cartId },
518
- bubbles: true,
519
- composed: true
520
- })
521
- );
522
- if (this.analyticsEnabled && this.appUrl) {
523
- const quantity = items.reduce((sum, i) => sum + i.quantity, 0);
524
- const totalPrice = items.reduce((sum, item) => {
525
- const product = this.bundle.products.find(
526
- (p) => p.variants.nodes.some((v) => v.id === item.variantId)
527
- );
528
- const variant = product?.variants.nodes.find((v) => v.id === item.variantId);
529
- const price = variant ? parseFloat(variant.price.amount) : 0;
530
- return sum + price * item.quantity;
531
- }, 0);
532
- (0, import_core4.reportAddToCart)(
533
- { shopDomain: this.shopDomain, appUrl: this.appUrl },
534
- {
535
- bundleGid: this.bundleGid,
536
- bundleType: this.bundle.bundleType,
537
- productId: this.bundle.products[0]?.id ?? "",
538
- quantity,
539
- totalPrice: Math.round(totalPrice * 100) / 100
540
- }
541
- );
542
- }
543
- }
544
- return result;
545
- };
546
538
  switch (this.bundle.bundleType) {
547
539
  case "fixed":
548
- renderFixedBundle(container, this.bundle, addToCart);
540
+ renderFixedBundle(
541
+ container,
542
+ this.bundle,
543
+ this.dispatchAddToCart
544
+ );
549
545
  break;
550
546
  case "mix_match":
551
- renderMixMatchBundle(container, this.bundle, addToCart);
547
+ renderMixMatchBundle(
548
+ container,
549
+ this.bundle,
550
+ this.dispatchAddToCart
551
+ );
552
552
  break;
553
553
  case "volume":
554
- renderVolumeBundle(container, this.bundle, addToCart);
554
+ renderVolumeBundle(
555
+ container,
556
+ this.bundle,
557
+ this.dispatchAddToCart
558
+ );
555
559
  break;
556
560
  }
557
561
  this.shadow.innerHTML = "";
@@ -607,66 +611,12 @@ var LimeBundleElement = class extends HTMLElement {
607
611
  }
608
612
  };
609
613
 
610
- // src/thankyou/index.ts
611
- var reportedPurchases = /* @__PURE__ */ new Set();
612
- function trackPurchase(input) {
613
- const appUrl = input.appUrl ?? `https://${input.shopDomain}`;
614
- const bundleMap = /* @__PURE__ */ new Map();
615
- for (const item of input.lineItems) {
616
- if (!item.bundleGid) continue;
617
- const existing = bundleMap.get(item.bundleGid);
618
- if (existing) {
619
- existing.revenue += item.price * item.quantity;
620
- existing.lineItemCount += 1;
621
- } else {
622
- bundleMap.set(item.bundleGid, {
623
- bundleType: item.bundleType,
624
- revenue: item.price * item.quantity,
625
- lineItemCount: 1
626
- });
627
- }
628
- }
629
- for (const [bundleGid, data] of bundleMap) {
630
- const dedupKey = `${input.orderId}:${bundleGid}`;
631
- if (reportedPurchases.has(dedupKey)) continue;
632
- reportedPurchases.add(dedupKey);
633
- const payload = {
634
- shopDomain: input.shopDomain,
635
- eventType: "bundle_purchased",
636
- bundleGid,
637
- bundleType: data.bundleType,
638
- orderId: input.orderId,
639
- revenue: Math.round(data.revenue * 100) / 100,
640
- lineItemCount: data.lineItemCount,
641
- occurredAt: (/* @__PURE__ */ new Date()).toISOString()
642
- };
643
- const url = `${appUrl}/api/analytics`;
644
- const body = JSON.stringify(payload);
645
- try {
646
- fetch(url, {
647
- method: "POST",
648
- headers: { "Content-Type": "application/json" },
649
- body
650
- }).catch(() => {
651
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
652
- navigator.sendBeacon(url, body);
653
- }
654
- });
655
- } catch {
656
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
657
- navigator.sendBeacon(url, body);
658
- }
659
- }
660
- }
661
- }
662
-
663
614
  // src/index.ts
664
615
  if (typeof customElements !== "undefined" && !customElements.get("lime-bundle")) {
665
616
  customElements.define("lime-bundle", LimeBundleElement);
666
617
  }
667
618
  // Annotate the CommonJS export names for ESM import in node:
668
619
  0 && (module.exports = {
669
- LimeBundleElement,
670
- trackPurchase
620
+ LimeBundleElement
671
621
  });
672
622
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["/**\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","/**\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"],"mappings":"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","names":["import_core","import_core","escapeHtml","import_core","escapeHtml"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/styles/widget-styles.ts"],"sourcesContent":["/**\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","/**\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"],"mappings":"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","names":["import_core","import_core","escapeHtml","import_core","escapeHtml"]}