@behio/storefront-sdk 1.19.0 → 2.0.1
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 +214 -19
- package/dist/{chunk-W2P2D3EZ.mjs → chunk-SCFR6JH7.mjs} +73 -53
- package/dist/{chunk-GAYYRRUL.js → chunk-TDIAWIQR.js} +97 -77
- package/dist/{client-DhW5x2Zr.d.mts → client-C4dnHtID.d.mts} +197 -26
- package/dist/{client-DhW5x2Zr.d.ts → client-C4dnHtID.d.ts} +197 -26
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +84 -23
- package/dist/index.mjs +68 -7
- package/dist/next.d.mts +3 -1
- package/dist/next.d.ts +3 -1
- package/dist/next.js +9 -5
- package/dist/next.mjs +5 -1
- package/dist/react.d.mts +218 -32
- package/dist/react.d.ts +218 -32
- package/dist/react.js +166 -125
- package/dist/react.mjs +166 -126
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,11 @@ Behio gives you a complete e-commerce backend (products, inventory, orders, cust
|
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
|
+
React cart mutations in 2.0.1 cancel older cart reads before writing and before
|
|
17
|
+
accepting the server response. This includes adding, clearing, applying or
|
|
18
|
+
removing a discount, and merging baskets. A delayed read cannot restore an old
|
|
19
|
+
basket after one of those actions succeeds.
|
|
20
|
+
|
|
16
21
|
```bash
|
|
17
22
|
npm install @behio/storefront-sdk
|
|
18
23
|
```
|
|
@@ -26,42 +31,94 @@ const storefront = new BehioStorefront({
|
|
|
26
31
|
apiKey: 'pk_live_your_key',
|
|
27
32
|
});
|
|
28
33
|
|
|
29
|
-
// Fetch products
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
// Fetch products and check the typed result.
|
|
35
|
+
const products = await storefront.catalog.getProducts({limit: 12});
|
|
36
|
+
if (products.error || !products.data.items.length) throw new Error('No products available');
|
|
37
|
+
await storefront.cart.addItem({productId: products.data.items[0].id, quantity: 1});
|
|
38
|
+
|
|
39
|
+
// After the customer enters addresses and selects shipping/payment:
|
|
40
|
+
const previewResult = await storefront.checkout.preview(checkoutInput);
|
|
41
|
+
if (previewResult.error) throw new Error(previewResult.error.message);
|
|
42
|
+
// Render previewResult.data.grandTotal. Wait for the customer to submit.
|
|
43
|
+
const orderResult = await storefront.checkout.createOrder({
|
|
44
|
+
...checkoutInput,
|
|
45
|
+
previewToken: previewResult.data.previewToken,
|
|
39
46
|
});
|
|
47
|
+
if (orderResult.error) throw new Error(orderResult.error.message);
|
|
48
|
+
// Redirect to paymentRedirectUrl, or display the receipt for orderResult.data.
|
|
49
|
+
|
|
40
50
|
```
|
|
41
51
|
|
|
52
|
+
Paid orders may briefly return `contentDeliveryPending: true` while purchased
|
|
53
|
+
files and course access are being assigned. Render this state in the initial
|
|
54
|
+
receipt HTML and refresh the authenticated order detail until it clears. Tell
|
|
55
|
+
the customer that payment was received and that they do not need to place a
|
|
56
|
+
second order. Existing downloads remain usable while other content is pending.
|
|
57
|
+
Behio stores this work with the payment transaction and retries interrupted or
|
|
58
|
+
failed delivery after restart. Repeating delivery preserves access expiry,
|
|
59
|
+
download counters and course progress.
|
|
60
|
+
|
|
61
|
+
## Product resources and variant pages
|
|
62
|
+
|
|
63
|
+
Render `resolveVariantContent(product, selectedVariant).assetGroups` in the
|
|
64
|
+
initial HTML. A variant with public resources replaces the parent's groups; an
|
|
65
|
+
empty or absent array inherits them. Render every group and respect item order,
|
|
66
|
+
localized titles and sanitized descriptions. Files are normal public links,
|
|
67
|
+
images open at full size, uploaded videos use native controls, and external
|
|
68
|
+
YouTube/Vimeo players load after an explicit click. Preserve Vimeo's unlisted
|
|
69
|
+
privacy hash. These groups never contain purchased download URLs.
|
|
70
|
+
|
|
71
|
+
`requiresShipping` and `isDigital` belong to the selected variant. Use them for
|
|
72
|
+
its delivery information; the cart remains authoritative for the whole order.
|
|
73
|
+
`catalogSiblings` contains separate product pages: render real localized links
|
|
74
|
+
and mark `isCurrent`, preserving the remaining axes in the variant picker.
|
|
75
|
+
Disable values that have no purchasable variant; use `isPurchasable` so zero-stock
|
|
76
|
+
BACKORDER options remain selectable. Clear incompatible axis selections.
|
|
77
|
+
|
|
78
|
+
Product reviews must also be present in the first HTML. Fetch page one on the
|
|
79
|
+
server and pass it as `initialData` to `useProductReviews`; page and limit identify
|
|
80
|
+
the cache entry. Use the selected variant's ID and own rating, render photos and
|
|
81
|
+
merchant replies, and distinguish failures from an empty list. Verified purchase
|
|
82
|
+
requires authenticated order ownership. A helpful vote with `success: false` is
|
|
83
|
+
a duplicate; errors must roll back optimistic counts.
|
|
84
|
+
|
|
42
85
|
## React Hooks
|
|
43
86
|
|
|
87
|
+
Catalog pricing supports a request-specific `country` alongside `currency` and
|
|
88
|
+
customer authentication. `new BehioStorefront({apiKey, country: 'SK', currency:
|
|
89
|
+
'EUR'})` resolves country rules on every catalog surface. `setCountry()` changes
|
|
90
|
+
that default; explicit per-call country values win. It does not change the cart:
|
|
91
|
+
use `cart.setDestination()` for that. The Next.js adapter reads `behio_country`
|
|
92
|
+
for the initial server render. Keep authenticated responses private and include
|
|
93
|
+
both currency and country in guest cache keys.
|
|
94
|
+
When initializing a new cart, `cart.setCurrency()` and `cart.setDestination()`
|
|
95
|
+
save its returned session before the next mutation. Templates should initialize
|
|
96
|
+
this context before the first product or bundle is added, including after the
|
|
97
|
+
previous cart expires.
|
|
98
|
+
|
|
44
99
|
```tsx
|
|
45
|
-
import { BehioProvider, useProducts, useCart
|
|
100
|
+
import { BehioProvider, useProducts, useCart } from '@behio/storefront-sdk/react';
|
|
46
101
|
|
|
47
102
|
function App() {
|
|
48
103
|
return (
|
|
49
|
-
<BehioProvider
|
|
104
|
+
<BehioProvider apiKey="pk_live_your_key">
|
|
50
105
|
<ProductList />
|
|
51
106
|
</BehioProvider>
|
|
52
107
|
);
|
|
53
108
|
}
|
|
54
109
|
|
|
55
110
|
function ProductList() {
|
|
56
|
-
const {
|
|
57
|
-
const
|
|
111
|
+
const { items, isLoading, error } = useProducts({ limit: 12 });
|
|
112
|
+
const { addItem, isAdding } = useCart();
|
|
58
113
|
|
|
59
114
|
if (isLoading) return <div>Loading...</div>;
|
|
60
115
|
|
|
61
|
-
return
|
|
116
|
+
if (error) return <p role="alert">Products could not be loaded.</p>;
|
|
117
|
+
|
|
118
|
+
return items.map(p => (
|
|
62
119
|
<div key={p.id}>
|
|
63
|
-
<h3>{p.name}, {p.price} {p.currency}</h3>
|
|
64
|
-
<button onClick={() =>
|
|
120
|
+
<h3>{p.name}, {p.price ? `${p.price.amount} ${p.price.currency}` : "Sign in to view price"}</h3>
|
|
121
|
+
<button disabled={isAdding || !p.isPurchasable} onClick={() => addItem(p.id, 1).catch(() => window.alert("The item could not be added."))}>
|
|
65
122
|
Add to Cart
|
|
66
123
|
</button>
|
|
67
124
|
</div>
|
|
@@ -69,6 +126,15 @@ function ProductList() {
|
|
|
69
126
|
}
|
|
70
127
|
```
|
|
71
128
|
|
|
129
|
+
The example above shows browser hook usage. Production templates render the
|
|
130
|
+
initial catalog/cart on the server and hydrate their data. HTTP-only sessions
|
|
131
|
+
stay behind Server Actions or a session-bound API. `useCart()` returns `cart`,
|
|
132
|
+
`isEmpty`, `itemCount` and its mutation methods. Update/removal retain the last
|
|
133
|
+
complete server snapshot while pending and accept the entire successful response,
|
|
134
|
+
even when query fetching is disabled. Failed writes do not roll back newer cache
|
|
135
|
+
values. `isEmpty` includes bundle lines; `itemCount` uses server bundle quantities.
|
|
136
|
+
The provider notifies hooks after restoring browser session storage.
|
|
137
|
+
|
|
72
138
|
## What's Included
|
|
73
139
|
|
|
74
140
|
### SDK Modules
|
|
@@ -78,7 +144,7 @@ function ProductList() {
|
|
|
78
144
|
| `catalog` | Products, categories, labels, search, filters, bundles, cross-sell, promotions |
|
|
79
145
|
| `auth` | Register, login, logout, password reset, token refresh |
|
|
80
146
|
| `cart` | Items, discounts, gift cards, bundles, cart merge |
|
|
81
|
-
| `checkout` |
|
|
147
|
+
| `checkout` | Preview the final total and create orders with a signed price review |
|
|
82
148
|
| `orders` | List, detail, tracking, cancel |
|
|
83
149
|
| `customer` | Profile, addresses, password change |
|
|
84
150
|
| `wishlist` | Add, remove, check |
|
|
@@ -94,7 +160,7 @@ function ProductList() {
|
|
|
94
160
|
|
|
95
161
|
### React Hooks (30+)
|
|
96
162
|
|
|
97
|
-
`useProducts` · `useProduct` · `useCategories` · `
|
|
163
|
+
`useProducts` · `useProduct` · `useCategories` · `useFeatured` · `useLabels` · `useSearch` · `useFilters` · `useBundles` · `useBundle` · `useCrossSell` · `useProductPromotions` · `useGiftCardBalance` · `useCart` · `useCheckout` · `useOrders` · `useOrder` · `useCustomer` · `useAddresses` · `useAddressAutocomplete` · `useWishlist` · `useProductReviews` · `useSubmitReview` · `useShopInfo` · `useShopSeo` · `useCartCount` · `useBlogs` · `useBlogPosts` · `useBlogPost` · `useSiteForm` · `useSiteFormSubmit`
|
|
98
164
|
|
|
99
165
|
### Framework Support
|
|
100
166
|
|
|
@@ -122,3 +188,132 @@ Full API reference, framework guides, and examples:
|
|
|
122
188
|
## License
|
|
123
189
|
|
|
124
190
|
MIT
|
|
191
|
+
|
|
192
|
+
## Checkout price review and merchant policies
|
|
193
|
+
|
|
194
|
+
Call `checkout.preview(input)` after the shopper completes delivery and payment choices.
|
|
195
|
+
Render its `grandTotal`, then send its `previewToken` with the final `createOrder` input.
|
|
196
|
+
The token lasts five minutes and is bound to the current cart, prices and choices. A
|
|
197
|
+
changed or expired review returns `be.storefront.checkoutChanged`: refresh the display
|
|
198
|
+
and wait for the shopper to submit again. `useCheckoutPreview` is available for React;
|
|
199
|
+
Server Actions are preferred when storing the guest receipt token in an HttpOnly cookie.
|
|
200
|
+
|
|
201
|
+
`discountTotal` includes loyalty and gift-card deductions. Receipt snapshots add
|
|
202
|
+
`paymentFee`, `roundingAdjustment`, `loyaltyDiscount`, and `giftCardDeducted`; null denotes
|
|
203
|
+
an older order. Never double-subtract a breakdown. Use `cart.checkoutLimits` for min/max
|
|
204
|
+
values in cart currency. Checkout flags, account/password policy, appearance, maintenance,
|
|
205
|
+
SEO and currency display come from the typed shop contract. See the checkout documentation
|
|
206
|
+
for independent legal consents and the exact preview lifecycle.
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
Physical delivery uses `product.requiresShipping` and `cart.requiresShipping`, independently
|
|
210
|
+
of downloadable bonuses. Online-only orders need a billing address and omit shipping.
|
|
211
|
+
`CourseListItem.isRevoked` and `DigitalDownload.isRevoked` distinguish withdrawn access
|
|
212
|
+
from expiry. Course purchases have independent access periods; retries do not extend
|
|
213
|
+
access, and refunding one purchase preserves another valid purchase.
|
|
214
|
+
|
|
215
|
+
Delivery progress uses `Cart.shippingSubtotal`, after product promotions and before order coupons/loyalty/gift cards. `shippingPromotionApplied` marks an active free-delivery promotion. Method thresholds already include the shop-wide threshold converted into the requested currency. Online carts (`requiresShipping: false`) have no delivery progress.
|
|
216
|
+
|
|
217
|
+
Guest course purchases are linked to an account only after its email address is
|
|
218
|
+
verified, even when email verification is optional for registration. Purchases
|
|
219
|
+
made while authenticated belong to that account. A different checkout receipt
|
|
220
|
+
email cannot claim another account’s courses. Unverified accounts cannot extend
|
|
221
|
+
their access using guest purchases sent to the same address. Render a verification
|
|
222
|
+
notice for unverified customers in the course area without revealing guest purchases.
|
|
223
|
+
|
|
224
|
+
Show customer cancellation only for an owned `PENDING` and `UNPAID` order.
|
|
225
|
+
The backend checks both states while holding the order lock; payment can change
|
|
226
|
+
after the page loads, so keep a visible error and refresh the order after rejection.
|
|
227
|
+
Cancellation restores the actual remaining stock deduction once. Warehouse
|
|
228
|
+
allocation identifiers are internal and must never be rendered by a template.
|
|
229
|
+
|
|
230
|
+
### Canonical discovery URLs
|
|
231
|
+
|
|
232
|
+
`catalog.getSitemap(locale?)` follows canonical variant indexing and bound-domain
|
|
233
|
+
product selection. Its entries and `ProductDetail.seo` expose optional
|
|
234
|
+
`localizedSlugs` in 1.20, mapping enabled languages to actual canonical slugs.
|
|
235
|
+
Use those for hreflang and language links; omit unknown translations instead
|
|
236
|
+
of inventing them. Crawler clients must be sessionless. In Next metadata routes,
|
|
237
|
+
call `connection()` before dynamic SDK fetches and verify a production build.
|
|
238
|
+
|
|
239
|
+
### Catalog availability and price privacy
|
|
240
|
+
|
|
241
|
+
A failed price entitlement lookup returns HTTP 503 with `be.storefront.pricingUnavailable`; unrestricted guest pricing is never a fallback. With `throwOnAvailabilityError: true`, catch known SDK availability exceptions only where an explicit server-rendered error and retry replace the failed content. A `{data, error}` check alone cannot handle that exception path. Never treat an outage as zero reviews, zero products, a free offer, or a 404.
|
|
242
|
+
|
|
243
|
+
### Complete category filters
|
|
244
|
+
|
|
245
|
+
`catalog.getCategoryProducts(slug, query)` shares the full `ProductsQuery` serializer with `getProducts`. Price bounds retain zero, boolean filters retain false, arrays use repeated parameters and public parameter/facet selections use JSON. Category, main-list and featured availability follow the same published-variant and stock-mode rules in the backend.
|
|
246
|
+
|
|
247
|
+
### Bundle offers and galleries (2.0, unreleased)
|
|
248
|
+
|
|
249
|
+
`catalog.getBundles({locale, currency, country})` and `getBundle(slug, options)`
|
|
250
|
+
return explicit `priceHidden`, `isPurchasable` and `unavailableReason` alongside
|
|
251
|
+
nullable prices and savings. Never render a null price as zero. Honor
|
|
252
|
+
`quantityRules.minimum`, `step` and nullable `maximum` for additional complete
|
|
253
|
+
sets in the current visitor basket. The server accounts for component steps,
|
|
254
|
+
ordinary rows, other bundles, shared stock and merchant limits. Bundle list/detail
|
|
255
|
+
requests carry the cart session and customer, return private `no-store` data, and
|
|
256
|
+
must not enter shared caches. Hidden stock does not expose a numerical ceiling. React bundle
|
|
257
|
+
hooks accept the same context plus server `initialData` and separate query caches
|
|
258
|
+
by that context. They wait for session restoration; cart and auth hooks refresh
|
|
259
|
+
their ranges after basket or identity changes. Direct core bundle mutations need
|
|
260
|
+
cart refresh plus cancellation/invalidation of both bundle query prefixes.
|
|
261
|
+
API errors require a retry state; they are not an empty catalog.
|
|
262
|
+
|
|
263
|
+
Merge a product's listing `images` with its shared `media` and deduplicate safe
|
|
264
|
+
URLs. A shared video must not hide additional listing photos. Render the image
|
|
265
|
+
links in the first HTML, then enhance thumbnail selection and native video.
|
|
266
|
+
|
|
267
|
+
Bundle checkout previews resolve the current explicit currency offer and component
|
|
268
|
+
rules. Submit the preview token with the confirmed order; handle `checkoutChanged`
|
|
269
|
+
by showing a new preview. Pending orders hold the bundle quota, terminal
|
|
270
|
+
cancellation/refund releases it, and reopening must claim it again. Stock modes
|
|
271
|
+
ALWAYS_AVAILABLE and MADE_TO_ORDER override saved tracked-stock limits throughout
|
|
272
|
+
cart, checkout and order transitions, while retaining exact inventory movements.
|
|
273
|
+
|
|
274
|
+
Cart bundle lines return the **current** offer in the legacy-named
|
|
275
|
+
`bundlePriceSnapshot` field. Stored cart and order snapshots are unchanged by a
|
|
276
|
+
read. Show `priceChanged` and use `quantityControls.decreaseTo` / `increaseTo`
|
|
277
|
+
for exact resulting whole-set quantities; null disables that direction. Keep
|
|
278
|
+
server validation errors next to the row. Older servers can omit these controls. Native
|
|
279
|
+
server-bound quantity/remove forms work before hydration. Bundle components keep
|
|
280
|
+
their own tax rates and their allocated price participates in coupon targeting;
|
|
281
|
+
cart reads recheck coupon eligibility after merchant or cart changes.
|
|
282
|
+
|
|
283
|
+
Version 2 changes bundle catalog prices and `CartBundleLine.bundlePriceSnapshot`
|
|
284
|
+
to nullable values. Update consumers before upgrading: null is unavailable,
|
|
285
|
+
never a free offer. An invalid bundle stays in the cart with `isPurchasable: false`
|
|
286
|
+
and an `unavailableReason`; keep its remove control. `QUANTITY_UNAVAILABLE` may
|
|
287
|
+
be repairable by changing the quantity. When `cart.totalsAvailable === false`,
|
|
288
|
+
hide monetary totals and disable checkout in both cart and mini cart. Do not
|
|
289
|
+
interpret the remaining numeric summary fields as a payable quote. The server
|
|
290
|
+
rechecks component publication, sale windows, quantity, stock and domain selection
|
|
291
|
+
before a bundle mutation. The SDK retains the session returned when an anonymous
|
|
292
|
+
visitor first adds a bundle, so subsequent reads address the same cart.
|
|
293
|
+
|
|
294
|
+
### Retained product rows (2.0, unreleased)
|
|
295
|
+
|
|
296
|
+
SDK 2 sends `X-Behio-Cart-Contract: 2` on every cart request. This opts into
|
|
297
|
+
nullable current prices. On the same v1 route, clients without this header
|
|
298
|
+
retain numeric, same-currency stored price snapshots when the current offer
|
|
299
|
+
disappears. That compatibility projection is not purchase authorization:
|
|
300
|
+
preview and checkout always validate the current offer. An old client still
|
|
301
|
+
needs upgrading to display the new availability and repair controls. Snapshots
|
|
302
|
+
never bypass hidden-price authentication or relabel a foreign currency.
|
|
303
|
+
|
|
304
|
+
A product row also reports `isPurchasable` and `unavailableReason`. Its unit,
|
|
305
|
+
line and tax amounts, plus `product.currentPrice`, are nullable when there is
|
|
306
|
+
no current currency price. Do not revive an old snapshot or show null as zero.
|
|
307
|
+
Keep the row removable; `cart.totalsAvailable` applies to products and bundles.
|
|
308
|
+
|
|
309
|
+
Prefer `item.quantityControls.decreaseTo` and `increaseTo` for cart steppers.
|
|
310
|
+
A null target disables that direction. These targets account for units of the
|
|
311
|
+
same product inside bundles, other listings sharing its stock, merchant minima,
|
|
312
|
+
step multiples and maximums. They can jump directly to a valid repair after a
|
|
313
|
+
merchant edit. Existing fractional units keep their fraction when no merchant
|
|
314
|
+
step is configured. The server validates the resulting basket before writing;
|
|
315
|
+
show returned errors next to the native form and reread the authoritative cart.
|
|
316
|
+
|
|
317
|
+
Domain-bound availability is preserved in currency, destination, code, merge,
|
|
318
|
+
and removal responses. Analytics must omit unknown amounts and must not emit a
|
|
319
|
+
payable cart value when `totalsAvailable` is false.
|
|
@@ -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 = config.timeout ?? 3e4;
|
|
186
187
|
this.retries = 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 = country?.trim().toUpperCase();
|
|
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;
|
|
@@ -497,12 +506,16 @@ 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
|
}
|
|
@@ -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",
|
|
@@ -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`,
|
|
@@ -747,7 +757,7 @@ var CatalogModule = class {
|
|
|
747
757
|
"GET",
|
|
748
758
|
"/catalog/featured",
|
|
749
759
|
{
|
|
750
|
-
query: { locale: options?.locale, currency: options?.currency }
|
|
760
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.country }
|
|
751
761
|
}
|
|
752
762
|
);
|
|
753
763
|
}
|
|
@@ -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: options?.locale, currency: options?.currency }
|
|
861
|
+
query: { locale: options?.locale, currency: options?.currency, country: options?.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: options?.locale, currency: options?.currency } }
|
|
876
|
+
{ query: { locale: options?.locale, currency: options?.currency, country: options?.country } }
|
|
866
877
|
);
|
|
867
878
|
}
|
|
868
879
|
/** Active promotions applicable to a product (with countdown end time) */
|
|
@@ -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
|
|
1181
|
-
async updateBundleQuantity(
|
|
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/${
|
|
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
|
|
1194
|
-
async removeBundle(
|
|
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/${
|
|
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", {
|