Skip to content

fix(solid-query): consume hydration data client-side via a provider-owned streaming channel - #11168

Merged
birkskyum merged 8 commits into
TanStack:solid-query-v6-prefrom
ryansolid:fix/solid-query-hydration-data-consumption
Aug 11, 2026
Merged

fix(solid-query): consume hydration data client-side via a provider-owned streaming channel#11168
birkskyum merged 8 commits into
TanStack:solid-query-v6-prefrom
ryansolid:fix/solid-query-hydration-data-consumption

Conversation

@ryansolid

Copy link
Copy Markdown

Target branch: solid-query-v6-pre (@tanstack/solid-query@6.0.0-beta.x, Solid 2)

Problem

During SSR the adapter fetches queries and serializes each observer result
through Solid's per-computation serialization, but on the 6.x line nothing on
the client ever primes the QueryClient cache from it. The consumption used
to live in createResource's onHydrated callback (it called query-core's
hydrate() and attached the client subscriber), and it was lost when the
Solid 2 rewrite (#10272 / #10316) replaced createResource with a
promise-returning derived memo, which has no onHydrated hook.
hydratableObserverResult still attached a per-result hydrationData copy —
"this will be removed on client after hydration" — but no client code read it.

The result after SSR:

  • the QueryClient cache comes up cold (getQueryState() returns pending /
    data: undefined) even though the data was fetched on the server and
    serialized to the client, and
  • every observer refetches on mount, including queries whose data is still
    well within staleTime.

The Solid team hit this while building a full external-SSR integration for
solid-query on Solid 2; the integration currently works around it with a
manual dehydrate()/hydrate() pass in the template, which confirmed the
serialized payload arrives intact — it just was never consumed.

Fix design

The dehydrated cache state now travels through a library-owned
serialization channel
on QueryClientProvider, the way @solidjs/router's
query() owns its serialization, instead of piggybacking a copy on every
observer result and reading it back through per-owner registry lookups. The
adapter ends with zero sharedConfig / internal-API usage.

Server: a signal-shaped async channel (hydrationChannel.ts)

QueryClientProvider holds the channel as a plain async-iterable-valued
computation — the computation's value IS the channel:

const [channelValue] = createSignal<DehydrationChannelYield | undefined>(
  () => (isServer ? createServerDehydrationChannel(props.client) : undefined),
)

createServerDehydrationChannel subscribes to the QueryCache and, on every
updated/success event, emits a cumulative snapshot of the dehydrated
cache — success entries in query-core dehydrate() shape (dehydratedAt,
state, queryKey, queryHash, meta?, queryType?), with entry objects
reused across snapshots while the query state is unchanged. Entries already
settled when the channel is created ride the first emission.

Because the channel is just a component's async signal value, Solid
serializes it through the normal path: the server runtime tees the iterator
into the hydration serializer (processResultctx.serialize(id, tapped) in solid-js' server runtime), and seroval streams each cumulative
yield as a script chunk riding the SSR stream. Nothing reads the signal
during SSR, so it never suspends anything.

Termination. The SSR stream can only finish once every serialized stream
closes (Serializer.flush() in seroval only fires onDone when its pending
count drains, and the render root is disposed after that), so the channel
must close itself. It closes on cache quiescence: after every cache event
(and once at creation) it schedules a timer-task check; if no query is
fetching by then, no further settle can occur — suspense retry passes that
start waterfall fetches are scheduled on microtasks, so they have begun
before the check runs — and the channel emits a final cumulative snapshot
with done: true and completes. Measured on the streaming fixture the
terminal yield and stream close ride the same flush cycle as the last
query's boundary.

Client: provider consumes, per-query attach coordination

During hydration the provider's signal replays from the serialized value
through Solid's ordinary public path (hydrateSignalFromAsyncIterable /
normalizeIterator internally — the adapter just owns a signal): yields
that were still buffered when hydration began are conflated to the latest
one, and live yields after that apply one at a time. A render effect reads
channelValue() and hands each yield to a hydration coordinator, which:

  • applies new entries via query-core hydrate() — inheriting its exact
    semantics: newer-wins on dataUpdatedAt, fetchStatus preserved for
    in-flight fetches, defaultOptions.hydrate (e.g. deserializeData)
    respected;
  • resolves per-query waiters registered by useBaseQuery (below), and
    releases all remaining waiters when the channel reports done (covers
    queries that errored during SSR and were never dehydrated).

On a fresh client mount the compute returns undefined and none of this
runs.

useBaseQuery: replay detection + coordinated attach

The hydration replay runs the queryResource compute with Promise mocked,
so the promise executor that normally creates the client subscriber never
runs for hydrated queries (nor could it: a mount refetch started under the
mocked Promise would never settle). The replay is now detected with plain
JavaScript instead of sharedConfig: a real Promise runs its executor
synchronously, the hydration mock does not, so an executorRan flag is
false exactly when the compute was replayed.

When that happens, the component asks the coordinator to attach its observer
once its query's entry has been primed (whenQueryPrimed(queryHash, attach))
— or on a plain microtask if there is no provider (manual queryClient
option). Attaching applies query-core's normal mount semantics
(shouldFetchOnMount / updateResult):

  • data still within staleTime → no mount refetch,
  • stale data → refetch fires as usual, and the observer keeps the UI
    reactive,
  • any cache write that landed before the attach is reconciled at attach time.

The wait is per-query, not global-hydration-end: a component hydrated from an
early flush goes live while later boundaries are still streaming, so it is
not deaf to cache writes, is seen by invalidateQueries' active-query
refetch, and cannot be gc'ed while visible — a global "wait for hydration
end" design would reintroduce all three problems for early-flush queries.

hydratableObserverResult no longer attaches hydrationData — nothing else
consumed it (TanStack Start's ssr-query bypasses it) — it now only strips
non-serializable functions from the resolved resource value.

Why the channel is signal-shaped and cumulative

The signal shape is the natural container — the channel is one value that
advances over time, not an object whose properties change independently —
and the cumulative yields are what make Solid's replay of it lossless:

  • Buffered replay conflates to the latest yield. When hydration
    begins after chunks have already arrived (the normal case for anything
    but the shell, and the whole story for a slow client hydrating a
    completed stream), hydrateSignalFromAsyncIterable wraps the
    deserialized iterator in normalizeIterator, which drains the
    synchronously-available backlog and delivers only its latest data
    yield (the stream's done result follows on the next pull). Because
    every yield is a cumulative snapshot, the latest one alone carries
    everything the collapsed intermediates did: one applyYield call
    primes every entry and observes the done marker. Live (post-
    hydration) yields apply one at a time as their chunks execute.
  • Entry identity keeps the cumulative shape cheap. Entry objects are
    reused across yields while the query state is unchanged, so seroval's
    reference deduplication emits each entry once; later yields are arrays
    of $R[n] references (verified in the fixture payload: the terminal
    yield serializes as {entries:[$R[14],$R[19]],done:!0}). Bytes scale
    with the number of entries, not its square. Unlike the store path —
    whose serialized first snapshot is JSON-cloned by the server runtime to
    lock SSR-visible state, breaking identity for entries in it — the
    signal path serializes the first yield object directly, so no
    first-yield dodge is needed.

Minimum solid-js version. The conflate-to-latest replay ships in
solid-js 2.0.0-beta.33 (the normalizeIterator buffered async-iterable
replay fix). On 2.0.0-beta.32 and earlier, the signal
replay pins at the first buffered yield: the batching loop lets the
stream's done result clobber the later data yields, so every entry after
the first yield — and the terminal done marker — is dropped whenever
hydration starts after more than one chunk has arrived. Verified
empirically on published beta.32 (2-query fixture, full payload delivered
before hydrate()): the DOM keeps the server-rendered text for both
queries and the first yield's query hydrates and attaches normally, but
the second query's cache entry is never primed and its whenQueryPrimed
waiter never resolves — its observer never attaches, so the component
never refetches and is deaf to cache writes and invalidateQueries. That
is a silently-frozen component, not graceful degradation, which is why
this PR bumps the repo's solid pins and raises the package's solid-js
peer-range floor to 2.0.0-beta.33 (hydration that begins before the
stream's chunks arrive — the fast-client path — is unaffected even on
beta.32).

Design verification

Does serialization deliver progressively during streaming SSR? Yes.
Runtime path, server: solid-js's server processResult (shared by
createMemo / createSignal(fn)) detects the async-iterable computation
value and serializes a tapped iterator via ctx.serialize(owner.id, tapped); @solidjs/web renderToStream routes that into seroval's
Serializer, which parses each yield as it arrives and pushes it through
sink.data() → script chunk on the stream. Client: the deserialized
seroval stream buffers pushed values and serves them to
hydrateSignalFromAsyncIterable, which conflates the buffered backlog to
its latest yield and applies live yields as their chunks execute.

Ordering: entry vs. boundary. Measured on the streaming fixture
(chunk-level, timestamps in ms, signal shape):

t chunk
21 shell (fallbacks + channel scaffolding)
27 channel yield: header entry
28 header boundary template + $df reveal
273 channel yield: feed entry
274 terminal cumulative yield (done: true) + return
274 feed boundary template + $df reveal

The entry patch flushes before its boundary's HTML in the same flush
cycle, because the cache settle event fires synchronously when the query
resolves, while the boundary's fragment flush waits for the boundary to
re-render and resolve. So an entry is always in the seroval buffer by the
time its boundary hydrates; the coordinator makes attach ordering
deterministic regardless.

Surface tally. rg "sharedConfig|_\$HY|peekNextChildId|hydrationData"
over packages/solid-query/src (tests excluded): zero matches. Public APIs
used for hydration: solid-js createSignal (function form — the documented
async computation protocol; async-iterable values are part of its public
compute contract), createRenderEffect, createContext / useContext,
runWithOwner; query-core hydrate, QueryCache.subscribe,
QueryClient.isFetching, and Query's public state / queryKey /
queryHash / meta / queryType. Replay detection uses no API at all
(observable Promise executor semantics).

Byte cost (dev-mode fixture, two queries, string data, solid-js
2.0.0-beta.33 replay behavior): with the channel disabled →
signal-shaped channel, the full-settled string payload goes 3,085 → 5,780
bytes (1,151 → 1,972 gzipped) and the streaming payload 3,373 → 6,068
(1,205 → 2,035 gzipped). Roughly 1.7KB of the delta is seroval's one-time
stream/iterator replay helper code (fixed cost, amortized over any number
of queries, highly compressible boilerplate); the marginal per-query cost —
one dehydrated entry riding a cumulative yield of $R[n] references — is
on par with the removed per-query hydrationData copy, and data payloads
of object shape deduplicate by reference against the serialized observer
results. For comparison, an earlier store-shaped prototype of the channel
measured 167 B (≈60 B gzipped) more than the signal shape on the same
fixture (patch wrappers and store scaffolding outweigh the
repeated-snapshot cost that reference dedup already eliminates).

Test coverage

packages/solid-query/src/__tests__/hydration.test.tsx adds real
SSR → hydration integration tests (there was previously no SSR coverage in
the package). A small fixture app is built with vite (fixtures/hydration/):
string and streaming server bundles rendered in a node subprocess (so
solid-js resolves to its server build) and a hydratable client bundle that
is hydrated in jsdom with the real hydrate() from @solidjs/web against
the server HTML and its serialized payload. (The fully-settled string render
collects renderToStream through pipe()renderToStringAsync no longer
exists on current solid-js 2 betas.) The streaming fixture captures
renderToStream's chunks with timestamps and replays them in phases, so a
slow boundary can hold the stream open while tests probe an already-hydrated
section. Assertions:

  • the SSR HTML contains the rendered data and the channel payload
    (dehydrated entries), and no hydrationData field anywhere,
  • within microtasks of hydration (before a browser would paint) the cache is
    warm: server data and the server's dataUpdatedAt (newer-wins semantics
    observable),
  • a query with staleTime: 60_000 never refetches on mount and the DOM
    keeps the server-rendered value (client queryFn call count stays 0),
  • a query with staleTime: 0 refetches on mount per normal staleness rules
    and the DOM updates to the client-fetched value (proving the
    post-hydration subscriber works),
  • a cache write landing between hydration and the subscriber attach is
    applied at attach time without a refetch,
  • ordering: with only the first flush applied and the stream held open,
    the shell-flush query is primed with the server's dataUpdatedAt and its
    observer attached (active query) within microtasks of shell hydration —
    not at stream end — while the late query's entry is verifiably absent
    until its boundary's flush arrives with it; this also proves the
    mocked-Promise hydration replay does not wedge the provider's channel
    consumption,
  • with the stream still open, an already-hydrated query stays live: newer
    data written to the cache reaches its DOM immediately and
    invalidateQueries refetches it immediately, while the late-arriving
    boundary still hydrates correctly afterwards,
  • buffered-replay conflation: with the entire stream delivered before
    hydrate() runs (slow-client case — every channel yield buffered), the
    replay's conflate-to-latest still primes every entry with the server's
    exact state, attaches every observer (the done snapshot is not
    dropped), refetches nothing, and leaves the components live. This is the
    test that fails on solid-js ≤ 2.0.0-beta.32 (see the version note above).

Without the fix, the warm-cache and no-refetch assertions fail exactly as
described above (cold cache, mount refetch).

  • coexistence: a host that primes the cache through its own external
    hydrate() call before DOM hydration (the TanStack Start pattern, see
    below) does not break the channel: double-priming produces zero cache
    updated events for the pre-primed query, no refetch, and the attach
    coordination still resolves.

Full package suite is green: @tanstack/solid-query 22 test files / 329
tests passing, vitest typecheck clean, eslint clean — run against published
solid-js 2.0.0-beta.33.

Coexistence with TanStack Start

TanStack Start does not use this channel — its router integration
(@tanstack/solid-router-ssr-queryrouter-ssr-query-core) primes the
QueryClient itself via query-core hydrate(): an initial dehydrated blob
(including pending queries as seroval-streamed live promises) plus a
queryStream for queries that start during rendering, all applied in
router.options.hydrate before Solid's DOM hydrate() runs. Since the
integration also wraps the app in QueryClientProvider
(router.options.Wrap), under Start both channels are live. Measured on
an instrumented Start app (@tanstack/solid-start 2.0.0-beta.30, solid-js
2.0.0-beta.32, three queries exercising all three of Start's sub-channels):

  • The provider channel activates under Start. Start's SSR renders the
    provider inside Solid's normal hydration serialization context, so the
    channel serializes and streams exactly as in standalone SSR; its entry
    patches flush at each query's settle time, before that query's boundary
    HTML, same as standalone.
  • Double-priming is behaviorally silent. Start's channel wins the race
    (its hydrate hook runs pre-DOM-hydration); the provider channel then
    re-primes the same entries with equal dataUpdatedAt, which query-core
    hydrate() skips (it only writes strictly-newer data). The client cache
    timeline shows exactly one added event per query, zero extra updated
    events, zero observer churn, zero spurious refetches (with staleTime
    set, client queryFn executions stay 0 and all DOM tokens remain the
    server values). The provider's attach coordination resolves normally —
    each query's observer attaches at its boundary's hydration, and the page
    is interactive mid-stream.
  • Byte cost of the redundancy: +2,361 B uncompressed / +477 B
    gzipped
    (+13.5%) on the three-query page, measured on the earlier
    store-shaped channel; the final signal shape measured ~60 B gzipped
    smaller than the store shape on the two-query fixture, so treat the
    Start figure as a slight over-estimate. Roughly 650 B of it is fixed
    seroval scaffolding; and because the channel's entries flush before the
    per-owner observer results, the observer results reference the entry
    objects instead of re-serializing their data (the observer-result scripts
    shrank by 1,169 B in the same measurement), so the true duplicate cost is
    under ~160 B gzipped per query.
  • A note on defaults: the fix restores correct staleness semantics
    under Start too. With default staleTime: 0, hydrated queries now
    refetch on mount (as they always should have, and as React does after
    HydrationBoundary); previously they silently never did because no
    observer was ever attached after hydration. Apps that want no mount
    refetch after SSR should set staleTime, per standard TanStack SSR
    guidance.

Given benign-and-small, no stand-down mechanism is included; the redundancy
is one dehydrated copy per query. Start's integration could later delegate
to the provider channel (e.g. stop double-carrying settled entries) as a
follow-up on their side. The coexistence contract is pinned by the
external-hydrate() test described above.

Notes

  • Version requirement: the signal-shaped channel needs solid-js
    2.0.0-beta.33 (the normalizeIterator buffered async-iterable replay
    conflation fix, solid commit 23657d29). This PR includes a commit
    bumping the repo's solid pins from 2.0.0-beta.29 to 2.0.0-beta.33 and
    raising @tanstack/solid-query's solid-js peer-range floor to
    >=2.0.0-beta.33, because on ≤ beta.32 the failure mode under buffered
    replay is a never-primed, never-attached query (a silently frozen
    component), documented with an empirical probe in the design section.
  • Release sequencing (does not block this PR): for TanStack Start
    apps, shipping a solid-query release with this fix is gated on
    TanStack/router#8026
    (router-core currently quarantines render output flushed after
    </body> instead of streaming it) landing and releasing first; that
    gates the release coordination, not the review or merge of this
    change.
  • A changeset (patch for @tanstack/solid-query) is included.
  • This makes the adapter streaming-correct in the way
    ReactQueryStreamedHydration aspires to be for React: per-flush cache
    priming riding the framework's own serialization, with observers live
    before the stream ends.
  • useQueries does not go through this path (it did not serialize hydration
    data before either); with the channel in place its queries would be primed
    too as soon as they settle on the server — client attach for it could be
    covered in a follow-up.

ryansolid and others added 5 commits August 10, 2026 00:51
…hydration channel

During SSR the adapter fetches queries and serializes each observer
result, but since the Solid 2 rewrite dropped createResource's
onHydrated hook nothing on the client ever primed the QueryClient
cache: it came up cold after hydration and every observer refetched on
mount, even for data well within staleTime.

QueryClientProvider now owns a serialization channel: an async store
whose generator emits cumulative snapshots of the dehydrated cache
(query-core dehydrate() shapes) as queries settle during SSR. Solid
serializes it through the normal per-computation path, so entries
stream progressively and flush before the boundary content that awaited
them. The channel closes itself on cache quiescence so the SSR stream
can complete. On the client the provider applies each yield via
query-core hydrate() (newer-wins) and useBaseQuery attaches each
hydrated component's observer as soon as its query's entry is primed
(or the channel completes), restoring normal mount semantics: fresh
data does not refetch, stale data does, and earlier cache writes are
reconciled at attach.

The channel is store-shaped because Solid's hydration replay of
signal-shaped async iterables collapses buffered yields into the
latest result (dropping entries whenever hydration starts after their
chunks arrived), while the store replay applies every yield in order.
Yields are cumulative so collapsing intermediate states is lossless;
entry objects keep their identity so seroval emits each entry once.

The replay itself is detected without internals — a real Promise runs
its executor synchronously, the hydration mock does not — leaving the
adapter with zero sharedConfig or hydration-registry usage. The
vestigial per-observer-result hydrationData copy is no longer
serialized; nothing consumed it.

Co-authored-by: Cursor <cursoragent@cursor.com>
Build a small fixture app with vite (string + streaming server bundles
rendered in a node subprocess, hydratable client bundle) and hydrate it
in jsdom with the real @solidjs/web hydrate() against the server HTML
and serialized payload. The streaming fixture captures renderToStream
chunks with timestamps and replays them in phases so a slow boundary
holds the stream open while tests probe an already-hydrated section.

Covers: channel payload in the SSR output (and no hydrationData field),
warm cache within microtasks of hydration with the server's
dataUpdatedAt (newer-wins), no mount refetch at staleTime 60s, mount
refetch at staleTime 0, cache writes landing before the subscriber
attach reconciled without a refetch, shell-flush entries primed at
shell hydration rather than stream end (with the late entry verifiably
absent until its boundary's flush), and hydrated components staying
live — setQueryData and invalidateQueries both effective — while the
stream is still open, with the late boundary hydrating correctly after.

Co-authored-by: Cursor <cursoragent@cursor.com>
Hosts like TanStack Start prime the QueryClient through their own
query-core hydrate() call before DOM hydration. Pin that the provider
channel's re-priming of the same entries is silent (no cache updates,
no observer churn, no refetch) and that its per-query attach
coordination still resolves.

Co-authored-by: Cursor <cursoragent@cursor.com>
…d replay conflation makes it the right container

The store shape was a workaround for solid-js' signal-path hydration
replay dropping buffered async-iterable yields (normalizeIterator let the
stream's done result clobber the backlog, pinning the value at the first
yield). With the conflate-to-latest replay fix (solid 23657d29, shipping
in the beta after 2.0.0-beta.32), the natural shape works: the provider
holds the channel as a plain async-iterable-valued createSignal(fn)
computation, the server serializes the tapped iterator through the normal
per-computation path, and the client replay conflates any buffered
backlog to the latest yield — lossless exactly because yields are
cumulative snapshots. Requires that beta: on stock beta.32, buffered
replay leaves post-first-yield entries unprimed and their waiters
unresolved (frozen components, verified empirically), so the solid pin
must be bumped when the beta publishes.

Wins over the store shape, verified on the fixture: no draft mutation or
yield-undefined first-snapshot dodge (the JSON-cloned-first-snapshot
quirk is store-path-only; the signal path serializes the first yield
object directly, so entry identity and seroval reference dedup hold from
the first yield — the terminal yield serializes as pure $R references),
and 167 B / ~60 B gz smaller on the two-query fixture. Coordinator,
whenQueryPrimed, and useBaseQuery semantics unchanged.

Tests: new buffered-replay conflation integration test (entire stream
delivered before hydrate() — all entries primed from the conflated
snapshot, all observers attach, nothing refetches); string fixture now
collects renderToStream via pipe() (renderToStringAsync is gone from
current solid betas). 22 files / 329 tests green against a local solid
build at the fix commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Bump solid-js 2.0.0-beta.29 -> 2.0.0-beta.33 plus matching
  @solidjs/web, @solidjs/signals, and babel-preset-solid bumps across
  the solid packages, solid-vite integration, and solid examples
- Raise @tanstack/solid-query's solid-js peer range floor to
  2.0.0-beta.33: the hydration channel added in this PR requires
  beta.33's normalizeIterator buffered-replay conflation fix
  (solid 23657d29). On <= beta.32, hydration that starts after more
  than one stream chunk has arrived pins the channel replay at its
  first buffered yield, so later queries are never primed and their
  observers never attach - silently frozen components, not graceful
  degradation.
- Suite green against published beta.33: 22 files / 329 tests,
  vitest typecheck and eslint clean

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: db42c3fb-a4b5-46d8-8868-62c091663473

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​solidjs/​web@​2.0.0-beta.29 ⏵ 2.0.0-beta.3396 +181008397 +1100
Addedbabel-preset-solid@​2.0.0-beta.331001009096100
Updatedsolid-js@​2.0.0-beta.29 ⏵ 2.0.0-beta.33100 +210095 +196 +1100
Updated@​solidjs/​signals@​2.0.0-beta.29 ⏵ 2.0.0-beta.3395 +15100100 +198 +1100

View full report

@nx-cloud

nx-cloud Bot commented Aug 11, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 70b2e10

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 3m 38s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-11 21:21:52 UTC

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@11168

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@11168

@tanstack/lit-query

npm i https://pkg.pr.new/@tanstack/lit-query@11168

@tanstack/preact-query

npm i https://pkg.pr.new/@tanstack/preact-query@11168

@tanstack/preact-query-devtools

npm i https://pkg.pr.new/@tanstack/preact-query-devtools@11168

@tanstack/preact-query-persist-client

npm i https://pkg.pr.new/@tanstack/preact-query-persist-client@11168

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@11168

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@11168

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@11168

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@11168

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@11168

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@11168

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@11168

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@11168

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@11168

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@11168

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@11168

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@11168

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@11168

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@11168

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@11168

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@11168

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@11168

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@11168

commit: 70b2e10

…t headroom

The first test executed by each type-checked RuleTester
(parserOptions.project: true) pays the one-time cost of building the TS
program for the ts-fixture. On a shared CI runner - this PR adds a
solid-query test task that builds vite fixture bundles in parallel under
Nx - that cold build pushed the first type-aware test in
no-rest-destructuring.test.ts and no-void-query-fn.test.ts just past
vitest's 5s default (5.9s measured), failing the run on timeouts with
zero assertion failures. Raise the package's testTimeout to 15s; no test
logic changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ryansolid

Copy link
Copy Markdown
Author

The two CI failures were collateral timeouts in @tanstack/eslint-plugin-query, not solid-query failures: the first test executed by each type-checked RuleTester (parserOptions.project: true) pays the one-time TS program build for the ts-fixture, and with this PR's solid-query test task (vite fixture builds) running in parallel under Nx on the same runner, that cold build landed at ~5.9s — just past vitest's 5s default. Both were pure Test timed out in 5000ms with zero assertion failures, and both files pass locally (1702/1702).

Pushed a commit raising that package's testTimeout to 15s (config-level only — the RuleTester generates the it() calls, so per-test timeouts aren't reachable; no test logic changed). Happy to drop it or split it out if you'd rather handle the flake differently.

…, unexport internal coordinator type

The hydration fixture app is only reachable dynamically (built and
rendered via a spawned build-and-render.mjs), so knip can't trace it;
ignore the fixture dir in the solid-query workspace like the existing
query-codemods/lit-query fixture ignores. HydrationCoordinator is only
used within hydrationChannel.ts, so it doesn't need to be exported.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ryansolid

Copy link
Copy Markdown
Author

The remaining Test failure was root:test:knip: it flagged the hydration fixture app as unused files (it's only reachable dynamically — built and rendered through a spawned build-and-render.mjs, which knip can't trace) plus the HydrationCoordinator interface, which is only used within hydrationChannel.ts. Pushed 70b2e10 ignoring the fixture dir in the solid-query workspace (same treatment as the existing query-codemods/lit-query fixture ignores) and dropping the unneeded export; knip --treat-config-hints-as-errors is clean locally.

@birkskyum
birkskyum merged commit 67c8179 into TanStack:solid-query-v6-pre Aug 11, 2026
9 checks passed
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.

3 participants