@blamejs/blamejs-shop 0.4.58 → 0.4.59
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/lib/asset-manifest.json +1 -1
- package/lib/security-middleware.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.4.x
|
|
10
10
|
|
|
11
|
+
- v0.4.59 (2026-06-19) — **Win-back campaign sends now actually run (they were being silently blocked).** Fixes a defect that left win-back campaigns completely inert since they shipped: the scheduled worker tick that drives win-back enrolment and dispatch was being rejected by the application bot-guard before it could reach its own shared-secret gate, so no win-back step ever sent. The worker fires its internal cron ticks machine-to-machine with only a content type and the bridge-secret header — no browser fingerprint — and the bot-guard's missing-Accept-Language heuristic returned 403 for the win-back path. Every other internal cron tick (cart-recovery, stock-alert and low-stock sweeps, wishlist alerts and digests, campaign send, customer-portal expiry, stale-order reap, quote expiry) was already exempt from the bot-guard; the win-back tick was the one omission. It is now exempt as well, so the tick reaches its handler (still gated by the bridge secret), and an operator who has win-back configured now sees the escalating offer sequence actually go out. A new test asserts every internal cron path is exempt from the bot-guard, so a future cron-driven feature can't ship blocked the same way. **Fixed:** *Win-back campaign cron tick was blocked by the bot-guard* — The internal /_/winback-send-tick POST the worker fires on a schedule was missing from the bot-guard exemption list, so the application bot-guard returned 403 on the missing-Accept-Language heuristic before the handler's bridge-secret check ran. Win-back enrolment and step dispatch therefore never executed. The path is now exempt like the other nine internal cron ticks (the bridge-secret gate remains the deciding check, so an unauthenticated caller is still refused). A bot-guard skip-list parity test now asserts every internal cron path is exempt, anchored to the security middleware so it runs in the container image too — closing the gap that let this ship.
|
|
12
|
+
|
|
11
13
|
- v0.4.58 (2026-06-19) — **Make concurrent state transitions atomic across payouts, key rotation, and credit accounting.** Closes a class of concurrency races where a state transition or ledger write read the current state, checked it in application code, and then wrote — leaving a window in which two requests for the same record could both pass the check and both apply their effect. Each of these now performs the check and the write as a single conditional statement (the update or insert only lands when the record is still in the expected state, or the balance still satisfies the invariant), so exactly one of two racing callers wins and the other is cleanly refused. The affected operations: an affiliate commission can no longer be marked paid (or voided) twice, so a second payout reference can't be issued for one commission; rotating an API key claims the rotation before minting the replacement, so two concurrent rotations can't issue two successor keys; a B2B credit charge, hold release, and payment now evaluate the credit limit and running balance inside the insert, so concurrent charges can't push an account past its limit or corrupt its denormalized balance; fulfilling or cancelling a backorder line debits the per-SKU pending counter only when it wins the line transition, so the counter can't be double-debited; and a click-and-collect pickup advances the parent order exactly once. No API change and no migration — the operations behave identically for a single caller and now stay correct under concurrent ones. **Fixed:** *Affiliate commission payout transitions are atomic* — markCommissionPaid and markCommissionVoided now gate the status change on the row still being pending in the UPDATE itself and refuse (AFFILIATE_COMMISSION_TRANSITION_REFUSED) when they lose the race. Previously the pending check ran in application code before an unconditional write, so two concurrent payouts for the same commission could both proceed and issue two payout references for one commission, or a void could clobber a paid row. · *API-key rotation claims the rotation before issuing the replacement* — rotate now transitions the key from active to rotated with a conditional update and refuses (API_KEY_NOT_ROTATABLE) if the row is no longer active, before minting the successor key. Previously two concurrent rotations of the same key could both pass the active check and both issue a replacement, leaving an unexpected extra valid credential. · *B2B credit charges, releases, and payments enforce their invariant atomically* — chargeOrder, releaseHold, and recordPayment now compute the new running balance and evaluate their invariant (the credit limit for a charge, the outstanding balance for a payment, the order's net exposure for a release) inside a single conditional insert. Previously the balance was read, checked, and written in separate steps, so two concurrent charges could both pass the limit check — pushing the account past its credit limit and stamping the same balance_after on both rows, permanently under-stating outstanding exposure. Refusals carry the same typed codes (CREDIT_LIMIT_EXCEEDED, CREDIT_PAYMENT_EXCEEDS_OUTSTANDING, CREDIT_RELEASE_NOT_FOUND) and the denormalized per-customer balance chain stays correct under concurrent writes. · *Backorder fulfilment and cancellation debit the counter exactly once* — fulfillBackorder and cancelBackorder now claim the line's pending to fulfilled/cancelled transition with a conditional update and only decrement the per-SKU pending_quantity counter when the claim wins. Previously the line status was checked in application code before an unconditional write and counter decrement, so two concurrent calls for one line could both decrement the counter, under-counting pending demand. · *Click-and-collect pickup advances the order once* — markPickedUp now claims the schedule's ready to picked_up transition with a conditional update before driving the parent order to its delivered state, so two concurrent pickups for one order can't both advance the order FSM.
|
|
12
14
|
|
|
13
15
|
- v0.4.57 (2026-06-19) — **Refresh the vendored blamejs framework to 0.15.13.** Refreshes the vendored blamejs framework from 0.15.12 to 0.15.13, which consolidates a set of duplicated, security-sensitive code paths onto shared hardened primitives so the strongest available guard now applies everywhere an operation happens. The shop picks up several concrete fixes by composing the framework: a webhook delivery that fails with a transient transport error (connection reset, timeout, 5xx) now backs off and retries up to the configured limit instead of being recorded as permanently failed and dead-lettered, with the SSRF / blocked-host destination check the only permanent failure; an inline webhook delivery is claim-guarded before the POST so a concurrent retry pass can no longer pick up and re-send the same delivery; the webhook deliveries table now creates on a MySQL backend (the pending-delivery index is dialect-aware); and the best-effort final audit checkpoint fired during database shutdown now binds to the database it measured and refuses to write if that handle has been closed or replaced, so a checkpoint can never anchor one database's chain tip into a freshly opened one. File reads across the framework route through a single TOCTOU-safe reader that binds a file's size, contents, and integrity measurement to the inode an open fd holds, closing the swap-after-stat race on operator-writable paths. The Content-Security-Policy response-header parser now splits on the literal comma in linear time, removing a quadratic stall on a crafted header. Two configuration-validation paths (HTTP client pool options, database stream-limit) now throw the framework's typed error instead of a bare Error; both remain Error subclasses, so existing try/catch handling is unaffected. This refresh carries no shop-facing API change and applies no migration; it keeps the bundled framework current and tightens the security posture without adding any opt-in. **Changed:** *Vendored blamejs refreshed to 0.15.13* — The bundled framework is updated to blamejs 0.15.13. Webhook deliveries that hit a transient transport failure (connection reset, timeout, 5xx) now retry with backoff up to the configured limit instead of being dead-lettered as permanently failed; only the SSRF / blocked-host destination check is a permanent failure. An inline webhook delivery is claimed (marked in-flight before the POST) so a concurrent retry pass cannot double-send it, and a row left in-flight by a crash is reclaimed after the stale-claim window. The webhook deliveries table now creates on MySQL via a dialect-aware index. The audit checkpoint fired at database shutdown binds to the database it read the tip from and fail-closes (writes nothing) if that handle has since been closed or replaced. Every framework file read now uses a TOCTOU-safe reader (open fd, then bind size / contents / integrity to that inode), with symlink refusal, inode-equality, a byte cap, and an integrity hash available to callers. The Content-Security-Policy header parser splits on the literal comma in linear time (removing a quadratic-stall vector on a crafted header). Parsed INI and OpenAPI path maps are null-prototype objects, so a parser gap could only set an own property and never reach Object.prototype. HTTP-client pool-option and database stream-limit configuration errors now throw the module's typed framework error (still an Error subclass). No shop API change; the framework's PQC-first crypto, security middleware, and request lifecycle are carried forward as-is.
|
package/lib/asset-manifest.json
CHANGED
package/package.json
CHANGED