@blamejs/blamejs-shop 0.4.23 → 0.4.24
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/CHANGELOG.md +2 -0
- package/README.md +6 -1
- package/SECURITY.md +13 -0
- package/lib/admin.js +1228 -15
- package/lib/asset-manifest.json +5 -5
- package/lib/customers.js +53 -0
- package/lib/cycle-counting.js +24 -4
- package/lib/gift-card-ledger.js +81 -10
- package/lib/giftcards.js +88 -0
- package/lib/inventory-allocations.js +33 -14
- package/lib/inventory-receive.js +116 -20
- package/lib/inventory-writeoffs.js +53 -64
- package/lib/loyalty-earn-rules.js +117 -0
- package/lib/loyalty.js +79 -0
- package/lib/newsletter.js +39 -2
- package/lib/operator-audit-log.js +20 -0
- package/lib/operator-inbox.js +202 -9
- package/lib/order.js +227 -27
- package/lib/quotes.js +107 -15
- package/lib/referrals.js +71 -0
- package/lib/security-middleware.js +27 -1
- package/lib/stock-transfers.js +185 -53
- package/lib/storefront.js +979 -126
- package/lib/translations.js +1 -0
- package/lib/webhook-receiver.js +15 -19
- package/lib/wishlist-alerts.js +37 -0
- package/package.json +1 -1
package/lib/translations.js
CHANGED
|
@@ -606,6 +606,7 @@ var CHROME_DEFAULTS = Object.freeze({
|
|
|
606
606
|
footer_operators_heading: "Your account",
|
|
607
607
|
footer_operators_account: "Account",
|
|
608
608
|
footer_operators_orders: "Orders",
|
|
609
|
+
footer_operators_suggestions: "Suggestion box",
|
|
609
610
|
footer_operators_contact: "Contact",
|
|
610
611
|
|
|
611
612
|
footer_copy_suffix: "built on blamejs · Apache 2.0 licensed.",
|
package/lib/webhook-receiver.js
CHANGED
|
@@ -104,12 +104,12 @@ var SLUG_RE = /^[a-z0-9][a-z0-9_\-]{0,63}$/;
|
|
|
104
104
|
var MAX_HEADER_NAME_LEN = 128;
|
|
105
105
|
var HEADER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_\-]{0,127}$/;
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
107
|
+
// Idempotency keys are third-party-supplied opaque strings. Validation is
|
|
108
|
+
// delegated to the framework's idempotency-key guard (strict profile:
|
|
109
|
+
// ≤256 bytes, ASCII-only) which additionally refuses path-traversal
|
|
110
|
+
// shapes — `/`, `\`, and `..` runs — that the earlier printable-ASCII
|
|
111
|
+
// regex let through. Strict allows a literal space (security-neutral for
|
|
112
|
+
// a stored-comparison key) and does NOT NFC-normalize the input.
|
|
113
113
|
|
|
114
114
|
var MAX_OUTCOME_LEN = 280;
|
|
115
115
|
var MAX_REASON_LEN = 1024;
|
|
@@ -228,20 +228,16 @@ function _retry(v) {
|
|
|
228
228
|
|
|
229
229
|
function _idempotencyKey(s) {
|
|
230
230
|
if (s == null) return null;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
);
|
|
231
|
+
// The framework guard owns the alphabet + length policy. It refuses
|
|
232
|
+
// empty keys, control bytes, non-ASCII, over-length, and the
|
|
233
|
+
// path-traversal shapes (`/`, `\`, `..`) the prior regex admitted.
|
|
234
|
+
// Translate its structured error to this primitive's TypeError shape
|
|
235
|
+
// so callers keep a uniform validation-failure surface.
|
|
236
|
+
try {
|
|
237
|
+
return b.guardIdempotencyKey.validate(s, { profile: "strict" });
|
|
238
|
+
} catch (e) {
|
|
239
|
+
throw new TypeError("webhookReceiver: idempotency_key — " + (e && e.message || "invalid idempotency key"));
|
|
238
240
|
}
|
|
239
|
-
if (!IDEMPOTENCY_KEY_RE.test(s)) {
|
|
240
|
-
throw new TypeError(
|
|
241
|
-
"webhookReceiver: idempotency_key must be printable ASCII (no control / non-ASCII bytes)"
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
return s;
|
|
245
241
|
}
|
|
246
242
|
|
|
247
243
|
function _eventId(s) {
|
package/lib/wishlist-alerts.js
CHANGED
|
@@ -77,6 +77,13 @@
|
|
|
77
77
|
* paths don't double-deliver the same restock signal. Without
|
|
78
78
|
* it, every back_in_stock match fires regardless of stockAlerts
|
|
79
79
|
* state.
|
|
80
|
+
* - emailSuppressions (optional) — when wired, the scanner
|
|
81
|
+
* short-circuits sends to addresses on the marketing-scope
|
|
82
|
+
* suppression list (hard bounce / spam complaint / unsubscribe)
|
|
83
|
+
* and accounts them as `suppressed` without consuming a
|
|
84
|
+
* dedupe / weekly-cap slot. These alerts are marketing mail and
|
|
85
|
+
* honour the same suppression list every other marketing path
|
|
86
|
+
* consults.
|
|
80
87
|
*
|
|
81
88
|
* Monotonic clock: a per-factory monotonic timestamp ensures that
|
|
82
89
|
* two alerts written in the same wall-clock millisecond carry
|
|
@@ -269,6 +276,16 @@ function create(opts) {
|
|
|
269
276
|
if (stockAlerts && typeof stockAlerts.isSubscribed !== "function") {
|
|
270
277
|
throw new TypeError("wishlistAlerts.create: opts.stockAlerts must expose isSubscribed when wired");
|
|
271
278
|
}
|
|
279
|
+
// Marketing suppression list. When wired the scanner short-circuits
|
|
280
|
+
// sends to addresses that hard-bounced, filed a spam complaint, or
|
|
281
|
+
// unsubscribed — these price-drop / back-in-stock notices are
|
|
282
|
+
// marketing-scope mail and must honour the same suppression list every
|
|
283
|
+
// other marketing path consults. Optional so an operator running only
|
|
284
|
+
// the cron without the suppression primitive still dispatches.
|
|
285
|
+
var emailSuppressions = opts.emailSuppressions || null;
|
|
286
|
+
if (emailSuppressions && typeof emailSuppressions.isSuppressed !== "function") {
|
|
287
|
+
throw new TypeError("wishlistAlerts.create: opts.emailSuppressions must expose isSuppressed when wired");
|
|
288
|
+
}
|
|
272
289
|
|
|
273
290
|
// Pagination cursor secret — same posture as wishlist.create.
|
|
274
291
|
if (typeof opts.cursorSecret !== "string" || !opts.cursorSecret.length) {
|
|
@@ -831,6 +848,7 @@ function create(opts) {
|
|
|
831
848
|
weekly_cap_reached: 0,
|
|
832
849
|
recent_dedupe: 0,
|
|
833
850
|
no_email: 0,
|
|
851
|
+
suppressed: 0,
|
|
834
852
|
email_dispatch_failed: 0,
|
|
835
853
|
no_baseline_price: 0,
|
|
836
854
|
no_current_price: 0,
|
|
@@ -892,6 +910,25 @@ function create(opts) {
|
|
|
892
910
|
skipped += 1; skippedBy.no_email += 1; continue;
|
|
893
911
|
}
|
|
894
912
|
|
|
913
|
+
// Marketing-suppression short-circuit. A hard-bounced /
|
|
914
|
+
// spam-complaint / unsubscribed address must not be mailed.
|
|
915
|
+
// Checked BEFORE the ledger claim so a suppressed customer never
|
|
916
|
+
// consumes a dedupe / weekly-cap slot for a send that can't
|
|
917
|
+
// happen — if they later resubscribe the next sweep can deliver.
|
|
918
|
+
if (emailSuppressions) {
|
|
919
|
+
var suppressed = false;
|
|
920
|
+
try {
|
|
921
|
+
var supView = await emailSuppressions.isSuppressed({
|
|
922
|
+
email: customerEmail,
|
|
923
|
+
scope: "marketing",
|
|
924
|
+
});
|
|
925
|
+
suppressed = !!(supView && supView.suppressed === true);
|
|
926
|
+
} catch (_eSup) { suppressed = false; }
|
|
927
|
+
if (suppressed) {
|
|
928
|
+
skipped += 1; skippedBy.suppressed += 1; continue;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
895
932
|
// Atomic dedupe + weekly-cap CLAIM. The conditional INSERT is the
|
|
896
933
|
// serialization point: two concurrent sweeps can't both pass the
|
|
897
934
|
// dedupe/cap guard and then both write — the loser's INSERT...
|
package/package.json
CHANGED