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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +1 -1
  3. package/dist/actions.d.ts +32 -30
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +251 -266
  6. package/dist/actions.js.map +1 -1
  7. package/dist/async.d.ts +20 -17
  8. package/dist/async.d.ts.map +1 -1
  9. package/dist/async.js +18 -17
  10. package/dist/async.js.map +1 -1
  11. package/dist/compiled/async-resource.d.ts +9 -6
  12. package/dist/compiled/async-resource.d.ts.map +1 -1
  13. package/dist/compiled/async-resource.js +69 -77
  14. package/dist/compiled/async-resource.js.map +1 -1
  15. package/dist/compiled/core.d.ts +136 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +2672 -2985
  18. package/dist/compiled/core.js.map +1 -1
  19. package/dist/compiled/types.d.ts +44 -56
  20. package/dist/compiled/types.d.ts.map +1 -1
  21. package/dist/compiled/types.js +10 -6
  22. package/dist/compiled/types.js.map +1 -1
  23. package/dist/compiled/validation.d.ts +4 -1
  24. package/dist/compiled/validation.d.ts.map +1 -1
  25. package/dist/compiled/validation.js +17 -20
  26. package/dist/compiled/validation.js.map +1 -1
  27. package/dist/component-spread.d.ts +5 -3
  28. package/dist/component-spread.d.ts.map +1 -1
  29. package/dist/component-spread.js +49 -67
  30. package/dist/component-spread.js.map +1 -1
  31. package/dist/concurrent.d.ts +20 -18
  32. package/dist/concurrent.d.ts.map +1 -1
  33. package/dist/concurrent.js +191 -210
  34. package/dist/concurrent.js.map +1 -1
  35. package/dist/direct-dom.d.ts +17 -12
  36. package/dist/direct-dom.d.ts.map +1 -1
  37. package/dist/direct-dom.js +187 -221
  38. package/dist/direct-dom.js.map +1 -1
  39. package/dist/dom/attributes.d.ts +9 -6
  40. package/dist/dom/attributes.d.ts.map +1 -1
  41. package/dist/dom/attributes.js +65 -47
  42. package/dist/dom/attributes.js.map +1 -1
  43. package/dist/dom/events.d.ts +5 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +53 -65
  46. package/dist/dom/events.js.map +1 -1
  47. package/dist/dom/forms.d.ts +9 -6
  48. package/dist/dom/forms.d.ts.map +1 -1
  49. package/dist/dom/forms.js +152 -198
  50. package/dist/dom/forms.js.map +1 -1
  51. package/dist/dom/intrinsic.d.ts +17 -15
  52. package/dist/dom/intrinsic.d.ts.map +1 -1
  53. package/dist/dom/intrinsic.js +48 -50
  54. package/dist/dom/intrinsic.js.map +1 -1
  55. package/dist/dom/namespace.d.ts +4 -1
  56. package/dist/dom/namespace.d.ts.map +1 -1
  57. package/dist/dom/namespace.js +25 -28
  58. package/dist/dom/namespace.js.map +1 -1
  59. package/dist/dom/namespaces.d.ts +11 -8
  60. package/dist/dom/namespaces.d.ts.map +1 -1
  61. package/dist/dom/namespaces.js +14 -12
  62. package/dist/dom/namespaces.js.map +1 -1
  63. package/dist/dom/properties.d.ts +18 -16
  64. package/dist/dom/properties.d.ts.map +1 -1
  65. package/dist/dom/properties.js +273 -296
  66. package/dist/dom/properties.js.map +1 -1
  67. package/dist/dom/style-properties.d.ts +4 -1
  68. package/dist/dom/style-properties.d.ts.map +1 -1
  69. package/dist/dom/style-properties.js +49 -45
  70. package/dist/dom/style-properties.js.map +1 -1
  71. package/dist/dom/styles.d.ts +5 -2
  72. package/dist/dom/styles.d.ts.map +1 -1
  73. package/dist/dom/styles.js +47 -67
  74. package/dist/dom/styles.js.map +1 -1
  75. package/dist/dom-forms.d.ts +4 -1
  76. package/dist/dom-forms.d.ts.map +1 -1
  77. package/dist/dom-forms.js +16 -13
  78. package/dist/dom-forms.js.map +1 -1
  79. package/dist/dom-styles.d.ts +4 -1
  80. package/dist/dom-styles.d.ts.map +1 -1
  81. package/dist/dom-styles.js +10 -7
  82. package/dist/dom-styles.js.map +1 -1
  83. package/dist/framework-hydrate.d.ts +20 -17
  84. package/dist/framework-hydrate.d.ts.map +1 -1
  85. package/dist/framework-hydrate.js +128 -146
  86. package/dist/framework-hydrate.js.map +1 -1
  87. package/dist/framework-protocol.d.ts +27 -25
  88. package/dist/framework-protocol.d.ts.map +1 -1
  89. package/dist/framework-protocol.js +173 -211
  90. package/dist/framework-protocol.js.map +1 -1
  91. package/dist/framework.d.ts +34 -32
  92. package/dist/framework.d.ts.map +1 -1
  93. package/dist/framework.js +74 -93
  94. package/dist/framework.js.map +1 -1
  95. package/dist/hydrate.d.ts +11 -8
  96. package/dist/hydrate.d.ts.map +1 -1
  97. package/dist/hydrate.js +13 -9
  98. package/dist/hydrate.js.map +1 -1
  99. package/dist/hydration-bridge.d.ts +70 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +77 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +54 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +450 -463
  106. package/dist/hydration.js.map +1 -1
  107. package/dist/index.d.ts +13 -13
  108. package/dist/index.js +11 -13
  109. package/dist/indexed-list.d.ts +8 -5
  110. package/dist/indexed-list.d.ts.map +1 -1
  111. package/dist/indexed-list.js +14 -7
  112. package/dist/indexed-list.js.map +1 -1
  113. package/dist/jsx-runtime.d.ts +10 -8
  114. package/dist/jsx-runtime.d.ts.map +1 -1
  115. package/dist/jsx-runtime.js +28 -23
  116. package/dist/jsx-runtime.js.map +1 -1
  117. package/dist/keyed-list.d.ts +13 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +132 -160
  120. package/dist/keyed-list.js.map +1 -1
  121. package/dist/metadata.d.ts +6 -3
  122. package/dist/metadata.d.ts.map +1 -1
  123. package/dist/metadata.js +131 -164
  124. package/dist/metadata.js.map +1 -1
  125. package/dist/nested-prop.d.ts +4 -2
  126. package/dist/nested-prop.d.ts.map +1 -1
  127. package/dist/nested-prop.js +17 -16
  128. package/dist/nested-prop.js.map +1 -1
  129. package/dist/profiling.d.ts +11 -8
  130. package/dist/profiling.d.ts.map +1 -1
  131. package/dist/profiling.js +17 -17
  132. package/dist/profiling.js.map +1 -1
  133. package/dist/protocol.d.ts +5 -2
  134. package/dist/protocol.d.ts.map +1 -1
  135. package/dist/protocol.js +8 -5
  136. package/dist/protocol.js.map +1 -1
  137. package/dist/raw-html.d.ts +5 -2
  138. package/dist/raw-html.d.ts.map +1 -1
  139. package/dist/raw-html.js +86 -146
  140. package/dist/raw-html.js.map +1 -1
  141. package/dist/ref.d.ts +8 -3
  142. package/dist/ref.d.ts.map +1 -1
  143. package/dist/ref.js +10 -2
  144. package/dist/ref.js.map +1 -1
  145. package/dist/renderable-protocol.d.ts +26 -8
  146. package/dist/renderable-protocol.d.ts.map +1 -1
  147. package/dist/renderable-protocol.js +30 -9
  148. package/dist/renderable-protocol.js.map +1 -1
  149. package/dist/renderable.d.ts +27 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +141 -133
  152. package/dist/renderable.js.map +1 -1
  153. package/dist/retained-ui.d.ts +9 -6
  154. package/dist/retained-ui.d.ts.map +1 -1
  155. package/dist/retained-ui.js +12 -10
  156. package/dist/retained-ui.js.map +1 -1
  157. package/dist/root.d.ts +18 -15
  158. package/dist/root.d.ts.map +1 -1
  159. package/dist/root.js +73 -85
  160. package/dist/root.js.map +1 -1
  161. package/dist/scheduler.d.ts +10 -8
  162. package/dist/scheduler.d.ts.map +1 -1
  163. package/dist/scheduler.js +97 -105
  164. package/dist/scheduler.js.map +1 -1
  165. package/dist/server-framework.d.ts +45 -42
  166. package/dist/server-framework.d.ts.map +1 -1
  167. package/dist/server-framework.js +209 -217
  168. package/dist/server-framework.js.map +1 -1
  169. package/dist/server.d.ts +130 -128
  170. package/dist/server.d.ts.map +1 -1
  171. package/dist/server.js +603 -710
  172. package/dist/server.js.map +1 -1
  173. package/dist/shared/promise.d.ts +6 -3
  174. package/dist/shared/promise.d.ts.map +1 -1
  175. package/dist/shared/promise.js +21 -19
  176. package/dist/shared/promise.js.map +1 -1
  177. package/dist/source-mask.d.ts +9 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +45 -63
  180. package/dist/source-mask.js.map +1 -1
  181. package/dist/spread.d.ts +5 -2
  182. package/dist/spread.d.ts.map +1 -1
  183. package/dist/spread.js +79 -99
  184. package/dist/spread.js.map +1 -1
  185. package/dist/state-slot.d.ts +18 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +43 -42
  188. package/dist/state-slot.js.map +1 -1
  189. package/dist/testing.d.ts +9 -6
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +16 -12
  192. package/dist/testing.js.map +1 -1
  193. package/package.json +8 -7
  194. package/src/actions.ts +415 -0
  195. package/src/async.ts +78 -0
  196. package/src/compiled/async-resource.ts +113 -0
  197. package/src/compiled/core.ts +4041 -0
  198. package/src/compiled/types.ts +96 -0
  199. package/src/compiled/validation.ts +23 -0
  200. package/src/component-spread.ts +86 -0
  201. package/src/concurrent.ts +310 -0
  202. package/src/direct-dom.ts +365 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +93 -0
  205. package/src/dom/forms.ts +220 -0
  206. package/src/dom/intrinsic.ts +94 -0
  207. package/src/dom/namespace.ts +30 -0
  208. package/src/dom/namespaces.ts +14 -0
  209. package/src/dom/properties.ts +355 -0
  210. package/src/dom/style-properties.ts +46 -0
  211. package/src/dom/styles.ts +86 -0
  212. package/src/dom-forms.ts +23 -0
  213. package/src/dom-styles.ts +11 -0
  214. package/src/env.d.ts +6 -0
  215. package/src/framework-hydrate.ts +224 -0
  216. package/src/framework-protocol.ts +335 -0
  217. package/src/framework.ts +161 -0
  218. package/src/hydrate.ts +38 -0
  219. package/src/hydration-bridge.ts +196 -0
  220. package/src/hydration.ts +642 -0
  221. package/src/index.ts +92 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +233 -0
  225. package/src/metadata.ts +212 -0
  226. package/src/nested-prop.ts +27 -0
  227. package/src/profiling.ts +43 -0
  228. package/src/protocol.ts +8 -0
  229. package/src/raw-html.ts +206 -0
  230. package/src/ref.ts +13 -0
  231. package/src/renderable-protocol.ts +63 -0
  232. package/src/renderable.ts +283 -0
  233. package/src/retained-ui.ts +19 -0
  234. package/src/root.ts +122 -0
  235. package/src/scheduler.ts +136 -0
  236. package/src/server-framework.ts +397 -0
  237. package/src/server.ts +1160 -0
  238. package/src/shared/promise.ts +28 -0
  239. package/src/source-mask.ts +83 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +76 -0
  242. package/src/testing.ts +27 -0
  243. package/dist/index.d.ts.map +0 -1
  244. package/dist/index.js.map +0 -1
package/src/index.ts ADDED
@@ -0,0 +1,92 @@
1
+ // oxlint-disable-next-line typescript/triple-slash-reference -- Include the build define in consuming TypeScript programs.
2
+ /// <reference path="./env.d.ts" />
3
+
4
+ export {
5
+ binding,
6
+ choose,
7
+ compiledEvent,
8
+ compiledEffect,
9
+ compiledImperativeHandle,
10
+ compiledInsertionEffect,
11
+ compiledLayoutEffect,
12
+ compiledRoot,
13
+ createCompiledContext,
14
+ createCompiledEffectEvent,
15
+ createCompiledExternalStore,
16
+ createCompiledId,
17
+ createCompiledMemo,
18
+ createCompiledProp,
19
+ createCompiledReducer,
20
+ createCompiledRestProp,
21
+ createCompiledScope,
22
+ createCompiledState,
23
+ createContext,
24
+ createNarrowCompiledScope,
25
+ createPortal,
26
+ deferred,
27
+ dispatch,
28
+ errorBoundary,
29
+ indexed,
30
+ keyed,
31
+ mountCompiled,
32
+ mountCompiledRef,
33
+ objectRest,
34
+ runWithCompiledContext,
35
+ use,
36
+ useCallback,
37
+ useContext,
38
+ useEffect,
39
+ useEffectEvent,
40
+ useId,
41
+ useImperativeHandle,
42
+ useInsertionEffect,
43
+ useLayoutEffect,
44
+ useMemo,
45
+ useSyncExternalStore,
46
+ when,
47
+ type ChoiceMode,
48
+ type MountCompiledOptions,
49
+ } from './compiled/core.ts'
50
+ export type {
51
+ CompiledBinding,
52
+ CompiledComponentResult,
53
+ CompiledContext,
54
+ CompiledErrorHandler,
55
+ CompiledRenderValue,
56
+ CompiledScope,
57
+ OwnedBlock,
58
+ StructuralBinding,
59
+ } from './compiled/types.ts'
60
+ export { compiledComponentSpread } from './component-spread.ts'
61
+ export {
62
+ Fragment,
63
+ h,
64
+ type DirectChild,
65
+ type DirectComponent,
66
+ type DirectProps,
67
+ } from './direct-dom.ts'
68
+ export { nestedProp } from './nested-prop.ts'
69
+ export { assertRuntimeProtocol, VIDACT_RUNTIME_PROTOCOL } from './protocol.ts'
70
+ export { createRef, useRef, type MutableRef } from './ref.ts'
71
+ export {
72
+ cloneRenderable,
73
+ cloneRenderableComponent,
74
+ createReactElement,
75
+ createReactElement as createElement,
76
+ createRenderable,
77
+ dynamicIntrinsicComponent,
78
+ forwardedRef,
79
+ isRenderable,
80
+ keyedFragmentComponent,
81
+ renderableChildren,
82
+ renderableMarker,
83
+ renderableProps,
84
+ renderableRef,
85
+ renderableToArray,
86
+ type CompiledRenderable,
87
+ type RenderablePropsInput,
88
+ } from './renderable.ts'
89
+ export { createRoot, mountHotRoot, type CompiledRoot, type HotContext } from './root.ts'
90
+ export { combineSources, intersectsSources, source, type SourceMask } from './source-mask.ts'
91
+ export { compiledSpread } from './spread.ts'
92
+ export type { StateUpdate } from './state-slot.ts'
@@ -0,0 +1,20 @@
1
+ import { createKeyedList, type KeyedList, type KeyedRenderResult } from './keyed-list.ts'
2
+
3
+ export interface IndexedListOptions<T> {
4
+ readonly render: (value: T, index: number) => KeyedRenderResult<T>
5
+ }
6
+
7
+ export type IndexedList<T> = KeyedList<T>
8
+
9
+ /**
10
+ * Reconciles records by their current position. Existing owners are retained at
11
+ * an index and receive the new value at that index, matching React's unkeyed
12
+ * collection identity semantics.
13
+ */
14
+ export function createIndexedList<T>(
15
+ parent: Node,
16
+ options: IndexedListOptions<T>,
17
+ before: Node | null = null,
18
+ ): IndexedList<T> {
19
+ return createKeyedList(parent, { key: (_value, index) => index, render: options.render }, before)
20
+ }
@@ -0,0 +1,55 @@
1
+ // oxlint-disable-next-line typescript/triple-slash-reference -- Include the build define in consuming TypeScript programs.
2
+ /// <reference path="./env.d.ts" />
3
+
4
+ import { h, type DirectChild, type DirectComponent } from './direct-dom.ts'
5
+ import {
6
+ isRenderableProtocol,
7
+ renderableCloneHandler,
8
+ type RenderableProtocol,
9
+ } from './renderable-protocol.ts'
10
+ import type { CompiledRenderable } from './renderable.ts'
11
+
12
+ export const Fragment: symbol = Symbol.for('vidact.v1.Fragment')
13
+
14
+ type ElementType = string | typeof Fragment | DirectComponent | CompiledRenderable
15
+
16
+ interface JsxProps extends Record<string, unknown> {
17
+ readonly children?: DirectChild | readonly DirectChild[]
18
+ }
19
+
20
+ export function jsx(type: ElementType, props: JsxProps | null, _key?: unknown): DirectChild {
21
+ if (isRenderableProtocol(type)) return cloneElementType(type, props)
22
+ const children = props?.children
23
+ const hasChildren = props !== null && Object.hasOwn(props, 'children')
24
+ if (!hasChildren) return h(type, props)
25
+ return h(type, props, children)
26
+ }
27
+
28
+ export function jsxs(type: ElementType, props: JsxProps | null, _key?: unknown): DirectChild {
29
+ if (isRenderableProtocol(type)) return cloneElementType(type, props)
30
+ const children = props?.children
31
+ const hasChildren = props !== null && Object.hasOwn(props, 'children')
32
+ if (!hasChildren) return h(type, props)
33
+ return Array.isArray(children) ? h(type, props, ...children) : h(type, props, children)
34
+ }
35
+
36
+ export function jsxDEV(
37
+ type: ElementType,
38
+ props: JsxProps | null,
39
+ key?: unknown,
40
+ isStaticChildren = false,
41
+ ): DirectChild {
42
+ return isStaticChildren ? jsxs(type, props, key) : jsx(type, props, key)
43
+ }
44
+
45
+ /**
46
+ * A renderable can only reach `jsx` as an element type if something already
47
+ * built one, which is what registers the clone.
48
+ */
49
+ function cloneElementType(type: RenderableProtocol, props: JsxProps | null): DirectChild {
50
+ const clone = renderableCloneHandler()
51
+ if (clone === undefined) {
52
+ throw new TypeError('V107')
53
+ }
54
+ return clone(type, props ?? {}) as DirectChild
55
+ }
@@ -0,0 +1,233 @@
1
+ export type KeyedItem<T> = readonly [
2
+ nodes: readonly Node[],
3
+ update?: ((value: T, index: number) => void) | undefined,
4
+ dispose?: (() => void) | undefined,
5
+ ]
6
+
7
+ export type KeyedRenderResult<T> = Node | readonly Node[] | KeyedItem<T>
8
+
9
+ export interface KeyedListOptions<T, K> {
10
+ readonly key: (value: T, index: number) => K
11
+ readonly render: (value: T, index: number) => KeyedRenderResult<T>
12
+ }
13
+
14
+ export interface KeyedList<T> {
15
+ readonly dispose: () => void
16
+ readonly parent: () => Node | null
17
+ readonly update: (values: readonly T[]) => readonly Node[]
18
+ }
19
+
20
+ type RecordState<T, K> = readonly [
21
+ key: K,
22
+ nodes: readonly Node[],
23
+ update: ((value: T, index: number) => void) | undefined,
24
+ dispose: (() => void) | undefined,
25
+ ]
26
+
27
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
28
+
29
+ export function createKeyedList<T, K>(
30
+ parent: Node,
31
+ options: KeyedListOptions<T, K>,
32
+ before: Node | null = null,
33
+ ): KeyedList<T> {
34
+ const hydratedRange = claimHydrationArrayRange(parent)
35
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:list' : '')
36
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:list' : '')
37
+ if (hydratedRange === undefined) {
38
+ parent.insertBefore(start, before)
39
+ parent.insertBefore(end, before)
40
+ }
41
+
42
+ let disposed = false
43
+ let hydrationPending = hydratedRange !== undefined
44
+ let records: readonly RecordState<T, K>[] = []
45
+
46
+ const update = (values: readonly T[]): readonly Node[] => {
47
+ if (disposed) throw new Error(DEV ? 'cannot update a disposed keyed list' : 'V801')
48
+ const currentParent = end.parentNode
49
+ if (currentParent === null || start.parentNode !== currentParent) {
50
+ throw new Error(DEV ? 'cannot update a detached keyed list' : 'V802')
51
+ }
52
+
53
+ const keys = values.map(options.key)
54
+ assertValidKeys(keys)
55
+ assertUniqueKeys(keys)
56
+
57
+ const previousByKey = new Map<K, RecordState<T, K>>()
58
+ for (const record of records) previousByKey.set(record[0], record)
59
+ const created: RecordState<T, K>[] = []
60
+ const retained: Array<readonly [RecordState<T, K>, T, number]> = []
61
+ let nextRecords: RecordState<T, K>[]
62
+ try {
63
+ nextRecords = values.map((value, index): RecordState<T, K> => {
64
+ const key = keys[index] as K
65
+ const previous = previousByKey.get(key)
66
+ if (previous !== undefined) {
67
+ previousByKey.delete(key)
68
+ retained.push([previous, value, index])
69
+ return previous
70
+ }
71
+
72
+ const item = normalizeRenderResult(
73
+ withHydrationInsertion(parent, end, () => options.render(value, index)),
74
+ )
75
+ const record: RecordState<T, K> = [key, item[0], item[1], item[2]]
76
+ created.push(record)
77
+ return record
78
+ })
79
+ for (const [record, value, index] of retained) record[2]?.(value, index)
80
+ } catch (error) {
81
+ disposeRecords(currentParent, created)
82
+ throw error
83
+ }
84
+
85
+ const cleanup = disposeRecords(currentParent, previousByKey.values())
86
+
87
+ const activeElement =
88
+ document.activeElement instanceof HTMLElement &&
89
+ currentParent.contains(document.activeElement)
90
+ ? document.activeElement
91
+ : null
92
+ const selectionControl =
93
+ activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement
94
+ ? activeElement
95
+ : null
96
+ const selection =
97
+ selectionControl === null
98
+ ? null
99
+ : ([selectionControl.selectionStart, selectionControl.selectionEnd] as const)
100
+ let cursor: Node = end
101
+ for (let recordIndex = nextRecords.length - 1; recordIndex >= 0; recordIndex -= 1) {
102
+ const record = nextRecords[recordIndex] as RecordState<T, K>
103
+ for (let nodeIndex = record[1].length - 1; nodeIndex >= 0; nodeIndex -= 1) {
104
+ const node = record[1][nodeIndex] as Node
105
+ if (node.nextSibling !== cursor) moveBefore(currentParent, node, cursor)
106
+ cursor = node
107
+ }
108
+ }
109
+ if (
110
+ activeElement !== null &&
111
+ document.activeElement !== activeElement &&
112
+ currentParent.contains(activeElement)
113
+ ) {
114
+ activeElement.focus({ preventScroll: true })
115
+ if (
116
+ selectionControl !== null &&
117
+ selection !== null &&
118
+ selection[0] !== null &&
119
+ selection[1] !== null
120
+ ) {
121
+ selectionControl.setSelectionRange(selection[0], selection[1])
122
+ }
123
+ }
124
+
125
+ records = nextRecords
126
+ if (hydrationPending) {
127
+ hydrationPending = false
128
+ finishHydrationArrayRange(currentParent, end)
129
+ }
130
+ if (cleanup[1]) throw cleanup[0]
131
+ return created.flatMap((record) => record[1])
132
+ }
133
+
134
+ const dispose = (): void => {
135
+ if (disposed) return
136
+ disposed = true
137
+ const currentParent = end.parentNode
138
+ const cleanup =
139
+ currentParent === null
140
+ ? ([undefined, false] as const)
141
+ : disposeRecords(currentParent, records)
142
+ records = []
143
+ start.remove()
144
+ end.remove()
145
+ if (cleanup[1]) throw cleanup[0]
146
+ }
147
+
148
+ return { dispose, parent: () => end.parentNode, update }
149
+ }
150
+
151
+ function moveBefore(parent: Node, node: Node, before: Node): void {
152
+ const statePreservingParent = parent as Node & {
153
+ moveBefore?: (node: Node, before: Node | null) => void
154
+ }
155
+ if (statePreservingParent.moveBefore !== undefined && node.parentNode === parent) {
156
+ statePreservingParent.moveBefore(node, before)
157
+ } else {
158
+ parent.insertBefore(node, before)
159
+ }
160
+ }
161
+
162
+ function assertValidKeys<K>(keys: readonly K[]): void {
163
+ for (const key of keys) {
164
+ if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'bigint') {
165
+ throw new Error(DEV ? `invalid key in keyed list: ${String(key)}` : 'V803')
166
+ }
167
+ }
168
+ }
169
+
170
+ function assertUniqueKeys<K>(keys: readonly K[]): void {
171
+ const seen = new Set<K>()
172
+ for (const key of keys) {
173
+ if (seen.has(key)) {
174
+ throw new Error(DEV ? `duplicate key in keyed list: ${String(key)}` : 'V804')
175
+ }
176
+ seen.add(key)
177
+ }
178
+ }
179
+
180
+ function normalizeRenderResult<T>(result: KeyedRenderResult<T>): KeyedItem<T> {
181
+ if (isNode(result)) return [normalizeNodes([result])]
182
+ if (isNodeArray(result)) return [normalizeNodes(result)]
183
+ return [normalizeNodes(result[0]), result[1], result[2]]
184
+ }
185
+
186
+ function normalizeNodes(nodes: readonly Node[]): readonly Node[] {
187
+ const normalized: Node[] = []
188
+ for (const node of nodes) {
189
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
190
+ normalized.push(...node.childNodes)
191
+ } else {
192
+ normalized.push(node)
193
+ }
194
+ }
195
+ return normalized.length === 0 ? [document.createComment(DEV ? 'vidact:empty' : '')] : normalized
196
+ }
197
+
198
+ function isNode(value: unknown): value is Node {
199
+ return typeof value === 'object' && value !== null && 'nodeType' in value
200
+ }
201
+
202
+ function isNodeArray<T>(value: KeyedRenderResult<T>): value is readonly Node[] {
203
+ return Array.isArray(value) && (value.length === 0 || isNode(value[0]))
204
+ }
205
+
206
+ function removeNodes(parent: Node, nodes: readonly Node[]): void {
207
+ for (const node of nodes) {
208
+ if (node.parentNode === parent) parent.removeChild(node)
209
+ }
210
+ }
211
+
212
+ function disposeRecords<T, K>(
213
+ parent: Node,
214
+ records: Iterable<RecordState<T, K>>,
215
+ ): readonly [error: unknown, failed: boolean] {
216
+ let firstError: unknown
217
+ let failed = false
218
+ for (const record of records) {
219
+ removeNodes(parent, record[1])
220
+ try {
221
+ record[3]?.()
222
+ } catch (error) {
223
+ if (!failed) firstError = error
224
+ failed = true
225
+ }
226
+ }
227
+ return [firstError, failed]
228
+ }
229
+ import {
230
+ claimHydrationArrayRange,
231
+ finishHydrationArrayRange,
232
+ withHydrationInsertion,
233
+ } from './hydration-bridge.ts'
@@ -0,0 +1,212 @@
1
+ import { registerCompiledCleanup } from './compiled/core.ts'
2
+ import {
3
+ LINK_IDENTITY_PROPS,
4
+ META_IDENTITY_ATTRIBUTES,
5
+ META_IDENTITY_PROPS,
6
+ } from './dom/attributes.ts'
7
+ import { applyBaseDomProp, installDomPropHandler } from './dom/properties.ts'
8
+
9
+ type MetadataEntry = {
10
+ readonly elements: Element[]
11
+ readonly baseline: number
12
+ }
13
+
14
+ type MetadataState = {
15
+ readonly entries: Map<string, MetadataEntry>
16
+ readonly stylePrecedence: string[]
17
+ }
18
+
19
+ type MetadataOwner = {
20
+ readonly element: Element
21
+ readonly state: MetadataState
22
+ entry: MetadataEntry
23
+ key: string
24
+ }
25
+
26
+ const metadataByDocument = new WeakMap<Document, MetadataState>()
27
+ const metadataOwners = new WeakMap<Element, MetadataOwner>()
28
+ let propRekeyingInstalled = false
29
+
30
+ export function installFrameworkMetadataPropRekeying(): void {
31
+ if (propRekeyingInstalled) return
32
+ propRekeyingInstalled = true
33
+ installDomPropHandler((element, name, value) => {
34
+ const owner = metadataOwners.get(element)
35
+ if (owner === undefined || !isMetadataIdentityProp(element.localName, name)) return false
36
+ detachMetadataOwner(owner)
37
+ applyBaseDomProp(element, name, value)
38
+ if (name === 'precedence') {
39
+ element.setAttribute('data-precedence', String(value ?? 'default'))
40
+ element.removeAttribute('precedence')
41
+ }
42
+ attachMetadataOwner(owner)
43
+ return true
44
+ })
45
+ }
46
+
47
+ export function isFrameworkMetadataElement(
48
+ element: Element,
49
+ props: Readonly<Record<string, unknown>> | null = null,
50
+ ): boolean {
51
+ const name = element.localName
52
+ if (
53
+ (name === 'title' || name === 'meta' || name === 'link') &&
54
+ element.hasAttribute('itemprop')
55
+ ) {
56
+ return false
57
+ }
58
+ if (name === 'title' || name === 'meta') return true
59
+ if (name === 'link') {
60
+ if (
61
+ props !== null &&
62
+ ['onLoad', 'onError', 'disabled'].some((key) => Object.hasOwn(props, key))
63
+ ) {
64
+ return false
65
+ }
66
+ return element.getAttribute('rel') !== 'stylesheet' || element.hasAttribute('precedence')
67
+ }
68
+ if (name === 'style') return element.hasAttribute('href') && element.hasAttribute('precedence')
69
+ return name === 'script' && element.hasAttribute('async') && element.hasAttribute('src')
70
+ }
71
+
72
+ export function hoistFrameworkMetadata(element: Element): DocumentFragment {
73
+ const document = element.ownerDocument
74
+ const state = metadataState(document)
75
+ const key = metadataKey(element)
76
+ const owner: MetadataOwner = {
77
+ element,
78
+ state,
79
+ key,
80
+ entry: metadataEntry(document, state, key),
81
+ }
82
+ attachMetadataOwner(owner)
83
+ metadataOwners.set(element, owner)
84
+ element.removeAttribute('precedence')
85
+ registerCompiledCleanup(() => {
86
+ detachMetadataOwner(owner)
87
+ metadataOwners.delete(element)
88
+ })
89
+ return document.createDocumentFragment()
90
+ }
91
+
92
+ function metadataState(document: Document): MetadataState {
93
+ let state = metadataByDocument.get(document)
94
+ if (state !== undefined) return state
95
+ state = {
96
+ entries: new Map(),
97
+ stylePrecedence: [...document.head.children]
98
+ .filter((candidate) => metadataTier(candidate) === 2)
99
+ .map(stylePrecedence)
100
+ .filter((value, index, values) => values.indexOf(value) === index),
101
+ }
102
+ metadataByDocument.set(document, state)
103
+ return state
104
+ }
105
+
106
+ function metadataEntry(document: Document, state: MetadataState, key: string): MetadataEntry {
107
+ let entry = state.entries.get(key)
108
+ if (entry !== undefined) return entry
109
+ const existing = [...document.head.children].find(
110
+ (candidate) => isFrameworkMetadataElement(candidate) && metadataKey(candidate) === key,
111
+ )
112
+ entry = {
113
+ elements: existing === undefined ? [] : [existing],
114
+ baseline: existing === undefined ? 0 : 1,
115
+ }
116
+ state.entries.set(key, entry)
117
+ return entry
118
+ }
119
+
120
+ function attachMetadataOwner(owner: MetadataOwner): void {
121
+ const document = owner.element.ownerDocument
122
+ owner.key = metadataKey(owner.element)
123
+ owner.entry = metadataEntry(document, owner.state, owner.key)
124
+ owner.entry.elements.at(-1)?.remove()
125
+ owner.entry.elements.push(owner.element)
126
+ insertByPrecedence(document.head, owner.element, owner.state)
127
+ }
128
+
129
+ function detachMetadataOwner(owner: MetadataOwner): void {
130
+ const { element, entry, key, state } = owner
131
+ const index = entry.elements.indexOf(element)
132
+ if (index === -1) return
133
+ const visible = index === entry.elements.length - 1
134
+ entry.elements.splice(index, 1)
135
+ element.remove()
136
+ if (entry.elements.length === entry.baseline) {
137
+ if (visible && entry.elements.length !== 0) {
138
+ element.ownerDocument.head.append(entry.elements.at(-1)!)
139
+ }
140
+ if (state.entries.get(key) === entry) state.entries.delete(key)
141
+ } else if (visible) {
142
+ insertByPrecedence(element.ownerDocument.head, entry.elements.at(-1)!, state)
143
+ }
144
+ }
145
+
146
+ function isMetadataIdentityProp(elementName: string, propName: string): boolean {
147
+ if (propName === 'precedence') return elementName === 'link' || elementName === 'style'
148
+ if (elementName === 'meta') {
149
+ return (META_IDENTITY_PROPS as readonly string[]).includes(propName)
150
+ }
151
+ if (elementName === 'link') return (LINK_IDENTITY_PROPS as readonly string[]).includes(propName)
152
+ if (elementName === 'script') return propName === 'src'
153
+ return false
154
+ }
155
+
156
+ function metadataKey(element: Element): string {
157
+ const name = element.localName
158
+ if (name === 'title') return 'title'
159
+ if (name === 'meta') {
160
+ for (const attribute of META_IDENTITY_ATTRIBUTES) {
161
+ if (element.hasAttribute(attribute))
162
+ return `meta:${attribute}:${element.getAttribute(attribute)}`
163
+ }
164
+ }
165
+ if (name === 'link') {
166
+ return `link:${element.getAttribute('rel') ?? ''}:${element.getAttribute('href') ?? ''}:${element.getAttribute('as') ?? ''}`
167
+ }
168
+ if (name === 'style') return `style:${element.getAttribute('href') ?? ''}`
169
+ return `script:${element.getAttribute('src') ?? ''}`
170
+ }
171
+
172
+ function insertByPrecedence(head: HTMLHeadElement, element: Element, state: MetadataState): void {
173
+ const tier = metadataTier(element) ?? 1
174
+ const precedence = stylePrecedence(element)
175
+ if (tier === 2) {
176
+ if (!state.stylePrecedence.includes(precedence)) state.stylePrecedence.push(precedence)
177
+ element.setAttribute('data-precedence', precedence)
178
+ }
179
+ const before = [...head.children].find((candidate) => {
180
+ const candidateTier = metadataTier(candidate)
181
+ if (candidateTier === undefined) return false
182
+ if (candidateTier !== tier) return candidateTier > tier
183
+ return (
184
+ tier === 2 &&
185
+ state.stylePrecedence.indexOf(stylePrecedence(candidate)) >
186
+ state.stylePrecedence.indexOf(precedence)
187
+ )
188
+ })
189
+ head.insertBefore(element, before ?? null)
190
+ }
191
+
192
+ function metadataTier(element: Element): number | undefined {
193
+ const name = element.localName
194
+ if (name === 'style') return element.hasAttribute('data-precedence') ? 2 : undefined
195
+ if (name === 'script') return element.hasAttribute('src') ? 3 : undefined
196
+ if (name === 'link') {
197
+ const relation = element.getAttribute('rel')
198
+ if (relation === 'stylesheet') {
199
+ return element.hasAttribute('precedence') || element.hasAttribute('data-precedence')
200
+ ? 2
201
+ : undefined
202
+ }
203
+ if (['preconnect', 'dns-prefetch', 'preload', 'modulepreload'].includes(relation ?? ''))
204
+ return 0
205
+ return 1
206
+ }
207
+ return name === 'title' || name === 'meta' ? 1 : undefined
208
+ }
209
+
210
+ function stylePrecedence(element: Element): string {
211
+ return element.getAttribute('precedence') ?? element.getAttribute('data-precedence') ?? 'default'
212
+ }
@@ -0,0 +1,27 @@
1
+ import { binding, isCompiledBinding } from './compiled/core.ts'
2
+
3
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
4
+
5
+ type ContainerFallback = (() => unknown) | null
6
+
7
+ export function nestedProp(
8
+ input: unknown,
9
+ path: readonly string[],
10
+ containerFallbacks: readonly ContainerFallback[],
11
+ ): unknown {
12
+ const read = (): unknown => {
13
+ let value = isCompiledBinding(input) ? input[1]() : input
14
+ for (const [index, name] of path.entries()) {
15
+ if (value === undefined) value = containerFallbacks[index]?.()
16
+ if (value === null || value === undefined) {
17
+ throw new TypeError(
18
+ DEV ? `cannot destructure nested prop ${name} from a nullish value` : 'V015',
19
+ )
20
+ }
21
+ value = (value as Record<string, unknown>)[name]
22
+ }
23
+ return value
24
+ }
25
+
26
+ return isCompiledBinding(input) ? binding(input[2], input[3], read, input[4], input[5]) : read()
27
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ captureCompiledOwnerStack,
3
+ enableProfiling,
4
+ profiled,
5
+ recordCompiledDebugValue,
6
+ } from './compiled/core.ts'
7
+ import type {
8
+ CompiledBinding,
9
+ CompiledRenderValue,
10
+ ProfilerOnRender,
11
+ StructuralBinding,
12
+ } from './compiled/types.ts'
13
+
14
+ export interface ProfilerProps {
15
+ readonly children?: (() => CompiledRenderValue) | readonly [() => CompiledRenderValue]
16
+ readonly id: string | CompiledBinding<string>
17
+ readonly onRender: ProfilerOnRender | CompiledBinding<ProfilerOnRender>
18
+ }
19
+
20
+ export function Profiler(props: ProfilerProps): StructuralBinding {
21
+ const render = Array.isArray(props.children) ? props.children[0] : props.children
22
+ if (typeof render !== 'function') {
23
+ throw new TypeError('Profiler children must be a compiler-generated render function')
24
+ }
25
+ enableProfiling()
26
+ return profiled(props.id, props.onRender, render)
27
+ }
28
+
29
+ export function useDebugValue<Value>(
30
+ value: Value | CompiledBinding<Value>,
31
+ format?: (value: Value) => unknown,
32
+ ): void {
33
+ if (format !== undefined && typeof format !== 'function') {
34
+ throw new TypeError('useDebugValue formatter must be a function')
35
+ }
36
+ enableProfiling()
37
+ recordCompiledDebugValue(value, format)
38
+ }
39
+
40
+ export function captureOwnerStack(): string | null {
41
+ enableProfiling()
42
+ return captureCompiledOwnerStack()
43
+ }
@@ -0,0 +1,8 @@
1
+ export const VIDACT_RUNTIME_PROTOCOL = 'vidact-runtime-v2'
2
+
3
+ export function assertRuntimeProtocol(expected: string): void {
4
+ if (expected === VIDACT_RUNTIME_PROTOCOL) return
5
+ throw new Error(
6
+ `Vidact compiler/runtime protocol mismatch: compiled for ${expected}, runtime provides ${VIDACT_RUNTIME_PROTOCOL}`,
7
+ )
8
+ }