ripple 0.3.128 → 0.4.0

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 (192) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/package.json +10 -4
  3. package/src/runtime/element.js +23 -6
  4. package/src/runtime/index-client.js +81 -41
  5. package/src/runtime/index-server.js +1 -0
  6. package/src/runtime/internal/client/blocks.js +173 -91
  7. package/src/runtime/internal/client/component.js +4 -30
  8. package/src/runtime/internal/client/composite.js +12 -13
  9. package/src/runtime/internal/client/constants.js +9 -0
  10. package/src/runtime/internal/client/context.js +12 -19
  11. package/src/runtime/internal/client/css.js +7 -9
  12. package/src/runtime/internal/client/events.js +178 -90
  13. package/src/runtime/internal/client/expression.js +45 -25
  14. package/src/runtime/internal/client/for.js +409 -129
  15. package/src/runtime/internal/client/head.js +10 -5
  16. package/src/runtime/internal/client/html.js +1 -0
  17. package/src/runtime/internal/client/hydration.js +152 -10
  18. package/src/runtime/internal/client/if.js +239 -50
  19. package/src/runtime/internal/client/index.js +4 -12
  20. package/src/runtime/internal/client/operations.js +40 -61
  21. package/src/runtime/internal/client/portal.js +163 -85
  22. package/src/runtime/internal/client/render.js +39 -39
  23. package/src/runtime/internal/client/rpc.js +3 -3
  24. package/src/runtime/internal/client/runtime.js +592 -433
  25. package/src/runtime/internal/client/selector.js +5 -2
  26. package/src/runtime/internal/client/template.js +133 -158
  27. package/src/runtime/internal/client/transport.js +38 -0
  28. package/src/runtime/internal/client/try.js +465 -426
  29. package/src/runtime/internal/client/types.d.ts +56 -23
  30. package/src/runtime/internal/client/utils.js +1 -3
  31. package/src/runtime/internal/server/dynamic.js +20 -21
  32. package/src/runtime/internal/server/index.js +325 -275
  33. package/src/runtime/internal/server/rpc.js +3 -2
  34. package/src/runtime/internal/server/transport.js +16 -0
  35. package/src/runtime/transport.js +21 -0
  36. package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
  37. package/tests/client/array/array.copy-within.test.tsrx +12 -12
  38. package/tests/client/array/array.derived.test.tsrx +46 -46
  39. package/tests/client/array/array.iteration.test.tsrx +10 -10
  40. package/tests/client/array/array.mutations.test.tsrx +20 -20
  41. package/tests/client/array/array.static.test.tsrx +11 -11
  42. package/tests/client/array/array.to-methods.test.tsrx +6 -6
  43. package/tests/client/async-suspend.test.tsrx +74 -75
  44. package/tests/client/basic/basic.attributes.test.tsrx +86 -83
  45. package/tests/client/basic/basic.collections.test.tsrx +12 -12
  46. package/tests/client/basic/basic.components.test.tsrx +99 -25
  47. package/tests/client/basic/basic.errors.test.tsrx +18 -18
  48. package/tests/client/basic/basic.events.test.tsrx +42 -42
  49. package/tests/client/basic/basic.get-set.test.tsrx +8 -8
  50. package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
  51. package/tests/client/basic/basic.rendering.test.tsrx +25 -25
  52. package/tests/client/basic/basic.utilities.test.tsrx +3 -3
  53. package/tests/client/boundaries.test.tsrx +8 -8
  54. package/tests/client/boxed-locals.test.tsrx +452 -0
  55. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
  56. package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
  57. package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
  58. package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
  59. package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
  60. package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
  61. package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
  62. package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
  63. package/tests/client/composite/composite.props.test.tsrx +149 -12
  64. package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
  65. package/tests/client/composite/composite.render.test.tsrx +26 -9
  66. package/tests/client/css/scoped-styles.test.tsrx +8 -8
  67. package/tests/client/date.test.tsrx +42 -42
  68. package/tests/client/derived-release.test.tsrx +370 -0
  69. package/tests/client/dynamic-elements.test.tsrx +45 -45
  70. package/tests/client/events.test.tsrx +97 -71
  71. package/tests/client/for-selector.test.tsrx +14 -14
  72. package/tests/client/for-single-root-items.test.tsrx +118 -3
  73. package/tests/client/for.test.tsrx +332 -31
  74. package/tests/client/head.test.tsrx +19 -19
  75. package/tests/client/html.test.tsrx +11 -9
  76. package/tests/client/if-block.test.tsrx +80 -0
  77. package/tests/client/map.test.tsrx +16 -16
  78. package/tests/client/media-query.test.tsrx +7 -7
  79. package/tests/client/portal.test.tsrx +224 -26
  80. package/tests/client/primitive-text.test.tsrx +50 -0
  81. package/tests/client/prop-kind-switch.test.tsrx +141 -0
  82. package/tests/client/ref.test.tsrx +17 -17
  83. package/tests/client/return.test.tsrx +26 -3
  84. package/tests/client/root-boundary.test.tsrx +42 -0
  85. package/tests/client/scoped-flush.test.tsrx +8 -8
  86. package/tests/client/set.test.tsrx +6 -6
  87. package/tests/client/svg.test.tsrx +11 -11
  88. package/tests/client/switch.test.tsrx +44 -44
  89. package/tests/client/track-async-hydration.test.tsrx +4 -4
  90. package/tests/client/try.test.tsrx +151 -83
  91. package/tests/client/tsx.test.tsrx +43 -40
  92. package/tests/client/url/url.derived.test.tsrx +6 -6
  93. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
  94. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
  95. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
  96. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
  97. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
  98. package/tests/hydration/basic.test.js +74 -1
  99. package/tests/hydration/build-components.js +22 -1
  100. package/tests/hydration/compiled/client/basic.js +1142 -713
  101. package/tests/hydration/compiled/client/composite.js +151 -100
  102. package/tests/hydration/compiled/client/events.js +184 -171
  103. package/tests/hydration/compiled/client/for.js +1472 -1132
  104. package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
  105. package/tests/hydration/compiled/client/head.js +254 -201
  106. package/tests/hydration/compiled/client/hmr.js +54 -41
  107. package/tests/hydration/compiled/client/html-in-template.js +54 -39
  108. package/tests/hydration/compiled/client/html.js +1633 -1263
  109. package/tests/hydration/compiled/client/if-children.js +420 -270
  110. package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
  111. package/tests/hydration/compiled/client/if.js +229 -219
  112. package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
  113. package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
  114. package/tests/hydration/compiled/client/portal.js +87 -95
  115. package/tests/hydration/compiled/client/reactivity.js +135 -101
  116. package/tests/hydration/compiled/client/return.js +28 -19
  117. package/tests/hydration/compiled/client/streaming.js +390 -306
  118. package/tests/hydration/compiled/client/switch.js +258 -266
  119. package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
  120. package/tests/hydration/compiled/client/try.js +173 -129
  121. package/tests/hydration/compiled/client/typed-text.js +71 -0
  122. package/tests/hydration/compiled/fixtures/money.js +10 -0
  123. package/tests/hydration/compiled/server/basic.js +123 -6
  124. package/tests/hydration/compiled/server/composite.js +16 -30
  125. package/tests/hydration/compiled/server/events.js +66 -18
  126. package/tests/hydration/compiled/server/for.js +117 -41
  127. package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
  128. package/tests/hydration/compiled/server/head.js +20 -20
  129. package/tests/hydration/compiled/server/hmr.js +2 -2
  130. package/tests/hydration/compiled/server/html.js +70 -238
  131. package/tests/hydration/compiled/server/if-children.js +107 -15
  132. package/tests/hydration/compiled/server/if.js +11 -15
  133. package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
  134. package/tests/hydration/compiled/server/portal.js +14 -22
  135. package/tests/hydration/compiled/server/reactivity.js +14 -14
  136. package/tests/hydration/compiled/server/streaming.js +19 -19
  137. package/tests/hydration/compiled/server/switch.js +8 -8
  138. package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
  139. package/tests/hydration/compiled/server/try.js +8 -8
  140. package/tests/hydration/compiled/server/typed-text.js +17 -0
  141. package/tests/hydration/components/basic.tsrx +44 -8
  142. package/tests/hydration/components/composite.tsrx +2 -2
  143. package/tests/hydration/components/events.tsrx +25 -25
  144. package/tests/hydration/components/for.tsrx +115 -67
  145. package/tests/hydration/components/fragment-cursor.tsrx +650 -0
  146. package/tests/hydration/components/head.tsrx +16 -16
  147. package/tests/hydration/components/hmr.tsrx +2 -2
  148. package/tests/hydration/components/html.tsrx +7 -7
  149. package/tests/hydration/components/if-children.tsrx +72 -25
  150. package/tests/hydration/components/if.tsrx +18 -18
  151. package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
  152. package/tests/hydration/components/portal.tsrx +3 -3
  153. package/tests/hydration/components/reactivity.tsrx +16 -16
  154. package/tests/hydration/components/streaming.tsrx +20 -20
  155. package/tests/hydration/components/switch.tsrx +20 -20
  156. package/tests/hydration/components/text-types.ts +4 -0
  157. package/tests/hydration/components/track-async-serialization.tsrx +80 -26
  158. package/tests/hydration/components/try.tsrx +8 -8
  159. package/tests/hydration/components/typed-text.tsrx +22 -0
  160. package/tests/hydration/fixtures/money.js +10 -0
  161. package/tests/hydration/for.test.js +36 -0
  162. package/tests/hydration/fragment-cursor.test.js +111 -0
  163. package/tests/hydration/if-children.test.js +24 -0
  164. package/tests/hydration/streaming.test.js +19 -0
  165. package/tests/hydration/track-async-serialization.test.js +87 -2
  166. package/tests/hydration/tsconfig.text-types.json +10 -0
  167. package/tests/hydration/typed-text.test.js +34 -0
  168. package/tests/server/await.test.tsrx +3 -3
  169. package/tests/server/basic.attributes.test.tsrx +42 -42
  170. package/tests/server/basic.components.test.tsrx +64 -8
  171. package/tests/server/basic.test.tsrx +34 -32
  172. package/tests/server/compiler.test.tsrx +1 -70
  173. package/tests/server/composite.props.test.tsrx +6 -6
  174. package/tests/server/dynamic-elements.test.tsrx +31 -8
  175. package/tests/server/for.test.tsrx +94 -0
  176. package/tests/server/head.test.tsrx +11 -11
  177. package/tests/server/scoped-styles.test.tsrx +2 -2
  178. package/tests/server/streaming-ssr.test.tsrx +48 -43
  179. package/tests/server/track-async-serialization.test.tsrx +204 -21
  180. package/tests/server/try.test.tsrx +48 -48
  181. package/tests/types/transport.ts +40 -0
  182. package/tests/utils/vite-plugin-config.test.js +23 -0
  183. package/types/index.d.ts +48 -7
  184. package/types/server.d.ts +3 -0
  185. package/types/transport.d.ts +15 -0
  186. package/src/runtime/internal/client/switch.js +0 -113
  187. package/src/utils/errors.js +0 -13
  188. package/tests/client/lazy-array.test.tsrx +0 -31
  189. package/tests/client/lazy-destructuring.test.tsrx +0 -438
  190. package/tests/client/tracked-index-access.test.tsrx +0 -113
  191. package/tests/server/lazy-destructuring.test.tsrx +0 -595
  192. package/tests/server/tracked-index-access.test.tsrx +0 -76
package/types/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { ExtendedEventOptions } from '@tsrx/core/types';
2
+ export { setTransport } from './transport.js';
3
+ export type { Transport, Transporter } from './transport.js';
2
4
  export type { RefValue } from '@tsrx/core/runtime/ref';
3
5
  export type { AddEventOptions, AddEventObject, ExtendedEventOptions } from '@tsrx/core/types';
4
6
 
@@ -61,12 +63,26 @@ export function Dynamic<T>(
61
63
 
62
64
  export function mount(
63
65
  component: Component,
64
- options: { target: HTMLElement; props?: Record<string, any>; rootBoundary?: RootBoundaryOptions },
66
+ options: {
67
+ target: HTMLElement;
68
+ props?: Record<string, any>;
69
+ /**
70
+ * The default `try`/`pending`/`catch` boundary the app renders under.
71
+ * `false` renders without one: `trackAsync()` must then sit inside a
72
+ * user `@try` block, and errors that escape one propagate out of the
73
+ * flush.
74
+ */
75
+ rootBoundary?: RootBoundaryOptions | false;
76
+ },
65
77
  ): () => void;
66
78
 
67
79
  export function hydrate(
68
80
  component: Component,
69
- options: { target: HTMLElement; props?: Record<string, any>; rootBoundary?: RootBoundaryOptions },
81
+ options: {
82
+ target: HTMLElement;
83
+ props?: Record<string, any>;
84
+ rootBoundary?: RootBoundaryOptions | false;
85
+ },
70
86
  ): () => void;
71
87
 
72
88
  export interface RootBoundaryOptions {
@@ -208,14 +224,34 @@ export const SUSPENSE_REJECTED: unique symbol;
208
224
  interface TrackedBase<V> {
209
225
  '#v': V;
210
226
  value: V;
227
+ /**
228
+ * A read-only view of this value: a `Derived<V>` that follows it, for a
229
+ * receiver that should read but not write it. Equivalent to
230
+ * `track(() => tracked.value)`.
231
+ */
232
+ readOnly(): Derived<V>;
211
233
  }
212
234
  // Augment Tracked to be callable when V is a Component.
213
235
  // This allows tracked component values to continue flowing through JSX checks.
214
236
  interface TrackedCallable<V> {
215
237
  (props: V extends Component<infer P> ? P : never): V extends Component ? void : never;
216
238
  }
217
- // Supports destructuring `const [one, two] = track(0);`
218
- export type Tracked<V> = [V, Tracked<V>] & TrackedBase<V> & TrackedCallable<V>;
239
+ // A tracked value: `track(0)`. Read and write it through `.value`.
240
+ export type Tracked<V> = TrackedBase<V> & TrackedCallable<V>;
241
+
242
+ // A computed value: `track(() => ...)`. Its `value` is read-only unless the
243
+ // call opted into writes (see `WritableDerived`). A `Tracked` satisfies it.
244
+ interface DerivedBase<V> {
245
+ '#v': V;
246
+ readonly value: V;
247
+ /** A derived is already read-only: returns itself. */
248
+ readOnly(): Derived<V>;
249
+ }
250
+ export type Derived<V> = DerivedBase<V> & TrackedCallable<V>;
251
+ // A computed value created with a setter (`track(fn, get, set)`) or with
252
+ // `true` in the setter position: writes land as a temporary value until the
253
+ // next recompute.
254
+ export type WritableDerived<V> = TrackedBase<V> & TrackedCallable<V>;
219
255
 
220
256
  // Helper type to infer component type from a function that returns a component
221
257
  // If T is a function returning a Component, extract the Component type itself, not the return type (void)
@@ -235,12 +271,17 @@ type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
235
271
 
236
272
  // Overload for tracked values - returns the original tracked value type
237
273
  export function track<V>(value: Tracked<V>): Tracked<V>;
238
- // Overload for function values - infers the return type of the function
274
+ // Overload for function values - a read-only derived
239
275
  export function track<V>(
240
276
  value: () => V,
241
277
  get?: (v: InferComponent<V>) => InferComponent<V>,
242
- set?: (next: InferComponent<V>, prev: InferComponent<V>) => InferComponent<V>,
243
- ): Tracked<InferComponent<V>>;
278
+ ): Derived<InferComponent<V>>;
279
+ // Overload for function values with a setter (or `true`) - a writable derived
280
+ export function track<V>(
281
+ value: () => V,
282
+ get: ((v: InferComponent<V>) => InferComponent<V>) | undefined,
283
+ set: ((next: InferComponent<V>, prev: InferComponent<V>) => InferComponent<V>) | true,
284
+ ): WritableDerived<InferComponent<V>>;
244
285
  // Overload for non-function values
245
286
  export function track<V>(value?: V, get?: (v: V) => V, set?: (next: V, prev: V) => V): Tracked<V>;
246
287
 
package/types/server.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { Component, RootBoundaryOptions } from '#public';
2
2
 
3
3
  // Re-export runtime types for server-compiled components
4
4
  export {
5
+ setTransport,
5
6
  track,
6
7
  untrack,
7
8
  flushSync,
@@ -16,6 +17,8 @@ export {
16
17
  RippleURLSearchParams,
17
18
  } from './index.js';
18
19
 
20
+ export type { Transport, Transporter } from './index.js';
21
+
19
22
  export interface RenderResult {
20
23
  head: string;
21
24
  body: string;
@@ -0,0 +1,15 @@
1
+ /** A custom type's synchronous serialization pair for hydration and RPC. */
2
+ export interface Transporter<T = any, Encoded = any> {
3
+ /** Return truthy serializable data, or false/undefined for values not handled. */
4
+ encode: (value: unknown) => Encoded | false | undefined;
5
+ decode: (data: Encoded) => T;
6
+ }
7
+
8
+ export type Transport = Record<string, Transporter>;
9
+
10
+ /**
11
+ * Register the app's transport once, before rendering, hydration, mounting or
12
+ * RPC. The Vite plugin calls this automatically for ripple.config.ts transport.
13
+ * Omit the argument or pass an empty object to restore built-in serialization.
14
+ */
15
+ export function setTransport(transport?: Transport): void;
@@ -1,113 +0,0 @@
1
- /** @import { Block } from '#client' */
2
-
3
- import { branch, destroy_block, get_first_node, get_last_node, render } from './blocks.js';
4
- import { SWITCH_BLOCK } from './constants.js';
5
- import { hydrate_next, hydrate_node, hydrating } from './hydration.js';
6
- import { next_sibling } from './operations.js';
7
- import { append } from './template.js';
8
-
9
- /**
10
- * Moves a block's DOM nodes to before an anchor node
11
- * @param {Block} block
12
- * @param {ChildNode} anchor
13
- * @returns {void}
14
- */
15
- function move(block, anchor) {
16
- // Fast path: a normal case records its own range. Only an optimized single
17
- // control-flow / component root case (DOM rendered through a descendant
18
- // block, `s.start` null) needs the descent via `get_first_node`/`get_last_node`.
19
- var s = block.s;
20
- var node = s !== null ? s.start : null;
21
- var end;
22
-
23
- if (node === null) {
24
- node = get_first_node(block);
25
- if (node === null) {
26
- return;
27
- }
28
- end = get_last_node(block);
29
- } else {
30
- end = s.end;
31
- }
32
- /** @type {Node | null} */
33
- var sibling;
34
-
35
- while (node !== null) {
36
- if (node === end) {
37
- append(anchor, node);
38
- break;
39
- }
40
- sibling = next_sibling(node);
41
- append(anchor, node);
42
- node = sibling;
43
- }
44
- }
45
-
46
- /**
47
- * @param {ChildNode} anchor
48
- * @param {() => ((anchor: ChildNode) => void)[] | null} fn
49
- * @param {boolean} [root_controlled] When true the block renders before the
50
- * component's `__anchor` (no `<!>` wrapper); during hydration the SSR boundary
51
- * marker is handed to `append()` afterwards for the context-aware cursor
52
- * advance the eliminated wrapper used to perform.
53
- * @returns {void}
54
- */
55
- export function switch_block(anchor, fn, root_controlled) {
56
- /** @type {Node | undefined} */
57
- var boundary;
58
-
59
- if (hydrating) {
60
- if (root_controlled) {
61
- boundary = /** @type {Node} */ (hydrate_node);
62
- }
63
- hydrate_next();
64
- }
65
-
66
- /** @type {((anchor: ChildNode) => void)[]} */
67
- var prev = [];
68
- /** @type {Map<(anchor: ChildNode) => void, Block>} */
69
- var blocks = new Map();
70
-
71
- render(
72
- () => {
73
- var funcs = fn();
74
- let same = prev.length === funcs?.length || false;
75
-
76
- for (var i = 0; i < prev.length; i++) {
77
- var p = prev[i];
78
-
79
- if (!funcs || funcs.indexOf(p) === -1) {
80
- same = false;
81
- destroy_block(/** @type {Block} */ (blocks.get(p)));
82
- blocks.delete(p);
83
- }
84
- }
85
-
86
- prev = funcs ?? [];
87
-
88
- if (same || !funcs) {
89
- return;
90
- }
91
-
92
- for (var i = 0; i < funcs.length; i++) {
93
- var n = funcs[i];
94
- var b = blocks.get(n);
95
- if (b) {
96
- move(b, anchor);
97
- continue;
98
- }
99
-
100
- blocks.set(
101
- n,
102
- branch(() => n(anchor)),
103
- );
104
- }
105
- },
106
- null,
107
- SWITCH_BLOCK,
108
- );
109
-
110
- if (hydrating && root_controlled) {
111
- append(anchor, /** @type {Node} */ (boundary));
112
- }
113
- }
@@ -1,13 +0,0 @@
1
- /** @returns {never} */
2
- export function throw_tracked_index_value_error() {
3
- throw new Error(
4
- 'Do not access tracked values with [0]. Use .value or &[] lazy destructuring instead. Numeric tracked access leads to degraded performance.',
5
- );
6
- }
7
-
8
- /** @returns {never} */
9
- export function throw_tracked_index_reference_error() {
10
- throw new Error(
11
- 'Do not access tracked values with [1]. Use the tracked value directly instead. Numeric tracked access leads to degraded performance.',
12
- );
13
- }
@@ -1,31 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { flushSync, track } from 'ripple';
3
-
4
- describe('lazy array destructuring', () => {
5
- it('updates unknown lazy array bindings through tracked values', () => {
6
- function Child({ tr: &[count, tr] }) @{
7
- <button
8
- onClick={() => {
9
- count++;
10
- tr[0]++;
11
- }}
12
- >{count}</button>
13
- }
14
-
15
- function App() @{
16
- let tracked = track(0);
17
- <Child tr={tracked} />
18
- }
19
-
20
- render(App);
21
-
22
- const button = container.querySelector('button');
23
- expect(button?.textContent).toBe('0');
24
-
25
- flushSync(() => {
26
- button?.click();
27
- });
28
-
29
- expect(button?.textContent).toBe('2');
30
- });
31
- });