feat(solid-query): built-in single-flight consumer via FLIGHT_DATA_SOURCE - #11326
feat(solid-query): built-in single-flight consumer via FLIGHT_DATA_SOURCE#11326ryansolid wants to merge 3 commits into
Conversation
…URCE
Solid's single-flight channel is becoming multi-source (solidjs/solid
653dd41e): mutation responses carry a keyed envelope of per-cache
slices, each routed to the consumer subscribed under its source id, so
independent caches (Solid Router's route data, the query cache) refresh
from one round trip without competing for the single legacy slot.
QueryClientProvider now subscribes the query cache's consumer under the
exported FLIGHT_DATA_SOURCE id ("sq", matching the sq: hydration-registry
namespace): a mutation response carrying that slice — a DehydratedState
produced by a server collector registered with
registerFlightDataSource(FLIGHT_DATA_SOURCE, hook) — hydrates the
provider's client before the mutation's promise resolves. Mounted
queries on those keys update with no follow-up refetches, and apps
delete the hand-rolled subscribeFlightData/hydrate wiring entirely.
Subscribing is inert when no server collector exists (the server folds
nothing), client-only (the server registry is cross-request module
state), and torn down with the provider.
Requires the @solidjs/web release following 2.0.0-rc.4 for the
named-source protocol; a typed shim bridges the installed declarations
until the peer range bumps.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ering Fills the gaps the router-ssr-query transport used to cover, natively: - dehydrateSettled(client): the extraction half of a single-flight collector — waits for every in-flight fetch (chased to quiescence) so loaders' fire-and-forget prefetches land before dehydrating. - SSR teardown: the provider cancels and clears the per-request cache on render disposal, so user-configured finite gcTime timers cannot pin the client after the response. - The registry serializer now respects defaultOptions.dehydrate .shouldDehydrateQuery, the same knob apps use on any other transport. Co-authored-by: Cursor <cursoragent@cursor.com>
…ainst workspace-built core Core-side suites structurally cannot represent the adapter's composed shapes — rc.5 shipped a settle-walk regression (#3181's fix waking parked readers into uninitialized projections) that only the adapter's own suite could see: premature wakes are self-healing for ordinary async nodes and corrupt only through an empty-seed projection over a stable chained promise with boundary-parked readers. The gate packs signals/solid/web from the tree, downloads the adapter repo, forces resolution through pnpm-workspace.yaml overrides (pnpm 11 ignores package.json pnpm.overrides), asserts the tarballs actually resolved via file+ store realpaths (a version compare cannot tell tarball from registry), builds the TS reference graph, and runs the full suite. Wired into scripts/release.mjs before publish so a candidate that breaks the flagship adapter fails on the runner, not on npm. Tracks the PR head carrying the Solid 2.0 pairing until TanStack/query#11326 merges to main. Co-authored-by: Cursor <cursoragent@cursor.com>
Solid's single-flight channel is becoming multi-source (solidjs/solid 653dd41e): mutation responses carry a keyed envelope of per-cache slices, each routed to the consumer subscribed under its source id. Today Start claims the single unnamed slot on both halves, which means any other cache wanting mutation-response data (e.g. solid-query, whose provider subscribes under "sq" in TanStack/query#11326) displaces the router's — whichever registers last wins, silently. The router's flight data now rides its own source id ("tsr"): the server collector registers additively with registerFlightDataSource and the client subscribes its consumer under the same id, so router loader/match state and other caches' slices coexist on one round trip. A user-supplied collectFlightData hook keeps the unnamed slot to itself, adding data alongside the router's instead of displacing it. Both halves feature-detect the protocol on the installed @solidjs/web (it ships in the release after 2.0.0-rc.4) and fall back to the exact previous unnamed-slot behavior on older versions; since client and server resolve the same install, the halves cannot disagree. Co-authored-by: Cursor <cursoragent@cursor.com>
Solid's single-flight channel is becoming multi-source (solidjs/solid 653dd41e): mutation responses carry a keyed envelope of per-cache slices, each routed to the consumer subscribed under its source id. Today Start claims the single unnamed slot on both halves, which means any other cache wanting mutation-response data (e.g. solid-query, whose provider subscribes under "sq" in TanStack/query#11326) displaces the router's — whichever registers last wins, silently. The router's flight data now rides its own source id ("tsr"): the server collector registers additively with registerFlightDataSource and the client subscribes its consumer under the same id, so router loader/match state and other caches' slices coexist on one round trip. A user-supplied collectFlightData hook keeps the unnamed slot to itself, adding data alongside the router's instead of displacing it. Both halves feature-detect the protocol on the installed @solidjs/web (it ships in the release after 2.0.0-rc.4) and fall back to the exact previous unnamed-slot behavior on older versions; since client and server resolve the same install, the halves cannot disagree. Co-authored-by: Cursor <cursoragent@cursor.com>
… bridge rc.6 publishes the named flight-data source API with real types, so the subscribeFlightData cast goes away; the peer floor moves to rc.6 because rc.5's settle-walk regression breaks query hydration. Also adds the missing changeset for dehydrateSettled and the SSR teardown work. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:sherif,test:knip,tes... |
❌ Failed | 6m 15s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 1m 56s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-09-02 21:33:58 UTC

Draft — depends on the unreleased
@solidjs/webmulti-source single-flight protocol (solidjs/solid@653dd41e). Ready to land once that ships and the peer range bumps.Summary
Solid's single-flight channel is becoming multi-source: a mutation response can carry a keyed envelope of per-cache slices, each routed to the consumer subscribed under its source id. Independent caches — Solid Router's route data and the TanStack Query cache — refresh from one round trip without competing for the single legacy consumer slot (which previously meant whichever library subscribed last silently displaced the other).
This PR internalizes the query cache's half:
QueryClientProvidersubscribes a consumer under the new exportedFLIGHT_DATA_SOURCEid ("sq", matching thesq:hydration-registry namespace). A mutation response carrying that slice — aDehydratedState— hydrates the provider's client before the mutation's promise resolves: mounted queries on those keys update in the same beat, with no follow-up refetches.Apps delete their hand-rolled
subscribeFlightData(...)+hydrate(...)client wiring entirely. Subscribing is inert when no server collector exists (the server folds nothing for the source), client-only (the server-side consumer registry is module state shared across requests), and torn down with the provider.New:
dehydrateSettled, SSR teardown, dehydrate filteringThree additions that complete the native SSR/flight story (covering everything
router-ssr-query-core's transport provided, on the query side):dehydrateSettled(client, options?)— the extraction half of a flight collector. Route loaders commonly fire prefetches without awaiting them; plaindehydrate()would snapshot those mid-fetch and ship nothing. Waits for every in-flight fetch, chased to quiescence (a settled batch can dispatch dependent fetches), then dehydrates.gcTimetoInfinityon the server, but any app setting a finitegcTimeindefaultOptionswould otherwise pin the per-request client (and everything its queries closed over) until the timers fire.defaultOptions.dehydrate.shouldDehydrateQuery, so apps keep sensitive or oversized queries out of the HTML payload with the same knob they'd pass any other transport. Consulted per cache event until it passes, so a filter rejecting pending queries still admits the settled value if it lands while the request's serialization context is live.Before landing (once
@solidjs/web2.0.0-rc.5 ships)subscribeFlightSourcetyped shim inQueryClientProvider.tsx— call the named-sourcesubscribeFlightData(FLIGHT_DATA_SOURCE, ...)overload directly.@solidjs/webpeer floor to rc.5.This PR deliberately waits for the release rather than landing with the shim: the whole stack (this, TanStack/router#8192, solidjs/templates#287) ships in lockstep, so landing early buys nothing and leaves cleanup to forget.
Notes
@solidjs/webdeclarations until the named-source overload ships and the peer range bumps; it should be removed at that point.@solidjs/web's suite.sq:<queryHash>), post-mutation transfer is source-addressed through the flight envelope (sq) — the same recognition model at two moments.Verification
flightData.test.tsx(consumer) anddehydrateSettled.test.tsx(settling, quiescence-chasing, failure settling + option forwarding) passing; the SSR fixture suite gained teardown (cacheEmptyAfterDisposeon both string and streaming renders) and filter assertions (the filtered query's registry entry stays off the wire while others — including a never-rendered prefetch — still ship).@solidjs/webcarrying the protocol.Made with Cursor