@ogygia/core 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LateIsland.svelte +3 -2
- package/dist/LateIslandAwait.svelte +6 -2
- package/dist/Region.svelte +22 -33
- package/dist/boundary.d.ts +0 -2
- package/dist/boundary.js +8 -6
- package/dist/compiler/debarrel/barrel.d.ts +15 -8
- package/dist/compiler/debarrel/barrel.js +92 -45
- package/dist/compiler/debarrel/plugin.js +41 -17
- package/dist/compiler/dev/dev-hmr.d.ts +3 -0
- package/dist/compiler/dev/dev-hmr.js +3 -0
- package/dist/compiler/driver.js +11 -4
- package/dist/compiler/flags.js +21 -7
- package/dist/compiler/fouc-css.js +5 -1
- package/dist/compiler/kit.js +5 -1
- package/dist/compiler/link/build-output.js +12 -3
- package/dist/compiler/macros/bake.js +18 -4
- package/dist/compiler/macros/dollar.js +6 -1
- package/dist/compiler/macros/source.js +6 -1
- package/dist/compiler/macros/store.js +10 -2
- package/dist/compiler/macros/wire.js +6 -1
- package/dist/compiler/parse/scan.js +10 -3
- package/dist/compiler/region/transform.js +188 -74
- package/dist/context-bridge.js +13 -5
- package/dist/context.d.ts +9 -0
- package/dist/context.js +30 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.js +7 -1
- package/dist/early-holes-head.d.ts +5 -0
- package/dist/early-holes-head.js +20 -0
- package/dist/early-holes.d.ts +34 -0
- package/dist/early-holes.js +51 -0
- package/dist/entry-hydrate.js +9 -12
- package/dist/events/bus.js +7 -2
- package/dist/events/schema.d.ts +2 -2
- package/dist/fn-transport.js +4 -3
- package/dist/hooks.js +94 -163
- package/dist/insights/page-assets.js +11 -3
- package/dist/insights/page-insights.d.ts +0 -2
- package/dist/insights/page-insights.js +14 -10
- package/dist/insights/session-insights.js +2 -2
- package/dist/internal-instruments.d.ts +3 -1
- package/dist/internal-instruments.js +3 -1
- package/dist/internal-server.d.ts +3 -2
- package/dist/internal-server.js +3 -2
- package/dist/internal.d.ts +1 -2
- package/dist/internal.js +1 -2
- package/dist/keep-fallback.d.ts +0 -2
- package/dist/keep-fallback.js +4 -5
- package/dist/live-transport.js +3 -2
- package/dist/messages/build.d.ts +585 -0
- package/dist/messages/build.js +800 -0
- package/dist/messages/core.d.ts +464 -0
- package/dist/messages/core.js +843 -0
- package/dist/messages/define.d.ts +62 -0
- package/dist/messages/define.js +49 -0
- package/dist/messages/index.d.ts +6 -0
- package/dist/messages/index.js +6 -0
- package/dist/messages/island.d.ts +4 -0
- package/dist/messages/island.js +34 -0
- package/dist/messages/lazy.d.ts +3 -0
- package/dist/messages/lazy.js +22 -0
- package/dist/messages/prose.d.ts +15 -0
- package/dist/messages/prose.js +43 -0
- package/dist/messages/server.d.ts +142 -0
- package/dist/messages/server.js +246 -0
- package/dist/nested.d.ts +3 -0
- package/dist/nested.js +8 -1
- package/dist/plugin/css.js +14 -13
- package/dist/plugin/facts.d.ts +2 -0
- package/dist/plugin/facts.js +10 -6
- package/dist/plugin/hooks.d.ts +7 -17
- package/dist/plugin/hooks.js +32 -4
- package/dist/plugin/index.d.ts +9 -2
- package/dist/plugin/request-scope.d.ts +20 -0
- package/dist/plugin/request-scope.js +149 -0
- package/dist/plugin/server.d.ts +20 -2
- package/dist/plugin/server.js +98 -28
- package/dist/plugin/testing.d.ts +6 -0
- package/dist/plugin/testing.js +14 -2
- package/dist/policies.d.ts +21 -0
- package/dist/policies.js +66 -0
- package/dist/preference.js +6 -1
- package/dist/preload.js +6 -9
- package/dist/ref-watch.d.ts +0 -10
- package/dist/ref-watch.js +14 -1
- package/dist/ref.js +17 -5
- package/dist/region-snippet.js +9 -3
- package/dist/region.js +2 -1
- package/dist/report-thrown.d.ts +9 -0
- package/dist/report-thrown.js +15 -0
- package/dist/rules.d.ts +17 -0
- package/dist/rules.js +57 -0
- package/dist/runtime/beacon.d.ts +0 -4
- package/dist/runtime/beacon.js +58 -43
- package/dist/runtime/boot-link.js +19 -2
- package/dist/runtime/client-hooks.js +2 -1
- package/dist/runtime/concurrency.d.ts +16 -5
- package/dist/runtime/concurrency.js +123 -7
- package/dist/runtime/core.d.ts +3 -1
- package/dist/runtime/core.js +718 -384
- package/dist/runtime/devalue-lane.d.ts +13 -0
- package/dist/runtime/devalue-lane.js +49 -0
- package/dist/runtime/disclose-version.d.ts +1 -0
- package/dist/runtime/disclose-version.js +14 -0
- package/dist/runtime/early-holes.d.ts +4 -0
- package/dist/runtime/early-holes.js +40 -0
- package/dist/runtime/entry-locations.js +40 -49
- package/dist/runtime/foreign-props.d.ts +0 -7
- package/dist/runtime/foreign-props.js +2 -1
- package/dist/runtime/form-continuity.d.ts +2 -2
- package/dist/runtime/form-continuity.js +30 -8
- package/dist/runtime/frame-nav.d.ts +7 -1
- package/dist/runtime/frame-nav.js +84 -18
- package/dist/runtime/frame-store.d.ts +25 -16
- package/dist/runtime/frame-store.js +124 -98
- package/dist/runtime/frames.js +8 -4
- package/dist/runtime/hole-facts.js +6 -7
- package/dist/runtime/hole-fetch.d.ts +17 -0
- package/dist/runtime/hole-fetch.js +47 -0
- package/dist/runtime/hydrate-core.d.ts +4 -4
- package/dist/runtime/hydrate-core.js +80 -64
- package/dist/runtime/import-again.d.ts +33 -0
- package/dist/runtime/import-again.js +217 -0
- package/dist/runtime/interaction-replay.d.ts +5 -3
- package/dist/runtime/interaction-replay.js +6 -6
- package/dist/runtime/interaction.d.ts +14 -3
- package/dist/runtime/interaction.js +46 -23
- package/dist/runtime/kit-page-thread.svelte.d.ts +2 -1
- package/dist/runtime/kit-page-thread.svelte.js +44 -20
- package/dist/runtime/lakes.d.ts +1 -1
- package/dist/runtime/lakes.js +18 -5
- package/dist/runtime/live.d.ts +1 -2
- package/dist/runtime/live.js +6 -8
- package/dist/runtime/load-scheduler.d.ts +29 -18
- package/dist/runtime/load-scheduler.js +236 -136
- package/dist/runtime/nav-handle.js +25 -6
- package/dist/runtime/navigation.d.ts +24 -0
- package/dist/runtime/navigation.js +24 -0
- package/dist/runtime/ownership.d.ts +1 -1
- package/dist/runtime/page-defer.js +3 -3
- package/dist/runtime/parse-html.d.ts +15 -4
- package/dist/runtime/parse-html.js +24 -5
- package/dist/runtime/pushable-props.svelte.d.ts +21 -0
- package/dist/runtime/pushable-props.svelte.js +37 -0
- package/dist/runtime/region-endpoint-url.js +4 -6
- package/dist/runtime/region-machines.d.ts +111 -0
- package/dist/runtime/region-machines.js +97 -0
- package/dist/runtime/region-sheets.d.ts +31 -0
- package/dist/runtime/region-sheets.js +49 -0
- package/dist/runtime/restore-report.d.ts +1 -0
- package/dist/runtime/restore-report.js +36 -0
- package/dist/runtime/restore.d.ts +5 -2
- package/dist/runtime/restore.js +33 -14
- package/dist/runtime/router-nav.d.ts +32 -12
- package/dist/runtime/router-nav.js +320 -228
- package/dist/runtime/router.d.ts +83 -19
- package/dist/runtime/router.js +297 -129
- package/dist/runtime/rules.d.ts +35 -0
- package/dist/runtime/rules.js +137 -0
- package/dist/runtime/schedule.d.ts +6 -3
- package/dist/runtime/schedule.js +172 -57
- package/dist/runtime/seeds.d.ts +5 -1
- package/dist/runtime/seeds.js +11 -5
- package/dist/runtime/session.d.ts +46 -7
- package/dist/runtime/session.js +78 -10
- package/dist/runtime/slots.d.ts +53 -20
- package/dist/runtime/slots.js +2 -3
- package/dist/runtime/soft-refresh.d.ts +16 -0
- package/dist/runtime/soft-refresh.js +14 -0
- package/dist/runtime/svelte-warnings.d.ts +4 -10
- package/dist/runtime/svelte-warnings.js +7 -4
- package/dist/runtime/wire-classes.js +21 -8
- package/dist/runtime/wire-format.js +6 -3
- package/dist/script.js +8 -7
- package/dist/seed-ref-reviver.d.ts +0 -9
- package/dist/seed-ref-reviver.js +2 -1
- package/dist/seed-refs.d.ts +46 -36
- package/dist/seed-refs.js +131 -92
- package/dist/server/document-tail.d.ts +6 -0
- package/dist/server/document-tail.js +7 -1
- package/dist/server/early-holes-csp.d.ts +14 -0
- package/dist/server/early-holes-csp.js +73 -0
- package/dist/server/page-facts.d.ts +4 -1
- package/dist/server/page-facts.js +4 -1
- package/dist/server/page-seed.d.ts +2 -2
- package/dist/server/page-seed.js +104 -11
- package/dist/server/page-stream.d.ts +14 -1
- package/dist/server/page-stream.js +119 -2
- package/dist/server/personal-response.d.ts +0 -5
- package/dist/server/personal-response.js +6 -2
- package/dist/server/props-wire.js +5 -1
- package/dist/server/region-endpoint.js +10 -2
- package/dist/server/render-cache.d.ts +24 -20
- package/dist/server/render-cache.js +99 -40
- package/dist/server/render-page.js +46 -15
- package/dist/server/request-stats.d.ts +10 -8
- package/dist/server/request-stats.js +20 -12
- package/dist/server/rules.d.ts +11 -0
- package/dist/server/rules.js +64 -0
- package/dist/server/signing-key.js +31 -18
- package/dist/server/stream-regions.d.ts +14 -1
- package/dist/server/stream-regions.js +38 -2
- package/dist/shared-state.js +5 -1
- package/dist/shims/app-navigation.js +3 -2
- package/dist/shims/page-store.svelte.js +16 -2
- package/dist/source.js +7 -2
- package/dist/store-transport.js +12 -5
- package/dist/transport.js +3 -2
- package/dist/vite/index.js +83 -74
- package/dist/vite/kit-dirs.js +7 -1
- package/dist/vite/options.d.ts +2 -1
- package/dist/vite/options.js +11 -20
- package/dist/vite/package-files.js +13 -3
- package/dist/vite/paths.d.ts +2 -0
- package/dist/vite/paths.js +4 -4
- package/dist/vite/plugin-api.d.ts +6 -5
- package/dist/vite/plugin-api.js +42 -14
- package/dist/vite/style-preprocess.js +10 -3
- package/package.json +8 -1
- package/dist/KeepHost.svelte +0 -19
- package/dist/KeepHost.svelte.d.ts +0 -10
- package/dist/LiveHost.svelte +0 -19
- package/dist/LiveHost.svelte.d.ts +0 -10
- package/dist/NestedProvider.svelte +0 -13
- package/dist/NestedProvider.svelte.d.ts +0 -8
- package/dist/runtime/page-cache.d.ts +0 -23
- package/dist/runtime/page-cache.js +0 -74
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Rules } from "../rules.js";
|
|
2
|
+
import { warn } from "../messages/core.js";
|
|
3
|
+
import { STALE, observe } from "@ogygia/machine";
|
|
4
|
+
// src/runtime/rules.ts
|
|
5
|
+
/**
|
|
6
|
+
* THE RUNTIME'S RULES (#313, ../rules.ts): what is always true of a page, checked in dev a task
|
|
7
|
+
* after machine moves (a navigation is the navigation machine's) and by e2e at the end of a test.
|
|
8
|
+
*
|
|
9
|
+
* - `rule_region_mounted_twice`: one mount per region. A region element holds one island at a time,
|
|
10
|
+
* and an island whose element left the page is let go with it.
|
|
11
|
+
* - `rule_address_bar_drift`: the address bar and the router's history model (whose current entry
|
|
12
|
+
* is the page on screen's) agree once no navigation is out.
|
|
13
|
+
* - `rule_old_page_open`: nothing of a page that was left is still open (its visit's scope closed:
|
|
14
|
+
* its listeners, timers, streams).
|
|
15
|
+
* - `rule_failure_uncoded`: no failure leaves ogygia without a code: an uncaught ogygia error names
|
|
16
|
+
* its code's page, and a `STALE` never escapes as a rejection.
|
|
17
|
+
* - `rule_parse_upgraded`: a parsed answer is inert (./parse-html.ts, #382): no element of it was
|
|
18
|
+
* built by a component's constructor before the restore reached it.
|
|
19
|
+
*
|
|
20
|
+
* Each area tells this module what it holds (`note_mount`, `note_visit`, `watch_router`) only when
|
|
21
|
+
* rules are on (`RULES`): every call site folds out of a build without them.
|
|
22
|
+
*/
|
|
23
|
+
/** The runtime's rules. */
|
|
24
|
+
const runtime_rules = /* @__PURE__ */ new Rules();
|
|
25
|
+
/** The region elements that hold an island now, and how many. */
|
|
26
|
+
const mounts = /* @__PURE__ */ new Map();
|
|
27
|
+
/** A region element mounted its island. */
|
|
28
|
+
function note_mount(region) {
|
|
29
|
+
mounts.set(region, (mounts.get(region) ?? 0) + 1);
|
|
30
|
+
}
|
|
31
|
+
/** A region element let its island go. */
|
|
32
|
+
function note_unmount(region) {
|
|
33
|
+
const n = (mounts.get(region) ?? 0) - 1;
|
|
34
|
+
if (n > 0) mounts.set(region, n);
|
|
35
|
+
else mounts.delete(region);
|
|
36
|
+
}
|
|
37
|
+
const entry_of = (region) => region.getAttribute("entry") ?? region.tagName;
|
|
38
|
+
function one_mount_per_region() {
|
|
39
|
+
for (const [region, n] of mounts) {
|
|
40
|
+
if (n > 1) return `${entry_of(region)} holds ${n} islands at once`;
|
|
41
|
+
if (!region.isConnected) return `${entry_of(region)} left the page with its island still mounted`;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Each visit made, in the order made: one older than the page on screen's is over. */
|
|
46
|
+
const visits = [];
|
|
47
|
+
let visit_n = 0;
|
|
48
|
+
let current = null;
|
|
49
|
+
/** A page visit was made (`current`: it is the page on screen's now). */
|
|
50
|
+
function note_visit(visit, is_current) {
|
|
51
|
+
if (!visits.some((v) => v.visit === visit)) visits.push({
|
|
52
|
+
visit,
|
|
53
|
+
n: ++visit_n
|
|
54
|
+
});
|
|
55
|
+
if (is_current) current = visit;
|
|
56
|
+
}
|
|
57
|
+
function no_old_page_open() {
|
|
58
|
+
const now = visits.find((v) => v.visit === current);
|
|
59
|
+
if (now === void 0) return null;
|
|
60
|
+
for (let i = visits.length - 1; i >= 0; i--) if (visits[i].visit.scope.closed) visits.splice(i, 1);
|
|
61
|
+
const open = visits.filter((v) => v.n < now.n).length;
|
|
62
|
+
return open === 0 ? null : `${open} page visit(s) before the one on screen still open`;
|
|
63
|
+
}
|
|
64
|
+
const no_hash = (href) => href.split("#")[0];
|
|
65
|
+
/** The router started: its history model is checked against the address bar. */
|
|
66
|
+
function watch_router(router) {
|
|
67
|
+
runtime_rules.add("rule_address_bar_drift", () => {
|
|
68
|
+
if (router.nav.now.kind !== "idle") return null;
|
|
69
|
+
const id = history.state?.ogygia;
|
|
70
|
+
if (typeof id !== "string") return null;
|
|
71
|
+
const page = router.history.current;
|
|
72
|
+
if (id !== page.id) return `the address bar is on entry ${id}, the history model on ${page.id}`;
|
|
73
|
+
const bar = no_hash(location.href);
|
|
74
|
+
const model = no_hash(page.url.href);
|
|
75
|
+
return bar === model ? null : `the address bar is ${bar}, the history model's page ${model}`;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const CODE_LINK_RE = /ogygiajs\.dev\/e\/[a-z_]+/;
|
|
79
|
+
/** The failures that left ogygia without a code, as found (a few). */
|
|
80
|
+
const uncoded = [];
|
|
81
|
+
function failure(reason) {
|
|
82
|
+
if (uncoded.length >= 10) return;
|
|
83
|
+
if (reason === STALE) {
|
|
84
|
+
uncoded.push("a STALE escaped as a rejection");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "";
|
|
88
|
+
if (message.startsWith("[ogygia]") && !CODE_LINK_RE.test(message)) uncoded.push("an ogygia error with no code: " + message.split("\n")[0].slice(0, 160));
|
|
89
|
+
}
|
|
90
|
+
const no_failure_uncoded = () => uncoded.length === 0 ? null : uncoded.join("; ");
|
|
91
|
+
/** What parses built that they should not have, as found (a few). */
|
|
92
|
+
const parsed_built = [];
|
|
93
|
+
/** The rule's ear on every parse (./parse-html.ts calls it): an element of the parsed fragment that
|
|
94
|
+
* a component's constructor built (a defined name, an instance of its definition). */
|
|
95
|
+
function note_parse(fragment) {
|
|
96
|
+
if (parsed_built.length >= 10 || typeof customElements === "undefined") return;
|
|
97
|
+
const roots = [fragment];
|
|
98
|
+
for (let q = 0; q < roots.length; q++) {
|
|
99
|
+
const walk = document.createTreeWalker(roots[q], 1);
|
|
100
|
+
for (let n = walk.nextNode(); n; n = walk.nextNode()) {
|
|
101
|
+
if (n.shadowRoot) roots.push(n.shadowRoot);
|
|
102
|
+
const ctor = customElements.get(n.localName);
|
|
103
|
+
if (ctor !== void 0 && n instanceof ctor) {
|
|
104
|
+
parsed_built.push(`<${n.localName}> was built while its answer parsed`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const no_parse_upgraded = () => parsed_built.length === 0 ? null : parsed_built.join("; ");
|
|
111
|
+
let installed = false;
|
|
112
|
+
let pending = false;
|
|
113
|
+
/** Checks every rule a task from now (once, however many moves ask), and says what broke. */
|
|
114
|
+
function check_soon() {
|
|
115
|
+
if (pending) return;
|
|
116
|
+
pending = true;
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
pending = false;
|
|
119
|
+
runtime_rules.say((code, detail) => warn(code, { detail }));
|
|
120
|
+
}, 0);
|
|
121
|
+
}
|
|
122
|
+
/** Rules on: `globalThis.__ogygia_rules()` gives what is broken now (for e2e); in dev they are also
|
|
123
|
+
* checked after machine moves, and what broke is said with its code. */
|
|
124
|
+
function install_rules(dev) {
|
|
125
|
+
if (installed || typeof window === "undefined") return;
|
|
126
|
+
installed = true;
|
|
127
|
+
runtime_rules.add("rule_region_mounted_twice", one_mount_per_region);
|
|
128
|
+
runtime_rules.add("rule_old_page_open", no_old_page_open);
|
|
129
|
+
runtime_rules.add("rule_failure_uncoded", no_failure_uncoded);
|
|
130
|
+
runtime_rules.add("rule_parse_upgraded", no_parse_upgraded);
|
|
131
|
+
globalThis.__og_parsed = note_parse;
|
|
132
|
+
globalThis.__ogygia_rules = () => runtime_rules.broken();
|
|
133
|
+
window.addEventListener("unhandledrejection", (event) => failure(event.reason));
|
|
134
|
+
window.addEventListener("error", (event) => failure(event.error ?? event.message));
|
|
135
|
+
if (dev) observe(check_soon);
|
|
136
|
+
}
|
|
137
|
+
export { install_rules, note_mount, note_unmount, note_visit, runtime_rules, watch_router };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Scope, Stale } from "@ogygia/machine";
|
|
1
2
|
export declare function yield_task(): Promise<void>;
|
|
2
3
|
/**
|
|
3
4
|
* Run `fn` at background priority, off the current task: `scheduler.postTask` at 'background' where
|
|
@@ -21,14 +22,16 @@ export declare function background_start(fn: () => void): void;
|
|
|
21
22
|
* so it waits one task instead. Once resolved it stays resolved: a late region (SPA swap, a hole's
|
|
22
23
|
* answer) passes straight through.
|
|
23
24
|
*/
|
|
24
|
-
export declare function after_document_painted(): Promise<void>;
|
|
25
|
+
export declare function after_document_painted(): Promise<void | Stale>;
|
|
25
26
|
/** Test seam: forget the resolved gate (a test that fakes a fresh document). */
|
|
26
27
|
export declare function reset_document_painted(): void;
|
|
27
28
|
/**
|
|
28
29
|
* Register a region with the scheduler at connect: stamp its order, start its viewport snapshot.
|
|
29
|
-
* Cheap on purpose (a stamp and an `observe`) — connect runs for every region in one task.
|
|
30
|
+
* Cheap on purpose (a stamp and an `observe`) — connect runs for every region in one task. The
|
|
31
|
+
* snapshot is `owner`'s (the region's lifetime in the page): it stops at the region's turn, or when
|
|
32
|
+
* the owner ends.
|
|
30
33
|
*/
|
|
31
|
-
export declare function register_region(el: Element): void;
|
|
34
|
+
export declare function register_region(el: Element, owner: Scope): void;
|
|
32
35
|
/**
|
|
33
36
|
* Drop a region from the scheduler (disconnect): its snapshot stops and any pending turn is handed
|
|
34
37
|
* out at once — the waiting hydrate step then sees `isConnected === false` and stands down (or, if
|
package/dist/runtime/schedule.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { observe } from "./observe.js";
|
|
1
|
+
import { observe as observe$1 } from "./observe.js";
|
|
2
|
+
import { flights, machine } from "@ogygia/machine";
|
|
2
3
|
// src/runtime/schedule.ts
|
|
3
4
|
/**
|
|
4
5
|
* THE HYDRATION SCHEDULER — one island per task, viewport first.
|
|
@@ -28,17 +29,100 @@ import { observe } from "./observe.js";
|
|
|
28
29
|
const SNAPSHOT_WAIT_MS = 48;
|
|
29
30
|
/** How long a ready below-the-fold island holds for a viewport island still loading its module. */
|
|
30
31
|
const HOLD_FOR_VIEWPORT_MS = 1e3;
|
|
31
|
-
|
|
32
|
-
let
|
|
32
|
+
let pending_count = 0;
|
|
33
|
+
let next_seq = 0;
|
|
34
|
+
const heap_vis = [];
|
|
35
|
+
const heap_rest = [];
|
|
36
|
+
const turns_of = /* @__PURE__ */ new Map();
|
|
37
|
+
const unsnapped = [];
|
|
38
|
+
let unsnapped_head = 0;
|
|
39
|
+
/**
|
|
40
|
+
* The drain, as a machine:
|
|
41
|
+
*
|
|
42
|
+
* idle ─ queued ─ idle a drain waits for its task (`yield_task`), then runs
|
|
43
|
+
* ╲ waiting{at} ╱ a drain waits for a time: the snapshot, or a viewport island
|
|
44
|
+
*
|
|
45
|
+
* `waiting` owns its timer (its visit's scope), so leaving it drops the timer: a drain queued
|
|
46
|
+
* meanwhile runs sooner and works out the waits again, and a sooner wait replaces a later one
|
|
47
|
+
* (#236). One state in place of the `draining` flag beside a `wait_timer` and its `wait_at`.
|
|
48
|
+
*/
|
|
49
|
+
const drainer = machine()({
|
|
50
|
+
name: "hydrate-drain",
|
|
51
|
+
initial: "idle",
|
|
52
|
+
steps: {
|
|
53
|
+
idle: ["queued", "waiting"],
|
|
54
|
+
queued: ["idle"],
|
|
55
|
+
waiting: ["queued", "waiting"]
|
|
56
|
+
}
|
|
57
|
+
}).create();
|
|
33
58
|
/** Connect-order stamp per region — document order at upgrade, insertion order after a morph. */
|
|
34
59
|
const order_of = /* @__PURE__ */ new WeakMap();
|
|
35
60
|
let next_order = 0;
|
|
36
61
|
/** The snapshot: intersecting now? Absent = the observer has not reported yet. */
|
|
37
62
|
const in_viewport = /* @__PURE__ */ new WeakMap();
|
|
38
|
-
|
|
63
|
+
/** Each region's viewport snapshot while it runs (closing it stops the snapshot). */
|
|
64
|
+
const snapshot_of = /* @__PURE__ */ new WeakMap();
|
|
39
65
|
/** Regions whose hydrate has STARTED (module in flight) and not yet taken its turn. */
|
|
40
66
|
const loading = /* @__PURE__ */ new Set();
|
|
41
|
-
|
|
67
|
+
/** The loading regions the snapshot says intersect the viewport (what a hold waits for). */
|
|
68
|
+
const loading_vis = /* @__PURE__ */ new Set();
|
|
69
|
+
/** Keep `loading_vis` in step after `el`'s loading state or snapshot changed. */
|
|
70
|
+
function sync_loading(el) {
|
|
71
|
+
if (loading.has(el) && in_viewport.get(el) === true) loading_vis.add(el);
|
|
72
|
+
else loading_vis.delete(el);
|
|
73
|
+
}
|
|
74
|
+
function stop_loading(el) {
|
|
75
|
+
loading_vis.delete(el);
|
|
76
|
+
return loading.delete(el);
|
|
77
|
+
}
|
|
78
|
+
/** Connect order, then request order: the pick order within one heap. */
|
|
79
|
+
const before = (a, b) => a.order < b.order || a.order === b.order && a.seq < b.seq;
|
|
80
|
+
function heap_push(h, t) {
|
|
81
|
+
let i = h.push(t) - 1;
|
|
82
|
+
while (i > 0) {
|
|
83
|
+
const p = i - 1 >> 1;
|
|
84
|
+
if (!before(h[i], h[p])) break;
|
|
85
|
+
[h[i], h[p]] = [h[p], h[i]];
|
|
86
|
+
i = p;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function heap_pop(h) {
|
|
90
|
+
const last = h.pop();
|
|
91
|
+
if (!h.length) return;
|
|
92
|
+
h[0] = last;
|
|
93
|
+
for (let i = 0;;) {
|
|
94
|
+
const l = 2 * i + 1;
|
|
95
|
+
const r = l + 1;
|
|
96
|
+
let m = i;
|
|
97
|
+
if (l < h.length && before(h[l], h[m])) m = l;
|
|
98
|
+
if (r < h.length && before(h[r], h[m])) m = r;
|
|
99
|
+
if (m === i) return;
|
|
100
|
+
[h[i], h[m]] = [h[m], h[i]];
|
|
101
|
+
i = m;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** The heap's first live turn (stale entries on top are dropped on the way). */
|
|
105
|
+
function heap_top(h, vis) {
|
|
106
|
+
while (h.length && (h[0].done || h[0].vis !== vis)) heap_pop(h);
|
|
107
|
+
return h[0];
|
|
108
|
+
}
|
|
109
|
+
/** File each pending turn of `el` in the heap its snapshot now says. */
|
|
110
|
+
function refile(el) {
|
|
111
|
+
const vis = in_viewport.get(el) === true;
|
|
112
|
+
for (const t of turns_of.get(el) ?? []) {
|
|
113
|
+
if (t.vis === vis) continue;
|
|
114
|
+
t.vis = vis;
|
|
115
|
+
heap_push(vis ? heap_vis : heap_rest, t);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** The turn leaves the queue (taken, or its region went away). */
|
|
119
|
+
function retire(t) {
|
|
120
|
+
t.done = true;
|
|
121
|
+
pending_count--;
|
|
122
|
+
const list = turns_of.get(t.el);
|
|
123
|
+
list.splice(list.indexOf(t), 1);
|
|
124
|
+
if (!list.length) turns_of.delete(t.el);
|
|
125
|
+
}
|
|
42
126
|
/**
|
|
43
127
|
* Yield to the event loop: resolves in a NEW task, so whatever ran before is a finished task and
|
|
44
128
|
* the browser may render and handle input in between. `scheduler.yield()` where it exists (it
|
|
@@ -99,7 +183,11 @@ function post_background(fn) {
|
|
|
99
183
|
} catch {}
|
|
100
184
|
setTimeout(fn, 0);
|
|
101
185
|
}
|
|
102
|
-
|
|
186
|
+
/** The gate below, once for the document (a test's reset drops it). */
|
|
187
|
+
const document_painted = flights({
|
|
188
|
+
name: "document painted",
|
|
189
|
+
run: () => until_painted()
|
|
190
|
+
});
|
|
103
191
|
/**
|
|
104
192
|
* Resolves once the document is parsed AND painted: after `DOMContentLoaded`, then one rendered
|
|
105
193
|
* frame. This is the moment SvelteKit starts hydrating (its start runs from a deferred module
|
|
@@ -115,7 +203,10 @@ let document_painted = null;
|
|
|
115
203
|
* answer) passes straight through.
|
|
116
204
|
*/
|
|
117
205
|
function after_document_painted() {
|
|
118
|
-
return document_painted
|
|
206
|
+
return document_painted.get();
|
|
207
|
+
}
|
|
208
|
+
function until_painted() {
|
|
209
|
+
return new Promise((resolve) => {
|
|
119
210
|
if (typeof document === "undefined") return resolve();
|
|
120
211
|
const painted = () => {
|
|
121
212
|
if (document.visibilityState === "hidden" || typeof requestAnimationFrame !== "function") setTimeout(resolve, 0);
|
|
@@ -142,19 +233,27 @@ function dom_content_loaded() {
|
|
|
142
233
|
}
|
|
143
234
|
/** Test seam: forget the resolved gate (a test that fakes a fresh document). */
|
|
144
235
|
function reset_document_painted() {
|
|
145
|
-
document_painted
|
|
236
|
+
document_painted.drop(() => true);
|
|
146
237
|
}
|
|
147
238
|
/**
|
|
148
239
|
* Register a region with the scheduler at connect: stamp its order, start its viewport snapshot.
|
|
149
|
-
* Cheap on purpose (a stamp and an `observe`) — connect runs for every region in one task.
|
|
240
|
+
* Cheap on purpose (a stamp and an `observe`) — connect runs for every region in one task. The
|
|
241
|
+
* snapshot is `owner`'s (the region's lifetime in the page): it stops at the region's turn, or when
|
|
242
|
+
* the owner ends.
|
|
150
243
|
*/
|
|
151
|
-
function register_region(el) {
|
|
244
|
+
function register_region(el, owner) {
|
|
152
245
|
if (!order_of.has(el)) order_of.set(el, next_order++);
|
|
153
|
-
if (
|
|
154
|
-
|
|
246
|
+
if (snapshot_of.has(el) || owner.closed) return;
|
|
247
|
+
const stop = observe$1(el, "0px", (intersecting) => {
|
|
155
248
|
in_viewport.set(el, intersecting);
|
|
156
|
-
|
|
157
|
-
|
|
249
|
+
sync_loading(el);
|
|
250
|
+
refile(el);
|
|
251
|
+
if (pending_count) schedule_drain();
|
|
252
|
+
});
|
|
253
|
+
snapshot_of.set(el, stop);
|
|
254
|
+
owner.defer(() => {
|
|
255
|
+
if (snapshot_of.get(el) === stop) stop_snapshot(el);
|
|
256
|
+
});
|
|
158
257
|
}
|
|
159
258
|
/**
|
|
160
259
|
* Drop a region from the scheduler (disconnect): its snapshot stops and any pending turn is handed
|
|
@@ -164,26 +263,29 @@ function register_region(el) {
|
|
|
164
263
|
function unregister_region(el) {
|
|
165
264
|
stop_snapshot(el);
|
|
166
265
|
in_viewport.delete(el);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
266
|
+
stop_loading(el);
|
|
267
|
+
const list = turns_of.get(el);
|
|
268
|
+
if (list) for (const turn of list.slice().reverse()) {
|
|
269
|
+
retire(turn);
|
|
171
270
|
turn.resolve();
|
|
172
271
|
}
|
|
173
|
-
if (
|
|
272
|
+
if (pending_count) schedule_drain();
|
|
174
273
|
}
|
|
175
274
|
function stop_snapshot(el) {
|
|
176
|
-
|
|
177
|
-
|
|
275
|
+
const stop = snapshot_of.get(el);
|
|
276
|
+
if (stop === void 0) return;
|
|
277
|
+
snapshot_of.delete(el);
|
|
278
|
+
stop();
|
|
178
279
|
}
|
|
179
280
|
/** The island's module is in flight: a viewport island here holds ready islands below the fold. */
|
|
180
281
|
function hydrate_started(el) {
|
|
181
282
|
loading.add(el);
|
|
283
|
+
sync_loading(el);
|
|
182
284
|
}
|
|
183
285
|
/** The hydrate attempt is over (turn taken, or it never got there — no entry, a failed import). */
|
|
184
286
|
function hydrate_settled(el) {
|
|
185
|
-
if (!
|
|
186
|
-
if (
|
|
287
|
+
if (!stop_loading(el)) return;
|
|
288
|
+
if (pending_count) schedule_drain();
|
|
187
289
|
}
|
|
188
290
|
/**
|
|
189
291
|
* Wait for this region's turn. Resolves in a task of its own, after every region ahead of it in
|
|
@@ -192,12 +294,21 @@ function hydrate_settled(el) {
|
|
|
192
294
|
*/
|
|
193
295
|
function hydrate_turn(el) {
|
|
194
296
|
return new Promise((resolve) => {
|
|
195
|
-
|
|
297
|
+
const turn = {
|
|
196
298
|
el,
|
|
197
299
|
order: order_of.get(el) ?? (order_of.set(el, next_order), next_order++),
|
|
300
|
+
seq: next_seq++,
|
|
198
301
|
queued_at: now(),
|
|
199
|
-
resolve
|
|
200
|
-
|
|
302
|
+
resolve,
|
|
303
|
+
vis: in_viewport.get(el) === true,
|
|
304
|
+
done: false
|
|
305
|
+
};
|
|
306
|
+
pending_count++;
|
|
307
|
+
heap_push(turn.vis ? heap_vis : heap_rest, turn);
|
|
308
|
+
const list = turns_of.get(el);
|
|
309
|
+
if (list) list.push(turn);
|
|
310
|
+
else turns_of.set(el, [turn]);
|
|
311
|
+
if (!in_viewport.has(el)) unsnapped.push(turn);
|
|
201
312
|
schedule_drain();
|
|
202
313
|
});
|
|
203
314
|
}
|
|
@@ -205,52 +316,56 @@ function now() {
|
|
|
205
316
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
206
317
|
}
|
|
207
318
|
function schedule_drain() {
|
|
208
|
-
if (
|
|
209
|
-
|
|
319
|
+
if (drainer.now.kind === "queued") return;
|
|
320
|
+
drainer.go("queued");
|
|
210
321
|
yield_task().then(drain);
|
|
211
322
|
}
|
|
212
|
-
/** Re-drain after `ms
|
|
323
|
+
/** Re-drain after `ms`. One wait at a time, and it is the EARLIEST: a sooner wait replaces a later
|
|
324
|
+
* one (a short snapshot wait set during a long hold), a later one rides the sooner. */
|
|
213
325
|
function drain_after(ms) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
326
|
+
const at = now() + Math.max(1, ms);
|
|
327
|
+
const s = drainer.now;
|
|
328
|
+
if (s.kind === "waiting" && s.at <= at) return;
|
|
329
|
+
drainer.go("waiting", { at });
|
|
330
|
+
drainer.scope.timeout(schedule_drain, Math.max(1, ms));
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* The earliest-requested pending turn still waiting for its snapshot within its wait, or none.
|
|
334
|
+
* Turns are requested in time order, so the ones ahead of it that reported, went, or ran out of
|
|
335
|
+
* wait are behind for good (the clock only moves on) and leave the queue.
|
|
336
|
+
*/
|
|
337
|
+
function first_unsnapped(t) {
|
|
338
|
+
while (unsnapped_head < unsnapped.length) {
|
|
339
|
+
const p = unsnapped[unsnapped_head];
|
|
340
|
+
if (!p.done && !in_viewport.has(p.el) && SNAPSHOT_WAIT_MS - (t - p.queued_at) > 0) return p;
|
|
341
|
+
unsnapped_head++;
|
|
342
|
+
}
|
|
343
|
+
unsnapped.length = unsnapped_head = 0;
|
|
219
344
|
}
|
|
220
345
|
/** One turn per call: pick the best pending region, hand it its task; the next pick after a yield. */
|
|
221
346
|
function drain() {
|
|
222
|
-
|
|
223
|
-
if (!
|
|
347
|
+
drainer.go("idle");
|
|
348
|
+
if (!pending_count) return;
|
|
224
349
|
const t = now();
|
|
225
350
|
const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
226
|
-
if (!hidden)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (left > 0) return drain_after(left);
|
|
351
|
+
if (!hidden) {
|
|
352
|
+
const p = first_unsnapped(t);
|
|
353
|
+
if (p) return drain_after(SNAPSHOT_WAIT_MS - (t - p.queued_at));
|
|
230
354
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const a = pending[best];
|
|
234
|
-
const b = pending[i];
|
|
235
|
-
const va = in_viewport.get(a.el) === true;
|
|
236
|
-
const vb = in_viewport.get(b.el) === true;
|
|
237
|
-
if (vb !== va ? vb : b.order < a.order) best = i;
|
|
238
|
-
}
|
|
239
|
-
const turn = pending[best];
|
|
240
|
-
if (!hidden && in_viewport.get(turn.el) !== true) {
|
|
355
|
+
const turn = heap_top(heap_vis, true) ?? heap_top(heap_rest, false);
|
|
356
|
+
if (!hidden && !turn.vis && loading_vis.size) {
|
|
241
357
|
const left = HOLD_FOR_VIEWPORT_MS - (t - turn.queued_at);
|
|
242
|
-
if (left > 0)
|
|
243
|
-
for (const el of loading) if (in_viewport.get(el) === true) return drain_after(left);
|
|
244
|
-
}
|
|
358
|
+
if (left > 0) return drain_after(left);
|
|
245
359
|
}
|
|
246
|
-
|
|
247
|
-
|
|
360
|
+
heap_pop(turn.vis ? heap_vis : heap_rest);
|
|
361
|
+
retire(turn);
|
|
362
|
+
stop_loading(turn.el);
|
|
248
363
|
stop_snapshot(turn.el);
|
|
249
364
|
turn.resolve();
|
|
250
|
-
if (
|
|
365
|
+
if (pending_count) schedule_drain();
|
|
251
366
|
}
|
|
252
367
|
/** Test seam: is a hydration turn queued or being drained right now? */
|
|
253
368
|
function schedule_idle() {
|
|
254
|
-
return
|
|
369
|
+
return pending_count === 0 && drainer.now.kind !== "queued";
|
|
255
370
|
}
|
|
256
371
|
export { after_document_painted, background_start, hydrate_settled, hydrate_started, hydrate_turn, register_region, reset_document_painted, schedule_idle, unregister_region, yield_task };
|
package/dist/runtime/seeds.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PageVisit } from "./session.js";
|
|
1
2
|
import { parse_sidecar_text } from "./wire-format.js";
|
|
2
3
|
import { PageSnapshot } from "../shims/page-store.svelte.js";
|
|
3
4
|
export declare const PAGE_SEED_SELECTOR = "script[type=\"application/ogygia-page\"]";
|
|
@@ -44,8 +45,11 @@ export declare function apply_soft_invalidate_doc(doc: Document): void;
|
|
|
44
45
|
* Do **not** clear `query_map` / `live_query_map` here — old islands are still mounted, and Kit's
|
|
45
46
|
* LiveQueryProxy throws if its cache entry vanishes mid-render. Instance sweep happens in
|
|
46
47
|
* {@link finish_spa_document} after the swap.
|
|
48
|
+
*
|
|
49
|
+
* `next`: the visit of the page going in, made by its navigation (runtime/session.ts `PageVisit`):
|
|
50
|
+
* from here on it is the page's, and the visit of the page going out ends.
|
|
47
51
|
*/
|
|
48
|
-
export declare function prepare_spa_document(): void;
|
|
52
|
+
export declare function prepare_spa_document(next?: PageVisit): void;
|
|
49
53
|
/**
|
|
50
54
|
* After the body swap: old islands have disconnected; new ones have only queued their hydrate.
|
|
51
55
|
* Sweep Kit query/live instance maps so the next page cannot reuse a LiveQuery whose `#start` is
|
package/dist/runtime/seeds.js
CHANGED
|
@@ -5,6 +5,7 @@ import { install_page_defer, page_defer_revivers } from "./page-defer.js";
|
|
|
5
5
|
import { transport_decoders } from "./app-transport.js";
|
|
6
6
|
import { kit_page_thread } from "./kit-page-thread.svelte.js";
|
|
7
7
|
import { boot_link, slots } from "./slots.js";
|
|
8
|
+
import { warn } from "../messages/lazy.js";
|
|
8
9
|
// src/runtime/seeds.ts
|
|
9
10
|
/**
|
|
10
11
|
* THE PAGE SEED on the client — parsed ONCE per document, kept in ONE place.
|
|
@@ -125,7 +126,7 @@ function seed_page_once() {
|
|
|
125
126
|
const el = document.querySelector(PAGE_SEED_SELECTOR);
|
|
126
127
|
if (el) apply_page_seed(parse_page_seed(el));
|
|
127
128
|
}
|
|
128
|
-
if (session().
|
|
129
|
+
if (session().visit.facts.length) absorb_page_facts();
|
|
129
130
|
}
|
|
130
131
|
/**
|
|
131
132
|
* ONE PAGE, NOT TWO (server/render-page.ts): an answer rendered outside its page — a hole, a region a
|
|
@@ -134,9 +135,11 @@ function seed_page_once() {
|
|
|
134
135
|
* values their HTML was rendered from, and every island reading the same keys sees them too (the
|
|
135
136
|
* page's data changed: a locale saved, a sign-in). On a Kit-hydrated page Kit owns `page.data`: the
|
|
136
137
|
* page's data is older than the answer's, and dev says to refresh it (`invalidateAll()`) first.
|
|
138
|
+
* Only the page's own: the facts queued on its visit (runtime/session.ts `PageVisit`), never the
|
|
139
|
+
* ones a page left before them brought (#269).
|
|
137
140
|
*/
|
|
138
141
|
function absorb_page_facts() {
|
|
139
|
-
const queued = session().
|
|
142
|
+
const queued = session().visit.facts.splice(0);
|
|
140
143
|
let merged = null;
|
|
141
144
|
for (const text of queued) {
|
|
142
145
|
let facts;
|
|
@@ -154,7 +157,7 @@ function absorb_page_facts() {
|
|
|
154
157
|
if (import.meta.env.DEV) {
|
|
155
158
|
const data = kit.page.data ?? {};
|
|
156
159
|
const older = Object.keys(merged).filter((k) => data[k] !== merged[k] && JSON.stringify(data[k]) !== JSON.stringify(merged[k]));
|
|
157
|
-
if (older.length)
|
|
160
|
+
if (older.length) warn("region_newer_page_data", { keys: older.join(", ") });
|
|
158
161
|
}
|
|
159
162
|
return;
|
|
160
163
|
}
|
|
@@ -195,9 +198,12 @@ function apply_soft_invalidate_doc(doc) {
|
|
|
195
198
|
* Do **not** clear `query_map` / `live_query_map` here — old islands are still mounted, and Kit's
|
|
196
199
|
* LiveQueryProxy throws if its cache entry vanishes mid-render. Instance sweep happens in
|
|
197
200
|
* {@link finish_spa_document} after the swap.
|
|
201
|
+
*
|
|
202
|
+
* `next`: the visit of the page going in, made by its navigation (runtime/session.ts `PageVisit`):
|
|
203
|
+
* from here on it is the page's, and the visit of the page going out ends.
|
|
198
204
|
*/
|
|
199
|
-
function prepare_spa_document() {
|
|
200
|
-
session().reset();
|
|
205
|
+
function prepare_spa_document(next) {
|
|
206
|
+
session().reset(next);
|
|
201
207
|
slots.remoteSeeds?.clear_remote_seeds();
|
|
202
208
|
reset_page();
|
|
203
209
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConcurrencyGate } from "./concurrency.js";
|
|
2
|
+
import { Scope } from "@ogygia/machine";
|
|
2
3
|
/** Cached SSR DOM (+ optional SWR endpoint) for `{#if}` remount of hydrate:none regions.
|
|
3
4
|
* Keyed by lake entry id — size is O(unique lakes on the page), not O(toggles).
|
|
4
5
|
* Cleared on SPA body swap via `reset()`. SWR refresh replaces the entry (does not grow). */
|
|
@@ -11,6 +12,33 @@ export type LakeCacheEntry = {
|
|
|
11
12
|
/** Client TTL in ms; `0` = no expiry. */
|
|
12
13
|
maxAgeMs: number;
|
|
13
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* THE PAGE VISIT: one per page the visitor is on, the document load, then each page a navigation
|
|
17
|
+
* swaps in. It ends when the next page's swap takes over (`RuntimeSession.reset`). What the page
|
|
18
|
+
* holds is its own and goes with it: on `scope`, its listeners, timers, observers and streams (the
|
|
19
|
+
* router's link scan, the batch of its holes, the waits for the sheets its answers bring); in
|
|
20
|
+
* `facts`, what its answers brought for its next hydrate.
|
|
21
|
+
*
|
|
22
|
+
* A navigation makes the visit of the page it brings before its swap (`RuntimeSession.next_visit`):
|
|
23
|
+
* what it starts for that page early, the batch of its holes, is that visit's. A navigation that
|
|
24
|
+
* ends without its swap (called off, taken over, given to the browser) ends that visit, never the
|
|
25
|
+
* page's (./router-nav.ts `navigate`).
|
|
26
|
+
*/
|
|
27
|
+
export type PageVisit = {
|
|
28
|
+
/** Closed when the visit ends. */
|
|
29
|
+
readonly scope: Scope;
|
|
30
|
+
/** The page facts answers rendered with (server/render-page.ts), queued at the answer seam
|
|
31
|
+
* (`region_fragment`) for the next hydrate to merge into the page store (seeds.ts). This page's
|
|
32
|
+
* only: the next page's visit starts with none (#269). */
|
|
33
|
+
readonly facts: string[];
|
|
34
|
+
};
|
|
35
|
+
/** What one island's hydrate step put in `<head>` ({@link RuntimeSession.mark_head}). */
|
|
36
|
+
export interface HeadMark {
|
|
37
|
+
/** The head's elements now that were not its children when the mark was made, in head order. */
|
|
38
|
+
added(): Element[];
|
|
39
|
+
/** Stop noting (a step that ended without a record). */
|
|
40
|
+
stop(): void;
|
|
41
|
+
}
|
|
14
42
|
export declare class RuntimeSession {
|
|
15
43
|
#private;
|
|
16
44
|
/** Page-owned head nodes (see above). Survives `reset()`: it is the document's, not a page's. */
|
|
@@ -21,9 +49,16 @@ export declare class RuntimeSession {
|
|
|
21
49
|
claim_page_head(node: Node): void;
|
|
22
50
|
/** At boot: everything already in <head> came from the server — the page owns it. */
|
|
23
51
|
adopt_document_head(): void;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Before an island's hydrate step: start noting what lands in `<head>`. A MutationObserver on the
|
|
54
|
+
* head's own children, read synchronously when the step ends, so the record costs what the island
|
|
55
|
+
* added, not the whole head (a copy of the head per island grew as islands × head). Without
|
|
56
|
+
* MutationObserver: the head's elements, copied.
|
|
57
|
+
*/
|
|
58
|
+
mark_head(): HeadMark;
|
|
59
|
+
/** After an island's hydrate: the head elements it added (not there before the step, not
|
|
60
|
+
* page-owned) are that island's; then retire the page's copies of them. */
|
|
61
|
+
record_island_head(mark: HeadMark): void;
|
|
27
62
|
/**
|
|
28
63
|
* Remove every PAGE-owned head element that duplicates a live island-owned one (same markup), and
|
|
29
64
|
* the page's `<title>` once an island renders its own (the browser honours the first title, and
|
|
@@ -34,9 +69,11 @@ export declare class RuntimeSession {
|
|
|
34
69
|
readonly settled_lakes: WeakSet<Element>;
|
|
35
70
|
readonly initialized_lakes: Set<string>;
|
|
36
71
|
readonly server_gate: ConcurrencyGate;
|
|
37
|
-
/** The
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
/** The visit of the page on screen (see {@link PageVisit}). */
|
|
73
|
+
get visit(): PageVisit;
|
|
74
|
+
/** The visit of the page a navigation brings, made before its swap: the navigation's (it ends
|
|
75
|
+
* it when there is no swap) until `reset(next)` makes it the page. */
|
|
76
|
+
next_visit(): PageVisit;
|
|
40
77
|
get remote_seeded(): boolean;
|
|
41
78
|
mark_remote_seeded(): void;
|
|
42
79
|
get page_seeded(): boolean;
|
|
@@ -46,7 +83,9 @@ export declare class RuntimeSession {
|
|
|
46
83
|
/** Insert/replace a lake cache entry; evict oldest if over LAKE_CACHE_MAX. */
|
|
47
84
|
set_lake_cache(id: string, entry: LakeCacheEntry): void;
|
|
48
85
|
settle_lakes_in(root: ParentNode): void;
|
|
49
|
-
|
|
86
|
+
/** The next page takes over (its visit, made by its navigation; a new one when none is given),
|
|
87
|
+
* and the visit of the page it replaces ends. */
|
|
88
|
+
reset(next?: PageVisit): void;
|
|
50
89
|
}
|
|
51
90
|
/** Process-wide session for the islands runtime (one client module graph). */
|
|
52
91
|
export declare const runtime_session: RuntimeSession;
|