brainerce 1.58.0 → 1.59.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 +450 -129
- package/dist/index.d.mts +349 -33
- package/dist/index.d.ts +349 -33
- package/dist/index.js +142 -23
- package/dist/index.mjs +142 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1999,6 +1999,98 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
1999
1999
|
async createProduct(data) {
|
|
2000
2000
|
return this.request("POST", "/api/v1/products", data);
|
|
2001
2001
|
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Create many products in one call.
|
|
2004
|
+
*
|
|
2005
|
+
* QUEUED, not immediate: this returns a job id straight away and the products
|
|
2006
|
+
* appear over the following seconds or minutes. It does NOT return the
|
|
2007
|
+
* created products — poll {@link getBulkCreateProductsStatus} with the
|
|
2008
|
+
* returned `jobId`.
|
|
2009
|
+
*
|
|
2010
|
+
* Every field {@link createProduct} accepts is accepted per row, including
|
|
2011
|
+
* variants, categories, brands, tags, images, translations and tax behaviour.
|
|
2012
|
+
*
|
|
2013
|
+
* At most 1000 products per call (500 is the comfortable size). For a
|
|
2014
|
+
* 3,000-50,000 product catalog, send several calls carrying the same
|
|
2015
|
+
* `importId` and poll {@link getBulkCreateProductsImportStatus} once for the
|
|
2016
|
+
* whole import.
|
|
2017
|
+
*
|
|
2018
|
+
* Retry-safe: a row whose `sku` or `externalId` already exists is skipped
|
|
2019
|
+
* rather than duplicated, so re-sending a batch after a timeout cannot create
|
|
2020
|
+
* the catalog twice. Pass `idempotencyKey` to have an identical re-send
|
|
2021
|
+
* return the original job instead of starting a second import.
|
|
2022
|
+
*
|
|
2023
|
+
* @example
|
|
2024
|
+
* ```typescript
|
|
2025
|
+
* const job = await client.bulkCreateProducts({
|
|
2026
|
+
* products: rows.map((r) => ({
|
|
2027
|
+
* name: r.title,
|
|
2028
|
+
* sku: r.sku,
|
|
2029
|
+
* externalId: r.supplier_id,
|
|
2030
|
+
* basePrice: Number(r.price),
|
|
2031
|
+
* type: 'SIMPLE',
|
|
2032
|
+
* categoryNames: [r.category],
|
|
2033
|
+
* })),
|
|
2034
|
+
* importId: 'supplier-catalog-2026-08-21',
|
|
2035
|
+
* });
|
|
2036
|
+
*
|
|
2037
|
+
* let status = await client.getBulkCreateProductsStatus(job.jobId);
|
|
2038
|
+
* while (status.status === 'QUEUED' || status.status === 'RUNNING') {
|
|
2039
|
+
* await new Promise((r) => setTimeout(r, 2000));
|
|
2040
|
+
* status = await client.getBulkCreateProductsStatus(job.jobId);
|
|
2041
|
+
* }
|
|
2042
|
+
* // `succeeded` is what was created; `skipped` already existed.
|
|
2043
|
+
* console.log(status.succeeded, status.skipped, status.failed);
|
|
2044
|
+
* ```
|
|
2045
|
+
*/
|
|
2046
|
+
async bulkCreateProducts(data) {
|
|
2047
|
+
return this.request("POST", "/api/v1/products/bulk", data);
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Progress of one queued product import.
|
|
2051
|
+
*
|
|
2052
|
+
* Read the counters literally: `skipped` rows already existed and were NOT
|
|
2053
|
+
* created, so `succeeded` alone is what this import added.
|
|
2054
|
+
*
|
|
2055
|
+
* `COMPLETED_WITH_ERRORS` means the import finished with some rows failing —
|
|
2056
|
+
* that is not something to re-run; read the failures with
|
|
2057
|
+
* {@link getBulkCreateProductsErrors}.
|
|
2058
|
+
*/
|
|
2059
|
+
async getBulkCreateProductsStatus(jobId) {
|
|
2060
|
+
return this.request(
|
|
2061
|
+
"GET",
|
|
2062
|
+
`/api/v1/products/bulk/${encodePathSegment(jobId)}`
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Aggregate progress across every batch that shared an `importId`.
|
|
2067
|
+
*
|
|
2068
|
+
* The status is the least-complete state across the chunks, and `finishedAt`
|
|
2069
|
+
* stays null until all of them have finished — so a caller cannot mistake
|
|
2070
|
+
* "the first 500 landed" for "the catalog is imported".
|
|
2071
|
+
*/
|
|
2072
|
+
async getBulkCreateProductsImportStatus(importId) {
|
|
2073
|
+
return this.request(
|
|
2074
|
+
"GET",
|
|
2075
|
+
`/api/v1/products/bulk/import/${encodePathSegment(importId)}`
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Per-row failures for a product import, paginated.
|
|
2080
|
+
*
|
|
2081
|
+
* Every failure is recorded — nothing is truncated — so walk the pages when
|
|
2082
|
+
* `meta.totalPages > 1`. Each entry carries the 1-indexed `row` from the
|
|
2083
|
+
* array you submitted, so a failure maps back to the line of the source
|
|
2084
|
+
* spreadsheet.
|
|
2085
|
+
*/
|
|
2086
|
+
async getBulkCreateProductsErrors(jobId, options) {
|
|
2087
|
+
return this.request(
|
|
2088
|
+
"GET",
|
|
2089
|
+
`/api/v1/products/bulk/${encodePathSegment(jobId)}/errors`,
|
|
2090
|
+
void 0,
|
|
2091
|
+
{ page: options?.page, limit: options?.limit }
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2002
2094
|
/**
|
|
2003
2095
|
* Update an existing product
|
|
2004
2096
|
*/
|
|
@@ -3265,11 +3357,18 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
3265
3357
|
* @param options.redirectUrl - Where to send the browser once OAuth finishes —
|
|
3266
3358
|
* on success *and* on failure. Validated server-side against the sales
|
|
3267
3359
|
* channel's trusted origins, so what is accepted depends on the mode:
|
|
3268
|
-
* - vibe-coded (`salesChannelId: 'vc_*'`)
|
|
3269
|
-
* registered `domain
|
|
3270
|
-
* `
|
|
3271
|
-
*
|
|
3272
|
-
*
|
|
3360
|
+
* - vibe-coded (`salesChannelId: 'vc_*'`), **LIVE**: an `https` URL on the
|
|
3361
|
+
* channel's registered `domain`, or on a subdomain of it. Nothing else —
|
|
3362
|
+
* `allowedOrigins` grants no OAuth redirect on a LIVE channel, and an
|
|
3363
|
+
* `http://` target is refused even on the registered domain. You
|
|
3364
|
+
* therefore cannot complete social login from `localhost` against a LIVE
|
|
3365
|
+
* channel; use a TEST channel for that.
|
|
3366
|
+
* - vibe-coded, **TEST**: the above, plus an **exact** match (scheme, host
|
|
3367
|
+
* and port) against one of the channel's `allowedOrigins`, plus any
|
|
3368
|
+
* `localhost`/`127.0.0.1`/`[::1]` port.
|
|
3369
|
+
* - Either mode: a relative path (`/auth/callback`) also works — it is
|
|
3370
|
+
* resolved against the channel's `domain` on the way back, so the channel
|
|
3371
|
+
* must have one registered.
|
|
3273
3372
|
* - storefront (`storeId`): **social login cannot round-trip in this mode.**
|
|
3274
3373
|
* No channel is bound to the request, so an absolute URL has no
|
|
3275
3374
|
* trusted-origin list to match (400 at this call) and a relative path has
|
|
@@ -4106,7 +4205,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4106
4205
|
* try {
|
|
4107
4206
|
* await client.createCheckout(cartId);
|
|
4108
4207
|
* } catch (err) {
|
|
4109
|
-
*
|
|
4208
|
+
* // BrainerceError.details is the whole response body — the code lives
|
|
4209
|
+
* // there, NOT on the error object itself.
|
|
4210
|
+
* if ((err as BrainerceError).details?.code === 'PRICE_DRIFT') {
|
|
4110
4211
|
* // ask user to confirm new prices, then:
|
|
4111
4212
|
* await client.refreshCartSnapshots(cartId);
|
|
4112
4213
|
* await client.createCheckout(cartId);
|
|
@@ -5944,6 +6045,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5944
6045
|
* Get applicable custom field definitions for a checkout.
|
|
5945
6046
|
* Returns fields filtered by visibility conditions (delivery type, products in cart).
|
|
5946
6047
|
* Use these to render dynamic input fields in the checkout flow.
|
|
6048
|
+
*
|
|
6049
|
+
* **Vibe-coded or storefront mode only.** There is no checkout custom-field
|
|
6050
|
+
* route on the API-key `/v1` surface; in admin mode this throws.
|
|
5947
6051
|
*/
|
|
5948
6052
|
async getCheckoutCustomFields(checkoutId) {
|
|
5949
6053
|
if (this.isVibeCodedMode()) {
|
|
@@ -5958,15 +6062,18 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5958
6062
|
`/checkout/${encodePathSegment(checkoutId)}/custom-fields`
|
|
5959
6063
|
);
|
|
5960
6064
|
}
|
|
5961
|
-
|
|
5962
|
-
"
|
|
5963
|
-
|
|
6065
|
+
throw new BrainerceError(
|
|
6066
|
+
"getCheckoutCustomFields is only available in vibe-coded or storefront mode",
|
|
6067
|
+
400
|
|
5964
6068
|
);
|
|
5965
6069
|
}
|
|
5966
6070
|
/**
|
|
5967
6071
|
* Set checkout custom field values and recalculate surcharges.
|
|
5968
6072
|
* The checkout total is automatically updated to include surcharges.
|
|
5969
6073
|
*
|
|
6074
|
+
* **Vibe-coded or storefront mode only.** There is no checkout custom-field
|
|
6075
|
+
* route on the API-key `/v1` surface; in admin mode this throws.
|
|
6076
|
+
*
|
|
5970
6077
|
* @example
|
|
5971
6078
|
* ```typescript
|
|
5972
6079
|
* const checkout = await client.setCheckoutCustomFields(checkoutId, {
|
|
@@ -5994,10 +6101,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5994
6101
|
data
|
|
5995
6102
|
);
|
|
5996
6103
|
}
|
|
5997
|
-
|
|
5998
|
-
"
|
|
5999
|
-
|
|
6000
|
-
data
|
|
6104
|
+
throw new BrainerceError(
|
|
6105
|
+
"setCheckoutCustomFields is only available in vibe-coded or storefront mode",
|
|
6106
|
+
400
|
|
6001
6107
|
);
|
|
6002
6108
|
}
|
|
6003
6109
|
/**
|
|
@@ -8263,8 +8369,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
8263
8369
|
// /api/stores/:storeId/products/:productId/modifier-groups[/:attachmentId]
|
|
8264
8370
|
//
|
|
8265
8371
|
// Server-side validation failures arrive as a structured 400 envelope:
|
|
8266
|
-
// { code: 'MODIFIER_VALIDATION_FAILED', errors:
|
|
8267
|
-
//
|
|
8372
|
+
// { code: 'MODIFIER_VALIDATION_FAILED', message, details: { errors: [...] } }
|
|
8373
|
+
// BrainerceError.details holds the WHOLE body, so the issue list is
|
|
8374
|
+
// `err.details.details.errors` — see ModifierValidationFailedError.
|
|
8268
8375
|
/**
|
|
8269
8376
|
* List modifier groups in a store, paginated.
|
|
8270
8377
|
* Requires Admin mode (apiKey).
|
|
@@ -9316,27 +9423,36 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9316
9423
|
throw new Error("uploadCustomizationFile requires storefront or vibe-coded mode");
|
|
9317
9424
|
}
|
|
9318
9425
|
// -------------------- Team Management (Admin) - DEPRECATED --------------------
|
|
9319
|
-
//
|
|
9426
|
+
// Account-level team methods. These are the ONLY team endpoints reachable with
|
|
9427
|
+
// a `brainerce_*` API key. The store-level equivalents (`getStoreTeam`,
|
|
9428
|
+
// `inviteStoreMember`, `updateStoreMember`, ...) sit behind
|
|
9429
|
+
// `DashboardOnlyGuard`, which rejects api_key principals by design — so they
|
|
9430
|
+
// return 403 from the SDK, not 404, and "fixing" their path would not help.
|
|
9431
|
+
// Do not migrate SDK code onto them.
|
|
9320
9432
|
/**
|
|
9321
|
-
* @deprecated
|
|
9433
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `getStoreTeam`
|
|
9434
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9322
9435
|
*/
|
|
9323
9436
|
async getTeamMembers() {
|
|
9324
9437
|
return this.adminRequest("GET", "/api/v1/team/members");
|
|
9325
9438
|
}
|
|
9326
9439
|
/**
|
|
9327
|
-
* @deprecated
|
|
9440
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `getStoreTeam`
|
|
9441
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9328
9442
|
*/
|
|
9329
9443
|
async getTeamInvitations() {
|
|
9330
9444
|
return this.adminRequest("GET", "/api/v1/team/invitations");
|
|
9331
9445
|
}
|
|
9332
9446
|
/**
|
|
9333
|
-
* @deprecated
|
|
9447
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `inviteStoreMember`
|
|
9448
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9334
9449
|
*/
|
|
9335
9450
|
async inviteTeamMember(data) {
|
|
9336
9451
|
return this.adminRequest("POST", "/api/v1/team/invitations", data);
|
|
9337
9452
|
}
|
|
9338
9453
|
/**
|
|
9339
|
-
* @deprecated
|
|
9454
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `resendStoreInvitation`
|
|
9455
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9340
9456
|
*/
|
|
9341
9457
|
async resendTeamInvitation(invitationId) {
|
|
9342
9458
|
return this.adminRequest(
|
|
@@ -9345,7 +9461,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9345
9461
|
);
|
|
9346
9462
|
}
|
|
9347
9463
|
/**
|
|
9348
|
-
* @deprecated
|
|
9464
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `revokeStoreInvitation`
|
|
9465
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9349
9466
|
*/
|
|
9350
9467
|
async revokeTeamInvitation(invitationId) {
|
|
9351
9468
|
await this.adminRequest(
|
|
@@ -9354,7 +9471,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9354
9471
|
);
|
|
9355
9472
|
}
|
|
9356
9473
|
/**
|
|
9357
|
-
* @deprecated
|
|
9474
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `updateStoreMember`
|
|
9475
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9358
9476
|
*/
|
|
9359
9477
|
async updateTeamMemberRole(memberId, data) {
|
|
9360
9478
|
return this.adminRequest(
|
|
@@ -9364,7 +9482,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9364
9482
|
);
|
|
9365
9483
|
}
|
|
9366
9484
|
/**
|
|
9367
|
-
* @deprecated
|
|
9485
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `removeStoreMember`
|
|
9486
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9368
9487
|
*/
|
|
9369
9488
|
async removeTeamMember(memberId) {
|
|
9370
9489
|
await this.adminRequest("DELETE", `/api/v1/team/members/${encodePathSegment(memberId)}`);
|
package/dist/index.mjs
CHANGED
|
@@ -1911,6 +1911,98 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
1911
1911
|
async createProduct(data) {
|
|
1912
1912
|
return this.request("POST", "/api/v1/products", data);
|
|
1913
1913
|
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Create many products in one call.
|
|
1916
|
+
*
|
|
1917
|
+
* QUEUED, not immediate: this returns a job id straight away and the products
|
|
1918
|
+
* appear over the following seconds or minutes. It does NOT return the
|
|
1919
|
+
* created products — poll {@link getBulkCreateProductsStatus} with the
|
|
1920
|
+
* returned `jobId`.
|
|
1921
|
+
*
|
|
1922
|
+
* Every field {@link createProduct} accepts is accepted per row, including
|
|
1923
|
+
* variants, categories, brands, tags, images, translations and tax behaviour.
|
|
1924
|
+
*
|
|
1925
|
+
* At most 1000 products per call (500 is the comfortable size). For a
|
|
1926
|
+
* 3,000-50,000 product catalog, send several calls carrying the same
|
|
1927
|
+
* `importId` and poll {@link getBulkCreateProductsImportStatus} once for the
|
|
1928
|
+
* whole import.
|
|
1929
|
+
*
|
|
1930
|
+
* Retry-safe: a row whose `sku` or `externalId` already exists is skipped
|
|
1931
|
+
* rather than duplicated, so re-sending a batch after a timeout cannot create
|
|
1932
|
+
* the catalog twice. Pass `idempotencyKey` to have an identical re-send
|
|
1933
|
+
* return the original job instead of starting a second import.
|
|
1934
|
+
*
|
|
1935
|
+
* @example
|
|
1936
|
+
* ```typescript
|
|
1937
|
+
* const job = await client.bulkCreateProducts({
|
|
1938
|
+
* products: rows.map((r) => ({
|
|
1939
|
+
* name: r.title,
|
|
1940
|
+
* sku: r.sku,
|
|
1941
|
+
* externalId: r.supplier_id,
|
|
1942
|
+
* basePrice: Number(r.price),
|
|
1943
|
+
* type: 'SIMPLE',
|
|
1944
|
+
* categoryNames: [r.category],
|
|
1945
|
+
* })),
|
|
1946
|
+
* importId: 'supplier-catalog-2026-08-21',
|
|
1947
|
+
* });
|
|
1948
|
+
*
|
|
1949
|
+
* let status = await client.getBulkCreateProductsStatus(job.jobId);
|
|
1950
|
+
* while (status.status === 'QUEUED' || status.status === 'RUNNING') {
|
|
1951
|
+
* await new Promise((r) => setTimeout(r, 2000));
|
|
1952
|
+
* status = await client.getBulkCreateProductsStatus(job.jobId);
|
|
1953
|
+
* }
|
|
1954
|
+
* // `succeeded` is what was created; `skipped` already existed.
|
|
1955
|
+
* console.log(status.succeeded, status.skipped, status.failed);
|
|
1956
|
+
* ```
|
|
1957
|
+
*/
|
|
1958
|
+
async bulkCreateProducts(data) {
|
|
1959
|
+
return this.request("POST", "/api/v1/products/bulk", data);
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Progress of one queued product import.
|
|
1963
|
+
*
|
|
1964
|
+
* Read the counters literally: `skipped` rows already existed and were NOT
|
|
1965
|
+
* created, so `succeeded` alone is what this import added.
|
|
1966
|
+
*
|
|
1967
|
+
* `COMPLETED_WITH_ERRORS` means the import finished with some rows failing —
|
|
1968
|
+
* that is not something to re-run; read the failures with
|
|
1969
|
+
* {@link getBulkCreateProductsErrors}.
|
|
1970
|
+
*/
|
|
1971
|
+
async getBulkCreateProductsStatus(jobId) {
|
|
1972
|
+
return this.request(
|
|
1973
|
+
"GET",
|
|
1974
|
+
`/api/v1/products/bulk/${encodePathSegment(jobId)}`
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Aggregate progress across every batch that shared an `importId`.
|
|
1979
|
+
*
|
|
1980
|
+
* The status is the least-complete state across the chunks, and `finishedAt`
|
|
1981
|
+
* stays null until all of them have finished — so a caller cannot mistake
|
|
1982
|
+
* "the first 500 landed" for "the catalog is imported".
|
|
1983
|
+
*/
|
|
1984
|
+
async getBulkCreateProductsImportStatus(importId) {
|
|
1985
|
+
return this.request(
|
|
1986
|
+
"GET",
|
|
1987
|
+
`/api/v1/products/bulk/import/${encodePathSegment(importId)}`
|
|
1988
|
+
);
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Per-row failures for a product import, paginated.
|
|
1992
|
+
*
|
|
1993
|
+
* Every failure is recorded — nothing is truncated — so walk the pages when
|
|
1994
|
+
* `meta.totalPages > 1`. Each entry carries the 1-indexed `row` from the
|
|
1995
|
+
* array you submitted, so a failure maps back to the line of the source
|
|
1996
|
+
* spreadsheet.
|
|
1997
|
+
*/
|
|
1998
|
+
async getBulkCreateProductsErrors(jobId, options) {
|
|
1999
|
+
return this.request(
|
|
2000
|
+
"GET",
|
|
2001
|
+
`/api/v1/products/bulk/${encodePathSegment(jobId)}/errors`,
|
|
2002
|
+
void 0,
|
|
2003
|
+
{ page: options?.page, limit: options?.limit }
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
1914
2006
|
/**
|
|
1915
2007
|
* Update an existing product
|
|
1916
2008
|
*/
|
|
@@ -3177,11 +3269,18 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
3177
3269
|
* @param options.redirectUrl - Where to send the browser once OAuth finishes —
|
|
3178
3270
|
* on success *and* on failure. Validated server-side against the sales
|
|
3179
3271
|
* channel's trusted origins, so what is accepted depends on the mode:
|
|
3180
|
-
* - vibe-coded (`salesChannelId: 'vc_*'`)
|
|
3181
|
-
* registered `domain
|
|
3182
|
-
* `
|
|
3183
|
-
*
|
|
3184
|
-
*
|
|
3272
|
+
* - vibe-coded (`salesChannelId: 'vc_*'`), **LIVE**: an `https` URL on the
|
|
3273
|
+
* channel's registered `domain`, or on a subdomain of it. Nothing else —
|
|
3274
|
+
* `allowedOrigins` grants no OAuth redirect on a LIVE channel, and an
|
|
3275
|
+
* `http://` target is refused even on the registered domain. You
|
|
3276
|
+
* therefore cannot complete social login from `localhost` against a LIVE
|
|
3277
|
+
* channel; use a TEST channel for that.
|
|
3278
|
+
* - vibe-coded, **TEST**: the above, plus an **exact** match (scheme, host
|
|
3279
|
+
* and port) against one of the channel's `allowedOrigins`, plus any
|
|
3280
|
+
* `localhost`/`127.0.0.1`/`[::1]` port.
|
|
3281
|
+
* - Either mode: a relative path (`/auth/callback`) also works — it is
|
|
3282
|
+
* resolved against the channel's `domain` on the way back, so the channel
|
|
3283
|
+
* must have one registered.
|
|
3185
3284
|
* - storefront (`storeId`): **social login cannot round-trip in this mode.**
|
|
3186
3285
|
* No channel is bound to the request, so an absolute URL has no
|
|
3187
3286
|
* trusted-origin list to match (400 at this call) and a relative path has
|
|
@@ -4018,7 +4117,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4018
4117
|
* try {
|
|
4019
4118
|
* await client.createCheckout(cartId);
|
|
4020
4119
|
* } catch (err) {
|
|
4021
|
-
*
|
|
4120
|
+
* // BrainerceError.details is the whole response body — the code lives
|
|
4121
|
+
* // there, NOT on the error object itself.
|
|
4122
|
+
* if ((err as BrainerceError).details?.code === 'PRICE_DRIFT') {
|
|
4022
4123
|
* // ask user to confirm new prices, then:
|
|
4023
4124
|
* await client.refreshCartSnapshots(cartId);
|
|
4024
4125
|
* await client.createCheckout(cartId);
|
|
@@ -5856,6 +5957,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5856
5957
|
* Get applicable custom field definitions for a checkout.
|
|
5857
5958
|
* Returns fields filtered by visibility conditions (delivery type, products in cart).
|
|
5858
5959
|
* Use these to render dynamic input fields in the checkout flow.
|
|
5960
|
+
*
|
|
5961
|
+
* **Vibe-coded or storefront mode only.** There is no checkout custom-field
|
|
5962
|
+
* route on the API-key `/v1` surface; in admin mode this throws.
|
|
5859
5963
|
*/
|
|
5860
5964
|
async getCheckoutCustomFields(checkoutId) {
|
|
5861
5965
|
if (this.isVibeCodedMode()) {
|
|
@@ -5870,15 +5974,18 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5870
5974
|
`/checkout/${encodePathSegment(checkoutId)}/custom-fields`
|
|
5871
5975
|
);
|
|
5872
5976
|
}
|
|
5873
|
-
|
|
5874
|
-
"
|
|
5875
|
-
|
|
5977
|
+
throw new BrainerceError(
|
|
5978
|
+
"getCheckoutCustomFields is only available in vibe-coded or storefront mode",
|
|
5979
|
+
400
|
|
5876
5980
|
);
|
|
5877
5981
|
}
|
|
5878
5982
|
/**
|
|
5879
5983
|
* Set checkout custom field values and recalculate surcharges.
|
|
5880
5984
|
* The checkout total is automatically updated to include surcharges.
|
|
5881
5985
|
*
|
|
5986
|
+
* **Vibe-coded or storefront mode only.** There is no checkout custom-field
|
|
5987
|
+
* route on the API-key `/v1` surface; in admin mode this throws.
|
|
5988
|
+
*
|
|
5882
5989
|
* @example
|
|
5883
5990
|
* ```typescript
|
|
5884
5991
|
* const checkout = await client.setCheckoutCustomFields(checkoutId, {
|
|
@@ -5906,10 +6013,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5906
6013
|
data
|
|
5907
6014
|
);
|
|
5908
6015
|
}
|
|
5909
|
-
|
|
5910
|
-
"
|
|
5911
|
-
|
|
5912
|
-
data
|
|
6016
|
+
throw new BrainerceError(
|
|
6017
|
+
"setCheckoutCustomFields is only available in vibe-coded or storefront mode",
|
|
6018
|
+
400
|
|
5913
6019
|
);
|
|
5914
6020
|
}
|
|
5915
6021
|
/**
|
|
@@ -8175,8 +8281,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
8175
8281
|
// /api/stores/:storeId/products/:productId/modifier-groups[/:attachmentId]
|
|
8176
8282
|
//
|
|
8177
8283
|
// Server-side validation failures arrive as a structured 400 envelope:
|
|
8178
|
-
// { code: 'MODIFIER_VALIDATION_FAILED', errors:
|
|
8179
|
-
//
|
|
8284
|
+
// { code: 'MODIFIER_VALIDATION_FAILED', message, details: { errors: [...] } }
|
|
8285
|
+
// BrainerceError.details holds the WHOLE body, so the issue list is
|
|
8286
|
+
// `err.details.details.errors` — see ModifierValidationFailedError.
|
|
8180
8287
|
/**
|
|
8181
8288
|
* List modifier groups in a store, paginated.
|
|
8182
8289
|
* Requires Admin mode (apiKey).
|
|
@@ -9228,27 +9335,36 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9228
9335
|
throw new Error("uploadCustomizationFile requires storefront or vibe-coded mode");
|
|
9229
9336
|
}
|
|
9230
9337
|
// -------------------- Team Management (Admin) - DEPRECATED --------------------
|
|
9231
|
-
//
|
|
9338
|
+
// Account-level team methods. These are the ONLY team endpoints reachable with
|
|
9339
|
+
// a `brainerce_*` API key. The store-level equivalents (`getStoreTeam`,
|
|
9340
|
+
// `inviteStoreMember`, `updateStoreMember`, ...) sit behind
|
|
9341
|
+
// `DashboardOnlyGuard`, which rejects api_key principals by design — so they
|
|
9342
|
+
// return 403 from the SDK, not 404, and "fixing" their path would not help.
|
|
9343
|
+
// Do not migrate SDK code onto them.
|
|
9232
9344
|
/**
|
|
9233
|
-
* @deprecated
|
|
9345
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `getStoreTeam`
|
|
9346
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9234
9347
|
*/
|
|
9235
9348
|
async getTeamMembers() {
|
|
9236
9349
|
return this.adminRequest("GET", "/api/v1/team/members");
|
|
9237
9350
|
}
|
|
9238
9351
|
/**
|
|
9239
|
-
* @deprecated
|
|
9352
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `getStoreTeam`
|
|
9353
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9240
9354
|
*/
|
|
9241
9355
|
async getTeamInvitations() {
|
|
9242
9356
|
return this.adminRequest("GET", "/api/v1/team/invitations");
|
|
9243
9357
|
}
|
|
9244
9358
|
/**
|
|
9245
|
-
* @deprecated
|
|
9359
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `inviteStoreMember`
|
|
9360
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9246
9361
|
*/
|
|
9247
9362
|
async inviteTeamMember(data) {
|
|
9248
9363
|
return this.adminRequest("POST", "/api/v1/team/invitations", data);
|
|
9249
9364
|
}
|
|
9250
9365
|
/**
|
|
9251
|
-
* @deprecated
|
|
9366
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `resendStoreInvitation`
|
|
9367
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9252
9368
|
*/
|
|
9253
9369
|
async resendTeamInvitation(invitationId) {
|
|
9254
9370
|
return this.adminRequest(
|
|
@@ -9257,7 +9373,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9257
9373
|
);
|
|
9258
9374
|
}
|
|
9259
9375
|
/**
|
|
9260
|
-
* @deprecated
|
|
9376
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `revokeStoreInvitation`
|
|
9377
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9261
9378
|
*/
|
|
9262
9379
|
async revokeTeamInvitation(invitationId) {
|
|
9263
9380
|
await this.adminRequest(
|
|
@@ -9266,7 +9383,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9266
9383
|
);
|
|
9267
9384
|
}
|
|
9268
9385
|
/**
|
|
9269
|
-
* @deprecated
|
|
9386
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `updateStoreMember`
|
|
9387
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9270
9388
|
*/
|
|
9271
9389
|
async updateTeamMemberRole(memberId, data) {
|
|
9272
9390
|
return this.adminRequest(
|
|
@@ -9276,7 +9394,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9276
9394
|
);
|
|
9277
9395
|
}
|
|
9278
9396
|
/**
|
|
9279
|
-
* @deprecated
|
|
9397
|
+
* @deprecated Retiring, but there is no API-key replacement yet: `removeStoreMember`
|
|
9398
|
+
* is dashboard-only (403 for api_key). Keep using this until one ships.
|
|
9280
9399
|
*/
|
|
9281
9400
|
async removeTeamMember(memberId) {
|
|
9282
9401
|
await this.adminRequest("DELETE", `/api/v1/team/members/${encodePathSegment(memberId)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.59.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",
|