@ogygia/core 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LateIsland.svelte +3 -2
- package/dist/LateIslandAwait.svelte +6 -2
- package/dist/Region.svelte +22 -33
- package/dist/boundary.d.ts +0 -2
- package/dist/boundary.js +8 -6
- package/dist/compiler/debarrel/barrel.d.ts +15 -8
- package/dist/compiler/debarrel/barrel.js +92 -45
- package/dist/compiler/debarrel/plugin.js +41 -17
- package/dist/compiler/dev/dev-hmr.d.ts +3 -0
- package/dist/compiler/dev/dev-hmr.js +3 -0
- package/dist/compiler/driver.js +11 -4
- package/dist/compiler/flags.js +21 -7
- package/dist/compiler/fouc-css.js +5 -1
- package/dist/compiler/kit.js +5 -1
- package/dist/compiler/link/build-output.js +12 -3
- package/dist/compiler/macros/bake.js +18 -4
- package/dist/compiler/macros/dollar.js +6 -1
- package/dist/compiler/macros/source.js +6 -1
- package/dist/compiler/macros/store.js +10 -2
- package/dist/compiler/macros/wire.js +6 -1
- package/dist/compiler/parse/scan.js +10 -3
- package/dist/compiler/region/transform.js +188 -74
- package/dist/context-bridge.js +13 -5
- package/dist/context.d.ts +9 -0
- package/dist/context.js +30 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.js +7 -1
- package/dist/early-holes-head.d.ts +5 -0
- package/dist/early-holes-head.js +20 -0
- package/dist/early-holes.d.ts +34 -0
- package/dist/early-holes.js +51 -0
- package/dist/entry-hydrate.js +9 -12
- package/dist/events/bus.js +7 -2
- package/dist/events/schema.d.ts +2 -2
- package/dist/fn-transport.js +4 -3
- package/dist/hooks.js +94 -163
- package/dist/insights/page-assets.js +11 -3
- package/dist/insights/page-insights.d.ts +0 -2
- package/dist/insights/page-insights.js +14 -10
- package/dist/insights/session-insights.js +2 -2
- package/dist/internal-instruments.d.ts +3 -1
- package/dist/internal-instruments.js +3 -1
- package/dist/internal-server.d.ts +3 -2
- package/dist/internal-server.js +3 -2
- package/dist/internal.d.ts +1 -2
- package/dist/internal.js +1 -2
- package/dist/keep-fallback.d.ts +0 -2
- package/dist/keep-fallback.js +4 -5
- package/dist/live-transport.js +3 -2
- package/dist/messages/build.d.ts +585 -0
- package/dist/messages/build.js +800 -0
- package/dist/messages/core.d.ts +464 -0
- package/dist/messages/core.js +843 -0
- package/dist/messages/define.d.ts +62 -0
- package/dist/messages/define.js +49 -0
- package/dist/messages/index.d.ts +6 -0
- package/dist/messages/index.js +6 -0
- package/dist/messages/island.d.ts +4 -0
- package/dist/messages/island.js +34 -0
- package/dist/messages/lazy.d.ts +3 -0
- package/dist/messages/lazy.js +22 -0
- package/dist/messages/prose.d.ts +15 -0
- package/dist/messages/prose.js +43 -0
- package/dist/messages/server.d.ts +142 -0
- package/dist/messages/server.js +246 -0
- package/dist/nested.d.ts +3 -0
- package/dist/nested.js +8 -1
- package/dist/plugin/css.js +14 -13
- package/dist/plugin/facts.d.ts +2 -0
- package/dist/plugin/facts.js +10 -6
- package/dist/plugin/hooks.d.ts +7 -17
- package/dist/plugin/hooks.js +32 -4
- package/dist/plugin/index.d.ts +9 -2
- package/dist/plugin/request-scope.d.ts +20 -0
- package/dist/plugin/request-scope.js +149 -0
- package/dist/plugin/server.d.ts +20 -2
- package/dist/plugin/server.js +98 -28
- package/dist/plugin/testing.d.ts +6 -0
- package/dist/plugin/testing.js +14 -2
- package/dist/policies.d.ts +21 -0
- package/dist/policies.js +66 -0
- package/dist/preference.js +6 -1
- package/dist/preload.js +6 -9
- package/dist/ref-watch.d.ts +0 -10
- package/dist/ref-watch.js +14 -1
- package/dist/ref.js +17 -5
- package/dist/region-snippet.js +9 -3
- package/dist/region.js +2 -1
- package/dist/report-thrown.d.ts +9 -0
- package/dist/report-thrown.js +15 -0
- package/dist/rules.d.ts +17 -0
- package/dist/rules.js +57 -0
- package/dist/runtime/beacon.d.ts +0 -4
- package/dist/runtime/beacon.js +58 -43
- package/dist/runtime/boot-link.js +19 -2
- package/dist/runtime/client-hooks.js +2 -1
- package/dist/runtime/concurrency.d.ts +16 -5
- package/dist/runtime/concurrency.js +123 -7
- package/dist/runtime/core.d.ts +3 -1
- package/dist/runtime/core.js +718 -384
- package/dist/runtime/devalue-lane.d.ts +13 -0
- package/dist/runtime/devalue-lane.js +49 -0
- package/dist/runtime/disclose-version.d.ts +1 -0
- package/dist/runtime/disclose-version.js +14 -0
- package/dist/runtime/early-holes.d.ts +4 -0
- package/dist/runtime/early-holes.js +40 -0
- package/dist/runtime/entry-locations.js +40 -49
- package/dist/runtime/foreign-props.d.ts +0 -7
- package/dist/runtime/foreign-props.js +2 -1
- package/dist/runtime/form-continuity.d.ts +2 -2
- package/dist/runtime/form-continuity.js +30 -8
- package/dist/runtime/frame-nav.d.ts +7 -1
- package/dist/runtime/frame-nav.js +84 -18
- package/dist/runtime/frame-store.d.ts +25 -16
- package/dist/runtime/frame-store.js +124 -98
- package/dist/runtime/frames.js +8 -4
- package/dist/runtime/hole-facts.js +6 -7
- package/dist/runtime/hole-fetch.d.ts +17 -0
- package/dist/runtime/hole-fetch.js +47 -0
- package/dist/runtime/hydrate-core.d.ts +4 -4
- package/dist/runtime/hydrate-core.js +80 -64
- package/dist/runtime/import-again.d.ts +33 -0
- package/dist/runtime/import-again.js +217 -0
- package/dist/runtime/interaction-replay.d.ts +5 -3
- package/dist/runtime/interaction-replay.js +6 -6
- package/dist/runtime/interaction.d.ts +14 -3
- package/dist/runtime/interaction.js +46 -23
- package/dist/runtime/kit-page-thread.svelte.d.ts +2 -1
- package/dist/runtime/kit-page-thread.svelte.js +44 -20
- package/dist/runtime/lakes.d.ts +1 -1
- package/dist/runtime/lakes.js +18 -5
- package/dist/runtime/live.d.ts +1 -2
- package/dist/runtime/live.js +6 -8
- package/dist/runtime/load-scheduler.d.ts +29 -18
- package/dist/runtime/load-scheduler.js +236 -136
- package/dist/runtime/nav-handle.js +25 -6
- package/dist/runtime/navigation.d.ts +24 -0
- package/dist/runtime/navigation.js +24 -0
- package/dist/runtime/ownership.d.ts +1 -1
- package/dist/runtime/page-defer.js +3 -3
- package/dist/runtime/parse-html.d.ts +15 -4
- package/dist/runtime/parse-html.js +24 -5
- package/dist/runtime/pushable-props.svelte.d.ts +21 -0
- package/dist/runtime/pushable-props.svelte.js +37 -0
- package/dist/runtime/region-endpoint-url.js +4 -6
- package/dist/runtime/region-machines.d.ts +111 -0
- package/dist/runtime/region-machines.js +97 -0
- package/dist/runtime/region-sheets.d.ts +31 -0
- package/dist/runtime/region-sheets.js +49 -0
- package/dist/runtime/restore-report.d.ts +1 -0
- package/dist/runtime/restore-report.js +36 -0
- package/dist/runtime/restore.d.ts +5 -2
- package/dist/runtime/restore.js +33 -14
- package/dist/runtime/router-nav.d.ts +32 -12
- package/dist/runtime/router-nav.js +320 -228
- package/dist/runtime/router.d.ts +83 -19
- package/dist/runtime/router.js +297 -129
- package/dist/runtime/rules.d.ts +35 -0
- package/dist/runtime/rules.js +137 -0
- package/dist/runtime/schedule.d.ts +6 -3
- package/dist/runtime/schedule.js +172 -57
- package/dist/runtime/seeds.d.ts +5 -1
- package/dist/runtime/seeds.js +11 -5
- package/dist/runtime/session.d.ts +46 -7
- package/dist/runtime/session.js +78 -10
- package/dist/runtime/slots.d.ts +53 -20
- package/dist/runtime/slots.js +2 -3
- package/dist/runtime/soft-refresh.d.ts +16 -0
- package/dist/runtime/soft-refresh.js +14 -0
- package/dist/runtime/svelte-warnings.d.ts +4 -10
- package/dist/runtime/svelte-warnings.js +7 -4
- package/dist/runtime/wire-classes.js +21 -8
- package/dist/runtime/wire-format.js +6 -3
- package/dist/script.js +8 -7
- package/dist/seed-ref-reviver.d.ts +0 -9
- package/dist/seed-ref-reviver.js +2 -1
- package/dist/seed-refs.d.ts +46 -36
- package/dist/seed-refs.js +131 -92
- package/dist/server/document-tail.d.ts +6 -0
- package/dist/server/document-tail.js +7 -1
- package/dist/server/early-holes-csp.d.ts +14 -0
- package/dist/server/early-holes-csp.js +73 -0
- package/dist/server/page-facts.d.ts +4 -1
- package/dist/server/page-facts.js +4 -1
- package/dist/server/page-seed.d.ts +2 -2
- package/dist/server/page-seed.js +104 -11
- package/dist/server/page-stream.d.ts +14 -1
- package/dist/server/page-stream.js +119 -2
- package/dist/server/personal-response.d.ts +0 -5
- package/dist/server/personal-response.js +6 -2
- package/dist/server/props-wire.js +5 -1
- package/dist/server/region-endpoint.js +10 -2
- package/dist/server/render-cache.d.ts +24 -20
- package/dist/server/render-cache.js +99 -40
- package/dist/server/render-page.js +46 -15
- package/dist/server/request-stats.d.ts +10 -8
- package/dist/server/request-stats.js +20 -12
- package/dist/server/rules.d.ts +11 -0
- package/dist/server/rules.js +64 -0
- package/dist/server/signing-key.js +31 -18
- package/dist/server/stream-regions.d.ts +14 -1
- package/dist/server/stream-regions.js +38 -2
- package/dist/shared-state.js +5 -1
- package/dist/shims/app-navigation.js +3 -2
- package/dist/shims/page-store.svelte.js +16 -2
- package/dist/source.js +7 -2
- package/dist/store-transport.js +12 -5
- package/dist/transport.js +3 -2
- package/dist/vite/index.js +83 -74
- package/dist/vite/kit-dirs.js +7 -1
- package/dist/vite/options.d.ts +2 -1
- package/dist/vite/options.js +11 -20
- package/dist/vite/package-files.js +13 -3
- package/dist/vite/paths.d.ts +2 -0
- package/dist/vite/paths.js +4 -4
- package/dist/vite/plugin-api.d.ts +6 -5
- package/dist/vite/plugin-api.js +42 -14
- package/dist/vite/style-preprocess.js +10 -3
- package/package.json +8 -1
- package/dist/KeepHost.svelte +0 -19
- package/dist/KeepHost.svelte.d.ts +0 -10
- package/dist/LiveHost.svelte +0 -19
- package/dist/LiveHost.svelte.d.ts +0 -10
- package/dist/NestedProvider.svelte +0 -13
- package/dist/NestedProvider.svelte.d.ts +0 -8
- package/dist/runtime/page-cache.d.ts +0 -23
- package/dist/runtime/page-cache.js +0 -74
package/dist/runtime/session.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { ConcurrencyGate } from "./concurrency.js";
|
|
2
2
|
import { FROZEN_SELECTOR } from "./region-attrs.js";
|
|
3
|
+
import { note_visit } from "./rules.js";
|
|
4
|
+
import { scope } from "@ogygia/machine";
|
|
3
5
|
// src/runtime/session.ts
|
|
4
6
|
/**
|
|
5
|
-
* Document-scoped runtime session: lake caches, seed flags, kit-page probe, server-island gate
|
|
6
|
-
* Cleared on SPA body swap via `reset()` so connecting regions never see the previous
|
|
7
|
+
* Document-scoped runtime session: lake caches, seed flags, kit-page probe, server-island gate, and
|
|
8
|
+
* the page visit. Cleared on SPA body swap via `reset()` so connecting regions never see the previous
|
|
9
|
+
* page.
|
|
7
10
|
*/
|
|
8
11
|
/** Soft cap on lake cache entries (unique lake ids). Evict oldest insertion on overflow. */
|
|
9
12
|
const LAKE_CACHE_MAX = 64;
|
|
@@ -28,6 +31,31 @@ const LAKE_CACHE_MAX = 64;
|
|
|
28
31
|
* copy could unstyle the page for a frame; a doubled sheet is harmless).
|
|
29
32
|
*/
|
|
30
33
|
const STYLESHEET_REL_RE = /(?:^|\s)stylesheet(?:\s|$)/i;
|
|
34
|
+
const RULES = import.meta.env?.DEV || (typeof __OGYGIA_RULES__ !== "undefined" ? __OGYGIA_RULES__ : false);
|
|
35
|
+
function page_visit() {
|
|
36
|
+
const visit = {
|
|
37
|
+
scope: scope(),
|
|
38
|
+
facts: []
|
|
39
|
+
};
|
|
40
|
+
if (RULES) note_visit(visit, false);
|
|
41
|
+
return visit;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The head's element children that a step added, from its child-list records: a node's first
|
|
45
|
+
* record says whether it was a child before the step (removed first: it was; added first: it was
|
|
46
|
+
* not), and it counts only if it is still a child at the end.
|
|
47
|
+
*/
|
|
48
|
+
function added_children(head, records) {
|
|
49
|
+
const was_child = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const r of records) {
|
|
51
|
+
for (const n of r.removedNodes) if (!was_child.has(n)) was_child.set(n, true);
|
|
52
|
+
for (const n of r.addedNodes) if (!was_child.has(n)) was_child.set(n, false);
|
|
53
|
+
}
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const [n, was] of was_child) if (!was && n.nodeType === 1 && n.parentNode === head) out.push(n);
|
|
56
|
+
if (out.length > 1) out.sort((a, b) => a.compareDocumentPosition(b) & 4 ? -1 : 1);
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
31
59
|
var RuntimeSession = class {
|
|
32
60
|
/** Page-owned head nodes (see above). Survives `reset()`: it is the document's, not a page's. */
|
|
33
61
|
page_head = /* @__PURE__ */ new WeakSet();
|
|
@@ -42,11 +70,38 @@ var RuntimeSession = class {
|
|
|
42
70
|
if (typeof document === "undefined") return;
|
|
43
71
|
for (const n of document.head.children) this.page_head.add(n);
|
|
44
72
|
}
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
47
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Before an island's hydrate step: start noting what lands in `<head>`. A MutationObserver on the
|
|
75
|
+
* head's own children, read synchronously when the step ends, so the record costs what the island
|
|
76
|
+
* added, not the whole head (a copy of the head per island grew as islands × head). Without
|
|
77
|
+
* MutationObserver: the head's elements, copied.
|
|
78
|
+
*/
|
|
79
|
+
mark_head() {
|
|
80
|
+
const head = document.head;
|
|
81
|
+
if (typeof MutationObserver === "undefined") {
|
|
82
|
+
const before = new Set(head.children);
|
|
83
|
+
return {
|
|
84
|
+
added: () => Array.from(head.children).filter((n) => !before.has(n)),
|
|
85
|
+
stop: () => {}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const delivered = [];
|
|
89
|
+
const watch = new MutationObserver((records) => delivered.push(...records));
|
|
90
|
+
watch.observe(head, { childList: true });
|
|
91
|
+
return {
|
|
92
|
+
added: () => {
|
|
93
|
+
const records = delivered.concat(watch.takeRecords());
|
|
94
|
+
watch.disconnect();
|
|
95
|
+
return added_children(head, records);
|
|
96
|
+
},
|
|
97
|
+
stop: () => watch.disconnect()
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** After an island's hydrate: the head elements it added (not there before the step, not
|
|
101
|
+
* page-owned) are that island's; then retire the page's copies of them. */
|
|
102
|
+
record_island_head(mark) {
|
|
48
103
|
let added = false;
|
|
49
|
-
for (const n of
|
|
104
|
+
for (const n of mark.added()) if (!this.page_head.has(n)) {
|
|
50
105
|
this.island_head.add(n);
|
|
51
106
|
added = true;
|
|
52
107
|
}
|
|
@@ -79,9 +134,16 @@ var RuntimeSession = class {
|
|
|
79
134
|
settled_lakes = /* @__PURE__ */ new WeakSet();
|
|
80
135
|
initialized_lakes = /* @__PURE__ */ new Set();
|
|
81
136
|
server_gate = new ConcurrencyGate(3);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
137
|
+
#visit = page_visit();
|
|
138
|
+
/** The visit of the page on screen (see {@link PageVisit}). */
|
|
139
|
+
get visit() {
|
|
140
|
+
return this.#visit;
|
|
141
|
+
}
|
|
142
|
+
/** The visit of the page a navigation brings, made before its swap: the navigation's (it ends
|
|
143
|
+
* it when there is no swap) until `reset(next)` makes it the page. */
|
|
144
|
+
next_visit() {
|
|
145
|
+
return page_visit();
|
|
146
|
+
}
|
|
85
147
|
#remote_seeded = false;
|
|
86
148
|
#page_seeded = false;
|
|
87
149
|
#kit_page;
|
|
@@ -117,12 +179,18 @@ var RuntimeSession = class {
|
|
|
117
179
|
if (root instanceof Element && root.matches?.("ogygia-region[wake=\"none\"]")) this.settled_lakes.add(root);
|
|
118
180
|
for (const lake of root.querySelectorAll(FROZEN_SELECTOR)) this.settled_lakes.add(lake);
|
|
119
181
|
}
|
|
120
|
-
|
|
182
|
+
/** The next page takes over (its visit, made by its navigation; a new one when none is given),
|
|
183
|
+
* and the visit of the page it replaces ends. */
|
|
184
|
+
reset(next = page_visit()) {
|
|
185
|
+
const left = this.#visit;
|
|
186
|
+
this.#visit = next;
|
|
187
|
+
if (RULES) note_visit(next, true);
|
|
121
188
|
this.#kit_page = void 0;
|
|
122
189
|
this.#remote_seeded = false;
|
|
123
190
|
this.#page_seeded = false;
|
|
124
191
|
this.lake_cache.clear();
|
|
125
192
|
this.initialized_lakes.clear();
|
|
193
|
+
if (left !== next) left.scope.close();
|
|
126
194
|
}
|
|
127
195
|
};
|
|
128
196
|
/** Process-wide session for the islands runtime (one client module graph). */
|
package/dist/runtime/slots.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WakeFn, resolve_address } from "./interaction.js";
|
|
1
2
|
import { KitBoot, kit_hydrates_page } from "./kit-boot.js";
|
|
2
3
|
import { register_island_graph } from "./island-graph-preload.js";
|
|
3
4
|
import { is_warmed_module, warm_island_module } from "./region-endpoint-url.js";
|
|
@@ -6,15 +7,19 @@ import { props_sidecar_of } from "./sidecar.js";
|
|
|
6
7
|
import { parse_region_html } from "./parse-html.js";
|
|
7
8
|
import { runtime_session } from "./session.js";
|
|
8
9
|
import { regions_in_shadow } from "./connected.js";
|
|
10
|
+
import { release_sheets } from "./region-sheets.js";
|
|
9
11
|
import { yield_task } from "./schedule.js";
|
|
10
12
|
import { beacon_nav } from "./beacon.js";
|
|
11
|
-
import {
|
|
13
|
+
import { LoadSlot } from "./load-scheduler.js";
|
|
12
14
|
import { is_self_owned, record_server_body, server_body_attributes, server_body_child, walk_enters } from "./ownership.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
+
import { devalue, load_devalue, needs_devalue, with_devalue } from "./devalue-lane.js";
|
|
16
|
+
import { OgygiaError, warn } from "../messages/core.js";
|
|
17
|
+
import { SoftRefresh } from "./soft-refresh.js";
|
|
18
|
+
import { jump_to_hash } from "./router.js";
|
|
19
|
+
import { PushableProps } from "./pushable-props.svelte.js";
|
|
15
20
|
import { sync_attributes } from "./morph.js";
|
|
16
21
|
import { Frame } from "../frame.js";
|
|
17
|
-
import {
|
|
22
|
+
import { Scope, Stale } from "@ogygia/machine";
|
|
18
23
|
/** Frozen SSR DOM detached before hydrate. */
|
|
19
24
|
export type LiftedLake = {
|
|
20
25
|
id: string;
|
|
@@ -38,9 +43,10 @@ export type LakeOps = {
|
|
|
38
43
|
restore(parent: Element, lifted: LiftedLake[]): void;
|
|
39
44
|
settle_in(root: ParentNode): void;
|
|
40
45
|
mark_frozen_settled(region: HTMLElement): void;
|
|
46
|
+
/** After an answer swapped into `region`: a lake that settled wakes the regions waiting on it. */
|
|
41
47
|
after_html_swap(region: HTMLElement, opts: {
|
|
42
48
|
revalidate?: boolean;
|
|
43
|
-
}): void;
|
|
49
|
+
}, wake_children: () => void): void;
|
|
44
50
|
after_fetch_exhausted(region: HTMLElement, opts: {
|
|
45
51
|
revalidate?: boolean;
|
|
46
52
|
}, wake_children: () => void): void;
|
|
@@ -50,8 +56,11 @@ export type FormOps = {
|
|
|
50
56
|
snapshot(root: ParentNode, pathKey: string): void;
|
|
51
57
|
restore(pathKey: string): void;
|
|
52
58
|
};
|
|
53
|
-
/** Wake a cold island when interaction lands inside it; returns a disarm fn
|
|
54
|
-
|
|
59
|
+
/** Wake a cold island when interaction lands inside it; returns a disarm fn (`over`: the region
|
|
60
|
+
* left while it was waking — the clicks it queued are dropped, not replayed). `fire` gets what
|
|
61
|
+
* replays the visitor's input (./interaction.ts `WakeFn`): the island goes live once it is in.
|
|
62
|
+
* `owner`: the region's lifetime in the page, which the replay's frame belongs to. */
|
|
63
|
+
export type ArmFn = (el: HTMLElement, fire: WakeFn, owner: Scope) => void | ((over?: boolean) => void);
|
|
55
64
|
/** `target: 'walk'` = the hydration repair (the server sequence Svelte's walk binds); default = a live
|
|
56
65
|
* morph toward a new answer. Same ownership rules either way (runtime/ownership.ts). `stays`: a
|
|
57
66
|
* child of `parent` the morph steps over (the page's own, beside the render's). */
|
|
@@ -79,8 +88,9 @@ export type PageOps = {
|
|
|
79
88
|
seed_page_once(): void;
|
|
80
89
|
/** the `page.data` a seed reference in island props resolves against */
|
|
81
90
|
seed_data_of(doc: Document): unknown;
|
|
82
|
-
/** on a Kit-hydrated document: the wait for Kit to apply its page (`null` once applied)
|
|
83
|
-
|
|
91
|
+
/** on a Kit-hydrated document: the wait for Kit to apply its page (`null` once applied), for as
|
|
92
|
+
* long as `signal` (the waiting region's visit) lasts */
|
|
93
|
+
kit_page_thread(signal?: AbortSignal): Promise<unknown> | null;
|
|
84
94
|
};
|
|
85
95
|
/**
|
|
86
96
|
* The client frame store, filled by the `frames` feature. Core reads it ONLY on the deferred /
|
|
@@ -89,17 +99,22 @@ export type PageOps = {
|
|
|
89
99
|
*/
|
|
90
100
|
export type FrameOps = {
|
|
91
101
|
subscribe(a: string, cb: (f: Frame) => void): () => void;
|
|
102
|
+
/** `STALE` when its `signal` aborted: only that caller leaves the fetch. */
|
|
92
103
|
ensure(a: string, fetcher: (signal: AbortSignal) => Promise<string>, opts?: {
|
|
93
104
|
force?: boolean;
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): Promise<string | Stale>;
|
|
107
|
+
/** A renewal answered a fresh capability for this address; and the one it answered, if any. */
|
|
108
|
+
renew(a: string, fresh: string): void;
|
|
109
|
+
renewal(a: string): string | null;
|
|
96
110
|
/**
|
|
97
111
|
* SINGLE-FLIGHT NAVIGATION: batch a page's signed deferred endpoints into the store as one streamed response. Lives
|
|
98
112
|
* on the seam (not a static router import) so an app with `router` but no deferred/live/lake region
|
|
99
113
|
* — hence no `frames` feature — never bundles the frame store. The router optional-chains this: no
|
|
100
|
-
* frames feature ⇒ no `render="defer"` holes to batch ⇒ the call never fires anyway.
|
|
114
|
+
* frames feature ⇒ no `render="defer"` holes to batch ⇒ the call never fires anyway. `visit`: the
|
|
115
|
+
* scope of the page the holes are for (./session.ts `PageVisit`); the batch stops when it ends.
|
|
101
116
|
*/
|
|
102
|
-
stream(endpoints: string[]): Promise<void>;
|
|
117
|
+
stream(endpoints: string[], visit: import("@ogygia/machine").Scope): Promise<void>;
|
|
103
118
|
/** Holes that start fetching in the same task go out as one batch (frame-nav.ts `join_batch`);
|
|
104
119
|
* await it, then `ensure` joins the batch's reservation. */
|
|
105
120
|
join?(endpoint: string): Promise<void>;
|
|
@@ -143,11 +158,13 @@ export type BootLink = {
|
|
|
143
158
|
parse_region_html: typeof parse_region_html;
|
|
144
159
|
runtime_session: typeof runtime_session;
|
|
145
160
|
regions_in_shadow: typeof regions_in_shadow;
|
|
161
|
+
/** the region sheets a navigation's head merge left, taken out but for those a region uses */
|
|
162
|
+
release_sheets: typeof release_sheets;
|
|
146
163
|
yield_task: typeof yield_task;
|
|
147
164
|
/** the router's navigation, timed for the profiler's beacon (a no-op without it) */
|
|
148
165
|
beacon_nav: typeof beacon_nav;
|
|
149
166
|
/** the load scheduler's queue (./load-scheduler.ts) */
|
|
150
|
-
|
|
167
|
+
LoadSlot: typeof LoadSlot;
|
|
151
168
|
/** DOM ownership (./ownership.ts): which children Svelte's walk enters, and self-owned elements */
|
|
152
169
|
walk_enters: typeof walk_enters;
|
|
153
170
|
is_self_owned: typeof is_self_owned;
|
|
@@ -155,12 +172,29 @@ export type BootLink = {
|
|
|
155
172
|
record_server_body: typeof record_server_body;
|
|
156
173
|
server_body_attributes: typeof server_body_attributes;
|
|
157
174
|
server_body_child: typeof server_body_child;
|
|
175
|
+
/** devalue, loaded when a page needs it (./devalue-lane.ts) */
|
|
176
|
+
load_devalue: typeof load_devalue;
|
|
177
|
+
devalue: typeof devalue;
|
|
178
|
+
with_devalue: typeof with_devalue;
|
|
179
|
+
needs_devalue: typeof needs_devalue;
|
|
180
|
+
/** the runtime's messages (../messages/core.ts), as a lazy chunk throws and logs them (../messages/lazy.ts) */
|
|
181
|
+
OgygiaError: typeof OgygiaError;
|
|
182
|
+
warn: typeof warn;
|
|
183
|
+
/** what a machine's task answers once its visit is left (`@ogygia/machine`, which the boot runs on) */
|
|
184
|
+
STALE: typeof import("@ogygia/machine").STALE;
|
|
185
|
+
/** keyed shared async work (`@ogygia/machine`): the router's page fetches */
|
|
186
|
+
flights: typeof import("@ogygia/machine").flights;
|
|
187
|
+
/** a change built off to the side and switched in at once (`@ogygia/machine`): the router's
|
|
188
|
+
* swap and the soft refresh */
|
|
189
|
+
stage: typeof import("@ogygia/machine").stage;
|
|
190
|
+
/** a stamp that only moves forward (`@ogygia/machine`): the world the router's page cache is
|
|
191
|
+
* made in */
|
|
192
|
+
world: typeof import("@ogygia/machine").world;
|
|
193
|
+
/** the page's soft refresh (./soft-refresh.ts): `invalidateAll`, with the router or without it */
|
|
194
|
+
soft: SoftRefresh;
|
|
158
195
|
};
|
|
159
196
|
export type RouterLink = {
|
|
160
|
-
document_key: typeof document_key;
|
|
161
197
|
jump_to_hash: typeof jump_to_hash;
|
|
162
|
-
push_state: typeof push_state;
|
|
163
|
-
replace_state: typeof replace_state;
|
|
164
198
|
};
|
|
165
199
|
export type InteractionLink = {
|
|
166
200
|
resolve_address: typeof resolve_address;
|
|
@@ -170,9 +204,8 @@ export type Slots = {
|
|
|
170
204
|
forms: FormOps;
|
|
171
205
|
interaction: ArmFn | null;
|
|
172
206
|
morph: MorphFn | null;
|
|
173
|
-
live
|
|
174
|
-
|
|
175
|
-
keep: Component<Record<string, unknown>> | null;
|
|
207
|
+
/** props a live region or a kept island is hydrated with, pushed in place later (./live.ts) */
|
|
208
|
+
Pushable: typeof PushableProps | null;
|
|
176
209
|
wire: WireOps | null;
|
|
177
210
|
/** The transportable classes an island's props, context and seeds need, loaded before it
|
|
178
211
|
* decodes (./wire-classes.ts). Null in an app without transportable classes: nothing to load. */
|
package/dist/runtime/slots.js
CHANGED
|
@@ -19,8 +19,7 @@ const slots = {
|
|
|
19
19
|
},
|
|
20
20
|
interaction: null,
|
|
21
21
|
morph: null,
|
|
22
|
-
|
|
23
|
-
keep: null,
|
|
22
|
+
Pushable: null,
|
|
24
23
|
wire: null,
|
|
25
24
|
wire_classes: null,
|
|
26
25
|
remoteSeeds: null,
|
|
@@ -36,7 +35,7 @@ const slots = {
|
|
|
36
35
|
/** A link a lazy chunk needs, or a loud error: every link is filled before its chunk can load, so a
|
|
37
36
|
* missing one means a chunk ran without the boot that owns it (a test that skipped `link_boot()`). */
|
|
38
37
|
function linked(link, name) {
|
|
39
|
-
if (link === null) throw new Error(
|
|
38
|
+
if (link === null) throw new Error("[ogygia] https://ogygiajs.dev/e/slot_not_linked?name=" + name);
|
|
40
39
|
return link;
|
|
41
40
|
}
|
|
42
41
|
const boot_link = () => linked(slots.boot, "boot");
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Machine } from "@ogygia/machine";
|
|
2
|
+
type States = {
|
|
3
|
+
idle: {};
|
|
4
|
+
refreshing: {
|
|
5
|
+
href: string;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
declare const steps: {
|
|
9
|
+
readonly idle: readonly ["refreshing"];
|
|
10
|
+
readonly refreshing: readonly ["refreshing", "idle"];
|
|
11
|
+
};
|
|
12
|
+
/** The page's soft refresh machine. */
|
|
13
|
+
export type SoftRefresh = Machine<States, typeof steps>;
|
|
14
|
+
/** A soft refresh machine, idle. */
|
|
15
|
+
export declare function create_soft_refresh(): SoftRefresh;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { machine } from "@ogygia/machine";
|
|
2
|
+
const definition = machine()({
|
|
3
|
+
name: "soft refresh",
|
|
4
|
+
initial: "idle",
|
|
5
|
+
steps: {
|
|
6
|
+
idle: ["refreshing"],
|
|
7
|
+
refreshing: ["refreshing", "idle"]
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
/** A soft refresh machine, idle. */
|
|
11
|
+
function create_soft_refresh() {
|
|
12
|
+
return definition.create();
|
|
13
|
+
}
|
|
14
|
+
export { create_soft_refresh };
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* SVELTE'S HYDRATION WARNINGS, KEPT — in dev, Svelte says in the console when the server and the
|
|
3
|
-
* browser disagreed while an island hydrated (`hydration_html_changed`, `hydration_attribute_changed`,
|
|
4
|
-
* `hydration_mismatch`…). Svelte keeps the server's value, so nothing on screen shows it: the console
|
|
5
|
-
* line is the only trace. This keeps each one (with the island hydrating at the time) for the devtools
|
|
6
|
-
* and the profiler's browser half. Dev only (production Svelte has no such warnings); the console line
|
|
7
|
-
* itself goes out unchanged. No regex: warnings are rare, read by string search.
|
|
8
|
-
*/
|
|
1
|
+
import { Scope } from "@ogygia/machine";
|
|
9
2
|
export interface SvelteWarning {
|
|
10
3
|
code: string;
|
|
11
4
|
message: string;
|
|
@@ -15,8 +8,9 @@ export interface SvelteWarning {
|
|
|
15
8
|
}
|
|
16
9
|
/** core.ts marks the island whose hydrate step is running. Svelte raises some warnings as the
|
|
17
10
|
* step's effects flush (a microtask after it returns): the mark holds until the task ends, and
|
|
18
|
-
* islands hydrate one per task, so the next one never inherits it.
|
|
19
|
-
|
|
11
|
+
* islands hydrate one per task, so the next one never inherits it. The task's end is `owner`'s to
|
|
12
|
+
* wait for (the island's lifetime in the page): an island gone first takes its mark with it. */
|
|
13
|
+
export declare function set_hydrating(el: Element, owner: Scope): void;
|
|
20
14
|
export declare function svelte_warnings(): SvelteWarning[];
|
|
21
15
|
export declare function on_svelte_warning(cb: (w: SvelteWarning) => void): void;
|
|
22
16
|
/** `%c[svelte] code\n%cmessage …` → the parts (null: not a Svelte hydration warning). */
|
|
@@ -5,12 +5,15 @@ const kept = [];
|
|
|
5
5
|
const listeners = [];
|
|
6
6
|
/** core.ts marks the island whose hydrate step is running. Svelte raises some warnings as the
|
|
7
7
|
* step's effects flush (a microtask after it returns): the mark holds until the task ends, and
|
|
8
|
-
* islands hydrate one per task, so the next one never inherits it.
|
|
9
|
-
|
|
8
|
+
* islands hydrate one per task, so the next one never inherits it. The task's end is `owner`'s to
|
|
9
|
+
* wait for (the island's lifetime in the page): an island gone first takes its mark with it. */
|
|
10
|
+
function set_hydrating(el, owner) {
|
|
10
11
|
hydrating = el;
|
|
11
|
-
|
|
12
|
+
const clear = () => {
|
|
12
13
|
if (hydrating === el) hydrating = null;
|
|
13
|
-
}
|
|
14
|
+
};
|
|
15
|
+
owner.timeout(clear, 0);
|
|
16
|
+
owner.defer(clear);
|
|
14
17
|
}
|
|
15
18
|
function svelte_warnings() {
|
|
16
19
|
return kept.slice();
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { import_again } from "./import-again.js";
|
|
1
2
|
import { props_sidecar_of } from "./sidecar.js";
|
|
2
3
|
import { yield_task } from "./schedule.js";
|
|
3
4
|
import { slots } from "./slots.js";
|
|
5
|
+
import { download } from "../policies.js";
|
|
6
|
+
import { flights } from "@ogygia/machine";
|
|
4
7
|
import { wire_all, wire_loaders } from "virtual:ogygia/transportables";
|
|
5
8
|
// src/runtime/wire-classes.ts
|
|
6
9
|
/**
|
|
@@ -16,6 +19,10 @@ import { wire_all, wire_loaders } from "virtual:ogygia/transportables";
|
|
|
16
19
|
* through Kit's `transport` (`wire_all`: remote functions, loads) gets every class with every island,
|
|
17
20
|
* as before: those values decode synchronously whenever they arrive. Each module loads once.
|
|
18
21
|
*
|
|
22
|
+
* A download that fails is tried again at once, and a failure is never kept: the wake it failed for
|
|
23
|
+
* decodes without the class (the decode says which class is missing), and the next wake that needs
|
|
24
|
+
* it loads it afresh, under a fresh URL (./import-again.ts: the browser keeps the failed one).
|
|
25
|
+
*
|
|
19
26
|
* A BOOT-phase feature (link/runtime-entry.ts), installed only when the app has transportable
|
|
20
27
|
* classes: without one the loader map is empty, but the bundler cannot see that through the virtual
|
|
21
28
|
* module, so this code shipped to every island page for a lookup that always found nothing.
|
|
@@ -26,15 +33,21 @@ function install() {
|
|
|
26
33
|
}
|
|
27
34
|
const loaders = wire_loaders;
|
|
28
35
|
const paths = Object.keys(loaders);
|
|
29
|
-
|
|
36
|
+
/** Each class module's loader: what it asks for next (its file, or a fresh URL after a failure). */
|
|
37
|
+
const imports = /* @__PURE__ */ new Map();
|
|
38
|
+
/** Each class module's load, by path, kept once it succeeds (a failure is never kept). */
|
|
39
|
+
const loads = flights({
|
|
40
|
+
name: "wire class",
|
|
41
|
+
run: (path) => {
|
|
42
|
+
let import_class = imports.get(path);
|
|
43
|
+
if (!import_class) imports.set(path, import_class = import_again(loaders[path]));
|
|
44
|
+
return yield_task().then(import_class);
|
|
45
|
+
},
|
|
46
|
+
retry: download
|
|
47
|
+
});
|
|
30
48
|
function load(path) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const loader = loaders[path];
|
|
34
|
-
p = loader ? yield_task().then(loader).catch(() => void 0) : Promise.resolve();
|
|
35
|
-
loading.set(path, p);
|
|
36
|
-
}
|
|
37
|
-
return p;
|
|
49
|
+
if (!loaders[path]) return Promise.resolve();
|
|
50
|
+
return loads.get(path).catch(() => void 0);
|
|
38
51
|
}
|
|
39
52
|
/** The classes a text names (`"<tag path>#…` appears in it), into `want`. */
|
|
40
53
|
function scan(text, want) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { boot_link } from "./slots.js";
|
|
2
2
|
// src/runtime/wire-format.ts
|
|
3
3
|
/**
|
|
4
4
|
* THE WIRE FORMAT of a side-channel script, read back. The server emits two lanes
|
|
5
5
|
* (server/props-wire.ts): plain JSON under `data-og-format="json"` (a payload with nothing devalue
|
|
6
6
|
* exists for — the common CMS props and seed), and devalue otherwise. This is the ONE place the
|
|
7
|
-
* client tells them apart: every seed / props / remote read goes through `parse_wire_text`.
|
|
7
|
+
* client tells them apart: every seed / props / remote read goes through `parse_wire_text`. devalue
|
|
8
|
+
* itself is loaded only for a page that has a devalue-lane script (./devalue-lane.ts).
|
|
8
9
|
*
|
|
9
10
|
* Pure (no DOM types beyond an attribute read) so the runtime, the shims and the tests share it.
|
|
10
11
|
*/
|
|
@@ -17,7 +18,9 @@ function wire_is_json(el) {
|
|
|
17
18
|
/** Parse a side-channel's text in its lane: `JSON.parse` for the JSON lane (nothing to revive by
|
|
18
19
|
* construction), devalue `parse` with the caller's revivers otherwise. */
|
|
19
20
|
function parse_wire_text(text, json, revivers) {
|
|
20
|
-
|
|
21
|
+
if (json) return JSON.parse(text);
|
|
22
|
+
const { parse } = boot_link().devalue();
|
|
23
|
+
return parse(text, revivers);
|
|
21
24
|
}
|
|
22
25
|
/** A seed or props script's payload, in its lane: JSON when the server marked it plain, devalue
|
|
23
26
|
* with `revivers` otherwise. Every seed / props read goes through here. */
|
package/dist/script.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { escape_attr, escape_script_text } from "./escape.js";
|
|
2
|
+
import { OgygiaError } from "./messages/island.js";
|
|
2
3
|
// src/script.ts
|
|
3
4
|
/**
|
|
4
5
|
* `script()` — an inline `<script>` tag as a string, for the work a csr=false page must do in the
|
|
@@ -45,7 +46,7 @@ function script(input, ...rest) {
|
|
|
45
46
|
let kinds = 0;
|
|
46
47
|
if (o.run !== void 0) {
|
|
47
48
|
kinds++;
|
|
48
|
-
if (typeof o.run !== "function") throw new
|
|
49
|
+
if (typeof o.run !== "function") throw new OgygiaError("script_run_not_function");
|
|
49
50
|
const module = o.type === "module";
|
|
50
51
|
if (module) type = "module";
|
|
51
52
|
const args = o.args ?? [];
|
|
@@ -56,13 +57,13 @@ function script(input, ...rest) {
|
|
|
56
57
|
}
|
|
57
58
|
let lines = "";
|
|
58
59
|
if (o.imports !== void 0) {
|
|
59
|
-
if (!module) throw new
|
|
60
|
+
if (!module) throw new OgygiaError("script_imports_need_module");
|
|
60
61
|
let mods = "";
|
|
61
62
|
let n = 0;
|
|
62
63
|
const imports = o.imports;
|
|
63
64
|
for (const name in imports) {
|
|
64
65
|
const url = imports[name];
|
|
65
|
-
if (typeof url !== "string" || !url) throw new
|
|
66
|
+
if (typeof url !== "string" || !url) throw new OgygiaError("script_import_not_url", { name });
|
|
66
67
|
lines += `import * as __og_m${n} from ${escape_script_text(JSON.stringify(url))};`;
|
|
67
68
|
mods += (n ? "," : "") + `${escape_script_text(JSON.stringify(name))}:__og_m${n}`;
|
|
68
69
|
n++;
|
|
@@ -70,7 +71,7 @@ function script(input, ...rest) {
|
|
|
70
71
|
call = `{${mods}}` + (call ? "," + call : "");
|
|
71
72
|
}
|
|
72
73
|
const src = fn_source(o.run);
|
|
73
|
-
if (src.indexOf("__vite_ssr_") !== -1) throw new
|
|
74
|
+
if (src.indexOf("__vite_ssr_") !== -1) throw new OgygiaError("script_run_rewritten");
|
|
74
75
|
body = `${lines}${module ? "await " : ""}(${close_safe(src)})(${call});`;
|
|
75
76
|
} else body = "";
|
|
76
77
|
for (const [key, kind] of DATA_KINDS) {
|
|
@@ -78,16 +79,16 @@ function script(input, ...rest) {
|
|
|
78
79
|
kinds++;
|
|
79
80
|
type = kind;
|
|
80
81
|
const json = JSON.stringify(o[key]);
|
|
81
|
-
if (json === void 0) throw new
|
|
82
|
+
if (json === void 0) throw new OgygiaError("script_not_json", { key });
|
|
82
83
|
body = escape_script_text(json);
|
|
83
84
|
}
|
|
84
|
-
if (kinds !== 1) throw new
|
|
85
|
+
if (kinds !== 1) throw new OgygiaError("script_one_kind", { count: kinds });
|
|
85
86
|
let attrs = type ? ` type="${type}"` : "";
|
|
86
87
|
for (const key in o) {
|
|
87
88
|
if (RESERVED.has(key)) continue;
|
|
88
89
|
const v = o[key];
|
|
89
90
|
if (v === void 0 || v === null || v === false) continue;
|
|
90
|
-
if (!is_attr_name(key)) throw new
|
|
91
|
+
if (!is_attr_name(key)) throw new OgygiaError("script_bad_attribute", { key });
|
|
91
92
|
attrs += v === true ? " " + key : ` ${key}="${escape_attr(String(v))}"`;
|
|
92
93
|
}
|
|
93
94
|
return `<script${attrs}>${body}<\/script>`;
|
|
@@ -1,12 +1,3 @@
|
|
|
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
1
|
/** The devalue type tag. */
|
|
11
2
|
export declare const SEED_REF_KEY = "OgygiaSeedRef";
|
|
12
3
|
export type SeedPath = (string | number)[];
|
package/dist/seed-ref-reviver.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { warn } from "./messages/lazy.js";
|
|
1
2
|
// src/seed-ref-reviver.ts
|
|
2
3
|
/**
|
|
3
4
|
* The CLIENT half of seed references (seed-refs.ts): the devalue type tag and the reviver that
|
|
@@ -37,7 +38,7 @@ function seed_ref_reviver(get_data) {
|
|
|
37
38
|
resolved = true;
|
|
38
39
|
}
|
|
39
40
|
const node = resolve_seed_ref(data, path);
|
|
40
|
-
if (node === void 0 && typeof console !== "undefined" && import.meta.env?.DEV)
|
|
41
|
+
if (node === void 0 && typeof console !== "undefined" && import.meta.env?.DEV) warn("page_seed_ref_missing", { path: JSON.stringify(path) });
|
|
41
42
|
return node;
|
|
42
43
|
};
|
|
43
44
|
}
|
package/dist/seed-refs.d.ts
CHANGED
|
@@ -1,33 +1,4 @@
|
|
|
1
1
|
import { SEED_REF_KEY, SeedPath, resolve_seed_ref, seed_ref_reviver } from "./seed-ref-reviver.js";
|
|
2
|
-
export interface SeedIndex {
|
|
3
|
-
/** node → its path from `page.data` (identity matches: the app passed the seed's own object).
|
|
4
|
-
* The path is materialised on the first ask — an indexed node that no island references
|
|
5
|
-
* never allocates one. */
|
|
6
|
-
readonly by_identity: {
|
|
7
|
-
get(node: object): SeedPath | undefined;
|
|
8
|
-
has(node: object): boolean;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* The seed nodes with exactly this byte estimate — the only ones a props node of that size can
|
|
12
|
-
* be a clone of. Structure matching hashes just those few candidates (memoised per seed node)
|
|
13
|
-
* instead of the whole seed, so an app that passes the seed's own objects never hashes a seed
|
|
14
|
-
* node at all, and a cloning app hashes only what it has to. The buckets are built on the first
|
|
15
|
-
* ask — a page whose islands only pass seed objects by identity never builds them.
|
|
16
|
-
*/
|
|
17
|
-
candidates(bytes: number): readonly object[];
|
|
18
|
-
/** Structural hash of an indexed seed node, memoised for the request. */
|
|
19
|
-
hash_of(node: object): string | null;
|
|
20
|
-
/** nodes indexed (for tests / devtools). */
|
|
21
|
-
readonly size: number;
|
|
22
|
-
/**
|
|
23
|
-
* SEED SHAPING: the top-level `page.data` keys under which a props node was matched
|
|
24
|
-
* (`plan_seed_refs` records the first path segment of every reference it plans). The handle
|
|
25
|
-
* renders the tail against the FULL index, then ships every touched key — a key no island's
|
|
26
|
-
* code reads but whose node an island's props point into still ships, so a reference always
|
|
27
|
-
* has something to point at.
|
|
28
|
-
*/
|
|
29
|
-
readonly touched: Set<string>;
|
|
30
|
-
}
|
|
31
2
|
/** Exact structural equality for the shapes `hash_subtree` accepts — the check behind a hash hit. */
|
|
32
3
|
export declare function deep_equal_plain(a: unknown, b: unknown): boolean;
|
|
33
4
|
/** What one walk learns about a subtree. */
|
|
@@ -61,15 +32,53 @@ export declare function analyze(value: unknown): Measure;
|
|
|
61
32
|
*/
|
|
62
33
|
export declare function json_culprit(value: unknown, max_depth?: number): string | null;
|
|
63
34
|
/**
|
|
64
|
-
*
|
|
35
|
+
* The seed's `data` indexed for referencing: every referenceable node at or above `min_bytes`, by
|
|
65
36
|
* identity now and by structural hash on demand (first occurrence wins — the shortest path is not
|
|
66
|
-
* guaranteed, the first in key order is, which is deterministic).
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* is most of the nodes.
|
|
37
|
+
* guaranteed, the first in key order is, which is deterministic). It reads the one measuring walk
|
|
38
|
+
* (`analyze`) and prunes: a node below `min_bytes` cannot hold a child at or above it (a child is
|
|
39
|
+
* never larger than its parent), so its subtree is skipped BEFORE anything is materialised — on a
|
|
40
|
+
* CMS tree that is most of the nodes. Data that is not a plain object or array indexes nothing.
|
|
71
41
|
*/
|
|
72
|
-
export declare
|
|
42
|
+
export declare class SeedIndex {
|
|
43
|
+
#private;
|
|
44
|
+
/** nodes indexed (for tests / devtools). */
|
|
45
|
+
readonly size: number;
|
|
46
|
+
/**
|
|
47
|
+
* SEED SHAPING: the top-level `page.data` keys under which a props node was matched
|
|
48
|
+
* (`plan_seed_refs` records the first path segment of every reference it plans). The handle
|
|
49
|
+
* renders the tail against the FULL index, then ships every touched key — a key no island's
|
|
50
|
+
* code reads but whose node an island's props point into still ships, so a reference always
|
|
51
|
+
* has something to point at.
|
|
52
|
+
*/
|
|
53
|
+
readonly touched: Set<string>;
|
|
54
|
+
/** The index of `data`, cached per `data` object: one index per request, however many islands
|
|
55
|
+
* ask. */
|
|
56
|
+
static of(data: unknown, min_bytes?: number): SeedIndex;
|
|
57
|
+
constructor(data: unknown, min_bytes: number);
|
|
58
|
+
/** node → its path from `page.data` (identity matches: the app passed the seed's own object).
|
|
59
|
+
* The path is materialised on the first ask — an indexed node that no island references
|
|
60
|
+
* never allocates one. */
|
|
61
|
+
path(node: object): SeedPath | undefined;
|
|
62
|
+
/** Whether `node` is an indexed seed node (by identity). */
|
|
63
|
+
has(node: object): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The seed nodes with exactly this byte estimate — the only ones a props node of that size can
|
|
66
|
+
* be a clone of. Structure matching hashes just those few candidates (memoised per seed node)
|
|
67
|
+
* instead of the whole seed, so an app that passes the seed's own objects never hashes a seed
|
|
68
|
+
* node at all, and a cloning app hashes only what it has to. The buckets are built on the first
|
|
69
|
+
* ask — a page whose islands only pass seed objects by identity never builds them.
|
|
70
|
+
*/
|
|
71
|
+
candidates(bytes: number): readonly object[];
|
|
72
|
+
/**
|
|
73
|
+
* The first seed node of exactly this byte estimate whose structural hash is `hash` (bucket
|
|
74
|
+
* order, as `candidates(bytes).find(...)` gives it), in one map lookup. The bucket is hashed in
|
|
75
|
+
* order only as far as an ask needs, each hash keeping its first node, so the many same-sized
|
|
76
|
+
* records of a list cost one hash each however many props nodes ask.
|
|
77
|
+
*/
|
|
78
|
+
match(bytes: number, hash: string): object | undefined;
|
|
79
|
+
/** Structural hash of an indexed seed node, memoised for the request. */
|
|
80
|
+
hash_of(node: object): string | null;
|
|
81
|
+
}
|
|
73
82
|
export interface SeedRefPlan {
|
|
74
83
|
/** The devalue reducer for the `OgygiaSeedRef` type: `(value) => path | undefined`. */
|
|
75
84
|
reducer: (value: unknown) => SeedPath | undefined;
|
|
@@ -86,6 +95,7 @@ export interface SeedRefPlan {
|
|
|
86
95
|
* ride inside the reference), and neither is anything under a node below `min_bytes` (no child
|
|
87
96
|
* of it can be referenced). The props' own measures come from the request memo, so the plan adds
|
|
88
97
|
* no walk of its own beyond the pointer lookups.
|
|
98
|
+
* @heisenbench linear in props
|
|
89
99
|
*/
|
|
90
100
|
export declare function plan_seed_refs(index: SeedIndex, props: unknown, min_bytes?: number): SeedRefPlan;
|
|
91
101
|
export { SEED_REF_KEY, type SeedPath, resolve_seed_ref, seed_ref_reviver };
|