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/README.md
CHANGED
|
@@ -32,28 +32,30 @@ yarn add brainerce
|
|
|
32
32
|
|
|
33
33
|
Every Brainerce storefront must include **all mandatory features** below. Features auto-hide when the underlying capability is disabled, so build them all anyway — they'll appear the moment the store owner enables them.
|
|
34
34
|
|
|
35
|
-
| Feature
|
|
36
|
-
|
|
|
37
|
-
| Product list with search, filter, pagination
|
|
38
|
-
| Product detail with variant picker, stock, price
|
|
39
|
-
| Buyer customization fields (engraving, uploads, select)
|
|
40
|
-
| Cart (add, update, remove, coupon, totals)
|
|
41
|
-
| Inventory reservation countdown
|
|
42
|
-
| Full checkout end-to-end with payment
|
|
43
|
-
| Order confirmation (clear cart + wait for real order)
|
|
44
|
-
| Register + email verification flow
|
|
45
|
-
| Login + verification branch
|
|
46
|
-
| Forgot / reset password
|
|
47
|
-
| OAuth sign-in buttons + callback handler
|
|
48
|
-
| Account area (profile + order history)
|
|
49
|
-
| Loyalty & rewards (points balance + tiers + redeem)
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
35
|
+
| Feature | SDK entry point | Mandatory |
|
|
36
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
|
37
|
+
| Product list with search, filter, pagination | `client.getProducts()`, `client.getSearchSuggestions(query)` | ✅ |
|
|
38
|
+
| Product detail with variant picker, stock, price | `client.getProductBySlug()` + helpers | ✅ |
|
|
39
|
+
| Buyer customization fields (engraving, uploads, select) | `product.customizationFields`, `client.uploadCustomizationFile()` | ✅ |
|
|
40
|
+
| Cart (add, update, remove, coupon, totals) | `client.addToCart()`, `getCartTotals(cart)` | ✅ |
|
|
41
|
+
| Inventory reservation countdown | Cart expiry timestamp from `client.getCart()` | ✅ |
|
|
42
|
+
| Full checkout end-to-end with payment | `setShippingAddress → selectShippingMethod → getPaymentProviders → pay → handlePaymentSuccess → waitForOrder` | ✅ |
|
|
43
|
+
| Order confirmation (clear cart + wait for real order) | `client.handlePaymentSuccess()`, `client.waitForOrder()` | ✅ |
|
|
44
|
+
| Register + email verification flow | `client.registerCustomer()`, `client.verifyEmail()` | ✅ |
|
|
45
|
+
| Login + verification branch | `client.loginCustomer()` | ✅ |
|
|
46
|
+
| Forgot / reset password | `client.forgotPassword()`, `client.resetPassword()` | ✅ |
|
|
47
|
+
| OAuth sign-in buttons + callback handler | `client.getAvailableOAuthProviders()` | ✅ |
|
|
48
|
+
| Account area (profile + order history) | `client.getMyProfile()`, `client.getMyOrders()` | ✅ |
|
|
49
|
+
| Loyalty & rewards (points balance + tiers + redeem) | `client.getLoyaltyStatus()`, `client.getAvailableRewards()`, `client.getRecommendedReward()`, `client.redeemLoyaltyReward(id)`, `client.reportSocialShare()` | conditional |
|
|
50
|
+
| Loyalty paid membership (premium subscription) | `client.getMembershipPlans()`, `client.getMySavedPaymentMethods()`, `client.subscribeToMembership(params)`, `client.cancelMembership()` | conditional |
|
|
51
|
+
| Embeddable loyalty widget (points + rewards on ANY site) | `client.getLoyaltyWidgetSession()` | conditional |
|
|
52
|
+
| Global header: cart count + search autocomplete | `client.getCart()`, `client.getSearchSuggestions(query)` | ✅ |
|
|
53
|
+
| Discount banners + product badges | `client.getDiscountBanners()`, `client.getProductDiscountBadge(productId)` | ✅ |
|
|
54
|
+
| Product reviews on PDP + JSON-LD aggregateRating | `client.listProductReviews(id)`, `client.submitProductReview(id, …)` | ✅ |
|
|
55
|
+
| Site chrome (header + footer + announcement bar) | `client.content.header.get()`, `client.content.footer.get()`, `client.content.announcement.list()` | ✅ |
|
|
56
|
+
| FAQ page | `client.content.faq.get('main', locale)` | conditional |
|
|
57
|
+
| Static pages catch-all (`/pages/[slug]`) | `client.content.page.getBySlug(slug, locale)` | conditional |
|
|
58
|
+
| Multi-language + RTL (when i18n enabled) | `client.setLocale()`, `client.getStoreDirection(locale)` | conditional |
|
|
57
59
|
|
|
58
60
|
---
|
|
59
61
|
|
|
@@ -266,6 +268,12 @@ The SDK exports these utility functions for common UI tasks:
|
|
|
266
268
|
| `isCouponApplicableToProduct(coupon, product)` | Check if coupon applies | `isCouponApplicableToProduct(coupon, product)` |
|
|
267
269
|
| `isAllowedPaymentUrl(url, options?)` | Validate a payment URL host | `isAllowedPaymentUrl(intent.clientSecret)` → `true` |
|
|
268
270
|
| `safePaymentRedirect(url, options?)` | Validate then `window.location.href` | `safePaymentRedirect(intent.clientSecret)` |
|
|
271
|
+
| `buildProductJsonLd(product, opts)` | schema.org Product JSON-LD (PDPs only) | See SEO section |
|
|
272
|
+
| `buildArticleJsonLd(post, opts)` | schema.org Article JSON-LD for blog posts | See SEO section |
|
|
273
|
+
| `buildOrganizationJsonLd(store, opts)` | schema.org Organization for the homepage | See SEO section |
|
|
274
|
+
| `buildBreadcrumbJsonLd(items)` | schema.org BreadcrumbList | See SEO section |
|
|
275
|
+
| `jsonLdScriptProps(data)` | XSS-safe `<script type="application/ld+json">` props | `<script {...jsonLdScriptProps(data)} />` |
|
|
276
|
+
| `getBlogSitemapEntries(client, opts)` | Paginate published posts into sitemap entries | See SEO section |
|
|
269
277
|
|
|
270
278
|
```typescript
|
|
271
279
|
import {
|
|
@@ -451,7 +459,57 @@ const safeHtml = DOMPurify.sanitize(post.content);
|
|
|
451
459
|
return <div dangerouslySetInnerHTML={{ __html: safeHtml }} className="prose" />;
|
|
452
460
|
```
|
|
453
461
|
|
|
454
|
-
**Scheduling**: A post is visible once `status === 'PUBLISHED'` and `publishedAt <= now()`. Set a future `publishedAt` when publishing to schedule
|
|
462
|
+
**Scheduling**: A post is visible once `status === 'PUBLISHED'` and `publishedAt <= now()`. Set a future `publishedAt` when publishing to schedule.
|
|
463
|
+
|
|
464
|
+
**SEO Autopilot writes here too**: the platform's SEO Autopilot publishes AI-written articles into this same blog automatically — render whatever `getPosts()` returns, and see the SEO section below for the required discoverability pieces.
|
|
465
|
+
|
|
466
|
+
### SEO — JSON-LD builders, sitemap helper, IndexNow key, llms.txt
|
|
467
|
+
|
|
468
|
+
The SDK ships schema.org builders that encode Google's structured-data rules (aggregateRating gated on `reviewCount > 0`, AggregateOffer for VARIABLE products, XSS-safe serialization). `buildProductJsonLd`'s Offer also always includes `itemCondition` (hardcoded `NewCondition` — first-party new-goods catalog), `priceValidUntil` when the product has an active sale-price window (`salePriceEndsAt`), and `shippingDetails` when you pass `shipping` (real flat-rate/free zones from `storeInfo.shipping` — omitted entirely, never fabricated, if you don't pass it). Prefer these builders over hand-rolled JSON-LD:
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
import {
|
|
472
|
+
buildProductJsonLd, // single-product pages ONLY (never listing pages)
|
|
473
|
+
buildArticleJsonLd, // blog post pages
|
|
474
|
+
buildOrganizationJsonLd, // homepage
|
|
475
|
+
buildCollectionPageJsonLd, // category landing pages
|
|
476
|
+
buildBreadcrumbJsonLd,
|
|
477
|
+
jsonLdScriptProps, // XSS-safe <script> props
|
|
478
|
+
} from 'brainerce';
|
|
479
|
+
|
|
480
|
+
// Blog article page
|
|
481
|
+
<script {...jsonLdScriptProps(buildArticleJsonLd(post, {
|
|
482
|
+
siteUrl: process.env.NEXT_PUBLIC_SITE_URL!,
|
|
483
|
+
path: `/blog/${post.slug}`,
|
|
484
|
+
organizationName: storeInfo.name,
|
|
485
|
+
}))} />
|
|
486
|
+
|
|
487
|
+
// Product page
|
|
488
|
+
<script {...jsonLdScriptProps(buildProductJsonLd(product, {
|
|
489
|
+
siteUrl: process.env.NEXT_PUBLIC_SITE_URL!,
|
|
490
|
+
path: `/products/${product.slug}`,
|
|
491
|
+
currency: storeInfo.currency,
|
|
492
|
+
shipping: storeInfo.shipping, // optional — adds shippingDetails when present
|
|
493
|
+
}))} />
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Blog entries in sitemap.xml** (required — autopilot articles missing from the sitemap never get crawled):
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
// app/sitemap.ts
|
|
500
|
+
import { getBlogSitemapEntries } from 'brainerce';
|
|
501
|
+
|
|
502
|
+
const blogPages = await getBlogSitemapEntries(client, {
|
|
503
|
+
siteUrl: baseUrl,
|
|
504
|
+
locales: supportedLocales, // optional (multi-locale stores)
|
|
505
|
+
defaultLocale,
|
|
506
|
+
}).catch(() => []);
|
|
507
|
+
return [...staticPages, ...productPages, ...blogPages];
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**IndexNow key file** (required): the platform pings IndexNow when posts publish; search engines verify by fetching `GET /indexnow-key.txt`. Serve `getStoreInfo().seo.indexNowKey` as `text/plain`, 404 while `null`. The key is **not a secret** (public by protocol design).
|
|
511
|
+
|
|
512
|
+
**llms.txt** (recommended): a plain-text site summary (store name, key pages, recent article links) for AI answer engines at `GET /llms.txt`.
|
|
455
513
|
|
|
456
514
|
---
|
|
457
515
|
|
|
@@ -807,6 +865,26 @@ import { getCartTotals } from 'brainerce';
|
|
|
807
865
|
const totals = getCartTotals(cart); // { subtotal, discount, shipping, total }
|
|
808
866
|
```
|
|
809
867
|
|
|
868
|
+
### Detecting a Silent Session Cart Reset
|
|
869
|
+
|
|
870
|
+
If a guest's stored session cart can no longer be resolved as-is — the fetch
|
|
871
|
+
fails, or the cart was found but is no longer `ACTIVE` (e.g. a prior checkout
|
|
872
|
+
on it already completed) — `smartAddToCart()` / `smartGetCart()` /
|
|
873
|
+
`smartUpdateCartItem()` transparently start a fresh empty cart so the call
|
|
874
|
+
still succeeds. Pass `onCartReset` to the constructor to find out when this
|
|
875
|
+
happens, so you can tell the shopper their cart expired instead of them just
|
|
876
|
+
seeing it empty out:
|
|
877
|
+
|
|
878
|
+
```typescript
|
|
879
|
+
const client = new BrainerceClient({
|
|
880
|
+
salesChannelId: 'vc_abc123...',
|
|
881
|
+
onCartReset: ({ previousCartId, reason }) => {
|
|
882
|
+
// reason: 'not_found' (fetch failed) | 'not_active' (cart no longer ACTIVE)
|
|
883
|
+
toast.warn('Your cart session expired — please re-add your items.');
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
```
|
|
887
|
+
|
|
810
888
|
### On Login — Merge Guest Cart
|
|
811
889
|
|
|
812
890
|
```typescript
|
|
@@ -1147,11 +1225,45 @@ const { categories } = await client.getCategories();
|
|
|
1147
1225
|
interface CategoryNode {
|
|
1148
1226
|
id: string;
|
|
1149
1227
|
name: string;
|
|
1228
|
+
slug?: string | null; // link a category page as `/category/${slug}`
|
|
1150
1229
|
parentId?: string | null;
|
|
1230
|
+
image?: string | null;
|
|
1151
1231
|
children: CategoryNode[]; // Recursive — can nest to any depth
|
|
1152
1232
|
}
|
|
1153
1233
|
```
|
|
1154
1234
|
|
|
1235
|
+
#### Get Category by Slug (Category Page)
|
|
1236
|
+
|
|
1237
|
+
Category (collection) pages are the highest-leverage organic-SEO surface — they rank for broad "research intent" queries that individual product pages never do. `getCategoryBySlug` returns the landing-page payload; fetch the products themselves with `getProducts({ categories: [category.id] })`.
|
|
1238
|
+
|
|
1239
|
+
```typescript
|
|
1240
|
+
// app/category/[slug]/page.tsx
|
|
1241
|
+
const category = await client.getCategoryBySlug(params.slug).catch(() => null);
|
|
1242
|
+
if (!category) notFound();
|
|
1243
|
+
|
|
1244
|
+
const { data: products } = await client.getProducts({ categories: [category.id] });
|
|
1245
|
+
|
|
1246
|
+
// SEO: CollectionPage + BreadcrumbList JSON-LD (never Product markup on a listing)
|
|
1247
|
+
const jsonLd = buildCollectionPageJsonLd(category, { siteUrl, path: `/category/${category.slug}` });
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
**Type:**
|
|
1251
|
+
|
|
1252
|
+
```typescript
|
|
1253
|
+
interface CategoryDetail {
|
|
1254
|
+
id: string;
|
|
1255
|
+
name: string;
|
|
1256
|
+
slug: string | null;
|
|
1257
|
+
description: string | null; // long-form HTML — render below the product grid
|
|
1258
|
+
metaDescription: string | null; // <meta name="description">
|
|
1259
|
+
image: string | null;
|
|
1260
|
+
breadcrumb: Array<{ name: string; slug: string | null }>; // root → parent
|
|
1261
|
+
productCount: number;
|
|
1262
|
+
}
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
> Vibe-coded mode only (like `getCategories`). Category slugs come from the dashboard SEO hub, which also writes the description + meta.
|
|
1266
|
+
|
|
1155
1267
|
#### Filter Products by Category
|
|
1156
1268
|
|
|
1157
1269
|
Pass a category ID to `getProducts()`. **The backend automatically includes all subcategories.** So filtering by a parent category returns products from that category AND all its descendants.
|
|
@@ -1425,7 +1537,9 @@ function SearchInput() {
|
|
|
1425
1537
|
</a>
|
|
1426
1538
|
))}
|
|
1427
1539
|
{suggestions.categories.map((category) => (
|
|
1428
|
-
|
|
1540
|
+
// Suggestions carry no slug — link to the filtered listing (by id),
|
|
1541
|
+
// not the SEO category page (which is `/category/${slug}`).
|
|
1542
|
+
<a key={category.id} href={`/products?category=${category.id}`}>
|
|
1429
1543
|
{category.name} ({category.productCount} products)
|
|
1430
1544
|
</a>
|
|
1431
1545
|
))}
|
|
@@ -1445,17 +1559,19 @@ interface Product {
|
|
|
1445
1559
|
description?: string | null;
|
|
1446
1560
|
descriptionFormat?: 'text' | 'html' | 'markdown'; // Format of description content
|
|
1447
1561
|
sku: string;
|
|
1448
|
-
basePrice:
|
|
1449
|
-
salePrice?:
|
|
1562
|
+
basePrice: string; // Decimal as string — use parseFloat() for calculations
|
|
1563
|
+
salePrice?: string | null;
|
|
1564
|
+
salePriceStartsAt?: string | null; // ISO 8601 — sale-price effective window start
|
|
1565
|
+
salePriceEndsAt?: string | null; // ISO 8601 — sale-price window end; feeds buildProductJsonLd's priceValidUntil
|
|
1450
1566
|
priceMin?: string | null; // Lowest variant price (VARIABLE products only)
|
|
1451
1567
|
priceMax?: string | null; // Highest variant price (VARIABLE products only)
|
|
1452
1568
|
priceVaries?: boolean; // true when range should be shown ("₪49 – ₪199")
|
|
1453
|
-
status:
|
|
1569
|
+
status: string; // e.g. "active" | "draft"
|
|
1454
1570
|
type: 'SIMPLE' | 'VARIABLE';
|
|
1455
1571
|
images?: ProductImage[];
|
|
1456
1572
|
inventory?: InventoryInfo | null;
|
|
1457
1573
|
variants?: ProductVariant[];
|
|
1458
|
-
categories?: string[]
|
|
1574
|
+
categories?: Array<{ id: string; name: string; slug?: string | null }>; // NOT string[] — use slug to link to /category/{slug}
|
|
1459
1575
|
tags?: string[];
|
|
1460
1576
|
createdAt: string;
|
|
1461
1577
|
updatedAt: string;
|
|
@@ -2979,20 +3095,22 @@ const { data: orders, meta } = await client.getMyOrders({
|
|
|
2979
3095
|
|
|
2980
3096
|
#### Loyalty & Rewards
|
|
2981
3097
|
|
|
2982
|
-
Storefront-mode
|
|
2983
|
-
customers check their points balance, enroll,
|
|
2984
|
-
coupons that apply at checkout.
|
|
3098
|
+
Storefront or vibe-coded mode (requires `storeId` or `salesChannelId` +
|
|
3099
|
+
`customerToken`). Let logged-in customers check their points balance, enroll,
|
|
3100
|
+
and redeem rewards for one-time coupons that apply at checkout.
|
|
2985
3101
|
|
|
2986
3102
|
```typescript
|
|
2987
|
-
// Current status — points balance + program display config + tier progress
|
|
2988
|
-
// `
|
|
2989
|
-
//
|
|
3103
|
+
// Current status — points balance + program display config + tier progress +
|
|
3104
|
+
// earned milestone `badges` + `paidMembership` (paid subscription, null for
|
|
3105
|
+
// free members). `program`/`tier`/`nextTier` are null if the store has no
|
|
3106
|
+
// loyalty program (or no tiers configured / the customer hasn't reached one yet).
|
|
2990
3107
|
const status = await client.getLoyaltyStatus();
|
|
2991
3108
|
if (status.enrolled) {
|
|
2992
3109
|
console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
2993
3110
|
if (status.nextTier) {
|
|
2994
3111
|
console.log(`${status.pointsToNextTier} to reach ${status.nextTier.name}`);
|
|
2995
3112
|
}
|
|
3113
|
+
status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
|
|
2996
3114
|
}
|
|
2997
3115
|
|
|
2998
3116
|
// Enroll (idempotent) — only if the program is ACTIVE. May grant a one-time
|
|
@@ -3003,6 +3121,11 @@ await client.enrollInLoyalty();
|
|
|
3003
3121
|
// their current tier qualifies for).
|
|
3004
3122
|
const rewards = await client.getAvailableRewards();
|
|
3005
3123
|
|
|
3124
|
+
// AI "recommended for you" — always one of the catalog's real rewards (the AI
|
|
3125
|
+
// only ranks; it can never invent a discount). `{ reward: null }` when the
|
|
3126
|
+
// catalog is empty. Rate-limited to 5/min per customer.
|
|
3127
|
+
const { reward, reason } = await client.getRecommendedReward();
|
|
3128
|
+
|
|
3006
3129
|
// Redeem → spends points, returns a one-time coupon code to apply to the cart.
|
|
3007
3130
|
// `discountType` tells you how to interpret `discountValue` (currency amount
|
|
3008
3131
|
// for FIXED_DISCOUNT, 0-100 percent for PERCENT_DISCOUNT).
|
|
@@ -3047,6 +3170,64 @@ Birthday gifts need no SDK calls beyond profile data: set the customer's
|
|
|
3047
3170
|
platform emails a one-time gift coupon ahead of their birthday automatically
|
|
3048
3171
|
(when the store has it enabled).
|
|
3049
3172
|
|
|
3173
|
+
#### Paid Loyalty Membership
|
|
3174
|
+
|
|
3175
|
+
Stores can offer a paid "premium membership" inside the loyalty program — a
|
|
3176
|
+
recurring charge (default every 30 days) that grants a points multiplier and
|
|
3177
|
+
other perks. Storefront or vibe-coded mode, requires `customerToken`. The
|
|
3178
|
+
customer must first have a saved card (vault one by checking out with
|
|
3179
|
+
`saveCard: true`).
|
|
3180
|
+
|
|
3181
|
+
```typescript
|
|
3182
|
+
// 1. Plans the store offers (empty if none / program not active).
|
|
3183
|
+
const plans = await client.getMembershipPlans();
|
|
3184
|
+
|
|
3185
|
+
// 2. The customer's saved cards — display fields only (brand / last4 / expiry).
|
|
3186
|
+
const methods = await client.getMySavedPaymentMethods();
|
|
3187
|
+
|
|
3188
|
+
// 3. Subscribe — charges the chosen card IMMEDIATELY and schedules the
|
|
3189
|
+
// recurring cycle. Declines throw a 409 with a `code` ('card_declined',
|
|
3190
|
+
// 'requires_action' when the bank demands 3D-Secure — not supported
|
|
3191
|
+
// off-session, ask the customer to use a different card).
|
|
3192
|
+
const membership = await client.subscribeToMembership({
|
|
3193
|
+
planId: plans[0].id,
|
|
3194
|
+
savedPaymentTokenId: methods.find((m) => m.isDefault)!.id,
|
|
3195
|
+
});
|
|
3196
|
+
// membership.status === 'ACTIVE'; points now earn at plan.pointsMultiplier ×
|
|
3197
|
+
// the tier multiplier.
|
|
3198
|
+
|
|
3199
|
+
// 4. Cancel — end-of-period: perks continue until `nextBillingAt`, then the
|
|
3200
|
+
// subscription ends with no further charge. Re-subscribing to the same plan
|
|
3201
|
+
// before period end un-cancels.
|
|
3202
|
+
const cancelled = await client.cancelMembership();
|
|
3203
|
+
// cancelled.cancelAtPeriodEnd === true
|
|
3204
|
+
|
|
3205
|
+
// `getLoyaltyStatus().paidMembership` reflects the subscription everywhere:
|
|
3206
|
+
// { status: 'ACTIVE' | 'PAST_DUE' | 'CANCELLED', nextBillingAt, plan, cancelAtPeriodEnd }
|
|
3207
|
+
// PAST_DUE = the last recurring charge failed; the platform retries daily and
|
|
3208
|
+
// pauses the multiplier until the charge clears.
|
|
3209
|
+
```
|
|
3210
|
+
|
|
3211
|
+
#### Embeddable Loyalty Widget
|
|
3212
|
+
|
|
3213
|
+
Drop the loyalty program into ANY website — not just your SDK-connected
|
|
3214
|
+
storefront. Mint a short-lived widget session (never exposes the real
|
|
3215
|
+
`customerToken` to the embedding page) and point an `<iframe>` at it. The
|
|
3216
|
+
merchant enables this per-domain in the dashboard (Loyalty → Settings →
|
|
3217
|
+
Embeddable widget) before it will render anywhere.
|
|
3218
|
+
|
|
3219
|
+
```typescript
|
|
3220
|
+
client.setCustomerToken(auth.token);
|
|
3221
|
+
|
|
3222
|
+
const { embedUrl } = await client.getLoyaltyWidgetSession();
|
|
3223
|
+
|
|
3224
|
+
// <iframe src={embedUrl} width="360" height="420" style={{ border: 0 }} />
|
|
3225
|
+
```
|
|
3226
|
+
|
|
3227
|
+
`embedUrl` expires in ~15 minutes — re-call `getLoyaltyWidgetSession()` before
|
|
3228
|
+
that (e.g. on page load) rather than caching it long-term. Storefront or
|
|
3229
|
+
vibe-coded mode, requires `customerToken`.
|
|
3230
|
+
|
|
3050
3231
|
#### Auth Response Type
|
|
3051
3232
|
|
|
3052
3233
|
```typescript
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";(()=>{var U="https://api.brainerce.com",z=new Set(["he","ar"]),L={en:{online:"Online",placeholder:"Ask anything\u2026",error:"Something went wrong \u2014 please try again.",leaveMessage:"Leave a message for the team",yourEmail:"Your email",yourMessage:"Your message",send:"Send",sent:"Thanks! The team will get back to you by email.",close:"Close",expand:"Expand",collapse:"Collapse",searching:"Searching the store\u2026",addToCart:"Add to cart",added:"Added",view:"View",chooseOptions:"View product",results:"From the store",poweredBy:"Powered by Brainerce",addFailed:"I couldn\u2019t add that to the cart \u2014 try the button on the product card."},he:{online:"\u05DE\u05D7\u05D5\u05D1\u05E8",placeholder:"\u05E9\u05D0\u05DC\u05D5 \u05D0\u05D5\u05EA\u05D9 \u05D4\u05DB\u05DC\u2026",error:"\u05DE\u05E9\u05D4\u05D5 \u05D4\u05E9\u05EA\u05D1\u05E9 \u2014 \u05E0\u05E1\u05D5 \u05E9\u05D5\u05D1.",leaveMessage:"\u05D4\u05E9\u05D0\u05D9\u05E8\u05D5 \u05D4\u05D5\u05D3\u05E2\u05D4 \u05DC\u05E6\u05D5\u05D5\u05EA",yourEmail:"\u05D4\u05D0\u05D9\u05DE\u05D9\u05D9\u05DC \u05E9\u05DC\u05DB\u05DD",yourMessage:"\u05D4\u05D4\u05D5\u05D3\u05E2\u05D4 \u05E9\u05DC\u05DB\u05DD",send:"\u05E9\u05DC\u05D9\u05D7\u05D4",sent:"\u05EA\u05D5\u05D3\u05D4! \u05D4\u05E6\u05D5\u05D5\u05EA \u05D9\u05D7\u05D6\u05D5\u05E8 \u05D0\u05DC\u05D9\u05DB\u05DD \u05D1\u05DE\u05D9\u05D9\u05DC.",close:"\u05E1\u05D2\u05D9\u05E8\u05D4",expand:"\u05D4\u05E8\u05D7\u05D1\u05D4",collapse:"\u05DB\u05D9\u05D5\u05D5\u05E5",searching:"\u05DE\u05D7\u05E4\u05E9 \u05D1\u05D7\u05E0\u05D5\u05EA\u2026",addToCart:"\u05D4\u05D5\u05E1\u05E4\u05D4 \u05DC\u05E1\u05DC",added:"\u05E0\u05D5\u05E1\u05E3",view:"\u05E6\u05E4\u05D9\u05D9\u05D4",chooseOptions:"\u05DC\u05E6\u05E4\u05D5\u05EA \u05D1\u05DE\u05D5\u05E6\u05E8",results:"\u05DE\u05EA\u05D5\u05DA \u05D4\u05D7\u05E0\u05D5\u05EA",poweredBy:"\u05DE\u05D5\u05E4\u05E2\u05DC \u05E2\u05DC \u05D9\u05D3\u05D9 Brainerce",addFailed:"\u05DC\u05D0 \u05D4\u05E6\u05DC\u05D7\u05EA\u05D9 \u05DC\u05D4\u05D5\u05E1\u05D9\u05E3 \u05DC\u05E1\u05DC \u2014 \u05E0\u05E1\u05D5 \u05D3\u05E8\u05DA \u05D4\u05DB\u05E4\u05EA\u05D5\u05E8 \u05D1\u05DB\u05E8\u05D8\u05D9\u05E1 \u05D4\u05DE\u05D5\u05E6\u05E8."}},H={chat:'<svg viewBox="0 0 24 24" fill="none"><path d="M12 3C7.03 3 3 6.58 3 11c0 2.04.86 3.9 2.28 5.32-.15 1.23-.62 2.39-1.1 3.21-.13.23.05.52.31.47 1.56-.27 3.07-.93 4.13-1.62A10.6 10.6 0 0 0 12 19c4.97 0 9-3.58 9-8s-4.03-8-9-8Z" fill="currentColor"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',expand:'<svg viewBox="0 0 24 24" fill="none"><path d="M14 4h6v6M10 20H4v-6M20 4l-7 7M4 20l7-7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',collapse:'<svg viewBox="0 0 24 24" fill="none"><path d="M20 10h-6V4M4 14h6v6M20 4l-6 6M4 20l6-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',mail:'<svg viewBox="0 0 24 24" fill="none"><rect x="3.5" y="5.5" width="17" height="13" rx="2.5" stroke="currentColor" stroke-width="1.7"/><path d="m4.5 7.5 7.5 5.5 7.5-5.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none"><path d="M4.4 11.2 19 4.6c.7-.3 1.4.4 1.1 1.1l-6.6 14.6c-.3.7-1.3.6-1.5-.1l-1.7-5.4a1 1 0 0 0-.6-.6l-5.4-1.7c-.7-.2-.8-1.2-.1-1.5Z" fill="currentColor"/></svg>',cart:'<svg viewBox="0 0 24 24" fill="none"><path d="M3 4h2l2.4 11.2A2 2 0 0 0 9.36 17H17.5a2 2 0 0 0 1.95-1.55L21 8H6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><circle cx="10" cy="20.5" r="1.4" fill="currentColor"/><circle cx="17" cy="20.5" r="1.4" fill="currentColor"/></svg>',check:'<svg viewBox="0 0 24 24" fill="none"><path d="m5 12.5 4.5 4.5L19 7.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>',arrow:'<svg viewBox="0 0 24 24" fill="none"><path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',sparkle:'<svg viewBox="0 0 24 24" fill="none"><path d="M12 3v2M12 19v2M3 12h2M19 12h2M6.34 6.34l1.42 1.42M16.24 16.24l1.42 1.42M6.34 17.66l1.42-1.42M16.24 7.76l1.42-1.42" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="12" r="3.5" fill="currentColor"/></svg>'},j="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAPoAAAD6AG1e1JrAAARlElEQVR42pVZB1RU19rdM0NvIzAw1GfDDqj0XgQUo0LQDCrGaKLRREHFFqNEYoqap0lejMZn9FmiJv7E3hUMIkUgiJRhCkOZDgIaxUbT+c+5M4Ok/H/Wu2vdhWvOzD377m9/+/vOJ/B/X6xsZJvsQ7ZVLvZxK5HjlIcL7mdwe8hZVI/KgXDsXtSO3wmx/1bUBm60bAxab9cYtIDb4J/ClfvFmkrHu9gIx9raVo7g8quGWtiXe1pZFbgiOp+H5DODkPCDNRIzzIFsNt0L/+XFJuDMzuKAbRlOO4px1q0CN4bfQN24S2gIuARJ+CFI4vZDkXACj+P3oTd+CdRxCaiLGglFhCsaIyLxLGyoSU+QCQHO5Yr9nYY0jTcbIR2DiJphSCn1gOCWE+JzuBDkWCKjH+jfXwIIOIeQbUFZq8I1ZwmuDRWh2LsRwqBSSCZdQeO0WnRNr4du+kXcF2wy/WXRFJNvVh9cKEwvPt750eUjbZtP7Xma4T/0xGJr02Npjtz2WAtbXRh4ymjrOFWMZWJ9CN6S+GKZ0AuZJe5YnuuIdWdtkZlpiehok78F9yUyLSk4IS66yHDVS44SvyaIoqsgnqHE0xQ1dKnbTM5nhtilH+Q5TiuEfajCeWTcr319vXU6/fX84aPfqq08o+rAC5fDIa5oRMCmr+atf5YKjiyWE6t6DQubpiBDHIYssY/5ptKh2HHNGbmEzexsK3ITkLo/h5yElIaVYU6EK66NyB3ZgtvBTaiaIof8DR0Btsvk5PpR3NmX2E6hCjgFdHDcwu7Bwlu785vDRQRYd3d3LwX48stvj9TC1Fth5hmjgukY7b5DJ1Tk85bS2oc7Iza0zkW6bhpnrSYZWyRx2FY/0Xx3iRcu5rggdx8XO3ZYIzr7T0yyjMyJcMq1GdfH3EdZaCPuTutGy+xHUL4zyeb94xynUCWcA9vhHKpiOxN2HEPVTsPjJE8eP1Ea2NO9ePGibaR/ihTcQBWcQjROXgk1XV1dcuP6o87OKtvgFbuxUryQ88XT17FVNBn/Fgfgp1sj+0F+ScItEHD6weWQhCgiCSFEjksTrozqQEWIAiUzdGidW8ouWOXOnVYMZ//7LAKM5USAEaAclwglzH3V67O+ukv2ffbixUu6/9OTp68KYemjMPWMVsJsnCr78z0SPbs9DLunL+Q1w3JcO3vEtHIsvZ6OA52vY7cwEcfvBuHC9RHIN4DMzraAjoQ6h+juGnZYV+A8j2quAyVBapRN10GTdpF97gM7xxgRZY1tAEZuFYvP/FVb8MNkSpWmWffq0vpFpVWAG6Bl8cNVps4hYo2mhWH35Uv9elDMPCFs/ZRsp+A2eMZIsfLkOvzYmYLD5VNxtigAV696ofgHZxz4wpawaEYAfmlZiO/sCThPNYonalA2RYeGuWXsvDVch2ghAXeP7RwmJ2FVGm+Oa7gSVr6aNxdvrCGbdvb1vWB2Ly69KyOfy0w9olSw8FUtTt9STVnt6WW0qfvlZmkFYbeJ7RqhBn2mY2ArA/Lzi5nIk7+BnwsmI/fyRBTkDEVJjgN+WGONyzhq14hzfKq7NlRGP8Td1B5IlwzlTivqZ24AOHqz+GEq2E5srKispQD6DOF9PGve6loCUM1xIwDsJkrr6uoZdnv7+pjsTklbLYHVeBVZVzDPcglrhkNgG0aTcJ+++y6uFAqQdzEW+ae8UfSjG3K3c1GGI47NuDJEi+JABUqTdWifN9Nq+VGiuQ4mEZx+D47tEqaA9QR1ZOI7lL0HLw2xk0ibFGyHQBnbJVwF6/GaxJnL6PpjkjTMukjUoGE5BDaAvBxr4AtTkPYBHZi8hBSnmnm4cnEmCs+FoPz4MJzfx0MNYU+OvLEq/Bqjgyz1EvvnjaY8Qj8/VEUeoPgjewwAKx/51eu3KDsvDOHrzli7tQZm3iomvJY+8txfSkR0oaeHWe9JX7NVBHNvFcc1QvnHZ5JsVxGgKvxrdzaEV+bi5o8JKDs8DgVHXUG110a0p8/ae/MjbOadBz+gjeX859Cy+WFKkgCasSFv0M0fGDPj/v0H7Tyv+EaykRKDArXeoalC8nHbS8N6a2t7i41HpITlEq4hz/jTSzNEDCKhjhBchSJ3AW7vn4lf9weh+NhgCtCLJEcYSYyUAvbFtRaOETLCnlr/ZobbmBxuESpwRsv3H/yZWsdLg3W82LHrsAQm4+SMMZuNUxz+4XQdw66Bvc/+uU8K07Eqjq0fsalgNYuC1D9X9YrFYDXcIuQ4/MVGyA+/gdt7Y1B0YBQ0yB+twm0S3jbBAsu1+0mFuMeib0TsxHAzIJnEcAjSOHvFU2PWGNnr7up6NGzCDCnsA9VwDFJ7jpsq6Xre1WFcf/78eYfb6CkN1h5Rjdu+PljEdY+Uke9qWX9kkk/2tA9ux/y0o3h8OBUluxNRsc8Hjbjpo0HpZB1Ub47izrwJflAbS6891cA3ZKzFbKxq87a9MsqOgT1dzumrcpga2fNWbdm6VzSAvb5j/3OxHvBULk7/mH7+5NfKWqGNe7gUvGBNP0gnQ5jtg+8hKLEY2m8XoGLndFR860cYLPHrRd1rIhQut3WMJaZMfvh7+o1/tSz7AJFKpRUyO+u977eIxLdlxHhp2Ihxh0qJMbcOMO7OiZFzpbCZ0CSRNhrLXdfV3CIR+U0zNfPf7cEjYR4S1YC87FVo+iwZVRuCoUJhiA6KpJMmZ9b3622g9piyRtiz8NHMX7KJ+t5jo/HmF5TVw9K30dQ9Uk2Ne8F7H1FreWJgjxhzWRPRrGJMWCrVJAO8T287j+Yu2lBn8Ex9tOh+vBAaag32Z2xGa3YSataGohlFocT7Xt/O+X4LnII0f5UcLOcwNQb5N1fcEdbT5Ojt1RsvMeZGarwmbpEacP1kVdVikb5h0NflxJRlItj5a4gvyj/4eBcF2W347YuyimolNXuWS/grKdEuyTG0BZ8s2oFHBGDpqkiSxTcJwM7kLNNvtpHwag0Jonjle+QNbcZr4pOWMmXNsLlOImnQYlCAjJQtFTXuqTOXUQAPjWWvTiRrBte/gcWPoOyoTewDGhRKtdYY+96engee42eIib2oCQGKfh06hrbiw/nfoIuE+PaaGIgZi3ma9KHpv7ZSgHBi6q5RvAq2C1N3m6/lFVF2XhqNOX3tNinMfJSmHjG0csiv3ShuHGDMXUvSPxbC3EdN5cGYMzHvI8fO1g8I88PwqYvE5OVUbD4TZoW+PhOAG9N2oeuTmahcF4160loRgDO2mvznI+JFGhgZpOBoltn5q31CBJSd33SvjFdLjFdKHqiixu0bPltMNzSut7W1yy354RLyDA2NCAPQzFv+7d7jTfQljQkWm/SeCFYTaOumB0jJ4ZEQb33zn+jMmomKdZGEwbKAR7g/9Tj7+ko2L7zZIFgGINPzmXkr/nPoJM3cPoN+nm7deUBIOmYlsRYl7Zx37ztRNtBatu88IKPrxrJGNErLY3P+zdLGARn+wDtitgR2fup+Bum+LkTvxxZnoXXd66hcHYa7KJsggmLyDTS9ZeMQX8nokBemILpQwjFYy/OKExJjVhmf2tPdrfEcnSiCfRBjzO5jpkifPn1OLYQR5/Nnzzo8xk0VU92ZukcpTD1I42rhq/UOfuMOrYrG5yhVWi2H9JMEmFrvuyRaPFLBhkeKUZKRgaYNSahdE4g8cr69ivqEOuiSh3DnnIczNerwZo5bpIIW982f7aHJ8dzAXvexE+dp0VeaUvb067UDsrP72I/nJZR1DtEWi2iQ/Fs5KmiWiDS2DHvdPfry+PXuo8y5hYkSU0lIeAcRow6LL8CDdfNxd8001GaOxwVUjjgNYUQJdInxllu2Equ5p28UwtTmLuESjaa1aaDxBkSliYkuVdRk9cbcyvR8hsbgiX/UXBmLF6T8cMsu2YqNO8SHfzxX19PbQxuLPiIB5mudnY9bXEYnSkgUiEZpeMP0AO1D2pA+az96N85C+dp4CDPG4iSKB5+AzO8HdCRkofxNc8eYGhbteC3Gq95+fzPV3mNjU/BLQZmIZJ3cxDWKhk2zYGkWNe4HRuspLK5QAl6KqbPS6TnlseGl+mjP2NPbZ2xufpuZtrqKJIeazbAXprcXpopENuDGskw0r01h9HdnkRd2k/PvHtwZswXSyF3QxQ+3XXQA/OAHhCVJTa2kfkBH/GTGnBU061QmrqRy2PrJqmsk0gFl71nynFUSYIQiv7Cc+R0BpV97wRD8sqen597st9dX0+MAh/qnsSjow9uOlGmn0JeVSuzlNVSv80PtQk9sIKONbOQP+QR1Ez9AS0yadflsWAaK42a8S7XVYmSnVixTwCGgiWhGBZuJ2tjp71L2Hho76to6qYx6nVfgzCr6O2Om6k903Q/PXbkpHxGQIialUck8g6lWlD2aHCEapgYb2av6IAI16aNxew6fHNiP2m0kB/XlqBq5CLXhi8x1MRZIz7qeX6Q1dC0UQd/yNZ83UsEzxmzp03z9RnHzwJ7v3YwtEpI0Gst/RDeGTlkoW7Bsc/X89zcXTZ+7qtbdZ1oDASZn2QVomKSgwPpLKdVeaDtWCPaje5MAZZmJqF7lhzvvDEaJwAGZ5FS3nJxLluHG4FQ0eCdaNEV7oC1aWtd17JUxt7XZuBN9OBFjJpt4hwhqB1pGS0tbq4VLmJQyweIRH7WeqKJVhOhYC4sJKlISqd5UTEdurPMUpAvxXW5IOxKmXsWDDQtwl7BXuz4M1StHMexdTrRDBjLMk8ih/T2cchbgjlekqdzPndMcY25eG9vU9OQ6BfDJ9u/FjDFT9sx8FAeOnKLJ86yru5uR6LavDkhpL2hkh80n5Y0fqdDfEUpy+DKwFvaKPUZ3JGv9JpVDlLkMsjWpRHdxqM70QflsTxROs8e+JVZ0ZMTxIGMPfzL2GM0rcOXZ1o3gkRkfTKSRY321iZWVLUeH+b5Guplg2g5pHIbE1hJjNg6KdGSs0e4xZqoIgwyW8Tsgf3EzWcskRQf84stxO3MFFOtm487aRNSs8UfV4qGomM5DjsCGObjrh4fRJnwyXfgH97i9o+N1N1sCkktA2tg0RYNTlshxn3eCDIraYD6uZdOne2hFaDd21EdPXGgm7Mo5rqSc/RmQoh8Uret84q80IRxIxsYlXYPw06VQ0KxdMxU16wJwZ5kXbs/g4ygNLZkZZr+aGRKQAjNfAtLDLsfB0bGIAWlnJwm0Htk+iROlS8CorC847rElarVWMWCU8Tgo7q162PqrmTDy+jVmaNloS09N3wDMPqgDg8kkIWPJv3Fv90JIPk8lupuMmlUBuL10BMoWuDC6+8PwaADIJaa+DJMX7a1IuO3shF5Ww8X+8JfFYLIugZNcP2t/3sNNHQ+7CmhBuHatgCTDOCL+cDWpo8QuDIwZZjhwDNEykwPHoHsYOkmCpEU5OP/9arTnpKHiSwEqP42HiPgdZa5MQIZG8Vzk9A8y/3IszBpLmPQnc+nB3DOD6EyZ61wzzDRcOh7xjRGYq5mCxObXkCyesfqy7p2gtC2H4TmphuUR3UTPLISlFlIRWuBMgLlFyjF8SjWC5+RicdYenDqbiabi+ag6MQdF3yWh4mAkKveOJ+wNQ1WKMwMumyTFX8wGBzBovJaY8rHGmks0aT00j4/YO4ORJByLeSTkmapYsyz1dCyTJXM++y0FO6ULkZW3Eqt++gjv792O9H3b8cFPH2PHRTK1uvMeyuTzUCFJReENAa6fTcKNU5NQdoSEdP8YFK74B24JnJiwHlpgYQjr3w7UDV/INvEgHukQdNQOc8lkfk6RG9LKh2EpAbqiwd/i88ZI8+31CdjVPBUH22fgWGcyzpCB5CUy77umTMG1hhScq0zGyZszcP7GZFy5EonrlwJwK2csM70q+MoVV4kR02z95r8Yov8eqIBDQ873JWPZBeS/DoxAV94egqzqUfhE7IMdtYH4WhSK3VUR+K4iEgdLI3GsOArHCyPw861gnC2eiAv53si9NBL5Z4Yg/xDR2reOuJxhx/hcdvb/Ozj/X+wq94A9XinEAAAAAElFTkSuQmCC",P=typeof DOMParser<"u"?new DOMParser:null;function m(p){let e=(H[p]??H.chat).replace("<svg ",'<svg xmlns="http://www.w3.org/2000/svg" '),t=P?.parseFromString(e,"image/svg+xml")?.documentElement;return!t||t.nodeName==="parsererror"?document.createTextNode(""):(t.setAttribute("aria-hidden","true"),t.setAttribute("class","bb-ic"),t)}function F(p){let e=new Uint8Array(16);crypto.getRandomValues(e);let n=btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");return`${p}${n}`}function B(p){return/^\/(?!\/)/.test(p)||/^https?:\/\//i.test(p)}var R=/\*\*([^*\n]+)\*\*|\[([^\]\n]+)\]\(([^)\s]+)\)/g;function D(p,e){let n=0;R.lastIndex=0;for(let t=R.exec(e);t;t=R.exec(e)){if(t.index>n&&p.appendChild(document.createTextNode(e.slice(n,t.index))),t[1]!==void 0){let i=document.createElement("strong");i.textContent=t[1],p.appendChild(i)}else if(B(t[3])){let i=document.createElement("a");i.href=t[3],i.target="_blank",i.rel="noopener noreferrer",i.textContent=t[2],p.appendChild(i)}else p.appendChild(document.createTextNode(t[2]));n=t.index+t[0].length}n<e.length&&p.appendChild(document.createTextNode(e.slice(n)))}function O(p,e){p.replaceChildren();let n=e.split(`
|
|
2
|
-
`),t=null,
|
|
1
|
+
"use strict";(()=>{var D="https://api.brainerce.com",U=new Set(["he","ar"]),L={en:{online:"Online",placeholder:"Ask anything\u2026",error:"Something went wrong \u2014 please try again.",leaveMessage:"Leave a message for the team",yourEmail:"Your email",yourMessage:"Your message",send:"Send",sent:"Thanks! The team will get back to you by email.",close:"Close",expand:"Expand",collapse:"Collapse",searching:"Searching the store\u2026",addToCart:"Add to cart",added:"Added",view:"View",chooseOptions:"View product",results:"From the store",poweredBy:"Powered by Brainerce",addFailed:"I couldn\u2019t add that to the cart \u2014 try the button on the product card."},he:{online:"\u05DE\u05D7\u05D5\u05D1\u05E8",placeholder:"\u05E9\u05D0\u05DC\u05D5 \u05D0\u05D5\u05EA\u05D9 \u05D4\u05DB\u05DC\u2026",error:"\u05DE\u05E9\u05D4\u05D5 \u05D4\u05E9\u05EA\u05D1\u05E9 \u2014 \u05E0\u05E1\u05D5 \u05E9\u05D5\u05D1.",leaveMessage:"\u05D4\u05E9\u05D0\u05D9\u05E8\u05D5 \u05D4\u05D5\u05D3\u05E2\u05D4 \u05DC\u05E6\u05D5\u05D5\u05EA",yourEmail:"\u05D4\u05D0\u05D9\u05DE\u05D9\u05D9\u05DC \u05E9\u05DC\u05DB\u05DD",yourMessage:"\u05D4\u05D4\u05D5\u05D3\u05E2\u05D4 \u05E9\u05DC\u05DB\u05DD",send:"\u05E9\u05DC\u05D9\u05D7\u05D4",sent:"\u05EA\u05D5\u05D3\u05D4! \u05D4\u05E6\u05D5\u05D5\u05EA \u05D9\u05D7\u05D6\u05D5\u05E8 \u05D0\u05DC\u05D9\u05DB\u05DD \u05D1\u05DE\u05D9\u05D9\u05DC.",close:"\u05E1\u05D2\u05D9\u05E8\u05D4",expand:"\u05D4\u05E8\u05D7\u05D1\u05D4",collapse:"\u05DB\u05D9\u05D5\u05D5\u05E5",searching:"\u05DE\u05D7\u05E4\u05E9 \u05D1\u05D7\u05E0\u05D5\u05EA\u2026",addToCart:"\u05D4\u05D5\u05E1\u05E4\u05D4 \u05DC\u05E1\u05DC",added:"\u05E0\u05D5\u05E1\u05E3",view:"\u05E6\u05E4\u05D9\u05D9\u05D4",chooseOptions:"\u05DC\u05E6\u05E4\u05D5\u05EA \u05D1\u05DE\u05D5\u05E6\u05E8",results:"\u05DE\u05EA\u05D5\u05DA \u05D4\u05D7\u05E0\u05D5\u05EA",poweredBy:"\u05DE\u05D5\u05E4\u05E2\u05DC \u05E2\u05DC \u05D9\u05D3\u05D9 Brainerce",addFailed:"\u05DC\u05D0 \u05D4\u05E6\u05DC\u05D7\u05EA\u05D9 \u05DC\u05D4\u05D5\u05E1\u05D9\u05E3 \u05DC\u05E1\u05DC \u2014 \u05E0\u05E1\u05D5 \u05D3\u05E8\u05DA \u05D4\u05DB\u05E4\u05EA\u05D5\u05E8 \u05D1\u05DB\u05E8\u05D8\u05D9\u05E1 \u05D4\u05DE\u05D5\u05E6\u05E8."}},H={chat:'<svg viewBox="0 0 24 24" fill="none"><path d="M12 3C7.03 3 3 6.58 3 11c0 2.04.86 3.9 2.28 5.32-.15 1.23-.62 2.39-1.1 3.21-.13.23.05.52.31.47 1.56-.27 3.07-.93 4.13-1.62A10.6 10.6 0 0 0 12 19c4.97 0 9-3.58 9-8s-4.03-8-9-8Z" fill="currentColor"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',expand:'<svg viewBox="0 0 24 24" fill="none"><path d="M14 4h6v6M10 20H4v-6M20 4l-7 7M4 20l7-7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',collapse:'<svg viewBox="0 0 24 24" fill="none"><path d="M20 10h-6V4M4 14h6v6M20 4l-6 6M4 20l6-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',mail:'<svg viewBox="0 0 24 24" fill="none"><rect x="3.5" y="5.5" width="17" height="13" rx="2.5" stroke="currentColor" stroke-width="1.7"/><path d="m4.5 7.5 7.5 5.5 7.5-5.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none"><path d="M4.4 11.2 19 4.6c.7-.3 1.4.4 1.1 1.1l-6.6 14.6c-.3.7-1.3.6-1.5-.1l-1.7-5.4a1 1 0 0 0-.6-.6l-5.4-1.7c-.7-.2-.8-1.2-.1-1.5Z" fill="currentColor"/></svg>',cart:'<svg viewBox="0 0 24 24" fill="none"><path d="M3 4h2l2.4 11.2A2 2 0 0 0 9.36 17H17.5a2 2 0 0 0 1.95-1.55L21 8H6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><circle cx="10" cy="20.5" r="1.4" fill="currentColor"/><circle cx="17" cy="20.5" r="1.4" fill="currentColor"/></svg>',check:'<svg viewBox="0 0 24 24" fill="none"><path d="m5 12.5 4.5 4.5L19 7.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>',arrow:'<svg viewBox="0 0 24 24" fill="none"><path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',sparkle:'<svg viewBox="0 0 24 24" fill="none"><path d="M12 3v2M12 19v2M3 12h2M19 12h2M6.34 6.34l1.42 1.42M16.24 16.24l1.42 1.42M6.34 17.66l1.42-1.42M16.24 7.76l1.42-1.42" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="12" r="3.5" fill="currentColor"/></svg>'},j="data:image/png;base64,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",z=typeof DOMParser<"u"?new DOMParser:null;function m(p){let e=(H[p]??H.chat).replace("<svg ",'<svg xmlns="http://www.w3.org/2000/svg" '),t=z?.parseFromString(e,"image/svg+xml")?.documentElement;return!t||t.nodeName==="parsererror"?document.createTextNode(""):(t.setAttribute("aria-hidden","true"),t.setAttribute("class","bb-ic"),t)}function F(p){let e=new Uint8Array(16);crypto.getRandomValues(e);let n=btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");return`${p}${n}`}function T(p){return/^\/(?!\/)/.test(p)||/^https?:\/\//i.test(p)}var R=/\*\*([^*\n]+)\*\*|\[([^\]\n]+)\]\(([^)\s]+)\)/g;function P(p,e){let n=0;R.lastIndex=0;for(let t=R.exec(e);t;t=R.exec(e)){if(t.index>n&&p.appendChild(document.createTextNode(e.slice(n,t.index))),t[1]!==void 0){let s=document.createElement("strong");s.textContent=t[1],p.appendChild(s)}else if(T(t[3])){let s=document.createElement("a");s.href=t[3],s.target="_blank",s.rel="noopener noreferrer",s.textContent=t[2],p.appendChild(s)}else p.appendChild(document.createTextNode(t[2]));n=t.index+t[0].length}n<e.length&&p.appendChild(document.createTextNode(e.slice(n)))}function O(p,e){p.replaceChildren();let n=e.split(`
|
|
2
|
+
`),t=null,s=null;for(let o of n){let r=/^\s*[-•*]\s+(.*)$/.exec(o);if(r){s=null,t||(t=document.createElement("ul"),p.appendChild(t));let d=document.createElement("li");P(d,r[1]),t.appendChild(d);continue}if(t=null,!o.trim()){s=null;continue}s?s.appendChild(document.createElement("br")):(s=document.createElement("p"),p.appendChild(s)),P(s,o)}}var A=class p{constructor(e){this.botSessionToken=null;this.botSessionFetchedAt=0;this.settings={enabled:!1};this.locale="en";this.sessionId=null;this.conversationId=null;this.busy=!1;this.opened=!1;this.expanded=!1;this.destroyed=!1;this.pendingText="";this.cardsRow=null;this.cardIds=new Set;this.prevBodyOverflow=null;this.connectionId=e.connectionId,this.baseUrl=(e.baseUrl||D).replace(/\/$/,""),this.storageKey=`brainerce-bot:${this.connectionId}`,this.onAddToCart=e.onAddToCart,this.customerSessionProxyPath=e.customerSessionProxyPath?.replace(/\/$/,"")}static async mount(e){if(!e?.connectionId)return console.warn("[BrainerceBot] connectionId is required"),null;let n=new p(e);return await n.boot(e.target??document.body)?n:null}destroy(){this.destroyed=!0,this.prevBodyOverflow!==null&&(document.body.style.overflow=this.prevBodyOverflow,this.prevBodyOverflow=null),this.host?.remove()}async boot(e){try{let n=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/settings`);if(!n.ok)return!1;this.settings=await n.json()}catch{return!1}return this.settings.enabled?(this.locale=this.settings.languages?.[0]??"en",this.restoreIds(),this.render(e),this.settings.displayMode==="auto_open"&&setTimeout(()=>!this.destroyed&&this.open(),3e3),!0):!1}t(e){return(L[this.locale]??L.en)[e]??L.en[e]??e}restoreIds(){try{let e=localStorage.getItem(this.storageKey);if(e){let n=JSON.parse(e);this.sessionId=n.sessionId??null,this.conversationId=n.conversationId??null}}catch{}}persistIds(){try{localStorage.setItem(this.storageKey,JSON.stringify({sessionId:this.sessionId,conversationId:this.conversationId}))}catch{}}css(e,n,t){let s=this.settings.bubbleShape==="square",o=s?"14px":"22px",r=s?"8px":"15px",d="5px",a=this.settings.displayMode??"floating",l=a==="side_rail",i=a==="full_screen";return`
|
|
3
3
|
:host { all: initial; }
|
|
4
4
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0;
|
|
5
5
|
font-family: -apple-system, "SF Pro Text", "Segoe UI Variable Text", "Segoe UI", system-ui, "Helvetica Neue", sans-serif;
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
.bb-launcher {
|
|
34
34
|
width: 58px; height: 58px; position: relative; display: flex; align-items: center;
|
|
35
35
|
justify-content: center; color: #fff; background: ${e};
|
|
36
|
-
border-radius: ${
|
|
36
|
+
border-radius: ${s?"16px":"999px"}; overflow: hidden;
|
|
37
37
|
box-shadow: 0 6px 16px -4px color-mix(in srgb, ${e} 55%, rgba(10,12,30,.4)), 0 2px 6px rgba(10,12,30,.18);
|
|
38
38
|
transition: transform .22s cubic-bezier(.34,1.56,.64,1), box-shadow .22s ease;
|
|
39
39
|
}
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
width: auto; max-width: none; height: auto; }
|
|
69
69
|
${l?`.bb-window { bottom: 0; top: auto; ${t}: 0; height: calc(100vh - 98px); border-end-start-radius: ${o}; }
|
|
70
70
|
.bb.expanded .bb-window { width: min(560px, calc(100vw - 40px)); }`:""}
|
|
71
|
-
${
|
|
71
|
+
${i?`.bb.open .bb-window { position: fixed;
|
|
72
72
|
inset: min(6vh, 60px) max(24px, calc((100vw - 1680px) / 2));
|
|
73
73
|
width: auto; max-width: none; height: auto; }
|
|
74
74
|
.bb.open .bb-launcher { opacity: 0; pointer-events: none; }`:""}
|
|
@@ -245,6 +245,6 @@
|
|
|
245
245
|
transition: color .12s ease; }
|
|
246
246
|
.bb-foot a:hover { color: #6b7280; }
|
|
247
247
|
.bb-foot-mark { width: 14px; height: 14px; border-radius: 3px; display: block; }
|
|
248
|
-
`}render(e){let n=this.settings.accentColor||"#6366F1",t=
|
|
248
|
+
`}render(e){let n=this.settings.accentColor||"#6366F1",t=U.has(this.locale)?"rtl":"ltr",s=this.settings.position==="start"?"left":"right",o=t==="rtl"?s==="left"?"right":"left":s,r=this.settings.displayName||"Assistant";this.host=document.createElement("div"),this.host.setAttribute("data-brainerce-bot",this.connectionId),this.root=this.host.attachShadow({mode:"open"});let d=document.createElement("style");d.textContent=this.css(n,t,o),this.root.appendChild(d);let a=document.createElement("div");a.className="bb",this.root.appendChild(a);let l=document.createElement("div");l.className="bb-scrim",l.addEventListener("click",()=>{this.expanded?this.toggleExpand():this.close()}),a.appendChild(l),this.windowEl=document.createElement("div"),this.windowEl.className="bb-window",a.appendChild(this.windowEl);let i=document.createElement("div");i.className="bb-header";let b=document.createElement("span");if(b.className="bb-avatar",this.settings.avatarUrl&&T(this.settings.avatarUrl)){let g=document.createElement("img");g.src=this.settings.avatarUrl,g.alt="",b.appendChild(g)}else b.appendChild(m("sparkle"));let u=document.createElement("span");u.className="bb-head-main";let h=document.createElement("span");h.className="bb-name",h.textContent=r;let w=document.createElement("span");w.className="bb-status",w.textContent=this.t("online"),u.appendChild(h),u.appendChild(w);let v=document.createElement("span");v.className="bb-actions",(this.settings.displayMode??"floating")!=="full_screen"&&this.settings.allowExpand!==!1&&(this.expandBtn=this.iconButton("expand",this.t("expand"),()=>this.toggleExpand()),v.appendChild(this.expandBtn)),v.appendChild(this.iconButton("mail",this.t("leaveMessage"),()=>this.toggleEscalation())),v.appendChild(this.iconButton("close",this.t("close"),()=>this.close())),i.appendChild(b),i.appendChild(u),i.appendChild(v),this.windowEl.appendChild(i),this.messagesEl=document.createElement("div"),this.messagesEl.className="bb-msgs",this.windowEl.appendChild(this.messagesEl),this.chipsEl=document.createElement("div"),this.chipsEl.className="bb-chips";for(let g of this.settings.starterQuestions??[]){let M=document.createElement("button");M.className="bb-chip",M.textContent=g,M.addEventListener("click",()=>this.send(g)),this.chipsEl.appendChild(M)}this.windowEl.appendChild(this.chipsEl);let c=document.createElement("div");c.className="bb-esc";let k=document.createElement("span");k.className="bb-esc-title",k.textContent=this.t("leaveMessage");let E=document.createElement("input");E.type="email",E.name="email",E.placeholder=this.t("yourEmail");let C=document.createElement("textarea");C.name="message",C.rows=2,C.placeholder=this.t("yourMessage");let I=document.createElement("button");I.type="button",I.className="bb-esc-send",I.textContent=this.t("send"),I.addEventListener("click",()=>this.submitEscalation(c)),c.appendChild(k),c.appendChild(E),c.appendChild(C),c.appendChild(I),this.windowEl.appendChild(c);let f=document.createElement("div");f.className="bb-composer",this.inputEl=document.createElement("textarea"),this.inputEl.className="bb-input",this.inputEl.rows=1,this.inputEl.maxLength=4e3,this.inputEl.placeholder=this.t("placeholder"),this.inputEl.addEventListener("keydown",g=>{g.key==="Enter"&&!g.shiftKey&&(g.preventDefault(),this.send(this.inputEl?.value??""))}),this.inputEl.addEventListener("input",()=>this.syncSendState()),this.sendBtn=document.createElement("button"),this.sendBtn.className="bb-send",this.sendBtn.disabled=!0,this.sendBtn.setAttribute("aria-label",this.t("send")),this.sendBtn.appendChild(m("send")),this.sendBtn.addEventListener("click",()=>this.send(this.inputEl?.value??"")),f.appendChild(this.inputEl),f.appendChild(this.sendBtn),this.windowEl.appendChild(f);let B=document.createElement("div");B.className="bb-foot";let x=document.createElement("a");x.href="https://brainerce.com",x.target="_blank",x.rel="noopener noreferrer";let N=document.createElement("img");if(N.src=j,N.alt="",N.className="bb-foot-mark",x.appendChild(N),x.appendChild(document.createTextNode(this.t("poweredBy"))),B.appendChild(x),this.windowEl.appendChild(B),this.launcherEl=document.createElement("button"),this.launcherEl.className="bb-launcher",this.launcherEl.setAttribute("aria-label",r),this.settings.avatarUrl&&T(this.settings.avatarUrl)){let g=document.createElement("img");g.src=this.settings.avatarUrl,g.alt="",this.launcherEl.appendChild(g)}else{let g=m("chat");g.classList.add("bb-l-chat"),this.launcherEl.appendChild(g)}let S=m("close");S.classList.add("bb-l-close"),this.launcherEl.appendChild(S),this.launcherEl.addEventListener("click",()=>this.opened?this.close():this.open()),a.appendChild(this.launcherEl),e.appendChild(this.host)}iconButton(e,n,t){let s=document.createElement("button");return s.className="bb-iconbtn",s.title=n,s.setAttribute("aria-label",n),s.appendChild(m(e)),s.addEventListener("click",t),s}syncSendState(){this.sendBtn&&(this.sendBtn.disabled=!(this.inputEl?.value??"").trim()||this.busy)}open(){if(!this.windowEl||this.opened)return;this.opened=!0;let e=this.root?.querySelector(".bb");e?.classList.add("open");let n=(this.settings.displayMode??"floating")==="full_screen"||window.innerWidth<=520;e?.classList.toggle("big",n||this.expanded),this.syncBodyScroll(),this.messagesEl&&this.messagesEl.childElementCount===0&&this.primeThread(),this.inputEl?.focus()}close(){this.opened=!1;let e=this.root?.querySelector(".bb");e?.classList.remove("open"),this.expanded||e?.classList.remove("big"),this.syncBodyScroll()}syncBodyScroll(){let e=this.opened&&!!this.root?.querySelector(".bb")?.classList.contains("big");e&&this.prevBodyOverflow===null?(this.prevBodyOverflow=document.body.style.overflow||"",document.body.style.overflow="hidden"):!e&&this.prevBodyOverflow!==null&&(document.body.style.overflow=this.prevBodyOverflow,this.prevBodyOverflow=null)}toggleExpand(){if(this.expanded=!this.expanded,this.root?.querySelector(".bb")?.classList.toggle("expanded",this.expanded),this.root?.querySelector(".bb")?.classList.toggle("big",this.expanded),this.syncBodyScroll(),this.expandBtn){this.expandBtn.replaceChildren(m(this.expanded?"collapse":"expand"));let e=this.t(this.expanded?"collapse":"expand");this.expandBtn.title=e,this.expandBtn.setAttribute("aria-label",e)}}async primeThread(){if(this.conversationId&&this.sessionId)try{let e=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/conversations/${encodeURIComponent(this.conversationId)}?limit=50`,{headers:{"X-Bot-Session":this.sessionId}});if(e.ok){let n=await e.json();for(let t of n.data){let s=this.appendMessage(t.role==="assistant"?"bot":"user","");O(s,t.content)}if(n.data.length>0){this.chipsEl?.remove();return}}else this.conversationId=null,this.sessionId=null,this.persistIds()}catch{}if(this.settings.greeting){let e=this.appendMessage("bot","");O(e,this.settings.greeting)}}async refreshCustomerSession(){if(!this.customerSessionProxyPath)return;let e=240*1e3;if(!(this.botSessionToken&&Date.now()-this.botSessionFetchedAt<e))try{let n=await fetch(`${this.customerSessionProxyPath}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/customer-session`,{credentials:"include"});if(!n.ok)return;let t=await n.json();this.botSessionToken=t.loggedIn&&t.botSessionToken?t.botSessionToken:null,this.botSessionFetchedAt=Date.now()}catch{}}async send(e){let n=e.trim();if(!n||this.busy)return;this.busy=!0,this.inputEl&&(this.inputEl.value=""),this.syncSendState(),this.chipsEl?.remove(),this.appendMessage("user",n);let t=this.appendTyping();this.pendingText="",this.cardsRow=null,this.cardIds=new Set;let s=null;try{await this.refreshCustomerSession();let o=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:n,turnId:F("trn_"),...this.conversationId?{conversationId:this.conversationId}:{},...this.sessionId?{anonymousSessionId:this.sessionId}:{},locale:this.locale,...this.botSessionToken?{botSessionToken:this.botSessionToken}:{}})});if(!o.ok||!o.body)throw new Error(`chat failed (${o.status})`);let r=o.body.getReader(),d=new TextDecoder,a="";for(;;){let{value:l,done:i}=await r.read();if(i)break;a+=d.decode(l,{stream:!0});let b;for(;(b=a.indexOf(`
|
|
249
249
|
|
|
250
|
-
`))>=0;){let u=a.slice(0,b);if(a=a.slice(b+2),!u.startsWith("data: "))continue;let h;try{h=JSON.parse(u.slice(6))}catch{continue}
|
|
250
|
+
`))>=0;){let u=a.slice(0,b);if(a=a.slice(b+2),!u.startsWith("data: "))continue;let h;try{h=JSON.parse(u.slice(6))}catch{continue}s=this.handleFrame(h,t,s)}}s&&this.pendingText&&O(s,this.pendingText)}catch{this.appendMessage("err",this.t("error"))}finally{t.remove(),this.busy=!1,this.syncSendState()}}handleFrame(e,n,t){switch(e.type){case"connected":return this.conversationId=e.conversationId||this.conversationId,this.sessionId=e.anonymousSessionId||this.sessionId,this.persistIds(),t;case"token":return t||(n.remove(),t=this.appendMessage("bot","")),this.pendingText+=e.text,t.textContent=this.pendingText,this.scrollDown(),t;case"tool":return n.classList.toggle("searching",e.status==="running"),t;case"card":return this.appendCard(e.card),t;case"action":return this.handleAction(e),t;case"error":return this.appendMessage("err",e.message||this.t("error")),t;case"escalate_offer":return this.openEscalation(),t;case"done":default:return t}}appendCard(e){if(!this.messagesEl||this.cardIds.has(e.productId))return;if(this.cardIds.add(e.productId),!this.cardsRow){let i=document.createElement("div");i.className="bb-shelf";let b=document.createElement("div");b.className="bb-shelf-cap",b.textContent=this.t("results"),this.cardsRow=document.createElement("div"),this.cardsRow.className="bb-cards",i.appendChild(b),i.appendChild(this.cardsRow),this.messagesEl.appendChild(i)}let n=T(e.url)?e.url:null,t=document.createElement("div");t.className="bb-card";let s=()=>{this.beacon(e.botRef),n&&(window.location.href=n)},o=document.createElement("span");if(o.className="bb-card-img",e.imageUrl&&T(e.imageUrl)){let i=document.createElement("img");i.src=e.imageUrl,i.alt="",i.loading="lazy",o.appendChild(i)}else o.appendChild(m("cart"));o.addEventListener("click",s),t.appendChild(o);let r=document.createElement("span");r.className="bb-card-body";let d=document.createElement("span");d.className="bb-card-title",d.textContent=e.title,d.addEventListener("click",s);let a=document.createElement("span");a.className="bb-card-price",a.textContent=e.price.formatted,r.appendChild(d),r.appendChild(a);let l=document.createElement("span");if(l.className="bb-card-cta",e.requiresOptions){let i=document.createElement("button");i.className="bb-btn bb-btn-add",i.setAttribute("aria-label",this.t("addToCart")),i.appendChild(m("cart")),e.variants?.length?i.addEventListener("click",()=>this.togglePicker(r,e,o)):i.addEventListener("click",s),l.appendChild(i)}else{let i=document.createElement("button");i.className="bb-btn bb-btn-add bb-sq",i.setAttribute("aria-label",this.t("addToCart")),i.appendChild(m("cart")),i.addEventListener("click",()=>void this.addToCart(e,i));let b=document.createElement("button");b.className="bb-btn bb-btn-ghost bb-wide",b.appendChild(document.createTextNode(this.t("view"))),b.addEventListener("click",s),l.appendChild(b),l.appendChild(i)}r.appendChild(l),t.appendChild(r),this.cardsRow.appendChild(t),this.scrollDown()}togglePicker(e,n,t){let s=e.parentElement,o=e.querySelector(".bb-pick");if(o){o.remove(),s?.classList.remove("picking");return}s?.classList.add("picking");let r=n.variants??[],d=[];for(let u of r)for(let h of Object.keys(u.attributes))d.includes(h)||d.push(h);let a={},l=document.createElement("span");l.className="bb-pick";let i=()=>r.find(u=>d.every(h=>a[h]&&u.attributes[h]===a[h]))??null,b=()=>{l.replaceChildren();for(let c of d){let k=document.createElement("span");k.className="bb-pick-key",k.textContent=c;let E=document.createElement("span");E.className="bb-pick-vals";let C=new Set;for(let I of r){let f=I.attributes[c];if(!f||C.has(f))continue;C.add(f);let B=r.some(N=>N.attributes[c]===f&&d.every(S=>S===c||!a[S]||N.attributes[S]===a[S])),x=document.createElement("button");x.className=`bb-chipv${a[c]===f?" sel":""}${B?"":" off"}`,x.textContent=f,x.addEventListener("click",()=>{a[c]===f?delete a[c]:a[c]=f,b()}),E.appendChild(x)}l.appendChild(k),l.appendChild(E)}let u=document.createElement("button");u.className="bb-pick-close",u.setAttribute("aria-label",this.t("close")),u.appendChild(m("close")),u.addEventListener("click",()=>{l.remove(),s?.classList.remove("picking")}),l.appendChild(u);let h=i(),w=document.createElement("span");w.className="bb-pick-foot";let v=document.createElement("span");v.className="bb-pick-price",v.textContent=h?h.price.formatted:"";let y=document.createElement("button");if(y.className="bb-btn bb-btn-add bb-sq",y.setAttribute("aria-label",this.t("addToCart")),y.appendChild(m("cart")),h||(y.disabled=!0),y.addEventListener("click",()=>{let c=i();c&&this.addToCart(n,y,c.id)}),w.appendChild(v),w.appendChild(y),l.appendChild(w),h?.imageUrl&&T(h.imageUrl)){let c=t.querySelector("img");c&&(c.src=h.imageUrl)}this.scrollDown()};b(),e.appendChild(l),this.scrollDown()}async addToCart(e,n,t){this.beacon(e.botRef),n.dataset.state="busy",await this.dispatchAdd(e.productId,t??null)?(n.dataset.state="done",n.replaceChildren(m("check"),document.createTextNode(this.t("added"))),setTimeout(()=>{!this.destroyed&&n.isConnected&&(delete n.dataset.state,n.replaceChildren(m("cart"),document.createTextNode(this.t("addToCart"))))},2200)):(delete n.dataset.state,T(e.url)&&(window.location.href=e.url))}async dispatchAdd(e,n){try{if(this.onAddToCart)return await this.onAddToCart({productId:e,variantId:n,quantity:1})!==!1;let t=new CustomEvent("brainerce:bot:add-to-cart",{detail:{productId:e,variantId:n,quantity:1,connectionId:this.connectionId},cancelable:!0,bubbles:!0,composed:!0});return!window.dispatchEvent(t)}catch{return!1}}async handleAction(e){if(e.action!=="add_to_cart")return;e.botRef&&this.beacon(e.botRef),await this.dispatchAdd(e.productId,e.variantId)||this.appendMessage("err",this.t("addFailed"))}beacon(e){try{navigator.sendBeacon?.(`${this.baseUrl}/api/storefront-bot/attribution/click`,new Blob([JSON.stringify({botRef:e})],{type:"application/json"}))}catch{}}toggleEscalation(){this.root?.querySelector(".bb-esc")?.classList.toggle("open")}openEscalation(){this.root?.querySelector(".bb-esc")?.classList.add("open")}async submitEscalation(e){let n=e.querySelector('input[name="email"]')?.value.trim(),t=e.querySelector('textarea[name="message"]')?.value.trim();if(!(!n||!t||!this.conversationId||!this.sessionId))try{if((await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/escalate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:n,message:t,conversationId:this.conversationId,anonymousSessionId:this.sessionId,locale:this.locale})})).ok){let o=document.createElement("span");o.className="bb-esc-note",o.appendChild(m("check")),o.appendChild(document.createTextNode(this.t("sent"))),e.replaceChildren(o)}}catch{}}appendMessage(e,n){let t=document.createElement("div");return t.className=`bb-msg ${e}`,t.setAttribute("dir","auto"),t.textContent=n,this.messagesEl?.appendChild(t),this.scrollDown(),t}appendTyping(){let e=document.createElement("div");e.className="bb-typing";let n=document.createElement("span");n.className="bb-dots";for(let s=0;s<3;s++)n.appendChild(document.createElement("i"));let t=document.createElement("span");return t.className="bb-tool",t.textContent=this.t("searching"),e.appendChild(n),e.appendChild(t),this.messagesEl?.appendChild(e),this.scrollDown(),e}scrollDown(){this.messagesEl&&(this.messagesEl.scrollTop=this.messagesEl.scrollHeight)}};(()=>{let p=document.currentScript,e=p?.dataset.connectionId;if(!e){console.warn("[BrainerceBot] missing data-connection-id on the bot.js script tag");return}let n=p?.dataset.apiBase||void 0,t=()=>void A.mount({connectionId:e,baseUrl:n});document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()})();})();
|
package/dist/bot/index.d.mts
CHANGED
|
@@ -33,12 +33,29 @@ interface BrainerceBotOptions {
|
|
|
33
33
|
variantId?: string | null;
|
|
34
34
|
quantity: number;
|
|
35
35
|
}) => boolean | Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Part 2 (order-lookup login gating) — a SAME-ORIGIN path (e.g. `/api/store`, the
|
|
38
|
+
* Next.js template's own BFF proxy) that forwards to the backend and attaches the
|
|
39
|
+
* shopper's customer-session cookie as a Bearer header. When set, the widget calls
|
|
40
|
+
* `${customerSessionProxyPath}/api/storefront-bot/:connectionId/customer-session`
|
|
41
|
+
* (through THIS path, not `baseUrl` — a cross-origin request to `baseUrl` would
|
|
42
|
+
* never carry the httpOnly session cookie) to learn login state and obtain a
|
|
43
|
+
* short-lived token it attaches to its normal direct-to-backend /chat call, so
|
|
44
|
+
* streaming is completely unaffected. Omit entirely on any site without such a
|
|
45
|
+
* proxy — checkOrderStatus then simply always answers as a guest, no error.
|
|
46
|
+
*/
|
|
47
|
+
customerSessionProxyPath?: string;
|
|
36
48
|
}
|
|
37
49
|
declare class BrainerceBot {
|
|
38
50
|
private readonly connectionId;
|
|
39
51
|
private readonly baseUrl;
|
|
40
52
|
private readonly storageKey;
|
|
41
53
|
private readonly onAddToCart?;
|
|
54
|
+
private readonly customerSessionProxyPath?;
|
|
55
|
+
/** Part 2 (order-lookup login gating) — short-lived, in-memory only (never
|
|
56
|
+
* persisted): refreshed opportunistically before a chat send when stale. */
|
|
57
|
+
private botSessionToken;
|
|
58
|
+
private botSessionFetchedAt;
|
|
42
59
|
private host?;
|
|
43
60
|
private root?;
|
|
44
61
|
private windowEl?;
|
|
@@ -83,6 +100,15 @@ declare class BrainerceBot {
|
|
|
83
100
|
private toggleExpand;
|
|
84
101
|
/** First open: restore the server thread, or show the greeting. */
|
|
85
102
|
private primeThread;
|
|
103
|
+
/**
|
|
104
|
+
* Part 2 (order-lookup login gating) — refresh the short-lived bot-session token
|
|
105
|
+
* if this site opted in (`customerSessionProxyPath` set) and the current one is
|
|
106
|
+
* stale or absent. Comfortably under the backend's 5-minute expiry so a token
|
|
107
|
+
* fetched here is never expired by the time /chat reads it. Fails silently on any
|
|
108
|
+
* error (offline, no proxy configured server-side, etc.) — a guest must always be
|
|
109
|
+
* able to chat regardless of this call's outcome.
|
|
110
|
+
*/
|
|
111
|
+
private refreshCustomerSession;
|
|
86
112
|
private send;
|
|
87
113
|
private handleFrame;
|
|
88
114
|
private appendCard;
|