ripple 0.4.2 → 0.4.4

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 (98) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/package.json +10 -4
  3. package/src/constants.js +6 -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 +439 -0
  9. package/src/runtime/internal/client/blocks.js +20 -20
  10. package/src/runtime/internal/client/component.js +2 -5
  11. package/src/runtime/internal/client/composite.js +295 -95
  12. package/src/runtime/internal/client/constants.js +14 -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 +25 -120
  18. package/src/runtime/internal/client/for.js +113 -262
  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 -81
  24. package/src/runtime/internal/client/if.js +69 -10
  25. package/src/runtime/internal/client/index.js +18 -10
  26. package/src/runtime/internal/client/operations.js +11 -7
  27. package/src/runtime/internal/client/portal.js +3 -2
  28. package/src/runtime/internal/client/render.js +42 -325
  29. package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
  30. package/src/runtime/internal/client/runtime.js +157 -371
  31. package/src/runtime/internal/client/template-ns.js +88 -0
  32. package/src/runtime/internal/client/template.js +118 -139
  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 +31 -29
  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 +2 -2
  43. package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
  44. package/tests/client/composite/composite.dynamic-components.test.tsrx +40 -0
  45. package/tests/client/composite/composite.props.test.tsrx +10 -10
  46. package/tests/client/css/scoped-styles.test.tsrx +3 -1
  47. package/tests/client/derived-release.test.tsrx +3 -3
  48. package/tests/client/for-item.test.tsrx +39 -0
  49. package/tests/client/for-selector.test.tsrx +95 -0
  50. package/tests/client/svg.test.tsrx +273 -1
  51. package/tests/hydration/compiled/client/basic.js +88 -87
  52. package/tests/hydration/compiled/client/composite.js +15 -6
  53. package/tests/hydration/compiled/client/events.js +40 -48
  54. package/tests/hydration/compiled/client/for.js +191 -199
  55. package/tests/hydration/compiled/client/fragment-cursor.js +633 -334
  56. package/tests/hydration/compiled/client/head.js +22 -22
  57. package/tests/hydration/compiled/client/hmr.js +14 -5
  58. package/tests/hydration/compiled/client/html-in-template.js +5 -5
  59. package/tests/hydration/compiled/client/html.js +195 -177
  60. package/tests/hydration/compiled/client/if-children.js +136 -63
  61. package/tests/hydration/compiled/client/if-fragment-controlflow.js +63 -43
  62. package/tests/hydration/compiled/client/if.js +78 -30
  63. package/tests/hydration/compiled/client/mixed-control-flow.js +90 -72
  64. package/tests/hydration/compiled/client/nested-control-flow.js +266 -307
  65. package/tests/hydration/compiled/client/portal.js +20 -11
  66. package/tests/hydration/compiled/client/reactivity.js +29 -29
  67. package/tests/hydration/compiled/client/return.js +2 -2
  68. package/tests/hydration/compiled/client/streaming.js +33 -35
  69. package/tests/hydration/compiled/client/switch.js +25 -25
  70. package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
  71. package/tests/hydration/compiled/client/try.js +15 -15
  72. package/tests/hydration/compiled/client/typed-text.js +8 -7
  73. package/tests/hydration/compiled/server/basic.js +6 -6
  74. package/tests/hydration/compiled/server/events.js +8 -8
  75. package/tests/hydration/compiled/server/for.js +22 -22
  76. package/tests/hydration/compiled/server/fragment-cursor.js +188 -52
  77. package/tests/hydration/compiled/server/head.js +6 -6
  78. package/tests/hydration/compiled/server/hmr.js +1 -1
  79. package/tests/hydration/compiled/server/html.js +1 -1
  80. package/tests/hydration/compiled/server/if-children.js +9 -9
  81. package/tests/hydration/compiled/server/if.js +5 -5
  82. package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
  83. package/tests/hydration/compiled/server/portal.js +1 -1
  84. package/tests/hydration/compiled/server/reactivity.js +9 -9
  85. package/tests/hydration/compiled/server/streaming.js +9 -9
  86. package/tests/hydration/compiled/server/switch.js +4 -4
  87. package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
  88. package/tests/hydration/compiled/server/try.js +4 -4
  89. package/tests/hydration/compiled/server/typed-text.js +1 -1
  90. package/tests/hydration/components/fragment-cursor.tsrx +60 -0
  91. package/tests/hydration/components/track-async-serialization.tsrx +2 -2
  92. package/tests/hydration/fragment-cursor.test.js +34 -1
  93. package/tests/hydration/track-async-serialization.test.js +1 -1
  94. package/tests/server/basic.components.test.tsrx +3 -3
  95. package/tests/server/track-async-serialization.test.tsrx +6 -6
  96. package/tests/utils/tracked-types.test.js +3 -3
  97. package/types/index.d.ts +8 -8
  98. package/types/server.d.ts +17 -0
@@ -4,13 +4,7 @@
4
4
  /** @typedef {DerivedValue} Derived */
5
5
 
6
6
  import { DEV } from 'esm-env';
7
- import {
8
- destroy_block,
9
- destroy_non_branch_children,
10
- effect,
11
- pause_block,
12
- pre_effect,
13
- } from './blocks.js';
7
+ import { destroy_block, destroy_non_branch_children, effect } from './blocks.js';
14
8
  import {
15
9
  ASYNC_DERIVED_READ_THROWN,
16
10
  BLOCK_HAS_RUN,
@@ -28,30 +22,21 @@ import {
28
22
  UNINITIALIZED,
29
23
  REF_PROP,
30
24
  DEFAULT_NAMESPACE,
31
- TRACKED_UPDATED,
32
25
  SUSPENSE_PENDING,
33
26
  SUSPENSE_REJECTED,
34
- TRY_BLOCK,
35
- DIRECT_CHILD_BLOCK,
36
27
  SCHEDULED,
37
28
  SELECTOR,
38
29
  IF_BLOCK,
30
+ COMPOSITE_BLOCK,
39
31
  ITEM_BLOCK,
40
32
  RELEASED,
41
33
  RENDER_ENTRY,
42
34
  CREATES_DERIVEDS,
35
+ NAMESPACE_BLOCK,
36
+ SVG_BLOCK,
43
37
  } from './constants.js';
44
- import {
45
- begin_boundary_request,
46
- complete_boundary_request,
47
- get_boundary_with_catch,
48
- get_pending_boundary,
49
- handle_boundary_error,
50
- register_boundary_deferred,
51
- register_boundary_paused_block,
52
- replace_boundary_request,
53
- } from './try.js';
54
38
  import { is_ripple_object } from './utils.js';
39
+ import { scope_orphan, set_in_derived, track_orphan, update_depth_exceeded } from './errors.js';
55
40
  import { render_value } from './expression.js';
56
41
  import { throw_invalid_component_type } from './component.js';
57
42
 
@@ -61,10 +46,8 @@ import {
61
46
  is_array,
62
47
  object_keys,
63
48
  } from '@tsrx/core/runtime/language-helpers';
64
- import { get_async_track_result } from '../../../utils/async.js';
65
- import { get_track_async_script_id } from '../../../utils/track-async-serialization.js';
66
- import { revive } from './transport.js';
67
49
  import { hydrating, track_hash_reference } from './hydration.js';
50
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
68
51
  import { create_ref_prop as create_core_ref_prop } from '@tsrx/core/runtime/ref';
69
52
 
70
53
  const FLUSH_MICROTASK = 0;
@@ -130,7 +113,7 @@ let flush_count = 0;
130
113
  /** @type {(() => void)[]} */
131
114
  var queued_post_block_flush = [];
132
115
  /** @type {null | Dependency} */
133
- let active_dependency = null;
116
+ export let active_dependency = null;
134
117
 
135
118
  export let tracking = false;
136
119
  export let teardown = false;
@@ -170,6 +153,28 @@ export function set_tracking(value) {
170
153
  tracking = value;
171
154
  }
172
155
 
156
+ /**
157
+ * @param {boolean} value
158
+ */
159
+ export function set_mutating_allowed(value) {
160
+ is_mutating_allowed = value;
161
+ }
162
+
163
+ /**
164
+ * Handles a block whose run read a pending async value (see
165
+ * `track-async.js`). Installed by the first `trackAsync()`, so an application
166
+ * without one carries none of the boundary request machinery.
167
+ * @type {((block: Block) => void) | null}
168
+ */
169
+ let pending_read = null;
170
+
171
+ /**
172
+ * @param {(block: Block) => void} fn
173
+ */
174
+ export function set_pending_read_handler(fn) {
175
+ pending_read = fn;
176
+ }
177
+
173
178
  /**
174
179
  * @param {Block} block
175
180
  */
@@ -236,7 +241,7 @@ function update_derived(computed) {
236
241
  * @param {Tracked} tracked
237
242
  * @param {any} value
238
243
  */
239
- function update_tracked_value_clock(tracked, value) {
244
+ export function update_tracked_value_clock(tracked, value) {
240
245
  tracked.__v = value;
241
246
  tracked.c = increment_clock();
242
247
  mark_subscribers(tracked);
@@ -306,76 +311,51 @@ function run_derived(computed) {
306
311
  }
307
312
  }
308
313
 
314
+ /**
315
+ * Routes an error thrown by a block into the nearest boundary with a catch
316
+ * branch (see `try.js`). Installed by the first `try_block`, so a bundle whose
317
+ * mount and components create no boundary carries no catch machinery.
318
+ * @type {((error: unknown, block: Block) => BlockWithTryBoundaryAndCatch | null) | null}
319
+ */
320
+ let catch_router = null;
321
+
322
+ /**
323
+ * @param {(error: unknown, block: Block) => BlockWithTryBoundaryAndCatch | null} fn
324
+ */
325
+ export function set_catch_router(fn) {
326
+ catch_router = fn;
327
+ }
328
+
309
329
  /**
310
330
  * @param {unknown} error
311
331
  * @param {Block} block
312
332
  * @returns {BlockWithTryBoundaryAndCatch}
313
333
  */
314
334
  export function handle_error(error, block) {
315
- var boundary_with_catch = get_boundary_with_catch(block);
316
- if (boundary_with_catch !== null) {
317
- handle_boundary_error(boundary_with_catch, error);
318
- return boundary_with_catch;
335
+ var boundary_with_catch = catch_router === null ? null : catch_router(error, block);
336
+ if (boundary_with_catch === null) {
337
+ throw error;
319
338
  }
320
-
321
- throw error;
339
+ return boundary_with_catch;
322
340
  }
323
341
 
324
342
  /**
325
343
  * The error path of {@link run_block}, kept out of that hot function so a
326
344
  * healthy mount never compiles it: routes real errors to the nearest catch
327
- * boundary and pauses a block that read a pending async value under its
328
- * pending boundary.
345
+ * boundary, and a read of a pending async value to the handler `trackAsync`
346
+ * installs (the read is registered as a dependency, so the block re-runs once
347
+ * the value settles).
329
348
  * @param {unknown} error
330
349
  * @param {Block} block
331
350
  */
332
351
  function handle_run_error(error, block) {
333
- var is_component_direct = false;
334
- var is_try_fn_block = false;
335
352
  finish_dependencies(block, active_dependency);
336
- // When a derived read throws ASYNC_DERIVED_READ_THROWN, it means the
337
- // derived is still SUSPENSE_PENDING. The dependency was already registered,
338
- // so we swallow the throw and let the parent continue processing. When
339
- // the derived settles, the block will be dirty and rerun automatically.
340
353
  if (error !== ASYNC_DERIVED_READ_THROWN) {
341
354
  handle_error(error, block);
342
- } else if (
343
- // pending async tracked was read outside allowed blocks
344
- (is_component_direct = active_component?.b === block) ||
345
- (is_try_fn_block =
346
- block.p !== null && (block.p.f & TRY_BLOCK) !== 0 && (block.f & DIRECT_CHILD_BLOCK) !== 0)
347
- ) {
348
- throw new Error(
349
- `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.`,
350
- );
355
+ } else if (pending_read !== null) {
356
+ pending_read(block);
351
357
  } else {
352
- // pending async tracked was read and threw ASYNC_DERIVED_READ_THROWN
353
- var boundary = get_pending_boundary(block);
354
- if (boundary !== null) {
355
- pause_block(block);
356
- register_boundary_paused_block(boundary, block);
357
-
358
- // Register deferred boundary completions for async tracked deps.
359
- // This handles the case where a child boundary reads a tracked value
360
- // whose resolution is managed by a different (parent) boundary.
361
- var dep = block.d;
362
- while (dep !== null) {
363
- var dep_tracked = /** @type {Tracked} */ (dep.t);
364
- if (
365
- (dep_tracked.__v === SUSPENSE_PENDING || dep_tracked.__v === SUSPENSE_REJECTED) &&
366
- (dep_tracked.f & TRACKED) !== 0
367
- ) {
368
- var deferred_req = begin_boundary_request(boundary);
369
- var entry = /** @type {DeferredTrackedEntry} */ ({ b: boundary, r: deferred_req });
370
- if (dep_tracked.d === null) {
371
- dep_tracked.d = [entry];
372
- } else {
373
- dep_tracked.d.push(entry);
374
- }
375
- }
376
- dep = dep.n;
377
- }
378
- }
358
+ throw error;
379
359
  }
380
360
  }
381
361
 
@@ -390,9 +370,10 @@ function handle_run_error(error, block) {
390
370
  * @returns {Derived[] | null}
391
371
  */
392
372
  function prepare_rerun(block) {
393
- // A list, an if, and a list item re-run only their own logic; their child
394
- // blocks (items, the branch's blocks, an item body's nested blocks) stay.
395
- if ((block.f & (FOR_BLOCK | IF_BLOCK | ITEM_BLOCK)) === 0) {
373
+ // A list, an if, a composite and a list item re-run only their own logic;
374
+ // their child blocks (items, the branch's blocks, a dynamic element's
375
+ // children, an item body's nested blocks) stay.
376
+ if ((block.f & (FOR_BLOCK | IF_BLOCK | ITEM_BLOCK | COMPOSITE_BLOCK)) === 0) {
396
377
  destroy_non_branch_children(block);
397
378
  }
398
379
  run_teardown(block);
@@ -416,16 +397,18 @@ function register_teardown(block, teardown) {
416
397
 
417
398
  /**
418
399
  * Runs the body of a branch block that was just created, in place of a first
419
- * `run_block`: a branch renders untracked and never re-runs, so the only
420
- * bookkeeping its first run needs is the globals the body reads and the
421
- * dependencies a nested `item` records on it. Errors are handled as
422
- * `run_block` handles them.
400
+ * `run_block`: a branch renders untracked, so the only bookkeeping its first
401
+ * run needs is the globals the body reads and the dependencies a nested
402
+ * `item` records on it (a list item re-runs through `run_block`, see
403
+ * `run_item`). Errors are handled as `run_block` handles them.
423
404
  * @param {Block} block
424
- * @param {(anchor: any, value: any) => void} fn
405
+ * @param {(anchor: any, value: any, index?: any, key?: any) => void} fn
425
406
  * @param {any} anchor
426
- * @param {any} value
407
+ * @param {any} value a list item's value, or its tracked
408
+ * @param {any} [index] a list item's tracked index (see `create_item`)
409
+ * @param {any} [key] a keyed list item's key
427
410
  */
428
- export function run_branch(block, fn, anchor, value) {
411
+ export function run_branch(block, fn, anchor, value, index, key) {
429
412
  var previous_block = active_block;
430
413
  var previous_reaction = active_reaction;
431
414
  var previous_tracking = tracking;
@@ -438,7 +421,7 @@ export function run_branch(block, fn, anchor, value) {
438
421
  active_component = block.co;
439
422
  tracking = false;
440
423
  active_dependency = null;
441
- fn(anchor, value);
424
+ fn(anchor, value, index, key);
442
425
  if (active_dependency !== null) {
443
426
  block.d = active_dependency;
444
427
  }
@@ -453,6 +436,43 @@ export function run_branch(block, fn, anchor, value) {
453
436
  }
454
437
  }
455
438
 
439
+ /**
440
+ * Runs `fn(block.s, arg)` as a rerun of `block` driven from outside it: an if
441
+ * whose condition the enclosing render function evaluates renders its branch
442
+ * this way. The function runs under the block, in the block's namespace, and
443
+ * the deriveds the block's previous run created are released afterwards
444
+ * unless still read, as `run_block` does for a rerun. An error propagates to
445
+ * the caller's block.
446
+ * @template S, A
447
+ * @param {Block} block
448
+ * @param {(state: S, arg: A) => void} fn
449
+ * @param {A} arg
450
+ */
451
+ export function run_in_block(block, fn, arg) {
452
+ var previous_block = active_block;
453
+ var previous_reaction = active_reaction;
454
+ var previous_component = active_component;
455
+ var previous_namespace = active_namespace;
456
+ var previous_deriveds = (block.f & CREATES_DERIVEDS) !== 0 ? take_created_deriveds(block) : null;
457
+
458
+ try {
459
+ active_block = block;
460
+ active_reaction = block;
461
+ active_component = block.co;
462
+ var ns = block.f & NAMESPACE_BLOCK;
463
+ active_namespace = ns === 0 ? DEFAULT_NAMESPACE : ns === SVG_BLOCK ? 'svg' : 'mathml';
464
+ fn(/** @type {S} */ (block.s), arg);
465
+ } finally {
466
+ active_block = previous_block;
467
+ active_reaction = previous_reaction;
468
+ active_component = previous_component;
469
+ active_namespace = previous_namespace;
470
+ if (previous_deriveds !== null) {
471
+ keep_deriveds(block, release_deriveds(previous_deriveds));
472
+ }
473
+ }
474
+ }
475
+
456
476
  /**
457
477
  * @param {Block} block
458
478
  * @param {boolean} [first_run] true when the block has no children, teardown,
@@ -464,6 +484,7 @@ export function run_block(block, first_run = false) {
464
484
  var previous_tracking = tracking;
465
485
  var previous_dependency = active_dependency;
466
486
  var previous_component = active_component;
487
+ var previous_namespace = active_namespace;
467
488
  /** @type {Derived[] | null} */
468
489
  var previous_deriveds = null;
469
490
 
@@ -471,6 +492,10 @@ export function run_block(block, first_run = false) {
471
492
  active_block = block;
472
493
  active_reaction = block;
473
494
  active_component = block.co;
495
+ // The namespace the block was created in (see `create_block`): a rerun
496
+ // from a flush is outside the `with_ns()` call that established it.
497
+ var ns = block.f & NAMESPACE_BLOCK;
498
+ active_namespace = ns === 0 ? DEFAULT_NAMESPACE : ns === SVG_BLOCK ? 'svg' : 'mathml';
474
499
 
475
500
  if (!first_run) {
476
501
  previous_deriveds = prepare_rerun(block);
@@ -500,6 +525,7 @@ export function run_block(block, first_run = false) {
500
525
  tracking = previous_tracking;
501
526
  active_dependency = previous_dependency;
502
527
  active_component = previous_component;
528
+ active_namespace = previous_namespace;
503
529
 
504
530
  // The previous run's deriveds, whether this run finished or threw, and
505
531
  // after this run's dependencies replaced the old ones (so a derived the
@@ -514,21 +540,6 @@ export function run_block(block, first_run = false) {
514
540
 
515
541
  var empty_get_set = { get: undefined, set: undefined };
516
542
 
517
- /**
518
- * Complete all deferred boundary requests registered on a tracked value.
519
- * @param {Tracked} t
520
- * @param {boolean} [show_resolved=true]
521
- */
522
- function complete_deferred_boundaries(t, show_resolved = true) {
523
- if (t.d !== null) {
524
- for (var i = 0; i < t.d.length; i++) {
525
- var entry = t.d[i];
526
- complete_boundary_request(entry.b, entry.r, show_resolved);
527
- }
528
- t.d = null;
529
- }
530
- }
531
-
532
543
  class TrackedValue {
533
544
  /**
534
545
  * @param {any} v
@@ -564,17 +575,6 @@ class TrackedValue {
564
575
  set value(v) {
565
576
  set(this, v);
566
577
  }
567
- /**
568
- * A read-only view: a derived over this value, for a receiver that should
569
- * read but not write it. Equivalent to `track(() => tracked.value)`. The
570
- * view is owned by the block that creates it, not by this value's block, so
571
- * a view made in a component is released with that component even when the
572
- * value outlives it.
573
- * @returns {Derived}
574
- */
575
- readOnly() {
576
- return derived(() => get_tracked(this), /** @type {Block} */ (active_block));
577
- }
578
578
  }
579
579
 
580
580
  class DerivedValue {
@@ -618,17 +618,6 @@ class DerivedValue {
618
618
  set value(v) {
619
619
  set(this, v);
620
620
  }
621
- /**
622
- * A read-only view (see `TrackedValue#readOnly`). A derived without a
623
- * setter is already read-only and is returned as is; a writable one is
624
- * wrapped in a derived that follows it.
625
- * @returns {Derived}
626
- */
627
- readOnly() {
628
- return this.a.set === undefined
629
- ? this
630
- : derived(() => get_derived(this), /** @type {Block} */ (active_block));
631
- }
632
621
  }
633
622
 
634
623
  if (DEV) {
@@ -649,7 +638,7 @@ export function tracked(v, block, hash, get, set) {
649
638
  var t = /** @type {Tracked} */ (
650
639
  new TrackedValue(v, block || active_block, get || set ? { get, set } : empty_get_set, hash)
651
640
  );
652
- if (hydrating && hash !== undefined) {
641
+ if (HYDRATION && hydrating && hash !== undefined) {
653
642
  track_hash_reference.set(hash, t);
654
643
  }
655
644
  return t;
@@ -680,7 +669,7 @@ export function derived(fn, block, hash, get, set) {
680
669
  created.push(d);
681
670
  }
682
671
  }
683
- if (hydrating && hash !== undefined) {
672
+ if (HYDRATION && hydrating && hash !== undefined) {
684
673
  track_hash_reference.set(hash, d);
685
674
  }
686
675
  return d;
@@ -774,6 +763,30 @@ function keep_deriveds(block, kept) {
774
763
  block.f |= CREATES_DERIVEDS;
775
764
  }
776
765
 
766
+ /**
767
+ * A read-only view of a tracked or derived value, for a receiver that should
768
+ * read but not write it: a derived over a tracked (or a writable derived),
769
+ * equivalent to `track(() => value.value)`; a read-only derived as it is; a
770
+ * plain value as it is, like `get`. The view is owned by the block that
771
+ * creates it, not by the value's block, so a view made in a component is
772
+ * released with that component even when the value outlives it.
773
+ * @param {any} value
774
+ * @param {Block} [block]
775
+ * @returns {any}
776
+ */
777
+ export function track_read_only(value, block) {
778
+ if (!is_ripple_object(value)) {
779
+ return value;
780
+ }
781
+ var owner = block || /** @type {Block} */ (active_block);
782
+ if ((value.f & DERIVED) !== 0) {
783
+ var d = /** @type {Derived} */ (value);
784
+ return d.a.set === undefined ? d : derived(() => get_derived(d), owner);
785
+ }
786
+ var t = /** @type {Tracked} */ (value);
787
+ return derived(() => get_tracked(t), owner);
788
+ }
789
+
777
790
  /**
778
791
  * @param {any} v
779
792
  * @param {Block} b
@@ -787,7 +800,7 @@ export function track(v, b, hash, get, set) {
787
800
  return v;
788
801
  }
789
802
  if (b === null) {
790
- throw new TypeError('track() requires a valid component context');
803
+ track_orphan();
791
804
  }
792
805
 
793
806
  if (typeof v === 'function') {
@@ -796,243 +809,6 @@ export function track(v, b, hash, get, set) {
796
809
  return tracked(v, b, hash, get, set);
797
810
  }
798
811
 
799
- /**
800
- * @param {any} fn
801
- * @param {Block} b
802
- * @param {string} hash - Unique hash for SSR serialization/hydration
803
- * @returns {Tracked | void}
804
- */
805
- export function track_async(fn, b, hash) {
806
- if (is_ripple_object(fn)) {
807
- return fn;
808
- }
809
-
810
- var target_block = b || active_block;
811
- if (target_block === null) {
812
- throw new TypeError('trackAsync() requires a valid component context');
813
- }
814
-
815
- if (typeof fn !== 'function') {
816
- throw new TypeError(
817
- 'trackAsync() only accepts function arguments that return a promise or an object with a promise property',
818
- );
819
- }
820
-
821
- // During hydration, attempt to read serialized data from SSR
822
- var had_hydration_data = false;
823
- var hydration_value;
824
- /** @type {string[] | undefined} */
825
- var hydration_deps;
826
-
827
- if (hydrating) {
828
- var script_id = get_track_async_script_id(hash);
829
- var script_el = document.getElementById(script_id);
830
- if (script_el) {
831
- var envelope = JSON.parse(/** @type {string} */ (script_el.textContent));
832
- script_el.remove();
833
-
834
- if (envelope.ok) {
835
- had_hydration_data = true;
836
- hydration_value =
837
- envelope.payload === undefined ? envelope.value : revive(envelope.payload);
838
- hydration_deps = envelope.deps;
839
- } else {
840
- // trigger the catch block
841
- throw new Error(envelope.error?.message ?? 'Unknown server error');
842
- }
843
- }
844
- }
845
-
846
- var t = tracked(had_hydration_data ? hydration_value : SUSPENSE_PENDING, target_block, hash);
847
-
848
- // Capture the call-site block for boundary lookups. target_block is the
849
- // component's block (passed by compiler), but the actual try/pending/catch
850
- // boundary is an ancestor of active_block (the block executing trackAsync).
851
- var call_site_block = /** @type {Block} */ (active_block);
852
-
853
- var version = 0;
854
- /** @type {AbortController | null} */
855
- var abort_controller = null;
856
- var request_id = 0;
857
- /** @type {Block | null} */
858
- var boundary = null;
859
-
860
- // TODO: decide if instead of insisting on pending, we create our own boundary
861
- // we currently require a pending block upstream but we could also
862
- // create a try/pending/catch boundary at mount and hydration like
863
- // we do on the server so that there is always a boundary present.
864
- // It can handle global pending when none were provided.
865
- // Not sure about the catch boundary because if none were provided,
866
- // the whole app for any error will be unmounted with the catch block rendered
867
-
868
- // Find boundary from the call-site block.
869
- boundary = get_pending_boundary(active_block);
870
- if (boundary === null) {
871
- throw new Error('Missing parent `try { ... } pending { ... }` statement');
872
- }
873
-
874
- // If we hydrated with resolved data, the SSR already completed this request.
875
- // Otherwise mark a pending request on the boundary for the client-side run.
876
- if (!had_hydration_data) {
877
- request_id = begin_boundary_request(boundary);
878
- }
879
-
880
- pre_effect(() => {
881
- if (had_hydration_data) {
882
- // First run after hydration: skip fn() entirely (the SSR already
883
- // produced the resolved value) and instead register the direct
884
- // dependencies from the serialized deps list so future dep changes
885
- // trigger a re-run via the normal async path.
886
- had_hydration_data = false;
887
- if (hydration_deps !== undefined) {
888
- for (var i = 0; i < hydration_deps.length; i++) {
889
- var dep_ref = track_hash_reference.get(hydration_deps[i]);
890
- if (dep_ref !== undefined) {
891
- get(dep_ref);
892
- }
893
- }
894
- }
895
- return;
896
- }
897
-
898
- var current_version = ++version;
899
-
900
- // Abort previous in-flight request
901
- if (abort_controller !== null && abort_controller.signal.aborted === false) {
902
- abort_controller.abort(TRACKED_UPDATED);
903
- }
904
- abort_controller = null;
905
-
906
- // Manage boundary request: replace if in-flight, or begin new if previous completed
907
- if (request_id > 0 && boundary !== null) {
908
- request_id = replace_boundary_request(boundary, request_id);
909
- } else if (boundary !== null) {
910
- request_id = begin_boundary_request(boundary);
911
- }
912
-
913
- // Set to pending before calling fn() in case it's sync.
914
- if (t.__v !== SUSPENSE_PENDING) {
915
- update_tracked_value_clock(t, SUSPENSE_PENDING);
916
- }
917
-
918
- // Temporarily allow mutations so set() doesn't throw inside the pre-effect
919
- var previous_is_mutating_allowed = is_mutating_allowed;
920
- is_mutating_allowed = true;
921
-
922
- var result;
923
- try {
924
- result = fn();
925
- } catch (e) {
926
- is_mutating_allowed = previous_is_mutating_allowed;
927
- if (e === ASYNC_DERIVED_READ_THROWN) {
928
- // A dependency is still pending or rejected (e.g. chained trackAsync).
929
- // Check if any dependency is rejected — if so, propagate rejection.
930
- var dep = active_dependency;
931
- while (dep !== null) {
932
- if (dep.t.__v === SUSPENSE_REJECTED) {
933
- update_tracked_value_clock(t, SUSPENSE_REJECTED);
934
- complete_deferred_boundaries(t, false);
935
- if (request_id > 0 && boundary !== null) {
936
- complete_boundary_request(boundary, request_id, false);
937
- request_id = 0;
938
- }
939
- return;
940
- }
941
- dep = dep.n;
942
- }
943
- // Dependencies are pending, not rejected — register deferred
944
- // rejection so that if the boundary goes to catch mode, this
945
- // tracked value is also set to REJECTED.
946
- if (request_id > 0 && boundary !== null) {
947
- register_boundary_deferred(boundary, request_id, () => {
948
- update_tracked_value_clock(t, SUSPENSE_REJECTED);
949
- });
950
- }
951
- return;
952
- }
953
- throw e;
954
- }
955
- is_mutating_allowed = previous_is_mutating_allowed;
956
-
957
- // Check if the result is async
958
- var previous_tracking = tracking;
959
- tracking = false;
960
- var async_result = get_async_track_result(result);
961
- tracking = previous_tracking;
962
-
963
- if (async_result === null) {
964
- // Sync result
965
- update_tracked_value_clock(t, result);
966
- if (request_id > 0 && boundary !== null) {
967
- complete_boundary_request(boundary, request_id);
968
- request_id = 0;
969
- }
970
- return;
971
- }
972
-
973
- // Capture per-invocation so async closures (rejection handler, teardown)
974
- // have a stable reference. The shared abort_controller is only read
975
- // synchronously at the top of the pre_effect to abort the previous request.
976
- var current_abort_controller = async_result.abort_controller;
977
- abort_controller = current_abort_controller;
978
-
979
- async_result.promise.then(
980
- (resolved) => {
981
- if (current_version !== version) {
982
- // stale
983
- return;
984
- }
985
- update_tracked_value_clock(t, resolved);
986
- complete_deferred_boundaries(t);
987
- if (request_id > 0 && boundary !== null) {
988
- complete_boundary_request(boundary, request_id);
989
- request_id = 0;
990
- }
991
- },
992
- (error) => {
993
- if (current_version !== version) return; // stale
994
-
995
- var is_internal_abort =
996
- error === TRACKED_UPDATED || current_abort_controller?.signal?.reason === TRACKED_UPDATED;
997
- if (is_internal_abort) {
998
- // Internal abort (superseded by a new request) — don't set rejected
999
- if (request_id > 0 && boundary !== null) {
1000
- complete_boundary_request(boundary, request_id, false);
1001
- request_id = 0;
1002
- }
1003
- complete_deferred_boundaries(t, false);
1004
- return;
1005
- }
1006
-
1007
- update_tracked_value_clock(t, SUSPENSE_REJECTED);
1008
- complete_deferred_boundaries(t, false);
1009
-
1010
- // Route error to catch boundary
1011
- var boundary_with_catch = get_boundary_with_catch(call_site_block);
1012
- if (boundary_with_catch !== null) {
1013
- handle_boundary_error(boundary_with_catch, error);
1014
- }
1015
-
1016
- if (request_id > 0 && boundary !== null) {
1017
- var should_show_resolved =
1018
- boundary_with_catch === boundary || boundary === null ? false : true;
1019
- complete_boundary_request(boundary, request_id, should_show_resolved);
1020
- request_id = 0;
1021
- }
1022
- },
1023
- );
1024
-
1025
- return () => {
1026
- // Teardown: abort in-flight request when block is destroyed
1027
- if (current_abort_controller !== null && current_abort_controller.signal.aborted === false) {
1028
- current_abort_controller.abort(TRACKED_UPDATED);
1029
- }
1030
- };
1031
- });
1032
-
1033
- return t;
1034
- }
1035
-
1036
812
  /**
1037
813
  * @param {(Derived | Tracked) | (() => any)} t
1038
814
  * @returns {boolean}
@@ -1463,9 +1239,7 @@ function flush_microtasks() {
1463
1239
 
1464
1240
  flush_count++;
1465
1241
  if (flush_count > 1001) {
1466
- throw new Error(
1467
- 'Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state.',
1468
- );
1242
+ update_depth_exceeded();
1469
1243
  }
1470
1244
  var pending = queue;
1471
1245
  queue = create_queue();
@@ -1627,9 +1401,7 @@ export function get_tracked(tracked) {
1627
1401
  */
1628
1402
  export function set(tracked, value) {
1629
1403
  if (!is_mutating_allowed) {
1630
- throw new Error(
1631
- 'Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation',
1632
- );
1404
+ set_in_derived();
1633
1405
  }
1634
1406
 
1635
1407
  var old_value = tracked.__v;
@@ -1894,9 +1666,9 @@ export function scope() {
1894
1666
  * @param {string} [err]
1895
1667
  * @returns {Block | never}
1896
1668
  */
1897
- export function safe_scope(err = 'Cannot access outside of a component context') {
1669
+ export function safe_scope(err) {
1898
1670
  if (active_scope === null) {
1899
- throw new Error(err);
1671
+ scope_orphan(err);
1900
1672
  }
1901
1673
 
1902
1674
  return /** @type {Block} */ (active_scope);
@@ -2103,6 +1875,20 @@ export function with_ns(namespace, fn) {
2103
1875
  }
2104
1876
  }
2105
1877
 
1878
+ /**
1879
+ * Sets the active namespace and returns the previous one, for setup code that
1880
+ * switches namespace part-way through a render function (the children of a
1881
+ * `foreignObject`), where a `with_ns()` thunk would hide its declarations.
1882
+ * `run_block` restores the namespace after the render function either way.
1883
+ * @param {keyof typeof NAMESPACE_URI} namespace
1884
+ * @returns {keyof typeof NAMESPACE_URI}
1885
+ */
1886
+ export function set_ns(namespace) {
1887
+ var previous_namespace = active_namespace;
1888
+ active_namespace = namespace;
1889
+ return previous_namespace;
1890
+ }
1891
+
2106
1892
  /**
2107
1893
  * @returns {symbol}
2108
1894
  */