Skip to content

refactor: on-demand cache sync, message validation, tests - #263

Merged
dborysov merged 4 commits into
mainfrom
refactor/on-demand-sync
Oct 20, 2026
Merged

dborysov merged 4 commits into
mainfrom
refactor/on-demand-sync

Conversation

@dborysov

@dborysov dborysov commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • On-demand streaming: the page subscribes to the QueryClient caches only between START_SYNC and STOP_SYNC, which the background sends when the first viewer of a tab connects and when the last one leaves. Cache events are coalesced per query hash / mutation id and flushed in batches. Pages without TanStack Query never open a port to the background.
  • Message validation: every bridge (MAIN ↔ ISOLATED) and port message goes through runtime guards (utils/guards.ts) instead of a type cast.
  • Background: keeps per-tab entry maps only while a viewer is connected, sends CONNECT_ACK, ignores stale relay ports, and tracks client presence (CLIENT_READY) separately from streaming for the toolbar icon.
  • Bounded encoding (utils/serialization.ts, utils/wire-entities.ts):
    • Each user-data field (data, error, meta, variables, …) gets an 8 MiB UTF-8 budget plus a nesting cap. Entry structure is always sent whole.
    • Containers repeated within one snapshot or flush draw from a shared 16 MiB allowance (EncodePass).
    • Typed arrays are walked lazily.
    • Anything over budget becomes a truncated sentinel, shown as [Truncated]. Positions are kept, so path edits still target the right page value.
    • Decoded Maps and Sets stop at the first colliding key or member, so positional edit paths stay aligned.
  • Chunked transfer (utils/chunking.ts):
    • Snapshots go out as size-bounded chunks (snapshotId/chunkIndex/chunkCount), from the page and from the background cache for late viewers. Large update batches are split the same way.
    • Messages are sized in UTF-8 bytes, with a 32 MiB backstop in the relay. The old 10k-entry list cap is gone.
    • A lost update triggers a rate-limited resync (utils/resync.ts). A lost snapshot shows an explicit failure state instead of the setup screen, and viewers show loading progress for multi-chunk snapshots.
  • UI:
    • Virtualized query and mutation lists (@tanstack/react-virtual).
    • Shared EntityList, DetailHeader and Toolbar components, plus an ErrorBoundary.
    • TreeView reworked to show large nodes in collapsed ranges.
    • Map entries addressed by index (MapEntrySegment).
  • Tests: vitest suite (258 tests) covering serialization, set-at-path, sorting, apply-changes, chunking, and end-to-end sync flows on a fake extension harness.
  • Tooling:
    • CI runs pnpm test, and action versions are pinned.
    • Dependabot groups the react packages and also tracks github-actions.
    • Node 24.21.0.
    • @tanstack/query-core moved to devDependencies (it is only imported for types).
  • Manifest: removed the <all_urls> host permission, updated the description, version 1.0.3.

Testing

  • pnpm compile, pnpm test (258 passed), biome ci, pnpm build and pnpm build:firefox all pass.
  • Encoder benchmarks on pathological shapes (shared and back-referenced graphs, React Fiber-like _owner graphs, large typed arrays) stay within roughly 50–450 ms per snapshot. Ordinary data encodes byte-identically to before.
  • Not tested by hand in Chrome or Firefox. The end-to-end tests use fake ports and a fake window.

…, add tests

- Page streams only between START_SYNC/STOP_SYNC; changes are coalesced
  per entity and flushed in batches
- Validate every bridge and port message with runtime guards; drop
  oversized or unencodable payloads in the relay
- Background caches per-tab entries only while a viewer is connected and
  ignores stale relay ports
- Virtualized query/mutation lists, shared EntityList/DetailHeader/Toolbar,
  ErrorBoundary, chunked TreeView with Map entries addressed by index
- Add vitest suite for serialization, set-at-path, sorting, apply-changes
- CI runs tests; pin action versions; dependabot groups react and tracks
  github-actions
- Drop <all_urls> host permission; bump version to 1.0.3
Shared references are still duplicated, but each user-data field now has an
8 MiB UTF-8 budget and a nesting cap; past them values become a truncated
sentinel shown as [Truncated] in the viewer. Snapshot entries are encoded and
isolated one by one, and the encoder reports exact UTF-8 JSON size.
…ures

- Drop the 10k list cap; size messages in UTF-8 bytes (32 MiB backstop)
- Page and background split snapshots into ~8 MiB chunks, reassembled with
  supersession; oversized entities re-encoded with smaller field budgets
- Relay wraps port posts, resyncs on a lost update with backoff, never on a
  lost snapshot or forged junk
- Viewers show loading progress and an explicit failure state instead of the
  setup screen
- Snapshots and flushes share one EncodePass: containers met again in user
  data are charged to a 16 MiB repeat allowance and truncated once it is
  spent; retries and failures roll back
- Typed arrays are walked lazily instead of via Object.keys
- Decoding stops a Map/Set at the first colliding key or member and counts
  the rest as omitted, so positional edit paths stay aligned
@dborysov
dborysov merged commit c100602 into main Oct 20, 2026
8 checks passed
@dborysov
dborysov deleted the refactor/on-demand-sync branch October 20, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant