brainerce 2.5.0 → 2.8.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 +8485 -8289
- package/dist/index.d.mts +461 -15
- package/dist/index.d.ts +461 -15
- package/dist/index.js +260 -7
- package/dist/index.mjs +260 -7
- package/package.json +1 -1
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.
|
|
207
|
+
var SDK_VERSION = "2.8.0";
|
|
208
208
|
|
|
209
209
|
// src/client.ts
|
|
210
210
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -381,10 +381,14 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
381
381
|
* Storefront (public) and vibe-coded modes only. Rate-limited server-side to
|
|
382
382
|
* 3 requests / 60s per IP, plus one confirmation email per address per 24h.
|
|
383
383
|
*
|
|
384
|
-
* **Where the discount goes.**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
384
|
+
* **Where the discount goes.** Configure the newsletter welcome offer and the
|
|
385
|
+
* platform issues the coupon itself: read it with `marketing.getBenefit()`,
|
|
386
|
+
* show those terms beside the field, and stop there.
|
|
387
|
+
*
|
|
388
|
+
* ⛔ DO NOT SHOW A CODE AFTER THIS CALL RESOLVES. No coupon exists yet. It is
|
|
389
|
+
* minted when the recipient clicks the confirmation link, and it is mailed to
|
|
390
|
+
* them at that moment — a code rendered here is a code that was never issued.
|
|
391
|
+
* Say "check your email", the same as for the subscription itself.
|
|
388
392
|
*
|
|
389
393
|
* @example
|
|
390
394
|
* ```typescript
|
|
@@ -412,6 +416,163 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
412
416
|
"/marketing/subscribe",
|
|
413
417
|
input
|
|
414
418
|
);
|
|
419
|
+
},
|
|
420
|
+
/**
|
|
421
|
+
* The welcome offer to render beside the signup field, or `null` when this
|
|
422
|
+
* store offers none.
|
|
423
|
+
*
|
|
424
|
+
* Show the discount, how long the coupon lasts, any minimum order, whether
|
|
425
|
+
* it is first-order only, and the merchant's own headline and terms. Then
|
|
426
|
+
* post to `marketing.subscribe()` and tell the shopper to check their
|
|
427
|
+
* inbox.
|
|
428
|
+
*
|
|
429
|
+
* ⛔ THE COUPON DOES NOT EXIST YET at any point in that sequence. It is
|
|
430
|
+
* created when the recipient clicks the confirmation link in their email,
|
|
431
|
+
* and it is mailed to them there. Rendering a code on this screen renders a
|
|
432
|
+
* code nobody was issued.
|
|
433
|
+
*
|
|
434
|
+
* ⛔ Takes no email address and returns nothing about any individual, on
|
|
435
|
+
* purpose. There is no "has this person already claimed" call, because an
|
|
436
|
+
* unauthenticated one would be an oracle for who shops here. If you need to
|
|
437
|
+
* discourage a repeat signup, say the offer is one per address; do not try
|
|
438
|
+
* to detect it.
|
|
439
|
+
*
|
|
440
|
+
* `null` is the common case on a store that never set this up, so handle it
|
|
441
|
+
* rather than assuming the object. Cache it per page load: it belongs to
|
|
442
|
+
* the store, not to the visitor.
|
|
443
|
+
*
|
|
444
|
+
* Storefront (public) and vibe-coded modes.
|
|
445
|
+
*
|
|
446
|
+
* @param locale - Storefront locale, e.g. `"he"`. Picks the language of the
|
|
447
|
+
* headline and terms; falls back to the store language when omitted.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```typescript
|
|
451
|
+
* const offer = await brainerce.marketing.getBenefit('he');
|
|
452
|
+
* if (offer) {
|
|
453
|
+
* // "10% הנחה על ההזמנה הראשונה"
|
|
454
|
+
* render(offer.headline ?? defaultHeadline(offer), offer.terms);
|
|
455
|
+
* }
|
|
456
|
+
* await brainerce.marketing.subscribe({ email, locale: 'he', honeypot });
|
|
457
|
+
* // → "בדקו את המייל שלכם" — never a coupon code
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
getBenefit: async (locale) => {
|
|
461
|
+
const query = locale ? { locale } : void 0;
|
|
462
|
+
if (this.isVibeCodedMode()) {
|
|
463
|
+
return this.vibeCodedRequest(
|
|
464
|
+
"GET",
|
|
465
|
+
"/newsletter-benefit",
|
|
466
|
+
void 0,
|
|
467
|
+
query
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return this.storefrontRequest(
|
|
471
|
+
"GET",
|
|
472
|
+
"/newsletter-benefit",
|
|
473
|
+
void 0,
|
|
474
|
+
query
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
// -------------------- Newsletter signup benefit (Admin) --------------------
|
|
479
|
+
/**
|
|
480
|
+
* Manage the newsletter welcome offer: the terms merchants configure, and the
|
|
481
|
+
* benefits that offer has produced.
|
|
482
|
+
*
|
|
483
|
+
* Admin mode (`apiKey`) only, on the `coupons:read` / `coupons:write` scopes.
|
|
484
|
+
* The benefit IS a coupon feature — it mints a Coupon row and the coupon
|
|
485
|
+
* machinery enforces it — so it carries no scope of its own.
|
|
486
|
+
*
|
|
487
|
+
* ⛔ THERE IS NO "ISSUE A BENEFIT TO THIS ADDRESS" CALL, and there will not
|
|
488
|
+
* be one. A benefit exists because someone submitted the signup form AND
|
|
489
|
+
* clicked the confirmation link; handing one out directly would skip the
|
|
490
|
+
* consent the double opt-in exists to collect and break the one-per-address
|
|
491
|
+
* guarantee that the grant's unique constraint provides. `resend` re-sends a
|
|
492
|
+
* code that already exists; it never creates one.
|
|
493
|
+
*/
|
|
494
|
+
this.newsletterBenefit = {
|
|
495
|
+
/**
|
|
496
|
+
* The store's configuration, or `null` when none was ever saved.
|
|
497
|
+
*
|
|
498
|
+
* `null` and `{ enabled: false }` are different: never configured, versus
|
|
499
|
+
* configured and switched off. Both mean "offer nothing" to a storefront.
|
|
500
|
+
*/
|
|
501
|
+
getSettings: async () => {
|
|
502
|
+
return this.adminRequest(
|
|
503
|
+
"GET",
|
|
504
|
+
"/api/v1/newsletter-benefit/settings"
|
|
505
|
+
);
|
|
506
|
+
},
|
|
507
|
+
/**
|
|
508
|
+
* Create or replace the offer.
|
|
509
|
+
*
|
|
510
|
+
* ⛔ A FULL REPLACEMENT, not a patch. Every field is written, so a field you
|
|
511
|
+
* omit is cleared rather than kept.
|
|
512
|
+
*
|
|
513
|
+
* Saving never rewrites a promise already made: signups still waiting for a
|
|
514
|
+
* confirmation click keep the terms they were shown, and coupons already
|
|
515
|
+
* issued are untouched. Switching `enabled` off stops new offers and leaves
|
|
516
|
+
* every issued coupon working until it expires.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```typescript
|
|
520
|
+
* await brainerce.newsletterBenefit.updateSettings({
|
|
521
|
+
* enabled: true,
|
|
522
|
+
* discountType: 'PERCENTAGE',
|
|
523
|
+
* discountValue: 10,
|
|
524
|
+
* minimumOrderAmount: 200,
|
|
525
|
+
* combinesWithOther: false,
|
|
526
|
+
* validityDays: 7,
|
|
527
|
+
* eligibilityTtlHours: 168,
|
|
528
|
+
* firstOrderOnly: true,
|
|
529
|
+
* content: { he: { headline: '10% הנחה על ההזמנה הראשונה' } },
|
|
530
|
+
* });
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
533
|
+
updateSettings: async (input) => {
|
|
534
|
+
return this.adminRequest(
|
|
535
|
+
"PUT",
|
|
536
|
+
"/api/v1/newsletter-benefit/settings",
|
|
537
|
+
input
|
|
538
|
+
);
|
|
539
|
+
},
|
|
540
|
+
/**
|
|
541
|
+
* Issued benefits, newest first, as `{ data, meta }`.
|
|
542
|
+
*
|
|
543
|
+
* ⛔ NO EMAIL FILTER — the API refuses the parameter. Filter the page you
|
|
544
|
+
* get back rather than asking the server about one address.
|
|
545
|
+
*/
|
|
546
|
+
listGrants: async (params = {}) => {
|
|
547
|
+
return this.adminRequest(
|
|
548
|
+
"GET",
|
|
549
|
+
"/api/v1/newsletter-benefit/grants",
|
|
550
|
+
void 0,
|
|
551
|
+
{
|
|
552
|
+
page: params.page,
|
|
553
|
+
limit: params.limit,
|
|
554
|
+
status: params.status,
|
|
555
|
+
from: params.from,
|
|
556
|
+
to: params.to
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
},
|
|
560
|
+
/**
|
|
561
|
+
* Re-send one benefit that went astray.
|
|
562
|
+
*
|
|
563
|
+
* ⛔ SENDS THE SAME CODE. It never mints a second coupon, so a support
|
|
564
|
+
* ticket cannot become two discounts. For a benefit whose issuance failed
|
|
565
|
+
* before any coupon existed, this retries the issuance and mails the result.
|
|
566
|
+
*
|
|
567
|
+
* Rejects a signup that has not been confirmed and one that lapsed before a
|
|
568
|
+
* coupon was minted: there is nothing to re-send in either case, and
|
|
569
|
+
* nothing that may be created.
|
|
570
|
+
*/
|
|
571
|
+
resend: async (grantId) => {
|
|
572
|
+
return this.adminRequest(
|
|
573
|
+
"POST",
|
|
574
|
+
`/api/v1/newsletter-benefit/grants/${encodePathSegment(grantId)}/resend`
|
|
575
|
+
);
|
|
415
576
|
}
|
|
416
577
|
};
|
|
417
578
|
// -------------------- Stock alerts --------------------
|
|
@@ -2505,8 +2666,13 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2505
2666
|
*
|
|
2506
2667
|
* Rejected: a product that is not a KIT, a component from another store, a
|
|
2507
2668
|
* component that is itself a KIT, a VARIABLE component with no variant
|
|
2508
|
-
* pinned, a variant that does not belong to its product,
|
|
2509
|
-
* listed twice
|
|
2669
|
+
* pinned, a variant that does not belong to its product, the same slot
|
|
2670
|
+
* listed twice, and a component whose product or pinned variant is not
|
|
2671
|
+
* published.
|
|
2672
|
+
*
|
|
2673
|
+
* That last one is checked over the WHOLE list you send, not just the rows
|
|
2674
|
+
* you changed. Once a product already inside a kit is unpublished, no edit
|
|
2675
|
+
* to that kit saves until you publish it again or drop it from the list.
|
|
2510
2676
|
*
|
|
2511
2677
|
* @example
|
|
2512
2678
|
* ```typescript
|
|
@@ -2795,6 +2961,79 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2795
2961
|
async updateOrder(orderId, data) {
|
|
2796
2962
|
return this.request("PATCH", `/api/v1/orders/${encodePathSegment(orderId)}`, data);
|
|
2797
2963
|
}
|
|
2964
|
+
/**
|
|
2965
|
+
* List the store's order custom field definitions.
|
|
2966
|
+
*
|
|
2967
|
+
* Call this before writing values: the `key` of each definition is what
|
|
2968
|
+
* {@link setOrderCustomFieldValues} accepts, and `type` is what a value has
|
|
2969
|
+
* to fit. Inactive definitions are included, so you can tell "the merchant
|
|
2970
|
+
* turned this field off" apart from "the merchant never created it".
|
|
2971
|
+
*
|
|
2972
|
+
* Requires an API key with the `orders:read` scope.
|
|
2973
|
+
*/
|
|
2974
|
+
async getOrderCustomFieldDefinitions() {
|
|
2975
|
+
return this.adminRequest("GET", "/api/v1/order-custom-fields");
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Read the custom field values stored on one order.
|
|
2979
|
+
*
|
|
2980
|
+
* Requires an API key with the `orders:read` scope.
|
|
2981
|
+
*/
|
|
2982
|
+
async getOrderCustomFieldValues(orderId) {
|
|
2983
|
+
return this.adminRequest(
|
|
2984
|
+
"GET",
|
|
2985
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/custom-fields`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Write custom field values onto an order.
|
|
2990
|
+
*
|
|
2991
|
+
* This is how work that finishes OUTSIDE Brainerce gets back onto the order
|
|
2992
|
+
* it belongs to. Subscribe to the `order.paid` webhook, call whatever third
|
|
2993
|
+
* party issues the thing you sell — a licence key, a booking reference, a
|
|
2994
|
+
* warranty number — then write the answer here. The value travels to the
|
|
2995
|
+
* merchant's own order email templates as `orderCustomFields` and, when the
|
|
2996
|
+
* definition is `isPublic`, to the customer's own order page. No email
|
|
2997
|
+
* template or endpoint has to be built per integration.
|
|
2998
|
+
*
|
|
2999
|
+
* ⛔ No default template PRINTS `orderCustomFields`. The variable reaches
|
|
3000
|
+
* every order email, but until the merchant adds the block to their template
|
|
3001
|
+
* once, a value written here is invisible to the customer. Writing the field
|
|
3002
|
+
* is not the same as the customer being told.
|
|
3003
|
+
*
|
|
3004
|
+
* The write is a MERGE: keys you leave out keep their current value, and
|
|
3005
|
+
* `null` clears a field that is not required. Values are coerced to the
|
|
3006
|
+
* definition's type and rejected with a 400 when they cannot be — but a key
|
|
3007
|
+
* with no active definition on the store is IGNORED rather than failing the
|
|
3008
|
+
* whole call, so read the returned `fields` to confirm what was stored.
|
|
3009
|
+
*
|
|
3010
|
+
* Pass an `idempotencyKey` when the caller may retry: an identical re-send
|
|
3011
|
+
* then replays the original response instead of writing again.
|
|
3012
|
+
*
|
|
3013
|
+
* Requires an API key with the `orders:write` scope.
|
|
3014
|
+
*
|
|
3015
|
+
* @example
|
|
3016
|
+
* ```typescript
|
|
3017
|
+
* // after the third party answered
|
|
3018
|
+
* await client.setOrderCustomFieldValues(
|
|
3019
|
+
* order.id,
|
|
3020
|
+
* { licence_key: 'ABCD-EFGH-IJKL' },
|
|
3021
|
+
* { idempotencyKey: `licence-${order.id}` }
|
|
3022
|
+
* );
|
|
3023
|
+
* // fires the "order completed" email, which carries the field
|
|
3024
|
+
* await client.updateOrder(order.id, { status: 'COMPLETED' });
|
|
3025
|
+
* ```
|
|
3026
|
+
*/
|
|
3027
|
+
async setOrderCustomFieldValues(orderId, fields, options) {
|
|
3028
|
+
return this.adminRequest(
|
|
3029
|
+
"PATCH",
|
|
3030
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/custom-fields`,
|
|
3031
|
+
{ fields },
|
|
3032
|
+
void 0,
|
|
3033
|
+
"json",
|
|
3034
|
+
this.idempotencyHeaders(options)
|
|
3035
|
+
);
|
|
3036
|
+
}
|
|
2798
3037
|
/**
|
|
2799
3038
|
* Update order status.
|
|
2800
3039
|
*
|
|
@@ -3111,6 +3350,13 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
3111
3350
|
* exist and 404'd silently. The live route is product-scoped:
|
|
3112
3351
|
* `GET /api/v1/products/:id/inventory`. A product with no inventory row
|
|
3113
3352
|
* reads back as all zeroes rather than 404ing.
|
|
3353
|
+
*
|
|
3354
|
+
* The response carries the whole {@link ProductInventoryResponse} — the
|
|
3355
|
+
* `InventoryItem` id, `trackingMode`, `backorderMode`, `backorderLimit`,
|
|
3356
|
+
* `lowStockThreshold`, `lastInventorySyncAt` and `updatedAt` alongside the
|
|
3357
|
+
* three counters. It always did; only the three counters were declared.
|
|
3358
|
+
* On the all-zeroes no-row branch everything but the counters is absent,
|
|
3359
|
+
* so test `id` rather than expecting a 404.
|
|
3114
3360
|
*/
|
|
3115
3361
|
async getInventory(productId) {
|
|
3116
3362
|
return this.adminRequest(
|
|
@@ -11094,6 +11340,13 @@ var ALLOWED_PAYMENT_HOSTS = [
|
|
|
11094
11340
|
// reachable, so a terminal configured against it keeps working.
|
|
11095
11341
|
"pay.hyp.co.il",
|
|
11096
11342
|
"icom.yaad.net",
|
|
11343
|
+
// iCredit (ריווחית) — the hosted payment page returned by
|
|
11344
|
+
// PaymentPageRequest. Both environments are listed EXPLICITLY rather than
|
|
11345
|
+
// allowing `rivhit.co.il`: the matcher below also accepts `*.<host>`, so a
|
|
11346
|
+
// bare parent entry would open every Rivhit subdomain (their accounting app,
|
|
11347
|
+
// marketing site, anything they add later) to a payment redirect.
|
|
11348
|
+
"icredit.rivhit.co.il",
|
|
11349
|
+
"testicredit.rivhit.co.il",
|
|
11097
11350
|
// Brainerce-hosted payment embeds (backend payment-embed proxy at
|
|
11098
11351
|
// `/api/payment/embed/...` that fronts provider apps' embed shells —
|
|
11099
11352
|
// e.g. cardcom-payments OpenFields wrapper). The match also covers
|
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.
|
|
118
|
+
var SDK_VERSION = "2.8.0";
|
|
119
119
|
|
|
120
120
|
// src/client.ts
|
|
121
121
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -292,10 +292,14 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
292
292
|
* Storefront (public) and vibe-coded modes only. Rate-limited server-side to
|
|
293
293
|
* 3 requests / 60s per IP, plus one confirmation email per address per 24h.
|
|
294
294
|
*
|
|
295
|
-
* **Where the discount goes.**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
295
|
+
* **Where the discount goes.** Configure the newsletter welcome offer and the
|
|
296
|
+
* platform issues the coupon itself: read it with `marketing.getBenefit()`,
|
|
297
|
+
* show those terms beside the field, and stop there.
|
|
298
|
+
*
|
|
299
|
+
* ⛔ DO NOT SHOW A CODE AFTER THIS CALL RESOLVES. No coupon exists yet. It is
|
|
300
|
+
* minted when the recipient clicks the confirmation link, and it is mailed to
|
|
301
|
+
* them at that moment — a code rendered here is a code that was never issued.
|
|
302
|
+
* Say "check your email", the same as for the subscription itself.
|
|
299
303
|
*
|
|
300
304
|
* @example
|
|
301
305
|
* ```typescript
|
|
@@ -323,6 +327,163 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
323
327
|
"/marketing/subscribe",
|
|
324
328
|
input
|
|
325
329
|
);
|
|
330
|
+
},
|
|
331
|
+
/**
|
|
332
|
+
* The welcome offer to render beside the signup field, or `null` when this
|
|
333
|
+
* store offers none.
|
|
334
|
+
*
|
|
335
|
+
* Show the discount, how long the coupon lasts, any minimum order, whether
|
|
336
|
+
* it is first-order only, and the merchant's own headline and terms. Then
|
|
337
|
+
* post to `marketing.subscribe()` and tell the shopper to check their
|
|
338
|
+
* inbox.
|
|
339
|
+
*
|
|
340
|
+
* ⛔ THE COUPON DOES NOT EXIST YET at any point in that sequence. It is
|
|
341
|
+
* created when the recipient clicks the confirmation link in their email,
|
|
342
|
+
* and it is mailed to them there. Rendering a code on this screen renders a
|
|
343
|
+
* code nobody was issued.
|
|
344
|
+
*
|
|
345
|
+
* ⛔ Takes no email address and returns nothing about any individual, on
|
|
346
|
+
* purpose. There is no "has this person already claimed" call, because an
|
|
347
|
+
* unauthenticated one would be an oracle for who shops here. If you need to
|
|
348
|
+
* discourage a repeat signup, say the offer is one per address; do not try
|
|
349
|
+
* to detect it.
|
|
350
|
+
*
|
|
351
|
+
* `null` is the common case on a store that never set this up, so handle it
|
|
352
|
+
* rather than assuming the object. Cache it per page load: it belongs to
|
|
353
|
+
* the store, not to the visitor.
|
|
354
|
+
*
|
|
355
|
+
* Storefront (public) and vibe-coded modes.
|
|
356
|
+
*
|
|
357
|
+
* @param locale - Storefront locale, e.g. `"he"`. Picks the language of the
|
|
358
|
+
* headline and terms; falls back to the store language when omitted.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* const offer = await brainerce.marketing.getBenefit('he');
|
|
363
|
+
* if (offer) {
|
|
364
|
+
* // "10% הנחה על ההזמנה הראשונה"
|
|
365
|
+
* render(offer.headline ?? defaultHeadline(offer), offer.terms);
|
|
366
|
+
* }
|
|
367
|
+
* await brainerce.marketing.subscribe({ email, locale: 'he', honeypot });
|
|
368
|
+
* // → "בדקו את המייל שלכם" — never a coupon code
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
getBenefit: async (locale) => {
|
|
372
|
+
const query = locale ? { locale } : void 0;
|
|
373
|
+
if (this.isVibeCodedMode()) {
|
|
374
|
+
return this.vibeCodedRequest(
|
|
375
|
+
"GET",
|
|
376
|
+
"/newsletter-benefit",
|
|
377
|
+
void 0,
|
|
378
|
+
query
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return this.storefrontRequest(
|
|
382
|
+
"GET",
|
|
383
|
+
"/newsletter-benefit",
|
|
384
|
+
void 0,
|
|
385
|
+
query
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
// -------------------- Newsletter signup benefit (Admin) --------------------
|
|
390
|
+
/**
|
|
391
|
+
* Manage the newsletter welcome offer: the terms merchants configure, and the
|
|
392
|
+
* benefits that offer has produced.
|
|
393
|
+
*
|
|
394
|
+
* Admin mode (`apiKey`) only, on the `coupons:read` / `coupons:write` scopes.
|
|
395
|
+
* The benefit IS a coupon feature — it mints a Coupon row and the coupon
|
|
396
|
+
* machinery enforces it — so it carries no scope of its own.
|
|
397
|
+
*
|
|
398
|
+
* ⛔ THERE IS NO "ISSUE A BENEFIT TO THIS ADDRESS" CALL, and there will not
|
|
399
|
+
* be one. A benefit exists because someone submitted the signup form AND
|
|
400
|
+
* clicked the confirmation link; handing one out directly would skip the
|
|
401
|
+
* consent the double opt-in exists to collect and break the one-per-address
|
|
402
|
+
* guarantee that the grant's unique constraint provides. `resend` re-sends a
|
|
403
|
+
* code that already exists; it never creates one.
|
|
404
|
+
*/
|
|
405
|
+
this.newsletterBenefit = {
|
|
406
|
+
/**
|
|
407
|
+
* The store's configuration, or `null` when none was ever saved.
|
|
408
|
+
*
|
|
409
|
+
* `null` and `{ enabled: false }` are different: never configured, versus
|
|
410
|
+
* configured and switched off. Both mean "offer nothing" to a storefront.
|
|
411
|
+
*/
|
|
412
|
+
getSettings: async () => {
|
|
413
|
+
return this.adminRequest(
|
|
414
|
+
"GET",
|
|
415
|
+
"/api/v1/newsletter-benefit/settings"
|
|
416
|
+
);
|
|
417
|
+
},
|
|
418
|
+
/**
|
|
419
|
+
* Create or replace the offer.
|
|
420
|
+
*
|
|
421
|
+
* ⛔ A FULL REPLACEMENT, not a patch. Every field is written, so a field you
|
|
422
|
+
* omit is cleared rather than kept.
|
|
423
|
+
*
|
|
424
|
+
* Saving never rewrites a promise already made: signups still waiting for a
|
|
425
|
+
* confirmation click keep the terms they were shown, and coupons already
|
|
426
|
+
* issued are untouched. Switching `enabled` off stops new offers and leaves
|
|
427
|
+
* every issued coupon working until it expires.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```typescript
|
|
431
|
+
* await brainerce.newsletterBenefit.updateSettings({
|
|
432
|
+
* enabled: true,
|
|
433
|
+
* discountType: 'PERCENTAGE',
|
|
434
|
+
* discountValue: 10,
|
|
435
|
+
* minimumOrderAmount: 200,
|
|
436
|
+
* combinesWithOther: false,
|
|
437
|
+
* validityDays: 7,
|
|
438
|
+
* eligibilityTtlHours: 168,
|
|
439
|
+
* firstOrderOnly: true,
|
|
440
|
+
* content: { he: { headline: '10% הנחה על ההזמנה הראשונה' } },
|
|
441
|
+
* });
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
updateSettings: async (input) => {
|
|
445
|
+
return this.adminRequest(
|
|
446
|
+
"PUT",
|
|
447
|
+
"/api/v1/newsletter-benefit/settings",
|
|
448
|
+
input
|
|
449
|
+
);
|
|
450
|
+
},
|
|
451
|
+
/**
|
|
452
|
+
* Issued benefits, newest first, as `{ data, meta }`.
|
|
453
|
+
*
|
|
454
|
+
* ⛔ NO EMAIL FILTER — the API refuses the parameter. Filter the page you
|
|
455
|
+
* get back rather than asking the server about one address.
|
|
456
|
+
*/
|
|
457
|
+
listGrants: async (params = {}) => {
|
|
458
|
+
return this.adminRequest(
|
|
459
|
+
"GET",
|
|
460
|
+
"/api/v1/newsletter-benefit/grants",
|
|
461
|
+
void 0,
|
|
462
|
+
{
|
|
463
|
+
page: params.page,
|
|
464
|
+
limit: params.limit,
|
|
465
|
+
status: params.status,
|
|
466
|
+
from: params.from,
|
|
467
|
+
to: params.to
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
},
|
|
471
|
+
/**
|
|
472
|
+
* Re-send one benefit that went astray.
|
|
473
|
+
*
|
|
474
|
+
* ⛔ SENDS THE SAME CODE. It never mints a second coupon, so a support
|
|
475
|
+
* ticket cannot become two discounts. For a benefit whose issuance failed
|
|
476
|
+
* before any coupon existed, this retries the issuance and mails the result.
|
|
477
|
+
*
|
|
478
|
+
* Rejects a signup that has not been confirmed and one that lapsed before a
|
|
479
|
+
* coupon was minted: there is nothing to re-send in either case, and
|
|
480
|
+
* nothing that may be created.
|
|
481
|
+
*/
|
|
482
|
+
resend: async (grantId) => {
|
|
483
|
+
return this.adminRequest(
|
|
484
|
+
"POST",
|
|
485
|
+
`/api/v1/newsletter-benefit/grants/${encodePathSegment(grantId)}/resend`
|
|
486
|
+
);
|
|
326
487
|
}
|
|
327
488
|
};
|
|
328
489
|
// -------------------- Stock alerts --------------------
|
|
@@ -2416,8 +2577,13 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2416
2577
|
*
|
|
2417
2578
|
* Rejected: a product that is not a KIT, a component from another store, a
|
|
2418
2579
|
* component that is itself a KIT, a VARIABLE component with no variant
|
|
2419
|
-
* pinned, a variant that does not belong to its product,
|
|
2420
|
-
* listed twice
|
|
2580
|
+
* pinned, a variant that does not belong to its product, the same slot
|
|
2581
|
+
* listed twice, and a component whose product or pinned variant is not
|
|
2582
|
+
* published.
|
|
2583
|
+
*
|
|
2584
|
+
* That last one is checked over the WHOLE list you send, not just the rows
|
|
2585
|
+
* you changed. Once a product already inside a kit is unpublished, no edit
|
|
2586
|
+
* to that kit saves until you publish it again or drop it from the list.
|
|
2421
2587
|
*
|
|
2422
2588
|
* @example
|
|
2423
2589
|
* ```typescript
|
|
@@ -2706,6 +2872,79 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
2706
2872
|
async updateOrder(orderId, data) {
|
|
2707
2873
|
return this.request("PATCH", `/api/v1/orders/${encodePathSegment(orderId)}`, data);
|
|
2708
2874
|
}
|
|
2875
|
+
/**
|
|
2876
|
+
* List the store's order custom field definitions.
|
|
2877
|
+
*
|
|
2878
|
+
* Call this before writing values: the `key` of each definition is what
|
|
2879
|
+
* {@link setOrderCustomFieldValues} accepts, and `type` is what a value has
|
|
2880
|
+
* to fit. Inactive definitions are included, so you can tell "the merchant
|
|
2881
|
+
* turned this field off" apart from "the merchant never created it".
|
|
2882
|
+
*
|
|
2883
|
+
* Requires an API key with the `orders:read` scope.
|
|
2884
|
+
*/
|
|
2885
|
+
async getOrderCustomFieldDefinitions() {
|
|
2886
|
+
return this.adminRequest("GET", "/api/v1/order-custom-fields");
|
|
2887
|
+
}
|
|
2888
|
+
/**
|
|
2889
|
+
* Read the custom field values stored on one order.
|
|
2890
|
+
*
|
|
2891
|
+
* Requires an API key with the `orders:read` scope.
|
|
2892
|
+
*/
|
|
2893
|
+
async getOrderCustomFieldValues(orderId) {
|
|
2894
|
+
return this.adminRequest(
|
|
2895
|
+
"GET",
|
|
2896
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/custom-fields`
|
|
2897
|
+
);
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Write custom field values onto an order.
|
|
2901
|
+
*
|
|
2902
|
+
* This is how work that finishes OUTSIDE Brainerce gets back onto the order
|
|
2903
|
+
* it belongs to. Subscribe to the `order.paid` webhook, call whatever third
|
|
2904
|
+
* party issues the thing you sell — a licence key, a booking reference, a
|
|
2905
|
+
* warranty number — then write the answer here. The value travels to the
|
|
2906
|
+
* merchant's own order email templates as `orderCustomFields` and, when the
|
|
2907
|
+
* definition is `isPublic`, to the customer's own order page. No email
|
|
2908
|
+
* template or endpoint has to be built per integration.
|
|
2909
|
+
*
|
|
2910
|
+
* ⛔ No default template PRINTS `orderCustomFields`. The variable reaches
|
|
2911
|
+
* every order email, but until the merchant adds the block to their template
|
|
2912
|
+
* once, a value written here is invisible to the customer. Writing the field
|
|
2913
|
+
* is not the same as the customer being told.
|
|
2914
|
+
*
|
|
2915
|
+
* The write is a MERGE: keys you leave out keep their current value, and
|
|
2916
|
+
* `null` clears a field that is not required. Values are coerced to the
|
|
2917
|
+
* definition's type and rejected with a 400 when they cannot be — but a key
|
|
2918
|
+
* with no active definition on the store is IGNORED rather than failing the
|
|
2919
|
+
* whole call, so read the returned `fields` to confirm what was stored.
|
|
2920
|
+
*
|
|
2921
|
+
* Pass an `idempotencyKey` when the caller may retry: an identical re-send
|
|
2922
|
+
* then replays the original response instead of writing again.
|
|
2923
|
+
*
|
|
2924
|
+
* Requires an API key with the `orders:write` scope.
|
|
2925
|
+
*
|
|
2926
|
+
* @example
|
|
2927
|
+
* ```typescript
|
|
2928
|
+
* // after the third party answered
|
|
2929
|
+
* await client.setOrderCustomFieldValues(
|
|
2930
|
+
* order.id,
|
|
2931
|
+
* { licence_key: 'ABCD-EFGH-IJKL' },
|
|
2932
|
+
* { idempotencyKey: `licence-${order.id}` }
|
|
2933
|
+
* );
|
|
2934
|
+
* // fires the "order completed" email, which carries the field
|
|
2935
|
+
* await client.updateOrder(order.id, { status: 'COMPLETED' });
|
|
2936
|
+
* ```
|
|
2937
|
+
*/
|
|
2938
|
+
async setOrderCustomFieldValues(orderId, fields, options) {
|
|
2939
|
+
return this.adminRequest(
|
|
2940
|
+
"PATCH",
|
|
2941
|
+
`/api/v1/orders/${encodePathSegment(orderId)}/custom-fields`,
|
|
2942
|
+
{ fields },
|
|
2943
|
+
void 0,
|
|
2944
|
+
"json",
|
|
2945
|
+
this.idempotencyHeaders(options)
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2709
2948
|
/**
|
|
2710
2949
|
* Update order status.
|
|
2711
2950
|
*
|
|
@@ -3022,6 +3261,13 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
3022
3261
|
* exist and 404'd silently. The live route is product-scoped:
|
|
3023
3262
|
* `GET /api/v1/products/:id/inventory`. A product with no inventory row
|
|
3024
3263
|
* reads back as all zeroes rather than 404ing.
|
|
3264
|
+
*
|
|
3265
|
+
* The response carries the whole {@link ProductInventoryResponse} — the
|
|
3266
|
+
* `InventoryItem` id, `trackingMode`, `backorderMode`, `backorderLimit`,
|
|
3267
|
+
* `lowStockThreshold`, `lastInventorySyncAt` and `updatedAt` alongside the
|
|
3268
|
+
* three counters. It always did; only the three counters were declared.
|
|
3269
|
+
* On the all-zeroes no-row branch everything but the counters is absent,
|
|
3270
|
+
* so test `id` rather than expecting a 404.
|
|
3025
3271
|
*/
|
|
3026
3272
|
async getInventory(productId) {
|
|
3027
3273
|
return this.adminRequest(
|
|
@@ -11005,6 +11251,13 @@ var ALLOWED_PAYMENT_HOSTS = [
|
|
|
11005
11251
|
// reachable, so a terminal configured against it keeps working.
|
|
11006
11252
|
"pay.hyp.co.il",
|
|
11007
11253
|
"icom.yaad.net",
|
|
11254
|
+
// iCredit (ריווחית) — the hosted payment page returned by
|
|
11255
|
+
// PaymentPageRequest. Both environments are listed EXPLICITLY rather than
|
|
11256
|
+
// allowing `rivhit.co.il`: the matcher below also accepts `*.<host>`, so a
|
|
11257
|
+
// bare parent entry would open every Rivhit subdomain (their accounting app,
|
|
11258
|
+
// marketing site, anything they add later) to a payment redirect.
|
|
11259
|
+
"icredit.rivhit.co.il",
|
|
11260
|
+
"testicredit.rivhit.co.il",
|
|
11008
11261
|
// Brainerce-hosted payment embeds (backend payment-embed proxy at
|
|
11009
11262
|
// `/api/payment/embed/...` that fronts provider apps' embed shells —
|
|
11010
11263
|
// e.g. cardcom-payments OpenFields wrapper). The match also covers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.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",
|