- Add saveDocImmediate() for synchronous awaitable saves (no debounce)
- Add SyncServer.flushAll() to iterate all in-memory docs
- Fix eviction race: onDocEvict now uses saveDocImmediate instead of
debounced saveDoc (which could fire after doc deleted from memory)
- Add SIGTERM/SIGINT handlers with 10s timeout safety net
- Add visibilitychange flush on client (reliable on mobile/bfcache)
- Flush pending IDB saves in DocSyncManager.disconnect()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser-side:
- Fix switchSpace() to LRU-evict idle space WebSocket connections (cap: 3)
- Add runtime.unsubscribe() to disconnectedCallback in 24 components
- Fix DocSyncManager.unsubscribe() to clean up syncStates, timers, listeners
- Fix 14 components leaking RAF loops, ResizeObservers, MutationObservers,
document/window listeners, setIntervals, MapLibre WebGL contexts, and
AbortControllers on disconnect
- Deduplicate Automerge WASM: module builds now use global shim from
shell-offline instead of bundling ~2.5MB each (8 modules affected)
Server-side:
- Add LRU eviction to SyncServer.#docs (cap: 500, evicts idle docs with
no subscribers, persists to disk before eviction)
- registerWatcher() now returns unsubscribe function
Data:
- Cap unbounded CRDT arrays: rexchange chatMessages (200), rcart events (200)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move RSpaceOfflineRuntime, CommunitySync, OfflineStore, and
RStackHistoryPanel into a new shell-offline.ts chunk loaded via
dynamic import(). This removes ~2.5MB of Automerge WASM from the
critical path, reducing blocking JS from ~960KB to ~150KB brotli.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>