@ogygia/core 0.12.3 → 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 +17 -10
- 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.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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { after_document_painted, yield_task } from "./schedule.js";
|
|
2
2
|
import { kit_hydrates_page } from "./kit-boot.js";
|
|
3
3
|
import { emit } from "../events/bus.js";
|
|
4
|
+
import { flights, machine, scope } from "@ogygia/machine";
|
|
4
5
|
// src/runtime/load-scheduler.ts
|
|
5
6
|
/**
|
|
6
7
|
* THE LOAD SCHEDULER — every download ogygia starts on its own goes through one queue.
|
|
@@ -43,6 +44,12 @@ const RANK = {
|
|
|
43
44
|
ahead: 2,
|
|
44
45
|
speculative: 3
|
|
45
46
|
};
|
|
47
|
+
const CLASSES = [
|
|
48
|
+
"user",
|
|
49
|
+
"visible",
|
|
50
|
+
"ahead",
|
|
51
|
+
"speculative"
|
|
52
|
+
];
|
|
46
53
|
/** The fetch priority a class downloads at (`fetch(…, { priority })`, `<link fetchpriority>`). */
|
|
47
54
|
function fetch_priority_of(cls) {
|
|
48
55
|
return cls === "user" ? "high" : cls === "visible" ? "auto" : "low";
|
|
@@ -67,23 +74,74 @@ function on_screen(el, depth) {
|
|
|
67
74
|
}
|
|
68
75
|
return crosses && (r.width > 0 || r.height > 0);
|
|
69
76
|
}
|
|
70
|
-
|
|
77
|
+
/**
|
|
78
|
+
* The gate, as a machine:
|
|
79
|
+
*
|
|
80
|
+
* fresh ─ waiting{t0, resources} ─ settled{report}
|
|
81
|
+
* ╲ touched ───────────────────╱
|
|
82
|
+
*
|
|
83
|
+
* `fresh`: no wait yet. `touched`: the visitor clicked or typed before the wait began, so it waits on
|
|
84
|
+
* nothing. `waiting`: the wait is up; its visit owns the cap and the resources' listeners, so they go
|
|
85
|
+
* as it settles, however it settles. `settled`: open for good, with how it ended (the beacon reads
|
|
86
|
+
* it). One state in place of `input_seen`, `critical_done`, `release_critical` and the report.
|
|
87
|
+
*/
|
|
88
|
+
const gate_definition = machine()({
|
|
89
|
+
name: "critical-gate",
|
|
90
|
+
initial: "fresh",
|
|
91
|
+
steps: {
|
|
92
|
+
fresh: [
|
|
93
|
+
"touched",
|
|
94
|
+
"waiting",
|
|
95
|
+
"settled"
|
|
96
|
+
],
|
|
97
|
+
touched: ["settled"],
|
|
98
|
+
waiting: ["settled"],
|
|
99
|
+
settled: []
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
let gate = gate_definition.create();
|
|
71
103
|
const on_first_input = () => {
|
|
72
|
-
|
|
73
|
-
|
|
104
|
+
const kind = gate.now.kind;
|
|
105
|
+
if (kind === "fresh") gate.go("touched");
|
|
106
|
+
else if (kind === "waiting") settle_critical("input");
|
|
74
107
|
};
|
|
75
108
|
if (typeof document !== "undefined") for (const type of ["pointerdown", "keydown"]) addEventListener(type, on_first_input, {
|
|
76
109
|
capture: true,
|
|
77
110
|
passive: true,
|
|
78
111
|
once: true
|
|
79
112
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
113
|
+
/** The critical wait below, once for the document (a test's reset drops it). */
|
|
114
|
+
const critical = flights({
|
|
115
|
+
name: "critical resources",
|
|
116
|
+
run: () => until_critical()
|
|
117
|
+
});
|
|
118
|
+
/** The gate is open: the critical wait settled. */
|
|
119
|
+
const critical_done = () => gate.now.kind === "settled";
|
|
84
120
|
/** The critical wait's outcome once it settled (the profiler's beacon reads it), else `null`. */
|
|
85
121
|
function critical_outcome() {
|
|
86
|
-
|
|
122
|
+
const s = gate.now;
|
|
123
|
+
return s.kind === "settled" ? s.report : null;
|
|
124
|
+
}
|
|
125
|
+
/** The wait ends (once: a gate already settled stays as it settled). */
|
|
126
|
+
function settle_critical(outcome) {
|
|
127
|
+
const s = gate.now;
|
|
128
|
+
if (s.kind === "settled") return;
|
|
129
|
+
const report = s.kind === "waiting" ? {
|
|
130
|
+
outcome,
|
|
131
|
+
resources: s.resources,
|
|
132
|
+
waited_ms: Math.round(now() - s.t0)
|
|
133
|
+
} : {
|
|
134
|
+
outcome,
|
|
135
|
+
resources: 0,
|
|
136
|
+
waited_ms: 0
|
|
137
|
+
};
|
|
138
|
+
gate.go("settled", { report });
|
|
139
|
+
if (DEVTOOLS) emit({
|
|
140
|
+
domain: "runtime",
|
|
141
|
+
name: "load.critical",
|
|
142
|
+
...report
|
|
143
|
+
});
|
|
144
|
+
drain();
|
|
87
145
|
}
|
|
88
146
|
/** Elements carrying the page's critical resources that have not arrived yet: an eager
|
|
89
147
|
* `img[fetchpriority=high]` not complete, a `link[rel=preload][fetchpriority=high]` not fetched. */
|
|
@@ -126,78 +184,97 @@ function preload_urls(link) {
|
|
|
126
184
|
* wait. A page with no marked resource resolves right after the paint. Once resolved it stays so.
|
|
127
185
|
*/
|
|
128
186
|
function critical_settled() {
|
|
129
|
-
return critical
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
name: "load.critical",
|
|
144
|
-
...critical_report
|
|
145
|
-
});
|
|
146
|
-
resolve();
|
|
147
|
-
drain();
|
|
148
|
-
};
|
|
149
|
-
const waiting = input_seen || document.visibilityState === "hidden" ? [] : pending_critical();
|
|
150
|
-
const timer = waiting.length ? setTimeout(() => done("cap"), CRITICAL_CAP_MS) : void 0;
|
|
151
|
-
if (!waiting.length) return done("none");
|
|
152
|
-
release_critical = () => done("input");
|
|
187
|
+
return critical.get();
|
|
188
|
+
}
|
|
189
|
+
function until_critical() {
|
|
190
|
+
return after_document_painted().then(async () => {
|
|
191
|
+
const s = gate.now;
|
|
192
|
+
if (s.kind === "settled") return;
|
|
193
|
+
const waiting = s.kind === "touched" || document.visibilityState === "hidden" ? [] : pending_critical();
|
|
194
|
+
if (!waiting.length) return settle_critical("none");
|
|
195
|
+
gate.go("waiting", {
|
|
196
|
+
t0: now(),
|
|
197
|
+
resources: waiting.length
|
|
198
|
+
});
|
|
199
|
+
const wait = gate.scope;
|
|
200
|
+
wait.timeout(() => settle_critical("cap"), CRITICAL_CAP_MS);
|
|
153
201
|
let left = waiting.length;
|
|
154
202
|
const one = () => {
|
|
155
|
-
if (--left === 0)
|
|
203
|
+
if (--left === 0) settle_critical("arrived");
|
|
156
204
|
};
|
|
157
205
|
for (const el of waiting) {
|
|
158
|
-
|
|
159
|
-
|
|
206
|
+
wait.on(el, "load", one, { once: true });
|
|
207
|
+
wait.on(el, "error", one, { once: true });
|
|
160
208
|
}
|
|
161
|
-
|
|
209
|
+
await gate.wait("settled");
|
|
210
|
+
});
|
|
162
211
|
}
|
|
212
|
+
/** The slots waiting, in the order they were asked for (a pass takes out the ones it started when
|
|
213
|
+
* it ends; `waiting` counts the rest at any moment). */
|
|
163
214
|
const queue = [];
|
|
215
|
+
let waiting = 0;
|
|
164
216
|
let in_flight_background = 0;
|
|
165
|
-
|
|
166
|
-
|
|
217
|
+
/** The scroll watch while a queued download waits (its listener and its frame): closed once the
|
|
218
|
+
* queue is empty. */
|
|
219
|
+
let scroll_watch = null;
|
|
167
220
|
/**
|
|
168
|
-
*
|
|
169
|
-
* success or not). `cls` may be a getter, re-read whenever the queue
|
|
221
|
+
* A queued download: asking for one queues it. `ready` resolves with a `release` to call when the
|
|
222
|
+
* download is done (settled, success or not). `cls` may be a getter, re-read whenever the queue
|
|
223
|
+
* drains (viewport changes); `promote` lifts it.
|
|
170
224
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
225
|
+
var LoadSlot = class {
|
|
226
|
+
kind;
|
|
227
|
+
label;
|
|
228
|
+
queued_at = now();
|
|
229
|
+
ready;
|
|
230
|
+
/** Still in the queue: a drain takes it out when it starts. */
|
|
231
|
+
queued = true;
|
|
232
|
+
#read;
|
|
233
|
+
#forced = null;
|
|
234
|
+
#resolve;
|
|
235
|
+
constructor(o) {
|
|
236
|
+
this.kind = o.kind;
|
|
237
|
+
this.label = o.label;
|
|
238
|
+
this.#read = typeof o.cls === "function" ? o.cls : () => o.cls;
|
|
239
|
+
this.ready = new Promise((r) => this.#resolve = r);
|
|
240
|
+
queue.push(this);
|
|
241
|
+
waiting++;
|
|
242
|
+
if (typeof o.cls === "function") watch_scroll();
|
|
243
|
+
if (!critical_done() && waits_for_critical(this.kind, this.cls())) critical_settled();
|
|
244
|
+
drain();
|
|
245
|
+
}
|
|
246
|
+
/** Its class now: the getter's, or the promoted one when that ranks higher. */
|
|
247
|
+
cls() {
|
|
248
|
+
const c = this.#read();
|
|
249
|
+
const forced = this.#forced;
|
|
176
250
|
return forced !== null && RANK[forced] < RANK[c] ? forced : c;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
251
|
+
}
|
|
252
|
+
/** Lift it (a prefetch the visitor then navigates to). */
|
|
253
|
+
promote(c) {
|
|
254
|
+
this.#forced = c;
|
|
255
|
+
drain();
|
|
256
|
+
}
|
|
257
|
+
/** Out of the queue: the holder's `ready` resolves, counted in the background window when it
|
|
258
|
+
* is one of the guesses. */
|
|
259
|
+
start(cls, background) {
|
|
260
|
+
if (background) in_flight_background++;
|
|
261
|
+
let released = false;
|
|
262
|
+
if (DEVTOOLS) emit({
|
|
263
|
+
domain: "runtime",
|
|
264
|
+
name: "load.started",
|
|
265
|
+
class: cls,
|
|
266
|
+
kind: this.kind,
|
|
267
|
+
label: this.label,
|
|
268
|
+
waited_ms: Math.round(now() - this.queued_at)
|
|
269
|
+
});
|
|
270
|
+
this.#resolve(() => {
|
|
271
|
+
if (released) return;
|
|
272
|
+
released = true;
|
|
273
|
+
if (background) in_flight_background--;
|
|
197
274
|
drain();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
};
|
|
201
278
|
function waits_for_critical(kind, cls) {
|
|
202
279
|
if (cls === "user") return false;
|
|
203
280
|
if (kit_hydrates_page()) return false;
|
|
@@ -210,91 +287,114 @@ function waits_for_critical(kind, cls) {
|
|
|
210
287
|
* task (52 ms on a 4×-throttled runner). After this much work in one task the drain yields.
|
|
211
288
|
*/
|
|
212
289
|
const START_BUDGET_MS = 16;
|
|
213
|
-
|
|
214
|
-
|
|
290
|
+
/**
|
|
291
|
+
* The queue's drain, as a machine: `idle`, `draining`, or `again` (a drain was asked for while one
|
|
292
|
+
* ran: the one running goes round once more, and asks again when it is done). One state in place of
|
|
293
|
+
* `draining` beside `drain_again`.
|
|
294
|
+
*/
|
|
295
|
+
const drain_definition = machine()({
|
|
296
|
+
name: "load-drain",
|
|
297
|
+
initial: "idle",
|
|
298
|
+
steps: {
|
|
299
|
+
idle: ["draining"],
|
|
300
|
+
draining: ["idle", "again"],
|
|
301
|
+
again: ["idle", "draining"]
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
let drainer = drain_definition.create();
|
|
215
305
|
function drain() {
|
|
216
|
-
|
|
217
|
-
|
|
306
|
+
const kind = drainer.now.kind;
|
|
307
|
+
if (kind !== "idle") {
|
|
308
|
+
if (kind === "draining") drainer.go("again");
|
|
218
309
|
return;
|
|
219
310
|
}
|
|
220
|
-
if (!
|
|
221
|
-
draining
|
|
311
|
+
if (!waiting) return;
|
|
312
|
+
drainer.go("draining");
|
|
313
|
+
const mine = drainer;
|
|
222
314
|
drain_queue().finally(() => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
315
|
+
if (drainer !== mine) return;
|
|
316
|
+
const again = drainer.now.kind === "again";
|
|
317
|
+
drainer.go("idle");
|
|
318
|
+
if (again) drain();
|
|
228
319
|
});
|
|
229
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* One pass goes through the queue by class, then in the order asked, and then through the slots
|
|
323
|
+
* asked for while it ran, in the order asked. The queue is kept in the order asked, so reading each
|
|
324
|
+
* class once and putting the slot in its class's list is that order (a sort compared two classes per
|
|
325
|
+
* step, every pass: passes × queue × log queue reads on a page whose downloads each end a pass).
|
|
326
|
+
*
|
|
327
|
+
* @heisenbench linear
|
|
328
|
+
*/
|
|
230
329
|
async function drain_queue() {
|
|
231
330
|
let until = now() + START_BUDGET_MS;
|
|
232
331
|
for (;;) {
|
|
233
|
-
|
|
234
|
-
|
|
332
|
+
if (drainer.now.kind === "again") drainer.go("draining");
|
|
333
|
+
const asked = queue.length;
|
|
334
|
+
const by_class = [
|
|
335
|
+
[],
|
|
336
|
+
[],
|
|
337
|
+
[],
|
|
338
|
+
[]
|
|
339
|
+
];
|
|
340
|
+
for (let i = 0; i < asked; i++) by_class[RANK[queue[i].cls()]].push(queue[i]);
|
|
341
|
+
let ran = false;
|
|
342
|
+
let started = false;
|
|
235
343
|
let yielded = false;
|
|
236
|
-
for (let
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
344
|
+
pass: for (let r = 0; r <= 4; r++) {
|
|
345
|
+
const list = r < 4 ? by_class[r] : queue;
|
|
346
|
+
for (let i = r < 4 ? 0 : asked; i < list.length; i++) {
|
|
347
|
+
const e = list[i];
|
|
348
|
+
if (!e.queued) continue;
|
|
349
|
+
const cls = r < 4 && !ran ? CLASSES[r] : e.cls();
|
|
350
|
+
if (!critical_done() && waits_for_critical(e.kind, cls)) continue;
|
|
351
|
+
const background = cls === "ahead" || cls === "speculative";
|
|
352
|
+
if (background && in_flight_background >= 3) continue;
|
|
353
|
+
e.queued = false;
|
|
354
|
+
waiting--;
|
|
355
|
+
started = true;
|
|
356
|
+
e.start(cls, background);
|
|
357
|
+
await Promise.resolve();
|
|
358
|
+
ran = true;
|
|
359
|
+
if (now() >= until) {
|
|
360
|
+
leave_queue();
|
|
361
|
+
await yield_task();
|
|
362
|
+
until = now() + START_BUDGET_MS;
|
|
363
|
+
yielded = true;
|
|
364
|
+
break pass;
|
|
365
|
+
}
|
|
256
366
|
}
|
|
257
367
|
}
|
|
258
|
-
if (!yielded
|
|
368
|
+
if (!yielded) {
|
|
369
|
+
if (started) leave_queue();
|
|
370
|
+
if (drainer.now.kind !== "again") return;
|
|
371
|
+
}
|
|
259
372
|
}
|
|
260
373
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
let
|
|
265
|
-
|
|
266
|
-
domain: "runtime",
|
|
267
|
-
name: "load.started",
|
|
268
|
-
class: cls,
|
|
269
|
-
kind: e.kind,
|
|
270
|
-
label: e.label,
|
|
271
|
-
waited_ms: Math.round(now() - e.queued_at)
|
|
272
|
-
});
|
|
273
|
-
e.resolve(() => {
|
|
274
|
-
if (released) return;
|
|
275
|
-
released = true;
|
|
276
|
-
if (background) in_flight_background--;
|
|
277
|
-
drain();
|
|
278
|
-
});
|
|
374
|
+
/** The started slots out of the queue, the rest kept in the order asked. */
|
|
375
|
+
function leave_queue() {
|
|
376
|
+
let w = 0;
|
|
377
|
+
for (let i = 0; i < queue.length; i++) if (queue[i].queued) queue[w++] = queue[i];
|
|
378
|
+
queue.length = w;
|
|
279
379
|
}
|
|
280
380
|
/** While a queued download's class can change with the viewport, re-drain on scroll (one frame). */
|
|
281
381
|
function watch_scroll() {
|
|
282
|
-
if (scroll_watch || typeof
|
|
283
|
-
scroll_watch =
|
|
382
|
+
if (scroll_watch || typeof window === "undefined") return;
|
|
383
|
+
const watch = scroll_watch = scope();
|
|
284
384
|
let framed = false;
|
|
285
385
|
const on_scroll = () => {
|
|
286
386
|
if (framed) return;
|
|
287
387
|
framed = true;
|
|
288
|
-
|
|
388
|
+
watch.timeout(() => {
|
|
289
389
|
framed = false;
|
|
290
390
|
drain();
|
|
291
|
-
if (!
|
|
292
|
-
|
|
293
|
-
scroll_watch =
|
|
391
|
+
if (!waiting) {
|
|
392
|
+
watch.close();
|
|
393
|
+
scroll_watch = null;
|
|
294
394
|
}
|
|
295
|
-
});
|
|
395
|
+
}, "frame");
|
|
296
396
|
};
|
|
297
|
-
|
|
397
|
+
watch.on(window, "scroll", on_scroll, {
|
|
298
398
|
capture: true,
|
|
299
399
|
passive: true
|
|
300
400
|
});
|
|
@@ -304,14 +404,14 @@ function now() {
|
|
|
304
404
|
}
|
|
305
405
|
/** Test seam: a fresh document. */
|
|
306
406
|
function reset_load_scheduler() {
|
|
407
|
+
for (const s of queue) s.queued = false;
|
|
307
408
|
queue.length = 0;
|
|
409
|
+
waiting = 0;
|
|
308
410
|
in_flight_background = 0;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
critical
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
critical_report = null;
|
|
315
|
-
input_seen = false;
|
|
411
|
+
drainer.stop();
|
|
412
|
+
drainer = drain_definition.create();
|
|
413
|
+
critical.drop(() => true);
|
|
414
|
+
gate.stop();
|
|
415
|
+
gate = gate_definition.create();
|
|
316
416
|
}
|
|
317
|
-
export { BACKGROUND_WINDOW, CRITICAL_CAP_MS, critical_outcome, critical_settled, fetch_priority_of,
|
|
417
|
+
export { BACKGROUND_WINDOW, CRITICAL_CAP_MS, LoadSlot, critical_outcome, critical_settled, fetch_priority_of, preload_urls, reset_load_scheduler, viewport_class };
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { import_again } from "./import-again.js";
|
|
1
2
|
import { speculate_url } from "./speculate-hint.js";
|
|
3
|
+
import { OgygiaError, warn } from "../messages/core.js";
|
|
4
|
+
import { download } from "../policies.js";
|
|
5
|
+
import { STALE, flights } from "@ogygia/machine";
|
|
2
6
|
// src/runtime/nav-handle.ts
|
|
3
7
|
/**
|
|
4
8
|
* THE NAVIGATION HANDLE — how ISLAND-SIDE code (the `$app/navigation` shim, `ogygia/app`, the
|
|
@@ -25,6 +29,21 @@ import { speculate_url } from "./speculate-hint.js";
|
|
|
25
29
|
* purpose: importing {@link NAV_HANDLE_KEY} would be the very edge this module exists to avoid).
|
|
26
30
|
*/
|
|
27
31
|
const NAV_HANDLE_KEY = Symbol.for("ogygia.nav");
|
|
32
|
+
/**
|
|
33
|
+
* THE SOFT REFRESH (./router-nav.ts `invalidate_all`), loaded once, on the first refresh. A
|
|
34
|
+
* download that fails is tried again at once, and a failure is never kept: the next refresh loads
|
|
35
|
+
* it afresh, under a fresh URL (./import-again.ts: the browser keeps the failed one). It once
|
|
36
|
+
* imported the same URL again, which fails at once for the life of the page. (Only the refresh is
|
|
37
|
+
* read from the module, so the bundler leaves the rest of the navigation out of this chunk; the
|
|
38
|
+
* router's own load, ./router.ts, reads all of it.)
|
|
39
|
+
*/
|
|
40
|
+
const load_refresh = flights({
|
|
41
|
+
name: "soft refresh",
|
|
42
|
+
run: import_again(() => import("./router-nav.js").then(({ invalidate_all }) => ({ invalidate_all }))),
|
|
43
|
+
retry: download
|
|
44
|
+
});
|
|
45
|
+
/** The refresh, once its module is in (never STALE: nothing drops it). */
|
|
46
|
+
const invalidate_all = () => load_refresh.get().then((n) => n === STALE ? void 0 : n.invalidate_all());
|
|
28
47
|
/** Feature entry (`mpa-nav`, boot phase): the MPA handle, first — the router's install, later in
|
|
29
48
|
* the feature order, replaces it. */
|
|
30
49
|
function install() {
|
|
@@ -43,14 +62,14 @@ function mpa_nav() {
|
|
|
43
62
|
return {
|
|
44
63
|
goto(url, opts = {}) {
|
|
45
64
|
const target = new URL(url, location.href);
|
|
46
|
-
if (target.protocol !== "http:" && target.protocol !== "https:") throw new
|
|
47
|
-
if (target.origin !== location.origin && !opts.external) throw new
|
|
65
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") throw new OgygiaError("goto_not_http");
|
|
66
|
+
if (target.origin !== location.origin && !opts.external) throw new OgygiaError("goto_cross_origin");
|
|
48
67
|
if (opts.replaceState) location.replace(target.href);
|
|
49
68
|
else location.assign(target.href);
|
|
50
69
|
return Promise.resolve();
|
|
51
70
|
},
|
|
52
|
-
invalidate:
|
|
53
|
-
invalidateAll:
|
|
71
|
+
invalidate: invalidate_all,
|
|
72
|
+
invalidateAll: invalidate_all,
|
|
54
73
|
preloadData(url) {
|
|
55
74
|
speculate_url(url, "prerender");
|
|
56
75
|
return Promise.resolve({
|
|
@@ -65,10 +84,10 @@ function mpa_nav() {
|
|
|
65
84
|
},
|
|
66
85
|
disableScrollHandling() {},
|
|
67
86
|
pushState() {
|
|
68
|
-
|
|
87
|
+
warn("shallow_routing_unsupported", { method: "pushState" });
|
|
69
88
|
},
|
|
70
89
|
replaceState() {
|
|
71
|
-
|
|
90
|
+
warn("shallow_routing_unsupported", { method: "replaceState" });
|
|
72
91
|
},
|
|
73
92
|
beforeNavigate: () => NO_UNSUBSCRIBE,
|
|
74
93
|
afterNavigate: () => NO_UNSUBSCRIBE,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Machine } from "@ogygia/machine";
|
|
2
|
+
type States = {
|
|
3
|
+
idle: {};
|
|
4
|
+
claimed: {
|
|
5
|
+
href: string;
|
|
6
|
+
};
|
|
7
|
+
loading: {
|
|
8
|
+
href: string;
|
|
9
|
+
};
|
|
10
|
+
settling: {
|
|
11
|
+
href: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
declare const steps: {
|
|
15
|
+
readonly idle: readonly ["claimed"];
|
|
16
|
+
readonly claimed: readonly ["claimed", "loading", "idle"];
|
|
17
|
+
readonly loading: readonly ["claimed", "settling", "idle"];
|
|
18
|
+
readonly settling: readonly ["claimed", "idle"];
|
|
19
|
+
};
|
|
20
|
+
/** The navigation machine the router holds. */
|
|
21
|
+
export type Navigation = Machine<States, typeof steps>;
|
|
22
|
+
/** A navigation machine, idle. */
|
|
23
|
+
export declare function create_navigation(): Navigation;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { machine } from "@ogygia/machine";
|
|
2
|
+
const definition = machine()({
|
|
3
|
+
name: "navigation",
|
|
4
|
+
initial: "idle",
|
|
5
|
+
steps: {
|
|
6
|
+
idle: ["claimed"],
|
|
7
|
+
claimed: [
|
|
8
|
+
"claimed",
|
|
9
|
+
"loading",
|
|
10
|
+
"idle"
|
|
11
|
+
],
|
|
12
|
+
loading: [
|
|
13
|
+
"claimed",
|
|
14
|
+
"settling",
|
|
15
|
+
"idle"
|
|
16
|
+
],
|
|
17
|
+
settling: ["claimed", "idle"]
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
/** A navigation machine, idle. */
|
|
21
|
+
function create_navigation() {
|
|
22
|
+
return definition.create();
|
|
23
|
+
}
|
|
24
|
+
export { create_navigation };
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* DOM is not the island drifting).
|
|
25
25
|
*
|
|
26
26
|
* `data-ogygia-keep` on an `<ogygia-region>` is NOT `foreign`: there it names an island that survives
|
|
27
|
-
* navigation (
|
|
27
|
+
* navigation (a kept island) — Svelte owns that island, and it is repaired and hydrated like any other.
|
|
28
28
|
*
|
|
29
29
|
* FACETS (note §11): `owner_of` answers who writes INSIDE an element; the same module answers who
|
|
30
30
|
* writes its ATTRIBUTES (`attributes_writer`, `REGION_RENDER_ATTRS`), who ORDERS its children
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { boot_link } from "./slots.js";
|
|
1
2
|
import { PAGE_DEFER_BOOTSTRAP, PAGE_DEFER_GLOBAL, PAGE_DEFER_KEY, PAGE_DEFER_REGISTRY_KEY, PAGE_SETTLED_KEY } from "../page-defer.js";
|
|
2
|
-
import { parse } from "devalue";
|
|
3
3
|
// src/runtime/page-defer.ts
|
|
4
4
|
/**
|
|
5
5
|
* Client side of streaming `$page.data` promises into islands (see `../page-defer.ts` for the why).
|
|
@@ -70,13 +70,13 @@ function install_page_defer(extra_revivers) {
|
|
|
70
70
|
const r = reg();
|
|
71
71
|
if (r.live) return;
|
|
72
72
|
r.revivers = page_defer_revivers(extra_revivers);
|
|
73
|
-
r.live = (id, ok, encoded) => {
|
|
73
|
+
r.live = (id, ok, encoded) => boot_link().with_devalue(({ parse }) => {
|
|
74
74
|
let value = null;
|
|
75
75
|
try {
|
|
76
76
|
value = parse(encoded, r.revivers);
|
|
77
77
|
} catch {}
|
|
78
78
|
settle(id, ok, value);
|
|
79
|
-
};
|
|
79
|
+
});
|
|
80
80
|
const queued = r.q;
|
|
81
81
|
r.q = [];
|
|
82
82
|
if (queued) for (const [id, ok, encoded] of queued) r.live(id, ok, encoded);
|
|
@@ -13,10 +13,21 @@
|
|
|
13
13
|
* swap-then-blank-then-paint flicker). "Unsafe" means only "no sanitizer"; a region body is our own
|
|
14
14
|
* signed, same-origin SSR (HOLE-TRUST), so it is exactly as trusted as the page itself.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* INERT BY CONSTRUCTION (#382). The parse runs in a document with no browsing context (one per
|
|
17
|
+
* runtime, made on first use): it has no custom element registry, so nothing in the answer is built
|
|
18
|
+
* by a component's constructor before the restore (./restore.ts) puts it in shape, in any engine.
|
|
19
|
+
* A page-owned `<template>` was inert by a browser detail only: Firefox 159 (scoped registries)
|
|
20
|
+
* builds custom elements while one parses with `setHTMLUnsafe`, so a host had its constructor's
|
|
21
|
+
* root before the restore reached it. Attaching a declarative shadow root is a parse step, not an
|
|
22
|
+
* upgrade: it still happens here. The fragment's nodes belong to that document until they are
|
|
23
|
+
* inserted (insertion adopts them; they upgrade at connect, constructor then `connectedCallback`,
|
|
24
|
+
* a declarative root kept). `repair_if_drifted` needs this too: the server copy stays inert, and its
|
|
25
|
+
* light-DOM sequence is DSD-consistent with a live region whose hosts already carry shadow roots.
|
|
26
|
+
*
|
|
27
|
+
* (`Document.parseHTMLUnsafe` would also parse without a registry, but makes a whole document per
|
|
28
|
+
* call; one shared document and its template is the cheaper of the two.)
|
|
29
|
+
*
|
|
30
|
+
* In dev and e2e every parse is checked (`rule_parse_upgraded`, ./rules.ts): nothing built.
|
|
20
31
|
*/
|
|
21
32
|
export declare function parse_region_html(html: string): DocumentFragment;
|
|
22
33
|
/** The spec's declarative-shadow-DOM attach step, for engines that render DSD on first parse but predate
|