Repository navigation
Conversation
…ume in a lazy chunk; `live` coverage closure
The live wire moves out of the eager frames client into the sixth lazy
tier, `@solidjs/web/frames/wire` (frames/src/wire-tier.ts → wire.js,
1,922 min / 934 br): `connect` — the per-address 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; `connection.ended` / `cancel`; the SSE reader
selection, proxied so a drained body applies nothing further), `cancel`
(supersession from `bump`), `have` / `haveOf` (the have-list ledger per
mount, RFC 11 §9.5, fed by the client's `#recordHave` at its three
sites) with `encodeHaveList` and the FRAME_HAVE_* literals bundled, and
`resume` (the ordinal + the have-list header). The eager client keeps:
`applyFrames` reading `options[LIVE_WIRE]` as its reader; `handle`'s
live arm awaiting `prepareTier("wire")` before the body is read; `onLive`
→ `prepareTier("wire")`; `resume` and `bump`'s cancel delegating through
`tierLoads.wire?.r`.
Preload-at-call: `live()`'s makeIterable fires the new optional
`responseHandler.onLive()` before the first fetch, so the import races
only the request; a non-live call never loads the tier. Accepted degraded
case: content applied before the chunk was resident kept no ledger, and
`resume` answers nothing while the tier is absent — that first connect
sends neither X-Frame-Have nor Last-Event-ID and gets a full snapshot
(the server never read the ordinal for a frame render); pinned.
Public surface: `Frame.have?()` removed from the Frame interface (the
ledger is the tier's `haveOf(frame)`); `encodeHaveList` moved from
frame-transport to the tier (@internal); `@solidjs/web/frames/wire`
export path added; `responseHandler.onLive?()` and
`ServerComponentHandler.onLive()` added. `ServerFunctionsClientConfig`
gains no key.
Pins: consistency/tier-wire-preload.spec (3) — the hook starts the import
at the call before any fetch, the arm awaits residency, X-Frame-Tiers:
wire joins the same load, a non-live call never loads the tier,
reconnect through the tier with Last-Event-ID + the have-list, the
degraded first connect. frames-live-resume re-pointed to the tier's
haveOf / encodeHaveList; frames-live / frames-live-showing warm the tier.
The coverage closure: server-functions-live-loop.spec (19, client suite)
drives live()'s loop over hand-framed chunk / SSE bodies — first-connect
failure, the fail-fast 4xx set and 408 / 425 / 429 / Retry-After, the
cursor as Last-Event-ID, the online wake, return() mid-connect /
mid-backoff / mid-seed, invoke's signal mid-stream / mid-backoff, no
global EventTarget, live(GET(fn)), the handler's local answers — `live`
per-function branch coverage (audit §2.4 method, client + hydrate) 26/59
sites (91/129 paths) on the base → 57/60 (129/132).
Size (scripts/size, this head vs the base 55eb4c3): frames eager
33,767 / 11,193 → 32,802 / 10,888 (−965 min / −305 br; cap 11.21 → 10.90
KB); page base 112,938 / 36,071 → 112,003 / 35,722 (−349 br; cap 36.09 →
35.74); page live 124,810 / 39,747 → 123,903 / 39,412 (−335 br eager; cap
39.76 → 39.43; the page loads wire.js at its first live() call — total
transfer 40,345 br, +598 against the base); non-SC scenarios 0 / 0; no
cap raised. Measured before writing on edited dist copies through the
harness's bundler: ceiling −1,432 / −472, lean −965 / −310, the carrier
≈ +467 / +162.
Suites: web client 130 / 1,218, hydrate + consistency 89 / 452, server
159 / 1,511, solid 42 / 833, types + test-types clean; harness 500 ×
{3289, 91501} SC arm 0, generic arm (CONSISTENCY_IGNORE=C1,C9,C19,E) 0.
Docs: savings plan §1 wire row built, §3 row C2 landed, §4 chunk table;
residue doc status + §7e.
Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: d9395e3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Summary
Frames savings pass §3 row C2 — the live wire tier, in the
leanform the maintainer ruled, on residue 4's head (55eb4c346, the stack #3867 → #3866 → #3864 → #3863 → #3862 → #3860 → #3849 →next). The live wire moves out of the eager frames client into the sixth lazy chunk,@solidjs/web/frames/wire(frames/src/wire-tier.ts→wire.js, 1,922 min / 934 br):connect): the per-address connection alive()loop's response runs under — the open-frame count its end is judged by (an end with frames open is a death the loop reconnects from; with none a completion), the join of a second loop onto an address already live (one wire per address, the second loop reads the same store),connection.ended/cancel, the SSE reader selection (wire.open(body)for an event stream,ChunkReaderotherwise), proxied so a drained body applies nothing further;cancel), reached frombumpthroughtierLoads.wire?.r?.cancel;have/haveOf, RFC 11 §9.5), fed by the client's#recordHaveat its three sites, withencodeHaveListand theFRAME_HAVE_*literals bundled;resume→{ position: String(version), headers: { "X-Frame-Have": … } }).The eager client keeps:
applyFramesreadingoptions[LIVE_WIRE]as its reader (one line);handle's live arm —prepareTier("wire").then(() => tierLoads.wire.r.connect(…))— awaiting residency before the body is read, so a live connection without the tier cannot happen;onLive() { prepareTier("wire") };resumedelegating.Preload-at-call, as the plan's §1 wire row bounds it:
live()'smakeIterablefiresresponseHandler.onLive()at the call, before its first fetch — the import races only the request; a non-live call never loads the tier. The hook ridesresponseHandler.onLive(the maintainer's adjustment 2 — no newServerFunctionsClientConfigkey; measured ≈ 45 br lighter on the pages).Running number: frames eager 33,767 / 11,193 → 32,802 / 10,888 br — 888 B above the ≤ 10 KB target (was 1,193 B above). What remains of the incremental path is C1 (≈ −440) and the claims chunk (≈ −270).
Not in this PR: the trace update-site wait (landed apart, #3861).
Measured before written
Re-measured on this head (the base had moved by residue 1–4 — the sync loop,
insert-placed fills, the table-owned pending read — so the phase-1 edits were re-derived:.wt-logs/c2b-edit.mjs,c2b-measure.mjs; edited dist copies of the frames client and the sf client throughscripts/size's own bundler). min / br; frames eager, then page base, page live:55eb4c346)del— the ceiling (the wire out whole; unsound alone)lean— the tier owns the connection, the ledger, the resume; the client keeps the arm, the hook, the delegationslean−del)onLivefired at the call)#recordHave×3 forwarding)Net −310 br on frames eager cleared the −200 stop line, so the tier was built. Built: −965 / −305 (+0 min / +5 br over the copy).
Public API changes
Frame.have?(): Record<string, string> | undefinedfrom the publicFrameinterface (frames/src/frame-client.ts). The ledger is the tier'shaveOf(frame)(@internal). The maintainer's adjustment 3 — an approved-class removal.frames-live-resume.specre-pointed.encodeHaveListfromframes/src/frame-transport.tsinto the tier (@internal).decodeHaveListand theFRAME_HAVE_HEADER/FRAME_HAVE_BUDGETconstants stay inframe-transport(they were already off the client entry — residue 1, frames: residue 1 — D list, FRAME_HAVE_* off the client entry, lean re-ask #3863; nothing to re-export).@solidjs/web/frames/wireexport path (the chunk;types/frames/wire-tier.d.ts; every export@internal).ServerFunctionsClientConfig.responseHandler.onLive?(): void— optional; fired bylive()at the call, before its first fetch, per call.ServerFunctionsClientConfigitself gains no key.ServerComponentHandler.onLive(): void(the frames handler's implementation of 4).Behaviour, no surface: while the tier is not resident,
responseHandler.resumeanswersundefined(the ledger lives in the tier) — see the degraded case under Pins.Pins
consistency/tier-wire-preload.spec(new, 3): the hook starts the import synchronously atgetRoom(), before any fetch; the response arm awaits residency (chunks sent, nothing applied, the fallback shown, no status) and the release lands the content;X-Frame-Tiers: wireon a live response joins the same load (the loader called once); a non-live call never loads the tier and its refetch bumps nothing of the tier's; reconnect through the tier carriesLast-Event-IDand the have-list ({ "": root, "lh:1": hole }), morphs in place, completes toclosed; the degraded first connect, pinned as accepted: content a plain call applied with digests while the tier was gated → the live call's first connect carries neitherX-Frame-HavenorLast-Event-ID→ full snapshot; the release lands the live root; the reconnect after a death carries both from the live root's ledger.Every live suite green through the tier:
frames-live-resumere-pointed to the tier'shaveOf/encodeHaveList;frames-liveandframes-live-showingwarm it (beforeAll(() => prepareTier("wire"))— resident cells);frames-get,frames-plain-bound, the serverframe-live-*/server-functions-live-*suites unchanged.The coverage closure (
server-functions-live-loop.spec, new, 19, client suite):live()'s loop driven from the consumer's side over hand-framed bodies — the codec's chunk stream, and the same payloads as server-sent events withid:lines — ending as each test says (cleanly / dropping / hanging until the request's signal): the non-server-function throw; a handler withoutintercept(andonLivefired before the fetch); a first-connect failure as the call's rejection (no status, no retry); a death reconnecting on theonlinewake, the reader's cursor riding back asLast-Event-ID; the fail-fast 4xx closing with the error; 408 / 425 / 429 retrying;Retry-Afternaming the wait;return()mid-connect (the arriving stream closed, the wire severed) / mid-backoff (the sleep cut) / mid-seed (a late local answer not yielded);invoke's signal mid-stream (a rejection, ended for good) / mid-backoff (the sleep woken, the connect refused); a host without a globalEventTarget;live(GET(fn))at the live address over GET; the handler's local answers (a hit at the call yielded first and adopted; a deferred hit awaited, settling to a value / to nothing; a miss at the call but a hit at connect — one value, no wire).live's per-function branch coverage, counted as the audit did (§2.4: v8 coverage over the client suite + the hydrate suite, merged;server-functions/src/client.ts, the functionlive):55eb4c346(its own tests)The three left:
return()landing between a value's arrival and its read (L1593), and theresume/ seed arms' stopped-while-pending elses (L1527, L1579's implicit else). The brief's "22/61 → ≥ 45/61": the audit's 22/61 was counted on an older head whoselivehad 61 sites; by sites on this head the closure is 26 → 57 of 60.Suites: web client 130 / 1,218; hydrate + consistency 89 / 452; server 159 / 1,511; solid 42 / 833;
types+test-typesclean. Harness:CONSISTENCY_CASES=500× seeds {3289, 91501}: SC arm 0 findings; generic arm (CONSISTENCY_IGNORE=C1,C9,C19,E) 0 findings.Size
scripts/sizeon this head (freshturbo --forcebuild, native compiler built fresh), vs the base55eb4c346and vsnext@9d89df731(unchanged since int3's fresh measurement). min / br:nextwire.js1,922 min / 934 br (the plan'sest.≈ 1,300 / 470 priced the dispatch; the tier carries the whole connection). The other five lazy chunks unchanged.wire.jsat its firstlive()call, so its total transfer is 39,412 + 934 = 40,345 br against the base's 39,747 (+598) — the chunk's own weight plus the carrier, the price of the eager −335 and of the frames-eager −305 on pages that never go live.scenarios.jsledger lines,floor-caps.jsonwith recorded minified); no cap raised (check-floor-caps.mjsagainst the base: clean). The three pre-existing local FAILs (signals: core floor+11 B,+ isPending/latest+23 B,CSR with Show/For+45 B over cap) are byte-identical to every earlier head and tonext— the known local-vs-CI brotli difference, not this PR's.Not done
live's last three branch sites (above) — areturn()race and two stopped-while-pending elses; not reachable deterministically from the consumer's side.leanform drops it with the have-list; a two-lineresumefallback in the client would restore it at ≈ +40 br if wanted.