brainerce 2.1.0 → 2.3.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/LICENSE +21 -0
- package/README.md +8090 -7618
- package/dist/index.d.mts +500 -41
- package/dist/index.d.ts +500 -41
- package/dist/index.js +464 -147
- package/dist/index.mjs +464 -147
- package/package.json +86 -84
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
|
|
118
|
+
var SDK_VERSION = "2.3.0";
|
|
119
119
|
|
|
120
120
|
// src/client.ts
|
|
121
121
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -361,7 +361,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
361
361
|
* Works in all three SDK modes (vibe-coded, storefront, admin):
|
|
362
362
|
* - **Public reads** (`get`, `list`, `getBySlug`): work in any mode.
|
|
363
363
|
* - **Write operations** (`create`, `update`, `publish`, `unpublish`,
|
|
364
|
-
* `remove`): admin mode only — they call `/api/
|
|
364
|
+
* `remove`): admin mode only — they call `/api/content/...` with
|
|
365
365
|
* the API key. Calling from storefront / vibe-coded mode throws.
|
|
366
366
|
*
|
|
367
367
|
* **Default key:** every type has `'main'` as its universal default key.
|
|
@@ -398,7 +398,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
398
398
|
this.content = (() => {
|
|
399
399
|
const DEFAULT_KEY = "main";
|
|
400
400
|
const publicGetPath = (type, key) => `/content/${encodeURIComponent(type)}/${encodeURIComponent(key)}`;
|
|
401
|
-
const adminBase = () => "/api/
|
|
401
|
+
const adminBase = () => "/api/content";
|
|
402
402
|
const publicGet = async (type, key, locale) => {
|
|
403
403
|
const query = locale ? { locale } : void 0;
|
|
404
404
|
const path = publicGetPath(type, key);
|
|
@@ -837,10 +837,19 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
837
837
|
this.customerToken = null;
|
|
838
838
|
}
|
|
839
839
|
// -------------------- Private Methods --------------------
|
|
840
|
+
/**
|
|
841
|
+
* Turn an `idempotencyKey` option into the header the backend reads.
|
|
842
|
+
*
|
|
843
|
+
* Returns `undefined` when there is no key, so the header is absent rather
|
|
844
|
+
* than empty — the interceptor rejects a present-but-blank key with a 400.
|
|
845
|
+
*/
|
|
846
|
+
idempotencyHeaders(options) {
|
|
847
|
+
return options?.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : void 0;
|
|
848
|
+
}
|
|
840
849
|
/**
|
|
841
850
|
* Make a request to the Admin API (requires apiKey)
|
|
842
851
|
*/
|
|
843
|
-
async adminRequest(method, path, body, queryParams, responseType = "json") {
|
|
852
|
+
async adminRequest(method, path, body, queryParams, responseType = "json", extraHeaders) {
|
|
844
853
|
if (!this.apiKey) {
|
|
845
854
|
throw new BrainerceError(
|
|
846
855
|
"This operation requires an API key. Initialize with apiKey instead of storeId.",
|
|
@@ -870,6 +879,11 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
870
879
|
if (this.locale) {
|
|
871
880
|
headers["Accept-Language"] = this.locale;
|
|
872
881
|
}
|
|
882
|
+
if (extraHeaders) {
|
|
883
|
+
for (const [key, value] of Object.entries(extraHeaders)) {
|
|
884
|
+
if (value) headers[key] = value;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
873
887
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
874
888
|
const controller = new AbortController();
|
|
875
889
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -2018,11 +2032,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2018
2032
|
queryParams
|
|
2019
2033
|
);
|
|
2020
2034
|
}
|
|
2021
|
-
|
|
2022
|
-
"
|
|
2023
|
-
|
|
2024
|
-
void 0,
|
|
2025
|
-
queryParams
|
|
2035
|
+
throw new BrainerceError(
|
|
2036
|
+
"getSearchSuggestions is a storefront API. There is no admin suggestions endpoint \u2014 construct the client with `salesChannelId` or `storeId` to use it, or call getProducts({ search }) in admin mode.",
|
|
2037
|
+
400
|
|
2026
2038
|
);
|
|
2027
2039
|
}
|
|
2028
2040
|
/**
|
|
@@ -2196,21 +2208,25 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2196
2208
|
);
|
|
2197
2209
|
}
|
|
2198
2210
|
/**
|
|
2199
|
-
* Publish a product to specific platforms
|
|
2211
|
+
* Publish a product to specific platforms.
|
|
2200
2212
|
*
|
|
2201
|
-
*
|
|
2202
|
-
*
|
|
2203
|
-
*
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2213
|
+
* **Not callable.** The API-key `/v1` surface has no `products/:id/publish`
|
|
2214
|
+
* route. Platform publishing for products exists only on the dashboard
|
|
2215
|
+
* surface (`POST /api/products/:id/publish?storeId=`,
|
|
2216
|
+
* `products.controller.ts:556`), which resolves the acting user from a
|
|
2217
|
+
* dashboard session an API key does not carry. The generic trigger
|
|
2218
|
+
* `POST /v1/sync` answers `501 Not Implemented` and points at per-resource
|
|
2219
|
+
* publish endpoints — and products have none.
|
|
2220
|
+
*
|
|
2221
|
+
* {@link publishProductToSalesChannel} is a DIFFERENT operation: it controls
|
|
2222
|
+
* visibility on a vibe-coded storefront, not a push to an external platform.
|
|
2206
2223
|
*/
|
|
2207
2224
|
async publishProduct(productId, platforms) {
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
}
|
|
2225
|
+
void productId;
|
|
2226
|
+
void platforms;
|
|
2227
|
+
throw new BrainerceError(
|
|
2228
|
+
"publishProduct is not a route on the API-key /v1 surface. There is no products/:id/publish endpoint to call, so nothing was published; publish the product to a platform from the Brainerce dashboard. Note that publishProductToSalesChannel is a different operation \u2014 it controls vibe-coded storefront visibility, not an external-platform push.",
|
|
2229
|
+
400
|
|
2214
2230
|
);
|
|
2215
2231
|
}
|
|
2216
2232
|
// -------------------- Variants --------------------
|
|
@@ -4205,6 +4221,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4205
4221
|
* flags reflect the live state. Call this on cart load if you want to
|
|
4206
4222
|
* surface drift to the customer before they reach checkout.
|
|
4207
4223
|
*
|
|
4224
|
+
* **Storefront and vibe-coded modes only.** There is no admin (`apiKey`)
|
|
4225
|
+
* route for this, so an admin-mode client throws instead of 404ing.
|
|
4226
|
+
*
|
|
4208
4227
|
* @example
|
|
4209
4228
|
* ```typescript
|
|
4210
4229
|
* const cart = await client.recalculateCart('cart_123');
|
|
@@ -4229,9 +4248,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4229
4248
|
"cart"
|
|
4230
4249
|
);
|
|
4231
4250
|
}
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4251
|
+
throw new BrainerceError(
|
|
4252
|
+
"recalculateCart is not a route on the API-key /v1 surface. There is no cart/:cartId/recalculate endpoint to call, so nothing was recalculated. Cart recalculation is a storefront operation \u2014 call it from a client constructed with `salesChannelId` (vibe-coded) or `storeId` (public storefront), where the route exists.",
|
|
4253
|
+
400
|
|
4235
4254
|
);
|
|
4236
4255
|
}
|
|
4237
4256
|
/**
|
|
@@ -4240,6 +4259,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4240
4259
|
* subsequent `createCheckout` call will then succeed (it would otherwise
|
|
4241
4260
|
* throw `PRICE_DRIFT`).
|
|
4242
4261
|
*
|
|
4262
|
+
* **Storefront and vibe-coded modes only.** There is no admin (`apiKey`)
|
|
4263
|
+
* route for this, so an admin-mode client throws instead of 404ing.
|
|
4264
|
+
*
|
|
4243
4265
|
* @example
|
|
4244
4266
|
* ```typescript
|
|
4245
4267
|
* try {
|
|
@@ -4274,12 +4296,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
4274
4296
|
"cart"
|
|
4275
4297
|
);
|
|
4276
4298
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
`/api/v1/cart/${encodePathSegment(cartId)}/refresh-snapshots`
|
|
4281
|
-
),
|
|
4282
|
-
"cart"
|
|
4299
|
+
throw new BrainerceError(
|
|
4300
|
+
"refreshCartSnapshots is not a route on the API-key /v1 surface. There is no cart/:cartId/refresh-snapshots endpoint to call, so no price snapshots were refreshed. Snapshot refresh is a storefront operation \u2014 call it from a client constructed with `salesChannelId` (vibe-coded) or `storeId` (public storefront), where the route exists.",
|
|
4301
|
+
400
|
|
4283
4302
|
);
|
|
4284
4303
|
}
|
|
4285
4304
|
/**
|
|
@@ -5723,6 +5742,202 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5723
5742
|
return this.adminRequest("POST", "/api/v1/gift-cards/balance", { code });
|
|
5724
5743
|
}
|
|
5725
5744
|
// ==========================================================================
|
|
5745
|
+
// Gift cards — administration (API key only)
|
|
5746
|
+
//
|
|
5747
|
+
// Full programmatic management, unlike Shopify, which requires you to ask
|
|
5748
|
+
// their support for the equivalent scope. The gate here is the SCOPE the
|
|
5749
|
+
// merchant granted your key, so the separation matters: `gift_cards:issue`
|
|
5750
|
+
// mints stored value and `gift_cards:adjust` rewrites a balance, and neither
|
|
5751
|
+
// is implied by `gift_cards:read`. Ask for the least your integration needs.
|
|
5752
|
+
//
|
|
5753
|
+
// Every call reaches the same service the dashboard uses, so the rules hold
|
|
5754
|
+
// identically: a note is mandatory on anything that moves value, a debit
|
|
5755
|
+
// cannot cross what live checkout holds have reserved, and there is no delete
|
|
5756
|
+
// anywhere — the ledger is append-only.
|
|
5757
|
+
// ==========================================================================
|
|
5758
|
+
/**
|
|
5759
|
+
* List gift cards.
|
|
5760
|
+
*
|
|
5761
|
+
* `search` matches the LAST FOUR of a code or part of a recipient email. It
|
|
5762
|
+
* cannot match a full code: only an HMAC is stored, so there is nothing to
|
|
5763
|
+
* search against.
|
|
5764
|
+
*
|
|
5765
|
+
* Requires `gift_cards:read`.
|
|
5766
|
+
*/
|
|
5767
|
+
async listGiftCards(params) {
|
|
5768
|
+
const q = new URLSearchParams();
|
|
5769
|
+
if (params?.page) q.set("page", String(params.page));
|
|
5770
|
+
if (params?.limit) q.set("limit", String(params.limit));
|
|
5771
|
+
if (params?.filter) q.set("filter", params.filter);
|
|
5772
|
+
if (params?.search) q.set("search", params.search);
|
|
5773
|
+
const qs = q.toString();
|
|
5774
|
+
return this.adminRequest(
|
|
5775
|
+
"GET",
|
|
5776
|
+
`/api/v1/gift-cards${qs ? `?${qs}` : ""}`
|
|
5777
|
+
);
|
|
5778
|
+
}
|
|
5779
|
+
/**
|
|
5780
|
+
* Outstanding gift card liability, per currency.
|
|
5781
|
+
*
|
|
5782
|
+
* This is the month-end number. Read `byCurrency` if the store sells in more
|
|
5783
|
+
* than one — currencies are never summed together.
|
|
5784
|
+
*
|
|
5785
|
+
* Requires `gift_cards:read`.
|
|
5786
|
+
*/
|
|
5787
|
+
async getGiftCardLiability() {
|
|
5788
|
+
return this.adminRequest("GET", "/api/v1/gift-cards/liability");
|
|
5789
|
+
}
|
|
5790
|
+
/**
|
|
5791
|
+
* One gift card with its full ledger.
|
|
5792
|
+
*
|
|
5793
|
+
* Requires `gift_cards:read`.
|
|
5794
|
+
*/
|
|
5795
|
+
async getGiftCard(giftCardId) {
|
|
5796
|
+
return this.adminRequest(
|
|
5797
|
+
"GET",
|
|
5798
|
+
`/api/v1/gift-cards/${encodePathSegment(giftCardId)}`
|
|
5799
|
+
);
|
|
5800
|
+
}
|
|
5801
|
+
/**
|
|
5802
|
+
* Issue a gift card.
|
|
5803
|
+
*
|
|
5804
|
+
* ⚠️ **The code comes back exactly once.** It is stored only as an HMAC, so
|
|
5805
|
+
* this response is the only time it exists in readable form anywhere. Persist
|
|
5806
|
+
* it or deliver it before you discard the response — no later call, dashboard
|
|
5807
|
+
* screen or database query can recover it.
|
|
5808
|
+
*
|
|
5809
|
+
* A `note` is required. Refused when gift cards are switched off for the
|
|
5810
|
+
* store.
|
|
5811
|
+
*
|
|
5812
|
+
* ⚠️ **Pass an `idempotencyKey`.** It is the ONLY recovery that exists here:
|
|
5813
|
+
* re-sending the identical request with the same key inside 24 hours replays
|
|
5814
|
+
* the cached response, code included. Without one, a retried timeout mints a
|
|
5815
|
+
* SECOND card and a second real liability. The key is sent as the
|
|
5816
|
+
* `Idempotency-Key` header; reuse the same value across retries of the same
|
|
5817
|
+
* logical issue, never a fresh random one.
|
|
5818
|
+
*
|
|
5819
|
+
* Requires `gift_cards:issue`.
|
|
5820
|
+
*
|
|
5821
|
+
* @example
|
|
5822
|
+
* ```typescript
|
|
5823
|
+
* const card = await client.issueGiftCard(
|
|
5824
|
+
* {
|
|
5825
|
+
* amount: '200.00',
|
|
5826
|
+
* note: 'Compensation for order #1042',
|
|
5827
|
+
* recipientEmail: 'dana@example.com',
|
|
5828
|
+
* },
|
|
5829
|
+
* { idempotencyKey: 'compensation-order-1042' }
|
|
5830
|
+
* );
|
|
5831
|
+
* await sendToCustomer(card.plaintextCode); // your only chance
|
|
5832
|
+
* ```
|
|
5833
|
+
*/
|
|
5834
|
+
async issueGiftCard(data, options) {
|
|
5835
|
+
return this.adminRequest(
|
|
5836
|
+
"POST",
|
|
5837
|
+
"/api/v1/gift-cards",
|
|
5838
|
+
data,
|
|
5839
|
+
void 0,
|
|
5840
|
+
"json",
|
|
5841
|
+
this.idempotencyHeaders(options)
|
|
5842
|
+
);
|
|
5843
|
+
}
|
|
5844
|
+
/**
|
|
5845
|
+
* Re-issue a gift card onto a new code.
|
|
5846
|
+
*
|
|
5847
|
+
* The answer to a customer losing their code. Mints a new code, moves the
|
|
5848
|
+
* WHOLE balance to it, and REVOKES the old card.
|
|
5849
|
+
*
|
|
5850
|
+
* **This is not a resend.** The old code stops working the moment this
|
|
5851
|
+
* returns — if the customer still holds a printed card, it dies. Refused
|
|
5852
|
+
* while a checkout holds value on the card. The original expiry carries
|
|
5853
|
+
* forward, so this cannot be used to restart an expiry clock.
|
|
5854
|
+
*
|
|
5855
|
+
* The new code is returned exactly once, under the same rules as issuance —
|
|
5856
|
+
* so pass an `idempotencyKey` here for the same reason, and with the same
|
|
5857
|
+
* force: a retried timeout without one revokes the replacement it just made
|
|
5858
|
+
* and mints another.
|
|
5859
|
+
*
|
|
5860
|
+
* Requires `gift_cards:issue`.
|
|
5861
|
+
*/
|
|
5862
|
+
async reissueGiftCard(giftCardId, note, options) {
|
|
5863
|
+
return this.adminRequest(
|
|
5864
|
+
"POST",
|
|
5865
|
+
`/api/v1/gift-cards/${encodePathSegment(giftCardId)}/reissue`,
|
|
5866
|
+
{ note },
|
|
5867
|
+
void 0,
|
|
5868
|
+
"json",
|
|
5869
|
+
this.idempotencyHeaders(options)
|
|
5870
|
+
);
|
|
5871
|
+
}
|
|
5872
|
+
/**
|
|
5873
|
+
* Adjust a gift card balance.
|
|
5874
|
+
*
|
|
5875
|
+
* `delta` is a SIGNED decimal string: `"25.00"` adds, `"-25.00"` takes away.
|
|
5876
|
+
* The `note` is required and is written to the ledger permanently — it is the
|
|
5877
|
+
* row a finance review reads a year from now.
|
|
5878
|
+
*
|
|
5879
|
+
* A debit cannot take the balance below what live checkout holds have already
|
|
5880
|
+
* reserved; that refusal names the held amount so you can act on it.
|
|
5881
|
+
*
|
|
5882
|
+
* Pass an `idempotencyKey`: an adjustment is a relative move, so a retried
|
|
5883
|
+
* timeout without one applies the delta twice.
|
|
5884
|
+
*
|
|
5885
|
+
* Requires `gift_cards:adjust`.
|
|
5886
|
+
*/
|
|
5887
|
+
async adjustGiftCardBalance(giftCardId, delta, note, options) {
|
|
5888
|
+
return this.adminRequest(
|
|
5889
|
+
"PATCH",
|
|
5890
|
+
`/api/v1/gift-cards/${encodePathSegment(giftCardId)}/adjust`,
|
|
5891
|
+
{ delta, note },
|
|
5892
|
+
void 0,
|
|
5893
|
+
"json",
|
|
5894
|
+
this.idempotencyHeaders(options)
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
/**
|
|
5898
|
+
* Enable, disable or revoke one gift card.
|
|
5899
|
+
*
|
|
5900
|
+
* Deliberately does NOT touch live holds: a checkout that already reserved
|
|
5901
|
+
* value settles normally, because pulling it out from under a shopper
|
|
5902
|
+
* mid-payment would strand a provider charge already in flight. Disabling
|
|
5903
|
+
* stops NEW holds, which is what "off" actually means.
|
|
5904
|
+
*
|
|
5905
|
+
* Requires `gift_cards:write`.
|
|
5906
|
+
*/
|
|
5907
|
+
async setGiftCardStatus(giftCardId, status, options) {
|
|
5908
|
+
return this.adminRequest(
|
|
5909
|
+
"PATCH",
|
|
5910
|
+
`/api/v1/gift-cards/${encodePathSegment(giftCardId)}/status`,
|
|
5911
|
+
{ status },
|
|
5912
|
+
void 0,
|
|
5913
|
+
"json",
|
|
5914
|
+
this.idempotencyHeaders(options)
|
|
5915
|
+
);
|
|
5916
|
+
}
|
|
5917
|
+
/**
|
|
5918
|
+
* Disable or reactivate many gift cards at once.
|
|
5919
|
+
*
|
|
5920
|
+
* `REVOKED` is not accepted here — it belongs to re-issue, which moves the
|
|
5921
|
+
* balance to a replacement first. Revoking in bulk would strand balances with
|
|
5922
|
+
* nowhere to go. Cards already revoked are skipped, so the returned count is
|
|
5923
|
+
* the honest one and may be lower than the ids you sent.
|
|
5924
|
+
*
|
|
5925
|
+
* There is no bulk delete, here or anywhere: the ledger is append-only and a
|
|
5926
|
+
* card may carry a statutory retention life.
|
|
5927
|
+
*
|
|
5928
|
+
* Requires `gift_cards:write`.
|
|
5929
|
+
*/
|
|
5930
|
+
async bulkSetGiftCardStatus(giftCardIds, status, options) {
|
|
5931
|
+
return this.adminRequest(
|
|
5932
|
+
"PATCH",
|
|
5933
|
+
"/api/v1/gift-cards/bulk/status",
|
|
5934
|
+
{ giftCardIds, status },
|
|
5935
|
+
void 0,
|
|
5936
|
+
"json",
|
|
5937
|
+
this.idempotencyHeaders(options)
|
|
5938
|
+
);
|
|
5939
|
+
}
|
|
5940
|
+
// ==========================================================================
|
|
5726
5941
|
// Donations
|
|
5727
5942
|
// ==========================================================================
|
|
5728
5943
|
/**
|
|
@@ -5865,10 +6080,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5865
6080
|
"/checkouts/shipping-destinations"
|
|
5866
6081
|
);
|
|
5867
6082
|
}
|
|
5868
|
-
return this.adminRequest(
|
|
5869
|
-
"GET",
|
|
5870
|
-
"/api/v1/checkouts/shipping-destinations"
|
|
5871
|
-
);
|
|
6083
|
+
return this.adminRequest("GET", "/api/checkouts/shipping-destinations");
|
|
5872
6084
|
}
|
|
5873
6085
|
/**
|
|
5874
6086
|
* Set shipping address on checkout (includes customer email).
|
|
@@ -6127,11 +6339,14 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
6127
6339
|
if (this.storeId && !this.apiKey) {
|
|
6128
6340
|
return this.storefrontRequest("GET", "/pickup-locations");
|
|
6129
6341
|
}
|
|
6130
|
-
return this.adminRequest("GET", "/api/
|
|
6342
|
+
return this.adminRequest("GET", "/api/checkouts/pickup-locations");
|
|
6131
6343
|
}
|
|
6132
6344
|
/**
|
|
6133
6345
|
* Set delivery type on checkout (shipping or pickup).
|
|
6134
6346
|
*
|
|
6347
|
+
* **Storefront and vibe-coded modes only.** There is no admin (`apiKey`)
|
|
6348
|
+
* route for this, so an admin-mode client throws instead of 404ing.
|
|
6349
|
+
*
|
|
6135
6350
|
* @example
|
|
6136
6351
|
* ```typescript
|
|
6137
6352
|
* const checkout = await client.setDeliveryType('checkout_123', 'pickup');
|
|
@@ -6156,12 +6371,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
6156
6371
|
}
|
|
6157
6372
|
);
|
|
6158
6373
|
}
|
|
6159
|
-
|
|
6160
|
-
"
|
|
6161
|
-
|
|
6162
|
-
{
|
|
6163
|
-
deliveryType
|
|
6164
|
-
}
|
|
6374
|
+
throw new BrainerceError(
|
|
6375
|
+
"setDeliveryType is not a route on the API-key /v1 surface. There is no checkout/:checkoutId/delivery-type endpoint to call, so the delivery type was not changed. Set it from the storefront session that owns the checkout \u2014 a client constructed with `salesChannelId` (vibe-coded) or `storeId` (public storefront) \u2014 or from the Brainerce dashboard.",
|
|
6376
|
+
400
|
|
6165
6377
|
);
|
|
6166
6378
|
}
|
|
6167
6379
|
/**
|
|
@@ -6169,6 +6381,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
6169
6381
|
* This sets the delivery type to "pickup", records customer info, and prepares for payment.
|
|
6170
6382
|
* Equivalent to setShippingAddress + selectShippingMethod for delivery orders.
|
|
6171
6383
|
*
|
|
6384
|
+
* **Storefront and vibe-coded modes only.** There is no admin (`apiKey`)
|
|
6385
|
+
* route for this, so an admin-mode client throws instead of 404ing.
|
|
6386
|
+
*
|
|
6172
6387
|
* @example
|
|
6173
6388
|
* ```typescript
|
|
6174
6389
|
* const checkout = await client.selectPickupLocation('checkout_123', {
|
|
@@ -6195,10 +6410,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
6195
6410
|
data
|
|
6196
6411
|
);
|
|
6197
6412
|
}
|
|
6198
|
-
|
|
6199
|
-
"
|
|
6200
|
-
|
|
6201
|
-
data
|
|
6413
|
+
throw new BrainerceError(
|
|
6414
|
+
"selectPickupLocation is not a route on the API-key /v1 surface. There is no checkout/:checkoutId/pickup-location endpoint to call, so no pickup location was selected. Select it from the storefront session that owns the checkout \u2014 a client constructed with `salesChannelId` (vibe-coded) or `storeId` (public storefront) \u2014 or from the Brainerce dashboard.",
|
|
6415
|
+
400
|
|
6202
6416
|
);
|
|
6203
6417
|
}
|
|
6204
6418
|
/**
|
|
@@ -7523,13 +7737,17 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
7523
7737
|
* lifetime earned, the program's display config, earned milestone `badges`,
|
|
7524
7738
|
* and the `paidMembership` subscription state (null for free members).
|
|
7525
7739
|
* Requires customerToken. Only available in storefront mode. `program` is
|
|
7526
|
-
* null when the store has no loyalty program.
|
|
7740
|
+
* null when the store has no loyalty program. `pointsBalance` excludes points
|
|
7741
|
+
* still inside the return window - those are in `pendingPoints`, and a panel
|
|
7742
|
+
* that ignores them shows a shopper 0 the day they order.
|
|
7527
7743
|
*
|
|
7528
7744
|
* @example
|
|
7529
7745
|
* ```typescript
|
|
7530
7746
|
* client.setCustomerToken(auth.token);
|
|
7531
7747
|
* const status = await client.getLoyaltyStatus();
|
|
7532
7748
|
* if (status.enrolled) console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
7749
|
+
* // Points from an order just placed are in pendingPoints, NOT pointsBalance.
|
|
7750
|
+
* if (status.pendingPoints > 0) console.log(`+${status.pendingPoints} on ${status.pendingPointsConfirmAt}`);
|
|
7533
7751
|
* status.badges?.forEach((b) => console.log(`🏅 ${b.name}`));
|
|
7534
7752
|
* if (status.paidMembership?.status === 'ACTIVE') showPremiumPerks(status.paidMembership.plan);
|
|
7535
7753
|
* ```
|
|
@@ -8376,11 +8594,28 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
8376
8594
|
return this.adminRequest("POST", "/api/v1/tags", data);
|
|
8377
8595
|
}
|
|
8378
8596
|
/**
|
|
8379
|
-
* Update an existing tag
|
|
8380
|
-
*
|
|
8597
|
+
* Update an existing tag.
|
|
8598
|
+
*
|
|
8599
|
+
* **Not callable.** The API-key `/v1` surface serves `GET`, `POST` and
|
|
8600
|
+
* `DELETE` on tags (`external-api.controller.ts:3276`, `:3310`, `:3331`) but
|
|
8601
|
+
* no update verb — `PATCH /api/v1/tags/:id` 404'd silently. Tag editing lives
|
|
8602
|
+
* only on the dashboard surface (`PATCH /api/stores/:storeId/tags/:id`,
|
|
8603
|
+
* `tags.controller.ts:127`), which needs a `storeId` in the path that this
|
|
8604
|
+
* signature does not carry, and resolves the acting user from a dashboard
|
|
8605
|
+
* session an API key does not have.
|
|
8606
|
+
*
|
|
8607
|
+
* Edit the tag in the Brainerce dashboard. {@link deleteTag} +
|
|
8608
|
+
* {@link createTag} is NOT an equivalent workaround: it drops the tag's
|
|
8609
|
+
* product assignments, and `CreateTagDto` has no `translations` field, so any
|
|
8610
|
+
* per-locale names are lost too.
|
|
8381
8611
|
*/
|
|
8382
8612
|
async updateTag(tagId, data) {
|
|
8383
|
-
|
|
8613
|
+
void tagId;
|
|
8614
|
+
void data;
|
|
8615
|
+
throw new BrainerceError(
|
|
8616
|
+
"updateTag is not a route on the API-key /v1 surface. Tags are served there for read, create and delete only, so the tag was not changed; edit it in the Brainerce dashboard. Deleting and recreating the tag is not equivalent \u2014 it drops the product assignments and the translations.",
|
|
8617
|
+
400
|
|
8618
|
+
);
|
|
8384
8619
|
}
|
|
8385
8620
|
/**
|
|
8386
8621
|
* Delete a tag
|
|
@@ -8478,24 +8713,41 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
8478
8713
|
);
|
|
8479
8714
|
}
|
|
8480
8715
|
/**
|
|
8481
|
-
* Update an attribute option
|
|
8482
|
-
*
|
|
8716
|
+
* Update an attribute option.
|
|
8717
|
+
*
|
|
8718
|
+
* **Not callable.** The API-key `/v1` surface serves attribute options for
|
|
8719
|
+
* list and create only (`external-api.controller.ts:3517`, `:3543`); there is
|
|
8720
|
+
* no per-option route, so this 404'd silently. Editing an option lives on the
|
|
8721
|
+
* dashboard surface (`PUT /api/stores/:storeId/attributes/:id/options/:optionId`,
|
|
8722
|
+
* `attributes.controller.ts:132` — note it is `PUT` there, not `PATCH`), which
|
|
8723
|
+
* needs a path `storeId` this signature does not carry.
|
|
8724
|
+
*
|
|
8725
|
+
* Edit the option in the Brainerce dashboard.
|
|
8483
8726
|
*/
|
|
8484
8727
|
async updateAttributeOption(attributeId, optionId, data) {
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8728
|
+
void attributeId;
|
|
8729
|
+
void optionId;
|
|
8730
|
+
void data;
|
|
8731
|
+
throw new BrainerceError(
|
|
8732
|
+
"updateAttributeOption is not a route on the API-key /v1 surface. Attribute options are served there for list and create only, so the option was not changed; edit it in the Brainerce dashboard.",
|
|
8733
|
+
400
|
|
8489
8734
|
);
|
|
8490
8735
|
}
|
|
8491
8736
|
/**
|
|
8492
|
-
* Delete an attribute option
|
|
8493
|
-
*
|
|
8737
|
+
* Delete an attribute option.
|
|
8738
|
+
*
|
|
8739
|
+
* **Not callable.** Same gap as {@link updateAttributeOption}: the `/v1`
|
|
8740
|
+
* surface has no per-option route. Deleting an option lives on the dashboard
|
|
8741
|
+
* surface (`DELETE /api/stores/:storeId/attributes/:id/options/:optionId`,
|
|
8742
|
+
* `attributes.controller.ts:145`), which needs a path `storeId` this signature
|
|
8743
|
+
* does not carry.
|
|
8494
8744
|
*/
|
|
8495
8745
|
async deleteAttributeOption(attributeId, optionId) {
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8746
|
+
void attributeId;
|
|
8747
|
+
void optionId;
|
|
8748
|
+
throw new BrainerceError(
|
|
8749
|
+
"deleteAttributeOption is not a route on the API-key /v1 surface. Attribute options are served there for list and create only, so nothing was deleted; delete the option in the Brainerce dashboard.",
|
|
8750
|
+
400
|
|
8499
8751
|
);
|
|
8500
8752
|
}
|
|
8501
8753
|
// -------------------- Modifier Groups (Admin) --------------------
|
|
@@ -9101,10 +9353,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9101
9353
|
* ```
|
|
9102
9354
|
*/
|
|
9103
9355
|
async applyTaxPreset(presetKey) {
|
|
9104
|
-
return this.adminRequest(
|
|
9105
|
-
"POST",
|
|
9106
|
-
`/api/v1/tax/presets/${encodePathSegment(presetKey)}/apply`
|
|
9107
|
-
);
|
|
9356
|
+
return this.adminRequest("POST", `/api/v1/tax/presets/${encodePathSegment(presetKey)}/apply`);
|
|
9108
9357
|
}
|
|
9109
9358
|
/**
|
|
9110
9359
|
* Get a single tax rate by ID
|
|
@@ -9321,25 +9570,48 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9321
9570
|
// Products/Coupons. When no publishes exist for an entity, it remains
|
|
9322
9571
|
// visible to all vibe-coded sites of the store (legacy default).
|
|
9323
9572
|
/**
|
|
9324
|
-
* Publish a metafield definition to a
|
|
9325
|
-
*
|
|
9326
|
-
*
|
|
9327
|
-
*
|
|
9328
|
-
*
|
|
9573
|
+
* Publish a metafield definition to a sales channel (admin mode).
|
|
9574
|
+
*
|
|
9575
|
+
* **Not callable.** This asked for `publish-vibe-coded`, which is a
|
|
9576
|
+
* deprecated backend alias, not the canonical route. The canonical spelling
|
|
9577
|
+
* is `publish-sales-channel`, and the `/v1` surface serves it for products
|
|
9578
|
+
* (`external-api.controller.ts:862`), coupons (`:947`), customers (`:1548`),
|
|
9579
|
+
* categories (`:3039`), brands (`:3211`) and tags (`:3349`) — but NOT for
|
|
9580
|
+
* metafield definitions. Both spellings 404 there.
|
|
9581
|
+
*
|
|
9582
|
+
* The operation exists only on the dashboard surface
|
|
9583
|
+
* (`POST /api/stores/:storeId/metafield-definitions/:id/publish-sales-channel`,
|
|
9584
|
+
* `metafields.controller.ts:190`), which needs a path `storeId` this signature
|
|
9585
|
+
* does not carry.
|
|
9586
|
+
*
|
|
9587
|
+
* Publish the definition to a sales channel from the Brainerce dashboard. A
|
|
9588
|
+
* definition with no publishes stays visible to every sales channel of the
|
|
9589
|
+
* store, so leaving it unpublished is the permissive default, not a lockout.
|
|
9329
9590
|
*/
|
|
9330
9591
|
async publishMetafieldDefinitionToVibeCodedSite(definitionId, vibeCodedConnectionId) {
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9592
|
+
void definitionId;
|
|
9593
|
+
void vibeCodedConnectionId;
|
|
9594
|
+
throw new BrainerceError(
|
|
9595
|
+
"publishMetafieldDefinitionToVibeCodedSite is not a route on the API-key /v1 surface. Per-sales-channel publishing is served there for products, coupons, customers, categories, brands and tags, but not for metafield definitions, so nothing was published; publish it in the Brainerce dashboard. A definition with no publishes remains visible to every sales channel of the store.",
|
|
9596
|
+
400
|
|
9335
9597
|
);
|
|
9336
9598
|
}
|
|
9337
|
-
/**
|
|
9599
|
+
/**
|
|
9600
|
+
* Unpublish a metafield definition from a sales channel (admin mode).
|
|
9601
|
+
*
|
|
9602
|
+
* **Not callable.** Same gap as
|
|
9603
|
+
* {@link publishMetafieldDefinitionToVibeCodedSite} — the `/v1` surface
|
|
9604
|
+
* carries no per-sales-channel routes for metafield definitions under either
|
|
9605
|
+
* the canonical `unpublish-sales-channel` spelling or the deprecated
|
|
9606
|
+
* `unpublish-vibe-coded` alias. The dashboard route is
|
|
9607
|
+
* `metafields.controller.ts:217`.
|
|
9608
|
+
*/
|
|
9338
9609
|
async unpublishMetafieldDefinitionFromVibeCodedSite(definitionId, vibeCodedConnectionId) {
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9610
|
+
void definitionId;
|
|
9611
|
+
void vibeCodedConnectionId;
|
|
9612
|
+
throw new BrainerceError(
|
|
9613
|
+
"unpublishMetafieldDefinitionFromVibeCodedSite is not a route on the API-key /v1 surface. Per-sales-channel publishing is served there for products, coupons, customers, categories, brands and tags, but not for metafield definitions, so nothing was unpublished; unpublish it in the Brainerce dashboard.",
|
|
9614
|
+
400
|
|
9343
9615
|
);
|
|
9344
9616
|
}
|
|
9345
9617
|
/**
|
|
@@ -9485,10 +9757,11 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9485
9757
|
* ```
|
|
9486
9758
|
*/
|
|
9487
9759
|
async setDefinitionProducts(definitionId, data) {
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9760
|
+
void definitionId;
|
|
9761
|
+
void data;
|
|
9762
|
+
throw new BrainerceError(
|
|
9763
|
+
"setDefinitionProducts is not a route on the API-key /v1 surface. There is no metafield-definitions/:id/products endpoint to call, so the definition's product list was not changed; set it in the Brainerce dashboard.",
|
|
9764
|
+
400
|
|
9492
9765
|
);
|
|
9493
9766
|
}
|
|
9494
9767
|
// -------------------- Metafields: Product Values --------------------
|
|
@@ -9534,24 +9807,41 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9534
9807
|
// -------------------- Product Customization Fields (Admin) --------------------
|
|
9535
9808
|
/**
|
|
9536
9809
|
* Get customization fields assigned to a product.
|
|
9537
|
-
*
|
|
9810
|
+
*
|
|
9811
|
+
* **Not callable.** The API-key `/v1` surface has no
|
|
9812
|
+
* `metafield-definitions/products/:productId/customization-fields` route, so
|
|
9813
|
+
* this 404'd silently. It exists only on the dashboard surface
|
|
9814
|
+
* (`GET /api/stores/:storeId/metafield-definitions/products/:productId/customization-fields`,
|
|
9815
|
+
* `metafields.controller.ts:277`), which needs a path `storeId` this signature
|
|
9816
|
+
* does not carry.
|
|
9817
|
+
*
|
|
9818
|
+
* {@link getProductMetafields} is the closest working call — it returns the
|
|
9819
|
+
* metafield VALUES stored on a product over
|
|
9820
|
+
* `GET /api/v1/products/:productId/metafields`, not the customer-input field
|
|
9821
|
+
* definitions attached to it.
|
|
9538
9822
|
*/
|
|
9539
9823
|
async getProductCustomizationFields(productId) {
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9824
|
+
void productId;
|
|
9825
|
+
throw new BrainerceError(
|
|
9826
|
+
"getProductCustomizationFields is not a route on the API-key /v1 surface. There is no metafield-definitions/products/:productId/customization-fields endpoint to call; read the assignments in the Brainerce dashboard. getProductMetafields returns the product's stored metafield values, which is a different thing.",
|
|
9827
|
+
400
|
|
9543
9828
|
);
|
|
9544
9829
|
}
|
|
9545
9830
|
/**
|
|
9546
9831
|
* Set customization fields for a product (replaces all existing assignments).
|
|
9547
|
-
*
|
|
9548
|
-
*
|
|
9832
|
+
*
|
|
9833
|
+
* **Not callable.** Same gap as {@link getProductCustomizationFields}: the
|
|
9834
|
+
* `/v1` surface carries no customization-field routes. The dashboard route is
|
|
9835
|
+
* `PATCH /api/stores/:storeId/metafield-definitions/products/:productId/customization-fields`
|
|
9836
|
+
* (`metafields.controller.ts:298`), which needs a path `storeId` this
|
|
9837
|
+
* signature does not carry.
|
|
9549
9838
|
*/
|
|
9550
9839
|
async setProductCustomizationFields(productId, definitionIds) {
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9840
|
+
void productId;
|
|
9841
|
+
void definitionIds;
|
|
9842
|
+
throw new BrainerceError(
|
|
9843
|
+
"setProductCustomizationFields is not a route on the API-key /v1 surface. There is no metafield-definitions/products/:productId/customization-fields endpoint to call, so the assignments were not changed; set them in the Brainerce dashboard.",
|
|
9844
|
+
400
|
|
9555
9845
|
);
|
|
9556
9846
|
}
|
|
9557
9847
|
/**
|
|
@@ -9734,6 +10024,27 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9734
10024
|
}
|
|
9735
10025
|
// -------------------- Store Team Management (Admin) --------------------
|
|
9736
10026
|
// Store-level team management. Each store has its own team with roles and permissions.
|
|
10027
|
+
/**
|
|
10028
|
+
* Every store-level team operation is dashboard-only.
|
|
10029
|
+
*
|
|
10030
|
+
* `store-team.controller.ts:53` carries `DashboardOnlyGuard`, which rejects
|
|
10031
|
+
* `api_key` and `app_installation` principals outright, so no SDK caller can
|
|
10032
|
+
* reach these however the URL is spelled. They additionally pointed at
|
|
10033
|
+
* `/api/v1/stores/:storeId/team*`, and `@Controller('v1')`
|
|
10034
|
+
* (external-api.controller.ts:181) has no `stores` root — so what they
|
|
10035
|
+
* actually returned was a 404, not the 403 you would expect from the guard.
|
|
10036
|
+
*
|
|
10037
|
+
* Throwing beats either status code: a 404 reads as "wrong id" and a 403 as
|
|
10038
|
+
* "missing permission", and both send the caller looking for a fix that does
|
|
10039
|
+
* not exist. Use the account-level `getTeamMembers()` family, or the
|
|
10040
|
+
* dashboard.
|
|
10041
|
+
*/
|
|
10042
|
+
dashboardOnlyTeamOperation(operation) {
|
|
10043
|
+
throw new BrainerceError(
|
|
10044
|
+
`${operation} is a dashboard-only operation. Store-level team management sits behind DashboardOnlyGuard, which rejects API-key principals, so there is no SDK path to it in any mode. Use the account-level team methods (getTeamMembers, inviteTeamMember, ...) or the Brainerce dashboard.`,
|
|
10045
|
+
403
|
|
10046
|
+
);
|
|
10047
|
+
}
|
|
9737
10048
|
/**
|
|
9738
10049
|
* Get the team for a specific store (members + pending invitations)
|
|
9739
10050
|
* Requires Admin mode (apiKey) and MANAGE_TEAM permission
|
|
@@ -9743,11 +10054,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9743
10054
|
* const { members, invitations } = await client.getStoreTeam('store_id');
|
|
9744
10055
|
* ```
|
|
9745
10056
|
*/
|
|
9746
|
-
async getStoreTeam(
|
|
9747
|
-
return this.
|
|
9748
|
-
"GET",
|
|
9749
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team`
|
|
9750
|
-
);
|
|
10057
|
+
async getStoreTeam(_storeId) {
|
|
10058
|
+
return this.dashboardOnlyTeamOperation("getStoreTeam");
|
|
9751
10059
|
}
|
|
9752
10060
|
/**
|
|
9753
10061
|
* Invite a new member to a store
|
|
@@ -9764,12 +10072,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9764
10072
|
* });
|
|
9765
10073
|
* ```
|
|
9766
10074
|
*/
|
|
9767
|
-
async inviteStoreMember(
|
|
9768
|
-
return this.
|
|
9769
|
-
"POST",
|
|
9770
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/invite`,
|
|
9771
|
-
data
|
|
9772
|
-
);
|
|
10075
|
+
async inviteStoreMember(_storeId, _data) {
|
|
10076
|
+
return this.dashboardOnlyTeamOperation("inviteStoreMember");
|
|
9773
10077
|
}
|
|
9774
10078
|
/**
|
|
9775
10079
|
* Update a store team member's role and/or permissions
|
|
@@ -9786,12 +10090,8 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9786
10090
|
* });
|
|
9787
10091
|
* ```
|
|
9788
10092
|
*/
|
|
9789
|
-
async updateStoreMember(
|
|
9790
|
-
return this.
|
|
9791
|
-
"PATCH",
|
|
9792
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/${encodePathSegment(memberId)}`,
|
|
9793
|
-
data
|
|
9794
|
-
);
|
|
10093
|
+
async updateStoreMember(_storeId, _memberId, _data) {
|
|
10094
|
+
return this.dashboardOnlyTeamOperation("updateStoreMember");
|
|
9795
10095
|
}
|
|
9796
10096
|
/**
|
|
9797
10097
|
* Replace the set of vibe-coded sales channels a store member is restricted to.
|
|
@@ -9812,42 +10112,29 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9812
10112
|
* });
|
|
9813
10113
|
* ```
|
|
9814
10114
|
*/
|
|
9815
|
-
async updateStoreMemberSalesChannels(
|
|
9816
|
-
return this.
|
|
9817
|
-
"PATCH",
|
|
9818
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/${encodePathSegment(memberId)}/sales-channels`,
|
|
9819
|
-
data
|
|
9820
|
-
);
|
|
10115
|
+
async updateStoreMemberSalesChannels(_storeId, _memberId, _data) {
|
|
10116
|
+
return this.dashboardOnlyTeamOperation("updateStoreMemberSalesChannels");
|
|
9821
10117
|
}
|
|
9822
10118
|
/**
|
|
9823
10119
|
* Remove a member from a store team
|
|
9824
10120
|
* Requires Admin mode (apiKey) and MANAGE_TEAM permission
|
|
9825
10121
|
*/
|
|
9826
|
-
async removeStoreMember(
|
|
9827
|
-
|
|
9828
|
-
"DELETE",
|
|
9829
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/${encodePathSegment(memberId)}`
|
|
9830
|
-
);
|
|
10122
|
+
async removeStoreMember(_storeId, _memberId) {
|
|
10123
|
+
return this.dashboardOnlyTeamOperation("removeStoreMember");
|
|
9831
10124
|
}
|
|
9832
10125
|
/**
|
|
9833
10126
|
* Resend a store invitation email
|
|
9834
10127
|
* Requires Admin mode (apiKey) and MANAGE_TEAM permission
|
|
9835
10128
|
*/
|
|
9836
|
-
async resendStoreInvitation(
|
|
9837
|
-
return this.
|
|
9838
|
-
"POST",
|
|
9839
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/invitations/${encodePathSegment(invitationId)}/resend`
|
|
9840
|
-
);
|
|
10129
|
+
async resendStoreInvitation(_storeId, _invitationId) {
|
|
10130
|
+
return this.dashboardOnlyTeamOperation("resendStoreInvitation");
|
|
9841
10131
|
}
|
|
9842
10132
|
/**
|
|
9843
10133
|
* Revoke a store invitation
|
|
9844
10134
|
* Requires Admin mode (apiKey) and MANAGE_TEAM permission
|
|
9845
10135
|
*/
|
|
9846
|
-
async revokeStoreInvitation(
|
|
9847
|
-
|
|
9848
|
-
"DELETE",
|
|
9849
|
-
`/api/v1/stores/${encodePathSegment(storeId)}/team/invitations/${encodePathSegment(invitationId)}`
|
|
9850
|
-
);
|
|
10136
|
+
async revokeStoreInvitation(_storeId, _invitationId) {
|
|
10137
|
+
return this.dashboardOnlyTeamOperation("revokeStoreInvitation");
|
|
9851
10138
|
}
|
|
9852
10139
|
/**
|
|
9853
10140
|
* Get public invitation details by token (no auth required)
|
|
@@ -9856,7 +10143,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9856
10143
|
async getStoreInvitationByToken(token) {
|
|
9857
10144
|
return this.request(
|
|
9858
10145
|
"GET",
|
|
9859
|
-
`/api/
|
|
10146
|
+
`/api/store-invitations/${encodePathSegment(token)}`
|
|
9860
10147
|
);
|
|
9861
10148
|
}
|
|
9862
10149
|
/**
|
|
@@ -9864,9 +10151,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9864
10151
|
* Requires Admin mode (apiKey)
|
|
9865
10152
|
*/
|
|
9866
10153
|
async acceptStoreInvitation(token) {
|
|
9867
|
-
|
|
9868
|
-
"
|
|
9869
|
-
|
|
10154
|
+
throw new BrainerceError(
|
|
10155
|
+
"acceptStoreInvitation requires a dashboard session belonging to the invited user: the invitation is matched against the email address of that user, which an API key does not have. Send the invitee to the invitation link instead.",
|
|
10156
|
+
403
|
|
9870
10157
|
);
|
|
9871
10158
|
}
|
|
9872
10159
|
/**
|
|
@@ -9883,7 +10170,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9883
10170
|
* ```
|
|
9884
10171
|
*/
|
|
9885
10172
|
async getMyStores() {
|
|
9886
|
-
return this.
|
|
10173
|
+
return this.dashboardOnlyUserContext("getMyStores");
|
|
9887
10174
|
}
|
|
9888
10175
|
/**
|
|
9889
10176
|
* Get the current user's resolved permissions for a specific store
|
|
@@ -9897,10 +10184,23 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
9897
10184
|
* }
|
|
9898
10185
|
* ```
|
|
9899
10186
|
*/
|
|
9900
|
-
async getMyStorePermissions(
|
|
9901
|
-
return this.
|
|
9902
|
-
|
|
9903
|
-
|
|
10187
|
+
async getMyStorePermissions(_storeId) {
|
|
10188
|
+
return this.dashboardOnlyUserContext("getMyStorePermissions");
|
|
10189
|
+
}
|
|
10190
|
+
/**
|
|
10191
|
+
* `/me/*` answers "who am I and what can I reach", which only a real user can
|
|
10192
|
+
* ask. `UserContextController` (store-team.controller.ts:246) is guarded by
|
|
10193
|
+
* `DashboardOnlyGuard` for a load-bearing reason its own G16 comment spells
|
|
10194
|
+
* out: both routes resolve access purely from `@CurrentUserId()`, which is
|
|
10195
|
+
* `undefined` for an api_key principal, so the store filter would be stripped
|
|
10196
|
+
* and every store on the platform returned. The guard is the control. These
|
|
10197
|
+
* also pointed at `/api/v1/me/*`, which no controller serves, so the observed
|
|
10198
|
+
* failure was a 404 rather than the guard's 403.
|
|
10199
|
+
*/
|
|
10200
|
+
dashboardOnlyUserContext(operation) {
|
|
10201
|
+
throw new BrainerceError(
|
|
10202
|
+
`${operation} resolves the CURRENT USER, so it requires a dashboard session and is rejected for API-key principals by design. An API key is already scoped to one store: read it with getStore(), and read the permissions of a member from getStoreTeam() in the dashboard.`,
|
|
10203
|
+
403
|
|
9904
10204
|
);
|
|
9905
10205
|
}
|
|
9906
10206
|
// -------------------- Email Settings & Templates (Admin) --------------------
|
|
@@ -10043,7 +10343,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
10043
10343
|
* Requires Admin mode (apiKey)
|
|
10044
10344
|
*/
|
|
10045
10345
|
async getSyncConflicts() {
|
|
10046
|
-
return this.
|
|
10346
|
+
return this.syncConflictsNotImplemented("getSyncConflicts");
|
|
10047
10347
|
}
|
|
10048
10348
|
/**
|
|
10049
10349
|
* Resolve a sync conflict
|
|
@@ -10053,12 +10353,28 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
10053
10353
|
* @param resolution - 'MERGE' to link to existing product, 'CREATE_NEW' to create new product
|
|
10054
10354
|
*/
|
|
10055
10355
|
async resolveSyncConflict(conflictId, resolution) {
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10356
|
+
void conflictId;
|
|
10357
|
+
void resolution;
|
|
10358
|
+
return this.syncConflictsNotImplemented("resolveSyncConflict");
|
|
10359
|
+
}
|
|
10360
|
+
/**
|
|
10361
|
+
* Sync conflicts were never implemented on the server.
|
|
10362
|
+
*
|
|
10363
|
+
* There is no `sync-conflict` route anywhere in the backend — not under
|
|
10364
|
+
* `@Controller('v1')`, not on any other controller. These two methods have
|
|
10365
|
+
* called a URL that has never existed, and `SyncConflict` /
|
|
10366
|
+
* `SyncConflictResolution` / `ResolveSyncConflictDto` are exported types with
|
|
10367
|
+
* no producer. The METAFIELD conflict siblings below are real
|
|
10368
|
+
* (external-api.controller.ts:4788, :4802) and are easy to mistake for these.
|
|
10369
|
+
*
|
|
10370
|
+
* Kept as throwing stubs rather than deleted: removing exported methods from a
|
|
10371
|
+
* published package is a breaking change, and a caller who has been swallowing
|
|
10372
|
+
* a 404 deserves to be told why.
|
|
10373
|
+
*/
|
|
10374
|
+
syncConflictsNotImplemented(operation) {
|
|
10375
|
+
throw new BrainerceError(
|
|
10376
|
+
`${operation} is not implemented: the platform exposes no sync-conflict endpoint. If you are looking for metafield sync conflicts, use getMetafieldConflicts() / resolveMetafieldConflict().`,
|
|
10377
|
+
501
|
|
10062
10378
|
);
|
|
10063
10379
|
}
|
|
10064
10380
|
// -------------------- Metafield Conflicts (Admin) --------------------
|
|
@@ -10920,6 +11236,7 @@ function buildProductJsonLd(product, opts) {
|
|
|
10920
11236
|
const inv = product.inventory;
|
|
10921
11237
|
const availability = !inv ? "https://schema.org/InStock" : inv.inStock ?? (inv.available ?? 0) > 0 ? "https://schema.org/InStock" : inv.canPurchase ? "https://schema.org/BackOrder" : "https://schema.org/OutOfStock";
|
|
10922
11238
|
const isVariable = product.type === "VARIABLE" && product.priceMin && product.priceMax;
|
|
11239
|
+
const omitOffer = product.type === "KIT";
|
|
10923
11240
|
const itemCondition = "https://schema.org/NewCondition";
|
|
10924
11241
|
const shippingDetails = (opts.shipping ?? []).filter((z) => z.amount !== null).map((z) => ({
|
|
10925
11242
|
"@type": "OfferShippingDetails",
|
|
@@ -10982,7 +11299,7 @@ function buildProductJsonLd(product, opts) {
|
|
|
10982
11299
|
...product.gtin ? { gtin: product.gtin } : {},
|
|
10983
11300
|
...product.mpn ? { mpn: product.mpn } : {},
|
|
10984
11301
|
...brand ? { brand: { "@type": "Brand", name: brand } } : {},
|
|
10985
|
-
offers: offer,
|
|
11302
|
+
...omitOffer ? {} : { offers: offer },
|
|
10986
11303
|
// Google policy: never emit an empty/zero rating block. bestRating /
|
|
10987
11304
|
// worstRating make the 1-5 scale explicit so aggregators can't misread
|
|
10988
11305
|
// a 4.8 on an assumed 0-10 scale.
|