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

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 +37 -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 +2669 -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 +61 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +67 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +31 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +376 -462
  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 +592 -706
  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 +4026 -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 +183 -0
  220. package/src/hydration.ts +556 -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 +1143 -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
@@ -0,0 +1,4026 @@
1
+ import {
2
+ currentIntrinsicNamespace,
3
+ withIntrinsicNamespace,
4
+ type IntrinsicNamespace,
5
+ } from '../dom/intrinsic.ts'
6
+ import {
7
+ HydrationMismatch,
8
+ beginHydration,
9
+ borrowHydrationSlotRange,
10
+ claimHydrationArrayRange,
11
+ claimHydrationComponentMount,
12
+ claimHydrationComponentRange,
13
+ claimHydrationNode,
14
+ claimHydrationSlotRange,
15
+ claimHydrationSuspenseFallback,
16
+ claimHydrationText,
17
+ claimHydrationTextRange,
18
+ hydrationRangeParent,
19
+ hydrationRootMarkers,
20
+ finishHydration,
21
+ finishHydrationArrayRange,
22
+ hydrationCursor,
23
+ hydrationFragmentChildren,
24
+ hydrationInsertionPoint,
25
+ isHydrating,
26
+ isHydrationMismatch,
27
+ noteHydrationStructuralParent,
28
+ withoutHydration,
29
+ withHydrationComponentRange,
30
+ withHydrationInsertion,
31
+ withHydrationCursor,
32
+ } from '../hydration-bridge.ts'
33
+ import { createIndexedList } from '../indexed-list.ts'
34
+ import { createKeyedList } from '../keyed-list.ts'
35
+ import {
36
+ canReconcileRenderable,
37
+ isRenderableProtocol,
38
+ materializeRenderable,
39
+ materializeRenderableWithInput,
40
+ RENDERABLE,
41
+ renderableIdentity,
42
+ renderablePropsSnapshot,
43
+ type RenderableProtocol,
44
+ } from '../renderable-protocol.ts'
45
+ import { scheduleDeferredTask, scheduleTask, type CancelScheduledTask } from '../scheduler.ts'
46
+ import { intersectsSources, isEmptySources, unionSources, type SourceMask } from '../source-mask.ts'
47
+ import { createStateSlot, type StateSlot } from '../state-slot.ts'
48
+ import { isSuspension, subscribeResource, useAsync } from './async-resource.ts'
49
+ import {
50
+ BINDING,
51
+ COMPONENT_SPREAD_SOURCE as componentSpreadSourceSymbol,
52
+ CONTEXT,
53
+ STRUCTURAL,
54
+ type AsyncResource,
55
+ type CompiledBinding,
56
+ type CompiledComponentResult,
57
+ type CompiledContext,
58
+ type CompiledErrorHandler,
59
+ type CompiledRenderValue,
60
+ type CompiledScope,
61
+ type ProfilerOnRender,
62
+ type StructuralBinding,
63
+ } from './types.ts'
64
+ import { hasInvalidChild } from './validation.ts'
65
+
66
+ export const COMPONENT_SPREAD_SOURCE = componentSpreadSourceSymbol
67
+
68
+ const MAX_FLUSH_PASSES = 100
69
+ export const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
70
+ let retainedUiEnabled = typeof __VIDACT_RETAINED_UI__ !== 'undefined' && __VIDACT_RETAINED_UI__
71
+ let profilingEnabled = false
72
+ let activeOwnerCount = 0
73
+ let createdOwnerCount = 0
74
+ const noop = (): void => {}
75
+
76
+ type ContextFrame = {
77
+ readonly context: CompiledContext<unknown>
78
+ readonly input: unknown
79
+ readonly parent: ContextFrame | null
80
+ readonly owner: Owner | null
81
+ }
82
+
83
+ type ErrorBoundaryHandler = {
84
+ readonly handle: (error: unknown) => void
85
+ readonly parent: ErrorBoundaryHandler | null
86
+ }
87
+
88
+ type RootIdentity = {
89
+ mounted: boolean
90
+ nextId: number
91
+ readonly prefix: string
92
+ readonly onCaughtError: CompiledErrorHandler | undefined
93
+ readonly onUncaughtError: CompiledErrorHandler | undefined
94
+ }
95
+
96
+ type PortalPublication = readonly [commit: () => void, rollback: () => void]
97
+
98
+ type Owner = [
99
+ disposed: boolean,
100
+ cleanups: Set<() => void>,
101
+ context: ContextFrame | null,
102
+ rootIdentity: RootIdentity,
103
+ errorBoundary: ErrorBoundaryHandler | null,
104
+ retainedConnection?: RetainedConnection | null,
105
+ profileContext?: ProfileContext | null,
106
+ debugName?: string | null,
107
+ ]
108
+
109
+ type DebugOwnerFrame = {
110
+ readonly name: string
111
+ readonly parent: DebugOwnerFrame | null
112
+ readonly values: unknown[]
113
+ }
114
+
115
+ type ProfileContext = {
116
+ readonly frame: DebugOwnerFrame | null
117
+ readonly boundary: ProfileBoundary | null
118
+ }
119
+
120
+ type ProfileBoundary = {
121
+ readonly parent: ProfileBoundary | null
122
+ id: string
123
+ onRender: ProfilerOnRender
124
+ baseDuration: number
125
+ mounted: boolean
126
+ pendingDuration: number
127
+ pendingStart: number
128
+ scheduled: boolean
129
+ }
130
+
131
+ type RetainedResource = {
132
+ readonly owner: Owner
133
+ readonly connect: () => void
134
+ readonly disconnect: () => void
135
+ active: boolean
136
+ connected: boolean
137
+ disposed: boolean
138
+ readonly phase: number
139
+ }
140
+
141
+ type RetainedConnection = {
142
+ readonly parent: RetainedConnection | null
143
+ readonly children: Set<RetainedConnection>
144
+ readonly resources: Set<RetainedResource>
145
+ readonly deferredFlushes: Map<() => void, CancelScheduledTask>
146
+ readonly afterFlush: Set<() => void>
147
+ visible: boolean
148
+ connected: boolean
149
+ disposed: boolean
150
+ }
151
+
152
+ type CompiledUpdater = [
153
+ reads: SourceMask,
154
+ writes: SourceMask | undefined,
155
+ run: (active: SourceMask) => void,
156
+ active: boolean,
157
+ ]
158
+
159
+ type SourceOperations = readonly [
160
+ empty: (mask: SourceMask) => boolean,
161
+ intersects: (left: SourceMask, right: SourceMask) => boolean,
162
+ union: (left: SourceMask, right: SourceMask) => SourceMask,
163
+ ]
164
+
165
+ const wideSourceOperations: SourceOperations = [isEmptySources, intersectsSources, unionSources]
166
+ const narrowSourceOperations: SourceOperations = [
167
+ (mask) => mask === 0,
168
+ (left, right) => ((left as number) & (right as number)) !== 0,
169
+ (left, right) => ((left as number) | (right as number)) >>> 0,
170
+ ]
171
+
172
+ type RenderValue = CompiledRenderValue
173
+
174
+ export function deferred(render: () => CompiledRenderValue): StructuralBinding {
175
+ noteHydrationStructuralParent()
176
+ return [
177
+ STRUCTURAL,
178
+ (parent, before) => {
179
+ const hydrated = claimHydrationSlotRange(parent)
180
+ if (hydrated === undefined) insertValue(parent, render(), before)
181
+ else insertValue(parent, render(), hydrated[1])
182
+ },
183
+ 'slot',
184
+ ]
185
+ }
186
+
187
+ /** @internal */
188
+ export function retainedActivity(
189
+ modeInput: 'visible' | 'hidden' | CompiledBinding<'visible' | 'hidden'>,
190
+ render: () => CompiledRenderValue,
191
+ ): StructuralBinding {
192
+ noteHydrationStructuralParent()
193
+ const context = activeContextFrame ?? activeOwner?.[2] ?? null
194
+ const rootIdentity = activeOwner?.[3] ?? activeRootIdentity
195
+ if (rootIdentity === null) {
196
+ throw new Error(DEV ? 'Activity must run during compiled root construction' : 'V033')
197
+ }
198
+ const parentErrorBoundary = activeOwner?.[4] ?? null
199
+ const parentConnection = activeOwner?.[5] ?? null
200
+ let mounted = false
201
+ return [
202
+ STRUCTURAL,
203
+ (parent, before) => {
204
+ if (mounted) throw new Error(DEV ? 'compiled Activity is already mounted' : 'V008')
205
+ mounted = true
206
+ const lifetimeOwner = activeOwner
207
+ if (lifetimeOwner === null) {
208
+ throw new Error(DEV ? 'Activity must mount inside an owned compiled root' : 'V033')
209
+ }
210
+ const readMode = (): 'visible' | 'hidden' =>
211
+ validateActivityMode(isCompiledBinding(modeInput) ? modeInput[1]() : modeInput)
212
+ let currentMode = readMode()
213
+ const connection = createRetainedConnection(parentConnection, currentMode === 'visible')
214
+ const childOwner = createOwner(context, rootIdentity, parentErrorBoundary, connection)
215
+ const hydratedRange = claimHydrationSlotRange(parent)
216
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:activity' : '')
217
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:activity' : '')
218
+ if (hydratedRange === undefined) {
219
+ parent.insertBefore(start, before)
220
+ parent.insertBefore(end, before)
221
+ }
222
+ const currentParent = rangeParent(start, end, 'Activity boundary')
223
+ const hiddenDisplays = new Map<Element, readonly [string, string]>()
224
+ const hiddenTexts = new Map<Text, Comment>()
225
+ const heldText = document.createDocumentFragment()
226
+ let restoreHydratedDisplays = hydratedRange !== undefined && currentMode === 'hidden'
227
+
228
+ const conceal = (): void => {
229
+ for (const node of nodesBetween(start, end)) {
230
+ if (node instanceof Element && 'style' in node) {
231
+ const styled = node as HTMLElement | SVGElement
232
+ const previous = hiddenDisplays.get(node)
233
+ const hydratedDisplay = restoreHydratedDisplays
234
+ ? readServerHiddenDisplay(node)
235
+ : undefined
236
+ if (previous === undefined && hydratedDisplay !== undefined) {
237
+ hiddenDisplays.set(node, hydratedDisplay)
238
+ } else if (
239
+ previous === undefined ||
240
+ styled.style.display !== 'none' ||
241
+ styled.style.getPropertyPriority('display') !== 'important'
242
+ ) {
243
+ hiddenDisplays.set(node, [
244
+ styled.style.display,
245
+ styled.style.getPropertyPriority('display'),
246
+ ])
247
+ }
248
+ styled.style.setProperty('display', 'none', 'important')
249
+ } else if (node instanceof Text && !hiddenTexts.has(node)) {
250
+ const placeholder = document.createComment(DEV ? 'vidact:activity-text' : '')
251
+ node.replaceWith(placeholder)
252
+ heldText.append(node)
253
+ hiddenTexts.set(node, placeholder)
254
+ }
255
+ }
256
+ restoreHydratedDisplays = false
257
+ }
258
+ const reveal = (): void => {
259
+ for (const [element, [display, priority]] of hiddenDisplays) {
260
+ const styled = element as HTMLElement | SVGElement
261
+ if (display === '') styled.style.removeProperty('display')
262
+ else styled.style.setProperty('display', display, priority)
263
+ }
264
+ hiddenDisplays.clear()
265
+ for (const [text, placeholder] of hiddenTexts) {
266
+ if (placeholder.parentNode !== null) placeholder.replaceWith(text)
267
+ }
268
+ hiddenTexts.clear()
269
+ }
270
+ const setMode = (nextMode: 'visible' | 'hidden'): void => {
271
+ if (nextMode === currentMode) return
272
+ currentMode = nextMode
273
+ if (nextMode === 'hidden') {
274
+ connection.visible = false
275
+ updateRetainedConnection(connection)
276
+ conceal()
277
+ } else {
278
+ reveal()
279
+ connection.visible = true
280
+ updateRetainedConnection(connection)
281
+ concealDisconnectedDescendants(connection)
282
+ }
283
+ }
284
+
285
+ connection.afterFlush.add(conceal)
286
+ let stagedNodes: readonly Node[] = []
287
+ try {
288
+ const [fragment, nodes] =
289
+ hydratedRange === undefined
290
+ ? stageRender(render, childOwner)
291
+ : withHydrationInsertion(currentParent, end, () => stageRender(render, childOwner))
292
+ stagedNodes = nodes
293
+ currentParent.insertBefore(fragment, end)
294
+ if (currentMode === 'hidden') conceal()
295
+ commitPublishedNodes(nodes)
296
+ } catch (error) {
297
+ disposePublished(childOwner, stagedNodes)
298
+ disposeRetainedConnection(connection)
299
+ throw error
300
+ }
301
+ const removeModeUpdater = isCompiledBinding(modeInput)
302
+ ? subscribeBinding(modeInput, () => setMode(readMode()))
303
+ : noop
304
+ onCleanup(() => {
305
+ removeModeUpdater()
306
+ reveal()
307
+ try {
308
+ disposeRange(childOwner, start, end)
309
+ } finally {
310
+ disposeRetainedConnection(connection)
311
+ start.remove()
312
+ end.remove()
313
+ }
314
+ })
315
+ },
316
+ 'slot',
317
+ ]
318
+ }
319
+
320
+ /** @internal */
321
+ export function enableRetainedUi(): void {
322
+ retainedUiEnabled = true
323
+ }
324
+
325
+ /** @internal */
326
+ export function enableProfiling(): void {
327
+ profilingEnabled = DEV
328
+ }
329
+
330
+ /** @internal */
331
+ export function profiled(
332
+ idInput: string | CompiledBinding<string>,
333
+ onRenderInput: ProfilerOnRender | CompiledBinding<ProfilerOnRender>,
334
+ render: () => CompiledRenderValue,
335
+ ): StructuralBinding {
336
+ if (!DEV) return deferred(render)
337
+ noteHydrationStructuralParent()
338
+ const context = activeContextFrame ?? activeOwner?.[2] ?? null
339
+ const rootIdentity = activeOwner?.[3] ?? activeRootIdentity
340
+ if (rootIdentity === null) {
341
+ throw new Error(DEV ? 'Profiler must run during compiled root construction' : 'V035')
342
+ }
343
+ const parentErrorBoundary = activeOwner?.[4] ?? null
344
+ const parentConnection = activeOwner?.[5] ?? null
345
+ const parentProfile = activeOwner?.[6] ?? null
346
+ let mounted = false
347
+ return [
348
+ STRUCTURAL,
349
+ (parent, before) => {
350
+ if (mounted) throw new Error(DEV ? 'compiled Profiler is already mounted' : 'V008')
351
+ mounted = true
352
+ const readId = (): string => {
353
+ const id = isCompiledBinding(idInput) ? idInput[1]() : idInput
354
+ if (typeof id !== 'string') throw new TypeError('Profiler id must be a string')
355
+ return id
356
+ }
357
+ const readOnRender = (): ProfilerOnRender => {
358
+ const onRender = isCompiledBinding(onRenderInput) ? onRenderInput[1]() : onRenderInput
359
+ if (typeof onRender !== 'function') {
360
+ throw new TypeError('Profiler onRender must be a function')
361
+ }
362
+ return onRender
363
+ }
364
+ const boundary: ProfileBoundary = {
365
+ parent: parentProfile?.boundary ?? null,
366
+ id: readId(),
367
+ onRender: readOnRender(),
368
+ baseDuration: 0,
369
+ mounted: false,
370
+ pendingDuration: 0,
371
+ pendingStart: 0,
372
+ scheduled: false,
373
+ }
374
+ const childOwner = createOwner(context, rootIdentity, parentErrorBoundary, parentConnection, {
375
+ frame: parentProfile?.frame ?? null,
376
+ boundary,
377
+ })
378
+ const hydratedRange = claimHydrationSlotRange(parent)
379
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:profiler' : '')
380
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:profiler' : '')
381
+ if (hydratedRange === undefined) {
382
+ parent.insertBefore(start, before)
383
+ parent.insertBefore(end, before)
384
+ }
385
+ const currentParent = rangeParent(start, end, 'Profiler boundary')
386
+ let stagedNodes: readonly Node[] = []
387
+ const started = profileNow()
388
+ try {
389
+ const [fragment, nodes] =
390
+ hydratedRange === undefined
391
+ ? stageRender(render, childOwner)
392
+ : withHydrationInsertion(currentParent, end, () => stageRender(render, childOwner))
393
+ stagedNodes = nodes
394
+ currentParent.insertBefore(fragment, end)
395
+ commitPublishedNodes(nodes)
396
+ } catch (error) {
397
+ disposePublished(childOwner, stagedNodes)
398
+ throw error
399
+ }
400
+ const finished = profileNow()
401
+ boundary.baseDuration = finished - started
402
+ boundary.mounted = true
403
+ emitProfileMeasure(childOwner, 'range', started, finished)
404
+ runOwnerTask(childOwner, () =>
405
+ boundary.onRender(
406
+ boundary.id,
407
+ 'mount',
408
+ boundary.baseDuration,
409
+ boundary.baseDuration,
410
+ started,
411
+ finished,
412
+ ),
413
+ )
414
+ const removeIdUpdater = isCompiledBinding(idInput)
415
+ ? subscribeBinding(idInput, () => {
416
+ boundary.id = readId()
417
+ })
418
+ : noop
419
+ const removeCallbackUpdater = isCompiledBinding(onRenderInput)
420
+ ? subscribeBinding(onRenderInput, () => {
421
+ boundary.onRender = readOnRender()
422
+ })
423
+ : noop
424
+ onCleanup(() => {
425
+ removeCallbackUpdater()
426
+ removeIdUpdater()
427
+ disposeRange(childOwner, start, end)
428
+ })
429
+ },
430
+ 'slot',
431
+ ]
432
+ }
433
+
434
+ /** @internal */
435
+ export function recordCompiledDebugValue<Value>(
436
+ valueInput: Value | CompiledBinding<Value>,
437
+ format?: (value: Value) => unknown,
438
+ ): void {
439
+ if (!profilingEnabled) return
440
+ const owner = activeConstructionOwner ?? activeOwner
441
+ if (owner === null) {
442
+ throw new Error(DEV ? 'useDebugValue must run inside a compiled component' : 'V036')
443
+ }
444
+ const frame = ensureProfileContext(owner).frame
445
+ if (frame === null) {
446
+ throw new Error(DEV ? 'useDebugValue must run inside a compiled component' : 'V036')
447
+ }
448
+ const read = (): unknown => {
449
+ const value = isCompiledBinding(valueInput) ? valueInput[1]() : valueInput
450
+ return format === undefined ? value : format(value)
451
+ }
452
+ const index = frame.values.length
453
+ frame.values.push(read())
454
+ if (isCompiledBinding(valueInput)) {
455
+ const remove = subscribeBinding(valueInput, () => {
456
+ frame.values[index] = read()
457
+ })
458
+ owner[1].add(remove)
459
+ }
460
+ }
461
+
462
+ /** @internal */
463
+ export function captureCompiledOwnerStack(): string | null {
464
+ if (!profilingEnabled) return null
465
+ const owner = activeConstructionOwner ?? activeOwner
466
+ let frame = owner === null ? null : ensureProfileContext(owner).frame
467
+ if (frame === null) return null
468
+ const lines: string[] = []
469
+ while (frame !== null) {
470
+ const values = frame.values.length === 0 ? '' : ` [${frame.values.map(String).join(', ')}]`
471
+ lines.push(`\n at ${frame.name}${values}`)
472
+ frame = frame.parent
473
+ }
474
+ return lines.join('')
475
+ }
476
+
477
+ function validateActivityMode(mode: unknown): 'visible' | 'hidden' {
478
+ if (mode === 'visible' || mode === 'hidden') return mode
479
+ throw new TypeError(DEV ? 'Activity mode must be "visible" or "hidden"' : 'V034')
480
+ }
481
+
482
+ function readServerHiddenDisplay(element: Element): readonly [string, string] | undefined {
483
+ const cssText = element.getAttribute('style')
484
+ const hiddenDeclaration = 'display:none!important'
485
+ if (cssText === null || !cssText.endsWith(hiddenDeclaration)) return undefined
486
+ let authoredCssText = cssText.slice(0, -hiddenDeclaration.length)
487
+ if (authoredCssText.endsWith(';')) authoredCssText = authoredCssText.slice(0, -1)
488
+ const style = document.createElement('div').style
489
+ style.cssText = authoredCssText
490
+ return [style.display, style.getPropertyPriority('display')]
491
+ }
492
+
493
+ export function errorBoundary(
494
+ render: () => CompiledRenderValue,
495
+ fallback: (error: unknown, reset: () => void) => CompiledRenderValue,
496
+ onError?: CompiledErrorHandler,
497
+ ): StructuralBinding {
498
+ noteHydrationStructuralParent()
499
+ const context = activeContextFrame ?? activeOwner?.[2] ?? null
500
+ const rootIdentity = activeOwner?.[3] ?? activeRootIdentity
501
+ if (rootIdentity === null) {
502
+ throw new Error(DEV ? 'errorBoundary must run during compiled root construction' : 'V028')
503
+ }
504
+ const parentBoundary = activeOwner?.[4] ?? null
505
+ let mounted = false
506
+ return [
507
+ STRUCTURAL,
508
+ (parent, before) => {
509
+ if (mounted) throw new Error(DEV ? 'compiled error boundary is already mounted' : 'V008')
510
+ mounted = true
511
+ const hydratedRange = claimHydrationSlotRange(parent)
512
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:error' : '')
513
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:error' : '')
514
+ if (hydratedRange === undefined) {
515
+ parent.insertBefore(start, before)
516
+ parent.insertBefore(end, before)
517
+ }
518
+ let currentOwner: Owner | null = null
519
+ let currentNodes: readonly Node[] = []
520
+ let failed = false
521
+
522
+ const publish = (
523
+ read: () => CompiledRenderValue,
524
+ boundary: ErrorBoundaryHandler | null,
525
+ ): void => {
526
+ const currentParent = rangeParent(start, end, 'error boundary')
527
+ const nextOwner = createOwner(context, rootIdentity, boundary)
528
+ const [fragment, nodes] =
529
+ hydratedRange === undefined
530
+ ? stageRender(read, nextOwner)
531
+ : withHydrationInsertion(currentParent, end, () => stageRender(read, nextOwner))
532
+ try {
533
+ currentParent.insertBefore(fragment, end)
534
+ commitPublishedNodes(nodes)
535
+ } catch (error) {
536
+ disposePublished(nextOwner, nodes)
537
+ throw error
538
+ }
539
+ const previousOwner = currentOwner
540
+ const previousNodes = currentNodes
541
+ currentOwner = nextOwner
542
+ currentNodes = nodes
543
+ disposePublished(previousOwner, previousNodes)
544
+ }
545
+
546
+ const reset = (): void => {
547
+ if (!failed) return
548
+ try {
549
+ failed = false
550
+ publish(render, boundary)
551
+ } catch (error) {
552
+ recover(error)
553
+ }
554
+ }
555
+ const recover = (error: unknown): void => {
556
+ failed = true
557
+ publish(() => fallback(error, reset), parentBoundary)
558
+ onError?.(error)
559
+ rootIdentity.onCaughtError?.(error)
560
+ }
561
+ const boundary: ErrorBoundaryHandler = { handle: recover, parent: parentBoundary }
562
+
563
+ try {
564
+ publish(render, boundary)
565
+ } catch (error) {
566
+ recover(error)
567
+ }
568
+ onCleanup(() => {
569
+ try {
570
+ disposePublished(currentOwner, currentNodes)
571
+ } finally {
572
+ start.remove()
573
+ end.remove()
574
+ }
575
+ })
576
+ },
577
+ 'slot',
578
+ ]
579
+ }
580
+
581
+ export function suspense(
582
+ render: () => CompiledRenderValue,
583
+ fallback: () => CompiledRenderValue,
584
+ ): StructuralBinding {
585
+ noteHydrationStructuralParent()
586
+ const context = activeContextFrame ?? activeOwner?.[2] ?? null
587
+ const rootIdentity = activeOwner?.[3] ?? activeRootIdentity
588
+ if (rootIdentity === null) {
589
+ throw new Error(DEV ? 'Suspense must run during compiled root construction' : 'V032')
590
+ }
591
+ const parentErrorBoundary = activeOwner?.[4] ?? null
592
+ let mounted = false
593
+ return [
594
+ STRUCTURAL,
595
+ (parent, before) => {
596
+ if (mounted) throw new Error(DEV ? 'compiled Suspense boundary is already mounted' : 'V008')
597
+ mounted = true
598
+ const lifetimeOwner = activeOwner
599
+ if (lifetimeOwner === null) {
600
+ throw new Error(DEV ? 'Suspense must mount inside an owned compiled root' : 'V032')
601
+ }
602
+ const hydratedRange = claimHydrationSlotRange(parent)
603
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:suspense' : '')
604
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:suspense' : '')
605
+ if (hydratedRange === undefined) {
606
+ parent.insertBefore(start, before)
607
+ parent.insertBefore(end, before)
608
+ }
609
+ const pendingMarker =
610
+ hydratedRange === undefined ? undefined : claimHydrationSuspenseFallback(parent)
611
+ let currentOwner: Owner | null = null
612
+ let currentNodes: readonly Node[] = []
613
+ let currentKind: 'content' | 'fallback' | null = null
614
+ let removeResourceListener = noop
615
+ let generation = 0
616
+ let boundary: ErrorBoundaryHandler
617
+
618
+ const publish = (
619
+ read: () => CompiledRenderValue,
620
+ kind: 'content' | 'fallback',
621
+ detachedHydrationProbe = false,
622
+ ): void => {
623
+ const currentParent = rangeParent(start, end, 'Suspense boundary')
624
+ const nextOwner = createOwner(
625
+ context,
626
+ rootIdentity,
627
+ kind === 'content' ? boundary : parentErrorBoundary,
628
+ )
629
+ const serverNodes = detachedHydrationProbe ? nodesBetween(start, end) : []
630
+ const [fragment, nodes] = detachedHydrationProbe
631
+ ? withoutHydration(() => stageRender(read, nextOwner))
632
+ : hydratedRange === undefined || currentKind !== null
633
+ ? stageRender(read, nextOwner)
634
+ : withHydrationInsertion(currentParent, end, () => stageRender(read, nextOwner))
635
+ try {
636
+ currentParent.insertBefore(fragment, end)
637
+ commitPublishedNodes(nodes)
638
+ } catch (error) {
639
+ disposePublished(nextOwner, nodes)
640
+ throw error
641
+ }
642
+ const previousOwner = currentOwner
643
+ const previousNodes = currentNodes
644
+ currentOwner = nextOwner
645
+ currentNodes = nodes
646
+ currentKind = kind
647
+ disposePublished(previousOwner, previousNodes)
648
+ for (const node of serverNodes) node.parentNode?.removeChild(node)
649
+ }
650
+
651
+ const beginAttempt = (): number => {
652
+ const attemptGeneration = ++generation
653
+ removeResourceListener()
654
+ removeResourceListener = noop
655
+ return attemptGeneration
656
+ }
657
+
658
+ const suspend = (failure: unknown, attemptGeneration: number): void => {
659
+ if (!isSuspension(failure)) throw failure
660
+ const retry = (): void => {
661
+ if (lifetimeOwner[0] || attemptGeneration !== generation) return
662
+ runOwnerTask(lifetimeOwner, () => attempt(false))
663
+ }
664
+ removeResourceListener = subscribeResource(failure.resource, retry)
665
+ if (currentKind !== 'fallback') {
666
+ try {
667
+ publish(fallback, 'fallback')
668
+ if (pendingMarker !== undefined) {
669
+ currentNodes = [pendingMarker, ...currentNodes]
670
+ }
671
+ } catch (fallbackError) {
672
+ removeResourceListener()
673
+ removeResourceListener = noop
674
+ throw fallbackError
675
+ }
676
+ }
677
+ }
678
+
679
+ const attempt = (initial: boolean): void => {
680
+ const attemptGeneration = beginAttempt()
681
+ try {
682
+ publish(render, 'content', initial && pendingMarker !== undefined)
683
+ } catch (error) {
684
+ if (!isSuspension(error)) {
685
+ if (initial || !routeOwnerError(lifetimeOwner, error)) throw error
686
+ return
687
+ }
688
+ suspend(error, attemptGeneration)
689
+ }
690
+ }
691
+
692
+ boundary = {
693
+ handle: (error) => suspend(error, beginAttempt()),
694
+ parent: parentErrorBoundary,
695
+ }
696
+ attempt(true)
697
+ onCleanup(() => {
698
+ generation += 1
699
+ removeResourceListener()
700
+ try {
701
+ disposePublished(currentOwner, currentNodes)
702
+ } finally {
703
+ start.remove()
704
+ end.remove()
705
+ }
706
+ })
707
+ },
708
+ 'slot',
709
+ ]
710
+ }
711
+
712
+ type PortalRenderableProps = {
713
+ readonly children: CompiledRenderValue | readonly CompiledRenderValue[]
714
+ }
715
+
716
+ const keyedPortalIdentities = new WeakMap<ParentNode, Map<string | number | bigint, object>>()
717
+
718
+ export function createPortal(
719
+ children: CompiledRenderValue | readonly CompiledRenderValue[],
720
+ container: ParentNode,
721
+ key?: string | number | bigint | null,
722
+ ): CompiledRenderValue {
723
+ if (!(container instanceof Node) || typeof container.insertBefore !== 'function') {
724
+ throw new TypeError(DEV ? 'createPortal requires a DOM container' : 'V026')
725
+ }
726
+ const input: PortalRenderableProps = { children }
727
+ const capability = { props: input } as RenderableProtocol & {
728
+ readonly props: PortalRenderableProps
729
+ }
730
+ Object.defineProperty(capability, RENDERABLE, {
731
+ configurable: false,
732
+ enumerable: false,
733
+ value: {
734
+ identity: portalIdentity(container, key),
735
+ input,
736
+ reconcile: true,
737
+ construct: (currentInput: PortalRenderableProps | CompiledBinding<PortalRenderableProps>) =>
738
+ createPortalStructural(portalChildren(currentInput), container),
739
+ },
740
+ })
741
+ return capability as unknown as CompiledRenderValue
742
+ }
743
+
744
+ function portalIdentity(
745
+ container: ParentNode,
746
+ key: string | number | bigint | null | undefined,
747
+ ): object {
748
+ if (key == null) return container
749
+ let identities = keyedPortalIdentities.get(container)
750
+ if (identities === undefined) {
751
+ identities = new Map()
752
+ keyedPortalIdentities.set(container, identities)
753
+ }
754
+ let identity = identities.get(key)
755
+ if (identity === undefined) {
756
+ identity = {}
757
+ identities.set(key, identity)
758
+ }
759
+ return identity
760
+ }
761
+
762
+ function portalChildren(
763
+ input: PortalRenderableProps | CompiledBinding<PortalRenderableProps>,
764
+ ): CompiledRenderValue | readonly CompiledRenderValue[] {
765
+ if (!isCompiledBinding(input)) return input.children
766
+ return binding(
767
+ input[2],
768
+ input[3],
769
+ () => input[1]().children,
770
+ input[4],
771
+ input[5],
772
+ ) as CompiledBinding<CompiledRenderValue>
773
+ }
774
+
775
+ function createPortalStructural(
776
+ children: CompiledRenderValue | readonly CompiledRenderValue[],
777
+ container: ParentNode,
778
+ ): StructuralBinding {
779
+ const context = activeContextFrame ?? activeOwner?.[2] ?? null
780
+ const rootIdentity = activeOwner?.[3] ?? activeRootIdentity
781
+ if (rootIdentity === null) {
782
+ throw new Error(DEV ? 'createPortal must run during compiled root construction' : 'V027')
783
+ }
784
+ let mounted = false
785
+ return [
786
+ STRUCTURAL,
787
+ (logicalParent, before) => {
788
+ if (mounted) throw new Error(DEV ? 'compiled portal is already mounted' : 'V008')
789
+ mounted = true
790
+ const logicalMarker = document.createComment(DEV ? 'vidact:portal' : '')
791
+ logicalParent.insertBefore(logicalMarker, before)
792
+ const start = document.createComment(DEV ? 'vidact:portal:start' : '')
793
+ const end = document.createComment(DEV ? 'vidact:portal:end' : '')
794
+ const fragment = document.createDocumentFragment()
795
+ fragment.append(start, end)
796
+ const portalOwner = withRootIdentity(rootIdentity, () =>
797
+ withContextFrame(context, createOwner),
798
+ )
799
+ try {
800
+ withOwner(portalOwner, () => insertValue(fragment, children, end))
801
+ } catch (error) {
802
+ disposeOwner(portalOwner)
803
+ logicalMarker.remove()
804
+ throw error
805
+ }
806
+ const rollback = (): void => {
807
+ removeBetween(start, end)
808
+ start.remove()
809
+ end.remove()
810
+ }
811
+ const publication: PortalPublication = [
812
+ () => {
813
+ container.insertBefore(fragment, null)
814
+ try {
815
+ commitRangeRefs(start, end)
816
+ } catch (error) {
817
+ rollback()
818
+ throw error
819
+ }
820
+ },
821
+ rollback,
822
+ ]
823
+ if (rootIdentity.mounted) {
824
+ stagePublication([publication[0], publication[1], () => disposeOwner(portalOwner)])
825
+ } else {
826
+ let pending = pendingRootPortals.get(rootIdentity)
827
+ if (pending === undefined) {
828
+ pending = new Set()
829
+ pendingRootPortals.set(rootIdentity, pending)
830
+ }
831
+ pending.add(publication)
832
+ }
833
+ onCleanup(() => {
834
+ pendingRootPortals.get(rootIdentity)?.delete(publication)
835
+ try {
836
+ disposeOwner(portalOwner)
837
+ } finally {
838
+ publication[1]()
839
+ logicalMarker.remove()
840
+ }
841
+ })
842
+ },
843
+ ]
844
+ }
845
+
846
+ type RefValue<T = Element> =
847
+ | ((value: T | null) => void | (() => void))
848
+ | { current: unknown }
849
+ | null
850
+ | undefined
851
+
852
+ type PendingRef = [owner: Owner | null, value: RefValue, attached?: (cleanup: () => void) => void]
853
+
854
+ type NodePosition = readonly [node: Node, parent: Node | null, nextSibling: Node | null]
855
+
856
+ type PublicationOperation = readonly [
857
+ commit: () => void,
858
+ rollback: () => void,
859
+ abort?: (() => void) | undefined,
860
+ finalize?: (() => void) | undefined,
861
+ priority?: number | undefined,
862
+ errorOwner?: Owner | null | undefined,
863
+ ]
864
+
865
+ export type CompiledPropTransition = PublicationOperation
866
+
867
+ type ComponentRange = readonly [start: Comment, end: Comment, scope: CompiledScope]
868
+
869
+ let activeOwner: Owner | null = null
870
+ let activeContextFrame: ContextFrame | null = null
871
+ let activeRootIdentity: RootIdentity | null = null
872
+ let nextClientRoot = 0
873
+ let activeScopeCollector: Set<CompiledScope> | null = null
874
+ let activeConstructionOwner: Owner | null = null
875
+ let activeProfileName: string | null = null
876
+ let activeErrorOwner: Owner | null = null
877
+ let failedOwner: Owner | null = null
878
+ let transactionDepth = 0
879
+ let disposalCascadeDepth = 0
880
+ let drainingFlushes = false
881
+ let activePublication: PublicationOperation[] | null = null
882
+ const scheduledFlushes = new Set<() => void>()
883
+ const disposalCascadeOwners = new Set<Owner>()
884
+ const scopeOwners = new WeakMap<CompiledScope, Owner>()
885
+ const scopeNamespaces = new WeakMap<CompiledScope, IntrinsicNamespace>()
886
+ const componentRanges = new WeakMap<CompiledComponentResult, ComponentRange>()
887
+ const pendingRefs = new WeakMap<Element, PendingRef>()
888
+ const componentCommitOwners = new WeakMap<Comment, Owner>()
889
+ const pendingInsertionCommits = new WeakMap<Owner, Set<() => void>>()
890
+ const pendingOwnerCommits = new WeakMap<Owner, Set<() => void>>()
891
+ const pendingRootPortals = new WeakMap<RootIdentity, Set<PortalPublication>>()
892
+
893
+ export function createCompiledScope(): CompiledScope {
894
+ return createScope(wideSourceOperations)
895
+ }
896
+
897
+ export function createNarrowCompiledScope(): CompiledScope {
898
+ return createScope(narrowSourceOperations)
899
+ }
900
+
901
+ export function runCompiledTransaction<T>(operation: () => T): T {
902
+ transactionDepth += 1
903
+ try {
904
+ return operation()
905
+ } finally {
906
+ transactionDepth -= 1
907
+ if (transactionDepth === 0) drainFlushes()
908
+ }
909
+ }
910
+
911
+ function createScope(operations: SourceOperations): CompiledScope {
912
+ const namespace = currentIntrinsicNamespace()
913
+ const owner = createOwner()
914
+ const updaters: Array<CompiledUpdater | undefined> = []
915
+ const freeUpdaterIndexes: number[] = []
916
+ const addedDuringFlush = new Set<CompiledUpdater>()
917
+ let batchDepth = 0
918
+ let flushing = false
919
+ let pending: SourceMask = 0
920
+ let cachedUpdaterOrder: number[] | undefined
921
+
922
+ const flush = (): void => {
923
+ if (owner[0] || flushing) return
924
+ const operation = (): void => {
925
+ flushing = true
926
+ try {
927
+ let pass = 0
928
+ const updaterOrder = (cachedUpdaterOrder ??= topologicalUpdaterOrder(updaters, operations))
929
+ while (!operations[0](pending)) {
930
+ pass += 1
931
+ if (pass > MAX_FLUSH_PASSES) {
932
+ pending = 0
933
+ throw new Error(DEV ? 'Vidact compiled scope did not stabilize' : 'V001')
934
+ }
935
+
936
+ let active = pending
937
+ pending = 0
938
+ for (const index of updaterOrder) {
939
+ const updater = updaters[index]
940
+ if (
941
+ updater === undefined ||
942
+ addedDuringFlush.has(updater) ||
943
+ !updater[3] ||
944
+ !operations[1](active, updater[0])
945
+ ) {
946
+ continue
947
+ }
948
+ if (profilingEnabled) {
949
+ measureProfileWork(owner, 'updater', () => updater[2](active))
950
+ } else {
951
+ updater[2](active)
952
+ }
953
+ if (updater[1] !== undefined) active = operations[2](active, updater[1])
954
+ }
955
+ addedDuringFlush.clear()
956
+ }
957
+ } finally {
958
+ addedDuringFlush.clear()
959
+ flushing = false
960
+ }
961
+ if (retainedUiEnabled) notifyRetainedFlush(owner[5] ?? null)
962
+ }
963
+ if (profilingEnabled) measureProfileWork(owner, 'scheduler', operation, true)
964
+ else operation()
965
+ }
966
+
967
+ const scope: CompiledScope = [
968
+ (reads, run, writes) => {
969
+ if (owner[0]) {
970
+ throw new Error(DEV ? 'cannot add an updater to a disposed scope' : 'V002')
971
+ }
972
+ const errorOwner = activeOwner ?? owner
973
+ const context = activeContextFrame ?? errorOwner[2]
974
+ const entry: CompiledUpdater = [
975
+ reads,
976
+ writes,
977
+ (active) => {
978
+ try {
979
+ withOwner(errorOwner, () =>
980
+ withContextFrame(context, () => withScopeNamespace(scope, () => run(active))),
981
+ )
982
+ } catch (error) {
983
+ if (!routeOwnerError(errorOwner, error)) {
984
+ failedOwner = errorOwner
985
+ throw error
986
+ }
987
+ }
988
+ },
989
+ true,
990
+ ]
991
+ const reusableIndex = freeUpdaterIndexes.pop()
992
+ const index = reusableIndex ?? updaters.length
993
+ updaters[index] = entry
994
+ cachedUpdaterOrder = undefined
995
+ if (flushing) addedDuringFlush.add(entry)
996
+ const remove = (): void => {
997
+ if (updaters[index] !== entry) return
998
+ entry[3] = false
999
+ updaters[index] = undefined
1000
+ cachedUpdaterOrder = undefined
1001
+ freeUpdaterIndexes.push(index)
1002
+ }
1003
+ if (activeOwner !== null && activeOwner !== owner) activeOwner[1].add(remove)
1004
+ return remove
1005
+ },
1006
+ (sources) => {
1007
+ if (owner[0] || operations[0](sources)) return
1008
+ pending = operations[2](pending, sources)
1009
+ if (batchDepth === 0) {
1010
+ if (retainedUiEnabled) scheduleOwnerFlush(owner, flush)
1011
+ else scheduleFlush(flush)
1012
+ }
1013
+ },
1014
+ <T>(operation: () => T): T => {
1015
+ batchDepth += 1
1016
+ transactionDepth += 1
1017
+ try {
1018
+ return operation()
1019
+ } finally {
1020
+ batchDepth -= 1
1021
+ if (batchDepth === 0 && !operations[0](pending)) {
1022
+ if (retainedUiEnabled) scheduleOwnerFlush(owner, flush)
1023
+ else scheduleFlush(flush)
1024
+ }
1025
+ transactionDepth -= 1
1026
+ if (transactionDepth === 0) drainFlushes()
1027
+ }
1028
+ },
1029
+ () => {
1030
+ if (owner[0]) return
1031
+ try {
1032
+ disposeOwner(owner)
1033
+ } finally {
1034
+ pending = 0
1035
+ for (const updater of updaters) {
1036
+ if (updater !== undefined) updater[3] = false
1037
+ }
1038
+ updaters.length = 0
1039
+ freeUpdaterIndexes.length = 0
1040
+ addedDuringFlush.clear()
1041
+ }
1042
+ },
1043
+ ]
1044
+ scopeOwners.set(scope, owner)
1045
+ scopeNamespaces.set(scope, namespace)
1046
+ activeScopeCollector?.add(scope)
1047
+ if (activeScopeCollector !== null) activeConstructionOwner = owner
1048
+ return scope
1049
+ }
1050
+
1051
+ function topologicalUpdaterOrder(
1052
+ updaters: ReadonlyArray<CompiledUpdater | undefined>,
1053
+ operations: SourceOperations,
1054
+ ): number[] {
1055
+ const activeIndexes = updaters.flatMap((updater, index) =>
1056
+ updater === undefined || !updater[3] ? [] : [index],
1057
+ )
1058
+ const edges = new Map<number, number[]>()
1059
+ for (const writerIndex of activeIndexes) {
1060
+ const writes = updaters[writerIndex]![1]
1061
+ if (writes === undefined) continue
1062
+ const readers: number[] = []
1063
+ for (const readerIndex of activeIndexes) {
1064
+ if (writerIndex !== readerIndex && operations[1](writes, updaters[readerIndex]![0])) {
1065
+ readers.push(readerIndex)
1066
+ }
1067
+ }
1068
+ if (readers.length > 0) edges.set(writerIndex, readers)
1069
+ }
1070
+
1071
+ const visitIndexes = new Int32Array(updaters.length).fill(-1)
1072
+ const lowLinks = new Int32Array(updaters.length)
1073
+ const stack: number[] = []
1074
+ const onStack = new Uint8Array(updaters.length)
1075
+ const components: number[][] = []
1076
+ let nextVisitIndex = 0
1077
+
1078
+ const connect = (updaterIndex: number): void => {
1079
+ const visitIndex = nextVisitIndex++
1080
+ visitIndexes[updaterIndex] = visitIndex
1081
+ lowLinks[updaterIndex] = visitIndex
1082
+ stack.push(updaterIndex)
1083
+ onStack[updaterIndex] = 1
1084
+
1085
+ for (const readerIndex of edges.get(updaterIndex) ?? []) {
1086
+ if (visitIndexes[readerIndex] === -1) {
1087
+ connect(readerIndex)
1088
+ lowLinks[updaterIndex] = Math.min(lowLinks[updaterIndex]!, lowLinks[readerIndex]!)
1089
+ } else if (onStack[readerIndex] === 1) {
1090
+ lowLinks[updaterIndex] = Math.min(lowLinks[updaterIndex]!, visitIndexes[readerIndex]!)
1091
+ }
1092
+ }
1093
+
1094
+ if (lowLinks[updaterIndex] !== visitIndexes[updaterIndex]) return
1095
+ const component: number[] = []
1096
+ let member: number
1097
+ do {
1098
+ member = stack.pop()!
1099
+ onStack[member] = 0
1100
+ component.push(member)
1101
+ } while (member !== updaterIndex)
1102
+ component.sort((left, right) => left - right)
1103
+ components.push(component)
1104
+ }
1105
+
1106
+ for (const updaterIndex of activeIndexes) {
1107
+ if (visitIndexes[updaterIndex] === -1) connect(updaterIndex)
1108
+ }
1109
+
1110
+ const componentByUpdater = new Int32Array(updaters.length).fill(-1)
1111
+ const componentEdges = components.map(() => new Set<number>())
1112
+ const componentIndegrees = new Uint32Array(components.length)
1113
+ const componentFirstIndexes = components.map((component) => component[0]!)
1114
+ for (let componentIndex = 0; componentIndex < components.length; componentIndex += 1) {
1115
+ for (const updaterIndex of components[componentIndex]!) {
1116
+ componentByUpdater[updaterIndex] = componentIndex
1117
+ }
1118
+ }
1119
+ for (const [writerIndex, readers] of edges) {
1120
+ const writerComponent = componentByUpdater[writerIndex]!
1121
+ for (const readerIndex of readers) {
1122
+ const readerComponent = componentByUpdater[readerIndex]!
1123
+ if (
1124
+ writerComponent === readerComponent ||
1125
+ componentEdges[writerComponent]!.has(readerComponent)
1126
+ ) {
1127
+ continue
1128
+ }
1129
+ componentEdges[writerComponent]!.add(readerComponent)
1130
+ componentIndegrees[readerComponent] = componentIndegrees[readerComponent]! + 1
1131
+ }
1132
+ }
1133
+
1134
+ const ready = components
1135
+ .map((_, index) => index)
1136
+ .filter((index) => componentIndegrees[index] === 0)
1137
+ .sort((left, right) => componentFirstIndexes[left]! - componentFirstIndexes[right]!)
1138
+ const ordered: number[] = []
1139
+ while (ready.length > 0) {
1140
+ const componentIndex = ready.shift()!
1141
+ ordered.push(...components[componentIndex]!)
1142
+ for (const readerComponent of componentEdges[componentIndex]!) {
1143
+ componentIndegrees[readerComponent] = componentIndegrees[readerComponent]! - 1
1144
+ if (componentIndegrees[readerComponent] === 0) {
1145
+ ready.push(readerComponent)
1146
+ ready.sort((left, right) => componentFirstIndexes[left]! - componentFirstIndexes[right]!)
1147
+ }
1148
+ }
1149
+ }
1150
+ return ordered
1151
+ }
1152
+
1153
+ export function createCompiledState<T>(
1154
+ scope: CompiledScope,
1155
+ sourceMask: SourceMask,
1156
+ initialValue: T | (() => T),
1157
+ ): StateSlot<T> {
1158
+ const assertWritable = stateWriteGuard(scope)
1159
+ const value = typeof initialValue === 'function' ? (initialValue as () => T)() : initialValue
1160
+ return createStateSlot(scope[1], sourceMask, value, assertWritable)
1161
+ }
1162
+
1163
+ export function createCompiledReducer<State, Action, Initial>(
1164
+ scope: CompiledScope,
1165
+ sourceMask: SourceMask,
1166
+ reducer: (state: State, action: Action) => State,
1167
+ initialArg: Initial,
1168
+ initialize?: (initialArg: Initial) => State,
1169
+ ): { readonly get: () => State; readonly set: (action: Action) => void } {
1170
+ const state = createStateSlot<State>(
1171
+ scope[1],
1172
+ sourceMask,
1173
+ initialize === undefined ? (initialArg as unknown as State) : initialize(initialArg),
1174
+ stateWriteGuard(scope),
1175
+ )
1176
+ return {
1177
+ get: state.get,
1178
+ set: (action) => state.set((previous) => reducer(previous, action)),
1179
+ }
1180
+ }
1181
+
1182
+ export function createCompiledMemo<T>(
1183
+ scope: CompiledScope,
1184
+ reads: SourceMask,
1185
+ writes: SourceMask,
1186
+ evaluate: () => T,
1187
+ readDependencies: () => readonly unknown[],
1188
+ ): StateSlot<T> {
1189
+ let currentDependencies = readDependencies()
1190
+ const slot = createStateSlot(scope[1], writes, evaluate(), stateWriteGuard(scope))
1191
+ const removeUpdater = scope[0](reads, () => {
1192
+ const nextDependencies = readDependencies()
1193
+ if (equalDependencies(nextDependencies, currentDependencies)) return
1194
+ slot.replace(evaluate())
1195
+ currentDependencies = nextDependencies
1196
+ })
1197
+ const owner = scopeOwners.get(scope)
1198
+ if (owner === undefined) {
1199
+ removeUpdater()
1200
+ throw new Error(DEV ? 'createCompiledMemo received an unknown scope' : 'V003')
1201
+ }
1202
+ owner[1].add(removeUpdater)
1203
+ return slot
1204
+ }
1205
+
1206
+ export function createContext<T>(defaultValue: T): CompiledContext<T> {
1207
+ let context: CompiledContext<T>
1208
+ const provider = ((props: {
1209
+ readonly children?: CompiledRenderValue | readonly CompiledRenderValue[]
1210
+ readonly value: T | CompiledBinding<T>
1211
+ }): StructuralBinding =>
1212
+ provideContext(context, props.value, props.children)) as CompiledContext<T>
1213
+ context = provider
1214
+ context.Provider = provider
1215
+ Object.defineProperty(context, CONTEXT, { value: defaultValue })
1216
+ return context
1217
+ }
1218
+
1219
+ /** @internal */
1220
+ export function runWithCompiledContext<Value, Result>(
1221
+ context: CompiledContext<Value>,
1222
+ value: Value | CompiledBinding<Value>,
1223
+ operation: () => Result,
1224
+ ): Result {
1225
+ if (!(CONTEXT in context)) {
1226
+ throw new Error(DEV ? 'runWithCompiledContext received an unknown context' : 'V003')
1227
+ }
1228
+ const parent = activeContextFrame ?? activeOwner?.[2] ?? null
1229
+ return withContextFrame(
1230
+ {
1231
+ context: context as CompiledContext<unknown>,
1232
+ input: value,
1233
+ parent,
1234
+ owner: activeOwner,
1235
+ },
1236
+ operation,
1237
+ )
1238
+ }
1239
+
1240
+ export function createCompiledContext<T>(
1241
+ scope: CompiledScope,
1242
+ sourceMask: SourceMask,
1243
+ context: CompiledContext<T>,
1244
+ ): StateSlot<T> {
1245
+ const input = contextInput(scope, context)
1246
+ return createCompiledProp(scope, sourceMask, input)
1247
+ }
1248
+
1249
+ export function createCompiledExternalStore<T>(
1250
+ scope: CompiledScope,
1251
+ sourceMask: SourceMask,
1252
+ storeSubscribe:
1253
+ | ((onStoreChange: () => void) => () => void)
1254
+ | CompiledBinding<readonly [(onStoreChange: () => void) => () => void, () => T, (() => T)?]>,
1255
+ getSnapshot?: () => T,
1256
+ _getServerSnapshot?: () => T,
1257
+ ): StateSlot<T> {
1258
+ const owner = scopeOwners.get(scope)
1259
+ if (owner === undefined) {
1260
+ throw new Error(DEV ? 'createCompiledExternalStore received an unknown scope' : 'V003')
1261
+ }
1262
+ const reactiveStore = isCompiledBinding(storeSubscribe) ? storeSubscribe : undefined
1263
+ let currentSubscribe: (onStoreChange: () => void) => () => void
1264
+ let currentGetSnapshot!: () => T
1265
+ const readStore = (): void => {
1266
+ const store = reactiveStore?.[1]()
1267
+ currentSubscribe = store?.[0] ?? (storeSubscribe as (onStoreChange: () => void) => () => void)
1268
+ currentGetSnapshot = store?.[1] ?? (getSnapshot as () => T)
1269
+ if (typeof currentSubscribe !== 'function' || typeof currentGetSnapshot !== 'function') {
1270
+ throw new TypeError(
1271
+ DEV ? 'external store requires subscribe and getSnapshot functions' : 'V020',
1272
+ )
1273
+ }
1274
+ }
1275
+ readStore()
1276
+ const slot = createStateSlot(scope[1], sourceMask, currentGetSnapshot(), stateWriteGuard(scope))
1277
+ let active = false
1278
+ let unsubscribe = noop
1279
+ const checkSnapshot = (): void => {
1280
+ if (!active) return
1281
+ const nextSnapshot = currentGetSnapshot()
1282
+ scope[2](() => slot.replace(nextSnapshot))
1283
+ }
1284
+ const activateStore = (): void => {
1285
+ readStore()
1286
+ active = true
1287
+ try {
1288
+ const remove = currentSubscribe(() => runOwnerTask(owner, checkSnapshot))
1289
+ if (typeof remove !== 'function') {
1290
+ throw new TypeError(
1291
+ DEV ? 'external store subscribe must return an unsubscribe function' : 'V020',
1292
+ )
1293
+ }
1294
+ unsubscribe = remove
1295
+ checkSnapshot()
1296
+ } catch (error) {
1297
+ active = false
1298
+ unsubscribe()
1299
+ unsubscribe = noop
1300
+ throw error
1301
+ }
1302
+ }
1303
+ const deactivateStore = (): void => {
1304
+ active = false
1305
+ unsubscribe()
1306
+ unsubscribe = noop
1307
+ }
1308
+ const reconnect = (): void => {
1309
+ if (!active) {
1310
+ readStore()
1311
+ return
1312
+ }
1313
+ deactivateStore()
1314
+ activateStore()
1315
+ }
1316
+ const removeStoreUpdater =
1317
+ reactiveStore === undefined ? noop : subscribeBinding(reactiveStore, reconnect)
1318
+
1319
+ if (!retainedUiEnabled) {
1320
+ try {
1321
+ activateStore()
1322
+ } catch (error) {
1323
+ removeStoreUpdater()
1324
+ throw error
1325
+ }
1326
+ owner[1].add(() => {
1327
+ removeStoreUpdater()
1328
+ deactivateStore()
1329
+ })
1330
+ return slot
1331
+ }
1332
+ const [activateResource, disposeResource] = createRetainedResource(
1333
+ owner,
1334
+ activateStore,
1335
+ deactivateStore,
1336
+ 0,
1337
+ )
1338
+ try {
1339
+ activateResource()
1340
+ } catch (error) {
1341
+ disposeResource()
1342
+ throw error
1343
+ }
1344
+ owner[1].add(() => {
1345
+ removeStoreUpdater()
1346
+ disposeResource()
1347
+ })
1348
+ return slot
1349
+ }
1350
+
1351
+ export function createCompiledEffectEvent<Arguments extends unknown[], Result>(
1352
+ scope: CompiledScope,
1353
+ callback: (...arguments_: Arguments) => Result,
1354
+ ): (...arguments_: Arguments) => Result {
1355
+ const owner = scopeOwners.get(scope)
1356
+ if (owner === undefined) {
1357
+ throw new Error(DEV ? 'createCompiledEffectEvent received an unknown scope' : 'V003')
1358
+ }
1359
+ return (...arguments_) => {
1360
+ if (owner[0]) throw new Error(DEV ? 'cannot call an effect event after disposal' : 'V022')
1361
+ return callback(...arguments_)
1362
+ }
1363
+ }
1364
+
1365
+ export function createCompiledId(scope: CompiledScope): string {
1366
+ const owner = scopeOwners.get(scope)
1367
+ if (owner === undefined) {
1368
+ throw new Error(DEV ? 'createCompiledId received an unknown scope' : 'V003')
1369
+ }
1370
+ const id = `:${owner[3].prefix}r${owner[3].nextId}:`
1371
+ owner[3].nextId += 1
1372
+ return id
1373
+ }
1374
+
1375
+ export function useContext<T>(context: CompiledContext<T>): T {
1376
+ if (activeConstructionOwner === null) {
1377
+ throw new Error(DEV ? 'useContext must run during compiled component construction' : 'V019')
1378
+ }
1379
+ const input = contextInputFromFrame(activeConstructionOwner[2], context)
1380
+ return isCompiledBinding(input) ? (input[1]() as T) : (input as T)
1381
+ }
1382
+
1383
+ export function use<T>(context: CompiledContext<T>): T {
1384
+ return useContext(context)
1385
+ }
1386
+
1387
+ export function createCompiledAsync<Value>(
1388
+ scope: CompiledScope,
1389
+ reads: SourceMask,
1390
+ writes: SourceMask,
1391
+ evaluate: () => CompiledContext<Value> | AsyncResource<Value> | PromiseLike<Value>,
1392
+ ): StateSlot<Value> {
1393
+ const input = evaluate()
1394
+ if (isCompiledContext<Value>(input)) return createCompiledContext(scope, writes, input)
1395
+ const slot = createCompiledState(scope, writes, useAsync(input))
1396
+ scope[0](reads, () => slot.replace(useAsync(evaluate())))
1397
+ return slot
1398
+ }
1399
+
1400
+ export function Suspense(props: {
1401
+ readonly children?: (() => CompiledRenderValue) | readonly [() => CompiledRenderValue] | undefined
1402
+ readonly fallback: () => CompiledRenderValue
1403
+ }): StructuralBinding {
1404
+ const render = Array.isArray(props.children) ? props.children[0] : props.children
1405
+ if (typeof render !== 'function' || typeof props.fallback !== 'function') {
1406
+ throw new TypeError(
1407
+ DEV
1408
+ ? `Suspense children and fallback must be compiler-generated render functions; received ${typeof render} and ${typeof props.fallback}`
1409
+ : 'V031',
1410
+ )
1411
+ }
1412
+ return suspense(render, props.fallback)
1413
+ }
1414
+
1415
+ export function useSyncExternalStore<T>(
1416
+ _subscribe: (onStoreChange: () => void) => () => void,
1417
+ _getSnapshot: () => T,
1418
+ _getServerSnapshot?: () => T,
1419
+ ): T {
1420
+ throw new Error(DEV ? 'useSyncExternalStore requires compiler lowering' : 'V021')
1421
+ }
1422
+
1423
+ export function useEffectEvent<Arguments extends unknown[], Result>(
1424
+ callback: (...arguments_: Arguments) => Result,
1425
+ ): (...arguments_: Arguments) => Result {
1426
+ return callback
1427
+ }
1428
+
1429
+ export function useId(): string {
1430
+ if (activeConstructionOwner === null) {
1431
+ throw new Error(DEV ? 'useId must run during compiled component construction' : 'V023')
1432
+ }
1433
+ const id = `:${activeConstructionOwner[3].prefix}r${activeConstructionOwner[3].nextId}:`
1434
+ activeConstructionOwner[3].nextId += 1
1435
+ return id
1436
+ }
1437
+
1438
+ export function useMemo<T>(factory: () => T, _dependencies: readonly unknown[]): T {
1439
+ return factory()
1440
+ }
1441
+
1442
+ export function useCallback<T extends Function>(callback: T, _dependencies: readonly unknown[]): T {
1443
+ return callback
1444
+ }
1445
+
1446
+ export function createCompiledProp<T>(
1447
+ scope: CompiledScope,
1448
+ sourceMask: SourceMask,
1449
+ input: T | CompiledBinding<T>,
1450
+ fallback?: () => T,
1451
+ ): StateSlot<T> {
1452
+ const assertWritable = stateWriteGuard(scope)
1453
+ const upstream = isCompiledBinding(input) ? input : undefined
1454
+ const read = (): T => {
1455
+ const value = upstream === undefined ? (input as T) : upstream[1]()
1456
+ return value === undefined && fallback !== undefined ? fallback() : value
1457
+ }
1458
+ const slot = createStateSlot<T>(scope[1], sourceMask, read(), assertWritable)
1459
+ if (upstream !== undefined) {
1460
+ const remove = subscribeBinding(upstream, () => slot.replace(read()))
1461
+ const owner = scopeOwners.get(scope)
1462
+ if (owner === undefined) {
1463
+ throw new Error(DEV ? 'createCompiledProp received an unknown scope' : 'V003')
1464
+ }
1465
+ owner[1].add(remove)
1466
+ }
1467
+ return slot
1468
+ }
1469
+
1470
+ export function createCompiledRestProp(
1471
+ scope: CompiledScope,
1472
+ sourceMask: SourceMask,
1473
+ input: Record<string, unknown>,
1474
+ excludedNames: readonly string[],
1475
+ ): StateSlot<Record<string, unknown>> {
1476
+ const excluded = new Set(excludedNames)
1477
+ const entries = (): Array<[string, unknown]> =>
1478
+ Object.entries(input).filter(([name]) => !excluded.has(name))
1479
+ const read = (): Record<string, unknown> =>
1480
+ Object.fromEntries(
1481
+ entries().map(([name, value]) => [name, isCompiledBinding(value) ? value[1]() : value]),
1482
+ )
1483
+ const slot = createStateSlot(scope[1], sourceMask, read(), stateWriteGuard(scope))
1484
+ const publish = (): void => {
1485
+ const previous = slot.get()
1486
+ const next = read()
1487
+ const names = Object.keys(next)
1488
+ if (
1489
+ names.length === Object.keys(previous).length &&
1490
+ names.every((name) => Object.hasOwn(previous, name) && Object.is(previous[name], next[name]))
1491
+ ) {
1492
+ return
1493
+ }
1494
+ slot.replace(next)
1495
+ }
1496
+ const componentSpreadSource = (input as Record<PropertyKey, unknown>)[COMPONENT_SPREAD_SOURCE]
1497
+ const upstreams = new Set(
1498
+ entries()
1499
+ .map(([, value]) => value)
1500
+ .filter(isCompiledBinding),
1501
+ )
1502
+ if (isCompiledBinding(componentSpreadSource)) upstreams.add(componentSpreadSource)
1503
+ const removers = [...upstreams].map((upstream) => subscribeBinding(upstream, publish))
1504
+ const owner = scopeOwners.get(scope)
1505
+ if (owner === undefined) {
1506
+ throw new Error(DEV ? 'createCompiledRestProp received an unknown scope' : 'V003')
1507
+ }
1508
+ owner[1].add(() => {
1509
+ for (const remove of removers) remove()
1510
+ })
1511
+ return slot
1512
+ }
1513
+
1514
+ /** @internal */
1515
+ export function objectRest(
1516
+ input: Record<PropertyKey, unknown>,
1517
+ excludedNames: readonly PropertyKey[],
1518
+ ): Record<PropertyKey, unknown> {
1519
+ const excluded = new Set(excludedNames)
1520
+ const output: Record<PropertyKey, unknown> = {}
1521
+ for (const key of Reflect.ownKeys(input)) {
1522
+ if (excluded.has(key) || !Object.getOwnPropertyDescriptor(input, key)?.enumerable) continue
1523
+ Object.defineProperty(output, key, {
1524
+ configurable: true,
1525
+ enumerable: true,
1526
+ value: input[key],
1527
+ writable: true,
1528
+ })
1529
+ }
1530
+ return output
1531
+ }
1532
+
1533
+ function stateWriteGuard(scope: CompiledScope): () => void {
1534
+ const owner = scopeOwners.get(scope)
1535
+ if (owner === undefined) {
1536
+ throw new Error(DEV ? 'compiled state received an unknown scope' : 'V003')
1537
+ }
1538
+ return () => {
1539
+ if (owner[0] && !disposalCascadeOwners.has(owner)) {
1540
+ throw new Error(DEV ? 'cannot update state after disposal' : 'V012')
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ export function binding<T>(
1546
+ scope: CompiledScope,
1547
+ reads: SourceMask,
1548
+ evaluate: () => T,
1549
+ additionalScope?: CompiledScope,
1550
+ additionalReads?: SourceMask,
1551
+ ): CompiledBinding<T> {
1552
+ return [BINDING, evaluate, scope, reads, additionalScope, additionalReads]
1553
+ }
1554
+
1555
+ export function compiledEvent<Arguments extends unknown[]>(
1556
+ scope: CompiledScope,
1557
+ handler:
1558
+ | ((...arguments_: Arguments) => void)
1559
+ | CompiledBinding<((...arguments_: Arguments) => void) | null | undefined>
1560
+ | null
1561
+ | undefined,
1562
+ ): (...arguments_: Arguments) => void {
1563
+ const owner = scopeOwners.get(scope)
1564
+ if (owner === undefined) {
1565
+ throw new Error(DEV ? 'compiledEvent received an unknown scope' : 'V003')
1566
+ }
1567
+ const errorOwner = activeOwner ?? owner
1568
+ return (...arguments_) =>
1569
+ runOwnerTask(errorOwner, () =>
1570
+ withOwner(errorOwner, () =>
1571
+ scope[2](() => {
1572
+ const current = isCompiledBinding(handler) ? handler[1]() : handler
1573
+ if (typeof current !== 'function') return
1574
+ return current(...arguments_)
1575
+ }),
1576
+ ),
1577
+ )
1578
+ }
1579
+
1580
+ export interface CompiledTaskController {
1581
+ readonly disposed: () => boolean
1582
+ readonly report: (error: unknown) => void
1583
+ }
1584
+
1585
+ export function captureCompiledTask(scope?: CompiledScope): CompiledTaskController {
1586
+ const owner = scope === undefined ? activeOwner : scopeOwners.get(scope)
1587
+ if (owner === null || owner === undefined) {
1588
+ throw new Error(DEV ? 'compiled task must be captured during owned construction' : 'V034')
1589
+ }
1590
+ return {
1591
+ disposed: () => owner[0],
1592
+ report: (error) =>
1593
+ runOwnerTask(owner, () => {
1594
+ throw error
1595
+ }),
1596
+ }
1597
+ }
1598
+
1599
+ export function when(
1600
+ scope: CompiledScope,
1601
+ reads: SourceMask,
1602
+ condition: () => unknown,
1603
+ render: () => RenderValue,
1604
+ additionalScope?: CompiledScope,
1605
+ additionalReads?: SourceMask,
1606
+ ): StructuralBinding {
1607
+ return structural(
1608
+ scope,
1609
+ (parent, before) => {
1610
+ const [start, end, hydrated] = structuralRange(parent, before, 'when')
1611
+ let branchOwner: Owner | null = null
1612
+ let mounted = false
1613
+ let hydrationPending = hydrated
1614
+
1615
+ const update = (): void => {
1616
+ const next = Boolean(condition())
1617
+ if (hydrationPending && !next) {
1618
+ claimHydrationText(parent, '')
1619
+ hydrationPending = false
1620
+ return
1621
+ }
1622
+ if (next === mounted) return
1623
+ const currentParent = rangeParent(start, end, 'conditional block')
1624
+ if (!next) {
1625
+ const owner = branchOwner
1626
+ branchOwner = null
1627
+ mounted = false
1628
+ disposeRange(owner, start, end)
1629
+ return
1630
+ }
1631
+
1632
+ const nextOwner = createOwner()
1633
+ const [fragment, staged] = hydrationPending
1634
+ ? withHydrationInsertion(currentParent, end, () => stageRender(render, nextOwner))
1635
+ : stageRender(render, nextOwner)
1636
+ hydrationPending = false
1637
+ currentParent.insertBefore(fragment, end)
1638
+ commitPublishedNodes(staged)
1639
+ branchOwner = nextOwner
1640
+ mounted = true
1641
+ }
1642
+
1643
+ const removeUpdater = subscribe(scope, reads, update, additionalScope, additionalReads)
1644
+ try {
1645
+ update()
1646
+ } catch (error) {
1647
+ removeUpdater()
1648
+ throw error
1649
+ }
1650
+ onCleanup(() => {
1651
+ removeUpdater()
1652
+ try {
1653
+ disposeRange(branchOwner, start, end)
1654
+ } finally {
1655
+ start.remove()
1656
+ end.remove()
1657
+ }
1658
+ })
1659
+ },
1660
+ 'slot',
1661
+ )
1662
+ }
1663
+
1664
+ export type ChoiceMode = 'truthy' | 'not-nullish'
1665
+
1666
+ export function choose(
1667
+ scope: CompiledScope,
1668
+ reads: SourceMask,
1669
+ mode: ChoiceMode,
1670
+ select: () => unknown,
1671
+ consequent: () => CompiledRenderValue,
1672
+ alternate: () => CompiledRenderValue,
1673
+ additionalScope?: CompiledScope,
1674
+ additionalReads?: SourceMask,
1675
+ ): StructuralBinding {
1676
+ return structural(
1677
+ scope,
1678
+ (parent, before) => {
1679
+ const [start, end, hydrated] = structuralRange(parent, before, 'choice')
1680
+ let selected = -1
1681
+ let branchOwner: Owner | null = null
1682
+ let branchNodes: readonly Node[] = []
1683
+
1684
+ const update = (): void => {
1685
+ const value = select()
1686
+ const next =
1687
+ mode === 'not-nullish' ? (value === null || value === undefined ? 1 : 0) : value ? 0 : 1
1688
+ if (next === selected) return
1689
+ const currentParent = rangeParent(start, end, 'choice block')
1690
+ const nextOwner = createOwner()
1691
+ const [fragment, nodes] =
1692
+ hydrated && selected === -1
1693
+ ? withHydrationInsertion(currentParent, end, () =>
1694
+ stageRender(next === 0 ? consequent : alternate, nextOwner),
1695
+ )
1696
+ : stageRender(next === 0 ? consequent : alternate, nextOwner)
1697
+ currentParent.insertBefore(fragment, end)
1698
+ try {
1699
+ commitPublishedNodes(nodes)
1700
+ } catch (error) {
1701
+ disposePublished(nextOwner, nodes)
1702
+ throw error
1703
+ }
1704
+
1705
+ const previousOwner = branchOwner
1706
+ const previousNodes = branchNodes
1707
+ branchOwner = nextOwner
1708
+ branchNodes = nodes
1709
+ selected = next
1710
+ disposePublished(previousOwner, previousNodes)
1711
+ }
1712
+
1713
+ const removeUpdater = subscribe(scope, reads, update, additionalScope, additionalReads)
1714
+ try {
1715
+ update()
1716
+ } catch (error) {
1717
+ removeUpdater()
1718
+ throw error
1719
+ }
1720
+ onCleanup(() => {
1721
+ removeUpdater()
1722
+ try {
1723
+ disposePublished(branchOwner, branchNodes)
1724
+ } finally {
1725
+ start.remove()
1726
+ end.remove()
1727
+ }
1728
+ })
1729
+ },
1730
+ 'slot',
1731
+ )
1732
+ }
1733
+
1734
+ export function dispatch(
1735
+ scope: CompiledScope,
1736
+ reads: SourceMask,
1737
+ type: () => unknown,
1738
+ key: () => unknown,
1739
+ render: () => CompiledRenderValue,
1740
+ additionalScope?: CompiledScope,
1741
+ additionalReads?: SourceMask,
1742
+ ): StructuralBinding {
1743
+ return structural(
1744
+ scope,
1745
+ (parent, before) => {
1746
+ const [start, end, hydrated] = structuralRange(parent, before, 'dispatch')
1747
+ const unset = Symbol('Vidact.UnsetIdentity')
1748
+ let currentType: unknown = unset
1749
+ let currentKey: unknown = unset
1750
+ let currentOwner: Owner | null = null
1751
+ let currentNodes: readonly Node[] = []
1752
+
1753
+ const update = (): void => {
1754
+ const nextType = type()
1755
+ const nextKey = key()
1756
+ if (
1757
+ currentType !== unset &&
1758
+ Object.is(nextType, currentType) &&
1759
+ Object.is(nextKey, currentKey)
1760
+ ) {
1761
+ return
1762
+ }
1763
+ const currentParent = rangeParent(start, end, 'dispatch block')
1764
+ const nextOwner = createOwner()
1765
+ const [fragment, nodes] =
1766
+ hydrated && currentType === unset
1767
+ ? withHydrationInsertion(currentParent, end, () => stageRender(render, nextOwner))
1768
+ : stageRender(render, nextOwner)
1769
+ currentParent.insertBefore(fragment, end)
1770
+ try {
1771
+ commitPublishedNodes(nodes)
1772
+ } catch (error) {
1773
+ disposePublished(nextOwner, nodes)
1774
+ throw error
1775
+ }
1776
+
1777
+ const previousOwner = currentOwner
1778
+ const previousNodes = currentNodes
1779
+ currentType = nextType
1780
+ currentKey = nextKey
1781
+ currentOwner = nextOwner
1782
+ currentNodes = nodes
1783
+ disposePublished(previousOwner, previousNodes)
1784
+ }
1785
+
1786
+ const removeUpdater = subscribe(scope, reads, update, additionalScope, additionalReads)
1787
+ try {
1788
+ update()
1789
+ } catch (error) {
1790
+ removeUpdater()
1791
+ throw error
1792
+ }
1793
+ onCleanup(() => {
1794
+ removeUpdater()
1795
+ try {
1796
+ disposePublished(currentOwner, currentNodes)
1797
+ } finally {
1798
+ start.remove()
1799
+ end.remove()
1800
+ }
1801
+ })
1802
+ },
1803
+ 'slot',
1804
+ )
1805
+ }
1806
+
1807
+ export function keyed<T, K>(
1808
+ scope: CompiledScope,
1809
+ reads: SourceMask,
1810
+ values: () => readonly T[],
1811
+ key: (value: T, index: number) => K,
1812
+ render: (value: StateSlot<T>, index: StateSlot<number>, itemScope: CompiledScope) => RenderValue,
1813
+ additionalScope?: CompiledScope,
1814
+ additionalReads?: SourceMask,
1815
+ ): StructuralBinding {
1816
+ return structural(
1817
+ scope,
1818
+ (parent, before) => {
1819
+ const itemSource = 1
1820
+ const indexSource = 2
1821
+ const list = createKeyedList(
1822
+ parent,
1823
+ {
1824
+ key,
1825
+ render(value, index) {
1826
+ const owner = createOwner()
1827
+ const itemScope = createNarrowCompiledScope()
1828
+ const valueSlot = createCompiledState(itemScope, itemSource, value)
1829
+ const indexSlot = createCompiledState(itemScope, indexSource, index)
1830
+ try {
1831
+ const nodes = withOwner(owner, () => {
1832
+ onCleanup(itemScope[3])
1833
+ return materialize(render(valueSlot, indexSlot, itemScope))
1834
+ })
1835
+ return [
1836
+ nodes,
1837
+ (nextValue: T, nextIndex: number) => {
1838
+ itemScope[2](() => {
1839
+ valueSlot.set(nextValue)
1840
+ indexSlot.set(nextIndex)
1841
+ })
1842
+ },
1843
+ () => disposeOwner(owner),
1844
+ ] as const
1845
+ } catch (error) {
1846
+ disposeOwner(owner)
1847
+ throw error
1848
+ }
1849
+ },
1850
+ },
1851
+ before,
1852
+ )
1853
+ const update = (): void => {
1854
+ try {
1855
+ commitPublishedNodes(list.update(values()))
1856
+ } catch (error) {
1857
+ const currentParent = list.parent()
1858
+ if (currentParent !== null) commitPendingRefs(currentParent)
1859
+ throw error
1860
+ }
1861
+ }
1862
+ update()
1863
+ const removeUpdater = subscribe(scope, reads, update, additionalScope, additionalReads)
1864
+ onCleanup(() => {
1865
+ removeUpdater()
1866
+ list.dispose()
1867
+ })
1868
+ },
1869
+ 'array',
1870
+ )
1871
+ }
1872
+
1873
+ export function indexed<T>(
1874
+ scope: CompiledScope,
1875
+ reads: SourceMask,
1876
+ values: () => readonly T[],
1877
+ render: (value: StateSlot<T>, index: StateSlot<number>, itemScope: CompiledScope) => RenderValue,
1878
+ additionalScope?: CompiledScope,
1879
+ additionalReads?: SourceMask,
1880
+ ): StructuralBinding {
1881
+ return structural(
1882
+ scope,
1883
+ (parent, before) => {
1884
+ const itemSource = 1
1885
+ const indexSource = 2
1886
+ const list = createIndexedList<T>(
1887
+ parent,
1888
+ {
1889
+ render(value, index) {
1890
+ const owner = createOwner()
1891
+ const itemScope = createNarrowCompiledScope()
1892
+ const valueSlot = createCompiledState(itemScope, itemSource, value)
1893
+ const indexSlot = createCompiledState(itemScope, indexSource, index)
1894
+ try {
1895
+ const nodes = withOwner(owner, () => {
1896
+ onCleanup(itemScope[3])
1897
+ return materialize(render(valueSlot, indexSlot, itemScope))
1898
+ })
1899
+ return [
1900
+ nodes,
1901
+ (nextValue: T, nextIndex: number) => {
1902
+ itemScope[2](() => {
1903
+ valueSlot.set(nextValue)
1904
+ indexSlot.set(nextIndex)
1905
+ })
1906
+ },
1907
+ () => disposeOwner(owner),
1908
+ ] as const
1909
+ } catch (error) {
1910
+ disposeOwner(owner)
1911
+ throw error
1912
+ }
1913
+ },
1914
+ },
1915
+ before,
1916
+ )
1917
+ const update = (): void => {
1918
+ try {
1919
+ commitPublishedNodes(list.update(values()))
1920
+ } catch (error) {
1921
+ const currentParent = list.parent()
1922
+ if (currentParent !== null) commitPendingRefs(currentParent)
1923
+ throw error
1924
+ }
1925
+ }
1926
+ update()
1927
+ const removeUpdater = subscribe(scope, reads, update, additionalScope, additionalReads)
1928
+ onCleanup(() => {
1929
+ removeUpdater()
1930
+ list.dispose()
1931
+ })
1932
+ },
1933
+ 'array',
1934
+ )
1935
+ }
1936
+
1937
+ export function compiledRoot(
1938
+ scope: CompiledScope,
1939
+ render: () => CompiledRenderValue,
1940
+ ): CompiledComponentResult {
1941
+ const owner = scopeOwners.get(scope)
1942
+ if (owner === undefined) {
1943
+ throw new Error(DEV ? 'compiledRoot received an unknown scope' : 'V004')
1944
+ }
1945
+ const hydratedRange = claimHydrationComponentRange()
1946
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:component' : '')
1947
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:component' : '')
1948
+ componentCommitOwners.set(end, owner)
1949
+ const fragment = document.createDocumentFragment()
1950
+ if (hydratedRange === undefined) fragment.append(start, end)
1951
+ owner[1].add(() => {
1952
+ try {
1953
+ removeBetween(start, end)
1954
+ } finally {
1955
+ start.remove()
1956
+ end.remove()
1957
+ }
1958
+ })
1959
+
1960
+ try {
1961
+ const renderParent = hydrationRangeParent(start, end) ?? fragment
1962
+ const insertRender = () =>
1963
+ withOwner(owner, () =>
1964
+ withScopeNamespace(scope, () => insertValue(renderParent, render(), end)),
1965
+ )
1966
+ const insert = () => {
1967
+ if (hydratedRange === undefined) insertRender()
1968
+ else
1969
+ withHydrationComponentRange(hydratedRange, () =>
1970
+ withHydrationCursor(renderParent, start.nextSibling, insertRender),
1971
+ )
1972
+ }
1973
+ if (profilingEnabled) measureProfileWork(owner, 'range', insert)
1974
+ else insert()
1975
+ } catch (error) {
1976
+ try {
1977
+ scope[3]()
1978
+ } catch {
1979
+ // Preserve the render error that made this component unmountable.
1980
+ }
1981
+ throw error
1982
+ }
1983
+
1984
+ let mounted = false
1985
+ const component: CompiledComponentResult = [
1986
+ STRUCTURAL,
1987
+ (parent, before) => {
1988
+ if (mounted) throw new Error(DEV ? 'compiled component is already mounted' : 'V005')
1989
+ mounted = true
1990
+ adoptCompiledRoot(component)
1991
+ try {
1992
+ if (!claimHydrationComponentMount(parent, start, end)) {
1993
+ parent.insertBefore(fragment, before)
1994
+ }
1995
+ } catch (error) {
1996
+ try {
1997
+ scope[3]()
1998
+ } catch {
1999
+ // Preserve the insertion error that made this component unmountable.
2000
+ }
2001
+ throw error
2002
+ }
2003
+ },
2004
+ ]
2005
+ componentRanges.set(component, [start, end, scope])
2006
+ return component
2007
+ }
2008
+
2009
+ export function adoptCompiledRoot(root: unknown): void {
2010
+ if (!isCompiledComponentResult(root)) return
2011
+ const range = componentRanges.get(root)
2012
+ if (range === undefined) return
2013
+ const owner = scopeOwners.get(range[2])
2014
+ if (activeOwner !== null && owner !== undefined && activeOwner !== owner) {
2015
+ activeOwner[1].add(range[2][3])
2016
+ }
2017
+ }
2018
+
2019
+ export function constructCompiledComponent<T extends CompiledRenderValue>(
2020
+ component: () => T,
2021
+ componentType?: unknown,
2022
+ ): T {
2023
+ const previousCollector = activeScopeCollector
2024
+ const previousConstructionOwner = activeConstructionOwner
2025
+ const previousProfileName = activeProfileName
2026
+ const scopes = new Set<CompiledScope>()
2027
+ activeScopeCollector = scopes
2028
+ activeConstructionOwner = null
2029
+ const profileType = componentType as
2030
+ | { readonly name?: string; readonly displayName?: string }
2031
+ | undefined
2032
+ if (DEV) activeProfileName = profileType?.displayName ?? profileType?.name ?? 'Anonymous'
2033
+ try {
2034
+ return component()
2035
+ } catch (error) {
2036
+ // oxlint-disable-next-line unicorn/no-array-reverse - scopes is already copied to avoid mutation during iteration
2037
+ for (const scope of [...scopes].reverse()) {
2038
+ try {
2039
+ scope[3]()
2040
+ } catch {
2041
+ // Preserve the construction error that made this component unmountable.
2042
+ }
2043
+ }
2044
+ throw error
2045
+ } finally {
2046
+ activeScopeCollector = previousCollector
2047
+ activeConstructionOwner = previousConstructionOwner
2048
+ if (DEV) activeProfileName = previousProfileName
2049
+ }
2050
+ }
2051
+
2052
+ export function queueElementRef(element: Element, value: unknown): void {
2053
+ if (!isRefValue(value)) {
2054
+ throw new TypeError(DEV ? 'ref must be a callback or an object with current' : 'V006')
2055
+ }
2056
+ pendingRefs.set(element, [activeOwner, value])
2057
+ }
2058
+
2059
+ export function useImperativeHandle<T>(
2060
+ ref: RefValue<T>,
2061
+ create: () => T,
2062
+ dependencies?: readonly unknown[],
2063
+ ): void {
2064
+ const owner = activeConstructionOwner
2065
+ if (owner === null) {
2066
+ throw new Error(
2067
+ DEV ? 'useImperativeHandle must run during compiled component construction' : 'V013',
2068
+ )
2069
+ }
2070
+ if (!isRefValue(ref)) {
2071
+ throw new TypeError(
2072
+ DEV ? 'imperative ref must be null, a callback, or an object with current' : 'V006',
2073
+ )
2074
+ }
2075
+ if (dependencies !== undefined && dependencies.length !== 0) {
2076
+ throw new Error(
2077
+ DEV ? 'reactive useImperativeHandle dependencies are not supported yet' : 'V014',
2078
+ )
2079
+ }
2080
+ queueOwnerCommit(owner, () => owner[1].add(attachRef(ref, create())))
2081
+ }
2082
+
2083
+ type EffectResult = void | (() => void)
2084
+
2085
+ export function useLayoutEffect(
2086
+ create: () => EffectResult,
2087
+ dependencies?: readonly unknown[],
2088
+ ): void {
2089
+ registerEffect(create, dependencies, false)
2090
+ }
2091
+
2092
+ export function useInsertionEffect(
2093
+ create: () => EffectResult,
2094
+ dependencies?: readonly unknown[],
2095
+ ): void {
2096
+ const owner = activeConstructionOwner
2097
+ if (owner === null) {
2098
+ throw new Error(
2099
+ DEV ? 'insertion effects must run during compiled component construction' : 'V016',
2100
+ )
2101
+ }
2102
+ if (dependencies !== undefined && dependencies.length !== 0) {
2103
+ throw new Error(
2104
+ DEV ? 'reactive insertion effect dependencies require compiler lowering' : 'V017',
2105
+ )
2106
+ }
2107
+ const lifetimeOwner = activeOwner ?? owner
2108
+ if (!retainedUiEnabled) {
2109
+ let cleanup = noop
2110
+ queueInsertionCommit(owner, () => {
2111
+ if (lifetimeOwner[0]) return
2112
+ cleanup()
2113
+ cleanup = profilingEnabled
2114
+ ? readProfiledEffectCleanup(owner, create)
2115
+ : readEffectCleanup(create())
2116
+ })
2117
+ lifetimeOwner[1].add(() => cleanup())
2118
+ return
2119
+ }
2120
+ let cleanup = noop
2121
+ const [activate, disposeResource] = createRetainedResource(
2122
+ lifetimeOwner,
2123
+ () => {
2124
+ stagePublication([
2125
+ noop,
2126
+ noop,
2127
+ undefined,
2128
+ () => {
2129
+ cleanup = profilingEnabled
2130
+ ? readProfiledEffectCleanup(owner, create)
2131
+ : readEffectCleanup(create())
2132
+ },
2133
+ -20,
2134
+ ])
2135
+ },
2136
+ () => {
2137
+ cleanup()
2138
+ cleanup = noop
2139
+ },
2140
+ )
2141
+ queueInsertionCommit(owner, () => {
2142
+ if (!lifetimeOwner[0]) activate()
2143
+ })
2144
+ lifetimeOwner[1].add(disposeResource)
2145
+ }
2146
+
2147
+ export function useEffect(create: () => EffectResult, dependencies?: readonly unknown[]): void {
2148
+ registerEffect(create, dependencies, true)
2149
+ }
2150
+
2151
+ function registerEffect(
2152
+ create: () => EffectResult,
2153
+ dependencies: readonly unknown[] | undefined,
2154
+ passive: boolean,
2155
+ ): void {
2156
+ const owner = activeConstructionOwner
2157
+ if (owner === null) {
2158
+ throw new Error(DEV ? 'effects must run during compiled component construction' : 'V016')
2159
+ }
2160
+ if (dependencies !== undefined && dependencies.length !== 0) {
2161
+ throw new Error(DEV ? 'reactive effect dependencies require compiler lowering' : 'V017')
2162
+ }
2163
+ const lifetimeOwner = activeOwner ?? owner
2164
+ if (!retainedUiEnabled) {
2165
+ let cleanup = noop
2166
+ let generation = 0
2167
+ const run = (): void => {
2168
+ cleanup()
2169
+ cleanup = profilingEnabled
2170
+ ? readProfiledEffectCleanup(owner, create)
2171
+ : readEffectCleanup(create())
2172
+ }
2173
+ queueOwnerCommit(owner, () => {
2174
+ if (lifetimeOwner[0]) return
2175
+ if (passive) {
2176
+ const scheduled = ++generation
2177
+ ;(DEV ? scheduleTask : queueMicrotask)(() => {
2178
+ if (!lifetimeOwner[0] && scheduled === generation) runOwnerTask(lifetimeOwner, run)
2179
+ })
2180
+ } else {
2181
+ run()
2182
+ }
2183
+ })
2184
+ lifetimeOwner[1].add(() => {
2185
+ generation += 1
2186
+ if (passive) (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, cleanup))
2187
+ else cleanup()
2188
+ })
2189
+ return
2190
+ }
2191
+ let cleanup = noop
2192
+ let generation = 0
2193
+ const [activate, disposeResource] = createRetainedResource(
2194
+ lifetimeOwner,
2195
+ () => {
2196
+ stagePublication([
2197
+ noop,
2198
+ noop,
2199
+ undefined,
2200
+ () => {
2201
+ if (passive) {
2202
+ const scheduled = ++generation
2203
+ ;(DEV ? scheduleTask : queueMicrotask)(() => {
2204
+ if (!lifetimeOwner[0] && scheduled === generation) {
2205
+ runOwnerTask(lifetimeOwner, () => {
2206
+ cleanup = profilingEnabled
2207
+ ? readProfiledEffectCleanup(owner, create)
2208
+ : readEffectCleanup(create())
2209
+ })
2210
+ }
2211
+ })
2212
+ } else {
2213
+ cleanup = profilingEnabled
2214
+ ? readProfiledEffectCleanup(owner, create)
2215
+ : readEffectCleanup(create())
2216
+ }
2217
+ },
2218
+ passive ? 40 : 20,
2219
+ ])
2220
+ },
2221
+ () => {
2222
+ generation += 1
2223
+ const previousCleanup = cleanup
2224
+ cleanup = noop
2225
+ if (passive)
2226
+ (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, previousCleanup))
2227
+ else previousCleanup()
2228
+ },
2229
+ )
2230
+ queueOwnerCommit(owner, () => {
2231
+ if (!lifetimeOwner[0]) activate()
2232
+ })
2233
+ lifetimeOwner[1].add(disposeResource)
2234
+ }
2235
+
2236
+ export function compiledLayoutEffect(
2237
+ scope: CompiledScope,
2238
+ reads: SourceMask,
2239
+ readCreate: () => () => EffectResult,
2240
+ readDependencies?: () => readonly unknown[],
2241
+ ): void {
2242
+ compiledEffect(scope, reads, readCreate, readDependencies, false)
2243
+ }
2244
+
2245
+ export function compiledInsertionEffect(
2246
+ scope: CompiledScope,
2247
+ reads: SourceMask,
2248
+ readCreate: () => () => EffectResult,
2249
+ readDependencies?: () => readonly unknown[],
2250
+ ): void {
2251
+ const owner = activeConstructionOwner
2252
+ if (owner === null || scopeOwners.get(scope) !== owner) {
2253
+ throw new Error(DEV ? 'compiled insertion effects must run in their component scope' : 'V016')
2254
+ }
2255
+ const lifetimeOwner = activeOwner ?? owner
2256
+ if (!retainedUiEnabled) {
2257
+ let mounted = false
2258
+ let currentDependencies: readonly unknown[] | undefined
2259
+ let cleanup = noop
2260
+ const run = (): void => {
2261
+ const nextDependencies = readDependencies?.()
2262
+ if (
2263
+ mounted &&
2264
+ readDependencies !== undefined &&
2265
+ equalDependencies(nextDependencies, currentDependencies)
2266
+ ) {
2267
+ return
2268
+ }
2269
+ cleanup()
2270
+ cleanup = profilingEnabled
2271
+ ? readProfiledEffectCleanup(owner, () => readCreate()())
2272
+ : readEffectCleanup(readCreate()())
2273
+ currentDependencies = nextDependencies
2274
+ mounted = true
2275
+ }
2276
+ queueInsertionCommit(owner, () => {
2277
+ if (!lifetimeOwner[0]) run()
2278
+ })
2279
+ const removeUpdater = subscribe(scope, reads, () =>
2280
+ stagePublication([noop, noop, undefined, run, -20]),
2281
+ )
2282
+ lifetimeOwner[1].add(() => {
2283
+ removeUpdater()
2284
+ cleanup()
2285
+ })
2286
+ return
2287
+ }
2288
+ let mounted = false
2289
+ let connected = false
2290
+ let currentDependencies: readonly unknown[] | undefined
2291
+ let cleanup = noop
2292
+ const run = (): void => {
2293
+ const nextDependencies = readDependencies?.()
2294
+ if (
2295
+ mounted &&
2296
+ readDependencies !== undefined &&
2297
+ equalDependencies(nextDependencies, currentDependencies)
2298
+ ) {
2299
+ return
2300
+ }
2301
+ cleanup()
2302
+ cleanup = profilingEnabled
2303
+ ? readProfiledEffectCleanup(owner, () => readCreate()())
2304
+ : readEffectCleanup(readCreate()())
2305
+ currentDependencies = nextDependencies
2306
+ mounted = true
2307
+ }
2308
+ const [activate, disposeResource] = createRetainedResource(
2309
+ lifetimeOwner,
2310
+ () => {
2311
+ stagePublication([
2312
+ noop,
2313
+ noop,
2314
+ undefined,
2315
+ () => {
2316
+ connected = true
2317
+ run()
2318
+ },
2319
+ -20,
2320
+ ])
2321
+ },
2322
+ () => {
2323
+ connected = false
2324
+ cleanup()
2325
+ cleanup = noop
2326
+ mounted = false
2327
+ },
2328
+ )
2329
+ queueInsertionCommit(owner, () => {
2330
+ if (!lifetimeOwner[0]) activate()
2331
+ })
2332
+ const removeUpdater = subscribe(scope, reads, () => {
2333
+ if (connected) stagePublication([noop, noop, undefined, run, -20])
2334
+ })
2335
+ lifetimeOwner[1].add(() => {
2336
+ removeUpdater()
2337
+ disposeResource()
2338
+ })
2339
+ }
2340
+
2341
+ export function compiledEffect(
2342
+ scope: CompiledScope,
2343
+ reads: SourceMask,
2344
+ readCreate: () => () => EffectResult,
2345
+ readDependencies?: () => readonly unknown[],
2346
+ passive = true,
2347
+ ): void {
2348
+ const owner = activeConstructionOwner
2349
+ if (owner === null || scopeOwners.get(scope) !== owner) {
2350
+ throw new Error(DEV ? 'compiled effects must run in their component scope' : 'V016')
2351
+ }
2352
+ const lifetimeOwner = activeOwner ?? owner
2353
+ if (!retainedUiEnabled) {
2354
+ let mounted = false
2355
+ let generation = 0
2356
+ let currentDependencies: readonly unknown[] | undefined
2357
+ let cleanup = noop
2358
+ const run = (): void => {
2359
+ const nextDependencies = readDependencies?.()
2360
+ if (
2361
+ mounted &&
2362
+ readDependencies !== undefined &&
2363
+ equalDependencies(nextDependencies, currentDependencies)
2364
+ ) {
2365
+ return
2366
+ }
2367
+ cleanup()
2368
+ cleanup = profilingEnabled
2369
+ ? readProfiledEffectCleanup(owner, () => readCreate()())
2370
+ : readEffectCleanup(readCreate()())
2371
+ currentDependencies = nextDependencies
2372
+ mounted = true
2373
+ }
2374
+ const schedule = (): void => {
2375
+ if (!passive) {
2376
+ run()
2377
+ return
2378
+ }
2379
+ const scheduled = ++generation
2380
+ ;(DEV ? scheduleTask : queueMicrotask)(() => {
2381
+ if (!lifetimeOwner[0] && scheduled === generation) runOwnerTask(lifetimeOwner, run)
2382
+ })
2383
+ }
2384
+ queueOwnerCommit(owner, () => {
2385
+ if (!lifetimeOwner[0]) schedule()
2386
+ })
2387
+ const removeUpdater = subscribe(scope, reads, () => {
2388
+ if (passive) schedule()
2389
+ else stagePublication([noop, noop, undefined, run, 20])
2390
+ })
2391
+ lifetimeOwner[1].add(() => {
2392
+ generation += 1
2393
+ removeUpdater()
2394
+ if (passive) (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, cleanup))
2395
+ else cleanup()
2396
+ })
2397
+ return
2398
+ }
2399
+ let mounted = false
2400
+ let connected = false
2401
+ let generation = 0
2402
+ let currentDependencies: readonly unknown[] | undefined
2403
+ let cleanup = noop
2404
+ const run = (): void => {
2405
+ const nextDependencies = readDependencies?.()
2406
+ if (
2407
+ mounted &&
2408
+ readDependencies !== undefined &&
2409
+ equalDependencies(nextDependencies, currentDependencies)
2410
+ ) {
2411
+ return
2412
+ }
2413
+ cleanup()
2414
+ cleanup = profilingEnabled
2415
+ ? readProfiledEffectCleanup(owner, () => readCreate()())
2416
+ : readEffectCleanup(readCreate()())
2417
+ currentDependencies = nextDependencies
2418
+ mounted = true
2419
+ }
2420
+ const schedule = (): void => {
2421
+ if (!connected) return
2422
+ if (!passive) {
2423
+ run()
2424
+ return
2425
+ }
2426
+ const scheduled = ++generation
2427
+ ;(DEV ? scheduleTask : queueMicrotask)(() => {
2428
+ if (!lifetimeOwner[0] && scheduled === generation) runOwnerTask(lifetimeOwner, run)
2429
+ })
2430
+ }
2431
+ const [activate, disposeResource] = createRetainedResource(
2432
+ lifetimeOwner,
2433
+ () => {
2434
+ stagePublication([
2435
+ noop,
2436
+ noop,
2437
+ undefined,
2438
+ () => {
2439
+ connected = true
2440
+ schedule()
2441
+ },
2442
+ passive ? 40 : 20,
2443
+ ])
2444
+ },
2445
+ () => {
2446
+ connected = false
2447
+ generation += 1
2448
+ const previousCleanup = cleanup
2449
+ cleanup = noop
2450
+ mounted = false
2451
+ if (passive)
2452
+ (DEV ? scheduleTask : queueMicrotask)(() => runOwnerTask(lifetimeOwner, previousCleanup))
2453
+ else previousCleanup()
2454
+ },
2455
+ )
2456
+ queueOwnerCommit(owner, () => {
2457
+ if (!lifetimeOwner[0]) activate()
2458
+ })
2459
+ const removeUpdater = subscribe(scope, reads, () => {
2460
+ if (!connected) return
2461
+ if (passive) schedule()
2462
+ else stagePublication([noop, noop, undefined, run, 20])
2463
+ })
2464
+ lifetimeOwner[1].add(() => {
2465
+ removeUpdater()
2466
+ disposeResource()
2467
+ })
2468
+ }
2469
+
2470
+ function readEffectCleanup(result: EffectResult): () => void {
2471
+ if (result === undefined) return noop
2472
+ if (typeof result === 'function') return result
2473
+ throw new TypeError(DEV ? 'an effect must return a cleanup function or undefined' : 'V018')
2474
+ }
2475
+
2476
+ export function compiledImperativeHandle<T>(
2477
+ scope: CompiledScope,
2478
+ reads: SourceMask,
2479
+ readRef: () => RefValue<T>,
2480
+ create: () => T,
2481
+ readDependencies?: () => readonly unknown[],
2482
+ ): void {
2483
+ const owner = activeConstructionOwner
2484
+ if (owner === null || scopeOwners.get(scope) !== owner) {
2485
+ throw new Error(
2486
+ DEV ? 'compiledImperativeHandle must run in its compiled component scope' : 'V013',
2487
+ )
2488
+ }
2489
+
2490
+ let mounted = false
2491
+ let currentRef: RefValue<T>
2492
+ let currentHandle: T
2493
+ let currentDependencies: readonly unknown[] | undefined
2494
+ // oxlint-disable-next-line unicorn/consistent-function-scoping -- This placeholder is replaced by the committed resource cleanup.
2495
+ let cleanup = noop
2496
+
2497
+ const commitInitial = (): void => {
2498
+ currentRef = readImperativeRef(readRef())
2499
+ currentDependencies = readDependencies?.()
2500
+ currentHandle = create()
2501
+ cleanup = attachRef(currentRef, currentHandle)
2502
+ mounted = true
2503
+ }
2504
+ queueOwnerCommit(owner, commitInitial)
2505
+
2506
+ const update = (): void => {
2507
+ if (!mounted) return
2508
+ const nextRef = readImperativeRef(readRef())
2509
+ const nextDependencies = readDependencies?.()
2510
+ if (
2511
+ Object.is(nextRef, currentRef) &&
2512
+ readDependencies !== undefined &&
2513
+ equalDependencies(nextDependencies, currentDependencies)
2514
+ ) {
2515
+ return
2516
+ }
2517
+
2518
+ const previousRef = currentRef
2519
+ const previousHandle = currentHandle
2520
+ const previousDependencies = currentDependencies
2521
+ const previousCleanup = cleanup
2522
+ let nextCleanup: (() => void) | undefined
2523
+ let finalized = false
2524
+ stagePublication([
2525
+ noop,
2526
+ () => {
2527
+ if (!finalized) return
2528
+ nextCleanup?.()
2529
+ cleanup = attachRef(previousRef, previousHandle)
2530
+ currentRef = previousRef
2531
+ currentHandle = previousHandle
2532
+ currentDependencies = previousDependencies
2533
+ finalized = false
2534
+ },
2535
+ undefined,
2536
+ () => {
2537
+ const nextHandle = create()
2538
+ if (Object.is(nextRef, previousRef)) {
2539
+ try {
2540
+ previousCleanup()
2541
+ } catch (error) {
2542
+ try {
2543
+ cleanup = attachRef(previousRef, previousHandle)
2544
+ } catch {
2545
+ // Preserve the cleanup error that aborted publication.
2546
+ }
2547
+ throw error
2548
+ }
2549
+ try {
2550
+ nextCleanup = attachRef(nextRef, nextHandle)
2551
+ } catch (error) {
2552
+ try {
2553
+ cleanup = attachRef(previousRef, previousHandle)
2554
+ } catch {
2555
+ // Preserve the next-ref attachment error.
2556
+ }
2557
+ throw error
2558
+ }
2559
+ } else {
2560
+ nextCleanup = attachRef(nextRef, nextHandle)
2561
+ try {
2562
+ previousCleanup()
2563
+ } catch (error) {
2564
+ nextCleanup()
2565
+ try {
2566
+ cleanup = attachRef(previousRef, previousHandle)
2567
+ } catch {
2568
+ // Preserve the previous-ref cleanup error.
2569
+ }
2570
+ throw error
2571
+ }
2572
+ }
2573
+ cleanup = nextCleanup
2574
+ currentRef = nextRef
2575
+ currentHandle = nextHandle
2576
+ currentDependencies = nextDependencies
2577
+ finalized = true
2578
+ },
2579
+ 10,
2580
+ ])
2581
+ }
2582
+ const removeUpdater = subscribe(scope, reads, update)
2583
+ owner[1].add(() => {
2584
+ removeUpdater()
2585
+ cleanup()
2586
+ })
2587
+ }
2588
+
2589
+ function queueOwnerCommit(owner: Owner, commit: () => void): void {
2590
+ let commits = pendingOwnerCommits.get(owner)
2591
+ if (commits === undefined) {
2592
+ commits = new Set()
2593
+ pendingOwnerCommits.set(owner, commits)
2594
+ }
2595
+ commits.add(commit)
2596
+ }
2597
+
2598
+ function queueInsertionCommit(owner: Owner, commit: () => void): void {
2599
+ let commits = pendingInsertionCommits.get(owner)
2600
+ if (commits === undefined) {
2601
+ commits = new Set()
2602
+ pendingInsertionCommits.set(owner, commits)
2603
+ }
2604
+ commits.add(commit)
2605
+ }
2606
+
2607
+ function readImperativeRef<T>(ref: RefValue<T>): RefValue<T> {
2608
+ if (!isRefValue(ref)) {
2609
+ throw new TypeError(
2610
+ DEV ? 'imperative ref must be null, a callback, or an object with current' : 'V006',
2611
+ )
2612
+ }
2613
+ return ref
2614
+ }
2615
+
2616
+ function equalDependencies(
2617
+ next: readonly unknown[] | undefined,
2618
+ previous: readonly unknown[] | undefined,
2619
+ ): boolean {
2620
+ if (next === undefined || previous === undefined || next.length !== previous.length) return false
2621
+ return next.every((value, index) => Object.is(value, previous[index]))
2622
+ }
2623
+
2624
+ export function mountCompiledRef(element: Element, value: CompiledBinding<unknown>): void {
2625
+ const initial = value[1]()
2626
+ if (!isRefValue(initial)) {
2627
+ throw new TypeError(DEV ? 'ref must be null, a callback, or an object with current' : 'V006')
2628
+ }
2629
+ let current: RefValue = initial
2630
+ // oxlint-disable-next-line unicorn/consistent-function-scoping -- This placeholder is replaced when the ref attaches.
2631
+ let cleanup = noop
2632
+ const pending: PendingRef = [activeOwner, current, (attached) => (cleanup = attached)]
2633
+ pendingRefs.set(element, pending)
2634
+
2635
+ const removeUpdater = subscribeBinding(value, () => {
2636
+ const next = value[1]()
2637
+ if (Object.is(next, current)) return
2638
+ if (!isRefValue(next)) {
2639
+ throw new TypeError(DEV ? 'ref must be null, a callback, or an object with current' : 'V006')
2640
+ }
2641
+ const previous = current
2642
+ const previousCleanup = cleanup
2643
+ let nextCleanup: (() => void) | undefined
2644
+ let previousDetached = false
2645
+ let committed = false
2646
+ stagePublication([
2647
+ () => {
2648
+ try {
2649
+ previousCleanup()
2650
+ previousDetached = true
2651
+ } catch (error) {
2652
+ try {
2653
+ cleanup = attachRef(previous, element)
2654
+ } catch {
2655
+ // Preserve the previous-ref cleanup error.
2656
+ }
2657
+ throw error
2658
+ }
2659
+ try {
2660
+ nextCleanup = attachRef(next, element)
2661
+ } catch (error) {
2662
+ try {
2663
+ cleanup = attachRef(previous, element)
2664
+ previousDetached = false
2665
+ } catch {
2666
+ // Preserve the next-ref attachment error.
2667
+ }
2668
+ throw error
2669
+ }
2670
+ cleanup = nextCleanup
2671
+ current = next
2672
+ committed = true
2673
+ },
2674
+ () => {
2675
+ if (!committed) {
2676
+ nextCleanup?.()
2677
+ if (previousDetached) {
2678
+ cleanup = attachRef(previous, element)
2679
+ previousDetached = false
2680
+ }
2681
+ return
2682
+ }
2683
+ cleanup()
2684
+ cleanup = attachRef(previous, element)
2685
+ current = previous
2686
+ committed = false
2687
+ },
2688
+ ])
2689
+ })
2690
+ onCleanup(() => {
2691
+ removeUpdater()
2692
+ if (pendingRefs.get(element) === pending) pendingRefs.delete(element)
2693
+ cleanup()
2694
+ })
2695
+ }
2696
+
2697
+ export function registerCompiledCleanup(cleanup: () => void): void {
2698
+ onCleanup(cleanup)
2699
+ }
2700
+
2701
+ export interface MountCompiledOptions {
2702
+ readonly identifierPrefix?: string
2703
+ readonly onCaughtError?: CompiledErrorHandler
2704
+ readonly onRecoverableError?: CompiledErrorHandler
2705
+ readonly onUncaughtError?: CompiledErrorHandler
2706
+ }
2707
+
2708
+ export function hydrateCompiled(
2709
+ component: () => CompiledComponentResult,
2710
+ host: ParentNode,
2711
+ options?: MountCompiledOptions,
2712
+ ): { dispose: () => void } {
2713
+ let endHydration: (() => void) | undefined
2714
+ let hydratedMount: { dispose: () => void } | undefined
2715
+ try {
2716
+ endHydration = beginHydration(host)
2717
+ hydratedMount = mountCompiled(component, host, options)
2718
+ finishHydration()
2719
+ const rootMarkers = hydrationRootMarkers()
2720
+ return {
2721
+ dispose: () => {
2722
+ try {
2723
+ hydratedMount?.dispose()
2724
+ } finally {
2725
+ removeBetween(rootMarkers[0], rootMarkers[1])
2726
+ rootMarkers[0].remove()
2727
+ rootMarkers[1].remove()
2728
+ }
2729
+ },
2730
+ }
2731
+ } catch (error) {
2732
+ if (!isHydrationMismatch(error)) throw error
2733
+ try {
2734
+ hydratedMount?.dispose()
2735
+ } catch {
2736
+ // Preserve and report the mismatch that forced recovery.
2737
+ }
2738
+ hydratedMount = undefined
2739
+ endHydration?.()
2740
+ endHydration = undefined
2741
+ options?.onRecoverableError?.(error)
2742
+ return mountCompiled(component, host, options)
2743
+ } finally {
2744
+ endHydration?.()
2745
+ }
2746
+ }
2747
+
2748
+ export function mountCompiled(
2749
+ component: () => CompiledComponentResult,
2750
+ host: ParentNode,
2751
+ options?: MountCompiledOptions,
2752
+ ): { dispose: () => void } {
2753
+ const hydrating = isHydrating()
2754
+ const previousRootIdentity = activeRootIdentity
2755
+ const rootIdentity = createRootIdentity(
2756
+ options?.identifierPrefix,
2757
+ options?.onCaughtError,
2758
+ options?.onUncaughtError,
2759
+ )
2760
+ activeRootIdentity = rootIdentity
2761
+ let root: CompiledComponentResult
2762
+ try {
2763
+ root = constructCompiledComponent(component)
2764
+ } catch (error) {
2765
+ if (isHydrationMismatch(error)) throw error
2766
+ if (rootIdentity.onUncaughtError === undefined) throw error
2767
+ rootIdentity.onUncaughtError(error)
2768
+ return { dispose: noop }
2769
+ } finally {
2770
+ activeRootIdentity = previousRootIdentity
2771
+ }
2772
+ const range = componentRanges.get(root)
2773
+ if (range === undefined) {
2774
+ throw new Error(DEV ? 'mountCompiled received an unknown component result' : 'V007')
2775
+ }
2776
+ const previous = [...host.childNodes]
2777
+ try {
2778
+ root[1](host, previous[0] ?? null)
2779
+ const rootOwner = scopeOwners.get(range[2])
2780
+ const mountedRootIdentity = rootOwner?.[3]
2781
+ if (mountedRootIdentity !== undefined) {
2782
+ mountedRootIdentity.mounted = true
2783
+ commitRootPortals(mountedRootIdentity)
2784
+ }
2785
+ commitOwnerInsertions(rootOwner)
2786
+ commitRangeRefs(range[0], range[1])
2787
+ commitOwnerResources(rootOwner)
2788
+ if (!hydrating) {
2789
+ for (const node of previous) host.removeChild(node)
2790
+ }
2791
+ } catch (error) {
2792
+ try {
2793
+ range[2][3]()
2794
+ } catch {
2795
+ // Preserve the mount error while still running every component cleanup.
2796
+ }
2797
+ if (isHydrationMismatch(error) || rootIdentity.onUncaughtError === undefined) throw error
2798
+ rootIdentity.onUncaughtError(error)
2799
+ return { dispose: noop }
2800
+ }
2801
+ return {
2802
+ dispose: range[2][3],
2803
+ }
2804
+ }
2805
+
2806
+ export function isCompiledBinding(value: unknown): value is CompiledBinding<unknown> {
2807
+ return Array.isArray(value) && value[0] === BINDING
2808
+ }
2809
+
2810
+ export function isStructuralBinding(value: unknown): value is StructuralBinding {
2811
+ return Array.isArray(value) && value[0] === STRUCTURAL
2812
+ }
2813
+
2814
+ export function isCompiledComponentResult(value: unknown): value is CompiledComponentResult {
2815
+ return isStructuralBinding(value) && componentRanges.has(value)
2816
+ }
2817
+
2818
+ export function mountCompiledBinding(parent: Node, value: CompiledBinding<unknown>): void {
2819
+ mountCompiledBindingBefore(parent, value, null)
2820
+ }
2821
+
2822
+ export function mountCompiledProp(
2823
+ value: CompiledBinding<unknown>,
2824
+ apply: (next: unknown) => void | (() => void),
2825
+ ): void {
2826
+ let current = value[1]()
2827
+ let cleanup = apply(current)
2828
+ const removeUpdater = subscribeBinding(value, () => {
2829
+ const next = value[1]()
2830
+ if (Object.is(next, current)) return
2831
+ const previous = current
2832
+ let nextCleanup: void | (() => void)
2833
+ let committedNext = false
2834
+ stagePublication([
2835
+ () => {
2836
+ try {
2837
+ nextCleanup = apply(next)
2838
+ } catch (error) {
2839
+ try {
2840
+ apply(previous)
2841
+ } catch {
2842
+ // Preserve the setter error that aborted publication.
2843
+ }
2844
+ throw error
2845
+ }
2846
+ committedNext = true
2847
+ cleanup?.()
2848
+ cleanup = nextCleanup
2849
+ current = next
2850
+ },
2851
+ () => {
2852
+ if (!committedNext) return
2853
+ nextCleanup?.()
2854
+ cleanup = apply(previous)
2855
+ current = previous
2856
+ committedNext = false
2857
+ },
2858
+ ])
2859
+ })
2860
+ onCleanup(() => {
2861
+ removeUpdater()
2862
+ cleanup?.()
2863
+ })
2864
+ }
2865
+
2866
+ export function mountCompiledPropTransition<T>(
2867
+ value: CompiledBinding<T>,
2868
+ initialize: (initial: T) => void,
2869
+ prepare: (next: T, previous: T) => CompiledPropTransition | undefined,
2870
+ ): void {
2871
+ let current = value[1]()
2872
+ initialize(current)
2873
+ const removeUpdater = subscribeBinding(value, () => {
2874
+ const next = value[1]()
2875
+ if (Object.is(next, current)) return
2876
+ const previous = current
2877
+ const transition = prepare(next, previous)
2878
+ let attempted = false
2879
+ stagePublication([
2880
+ () => {
2881
+ attempted = true
2882
+ transition?.[0]()
2883
+ current = next
2884
+ },
2885
+ () => {
2886
+ if (!attempted) return
2887
+ try {
2888
+ transition?.[1]()
2889
+ } finally {
2890
+ current = previous
2891
+ attempted = false
2892
+ }
2893
+ },
2894
+ transition?.[2],
2895
+ transition?.[3],
2896
+ transition?.[4],
2897
+ ])
2898
+ })
2899
+ onCleanup(removeUpdater)
2900
+ }
2901
+
2902
+ function structural(
2903
+ scope: CompiledScope,
2904
+ mount: StructuralBinding[1],
2905
+ hydrationKind?: StructuralBinding[2],
2906
+ ): StructuralBinding {
2907
+ if (hydrationKind !== undefined) noteHydrationStructuralParent()
2908
+ let mounted = false
2909
+ const owner = activeOwner ?? scopeOwners.get(scope)!
2910
+ const context = activeContextFrame ?? owner[2]
2911
+ const mountOnce: StructuralBinding[1] = (parent, before) => {
2912
+ if (mounted) throw new Error(DEV ? 'compiled block is already mounted' : 'V008')
2913
+ mounted = true
2914
+ const operation = () =>
2915
+ withOwner(owner, () =>
2916
+ withContextFrame(context, () => withScopeNamespace(scope, () => mount(parent, before))),
2917
+ )
2918
+ if (profilingEnabled) measureProfileWork(owner, 'range', operation)
2919
+ else operation()
2920
+ }
2921
+ return hydrationKind === undefined
2922
+ ? [STRUCTURAL, mountOnce]
2923
+ : [STRUCTURAL, mountOnce, hydrationKind]
2924
+ }
2925
+
2926
+ function structuralRange(
2927
+ parent: Node,
2928
+ before: Node | null,
2929
+ label: string,
2930
+ ): readonly [start: Comment, end: Comment, hydrated: boolean] {
2931
+ const hydrated = claimHydrationSlotRange(parent)
2932
+ if (hydrated !== undefined) return [hydrated[0], hydrated[1], true]
2933
+ const start = document.createComment(DEV ? `vidact:${label}` : '')
2934
+ const end = document.createComment(DEV ? `/vidact:${label}` : '')
2935
+ parent.insertBefore(start, before)
2936
+ parent.insertBefore(end, before)
2937
+ return [start, end, false]
2938
+ }
2939
+
2940
+ function subscribe(
2941
+ scope: CompiledScope,
2942
+ reads: SourceMask,
2943
+ run: () => void,
2944
+ additionalScope?: CompiledScope,
2945
+ additionalReads?: SourceMask,
2946
+ ): () => void {
2947
+ const removers = [scope[0](reads, run)]
2948
+ if (additionalScope !== undefined && additionalReads !== undefined) {
2949
+ removers.push(additionalScope[0](additionalReads, () => withScopeNamespace(scope, run)))
2950
+ }
2951
+ return () => {
2952
+ for (const remove of removers) remove()
2953
+ }
2954
+ }
2955
+
2956
+ function subscribeBinding(compiledBinding: CompiledBinding<unknown>, run: () => void): () => void {
2957
+ return subscribe(
2958
+ compiledBinding[2],
2959
+ compiledBinding[3],
2960
+ run,
2961
+ compiledBinding[4],
2962
+ compiledBinding[5],
2963
+ )
2964
+ }
2965
+
2966
+ function withScopeNamespace<Result>(scope: CompiledScope, operation: () => Result): Result {
2967
+ return withIntrinsicNamespace(scopeNamespaces.get(scope), operation)
2968
+ }
2969
+
2970
+ function createOwner(
2971
+ context = activeContextFrame ?? activeOwner?.[2] ?? null,
2972
+ rootIdentity = activeOwner?.[3] ?? activeRootIdentity ?? createRootIdentity(),
2973
+ boundary = activeOwner?.[4] ?? null,
2974
+ retainedConnection = activeOwner?.[5] ?? null,
2975
+ profileContext = activeOwner?.[6] ?? activeConstructionOwner?.[6] ?? null,
2976
+ ): Owner {
2977
+ if (DEV) {
2978
+ activeOwnerCount += 1
2979
+ createdOwnerCount += 1
2980
+ }
2981
+ let nextProfile = profileContext
2982
+ if (profilingEnabled && activeProfileName !== null && activeConstructionOwner === null) {
2983
+ nextProfile = {
2984
+ frame: {
2985
+ name: activeProfileName,
2986
+ parent: profileContext?.frame ?? null,
2987
+ values: [],
2988
+ },
2989
+ boundary: profileContext?.boundary ?? null,
2990
+ }
2991
+ }
2992
+ if (profilingEnabled) {
2993
+ const owner: Owner = [
2994
+ false,
2995
+ new Set(),
2996
+ context,
2997
+ rootIdentity,
2998
+ boundary,
2999
+ retainedUiEnabled ? retainedConnection : null,
3000
+ nextProfile,
3001
+ ]
3002
+ if (DEV) owner[7] = activeConstructionOwner?.[7] ?? activeProfileName
3003
+ return owner
3004
+ }
3005
+ const owner: Owner = retainedUiEnabled
3006
+ ? [false, new Set(), context, rootIdentity, boundary, retainedConnection]
3007
+ : [false, new Set(), context, rootIdentity, boundary]
3008
+ if (DEV) owner[7] = activeConstructionOwner?.[7] ?? activeProfileName
3009
+ return owner
3010
+ }
3011
+
3012
+ function ensureProfileContext(owner: Owner): ProfileContext {
3013
+ const current = owner[6]
3014
+ if (current?.frame !== null && current?.frame !== undefined) return current
3015
+ const parent =
3016
+ current ?? (activeOwner !== null && activeOwner !== owner ? (activeOwner[6] ?? null) : null)
3017
+ const next = {
3018
+ frame: {
3019
+ name: owner[7] ?? activeProfileName ?? 'Anonymous',
3020
+ parent: parent?.frame ?? null,
3021
+ values: [],
3022
+ },
3023
+ boundary: parent?.boundary ?? null,
3024
+ }
3025
+ owner[6] = next
3026
+ return next
3027
+ }
3028
+
3029
+ function profileNow(): number {
3030
+ return typeof performance === 'undefined' ? Date.now() : performance.now()
3031
+ }
3032
+
3033
+ function measureProfileWork<Result>(
3034
+ owner: Owner,
3035
+ kind: 'effect' | 'range' | 'scheduler' | 'updater',
3036
+ operation: () => Result,
3037
+ contribute = false,
3038
+ ): Result {
3039
+ const started = profileNow()
3040
+ try {
3041
+ return operation()
3042
+ } finally {
3043
+ const finished = profileNow()
3044
+ emitProfileMeasure(owner, kind, started, finished)
3045
+ if (contribute) recordProfileCommit(owner, started, finished)
3046
+ }
3047
+ }
3048
+
3049
+ function emitProfileMeasure(
3050
+ owner: Owner,
3051
+ kind: 'effect' | 'range' | 'scheduler' | 'updater',
3052
+ started: number,
3053
+ finished: number,
3054
+ ): void {
3055
+ if (typeof performance === 'undefined' || typeof performance.measure !== 'function') return
3056
+ const component = owner[6]?.frame?.name ?? 'Anonymous'
3057
+ performance.measure(`vidact.${kind}:${component}`, { start: started, end: finished })
3058
+ }
3059
+
3060
+ function recordProfileCommit(owner: Owner, started: number, finished: number): void {
3061
+ let boundary = owner[6]?.boundary ?? null
3062
+ while (boundary !== null) {
3063
+ queueProfileBoundaryCommit(boundary, owner, started, finished)
3064
+ boundary = boundary.parent
3065
+ }
3066
+ }
3067
+
3068
+ function queueProfileBoundaryCommit(
3069
+ boundary: ProfileBoundary,
3070
+ owner: Owner,
3071
+ started: number,
3072
+ finished: number,
3073
+ ): void {
3074
+ if (!boundary.mounted) return
3075
+ boundary.pendingDuration += finished - started
3076
+ boundary.pendingStart =
3077
+ boundary.pendingStart === 0 ? started : Math.min(boundary.pendingStart, started)
3078
+ if (boundary.scheduled) return
3079
+ boundary.scheduled = true
3080
+ stagePublication([
3081
+ noop,
3082
+ noop,
3083
+ undefined,
3084
+ () => {
3085
+ const actualDuration = boundary.pendingDuration
3086
+ const startTime = boundary.pendingStart
3087
+ boundary.pendingDuration = 0
3088
+ boundary.pendingStart = 0
3089
+ boundary.scheduled = false
3090
+ boundary.onRender(
3091
+ boundary.id,
3092
+ 'update',
3093
+ actualDuration,
3094
+ boundary.baseDuration,
3095
+ startTime,
3096
+ profileNow(),
3097
+ )
3098
+ },
3099
+ 100,
3100
+ owner,
3101
+ ])
3102
+ }
3103
+
3104
+ function readProfiledEffectCleanup(owner: Owner, evaluate: () => EffectResult): () => void {
3105
+ const cleanup = measureProfileWork(owner, 'effect', () => readEffectCleanup(evaluate()))
3106
+ if (cleanup === noop) return cleanup
3107
+ return () => measureProfileWork(owner, 'effect', cleanup)
3108
+ }
3109
+
3110
+ function createRetainedConnection(
3111
+ parent: RetainedConnection | null,
3112
+ visible: boolean,
3113
+ ): RetainedConnection {
3114
+ const connection: RetainedConnection = {
3115
+ parent,
3116
+ children: new Set(),
3117
+ resources: new Set(),
3118
+ deferredFlushes: new Map(),
3119
+ afterFlush: new Set(),
3120
+ visible,
3121
+ connected: visible && (parent?.connected ?? true),
3122
+ disposed: false,
3123
+ }
3124
+ parent?.children.add(connection)
3125
+ return connection
3126
+ }
3127
+
3128
+ function createRetainedResource(
3129
+ owner: Owner,
3130
+ connect: () => void,
3131
+ disconnect: () => void,
3132
+ phase = 1,
3133
+ ): readonly [activate: () => void, dispose: () => void] {
3134
+ const connection = owner[5]
3135
+ const resource: RetainedResource = {
3136
+ owner,
3137
+ connect,
3138
+ disconnect,
3139
+ active: false,
3140
+ connected: false,
3141
+ disposed: false,
3142
+ phase,
3143
+ }
3144
+ connection?.resources.add(resource)
3145
+ const activate = (): void => {
3146
+ if (resource.disposed) return
3147
+ resource.active = true
3148
+ reconcileRetainedResource(resource, connection?.connected ?? true)
3149
+ }
3150
+ const dispose = (): void => {
3151
+ if (resource.disposed) return
3152
+ resource.disposed = true
3153
+ resource.active = false
3154
+ connection?.resources.delete(resource)
3155
+ reconcileRetainedResource(resource, false)
3156
+ }
3157
+ return [activate, dispose]
3158
+ }
3159
+
3160
+ function reconcileRetainedResource(resource: RetainedResource, connected: boolean): void {
3161
+ const nextConnected = !resource.disposed && resource.active && connected
3162
+ if (resource.connected === nextConnected) return
3163
+ resource.connected = nextConnected
3164
+ try {
3165
+ runOwnerTask(resource.owner, nextConnected ? resource.connect : resource.disconnect)
3166
+ } catch (error) {
3167
+ resource.connected = false
3168
+ throw error
3169
+ }
3170
+ }
3171
+
3172
+ function updateRetainedConnection(connection: RetainedConnection): void {
3173
+ if (connection.disposed) return
3174
+ const nextConnected = connection.visible && (connection.parent?.connected ?? true)
3175
+ if (connection.connected === nextConnected) return
3176
+ connection.connected = nextConnected
3177
+ if (nextConnected) {
3178
+ for (const [flush, cancel] of connection.deferredFlushes) {
3179
+ cancel()
3180
+ scheduleFlush(flush)
3181
+ }
3182
+ connection.deferredFlushes.clear()
3183
+ for (const resource of [...connection.resources].toSorted(
3184
+ (left, right) => left.phase - right.phase,
3185
+ )) {
3186
+ reconcileRetainedResource(resource, true)
3187
+ }
3188
+ } else {
3189
+ for (const resource of [...connection.resources].toReversed()) {
3190
+ reconcileRetainedResource(resource, false)
3191
+ }
3192
+ }
3193
+ for (const child of connection.children) updateRetainedConnection(child)
3194
+ }
3195
+
3196
+ function concealDisconnectedDescendants(connection: RetainedConnection): void {
3197
+ for (const child of connection.children) {
3198
+ if (!child.connected) {
3199
+ for (const conceal of child.afterFlush) conceal()
3200
+ }
3201
+ concealDisconnectedDescendants(child)
3202
+ }
3203
+ }
3204
+
3205
+ function disposeRetainedConnection(connection: RetainedConnection): void {
3206
+ if (connection.disposed) return
3207
+ connection.disposed = true
3208
+ connection.parent?.children.delete(connection)
3209
+ for (const cancel of connection.deferredFlushes.values()) cancel()
3210
+ connection.deferredFlushes.clear()
3211
+ connection.afterFlush.clear()
3212
+ for (const child of connection.children) disposeRetainedConnection(child)
3213
+ connection.children.clear()
3214
+ for (const resource of [...connection.resources].toReversed()) {
3215
+ resource.disposed = true
3216
+ resource.active = false
3217
+ reconcileRetainedResource(resource, false)
3218
+ }
3219
+ connection.resources.clear()
3220
+ }
3221
+
3222
+ function notifyRetainedFlush(connection: RetainedConnection | null): void {
3223
+ for (let current = connection; current !== null; current = current.parent) {
3224
+ if (!current.connected) {
3225
+ for (const notify of current.afterFlush) notify()
3226
+ }
3227
+ }
3228
+ }
3229
+
3230
+ function createRootIdentity(
3231
+ identifierPrefix?: string,
3232
+ onCaughtError?: CompiledErrorHandler,
3233
+ onUncaughtError?: CompiledErrorHandler,
3234
+ ): RootIdentity {
3235
+ return {
3236
+ mounted: false,
3237
+ nextId: 0,
3238
+ prefix: identifierPrefix ?? `v${nextClientRoot++}-`,
3239
+ onCaughtError,
3240
+ onUncaughtError,
3241
+ }
3242
+ }
3243
+
3244
+ function commitRootPortals(rootIdentity: RootIdentity): void {
3245
+ const pending = pendingRootPortals.get(rootIdentity)
3246
+ if (pending === undefined) return
3247
+ pendingRootPortals.delete(rootIdentity)
3248
+ const committed: PortalPublication[] = []
3249
+ try {
3250
+ for (const publication of pending) {
3251
+ publication[0]()
3252
+ committed.push(publication)
3253
+ }
3254
+ } catch (error) {
3255
+ for (const publication of committed.toReversed()) publication[1]()
3256
+ throw error
3257
+ }
3258
+ }
3259
+
3260
+ function provideContext<T>(
3261
+ context: CompiledContext<T>,
3262
+ input: T | CompiledBinding<T>,
3263
+ children: CompiledRenderValue | readonly CompiledRenderValue[] | undefined,
3264
+ ): StructuralBinding {
3265
+ const parentContext = activeContextFrame ?? activeOwner?.[2] ?? null
3266
+ let mounted = false
3267
+ return [
3268
+ STRUCTURAL,
3269
+ (parent, before) => {
3270
+ if (mounted) throw new Error(DEV ? 'context provider is already mounted' : 'V008')
3271
+ mounted = true
3272
+ withContextFrame(
3273
+ {
3274
+ context: context as CompiledContext<unknown>,
3275
+ input,
3276
+ parent: parentContext,
3277
+ owner: activeOwner,
3278
+ },
3279
+ () => insertValue(parent, children, before),
3280
+ )
3281
+ },
3282
+ 'transparent',
3283
+ ]
3284
+ }
3285
+
3286
+ function contextInput<T>(
3287
+ scope: CompiledScope,
3288
+ context: CompiledContext<T>,
3289
+ ): T | CompiledBinding<T> {
3290
+ const owner = scopeOwners.get(scope)
3291
+ if (owner === undefined) {
3292
+ throw new Error(DEV ? 'createCompiledContext received an unknown scope' : 'V003')
3293
+ }
3294
+ return contextInputFromFrame(owner[2], context)
3295
+ }
3296
+
3297
+ function contextInputFromFrame<T>(
3298
+ frame: ContextFrame | null,
3299
+ context: CompiledContext<T>,
3300
+ ): T | CompiledBinding<T> {
3301
+ if (typeof context !== 'function' || !(CONTEXT in context)) {
3302
+ throw new TypeError(DEV ? 'useContext requires a context created by createContext' : 'V018')
3303
+ }
3304
+ for (let current = frame; current !== null; current = current.parent) {
3305
+ if (current.context === context) return current.input as T | CompiledBinding<T>
3306
+ }
3307
+ return context[CONTEXT]
3308
+ }
3309
+
3310
+ function withOwner<T>(owner: Owner, operation: () => T): T {
3311
+ const previous = activeOwner
3312
+ const previousContext = activeContextFrame
3313
+ const previousRootIdentity = activeRootIdentity
3314
+ const previousErrorOwner = activeErrorOwner
3315
+ activeOwner = owner
3316
+ activeContextFrame = owner[2]
3317
+ activeRootIdentity = owner[3]
3318
+ activeErrorOwner = owner
3319
+ try {
3320
+ return operation()
3321
+ } finally {
3322
+ activeOwner = previous
3323
+ activeContextFrame = previousContext
3324
+ activeRootIdentity = previousRootIdentity
3325
+ activeErrorOwner = previousErrorOwner
3326
+ }
3327
+ }
3328
+
3329
+ function withContextFrame<T>(frame: ContextFrame | null, operation: () => T): T {
3330
+ const previous = activeContextFrame
3331
+ activeContextFrame = frame
3332
+ try {
3333
+ return operation()
3334
+ } finally {
3335
+ activeContextFrame = previous
3336
+ }
3337
+ }
3338
+
3339
+ function withRootIdentity<T>(rootIdentity: RootIdentity | null, operation: () => T): T {
3340
+ const previous = activeRootIdentity
3341
+ activeRootIdentity = rootIdentity
3342
+ try {
3343
+ return operation()
3344
+ } finally {
3345
+ activeRootIdentity = previous
3346
+ }
3347
+ }
3348
+
3349
+ function onCleanup(cleanup: () => void): void {
3350
+ activeOwner?.[1].add(cleanup)
3351
+ }
3352
+
3353
+ function disposeOwner(owner: Owner): void {
3354
+ if (owner[0]) return
3355
+ owner[0] = true
3356
+ disposalCascadeDepth += 1
3357
+ disposalCascadeOwners.add(owner)
3358
+ if (DEV) activeOwnerCount -= 1
3359
+ pendingInsertionCommits.delete(owner)
3360
+ pendingOwnerCommits.delete(owner)
3361
+ const cleanups = [...owner[1]]
3362
+ owner[1].clear()
3363
+ let firstError: unknown
3364
+ let hasError = false
3365
+ try {
3366
+ for (let index = cleanups.length - 1; index >= 0; index -= 1) {
3367
+ try {
3368
+ cleanups[index]?.()
3369
+ } catch (error) {
3370
+ if (!hasError) firstError = error
3371
+ hasError = true
3372
+ }
3373
+ }
3374
+ } finally {
3375
+ disposalCascadeDepth -= 1
3376
+ if (disposalCascadeDepth === 0) disposalCascadeOwners.clear()
3377
+ }
3378
+ if (hasError) throw firstError
3379
+ }
3380
+
3381
+ /** @internal Test-only allocation and retention evidence. */
3382
+ export function readCompiledOwnerMetrics(): {
3383
+ readonly active: number
3384
+ readonly created: number
3385
+ } {
3386
+ return { active: activeOwnerCount, created: createdOwnerCount }
3387
+ }
3388
+
3389
+ function mountCompiledBindingBefore(
3390
+ parent: Node,
3391
+ value: CompiledBinding<unknown>,
3392
+ before: Node | null,
3393
+ ): void {
3394
+ const unset = Symbol(DEV ? 'Vidact.UnsetBinding' : undefined)
3395
+ const initial = value[1]()
3396
+ const scalarInitial = isScalarRenderValue(initial)
3397
+ const hydratedTextRange = scalarInitial
3398
+ ? claimHydrationTextRange(parent, toText(initial))
3399
+ : undefined
3400
+ const borrowedHydrationRange =
3401
+ isHydrating() && !scalarInitial
3402
+ ? borrowHydrationSlotRange(parent, isStructuralBinding(initial) && initial[2] === 'slot')
3403
+ : undefined
3404
+ const hydratedStructuralRange =
3405
+ isHydrating() && !scalarInitial
3406
+ ? (borrowedHydrationRange ?? claimHydrationSlotRange(parent))
3407
+ : undefined
3408
+ const hydratedRange = hydratedTextRange ?? hydratedStructuralRange
3409
+ const start = hydratedRange?.[0] ?? document.createComment(DEV ? 'vidact:binding' : '')
3410
+ const end = hydratedRange?.[1] ?? document.createComment(DEV ? '/vidact:binding' : '')
3411
+ if (hydratedRange === undefined) {
3412
+ parent.insertBefore(start, before)
3413
+ parent.insertBefore(end, before)
3414
+ }
3415
+ let current: unknown = hydratedTextRange === undefined ? unset : initial
3416
+ let currentOwner: Owner | null = null
3417
+ let text: Text | null = hydratedTextRange?.[2] ?? null
3418
+ const stableRenderable = (renderable: RenderableProtocol): unknown => {
3419
+ const identity = renderableIdentity(renderable)
3420
+ let previous = renderablePropsSnapshot(renderable)
3421
+ const input = binding(
3422
+ value[2],
3423
+ value[3],
3424
+ () => {
3425
+ const next = value[1]()
3426
+ if (!isRenderableProtocol(next) || !Object.is(renderableIdentity(next), identity)) {
3427
+ return previous
3428
+ }
3429
+ previous = renderablePropsSnapshot(next)
3430
+ return previous
3431
+ },
3432
+ value[4],
3433
+ value[5],
3434
+ )
3435
+ return materializeRenderableWithInput(renderable, input)
3436
+ }
3437
+
3438
+ const clear = (): void => {
3439
+ const owner = currentOwner
3440
+ currentOwner = null
3441
+ text = null
3442
+ disposeRange(owner, start, end)
3443
+ }
3444
+ const update = (): void => {
3445
+ const next = value[1]()
3446
+ if (current !== unset && Object.is(next, current)) return
3447
+ if (
3448
+ current !== unset &&
3449
+ isRenderableProtocol(current) &&
3450
+ isRenderableProtocol(next) &&
3451
+ canReconcileRenderable(current) &&
3452
+ canReconcileRenderable(next) &&
3453
+ Object.is(renderableIdentity(current), renderableIdentity(next))
3454
+ ) {
3455
+ current = next
3456
+ return
3457
+ }
3458
+ const currentParent = rangeParent(start, end, 'binding range')
3459
+
3460
+ if (isScalarRenderValue(next)) {
3461
+ const content = toText(next)
3462
+ if (text !== null) {
3463
+ const target = text
3464
+ const previousContent = target.data
3465
+ const previous = current
3466
+ stagePublication([
3467
+ () => {
3468
+ if (target.data !== content) target.data = content
3469
+ current = next
3470
+ },
3471
+ () => {
3472
+ if (target.data !== previousContent) target.data = previousContent
3473
+ current = previous
3474
+ },
3475
+ ])
3476
+ return
3477
+ }
3478
+ clear()
3479
+ text = document.createTextNode(content)
3480
+ currentParent.insertBefore(text, end)
3481
+ current = next
3482
+ return
3483
+ }
3484
+
3485
+ const nextOwner = createOwner()
3486
+ const stagedValue =
3487
+ isRenderableProtocol(next) && canReconcileRenderable(next)
3488
+ ? stableRenderable(next)
3489
+ : (next as RenderValue)
3490
+ const [fragment, staged] = stageValue(stagedValue as RenderValue, nextOwner)
3491
+ try {
3492
+ clear()
3493
+ } catch (error) {
3494
+ disposeOwner(nextOwner)
3495
+ throw error
3496
+ }
3497
+ currentOwner = nextOwner
3498
+ currentParent.insertBefore(fragment, end)
3499
+ commitPublishedNodes(staged)
3500
+ current = next
3501
+ }
3502
+
3503
+ if (hydratedStructuralRange !== undefined) {
3504
+ const nextOwner = createOwner()
3505
+ const [fragment, staged] = withHydrationInsertion(parent, end, () =>
3506
+ stageRender(
3507
+ () =>
3508
+ (isRenderableProtocol(initial) && canReconcileRenderable(initial)
3509
+ ? stableRenderable(initial)
3510
+ : initial) as RenderValue,
3511
+ nextOwner,
3512
+ ),
3513
+ )
3514
+ parent.insertBefore(fragment, end)
3515
+ commitPublishedNodes(staged)
3516
+ currentOwner = nextOwner
3517
+ current = initial
3518
+ } else if (hydratedRange === undefined) {
3519
+ update()
3520
+ }
3521
+ const removeUpdater = subscribeBinding(value, update)
3522
+ onCleanup(() => {
3523
+ removeUpdater()
3524
+ clear()
3525
+ if (borrowedHydrationRange === undefined) {
3526
+ start.remove()
3527
+ end.remove()
3528
+ }
3529
+ })
3530
+ }
3531
+
3532
+ function materialize(value: RenderValue): Node[] {
3533
+ const insertionPoint = hydrationInsertionPoint()
3534
+ if (insertionPoint !== undefined) {
3535
+ const [parent, before] = insertionPoint
3536
+ const first = hydrationCursor(parent)
3537
+ if (first === undefined) throw new Error('hydration insertion point requires active hydration')
3538
+ insertValue(parent, value, before)
3539
+ const after = hydrationCursor(parent)
3540
+ const nodes: Node[] = []
3541
+ for (let node = first; node !== null && node !== after; node = node.nextSibling) {
3542
+ nodes.push(node)
3543
+ }
3544
+ return nodes
3545
+ }
3546
+ const fragment = document.createDocumentFragment()
3547
+ const moves: NodePosition[] = []
3548
+ try {
3549
+ insertValue(fragment, value, null, moves)
3550
+ } catch (error) {
3551
+ restoreNodePositions(moves)
3552
+ throw error
3553
+ }
3554
+ return [...fragment.childNodes]
3555
+ }
3556
+
3557
+ function stageValue(
3558
+ value: RenderValue,
3559
+ owner: Owner,
3560
+ ): readonly [fragment: DocumentFragment, nodes: readonly Node[]] {
3561
+ const insertionPoint = hydrationInsertionPoint()
3562
+ if (insertionPoint !== undefined) {
3563
+ const [parent, before] = insertionPoint
3564
+ const first = hydrationCursor(parent)
3565
+ if (first === undefined) throw new Error('hydration insertion point requires active hydration')
3566
+ withOwner(owner, () => insertValue(parent, value, before))
3567
+ const nodes: Node[] = []
3568
+ for (let node = first; node !== null && node !== before; node = node.nextSibling) {
3569
+ nodes.push(node)
3570
+ }
3571
+ return [document.createDocumentFragment(), nodes]
3572
+ }
3573
+ const fragment = document.createDocumentFragment()
3574
+ const moves: NodePosition[] = []
3575
+ try {
3576
+ withOwner(owner, () => insertValue(fragment, value, null, moves))
3577
+ } catch (error) {
3578
+ restoreNodePositions(moves)
3579
+ try {
3580
+ disposeOwner(owner)
3581
+ } catch {
3582
+ // Preserve the staging error; cleanup still ran every registered disposer.
3583
+ }
3584
+ throw error
3585
+ }
3586
+ return [fragment, [...fragment.childNodes]]
3587
+ }
3588
+
3589
+ function stageRender(
3590
+ render: () => CompiledRenderValue,
3591
+ owner: Owner,
3592
+ ): readonly [fragment: DocumentFragment, nodes: readonly Node[]] {
3593
+ try {
3594
+ return stageValue(withOwner(owner, render), owner)
3595
+ } catch (error) {
3596
+ try {
3597
+ disposeOwner(owner)
3598
+ } catch {
3599
+ // Preserve the render or staging error after every registered cleanup ran.
3600
+ }
3601
+ throw error
3602
+ }
3603
+ }
3604
+
3605
+ function insertValue(
3606
+ parent: Node,
3607
+ value: RenderValue,
3608
+ before: Node | null,
3609
+ moves?: NodePosition[],
3610
+ ): void {
3611
+ if (value === null || value === undefined || typeof value === 'boolean') {
3612
+ claimHydrationText(parent, '')
3613
+ return
3614
+ }
3615
+ if (isStructuralBinding(value)) {
3616
+ if (isHydrating() && !isCompiledComponentResult(value) && value[2] === undefined) {
3617
+ throw new HydrationMismatch('structural binding hydration markers are not available')
3618
+ }
3619
+ adoptCompiledRoot(value)
3620
+ value[1](parent, before)
3621
+ return
3622
+ }
3623
+ if (isCompiledBinding(value)) {
3624
+ mountCompiledBindingBefore(parent, value, before)
3625
+ return
3626
+ }
3627
+ if (isRenderableProtocol(value)) {
3628
+ insertValue(parent, materializeRenderable(value) as RenderValue, before, moves)
3629
+ return
3630
+ }
3631
+ if (Array.isArray(value)) {
3632
+ if (hasInvalidChild(value)) {
3633
+ throw new TypeError(
3634
+ DEV ? 'unsupported compiled child value; expected a DOM node or owned block' : 'V009',
3635
+ )
3636
+ }
3637
+ const hydratedRange = claimHydrationArrayRange(parent)
3638
+ for (const item of value) insertValue(parent, item, before, moves)
3639
+ if (hydratedRange !== undefined) finishHydrationArrayRange(parent, hydratedRange[1])
3640
+ return
3641
+ }
3642
+ if (value instanceof DocumentFragment) {
3643
+ const hydrationChildren = hydrationFragmentChildren(value)
3644
+ if (hydrationChildren !== undefined) {
3645
+ for (const child of hydrationChildren) {
3646
+ insertValue(parent, child as RenderValue, before, moves)
3647
+ }
3648
+ return
3649
+ }
3650
+ // oxlint-disable-next-line unicorn/no-useless-spread -- Snapshot the live NodeList before moving nodes.
3651
+ for (const child of [...value.childNodes]) insertValue(parent, child, before, moves)
3652
+ return
3653
+ }
3654
+ if (value instanceof Node) {
3655
+ moves?.push([value, value.parentNode, value.nextSibling])
3656
+ adoptCompiledRoot(value)
3657
+ claimPendingRefOwners(value)
3658
+ if (!claimHydrationNode(parent, value)) parent.insertBefore(value, before)
3659
+ if (!(parent instanceof DocumentFragment)) commitPendingRefs(value)
3660
+ return
3661
+ }
3662
+ if (typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol') {
3663
+ throw new TypeError(
3664
+ DEV ? 'unsupported compiled child value; expected a DOM node or owned block' : 'V009',
3665
+ )
3666
+ }
3667
+ const content = String(value)
3668
+ if (claimHydrationText(parent, content) === undefined) {
3669
+ parent.insertBefore(document.createTextNode(content), before)
3670
+ }
3671
+ }
3672
+
3673
+ function restoreNodePositions(positions: readonly NodePosition[]): void {
3674
+ for (let index = positions.length - 1; index >= 0; index -= 1) {
3675
+ const position = positions[index]
3676
+ if (position === undefined) continue
3677
+ if (position[1] === null) {
3678
+ position[0].parentNode?.removeChild(position[0])
3679
+ continue
3680
+ }
3681
+ const before = position[2]?.parentNode === position[1] ? position[2] : null
3682
+ position[1].insertBefore(position[0], before)
3683
+ }
3684
+ }
3685
+
3686
+ function removeBetween(start: Node, end: Node): void {
3687
+ let node = start.nextSibling
3688
+ while (node !== null && node !== end) {
3689
+ const next = node.nextSibling
3690
+ node.parentNode?.removeChild(node)
3691
+ node = next
3692
+ }
3693
+ }
3694
+
3695
+ function nodesBetween(start: Node, end: Node): Node[] {
3696
+ const nodes: Node[] = []
3697
+ for (let node = start.nextSibling; node !== null && node !== end; node = node.nextSibling) {
3698
+ nodes.push(node)
3699
+ }
3700
+ return nodes
3701
+ }
3702
+
3703
+ function rangeParent(start: Node, end: Node, description: string): Node {
3704
+ const parent = end.parentNode
3705
+ if (parent === null || start.parentNode !== parent) {
3706
+ throw new Error(DEV ? `cannot update a detached ${description}` : 'V010')
3707
+ }
3708
+ return parent
3709
+ }
3710
+
3711
+ function disposeRange(owner: Owner | null, start: Node, end: Node): void {
3712
+ try {
3713
+ if (owner !== null) disposeOwner(owner)
3714
+ } finally {
3715
+ removeBetween(start, end)
3716
+ }
3717
+ }
3718
+
3719
+ function disposePublished(owner: Owner | null, nodes: readonly Node[]): void {
3720
+ try {
3721
+ if (owner !== null) disposeOwner(owner)
3722
+ } finally {
3723
+ for (const node of nodes) node.parentNode?.removeChild(node)
3724
+ }
3725
+ }
3726
+
3727
+ function toText(value: unknown): string {
3728
+ return value === null || value === undefined || typeof value === 'boolean' ? '' : String(value)
3729
+ }
3730
+
3731
+ function isScalarRenderValue(
3732
+ value: unknown,
3733
+ ): value is string | number | bigint | boolean | null | undefined {
3734
+ return (
3735
+ value === null ||
3736
+ value === undefined ||
3737
+ typeof value === 'string' ||
3738
+ typeof value === 'number' ||
3739
+ typeof value === 'bigint' ||
3740
+ typeof value === 'boolean'
3741
+ )
3742
+ }
3743
+
3744
+ function isRefValue(value: unknown): value is RefValue {
3745
+ return (
3746
+ value === null ||
3747
+ value === undefined ||
3748
+ typeof value === 'function' ||
3749
+ (typeof value === 'object' && value !== null && 'current' in value)
3750
+ )
3751
+ }
3752
+
3753
+ function claimPendingRefOwners(root: Node): void {
3754
+ visitNodes(root, (node) => {
3755
+ if (!(node instanceof Element)) return
3756
+ const pending = pendingRefs.get(node)
3757
+ if (pending !== undefined && pending[0] === null) pending[0] = activeOwner
3758
+ })
3759
+ }
3760
+
3761
+ function commitPendingRefs(root: Node): void {
3762
+ if (!isHydrating()) commitPublishedNodesNow([root])
3763
+ }
3764
+
3765
+ function commitPublishedNodes(nodes: readonly Node[]): void {
3766
+ if (isHydrating()) return
3767
+ commitPublishedNodesNow(nodes)
3768
+ }
3769
+
3770
+ function commitPublishedNodesNow(nodes: readonly Node[]): void {
3771
+ const publicationRoot = nodes[0]?.getRootNode()
3772
+ if (publicationRoot instanceof DocumentFragment && !(publicationRoot instanceof ShadowRoot)) {
3773
+ return
3774
+ }
3775
+ for (const node of nodes) commitNodeInsertions(node)
3776
+ for (const node of nodes) commitNodeRefs(node)
3777
+ for (const node of nodes) commitNodeResources(node)
3778
+ }
3779
+
3780
+ function commitNodeInsertions(root: Node): void {
3781
+ visitNodes(root, (node) => {
3782
+ if (node instanceof Comment) commitOwnerInsertions(componentCommitOwners.get(node))
3783
+ })
3784
+ }
3785
+
3786
+ function commitNodeRefs(root: Node): void {
3787
+ visitNodes(root, (node) => {
3788
+ if (node instanceof Element) {
3789
+ const pending = pendingRefs.get(node)
3790
+ if (pending !== undefined) {
3791
+ pendingRefs.delete(node)
3792
+ const owner = pending[0] ?? activeOwner
3793
+ commitContextInsertions(owner?.[2] ?? null)
3794
+ commitOwnerInsertions(owner ?? undefined)
3795
+ const cleanup = attachRef(pending[1], node)
3796
+ if (pending[2] === undefined) owner?.[1].add(cleanup)
3797
+ else pending[2](cleanup)
3798
+ }
3799
+ }
3800
+ })
3801
+ }
3802
+
3803
+ function commitContextInsertions(frame: ContextFrame | null): void {
3804
+ if (frame === null) return
3805
+ commitContextInsertions(frame.parent)
3806
+ commitOwnerInsertions(frame.owner ?? undefined)
3807
+ }
3808
+
3809
+ function commitNodeResources(root: Node): void {
3810
+ visitNodes(root, (node) => {
3811
+ if (node instanceof Comment) commitOwnerResources(componentCommitOwners.get(node))
3812
+ })
3813
+ }
3814
+
3815
+ function commitOwnerInsertions(owner: Owner | undefined): void {
3816
+ if (owner === undefined || owner[0]) return
3817
+ const commits = pendingInsertionCommits.get(owner)
3818
+ if (commits === undefined) return
3819
+ pendingInsertionCommits.delete(owner)
3820
+ for (const commit of commits) commit()
3821
+ }
3822
+
3823
+ function commitOwnerResources(owner: Owner | undefined): void {
3824
+ if (owner === undefined || owner[0]) return
3825
+ const commits = pendingOwnerCommits.get(owner)
3826
+ if (commits === undefined) return
3827
+ pendingOwnerCommits.delete(owner)
3828
+ for (const commit of commits) commit()
3829
+ }
3830
+
3831
+ function commitRangeRefs(start: Node, end: Node): void {
3832
+ const nodes: Node[] = []
3833
+ let node = start.nextSibling
3834
+ while (node !== null && node !== end) {
3835
+ nodes.push(node)
3836
+ node = node.nextSibling
3837
+ }
3838
+ commitPublishedNodesNow(nodes)
3839
+ }
3840
+
3841
+ function visitNodes(root: Node, visit: (node: Node) => void): void {
3842
+ visit(root)
3843
+ for (const child of root.childNodes) visitNodes(child, visit)
3844
+ }
3845
+
3846
+ function attachRef<T>(value: RefValue<T>, target: T): () => void {
3847
+ if (value === null || value === undefined) return noop
3848
+ if (typeof value === 'function') {
3849
+ const cleanup = value(target)
3850
+ return typeof cleanup === 'function' ? cleanup : () => value(null)
3851
+ }
3852
+ value.current = target
3853
+ return () => {
3854
+ if (value.current === target) value.current = null
3855
+ }
3856
+ }
3857
+
3858
+ function scheduleFlush(flush: () => void): void {
3859
+ scheduledFlushes.add(flush)
3860
+ if (transactionDepth === 0) drainFlushes()
3861
+ }
3862
+
3863
+ function scheduleOwnerFlush(owner: Owner, flush: () => void): void {
3864
+ const connection = owner[5]
3865
+ if (connection === null || connection === undefined || connection.connected) {
3866
+ scheduleFlush(flush)
3867
+ return
3868
+ }
3869
+ if (connection.deferredFlushes.has(flush)) return
3870
+ const cancel = scheduleDeferredTask(() => {
3871
+ connection.deferredFlushes.delete(flush)
3872
+ scheduleFlush(flush)
3873
+ })
3874
+ connection.deferredFlushes.set(flush, cancel)
3875
+ }
3876
+
3877
+ function drainFlushes(): void {
3878
+ if (drainingFlushes || transactionDepth > 0) return
3879
+ drainingFlushes = true
3880
+ const publication: PublicationOperation[] = []
3881
+ activePublication = publication
3882
+ const runs = new Map<() => void, number>()
3883
+ let committing = false
3884
+ let failure: unknown
3885
+ let failed = false
3886
+ try {
3887
+ while (scheduledFlushes.size > 0) {
3888
+ const flush = scheduledFlushes.values().next().value
3889
+ if (flush === undefined) break
3890
+ scheduledFlushes.delete(flush)
3891
+ const runCount = (runs.get(flush) ?? 0) + 1
3892
+ if (runCount > MAX_FLUSH_PASSES) {
3893
+ scheduledFlushes.clear()
3894
+ throw new Error(DEV ? 'Vidact compiled scopes did not stabilize' : 'V011')
3895
+ }
3896
+ runs.set(flush, runCount)
3897
+ flush()
3898
+ }
3899
+ activePublication = null
3900
+ committing = true
3901
+ commitPublication(publication)
3902
+ } catch (error) {
3903
+ activePublication = null
3904
+ if (!committing) abortPublication(publication)
3905
+ failure = error
3906
+ failed = true
3907
+ } finally {
3908
+ activePublication = null
3909
+ drainingFlushes = false
3910
+ }
3911
+ if (failed) {
3912
+ const owner = failedOwner
3913
+ failedOwner = null
3914
+ if (!routeOwnerError(owner, failure)) throw failure
3915
+ }
3916
+ if (scheduledFlushes.size > 0) drainFlushes()
3917
+ }
3918
+
3919
+ function stagePublication(operation: PublicationOperation): void {
3920
+ if (activePublication === null) {
3921
+ operation[0]()
3922
+ try {
3923
+ operation[3]?.()
3924
+ } catch (error) {
3925
+ operation[1]()
3926
+ throw error
3927
+ }
3928
+ return
3929
+ }
3930
+ activePublication.push([
3931
+ operation[0],
3932
+ operation[1],
3933
+ operation[2],
3934
+ operation[3],
3935
+ operation[4],
3936
+ operation[5] ?? activeErrorOwner,
3937
+ ])
3938
+ }
3939
+
3940
+ function commitPublication(operations: readonly PublicationOperation[]): void {
3941
+ const ordered = operations.some((operation) => operation[4] !== undefined)
3942
+ ? operations.toSorted((left, right) => (left[4] ?? 0) - (right[4] ?? 0))
3943
+ : operations
3944
+ const applied: PublicationOperation[] = []
3945
+ let current: PublicationOperation | undefined
3946
+ try {
3947
+ for (const operation of ordered) {
3948
+ current = operation
3949
+ applied.push(operation)
3950
+ operation[0]()
3951
+ }
3952
+ } catch (error) {
3953
+ for (let index = applied.length - 1; index >= 0; index -= 1) {
3954
+ try {
3955
+ applied[index]?.[1]()
3956
+ } catch {
3957
+ // Preserve the publication error while attempting every inverse.
3958
+ }
3959
+ }
3960
+ for (const operation of ordered.slice(applied.length)) {
3961
+ try {
3962
+ operation[2]?.()
3963
+ } catch {
3964
+ // Preserve the publication error while disposing every staged value.
3965
+ }
3966
+ }
3967
+ failedOwner = current?.[5] ?? null
3968
+ throw error
3969
+ }
3970
+ try {
3971
+ for (const operation of ordered) {
3972
+ current = operation
3973
+ operation[3]?.()
3974
+ }
3975
+ } catch (error) {
3976
+ for (let index = applied.length - 1; index >= 0; index -= 1) {
3977
+ try {
3978
+ applied[index]?.[1]()
3979
+ } catch {
3980
+ // Preserve the finalization error while attempting every inverse.
3981
+ }
3982
+ }
3983
+ failedOwner = current?.[5] ?? null
3984
+ throw error
3985
+ }
3986
+ }
3987
+
3988
+ function runOwnerTask(owner: Owner, operation: () => void): void {
3989
+ try {
3990
+ operation()
3991
+ } catch (error) {
3992
+ if (!routeOwnerError(owner, error)) throw error
3993
+ }
3994
+ }
3995
+
3996
+ function routeOwnerError(owner: Owner | null, failure: unknown): boolean {
3997
+ let error = failure
3998
+ let boundary = owner?.[4] ?? null
3999
+ while (boundary !== null) {
4000
+ try {
4001
+ boundary.handle(error)
4002
+ return true
4003
+ } catch (nextError) {
4004
+ error = nextError
4005
+ boundary = boundary.parent
4006
+ }
4007
+ }
4008
+ const onUncaughtError = owner?.[3].onUncaughtError
4009
+ if (onUncaughtError === undefined) return false
4010
+ onUncaughtError(error)
4011
+ return true
4012
+ }
4013
+
4014
+ export function isCompiledContext<Value>(value: unknown): value is CompiledContext<Value> {
4015
+ return typeof value === 'function' && CONTEXT in value
4016
+ }
4017
+
4018
+ function abortPublication(operations: readonly PublicationOperation[]): void {
4019
+ for (let index = operations.length - 1; index >= 0; index -= 1) {
4020
+ try {
4021
+ operations[index]?.[2]?.()
4022
+ } catch {
4023
+ // Preserve the computation error while disposing every staged value.
4024
+ }
4025
+ }
4026
+ }