brainerce 1.46.2 → 1.47.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 +141 -6
- 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 +346 -13
- package/dist/index.d.ts +346 -13
- package/dist/index.js +399 -6
- package/dist/index.mjs +390 -6
- 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);
|
|
@@ -1314,6 +1323,34 @@ var BrainerceClient = class {
|
|
|
1314
1323
|
}
|
|
1315
1324
|
throw new BrainerceError("getCategories is only available in vibe-coded mode", 400);
|
|
1316
1325
|
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Get one category by slug — the payload for a storefront category
|
|
1328
|
+
* (collection) landing page: name, description HTML, meta, breadcrumb and
|
|
1329
|
+
* product count. Fetch the products themselves with
|
|
1330
|
+
* `getProducts({ categories: [category.id] })`. Vibe-coded mode only, like
|
|
1331
|
+
* {@link getCategories}.
|
|
1332
|
+
*
|
|
1333
|
+
* @example
|
|
1334
|
+
* ```typescript
|
|
1335
|
+
* const category = await client.getCategoryBySlug('running-shoes').catch(() => null);
|
|
1336
|
+
* if (!category) notFound();
|
|
1337
|
+
* const { data: products } = await client.getProducts({ categories: [category.id] });
|
|
1338
|
+
* ```
|
|
1339
|
+
*/
|
|
1340
|
+
async getCategoryBySlug(slug, options) {
|
|
1341
|
+
const headerOverrides = options?.locale ? { "Accept-Language": options.locale } : void 0;
|
|
1342
|
+
const encodedSlug = encodePathSegment(slug);
|
|
1343
|
+
if (this.isVibeCodedMode()) {
|
|
1344
|
+
return this.vibeCodedRequest(
|
|
1345
|
+
"GET",
|
|
1346
|
+
`/categories/slug/${encodedSlug}`,
|
|
1347
|
+
void 0,
|
|
1348
|
+
void 0,
|
|
1349
|
+
headerOverrides
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
throw new BrainerceError("getCategoryBySlug is only available in vibe-coded mode", 400);
|
|
1353
|
+
}
|
|
1317
1354
|
/**
|
|
1318
1355
|
* Get available brands for filtering products
|
|
1319
1356
|
* Works in vibe-coded mode
|
|
@@ -6466,15 +6503,18 @@ var BrainerceClient = class {
|
|
|
6466
6503
|
// these do NOT branch on isVibeCodedMode() (that would 404 silently).
|
|
6467
6504
|
/**
|
|
6468
6505
|
* Get the logged-in customer's loyalty status: enrollment, points balance,
|
|
6469
|
-
* lifetime earned,
|
|
6470
|
-
*
|
|
6471
|
-
*
|
|
6506
|
+
* lifetime earned, the program's display config, earned milestone `badges`,
|
|
6507
|
+
* and the `paidMembership` subscription state (null for free members).
|
|
6508
|
+
* Requires customerToken. Only available in storefront mode. `program` is
|
|
6509
|
+
* null when the store has no loyalty program.
|
|
6472
6510
|
*
|
|
6473
6511
|
* @example
|
|
6474
6512
|
* ```typescript
|
|
6475
6513
|
* client.setCustomerToken(auth.token);
|
|
6476
6514
|
* const status = await client.getLoyaltyStatus();
|
|
6477
6515
|
* if (status.enrolled) console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
6516
|
+
* status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
|
|
6517
|
+
* if (status.paidMembership?.status === 'ACTIVE') showPremiumPerks(status.paidMembership.plan);
|
|
6478
6518
|
* ```
|
|
6479
6519
|
*/
|
|
6480
6520
|
async getLoyaltyStatus() {
|
|
@@ -6607,6 +6647,144 @@ var BrainerceClient = class {
|
|
|
6607
6647
|
}
|
|
6608
6648
|
throw new BrainerceError("getReferralInfo is only available in storefront mode", 400);
|
|
6609
6649
|
}
|
|
6650
|
+
/**
|
|
6651
|
+
* AI-recommended reward for the logged-in customer — "recommended for you"
|
|
6652
|
+
* at the top of the rewards list (requires customerToken). The result is
|
|
6653
|
+
* ALWAYS a real reward from the store's catalog (the AI only ranks; a
|
|
6654
|
+
* hallucinated pick falls back to a deterministic choice). Returns
|
|
6655
|
+
* `{ reward: null }` when the catalog is empty. Rate-limited (5/min per
|
|
6656
|
+
* customer — it spends the merchant's AI credits). Only available in
|
|
6657
|
+
* storefront mode.
|
|
6658
|
+
*
|
|
6659
|
+
* @example
|
|
6660
|
+
* ```typescript
|
|
6661
|
+
* const { reward, reason } = await client.getRecommendedReward();
|
|
6662
|
+
* if (reward) showRecommendation(reward, reason);
|
|
6663
|
+
* ```
|
|
6664
|
+
*/
|
|
6665
|
+
async getRecommendedReward() {
|
|
6666
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6667
|
+
throw new BrainerceError(
|
|
6668
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6669
|
+
401
|
|
6670
|
+
);
|
|
6671
|
+
}
|
|
6672
|
+
if (this.storeId && !this.apiKey) {
|
|
6673
|
+
return this.storefrontRequest(
|
|
6674
|
+
"GET",
|
|
6675
|
+
"/loyalty/rewards/recommended"
|
|
6676
|
+
);
|
|
6677
|
+
}
|
|
6678
|
+
throw new BrainerceError("getRecommendedReward is only available in storefront mode", 400);
|
|
6679
|
+
}
|
|
6680
|
+
/**
|
|
6681
|
+
* List the paid membership plans the customer can subscribe to (requires
|
|
6682
|
+
* customerToken). Empty when the store offers none or the program is not
|
|
6683
|
+
* active. Only available in storefront mode.
|
|
6684
|
+
*
|
|
6685
|
+
* @example
|
|
6686
|
+
* ```typescript
|
|
6687
|
+
* const plans = await client.getMembershipPlans();
|
|
6688
|
+
* ```
|
|
6689
|
+
*/
|
|
6690
|
+
async getMembershipPlans() {
|
|
6691
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6692
|
+
throw new BrainerceError(
|
|
6693
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6694
|
+
401
|
|
6695
|
+
);
|
|
6696
|
+
}
|
|
6697
|
+
if (this.storeId && !this.apiKey) {
|
|
6698
|
+
return this.storefrontRequest("GET", "/loyalty/membership/plans");
|
|
6699
|
+
}
|
|
6700
|
+
throw new BrainerceError("getMembershipPlans is only available in storefront mode", 400);
|
|
6701
|
+
}
|
|
6702
|
+
/**
|
|
6703
|
+
* List the customer's saved payment methods (display fields only — brand /
|
|
6704
|
+
* last4 / expiry, never card data) for the membership subscribe flow.
|
|
6705
|
+
* Requires customerToken. Cards are vaulted by checking out with
|
|
6706
|
+
* `saveCard: true`. Only available in storefront mode.
|
|
6707
|
+
*
|
|
6708
|
+
* @example
|
|
6709
|
+
* ```typescript
|
|
6710
|
+
* const methods = await client.getMySavedPaymentMethods();
|
|
6711
|
+
* ```
|
|
6712
|
+
*/
|
|
6713
|
+
async getMySavedPaymentMethods() {
|
|
6714
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6715
|
+
throw new BrainerceError(
|
|
6716
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6717
|
+
401
|
|
6718
|
+
);
|
|
6719
|
+
}
|
|
6720
|
+
if (this.storeId && !this.apiKey) {
|
|
6721
|
+
return this.storefrontRequest(
|
|
6722
|
+
"GET",
|
|
6723
|
+
"/loyalty/membership/payment-methods"
|
|
6724
|
+
);
|
|
6725
|
+
}
|
|
6726
|
+
throw new BrainerceError(
|
|
6727
|
+
"getMySavedPaymentMethods is only available in storefront mode",
|
|
6728
|
+
400
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6731
|
+
/**
|
|
6732
|
+
* Subscribe the customer to a paid membership plan — charges the saved card
|
|
6733
|
+
* IMMEDIATELY and starts the recurring cycle (requires customerToken).
|
|
6734
|
+
* Throws a 409 with a `code` ('card_declined' | 'requires_action' | ...)
|
|
6735
|
+
* when the charge fails; 3D-Secure challenges are not supported off-session
|
|
6736
|
+
* and surface as `requires_action`. Only available in storefront mode.
|
|
6737
|
+
*
|
|
6738
|
+
* @example
|
|
6739
|
+
* ```typescript
|
|
6740
|
+
* const membership = await client.subscribeToMembership({
|
|
6741
|
+
* planId: plan.id,
|
|
6742
|
+
* savedPaymentTokenId: method.id,
|
|
6743
|
+
* });
|
|
6744
|
+
* // membership.status === 'ACTIVE'
|
|
6745
|
+
* ```
|
|
6746
|
+
*/
|
|
6747
|
+
async subscribeToMembership(params) {
|
|
6748
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6749
|
+
throw new BrainerceError(
|
|
6750
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6751
|
+
401
|
|
6752
|
+
);
|
|
6753
|
+
}
|
|
6754
|
+
if (this.storeId && !this.apiKey) {
|
|
6755
|
+
return this.storefrontRequest(
|
|
6756
|
+
"POST",
|
|
6757
|
+
"/loyalty/membership/subscribe",
|
|
6758
|
+
params
|
|
6759
|
+
);
|
|
6760
|
+
}
|
|
6761
|
+
throw new BrainerceError("subscribeToMembership is only available in storefront mode", 400);
|
|
6762
|
+
}
|
|
6763
|
+
/**
|
|
6764
|
+
* Cancel the customer's paid membership (requires customerToken).
|
|
6765
|
+
* End-of-period semantics: perks continue until `nextBillingAt`, then the
|
|
6766
|
+
* subscription ends without another charge (a PAST_DUE membership cancels
|
|
6767
|
+
* immediately). Re-subscribing to the same plan before period end simply
|
|
6768
|
+
* un-cancels. Only available in storefront mode.
|
|
6769
|
+
*
|
|
6770
|
+
* @example
|
|
6771
|
+
* ```typescript
|
|
6772
|
+
* const membership = await client.cancelMembership();
|
|
6773
|
+
* // membership.cancelAtPeriodEnd === true
|
|
6774
|
+
* ```
|
|
6775
|
+
*/
|
|
6776
|
+
async cancelMembership() {
|
|
6777
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6778
|
+
throw new BrainerceError(
|
|
6779
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6780
|
+
401
|
|
6781
|
+
);
|
|
6782
|
+
}
|
|
6783
|
+
if (this.storeId && !this.apiKey) {
|
|
6784
|
+
return this.storefrontRequest("POST", "/loyalty/membership/cancel");
|
|
6785
|
+
}
|
|
6786
|
+
throw new BrainerceError("cancelMembership is only available in storefront mode", 400);
|
|
6787
|
+
}
|
|
6610
6788
|
/**
|
|
6611
6789
|
* Get the current customer's orders (requires customerToken)
|
|
6612
6790
|
* Works in vibe-coded and storefront modes
|
|
@@ -8892,6 +9070,212 @@ function formatAmount(amount, currency, locale) {
|
|
|
8892
9070
|
}
|
|
8893
9071
|
}
|
|
8894
9072
|
|
|
9073
|
+
// src/jsonld.ts
|
|
9074
|
+
function absoluteUrl(siteUrl, path) {
|
|
9075
|
+
if (!path) return void 0;
|
|
9076
|
+
const base = siteUrl.replace(/\/+$/, "");
|
|
9077
|
+
return path.startsWith("http") ? path : `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
9078
|
+
}
|
|
9079
|
+
function stripHtml(html) {
|
|
9080
|
+
return (html ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
9081
|
+
}
|
|
9082
|
+
function buildArticleJsonLd(post, opts) {
|
|
9083
|
+
const url = absoluteUrl(opts.siteUrl, opts.path ?? `/blog/${post.slug}`);
|
|
9084
|
+
const result = {
|
|
9085
|
+
"@context": "https://schema.org",
|
|
9086
|
+
"@type": "Article",
|
|
9087
|
+
headline: post.seoTitle ?? post.title,
|
|
9088
|
+
name: post.title,
|
|
9089
|
+
...post.excerpt || post.seoDescription ? { description: post.seoDescription ?? post.excerpt } : {},
|
|
9090
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9091
|
+
...post.coverImageUrl ? { image: [post.coverImageUrl] } : {},
|
|
9092
|
+
...post.publishedAt ? { datePublished: post.publishedAt } : {},
|
|
9093
|
+
dateModified: post.updatedAt,
|
|
9094
|
+
...post.author ? { author: { "@type": "Person", name: post.author } } : opts.organizationName ? { author: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9095
|
+
...opts.organizationName ? { publisher: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9096
|
+
...post.tags.length > 0 ? { keywords: post.tags.join(", ") } : {}
|
|
9097
|
+
};
|
|
9098
|
+
return result;
|
|
9099
|
+
}
|
|
9100
|
+
function buildProductJsonLd(product, opts) {
|
|
9101
|
+
const url = absoluteUrl(opts.siteUrl, opts.path ?? (product.slug ? `/products/${product.slug}` : void 0));
|
|
9102
|
+
const images = (product.images ?? []).map((img) => img.url).filter(Boolean);
|
|
9103
|
+
const brand = opts.brandName ?? product.brands?.[0]?.name;
|
|
9104
|
+
const description = stripHtml(product.description).slice(0, 5e3);
|
|
9105
|
+
const effectivePrice = product.salePrice ?? product.basePrice;
|
|
9106
|
+
const inStock = product.inventory ? (product.inventory.available ?? 0) > 0 : true;
|
|
9107
|
+
const isVariable = product.type === "VARIABLE" && product.priceMin && product.priceMax;
|
|
9108
|
+
const offer = isVariable ? {
|
|
9109
|
+
"@type": "AggregateOffer",
|
|
9110
|
+
lowPrice: product.priceMin,
|
|
9111
|
+
highPrice: product.priceMax,
|
|
9112
|
+
priceCurrency: opts.currency,
|
|
9113
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9114
|
+
...url ? { url } : {}
|
|
9115
|
+
} : {
|
|
9116
|
+
"@type": "Offer",
|
|
9117
|
+
price: effectivePrice,
|
|
9118
|
+
priceCurrency: opts.currency,
|
|
9119
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9120
|
+
...url ? { url } : {}
|
|
9121
|
+
};
|
|
9122
|
+
return {
|
|
9123
|
+
"@context": "https://schema.org",
|
|
9124
|
+
"@type": "Product",
|
|
9125
|
+
name: product.name,
|
|
9126
|
+
...description ? { description } : {},
|
|
9127
|
+
...images.length > 0 ? { image: images } : {},
|
|
9128
|
+
...url ? { url } : {},
|
|
9129
|
+
sku: product.sku,
|
|
9130
|
+
// Identifiers Google uses to match a product to its catalog — key for
|
|
9131
|
+
// free merchant-listing eligibility without a Merchant Center feed.
|
|
9132
|
+
...product.gtin ? { gtin: product.gtin } : {},
|
|
9133
|
+
...product.mpn ? { mpn: product.mpn } : {},
|
|
9134
|
+
...brand ? { brand: { "@type": "Brand", name: brand } } : {},
|
|
9135
|
+
offers: offer,
|
|
9136
|
+
// Google policy: never emit an empty/zero rating block.
|
|
9137
|
+
...product.reviewCount && product.reviewCount > 0 && product.avgRating ? {
|
|
9138
|
+
aggregateRating: {
|
|
9139
|
+
"@type": "AggregateRating",
|
|
9140
|
+
ratingValue: product.avgRating,
|
|
9141
|
+
reviewCount: product.reviewCount
|
|
9142
|
+
}
|
|
9143
|
+
} : {}
|
|
9144
|
+
};
|
|
9145
|
+
}
|
|
9146
|
+
function buildOrganizationJsonLd(store, opts) {
|
|
9147
|
+
const sameAs = Object.values(store.socialLinks ?? {}).filter(Boolean);
|
|
9148
|
+
return {
|
|
9149
|
+
"@context": "https://schema.org",
|
|
9150
|
+
"@type": "Organization",
|
|
9151
|
+
name: store.name,
|
|
9152
|
+
url: opts.siteUrl,
|
|
9153
|
+
...store.logo ? { logo: store.logo } : {},
|
|
9154
|
+
...store.metaDescription ? { description: store.metaDescription } : {},
|
|
9155
|
+
...store.contactEmail ? { email: store.contactEmail } : {},
|
|
9156
|
+
...store.contactPhone ? { telephone: store.contactPhone } : {},
|
|
9157
|
+
...sameAs.length > 0 ? { sameAs } : {}
|
|
9158
|
+
};
|
|
9159
|
+
}
|
|
9160
|
+
function buildWebsiteJsonLd(store, opts) {
|
|
9161
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9162
|
+
const result = {
|
|
9163
|
+
"@context": "https://schema.org",
|
|
9164
|
+
"@type": "WebSite",
|
|
9165
|
+
name: store.name,
|
|
9166
|
+
url: opts.siteUrl
|
|
9167
|
+
};
|
|
9168
|
+
if (opts.searchUrlTemplate?.includes("{search_term_string}")) {
|
|
9169
|
+
const tmpl = opts.searchUrlTemplate.startsWith("http") ? opts.searchUrlTemplate : `${base}${opts.searchUrlTemplate.startsWith("/") ? "" : "/"}${opts.searchUrlTemplate}`;
|
|
9170
|
+
result.potentialAction = {
|
|
9171
|
+
"@type": "SearchAction",
|
|
9172
|
+
target: { "@type": "EntryPoint", urlTemplate: tmpl },
|
|
9173
|
+
"query-input": "required name=search_term_string"
|
|
9174
|
+
};
|
|
9175
|
+
}
|
|
9176
|
+
return result;
|
|
9177
|
+
}
|
|
9178
|
+
function buildCollectionPageJsonLd(category, opts) {
|
|
9179
|
+
const url = absoluteUrl(opts.siteUrl, opts.path);
|
|
9180
|
+
const description = category.metaDescription ?? (stripHtml(category.description) || void 0);
|
|
9181
|
+
return {
|
|
9182
|
+
"@context": "https://schema.org",
|
|
9183
|
+
"@type": "CollectionPage",
|
|
9184
|
+
name: category.name,
|
|
9185
|
+
...description ? { description } : {},
|
|
9186
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9187
|
+
...category.image ? { image: [category.image] } : {}
|
|
9188
|
+
};
|
|
9189
|
+
}
|
|
9190
|
+
function buildBreadcrumbJsonLd(items) {
|
|
9191
|
+
return {
|
|
9192
|
+
"@context": "https://schema.org",
|
|
9193
|
+
"@type": "BreadcrumbList",
|
|
9194
|
+
itemListElement: items.map((item, index) => ({
|
|
9195
|
+
"@type": "ListItem",
|
|
9196
|
+
position: index + 1,
|
|
9197
|
+
name: item.name,
|
|
9198
|
+
item: item.url
|
|
9199
|
+
}))
|
|
9200
|
+
};
|
|
9201
|
+
}
|
|
9202
|
+
function jsonLdScriptProps(data) {
|
|
9203
|
+
return {
|
|
9204
|
+
type: "application/ld+json",
|
|
9205
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(data).replace(/</g, "\\u003c") }
|
|
9206
|
+
};
|
|
9207
|
+
}
|
|
9208
|
+
|
|
9209
|
+
// src/sitemap.ts
|
|
9210
|
+
async function getBlogSitemapEntries(client, opts) {
|
|
9211
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9212
|
+
const basePath = opts.basePath ?? "/blog";
|
|
9213
|
+
const pageSize = Math.min(opts.pageSize ?? 100, 100);
|
|
9214
|
+
const maxEntries = opts.maxEntries ?? 5e3;
|
|
9215
|
+
const posts = [];
|
|
9216
|
+
let page = 1;
|
|
9217
|
+
for (; ; ) {
|
|
9218
|
+
const res = await client.blog.getPosts({ page, limit: pageSize });
|
|
9219
|
+
posts.push(...res.data.map((p) => ({ slug: p.slug, updatedAt: p.updatedAt })));
|
|
9220
|
+
if (page >= res.meta.totalPages || posts.length >= maxEntries) break;
|
|
9221
|
+
page += 1;
|
|
9222
|
+
}
|
|
9223
|
+
const nonDefaultLocales = opts.locales?.filter((locale) => locale !== opts.defaultLocale) ?? [];
|
|
9224
|
+
const entries = [];
|
|
9225
|
+
entries.push({ url: `${base}${basePath}`, changeFrequency: "daily", priority: 0.7 });
|
|
9226
|
+
for (const locale of nonDefaultLocales) {
|
|
9227
|
+
entries.push({
|
|
9228
|
+
url: `${base}/${locale}${basePath}`,
|
|
9229
|
+
changeFrequency: "daily",
|
|
9230
|
+
priority: 0.7
|
|
9231
|
+
});
|
|
9232
|
+
}
|
|
9233
|
+
for (const post of posts.slice(0, maxEntries)) {
|
|
9234
|
+
const lastModified = new Date(post.updatedAt);
|
|
9235
|
+
entries.push({
|
|
9236
|
+
url: `${base}${basePath}/${post.slug}`,
|
|
9237
|
+
lastModified,
|
|
9238
|
+
changeFrequency: "weekly",
|
|
9239
|
+
priority: 0.6
|
|
9240
|
+
});
|
|
9241
|
+
for (const locale of nonDefaultLocales) {
|
|
9242
|
+
entries.push({
|
|
9243
|
+
url: `${base}/${locale}${basePath}/${post.slug}`,
|
|
9244
|
+
lastModified,
|
|
9245
|
+
changeFrequency: "weekly",
|
|
9246
|
+
priority: 0.5
|
|
9247
|
+
});
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9250
|
+
return entries;
|
|
9251
|
+
}
|
|
9252
|
+
async function getCategorySitemapEntries(client, opts) {
|
|
9253
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9254
|
+
const basePath = opts.basePath ?? "/category";
|
|
9255
|
+
const nonDefaultLocales = opts.locales?.filter((l) => l !== opts.defaultLocale) ?? [];
|
|
9256
|
+
const { categories } = await client.getCategories();
|
|
9257
|
+
const slugs = [];
|
|
9258
|
+
const walk = (nodes) => {
|
|
9259
|
+
for (const node of nodes) {
|
|
9260
|
+
if (node.slug) slugs.push(node.slug);
|
|
9261
|
+
if (node.children?.length) walk(node.children);
|
|
9262
|
+
}
|
|
9263
|
+
};
|
|
9264
|
+
walk(categories);
|
|
9265
|
+
const entries = [];
|
|
9266
|
+
for (const slug of slugs) {
|
|
9267
|
+
entries.push({ url: `${base}${basePath}/${slug}`, changeFrequency: "daily", priority: 0.8 });
|
|
9268
|
+
for (const locale of nonDefaultLocales) {
|
|
9269
|
+
entries.push({
|
|
9270
|
+
url: `${base}/${locale}${basePath}/${slug}`,
|
|
9271
|
+
changeFrequency: "daily",
|
|
9272
|
+
priority: 0.7
|
|
9273
|
+
});
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
return entries;
|
|
9277
|
+
}
|
|
9278
|
+
|
|
8895
9279
|
// src/types.ts
|
|
8896
9280
|
function isHtmlDescription(product) {
|
|
8897
9281
|
if (product?.descriptionFormat === "html") return true;
|
|
@@ -8907,7 +9291,7 @@ function getDescriptionContent(product) {
|
|
|
8907
9291
|
}
|
|
8908
9292
|
return { text: product.description };
|
|
8909
9293
|
}
|
|
8910
|
-
function
|
|
9294
|
+
function stripHtml2(html) {
|
|
8911
9295
|
if (!html) return "";
|
|
8912
9296
|
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
9297
|
}
|
|
@@ -8916,7 +9300,7 @@ function deriveSeoDescription(product, options) {
|
|
|
8916
9300
|
const maxLength = options?.maxLength ?? 160;
|
|
8917
9301
|
const authored = (product.seoDescription ?? product.metaDescription ?? "").trim();
|
|
8918
9302
|
if (authored) return authored;
|
|
8919
|
-
const plain =
|
|
9303
|
+
const plain = stripHtml2(product.description);
|
|
8920
9304
|
if (plain) {
|
|
8921
9305
|
if (plain.length <= maxLength) return plain;
|
|
8922
9306
|
const sliceLength = maxLength - 1;
|
|
@@ -9138,6 +9522,12 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9138
9522
|
BrainerceError,
|
|
9139
9523
|
RTL_LOCALES,
|
|
9140
9524
|
SDK_VERSION,
|
|
9525
|
+
buildArticleJsonLd,
|
|
9526
|
+
buildBreadcrumbJsonLd,
|
|
9527
|
+
buildCollectionPageJsonLd,
|
|
9528
|
+
buildOrganizationJsonLd,
|
|
9529
|
+
buildProductJsonLd,
|
|
9530
|
+
buildWebsiteJsonLd,
|
|
9141
9531
|
createWebhookHandler,
|
|
9142
9532
|
deriveSeoDescription,
|
|
9143
9533
|
enableDevGuards,
|
|
@@ -9145,9 +9535,11 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9145
9535
|
formatPrice,
|
|
9146
9536
|
formatProductPrice,
|
|
9147
9537
|
formatVariantPrice,
|
|
9538
|
+
getBlogSitemapEntries,
|
|
9148
9539
|
getCartItemImage,
|
|
9149
9540
|
getCartItemName,
|
|
9150
9541
|
getCartTotals,
|
|
9542
|
+
getCategorySitemapEntries,
|
|
9151
9543
|
getDescriptionContent,
|
|
9152
9544
|
getDirectionForLocale,
|
|
9153
9545
|
getPriceDisplay,
|
|
@@ -9165,6 +9557,7 @@ function isCouponApplicableToProduct(coupon, productId) {
|
|
|
9165
9557
|
isCouponApplicableToProduct,
|
|
9166
9558
|
isHtmlDescription,
|
|
9167
9559
|
isWebhookEventType,
|
|
9560
|
+
jsonLdScriptProps,
|
|
9168
9561
|
parseWebhookEvent,
|
|
9169
9562
|
safePaymentRedirect,
|
|
9170
9563
|
stripHtml,
|