@behio/storefront-sdk 1.19.0 → 2.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/react.js CHANGED
@@ -53,6 +53,7 @@ __export(react_exports, {
53
53
  useCategory: () => useCategory,
54
54
  useCertificateVerification: () => useCertificateVerification,
55
55
  useCheckout: () => useCheckout,
56
+ useCheckoutPreview: () => useCheckoutPreview,
56
57
  useCookieConsent: () => useCookieConsent,
57
58
  useCourse: () => useCourse,
58
59
  useCourseCertificates: () => useCourseCertificates,
@@ -259,6 +260,7 @@ var BehioStorefront = class {
259
260
  this.shopDomain = config.shopDomain;
260
261
  this.defaultLocale = config.locale;
261
262
  this.defaultCurrency = config.currency;
263
+ this.setCountry(config.country);
262
264
  this.fetchFn = config.fetch || globalThis.fetch.bind(globalThis);
263
265
  this.timeout = config.timeout ?? 3e4;
264
266
  this.retries = config.retries ?? 1;
@@ -420,6 +422,14 @@ var BehioStorefront = class {
420
422
  getCurrency() {
421
423
  return this.defaultCurrency;
422
424
  }
425
+ /** Set the catalog's delivery country. Use cart.setDestination separately for the cart. */
426
+ setCountry(country) {
427
+ const normalized = country?.trim().toUpperCase();
428
+ this.defaultCountry = normalized && /^[A-Z]{2}$/.test(normalized) ? normalized : void 0;
429
+ }
430
+ getCountry() {
431
+ return this.defaultCountry;
432
+ }
423
433
  /** Set the default locale sent on every catalog request (per-call wins). */
424
434
  setLocale(locale) {
425
435
  this.defaultLocale = locale || void 0;
@@ -575,12 +585,16 @@ var BehioStorefront = class {
575
585
  if (this.defaultCurrency && !params.has("currency")) {
576
586
  params.set("currency", this.defaultCurrency);
577
587
  }
588
+ if (this.defaultCountry && path.startsWith("/catalog/") && !params.has("country")) {
589
+ params.set("country", this.defaultCountry);
590
+ }
578
591
  const qs = params.toString();
579
592
  const url = `${this.baseUrl}/storefront/v1${path}${qs ? `?${qs}` : ""}`;
580
593
  const headers = {
581
594
  "X-Api-Key": this.apiKey,
582
595
  "Content-Type": "application/json"
583
596
  };
597
+ if (path === "/cart" || path.startsWith("/cart/")) headers["X-Behio-Cart-Contract"] = "2";
584
598
  if (this.shopDomain) {
585
599
  headers["X-Shop-Domain"] = this.shopDomain;
586
600
  }
@@ -707,42 +721,46 @@ var BehioStorefront = class {
707
721
  throw new BehioNetworkError("Request failed after retries");
708
722
  }
709
723
  };
724
+ function catalogProductsQuery(query) {
725
+ const q = {};
726
+ if (query) {
727
+ if (query.page !== void 0) q.page = query.page;
728
+ if (query.limit !== void 0) q.limit = query.limit;
729
+ if (query.category) q.category = query.category;
730
+ if (query.label) q.label = query.label;
731
+ if (query.priceMin !== void 0) q.priceMin = query.priceMin;
732
+ if (query.priceMax !== void 0) q.priceMax = query.priceMax;
733
+ if (query.currency) q.currency = query.currency;
734
+ if (query.country) q.country = query.country;
735
+ if (query.locale) q.locale = query.locale;
736
+ if (query.sort) q.sort = query.sort;
737
+ if (query.inStock !== void 0) q.inStock = query.inStock;
738
+ if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
739
+ if (query.search) q.search = query.search;
740
+ if (query.parameters) q.parameters = JSON.stringify(query.parameters);
741
+ if (query.facets) q.facets = JSON.stringify(query.facets);
742
+ if (query.ids && query.ids.length > 0) q.ids = query.ids;
743
+ if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
744
+ if (query.labels && query.labels.length > 0) q.labels = query.labels;
745
+ if (query.categories && query.categories.length > 0)
746
+ q.categories = query.categories;
747
+ if (query.excludeIds && query.excludeIds.length > 0)
748
+ q.excludeIds = query.excludeIds;
749
+ if (query.excludeCategories && query.excludeCategories.length > 0)
750
+ q.excludeCategories = query.excludeCategories;
751
+ if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
752
+ if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
753
+ if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
754
+ }
755
+ return q;
756
+ }
710
757
  var CatalogModule = class {
711
758
  constructor(client) {
712
759
  this.client = client;
713
760
  }
714
761
  /** List products with filtering, pagination, search */
715
762
  async getProducts(query) {
716
- const q = {};
717
- if (query) {
718
- if (query.page) q.page = query.page;
719
- if (query.limit) q.limit = query.limit;
720
- if (query.category) q.category = query.category;
721
- if (query.label) q.label = query.label;
722
- if (query.priceMin) q.priceMin = query.priceMin;
723
- if (query.priceMax) q.priceMax = query.priceMax;
724
- if (query.currency) q.currency = query.currency;
725
- if (query.country) q.country = query.country;
726
- if (query.locale) q.locale = query.locale;
727
- if (query.sort) q.sort = query.sort;
728
- if (query.inStock !== void 0) q.inStock = query.inStock;
729
- if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
730
- if (query.search) q.search = query.search;
731
- if (query.parameters) q.parameters = JSON.stringify(query.parameters);
732
- if (query.facets) q.facets = JSON.stringify(query.facets);
733
- if (query.ids && query.ids.length > 0) q.ids = query.ids;
734
- if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
735
- if (query.labels && query.labels.length > 0) q.labels = query.labels;
736
- if (query.categories && query.categories.length > 0)
737
- q.categories = query.categories;
738
- if (query.excludeIds && query.excludeIds.length > 0)
739
- q.excludeIds = query.excludeIds;
740
- if (query.excludeCategories && query.excludeCategories.length > 0)
741
- q.excludeCategories = query.excludeCategories;
742
- if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
743
- if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
744
- if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
745
- }
763
+ const q = catalogProductsQuery(query);
746
764
  return this.client.request(
747
765
  "GET",
748
766
  "/catalog/products",
@@ -784,14 +802,7 @@ var CatalogModule = class {
784
802
  }
785
803
  /** Get products in a category */
786
804
  async getCategoryProducts(slug, query) {
787
- const q = {};
788
- if (query) {
789
- if (query.page) q.page = query.page;
790
- if (query.limit) q.limit = query.limit;
791
- if (query.sort) q.sort = query.sort;
792
- if (query.locale) q.locale = query.locale;
793
- if (query.currency) q.currency = query.currency;
794
- }
805
+ const q = catalogProductsQuery(query);
795
806
  return this.client.request(
796
807
  "GET",
797
808
  `/catalog/categories/${slug}/products`,
@@ -825,7 +836,7 @@ var CatalogModule = class {
825
836
  "GET",
826
837
  "/catalog/featured",
827
838
  {
828
- query: { locale: options?.locale, currency: options?.currency }
839
+ query: { locale: options?.locale, currency: options?.currency, country: options?.country }
829
840
  }
830
841
  );
831
842
  }
@@ -883,9 +894,10 @@ var CatalogModule = class {
883
894
  if (query) {
884
895
  if (query.category) q.category = query.category;
885
896
  if (query.label) q.label = query.label;
886
- if (query.priceMin) q.priceMin = query.priceMin;
887
- if (query.priceMax) q.priceMax = query.priceMax;
897
+ if (query.priceMin !== void 0) q.priceMin = query.priceMin;
898
+ if (query.priceMax !== void 0) q.priceMax = query.priceMax;
888
899
  if (query.currency) q.currency = query.currency;
900
+ if (query.country) q.country = query.country;
889
901
  if (query.locale) q.locale = query.locale;
890
902
  if (query.inStock !== void 0) q.inStock = query.inStock;
891
903
  if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
@@ -907,12 +919,12 @@ var CatalogModule = class {
907
919
  return this.getProducts({ search: query, ...options });
908
920
  }
909
921
  /** List all active bundles */
910
- async getBundles() {
911
- return this.client.request("GET", "/catalog/bundles");
922
+ async getBundles(options) {
923
+ return this.client.request("GET", "/catalog/bundles", { query: { ...options } });
912
924
  }
913
925
  /** Get a single bundle by slug */
914
- async getBundle(slug) {
915
- return this.client.request("GET", `/catalog/bundles/${slug}`);
926
+ async getBundle(slug, options) {
927
+ return this.client.request("GET", `/catalog/bundles/${encodeURIComponent(slug)}`, { query: { ...options } });
916
928
  }
917
929
  /**
918
930
  * One product group ("collection") by slug with its products as standard
@@ -925,7 +937,7 @@ var CatalogModule = class {
925
937
  "GET",
926
938
  `/catalog/product-groups/${encodeURIComponent(slug)}`,
927
939
  {
928
- query: { locale: options?.locale, currency: options?.currency }
940
+ query: { locale: options?.locale, currency: options?.currency, country: options?.country }
929
941
  }
930
942
  );
931
943
  }
@@ -940,7 +952,7 @@ var CatalogModule = class {
940
952
  return this.client.request(
941
953
  "GET",
942
954
  `/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
943
- { query: { locale: options?.locale, currency: options?.currency } }
955
+ { query: { locale: options?.locale, currency: options?.currency, country: options?.country } }
944
956
  );
945
957
  }
946
958
  /** Active promotions applicable to a product (with countdown end time) */
@@ -1148,6 +1160,7 @@ var CartModule = class {
1148
1160
  body: { currency }
1149
1161
  });
1150
1162
  if (res.error) return res;
1163
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1151
1164
  this.client.emit("cart:updated", res.data);
1152
1165
  return res;
1153
1166
  }
@@ -1168,6 +1181,7 @@ var CartModule = class {
1168
1181
  body: input
1169
1182
  });
1170
1183
  if (res.error) return res;
1184
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1171
1185
  this.client.emit("cart:updated", res.data);
1172
1186
  return res;
1173
1187
  }
@@ -1252,14 +1266,15 @@ var CartModule = class {
1252
1266
  body
1253
1267
  });
1254
1268
  if (res.error) return res;
1269
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1255
1270
  this.client.emit("cart:updated", res.data);
1256
1271
  return res;
1257
1272
  }
1258
- /** Update quantity of a bundle already in the cart */
1259
- async updateBundleQuantity(bundleId, quantity) {
1273
+ /** Update by CartBundleLine.id; legacy catalog bundle IDs are accepted by the API */
1274
+ async updateBundleQuantity(lineId, quantity) {
1260
1275
  const res = await this.client.request(
1261
1276
  "PATCH",
1262
- `/cart/bundles/${bundleId}`,
1277
+ `/cart/bundles/${lineId}`,
1263
1278
  {
1264
1279
  body: { quantity }
1265
1280
  }
@@ -1268,11 +1283,11 @@ var CartModule = class {
1268
1283
  this.client.emit("cart:updated", res.data);
1269
1284
  return res;
1270
1285
  }
1271
- /** Remove a bundle from the cart */
1272
- async removeBundle(bundleId) {
1286
+ /** Remove by CartBundleLine.id; scoped to the current visitor cart */
1287
+ async removeBundle(lineId) {
1273
1288
  const res = await this.client.request(
1274
1289
  "DELETE",
1275
- `/cart/bundles/${bundleId}`
1290
+ `/cart/bundles/${lineId}`
1276
1291
  );
1277
1292
  if (res.error) return res;
1278
1293
  this.client.emit("cart:updated", res.data);
@@ -1306,6 +1321,12 @@ var CheckoutModule = class {
1306
1321
  constructor(client) {
1307
1322
  this.client = client;
1308
1323
  }
1324
+ /** Recalculate the current cart including shipping, fees, benefits and rounding.
1325
+ * No order, stock claim, payment or gift-card redemption is created. Required
1326
+ * legal consents are enforced only on createOrder, not while quoting. */
1327
+ async preview(input) {
1328
+ return this.client.request("POST", "/checkout/preview", { body: input });
1329
+ }
1309
1330
  /** Create order from cart */
1310
1331
  async createOrder(input) {
1311
1332
  const res = await this.client.request("POST", "/checkout", {
@@ -2200,6 +2221,7 @@ function BehioProvider({
2200
2221
  });
2201
2222
  }
2202
2223
  const client = clientRef.current;
2224
+ const [sessionRestored, setSessionRestored] = (0, import_react2.useState)(false);
2203
2225
  (0, import_react2.useEffect)(() => {
2204
2226
  const accessToken = storageAdapter.get(STORAGE_KEYS.ACCESS_TOKEN);
2205
2227
  const refreshToken = storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
@@ -2210,6 +2232,7 @@ function BehioProvider({
2210
2232
  if (cartSession) {
2211
2233
  client.setCartSession(cartSession);
2212
2234
  }
2235
+ setSessionRestored(true);
2213
2236
  }, [client, storageAdapter]);
2214
2237
  (0, import_react2.useEffect)(() => {
2215
2238
  if (!persistCurrency) return;
@@ -2264,12 +2287,13 @@ function BehioProvider({
2264
2287
  () => ({
2265
2288
  client,
2266
2289
  storage: storageAdapter,
2290
+ sessionRestored,
2267
2291
  currency: activeCurrency,
2268
2292
  setCurrency,
2269
2293
  configuredDefaultCurrency: defaultCurrency,
2270
2294
  configuredCurrencies: currencies
2271
2295
  }),
2272
- [client, storageAdapter, activeCurrency, setCurrency, defaultCurrency, currencies]
2296
+ [client, storageAdapter, sessionRestored, activeCurrency, setCurrency, defaultCurrency, currencies]
2273
2297
  );
2274
2298
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.QueryClientProvider, { client: qc, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BehioContext.Provider, { value: ctxValue, children }) });
2275
2299
  }
@@ -2519,6 +2543,15 @@ function useSearch(query, options) {
2519
2543
  // src/react/hooks/use-cart.ts
2520
2544
  var import_react5 = require("react");
2521
2545
  var import_react_query12 = require("@tanstack/react-query");
2546
+
2547
+ // src/react/utils/refresh-bundle-queries.ts
2548
+ async function refreshBundleQueries(queryClient) {
2549
+ const keys = [["behio", "bundle"], ["behio", "bundles"]];
2550
+ await Promise.all(keys.map((queryKey) => queryClient.cancelQueries({ queryKey })));
2551
+ await Promise.all(keys.map((queryKey) => queryClient.invalidateQueries({ queryKey })));
2552
+ }
2553
+
2554
+ // src/react/hooks/use-cart.ts
2522
2555
  var CART_KEY = ["behio", "cart"];
2523
2556
  function useCart(options) {
2524
2557
  const { client, storage } = useBehio();
@@ -2542,32 +2575,21 @@ function useCart(options) {
2542
2575
  storage.set(STORAGE_KEYS.CART_SESSION, result.newSessionToken);
2543
2576
  }
2544
2577
  queryClient.setQueryData([...CART_KEY], result);
2578
+ void refreshBundleQueries(queryClient);
2545
2579
  }
2546
2580
  });
2581
+ const cancelCartRead = () => queryClient.cancelQueries({ queryKey: [...CART_KEY] });
2582
+ const acceptCart = async (result) => {
2583
+ await cancelCartRead();
2584
+ queryClient.setQueryData([...CART_KEY], result);
2585
+ void refreshBundleQueries(queryClient);
2586
+ };
2547
2587
  const updateMutation = (0, import_react_query12.useMutation)({
2548
2588
  mutationFn: async ({ itemId, quantity }) => {
2549
2589
  return unwrap(client.cart.updateQuantity(itemId, quantity));
2550
2590
  },
2551
- onMutate: async ({ itemId, quantity }) => {
2552
- await queryClient.cancelQueries({ queryKey: [...CART_KEY] });
2553
- const previous = queryClient.getQueryData([...CART_KEY]);
2554
- if (previous) {
2555
- const updatedItems = previous.items.map(
2556
- (item) => item.id === itemId ? { ...item, quantity, totalPrice: item.unitPrice * quantity } : item
2557
- );
2558
- queryClient.setQueryData([...CART_KEY], {
2559
- ...previous,
2560
- items: updatedItems,
2561
- itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
2562
- });
2563
- }
2564
- return { previous };
2565
- },
2566
- onError: (_err, _vars, context) => {
2567
- if (context?.previous) {
2568
- queryClient.setQueryData([...CART_KEY], context.previous);
2569
- }
2570
- },
2591
+ onMutate: cancelCartRead,
2592
+ onSuccess: acceptCart,
2571
2593
  onSettled: () => {
2572
2594
  queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
2573
2595
  }
@@ -2576,24 +2598,8 @@ function useCart(options) {
2576
2598
  mutationFn: async (itemId) => {
2577
2599
  return unwrap(client.cart.removeItem(itemId));
2578
2600
  },
2579
- onMutate: async (itemId) => {
2580
- await queryClient.cancelQueries({ queryKey: [...CART_KEY] });
2581
- const previous = queryClient.getQueryData([...CART_KEY]);
2582
- if (previous) {
2583
- const updatedItems = previous.items.filter((item) => item.id !== itemId);
2584
- queryClient.setQueryData([...CART_KEY], {
2585
- ...previous,
2586
- items: updatedItems,
2587
- itemCount: updatedItems.reduce((sum, i) => sum + i.quantity, 0)
2588
- });
2589
- }
2590
- return { previous };
2591
- },
2592
- onError: (_err, _vars, context) => {
2593
- if (context?.previous) {
2594
- queryClient.setQueryData([...CART_KEY], context.previous);
2595
- }
2596
- },
2601
+ onMutate: cancelCartRead,
2602
+ onSuccess: acceptCart,
2597
2603
  onSettled: () => {
2598
2604
  queryClient.invalidateQueries({ queryKey: [...CART_KEY] });
2599
2605
  }
@@ -2604,6 +2610,7 @@ function useCart(options) {
2604
2610
  queryClient.setQueryData([...CART_KEY], null);
2605
2611
  client.clearCartSession();
2606
2612
  storage.remove(STORAGE_KEYS.CART_SESSION);
2613
+ void refreshBundleQueries(queryClient);
2607
2614
  }
2608
2615
  });
2609
2616
  const applyDiscountMutation = (0, import_react_query12.useMutation)({
@@ -2624,6 +2631,7 @@ function useCart(options) {
2624
2631
  queryClient.setQueryData([...CART_KEY], result);
2625
2632
  client.clearCartSession();
2626
2633
  storage.remove(STORAGE_KEYS.CART_SESSION);
2634
+ void refreshBundleQueries(queryClient);
2627
2635
  }
2628
2636
  });
2629
2637
  const addItem = (0, import_react5.useCallback)(
@@ -2668,7 +2676,7 @@ function useCart(options) {
2668
2676
  merge,
2669
2677
  // Computed
2670
2678
  itemCount: cart?.itemCount ?? 0,
2671
- isEmpty: !cart || cart.items.length === 0,
2679
+ isEmpty: !cart || cart.items.length === 0 && (cart.bundleLines?.length ?? 0) === 0,
2672
2680
  // Mutation states
2673
2681
  isAdding: addMutation.isPending,
2674
2682
  isUpdating: updateMutation.isPending,
@@ -2724,6 +2732,7 @@ function useAuth() {
2724
2732
  queryClient.setQueryData([...CUSTOMER_KEY], null);
2725
2733
  queryClient.invalidateQueries({ queryKey: ["behio", "orders"] });
2726
2734
  queryClient.invalidateQueries({ queryKey: ["behio", "addresses"] });
2735
+ void refreshBundleQueries(queryClient);
2727
2736
  }, [client, storage, queryClient]);
2728
2737
  const postAuth = (0, import_react6.useCallback)(async () => {
2729
2738
  await queryClient.invalidateQueries({ queryKey: [...CUSTOMER_KEY] });
@@ -2736,6 +2745,7 @@ function useAuth() {
2736
2745
  }
2737
2746
  }
2738
2747
  queryClient.invalidateQueries({ queryKey: [...CART_KEY3] });
2748
+ void refreshBundleQueries(queryClient);
2739
2749
  }, [client, storage, queryClient]);
2740
2750
  const loginMutation = (0, import_react_query14.useMutation)({
2741
2751
  mutationFn: async ({ email, password }) => {
@@ -3699,6 +3709,11 @@ function useCheckout() {
3699
3709
  reset
3700
3710
  };
3701
3711
  }
3712
+ function useCheckoutPreview() {
3713
+ const { client } = useBehio();
3714
+ const mutation = (0, import_react_query34.useMutation)({ mutationFn: (input) => unwrap(client.checkout.preview(input)) });
3715
+ return { preview: mutation.mutateAsync, data: mutation.data, isPreviewing: mutation.isPending, error: mutation.error, reset: mutation.reset };
3716
+ }
3702
3717
 
3703
3718
  // src/react/hooks/use-pages.ts
3704
3719
  var import_react_query35 = require("@tanstack/react-query");
@@ -3863,7 +3878,8 @@ function useShopScripts(options) {
3863
3878
  return (0, import_react_query39.useQuery)({
3864
3879
  queryKey: ["behio", "shop-scripts"],
3865
3880
  queryFn: () => unwrap(client.getShopScripts()),
3866
- enabled: options?.enabled !== false
3881
+ enabled: options?.enabled !== false,
3882
+ initialData: options?.initialData
3867
3883
  });
3868
3884
  }
3869
3885
 
@@ -3997,31 +4013,32 @@ function partsFor(s) {
3997
4013
  return [{ target: "head", html: `<meta name="google-site-verification" content="${id}" />` }];
3998
4014
  case "RAW":
3999
4015
  default:
4000
- return [{ target: s.placement === "HEAD" ? "head" : "body", html: s.value }];
4016
+ return [{ target: s.placement === "HEAD" ? "head" : "body", start: s.placement === "BODY_START", html: s.value }];
4001
4017
  }
4002
4018
  }
4003
- function injectHtml(target, html) {
4019
+ function injectHtml(target, html, start = false) {
4004
4020
  const tpl = document.createElement("template");
4005
4021
  tpl.innerHTML = html;
4006
4022
  const added = [];
4023
+ const anchor = start ? target.firstChild : null;
4007
4024
  Array.from(tpl.content.childNodes).forEach((node) => {
4008
4025
  if (node.nodeName === "SCRIPT") {
4009
4026
  const old = node;
4010
4027
  const script = document.createElement("script");
4011
4028
  for (const attr of Array.from(old.attributes)) script.setAttribute(attr.name, attr.value);
4012
4029
  script.text = old.text;
4013
- target.appendChild(script);
4030
+ target.insertBefore(script, anchor);
4014
4031
  added.push(script);
4015
4032
  } else if (node.nodeType === Node.ELEMENT_NODE) {
4016
4033
  const clone = node.cloneNode(true);
4017
- target.appendChild(clone);
4034
+ target.insertBefore(clone, anchor);
4018
4035
  added.push(clone);
4019
4036
  }
4020
4037
  });
4021
4038
  return added;
4022
4039
  }
4023
- function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
4024
- const { data } = useShopScripts();
4040
+ function StorefrontScripts({ visitorId: visitorIdProp, initialScripts } = {}) {
4041
+ const { data } = useShopScripts({ enabled: initialScripts === void 0, initialData: initialScripts ? { scripts: initialScripts } : void 0 });
4025
4042
  const [visitorId, setVisitorId] = (0, import_react18.useState)(visitorIdProp ?? "");
4026
4043
  (0, import_react18.useEffect)(() => {
4027
4044
  if (visitorIdProp) return;
@@ -4033,21 +4050,30 @@ function StorefrontScripts({ visitorId: visitorIdProp } = {}) {
4033
4050
  }, [visitorIdProp]);
4034
4051
  const { data: consent } = useCookieConsent(visitorId || void 0);
4035
4052
  const analyticsOk = Boolean(consent?.analytics);
4053
+ const marketingOk = Boolean(consent?.marketing);
4036
4054
  const scripts = (0, import_react18.useMemo)(
4037
- () => (data?.scripts ?? []).filter((s) => !s.consentRequired || analyticsOk),
4038
- [data, analyticsOk]
4055
+ () => (initialScripts ?? data?.scripts ?? []).filter((s) => !s.consentRequired || (s.type === "META_PIXEL" ? marketingOk : analyticsOk)),
4056
+ [initialScripts, data, analyticsOk, marketingOk]
4039
4057
  );
4040
4058
  const signature = (0, import_react18.useMemo)(
4041
4059
  () => JSON.stringify(scripts.map((s) => [s.id, s.type, s.placement, s.value])),
4042
4060
  [scripts]
4043
4061
  );
4062
+ const activeConsented = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
4044
4063
  (0, import_react18.useEffect)(() => {
4045
4064
  if (typeof document === "undefined") return;
4065
+ const nextConsented = new Set(scripts.filter((s) => s.consentRequired).map((s) => s.id));
4066
+ const revoked = [...activeConsented.current].some((id) => !nextConsented.has(id));
4067
+ activeConsented.current = nextConsented;
4068
+ if (revoked) {
4069
+ window.location.reload();
4070
+ return;
4071
+ }
4046
4072
  const added = [];
4047
4073
  for (const s of scripts) {
4048
4074
  for (const part of partsFor(s)) {
4049
4075
  const target = part.target === "head" ? document.head : document.body;
4050
- added.push(...injectHtml(target, part.html));
4076
+ added.push(...injectHtml(target, part.html, part.start));
4051
4077
  }
4052
4078
  }
4053
4079
  return () => {
@@ -4309,20 +4335,22 @@ function utmFromSearch(search) {
4309
4335
  // src/react/hooks/use-bundles.ts
4310
4336
  var import_react_query42 = require("@tanstack/react-query");
4311
4337
  function useBundles(options) {
4312
- const { client } = useBehio();
4338
+ const { client, sessionRestored } = useBehio();
4339
+ const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
4313
4340
  return (0, import_react_query42.useQuery)({
4314
- queryKey: ["behio", "bundles"],
4315
- queryFn: () => unwrap(client.catalog.getBundles()),
4316
- enabled: options?.enabled ?? true,
4341
+ queryKey: ["behio", "bundles", query],
4342
+ queryFn: () => unwrap(client.catalog.getBundles(query)),
4343
+ enabled: sessionRestored && (options?.enabled ?? true),
4317
4344
  initialData: options?.initialData
4318
4345
  });
4319
4346
  }
4320
4347
  function useBundle(slug, options) {
4321
- const { client } = useBehio();
4348
+ const { client, sessionRestored } = useBehio();
4349
+ const query = { locale: options?.locale ?? client.getLocale(), currency: options?.currency ?? client.getCurrency(), country: options?.country ?? client.getCountry() };
4322
4350
  return (0, import_react_query42.useQuery)({
4323
- queryKey: ["behio", "bundle", slug],
4324
- queryFn: () => unwrap(client.catalog.getBundle(slug)),
4325
- enabled: Boolean(slug) && (options?.enabled ?? true),
4351
+ queryKey: ["behio", "bundle", slug, query],
4352
+ queryFn: () => unwrap(client.catalog.getBundle(slug, query)),
4353
+ enabled: sessionRestored && Boolean(slug) && (options?.enabled ?? true),
4326
4354
  initialData: options?.initialData
4327
4355
  });
4328
4356
  }
@@ -4425,8 +4453,10 @@ var import_react_query48 = require("@tanstack/react-query");
4425
4453
  function useProductReviews(productId, options) {
4426
4454
  const { client } = useBehio();
4427
4455
  return (0, import_react_query48.useQuery)({
4428
- queryKey: ["behio", "reviews", productId, options?.page ?? 1],
4456
+ queryKey: ["behio", "reviews", productId, options?.page ?? 1, options?.limit ?? 20],
4429
4457
  queryFn: () => unwrap(client.reviews.getProductReviews(productId, options?.page, options?.limit)),
4458
+ initialData: options?.initialData,
4459
+ staleTime: 3e4,
4430
4460
  enabled: Boolean(productId) && (options?.enabled ?? true)
4431
4461
  });
4432
4462
  }
@@ -4489,15 +4519,23 @@ function useNotifyWhenAvailable() {
4489
4519
  }
4490
4520
 
4491
4521
  // src/react/utils/format-price.ts
4492
- function formatPrice(amount, currency, locale) {
4522
+ function formatPrice(amount, currency, locale, display) {
4493
4523
  const resolvedLocale = locale ?? "cs";
4494
4524
  try {
4495
- return new Intl.NumberFormat(resolvedLocale, {
4525
+ const formatter = new Intl.NumberFormat(resolvedLocale, {
4496
4526
  style: "currency",
4497
4527
  currency,
4528
+ currencyDisplay: display === "CODE" ? "code" : "symbol",
4498
4529
  minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
4499
4530
  maximumFractionDigits: 2
4500
- }).format(amount);
4531
+ });
4532
+ if (display === "SYMBOL_BEFORE" || display === "SYMBOL_AFTER") {
4533
+ const parts = formatter.formatToParts(amount);
4534
+ const symbol = parts.find((p) => p.type === "currency")?.value ?? currency;
4535
+ const value = parts.filter((p) => p.type !== "currency").map((p) => p.value).join("").trim();
4536
+ return display === "SYMBOL_BEFORE" ? `${symbol}\xA0${value}` : `${value}\xA0${symbol}`;
4537
+ }
4538
+ return formatter.format(amount);
4501
4539
  } catch {
4502
4540
  return `${amount} ${currency}`;
4503
4541
  }
@@ -4631,6 +4669,7 @@ async function revokeAnalyticsConsent(client) {
4631
4669
  useCategory,
4632
4670
  useCertificateVerification,
4633
4671
  useCheckout,
4672
+ useCheckoutPreview,
4634
4673
  useCookieConsent,
4635
4674
  useCourse,
4636
4675
  useCourseCertificates,