Skip to content

Commit c809e09

Browse files
ryansolidcursoragentautofix-ci[bot]brenelzclaude
authored
feat(solid-query): rewrite the adapter onto Solid 2.0's native async model (#11308)
* feat(solid-query): rewrite the adapter onto Solid 2's native async model Reads become a single async memo over the query cache (suspends into <Loading>, holds previous data through refetches, routes rejections to <Errored>, serializes settled values for streaming hydration); mutations ride Solid core's `action` primitive with transactional settle and optimistic overlays; cross-cache hooks share a dual-write aggregate with a hydration latch. The observer notification/store layer (createStore/reconcile, tracking proxies, notifyManager batching) is deleted — QueryObserver/MutationObserver remain as lifecycle/policy engines with noop listeners. Breaking: result `data` is non-optional (reads suspend instead of returning undefined), mutateAsync is removed (mutate returns a promise), and the reconcile/suspense/deferStream options are removed. SSR contracts pinned by the fixture suite: settled-only serialization for every exposed field, hydration id parity, no fetches inside the hydration/restore windows, and hydration-latched cross-cache aggregates. Validated against solid-js 2.0.0-rc.0 and rc.3, including an end-to-end template run with single-flight mutations. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: apply automated fixes * feat(solid-query): wire deferStream through to Solid's per-computation SSR option deferStream was declared in the option types on this line but wired to nothing. Solid 2 exposes it as a creation-time memo option, so pass it straight through to the data node: the server holds the stream flush until the query resolves instead of flushing the surrounding boundary's fallback. Server-only; ignored on the client. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(solid-query): serve data through an auto-reconciling store projection Solid 2 projections auto-reconcile their returns by key, so the data face becomes a store: deep reads are fine-grained and item identity survives across refetches (keyed by the new `reconcile` option, default 'id'). The async memo remains the SSR/hydration spine underneath; the projection derive is inert on the server — server projections run eagerly at creation, so a live derive would fetch at hook creation and serialize a duplicate payload, and the serialized entry would latch the hydrating client's projection for the whole stream. With nothing serialized the client projection computes live through the hydration window. Rewrites the structural-sharing test to the stronger store contract and adds leaf-granularity, custom-key, and primitive-data semantics tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(solid-query): green up CI for the native rewrite - align @solidjs/web, babel-preset-solid and solid-js dev ranges across the solid packages (sherif flagged the split, and devtools/persist-client were resolving a second solid-js copy) - drop the unused `export` on the internal BaseQueryLayer seam (knip) - port the PersistQueryClientProvider suite to the 2.0 read layer: a query with nothing cached now suspends for the whole restore window, so the tests assert the visible fallback -> restored -> refreshed transitions and the fetch/callback bookkeeping instead of observer result snapshots Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(solid-query): unify the data node into a single hybrid projection Collapse the three-node data face (serialized async memo + transparent hydration memo + client-only projection) into one createProjection with an identical derive on both sides — the Solid Router feed-query-into- projection shape. The engine owns SSR serialization (deferStream rides the projection now), suspense, hydration adoption, and keyed reconciliation. ssrSource 'hybrid' keeps hydrated components live: the serialized value claims the DOM and the derive re-takes over from the live cache per hydrated region. A per-hook `primed` signal gates the pull-model fetch so the hydration takeover recompute cannot race data the channel is about to deliver (SSR-errored queries fall through and fetch normally once the channel declines them). Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(solid-query): default ('server') hydration semantics for the data node Drop ssrSource 'hybrid' from the data projection: query data IS server-serialized truth, so the serialized value owns the DOM for the whole hydration window. Mid-stream cache writes commit when the stream closes through Solid's hydration-end divergence takeover (solid-js > 2.0.0-rc.3 — earlier engines lose a latched node's mid-stream divergence instead of deferring it). Network activity is unchanged: observers attach per-query as the channel primes, so mid-stream invalidations still refetch immediately; only the DOM commit defers to hydration end. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(solid-query): single-channel hydration — the node payload is the transport Delete the provider-owned dehydration channel and its coordinator. The data projection's serialized root now carries the cache facts hydration needs ({ value, t: dataUpdatedAt, raw?: pre-select data }); on a hydrated mount each hook peeks its projection's node id, loads that entry itself, primes the query cache through query-core hydrate() (staleness intact, newer-wins, silent under external pre-priming), and attaches its observer per query as chunks land. One serialization story, engine-owned; ~240 lines deleted. Requires solid-js > 2.0.0-rc.3 (peekNextChildId export). Co-authored-by: Cursor <cursoragent@cursor.com> * chore(solid-query): drop internal porting notes from the tree Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(solid-query): content-addressed hydration transfer (router query() pattern) Serialize cache entries under sq:<queryHash> at fetch-dispatch time (provider-side, promise-valued so streaming settles ride the payload) instead of tying transfer to a rendered node's positional id. Client hooks look their own hash up and prime through query-core hydrate(); the registry outlives the hydration window, so prefetched-never-rendered queries transfer and late mounts (lazy routes, post-hydration navigation) adopt the server payload instead of refetching — restoring full cache-level dehydration coverage with less machinery. Priming moved ahead of the attach render effect (its effect half runs synchronously inside an active flush, and mount policy against a cold cache would refetch). Drops the peekNextChildId dependency; the data root reverts to { value }. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(solid-query): drop internal porting notes from the tree Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(solid-query)!: remove create* aliases, completing the #8950 deprecation The create*/Create* names were kept as a migration bridge when v5 made use* canonical (#8950 deprecated them). This major already rewrites every callsite's semantics, so the bridge retires: use* is the only naming, matching the docs. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: apply automated fixes * refactor(solid-query)!: drop isInitialLoading (deprecated alias of isLoading) Core deprecated it in favor of isLoading; the adapter's getter was byte-identical. Result types now omit it (distributively over the Defined result unions so status discriminants survive), and useBaseQuery declares the adapter's own result face instead of the core union. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(solid-query): require solid-js >= 2.0.0-rc.4 rc.4 is published and carries the hydration-end divergence takeover the adapter's streaming hydration relies on. Bump the peer range and dev dep off the locally-built solid and onto the released bits; full suite (344 adapter + 7 persist-client tests, typecheck) green against published rc.4. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Brenley Dueck <brenleydueck@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 606d09d commit c809e09

41 files changed

Lines changed: 5523 additions & 6130 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
'@tanstack/solid-query': major
3+
---
4+
5+
feat: rewrite the adapter onto Solid 2.0's native async model. Query reads
6+
are now a single async computation (suspends into `<Loading>`, holds
7+
previous data through refetches, routes rejections to `<Errored>`,
8+
serializes settled values for streaming hydration) served through an
9+
auto-reconciling store projection — deep reads are fine-grained and item
10+
identity survives across fetches (keyed by the `reconcile` option, default
11+
`'id'`). Mutations ride Solid core's `action` primitive (transactional
12+
settle, optimistic overlays, callbacks inside the transaction). The
13+
observer notification/store layer is deleted —
14+
`QueryObserver`/`MutationObserver` remain as lifecycle/policy engines only.
15+
Hydration is single-channel and content-addressed (the Solid Router
16+
`query()` pattern): the provider serializes every query the request
17+
touches into Solid's hydration registry under `sq:<queryHash>` at
18+
fetch-dispatch time, each client hook primes the query cache from its own
19+
hash entry through query-core `hydrate()`, prefetched-never-rendered
20+
queries transfer, late mounts adopt past hydration end, and the
21+
provider-owned dehydration channel is deleted. Requires solid-js >=
22+
2.0.0-rc.4 (hydration-end divergence takeover).
23+
24+
Breaking: `data` is non-optional on query results (reads suspend instead of
25+
returning `undefined`) and is a readonly store view; `mutateAsync` removed
26+
(`mutate` returns a promise); `reconcile` is now the reconciliation key for
27+
the data store (`string | (item => any) | null`, default `'id'` — the v5
28+
function form is gone, core auto-reconciles); `suspense` option removed
29+
(suspension is the model, boundaries are the control point); the `create*`
30+
runtime and `Create*` type aliases are removed, completing the deprecation
31+
from #8950`use*` is the only naming; `isInitialLoading` (deprecated
32+
alias of `isLoading`) is removed from query results. `deferStream` is now implemented
33+
(it was declared but unwired on the v6 line): it passes through to Solid's
34+
per-computation `deferStream` memo option.

packages/solid-query-devtools/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@
7070
"@solidjs/signals": "^2.0.0-rc.0",
7171
"@solidjs/testing-library": "^0.8.10",
7272
"@solidjs/vite-plugin": "^3.0.0-next.27",
73-
"@solidjs/web": "^2.0.0-rc.0",
73+
"@solidjs/web": "^2.0.0-rc.3",
7474
"@tanstack/solid-query": "workspace:*",
75-
"babel-preset-solid": "^2.0.0-rc.0",
75+
"babel-preset-solid": "^2.0.0-rc.2",
7676
"npm-run-all2": "^5.0.0",
77-
"solid-js": "^2.0.0-rc.0",
77+
"solid-js": "^2.0.0-rc.3",
7878
"tsup-preset-solid": "^2.2.0"
7979
},
8080
"peerDependencies": {

packages/solid-query-persist-client/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@
7070
"@babel/preset-typescript": "^7.18.6",
7171
"@solidjs/testing-library": "^0.8.10",
7272
"@solidjs/vite-plugin": "^3.0.0-next.27",
73-
"@solidjs/web": "^2.0.0-rc.0",
73+
"@solidjs/web": "^2.0.0-rc.3",
7474
"@tanstack/query-test-utils": "workspace:*",
7575
"@tanstack/solid-query": "workspace:*",
76-
"babel-preset-solid": "^2.0.0-rc.0",
76+
"babel-preset-solid": "^2.0.0-rc.2",
7777
"npm-run-all2": "^5.0.0",
78-
"solid-js": "^2.0.0-rc.0",
78+
"solid-js": "^2.0.0-rc.4",
7979
"tsup-preset-solid": "^2.2.0"
8080
},
8181
"peerDependencies": {

0 commit comments

Comments
 (0)