Skip to content

@solidjs/signals@2.0.0-rc.21

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Nov 01:13
· 48 commits to main since this release
2105ffb

Patch Changes

  • f509fe3: Avoid evaluating property getters when attribution compares memo outputs and list items for unstable identity diagnostics.

  • 46ab2dd: Dev diagnostic CAUGHT_REACTIVE_READ (D-42 / S-47): warns once per computation when a reactive read's throw — NotReadyError or the source's error — was caught inside the computation's body and the pass completed with a value. An async computation is judged when its flight settles (a rejection is the throw continuing; a landed value is the catch). The runtime's own catches (boundaries, isPending, watch, comparator/cleanup/effect-callback routing, solid-js's hydration trace) never warn. Dev-only; no production bytes.

  • 9379257: deep() tracks and snapshots a plain store record in one walk instead of two. Records in a projection or optimistic family, chained stores and overlays keep the two walks.

  • f17cd09: deep() and snapshot() read inside a computation now serve the frame the store's container node serves that computation. Before, a container held by a transaction (an action that wrote the store on both sides of a yield) always served its committed backing to deep()/snapshot(), even in the transaction's own landing flush, where the same computation's property reads already saw the landed values: a render effect reading s.x and deep(s) logged 3:{"x":1} and was never re-run. The same applied to a derived createStore(fn, seed) whose user setter wrote across a yield. Reads outside a computation still see the committed backing of a held container.

  • 65da5de: fix(signals): defer: true skips an effect's first run when a held update mounts it (#4543)

  • 689347f: fix(signals): a deferred effect mounted by a held update keeps its first value as the next pass's prev (#4556)

  • 20c115c: createStore(fn): a derive that went pending on a source and was settled without re-running (a staged rejection, or a re-ask that returned to the committed question) settles its readers with it, as a memo's would: isPending ends and a refetch's rejection reaches them.

  • 5315127: A derived store (createStore(fn), createProjection, createOptimisticStore(fn)) hands an upstream error to its readers once: a re-run of the derive that meets the same error no longer re-runs a reader already showing it, so an effect's error arm runs once per error.

  • ecf3011: A derived store's held rejection is staged like a memo's (D-41, #4489). While a hold is open — an action holding a filter change behind a pending guess, or any transition — an untracked read of the store (a click handler on a row the screen still shows) reads the screen instead of throwing the new error; the effect's error arm, <Errored> and the read all see the error together at the reveal. Closes D-41's derived-store deviation (the it.fails pin in error-is-an-answer.test.ts now passes as it).

  • bb7f9b5: createStore(fn) over a latest()-reading memo shows an action's value as the memo does: a derive pass that is a lane's work writes its leaves as that lane's derivation instead of staging them with the action.

  • 64fedc7: A reader of a derived store that mounts while the derive's input is in flight is held until it answers, as a reader of the async memo is, instead of showing the previous answer first: createStore(d => { d.v = m1() }) over a memo that reads latest(), read mid-flight, no longer shows the old question's value before the new one (#4491).

  • e49b0c7: A sync derived store over a failing sync memo hands each new error to its readers once per flush, not twice: the error a source propagates to the derive now reaches the family's dedupe.

  • 6af149e: A write that a custom equals calls equal to the committed value now nets to it (D-8) while another write to the signal is staged, so it pends nothing (#4483).

  • 19e0951: An error boundary no longer hands its reader an error that was superseded inside the same transition (#4527). Here is the case: an action keeps its transition open, the store's re-ask is rejected (that rejection is staged, D-41), and a second re-ask answers before the commit. An effect whose compute created createErrorBoundary(() => store, …) used to get the fallback at the commit and never ran again. Now it gets the answer, as the screen does.

    Two changes make that work. First, a pass that a derived store's pull throws a staged error into is now linked to the store's derive, including the untracked shape probe of a returned proxy. That way the re-ask and the answer that supersede the error reach the memo. Second, an error boundary no longer waits for first content: one that has not shown content reveals a recovery that lands in the hold it caught the error in together with that hold's commit, instead of keeping its fallback past it.

  • dd5f9d1: An <Errored> heals once no reader it caught still reads the failed source, instead of staying on its fallback after navigating to another branch (#4644). This covers an error read only by memos (a <Show>/<Match> condition), and a source something outside the boundary still reads: a hoisted memo, a derived store or projection, a reader under another <Errored>.

  • ecf3011: An error boundary that a held pass mounts over a source whose error is staged in that hold (D-41) no longer loops the flush for good (#4480). The seam's fold-back of a fallback staged into the hold its readers landed in (D-16, U-9) is a loading boundary's: it also cleared an error boundary's readers, so the output re-derived into the staged error every round and the effect owning the boundary re-ran on each new output. The error now waits with the hold and its fallback reveals with the frame (D-41, F-12). Seen with a derived optimistic store whose re-ask failed while a guess was pending, read by an effect through createErrorBoundary.

  • 39f951b: A rejection under a <Loading> that has not shown its content reaches <Errored> at once when a latest() reader under the same boundary reads a derivation whose upstream still has a flight out for an older question (#4568). The boundary judged its readers settled while that latest() reader was queued to re-derive, revealed into a hold the reader then went pending in, and the later rejection was staged in that hold until the obsolete flight landed. A verdict reader queued to re-derive is now waited on until its pass answers (A19, D-41).

  • 3a73a36: An <Errored> boundary over a list no longer reveals the list from before the error when the error's replacement lands held with an action (a guess on the errored store re-asked it): the error stays until that action's frame reveals, instead of showing the previous question's rows.

  • ceb7896: A memo over an error boundary's output no longer halts when the boundary's source rejects during a held change: the boundary shows its fallback, as it does when a render effect reads the output directly (#4640).

  • 4235019: An <Errored> recovery re-arms its subtree: every <Loading> under an error fallback counts as not shown, so on recovery it catches what is still pending in the replacement frame and shows its fallback beside the recovered content, instead of the error staying up until the nested content is ready (#4627).

  • a56b10b: <Errored> no longer recovers beside a memo that is still answering an older input (#4592). While the error fallback shows, a reader behind it that goes pending — which passes the error boundary by (A6) and, hidden, holds nothing — is now also waited on by the boundary, as a pending under a hidden loading fallback already was. When the source that threw answers but another reader in the content is still asking for the new input, the error stays the display until that reader answers too, so the content reveals as one whole frame instead of showing the pending memo's stale value beside the new input (D-39, F-1).

  • b773146: A Loading/Errored fallback is born held only when the boundary's output pass that builds it read the hold (D-27), no longer by the flag of the enclosing pass it is parented under (#4428).

  • 343f007: A memo whose verdict posture is only inherited (it reads a memo that calls isPending/latest, and probes nothing itself) is the hold's work where it reads the hold's own write: it reads that staging instead of the pre-hold value, so a branch the hold opens no longer reads the pre-hold world and the hold lands, whether the writes go through one flush() or several. A memo that probes in its own body still reads the screen, and what a branch born into the hold then asks for no longer keeps the hold from landing (#4544).

  • ab3e844: A derived store heals with its Errored like the memo it derives from (#4451). A leaf born from a lane container's staging — a derive's lane-pass write to a key nobody had read yet — takes the staging as the lane's derivation, as the write would have left a leaf that existed then; before, the leaf was born a plain staging the park held with the frame, and an Errored the memo's lane value healed stayed on its fallback over the store.

  • 87f3446: Clear the settled source at propagateStatus's verdict re-derive shortcut: a verdict reader (isPending/latest) whose probed source errored kept that source in its pending set, so when its re-run parked on a second source, that source's landing left the reader pending forever (and a Loading boundary over it never released).

  • 62745f6: Shrink the compiled and hand-written floors: the dependent walks, dependency clearing, status writes, effect-run queueing, flatten's thunk walk and the delegated-event parent hop share their code (no behavior change).

  • 4a4803c: A derived store (or memo) reading a keyed optimistic store through latest() now picks up a confirmed row after a correction folded the truth beneath the guess: a confirming truth landing re-derives the latest() readers too.

  • 6ec1613: fix(signals): a guess over a question in flight keeps the frame whole when a memo, or a chain of derivations, sits between the list and its binding — the guess shows (#4561)

  • d9aa8f7: A keyed optimistic store's arrangement guess written while its derive asks a new question (an input changed, not a refresh()) is re-based over that question's answer, like an older truth (#4525). Before, the answer folded beneath the guess, so the rows the guess had only carried over from the old answer stayed on screen; under <Loading on={filter()}> the region left its fallback showing the new input beside the old list minus the guess until the action's refresh landed.

  • 60d6307: A render effect in a branch an action holds (<Show when={y() > 0}> opened by the action's write) no longer runs ahead of that branch's landing when it reads latest(y) inline, while the same read through a memo waited: a member of the frame a held pass built is the hold's own work, so neither form seats it as the hold's verdict reader, and memo wrapping no longer changes what it shows.

  • 7135a77: A verdict memo (createMemo(() => isPending(x)), the router's useIsRouting) whose probe ran before its flush joined a transaction now shows true while that transaction is held, instead of false until it lands (#4439).

  • 592a43b: A keyed child under a display-ahead branch no longer mounts a third time when a flush parks: a re-derive that gives back the staging it superseded keeps its held readers and their children (#4417). A display-ahead branch no longer attaches with an empty text hole while a child born into the open hold is still pending (#4574).

  • 2ba429b: A lane member disposed before its lane dissolves no longer runs again (#4613). When an action's commit disposed a region whose list was still the action's lane work (e.g. a keyed <For each={[filter()]}> region swapped by a filter change held with the action), dissolving the lane re-queued the dead list memo; its pass dropped the disposed flag, so it mapped rows in the torn-down region, stayed subscribed, and dev logged RUN_WITH_DISPOSED_OWNER. A disposed reader is no reader (F-3, D-11): dissolution now skips it.

  • c8d6fd2: A landing that settles a reader held by an optimistic lane no longer makes the flush join that lane as its transaction: rows a list guess mounted over a pending refresh render instead of staying empty.

  • 64625bc: fix(signals): effects an optimistic guess mounts honor defer and run once per write (#4557)

  • 2ba10bd: An async memo over two verdict memos asks each question once, and a lane's readers show its answer together (#4408, #4399; F-6, F-9, D-25, D-26).

    • An async memo with no verdict window of its own that waits on a source's flight as the hold's work (holdWork) drops its verdict posture at the wait, so the source's verdict-lane re-derive does not move it into that lane early, and the pass the landing runs reads plainly. Before, latest(m2) showed c1101 while m1:1122 of the same lane was still in flight.
    • A held memo's pass that re-entered its hold at its head stays that hold's work when it reads another hold's verdict lane, in either read order. Before, moved into the other lane, it read its own hold's landing as the screen and asked m1:15 a second time.
    • A memo with a committed value and no pass staged keeps the verdict posture it reads when a pass of it reads that verdict memo while it is pending (#4408 after #4504; D-25). #4504's rule — a pending read hands on no posture — stays for the hold's own work: a pass born in the hold's world, or one over a staged pass of its own. Before, m2 = m0b() + 1 lost its posture at such a read, re-ran plainly into the frame m0b's answer landed in, and latest(m2) showed c1101 before v1122.
  • 8cac5f0: fix(signals): lane work re-based over a held change's answer reads that change's world in every flush and is not re-derived on the committed world while it is parked, so a refresh or a second entangled change no longer commits a derivation over the old inputs beside the new ones, and the held change lands once its last async answers (#4576, #4577)

  • 42924f4: A pass that reads latest(m) and then isPending(m) of an unheld refetch outside any Loading no longer loops ("Potential Infinite Loop Detected"): the latest read opened the flush's transaction for nothing held, the probe asked to re-run at that transaction's landing, and every re-run opened another. The probe now joins the verdict lane without asking for that re-run; the reader shows the committed value beside true, then the refetch's answer.

  • 9c4e4a8: The transaction a latest() read of an unheld refetch opens is no longer kept alive after it lands: the mark that it was opened by latest lives on the transaction itself instead of in a module-level reference.

  • edd3c44: A memo pulled inside latest() now computes as its own pass (A31): an isPending() it calls answers for what it reads, so a held write still in flight reads as pending there as it does when a plain read pulls the memo (D-23).

  • 77be406: fix(signals): latest() and isPending() in a hydration claim read the server's snapshot, as a plain read does, so a client write during the hydration window no longer puts a nested <Loading on> into a fallback the server never rendered (a "Hydration key miss") (#4570)

  • 061b013: A memo whose last run read latest(x) no longer holds a <Loading on> switch while x's new flight is up: its read of the switch's own write is the screen's, and a flight it reads only inside latest() is one it reads ahead of (F-6). The fallback shows at once; the memo keeps its committed value until the flight lands (#4661).

  • d9a97d0: The UNSTABLE_LIST_IDENTITY diagnostic reads a row's id/key/_id only from an own data property, so it never invokes a getter (a class instance's prototype id or an own accessor) while pairing rows.

  • 6e569c7: A <Loading on> re-arm whose content reads its async through an error boundary built outside the <Loading> shows its fallback with the change's frame again, beside a sibling re-armed boundary (#4638). A pending error-boundary output read only behind fallbacks no longer holds the frame.

  • f53d927: An error boundary output read only behind fallbacks now holds nothing when it reaches them through a memo or another error boundary built outside the <Loading>, or is not read at all; LOADING_ON_OUTSIDE_HOLD no longer fires while the re-armed fallback is on screen.

  • 97e1826: A <Loading> that has not shown content no longer reveals content derived from a write still held outside it (F-7, F-1). Two reveals did not consult the hold: a first load the held write re-asked (kept out of every hold, #2937) landed mainline and revealed its answer beside a screen still showing the frame before the write (#4593); and a reader settled by an answer staged in the hold — a rejection, which marks no frame reader (D-41) — revealed the content's stale committed value under a re-armed on boundary and let the held write commit early (#4591). The boundary now waits on the held node and reveals with that hold's commit; the rejection shows when a value for the same question would. A first load re-asked under an open action still reveals at its landing (#2937).

  • eed4469: mapArray and <For> with a keyed function or keyed: false no longer call setSignal for a row whose signal already holds the new item and has no staged write. Before, every retained row in the common prefix and suffix was written on every list update.

  • 71a6a75: A memo between a render effect and an async memo in flight is born held with the mount that first reads it (#4504, D-2, D-22, D-27). With an input that reads latest()/isPending(), the memo carried that verdict posture by what it read (D-25), and its pass in a plain mount mid-flight stamped the posture from the read of the pending async memo even though the read threw: at the landing the memo re-ran as a verdict reader and was served the async memo's committed value, so the held reader showed the old answer at the landing and the new one only at the commit (r:2, then r:4). A read of a pending node now hands on no posture to a pass that is not a verdict reader already: the reader waits for the re-ask's answer and shows it once (r:4), as when it reads the async memo directly. A memo that was already a verdict reader on screen keeps its posture.

  • 2abf27d: A signal written and written back to its committed value in one tick (D-8, #4452) is no longer read as a staged value by a labelled untrack read in dev, which took the slow read path and held what prod did not. The reader-seat shadow's table gains the D1'' / L1'' / V3'' rows for that read (test-only), clearing the INV-seat-3 "world after the read" classes on next (8 in the signals suite, 7 in web, 60 in the generator).

  • b5077ea: Writes that net to a signal's committed value in the tick an action opens a hold no longer pend the signal: a render effect reading isPending(s) shows false during the hold and is not re-run when the action lands (D-8).

  • 5315127: createOptimisticStore(fn) delivers the error of an upstream memo its derive reads, as createStore(fn) does: the optimistic store now builds its family through the projection's own constructor.

  • 4f9768f: Fix an infinite flush loop when a memo over an optimistic store list (an "N left" count, a filter) read through a lane re-based over a parked refresh (#4643). Lane work re-based over a hold's answer no longer joins the flush into that hold, so a plain write beside it (a filter change) shows once instead of showing, retracting and showing again, and a guess beside the held answer no longer reveals it early. A push after a removal guess no longer leaves an undefined row in the list.

  • 227a01d: Optimistic keyed lists: a push after a filter (with earlier saves still out) no longer hands <For> an undefined row; a guess written while an earlier action's refresh() is out shows at once instead of waiting for that refresh; a refresh landing older than a guess no longer brings back a row another pending guess removed; and an unkeyed list's guess written after a correction composes on what the screen shows, not on the truth beneath it (no duplicated, empty or missing rows).

    New DEV diagnostic OPTIMISTIC_WRITE_BEFORE_LOAD: an optimistic write to a source whose first answer has not landed (createOptimistic(async fn), createOptimisticStore(fn, seed)) throws in development and makes no guess — before its first answer the source is loading, not a value to be optimistic over. Keep pending items in a separate list until the source loads, or disable the action until then. Production behaviour is unchanged.

  • 5f07d04: createOptimisticStore: a row removed by an accepted action no longer comes back when its refresh lands while another removal is pending; each re-base of an arrangement guess composes from the rows the setter wrote.

  • ecf3011: A keyed optimistic store's removal guess re-based over a new question's answer no longer shows the old list's rows beside the new ones (#4482). The re-base (D-35) kept every guessed row the new truth lacked, including the rows the guess had only carried over from the truth it was written on; it now composes the guess's own edit — the rows it added or carried from another action's guess, minus the rows it removed — over the new truth, and takes the rest from that truth (F-1).

  • 885b99b: A second action's guess into an optimistic slot now shows at once while the first action's refresh() of a list the overlay also reads is still in flight, whatever order the overlay reads the two in, and whether it reads the guess directly or through a memo derived from it (#4629).

  • ab5b5cb: An optimistic guess into a second createOptimistic log shows at once while another action's refresh() is in flight over the list both read (#4666). A reader held by that action's hold is seated in the new guess's lane when every flight it waits on is a re-ask (D-34 ¶3, F-11); one pending on a new question stays held (#4577). The same holds when that log already carries another action's guess whose lane has shown: a render effect reading the overlay now re-runs at that lane's reveal instead of waiting with the refresh's hold.

  • 9034647: In the production build, getContext (and so useContext) with no owner now reads as if under an empty root: it returns the context's default, and throws ContextNotFoundError when there is none. Before, it threw NoOwnerError even when the context had a default, so a component created outside any owner (JSX at module scope, in a timer or event handler, or after an await) crashed in production while dev and observe, whose createComponent runs each component in a root, returned the default. Dev still throws NoOwnerError for a bare read with no owner. Reads under an owner are unchanged.

  • efd7f7d: A projection's adopted leaves (length among them) written by a lane's pass are the lane's derivation, as its container is: a row closed through an optimistic filter while a truth landing was held beneath another guess no longer stays on screen (or reads as undefined) (#4631). A second guess made while an earlier action's re-ask is in flight now shows through a keyed projection when that re-ask lands, as it does through the memo (#4629).

  • 25b94f0: A reader of a derived store whose derive carries a staged error (D-41) now links to the derive, as a reader of a pending or errored one does, so it re-runs when the derive's source answers. An <Errored> mounted in the change that errored its content over a keyed createProjection showed its fallback for good; it now shows the answer.

  • e704cfc: A store whose own __proto__ data is removed — by reconcile, a setter's delete, or a projection — now reads undefined, as a store that never had it does, for tracked and untracked readers alike (#4672, core R30). The key's node used to be told the backing's inherited value, so the read served Object.prototype wrapped as a store. Tracked readers are notified of the change; __proto__ assignment stays inert.

  • e56a4a0: Reader seat S1 M1 (test-only): the shadow seat and slot table (core/seat.ts, __TEST__) re-baselined against current next — the verdict seat (vhold), the branch world (creatorsWorld) and the owed table rows (V1'' staged error, Vl4', D8, lane rows for a boundary's structure, fallback's bracketed read) modelled, INV-seat-1 amended per D-27, and a shadowKnown allow-list naming the filed next bugs (#4426, #4427, #4428, #4429). No change to any build output.

  • e56a4a0: Reader seat shadow (test-only): a new core/seat.ts computes the reader seat and slot table beside the existing deciders under __TEST__, with the INV-seat family recording disagreements for the differential. No change to any build output.

  • 8a8e950: fix(signals): a guess re-based over a question's answer stays off the screen while that question's transaction is parked on another read, so the old question never shows beside the new answer's rows (#4559)

  • 74126b1: A memo on the observation lifecycle (unowned, or lazy: true) that blocked on an async source is no longer released before the retry its source's landing queued (#4474). The settle walk re-enqueues the blocked reader and, finding it unobserved, released it on the spot: the release pulled the retry back out of the heap, so the memo never computed the landing and woke later with no value — latest() of it returned undefined beside isPending() === true. The release now waits for that pass (REACTIVE_RELEASE_AFTER): the retry runs once, as the owned twin's does, and the node is released at the pass's tail if it is still unobserved and settled. #2934's contract is kept (no perpetual recompute — one pass, then the release), #3078's untouched (reads revive; a dormant memo dirtied in the same tick is still reclaimed before the heap). Laziness is never observable (D-22).

  • 2c9a1e3: fix(signals): a re-parked node drops pending sources its pass no longer reaches, so a <Loading> whose first answer was superseded while a sibling refetched reveals once both settle (#4541)

  • 718ec9c: Keep observing a pending store query when a later optimistic guess re-runs an existing reader, so bindings (shared or separate) do not show the new query's inputs beside rows from the previous answer, and the new answer's rows never show beside the old query for a frame when it lands.

  • 643a7a7: An async memo's first answer that resolves in the same tick as a plain write re-asking it is answered, then re-asked (D-22, F-9): isPending of it is true while the new question flies, and an identity memo or derived store over it reads the answer through latest like the memo itself.

  • fda83a3: A chain's verdict no longer flips to false mid-hold where a diamond's stays true (#4562). When a superseded flight's error is staged in a hold (D-41), the settle walk now marks every derivation it fully settles with the staging, not only a join it reaches a second time, so isPending stays true until the commit whatever the graph shape (F-9, F-2). The mark moved behind the verdict engine's hooks (installVerdictHooks), so builds without isPending/latest carry none of it.

  • 84353de: A non-keyed <Show>'s or <Match>'s narrowed accessor read inside an isPending() thunk no longer throws the stale-value error for a branch that mounted in the flush its condition landed in, so the thunk's later reads link and the verdict flips (#4648). The exemption holds only inside a verdict window (isPending / latest): a plain read of the accessor while its branch's opening is held still throws, rather than hand out the committed falsy.

  • f0471e8: Internal: the snapshot walk drops a redundant overlay guard before materializing a pending backing (materializePB already returns the backing unchanged without an overlay). No behavior change.

  • 66c6f56: Writes that net to a signal's committed value in one tick no longer hold an errored memo's re-run with an action, so isPending of it stays false (D-8).

  • 749b0d7: A memo's error staged with a hold now reaches the memos and derived stores that derive from it: latest() of them throws the proposed error and isPending() of them is true until the hold commits, as for the memo itself (D-41).

  • 404d06a: fix(signals): a re-pass equal to a first value already staged this flush is not a change, so a non-keyed Show over an async condition no longer remounts its child when the child writes the source on mount (#4540)

  • 50dec1b: Preserve signed zero and pending-state atomicity when a derivation reads a staged signal write accepted by a custom comparator.

  • 456e6d5: Store proxies wrap an empty object (an empty array for array stores) and use the store's internal target as their handler. V8 checks every trap result against the proxy target's own keys, so key listing, for...in and spread on stores get faster. Object.freeze, Object.preventExtensions and Object.setPrototypeOf on a store now throw a TypeError instead of breaking it.

  • e44f824: Smaller store bundles: the store family field (_fam) and its _overlaid, _asked, _woke and _shallow fields are now _-prefixed internals, so the prod and observe builds mangle them (no behavior change).

  • d5cf231: A store proxy over an array subclass or a class instance keeps the source's prototype: instanceof, Object.getPrototypeOf and .constructor answer as they do on the source, also after a write and in a projection's derive (#4670). Reading an inherited constructor on a draft still returns undefined (the prototype pollution guard).

  • ecf3011: A derived store's writes after an await no longer leak into the next user setter (#4484). A write-override write (the derive's continuation) notifies its target at once but left it listed for notification, so the next outermost store setter — on any store — notified the derive's staged landing as its own write; on an optimistic family that made the landing a guess over a pending one, and a pending removal guess showed its row again when another action guessed on a different store. An outermost setter now starts with no stale targets (F-11, D-33).

  • 7ea8706: A createTrackedEffect now re-runs for a same-pass write it reads through a memo (#4606), and for an action's landing when it re-ran during the transition for another dependency (#4607). A memo the run's own read pulls stages its value while the run recomputes, so its subscriber walk woke nobody; the run now re-queues for any memo dependency staged with a different value, as it does for a signal written before the run (#4598). A run during a hold is served the committed value of the held dependency and had consumed the run the landing replays; it now queues one run with that hold's stashed runs, so it re-runs at the landing as a stale reader would (A15), without holding the transaction open (D-29). A tracked effect that writes a signal and then reads a memo of it re-runs once the write commits, as one that reads the memo first already did.

  • aeca8da: A createTrackedEffect whose first run reads a signal written earlier in the same effect phase (another effect's first run, a parent's onSettled writing a prop) now re-runs once the write commits, as createEffect(compute, fn) does (#4598). A tracked effect reads the committed value and links its dependencies in the effect phase, after the write had already walked the signal's subscribers, so the commit at the next seam notified nobody. After a tracked effect's run, a dependency staged this flush whose subscriber walk predates the run queues the effect again for the round that commits it; writes that net to the committed value stage no change and queue nothing. The pass's own write-then-read is still never observed and never re-runs (#3006), and onSettled stays one-shot.

  • ef0aadd: Two or more async memos whose answers resolve in the same tick as a plain write re-asking them are all answered, then re-asked (D-22, #4495): a memo over them reads every answer through latest and is pending on the re-ask. An earlier landing's flush no longer re-asks the others before their answers arrive, dropping them.

  • d0fc285: createStore(fn) that nothing reads no longer holds a plain write with its derive's flight: a derive over a latest()-reading memo observed the flight as a frame reader although its store had no reader.

  • 62bf4c4: fix(signals): a pending memo nothing reads no longer holds a fresh <Loading> when its subtree mounts under a held update (#4542)

  • f8080f3: Dev: in a component body, a plain untrack(fn) that reads a pending async memo created in that same body now throws PENDING_ASYNC_UNTRACKED_READ, as a direct read does. Before, untrack cleared the check: the read threw NotReadyError to <Loading>, which re-ran the body onto a fresh pending memo forever, and the page froze. An untracked read of a memo created outside the body still reaches the boundary (D-23), sync untracked reads stay quiet, and untrack(fn, false) opts out. Production is unchanged.

  • 663ecb0: A memo born into a hold that reads latest() in its pass (for example by calling a function a Loading fallback returned) no longer mounts that Loading again and again once the content lands. The memo's re-run is still the hold's own work, so it joins the hold whatever it asked before (D-25), and the boundary it mounts belongs to the hold and appears at its commit (D-13): the screen goes straight to the content.

  • 2e05808: A verdict reader born into a hold reads that hold's value of a node it holds in every flush, not only the hold's own: one run by another transaction's flush was served the committed value (#4426).

  • 1445197: isPending() read untracked inside a pass reads what the pass's plain read does in a normal flush — the flush's staging — so a pass never settles on the old value beside the new one. Inside a hold it still reads the committed value, but it is final in the hold's frame (never true beside the fresh value), and the pass re-derives on the landed world before the hold's held runs show, so the hold ends with one frame — whether the pass was re-run while the hold was up, first read the held node in a later flush, or probed only in the flush that staged the write. A probe of a first value staged in the current flush (a narrowed <Show>/<Match> accessor in its landing flush) reads that value instead of undefined (#4648).

  • ab3d2ad: A memo pulled inside isPending() now computes as its own pass (A31): the probe's state no longer leaks into it, so a latest() it calls doesn't join the hold through the probe, and its reads don't answer the outer isPending (#4429).

  • 131ceb8: A render effect seated as a hold's verdict reader no longer leaves the seat when it reads a verdict lane of a transaction merged into that hold: the merged transaction's lane is the same hold's, read as the lane's value and shown with it (#4427).