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

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 +73 -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 +148 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +3064 -2987
  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 +279 -222
  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 +8 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +118 -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 +17 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +244 -161
  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 +28 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +152 -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 +10 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +60 -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 +29 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +73 -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 +4647 -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 +491 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +179 -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 +202 -0
  220. package/src/hydration.ts +644 -0
  221. package/src/index.ts +94 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +379 -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 +313 -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 +103 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +132 -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,94 @@
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
+ compiledInlineEvent,
9
+ compiledEffect,
10
+ compiledImperativeHandle,
11
+ compiledInsertionEffect,
12
+ compiledLayoutEffect,
13
+ compiledRoot,
14
+ createCompiledContext,
15
+ createCompiledEffectEvent,
16
+ createCompiledExternalStore,
17
+ createCompiledId,
18
+ createCompiledMemo,
19
+ createCompiledProp,
20
+ createCompiledReducer,
21
+ createCompiledRestProp,
22
+ createCompiledScope,
23
+ createCompiledState,
24
+ createContext,
25
+ createNarrowCompiledScope,
26
+ createPortal,
27
+ deferred,
28
+ dispatch,
29
+ errorBoundary,
30
+ indexed,
31
+ keyed,
32
+ mountCompiled,
33
+ mountCompiledRef,
34
+ objectRest,
35
+ runWithCompiledContext,
36
+ use,
37
+ useCallback,
38
+ useContext,
39
+ useEffect,
40
+ useEffectEvent,
41
+ useId,
42
+ useImperativeHandle,
43
+ useInsertionEffect,
44
+ useLayoutEffect,
45
+ useMemo,
46
+ useSyncExternalStore,
47
+ when,
48
+ type ChoiceMode,
49
+ type MountCompiledOptions,
50
+ } from './compiled/core.ts'
51
+ export type {
52
+ CompiledBinding,
53
+ CompiledComponentResult,
54
+ CompiledContext,
55
+ CompiledErrorHandler,
56
+ CompiledRenderValue,
57
+ CompiledScope,
58
+ OwnedBlock,
59
+ StructuralBinding,
60
+ } from './compiled/types.ts'
61
+ export { compiledComponentSpread } from './component-spread.ts'
62
+ export {
63
+ Fragment,
64
+ h,
65
+ type DirectChild,
66
+ type DirectComponent,
67
+ type DirectProps,
68
+ } from './direct-dom.ts'
69
+ export { nestedProp } from './nested-prop.ts'
70
+ export { assertRuntimeProtocol, VIDACT_RUNTIME_PROTOCOL } from './protocol.ts'
71
+ export { createRef, useRef, type MutableRef } from './ref.ts'
72
+ export {
73
+ cloneRenderable,
74
+ cloneRenderableComponent,
75
+ createComponentRenderable,
76
+ createReactElement,
77
+ createReactElement as createElement,
78
+ createRenderable,
79
+ dynamicIntrinsicComponent,
80
+ forwardedRef,
81
+ isRenderable,
82
+ keyedFragmentComponent,
83
+ renderableChildren,
84
+ renderableMarker,
85
+ renderableProps,
86
+ renderableRef,
87
+ renderableToArray,
88
+ type CompiledRenderable,
89
+ type RenderablePropsInput,
90
+ } from './renderable.ts'
91
+ export { createRoot, mountHotRoot, type CompiledRoot, type HotContext } from './root.ts'
92
+ export { combineSources, intersectsSources, source, type SourceMask } from './source-mask.ts'
93
+ export { compiledSpread } from './spread.ts'
94
+ 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,379 @@
1
+ export interface KeyedDisposeTarget {
2
+ readonly [3]: () => void
3
+ }
4
+
5
+ export type KeyedDisposer = (() => void) | KeyedDisposeTarget
6
+
7
+ export type KeyedItem<T> = readonly [
8
+ nodes: readonly Node[],
9
+ update?: ((value: T, index: number) => void) | undefined,
10
+ dispose?: KeyedDisposer | undefined,
11
+ ]
12
+
13
+ export type KeyedRenderResult<T> = Node | readonly Node[] | KeyedItem<T>
14
+
15
+ export interface KeyedListOptions<T, K> {
16
+ readonly key: (value: T, index: number) => K
17
+ readonly render: (value: T, index: number) => KeyedRenderResult<T>
18
+ }
19
+
20
+ export interface KeyedList<T> {
21
+ readonly dispose: () => void
22
+ readonly parent: () => Node | null
23
+ readonly update: (values: readonly T[]) => readonly Node[]
24
+ }
25
+
26
+ type RecordState<T, K> = readonly [
27
+ key: K,
28
+ nodes: readonly Node[],
29
+ update: ((value: T, index: number) => void) | undefined,
30
+ dispose: KeyedDisposer | undefined,
31
+ ]
32
+
33
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
34
+
35
+ export function createKeyedList<T, K>(
36
+ parent: Node,
37
+ options: KeyedListOptions<T, K>,
38
+ before: Node | null = null,
39
+ ): KeyedList<T> {
40
+ const hydratedRange = claimHydrationArrayRange(parent)
41
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:list' : '')
42
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:list' : '')
43
+ if (hydratedRange === undefined) {
44
+ parent.insertBefore(start, before)
45
+ parent.insertBefore(end, before)
46
+ }
47
+
48
+ let disposed = false
49
+ let hydrationPending = hydratedRange !== undefined
50
+ let records: readonly RecordState<T, K>[] = []
51
+
52
+ const update = (values: readonly T[]): readonly Node[] => {
53
+ if (disposed) throw new Error(DEV ? 'cannot update a disposed keyed list' : 'V801')
54
+ const currentParent = end.parentNode
55
+ if (currentParent === null || start.parentNode !== currentParent) {
56
+ throw new Error(DEV ? 'cannot update a detached keyed list' : 'V802')
57
+ }
58
+
59
+ const keys = values.map(options.key)
60
+ assertValidKeys(keys)
61
+ assertUniqueKeys(keys)
62
+
63
+ if (!hydrationPending && values.length === 0 && records.length > 0) {
64
+ const removed = records
65
+ records = []
66
+ deleteRangeContents(start, end)
67
+ const cleanup = disposeRecordOwners(removed)
68
+ if (cleanup[1]) throw cleanup[0]
69
+ return []
70
+ }
71
+
72
+ if (!hydrationPending && hasRetainedPrefix(records, keys)) {
73
+ const retainedCount = Math.min(records.length, values.length)
74
+ const created: RecordState<T, K>[] = []
75
+ try {
76
+ for (let index = records.length; index < values.length; index += 1) {
77
+ const value = values[index] as T
78
+ const item = normalizeRenderResult(
79
+ withHydrationInsertion(parent, end, () => options.render(value, index)),
80
+ )
81
+ created.push([keys[index] as K, item[0], item[1], item[2]])
82
+ }
83
+ for (let index = 0; index < retainedCount; index += 1) {
84
+ records[index]![2]?.(values[index] as T, index)
85
+ }
86
+ } catch (error) {
87
+ disposeRecords(currentParent, created)
88
+ throw error
89
+ }
90
+
91
+ const cleanup = disposeRecords(currentParent, records.slice(values.length))
92
+ const fragment = document.createDocumentFragment()
93
+ for (const record of created) {
94
+ for (const node of record[1]) fragment.append(node)
95
+ }
96
+ currentParent.insertBefore(fragment, end)
97
+ if (records.length !== values.length) {
98
+ records = retainedCount === 0 ? created : [...records.slice(0, retainedCount), ...created]
99
+ }
100
+ if (cleanup[1]) throw cleanup[0]
101
+ return collectRecordNodes(created)
102
+ }
103
+
104
+ const previousByKey = new Map<K, readonly [record: RecordState<T, K>, index: number]>()
105
+ for (let index = 0; index < records.length; index += 1) {
106
+ const record = records[index] as RecordState<T, K>
107
+ previousByKey.set(record[0], [record, index])
108
+ }
109
+ const created: RecordState<T, K>[] = []
110
+ const retained: Array<readonly [RecordState<T, K>, T, number]> = []
111
+ const previousIndexes: number[] = []
112
+ let nextRecords: RecordState<T, K>[]
113
+ try {
114
+ nextRecords = values.map((value, index): RecordState<T, K> => {
115
+ const key = keys[index] as K
116
+ const previous = previousByKey.get(key)
117
+ if (previous !== undefined) {
118
+ previousByKey.delete(key)
119
+ retained.push([previous[0], value, index])
120
+ previousIndexes.push(previous[1])
121
+ return previous[0]
122
+ }
123
+
124
+ const item = normalizeRenderResult(
125
+ withHydrationInsertion(parent, end, () => options.render(value, index)),
126
+ )
127
+ const record: RecordState<T, K> = [key, item[0], item[1], item[2]]
128
+ created.push(record)
129
+ previousIndexes.push(-1)
130
+ return record
131
+ })
132
+ for (const [record, value, index] of retained) record[2]?.(value, index)
133
+ } catch (error) {
134
+ disposeRecords(currentParent, created)
135
+ throw error
136
+ }
137
+
138
+ const cleanup = disposeRecords(currentParent, mapRecords(previousByKey.values()))
139
+
140
+ const stableRecords = longestIncreasingSubsequence(previousIndexes)
141
+ const hasRetainedMoves = previousIndexes.some(
142
+ (previousIndex, index) => previousIndex >= 0 && stableRecords[index] === 0,
143
+ )
144
+ const activeElement = hasRetainedMoves
145
+ ? document.activeElement instanceof HTMLElement &&
146
+ currentParent.contains(document.activeElement)
147
+ ? document.activeElement
148
+ : null
149
+ : null
150
+ const selectionControl =
151
+ activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement
152
+ ? activeElement
153
+ : null
154
+ const selection =
155
+ selectionControl === null
156
+ ? null
157
+ : ([selectionControl.selectionStart, selectionControl.selectionEnd] as const)
158
+ let cursor: Node = end
159
+ for (let recordIndex = nextRecords.length - 1; recordIndex >= 0; recordIndex -= 1) {
160
+ const record = nextRecords[recordIndex] as RecordState<T, K>
161
+ if (previousIndexes[recordIndex] === -1 || stableRecords[recordIndex] === 0) {
162
+ for (let nodeIndex = record[1].length - 1; nodeIndex >= 0; nodeIndex -= 1) {
163
+ const node = record[1][nodeIndex] as Node
164
+ if (node.nextSibling !== cursor) moveBefore(currentParent, node, cursor)
165
+ cursor = node
166
+ }
167
+ } else {
168
+ cursor = record[1][0] as Node
169
+ }
170
+ }
171
+ if (
172
+ activeElement !== null &&
173
+ document.activeElement !== activeElement &&
174
+ currentParent.contains(activeElement)
175
+ ) {
176
+ activeElement.focus({ preventScroll: true })
177
+ if (
178
+ selectionControl !== null &&
179
+ selection !== null &&
180
+ selection[0] !== null &&
181
+ selection[1] !== null
182
+ ) {
183
+ selectionControl.setSelectionRange(selection[0], selection[1])
184
+ }
185
+ }
186
+
187
+ records = nextRecords
188
+ if (hydrationPending) {
189
+ hydrationPending = false
190
+ finishHydrationArrayRange(currentParent, end)
191
+ }
192
+ if (cleanup[1]) throw cleanup[0]
193
+ return collectRecordNodes(created)
194
+ }
195
+
196
+ const dispose = (): void => {
197
+ if (disposed) return
198
+ disposed = true
199
+ const currentParent = end.parentNode
200
+ const cleanup =
201
+ currentParent === null
202
+ ? ([undefined, false] as const)
203
+ : disposeRecords(currentParent, records)
204
+ records = []
205
+ start.remove()
206
+ end.remove()
207
+ if (cleanup[1]) throw cleanup[0]
208
+ }
209
+
210
+ return { dispose, parent: () => end.parentNode, update }
211
+ }
212
+
213
+ function* mapRecords<T, K>(
214
+ entries: Iterable<readonly [record: RecordState<T, K>, index: number]>,
215
+ ): Iterable<RecordState<T, K>> {
216
+ for (const [record] of entries) yield record
217
+ }
218
+
219
+ function collectRecordNodes<T, K>(records: readonly RecordState<T, K>[]): Node[] {
220
+ const nodes: Node[] = []
221
+ for (const record of records) nodes.push(...record[1])
222
+ return nodes
223
+ }
224
+
225
+ function longestIncreasingSubsequence(indexes: readonly number[]): Uint8Array {
226
+ const predecessors = new Int32Array(indexes.length).fill(-1)
227
+ const tails: number[] = []
228
+ for (let index = 0; index < indexes.length; index += 1) {
229
+ const value = indexes[index] as number
230
+ if (value < 0) continue
231
+ let low = 0
232
+ let high = tails.length
233
+ while (low < high) {
234
+ const middle = (low + high) >>> 1
235
+ if ((indexes[tails[middle] as number] as number) < value) low = middle + 1
236
+ else high = middle
237
+ }
238
+ if (low > 0) predecessors[index] = tails[low - 1] as number
239
+ tails[low] = index
240
+ }
241
+
242
+ const stable = new Uint8Array(indexes.length)
243
+ let index = tails.at(-1) ?? -1
244
+ while (index >= 0) {
245
+ stable[index] = 1
246
+ index = predecessors[index] as number
247
+ }
248
+ return stable
249
+ }
250
+
251
+ function hasRetainedPrefix<T, K>(
252
+ records: readonly RecordState<T, K>[],
253
+ keys: readonly K[],
254
+ ): boolean {
255
+ const retainedCount = Math.min(records.length, keys.length)
256
+ for (let index = 0; index < retainedCount; index += 1) {
257
+ if (records[index]![0] !== keys[index]) return false
258
+ }
259
+ return true
260
+ }
261
+
262
+ function moveBefore(parent: Node, node: Node, before: Node): void {
263
+ const statePreservingParent = parent as Node & {
264
+ moveBefore?: (node: Node, before: Node | null) => void
265
+ }
266
+ if (statePreservingParent.moveBefore !== undefined && node.parentNode === parent) {
267
+ statePreservingParent.moveBefore(node, before)
268
+ } else {
269
+ parent.insertBefore(node, before)
270
+ }
271
+ }
272
+
273
+ function assertValidKeys<K>(keys: readonly K[]): void {
274
+ for (const key of keys) {
275
+ if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'bigint') {
276
+ throw new Error(DEV ? `invalid key in keyed list: ${String(key)}` : 'V803')
277
+ }
278
+ }
279
+ }
280
+
281
+ function assertUniqueKeys<K>(keys: readonly K[]): void {
282
+ const seen = new Set<K>()
283
+ for (const key of keys) {
284
+ if (seen.has(key)) {
285
+ throw new Error(DEV ? `duplicate key in keyed list: ${String(key)}` : 'V804')
286
+ }
287
+ seen.add(key)
288
+ }
289
+ }
290
+
291
+ function normalizeRenderResult<T>(result: KeyedRenderResult<T>): KeyedItem<T> {
292
+ if (isNode(result)) return [normalizeNodes([result])]
293
+ if (isNodeArray(result)) return [normalizeNodes(result)]
294
+ return [normalizeNodes(result[0]), result[1], result[2]]
295
+ }
296
+
297
+ function normalizeNodes(nodes: readonly Node[]): readonly Node[] {
298
+ if (nodes.length === 0) return [document.createComment(DEV ? 'vidact:empty' : '')]
299
+ let firstFragment = -1
300
+ for (let index = 0; index < nodes.length; index += 1) {
301
+ if (nodes[index]!.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
302
+ firstFragment = index
303
+ break
304
+ }
305
+ }
306
+ if (firstFragment === -1) return nodes
307
+ const normalized = nodes.slice(0, firstFragment) as Node[]
308
+ for (let index = firstFragment; index < nodes.length; index += 1) {
309
+ const node = nodes[index]!
310
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) normalized.push(...node.childNodes)
311
+ else normalized.push(node)
312
+ }
313
+ return normalized.length === 0 ? [document.createComment(DEV ? 'vidact:empty' : '')] : normalized
314
+ }
315
+
316
+ function isNode(value: unknown): value is Node {
317
+ return typeof value === 'object' && value !== null && 'nodeType' in value
318
+ }
319
+
320
+ function isNodeArray<T>(value: KeyedRenderResult<T>): value is readonly Node[] {
321
+ return Array.isArray(value) && (value.length === 0 || isNode(value[0]))
322
+ }
323
+
324
+ function removeNodes(parent: Node, nodes: readonly Node[]): void {
325
+ for (const node of nodes) {
326
+ if (node.parentNode === parent) parent.removeChild(node)
327
+ }
328
+ }
329
+
330
+ function disposeRecords<T, K>(
331
+ parent: Node,
332
+ records: Iterable<RecordState<T, K>>,
333
+ ): readonly [error: unknown, failed: boolean] {
334
+ let firstError: unknown
335
+ let failed = false
336
+ for (const record of records) {
337
+ removeNodes(parent, record[1])
338
+ try {
339
+ disposeRecord(record[3])
340
+ } catch (error) {
341
+ if (!failed) firstError = error
342
+ failed = true
343
+ }
344
+ }
345
+ return [firstError, failed]
346
+ }
347
+
348
+ function disposeRecordOwners<T, K>(
349
+ records: Iterable<RecordState<T, K>>,
350
+ ): readonly [error: unknown, failed: boolean] {
351
+ let firstError: unknown
352
+ let failed = false
353
+ for (const record of records) {
354
+ try {
355
+ disposeRecord(record[3])
356
+ } catch (error) {
357
+ if (!failed) firstError = error
358
+ failed = true
359
+ }
360
+ }
361
+ return [firstError, failed]
362
+ }
363
+
364
+ function disposeRecord(disposer: KeyedDisposer | undefined): void {
365
+ if (typeof disposer === 'function') disposer()
366
+ else disposer?.[3]()
367
+ }
368
+
369
+ function deleteRangeContents(start: Node, end: Node): void {
370
+ const range = start.ownerDocument?.createRange() ?? document.createRange()
371
+ range.setStartAfter(start)
372
+ range.setEndBefore(end)
373
+ range.deleteContents()
374
+ }
375
+ import {
376
+ claimHydrationArrayRange,
377
+ finishHydrationArrayRange,
378
+ withHydrationInsertion,
379
+ } from './hydration-bridge.ts'