@ogygia/core 0.11.4 → 0.11.5
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/SlotBoundary.svelte +1 -1
- package/dist/compiler/driver.js +53 -11
- package/dist/compiler/ids.d.ts +3 -0
- package/dist/compiler/ids.js +4 -1
- package/dist/compiler/index.d.ts +2 -2
- package/dist/compiler/index.js +2 -2
- package/dist/compiler/kit.d.ts +7 -0
- package/dist/compiler/kit.js +22 -10
- package/dist/compiler/link/runtime-entry.d.ts +19 -2
- package/dist/compiler/link/runtime-entry.js +34 -1
- package/dist/compiler/program.d.ts +4 -0
- package/dist/compiler/program.js +5 -1
- package/dist/context.d.ts +3 -12
- package/dist/context.js +1 -16
- 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/core.js +7 -5
- 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/nav-handle.d.ts +3 -0
- package/dist/runtime/nav-handle.js +12 -5
- package/dist/runtime/region-ids.js +2 -2
- package/dist/runtime/remote-seeds.js +14 -3
- package/dist/runtime/seeds.d.ts +10 -5
- package/dist/runtime/seeds.js +16 -12
- package/dist/runtime/slots.d.ts +16 -0
- 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/props-wire.js +2 -1
- package/package.json +3 -3
package/dist/nested.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
// src/nested.ts
|
|
3
|
+
const NESTED_KEY = Symbol.for("ogygia.nested-island");
|
|
4
|
+
/**
|
|
5
|
+
* Mark the current subtree's hydration state. `true` = inside a hydrated island (nested islands
|
|
6
|
+
* degrade). `false` = a LAKE resets its subtree to "dead", so an island inside the lake self-
|
|
7
|
+
* hydrates again (the nearest-boundary rule — DESIGN.md).
|
|
8
|
+
*/
|
|
9
|
+
function setNested(value = true) {
|
|
10
|
+
setContext(NESTED_KEY, value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* True when an ancestor island wrapper already marked the subtree. `getContext` returns `undefined`
|
|
14
|
+
* when no ancestor set it (a top-level island) — that absence is exactly "not nested".
|
|
15
|
+
*/
|
|
16
|
+
function isNested() {
|
|
17
|
+
return getContext(NESTED_KEY) === true;
|
|
18
|
+
}
|
|
19
|
+
export { isNested, setNested };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
3
|
+
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
4
|
+
* `resolve()` (ref.ts) calls the current sink with each id it freshly remembers in the page scope;
|
|
5
|
+
* the runtime's sink attributes it to the island currently hydrating (region → id-set), so a
|
|
6
|
+
* reconcile nav can dispose exactly the ids owned by REMOVED regions. Null when no island is
|
|
7
|
+
* hydrating.
|
|
8
|
+
*
|
|
9
|
+
* A leaf of its own (ref.ts re-exports the setter): the hydrate core installs the sink around every
|
|
10
|
+
* island, wired values or not, and shares modules with Kit's client graph, which uses the rest of
|
|
11
|
+
* the hub. A shared module ships whole, so reaching the sink through ref.ts sent every island page
|
|
12
|
+
* the hub's registry and minting.
|
|
13
|
+
*/
|
|
14
|
+
export declare let resolve_sink: ((id: string) => void) | null;
|
|
15
|
+
export declare function set_resolve_sink(fn: ((id: string) => void) | null): void;
|
package/dist/ref-sink.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/ref-sink.ts
|
|
2
|
+
/**
|
|
3
|
+
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
4
|
+
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
5
|
+
* `resolve()` (ref.ts) calls the current sink with each id it freshly remembers in the page scope;
|
|
6
|
+
* the runtime's sink attributes it to the island currently hydrating (region → id-set), so a
|
|
7
|
+
* reconcile nav can dispose exactly the ids owned by REMOVED regions. Null when no island is
|
|
8
|
+
* hydrating.
|
|
9
|
+
*
|
|
10
|
+
* A leaf of its own (ref.ts re-exports the setter): the hydrate core installs the sink around every
|
|
11
|
+
* island, wired values or not, and shares modules with Kit's client graph, which uses the rest of
|
|
12
|
+
* the hub. A shared module ships whole, so reaching the sink through ref.ts sent every island page
|
|
13
|
+
* the hub's registry and minting.
|
|
14
|
+
*/
|
|
15
|
+
let resolve_sink = null;
|
|
16
|
+
function set_resolve_sink(fn) {
|
|
17
|
+
resolve_sink = fn;
|
|
18
|
+
}
|
|
19
|
+
export { resolve_sink, set_resolve_sink };
|
package/dist/ref.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { set_resolve_sink } from "./ref-sink.js";
|
|
1
2
|
import { batch, notify, watch, watcher_count } from "./ref-watch.js";
|
|
2
3
|
/**
|
|
3
4
|
* THE HUB — ogygia's identity layer. One primitive carries everything that crosses an
|
|
@@ -78,7 +79,6 @@ export declare function register_kind(kind: RefKind): void;
|
|
|
78
79
|
* silently mis-read as one). Same instance → same id, always.
|
|
79
80
|
*/
|
|
80
81
|
export declare function mint(value: unknown, only?: ReadonlySet<string>): Ref | undefined;
|
|
81
|
-
export declare function set_resolve_sink(fn: ((id: string) => void) | null): void;
|
|
82
82
|
/**
|
|
83
83
|
* DISPOSE a whole scope (hub v2, phase D) — tear down every instance in its bucket via the
|
|
84
84
|
* instance's `kind.dispose`, run any registered scope-disposers (side-effect cleanups like the
|
|
@@ -121,4 +121,4 @@ export declare const REF_WIRE_KEY = "OgygiaRef";
|
|
|
121
121
|
export declare function ref_reducer(only?: ReadonlySet<string>): (value: unknown) => Ref | undefined;
|
|
122
122
|
/** Devalue reviver for a seam. Browser passes `remember: true`; the server must pass `false`. */
|
|
123
123
|
export declare function ref_reviver(remember: boolean): (ref: Ref) => unknown;
|
|
124
|
-
export { batch, notify, watch, watcher_count };
|
|
124
|
+
export { batch, notify, set_resolve_sink, watch, watcher_count };
|
package/dist/ref.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { emit } from "./events/bus.js";
|
|
2
2
|
import { batch, notify, watch, watcher_count, watchers } from "./ref-watch.js";
|
|
3
|
+
import { resolve_sink, set_resolve_sink } from "./ref-sink.js";
|
|
3
4
|
// src/ref.ts
|
|
4
5
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
5
6
|
const REGISTRY_KEY = Symbol.for("ogygia.hub");
|
|
@@ -62,17 +63,6 @@ function mint(value, only) {
|
|
|
62
63
|
return ref;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
|
-
/**
|
|
66
|
-
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
67
|
-
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
68
|
-
* `resolve()` calls the current sink with each id it freshly remembers in the page scope; the
|
|
69
|
-
* runtime's sink attributes it to the island currently hydrating (region → id-set), so a reconcile
|
|
70
|
-
* nav can dispose exactly the ids owned by REMOVED regions. Null when no island is hydrating.
|
|
71
|
-
*/
|
|
72
|
-
let resolve_sink = null;
|
|
73
|
-
function set_resolve_sink(fn) {
|
|
74
|
-
resolve_sink = fn;
|
|
75
|
-
}
|
|
76
66
|
/** The live bucket for a scope (never 'request' — that scope is never stored). */
|
|
77
67
|
function bucket(reg, scope) {
|
|
78
68
|
let b = reg.instances.get(scope);
|
package/dist/runtime/core.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { frameAddress } from "../frame.js";
|
|
2
2
|
import { kit_hydrates_page } from "./kit-boot.js";
|
|
3
3
|
import { init_shadow_registries, parse_region_html, restore_adopt_sheets, restore_markup } from "./parse-html.js";
|
|
4
|
-
import { wire_classes_for } from "./wire-classes.js";
|
|
5
4
|
import { mark_region_changed, region_changed, unwatch_region, watch_region } from "./drift-watch.js";
|
|
6
5
|
import { runtime_session } from "./session.js";
|
|
7
6
|
import { RegionAnswerRefused, capability_expired, deny_of, is_allowed_region_endpoint, is_document_answer, is_redirected_answer, is_same_origin_response, island_module_url, renewal_url } from "./region-endpoint-url.js";
|
|
@@ -10,7 +9,6 @@ import { is_awake, is_deferred, is_frozen, ours_on_kit_document, phase2_hydrate_
|
|
|
10
9
|
import { slots } from "./slots.js";
|
|
11
10
|
import { KEEP_FALLBACK_HTML } from "../keep-fallback-marker.js";
|
|
12
11
|
import { PAGE_FACTS_SELECTOR } from "../page-facts-script.js";
|
|
13
|
-
import { NAV_HANDLE_KEY, mpa_nav, publish_nav } from "./nav-handle.js";
|
|
14
12
|
import { link_boot } from "./boot-link.js";
|
|
15
13
|
import { background_start, hydrate_settled, hydrate_started, hydrate_turn, register_region, unregister_region, yield_task } from "./schedule.js";
|
|
16
14
|
import { once_visible } from "./observe.js";
|
|
@@ -24,6 +22,7 @@ import { on_svelte_warning, set_hydrating, tap_svelte_warnings } from "./svelte-
|
|
|
24
22
|
import { emit } from "../events/bus.js";
|
|
25
23
|
import { ingest_server_events, install_window_sink } from "../events/sinks.js";
|
|
26
24
|
import { CLIENT_PLUGINS, boot_client_plugins, client_region_mount, client_region_unmount } from "./client-plugins.js";
|
|
25
|
+
import { FRAMES } from "virtual:ogygia/runtime-flags";
|
|
27
26
|
// src/runtime/core.ts
|
|
28
27
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
29
28
|
const BEACON = typeof __OGYGIA_BEACON__ !== "undefined" ? __OGYGIA_BEACON__ : true;
|
|
@@ -353,7 +352,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
353
352
|
connected_regions.add(this);
|
|
354
353
|
pin_region_urls(this);
|
|
355
354
|
if (this.hasAttribute("live")) return;
|
|
356
|
-
if (this.#minted_endpoint === null && is_deferred(this)) {
|
|
355
|
+
if (FRAMES && this.#minted_endpoint === null && is_deferred(this)) {
|
|
357
356
|
const minted = this.getAttribute("endpoint");
|
|
358
357
|
if (minted) this.#minted_endpoint = minted;
|
|
359
358
|
else {
|
|
@@ -427,6 +426,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
427
426
|
* nothing left to warm.
|
|
428
427
|
*/
|
|
429
428
|
#prefetch_html() {
|
|
429
|
+
if (!FRAMES) return;
|
|
430
430
|
if (this.#done || this.#fetching || !this.isConnected) return;
|
|
431
431
|
const endpoint = this.#endpoint();
|
|
432
432
|
if (!endpoint || !is_allowed_region_endpoint(endpoint)) return;
|
|
@@ -592,6 +592,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
592
592
|
* island), schedule phase-2 hydrate — coalescing matching schedules to immediate load.
|
|
593
593
|
*/
|
|
594
594
|
async #server() {
|
|
595
|
+
if (!FRAMES) return;
|
|
595
596
|
const endpoint = this.#endpoint();
|
|
596
597
|
if (endpoint && !this.#frame_unsub) {
|
|
597
598
|
const address = this.#frame_address = frameAddress(endpoint);
|
|
@@ -718,6 +719,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
718
719
|
* not (the endpoint answers `cache-control: private, max-age=30`, and stale is the whole point).
|
|
719
720
|
*/
|
|
720
721
|
async #fetch_html(opts = {}) {
|
|
722
|
+
if (!FRAMES) return;
|
|
721
723
|
if (this.#fetching || this.#done && !opts.revalidate) return;
|
|
722
724
|
const endpoint = this.#endpoint();
|
|
723
725
|
if (!endpoint) return;
|
|
@@ -879,7 +881,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
879
881
|
[mod, core] = await Promise.all([
|
|
880
882
|
load_island(entry, cls()),
|
|
881
883
|
hydrate_core(),
|
|
882
|
-
|
|
884
|
+
slots.wire_classes?.(this)
|
|
883
885
|
]);
|
|
884
886
|
} finally {
|
|
885
887
|
release();
|
|
@@ -952,6 +954,7 @@ var OgygiaRegion = class OgygiaRegion extends HTMLElement {
|
|
|
952
954
|
* - **Later tick, module changed:** unmount, swap, re-hydrate.
|
|
953
955
|
*/
|
|
954
956
|
async applyLive(desc) {
|
|
957
|
+
if (!FRAMES) return;
|
|
955
958
|
this.setAttribute("render", "defer");
|
|
956
959
|
this.setAttribute("entry", desc.module || desc.id);
|
|
957
960
|
if (desc.url) this.setAttribute("endpoint", desc.url);
|
|
@@ -1122,7 +1125,6 @@ function apply_dev_head_region_css() {
|
|
|
1122
1125
|
});
|
|
1123
1126
|
}
|
|
1124
1127
|
function boot(installers = [], client_plugins = []) {
|
|
1125
|
-
if (typeof document !== "undefined" && !(NAV_HANDLE_KEY in globalThis)) publish_nav(mpa_nav());
|
|
1126
1128
|
runtime_session.adopt_document_head();
|
|
1127
1129
|
link_boot();
|
|
1128
1130
|
for (const install of installers) install();
|
package/dist/runtime/full.js
CHANGED
|
@@ -5,13 +5,15 @@ import { install as install$2 } from "./morph.js";
|
|
|
5
5
|
import { install as install$3 } from "./interaction.js";
|
|
6
6
|
import { install as install$4 } from "./form-continuity.js";
|
|
7
7
|
import { install as install$5 } from "./router.js";
|
|
8
|
+
import { install as install$6 } from "./wire-classes.js";
|
|
9
|
+
import { install as install$7 } from "./nav-handle.js";
|
|
8
10
|
// src/runtime/full.ts
|
|
9
11
|
/**
|
|
10
12
|
* Kitchen-sink browser runtime — every BOOT-phase feature installed. The dev / no-marks entry: the
|
|
11
13
|
* ogygia compiler injects `bootDev()` (see the plugin's `virtual:ogygia-runtime`); a per-app production
|
|
12
14
|
* build instead emits a generated entry (see `vite/runtime-entry.ts`) that boots only the features it
|
|
13
15
|
* uses. Both boot through {@link ./core.js core} in the same {@link ../vite/runtime-entry.js FEATURE_ORDER}.
|
|
14
|
-
* HYDRATE-phase features (context, live, wire, remote-seeds) are never in the boot, dev included: the
|
|
16
|
+
* HYDRATE-phase features (context, live, wire, remote-seeds, page) are never in the boot, dev included: the
|
|
15
17
|
* hydrate core installs them from `virtual:ogygia/hydrate-features` (every one, in dev).
|
|
16
18
|
*
|
|
17
19
|
* Exposed as an explicit FUNCTION, not a top-level side effect: the compiler always calls it, so the
|
|
@@ -21,11 +23,13 @@ import { install as install$5 } from "./router.js";
|
|
|
21
23
|
* Idempotent via {@link ./core.js boot}. */
|
|
22
24
|
function bootDev(client_plugins = []) {
|
|
23
25
|
boot([
|
|
26
|
+
install$6,
|
|
24
27
|
install,
|
|
25
28
|
install$1,
|
|
26
29
|
install$2,
|
|
27
30
|
install$3,
|
|
28
31
|
install$4,
|
|
32
|
+
install$7,
|
|
29
33
|
install$5
|
|
30
34
|
], client_plugins);
|
|
31
35
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { seed_page_once, seed_remote_once } from "./seeds.js";
|
|
2
|
-
import { kit_page_thread } from "./kit-page-thread.svelte.js";
|
|
3
1
|
import { Component } from "svelte";
|
|
2
|
+
export declare const seed_page_once: () => void;
|
|
3
|
+
export declare const seed_remote_once: () => void;
|
|
4
|
+
export declare const kit_page_thread: () => Promise<void> | null;
|
|
4
5
|
/** A hydrated island as the element holds it: how to tear it down, and (keep / live hosts) how to
|
|
5
6
|
* push the next page's props into it. */
|
|
6
7
|
export type IslandHandle = {
|
|
@@ -97,5 +98,4 @@ export declare function hydrate_island(region: HTMLElement, entry: string, mod:
|
|
|
97
98
|
* HTML comes from svelte `render()` (both envelope layers) — same as the deferred swap path, so it
|
|
98
99
|
* is NOT wrapped in extra `[..]` hydration comments. `null` when the live feature is absent.
|
|
99
100
|
*/
|
|
100
|
-
export declare function hydrate_live(region: HTMLElement, entry: string, mod: IslandModule, props: Record<string, unknown>): IslandHandle | null;
|
|
101
|
-
export { kit_page_thread, seed_page_once, seed_remote_once };
|
|
101
|
+
export declare function hydrate_live(region: HTMLElement, entry: string, mod: IslandModule, props: Record<string, unknown>): IslandHandle | null;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { set_current_region, set_foreign_hydrate } from "../current-region.js";
|
|
2
2
|
import { capture_region_ids } from "./region-ids.js";
|
|
3
3
|
import { foreign_region_prop_revivers } from "./foreign-props.js";
|
|
4
|
-
import { SEED_REF_KEY, seed_ref_reviver } from "../seed-
|
|
5
|
-
import { parse_sidecar_text
|
|
4
|
+
import { SEED_REF_KEY, seed_ref_reviver } from "../seed-ref-reviver.js";
|
|
5
|
+
import { parse_sidecar_text } from "./wire-format.js";
|
|
6
6
|
import { is_deferred, ours_on_kit_document, region_ssr_truncated } from "./region-attrs.js";
|
|
7
7
|
import { boot_link, slots } from "./slots.js";
|
|
8
8
|
import { keep_host_classes, note_host_classes } from "./host-classes.js";
|
|
9
9
|
import { emit } from "../events/bus.js";
|
|
10
|
-
import { kit_page_thread } from "./kit-page-thread.svelte.js";
|
|
11
10
|
import { hydrate, unmount } from "svelte";
|
|
12
11
|
import NestedProvider from "../NestedProvider.svelte";
|
|
13
12
|
import { install } from "virtual:ogygia/hydrate-features";
|
|
@@ -24,6 +23,9 @@ import { install } from "virtual:ogygia/hydrate-features";
|
|
|
24
23
|
* Stateless towards the element: the element owns its handle (`IslandHandle`) and its flags; this
|
|
25
24
|
* module owns the how.
|
|
26
25
|
*/
|
|
26
|
+
const seed_page_once = () => slots.page?.seed_page_once();
|
|
27
|
+
const seed_remote_once = () => slots.remoteSeeds?.seed_once();
|
|
28
|
+
const kit_page_thread = () => slots.page?.kit_page_thread() ?? null;
|
|
27
29
|
install();
|
|
28
30
|
const kit_hydrates_page = () => boot_link().kit_hydrates_page();
|
|
29
31
|
const props_sidecar_of = (region) => boot_link().props_sidecar_of(region);
|
|
@@ -335,9 +337,10 @@ function read_region_props(region, foreign = false) {
|
|
|
335
337
|
if (!sidecar) return {};
|
|
336
338
|
const base = foreign ? foreign_region_prop_revivers() : region_prop_revivers();
|
|
337
339
|
const doc = sidecar.ownerDocument;
|
|
338
|
-
const
|
|
340
|
+
const page = slots.page;
|
|
341
|
+
const revivers = foreign || !page ? base : {
|
|
339
342
|
...base,
|
|
340
|
-
[SEED_REF_KEY]: seed_ref_reviver(() => seed_data_of(doc))
|
|
343
|
+
[SEED_REF_KEY]: seed_ref_reviver(() => page.seed_data_of(doc))
|
|
341
344
|
};
|
|
342
345
|
return parse_sidecar_text(sidecar, revivers);
|
|
343
346
|
}
|
|
@@ -16,6 +16,9 @@ export type NavHandle = {
|
|
|
16
16
|
afterNavigate(fn: AfterNavigateCallback): () => void;
|
|
17
17
|
bust_page_cache(): void;
|
|
18
18
|
};
|
|
19
|
+
/** Feature entry (`mpa-nav`, boot phase): the MPA handle, first — the router's install, later in
|
|
20
|
+
* the feature order, replaces it. */
|
|
21
|
+
export declare function install(): void;
|
|
19
22
|
/** Publish the document's navigation handle (the router's replaces the MPA one). */
|
|
20
23
|
export declare function publish_nav(handle: NavHandle): void;
|
|
21
24
|
/**
|
|
@@ -13,16 +13,23 @@ import { speculate_url } from "./speculate-hint.js";
|
|
|
13
13
|
* from the global realm (the same pattern as the Kit page bridge, `Symbol.for('ogygia.kit-page')`),
|
|
14
14
|
* and `test/runtime-boot-svelte-free.test.ts` pins that no island-side module imports a boot module.
|
|
15
15
|
*
|
|
16
|
-
* Who publishes it,
|
|
16
|
+
* Who publishes it, on a document ogygia boots:
|
|
17
17
|
* - the ROUTER feature (./router.ts `install`) — the SPA router's API;
|
|
18
|
-
* - otherwise
|
|
19
|
-
* Speculation Rules hints, and `invalidateAll` is still the soft seed
|
|
20
|
-
* loaded on first use) — exactly what `router: false` apps got before.
|
|
18
|
+
* - otherwise the `mpa-nav` feature ({@link install}) publishes {@link mpa_nav}: the browser
|
|
19
|
+
* navigates, preloads become Speculation Rules hints, and `invalidateAll` is still the soft seed
|
|
20
|
+
* refresh (./router-nav.ts, loaded on first use) — exactly what `router: false` apps got before.
|
|
21
|
+
* Installed only when island code can reach the handle (the `page` mark: `$app/navigation`,
|
|
22
|
+
* `ogygia/app`, or anything the build could not follow): with none of those, nothing reads it.
|
|
21
23
|
*
|
|
22
24
|
* Island code reads it as `globalThis[Symbol.for('ogygia.nav')]` (the key is repeated there on
|
|
23
25
|
* purpose: importing {@link NAV_HANDLE_KEY} would be the very edge this module exists to avoid).
|
|
24
26
|
*/
|
|
25
27
|
const NAV_HANDLE_KEY = Symbol.for("ogygia.nav");
|
|
28
|
+
/** Feature entry (`mpa-nav`, boot phase): the MPA handle, first — the router's install, later in
|
|
29
|
+
* the feature order, replaces it. */
|
|
30
|
+
function install() {
|
|
31
|
+
if (typeof document !== "undefined" && !(NAV_HANDLE_KEY in globalThis)) publish_nav(mpa_nav());
|
|
32
|
+
}
|
|
26
33
|
/** Publish the document's navigation handle (the router's replaces the MPA one). */
|
|
27
34
|
function publish_nav(handle) {
|
|
28
35
|
globalThis[NAV_HANDLE_KEY] = handle;
|
|
@@ -68,4 +75,4 @@ function mpa_nav() {
|
|
|
68
75
|
bust_page_cache() {}
|
|
69
76
|
};
|
|
70
77
|
}
|
|
71
|
-
export { NAV_HANDLE_KEY, mpa_nav, publish_nav };
|
|
78
|
+
export { NAV_HANDLE_KEY, install, mpa_nav, publish_nav };
|
|
@@ -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
|
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
|
@@ -64,10 +64,22 @@ export type WireOps = {
|
|
|
64
64
|
resolve: (ref: never, remember: boolean) => unknown;
|
|
65
65
|
};
|
|
66
66
|
export type RemoteSeedOps = {
|
|
67
|
+
/** this document's remote seed into the query cache, once per document */
|
|
68
|
+
seed_once(): void;
|
|
67
69
|
seed_query_responses(text: string): void;
|
|
68
70
|
clear_remote_seeds(): void;
|
|
69
71
|
clear_remote_instances(): void;
|
|
70
72
|
};
|
|
73
|
+
/** The page as island code sees it, filled by the `page` feature (./seeds.ts). Null when no island
|
|
74
|
+
* code can read its page: the server ships no seed then, so there is nothing to parse or wait for. */
|
|
75
|
+
export type PageOps = {
|
|
76
|
+
/** the document's page seed into the page store (once), then any page facts answers brought */
|
|
77
|
+
seed_page_once(): void;
|
|
78
|
+
/** the `page.data` a seed reference in island props resolves against */
|
|
79
|
+
seed_data_of(doc: Document): unknown;
|
|
80
|
+
/** on a Kit-hydrated document: the wait for Kit to apply its page (`null` once applied) */
|
|
81
|
+
kit_page_thread(): Promise<void> | null;
|
|
82
|
+
};
|
|
71
83
|
/**
|
|
72
84
|
* The client frame store, filled by the `frames` feature. Core reads it ONLY on the deferred /
|
|
73
85
|
* live / SWR-lake paths (a region with a signed `endpoint`), so a plain load-hydrated app never
|
|
@@ -156,7 +168,11 @@ export type Slots = {
|
|
|
156
168
|
/** a kept island's host (the live feature fills it beside `live`) */
|
|
157
169
|
keep: Component<Record<string, unknown>> | null;
|
|
158
170
|
wire: WireOps | null;
|
|
171
|
+
/** The transportable classes an island's props, context and seeds need, loaded before it
|
|
172
|
+
* decodes (./wire-classes.ts). Null in an app without transportable classes: nothing to load. */
|
|
173
|
+
wire_classes: ((region: Element) => Promise<unknown> | null) | null;
|
|
159
174
|
remoteSeeds: RemoteSeedOps | null;
|
|
175
|
+
page: PageOps | null;
|
|
160
176
|
frames: FrameOps | null;
|
|
161
177
|
nav: NavOps | null;
|
|
162
178
|
/**
|
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 };
|