brainerce 1.23.14 → 1.25.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
@@ -48,6 +48,7 @@ Every Brainerce storefront must include **all mandatory features** below. Featur
48
48
  | Account area (profile + order history) | `client.getMyProfile()`, `client.getMyOrders()` | ✅ |
49
49
  | Global header: cart count + search autocomplete | `client.getCart()`, `client.getSearchSuggestions(query)` | ✅ |
50
50
  | Discount banners + product badges | `client.getDiscountBanners()`, `client.getProductDiscountBadge(productId)` | ✅ |
51
+ | Product reviews on PDP + JSON-LD aggregateRating | `client.listProductReviews(id)`, `client.submitProductReview(id, …)` | ✅ |
51
52
  | Multi-language + RTL (when i18n enabled) | `client.setLocale()` | conditional |
52
53
 
53
54
  ---
package/dist/index.d.mts CHANGED
@@ -323,9 +323,55 @@ interface Product {
323
323
  * (`getProductBySlug`, `getProductById`); omitted from list responses.
324
324
  */
325
325
  modifierGroups?: ModifierGroup[];
326
+ /** Average rating across visible reviews (0 when reviewCount is 0). */
327
+ avgRating?: number;
328
+ /** Count of visible reviews (non-hidden). */
329
+ reviewCount?: number;
326
330
  createdAt: string;
327
331
  updatedAt: string;
328
332
  }
333
+ /**
334
+ * Product review submitted by a customer.
335
+ * Reviews publish immediately (no PENDING state). Merchants hide via the admin
336
+ * surface — hiddenAt is null for visible reviews.
337
+ */
338
+ interface ProductReview {
339
+ id: string;
340
+ productId: string;
341
+ authorName: string;
342
+ rating: number;
343
+ body: string | null;
344
+ verifiedPurchase: boolean;
345
+ /** Only present in admin responses; null on storefront responses. */
346
+ hiddenAt?: string | null;
347
+ createdAt: string;
348
+ }
349
+ /** Admin-mode review with full PII. Returned by `client.adminReviews.*`. */
350
+ interface ProductReviewAdmin extends ProductReview {
351
+ customerId: string | null;
352
+ authorEmail: string | null;
353
+ orderId: string | null;
354
+ updatedAt: string;
355
+ }
356
+ /**
357
+ * Body for customer-authenticated submit / update.
358
+ * Author name + email are derived server-side from the customer profile.
359
+ */
360
+ interface WriteProductReviewInput {
361
+ rating: number;
362
+ body?: string;
363
+ }
364
+ /**
365
+ * Returned by `client.getMyProductReview(productId)`. Tells the storefront which
366
+ * UI to render: sign-in / not-eligible / submit / edit.
367
+ */
368
+ interface MyProductReview {
369
+ eligible: boolean;
370
+ /** Machine-readable reason when not eligible. null when eligible=true. */
371
+ reason: 'no_eligible_order' | 'reviews_disabled' | 'product_not_found' | null;
372
+ /** The customer's existing review for this product, or null. */
373
+ myReview: ProductReview | null;
374
+ }
329
375
  interface ProductImage {
330
376
  url: string;
331
377
  position?: number;
@@ -6081,6 +6127,79 @@ declare class BrainerceClient {
6081
6127
  * ```
6082
6128
  */
6083
6129
  getProductRecommendations(productId: string, type?: ProductRelationType): Promise<ProductRecommendationsResponse>;
6130
+ /**
6131
+ * List visible reviews for a product (storefront / sales-channel modes).
6132
+ * Reviews that the merchant has hidden are excluded.
6133
+ *
6134
+ * @example
6135
+ * ```typescript
6136
+ * const { data, meta } = await client.listProductReviews('prod_123', { page: 1, limit: 20 });
6137
+ * data.forEach(r => console.log(r.rating, r.body, r.verifiedPurchase));
6138
+ * ```
6139
+ */
6140
+ listProductReviews(productId: string, params?: {
6141
+ page?: number;
6142
+ limit?: number;
6143
+ }): Promise<PaginatedResponse<ProductReview>>;
6144
+ /**
6145
+ * Get the current customer's review state for a product (storefront / sales-channel modes).
6146
+ * Requires a customer token (`setCustomerToken(...)` after login).
6147
+ *
6148
+ * Returns whether the customer is *eligible* to review (only purchasers are),
6149
+ * and their existing review for this product if any.
6150
+ *
6151
+ * @example
6152
+ * ```typescript
6153
+ * const { eligible, reason, myReview } = await client.getMyProductReview('prod_123');
6154
+ * if (!eligible) showMessage(reason); // 'no_eligible_order' | 'reviews_disabled' | …
6155
+ * else if (myReview) renderEditForm(myReview);
6156
+ * else renderSubmitForm();
6157
+ * ```
6158
+ */
6159
+ getMyProductReview(productId: string): Promise<MyProductReview>;
6160
+ /**
6161
+ * Submit a customer review for a product (storefront / sales-channel modes).
6162
+ * Requires customer authentication AND that the customer has purchased the
6163
+ * product (SHIPPED for physical, PAID for downloadable).
6164
+ *
6165
+ * 403 `no_eligible_order` if the customer didn't purchase the product.
6166
+ * 409 `Conflict` if they already submitted a review (use `updateMyProductReview` instead).
6167
+ *
6168
+ * Author name + email are derived server-side from the customer profile.
6169
+ *
6170
+ * @example
6171
+ * ```typescript
6172
+ * const review = await client.submitProductReview('prod_123', {
6173
+ * rating: 5,
6174
+ * body: 'Loved it!',
6175
+ * });
6176
+ * ```
6177
+ */
6178
+ submitProductReview(productId: string, input: WriteProductReviewInput): Promise<ProductReview>;
6179
+ /**
6180
+ * Edit your own review on a product (storefront / sales-channel modes).
6181
+ * Only updates rating + body; the author name/email and verifiedPurchase flag
6182
+ * are preserved from the original submit.
6183
+ */
6184
+ updateMyProductReview(productId: string, input: WriteProductReviewInput): Promise<ProductReview>;
6185
+ /**
6186
+ * Delete your own review on a product (storefront / sales-channel modes).
6187
+ * After deletion the customer is free to submit a new review (subject to eligibility).
6188
+ */
6189
+ deleteMyProductReview(productId: string): Promise<void>;
6190
+ /**
6191
+ * Admin: list all reviews for a product (incl. hidden). Requires API key with `reviews:read`.
6192
+ */
6193
+ adminListProductReviews(productId: string, params?: {
6194
+ storeId?: string;
6195
+ page?: number;
6196
+ limit?: number;
6197
+ visibility?: 'visible' | 'hidden' | 'all';
6198
+ }): Promise<PaginatedResponse<ProductReviewAdmin>>;
6199
+ /** Admin: hide a review (sets hiddenAt). */
6200
+ hideProductReview(reviewId: string, storeId?: string): Promise<ProductReviewAdmin>;
6201
+ /** Admin: unhide a previously hidden review. */
6202
+ showProductReview(reviewId: string, storeId?: string): Promise<ProductReviewAdmin>;
6084
6203
  /**
6085
6204
  * Get cross-sell recommendations based on cart contents.
6086
6205
  * Returns products that complement items already in the cart.
package/dist/index.d.ts CHANGED
@@ -323,9 +323,55 @@ interface Product {
323
323
  * (`getProductBySlug`, `getProductById`); omitted from list responses.
324
324
  */
325
325
  modifierGroups?: ModifierGroup[];
326
+ /** Average rating across visible reviews (0 when reviewCount is 0). */
327
+ avgRating?: number;
328
+ /** Count of visible reviews (non-hidden). */
329
+ reviewCount?: number;
326
330
  createdAt: string;
327
331
  updatedAt: string;
328
332
  }
333
+ /**
334
+ * Product review submitted by a customer.
335
+ * Reviews publish immediately (no PENDING state). Merchants hide via the admin
336
+ * surface — hiddenAt is null for visible reviews.
337
+ */
338
+ interface ProductReview {
339
+ id: string;
340
+ productId: string;
341
+ authorName: string;
342
+ rating: number;
343
+ body: string | null;
344
+ verifiedPurchase: boolean;
345
+ /** Only present in admin responses; null on storefront responses. */
346
+ hiddenAt?: string | null;
347
+ createdAt: string;
348
+ }
349
+ /** Admin-mode review with full PII. Returned by `client.adminReviews.*`. */
350
+ interface ProductReviewAdmin extends ProductReview {
351
+ customerId: string | null;
352
+ authorEmail: string | null;
353
+ orderId: string | null;
354
+ updatedAt: string;
355
+ }
356
+ /**
357
+ * Body for customer-authenticated submit / update.
358
+ * Author name + email are derived server-side from the customer profile.
359
+ */
360
+ interface WriteProductReviewInput {
361
+ rating: number;
362
+ body?: string;
363
+ }
364
+ /**
365
+ * Returned by `client.getMyProductReview(productId)`. Tells the storefront which
366
+ * UI to render: sign-in / not-eligible / submit / edit.
367
+ */
368
+ interface MyProductReview {
369
+ eligible: boolean;
370
+ /** Machine-readable reason when not eligible. null when eligible=true. */
371
+ reason: 'no_eligible_order' | 'reviews_disabled' | 'product_not_found' | null;
372
+ /** The customer's existing review for this product, or null. */
373
+ myReview: ProductReview | null;
374
+ }
329
375
  interface ProductImage {
330
376
  url: string;
331
377
  position?: number;
@@ -6081,6 +6127,79 @@ declare class BrainerceClient {
6081
6127
  * ```
6082
6128
  */
6083
6129
  getProductRecommendations(productId: string, type?: ProductRelationType): Promise<ProductRecommendationsResponse>;
6130
+ /**
6131
+ * List visible reviews for a product (storefront / sales-channel modes).
6132
+ * Reviews that the merchant has hidden are excluded.
6133
+ *
6134
+ * @example
6135
+ * ```typescript
6136
+ * const { data, meta } = await client.listProductReviews('prod_123', { page: 1, limit: 20 });
6137
+ * data.forEach(r => console.log(r.rating, r.body, r.verifiedPurchase));
6138
+ * ```
6139
+ */
6140
+ listProductReviews(productId: string, params?: {
6141
+ page?: number;
6142
+ limit?: number;
6143
+ }): Promise<PaginatedResponse<ProductReview>>;
6144
+ /**
6145
+ * Get the current customer's review state for a product (storefront / sales-channel modes).
6146
+ * Requires a customer token (`setCustomerToken(...)` after login).
6147
+ *
6148
+ * Returns whether the customer is *eligible* to review (only purchasers are),
6149
+ * and their existing review for this product if any.
6150
+ *
6151
+ * @example
6152
+ * ```typescript
6153
+ * const { eligible, reason, myReview } = await client.getMyProductReview('prod_123');
6154
+ * if (!eligible) showMessage(reason); // 'no_eligible_order' | 'reviews_disabled' | …
6155
+ * else if (myReview) renderEditForm(myReview);
6156
+ * else renderSubmitForm();
6157
+ * ```
6158
+ */
6159
+ getMyProductReview(productId: string): Promise<MyProductReview>;
6160
+ /**
6161
+ * Submit a customer review for a product (storefront / sales-channel modes).
6162
+ * Requires customer authentication AND that the customer has purchased the
6163
+ * product (SHIPPED for physical, PAID for downloadable).
6164
+ *
6165
+ * 403 `no_eligible_order` if the customer didn't purchase the product.
6166
+ * 409 `Conflict` if they already submitted a review (use `updateMyProductReview` instead).
6167
+ *
6168
+ * Author name + email are derived server-side from the customer profile.
6169
+ *
6170
+ * @example
6171
+ * ```typescript
6172
+ * const review = await client.submitProductReview('prod_123', {
6173
+ * rating: 5,
6174
+ * body: 'Loved it!',
6175
+ * });
6176
+ * ```
6177
+ */
6178
+ submitProductReview(productId: string, input: WriteProductReviewInput): Promise<ProductReview>;
6179
+ /**
6180
+ * Edit your own review on a product (storefront / sales-channel modes).
6181
+ * Only updates rating + body; the author name/email and verifiedPurchase flag
6182
+ * are preserved from the original submit.
6183
+ */
6184
+ updateMyProductReview(productId: string, input: WriteProductReviewInput): Promise<ProductReview>;
6185
+ /**
6186
+ * Delete your own review on a product (storefront / sales-channel modes).
6187
+ * After deletion the customer is free to submit a new review (subject to eligibility).
6188
+ */
6189
+ deleteMyProductReview(productId: string): Promise<void>;
6190
+ /**
6191
+ * Admin: list all reviews for a product (incl. hidden). Requires API key with `reviews:read`.
6192
+ */
6193
+ adminListProductReviews(productId: string, params?: {
6194
+ storeId?: string;
6195
+ page?: number;
6196
+ limit?: number;
6197
+ visibility?: 'visible' | 'hidden' | 'all';
6198
+ }): Promise<PaginatedResponse<ProductReviewAdmin>>;
6199
+ /** Admin: hide a review (sets hiddenAt). */
6200
+ hideProductReview(reviewId: string, storeId?: string): Promise<ProductReviewAdmin>;
6201
+ /** Admin: unhide a previously hidden review. */
6202
+ showProductReview(reviewId: string, storeId?: string): Promise<ProductReviewAdmin>;
6084
6203
  /**
6085
6204
  * Get cross-sell recommendations based on cart contents.
6086
6205
  * Returns products that complement items already in the cart.
package/dist/index.js CHANGED
@@ -2790,6 +2790,172 @@ var BrainerceClient = class {
2790
2790
  400
2791
2791
  );
2792
2792
  }
2793
+ // ===================================================
2794
+ // Product Reviews
2795
+ // ===================================================
2796
+ /**
2797
+ * List visible reviews for a product (storefront / sales-channel modes).
2798
+ * Reviews that the merchant has hidden are excluded.
2799
+ *
2800
+ * @example
2801
+ * ```typescript
2802
+ * const { data, meta } = await client.listProductReviews('prod_123', { page: 1, limit: 20 });
2803
+ * data.forEach(r => console.log(r.rating, r.body, r.verifiedPurchase));
2804
+ * ```
2805
+ */
2806
+ async listProductReviews(productId, params) {
2807
+ const queryParams = {};
2808
+ if (params?.page) queryParams.page = params.page;
2809
+ if (params?.limit) queryParams.limit = params.limit;
2810
+ if (this.isVibeCodedMode()) {
2811
+ return this.vibeCodedRequest(
2812
+ "GET",
2813
+ `/products/${productId}/reviews`,
2814
+ void 0,
2815
+ queryParams
2816
+ );
2817
+ }
2818
+ if (this.storeId && !this.apiKey) {
2819
+ return this.storefrontRequest(
2820
+ "GET",
2821
+ `/products/${productId}/reviews`,
2822
+ void 0,
2823
+ queryParams
2824
+ );
2825
+ }
2826
+ throw new BrainerceError("listProductReviews() requires vibe-coded or storefront mode", 400);
2827
+ }
2828
+ /**
2829
+ * Get the current customer's review state for a product (storefront / sales-channel modes).
2830
+ * Requires a customer token (`setCustomerToken(...)` after login).
2831
+ *
2832
+ * Returns whether the customer is *eligible* to review (only purchasers are),
2833
+ * and their existing review for this product if any.
2834
+ *
2835
+ * @example
2836
+ * ```typescript
2837
+ * const { eligible, reason, myReview } = await client.getMyProductReview('prod_123');
2838
+ * if (!eligible) showMessage(reason); // 'no_eligible_order' | 'reviews_disabled' | …
2839
+ * else if (myReview) renderEditForm(myReview);
2840
+ * else renderSubmitForm();
2841
+ * ```
2842
+ */
2843
+ async getMyProductReview(productId) {
2844
+ if (this.isVibeCodedMode()) {
2845
+ return this.vibeCodedRequest("GET", `/products/${productId}/reviews/me`);
2846
+ }
2847
+ if (this.storeId && !this.apiKey) {
2848
+ return this.storefrontRequest("GET", `/products/${productId}/reviews/me`);
2849
+ }
2850
+ throw new BrainerceError("getMyProductReview() requires vibe-coded or storefront mode", 400);
2851
+ }
2852
+ /**
2853
+ * Submit a customer review for a product (storefront / sales-channel modes).
2854
+ * Requires customer authentication AND that the customer has purchased the
2855
+ * product (SHIPPED for physical, PAID for downloadable).
2856
+ *
2857
+ * 403 `no_eligible_order` if the customer didn't purchase the product.
2858
+ * 409 `Conflict` if they already submitted a review (use `updateMyProductReview` instead).
2859
+ *
2860
+ * Author name + email are derived server-side from the customer profile.
2861
+ *
2862
+ * @example
2863
+ * ```typescript
2864
+ * const review = await client.submitProductReview('prod_123', {
2865
+ * rating: 5,
2866
+ * body: 'Loved it!',
2867
+ * });
2868
+ * ```
2869
+ */
2870
+ async submitProductReview(productId, input) {
2871
+ if (this.isVibeCodedMode()) {
2872
+ return this.vibeCodedRequest("POST", `/products/${productId}/reviews`, input);
2873
+ }
2874
+ if (this.storeId && !this.apiKey) {
2875
+ return this.storefrontRequest("POST", `/products/${productId}/reviews`, input);
2876
+ }
2877
+ throw new BrainerceError("submitProductReview() requires vibe-coded or storefront mode", 400);
2878
+ }
2879
+ /**
2880
+ * Edit your own review on a product (storefront / sales-channel modes).
2881
+ * Only updates rating + body; the author name/email and verifiedPurchase flag
2882
+ * are preserved from the original submit.
2883
+ */
2884
+ async updateMyProductReview(productId, input) {
2885
+ if (this.isVibeCodedMode()) {
2886
+ return this.vibeCodedRequest(
2887
+ "PATCH",
2888
+ `/products/${productId}/reviews/me`,
2889
+ input
2890
+ );
2891
+ }
2892
+ if (this.storeId && !this.apiKey) {
2893
+ return this.storefrontRequest(
2894
+ "PATCH",
2895
+ `/products/${productId}/reviews/me`,
2896
+ input
2897
+ );
2898
+ }
2899
+ throw new BrainerceError("updateMyProductReview() requires vibe-coded or storefront mode", 400);
2900
+ }
2901
+ /**
2902
+ * Delete your own review on a product (storefront / sales-channel modes).
2903
+ * After deletion the customer is free to submit a new review (subject to eligibility).
2904
+ */
2905
+ async deleteMyProductReview(productId) {
2906
+ if (this.isVibeCodedMode()) {
2907
+ await this.vibeCodedRequest("DELETE", `/products/${productId}/reviews/me`);
2908
+ return;
2909
+ }
2910
+ if (this.storeId && !this.apiKey) {
2911
+ await this.storefrontRequest("DELETE", `/products/${productId}/reviews/me`);
2912
+ return;
2913
+ }
2914
+ throw new BrainerceError("deleteMyProductReview() requires vibe-coded or storefront mode", 400);
2915
+ }
2916
+ /**
2917
+ * Admin: list all reviews for a product (incl. hidden). Requires API key with `reviews:read`.
2918
+ */
2919
+ async adminListProductReviews(productId, params) {
2920
+ if (!this.apiKey) {
2921
+ throw new BrainerceError("adminListProductReviews() requires admin (API key) mode", 400);
2922
+ }
2923
+ const queryParams = {};
2924
+ if (params?.storeId) queryParams.storeId = params.storeId;
2925
+ if (params?.page) queryParams.page = params.page;
2926
+ if (params?.limit) queryParams.limit = params.limit;
2927
+ if (params?.visibility) queryParams.visibility = params.visibility;
2928
+ return this.adminRequest(
2929
+ "GET",
2930
+ `/api/v1/products/${productId}/reviews`,
2931
+ void 0,
2932
+ queryParams
2933
+ );
2934
+ }
2935
+ /** Admin: hide a review (sets hiddenAt). */
2936
+ async hideProductReview(reviewId, storeId) {
2937
+ if (!this.apiKey) {
2938
+ throw new BrainerceError("hideProductReview() requires admin (API key) mode", 400);
2939
+ }
2940
+ return this.adminRequest(
2941
+ "PATCH",
2942
+ `/api/v1/reviews/${reviewId}/hide`,
2943
+ void 0,
2944
+ storeId ? { storeId } : void 0
2945
+ );
2946
+ }
2947
+ /** Admin: unhide a previously hidden review. */
2948
+ async showProductReview(reviewId, storeId) {
2949
+ if (!this.apiKey) {
2950
+ throw new BrainerceError("showProductReview() requires admin (API key) mode", 400);
2951
+ }
2952
+ return this.adminRequest(
2953
+ "PATCH",
2954
+ `/api/v1/reviews/${reviewId}/show`,
2955
+ void 0,
2956
+ storeId ? { storeId } : void 0
2957
+ );
2958
+ }
2793
2959
  /**
2794
2960
  * Get cross-sell recommendations based on cart contents.
2795
2961
  * Returns products that complement items already in the cart.
package/dist/index.mjs CHANGED
@@ -2727,6 +2727,172 @@ var BrainerceClient = class {
2727
2727
  400
2728
2728
  );
2729
2729
  }
2730
+ // ===================================================
2731
+ // Product Reviews
2732
+ // ===================================================
2733
+ /**
2734
+ * List visible reviews for a product (storefront / sales-channel modes).
2735
+ * Reviews that the merchant has hidden are excluded.
2736
+ *
2737
+ * @example
2738
+ * ```typescript
2739
+ * const { data, meta } = await client.listProductReviews('prod_123', { page: 1, limit: 20 });
2740
+ * data.forEach(r => console.log(r.rating, r.body, r.verifiedPurchase));
2741
+ * ```
2742
+ */
2743
+ async listProductReviews(productId, params) {
2744
+ const queryParams = {};
2745
+ if (params?.page) queryParams.page = params.page;
2746
+ if (params?.limit) queryParams.limit = params.limit;
2747
+ if (this.isVibeCodedMode()) {
2748
+ return this.vibeCodedRequest(
2749
+ "GET",
2750
+ `/products/${productId}/reviews`,
2751
+ void 0,
2752
+ queryParams
2753
+ );
2754
+ }
2755
+ if (this.storeId && !this.apiKey) {
2756
+ return this.storefrontRequest(
2757
+ "GET",
2758
+ `/products/${productId}/reviews`,
2759
+ void 0,
2760
+ queryParams
2761
+ );
2762
+ }
2763
+ throw new BrainerceError("listProductReviews() requires vibe-coded or storefront mode", 400);
2764
+ }
2765
+ /**
2766
+ * Get the current customer's review state for a product (storefront / sales-channel modes).
2767
+ * Requires a customer token (`setCustomerToken(...)` after login).
2768
+ *
2769
+ * Returns whether the customer is *eligible* to review (only purchasers are),
2770
+ * and their existing review for this product if any.
2771
+ *
2772
+ * @example
2773
+ * ```typescript
2774
+ * const { eligible, reason, myReview } = await client.getMyProductReview('prod_123');
2775
+ * if (!eligible) showMessage(reason); // 'no_eligible_order' | 'reviews_disabled' | …
2776
+ * else if (myReview) renderEditForm(myReview);
2777
+ * else renderSubmitForm();
2778
+ * ```
2779
+ */
2780
+ async getMyProductReview(productId) {
2781
+ if (this.isVibeCodedMode()) {
2782
+ return this.vibeCodedRequest("GET", `/products/${productId}/reviews/me`);
2783
+ }
2784
+ if (this.storeId && !this.apiKey) {
2785
+ return this.storefrontRequest("GET", `/products/${productId}/reviews/me`);
2786
+ }
2787
+ throw new BrainerceError("getMyProductReview() requires vibe-coded or storefront mode", 400);
2788
+ }
2789
+ /**
2790
+ * Submit a customer review for a product (storefront / sales-channel modes).
2791
+ * Requires customer authentication AND that the customer has purchased the
2792
+ * product (SHIPPED for physical, PAID for downloadable).
2793
+ *
2794
+ * 403 `no_eligible_order` if the customer didn't purchase the product.
2795
+ * 409 `Conflict` if they already submitted a review (use `updateMyProductReview` instead).
2796
+ *
2797
+ * Author name + email are derived server-side from the customer profile.
2798
+ *
2799
+ * @example
2800
+ * ```typescript
2801
+ * const review = await client.submitProductReview('prod_123', {
2802
+ * rating: 5,
2803
+ * body: 'Loved it!',
2804
+ * });
2805
+ * ```
2806
+ */
2807
+ async submitProductReview(productId, input) {
2808
+ if (this.isVibeCodedMode()) {
2809
+ return this.vibeCodedRequest("POST", `/products/${productId}/reviews`, input);
2810
+ }
2811
+ if (this.storeId && !this.apiKey) {
2812
+ return this.storefrontRequest("POST", `/products/${productId}/reviews`, input);
2813
+ }
2814
+ throw new BrainerceError("submitProductReview() requires vibe-coded or storefront mode", 400);
2815
+ }
2816
+ /**
2817
+ * Edit your own review on a product (storefront / sales-channel modes).
2818
+ * Only updates rating + body; the author name/email and verifiedPurchase flag
2819
+ * are preserved from the original submit.
2820
+ */
2821
+ async updateMyProductReview(productId, input) {
2822
+ if (this.isVibeCodedMode()) {
2823
+ return this.vibeCodedRequest(
2824
+ "PATCH",
2825
+ `/products/${productId}/reviews/me`,
2826
+ input
2827
+ );
2828
+ }
2829
+ if (this.storeId && !this.apiKey) {
2830
+ return this.storefrontRequest(
2831
+ "PATCH",
2832
+ `/products/${productId}/reviews/me`,
2833
+ input
2834
+ );
2835
+ }
2836
+ throw new BrainerceError("updateMyProductReview() requires vibe-coded or storefront mode", 400);
2837
+ }
2838
+ /**
2839
+ * Delete your own review on a product (storefront / sales-channel modes).
2840
+ * After deletion the customer is free to submit a new review (subject to eligibility).
2841
+ */
2842
+ async deleteMyProductReview(productId) {
2843
+ if (this.isVibeCodedMode()) {
2844
+ await this.vibeCodedRequest("DELETE", `/products/${productId}/reviews/me`);
2845
+ return;
2846
+ }
2847
+ if (this.storeId && !this.apiKey) {
2848
+ await this.storefrontRequest("DELETE", `/products/${productId}/reviews/me`);
2849
+ return;
2850
+ }
2851
+ throw new BrainerceError("deleteMyProductReview() requires vibe-coded or storefront mode", 400);
2852
+ }
2853
+ /**
2854
+ * Admin: list all reviews for a product (incl. hidden). Requires API key with `reviews:read`.
2855
+ */
2856
+ async adminListProductReviews(productId, params) {
2857
+ if (!this.apiKey) {
2858
+ throw new BrainerceError("adminListProductReviews() requires admin (API key) mode", 400);
2859
+ }
2860
+ const queryParams = {};
2861
+ if (params?.storeId) queryParams.storeId = params.storeId;
2862
+ if (params?.page) queryParams.page = params.page;
2863
+ if (params?.limit) queryParams.limit = params.limit;
2864
+ if (params?.visibility) queryParams.visibility = params.visibility;
2865
+ return this.adminRequest(
2866
+ "GET",
2867
+ `/api/v1/products/${productId}/reviews`,
2868
+ void 0,
2869
+ queryParams
2870
+ );
2871
+ }
2872
+ /** Admin: hide a review (sets hiddenAt). */
2873
+ async hideProductReview(reviewId, storeId) {
2874
+ if (!this.apiKey) {
2875
+ throw new BrainerceError("hideProductReview() requires admin (API key) mode", 400);
2876
+ }
2877
+ return this.adminRequest(
2878
+ "PATCH",
2879
+ `/api/v1/reviews/${reviewId}/hide`,
2880
+ void 0,
2881
+ storeId ? { storeId } : void 0
2882
+ );
2883
+ }
2884
+ /** Admin: unhide a previously hidden review. */
2885
+ async showProductReview(reviewId, storeId) {
2886
+ if (!this.apiKey) {
2887
+ throw new BrainerceError("showProductReview() requires admin (API key) mode", 400);
2888
+ }
2889
+ return this.adminRequest(
2890
+ "PATCH",
2891
+ `/api/v1/reviews/${reviewId}/show`,
2892
+ void 0,
2893
+ storeId ? { storeId } : void 0
2894
+ );
2895
+ }
2730
2896
  /**
2731
2897
  * Get cross-sell recommendations based on cart contents.
2732
2898
  * Returns products that complement items already in the cart.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainerce",
3
- "version": "1.23.14",
3
+ "version": "1.25.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",