Patch Changes
-
4b4c2cb: A server
<Loading>whose creating pass is disposed (an<Errored>retry re-rendering its children) now stops retrying instead of looping until "did not converge". A boundary re-created under the same fragment id owns the fragment, so the abandoned instance never settles it with blank markup; with no successor, the abandoned instance releases its fragment so the response still ends. -
21ff8f8: Infer
createEffectandcreateRenderEffectcompute results as const so inline tuple results retain their element types without a type argument oras const. -
3c1d512: A fill that claims adopted markup reads the state the server rendered it from (frames-rulings 3.6 (iii), "the consumer parks" — S1's third commit ported onto
nextwithout itsclaiminghint).materializeContainerTraceparks a replayed backlog beyond the snapshot until hydration ends (onHydrationEnd; the next microtask when no hydration is in progress) and then applies it as one ordinary update. A container trace is materialized at a fill's arg-read; when that fill claims server markup — the document's pass, a frame's deferred claim under its hold, a claim at a fragment's reveal after hydration-done — the snapshot is what the markup was rendered from, the claim trusts the markup (a text hole is never rewritten during a claim), and a store already past the markup left the DOM diverged for good. Parked, the claim reads the snapshot and the backlog lands after it, so the DOM catches up outside hydration. The release order is the one 3.2 pins: the claim, the frame's hold release, done, then the backlog. A fresh mount pays one beat for not being told apart: its backlog lands a microtask after its snapshot, before any paint. A failure in the backlog applies in order, after the parked patches.- The materializer creates its projection under a DETACHED root. Rooted under the reading owner — during hydration an id-carrying one — a trace revived at t=0 consumed one child id per trace while one revived by a late claim consumed none, and a keyed sibling after the frame hydrated under different keys in the two runs.
-
9a213bb: Derived writes apply first, then derivations re-run. A manual write to a writable derived value (
createSignal(fn),createStore(fn)) lands at once; when one of its sources changes — in the same update or later, inside or outside an action, across async holds — the function re-runs and receives the write asprev(or as the draft forcreateStore(fn)), and decides what to keep. A write on its own never re-runs the function.This reverses beta.11's same-tick precedence (#2692), where a write beat a source change in the same flush: a function that ignores
prevnow discards a write made in the same update as a source change, and a same-value write no longer holds against it. To keep a local value across source changes, carry it in the data as a flag the function honors. It also fixes #3733 (a write inside an action blocked later source changes for the whole hold, a regression since #2692) and supersedes the frame-scoped mask from #3740, whose changeset this replaces. -
b0c6e6a: Add the missing diagnostic guide sections so every diagnostic code links to its repair guidance.
-
da84bd9: Fix hydration adoption throwing when a traced compute calls
Promise.withResolvers()orPromise.try(). The trace run swaps the globalPromisefor a never-settling mock that lacked these two statics, so the call threw out of hydration instead of adopting the server value. The mock now provides both:withResolversreturns a never-settling mock with no-opresolve/reject, andtryreturns a never-settling mock without invoking its callback (matching how the mock's constructor ignores its executor). -
3c1d512: Frames A5′ — a deferred fragment's placeholder inside a server component's element is the frame's content by rendering, not by adoption (frames-rulings 3.3, ruled 2026-10-06).
solid-js: the document fragment ledger'sfragmentPolicylets a post-done swap proceed when the fragment is owned — itspl-*template is in the document and the integration's ownership predicate_$HY.fa(placeholder)says so — beside the existing claimant case; no hold, no replay for owned fragments._$HY.fr.claim/_$HY.fr.releaseare removed from the published ledger (_$HY.fris{ pending, subscribe });_$HY.fa(placeholder): booleanis the new integration hook.fragmentPendingnow reads a revealed fragment from its swap record (_$HY.v) before its_frstamp: the producer emits the swap script and then the_frsettle in the same batch, so a_$HY.fr.pending()read inside the reveal notification saw the revealing fragment as still pending — a page's last reveal never read as exhaustion and a waiter released on exhaustion waited forever.@solidjs/web(frames client): installs_$HY.faonce (pl.closest("[data-fid]"), minus elements of a boundary disposed in place — C14); deletesclaimRegionFragments, the per-adoption claim set, the cascade's claim half and the release loop (the dev-only rejection report over the region'spl-*templates stays, 0 prod bytes);documentBoundarypends on the intercept's one arrival answer (awaitBoundary) andboundaryWaitersis deleted (G9). A post-done swap into server-component markup no client has adopted yet now lands at once; the adoption that follows finds it in place and reads its declared records synchronously. -
3c1d512: A server
<Loading>inside a server component that fails after the first flush renders the server's outcome into its fragment instead of a blank (C12 (c), frames-rulings 3.3): the nearest server<Errored>'s fallback for the error, at the<Loading>'s position (asked through the boundary error handler's newoutcomemode; a<Loading>between passes the question up); with no server<Errored>the error escapes the component — the frame's own:erroron the stream face (an unkeyederrorchunk), a frame-addressed{ type: "error", fid, error }op on the document face'ssc:livechannel (only the owning adopted boundary applies it) — and the position keeps the boundary's own fallback._frstill rejects and the keyed error chunk still rides (the diagnostics). Outside a server component nothing changes (the blank the client twin renders fresh over).HydrationContext.registerFragment's resolver gains a third argument (escaped?: { frame?: string }) and the context an internalframeId. -
3c1d512: An adopted frame occurrence claims its server markup by re-entering hydration the way a streamed
<Loading>resume does (frames-rulings 3.1 / 3.2, the savings plan's A2 — S-hold's window form).solid-js:hydrateWindow(id, fn, scope?)is factored out of a streamed boundary's resume and reached assharedConfig.hydrateWindow(@internal): the keys underidgathered into the registry (the capturedscopepair when anotherhydrate()root replaced the live one, #2917), hydrating on for the synchronous window, the current owner the claim owner (a render the window forces elsewhere is a client render, #3504), the owner the window's snapshot and live scope when none is open — so a write during a late claim is held and replays once the claim is over, and a late claim no longer re-marks the root's scope.sharedConfig.claimRoots(@internal) is typed: the claimant declares a range that may be detached around its window.holdBoundarystays the registration; the resume path is unchanged in behaviour.@solidjs/web(frames):claimRenderis the window — onecreateOwner({ id: prefix })and the call — instead of a registry of its own gathered by walking the range, a hydrating flag flipped throughsharedConfig's setter (which reset hydration-done and re-ran its completion from outside the runtime), and a hand-over of keys from the root registry:gatherClaimsandhasPendingFragmentare deleted (the window gathers by the producer prefix and always engages).adoptBoundarycaptures the registry/gather pair it adopts under so a claim made long after — under the frame's hold, at a fragment's reveal — gathers against the root that holds the frame.@solidjs/web:gatherHydratable's prefix-scoped gather selects its keys natively ([_hk^="…"]) instead of sweeping every_hkand filtering in JS — it now runs once per adopted occurrence, not only per late resume.
-
9d89df7: Hydration-done counts the frames client's holds (frames-rulings 3.1, ruled): an adopted occurrence the frame has not claimed yet — waiting for its args record or a
{$ref}'s data — registers as a pending boundary through the same registration a streamed<Loading>resume takes (sharedConfig.holdBoundary, internal), soonHydrationEndandisHydrationInProgress()mean the same thing with or without server components (contract C3 a). -
7233451: frames: the container-trace materializer is the frames client's traces tier —
@solidjs/web/frames/trace, loaded through the server-announced tier mechanism (prepareTier("trace")), so the store engine leaves every server-component page that never meets a trace (page base −6.6 KB brotli, page live −6.7 KB; frames eager −83 B).solid-js/internal/container-traceis a newsolid-jsentry carryingmaterializeContainerTrace(marker, claiming?)(the store engine reached through@solidjs/signals, the hydration dispatchwithStoreHydrationand the patch protocol read back fromsolid-js); the materializer leavessolid-js's main andsolid-js/internalentries. The eager frames client keeps the trigger: the loader entry, the held-set predicate (an adopt-time record whose args carry a{ $tr }marker while the tier is absent is held under frames-rulings 3.1 — its server interior on screen, hydration-done waits — and mounts with the record it was held on, a replacement applying as an args change), and theclaiminghint (FrameHostOptions.revive(value, claiming?)), which keys the materializer's parked backlog on the claim again: a fresh mount reads the fold of its whole backlog at once. -
759a9b6: A server memo re-created at a still-pending slot now takes the slot's answer when the earlier flight settles it (#3815). A retry pass that re-creates a component while its async memo is in flight hands the new memo the slot's shared deferred, but only the earlier memo's promise settles it, so the new memo kept its
NotReadyErroron that already-resolved promise. A memo reading it then retried every microtask, so no timer fired again, the stream never ended and the process sat at 100% CPU. On rc.13 the trigger was a function hole that created a component returning a pendinglazy()view. Onnextit is an<Errored>retry that re-creates a pending<Loading>, with a fresh promise per setup and a memo reading it. The joined memo now adopts the slot's value or error when the shared deferred settles, the same way a re-created async-iterable node already did. -
e345dc7: A live source whose server value is still streaming when the root hydration pass ends now takes over when hydration ends, not when the root pass ends. A source with a
loadingValuekeeps taking over when the root pass ends: its markup is commit #0, which needs no claim against the streamed value. Before, a source created in the shell and read by a streamed<Loading>boundary connected before that boundary's fragment arrived, so the boundary could not claim its server-rendered DOM and rendered a second copy beside it. -
41fdf96:
reportRequestFailure(error, event)(@solidjs/web, server) reports a failure that fails a request outside any render or server function the runtime reports, such as a middleware throw that a framework's request handler catches. The ambient server error hook hears it askind: "request",handling: "failed", with the request event, once per error object, and its return is ignored. With no hook registered the failure goes toconsole.error, as a render that fails before its shell does.ServerErrorSite.kind(solid-js) gains"request". On the client the function is a no-op.A synchronous throw out of
renderToString, or out ofrenderToStream's first pass (and sorenderToFrameStreamandserverComponentResponse), is now reported to the server error hook askind: "render",handling: "failed"before it is rethrown, where it previously reached the caller without the hook hearing it. A request handler that catches it and callsreportRequestFailureadds nothing; the hook hears it once, as the render's. -
87a3862: Server
createMemoover an async iterable now keeps by-slot flight memory like thenables do: a<Loading>retry that re-creates the node adopts the slot's settled first value (or joins its pending flight) instead of opening a fresh iterable every pass, so sources that hand back a new iterable per call (subscribers,liveQuery()) converge instead of reporting "did not converge". -
7addcc6: Fix two SSR failures when the shell suspends above an
<Errored>(a lazy memo throwingNotReadyError, the router's flash-decode shape). A<Loading>below the boundary whose content settled before the shell's suspension did lost its content: the fallback shipped and nothing swapped it, because the fragment inlined into a shell that did not yet hold its placeholder. And a hole that reached a suspension through a returned accessor ({props.children}resolving to an<Errored>whose subtree is pending — alazy()route with no<Loading>between, for one) re-read its expression on every retry, re-creating every component above the boundary — state and pending sources included, so a component owning its pending source never converged. The retry now resumes the accessor that suspended. -
Updated dependencies [0c550fd]
-
Updated dependencies [ecb68a1]
-
Updated dependencies [b07fed5]
-
Updated dependencies [21ff8f8]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [fa371c3]
-
Updated dependencies [96c5e19]
-
Updated dependencies [9a213bb]
-
Updated dependencies [721eb06]
-
Updated dependencies [b07fed5]
-
Updated dependencies [bc51ada]
-
Updated dependencies [eaddda4]
-
Updated dependencies [d231b99]
-
Updated dependencies [3086f1b]
-
Updated dependencies [3c7631a]
-
Updated dependencies [09eadfe]
-
Updated dependencies [be82cf3]
-
Updated dependencies [fd9f381]
-
Updated dependencies [b07fed5]
-
Updated dependencies [fff1615]
-
Updated dependencies [11e9fb6]
-
Updated dependencies [11e9fb6]
-
Updated dependencies [b0bad02]
-
Updated dependencies [0c550fd]
-
Updated dependencies [2317623]
-
Updated dependencies [4f67697]
-
Updated dependencies [2317623]
-
Updated dependencies [d1309ad]
-
Updated dependencies [924d909]
-
Updated dependencies [6be6c51]
-
Updated dependencies [7acc039]
-
Updated dependencies [bde4299]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1b9ceb6]
-
Updated dependencies [1a3f87f]
-
Updated dependencies [5622be8]
-
Updated dependencies [11e9fb6]
-
Updated dependencies [b07fed5]
-
Updated dependencies [b0bad02]
-
Updated dependencies [b07fed5]
-
Updated dependencies [b0c8489]
- @solidjs/signals@2.0.0-rc.14