Conversation
Adapter-author field report: implementing the PR #1564 SSR contract in a sync backendFindings from building full SSR support for tanstack-do-db-collection (a Cloudflare Durable Object sync backend) against TanStack/db PR #1564 at What works well
Findings (ordered by how hard they bit)1. Rows are applied BEFORE
|
dbb093d to
64bbf64
Compare
…ndency) SSR (ADR-0011) tracks TanStack DB draft PR #1564, whose dehydrate/hydrate/ DbClient and syncMeta hooks are upstream and unreleased. Tests build against a packed PR-branch tarball; removed when upstream publishes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNF6dsTLXtUxHJ6rEE33po
…R-0011 D1)
One consistent {rows, cursor} read over the DO binding, no WebSocket. The
required request runs through parseAttachment — the same auth gate as the WS
upgrade, so one tenant check guards both paths (test: a 'forbidden' identity
rejects the read). The cursor is a durable high-water mark (highWaterSeq =
max(currentSeq, drainCursor)), robust to retention pruning the changelog empty;
'0' honestly means no resume point. A catch-up terminal is scoped to its sub
(uptodate.sub) so a transient hydration sub tears down on its own boundary.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KNF6dsTLXtUxHJ6rEE33po
…, syncMeta hooks (ADR-0011) Transport becomes a generic + branded structural interface Transport<Api>; WebSocketTransport<Api> and the new SsrSnapshotTransport<Api> both satisfy it and carry Api, so doCollectionOptions inference survives the seam. seedCursor claims a shorter applied prefix (SSR hydration) — a live regress forces a reconnect (advance suppressed) so replay owns the repair window. The eager path always arms snapshot reconcile (authoritative set semantics, no flash-to-empty); the syncMeta hooks (export/import/merge) fail loud but SAFE — hydratedCursor='0' before throwing so applied rows still reconcile. Test mocks that hit the eager reconcile carry _state.syncedData. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNF6dsTLXtUxHJ6rEE33po
One todos collection in a DO, server-rendered two ways (useLiveQuery / useLiveSuspenseQuery). Authored with the object-schema API (defineSync); the browser brands its transport with the exported TodosApi. Loader reads one snapshot and dehydrates; the browser hydrates, paints, and converges live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNF6dsTLXtUxHJ6rEE33po
Generalizes ADR-0002 C1 (deltas flush before committed) to C1' (a socket's pending coalesced deltas precede any cursor boundary). Marks SSR experimental in the changelog under [Unreleased]. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNF6dsTLXtUxHJ6rEE33po
SSR support (experimental) — dehydrate on the worker, hydrate to the cursor
Implements SSR for this adapter against TanStack DB draft PR #1564 (
DbClient,dehydrate()/hydrate(), theexportSyncMeta/importSyncMeta/mergeSyncMetasync-config hooks). Design and every trade-off: ADR-0011.What lands
readSyncSnapshot(req, request)RPC — one consistent{rows, cursor}read over the DO binding, no WebSocket. The requiredrequestruns throughparseAttachment: one auth gate for the socket and the read path. Cursor is a durable high-water mark;"0"honestly means "no resume point".SsrSnapshotTransport(same adapter, swapped at the new structuralTransportseam; read-only, fails loud), syncMeta{v, cursor, where-fingerprint}round-trip,sinceon first sub,seedCursor(late chunks regress-and-replay via forced reconnect), always-armed eager snapshot reconcile (authoritative set semantics — no flash-to-empty, no stranded deletes), on-demand transient catch-up with honest truncate for unresumable rows.uptodategains optionalsub(a catch-up's terminal is sub-scoped);subacceptssinceon first subscribe.examples/ssr: TanStack Start on Cloudflare —/live-query+/live-suspense-query(a hydrated collection never suspends; rows are in the server HTML inside a completed boundary).Hardened by two adversarial reviews (gpt-5.5) + a full grill session — five of their findings were real bugs, all fixed with pinned tests. 169 tests green against the vendored PR build (zero breakage from released 0.6.5 → PR 0.6.7).
Vendored upstream builds — provenance & rebase policy
vendor/*.tgzare built from upstream PR head132d53a9f03e9d0df442b2d15c74e5931925b77b(2026-05-30) — full provenance + single-copy-resolution gotcha invendor/README.md. Policy:vendor/, move to the published version, and rebase the tarball commits out of history before merging.Merge policy
mainseparately (fix/reconnect-hardening→ 0.3.1) — plan in.claude/scratch/reconnect-hardening-followup.md.Known limitations (documented in ADR-0011)
No incarnation epoch (protocol-rev material, deferred until client-side persistence makes old cursors routine); below-retention-floor hydration flashes (pathological: requires retention shorter than HTML flight time); pre-1.0 client/server version skew unsupported.
🤖 Generated with Claude Code