ripple 0.3.11 → 0.3.13

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 (190) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/package.json +8 -2
  3. package/src/compiler/phases/1-parse/index.js +73 -30
  4. package/src/compiler/phases/2-analyze/index.js +28 -58
  5. package/src/compiler/phases/3-transform/client/index.js +127 -164
  6. package/src/compiler/phases/3-transform/segments.js +4 -8
  7. package/src/compiler/phases/3-transform/server/index.js +210 -360
  8. package/src/compiler/types/import.d.ts +0 -12
  9. package/src/compiler/types/index.d.ts +12 -5
  10. package/src/compiler/types/parse.d.ts +2 -0
  11. package/src/compiler/utils.js +39 -44
  12. package/src/helpers.d.ts +2 -0
  13. package/src/runtime/index-client.js +15 -13
  14. package/src/runtime/index-server.js +18 -11
  15. package/src/runtime/internal/client/blocks.js +19 -23
  16. package/src/runtime/internal/client/constants.js +20 -9
  17. package/src/runtime/internal/client/index.js +14 -4
  18. package/src/runtime/internal/client/runtime.js +435 -173
  19. package/src/runtime/internal/client/try.js +334 -156
  20. package/src/runtime/internal/client/types.d.ts +26 -0
  21. package/src/runtime/internal/server/blocks.js +183 -0
  22. package/src/runtime/internal/server/constants.js +7 -0
  23. package/src/runtime/internal/server/index.js +780 -148
  24. package/src/runtime/internal/server/types.d.ts +35 -0
  25. package/src/server/index.js +1 -1
  26. package/src/utils/async.js +35 -0
  27. package/src/utils/builders.js +3 -1
  28. package/tests/client/__snapshots__/computed-properties.test.rsrx.snap +49 -0
  29. package/tests/client/__snapshots__/for.test.rsrx.snap +319 -0
  30. package/tests/client/__snapshots__/html.test.rsrx.snap +40 -0
  31. package/tests/client/_etc.test.rsrx +7 -0
  32. package/tests/client/array/{array.static.test.ripple → array.static.test.rsrx} +18 -20
  33. package/tests/client/async-suspend.test.rsrx +662 -0
  34. package/tests/client/basic/__snapshots__/basic.attributes.test.rsrx.snap +60 -0
  35. package/tests/client/basic/__snapshots__/basic.rendering.test.rsrx.snap +59 -0
  36. package/tests/client/basic/{basic.errors.test.ripple → basic.errors.test.rsrx} +2 -2
  37. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +12 -0
  38. package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +46 -0
  39. package/tests/client/compiler/{compiler.try-in-function.test.ripple → compiler.try-in-function.test.rsrx} +8 -6
  40. package/tests/client/composite/__snapshots__/composite.render.test.rsrx.snap +37 -0
  41. package/tests/client/{function-overload.test.ripple → function-overload.test.rsrx} +1 -1
  42. package/tests/client/try.test.rsrx +1702 -0
  43. package/tests/hydration/build-components.js +5 -3
  44. package/tests/hydration/compiled/client/head.js +11 -11
  45. package/tests/hydration/compiled/client/mixed-control-flow.js +55 -70
  46. package/tests/hydration/compiled/client/nested-control-flow.js +72 -88
  47. package/tests/hydration/compiled/client/try.js +42 -54
  48. package/tests/hydration/compiled/server/basic.js +491 -369
  49. package/tests/hydration/compiled/server/composite.js +153 -128
  50. package/tests/hydration/compiled/server/events.js +166 -145
  51. package/tests/hydration/compiled/server/for.js +821 -677
  52. package/tests/hydration/compiled/server/head.js +200 -165
  53. package/tests/hydration/compiled/server/hmr.js +62 -54
  54. package/tests/hydration/compiled/server/html-in-template.js +64 -55
  55. package/tests/hydration/compiled/server/html.js +1477 -1360
  56. package/tests/hydration/compiled/server/if-children.js +448 -408
  57. package/tests/hydration/compiled/server/if.js +204 -171
  58. package/tests/hydration/compiled/server/mixed-control-flow.js +237 -195
  59. package/tests/hydration/compiled/server/nested-control-flow.js +533 -467
  60. package/tests/hydration/compiled/server/portal.js +94 -107
  61. package/tests/hydration/compiled/server/reactivity.js +87 -64
  62. package/tests/hydration/compiled/server/return.js +1424 -1174
  63. package/tests/hydration/compiled/server/switch.js +268 -238
  64. package/tests/hydration/compiled/server/try.js +98 -87
  65. package/tests/hydration/components/{mixed-control-flow.ripple → mixed-control-flow.rsrx} +2 -2
  66. package/tests/hydration/components/{try.ripple → try.rsrx} +4 -2
  67. package/tests/hydration/mixed-control-flow.test.js +14 -0
  68. package/tests/hydration/nested-control-flow.test.js +50 -48
  69. package/tests/hydration/try.test.js +25 -0
  70. package/tests/server/__snapshots__/compiler.test.ripple.snap +0 -32
  71. package/tests/server/__snapshots__/compiler.test.rsrx.snap +95 -0
  72. package/tests/server/{compiler.test.ripple → compiler.test.rsrx} +0 -17
  73. package/tests/server/{html-nesting-validation.test.ripple → html-nesting-validation.test.rsrx} +3 -3
  74. package/tests/server/streaming-ssr.test.rsrx +115 -0
  75. package/tests/server/try.test.rsrx +503 -0
  76. package/tests/utils/compiler-compat-config.test.js +3 -3
  77. package/tests/utils/vite-plugin-config.test.js +1 -1
  78. package/tests/utils/vite-plugin-hmr.test.js +5 -5
  79. package/tsconfig.json +4 -0
  80. package/types/index.d.ts +13 -23
  81. package/types/server.d.ts +43 -16
  82. package/tests/client/_etc.test.ripple +0 -5
  83. package/tests/client/async-suspend.test.ripple +0 -94
  84. package/tests/client/try.test.ripple +0 -196
  85. package/tests/server/streaming-ssr.test.ripple +0 -68
  86. package/tests/server/try.test.ripple +0 -82
  87. /package/tests/client/array/{array.copy-within.test.ripple → array.copy-within.test.rsrx} +0 -0
  88. /package/tests/client/array/{array.derived.test.ripple → array.derived.test.rsrx} +0 -0
  89. /package/tests/client/array/{array.iteration.test.ripple → array.iteration.test.rsrx} +0 -0
  90. /package/tests/client/array/{array.mutations.test.ripple → array.mutations.test.rsrx} +0 -0
  91. /package/tests/client/array/{array.to-methods.test.ripple → array.to-methods.test.rsrx} +0 -0
  92. /package/tests/client/basic/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  93. /package/tests/client/basic/{basic.collections.test.ripple → basic.collections.test.rsrx} +0 -0
  94. /package/tests/client/basic/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  95. /package/tests/client/basic/{basic.events.test.ripple → basic.events.test.rsrx} +0 -0
  96. /package/tests/client/basic/{basic.get-set.test.ripple → basic.get-set.test.rsrx} +0 -0
  97. /package/tests/client/basic/{basic.hmr.test.ripple → basic.hmr.test.rsrx} +0 -0
  98. /package/tests/client/basic/{basic.reactivity.test.ripple → basic.reactivity.test.rsrx} +0 -0
  99. /package/tests/client/basic/{basic.rendering.test.ripple → basic.rendering.test.rsrx} +0 -0
  100. /package/tests/client/basic/{basic.styling.test.ripple → basic.styling.test.rsrx} +0 -0
  101. /package/tests/client/basic/{basic.utilities.test.ripple → basic.utilities.test.rsrx} +0 -0
  102. /package/tests/client/{boundaries.test.ripple → boundaries.test.rsrx} +0 -0
  103. /package/tests/client/compiler/{compiler.assignments.test.ripple → compiler.assignments.test.rsrx} +0 -0
  104. /package/tests/client/compiler/{compiler.attributes.test.ripple → compiler.attributes.test.rsrx} +0 -0
  105. /package/tests/client/compiler/{compiler.basic.test.ripple → compiler.basic.test.rsrx} +0 -0
  106. /package/tests/client/compiler/{compiler.regex.test.ripple → compiler.regex.test.rsrx} +0 -0
  107. /package/tests/client/compiler/{compiler.tracked-access.test.ripple → compiler.tracked-access.test.rsrx} +0 -0
  108. /package/tests/client/compiler/{compiler.typescript.test.ripple → compiler.typescript.test.rsrx} +0 -0
  109. /package/tests/client/composite/{composite.dynamic-components.test.ripple → composite.dynamic-components.test.rsrx} +0 -0
  110. /package/tests/client/composite/{composite.generics.test.ripple → composite.generics.test.rsrx} +0 -0
  111. /package/tests/client/composite/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  112. /package/tests/client/composite/{composite.reactivity.test.ripple → composite.reactivity.test.rsrx} +0 -0
  113. /package/tests/client/composite/{composite.render.test.ripple → composite.render.test.rsrx} +0 -0
  114. /package/tests/client/{computed-properties.test.ripple → computed-properties.test.rsrx} +0 -0
  115. /package/tests/client/{context.test.ripple → context.test.rsrx} +0 -0
  116. /package/tests/client/css/{global-additional-cases.test.ripple → global-additional-cases.test.rsrx} +0 -0
  117. /package/tests/client/css/{global-advanced-selectors.test.ripple → global-advanced-selectors.test.rsrx} +0 -0
  118. /package/tests/client/css/{global-at-rules.test.ripple → global-at-rules.test.rsrx} +0 -0
  119. /package/tests/client/css/{global-basic.test.ripple → global-basic.test.rsrx} +0 -0
  120. /package/tests/client/css/{global-classes-ids.test.ripple → global-classes-ids.test.rsrx} +0 -0
  121. /package/tests/client/css/{global-combinators.test.ripple → global-combinators.test.rsrx} +0 -0
  122. /package/tests/client/css/{global-complex-nesting.test.ripple → global-complex-nesting.test.rsrx} +0 -0
  123. /package/tests/client/css/{global-edge-cases.test.ripple → global-edge-cases.test.rsrx} +0 -0
  124. /package/tests/client/css/{global-keyframes.test.ripple → global-keyframes.test.rsrx} +0 -0
  125. /package/tests/client/css/{global-nested.test.ripple → global-nested.test.rsrx} +0 -0
  126. /package/tests/client/css/{global-pseudo.test.ripple → global-pseudo.test.rsrx} +0 -0
  127. /package/tests/client/css/{global-scoping.test.ripple → global-scoping.test.rsrx} +0 -0
  128. /package/tests/client/css/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
  129. /package/tests/client/{date.test.ripple → date.test.rsrx} +0 -0
  130. /package/tests/client/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  131. /package/tests/client/{events.test.ripple → events.test.rsrx} +0 -0
  132. /package/tests/client/{for.test.ripple → for.test.rsrx} +0 -0
  133. /package/tests/client/{function-overload-import.ripple → function-overload-import.rsrx} +0 -0
  134. /package/tests/client/{head.test.ripple → head.test.rsrx} +0 -0
  135. /package/tests/client/{html.test.ripple → html.test.rsrx} +0 -0
  136. /package/tests/client/{input-value.test.ripple → input-value.test.rsrx} +0 -0
  137. /package/tests/client/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  138. /package/tests/client/{map.test.ripple → map.test.rsrx} +0 -0
  139. /package/tests/client/{media-query.test.ripple → media-query.test.rsrx} +0 -0
  140. /package/tests/client/{object.test.ripple → object.test.rsrx} +0 -0
  141. /package/tests/client/{portal.test.ripple → portal.test.rsrx} +0 -0
  142. /package/tests/client/{ref.test.ripple → ref.test.rsrx} +0 -0
  143. /package/tests/client/{return.test.ripple → return.test.rsrx} +0 -0
  144. /package/tests/client/{set.test.ripple → set.test.rsrx} +0 -0
  145. /package/tests/client/{svg.test.ripple → svg.test.rsrx} +0 -0
  146. /package/tests/client/{switch.test.ripple → switch.test.rsrx} +0 -0
  147. /package/tests/client/{tsx.test.ripple → tsx.test.rsrx} +0 -0
  148. /package/tests/client/{typescript-generics.test.ripple → typescript-generics.test.rsrx} +0 -0
  149. /package/tests/client/url/{url.derived.test.ripple → url.derived.test.rsrx} +0 -0
  150. /package/tests/client/url/{url.parsing.test.ripple → url.parsing.test.rsrx} +0 -0
  151. /package/tests/client/url/{url.partial-removal.test.ripple → url.partial-removal.test.rsrx} +0 -0
  152. /package/tests/client/url/{url.reactivity.test.ripple → url.reactivity.test.rsrx} +0 -0
  153. /package/tests/client/url/{url.serialization.test.ripple → url.serialization.test.rsrx} +0 -0
  154. /package/tests/client/url-search-params/{url-search-params.derived.test.ripple → url-search-params.derived.test.rsrx} +0 -0
  155. /package/tests/client/url-search-params/{url-search-params.initialization.test.ripple → url-search-params.initialization.test.rsrx} +0 -0
  156. /package/tests/client/url-search-params/{url-search-params.iteration.test.ripple → url-search-params.iteration.test.rsrx} +0 -0
  157. /package/tests/client/url-search-params/{url-search-params.mutation.test.ripple → url-search-params.mutation.test.rsrx} +0 -0
  158. /package/tests/client/url-search-params/{url-search-params.retrieval.test.ripple → url-search-params.retrieval.test.rsrx} +0 -0
  159. /package/tests/client/url-search-params/{url-search-params.serialization.test.ripple → url-search-params.serialization.test.rsrx} +0 -0
  160. /package/tests/client/url-search-params/{url-search-params.tracked-url.test.ripple → url-search-params.tracked-url.test.rsrx} +0 -0
  161. /package/tests/hydration/components/{basic.ripple → basic.rsrx} +0 -0
  162. /package/tests/hydration/components/{composite.ripple → composite.rsrx} +0 -0
  163. /package/tests/hydration/components/{events.ripple → events.rsrx} +0 -0
  164. /package/tests/hydration/components/{for.ripple → for.rsrx} +0 -0
  165. /package/tests/hydration/components/{head.ripple → head.rsrx} +0 -0
  166. /package/tests/hydration/components/{hmr.ripple → hmr.rsrx} +0 -0
  167. /package/tests/hydration/components/{html-in-template.ripple → html-in-template.rsrx} +0 -0
  168. /package/tests/hydration/components/{html.ripple → html.rsrx} +0 -0
  169. /package/tests/hydration/components/{if-children.ripple → if-children.rsrx} +0 -0
  170. /package/tests/hydration/components/{if.ripple → if.rsrx} +0 -0
  171. /package/tests/hydration/components/{nested-control-flow.ripple → nested-control-flow.rsrx} +0 -0
  172. /package/tests/hydration/components/{portal.ripple → portal.rsrx} +0 -0
  173. /package/tests/hydration/components/{reactivity.ripple → reactivity.rsrx} +0 -0
  174. /package/tests/hydration/components/{return.ripple → return.rsrx} +0 -0
  175. /package/tests/hydration/components/{switch.ripple → switch.rsrx} +0 -0
  176. /package/tests/server/{await.test.ripple → await.test.rsrx} +0 -0
  177. /package/tests/server/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  178. /package/tests/server/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  179. /package/tests/server/{basic.test.ripple → basic.test.rsrx} +0 -0
  180. /package/tests/server/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  181. /package/tests/server/{composite.test.ripple → composite.test.rsrx} +0 -0
  182. /package/tests/server/{context.test.ripple → context.test.rsrx} +0 -0
  183. /package/tests/server/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  184. /package/tests/server/{for.test.ripple → for.test.rsrx} +0 -0
  185. /package/tests/server/{head.test.ripple → head.test.rsrx} +0 -0
  186. /package/tests/server/{if.test.ripple → if.test.rsrx} +0 -0
  187. /package/tests/server/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  188. /package/tests/server/{return.test.ripple → return.test.rsrx} +0 -0
  189. /package/tests/server/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
  190. /package/tests/server/{switch.test.ripple → switch.test.rsrx} +0 -0
@@ -1,4 +1,4 @@
1
- /** @import { Block, Component, Dependency, Derived, Tracked } from '#client' */
1
+ /** @import { Block, Component, Dependency, Derived, Tracked, BlockWithTryBoundaryAndCatch, DeferredTrackedEntry } from '#client' */
2
2
  /** @import { NAMESPACE_URI } from './constants.js' */
3
3
 
4
4
  import { DEV } from 'esm-env';
@@ -6,30 +6,42 @@ import {
6
6
  destroy_block,
7
7
  destroy_non_branch_children,
8
8
  effect,
9
- is_destroyed,
10
- render,
9
+ pause_block,
10
+ pre_effect,
11
11
  } from './blocks.js';
12
12
  import {
13
- ASYNC_BLOCK,
13
+ ASYNC_DERIVED_READ_THROWN,
14
14
  BLOCK_HAS_RUN,
15
15
  BRANCH_BLOCK,
16
16
  DERIVED,
17
17
  COMPUTED_PROPERTY,
18
18
  CONTAINS_TEARDOWN,
19
19
  CONTAINS_UPDATE,
20
- DEFERRED,
21
20
  DESTROYED,
22
21
  EFFECT_BLOCK,
23
22
  PAUSED,
23
+ PRE_EFFECT_BLOCK,
24
24
  ROOT_BLOCK,
25
25
  TRACKED,
26
- TRY_BLOCK,
27
26
  UNINITIALIZED,
28
27
  REF_PROP,
29
28
  TRACKED_OBJECT,
30
29
  DEFAULT_NAMESPACE,
30
+ DERIVED_UPDATED,
31
+ SUSPENSE_PENDING,
32
+ SUSPENSE_REJECTED,
33
+ TRY_BLOCK,
34
+ DIRECT_CHILD_BLOCK,
31
35
  } from './constants.js';
32
- import { capture, suspend } from './try.js';
36
+ import {
37
+ begin_boundary_request,
38
+ complete_boundary_request,
39
+ get_boundary_with_catch,
40
+ get_pending_boundary,
41
+ register_boundary_deferred,
42
+ register_boundary_paused_block,
43
+ replace_boundary_request,
44
+ } from './try.js';
33
45
  import {
34
46
  define_property,
35
47
  get_descriptor,
@@ -38,6 +50,7 @@ import {
38
50
  is_ripple_object,
39
51
  object_keys,
40
52
  } from './utils.js';
53
+ import { get_async_track_result } from '../../../utils/async.js';
41
54
 
42
55
  const FLUSH_MICROTASK = 0;
43
56
  const FLUSH_SYNC = 1;
@@ -55,7 +68,7 @@ export let active_namespace = DEFAULT_NAMESPACE;
55
68
  /** @type {boolean} */
56
69
  export let is_mutating_allowed = true;
57
70
 
58
- /** @type {Map<Tracked, any>} */
71
+ /** @type {Map<Tracked | Derived, any>} */
59
72
  var old_values = new Map();
60
73
 
61
74
  // Used for controlling the flush of blocks
@@ -72,6 +85,8 @@ let queued_root_blocks = [];
72
85
  let queued_microtasks = [];
73
86
  /** @type {number} */
74
87
  let flush_count = 0;
88
+ /** @type {(() => void)[]} */
89
+ var queued_post_block_flush = [];
75
90
  /** @type {null | Dependency} */
76
91
  let active_dependency = null;
77
92
 
@@ -141,7 +156,7 @@ export function run_teardown(block) {
141
156
 
142
157
  /**
143
158
  * @param {Block} block
144
- * @param {() => void} fn
159
+ * @param {() => any} fn
145
160
  */
146
161
  export function with_block(block, fn) {
147
162
  var prev_block = active_block;
@@ -172,6 +187,15 @@ function update_derived(computed) {
172
187
  }
173
188
  }
174
189
 
190
+ /**
191
+ * @param {Tracked} tracked
192
+ * @param {any} value
193
+ */
194
+ function update_tracked_value_clock(tracked, value) {
195
+ tracked.__v = value;
196
+ tracked.c = increment_clock();
197
+ }
198
+
175
199
  /**
176
200
  * @param {Derived} computed
177
201
  */
@@ -212,6 +236,20 @@ function run_derived(computed) {
212
236
  computed.d = active_dependency;
213
237
 
214
238
  return value;
239
+ } catch (error) {
240
+ computed.d = active_dependency;
241
+ if (error === ASYNC_DERIVED_READ_THROWN) {
242
+ // Check if any dependency is rejected — if so, propagate rejection
243
+ var dep = active_dependency;
244
+ while (dep !== null) {
245
+ if (dep.t.__v === SUSPENSE_REJECTED) {
246
+ return SUSPENSE_REJECTED;
247
+ }
248
+ dep = dep.n;
249
+ }
250
+ return SUSPENSE_PENDING;
251
+ }
252
+ throw error;
215
253
  } finally {
216
254
  active_block = previous_block;
217
255
  active_reaction = previous_reaction;
@@ -225,18 +263,13 @@ function run_derived(computed) {
225
263
  /**
226
264
  * @param {unknown} error
227
265
  * @param {Block} block
266
+ * @returns {BlockWithTryBoundaryAndCatch}
228
267
  */
229
268
  export function handle_error(error, block) {
230
- /** @type {Block | null} */
231
- var current = block;
232
-
233
- while (current !== null) {
234
- var state = current.s;
235
- if ((current.f & TRY_BLOCK) !== 0 && state.c !== null) {
236
- state.c(error);
237
- return;
238
- }
239
- current = current.p;
269
+ var boundary_with_catch = get_boundary_with_catch(block);
270
+ if (boundary_with_catch !== null) {
271
+ boundary_with_catch.s.c(error);
272
+ return boundary_with_catch;
240
273
  }
241
274
 
242
275
  throw error;
@@ -277,7 +310,53 @@ export function run_block(block) {
277
310
 
278
311
  block.d = active_dependency;
279
312
  } catch (error) {
280
- handle_error(error, block);
313
+ var is_component_direct = false;
314
+ var is_try_fn_block = false;
315
+ block.d = active_dependency;
316
+ // When a derived read throws ASYNC_DERIVED_READ_THROWN, it means the
317
+ // derived is still SUSPENSE_PENDING. The dependency was already registered,
318
+ // so we swallow the throw and let the parent continue processing. When
319
+ // the derived settles, the block will be dirty and rerun automatically.
320
+ if (error !== ASYNC_DERIVED_READ_THROWN) {
321
+ handle_error(error, block);
322
+ } else if (
323
+ // pending async tracked was read outside allowed blocks
324
+ (is_component_direct = active_component?.b === block) ||
325
+ (is_try_fn_block =
326
+ block.p !== null && (block.p.f & TRY_BLOCK) !== 0 && (block.f & DIRECT_CHILD_BLOCK) !== 0)
327
+ ) {
328
+ throw new Error(
329
+ `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.`,
330
+ );
331
+ } else {
332
+ // pending async tracked was read and threw ASYNC_DERIVED_READ_THROWN
333
+ var boundary = get_pending_boundary(block);
334
+ if (boundary !== null) {
335
+ pause_block(block);
336
+ register_boundary_paused_block(boundary, block);
337
+
338
+ // Register deferred boundary completions for async tracked deps.
339
+ // This handles the case where a child boundary reads a tracked value
340
+ // whose resolution is managed by a different (parent) boundary.
341
+ var dep = block.d;
342
+ while (dep !== null) {
343
+ var dep_tracked = /** @type {Tracked} */ (dep.t);
344
+ if (
345
+ (dep_tracked.__v === SUSPENSE_PENDING || dep_tracked.__v === SUSPENSE_REJECTED) &&
346
+ (dep_tracked.f & TRACKED) !== 0
347
+ ) {
348
+ var deferred_req = begin_boundary_request(boundary);
349
+ var entry = /** @type {DeferredTrackedEntry} */ ({ b: boundary, r: deferred_req });
350
+ if (dep_tracked.d === null) {
351
+ dep_tracked.d = [entry];
352
+ } else {
353
+ dep_tracked.d.push(entry);
354
+ }
355
+ }
356
+ dep = dep.n;
357
+ }
358
+ }
359
+ }
281
360
  } finally {
282
361
  active_block = previous_block;
283
362
  active_reaction = previous_reaction;
@@ -289,6 +368,22 @@ export function run_block(block) {
289
368
 
290
369
  var empty_get_set = { get: undefined, set: undefined };
291
370
 
371
+ /**
372
+ * Complete all deferred boundary requests registered on a tracked value.
373
+ * @param {Tracked} t
374
+ * @param {boolean} [show_resolved=true]
375
+ */
376
+ function complete_deferred_boundaries(t, show_resolved = true) {
377
+ if (t.d !== null) {
378
+ for (var i = 0; i < t.d.length; i++) {
379
+ var entry = t.d[i];
380
+ complete_boundary_request(entry.b, entry.r, show_resolved);
381
+ }
382
+ t.d = null;
383
+ }
384
+ }
385
+
386
+ /** @type {Tracked} */
292
387
  class TrackedValue {
293
388
  /**
294
389
  * @param {any} v
@@ -299,35 +394,37 @@ class TrackedValue {
299
394
  this.a = a;
300
395
  this.b = block;
301
396
  this.c = 0;
397
+ this.d = null;
302
398
  this.f = TRACKED;
303
399
  this.__v = v;
304
400
  }
305
401
  get [0]() {
306
- return get_tracked(/** @type {Tracked} */ (this));
402
+ return get_tracked(this);
307
403
  }
308
404
  set [0](v) {
309
- set(/** @type {Tracked} */ (this), v);
405
+ set(this, v);
310
406
  }
311
407
  get [1]() {
312
408
  return this;
313
409
  }
314
410
  get value() {
315
- return get_tracked(/** @type {Tracked} */ (this));
411
+ return get_tracked(this);
316
412
  }
317
413
  /** @param {any} v */
318
414
  set value(v) {
319
- set(/** @type {Tracked} */ (this), v);
415
+ set(this, v);
320
416
  }
321
417
  /** @returns {2} */
322
418
  get length() {
323
419
  return 2;
324
420
  }
325
421
  *[Symbol.iterator]() {
326
- yield get_tracked(/** @type {Tracked} */ (this));
422
+ yield get_tracked(this);
327
423
  yield this;
328
424
  }
329
425
  }
330
426
 
427
+ /** @type {Derived} */
331
428
  class DerivedValue {
332
429
  /**
333
430
  * @param {Function} fn
@@ -343,32 +440,32 @@ class DerivedValue {
343
440
  this.co = active_component;
344
441
  /** @type {null | Dependency} */
345
442
  this.d = null;
346
- this.f = TRACKED | DERIVED;
443
+ this.f = DERIVED;
347
444
  this.fn = fn;
348
445
  this.__v = UNINITIALIZED;
349
446
  }
350
447
  get [0]() {
351
- return get_derived(/** @type {Derived} */ (this));
448
+ return get_derived(this);
352
449
  }
353
450
  set [0](v) {
354
- set(/** @type {Derived} */ (this), v);
451
+ set(this, v);
355
452
  }
356
453
  get [1]() {
357
454
  return this;
358
455
  }
359
456
  get value() {
360
- return get_derived(/** @type {Derived} */ (this));
457
+ return get_derived(this);
361
458
  }
362
459
  /** @param {any} v */
363
460
  set value(v) {
364
- set(/** @type {Derived} */ (this), v);
461
+ set(this, v);
365
462
  }
366
463
  /** @returns {2} */
367
464
  get length() {
368
465
  return 2;
369
466
  }
370
467
  *[Symbol.iterator]() {
371
- yield get_derived(/** @type {Derived} */ (this));
468
+ yield get_derived(this);
372
469
  yield this;
373
470
  }
374
471
  }
@@ -426,6 +523,225 @@ export function track(v, get, set, b) {
426
523
  return tracked(v, b, get, set);
427
524
  }
428
525
 
526
+ /**
527
+ * @param {any} fn
528
+ * @param {Block} b
529
+ * @returns {Tracked | void}
530
+ */
531
+ export function track_async(fn, b) {
532
+ if (is_ripple_object(fn)) {
533
+ return fn;
534
+ }
535
+
536
+ var target_block = b || active_block;
537
+ if (target_block === null) {
538
+ throw new TypeError('trackAsync() requires a valid component context');
539
+ }
540
+
541
+ if (typeof fn !== 'function') {
542
+ throw new TypeError(
543
+ 'trackAsync() only accepts function arguments that return a promise or an object with a promise property',
544
+ );
545
+ }
546
+
547
+ var t = tracked(SUSPENSE_PENDING, target_block);
548
+
549
+ // Capture the call-site block for boundary lookups. target_block is the
550
+ // component's block (passed by compiler), but the actual try/pending/catch
551
+ // boundary is an ancestor of active_block (the block executing trackAsync).
552
+ var call_site_block = /** @type {Block} */ (active_block);
553
+
554
+ var version = 0;
555
+ /** @type {AbortController | null} */
556
+ var abort_controller = null;
557
+ var request_id = 0;
558
+ /** @type {Block | null} */
559
+ var boundary = null;
560
+
561
+ // Find boundary from the call-site block.
562
+ boundary = get_pending_boundary(active_block);
563
+ if (boundary === null) {
564
+ throw new Error('Missing parent `try { ... } pending { ... }` statement');
565
+ }
566
+
567
+ request_id = begin_boundary_request(boundary);
568
+
569
+ pre_effect(() => {
570
+ var current_version = ++version;
571
+
572
+ // Abort previous in-flight request
573
+ if (abort_controller !== null && abort_controller.signal.aborted === false) {
574
+ abort_controller.abort(DERIVED_UPDATED);
575
+ }
576
+ abort_controller = null;
577
+
578
+ // Manage boundary request: replace if in-flight, or begin new if previous completed
579
+ if (request_id > 0 && boundary !== null) {
580
+ request_id = replace_boundary_request(boundary, request_id);
581
+ } else if (boundary !== null) {
582
+ request_id = begin_boundary_request(boundary);
583
+ }
584
+
585
+ // Set to pending before calling fn() in case it's sync
586
+ if (t.__v !== SUSPENSE_PENDING) {
587
+ update_tracked_value_clock(t, SUSPENSE_PENDING);
588
+ schedule_update(t.b);
589
+ }
590
+
591
+ // Temporarily allow mutations so set() doesn't throw inside the pre-effect
592
+ var previous_is_mutating_allowed = is_mutating_allowed;
593
+ is_mutating_allowed = true;
594
+
595
+ var result;
596
+ try {
597
+ result = fn();
598
+ } catch (e) {
599
+ is_mutating_allowed = previous_is_mutating_allowed;
600
+ if (e === ASYNC_DERIVED_READ_THROWN) {
601
+ // A dependency is still pending or rejected (e.g. chained trackAsync).
602
+ // Check if any dependency is rejected — if so, propagate rejection.
603
+ var dep = active_dependency;
604
+ while (dep !== null) {
605
+ if (dep.t.__v === SUSPENSE_REJECTED) {
606
+ update_tracked_value_clock(t, SUSPENSE_REJECTED);
607
+ schedule_update(t.b);
608
+ complete_deferred_boundaries(t, false);
609
+ if (request_id > 0 && boundary !== null) {
610
+ complete_boundary_request(boundary, request_id, false);
611
+ request_id = 0;
612
+ }
613
+ return;
614
+ }
615
+ dep = dep.n;
616
+ }
617
+ // Dependencies are pending, not rejected — register deferred
618
+ // rejection so that if the boundary goes to catch mode, this
619
+ // tracked value is also set to REJECTED.
620
+ if (request_id > 0 && boundary !== null) {
621
+ register_boundary_deferred(boundary, request_id, () => {
622
+ update_tracked_value_clock(t, SUSPENSE_REJECTED);
623
+ });
624
+ }
625
+ return;
626
+ }
627
+ throw e;
628
+ }
629
+ is_mutating_allowed = previous_is_mutating_allowed;
630
+
631
+ // Check if the result is async
632
+ var previous_tracking = tracking;
633
+ tracking = false;
634
+ var async_result = get_async_track_result(result);
635
+ tracking = previous_tracking;
636
+
637
+ if (async_result === null) {
638
+ // Sync result
639
+ update_tracked_value_clock(t, result);
640
+ schedule_update(t.b);
641
+ if (request_id > 0 && boundary !== null) {
642
+ complete_boundary_request(boundary, request_id);
643
+ request_id = 0;
644
+ }
645
+ return;
646
+ }
647
+
648
+ // Capture per-invocation so async closures (rejection handler, teardown)
649
+ // have a stable reference. The shared abort_controller is only read
650
+ // synchronously at the top of the pre_effect to abort the previous request.
651
+ var current_abort_controller = async_result.abort_controller;
652
+ abort_controller = current_abort_controller;
653
+
654
+ async_result.promise.then(
655
+ (resolved) => {
656
+ if (current_version !== version) {
657
+ // stale
658
+ return;
659
+ }
660
+ update_tracked_value_clock(t, resolved);
661
+ schedule_update(t.b);
662
+ complete_deferred_boundaries(t);
663
+ if (request_id > 0 && boundary !== null) {
664
+ complete_boundary_request(boundary, request_id);
665
+ request_id = 0;
666
+ }
667
+ },
668
+ (error) => {
669
+ if (current_version !== version) return; // stale
670
+
671
+ var is_internal_abort =
672
+ error === DERIVED_UPDATED || current_abort_controller?.signal?.reason === DERIVED_UPDATED;
673
+ if (is_internal_abort) {
674
+ // Internal abort (superseded by a new request) — don't set rejected
675
+ if (request_id > 0 && boundary !== null) {
676
+ complete_boundary_request(boundary, request_id, false);
677
+ request_id = 0;
678
+ }
679
+ complete_deferred_boundaries(t, false);
680
+ return;
681
+ }
682
+
683
+ update_tracked_value_clock(t, SUSPENSE_REJECTED);
684
+ schedule_update(t.b);
685
+ complete_deferred_boundaries(t, false);
686
+
687
+ // Route error to catch boundary
688
+ var boundary_with_catch = get_boundary_with_catch(call_site_block);
689
+ if (boundary_with_catch !== null) {
690
+ boundary_with_catch.s.c(error);
691
+ }
692
+
693
+ if (request_id > 0 && boundary !== null) {
694
+ var should_show_resolved =
695
+ boundary_with_catch === boundary || boundary === null ? false : true;
696
+ complete_boundary_request(boundary, request_id, should_show_resolved);
697
+ request_id = 0;
698
+ }
699
+ },
700
+ );
701
+
702
+ return () => {
703
+ // Teardown: abort in-flight request when block is destroyed
704
+ if (current_abort_controller !== null && current_abort_controller.signal.aborted === false) {
705
+ current_abort_controller.abort(DERIVED_UPDATED);
706
+ }
707
+ };
708
+ });
709
+
710
+ return t;
711
+ }
712
+
713
+ /**
714
+ * @param {(Derived | Tracked) | (() => any)} tracked
715
+ * @returns {boolean}
716
+ */
717
+ export function is_tracked_pending(tracked) {
718
+ try {
719
+ if (typeof tracked === 'function') {
720
+ tracked();
721
+ } else {
722
+ get(tracked);
723
+ }
724
+ return false;
725
+ } catch (error) {
726
+ if (error === ASYNC_DERIVED_READ_THROWN) {
727
+ return true;
728
+ }
729
+ throw error;
730
+ }
731
+ }
732
+
733
+ /**
734
+ * @param {Tracked | Derived} tracked
735
+ * @return {any}
736
+ */
737
+ export function peek_tracked(tracked) {
738
+ if (!is_ripple_object(tracked)) {
739
+ return tracked;
740
+ }
741
+
742
+ return tracked.__v;
743
+ }
744
+
429
745
  /**
430
746
  * @param {Tracked | Derived} tracked
431
747
  * @returns {Dependency}
@@ -461,7 +777,15 @@ function is_tracking_dirty(tracking) {
461
777
  var tracked = tracking.t;
462
778
 
463
779
  if ((tracked.f & DERIVED) !== 0) {
464
- update_derived(/** @type {Derived} **/ (tracked));
780
+ try {
781
+ update_derived(/** @type {Derived} **/ (tracked));
782
+ } catch (e) {
783
+ if (e === ASYNC_DERIVED_READ_THROWN) {
784
+ // The derived depends on a pending async value — treat as dirty
785
+ return true;
786
+ }
787
+ throw e;
788
+ }
465
789
  }
466
790
 
467
791
  if (tracked.c > tracking.c) {
@@ -490,77 +814,6 @@ export function is_block_dirty(block) {
490
814
  return is_tracking_dirty(block.d);
491
815
  }
492
816
 
493
- /**
494
- * @param {() => Promise<any>} fn
495
- * @param {Block} block
496
- * @returns {Promise<Tracked>}
497
- */
498
- export function async_computed(fn, block) {
499
- /** @type {Block | Derived | null} */
500
- let parent = active_reaction;
501
- var t = tracked(UNINITIALIZED, block);
502
- /** @type {Promise<any>} */
503
- var promise;
504
- /** @type {Map<Tracked, {v: any, c: number}>} */
505
- var new_values = new Map();
506
-
507
- render(
508
- () => {
509
- var [current, deferred] = capture_deferred(() => (promise = fn()));
510
-
511
- var restore = capture();
512
- /** @type {(() => void) | undefined} */
513
- var unsuspend;
514
-
515
- if (deferred === null) {
516
- unsuspend = suspend();
517
- } else {
518
- for (var i = 0; i < deferred.length; i++) {
519
- var tracked = deferred[i];
520
- new_values.set(tracked, { v: tracked.__v, c: tracked.c });
521
- }
522
- }
523
-
524
- promise.then((v) => {
525
- if (parent && is_destroyed(/** @type {Block} */ (parent))) {
526
- return;
527
- }
528
- if (promise === current && t.__v !== v) {
529
- restore();
530
-
531
- if (t.__v === UNINITIALIZED) {
532
- t.__v = v;
533
- } else {
534
- set(t, v);
535
- }
536
- }
537
-
538
- if (deferred === null) {
539
- unsuspend?.();
540
- } else if (promise === current) {
541
- for (var i = 0; i < deferred.length; i++) {
542
- var tracked = deferred[i];
543
- var stored = /** @type {{ v: any, c: number }} */ (new_values.get(tracked));
544
- var { v, c } = stored;
545
- tracked.__v = v;
546
- tracked.c = c;
547
- schedule_update(tracked.b);
548
- }
549
- new_values.clear();
550
- }
551
- });
552
- },
553
- null,
554
- ASYNC_BLOCK,
555
- );
556
-
557
- return new Promise(async (resolve) => {
558
- var p;
559
- while (p !== (p = promise)) await p;
560
- return resolve(t);
561
- });
562
- }
563
-
564
817
  /**
565
818
  * @template V
566
819
  * @param {Function} fn
@@ -576,29 +829,6 @@ function trigger_track_get(fn, v) {
576
829
  }
577
830
  }
578
831
 
579
- /**
580
- * @param {() => any} fn
581
- * @returns {[any, Tracked[] | null]}
582
- */
583
- function capture_deferred(fn) {
584
- var value = fn();
585
- /** @type {Tracked[] | null} */
586
- var deferred = null;
587
- var dependency = active_dependency;
588
-
589
- while (dependency !== null) {
590
- var tracked = dependency.t;
591
- if ((tracked.f & DEFERRED) !== 0) {
592
- deferred ??= [];
593
- deferred.push(tracked);
594
- break;
595
- }
596
- dependency = dependency.n;
597
- }
598
-
599
- return [value, deferred];
600
- }
601
-
602
832
  /**
603
833
  * @param {Block} root_block
604
834
  */
@@ -606,6 +836,8 @@ function flush_updates(root_block) {
606
836
  /** @type {Block | null} */
607
837
  var current = root_block;
608
838
  var containing_update = null;
839
+ var pre_effects = [];
840
+ var other_blocks = [];
609
841
  var effects = [];
610
842
  var containing_update_head = null;
611
843
 
@@ -619,16 +851,12 @@ function flush_updates(root_block) {
619
851
  }
620
852
 
621
853
  if ((flags & PAUSED) === 0 && containing_update !== null) {
622
- if ((flags & EFFECT_BLOCK) !== 0) {
854
+ if ((flags & PRE_EFFECT_BLOCK) !== 0) {
855
+ pre_effects.push(current);
856
+ } else if ((flags & EFFECT_BLOCK) !== 0) {
623
857
  effects.push(current);
624
858
  } else {
625
- try {
626
- if (is_block_dirty(current)) {
627
- run_block(current);
628
- }
629
- } catch (error) {
630
- handle_error(error, current);
631
- }
859
+ other_blocks.push(current);
632
860
  }
633
861
  /** @type {Block | null} */
634
862
  var child = current.first;
@@ -654,18 +882,47 @@ function flush_updates(root_block) {
654
882
  }
655
883
  }
656
884
 
657
- var length = effects.length;
885
+ var arr_length = 0;
886
+
887
+ // Phase 1: pre-effects (e.g. update tracked values before render blocks read them)
888
+ arr_length = pre_effects.length;
889
+ for (var i = 0; i < arr_length; i++) {
890
+ var block = pre_effects[i];
891
+
892
+ try {
893
+ if ((block.f & (PAUSED | DESTROYED)) === 0 && is_block_dirty(block)) {
894
+ run_block(block);
895
+ }
896
+ } catch (error) {
897
+ handle_error(error, block);
898
+ }
899
+ }
900
+
901
+ // Phase 2: all other blocks except effects
902
+ arr_length = other_blocks.length;
903
+ for (var i = 0; i < arr_length; i++) {
904
+ var block = other_blocks[i];
905
+
906
+ try {
907
+ if ((block.f & (PAUSED | DESTROYED)) === 0 && is_block_dirty(block)) {
908
+ run_block(block);
909
+ }
910
+ } catch (error) {
911
+ handle_error(error, block);
912
+ }
913
+ }
658
914
 
659
- for (var i = 0; i < length; i++) {
660
- var effect = effects[i];
661
- var flags = effect.f;
915
+ // Phase 3: effects
916
+ arr_length = effects.length;
917
+ for (var i = 0; i < arr_length; i++) {
918
+ var block = effects[i];
662
919
 
663
920
  try {
664
- if ((flags & (PAUSED | DESTROYED)) === 0 && is_block_dirty(effect)) {
665
- run_block(effect);
921
+ if ((block.f & (PAUSED | DESTROYED)) === 0 && is_block_dirty(block)) {
922
+ run_block(block);
666
923
  }
667
924
  } catch (error) {
668
- handle_error(error, effect);
925
+ handle_error(error, block);
669
926
  }
670
927
  }
671
928
  }
@@ -677,6 +934,14 @@ function flush_queued_root_blocks(root_blocks) {
677
934
  for (let i = 0; i < root_blocks.length; i++) {
678
935
  flush_updates(root_blocks[i]);
679
936
  }
937
+
938
+ if (queued_post_block_flush.length > 0) {
939
+ var callbacks = queued_post_block_flush;
940
+ queued_post_block_flush = [];
941
+ for (var j = 0; j < callbacks.length; j++) {
942
+ callbacks[j]();
943
+ }
944
+ }
680
945
  }
681
946
 
682
947
  /**
@@ -702,8 +967,9 @@ function flush_microtasks() {
702
967
 
703
968
  flush_count++;
704
969
  if (flush_count > 1001) {
705
- flush_count = 0;
706
- return;
970
+ throw new Error(
971
+ 'Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state.',
972
+ );
707
973
  }
708
974
  var previous_queued_root_blocks = queued_root_blocks;
709
975
  queued_root_blocks = [];
@@ -728,6 +994,16 @@ export function queue_microtask(fn) {
728
994
  }
729
995
  }
730
996
 
997
+ /**
998
+ * Queue a callback to run after all root blocks are flushed.
999
+ * Used to defer boundary completions so chained async deriveds evaluated during
1000
+ * the flush can start new requests before the boundary transitions out of pending.
1001
+ * @param {() => void} fn
1002
+ */
1003
+ export function queue_post_block_flush_callback(fn) {
1004
+ queued_post_block_flush.push(fn);
1005
+ }
1006
+
731
1007
  /**
732
1008
  * @param {Block} block
733
1009
  */
@@ -751,7 +1027,7 @@ export function schedule_update(block) {
751
1027
  }
752
1028
 
753
1029
  /**
754
- * @param {Tracked} tracked
1030
+ * @param {Tracked | Derived} tracked
755
1031
  */
756
1032
  function register_dependency(tracked) {
757
1033
  var dependency = active_dependency;
@@ -787,12 +1063,18 @@ export function get_derived(computed) {
787
1063
  if (tracking) {
788
1064
  register_dependency(computed);
789
1065
  }
1066
+ var value = computed.__v;
790
1067
  var get = computed.a.get;
791
1068
  if (get !== undefined) {
792
- computed.__v = trigger_track_get(get, computed.__v);
1069
+ value = trigger_track_get(get, value);
1070
+ computed.__v = value;
793
1071
  }
794
1072
 
795
- return computed.__v;
1073
+ if (value === SUSPENSE_PENDING || value === SUSPENSE_REJECTED) {
1074
+ throw ASYNC_DERIVED_READ_THROWN;
1075
+ }
1076
+
1077
+ return value;
796
1078
  }
797
1079
 
798
1080
  /**
@@ -806,7 +1088,7 @@ export function get(tracked) {
806
1088
 
807
1089
  return (tracked.f & DERIVED) !== 0
808
1090
  ? get_derived(/** @type {Derived} */ (tracked))
809
- : get_tracked(tracked);
1091
+ : get_tracked(/** @type {Tracked} */ (tracked));
810
1092
  }
811
1093
 
812
1094
  /**
@@ -817,6 +1099,11 @@ export function get_tracked(tracked) {
817
1099
  if (tracking) {
818
1100
  register_dependency(tracked);
819
1101
  }
1102
+
1103
+ if (value === SUSPENSE_PENDING || value === SUSPENSE_REJECTED) {
1104
+ throw ASYNC_DERIVED_READ_THROWN;
1105
+ }
1106
+
820
1107
  if (teardown && old_values.has(tracked)) {
821
1108
  value = old_values.get(tracked);
822
1109
  }
@@ -1203,6 +1490,7 @@ export function safe_scope(err = 'Cannot access outside of a component context')
1203
1490
 
1204
1491
  export function create_component_ctx() {
1205
1492
  return {
1493
+ b: active_block,
1206
1494
  c: null,
1207
1495
  e: null,
1208
1496
  m: false,
@@ -1304,29 +1592,3 @@ export function exclude_from_object(obj, exclude_keys) {
1304
1592
 
1305
1593
  return new_obj;
1306
1594
  }
1307
-
1308
- /**
1309
- * @param {any} v
1310
- * @returns {Promise<() => any>}
1311
- */
1312
- export async function maybe_tracked(v) {
1313
- var restore = capture();
1314
- let value;
1315
-
1316
- if (is_ripple_object(v)) {
1317
- if ((v.f & DERIVED) !== 0) {
1318
- value = await async_computed(v.fn, v.b);
1319
- } else {
1320
- value = await async_computed(async () => {
1321
- return await get_tracked(v);
1322
- }, /** @type {Block} */ (active_block));
1323
- }
1324
- } else {
1325
- value = await v;
1326
- }
1327
-
1328
- return () => {
1329
- restore();
1330
- return value;
1331
- };
1332
- }