ripple 0.4.1 → 0.4.3

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 (96) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/package.json +9 -3
  3. package/src/constants.js +4 -0
  4. package/src/jsx-runtime.d.ts +2 -1
  5. package/src/runtime/array.js +15 -9
  6. package/src/runtime/index-client.js +25 -7
  7. package/src/runtime/index-server.js +1 -0
  8. package/src/runtime/internal/client/attributes.js +313 -0
  9. package/src/runtime/internal/client/blocks.js +4 -14
  10. package/src/runtime/internal/client/component.js +2 -5
  11. package/src/runtime/internal/client/composite.js +14 -6
  12. package/src/runtime/internal/client/constants.js +12 -6
  13. package/src/runtime/internal/client/context.js +3 -2
  14. package/src/runtime/internal/client/css.js +11 -26
  15. package/src/runtime/internal/client/errors.js +188 -0
  16. package/src/runtime/internal/client/events.js +79 -34
  17. package/src/runtime/internal/client/expression.js +21 -116
  18. package/src/runtime/internal/client/for.js +272 -222
  19. package/src/runtime/internal/client/head.js +4 -3
  20. package/src/runtime/internal/client/html.js +5 -3
  21. package/src/runtime/internal/client/hydrate.js +484 -0
  22. package/src/runtime/internal/client/hydration-enabled.js +7 -0
  23. package/src/runtime/internal/client/hydration.js +16 -97
  24. package/src/runtime/internal/client/if.js +13 -9
  25. package/src/runtime/internal/client/index.js +16 -8
  26. package/src/runtime/internal/client/operations.js +12 -9
  27. package/src/runtime/internal/client/portal.js +3 -2
  28. package/src/runtime/internal/client/render.js +75 -321
  29. package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
  30. package/src/runtime/internal/client/runtime.js +129 -362
  31. package/src/runtime/internal/client/template-ns.js +47 -0
  32. package/src/runtime/internal/client/template.js +63 -140
  33. package/src/runtime/internal/client/track-async.js +356 -0
  34. package/src/runtime/internal/client/try.js +133 -319
  35. package/src/runtime/internal/client/types.d.ts +48 -24
  36. package/src/runtime/internal/server/index.js +38 -33
  37. package/src/runtime/internal/server/prerender.js +27 -0
  38. package/src/server/index.js +1 -0
  39. package/src/utils/class-name.js +28 -0
  40. package/tests/client/basic/basic.errors.test.tsrx +3 -3
  41. package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
  42. package/tests/client/compiler/compiler.basic.test.tsrx +4 -4
  43. package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
  44. package/tests/client/composite/composite.props.test.tsrx +10 -10
  45. package/tests/client/derived-release.test.tsrx +3 -3
  46. package/tests/client/for-item.test.tsrx +443 -0
  47. package/tests/client/for-selector.test.tsrx +95 -0
  48. package/tests/hydration/compiled/client/basic.js +97 -123
  49. package/tests/hydration/compiled/client/composite.js +5 -5
  50. package/tests/hydration/compiled/client/events.js +84 -79
  51. package/tests/hydration/compiled/client/for.js +229 -233
  52. package/tests/hydration/compiled/client/fragment-cursor.js +781 -481
  53. package/tests/hydration/compiled/client/head.js +23 -26
  54. package/tests/hydration/compiled/client/hmr.js +4 -4
  55. package/tests/hydration/compiled/client/html-in-template.js +5 -5
  56. package/tests/hydration/compiled/client/html.js +174 -208
  57. package/tests/hydration/compiled/client/if-children.js +71 -70
  58. package/tests/hydration/compiled/client/if-fragment-controlflow.js +46 -46
  59. package/tests/hydration/compiled/client/if.js +26 -26
  60. package/tests/hydration/compiled/client/mixed-control-flow.js +63 -105
  61. package/tests/hydration/compiled/client/nested-control-flow.js +170 -390
  62. package/tests/hydration/compiled/client/portal.js +10 -10
  63. package/tests/hydration/compiled/client/reactivity.js +36 -50
  64. package/tests/hydration/compiled/client/return.js +2 -2
  65. package/tests/hydration/compiled/client/streaming.js +34 -40
  66. package/tests/hydration/compiled/client/switch.js +25 -25
  67. package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
  68. package/tests/hydration/compiled/client/try.js +15 -15
  69. package/tests/hydration/compiled/client/typed-text.js +8 -19
  70. package/tests/hydration/compiled/server/basic.js +6 -6
  71. package/tests/hydration/compiled/server/events.js +13 -61
  72. package/tests/hydration/compiled/server/for.js +23 -31
  73. package/tests/hydration/compiled/server/fragment-cursor.js +214 -366
  74. package/tests/hydration/compiled/server/head.js +6 -6
  75. package/tests/hydration/compiled/server/hmr.js +1 -1
  76. package/tests/hydration/compiled/server/html.js +1 -1
  77. package/tests/hydration/compiled/server/if-children.js +9 -9
  78. package/tests/hydration/compiled/server/if.js +5 -5
  79. package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
  80. package/tests/hydration/compiled/server/portal.js +1 -1
  81. package/tests/hydration/compiled/server/reactivity.js +9 -9
  82. package/tests/hydration/compiled/server/streaming.js +9 -9
  83. package/tests/hydration/compiled/server/switch.js +4 -4
  84. package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
  85. package/tests/hydration/compiled/server/try.js +4 -4
  86. package/tests/hydration/compiled/server/typed-text.js +1 -1
  87. package/tests/hydration/components/fragment-cursor.tsrx +60 -0
  88. package/tests/hydration/components/track-async-serialization.tsrx +2 -2
  89. package/tests/hydration/fragment-cursor.test.js +34 -1
  90. package/tests/hydration/track-async-serialization.test.js +1 -1
  91. package/tests/server/basic.components.test.tsrx +3 -3
  92. package/tests/server/basic.test.tsrx +23 -0
  93. package/tests/server/track-async-serialization.test.tsrx +6 -6
  94. package/tests/utils/tracked-types.test.js +3 -3
  95. package/types/index.d.ts +8 -8
  96. package/types/server.d.ts +17 -0
@@ -9,6 +9,7 @@ import {
9
9
  set_hydrating,
10
10
  set_hydration,
11
11
  } from './hydration.js';
12
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
12
13
 
13
14
  /**
14
15
  * @param {string} hash
@@ -19,12 +20,12 @@ export function head(hash, render_fn) {
19
20
  // The head function may be called after the first hydration pass and ssr comment nodes may still be present,
20
21
  // therefore we need to skip that when we detect that we're not in hydration mode.
21
22
  let previous_hydrate_node = null;
22
- let was_hydrating = hydrating;
23
+ let was_hydrating = HYDRATION && hydrating;
23
24
 
24
25
  /** @type {Comment | Text} */
25
26
  var anchor;
26
27
 
27
- if (hydrating) {
28
+ if (HYDRATION && hydrating) {
28
29
  previous_hydrate_node = hydrate_node;
29
30
 
30
31
  var head_anchor = get_first_child(document.head);
@@ -50,7 +51,7 @@ export function head(hash, render_fn) {
50
51
  }
51
52
  }
52
53
 
53
- if (!hydrating) {
54
+ if (!(HYDRATION && hydrating)) {
54
55
  anchor = document.head.appendChild(create_text());
55
56
  }
56
57
 
@@ -1,10 +1,12 @@
1
1
  /** @import { Block } from '#client' */
2
+ import { hydration_mismatch } from './errors.js';
2
3
 
3
4
  import { remove_block_dom, render } from './blocks.js';
4
5
  import { get_first_child, get_next_sibling } from './operations.js';
5
6
  import { active_block } from './runtime.js';
6
7
  import { assign_nodes, create_fragment_from_html } from './template.js';
7
8
  import { hydrate_next, hydrating, set_hydrate_node } from './hydration.js';
9
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
8
10
  import { COMMENT_NODE } from '../../../constants.js';
9
11
 
10
12
  /**
@@ -25,7 +27,7 @@ export function html(node, get_html, svg = false, mathml = false) {
25
27
  // If the HTML hasn't changed, skip the update (but still hydrate on first run)
26
28
  if (html === new_html) {
27
29
  // During hydration, we need to skip past the content and end marker even if value hasn't changed
28
- if (hydrating) {
30
+ if (HYDRATION && hydrating) {
29
31
  // The anchor is the hash comment - we need to skip past it and its content
30
32
  set_hydrate_node(anchor);
31
33
  /** @type {Node | null} */
@@ -60,7 +62,7 @@ export function html(node, get_html, svg = false, mathml = false) {
60
62
  block.s.start = block.s.end = null;
61
63
  }
62
64
 
63
- if (hydrating) {
65
+ if (HYDRATION && hydrating) {
64
66
  set_hydrate_node(anchor);
65
67
 
66
68
  /** @type {Node | null} */
@@ -75,7 +77,7 @@ export function html(node, get_html, svg = false, mathml = false) {
75
77
  }
76
78
 
77
79
  if (next === null) {
78
- throw new Error('Hydration mismatch: expected end marker for HTML block');
80
+ hydration_mismatch('HTML');
79
81
  }
80
82
 
81
83
  // Include the hash comment and end marker in the assigned nodes
@@ -0,0 +1,484 @@
1
+ /** @import { AppendIntoAnchor, Block, TryPendingFunction, TryState } from '#client' */
2
+
3
+ import {
4
+ COMMENT_NODE,
5
+ HYDRATION_END,
6
+ HYDRATION_START,
7
+ HYDRATION_START_ERRORED,
8
+ HYDRATION_START_PENDING,
9
+ STREAM_ERROR_SCRIPT_PREFIX,
10
+ TEXT_NODE,
11
+ } from '../../../constants.js';
12
+ import { is_destroyed } from './blocks.js';
13
+ import { hydration_mismatch } from './errors.js';
14
+ import { clear_expression_range } from './expression.js';
15
+ import {
16
+ hydrate_next,
17
+ hydrate_node,
18
+ hydrating,
19
+ set_hydrate_node,
20
+ set_hydration,
21
+ set_hydration_runtime,
22
+ } from './hydration.js';
23
+ import { create_text, get_next_sibling, next_sibling_getter } from './operations.js';
24
+ import { active_block, queue_microtask } from './runtime.js';
25
+ import { assign_nodes } from './template.js';
26
+ import {
27
+ boundary_branch,
28
+ catch_error,
29
+ destroy_pending,
30
+ get_boundary_with_catch,
31
+ render_resolved,
32
+ } from './try.js';
33
+
34
+ /**
35
+ * The hydration paths of the DOM runtime. `hydrate()` installs them on the
36
+ * hydration module (see `H` there); the runtime's insert, template, expression
37
+ * and boundary code calls through that object only while hydrating, so a
38
+ * client-only mount compiles and ships none of this.
39
+ * @typedef {{
40
+ * a: typeof hydrate_append;
41
+ * t: typeof hydrate_template;
42
+ * x: typeof hydrate_text;
43
+ * e: typeof expression_end;
44
+ * h: typeof hydrated_text;
45
+ * m: typeof try_marker;
46
+ * s: typeof try_slot;
47
+ * f: typeof try_fallback;
48
+ * }} HydrationRuntime
49
+ */
50
+
51
+ /** @type {HydrationRuntime} */
52
+ var runtime = {
53
+ a: hydrate_append,
54
+ t: hydrate_template,
55
+ x: hydrate_text,
56
+ e: expression_end,
57
+ h: hydrated_text,
58
+ m: try_marker,
59
+ s: try_slot,
60
+ f: try_fallback,
61
+ };
62
+
63
+ export function install_hydration() {
64
+ set_hydration_runtime(runtime);
65
+ }
66
+
67
+ /**
68
+ * The hydration path of `append`: repositions the hydration cursor instead
69
+ * of inserting.
70
+ *
71
+ * Every hydrated node, block, and component leaves the cursor on its last
72
+ * DOM node, and whoever owns the next node steps past it: a parent element
73
+ * with its sibling traversal, a control-flow block by reaching its end
74
+ * marker, an append-into sentinel by adopting the cursor as the next
75
+ * component's first node.
76
+ * @param {ChildNode | AppendIntoAnchor} anchor
77
+ * @param {Node} dom
78
+ */
79
+ function hydrate_append(anchor, dom) {
80
+ var node = /** @type {Node} */ (hydrate_node);
81
+
82
+ // The cursor descended into dom's children (child()/sibling() traversal
83
+ // inside a single-node template) without a compiler-emitted pop(): bring it
84
+ // back up to dom before deciding where to leave it.
85
+ if (node !== dom && dom.contains(node)) {
86
+ node = dom;
87
+ }
88
+
89
+ // A child component renders into the anchor it was handed, which is its
90
+ // own first node. Its content is hydrated, so leave the cursor on the
91
+ // content's last node for the parent's sibling traversal.
92
+ if (anchor === dom) {
93
+ set_hydrate_node(node);
94
+ return;
95
+ }
96
+
97
+ if (node !== dom) {
98
+ // A fragment's cursor sits on its last top-level node, past the
99
+ // template's first node: widen the block's end to cover the whole
100
+ // fragment rather than only the node assign_nodes saw.
101
+ var s = /** @type {Block} */ (active_block).s;
102
+ if (s !== null) {
103
+ s.end = node;
104
+ }
105
+ }
106
+
107
+ // Step past the content: a branch lands on its block's end marker, the
108
+ // root on the boundary's end marker, an append-into sentinel on the next
109
+ // component's first node.
110
+ set_hydrate_node(next_sibling_getter.call(node), true);
111
+ }
112
+
113
+ /**
114
+ * Scans forward from the current hydrate_node to find the matching HYDRATION_END
115
+ * comment, handling nested blocks by tracking depth.
116
+ * Should be called after hydrate_next() has consumed the opening HYDRATION_START.
117
+ * Any `[`-prefixed comment opens a nested region — this includes the plain
118
+ * `<!--[-->` markers as well as streaming slot markers (`<!--[?N-->`,
119
+ * `<!--[!N-->`), which always pair with a `<!--]-->`.
120
+ * @returns {Node} The HYDRATION_END comment node.
121
+ */
122
+ function skip_to_hydration_end() {
123
+ var depth = 0;
124
+ var node = /** @type {Node} */ (hydrate_node);
125
+ while (true) {
126
+ if (node.nodeType === COMMENT_NODE) {
127
+ var data = /** @type {Comment} */ (node).data;
128
+ if (data === HYDRATION_END) {
129
+ if (depth === 0) return node;
130
+ depth -= 1;
131
+ } else if (data.startsWith(HYDRATION_START)) {
132
+ depth += 1;
133
+ }
134
+ }
135
+ node = /** @type {Node} */ (next_sibling_getter.call(node));
136
+ }
137
+ }
138
+
139
+ /**
140
+ * The hydration path of a `template()` instance: adopts the server node(s)
141
+ * at the cursor instead of cloning.
142
+ * @param {boolean} is_fragment
143
+ * @param {number} count
144
+ * @returns {Node}
145
+ */
146
+ function hydrate_template(is_fragment, count) {
147
+ var node = /** @type {Node} */ (hydrate_node);
148
+ assign_nodes(node, is_fragment ? hydrate_fragment_end(node, count) : node);
149
+ return node;
150
+ }
151
+
152
+ /**
153
+ * The last top-level node of a hydrated fragment template. Walks using the
154
+ * compiler-provided hop count so hydration never parses template HTML.
155
+ * @param {Node} start
156
+ * @param {number} count
157
+ * @returns {Node}
158
+ */
159
+ function hydrate_fragment_end(start, count) {
160
+ var end = start;
161
+
162
+ for (var i = 1; i < count; i++) {
163
+ var next = get_next_sibling(end);
164
+
165
+ while (next !== null && next.nodeType === COMMENT_NODE) {
166
+ next = get_next_sibling(next);
167
+ }
168
+
169
+ if (next === null) {
170
+ break;
171
+ }
172
+
173
+ end = next;
174
+ }
175
+
176
+ return end;
177
+ }
178
+
179
+ /**
180
+ * The hydration path of an expression's text: adopts the server text node at
181
+ * the cursor, splitting it when the server rendered adjacent text into one.
182
+ * @param {string} value
183
+ * @param {ChildNode} anchor
184
+ * @param {boolean} assign
185
+ * @returns {void}
186
+ */
187
+ function hydrate_text(value, anchor, assign) {
188
+ var node = hydrate_node;
189
+
190
+ if (node?.nodeType === COMMENT_NODE && /** @type {Comment} */ (node).data === HYDRATION_START) {
191
+ node = get_next_sibling(node);
192
+ }
193
+
194
+ if (node?.nodeType === TEXT_NODE) {
195
+ var current_value = /** @type {Text} */ (node).nodeValue ?? '';
196
+
197
+ if (current_value !== value) {
198
+ /** @type {Text} */ (node).nodeValue = value;
199
+
200
+ if (current_value.startsWith(value)) {
201
+ var remaining = current_value.slice(value.length);
202
+
203
+ if (remaining !== '') {
204
+ var remaining_text = create_text(remaining);
205
+ /** @type {ChildNode} */ (node).after(remaining_text);
206
+ if (assign) {
207
+ assign_nodes(node, node);
208
+ }
209
+ set_hydrate_node(remaining_text);
210
+ return;
211
+ }
212
+ }
213
+ }
214
+
215
+ if (assign) {
216
+ assign_nodes(node, node);
217
+ }
218
+ set_hydrate_node(get_next_sibling(node) ?? anchor);
219
+ return;
220
+ }
221
+
222
+ var new_text = create_text(value);
223
+
224
+ if (node !== null && node !== anchor) {
225
+ /** @type {ChildNode} */ (node).before(new_text);
226
+ } else {
227
+ anchor.before(new_text);
228
+ }
229
+
230
+ if (assign) {
231
+ assign_nodes(new_text, new_text);
232
+ }
233
+ set_hydrate_node(node ?? anchor);
234
+ }
235
+
236
+ /**
237
+ * The end marker of a server-rendered expression: the `<!--]-->` that pairs
238
+ * with the `<!--[-->` at `anchor`.
239
+ * @param {Node} anchor
240
+ * @returns {Comment}
241
+ */
242
+ function expression_end(anchor) {
243
+ /** @type {Node | null} */
244
+ var current = get_next_sibling(anchor);
245
+ var depth = 0;
246
+
247
+ while (current !== null) {
248
+ if (current.nodeType === COMMENT_NODE) {
249
+ var data = /** @type {Comment} */ (current).data;
250
+
251
+ if (data === HYDRATION_START) {
252
+ depth += 1;
253
+ } else if (data === HYDRATION_END) {
254
+ if (depth === 0) {
255
+ return /** @type {Comment} */ (current);
256
+ }
257
+
258
+ depth -= 1;
259
+ }
260
+ }
261
+
262
+ current = get_next_sibling(current);
263
+ }
264
+
265
+ hydration_mismatch('expression');
266
+ }
267
+
268
+ /**
269
+ * The single text node a server-rendered expression holds between its
270
+ * markers, or null once whatever else was there is cleared.
271
+ * @param {Node} anchor
272
+ * @param {Node} end
273
+ * @returns {Text | null}
274
+ */
275
+ function hydrated_text(anchor, end) {
276
+ var first = get_next_sibling(anchor);
277
+
278
+ if (first === end) {
279
+ return null;
280
+ }
281
+
282
+ if (first?.nodeType === TEXT_NODE && get_next_sibling(first) === end) {
283
+ return /** @type {Text} */ (first);
284
+ }
285
+
286
+ clear_expression_range(anchor, end);
287
+ return null;
288
+ }
289
+
290
+ /**
291
+ * A boundary's hydration walk begins: consumes its `<!--[-->` marker, or
292
+ * adopts the streamed slot that stands in for it (see `try_slot`).
293
+ * @param {TryState} state
294
+ */
295
+ function try_marker(state) {
296
+ var marker = /** @type {Comment} */ (hydrate_node);
297
+ var data = marker.nodeType === COMMENT_NODE ? marker.data : '';
298
+
299
+ // A slot at this anchor belongs to this boundary, including the root.
300
+ if (data.startsWith(HYDRATION_START_PENDING) || data.startsWith(HYDRATION_START_ERRORED)) {
301
+ hydrate_streamed_slot(state, marker, data);
302
+ } else {
303
+ // Settled SSR content must not transition back to pending.
304
+ if (state.p !== null) {
305
+ state.h = true;
306
+ }
307
+ hydrate_next(); // consume <!--[-->
308
+ }
309
+ }
310
+
311
+ /**
312
+ * @param {TryState} state
313
+ * @param {Comment} marker
314
+ * @param {string} data
315
+ */
316
+ function hydrate_streamed_slot(state, marker, data) {
317
+ // live streamed slot
318
+ state.si = data.slice(HYDRATION_START_PENDING.length);
319
+ state.se = data.startsWith(HYDRATION_START_ERRORED);
320
+ state.so = marker;
321
+ hydrate_next(); // consume the slot wrapper open
322
+ state.sc = /** @type {Comment} */ (skip_to_hydration_end());
323
+ var fallback_start = /** @type {Comment} */ (hydrate_node);
324
+ state.sf =
325
+ !state.se &&
326
+ state.p !== null &&
327
+ fallback_start.nodeType === COMMENT_NODE &&
328
+ fallback_start.data === HYDRATION_START;
329
+ if (state.sf) {
330
+ hydrate_next(); // consume the fallback's <!--[-->
331
+ }
332
+ }
333
+
334
+ /**
335
+ * A boundary that hydrated a still-pending streamed slot: the outer walk
336
+ * continues after the slot, and the slot's chunk activates the boundary when
337
+ * it arrives.
338
+ * @param {TryState} state
339
+ */
340
+ function try_slot(state) {
341
+ // continue the outer hydration walk after the slot
342
+ set_hydrate_node(state.sc);
343
+
344
+ var registry = (window.__RIPPLE_B__ ??= {});
345
+ var unit_id = /** @type {string} */ (state.si);
346
+ if (state.se) {
347
+ // the inline runtime already emptied the slot and marked it errored
348
+ // — route the error once the surrounding hydration has finished
349
+ queue_microtask(() => route_streamed_error(state, unit_id));
350
+ } else {
351
+ registry[unit_id] = {
352
+ a: (template, errored) => activate_streamed_chunk(state, template, errored),
353
+ };
354
+ }
355
+ }
356
+
357
+ /**
358
+ * The first run of a boundary whose chunk has not arrived: hydrates its
359
+ * fallback until activation.
360
+ * @param {TryState} state
361
+ */
362
+ function try_fallback(state) {
363
+ if (state.sf) {
364
+ state.m = 1;
365
+ state.pb = boundary_branch(() => {
366
+ /** @type {TryPendingFunction} */ (state.p)(state.a);
367
+ });
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Retires the slot wrapper markers once the slot's fate is decided: the
373
+ * open comment loses its marker data (it may still serve as the boundary
374
+ * anchor, so it must stay in the DOM) and the close comment is dropped —
375
+ * keeping `[`/`]` depth balanced for scans over surrounding slots.
376
+ * @param {TryState} state
377
+ * @returns {void}
378
+ */
379
+ function neutralize_slot_markers(state) {
380
+ /** @type {Comment} */ (state.so).data = '';
381
+ /** @type {ChildNode} */ (state.sc).remove();
382
+ }
383
+
384
+ /**
385
+ * Reads the streamed unit error envelope for this slot and routes the
386
+ * error into this boundary, or the nearest catch boundary above it.
387
+ * @param {TryState} state
388
+ * @param {string} id
389
+ * @returns {void}
390
+ */
391
+ function route_streamed_error(state, id) {
392
+ if (state.b === null || is_destroyed(state.b)) {
393
+ return;
394
+ }
395
+ neutralize_slot_markers(state);
396
+ var message = 'An error occurred during server rendering';
397
+ var script = document.getElementById(STREAM_ERROR_SCRIPT_PREFIX + id);
398
+ if (script !== null) {
399
+ try {
400
+ message = JSON.parse(/** @type {string} */ (script.textContent)).message ?? message;
401
+ } catch {
402
+ // keep the generic message
403
+ }
404
+ script.remove();
405
+ }
406
+ var error = new Error(message);
407
+ if (state.c !== null) {
408
+ catch_error(state, error);
409
+ return;
410
+ }
411
+ var outer = get_boundary_with_catch(/** @type {Block} */ (state.b));
412
+ if (outer === null) {
413
+ throw error;
414
+ }
415
+ catch_error(outer.s, error);
416
+ }
417
+
418
+ /**
419
+ * Empties the streamed slot: destroys the hydrated fallback branch and
420
+ * sweeps whatever remains between the wrapper comments (leftover marker
421
+ * comments included).
422
+ * @param {TryState} state
423
+ * @returns {void}
424
+ */
425
+ function clear_streamed_slot(state) {
426
+ destroy_pending(state);
427
+ var node = /** @type {ChildNode} */ (state.so).nextSibling;
428
+ while (node !== null && node !== state.sc) {
429
+ var next = node.nextSibling;
430
+ /** @type {ChildNode} */ (node).remove();
431
+ node = next;
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Called by the inline stream runtime when this slot's chunk arrives after
437
+ * hydration: swaps the fallback for the streamed content and claims the
438
+ * new DOM through a boundary-scoped hydration walk (trackAsync inside the
439
+ * body picks its serialized envelope up from the same chunk).
440
+ * @param {TryState} state
441
+ * @param {HTMLTemplateElement | null} template
442
+ * @param {number | undefined} [errored]
443
+ * @returns {void}
444
+ */
445
+ function activate_streamed_chunk(state, template, errored) {
446
+ if (state.b === null || is_destroyed(state.b)) {
447
+ return;
448
+ }
449
+ var id = /** @type {string} */ (state.si);
450
+ state.si = null;
451
+ if (errored) {
452
+ clear_streamed_slot(state);
453
+ route_streamed_error(state, id);
454
+ return;
455
+ }
456
+ clear_streamed_slot(state);
457
+ if (template !== null) {
458
+ /** @type {ChildNode} */ (state.sc).before(template.content);
459
+ }
460
+ var first = /** @type {ChildNode} */ (state.so).nextSibling;
461
+ if (first === null || first === state.sc) {
462
+ neutralize_slot_markers(state);
463
+ return;
464
+ }
465
+ // adopt the streamed body's own <!--[--> as the boundary anchor (the
466
+ // node the buffered-SSR hydration path would have used) and drop the
467
+ // slot wrapper comments, so the resulting DOM matches buffered SSR
468
+ // exactly like the pre-hydration swap path does
469
+ if (state.a === state.so) {
470
+ state.a = first;
471
+ }
472
+ /** @type {ChildNode} */ (state.so).remove();
473
+ /** @type {ChildNode} */ (state.sc).remove();
474
+ var previous_hydrating = hydrating;
475
+ var previous_hydrate_node = hydrate_node;
476
+ set_hydration(true, first);
477
+ hydrate_next(); // consume the streamed body's <!--[-->
478
+ try {
479
+ state.h = true;
480
+ render_resolved(state);
481
+ } finally {
482
+ set_hydration(previous_hydrating, previous_hydrate_node);
483
+ }
484
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether this build can hydrate server-rendered HTML. The Vite plugin's
3
+ * `ssr: false` build aliases this module to one exporting `false`, and the
4
+ * bundler then drops every hydration path in the runtime along with the
5
+ * hydration cursor the compiled components no longer reference.
6
+ */
7
+ export const HYDRATION = true;
@@ -1,14 +1,7 @@
1
1
  /** @import { AppendIntoAnchor, Block, Derived, Tracked } from '#client' */
2
2
 
3
- import {
4
- COMMENT_NODE,
5
- HYDRATION_END,
6
- HYDRATION_ERROR,
7
- HYDRATION_START,
8
- TEXT_NODE,
9
- } from '../../../constants.js';
3
+ import { HYDRATION_ERROR, TEXT_NODE } from '../../../constants.js';
10
4
  import { create_text, first_child_getter, next_sibling_getter } from './operations.js';
11
- import { active_block } from './runtime.js';
12
5
 
13
6
  export let hydrating = false;
14
7
 
@@ -23,6 +16,21 @@ export let hydrate_node = null;
23
16
  */
24
17
  export const track_hash_reference = new Map();
25
18
 
19
+ /**
20
+ * The hydration paths of the DOM runtime (see `hydrate.js`), installed by
21
+ * `hydrate()`: the runtime reaches them through this object, so a client-only
22
+ * mount never loads them.
23
+ * @type {import('./hydrate.js').HydrationRuntime | null}
24
+ */
25
+ export let H = null;
26
+
27
+ /**
28
+ * @param {import('./hydrate.js').HydrationRuntime} runtime
29
+ */
30
+ export function set_hydration_runtime(runtime) {
31
+ H = runtime;
32
+ }
33
+
26
34
  /**
27
35
  * @param {boolean} value
28
36
  */
@@ -76,22 +84,6 @@ export function hydrate_first_child(is_text) {
76
84
  return (hydrate_node = child);
77
85
  }
78
86
 
79
- /**
80
- * Adopts the text node that is an element's only child, leaving the cursor on
81
- * the element: nothing else inside it navigates, so the parent's sibling
82
- * traversal continues from the element with no pop().
83
- * @returns {Node}
84
- */
85
- export function hydrate_text_child() {
86
- var child = first_child_getter.call(/** @type {Node} */ (hydrate_node));
87
-
88
- if (child === null || child.nodeType !== TEXT_NODE) {
89
- child = repair_first_child(child);
90
- }
91
-
92
- return child;
93
- }
94
-
95
87
  /**
96
88
  * Handles `<p>{text}</p>` where `text` rendered empty on the server: there is
97
89
  * no text node to adopt (or, with siblings, the next node is not one), so one
@@ -146,53 +138,6 @@ function repair_next_sibling(next_sibling) {
146
138
  return text;
147
139
  }
148
140
 
149
- /**
150
- * The hydration path of `append`: repositions the hydration cursor instead
151
- * of inserting. Kept out of the insert path so a client-only mount never
152
- * compiles it.
153
- *
154
- * Every hydrated node, block, and component leaves the cursor on its last
155
- * DOM node, and whoever owns the next node steps past it: a parent element
156
- * with its sibling traversal, a control-flow block by reaching its end
157
- * marker, an append-into sentinel by adopting the cursor as the next
158
- * component's first node.
159
- * @param {ChildNode | AppendIntoAnchor} anchor
160
- * @param {Node} dom
161
- */
162
- export function hydrate_append(anchor, dom) {
163
- var node = /** @type {Node} */ (hydrate_node);
164
-
165
- // The cursor descended into dom's children (child()/sibling() traversal
166
- // inside a single-node template) without a compiler-emitted pop(): bring it
167
- // back up to dom before deciding where to leave it.
168
- if (node !== dom && dom.contains(node)) {
169
- node = dom;
170
- }
171
-
172
- // A child component renders into the anchor it was handed, which is its
173
- // own first node. Its content is hydrated, so leave the cursor on the
174
- // content's last node for the parent's sibling traversal.
175
- if (anchor === dom) {
176
- hydrate_node = node;
177
- return;
178
- }
179
-
180
- if (node !== dom) {
181
- // A fragment's cursor sits on its last top-level node, past the
182
- // template's first node: widen the block's end to cover the whole
183
- // fragment rather than only the node assign_nodes saw.
184
- var s = /** @type {Block} */ (active_block).s;
185
- if (s !== null) {
186
- s.end = node;
187
- }
188
- }
189
-
190
- // Step past the content: a branch lands on its block's end marker, the
191
- // root on the boundary's end marker, an append-into sentinel on the next
192
- // component's first node.
193
- hydrate_node = next_sibling_getter.call(node);
194
- }
195
-
196
141
  export function next(n = 1) {
197
142
  if (hydrating) {
198
143
  var node = hydrate_node;
@@ -210,29 +155,3 @@ export function pop(node) {
210
155
  if (!hydrating) return;
211
156
  hydrate_node = node;
212
157
  }
213
-
214
- /**
215
- * Scans forward from the current hydrate_node to find the matching HYDRATION_END
216
- * comment, handling nested blocks by tracking depth.
217
- * Should be called after hydrate_next() has consumed the opening HYDRATION_START.
218
- * Any `[`-prefixed comment opens a nested region — this includes the plain
219
- * `<!--[-->` markers as well as streaming slot markers (`<!--[?N-->`,
220
- * `<!--[!N-->`), which always pair with a `<!--]-->`.
221
- * @returns {Node} The HYDRATION_END comment node.
222
- */
223
- export function skip_to_hydration_end() {
224
- var depth = 0;
225
- var node = /** @type {Node} */ (hydrate_node);
226
- while (true) {
227
- if (node.nodeType === COMMENT_NODE) {
228
- var data = /** @type {Comment} */ (node).data;
229
- if (data === HYDRATION_END) {
230
- if (depth === 0) return node;
231
- depth -= 1;
232
- } else if (data.startsWith(HYDRATION_START)) {
233
- depth += 1;
234
- }
235
- }
236
- node = /** @type {Node} */ (next_sibling_getter.call(node));
237
- }
238
- }