@ogygia/core 0.11.4 → 0.12.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/README.md +1 -1
- package/dist/KeepHost.svelte +1 -1
- package/dist/LiveHost.svelte +1 -1
- package/dist/NestedProvider.svelte +1 -1
- package/dist/Region.svelte +28 -21
- package/dist/Region.svelte.d.ts +0 -2
- package/dist/SlotBoundary.svelte +1 -1
- package/dist/compiler/bom.js +1 -1
- package/dist/compiler/browser.d.ts +1 -1
- package/dist/compiler/browser.js +1 -1
- package/dist/compiler/ctx.d.ts +1 -1
- package/dist/compiler/ctx.js +3 -3
- package/dist/compiler/{macros/dedent.d.ts → dedent.d.ts} +12 -1
- package/dist/compiler/dedent.js +43 -0
- package/dist/compiler/driver.d.ts +9 -6
- package/dist/compiler/driver.js +69 -35
- package/dist/compiler/ids.d.ts +3 -0
- package/dist/compiler/ids.js +4 -1
- package/dist/compiler/index.d.ts +3 -3
- package/dist/compiler/index.js +3 -3
- package/dist/compiler/kit.d.ts +7 -0
- package/dist/compiler/kit.js +22 -10
- package/dist/compiler/link/caps.js +59 -9
- package/dist/compiler/link/island-deps.js +225 -22
- package/dist/compiler/link/location-map.d.ts +10 -0
- package/dist/compiler/link/location-map.js +53 -0
- package/dist/compiler/link/manifest.js +5 -1
- package/dist/compiler/link/runtime-entry.d.ts +19 -2
- package/dist/compiler/link/runtime-entry.js +34 -1
- package/dist/compiler/link/server-manifest.js +43 -7
- package/dist/compiler/link/transport.js +39 -7
- package/dist/compiler/macros/source.js +2 -2
- package/dist/compiler/program.d.ts +4 -0
- package/dist/compiler/program.js +5 -1
- package/dist/compiler/region/transform.d.ts +13 -4
- package/dist/compiler/region/transform.js +15 -6
- package/dist/context.d.ts +3 -12
- package/dist/context.js +1 -16
- package/dist/hooks.d.ts +16 -0
- package/dist/hooks.js +4 -2
- package/dist/insights/page-insights.js +2 -2
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/nested.d.ts +11 -0
- package/dist/nested.js +19 -0
- package/dist/ref-sink.d.ts +15 -0
- package/dist/ref-sink.js +19 -0
- package/dist/ref.d.ts +2 -2
- package/dist/ref.js +1 -11
- package/dist/runtime/beacon.js +1 -1
- package/dist/runtime/boot-link.js +4 -1
- package/dist/runtime/core.js +10 -7
- package/dist/runtime/entry-locations.d.ts +10 -3
- package/dist/runtime/entry-locations.js +132 -21
- package/dist/runtime/full.js +5 -1
- package/dist/runtime/hydrate-core.d.ts +4 -4
- package/dist/runtime/hydrate-core.js +8 -5
- package/dist/runtime/morph.d.ts +7 -0
- package/dist/runtime/morph.js +21 -15
- package/dist/runtime/nav-handle.d.ts +3 -0
- package/dist/runtime/nav-handle.js +12 -5
- package/dist/runtime/ownership.d.ts +10 -1
- package/dist/runtime/ownership.js +19 -1
- package/dist/runtime/reconcile.d.ts +5 -2
- package/dist/runtime/reconcile.js +52 -16
- package/dist/runtime/region-endpoint-url.js +1 -0
- package/dist/runtime/region-ids.js +2 -2
- package/dist/runtime/remote-seeds.js +14 -3
- package/dist/runtime/router-nav.js +15 -0
- package/dist/runtime/router.js +3 -0
- package/dist/runtime/seeds.d.ts +10 -5
- package/dist/runtime/seeds.js +16 -12
- package/dist/runtime/slots.d.ts +24 -2
- package/dist/runtime/slots.js +2 -0
- package/dist/runtime/wire-classes.d.ts +2 -0
- package/dist/runtime/wire-classes.js +10 -1
- package/dist/runtime/wire-format.d.ts +4 -1
- package/dist/runtime/wire-format.js +6 -1
- package/dist/seed-ref-reviver.d.ts +24 -0
- package/dist/seed-ref-reviver.js +44 -0
- package/dist/seed-refs.d.ts +2 -16
- package/dist/seed-refs.js +1 -33
- package/dist/server/document-tail.d.ts +3 -2
- package/dist/server/document-tail.js +4 -3
- package/dist/server/entry-location.js +11 -8
- package/dist/server/props-wire.js +4 -3
- package/dist/server/signing-key.d.ts +10 -2
- package/dist/server/signing-key.js +49 -15
- package/dist/vite/index.js +5 -4
- package/dist/vite/kit-dirs.js +1 -1
- package/package.json +3 -3
- package/dist/compiler/link/entry-shim.d.ts +0 -4
- package/dist/compiler/link/entry-shim.js +0 -29
- package/dist/compiler/macros/dedent.js +0 -36
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dispose_ids } from "../ref.js";
|
|
2
|
-
import { boot_link, morph_sync_attributes } from "./slots.js";
|
|
2
|
+
import { boot_link, morph_sync_attributes, slots } from "./slots.js";
|
|
3
3
|
import { region_owned_ids } from "./region-ids.js";
|
|
4
4
|
import { emit } from "../events/bus.js";
|
|
5
5
|
import { endpoint_key, fingerprint_of, fnv1a, signature_of } from "./hash.js";
|
|
@@ -27,22 +27,48 @@ import { endpoint_key, fingerprint_of, fnv1a, signature_of } from "./hash.js";
|
|
|
27
27
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
28
28
|
const props_sidecar_of = (region) => boot_link().props_sidecar_of(region);
|
|
29
29
|
/**
|
|
30
|
-
* `<body>`
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* `<body>` has two owners (runtime/ownership.ts): the server, page by page (a theme class, a layout
|
|
31
|
+
* flag, the content), and the scripts living on the page (a scroll lock, a consent banner's state; a
|
|
32
|
+
* component mounted into the body, such as a popover's portal). A navigation moves the server's part
|
|
33
|
+
* to the new page's and leaves the page's part alone:
|
|
34
|
+
* - ATTRIBUTES: it removes only those the PREVIOUS page's server sent that the new one doesn't;
|
|
35
|
+
* - CHILDREN: the morph steps over a child the server never sent (a portal's mount anchor: taking
|
|
36
|
+
* it away leaves the portal inserting its content beside a detached node, so a popover in a kept
|
|
37
|
+
* island never opens again).
|
|
38
|
+
* ONE record per reconciled root (ownership.ts `record_server_body`), reached through the boot
|
|
39
|
+
* registry like every boot helper this lazy chunk uses: the router takes the body's when it starts,
|
|
40
|
+
* before any island wakes; each reconcile renews its root's (a harness driving it on its own root
|
|
41
|
+
* gets one from its first call). A root with no record yet is all the server's: every child is
|
|
42
|
+
* matched, and no attribute is taken away.
|
|
37
43
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
/** The record of what the server last sent `live`, or null while there is none. */
|
|
45
|
+
function body_record(live) {
|
|
46
|
+
const link = slots.boot;
|
|
47
|
+
return link && link.server_body_attributes(live) !== null ? link : null;
|
|
48
|
+
}
|
|
40
49
|
function sync_body_attributes(live, next) {
|
|
41
50
|
morph_sync_attributes()(live, next, true);
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
const sent = body_record(live)?.server_body_attributes(live);
|
|
52
|
+
if (sent) {
|
|
53
|
+
for (const name of sent) if (!next.hasAttribute(name)) live.removeAttribute(name);
|
|
44
54
|
}
|
|
45
|
-
|
|
55
|
+
}
|
|
56
|
+
/** The live body's children that are the page's own, not the server's (none without a record). */
|
|
57
|
+
function page_own_children(live) {
|
|
58
|
+
const own = /* @__PURE__ */ new Set();
|
|
59
|
+
const record = body_record(live);
|
|
60
|
+
if (record === null) return own;
|
|
61
|
+
for (let n = live.firstChild; n; n = n.nextSibling) if (!record.server_body_child(n)) own.add(n);
|
|
62
|
+
return own;
|
|
63
|
+
}
|
|
64
|
+
/** After a navigation the server's body is the new page's: its attributes, and every live child that
|
|
65
|
+
* is not the page's own. */
|
|
66
|
+
function renew_server_body(live, next, own) {
|
|
67
|
+
const link = slots.boot;
|
|
68
|
+
if (!link) return;
|
|
69
|
+
const children = [];
|
|
70
|
+
for (let n = live.firstChild; n; n = n.nextSibling) if (!own.has(n)) children.push(n);
|
|
71
|
+
link.record_server_body(live, next, children);
|
|
46
72
|
}
|
|
47
73
|
/** The `<script data-ogygia-props>` seed text of a region — keyed at the end of the body or the
|
|
48
74
|
* adjacent sibling (runtime/sidecar.ts) — or '' — the same lookup the hydrator uses, read-only.
|
|
@@ -143,6 +169,7 @@ function dt_emit_reconcile(live, next) {
|
|
|
143
169
|
function reconcile_body(live, next, morph) {
|
|
144
170
|
stamp_region_keys(live);
|
|
145
171
|
stamp_region_keys(next);
|
|
172
|
+
const own = page_own_children(live);
|
|
146
173
|
sync_body_attributes(live, next);
|
|
147
174
|
if (DEVTOOLS) dt_emit_reconcile(live, next);
|
|
148
175
|
const next_keys = /* @__PURE__ */ new Set();
|
|
@@ -158,7 +185,8 @@ function reconcile_body(live, next, morph) {
|
|
|
158
185
|
if (set !== void 0) for (const id of set) removed_ids.push(id);
|
|
159
186
|
}
|
|
160
187
|
absorb_kept_props(live, next);
|
|
161
|
-
morph(live, next.childNodes);
|
|
188
|
+
morph(live, next.childNodes, own.size > 0 ? { stays: (n) => own.has(n) } : void 0);
|
|
189
|
+
renew_server_body(live, next, own);
|
|
162
190
|
if (removed_ids.length > 0) dispose_ids(removed_ids);
|
|
163
191
|
}
|
|
164
192
|
/**
|
|
@@ -168,12 +196,20 @@ function reconcile_body(live, next, morph) {
|
|
|
168
196
|
* analytics, a scroll library) attached to `document.body`. htmx 4 boosts land the same way and use
|
|
169
197
|
* `outerSync` for exactly this reason: keep the element, sync its attributes, replace its children.
|
|
170
198
|
* So we do too — the live body node survives, only its guts change. Incoming children are moved out
|
|
171
|
-
* of the parsed document (insertion adopts them across documents).
|
|
199
|
+
* of the parsed document (insertion adopts them across documents). The page's own children (a
|
|
200
|
+
* portal's anchor) stay, as in the reconcile; the server's are replaced.
|
|
172
201
|
*/
|
|
173
202
|
function swap_body(live, next) {
|
|
203
|
+
const own = page_own_children(live);
|
|
174
204
|
sync_body_attributes(live, next);
|
|
175
|
-
live.replaceChildren();
|
|
205
|
+
if (own.size === 0) live.replaceChildren();
|
|
206
|
+
else for (let n = live.firstChild; n;) {
|
|
207
|
+
const here = n;
|
|
208
|
+
n = n.nextSibling;
|
|
209
|
+
if (!own.has(here)) live.removeChild(here);
|
|
210
|
+
}
|
|
176
211
|
while (next.firstChild) live.appendChild(next.firstChild);
|
|
212
|
+
renew_server_body(live, next, own);
|
|
177
213
|
}
|
|
178
214
|
/** For each live region whose reconcile key also appears in `next`, push the incoming region's
|
|
179
215
|
* props into the live island (kept regions only — a re-mounting region gets its props on mount). */
|
|
@@ -143,6 +143,7 @@ function warm_key(url) {
|
|
|
143
143
|
function warm_island_module(entry, base, cls = "speculative") {
|
|
144
144
|
const url = island_module_url(entry, base);
|
|
145
145
|
if (!url) return;
|
|
146
|
+
if (!entry_location(entry, base) && url.includes("/ogygia/og-region.")) return;
|
|
146
147
|
const key = warm_key(url);
|
|
147
148
|
if (warmed_modules.has(key)) return;
|
|
148
149
|
warmed_modules.add(key);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { set_resolve_sink } from "../ref.js";
|
|
1
|
+
import { set_resolve_sink } from "../ref-sink.js";
|
|
2
2
|
// src/runtime/region-ids.ts
|
|
3
3
|
/**
|
|
4
4
|
* R3 OWNERSHIP: which page-scoped hub ids each region's island resolved during hydration. The
|
|
5
|
-
* hydrate core installs a resolve-sink (ref.set_resolve_sink) around each island's sync prop +
|
|
5
|
+
* hydrate core installs a resolve-sink (ref-sink.ts `set_resolve_sink`) around each island's sync prop +
|
|
6
6
|
* context decode that funnels ids here, keyed by the region element; the reconciler (a navigation)
|
|
7
7
|
* disposes exactly the ids of REMOVED regions. WeakMap → a removed element's set is GC'd with it.
|
|
8
8
|
* Its own module so the boot-time reconciler and the lazily loaded hydrate core share one map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { slots } from "./slots.js";
|
|
1
|
+
import { boot_link, slots } from "./slots.js";
|
|
2
2
|
import { seed_query_responses } from "../shims/kit-remote/client-stub.js";
|
|
3
3
|
import { clear_remote_instances, clear_remote_seeds } from "../shims/kit-remote/remote-cache.js";
|
|
4
4
|
// src/runtime/remote-seeds.ts
|
|
@@ -7,12 +7,23 @@ import { clear_remote_instances, clear_remote_seeds } from "../shims/kit-remote/
|
|
|
7
7
|
* `application/ogygia-remote` side-channel (zero-flash hydration), and clear it across SPA swaps.
|
|
8
8
|
*
|
|
9
9
|
* A HYDRATE-phase feature (link/runtime-entry.ts): installed by the hydrate core, never the boot —
|
|
10
|
-
* only
|
|
11
|
-
* (its own `src/hooks.ts`), which reach Svelte.
|
|
10
|
+
* only the hydrate core (and the navigation's seeds.ts) reads the slot, and the decoder imports the
|
|
11
|
+
* APP's transport codecs (its own `src/hooks.ts`), which reach Svelte. Installed only in an app that
|
|
12
|
+
* has a remote function at all.
|
|
12
13
|
*/
|
|
14
|
+
const REMOTE_SEED_SELECTOR = "script[type=\"application/ogygia-remote\"]";
|
|
15
|
+
function seed_once() {
|
|
16
|
+
const session = boot_link().runtime_session;
|
|
17
|
+
if (session.remote_seeded) return;
|
|
18
|
+
session.mark_remote_seeded();
|
|
19
|
+
if (typeof document === "undefined") return;
|
|
20
|
+
const text = document.querySelector(REMOTE_SEED_SELECTOR)?.textContent;
|
|
21
|
+
if (text) seed_query_responses(text);
|
|
22
|
+
}
|
|
13
23
|
/** Feature entry: fill the `remoteSeeds` slot. */
|
|
14
24
|
function install() {
|
|
15
25
|
slots.remoteSeeds = {
|
|
26
|
+
seed_once,
|
|
16
27
|
seed_query_responses,
|
|
17
28
|
clear_remote_seeds,
|
|
18
29
|
clear_remote_instances
|
|
@@ -389,6 +389,18 @@ function preflight(doc, dest) {
|
|
|
389
389
|
stamp_region_keys(doc.body);
|
|
390
390
|
page_seed_of(doc);
|
|
391
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Which client build a document belongs to: its runtime script's file and its location map's
|
|
394
|
+
* (server/document-tail.ts `runtime_bootstrap_tags`) — both content-hashed, so a deploy that changed
|
|
395
|
+
* any island, Svelte or the runtime names another. By file name: a `src` written relative to its
|
|
396
|
+
* page reads the same from any address. Null: the document names no runtime (no island on it).
|
|
397
|
+
*/
|
|
398
|
+
function build_of(d) {
|
|
399
|
+
const s = d.querySelector("script[data-ogygia-runtime]");
|
|
400
|
+
if (!s) return null;
|
|
401
|
+
const file = (v) => v ? v.slice(v.lastIndexOf("/") + 1) : "";
|
|
402
|
+
return file(s.getAttribute("src")) + " " + file(s.getAttribute("data-og-locations"));
|
|
403
|
+
}
|
|
392
404
|
async function navigate(r, url, from, { push = true, pop_scroll = null, type = "link", replace = false } = {}) {
|
|
393
405
|
if (!r.run_before(from, url, type)) {
|
|
394
406
|
if (r.nav_target === url.href) r.nav_target = null;
|
|
@@ -460,6 +472,9 @@ async function navigate(r, url, from, { push = true, pop_scroll = null, type = "
|
|
|
460
472
|
if (KitBoot.document_has(doc)) return hand_off();
|
|
461
473
|
const marker = doc.querySelector("meta[name=\"ogygia-router\"]");
|
|
462
474
|
if (!marker) return hand_off();
|
|
475
|
+
const next_build = build_of(doc);
|
|
476
|
+
const this_build = next_build === null ? null : build_of(document);
|
|
477
|
+
if (this_build !== null && next_build !== this_build) return hand_off();
|
|
463
478
|
if (doc.querySelector("meta[name=\"ogygia-first-load\"]")) return hand_off();
|
|
464
479
|
globalThis.__og_restore?.(doc);
|
|
465
480
|
if (dest !== url) replace_state({
|
package/dist/runtime/router.js
CHANGED
|
@@ -2,6 +2,7 @@ import { kit_hydrates_page } from "./kit-boot.js";
|
|
|
2
2
|
import { slots } from "./slots.js";
|
|
3
3
|
import { publish_nav } from "./nav-handle.js";
|
|
4
4
|
import { nearest_attr } from "./nearest-attr.js";
|
|
5
|
+
import { record_server_body } from "./ownership.js";
|
|
5
6
|
// src/runtime/router.ts
|
|
6
7
|
/**
|
|
7
8
|
* Minimal SPA router for ogygia (Astro's ClientRouter equivalent).
|
|
@@ -300,6 +301,8 @@ var SpaRouter = class {
|
|
|
300
301
|
this.#started = true;
|
|
301
302
|
this.doc_key = document_key(new URL(location.href));
|
|
302
303
|
this.current_url = new URL(location.href);
|
|
304
|
+
const body = document.body;
|
|
305
|
+
if (body) record_server_body(body, body, Array.from(body.childNodes));
|
|
303
306
|
document.addEventListener("click", (event) => {
|
|
304
307
|
const anchor = anchor_of(event);
|
|
305
308
|
const url = this.#should_intercept(event, anchor);
|
package/dist/runtime/seeds.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { parse_sidecar_text } from "./wire-format.js";
|
|
1
2
|
import { PageSnapshot } from "../shims/page-store.svelte.js";
|
|
2
3
|
export declare const PAGE_SEED_SELECTOR = "script[type=\"application/ogygia-page\"]";
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The PAGE feature (link/runtime-entry.ts, hydrate phase): what island code sees of its page — the
|
|
6
|
+
* page seed behind `$app/state` / `$app/stores`, the seed references in island props, and, on a
|
|
7
|
+
* Kit-hydrated document, the wait for Kit's own page. Installed only when island code can reach
|
|
8
|
+
* Kit's world (the driver's island-closure walk, failing open on anything it cannot follow): an app
|
|
9
|
+
* whose islands never read their page gets no seed from the server and ships none of this.
|
|
10
|
+
*/
|
|
11
|
+
export declare function install(): void;
|
|
6
12
|
type PageSeed = Partial<Omit<PageSnapshot, "url"> & {
|
|
7
13
|
url: string | URL;
|
|
8
14
|
}>;
|
|
@@ -19,7 +25,6 @@ export declare function page_seed_of(doc: Document): PageSeed | null;
|
|
|
19
25
|
* on first ask), the once-parsed seed for a foreign one. `undefined` when the document has no seed.
|
|
20
26
|
*/
|
|
21
27
|
export declare function seed_data_of(doc: Document): unknown;
|
|
22
|
-
export declare function seed_remote_once(): void;
|
|
23
28
|
/** Document-level page seed (one script from ogygiaHandle) — once per document; then any page facts
|
|
24
29
|
* an answer brought since. Every hydrate calls it, before the island reads `page`. */
|
|
25
30
|
export declare function seed_page_once(): void;
|
|
@@ -47,4 +52,4 @@ export declare function prepare_spa_document(): void;
|
|
|
47
52
|
* already spent (`once`) — that reuse never opens SSE again and leaves "connecting…".
|
|
48
53
|
*/
|
|
49
54
|
export declare function finish_spa_document(): void;
|
|
50
|
-
export {};
|
|
55
|
+
export { parse_sidecar_text };
|
package/dist/runtime/seeds.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parse_sidecar_text, parse_wire_text } from "./wire-format.js";
|
|
2
2
|
import { page_state, reset_page, set_page } from "../shims/page-store.svelte.js";
|
|
3
3
|
import { kit_bridge } from "../shims/kit-bridge.js";
|
|
4
4
|
import { install_page_defer, page_defer_revivers } from "./page-defer.js";
|
|
5
5
|
import { transport_decoders } from "./app-transport.js";
|
|
6
|
+
import { kit_page_thread } from "./kit-page-thread.svelte.js";
|
|
6
7
|
import { boot_link, slots } from "./slots.js";
|
|
7
8
|
// src/runtime/seeds.ts
|
|
8
9
|
/**
|
|
@@ -38,10 +39,19 @@ const session = () => boot_link().runtime_session;
|
|
|
38
39
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
39
40
|
const PAGE_SEED_SELECTOR = "script[type=\"application/ogygia-page\"]";
|
|
40
41
|
const REMOTE_SEED_SELECTOR = "script[type=\"application/ogygia-remote\"]";
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The PAGE feature (link/runtime-entry.ts, hydrate phase): what island code sees of its page — the
|
|
44
|
+
* page seed behind `$app/state` / `$app/stores`, the seed references in island props, and, on a
|
|
45
|
+
* Kit-hydrated document, the wait for Kit's own page. Installed only when island code can reach
|
|
46
|
+
* Kit's world (the driver's island-closure walk, failing open on anything it cannot follow): an app
|
|
47
|
+
* whose islands never read their page gets no seed from the server and ships none of this.
|
|
48
|
+
*/
|
|
49
|
+
function install() {
|
|
50
|
+
slots.page = {
|
|
51
|
+
seed_page_once,
|
|
52
|
+
seed_data_of,
|
|
53
|
+
kit_page_thread
|
|
54
|
+
};
|
|
45
55
|
}
|
|
46
56
|
let parses = 0;
|
|
47
57
|
/** Test seam: how many page-seed scripts were parsed since boot (one per document is the law). */
|
|
@@ -106,12 +116,6 @@ function apply_remote_seed_text(text) {
|
|
|
106
116
|
if (!text) return;
|
|
107
117
|
slots.remoteSeeds?.seed_query_responses(text);
|
|
108
118
|
}
|
|
109
|
-
function seed_remote_once() {
|
|
110
|
-
if (session().remote_seeded) return;
|
|
111
|
-
session().mark_remote_seeded();
|
|
112
|
-
if (typeof document === "undefined") return;
|
|
113
|
-
apply_remote_seed_text(document.querySelector(REMOTE_SEED_SELECTOR)?.textContent);
|
|
114
|
-
}
|
|
115
119
|
/** Document-level page seed (one script from ogygiaHandle) — once per document; then any page facts
|
|
116
120
|
* an answer brought since. Every hydrate calls it, before the island reads `page`. */
|
|
117
121
|
function seed_page_once() {
|
|
@@ -205,4 +209,4 @@ function prepare_spa_document() {
|
|
|
205
209
|
function finish_spa_document() {
|
|
206
210
|
slots.remoteSeeds?.clear_remote_instances();
|
|
207
211
|
}
|
|
208
|
-
export { PAGE_SEED_SELECTOR, apply_soft_invalidate_doc, finish_spa_document, page_seed_of, parse_sidecar_text, prepare_spa_document, seed_data_of, seed_page_once, seed_parse_count
|
|
212
|
+
export { PAGE_SEED_SELECTOR, apply_soft_invalidate_doc, finish_spa_document, install, page_seed_of, parse_sidecar_text, prepare_spa_document, seed_data_of, seed_page_once, seed_parse_count };
|
package/dist/runtime/slots.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { regions_in_shadow } from "./connected.js";
|
|
|
9
9
|
import { yield_task } from "./schedule.js";
|
|
10
10
|
import { beacon_nav } from "./beacon.js";
|
|
11
11
|
import { load_slot } from "./load-scheduler.js";
|
|
12
|
-
import { is_self_owned, walk_enters } from "./ownership.js";
|
|
12
|
+
import { is_self_owned, record_server_body, server_body_attributes, server_body_child, walk_enters } from "./ownership.js";
|
|
13
13
|
import { document_key, jump_to_hash, push_state, replace_state } from "./router.js";
|
|
14
14
|
import { resolve_address } from "./interaction.js";
|
|
15
15
|
import { sync_attributes } from "./morph.js";
|
|
@@ -53,9 +53,11 @@ export type FormOps = {
|
|
|
53
53
|
/** Wake a cold island when interaction lands inside it; returns a disarm fn. */
|
|
54
54
|
export type ArmFn = (el: HTMLElement, fire: () => void) => void | (() => void);
|
|
55
55
|
/** `target: 'walk'` = the hydration repair (the server sequence Svelte's walk binds); default = a live
|
|
56
|
-
* morph toward a new answer. Same ownership rules either way (runtime/ownership.ts).
|
|
56
|
+
* morph toward a new answer. Same ownership rules either way (runtime/ownership.ts). `stays`: a
|
|
57
|
+
* child of `parent` the morph steps over (the page's own, beside the render's). */
|
|
57
58
|
export type MorphFn = (parent: Element, nodes: ArrayLike<Node>, options?: {
|
|
58
59
|
target?: "live" | "walk";
|
|
60
|
+
stays?: (node: Node) => boolean;
|
|
59
61
|
}) => void;
|
|
60
62
|
export type WireOps = {
|
|
61
63
|
/** THE hub key (`OgygiaRef`) — every transportable kind crosses under it. */
|
|
@@ -64,10 +66,22 @@ export type WireOps = {
|
|
|
64
66
|
resolve: (ref: never, remember: boolean) => unknown;
|
|
65
67
|
};
|
|
66
68
|
export type RemoteSeedOps = {
|
|
69
|
+
/** this document's remote seed into the query cache, once per document */
|
|
70
|
+
seed_once(): void;
|
|
67
71
|
seed_query_responses(text: string): void;
|
|
68
72
|
clear_remote_seeds(): void;
|
|
69
73
|
clear_remote_instances(): void;
|
|
70
74
|
};
|
|
75
|
+
/** The page as island code sees it, filled by the `page` feature (./seeds.ts). Null when no island
|
|
76
|
+
* code can read its page: the server ships no seed then, so there is nothing to parse or wait for. */
|
|
77
|
+
export type PageOps = {
|
|
78
|
+
/** the document's page seed into the page store (once), then any page facts answers brought */
|
|
79
|
+
seed_page_once(): void;
|
|
80
|
+
/** the `page.data` a seed reference in island props resolves against */
|
|
81
|
+
seed_data_of(doc: Document): unknown;
|
|
82
|
+
/** on a Kit-hydrated document: the wait for Kit to apply its page (`null` once applied) */
|
|
83
|
+
kit_page_thread(): Promise<void> | null;
|
|
84
|
+
};
|
|
71
85
|
/**
|
|
72
86
|
* The client frame store, filled by the `frames` feature. Core reads it ONLY on the deferred /
|
|
73
87
|
* live / SWR-lake paths (a region with a signed `endpoint`), so a plain load-hydrated app never
|
|
@@ -137,6 +151,10 @@ export type BootLink = {
|
|
|
137
151
|
/** DOM ownership (./ownership.ts): which children Svelte's walk enters, and self-owned elements */
|
|
138
152
|
walk_enters: typeof walk_enters;
|
|
139
153
|
is_self_owned: typeof is_self_owned;
|
|
154
|
+
/** `<body>`'s two owners (./ownership.ts): the router's record of what the server sent it */
|
|
155
|
+
record_server_body: typeof record_server_body;
|
|
156
|
+
server_body_attributes: typeof server_body_attributes;
|
|
157
|
+
server_body_child: typeof server_body_child;
|
|
140
158
|
};
|
|
141
159
|
export type RouterLink = {
|
|
142
160
|
document_key: typeof document_key;
|
|
@@ -156,7 +174,11 @@ export type Slots = {
|
|
|
156
174
|
/** a kept island's host (the live feature fills it beside `live`) */
|
|
157
175
|
keep: Component<Record<string, unknown>> | null;
|
|
158
176
|
wire: WireOps | null;
|
|
177
|
+
/** The transportable classes an island's props, context and seeds need, loaded before it
|
|
178
|
+
* decodes (./wire-classes.ts). Null in an app without transportable classes: nothing to load. */
|
|
179
|
+
wire_classes: ((region: Element) => Promise<unknown> | null) | null;
|
|
159
180
|
remoteSeeds: RemoteSeedOps | null;
|
|
181
|
+
page: PageOps | null;
|
|
160
182
|
frames: FrameOps | null;
|
|
161
183
|
nav: NavOps | null;
|
|
162
184
|
/**
|
package/dist/runtime/slots.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Feature entry: fill the `wire_classes` slot. */
|
|
2
|
+
export declare function install(): void;
|
|
1
3
|
/** The loads this island's props, context and page seeds need, or `null` when none (the common
|
|
2
4
|
* case: an app without transportables, or an island that carries none). */
|
|
3
5
|
export declare function wire_classes_for(region: Element): Promise<unknown> | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { props_sidecar_of } from "./sidecar.js";
|
|
2
2
|
import { yield_task } from "./schedule.js";
|
|
3
|
+
import { slots } from "./slots.js";
|
|
3
4
|
import { wire_all, wire_loaders } from "virtual:ogygia/transportables";
|
|
4
5
|
// src/runtime/wire-classes.ts
|
|
5
6
|
/**
|
|
@@ -14,7 +15,15 @@ import { wire_all, wire_loaders } from "virtual:ogygia/transportables";
|
|
|
14
15
|
* The small texts are checked for each class's tag path (`"<path>#`). An app that sends wired values
|
|
15
16
|
* through Kit's `transport` (`wire_all`: remote functions, loads) gets every class with every island,
|
|
16
17
|
* as before: those values decode synchronously whenever they arrive. Each module loads once.
|
|
18
|
+
*
|
|
19
|
+
* A BOOT-phase feature (link/runtime-entry.ts), installed only when the app has transportable
|
|
20
|
+
* classes: without one the loader map is empty, but the bundler cannot see that through the virtual
|
|
21
|
+
* module, so this code shipped to every island page for a lookup that always found nothing.
|
|
17
22
|
*/
|
|
23
|
+
/** Feature entry: fill the `wire_classes` slot. */
|
|
24
|
+
function install() {
|
|
25
|
+
slots.wire_classes = wire_classes_for;
|
|
26
|
+
}
|
|
18
27
|
const loaders = wire_loaders;
|
|
19
28
|
const paths = Object.keys(loaders);
|
|
20
29
|
const loading = /* @__PURE__ */ new Map();
|
|
@@ -66,4 +75,4 @@ function wire_classes_for(region) {
|
|
|
66
75
|
for (const p of want) pending.push(load(p));
|
|
67
76
|
return Promise.all(pending);
|
|
68
77
|
}
|
|
69
|
-
export { wire_classes_for };
|
|
78
|
+
export { install, wire_classes_for };
|
|
@@ -4,4 +4,7 @@ export declare const WIRE_FORMAT_JSON = "json";
|
|
|
4
4
|
export declare function wire_is_json(el: Element): boolean;
|
|
5
5
|
/** Parse a side-channel's text in its lane: `JSON.parse` for the JSON lane (nothing to revive by
|
|
6
6
|
* construction), devalue `parse` with the caller's revivers otherwise. */
|
|
7
|
-
export declare function parse_wire_text(text: string, json: boolean, revivers?: Record<string, (value: never) => unknown>): unknown;
|
|
7
|
+
export declare function parse_wire_text(text: string, json: boolean, revivers?: Record<string, (value: never) => unknown>): unknown;
|
|
8
|
+
/** A seed or props script's payload, in its lane: JSON when the server marked it plain, devalue
|
|
9
|
+
* with `revivers` otherwise. Every seed / props read goes through here. */
|
|
10
|
+
export declare function parse_sidecar_text(el: Element, revivers?: Record<string, (d: never) => unknown>): unknown;
|
|
@@ -19,4 +19,9 @@ function wire_is_json(el) {
|
|
|
19
19
|
function parse_wire_text(text, json, revivers) {
|
|
20
20
|
return json ? JSON.parse(text) : parse(text, revivers);
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
/** A seed or props script's payload, in its lane: JSON when the server marked it plain, devalue
|
|
23
|
+
* with `revivers` otherwise. Every seed / props read goes through here. */
|
|
24
|
+
function parse_sidecar_text(el, revivers) {
|
|
25
|
+
return parse_wire_text(el.textContent ?? "", wire_is_json(el), revivers);
|
|
26
|
+
}
|
|
27
|
+
export { WIRE_FORMAT_ATTR, WIRE_FORMAT_JSON, parse_sidecar_text, parse_wire_text, wire_is_json };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLIENT half of seed references (seed-refs.ts): the devalue type tag and the reviver that
|
|
3
|
+
* resolves a reference against the page seed. Its own module because the hydrate core is a lazy
|
|
4
|
+
* runtime chunk that shares modules with Kit's client graph (route components compiled for the
|
|
5
|
+
* browser, never loaded on a csr=false page). The bundler keeps a shared module whole — every
|
|
6
|
+
* export ANY importer uses — so while the reviver lived beside the server-side index and planner,
|
|
7
|
+
* every island page downloaded the planner (~4 kB) for the Kit graph's sake. A leaf holds only
|
|
8
|
+
* what the hydrate core runs.
|
|
9
|
+
*/
|
|
10
|
+
/** The devalue type tag. */
|
|
11
|
+
export declare const SEED_REF_KEY = "OgygiaSeedRef";
|
|
12
|
+
export type SeedPath = (string | number)[];
|
|
13
|
+
/** Client: the node a path points at, or `undefined` when the seed does not have it. */
|
|
14
|
+
export declare function resolve_seed_ref(data: unknown, path: SeedPath): unknown;
|
|
15
|
+
/**
|
|
16
|
+
* Client reviver for the devalue type: resolve BY REFERENCE. The island receives the seed's own
|
|
17
|
+
* node — the same object its `page.data` read would hand it, shared exactly the way `page.data`
|
|
18
|
+
* already is between islands. A deep copy per reference (129 of them on a measured page) bought
|
|
19
|
+
* nothing: island props are a snapshot either way, and the DEV mutation guard the runtime wraps
|
|
20
|
+
* every island's props in (`PropMutationGuard`) already warns on a write into them. `get_data` is
|
|
21
|
+
* called lazily, once per reviver — the first reference resolves the seed. A dangling path (no seed,
|
|
22
|
+
* or a seed that does not carry the node) revives to `undefined` and, in dev, says why.
|
|
23
|
+
*/
|
|
24
|
+
export declare function seed_ref_reviver(get_data: () => unknown): (path: SeedPath) => unknown;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/seed-ref-reviver.ts
|
|
2
|
+
/**
|
|
3
|
+
* The CLIENT half of seed references (seed-refs.ts): the devalue type tag and the reviver that
|
|
4
|
+
* resolves a reference against the page seed. Its own module because the hydrate core is a lazy
|
|
5
|
+
* runtime chunk that shares modules with Kit's client graph (route components compiled for the
|
|
6
|
+
* browser, never loaded on a csr=false page). The bundler keeps a shared module whole — every
|
|
7
|
+
* export ANY importer uses — so while the reviver lived beside the server-side index and planner,
|
|
8
|
+
* every island page downloaded the planner (~4 kB) for the Kit graph's sake. A leaf holds only
|
|
9
|
+
* what the hydrate core runs.
|
|
10
|
+
*/
|
|
11
|
+
/** The devalue type tag. */
|
|
12
|
+
const SEED_REF_KEY = "OgygiaSeedRef";
|
|
13
|
+
/** Client: the node a path points at, or `undefined` when the seed does not have it. */
|
|
14
|
+
function resolve_seed_ref(data, path) {
|
|
15
|
+
let cur = data;
|
|
16
|
+
for (const key of path) {
|
|
17
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
18
|
+
cur = cur[key];
|
|
19
|
+
}
|
|
20
|
+
return cur;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Client reviver for the devalue type: resolve BY REFERENCE. The island receives the seed's own
|
|
24
|
+
* node — the same object its `page.data` read would hand it, shared exactly the way `page.data`
|
|
25
|
+
* already is between islands. A deep copy per reference (129 of them on a measured page) bought
|
|
26
|
+
* nothing: island props are a snapshot either way, and the DEV mutation guard the runtime wraps
|
|
27
|
+
* every island's props in (`PropMutationGuard`) already warns on a write into them. `get_data` is
|
|
28
|
+
* called lazily, once per reviver — the first reference resolves the seed. A dangling path (no seed,
|
|
29
|
+
* or a seed that does not carry the node) revives to `undefined` and, in dev, says why.
|
|
30
|
+
*/
|
|
31
|
+
function seed_ref_reviver(get_data) {
|
|
32
|
+
let data;
|
|
33
|
+
let resolved = false;
|
|
34
|
+
return (path) => {
|
|
35
|
+
if (!resolved) {
|
|
36
|
+
data = get_data();
|
|
37
|
+
resolved = true;
|
|
38
|
+
}
|
|
39
|
+
const node = resolve_seed_ref(data, path);
|
|
40
|
+
if (node === void 0 && typeof console !== "undefined" && import.meta.env?.DEV) console.warn(`[ogygia] island props reference page.data at ${JSON.stringify(path)} but the page seed does not carry it — the island receives undefined there.`);
|
|
41
|
+
return node;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export { SEED_REF_KEY, resolve_seed_ref, seed_ref_reviver };
|
package/dist/seed-refs.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const SEED_REF_KEY = "OgygiaSeedRef";
|
|
3
|
-
export type SeedPath = (string | number)[];
|
|
1
|
+
import { SEED_REF_KEY, SeedPath, resolve_seed_ref, seed_ref_reviver } from "./seed-ref-reviver.js";
|
|
4
2
|
export interface SeedIndex {
|
|
5
3
|
/** node → its path from `page.data` (identity matches: the app passed the seed's own object).
|
|
6
4
|
* The path is materialised on the first ask — an indexed node that no island references
|
|
@@ -90,16 +88,4 @@ export interface SeedRefPlan {
|
|
|
90
88
|
* no walk of its own beyond the pointer lookups.
|
|
91
89
|
*/
|
|
92
90
|
export declare function plan_seed_refs(index: SeedIndex, props: unknown, min_bytes?: number): SeedRefPlan;
|
|
93
|
-
|
|
94
|
-
export declare function resolve_seed_ref(data: unknown, path: SeedPath): unknown;
|
|
95
|
-
/**
|
|
96
|
-
* Client reviver for the devalue type: resolve BY REFERENCE. The island receives the seed's own
|
|
97
|
-
* node — the same object its `page.data` read would hand it, shared exactly the way `page.data`
|
|
98
|
-
* already is between islands. A deep copy per reference (129 of them on a measured page) bought
|
|
99
|
-
* nothing: island props are a snapshot either way, and the DEV mutation guard the runtime wraps
|
|
100
|
-
* every island's props in (`PropMutationGuard`) already warns on a write into them. `get_data` is
|
|
101
|
-
* called lazily, once per reviver — the first reference resolves the seed. A dangling path (no seed,
|
|
102
|
-
* or a seed that does not carry the node) revives to `undefined` and, in dev, says why.
|
|
103
|
-
*/
|
|
104
|
-
export declare function seed_ref_reviver(get_data: () => unknown): (path: SeedPath) => unknown;
|
|
105
|
-
export {};
|
|
91
|
+
export { SEED_REF_KEY, type SeedPath, resolve_seed_ref, seed_ref_reviver };
|
package/dist/seed-refs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fnv1a } from "./runtime/hash.js";
|
|
2
|
+
import { SEED_REF_KEY, resolve_seed_ref, seed_ref_reviver } from "./seed-ref-reviver.js";
|
|
2
3
|
// src/seed-refs.ts
|
|
3
4
|
/**
|
|
4
5
|
* SEED REFERENCES — island props that point into the page seed instead of copying it.
|
|
@@ -43,8 +44,6 @@ import { fnv1a } from "./runtime/hash.js";
|
|
|
43
44
|
* Universal module (no Node imports): the index + reducer run on the server, the resolver on the
|
|
44
45
|
* client, and the unit tests exercise both ends against a real devalue round trip.
|
|
45
46
|
*/
|
|
46
|
-
/** The devalue type tag. */
|
|
47
|
-
const SEED_REF_KEY = "OgygiaSeedRef";
|
|
48
47
|
/** A node the codec may reference: a plain object or an array (own prototype only). */
|
|
49
48
|
function is_plain(v) {
|
|
50
49
|
if (v === null || typeof v !== "object") return false;
|
|
@@ -453,35 +452,4 @@ function plan_seed_refs(index, props, min_bytes = 96) {
|
|
|
453
452
|
keys
|
|
454
453
|
};
|
|
455
454
|
}
|
|
456
|
-
/** Client: the node a path points at, or `undefined` when the seed does not have it. */
|
|
457
|
-
function resolve_seed_ref(data, path) {
|
|
458
|
-
let cur = data;
|
|
459
|
-
for (const key of path) {
|
|
460
|
-
if (cur === null || typeof cur !== "object") return void 0;
|
|
461
|
-
cur = cur[key];
|
|
462
|
-
}
|
|
463
|
-
return cur;
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Client reviver for the devalue type: resolve BY REFERENCE. The island receives the seed's own
|
|
467
|
-
* node — the same object its `page.data` read would hand it, shared exactly the way `page.data`
|
|
468
|
-
* already is between islands. A deep copy per reference (129 of them on a measured page) bought
|
|
469
|
-
* nothing: island props are a snapshot either way, and the DEV mutation guard the runtime wraps
|
|
470
|
-
* every island's props in (`PropMutationGuard`) already warns on a write into them. `get_data` is
|
|
471
|
-
* called lazily, once per reviver — the first reference resolves the seed. A dangling path (no seed,
|
|
472
|
-
* or a seed that does not carry the node) revives to `undefined` and, in dev, says why.
|
|
473
|
-
*/
|
|
474
|
-
function seed_ref_reviver(get_data) {
|
|
475
|
-
let data;
|
|
476
|
-
let resolved = false;
|
|
477
|
-
return (path) => {
|
|
478
|
-
if (!resolved) {
|
|
479
|
-
data = get_data();
|
|
480
|
-
resolved = true;
|
|
481
|
-
}
|
|
482
|
-
const node = resolve_seed_ref(data, path);
|
|
483
|
-
if (node === void 0 && typeof console !== "undefined" && import.meta.env?.DEV) console.warn(`[ogygia] island props reference page.data at ${JSON.stringify(path)} but the page seed does not carry it — the island receives undefined there.`);
|
|
484
|
-
return node;
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
455
|
export { SEED_REF_KEY, analyze, deep_equal_plain, index_seed, json_culprit, plan_seed_refs, resolve_seed_ref, seed_ref_reviver, set_measure_memo_reader };
|
|
@@ -41,9 +41,10 @@ export declare function modulepreload_tag(href: string): string;
|
|
|
41
41
|
* module's imports after downloading and parsing it; hinted here, they download ALONGSIDE the
|
|
42
42
|
* runtime instead of one round trip after it. Normal priority — the runtime cannot run without
|
|
43
43
|
* them. `data-ogygia-runtime-dep` keeps them attached to the script when the handle moves it
|
|
44
|
-
* (head-presence.ts `runtime_first`).
|
|
44
|
+
* (head-presence.ts `runtime_first`). `locations`: the build's location map, which the runtime reads
|
|
45
|
+
* for a load that arrives with an identity alone (runtime/entry-locations.ts).
|
|
45
46
|
*/
|
|
46
|
-
export declare function runtime_bootstrap_tags(src: string, deps: readonly string[]): string;
|
|
47
|
+
export declare function runtime_bootstrap_tags(src: string, deps: readonly string[], locations?: string | null): string;
|
|
47
48
|
/**
|
|
48
49
|
* The island graph (island-graph.ts) as one inert JSON script: entry → the chunks its code needs.
|
|
49
50
|
* The runtime turns an entry's list into modulepreload links when that island wakes. Empty map →
|
|
@@ -33,10 +33,11 @@ function modulepreload_tag(href) {
|
|
|
33
33
|
* module's imports after downloading and parsing it; hinted here, they download ALONGSIDE the
|
|
34
34
|
* runtime instead of one round trip after it. Normal priority — the runtime cannot run without
|
|
35
35
|
* them. `data-ogygia-runtime-dep` keeps them attached to the script when the handle moves it
|
|
36
|
-
* (head-presence.ts `runtime_first`).
|
|
36
|
+
* (head-presence.ts `runtime_first`). `locations`: the build's location map, which the runtime reads
|
|
37
|
+
* for a load that arrives with an identity alone (runtime/entry-locations.ts).
|
|
37
38
|
*/
|
|
38
|
-
function runtime_bootstrap_tags(src, deps) {
|
|
39
|
-
let html = "<script type=\"module\" data-ogygia-runtime src=\"" + src + "\"><\/script>";
|
|
39
|
+
function runtime_bootstrap_tags(src, deps, locations = null) {
|
|
40
|
+
let html = "<script type=\"module\" data-ogygia-runtime" + (locations ? " data-og-locations=\"" + locations + "\"" : "") + " src=\"" + src + "\"><\/script>";
|
|
40
41
|
for (const dep of deps) html += "<link rel=\"modulepreload\" href=\"" + dep + "\" data-ogygia-runtime-dep>";
|
|
41
42
|
return html;
|
|
42
43
|
}
|