diff --git a/.changeset/frames-dependency-edges.md b/.changeset/frames-dependency-edges.md
new file mode 100644
index 0000000000..2813c150ee
--- /dev/null
+++ b/.changeset/frames-dependency-edges.md
@@ -0,0 +1,7 @@
+---
+"@solidjs/signals": patch
+"solid-js": patch
+"@solidjs/web": patch
+---
+
+Frames: a signal or store write during deferred hydration mounts only the slot occurrences the server recorded as its readers, then applies, instead of mounting every pending occurrence first. The server names plain signals (`~`) and plain stores (by their root) in server-component renders and emits `sc:deps` records per frame; writes it cannot vouch for (unnamed, derived, optimistic, projection or wide sources) still mount everything first. Apps without server components keep their sizes and code paths.
diff --git a/packages/signals/src/core/core.ts b/packages/signals/src/core/core.ts
index 7f52190034..5e610220f1 100644
--- a/packages/signals/src/core/core.ts
+++ b/packages/signals/src/core/core.ts
@@ -1688,6 +1688,7 @@ export function pullComputed(owner: Computed, c: Computed): void {
}
export function read(el: Signal | Computed): T {
+ if (__DEV__) DEV.hooks.onRead?.(el);
if (__DEV__ && asyncTailFlights !== 0 && !tracking && untrackDepth === 0)
checkPostAwaitRead(
el,
@@ -2029,20 +2030,21 @@ export function captureWriteSnapshot(el: Signal | Computed, current: T)
}
/**
- * A callback run before every signal or store write lands, while set. The
+ * A callback run before every signal or store write lands, while set, with
+ * the written signal or the store proxy whose setter runs. The
* server-components client sets it while slot occurrences are left
* unmounted after hydration (deferred hydration): a write may be one an
* unmounted component would have observed, so the client mounts what is
* left before the write applies, and clears the hook. `null` otherwise.
*/
-export let writeHook: (() => void) | null = null;
+export let writeHook: ((node?: object) => void) | null = null;
/** @internal */
-export function setWriteHook(hook: (() => void) | null): void {
+export function setWriteHook(hook: ((node?: object) => void) | null): void {
writeHook = hook;
}
export function setSignal(el: Signal | Computed, v: T | ((prev: T) => T)): T {
- if (writeHook !== null) writeHook();
+ if (writeHook !== null) writeHook(el);
if (
__DEV__ &&
!(el._config & CONFIG_OWNED_WRITE) &&
diff --git a/packages/signals/src/core/dev.ts b/packages/signals/src/core/dev.ts
index 846debf64a..cae95e5c53 100644
--- a/packages/signals/src/core/dev.ts
+++ b/packages/signals/src/core/dev.ts
@@ -30,6 +30,8 @@ export interface DevHooks {
onGraph?: (value: any, owner: Owner | null) => void;
onUpdate?: () => void;
onStoreNodeUpdate?: (state: any, property: PropertyKey, value: any, prev: any) => void;
+ /** @internal Every node read (the frames client's identity-drift check; `setReadWatch`). */
+ onRead?: (node: object) => void;
}
/**
diff --git a/packages/signals/src/core/identities.ts b/packages/signals/src/core/identities.ts
new file mode 100644
index 0000000000..291aa77363
--- /dev/null
+++ b/packages/signals/src/core/identities.ts
@@ -0,0 +1,64 @@
+import { CONFIG_TRANSPARENT } from "./constants.js";
+import { DEV } from "./dev.js";
+import { getOwner } from "./owner.js";
+import type { Owner } from "./types.js";
+
+// Deferred hydration, dependency edges: plain-signal identities.
+// While on (`setSignalIdentities`; the server-components client turns it on
+// before hydrating), a plain signal created under an id-bearing owner is
+// named `~`, n counting the plain signals (and plain store
+// roots: `createStore` over an object, store/edges.ts) created under that
+// owner in its current pass (transparent owners defer to the nearest
+// id-bearing one, as child ids do). The server names the same signal the
+// same way and restarts the count on every re-run, so an edge it shipped (a
+// slot fill read it) matches the client node; the write hook receives the
+// node and looks its name up (`signalName`). The count restarts when the owner's pass
+// generation (`_depGen`, bumped by every recompute) moves on. Only the
+// frames client imports this module, so nothing here ships without it.
+// Names live in a side table, not on the node: frames reads them across the
+// package boundary, where a mangled field would not survive.
+let counts: WeakMap | null = null;
+const names = new WeakMap
+ );
+}
diff --git a/packages/web/test/hydration/frame-dependency-edges-derived.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-derived.spec.tsx
new file mode 100644
index 0000000000..25496b437e
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-derived.spec.tsx
@@ -0,0 +1,27 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a write the records cannot vouch for —
+// a derived signal (no name) — mounts every occurrence left before it applies, as deferred
+// hydration did before edges.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { ALL, bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, fallback", () => {
+ test("a write to a derived signal (no name) mounts every occurrence left first", () => {
+ const page = bootEdges();
+ shop.setCart(1);
+ expect(page.seen()).toEqual(["cart", "late"]);
+ shop.setDerived(3);
+ expect(page.seen()).toEqual(ALL);
+ flush();
+ expect(page.text("derived")).toBe("derived:3");
+ expect(page.text("item-a")).toBe("a:USD:0:0");
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-drift.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-drift.spec.tsx
new file mode 100644
index 0000000000..d2822a8348
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-drift.spec.tsx
@@ -0,0 +1,28 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half, dev: a client that names a shared signal
+// differently from the server (one extra client-only plain signal ahead of
+// the shared ones in the shell) is warned about when an occurrence mounting
+// reads a shared signal its record does not list.
+import { describe, expect, test } from "vitest";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, identity drift", () => {
+ test("dev: a shared signal named differently on the client is warned about", () => {
+ const page = bootEdges({ drift: true });
+ // The client's `cart` carries the server's name for `currency`.
+ shop.setCart(1);
+ // The wrong occurrences mounted, and the first one to read a shared
+ // signal the server did not record for it says so — once.
+ expect(page.seen()).toContain("item:a");
+ expect(page.seen()).not.toContain("cart");
+ const drift = page.warnings.filter(w => w.includes("the server did not record that read"));
+ expect(drift).toHaveLength(1);
+ expect(drift[0]).toContain('"item#0"');
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-helpers.tsx b/packages/web/test/hydration/frame-dependency-edges-helpers.tsx
new file mode 100644
index 0000000000..886d0d15cb
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-helpers.tsx
@@ -0,0 +1,70 @@
+/**
+ * @jsxImportSource @solidjs/web
+ *
+ * Shared by the frame-dependency-edges*.spec.tsx hydrate specs: boots the
+ * document test/server/frame-dependency-edges.spec.tsx rendered and
+ * hydrates it under deferred hydration. One page per spec file: the frames
+ * client's boundary index and claim set are module state.
+ *
+ * Writes are made synchronously after the boot: the idle pass waits for a
+ * timer, so every occurrence is still unmounted when they land.
+ */
+import { afterAll, beforeAll, vi } from "vitest";
+import { hydrate } from "@solidjs/web";
+import { installServerComponents, setDeferredHydration } from "../../frames/src/client.js";
+import { createServerReference } from "../../server-functions/src/client.js";
+import { makeHost } from "../lifecycle-matrix/harness.js";
+import { ARGS, FID, makeApp, mounted } from "../harness/frame-dependency-edges.jsx";
+import { applyChunk, loadArtifact } from "./frame-live-document-helpers.js";
+
+export const ALL = [
+ "cart",
+ "derived",
+ "dstore",
+ "item:a",
+ "item:b",
+ "item:c",
+ "item:d",
+ "late",
+ "store",
+ "wide"
+];
+
+/** Turn deferred hydration on for the spec file (the suite runs eager). */
+export function deferredHydration() {
+ beforeAll(() => setDeferredHydration(true));
+ afterAll(() => setDeferredHydration(false));
+}
+
+export function bootEdges(options: { drift?: boolean } = {}) {
+ const { shell, rest } = loadArtifact("frame-dependency-edges");
+ (globalThis as any)._$HY = { events: [], completed: new WeakSet(), r: {}, fe() {} };
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ installServerComponents(makeHost().host);
+ vi.stubGlobal("fetch", async () => {
+ throw new Error("unexpected fetch");
+ });
+ const warnings: string[] = [];
+ vi.spyOn(console, "warn").mockImplementation((...args: unknown[]) => {
+ warnings.push(args.map(String).join(" "));
+ });
+ const list = createServerReference(FID);
+ const App = makeApp(() => list(...ARGS), options);
+ applyChunk(container, shell + rest, true);
+ mounted.length = 0;
+ const dispose = hydrate(() => , container);
+ const text = (id: string) => container.querySelector(`#${id}`)!.textContent;
+ /** Mounted occurrences, sorted. */
+ const seen = () => [...mounted].sort();
+ return {
+ container,
+ warnings,
+ text,
+ seen,
+ dispose: () => {
+ dispose();
+ container.remove();
+ }
+ };
+}
diff --git a/packages/web/test/hydration/frame-dependency-edges-local.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-local.spec.tsx
new file mode 100644
index 0000000000..72ba6be6e4
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-local.spec.tsx
@@ -0,0 +1,28 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a write to an occurrence's own state (a
+// named signal and a card-local store no record lists) mounts nothing
+// beyond what the event woke.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, own state", () => {
+ test("a write to an occurrence's own state mounts nothing else", () => {
+ const page = bootEdges();
+ page.container
+ .querySelector("#item-b")!
+ .dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ // The click mounted its occurrence; the handler wrote that occurrence's
+ // counter and its card-local store, which no other occurrence read (`late` mounts on every named
+ // write: the server could not see its reads).
+ expect(page.seen()).toEqual(["item:b", "late"]);
+ flush();
+ expect(page.text("item-b")).toBe("b:USD:1:10");
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-projection.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-projection.spec.tsx
new file mode 100644
index 0000000000..07d1861006
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-projection.spec.tsx
@@ -0,0 +1,27 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a write to a derived store (a projection)
+// carries no name: it mounts every occurrence left before it applies, as
+// deferred hydration did before edges.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { ALL, bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, fallback", () => {
+ test("a write to a projection store mounts every occurrence left first", () => {
+ const page = bootEdges();
+ shop.setCart(1);
+ expect(page.seen()).toEqual(["cart", "late"]);
+ shop.setDStore(s => void (s.v = 2));
+ expect(page.seen()).toEqual(ALL);
+ flush();
+ expect(page.text("dstore")).toBe("dstore:2");
+ expect(page.text("item-a")).toBe("a:USD:0:0");
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-store-unnamed.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-store-unnamed.spec.tsx
new file mode 100644
index 0000000000..b5d7827822
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-store-unnamed.spec.tsx
@@ -0,0 +1,24 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a write to a store with no name (created
+// outside any owner) is one the records cannot vouch for: it mounts every
+// occurrence left before it applies, as deferred hydration did before edges.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { setLoose } from "../harness/frame-dependency-edges.jsx";
+import { ALL, bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, fallback", () => {
+ test("a write to an unnamed store mounts every occurrence left first", () => {
+ const page = bootEdges();
+ setLoose(s => void (s.n = 1));
+ expect(page.seen()).toEqual(ALL);
+ flush();
+ expect(page.text("item-a")).toBe("a:USD:0:0");
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-store.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-store.spec.tsx
new file mode 100644
index 0000000000..291206e7da
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-store.spec.tsx
@@ -0,0 +1,30 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a plain store goes by its root's name. A
+// setter call on it mounts only the unmounted occurrences that read the
+// store (any property, at any depth), before the write applies.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, stores", () => {
+ test("a write to a named store mounts only the occurrences that read it", () => {
+ const page = bootEdges();
+ const before = page.container.querySelector("#store");
+ shop.setStore(s => void (s.n = 1));
+ // The store widget, and `late` (it mounts on every named write); the
+ // items, the projection's reader and the rest stay unmounted.
+ expect(page.seen()).toEqual(["late", "store"]);
+ // Mounted against the server's markup before the write: hydrated in place.
+ expect(page.text("store")).toBe("store:0");
+ flush();
+ expect(page.text("store")).toBe("store:1");
+ expect(page.container.querySelector("#store")).toBe(before);
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges-wide.spec.tsx b/packages/web/test/hydration/frame-dependency-edges-wide.spec.tsx
new file mode 100644
index 0000000000..e6b210715b
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges-wide.spec.tsx
@@ -0,0 +1,27 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half: a write the records cannot vouch for —
+// a wide signal (read by a projection, whose readers are not tracked) — mounts every occurrence left before it applies, as deferred
+// hydration did before edges.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { ALL, bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate, fallback", () => {
+ test("a write to a wide signal (read by a projection, whose readers are not tracked) mounts every occurrence left first", () => {
+ const page = bootEdges();
+ shop.setCart(1);
+ expect(page.seen()).toEqual(["cart", "late"]);
+ shop.setWideSource(2);
+ expect(page.seen()).toEqual(ALL);
+ flush();
+ expect(page.text("wide")).toBe("wide:2");
+ expect(page.text("item-a")).toBe("a:USD:0:0");
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/hydration/frame-dependency-edges.spec.tsx b/packages/web/test/hydration/frame-dependency-edges.spec.tsx
new file mode 100644
index 0000000000..553703838d
--- /dev/null
+++ b/packages/web/test/hydration/frame-dependency-edges.spec.tsx
@@ -0,0 +1,36 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — client half, deferred hydration: a write before the idle
+// pass mounts only the unmounted occurrences the server recorded as reading
+// the written signal, before the write applies. Hydrates the document
+// test/server/frame-dependency-edges.spec.tsx rendered.
+import { describe, expect, test } from "vitest";
+import { flush } from "solid-js";
+import { shop } from "../harness/frame-dependency-edges.jsx";
+import { drain } from "./frame-live-document-helpers.js";
+import { bootEdges, deferredHydration } from "./frame-dependency-edges-helpers.jsx";
+
+deferredHydration();
+
+describe("document face — dependency edges — deferred hydrate", () => {
+ test("a named write mounts only the occurrences that read it", async () => {
+ const page = bootEdges();
+ expect(page.seen()).toEqual([]);
+ shop.setCart(1);
+ // The cart widget, and `late` (the server could not see its reads, so it
+ // mounts on every named write); none of the items.
+ expect(page.seen()).toEqual(["cart", "late"]);
+ flush();
+ expect(page.text("cart")).toBe("cart:1");
+ shop.setCurrency("EUR");
+ // Every item reads the currency; the rest stays unmounted.
+ expect(page.seen()).toEqual(["cart", "item:a", "item:b", "item:c", "item:d", "late"]);
+ flush();
+ expect(page.text("item-c")).toBe("c:EUR:0:0");
+ expect(page.text("store")).toBe("store:0");
+ await drain();
+ expect(page.warnings).toEqual([]);
+ page.dispose();
+ });
+});
diff --git a/packages/web/test/server/frame-dependency-edges.spec.tsx b/packages/web/test/server/frame-dependency-edges.spec.tsx
new file mode 100644
index 0000000000..3d05d2dcfe
--- /dev/null
+++ b/packages/web/test/server/frame-dependency-edges.spec.tsx
@@ -0,0 +1,58 @@
+/**
+ * @jsxImportSource @solidjs/web
+ */
+// Dependency edges — server half. Each document frame ships which slot
+// occurrences read which plain signals they did not create, as a per-frame
+// aggregate (`sc:deps::`, `sid -> "item#0-3"`), plus the signals
+// whose readers it could not track (`*`) and the occurrences with reads it
+// could not see (`!`). Writes the artifact
+// test/hydration/frame-dependency-edges.spec.tsx hydrates.
+import { describe, expect, test } from "vitest";
+import { frameTransformDirectResult, ServerComponentPlugin } from "../../frames/src/frame-sink.js";
+import { ARGS, FID, makeApp, makePageComponent } from "../harness/frame-dependency-edges.jsx";
+import { recordStream, writeArtifact } from "./artifact-recorder.js";
+
+/** The frame's `sc:deps` records, in order. */
+function depsRecords(html: string, site: string): Record[] {
+ const key = `_$HY.r["sc:deps:${site}:`;
+ const out: Record[] = [];
+ for (let at = html.indexOf(key); at >= 0; at = html.indexOf(key, at + 1)) {
+ const open = html.indexOf("{", at);
+ out.push(JSON.parse(html.slice(open, html.indexOf("}", open) + 1)));
+ }
+ return out;
+}
+
+describe("document face — dependency edges — server render", () => {
+ test("the frame ships its fills' reads of shell signals as one aggregate record", async () => {
+ const Inline = frameTransformDirectResult(makePageComponent(), { id: FID, args: ARGS }) as any;
+ const App = makeApp(() => Promise.resolve(Inline));
+ const { shell, rest } = await recordStream(() => , {
+ plugins: [ServerComponentPlugin]
+ } as any);
+ const records = depsRecords(shell + rest, FID);
+ expect(records.length).toBe(1);
+ const deps = records[0];
+ // The shell's three plain signals and its plain store, named in creation
+ // order under one owner.
+ const named = Object.keys(deps).filter(k => k !== "*" && k !== "!");
+ const owner = named[0].slice(0, named[0].lastIndexOf("~"));
+ const [cart, currency, wideSource, store] = [0, 1, 2, 3].map(n => `${owner}~${n}`);
+ // `currency`: every item (one run); `cart`: the cart widget only (the
+ // late widget reads it through a client-source memo the server never runs).
+ expect(deps[currency]).toBe("item#0-3");
+ expect(deps[cart]).toBe("cart#0");
+ // The store, by its root's name: the store widget read it.
+ expect(deps[store]).toBe("store#0");
+ // A projection read `wideSource`: its readers are not tracked.
+ expect(deps["*"]).toBe(wideSource);
+ expect(deps[wideSource]).toBeUndefined();
+ // The client-source memo's reads are the client's: `late` mounts on any named write.
+ expect(deps["!"]).toBe("late#0");
+ // Own state (the items' click counters and card-local stores) and
+ // unnamed sources (the projection store, the derived signal, the store
+ // created outside any owner) are not edges.
+ expect(named.sort()).toEqual([cart, currency, store].sort());
+ writeArtifact("frame-dependency-edges", { name: "frame-dependency-edges", shell, rest });
+ });
+});
diff --git a/scripts/size/floor-caps.json b/scripts/size/floor-caps.json
index 7dcfa8fa83..1792017253 100644
--- a/scripts/size/floor-caps.json
+++ b/scripts/size/floor-caps.json
@@ -12,12 +12,12 @@
"minified": 53236
},
"page: base server components (hydrating + dynamic + frames + sf reference)": {
- "cap": "33.62 KB",
- "minified": 103298
+ "cap": "34.28 KB",
+ "minified": 105776
},
"page: live server components (base + live/GET + action + isPending/latest)": {
- "cap": "37.78 KB",
- "minified": 116698
+ "cap": "38.57 KB",
+ "minified": 119182
},
"server: floor (getRequestEvent + isServer)": {
"cap": "1.34 KB",
diff --git a/scripts/size/scenarios.js b/scripts/size/scenarios.js
index f8e9776f15..4e14d789e8 100644
--- a/scripts/size/scenarios.js
+++ b/scripts/size/scenarios.js
@@ -4563,8 +4563,25 @@ module.exports = [
// measured + 10 B rounded up to 0.01 KB.
// record-only writes mount from the last slot walk (#4139, 2026-10-18): 12.25 -> 12.37 KB, measured locally at 12,359 B brotli / 37,310 B minified (+465 B minified over the recorded size): a write carrying only slot records mounts the occurrences it names from the frame's cached walk instead of walking the frame again (eager hydration of a 400-slot page walked it 401 times). Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 12.37 KB -> 12.46 KB, measured locally at 12,445 B brotli / 37,516 B minified (+206 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
- limit: "12.46 KB",
- capMinified: 37516,
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 12.25 -> 12.80 KB, measured locally (linux) at 12,784 B
+ // brotli / 38,793 B minified against next's 12,235 / 36,845 (+549 B; +1948
+ // B minified). Deferred hydration mounts only the slot occurrences the
+ // server recorded as readers of the written signal or store (an index built
+ // from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 12.80 KB -> 12.99 KB, measured locally (linux) at 12,980 B brotli / 39,466 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
+ limit: "12.99 KB",
+ capMinified: 39466,
alias: framesAlias,
external: framesExternal
},
@@ -4932,6 +4949,23 @@ module.exports = [
// slot records mounts the occurrences it names from the frame's cached
// walk instead of walking the frame again. Accepted by the maintainer.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 33.57 KB -> 33.62 KB (floor-caps.json), measured locally at 33,608 B brotli / 103,298 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 33.46 -> 34.08 KB, measured locally (linux) at 34,065 B
+ // brotli / 105,105 B minified against next's 33,441 / 102,629 (+624 B;
+ // +2476 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 34.08 KB -> 34.28 KB (floor-caps.json), measured locally (linux) at 34,270 B brotli / 105,776 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
limit: floorCaps["page: base server components (hydrating + dynamic + frames + sf reference)"],
capMinified:
floorMinified["page: base server components (hydrating + dynamic + frames + sf reference)"],
@@ -5246,6 +5280,23 @@ module.exports = [
// brotli / 116,502 B minified (+465 B minified). Accepted by the
// maintainer.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 37.67 KB -> 37.78 KB (floor-caps.json), measured locally at 37,768 B brotli / 116,698 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 37.58 -> 38.34 KB, measured locally (linux) at 38,322 B
+ // brotli / 118,464 B minified against next's 37,565 / 115,982 (+757 B;
+ // +2482 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 38.34 KB -> 38.57 KB (floor-caps.json), measured locally (linux) at 38,560 B brotli / 119,182 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
limit: floorCaps["page: live server components (base + live/GET + action + isPending/latest)"],
capMinified:
floorMinified["page: live server components (base + live/GET + action + isPending/latest)"],
@@ -5378,8 +5429,25 @@ module.exports = [
// measured + 10 B rounded up to 0.01 KB.
// record-only writes mount from the last slot walk (#4139, 2026-10-18): 35.42 -> 35.53 KB, measured locally at 35,517 B brotli / 109,446 B minified (+473 B minified over the recorded size): a write carrying only slot records mounts the occurrences it names from the frame's cached walk instead of walking the frame again (eager hydration of a 400-slot page walked it 401 times). Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 35.53 KB -> 35.58 KB, measured locally at 35,569 B brotli / 109,642 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
- limit: "35.58 KB",
- capMinified: 109642,
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 35.42 -> 36.10 KB, measured locally (linux) at 36,087 B
+ // brotli / 111,454 B minified against next's 35,408 / 108,973 (+679 B;
+ // +2481 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 36.10 KB -> 36.42 KB, measured locally (linux) at 36,407 B brotli / 112,125 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
+ limit: "36.42 KB",
+ capMinified: 112125,
alias: pageAlias
},
{
@@ -5481,8 +5549,25 @@ module.exports = [
// the maintainer (2026-10-17).
// record-only writes mount from the last slot walk (#4139, 2026-10-18): 41.48 -> 41.64 KB, measured locally at 41,628 B brotli / 124,449 B minified (+523 B minified over the recorded size): a write carrying only slot records mounts the occurrences it names from the frame's cached walk instead of walking the frame again (eager hydration of a 400-slot page walked it 401 times). Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 41.64 KB -> 41.66 KB, measured locally at 41,649 B brotli / 124,645 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
- limit: "41.66 KB",
- capMinified: 124645,
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 41.48 -> 42.18 KB, measured locally (linux) at 42,169 B
+ // brotli / 126,460 B minified against next's 41,439 / 123,929 (+730 B;
+ // +2531 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 42.18 KB -> 42.39 KB, measured locally (linux) at 42,375 B brotli / 127,178 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
+ limit: "42.39 KB",
+ capMinified: 127178,
alias: pageAlias
},
{
@@ -5654,8 +5739,25 @@ module.exports = [
// raised.
// record-only writes mount from the last slot walk (#4139, 2026-10-18): 40.90 -> 40.95 KB, measured locally at 40,937 B brotli / 126,936 B minified (+476 B minified over the recorded size): a write carrying only slot records mounts the occurrences it names from the frame's cached walk instead of walking the frame again (eager hydration of a 400-slot page walked it 401 times). Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 40.95 KB -> 41.04 KB, measured locally at 41,022 B brotli / 127,132 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
- limit: "41.04 KB",
- capMinified: 127132,
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 40.90 -> 41.66 KB, measured locally (linux) at 41,647 B
+ // brotli / 128,940 B minified against next's 40,817 / 126,463 (+830 B;
+ // +2477 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 41.66 KB -> 41.81 KB, measured locally (linux) at 41,799 B brotli / 129,611 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
+ limit: "41.81 KB",
+ capMinified: 129611,
alias: pageAlias,
conditions: solidConditions,
compile: { hydratable: true }
@@ -5803,8 +5905,25 @@ module.exports = [
// (2026-10-17).
// record-only writes mount from the last slot walk (#4139, 2026-10-18): 47.05 -> 47.19 KB, measured locally at 47,173 B brotli / 141,725 B minified (+470 B minified over the recorded size): a write carrying only slot records mounts the occurrences it names from the frame's cached walk instead of walking the frame again (eager hydration of a 400-slot page walked it 401 times). Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
// Flush content passes walk only non-slot keys (#4139, 2026-10-18): 47.19 KB -> 47.23 KB, measured locally at 47,220 B brotli / 141,921 B minified (+196 B minified over the recorded size): a document's slot record writes no longer each enumerate the whole record store; shop page JS at 4x, eager 564 -> 361 ms, deferred 516 -> 375. Accepted by the maintainer (2026-10-18). Cap at measured + 10 B rounded up to 0.01 KB.
- limit: "47.23 KB",
- capMinified: 141921,
+ // Size-Exception (dependency edges for deferred hydration, 2026-10-17, over
+ // next @ fb498491): 47.02 -> 47.74 KB, measured locally (linux) at 47,722 B
+ // brotli / 143,747 B minified against next's 47,006 / 141,205 (+716 B;
+ // +2542 B minified). Deferred hydration mounts only the slot occurrences
+ // the server recorded as readers of the written signal or store (an index
+ // built from the per-frame `sc:deps` records), falling back to mounting
+ // everything on an unnamed, wide or unrecorded write; a dev-only check
+ // warns when a mount reads a signal its record lacks. The plain-signal
+ // namer (`~`, restarted on each owner pass) ships with the
+ // frames client, which installs it into a null-checked slot in
+ // createSignal; a plain store goes by its root's name on the same count
+ // (createStore calls the same slot, and the write hook resolves a store
+ // proxy or property node to it); signals and app scenarios keep their
+ // minified size, except the compiled hydrating app (+3 B: a store setter
+ // hands its proxy to the write hook). Cap at measured + 10 B rounded up to
+ // 0.01 KB; recorded minified = measured.
+ // Re-measured after merging next @ ed51af36 (#4139 landed first): 47.74 KB -> 47.97 KB, measured locally (linux) at 47,954 B brotli / 144,465 B minified; the edges growth over that next is unchanged in kind. Accepted by the maintainer (2026-10-19). Cap at measured + 10 B rounded up to 0.01 KB; recorded minified = measured.
+ limit: "47.97 KB",
+ capMinified: 144465,
alias: pageAlias,
conditions: solidConditions,
compile: { hydratable: true }