github electric-sql/electric @electric-sql/client@1.5.15

latest releases: @electric-sql/experimental@6.0.15, expo-db-electric-starter@1.0.16, @electric-sql/y-electric@0.1.41...
15 hours ago

Patch Changes

  • 9f767cf: Add fast-check model-based and micro-target property tests (plus static analysis for unbounded retry loops, unconditional 409 cache busters, tail-position awaits, and error-path #publish calls) and fix client bugs uncovered by the new PBT suite:

    Stream / retry-loop fixes (uncovered by model-based PBT):

    • Unconditionally create a new cache buster on every 409 response so that the follow-up request URL always differs from the pre-409 URL (prevents CDN infinite loops on cached 409s).
    • Fix a parked stack-frame leak in ShapeStream#start where awaiting a never-resolving live fetch retained the full error handler chain.
    • Add EXPERIMENTAL_LIVE_SSE_QUERY_PARAM to ELECTRIC_PROTOCOL_QUERY_PARAMS so canonicalShapeKey strips it; previously the SSE and long-polling code paths produced divergent cache keys for the same shape.
    • Replace the raw 409 response body publish in #requestShape with a synthetic must-refetch control message so subscribers clear accumulated state rather than receiving stale data rows.
    • Bound the onError retry loop at 50 consecutive retries so a broken onError handler can no longer spin forever.

    Micro-target PBT fixes:

    • canonicalShapeKey collapsing duplicate query params
    • Shape#process clobbering notifications on [up-to-date, insert] batches
    • subset__limit=0 / subset__offset=0 dropped on GET path due to truthiness check
    • Non-canonical JSON keys in Shape#reexecuteSnapshots dedup
    • snakeToCamel colliding multi-underscore columns
    • Shape#reexecuteSnapshots swallowing errors silently
    • SnapshotTracker leaving stale reverse-index entries on re-add/remove
    • Shape#awaitUpToDate hanging forever on a terminally-errored stream

    Shape notification contract fix:

    • Shape#process no longer notifies subscribers on data messages while the shape is still syncing (i.e. before the first up-to-date control message). Previously, the sync-service's initial response (offset=-1) could cause subscribers to fire with a partial view while stream.lastSyncedAt() was still undefined. Shape now follows the N1/N2 invariants documented in SPEC.md (Shape notification semantics).
    • Shape#process no longer fires an intermediate empty-rows notification on must-refetch. The status transitions back to syncing and subscribers receive the post-rotation state on the next up-to-date, matching the long-standing should resync from scratch on a shape rotation integration test.
  • b449f70: Bound the onError retry loop to prevent unbounded retries and memory growth. When onError always returns a retry directive for a persistent error (e.g. a 400 from a misconfigured proxy), the client now limits consecutive retries to 50 before tearing down the stream and notifying subscribers. The counter resets on successful data (non-empty message batch or 204 No Content), so intermittent errors that recover do not accumulate toward the limit.

  • 690e25a: Fix permanently stuck expired shape handles in localStorage by adding self-healing retry. When stale cache retries are exhausted (3 attempts with cache busters), the client now clears the expired entry from localStorage and retries once without the expired_handle parameter. Since the server never reuses handles (documented as SPEC.md S0), the fresh response will have a new handle and bypass stale detection. This prevents shapes from being permanently unloadable when a proxy strips cache-buster query parameters.

Don't miss a new electric release

NewReleases is sending notifications on new releases.