@behio/storefront-sdk 1.8.0 → 1.17.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 +11 -9
- package/dist/{chunk-LQ4GGXMJ.js → chunk-FMMFIZHA.js} +188 -26
- package/dist/{chunk-LA6K4KYS.mjs → chunk-PU4QV6JS.mjs} +163 -1
- package/dist/{client-BP9n3a_D.d.mts → client-yo90ANGT.d.mts} +414 -4
- package/dist/{client-BP9n3a_D.d.ts → client-yo90ANGT.d.ts} +414 -4
- package/dist/index.d.mts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.js +743 -2
- package/dist/index.mjs +742 -1
- package/dist/next.d.mts +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.js +2 -2
- package/dist/next.mjs +1 -1
- package/dist/react.d.mts +503 -10
- package/dist/react.d.ts +503 -10
- package/dist/react.js +457 -92
- package/dist/react.mjs +440 -81
- package/package.json +2 -2
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
|
|
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
|
|
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}
|
|
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
|
|
100
|
-
- **React + Vite
|
|
101
|
-
- **Nuxt 3
|
|
102
|
-
- **Vue + Vite
|
|
103
|
-
- **Vanilla JS
|
|
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
|
|
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);
|
|
@@ -225,6 +264,21 @@ var BehioStorefront = class {
|
|
|
225
264
|
body: input
|
|
226
265
|
});
|
|
227
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Visitor messages from merchant automations (storefront.event action).
|
|
269
|
+
* Consent-gated visitor id; each message carries a merchant-defined `name`
|
|
270
|
+
* and free-form `payload` the template reacts to (modal, banner, ...).
|
|
271
|
+
* Messages stay listed until acknowledged via `ackVisitorMessage`.
|
|
272
|
+
*/
|
|
273
|
+
async getVisitorMessages(visitorId) {
|
|
274
|
+
return this.request("GET", "/messages", { query: { visitorId } });
|
|
275
|
+
}
|
|
276
|
+
/** Acknowledge a visitor message so it is not delivered again. */
|
|
277
|
+
async ackVisitorMessage(messageId, visitorId) {
|
|
278
|
+
return this.request("POST", `/messages/${messageId}/ack`, {
|
|
279
|
+
query: { visitorId }
|
|
280
|
+
});
|
|
281
|
+
}
|
|
228
282
|
/** Get basic shop info */
|
|
229
283
|
async getShopInfo() {
|
|
230
284
|
return this.request("GET", "/shop");
|
|
@@ -302,12 +356,12 @@ var BehioStorefront = class {
|
|
|
302
356
|
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
303
357
|
this.listeners.get(event).add(handler);
|
|
304
358
|
return () => {
|
|
305
|
-
_optionalChain([this, 'access',
|
|
359
|
+
_optionalChain([this, 'access', _3 => _3.listeners, 'access', _4 => _4.get, 'call', _5 => _5(event), 'optionalAccess', _6 => _6.delete, 'call', _7 => _7(handler)]);
|
|
306
360
|
};
|
|
307
361
|
}
|
|
308
362
|
/** @internal Emit an event (fire-and-forget, handler errors are swallowed) */
|
|
309
363
|
emit(event, data) {
|
|
310
|
-
_optionalChain([this, 'access',
|
|
364
|
+
_optionalChain([this, 'access', _8 => _8.listeners, 'access', _9 => _9.get, 'call', _10 => _10(event), 'optionalAccess', _11 => _11.forEach, 'call', _12 => _12((fn) => {
|
|
311
365
|
try {
|
|
312
366
|
fn(data);
|
|
313
367
|
} catch (e2) {
|
|
@@ -423,7 +477,7 @@ var BehioStorefront = class {
|
|
|
423
477
|
*/
|
|
424
478
|
async rawRequest(method, path, options) {
|
|
425
479
|
const params = new URLSearchParams();
|
|
426
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
480
|
+
if (_optionalChain([options, 'optionalAccess', _13 => _13.query])) {
|
|
427
481
|
for (const [key, value] of Object.entries(options.query)) {
|
|
428
482
|
if (value === void 0 || value === null || value === "") continue;
|
|
429
483
|
if (Array.isArray(value)) {
|
|
@@ -452,7 +506,7 @@ var BehioStorefront = class {
|
|
|
452
506
|
if (this.shopDomain) {
|
|
453
507
|
headers["X-Shop-Domain"] = this.shopDomain;
|
|
454
508
|
}
|
|
455
|
-
if (this.accessToken && _optionalChain([options, 'optionalAccess',
|
|
509
|
+
if (this.accessToken && _optionalChain([options, 'optionalAccess', _14 => _14.auth]) !== false) {
|
|
456
510
|
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
457
511
|
}
|
|
458
512
|
if (this.cartSession) {
|
|
@@ -468,10 +522,10 @@ var BehioStorefront = class {
|
|
|
468
522
|
} catch (e4) {
|
|
469
523
|
}
|
|
470
524
|
}
|
|
471
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
525
|
+
if (_optionalChain([options, 'optionalAccess', _15 => _15.headers])) {
|
|
472
526
|
Object.assign(headers, options.headers);
|
|
473
527
|
}
|
|
474
|
-
const bodyStr = _optionalChain([options, 'optionalAccess',
|
|
528
|
+
const bodyStr = _optionalChain([options, 'optionalAccess', _16 => _16.body]) ? JSON.stringify(options.body) : void 0;
|
|
475
529
|
let interceptedConfig = {
|
|
476
530
|
url,
|
|
477
531
|
method,
|
|
@@ -485,7 +539,7 @@ var BehioStorefront = class {
|
|
|
485
539
|
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
486
540
|
const controller = new AbortController();
|
|
487
541
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
488
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
542
|
+
if (_optionalChain([options, 'optionalAccess', _17 => _17.signal])) {
|
|
489
543
|
if (options.signal.aborted) {
|
|
490
544
|
clearTimeout(timeoutId);
|
|
491
545
|
throw new BehioNetworkError("Request aborted", false);
|
|
@@ -536,9 +590,9 @@ var BehioStorefront = class {
|
|
|
536
590
|
const apiError = new BehioApiError(
|
|
537
591
|
res.status,
|
|
538
592
|
body,
|
|
539
|
-
_optionalChain([body, 'optionalAccess',
|
|
593
|
+
_optionalChain([body, 'optionalAccess', _18 => _18.message]) || `API Error ${res.status}`
|
|
540
594
|
);
|
|
541
|
-
if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess',
|
|
595
|
+
if (res.status === 401 && this.refreshToken && _optionalChain([options, 'optionalAccess', _19 => _19.auth]) !== false && !_optionalChain([options, 'optionalAccess', _20 => _20._isRetryAfterRefresh])) {
|
|
542
596
|
try {
|
|
543
597
|
await this.handleTokenRefresh();
|
|
544
598
|
return this.rawRequest(method, path, {
|
|
@@ -622,7 +676,7 @@ var CatalogModule = class {
|
|
|
622
676
|
"GET",
|
|
623
677
|
`/catalog/products/${slug}`,
|
|
624
678
|
{
|
|
625
|
-
query: { locale: _optionalChain([options, 'optionalAccess',
|
|
679
|
+
query: { locale: _optionalChain([options, 'optionalAccess', _21 => _21.locale]), currency: _optionalChain([options, 'optionalAccess', _22 => _22.currency]) }
|
|
626
680
|
}
|
|
627
681
|
);
|
|
628
682
|
}
|
|
@@ -676,7 +730,7 @@ var CatalogModule = class {
|
|
|
676
730
|
"GET",
|
|
677
731
|
`/catalog/menu/${encodeURIComponent(handle)}`,
|
|
678
732
|
{
|
|
679
|
-
query: { locale: _optionalChain([options, 'optionalAccess',
|
|
733
|
+
query: { locale: _optionalChain([options, 'optionalAccess', _23 => _23.locale]) }
|
|
680
734
|
}
|
|
681
735
|
);
|
|
682
736
|
}
|
|
@@ -692,7 +746,7 @@ var CatalogModule = class {
|
|
|
692
746
|
"GET",
|
|
693
747
|
"/catalog/featured",
|
|
694
748
|
{
|
|
695
|
-
query: { locale: _optionalChain([options, 'optionalAccess',
|
|
749
|
+
query: { locale: _optionalChain([options, 'optionalAccess', _24 => _24.locale]), currency: _optionalChain([options, 'optionalAccess', _25 => _25.currency]) }
|
|
696
750
|
}
|
|
697
751
|
);
|
|
698
752
|
}
|
|
@@ -707,7 +761,7 @@ var CatalogModule = class {
|
|
|
707
761
|
return this.client.request(
|
|
708
762
|
"GET",
|
|
709
763
|
"/catalog/filters",
|
|
710
|
-
{ query: { locale: _optionalChain([options, 'optionalAccess',
|
|
764
|
+
{ query: { locale: _optionalChain([options, 'optionalAccess', _26 => _26.locale]) } }
|
|
711
765
|
);
|
|
712
766
|
}
|
|
713
767
|
/**
|
|
@@ -722,7 +776,7 @@ var CatalogModule = class {
|
|
|
722
776
|
return this.client.request(
|
|
723
777
|
"GET",
|
|
724
778
|
`/catalog/products/${slug}/parameters`,
|
|
725
|
-
{ query: { locale: _optionalChain([options, 'optionalAccess',
|
|
779
|
+
{ query: { locale: _optionalChain([options, 'optionalAccess', _27 => _27.locale]) } }
|
|
726
780
|
);
|
|
727
781
|
}
|
|
728
782
|
/**
|
|
@@ -734,7 +788,7 @@ var CatalogModule = class {
|
|
|
734
788
|
return this.client.request(
|
|
735
789
|
"GET",
|
|
736
790
|
`/catalog/products/${slug}/parameters/${groupSlug}`,
|
|
737
|
-
{ query: { locale: _optionalChain([options, 'optionalAccess',
|
|
791
|
+
{ query: { locale: _optionalChain([options, 'optionalAccess', _28 => _28.locale]) } }
|
|
738
792
|
);
|
|
739
793
|
}
|
|
740
794
|
/**
|
|
@@ -792,7 +846,7 @@ var CatalogModule = class {
|
|
|
792
846
|
"GET",
|
|
793
847
|
`/catalog/product-groups/${encodeURIComponent(slug)}`,
|
|
794
848
|
{
|
|
795
|
-
query: { locale: _optionalChain([options, 'optionalAccess',
|
|
849
|
+
query: { locale: _optionalChain([options, 'optionalAccess', _29 => _29.locale]), currency: _optionalChain([options, 'optionalAccess', _30 => _30.currency]) }
|
|
796
850
|
}
|
|
797
851
|
);
|
|
798
852
|
}
|
|
@@ -807,7 +861,7 @@ var CatalogModule = class {
|
|
|
807
861
|
return this.client.request(
|
|
808
862
|
"GET",
|
|
809
863
|
`/catalog/products/${encodeURIComponent(productSlug)}/cross-sell`,
|
|
810
|
-
{ query: { locale: _optionalChain([options, 'optionalAccess',
|
|
864
|
+
{ query: { locale: _optionalChain([options, 'optionalAccess', _31 => _31.locale]), currency: _optionalChain([options, 'optionalAccess', _32 => _32.currency]) } }
|
|
811
865
|
);
|
|
812
866
|
}
|
|
813
867
|
/** Active promotions applicable to a product (with countdown end time) */
|
|
@@ -844,16 +898,27 @@ var CatalogModule = class {
|
|
|
844
898
|
{ body: input }
|
|
845
899
|
);
|
|
846
900
|
}
|
|
847
|
-
/**
|
|
901
|
+
/**
|
|
902
|
+
* List configured payment methods (filtered by currency). `locale` picks the
|
|
903
|
+
* language of `instruments[].label` / `swifts[].label` (SDK 1.14.0); pass the
|
|
904
|
+
* locale of the page so the instrument tiles read in the shopper's language.
|
|
905
|
+
*/
|
|
848
906
|
async listPaymentMethods(opts) {
|
|
849
907
|
const query = {};
|
|
850
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
908
|
+
if (_optionalChain([opts, 'optionalAccess', _33 => _33.currency])) query.currency = opts.currency;
|
|
909
|
+
if (_optionalChain([opts, 'optionalAccess', _34 => _34.locale])) query.locale = opts.locale;
|
|
910
|
+
if (_optionalChain([opts, 'optionalAccess', _35 => _35.country])) query.country = opts.country;
|
|
911
|
+
if (_optionalChain([opts, 'optionalAccess', _36 => _36.shippingMethodId])) query.shippingMethodId = opts.shippingMethodId;
|
|
851
912
|
return this.client.request(
|
|
852
913
|
"GET",
|
|
853
914
|
"/catalog/payment-methods",
|
|
854
915
|
{ query }
|
|
855
916
|
);
|
|
856
917
|
}
|
|
918
|
+
/** Alias of `listPaymentMethods` (SDK 1.14.0). */
|
|
919
|
+
paymentMethods(opts) {
|
|
920
|
+
return this.listPaymentMethods(opts);
|
|
921
|
+
}
|
|
857
922
|
};
|
|
858
923
|
var AuthModule = class {
|
|
859
924
|
constructor(client) {
|
|
@@ -1007,6 +1072,26 @@ var CartModule = class {
|
|
|
1007
1072
|
this.client.emit("cart:updated", res.data);
|
|
1008
1073
|
return res;
|
|
1009
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Tell the cart where the order will ship (and, for B2B, the buyer's VAT
|
|
1077
|
+
* ID) so the VAT breakdown matches the checkout before the address form:
|
|
1078
|
+
* destination-country rate (OSS), 0 % export outside the EU, or reverse
|
|
1079
|
+
* charge for an EU business with a VIES-valid VAT ID. `cart.vatMode` says
|
|
1080
|
+
* which rule applied. SDK 1.17.0.
|
|
1081
|
+
*
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* await client.cart.setDestination({ country: "SK" });
|
|
1084
|
+
* await client.cart.setDestination({ vatId: "SK2020000001" }); // "" clears
|
|
1085
|
+
* ```
|
|
1086
|
+
*/
|
|
1087
|
+
async setDestination(input) {
|
|
1088
|
+
const res = await this.client.request("PUT", "/cart/destination", {
|
|
1089
|
+
body: input
|
|
1090
|
+
});
|
|
1091
|
+
if (res.error) return res;
|
|
1092
|
+
this.client.emit("cart:updated", res.data);
|
|
1093
|
+
return res;
|
|
1094
|
+
}
|
|
1010
1095
|
/** Update item quantity */
|
|
1011
1096
|
async updateQuantity(itemId, quantity) {
|
|
1012
1097
|
const res = await this.client.request(
|
|
@@ -1158,13 +1243,37 @@ var OrdersModule = class {
|
|
|
1158
1243
|
constructor(client) {
|
|
1159
1244
|
this.client = client;
|
|
1160
1245
|
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Ask the backend to re-check this order's payment with the gateway.
|
|
1248
|
+
*
|
|
1249
|
+
* Call it on the thank-you page the customer lands on after paying, BEFORE
|
|
1250
|
+
* you read the order. Some gateways (Tatrapay+) have no server-to-server
|
|
1251
|
+
* notification at all, so the customer's return is the only fast way the
|
|
1252
|
+
* payment gets confirmed; for the others it is a safety net for a lost
|
|
1253
|
+
* notification.
|
|
1254
|
+
*
|
|
1255
|
+
* The response is deliberately opaque (`{ok: true}` every time, even for an
|
|
1256
|
+
* order number that does not exist): order numbers are sequential, so
|
|
1257
|
+
* anything else would turn this into a probe for other people's orders.
|
|
1258
|
+
* Read the actual state afterwards through a path that proves entitlement
|
|
1259
|
+
* ({@link get}, {@link track} or the guest access-code flow).
|
|
1260
|
+
*
|
|
1261
|
+
* Never throws for a missing order and never blocks the page: treat a
|
|
1262
|
+
* failure as "not confirmed yet", the backend poller catches up on its own.
|
|
1263
|
+
*/
|
|
1264
|
+
async syncPaymentOnReturn(orderNumber) {
|
|
1265
|
+
return this.client.request(
|
|
1266
|
+
"POST",
|
|
1267
|
+
`/payments/return/${orderNumber}`
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1161
1270
|
/** List customer orders (requires auth) */
|
|
1162
1271
|
async list(options) {
|
|
1163
1272
|
return this.client.request(
|
|
1164
1273
|
"GET",
|
|
1165
1274
|
"/orders",
|
|
1166
1275
|
{
|
|
1167
|
-
query: { page: _optionalChain([options, 'optionalAccess',
|
|
1276
|
+
query: { page: _optionalChain([options, 'optionalAccess', _37 => _37.page]), limit: _optionalChain([options, 'optionalAccess', _38 => _38.limit]) }
|
|
1168
1277
|
}
|
|
1169
1278
|
);
|
|
1170
1279
|
}
|
|
@@ -1510,6 +1619,59 @@ var PagesModule = class {
|
|
|
1510
1619
|
});
|
|
1511
1620
|
}
|
|
1512
1621
|
};
|
|
1622
|
+
var BlogModule = class {
|
|
1623
|
+
constructor(client) {
|
|
1624
|
+
this.client = client;
|
|
1625
|
+
}
|
|
1626
|
+
/** List the site's blogs (active only). */
|
|
1627
|
+
async list(locale) {
|
|
1628
|
+
return this.client.request("GET", "/blogs", {
|
|
1629
|
+
query: { locale }
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
/** Published posts of one blog, newest first (featured first), paginated. */
|
|
1633
|
+
async posts(handle, query = {}) {
|
|
1634
|
+
return this.client.request("GET", `/blogs/${handle}/posts`, {
|
|
1635
|
+
query: {
|
|
1636
|
+
locale: query.locale,
|
|
1637
|
+
page: query.page,
|
|
1638
|
+
limit: query.limit,
|
|
1639
|
+
tag: query.tag
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
/** One published post with sanitised HTML content and related posts. */
|
|
1644
|
+
async post(handle, slug, locale) {
|
|
1645
|
+
return this.client.request("GET", `/blogs/${handle}/posts/${slug}`, {
|
|
1646
|
+
query: { locale }
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
var FormsModule = class {
|
|
1651
|
+
constructor(client) {
|
|
1652
|
+
this.client = client;
|
|
1653
|
+
}
|
|
1654
|
+
/** Public definition of one form (fields + settings). 404 for an unknown or inactive slug. */
|
|
1655
|
+
async get(slug) {
|
|
1656
|
+
return this.client.request(
|
|
1657
|
+
"GET",
|
|
1658
|
+
`/forms/${encodeURIComponent(slug)}`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Submit a response. On `be.forms.validationFailed` the returned error
|
|
1663
|
+
* carries per-field codes: read them with `formFieldErrors(error)`. Other
|
|
1664
|
+
* rejections: `be.forms.consentRequired`, `be.forms.tooLarge`,
|
|
1665
|
+
* `be.forms.formNotFound`; rate limit 10 submits per minute per visitor.
|
|
1666
|
+
*/
|
|
1667
|
+
async submit(slug, input) {
|
|
1668
|
+
return this.client.request(
|
|
1669
|
+
"POST",
|
|
1670
|
+
`/forms/${encodeURIComponent(slug)}/submit`,
|
|
1671
|
+
{ body: input, auth: false }
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1513
1675
|
var WishlistModule = class {
|
|
1514
1676
|
constructor(client) {
|
|
1515
1677
|
this.client = client;
|
|
@@ -1659,7 +1821,7 @@ var ConsentModule = class {
|
|
|
1659
1821
|
}
|
|
1660
1822
|
if (result.error) return { data: null, error: result.error };
|
|
1661
1823
|
return {
|
|
1662
|
-
data: _optionalChain([result, 'access',
|
|
1824
|
+
data: _optionalChain([result, 'access', _39 => _39.data, 'optionalAccess', _40 => _40.consented]) ? result.data.consent : null,
|
|
1663
1825
|
error: null
|
|
1664
1826
|
};
|
|
1665
1827
|
}
|
|
@@ -1746,10 +1908,10 @@ var ShippingModule = class {
|
|
|
1746
1908
|
*/
|
|
1747
1909
|
async listMethods(opts) {
|
|
1748
1910
|
const query = {};
|
|
1749
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1750
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1751
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1752
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
1911
|
+
if (_optionalChain([opts, 'optionalAccess', _41 => _41.currency])) query.currency = opts.currency;
|
|
1912
|
+
if (_optionalChain([opts, 'optionalAccess', _42 => _42.country])) query.country = opts.country;
|
|
1913
|
+
if (_optionalChain([opts, 'optionalAccess', _43 => _43.cartTotal]) != null) query.cartTotal = String(opts.cartTotal);
|
|
1914
|
+
if (_optionalChain([opts, 'optionalAccess', _44 => _44.cartWeightKg]) != null)
|
|
1753
1915
|
query.cartWeightKg = String(opts.cartWeightKg);
|
|
1754
1916
|
return this.client.request(
|
|
1755
1917
|
"GET",
|
|
@@ -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,6 +81,11 @@ 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";
|
|
84
|
+
const rawCode = body?.code;
|
|
85
|
+
if (typeof rawCode === "string") {
|
|
86
|
+
const mapped = BE_CODE_TO_ERROR[rawCode];
|
|
87
|
+
if (mapped) return mapped;
|
|
88
|
+
}
|
|
52
89
|
const msg = (body?.message || "").toLowerCase();
|
|
53
90
|
if (msg.includes("invalid") && msg.includes("password"))
|
|
54
91
|
return "INVALID_CREDENTIALS";
|
|
@@ -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);
|
|
@@ -225,6 +264,21 @@ var BehioStorefront = class {
|
|
|
225
264
|
body: input
|
|
226
265
|
});
|
|
227
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Visitor messages from merchant automations (storefront.event action).
|
|
269
|
+
* Consent-gated visitor id; each message carries a merchant-defined `name`
|
|
270
|
+
* and free-form `payload` the template reacts to (modal, banner, ...).
|
|
271
|
+
* Messages stay listed until acknowledged via `ackVisitorMessage`.
|
|
272
|
+
*/
|
|
273
|
+
async getVisitorMessages(visitorId) {
|
|
274
|
+
return this.request("GET", "/messages", { query: { visitorId } });
|
|
275
|
+
}
|
|
276
|
+
/** Acknowledge a visitor message so it is not delivered again. */
|
|
277
|
+
async ackVisitorMessage(messageId, visitorId) {
|
|
278
|
+
return this.request("POST", `/messages/${messageId}/ack`, {
|
|
279
|
+
query: { visitorId }
|
|
280
|
+
});
|
|
281
|
+
}
|
|
228
282
|
/** Get basic shop info */
|
|
229
283
|
async getShopInfo() {
|
|
230
284
|
return this.request("GET", "/shop");
|
|
@@ -844,16 +898,27 @@ var CatalogModule = class {
|
|
|
844
898
|
{ body: input }
|
|
845
899
|
);
|
|
846
900
|
}
|
|
847
|
-
/**
|
|
901
|
+
/**
|
|
902
|
+
* List configured payment methods (filtered by currency). `locale` picks the
|
|
903
|
+
* language of `instruments[].label` / `swifts[].label` (SDK 1.14.0); pass the
|
|
904
|
+
* locale of the page so the instrument tiles read in the shopper's language.
|
|
905
|
+
*/
|
|
848
906
|
async listPaymentMethods(opts) {
|
|
849
907
|
const query = {};
|
|
850
908
|
if (opts?.currency) query.currency = opts.currency;
|
|
909
|
+
if (opts?.locale) query.locale = opts.locale;
|
|
910
|
+
if (opts?.country) query.country = opts.country;
|
|
911
|
+
if (opts?.shippingMethodId) query.shippingMethodId = opts.shippingMethodId;
|
|
851
912
|
return this.client.request(
|
|
852
913
|
"GET",
|
|
853
914
|
"/catalog/payment-methods",
|
|
854
915
|
{ query }
|
|
855
916
|
);
|
|
856
917
|
}
|
|
918
|
+
/** Alias of `listPaymentMethods` (SDK 1.14.0). */
|
|
919
|
+
paymentMethods(opts) {
|
|
920
|
+
return this.listPaymentMethods(opts);
|
|
921
|
+
}
|
|
857
922
|
};
|
|
858
923
|
var AuthModule = class {
|
|
859
924
|
constructor(client) {
|
|
@@ -1007,6 +1072,26 @@ var CartModule = class {
|
|
|
1007
1072
|
this.client.emit("cart:updated", res.data);
|
|
1008
1073
|
return res;
|
|
1009
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Tell the cart where the order will ship (and, for B2B, the buyer's VAT
|
|
1077
|
+
* ID) so the VAT breakdown matches the checkout before the address form:
|
|
1078
|
+
* destination-country rate (OSS), 0 % export outside the EU, or reverse
|
|
1079
|
+
* charge for an EU business with a VIES-valid VAT ID. `cart.vatMode` says
|
|
1080
|
+
* which rule applied. SDK 1.17.0.
|
|
1081
|
+
*
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* await client.cart.setDestination({ country: "SK" });
|
|
1084
|
+
* await client.cart.setDestination({ vatId: "SK2020000001" }); // "" clears
|
|
1085
|
+
* ```
|
|
1086
|
+
*/
|
|
1087
|
+
async setDestination(input) {
|
|
1088
|
+
const res = await this.client.request("PUT", "/cart/destination", {
|
|
1089
|
+
body: input
|
|
1090
|
+
});
|
|
1091
|
+
if (res.error) return res;
|
|
1092
|
+
this.client.emit("cart:updated", res.data);
|
|
1093
|
+
return res;
|
|
1094
|
+
}
|
|
1010
1095
|
/** Update item quantity */
|
|
1011
1096
|
async updateQuantity(itemId, quantity) {
|
|
1012
1097
|
const res = await this.client.request(
|
|
@@ -1158,6 +1243,30 @@ var OrdersModule = class {
|
|
|
1158
1243
|
constructor(client) {
|
|
1159
1244
|
this.client = client;
|
|
1160
1245
|
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Ask the backend to re-check this order's payment with the gateway.
|
|
1248
|
+
*
|
|
1249
|
+
* Call it on the thank-you page the customer lands on after paying, BEFORE
|
|
1250
|
+
* you read the order. Some gateways (Tatrapay+) have no server-to-server
|
|
1251
|
+
* notification at all, so the customer's return is the only fast way the
|
|
1252
|
+
* payment gets confirmed; for the others it is a safety net for a lost
|
|
1253
|
+
* notification.
|
|
1254
|
+
*
|
|
1255
|
+
* The response is deliberately opaque (`{ok: true}` every time, even for an
|
|
1256
|
+
* order number that does not exist): order numbers are sequential, so
|
|
1257
|
+
* anything else would turn this into a probe for other people's orders.
|
|
1258
|
+
* Read the actual state afterwards through a path that proves entitlement
|
|
1259
|
+
* ({@link get}, {@link track} or the guest access-code flow).
|
|
1260
|
+
*
|
|
1261
|
+
* Never throws for a missing order and never blocks the page: treat a
|
|
1262
|
+
* failure as "not confirmed yet", the backend poller catches up on its own.
|
|
1263
|
+
*/
|
|
1264
|
+
async syncPaymentOnReturn(orderNumber) {
|
|
1265
|
+
return this.client.request(
|
|
1266
|
+
"POST",
|
|
1267
|
+
`/payments/return/${orderNumber}`
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1161
1270
|
/** List customer orders (requires auth) */
|
|
1162
1271
|
async list(options) {
|
|
1163
1272
|
return this.client.request(
|
|
@@ -1510,6 +1619,59 @@ var PagesModule = class {
|
|
|
1510
1619
|
});
|
|
1511
1620
|
}
|
|
1512
1621
|
};
|
|
1622
|
+
var BlogModule = class {
|
|
1623
|
+
constructor(client) {
|
|
1624
|
+
this.client = client;
|
|
1625
|
+
}
|
|
1626
|
+
/** List the site's blogs (active only). */
|
|
1627
|
+
async list(locale) {
|
|
1628
|
+
return this.client.request("GET", "/blogs", {
|
|
1629
|
+
query: { locale }
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
/** Published posts of one blog, newest first (featured first), paginated. */
|
|
1633
|
+
async posts(handle, query = {}) {
|
|
1634
|
+
return this.client.request("GET", `/blogs/${handle}/posts`, {
|
|
1635
|
+
query: {
|
|
1636
|
+
locale: query.locale,
|
|
1637
|
+
page: query.page,
|
|
1638
|
+
limit: query.limit,
|
|
1639
|
+
tag: query.tag
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
/** One published post with sanitised HTML content and related posts. */
|
|
1644
|
+
async post(handle, slug, locale) {
|
|
1645
|
+
return this.client.request("GET", `/blogs/${handle}/posts/${slug}`, {
|
|
1646
|
+
query: { locale }
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
var FormsModule = class {
|
|
1651
|
+
constructor(client) {
|
|
1652
|
+
this.client = client;
|
|
1653
|
+
}
|
|
1654
|
+
/** Public definition of one form (fields + settings). 404 for an unknown or inactive slug. */
|
|
1655
|
+
async get(slug) {
|
|
1656
|
+
return this.client.request(
|
|
1657
|
+
"GET",
|
|
1658
|
+
`/forms/${encodeURIComponent(slug)}`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Submit a response. On `be.forms.validationFailed` the returned error
|
|
1663
|
+
* carries per-field codes: read them with `formFieldErrors(error)`. Other
|
|
1664
|
+
* rejections: `be.forms.consentRequired`, `be.forms.tooLarge`,
|
|
1665
|
+
* `be.forms.formNotFound`; rate limit 10 submits per minute per visitor.
|
|
1666
|
+
*/
|
|
1667
|
+
async submit(slug, input) {
|
|
1668
|
+
return this.client.request(
|
|
1669
|
+
"POST",
|
|
1670
|
+
`/forms/${encodeURIComponent(slug)}/submit`,
|
|
1671
|
+
{ body: input, auth: false }
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1513
1675
|
var WishlistModule = class {
|
|
1514
1676
|
constructor(client) {
|
|
1515
1677
|
this.client = client;
|