@lime-bundles/widget 0.1.1 → 1.0.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) {
@@ -383,20 +382,35 @@ var WIDGET_STYLES = `
383
382
  `;
384
383
 
385
384
  // src/lime-bundle.ts
385
+ var cartStorageKey = (shopDomain) => `lb_cart_id:${shopDomain}`;
386
+ function resolveProductHandle(explicit) {
387
+ if (explicit) return explicit.trim() || null;
388
+ if (typeof document !== "undefined") {
389
+ const meta = document.querySelector(
390
+ 'meta[name="shopify:product-handle"]'
391
+ );
392
+ if (meta?.content) return meta.content.trim() || null;
393
+ }
394
+ if (typeof window !== "undefined") {
395
+ const match = window.location.pathname.match(/\/products\/([^/?#]+)/);
396
+ if (match?.[1]) return decodeURIComponent(match[1]);
397
+ }
398
+ return null;
399
+ }
386
400
  var LimeBundleElement = class extends HTMLElement {
387
401
  static observedAttributes = [
388
402
  "shop-domain",
389
403
  "storefront-token",
390
404
  "bundle-gid",
391
- "cart-id",
405
+ "product-handle",
392
406
  "app-url",
393
407
  "analytics",
394
408
  "locale"
395
409
  ];
396
410
  shadow;
397
- bundle = null;
411
+ bundles = [];
398
412
  abortController = null;
399
- impressionCleanup = null;
413
+ impressionCleanups = [];
400
414
  constructor() {
401
415
  super();
402
416
  this.shadow = this.attachShadow({ mode: "open" });
@@ -407,17 +421,16 @@ var LimeBundleElement = class extends HTMLElement {
407
421
  }
408
422
  disconnectedCallback() {
409
423
  this.abortController?.abort();
410
- this.teardownImpression();
424
+ this.teardownImpressions();
411
425
  }
412
- /** Clean up stale bundle state and active impression observer. */
413
- teardownImpression() {
414
- this.impressionCleanup?.();
415
- this.impressionCleanup = null;
426
+ teardownImpressions() {
427
+ for (const cleanup of this.impressionCleanups) cleanup();
428
+ this.impressionCleanups = [];
416
429
  }
417
430
  attributeChangedCallback(name, oldValue, newValue) {
418
431
  if (oldValue === newValue || !this.isConnected) return;
419
- if (name === "bundle-gid" || name === "shop-domain" || name === "storefront-token") {
420
- if (this.shopDomain && this.storefrontToken && this.bundleGid) {
432
+ if (name === "bundle-gid" || name === "product-handle" || name === "shop-domain" || name === "storefront-token") {
433
+ if (this.shopDomain && this.storefrontToken) {
421
434
  this.fetchBundle();
422
435
  }
423
436
  }
@@ -431,8 +444,8 @@ var LimeBundleElement = class extends HTMLElement {
431
444
  get bundleGid() {
432
445
  return this.getAttribute("bundle-gid") ?? "";
433
446
  }
434
- get cartId() {
435
- return this.getAttribute("cart-id") ?? void 0;
447
+ get productHandleAttr() {
448
+ return this.getAttribute("product-handle") ?? "";
436
449
  }
437
450
  get appUrl() {
438
451
  return this.getAttribute("app-url") ?? "";
@@ -441,147 +454,265 @@ var LimeBundleElement = class extends HTMLElement {
441
454
  return this.getAttribute("analytics") !== "false";
442
455
  }
443
456
  async fetchBundle() {
444
- if (!this.shopDomain || !this.storefrontToken || !this.bundleGid) {
445
- this.renderError("Missing required attributes: shop-domain, storefront-token, bundle-gid");
457
+ if (!this.shopDomain || !this.storefrontToken) {
458
+ this.renderError(
459
+ "Missing required attributes: shop-domain, storefront-token"
460
+ );
446
461
  return;
447
462
  }
448
463
  this.abortController?.abort();
449
464
  const controller = new AbortController();
450
465
  this.abortController = controller;
451
466
  this.renderLoading();
467
+ const client = (0, import_core4.createStorefrontClient)({
468
+ shopDomain: this.shopDomain,
469
+ accessToken: this.storefrontToken
470
+ });
452
471
  try {
453
- const client = (0, import_core4.createStorefrontClient)({
454
- shopDomain: this.shopDomain,
455
- accessToken: this.storefrontToken
456
- });
457
- const data = await client.query(
458
- import_core4.BUNDLE_METAOBJECT_QUERY,
459
- { id: this.bundleGid }
460
- );
472
+ let bundlePromise;
473
+ let singleBundleMode = false;
474
+ if (this.bundleGid) {
475
+ singleBundleMode = true;
476
+ bundlePromise = this.fetchSingleBundle(client, controller.signal);
477
+ } else {
478
+ const handle = resolveProductHandle(this.productHandleAttr);
479
+ if (!handle) {
480
+ this.teardownImpressions();
481
+ this.renderError(
482
+ "No bundle-gid or product-handle provided, and the current URL doesn't match /products/<handle>."
483
+ );
484
+ return;
485
+ }
486
+ bundlePromise = this.fetchProductBundles(
487
+ client,
488
+ controller.signal,
489
+ handle
490
+ );
491
+ }
492
+ const cssPromise = client.query(import_core4.SHOP_CUSTOM_CSS_QUERY, void 0, {
493
+ signal: controller.signal
494
+ }).catch(() => null);
495
+ await bundlePromise;
461
496
  if (controller.signal.aborted) return;
462
- if (!data.metaobject) {
463
- this.bundle = null;
464
- this.teardownImpression();
497
+ if (singleBundleMode && this.bundles.length === 0) {
498
+ this.teardownImpressions();
465
499
  this.renderError("Bundle not found");
466
500
  return;
467
501
  }
468
- this.bundle = (0, import_core4.parseMetaobjectBundle)(
469
- data.metaobject.id,
470
- data.metaobject.fields
471
- );
472
- if (!this.bundle) {
473
- this.teardownImpression();
474
- this.renderError("Bundle is not active or has expired");
475
- return;
502
+ const css = await cssPromise;
503
+ if (css?.shop?.metafield?.value) {
504
+ (0, import_core4.injectCustomCss)(this.shopDomain, css.shop.metafield.value);
476
505
  }
477
- this.renderBundle();
478
- this.setupImpression();
506
+ this.renderBundles();
479
507
  } catch (err) {
480
508
  if (controller.signal.aborted) return;
481
- this.bundle = null;
482
- this.teardownImpression();
509
+ this.bundles = [];
510
+ this.teardownImpressions();
483
511
  this.renderError(
484
512
  err instanceof Error ? err.message : "Failed to load bundle"
485
513
  );
486
514
  }
487
515
  }
488
- renderBundle() {
489
- if (!this.bundle) return;
490
- const container = document.createElement("div");
491
- container.className = "lb-bundle";
492
- container.setAttribute("role", "region");
493
- 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
- };
516
+ async fetchSingleBundle(client, signal) {
517
+ const data = await client.query(
518
+ import_core4.BUNDLE_METAOBJECT_QUERY,
519
+ { id: this.bundleGid },
520
+ { signal }
521
+ );
522
+ if (!data.metaobject) {
523
+ this.bundles = [];
524
+ return;
525
+ }
526
+ const parsed = (0, import_core4.parseMetaobjectBundle)(
527
+ data.metaobject.id,
528
+ data.metaobject.fields
529
+ );
530
+ this.bundles = parsed ? [parsed] : [];
531
+ }
532
+ async fetchProductBundles(client, signal, productHandle) {
533
+ const data = await client.query(
534
+ import_core4.BUNDLES_FOR_PRODUCT_QUERY,
535
+ { handle: productHandle },
536
+ { signal }
537
+ );
538
+ if (!data.product) {
539
+ this.bundles = [];
540
+ return;
541
+ }
542
+ const refs = data.product.metafield?.references?.nodes ?? [];
543
+ const bundles = [];
544
+ for (const ref of refs) {
545
+ const parsed = (0, import_core4.parseMetaobjectBundle)(ref.id, ref.fields);
546
+ if (parsed) bundles.push(parsed);
547
+ }
548
+ this.bundles = bundles;
549
+ }
550
+ /**
551
+ * Dispatch add-to-cart with a cancelable event, then — unless a listener
552
+ * called preventDefault — execute the default cart-and-checkout flow.
553
+ *
554
+ * `fire-and-forget` against `reportAddToCart` runs regardless so merchants
555
+ * with BYO cart still get analytics.
556
+ */
557
+ handleAddToCart = async (bundle, lines) => {
558
+ const ev = new CustomEvent("lime-bundle:add-to-cart", {
559
+ detail: { lines },
560
+ bubbles: true,
561
+ composed: true,
562
+ cancelable: true
563
+ });
564
+ const allowDefault = this.dispatchEvent(ev);
565
+ this.reportAddToCartEvent(bundle, lines);
566
+ if (allowDefault) {
567
+ await this.defaultAddToCart(lines);
568
+ }
569
+ };
570
+ /**
571
+ * Default cart flow: Shopify's Storefront Cart API is tokenless, so we
572
+ * don't need any additional scopes. Persist the cart ID in localStorage
573
+ * so subsequent adds on the same browser session join the existing cart
574
+ * instead of creating a new one every click.
575
+ */
576
+ async defaultAddToCart(lines) {
577
+ if (typeof window === "undefined") return;
578
+ const client = (0, import_core4.createStorefrontClient)({
579
+ shopDomain: this.shopDomain,
580
+ accessToken: this.storefrontToken
581
+ });
582
+ const storage = window.localStorage;
583
+ const key = cartStorageKey(this.shopDomain);
584
+ const existingCartId = storage?.getItem(key) ?? null;
585
+ try {
586
+ let checkoutUrl = null;
587
+ if (existingCartId) {
588
+ const res = await client.query(
589
+ import_core4.CART_LINES_ADD_MUTATION,
590
+ { cartId: existingCartId, lines }
591
+ );
592
+ const payload = res.cartLinesAdd;
593
+ if (payload?.userErrors?.length) {
594
+ storage?.removeItem(key);
595
+ } else if (payload?.cart) {
596
+ checkoutUrl = payload.cart.checkoutUrl;
597
+ }
513
598
  }
514
- if (result.success) {
599
+ if (!checkoutUrl) {
600
+ const res = await client.query(
601
+ import_core4.CART_CREATE_MUTATION,
602
+ { input: { lines } }
603
+ );
604
+ const payload = res.cartCreate;
605
+ if (payload?.cart) {
606
+ storage?.setItem(key, payload.cart.id);
607
+ checkoutUrl = payload.cart.checkoutUrl;
608
+ }
609
+ }
610
+ if (checkoutUrl) {
611
+ window.location.assign(checkoutUrl);
612
+ } else {
515
613
  this.dispatchEvent(
516
- new CustomEvent("lime-bundle:add-to-cart", {
517
- detail: { items, cartId: result.cartId },
614
+ new CustomEvent("lime-bundle:error", {
615
+ detail: { message: "Cart creation failed", code: "CART_ERROR" },
518
616
  bubbles: true,
519
617
  composed: true
520
618
  })
521
619
  );
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
620
  }
544
- return result;
545
- };
546
- switch (this.bundle.bundleType) {
547
- case "fixed":
548
- renderFixedBundle(container, this.bundle, addToCart);
549
- break;
550
- case "mix_match":
551
- renderMixMatchBundle(container, this.bundle, addToCart);
552
- break;
553
- case "volume":
554
- renderVolumeBundle(container, this.bundle, addToCart);
555
- break;
621
+ } catch (err) {
622
+ this.dispatchEvent(
623
+ new CustomEvent("lime-bundle:error", {
624
+ detail: {
625
+ message: err instanceof Error ? err.message : "Cart mutation failed",
626
+ code: "CART_ERROR"
627
+ },
628
+ bubbles: true,
629
+ composed: true
630
+ })
631
+ );
556
632
  }
633
+ }
634
+ reportAddToCartEvent(bundle, lines) {
635
+ if (!this.analyticsEnabled || !this.appUrl) return;
636
+ const quantity = lines.reduce((sum, l) => sum + l.quantity, 0);
637
+ const totalPrice = lines.reduce((sum, line) => {
638
+ const product = bundle.products.find(
639
+ (p) => p.variants.nodes.some((v) => v.id === line.merchandiseId)
640
+ );
641
+ const variant = product?.variants.nodes.find(
642
+ (v) => v.id === line.merchandiseId
643
+ );
644
+ const price = variant ? parseFloat(variant.price.amount) : 0;
645
+ return sum + price * line.quantity;
646
+ }, 0);
647
+ (0, import_core4.reportAddToCart)(
648
+ { shopDomain: this.shopDomain, appUrl: this.appUrl },
649
+ {
650
+ bundleGid: bundle.id,
651
+ bundleType: bundle.bundleType,
652
+ productId: bundle.products[0]?.id ?? "",
653
+ quantity,
654
+ totalPrice: Math.round(totalPrice * 100) / 100
655
+ }
656
+ );
657
+ }
658
+ renderBundles() {
659
+ this.teardownImpressions();
557
660
  this.shadow.innerHTML = "";
558
661
  const style = document.createElement("style");
559
662
  style.textContent = WIDGET_STYLES;
560
663
  this.shadow.appendChild(style);
561
- this.shadow.appendChild(container);
664
+ for (const bundle of this.bundles) {
665
+ const container = document.createElement("div");
666
+ container.className = "lb-bundle";
667
+ container.setAttribute("role", "region");
668
+ container.setAttribute("aria-label", bundle.title);
669
+ const dispatch = (lines) => this.handleAddToCart(bundle, lines);
670
+ switch (bundle.bundleType) {
671
+ case "fixed":
672
+ renderFixedBundle(container, bundle, dispatch);
673
+ break;
674
+ case "mix_match":
675
+ renderMixMatchBundle(
676
+ container,
677
+ bundle,
678
+ dispatch
679
+ );
680
+ break;
681
+ case "volume":
682
+ renderVolumeBundle(container, bundle, dispatch);
683
+ break;
684
+ }
685
+ this.shadow.appendChild(container);
686
+ this.setupImpressionFor(bundle, container);
687
+ }
688
+ const first = this.bundles[0];
562
689
  this.dispatchEvent(
563
690
  new CustomEvent("lime-bundle:loaded", {
564
691
  detail: {
565
- bundleType: this.bundle.bundleType,
566
- title: this.bundle.title
692
+ bundleCount: this.bundles.length,
693
+ bundleTypes: this.bundles.map((b) => b.bundleType),
694
+ // Legacy fields — meaningful only in single-bundle mode. Preserved
695
+ // for merchants who attached listeners against the pre-1.0 shape.
696
+ bundleType: first?.bundleType,
697
+ title: first?.title
567
698
  },
568
699
  bubbles: true,
569
700
  composed: true
570
701
  })
571
702
  );
572
703
  }
573
- setupImpression() {
574
- if (!this.analyticsEnabled || !this.bundle || !this.appUrl) return;
575
- this.impressionCleanup?.();
576
- this.impressionCleanup = (0, import_core4.observeImpression)(this, () => {
704
+ setupImpressionFor(bundle, element) {
705
+ if (!this.analyticsEnabled || !this.appUrl) return;
706
+ const cleanup = (0, import_core4.observeImpression)(element, () => {
577
707
  (0, import_core4.reportImpression)(
578
708
  { shopDomain: this.shopDomain, appUrl: this.appUrl },
579
709
  {
580
- bundleGid: this.bundleGid,
581
- bundleType: this.bundle.bundleType
710
+ bundleGid: bundle.id,
711
+ bundleType: bundle.bundleType
582
712
  }
583
713
  );
584
714
  });
715
+ this.impressionCleanups.push(cleanup);
585
716
  }
586
717
  renderLoading() {
587
718
  this.shadow.innerHTML = `
@@ -607,66 +738,12 @@ var LimeBundleElement = class extends HTMLElement {
607
738
  }
608
739
  };
609
740
 
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
741
  // src/index.ts
664
742
  if (typeof customElements !== "undefined" && !customElements.get("lime-bundle")) {
665
743
  customElements.define("lime-bundle", LimeBundleElement);
666
744
  }
667
745
  // Annotate the CommonJS export names for ESM import in node:
668
746
  0 && (module.exports = {
669
- LimeBundleElement,
670
- trackPurchase
747
+ LimeBundleElement
671
748
  });
672
749
  //# sourceMappingURL=index.cjs.map