kiru 0.54.3 → 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 +36 -50
  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 +29 -63
  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 +45 -58
  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 -41
  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,9 +55,7 @@ type ElementMap = {
58
55
  SignalableAriaProps &
59
56
  Kiru.EventAttributes<HTMLTagToElement<Tag>> &
60
57
  JSX.ElementAttributes & {
61
- ref?:
62
- | Kiru.Ref<HTMLTagToElement<Tag>>
63
- | SignalClass<HTMLTagToElement<Tag> | null>
58
+ ref?: Kiru.Ref<Element | null> | SignalClass<Element | null> | null
64
59
  }
65
60
  } & {
66
61
  [Tag in keyof SvgElementAttributes]: SignalableSvgElementAttributes<Tag> &
@@ -69,9 +64,7 @@ type ElementMap = {
69
64
  SignalableAriaProps &
70
65
  Kiru.EventAttributes<SVGTagToElement<Tag>> &
71
66
  JSX.ElementAttributes & {
72
- ref?:
73
- | Kiru.Ref<SVGTagToElement<Tag>>
74
- | SignalClass<SVGTagToElement<Tag> | null>
67
+ ref?: Kiru.Ref<Element | null> | SignalClass<Element | null> | null
75
68
  }
76
69
  } & {
77
70
  [Tag in `${string}-${string}`]: Record<string, any>
@@ -106,6 +99,7 @@ declare global {
106
99
  | Kiru.Element
107
100
  | PrimitiveChild
108
101
  | Kiru.Signal<PrimitiveChild>
102
+ | Kiru.FC<any>
109
103
 
110
104
  interface ElementAttributes {
111
105
  key?: JSX.ElementKey
@@ -119,42 +113,25 @@ declare global {
119
113
  namespace Kiru {
120
114
  interface CustomEvents {}
121
115
 
122
- interface ProviderProps<T> {
116
+ interface ContextProps<T> {
123
117
  value: T
124
- children?: JSX.Children | ((value: T) => JSX.Element)
118
+ children?: JSX.Children
125
119
  }
126
- interface Context<T> {
127
- [$CONTEXT]: true
128
- Provider: (({ value, children }: ProviderProps<T>) => JSX.Element) & {
129
- displayName?: string
130
- }
131
- default: () => T
132
- /** Used to display the name of the context in devtools */
133
- displayName?: string
120
+
121
+ interface Context<T> extends Kiru.FC<ContextProps<T>> {
122
+ [$CONTEXT]: () => T
134
123
  }
135
124
 
136
- interface FC<T = {}> {
137
- (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)
138
129
  /** Used to display the name of the component in devtools */
139
130
  displayName?: string
140
131
  }
141
132
 
142
- type FCProps<T = {}> = T & { children?: JSX.Children }
143
133
  type InferProps<T> = T extends Kiru.FC<infer P> ? P : never
144
134
 
145
- interface HookDevtoolsProvisions<T extends Record<string, any>> {
146
- get: () => T
147
- set?: (value: T) => void
148
- }
149
- type Hook<T> = T & {
150
- cleanup?: () => void
151
- name?: string
152
- dev?: {
153
- /** Used to perform invalidation during HMR when the hook's arguments have changed */
154
- initialArgs?: any
155
- devtools?: HookDevtoolsProvisions<any>
156
- }
157
- }
158
135
  interface RefObject<T> {
159
136
  current: T
160
137
  }
@@ -171,7 +148,7 @@ declare global {
171
148
  error?: Error
172
149
  }
173
150
 
174
- interface StatefulPromise<T> extends Promise<T>, PromiseState<T> {}
151
+ interface StatefulPromiseBase<T> extends Promise<T>, PromiseState<T> {}
175
152
 
176
153
  type RenderMode = "dom" | "hydrate" | "string" | "stream"
177
154
 
@@ -181,7 +158,7 @@ declare global {
181
158
 
182
159
  type ExoticSymbol =
183
160
  | typeof $FRAGMENT
184
- | typeof $CONTEXT_PROVIDER
161
+ | typeof $CONTEXT
185
162
  | typeof $ERROR_BOUNDARY
186
163
 
187
164
  interface Element {
@@ -190,12 +167,14 @@ declare global {
190
167
  props: {
191
168
  [key: string]: any
192
169
  children?: unknown
193
- ref?: Kiru.Ref<unknown>
170
+ ref?: Kiru.Ref<unknown> | null
194
171
  }
195
172
  }
196
173
 
174
+ type LifecycleHookCallback = () => (() => void) | void
175
+
197
176
  interface VNode extends Element {
198
- app?: AppContext
177
+ app?: AppHandle
199
178
  dom?: SomeDom
200
179
  index: number
201
180
  depth: number
@@ -205,22 +184,30 @@ declare global {
205
184
  sibling: VNode | null
206
185
  prev: VNodeSnapshot | null
207
186
  deletions: VNode[] | null
208
- hooks?: Hook<unknown>[]
209
187
  subs?: Set<Function>
210
188
  cleanups?: Record<string, Function>
211
- effects?: Array<Function>
212
- immediateEffects?: Array<Function>
213
- // dev-mode only
214
- hookSig?: string[]
189
+
190
+ hooks?: {
191
+ pre: LifecycleHookCallback[]
192
+ preCleanups: (() => void)[]
193
+ post: LifecycleHookCallback[]
194
+ postCleanups: (() => void)[]
195
+ }
196
+ render?: (props: VNode["props"]) => unknown
215
197
  }
216
- }
217
- interface VNodeSnapshot {
218
- props: Kiru.VNode["props"]
219
- key: Kiru.VNode["key"]
220
- 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
221
205
  }
222
206
 
223
207
  interface Element {
224
208
  __kiruNode?: Kiru.VNode
225
209
  }
210
+ interface ShadowRoot {
211
+ __kiruNode?: Kiru.VNode
212
+ }
226
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
+ }