@ogygia/core 0.12.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/LateIsland.svelte +3 -2
  2. package/dist/LateIslandAwait.svelte +6 -2
  3. package/dist/Region.svelte +22 -33
  4. package/dist/boundary.d.ts +0 -2
  5. package/dist/boundary.js +8 -6
  6. package/dist/compiler/debarrel/barrel.d.ts +15 -8
  7. package/dist/compiler/debarrel/barrel.js +92 -45
  8. package/dist/compiler/debarrel/plugin.js +41 -17
  9. package/dist/compiler/dev/dev-hmr.d.ts +3 -0
  10. package/dist/compiler/dev/dev-hmr.js +3 -0
  11. package/dist/compiler/driver.js +11 -4
  12. package/dist/compiler/flags.js +21 -7
  13. package/dist/compiler/fouc-css.js +5 -1
  14. package/dist/compiler/kit.js +5 -1
  15. package/dist/compiler/link/build-output.js +12 -3
  16. package/dist/compiler/macros/bake.js +18 -4
  17. package/dist/compiler/macros/dollar.js +6 -1
  18. package/dist/compiler/macros/source.js +6 -1
  19. package/dist/compiler/macros/store.js +10 -2
  20. package/dist/compiler/macros/wire.js +6 -1
  21. package/dist/compiler/parse/scan.js +10 -3
  22. package/dist/compiler/region/transform.js +188 -74
  23. package/dist/context-bridge.js +13 -5
  24. package/dist/context.d.ts +9 -0
  25. package/dist/context.js +30 -1
  26. package/dist/document.d.ts +1 -1
  27. package/dist/document.js +7 -1
  28. package/dist/early-holes-head.d.ts +5 -0
  29. package/dist/early-holes-head.js +20 -0
  30. package/dist/early-holes.d.ts +34 -0
  31. package/dist/early-holes.js +51 -0
  32. package/dist/entry-hydrate.js +9 -12
  33. package/dist/events/bus.js +7 -2
  34. package/dist/events/schema.d.ts +2 -2
  35. package/dist/fn-transport.js +4 -3
  36. package/dist/hooks.js +94 -163
  37. package/dist/insights/page-assets.js +11 -3
  38. package/dist/insights/page-insights.d.ts +0 -2
  39. package/dist/insights/page-insights.js +14 -10
  40. package/dist/insights/session-insights.js +2 -2
  41. package/dist/internal-instruments.d.ts +3 -1
  42. package/dist/internal-instruments.js +3 -1
  43. package/dist/internal-server.d.ts +3 -2
  44. package/dist/internal-server.js +3 -2
  45. package/dist/internal.d.ts +1 -2
  46. package/dist/internal.js +1 -2
  47. package/dist/keep-fallback.d.ts +0 -2
  48. package/dist/keep-fallback.js +4 -5
  49. package/dist/live-transport.js +3 -2
  50. package/dist/messages/build.d.ts +585 -0
  51. package/dist/messages/build.js +800 -0
  52. package/dist/messages/core.d.ts +464 -0
  53. package/dist/messages/core.js +843 -0
  54. package/dist/messages/define.d.ts +62 -0
  55. package/dist/messages/define.js +49 -0
  56. package/dist/messages/index.d.ts +6 -0
  57. package/dist/messages/index.js +6 -0
  58. package/dist/messages/island.d.ts +4 -0
  59. package/dist/messages/island.js +34 -0
  60. package/dist/messages/lazy.d.ts +3 -0
  61. package/dist/messages/lazy.js +22 -0
  62. package/dist/messages/prose.d.ts +15 -0
  63. package/dist/messages/prose.js +43 -0
  64. package/dist/messages/server.d.ts +142 -0
  65. package/dist/messages/server.js +246 -0
  66. package/dist/nested.d.ts +3 -0
  67. package/dist/nested.js +8 -1
  68. package/dist/plugin/css.js +14 -13
  69. package/dist/plugin/facts.d.ts +2 -0
  70. package/dist/plugin/facts.js +10 -6
  71. package/dist/plugin/hooks.d.ts +7 -17
  72. package/dist/plugin/hooks.js +32 -4
  73. package/dist/plugin/index.d.ts +9 -2
  74. package/dist/plugin/request-scope.d.ts +20 -0
  75. package/dist/plugin/request-scope.js +149 -0
  76. package/dist/plugin/server.d.ts +20 -2
  77. package/dist/plugin/server.js +98 -28
  78. package/dist/plugin/testing.d.ts +6 -0
  79. package/dist/plugin/testing.js +14 -2
  80. package/dist/policies.d.ts +21 -0
  81. package/dist/policies.js +66 -0
  82. package/dist/preference.js +6 -1
  83. package/dist/preload.js +6 -9
  84. package/dist/ref-watch.d.ts +0 -10
  85. package/dist/ref-watch.js +14 -1
  86. package/dist/ref.js +17 -5
  87. package/dist/region-snippet.js +9 -3
  88. package/dist/region.js +2 -1
  89. package/dist/report-thrown.d.ts +9 -0
  90. package/dist/report-thrown.js +15 -0
  91. package/dist/rules.d.ts +17 -0
  92. package/dist/rules.js +57 -0
  93. package/dist/runtime/beacon.d.ts +0 -4
  94. package/dist/runtime/beacon.js +58 -43
  95. package/dist/runtime/boot-link.js +19 -2
  96. package/dist/runtime/client-hooks.js +2 -1
  97. package/dist/runtime/concurrency.d.ts +16 -5
  98. package/dist/runtime/concurrency.js +123 -7
  99. package/dist/runtime/core.d.ts +3 -1
  100. package/dist/runtime/core.js +718 -384
  101. package/dist/runtime/devalue-lane.d.ts +13 -0
  102. package/dist/runtime/devalue-lane.js +49 -0
  103. package/dist/runtime/disclose-version.d.ts +1 -0
  104. package/dist/runtime/disclose-version.js +14 -0
  105. package/dist/runtime/early-holes.d.ts +4 -0
  106. package/dist/runtime/early-holes.js +40 -0
  107. package/dist/runtime/entry-locations.js +40 -49
  108. package/dist/runtime/foreign-props.d.ts +0 -7
  109. package/dist/runtime/foreign-props.js +2 -1
  110. package/dist/runtime/form-continuity.d.ts +2 -2
  111. package/dist/runtime/form-continuity.js +30 -8
  112. package/dist/runtime/frame-nav.d.ts +7 -1
  113. package/dist/runtime/frame-nav.js +84 -18
  114. package/dist/runtime/frame-store.d.ts +25 -16
  115. package/dist/runtime/frame-store.js +124 -98
  116. package/dist/runtime/frames.js +8 -4
  117. package/dist/runtime/hole-facts.js +6 -7
  118. package/dist/runtime/hole-fetch.d.ts +17 -0
  119. package/dist/runtime/hole-fetch.js +47 -0
  120. package/dist/runtime/hydrate-core.d.ts +4 -4
  121. package/dist/runtime/hydrate-core.js +80 -64
  122. package/dist/runtime/import-again.d.ts +33 -0
  123. package/dist/runtime/import-again.js +217 -0
  124. package/dist/runtime/interaction-replay.d.ts +5 -3
  125. package/dist/runtime/interaction-replay.js +6 -6
  126. package/dist/runtime/interaction.d.ts +14 -3
  127. package/dist/runtime/interaction.js +46 -23
  128. package/dist/runtime/kit-page-thread.svelte.d.ts +2 -1
  129. package/dist/runtime/kit-page-thread.svelte.js +44 -20
  130. package/dist/runtime/lakes.d.ts +1 -1
  131. package/dist/runtime/lakes.js +18 -5
  132. package/dist/runtime/live.d.ts +1 -2
  133. package/dist/runtime/live.js +6 -8
  134. package/dist/runtime/load-scheduler.d.ts +29 -18
  135. package/dist/runtime/load-scheduler.js +236 -136
  136. package/dist/runtime/nav-handle.js +25 -6
  137. package/dist/runtime/navigation.d.ts +24 -0
  138. package/dist/runtime/navigation.js +24 -0
  139. package/dist/runtime/ownership.d.ts +1 -1
  140. package/dist/runtime/page-defer.js +3 -3
  141. package/dist/runtime/parse-html.d.ts +15 -4
  142. package/dist/runtime/parse-html.js +24 -5
  143. package/dist/runtime/pushable-props.svelte.d.ts +21 -0
  144. package/dist/runtime/pushable-props.svelte.js +37 -0
  145. package/dist/runtime/region-endpoint-url.js +4 -6
  146. package/dist/runtime/region-machines.d.ts +111 -0
  147. package/dist/runtime/region-machines.js +97 -0
  148. package/dist/runtime/region-sheets.d.ts +31 -0
  149. package/dist/runtime/region-sheets.js +49 -0
  150. package/dist/runtime/restore-report.d.ts +1 -0
  151. package/dist/runtime/restore-report.js +36 -0
  152. package/dist/runtime/restore.d.ts +5 -2
  153. package/dist/runtime/restore.js +33 -14
  154. package/dist/runtime/router-nav.d.ts +32 -12
  155. package/dist/runtime/router-nav.js +320 -228
  156. package/dist/runtime/router.d.ts +83 -19
  157. package/dist/runtime/router.js +297 -129
  158. package/dist/runtime/rules.d.ts +35 -0
  159. package/dist/runtime/rules.js +137 -0
  160. package/dist/runtime/schedule.d.ts +6 -3
  161. package/dist/runtime/schedule.js +172 -57
  162. package/dist/runtime/seeds.d.ts +5 -1
  163. package/dist/runtime/seeds.js +11 -5
  164. package/dist/runtime/session.d.ts +46 -7
  165. package/dist/runtime/session.js +78 -10
  166. package/dist/runtime/slots.d.ts +53 -20
  167. package/dist/runtime/slots.js +2 -3
  168. package/dist/runtime/soft-refresh.d.ts +16 -0
  169. package/dist/runtime/soft-refresh.js +14 -0
  170. package/dist/runtime/svelte-warnings.d.ts +4 -10
  171. package/dist/runtime/svelte-warnings.js +7 -4
  172. package/dist/runtime/wire-classes.js +21 -8
  173. package/dist/runtime/wire-format.js +6 -3
  174. package/dist/script.js +8 -7
  175. package/dist/seed-ref-reviver.d.ts +0 -9
  176. package/dist/seed-ref-reviver.js +2 -1
  177. package/dist/seed-refs.d.ts +46 -36
  178. package/dist/seed-refs.js +131 -92
  179. package/dist/server/document-tail.d.ts +6 -0
  180. package/dist/server/document-tail.js +7 -1
  181. package/dist/server/early-holes-csp.d.ts +14 -0
  182. package/dist/server/early-holes-csp.js +73 -0
  183. package/dist/server/page-facts.d.ts +4 -1
  184. package/dist/server/page-facts.js +4 -1
  185. package/dist/server/page-seed.d.ts +2 -2
  186. package/dist/server/page-seed.js +104 -11
  187. package/dist/server/page-stream.d.ts +14 -1
  188. package/dist/server/page-stream.js +119 -2
  189. package/dist/server/personal-response.d.ts +0 -5
  190. package/dist/server/personal-response.js +6 -2
  191. package/dist/server/props-wire.js +5 -1
  192. package/dist/server/region-endpoint.js +10 -2
  193. package/dist/server/render-cache.d.ts +24 -20
  194. package/dist/server/render-cache.js +99 -40
  195. package/dist/server/render-page.js +46 -15
  196. package/dist/server/request-stats.d.ts +10 -8
  197. package/dist/server/request-stats.js +20 -12
  198. package/dist/server/rules.d.ts +11 -0
  199. package/dist/server/rules.js +64 -0
  200. package/dist/server/signing-key.js +31 -18
  201. package/dist/server/stream-regions.d.ts +14 -1
  202. package/dist/server/stream-regions.js +38 -2
  203. package/dist/shared-state.js +5 -1
  204. package/dist/shims/app-navigation.js +3 -2
  205. package/dist/shims/page-store.svelte.js +16 -2
  206. package/dist/source.js +7 -2
  207. package/dist/store-transport.js +12 -5
  208. package/dist/transport.js +3 -2
  209. package/dist/vite/index.js +17 -10
  210. package/dist/vite/kit-dirs.js +7 -1
  211. package/dist/vite/options.d.ts +2 -1
  212. package/dist/vite/options.js +11 -20
  213. package/dist/vite/package-files.js +13 -3
  214. package/dist/vite/paths.d.ts +2 -0
  215. package/dist/vite/paths.js +4 -4
  216. package/dist/vite/plugin-api.js +42 -14
  217. package/dist/vite/style-preprocess.js +10 -3
  218. package/package.json +8 -1
  219. package/dist/KeepHost.svelte +0 -19
  220. package/dist/KeepHost.svelte.d.ts +0 -10
  221. package/dist/LiveHost.svelte +0 -19
  222. package/dist/LiveHost.svelte.d.ts +0 -10
  223. package/dist/NestedProvider.svelte +0 -13
  224. package/dist/NestedProvider.svelte.d.ts +0 -8
  225. package/dist/runtime/page-cache.d.ts +0 -23
  226. package/dist/runtime/page-cache.js +0 -74
@@ -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 page.
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
- /** After an island's hydrate: the head elements it added (not in `before`, not page-owned) are
46
- * that island's; then retire the page's copies of them. */
47
- record_island_head(before) {
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 document.head.children) if (!before.has(n) && !this.page_head.has(n)) {
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
- /** The page facts answers rendered with (server/render-page.ts), queued at the answer seam
83
- * (`region_fragment`) for the next hydrate to merge into the page store (seeds.ts). */
84
- page_facts = [];
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
- reset() {
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). */
@@ -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 { load_slot } from "./load-scheduler.js";
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 { document_key, jump_to_hash, push_state, replace_state } from "./router.js";
14
- import { resolve_address } from "./interaction.js";
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 { Component } from "svelte";
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
- export type ArmFn = (el: HTMLElement, fire: () => void) => void | (() => void);
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
- kit_page_thread(): Promise<void> | null;
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
- }): Promise<string>;
95
- abandon(a: string): void;
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
- load_slot: typeof load_slot;
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: Component<Record<string, unknown>> | null;
174
- /** a kept island's host (the live feature fills it beside `live`) */
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. */
@@ -19,8 +19,7 @@ const slots = {
19
19
  },
20
20
  interaction: null,
21
21
  morph: null,
22
- live: null,
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(`[ogygia] runtime chunk used \`slots.${name}\` before the boot linked it`);
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
- export declare function set_hydrating(el: Element): void;
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
- function set_hydrating(el) {
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
- setTimeout(() => {
12
+ const clear = () => {
12
13
  if (hydrating === el) hydrating = null;
13
- }, 0);
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
- const loading = /* @__PURE__ */ new Map();
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
- let p = loading.get(path);
32
- if (!p) {
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 { parse } from "devalue";
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
- return json ? JSON.parse(text) : parse(text, revivers);
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 TypeError("[ogygia] script(): `run` must be a function");
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 TypeError("[ogygia] script(): `imports` needs `type: 'module'` (a static import only works in a module)");
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 TypeError(`[ogygia] script(): \`imports.${name}\` must be a URL string`);
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 TypeError("[ogygia] script(): `run` was rewritten by the bundler (it holds `__vite_ssr_…`): an `import()` or an imported binding inside it cannot cross into the browser. Declare the module in `imports` (with `type: 'module'`) and read it from `run`'s first parameter.");
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 TypeError(`[ogygia] script(): \`${key}\` must be JSON-serializable`);
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 TypeError(`[ogygia] script(): pass exactly one of \`run\`, \`json\`, \`ld\`, \`importmap\`, \`speculation\` (got ${kinds})`);
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 TypeError(`[ogygia] script(): \`${key}\` is not an attribute name`);
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)[];
@@ -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) 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
+ 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
  }
@@ -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
- * Index the seed's `data` for referencing: every referenceable node at or above `min_bytes`, by
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). Cached per `data` object: one
67
- * index per request, however many islands ask. It reads the one measuring walk (`analyze`) and
68
- * prunes: a node below `min_bytes` cannot hold a child at or above it (a child is never larger
69
- * than its parent), so its subtree is skipped BEFORE anything is materialised — on a CMS tree that
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 function index_seed(data: unknown, min_bytes?: number): SeedIndex;
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 };