Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
7982c1a
fix(solid): a claim reads the trace's snapshot; the backlog lands aft…
ryansolid Oct 6, 2026
3aeba4e
feat(solid,web/frames): an adopted occurrence claims through the hydr…
ryansolid Oct 6, 2026
092b40b
docs(server-components): consistency contract — C1's mechanism is the…
ryansolid Oct 6, 2026
433a874
docs(server-components): record the 2026-10-06 rulings on A2b — the p…
ryansolid Oct 6, 2026
a5472d7
fix(web/frames): A1b + A4 S-ref — refs settle at the write through th…
ryansolid Oct 6, 2026
193be50
fix(web/frames): A4 S-record — the document declares a slot record at…
ryansolid Oct 6, 2026
964dc35
docs(server-components): frames rulings — 1.2/1.3's carriers and the …
ryansolid Oct 6, 2026
40dfa26
fix(web/frames): A7 — a frame's error is an errored async value: the …
ryansolid Oct 6, 2026
9ec2e6c
fix(web/frames): A7 — the re-ask mints its callable through the late-…
ryansolid Oct 6, 2026
87e5827
fix(web/frames): A3 — a reveal applies its range; R.reveal's readines…
ryansolid Oct 6, 2026
7713f51
docs(server-components): frames rulings — 3.3's outward face landed (…
ryansolid Oct 6, 2026
1cb5b1a
test(web): pin the adopted face of frames-rulings 3.3 — an sc:live er…
ryansolid Oct 6, 2026
9c5875b
frames: A5′ — fragment ownership by rendering, G9 collapse, claimRegi…
ryansolid Oct 6, 2026
79d6996
docs(server-components): A5′ landed — rulings 3.3 claimant by renderi…
ryansolid Oct 6, 2026
de972bd
frames: one sweep is one chunk — the `ops` member (C13)
ryansolid Oct 6, 2026
4b5350c
frames: the plain response's streaming bound — `complete.bound`
ryansolid Oct 6, 2026
c73f20f
frames: a server <Loading>'s post-flush failure renders its outcome (…
ryansolid Oct 6, 2026
0e38a57
docs(server-components): frames rulings — the server half built (A6),…
ryansolid Oct 6, 2026
6cf0809
frames: the A6 client half, trimmed (−56 B min)
ryansolid Oct 6, 2026
dbb3237
test(hydration): generic hydration consistency pins and harness arm
ryansolid Oct 6, 2026
ca76d9a
size: raise hydrating caps (maintainer Size-Exception); frames/page c…
ryansolid Oct 6, 2026
f171325
docs(server-components): Phase A gate taken — reds green / unrepresen…
ryansolid Oct 6, 2026
0aab932
test(server): the generic-hydration artifact renders on a fake clock …
ryansolid Oct 6, 2026
d8158b1
frames: Phase B — the server-announced tier mechanism (seam only, no …
ryansolid Oct 6, 2026
e05ba02
docs(plans): frames savings pass — Phase B landed (measured +145 br v…
ryansolid Oct 6, 2026
5f3094d
frames: C3 — traces tier: the lazy store materializer on the tier mec…
ryansolid Oct 7, 2026
89954fa
docs(plans, server-components): frames savings pass — C3 landed (meas…
ryansolid Oct 7, 2026
085af0e
frames: C4 — regions tier: nested server-content regions as a lazy chunk
ryansolid Oct 7, 2026
920bcab
docs(plans, server-components): frames savings pass — C4 landed (meas…
ryansolid Oct 7, 2026
21438c6
test(frames): pin the stylesheet gate and inline-style application (e…
ryansolid Oct 7, 2026
b0d26ef
frames: C5 — assets tier with the reveal-readiness term
ryansolid Oct 7, 2026
83f780e
frames: C5 follow-up — inline styles hold on the assets tier too
ryansolid Oct 7, 2026
2ace704
size: ratchet frames eager / page base / page live caps to the integr…
ryansolid Oct 7, 2026
5c5991c
docs(plans): frames savings pass — Phases B + C3–C5 landed together (…
ryansolid Oct 7, 2026
6b7213d
frames: trace tier — update-site wait for a mounted occurrence's new …
ryansolid Oct 7, 2026
732836f
frames: drop the dead #slotNodes writes for data occurrences
ryansolid Oct 7, 2026
9b8ec56
frames: C6 — binding-slot tier; _hk on event-slot consumers closes th…
ryansolid Oct 7, 2026
e7d6e9e
size + docs: C6 ratchet (frames eager 11.92 / page base 36.66 / live …
ryansolid Oct 7, 2026
f7570eb
frames: residue 1 — the D list (documentAddress, one dispose map, the…
ryansolid Oct 7, 2026
1896582
frames: FRAME_HAVE_HEADER / FRAME_HAVE_BUDGET off the client entry's …
ryansolid Oct 7, 2026
d3c7225
frames + server-functions: the lean re-ask — ctx.retry from the sf cl…
ryansolid Oct 7, 2026
2a33c33
docs(plans): frames residue pass — re-attribution of #3860's head (T …
ryansolid Oct 7, 2026
fce81d2
size + docs: residue 1 ratchet (frames eager 11.67 / page base 36.50 …
ryansolid Oct 7, 2026
a47bbe8
test(frames): C15 (e) — the gap #3844 named, pinned by name: a same-a…
ryansolid Oct 7, 2026
f127083
docs: residue step 2 — the preview pull form measured and stopped (ca…
ryansolid Oct 7, 2026
87127a4
frames: residue 3 — every fill through insert; createFrame marker-less
ryansolid Oct 7, 2026
8a0b090
size + docs: residue 3 ratchet (frames eager 11.38 / page base 36.29 …
ryansolid Oct 7, 2026
2cf6d83
frames: residue 4 — pending {$ref} reads owned by the decode table
ryansolid Oct 7, 2026
55eb4c3
size + docs: residue 4 ratchet (frames eager 11.21 / page base 36.09 …
ryansolid Oct 7, 2026
d9395e3
frames: C2 — live wire tier (lean): connection, have-list ledger, res…
ryansolid Oct 7, 2026
9991514
web: dynamicComponent — component-only sibling of dynamic
ryansolid Oct 7, 2026
dd0f771
size: compiled server-component page scenarios
ryansolid Oct 7, 2026
3b70dd4
size: compiled SC pages carry no element spread (maintainer ruling)
ryansolid Oct 7, 2026
1793a2a
Merge branch 'next' into wip/frames-pass-integration-2
ryansolid Oct 7, 2026
9914189
size: re-derive the hydrating caps after merging next (#3849)
ryansolid Oct 7, 2026
e12a8df
Merge #3849's final head (991418926) into wip/frames-tiers-integration
ryansolid Oct 7, 2026
7f5b9e0
Merge branch 'next' into wip/frames-tiers-integration
ryansolid Oct 7, 2026
70fe0c7
size: re-derive the frames / page caps after merging next (#3860)
ryansolid Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/frames-assets-tier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: the assets tier (frames savings pass C5) — the head mirror a segment's `assets` record drives (the stylesheet gate, module and typed preloads, inline styles) leaves the eager frames client for the lazy chunk `@solidjs/web/frames/assets`, loaded through the tier mechanism. A segment whose assets record carries stylesheets or inline styles is not ready while the tier is not resident: the server's fallback stays on screen and the reveal happens at max(tier load, stylesheet load) — no segment reveals unstyled. Once the tier is resident only stylesheets gate; inline styles, modules and preloads apply at the record's arrival, and a segment with none of these never waits on the tier. New export path `@solidjs/web/frames/assets` (`@experimental`); `InstallOptions.tiers` loaders resolve `TierModule` (a module with or without `install()`).
5 changes: 5 additions & 0 deletions .changeset/frames-bind-tier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: the binding-slot tier (`@solidjs/web/frames/bind`, `@experimental`) — the `_s:*` marker parsers, consumer discovery, the morph's owned-position arms, the per-frame consumer sets and the fill's binding leave the eager frames client as a lazy chunk; `assign` leaves with them. A marker met while the tier is absent holds the frame under frames-rulings 3.1 (hydration-done waits; the install's flush binds). The delegated-event replay window is closed by a bare ` _hk` the server stamps on each document-face element with an `_s:on:*` position (4 bytes each, nothing else stamped): the tier marks the element completed at the bind and drains the queue, so a click before or during the hold reaches the bound handler. Data occurrences now mount with the current record.
5 changes: 5 additions & 0 deletions .changeset/frames-drop-dead-slot-nodes-writes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: drop the three dead `#slotNodes` writes for data occurrences — the only reader (the zombie check) skips data occurrences, so the consumer-element arrays were built and never read
11 changes: 11 additions & 0 deletions .changeset/frames-live-wire-tier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@solidjs/web": patch
---

frames: the live wire moves into a lazy tier (`@solidjs/web/frames/wire`, the sixth chunk). The connection a `live()` loop's response runs under — the open-frame count its end is judged by (a death with frames open vs a completion), the join of a second loop onto an address already live, the supersession cancel, `connection.ended` / `cancel` — the have-list ledger per mount with its encoder, and the resume shape (`Last-Event-ID` + `X-Frame-Have`) load with the chunk. The eager client keeps the arm that awaits the tier's residency before a live response's body is read, so a live connection without the chunk cannot happen; the chunk's import starts at the `live()` call, before its first fetch (preload-at-call, through the new optional `responseHandler.onLive()` hook the sf client fires), and a non-live call never loads it. One accepted degraded case: content applied before the chunk was resident kept no ledger, so that first connect sends no have-list (and no ordinal) and receives a full snapshot.

Public surface: `Frame.have?()` is removed from the `Frame` interface (the ledger is the tier's `haveOf(frame)`, `@internal`); `encodeHaveList` moves from `frame-transport` into the tier (`@internal`; `decodeHaveList` and the `FRAME_HAVE_*` constants stay); the `@solidjs/web/frames/wire` export path is added; `ServerFunctionsClientConfig.responseHandler` gains the optional `onLive()`; `ServerComponentHandler` gains `onLive()`.

Pins: `consistency/tier-wire-preload.spec` and `server-functions-live-loop.spec` — the latter drives `live()`'s reconnect loop from the consumer's side (first-connect failure, the fail-fast 4xx set and 408 / 425 / 429 / `Retry-After`, the cursor as `Last-Event-ID`, the `online` wake, `return()` and a caller's signal at every phase, `live(GET(fn))`, the handler's local answers), closing the loop's branch-coverage gap.

Size: frames eager −965 min / −305 br (10.89 KB); pages −349 / −335 br eager; the `wire.js` chunk 1,922 / 934 (lazy; the live page loads it at its first `live()` call). Caps ratcheted to measured + 10 B.
5 changes: 5 additions & 0 deletions .changeset/frames-regions-tier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: C4 — the regions tier. Nested server-content regions (`{$frame}` slot args resolved to `<solid-frame>` region elements with frames bound over them) leave the eager frames client for the lazy chunk `@solidjs/web/frames/regions` (a new `@experimental` export path), loaded through the tier mechanism: the server announces `regions` where it mints one; a record naming a region met before the tier is resident waits — a fresh mount in the held set (the frame's hold registered under frames-rulings 3.1 on the adopt path, the server interior on screen), a mounted occurrence's new record pending in the store until the install's flush. `InstallOptions.tiers` now types a tier's module as the new exported `TierModule` (its exports are the tier's appliers; `install()` stays optional). Frames eager −1,078 B minified / −237 B brotli; the page scenarios −1,083 B minified; the non-SC scenarios unchanged.
5 changes: 5 additions & 0 deletions .changeset/frames-residue-1-d-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

Frames client, residue pass 1 — the D list (dead / incidental code off the eager client; frames residue pass §2 rows 2b, 2c, 2e, 2f, 2g). `documentAddress` is gone: an adopted mount's address comes with its binding (every serialized reference resolves to the call's binding), and a mount with none — the per-function placeholder rendered directly — binds the function id. `slotsFor` keeps one dispose map: a live range binding's `insert` lives under the fill's own owner where there is one, else under an owner registered the same way, so a re-call disposes whichever path the previous invocation took. The zombie heuristic and the `#slotNodes` map it read are gone — the morph relocates ranges among siblings and recreates nothing (DR-5), so "mounted" is the frame's set. `#revealSegment` has one path: a frame created without `FrameOptions.reveal` reveals through a default seam that inserts the content at once (no boundary) — the content is applied as it is revealed, as on the boundary path — and a placeholder missing its closing comment is not revealed (dev error; `#showFallback` already treated it so). `handler.showing` no longer brands the bare per-function component: the reference a reader holds at t=0 is the bootstrap's binding, branded there. The capture arm (`contentHTML` + the last-unmount capture) stays: `lifecycle-matrix/remount` pins it.
5 changes: 5 additions & 0 deletions .changeset/frames-residue-1-have-constants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

`FRAME_HAVE_HEADER` and `FRAME_HAVE_BUDGET` leave the `@solidjs/web/frames` client entry's export list (frames residue pass §2 row 5a). They stay exported from `@solidjs/web/frames/server`, where the header is read; the client sends the have-list itself through the handler's `resume` and nothing consumed the names off the client entry. Removed public surface, `@experimental`.
5 changes: 5 additions & 0 deletions .changeset/frames-residue-1-lean-reask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

The lean re-ask (frames residue pass §3.3). The server-function client hands its response handler the call it dispatched as a thunk — `ctx.retry` on `responseHandler.handle`, `info.retry` on `responseHandler.intercept` — the same reference, arguments, declared shape and per-call options, so a `GET`-declared read stays a GET by construction. The frames client's re-ask for an errored boundary's `reset` (frames-rulings 3.3) is that thunk: `callFor(address).retry()`. Gone with it: the frames client's `reask` installer and its reads of the RPC slot and the metadata brand, and `createServerReference(id)` on the client half of the server-function RPC slot (`getServerFunctionRPC()`, `@internal`), which nothing reads any more. `callFor` (`@internal`) now returns the recorded call with its `retry`. The `responseHandler` type in `ServerFunctionsClientConfig` documents the existing `intercept` seam alongside `handle`, both carrying `retry`.
5 changes: 5 additions & 0 deletions .changeset/frames-residue-3-every-fill-through-insert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

Frames client: every slot fill is placed by `insert` — the fill's output has the core's lifecycle (created under the fill's owner, claimed in place under hydration through `insertExpression`'s claim pass, disposed with the owner) instead of a frame-side copy of it. The Solid binding's static path (`normalizeSlotContent`, `isReactiveContent`, the in-place `settle` test) and the frame runtime's own range writer (`#replaceRange`) are gone; a static fill is one `insert` of a non-function value (no effect created), a reactive one binds the range as before. `createFrame` / `createFrameElement`: the frame element IS the range — the never-used comment-marker range mode of the runtime is removed. **`Slot` (`FrameOptions.slots`, `@experimental`) changes shape:** a callback owns its range — it places or binds its output before `ctx.range.end` over `ctx.existing` — and its return value is no longer read (before: returned nodes were placed by the frame; `undefined` claimed). A slot range missing its end marker (`FRAME_MARKER_CORRUPTED`) is left as the server rendered it instead of being filled at the parent's end.
5 changes: 5 additions & 0 deletions .changeset/frames-residue-4-sref-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: residue 4 — a pending `{$ref}` read is owned by the decode table. A slot record's `{$ref}` to a key its response has not delivered yet is answered by that response's `createJSONDataTable` with a promise the table owns (marked `s = 0`, carrying `c`, the callbacks run when it settles), settled by the key's `data` chunk through the table's own `apply` and rejected by `close(error)` when the response ends (L1 — a value that never comes is an error where it is read, not a silence); a version bump drops the superseded table and its reads. The frames host keeps only the `record.pending` count (a fresh mount waits for its record, the covering `<Loading>` pends on the landing alone) and one fan-out helper; `pendingRef` / `settleWait` / the per-store waits are gone. `FrameHostOptions.closeData(frameId, version, error?)` (new, `@experimental`) tells the integration a response ended; the shared host closes that response's table. The transport awaits `prepareData` before a `slot` chunk whose args carry a `{$ref}`, as it does before a `data` chunk, so `resolve` always has a table. Frames eager client −505 min / −174 br (34,272 / 11,367 → 33,767 / 11,193); the lazy decode chunk +420 / +161.
11 changes: 11 additions & 0 deletions .changeset/frames-tier-mechanism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@solidjs/web": patch
---

Frames: the server-announced tier mechanism (frames savings pass §2, Phase B — the seam alone; no tier is cut yet, every capability stays eager).

The server learns at render time which frames-client capabilities a response or a document needs — it mints each feature — and announces their names so the client can start the tier's import in parallel with the content instead of at first use: on a frame stream, the `X-Frame-Tiers` response header (the tiers the sync render pass minted) and, for a tier first needed after the head left, an in-band `tiers` member on the next chunk out (`FrameChunk.tiers`, additive); on a document, the `_$HY.r["sc:tiers"]` hydration record (cumulative, re-written at each new mint) plus a `<link rel="modulepreload">` per tier whose chunk URL the integration gave (`frameTransformDirectResult(value, { id, args, tierUrls })` — the server does not know the client's chunks; absent, the record alone carries the name). The sink's `needs(tier)` runs at the mint sites: a binding-slot position read (`bind`), a nested server-content region (`regions`), an assets chunk (`assets`), a traced container in a slot arg (`trace`), a `live` response (`wire`). A sync `renderToString` document announces nothing (no shared render slot); the client detects.

The client (`@solidjs/web/frames`): `installServerComponents(host?, { tiers })` takes the tier → loader map (`() => import(...)`, the module exporting `install()`), reads the document's record and starts each named load; `applyFrameResponse` reads the header before the body and `chunk.tiers` at each chunk, and a `data` chunk awaits the tiers it names before it decodes (its node tree needs them). A fresh mount whose tier is not resident — a data occurrence's `bind`, a region-carrying record's `regions` — waits like a recordless called occurrence: on the adopt path that wait is one more reason in the frame's registered hold (frames-rulings 3.1 — the event-replay window stays open, hydration-done waits), on the stream path the record stays pending in the store; the readiness check itself starts the load when nothing announced it (detection is the fallback), and the install flushes every live frame so the held occurrence mounts with the record it was held on. A name with no loader is resident (eager), which is every name today.

Measured before written (edited dist copies, `scripts/size`): frames eager client +473 B min / +145 B br (≤ +150 budget; the plan's ≈ +100 estimate, ×2.5 pre-estimate ≈ +250); pages +478 min; every non-server-component scenario 0 B. Frames server dist +793 B min; `@solidjs/web`'s own server entry 0 B.
5 changes: 5 additions & 0 deletions .changeset/frames-trace-update-site-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

frames: the trace tier's update-site wait. A MOUNTED occurrence's new slot record whose args carry a `{ $tr }` container-trace marker while the trace tier is absent (a live slot op minting the page's first trace after the shell, a refetch adding a projection arg) now stays pending in the store: the live binding keeps the args it shows, the tier load starts, and the install's flush applies the record as an args change with the marker materialized. Before, `#syncSlots` guarded only the fresh mount (`needsTrace`) and the regions tier at the update site (`needsRegions`), so such a record was pushed into the live binding raw — the marker read as the value. Mirrors the regions tier's update-site wait.
6 changes: 6 additions & 0 deletions .changeset/frames-traces-tier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"solid-js": patch
"@solidjs/web": patch
---

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-trace` is a new `solid-js` entry carrying `materializeContainerTrace(marker, claiming?)` (the store engine reached through `@solidjs/signals`, the hydration dispatch `withStoreHydration` and the patch protocol read back from `solid-js`); the materializer leaves `solid-js`'s main and `solid-js/internal` entries. 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 the `claiming` hint (`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.
5 changes: 5 additions & 0 deletions .changeset/web-dynamic-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/web": patch
---

Add `dynamicComponent`, the component-only sibling of `dynamic` (client and server entries). Same contract, semantics and hydration shape as `dynamic` — the two share one implementation — but its source type excludes tag names, and it never references the element runtime: `dynamic` must be able to render a tag, so one `dynamic` on a page retains `createElement`, `spread`, the prop-collection helpers and the SVG/MathML tables for everyone; `dynamicComponent` never does. It is the documented way to mount a server component (`dynamicComponent(() => getStory(id))`); a server-component page mounted through it sheds ≈ 2.2 KB brotli. `dynamic` is unchanged.
Loading
Loading