@behio/storefront-sdk 1.18.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.
@@ -181,6 +181,7 @@ var BehioStorefront = class {
181
181
  this.shopDomain = config.shopDomain;
182
182
  this.defaultLocale = config.locale;
183
183
  this.defaultCurrency = config.currency;
184
+ this.setCountry(config.country);
184
185
  this.fetchFn = config.fetch || globalThis.fetch.bind(globalThis);
185
186
  this.timeout = _nullishCoalesce(config.timeout, () => ( 3e4));
186
187
  this.retries = _nullishCoalesce(config.retries, () => ( 1));
@@ -342,6 +343,14 @@ var BehioStorefront = class {
342
343
  getCurrency() {
343
344
  return this.defaultCurrency;
344
345
  }
346
+ /** Set the catalog's delivery country. Use cart.setDestination separately for the cart. */
347
+ setCountry(country) {
348
+ const normalized = _optionalChain([country, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4(), 'access', _5 => _5.toUpperCase, 'call', _6 => _6()]);
349
+ this.defaultCountry = normalized && /^[A-Z]{2}$/.test(normalized) ? normalized : void 0;
350
+ }
351
+ getCountry() {
352
+ return this.defaultCountry;
353
+ }
345
354
  /** Set the default locale sent on every catalog request (per-call wins). */
346
355
  setLocale(locale) {
347
356
  this.defaultLocale = locale || void 0;
@@ -356,12 +365,12 @@ var BehioStorefront = class {
356
365
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
357
366
  this.listeners.get(event).add(handler);
358
367
  return () => {
359
- _optionalChain([this, 'access', _3 => _3.listeners, 'access', _4 => _4.get, 'call', _5 => _5(event), 'optionalAccess', _6 => _6.delete, 'call', _7 => _7(handler)]);
368
+ _optionalChain([this, 'access', _7 => _7.listeners, 'access', _8 => _8.get, 'call', _9 => _9(event), 'optionalAccess', _10 => _10.delete, 'call', _11 => _11(handler)]);
360
369
  };
361
370
  }
362
371
  /** @internal Emit an event (fire-and-forget, handler errors are swallowed) */
363
372
  emit(event, data) {
364
- _optionalChain([this, 'access', _8 => _8.listeners, 'access', _9 => _9.get, 'call', _10 => _10(event), 'optionalAccess', _11 => _11.forEach, 'call', _12 => _12((fn) => {
373
+ _optionalChain([this, 'access', _12 => _12.listeners, 'access', _13 => _13.get, 'call', _14 => _14(event), 'optionalAccess', _15 => _15.forEach, 'call', _16 => _16((fn) => {
365
374
  try {
366
375
  fn(data);
367
376
  } catch (e2) {
@@ -477,7 +486,7 @@ var BehioStorefront = class {
477
486
  */
478
487
  async rawRequest(method, path, options) {
479
488
  const params = new URLSearchParams();
480
- if (_optionalChain([options, 'optionalAccess', _13 => _13.query])) {
489
+ if (_optionalChain([options, 'optionalAccess', _17 => _17.query])) {
481
490
  for (const [key, value] of Object.entries(options.query)) {
482
491
  if (value === void 0 || value === null || value === "") continue;
483
492
  if (Array.isArray(value)) {
@@ -497,16 +506,20 @@ var BehioStorefront = class {
497
506
  if (this.defaultCurrency && !params.has("currency")) {
498
507
  params.set("currency", this.defaultCurrency);
499
508
  }
509
+ if (this.defaultCountry && path.startsWith("/catalog/") && !params.has("country")) {
510
+ params.set("country", this.defaultCountry);
511
+ }
500
512
  const qs = params.toString();
501
513
  const url = `${this.baseUrl}/storefront/v1${path}${qs ? `?${qs}` : ""}`;
502
514
  const headers = {
503
515
  "X-Api-Key": this.apiKey,
504
516
  "Content-Type": "application/json"
505
517
  };
518
+ if (path === "/cart" || path.startsWith("/cart/")) headers["X-Behio-Cart-Contract"] = "2";
506
519
  if (this.shopDomain) {
507
520
  headers["X-Shop-Domain"] = this.shopDomain;
508
521
  }
509
- if (this.accessToken && _optionalChain([options, 'optionalAccess', _14 => _14.auth]) !== false) {
522
+ if (this.accessToken && _optionalChain([options, 'optionalAccess', _18 => _18.auth]) !== false) {
510
523
  headers["Authorization"] = `Bearer ${this.accessToken}`;
511
524
  }
512
525
  if (this.cartSession) {
@@ -522,10 +535,10 @@ var BehioStorefront = class {
522
535
  } catch (e4) {
523
536
  }
524
537
  }
525
- if (_optionalChain([options, 'optionalAccess', _15 => _15.headers])) {
538
+ if (_optionalChain([options, 'optionalAccess', _19 => _19.headers])) {
526
539
  Object.assign(headers, options.headers);
527
540
  }
528
- const bodyStr = _optionalChain([options, 'optionalAccess', _16 => _16.body]) ? JSON.stringify(options.body) : void 0;
541
+ const bodyStr = _optionalChain([options, 'optionalAccess', _20 => _20.body]) ? JSON.stringify(options.body) : void 0;
529
542
  let interceptedConfig = {
530
543
  url,
531
544
  method,
@@ -539,7 +552,7 @@ var BehioStorefront = class {
539
552
  for (let attempt = 0; attempt <= this.retries; attempt++) {
540
553
  const controller = new AbortController();
541
554
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
542
- if (_optionalChain([options, 'optionalAccess', _17 => _17.signal])) {
555
+ if (_optionalChain([options, 'optionalAccess', _21 => _21.signal])) {
543
556
  if (options.signal.aborted) {
544
557
  clearTimeout(timeoutId);
545
558
  throw new BehioNetworkError("Request aborted", false);
@@ -590,9 +603,9 @@ var BehioStorefront = class {
590
603
  const apiError = new BehioApiError(
591
604
  res.status,
592
605
  body,
593
- _optionalChain([body, 'optionalAccess', _18 => _18.message]) || `API Error ${res.status}`
606
+ _optionalChain([body, 'optionalAccess', _22 => _22.message]) || `API Error ${res.status}`
594
607
  );
595
- if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess', _19 => _19.auth]) !== false && !_optionalChain([options, 'optionalAccess', _20 => _20._isRetryAfterRefresh])) {
608
+ if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess', _23 => _23.auth]) !== false && !_optionalChain([options, 'optionalAccess', _24 => _24._isRetryAfterRefresh])) {
596
609
  try {
597
610
  await this.handleTokenRefresh();
598
611
  return this.rawRequest(method, path, {
@@ -629,42 +642,46 @@ var BehioStorefront = class {
629
642
  throw new BehioNetworkError("Request failed after retries");
630
643
  }
631
644
  };
645
+ function catalogProductsQuery(query) {
646
+ const q = {};
647
+ if (query) {
648
+ if (query.page !== void 0) q.page = query.page;
649
+ if (query.limit !== void 0) q.limit = query.limit;
650
+ if (query.category) q.category = query.category;
651
+ if (query.label) q.label = query.label;
652
+ if (query.priceMin !== void 0) q.priceMin = query.priceMin;
653
+ if (query.priceMax !== void 0) q.priceMax = query.priceMax;
654
+ if (query.currency) q.currency = query.currency;
655
+ if (query.country) q.country = query.country;
656
+ if (query.locale) q.locale = query.locale;
657
+ if (query.sort) q.sort = query.sort;
658
+ if (query.inStock !== void 0) q.inStock = query.inStock;
659
+ if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
660
+ if (query.search) q.search = query.search;
661
+ if (query.parameters) q.parameters = JSON.stringify(query.parameters);
662
+ if (query.facets) q.facets = JSON.stringify(query.facets);
663
+ if (query.ids && query.ids.length > 0) q.ids = query.ids;
664
+ if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
665
+ if (query.labels && query.labels.length > 0) q.labels = query.labels;
666
+ if (query.categories && query.categories.length > 0)
667
+ q.categories = query.categories;
668
+ if (query.excludeIds && query.excludeIds.length > 0)
669
+ q.excludeIds = query.excludeIds;
670
+ if (query.excludeCategories && query.excludeCategories.length > 0)
671
+ q.excludeCategories = query.excludeCategories;
672
+ if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
673
+ if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
674
+ if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
675
+ }
676
+ return q;
677
+ }
632
678
  var CatalogModule = class {
633
679
  constructor(client) {
634
680
  this.client = client;
635
681
  }
636
682
  /** List products with filtering, pagination, search */
637
683
  async getProducts(query) {
638
- const q = {};
639
- if (query) {
640
- if (query.page) q.page = query.page;
641
- if (query.limit) q.limit = query.limit;
642
- if (query.category) q.category = query.category;
643
- if (query.label) q.label = query.label;
644
- if (query.priceMin) q.priceMin = query.priceMin;
645
- if (query.priceMax) q.priceMax = query.priceMax;
646
- if (query.currency) q.currency = query.currency;
647
- if (query.country) q.country = query.country;
648
- if (query.locale) q.locale = query.locale;
649
- if (query.sort) q.sort = query.sort;
650
- if (query.inStock !== void 0) q.inStock = query.inStock;
651
- if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
652
- if (query.search) q.search = query.search;
653
- if (query.parameters) q.parameters = JSON.stringify(query.parameters);
654
- if (query.facets) q.facets = JSON.stringify(query.facets);
655
- if (query.ids && query.ids.length > 0) q.ids = query.ids;
656
- if (query.slugs && query.slugs.length > 0) q.slugs = query.slugs;
657
- if (query.labels && query.labels.length > 0) q.labels = query.labels;
658
- if (query.categories && query.categories.length > 0)
659
- q.categories = query.categories;
660
- if (query.excludeIds && query.excludeIds.length > 0)
661
- q.excludeIds = query.excludeIds;
662
- if (query.excludeCategories && query.excludeCategories.length > 0)
663
- q.excludeCategories = query.excludeCategories;
664
- if (query.hasDiscount !== void 0) q.hasDiscount = query.hasDiscount;
665
- if (query.isFeatured !== void 0) q.isFeatured = query.isFeatured;
666
- if (query.createdAfter !== void 0) q.createdAfter = query.createdAfter;
667
- }
684
+ const q = catalogProductsQuery(query);
668
685
  return this.client.request(
669
686
  "GET",
670
687
  "/catalog/products",
@@ -677,7 +694,7 @@ var CatalogModule = class {
677
694
  "GET",
678
695
  `/catalog/products/${slug}`,
679
696
  {
680
- query: { locale: _optionalChain([options, 'optionalAccess', _21 => _21.locale]), currency: _optionalChain([options, 'optionalAccess', _22 => _22.currency]), country: _optionalChain([options, 'optionalAccess', _23 => _23.country]) }
697
+ query: { locale: _optionalChain([options, 'optionalAccess', _25 => _25.locale]), currency: _optionalChain([options, 'optionalAccess', _26 => _26.currency]), country: _optionalChain([options, 'optionalAccess', _27 => _27.country]) }
681
698
  }
682
699
  );
683
700
  }
@@ -706,14 +723,7 @@ var CatalogModule = class {
706
723
  }
707
724
  /** Get products in a category */
708
725
  async getCategoryProducts(slug, query) {
709
- const q = {};
710
- if (query) {
711
- if (query.page) q.page = query.page;
712
- if (query.limit) q.limit = query.limit;
713
- if (query.sort) q.sort = query.sort;
714
- if (query.locale) q.locale = query.locale;
715
- if (query.currency) q.currency = query.currency;
716
- }
726
+ const q = catalogProductsQuery(query);
717
727
  return this.client.request(
718
728
  "GET",
719
729
  `/catalog/categories/${slug}/products`,
@@ -731,7 +741,7 @@ var CatalogModule = class {
731
741
  "GET",
732
742
  `/catalog/menu/${encodeURIComponent(handle)}`,
733
743
  {
734
- query: { locale: _optionalChain([options, 'optionalAccess', _24 => _24.locale]) }
744
+ query: { locale: _optionalChain([options, 'optionalAccess', _28 => _28.locale]) }
735
745
  }
736
746
  );
737
747
  }
@@ -747,7 +757,7 @@ var CatalogModule = class {
747
757
  "GET",
748
758
  "/catalog/featured",
749
759
  {
750
- query: { locale: _optionalChain([options, 'optionalAccess', _25 => _25.locale]), currency: _optionalChain([options, 'optionalAccess', _26 => _26.currency]) }
760
+ query: { locale: _optionalChain([options, 'optionalAccess', _29 => _29.locale]), currency: _optionalChain([options, 'optionalAccess', _30 => _30.currency]), country: _optionalChain([options, 'optionalAccess', _31 => _31.country]) }
751
761
  }
752
762
  );
753
763
  }
@@ -762,7 +772,7 @@ var CatalogModule = class {
762
772
  return this.client.request(
763
773
  "GET",
764
774
  "/catalog/filters",
765
- { query: { locale: _optionalChain([options, 'optionalAccess', _27 => _27.locale]) } }
775
+ { query: { locale: _optionalChain([options, 'optionalAccess', _32 => _32.locale]) } }
766
776
  );
767
777
  }
768
778
  /**
@@ -777,7 +787,7 @@ var CatalogModule = class {
777
787
  return this.client.request(
778
788
  "GET",
779
789
  `/catalog/products/${slug}/parameters`,
780
- { query: { locale: _optionalChain([options, 'optionalAccess', _28 => _28.locale]) } }
790
+ { query: { locale: _optionalChain([options, 'optionalAccess', _33 => _33.locale]) } }
781
791
  );
782
792
  }
783
793
  /**
@@ -789,7 +799,7 @@ var CatalogModule = class {
789
799
  return this.client.request(
790
800
  "GET",
791
801
  `/catalog/products/${slug}/parameters/${groupSlug}`,
792
- { query: { locale: _optionalChain([options, 'optionalAccess', _29 => _29.locale]) } }
802
+ { query: { locale: _optionalChain([options, 'optionalAccess', _34 => _34.locale]) } }
793
803
  );
794
804
  }
795
805
  /**
@@ -805,9 +815,10 @@ var CatalogModule = class {
805
815
  if (query) {
806
816
  if (query.category) q.category = query.category;
807
817
  if (query.label) q.label = query.label;
808
- if (query.priceMin) q.priceMin = query.priceMin;
809
- if (query.priceMax) q.priceMax = query.priceMax;
818
+ if (query.priceMin !== void 0) q.priceMin = query.priceMin;
819
+ if (query.priceMax !== void 0) q.priceMax = query.priceMax;
810
820
  if (query.currency) q.currency = query.currency;
821
+ if (query.country) q.country = query.country;
811
822
  if (query.locale) q.locale = query.locale;
812
823
  if (query.inStock !== void 0) q.inStock = query.inStock;
813
824
  if (query.ratingMin !== void 0) q.ratingMin = query.ratingMin;
@@ -829,12 +840,12 @@ var CatalogModule = class {
829
840
  return this.getProducts({ search: query, ...options });
830
841
  }
831
842
  /** List all active bundles */
832
- async getBundles() {
833
- return this.client.request("GET", "/catalog/bundles");
843
+ async getBundles(options) {
844
+ return this.client.request("GET", "/catalog/bundles", { query: { ...options } });
834
845
  }
835
846
  /** Get a single bundle by slug */
836
- async getBundle(slug) {
837
- return this.client.request("GET", `/catalog/bundles/${slug}`);
847
+ async getBundle(slug, options) {
848
+ return this.client.request("GET", `/catalog/bundles/${encodeURIComponent(slug)}`, { query: { ...options } });
838
849
  }
839
850
  /**
840
851
  * One product group ("collection") by slug with its products as standard
@@ -847,7 +858,7 @@ var CatalogModule = class {
847
858
  "GET",
848
859
  `/catalog/product-groups/${encodeURIComponent(slug)}`,
849
860
  {
850
- query: { locale: _optionalChain([options, 'optionalAccess', _30 => _30.locale]), currency: _optionalChain([options, 'optionalAccess', _31 => _31.currency]) }
861
+ query: { locale: _optionalChain([options, 'optionalAccess', _35 => _35.locale]), currency: _optionalChain([options, 'optionalAccess', _36 => _36.currency]), country: _optionalChain([options, 'optionalAccess', _37 => _37.country]) }
851
862
  }
852
863
  );
853
864
  }
@@ -862,7 +873,7 @@ var CatalogModule = class {
862
873
  return this.client.request(
863
874
  "GET",
864
875
  `/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
865
- { query: { locale: _optionalChain([options, 'optionalAccess', _32 => _32.locale]), currency: _optionalChain([options, 'optionalAccess', _33 => _33.currency]) } }
876
+ { query: { locale: _optionalChain([options, 'optionalAccess', _38 => _38.locale]), currency: _optionalChain([options, 'optionalAccess', _39 => _39.currency]), country: _optionalChain([options, 'optionalAccess', _40 => _40.country]) } }
866
877
  );
867
878
  }
868
879
  /** Active promotions applicable to a product (with countdown end time) */
@@ -906,10 +917,10 @@ var CatalogModule = class {
906
917
  */
907
918
  async listPaymentMethods(opts) {
908
919
  const query = {};
909
- if (_optionalChain([opts, 'optionalAccess', _34 => _34.currency])) query.currency = opts.currency;
910
- if (_optionalChain([opts, 'optionalAccess', _35 => _35.locale])) query.locale = opts.locale;
911
- if (_optionalChain([opts, 'optionalAccess', _36 => _36.country])) query.country = opts.country;
912
- if (_optionalChain([opts, 'optionalAccess', _37 => _37.shippingMethodId])) query.shippingMethodId = opts.shippingMethodId;
920
+ if (_optionalChain([opts, 'optionalAccess', _41 => _41.currency])) query.currency = opts.currency;
921
+ if (_optionalChain([opts, 'optionalAccess', _42 => _42.locale])) query.locale = opts.locale;
922
+ if (_optionalChain([opts, 'optionalAccess', _43 => _43.country])) query.country = opts.country;
923
+ if (_optionalChain([opts, 'optionalAccess', _44 => _44.shippingMethodId])) query.shippingMethodId = opts.shippingMethodId;
913
924
  return this.client.request(
914
925
  "GET",
915
926
  "/catalog/payment-methods",
@@ -1070,6 +1081,7 @@ var CartModule = class {
1070
1081
  body: { currency }
1071
1082
  });
1072
1083
  if (res.error) return res;
1084
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1073
1085
  this.client.emit("cart:updated", res.data);
1074
1086
  return res;
1075
1087
  }
@@ -1090,6 +1102,7 @@ var CartModule = class {
1090
1102
  body: input
1091
1103
  });
1092
1104
  if (res.error) return res;
1105
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1093
1106
  this.client.emit("cart:updated", res.data);
1094
1107
  return res;
1095
1108
  }
@@ -1174,14 +1187,15 @@ var CartModule = class {
1174
1187
  body
1175
1188
  });
1176
1189
  if (res.error) return res;
1190
+ if (res.data.sessionToken) this.client.setCartSession(res.data.sessionToken);
1177
1191
  this.client.emit("cart:updated", res.data);
1178
1192
  return res;
1179
1193
  }
1180
- /** Update quantity of a bundle already in the cart */
1181
- async updateBundleQuantity(bundleId, quantity) {
1194
+ /** Update by CartBundleLine.id; legacy catalog bundle IDs are accepted by the API */
1195
+ async updateBundleQuantity(lineId, quantity) {
1182
1196
  const res = await this.client.request(
1183
1197
  "PATCH",
1184
- `/cart/bundles/${bundleId}`,
1198
+ `/cart/bundles/${lineId}`,
1185
1199
  {
1186
1200
  body: { quantity }
1187
1201
  }
@@ -1190,11 +1204,11 @@ var CartModule = class {
1190
1204
  this.client.emit("cart:updated", res.data);
1191
1205
  return res;
1192
1206
  }
1193
- /** Remove a bundle from the cart */
1194
- async removeBundle(bundleId) {
1207
+ /** Remove by CartBundleLine.id; scoped to the current visitor cart */
1208
+ async removeBundle(lineId) {
1195
1209
  const res = await this.client.request(
1196
1210
  "DELETE",
1197
- `/cart/bundles/${bundleId}`
1211
+ `/cart/bundles/${lineId}`
1198
1212
  );
1199
1213
  if (res.error) return res;
1200
1214
  this.client.emit("cart:updated", res.data);
@@ -1228,6 +1242,12 @@ var CheckoutModule = class {
1228
1242
  constructor(client) {
1229
1243
  this.client = client;
1230
1244
  }
1245
+ /** Recalculate the current cart including shipping, fees, benefits and rounding.
1246
+ * No order, stock claim, payment or gift-card redemption is created. Required
1247
+ * legal consents are enforced only on createOrder, not while quoting. */
1248
+ async preview(input) {
1249
+ return this.client.request("POST", "/checkout/preview", { body: input });
1250
+ }
1231
1251
  /** Create order from cart */
1232
1252
  async createOrder(input) {
1233
1253
  const res = await this.client.request("POST", "/checkout", {
@@ -1274,7 +1294,7 @@ var OrdersModule = class {
1274
1294
  "GET",
1275
1295
  "/orders",
1276
1296
  {
1277
- query: { page: _optionalChain([options, 'optionalAccess', _38 => _38.page]), limit: _optionalChain([options, 'optionalAccess', _39 => _39.limit]) }
1297
+ query: { page: _optionalChain([options, 'optionalAccess', _45 => _45.page]), limit: _optionalChain([options, 'optionalAccess', _46 => _46.limit]) }
1278
1298
  }
1279
1299
  );
1280
1300
  }
@@ -1825,7 +1845,7 @@ var ConsentModule = class {
1825
1845
  }
1826
1846
  if (result.error) return { data: null, error: result.error };
1827
1847
  return {
1828
- data: _optionalChain([result, 'access', _40 => _40.data, 'optionalAccess', _41 => _41.consented]) ? result.data.consent : null,
1848
+ data: _optionalChain([result, 'access', _47 => _47.data, 'optionalAccess', _48 => _48.consented]) ? result.data.consent : null,
1829
1849
  error: null
1830
1850
  };
1831
1851
  }
@@ -1912,10 +1932,10 @@ var ShippingModule = class {
1912
1932
  */
1913
1933
  async listMethods(opts) {
1914
1934
  const query = {};
1915
- if (_optionalChain([opts, 'optionalAccess', _42 => _42.currency])) query.currency = opts.currency;
1916
- if (_optionalChain([opts, 'optionalAccess', _43 => _43.country])) query.country = opts.country;
1917
- if (_optionalChain([opts, 'optionalAccess', _44 => _44.cartTotal]) != null) query.cartTotal = String(opts.cartTotal);
1918
- if (_optionalChain([opts, 'optionalAccess', _45 => _45.cartWeightKg]) != null)
1935
+ if (_optionalChain([opts, 'optionalAccess', _49 => _49.currency])) query.currency = opts.currency;
1936
+ if (_optionalChain([opts, 'optionalAccess', _50 => _50.country])) query.country = opts.country;
1937
+ if (_optionalChain([opts, 'optionalAccess', _51 => _51.cartTotal]) != null) query.cartTotal = String(opts.cartTotal);
1938
+ if (_optionalChain([opts, 'optionalAccess', _52 => _52.cartWeightKg]) != null)
1919
1939
  query.cartWeightKg = String(opts.cartWeightKg);
1920
1940
  return this.client.request(
1921
1941
  "GET",