rask-ui 0.1.0 → 0.2.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 (234) hide show
  1. package/dist/component.d.ts +17 -0
  2. package/dist/component.d.ts.map +1 -1
  3. package/dist/component.js +4 -2
  4. package/dist/createAsync.d.ts +23 -0
  5. package/dist/createAsync.d.ts.map +1 -1
  6. package/dist/createAsync.js +23 -0
  7. package/dist/createAsync.test.d.ts +2 -0
  8. package/dist/createAsync.test.d.ts.map +1 -0
  9. package/dist/createAsync.test.js +110 -0
  10. package/dist/createContext.d.ts +26 -2
  11. package/dist/createContext.d.ts.map +1 -1
  12. package/dist/createContext.js +31 -5
  13. package/dist/createContext.test.d.ts +2 -0
  14. package/dist/createContext.test.d.ts.map +1 -0
  15. package/dist/createContext.test.js +136 -0
  16. package/dist/createMutation.d.ts +23 -0
  17. package/dist/createMutation.d.ts.map +1 -1
  18. package/dist/createMutation.js +23 -0
  19. package/dist/createMutation.test.d.ts +2 -0
  20. package/dist/createMutation.test.d.ts.map +1 -0
  21. package/dist/createMutation.test.js +168 -0
  22. package/dist/createQuery.d.ts +23 -0
  23. package/dist/createQuery.d.ts.map +1 -1
  24. package/dist/createQuery.js +23 -0
  25. package/dist/createQuery.test.d.ts +2 -0
  26. package/dist/createQuery.test.d.ts.map +1 -0
  27. package/dist/createQuery.test.js +156 -0
  28. package/dist/createRef.test.d.ts +2 -0
  29. package/dist/createRef.test.d.ts.map +1 -0
  30. package/dist/createRef.test.js +80 -0
  31. package/dist/createState.d.ts +24 -0
  32. package/dist/createState.d.ts.map +1 -1
  33. package/dist/createState.js +24 -0
  34. package/dist/createState.test.d.ts +2 -0
  35. package/dist/createState.test.d.ts.map +1 -0
  36. package/dist/createState.test.js +111 -0
  37. package/dist/createView.d.ts +54 -0
  38. package/dist/createView.d.ts.map +1 -0
  39. package/dist/createView.js +68 -0
  40. package/dist/createView.test.d.ts +2 -0
  41. package/dist/createView.test.d.ts.map +1 -0
  42. package/dist/createView.test.js +203 -0
  43. package/dist/error.d.ts.map +1 -1
  44. package/dist/error.js +15 -2
  45. package/dist/error.test.d.ts +2 -0
  46. package/dist/error.test.d.ts.map +1 -0
  47. package/dist/error.test.js +144 -0
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +3 -2
  51. package/dist/integration.test.d.ts +2 -0
  52. package/dist/integration.test.d.ts.map +1 -0
  53. package/dist/integration.test.js +155 -0
  54. package/dist/jsx-dev-runtime.d.ts +3 -3
  55. package/dist/jsx-dev-runtime.d.ts.map +1 -1
  56. package/dist/jsx-dev-runtime.js +2 -2
  57. package/dist/jsx-runtime.d.ts +1 -4
  58. package/dist/jsx-runtime.d.ts.map +1 -1
  59. package/dist/jsx-runtime.js +3 -14
  60. package/dist/observation.d.ts +1 -0
  61. package/dist/observation.d.ts.map +1 -1
  62. package/dist/observation.js +5 -0
  63. package/dist/observation.test.d.ts +2 -0
  64. package/dist/observation.test.d.ts.map +1 -0
  65. package/dist/observation.test.js +150 -0
  66. package/dist/render-test.d.ts +2 -0
  67. package/dist/render-test.d.ts.map +1 -0
  68. package/dist/render-test.js +21 -0
  69. package/dist/render.d.ts +1 -1
  70. package/dist/render.d.ts.map +1 -1
  71. package/dist/render.js +13 -1
  72. package/dist/test-setup.d.ts +16 -0
  73. package/dist/test-setup.d.ts.map +1 -0
  74. package/dist/test-setup.js +39 -0
  75. package/dist/tests/class.test.d.ts +2 -0
  76. package/dist/tests/class.test.d.ts.map +1 -0
  77. package/dist/tests/class.test.js +143 -0
  78. package/dist/tests/complex-rendering.test.d.ts +2 -0
  79. package/dist/tests/complex-rendering.test.d.ts.map +1 -0
  80. package/dist/tests/complex-rendering.test.js +400 -0
  81. package/dist/tests/component.cleanup.test.d.ts +2 -0
  82. package/dist/tests/component.cleanup.test.d.ts.map +1 -0
  83. package/dist/tests/component.cleanup.test.js +325 -0
  84. package/dist/tests/component.counter.test.d.ts +2 -0
  85. package/dist/tests/component.counter.test.d.ts.map +1 -0
  86. package/dist/tests/component.counter.test.js +124 -0
  87. package/dist/tests/component.interaction.test.d.ts +2 -0
  88. package/dist/tests/component.interaction.test.d.ts.map +1 -0
  89. package/dist/tests/component.interaction.test.js +73 -0
  90. package/dist/tests/component.props.test.d.ts +2 -0
  91. package/dist/tests/component.props.test.d.ts.map +1 -0
  92. package/dist/tests/component.props.test.js +88 -0
  93. package/dist/tests/component.return-types.test.d.ts +2 -0
  94. package/dist/tests/component.return-types.test.d.ts.map +1 -0
  95. package/dist/tests/component.return-types.test.js +357 -0
  96. package/dist/tests/component.state.test.d.ts +2 -0
  97. package/dist/tests/component.state.test.d.ts.map +1 -0
  98. package/dist/tests/component.state.test.js +129 -0
  99. package/dist/tests/component.test.d.ts +2 -0
  100. package/dist/tests/component.test.d.ts.map +1 -0
  101. package/dist/tests/component.test.js +63 -0
  102. package/dist/tests/createAsync.test.d.ts +2 -0
  103. package/dist/tests/createAsync.test.d.ts.map +1 -0
  104. package/dist/tests/createAsync.test.js +110 -0
  105. package/dist/tests/createContext.test.d.ts +2 -0
  106. package/dist/tests/createContext.test.d.ts.map +1 -0
  107. package/dist/tests/createContext.test.js +141 -0
  108. package/dist/tests/createMutation.test.d.ts +2 -0
  109. package/dist/tests/createMutation.test.d.ts.map +1 -0
  110. package/dist/tests/createMutation.test.js +168 -0
  111. package/dist/tests/createQuery.test.d.ts +2 -0
  112. package/dist/tests/createQuery.test.d.ts.map +1 -0
  113. package/dist/tests/createQuery.test.js +156 -0
  114. package/dist/tests/createRef.test.d.ts +2 -0
  115. package/dist/tests/createRef.test.d.ts.map +1 -0
  116. package/dist/tests/createRef.test.js +84 -0
  117. package/dist/tests/createState.test.d.ts +2 -0
  118. package/dist/tests/createState.test.d.ts.map +1 -0
  119. package/dist/tests/createState.test.js +111 -0
  120. package/dist/tests/createView.test.d.ts +2 -0
  121. package/dist/tests/createView.test.d.ts.map +1 -0
  122. package/dist/tests/createView.test.js +203 -0
  123. package/dist/tests/edge-cases.test.d.ts +2 -0
  124. package/dist/tests/edge-cases.test.d.ts.map +1 -0
  125. package/dist/tests/edge-cases.test.js +637 -0
  126. package/dist/tests/error-no-boundary.test.d.ts +2 -0
  127. package/dist/tests/error-no-boundary.test.d.ts.map +1 -0
  128. package/dist/tests/error-no-boundary.test.js +174 -0
  129. package/dist/tests/error.test.d.ts +2 -0
  130. package/dist/tests/error.test.d.ts.map +1 -0
  131. package/dist/tests/error.test.js +199 -0
  132. package/dist/tests/fragment.test.d.ts +2 -0
  133. package/dist/tests/fragment.test.d.ts.map +1 -0
  134. package/dist/tests/fragment.test.js +618 -0
  135. package/dist/tests/integration.test.d.ts +2 -0
  136. package/dist/tests/integration.test.d.ts.map +1 -0
  137. package/dist/tests/integration.test.js +192 -0
  138. package/dist/tests/keys.test.d.ts +2 -0
  139. package/dist/tests/keys.test.d.ts.map +1 -0
  140. package/dist/tests/keys.test.js +293 -0
  141. package/dist/tests/mount.test.d.ts +2 -0
  142. package/dist/tests/mount.test.d.ts.map +1 -0
  143. package/dist/tests/mount.test.js +91 -0
  144. package/dist/tests/observation.test.d.ts +2 -0
  145. package/dist/tests/observation.test.d.ts.map +1 -0
  146. package/dist/tests/observation.test.js +150 -0
  147. package/dist/tests/patch.test.d.ts +2 -0
  148. package/dist/tests/patch.test.d.ts.map +1 -0
  149. package/dist/tests/patch.test.js +498 -0
  150. package/dist/tests/patchChildren.test.d.ts +2 -0
  151. package/dist/tests/patchChildren.test.d.ts.map +1 -0
  152. package/dist/tests/patchChildren.test.js +387 -0
  153. package/dist/tests/primitives.test.d.ts +2 -0
  154. package/dist/tests/primitives.test.d.ts.map +1 -0
  155. package/dist/tests/primitives.test.js +132 -0
  156. package/dist/vdom/AbstractVNode.d.ts +22 -0
  157. package/dist/vdom/AbstractVNode.d.ts.map +1 -0
  158. package/dist/vdom/AbstractVNode.js +106 -0
  159. package/dist/vdom/ComponentVNode.d.ts +48 -0
  160. package/dist/vdom/ComponentVNode.d.ts.map +1 -0
  161. package/dist/vdom/ComponentVNode.js +209 -0
  162. package/dist/vdom/ElementVNode.d.ts +24 -0
  163. package/dist/vdom/ElementVNode.d.ts.map +1 -0
  164. package/dist/vdom/ElementVNode.js +126 -0
  165. package/dist/vdom/FragmentVNode.d.ts +13 -0
  166. package/dist/vdom/FragmentVNode.d.ts.map +1 -0
  167. package/dist/vdom/FragmentVNode.js +34 -0
  168. package/dist/vdom/RootVNode.d.ts +22 -0
  169. package/dist/vdom/RootVNode.d.ts.map +1 -0
  170. package/dist/vdom/RootVNode.js +55 -0
  171. package/dist/vdom/TextVNode.d.ts +11 -0
  172. package/dist/vdom/TextVNode.d.ts.map +1 -0
  173. package/dist/vdom/TextVNode.js +32 -0
  174. package/dist/vdom/class.test.d.ts +2 -0
  175. package/dist/vdom/class.test.d.ts.map +1 -0
  176. package/dist/vdom/class.test.js +143 -0
  177. package/dist/vdom/complex-rendering.test.d.ts +2 -0
  178. package/dist/vdom/complex-rendering.test.d.ts.map +1 -0
  179. package/dist/vdom/complex-rendering.test.js +400 -0
  180. package/dist/vdom/component.cleanup.test.d.ts +2 -0
  181. package/dist/vdom/component.cleanup.test.d.ts.map +1 -0
  182. package/dist/vdom/component.cleanup.test.js +323 -0
  183. package/dist/vdom/component.counter.test.d.ts +2 -0
  184. package/dist/vdom/component.counter.test.d.ts.map +1 -0
  185. package/dist/vdom/component.counter.test.js +124 -0
  186. package/dist/vdom/component.interaction.test.d.ts +2 -0
  187. package/dist/vdom/component.interaction.test.d.ts.map +1 -0
  188. package/dist/vdom/component.interaction.test.js +73 -0
  189. package/dist/vdom/component.props.test.d.ts +2 -0
  190. package/dist/vdom/component.props.test.d.ts.map +1 -0
  191. package/dist/vdom/component.props.test.js +88 -0
  192. package/dist/vdom/component.return-types.test.d.ts +2 -0
  193. package/dist/vdom/component.return-types.test.d.ts.map +1 -0
  194. package/dist/vdom/component.return-types.test.js +357 -0
  195. package/dist/vdom/component.state.test.d.ts +2 -0
  196. package/dist/vdom/component.state.test.d.ts.map +1 -0
  197. package/dist/vdom/component.state.test.js +129 -0
  198. package/dist/vdom/component.test.d.ts +2 -0
  199. package/dist/vdom/component.test.d.ts.map +1 -0
  200. package/dist/vdom/component.test.js +63 -0
  201. package/dist/vdom/dom-utils.d.ts +9 -0
  202. package/dist/vdom/dom-utils.d.ts.map +1 -0
  203. package/dist/vdom/dom-utils.js +74 -0
  204. package/dist/vdom/edge-cases.test.d.ts +2 -0
  205. package/dist/vdom/edge-cases.test.d.ts.map +1 -0
  206. package/dist/vdom/edge-cases.test.js +637 -0
  207. package/dist/vdom/fragment.test.d.ts +2 -0
  208. package/dist/vdom/fragment.test.d.ts.map +1 -0
  209. package/dist/vdom/fragment.test.js +618 -0
  210. package/dist/vdom/index.d.ts +10 -0
  211. package/dist/vdom/index.d.ts.map +1 -0
  212. package/dist/vdom/index.js +26 -0
  213. package/dist/vdom/keys.test.d.ts +2 -0
  214. package/dist/vdom/keys.test.d.ts.map +1 -0
  215. package/dist/vdom/keys.test.js +293 -0
  216. package/dist/vdom/mount.test.d.ts +2 -0
  217. package/dist/vdom/mount.test.d.ts.map +1 -0
  218. package/dist/vdom/mount.test.js +91 -0
  219. package/dist/vdom/patch.test.d.ts +2 -0
  220. package/dist/vdom/patch.test.d.ts.map +1 -0
  221. package/dist/vdom/patch.test.js +498 -0
  222. package/dist/vdom/patchChildren.test.d.ts +2 -0
  223. package/dist/vdom/patchChildren.test.d.ts.map +1 -0
  224. package/dist/vdom/patchChildren.test.js +392 -0
  225. package/dist/vdom/primitives.test.d.ts +2 -0
  226. package/dist/vdom/primitives.test.d.ts.map +1 -0
  227. package/dist/vdom/primitives.test.js +132 -0
  228. package/dist/vdom/types.d.ts +8 -0
  229. package/dist/vdom/types.d.ts.map +1 -0
  230. package/dist/vdom/types.js +1 -0
  231. package/dist/vdom/utils.d.ts +6 -0
  232. package/dist/vdom/utils.d.ts.map +1 -0
  233. package/dist/vdom/utils.js +63 -0
  234. package/package.json +1 -4
@@ -1,6 +1,23 @@
1
1
  import { type VNode } from "snabbdom";
2
2
  import { Observer } from "./observation";
3
3
  import { ChildNode } from "./render";
4
+ /**
5
+ * Component function type. Components receive reactive props that should not be destructured.
6
+ *
7
+ * @warning **Do not destructure props!** Props are wrapped in a reactive proxy, and destructuring
8
+ * breaks reactivity. This is the same rule as Solid.js.
9
+ *
10
+ * @example
11
+ * // ❌ Bad - destructuring props loses reactivity
12
+ * function MyComponent({ count, name }) {
13
+ * return () => <div>{count} {name}</div>; // Won't update!
14
+ * }
15
+ *
16
+ * // ✅ Good - access props directly in render
17
+ * function MyComponent(props) {
18
+ * return () => <div>{props.count} {props.name}</div>; // Reactive!
19
+ * }
20
+ */
4
21
  export type Component<P> = ((props: P) => () => VNode) | (() => () => VNode);
5
22
  export type ComponentInstance = {
6
23
  parent: ComponentInstance | null;
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,KAAK,EAAkB,MAAM,UAAU,CAAC;AAE7D,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AACrE,OAAO,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAE7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAIF,wBAAgB,mBAAmB,sBAElC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAQrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAQvC;AAwGD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,4BAOlC"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,KAAK,EAAkB,MAAM,UAAU,CAAC;AAE7D,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AACrE,OAAO,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAGjD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAE7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAIF,wBAAgB,mBAAmB,sBAElC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAQrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAQvC;AAuGD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,4BAWlC"}
package/dist/component.js CHANGED
@@ -26,7 +26,6 @@ const hook = {
26
26
  vnode.data.componentInstance.onMounts.forEach((cb) => cb());
27
27
  },
28
28
  destroy(vnode) {
29
- componentStack.shift();
30
29
  vnode.data.componentInstance.onCleanups.forEach((cb) => cb());
31
30
  },
32
31
  prepatch(oldVnode, thunk) {
@@ -69,7 +68,7 @@ const hook = {
69
68
  }, Array.isArray(renderResult) ? renderResult : [renderResult]);
70
69
  };
71
70
  const instance = {
72
- parent: getCurrentComponent(),
71
+ parent: thunk.data.parentComponent || null,
73
72
  component,
74
73
  contexts: null,
75
74
  onMounts: [],
@@ -116,6 +115,9 @@ const hook = {
116
115
  export function createComponent(component, props, children) {
117
116
  const thunkNode = thunk("component", props.key, component, [props, children]);
118
117
  Object.assign(thunkNode.data.hook, hook);
118
+ // Capture the parent component at vnode creation time (during render)
119
+ // rather than at init time, to ensure correct parent relationships
120
+ thunkNode.data.parentComponent = getCurrentComponent();
119
121
  return thunkNode;
120
122
  }
121
123
  function copyToThunk(vnode, thunk) {
@@ -11,6 +11,29 @@ type AsyncState<T> = {
11
11
  value: null;
12
12
  error: string;
13
13
  };
14
+ /**
15
+ * Creates a reactive async state that tracks the lifecycle of a promise.
16
+ *
17
+ * @warning **Do not destructure the returned reactive object!** Destructuring breaks reactivity.
18
+ * Access properties directly in your render function instead.
19
+ *
20
+ * @example
21
+ * // ❌ Bad - destructuring loses reactivity
22
+ * function Component() {
23
+ * const async = createAsync(fetchData());
24
+ * const { isPending, value, error } = async; // Don't do this!
25
+ * return () => <div>{isPending ? "Loading..." : value}</div>; // Won't update!
26
+ * }
27
+ *
28
+ * // ✅ Good - access properties directly
29
+ * function Component() {
30
+ * const async = createAsync(fetchData());
31
+ * return () => <div>{async.isPending ? "Loading..." : async.value}</div>;
32
+ * }
33
+ *
34
+ * @param promise - The promise to track
35
+ * @returns Reactive state with isPending, value, and error properties
36
+ */
14
37
  export declare function createAsync<T>(promise: Promise<T>): AsyncState<T>;
15
38
  export {};
16
39
  //# sourceMappingURL=createAsync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createAsync.d.ts","sourceRoot":"","sources":["../src/createAsync.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,CAAC,CAAC,IACb;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iBAwBjD"}
1
+ {"version":3,"file":"createAsync.d.ts","sourceRoot":"","sources":["../src/createAsync.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,CAAC,CAAC,IACb;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iBAwBjD"}
@@ -1,4 +1,27 @@
1
1
  import { createState } from "./createState";
2
+ /**
3
+ * Creates a reactive async state that tracks the lifecycle of a promise.
4
+ *
5
+ * @warning **Do not destructure the returned reactive object!** Destructuring breaks reactivity.
6
+ * Access properties directly in your render function instead.
7
+ *
8
+ * @example
9
+ * // ❌ Bad - destructuring loses reactivity
10
+ * function Component() {
11
+ * const async = createAsync(fetchData());
12
+ * const { isPending, value, error } = async; // Don't do this!
13
+ * return () => <div>{isPending ? "Loading..." : value}</div>; // Won't update!
14
+ * }
15
+ *
16
+ * // ✅ Good - access properties directly
17
+ * function Component() {
18
+ * const async = createAsync(fetchData());
19
+ * return () => <div>{async.isPending ? "Loading..." : async.value}</div>;
20
+ * }
21
+ *
22
+ * @param promise - The promise to track
23
+ * @returns Reactive state with isPending, value, and error properties
24
+ */
2
25
  export function createAsync(promise) {
3
26
  const state = createState({
4
27
  isPending: true,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createAsync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createAsync.test.d.ts","sourceRoot":"","sources":["../src/createAsync.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createAsync } from './createAsync';
3
+ describe('createAsync', () => {
4
+ it('should start in pending state', () => {
5
+ const promise = new Promise(() => { });
6
+ const async = createAsync(promise);
7
+ expect(async.isPending).toBe(true);
8
+ expect(async.value).toBeNull();
9
+ expect(async.error).toBeNull();
10
+ });
11
+ it('should resolve to value state on success', async () => {
12
+ const promise = Promise.resolve('success');
13
+ const async = createAsync(promise);
14
+ expect(async.isPending).toBe(true);
15
+ await promise;
16
+ // Wait for state update
17
+ await new Promise((resolve) => setTimeout(resolve, 0));
18
+ expect(async.isPending).toBe(false);
19
+ expect(async.value).toBe('success');
20
+ expect(async.error).toBeNull();
21
+ });
22
+ it('should resolve to error state on rejection', async () => {
23
+ const promise = Promise.reject(new Error('failed'));
24
+ const async = createAsync(promise);
25
+ expect(async.isPending).toBe(true);
26
+ try {
27
+ await promise;
28
+ }
29
+ catch {
30
+ // Ignore
31
+ }
32
+ // Wait for state update
33
+ await new Promise((resolve) => setTimeout(resolve, 0));
34
+ expect(async.isPending).toBe(false);
35
+ expect(async.value).toBeNull();
36
+ expect(async.error).toContain('failed');
37
+ });
38
+ it('should handle numeric values', async () => {
39
+ const promise = Promise.resolve(42);
40
+ const async = createAsync(promise);
41
+ await promise;
42
+ await new Promise((resolve) => setTimeout(resolve, 0));
43
+ expect(async.value).toBe(42);
44
+ });
45
+ it('should handle object values', async () => {
46
+ const data = { id: 1, name: 'Test' };
47
+ const promise = Promise.resolve(data);
48
+ const async = createAsync(promise);
49
+ await promise;
50
+ await new Promise((resolve) => setTimeout(resolve, 0));
51
+ expect(async.value).toEqual(data);
52
+ });
53
+ it('should handle array values', async () => {
54
+ const data = [1, 2, 3, 4, 5];
55
+ const promise = Promise.resolve(data);
56
+ const async = createAsync(promise);
57
+ await promise;
58
+ await new Promise((resolve) => setTimeout(resolve, 0));
59
+ expect(async.value).toEqual(data);
60
+ });
61
+ it('should convert error to string', async () => {
62
+ const promise = Promise.reject('string error');
63
+ const async = createAsync(promise);
64
+ try {
65
+ await promise;
66
+ }
67
+ catch {
68
+ // Ignore
69
+ }
70
+ await new Promise((resolve) => setTimeout(resolve, 0));
71
+ expect(typeof async.error).toBe('string');
72
+ expect(async.error).toBe('string error');
73
+ });
74
+ it('should handle error objects', async () => {
75
+ const error = new Error('Something went wrong');
76
+ const promise = Promise.reject(error);
77
+ const async = createAsync(promise);
78
+ try {
79
+ await promise;
80
+ }
81
+ catch {
82
+ // Ignore
83
+ }
84
+ await new Promise((resolve) => setTimeout(resolve, 0));
85
+ expect(async.error).toContain('Something went wrong');
86
+ });
87
+ it('should create reactive state', async () => {
88
+ const promise = new Promise((resolve) => {
89
+ setTimeout(() => resolve('delayed'), 10);
90
+ });
91
+ const async = createAsync(promise);
92
+ expect(async.isPending).toBe(true);
93
+ await promise;
94
+ await new Promise((resolve) => setTimeout(resolve, 20));
95
+ expect(async.isPending).toBe(false);
96
+ expect(async.value).toBe('delayed');
97
+ });
98
+ it('should handle immediate resolution', async () => {
99
+ const async = createAsync(Promise.resolve('immediate'));
100
+ await new Promise((resolve) => setTimeout(resolve, 0));
101
+ expect(async.isPending).toBe(false);
102
+ expect(async.value).toBe('immediate');
103
+ });
104
+ it('should handle immediate rejection', async () => {
105
+ const async = createAsync(Promise.reject('immediate error'));
106
+ await new Promise((resolve) => setTimeout(resolve, 0));
107
+ expect(async.isPending).toBe(false);
108
+ expect(async.error).toBe('immediate error');
109
+ });
110
+ });
@@ -1,5 +1,29 @@
1
- export declare function createContext<T extends object>(): {
2
- set(value: T): void;
1
+ /**
2
+ * Creates a context object for providing and consuming values across component trees.
3
+ *
4
+ * @warning **Do not destructure context values returned by context.get()!** The returned
5
+ * value may be a reactive object, and destructuring breaks reactivity.
6
+ *
7
+ * @example
8
+ * // ❌ Bad - destructuring context value
9
+ * const ThemeContext = createContext<{ color: string }>();
10
+ *
11
+ * function Consumer() {
12
+ * const theme = ThemeContext.get();
13
+ * const { color } = theme; // Don't do this!
14
+ * return () => <div style={{ color }}>Text</div>; // Won't update!
15
+ * }
16
+ *
17
+ * // ✅ Good - access properties directly
18
+ * function Consumer() {
19
+ * const theme = ThemeContext.get();
20
+ * return () => <div style={{ color: theme.color }}>Text</div>;
21
+ * }
22
+ *
23
+ * @returns Context object with inject() and get() methods
24
+ */
25
+ export declare function createContext<T>(): {
26
+ inject(value: T): void;
3
27
  get(): T;
4
28
  };
5
29
  //# sourceMappingURL=createContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createContext.d.ts","sourceRoot":"","sources":["../src/createContext.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM;eAE/B,CAAC;WAaL,CAAC;EAmBX"}
1
+ {"version":3,"file":"createContext.d.ts","sourceRoot":"","sources":["../src/createContext.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,CAAC;kBAEb,CAAC;WAaR,CAAC;EAoBX"}
@@ -1,10 +1,35 @@
1
- import { getCurrentComponent } from "./component";
1
+ import { getCurrentComponent, } from "./vdom/ComponentVNode";
2
+ import { findComponentVNode } from "./vdom/utils";
3
+ /**
4
+ * Creates a context object for providing and consuming values across component trees.
5
+ *
6
+ * @warning **Do not destructure context values returned by context.get()!** The returned
7
+ * value may be a reactive object, and destructuring breaks reactivity.
8
+ *
9
+ * @example
10
+ * // ❌ Bad - destructuring context value
11
+ * const ThemeContext = createContext<{ color: string }>();
12
+ *
13
+ * function Consumer() {
14
+ * const theme = ThemeContext.get();
15
+ * const { color } = theme; // Don't do this!
16
+ * return () => <div style={{ color }}>Text</div>; // Won't update!
17
+ * }
18
+ *
19
+ * // ✅ Good - access properties directly
20
+ * function Consumer() {
21
+ * const theme = ThemeContext.get();
22
+ * return () => <div style={{ color: theme.color }}>Text</div>;
23
+ * }
24
+ *
25
+ * @returns Context object with inject() and get() methods
26
+ */
2
27
  export function createContext() {
3
28
  const context = {
4
- set(value) {
29
+ inject(value) {
5
30
  const currentComponent = getCurrentComponent();
6
31
  if (!currentComponent) {
7
- throw new Error("You can not set context out component setup");
32
+ throw new Error("You can not inject context outside component setup");
8
33
  }
9
34
  if (!currentComponent.contexts) {
10
35
  currentComponent.contexts = new Map();
@@ -14,13 +39,14 @@ export function createContext() {
14
39
  get() {
15
40
  let currentComponent = getCurrentComponent();
16
41
  if (!currentComponent) {
17
- throw new Error("You can not set context out component setup");
42
+ throw new Error("You can not get context outside component setup");
18
43
  }
19
44
  while (currentComponent) {
20
45
  if (currentComponent.contexts?.has(context)) {
21
46
  return currentComponent.contexts.get(context);
22
47
  }
23
- currentComponent = currentComponent.parent;
48
+ const componentNode = findComponentVNode(currentComponent.parent);
49
+ currentComponent = componentNode?.instance ?? null;
24
50
  }
25
51
  throw new Error("Could not find context in parent components");
26
52
  },
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createContext.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createContext.test.d.ts","sourceRoot":"","sources":["../src/createContext.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,136 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "./jsx-runtime";
2
+ import { describe, it, expect } from "vitest";
3
+ import { createContext } from "./createContext";
4
+ import { render } from "./vdom";
5
+ describe("createContext", () => {
6
+ it("should create a context object", () => {
7
+ const context = createContext();
8
+ expect(context).toHaveProperty("inject");
9
+ expect(context).toHaveProperty("get");
10
+ });
11
+ it("should allow setting and getting context values", () => {
12
+ const ThemeContext = createContext();
13
+ function Parent() {
14
+ ThemeContext.inject({ theme: "dark" });
15
+ return () => _jsx(Child, {});
16
+ }
17
+ function Child() {
18
+ const theme = ThemeContext.get();
19
+ return () => _jsx("div", { children: theme.theme });
20
+ }
21
+ const container = document.createElement("div");
22
+ document.body.appendChild(container);
23
+ const vnode = render(_jsx(Parent, {}), container);
24
+ expect(vnode.elm.textContent).toContain("dark");
25
+ document.body.removeChild(vnode.elm);
26
+ });
27
+ it("should traverse parent components to find context", () => {
28
+ const ThemeContext = createContext();
29
+ function GrandParent() {
30
+ ThemeContext.inject({ theme: "light" });
31
+ return () => _jsx(Parent, {});
32
+ }
33
+ function Parent() {
34
+ return () => _jsx(Child, {});
35
+ }
36
+ function Child() {
37
+ const theme = ThemeContext.get();
38
+ return () => _jsx("div", { children: theme.theme });
39
+ }
40
+ const container = document.createElement("div");
41
+ document.body.appendChild(container);
42
+ const vnode = render(_jsx(GrandParent, {}), container);
43
+ expect(vnode.elm.textContent).toContain("light");
44
+ document.body.removeChild(vnode.elm);
45
+ });
46
+ it("should throw error when context is not found", () => {
47
+ const ThemeContext = createContext();
48
+ function Child() {
49
+ expect(() => {
50
+ ThemeContext.get();
51
+ }).toThrow("Could not find context in parent components");
52
+ return () => _jsx("div", { children: "Child" });
53
+ }
54
+ const container = document.createElement("div");
55
+ document.body.appendChild(container);
56
+ const vnode = render(_jsx(Child, {}), container);
57
+ document.body.removeChild(vnode.elm);
58
+ });
59
+ it("should throw error when setting context outside component", () => {
60
+ const ThemeContext = createContext();
61
+ expect(() => {
62
+ ThemeContext.inject({ theme: "dark" });
63
+ }).toThrow("No current root");
64
+ });
65
+ it("should throw error when getting context outside component", () => {
66
+ const ThemeContext = createContext();
67
+ expect(() => {
68
+ ThemeContext.get();
69
+ }).toThrow("No current root");
70
+ });
71
+ it("should allow overriding context in nested components", () => {
72
+ const ThemeContext = createContext();
73
+ function GrandParent() {
74
+ ThemeContext.inject({ theme: "light" });
75
+ return () => (_jsxs("div", { children: [_jsx(Parent, {}), _jsx(ChildOfGrandParent, {})] }));
76
+ }
77
+ function Parent() {
78
+ ThemeContext.inject({ theme: "dark" });
79
+ return () => _jsx(ChildOfParent, {});
80
+ }
81
+ function ChildOfParent() {
82
+ const theme = ThemeContext.get();
83
+ return () => _jsx("div", { class: "child-of-parent", children: theme.theme });
84
+ }
85
+ function ChildOfGrandParent() {
86
+ const theme = ThemeContext.get();
87
+ return () => _jsx("div", { class: "child-of-grandparent", children: theme.theme });
88
+ }
89
+ const container = document.createElement("div");
90
+ document.body.appendChild(container);
91
+ const vnode = render(_jsx(GrandParent, {}), container);
92
+ const childOfParent = document.querySelector(".child-of-parent");
93
+ const childOfGrandParent = document.querySelector(".child-of-grandparent");
94
+ expect(childOfParent?.textContent).toBe("dark");
95
+ expect(childOfGrandParent?.textContent).toBe("light");
96
+ document.body.removeChild(vnode.elm);
97
+ });
98
+ it("should support multiple different contexts", () => {
99
+ const ThemeContext = createContext();
100
+ const UserContext = createContext();
101
+ function Parent() {
102
+ ThemeContext.inject({ theme: "dark" });
103
+ UserContext.inject({ name: "Alice" });
104
+ return () => _jsx(Child, {});
105
+ }
106
+ function Child() {
107
+ const theme = ThemeContext.get();
108
+ const user = UserContext.get();
109
+ return () => (_jsxs("div", { children: [theme.theme, " - ", user.name] }));
110
+ }
111
+ const container = document.createElement("div");
112
+ document.body.appendChild(container);
113
+ const vnode = render(_jsx(Parent, {}), container);
114
+ expect(vnode.elm.textContent).toContain("dark - Alice");
115
+ document.body.removeChild(vnode.elm);
116
+ });
117
+ it("should handle context values of different types", () => {
118
+ const NumberContext = createContext();
119
+ const ArrayContext = createContext();
120
+ function Parent() {
121
+ NumberContext.inject(42);
122
+ ArrayContext.inject(["a", "b", "c"]);
123
+ return () => _jsx(Child, {});
124
+ }
125
+ function Child() {
126
+ const num = NumberContext.get();
127
+ const arr = ArrayContext.get();
128
+ return () => (_jsxs("div", { children: [num, " - ", arr.join(",")] }));
129
+ }
130
+ const container = document.createElement("div");
131
+ document.body.appendChild(container);
132
+ const vnode = render(_jsx(Parent, {}), container);
133
+ expect(vnode.elm.textContent).toContain("42 - a,b,c");
134
+ document.body.removeChild(vnode.elm);
135
+ });
136
+ });
@@ -15,6 +15,29 @@ export type Mutation<T> = MutationState<T> & {
15
15
  mutate(): void;
16
16
  mutate(params: T): void;
17
17
  };
18
+ /**
19
+ * Creates a reactive mutation that manages async mutations with loading and error states.
20
+ *
21
+ * @warning **Do not destructure the returned reactive object!** Destructuring breaks reactivity.
22
+ * Access properties directly in your render function instead.
23
+ *
24
+ * @example
25
+ * // ❌ Bad - destructuring loses reactivity
26
+ * function Component() {
27
+ * const mutation = createMutation((params) => updateUser(params));
28
+ * const { isPending, error } = mutation; // Don't do this!
29
+ * return () => <button disabled={isPending}>Save</button>; // Won't update!
30
+ * }
31
+ *
32
+ * // ✅ Good - access properties directly
33
+ * function Component() {
34
+ * const mutation = createMutation((params) => updateUser(params));
35
+ * return () => <button disabled={mutation.isPending}>Save</button>;
36
+ * }
37
+ *
38
+ * @param mutator - Function that performs the mutation and returns a promise
39
+ * @returns Reactive mutation object with isPending, params, error properties and mutate method
40
+ */
18
41
  export declare function createMutation<T>(mutator: (params: T) => Promise<T>): Mutation<T>;
19
42
  export {};
20
43
  //# sourceMappingURL=createMutation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createMutation.d.ts","sourceRoot":"","sources":["../src/createMutation.ts"],"names":[],"mappings":"AAEA,KAAK,aAAa,CAAC,CAAC,IAChB;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC3C,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,QAAQ,CAAC,CAAC,CAAC,CA0Db"}
1
+ {"version":3,"file":"createMutation.d.ts","sourceRoot":"","sources":["../src/createMutation.ts"],"names":[],"mappings":"AAEA,KAAK,aAAa,CAAC,CAAC,IAChB;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC3C,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,QAAQ,CAAC,CAAC,CAAC,CA0Db"}
@@ -1,4 +1,27 @@
1
1
  import { createState } from "./createState";
2
+ /**
3
+ * Creates a reactive mutation that manages async mutations with loading and error states.
4
+ *
5
+ * @warning **Do not destructure the returned reactive object!** Destructuring breaks reactivity.
6
+ * Access properties directly in your render function instead.
7
+ *
8
+ * @example
9
+ * // ❌ Bad - destructuring loses reactivity
10
+ * function Component() {
11
+ * const mutation = createMutation((params) => updateUser(params));
12
+ * const { isPending, error } = mutation; // Don't do this!
13
+ * return () => <button disabled={isPending}>Save</button>; // Won't update!
14
+ * }
15
+ *
16
+ * // ✅ Good - access properties directly
17
+ * function Component() {
18
+ * const mutation = createMutation((params) => updateUser(params));
19
+ * return () => <button disabled={mutation.isPending}>Save</button>;
20
+ * }
21
+ *
22
+ * @param mutator - Function that performs the mutation and returns a promise
23
+ * @returns Reactive mutation object with isPending, params, error properties and mutate method
24
+ */
2
25
  export function createMutation(mutator) {
3
26
  const state = createState({
4
27
  isPending: false,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createMutation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMutation.test.d.ts","sourceRoot":"","sources":["../src/createMutation.test.ts"],"names":[],"mappings":""}