@lime-bundles/react 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // src/components/FixedBundle.tsx
2
- import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "react";
2
+ import { useCallback as useCallback3, useEffect as useEffect4, useState as useState3 } from "react";
3
3
  import {
4
4
  formatMoney,
5
+ formatUnitPrice,
5
6
  calculateDiscount,
6
7
  resolveBundleQty
7
8
  } from "@lime-bundles/core";
@@ -188,6 +189,25 @@ function useAnalytics(options) {
188
189
  return { elementRef, trackAddToCart };
189
190
  }
190
191
 
192
+ // src/hooks/useWidgetConfigVars.ts
193
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2 } from "react";
194
+ import { applyWidgetConfigVars } from "@lime-bundles/core";
195
+ function useWidgetConfigVars(config) {
196
+ const elementRef = useRef2(null);
197
+ useEffect3(() => {
198
+ if (elementRef.current && config) {
199
+ applyWidgetConfigVars(elementRef.current, config);
200
+ }
201
+ }, [config]);
202
+ return useCallback2(
203
+ (el) => {
204
+ elementRef.current = el;
205
+ if (el && config) applyWidgetConfigVars(el, config);
206
+ },
207
+ [config]
208
+ );
209
+ }
210
+
191
211
  // src/components/FixedBundle.tsx
192
212
  import { jsx, jsxs } from "react/jsx-runtime";
193
213
  function FixedBundle(props) {
@@ -216,7 +236,8 @@ function FixedBundle(props) {
216
236
  const [addingToCart, setAddingToCart] = useState3(false);
217
237
  const [cartError, setCartError] = useState3(null);
218
238
  const bundle = result.status === "success" && result.bundle.bundleType === "fixed" ? result.bundle : null;
219
- useEffect3(() => {
239
+ const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
240
+ useEffect4(() => {
220
241
  if (result.status === "error") {
221
242
  onError?.(result.error);
222
243
  return;
@@ -229,7 +250,7 @@ function FixedBundle(props) {
229
250
  );
230
251
  }
231
252
  }, [result, onError]);
232
- const handleAddToCart = useCallback2(async () => {
253
+ const handleAddToCart = useCallback3(async () => {
233
254
  if (!bundle) return;
234
255
  const bundleLines = bundle.products.filter((p) => p.variants.nodes.some((v) => v.availableForSale)).map((p) => {
235
256
  const variant = p.variants.nodes.find((v) => v.availableForSale);
@@ -289,28 +310,44 @@ function FixedBundle(props) {
289
310
  return /* @__PURE__ */ jsxs(
290
311
  "div",
291
312
  {
292
- ref: elementRef,
313
+ ref: (el) => {
314
+ elementRef(el);
315
+ setConfigVarsRef(el);
316
+ },
293
317
  className: `lb-bundle lb-bundle--fixed ${className ?? ""}`,
294
318
  role: "region",
295
319
  "aria-label": bundle.title,
296
320
  children: [
297
321
  /* @__PURE__ */ jsx("h3", { className: "lb-bundle__title", children: bundle.title }),
298
322
  bundle.discountLabel && /* @__PURE__ */ jsx("span", { className: "lb-bundle__discount-badge", children: bundle.discountLabel }),
299
- /* @__PURE__ */ jsx("div", { className: "lb-bundle__products", children: bundle.products.map((product) => /* @__PURE__ */ jsxs("div", { className: "lb-bundle__product", part: "product", children: [
300
- product.featuredImage && /* @__PURE__ */ jsx(
301
- "img",
302
- {
303
- src: product.featuredImage.url,
304
- alt: product.featuredImage.altText ?? product.title,
305
- className: "lb-bundle__product-image",
306
- loading: "lazy"
307
- }
308
- ),
309
- /* @__PURE__ */ jsxs("div", { className: "lb-bundle__product-info", children: [
310
- /* @__PURE__ */ jsx("p", { className: "lb-bundle__product-title", children: product.title }),
311
- /* @__PURE__ */ jsx("p", { className: "lb-bundle__product-price", children: formatMoney(product.priceRange.minVariantPrice.amount, currency) })
312
- ] })
313
- ] }, product.id)) }),
323
+ /* @__PURE__ */ jsx("div", { className: "lb-bundle__products", children: bundle.products.map((product) => {
324
+ const variant = product.variants.nodes.find((v) => v.availableForSale) ?? product.variants.nodes[0];
325
+ const priceText = variant ? formatMoney(variant.price.amount, currency) : formatMoney(
326
+ product.priceRange.minVariantPrice.amount,
327
+ currency
328
+ );
329
+ const unitPriceText = variant ? formatUnitPrice(
330
+ variant.unitPrice,
331
+ variant.unitPriceMeasurement,
332
+ currency
333
+ ) : null;
334
+ return /* @__PURE__ */ jsxs("div", { className: "lb-bundle__product", part: "product", children: [
335
+ product.featuredImage && /* @__PURE__ */ jsx(
336
+ "img",
337
+ {
338
+ src: product.featuredImage.url,
339
+ alt: product.featuredImage.altText ?? product.title,
340
+ className: "lb-bundle__product-image",
341
+ loading: "lazy"
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsxs("div", { className: "lb-bundle__product-info", children: [
345
+ /* @__PURE__ */ jsx("p", { className: "lb-bundle__product-title", children: product.title }),
346
+ /* @__PURE__ */ jsx("p", { className: "lb-bundle__product-price", children: priceText }),
347
+ unitPriceText && /* @__PURE__ */ jsx("p", { className: "lb-bundle__product-unit-price", children: unitPriceText })
348
+ ] })
349
+ ] }, product.id);
350
+ }) }),
314
351
  cartError && /* @__PURE__ */ jsx("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
315
352
  /* @__PURE__ */ jsx(
316
353
  "button",
@@ -328,9 +365,10 @@ function FixedBundle(props) {
328
365
  }
329
366
 
330
367
  // src/components/MixMatchBundle.tsx
331
- import { useCallback as useCallback3, useEffect as useEffect4, useState as useState4 } from "react";
368
+ import { useCallback as useCallback4, useEffect as useEffect5, useState as useState4 } from "react";
332
369
  import {
333
370
  formatMoney as formatMoney2,
371
+ formatUnitPrice as formatUnitPrice2,
334
372
  validateQuantity,
335
373
  resolveBundleQty as resolveBundleQty2
336
374
  } from "@lime-bundles/core";
@@ -364,7 +402,8 @@ function MixMatchBundle(props) {
364
402
  const [addingToCart, setAddingToCart] = useState4(false);
365
403
  const [cartError, setCartError] = useState4(null);
366
404
  const bundle = result.status === "success" && result.bundle.bundleType === "mix_match" ? result.bundle : null;
367
- useEffect4(() => {
405
+ const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
406
+ useEffect5(() => {
368
407
  if (result.status === "error") {
369
408
  onError?.(result.error);
370
409
  return;
@@ -382,7 +421,7 @@ function MixMatchBundle(props) {
382
421
  0
383
422
  );
384
423
  const validation = bundle ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity) : { valid: false, totalQuantity: 0, message: null };
385
- const toggleProduct = useCallback3(
424
+ const toggleProduct = useCallback4(
386
425
  (productId, variant) => {
387
426
  setSelections((prev) => {
388
427
  const next = new Map(prev);
@@ -402,7 +441,7 @@ function MixMatchBundle(props) {
402
441
  },
403
442
  [bundle]
404
443
  );
405
- const updateQuantity = useCallback3(
444
+ const updateQuantity = useCallback4(
406
445
  (productId, variantId, quantity) => {
407
446
  setSelections((prev) => {
408
447
  const next = new Map(prev);
@@ -417,7 +456,7 @@ function MixMatchBundle(props) {
417
456
  },
418
457
  []
419
458
  );
420
- const handleAddToCart = useCallback3(async () => {
459
+ const handleAddToCart = useCallback4(async () => {
421
460
  if (!bundle || !validation.valid) return;
422
461
  const lines = Array.from(selections.values()).map((s) => ({
423
462
  merchandiseId: s.variantId,
@@ -473,7 +512,10 @@ function MixMatchBundle(props) {
473
512
  return /* @__PURE__ */ jsxs2(
474
513
  "div",
475
514
  {
476
- ref: elementRef,
515
+ ref: (el) => {
516
+ elementRef(el);
517
+ setConfigVarsRef(el);
518
+ },
477
519
  className: `lb-bundle lb-bundle--mix-match ${className ?? ""}`,
478
520
  role: "region",
479
521
  "aria-label": bundle.title,
@@ -502,7 +544,15 @@ function MixMatchBundle(props) {
502
544
  ),
503
545
  /* @__PURE__ */ jsxs2("div", { className: "lb-bundle__product-info", children: [
504
546
  /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-title", children: product.title }),
505
- /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-price", children: formatMoney2(defaultVariant.price.amount, currency) })
547
+ /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-price", children: formatMoney2(defaultVariant.price.amount, currency) }),
548
+ (() => {
549
+ const unitPriceText = formatUnitPrice2(
550
+ defaultVariant.unitPrice,
551
+ defaultVariant.unitPriceMeasurement,
552
+ currency
553
+ );
554
+ return unitPriceText ? /* @__PURE__ */ jsx2("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }) : null;
555
+ })()
506
556
  ] }),
507
557
  /* @__PURE__ */ jsx2("div", { className: "lb-bundle__product-actions", children: selected ? /* @__PURE__ */ jsxs2("div", { className: "lb-bundle__quantity-control", children: [
508
558
  /* @__PURE__ */ jsx2(
@@ -562,9 +612,10 @@ function MixMatchBundle(props) {
562
612
  }
563
613
 
564
614
  // src/components/VolumeBundle.tsx
565
- import { useCallback as useCallback4, useEffect as useEffect5, useState as useState5 } from "react";
615
+ import { useCallback as useCallback5, useEffect as useEffect6, useState as useState5 } from "react";
566
616
  import {
567
617
  formatMoney as formatMoney3,
618
+ formatUnitPrice as formatUnitPrice3,
568
619
  calculateTierSavings,
569
620
  getActiveTier
570
621
  } from "@lime-bundles/core";
@@ -596,7 +647,8 @@ function VolumeBundle(props) {
596
647
  const [addingToCart, setAddingToCart] = useState5(false);
597
648
  const [cartError, setCartError] = useState5(null);
598
649
  const bundle = result.status === "success" && result.bundle.bundleType === "volume" ? result.bundle : null;
599
- useEffect5(() => {
650
+ const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
651
+ useEffect6(() => {
600
652
  if (result.status === "error") {
601
653
  onError?.(result.error);
602
654
  return;
@@ -619,7 +671,7 @@ function VolumeBundle(props) {
619
671
  bundle.discountConfig.discountType
620
672
  ) : [];
621
673
  const activeTier = bundle ? getActiveTier(bundle.volumeTiers, quantity) : null;
622
- const handleAddToCart = useCallback4(async () => {
674
+ const handleAddToCart = useCallback5(async () => {
623
675
  if (!bundle || !product) return;
624
676
  const variant = product.variants.nodes.find((v) => v.availableForSale);
625
677
  if (!variant) return;
@@ -672,7 +724,10 @@ function VolumeBundle(props) {
672
724
  return /* @__PURE__ */ jsxs3(
673
725
  "div",
674
726
  {
675
- ref: elementRef,
727
+ ref: (el) => {
728
+ elementRef(el);
729
+ setConfigVarsRef(el);
730
+ },
676
731
  className: `lb-bundle lb-bundle--volume ${className ?? ""}`,
677
732
  role: "region",
678
733
  "aria-label": bundle.title,
@@ -693,7 +748,16 @@ function VolumeBundle(props) {
693
748
  /* @__PURE__ */ jsxs3("p", { className: "lb-bundle__product-price", children: [
694
749
  formatMoney3(basePrice, currency),
695
750
  " each"
696
- ] })
751
+ ] }),
752
+ (() => {
753
+ const variant = product.variants.nodes.find((v) => v.availableForSale) ?? product.variants.nodes[0];
754
+ const unitPriceText = variant ? formatUnitPrice3(
755
+ variant.unitPrice,
756
+ variant.unitPriceMeasurement,
757
+ currency
758
+ ) : null;
759
+ return unitPriceText ? /* @__PURE__ */ jsx3("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }) : null;
760
+ })()
697
761
  ] })
698
762
  ] }),
699
763
  /* @__PURE__ */ jsx3(
@@ -779,7 +843,7 @@ function VolumeBundle(props) {
779
843
  }
780
844
 
781
845
  // src/hooks/useBundlesForProduct.ts
782
- import { useState as useState6, useEffect as useEffect6 } from "react";
846
+ import { useState as useState6, useEffect as useEffect7 } from "react";
783
847
  import {
784
848
  fetchBundlesForProduct,
785
849
  injectCustomCss as injectCustomCss2
@@ -793,7 +857,7 @@ function useBundlesForProduct(options) {
793
857
  const [state, setState] = useState6(
794
858
  INITIAL_STATE2
795
859
  );
796
- useEffect6(() => {
860
+ useEffect7(() => {
797
861
  const controller = new AbortController();
798
862
  setState(INITIAL_STATE2);
799
863
  fetchBundlesForProduct({
@@ -839,7 +903,8 @@ import { BundleParseError as BundleParseError2, StorefrontApiError } from "@lime
839
903
  import {
840
904
  WIDGET_CONFIG_DEFAULTS,
841
905
  mergeWidgetConfig,
842
- applyWidgetConfigVars,
906
+ applyWidgetConfigVars as applyWidgetConfigVars2,
907
+ thumbnailRatioVars,
843
908
  applyABVariantB,
844
909
  parseCents,
845
910
  formatCents,
@@ -852,6 +917,7 @@ import {
852
917
  transformImageUrl,
853
918
  formatCountdown,
854
919
  formatMoney as formatMoney4,
920
+ formatUnitPrice as formatUnitPrice4,
855
921
  calculateDiscount as calculateDiscount2,
856
922
  reportImpression as reportImpression2,
857
923
  reportAddToCart as reportAddToCart2,
@@ -880,7 +946,7 @@ export {
880
946
  VolumeBundle,
881
947
  WIDGET_CONFIG_DEFAULTS,
882
948
  applyABVariantB,
883
- applyWidgetConfigVars,
949
+ applyWidgetConfigVars2 as applyWidgetConfigVars,
884
950
  calculateDiscount2 as calculateDiscount,
885
951
  calculateTierSavings2 as calculateTierSavings,
886
952
  computeBundleSaleCents,
@@ -892,6 +958,7 @@ export {
892
958
  formatCents,
893
959
  formatCountdown,
894
960
  formatMoney4 as formatMoney,
961
+ formatUnitPrice4 as formatUnitPrice,
895
962
  getABTestAssignment,
896
963
  getActiveTier2 as getActiveTier,
897
964
  hasConsent,
@@ -906,10 +973,12 @@ export {
906
973
  reportImpression2 as reportImpression,
907
974
  sanitizeCustomCss,
908
975
  setConsent,
976
+ thumbnailRatioVars,
909
977
  transformImageUrl,
910
978
  useAnalytics,
911
979
  useBundleData,
912
980
  useBundlesForProduct,
981
+ useWidgetConfigVars,
913
982
  validateQuantity2 as validateQuantity
914
983
  };
915
984
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/FixedBundle.tsx","../src/hooks/useBundleData.ts","../src/fetchBundleData.ts","../src/fetchShopCustomCss.ts","../src/hooks/useAnalytics.ts","../src/components/MixMatchBundle.tsx","../src/components/VolumeBundle.tsx","../src/hooks/useBundlesForProduct.ts","../src/index.ts"],"sourcesContent":["/**\n * Fixed bundle component — pre-selected products with a shared discount.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n calculateDiscount,\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function FixedBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"fixed\",\n enabled: analyticsEnabled !== false,\n });\n\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"fixed\"\n ? (result.bundle as FixedBundleData)\n : null;\n\n // Surface load/narrowing errors via onError — in a useEffect so we don't\n // fire side effects during render.\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"fixed\"\n ) {\n onError?.(\n new Error(\n `FixedBundle: expected bundleType=\"fixed\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle) return;\n\n // Qty resolution (variantQuantities → productQuantities → 1) lives in\n // `@lime-bundles/core` so the five-runtime fallback chain has a single\n // source of truth. See resolveBundleQty + PR #147 history.\n const bundleLines = 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 productId: p.id,\n variant,\n quantity: resolveBundleQty(bundle, p.id, variant.id),\n };\n })\n .filter((line) => line.quantity > 0);\n\n if (bundleLines.length === 0) return;\n\n const lines: CartLineInput[] = bundleLines.map((line) => ({\n merchandiseId: line.variant.id,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n const totalPrice = bundleLines.reduce((sum, line) => {\n const price = parseFloat(line.variant.price.amount);\n const discounted = calculateDiscount(\n price,\n bundle.discountConfig.discountType,\n bundle.discountConfig.discountValue,\n );\n return sum + discounted * line.quantity;\n }, 0);\n const totalQuantity = bundleLines.reduce(\n (sum, line) => sum + line.quantity,\n 0,\n );\n // productId is intentionally omitted — a fixed bundle spans multiple\n // products with no single \"page product\"; bundleGid on the event\n // already attributes to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [bundle, onAddToCart, onError, trackAddToCart]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n // Bundle loaded but wasn't a \"fixed\" variant — render nothing; the\n // useEffect above has already invoked onError with a descriptive message.\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={elementRef}\n className={`lb-bundle lb-bundle--fixed ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <div className=\"lb-bundle__products\">\n {bundle.products.map((product) => (\n <div key={product.id} className=\"lb-bundle__product\" part=\"product\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(product.priceRange.minVariantPrice.amount, currency)}\n </p>\n </div>\n </div>\n ))}\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? \"Add Bundle to Cart\")}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundleData` with loading / error\n * state + abort-on-unmount.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundleData` directly and pass the result to a client\n * component as a prop — the hook's only value-add over the async\n * function is React state management.\n *\n * `UseBundleDataResult` is a discriminated union locked at v1.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport { injectCustomCss, type ParsedBundle } from \"@lime-bundles/core\";\nimport { fetchBundleData } from \"../fetchBundleData\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n}\n\nexport type UseBundleDataResult =\n | { status: \"loading\"; bundle: null; error: null }\n | { status: \"success\"; bundle: ParsedBundle; error: null }\n | { status: \"error\"; bundle: null; error: Error };\n\nconst INITIAL_STATE: UseBundleDataResult = {\n status: \"loading\",\n bundle: null,\n error: null,\n};\n\nexport function useBundleData(\n options: UseBundleDataOptions,\n): UseBundleDataResult {\n const [state, setState] = useState<UseBundleDataResult>(INITIAL_STATE);\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundleData({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n bundleGid: options.bundleGid,\n signal: controller.signal,\n })\n .then((bundle) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundle, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundle: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch — best-effort, failures never block\n // bundle rendering.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.bundleGid,\n ]);\n\n return state;\n}\n","/**\n * Framework-agnostic async fetcher for bundle data.\n *\n * Primary path for Next.js App Router / RSC and Hydrogen loaders — call\n * this in a server-side context and pass the result as a prop to a\n * `\"use client\"` component. `useBundleData` is NOT needed in that\n * component because the data is already in hand.\n *\n * Always forward the end-buyer's IP via `buyerIp` when calling from SSR:\n * Shopify may return 430 Security Rejection for server-originated traffic\n * without it.\n *\n * Throws:\n * - `StorefrontApiError` on network / HTTP / GraphQL errors\n * - `BundleParseError` when the metaobject is missing, invalid type,\n * inactive, not yet started, or expired\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundleStrict,\n BundleParseError,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR. */\n buyerIp?: string;\n /** Abort the in-flight request (used by `useBundleData` to cancel on unmount). */\n signal?: AbortSignal;\n /** Override the Storefront API version pinned in `createStorefrontClient`. */\n apiVersion?: string;\n}\n\nexport async function fetchBundleData(\n options: FetchBundleDataOptions,\n): Promise<ParsedBundle> {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n apiVersion: options.apiVersion,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: options.bundleGid },\n { signal: options.signal },\n );\n\n if (!data.metaobject) {\n throw new BundleParseError(\n `Bundle not found: ${options.bundleGid}`,\n \"not_found\",\n );\n }\n\n return parseMetaobjectBundleStrict(\n data.metaobject.id,\n data.metaobject.fields,\n );\n}\n","/**\n * Best-effort fetch for the shop's `$app:custom_css` metafield.\n *\n * Returns the raw CSS string (still needs sanitization before injection —\n * see `injectCustomCss` from core) or null if the metafield is unset or\n * the request fails.\n *\n * **Per-shop in-memory cache.** Multiple `<FixedBundle>` / `<VolumeBundle>`\n * / `<MixMatchBundle>` components on the same page share one network\n * request — the first caller fetches, concurrent callers await the\n * same promise, and resolved values are cached so subsequent mounts\n * return instantly. Failures are NOT cached so the next caller retries.\n *\n * The cache lives for the life of the JS module (typically one page\n * load). Merchants updating their CSS see changes on the next page\n * load. The cache is exposed as `__clearShopCssCache` for test use only.\n */\nimport {\n createStorefrontClient,\n SHOP_CUSTOM_CSS_QUERY,\n type ShopCustomCssResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchShopCustomCssOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n buyerIp?: string;\n signal?: AbortSignal;\n}\n\n// Cache map: key = shopDomain, value = pending or resolved promise.\n// Failures delete their own entry so retries get a fresh fetch.\nconst cache = new Map<string, Promise<string | null>>();\n\nexport async function fetchShopCustomCss(\n options: FetchShopCustomCssOptions,\n): Promise<string | null> {\n const cached = cache.get(options.shopDomain);\n if (cached) return cached;\n\n const promise = (async () => {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n });\n\n const data = await client.query<ShopCustomCssResponse>(\n SHOP_CUSTOM_CSS_QUERY,\n undefined,\n { signal: options.signal },\n );\n\n return data.shop?.metafield?.value ?? null;\n })();\n\n // Cache the promise synchronously so concurrent callers dedupe.\n cache.set(options.shopDomain, promise);\n\n // Evict on failure so the next caller can retry.\n promise.catch(() => {\n if (cache.get(options.shopDomain) === promise) {\n cache.delete(options.shopDomain);\n }\n });\n\n return promise;\n}\n\n/**\n * Test-only helper. Clears the per-shop CSS cache so each test starts\n * from a known state. Do not call from application code.\n */\nexport function __clearShopCssCache(): void {\n cache.clear();\n}\n","/**\n * Hook for analytics event reporting (impressions + add-to-cart).\n * Impressions fire once per mount using IntersectionObserver.\n */\nimport { useEffect, useMemo, useRef, useCallback, useState } from \"react\";\nimport {\n reportImpression,\n reportAddToCart,\n observeImpression,\n type AnalyticsConfig,\n} from \"@lime-bundles/core\";\n\nexport interface UseAnalyticsOptions {\n shopDomain: string;\n appUrl: string;\n bundleGid: string;\n bundleType: string;\n enabled?: boolean;\n abTestId?: string;\n abVariant?: string;\n}\n\nexport function useAnalytics(options: UseAnalyticsOptions) {\n const impressionFiredRef = useRef(false);\n // Callback ref + state so the observer effect re-runs when the host element\n // is attached (e.g. after a `loading` branch swaps in the real DOM node).\n const [element, setElement] = useState<HTMLElement | null>(null);\n const elementRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n const config: AnalyticsConfig = useMemo(\n () => ({ shopDomain: options.shopDomain, appUrl: options.appUrl }),\n [options.shopDomain, options.appUrl],\n );\n\n // Reset the fired flag when bundle identity changes so a reused component\n // (e.g. via navigation) can report a fresh impression.\n useEffect(() => {\n impressionFiredRef.current = false;\n }, [options.bundleGid, options.abVariant]);\n\n // Set up impression observer once the element is attached\n useEffect(() => {\n if (options.enabled === false) return;\n if (impressionFiredRef.current) return;\n if (!element) return;\n\n const cleanup = observeImpression(element, () => {\n if (impressionFiredRef.current) return;\n impressionFiredRef.current = true;\n\n reportImpression(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n });\n\n return cleanup;\n }, [\n element,\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ]);\n\n const trackAddToCart = useCallback(\n (event: {\n /** Optional — omit for multi-product bundles (fixed, mix-match). */\n productId?: string;\n quantity: number;\n totalPrice: number;\n }) => {\n if (options.enabled === false) return;\n\n reportAddToCart(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n productId: event.productId,\n quantity: event.quantity,\n totalPrice: event.totalPrice,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n },\n [\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ],\n );\n\n return { elementRef, trackAddToCart };\n}\n","/**\n * Mix-and-match bundle component — customer picks products from a pool.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n validateQuantity,\n resolveBundleQty,\n type CartLineInput,\n type MixMatchBundleData,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport type { BundleComponentProps } from \"./types\";\n\ninterface SelectedItem {\n productId: string;\n variantId: string;\n quantity: number;\n}\n\nexport function MixMatchBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"mix_match\",\n enabled: analyticsEnabled !== false,\n });\n\n const [selections, setSelections] = useState<Map<string, SelectedItem>>(\n new Map(),\n );\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"mix_match\"\n ? (result.bundle as MixMatchBundleData)\n : null;\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"mix_match\"\n ) {\n onError?.(\n new Error(\n `MixMatchBundle: expected bundleType=\"mix_match\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const totalQuantity = Array.from(selections.values()).reduce(\n (sum, s) => sum + s.quantity,\n 0,\n );\n\n const validation = bundle\n ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity)\n : { valid: false, totalQuantity: 0, message: null };\n\n const toggleProduct = useCallback(\n (productId: string, variant: ProductVariant) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variant.id}`;\n if (next.has(key)) {\n next.delete(key);\n } else {\n // Pre-fill slot qty from the canonical resolver — variant-level\n // wins, product-level fallback, then 1. Customer can still adjust\n // via the stepper afterwards.\n const configuredQty = bundle\n ? resolveBundleQty(bundle, productId, variant.id)\n : 1;\n next.set(key, {\n productId,\n variantId: variant.id,\n quantity: configuredQty,\n });\n }\n return next;\n });\n },\n [bundle],\n );\n\n const updateQuantity = useCallback(\n (productId: string, variantId: string, quantity: number) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variantId}`;\n if (quantity <= 0) {\n next.delete(key);\n } else {\n next.set(key, { productId, variantId, quantity });\n }\n return next;\n });\n },\n [],\n );\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !validation.valid) return;\n\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 setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n\n const totalPrice = lines.reduce((sum, line) => {\n const product = 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 return (\n sum + parseFloat(variant?.price.amount ?? \"0\") * line.quantity\n );\n }, 0);\n\n // productId is intentionally omitted — mix-match bundles span\n // multiple products; bundleGid attributes the event to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n setSelections(new Map());\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n selections,\n validation.valid,\n onAddToCart,\n onError,\n trackAddToCart,\n totalQuantity,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={elementRef}\n className={`lb-bundle lb-bundle--mix-match ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <p className=\"lb-bundle__instructions\">\n {bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : bundle.maxQuantity\n ? `Select up to ${bundle.maxQuantity} items`\n : \"Select your items\"}\n </p>\n\n <div className=\"lb-bundle__products lb-bundle__products--selectable\">\n {bundle.products.map((product) => {\n const defaultVariant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n if (!defaultVariant) return null;\n\n const key = `${product.id}:${defaultVariant.id}`;\n const selected = selections.get(key);\n\n return (\n <div\n key={product.id}\n className={`lb-bundle__product lb-bundle__product--selectable ${selected ? \"lb-bundle__product--selected\" : \"\"}`}\n >\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(defaultVariant.price.amount, currency)}\n </p>\n </div>\n <div className=\"lb-bundle__product-actions\">\n {selected ? (\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label={`Decrease ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity - 1,\n )\n }\n >\n −\n </button>\n <span>{selected.quantity}</span>\n <button\n aria-label={`Increase ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity + 1,\n )\n }\n >\n +\n </button>\n </div>\n ) : (\n <button\n className=\"lb-bundle__select-btn\"\n onClick={() => toggleProduct(product.id, defaultVariant)}\n disabled={!defaultVariant.availableForSale}\n >\n {defaultVariant.availableForSale ? \"Select\" : \"Sold out\"}\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {validation.message && (\n <p className=\"lb-bundle__validation\" role=\"status\">\n {validation.message}\n </p>\n )}\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart || !validation.valid}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${totalQuantity} Items to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Volume bundle component — quantity selector with tier calculator.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n calculateTierSavings,\n getActiveTier,\n type CartLineInput,\n type VolumeBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function VolumeBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"volume\",\n enabled: analyticsEnabled !== false,\n });\n\n const [quantity, setQuantity] = useState(1);\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"volume\"\n ? (result.bundle as VolumeBundleData)\n : null;\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"volume\"\n ) {\n onError?.(\n new Error(\n `VolumeBundle: expected bundleType=\"volume\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const product = bundle?.products[0];\n const basePrice = product\n ? parseFloat(product.priceRange.minVariantPrice.amount)\n : 0;\n const currency = product?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const tierSavings = bundle\n ? calculateTierSavings(\n bundle.volumeTiers,\n basePrice,\n quantity,\n bundle.discountConfig.discountType,\n )\n : [];\n const activeTier = bundle\n ? getActiveTier(bundle.volumeTiers, quantity)\n : null;\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !product) return;\n\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n const lines: CartLineInput[] = [\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 setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n // Reuse the canonical computation from calculateTierSavings so the\n // analytics unit price matches what the component rendered.\n const unitPrice =\n tierSavings.find((ts) => ts.tier === activeTier)?.unitPrice ?? basePrice;\n trackAddToCart({\n productId: product.id,\n quantity,\n totalPrice: Math.round(unitPrice * quantity * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n product,\n quantity,\n activeTier,\n basePrice,\n onAddToCart,\n onError,\n trackAddToCart,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--tiers\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n if (!product) return null;\n\n return (\n <div\n ref={elementRef}\n className={`lb-bundle lb-bundle--volume ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n <div className=\"lb-bundle__product lb-bundle__product--volume\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(basePrice, currency)} each\n </p>\n </div>\n </div>\n\n <div\n className=\"lb-bundle__tiers\"\n role=\"table\"\n aria-label=\"Volume discounts\"\n >\n {tierSavings.map((ts) => (\n <div\n key={ts.tier.minQuantity}\n className={`lb-bundle__tier ${ts.isActive ? \"lb-bundle__tier--active\" : \"\"}`}\n role=\"row\"\n >\n <span className=\"lb-bundle__tier-quantity\" role=\"cell\">\n {ts.tier.minQuantity}+ items\n </span>\n <span className=\"lb-bundle__tier-price\" role=\"cell\">\n {formatMoney(ts.unitPrice, currency)} each\n </span>\n <span className=\"lb-bundle__tier-savings\" role=\"cell\">\n Save {ts.savingsPercent.toFixed(0)}%\n </span>\n </div>\n ))}\n </div>\n\n <div className=\"lb-bundle__quantity-selector\">\n <label htmlFor={`lb-qty-${bundle.id}`}>Quantity</label>\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label=\"Decrease quantity\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n >\n −\n </button>\n <input\n id={`lb-qty-${bundle.id}`}\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => {\n const val = parseInt(e.target.value, 10);\n if (!isNaN(val) && val > 0) setQuantity(val);\n }}\n className=\"lb-bundle__quantity-input\"\n />\n <button\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((q) => q + 1)}\n >\n +\n </button>\n </div>\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundlesForProduct` — the\n * React-SDK analogue of the web component's auto-detect-by-current-product\n * mode. The web component reads the handle from `location.pathname`; a\n * React app already has it on the router, so the hook takes it as an\n * explicit `productHandle` option.\n *\n * Returns every active bundle configured against the product. Inactive,\n * draft, scheduled-for-later, and expired bundles are filtered out by\n * the core helper — consumers don't have to guard.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundlesForProduct` directly and pass the list to a client\n * component as a prop. This hook's only value-add over the async\n * function is React state management + abort-on-unmount.\n *\n * `UseBundlesForProductResult` is a discriminated union locked at v2.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport {\n fetchBundlesForProduct,\n injectCustomCss,\n type ParsedBundle,\n} from \"@lime-bundles/core\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundlesForProductOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n productHandle: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR paths. */\n buyerIp?: string;\n}\n\nexport type UseBundlesForProductResult =\n | { status: \"loading\"; bundles: null; error: null }\n | { status: \"success\"; bundles: ParsedBundle[]; error: null }\n | { status: \"error\"; bundles: null; error: Error };\n\nconst INITIAL_STATE: UseBundlesForProductResult = {\n status: \"loading\",\n bundles: null,\n error: null,\n};\n\nexport function useBundlesForProduct(\n options: UseBundlesForProductOptions,\n): UseBundlesForProductResult {\n const [state, setState] = useState<UseBundlesForProductResult>(\n INITIAL_STATE,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundlesForProduct({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n productHandle: options.productHandle,\n buyerIp: options.buyerIp,\n signal: controller.signal,\n })\n .then((bundles) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundles, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundles: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch so storefronts using this hook get\n // merchant styling automatically — same behaviour as `useBundleData`.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.productHandle,\n options.buyerIp,\n ]);\n\n return state;\n}\n","// Bundle components — BYO-cart via `onAddToCart`.\nexport { FixedBundle } from \"./components/FixedBundle\";\nexport { MixMatchBundle } from \"./components/MixMatchBundle\";\nexport { VolumeBundle } from \"./components/VolumeBundle\";\nexport type { BundleComponentProps } from \"./components/types\";\n\n// Data fetching\nexport {\n fetchBundleData,\n type FetchBundleDataOptions,\n} from \"./fetchBundleData\";\nexport {\n fetchShopCustomCss,\n type FetchShopCustomCssOptions,\n} from \"./fetchShopCustomCss\";\nexport {\n useBundleData,\n type UseBundleDataOptions,\n type UseBundleDataResult,\n} from \"./hooks/useBundleData\";\nexport {\n useBundlesForProduct,\n type UseBundlesForProductOptions,\n type UseBundlesForProductResult,\n} from \"./hooks/useBundlesForProduct\";\nexport { useAnalytics } from \"./hooks/useAnalytics\";\n\n// Re-export core types consumers commonly need.\nexport type {\n ParsedBundle,\n FixedBundleData,\n VolumeBundleData,\n MixMatchBundleData,\n BundleType,\n Product,\n ProductVariant,\n CartLineInput,\n WidgetConfig,\n HeaderConfig,\n LayoutConfig,\n ProductListConfig,\n PricingConfig,\n SavingsBarConfig,\n CtaConfig,\n CountdownConfig,\n PopularBadgeConfig,\n VolumeTier,\n DiscountConfig,\n ABVariantOverrides,\n PricingRow,\n FixedBundlePricing,\n ImageTransform,\n} from \"@lime-bundles/core\";\nexport { BundleParseError, StorefrontApiError } from \"@lime-bundles/core\";\n\n// DIY widget primitives — every helper a developer needs to build their\n// own bundle UI with the same data, math, and theming the first-party\n// widget uses. All live in `@lime-bundles/core`; importing from the\n// React package is shorthand for consumers who already pulled `react`.\nexport {\n // Bundle config + variant merging\n WIDGET_CONFIG_DEFAULTS,\n mergeWidgetConfig,\n applyWidgetConfigVars,\n applyABVariantB,\n // Pricing (integer cents, matches Shopify Discount Function rounding)\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n // Volume-tier calculation\n calculateTierSavings,\n getActiveTier,\n // Mix-match quantity validation\n validateQuantity,\n // Image / countdown / money helpers\n transformImageUrl,\n formatCountdown,\n formatMoney,\n calculateDiscount,\n // Analytics (optional — FixedBundle / VolumeBundle / MixMatchBundle\n // auto-fire these unless analyticsEnabled is set to false)\n reportImpression,\n reportAddToCart,\n observeImpression,\n // A/B testing\n getABTestAssignment,\n setConsent,\n hasConsent,\n // CSS injection + sanitizer\n injectCustomCss,\n sanitizeCustomCss,\n // Custom-storefront data fetching\n createStorefrontClient,\n fetchBundlesForProduct,\n parseMetaobjectBundle,\n parseMetaobjectBundleStrict,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n SHOP_CUSTOM_CSS_QUERY,\n} from \"@lime-bundles/core\";\n"],"mappings":"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","names":["useCallback","useEffect","useState","createStorefrontClient","useEffect","useState","useState","useEffect","useCallback","useCallback","useEffect","useState","formatMoney","resolveBundleQty","jsx","jsxs","useState","useEffect","useCallback","resolveBundleQty","formatMoney","useCallback","useEffect","useState","formatMoney","jsx","jsxs","useState","useEffect","useCallback","formatMoney","useState","useEffect","injectCustomCss","INITIAL_STATE","useState","useEffect","injectCustomCss","BundleParseError","calculateTierSavings","getActiveTier","validateQuantity","formatMoney","calculateDiscount","reportImpression","reportAddToCart","observeImpression","injectCustomCss","createStorefrontClient","fetchBundlesForProduct","parseMetaobjectBundleStrict","BUNDLE_METAOBJECT_QUERY","SHOP_CUSTOM_CSS_QUERY"]}
1
+ {"version":3,"sources":["../src/components/FixedBundle.tsx","../src/hooks/useBundleData.ts","../src/fetchBundleData.ts","../src/fetchShopCustomCss.ts","../src/hooks/useAnalytics.ts","../src/hooks/useWidgetConfigVars.ts","../src/components/MixMatchBundle.tsx","../src/components/VolumeBundle.tsx","../src/hooks/useBundlesForProduct.ts","../src/index.ts"],"sourcesContent":["/**\n * Fixed bundle component — pre-selected products with a shared discount.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function FixedBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"fixed\",\n enabled: analyticsEnabled !== false,\n });\n\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"fixed\"\n ? (result.bundle as FixedBundleData)\n : null;\n\n // Apply merchant-config CSS vars (including --lb-thumbnail-aspect-ratio)\n // to the component root via a callback ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n // Surface load/narrowing errors via onError — in a useEffect so we don't\n // fire side effects during render.\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"fixed\"\n ) {\n onError?.(\n new Error(\n `FixedBundle: expected bundleType=\"fixed\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle) return;\n\n // Qty resolution (variantQuantities → productQuantities → 1) lives in\n // `@lime-bundles/core` so the five-runtime fallback chain has a single\n // source of truth. See resolveBundleQty + PR #147 history.\n const bundleLines = 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 productId: p.id,\n variant,\n quantity: resolveBundleQty(bundle, p.id, variant.id),\n };\n })\n .filter((line) => line.quantity > 0);\n\n if (bundleLines.length === 0) return;\n\n const lines: CartLineInput[] = bundleLines.map((line) => ({\n merchandiseId: line.variant.id,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n\n setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n const totalPrice = bundleLines.reduce((sum, line) => {\n const price = parseFloat(line.variant.price.amount);\n const discounted = calculateDiscount(\n price,\n bundle.discountConfig.discountType,\n bundle.discountConfig.discountValue,\n );\n return sum + discounted * line.quantity;\n }, 0);\n const totalQuantity = bundleLines.reduce(\n (sum, line) => sum + line.quantity,\n 0,\n );\n // productId is intentionally omitted — a fixed bundle spans multiple\n // products with no single \"page product\"; bundleGid on the event\n // already attributes to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [bundle, onAddToCart, onError, trackAddToCart]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n // Bundle loaded but wasn't a \"fixed\" variant — render nothing; the\n // useEffect above has already invoked onError with a descriptive message.\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--fixed ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <div className=\"lb-bundle__products\">\n {bundle.products.map((product) => {\n // Render the variant we'll actually add to cart so the price (and\n // unit price) match what the customer is buying — falling back to\n // the price range only when no variant is resolvable.\n const variant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n const priceText = variant\n ? formatMoney(variant.price.amount, currency)\n : formatMoney(\n product.priceRange.minVariantPrice.amount,\n currency,\n );\n const unitPriceText = variant\n ? formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n )\n : null;\n return (\n <div key={product.id} className=\"lb-bundle__product\" part=\"product\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">{priceText}</p>\n {unitPriceText && (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? \"Add Bundle to Cart\")}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundleData` with loading / error\n * state + abort-on-unmount.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundleData` directly and pass the result to a client\n * component as a prop — the hook's only value-add over the async\n * function is React state management.\n *\n * `UseBundleDataResult` is a discriminated union locked at v1.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport { injectCustomCss, type ParsedBundle } from \"@lime-bundles/core\";\nimport { fetchBundleData } from \"../fetchBundleData\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n}\n\nexport type UseBundleDataResult =\n | { status: \"loading\"; bundle: null; error: null }\n | { status: \"success\"; bundle: ParsedBundle; error: null }\n | { status: \"error\"; bundle: null; error: Error };\n\nconst INITIAL_STATE: UseBundleDataResult = {\n status: \"loading\",\n bundle: null,\n error: null,\n};\n\nexport function useBundleData(\n options: UseBundleDataOptions,\n): UseBundleDataResult {\n const [state, setState] = useState<UseBundleDataResult>(INITIAL_STATE);\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundleData({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n bundleGid: options.bundleGid,\n signal: controller.signal,\n })\n .then((bundle) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundle, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundle: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch — best-effort, failures never block\n // bundle rendering.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.bundleGid,\n ]);\n\n return state;\n}\n","/**\n * Framework-agnostic async fetcher for bundle data.\n *\n * Primary path for Next.js App Router / RSC and Hydrogen loaders — call\n * this in a server-side context and pass the result as a prop to a\n * `\"use client\"` component. `useBundleData` is NOT needed in that\n * component because the data is already in hand.\n *\n * Always forward the end-buyer's IP via `buyerIp` when calling from SSR:\n * Shopify may return 430 Security Rejection for server-originated traffic\n * without it.\n *\n * Throws:\n * - `StorefrontApiError` on network / HTTP / GraphQL errors\n * - `BundleParseError` when the metaobject is missing, invalid type,\n * inactive, not yet started, or expired\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n parseMetaobjectBundleStrict,\n BundleParseError,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchBundleDataOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n bundleGid: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR. */\n buyerIp?: string;\n /** Abort the in-flight request (used by `useBundleData` to cancel on unmount). */\n signal?: AbortSignal;\n /** Override the Storefront API version pinned in `createStorefrontClient`. */\n apiVersion?: string;\n}\n\nexport async function fetchBundleData(\n options: FetchBundleDataOptions,\n): Promise<ParsedBundle> {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n apiVersion: options.apiVersion,\n });\n\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: options.bundleGid },\n { signal: options.signal },\n );\n\n if (!data.metaobject) {\n throw new BundleParseError(\n `Bundle not found: ${options.bundleGid}`,\n \"not_found\",\n );\n }\n\n return parseMetaobjectBundleStrict(\n data.metaobject.id,\n data.metaobject.fields,\n );\n}\n","/**\n * Best-effort fetch for the shop's `$app:custom_css` metafield.\n *\n * Returns the raw CSS string (still needs sanitization before injection —\n * see `injectCustomCss` from core) or null if the metafield is unset or\n * the request fails.\n *\n * **Per-shop in-memory cache.** Multiple `<FixedBundle>` / `<VolumeBundle>`\n * / `<MixMatchBundle>` components on the same page share one network\n * request — the first caller fetches, concurrent callers await the\n * same promise, and resolved values are cached so subsequent mounts\n * return instantly. Failures are NOT cached so the next caller retries.\n *\n * The cache lives for the life of the JS module (typically one page\n * load). Merchants updating their CSS see changes on the next page\n * load. The cache is exposed as `__clearShopCssCache` for test use only.\n */\nimport {\n createStorefrontClient,\n SHOP_CUSTOM_CSS_QUERY,\n type ShopCustomCssResponse,\n} from \"@lime-bundles/core\";\n\nexport interface FetchShopCustomCssOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n buyerIp?: string;\n signal?: AbortSignal;\n}\n\n// Cache map: key = shopDomain, value = pending or resolved promise.\n// Failures delete their own entry so retries get a fresh fetch.\nconst cache = new Map<string, Promise<string | null>>();\n\nexport async function fetchShopCustomCss(\n options: FetchShopCustomCssOptions,\n): Promise<string | null> {\n const cached = cache.get(options.shopDomain);\n if (cached) return cached;\n\n const promise = (async () => {\n const client = createStorefrontClient({\n shopDomain: options.shopDomain,\n accessToken: options.storefrontAccessToken,\n buyerIp: options.buyerIp,\n });\n\n const data = await client.query<ShopCustomCssResponse>(\n SHOP_CUSTOM_CSS_QUERY,\n undefined,\n { signal: options.signal },\n );\n\n return data.shop?.metafield?.value ?? null;\n })();\n\n // Cache the promise synchronously so concurrent callers dedupe.\n cache.set(options.shopDomain, promise);\n\n // Evict on failure so the next caller can retry.\n promise.catch(() => {\n if (cache.get(options.shopDomain) === promise) {\n cache.delete(options.shopDomain);\n }\n });\n\n return promise;\n}\n\n/**\n * Test-only helper. Clears the per-shop CSS cache so each test starts\n * from a known state. Do not call from application code.\n */\nexport function __clearShopCssCache(): void {\n cache.clear();\n}\n","/**\n * Hook for analytics event reporting (impressions + add-to-cart).\n * Impressions fire once per mount using IntersectionObserver.\n */\nimport { useEffect, useMemo, useRef, useCallback, useState } from \"react\";\nimport {\n reportImpression,\n reportAddToCart,\n observeImpression,\n type AnalyticsConfig,\n} from \"@lime-bundles/core\";\n\nexport interface UseAnalyticsOptions {\n shopDomain: string;\n appUrl: string;\n bundleGid: string;\n bundleType: string;\n enabled?: boolean;\n abTestId?: string;\n abVariant?: string;\n}\n\nexport function useAnalytics(options: UseAnalyticsOptions) {\n const impressionFiredRef = useRef(false);\n // Callback ref + state so the observer effect re-runs when the host element\n // is attached (e.g. after a `loading` branch swaps in the real DOM node).\n const [element, setElement] = useState<HTMLElement | null>(null);\n const elementRef = useCallback((el: HTMLElement | null) => {\n setElement(el);\n }, []);\n\n const config: AnalyticsConfig = useMemo(\n () => ({ shopDomain: options.shopDomain, appUrl: options.appUrl }),\n [options.shopDomain, options.appUrl],\n );\n\n // Reset the fired flag when bundle identity changes so a reused component\n // (e.g. via navigation) can report a fresh impression.\n useEffect(() => {\n impressionFiredRef.current = false;\n }, [options.bundleGid, options.abVariant]);\n\n // Set up impression observer once the element is attached\n useEffect(() => {\n if (options.enabled === false) return;\n if (impressionFiredRef.current) return;\n if (!element) return;\n\n const cleanup = observeImpression(element, () => {\n if (impressionFiredRef.current) return;\n impressionFiredRef.current = true;\n\n reportImpression(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n });\n\n return cleanup;\n }, [\n element,\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ]);\n\n const trackAddToCart = useCallback(\n (event: {\n /** Optional — omit for multi-product bundles (fixed, mix-match). */\n productId?: string;\n quantity: number;\n totalPrice: number;\n }) => {\n if (options.enabled === false) return;\n\n reportAddToCart(config, {\n bundleGid: options.bundleGid,\n bundleType: options.bundleType,\n productId: event.productId,\n quantity: event.quantity,\n totalPrice: event.totalPrice,\n abTestId: options.abTestId,\n abVariant: options.abVariant,\n });\n },\n [\n config,\n options.enabled,\n options.bundleGid,\n options.bundleType,\n options.abTestId,\n options.abVariant,\n ],\n );\n\n return { elementRef, trackAddToCart };\n}\n","/**\n * Apply a bundle's `widgetConfig` to the component root as CSS custom\n * properties. Returns a callback ref so it composes with other callback\n * refs (e.g. `useAnalytics().elementRef`) without needing a ref-merge\n * library.\n *\n * Mirrors what the web component does in `applyWidgetConfigVars` — the\n * React surface uses a smaller subset of `--lb-*` vars in its own\n * stylesheet (`@lime-bundles/react/styles.css`), but having every var set\n * means merchant choices like `productList.thumbnailRatio` flow through to\n * the React image rules without further plumbing. Vars the React stylesheet\n * doesn't read are harmless extras.\n */\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { applyWidgetConfigVars, type WidgetConfig } from \"@lime-bundles/core\";\n\nexport function useWidgetConfigVars(\n config: WidgetConfig | null | undefined,\n): (el: HTMLElement | null) => void {\n const elementRef = useRef<HTMLElement | null>(null);\n\n // Re-apply when config changes (e.g. async bundle load completes).\n useEffect(() => {\n if (elementRef.current && config) {\n applyWidgetConfigVars(elementRef.current, config);\n }\n }, [config]);\n\n // Apply on first attachment so the merchant's chosen ratio is in effect\n // before the browser does its first paint.\n return useCallback(\n (el: HTMLElement | null) => {\n elementRef.current = el;\n if (el && config) applyWidgetConfigVars(el, config);\n },\n [config],\n );\n}\n","/**\n * Mix-and-match bundle component — customer picks products from a pool.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n validateQuantity,\n resolveBundleQty,\n type CartLineInput,\n type MixMatchBundleData,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\ninterface SelectedItem {\n productId: string;\n variantId: string;\n quantity: number;\n}\n\nexport function MixMatchBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"mix_match\",\n enabled: analyticsEnabled !== false,\n });\n\n const [selections, setSelections] = useState<Map<string, SelectedItem>>(\n new Map(),\n );\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"mix_match\"\n ? (result.bundle as MixMatchBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"mix_match\"\n ) {\n onError?.(\n new Error(\n `MixMatchBundle: expected bundleType=\"mix_match\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const totalQuantity = Array.from(selections.values()).reduce(\n (sum, s) => sum + s.quantity,\n 0,\n );\n\n const validation = bundle\n ? validateQuantity(totalQuantity, bundle.minQuantity, bundle.maxQuantity)\n : { valid: false, totalQuantity: 0, message: null };\n\n const toggleProduct = useCallback(\n (productId: string, variant: ProductVariant) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variant.id}`;\n if (next.has(key)) {\n next.delete(key);\n } else {\n // Pre-fill slot qty from the canonical resolver — variant-level\n // wins, product-level fallback, then 1. Customer can still adjust\n // via the stepper afterwards.\n const configuredQty = bundle\n ? resolveBundleQty(bundle, productId, variant.id)\n : 1;\n next.set(key, {\n productId,\n variantId: variant.id,\n quantity: configuredQty,\n });\n }\n return next;\n });\n },\n [bundle],\n );\n\n const updateQuantity = useCallback(\n (productId: string, variantId: string, quantity: number) => {\n setSelections((prev) => {\n const next = new Map(prev);\n const key = `${productId}:${variantId}`;\n if (quantity <= 0) {\n next.delete(key);\n } else {\n next.set(key, { productId, variantId, quantity });\n }\n return next;\n });\n },\n [],\n );\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !validation.valid) return;\n\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 setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n\n const totalPrice = lines.reduce((sum, line) => {\n const product = 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 return (\n sum + parseFloat(variant?.price.amount ?? \"0\") * line.quantity\n );\n }, 0);\n\n // productId is intentionally omitted — mix-match bundles span\n // multiple products; bundleGid attributes the event to the bundle.\n trackAddToCart({\n quantity: totalQuantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n });\n\n setSelections(new Map());\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n selections,\n validation.valid,\n onAddToCart,\n onError,\n trackAddToCart,\n totalQuantity,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--products\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--mix-match ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n {bundle.discountLabel && (\n <span className=\"lb-bundle__discount-badge\">{bundle.discountLabel}</span>\n )}\n\n <p className=\"lb-bundle__instructions\">\n {bundle.minQuantity && bundle.maxQuantity\n ? `Select ${bundle.minQuantity}–${bundle.maxQuantity} items`\n : bundle.minQuantity\n ? `Select at least ${bundle.minQuantity} items`\n : bundle.maxQuantity\n ? `Select up to ${bundle.maxQuantity} items`\n : \"Select your items\"}\n </p>\n\n <div className=\"lb-bundle__products lb-bundle__products--selectable\">\n {bundle.products.map((product) => {\n const defaultVariant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n if (!defaultVariant) return null;\n\n const key = `${product.id}:${defaultVariant.id}`;\n const selected = selections.get(key);\n\n return (\n <div\n key={product.id}\n className={`lb-bundle__product lb-bundle__product--selectable ${selected ? \"lb-bundle__product--selected\" : \"\"}`}\n >\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(defaultVariant.price.amount, currency)}\n </p>\n {(() => {\n const unitPriceText = formatUnitPrice(\n defaultVariant.unitPrice,\n defaultVariant.unitPriceMeasurement,\n currency,\n );\n return unitPriceText ? (\n <p className=\"lb-bundle__product-unit-price\">\n {unitPriceText}\n </p>\n ) : null;\n })()}\n </div>\n <div className=\"lb-bundle__product-actions\">\n {selected ? (\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label={`Decrease ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity - 1,\n )\n }\n >\n −\n </button>\n <span>{selected.quantity}</span>\n <button\n aria-label={`Increase ${product.title}`}\n onClick={() =>\n updateQuantity(\n product.id,\n defaultVariant.id,\n selected.quantity + 1,\n )\n }\n >\n +\n </button>\n </div>\n ) : (\n <button\n className=\"lb-bundle__select-btn\"\n onClick={() => toggleProduct(product.id, defaultVariant)}\n disabled={!defaultVariant.availableForSale}\n >\n {defaultVariant.availableForSale ? \"Select\" : \"Sold out\"}\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {validation.message && (\n <p className=\"lb-bundle__validation\" role=\"status\">\n {validation.message}\n </p>\n )}\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart || !validation.valid}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${totalQuantity} Items to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Volume bundle component — quantity selector with tier calculator.\n */\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n formatMoney,\n formatUnitPrice,\n calculateTierSavings,\n getActiveTier,\n type CartLineInput,\n type VolumeBundleData,\n} from \"@lime-bundles/core\";\nimport { useBundleData } from \"../hooks/useBundleData\";\nimport { useAnalytics } from \"../hooks/useAnalytics\";\nimport { useWidgetConfigVars } from \"../hooks/useWidgetConfigVars\";\nimport type { BundleComponentProps } from \"./types\";\n\nexport function VolumeBundle(props: BundleComponentProps) {\n const {\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n appUrl,\n analyticsEnabled,\n onAddToCart,\n onError,\n className,\n } = props;\n\n const result = useBundleData({\n shopDomain,\n storefrontAccessToken,\n bundleGid,\n });\n\n const { elementRef, trackAddToCart } = useAnalytics({\n shopDomain,\n appUrl: appUrl ?? `https://${shopDomain}`,\n bundleGid,\n bundleType: \"volume\",\n enabled: analyticsEnabled !== false,\n });\n\n const [quantity, setQuantity] = useState(1);\n const [addingToCart, setAddingToCart] = useState(false);\n const [cartError, setCartError] = useState<string | null>(null);\n\n const bundle =\n result.status === \"success\" && result.bundle.bundleType === \"volume\"\n ? (result.bundle as VolumeBundleData)\n : null;\n\n // Apply merchant-config CSS vars to the component root via a callback\n // ref composed with elementRef.\n const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);\n\n useEffect(() => {\n if (result.status === \"error\") {\n onError?.(result.error);\n return;\n }\n if (\n result.status === \"success\" &&\n result.bundle.bundleType !== \"volume\"\n ) {\n onError?.(\n new Error(\n `VolumeBundle: expected bundleType=\"volume\", got \"${result.bundle.bundleType}\"`,\n ),\n );\n }\n }, [result, onError]);\n\n const product = bundle?.products[0];\n const basePrice = product\n ? parseFloat(product.priceRange.minVariantPrice.amount)\n : 0;\n const currency = product?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const tierSavings = bundle\n ? calculateTierSavings(\n bundle.volumeTiers,\n basePrice,\n quantity,\n bundle.discountConfig.discountType,\n )\n : [];\n const activeTier = bundle\n ? getActiveTier(bundle.volumeTiers, quantity)\n : null;\n\n const handleAddToCart = useCallback(async () => {\n if (!bundle || !product) return;\n\n const variant = product.variants.nodes.find((v) => v.availableForSale);\n if (!variant) return;\n\n const lines: CartLineInput[] = [\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 setAddingToCart(true);\n setCartError(null);\n try {\n await onAddToCart(lines);\n // Reuse the canonical computation from calculateTierSavings so the\n // analytics unit price matches what the component rendered.\n const unitPrice =\n tierSavings.find((ts) => ts.tier === activeTier)?.unitPrice ?? basePrice;\n trackAddToCart({\n productId: product.id,\n quantity,\n totalPrice: Math.round(unitPrice * quantity * 100) / 100,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setCartError(error.message);\n onError?.(error);\n } finally {\n setAddingToCart(false);\n }\n }, [\n bundle,\n product,\n quantity,\n activeTier,\n basePrice,\n onAddToCart,\n onError,\n trackAddToCart,\n ]);\n\n if (result.status === \"loading\") {\n return (\n <div className={`lb-bundle lb-bundle--loading ${className ?? \"\"}`}>\n <div className=\"lb-skeleton lb-skeleton--title\" />\n <div className=\"lb-skeleton lb-skeleton--tiers\" />\n </div>\n );\n }\n\n if (result.status === \"error\") return null;\n if (!bundle) return null;\n if (!product) return null;\n\n return (\n <div\n ref={(el) => {\n elementRef(el);\n setConfigVarsRef(el);\n }}\n className={`lb-bundle lb-bundle--volume ${className ?? \"\"}`}\n role=\"region\"\n aria-label={bundle.title}\n >\n <h3 className=\"lb-bundle__title\">{bundle.title}</h3>\n\n <div className=\"lb-bundle__product lb-bundle__product--volume\">\n {product.featuredImage && (\n <img\n src={product.featuredImage.url}\n alt={product.featuredImage.altText ?? product.title}\n className=\"lb-bundle__product-image\"\n loading=\"lazy\"\n />\n )}\n <div className=\"lb-bundle__product-info\">\n <p className=\"lb-bundle__product-title\">{product.title}</p>\n <p className=\"lb-bundle__product-price\">\n {formatMoney(basePrice, currency)} each\n </p>\n {(() => {\n // Volume bundles surface the first available variant — read unit\n // pricing off the same variant so the row matches what's bought.\n const variant =\n product.variants.nodes.find((v) => v.availableForSale) ??\n product.variants.nodes[0];\n const unitPriceText = variant\n ? formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n )\n : null;\n return unitPriceText ? (\n <p className=\"lb-bundle__product-unit-price\">{unitPriceText}</p>\n ) : null;\n })()}\n </div>\n </div>\n\n <div\n className=\"lb-bundle__tiers\"\n role=\"table\"\n aria-label=\"Volume discounts\"\n >\n {tierSavings.map((ts) => (\n <div\n key={ts.tier.minQuantity}\n className={`lb-bundle__tier ${ts.isActive ? \"lb-bundle__tier--active\" : \"\"}`}\n role=\"row\"\n >\n <span className=\"lb-bundle__tier-quantity\" role=\"cell\">\n {ts.tier.minQuantity}+ items\n </span>\n <span className=\"lb-bundle__tier-price\" role=\"cell\">\n {formatMoney(ts.unitPrice, currency)} each\n </span>\n <span className=\"lb-bundle__tier-savings\" role=\"cell\">\n Save {ts.savingsPercent.toFixed(0)}%\n </span>\n </div>\n ))}\n </div>\n\n <div className=\"lb-bundle__quantity-selector\">\n <label htmlFor={`lb-qty-${bundle.id}`}>Quantity</label>\n <div className=\"lb-bundle__quantity-control\">\n <button\n aria-label=\"Decrease quantity\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n >\n −\n </button>\n <input\n id={`lb-qty-${bundle.id}`}\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => {\n const val = parseInt(e.target.value, 10);\n if (!isNaN(val) && val > 0) setQuantity(val);\n }}\n className=\"lb-bundle__quantity-input\"\n />\n <button\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((q) => q + 1)}\n >\n +\n </button>\n </div>\n </div>\n\n {cartError && (\n <p className=\"lb-bundle__error\" role=\"alert\">\n {cartError}\n </p>\n )}\n\n <button\n className=\"lb-bundle__cta\"\n onClick={handleAddToCart}\n disabled={addingToCart}\n aria-busy={addingToCart}\n >\n {addingToCart\n ? \"Adding...\"\n : (bundle.widgetConfig.cta.ctaText ?? `Add ${quantity} to Cart`)}\n </button>\n </div>\n );\n}\n","/**\n * Client-side React hook wrapping `fetchBundlesForProduct` — the\n * React-SDK analogue of the web component's auto-detect-by-current-product\n * mode. The web component reads the handle from `location.pathname`; a\n * React app already has it on the router, so the hook takes it as an\n * explicit `productHandle` option.\n *\n * Returns every active bundle configured against the product. Inactive,\n * draft, scheduled-for-later, and expired bundles are filtered out by\n * the core helper — consumers don't have to guard.\n *\n * For SSR / RSC contexts (Hydrogen loaders, Next.js server components),\n * prefer `fetchBundlesForProduct` directly and pass the list to a client\n * component as a prop. This hook's only value-add over the async\n * function is React state management + abort-on-unmount.\n *\n * `UseBundlesForProductResult` is a discriminated union locked at v2.0.0:\n * narrowing via `status` is safe. Widening or adding states is a\n * breaking change.\n */\nimport { useState, useEffect } from \"react\";\nimport {\n fetchBundlesForProduct,\n injectCustomCss,\n type ParsedBundle,\n} from \"@lime-bundles/core\";\nimport { fetchShopCustomCss } from \"../fetchShopCustomCss\";\n\nexport interface UseBundlesForProductOptions {\n shopDomain: string;\n storefrontAccessToken: string;\n productHandle: string;\n /** Forwarded as `Shopify-Storefront-Buyer-IP` — required for SSR paths. */\n buyerIp?: string;\n}\n\nexport type UseBundlesForProductResult =\n | { status: \"loading\"; bundles: null; error: null }\n | { status: \"success\"; bundles: ParsedBundle[]; error: null }\n | { status: \"error\"; bundles: null; error: Error };\n\nconst INITIAL_STATE: UseBundlesForProductResult = {\n status: \"loading\",\n bundles: null,\n error: null,\n};\n\nexport function useBundlesForProduct(\n options: UseBundlesForProductOptions,\n): UseBundlesForProductResult {\n const [state, setState] = useState<UseBundlesForProductResult>(\n INITIAL_STATE,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n setState(INITIAL_STATE);\n\n fetchBundlesForProduct({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n productHandle: options.productHandle,\n buyerIp: options.buyerIp,\n signal: controller.signal,\n })\n .then((bundles) => {\n if (controller.signal.aborted) return;\n setState({ status: \"success\", bundles, error: null });\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n bundles: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n });\n\n // Fire-and-forget custom CSS fetch so storefronts using this hook get\n // merchant styling automatically — same behaviour as `useBundleData`.\n fetchShopCustomCss({\n shopDomain: options.shopDomain,\n storefrontAccessToken: options.storefrontAccessToken,\n signal: controller.signal,\n })\n .then((css) => {\n if (controller.signal.aborted) return;\n injectCustomCss(options.shopDomain, css);\n })\n .catch(() => {\n // Silent — no merchant CSS is fine.\n });\n\n return () => {\n controller.abort();\n };\n }, [\n options.shopDomain,\n options.storefrontAccessToken,\n options.productHandle,\n options.buyerIp,\n ]);\n\n return state;\n}\n","// Bundle components — BYO-cart via `onAddToCart`.\nexport { FixedBundle } from \"./components/FixedBundle\";\nexport { MixMatchBundle } from \"./components/MixMatchBundle\";\nexport { VolumeBundle } from \"./components/VolumeBundle\";\nexport type { BundleComponentProps } from \"./components/types\";\n\n// Data fetching\nexport {\n fetchBundleData,\n type FetchBundleDataOptions,\n} from \"./fetchBundleData\";\nexport {\n fetchShopCustomCss,\n type FetchShopCustomCssOptions,\n} from \"./fetchShopCustomCss\";\nexport {\n useBundleData,\n type UseBundleDataOptions,\n type UseBundleDataResult,\n} from \"./hooks/useBundleData\";\nexport {\n useBundlesForProduct,\n type UseBundlesForProductOptions,\n type UseBundlesForProductResult,\n} from \"./hooks/useBundlesForProduct\";\nexport { useAnalytics } from \"./hooks/useAnalytics\";\nexport { useWidgetConfigVars } from \"./hooks/useWidgetConfigVars\";\n\n// Re-export core types consumers commonly need.\nexport type {\n ParsedBundle,\n FixedBundleData,\n VolumeBundleData,\n MixMatchBundleData,\n BundleType,\n Product,\n ProductVariant,\n CartLineInput,\n WidgetConfig,\n HeaderConfig,\n LayoutConfig,\n ProductListConfig,\n ThumbnailRatio,\n PricingConfig,\n SavingsBarConfig,\n CtaConfig,\n CountdownConfig,\n PopularBadgeConfig,\n VolumeTier,\n DiscountConfig,\n ABVariantOverrides,\n PricingRow,\n FixedBundlePricing,\n ImageTransform,\n} from \"@lime-bundles/core\";\nexport { BundleParseError, StorefrontApiError } from \"@lime-bundles/core\";\n\n// DIY widget primitives — every helper a developer needs to build their\n// own bundle UI with the same data, math, and theming the first-party\n// widget uses. All live in `@lime-bundles/core`; importing from the\n// React package is shorthand for consumers who already pulled `react`.\nexport {\n // Bundle config + variant merging\n WIDGET_CONFIG_DEFAULTS,\n mergeWidgetConfig,\n applyWidgetConfigVars,\n thumbnailRatioVars,\n applyABVariantB,\n // Pricing (integer cents, matches Shopify Discount Function rounding)\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n // Volume-tier calculation\n calculateTierSavings,\n getActiveTier,\n // Mix-match quantity validation\n validateQuantity,\n // Image / countdown / money helpers\n transformImageUrl,\n formatCountdown,\n formatMoney,\n formatUnitPrice,\n calculateDiscount,\n // Analytics (optional — FixedBundle / VolumeBundle / MixMatchBundle\n // auto-fire these unless analyticsEnabled is set to false)\n reportImpression,\n reportAddToCart,\n observeImpression,\n // A/B testing\n getABTestAssignment,\n setConsent,\n hasConsent,\n // CSS injection + sanitizer\n injectCustomCss,\n sanitizeCustomCss,\n // Custom-storefront data fetching\n createStorefrontClient,\n fetchBundlesForProduct,\n parseMetaobjectBundle,\n parseMetaobjectBundleStrict,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n SHOP_CUSTOM_CSS_QUERY,\n} from \"@lime-bundles/core\";\n"],"mappings":"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","names":["useCallback","useEffect","useState","createStorefrontClient","useEffect","useState","useCallback","useEffect","useRef","useState","useEffect","useCallback","useCallback","useEffect","useState","formatMoney","formatUnitPrice","resolveBundleQty","jsx","jsxs","useState","useEffect","useCallback","resolveBundleQty","formatMoney","formatUnitPrice","useCallback","useEffect","useState","formatMoney","formatUnitPrice","jsx","jsxs","useState","useEffect","useCallback","formatMoney","formatUnitPrice","useState","useEffect","injectCustomCss","INITIAL_STATE","useState","useEffect","injectCustomCss","BundleParseError","applyWidgetConfigVars","calculateTierSavings","getActiveTier","validateQuantity","formatMoney","formatUnitPrice","calculateDiscount","reportImpression","reportAddToCart","observeImpression","injectCustomCss","createStorefrontClient","fetchBundlesForProduct","parseMetaobjectBundleStrict","BUNDLE_METAOBJECT_QUERY","SHOP_CUSTOM_CSS_QUERY"]}