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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +1 -1
  3. package/dist/actions.d.ts +32 -30
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +251 -266
  6. package/dist/actions.js.map +1 -1
  7. package/dist/async.d.ts +20 -17
  8. package/dist/async.d.ts.map +1 -1
  9. package/dist/async.js +18 -17
  10. package/dist/async.js.map +1 -1
  11. package/dist/compiled/async-resource.d.ts +9 -6
  12. package/dist/compiled/async-resource.d.ts.map +1 -1
  13. package/dist/compiled/async-resource.js +69 -77
  14. package/dist/compiled/async-resource.js.map +1 -1
  15. package/dist/compiled/core.d.ts +136 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +2672 -2985
  18. package/dist/compiled/core.js.map +1 -1
  19. package/dist/compiled/types.d.ts +44 -56
  20. package/dist/compiled/types.d.ts.map +1 -1
  21. package/dist/compiled/types.js +10 -6
  22. package/dist/compiled/types.js.map +1 -1
  23. package/dist/compiled/validation.d.ts +4 -1
  24. package/dist/compiled/validation.d.ts.map +1 -1
  25. package/dist/compiled/validation.js +17 -20
  26. package/dist/compiled/validation.js.map +1 -1
  27. package/dist/component-spread.d.ts +5 -3
  28. package/dist/component-spread.d.ts.map +1 -1
  29. package/dist/component-spread.js +49 -67
  30. package/dist/component-spread.js.map +1 -1
  31. package/dist/concurrent.d.ts +20 -18
  32. package/dist/concurrent.d.ts.map +1 -1
  33. package/dist/concurrent.js +191 -210
  34. package/dist/concurrent.js.map +1 -1
  35. package/dist/direct-dom.d.ts +17 -12
  36. package/dist/direct-dom.d.ts.map +1 -1
  37. package/dist/direct-dom.js +187 -221
  38. package/dist/direct-dom.js.map +1 -1
  39. package/dist/dom/attributes.d.ts +9 -6
  40. package/dist/dom/attributes.d.ts.map +1 -1
  41. package/dist/dom/attributes.js +65 -47
  42. package/dist/dom/attributes.js.map +1 -1
  43. package/dist/dom/events.d.ts +5 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +53 -65
  46. package/dist/dom/events.js.map +1 -1
  47. package/dist/dom/forms.d.ts +9 -6
  48. package/dist/dom/forms.d.ts.map +1 -1
  49. package/dist/dom/forms.js +152 -198
  50. package/dist/dom/forms.js.map +1 -1
  51. package/dist/dom/intrinsic.d.ts +17 -15
  52. package/dist/dom/intrinsic.d.ts.map +1 -1
  53. package/dist/dom/intrinsic.js +48 -50
  54. package/dist/dom/intrinsic.js.map +1 -1
  55. package/dist/dom/namespace.d.ts +4 -1
  56. package/dist/dom/namespace.d.ts.map +1 -1
  57. package/dist/dom/namespace.js +25 -28
  58. package/dist/dom/namespace.js.map +1 -1
  59. package/dist/dom/namespaces.d.ts +11 -8
  60. package/dist/dom/namespaces.d.ts.map +1 -1
  61. package/dist/dom/namespaces.js +14 -12
  62. package/dist/dom/namespaces.js.map +1 -1
  63. package/dist/dom/properties.d.ts +18 -16
  64. package/dist/dom/properties.d.ts.map +1 -1
  65. package/dist/dom/properties.js +273 -296
  66. package/dist/dom/properties.js.map +1 -1
  67. package/dist/dom/style-properties.d.ts +4 -1
  68. package/dist/dom/style-properties.d.ts.map +1 -1
  69. package/dist/dom/style-properties.js +49 -45
  70. package/dist/dom/style-properties.js.map +1 -1
  71. package/dist/dom/styles.d.ts +5 -2
  72. package/dist/dom/styles.d.ts.map +1 -1
  73. package/dist/dom/styles.js +47 -67
  74. package/dist/dom/styles.js.map +1 -1
  75. package/dist/dom-forms.d.ts +4 -1
  76. package/dist/dom-forms.d.ts.map +1 -1
  77. package/dist/dom-forms.js +16 -13
  78. package/dist/dom-forms.js.map +1 -1
  79. package/dist/dom-styles.d.ts +4 -1
  80. package/dist/dom-styles.d.ts.map +1 -1
  81. package/dist/dom-styles.js +10 -7
  82. package/dist/dom-styles.js.map +1 -1
  83. package/dist/framework-hydrate.d.ts +20 -17
  84. package/dist/framework-hydrate.d.ts.map +1 -1
  85. package/dist/framework-hydrate.js +128 -146
  86. package/dist/framework-hydrate.js.map +1 -1
  87. package/dist/framework-protocol.d.ts +27 -25
  88. package/dist/framework-protocol.d.ts.map +1 -1
  89. package/dist/framework-protocol.js +173 -211
  90. package/dist/framework-protocol.js.map +1 -1
  91. package/dist/framework.d.ts +34 -32
  92. package/dist/framework.d.ts.map +1 -1
  93. package/dist/framework.js +74 -93
  94. package/dist/framework.js.map +1 -1
  95. package/dist/hydrate.d.ts +11 -8
  96. package/dist/hydrate.d.ts.map +1 -1
  97. package/dist/hydrate.js +13 -9
  98. package/dist/hydrate.js.map +1 -1
  99. package/dist/hydration-bridge.d.ts +70 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +77 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +54 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +450 -463
  106. package/dist/hydration.js.map +1 -1
  107. package/dist/index.d.ts +13 -13
  108. package/dist/index.js +11 -13
  109. package/dist/indexed-list.d.ts +8 -5
  110. package/dist/indexed-list.d.ts.map +1 -1
  111. package/dist/indexed-list.js +14 -7
  112. package/dist/indexed-list.js.map +1 -1
  113. package/dist/jsx-runtime.d.ts +10 -8
  114. package/dist/jsx-runtime.d.ts.map +1 -1
  115. package/dist/jsx-runtime.js +28 -23
  116. package/dist/jsx-runtime.js.map +1 -1
  117. package/dist/keyed-list.d.ts +13 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +132 -160
  120. package/dist/keyed-list.js.map +1 -1
  121. package/dist/metadata.d.ts +6 -3
  122. package/dist/metadata.d.ts.map +1 -1
  123. package/dist/metadata.js +131 -164
  124. package/dist/metadata.js.map +1 -1
  125. package/dist/nested-prop.d.ts +4 -2
  126. package/dist/nested-prop.d.ts.map +1 -1
  127. package/dist/nested-prop.js +17 -16
  128. package/dist/nested-prop.js.map +1 -1
  129. package/dist/profiling.d.ts +11 -8
  130. package/dist/profiling.d.ts.map +1 -1
  131. package/dist/profiling.js +17 -17
  132. package/dist/profiling.js.map +1 -1
  133. package/dist/protocol.d.ts +5 -2
  134. package/dist/protocol.d.ts.map +1 -1
  135. package/dist/protocol.js +8 -5
  136. package/dist/protocol.js.map +1 -1
  137. package/dist/raw-html.d.ts +5 -2
  138. package/dist/raw-html.d.ts.map +1 -1
  139. package/dist/raw-html.js +86 -146
  140. package/dist/raw-html.js.map +1 -1
  141. package/dist/ref.d.ts +8 -3
  142. package/dist/ref.d.ts.map +1 -1
  143. package/dist/ref.js +10 -2
  144. package/dist/ref.js.map +1 -1
  145. package/dist/renderable-protocol.d.ts +26 -8
  146. package/dist/renderable-protocol.d.ts.map +1 -1
  147. package/dist/renderable-protocol.js +30 -9
  148. package/dist/renderable-protocol.js.map +1 -1
  149. package/dist/renderable.d.ts +27 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +141 -133
  152. package/dist/renderable.js.map +1 -1
  153. package/dist/retained-ui.d.ts +9 -6
  154. package/dist/retained-ui.d.ts.map +1 -1
  155. package/dist/retained-ui.js +12 -10
  156. package/dist/retained-ui.js.map +1 -1
  157. package/dist/root.d.ts +18 -15
  158. package/dist/root.d.ts.map +1 -1
  159. package/dist/root.js +73 -85
  160. package/dist/root.js.map +1 -1
  161. package/dist/scheduler.d.ts +10 -8
  162. package/dist/scheduler.d.ts.map +1 -1
  163. package/dist/scheduler.js +97 -105
  164. package/dist/scheduler.js.map +1 -1
  165. package/dist/server-framework.d.ts +45 -42
  166. package/dist/server-framework.d.ts.map +1 -1
  167. package/dist/server-framework.js +209 -217
  168. package/dist/server-framework.js.map +1 -1
  169. package/dist/server.d.ts +130 -128
  170. package/dist/server.d.ts.map +1 -1
  171. package/dist/server.js +603 -710
  172. package/dist/server.js.map +1 -1
  173. package/dist/shared/promise.d.ts +6 -3
  174. package/dist/shared/promise.d.ts.map +1 -1
  175. package/dist/shared/promise.js +21 -19
  176. package/dist/shared/promise.js.map +1 -1
  177. package/dist/source-mask.d.ts +9 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +45 -63
  180. package/dist/source-mask.js.map +1 -1
  181. package/dist/spread.d.ts +5 -2
  182. package/dist/spread.d.ts.map +1 -1
  183. package/dist/spread.js +79 -99
  184. package/dist/spread.js.map +1 -1
  185. package/dist/state-slot.d.ts +18 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +43 -42
  188. package/dist/state-slot.js.map +1 -1
  189. package/dist/testing.d.ts +9 -6
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +16 -12
  192. package/dist/testing.js.map +1 -1
  193. package/package.json +8 -7
  194. package/src/actions.ts +415 -0
  195. package/src/async.ts +78 -0
  196. package/src/compiled/async-resource.ts +113 -0
  197. package/src/compiled/core.ts +4041 -0
  198. package/src/compiled/types.ts +96 -0
  199. package/src/compiled/validation.ts +23 -0
  200. package/src/component-spread.ts +86 -0
  201. package/src/concurrent.ts +310 -0
  202. package/src/direct-dom.ts +365 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +93 -0
  205. package/src/dom/forms.ts +220 -0
  206. package/src/dom/intrinsic.ts +94 -0
  207. package/src/dom/namespace.ts +30 -0
  208. package/src/dom/namespaces.ts +14 -0
  209. package/src/dom/properties.ts +355 -0
  210. package/src/dom/style-properties.ts +46 -0
  211. package/src/dom/styles.ts +86 -0
  212. package/src/dom-forms.ts +23 -0
  213. package/src/dom-styles.ts +11 -0
  214. package/src/env.d.ts +6 -0
  215. package/src/framework-hydrate.ts +224 -0
  216. package/src/framework-protocol.ts +335 -0
  217. package/src/framework.ts +161 -0
  218. package/src/hydrate.ts +38 -0
  219. package/src/hydration-bridge.ts +196 -0
  220. package/src/hydration.ts +642 -0
  221. package/src/index.ts +92 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +233 -0
  225. package/src/metadata.ts +212 -0
  226. package/src/nested-prop.ts +27 -0
  227. package/src/profiling.ts +43 -0
  228. package/src/protocol.ts +8 -0
  229. package/src/raw-html.ts +206 -0
  230. package/src/ref.ts +13 -0
  231. package/src/renderable-protocol.ts +63 -0
  232. package/src/renderable.ts +283 -0
  233. package/src/retained-ui.ts +19 -0
  234. package/src/root.ts +122 -0
  235. package/src/scheduler.ts +136 -0
  236. package/src/server-framework.ts +397 -0
  237. package/src/server.ts +1160 -0
  238. package/src/shared/promise.ts +28 -0
  239. package/src/source-mask.ts +83 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +76 -0
  242. package/src/testing.ts +27 -0
  243. package/dist/index.d.ts.map +0 -1
  244. package/dist/index.js.map +0 -1
package/src/server.ts ADDED
@@ -0,0 +1,1160 @@
1
+ // oxlint-disable-next-line typescript/triple-slash-reference -- Include compiler feature defines.
2
+ /// <reference path="./env.d.ts" />
3
+
4
+ import type { JSX as ReactJSX } from 'react'
5
+
6
+ import {
7
+ LINK_IDENTITY_PROPS,
8
+ META_IDENTITY_PROPS,
9
+ SERVER_BOOLEAN_ATTRIBUTES,
10
+ VALID_ATTRIBUTE_NAME,
11
+ serverHtmlAttributeName,
12
+ } from './dom/attributes.ts'
13
+ import { UNITLESS_STYLE_PROPERTIES } from './dom/style-properties.ts'
14
+ import type { PreinitOptions, PreloadOptions, ResourceHintOptions } from './framework.ts'
15
+ import { isPromiseLike } from './shared/promise.ts'
16
+
17
+ const SERVER_NODE = Symbol.for('vidact.v1.ServerNode')
18
+ const SERVER_NODE_KIND = Symbol.for('vidact.v1.ServerNodeKind')
19
+ const SERVER_CONTEXT = Symbol('Vidact.ServerContext')
20
+ const SERVER_ASYNC_RESOURCE = Symbol('Vidact.ServerAsyncResource')
21
+ const SERVER_SUSPENSION = Symbol('Vidact.ServerSuspension')
22
+ const SERVER_RENDERABLE = Symbol.for('vidact.v1.ServerRenderable')
23
+
24
+ export interface ServerNode {
25
+ readonly [SERVER_NODE]: (context: RenderContext) => string
26
+ readonly [SERVER_NODE_KIND]: 'component' | 'intrinsic' | 'transparent'
27
+ }
28
+
29
+ export type ServerChild =
30
+ | ServerNode
31
+ | ServerRenderable
32
+ | string
33
+ | number
34
+ | bigint
35
+ | boolean
36
+ | null
37
+ | undefined
38
+ | readonly ServerChild[]
39
+
40
+ export type ServerComponent = (props: Record<string, unknown>) => ServerChild
41
+ export type ServerElementType = string | typeof Fragment | ServerComponent | ServerRenderable
42
+ export type ServerProps = Record<string, unknown> | null
43
+
44
+ type ServerRenderable = {
45
+ readonly props: Record<string, unknown>
46
+ readonly [SERVER_RENDERABLE]: {
47
+ readonly input: Record<string, unknown>
48
+ readonly construct: (input: Record<string, unknown>) => ServerChild
49
+ }
50
+ }
51
+
52
+ type ServerIntrinsicElements = {
53
+ [Name in keyof ReactJSX.IntrinsicElements]: Omit<ReactJSX.IntrinsicElements[Name], 'children'> & {
54
+ readonly children?: ServerChild
55
+ }
56
+ }
57
+
58
+ export namespace JSX {
59
+ export type Element = ServerNode
60
+ export type ElementType = keyof IntrinsicElements | ServerComponent | ServerRenderable
61
+
62
+ export interface ElementChildrenAttribute extends ReactJSX.ElementChildrenAttribute {}
63
+
64
+ export interface IntrinsicAttributes extends ReactJSX.IntrinsicAttributes {}
65
+
66
+ export type IntrinsicElements = ServerIntrinsicElements
67
+ }
68
+
69
+ export function createRenderable(
70
+ input: Record<string, unknown>,
71
+ construct: (input: Record<string, unknown>) => ServerChild,
72
+ ): ServerRenderable {
73
+ return {
74
+ props: input,
75
+ [SERVER_RENDERABLE]: { input, construct },
76
+ }
77
+ }
78
+
79
+ export function isRenderable(value: unknown): value is ServerRenderable {
80
+ return (
81
+ typeof value === 'object' &&
82
+ value !== null &&
83
+ Object.hasOwn(value, SERVER_RENDERABLE) &&
84
+ typeof (value as ServerRenderable)[SERVER_RENDERABLE]?.construct === 'function'
85
+ )
86
+ }
87
+
88
+ export function renderableToArray(value: unknown): [ServerRenderable] {
89
+ if (!isRenderable(value)) throw new TypeError('expected a compiled server renderable')
90
+ return [value]
91
+ }
92
+
93
+ export function renderableMarker(value: unknown): undefined {
94
+ if (!isRenderable(value)) throw new TypeError('expected a compiled server renderable')
95
+ return undefined
96
+ }
97
+
98
+ export function cloneRenderable(
99
+ value: unknown,
100
+ overrides: Record<string, unknown> | null = {},
101
+ childrenOverride?: ServerChild,
102
+ ): ServerChild {
103
+ if (!isRenderable(value)) throw new TypeError('expected a compiled server renderable')
104
+ const renderable = value[SERVER_RENDERABLE]
105
+ const input = { ...renderable.input, ...overrides }
106
+ if (arguments.length >= 3) input.children = childrenOverride
107
+ return renderable.construct(input)
108
+ }
109
+
110
+ export function cloneRenderableComponent(props: Record<string, unknown>): ServerChild {
111
+ if (Object.hasOwn(props, 'childrenOverride')) {
112
+ return cloneRenderable(
113
+ props.value,
114
+ props.overrides as Record<string, unknown> | null | undefined,
115
+ props.childrenOverride as ServerChild,
116
+ )
117
+ }
118
+ return cloneRenderable(props.value, props.overrides as Record<string, unknown> | null | undefined)
119
+ }
120
+
121
+ export function keyedFragmentComponent(props: Record<string, unknown>): ServerChild {
122
+ return props.children as ServerChild
123
+ }
124
+
125
+ export function renderableProps(input: Record<string, unknown>): Record<string, unknown> {
126
+ return Object.fromEntries(
127
+ Object.entries(input).filter(([name]) => !['children', 'key', 'ref'].includes(name)),
128
+ )
129
+ }
130
+
131
+ export function renderableChildren(input: Record<string, unknown>): ServerChild {
132
+ return input.children as ServerChild
133
+ }
134
+
135
+ export function renderableRef(_input: Record<string, unknown>): undefined {
136
+ return undefined
137
+ }
138
+
139
+ export function forwardedRef(_props: Record<string, unknown>): undefined {
140
+ return undefined
141
+ }
142
+
143
+ export function dynamicIntrinsicComponent(props: Record<string, unknown>): ServerNode {
144
+ if (typeof props.tag !== 'string') {
145
+ throw new TypeError('dynamic intrinsic construction requires a string tag')
146
+ }
147
+ const input = { ...(props.props as Record<string, unknown>) }
148
+ if (Object.hasOwn(props, 'childrenOverride')) input.children = props.childrenOverride
149
+ return jsx(props.tag, {
150
+ ...renderableProps(input),
151
+ children: renderableChildren(input),
152
+ })
153
+ }
154
+
155
+ export interface ServerRenderOptions {
156
+ readonly identifierPrefix?: string
157
+ }
158
+
159
+ type FrameworkCacheEntry = {
160
+ readonly args: readonly unknown[]
161
+ readonly status: 'fulfilled' | 'rejected'
162
+ readonly value: unknown
163
+ }
164
+
165
+ /** @internal */
166
+ export interface ServerFrameworkRenderContext {
167
+ readonly signal: AbortSignal
168
+ readonly pending: Set<PromiseLike<unknown>>
169
+ readonly cache: Map<Function, FrameworkCacheEntry[]>
170
+ readonly head: Map<string, { readonly html: string; readonly precedence: string }>
171
+ readonly stylePrecedence: string[]
172
+ sawHead: boolean
173
+ }
174
+
175
+ interface RenderContext {
176
+ hydrationMarkers: boolean
177
+ identifierPrefix: string
178
+ nextBoundary: number
179
+ nextId: number
180
+ activityHidden: boolean
181
+ }
182
+
183
+ interface ContextState<Value> {
184
+ readonly defaultValue: Value
185
+ readonly stack: Value[]
186
+ }
187
+
188
+ export interface ServerContext<Value> {
189
+ readonly Provider: ServerComponent
190
+ readonly [SERVER_CONTEXT]: ContextState<Value>
191
+ }
192
+
193
+ type ServerAsyncState<Value> =
194
+ | { readonly status: 'pending' }
195
+ | { readonly status: 'fulfilled'; readonly value: Value }
196
+ | { readonly status: 'rejected'; readonly reason: unknown }
197
+
198
+ export interface ServerAsyncResource<Value> {
199
+ readonly [SERVER_ASYNC_RESOURCE]: ServerAsyncState<Value>
200
+ readonly promise: PromiseLike<Value>
201
+ }
202
+
203
+ type ServerSuspension = {
204
+ readonly [SERVER_SUSPENSION]: true
205
+ readonly resource: ServerAsyncResource<unknown>
206
+ }
207
+
208
+ export type ServerLazyModule<Props extends Record<string, unknown>> = {
209
+ readonly default: (props: Props) => ServerChild
210
+ }
211
+
212
+ const VOID_ELEMENTS = new Set([
213
+ 'area',
214
+ 'base',
215
+ 'br',
216
+ 'col',
217
+ 'embed',
218
+ 'hr',
219
+ 'img',
220
+ 'input',
221
+ 'keygen',
222
+ 'link',
223
+ 'menuitem',
224
+ 'meta',
225
+ 'param',
226
+ 'source',
227
+ 'track',
228
+ 'wbr',
229
+ ])
230
+
231
+ // Mirrors HYDRATION_PREFIX in hydration-bridge.ts; the server entry stays free of client modules.
232
+ const HYDRATION_PREFIX = 'v2'
233
+ const UNSAFE_HTML = typeof __VIDACT_UNSAFE_HTML__ !== 'undefined' && __VIDACT_UNSAFE_HTML__
234
+
235
+ let activeRender: RenderContext | undefined
236
+ let activeFrameworkRender: ServerFrameworkRenderContext | undefined
237
+ const serverBuiltins = new WeakSet<ServerComponent>()
238
+ serverBuiltins.add(dynamicIntrinsicComponent)
239
+ serverBuiltins.add(cloneRenderableComponent)
240
+ serverBuiltins.add(keyedFragmentComponent)
241
+ const serverPromiseResources = new WeakMap<object, ServerAsyncResource<unknown>>()
242
+
243
+ const FRAMEWORK_HEAD_PLACEHOLDER = '<!--vidact-framework:v1:head-->'
244
+
245
+ export const Fragment = Symbol('Vidact.ServerFragment')
246
+
247
+ /** @internal */
248
+ export function createServerFrameworkRenderContext(
249
+ signal: AbortSignal,
250
+ ): ServerFrameworkRenderContext {
251
+ return {
252
+ signal,
253
+ pending: new Set(),
254
+ cache: new Map(),
255
+ head: new Map(),
256
+ stylePrecedence: [],
257
+ sawHead: false,
258
+ }
259
+ }
260
+
261
+ /** @internal */
262
+ export function withServerFrameworkRenderContext<Result>(
263
+ context: ServerFrameworkRenderContext,
264
+ operation: () => Result,
265
+ ): Result {
266
+ const previous = activeFrameworkRender
267
+ activeFrameworkRender = context
268
+ try {
269
+ return operation()
270
+ } finally {
271
+ activeFrameworkRender = previous
272
+ }
273
+ }
274
+
275
+ export function cache<Arguments extends readonly unknown[], Result>(
276
+ operation: (...arguments_: Arguments) => Result,
277
+ ): (...arguments_: Arguments) => Result {
278
+ return (...arguments_) => {
279
+ const context = activeFrameworkRender
280
+ if (context === undefined) return operation(...arguments_)
281
+ let entries = context.cache.get(operation)
282
+ if (entries === undefined) {
283
+ entries = []
284
+ context.cache.set(operation, entries)
285
+ }
286
+ const existing = entries.find(
287
+ (entry) =>
288
+ entry.args.length === arguments_.length &&
289
+ entry.args.every((argument, index) => Object.is(argument, arguments_[index])),
290
+ )
291
+ if (existing !== undefined) {
292
+ if (existing.status === 'rejected') throw existing.value
293
+ return existing.value as Result
294
+ }
295
+ try {
296
+ const value = operation(...arguments_)
297
+ entries.push({ args: [...arguments_], status: 'fulfilled', value })
298
+ return value
299
+ } catch (error) {
300
+ entries.push({ args: [...arguments_], status: 'rejected', value: error })
301
+ throw error
302
+ }
303
+ }
304
+ }
305
+
306
+ export function cacheSignal(): AbortSignal | null {
307
+ return activeFrameworkRender?.signal ?? null
308
+ }
309
+
310
+ export function preconnect(href: string, options: ResourceHintOptions = {}): void {
311
+ registerResourceHint('preconnect', href, options)
312
+ }
313
+
314
+ export function prefetchDNS(href: string): void {
315
+ registerResourceHint('dns-prefetch', href, {})
316
+ }
317
+
318
+ export function preload(href: string, options: PreloadOptions): void {
319
+ registerResourceHint('preload', href, options)
320
+ }
321
+
322
+ export function preloadModule(href: string, options: ResourceHintOptions = {}): void {
323
+ registerResourceHint('modulepreload', href, options)
324
+ }
325
+
326
+ export function preinit(href: string, options: PreinitOptions): void {
327
+ if (options.as === 'style') {
328
+ registerHeadEntry(
329
+ `style:${href}`,
330
+ `<link rel="stylesheet" href="${escapeAttribute(href)}"${serializeHintOptions(options)}>`,
331
+ `2:${options.precedence ?? 'default'}`,
332
+ )
333
+ return
334
+ }
335
+ registerHeadEntry(
336
+ `script:${href}`,
337
+ `<script src="${escapeAttribute(href)}"${serializeHintOptions(options)}></script>`,
338
+ '3:script',
339
+ )
340
+ }
341
+
342
+ export function preinitModule(href: string, options: ResourceHintOptions = {}): void {
343
+ registerHeadEntry(
344
+ `module:${href}`,
345
+ `<script type="module" src="${escapeAttribute(href)}"${serializeHintOptions(options)}></script>`,
346
+ '3:script',
347
+ )
348
+ }
349
+
350
+ export function jsx(type: ServerElementType, props: ServerProps, _key?: unknown): ServerNode {
351
+ return createServerElement(type, props, false)
352
+ }
353
+
354
+ export function jsxs(type: ServerElementType, props: ServerProps, _key?: unknown): ServerNode {
355
+ return createServerElement(type, props, true)
356
+ }
357
+
358
+ export function jsxDEV(
359
+ type: ServerElementType,
360
+ props: ServerProps,
361
+ _key?: unknown,
362
+ isStaticChildren = false,
363
+ ): ServerNode {
364
+ return createServerElement(type, props, isStaticChildren)
365
+ }
366
+
367
+ export function createElement(
368
+ type: ServerElementType,
369
+ props: ServerProps,
370
+ ...children: ServerChild[]
371
+ ): ServerNode {
372
+ const nextProps: Record<string, unknown> = { ...props }
373
+ if (children.length === 1) nextProps.children = children[0]
374
+ else if (children.length > 1) nextProps.children = children
375
+ return createServerElement(type, nextProps, children.length > 1)
376
+ }
377
+
378
+ /** @internal */
379
+ export function createServerHydrationBoundary(
380
+ value: ServerChild,
381
+ hostProps: Readonly<Record<string, unknown>> = {},
382
+ ): ServerNode {
383
+ return serverNode((context) => {
384
+ const boundaryIndex = context.nextBoundary
385
+ context.nextBoundary += 1
386
+ const identifierPrefix = `${context.identifierPrefix}b${boundaryIndex}-`
387
+ const boundaryContext: RenderContext = {
388
+ hydrationMarkers: true,
389
+ identifierPrefix,
390
+ nextBoundary: 0,
391
+ nextId: 0,
392
+ activityHidden: false,
393
+ }
394
+ const attributes = serializeAttributes(
395
+ {
396
+ ...hostProps,
397
+ 'data-vidact-identifier-prefix': identifierPrefix,
398
+ },
399
+ context.activityHidden,
400
+ )
401
+ return `<div${attributes}>${hydrationRange('r', serializeSlot(value, boundaryContext))}</div>`
402
+ }, 'transparent')
403
+ }
404
+
405
+ function createServerElement(
406
+ type: ServerElementType,
407
+ props: ServerProps,
408
+ multipleChildren: boolean,
409
+ ): ServerNode {
410
+ if (type === Fragment) {
411
+ return serverNode(
412
+ (context) =>
413
+ Object.hasOwn(props ?? {}, 'children')
414
+ ? multipleChildren
415
+ ? serializeChildren(props?.children as ServerChild, context)
416
+ : serializeSlot(props?.children as ServerChild, context)
417
+ : '',
418
+ 'transparent',
419
+ )
420
+ }
421
+ if (isRenderable(type)) {
422
+ return serverNode(
423
+ (context) => serializeSlot(cloneRenderable(type, props ?? {}), context),
424
+ 'component',
425
+ )
426
+ }
427
+ if (typeof type === 'function') {
428
+ const builtin = serverBuiltins.has(type)
429
+ return serverNode(
430
+ (context) => {
431
+ const content = serializeSlot(type(props ?? {}) as ServerChild, context)
432
+ return context.hydrationMarkers && !builtin ? hydrationRange('c', content) : content
433
+ },
434
+ builtin ? 'transparent' : 'component',
435
+ )
436
+ }
437
+
438
+ if (!VALID_ATTRIBUTE_NAME.test(type)) throw new TypeError(`invalid server element name ${type}`)
439
+ return serverNode(
440
+ (context) => serializeIntrinsicElement(type, props, multipleChildren, context),
441
+ 'intrinsic',
442
+ )
443
+ }
444
+
445
+ function serializeIntrinsicElement(
446
+ type: string,
447
+ props: ServerProps,
448
+ multipleChildren: boolean,
449
+ context: RenderContext,
450
+ ): string {
451
+ if (activeFrameworkRender !== undefined && isHoistableHeadElement(type, props)) {
452
+ const hydrationMarkers = context.hydrationMarkers
453
+ context.hydrationMarkers = false
454
+ try {
455
+ const html = serializeIntrinsicContents(type, props, multipleChildren, context)
456
+ registerHeadEntry(metadataKey(type, props), html, metadataPrecedence(type, props))
457
+ return ''
458
+ } finally {
459
+ context.hydrationMarkers = hydrationMarkers
460
+ }
461
+ }
462
+ return serializeIntrinsicContents(type, props, multipleChildren, context)
463
+ }
464
+
465
+ function serializeIntrinsicContents(
466
+ type: string,
467
+ props: ServerProps,
468
+ multipleChildren: boolean,
469
+ context: RenderContext,
470
+ ): string {
471
+ const hiddenByActivity = context.activityHidden
472
+ const previousActivityHidden = context.activityHidden
473
+ context.activityHidden = false
474
+ try {
475
+ const attributes = serializeAttributes(props, hiddenByActivity)
476
+ if (VOID_ELEMENTS.has(type)) {
477
+ if (hasRenderableChild(props?.children as ServerChild) || readRawHtml(props) !== undefined) {
478
+ throw new TypeError(`${type} is a void element and cannot have children or raw HTML`)
479
+ }
480
+ return `<${type}${attributes}>`
481
+ }
482
+
483
+ const rawHtml = readRawHtml(props)
484
+ if (rawHtml !== undefined && hasRenderableChild(props?.children as ServerChild)) {
485
+ throw new TypeError('cannot set both children and dangerouslySetInnerHTML')
486
+ }
487
+ const hasChildren = Object.hasOwn(props ?? {}, 'children')
488
+ const children =
489
+ rawHtml === undefined
490
+ ? hasChildren
491
+ ? serializeElementChildren(
492
+ type,
493
+ props?.children as ServerChild,
494
+ multipleChildren,
495
+ context,
496
+ )
497
+ : ''
498
+ : context.hydrationMarkers
499
+ ? hydrationRange('h', assertSafeHydrationRawHtml(rawHtml))
500
+ : rawHtml
501
+ if (type === 'head' && activeFrameworkRender !== undefined) {
502
+ activeFrameworkRender.sawHead = true
503
+ return `<head${attributes}>${children}${FRAMEWORK_HEAD_PLACEHOLDER}</head>`
504
+ }
505
+ return `<${type}${attributes}>${children}</${type}>`
506
+ } finally {
507
+ context.activityHidden = previousActivityHidden
508
+ }
509
+ }
510
+
511
+ export function renderToString(
512
+ value: ServerChild | (() => ServerChild),
513
+ options: ServerRenderOptions = {},
514
+ ): string {
515
+ const previous = activeRender
516
+ beginFrameworkRenderPass()
517
+ const context = {
518
+ hydrationMarkers: true,
519
+ identifierPrefix: options.identifierPrefix ?? '',
520
+ nextBoundary: 0,
521
+ nextId: 0,
522
+ activityHidden: false,
523
+ }
524
+ activeRender = context
525
+ try {
526
+ const html = hydrationRange(
527
+ 'r',
528
+ serializeSlot(typeof value === 'function' ? value() : value, context),
529
+ )
530
+ return finalizeFrameworkHtml(html)
531
+ } finally {
532
+ activeRender = previous
533
+ }
534
+ }
535
+
536
+ export function renderToStaticMarkup(
537
+ value: ServerChild | (() => ServerChild),
538
+ options: ServerRenderOptions = {},
539
+ ): string {
540
+ const previous = activeRender
541
+ beginFrameworkRenderPass()
542
+ const context = {
543
+ hydrationMarkers: false,
544
+ identifierPrefix: options.identifierPrefix ?? '',
545
+ nextBoundary: 0,
546
+ nextId: 0,
547
+ activityHidden: false,
548
+ }
549
+ activeRender = context
550
+ try {
551
+ return finalizeFrameworkHtml(
552
+ serializeChild(typeof value === 'function' ? value() : value, context),
553
+ )
554
+ } finally {
555
+ activeRender = previous
556
+ }
557
+ }
558
+
559
+ export type StateUpdate<Value> = Value | ((previous: Value) => Value)
560
+
561
+ export function useState<Value>(
562
+ initial: Value | (() => Value),
563
+ ): [Value, (value: StateUpdate<Value>) => void] {
564
+ const value = typeof initial === 'function' ? (initial as () => Value)() : initial
565
+ return [value, serverDispatch]
566
+ }
567
+
568
+ export function useReducer<State, Action>(
569
+ _reducer: (state: State, action: Action) => State,
570
+ initial: State,
571
+ initialize?: (value: State) => State,
572
+ ): [State, (action: Action) => void] {
573
+ return [initialize === undefined ? initial : initialize(initial), serverDispatch]
574
+ }
575
+
576
+ export function startTransition(action: () => void | PromiseLike<void>): void {
577
+ void action()
578
+ }
579
+
580
+ export function useTransition(): [boolean, typeof startTransition] {
581
+ return [false, startTransition]
582
+ }
583
+
584
+ export function useDeferredValue<Value>(value: Value, _initialValue?: Value): Value {
585
+ return value
586
+ }
587
+
588
+ export function flushSync<Result>(operation?: () => Result): Result | undefined {
589
+ return operation?.()
590
+ }
591
+
592
+ export function useActionState<State, Payload>(
593
+ _action: (previousState: State, payload: Payload) => State | PromiseLike<State>,
594
+ initialState: State,
595
+ permalink?: string,
596
+ ): [State, (payload: Payload) => void, boolean] {
597
+ const dispatch = (_payload: Payload): void => serverDispatch()
598
+ if (permalink !== undefined) Object.defineProperty(dispatch, 'permalink', { value: permalink })
599
+ return [initialState, dispatch, false]
600
+ }
601
+
602
+ export function useOptimistic<Value, Update = Value>(
603
+ passthrough: Value,
604
+ _reducer?: (current: Value, update: Update) => Value,
605
+ ): [Value, (update: Update) => void] {
606
+ return [passthrough, serverDispatch]
607
+ }
608
+
609
+ export function useFormStatus(): {
610
+ readonly pending: false
611
+ readonly data: null
612
+ readonly method: 'get'
613
+ readonly action: null
614
+ } {
615
+ return { pending: false, data: null, method: 'get', action: null }
616
+ }
617
+
618
+ export function useMemo<Value>(factory: () => Value, _dependencies: readonly unknown[]): Value {
619
+ return factory()
620
+ }
621
+
622
+ export function useCallback<Value extends (...arguments_: never[]) => unknown>(
623
+ callback: Value,
624
+ _dependencies: readonly unknown[],
625
+ ): Value {
626
+ return callback
627
+ }
628
+
629
+ export function useRef<Value>(initial: Value): { current: Value } {
630
+ return { current: initial }
631
+ }
632
+
633
+ export function createRef<Value>(): { current: Value | null } {
634
+ return { current: null }
635
+ }
636
+
637
+ export function useEffect(
638
+ _create: () => void | (() => void),
639
+ _dependencies?: readonly unknown[],
640
+ ): void {}
641
+
642
+ export const useLayoutEffect = useEffect
643
+ export const useInsertionEffect = useEffect
644
+
645
+ export function useImperativeHandle(
646
+ _ref: unknown,
647
+ _create: () => unknown,
648
+ _dependencies?: readonly unknown[],
649
+ ): void {}
650
+
651
+ export function useEffectEvent<Arguments extends unknown[], Result>(
652
+ callback: (...arguments_: Arguments) => Result,
653
+ ): (...arguments_: Arguments) => Result {
654
+ return callback
655
+ }
656
+
657
+ export function useSyncExternalStore<Value>(
658
+ _subscribe: (notify: () => void) => () => void,
659
+ getSnapshot: () => Value,
660
+ getServerSnapshot?: () => Value,
661
+ ): Value {
662
+ return (getServerSnapshot ?? getSnapshot)()
663
+ }
664
+
665
+ export function useId(): string {
666
+ const context = (activeRender ??= {
667
+ hydrationMarkers: true,
668
+ identifierPrefix: '',
669
+ nextBoundary: 0,
670
+ nextId: 0,
671
+ activityHidden: false,
672
+ })
673
+ const id = `:${context.identifierPrefix}r${context.nextId}:`
674
+ context.nextId += 1
675
+ return id
676
+ }
677
+
678
+ export function createContext<Value>(defaultValue: Value): ServerContext<Value> {
679
+ let context: ServerContext<Value>
680
+ const Provider: ServerComponent = (props) =>
681
+ serverNode((renderContext) => {
682
+ context[SERVER_CONTEXT].stack.push(props.value as Value)
683
+ try {
684
+ return serializeChild(props.children as ServerChild, renderContext)
685
+ } finally {
686
+ context[SERVER_CONTEXT].stack.pop()
687
+ }
688
+ }, 'transparent')
689
+ context = { [SERVER_CONTEXT]: { defaultValue, stack: [] }, Provider }
690
+ serverBuiltins.add(Provider)
691
+ return context
692
+ }
693
+
694
+ export function useContext<Value>(context: ServerContext<Value>): Value {
695
+ const state = context[SERVER_CONTEXT]
696
+ return state.stack.length === 0 ? state.defaultValue : (state.stack.at(-1) as Value)
697
+ }
698
+
699
+ export function createResource<Value>(input: PromiseLike<Value>): ServerAsyncResource<Value> {
700
+ if (!isPromiseLike(input)) throw new TypeError('createResource requires a promise-like value')
701
+ const existing = serverPromiseResources.get(input as object) as
702
+ | ServerAsyncResource<Value>
703
+ | undefined
704
+ if (existing !== undefined) return existing
705
+ let state: ServerAsyncState<Value> = { status: 'pending' }
706
+ const resource = { promise: input } as ServerAsyncResource<Value>
707
+ Object.defineProperty(resource, SERVER_ASYNC_RESOURCE, { get: () => state })
708
+ serverPromiseResources.set(input as object, resource as ServerAsyncResource<unknown>)
709
+ void Promise.resolve(input).then(
710
+ (value) => {
711
+ if (state.status === 'pending') state = { status: 'fulfilled', value }
712
+ },
713
+ (reason: unknown) => {
714
+ if (state.status === 'pending') state = { status: 'rejected', reason }
715
+ },
716
+ )
717
+ return resource
718
+ }
719
+
720
+ export function use<Value>(
721
+ input: ServerContext<Value> | ServerAsyncResource<Value> | PromiseLike<Value>,
722
+ ): Value {
723
+ if (isServerContext<Value>(input)) return useContext(input)
724
+ const resource = isServerAsyncResource<Value>(input) ? input : createResource(input)
725
+ const state = resource[SERVER_ASYNC_RESOURCE]
726
+ if (state.status === 'fulfilled') return state.value
727
+ if (state.status === 'rejected') throw state.reason
728
+ throw { [SERVER_SUSPENSION]: true, resource } satisfies ServerSuspension
729
+ }
730
+
731
+ export function lazy<Props extends Record<string, unknown>>(
732
+ load: () => PromiseLike<ServerLazyModule<Props>>,
733
+ ): ServerComponent {
734
+ let resource: ServerAsyncResource<ServerLazyModule<Props>> | undefined
735
+ return (props) => {
736
+ resource ??= createResource(load())
737
+ const module = use(resource)
738
+ if (typeof module?.default !== 'function') {
739
+ throw new TypeError('lazy loader must resolve to a default component export')
740
+ }
741
+ return module.default(props as Props)
742
+ }
743
+ }
744
+
745
+ export function Suspense(props: Record<string, unknown>): ServerNode {
746
+ const render = props.children
747
+ const fallback = props.fallback
748
+ if (typeof render !== 'function' || typeof fallback !== 'function') {
749
+ throw new TypeError(
750
+ 'Suspense children and fallback must be compiler-generated render functions',
751
+ )
752
+ }
753
+ return serverNode((context) => {
754
+ try {
755
+ return serializeChild((render as () => ServerChild)(), context)
756
+ } catch (error) {
757
+ if (!isServerSuspension(error)) throw error
758
+ activeFrameworkRender?.pending.add(error.resource.promise)
759
+ const pendingMarker = context.hydrationMarkers ? `<!--${HYDRATION_PREFIX}:p-->` : ''
760
+ return pendingMarker + serializeChild((fallback as () => ServerChild)(), context)
761
+ }
762
+ }, 'transparent')
763
+ }
764
+
765
+ serverBuiltins.add(Suspense)
766
+
767
+ export function Activity(props: Record<string, unknown>): ServerNode {
768
+ const render = props.children
769
+ if (typeof render !== 'function') {
770
+ throw new TypeError('Activity children must be a compiler-generated render function')
771
+ }
772
+ if (props.mode !== 'visible' && props.mode !== 'hidden') {
773
+ throw new TypeError('Activity mode must be "visible" or "hidden"')
774
+ }
775
+ return serverNode((context) => {
776
+ const previousActivityHidden = context.activityHidden
777
+ context.activityHidden = props.mode === 'hidden' || previousActivityHidden
778
+ try {
779
+ return serializeChild((render as () => ServerChild)(), context)
780
+ } finally {
781
+ context.activityHidden = previousActivityHidden
782
+ }
783
+ }, 'transparent')
784
+ }
785
+
786
+ serverBuiltins.add(Activity)
787
+
788
+ export function Profiler(props: Record<string, unknown>): ServerChild {
789
+ const render = props.children
790
+ if (typeof render !== 'function') {
791
+ throw new TypeError('Profiler children must be a compiler-generated render function')
792
+ }
793
+ if (typeof props.id !== 'string') throw new TypeError('Profiler id must be a string')
794
+ if (typeof props.onRender !== 'function') {
795
+ throw new TypeError('Profiler onRender must be a function')
796
+ }
797
+ return (render as () => ServerChild)()
798
+ }
799
+
800
+ serverBuiltins.add(Profiler)
801
+
802
+ export function useDebugValue<Value>(_value: Value, _format?: (value: Value) => unknown): void {}
803
+
804
+ export function captureOwnerStack(): null {
805
+ return null
806
+ }
807
+
808
+ export function createPortal(_children: ServerChild, _container: unknown): never {
809
+ throw new Error(
810
+ 'portals cannot be emitted by the server target; render portal content in the client root',
811
+ )
812
+ }
813
+
814
+ function serverNode(
815
+ render: (context: RenderContext) => string,
816
+ kind: ServerNode[typeof SERVER_NODE_KIND],
817
+ ): ServerNode {
818
+ return { [SERVER_NODE]: render, [SERVER_NODE_KIND]: kind }
819
+ }
820
+
821
+ function isServerNode(value: unknown): value is ServerNode {
822
+ return typeof value === 'object' && value !== null && SERVER_NODE in value
823
+ }
824
+
825
+ function isServerContext<Value>(value: unknown): value is ServerContext<Value> {
826
+ return typeof value === 'object' && value !== null && SERVER_CONTEXT in value
827
+ }
828
+
829
+ function isServerAsyncResource<Value>(value: unknown): value is ServerAsyncResource<Value> {
830
+ return typeof value === 'object' && value !== null && SERVER_ASYNC_RESOURCE in value
831
+ }
832
+
833
+ function isServerSuspension(value: unknown): value is ServerSuspension {
834
+ return typeof value === 'object' && value !== null && SERVER_SUSPENSION in value
835
+ }
836
+
837
+ function serializeChild(value: ServerChild, context: RenderContext): string {
838
+ // Empty scalars render nothing and intrinsic elements and text render bare: the
839
+ // hydrator infers both from the DOM inside the enclosing child slot.
840
+ if (value === null || value === undefined || typeof value === 'boolean') return ''
841
+ if (isServerNode(value)) return value[SERVER_NODE](context)
842
+ if (isRenderable(value)) return serializeChild(cloneRenderable(value), context)
843
+ if (Array.isArray(value)) {
844
+ const content = value.map((child) => serializeChild(child, context)).join('')
845
+ return context.hydrationMarkers ? hydrationRange('a', content) : content
846
+ }
847
+ if (typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol') {
848
+ throw new TypeError('unsupported server child value')
849
+ }
850
+ const content = escapeText(String(value))
851
+ if (context.activityHidden && content !== '') {
852
+ throw new TypeError('initially hidden Activity server children require a host element root')
853
+ }
854
+ return content
855
+ }
856
+
857
+ function hasRenderableChild(value: ServerChild): boolean {
858
+ if (value === null || value === undefined || typeof value === 'boolean') return false
859
+ return !Array.isArray(value) || value.some(hasRenderableChild)
860
+ }
861
+
862
+ function serializeAttributes(props: ServerProps, hiddenByActivity = false): string {
863
+ if (props === null) return ' style="display:none!important"'
864
+ const attributes = Object.keys(props)
865
+ .toSorted()
866
+ .map((name) =>
867
+ name === 'style'
868
+ ? serializeStyleAttribute(props[name], hiddenByActivity)
869
+ : serializeAttribute(name, props[name]),
870
+ )
871
+ .join('')
872
+ return hiddenByActivity && !Object.hasOwn(props, 'style')
873
+ ? `${attributes} style="display:none!important"`
874
+ : attributes
875
+ }
876
+
877
+ function serializeAttribute(name: string, value: unknown): string {
878
+ if (
879
+ (name === 'action' || name === 'formAction') &&
880
+ typeof value === 'function' &&
881
+ typeof Reflect.get(value, 'permalink') === 'string'
882
+ ) {
883
+ return ` ${serverHtmlAttributeName(name)}="${escapeAttribute(Reflect.get(value, 'permalink') as string)}"`
884
+ }
885
+ if (
886
+ name === 'children' ||
887
+ name === 'dangerouslySetInnerHTML' ||
888
+ name === 'key' ||
889
+ name === 'precedence' ||
890
+ name === 'ref' ||
891
+ /^on/i.test(name) ||
892
+ value === null ||
893
+ value === undefined ||
894
+ typeof value === 'function' ||
895
+ typeof value === 'symbol'
896
+ ) {
897
+ return ''
898
+ }
899
+ if (name === 'style') return serializeStyleAttribute(value)
900
+ const normalizedName = serverHtmlAttributeName(name)
901
+ if (name.startsWith('data-') || name.startsWith('aria-')) {
902
+ return ` ${normalizedName}="${escapeAttribute(String(value))}"`
903
+ }
904
+ if (SERVER_BOOLEAN_ATTRIBUTES.has(name)) return value ? ` ${normalizedName}=""` : ''
905
+ if (typeof value === 'boolean') return ''
906
+ if (!VALID_ATTRIBUTE_NAME.test(normalizedName)) return ''
907
+ return ` ${normalizedName}="${escapeAttribute(String(value))}"`
908
+ }
909
+
910
+ function beginFrameworkRenderPass(): void {
911
+ const framework = activeFrameworkRender
912
+ if (framework === undefined) return
913
+ if (framework.signal.aborted) throw frameworkAbortReason(framework.signal)
914
+ framework.pending.clear()
915
+ framework.head.clear()
916
+ framework.stylePrecedence.length = 0
917
+ framework.sawHead = false
918
+ }
919
+
920
+ function finalizeFrameworkHtml(html: string): string {
921
+ const framework = activeFrameworkRender
922
+ if (framework === undefined) return html
923
+ const head = [...framework.head.entries()]
924
+ .toSorted(([, left], [, right]) => compareHeadPrecedence(framework, left, right))
925
+ .map(([, entry]) => entry.html)
926
+ .join('')
927
+ if (framework.sawHead) return html.replace(FRAMEWORK_HEAD_PLACEHOLDER, head)
928
+ if (head === '') return html
929
+ const htmlStart = html.indexOf('<html')
930
+ if (htmlStart !== -1) {
931
+ const htmlOpenEnd = html.indexOf('>', htmlStart)
932
+ if (htmlOpenEnd !== -1) {
933
+ return `${html.slice(0, htmlOpenEnd + 1)}<head>${head}</head>${html.slice(htmlOpenEnd + 1)}`
934
+ }
935
+ }
936
+ return `<head>${head}</head>${html}`
937
+ }
938
+
939
+ function isHoistableHeadElement(type: string, props: ServerProps): boolean {
940
+ if (type === 'title' || type === 'meta') return !hasOwnProp(props, 'itemProp')
941
+ if (type === 'link') {
942
+ if (['itemProp', 'onLoad', 'onError', 'disabled'].some((name) => hasOwnProp(props, name))) {
943
+ return false
944
+ }
945
+ return props?.rel !== 'stylesheet' || typeof props.precedence === 'string'
946
+ }
947
+ if (type === 'style') {
948
+ return typeof props?.href === 'string' && typeof props.precedence === 'string'
949
+ }
950
+ return type === 'script' && props?.async === true && typeof props.src === 'string'
951
+ }
952
+
953
+ function hasOwnProp(props: ServerProps, name: string): boolean {
954
+ return props !== null && Object.hasOwn(props, name)
955
+ }
956
+
957
+ function metadataKey(type: string, props: ServerProps): string {
958
+ if (type === 'title') return 'title'
959
+ if (type === 'meta') {
960
+ for (const name of META_IDENTITY_PROPS) {
961
+ const value = props?.[name]
962
+ if (value !== undefined) return `meta:${name}:${String(value)}`
963
+ }
964
+ }
965
+ if (type === 'link')
966
+ return `link:${LINK_IDENTITY_PROPS.map((name) => String(props?.[name] ?? '')).join(':')}`
967
+ if (type === 'style') return `style:${String(props?.href ?? '')}`
968
+ if (type === 'script') return `script:${String(props?.src ?? '')}`
969
+ return `${type}:${JSON.stringify(props)}`
970
+ }
971
+
972
+ function metadataPrecedence(type: string, props: ServerProps): string {
973
+ if (type === 'style' || (type === 'link' && props?.rel === 'stylesheet')) {
974
+ return `2:${String(props?.precedence ?? 'default')}`
975
+ }
976
+ if (type === 'script') return '3:script'
977
+ return '1:metadata'
978
+ }
979
+
980
+ function registerResourceHint(
981
+ relation: string,
982
+ href: string,
983
+ options: ResourceHintOptions | PreloadOptions,
984
+ ): void {
985
+ if (typeof href !== 'string' || href.length === 0)
986
+ throw new TypeError('resource href must be non-empty')
987
+ const as =
988
+ 'as' in options && typeof options.as === 'string' ? ` as="${escapeAttribute(options.as)}"` : ''
989
+ registerHeadEntry(
990
+ `hint:${relation}:${href}:${as}`,
991
+ `<link rel="${relation}" href="${escapeAttribute(href)}"${as}${serializeHintOptions(options)}>`,
992
+ '0:hint',
993
+ )
994
+ }
995
+
996
+ function registerHeadEntry(key: string, html: string, precedence: string): void {
997
+ const framework = activeFrameworkRender
998
+ if (framework === undefined) return
999
+ if (precedence.startsWith('2:')) {
1000
+ const stylePrecedence = precedence.slice(2)
1001
+ if (!framework.stylePrecedence.includes(stylePrecedence)) {
1002
+ framework.stylePrecedence.push(stylePrecedence)
1003
+ }
1004
+ }
1005
+ framework.head.set(key, { html, precedence })
1006
+ }
1007
+
1008
+ function compareHeadPrecedence(
1009
+ framework: ServerFrameworkRenderContext,
1010
+ left: { readonly precedence: string },
1011
+ right: { readonly precedence: string },
1012
+ ): number {
1013
+ const leftRank = Number(left.precedence[0])
1014
+ const rightRank = Number(right.precedence[0])
1015
+ if (leftRank !== rightRank) return leftRank - rightRank
1016
+ if (leftRank !== 2) return 0
1017
+ return (
1018
+ framework.stylePrecedence.indexOf(left.precedence.slice(2)) -
1019
+ framework.stylePrecedence.indexOf(right.precedence.slice(2))
1020
+ )
1021
+ }
1022
+
1023
+ function serializeHintOptions(
1024
+ options: ResourceHintOptions | PreloadOptions | PreinitOptions,
1025
+ ): string {
1026
+ const attributes: string[] = []
1027
+ if (options.crossOrigin !== undefined) {
1028
+ attributes.push(` crossorigin="${escapeAttribute(options.crossOrigin)}"`)
1029
+ }
1030
+ if ('fetchPriority' in options && options.fetchPriority !== undefined) {
1031
+ attributes.push(` fetchpriority="${options.fetchPriority}"`)
1032
+ }
1033
+ if ('imageSizes' in options && options.imageSizes !== undefined) {
1034
+ attributes.push(` imagesizes="${escapeAttribute(options.imageSizes)}"`)
1035
+ }
1036
+ if ('imageSrcSet' in options && options.imageSrcSet !== undefined) {
1037
+ attributes.push(` imagesrcset="${escapeAttribute(options.imageSrcSet)}"`)
1038
+ }
1039
+ if ('integrity' in options && options.integrity !== undefined) {
1040
+ attributes.push(` integrity="${escapeAttribute(options.integrity)}"`)
1041
+ }
1042
+ if ('nonce' in options && options.nonce !== undefined) {
1043
+ attributes.push(` nonce="${escapeAttribute(options.nonce)}"`)
1044
+ }
1045
+ if ('referrerPolicy' in options && options.referrerPolicy !== undefined) {
1046
+ attributes.push(` referrerpolicy="${escapeAttribute(options.referrerPolicy)}"`)
1047
+ }
1048
+ if ('type' in options && options.type !== undefined) {
1049
+ attributes.push(` type="${escapeAttribute(options.type)}"`)
1050
+ }
1051
+ return attributes.join('')
1052
+ }
1053
+
1054
+ function frameworkAbortReason(signal: AbortSignal): unknown {
1055
+ return signal.reason ?? new DOMException('The operation was aborted', 'AbortError')
1056
+ }
1057
+
1058
+ function serializeStyleAttribute(value: unknown, hiddenByActivity = false): string {
1059
+ if (typeof value === 'string') {
1060
+ const separator = value === '' || value.endsWith(';') ? '' : ';'
1061
+ const style = hiddenByActivity ? `${value}${separator}display:none!important` : value
1062
+ return style === '' ? '' : ` style="${escapeAttribute(style)}"`
1063
+ }
1064
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1065
+ return hiddenByActivity ? ' style="display:none!important"' : ''
1066
+ }
1067
+
1068
+ const declarations = Object.entries(value)
1069
+ .toSorted(([left], [right]) => left.localeCompare(right))
1070
+ .flatMap(([name, item]) => {
1071
+ if (item === null || item === undefined || typeof item === 'boolean' || item === '') return []
1072
+ const cssName = name.startsWith('--') ? name : camelToKebab(name)
1073
+ const cssValue =
1074
+ typeof item === 'number' && item !== 0 && !UNITLESS_STYLE_PROPERTIES.has(name)
1075
+ ? `${item}px`
1076
+ : item
1077
+ return `${cssName}:${String(cssValue)}`
1078
+ })
1079
+ if (hiddenByActivity) declarations.push('display:none!important')
1080
+ return declarations.length === 0 ? '' : ` style="${escapeAttribute(declarations.join(';'))}"`
1081
+ }
1082
+
1083
+ function camelToKebab(name: string): string {
1084
+ return name
1085
+ .replace(/[A-Z]/g, (character) => `-${character.toLowerCase()}`)
1086
+ .replace(/^ms-/, '-ms-')
1087
+ }
1088
+
1089
+ function readRawHtml(props: ServerProps): string | undefined {
1090
+ const value = props?.dangerouslySetInnerHTML
1091
+ if (value === null || value === undefined) return undefined
1092
+ if (!UNSAFE_HTML) {
1093
+ throw new Error('dangerouslySetInnerHTML requires the unsafe-html compiler feature')
1094
+ }
1095
+ if (typeof value !== 'object' || !('__html' in value)) {
1096
+ throw new TypeError('dangerouslySetInnerHTML must be an object with an __html property')
1097
+ }
1098
+ const html = Reflect.get(value, '__html') as unknown
1099
+ return html === null || html === undefined ? '' : String(html)
1100
+ }
1101
+
1102
+ function escapeText(value: string): string {
1103
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
1104
+ }
1105
+
1106
+ function escapeAttribute(value: string): string {
1107
+ return escapeText(value).replaceAll('"', '&quot;').replaceAll("'", '&#x27;')
1108
+ }
1109
+
1110
+ function serializeElementChildren(
1111
+ type: string,
1112
+ value: ServerChild,
1113
+ multipleChildren: boolean,
1114
+ context: RenderContext,
1115
+ ): string {
1116
+ if (!isRawTextElement(type)) {
1117
+ return multipleChildren ? serializeChildren(value, context) : serializeSlot(value, context)
1118
+ }
1119
+ // The HTML parser treats the contents of <script>, <style>, <textarea> and <title> as
1120
+ // text, so a hydration marker here would surface as literal content — a visible
1121
+ // "<!--v2:b-->" inside the textarea until hydration replaced it. The client claims the
1122
+ // parsed text node directly instead.
1123
+ const hydrationMarkers = context.hydrationMarkers
1124
+ context.hydrationMarkers = false
1125
+ try {
1126
+ return multipleChildren ? serializeChildren(value, context) : serializeSlot(value, context)
1127
+ } finally {
1128
+ context.hydrationMarkers = hydrationMarkers
1129
+ }
1130
+ }
1131
+
1132
+ function serializeChildren(value: ServerChild, context: RenderContext): string {
1133
+ return Array.isArray(value)
1134
+ ? value.map((child) => serializeSlot(child, context)).join('')
1135
+ : serializeSlot(value, context)
1136
+ }
1137
+
1138
+ function serializeSlot(value: ServerChild, context: RenderContext): string {
1139
+ const content = serializeChild(value, context)
1140
+ return context.hydrationMarkers ? hydrationRange('b', content) : content
1141
+ }
1142
+
1143
+ function hydrationRange(kind: 'a' | 'b' | 'c' | 'h' | 'r', content: string): string {
1144
+ return `<!--${HYDRATION_PREFIX}:${kind}-->${content}<!--/${HYDRATION_PREFIX}:${kind}-->`
1145
+ }
1146
+
1147
+ function assertSafeHydrationRawHtml(value: string): string {
1148
+ if (/<!--\/?(?:vidact:)?v\d+:[a-z]-->/i.test(value)) {
1149
+ throw new Error('raw HTML cannot contain Vidact hydration marker syntax')
1150
+ }
1151
+ return value
1152
+ }
1153
+
1154
+ function isRawTextElement(type: string): boolean {
1155
+ return type === 'script' || type === 'style' || type === 'textarea' || type === 'title'
1156
+ }
1157
+
1158
+ function serverDispatch(): never {
1159
+ throw new Error('state dispatch is unavailable during server rendering')
1160
+ }