@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,6 +1,10 @@
1
+ import { import_again } from "./import-again.js";
1
2
  import { slots } from "./slots.js";
2
3
  import { warm_island_module } from "./region-endpoint-url.js";
3
4
  import { island_entry_of } from "./entry-locations.js";
5
+ import { OgygiaError } from "../messages/core.js";
6
+ import { download } from "../policies.js";
7
+ import { STALE, flights } from "@ogygia/machine";
4
8
  // src/runtime/interaction.ts
5
9
  /**
6
10
  * WHAT A VISITOR DOES BEFORE AN ISLAND IS LIVE IS NOT LOST. `wake: 'interaction'` — the island sleeps
@@ -47,12 +51,12 @@ import { island_entry_of } from "./entry-locations.js";
47
51
  */
48
52
  /** Feature entry: fill the `interaction` slot — wake a cold island on first use, warm on hover. */
49
53
  function install() {
50
- slots.interaction = (el, fire) => {
54
+ slots.interaction = (el, fire, owner) => {
51
55
  const warm = () => {
52
56
  const entry = island_entry_of(el);
53
57
  if (entry) warm_island_module(entry, void 0, "user");
54
58
  };
55
- return arm_interaction(el, warm, () => Promise.resolve(fire()));
59
+ return arm_interaction(el, warm, fire, owner);
56
60
  };
57
61
  }
58
62
  const WAKE_EVENTS = [
@@ -99,12 +103,26 @@ function snapshot_fields(region) {
99
103
  }
100
104
  return out;
101
105
  }
106
+ /**
107
+ * Ends an arming: its entry goes, if it is still the region's (a newer arming's stays). `over`: the
108
+ * region left the page while it was woken — the visit that queued its clicks is over, and they go
109
+ * with it (the region that comes back is server markup of a new visit, with a capture of its own).
110
+ */
111
+ function disarm(a, over) {
112
+ if (armed.get(a.region) === a) armed.delete(a.region);
113
+ if (over && a.state === "woken") a.state = "over";
114
+ }
102
115
  const armed = /* @__PURE__ */ new Map();
103
116
  let installed = false;
104
- let replay_promise = null;
117
+ const load_replay = flights({
118
+ name: "interaction replay",
119
+ run: import_again(() => import("./interaction-replay.js")),
120
+ retry: download
121
+ });
122
+ /** (Never STALE: nothing drops the replay half.) */
105
123
  function replay() {
106
124
  slots.interaction_link ??= { resolve_address };
107
- return replay_promise ??= import("./interaction-replay.js");
125
+ return load_replay.get();
108
126
  }
109
127
  /** The nearest ARMED region above an event's target (past nested regions that are not armed), never
110
128
  * past a live one: a click inside an awake island is that island's, whatever sleeps around it. */
@@ -136,7 +154,7 @@ function on_warm_event(e) {
136
154
  if (!a || a.warmed) return;
137
155
  a.warmed = true;
138
156
  a.warm();
139
- replay();
157
+ replay().catch(() => {});
140
158
  }
141
159
  function install_listeners() {
142
160
  if (installed) return;
@@ -176,20 +194,22 @@ function wake(a, e) {
176
194
  });
177
195
  }
178
196
  }
179
- if (a.woken) return;
180
- a.woken = true;
197
+ if (a.state !== "armed") return;
198
+ a.state = "woken";
181
199
  region.setAttribute("data-og-woke", "interaction");
182
200
  const fields = snapshot_fields(region);
183
201
  const active = document.activeElement;
184
202
  const active_addr = active instanceof Element && region.contains(active) ? element_address(region, active) : null;
185
- const disarm = () => armed.delete(region);
186
- Promise.all([replay(), Promise.resolve(a.fire())]).then(([r]) => {
187
- disarm();
188
- r.after_wake(region, fields, active_addr, queued);
203
+ const replayed = replay();
204
+ Promise.all([replayed, Promise.resolve(a.fire(replayed))]).then(([r]) => {
205
+ if (a.state === "over") return;
206
+ disarm(a, false);
207
+ if (r !== STALE) r.after_wake(region, fields, active_addr, queued, a.owner);
189
208
  }, (err) => {
190
- disarm();
191
- replay().then((r) => r.replay_clicks(region, queued));
192
- console.error("[ogygia] interaction island failed to hydrate — leaving it static.", err);
209
+ if (a.state === "over") return;
210
+ disarm(a, false);
211
+ replay().then((r) => r !== STALE && r.replay_clicks(region, queued, a.owner), (again) => console.error(new OgygiaError("interaction_hydrate_failed"), again));
212
+ console.error(new OgygiaError("interaction_hydrate_failed"), err);
193
213
  });
194
214
  }
195
215
  /**
@@ -197,21 +217,24 @@ function wake(a, e) {
197
217
  *
198
218
  * @param region the `<ogygia-region>` element
199
219
  * @param warm prefetch the island module (no hydrate) — wired to the pointer entering the region
200
- * @param fire begin hydration; resolves when the island is live
201
- * @returns disarm() — forget the region (disconnected before any interaction)
220
+ * @param fire begin hydration ({@link WakeFn}); resolves when the island is live
221
+ * @param owner the region's lifetime in the page: the frame its clicks replay in is its
222
+ * @returns disarm(over?) — end this arming (the island is live or failed, or the region left
223
+ * before any interaction); `over`: the region left while it was woken, so its queued clicks
224
+ * are dropped instead of replayed
202
225
  */
203
- function arm_interaction(region, warm, fire) {
204
- armed.set(region, {
226
+ function arm_interaction(region, warm, fire, owner) {
227
+ const a = {
205
228
  region,
206
229
  warm,
207
230
  fire,
208
- woken: false,
231
+ owner,
232
+ state: "armed",
209
233
  warmed: false,
210
234
  queued: []
211
- });
212
- install_listeners();
213
- return () => {
214
- armed.delete(region);
215
235
  };
236
+ armed.set(region, a);
237
+ install_listeners();
238
+ return (over = false) => disarm(a, over);
216
239
  }
217
240
  export { arm_interaction, element_address, install, resolve_address };
@@ -1 +1,2 @@
1
- export declare function kit_page_thread(): Promise<void> | null;
1
+ /** `signal`: the waiter's (an island's wake). Once every waiter has left, the wait ends. */
2
+ export declare function kit_page_thread(signal?: AbortSignal): Promise<unknown> | null;
@@ -1,3 +1,5 @@
1
+ import { warn } from "../messages/lazy.js";
2
+ import { flights, scope } from "@ogygia/machine";
1
3
  // src/runtime/kit-page-thread.svelte.ts
2
4
  /**
3
5
  * KIT-WORLD PAGE THREAD (wait side). On a Kit-hydrated (csr=true) document the handle ships no
@@ -39,7 +41,6 @@
39
41
  const KIT_PAGE_KEY = Symbol.for("ogygia.kit-page");
40
42
  /** Kit constructs its `$app/state` page with this status; `initialize()` replaces it with the real one. */
41
43
  const KIT_PRE_START_STATUS = -1;
42
- let pending = null;
43
44
  function bridge_of() {
44
45
  return globalThis[KIT_PAGE_KEY];
45
46
  }
@@ -59,24 +60,37 @@ function page_applied(bridge) {
59
60
  * Dead code in a production build.
60
61
  */
61
62
  const DEV_STILL_WAITING_MS = 5e3;
62
- function kit_page_thread() {
63
+ /** `signal`: the waiter's (an island's wake). Once every waiter has left, the wait ends. */
64
+ function kit_page_thread(signal) {
63
65
  if (page_applied(bridge_of())) return null;
64
- return pending ??= new Promise((resolve) => {
65
- let slow = null;
66
- if (import.meta.env.DEV) slow = setTimeout(() => {
67
- const bridge = bridge_of();
68
- console.warn(`[ogygia] islands inside a lake / hole answer on this Kit (csr=true) page have waited ${DEV_STILL_WAITING_MS / 1e3}s for Kit to apply its page and are still holding their server HTML. ` + (bridge == null ? `Kit's client entry has not evaluated (no page bridge yet) — Kit's boot script may be blocked, failing, or missing.` : `Kit's client entry evaluated but start() has not applied the page (status is still -1) — Kit's start() may have thrown; check the console above.`) + ` They hydrate the moment Kit does; nothing is discarded meanwhile.`);
69
- }, DEV_STILL_WAITING_MS);
70
- const done = () => {
71
- if (slow !== null) clearTimeout(slow);
72
- pending = null;
73
- resolve();
74
- };
75
- await_bridge((bridge) => await_applied(bridge, done));
76
- });
66
+ return pending.get(void 0, { signal });
77
67
  }
68
+ /** The wait, shared by every island asking while Kit has not applied its page; kept for nothing
69
+ * (keep 0): once it lands, the fast path above answers. What it holds (the dev warning's timer, a
70
+ * sealed global's watch, the effect on Kit's page) is its own scope's: the flight's signal aborts
71
+ * once the last island waiting left, and that lets go of all of it. */
72
+ const pending = flights({
73
+ name: "kit page thread",
74
+ run: (_, signal) => {
75
+ const wait = scope();
76
+ wait.on(signal, "abort", () => wait.close());
77
+ return new Promise((resolve) => {
78
+ if (import.meta.env.DEV) wait.timeout(() => {
79
+ warn("kit_page_waiting", {
80
+ seconds: DEV_STILL_WAITING_MS / 1e3,
81
+ booted: bridge_of() != null
82
+ });
83
+ }, DEV_STILL_WAITING_MS);
84
+ await_bridge(wait, (bridge) => await_applied(wait, bridge, () => {
85
+ wait.close();
86
+ resolve();
87
+ }));
88
+ });
89
+ },
90
+ keep: 0
91
+ });
78
92
  /** Moment 1: the symbol is assigned. Calls back synchronously if it already is. */
79
- function await_bridge(cb) {
93
+ function await_bridge(wait, cb) {
80
94
  const g = globalThis;
81
95
  if (g[KIT_PAGE_KEY] != null) return cb(g[KIT_PAGE_KEY]);
82
96
  let value;
@@ -98,19 +112,29 @@ function await_bridge(cb) {
98
112
  set: settle
99
113
  });
100
114
  } catch {
101
- const tick = () => g[KIT_PAGE_KEY] != null ? settle(g[KIT_PAGE_KEY]) : setTimeout(tick, 50);
102
- tick();
115
+ const stop = wait.every(() => {
116
+ if (g[KIT_PAGE_KEY] == null) return;
117
+ stop();
118
+ cb(g[KIT_PAGE_KEY]);
119
+ }, 50);
103
120
  }
104
121
  }
105
122
  /** Moment 2: Kit assigns the real status onto its live page — observed by an effect, resolved once. */
106
- function await_applied(bridge, done) {
123
+ function await_applied(wait, bridge, done) {
107
124
  if (page_applied(bridge)) return done();
125
+ let ended = false;
126
+ const end = () => {
127
+ if (ended) return;
128
+ ended = true;
129
+ queueMicrotask(() => stop());
130
+ };
108
131
  const stop = $effect.root(() => {
109
132
  $effect(() => {
110
133
  if (!page_applied(bridge)) return;
111
- queueMicrotask(stop);
134
+ end();
112
135
  done();
113
136
  });
114
137
  });
138
+ wait.defer(end);
115
139
  }
116
140
  export { kit_page_thread };
@@ -9,7 +9,7 @@ export declare function wait_for_boundary(_el: HTMLElement, boundary: Element |
9
9
  export declare function mark_frozen_settled(region: HTMLElement): void;
10
10
  export declare function after_html_swap(region: HTMLElement, opts: {
11
11
  revalidate?: boolean;
12
- }): void;
12
+ }, wake_children: () => void): void;
13
13
  export declare function after_fetch_exhausted(region: HTMLElement, opts: {
14
14
  revalidate?: boolean;
15
15
  }, wake_children: () => void): void;
@@ -2,6 +2,7 @@ import { relocate_trailing_empty_comments } from "./lake-anchors.js";
2
2
  import { FROZEN_SELECTOR, document_is_stored, is_awake, is_frozen, region_is_vacant, region_max_age_ms, region_on_expire, region_remount } from "./region-attrs.js";
3
3
  import { runtime_session } from "./session.js";
4
4
  import { slots } from "./slots.js";
5
+ import { warn } from "../messages/core.js";
5
6
  // src/runtime/lakes.ts
6
7
  /** Feature entry: fill the `lakes` slot so core lifts/restores frozen regions. */
7
8
  function install() {
@@ -107,7 +108,7 @@ function on_frozen_connect(el, arm) {
107
108
  else if (when === "visible") arm.visible(fire);
108
109
  else if (when === "load") fire();
109
110
  else arm.media(when, fire);
110
- } else if (import.meta.env.DEV && region_remount(el) === "swr" && !el.parentElement?.closest("ogygia-region")) console.warn(`[ogygia] render:'live' region "${id}" has no island host — on regular pages it will never revalidate (only on documents served from a page store). Wrap it in a wake:'load' island to drive remount revalidation.`);
111
+ } else if (import.meta.env.DEV && region_remount(el) === "swr" && !el.parentElement?.closest("ogygia-region")) warn("live_region_no_host", { id });
111
112
  if (unhosted(el)) {
112
113
  runtime_session.settled_lakes.add(el);
113
114
  if (!region_is_vacant(el)) {
@@ -167,7 +168,7 @@ function on_frozen_connect(el, arm) {
167
168
  if (!cached.endpoint) {
168
169
  runtime_session.settled_lakes.add(el);
169
170
  arm.wake_children();
170
- if (import.meta.env.DEV) console.warn(`[ogygia] region "${id}" is render:'live' but no signed endpoint was captured at SSR — painting the cache only.`);
171
+ if (import.meta.env.DEV) warn("live_region_no_endpoint", { id });
171
172
  return true;
172
173
  }
173
174
  el.setAttribute("endpoint", cached.endpoint);
@@ -182,14 +183,25 @@ function on_frozen_connect(el, arm) {
182
183
  default: return policy;
183
184
  }
184
185
  }
186
+ /**
187
+ * The unsettled lakes regions authored inside them are waiting on (they connected, found the lake
188
+ * unsettled, and stopped: `wait_for_boundary`). A lake's settling wakes them, on every path it
189
+ * settles by: a refresh that MORPHS its children in place reconnects none of them (a vacant swr
190
+ * remount's cached copy, refreshed), so nothing else would.
191
+ */
192
+ const waited_on = /* @__PURE__ */ new WeakSet();
185
193
  function wait_for_boundary(_el, boundary) {
186
- return !!(boundary && is_frozen(boundary) && !runtime_session.settled_lakes.has(boundary));
194
+ if (!(boundary && is_frozen(boundary) && !runtime_session.settled_lakes.has(boundary))) return false;
195
+ waited_on.add(boundary);
196
+ return true;
187
197
  }
188
198
  function mark_frozen_settled(region) {
189
199
  if (is_frozen(region)) runtime_session.settled_lakes.add(region);
190
200
  }
191
- function after_html_swap(region, opts) {
192
- if (!(opts.revalidate && is_frozen(region))) return;
201
+ function after_html_swap(region, opts, wake_children) {
202
+ if (!is_frozen(region)) return;
203
+ if (waited_on.delete(region)) wake_children();
204
+ if (!opts.revalidate) return;
193
205
  const id = region.getAttribute("entry") || "";
194
206
  if (!id) return;
195
207
  const cached = document.createDocumentFragment();
@@ -206,6 +218,7 @@ function after_html_swap(region, opts) {
206
218
  function after_fetch_exhausted(region, opts, wake_children) {
207
219
  if (opts.revalidate && is_frozen(region) && region.isConnected) {
208
220
  runtime_session.settled_lakes.add(region);
221
+ waited_on.delete(region);
209
222
  wake_children();
210
223
  }
211
224
  }
@@ -1,3 +1,2 @@
1
- /** Feature entry: fill the `live` slot with {@link LiveHost}, and `keep` with {@link KeepHost} (a
2
- * kept island's host: the same pushable props, in a placed island's SSR shape). */
1
+ /** Feature entry: fill the `Pushable` slot with {@link PushableProps}. */
3
2
  export declare function install(): void;
@@ -1,17 +1,15 @@
1
1
  import { slots } from "./slots.js";
2
- import LiveHost from "../LiveHost.svelte";
3
- import KeepHost from "../KeepHost.svelte";
2
+ import { PushableProps } from "./pushable-props.svelte.js";
4
3
  // src/runtime/live.ts
5
4
  /**
6
- * Live-region feature: the props-pushable host a live `<Region>` hydrates through, so later
7
- * `query.live` ticks push new props into the mounted island (keep-alive) instead of re-hydrating.
5
+ * Live-region feature: the pushable props a live `<Region>` and a kept island hydrate with, so later
6
+ * `query.live` ticks (and the next page's props) reach the mounted island in place instead of
7
+ * re-hydrating it.
8
8
  *
9
9
  * A HYDRATE-phase feature (link/runtime-entry.ts): installed by the hydrate core, never the boot.
10
10
  */
11
- /** Feature entry: fill the `live` slot with {@link LiveHost}, and `keep` with {@link KeepHost} (a
12
- * kept island's host: the same pushable props, in a placed island's SSR shape). */
11
+ /** Feature entry: fill the `Pushable` slot with {@link PushableProps}. */
13
12
  function install() {
14
- slots.live = LiveHost;
15
- slots.keep = KeepHost;
13
+ slots.Pushable = PushableProps;
16
14
  }
17
15
  export { install };
@@ -1,3 +1,4 @@
1
+ import { Stale } from "@ogygia/machine";
1
2
  export type LoadClass = "user" | "visible" | "ahead" | "speculative";
2
3
  /** `code`: an island's modules. `content`: HTML (a hole's answer, a prefetched page). */
3
4
  export type LoadKind = "code" | "content";
@@ -17,13 +18,13 @@ export declare function viewport_class(el: Element): "visible" | "ahead";
17
18
  /** How the critical-resources wait ended: they `arrived` (or failed), the visitor's first `input`,
18
19
  * the `cap`, or `none` were pending. */
19
20
  export type CriticalOutcome = "arrived" | "input" | "cap" | "none";
20
- declare let critical_report: {
21
+ type CriticalReport = {
21
22
  outcome: CriticalOutcome;
22
23
  resources: number;
23
24
  waited_ms: number;
24
- } | null;
25
+ };
25
26
  /** The critical wait's outcome once it settled (the profiler's beacon reads it), else `null`. */
26
- export declare function critical_outcome(): typeof critical_report;
27
+ export declare function critical_outcome(): CriticalReport | null;
27
28
  /** The URLs a preload link may fetch: its `href`, else each `imagesrcset` candidate's. */
28
29
  export declare function preload_urls(link: HTMLLinkElement): string[];
29
30
  /**
@@ -31,23 +32,33 @@ export declare function preload_urls(link: HTMLLinkElement): string[];
31
32
  * arrived (or failed) — or at the visitor's first click or key, or {@link CRITICAL_CAP_MS} into the
32
33
  * wait. A page with no marked resource resolves right after the paint. Once resolved it stays so.
33
34
  */
34
- export declare function critical_settled(): Promise<void>;
35
- /** A queued download: its current class, and a way to lift it. */
36
- export interface LoadTicket {
37
- promote(cls: LoadClass): void;
38
- }
35
+ export declare function critical_settled(): Promise<void | Stale>;
39
36
  /**
40
- * Ask for a slot. `ready` resolves with a `release` to call when the download is done (settled,
41
- * success or not). `cls` may be a getter, re-read whenever the queue drains (viewport changes).
37
+ * A queued download: asking for one queues it. `ready` resolves with a `release` to call when the
38
+ * download is done (settled, success or not). `cls` may be a getter, re-read whenever the queue
39
+ * drains (viewport changes); `promote` lifts it.
42
40
  */
43
- export declare function load_slot(o: {
44
- kind: LoadKind;
45
- cls: LoadClass | (() => LoadClass);
46
- label?: string;
47
- }): {
48
- ready: Promise<() => void>;
49
- ticket: LoadTicket;
50
- };
41
+ export declare class LoadSlot {
42
+ #private;
43
+ readonly kind: LoadKind;
44
+ readonly label: string | undefined;
45
+ readonly queued_at: number;
46
+ readonly ready: Promise<() => void>;
47
+ /** Still in the queue: a drain takes it out when it starts. */
48
+ queued: boolean;
49
+ constructor(o: {
50
+ kind: LoadKind;
51
+ cls: LoadClass | (() => LoadClass);
52
+ label?: string;
53
+ });
54
+ /** Its class now: the getter's, or the promoted one when that ranks higher. */
55
+ cls(): LoadClass;
56
+ /** Lift it (a prefetch the visitor then navigates to). */
57
+ promote(c: LoadClass): void;
58
+ /** Out of the queue: the holder's `ready` resolves, counted in the background window when it
59
+ * is one of the guesses. */
60
+ start(cls: LoadClass, background: boolean): void;
61
+ }
51
62
  /** Test seam: a fresh document. */
52
63
  export declare function reset_load_scheduler(): void;
53
64
  export {};