You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
stitch-rs PR #47 ("add update_if and delete_if compare-and-set", merged f25ae800 to main on 2026-10-17, closing stitch-rs#26) adds client-driven version-fenced writes and exposes them on the wasm surface as updateIf / deleteIf, with a stale-version failure surfaced to JS as err.kind = "preconditionFailed" (a new terminal error, distinct from the retryable conflict).
Please cut a stitch-wasm release carrying f25ae800, then a wrapper release that raises the stitch-wasm floor to it and surfaces updateIf / deleteIf (plus the err.kind = "preconditionFailed" mapping) through the TS API.
Why
This is the one primitive gating an atomic stale-hold reclaim downstream. Without a client CAS, reclaiming a departed holder's row is a non-atomic delete(stale) → create(new) (ticket-live holds.ts acquireHold), which races — another client can claim the key between the two ops. A single fenced deleteIf / updateIf collapses it into one atomic op and closes the gap.
Installed @laboverwire/stitch@0.6.2 → @laboverwire/stitch-wasm@0.5.1: update_if / delete_if / updateIf / deleteIf / preconditionFailed are all absent (grepped the published package).
Notes
Upstream calls it remote-first / online-only (sent to the broker first, never queued, applied locally only on success) — the wrapper surface should preserve that contract, not optimistic-queue it.
Built on mqdb-core 0.7.12's _expected_version CAS (412 → PreconditionFailed); confirm the adapter binding matches the new wasm exports.
The engine half of this shipped, but the wrapper surface this issue asks for did not — so this should stay open.
Engine (done):@laboverwire/stitch-wasm@0.6.0 is published and its tarball contains updateIf / deleteIf / preconditionFailed. ✅
Wrapper (still needed):@laboverwire/stitch@0.6.4 raised the stitch-wasm floor to ^0.6.0 but does not expose the CAS methods. Its Store interface (dist/types.d.ts) has only create / update / delete — no updateIf / deleteIf / rowVersion, and no typed err.kind. The 0.6.4 CHANGELOG says so directly:
"two new compare-and-set methods (updateIf, deleteIf) the wrapper does not expose"
Since the wrapper's compiled JS never forwards to the engine methods, there's no typed (or even cast) path to store.deleteIf(...) through @laboverwire/stitch.
The remaining ask: a wrapper release that adds to the Store interface:
updateIf / deleteIf forwarding to the engine, preserving the engine's remote-first / online-only contract (sent to the broker first, never optimistic-queued, applied locally only on success)
rowVersion(record) to read _version for the fence
the terminal precondition failure mapped to a typed err.kind === 'preconditionFailed', distinct from the retryable conflict
Downstream use: ticket-live's stale-hold reclaim (holds.ts acquireHold) is a non-atomic delete(stale) → create(new) today; a single fenced deleteIf / updateIf collapses it into one atomic op and closes the reclaim race.
Activity
fabracht commentedon Oct 22, 2026
The engine half of this shipped, but the wrapper surface this issue asks for did not — so this should stay open.
Engine (done):
@laboverwire/stitch-wasm@0.6.0is published and its tarball containsupdateIf/deleteIf/preconditionFailed. ✅Wrapper (still needed):
@laboverwire/stitch@0.6.4raised thestitch-wasmfloor to^0.6.0but does not expose the CAS methods. ItsStoreinterface (dist/types.d.ts) has onlycreate/update/delete— noupdateIf/deleteIf/rowVersion, and no typederr.kind. The 0.6.4 CHANGELOG says so directly:Since the wrapper's compiled JS never forwards to the engine methods, there's no typed (or even cast) path to
store.deleteIf(...)through@laboverwire/stitch.The remaining ask: a wrapper release that adds to the
Storeinterface:updateIf/deleteIfforwarding to the engine, preserving the engine's remote-first / online-only contract (sent to the broker first, never optimistic-queued, applied locally only on success)rowVersion(record)to read_versionfor the fenceerr.kind === 'preconditionFailed', distinct from the retryableconflictDownstream use: ticket-live's stale-hold reclaim (
holds.ts acquireHold) is a non-atomicdelete(stale) → create(new)today; a single fenceddeleteIf/updateIfcollapses it into one atomic op and closes the reclaim race.