@blamejs/blamejs-shop 0.4.95 → 0.4.96
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/quotes.js +8 -16
- package/lib/text-guard.js +8 -14
- 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.96 (2026-06-25) — **Compose two newly-public framework primitives directly, removing an internal-path dependency and a drift-prone duplicate transition table.** Two internal refactors that adopt framework surface the application previously had to work around, with no change in behaviour. The text-guard module composed the framework's codepoint-threat catalog by reaching into an internal vendored module path, because the catalog was not on the framework's public surface; it now composes the public b.codepointClass (the same module, reference-identical), so a future framework refresh that moves the internal file can't break the import. The quotes module kept a hand-rolled loop that re-derived a quote's next status from a second copy of its transition table; it now resolves the destination through the state machine's own side-effect-free resolver, off the single transition definition, so the two can no longer drift apart. No configuration changes. **Changed:** *Text-guard composes the public codepoint-threat catalog* — The free-text guard built its bidi / control / null-byte / zero-width / mixed-script screening on the framework's codepoint catalog by requiring an internal vendored module path, since the catalog was not yet exported publicly. It now composes the public b.codepointClass — the identical module — so the screening no longer depends on an internal file location that an upstream refresh could relocate. · *Quote transitions resolve through the state machine instead of a duplicate table* — Validating a quote's lifecycle edge (respond / accept / reject / cancel / expire / convert) used the state machine to check the edge was legal, then a separate hand-rolled loop over a second copy of the transition table to read the destination status. The destination is now resolved by the state machine's own side-effect-free resolver, off the single transition definition the machine is built from, removing the duplicate table walk that could drift from it.
|
|
12
|
+
|
|
11
13
|
- v0.4.95 (2026-06-25) — **An order state change no longer logs a phantom transition when it loses a concurrent race, and a transient failure writing the history row no longer strands a stock hold.** Advancing an order through its lifecycle reads the order's current status, resolves the next state, then writes it with an UPDATE guarded on the row still being in the state it was read in — so when two writers race the same order (the stale-order reaper cancelling while a payment webhook marks it paid), exactly one wins and the loser collapses to a no-op. Two issues in that path are fixed. First, the state machine's transition audit was emitted before the guarded write resolved, so a writer that LOST the race still recorded a 'success' transition into the audit trail for a state change that never landed; the audit is now written only for the writer that actually moved the row. Second, the denormalized history-row insert ran between the committed status write and the inventory settlement with no failure handling: a transient error writing that row aborted the whole call after the status had already advanced, so the stock hold was never converted to a shelf debit (or released) and a webhook retry — finding the order already advanced — skipped settlement entirely, stranding the hold. The history insert is now best-effort (the state-machine audit is the authoritative record), so a failure writing it never prevents the hold from settling. No configuration changes. **Changed:** *Vendored framework refreshed to 0.15.24* — Updates the vendored blamejs framework to 0.15.24, an upstream build supply-chain hardening release with no runtime API changes. No operator action required. **Fixed:** *A lost order-transition race no longer records a phantom 'success' in the audit trail* — The order state machine emitted its transition audit during the in-memory replay, before the guarded status write that decides the race. A writer that lost the race (its conditional update matched zero rows and it no-opped) had nonetheless already written a 'success' transition into the audit trail — a record an auditor could not tell apart from a real one. The destination state is now resolved side-effect-free, and the transition audit is emitted only after the guarded write wins, so the audit trail records exactly the transitions that actually landed. · *A transient failure writing an order's history row no longer strands its stock hold* — After an order's status write committed, the order_transitions history row was inserted with no failure handling, ahead of the inventory settlement. A transient error on that insert aborted the call with the order already advanced, so the held stock was never debited or released; a webhook retry then found the order past its starting state, no-opped at the concurrency guard, and skipped settlement — leaving the hold stranded with no automatic recovery. A history-row write failure no longer aborts settlement, so the hold always settles once the state change commits. Because that row is also where a PayPal capture id is recovered from, a write failure is not silently dropped: the row's full payload — including any capture metadata — is recorded to the durable error feed (and the audit chain) for reconciliation.
|
|
12
14
|
|
|
13
15
|
- v0.4.94 (2026-06-25) — **A print-on-demand fulfillment status change is now an atomic claim — concurrent updates can no longer clobber each other or record a phantom transition.** Advancing a print-on-demand fulfillment through its lifecycle (submitted → shipped / failed / cancelled) read the row's current status, validated the move in memory, and then wrote the new status with an UPDATE keyed on the row id alone. Two updates racing the same fulfillment — a worker marking it shipped while an operator marks it failed, or a re-delivered supplier webhook — both read the same starting status, both passed the in-memory check, and both wrote unconditionally, so the later write silently overwrote the earlier one's status and columns (a lost update), and each emitted a 'success' transition into the audit trail even though only one write reflected reality. The status write is now a single-statement atomic claim gated on the row still being in the state it was read in (the same compare-and-swap the dropship-forwarding and returns lifecycles already use): exactly one update wins, the loser changes zero rows and is refused, and the audit record is written only for the update that actually changed the row. No configuration changes. **Fixed:** *Concurrent print-on-demand fulfillment transitions no longer clobber each other or log a phantom transition* — The fulfillment status update was keyed on the row id with no guard on the starting state, so two concurrent transitions both wrote unconditionally — last-writer-wins overwrote the other's status and stamped columns, and both emitted a 'success' transition audit. The update is now an atomic compare-and-swap gated on the row still being in the from-state, with the destination resolved side-effect-free and the audit emitted only on the winning write. The losing transition changes zero rows and is refused with a clear concurrency error instead of corrupting the record.
|
package/lib/asset-manifest.json
CHANGED
package/lib/quotes.js
CHANGED
|
@@ -212,35 +212,27 @@ function _getQuoteFsm() {
|
|
|
212
212
|
return _quoteFsm;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
// Resolve the to-status for a legal (from, event) pair off the transition
|
|
216
|
-
// table — the FSM's `can()` only answers yes/no, so the destination comes from
|
|
217
|
-
// the declaration the machine was built from (one source of truth).
|
|
218
|
-
function _toStatus(fromStatus, event) {
|
|
219
|
-
for (var i = 0; i < QUOTE_TRANSITIONS.length; i += 1) {
|
|
220
|
-
var t = QUOTE_TRANSITIONS[i];
|
|
221
|
-
if (t.from === fromStatus && t.on === event) return t.to;
|
|
222
|
-
}
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
215
|
// Validate one status-changing edge through the FSM. `event` is the verb name
|
|
227
216
|
// above; `fromStatus` is the row's CURRENT status. Returns the resolved
|
|
228
217
|
// to-status on success. Throws a coded QUOTE_TRANSITION_REFUSED Error when the
|
|
229
218
|
// machine refuses the edge (terminal state, wrong source state, or unknown
|
|
230
219
|
// event) so the caller maps it to a uniform 409 regardless of which guard
|
|
231
|
-
// tripped — the same shape the hand-rolled status checks used to throw.
|
|
232
|
-
//
|
|
233
|
-
//
|
|
220
|
+
// tripped — the same shape the hand-rolled status checks used to throw.
|
|
221
|
+
// `target` resolves the destination state side-effect-free (no audit emit, no
|
|
222
|
+
// state mutation) and returns null on an illegal edge — so it is BOTH the gate
|
|
223
|
+
// and the to-status resolver in one call, read off the single FSM source of
|
|
224
|
+
// truth rather than a parallel transition-table walk that could drift from it.
|
|
234
225
|
function _assertTransition(fromStatus, event, verbLabel) {
|
|
235
226
|
var fsm = _getQuoteFsm();
|
|
236
227
|
var instance = fsm.restore({ state: fromStatus, history: [], context: {} });
|
|
237
|
-
|
|
228
|
+
var toStatus = instance.target(event);
|
|
229
|
+
if (toStatus == null) {
|
|
238
230
|
var refused = new Error("quotes." + verbLabel + ": refused — quote is " +
|
|
239
231
|
fromStatus + ", cannot " + event);
|
|
240
232
|
refused.code = "QUOTE_TRANSITION_REFUSED";
|
|
241
233
|
throw refused;
|
|
242
234
|
}
|
|
243
|
-
return
|
|
235
|
+
return toStatus;
|
|
244
236
|
}
|
|
245
237
|
|
|
246
238
|
// ---- validators ---------------------------------------------------------
|
package/lib/text-guard.js
CHANGED
|
@@ -15,13 +15,9 @@
|
|
|
15
15
|
* The dangerous-codepoint surface (bidi overrides, C0 control bytes,
|
|
16
16
|
* null bytes, zero-width / invisible formatting, mixed-script
|
|
17
17
|
* confusables) is NOT reinvented here — it composes the framework's
|
|
18
|
-
* own codepoint catalog, the single source of
|
|
19
|
-
* family already builds on
|
|
20
|
-
*
|
|
21
|
-
* the catalog leaf directly. The leaf has no dependency on any shop
|
|
22
|
-
* module, so requiring it is refresh-safe; an upstream one-line
|
|
23
|
-
* `module.exports` add would let this collapse to `b.codepointClass`
|
|
24
|
-
* with no behavior change.
|
|
18
|
+
* own codepoint catalog (`b.codepointClass`), the single source of
|
|
19
|
+
* truth the guard-* family already builds on, so the tables and
|
|
20
|
+
* regexes never drift into a second hand-maintained copy.
|
|
25
21
|
*
|
|
26
22
|
* Validators THROW a `TypeError` on bad input. The admin / account
|
|
27
23
|
* request wrappers map a thrown `TypeError` to a clean 400, so a
|
|
@@ -50,13 +46,11 @@
|
|
|
50
46
|
var b = require("./vendor/blamejs");
|
|
51
47
|
|
|
52
48
|
// The framework's codepoint catalog (bidi / control / null / zero-width
|
|
53
|
-
// tables + regexes, the script ranges, and the detect/assert/strip
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
// allow:vendor-hand-edit — composing the framework's codepoint catalog leaf (no shop dependency, refresh-safe); collapses to b.codepointClass once upstream re-exports it
|
|
59
|
-
var codepointClass = require("./vendor/blamejs/lib/codepoint-class");
|
|
49
|
+
// tables + regexes, the script ranges, and the detect/assert/strip helpers
|
|
50
|
+
// the guard-* family composes), exposed on the public surface as
|
|
51
|
+
// b.codepointClass — so this composes a single source of truth instead of a
|
|
52
|
+
// second copy of the tables.
|
|
53
|
+
var codepointClass = b.codepointClass;
|
|
60
54
|
|
|
61
55
|
// ---- shop validators ----------------------------------------------------
|
|
62
56
|
|
package/package.json
CHANGED