brainerce 1.46.2 → 1.47.3
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 +215 -34
- package/dist/bot/bootstrap.global.js +6 -6
- package/dist/bot/index.d.mts +26 -0
- package/dist/bot/index.d.ts +26 -0
- package/dist/bot/index.js +32 -1
- package/dist/bot/index.mjs +32 -1
- package/dist/index.d.mts +432 -16
- package/dist/index.d.ts +432 -16
- package/dist/index.js +562 -15
- package/dist/index.mjs +553 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,12 @@ __export(index_exports, {
|
|
|
34
34
|
BrainerceError: () => BrainerceError,
|
|
35
35
|
RTL_LOCALES: () => RTL_LOCALES,
|
|
36
36
|
SDK_VERSION: () => SDK_VERSION,
|
|
37
|
+
buildArticleJsonLd: () => buildArticleJsonLd,
|
|
38
|
+
buildBreadcrumbJsonLd: () => buildBreadcrumbJsonLd,
|
|
39
|
+
buildCollectionPageJsonLd: () => buildCollectionPageJsonLd,
|
|
40
|
+
buildOrganizationJsonLd: () => buildOrganizationJsonLd,
|
|
41
|
+
buildProductJsonLd: () => buildProductJsonLd,
|
|
42
|
+
buildWebsiteJsonLd: () => buildWebsiteJsonLd,
|
|
37
43
|
createWebhookHandler: () => createWebhookHandler,
|
|
38
44
|
deriveSeoDescription: () => deriveSeoDescription,
|
|
39
45
|
enableDevGuards: () => enableDevGuards,
|
|
@@ -41,9 +47,11 @@ __export(index_exports, {
|
|
|
41
47
|
formatPrice: () => formatPrice,
|
|
42
48
|
formatProductPrice: () => formatProductPrice,
|
|
43
49
|
formatVariantPrice: () => formatVariantPrice,
|
|
50
|
+
getBlogSitemapEntries: () => getBlogSitemapEntries,
|
|
44
51
|
getCartItemImage: () => getCartItemImage,
|
|
45
52
|
getCartItemName: () => getCartItemName,
|
|
46
53
|
getCartTotals: () => getCartTotals,
|
|
54
|
+
getCategorySitemapEntries: () => getCategorySitemapEntries,
|
|
47
55
|
getDescriptionContent: () => getDescriptionContent,
|
|
48
56
|
getDirectionForLocale: () => getDirectionForLocale,
|
|
49
57
|
getPriceDisplay: () => formatPrice,
|
|
@@ -61,9 +69,10 @@ __export(index_exports, {
|
|
|
61
69
|
isCouponApplicableToProduct: () => isCouponApplicableToProduct,
|
|
62
70
|
isHtmlDescription: () => isHtmlDescription,
|
|
63
71
|
isWebhookEventType: () => isWebhookEventType,
|
|
72
|
+
jsonLdScriptProps: () => jsonLdScriptProps,
|
|
64
73
|
parseWebhookEvent: () => parseWebhookEvent,
|
|
65
74
|
safePaymentRedirect: () => safePaymentRedirect,
|
|
66
|
-
stripHtml: () =>
|
|
75
|
+
stripHtml: () => stripHtml2,
|
|
67
76
|
verifyWebhook: () => verifyWebhook
|
|
68
77
|
});
|
|
69
78
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -655,6 +664,7 @@ var BrainerceClient = class {
|
|
|
655
664
|
this.proxyMode = options.proxyMode || false;
|
|
656
665
|
this.analyticsBaseUrl = this.resolveAnalyticsBaseUrl(options.analyticsBaseUrl, resolvedBase);
|
|
657
666
|
this.onAuthError = options.onAuthError;
|
|
667
|
+
this.onCartReset = options.onCartReset;
|
|
658
668
|
this.hydrateSessionCart();
|
|
659
669
|
this.detectRecoverCartFromUrl();
|
|
660
670
|
}
|
|
@@ -1314,6 +1324,34 @@ var BrainerceClient = class {
|
|
|
1314
1324
|
}
|
|
1315
1325
|
throw new BrainerceError("getCategories is only available in vibe-coded mode", 400);
|
|
1316
1326
|
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Get one category by slug — the payload for a storefront category
|
|
1329
|
+
* (collection) landing page: name, description HTML, meta, breadcrumb and
|
|
1330
|
+
* product count. Fetch the products themselves with
|
|
1331
|
+
* `getProducts({ categories: [category.id] })`. Vibe-coded mode only, like
|
|
1332
|
+
* {@link getCategories}.
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* const category = await client.getCategoryBySlug('running-shoes').catch(() => null);
|
|
1337
|
+
* if (!category) notFound();
|
|
1338
|
+
* const { data: products } = await client.getProducts({ categories: [category.id] });
|
|
1339
|
+
* ```
|
|
1340
|
+
*/
|
|
1341
|
+
async getCategoryBySlug(slug, options) {
|
|
1342
|
+
const headerOverrides = options?.locale ? { "Accept-Language": options.locale } : void 0;
|
|
1343
|
+
const encodedSlug = encodePathSegment(slug);
|
|
1344
|
+
if (this.isVibeCodedMode()) {
|
|
1345
|
+
return this.vibeCodedRequest(
|
|
1346
|
+
"GET",
|
|
1347
|
+
`/categories/slug/${encodedSlug}`,
|
|
1348
|
+
void 0,
|
|
1349
|
+
void 0,
|
|
1350
|
+
headerOverrides
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
throw new BrainerceError("getCategoryBySlug is only available in vibe-coded mode", 400);
|
|
1354
|
+
}
|
|
1317
1355
|
/**
|
|
1318
1356
|
* Get available brands for filtering products
|
|
1319
1357
|
* Works in vibe-coded mode
|
|
@@ -4225,14 +4263,17 @@ var BrainerceClient = class {
|
|
|
4225
4263
|
const migrated = await this.migrateLocalCartToSession();
|
|
4226
4264
|
if (migrated) return migrated;
|
|
4227
4265
|
if (this.sessionCartId && this.sessionToken) {
|
|
4266
|
+
const previousCartId = this.sessionCartId;
|
|
4228
4267
|
try {
|
|
4229
4268
|
const cart2 = await this.getCart(this.sessionCartId);
|
|
4230
4269
|
if (cart2.status === "ACTIVE") {
|
|
4231
4270
|
return cart2;
|
|
4232
4271
|
}
|
|
4233
4272
|
this.clearSessionCart();
|
|
4273
|
+
this.onCartReset?.({ previousCartId, reason: "not_active" });
|
|
4234
4274
|
} catch {
|
|
4235
4275
|
this.clearSessionCart();
|
|
4276
|
+
this.onCartReset?.({ previousCartId, reason: "not_found" });
|
|
4236
4277
|
}
|
|
4237
4278
|
}
|
|
4238
4279
|
if (this.sessionToken) {
|
|
@@ -6461,20 +6502,23 @@ var BrainerceClient = class {
|
|
|
6461
6502
|
);
|
|
6462
6503
|
}
|
|
6463
6504
|
// -------------------- Loyalty --------------------
|
|
6464
|
-
//
|
|
6465
|
-
//
|
|
6466
|
-
//
|
|
6505
|
+
// Available in both storefront mode (storeId + customerToken) and
|
|
6506
|
+
// vibe-coded mode (salesChannelId + customerToken) — Phase 5 added the
|
|
6507
|
+
// /api/vc/:connectionId/loyalty/* routes mirroring /api/stores/:storeId/loyalty.
|
|
6467
6508
|
/**
|
|
6468
6509
|
* Get the logged-in customer's loyalty status: enrollment, points balance,
|
|
6469
|
-
* lifetime earned,
|
|
6470
|
-
*
|
|
6471
|
-
*
|
|
6510
|
+
* lifetime earned, the program's display config, earned milestone `badges`,
|
|
6511
|
+
* and the `paidMembership` subscription state (null for free members).
|
|
6512
|
+
* Requires customerToken. Only available in storefront mode. `program` is
|
|
6513
|
+
* null when the store has no loyalty program.
|
|
6472
6514
|
*
|
|
6473
6515
|
* @example
|
|
6474
6516
|
* ```typescript
|
|
6475
6517
|
* client.setCustomerToken(auth.token);
|
|
6476
6518
|
* const status = await client.getLoyaltyStatus();
|
|
6477
6519
|
* if (status.enrolled) console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
6520
|
+
* status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
|
|
6521
|
+
* if (status.paidMembership?.status === 'ACTIVE') showPremiumPerks(status.paidMembership.plan);
|
|
6478
6522
|
* ```
|
|
6479
6523
|
*/
|
|
6480
6524
|
async getLoyaltyStatus() {
|
|
@@ -6484,10 +6528,16 @@ var BrainerceClient = class {
|
|
|
6484
6528
|
401
|
|
6485
6529
|
);
|
|
6486
6530
|
}
|
|
6531
|
+
if (this.isVibeCodedMode()) {
|
|
6532
|
+
return this.vibeCodedRequest("GET", "/loyalty/me");
|
|
6533
|
+
}
|
|
6487
6534
|
if (this.storeId && !this.apiKey) {
|
|
6488
6535
|
return this.storefrontRequest("GET", "/loyalty/me");
|
|
6489
6536
|
}
|
|
6490
|
-
throw new BrainerceError(
|
|
6537
|
+
throw new BrainerceError(
|
|
6538
|
+
"getLoyaltyStatus is only available in vibe-coded or storefront mode",
|
|
6539
|
+
400
|
|
6540
|
+
);
|
|
6491
6541
|
}
|
|
6492
6542
|
/**
|
|
6493
6543
|
* Enroll the logged-in customer in the store's loyalty program (requires
|
|
@@ -6506,10 +6556,16 @@ var BrainerceClient = class {
|
|
|
6506
6556
|
401
|
|
6507
6557
|
);
|
|
6508
6558
|
}
|
|
6559
|
+
if (this.isVibeCodedMode()) {
|
|
6560
|
+
return this.vibeCodedRequest("POST", "/loyalty/enroll");
|
|
6561
|
+
}
|
|
6509
6562
|
if (this.storeId && !this.apiKey) {
|
|
6510
6563
|
return this.storefrontRequest("POST", "/loyalty/enroll");
|
|
6511
6564
|
}
|
|
6512
|
-
throw new BrainerceError(
|
|
6565
|
+
throw new BrainerceError(
|
|
6566
|
+
"enrollInLoyalty is only available in vibe-coded or storefront mode",
|
|
6567
|
+
400
|
|
6568
|
+
);
|
|
6513
6569
|
}
|
|
6514
6570
|
/**
|
|
6515
6571
|
* List the rewards the customer can redeem points for (active rewards, cheapest
|
|
@@ -6528,10 +6584,16 @@ var BrainerceClient = class {
|
|
|
6528
6584
|
401
|
|
6529
6585
|
);
|
|
6530
6586
|
}
|
|
6587
|
+
if (this.isVibeCodedMode()) {
|
|
6588
|
+
return this.vibeCodedRequest("GET", "/loyalty/rewards/available");
|
|
6589
|
+
}
|
|
6531
6590
|
if (this.storeId && !this.apiKey) {
|
|
6532
6591
|
return this.storefrontRequest("GET", "/loyalty/rewards/available");
|
|
6533
6592
|
}
|
|
6534
|
-
throw new BrainerceError(
|
|
6593
|
+
throw new BrainerceError(
|
|
6594
|
+
"getAvailableRewards is only available in vibe-coded or storefront mode",
|
|
6595
|
+
400
|
|
6596
|
+
);
|
|
6535
6597
|
}
|
|
6536
6598
|
/**
|
|
6537
6599
|
* Redeem a reward: spends the customer's points and mints a one-time coupon
|
|
@@ -6551,10 +6613,16 @@ var BrainerceClient = class {
|
|
|
6551
6613
|
401
|
|
6552
6614
|
);
|
|
6553
6615
|
}
|
|
6616
|
+
if (this.isVibeCodedMode()) {
|
|
6617
|
+
return this.vibeCodedRequest("POST", "/loyalty/redeem", { rewardId });
|
|
6618
|
+
}
|
|
6554
6619
|
if (this.storeId && !this.apiKey) {
|
|
6555
6620
|
return this.storefrontRequest("POST", "/loyalty/redeem", { rewardId });
|
|
6556
6621
|
}
|
|
6557
|
-
throw new BrainerceError(
|
|
6622
|
+
throw new BrainerceError(
|
|
6623
|
+
"redeemLoyaltyReward is only available in vibe-coded or storefront mode",
|
|
6624
|
+
400
|
|
6625
|
+
);
|
|
6558
6626
|
}
|
|
6559
6627
|
/**
|
|
6560
6628
|
* Report a social share, granting the SOCIAL_SHARE earning-rule bonus if the
|
|
@@ -6574,6 +6642,13 @@ var BrainerceClient = class {
|
|
|
6574
6642
|
401
|
|
6575
6643
|
);
|
|
6576
6644
|
}
|
|
6645
|
+
if (this.isVibeCodedMode()) {
|
|
6646
|
+
return this.vibeCodedRequest(
|
|
6647
|
+
"POST",
|
|
6648
|
+
"/loyalty/social-share",
|
|
6649
|
+
platform ? { platform } : {}
|
|
6650
|
+
);
|
|
6651
|
+
}
|
|
6577
6652
|
if (this.storeId && !this.apiKey) {
|
|
6578
6653
|
return this.storefrontRequest(
|
|
6579
6654
|
"POST",
|
|
@@ -6581,7 +6656,10 @@ var BrainerceClient = class {
|
|
|
6581
6656
|
platform ? { platform } : {}
|
|
6582
6657
|
);
|
|
6583
6658
|
}
|
|
6584
|
-
throw new BrainerceError(
|
|
6659
|
+
throw new BrainerceError(
|
|
6660
|
+
"reportSocialShare is only available in vibe-coded or storefront mode",
|
|
6661
|
+
400
|
|
6662
|
+
);
|
|
6585
6663
|
}
|
|
6586
6664
|
/**
|
|
6587
6665
|
* Public referral-link lookup: who referred the visitor and what welcome
|
|
@@ -6600,12 +6678,231 @@ var BrainerceClient = class {
|
|
|
6600
6678
|
* ```
|
|
6601
6679
|
*/
|
|
6602
6680
|
async getReferralInfo(code) {
|
|
6681
|
+
if (this.isVibeCodedMode()) {
|
|
6682
|
+
return this.vibeCodedRequest("GET", "/loyalty/referral-info", void 0, {
|
|
6683
|
+
code
|
|
6684
|
+
});
|
|
6685
|
+
}
|
|
6603
6686
|
if (this.storeId && !this.apiKey) {
|
|
6604
6687
|
return this.storefrontRequest("GET", "/loyalty/referral-info", void 0, {
|
|
6605
6688
|
code
|
|
6606
6689
|
});
|
|
6607
6690
|
}
|
|
6608
|
-
throw new BrainerceError(
|
|
6691
|
+
throw new BrainerceError(
|
|
6692
|
+
"getReferralInfo is only available in vibe-coded or storefront mode",
|
|
6693
|
+
400
|
|
6694
|
+
);
|
|
6695
|
+
}
|
|
6696
|
+
/**
|
|
6697
|
+
* AI-recommended reward for the logged-in customer — "recommended for you"
|
|
6698
|
+
* at the top of the rewards list (requires customerToken). The result is
|
|
6699
|
+
* ALWAYS a real reward from the store's catalog (the AI only ranks; a
|
|
6700
|
+
* hallucinated pick falls back to a deterministic choice). Returns
|
|
6701
|
+
* `{ reward: null }` when the catalog is empty. Rate-limited (5/min per
|
|
6702
|
+
* customer — it spends the merchant's AI credits). Only available in
|
|
6703
|
+
* storefront mode.
|
|
6704
|
+
*
|
|
6705
|
+
* @example
|
|
6706
|
+
* ```typescript
|
|
6707
|
+
* const { reward, reason } = await client.getRecommendedReward();
|
|
6708
|
+
* if (reward) showRecommendation(reward, reason);
|
|
6709
|
+
* ```
|
|
6710
|
+
*/
|
|
6711
|
+
async getRecommendedReward() {
|
|
6712
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6713
|
+
throw new BrainerceError(
|
|
6714
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6715
|
+
401
|
|
6716
|
+
);
|
|
6717
|
+
}
|
|
6718
|
+
if (this.isVibeCodedMode()) {
|
|
6719
|
+
return this.vibeCodedRequest(
|
|
6720
|
+
"GET",
|
|
6721
|
+
"/loyalty/rewards/recommended"
|
|
6722
|
+
);
|
|
6723
|
+
}
|
|
6724
|
+
if (this.storeId && !this.apiKey) {
|
|
6725
|
+
return this.storefrontRequest(
|
|
6726
|
+
"GET",
|
|
6727
|
+
"/loyalty/rewards/recommended"
|
|
6728
|
+
);
|
|
6729
|
+
}
|
|
6730
|
+
throw new BrainerceError(
|
|
6731
|
+
"getRecommendedReward is only available in vibe-coded or storefront mode",
|
|
6732
|
+
400
|
|
6733
|
+
);
|
|
6734
|
+
}
|
|
6735
|
+
/**
|
|
6736
|
+
* List the paid membership plans the customer can subscribe to (requires
|
|
6737
|
+
* customerToken). Empty when the store offers none or the program is not
|
|
6738
|
+
* active. Only available in storefront mode.
|
|
6739
|
+
*
|
|
6740
|
+
* @example
|
|
6741
|
+
* ```typescript
|
|
6742
|
+
* const plans = await client.getMembershipPlans();
|
|
6743
|
+
* ```
|
|
6744
|
+
*/
|
|
6745
|
+
async getMembershipPlans() {
|
|
6746
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6747
|
+
throw new BrainerceError(
|
|
6748
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6749
|
+
401
|
|
6750
|
+
);
|
|
6751
|
+
}
|
|
6752
|
+
if (this.isVibeCodedMode()) {
|
|
6753
|
+
return this.vibeCodedRequest("GET", "/loyalty/membership/plans");
|
|
6754
|
+
}
|
|
6755
|
+
if (this.storeId && !this.apiKey) {
|
|
6756
|
+
return this.storefrontRequest("GET", "/loyalty/membership/plans");
|
|
6757
|
+
}
|
|
6758
|
+
throw new BrainerceError(
|
|
6759
|
+
"getMembershipPlans is only available in vibe-coded or storefront mode",
|
|
6760
|
+
400
|
|
6761
|
+
);
|
|
6762
|
+
}
|
|
6763
|
+
/**
|
|
6764
|
+
* List the customer's saved payment methods (display fields only — brand /
|
|
6765
|
+
* last4 / expiry, never card data) for the membership subscribe flow.
|
|
6766
|
+
* Requires customerToken. Cards are vaulted by checking out with
|
|
6767
|
+
* `saveCard: true`. Only available in storefront mode.
|
|
6768
|
+
*
|
|
6769
|
+
* @example
|
|
6770
|
+
* ```typescript
|
|
6771
|
+
* const methods = await client.getMySavedPaymentMethods();
|
|
6772
|
+
* ```
|
|
6773
|
+
*/
|
|
6774
|
+
async getMySavedPaymentMethods() {
|
|
6775
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6776
|
+
throw new BrainerceError(
|
|
6777
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6778
|
+
401
|
|
6779
|
+
);
|
|
6780
|
+
}
|
|
6781
|
+
if (this.isVibeCodedMode()) {
|
|
6782
|
+
return this.vibeCodedRequest(
|
|
6783
|
+
"GET",
|
|
6784
|
+
"/loyalty/membership/payment-methods"
|
|
6785
|
+
);
|
|
6786
|
+
}
|
|
6787
|
+
if (this.storeId && !this.apiKey) {
|
|
6788
|
+
return this.storefrontRequest(
|
|
6789
|
+
"GET",
|
|
6790
|
+
"/loyalty/membership/payment-methods"
|
|
6791
|
+
);
|
|
6792
|
+
}
|
|
6793
|
+
throw new BrainerceError(
|
|
6794
|
+
"getMySavedPaymentMethods is only available in vibe-coded or storefront mode",
|
|
6795
|
+
400
|
|
6796
|
+
);
|
|
6797
|
+
}
|
|
6798
|
+
/**
|
|
6799
|
+
* Subscribe the customer to a paid membership plan — charges the saved card
|
|
6800
|
+
* IMMEDIATELY and starts the recurring cycle (requires customerToken).
|
|
6801
|
+
* Throws a 409 with a `code` ('card_declined' | 'requires_action' | ...)
|
|
6802
|
+
* when the charge fails; 3D-Secure challenges are not supported off-session
|
|
6803
|
+
* and surface as `requires_action`. Only available in storefront mode.
|
|
6804
|
+
*
|
|
6805
|
+
* @example
|
|
6806
|
+
* ```typescript
|
|
6807
|
+
* const membership = await client.subscribeToMembership({
|
|
6808
|
+
* planId: plan.id,
|
|
6809
|
+
* savedPaymentTokenId: method.id,
|
|
6810
|
+
* });
|
|
6811
|
+
* // membership.status === 'ACTIVE'
|
|
6812
|
+
* ```
|
|
6813
|
+
*/
|
|
6814
|
+
async subscribeToMembership(params) {
|
|
6815
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6816
|
+
throw new BrainerceError(
|
|
6817
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6818
|
+
401
|
|
6819
|
+
);
|
|
6820
|
+
}
|
|
6821
|
+
if (this.isVibeCodedMode()) {
|
|
6822
|
+
return this.vibeCodedRequest(
|
|
6823
|
+
"POST",
|
|
6824
|
+
"/loyalty/membership/subscribe",
|
|
6825
|
+
params
|
|
6826
|
+
);
|
|
6827
|
+
}
|
|
6828
|
+
if (this.storeId && !this.apiKey) {
|
|
6829
|
+
return this.storefrontRequest(
|
|
6830
|
+
"POST",
|
|
6831
|
+
"/loyalty/membership/subscribe",
|
|
6832
|
+
params
|
|
6833
|
+
);
|
|
6834
|
+
}
|
|
6835
|
+
throw new BrainerceError(
|
|
6836
|
+
"subscribeToMembership is only available in vibe-coded or storefront mode",
|
|
6837
|
+
400
|
|
6838
|
+
);
|
|
6839
|
+
}
|
|
6840
|
+
/**
|
|
6841
|
+
* Cancel the customer's paid membership (requires customerToken).
|
|
6842
|
+
* End-of-period semantics: perks continue until `nextBillingAt`, then the
|
|
6843
|
+
* subscription ends without another charge (a PAST_DUE membership cancels
|
|
6844
|
+
* immediately). Re-subscribing to the same plan before period end simply
|
|
6845
|
+
* un-cancels. Only available in storefront mode.
|
|
6846
|
+
*
|
|
6847
|
+
* @example
|
|
6848
|
+
* ```typescript
|
|
6849
|
+
* const membership = await client.cancelMembership();
|
|
6850
|
+
* // membership.cancelAtPeriodEnd === true
|
|
6851
|
+
* ```
|
|
6852
|
+
*/
|
|
6853
|
+
async cancelMembership() {
|
|
6854
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6855
|
+
throw new BrainerceError(
|
|
6856
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6857
|
+
401
|
|
6858
|
+
);
|
|
6859
|
+
}
|
|
6860
|
+
if (this.isVibeCodedMode()) {
|
|
6861
|
+
return this.vibeCodedRequest("POST", "/loyalty/membership/cancel");
|
|
6862
|
+
}
|
|
6863
|
+
if (this.storeId && !this.apiKey) {
|
|
6864
|
+
return this.storefrontRequest("POST", "/loyalty/membership/cancel");
|
|
6865
|
+
}
|
|
6866
|
+
throw new BrainerceError(
|
|
6867
|
+
"cancelMembership is only available in vibe-coded or storefront mode",
|
|
6868
|
+
400
|
|
6869
|
+
);
|
|
6870
|
+
}
|
|
6871
|
+
/**
|
|
6872
|
+
* Mint a short-lived session for the embeddable loyalty widget (Phase 5) and
|
|
6873
|
+
* return the ready-to-use iframe URL. Requires customerToken. The returned
|
|
6874
|
+
* `embedUrl` is safe to drop straight into an `<iframe src>` — it carries a
|
|
6875
|
+
* scoped ~15-minute session token, never the real customerToken. Re-call this
|
|
6876
|
+
* before the iframe reloads (e.g. on page navigation) to refresh it.
|
|
6877
|
+
*
|
|
6878
|
+
* @example
|
|
6879
|
+
* ```typescript
|
|
6880
|
+
* const { embedUrl } = await client.getLoyaltyWidgetSession();
|
|
6881
|
+
* // <iframe src={embedUrl} width="360" height="420" />
|
|
6882
|
+
* ```
|
|
6883
|
+
*/
|
|
6884
|
+
async getLoyaltyWidgetSession() {
|
|
6885
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6886
|
+
throw new BrainerceError(
|
|
6887
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6888
|
+
401
|
|
6889
|
+
);
|
|
6890
|
+
}
|
|
6891
|
+
let result;
|
|
6892
|
+
if (this.isVibeCodedMode()) {
|
|
6893
|
+
result = await this.vibeCodedRequest("POST", "/loyalty/widget-session");
|
|
6894
|
+
} else if (this.storeId && !this.apiKey) {
|
|
6895
|
+
result = await this.storefrontRequest("POST", "/loyalty/widget-session");
|
|
6896
|
+
} else {
|
|
6897
|
+
throw new BrainerceError(
|
|
6898
|
+
"getLoyaltyWidgetSession is only available in vibe-coded or storefront mode",
|
|
6899
|
+
400
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6902
|
+
return {
|
|
6903
|
+
...result,
|
|
6904
|
+
embedUrl: `${this.baseUrl}/api/loyalty/embed/${encodePathSegment(result.storeId)}/${encodePathSegment(result.sessionId)}`
|
|
6905
|
+
};
|
|
6609
6906
|
}
|
|
6610
6907
|
/**
|
|
6611
6908
|
* Get the current customer's orders (requires customerToken)
|
|
@@ -8892,6 +9189,247 @@ function formatAmount(amount, currency, locale) {
|
|
|
8892
9189
|
}
|
|
8893
9190
|
}
|
|
8894
9191
|
|
|
9192
|
+
// src/jsonld.ts
|
|
9193
|
+
function absoluteUrl(siteUrl, path) {
|
|
9194
|
+
if (!path) return void 0;
|
|
9195
|
+
const base = siteUrl.replace(/\/+$/, "");
|
|
9196
|
+
return path.startsWith("http") ? path : `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
9197
|
+
}
|
|
9198
|
+
function stripHtml(html) {
|
|
9199
|
+
return (html ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
9200
|
+
}
|
|
9201
|
+
function buildArticleJsonLd(post, opts) {
|
|
9202
|
+
const url = absoluteUrl(opts.siteUrl, opts.path ?? `/blog/${post.slug}`);
|
|
9203
|
+
const result = {
|
|
9204
|
+
"@context": "https://schema.org",
|
|
9205
|
+
"@type": "Article",
|
|
9206
|
+
headline: post.seoTitle ?? post.title,
|
|
9207
|
+
name: post.title,
|
|
9208
|
+
...post.excerpt || post.seoDescription ? { description: post.seoDescription ?? post.excerpt } : {},
|
|
9209
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9210
|
+
...post.coverImageUrl ? { image: [post.coverImageUrl] } : {},
|
|
9211
|
+
...post.publishedAt ? { datePublished: post.publishedAt } : {},
|
|
9212
|
+
dateModified: post.updatedAt,
|
|
9213
|
+
...post.author ? { author: { "@type": "Person", name: post.author } } : opts.organizationName ? { author: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9214
|
+
...opts.organizationName ? { publisher: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9215
|
+
...post.tags.length > 0 ? { keywords: post.tags.join(", ") } : {}
|
|
9216
|
+
};
|
|
9217
|
+
return result;
|
|
9218
|
+
}
|
|
9219
|
+
function buildProductJsonLd(product, opts) {
|
|
9220
|
+
const url = absoluteUrl(
|
|
9221
|
+
opts.siteUrl,
|
|
9222
|
+
opts.path ?? (product.slug ? `/products/${product.slug}` : void 0)
|
|
9223
|
+
);
|
|
9224
|
+
const images = (product.images ?? []).map((img) => img.url).filter(Boolean);
|
|
9225
|
+
const brand = opts.brandName ?? product.brands?.[0]?.name;
|
|
9226
|
+
const description = stripHtml(product.description).slice(0, 5e3);
|
|
9227
|
+
const effectivePrice = product.salePrice ?? product.basePrice;
|
|
9228
|
+
const inStock = product.inventory ? (product.inventory.available ?? 0) > 0 : true;
|
|
9229
|
+
const isVariable = product.type === "VARIABLE" && product.priceMin && product.priceMax;
|
|
9230
|
+
const itemCondition = "https://schema.org/NewCondition";
|
|
9231
|
+
const shippingDetails = (opts.shipping ?? []).filter((z) => z.amount !== null).map((z) => ({
|
|
9232
|
+
"@type": "OfferShippingDetails",
|
|
9233
|
+
shippingRate: { "@type": "MonetaryAmount", value: z.amount, currency: opts.currency },
|
|
9234
|
+
shippingDestination: { "@type": "DefinedRegion", addressCountry: z.countries },
|
|
9235
|
+
...z.handlingTime != null || z.minDeliveryDays != null || z.maxDeliveryDays != null ? {
|
|
9236
|
+
deliveryTime: {
|
|
9237
|
+
"@type": "ShippingDeliveryTime",
|
|
9238
|
+
...z.handlingTime != null ? {
|
|
9239
|
+
handlingTime: {
|
|
9240
|
+
"@type": "QuantitativeValue",
|
|
9241
|
+
minValue: 0,
|
|
9242
|
+
maxValue: z.handlingTime
|
|
9243
|
+
}
|
|
9244
|
+
} : {},
|
|
9245
|
+
...z.minDeliveryDays != null || z.maxDeliveryDays != null ? {
|
|
9246
|
+
transitTime: {
|
|
9247
|
+
"@type": "QuantitativeValue",
|
|
9248
|
+
minValue: z.minDeliveryDays ?? z.maxDeliveryDays,
|
|
9249
|
+
maxValue: z.maxDeliveryDays ?? z.minDeliveryDays
|
|
9250
|
+
}
|
|
9251
|
+
} : {}
|
|
9252
|
+
}
|
|
9253
|
+
} : {}
|
|
9254
|
+
}));
|
|
9255
|
+
const offer = isVariable ? {
|
|
9256
|
+
"@type": "AggregateOffer",
|
|
9257
|
+
lowPrice: product.priceMin,
|
|
9258
|
+
highPrice: product.priceMax,
|
|
9259
|
+
priceCurrency: opts.currency,
|
|
9260
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9261
|
+
itemCondition,
|
|
9262
|
+
...shippingDetails.length > 0 ? { shippingDetails } : {},
|
|
9263
|
+
...url ? { url } : {}
|
|
9264
|
+
} : {
|
|
9265
|
+
"@type": "Offer",
|
|
9266
|
+
price: effectivePrice,
|
|
9267
|
+
priceCurrency: opts.currency,
|
|
9268
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9269
|
+
itemCondition,
|
|
9270
|
+
// Only meaningful for an active sale price with a known end date —
|
|
9271
|
+
// a regular (non-sale) price has no expiry to declare.
|
|
9272
|
+
...product.salePrice && product.salePriceEndsAt ? { priceValidUntil: product.salePriceEndsAt } : {},
|
|
9273
|
+
...shippingDetails.length > 0 ? { shippingDetails } : {},
|
|
9274
|
+
...url ? { url } : {}
|
|
9275
|
+
};
|
|
9276
|
+
return {
|
|
9277
|
+
"@context": "https://schema.org",
|
|
9278
|
+
"@type": "Product",
|
|
9279
|
+
name: product.name,
|
|
9280
|
+
...description ? { description } : {},
|
|
9281
|
+
...images.length > 0 ? { image: images } : {},
|
|
9282
|
+
...url ? { url } : {},
|
|
9283
|
+
sku: product.sku,
|
|
9284
|
+
// Identifiers Google uses to match a product to its catalog — key for
|
|
9285
|
+
// free merchant-listing eligibility without a Merchant Center feed.
|
|
9286
|
+
...product.gtin ? { gtin: product.gtin } : {},
|
|
9287
|
+
...product.mpn ? { mpn: product.mpn } : {},
|
|
9288
|
+
...brand ? { brand: { "@type": "Brand", name: brand } } : {},
|
|
9289
|
+
offers: offer,
|
|
9290
|
+
// Google policy: never emit an empty/zero rating block.
|
|
9291
|
+
...product.reviewCount && product.reviewCount > 0 && product.avgRating ? {
|
|
9292
|
+
aggregateRating: {
|
|
9293
|
+
"@type": "AggregateRating",
|
|
9294
|
+
ratingValue: product.avgRating,
|
|
9295
|
+
reviewCount: product.reviewCount
|
|
9296
|
+
}
|
|
9297
|
+
} : {}
|
|
9298
|
+
};
|
|
9299
|
+
}
|
|
9300
|
+
function buildOrganizationJsonLd(store, opts) {
|
|
9301
|
+
const sameAs = Object.values(store.socialLinks ?? {}).filter(Boolean);
|
|
9302
|
+
return {
|
|
9303
|
+
"@context": "https://schema.org",
|
|
9304
|
+
"@type": "Organization",
|
|
9305
|
+
name: store.name,
|
|
9306
|
+
url: opts.siteUrl,
|
|
9307
|
+
...store.logo ? { logo: store.logo } : {},
|
|
9308
|
+
...store.metaDescription ? { description: store.metaDescription } : {},
|
|
9309
|
+
...store.contactEmail ? { email: store.contactEmail } : {},
|
|
9310
|
+
...store.contactPhone ? { telephone: store.contactPhone } : {},
|
|
9311
|
+
...sameAs.length > 0 ? { sameAs } : {}
|
|
9312
|
+
};
|
|
9313
|
+
}
|
|
9314
|
+
function buildWebsiteJsonLd(store, opts) {
|
|
9315
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9316
|
+
const result = {
|
|
9317
|
+
"@context": "https://schema.org",
|
|
9318
|
+
"@type": "WebSite",
|
|
9319
|
+
name: store.name,
|
|
9320
|
+
url: opts.siteUrl
|
|
9321
|
+
};
|
|
9322
|
+
if (opts.searchUrlTemplate?.includes("{search_term_string}")) {
|
|
9323
|
+
const tmpl = opts.searchUrlTemplate.startsWith("http") ? opts.searchUrlTemplate : `${base}${opts.searchUrlTemplate.startsWith("/") ? "" : "/"}${opts.searchUrlTemplate}`;
|
|
9324
|
+
result.potentialAction = {
|
|
9325
|
+
"@type": "SearchAction",
|
|
9326
|
+
target: { "@type": "EntryPoint", urlTemplate: tmpl },
|
|
9327
|
+
"query-input": "required name=search_term_string"
|
|
9328
|
+
};
|
|
9329
|
+
}
|
|
9330
|
+
return result;
|
|
9331
|
+
}
|
|
9332
|
+
function buildCollectionPageJsonLd(category, opts) {
|
|
9333
|
+
const url = absoluteUrl(opts.siteUrl, opts.path);
|
|
9334
|
+
const description = category.metaDescription ?? (stripHtml(category.description) || void 0);
|
|
9335
|
+
return {
|
|
9336
|
+
"@context": "https://schema.org",
|
|
9337
|
+
"@type": "CollectionPage",
|
|
9338
|
+
name: category.name,
|
|
9339
|
+
...description ? { description } : {},
|
|
9340
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9341
|
+
...category.image ? { image: [category.image] } : {}
|
|
9342
|
+
};
|
|
9343
|
+
}
|
|
9344
|
+
function buildBreadcrumbJsonLd(items) {
|
|
9345
|
+
return {
|
|
9346
|
+
"@context": "https://schema.org",
|
|
9347
|
+
"@type": "BreadcrumbList",
|
|
9348
|
+
itemListElement: items.map((item, index) => ({
|
|
9349
|
+
"@type": "ListItem",
|
|
9350
|
+
position: index + 1,
|
|
9351
|
+
name: item.name,
|
|
9352
|
+
item: item.url
|
|
9353
|
+
}))
|
|
9354
|
+
};
|
|
9355
|
+
}
|
|
9356
|
+
function jsonLdScriptProps(data) {
|
|
9357
|
+
return {
|
|
9358
|
+
type: "application/ld+json",
|
|
9359
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(data).replace(/</g, "\\u003c") }
|
|
9360
|
+
};
|
|
9361
|
+
}
|
|
9362
|
+
|
|
9363
|
+
// src/sitemap.ts
|
|
9364
|
+
async function getBlogSitemapEntries(client, opts) {
|
|
9365
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9366
|
+
const basePath = opts.basePath ?? "/blog";
|
|
9367
|
+
const pageSize = Math.min(opts.pageSize ?? 100, 100);
|
|
9368
|
+
const maxEntries = opts.maxEntries ?? 5e3;
|
|
9369
|
+
const posts = [];
|
|
9370
|
+
let page = 1;
|
|
9371
|
+
for (; ; ) {
|
|
9372
|
+
const res = await client.blog.getPosts({ page, limit: pageSize });
|
|
9373
|
+
posts.push(...res.data.map((p) => ({ slug: p.slug, updatedAt: p.updatedAt })));
|
|
9374
|
+
if (page >= res.meta.totalPages || posts.length >= maxEntries) break;
|
|
9375
|
+
page += 1;
|
|
9376
|
+
}
|
|
9377
|
+
const nonDefaultLocales = opts.locales?.filter((locale) => locale !== opts.defaultLocale) ?? [];
|
|
9378
|
+
const entries = [];
|
|
9379
|
+
entries.push({ url: `${base}${basePath}`, changeFrequency: "daily", priority: 0.7 });
|
|
9380
|
+
for (const locale of nonDefaultLocales) {
|
|
9381
|
+
entries.push({
|
|
9382
|
+
url: `${base}/${locale}${basePath}`,
|
|
9383
|
+
changeFrequency: "daily",
|
|
9384
|
+
priority: 0.7
|
|
9385
|
+
});
|
|
9386
|
+
}
|
|
9387
|
+
for (const post of posts.slice(0, maxEntries)) {
|
|
9388
|
+
const lastModified = new Date(post.updatedAt);
|
|
9389
|
+
entries.push({
|
|
9390
|
+
url: `${base}${basePath}/${post.slug}`,
|
|
9391
|
+
lastModified,
|
|
9392
|
+
changeFrequency: "weekly",
|
|
9393
|
+
priority: 0.6
|
|
9394
|
+
});
|
|
9395
|
+
for (const locale of nonDefaultLocales) {
|
|
9396
|
+
entries.push({
|
|
9397
|
+
url: `${base}/${locale}${basePath}/${post.slug}`,
|
|
9398
|
+
lastModified,
|
|
9399
|
+
changeFrequency: "weekly",
|
|
9400
|
+
priority: 0.5
|
|
9401
|
+
});
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
return entries;
|
|
9405
|
+
}
|
|
9406
|
+
async function getCategorySitemapEntries(client, opts) {
|
|
9407
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9408
|
+
const basePath = opts.basePath ?? "/category";
|
|
9409
|
+
const nonDefaultLocales = opts.locales?.filter((l) => l !== opts.defaultLocale) ?? [];
|
|
9410
|
+
const { categories } = await client.getCategories();
|
|
9411
|
+
const slugs = [];
|
|
9412
|
+
const walk = (nodes) => {
|
|
9413
|
+
for (const node of nodes) {
|
|
9414
|
+
if (node.slug) slugs.push(node.slug);
|
|
9415
|
+
if (node.children?.length) walk(node.children);
|
|
9416
|
+
}
|
|
9417
|
+
};
|
|
9418
|
+
walk(categories);
|
|
9419
|
+
const entries = [];
|
|
9420
|
+
for (const slug of slugs) {
|
|
9421
|
+
entries.push({ url: `${base}${basePath}/${slug}`, changeFrequency: "daily", priority: 0.8 });
|
|
9422
|
+
for (const locale of nonDefaultLocales) {
|
|
9423
|
+
entries.push({
|
|
9424
|
+
url: `${base}/${locale}${basePath}/${slug}`,
|
|
9425
|
+
changeFrequency: "daily",
|
|
9426
|
+
priority: 0.7
|
|
9427
|
+
});
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
9430
|
+
return entries;
|
|
9431
|
+
}
|
|
9432
|
+
|
|
8895
9433
|
// src/types.ts
|
|
8896
9434
|
function isHtmlDescription(product) {
|
|
8897
9435
|
if (product?.descriptionFormat === "html") return true;
|
|
@@ -8907,7 +9445,7 @@ function getDescriptionContent(product) {
|
|
|
8907
9445
|
}
|
|
8908
9446
|
return { text: product.description };
|
|
8909
9447
|
}
|
|
8910
|
-
function
|
|
9448
|
+
function stripHtml2(html) {
|
|
8911
9449
|
if (!html) return "";
|
|
8912
9450
|
return html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").replace(/'/gi, "'").replace(///gi, "/").replace(/\s+/g, " ").trim();
|
|
8913
9451
|
}
|
|
@@ -8916,7 +9454,7 @@ function deriveSeoDescription(product, options) {
|
|
|
8916
9454
|
const maxLength = options?.maxLength ?? 160;
|
|
8917
9455
|
const authored = (product.seoDescription ?? product.metaDescription ?? "").trim();
|
|
8918
9456
|
if (authored) return authored;
|
|
8919
|
-
const plain =
|
|
9457
|
+
const plain = stripHtml2(product.description);
|
|
8920
9458
|
if (plain) {
|
|
8921
9459
|
if (plain.length <= maxLength) return plain;
|
|
8922
9460
|
const sliceLength = maxLength - 1;
|
|
@@ -9138,6 +9676,12 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9138
9676
|
BrainerceError,
|
|
9139
9677
|
RTL_LOCALES,
|
|
9140
9678
|
SDK_VERSION,
|
|
9679
|
+
buildArticleJsonLd,
|
|
9680
|
+
buildBreadcrumbJsonLd,
|
|
9681
|
+
buildCollectionPageJsonLd,
|
|
9682
|
+
buildOrganizationJsonLd,
|
|
9683
|
+
buildProductJsonLd,
|
|
9684
|
+
buildWebsiteJsonLd,
|
|
9141
9685
|
createWebhookHandler,
|
|
9142
9686
|
deriveSeoDescription,
|
|
9143
9687
|
enableDevGuards,
|
|
@@ -9145,9 +9689,11 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9145
9689
|
formatPrice,
|
|
9146
9690
|
formatProductPrice,
|
|
9147
9691
|
formatVariantPrice,
|
|
9692
|
+
getBlogSitemapEntries,
|
|
9148
9693
|
getCartItemImage,
|
|
9149
9694
|
getCartItemName,
|
|
9150
9695
|
getCartTotals,
|
|
9696
|
+
getCategorySitemapEntries,
|
|
9151
9697
|
getDescriptionContent,
|
|
9152
9698
|
getDirectionForLocale,
|
|
9153
9699
|
getPriceDisplay,
|
|
@@ -9165,6 +9711,7 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9165
9711
|
isCouponApplicableToProduct,
|
|
9166
9712
|
isHtmlDescription,
|
|
9167
9713
|
isWebhookEventType,
|
|
9714
|
+
jsonLdScriptProps,
|
|
9168
9715
|
parseWebhookEvent,
|
|
9169
9716
|
safePaymentRedirect,
|
|
9170
9717
|
stripHtml,
|