Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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-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.
2 changes: 1 addition & 1 deletion documentation/plans/frames-savings-pass.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ exists to undo another mechanism's consequences; deletes with its cause.
| 16 | `#refArgsUnchanged` value-compare | A5 | **Done (Stage 2):** the #547 `$frame`-addition leniency deleted with unified records; the plain value-compare stays (it is the dedupe, not the patch). |
| 17 | `$ref`/`$frame` arg resolution + per-stream tables | A1/A3 | Derived; table scoping revisited under per-address stores (§5.2). |
| 18 | Region discovery from markup (`#discoverRegions`) | A5 | **Done (Stage 2, first half):** with A5, used regions have records on every transport; discovery remains only as claim wiring — and membership is now structural (outermost dotted id in this interior), not producer-prefix-matched, so address-keyed mounts adopt fn-id-prefixed markup. |
| 19 | Region bind/rebind/`renameRegion` (wire-id renames) | A3 | Compensatory: regions become store substructure keyed `(parent address, occurrence, arg)` (§5.3); wire-relative renames delete. |
| 19 | Region bind/rebind/`renameRegion` (wire-id renames) | A3 | Compensatory: regions become store substructure keyed `(parent address, occurrence, arg)` (§5.3); wire-relative renames delete **with that normalization — not before it.** Frames savings pass C4 (2026-10-06) found the rename LIVE, not dead: a single-flight response renders a shown boundary's regions under the call's address while a direct response and the document render them under the function id, so a flight refresh renames every region of the boundary (`preview`'s region check anticipates it; `lifecycle-matrix/call-driven-args` › regions and `frames-optimistic-hold` pin the rebind). C4 moved bind / rename into the regions tier chunk (`@solidjs/web/frames/regions`: `bind`, `rename`, the rename arm of `resolve`) — 0 eager bytes — and left the deletion to S7's store-boundary normalization. |
| 20 | `hy.r` occlusion absorption (adopt-time fake chunks) | A5/A6 | Compensatory. Deletes: occluded content is ordinary records in the one buffer, drained by the one consumer (DR-4). |
| 21 | Segment reveal + placeholder discovery (`#revealSegment`) | A6 | Derived — and becomes the only implementation (DR-4). |
| 22 | Stylesheet gating + modulepreload | A6/L1 | Derived — unchanged, one instance instead of two. |
Expand Down
46 changes: 31 additions & 15 deletions packages/web/frames/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import {
createFrameHost,
FRAME_ID_ATTR,
prepareTier,
tierLoaders
tierLoaders,
type TierModule
} from "./frame-client.js";
import {
COMPONENT_BINDING,
Expand Down Expand Up @@ -72,6 +73,15 @@ import { createLoadingBoundary, sharedConfig } from "solid-js/internal";
// in the lazy codec chunk.
const TRACE_STATE = Symbol.for("solid.container-trace-state");
tierLoaders.trace = () => import("@solidjs/web/frames/trace");
// The regions tier (frames savings pass §3 row C4): nested server-content
// regions — `{$frame}` slot args resolved to region elements with frames
// bound over them — as the chunk `@solidjs/web/frames/regions`
// (regions-tier.ts). The server announces `regions` where it mints one; a
// record naming a region met while the tier is absent waits for it
// (frame-client.ts, `needsRegions`). This entry keeps the document face's
// `sc:region:` drain (below): an occluded region's html lands in the
// store regardless, and the frame the tier binds on install seeds from it.
tierLoaders.regions = () => import("@solidjs/web/frames/regions");

// Build-time literal (see diagnostics.ts): dev-only guidance folds out of prod.
const IS_DEV = "_SOLID_DEV_" as unknown as boolean;
Expand Down Expand Up @@ -112,6 +122,8 @@ export {
createFrameElement,
FRAME_APPLIED_EVENT
} from "./frame-client.js";
// The shape a tier loader resolves (`InstallOptions.tiers`); type-only.
export type { TierModule } from "./frame-client.js";
export {
FRAME_STREAM_HEADER,
FRAME_HAVE_HEADER,
Expand Down Expand Up @@ -1647,14 +1659,16 @@ function adoptBoundary(
*/
export interface InstallOptions {
/**
* Frames-client tiers by name → loader. A tier's module exports
* `install()`, called once the import resolves; every live frame is then
* flushed so what the tier makes applicable applies (a held occurrence
* mounts). A name with no loader is resident (eager); `trace` has a
* built-in loader (`@solidjs/web/frames/trace`) that an entry here
* replaces. See `installServerComponents`.
* Frames-client tiers by name → loader. A tier's module's exports are its
* appliers (the runtime dispatches to them once resident) and its
* optional `install()` is called once the import resolves; every live
* frame is then flushed so what the tier makes applicable applies (a held
* occurrence mounts). A name with no loader is resident (eager); `trace`
* (`@solidjs/web/frames/trace`) and `regions` (`@solidjs/web/frames/regions`)
* have built-in loaders that an entry here replaces. See
* `installServerComponents`.
*/
tiers?: Record<string, () => Promise<{ install?(): void }>>;
tiers?: Record<string, () => Promise<TierModule>>;
}

/**
Expand All @@ -1674,13 +1688,15 @@ export interface InstallOptions {
* call again to rebind to a custom host.
*
* `options.tiers` maps a frames-client tier's name to its loader (`() =>
* import(...)`, the module exporting `install()`): the client resolves
* tier chunks itself, so the server announces NAMES only
* (`_$HY.r["sc:tiers"]`, `X-Frame-Tiers`) and the loads start here from
* the document's record — the `modulepreload` the document may also carry
* made the fetch warm. The built-in table carries `trace` (the container
* tier's client half, `@solidjs/web/frames/trace`); a loader given here
* for a name replaces the built-in one (tests gate a tier's load this way).
* import(...)`, the module whose exports are the tier's appliers, with an
* optional `install()`): the client resolves tier chunks itself, so the
* server announces NAMES only (`_$HY.r["sc:tiers"]`, `X-Frame-Tiers`) and
* the loads start here from the document's record — the `modulepreload`
* the document may also carry made the fetch warm. The built-in table
* carries `trace` (the container tier's client half,
* `@solidjs/web/frames/trace`) and `regions` (nested server-content
* regions, `@solidjs/web/frames/regions`); a loader given here for a name
* replaces the built-in one (tests gate a tier's load this way).
* @experimental
*/
export function installServerComponents(host: any = getFrameHost(), options?: InstallOptions) {
Expand Down
Loading