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

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