@vidact/runtime 0.2.0-beta.0 → 0.2.0-beta.5

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 (244) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +1 -1
  3. package/dist/actions.d.ts +32 -30
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +251 -266
  6. package/dist/actions.js.map +1 -1
  7. package/dist/async.d.ts +20 -17
  8. package/dist/async.d.ts.map +1 -1
  9. package/dist/async.js +18 -17
  10. package/dist/async.js.map +1 -1
  11. package/dist/compiled/async-resource.d.ts +9 -6
  12. package/dist/compiled/async-resource.d.ts.map +1 -1
  13. package/dist/compiled/async-resource.js +69 -77
  14. package/dist/compiled/async-resource.js.map +1 -1
  15. package/dist/compiled/core.d.ts +136 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +2672 -2985
  18. package/dist/compiled/core.js.map +1 -1
  19. package/dist/compiled/types.d.ts +44 -56
  20. package/dist/compiled/types.d.ts.map +1 -1
  21. package/dist/compiled/types.js +10 -6
  22. package/dist/compiled/types.js.map +1 -1
  23. package/dist/compiled/validation.d.ts +4 -1
  24. package/dist/compiled/validation.d.ts.map +1 -1
  25. package/dist/compiled/validation.js +17 -20
  26. package/dist/compiled/validation.js.map +1 -1
  27. package/dist/component-spread.d.ts +5 -3
  28. package/dist/component-spread.d.ts.map +1 -1
  29. package/dist/component-spread.js +49 -67
  30. package/dist/component-spread.js.map +1 -1
  31. package/dist/concurrent.d.ts +20 -18
  32. package/dist/concurrent.d.ts.map +1 -1
  33. package/dist/concurrent.js +191 -210
  34. package/dist/concurrent.js.map +1 -1
  35. package/dist/direct-dom.d.ts +17 -12
  36. package/dist/direct-dom.d.ts.map +1 -1
  37. package/dist/direct-dom.js +187 -221
  38. package/dist/direct-dom.js.map +1 -1
  39. package/dist/dom/attributes.d.ts +9 -6
  40. package/dist/dom/attributes.d.ts.map +1 -1
  41. package/dist/dom/attributes.js +65 -47
  42. package/dist/dom/attributes.js.map +1 -1
  43. package/dist/dom/events.d.ts +5 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +53 -65
  46. package/dist/dom/events.js.map +1 -1
  47. package/dist/dom/forms.d.ts +9 -6
  48. package/dist/dom/forms.d.ts.map +1 -1
  49. package/dist/dom/forms.js +152 -198
  50. package/dist/dom/forms.js.map +1 -1
  51. package/dist/dom/intrinsic.d.ts +17 -15
  52. package/dist/dom/intrinsic.d.ts.map +1 -1
  53. package/dist/dom/intrinsic.js +48 -50
  54. package/dist/dom/intrinsic.js.map +1 -1
  55. package/dist/dom/namespace.d.ts +4 -1
  56. package/dist/dom/namespace.d.ts.map +1 -1
  57. package/dist/dom/namespace.js +25 -28
  58. package/dist/dom/namespace.js.map +1 -1
  59. package/dist/dom/namespaces.d.ts +11 -8
  60. package/dist/dom/namespaces.d.ts.map +1 -1
  61. package/dist/dom/namespaces.js +14 -12
  62. package/dist/dom/namespaces.js.map +1 -1
  63. package/dist/dom/properties.d.ts +18 -16
  64. package/dist/dom/properties.d.ts.map +1 -1
  65. package/dist/dom/properties.js +273 -296
  66. package/dist/dom/properties.js.map +1 -1
  67. package/dist/dom/style-properties.d.ts +4 -1
  68. package/dist/dom/style-properties.d.ts.map +1 -1
  69. package/dist/dom/style-properties.js +49 -45
  70. package/dist/dom/style-properties.js.map +1 -1
  71. package/dist/dom/styles.d.ts +5 -2
  72. package/dist/dom/styles.d.ts.map +1 -1
  73. package/dist/dom/styles.js +47 -67
  74. package/dist/dom/styles.js.map +1 -1
  75. package/dist/dom-forms.d.ts +4 -1
  76. package/dist/dom-forms.d.ts.map +1 -1
  77. package/dist/dom-forms.js +16 -13
  78. package/dist/dom-forms.js.map +1 -1
  79. package/dist/dom-styles.d.ts +4 -1
  80. package/dist/dom-styles.d.ts.map +1 -1
  81. package/dist/dom-styles.js +10 -7
  82. package/dist/dom-styles.js.map +1 -1
  83. package/dist/framework-hydrate.d.ts +20 -17
  84. package/dist/framework-hydrate.d.ts.map +1 -1
  85. package/dist/framework-hydrate.js +128 -146
  86. package/dist/framework-hydrate.js.map +1 -1
  87. package/dist/framework-protocol.d.ts +27 -25
  88. package/dist/framework-protocol.d.ts.map +1 -1
  89. package/dist/framework-protocol.js +173 -211
  90. package/dist/framework-protocol.js.map +1 -1
  91. package/dist/framework.d.ts +34 -32
  92. package/dist/framework.d.ts.map +1 -1
  93. package/dist/framework.js +74 -93
  94. package/dist/framework.js.map +1 -1
  95. package/dist/hydrate.d.ts +11 -8
  96. package/dist/hydrate.d.ts.map +1 -1
  97. package/dist/hydrate.js +13 -9
  98. package/dist/hydrate.js.map +1 -1
  99. package/dist/hydration-bridge.d.ts +70 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +77 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +54 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +450 -463
  106. package/dist/hydration.js.map +1 -1
  107. package/dist/index.d.ts +13 -13
  108. package/dist/index.js +11 -13
  109. package/dist/indexed-list.d.ts +8 -5
  110. package/dist/indexed-list.d.ts.map +1 -1
  111. package/dist/indexed-list.js +14 -7
  112. package/dist/indexed-list.js.map +1 -1
  113. package/dist/jsx-runtime.d.ts +10 -8
  114. package/dist/jsx-runtime.d.ts.map +1 -1
  115. package/dist/jsx-runtime.js +28 -23
  116. package/dist/jsx-runtime.js.map +1 -1
  117. package/dist/keyed-list.d.ts +13 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +132 -160
  120. package/dist/keyed-list.js.map +1 -1
  121. package/dist/metadata.d.ts +6 -3
  122. package/dist/metadata.d.ts.map +1 -1
  123. package/dist/metadata.js +131 -164
  124. package/dist/metadata.js.map +1 -1
  125. package/dist/nested-prop.d.ts +4 -2
  126. package/dist/nested-prop.d.ts.map +1 -1
  127. package/dist/nested-prop.js +17 -16
  128. package/dist/nested-prop.js.map +1 -1
  129. package/dist/profiling.d.ts +11 -8
  130. package/dist/profiling.d.ts.map +1 -1
  131. package/dist/profiling.js +17 -17
  132. package/dist/profiling.js.map +1 -1
  133. package/dist/protocol.d.ts +5 -2
  134. package/dist/protocol.d.ts.map +1 -1
  135. package/dist/protocol.js +8 -5
  136. package/dist/protocol.js.map +1 -1
  137. package/dist/raw-html.d.ts +5 -2
  138. package/dist/raw-html.d.ts.map +1 -1
  139. package/dist/raw-html.js +86 -146
  140. package/dist/raw-html.js.map +1 -1
  141. package/dist/ref.d.ts +8 -3
  142. package/dist/ref.d.ts.map +1 -1
  143. package/dist/ref.js +10 -2
  144. package/dist/ref.js.map +1 -1
  145. package/dist/renderable-protocol.d.ts +26 -8
  146. package/dist/renderable-protocol.d.ts.map +1 -1
  147. package/dist/renderable-protocol.js +30 -9
  148. package/dist/renderable-protocol.js.map +1 -1
  149. package/dist/renderable.d.ts +27 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +141 -133
  152. package/dist/renderable.js.map +1 -1
  153. package/dist/retained-ui.d.ts +9 -6
  154. package/dist/retained-ui.d.ts.map +1 -1
  155. package/dist/retained-ui.js +12 -10
  156. package/dist/retained-ui.js.map +1 -1
  157. package/dist/root.d.ts +18 -15
  158. package/dist/root.d.ts.map +1 -1
  159. package/dist/root.js +73 -85
  160. package/dist/root.js.map +1 -1
  161. package/dist/scheduler.d.ts +10 -8
  162. package/dist/scheduler.d.ts.map +1 -1
  163. package/dist/scheduler.js +97 -105
  164. package/dist/scheduler.js.map +1 -1
  165. package/dist/server-framework.d.ts +45 -42
  166. package/dist/server-framework.d.ts.map +1 -1
  167. package/dist/server-framework.js +209 -217
  168. package/dist/server-framework.js.map +1 -1
  169. package/dist/server.d.ts +130 -128
  170. package/dist/server.d.ts.map +1 -1
  171. package/dist/server.js +603 -710
  172. package/dist/server.js.map +1 -1
  173. package/dist/shared/promise.d.ts +6 -3
  174. package/dist/shared/promise.d.ts.map +1 -1
  175. package/dist/shared/promise.js +21 -19
  176. package/dist/shared/promise.js.map +1 -1
  177. package/dist/source-mask.d.ts +9 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +45 -63
  180. package/dist/source-mask.js.map +1 -1
  181. package/dist/spread.d.ts +5 -2
  182. package/dist/spread.d.ts.map +1 -1
  183. package/dist/spread.js +79 -99
  184. package/dist/spread.js.map +1 -1
  185. package/dist/state-slot.d.ts +18 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +43 -42
  188. package/dist/state-slot.js.map +1 -1
  189. package/dist/testing.d.ts +9 -6
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +16 -12
  192. package/dist/testing.js.map +1 -1
  193. package/package.json +8 -7
  194. package/src/actions.ts +415 -0
  195. package/src/async.ts +78 -0
  196. package/src/compiled/async-resource.ts +113 -0
  197. package/src/compiled/core.ts +4041 -0
  198. package/src/compiled/types.ts +96 -0
  199. package/src/compiled/validation.ts +23 -0
  200. package/src/component-spread.ts +86 -0
  201. package/src/concurrent.ts +310 -0
  202. package/src/direct-dom.ts +365 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +93 -0
  205. package/src/dom/forms.ts +220 -0
  206. package/src/dom/intrinsic.ts +94 -0
  207. package/src/dom/namespace.ts +30 -0
  208. package/src/dom/namespaces.ts +14 -0
  209. package/src/dom/properties.ts +355 -0
  210. package/src/dom/style-properties.ts +46 -0
  211. package/src/dom/styles.ts +86 -0
  212. package/src/dom-forms.ts +23 -0
  213. package/src/dom-styles.ts +11 -0
  214. package/src/env.d.ts +6 -0
  215. package/src/framework-hydrate.ts +224 -0
  216. package/src/framework-protocol.ts +335 -0
  217. package/src/framework.ts +161 -0
  218. package/src/hydrate.ts +38 -0
  219. package/src/hydration-bridge.ts +196 -0
  220. package/src/hydration.ts +642 -0
  221. package/src/index.ts +92 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +233 -0
  225. package/src/metadata.ts +212 -0
  226. package/src/nested-prop.ts +27 -0
  227. package/src/profiling.ts +43 -0
  228. package/src/protocol.ts +8 -0
  229. package/src/raw-html.ts +206 -0
  230. package/src/ref.ts +13 -0
  231. package/src/renderable-protocol.ts +63 -0
  232. package/src/renderable.ts +283 -0
  233. package/src/retained-ui.ts +19 -0
  234. package/src/root.ts +122 -0
  235. package/src/scheduler.ts +136 -0
  236. package/src/server-framework.ts +397 -0
  237. package/src/server.ts +1160 -0
  238. package/src/shared/promise.ts +28 -0
  239. package/src/source-mask.ts +83 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +76 -0
  242. package/src/testing.ts +27 -0
  243. package/dist/index.d.ts.map +0 -1
  244. package/dist/index.js.map +0 -1
@@ -1,18 +1,21 @@
1
- import type { SourceMask } from './source-mask.js';
2
- export type StateUpdate<T> = T | ((previous: T) => T);
3
- export interface DeferredStateWrite<T> {
4
- readonly slot: object;
5
- readonly revision: number;
6
- readonly update: StateUpdate<T>;
7
- readonly commit: (update: StateUpdate<T>) => void;
8
- readonly currentRevision: () => number;
1
+ import { SourceMask } from "./source-mask.js";
2
+ //#region src/state-slot.d.ts
3
+ type StateUpdate<T> = T | ((previous: T) => T);
4
+ interface DeferredStateWrite<T> {
5
+ readonly slot: object;
6
+ readonly revision: number;
7
+ readonly update: StateUpdate<T>;
8
+ readonly commit: (update: StateUpdate<T>) => void;
9
+ readonly currentRevision: () => number;
9
10
  }
10
- export type StateWriteInterceptor = <T>(write: DeferredStateWrite<T>) => boolean;
11
- export declare function installStateWriteInterceptor(interceptor: StateWriteInterceptor): void;
12
- export interface StateSlot<T> {
13
- readonly get: () => T;
14
- readonly set: (update: StateUpdate<T>) => void;
15
- readonly replace: (value: T) => void;
11
+ type StateWriteInterceptor = <T>(write: DeferredStateWrite<T>) => boolean;
12
+ declare function installStateWriteInterceptor(interceptor: StateWriteInterceptor): void;
13
+ interface StateSlot<T> {
14
+ readonly get: () => T;
15
+ readonly set: (update: StateUpdate<T>) => void;
16
+ readonly replace: (value: T) => void;
16
17
  }
17
- export declare function createStateSlot<T>(invalidate: (sources: SourceMask) => void, source: SourceMask, initialValue: T, assertWritable?: () => void): StateSlot<T>;
18
+ declare function createStateSlot<T>(invalidate: (sources: SourceMask) => void, source: SourceMask, initialValue: T, assertWritable?: () => void): StateSlot<T>;
19
+ //#endregion
20
+ export { DeferredStateWrite, StateSlot, StateUpdate, StateWriteInterceptor, createStateSlot, installStateWriteInterceptor };
18
21
  //# sourceMappingURL=state-slot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-slot.d.ts","sourceRoot":"","sources":["../src/state-slot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;AAErD,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACjD,QAAQ,CAAC,eAAe,EAAE,MAAM,MAAM,CAAA;CACvC;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;AAIhF,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,qBAAqB,GAAG,IAAI,CAErF;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC9C,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAA;CACrC;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,UAAU,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,EACzC,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,CAAC,EACf,cAAc,CAAC,EAAE,MAAM,IAAI,GAC1B,SAAS,CAAC,CAAC,CAAC,CA4Cd"}
1
+ {"version":3,"file":"state-slot.d.ts","names":[],"sources":["../src/state-slot.ts"],"mappings":";;KAEY,YAAY,KAAK,MAAM,UAAU,MAAM;UAElC,mBAAmB;WACzB;WACA;WACA,QAAQ,YAAY;WACpB,SAAS,QAAQ,YAAY;WAC7B;;KAGC,yBAAyB,GAAG,OAAO,mBAAmB;iBAIlD,6BAA6B,aAAa;UAIzC,UAAU;WAChB,WAAW;WACX,MAAM,QAAQ,YAAY;WAC1B,UAAU,OAAO;;iBAGZ,gBAAgB,GAC9B,aAAa,SAAS,qBACtB,QAAQ,YACR,cAAc,GACd,8BACC,UAAU"}
@@ -1,46 +1,47 @@
1
+ //#region src/state-slot.ts
1
2
  let stateWriteInterceptor;
2
- export function installStateWriteInterceptor(interceptor) {
3
- stateWriteInterceptor = interceptor;
3
+ function installStateWriteInterceptor(interceptor) {
4
+ stateWriteInterceptor = interceptor;
4
5
  }
5
- export function createStateSlot(invalidate, source, initialValue, assertWritable) {
6
- let value = initialValue;
7
- let revision = 0;
8
- const token = {};
9
- const commit = (next) => {
10
- if (Object.is(value, next))
11
- return;
12
- value = next;
13
- revision += 1;
14
- invalidate(source);
15
- };
16
- const commitUpdate = (update) => {
17
- const next = typeof update === 'function' ? update(value) : update;
18
- commit(next);
19
- };
20
- const commitReplacement = (replacement) => commit(replacement);
21
- const write = (update, commitWrite) => {
22
- if (stateWriteInterceptor?.({
23
- slot: token,
24
- revision,
25
- update,
26
- commit: commitWrite,
27
- currentRevision: () => revision,
28
- })) {
29
- return;
30
- }
31
- commitWrite(update);
32
- };
33
- const replace = (next) => {
34
- assertWritable?.();
35
- write(next, commitReplacement);
36
- };
37
- return {
38
- get: () => value,
39
- set: (update) => {
40
- assertWritable?.();
41
- write(update, commitUpdate);
42
- },
43
- replace,
44
- };
6
+ function createStateSlot(invalidate, source, initialValue, assertWritable) {
7
+ let value = initialValue;
8
+ let revision = 0;
9
+ const token = {};
10
+ const commit = (next) => {
11
+ if (Object.is(value, next)) return;
12
+ value = next;
13
+ revision += 1;
14
+ invalidate(source);
15
+ };
16
+ const commitUpdate = (update) => {
17
+ const next = typeof update === "function" ? update(value) : update;
18
+ commit(next);
19
+ };
20
+ const commitReplacement = (replacement) => commit(replacement);
21
+ const write = (update, commitWrite) => {
22
+ if (stateWriteInterceptor?.({
23
+ slot: token,
24
+ revision,
25
+ update,
26
+ commit: commitWrite,
27
+ currentRevision: () => revision
28
+ })) return;
29
+ commitWrite(update);
30
+ };
31
+ const replace = (next) => {
32
+ assertWritable?.();
33
+ write(next, commitReplacement);
34
+ };
35
+ return {
36
+ get: () => value,
37
+ set: (update) => {
38
+ assertWritable?.();
39
+ write(update, commitUpdate);
40
+ },
41
+ replace
42
+ };
45
43
  }
44
+ //#endregion
45
+ export { createStateSlot, installStateWriteInterceptor };
46
+
46
47
  //# sourceMappingURL=state-slot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-slot.js","sourceRoot":"","sources":["../src/state-slot.ts"],"names":[],"mappings":"AAcA,IAAI,qBAAwD,CAAA;AAE5D,MAAM,UAAU,4BAA4B,CAAC,WAAkC;IAC7E,qBAAqB,GAAG,WAAW,CAAA;AACrC,CAAC;AAQD,MAAM,UAAU,eAAe,CAC7B,UAAyC,EACzC,MAAkB,EAClB,YAAe,EACf,cAA2B;IAE3B,IAAI,KAAK,GAAG,YAAY,CAAA;IACxB,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,MAAM,KAAK,GAAG,EAAE,CAAA;IAEhB,MAAM,MAAM,GAAG,CAAC,IAAO,EAAQ,EAAE;QAC/B,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;YAAE,OAAM;QAElC,KAAK,GAAG,IAAI,CAAA;QACZ,QAAQ,IAAI,CAAC,CAAA;QACb,UAAU,CAAC,MAAM,CAAC,CAAA;IACpB,CAAC,CAAA;IACD,MAAM,YAAY,GAAG,CAAC,MAAsB,EAAQ,EAAE;QACpD,MAAM,IAAI,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAE,MAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;QAC1F,MAAM,CAAC,IAAI,CAAC,CAAA;IACd,CAAC,CAAA;IACD,MAAM,iBAAiB,GAAG,CAAC,WAA2B,EAAQ,EAAE,CAAC,MAAM,CAAC,WAAgB,CAAC,CAAA;IACzF,MAAM,KAAK,GAAG,CAAC,MAAsB,EAAE,WAA6C,EAAQ,EAAE;QAC5F,IACE,qBAAqB,EAAE,CAAC;YACtB,IAAI,EAAE,KAAK;YACX,QAAQ;YACR,MAAM;YACN,MAAM,EAAE,WAAW;YACnB,eAAe,EAAE,GAAG,EAAE,CAAC,QAAQ;SAChC,CAAC,EACF,CAAC;YACD,OAAM;QACR,CAAC;QACD,WAAW,CAAC,MAAM,CAAC,CAAA;IACrB,CAAC,CAAA;IACD,MAAM,OAAO,GAAG,CAAC,IAAO,EAAQ,EAAE;QAChC,cAAc,EAAE,EAAE,CAAA;QAClB,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;IAChC,CAAC,CAAA;IAED,OAAO;QACL,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;QAChB,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE;YACd,cAAc,EAAE,EAAE,CAAA;YAClB,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QAC7B,CAAC;QACD,OAAO;KACR,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"state-slot.js","names":[],"sources":["../src/state-slot.ts"],"mappings":";AAcA,IAAI;AAEJ,SAAgB,6BAA6B,aAA0C;CACrF,wBAAwB;AAC1B;AAQA,SAAgB,gBACd,YACA,QACA,cACA,gBACc;CACd,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,MAAM,QAAQ,CAAC;CAEf,MAAM,UAAU,SAAkB;EAChC,IAAI,OAAO,GAAG,OAAO,IAAI,GAAG;EAE5B,QAAQ;EACR,YAAY;EACZ,WAAW,MAAM;CACnB;CACA,MAAM,gBAAgB,WAAiC;EACrD,MAAM,OAAO,OAAO,WAAW,aAAc,OAA8B,KAAK,IAAI;EACpF,OAAO,IAAI;CACb;CACA,MAAM,qBAAqB,gBAAsC,OAAO,WAAgB;CACxF,MAAM,SAAS,QAAwB,gBAAwD;EAC7F,IACE,wBAAwB;GACtB,MAAM;GACN;GACA;GACA,QAAQ;GACR,uBAAuB;EACzB,CAAC,GAED;EAEF,YAAY,MAAM;CACpB;CACA,MAAM,WAAW,SAAkB;EACjC,iBAAiB;EACjB,MAAM,MAAM,iBAAiB;CAC/B;CAEA,OAAO;EACL,WAAW;EACX,MAAM,WAAW;GACf,iBAAiB;GACjB,MAAM,QAAQ,YAAY;EAC5B;EACA;CACF;AACF"}
package/dist/testing.d.ts CHANGED
@@ -1,7 +1,10 @@
1
- import { readCompiledOwnerMetrics as readOwnerMetrics } from './compiled/core.js';
2
- export declare function beginActScope(): void;
3
- export declare function endActScope(): void;
4
- export declare function flushScheduledTasks(): number;
5
- export declare function hasScheduledTasks(): boolean;
6
- export declare function readCompiledOwnerMetrics(): ReturnType<typeof readOwnerMetrics>;
1
+ import { readCompiledOwnerMetrics as readCompiledOwnerMetrics$1 } from "./compiled/core.js";
2
+ //#region src/testing.d.ts
3
+ declare function beginActScope(): void;
4
+ declare function endActScope(): void;
5
+ declare function flushScheduledTasks(): number;
6
+ declare function hasScheduledTasks(): boolean;
7
+ declare function readCompiledOwnerMetrics(): ReturnType<typeof readCompiledOwnerMetrics$1>;
8
+ //#endregion
9
+ export { beginActScope, endActScope, flushScheduledTasks, hasScheduledTasks, readCompiledOwnerMetrics };
7
10
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAQjF,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,wBAAwB,IAAI,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAE9E"}
1
+ {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;iBAQgB;iBAIA;iBAIA;iBAIA;iBAIA,4BAA4B,kBAAkB"}
package/dist/testing.js CHANGED
@@ -1,18 +1,22 @@
1
- import { readCompiledOwnerMetrics as readOwnerMetrics } from './compiled/core.js';
2
- import { beginActScope as beginSchedulerActScope, endActScope as endSchedulerActScope, flushScheduledTasks as flushSchedulerTasks, hasScheduledTasks as schedulerHasTasks, } from './scheduler.js';
3
- export function beginActScope() {
4
- beginSchedulerActScope();
1
+ import { beginActScope as beginActScope$1, endActScope as endActScope$1, flushScheduledTasks as flushScheduledTasks$1, hasScheduledTasks as hasScheduledTasks$1 } from "./scheduler.js";
2
+ import { readCompiledOwnerMetrics as readCompiledOwnerMetrics$1 } from "./compiled/core.js";
3
+ //#region src/testing.ts
4
+ function beginActScope() {
5
+ beginActScope$1();
5
6
  }
6
- export function endActScope() {
7
- endSchedulerActScope();
7
+ function endActScope() {
8
+ endActScope$1();
8
9
  }
9
- export function flushScheduledTasks() {
10
- return flushSchedulerTasks();
10
+ function flushScheduledTasks() {
11
+ return flushScheduledTasks$1();
11
12
  }
12
- export function hasScheduledTasks() {
13
- return schedulerHasTasks();
13
+ function hasScheduledTasks() {
14
+ return hasScheduledTasks$1();
14
15
  }
15
- export function readCompiledOwnerMetrics() {
16
- return readOwnerMetrics();
16
+ function readCompiledOwnerMetrics() {
17
+ return readCompiledOwnerMetrics$1();
17
18
  }
19
+ //#endregion
20
+ export { beginActScope, endActScope, flushScheduledTasks, hasScheduledTasks, readCompiledOwnerMetrics };
21
+
18
22
  //# sourceMappingURL=testing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACjF,OAAO,EACL,aAAa,IAAI,sBAAsB,EACvC,WAAW,IAAI,oBAAoB,EACnC,mBAAmB,IAAI,mBAAmB,EAC1C,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,gBAAgB,CAAA;AAEvB,MAAM,UAAU,aAAa;IAC3B,sBAAsB,EAAE,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,oBAAoB,EAAE,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,mBAAmB,EAAE,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,iBAAiB,EAAE,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,OAAO,gBAAgB,EAAE,CAAA;AAC3B,CAAC"}
1
+ {"version":3,"file":"testing.js","names":["flushSchedulerTasks","schedulerHasTasks","readOwnerMetrics"],"sources":["../src/testing.ts"],"mappings":";;;AAQA,SAAgB,gBAAsB;CACpC,gBAAuB;AACzB;AAEA,SAAgB,cAAoB;CAClC,cAAqB;AACvB;AAEA,SAAgB,sBAA8B;CAC5C,OAAOA,sBAAoB;AAC7B;AAEA,SAAgB,oBAA6B;CAC3C,OAAOC,oBAAkB;AAC3B;AAEA,SAAgB,2BAAgE;CAC9E,OAAOC,2BAAiB;AAC1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vidact/runtime",
3
- "version": "0.2.0-beta.0",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "Fine-grained direct-DOM runtime for compiled Vidact applications",
5
5
  "keywords": [
6
6
  "compiler",
@@ -21,7 +21,8 @@
21
21
  "CHANGELOG.md",
22
22
  "dist",
23
23
  "LICENSE",
24
- "README.md"
24
+ "README.md",
25
+ "src"
25
26
  ],
26
27
  "type": "module",
27
28
  "sideEffects": [
@@ -126,16 +127,16 @@
126
127
  }
127
128
  },
128
129
  "publishConfig": {
129
- "access": "public",
130
- "provenance": true
130
+ "access": "public"
131
131
  },
132
132
  "devDependencies": {
133
133
  "@types/react": "^19.2.18",
134
134
  "@vitest/browser-playwright": "4.1.11",
135
+ "tsdown": "0.22.14",
135
136
  "typescript": "7.0.2",
136
137
  "vitest": "4.1.11",
137
- "@vidact/test-support": "0.2.0-beta.0",
138
- "@vidact/react-types": "0.2.0-beta.0"
138
+ "@vidact/test-support": "0.2.0-beta.5",
139
+ "@vidact/react-types": "0.2.0-beta.5"
139
140
  },
140
141
  "peerDependencies": {
141
142
  "@types/react": "^19.2.18"
@@ -144,7 +145,7 @@
144
145
  "node": ">=24"
145
146
  },
146
147
  "scripts": {
147
- "build": "node ../../scripts/clean-typescript-package.mjs dist && tsc -p tsconfig.build.json && node ../../scripts/finalize-typescript-package.mjs dist src/env.d.ts",
148
+ "build": "tsdown",
148
149
  "test": "vitest run && vitest run --config vitest.retained-ui.config.ts && vitest run --config vitest.server.config.ts",
149
150
  "typecheck": "tsc -p tsconfig.json"
150
151
  }
package/src/actions.ts ADDED
@@ -0,0 +1,415 @@
1
+ import {
2
+ captureCompiledTask,
3
+ createContext,
4
+ createCompiledExternalStore,
5
+ createCompiledState,
6
+ registerCompiledCleanup,
7
+ runWithCompiledContext,
8
+ useContext,
9
+ } from './compiled/core.ts'
10
+ import type { CompiledScope } from './compiled/types.ts'
11
+ import {
12
+ registerTransitionAborter,
13
+ registerTransitionFinalizer,
14
+ registerTransitionSettlement,
15
+ runUrgentUpdate,
16
+ startIndependentTransition,
17
+ } from './concurrent.ts'
18
+ import { h, type DirectChild, type DirectProps } from './direct-dom.ts'
19
+ import { resetActionForm } from './dom/forms.ts'
20
+ import { installDomPropHandler } from './dom/properties.ts'
21
+ import type { SourceMask } from './source-mask.ts'
22
+ import type { StateSlot } from './state-slot.ts'
23
+
24
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
25
+ const FORM_ACTION_EXECUTE = Symbol(DEV ? 'Vidact.FormActionExecute' : undefined)
26
+
27
+ export interface FormStatus {
28
+ readonly pending: boolean
29
+ readonly data: FormData | null
30
+ readonly method: 'get' | 'post'
31
+ readonly action: FunctionFormAction | null
32
+ }
33
+
34
+ export type FunctionFormAction = (data: FormData) => unknown | PromiseLike<unknown>
35
+
36
+ type FormStatusStore = {
37
+ current: FormStatus
38
+ readonly listeners: Set<() => void>
39
+ }
40
+
41
+ type ActionFrame = {
42
+ readonly settlements: Set<() => void>
43
+ }
44
+
45
+ type ActionDispatch<Payload> = {
46
+ (payload: Payload): void
47
+ readonly permalink?: string
48
+ readonly [FORM_ACTION_EXECUTE]: (payload: Payload) => Promise<void>
49
+ }
50
+
51
+ type ActionQueueEntry<Payload> = {
52
+ readonly payload: Payload
53
+ readonly reportError: boolean
54
+ readonly resolve: () => void
55
+ readonly reject: (error: unknown) => void
56
+ }
57
+
58
+ type OptimisticLayer<Update> = {
59
+ readonly frame: object
60
+ readonly update: Update
61
+ settling: boolean
62
+ }
63
+
64
+ export interface OptimisticSlot<Value, Update> {
65
+ readonly get: () => Value
66
+ readonly set: (update: Update) => void
67
+ }
68
+
69
+ const defaultFormStatus: FormStatus = {
70
+ pending: false,
71
+ data: null,
72
+ method: 'get',
73
+ action: null,
74
+ }
75
+ const functionActions = new WeakMap<Element, FunctionFormAction>()
76
+ const formStatusContext = createContext<FormStatusStore | undefined>(undefined)
77
+ let actionDomInstalled = false
78
+ let activeActionFrame: ActionFrame | undefined
79
+
80
+ export function createCompiledActionState<State, Payload>(
81
+ scope: CompiledScope,
82
+ stateSource: SourceMask,
83
+ pendingSource: SourceMask,
84
+ action: (previousState: State, payload: Payload) => State | PromiseLike<State>,
85
+ initialState: State,
86
+ permalink?: string,
87
+ ): {
88
+ readonly get: () => { readonly value: State; readonly pending: boolean }
89
+ readonly set: ActionDispatch<Payload>
90
+ } {
91
+ const state = createCompiledState(scope, stateSource, () => initialState)
92
+ const pending = createCompiledState(scope, pendingSource, false)
93
+ const task = captureCompiledTask(scope)
94
+ const queue: ActionQueueEntry<Payload>[] = []
95
+ let running = false
96
+
97
+ const enqueue = (payload: Payload, reportError: boolean): Promise<void> => {
98
+ runUrgentUpdate(() => pending.replace(true))
99
+ return new Promise<void>((resolve, reject) => {
100
+ queue.push({ payload, reportError, resolve, reject })
101
+ processNext()
102
+ })
103
+ }
104
+
105
+ const dispatch = ((payload: Payload): void => {
106
+ void enqueue(payload, true)
107
+ }) as ActionDispatch<Payload>
108
+ Object.defineProperty(dispatch, FORM_ACTION_EXECUTE, {
109
+ value: (payload: Payload) => enqueue(payload, false),
110
+ })
111
+ if (permalink !== undefined) Object.defineProperty(dispatch, 'permalink', { value: permalink })
112
+
113
+ const processNext = (): void => {
114
+ if (running) return
115
+ const entry = queue.shift()
116
+ if (entry === undefined) {
117
+ runUrgentUpdate(() => pending.replace(false))
118
+ return
119
+ }
120
+ running = true
121
+ const frame = createActionFrame()
122
+ let result: State | PromiseLike<State>
123
+ const previousFrame = activeActionFrame
124
+ activeActionFrame = frame
125
+ try {
126
+ result = action(state.get(), entry.payload)
127
+ } catch (error) {
128
+ activeActionFrame = previousFrame
129
+ finishFailure(entry, frame, error)
130
+ return
131
+ }
132
+ activeActionFrame = previousFrame
133
+ void Promise.resolve(result).then(
134
+ (nextState) => {
135
+ if (task.disposed()) {
136
+ entry.resolve()
137
+ running = false
138
+ return
139
+ }
140
+ runActionTransition(
141
+ () => {
142
+ state.replace(nextState)
143
+ settleActionFrame(frame)
144
+ },
145
+ () => {
146
+ entry.resolve()
147
+ running = false
148
+ processNext()
149
+ },
150
+ )
151
+ },
152
+ (error: unknown) => finishFailure(entry, frame, error),
153
+ )
154
+ }
155
+
156
+ const finishFailure = (
157
+ entry: ActionQueueEntry<Payload>,
158
+ frame: ActionFrame,
159
+ error: unknown,
160
+ ): void => {
161
+ if (task.disposed()) {
162
+ entry.resolve()
163
+ running = false
164
+ return
165
+ }
166
+ runActionTransition(
167
+ () => settleActionFrame(frame),
168
+ () => {
169
+ running = false
170
+ if (entry.reportError) {
171
+ entry.resolve()
172
+ task.report(error)
173
+ } else {
174
+ entry.reject(error)
175
+ }
176
+ processNext()
177
+ },
178
+ )
179
+ }
180
+
181
+ return {
182
+ get: () => ({ value: state.get(), pending: pending.get() }),
183
+ set: dispatch,
184
+ }
185
+ }
186
+
187
+ export function createCompiledOptimistic<Value, Update = Value>(
188
+ scope: CompiledScope,
189
+ reads: SourceMask,
190
+ writes: SourceMask,
191
+ evaluate: () => Value,
192
+ reducer: (current: Value, update: Update) => Value = (_current, update) =>
193
+ update as unknown as Value,
194
+ ): OptimisticSlot<Value, Update> {
195
+ const slot = createCompiledState(scope, writes, () => evaluate())
196
+ let layers: OptimisticLayer<Update>[] = []
197
+ const compute = (items = layers): Value =>
198
+ items.reduce(
199
+ (value, layer) => (layer.settling ? value : reducer(value, layer.update)),
200
+ evaluate(),
201
+ )
202
+ scope[0](reads, () => slot.replace(compute()))
203
+
204
+ const add = (update: Update): void => {
205
+ const frame = activeActionFrame ?? {}
206
+ const layer: OptimisticLayer<Update> = { frame, update, settling: false }
207
+ layers.push(layer)
208
+ try {
209
+ runUrgentUpdate(() => slot.replace(compute()))
210
+ } catch (error) {
211
+ layers = layers.filter((item) => item !== layer)
212
+ throw error
213
+ }
214
+ const settle = (): void => settleOptimisticFrame(frame)
215
+ if (activeActionFrame !== undefined) {
216
+ activeActionFrame.settlements.add(settle)
217
+ return
218
+ }
219
+ if (registerTransitionSettlement(() => startIndependentTransition(settle))) return
220
+ layers = layers.filter((item) => item !== layer)
221
+ runUrgentUpdate(() => slot.replace(compute()))
222
+ throw new Error(DEV ? 'optimistic updates must run inside an Action or transition' : 'V035')
223
+ }
224
+
225
+ const settleOptimisticFrame = (frame: object): void => {
226
+ const settling = layers.filter((layer) => layer.frame === frame)
227
+ if (settling.length === 0) return
228
+ for (const layer of settling) layer.settling = true
229
+ slot.replace(compute())
230
+ if (
231
+ registerTransitionFinalizer(() => {
232
+ layers = layers.filter((layer) => layer.frame !== frame)
233
+ })
234
+ ) {
235
+ registerTransitionAborter(() => {
236
+ for (const layer of settling) layer.settling = false
237
+ })
238
+ return
239
+ }
240
+ layers = layers.filter((layer) => layer.frame !== frame)
241
+ }
242
+
243
+ return { get: slot.get, set: add }
244
+ }
245
+
246
+ export function createCompiledFormStatus(
247
+ scope: CompiledScope,
248
+ sourceMask: SourceMask,
249
+ ): StateSlot<FormStatus> {
250
+ const store = useContext(formStatusContext)
251
+ if (store === undefined) return createCompiledState(scope, sourceMask, () => defaultFormStatus)
252
+ return createCompiledExternalStore(
253
+ scope,
254
+ sourceMask,
255
+ (notify) => {
256
+ store.listeners.add(notify)
257
+ return () => store.listeners.delete(notify)
258
+ },
259
+ () => store.current,
260
+ )
261
+ }
262
+
263
+ export function ActionForm(props: DirectProps): DirectChild {
264
+ ensureActionDom()
265
+ const task = captureCompiledTask()
266
+ const store: FormStatusStore = { current: defaultFormStatus, listeners: new Set() }
267
+ const children = normalizeChildren(props?.children)
268
+ const form = runWithCompiledContext(formStatusContext, store, () =>
269
+ h('form', props, ...children),
270
+ ) as HTMLFormElement
271
+ let activeSubmissions = 0
272
+ let latestSubmission = 0
273
+ let latestSucceeded = false
274
+ const submit = (event: SubmitEvent): void => {
275
+ const submitter = event.submitter
276
+ const hasSubmitterOverride =
277
+ submitter instanceof Element &&
278
+ (functionActions.has(submitter) || submitter.hasAttribute('formaction'))
279
+ const action = hasSubmitterOverride
280
+ ? functionActions.get(submitter as Element)
281
+ : functionActions.get(form)
282
+ if (action === undefined) return
283
+ event.preventDefault()
284
+ const submission = latestSubmission + 1
285
+ latestSubmission = submission
286
+ activeSubmissions += 1
287
+ const data = createFormData(form, submitter)
288
+ publishFormStatus(store, {
289
+ pending: true,
290
+ data,
291
+ method: 'post',
292
+ action,
293
+ })
294
+ const frame = createActionFrame()
295
+ let result: unknown
296
+ const previousFrame = activeActionFrame
297
+ activeActionFrame = frame
298
+ try {
299
+ result = executeFormAction(action, data)
300
+ } catch (error) {
301
+ activeActionFrame = previousFrame
302
+ finishFormAction(submission, false, frame, error)
303
+ return
304
+ }
305
+ activeActionFrame = previousFrame
306
+ void Promise.resolve(result).then(
307
+ () => finishFormAction(submission, true, frame),
308
+ (error: unknown) => finishFormAction(submission, false, frame, error),
309
+ )
310
+ }
311
+ const finishFormAction = (
312
+ submission: number,
313
+ succeeded: boolean,
314
+ frame: ActionFrame,
315
+ error?: unknown,
316
+ ): void => {
317
+ if (task.disposed()) return
318
+ runActionTransition(
319
+ () => settleActionFrame(frame),
320
+ () => {
321
+ activeSubmissions -= 1
322
+ if (submission === latestSubmission) latestSucceeded = succeeded
323
+ if (activeSubmissions === 0) {
324
+ if (latestSucceeded) resetActionForm(form)
325
+ publishFormStatus(store, defaultFormStatus)
326
+ }
327
+ if (!succeeded) task.report(error)
328
+ },
329
+ )
330
+ }
331
+ form.addEventListener('submit', submit)
332
+ registerCompiledCleanup(() => form.removeEventListener('submit', submit))
333
+ return form
334
+ }
335
+
336
+ export function compiledFormAction<Value>(value: Value): Value {
337
+ ensureActionDom()
338
+ return value
339
+ }
340
+
341
+ export function useActionState(): never {
342
+ throw new Error(DEV ? 'useActionState requires compiler lowering' : 'V036')
343
+ }
344
+
345
+ export function useOptimistic(): never {
346
+ throw new Error(DEV ? 'useOptimistic requires compiler lowering' : 'V037')
347
+ }
348
+
349
+ export function useFormStatus(): never {
350
+ throw new Error(DEV ? 'useFormStatus requires compiler lowering' : 'V038')
351
+ }
352
+
353
+ function createActionFrame(): ActionFrame {
354
+ return { settlements: new Set() }
355
+ }
356
+
357
+ function settleActionFrame(frame: ActionFrame): void {
358
+ for (const settle of frame.settlements) settle()
359
+ if (!registerTransitionFinalizer(() => frame.settlements.clear())) frame.settlements.clear()
360
+ }
361
+
362
+ function runActionTransition(stage: () => void, complete: () => void): void {
363
+ let committed = false
364
+ startIndependentTransition(() => {
365
+ stage()
366
+ registerTransitionFinalizer(() => {
367
+ committed = true
368
+ })
369
+ registerTransitionSettlement(() => {
370
+ if (committed) {
371
+ complete()
372
+ } else {
373
+ queueMicrotask(() => runActionTransition(stage, complete))
374
+ }
375
+ })
376
+ })
377
+ }
378
+
379
+ function ensureActionDom(): void {
380
+ if (actionDomInstalled) return
381
+ actionDomInstalled = true
382
+ installDomPropHandler((element, name, value) => {
383
+ const valid =
384
+ (name === 'action' && element instanceof HTMLFormElement) ||
385
+ (name === 'formAction' &&
386
+ (element instanceof HTMLButtonElement || element instanceof HTMLInputElement))
387
+ if (!valid) return false
388
+ if (typeof value !== 'function') {
389
+ functionActions.delete(element)
390
+ return false
391
+ }
392
+ functionActions.set(element, value as FunctionFormAction)
393
+ element.removeAttribute(name === 'action' ? 'action' : 'formaction')
394
+ return true
395
+ })
396
+ }
397
+
398
+ function executeFormAction(action: FunctionFormAction, data: FormData): unknown {
399
+ const specialized = (action as Partial<ActionDispatch<FormData>>)[FORM_ACTION_EXECUTE]
400
+ return specialized === undefined ? action(data) : specialized(data)
401
+ }
402
+
403
+ function createFormData(form: HTMLFormElement, submitter: HTMLElement | null): FormData {
404
+ return submitter === null ? new FormData(form) : new FormData(form, submitter)
405
+ }
406
+
407
+ function publishFormStatus(store: FormStatusStore, status: FormStatus): void {
408
+ store.current = status
409
+ for (const listener of store.listeners) listener()
410
+ }
411
+
412
+ function normalizeChildren(value: unknown): DirectChild[] {
413
+ if (value === undefined) return []
414
+ return (Array.isArray(value) ? value : [value]) as DirectChild[]
415
+ }