ripple 0.3.128 → 0.4.1

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 (197) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/package.json +11 -4
  3. package/src/jsx-runtime.d.ts +12 -2
  4. package/src/runtime/element.js +23 -6
  5. package/src/runtime/index-client.js +81 -41
  6. package/src/runtime/index-server.js +1 -0
  7. package/src/runtime/internal/client/blocks.js +173 -91
  8. package/src/runtime/internal/client/component.js +4 -30
  9. package/src/runtime/internal/client/composite.js +12 -13
  10. package/src/runtime/internal/client/constants.js +9 -0
  11. package/src/runtime/internal/client/context.js +12 -19
  12. package/src/runtime/internal/client/css.js +7 -9
  13. package/src/runtime/internal/client/events.js +178 -90
  14. package/src/runtime/internal/client/expression.js +45 -25
  15. package/src/runtime/internal/client/for.js +409 -129
  16. package/src/runtime/internal/client/head.js +10 -5
  17. package/src/runtime/internal/client/html.js +1 -0
  18. package/src/runtime/internal/client/hydration.js +152 -10
  19. package/src/runtime/internal/client/if.js +239 -50
  20. package/src/runtime/internal/client/index.js +4 -12
  21. package/src/runtime/internal/client/operations.js +40 -61
  22. package/src/runtime/internal/client/portal.js +163 -85
  23. package/src/runtime/internal/client/render.js +44 -43
  24. package/src/runtime/internal/client/rpc.js +3 -3
  25. package/src/runtime/internal/client/runtime.js +592 -433
  26. package/src/runtime/internal/client/selector.js +5 -2
  27. package/src/runtime/internal/client/template.js +133 -158
  28. package/src/runtime/internal/client/transport.js +38 -0
  29. package/src/runtime/internal/client/try.js +465 -426
  30. package/src/runtime/internal/client/types.d.ts +56 -23
  31. package/src/runtime/internal/client/utils.js +1 -3
  32. package/src/runtime/internal/server/dynamic.js +20 -21
  33. package/src/runtime/internal/server/index.js +329 -277
  34. package/src/runtime/internal/server/rpc.js +3 -2
  35. package/src/runtime/internal/server/transport.js +16 -0
  36. package/src/runtime/transport.js +21 -0
  37. package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
  38. package/tests/client/array/array.copy-within.test.tsrx +12 -12
  39. package/tests/client/array/array.derived.test.tsrx +46 -46
  40. package/tests/client/array/array.iteration.test.tsrx +10 -10
  41. package/tests/client/array/array.mutations.test.tsrx +20 -20
  42. package/tests/client/array/array.static.test.tsrx +11 -11
  43. package/tests/client/array/array.to-methods.test.tsrx +6 -6
  44. package/tests/client/async-suspend.test.tsrx +74 -75
  45. package/tests/client/basic/basic.attributes.test.tsrx +123 -83
  46. package/tests/client/basic/basic.collections.test.tsrx +12 -12
  47. package/tests/client/basic/basic.components.test.tsrx +99 -25
  48. package/tests/client/basic/basic.errors.test.tsrx +18 -18
  49. package/tests/client/basic/basic.events.test.tsrx +42 -42
  50. package/tests/client/basic/basic.get-set.test.tsrx +8 -8
  51. package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
  52. package/tests/client/basic/basic.rendering.test.tsrx +25 -25
  53. package/tests/client/basic/basic.utilities.test.tsrx +3 -3
  54. package/tests/client/boundaries.test.tsrx +8 -8
  55. package/tests/client/boxed-locals.test.tsrx +452 -0
  56. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
  57. package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
  58. package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
  59. package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
  60. package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
  61. package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
  62. package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
  63. package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
  64. package/tests/client/composite/composite.props.test.tsrx +149 -12
  65. package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
  66. package/tests/client/composite/composite.render.test.tsrx +26 -9
  67. package/tests/client/css/scoped-styles.test.tsrx +8 -8
  68. package/tests/client/date.test.tsrx +42 -42
  69. package/tests/client/derived-release.test.tsrx +370 -0
  70. package/tests/client/dynamic-elements.test.tsrx +45 -45
  71. package/tests/client/events.test.tsrx +97 -71
  72. package/tests/client/for-selector.test.tsrx +14 -14
  73. package/tests/client/for-single-root-items.test.tsrx +118 -3
  74. package/tests/client/for.test.tsrx +332 -31
  75. package/tests/client/head.test.tsrx +19 -19
  76. package/tests/client/html.test.tsrx +11 -9
  77. package/tests/client/if-block.test.tsrx +80 -0
  78. package/tests/client/map.test.tsrx +16 -16
  79. package/tests/client/media-query.test.tsrx +7 -7
  80. package/tests/client/portal.test.tsrx +224 -26
  81. package/tests/client/primitive-text.test.tsrx +50 -0
  82. package/tests/client/prop-kind-switch.test.tsrx +141 -0
  83. package/tests/client/ref.test.tsrx +17 -17
  84. package/tests/client/return.test.tsrx +26 -3
  85. package/tests/client/root-boundary.test.tsrx +42 -0
  86. package/tests/client/scoped-flush.test.tsrx +8 -8
  87. package/tests/client/set.test.tsrx +6 -6
  88. package/tests/client/svg.test.tsrx +11 -11
  89. package/tests/client/switch.test.tsrx +44 -44
  90. package/tests/client/track-async-hydration.test.tsrx +4 -4
  91. package/tests/client/try.test.tsrx +151 -83
  92. package/tests/client/tsx.test.tsrx +43 -40
  93. package/tests/client/url/url.derived.test.tsrx +6 -6
  94. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
  95. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
  96. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
  97. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
  98. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
  99. package/tests/hydration/basic.test.js +74 -1
  100. package/tests/hydration/build-components.js +22 -1
  101. package/tests/hydration/compiled/client/basic.js +1142 -713
  102. package/tests/hydration/compiled/client/composite.js +151 -100
  103. package/tests/hydration/compiled/client/events.js +184 -171
  104. package/tests/hydration/compiled/client/for.js +1472 -1132
  105. package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
  106. package/tests/hydration/compiled/client/head.js +254 -201
  107. package/tests/hydration/compiled/client/hmr.js +54 -41
  108. package/tests/hydration/compiled/client/html-in-template.js +54 -39
  109. package/tests/hydration/compiled/client/html.js +1633 -1263
  110. package/tests/hydration/compiled/client/if-children.js +420 -270
  111. package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
  112. package/tests/hydration/compiled/client/if.js +229 -219
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
  115. package/tests/hydration/compiled/client/portal.js +87 -95
  116. package/tests/hydration/compiled/client/reactivity.js +135 -101
  117. package/tests/hydration/compiled/client/return.js +28 -19
  118. package/tests/hydration/compiled/client/streaming.js +390 -306
  119. package/tests/hydration/compiled/client/switch.js +258 -266
  120. package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
  121. package/tests/hydration/compiled/client/try.js +173 -129
  122. package/tests/hydration/compiled/client/typed-text.js +71 -0
  123. package/tests/hydration/compiled/fixtures/money.js +10 -0
  124. package/tests/hydration/compiled/server/basic.js +123 -6
  125. package/tests/hydration/compiled/server/composite.js +16 -30
  126. package/tests/hydration/compiled/server/events.js +66 -18
  127. package/tests/hydration/compiled/server/for.js +117 -41
  128. package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
  129. package/tests/hydration/compiled/server/head.js +20 -20
  130. package/tests/hydration/compiled/server/hmr.js +2 -2
  131. package/tests/hydration/compiled/server/html.js +70 -238
  132. package/tests/hydration/compiled/server/if-children.js +107 -15
  133. package/tests/hydration/compiled/server/if.js +11 -15
  134. package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
  135. package/tests/hydration/compiled/server/portal.js +14 -22
  136. package/tests/hydration/compiled/server/reactivity.js +14 -14
  137. package/tests/hydration/compiled/server/streaming.js +19 -19
  138. package/tests/hydration/compiled/server/switch.js +8 -8
  139. package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
  140. package/tests/hydration/compiled/server/try.js +8 -8
  141. package/tests/hydration/compiled/server/typed-text.js +17 -0
  142. package/tests/hydration/components/basic.tsrx +44 -8
  143. package/tests/hydration/components/composite.tsrx +2 -2
  144. package/tests/hydration/components/events.tsrx +25 -25
  145. package/tests/hydration/components/for.tsrx +115 -67
  146. package/tests/hydration/components/fragment-cursor.tsrx +650 -0
  147. package/tests/hydration/components/head.tsrx +16 -16
  148. package/tests/hydration/components/hmr.tsrx +2 -2
  149. package/tests/hydration/components/html.tsrx +7 -7
  150. package/tests/hydration/components/if-children.tsrx +72 -25
  151. package/tests/hydration/components/if.tsrx +18 -18
  152. package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
  153. package/tests/hydration/components/portal.tsrx +3 -3
  154. package/tests/hydration/components/reactivity.tsrx +16 -16
  155. package/tests/hydration/components/streaming.tsrx +20 -20
  156. package/tests/hydration/components/switch.tsrx +20 -20
  157. package/tests/hydration/components/text-types.ts +4 -0
  158. package/tests/hydration/components/track-async-serialization.tsrx +80 -26
  159. package/tests/hydration/components/try.tsrx +8 -8
  160. package/tests/hydration/components/typed-text.tsrx +22 -0
  161. package/tests/hydration/fixtures/money.js +10 -0
  162. package/tests/hydration/for.test.js +36 -0
  163. package/tests/hydration/fragment-cursor.test.js +111 -0
  164. package/tests/hydration/if-children.test.js +24 -0
  165. package/tests/hydration/streaming.test.js +19 -0
  166. package/tests/hydration/track-async-serialization.test.js +87 -2
  167. package/tests/hydration/tsconfig.text-types.json +10 -0
  168. package/tests/hydration/typed-text.test.js +34 -0
  169. package/tests/server/await.test.tsrx +3 -3
  170. package/tests/server/basic.attributes.test.tsrx +70 -42
  171. package/tests/server/basic.components.test.tsrx +64 -8
  172. package/tests/server/basic.test.tsrx +34 -32
  173. package/tests/server/compiler.test.tsrx +1 -70
  174. package/tests/server/composite.props.test.tsrx +6 -6
  175. package/tests/server/dynamic-elements.test.tsrx +31 -8
  176. package/tests/server/for.test.tsrx +94 -0
  177. package/tests/server/head.test.tsrx +11 -11
  178. package/tests/server/scoped-styles.test.tsrx +2 -2
  179. package/tests/server/streaming-ssr.test.tsrx +48 -43
  180. package/tests/server/track-async-serialization.test.tsrx +204 -21
  181. package/tests/server/try.test.tsrx +48 -48
  182. package/tests/types/transport.ts +40 -0
  183. package/tests/utils/jsx-runtime-types.test.js +67 -2
  184. package/tests/utils/tracked-types.test.js +117 -0
  185. package/tests/utils/vite-plugin-config.test.js +23 -0
  186. package/types/index.d.ts +49 -9
  187. package/types/server.d.ts +3 -0
  188. package/types/transport.d.ts +15 -0
  189. package/src/runtime/internal/client/switch.js +0 -113
  190. package/src/utils/errors.js +0 -13
  191. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +0 -12
  192. package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +0 -46
  193. package/tests/client/lazy-array.test.tsrx +0 -31
  194. package/tests/client/lazy-destructuring.test.tsrx +0 -438
  195. package/tests/client/tracked-index-access.test.tsrx +0 -113
  196. package/tests/server/lazy-destructuring.test.tsrx +0 -595
  197. package/tests/server/tracked-index-access.test.tsrx +0 -76
@@ -1,4 +1,4 @@
1
- /** @import { Block, Component, Dependency, BlockWithTryBoundaryAndCatch, DeferredTrackedEntry } from '#client' */
1
+ /** @import { AppendIntoAnchor, Block, Component, Dependency, BlockWithTryBoundaryAndCatch, DeferredTrackedEntry } from '#client' */
2
2
  /** @import { NAMESPACE_URI } from './constants.js' */
3
3
  /** @typedef {TrackedValue} Tracked */
4
4
  /** @typedef {DerivedValue} Derived */
@@ -27,7 +27,6 @@ import {
27
27
  TRACKED,
28
28
  UNINITIALIZED,
29
29
  REF_PROP,
30
- TRACKED_OBJECT,
31
30
  DEFAULT_NAMESPACE,
32
31
  TRACKED_UPDATED,
33
32
  SUSPENSE_PENDING,
@@ -36,33 +35,34 @@ import {
36
35
  DIRECT_CHILD_BLOCK,
37
36
  SCHEDULED,
38
37
  SELECTOR,
38
+ IF_BLOCK,
39
+ RELEASED,
40
+ RENDER_ENTRY,
41
+ CREATES_DERIVEDS,
39
42
  } from './constants.js';
40
43
  import {
41
44
  begin_boundary_request,
42
45
  complete_boundary_request,
43
46
  get_boundary_with_catch,
44
47
  get_pending_boundary,
48
+ handle_boundary_error,
45
49
  register_boundary_deferred,
46
50
  register_boundary_paused_block,
47
51
  replace_boundary_request,
48
52
  } from './try.js';
49
53
  import { is_ripple_object } from './utils.js';
54
+ import { render_value } from './expression.js';
55
+ import { throw_invalid_component_type } from './component.js';
50
56
 
51
57
  import {
52
- iterable_array_from,
53
58
  define_property,
54
59
  get_descriptor,
55
- get_own_property_symbols,
56
60
  is_array,
57
61
  object_keys,
58
62
  } from '@tsrx/core/runtime/language-helpers';
59
63
  import { get_async_track_result } from '../../../utils/async.js';
60
- import {
61
- throw_tracked_index_reference_error,
62
- throw_tracked_index_value_error,
63
- } from '../../../utils/errors.js';
64
64
  import { get_track_async_script_id } from '../../../utils/track-async-serialization.js';
65
- import * as devalue from 'devalue';
65
+ import { revive } from './transport.js';
66
66
  import { hydrating, track_hash_reference } from './hydration.js';
67
67
  import { create_ref_prop as create_core_ref_prop } from '@tsrx/core/runtime/ref';
68
68
 
@@ -82,8 +82,25 @@ export let active_namespace = DEFAULT_NAMESPACE;
82
82
  /** @type {boolean} */
83
83
  export let is_mutating_allowed = true;
84
84
 
85
- /** @type {Map<Tracked | Derived, any>} */
86
- var old_values = new Map();
85
+ /**
86
+ * Tracked values written during the current flush that hold their previous
87
+ * value in `o`, for teardowns that read them; released when the flush ends.
88
+ * @type {(Tracked | Derived)[]}
89
+ */
90
+ var old_value_holders = [];
91
+ /** Nesting depth of running flushes; old values are released at depth zero. */
92
+ var flush_depth = 0;
93
+
94
+ /** @returns {void} */
95
+ function release_old_values() {
96
+ var holders = old_value_holders;
97
+ var length = holders.length;
98
+ if (length === 0) return;
99
+ for (var i = 0; i < length; i++) {
100
+ holders[i].o = UNINITIALIZED;
101
+ }
102
+ holders.length = 0;
103
+ }
87
104
 
88
105
  // Used for controlling the flush of blocks
89
106
  /** @type {number} */
@@ -168,7 +185,10 @@ export function run_teardown(block) {
168
185
  active_reaction = null;
169
186
  tracking = false;
170
187
  teardown = true;
171
- fn.call(null);
188
+ // Runtime-internal teardowns keyed on block state get it as their
189
+ // argument, so they need no per-block closure; user effects have
190
+ // null state.
191
+ fn.call(null, block.s);
172
192
  } finally {
173
193
  active_block = previous_block;
174
194
  active_reaction = previous_reaction;
@@ -293,13 +313,104 @@ function run_derived(computed) {
293
313
  export function handle_error(error, block) {
294
314
  var boundary_with_catch = get_boundary_with_catch(block);
295
315
  if (boundary_with_catch !== null) {
296
- boundary_with_catch.s.c(error);
316
+ handle_boundary_error(boundary_with_catch, error);
297
317
  return boundary_with_catch;
298
318
  }
299
319
 
300
320
  throw error;
301
321
  }
302
322
 
323
+ /**
324
+ * The error path of {@link run_block}, kept out of that hot function so a
325
+ * healthy mount never compiles it: routes real errors to the nearest catch
326
+ * boundary and pauses a block that read a pending async value under its
327
+ * pending boundary.
328
+ * @param {unknown} error
329
+ * @param {Block} block
330
+ */
331
+ function handle_run_error(error, block) {
332
+ var is_component_direct = false;
333
+ var is_try_fn_block = false;
334
+ finish_dependencies(block, active_dependency);
335
+ // When a derived read throws ASYNC_DERIVED_READ_THROWN, it means the
336
+ // derived is still SUSPENSE_PENDING. The dependency was already registered,
337
+ // so we swallow the throw and let the parent continue processing. When
338
+ // the derived settles, the block will be dirty and rerun automatically.
339
+ if (error !== ASYNC_DERIVED_READ_THROWN) {
340
+ handle_error(error, block);
341
+ } else if (
342
+ // pending async tracked was read outside allowed blocks
343
+ (is_component_direct = active_component?.b === block) ||
344
+ (is_try_fn_block =
345
+ block.p !== null && (block.p.f & TRY_BLOCK) !== 0 && (block.f & DIRECT_CHILD_BLOCK) !== 0)
346
+ ) {
347
+ throw new Error(
348
+ `Reads on pending tracked values directly inside ${is_component_direct ? 'component' : 'try/pending/catch'} body are prohibited. Use trackPending() test or peek() for safe access or create another derived instead.`,
349
+ );
350
+ } else {
351
+ // pending async tracked was read and threw ASYNC_DERIVED_READ_THROWN
352
+ var boundary = get_pending_boundary(block);
353
+ if (boundary !== null) {
354
+ pause_block(block);
355
+ register_boundary_paused_block(boundary, block);
356
+
357
+ // Register deferred boundary completions for async tracked deps.
358
+ // This handles the case where a child boundary reads a tracked value
359
+ // whose resolution is managed by a different (parent) boundary.
360
+ var dep = block.d;
361
+ while (dep !== null) {
362
+ var dep_tracked = /** @type {Tracked} */ (dep.t);
363
+ if (
364
+ (dep_tracked.__v === SUSPENSE_PENDING || dep_tracked.__v === SUSPENSE_REJECTED) &&
365
+ (dep_tracked.f & TRACKED) !== 0
366
+ ) {
367
+ var deferred_req = begin_boundary_request(boundary);
368
+ var entry = /** @type {DeferredTrackedEntry} */ ({ b: boundary, r: deferred_req });
369
+ if (dep_tracked.d === null) {
370
+ dep_tracked.d = [entry];
371
+ } else {
372
+ dep_tracked.d.push(entry);
373
+ }
374
+ }
375
+ dep = dep.n;
376
+ }
377
+ }
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Clears what a block's previous run left behind before it runs again. A
383
+ * list's children are all item branches, so there is nothing to sweep; an if
384
+ * block's children are its branch, which it replaces itself only when the
385
+ * condition changes. Returns the deriveds the previous run created, to be
386
+ * released once the new run has destroyed whatever read them (an if block
387
+ * swaps its branch inside its own run).
388
+ * @param {Block} block
389
+ * @returns {Derived[] | null}
390
+ */
391
+ function prepare_rerun(block) {
392
+ if ((block.f & (FOR_BLOCK | IF_BLOCK)) === 0) {
393
+ destroy_non_branch_children(block);
394
+ }
395
+ run_teardown(block);
396
+ return (block.f & CREATES_DERIVEDS) !== 0 ? take_created_deriveds(block) : null;
397
+ }
398
+
399
+ /**
400
+ * @param {Block} block
401
+ * @param {(state?: any) => void} teardown
402
+ */
403
+ function register_teardown(block, teardown) {
404
+ block.t = teardown;
405
+ /** @type {Block | null} */
406
+ var current = block;
407
+
408
+ while (current !== null && (current.f & CONTAINS_TEARDOWN) === 0) {
409
+ current.f ^= CONTAINS_TEARDOWN;
410
+ current = current.p;
411
+ }
412
+ }
413
+
303
414
  /**
304
415
  * @param {Block} block
305
416
  * @param {boolean} [first_run] true when the block has no children, teardown,
@@ -311,6 +422,8 @@ export function run_block(block, first_run = false) {
311
422
  var previous_tracking = tracking;
312
423
  var previous_dependency = active_dependency;
313
424
  var previous_component = active_component;
425
+ /** @type {Derived[] | null} */
426
+ var previous_deriveds = null;
314
427
 
315
428
  try {
316
429
  active_block = block;
@@ -318,11 +431,7 @@ export function run_block(block, first_run = false) {
318
431
  active_component = block.co;
319
432
 
320
433
  if (!first_run) {
321
- // A list's children are all item branches, so there is nothing to sweep.
322
- if ((block.f & FOR_BLOCK) === 0) {
323
- destroy_non_branch_children(block);
324
- }
325
- run_teardown(block);
434
+ previous_deriveds = prepare_rerun(block);
326
435
  }
327
436
 
328
437
  tracking = (block.f & (ROOT_BLOCK | BRANCH_BLOCK)) === 0;
@@ -330,71 +439,34 @@ export function run_block(block, first_run = false) {
330
439
  var res = block.fn(block.s);
331
440
 
332
441
  if (typeof res === 'function') {
333
- block.t = res;
334
- /** @type {Block | null} */
335
- let current = block;
336
-
337
- while (current !== null && (current.f & CONTAINS_TEARDOWN) === 0) {
338
- current.f ^= CONTAINS_TEARDOWN;
339
- current = current.p;
340
- }
442
+ register_teardown(block, res);
341
443
  }
342
444
 
343
- finish_dependencies(block, active_dependency);
344
- } catch (error) {
345
- var is_component_direct = false;
346
- var is_try_fn_block = false;
347
- finish_dependencies(block, active_dependency);
348
- // When a derived read throws ASYNC_DERIVED_READ_THROWN, it means the
349
- // derived is still SUSPENSE_PENDING. The dependency was already registered,
350
- // so we swallow the throw and let the parent continue processing. When
351
- // the derived settles, the block will be dirty and rerun automatically.
352
- if (error !== ASYNC_DERIVED_READ_THROWN) {
353
- handle_error(error, block);
354
- } else if (
355
- // pending async tracked was read outside allowed blocks
356
- (is_component_direct = active_component?.b === block) ||
357
- (is_try_fn_block =
358
- block.p !== null && (block.p.f & TRY_BLOCK) !== 0 && (block.f & DIRECT_CHILD_BLOCK) !== 0)
359
- ) {
360
- throw new Error(
361
- `Reads on pending tracked values directly inside ${is_component_direct ? 'component' : 'try/pending/catch'} body are prohibited. Use trackPending() test or peek() for safe access or create another derived instead.`,
362
- );
363
- } else {
364
- // pending async tracked was read and threw ASYNC_DERIVED_READ_THROWN
365
- var boundary = get_pending_boundary(block);
366
- if (boundary !== null) {
367
- pause_block(block);
368
- register_boundary_paused_block(boundary, block);
369
-
370
- // Register deferred boundary completions for async tracked deps.
371
- // This handles the case where a child boundary reads a tracked value
372
- // whose resolution is managed by a different (parent) boundary.
373
- var dep = block.d;
374
- while (dep !== null) {
375
- var dep_tracked = /** @type {Tracked} */ (dep.t);
376
- if (
377
- (dep_tracked.__v === SUSPENSE_PENDING || dep_tracked.__v === SUSPENSE_REJECTED) &&
378
- (dep_tracked.f & TRACKED) !== 0
379
- ) {
380
- var deferred_req = begin_boundary_request(boundary);
381
- var entry = /** @type {DeferredTrackedEntry} */ ({ b: boundary, r: deferred_req });
382
- if (dep_tracked.d === null) {
383
- dep_tracked.d = [entry];
384
- } else {
385
- dep_tracked.d.push(entry);
386
- }
387
- }
388
- dep = dep.n;
389
- }
445
+ if (block.d === null) {
446
+ // First run (or no dependencies so far): nothing stale to unlink.
447
+ if (active_dependency !== null) {
448
+ block.d = active_dependency;
390
449
  }
450
+ } else {
451
+ finish_dependencies(block, active_dependency);
391
452
  }
453
+ } catch (error) {
454
+ handle_run_error(error, block);
392
455
  } finally {
393
456
  active_block = previous_block;
394
457
  active_reaction = previous_reaction;
395
458
  tracking = previous_tracking;
396
459
  active_dependency = previous_dependency;
397
460
  active_component = previous_component;
461
+
462
+ // The previous run's deriveds, whether this run finished or threw, and
463
+ // after this run's dependencies replaced the old ones (so a derived the
464
+ // old run read is not kept alive by that stale link). Those still read
465
+ // stay recorded under the block for its next rerun or destruction: a
466
+ // rerun that keeps the same branch must not lose them.
467
+ if (previous_deriveds !== null) {
468
+ keep_deriveds(block, release_deriveds(previous_deriveds));
469
+ }
398
470
  }
399
471
  }
400
472
 
@@ -419,11 +491,11 @@ class TrackedValue {
419
491
  /**
420
492
  * @param {any} v
421
493
  * @param {Block} block
422
- * @param {{ get?: Function; set?: Function }} a
494
+ * @param {{ get?: Function; set?: Function | true }} a
423
495
  * @param {string} [hash]
424
496
  */
425
497
  constructor(v, block, a, hash) {
426
- /** @type {{ get?: Function; set?: Function }} */
498
+ /** @type {{ get?: Function; set?: Function | true }} */
427
499
  this.a = a;
428
500
  /** @type {Block} */
429
501
  this.b = block;
@@ -439,18 +511,8 @@ class TrackedValue {
439
511
  this.sb = null;
440
512
  /** @type {any} */
441
513
  this.__v = v;
442
- }
443
- /** @returns {any} */
444
- get [0]() {
445
- return throw_tracked_index_value_error();
446
- }
447
- /** @param {any} v */
448
- set [0](v) {
449
- throw_tracked_index_value_error();
450
- }
451
- /** @returns {Tracked} */
452
- get [1]() {
453
- return throw_tracked_index_reference_error();
514
+ /** @type {any} previous value while a flush with teardowns is running */
515
+ this.o = UNINITIALIZED;
454
516
  }
455
517
  /** @returns {any} */
456
518
  get value() {
@@ -460,14 +522,16 @@ class TrackedValue {
460
522
  set value(v) {
461
523
  set(this, v);
462
524
  }
463
- /** @returns {2} */
464
- get length() {
465
- return 2;
466
- }
467
- /** @returns {Iterator<any | Tracked>} */
468
- *[Symbol.iterator]() {
469
- yield get_tracked(this);
470
- yield this;
525
+ /**
526
+ * A read-only view: a derived over this value, for a receiver that should
527
+ * read but not write it. Equivalent to `track(() => tracked.value)`. The
528
+ * view is owned by the block that creates it, not by this value's block, so
529
+ * a view made in a component is released with that component even when the
530
+ * value outlives it.
531
+ * @returns {Derived}
532
+ */
533
+ readOnly() {
534
+ return derived(() => get_tracked(this), /** @type {Block} */ (active_block));
471
535
  }
472
536
  }
473
537
 
@@ -475,11 +539,11 @@ class DerivedValue {
475
539
  /**
476
540
  * @param {Function} fn
477
541
  * @param {Block} block
478
- * @param {{ get?: Function; set?: Function }} a
542
+ * @param {{ get?: Function; set?: Function | true }} a
479
543
  * @param {string} [hash]
480
544
  */
481
545
  constructor(fn, block, a, hash) {
482
- /** @type {{ get?: Function; set?: Function }} */
546
+ /** @type {{ get?: Function; set?: Function | true }} */
483
547
  this.a = a;
484
548
  /** @type {Block} */
485
549
  this.b = block;
@@ -501,18 +565,8 @@ class DerivedValue {
501
565
  this.sb = null;
502
566
  /** @type {any} */
503
567
  this.__v = UNINITIALIZED;
504
- }
505
- /** @returns {any} */
506
- get [0]() {
507
- return throw_tracked_index_value_error();
508
- }
509
- /** @param {any} v */
510
- set [0](v) {
511
- throw_tracked_index_value_error();
512
- }
513
- /** @returns {Derived} */
514
- get [1]() {
515
- return throw_tracked_index_reference_error();
568
+ /** @type {any} previous value while a flush with teardowns is running */
569
+ this.o = UNINITIALIZED;
516
570
  }
517
571
  /** @returns {any} */
518
572
  get value() {
@@ -522,14 +576,16 @@ class DerivedValue {
522
576
  set value(v) {
523
577
  set(this, v);
524
578
  }
525
- /** @returns {2} */
526
- get length() {
527
- return 2;
528
- }
529
- /** @returns {Iterator<any | Derived>} */
530
- *[Symbol.iterator]() {
531
- yield get_derived(this);
532
- yield this;
579
+ /**
580
+ * A read-only view (see `TrackedValue#readOnly`). A derived without a
581
+ * setter is already read-only and is returned as is; a writable one is
582
+ * wrapped in a derived that follows it.
583
+ * @returns {Derived}
584
+ */
585
+ readOnly() {
586
+ return this.a.set === undefined
587
+ ? this
588
+ : derived(() => get_derived(this), /** @type {Block} */ (active_block));
533
589
  }
534
590
  }
535
591
 
@@ -544,7 +600,7 @@ if (DEV) {
544
600
  * @param {Block} block
545
601
  * @param {string} [hash]
546
602
  * @param {(value: any) => any} [get]
547
- * @param {(next: any, prev: any) => any} [set]
603
+ * @param {((next: any, prev: any) => any) | true} [set]
548
604
  * @returns {Tracked}
549
605
  */
550
606
  export function tracked(v, block, hash, get, set) {
@@ -562,25 +618,126 @@ export function tracked(v, block, hash, get, set) {
562
618
  * @param {Block} block
563
619
  * @param {string} [hash]
564
620
  * @param {(value: any) => any} [get]
565
- * @param {(next: any, prev: any) => any} [set]
621
+ * @param {((next: any, prev: any) => any) | true} [set]
566
622
  * @returns {Derived}
567
623
  */
568
624
  export function derived(fn, block, hash, get, set) {
569
625
  var d = /** @type {Derived} */ (
570
626
  new DerivedValue(fn, block || active_block, get || set ? { get, set } : empty_get_set, hash)
571
627
  );
628
+ // The run that creates a derived owns its release: when that block reruns
629
+ // or is destroyed, the derived is unsubscribed from its sources unless a
630
+ // reader that is still alive holds it (see `release_deriveds`).
631
+ var creator = active_block;
632
+ if (creator !== null) {
633
+ var created = created_deriveds.get(creator);
634
+ if (created === undefined) {
635
+ created_deriveds.set(creator, [d]);
636
+ creator.f |= CREATES_DERIVEDS;
637
+ } else {
638
+ created.push(d);
639
+ }
640
+ }
572
641
  if (hydrating && hash !== undefined) {
573
642
  track_hash_reference.set(hash, d);
574
643
  }
575
644
  return d;
576
645
  }
577
646
 
647
+ /**
648
+ * The deriveds each block's latest run created, keyed by the block. A side
649
+ * table rather than a block field: most blocks never create a derived, and
650
+ * block creation is the hot path, so blocks keep their shape. A block with an
651
+ * entry carries `CREATES_DERIVEDS` in its flags, so a rerun or destroy of any
652
+ * other block never looks here.
653
+ * @type {Map<Block, Derived[]>}
654
+ */
655
+ var created_deriveds = new Map();
656
+
657
+ /**
658
+ * Removes and returns the deriveds `block`'s latest run created, or null.
659
+ * @param {Block} block
660
+ * @returns {Derived[] | null}
661
+ */
662
+ export function take_created_deriveds(block) {
663
+ var deriveds = created_deriveds.get(block);
664
+ block.f &= ~CREATES_DERIVEDS;
665
+ if (deriveds === undefined) {
666
+ return null;
667
+ }
668
+ created_deriveds.delete(block);
669
+ return deriveds;
670
+ }
671
+
672
+ /**
673
+ * Releases the deriveds a block run created, once that run's readers are
674
+ * gone: the block reran (its previous run's readers were destroyed by the
675
+ * rerun) or was destroyed. A derived that a live reader still holds (the
676
+ * branch is still showing, or it was handed to a longer-lived scope) is
677
+ * kept. Walked newest first, so a derived read by a later one of the same
678
+ * run sees that reader unlinked before it is examined; the survivors are
679
+ * packed into the tail of the same array as they are met, which leaves them
680
+ * in creation order for the next pass, and slid to the front at the end.
681
+ * @param {Derived[]} deriveds in creation order; reused for the result
682
+ * @returns {Derived[] | null} the deriveds kept, in creation order, if any
683
+ */
684
+ export function release_deriveds(deriveds) {
685
+ var length = deriveds.length;
686
+ var kept = length;
687
+ for (var i = length - 1; i >= 0; i--) {
688
+ var derived = deriveds[i];
689
+ var alive = false;
690
+ for (var sub = derived.sb; sub !== null; sub = sub.sn) {
691
+ if ((sub.r.f & DESTROYED) === 0) {
692
+ alive = true;
693
+ break;
694
+ }
695
+ }
696
+ if (alive) {
697
+ // `kept` never drops below `i`, so this only overwrites visited slots.
698
+ deriveds[--kept] = derived;
699
+ } else {
700
+ finish_dependencies(derived, null);
701
+ destroy_computed_children(derived);
702
+ }
703
+ }
704
+ if (kept === length) {
705
+ return null;
706
+ }
707
+ if (kept !== 0) {
708
+ deriveds.copyWithin(0, kept);
709
+ deriveds.length = length - kept;
710
+ }
711
+ return deriveds;
712
+ }
713
+
714
+ /**
715
+ * Records deriveds under `block` again after a rerun kept them, ahead of
716
+ * whatever the new run created: the kept ones are older, and the release
717
+ * order (newest first) relies on the list being in creation order.
718
+ * @param {Block} block
719
+ * @param {Derived[] | null} kept
720
+ */
721
+ function keep_deriveds(block, kept) {
722
+ if (kept === null) {
723
+ return;
724
+ }
725
+ var created = created_deriveds.get(block);
726
+ if (created !== undefined) {
727
+ for (var i = 0; i < created.length; i++) {
728
+ kept.push(created[i]);
729
+ }
730
+ }
731
+ created_deriveds.set(block, kept);
732
+ block.f |= CREATES_DERIVEDS;
733
+ }
734
+
578
735
  /**
579
736
  * @param {any} v
580
737
  * @param {Block} b
581
738
  * @param {string} [hash]
582
739
  * @param {(value: any) => any} [get]
583
- * @param {(next: any, prev: any) => any} [set]
740
+ * @param {((next: any, prev: any) => any) | true} [set]
584
741
  * @returns {Tracked | Derived}
585
742
  */
586
743
  export function track(v, b, hash, get, set) {
@@ -634,7 +791,8 @@ export function track_async(fn, b, hash) {
634
791
 
635
792
  if (envelope.ok) {
636
793
  had_hydration_data = true;
637
- hydration_value = devalue.parse(envelope.payload);
794
+ hydration_value =
795
+ envelope.payload === undefined ? envelope.value : revive(envelope.payload);
638
796
  hydration_deps = envelope.deps;
639
797
  } else {
640
798
  // trigger the catch block
@@ -810,7 +968,7 @@ export function track_async(fn, b, hash) {
810
968
  // Route error to catch boundary
811
969
  var boundary_with_catch = get_boundary_with_catch(call_site_block);
812
970
  if (boundary_with_catch !== null) {
813
- boundary_with_catch.s.c(error);
971
+ handle_boundary_error(boundary_with_catch, error);
814
972
  }
815
973
 
816
974
  if (request_id > 0 && boundary !== null) {
@@ -886,6 +1044,8 @@ function create_dependency(tracked) {
886
1044
  return existing;
887
1045
  }
888
1046
 
1047
+ // link_subscriber, inline: the first read of every block run lands here.
1048
+ var head = tracked.sb;
889
1049
  /** @type {Dependency} */
890
1050
  var dependency = {
891
1051
  c: tracked.c,
@@ -893,9 +1053,12 @@ function create_dependency(tracked) {
893
1053
  n: null,
894
1054
  r: reaction,
895
1055
  sp: null,
896
- sn: null,
1056
+ sn: head,
897
1057
  };
898
- link_subscriber(dependency, tracked);
1058
+ if (head !== null) {
1059
+ head.sp = dependency;
1060
+ }
1061
+ tracked.sb = dependency;
899
1062
  return dependency;
900
1063
  }
901
1064
 
@@ -930,8 +1093,7 @@ export function unlink_subscriber(dependency) {
930
1093
  }
931
1094
  tracked.sb = next;
932
1095
  if (next === null && (tracked.f & SELECTOR) !== 0) {
933
- // Last subscriber gone: release the selector's per-key entry.
934
- /** @type {SelectorAccessors} */ (tracked.a).m.delete(tracked.h);
1096
+ release_selector_key(/** @type {Tracked} */ (tracked));
935
1097
  }
936
1098
  }
937
1099
  if (next !== null) {
@@ -941,9 +1103,43 @@ export function unlink_subscriber(dependency) {
941
1103
  }
942
1104
 
943
1105
  /**
944
- * @typedef {{ get: undefined; set: undefined; m: Map<any, Tracked> }} SelectorAccessors
1106
+ * `n` counts released keys still in the map.
1107
+ * @typedef {{ get: undefined; set: undefined; m: Map<any, Tracked>; n: number }} SelectorAccessors
945
1108
  */
946
1109
 
1110
+ /** Released selector keys kept for reuse before the map is swept. */
1111
+ var SELECTOR_SWEEP_THRESHOLD = 1024;
1112
+
1113
+ /**
1114
+ * The last subscriber of a selector key left. The entry stays in the map so
1115
+ * a key that comes back (a list re-rendering the same items) reuses it
1116
+ * instead of allocating and inserting again; once enough released keys pile
1117
+ * up they are swept out, so the map stays bounded.
1118
+ * @param {Tracked} tracked
1119
+ */
1120
+ function release_selector_key(tracked) {
1121
+ var accessors = /** @type {SelectorAccessors} */ (tracked.a);
1122
+ tracked.f |= RELEASED;
1123
+ if (++accessors.n > SELECTOR_SWEEP_THRESHOLD) {
1124
+ var m = accessors.m;
1125
+ for (var [key, t] of m) {
1126
+ if ((t.f & RELEASED) !== 0) {
1127
+ m.delete(key);
1128
+ }
1129
+ }
1130
+ accessors.n = 0;
1131
+ }
1132
+ }
1133
+
1134
+ /**
1135
+ * A key found in the map again after its subscribers had all left.
1136
+ * @param {Tracked} tracked
1137
+ */
1138
+ export function revive_selector_key(tracked) {
1139
+ tracked.f ^= RELEASED;
1140
+ /** @type {SelectorAccessors} */ (tracked.a).n--;
1141
+ }
1142
+
947
1143
  /**
948
1144
  * A tracked match flag for one selector key. The selector's shared accessor
949
1145
  * object carries the map and the key rides in the hash slot, so the entry can
@@ -1007,13 +1203,14 @@ function mark_subscribers(tracked) {
1007
1203
  var flags = reaction.f;
1008
1204
  if ((flags & DERIVED) !== 0) {
1009
1205
  var derived = /** @type {Derived} */ (reaction);
1206
+ // Marking the derived's own subscribers first prunes its destroyed
1207
+ // readers, so a derived whose owner is gone and that nothing reads any
1208
+ // more is dropped on this write; one still read elsewhere keeps
1209
+ // forwarding notifications.
1210
+ mark_subscribers(derived);
1010
1211
  var derived_owner = derived.b;
1011
- // A derived whose owner is gone and that nothing reads any more is
1012
- // pruned; one still read elsewhere keeps forwarding notifications.
1013
1212
  if (derived_owner !== null && (derived_owner.f & DESTROYED) !== 0 && derived.sb === null) {
1014
1213
  unlink_subscriber(dependency);
1015
- } else {
1016
- mark_subscribers(derived);
1017
1214
  }
1018
1215
  } else if ((flags & DESTROYED) !== 0) {
1019
1216
  unlink_subscriber(dependency);
@@ -1103,11 +1300,55 @@ function by_block_id(a, b) {
1103
1300
  */
1104
1301
  function flush_queue(pending) {
1105
1302
  var blocks = pending.blocks;
1303
+ var length = blocks.length;
1304
+
1305
+ if (length > 0) {
1306
+ if (!pending.sorted) {
1307
+ blocks.sort(by_block_id);
1308
+ }
1309
+
1310
+ var has_effects = false;
1311
+
1312
+ for (var i = 0; i < length; i++) {
1313
+ var block = blocks[i];
1314
+ var flags = block.f;
1315
+ block.f = flags & ~SCHEDULED;
1316
+ if ((flags & (PRE_EFFECT_BLOCK | EFFECT_BLOCK)) !== 0) {
1317
+ has_effects = true;
1318
+ }
1319
+ }
1320
+
1321
+ // New schedules land in the queue that replaced `pending`, so the array
1322
+ // can be run in place. Effects need the three-phase ordering; a queue of
1323
+ // only render blocks (the common flush) runs straight through.
1324
+ if (has_effects) {
1325
+ run_phases(blocks, length);
1326
+ } else {
1327
+ run_phase(blocks);
1328
+ }
1106
1329
 
1107
- if (!pending.sorted) {
1108
- blocks.sort(by_block_id);
1330
+ blocks.length = 0;
1331
+ pending.sorted = true;
1332
+ pending.last = 0;
1109
1333
  }
1110
1334
 
1335
+ if (queued_post_block_flush.length > 0) {
1336
+ var callbacks = queued_post_block_flush;
1337
+ queued_post_block_flush = [];
1338
+ for (var j = 0; j < callbacks.length; j++) {
1339
+ callbacks[j]();
1340
+ }
1341
+ }
1342
+ }
1343
+
1344
+ /**
1345
+ * Runs a queue that contains effects in three phases: pre-effects, render
1346
+ * blocks, then effects. Kept out of {@link flush_queue} so a render-only flush
1347
+ * never compiles it.
1348
+ * @param {Block[]} blocks
1349
+ * @param {number} length
1350
+ */
1351
+ function run_phases(blocks, length) {
1111
1352
  /** @type {Block[]} */
1112
1353
  var pre_effects = [];
1113
1354
  /** @type {Block[]} */
@@ -1115,10 +1356,9 @@ function flush_queue(pending) {
1115
1356
  /** @type {Block[]} */
1116
1357
  var effects = [];
1117
1358
 
1118
- for (var i = 0; i < blocks.length; i++) {
1359
+ for (var i = 0; i < length; i++) {
1119
1360
  var block = blocks[i];
1120
1361
  var flags = block.f;
1121
- block.f = flags & ~SCHEDULED;
1122
1362
 
1123
1363
  // A paused block is re-checked when it resumes; a destroyed one is gone.
1124
1364
  if ((flags & (PAUSED | DESTROYED)) !== 0) {
@@ -1133,21 +1373,9 @@ function flush_queue(pending) {
1133
1373
  }
1134
1374
  }
1135
1375
 
1136
- blocks.length = 0;
1137
- pending.sorted = true;
1138
- pending.last = 0;
1139
-
1140
1376
  run_phase(pre_effects);
1141
1377
  run_phase(other_blocks);
1142
1378
  run_phase(effects);
1143
-
1144
- if (queued_post_block_flush.length > 0) {
1145
- var callbacks = queued_post_block_flush;
1146
- queued_post_block_flush = [];
1147
- for (var j = 0; j < callbacks.length; j++) {
1148
- callbacks[j]();
1149
- }
1150
- }
1151
1379
  }
1152
1380
 
1153
1381
  /**
@@ -1156,10 +1384,12 @@ function flush_queue(pending) {
1156
1384
  function run_phase(blocks) {
1157
1385
  for (var i = 0; i < blocks.length; i++) {
1158
1386
  var block = blocks[i];
1387
+ var flags = block.f;
1159
1388
 
1160
1389
  try {
1161
- if ((block.f & (PAUSED | DESTROYED)) === 0 && is_block_dirty(block)) {
1162
- run_block(block);
1390
+ if ((flags & (PAUSED | DESTROYED)) === 0 && is_block_dirty(block)) {
1391
+ // A block that has never run has no children or teardown to clear.
1392
+ run_block(block, (flags & BLOCK_HAS_RUN) === 0);
1163
1393
  }
1164
1394
  } catch (error) {
1165
1395
  handle_error(error, block);
@@ -1196,12 +1426,19 @@ function flush_microtasks() {
1196
1426
  }
1197
1427
  var pending = queue;
1198
1428
  queue = create_queue();
1199
- flush_queue(pending);
1429
+ flush_depth++;
1430
+ try {
1431
+ flush_queue(pending);
1432
+ } finally {
1433
+ flush_depth--;
1434
+ }
1200
1435
 
1201
1436
  if (!is_micro_task_queued) {
1202
1437
  flush_count = 0;
1203
1438
  }
1204
- old_values.clear();
1439
+ if (flush_depth === 0) {
1440
+ release_old_values();
1441
+ }
1205
1442
  }
1206
1443
 
1207
1444
  /**
@@ -1313,75 +1550,26 @@ export function get(tracked) {
1313
1550
  : get_tracked(/** @type {Tracked} */ (tracked));
1314
1551
  }
1315
1552
 
1316
- /**
1317
- * @param {any} lazy
1318
- * @param {number} [index]
1319
- * @returns {any}
1320
- */
1321
- export function lazy_array_get(lazy, index = 0) {
1322
- if (is_array(lazy)) {
1323
- return lazy[index];
1324
- }
1325
- var flags = lazy.f;
1326
- if (flags === TRACKED) {
1327
- return index === 0
1328
- ? get_tracked(/** @type {Tracked} */ (lazy))
1329
- : index === 1
1330
- ? lazy
1331
- : undefined;
1332
- }
1333
- if (flags === DERIVED) {
1334
- return index === 0
1335
- ? get_derived(/** @type {Derived} */ (lazy))
1336
- : index === 1
1337
- ? lazy
1338
- : undefined;
1339
- }
1340
- return iterable_array_from(lazy, index)[0];
1341
- }
1342
-
1343
- /**
1344
- * @param {any} lazy
1345
- * @param {number} [index]
1346
- * @returns {any[]}
1347
- */
1348
- export function lazy_array_rest(lazy, index = 0) {
1349
- if (is_array(lazy)) {
1350
- return lazy.slice(index);
1351
- }
1352
- var flags = lazy.f;
1353
- if (flags === TRACKED) {
1354
- return index === 0
1355
- ? [get_tracked(/** @type {Tracked} */ (lazy)), lazy]
1356
- : index === 1
1357
- ? [lazy]
1358
- : [];
1359
- }
1360
- if (flags === DERIVED) {
1361
- return index === 0
1362
- ? [get_derived(/** @type {Derived} */ (lazy)), lazy]
1363
- : index === 1
1364
- ? [lazy]
1365
- : [];
1366
- }
1367
- return iterable_array_from(lazy, index);
1368
- }
1369
-
1370
1553
  /**
1371
1554
  * @param {Tracked} tracked
1372
1555
  */
1373
1556
  export function get_tracked(tracked) {
1374
1557
  var value = tracked.__v;
1375
1558
  if (tracking) {
1376
- register_dependency(tracked);
1559
+ // register_dependency, inline for the first read of a run.
1560
+ if (active_dependency === null) {
1561
+ active_dependency = create_dependency(tracked);
1562
+ } else {
1563
+ register_dependency(tracked);
1564
+ }
1377
1565
  }
1378
1566
 
1379
1567
  if (value === SUSPENSE_PENDING || value === SUSPENSE_REJECTED) {
1380
1568
  throw ASYNC_DERIVED_READ_THROWN;
1381
1569
  }
1382
1570
 
1383
- if (teardown && old_values.has(tracked)) {
1384
- value = old_values.get(tracked);
1571
+ if (teardown && tracked.o !== UNINITIALIZED) {
1572
+ value = tracked.o;
1385
1573
  }
1386
1574
  var get = tracked.a.get;
1387
1575
  if (get !== undefined) {
@@ -1390,57 +1578,6 @@ export function get_tracked(tracked) {
1390
1578
  return value;
1391
1579
  }
1392
1580
 
1393
- /**
1394
- * @param {any} lazy
1395
- * @param {any} value
1396
- * @param {number} [index]
1397
- * @returns {void}
1398
- */
1399
- export function lazy_array_set(lazy, value, index = 0) {
1400
- if (is_array(lazy)) {
1401
- lazy[index] = value;
1402
- return;
1403
- }
1404
- var flags = lazy.f;
1405
- if (flags === TRACKED || flags === DERIVED) {
1406
- if (index === 0) {
1407
- set(/** @type {Derived | Tracked} */ (lazy), value);
1408
- return;
1409
- }
1410
- if (index === 1) {
1411
- throw_tracked_index_reference_error();
1412
- }
1413
- return;
1414
- }
1415
- lazy[index] = value;
1416
- }
1417
-
1418
- /**
1419
- * @param {any} lazy
1420
- * @param {number} [index]
1421
- * @param {number} [d]
1422
- * @returns {number}
1423
- */
1424
- export function lazy_array_update(lazy, index = 0, d = 1) {
1425
- var value = lazy_array_get(lazy, index);
1426
- var result = d === 1 ? value++ : value--;
1427
- lazy_array_set(lazy, value, index);
1428
- return result;
1429
- }
1430
-
1431
- /**
1432
- * @param {any} lazy
1433
- * @param {number} [index]
1434
- * @param {number} [d]
1435
- * @returns {number}
1436
- */
1437
- export function lazy_array_update_pre(lazy, index = 0, d = 1) {
1438
- var value = lazy_array_get(lazy, index);
1439
- var new_value = d === 1 ? ++value : --value;
1440
- lazy_array_set(lazy, new_value, index);
1441
- return new_value;
1442
- }
1443
-
1444
1581
  /**
1445
1582
  * @param {Derived | Tracked} tracked
1446
1583
  * @param {any} value
@@ -1458,16 +1595,17 @@ export function set(tracked, value) {
1458
1595
  var tracked_block = tracked.b;
1459
1596
 
1460
1597
  if ((tracked_block.f & CONTAINS_TEARDOWN) !== 0) {
1461
- if (teardown) {
1462
- old_values.set(tracked, value);
1463
- } else {
1464
- old_values.set(tracked, old_value);
1598
+ if (tracked.o === UNINITIALIZED) {
1599
+ old_value_holders.push(tracked);
1465
1600
  }
1601
+ tracked.o = teardown ? value : old_value;
1466
1602
  }
1467
1603
 
1468
1604
  let set = tracked.a.set;
1469
- if (set !== undefined) {
1605
+ if (typeof set === 'function') {
1470
1606
  value = untrack(() => set(value, old_value));
1607
+ } else if (DEV && set === undefined && (tracked.f & DERIVED) !== 0) {
1608
+ warn_readonly_derived_write(/** @type {Derived} */ (tracked));
1471
1609
  }
1472
1610
 
1473
1611
  tracked.__v = value;
@@ -1476,6 +1614,23 @@ export function set(tracked, value) {
1476
1614
  }
1477
1615
  }
1478
1616
 
1617
+ /** @type {WeakSet<object>} */
1618
+ var warned_derived = new WeakSet();
1619
+
1620
+ /**
1621
+ * A derived created without a setter is read-only by contract (`Derived<V>`);
1622
+ * a write still lands as a temporary value until the next recompute, so in
1623
+ * development it is reported once per derived.
1624
+ * @param {Derived} derived
1625
+ */
1626
+ function warn_readonly_derived_write(derived) {
1627
+ if (warned_derived.has(derived)) return;
1628
+ warned_derived.add(derived);
1629
+ console.warn(
1630
+ 'Writing to a read-only derived. Create it with a setter, `track(fn, undefined, true)` or `track(fn, get, set)`, when it is meant to be written.',
1631
+ );
1632
+ }
1633
+
1479
1634
  /**
1480
1635
  * @template T
1481
1636
  * @param {() => T} fn
@@ -1503,6 +1658,7 @@ export function flush_sync(fn) {
1503
1658
  var previous_scheduler_mode = scheduler_mode;
1504
1659
  var previous_queue = queue;
1505
1660
 
1661
+ flush_depth++;
1506
1662
  try {
1507
1663
  scheduler_mode = FLUSH_SYNC;
1508
1664
  queue = create_queue();
@@ -1513,130 +1669,30 @@ export function flush_sync(fn) {
1513
1669
 
1514
1670
  var result = fn?.();
1515
1671
 
1516
- if (queue.blocks.length > 0) {
1517
- flush_sync();
1672
+ // Each pass runs the blocks scheduled by the previous one.
1673
+ while (queue.blocks.length > 0) {
1674
+ var pending = queue;
1675
+ queue = create_queue();
1676
+ flush_queue(pending);
1518
1677
  }
1519
1678
 
1520
1679
  flush_count = 0;
1521
1680
 
1681
+ // Old values only matter to teardowns run by the flush in progress; a
1682
+ // sync flush nested in another flush (an effect calling `flushSync`)
1683
+ // leaves them for the outer flush's remaining teardowns.
1684
+ if (flush_depth === 1) {
1685
+ release_old_values();
1686
+ }
1687
+
1522
1688
  return /** @type {T} */ (result);
1523
1689
  } finally {
1690
+ flush_depth--;
1524
1691
  scheduler_mode = previous_scheduler_mode;
1525
1692
  queue = previous_queue;
1526
1693
  }
1527
1694
  }
1528
1695
 
1529
- /**
1530
- * @param {() => Object} fn
1531
- * @returns {Object}
1532
- */
1533
- export function spread_props(fn) {
1534
- return proxy_props(fn);
1535
- }
1536
-
1537
- /**
1538
- * @param {() => Object} fn
1539
- * @returns {Object}
1540
- */
1541
- export function proxy_props(fn) {
1542
- const memo = derived(fn, /** @type {Block} */ (active_block));
1543
-
1544
- return new Proxy(
1545
- {},
1546
- {
1547
- get(_, property) {
1548
- /** @type {Record<string | symbol, any> | Record<string | symbol, any>[]} */
1549
- var obj = get_derived(memo);
1550
-
1551
- // Handle array of objects/spreads (for multiple props)
1552
- if (is_array(obj)) {
1553
- // Search in reverse order (right-to-left) since later props override earlier ones
1554
- /** @type {Record<string | symbol, any>} */
1555
- var item;
1556
- for (var i = obj.length - 1; i >= 0; i--) {
1557
- item = obj[i];
1558
- if (property in item) {
1559
- return item[property];
1560
- }
1561
- }
1562
- return undefined;
1563
- }
1564
-
1565
- // Single object case
1566
- return obj[property];
1567
- },
1568
- has(_, property) {
1569
- if (property === TRACKED_OBJECT) {
1570
- return true;
1571
- }
1572
- /** @type {Record<string | symbol, any> | Record<string | symbol, any>[]} */
1573
- var obj = get_derived(memo);
1574
-
1575
- // Handle array of objects/spreads
1576
- if (is_array(obj)) {
1577
- for (var i = obj.length - 1; i >= 0; i--) {
1578
- if (property in obj[i]) {
1579
- return true;
1580
- }
1581
- }
1582
- return false;
1583
- }
1584
-
1585
- return property in obj;
1586
- },
1587
- getOwnPropertyDescriptor(_, key) {
1588
- /** @type {Record<string | symbol, any> | Record<string | symbol, any>[]} */
1589
- var obj = get_derived(memo);
1590
-
1591
- // Handle array of objects/spreads
1592
- if (is_array(obj)) {
1593
- /** @type {Record<string | symbol, any>} */
1594
- var item;
1595
- for (var i = obj.length - 1; i >= 0; i--) {
1596
- item = obj[i];
1597
- if (key in item) {
1598
- return get_descriptor(item, key);
1599
- }
1600
- }
1601
- return undefined;
1602
- }
1603
-
1604
- if (key in obj) {
1605
- return get_descriptor(obj, key);
1606
- }
1607
- },
1608
- ownKeys() {
1609
- /** @type {Record<string | symbol, any> | Record<string | symbol, any>[]} */
1610
- var obj = get_derived(memo);
1611
- /** @type {Record<string | symbol, 1>} */
1612
- var done = {};
1613
- /** @type {(string | symbol)[]} */
1614
- var keys = [];
1615
-
1616
- // Handle array of objects/spreads
1617
- if (is_array(obj)) {
1618
- // Collect all keys from all objects, order doesn't matter
1619
- /** @type {Record<string | symbol, any>} */
1620
- var item;
1621
- for (var i = 0; i < obj.length; i++) {
1622
- item = obj[i];
1623
- for (const key of Reflect.ownKeys(item)) {
1624
- if (done[key]) {
1625
- continue;
1626
- }
1627
- done[key] = 1;
1628
- keys.push(key);
1629
- }
1630
- }
1631
- return keys;
1632
- }
1633
-
1634
- return Reflect.ownKeys(obj);
1635
- },
1636
- },
1637
- );
1638
- }
1639
-
1640
1696
  /**
1641
1697
  * @template T
1642
1698
  * @param {() => T} fn
@@ -1806,7 +1862,7 @@ export function safe_scope(err = 'Cannot access outside of a component context')
1806
1862
  export function create_component_ctx() {
1807
1863
  return {
1808
1864
  b: active_block,
1809
- c: null,
1865
+ c: active_component === null ? null : active_component.c,
1810
1866
  e: null,
1811
1867
  m: false,
1812
1868
  p: active_component,
@@ -1817,8 +1873,7 @@ export function create_component_ctx() {
1817
1873
  * @returns {void}
1818
1874
  */
1819
1875
  export function push_component() {
1820
- var component = create_component_ctx();
1821
- active_component = component;
1876
+ active_component = create_component_ctx();
1822
1877
  }
1823
1878
 
1824
1879
  /**
@@ -1827,27 +1882,168 @@ export function push_component() {
1827
1882
  export function pop_component() {
1828
1883
  var component = /** @type {Component} */ (active_component);
1829
1884
  component.m = true;
1830
- var effects = component.e;
1831
- if (effects !== null) {
1832
- var length = effects.length;
1833
- for (var i = 0; i < length; i++) {
1834
- var { b: block, fn, r: reaction } = effects[i];
1835
- var previous_block = active_block;
1836
- var previous_reaction = active_reaction;
1885
+ if (component.e !== null) {
1886
+ create_deferred_effects(component.e);
1887
+ }
1888
+ active_component = component.p;
1889
+ }
1837
1890
 
1838
- try {
1839
- active_block = block;
1840
- active_reaction = reaction;
1841
- effect(fn);
1842
- } finally {
1843
- active_block = previous_block;
1844
- active_reaction = previous_reaction;
1845
- }
1846
- }
1891
+ /**
1892
+ * Renders a component: `fn(props)` runs under a fresh component context (the
1893
+ * owner of the component's context values and deferred effects), and the
1894
+ * element it returns renders before `anchor`.
1895
+ * @param {Function} fn
1896
+ * @param {Node | AppendIntoAnchor} anchor
1897
+ * @param {Record<string, any>} props
1898
+ * @param {Block | null} [block=active_block]
1899
+ * @returns {void}
1900
+ */
1901
+ export function render_component(fn, anchor, props, block = active_block) {
1902
+ // An optional component (an undefined prop, an import that resolved to
1903
+ // nothing) renders nothing; any other non-function is a programming error.
1904
+ if (fn == null) {
1905
+ return;
1906
+ }
1907
+ if (typeof fn !== 'function') {
1908
+ throw_invalid_component_type(fn);
1909
+ }
1910
+
1911
+ // push_component, inline: one component per call is the common case.
1912
+ var parent = active_component;
1913
+ /** @type {Component} */
1914
+ var component = (active_component = {
1915
+ b: active_block,
1916
+ c: parent === null ? null : parent.c,
1917
+ e: null,
1918
+ m: false,
1919
+ p: parent,
1920
+ });
1921
+
1922
+ // A module-level component carries its render function under a symbol
1923
+ // (see the compiler): calling it directly skips the element the component
1924
+ // would return only to be rendered here.
1925
+ var render = /** @type {any} */ (fn)[RENDER_ENTRY];
1926
+ if (render !== undefined) {
1927
+ render(anchor, block, props);
1928
+ } else {
1929
+ render_value(fn(props), /** @type {ChildNode} */ (anchor), block);
1930
+ }
1931
+
1932
+ // pop_component, inline.
1933
+ component.m = true;
1934
+ if (component.e !== null) {
1935
+ create_deferred_effects(component.e);
1847
1936
  }
1848
1937
  active_component = component.p;
1849
1938
  }
1850
1939
 
1940
+ /**
1941
+ * A reaction that stands in for a block whose creation is being decided (see
1942
+ * `probe_if`). Flagged destroyed so a write during the probe prunes
1943
+ * its subscription instead of scheduling it.
1944
+ * @type {any}
1945
+ */
1946
+ var probe_reaction = { f: DESTROYED, d: null, blocks: null };
1947
+
1948
+ /**
1949
+ * Evaluates an if condition once, tracked, against a scratch reaction that is
1950
+ * never scheduled. When the evaluation recorded no dependency the condition
1951
+ * can never re-run (a block with no dependencies is never scheduled), so the
1952
+ * selected branch is rendered straight away, untracked, and true is returned:
1953
+ * the if needs no block. Otherwise the recorded links are unlinked again and
1954
+ * false is returned: the caller creates the block, whose own first run
1955
+ * evaluates the condition and subscribes it. The links are never handed to
1956
+ * the block, because a write during the block's first run (a child's setup,
1957
+ * a nested if) would prune links that still point at this destroyed reaction
1958
+ * before the block could take them over. A throwing condition (a pending
1959
+ * async read) unlinks what it recorded and rethrows.
1960
+ * @param {(x: any) => any} fn
1961
+ * @param {any} x
1962
+ * @param {Node | import('#client').AppendIntoAnchor} node
1963
+ * @returns {boolean} whether the branch was rendered without a block
1964
+ */
1965
+ export function probe_if(fn, x, node) {
1966
+ var previous_reaction = active_reaction;
1967
+ var previous_tracking = tracking;
1968
+ var previous_dependency = active_dependency;
1969
+ active_reaction = probe_reaction;
1970
+ tracking = true;
1971
+ active_dependency = null;
1972
+ /** @type {Dependency | null} */
1973
+ var recorded;
1974
+ /** @type {any} */
1975
+ var branch;
1976
+ try {
1977
+ branch = fn(x);
1978
+ recorded = active_dependency;
1979
+ } catch (error) {
1980
+ unlink_dependencies(active_dependency);
1981
+ active_reaction = previous_reaction;
1982
+ tracking = previous_tracking;
1983
+ active_dependency = previous_dependency;
1984
+ throw error;
1985
+ }
1986
+ active_reaction = previous_reaction;
1987
+ active_dependency = previous_dependency;
1988
+ if (recorded !== null) {
1989
+ unlink_dependencies(recorded);
1990
+ tracking = previous_tracking;
1991
+ return false;
1992
+ }
1993
+ // The branch renders untracked, like a branch block would; `tracking` is
1994
+ // restored by the enclosing `run_block` if the branch throws.
1995
+ if (branch !== undefined) {
1996
+ tracking = false;
1997
+ branch(node, x);
1998
+ }
1999
+ tracking = previous_tracking;
2000
+ return true;
2001
+ }
2002
+
2003
+ /**
2004
+ * @param {Dependency | null} dependency the head of a recorded chain
2005
+ */
2006
+ function unlink_dependencies(dependency) {
2007
+ while (dependency !== null) {
2008
+ unlink_subscriber(dependency);
2009
+ dependency = dependency.n;
2010
+ }
2011
+ }
2012
+
2013
+ /**
2014
+ * Calls `fn(arg)` with tracking off, so reads inside it subscribe nothing.
2015
+ * `tracking` is restored by the enclosing `run_block` if `fn` throws.
2016
+ * @param {(arg: any, context: any) => void} fn
2017
+ * @param {any} arg
2018
+ * @param {any} [context]
2019
+ */
2020
+ export function run_untracked(fn, arg, context) {
2021
+ var previous_tracking = tracking;
2022
+ tracking = false;
2023
+ fn(arg, context);
2024
+ tracking = previous_tracking;
2025
+ }
2026
+
2027
+ /**
2028
+ * Creates the effects a component registered while rendering, each under the
2029
+ * block and reaction that were active at its `effect()` call.
2030
+ * @param {any[]} effects flat triples: fn, block, reaction (see `user_effect`)
2031
+ */
2032
+ function create_deferred_effects(effects) {
2033
+ // Creating an effect block only links and schedules it, so nothing here
2034
+ // can throw between saving and restoring the active block.
2035
+ var previous_block = active_block;
2036
+ var previous_reaction = active_reaction;
2037
+ var length = effects.length;
2038
+ for (var i = 0; i < length; i += 3) {
2039
+ active_block = /** @type {Block} */ (effects[i + 1]);
2040
+ active_reaction = /** @type {Block | Derived | null} */ (effects[i + 2]);
2041
+ effect(/** @type {Function} */ (effects[i]));
2042
+ }
2043
+ active_block = previous_block;
2044
+ active_reaction = previous_reaction;
2045
+ }
2046
+
1851
2047
  /**
1852
2048
  * @template T
1853
2049
  * @param {() => T} fn
@@ -1879,40 +2075,3 @@ export function ref_prop() {
1879
2075
  export function create_ref_prop(get_ref_value, set_ref_value) {
1880
2076
  return create_core_ref_prop(() => untrack(get_ref_value), set_ref_value);
1881
2077
  }
1882
-
1883
- /**
1884
- * @template T
1885
- * @param {T | undefined} value
1886
- * @param {T} fallback
1887
- * @returns {T}
1888
- */
1889
- export function fallback(value, fallback) {
1890
- return value === undefined ? fallback : value;
1891
- }
1892
-
1893
- /**
1894
- * @param {Record<string | symbol, unknown>} obj
1895
- * @param {string[]} exclude_keys
1896
- * @returns {Record<string | symbol, unknown>}
1897
- */
1898
- export function exclude_from_object(obj, exclude_keys) {
1899
- var keys = object_keys(obj);
1900
- /** @type {Record<string | symbol, unknown>} */
1901
- var new_obj = {};
1902
-
1903
- for (const key of keys) {
1904
- if (!exclude_keys.includes(key)) {
1905
- new_obj[key] = obj[key];
1906
- }
1907
- }
1908
-
1909
- for (const symbol of get_own_property_symbols(obj)) {
1910
- var ref_fn = obj[symbol];
1911
-
1912
- if (symbol.description === REF_PROP) {
1913
- new_obj[symbol] = ref_fn;
1914
- }
1915
- }
1916
-
1917
- return new_obj;
1918
- }