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.mjs
CHANGED
|
@@ -1244,6 +1244,34 @@ var BrainerceClient = class {
|
|
|
1244
1244
|
}
|
|
1245
1245
|
throw new BrainerceError("getCategories is only available in vibe-coded mode", 400);
|
|
1246
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Get one category by slug — the payload for a storefront category
|
|
1249
|
+
* (collection) landing page: name, description HTML, meta, breadcrumb and
|
|
1250
|
+
* product count. Fetch the products themselves with
|
|
1251
|
+
* `getProducts({ categories: [category.id] })`. Vibe-coded mode only, like
|
|
1252
|
+
* {@link getCategories}.
|
|
1253
|
+
*
|
|
1254
|
+
* @example
|
|
1255
|
+
* ```typescript
|
|
1256
|
+
* const category = await client.getCategoryBySlug('running-shoes').catch(() => null);
|
|
1257
|
+
* if (!category) notFound();
|
|
1258
|
+
* const { data: products } = await client.getProducts({ categories: [category.id] });
|
|
1259
|
+
* ```
|
|
1260
|
+
*/
|
|
1261
|
+
async getCategoryBySlug(slug, options) {
|
|
1262
|
+
const headerOverrides = options?.locale ? { "Accept-Language": options.locale } : void 0;
|
|
1263
|
+
const encodedSlug = encodePathSegment(slug);
|
|
1264
|
+
if (this.isVibeCodedMode()) {
|
|
1265
|
+
return this.vibeCodedRequest(
|
|
1266
|
+
"GET",
|
|
1267
|
+
`/categories/slug/${encodedSlug}`,
|
|
1268
|
+
void 0,
|
|
1269
|
+
void 0,
|
|
1270
|
+
headerOverrides
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
throw new BrainerceError("getCategoryBySlug is only available in vibe-coded mode", 400);
|
|
1274
|
+
}
|
|
1247
1275
|
/**
|
|
1248
1276
|
* Get available brands for filtering products
|
|
1249
1277
|
* Works in vibe-coded mode
|
|
@@ -6396,15 +6424,18 @@ var BrainerceClient = class {
|
|
|
6396
6424
|
// these do NOT branch on isVibeCodedMode() (that would 404 silently).
|
|
6397
6425
|
/**
|
|
6398
6426
|
* Get the logged-in customer's loyalty status: enrollment, points balance,
|
|
6399
|
-
* lifetime earned,
|
|
6400
|
-
*
|
|
6401
|
-
*
|
|
6427
|
+
* lifetime earned, the program's display config, earned milestone `badges`,
|
|
6428
|
+
* and the `paidMembership` subscription state (null for free members).
|
|
6429
|
+
* Requires customerToken. Only available in storefront mode. `program` is
|
|
6430
|
+
* null when the store has no loyalty program.
|
|
6402
6431
|
*
|
|
6403
6432
|
* @example
|
|
6404
6433
|
* ```typescript
|
|
6405
6434
|
* client.setCustomerToken(auth.token);
|
|
6406
6435
|
* const status = await client.getLoyaltyStatus();
|
|
6407
6436
|
* if (status.enrolled) console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
6437
|
+
* status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
|
|
6438
|
+
* if (status.paidMembership?.status === 'ACTIVE') showPremiumPerks(status.paidMembership.plan);
|
|
6408
6439
|
* ```
|
|
6409
6440
|
*/
|
|
6410
6441
|
async getLoyaltyStatus() {
|
|
@@ -6537,6 +6568,144 @@ var BrainerceClient = class {
|
|
|
6537
6568
|
}
|
|
6538
6569
|
throw new BrainerceError("getReferralInfo is only available in storefront mode", 400);
|
|
6539
6570
|
}
|
|
6571
|
+
/**
|
|
6572
|
+
* AI-recommended reward for the logged-in customer — "recommended for you"
|
|
6573
|
+
* at the top of the rewards list (requires customerToken). The result is
|
|
6574
|
+
* ALWAYS a real reward from the store's catalog (the AI only ranks; a
|
|
6575
|
+
* hallucinated pick falls back to a deterministic choice). Returns
|
|
6576
|
+
* `{ reward: null }` when the catalog is empty. Rate-limited (5/min per
|
|
6577
|
+
* customer — it spends the merchant's AI credits). Only available in
|
|
6578
|
+
* storefront mode.
|
|
6579
|
+
*
|
|
6580
|
+
* @example
|
|
6581
|
+
* ```typescript
|
|
6582
|
+
* const { reward, reason } = await client.getRecommendedReward();
|
|
6583
|
+
* if (reward) showRecommendation(reward, reason);
|
|
6584
|
+
* ```
|
|
6585
|
+
*/
|
|
6586
|
+
async getRecommendedReward() {
|
|
6587
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6588
|
+
throw new BrainerceError(
|
|
6589
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6590
|
+
401
|
|
6591
|
+
);
|
|
6592
|
+
}
|
|
6593
|
+
if (this.storeId && !this.apiKey) {
|
|
6594
|
+
return this.storefrontRequest(
|
|
6595
|
+
"GET",
|
|
6596
|
+
"/loyalty/rewards/recommended"
|
|
6597
|
+
);
|
|
6598
|
+
}
|
|
6599
|
+
throw new BrainerceError("getRecommendedReward is only available in storefront mode", 400);
|
|
6600
|
+
}
|
|
6601
|
+
/**
|
|
6602
|
+
* List the paid membership plans the customer can subscribe to (requires
|
|
6603
|
+
* customerToken). Empty when the store offers none or the program is not
|
|
6604
|
+
* active. Only available in storefront mode.
|
|
6605
|
+
*
|
|
6606
|
+
* @example
|
|
6607
|
+
* ```typescript
|
|
6608
|
+
* const plans = await client.getMembershipPlans();
|
|
6609
|
+
* ```
|
|
6610
|
+
*/
|
|
6611
|
+
async getMembershipPlans() {
|
|
6612
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6613
|
+
throw new BrainerceError(
|
|
6614
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6615
|
+
401
|
|
6616
|
+
);
|
|
6617
|
+
}
|
|
6618
|
+
if (this.storeId && !this.apiKey) {
|
|
6619
|
+
return this.storefrontRequest("GET", "/loyalty/membership/plans");
|
|
6620
|
+
}
|
|
6621
|
+
throw new BrainerceError("getMembershipPlans is only available in storefront mode", 400);
|
|
6622
|
+
}
|
|
6623
|
+
/**
|
|
6624
|
+
* List the customer's saved payment methods (display fields only — brand /
|
|
6625
|
+
* last4 / expiry, never card data) for the membership subscribe flow.
|
|
6626
|
+
* Requires customerToken. Cards are vaulted by checking out with
|
|
6627
|
+
* `saveCard: true`. Only available in storefront mode.
|
|
6628
|
+
*
|
|
6629
|
+
* @example
|
|
6630
|
+
* ```typescript
|
|
6631
|
+
* const methods = await client.getMySavedPaymentMethods();
|
|
6632
|
+
* ```
|
|
6633
|
+
*/
|
|
6634
|
+
async getMySavedPaymentMethods() {
|
|
6635
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6636
|
+
throw new BrainerceError(
|
|
6637
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6638
|
+
401
|
|
6639
|
+
);
|
|
6640
|
+
}
|
|
6641
|
+
if (this.storeId && !this.apiKey) {
|
|
6642
|
+
return this.storefrontRequest(
|
|
6643
|
+
"GET",
|
|
6644
|
+
"/loyalty/membership/payment-methods"
|
|
6645
|
+
);
|
|
6646
|
+
}
|
|
6647
|
+
throw new BrainerceError(
|
|
6648
|
+
"getMySavedPaymentMethods is only available in storefront mode",
|
|
6649
|
+
400
|
|
6650
|
+
);
|
|
6651
|
+
}
|
|
6652
|
+
/**
|
|
6653
|
+
* Subscribe the customer to a paid membership plan — charges the saved card
|
|
6654
|
+
* IMMEDIATELY and starts the recurring cycle (requires customerToken).
|
|
6655
|
+
* Throws a 409 with a `code` ('card_declined' | 'requires_action' | ...)
|
|
6656
|
+
* when the charge fails; 3D-Secure challenges are not supported off-session
|
|
6657
|
+
* and surface as `requires_action`. Only available in storefront mode.
|
|
6658
|
+
*
|
|
6659
|
+
* @example
|
|
6660
|
+
* ```typescript
|
|
6661
|
+
* const membership = await client.subscribeToMembership({
|
|
6662
|
+
* planId: plan.id,
|
|
6663
|
+
* savedPaymentTokenId: method.id,
|
|
6664
|
+
* });
|
|
6665
|
+
* // membership.status === 'ACTIVE'
|
|
6666
|
+
* ```
|
|
6667
|
+
*/
|
|
6668
|
+
async subscribeToMembership(params) {
|
|
6669
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6670
|
+
throw new BrainerceError(
|
|
6671
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6672
|
+
401
|
|
6673
|
+
);
|
|
6674
|
+
}
|
|
6675
|
+
if (this.storeId && !this.apiKey) {
|
|
6676
|
+
return this.storefrontRequest(
|
|
6677
|
+
"POST",
|
|
6678
|
+
"/loyalty/membership/subscribe",
|
|
6679
|
+
params
|
|
6680
|
+
);
|
|
6681
|
+
}
|
|
6682
|
+
throw new BrainerceError("subscribeToMembership is only available in storefront mode", 400);
|
|
6683
|
+
}
|
|
6684
|
+
/**
|
|
6685
|
+
* Cancel the customer's paid membership (requires customerToken).
|
|
6686
|
+
* End-of-period semantics: perks continue until `nextBillingAt`, then the
|
|
6687
|
+
* subscription ends without another charge (a PAST_DUE membership cancels
|
|
6688
|
+
* immediately). Re-subscribing to the same plan before period end simply
|
|
6689
|
+
* un-cancels. Only available in storefront mode.
|
|
6690
|
+
*
|
|
6691
|
+
* @example
|
|
6692
|
+
* ```typescript
|
|
6693
|
+
* const membership = await client.cancelMembership();
|
|
6694
|
+
* // membership.cancelAtPeriodEnd === true
|
|
6695
|
+
* ```
|
|
6696
|
+
*/
|
|
6697
|
+
async cancelMembership() {
|
|
6698
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6699
|
+
throw new BrainerceError(
|
|
6700
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6701
|
+
401
|
|
6702
|
+
);
|
|
6703
|
+
}
|
|
6704
|
+
if (this.storeId && !this.apiKey) {
|
|
6705
|
+
return this.storefrontRequest("POST", "/loyalty/membership/cancel");
|
|
6706
|
+
}
|
|
6707
|
+
throw new BrainerceError("cancelMembership is only available in storefront mode", 400);
|
|
6708
|
+
}
|
|
6540
6709
|
/**
|
|
6541
6710
|
* Get the current customer's orders (requires customerToken)
|
|
6542
6711
|
* Works in vibe-coded and storefront modes
|
|
@@ -8822,6 +8991,212 @@ function formatAmount(amount, currency, locale) {
|
|
|
8822
8991
|
}
|
|
8823
8992
|
}
|
|
8824
8993
|
|
|
8994
|
+
// src/jsonld.ts
|
|
8995
|
+
function absoluteUrl(siteUrl, path) {
|
|
8996
|
+
if (!path) return void 0;
|
|
8997
|
+
const base = siteUrl.replace(/\/+$/, "");
|
|
8998
|
+
return path.startsWith("http") ? path : `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
8999
|
+
}
|
|
9000
|
+
function stripHtml(html) {
|
|
9001
|
+
return (html ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
9002
|
+
}
|
|
9003
|
+
function buildArticleJsonLd(post, opts) {
|
|
9004
|
+
const url = absoluteUrl(opts.siteUrl, opts.path ?? `/blog/${post.slug}`);
|
|
9005
|
+
const result = {
|
|
9006
|
+
"@context": "https://schema.org",
|
|
9007
|
+
"@type": "Article",
|
|
9008
|
+
headline: post.seoTitle ?? post.title,
|
|
9009
|
+
name: post.title,
|
|
9010
|
+
...post.excerpt || post.seoDescription ? { description: post.seoDescription ?? post.excerpt } : {},
|
|
9011
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9012
|
+
...post.coverImageUrl ? { image: [post.coverImageUrl] } : {},
|
|
9013
|
+
...post.publishedAt ? { datePublished: post.publishedAt } : {},
|
|
9014
|
+
dateModified: post.updatedAt,
|
|
9015
|
+
...post.author ? { author: { "@type": "Person", name: post.author } } : opts.organizationName ? { author: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9016
|
+
...opts.organizationName ? { publisher: { "@type": "Organization", name: opts.organizationName } } : {},
|
|
9017
|
+
...post.tags.length > 0 ? { keywords: post.tags.join(", ") } : {}
|
|
9018
|
+
};
|
|
9019
|
+
return result;
|
|
9020
|
+
}
|
|
9021
|
+
function buildProductJsonLd(product, opts) {
|
|
9022
|
+
const url = absoluteUrl(opts.siteUrl, opts.path ?? (product.slug ? `/products/${product.slug}` : void 0));
|
|
9023
|
+
const images = (product.images ?? []).map((img) => img.url).filter(Boolean);
|
|
9024
|
+
const brand = opts.brandName ?? product.brands?.[0]?.name;
|
|
9025
|
+
const description = stripHtml(product.description).slice(0, 5e3);
|
|
9026
|
+
const effectivePrice = product.salePrice ?? product.basePrice;
|
|
9027
|
+
const inStock = product.inventory ? (product.inventory.available ?? 0) > 0 : true;
|
|
9028
|
+
const isVariable = product.type === "VARIABLE" && product.priceMin && product.priceMax;
|
|
9029
|
+
const offer = isVariable ? {
|
|
9030
|
+
"@type": "AggregateOffer",
|
|
9031
|
+
lowPrice: product.priceMin,
|
|
9032
|
+
highPrice: product.priceMax,
|
|
9033
|
+
priceCurrency: opts.currency,
|
|
9034
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9035
|
+
...url ? { url } : {}
|
|
9036
|
+
} : {
|
|
9037
|
+
"@type": "Offer",
|
|
9038
|
+
price: effectivePrice,
|
|
9039
|
+
priceCurrency: opts.currency,
|
|
9040
|
+
availability: inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
|
|
9041
|
+
...url ? { url } : {}
|
|
9042
|
+
};
|
|
9043
|
+
return {
|
|
9044
|
+
"@context": "https://schema.org",
|
|
9045
|
+
"@type": "Product",
|
|
9046
|
+
name: product.name,
|
|
9047
|
+
...description ? { description } : {},
|
|
9048
|
+
...images.length > 0 ? { image: images } : {},
|
|
9049
|
+
...url ? { url } : {},
|
|
9050
|
+
sku: product.sku,
|
|
9051
|
+
// Identifiers Google uses to match a product to its catalog — key for
|
|
9052
|
+
// free merchant-listing eligibility without a Merchant Center feed.
|
|
9053
|
+
...product.gtin ? { gtin: product.gtin } : {},
|
|
9054
|
+
...product.mpn ? { mpn: product.mpn } : {},
|
|
9055
|
+
...brand ? { brand: { "@type": "Brand", name: brand } } : {},
|
|
9056
|
+
offers: offer,
|
|
9057
|
+
// Google policy: never emit an empty/zero rating block.
|
|
9058
|
+
...product.reviewCount && product.reviewCount > 0 && product.avgRating ? {
|
|
9059
|
+
aggregateRating: {
|
|
9060
|
+
"@type": "AggregateRating",
|
|
9061
|
+
ratingValue: product.avgRating,
|
|
9062
|
+
reviewCount: product.reviewCount
|
|
9063
|
+
}
|
|
9064
|
+
} : {}
|
|
9065
|
+
};
|
|
9066
|
+
}
|
|
9067
|
+
function buildOrganizationJsonLd(store, opts) {
|
|
9068
|
+
const sameAs = Object.values(store.socialLinks ?? {}).filter(Boolean);
|
|
9069
|
+
return {
|
|
9070
|
+
"@context": "https://schema.org",
|
|
9071
|
+
"@type": "Organization",
|
|
9072
|
+
name: store.name,
|
|
9073
|
+
url: opts.siteUrl,
|
|
9074
|
+
...store.logo ? { logo: store.logo } : {},
|
|
9075
|
+
...store.metaDescription ? { description: store.metaDescription } : {},
|
|
9076
|
+
...store.contactEmail ? { email: store.contactEmail } : {},
|
|
9077
|
+
...store.contactPhone ? { telephone: store.contactPhone } : {},
|
|
9078
|
+
...sameAs.length > 0 ? { sameAs } : {}
|
|
9079
|
+
};
|
|
9080
|
+
}
|
|
9081
|
+
function buildWebsiteJsonLd(store, opts) {
|
|
9082
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9083
|
+
const result = {
|
|
9084
|
+
"@context": "https://schema.org",
|
|
9085
|
+
"@type": "WebSite",
|
|
9086
|
+
name: store.name,
|
|
9087
|
+
url: opts.siteUrl
|
|
9088
|
+
};
|
|
9089
|
+
if (opts.searchUrlTemplate?.includes("{search_term_string}")) {
|
|
9090
|
+
const tmpl = opts.searchUrlTemplate.startsWith("http") ? opts.searchUrlTemplate : `${base}${opts.searchUrlTemplate.startsWith("/") ? "" : "/"}${opts.searchUrlTemplate}`;
|
|
9091
|
+
result.potentialAction = {
|
|
9092
|
+
"@type": "SearchAction",
|
|
9093
|
+
target: { "@type": "EntryPoint", urlTemplate: tmpl },
|
|
9094
|
+
"query-input": "required name=search_term_string"
|
|
9095
|
+
};
|
|
9096
|
+
}
|
|
9097
|
+
return result;
|
|
9098
|
+
}
|
|
9099
|
+
function buildCollectionPageJsonLd(category, opts) {
|
|
9100
|
+
const url = absoluteUrl(opts.siteUrl, opts.path);
|
|
9101
|
+
const description = category.metaDescription ?? (stripHtml(category.description) || void 0);
|
|
9102
|
+
return {
|
|
9103
|
+
"@context": "https://schema.org",
|
|
9104
|
+
"@type": "CollectionPage",
|
|
9105
|
+
name: category.name,
|
|
9106
|
+
...description ? { description } : {},
|
|
9107
|
+
...url ? { url, mainEntityOfPage: { "@type": "WebPage", "@id": url } } : {},
|
|
9108
|
+
...category.image ? { image: [category.image] } : {}
|
|
9109
|
+
};
|
|
9110
|
+
}
|
|
9111
|
+
function buildBreadcrumbJsonLd(items) {
|
|
9112
|
+
return {
|
|
9113
|
+
"@context": "https://schema.org",
|
|
9114
|
+
"@type": "BreadcrumbList",
|
|
9115
|
+
itemListElement: items.map((item, index) => ({
|
|
9116
|
+
"@type": "ListItem",
|
|
9117
|
+
position: index + 1,
|
|
9118
|
+
name: item.name,
|
|
9119
|
+
item: item.url
|
|
9120
|
+
}))
|
|
9121
|
+
};
|
|
9122
|
+
}
|
|
9123
|
+
function jsonLdScriptProps(data) {
|
|
9124
|
+
return {
|
|
9125
|
+
type: "application/ld+json",
|
|
9126
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(data).replace(/</g, "\\u003c") }
|
|
9127
|
+
};
|
|
9128
|
+
}
|
|
9129
|
+
|
|
9130
|
+
// src/sitemap.ts
|
|
9131
|
+
async function getBlogSitemapEntries(client, opts) {
|
|
9132
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9133
|
+
const basePath = opts.basePath ?? "/blog";
|
|
9134
|
+
const pageSize = Math.min(opts.pageSize ?? 100, 100);
|
|
9135
|
+
const maxEntries = opts.maxEntries ?? 5e3;
|
|
9136
|
+
const posts = [];
|
|
9137
|
+
let page = 1;
|
|
9138
|
+
for (; ; ) {
|
|
9139
|
+
const res = await client.blog.getPosts({ page, limit: pageSize });
|
|
9140
|
+
posts.push(...res.data.map((p) => ({ slug: p.slug, updatedAt: p.updatedAt })));
|
|
9141
|
+
if (page >= res.meta.totalPages || posts.length >= maxEntries) break;
|
|
9142
|
+
page += 1;
|
|
9143
|
+
}
|
|
9144
|
+
const nonDefaultLocales = opts.locales?.filter((locale) => locale !== opts.defaultLocale) ?? [];
|
|
9145
|
+
const entries = [];
|
|
9146
|
+
entries.push({ url: `${base}${basePath}`, changeFrequency: "daily", priority: 0.7 });
|
|
9147
|
+
for (const locale of nonDefaultLocales) {
|
|
9148
|
+
entries.push({
|
|
9149
|
+
url: `${base}/${locale}${basePath}`,
|
|
9150
|
+
changeFrequency: "daily",
|
|
9151
|
+
priority: 0.7
|
|
9152
|
+
});
|
|
9153
|
+
}
|
|
9154
|
+
for (const post of posts.slice(0, maxEntries)) {
|
|
9155
|
+
const lastModified = new Date(post.updatedAt);
|
|
9156
|
+
entries.push({
|
|
9157
|
+
url: `${base}${basePath}/${post.slug}`,
|
|
9158
|
+
lastModified,
|
|
9159
|
+
changeFrequency: "weekly",
|
|
9160
|
+
priority: 0.6
|
|
9161
|
+
});
|
|
9162
|
+
for (const locale of nonDefaultLocales) {
|
|
9163
|
+
entries.push({
|
|
9164
|
+
url: `${base}/${locale}${basePath}/${post.slug}`,
|
|
9165
|
+
lastModified,
|
|
9166
|
+
changeFrequency: "weekly",
|
|
9167
|
+
priority: 0.5
|
|
9168
|
+
});
|
|
9169
|
+
}
|
|
9170
|
+
}
|
|
9171
|
+
return entries;
|
|
9172
|
+
}
|
|
9173
|
+
async function getCategorySitemapEntries(client, opts) {
|
|
9174
|
+
const base = opts.siteUrl.replace(/\/+$/, "");
|
|
9175
|
+
const basePath = opts.basePath ?? "/category";
|
|
9176
|
+
const nonDefaultLocales = opts.locales?.filter((l) => l !== opts.defaultLocale) ?? [];
|
|
9177
|
+
const { categories } = await client.getCategories();
|
|
9178
|
+
const slugs = [];
|
|
9179
|
+
const walk = (nodes) => {
|
|
9180
|
+
for (const node of nodes) {
|
|
9181
|
+
if (node.slug) slugs.push(node.slug);
|
|
9182
|
+
if (node.children?.length) walk(node.children);
|
|
9183
|
+
}
|
|
9184
|
+
};
|
|
9185
|
+
walk(categories);
|
|
9186
|
+
const entries = [];
|
|
9187
|
+
for (const slug of slugs) {
|
|
9188
|
+
entries.push({ url: `${base}${basePath}/${slug}`, changeFrequency: "daily", priority: 0.8 });
|
|
9189
|
+
for (const locale of nonDefaultLocales) {
|
|
9190
|
+
entries.push({
|
|
9191
|
+
url: `${base}/${locale}${basePath}/${slug}`,
|
|
9192
|
+
changeFrequency: "daily",
|
|
9193
|
+
priority: 0.7
|
|
9194
|
+
});
|
|
9195
|
+
}
|
|
9196
|
+
}
|
|
9197
|
+
return entries;
|
|
9198
|
+
}
|
|
9199
|
+
|
|
8825
9200
|
// src/types.ts
|
|
8826
9201
|
function isHtmlDescription(product) {
|
|
8827
9202
|
if (product?.descriptionFormat === "html") return true;
|
|
@@ -8837,7 +9212,7 @@ function getDescriptionContent(product) {
|
|
|
8837
9212
|
}
|
|
8838
9213
|
return { text: product.description };
|
|
8839
9214
|
}
|
|
8840
|
-
function
|
|
9215
|
+
function stripHtml2(html) {
|
|
8841
9216
|
if (!html) return "";
|
|
8842
9217
|
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();
|
|
8843
9218
|
}
|
|
@@ -8846,7 +9221,7 @@ function deriveSeoDescription(product, options) {
|
|
|
8846
9221
|
const maxLength = options?.maxLength ?? 160;
|
|
8847
9222
|
const authored = (product.seoDescription ?? product.metaDescription ?? "").trim();
|
|
8848
9223
|
if (authored) return authored;
|
|
8849
|
-
const plain =
|
|
9224
|
+
const plain = stripHtml2(product.description);
|
|
8850
9225
|
if (plain) {
|
|
8851
9226
|
if (plain.length <= maxLength) return plain;
|
|
8852
9227
|
const sliceLength = maxLength - 1;
|
|
@@ -9067,6 +9442,12 @@ export {
|
|
|
9067
9442
|
BrainerceError,
|
|
9068
9443
|
RTL_LOCALES,
|
|
9069
9444
|
SDK_VERSION,
|
|
9445
|
+
buildArticleJsonLd,
|
|
9446
|
+
buildBreadcrumbJsonLd,
|
|
9447
|
+
buildCollectionPageJsonLd,
|
|
9448
|
+
buildOrganizationJsonLd,
|
|
9449
|
+
buildProductJsonLd,
|
|
9450
|
+
buildWebsiteJsonLd,
|
|
9070
9451
|
createWebhookHandler,
|
|
9071
9452
|
deriveSeoDescription,
|
|
9072
9453
|
enableDevGuards,
|
|
@@ -9074,9 +9455,11 @@ export {
|
|
|
9074
9455
|
formatPrice,
|
|
9075
9456
|
formatProductPrice,
|
|
9076
9457
|
formatVariantPrice,
|
|
9458
|
+
getBlogSitemapEntries,
|
|
9077
9459
|
getCartItemImage,
|
|
9078
9460
|
getCartItemName,
|
|
9079
9461
|
getCartTotals,
|
|
9462
|
+
getCategorySitemapEntries,
|
|
9080
9463
|
getDescriptionContent,
|
|
9081
9464
|
getDirectionForLocale,
|
|
9082
9465
|
formatPrice as getPriceDisplay,
|
|
@@ -9094,8 +9477,9 @@ export {
|
|
|
9094
9477
|
isCouponApplicableToProduct,
|
|
9095
9478
|
isHtmlDescription,
|
|
9096
9479
|
isWebhookEventType,
|
|
9480
|
+
jsonLdScriptProps,
|
|
9097
9481
|
parseWebhookEvent,
|
|
9098
9482
|
safePaymentRedirect,
|
|
9099
|
-
stripHtml,
|
|
9483
|
+
stripHtml2 as stripHtml,
|
|
9100
9484
|
verifyWebhook
|
|
9101
9485
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.1",
|
|
4
4
|
"description": "Official SDK for building e-commerce storefronts with Brainerce Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|