brainerce 1.46.0 → 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 CHANGED
@@ -46,7 +46,8 @@ Every Brainerce storefront must include **all mandatory features** below. Featur
46
46
  | Forgot / reset password | `client.forgotPassword()`, `client.resetPassword()` | ✅ |
47
47
  | OAuth sign-in buttons + callback handler | `client.getAvailableOAuthProviders()` | ✅ |
48
48
  | Account area (profile + order history) | `client.getMyProfile()`, `client.getMyOrders()` | ✅ |
49
- | Loyalty & rewards (points balance + tiers + redeem) | `client.getLoyaltyStatus()`, `client.getAvailableRewards()`, `client.redeemLoyaltyReward(id)`, `client.reportSocialShare()` | conditional |
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 |
50
51
  | Global header: cart count + search autocomplete | `client.getCart()`, `client.getSearchSuggestions(query)` | ✅ |
51
52
  | Discount banners + product badges | `client.getDiscountBanners()`, `client.getProductDiscountBadge(productId)` | ✅ |
52
53
  | Product reviews on PDP + JSON-LD aggregateRating | `client.listProductReviews(id)`, `client.submitProductReview(id, …)` | ✅ |
@@ -266,6 +267,12 @@ The SDK exports these utility functions for common UI tasks:
266
267
  | `isCouponApplicableToProduct(coupon, product)` | Check if coupon applies | `isCouponApplicableToProduct(coupon, product)` |
267
268
  | `isAllowedPaymentUrl(url, options?)` | Validate a payment URL host | `isAllowedPaymentUrl(intent.clientSecret)` → `true` |
268
269
  | `safePaymentRedirect(url, options?)` | Validate then `window.location.href` | `safePaymentRedirect(intent.clientSecret)` |
270
+ | `buildProductJsonLd(product, opts)` | schema.org Product JSON-LD (PDPs only) | See SEO section |
271
+ | `buildArticleJsonLd(post, opts)` | schema.org Article JSON-LD for blog posts | See SEO section |
272
+ | `buildOrganizationJsonLd(store, opts)` | schema.org Organization for the homepage | See SEO section |
273
+ | `buildBreadcrumbJsonLd(items)` | schema.org BreadcrumbList | See SEO section |
274
+ | `jsonLdScriptProps(data)` | XSS-safe `<script type="application/ld+json">` props | `<script {...jsonLdScriptProps(data)} />` |
275
+ | `getBlogSitemapEntries(client, opts)` | Paginate published posts into sitemap entries | See SEO section |
269
276
 
270
277
  ```typescript
271
278
  import {
@@ -451,7 +458,55 @@ const safeHtml = DOMPurify.sanitize(post.content);
451
458
  return <div dangerouslySetInnerHTML={{ __html: safeHtml }} className="prose" />;
452
459
  ```
453
460
 
454
- **Scheduling**: A post is visible once `status === 'PUBLISHED'` and `publishedAt <= now()`. Set a future `publishedAt` when publishing to schedule — no cron needed.
461
+ **Scheduling**: A post is visible once `status === 'PUBLISHED'` and `publishedAt <= now()`. Set a future `publishedAt` when publishing to schedule.
462
+
463
+ **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.
464
+
465
+ ### SEO — JSON-LD builders, sitemap helper, IndexNow key, llms.txt
466
+
467
+ 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). Prefer them over hand-rolled JSON-LD:
468
+
469
+ ```tsx
470
+ import {
471
+ buildProductJsonLd, // single-product pages ONLY (never listing pages)
472
+ buildArticleJsonLd, // blog post pages
473
+ buildOrganizationJsonLd, // homepage
474
+ buildBreadcrumbJsonLd,
475
+ jsonLdScriptProps, // XSS-safe <script> props
476
+ } from 'brainerce';
477
+
478
+ // Blog article page
479
+ <script {...jsonLdScriptProps(buildArticleJsonLd(post, {
480
+ siteUrl: process.env.NEXT_PUBLIC_SITE_URL!,
481
+ path: `/blog/${post.slug}`,
482
+ organizationName: storeInfo.name,
483
+ }))} />
484
+
485
+ // Product page
486
+ <script {...jsonLdScriptProps(buildProductJsonLd(product, {
487
+ siteUrl: process.env.NEXT_PUBLIC_SITE_URL!,
488
+ path: `/products/${product.slug}`,
489
+ currency: storeInfo.currency,
490
+ }))} />
491
+ ```
492
+
493
+ **Blog entries in sitemap.xml** (required — autopilot articles missing from the sitemap never get crawled):
494
+
495
+ ```ts
496
+ // app/sitemap.ts
497
+ import { getBlogSitemapEntries } from 'brainerce';
498
+
499
+ const blogPages = await getBlogSitemapEntries(client, {
500
+ siteUrl: baseUrl,
501
+ locales: supportedLocales, // optional (multi-locale stores)
502
+ defaultLocale,
503
+ }).catch(() => []);
504
+ return [...staticPages, ...productPages, ...blogPages];
505
+ ```
506
+
507
+ **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).
508
+
509
+ **llms.txt** (recommended): a plain-text site summary (store name, key pages, recent article links) for AI answer engines at `GET /llms.txt`.
455
510
 
456
511
  ---
457
512
 
@@ -892,10 +947,19 @@ const suggestions = await client.getAddressSuggestions('Rothschild 1', sessionTo
892
947
  const { address, inZone } = await client.getAddressDetails(suggestions[0].placeId, sessionToken);
893
948
  // address: { line1, city, region, postalCode, country, lat, lng, formattedAddress }
894
949
 
950
+ // address.region is Google's own administrative-area code — usually (not
951
+ // guaranteed) the same ISO 3166-2 subdivision code this store's own region
952
+ // lists use. Validate against destinations.regions before trusting it;
953
+ // never assign a code the region <select> wouldn't recognize.
954
+ const destinations = await client.getShippingDestinations();
955
+ const validRegions = destinations.regions[address.country] ?? [];
956
+ const region = validRegions.some((r) => r.code === address.region) ? address.region : '';
957
+
895
958
  await client.setShippingAddress(checkout.id, {
896
959
  firstName: 'John',
897
960
  lastName: 'Doe',
898
961
  ...address,
962
+ region,
899
963
  });
900
964
 
901
965
  if (!inZone) {
@@ -1138,11 +1202,45 @@ const { categories } = await client.getCategories();
1138
1202
  interface CategoryNode {
1139
1203
  id: string;
1140
1204
  name: string;
1205
+ slug?: string | null; // link a category page as `/category/${slug}`
1141
1206
  parentId?: string | null;
1207
+ image?: string | null;
1142
1208
  children: CategoryNode[]; // Recursive — can nest to any depth
1143
1209
  }
1144
1210
  ```
1145
1211
 
1212
+ #### Get Category by Slug (Category Page)
1213
+
1214
+ 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] })`.
1215
+
1216
+ ```typescript
1217
+ // app/category/[slug]/page.tsx
1218
+ const category = await client.getCategoryBySlug(params.slug).catch(() => null);
1219
+ if (!category) notFound();
1220
+
1221
+ const { data: products } = await client.getProducts({ categories: [category.id] });
1222
+
1223
+ // SEO: CollectionPage + BreadcrumbList JSON-LD (never Product markup on a listing)
1224
+ const jsonLd = buildCollectionPageJsonLd(category, { siteUrl, path: `/category/${category.slug}` });
1225
+ ```
1226
+
1227
+ **Type:**
1228
+
1229
+ ```typescript
1230
+ interface CategoryDetail {
1231
+ id: string;
1232
+ name: string;
1233
+ slug: string | null;
1234
+ description: string | null; // long-form HTML — render below the product grid
1235
+ metaDescription: string | null; // <meta name="description">
1236
+ image: string | null;
1237
+ breadcrumb: Array<{ name: string; slug: string | null }>; // root → parent
1238
+ productCount: number;
1239
+ }
1240
+ ```
1241
+
1242
+ > Vibe-coded mode only (like `getCategories`). Category slugs come from the dashboard SEO hub, which also writes the description + meta.
1243
+
1146
1244
  #### Filter Products by Category
1147
1245
 
1148
1246
  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.
@@ -1416,7 +1514,9 @@ function SearchInput() {
1416
1514
  </a>
1417
1515
  ))}
1418
1516
  {suggestions.categories.map((category) => (
1419
- <a key={category.id} href={`/category/${category.id}`}>
1517
+ // Suggestions carry no slug — link to the filtered listing (by id),
1518
+ // not the SEO category page (which is `/category/${slug}`).
1519
+ <a key={category.id} href={`/products?category=${category.id}`}>
1420
1520
  {category.name} ({category.productCount} products)
1421
1521
  </a>
1422
1522
  ))}
@@ -2975,15 +3075,17 @@ customers check their points balance, enroll, and redeem rewards for one-time
2975
3075
  coupons that apply at checkout.
2976
3076
 
2977
3077
  ```typescript
2978
- // Current status — points balance + program display config + tier progress.
2979
- // `program`/`tier`/`nextTier` are null if the store has no loyalty program
2980
- // (or no tiers configured / the customer hasn't reached one yet).
3078
+ // Current status — points balance + program display config + tier progress +
3079
+ // earned milestone `badges` + `paidMembership` (paid subscription, null for
3080
+ // free members). `program`/`tier`/`nextTier` are null if the store has no
3081
+ // loyalty program (or no tiers configured / the customer hasn't reached one yet).
2981
3082
  const status = await client.getLoyaltyStatus();
2982
3083
  if (status.enrolled) {
2983
3084
  console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
2984
3085
  if (status.nextTier) {
2985
3086
  console.log(`${status.pointsToNextTier} to reach ${status.nextTier.name}`);
2986
3087
  }
3088
+ status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
2987
3089
  }
2988
3090
 
2989
3091
  // Enroll (idempotent) — only if the program is ACTIVE. May grant a one-time
@@ -2994,6 +3096,11 @@ await client.enrollInLoyalty();
2994
3096
  // their current tier qualifies for).
2995
3097
  const rewards = await client.getAvailableRewards();
2996
3098
 
3099
+ // AI "recommended for you" — always one of the catalog's real rewards (the AI
3100
+ // only ranks; it can never invent a discount). `{ reward: null }` when the
3101
+ // catalog is empty. Rate-limited to 5/min per customer.
3102
+ const { reward, reason } = await client.getRecommendedReward();
3103
+
2997
3104
  // Redeem → spends points, returns a one-time coupon code to apply to the cart.
2998
3105
  // `discountType` tells you how to interpret `discountValue` (currency amount
2999
3106
  // for FIXED_DISCOUNT, 0-100 percent for PERCENT_DISCOUNT).
@@ -3038,6 +3145,43 @@ Birthday gifts need no SDK calls beyond profile data: set the customer's
3038
3145
  platform emails a one-time gift coupon ahead of their birthday automatically
3039
3146
  (when the store has it enabled).
3040
3147
 
3148
+ #### Paid Loyalty Membership
3149
+
3150
+ Stores can offer a paid "premium membership" inside the loyalty program — a
3151
+ recurring charge (default every 30 days) that grants a points multiplier and
3152
+ other perks. Storefront-mode only, requires `customerToken`. The customer must
3153
+ first have a saved card (vault one by checking out with `saveCard: true`).
3154
+
3155
+ ```typescript
3156
+ // 1. Plans the store offers (empty if none / program not active).
3157
+ const plans = await client.getMembershipPlans();
3158
+
3159
+ // 2. The customer's saved cards — display fields only (brand / last4 / expiry).
3160
+ const methods = await client.getMySavedPaymentMethods();
3161
+
3162
+ // 3. Subscribe — charges the chosen card IMMEDIATELY and schedules the
3163
+ // recurring cycle. Declines throw a 409 with a `code` ('card_declined',
3164
+ // 'requires_action' when the bank demands 3D-Secure — not supported
3165
+ // off-session, ask the customer to use a different card).
3166
+ const membership = await client.subscribeToMembership({
3167
+ planId: plans[0].id,
3168
+ savedPaymentTokenId: methods.find((m) => m.isDefault)!.id,
3169
+ });
3170
+ // membership.status === 'ACTIVE'; points now earn at plan.pointsMultiplier ×
3171
+ // the tier multiplier.
3172
+
3173
+ // 4. Cancel — end-of-period: perks continue until `nextBillingAt`, then the
3174
+ // subscription ends with no further charge. Re-subscribing to the same plan
3175
+ // before period end un-cancels.
3176
+ const cancelled = await client.cancelMembership();
3177
+ // cancelled.cancelAtPeriodEnd === true
3178
+
3179
+ // `getLoyaltyStatus().paidMembership` reflects the subscription everywhere:
3180
+ // { status: 'ACTIVE' | 'PAST_DUE' | 'CANCELLED', nextBillingAt, plan, cancelAtPeriodEnd }
3181
+ // PAST_DUE = the last recurring charge failed; the platform retries daily and
3182
+ // pauses the multiplier until the charge clears.
3183
+ ```
3184
+
3041
3185
  #### Auth Response Type
3042
3186
 
3043
3187
  ```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,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",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,i=null;for(let o of n){let r=/^\s*[-•*]\s+(.*)$/.exec(o);if(r){i=null,t||(t=document.createElement("ul"),p.appendChild(t));let d=document.createElement("li");D(d,r[1]),t.appendChild(d);continue}if(t=null,!o.trim()){i=null;continue}i?i.appendChild(document.createElement("br")):(i=document.createElement("p"),p.appendChild(i)),D(i,o)}}var S=class p{constructor(e){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||U).replace(/\/$/,""),this.storageKey=`brainerce-bot:${this.connectionId}`,this.onAddToCart=e.onAddToCart}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 i=this.settings.bubbleShape==="square",o=i?"14px":"22px",r=i?"8px":"15px",d="5px",a=this.settings.displayMode??"floating",l=a==="side_rail",s=a==="full_screen";return`
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: ${i?"16px":"999px"}; overflow: hidden;
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
- ${s?`.bb.open .bb-window { position: fixed;
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=z.has(this.locale)?"rtl":"ltr",i=this.settings.position==="start"?"left":"right",o=t==="rtl"?i==="left"?"right":"left":i,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 s=document.createElement("div");s.className="bb-header";let b=document.createElement("span");if(b.className="bb-avatar",this.settings.avatarUrl&&B(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())),s.appendChild(b),s.appendChild(u),s.appendChild(v),this.windowEl.appendChild(s),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 A=document.createElement("button");A.className="bb-chip",A.textContent=g,A.addEventListener("click",()=>this.send(g)),this.chipsEl.appendChild(A)}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 M=document.createElement("div");M.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"))),M.appendChild(x),this.windowEl.appendChild(M),this.launcherEl=document.createElement("button"),this.launcherEl.className="bb-launcher",this.launcherEl.setAttribute("aria-label",r),this.settings.avatarUrl&&B(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 T=m("close");T.classList.add("bb-l-close"),this.launcherEl.appendChild(T),this.launcherEl.addEventListener("click",()=>this.opened?this.close():this.open()),a.appendChild(this.launcherEl),e.appendChild(this.host)}iconButton(e,n,t){let i=document.createElement("button");return i.className="bb-iconbtn",i.title=n,i.setAttribute("aria-label",n),i.appendChild(m(e)),i.addEventListener("click",t),i}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 i=this.appendMessage(t.role==="assistant"?"bot":"user","");O(i,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 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 i=null;try{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})});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:s}=await r.read();if(s)break;a+=d.decode(l,{stream:!0});let b;for(;(b=a.indexOf(`
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}i=this.handleFrame(h,t,i)}}i&&this.pendingText&&O(i,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 s=document.createElement("div");s.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",s.appendChild(b),s.appendChild(this.cardsRow),this.messagesEl.appendChild(s)}let n=B(e.url)?e.url:null,t=document.createElement("div");t.className="bb-card";let i=()=>{this.beacon(e.botRef),n&&(window.location.href=n)},o=document.createElement("span");if(o.className="bb-card-img",e.imageUrl&&B(e.imageUrl)){let s=document.createElement("img");s.src=e.imageUrl,s.alt="",s.loading="lazy",o.appendChild(s)}else o.appendChild(m("cart"));o.addEventListener("click",i),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",i);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 s=document.createElement("button");s.className="bb-btn bb-btn-add",s.setAttribute("aria-label",this.t("addToCart")),s.appendChild(m("cart")),e.variants?.length?s.addEventListener("click",()=>this.togglePicker(r,e,o)):s.addEventListener("click",i),l.appendChild(s)}else{let s=document.createElement("button");s.className="bb-btn bb-btn-add bb-sq",s.setAttribute("aria-label",this.t("addToCart")),s.appendChild(m("cart")),s.addEventListener("click",()=>void this.addToCart(e,s));let b=document.createElement("button");b.className="bb-btn bb-btn-ghost bb-wide",b.appendChild(document.createTextNode(this.t("view"))),b.addEventListener("click",i),l.appendChild(b),l.appendChild(s)}r.appendChild(l),t.appendChild(r),this.cardsRow.appendChild(t),this.scrollDown()}togglePicker(e,n,t){let i=e.parentElement,o=e.querySelector(".bb-pick");if(o){o.remove(),i?.classList.remove("picking");return}i?.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 s=()=>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 M=r.some(N=>N.attributes[c]===f&&d.every(T=>T===c||!a[T]||N.attributes[T]===a[T])),x=document.createElement("button");x.className=`bb-chipv${a[c]===f?" sel":""}${M?"":" 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(),i?.classList.remove("picking")}),l.appendChild(u);let h=s(),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=s();c&&this.addToCart(n,y,c.id)}),w.appendChild(v),w.appendChild(y),l.appendChild(w),h?.imageUrl&&B(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,B(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 i=0;i<3;i++)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 S.mount({connectionId:e,baseUrl:n});document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()})();})();
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()})();})();
@@ -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;
@@ -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;
package/dist/bot/index.js CHANGED
@@ -164,6 +164,10 @@ function renderRich(el, text) {
164
164
  }
165
165
  var BrainerceBot = class _BrainerceBot {
166
166
  constructor(options) {
167
+ /** Part 2 (order-lookup login gating) — short-lived, in-memory only (never
168
+ * persisted): refreshed opportunistically before a chat send when stale. */
169
+ this.botSessionToken = null;
170
+ this.botSessionFetchedAt = 0;
167
171
  this.settings = { enabled: false };
168
172
  this.locale = "en";
169
173
  this.sessionId = null;
@@ -185,6 +189,7 @@ var BrainerceBot = class _BrainerceBot {
185
189
  this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
186
190
  this.storageKey = `brainerce-bot:${this.connectionId}`;
187
191
  this.onAddToCart = options.onAddToCart;
192
+ this.customerSessionProxyPath = options.customerSessionProxyPath?.replace(/\/$/, "");
188
193
  }
189
194
  /** Boot the widget. Resolves to null when the bot is disabled server-side. */
190
195
  static async mount(options) {
@@ -749,6 +754,30 @@ var BrainerceBot = class _BrainerceBot {
749
754
  // --------------------------------------------------------------------------
750
755
  // The chat turn
751
756
  // --------------------------------------------------------------------------
757
+ /**
758
+ * Part 2 (order-lookup login gating) — refresh the short-lived bot-session token
759
+ * if this site opted in (`customerSessionProxyPath` set) and the current one is
760
+ * stale or absent. Comfortably under the backend's 5-minute expiry so a token
761
+ * fetched here is never expired by the time /chat reads it. Fails silently on any
762
+ * error (offline, no proxy configured server-side, etc.) — a guest must always be
763
+ * able to chat regardless of this call's outcome.
764
+ */
765
+ async refreshCustomerSession() {
766
+ if (!this.customerSessionProxyPath) return;
767
+ const FOUR_MINUTES_MS = 4 * 60 * 1e3;
768
+ if (this.botSessionToken && Date.now() - this.botSessionFetchedAt < FOUR_MINUTES_MS) return;
769
+ try {
770
+ const res = await fetch(
771
+ `${this.customerSessionProxyPath}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/customer-session`,
772
+ { credentials: "include" }
773
+ );
774
+ if (!res.ok) return;
775
+ const data = await res.json();
776
+ this.botSessionToken = data.loggedIn && data.botSessionToken ? data.botSessionToken : null;
777
+ this.botSessionFetchedAt = Date.now();
778
+ } catch {
779
+ }
780
+ }
752
781
  async send(text) {
753
782
  const message = text.trim();
754
783
  if (!message || this.busy) return;
@@ -763,6 +792,7 @@ var BrainerceBot = class _BrainerceBot {
763
792
  this.cardIds = /* @__PURE__ */ new Set();
764
793
  let botBubble = null;
765
794
  try {
795
+ await this.refreshCustomerSession();
766
796
  const res = await fetch(
767
797
  `${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/chat`,
768
798
  {
@@ -773,7 +803,8 @@ var BrainerceBot = class _BrainerceBot {
773
803
  turnId: randomId("trn_"),
774
804
  ...this.conversationId ? { conversationId: this.conversationId } : {},
775
805
  ...this.sessionId ? { anonymousSessionId: this.sessionId } : {},
776
- locale: this.locale
806
+ locale: this.locale,
807
+ ...this.botSessionToken ? { botSessionToken: this.botSessionToken } : {}
777
808
  })
778
809
  }
779
810
  );