@behio/storefront-sdk 1.9.0 → 1.18.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/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  **Headless e-commerce SDK for building custom storefronts.**
4
4
 
5
- Behio gives you a complete e-commerce backend products, inventory, orders, customers, discounts, multi-currency, multi-language and lets you design the storefront however you want. No themes, no templates, no vendor lock-in.
5
+ Behio gives you a complete e-commerce backend (products, inventory, orders, customers, discounts, multi-currency, multi-language) and lets you design the storefront however you want. Merchants run on it worldwide, with local tax rules, carriers and payment gateways handled natively. No themes, no templates, no vendor lock-in.
6
6
 
7
7
  ## Why Behio?
8
8
 
9
- - **You own the frontend.** Next.js, React, Vue, Nuxt, Astro, or plain JS the backend doesn't care.
9
+ - **You own the frontend.** Next.js, React, Vue, Nuxt, Astro, or plain JS, the backend doesn't care.
10
10
  - **Production-ready in minutes.** Catalog, cart, checkout, customer accounts, orders, CMS, discount codes, gift cards, loyalty programs, and more.
11
11
  - **Built for developers.** Full TypeScript types, auto-completing, modern React hooks with TanStack Query.
12
12
  - **Scale-ready.** Redis caching, rate limiting, webhooks, atomic checkout (no double-spend, no overselling).
@@ -60,7 +60,7 @@ function ProductList() {
60
60
 
61
61
  return data.items.map(p => (
62
62
  <div key={p.id}>
63
- <h3>{p.name} {p.price} {p.currency}</h3>
63
+ <h3>{p.name}, {p.price} {p.currency}</h3>
64
64
  <button onClick={() => addToCart.mutateAsync({ productId: p.id, quantity: 1 })}>
65
65
  Add to Cart
66
66
  </button>
@@ -89,18 +89,20 @@ function ProductList() {
89
89
  | `consent` | Cookie consent (GDPR) |
90
90
  | `quotes` | B2B quote requests |
91
91
  | `pages` | CMS pages |
92
+ | `blog` | Blogs and published posts (web + e-shop) |
93
+ | `forms` | Merchant-defined forms: definition + validated submit with per-field errors (web + e-shop) |
92
94
 
93
95
  ### React Hooks (30+)
94
96
 
95
- `useProducts` · `useProduct` · `useCategories` · `useCategoryProducts` · `useFeaturedProducts` · `useLabels` · `useProductSearch` · `useFilters` · `useBundles` · `useBundle` · `useCrossSell` · `useProductPromotions` · `useGiftCardBalance` · `useCart` · `useAddToCart` · `useUpdateCartItem` · `useRemoveCartItem` · `useCheckout` · `useOrders` · `useOrder` · `useOrderTracking` · `useCustomerProfile` · `useAddresses` · `useAddressAutocomplete` · `useWishlist` · `useProductReviews` · `useSubmitReview` · `useShopInfo` · `useShopSeo` · `useCartCount`
97
+ `useProducts` · `useProduct` · `useCategories` · `useCategoryProducts` · `useFeaturedProducts` · `useLabels` · `useProductSearch` · `useFilters` · `useBundles` · `useBundle` · `useCrossSell` · `useProductPromotions` · `useGiftCardBalance` · `useCart` · `useAddToCart` · `useUpdateCartItem` · `useRemoveCartItem` · `useCheckout` · `useOrders` · `useOrder` · `useOrderTracking` · `useCustomerProfile` · `useAddresses` · `useAddressAutocomplete` · `useWishlist` · `useProductReviews` · `useSubmitReview` · `useShopInfo` · `useShopSeo` · `useCartCount` · `useBlogs` · `useBlogPosts` · `useBlogPost` · `useSiteForm` · `useSiteFormSubmit`
96
98
 
97
99
  ### Framework Support
98
100
 
99
- - **Next.js** Server components + client hooks, SSR ready
100
- - **React + Vite** Standard SPA setup
101
- - **Nuxt 3** Composables with SSR
102
- - **Vue + Vite** Provide/inject pattern
103
- - **Vanilla JS** Works in any runtime (Node.js, Deno, Bun, Cloudflare Workers)
101
+ - **Next.js**: Server components + client hooks, SSR ready
102
+ - **React + Vite**: Standard SPA setup
103
+ - **Nuxt 3**: Composables with SSR
104
+ - **Vue + Vite**: Provide/inject pattern
105
+ - **Vanilla JS**: Works in any runtime (Node.js, Deno, Bun, Cloudflare Workers)
104
106
 
105
107
  ### Built-in Features
106
108
 
@@ -33,6 +33,38 @@ var AddressTypes = {
33
33
  SHIPPING: "SHIPPING",
34
34
  BILLING: "BILLING"
35
35
  };
36
+ var BE_CODE_TO_ERROR = {
37
+ "be.storefront.cartIsEmpty": "CART_EMPTY",
38
+ "be.storefront.productNotFound": "PRODUCT_NOT_FOUND",
39
+ "be.storefront.productNotAvailable": "PRODUCT_NOT_FOUND",
40
+ "be.storefront.invalidEmailOrPassword": "INVALID_CREDENTIALS",
41
+ "be.storefront.currentPasswordIncorrect": "INVALID_CREDENTIALS",
42
+ "be.storefront.discountExpired": "DISCOUNT_EXPIRED",
43
+ "be.storefront.discountExpiredOrLimit": "DISCOUNT_EXPIRED",
44
+ "be.storefront.invalidDiscountCode": "INVALID_DISCOUNT",
45
+ "be.storefront.discountInvalid": "INVALID_DISCOUNT",
46
+ "be.storefront.discountInactive": "INVALID_DISCOUNT",
47
+ "be.storefront.discountWrongCurrency": "INVALID_DISCOUNT",
48
+ "be.storefront.discountNotApplicable": "INVALID_DISCOUNT",
49
+ "be.storefront.discountNotYetValid": "INVALID_DISCOUNT",
50
+ "be.storefront.discountUsageLimitReached": "INVALID_DISCOUNT",
51
+ "be.storefront.discountLimitedToGroups": "INVALID_DISCOUNT",
52
+ "be.storefront.tokenInvalid": "TOKEN_INVALID",
53
+ "be.storefront.orderAccessTokenInvalid": "TOKEN_INVALID",
54
+ "be.storefront.resetTokenInvalid": "TOKEN_INVALID",
55
+ "be.storefront.invalidVerificationToken": "TOKEN_INVALID",
56
+ "be.storefront.invalidRefreshToken": "TOKEN_INVALID",
57
+ "be.storefront.refreshTokenExpired": "TOKEN_EXPIRED",
58
+ "be.storefront.apiKeyExpired": "TOKEN_EXPIRED",
59
+ "be.storefront.customerEmailTaken": "EMAIL_ALREADY_EXISTS",
60
+ "be.storefront.orderNotCancellable": "ORDER_NOT_CANCELLABLE",
61
+ "be.storefront.orderCancelledNotReturnable": "ORDER_NOT_CANCELLABLE",
62
+ "be.validation.failed": "VALIDATION_ERROR",
63
+ "be.forms.formNotFound": "NOT_FOUND",
64
+ "be.forms.validationFailed": "VALIDATION_ERROR",
65
+ "be.forms.consentRequired": "VALIDATION_ERROR",
66
+ "be.forms.tooLarge": "VALIDATION_ERROR"
67
+ };
36
68
  var BehioApiError = class _BehioApiError extends Error {
37
69
  constructor(status, body, message) {
38
70
  super(message || `API Error ${status}`);
@@ -49,7 +81,12 @@ var BehioApiError = class _BehioApiError extends Error {
49
81
  if (status === 409) return "EMAIL_ALREADY_EXISTS";
50
82
  if (status === 429) return "RATE_LIMITED";
51
83
  if (status >= 500) return "INTERNAL_ERROR";
52
- const msg = (_optionalChain([body, 'optionalAccess', _ => _.message]) || "").toLowerCase();
84
+ const rawCode = _optionalChain([body, 'optionalAccess', _ => _.code]);
85
+ if (typeof rawCode === "string") {
86
+ const mapped = BE_CODE_TO_ERROR[rawCode];
87
+ if (mapped) return mapped;
88
+ }
89
+ const msg = (_optionalChain([body, 'optionalAccess', _2 => _2.message]) || "").toLowerCase();
53
90
  if (msg.includes("invalid") && msg.includes("password"))
54
91
  return "INVALID_CREDENTIALS";
55
92
  if (msg.includes("invalid") && msg.includes("email"))
@@ -157,6 +194,8 @@ var BehioStorefront = class {
157
194
  this.orders = new OrdersModule(this);
158
195
  this.customer = new CustomerModule(this);
159
196
  this.pages = new PagesModule(this);
197
+ this.blog = new BlogModule(this);
198
+ this.forms = new FormsModule(this);
160
199
  this.wishlist = new WishlistModule(this);
161
200
  this.reviews = new ReviewsModule(this);
162
201
  this.returns = new ReturnsModule(this);
@@ -317,12 +356,12 @@ var BehioStorefront = class {
317
356
  if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
318
357
  this.listeners.get(event).add(handler);
319
358
  return () => {
320
- _optionalChain([this, 'access', _2 => _2.listeners, 'access', _3 => _3.get, 'call', _4 => _4(event), 'optionalAccess', _5 => _5.delete, 'call', _6 => _6(handler)]);
359
+ _optionalChain([this, 'access', _3 => _3.listeners, 'access', _4 => _4.get, 'call', _5 => _5(event), 'optionalAccess', _6 => _6.delete, 'call', _7 => _7(handler)]);
321
360
  };
322
361
  }
323
362
  /** @internal Emit an event (fire-and-forget, handler errors are swallowed) */
324
363
  emit(event, data) {
325
- _optionalChain([this, 'access', _7 => _7.listeners, 'access', _8 => _8.get, 'call', _9 => _9(event), 'optionalAccess', _10 => _10.forEach, 'call', _11 => _11((fn) => {
364
+ _optionalChain([this, 'access', _8 => _8.listeners, 'access', _9 => _9.get, 'call', _10 => _10(event), 'optionalAccess', _11 => _11.forEach, 'call', _12 => _12((fn) => {
326
365
  try {
327
366
  fn(data);
328
367
  } catch (e2) {
@@ -438,7 +477,7 @@ var BehioStorefront = class {
438
477
  */
439
478
  async rawRequest(method, path, options) {
440
479
  const params = new URLSearchParams();
441
- if (_optionalChain([options, 'optionalAccess', _12 => _12.query])) {
480
+ if (_optionalChain([options, 'optionalAccess', _13 => _13.query])) {
442
481
  for (const [key, value] of Object.entries(options.query)) {
443
482
  if (value === void 0 || value === null || value === "") continue;
444
483
  if (Array.isArray(value)) {
@@ -467,7 +506,7 @@ var BehioStorefront = class {
467
506
  if (this.shopDomain) {
468
507
  headers["X-Shop-Domain"] = this.shopDomain;
469
508
  }
470
- if (this.accessToken && _optionalChain([options, 'optionalAccess', _13 => _13.auth]) !== false) {
509
+ if (this.accessToken && _optionalChain([options, 'optionalAccess', _14 => _14.auth]) !== false) {
471
510
  headers["Authorization"] = `Bearer ${this.accessToken}`;
472
511
  }
473
512
  if (this.cartSession) {
@@ -483,10 +522,10 @@ var BehioStorefront = class {
483
522
  } catch (e4) {
484
523
  }
485
524
  }
486
- if (_optionalChain([options, 'optionalAccess', _14 => _14.headers])) {
525
+ if (_optionalChain([options, 'optionalAccess', _15 => _15.headers])) {
487
526
  Object.assign(headers, options.headers);
488
527
  }
489
- const bodyStr = _optionalChain([options, 'optionalAccess', _15 => _15.body]) ? JSON.stringify(options.body) : void 0;
528
+ const bodyStr = _optionalChain([options, 'optionalAccess', _16 => _16.body]) ? JSON.stringify(options.body) : void 0;
490
529
  let interceptedConfig = {
491
530
  url,
492
531
  method,
@@ -500,7 +539,7 @@ var BehioStorefront = class {
500
539
  for (let attempt = 0; attempt <= this.retries; attempt++) {
501
540
  const controller = new AbortController();
502
541
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
503
- if (_optionalChain([options, 'optionalAccess', _16 => _16.signal])) {
542
+ if (_optionalChain([options, 'optionalAccess', _17 => _17.signal])) {
504
543
  if (options.signal.aborted) {
505
544
  clearTimeout(timeoutId);
506
545
  throw new BehioNetworkError("Request aborted", false);
@@ -551,9 +590,9 @@ var BehioStorefront = class {
551
590
  const apiError = new BehioApiError(
552
591
  res.status,
553
592
  body,
554
- _optionalChain([body, 'optionalAccess', _17 => _17.message]) || `API Error ${res.status}`
593
+ _optionalChain([body, 'optionalAccess', _18 => _18.message]) || `API Error ${res.status}`
555
594
  );
556
- if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess', _18 => _18.auth]) !== false && !_optionalChain([options, 'optionalAccess', _19 => _19._isRetryAfterRefresh])) {
595
+ if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess', _19 => _19.auth]) !== false && !_optionalChain([options, 'optionalAccess', _20 => _20._isRetryAfterRefresh])) {
557
596
  try {
558
597
  await this.handleTokenRefresh();
559
598
  return this.rawRequest(method, path, {
@@ -605,6 +644,7 @@ var CatalogModule = class {
605
644
  if (query.priceMin) q.priceMin = query.priceMin;
606
645
  if (query.priceMax) q.priceMax = query.priceMax;
607
646
  if (query.currency) q.currency = query.currency;
647
+ if (query.country) q.country = query.country;
608
648
  if (query.locale) q.locale = query.locale;
609
649
  if (query.sort) q.sort = query.sort;
610
650
  if (query.inStock !== void 0) q.inStock = query.inStock;
@@ -637,7 +677,7 @@ var CatalogModule = class {
637
677
  "GET",
638
678
  `/catalog/products/${slug}`,
639
679
  {
640
- query: { locale: _optionalChain([options, 'optionalAccess', _20 => _20.locale]), currency: _optionalChain([options, 'optionalAccess', _21 => _21.currency]) }
680
+ query: { locale: _optionalChain([options, 'optionalAccess', _21 => _21.locale]), currency: _optionalChain([options, 'optionalAccess', _22 => _22.currency]), country: _optionalChain([options, 'optionalAccess', _23 => _23.country]) }
641
681
  }
642
682
  );
643
683
  }
@@ -691,7 +731,7 @@ var CatalogModule = class {
691
731
  "GET",
692
732
  `/catalog/menu/${encodeURIComponent(handle)}`,
693
733
  {
694
- query: { locale: _optionalChain([options, 'optionalAccess', _22 => _22.locale]) }
734
+ query: { locale: _optionalChain([options, 'optionalAccess', _24 => _24.locale]) }
695
735
  }
696
736
  );
697
737
  }
@@ -707,7 +747,7 @@ var CatalogModule = class {
707
747
  "GET",
708
748
  "/catalog/featured",
709
749
  {
710
- query: { locale: _optionalChain([options, 'optionalAccess', _23 => _23.locale]), currency: _optionalChain([options, 'optionalAccess', _24 => _24.currency]) }
750
+ query: { locale: _optionalChain([options, 'optionalAccess', _25 => _25.locale]), currency: _optionalChain([options, 'optionalAccess', _26 => _26.currency]) }
711
751
  }
712
752
  );
713
753
  }
@@ -722,7 +762,7 @@ var CatalogModule = class {
722
762
  return this.client.request(
723
763
  "GET",
724
764
  "/catalog/filters",
725
- { query: { locale: _optionalChain([options, 'optionalAccess', _25 => _25.locale]) } }
765
+ { query: { locale: _optionalChain([options, 'optionalAccess', _27 => _27.locale]) } }
726
766
  );
727
767
  }
728
768
  /**
@@ -737,7 +777,7 @@ var CatalogModule = class {
737
777
  return this.client.request(
738
778
  "GET",
739
779
  `/catalog/products/${slug}/parameters`,
740
- { query: { locale: _optionalChain([options, 'optionalAccess', _26 => _26.locale]) } }
780
+ { query: { locale: _optionalChain([options, 'optionalAccess', _28 => _28.locale]) } }
741
781
  );
742
782
  }
743
783
  /**
@@ -749,7 +789,7 @@ var CatalogModule = class {
749
789
  return this.client.request(
750
790
  "GET",
751
791
  `/catalog/products/${slug}/parameters/${groupSlug}`,
752
- { query: { locale: _optionalChain([options, 'optionalAccess', _27 => _27.locale]) } }
792
+ { query: { locale: _optionalChain([options, 'optionalAccess', _29 => _29.locale]) } }
753
793
  );
754
794
  }
755
795
  /**
@@ -807,7 +847,7 @@ var CatalogModule = class {
807
847
  "GET",
808
848
  `/catalog/product-groups/${encodeURIComponent(slug)}`,
809
849
  {
810
- query: { locale: _optionalChain([options, 'optionalAccess', _28 => _28.locale]), currency: _optionalChain([options, 'optionalAccess', _29 => _29.currency]) }
850
+ query: { locale: _optionalChain([options, 'optionalAccess', _30 => _30.locale]), currency: _optionalChain([options, 'optionalAccess', _31 => _31.currency]) }
811
851
  }
812
852
  );
813
853
  }
@@ -822,7 +862,7 @@ var CatalogModule = class {
822
862
  return this.client.request(
823
863
  "GET",
824
864
  `/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
825
- { query: { locale: _optionalChain([options, 'optionalAccess', _30 => _30.locale]), currency: _optionalChain([options, 'optionalAccess', _31 => _31.currency]) } }
865
+ { query: { locale: _optionalChain([options, 'optionalAccess', _32 => _32.locale]), currency: _optionalChain([options, 'optionalAccess', _33 => _33.currency]) } }
826
866
  );
827
867
  }
828
868
  /** Active promotions applicable to a product (with countdown end time) */
@@ -859,16 +899,27 @@ var CatalogModule = class {
859
899
  { body: input }
860
900
  );
861
901
  }
862
- /** List configured payment methods (filtered by currency). */
902
+ /**
903
+ * List configured payment methods (filtered by currency). `locale` picks the
904
+ * language of `instruments[].label` / `swifts[].label` (SDK 1.14.0); pass the
905
+ * locale of the page so the instrument tiles read in the shopper's language.
906
+ */
863
907
  async listPaymentMethods(opts) {
864
908
  const query = {};
865
- if (_optionalChain([opts, 'optionalAccess', _32 => _32.currency])) query.currency = opts.currency;
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;
866
913
  return this.client.request(
867
914
  "GET",
868
915
  "/catalog/payment-methods",
869
916
  { query }
870
917
  );
871
918
  }
919
+ /** Alias of `listPaymentMethods` (SDK 1.14.0). */
920
+ paymentMethods(opts) {
921
+ return this.listPaymentMethods(opts);
922
+ }
872
923
  };
873
924
  var AuthModule = class {
874
925
  constructor(client) {
@@ -1022,6 +1073,26 @@ var CartModule = class {
1022
1073
  this.client.emit("cart:updated", res.data);
1023
1074
  return res;
1024
1075
  }
1076
+ /**
1077
+ * Tell the cart where the order will ship (and, for B2B, the buyer's VAT
1078
+ * ID) so the VAT breakdown matches the checkout before the address form:
1079
+ * destination-country rate (OSS), 0 % export outside the EU, or reverse
1080
+ * charge for an EU business with a VIES-valid VAT ID. `cart.vatMode` says
1081
+ * which rule applied. SDK 1.17.0.
1082
+ *
1083
+ * ```ts
1084
+ * await client.cart.setDestination({ country: "SK" });
1085
+ * await client.cart.setDestination({ vatId: "SK2020000001" }); // "" clears
1086
+ * ```
1087
+ */
1088
+ async setDestination(input) {
1089
+ const res = await this.client.request("PUT", "/cart/destination", {
1090
+ body: input
1091
+ });
1092
+ if (res.error) return res;
1093
+ this.client.emit("cart:updated", res.data);
1094
+ return res;
1095
+ }
1025
1096
  /** Update item quantity */
1026
1097
  async updateQuantity(itemId, quantity) {
1027
1098
  const res = await this.client.request(
@@ -1173,13 +1244,37 @@ var OrdersModule = class {
1173
1244
  constructor(client) {
1174
1245
  this.client = client;
1175
1246
  }
1247
+ /**
1248
+ * Ask the backend to re-check this order's payment with the gateway.
1249
+ *
1250
+ * Call it on the thank-you page the customer lands on after paying, BEFORE
1251
+ * you read the order. Some gateways (Tatrapay+) have no server-to-server
1252
+ * notification at all, so the customer's return is the only fast way the
1253
+ * payment gets confirmed; for the others it is a safety net for a lost
1254
+ * notification.
1255
+ *
1256
+ * The response is deliberately opaque (`{ok: true}` every time, even for an
1257
+ * order number that does not exist): order numbers are sequential, so
1258
+ * anything else would turn this into a probe for other people's orders.
1259
+ * Read the actual state afterwards through a path that proves entitlement
1260
+ * ({@link get}, {@link track} or the guest access-code flow).
1261
+ *
1262
+ * Never throws for a missing order and never blocks the page: treat a
1263
+ * failure as "not confirmed yet", the backend poller catches up on its own.
1264
+ */
1265
+ async syncPaymentOnReturn(orderNumber) {
1266
+ return this.client.request(
1267
+ "POST",
1268
+ `/payments/return/${orderNumber}`
1269
+ );
1270
+ }
1176
1271
  /** List customer orders (requires auth) */
1177
1272
  async list(options) {
1178
1273
  return this.client.request(
1179
1274
  "GET",
1180
1275
  "/orders",
1181
1276
  {
1182
- query: { page: _optionalChain([options, 'optionalAccess', _33 => _33.page]), limit: _optionalChain([options, 'optionalAccess', _34 => _34.limit]) }
1277
+ query: { page: _optionalChain([options, 'optionalAccess', _38 => _38.page]), limit: _optionalChain([options, 'optionalAccess', _39 => _39.limit]) }
1183
1278
  }
1184
1279
  );
1185
1280
  }
@@ -1299,7 +1394,10 @@ var CustomerModule = class {
1299
1394
  body: address
1300
1395
  });
1301
1396
  }
1302
- /** Update address */
1397
+ /**
1398
+ * Update address. Partial: `{isDefault: true}` alone is a valid body. A
1399
+ * changed `vatId` or `country` re-runs the VIES check on the server.
1400
+ */
1303
1401
  async updateAddress(addressId, data) {
1304
1402
  return this.client.request(
1305
1403
  "PATCH",
@@ -1525,6 +1623,59 @@ var PagesModule = class {
1525
1623
  });
1526
1624
  }
1527
1625
  };
1626
+ var BlogModule = class {
1627
+ constructor(client) {
1628
+ this.client = client;
1629
+ }
1630
+ /** List the site's blogs (active only). */
1631
+ async list(locale) {
1632
+ return this.client.request("GET", "/blogs", {
1633
+ query: { locale }
1634
+ });
1635
+ }
1636
+ /** Published posts of one blog, newest first (featured first), paginated. */
1637
+ async posts(handle, query = {}) {
1638
+ return this.client.request("GET", `/blogs/${handle}/posts`, {
1639
+ query: {
1640
+ locale: query.locale,
1641
+ page: query.page,
1642
+ limit: query.limit,
1643
+ tag: query.tag
1644
+ }
1645
+ });
1646
+ }
1647
+ /** One published post with sanitised HTML content and related posts. */
1648
+ async post(handle, slug, locale) {
1649
+ return this.client.request("GET", `/blogs/${handle}/posts/${slug}`, {
1650
+ query: { locale }
1651
+ });
1652
+ }
1653
+ };
1654
+ var FormsModule = class {
1655
+ constructor(client) {
1656
+ this.client = client;
1657
+ }
1658
+ /** Public definition of one form (fields + settings). 404 for an unknown or inactive slug. */
1659
+ async get(slug) {
1660
+ return this.client.request(
1661
+ "GET",
1662
+ `/forms/${encodeURIComponent(slug)}`
1663
+ );
1664
+ }
1665
+ /**
1666
+ * Submit a response. On `be.forms.validationFailed` the returned error
1667
+ * carries per-field codes: read them with `formFieldErrors(error)`. Other
1668
+ * rejections: `be.forms.consentRequired`, `be.forms.tooLarge`,
1669
+ * `be.forms.formNotFound`; rate limit 10 submits per minute per visitor.
1670
+ */
1671
+ async submit(slug, input) {
1672
+ return this.client.request(
1673
+ "POST",
1674
+ `/forms/${encodeURIComponent(slug)}/submit`,
1675
+ { body: input, auth: false }
1676
+ );
1677
+ }
1678
+ };
1528
1679
  var WishlistModule = class {
1529
1680
  constructor(client) {
1530
1681
  this.client = client;
@@ -1674,7 +1825,7 @@ var ConsentModule = class {
1674
1825
  }
1675
1826
  if (result.error) return { data: null, error: result.error };
1676
1827
  return {
1677
- data: _optionalChain([result, 'access', _35 => _35.data, 'optionalAccess', _36 => _36.consented]) ? result.data.consent : null,
1828
+ data: _optionalChain([result, 'access', _40 => _40.data, 'optionalAccess', _41 => _41.consented]) ? result.data.consent : null,
1678
1829
  error: null
1679
1830
  };
1680
1831
  }
@@ -1761,10 +1912,10 @@ var ShippingModule = class {
1761
1912
  */
1762
1913
  async listMethods(opts) {
1763
1914
  const query = {};
1764
- if (_optionalChain([opts, 'optionalAccess', _37 => _37.currency])) query.currency = opts.currency;
1765
- if (_optionalChain([opts, 'optionalAccess', _38 => _38.country])) query.country = opts.country;
1766
- if (_optionalChain([opts, 'optionalAccess', _39 => _39.cartTotal]) != null) query.cartTotal = String(opts.cartTotal);
1767
- if (_optionalChain([opts, 'optionalAccess', _40 => _40.cartWeightKg]) != null)
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)
1768
1919
  query.cartWeightKg = String(opts.cartWeightKg);
1769
1920
  return this.client.request(
1770
1921
  "GET",