kiru 0.54.4 → 1.0.1

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 (335) hide show
  1. package/dist/{appContext.d.ts → appHandle.d.ts} +4 -4
  2. package/dist/appHandle.d.ts.map +1 -0
  3. package/dist/{appContext.js → appHandle.js} +12 -9
  4. package/dist/appHandle.js.map +1 -0
  5. package/dist/components/derive.d.ts +10 -8
  6. package/dist/components/derive.d.ts.map +1 -1
  7. package/dist/components/derive.js +50 -47
  8. package/dist/components/derive.js.map +1 -1
  9. package/dist/components/index.d.ts +0 -1
  10. package/dist/components/index.d.ts.map +1 -1
  11. package/dist/components/index.js +0 -1
  12. package/dist/components/index.js.map +1 -1
  13. package/dist/components/lazy.d.ts.map +1 -1
  14. package/dist/components/lazy.js +5 -4
  15. package/dist/components/lazy.js.map +1 -1
  16. package/dist/components/portal.d.ts.map +1 -1
  17. package/dist/components/portal.js +2 -3
  18. package/dist/components/portal.js.map +1 -1
  19. package/dist/components/transition.d.ts +3 -2
  20. package/dist/components/transition.d.ts.map +1 -1
  21. package/dist/components/transition.js +29 -26
  22. package/dist/components/transition.js.map +1 -1
  23. package/dist/constants.d.ts +1 -5
  24. package/dist/constants.d.ts.map +1 -1
  25. package/dist/constants.js +1 -5
  26. package/dist/constants.js.map +1 -1
  27. package/dist/context.d.ts +1 -1
  28. package/dist/context.d.ts.map +1 -1
  29. package/dist/context.js +25 -19
  30. package/dist/context.js.map +1 -1
  31. package/dist/devtools.d.ts +7 -0
  32. package/dist/devtools.d.ts.map +1 -0
  33. package/dist/devtools.js +15 -0
  34. package/dist/devtools.js.map +1 -0
  35. package/dist/dom.d.ts.map +1 -1
  36. package/dist/dom.js +25 -58
  37. package/dist/dom.js.map +1 -1
  38. package/dist/globalContext.d.ts +15 -16
  39. package/dist/globalContext.d.ts.map +1 -1
  40. package/dist/globalContext.js +36 -46
  41. package/dist/globalContext.js.map +1 -1
  42. package/dist/globals.d.ts +1 -4
  43. package/dist/globals.d.ts.map +1 -1
  44. package/dist/globals.js +1 -4
  45. package/dist/globals.js.map +1 -1
  46. package/dist/headlessRender.d.ts +6 -0
  47. package/dist/headlessRender.d.ts.map +1 -0
  48. package/dist/{recursiveRender.js → headlessRender.js} +17 -16
  49. package/dist/headlessRender.js.map +1 -0
  50. package/dist/hmr.d.ts +5 -7
  51. package/dist/hmr.d.ts.map +1 -1
  52. package/dist/hmr.js +27 -32
  53. package/dist/hmr.js.map +1 -1
  54. package/dist/hooks/index.d.ts +3 -14
  55. package/dist/hooks/index.d.ts.map +1 -1
  56. package/dist/hooks/index.js +3 -14
  57. package/dist/hooks/index.js.map +1 -1
  58. package/dist/hooks/onBeforeMount.d.ts +9 -0
  59. package/dist/hooks/onBeforeMount.d.ts.map +1 -0
  60. package/dist/hooks/onBeforeMount.js +12 -0
  61. package/dist/hooks/onBeforeMount.js.map +1 -0
  62. package/dist/hooks/onCleanup.d.ts +8 -0
  63. package/dist/hooks/onCleanup.d.ts.map +1 -0
  64. package/dist/hooks/onCleanup.js +15 -0
  65. package/dist/hooks/onCleanup.js.map +1 -0
  66. package/dist/hooks/onMount.d.ts +9 -0
  67. package/dist/hooks/onMount.d.ts.map +1 -0
  68. package/dist/hooks/onMount.js +12 -0
  69. package/dist/hooks/onMount.js.map +1 -0
  70. package/dist/hooks/utils.d.ts +2 -62
  71. package/dist/hooks/utils.d.ts.map +1 -1
  72. package/dist/hooks/utils.js +22 -144
  73. package/dist/hooks/utils.js.map +1 -1
  74. package/dist/index.d.ts +8 -4
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +8 -6
  77. package/dist/index.js.map +1 -1
  78. package/dist/profiling.d.ts +15 -14
  79. package/dist/profiling.d.ts.map +1 -1
  80. package/dist/profiling.js +9 -4
  81. package/dist/profiling.js.map +1 -1
  82. package/dist/reconciler.d.ts.map +1 -1
  83. package/dist/reconciler.js +12 -25
  84. package/dist/reconciler.js.map +1 -1
  85. package/dist/ref.d.ts +4 -0
  86. package/dist/ref.d.ts.map +1 -0
  87. package/dist/ref.js +4 -0
  88. package/dist/ref.js.map +1 -0
  89. package/dist/renderToString.js +1 -1
  90. package/dist/renderToString.js.map +1 -1
  91. package/dist/router/context.d.ts.map +1 -1
  92. package/dist/router/context.js +1 -2
  93. package/dist/router/context.js.map +1 -1
  94. package/dist/router/fileRouter.d.ts +1 -1
  95. package/dist/router/fileRouter.d.ts.map +1 -1
  96. package/dist/router/fileRouter.js +17 -11
  97. package/dist/router/fileRouter.js.map +1 -1
  98. package/dist/router/fileRouterController.d.ts.map +1 -1
  99. package/dist/router/fileRouterController.js +68 -55
  100. package/dist/router/fileRouterController.js.map +1 -1
  101. package/dist/router/link.d.ts.map +1 -1
  102. package/dist/router/link.js +19 -23
  103. package/dist/router/link.js.map +1 -1
  104. package/dist/router/server/index.d.ts.map +1 -1
  105. package/dist/router/server/index.js +14 -11
  106. package/dist/router/server/index.js.map +1 -1
  107. package/dist/router/types.d.ts +11 -6
  108. package/dist/router/types.d.ts.map +1 -1
  109. package/dist/scheduler.d.ts +1 -0
  110. package/dist/scheduler.d.ts.map +1 -1
  111. package/dist/scheduler.js +65 -52
  112. package/dist/scheduler.js.map +1 -1
  113. package/dist/signals/base.d.ts +0 -1
  114. package/dist/signals/base.d.ts.map +1 -1
  115. package/dist/signals/base.js +13 -36
  116. package/dist/signals/base.js.map +1 -1
  117. package/dist/signals/computed.d.ts +0 -2
  118. package/dist/signals/computed.d.ts.map +1 -1
  119. package/dist/signals/computed.js +1 -40
  120. package/dist/signals/computed.js.map +1 -1
  121. package/dist/signals/effect.d.ts +15 -14
  122. package/dist/signals/effect.d.ts.map +1 -1
  123. package/dist/signals/effect.js +65 -37
  124. package/dist/signals/effect.js.map +1 -1
  125. package/dist/signals/globals.d.ts +0 -5
  126. package/dist/signals/globals.d.ts.map +1 -1
  127. package/dist/signals/globals.js +0 -6
  128. package/dist/signals/globals.js.map +1 -1
  129. package/dist/signals/index.d.ts +4 -4
  130. package/dist/signals/index.d.ts.map +1 -1
  131. package/dist/signals/index.js +4 -4
  132. package/dist/signals/index.js.map +1 -1
  133. package/dist/signals/{for.d.ts → jsx.d.ts} +8 -1
  134. package/dist/signals/jsx.d.ts.map +1 -0
  135. package/dist/signals/{for.js → jsx.js} +4 -1
  136. package/dist/signals/jsx.js.map +1 -0
  137. package/dist/signals/tracking.d.ts +23 -0
  138. package/dist/signals/tracking.d.ts.map +1 -0
  139. package/dist/signals/tracking.js +51 -0
  140. package/dist/signals/tracking.js.map +1 -0
  141. package/dist/signals/types.d.ts +1 -1
  142. package/dist/signals/types.d.ts.map +1 -1
  143. package/dist/signals/utils.d.ts +2 -1
  144. package/dist/signals/utils.d.ts.map +1 -1
  145. package/dist/signals/utils.js +9 -2
  146. package/dist/signals/utils.js.map +1 -1
  147. package/dist/ssr/client.d.ts +3 -3
  148. package/dist/ssr/client.d.ts.map +1 -1
  149. package/dist/ssr/client.js.map +1 -1
  150. package/dist/ssr/server.js +1 -1
  151. package/dist/ssr/server.js.map +1 -1
  152. package/dist/statefulPromise.d.ts +22 -0
  153. package/dist/statefulPromise.d.ts.map +1 -0
  154. package/dist/statefulPromise.js +94 -0
  155. package/dist/statefulPromise.js.map +1 -0
  156. package/dist/types.d.ts +35 -49
  157. package/dist/types.d.ts.map +1 -1
  158. package/dist/types.dom.d.ts +4 -7
  159. package/dist/types.dom.d.ts.map +1 -1
  160. package/dist/types.utils.d.ts +3 -4
  161. package/dist/types.utils.d.ts.map +1 -1
  162. package/dist/utils/vdom.d.ts +8 -6
  163. package/dist/utils/vdom.d.ts.map +1 -1
  164. package/dist/utils/vdom.js +32 -9
  165. package/dist/utils/vdom.js.map +1 -1
  166. package/dist/viewTransitions.d.ts +7 -0
  167. package/dist/viewTransitions.d.ts.map +1 -0
  168. package/dist/viewTransitions.js +71 -0
  169. package/dist/viewTransitions.js.map +1 -0
  170. package/package.json +1 -1
  171. package/src/{appContext.ts → appHandle.ts} +21 -16
  172. package/src/components/derive.ts +74 -69
  173. package/src/components/index.ts +0 -1
  174. package/src/components/lazy.ts +5 -4
  175. package/src/components/portal.ts +2 -3
  176. package/src/components/transition.ts +33 -35
  177. package/src/constants.ts +0 -8
  178. package/src/context.ts +30 -23
  179. package/src/devtools.ts +16 -0
  180. package/src/dom.ts +31 -65
  181. package/src/globalContext.ts +57 -74
  182. package/src/globals.ts +1 -5
  183. package/src/{recursiveRender.ts → headlessRender.ts} +18 -18
  184. package/src/hmr.ts +29 -38
  185. package/src/hooks/index.ts +3 -14
  186. package/src/hooks/onBeforeMount.ts +12 -0
  187. package/src/hooks/onCleanup.ts +15 -0
  188. package/src/hooks/onMount.ts +12 -0
  189. package/src/hooks/utils.ts +28 -238
  190. package/src/index.ts +14 -6
  191. package/src/profiling.ts +22 -20
  192. package/src/reconciler.ts +18 -30
  193. package/src/ref.ts +6 -0
  194. package/src/renderToString.ts +1 -1
  195. package/src/router/context.ts +1 -2
  196. package/src/router/fileRouter.ts +23 -13
  197. package/src/router/fileRouterController.ts +72 -64
  198. package/src/router/link.ts +11 -25
  199. package/src/router/server/index.ts +24 -13
  200. package/src/router/types.ts +15 -8
  201. package/src/scheduler.ts +74 -71
  202. package/src/signals/base.ts +12 -41
  203. package/src/signals/computed.ts +1 -62
  204. package/src/signals/effect.ts +95 -48
  205. package/src/signals/globals.ts +0 -7
  206. package/src/signals/index.ts +4 -4
  207. package/src/signals/{for.ts → jsx.ts} +10 -0
  208. package/src/signals/tracking.ts +69 -0
  209. package/src/signals/types.ts +1 -1
  210. package/src/signals/utils.ts +9 -1
  211. package/src/ssr/client.ts +4 -4
  212. package/src/ssr/server.ts +2 -2
  213. package/src/statefulPromise.ts +136 -0
  214. package/src/types.dom.ts +4 -8
  215. package/src/types.ts +44 -59
  216. package/src/types.utils.ts +3 -4
  217. package/src/utils/vdom.ts +44 -15
  218. package/src/viewTransitions.ts +88 -0
  219. package/dist/appContext.d.ts.map +0 -1
  220. package/dist/appContext.js.map +0 -1
  221. package/dist/components/memo.d.ts +0 -10
  222. package/dist/components/memo.d.ts.map +0 -1
  223. package/dist/components/memo.js +0 -23
  224. package/dist/components/memo.js.map +0 -1
  225. package/dist/form/index.d.ts +0 -4
  226. package/dist/form/index.d.ts.map +0 -1
  227. package/dist/form/index.js +0 -518
  228. package/dist/form/index.js.map +0 -1
  229. package/dist/form/types.d.ts +0 -122
  230. package/dist/form/types.d.ts.map +0 -1
  231. package/dist/form/types.js +0 -2
  232. package/dist/form/types.js.map +0 -1
  233. package/dist/form/utils.d.ts +0 -3
  234. package/dist/form/utils.d.ts.map +0 -1
  235. package/dist/form/utils.js +0 -16
  236. package/dist/form/utils.js.map +0 -1
  237. package/dist/hooks/useAsync.d.ts +0 -18
  238. package/dist/hooks/useAsync.d.ts.map +0 -1
  239. package/dist/hooks/useAsync.js +0 -96
  240. package/dist/hooks/useAsync.js.map +0 -1
  241. package/dist/hooks/useCallback.d.ts +0 -7
  242. package/dist/hooks/useCallback.d.ts.map +0 -1
  243. package/dist/hooks/useCallback.js +0 -30
  244. package/dist/hooks/useCallback.js.map +0 -1
  245. package/dist/hooks/useContext.d.ts +0 -7
  246. package/dist/hooks/useContext.d.ts.map +0 -1
  247. package/dist/hooks/useContext.js +0 -59
  248. package/dist/hooks/useContext.js.map +0 -1
  249. package/dist/hooks/useEffect.d.ts +0 -8
  250. package/dist/hooks/useEffect.d.ts.map +0 -1
  251. package/dist/hooks/useEffect.js +0 -34
  252. package/dist/hooks/useEffect.js.map +0 -1
  253. package/dist/hooks/useEffectEvent.d.ts +0 -8
  254. package/dist/hooks/useEffectEvent.d.ts.map +0 -1
  255. package/dist/hooks/useEffectEvent.js +0 -23
  256. package/dist/hooks/useEffectEvent.js.map +0 -1
  257. package/dist/hooks/useId.d.ts +0 -8
  258. package/dist/hooks/useId.d.ts.map +0 -1
  259. package/dist/hooks/useId.js +0 -35
  260. package/dist/hooks/useId.js.map +0 -1
  261. package/dist/hooks/useLayoutEffect.d.ts +0 -8
  262. package/dist/hooks/useLayoutEffect.d.ts.map +0 -1
  263. package/dist/hooks/useLayoutEffect.js +0 -34
  264. package/dist/hooks/useLayoutEffect.js.map +0 -1
  265. package/dist/hooks/useMemo.d.ts +0 -8
  266. package/dist/hooks/useMemo.d.ts.map +0 -1
  267. package/dist/hooks/useMemo.js +0 -31
  268. package/dist/hooks/useMemo.js.map +0 -1
  269. package/dist/hooks/usePromise.d.ts +0 -8
  270. package/dist/hooks/usePromise.d.ts.map +0 -1
  271. package/dist/hooks/usePromise.js +0 -90
  272. package/dist/hooks/usePromise.js.map +0 -1
  273. package/dist/hooks/useReducer.d.ts +0 -7
  274. package/dist/hooks/useReducer.d.ts.map +0 -1
  275. package/dist/hooks/useReducer.js +0 -44
  276. package/dist/hooks/useReducer.js.map +0 -1
  277. package/dist/hooks/useRef.d.ts +0 -10
  278. package/dist/hooks/useRef.d.ts.map +0 -1
  279. package/dist/hooks/useRef.js +0 -29
  280. package/dist/hooks/useRef.js.map +0 -1
  281. package/dist/hooks/useState.d.ts +0 -7
  282. package/dist/hooks/useState.d.ts.map +0 -1
  283. package/dist/hooks/useState.js +0 -54
  284. package/dist/hooks/useState.js.map +0 -1
  285. package/dist/hooks/useSyncExternalStore.d.ts +0 -8
  286. package/dist/hooks/useSyncExternalStore.d.ts.map +0 -1
  287. package/dist/hooks/useSyncExternalStore.js +0 -50
  288. package/dist/hooks/useSyncExternalStore.js.map +0 -1
  289. package/dist/hooks/useViewTransition.d.ts +0 -10
  290. package/dist/hooks/useViewTransition.d.ts.map +0 -1
  291. package/dist/hooks/useViewTransition.js +0 -27
  292. package/dist/hooks/useViewTransition.js.map +0 -1
  293. package/dist/recursiveRender.d.ts +0 -7
  294. package/dist/recursiveRender.d.ts.map +0 -1
  295. package/dist/recursiveRender.js.map +0 -1
  296. package/dist/signals/for.d.ts.map +0 -1
  297. package/dist/signals/for.js.map +0 -1
  298. package/dist/signals/watch.d.ts +0 -21
  299. package/dist/signals/watch.d.ts.map +0 -1
  300. package/dist/signals/watch.js +0 -86
  301. package/dist/signals/watch.js.map +0 -1
  302. package/dist/store.d.ts +0 -28
  303. package/dist/store.d.ts.map +0 -1
  304. package/dist/store.js +0 -166
  305. package/dist/store.js.map +0 -1
  306. package/dist/swr.d.ts +0 -63
  307. package/dist/swr.d.ts.map +0 -1
  308. package/dist/swr.js +0 -236
  309. package/dist/swr.js.map +0 -1
  310. package/dist/utils/promise.d.ts +0 -16
  311. package/dist/utils/promise.d.ts.map +0 -1
  312. package/dist/utils/promise.js +0 -14
  313. package/dist/utils/promise.js.map +0 -1
  314. package/src/components/memo.ts +0 -39
  315. package/src/form/index.ts +0 -676
  316. package/src/form/types.ts +0 -262
  317. package/src/form/utils.ts +0 -19
  318. package/src/hooks/useAsync.ts +0 -121
  319. package/src/hooks/useCallback.ts +0 -32
  320. package/src/hooks/useContext.ts +0 -79
  321. package/src/hooks/useEffect.ts +0 -40
  322. package/src/hooks/useEffectEvent.ts +0 -24
  323. package/src/hooks/useId.ts +0 -42
  324. package/src/hooks/useLayoutEffect.ts +0 -43
  325. package/src/hooks/useMemo.ts +0 -34
  326. package/src/hooks/usePromise.ts +0 -126
  327. package/src/hooks/useReducer.ts +0 -50
  328. package/src/hooks/useRef.ts +0 -40
  329. package/src/hooks/useState.ts +0 -62
  330. package/src/hooks/useSyncExternalStore.ts +0 -59
  331. package/src/hooks/useViewTransition.ts +0 -25
  332. package/src/signals/watch.ts +0 -139
  333. package/src/store.ts +0 -245
  334. package/src/swr.ts +0 -351
  335. package/src/utils/promise.ts +0 -26
@@ -0,0 +1,69 @@
1
+ import { node } from "../globals.js"
2
+ import { sideEffectsEnabled } from "../utils/index.js"
3
+ import { effectQueue } from "./globals.js"
4
+ import { tick } from "./utils.js"
5
+ import type { Signal } from "./base.js"
6
+ import type { SignalValues } from "./types.js"
7
+
8
+ export const tracking = {
9
+ enabled: true,
10
+ stack: new Array<Map<string, Signal<unknown>>>(),
11
+ current: function (): Map<string, Signal<unknown>> | undefined {
12
+ return this.stack[this.stack.length - 1]
13
+ },
14
+ }
15
+
16
+ type TrackedExecutionContext<T, Deps extends readonly Signal<unknown>[]> = {
17
+ id: string
18
+ subs: Map<string, Function>
19
+ fn: (...values: SignalValues<Deps>) => T
20
+ deps?: Deps
21
+ onDepChanged: () => void
22
+ }
23
+
24
+ /**
25
+ * Executes an effect function with dependency tracking enabled, and manages
26
+ * the effect's subscriptions.
27
+ * @param ctx - The execution context
28
+ * @returns The result of the effect function
29
+ */
30
+ export function executeWithTracking<T, Deps extends readonly Signal<unknown>[]>(
31
+ ctx: TrackedExecutionContext<T, Deps>
32
+ ): T {
33
+ const { id, subs, fn, deps = [], onDepChanged } = ctx
34
+ let observations: Map<string, Signal<unknown>> | undefined
35
+
36
+ effectQueue.delete(id)
37
+ const isServer = !!node.current && !sideEffectsEnabled()
38
+
39
+ if (!isServer) {
40
+ observations = new Map<string, Signal<unknown>>()
41
+ tracking.stack.push(observations)
42
+ }
43
+
44
+ const result = fn(...(deps.map((s) => s.value) as SignalValues<Deps>))
45
+
46
+ if (!isServer) {
47
+ for (const [id, unsub] of subs) {
48
+ if (observations!.has(id)) continue
49
+ unsub()
50
+ subs.delete(id)
51
+ }
52
+
53
+ const effect = () => {
54
+ if (!effectQueue.size) {
55
+ queueMicrotask(tick)
56
+ }
57
+ effectQueue.set(id, onDepChanged)
58
+ }
59
+
60
+ for (const [id, sig] of observations!) {
61
+ if (subs.has(id)) continue
62
+ const unsub = sig.subscribe(effect)
63
+ subs.set(id, unsub)
64
+ }
65
+ tracking.stack.pop()
66
+ }
67
+
68
+ return result
69
+ }
@@ -7,7 +7,7 @@ export type SignalSubscriber<T = unknown> = (value: T, prevValue?: T) => void
7
7
 
8
8
  export type SignalValues<T extends readonly Signal<unknown>[]> = {
9
9
  [I in keyof T]: T[I] extends Signal<infer V>
10
- ? V extends Kiru.StatefulPromise<infer P>
10
+ ? V extends Kiru.StatefulPromiseBase<infer P>
11
11
  ? P
12
12
  : V
13
13
  : never
@@ -1,13 +1,21 @@
1
1
  import { call } from "../utils/index.js"
2
2
  import { Signal } from "./base.js"
3
3
  import { effectQueue } from "./globals.js"
4
+ import { tracking } from "./tracking.js"
4
5
 
5
6
  export function unwrap<T>(value: T | Signal<T>, reactive = false): T {
6
7
  if (!Signal.isSignal(value)) return value
7
8
  return reactive ? value.value : value.peek()
8
9
  }
9
10
 
10
- export const tick = () => {
11
+ export function tick() {
11
12
  effectQueue.forEach(call)
12
13
  effectQueue.clear()
13
14
  }
15
+
16
+ export function untrack<T>(fn: () => T) {
17
+ tracking.enabled = false
18
+ const result = fn()
19
+ tracking.enabled = true
20
+ return result
21
+ }
package/src/ssr/client.ts CHANGED
@@ -1,9 +1,9 @@
1
- import type { AppContext, AppContextOptions } from "../appContext"
1
+ import type { AppHandle, AppHandleOptions } from "../appHandle"
2
2
  import { hydrationStack } from "../hydration.js"
3
3
  import { hydrationMode, renderMode } from "../globals.js"
4
4
  import { mount } from "../index.js"
5
5
 
6
- interface HydrationAppContextOptions extends AppContextOptions {
6
+ interface AppHandleHydrationOptions extends AppHandleOptions {
7
7
  /**
8
8
  * Configures the hydration mode
9
9
  * - "static": SSG
@@ -16,8 +16,8 @@ interface HydrationAppContextOptions extends AppContextOptions {
16
16
  export function hydrate(
17
17
  children: JSX.Element,
18
18
  container: HTMLElement,
19
- options?: HydrationAppContextOptions
20
- ): AppContext {
19
+ options?: AppHandleHydrationOptions
20
+ ): AppHandle {
21
21
  hydrationStack.clear()
22
22
 
23
23
  const prevRenderMode = renderMode.current
package/src/ssr/server.ts CHANGED
@@ -2,7 +2,7 @@ import { Readable } from "node:stream"
2
2
  import { Fragment } from "../element.js"
3
3
  import { renderMode } from "../globals.js"
4
4
  import { STREAMED_DATA_EVENT } from "../constants.js"
5
- import { headlessRender, HeadlessRenderContext } from "../recursiveRender.js"
5
+ import { headlessRender, HeadlessRenderContext } from "../headlessRender.js"
6
6
 
7
7
  const STREAMED_DATA_SETUP = `
8
8
  <script type="text/javascript">
@@ -26,7 +26,7 @@ export function renderToReadableStream(element: JSX.Element): {
26
26
  } {
27
27
  const stream = new Readable({ read() {} })
28
28
  const rootNode = Fragment({ children: element })
29
- const streamPromises = new Set<Kiru.StatefulPromise<unknown>>()
29
+ const streamPromises = new Set<Kiru.StatefulPromiseBase<unknown>>()
30
30
  const pendingWritePromises: Promise<void>[] = []
31
31
 
32
32
  let immediate = ""
@@ -0,0 +1,136 @@
1
+ import { $STREAM_DATA, STREAMED_DATA_EVENT } from "./constants.js"
2
+ import { hydrationMode, node, renderMode } from "./globals.js"
3
+ import { Signal, signal } from "./signals/base.js"
4
+ import { getVNodeId } from "./utils/vdom.js"
5
+ import { onCleanup } from "./hooks/onCleanup.js"
6
+
7
+ export interface StreamDataThrowValue {
8
+ [$STREAM_DATA]: {
9
+ fallback?: JSX.Element
10
+ data: Kiru.StatefulPromiseBase<unknown>[]
11
+ }
12
+ }
13
+
14
+ /**
15
+ * Returns true if the value is a {@link StreamDataThrowValue}
16
+ */
17
+ export function isStreamDataThrowValue(
18
+ value: unknown
19
+ ): value is StreamDataThrowValue {
20
+ return typeof value === "object" && !!value && $STREAM_DATA in value
21
+ }
22
+
23
+ /**
24
+ * Returns true if the value is a {@link Kiru.StatefulPromiseBase}
25
+ */
26
+ export function isStatefulPromise(
27
+ thing: unknown
28
+ ): thing is Kiru.StatefulPromiseBase<unknown> {
29
+ return thing instanceof Promise && "id" in thing && "state" in thing
30
+ }
31
+
32
+ const nodeToPromiseIndex = new WeakMap<Kiru.VNode, number>()
33
+
34
+ type StatefulPromise<T> = Kiru.StatefulPromiseBase<T> & {
35
+ isPending: Signal<boolean>
36
+ }
37
+
38
+ export function statefulPromise<T>(
39
+ callback: (signal: AbortSignal) => Promise<T>
40
+ ): StatefulPromise<T> {
41
+ const vNode = node.current!
42
+ if (!vNode) {
43
+ throw new Error("statefulPromise must be called inside a Kiru component")
44
+ }
45
+ const id = getVNodeId(vNode)
46
+ const isPending = signal(true)
47
+
48
+ isPending.value = true
49
+
50
+ const controller = new AbortController()
51
+ onCleanup(() => controller.abort())
52
+
53
+ const index = nodeToPromiseIndex.get(vNode) ?? 0
54
+ nodeToPromiseIndex.set(vNode, index + 1)
55
+
56
+ const promiseId = `${id}:data:${index}`
57
+
58
+ let promise: Promise<T>
59
+ if (renderMode.current === "string") {
60
+ // if we're rendering to a string, there's no need to fire the callback
61
+ promise = Promise.resolve() as Promise<T>
62
+ } else if (
63
+ renderMode.current === "hydrate" &&
64
+ hydrationMode.current === "dynamic"
65
+ ) {
66
+ // if we're hydrating and the hydration mode is not static,
67
+ // we need to resolve the promise from cache/event
68
+ promise = resolveDeferredPromise<T>(promiseId, controller.signal)
69
+ } else {
70
+ // dom / stream / (hydrate + static)
71
+ promise = callback(controller.signal)
72
+ }
73
+
74
+ const state: Kiru.PromiseState<T> = {
75
+ id: promiseId,
76
+ state: "pending",
77
+ }
78
+ const statefulPromise: Kiru.StatefulPromiseBase<T> = Object.assign(
79
+ promise,
80
+ state
81
+ )
82
+
83
+ statefulPromise
84
+ .then((value) => {
85
+ statefulPromise.state = "fulfilled"
86
+ statefulPromise.value = value
87
+ isPending.value = false
88
+ })
89
+ .catch((error) => {
90
+ statefulPromise.state = "rejected"
91
+ statefulPromise.error = error instanceof Error ? error : new Error(error)
92
+ })
93
+
94
+ return Object.assign(statefulPromise, { isPending })
95
+ }
96
+
97
+ interface DeferredPromiseEventDetail<T> {
98
+ id: string
99
+ data?: T
100
+ error?: string
101
+ }
102
+
103
+ function resolveDeferredPromise<T>(
104
+ id: string,
105
+ signal: AbortSignal
106
+ ): Promise<T> {
107
+ return new Promise<T>((resolve, reject) => {
108
+ const deferralCache: Map<string, { data?: T; error?: string }> = // @ts-ignore
109
+ (window[STREAMED_DATA_EVENT] ??= new Map())
110
+
111
+ const existing = deferralCache.get(id)
112
+ if (existing) {
113
+ const { data, error } = existing
114
+ deferralCache.delete(id)
115
+ if (error) return reject(error)
116
+ return resolve(data!)
117
+ }
118
+
119
+ const onDataEvent = (event: Event) => {
120
+ const { detail } = event as CustomEvent<DeferredPromiseEventDetail<T>>
121
+ if (detail.id === id) {
122
+ deferralCache.delete(id)
123
+ window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
124
+ const { data, error } = detail
125
+ if (error) return reject(error)
126
+ resolve(data!)
127
+ }
128
+ }
129
+
130
+ window.addEventListener(STREAMED_DATA_EVENT, onDataEvent)
131
+ signal.addEventListener("abort", () => {
132
+ window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
133
+ reject()
134
+ })
135
+ })
136
+ }
package/src/types.dom.ts CHANGED
@@ -598,17 +598,13 @@ interface PopoverControlAttributes {
598
598
  popoverTargetAction?: "show" | "hide" | "toggle"
599
599
  }
600
600
 
601
- declare class DoNotUseBindWithPlainError extends Error {
602
- $brand: "DoNotUseBindWithPlainError"
603
- }
604
-
605
601
  type BindableProp<K extends string, V> =
606
602
  | ({
607
- [k in K]?: Signalable<V>
608
- } & { [k in `bind:${K}`]?: DoNotUseBindWithPlainError })
603
+ [k in K]?: Signalable<V | undefined>
604
+ } & { [k in `bind:${K}`]?: never })
609
605
  | ({
610
- [k in `bind:${K}`]?: Signal<V>
611
- } & { [k in K]?: DoNotUseBindWithPlainError })
606
+ [k in `bind:${K}`]?: Signal<V | undefined>
607
+ } & { [k in K]?: never })
612
608
 
613
609
  type MediaElementBindableProps = BindableProp<"volume", number> &
614
610
  BindableProp<"playbackRate", number> &
package/src/types.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  import type { ReadonlySignal, Signal as SignalClass } from "./signals"
2
- import type {
3
- $CONTEXT,
4
- $CONTEXT_PROVIDER,
5
- $ERROR_BOUNDARY,
6
- $FRAGMENT,
7
- } from "./constants"
2
+ import type { $CONTEXT, $ERROR_BOUNDARY, $FRAGMENT } from "./constants"
8
3
  import type { KiruGlobalContext } from "./globalContext"
9
4
  import type {
10
5
  GlobalAttributes,
@@ -22,7 +17,7 @@ import type {
22
17
  Signalable,
23
18
  SomeDom,
24
19
  } from "./types.utils"
25
- import type { AppContext } from "./appContext"
20
+ import type { AppHandle } from "./appHandle"
26
21
 
27
22
  export type { AsyncTaskState, ElementProps, Prettify, Signalable, StyleObject }
28
23
 
@@ -32,24 +27,26 @@ type ElementProps<T extends keyof JSX.IntrinsicElements> =
32
27
  type SignalableHtmlElementAttributes<Tag extends keyof HtmlElementAttributes> =
33
28
  {
34
29
  [K in keyof HtmlElementAttributes[Tag]]: Signalable<
35
- HtmlElementAttributes[Tag][K]
30
+ HtmlElementAttributes[Tag][K] | undefined
36
31
  >
37
32
  } & (Tag extends keyof HtmlElementBindableProps
38
33
  ? HtmlElementBindableProps[Tag]
39
34
  : {})
40
35
  type SignalableSvgElementAttributes<Tag extends keyof SvgElementAttributes> = {
41
36
  [K in keyof SvgElementAttributes[Tag]]: Signalable<
42
- SvgElementAttributes[Tag][K]
37
+ SvgElementAttributes[Tag][K] | undefined
43
38
  >
44
39
  }
45
40
  type SignalableAriaProps = {
46
- [K in keyof ARIAMixin]?: Signalable<ARIAMixin[K]>
41
+ [K in keyof ARIAMixin]?: Signalable<ARIAMixin[K] | undefined>
47
42
  }
48
43
  type SignalableGlobalAttributes = {
49
- [K in keyof GlobalAttributes]: Signalable<GlobalAttributes[K]>
44
+ [K in keyof GlobalAttributes]: Signalable<GlobalAttributes[K] | undefined>
50
45
  }
51
46
  type SignalableSvgGlobalAttributes = {
52
- [K in keyof SvgGlobalAttributes]: Signalable<SvgGlobalAttributes[K]>
47
+ [K in keyof SvgGlobalAttributes]: Signalable<
48
+ SvgGlobalAttributes[K] | undefined
49
+ >
53
50
  }
54
51
 
55
52
  type ElementMap = {
@@ -58,10 +55,7 @@ type ElementMap = {
58
55
  SignalableAriaProps &
59
56
  Kiru.EventAttributes<HTMLTagToElement<Tag>> &
60
57
  JSX.ElementAttributes & {
61
- ref?:
62
- | Kiru.Ref<HTMLTagToElement<Tag> | null>
63
- | SignalClass<HTMLTagToElement<Tag> | null>
64
- | null
58
+ ref?: Kiru.Ref<Element | null> | SignalClass<Element | null> | null
65
59
  }
66
60
  } & {
67
61
  [Tag in keyof SvgElementAttributes]: SignalableSvgElementAttributes<Tag> &
@@ -70,10 +64,7 @@ type ElementMap = {
70
64
  SignalableAriaProps &
71
65
  Kiru.EventAttributes<SVGTagToElement<Tag>> &
72
66
  JSX.ElementAttributes & {
73
- ref?:
74
- | Kiru.Ref<SVGTagToElement<Tag> | null>
75
- | SignalClass<SVGTagToElement<Tag> | null>
76
- | null
67
+ ref?: Kiru.Ref<Element | null> | SignalClass<Element | null> | null
77
68
  }
78
69
  } & {
79
70
  [Tag in `${string}-${string}`]: Record<string, any>
@@ -108,6 +99,7 @@ declare global {
108
99
  | Kiru.Element
109
100
  | PrimitiveChild
110
101
  | Kiru.Signal<PrimitiveChild>
102
+ | Kiru.FC<any>
111
103
 
112
104
  interface ElementAttributes {
113
105
  key?: JSX.ElementKey
@@ -121,42 +113,25 @@ declare global {
121
113
  namespace Kiru {
122
114
  interface CustomEvents {}
123
115
 
124
- interface ProviderProps<T> {
116
+ interface ContextProps<T> {
125
117
  value: T
126
- children?: JSX.Children | ((value: T) => JSX.Element)
118
+ children?: JSX.Children
127
119
  }
128
- interface Context<T> {
129
- [$CONTEXT]: true
130
- Provider: (({ value, children }: ProviderProps<T>) => JSX.Element) & {
131
- displayName?: string
132
- }
133
- default: () => T
134
- /** Used to display the name of the context in devtools */
135
- displayName?: string
120
+
121
+ interface Context<T> extends Kiru.FC<ContextProps<T>> {
122
+ [$CONTEXT]: () => T
136
123
  }
137
124
 
138
- interface FC<T = {}> {
139
- (props: FCProps<T>): JSX.Element
125
+ export interface FC<T = {}> {
126
+ (props: T):
127
+ | Exclude<JSX.Element, Kiru.FC<any>>
128
+ | ((props: T) => JSX.Element)
140
129
  /** Used to display the name of the component in devtools */
141
130
  displayName?: string
142
131
  }
143
132
 
144
- type FCProps<T = {}> = T & { children?: JSX.Children }
145
133
  type InferProps<T> = T extends Kiru.FC<infer P> ? P : never
146
134
 
147
- interface HookDevtoolsProvisions<T extends Record<string, any>> {
148
- get: () => T
149
- set?: (value: T) => void
150
- }
151
- type Hook<T> = T & {
152
- cleanup?: () => void
153
- name?: string
154
- dev?: {
155
- /** Used to perform invalidation during HMR when the hook's arguments have changed */
156
- initialArgs?: any
157
- devtools?: HookDevtoolsProvisions<any>
158
- }
159
- }
160
135
  interface RefObject<T> {
161
136
  current: T
162
137
  }
@@ -173,7 +148,7 @@ declare global {
173
148
  error?: Error
174
149
  }
175
150
 
176
- interface StatefulPromise<T> extends Promise<T>, PromiseState<T> {}
151
+ interface StatefulPromiseBase<T> extends Promise<T>, PromiseState<T> {}
177
152
 
178
153
  type RenderMode = "dom" | "hydrate" | "string" | "stream"
179
154
 
@@ -183,7 +158,7 @@ declare global {
183
158
 
184
159
  type ExoticSymbol =
185
160
  | typeof $FRAGMENT
186
- | typeof $CONTEXT_PROVIDER
161
+ | typeof $CONTEXT
187
162
  | typeof $ERROR_BOUNDARY
188
163
 
189
164
  interface Element {
@@ -196,8 +171,10 @@ declare global {
196
171
  }
197
172
  }
198
173
 
174
+ type LifecycleHookCallback = () => (() => void) | void
175
+
199
176
  interface VNode extends Element {
200
- app?: AppContext
177
+ app?: AppHandle
201
178
  dom?: SomeDom
202
179
  index: number
203
180
  depth: number
@@ -207,22 +184,30 @@ declare global {
207
184
  sibling: VNode | null
208
185
  prev: VNodeSnapshot | null
209
186
  deletions: VNode[] | null
210
- hooks?: Hook<unknown>[]
211
187
  subs?: Set<Function>
212
188
  cleanups?: Record<string, Function>
213
- effects?: Array<Function>
214
- immediateEffects?: Array<Function>
215
- // dev-mode only
216
- hookSig?: string[]
189
+
190
+ hooks?: {
191
+ pre: LifecycleHookCallback[]
192
+ preCleanups: (() => void)[]
193
+ post: LifecycleHookCallback[]
194
+ postCleanups: (() => void)[]
195
+ }
196
+ render?: (props: VNode["props"]) => unknown
217
197
  }
218
- }
219
- interface VNodeSnapshot {
220
- props: Kiru.VNode["props"]
221
- key: Kiru.VNode["key"]
222
- index: number
198
+ interface VNodeSnapshot {
199
+ props: Kiru.VNode["props"]
200
+ key: Kiru.VNode["key"]
201
+ index: number
202
+ }
203
+
204
+ type ContainerElement = HTMLElement | ShadowRoot
223
205
  }
224
206
 
225
207
  interface Element {
226
208
  __kiruNode?: Kiru.VNode
227
209
  }
210
+ interface ShadowRoot {
211
+ __kiruNode?: Kiru.VNode
212
+ }
228
213
  }
@@ -1,4 +1,4 @@
1
- import type { $CONTEXT_PROVIDER, $ERROR_BOUNDARY, $FRAGMENT } from "./constants"
1
+ import type { $CONTEXT, $ERROR_BOUNDARY, $FRAGMENT } from "./constants"
2
2
  import type { Signal } from "./signals"
3
3
  import type { ErrorBoundaryProps } from "./components/errorBoundary"
4
4
 
@@ -20,12 +20,11 @@ export interface DomVNode extends Kiru.VNode {
20
20
  type: "#text" | (string & {})
21
21
  }
22
22
 
23
- export interface ContextProviderNode<T> extends Kiru.VNode {
24
- type: typeof $CONTEXT_PROVIDER
23
+ export interface ContextNode<T> extends Kiru.VNode {
24
+ type: typeof $CONTEXT
25
25
  props: Kiru.VNode["props"] & {
26
26
  value: T
27
27
  ctx: Kiru.Context<T>
28
- dependents: Set<Kiru.VNode>
29
28
  }
30
29
  }
31
30
 
package/src/utils/vdom.ts CHANGED
@@ -1,17 +1,16 @@
1
1
  import {
2
2
  FLAG_DELETION,
3
3
  $FRAGMENT,
4
- $CONTEXT_PROVIDER,
5
4
  FLAG_PLACEMENT,
6
5
  FLAG_UPDATE,
7
6
  $ERROR_BOUNDARY,
7
+ $CONTEXT,
8
8
  } from "../constants.js"
9
9
  import { createElement } from "../index.js"
10
10
  import { KiruError } from "../error.js"
11
11
  import { node } from "../globals.js"
12
- import type { AppContext } from "../appContext.js"
12
+ import type { AppHandle } from "../appHandle.js"
13
13
  import type { ErrorBoundaryNode } from "../types.utils.js"
14
- import { isMemoFn } from "../components/memo.js"
15
14
 
16
15
  export {
17
16
  cloneElement,
@@ -22,17 +21,19 @@ export {
22
21
  isExoticType,
23
22
  isFragment,
24
23
  isLazy,
25
- isMemo,
26
24
  isContextProvider,
27
25
  vNodeContains,
28
26
  getCurrentVNode,
29
- getVNodeAppContext,
27
+ getVNodeApp,
30
28
  commitSnapshot,
31
29
  traverseApply,
32
30
  findParent,
33
31
  findParentErrorBoundary,
34
32
  assertValidElementProps,
35
33
  normalizeElementKey,
34
+ getVNodeId,
35
+ registerVNodeCleanup,
36
+ propsChanged,
36
37
  }
37
38
 
38
39
  function cloneElement(vNode: Kiru.VNode): Kiru.Element {
@@ -68,9 +69,7 @@ function isValidTextChild(thing: unknown): thing is string | number | bigint {
68
69
  }
69
70
 
70
71
  function isExoticType(type: Kiru.VNode["type"]): type is Kiru.ExoticSymbol {
71
- return (
72
- type === $FRAGMENT || type === $CONTEXT_PROVIDER || type === $ERROR_BOUNDARY
73
- )
72
+ return type === $FRAGMENT || type === $CONTEXT || type === $ERROR_BOUNDARY
74
73
  }
75
74
 
76
75
  function isFragment(
@@ -87,21 +86,17 @@ function isLazy(vNode: Kiru.VNode): boolean {
87
86
  )
88
87
  }
89
88
 
90
- function isMemo(vNode: Kiru.VNode): boolean {
91
- return typeof vNode.type === "function" && isMemoFn(vNode.type)
92
- }
93
-
94
89
  function isContextProvider(
95
90
  thing: unknown
96
- ): thing is Kiru.VNode & { type: typeof $CONTEXT_PROVIDER } {
97
- return isVNode(thing) && thing.type === $CONTEXT_PROVIDER
91
+ ): thing is Kiru.VNode & { type: typeof $CONTEXT } {
92
+ return isVNode(thing) && thing.type === $CONTEXT
98
93
  }
99
94
 
100
95
  function getCurrentVNode(): Kiru.VNode | null {
101
96
  return node.current
102
97
  }
103
98
 
104
- function getVNodeAppContext(vNode: Kiru.VNode): AppContext | null {
99
+ function getVNodeApp(vNode: Kiru.VNode): AppHandle | null {
105
100
  let n: Kiru.VNode | null = vNode
106
101
  while (n) {
107
102
  if (n.app) {
@@ -190,3 +185,37 @@ function normalizeElementKey(thing: unknown): JSX.ElementKey | null {
190
185
  }
191
186
  return null
192
187
  }
188
+
189
+ function getVNodeId(vNode: Kiru.VNode): string {
190
+ const accumulator: number[] = []
191
+ let n: Kiru.VNode | null = vNode
192
+ while (n) {
193
+ accumulator.push(n.index)
194
+ accumulator.push(n.depth)
195
+ n = n.parent
196
+ }
197
+ return `k:${BigInt(accumulator.join("")).toString(36)}`
198
+ }
199
+
200
+ function registerVNodeCleanup(
201
+ vNode: Kiru.VNode,
202
+ id: string,
203
+ callback: () => void
204
+ ) {
205
+ ;(vNode.cleanups ??= {})[id] = callback
206
+ }
207
+
208
+ function propsChanged(
209
+ oldProps: Kiru.VNode["props"],
210
+ newProps: Kiru.VNode["props"],
211
+ keysToSkip?: string[]
212
+ ) {
213
+ const aKeys = Object.keys(oldProps)
214
+ const bKeys = Object.keys(newProps)
215
+ if (aKeys.length !== bKeys.length) return true
216
+ for (let key of aKeys) {
217
+ if (keysToSkip?.includes(key)) continue
218
+ if (oldProps[key] !== newProps[key]) return true
219
+ }
220
+ return false
221
+ }