brainerce 1.33.0 → 1.34.0

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
@@ -409,6 +409,33 @@ The `create-brainerce-store` scaffold ships ready-made components (`<Announcemen
409
409
 
410
410
  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).
411
411
 
412
+ ### Blog
413
+
414
+ Merchants publish blog posts in **Content → Blog**. Storefronts choose their own URL scheme — render posts at `/blog/[slug]`, `/articles/[slug]`, or whatever fits the brand.
415
+
416
+ ```typescript
417
+ // List published posts (any SDK mode)
418
+ const { data: posts, meta } = await client.blog.getPosts({ page: 1, limit: 10 });
419
+
420
+ // Filter by category or tag
421
+ const { data: news } = await client.blog.getPosts({ category: 'news' });
422
+ const { data: tips } = await client.blog.getPosts({ tag: 'tutorial' });
423
+
424
+ // Fetch one by slug — returns null on 404
425
+ const post = await client.blog.getPost(params.slug);
426
+ if (!post) notFound();
427
+ ```
428
+
429
+ **Security**: `post.content` is merchant-authored HTML. Always sanitize before rendering:
430
+
431
+ ```tsx
432
+ import DOMPurify from 'isomorphic-dompurify';
433
+ const safeHtml = DOMPurify.sanitize(post.content);
434
+ return <div dangerouslySetInnerHTML={{ __html: safeHtml }} className="prose" />;
435
+ ```
436
+
437
+ **Scheduling**: A post is visible once `status === 'PUBLISHED'` and `publishedAt <= now()`. Set a future `publishedAt` when publishing to schedule — no cron needed.
438
+
412
439
  ---
413
440
 
414
441
  ## Common Mistakes to Avoid
package/dist/index.d.mts CHANGED
@@ -5168,6 +5168,61 @@ interface ModifierValidationError {
5168
5168
  /** Modifier the error belongs to (when applicable). */
5169
5169
  modifierId?: string;
5170
5170
  }
5171
+ type BlogPostStatus = 'DRAFT' | 'SCHEDULED' | 'PUBLISHED';
5172
+ interface BlogPost {
5173
+ id: string;
5174
+ storeId: string;
5175
+ title: string;
5176
+ slug: string;
5177
+ category?: string;
5178
+ content: string;
5179
+ excerpt?: string;
5180
+ coverImageUrl?: string;
5181
+ coverImageAlt?: string;
5182
+ author?: string;
5183
+ tags: string[];
5184
+ metadata: Record<string, unknown>;
5185
+ status: BlogPostStatus;
5186
+ publishedAt?: string;
5187
+ seoTitle?: string;
5188
+ seoDescription?: string;
5189
+ ogImageUrl?: string;
5190
+ createdAt: string;
5191
+ updatedAt: string;
5192
+ }
5193
+ interface CreateBlogPostInput {
5194
+ title: string;
5195
+ slug?: string;
5196
+ category?: string;
5197
+ content?: string;
5198
+ excerpt?: string;
5199
+ coverImageUrl?: string;
5200
+ coverImageAlt?: string;
5201
+ author?: string;
5202
+ tags?: string[];
5203
+ metadata?: Record<string, unknown>;
5204
+ status?: BlogPostStatus;
5205
+ publishedAt?: string | null;
5206
+ seoTitle?: string;
5207
+ seoDescription?: string;
5208
+ ogImageUrl?: string;
5209
+ }
5210
+ type UpdateBlogPostInput = Partial<CreateBlogPostInput>;
5211
+ interface BlogPostListParams {
5212
+ page?: number;
5213
+ limit?: number;
5214
+ category?: string;
5215
+ tag?: string;
5216
+ }
5217
+ interface BlogPostListResponse {
5218
+ data: BlogPost[];
5219
+ meta: {
5220
+ page: number;
5221
+ limit: number;
5222
+ total: number;
5223
+ totalPages: number;
5224
+ };
5225
+ }
5171
5226
  interface BrainerceApiError {
5172
5227
  statusCode: number;
5173
5228
  message: string;
@@ -6637,6 +6692,42 @@ declare class BrainerceClient {
6637
6692
  /** Hard delete the row. Admin mode only. */
6638
6693
  remove: (id: string) => Promise<void>;
6639
6694
  };
6695
+ /**
6696
+ * Read and manage blog posts.
6697
+ *
6698
+ * ```typescript
6699
+ * // Storefront / vibe-coded: list published posts
6700
+ * const { data: posts } = await brainerce.blog.getPosts({ category: 'news' });
6701
+ *
6702
+ * // Fetch one by slug
6703
+ * const post = await brainerce.blog.getPost('my-first-post');
6704
+ *
6705
+ * // Admin: create a draft
6706
+ * const draft = await brainerce.blog.create({ title: 'Hello World' });
6707
+ * ```
6708
+ */
6709
+ blog: {
6710
+ /**
6711
+ * List published posts. Works in all modes.
6712
+ * Filters: `category`, `tag`, `page`, `limit`.
6713
+ */
6714
+ getPosts: (params?: BlogPostListParams) => Promise<BlogPostListResponse>;
6715
+ /**
6716
+ * Fetch one published post by its slug. Returns `null` on 404.
6717
+ * Works in all modes.
6718
+ */
6719
+ getPost: (slug: string) => Promise<BlogPost | null>;
6720
+ /** Create a blog post in DRAFT status. Admin mode only. */
6721
+ create: (input: CreateBlogPostInput) => Promise<BlogPost>;
6722
+ /** Update a blog post by ID. Admin mode only. */
6723
+ update: (id: string, input: UpdateBlogPostInput) => Promise<BlogPost>;
6724
+ /** Transition status → PUBLISHED (sets publishedAt = now if unset). Admin mode only. */
6725
+ publish: (id: string) => Promise<BlogPost>;
6726
+ /** Transition status PUBLISHED → DRAFT. Admin mode only. */
6727
+ unpublish: (id: string) => Promise<BlogPost>;
6728
+ /** Hard-delete a blog post. Admin mode only. */
6729
+ remove: (id: string) => Promise<void>;
6730
+ };
6640
6731
  /**
6641
6732
  * Submit a contact inquiry from a storefront contact form.
6642
6733
  *
package/dist/index.d.ts CHANGED
@@ -5168,6 +5168,61 @@ interface ModifierValidationError {
5168
5168
  /** Modifier the error belongs to (when applicable). */
5169
5169
  modifierId?: string;
5170
5170
  }
5171
+ type BlogPostStatus = 'DRAFT' | 'SCHEDULED' | 'PUBLISHED';
5172
+ interface BlogPost {
5173
+ id: string;
5174
+ storeId: string;
5175
+ title: string;
5176
+ slug: string;
5177
+ category?: string;
5178
+ content: string;
5179
+ excerpt?: string;
5180
+ coverImageUrl?: string;
5181
+ coverImageAlt?: string;
5182
+ author?: string;
5183
+ tags: string[];
5184
+ metadata: Record<string, unknown>;
5185
+ status: BlogPostStatus;
5186
+ publishedAt?: string;
5187
+ seoTitle?: string;
5188
+ seoDescription?: string;
5189
+ ogImageUrl?: string;
5190
+ createdAt: string;
5191
+ updatedAt: string;
5192
+ }
5193
+ interface CreateBlogPostInput {
5194
+ title: string;
5195
+ slug?: string;
5196
+ category?: string;
5197
+ content?: string;
5198
+ excerpt?: string;
5199
+ coverImageUrl?: string;
5200
+ coverImageAlt?: string;
5201
+ author?: string;
5202
+ tags?: string[];
5203
+ metadata?: Record<string, unknown>;
5204
+ status?: BlogPostStatus;
5205
+ publishedAt?: string | null;
5206
+ seoTitle?: string;
5207
+ seoDescription?: string;
5208
+ ogImageUrl?: string;
5209
+ }
5210
+ type UpdateBlogPostInput = Partial<CreateBlogPostInput>;
5211
+ interface BlogPostListParams {
5212
+ page?: number;
5213
+ limit?: number;
5214
+ category?: string;
5215
+ tag?: string;
5216
+ }
5217
+ interface BlogPostListResponse {
5218
+ data: BlogPost[];
5219
+ meta: {
5220
+ page: number;
5221
+ limit: number;
5222
+ total: number;
5223
+ totalPages: number;
5224
+ };
5225
+ }
5171
5226
  interface BrainerceApiError {
5172
5227
  statusCode: number;
5173
5228
  message: string;
@@ -6637,6 +6692,42 @@ declare class BrainerceClient {
6637
6692
  /** Hard delete the row. Admin mode only. */
6638
6693
  remove: (id: string) => Promise<void>;
6639
6694
  };
6695
+ /**
6696
+ * Read and manage blog posts.
6697
+ *
6698
+ * ```typescript
6699
+ * // Storefront / vibe-coded: list published posts
6700
+ * const { data: posts } = await brainerce.blog.getPosts({ category: 'news' });
6701
+ *
6702
+ * // Fetch one by slug
6703
+ * const post = await brainerce.blog.getPost('my-first-post');
6704
+ *
6705
+ * // Admin: create a draft
6706
+ * const draft = await brainerce.blog.create({ title: 'Hello World' });
6707
+ * ```
6708
+ */
6709
+ blog: {
6710
+ /**
6711
+ * List published posts. Works in all modes.
6712
+ * Filters: `category`, `tag`, `page`, `limit`.
6713
+ */
6714
+ getPosts: (params?: BlogPostListParams) => Promise<BlogPostListResponse>;
6715
+ /**
6716
+ * Fetch one published post by its slug. Returns `null` on 404.
6717
+ * Works in all modes.
6718
+ */
6719
+ getPost: (slug: string) => Promise<BlogPost | null>;
6720
+ /** Create a blog post in DRAFT status. Admin mode only. */
6721
+ create: (input: CreateBlogPostInput) => Promise<BlogPost>;
6722
+ /** Update a blog post by ID. Admin mode only. */
6723
+ update: (id: string, input: UpdateBlogPostInput) => Promise<BlogPost>;
6724
+ /** Transition status → PUBLISHED (sets publishedAt = now if unset). Admin mode only. */
6725
+ publish: (id: string) => Promise<BlogPost>;
6726
+ /** Transition status PUBLISHED → DRAFT. Admin mode only. */
6727
+ unpublish: (id: string) => Promise<BlogPost>;
6728
+ /** Hard-delete a blog post. Admin mode only. */
6729
+ remove: (id: string) => Promise<void>;
6730
+ };
6640
6731
  /**
6641
6732
  * Submit a contact inquiry from a storefront contact form.
6642
6733
  *
package/dist/index.js CHANGED
@@ -489,6 +489,120 @@ var BrainerceClient = class {
489
489
  remove: removeById
490
490
  };
491
491
  })();
492
+ // -------------------- Blog --------------------
493
+ /**
494
+ * Read and manage blog posts.
495
+ *
496
+ * ```typescript
497
+ * // Storefront / vibe-coded: list published posts
498
+ * const { data: posts } = await brainerce.blog.getPosts({ category: 'news' });
499
+ *
500
+ * // Fetch one by slug
501
+ * const post = await brainerce.blog.getPost('my-first-post');
502
+ *
503
+ * // Admin: create a draft
504
+ * const draft = await brainerce.blog.create({ title: 'Hello World' });
505
+ * ```
506
+ */
507
+ this.blog = /* @__PURE__ */ (() => {
508
+ const publicBase = "/blog/posts";
509
+ const adminBase = "/blog/posts";
510
+ const requireAdmin = (action) => {
511
+ if (this.isVibeCodedMode() || this.storeId && !this.apiKey) {
512
+ throw new BrainerceError(
513
+ `client.blog.${action}() requires admin mode (apiKey). Vibe-coded and storefront modes are read-only.`,
514
+ 403
515
+ );
516
+ }
517
+ };
518
+ const onNotFound = (err) => {
519
+ if (err instanceof BrainerceError && err.statusCode === 404) return null;
520
+ throw err;
521
+ };
522
+ const buildQuery = (params) => Object.fromEntries(
523
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
524
+ );
525
+ return {
526
+ /**
527
+ * List published posts. Works in all modes.
528
+ * Filters: `category`, `tag`, `page`, `limit`.
529
+ */
530
+ getPosts: (params = {}) => {
531
+ const query = buildQuery(params);
532
+ if (this.isVibeCodedMode()) {
533
+ return this.vibeCodedRequest(
534
+ "GET",
535
+ publicBase,
536
+ void 0,
537
+ query
538
+ );
539
+ }
540
+ if (this.storeId && !this.apiKey) {
541
+ return this.storefrontRequest(
542
+ "GET",
543
+ publicBase,
544
+ void 0,
545
+ query
546
+ );
547
+ }
548
+ return this.adminRequest(
549
+ "GET",
550
+ adminBase,
551
+ void 0,
552
+ query
553
+ );
554
+ },
555
+ /**
556
+ * Fetch one published post by its slug. Returns `null` on 404.
557
+ * Works in all modes.
558
+ */
559
+ getPost: (slug) => {
560
+ const path = `${publicBase}/${encodeURIComponent(slug)}`;
561
+ if (this.isVibeCodedMode()) {
562
+ return this.vibeCodedRequest("GET", path).catch(onNotFound);
563
+ }
564
+ if (this.storeId && !this.apiKey) {
565
+ return this.storefrontRequest("GET", path).catch(onNotFound);
566
+ }
567
+ return this.adminRequest("GET", path).catch(onNotFound);
568
+ },
569
+ /** Create a blog post in DRAFT status. Admin mode only. */
570
+ create: (input) => {
571
+ requireAdmin("create");
572
+ return this.adminRequest("POST", adminBase, input);
573
+ },
574
+ /** Update a blog post by ID. Admin mode only. */
575
+ update: (id, input) => {
576
+ requireAdmin("update");
577
+ return this.adminRequest(
578
+ "PATCH",
579
+ `${adminBase}/${encodeURIComponent(id)}`,
580
+ input
581
+ );
582
+ },
583
+ /** Transition status → PUBLISHED (sets publishedAt = now if unset). Admin mode only. */
584
+ publish: (id) => {
585
+ requireAdmin("publish");
586
+ return this.adminRequest(
587
+ "POST",
588
+ `${adminBase}/${encodeURIComponent(id)}/publish`
589
+ );
590
+ },
591
+ /** Transition status PUBLISHED → DRAFT. Admin mode only. */
592
+ unpublish: (id) => {
593
+ requireAdmin("unpublish");
594
+ return this.adminRequest(
595
+ "POST",
596
+ `${adminBase}/${encodeURIComponent(id)}/unpublish`
597
+ );
598
+ },
599
+ /** Hard-delete a blog post. Admin mode only. */
600
+ remove: async (id) => {
601
+ requireAdmin("remove");
602
+ await this.adminRequest("DELETE", `${adminBase}/${encodeURIComponent(id)}`);
603
+ }
604
+ };
605
+ })();
492
606
  // -------------------- Local Cart (Client-Side for Guests) --------------------
493
607
  // These methods store cart data in localStorage - NO API calls!
494
608
  // Use for guest users in vibe-coded sites
@@ -8112,6 +8226,9 @@ var ALLOWED_PAYMENT_HOSTS = [
8112
8226
  "grow.security",
8113
8227
  // CreditGuard
8114
8228
  "creditguard.co.il",
8229
+ // Takbull
8230
+ "takbull.co.il",
8231
+ "api.takbull.co.il",
8115
8232
  // Brainerce-hosted payment embeds (backend payment-embed proxy at
8116
8233
  // `/api/payment/embed/...` that fronts provider apps' embed shells —
8117
8234
  // e.g. cardcom-payments OpenFields wrapper). The match also covers
package/dist/index.mjs CHANGED
@@ -419,6 +419,120 @@ var BrainerceClient = class {
419
419
  remove: removeById
420
420
  };
421
421
  })();
422
+ // -------------------- Blog --------------------
423
+ /**
424
+ * Read and manage blog posts.
425
+ *
426
+ * ```typescript
427
+ * // Storefront / vibe-coded: list published posts
428
+ * const { data: posts } = await brainerce.blog.getPosts({ category: 'news' });
429
+ *
430
+ * // Fetch one by slug
431
+ * const post = await brainerce.blog.getPost('my-first-post');
432
+ *
433
+ * // Admin: create a draft
434
+ * const draft = await brainerce.blog.create({ title: 'Hello World' });
435
+ * ```
436
+ */
437
+ this.blog = /* @__PURE__ */ (() => {
438
+ const publicBase = "/blog/posts";
439
+ const adminBase = "/blog/posts";
440
+ const requireAdmin = (action) => {
441
+ if (this.isVibeCodedMode() || this.storeId && !this.apiKey) {
442
+ throw new BrainerceError(
443
+ `client.blog.${action}() requires admin mode (apiKey). Vibe-coded and storefront modes are read-only.`,
444
+ 403
445
+ );
446
+ }
447
+ };
448
+ const onNotFound = (err) => {
449
+ if (err instanceof BrainerceError && err.statusCode === 404) return null;
450
+ throw err;
451
+ };
452
+ const buildQuery = (params) => Object.fromEntries(
453
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
454
+ );
455
+ return {
456
+ /**
457
+ * List published posts. Works in all modes.
458
+ * Filters: `category`, `tag`, `page`, `limit`.
459
+ */
460
+ getPosts: (params = {}) => {
461
+ const query = buildQuery(params);
462
+ if (this.isVibeCodedMode()) {
463
+ return this.vibeCodedRequest(
464
+ "GET",
465
+ publicBase,
466
+ void 0,
467
+ query
468
+ );
469
+ }
470
+ if (this.storeId && !this.apiKey) {
471
+ return this.storefrontRequest(
472
+ "GET",
473
+ publicBase,
474
+ void 0,
475
+ query
476
+ );
477
+ }
478
+ return this.adminRequest(
479
+ "GET",
480
+ adminBase,
481
+ void 0,
482
+ query
483
+ );
484
+ },
485
+ /**
486
+ * Fetch one published post by its slug. Returns `null` on 404.
487
+ * Works in all modes.
488
+ */
489
+ getPost: (slug) => {
490
+ const path = `${publicBase}/${encodeURIComponent(slug)}`;
491
+ if (this.isVibeCodedMode()) {
492
+ return this.vibeCodedRequest("GET", path).catch(onNotFound);
493
+ }
494
+ if (this.storeId && !this.apiKey) {
495
+ return this.storefrontRequest("GET", path).catch(onNotFound);
496
+ }
497
+ return this.adminRequest("GET", path).catch(onNotFound);
498
+ },
499
+ /** Create a blog post in DRAFT status. Admin mode only. */
500
+ create: (input) => {
501
+ requireAdmin("create");
502
+ return this.adminRequest("POST", adminBase, input);
503
+ },
504
+ /** Update a blog post by ID. Admin mode only. */
505
+ update: (id, input) => {
506
+ requireAdmin("update");
507
+ return this.adminRequest(
508
+ "PATCH",
509
+ `${adminBase}/${encodeURIComponent(id)}`,
510
+ input
511
+ );
512
+ },
513
+ /** Transition status → PUBLISHED (sets publishedAt = now if unset). Admin mode only. */
514
+ publish: (id) => {
515
+ requireAdmin("publish");
516
+ return this.adminRequest(
517
+ "POST",
518
+ `${adminBase}/${encodeURIComponent(id)}/publish`
519
+ );
520
+ },
521
+ /** Transition status PUBLISHED → DRAFT. Admin mode only. */
522
+ unpublish: (id) => {
523
+ requireAdmin("unpublish");
524
+ return this.adminRequest(
525
+ "POST",
526
+ `${adminBase}/${encodeURIComponent(id)}/unpublish`
527
+ );
528
+ },
529
+ /** Hard-delete a blog post. Admin mode only. */
530
+ remove: async (id) => {
531
+ requireAdmin("remove");
532
+ await this.adminRequest("DELETE", `${adminBase}/${encodeURIComponent(id)}`);
533
+ }
534
+ };
535
+ })();
422
536
  // -------------------- Local Cart (Client-Side for Guests) --------------------
423
537
  // These methods store cart data in localStorage - NO API calls!
424
538
  // Use for guest users in vibe-coded sites
@@ -8042,6 +8156,9 @@ var ALLOWED_PAYMENT_HOSTS = [
8042
8156
  "grow.security",
8043
8157
  // CreditGuard
8044
8158
  "creditguard.co.il",
8159
+ // Takbull
8160
+ "takbull.co.il",
8161
+ "api.takbull.co.il",
8045
8162
  // Brainerce-hosted payment embeds (backend payment-embed proxy at
8046
8163
  // `/api/payment/embed/...` that fronts provider apps' embed shells —
8047
8164
  // e.g. cardcom-payments OpenFields wrapper). The match also covers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainerce",
3
- "version": "1.33.0",
3
+ "version": "1.34.0",
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",