@behio/storefront-sdk 0.2.0 → 0.4.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 +67 -1340
- package/dist/{chunk-EFFPOXWL.mjs → chunk-L5KVNLTD.mjs} +176 -63
- package/dist/{chunk-R7H7E5BX.js → chunk-YLKO3SUR.js} +179 -66
- package/dist/index.d.mts +173 -86
- package/dist/index.d.ts +173 -86
- package/dist/index.js +8 -2
- package/dist/index.mjs +9 -3
- package/dist/react.d.mts +53 -4
- package/dist/react.d.ts +53 -4
- package/dist/react.js +167 -86
- package/dist/react.mjs +187 -106
- package/package.json +1 -1
|
@@ -72,6 +72,40 @@ var BehioNetworkError = class extends Error {
|
|
|
72
72
|
this.code = isTimeout ? "TIMEOUT" : "NETWORK_ERROR";
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
|
+
function ok(data) {
|
|
76
|
+
return { data, error: null };
|
|
77
|
+
}
|
|
78
|
+
function err(error) {
|
|
79
|
+
return { data: null, error };
|
|
80
|
+
}
|
|
81
|
+
function toSdkError(err2) {
|
|
82
|
+
if (err2 instanceof BehioApiError) {
|
|
83
|
+
return {
|
|
84
|
+
code: err2.code,
|
|
85
|
+
message: err2.message,
|
|
86
|
+
status: err2.status,
|
|
87
|
+
body: err2.body,
|
|
88
|
+
isRetryable: err2.isRetryable,
|
|
89
|
+
cause: err2
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (err2 instanceof BehioNetworkError) {
|
|
93
|
+
return {
|
|
94
|
+
code: err2.code,
|
|
95
|
+
message: err2.message,
|
|
96
|
+
status: null,
|
|
97
|
+
isRetryable: err2.isRetryable,
|
|
98
|
+
cause: err2
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
code: "UNKNOWN",
|
|
103
|
+
message: err2 instanceof Error ? err2.message : String(err2),
|
|
104
|
+
status: null,
|
|
105
|
+
isRetryable: false,
|
|
106
|
+
cause: err2
|
|
107
|
+
};
|
|
108
|
+
}
|
|
75
109
|
|
|
76
110
|
// src/client.ts
|
|
77
111
|
var BehioStorefront = class {
|
|
@@ -107,6 +141,7 @@ var BehioStorefront = class {
|
|
|
107
141
|
this.returns = new ReturnsModule(this);
|
|
108
142
|
this.consent = new ConsentModule(this);
|
|
109
143
|
this.quotes = new QuotesModule(this);
|
|
144
|
+
this.addresses = new AddressModule(this);
|
|
110
145
|
}
|
|
111
146
|
// --- Public methods ---
|
|
112
147
|
/** Get basic shop info */
|
|
@@ -196,11 +231,17 @@ var BehioStorefront = class {
|
|
|
196
231
|
this.isRefreshing = true;
|
|
197
232
|
this.refreshPromise = (async () => {
|
|
198
233
|
try {
|
|
199
|
-
await this.auth.refresh();
|
|
234
|
+
const res = await this.auth.refresh();
|
|
235
|
+
if (res.error) {
|
|
236
|
+
this.clearTokens();
|
|
237
|
+
this.emit("auth:token-refresh-failed");
|
|
238
|
+
throw new BehioApiError(401, null, "Token refresh failed");
|
|
239
|
+
}
|
|
200
240
|
this.emit("auth:token-refresh");
|
|
201
|
-
} catch {
|
|
241
|
+
} catch (err2) {
|
|
202
242
|
this.clearTokens();
|
|
203
243
|
this.emit("auth:token-refresh-failed");
|
|
244
|
+
if (err2 instanceof BehioApiError) throw err2;
|
|
204
245
|
throw new BehioApiError(401, null, "Token refresh failed");
|
|
205
246
|
} finally {
|
|
206
247
|
this.isRefreshing = false;
|
|
@@ -209,9 +250,29 @@ var BehioStorefront = class {
|
|
|
209
250
|
})();
|
|
210
251
|
return this.refreshPromise;
|
|
211
252
|
}
|
|
212
|
-
// ---
|
|
213
|
-
/**
|
|
253
|
+
// --- Public request wrapper (SdkResult) ---
|
|
254
|
+
/**
|
|
255
|
+
* Every public module method funnels through here. Internally calls
|
|
256
|
+
* `rawRequest` (which throws on failure) and maps thrown errors to
|
|
257
|
+
* `SdkError` so the public surface can return `SdkResult<T>`.
|
|
258
|
+
*
|
|
259
|
+
* @internal — don't call from outside the SDK; use the typed module
|
|
260
|
+
* methods (behio.catalog.*, behio.cart.*, …) instead.
|
|
261
|
+
*/
|
|
214
262
|
async request(method, path, options) {
|
|
263
|
+
try {
|
|
264
|
+
const data = await this.rawRequest(method, path, options);
|
|
265
|
+
return ok(data);
|
|
266
|
+
} catch (err2) {
|
|
267
|
+
return { data: null, error: toSdkError(err2) };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Throws on failure (API error / network / timeout). Kept private so
|
|
272
|
+
* internal auth refresh recursion keeps its existing control flow —
|
|
273
|
+
* public callers must go through `request()` which returns Result.
|
|
274
|
+
*/
|
|
275
|
+
async rawRequest(method, path, options) {
|
|
215
276
|
const params = new URLSearchParams();
|
|
216
277
|
if (options?.query) {
|
|
217
278
|
for (const [key, value] of Object.entries(options.query)) {
|
|
@@ -274,11 +335,11 @@ var BehioStorefront = class {
|
|
|
274
335
|
body: interceptedConfig.body,
|
|
275
336
|
signal: controller.signal
|
|
276
337
|
});
|
|
277
|
-
} catch (
|
|
338
|
+
} catch (err2) {
|
|
278
339
|
clearTimeout(timeoutId);
|
|
279
|
-
const isAbort =
|
|
340
|
+
const isAbort = err2 instanceof DOMException && err2.name === "AbortError";
|
|
280
341
|
const networkErr = new BehioNetworkError(
|
|
281
|
-
isAbort ? "Request timed out" :
|
|
342
|
+
isAbort ? "Request timed out" : err2.message || "Network error",
|
|
282
343
|
isAbort
|
|
283
344
|
);
|
|
284
345
|
this.emit("error", networkErr);
|
|
@@ -304,7 +365,7 @@ var BehioStorefront = class {
|
|
|
304
365
|
if (res.status === 401 && this.refreshToken && options?.auth !== false && !options?._isRetryAfterRefresh) {
|
|
305
366
|
try {
|
|
306
367
|
await this.handleTokenRefresh();
|
|
307
|
-
return this.
|
|
368
|
+
return this.rawRequest(method, path, { ...options, _isRetryAfterRefresh: true });
|
|
308
369
|
} catch {
|
|
309
370
|
this.emit("error", apiError);
|
|
310
371
|
throw apiError;
|
|
@@ -370,7 +431,8 @@ var CatalogModule = class {
|
|
|
370
431
|
/** Get category tree */
|
|
371
432
|
async getCategories(locale) {
|
|
372
433
|
const res = await this.client.request("GET", "/catalog/categories", { query: { locale } });
|
|
373
|
-
|
|
434
|
+
if (res.error) return res;
|
|
435
|
+
return ok({ categories: res.data.categories || res.data.items || [] });
|
|
374
436
|
}
|
|
375
437
|
/** Get category detail by slug */
|
|
376
438
|
async getCategory(slug, locale) {
|
|
@@ -391,7 +453,8 @@ var CatalogModule = class {
|
|
|
391
453
|
/** Get all labels */
|
|
392
454
|
async getLabels(locale) {
|
|
393
455
|
const res = await this.client.request("GET", "/catalog/labels", { query: { locale } });
|
|
394
|
-
|
|
456
|
+
if (res.error) return res;
|
|
457
|
+
return ok({ labels: res.data.labels || res.data.items || [] });
|
|
395
458
|
}
|
|
396
459
|
/** Get featured products */
|
|
397
460
|
async getFeatured(options) {
|
|
@@ -417,7 +480,10 @@ var CatalogModule = class {
|
|
|
417
480
|
}
|
|
418
481
|
/** Cross-sell / related / upsell products for a product */
|
|
419
482
|
async getCrossSell(productSlug) {
|
|
420
|
-
return this.client.request(
|
|
483
|
+
return this.client.request(
|
|
484
|
+
"GET",
|
|
485
|
+
`/catalog/products/${productSlug}/cross-sell`
|
|
486
|
+
);
|
|
421
487
|
}
|
|
422
488
|
/** Active promotions applicable to a product (with countdown end time) */
|
|
423
489
|
async getProductPromotions(productSlug) {
|
|
@@ -434,44 +500,57 @@ var AuthModule = class {
|
|
|
434
500
|
}
|
|
435
501
|
/** Register a new customer */
|
|
436
502
|
async register(input) {
|
|
437
|
-
const
|
|
503
|
+
const res = await this.client.request("POST", "/auth/register", {
|
|
438
504
|
body: input,
|
|
439
505
|
auth: false
|
|
440
506
|
});
|
|
441
|
-
|
|
507
|
+
if (res.error) return res;
|
|
508
|
+
this.client.setTokens(res.data);
|
|
442
509
|
this.client.emit("auth:login", { email: input.email });
|
|
443
|
-
return
|
|
510
|
+
return res;
|
|
444
511
|
}
|
|
445
512
|
/** Login with email and password */
|
|
446
513
|
async login(input) {
|
|
447
|
-
const
|
|
514
|
+
const res = await this.client.request("POST", "/auth/login", {
|
|
448
515
|
body: input,
|
|
449
516
|
auth: false
|
|
450
517
|
});
|
|
451
|
-
|
|
518
|
+
if (res.error) return res;
|
|
519
|
+
this.client.setTokens(res.data);
|
|
452
520
|
this.client.emit("auth:login", { email: input.email });
|
|
453
|
-
return
|
|
521
|
+
return res;
|
|
454
522
|
}
|
|
455
523
|
/** Refresh access token using refresh token */
|
|
456
524
|
async refresh(refreshToken) {
|
|
457
525
|
const token = refreshToken || this.client.getRefreshToken();
|
|
458
|
-
if (!token)
|
|
459
|
-
|
|
526
|
+
if (!token) {
|
|
527
|
+
return {
|
|
528
|
+
data: null,
|
|
529
|
+
error: {
|
|
530
|
+
code: "UNAUTHORIZED",
|
|
531
|
+
message: "No refresh token available",
|
|
532
|
+
status: null,
|
|
533
|
+
isRetryable: false
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const res = await this.client.request("POST", "/auth/refresh", {
|
|
460
538
|
body: { refreshToken: token },
|
|
461
539
|
auth: false
|
|
462
540
|
});
|
|
463
|
-
|
|
464
|
-
|
|
541
|
+
if (res.error) return res;
|
|
542
|
+
this.client.setTokens(res.data);
|
|
543
|
+
return res;
|
|
465
544
|
}
|
|
466
545
|
/** Logout (invalidate refresh token) */
|
|
467
546
|
async logout(refreshToken) {
|
|
468
547
|
const token = refreshToken || this.client.getRefreshToken();
|
|
469
|
-
const
|
|
548
|
+
const res = await this.client.request("POST", "/auth/logout", {
|
|
470
549
|
body: { refreshToken: token }
|
|
471
550
|
});
|
|
472
551
|
this.client.clearTokens();
|
|
473
552
|
this.client.emit("auth:logout");
|
|
474
|
-
return
|
|
553
|
+
return res;
|
|
475
554
|
}
|
|
476
555
|
/** Request password reset email */
|
|
477
556
|
async forgotPassword(email) {
|
|
@@ -509,90 +588,102 @@ var CartModule = class {
|
|
|
509
588
|
}
|
|
510
589
|
/** Add item to cart */
|
|
511
590
|
async addItem(input) {
|
|
512
|
-
const
|
|
591
|
+
const res = await this.client.request("POST", "/cart/items", {
|
|
513
592
|
body: { whItemId: input.productId, quantity: input.quantity }
|
|
514
593
|
});
|
|
515
|
-
if (
|
|
516
|
-
|
|
594
|
+
if (res.error) return res;
|
|
595
|
+
if (res.data.newSessionToken) {
|
|
596
|
+
this.client.setCartSession(res.data.newSessionToken);
|
|
517
597
|
}
|
|
518
|
-
this.client.emit("cart:updated",
|
|
519
|
-
return
|
|
598
|
+
this.client.emit("cart:updated", res.data);
|
|
599
|
+
return res;
|
|
520
600
|
}
|
|
521
601
|
/** Update item quantity */
|
|
522
602
|
async updateQuantity(itemId, quantity) {
|
|
523
|
-
const
|
|
603
|
+
const res = await this.client.request("PATCH", `/cart/items/${itemId}`, {
|
|
524
604
|
body: { quantity }
|
|
525
605
|
});
|
|
526
|
-
|
|
527
|
-
|
|
606
|
+
if (res.error) return res;
|
|
607
|
+
this.client.emit("cart:updated", res.data);
|
|
608
|
+
return res;
|
|
528
609
|
}
|
|
529
610
|
/** Remove item from cart */
|
|
530
611
|
async removeItem(itemId) {
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
612
|
+
const res = await this.client.request("DELETE", `/cart/items/${itemId}`);
|
|
613
|
+
if (res.error) return res;
|
|
614
|
+
this.client.emit("cart:updated", res.data);
|
|
615
|
+
return res;
|
|
534
616
|
}
|
|
535
617
|
/** Clear entire cart */
|
|
536
618
|
async clear() {
|
|
537
|
-
const
|
|
619
|
+
const res = await this.client.request("DELETE", "/cart");
|
|
620
|
+
if (res.error) return res;
|
|
538
621
|
this.client.emit("cart:cleared");
|
|
539
|
-
return
|
|
622
|
+
return res;
|
|
540
623
|
}
|
|
541
624
|
/** Apply a gift card code to the cart. Balance is deducted at checkout. */
|
|
542
625
|
async applyGiftCard(code) {
|
|
543
|
-
const
|
|
626
|
+
const res = await this.client.request("POST", "/cart/gift-card", {
|
|
544
627
|
body: { code }
|
|
545
628
|
});
|
|
546
|
-
|
|
547
|
-
|
|
629
|
+
if (res.error) return res;
|
|
630
|
+
this.client.emit("cart:updated", res.data);
|
|
631
|
+
return res;
|
|
548
632
|
}
|
|
549
633
|
/** Remove a gift card from the cart */
|
|
550
634
|
async removeGiftCard() {
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
635
|
+
const res = await this.client.request("DELETE", "/cart/gift-card");
|
|
636
|
+
if (res.error) return res;
|
|
637
|
+
this.client.emit("cart:updated", res.data);
|
|
638
|
+
return res;
|
|
554
639
|
}
|
|
555
640
|
/** Add a bundle to the cart (price is locked at the bundle's current price) */
|
|
556
641
|
async addBundle(bundleId, quantity = 1) {
|
|
557
|
-
const
|
|
642
|
+
const res = await this.client.request("POST", "/cart/bundles", {
|
|
558
643
|
body: { bundleId, quantity }
|
|
559
644
|
});
|
|
560
|
-
|
|
561
|
-
|
|
645
|
+
if (res.error) return res;
|
|
646
|
+
this.client.emit("cart:updated", res.data);
|
|
647
|
+
return res;
|
|
562
648
|
}
|
|
563
649
|
/** Update quantity of a bundle already in the cart */
|
|
564
650
|
async updateBundleQuantity(bundleId, quantity) {
|
|
565
|
-
const
|
|
651
|
+
const res = await this.client.request("PATCH", `/cart/bundles/${bundleId}`, {
|
|
566
652
|
body: { quantity }
|
|
567
653
|
});
|
|
568
|
-
|
|
569
|
-
|
|
654
|
+
if (res.error) return res;
|
|
655
|
+
this.client.emit("cart:updated", res.data);
|
|
656
|
+
return res;
|
|
570
657
|
}
|
|
571
658
|
/** Remove a bundle from the cart */
|
|
572
659
|
async removeBundle(bundleId) {
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
660
|
+
const res = await this.client.request("DELETE", `/cart/bundles/${bundleId}`);
|
|
661
|
+
if (res.error) return res;
|
|
662
|
+
this.client.emit("cart:updated", res.data);
|
|
663
|
+
return res;
|
|
576
664
|
}
|
|
577
665
|
/** Merge anonymous cart into authenticated customer cart */
|
|
578
666
|
async merge() {
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
667
|
+
const res = await this.client.request("POST", "/cart/merge");
|
|
668
|
+
if (res.error) return res;
|
|
669
|
+
this.client.emit("cart:updated", res.data);
|
|
670
|
+
return res;
|
|
582
671
|
}
|
|
583
672
|
/** Apply discount code */
|
|
584
673
|
async applyDiscount(code) {
|
|
585
|
-
const
|
|
674
|
+
const res = await this.client.request("POST", "/cart/discount", {
|
|
586
675
|
body: { code }
|
|
587
676
|
});
|
|
588
|
-
|
|
589
|
-
|
|
677
|
+
if (res.error) return res;
|
|
678
|
+
this.client.emit("cart:updated", res.data);
|
|
679
|
+
return res;
|
|
590
680
|
}
|
|
591
681
|
/** Remove discount code */
|
|
592
682
|
async removeDiscount() {
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
683
|
+
const res = await this.client.request("DELETE", "/cart/discount");
|
|
684
|
+
if (res.error) return res;
|
|
685
|
+
this.client.emit("cart:updated", res.data);
|
|
686
|
+
return res;
|
|
596
687
|
}
|
|
597
688
|
};
|
|
598
689
|
var CheckoutModule = class {
|
|
@@ -601,13 +692,14 @@ var CheckoutModule = class {
|
|
|
601
692
|
}
|
|
602
693
|
/** Create order from cart */
|
|
603
694
|
async createOrder(input) {
|
|
604
|
-
const
|
|
695
|
+
const res = await this.client.request("POST", "/checkout", {
|
|
605
696
|
body: input
|
|
606
697
|
});
|
|
698
|
+
if (res.error) return res;
|
|
607
699
|
this.client.clearCartSession();
|
|
608
|
-
this.client.emit("order:created",
|
|
700
|
+
this.client.emit("order:created", res.data);
|
|
609
701
|
this.client.emit("cart:cleared");
|
|
610
|
-
return
|
|
702
|
+
return res;
|
|
611
703
|
}
|
|
612
704
|
};
|
|
613
705
|
var OrdersModule = class {
|
|
@@ -753,6 +845,24 @@ var QuotesModule = class {
|
|
|
753
845
|
return this.client.request("GET", `/quotes/${quoteId}`);
|
|
754
846
|
}
|
|
755
847
|
};
|
|
848
|
+
var AddressModule = class {
|
|
849
|
+
constructor(client) {
|
|
850
|
+
this.client = client;
|
|
851
|
+
}
|
|
852
|
+
/** Search for address suggestions (debounce on your side, or use the React hook) */
|
|
853
|
+
async autocomplete(query, country) {
|
|
854
|
+
if (!query || query.length < 2) return ok({ suggestions: [] });
|
|
855
|
+
return this.client.request("GET", "/addresses/autocomplete", {
|
|
856
|
+
query: { q: query, country }
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
/** Get full structured address from a suggestion's placeId */
|
|
860
|
+
async getDetail(placeId) {
|
|
861
|
+
return this.client.request("GET", "/addresses/place-detail", {
|
|
862
|
+
query: { placeId }
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
};
|
|
756
866
|
|
|
757
867
|
export {
|
|
758
868
|
ProductSort,
|
|
@@ -762,5 +872,8 @@ export {
|
|
|
762
872
|
AddressTypes,
|
|
763
873
|
BehioApiError,
|
|
764
874
|
BehioNetworkError,
|
|
875
|
+
ok,
|
|
876
|
+
err,
|
|
877
|
+
toSdkError,
|
|
765
878
|
BehioStorefront
|
|
766
879
|
};
|