brainerce 2.0.0 → 2.0.2

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/dist/index.js CHANGED
@@ -204,7 +204,7 @@ function isDevGuardsEnabled() {
204
204
  }
205
205
 
206
206
  // src/version.ts
207
- var SDK_VERSION = "2.0.0";
207
+ var SDK_VERSION = "2.0.1";
208
208
 
209
209
  // src/client.ts
210
210
  var DEFAULT_BASE_URL = "https://api.brainerce.com";
@@ -763,7 +763,7 @@ var _BrainerceClient = class _BrainerceClient {
763
763
  }
764
764
  if (!options.salesChannelId && options.connectionId) {
765
765
  console.warn(
766
- "BrainerceClient: `connectionId` is deprecated \u2014 use `salesChannelId` instead. `connectionId` will be removed in SDK 2.0."
766
+ "BrainerceClient: `connectionId` is deprecated \u2014 use `salesChannelId` instead. `connectionId` is a permanent backward-compat alias and is not scheduled for removal."
767
767
  );
768
768
  }
769
769
  if (options.apiKey && typeof window !== "undefined") {
@@ -2639,6 +2639,36 @@ var _BrainerceClient = class _BrainerceClient {
2639
2639
  async getOrderShipments(orderId) {
2640
2640
  return this.request("GET", `/api/v1/orders/${encodePathSegment(orderId)}/shipments`);
2641
2641
  }
2642
+ /**
2643
+ * Buy a return label the merchant sends to their customer to print.
2644
+ *
2645
+ * Requires admin mode (`apiKey`) with `FULFILL_ORDERS` permission — it
2646
+ * spends the store's carrier balance, same as {@link createShippingLabel}.
2647
+ *
2648
+ * Unlike {@link createShippingLabel}, this is **not** on the API-key `/v1`
2649
+ * surface — it calls the internal `/api/orders/:id/shipments/return-label`
2650
+ * route, which takes `storeId` explicitly rather than resolving it from the
2651
+ * key. There is no `rateId` in the body: a return is quoted and bought in
2652
+ * one call at the shipping app, because the carrier fixes a shipment as a
2653
+ * return when it is created and will not amend it afterwards.
2654
+ *
2655
+ * @example
2656
+ * ```typescript
2657
+ * const label = await client.createReturnLabel('store_abc', 'order_abc', {
2658
+ * reason: 'Wrong size',
2659
+ * returnForShipmentId: 'shp_original123',
2660
+ * });
2661
+ * console.log('Return label URL:', label.labelUrl);
2662
+ * ```
2663
+ */
2664
+ async createReturnLabel(storeId, orderId, data) {
2665
+ return this.adminRequest(
2666
+ "POST",
2667
+ `/api/orders/${encodePathSegment(orderId)}/shipments/return-label`,
2668
+ data,
2669
+ { storeId }
2670
+ );
2671
+ }
2642
2672
  /**
2643
2673
  * Cancel an order.
2644
2674
  *
@@ -10074,6 +10104,156 @@ var _BrainerceClient = class _BrainerceClient {
10074
10104
  `/api/v1/oauth-providers/${encodePathSegment(provider)}`
10075
10105
  );
10076
10106
  }
10107
+ // -------------------- Translations (Admin) --------------------
10108
+ // These methods require Admin mode (apiKey) with a scope-bearing key —
10109
+ // `products:read`/`products:write` cover most entity types via
10110
+ // StorePermissionGuard's STORE_PERMISSION_TO_SCOPE mapping (VIEW_PRODUCTS /
10111
+ // EDIT_PRODUCTS). Routes: /api/stores/:storeId/translations/...
10112
+ //
10113
+ // Reads and writes the SAME `translations` JSON blob the dashboard editor
10114
+ // uses — this is not a separate translation system, it's API-key access to
10115
+ // the dashboard's own persistence path. Use it for bulk-importing
10116
+ // pre-translated content or automating locale coverage without a human in
10117
+ // the dashboard.
10118
+ /**
10119
+ * Get translation completeness across every translatable entity type, for
10120
+ * one or more locales. Useful as a pre-flight before a bulk import to see
10121
+ * which entity types/locales still need coverage.
10122
+ * Requires Admin mode (apiKey).
10123
+ *
10124
+ * @param storeId - Store to inspect.
10125
+ * @param locales - BCP-47 locale codes to check (e.g. `['he', 'fr']`). Omit
10126
+ * or pass an empty array to get rows with `total` populated but no
10127
+ * locale breakdown.
10128
+ *
10129
+ * @example
10130
+ * ```typescript
10131
+ * const status = await client.getTranslationStatus('store_123', ['he', 'fr']);
10132
+ * const blogHe = status.find((s) => s.entityType === 'blogPost' && s.locale === 'he');
10133
+ * console.log(`${blogHe?.missing} blog posts still need Hebrew`);
10134
+ * ```
10135
+ */
10136
+ async getTranslationStatus(storeId, locales) {
10137
+ return this.adminRequest(
10138
+ "GET",
10139
+ `/api/stores/${encodePathSegment(storeId)}/translations/status`,
10140
+ void 0,
10141
+ locales && locales.length > 0 ? { locales: locales.join(",") } : void 0
10142
+ );
10143
+ }
10144
+ /**
10145
+ * Get every persisted translation for a single entity, keyed by locale.
10146
+ * Requires Admin mode (apiKey).
10147
+ *
10148
+ * @example
10149
+ * ```typescript
10150
+ * const translations = await client.getTranslations('store_123', 'product', 'prod_abc');
10151
+ * console.log(translations.he?.name); // Hebrew product name, if set
10152
+ * ```
10153
+ */
10154
+ async getTranslations(storeId, entityType, entityId) {
10155
+ return this.adminRequest(
10156
+ "GET",
10157
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}`
10158
+ );
10159
+ }
10160
+ /**
10161
+ * Set/update one locale's translation for an entity. Only the fields valid
10162
+ * for `entityType` are persisted (e.g. `title`/`excerpt`/`content` for
10163
+ * `blogPost`, `name`/`description` for `category`) — fields outside that
10164
+ * entity's allowlist are silently ignored server-side, and omitted fields
10165
+ * leave any existing translation for them untouched (this is a merge, not
10166
+ * a replace, of the locale's fields).
10167
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10168
+ * scope for the target entity type).
10169
+ *
10170
+ * @example
10171
+ * ```typescript
10172
+ * // Bulk-import a pre-translated blog post
10173
+ * await client.setTranslation('store_123', 'blogPost', 'post_abc', 'fr', {
10174
+ * title: 'Le titre en français',
10175
+ * excerpt: "L'extrait en français",
10176
+ * content: '<p>Le contenu en français</p>',
10177
+ * });
10178
+ * ```
10179
+ */
10180
+ async setTranslation(storeId, entityType, entityId, locale, fields) {
10181
+ return this.adminRequest(
10182
+ "PUT",
10183
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}/${encodePathSegment(locale)}`,
10184
+ fields
10185
+ );
10186
+ }
10187
+ /**
10188
+ * Delete one locale's translation for an entity. The entity's base
10189
+ * (default-locale) fields are unaffected.
10190
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10191
+ * scope for the target entity type).
10192
+ */
10193
+ async deleteTranslation(storeId, entityType, entityId, locale) {
10194
+ await this.adminRequest(
10195
+ "DELETE",
10196
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}/${encodePathSegment(locale)}`
10197
+ );
10198
+ }
10199
+ /**
10200
+ * AI-translate a single entity into one target locale and persist the
10201
+ * result inline (synchronous — the response already reflects the write).
10202
+ * Only fields that are still empty for `targetLocale` are filled; existing
10203
+ * translated values are never overwritten.
10204
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10205
+ * scope for the target entity type).
10206
+ *
10207
+ * @param sourceFields - Optional override of the source-language text to
10208
+ * translate from (e.g. unsaved edits from an open editor), instead of the
10209
+ * entity's persisted base fields. Keys outside the entity's translatable
10210
+ * field set are ignored.
10211
+ *
10212
+ * @example
10213
+ * ```typescript
10214
+ * const translations = await client.aiTranslateSingle('store_123', {
10215
+ * entityType: 'product',
10216
+ * entityId: 'prod_abc',
10217
+ * targetLocale: 'he',
10218
+ * });
10219
+ * ```
10220
+ */
10221
+ async aiTranslateSingle(storeId, input) {
10222
+ return this.adminRequest(
10223
+ "POST",
10224
+ `/api/stores/${encodePathSegment(storeId)}/translations/ai-translate-single`,
10225
+ input
10226
+ );
10227
+ }
10228
+ /**
10229
+ * Bulk AI-translate — enqueues a background job per entity (and, for
10230
+ * `entityType: 'attribute'`, one per attribute option too) rather than
10231
+ * translating inline. Returns the number of jobs queued, not the finished
10232
+ * translations; poll `getTranslationStatus` or `getTranslations` to see
10233
+ * results land.
10234
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10235
+ * scope for the target entity type).
10236
+ *
10237
+ * @param entityIds - Optional explicit ids to translate. Omit to target
10238
+ * every entity of `entityType` in the store that isn't already fully
10239
+ * translated for `targetLocale`.
10240
+ *
10241
+ * @example
10242
+ * ```typescript
10243
+ * // Translate every blog post missing French coverage
10244
+ * const { queued } = await client.aiTranslateBulk('store_123', {
10245
+ * entityType: 'blogPost',
10246
+ * targetLocale: 'fr',
10247
+ * });
10248
+ * ```
10249
+ */
10250
+ async aiTranslateBulk(storeId, input) {
10251
+ return this.adminRequest(
10252
+ "POST",
10253
+ `/api/stores/${encodePathSegment(storeId)}/translations/ai-translate`,
10254
+ input
10255
+ );
10256
+ }
10077
10257
  };
10078
10258
  /**
10079
10259
  * Fields present on `getAddressDetails().address` that the address endpoints
package/dist/index.mjs CHANGED
@@ -115,7 +115,7 @@ function isDevGuardsEnabled() {
115
115
  }
116
116
 
117
117
  // src/version.ts
118
- var SDK_VERSION = "2.0.0";
118
+ var SDK_VERSION = "2.0.1";
119
119
 
120
120
  // src/client.ts
121
121
  var DEFAULT_BASE_URL = "https://api.brainerce.com";
@@ -674,7 +674,7 @@ var _BrainerceClient = class _BrainerceClient {
674
674
  }
675
675
  if (!options.salesChannelId && options.connectionId) {
676
676
  console.warn(
677
- "BrainerceClient: `connectionId` is deprecated \u2014 use `salesChannelId` instead. `connectionId` will be removed in SDK 2.0."
677
+ "BrainerceClient: `connectionId` is deprecated \u2014 use `salesChannelId` instead. `connectionId` is a permanent backward-compat alias and is not scheduled for removal."
678
678
  );
679
679
  }
680
680
  if (options.apiKey && typeof window !== "undefined") {
@@ -2550,6 +2550,36 @@ var _BrainerceClient = class _BrainerceClient {
2550
2550
  async getOrderShipments(orderId) {
2551
2551
  return this.request("GET", `/api/v1/orders/${encodePathSegment(orderId)}/shipments`);
2552
2552
  }
2553
+ /**
2554
+ * Buy a return label the merchant sends to their customer to print.
2555
+ *
2556
+ * Requires admin mode (`apiKey`) with `FULFILL_ORDERS` permission — it
2557
+ * spends the store's carrier balance, same as {@link createShippingLabel}.
2558
+ *
2559
+ * Unlike {@link createShippingLabel}, this is **not** on the API-key `/v1`
2560
+ * surface — it calls the internal `/api/orders/:id/shipments/return-label`
2561
+ * route, which takes `storeId` explicitly rather than resolving it from the
2562
+ * key. There is no `rateId` in the body: a return is quoted and bought in
2563
+ * one call at the shipping app, because the carrier fixes a shipment as a
2564
+ * return when it is created and will not amend it afterwards.
2565
+ *
2566
+ * @example
2567
+ * ```typescript
2568
+ * const label = await client.createReturnLabel('store_abc', 'order_abc', {
2569
+ * reason: 'Wrong size',
2570
+ * returnForShipmentId: 'shp_original123',
2571
+ * });
2572
+ * console.log('Return label URL:', label.labelUrl);
2573
+ * ```
2574
+ */
2575
+ async createReturnLabel(storeId, orderId, data) {
2576
+ return this.adminRequest(
2577
+ "POST",
2578
+ `/api/orders/${encodePathSegment(orderId)}/shipments/return-label`,
2579
+ data,
2580
+ { storeId }
2581
+ );
2582
+ }
2553
2583
  /**
2554
2584
  * Cancel an order.
2555
2585
  *
@@ -9985,6 +10015,156 @@ var _BrainerceClient = class _BrainerceClient {
9985
10015
  `/api/v1/oauth-providers/${encodePathSegment(provider)}`
9986
10016
  );
9987
10017
  }
10018
+ // -------------------- Translations (Admin) --------------------
10019
+ // These methods require Admin mode (apiKey) with a scope-bearing key —
10020
+ // `products:read`/`products:write` cover most entity types via
10021
+ // StorePermissionGuard's STORE_PERMISSION_TO_SCOPE mapping (VIEW_PRODUCTS /
10022
+ // EDIT_PRODUCTS). Routes: /api/stores/:storeId/translations/...
10023
+ //
10024
+ // Reads and writes the SAME `translations` JSON blob the dashboard editor
10025
+ // uses — this is not a separate translation system, it's API-key access to
10026
+ // the dashboard's own persistence path. Use it for bulk-importing
10027
+ // pre-translated content or automating locale coverage without a human in
10028
+ // the dashboard.
10029
+ /**
10030
+ * Get translation completeness across every translatable entity type, for
10031
+ * one or more locales. Useful as a pre-flight before a bulk import to see
10032
+ * which entity types/locales still need coverage.
10033
+ * Requires Admin mode (apiKey).
10034
+ *
10035
+ * @param storeId - Store to inspect.
10036
+ * @param locales - BCP-47 locale codes to check (e.g. `['he', 'fr']`). Omit
10037
+ * or pass an empty array to get rows with `total` populated but no
10038
+ * locale breakdown.
10039
+ *
10040
+ * @example
10041
+ * ```typescript
10042
+ * const status = await client.getTranslationStatus('store_123', ['he', 'fr']);
10043
+ * const blogHe = status.find((s) => s.entityType === 'blogPost' && s.locale === 'he');
10044
+ * console.log(`${blogHe?.missing} blog posts still need Hebrew`);
10045
+ * ```
10046
+ */
10047
+ async getTranslationStatus(storeId, locales) {
10048
+ return this.adminRequest(
10049
+ "GET",
10050
+ `/api/stores/${encodePathSegment(storeId)}/translations/status`,
10051
+ void 0,
10052
+ locales && locales.length > 0 ? { locales: locales.join(",") } : void 0
10053
+ );
10054
+ }
10055
+ /**
10056
+ * Get every persisted translation for a single entity, keyed by locale.
10057
+ * Requires Admin mode (apiKey).
10058
+ *
10059
+ * @example
10060
+ * ```typescript
10061
+ * const translations = await client.getTranslations('store_123', 'product', 'prod_abc');
10062
+ * console.log(translations.he?.name); // Hebrew product name, if set
10063
+ * ```
10064
+ */
10065
+ async getTranslations(storeId, entityType, entityId) {
10066
+ return this.adminRequest(
10067
+ "GET",
10068
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}`
10069
+ );
10070
+ }
10071
+ /**
10072
+ * Set/update one locale's translation for an entity. Only the fields valid
10073
+ * for `entityType` are persisted (e.g. `title`/`excerpt`/`content` for
10074
+ * `blogPost`, `name`/`description` for `category`) — fields outside that
10075
+ * entity's allowlist are silently ignored server-side, and omitted fields
10076
+ * leave any existing translation for them untouched (this is a merge, not
10077
+ * a replace, of the locale's fields).
10078
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10079
+ * scope for the target entity type).
10080
+ *
10081
+ * @example
10082
+ * ```typescript
10083
+ * // Bulk-import a pre-translated blog post
10084
+ * await client.setTranslation('store_123', 'blogPost', 'post_abc', 'fr', {
10085
+ * title: 'Le titre en français',
10086
+ * excerpt: "L'extrait en français",
10087
+ * content: '<p>Le contenu en français</p>',
10088
+ * });
10089
+ * ```
10090
+ */
10091
+ async setTranslation(storeId, entityType, entityId, locale, fields) {
10092
+ return this.adminRequest(
10093
+ "PUT",
10094
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}/${encodePathSegment(locale)}`,
10095
+ fields
10096
+ );
10097
+ }
10098
+ /**
10099
+ * Delete one locale's translation for an entity. The entity's base
10100
+ * (default-locale) fields are unaffected.
10101
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10102
+ * scope for the target entity type).
10103
+ */
10104
+ async deleteTranslation(storeId, entityType, entityId, locale) {
10105
+ await this.adminRequest(
10106
+ "DELETE",
10107
+ `/api/stores/${encodePathSegment(storeId)}/translations/${encodePathSegment(entityType)}/${encodePathSegment(entityId)}/${encodePathSegment(locale)}`
10108
+ );
10109
+ }
10110
+ /**
10111
+ * AI-translate a single entity into one target locale and persist the
10112
+ * result inline (synchronous — the response already reflects the write).
10113
+ * Only fields that are still empty for `targetLocale` are filled; existing
10114
+ * translated values are never overwritten.
10115
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10116
+ * scope for the target entity type).
10117
+ *
10118
+ * @param sourceFields - Optional override of the source-language text to
10119
+ * translate from (e.g. unsaved edits from an open editor), instead of the
10120
+ * entity's persisted base fields. Keys outside the entity's translatable
10121
+ * field set are ignored.
10122
+ *
10123
+ * @example
10124
+ * ```typescript
10125
+ * const translations = await client.aiTranslateSingle('store_123', {
10126
+ * entityType: 'product',
10127
+ * entityId: 'prod_abc',
10128
+ * targetLocale: 'he',
10129
+ * });
10130
+ * ```
10131
+ */
10132
+ async aiTranslateSingle(storeId, input) {
10133
+ return this.adminRequest(
10134
+ "POST",
10135
+ `/api/stores/${encodePathSegment(storeId)}/translations/ai-translate-single`,
10136
+ input
10137
+ );
10138
+ }
10139
+ /**
10140
+ * Bulk AI-translate — enqueues a background job per entity (and, for
10141
+ * `entityType: 'attribute'`, one per attribute option too) rather than
10142
+ * translating inline. Returns the number of jobs queued, not the finished
10143
+ * translations; poll `getTranslationStatus` or `getTranslations` to see
10144
+ * results land.
10145
+ * Requires Admin mode (apiKey) with `products:write` (or the equivalent
10146
+ * scope for the target entity type).
10147
+ *
10148
+ * @param entityIds - Optional explicit ids to translate. Omit to target
10149
+ * every entity of `entityType` in the store that isn't already fully
10150
+ * translated for `targetLocale`.
10151
+ *
10152
+ * @example
10153
+ * ```typescript
10154
+ * // Translate every blog post missing French coverage
10155
+ * const { queued } = await client.aiTranslateBulk('store_123', {
10156
+ * entityType: 'blogPost',
10157
+ * targetLocale: 'fr',
10158
+ * });
10159
+ * ```
10160
+ */
10161
+ async aiTranslateBulk(storeId, input) {
10162
+ return this.adminRequest(
10163
+ "POST",
10164
+ `/api/stores/${encodePathSegment(storeId)}/translations/ai-translate`,
10165
+ input
10166
+ );
10167
+ }
9988
10168
  };
9989
10169
  /**
9990
10170
  * Fields present on `getAddressDetails().address` that the address endpoints
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainerce",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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",