ripple 0.3.12 → 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 +36 -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 +2 -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,7 +1,7 @@
1
- /** @import { Block } from '#client' */
1
+ /** @import { Block, TryBoundaryState, BlockWithTryBoundary, BlockWithTryBoundaryAndCatch } from '#client' */
2
2
 
3
3
  import {
4
- branch,
4
+ boundary_fn_running_block,
5
5
  create_try_block,
6
6
  destroy_block,
7
7
  is_destroyed,
@@ -20,67 +20,158 @@ import {
20
20
  import { get_next_sibling } from './operations.js';
21
21
  import {
22
22
  active_block,
23
- active_component,
24
- active_reaction,
25
23
  queue_microtask,
26
- set_active_block,
27
- set_active_component,
28
- set_active_reaction,
29
- set_tracking,
30
- tracking,
24
+ queue_post_block_flush_callback,
25
+ with_block,
31
26
  } from './runtime.js';
32
27
 
28
+ /**
29
+ @typedef {(
30
+ anchor: Node,
31
+ error: any,
32
+ reset?: () => void
33
+ ) => void} CatchFunction;
34
+
35
+ @typedef {(anchor: Node) => void} PendingFunction
36
+ */
37
+
33
38
  /**
34
39
  * @param {Node} node
35
- * @param {(anchor: Node) => void} fn
36
- * @param {((anchor: Node, error: any) => void) | null} catch_fn
37
- * @param {((anchor: Node) => void) | null} [pending_fn=null]
40
+ * @param {(anchor: Node, block?: Block) => void} try_fn
41
+ * @param {CatchFunction | null} catch_fn
42
+ * @param {PendingFunction | null} [pending_fn=null]
38
43
  * @returns {void}
39
44
  */
40
- export function try_block(node, fn, catch_fn, pending_fn = null) {
45
+ export function try_block(node, try_fn, catch_fn, pending_fn = null) {
41
46
  var anchor = node;
47
+ var pending_count = 0;
48
+ var request_version = 0;
49
+ /** @type {Set<number>} */
50
+ var active_requests = new Set();
42
51
  /** @type {Block | null} */
43
- var b = null;
52
+ var try_block = null;
44
53
  /** @type {Block | null} */
45
- var suspended = null;
46
- var pending_count = 0;
54
+ var resolved_branch = null;
55
+ /** @type {Block | null} */
56
+ var pending_branch = null;
57
+ /** @type {Block | null} */
58
+ var catch_branch = null;
47
59
  /** @type {DocumentFragment | null} */
48
60
  var offscreen_fragment = null;
49
61
  var has_resolved = false;
62
+ /** @type {'resolved' | 'pending' | 'catch'} */
63
+ var mode = 'resolved';
64
+ /** @type {Map<number, (reason: any) => void>} */
65
+ var pending_deferreds = new Map();
66
+ /** @type {Set<Block>} */
67
+ var paused_blocks = new Set();
50
68
 
51
- function handle_await() {
52
- if (pending_count++ === 0) {
53
- queue_microtask(() => {
54
- if (b !== null && suspended === null) {
55
- suspended = b;
56
- offscreen_fragment = document.createDocumentFragment();
57
- // Only move content if promise has resolved before (re-suspension)
58
- if (has_resolved) {
59
- move_block(b, offscreen_fragment);
60
- }
61
-
62
- b = branch(() => {
63
- /** @type {(anchor: Node) => void} */ (pending_fn)(anchor);
64
- });
65
- }
66
- });
69
+ function clear_paused_blocks() {
70
+ paused_blocks.clear();
71
+ }
72
+
73
+ /**
74
+ * @returns {boolean}
75
+ */
76
+ function resume_paused_blocks() {
77
+ if (paused_blocks.size === 0) {
78
+ return false;
67
79
  }
68
80
 
69
- return () => {
70
- if (--pending_count === 0) {
71
- has_resolved = true;
72
- if (b !== null) {
73
- destroy_block(b);
74
- }
75
- /** @type {ChildNode} */ (anchor).before(
76
- /** @type {DocumentFragment} */ (offscreen_fragment),
77
- );
78
- offscreen_fragment = null;
79
- resume_block(/** @type {Block} */ (suspended));
80
- b = suspended;
81
- suspended = null;
81
+ var blocks = paused_blocks;
82
+ paused_blocks = new Set();
83
+ var resumed = false;
84
+
85
+ for (var block of blocks) {
86
+ if (!is_destroyed(block)) {
87
+ resume_block(block);
88
+ resumed = true;
89
+ }
90
+ }
91
+
92
+ return resumed;
93
+ }
94
+
95
+ function show_resolved_fragment() {
96
+ if (offscreen_fragment !== null) {
97
+ /** @type {ChildNode} */ (anchor).before(offscreen_fragment);
98
+ offscreen_fragment = null;
99
+ }
100
+
101
+ has_resolved = true;
102
+ mode = 'resolved';
103
+ }
104
+
105
+ function render_resolved() {
106
+ if (
107
+ try_block !== null &&
108
+ !is_destroyed(try_block) &&
109
+ (resolved_branch === null || is_destroyed(resolved_branch))
110
+ ) {
111
+ if (catch_branch !== null) {
112
+ destroy_block(catch_branch);
113
+ catch_branch = null;
82
114
  }
115
+ mode = 'resolved';
116
+ if (active_block !== try_block) {
117
+ with_block(try_block, () => {
118
+ resolved_branch = boundary_fn_running_block(() => try_fn(anchor));
119
+ });
120
+ } else {
121
+ resolved_branch = boundary_fn_running_block(() => try_fn(anchor));
122
+ }
123
+ }
124
+ }
125
+
126
+ function destroy_resolved() {
127
+ if (resolved_branch !== null && !is_destroyed(resolved_branch)) {
128
+ destroy_block(resolved_branch);
129
+ }
130
+ resolved_branch = null;
131
+ offscreen_fragment = null;
132
+ }
133
+
134
+ function move_resolved_offscreen() {
135
+ if (resolved_branch !== null) {
136
+ if (!offscreen_fragment) {
137
+ // if offcreen_fragment exists, it means the resolved_branch is already offscreen,
138
+ // so we can skip moving it again
139
+ offscreen_fragment = document.createDocumentFragment();
140
+ move_block(resolved_branch, offscreen_fragment);
141
+ }
142
+ }
143
+ }
144
+
145
+ function render_pending() {
146
+ if (pending_fn === null || mode === 'pending') {
147
+ return;
148
+ }
149
+
150
+ move_resolved_offscreen();
151
+
152
+ mode = 'pending';
153
+
154
+ var create_pending = () => {
155
+ pending_branch = boundary_fn_running_block(() => {
156
+ /** @type {PendingFunction} */ (pending_fn)(anchor);
157
+ });
83
158
  };
159
+
160
+ // with_block ensures the branch is parented under the TRY_BLOCK when called
161
+ // from async contexts (microtasks) where active_block is null. During synchronous
162
+ // execution (try_block not yet assigned), active_block is already the TRY_BLOCK.
163
+ if (try_block !== null && !is_destroyed(try_block) && active_block !== try_block) {
164
+ with_block(try_block, create_pending);
165
+ } else {
166
+ create_pending();
167
+ }
168
+ }
169
+
170
+ function destroy_pending() {
171
+ if (pending_branch !== null && !is_destroyed(pending_branch)) {
172
+ destroy_block(pending_branch);
173
+ }
174
+ pending_branch = null;
84
175
  }
85
176
 
86
177
  /**
@@ -88,157 +179,244 @@ export function try_block(node, fn, catch_fn, pending_fn = null) {
88
179
  * @returns {void}
89
180
  */
90
181
  function handle_error(error) {
91
- if (suspended !== null) {
92
- destroy_block(suspended);
93
- suspended = null;
94
- offscreen_fragment = null;
95
- pending_count = 0;
182
+ if (mode === 'catch') {
183
+ // we don't want to do this again and render catch block again
184
+ return;
185
+ }
186
+ pending_count = 0;
187
+ active_requests.clear();
188
+ clear_paused_blocks();
189
+
190
+ // Reject all pending deferred promises so dependent async tracked settle
191
+ // handlers fire and clean up. The settle will see the request already
192
+ // cleared and skip error routing, avoiding double-catch.
193
+ if (pending_deferreds.size > 0) {
194
+ for (var [, reject_fn] of pending_deferreds) {
195
+ reject_fn(error);
196
+ }
197
+ pending_deferreds.clear();
198
+ }
199
+
200
+ if (mode === 'pending') {
201
+ destroy_pending();
202
+ } else if (mode === 'resolved') {
203
+ move_resolved_offscreen();
204
+ }
205
+
206
+ mode = 'catch';
207
+
208
+ var create_catch = () => {
209
+ catch_branch = boundary_fn_running_block(() => {
210
+ /** @type {CatchFunction} */ (catch_fn)(anchor, error, render_resolved);
211
+ });
212
+ };
213
+
214
+ // with_block ensures the branch is parented under the TRY_BLOCK when called
215
+ // from async contexts where active_block is null. During synchronous
216
+ // execution (try_block not yet assigned), active_block is already the TRY_BLOCK.
217
+ if (try_block !== null && !is_destroyed(try_block) && active_block !== try_block) {
218
+ with_block(try_block, create_catch);
219
+ } else {
220
+ create_catch();
221
+ }
222
+
223
+ destroy_resolved();
224
+ }
225
+
226
+ function begin_request() {
227
+ var request_id = ++request_version;
228
+ active_requests.add(request_id);
229
+
230
+ if (pending_count++ === 0 && pending_fn !== null && !has_resolved) {
231
+ queue_microtask(() => {
232
+ if (try_block !== null && !is_destroyed(try_block) && pending_count > 0 && !has_resolved) {
233
+ render_pending();
234
+ }
235
+ });
236
+ }
237
+
238
+ return request_id;
239
+ }
240
+
241
+ /**
242
+ * @param {number} old_request_id
243
+ * @returns {number}
244
+ */
245
+ function replace_request(old_request_id) {
246
+ active_requests.delete(old_request_id);
247
+ pending_deferreds.delete(old_request_id);
248
+ // pending_count unchanged — one out, one in
249
+ var request_id = ++request_version;
250
+ active_requests.add(request_id);
251
+ return request_id;
252
+ }
253
+
254
+ /**
255
+ * @param {number} request_id
256
+ * @param {boolean} [show_resolved_branch=true]
257
+ * @returns {boolean}
258
+ */
259
+ function complete_request(request_id, show_resolved_branch = true) {
260
+ if (!active_requests.delete(request_id)) {
261
+ return false;
96
262
  }
97
263
 
98
- if (b !== null) {
99
- destroy_block(b);
264
+ pending_deferreds.delete(request_id);
265
+
266
+ pending_count--;
267
+
268
+ if (pending_count === 0) {
269
+ if (!show_resolved_branch) {
270
+ clear_paused_blocks();
271
+ return true;
272
+ }
273
+
274
+ resume_paused_blocks();
275
+
276
+ queue_post_block_flush_callback(() => {
277
+ // run this only after the blocks have a chance to run
278
+ // and find more pending requests (and pause themselves) before we are
279
+ // certain to render the resolved state.
280
+ // Otherwise, we'll have multiple renders.
281
+ if (try_block === null || is_destroyed(try_block) || pending_count > 0) {
282
+ return;
283
+ }
284
+
285
+ if (mode === 'pending') {
286
+ destroy_pending();
287
+ show_resolved_fragment();
288
+ }
289
+
290
+ has_resolved = true;
291
+ mode = 'resolved';
292
+ });
293
+ // this is more just in case here and shouldn't really cause anything to run
294
+ // most likely the scheduling is already there
295
+ // leaving it here in case there are some weird edge cases
296
+ queue_microtask();
100
297
  }
101
298
 
102
- b = branch(() => {
103
- /** @type {(anchor: Node, error: any) => void} */ (catch_fn)(anchor, error);
104
- });
299
+ return true;
105
300
  }
106
301
 
302
+ /** @type {TryBoundaryState} */
107
303
  var state = {
108
- a: pending_fn !== null ? handle_await : null,
304
+ p: pending_fn !== null,
305
+ b: begin_request,
306
+ r: complete_request,
109
307
  c: catch_fn !== null ? handle_error : null,
308
+ /** @param {number} request_id @param {(reason: any) => void} reject_fn */
309
+ rd: (request_id, reject_fn) => {
310
+ pending_deferreds.set(request_id, reject_fn);
311
+ },
312
+ /** @param {Block} block */
313
+ pb: (block) => {
314
+ paused_blocks.add(block);
315
+ },
316
+ rp: replace_request,
110
317
  };
111
318
 
112
- if (hydrating && pending_fn !== null) {
113
- // SSR emits <!--[-->_try <pending_html> <resolved_html> <!--]-->_try
114
- // Advance past the opening marker, discard SSR content, and recreate fresh
115
- // client-side DOM in non-hydrating mode. The `_$_.async` wrapper in blocks.js
116
- // adds an extra `await Promise.resolve()` before calling unsuspend(), which
117
- // ensures the pending UI created by handle_await's microtask is observable for
118
- // at least one event-loop tick before the resolved content replaces it.
119
- hydrate_next(); // consume <!--[-->_try
120
- var end = skip_to_hydration_end(); // find matching <!--]-->_try
121
- // Remove SSR pending+resolved nodes that sit between the two markers
122
- var n = hydrate_node;
123
- while (n !== null && n !== end) {
124
- var next_n = get_next_sibling(n);
125
- if (n.parentNode) n.parentNode.removeChild(n);
126
- n = next_n;
127
- }
128
- set_hydrate_node(end); // position cursor at <!--]-->_try
129
- set_hydrating(false);
130
-
131
- // Save a reference to the nearest ancestor branch-block so we can update its
132
- // DOM-range tracking (s.start) to cover the fresh client-side nodes we are
133
- // about to insert. Without this, destroy_block on the parent would try to
134
- // remove the already-removed SSR node and miss the new content entirely.
135
- var hydration_parent = active_block;
136
- // Remember what was before anchor so we can find the first new node afterward.
137
- var prev_sibling_before = anchor.previousSibling;
138
-
139
- create_try_block(() => {
140
- b = branch(() => {
141
- fn(anchor);
142
- });
143
- }, state);
144
-
145
- // fn(anchor) inserted new DOM immediately before `anchor`.
146
- // Find the first of those newly inserted nodes and update the parent block's
147
- // s.start so that destroy_block can later remove both the hydration markers
148
- // (<!--[-->/<!--]-->) and the fresh content in one range sweep.
149
- var new_first =
150
- prev_sibling_before !== null
151
- ? get_next_sibling(prev_sibling_before)
152
- : anchor.parentNode
153
- ? anchor.parentNode.firstChild
154
- : null;
155
- if (
156
- new_first !== null &&
157
- new_first !== anchor &&
158
- hydration_parent !== null &&
159
- hydration_parent.s !== null
160
- ) {
161
- hydration_parent.s.start = new_first;
319
+ if (hydrating && (pending_fn !== null || catch_fn !== null)) {
320
+ // Server wraps try_fn body with <!--[-->...<!--]--> markers when pending or catch is present.
321
+ // Server resolves all async content fully (pending is only for future streaming SSR),
322
+ // so the SSR HTML contains resolved content.
323
+ // Mark as already resolved so begin_request's microtask won't transition to pending.
324
+ if (pending_fn !== null) {
325
+ has_resolved = true;
162
326
  }
163
-
164
- set_hydrating(true);
165
- return;
327
+ hydrate_next(); // consume <!--[-->
166
328
  }
167
329
 
168
- create_try_block(() => {
169
- b = branch(() => {
170
- fn(anchor);
171
- });
330
+ try_block = create_try_block(() => {
331
+ resolved_branch = boundary_fn_running_block(() => try_fn(anchor));
172
332
  }, state);
173
333
  }
174
334
 
175
335
  /**
176
- * @returns {() => void}
336
+ * @param {Block | null} block
337
+ * @returns {BlockWithTryBoundary | null}
177
338
  */
178
- export function suspend() {
179
- var current = active_block;
339
+ export function get_pending_boundary(block) {
340
+ var current = block;
180
341
 
181
342
  while (current !== null) {
182
- var state = current.s;
183
- if ((current.f & TRY_BLOCK) !== 0 && state.a !== null) {
184
- return state.a();
343
+ var state = /** @type {BlockWithTryBoundary} */ (current).s;
344
+ if ((current.f & TRY_BLOCK) !== 0 && state.p) {
345
+ return /** @type {BlockWithTryBoundary} */ (current);
185
346
  }
186
347
  current = current.p;
187
348
  }
188
349
 
189
- throw new Error('Missing parent `try { ... } pending { ... }` statement');
350
+ return null;
190
351
  }
191
352
 
192
353
  /**
193
- * @returns {void}
354
+ * @param {Block} block
355
+ * @returns {BlockWithTryBoundaryAndCatch | null}
356
+ */
357
+ export function get_boundary_with_catch(block) {
358
+ /** @type {Block | null} */
359
+ var current = block;
360
+
361
+ while (current !== null) {
362
+ var state = /** @type {BlockWithTryBoundary} */ (current).s;
363
+ if ((current.f & TRY_BLOCK) !== 0 && state.c !== null) {
364
+ return /** @type {BlockWithTryBoundaryAndCatch} */ (current);
365
+ }
366
+ current = current.p;
367
+ }
368
+
369
+ return null;
370
+ }
371
+
372
+ /**
373
+ * @param {BlockWithTryBoundary} boundary
374
+ * @returns {number}
194
375
  */
195
- function exit() {
196
- set_tracking(false);
197
- set_active_reaction(null);
198
- set_active_block(null);
199
- set_active_component(null);
376
+ export function begin_boundary_request(boundary) {
377
+ return boundary.s.b();
200
378
  }
201
379
 
202
380
  /**
203
- * @returns {() => void}
381
+ * @param {BlockWithTryBoundary} boundary
382
+ * @param {number} old_request_id
383
+ * @returns {number}
204
384
  */
205
- export function capture() {
206
- var previous_tracking = tracking;
207
- var previous_block = active_block;
208
- var previous_reaction = active_reaction;
209
- var previous_component = active_component;
210
-
211
- return () => {
212
- set_tracking(previous_tracking);
213
- set_active_block(previous_block);
214
- set_active_reaction(previous_reaction);
215
- set_active_component(previous_component);
216
-
217
- queue_microtask(exit);
218
- };
385
+ export function replace_boundary_request(boundary, old_request_id) {
386
+ return boundary.s.rp(old_request_id);
219
387
  }
220
388
 
221
389
  /**
390
+ * @param {BlockWithTryBoundary | null} boundary
391
+ * @param {number} request_id
392
+ * @param {boolean} [show_resolved_branch=true]
222
393
  * @returns {boolean}
223
394
  */
224
- export function aborted() {
225
- if (active_block === null) {
226
- return true;
227
- }
228
- return is_destroyed(active_block);
395
+ export function complete_boundary_request(boundary, request_id, show_resolved_branch = true) {
396
+ return boundary !== null && !is_destroyed(boundary)
397
+ ? boundary.s.r(request_id, show_resolved_branch)
398
+ : false;
229
399
  }
230
400
 
231
401
  /**
232
- * @template T
233
- * @param {Promise<T>} promise
234
- * @returns {Promise<() => T>}
402
+ * @param {BlockWithTryBoundary | null} boundary
403
+ * @param {number} request_id
404
+ * @param {(reason: any) => void} reject_fn
405
+ * @returns {void}
235
406
  */
236
- export async function resume_context(promise) {
237
- var restore = capture();
238
- var value = await promise;
407
+ export function register_boundary_deferred(boundary, request_id, reject_fn) {
408
+ if (boundary !== null && !is_destroyed(boundary) && boundary.s?.rd) {
409
+ boundary.s.rd(request_id, reject_fn);
410
+ }
411
+ }
239
412
 
240
- return () => {
241
- restore();
242
- return value;
243
- };
413
+ /**
414
+ * @param {BlockWithTryBoundary | null} boundary
415
+ * @param {Block} block
416
+ * @returns {void}
417
+ */
418
+ export function register_boundary_paused_block(boundary, block) {
419
+ if (boundary !== null && !is_destroyed(boundary) && boundary.s?.pb) {
420
+ boundary.s.pb(block);
421
+ }
244
422
  }
@@ -1,6 +1,7 @@
1
1
  import type { Context } from './context.js';
2
2
 
3
3
  export type Component = {
4
+ b: null | Block;
4
5
  c: null | Map<Context<any>, any>;
5
6
  e: null | Array<{
6
7
  b: Block;
@@ -22,6 +23,7 @@ export type Tracked<V = any> = {
22
23
  a: { get?: Function; set?: Function };
23
24
  b: Block;
24
25
  c: number;
26
+ d: null | DeferredTrackedEntry[];
25
27
  f: number;
26
28
  __v: V;
27
29
  readonly [0]: V;
@@ -31,6 +33,11 @@ export type Tracked<V = any> = {
31
33
  [Symbol.iterator](): Iterator<V | Tracked<V>>;
32
34
  };
33
35
 
36
+ export type DeferredTrackedEntry = {
37
+ b: Block; // boundary block
38
+ r: number; // request version id
39
+ };
40
+
34
41
  export type Derived = {
35
42
  DO_NOT_ACCESS_THIS_OBJECT_DIRECTLY?: true;
36
43
  a: { get?: Function; set?: Function };
@@ -64,6 +71,24 @@ export type Block = {
64
71
  t: (() => {}) | null;
65
72
  };
66
73
 
74
+ export type TryBoundaryState = {
75
+ p: boolean; // whether pending_fn exists
76
+ b: () => number; // begin request, returns request id
77
+ r: (request_id: number, show_resolved_branch?: boolean) => boolean; // complete request, returns whether the request was active
78
+ c: ((error: any) => void) | null; // catch function
79
+ rd: (request_id: number, reject_fn: (reason: any) => void) => void; // register deferred reject function
80
+ pb: (block: Block) => void; // register paused block
81
+ rp: (old_request_id: number) => number; // replace request, returns new request id
82
+ };
83
+
84
+ export type BlockWithTryBoundary = Omit<Block, 's'> & {
85
+ s: TryBoundaryState;
86
+ };
87
+
88
+ export type BlockWithTryBoundaryAndCatch = Omit<BlockWithTryBoundary, 's'> & {
89
+ s: TryBoundaryState & { c: NonNullable<TryBoundaryState['c']> };
90
+ };
91
+
67
92
  export type CompatApi = {
68
93
  createRoot: () => void;
69
94
  createComponent: (node: any, children_fn: () => any) => void;
@@ -81,6 +106,7 @@ declare global {
81
106
  value?: string;
82
107
  };
83
108
  __click?: () => void;
109
+ __ripple_block?: Block;
84
110
  }
85
111
 
86
112
  interface Event {