brainerce 1.26.0 → 1.27.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
@@ -50,9 +50,9 @@ Every Brainerce storefront must include **all mandatory features** below. Featur
50
50
  | Discount banners + product badges | `client.getDiscountBanners()`, `client.getProductDiscountBadge(productId)` | ✅ |
51
51
  | Product reviews on PDP + JSON-LD aggregateRating | `client.listProductReviews(id)`, `client.submitProductReview(id, …)` | ✅ |
52
52
  | Site chrome (header + footer + announcement bar) | `client.content.header.get()`, `client.content.footer.get()`, `client.content.announcement.list()` | ✅ |
53
- | FAQ page | `client.content.faq.get('main', locale)` | conditional |
54
- | Static pages catch-all (`/pages/[slug]`) | `client.content.page.getBySlug(slug, locale)` | conditional |
55
- | Multi-language + RTL (when i18n enabled) | `client.setLocale()`, `client.getStoreDirection(locale)` | conditional |
53
+ | FAQ page | `client.content.faq.get('main', locale)` | conditional |
54
+ | Static pages catch-all (`/pages/[slug]`) | `client.content.page.getBySlug(slug, locale)` | conditional |
55
+ | Multi-language + RTL (when i18n enabled) | `client.setLocale()`, `client.getStoreDirection(locale)` | conditional |
56
56
 
57
57
  ---
58
58
 
@@ -349,7 +349,7 @@ await client.addToCart(cart.id, {
349
349
  });
350
350
  ```
351
351
 
352
- Full rendering guide + per-type validation rules: [INTEGRATION.md §2.8](https://brainerce.com/docs/integration) and [INTEGRATION-RULES.md](https://brainerce.com/docs/integration/raw?part=rules).
352
+ Full rendering guide + per-type validation rules: [Core Integration §2.8](https://brainerce.com/docs/integration/core) and [Rules & Reference](https://brainerce.com/docs/integration/rules).
353
353
 
354
354
  ### Modifier groups (restaurant / build-your-own products)
355
355
 
@@ -369,7 +369,7 @@ await client.addToCart(cart.id, {
369
369
 
370
370
  Money on the wire is **always strings** (`priceDelta: "5.00"`). Validation failures arrive as a structured 400 envelope on `BrainerceError.details` with `code: 'MODIFIER_VALIDATION_FAILED'` and `errors[]` — see INTEGRATION-RULES.md "Modifier validation errors" for the full code list.
371
371
 
372
- Full rendering guide: [INTEGRATION.md §2.9](https://brainerce.com/docs/integration). Restaurant features (allergens, scheduled availability, nested combos to depth 3, downsell modifiers): [INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products"](https://brainerce.com/docs/integration/raw?part=optional).
372
+ Full rendering guide: [Core Integration §2.9](https://brainerce.com/docs/integration/core). Restaurant features (allergens, scheduled availability, nested combos to depth 3, downsell modifiers): [Optional Features "Restaurant / build-your-own products"](https://brainerce.com/docs/integration/optional).
373
373
 
374
374
  ### Content (FAQ / Footer / Header / Announcements / Pages)
375
375
 
@@ -402,7 +402,7 @@ import DOMPurify from 'isomorphic-dompurify';
402
402
 
403
403
  The `create-brainerce-store` scaffold ships ready-made components (`<AnnouncementBar>`, `<SiteHeader>`, `<SiteFooter>`, `<FaqSection>`, `<RichTextBlock>`) + a `/pages/[slug]` catch-all route — use them rather than rolling your own renderers.
404
404
 
405
- Full guide: [INTEGRATION.md "Content"](https://brainerce.com/docs/integration#content). Advanced patterns (channel scoping, custom fields, translations, admin writes): [INTEGRATION-OPTIONAL.md "Content"](https://brainerce.com/docs/integration/raw?part=optional). Validation + sanitize rules: [INTEGRATION-RULES.md "Content"](https://brainerce.com/docs/integration/raw?part=rules).
405
+ Full guide: [Core Integration "Content"](https://brainerce.com/docs/integration/core#content). Advanced patterns (channel scoping, custom fields, translations, admin writes): [Optional Features "Content"](https://brainerce.com/docs/integration/optional). Validation + sanitize rules: [Rules & Reference "Content"](https://brainerce.com/docs/integration/rules).
406
406
 
407
407
  ---
408
408
 
package/dist/index.d.mts CHANGED
@@ -3793,12 +3793,6 @@ interface CreateMetafieldDefinitionDto {
3793
3793
  enumValues?: string[];
3794
3794
  defaultValue?: string;
3795
3795
  position?: number;
3796
- /**
3797
- * When `true`, create the definition account-wide (`storeId = null`).
3798
- * Defaults to false — the definition belongs to the store named in the
3799
- * route's `:storeId` parameter.
3800
- */
3801
- accountWide?: boolean;
3802
3796
  /** Sales channels / platforms where this field is published. */
3803
3797
  publishedOn?: string[];
3804
3798
  /**
package/dist/index.d.ts CHANGED
@@ -3793,12 +3793,6 @@ interface CreateMetafieldDefinitionDto {
3793
3793
  enumValues?: string[];
3794
3794
  defaultValue?: string;
3795
3795
  position?: number;
3796
- /**
3797
- * When `true`, create the definition account-wide (`storeId = null`).
3798
- * Defaults to false — the definition belongs to the store named in the
3799
- * route's `:storeId` parameter.
3800
- */
3801
- accountWide?: boolean;
3802
3796
  /** Sales channels / platforms where this field is published. */
3803
3797
  publishedOn?: string[];
3804
3798
  /**
package/dist/index.js CHANGED
@@ -193,6 +193,15 @@ function getDirectionForLocale(locale) {
193
193
  const primary = locale.split("-")[0].toLowerCase();
194
194
  return RTL_LOCALES.has(primary) ? "rtl" : "ltr";
195
195
  }
196
+ function encodePathSegment(value) {
197
+ let normalized = value;
198
+ try {
199
+ normalized = decodeURIComponent(value);
200
+ } catch {
201
+ normalized = value;
202
+ }
203
+ return encodeURIComponent(normalized);
204
+ }
196
205
  var BrainerceClient = class {
197
206
  constructor(options) {
198
207
  this.customerToken = null;
@@ -309,87 +318,76 @@ var BrainerceClient = class {
309
318
  * ```
310
319
  */
311
320
  this.content = (() => {
312
- const self = this;
313
321
  const DEFAULT_KEY = "main";
314
- function publicListPath(type) {
315
- return `/content?type=${encodeURIComponent(type)}`;
316
- }
317
- function publicGetPath(type, key) {
318
- return `/content/${encodeURIComponent(type)}/${encodeURIComponent(key)}`;
319
- }
320
- function adminBase() {
321
- return "/api/v1/content";
322
- }
323
- async function publicGet(type, key, locale) {
322
+ const publicGetPath = (type, key) => `/content/${encodeURIComponent(type)}/${encodeURIComponent(key)}`;
323
+ const adminBase = () => "/api/v1/content";
324
+ const publicGet = async (type, key, locale) => {
324
325
  const query = locale ? { locale } : void 0;
325
326
  const path = publicGetPath(type, key);
326
327
  const onNotFound = (err) => {
327
328
  if (err instanceof BrainerceError && err.statusCode === 404) return null;
328
329
  throw err;
329
330
  };
330
- if (self.isVibeCodedMode()) {
331
- return self.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
331
+ if (this.isVibeCodedMode()) {
332
+ return this.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
332
333
  }
333
- if (self.storeId && !self.apiKey) {
334
- return self.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
334
+ if (this.storeId && !this.apiKey) {
335
+ return this.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
335
336
  }
336
337
  throw new BrainerceError(
337
338
  "content.<type>.get(key) is a public-read API. In admin mode, call client.content.list({ type }) and filter by key, or use client.content.findById(id).",
338
339
  400
339
340
  );
340
- }
341
- async function publicList(type, locale) {
341
+ };
342
+ const publicList = async (type, locale) => {
342
343
  const query = { type };
343
344
  if (locale) query.locale = locale;
344
- if (self.isVibeCodedMode()) {
345
- return self.vibeCodedRequest("GET", "/content", void 0, query);
345
+ if (this.isVibeCodedMode()) {
346
+ return this.vibeCodedRequest("GET", "/content", void 0, query);
346
347
  }
347
- if (self.storeId && !self.apiKey) {
348
- return self.storefrontRequest("GET", "/content", void 0, query);
348
+ if (this.storeId && !this.apiKey) {
349
+ return this.storefrontRequest("GET", "/content", void 0, query);
349
350
  }
350
- return self.adminRequest(
351
+ return this.adminRequest(
351
352
  "GET",
352
353
  `${adminBase()}?type=${encodeURIComponent(type)}`
353
354
  );
354
- }
355
- function requireAdmin(action) {
356
- if (self.isVibeCodedMode() || self.storeId && !self.apiKey) {
355
+ };
356
+ const requireAdmin = (action) => {
357
+ if (this.isVibeCodedMode() || this.storeId && !this.apiKey) {
357
358
  throw new BrainerceError(
358
359
  `client.content.${action}() requires admin mode (apiKey). Vibe-coded and storefront modes are read-only.`,
359
360
  403
360
361
  );
361
362
  }
362
- }
363
- async function createByType(type, input) {
363
+ };
364
+ const createByType = async (type, input) => {
364
365
  requireAdmin("create");
365
- return self.adminRequest("POST", adminBase(), { ...input, type });
366
- }
367
- async function updateById(id, input) {
366
+ return this.adminRequest("POST", adminBase(), { ...input, type });
367
+ };
368
+ const updateById = async (id, input) => {
368
369
  requireAdmin("update");
369
- return self.adminRequest(
370
+ return this.adminRequest(
370
371
  "PATCH",
371
372
  `${adminBase()}/${encodeURIComponent(id)}`,
372
373
  input
373
374
  );
374
- }
375
- async function publishById(id) {
375
+ };
376
+ const publishById = async (id) => {
376
377
  requireAdmin("publish");
377
- return self.adminRequest(
378
- "POST",
379
- `${adminBase()}/${encodeURIComponent(id)}/publish`
380
- );
381
- }
382
- async function unpublishById(id) {
378
+ return this.adminRequest("POST", `${adminBase()}/${encodeURIComponent(id)}/publish`);
379
+ };
380
+ const unpublishById = async (id) => {
383
381
  requireAdmin("unpublish");
384
- return self.adminRequest(
382
+ return this.adminRequest(
385
383
  "POST",
386
384
  `${adminBase()}/${encodeURIComponent(id)}/unpublish`
387
385
  );
388
- }
389
- async function removeById(id) {
386
+ };
387
+ const removeById = async (id) => {
390
388
  requireAdmin("remove");
391
- await self.adminRequest("DELETE", `${adminBase()}/${encodeURIComponent(id)}`);
392
- }
389
+ await this.adminRequest("DELETE", `${adminBase()}/${encodeURIComponent(id)}`);
390
+ };
393
391
  function makeNamespace(type) {
394
392
  return {
395
393
  /**
@@ -419,16 +417,20 @@ var BrainerceClient = class {
419
417
  */
420
418
  getBySlug: async (slug, locale) => {
421
419
  const query = locale ? { locale } : void 0;
422
- const path = `/content/pages/by-slug/${encodeURIComponent(slug)}`;
420
+ const path = `/content/pages/by-slug/${encodePathSegment(slug)}`;
423
421
  const onNotFound = (err) => {
424
422
  if (err instanceof BrainerceError && err.statusCode === 404) return null;
425
423
  throw err;
426
424
  };
427
- if (self.isVibeCodedMode()) {
428
- return self.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
425
+ if (this.isVibeCodedMode()) {
426
+ return this.vibeCodedRequest("GET", path, void 0, query).catch(
427
+ onNotFound
428
+ );
429
429
  }
430
- if (self.storeId && !self.apiKey) {
431
- return self.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
430
+ if (this.storeId && !this.apiKey) {
431
+ return this.storefrontRequest("GET", path, void 0, query).catch(
432
+ onNotFound
433
+ );
432
434
  }
433
435
  throw new BrainerceError(
434
436
  "content.page.getBySlug() is a public-read API; not available in admin mode.",
@@ -440,7 +442,7 @@ var BrainerceClient = class {
440
442
  /** Find a single row by its admin id (admin mode). */
441
443
  findById: async (id) => {
442
444
  requireAdmin("findById");
443
- return self.adminRequest("GET", `${adminBase()}/${encodeURIComponent(id)}`);
445
+ return this.adminRequest("GET", `${adminBase()}/${encodeURIComponent(id)}`);
444
446
  },
445
447
  /** List rows in admin mode with optional filters. */
446
448
  listAdmin: async (filters) => {
@@ -449,7 +451,7 @@ var BrainerceClient = class {
449
451
  if (filters?.type) params.set("type", filters.type);
450
452
  if (filters?.status) params.set("status", filters.status);
451
453
  const qs = params.toString();
452
- return self.adminRequest(
454
+ return this.adminRequest(
453
455
  "GET",
454
456
  qs ? `${adminBase()}?${qs}` : adminBase()
455
457
  );
@@ -1001,7 +1003,7 @@ var BrainerceClient = class {
1001
1003
  */
1002
1004
  async getProductBySlug(slug, options) {
1003
1005
  const headerOverrides = options?.locale ? { "Accept-Language": options.locale } : void 0;
1004
- const encodedSlug = encodeURIComponent(slug);
1006
+ const encodedSlug = encodePathSegment(slug);
1005
1007
  if (this.isVibeCodedMode()) {
1006
1008
  return this.vibeCodedRequest(
1007
1009
  "GET",
package/dist/index.mjs CHANGED
@@ -126,6 +126,15 @@ function getDirectionForLocale(locale) {
126
126
  const primary = locale.split("-")[0].toLowerCase();
127
127
  return RTL_LOCALES.has(primary) ? "rtl" : "ltr";
128
128
  }
129
+ function encodePathSegment(value) {
130
+ let normalized = value;
131
+ try {
132
+ normalized = decodeURIComponent(value);
133
+ } catch {
134
+ normalized = value;
135
+ }
136
+ return encodeURIComponent(normalized);
137
+ }
129
138
  var BrainerceClient = class {
130
139
  constructor(options) {
131
140
  this.customerToken = null;
@@ -242,87 +251,76 @@ var BrainerceClient = class {
242
251
  * ```
243
252
  */
244
253
  this.content = (() => {
245
- const self = this;
246
254
  const DEFAULT_KEY = "main";
247
- function publicListPath(type) {
248
- return `/content?type=${encodeURIComponent(type)}`;
249
- }
250
- function publicGetPath(type, key) {
251
- return `/content/${encodeURIComponent(type)}/${encodeURIComponent(key)}`;
252
- }
253
- function adminBase() {
254
- return "/api/v1/content";
255
- }
256
- async function publicGet(type, key, locale) {
255
+ const publicGetPath = (type, key) => `/content/${encodeURIComponent(type)}/${encodeURIComponent(key)}`;
256
+ const adminBase = () => "/api/v1/content";
257
+ const publicGet = async (type, key, locale) => {
257
258
  const query = locale ? { locale } : void 0;
258
259
  const path = publicGetPath(type, key);
259
260
  const onNotFound = (err) => {
260
261
  if (err instanceof BrainerceError && err.statusCode === 404) return null;
261
262
  throw err;
262
263
  };
263
- if (self.isVibeCodedMode()) {
264
- return self.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
264
+ if (this.isVibeCodedMode()) {
265
+ return this.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
265
266
  }
266
- if (self.storeId && !self.apiKey) {
267
- return self.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
267
+ if (this.storeId && !this.apiKey) {
268
+ return this.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
268
269
  }
269
270
  throw new BrainerceError(
270
271
  "content.<type>.get(key) is a public-read API. In admin mode, call client.content.list({ type }) and filter by key, or use client.content.findById(id).",
271
272
  400
272
273
  );
273
- }
274
- async function publicList(type, locale) {
274
+ };
275
+ const publicList = async (type, locale) => {
275
276
  const query = { type };
276
277
  if (locale) query.locale = locale;
277
- if (self.isVibeCodedMode()) {
278
- return self.vibeCodedRequest("GET", "/content", void 0, query);
278
+ if (this.isVibeCodedMode()) {
279
+ return this.vibeCodedRequest("GET", "/content", void 0, query);
279
280
  }
280
- if (self.storeId && !self.apiKey) {
281
- return self.storefrontRequest("GET", "/content", void 0, query);
281
+ if (this.storeId && !this.apiKey) {
282
+ return this.storefrontRequest("GET", "/content", void 0, query);
282
283
  }
283
- return self.adminRequest(
284
+ return this.adminRequest(
284
285
  "GET",
285
286
  `${adminBase()}?type=${encodeURIComponent(type)}`
286
287
  );
287
- }
288
- function requireAdmin(action) {
289
- if (self.isVibeCodedMode() || self.storeId && !self.apiKey) {
288
+ };
289
+ const requireAdmin = (action) => {
290
+ if (this.isVibeCodedMode() || this.storeId && !this.apiKey) {
290
291
  throw new BrainerceError(
291
292
  `client.content.${action}() requires admin mode (apiKey). Vibe-coded and storefront modes are read-only.`,
292
293
  403
293
294
  );
294
295
  }
295
- }
296
- async function createByType(type, input) {
296
+ };
297
+ const createByType = async (type, input) => {
297
298
  requireAdmin("create");
298
- return self.adminRequest("POST", adminBase(), { ...input, type });
299
- }
300
- async function updateById(id, input) {
299
+ return this.adminRequest("POST", adminBase(), { ...input, type });
300
+ };
301
+ const updateById = async (id, input) => {
301
302
  requireAdmin("update");
302
- return self.adminRequest(
303
+ return this.adminRequest(
303
304
  "PATCH",
304
305
  `${adminBase()}/${encodeURIComponent(id)}`,
305
306
  input
306
307
  );
307
- }
308
- async function publishById(id) {
308
+ };
309
+ const publishById = async (id) => {
309
310
  requireAdmin("publish");
310
- return self.adminRequest(
311
- "POST",
312
- `${adminBase()}/${encodeURIComponent(id)}/publish`
313
- );
314
- }
315
- async function unpublishById(id) {
311
+ return this.adminRequest("POST", `${adminBase()}/${encodeURIComponent(id)}/publish`);
312
+ };
313
+ const unpublishById = async (id) => {
316
314
  requireAdmin("unpublish");
317
- return self.adminRequest(
315
+ return this.adminRequest(
318
316
  "POST",
319
317
  `${adminBase()}/${encodeURIComponent(id)}/unpublish`
320
318
  );
321
- }
322
- async function removeById(id) {
319
+ };
320
+ const removeById = async (id) => {
323
321
  requireAdmin("remove");
324
- await self.adminRequest("DELETE", `${adminBase()}/${encodeURIComponent(id)}`);
325
- }
322
+ await this.adminRequest("DELETE", `${adminBase()}/${encodeURIComponent(id)}`);
323
+ };
326
324
  function makeNamespace(type) {
327
325
  return {
328
326
  /**
@@ -352,16 +350,20 @@ var BrainerceClient = class {
352
350
  */
353
351
  getBySlug: async (slug, locale) => {
354
352
  const query = locale ? { locale } : void 0;
355
- const path = `/content/pages/by-slug/${encodeURIComponent(slug)}`;
353
+ const path = `/content/pages/by-slug/${encodePathSegment(slug)}`;
356
354
  const onNotFound = (err) => {
357
355
  if (err instanceof BrainerceError && err.statusCode === 404) return null;
358
356
  throw err;
359
357
  };
360
- if (self.isVibeCodedMode()) {
361
- return self.vibeCodedRequest("GET", path, void 0, query).catch(onNotFound);
358
+ if (this.isVibeCodedMode()) {
359
+ return this.vibeCodedRequest("GET", path, void 0, query).catch(
360
+ onNotFound
361
+ );
362
362
  }
363
- if (self.storeId && !self.apiKey) {
364
- return self.storefrontRequest("GET", path, void 0, query).catch(onNotFound);
363
+ if (this.storeId && !this.apiKey) {
364
+ return this.storefrontRequest("GET", path, void 0, query).catch(
365
+ onNotFound
366
+ );
365
367
  }
366
368
  throw new BrainerceError(
367
369
  "content.page.getBySlug() is a public-read API; not available in admin mode.",
@@ -373,7 +375,7 @@ var BrainerceClient = class {
373
375
  /** Find a single row by its admin id (admin mode). */
374
376
  findById: async (id) => {
375
377
  requireAdmin("findById");
376
- return self.adminRequest("GET", `${adminBase()}/${encodeURIComponent(id)}`);
378
+ return this.adminRequest("GET", `${adminBase()}/${encodeURIComponent(id)}`);
377
379
  },
378
380
  /** List rows in admin mode with optional filters. */
379
381
  listAdmin: async (filters) => {
@@ -382,7 +384,7 @@ var BrainerceClient = class {
382
384
  if (filters?.type) params.set("type", filters.type);
383
385
  if (filters?.status) params.set("status", filters.status);
384
386
  const qs = params.toString();
385
- return self.adminRequest(
387
+ return this.adminRequest(
386
388
  "GET",
387
389
  qs ? `${adminBase()}?${qs}` : adminBase()
388
390
  );
@@ -934,7 +936,7 @@ var BrainerceClient = class {
934
936
  */
935
937
  async getProductBySlug(slug, options) {
936
938
  const headerOverrides = options?.locale ? { "Accept-Language": options.locale } : void 0;
937
- const encodedSlug = encodeURIComponent(slug);
939
+ const encodedSlug = encodePathSegment(slug);
938
940
  if (this.isVibeCodedMode()) {
939
941
  return this.vibeCodedRequest(
940
942
  "GET",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainerce",
3
- "version": "1.26.0",
3
+ "version": "1.27.1",
4
4
  "description": "Official SDK for building e-commerce storefronts with Brainerce Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",