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

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