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

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 +71 -0
  2. package/README.md +1 -1
  3. package/dist/actions.d.ts +32 -30
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +251 -266
  6. package/dist/actions.js.map +1 -1
  7. package/dist/async.d.ts +20 -17
  8. package/dist/async.d.ts.map +1 -1
  9. package/dist/async.js +18 -17
  10. package/dist/async.js.map +1 -1
  11. package/dist/compiled/async-resource.d.ts +9 -6
  12. package/dist/compiled/async-resource.d.ts.map +1 -1
  13. package/dist/compiled/async-resource.js +69 -77
  14. package/dist/compiled/async-resource.js.map +1 -1
  15. package/dist/compiled/core.d.ts +148 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +3064 -2987
  18. package/dist/compiled/core.js.map +1 -1
  19. package/dist/compiled/types.d.ts +44 -56
  20. package/dist/compiled/types.d.ts.map +1 -1
  21. package/dist/compiled/types.js +10 -6
  22. package/dist/compiled/types.js.map +1 -1
  23. package/dist/compiled/validation.d.ts +4 -1
  24. package/dist/compiled/validation.d.ts.map +1 -1
  25. package/dist/compiled/validation.js +17 -20
  26. package/dist/compiled/validation.js.map +1 -1
  27. package/dist/component-spread.d.ts +5 -3
  28. package/dist/component-spread.d.ts.map +1 -1
  29. package/dist/component-spread.js +49 -67
  30. package/dist/component-spread.js.map +1 -1
  31. package/dist/concurrent.d.ts +20 -18
  32. package/dist/concurrent.d.ts.map +1 -1
  33. package/dist/concurrent.js +191 -210
  34. package/dist/concurrent.js.map +1 -1
  35. package/dist/direct-dom.d.ts +17 -12
  36. package/dist/direct-dom.d.ts.map +1 -1
  37. package/dist/direct-dom.js +279 -222
  38. package/dist/direct-dom.js.map +1 -1
  39. package/dist/dom/attributes.d.ts +9 -6
  40. package/dist/dom/attributes.d.ts.map +1 -1
  41. package/dist/dom/attributes.js +65 -47
  42. package/dist/dom/attributes.js.map +1 -1
  43. package/dist/dom/events.d.ts +8 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +118 -65
  46. package/dist/dom/events.js.map +1 -1
  47. package/dist/dom/forms.d.ts +9 -6
  48. package/dist/dom/forms.d.ts.map +1 -1
  49. package/dist/dom/forms.js +152 -198
  50. package/dist/dom/forms.js.map +1 -1
  51. package/dist/dom/intrinsic.d.ts +17 -15
  52. package/dist/dom/intrinsic.d.ts.map +1 -1
  53. package/dist/dom/intrinsic.js +48 -50
  54. package/dist/dom/intrinsic.js.map +1 -1
  55. package/dist/dom/namespace.d.ts +4 -1
  56. package/dist/dom/namespace.d.ts.map +1 -1
  57. package/dist/dom/namespace.js +25 -28
  58. package/dist/dom/namespace.js.map +1 -1
  59. package/dist/dom/namespaces.d.ts +11 -8
  60. package/dist/dom/namespaces.d.ts.map +1 -1
  61. package/dist/dom/namespaces.js +14 -12
  62. package/dist/dom/namespaces.js.map +1 -1
  63. package/dist/dom/properties.d.ts +18 -16
  64. package/dist/dom/properties.d.ts.map +1 -1
  65. package/dist/dom/properties.js +273 -296
  66. package/dist/dom/properties.js.map +1 -1
  67. package/dist/dom/style-properties.d.ts +4 -1
  68. package/dist/dom/style-properties.d.ts.map +1 -1
  69. package/dist/dom/style-properties.js +49 -45
  70. package/dist/dom/style-properties.js.map +1 -1
  71. package/dist/dom/styles.d.ts +5 -2
  72. package/dist/dom/styles.d.ts.map +1 -1
  73. package/dist/dom/styles.js +47 -67
  74. package/dist/dom/styles.js.map +1 -1
  75. package/dist/dom-forms.d.ts +4 -1
  76. package/dist/dom-forms.d.ts.map +1 -1
  77. package/dist/dom-forms.js +16 -13
  78. package/dist/dom-forms.js.map +1 -1
  79. package/dist/dom-styles.d.ts +4 -1
  80. package/dist/dom-styles.d.ts.map +1 -1
  81. package/dist/dom-styles.js +10 -7
  82. package/dist/dom-styles.js.map +1 -1
  83. package/dist/framework-hydrate.d.ts +20 -17
  84. package/dist/framework-hydrate.d.ts.map +1 -1
  85. package/dist/framework-hydrate.js +128 -146
  86. package/dist/framework-hydrate.js.map +1 -1
  87. package/dist/framework-protocol.d.ts +27 -25
  88. package/dist/framework-protocol.d.ts.map +1 -1
  89. package/dist/framework-protocol.js +173 -211
  90. package/dist/framework-protocol.js.map +1 -1
  91. package/dist/framework.d.ts +34 -32
  92. package/dist/framework.d.ts.map +1 -1
  93. package/dist/framework.js +74 -93
  94. package/dist/framework.js.map +1 -1
  95. package/dist/hydrate.d.ts +11 -8
  96. package/dist/hydrate.d.ts.map +1 -1
  97. package/dist/hydrate.js +13 -9
  98. package/dist/hydrate.js.map +1 -1
  99. package/dist/hydration-bridge.d.ts +70 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +77 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +54 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +450 -463
  106. package/dist/hydration.js.map +1 -1
  107. package/dist/index.d.ts +13 -13
  108. package/dist/index.js +11 -13
  109. package/dist/indexed-list.d.ts +8 -5
  110. package/dist/indexed-list.d.ts.map +1 -1
  111. package/dist/indexed-list.js +14 -7
  112. package/dist/indexed-list.js.map +1 -1
  113. package/dist/jsx-runtime.d.ts +10 -8
  114. package/dist/jsx-runtime.d.ts.map +1 -1
  115. package/dist/jsx-runtime.js +28 -23
  116. package/dist/jsx-runtime.js.map +1 -1
  117. package/dist/keyed-list.d.ts +17 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +244 -161
  120. package/dist/keyed-list.js.map +1 -1
  121. package/dist/metadata.d.ts +6 -3
  122. package/dist/metadata.d.ts.map +1 -1
  123. package/dist/metadata.js +131 -164
  124. package/dist/metadata.js.map +1 -1
  125. package/dist/nested-prop.d.ts +4 -2
  126. package/dist/nested-prop.d.ts.map +1 -1
  127. package/dist/nested-prop.js +17 -16
  128. package/dist/nested-prop.js.map +1 -1
  129. package/dist/profiling.d.ts +11 -8
  130. package/dist/profiling.d.ts.map +1 -1
  131. package/dist/profiling.js +17 -17
  132. package/dist/profiling.js.map +1 -1
  133. package/dist/protocol.d.ts +5 -2
  134. package/dist/protocol.d.ts.map +1 -1
  135. package/dist/protocol.js +8 -5
  136. package/dist/protocol.js.map +1 -1
  137. package/dist/raw-html.d.ts +5 -2
  138. package/dist/raw-html.d.ts.map +1 -1
  139. package/dist/raw-html.js +86 -146
  140. package/dist/raw-html.js.map +1 -1
  141. package/dist/ref.d.ts +8 -3
  142. package/dist/ref.d.ts.map +1 -1
  143. package/dist/ref.js +10 -2
  144. package/dist/ref.js.map +1 -1
  145. package/dist/renderable-protocol.d.ts +26 -8
  146. package/dist/renderable-protocol.d.ts.map +1 -1
  147. package/dist/renderable-protocol.js +30 -9
  148. package/dist/renderable-protocol.js.map +1 -1
  149. package/dist/renderable.d.ts +28 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +152 -133
  152. package/dist/renderable.js.map +1 -1
  153. package/dist/retained-ui.d.ts +9 -6
  154. package/dist/retained-ui.d.ts.map +1 -1
  155. package/dist/retained-ui.js +12 -10
  156. package/dist/retained-ui.js.map +1 -1
  157. package/dist/root.d.ts +18 -15
  158. package/dist/root.d.ts.map +1 -1
  159. package/dist/root.js +73 -85
  160. package/dist/root.js.map +1 -1
  161. package/dist/scheduler.d.ts +10 -8
  162. package/dist/scheduler.d.ts.map +1 -1
  163. package/dist/scheduler.js +97 -105
  164. package/dist/scheduler.js.map +1 -1
  165. package/dist/server-framework.d.ts +45 -42
  166. package/dist/server-framework.d.ts.map +1 -1
  167. package/dist/server-framework.js +209 -217
  168. package/dist/server-framework.js.map +1 -1
  169. package/dist/server.d.ts +130 -128
  170. package/dist/server.d.ts.map +1 -1
  171. package/dist/server.js +603 -710
  172. package/dist/server.js.map +1 -1
  173. package/dist/shared/promise.d.ts +6 -3
  174. package/dist/shared/promise.d.ts.map +1 -1
  175. package/dist/shared/promise.js +21 -19
  176. package/dist/shared/promise.js.map +1 -1
  177. package/dist/source-mask.d.ts +10 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +60 -63
  180. package/dist/source-mask.js.map +1 -1
  181. package/dist/spread.d.ts +5 -2
  182. package/dist/spread.d.ts.map +1 -1
  183. package/dist/spread.js +79 -99
  184. package/dist/spread.js.map +1 -1
  185. package/dist/state-slot.d.ts +29 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +73 -42
  188. package/dist/state-slot.js.map +1 -1
  189. package/dist/testing.d.ts +9 -6
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +16 -12
  192. package/dist/testing.js.map +1 -1
  193. package/package.json +8 -7
  194. package/src/actions.ts +415 -0
  195. package/src/async.ts +78 -0
  196. package/src/compiled/async-resource.ts +113 -0
  197. package/src/compiled/core.ts +4647 -0
  198. package/src/compiled/types.ts +96 -0
  199. package/src/compiled/validation.ts +23 -0
  200. package/src/component-spread.ts +86 -0
  201. package/src/concurrent.ts +310 -0
  202. package/src/direct-dom.ts +491 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +179 -0
  205. package/src/dom/forms.ts +220 -0
  206. package/src/dom/intrinsic.ts +94 -0
  207. package/src/dom/namespace.ts +30 -0
  208. package/src/dom/namespaces.ts +14 -0
  209. package/src/dom/properties.ts +355 -0
  210. package/src/dom/style-properties.ts +46 -0
  211. package/src/dom/styles.ts +86 -0
  212. package/src/dom-forms.ts +23 -0
  213. package/src/dom-styles.ts +11 -0
  214. package/src/env.d.ts +6 -0
  215. package/src/framework-hydrate.ts +224 -0
  216. package/src/framework-protocol.ts +335 -0
  217. package/src/framework.ts +161 -0
  218. package/src/hydrate.ts +38 -0
  219. package/src/hydration-bridge.ts +202 -0
  220. package/src/hydration.ts +644 -0
  221. package/src/index.ts +94 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +379 -0
  225. package/src/metadata.ts +212 -0
  226. package/src/nested-prop.ts +27 -0
  227. package/src/profiling.ts +43 -0
  228. package/src/protocol.ts +8 -0
  229. package/src/raw-html.ts +206 -0
  230. package/src/ref.ts +13 -0
  231. package/src/renderable-protocol.ts +63 -0
  232. package/src/renderable.ts +313 -0
  233. package/src/retained-ui.ts +19 -0
  234. package/src/root.ts +122 -0
  235. package/src/scheduler.ts +136 -0
  236. package/src/server-framework.ts +397 -0
  237. package/src/server.ts +1160 -0
  238. package/src/shared/promise.ts +28 -0
  239. package/src/source-mask.ts +103 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +132 -0
  242. package/src/testing.ts +27 -0
  243. package/dist/index.d.ts.map +0 -1
  244. package/dist/index.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ import { registerCompiledCleanup } from './compiled/core.ts'
2
+ import {
3
+ LINK_IDENTITY_PROPS,
4
+ META_IDENTITY_ATTRIBUTES,
5
+ META_IDENTITY_PROPS,
6
+ } from './dom/attributes.ts'
7
+ import { applyBaseDomProp, installDomPropHandler } from './dom/properties.ts'
8
+
9
+ type MetadataEntry = {
10
+ readonly elements: Element[]
11
+ readonly baseline: number
12
+ }
13
+
14
+ type MetadataState = {
15
+ readonly entries: Map<string, MetadataEntry>
16
+ readonly stylePrecedence: string[]
17
+ }
18
+
19
+ type MetadataOwner = {
20
+ readonly element: Element
21
+ readonly state: MetadataState
22
+ entry: MetadataEntry
23
+ key: string
24
+ }
25
+
26
+ const metadataByDocument = new WeakMap<Document, MetadataState>()
27
+ const metadataOwners = new WeakMap<Element, MetadataOwner>()
28
+ let propRekeyingInstalled = false
29
+
30
+ export function installFrameworkMetadataPropRekeying(): void {
31
+ if (propRekeyingInstalled) return
32
+ propRekeyingInstalled = true
33
+ installDomPropHandler((element, name, value) => {
34
+ const owner = metadataOwners.get(element)
35
+ if (owner === undefined || !isMetadataIdentityProp(element.localName, name)) return false
36
+ detachMetadataOwner(owner)
37
+ applyBaseDomProp(element, name, value)
38
+ if (name === 'precedence') {
39
+ element.setAttribute('data-precedence', String(value ?? 'default'))
40
+ element.removeAttribute('precedence')
41
+ }
42
+ attachMetadataOwner(owner)
43
+ return true
44
+ })
45
+ }
46
+
47
+ export function isFrameworkMetadataElement(
48
+ element: Element,
49
+ props: Readonly<Record<string, unknown>> | null = null,
50
+ ): boolean {
51
+ const name = element.localName
52
+ if (
53
+ (name === 'title' || name === 'meta' || name === 'link') &&
54
+ element.hasAttribute('itemprop')
55
+ ) {
56
+ return false
57
+ }
58
+ if (name === 'title' || name === 'meta') return true
59
+ if (name === 'link') {
60
+ if (
61
+ props !== null &&
62
+ ['onLoad', 'onError', 'disabled'].some((key) => Object.hasOwn(props, key))
63
+ ) {
64
+ return false
65
+ }
66
+ return element.getAttribute('rel') !== 'stylesheet' || element.hasAttribute('precedence')
67
+ }
68
+ if (name === 'style') return element.hasAttribute('href') && element.hasAttribute('precedence')
69
+ return name === 'script' && element.hasAttribute('async') && element.hasAttribute('src')
70
+ }
71
+
72
+ export function hoistFrameworkMetadata(element: Element): DocumentFragment {
73
+ const document = element.ownerDocument
74
+ const state = metadataState(document)
75
+ const key = metadataKey(element)
76
+ const owner: MetadataOwner = {
77
+ element,
78
+ state,
79
+ key,
80
+ entry: metadataEntry(document, state, key),
81
+ }
82
+ attachMetadataOwner(owner)
83
+ metadataOwners.set(element, owner)
84
+ element.removeAttribute('precedence')
85
+ registerCompiledCleanup(() => {
86
+ detachMetadataOwner(owner)
87
+ metadataOwners.delete(element)
88
+ })
89
+ return document.createDocumentFragment()
90
+ }
91
+
92
+ function metadataState(document: Document): MetadataState {
93
+ let state = metadataByDocument.get(document)
94
+ if (state !== undefined) return state
95
+ state = {
96
+ entries: new Map(),
97
+ stylePrecedence: [...document.head.children]
98
+ .filter((candidate) => metadataTier(candidate) === 2)
99
+ .map(stylePrecedence)
100
+ .filter((value, index, values) => values.indexOf(value) === index),
101
+ }
102
+ metadataByDocument.set(document, state)
103
+ return state
104
+ }
105
+
106
+ function metadataEntry(document: Document, state: MetadataState, key: string): MetadataEntry {
107
+ let entry = state.entries.get(key)
108
+ if (entry !== undefined) return entry
109
+ const existing = [...document.head.children].find(
110
+ (candidate) => isFrameworkMetadataElement(candidate) && metadataKey(candidate) === key,
111
+ )
112
+ entry = {
113
+ elements: existing === undefined ? [] : [existing],
114
+ baseline: existing === undefined ? 0 : 1,
115
+ }
116
+ state.entries.set(key, entry)
117
+ return entry
118
+ }
119
+
120
+ function attachMetadataOwner(owner: MetadataOwner): void {
121
+ const document = owner.element.ownerDocument
122
+ owner.key = metadataKey(owner.element)
123
+ owner.entry = metadataEntry(document, owner.state, owner.key)
124
+ owner.entry.elements.at(-1)?.remove()
125
+ owner.entry.elements.push(owner.element)
126
+ insertByPrecedence(document.head, owner.element, owner.state)
127
+ }
128
+
129
+ function detachMetadataOwner(owner: MetadataOwner): void {
130
+ const { element, entry, key, state } = owner
131
+ const index = entry.elements.indexOf(element)
132
+ if (index === -1) return
133
+ const visible = index === entry.elements.length - 1
134
+ entry.elements.splice(index, 1)
135
+ element.remove()
136
+ if (entry.elements.length === entry.baseline) {
137
+ if (visible && entry.elements.length !== 0) {
138
+ element.ownerDocument.head.append(entry.elements.at(-1)!)
139
+ }
140
+ if (state.entries.get(key) === entry) state.entries.delete(key)
141
+ } else if (visible) {
142
+ insertByPrecedence(element.ownerDocument.head, entry.elements.at(-1)!, state)
143
+ }
144
+ }
145
+
146
+ function isMetadataIdentityProp(elementName: string, propName: string): boolean {
147
+ if (propName === 'precedence') return elementName === 'link' || elementName === 'style'
148
+ if (elementName === 'meta') {
149
+ return (META_IDENTITY_PROPS as readonly string[]).includes(propName)
150
+ }
151
+ if (elementName === 'link') return (LINK_IDENTITY_PROPS as readonly string[]).includes(propName)
152
+ if (elementName === 'script') return propName === 'src'
153
+ return false
154
+ }
155
+
156
+ function metadataKey(element: Element): string {
157
+ const name = element.localName
158
+ if (name === 'title') return 'title'
159
+ if (name === 'meta') {
160
+ for (const attribute of META_IDENTITY_ATTRIBUTES) {
161
+ if (element.hasAttribute(attribute))
162
+ return `meta:${attribute}:${element.getAttribute(attribute)}`
163
+ }
164
+ }
165
+ if (name === 'link') {
166
+ return `link:${element.getAttribute('rel') ?? ''}:${element.getAttribute('href') ?? ''}:${element.getAttribute('as') ?? ''}`
167
+ }
168
+ if (name === 'style') return `style:${element.getAttribute('href') ?? ''}`
169
+ return `script:${element.getAttribute('src') ?? ''}`
170
+ }
171
+
172
+ function insertByPrecedence(head: HTMLHeadElement, element: Element, state: MetadataState): void {
173
+ const tier = metadataTier(element) ?? 1
174
+ const precedence = stylePrecedence(element)
175
+ if (tier === 2) {
176
+ if (!state.stylePrecedence.includes(precedence)) state.stylePrecedence.push(precedence)
177
+ element.setAttribute('data-precedence', precedence)
178
+ }
179
+ const before = [...head.children].find((candidate) => {
180
+ const candidateTier = metadataTier(candidate)
181
+ if (candidateTier === undefined) return false
182
+ if (candidateTier !== tier) return candidateTier > tier
183
+ return (
184
+ tier === 2 &&
185
+ state.stylePrecedence.indexOf(stylePrecedence(candidate)) >
186
+ state.stylePrecedence.indexOf(precedence)
187
+ )
188
+ })
189
+ head.insertBefore(element, before ?? null)
190
+ }
191
+
192
+ function metadataTier(element: Element): number | undefined {
193
+ const name = element.localName
194
+ if (name === 'style') return element.hasAttribute('data-precedence') ? 2 : undefined
195
+ if (name === 'script') return element.hasAttribute('src') ? 3 : undefined
196
+ if (name === 'link') {
197
+ const relation = element.getAttribute('rel')
198
+ if (relation === 'stylesheet') {
199
+ return element.hasAttribute('precedence') || element.hasAttribute('data-precedence')
200
+ ? 2
201
+ : undefined
202
+ }
203
+ if (['preconnect', 'dns-prefetch', 'preload', 'modulepreload'].includes(relation ?? ''))
204
+ return 0
205
+ return 1
206
+ }
207
+ return name === 'title' || name === 'meta' ? 1 : undefined
208
+ }
209
+
210
+ function stylePrecedence(element: Element): string {
211
+ return element.getAttribute('precedence') ?? element.getAttribute('data-precedence') ?? 'default'
212
+ }
@@ -0,0 +1,27 @@
1
+ import { binding, isCompiledBinding } from './compiled/core.ts'
2
+
3
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
4
+
5
+ type ContainerFallback = (() => unknown) | null
6
+
7
+ export function nestedProp(
8
+ input: unknown,
9
+ path: readonly string[],
10
+ containerFallbacks: readonly ContainerFallback[],
11
+ ): unknown {
12
+ const read = (): unknown => {
13
+ let value = isCompiledBinding(input) ? input[1]() : input
14
+ for (const [index, name] of path.entries()) {
15
+ if (value === undefined) value = containerFallbacks[index]?.()
16
+ if (value === null || value === undefined) {
17
+ throw new TypeError(
18
+ DEV ? `cannot destructure nested prop ${name} from a nullish value` : 'V015',
19
+ )
20
+ }
21
+ value = (value as Record<string, unknown>)[name]
22
+ }
23
+ return value
24
+ }
25
+
26
+ return isCompiledBinding(input) ? binding(input[2], input[3], read, input[4], input[5]) : read()
27
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ captureCompiledOwnerStack,
3
+ enableProfiling,
4
+ profiled,
5
+ recordCompiledDebugValue,
6
+ } from './compiled/core.ts'
7
+ import type {
8
+ CompiledBinding,
9
+ CompiledRenderValue,
10
+ ProfilerOnRender,
11
+ StructuralBinding,
12
+ } from './compiled/types.ts'
13
+
14
+ export interface ProfilerProps {
15
+ readonly children?: (() => CompiledRenderValue) | readonly [() => CompiledRenderValue]
16
+ readonly id: string | CompiledBinding<string>
17
+ readonly onRender: ProfilerOnRender | CompiledBinding<ProfilerOnRender>
18
+ }
19
+
20
+ export function Profiler(props: ProfilerProps): StructuralBinding {
21
+ const render = Array.isArray(props.children) ? props.children[0] : props.children
22
+ if (typeof render !== 'function') {
23
+ throw new TypeError('Profiler children must be a compiler-generated render function')
24
+ }
25
+ enableProfiling()
26
+ return profiled(props.id, props.onRender, render)
27
+ }
28
+
29
+ export function useDebugValue<Value>(
30
+ value: Value | CompiledBinding<Value>,
31
+ format?: (value: Value) => unknown,
32
+ ): void {
33
+ if (format !== undefined && typeof format !== 'function') {
34
+ throw new TypeError('useDebugValue formatter must be a function')
35
+ }
36
+ enableProfiling()
37
+ recordCompiledDebugValue(value, format)
38
+ }
39
+
40
+ export function captureOwnerStack(): string | null {
41
+ enableProfiling()
42
+ return captureCompiledOwnerStack()
43
+ }
@@ -0,0 +1,8 @@
1
+ export const VIDACT_RUNTIME_PROTOCOL = 'vidact-runtime-v2'
2
+
3
+ export function assertRuntimeProtocol(expected: string): void {
4
+ if (expected === VIDACT_RUNTIME_PROTOCOL) return
5
+ throw new Error(
6
+ `Vidact compiler/runtime protocol mismatch: compiled for ${expected}, runtime provides ${VIDACT_RUNTIME_PROTOCOL}`,
7
+ )
8
+ }
@@ -0,0 +1,206 @@
1
+ import {
2
+ isCompiledBinding,
3
+ mountCompiledPropTransition,
4
+ type CompiledPropTransition,
5
+ } from './compiled/core.ts'
6
+ import { HYDRATION_PREFIX, HydrationMismatch, isHydrating } from './hydration-bridge.ts'
7
+
8
+ const RAW_HTML_PUBLICATION_PRIORITY = 100
9
+ const rawHtmlHosts = new WeakSet<HTMLElement>()
10
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
11
+
12
+ export function mountRawHtmlProp(
13
+ element: HTMLElement,
14
+ value: unknown,
15
+ children: readonly unknown[],
16
+ ): void {
17
+ if (value === null || value === undefined) return
18
+ if (isCompiledBinding(value)) {
19
+ mountCompiledPropTransition(
20
+ value,
21
+ (initial) => mountRawHtml(element, initial, children),
22
+ (next, previous) => prepareRawHtml(element, next, previous, children),
23
+ )
24
+ return
25
+ }
26
+ mountRawHtml(element, value, children)
27
+ }
28
+
29
+ function mountRawHtml(element: HTMLElement, value: unknown, children: readonly unknown[]): void {
30
+ const html = readRawHtml(element, value, children)
31
+ if (html === null || html === undefined) return
32
+ const container = rawHtmlContainer(element)
33
+ const staged = parseRawHtml(element, html)
34
+ if (isHydrating()) {
35
+ if (!hasEqualHydrationRawChildren(container, staged)) {
36
+ throw new HydrationMismatch('server raw HTML does not match the client value')
37
+ }
38
+ } else {
39
+ container.replaceChildren(adoptRawHtml(element, staged))
40
+ }
41
+ rawHtmlHosts.add(element)
42
+ }
43
+
44
+ function hasEqualHydrationRawChildren(container: Node, expected: DocumentFragment): boolean {
45
+ const start = container.firstChild
46
+ const end = container.lastChild
47
+ if (
48
+ !(start instanceof Comment) ||
49
+ start.data !== `${HYDRATION_PREFIX}:h` ||
50
+ !(end instanceof Comment) ||
51
+ end.data !== `/${HYDRATION_PREFIX}:h`
52
+ ) {
53
+ return false
54
+ }
55
+ const actual: Node[] = []
56
+ for (let node = start.nextSibling; node !== null && node !== end; node = node.nextSibling) {
57
+ actual.push(node)
58
+ }
59
+ if (actual.length !== expected.childNodes.length) return false
60
+ for (let index = 0; index < actual.length; index += 1) {
61
+ if (!actual[index]?.isEqualNode(expected.childNodes[index] ?? null)) return false
62
+ }
63
+ return true
64
+ }
65
+
66
+ function prepareRawHtml(
67
+ element: HTMLElement,
68
+ nextValue: unknown,
69
+ previousValue: unknown,
70
+ children: readonly unknown[],
71
+ ): CompiledPropTransition | undefined {
72
+ const nextHtml = readRawHtml(element, nextValue, children)
73
+ const previousHtml = readRawHtml(element, previousValue, children)
74
+ if (nextHtml === null || nextHtml === undefined || nextHtml === previousHtml) return undefined
75
+
76
+ const staged = parseRawHtml(element, nextHtml)
77
+ const container = rawHtmlContainer(element)
78
+ const wasRawHtmlHost = rawHtmlHosts.has(element)
79
+ let previousNodes: Node[] | undefined
80
+ return [
81
+ () => {
82
+ // A reactive script `type` prop commits before the raw subtree.
83
+ assertExecutableRawHtmlTarget(element)
84
+ const adopted = adoptRawHtml(element, staged)
85
+ previousNodes = [...container.childNodes]
86
+ container.replaceChildren(adopted)
87
+ rawHtmlHosts.add(element)
88
+ },
89
+ () => {
90
+ if (previousNodes === undefined) return
91
+ container.replaceChildren(...previousNodes)
92
+ if (!wasRawHtmlHost) rawHtmlHosts.delete(element)
93
+ previousNodes = undefined
94
+ },
95
+ undefined,
96
+ undefined,
97
+ RAW_HTML_PUBLICATION_PRIORITY,
98
+ ]
99
+ }
100
+
101
+ export function validateRawHtmlRelatedProp(element: HTMLElement, name: string): void {
102
+ if (name === 'type' && rawHtmlHosts.has(element)) assertExecutableRawHtmlTarget(element)
103
+ }
104
+
105
+ function readRawHtml(element: HTMLElement, value: unknown, children: readonly unknown[]): unknown {
106
+ if (value === null || value === undefined) return undefined
107
+ assertRawHtmlPropTarget(element)
108
+ if (typeof value !== 'object' || !('__html' in value)) {
109
+ throw new Error(
110
+ DEV
111
+ ? '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://react.dev/link/dangerously-set-inner-html for more information.'
112
+ : 'V601',
113
+ )
114
+ }
115
+ const html = value['__html']
116
+ if (html === null || html === undefined) return html
117
+ if (children.some((child) => child !== null && child !== undefined)) {
118
+ throw new Error(
119
+ DEV ? 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.' : 'V602',
120
+ )
121
+ }
122
+ assertExecutableRawHtmlTarget(element)
123
+ return html
124
+ }
125
+
126
+ function assertRawHtmlPropTarget(element: HTMLElement): void {
127
+ const tag = element.localName
128
+ if (isVoidHtmlElement(tag)) {
129
+ throw new Error(
130
+ DEV
131
+ ? `${tag} is a void element tag and must neither have \`children\` nor use \`dangerouslySetInnerHTML\`.`
132
+ : 'V603',
133
+ )
134
+ }
135
+ if (tag === 'textarea') {
136
+ throw new Error(DEV ? '`dangerouslySetInnerHTML` does not make sense on <textarea>.' : 'V604')
137
+ }
138
+ }
139
+
140
+ function assertExecutableRawHtmlTarget(element: HTMLElement): void {
141
+ const tag = element.localName
142
+ if (tag === 'script' && isExecutableScript(element as HTMLScriptElement)) {
143
+ throw new Error(
144
+ DEV
145
+ ? 'dangerouslySetInnerHTML on an executable <script> is unsupported because direct-created scripts execute when connected; use a non-executable data MIME type'
146
+ : 'V605',
147
+ )
148
+ }
149
+ }
150
+
151
+ function isVoidHtmlElement(tag: string): boolean {
152
+ return (
153
+ tag === 'area' ||
154
+ tag === 'base' ||
155
+ tag === 'br' ||
156
+ tag === 'col' ||
157
+ tag === 'embed' ||
158
+ tag === 'hr' ||
159
+ tag === 'img' ||
160
+ tag === 'input' ||
161
+ tag === 'keygen' ||
162
+ tag === 'link' ||
163
+ tag === 'menuitem' ||
164
+ tag === 'meta' ||
165
+ tag === 'param' ||
166
+ tag === 'source' ||
167
+ tag === 'track' ||
168
+ tag === 'wbr'
169
+ )
170
+ }
171
+
172
+ function isExecutableScript(element: HTMLScriptElement): boolean {
173
+ const type = element.type.trim().toLowerCase().split(';', 1)[0] ?? ''
174
+ if (type === '' || type === 'module' || type === 'importmap' || type === 'speculationrules') {
175
+ return true
176
+ }
177
+ return (
178
+ /^(?:application|text)\/(?:x-)?(?:java|ecma)script(?:1\.[0-5])?$/.test(type) ||
179
+ type === 'text/jscript' ||
180
+ type === 'text/livescript'
181
+ )
182
+ }
183
+
184
+ function parseRawHtml(element: HTMLElement, html: unknown): DocumentFragment {
185
+ const stagingDocument = element.ownerDocument.implementation.createHTMLDocument()
186
+ const staging =
187
+ element.localName === 'noscript'
188
+ ? (element.cloneNode(false) as HTMLElement)
189
+ : stagingDocument.createElement(element.localName.includes('-') ? 'div' : element.localName)
190
+ Reflect.set(staging, 'innerHTML', html)
191
+ const fragment = staging.ownerDocument.createDocumentFragment()
192
+ fragment.append(...rawHtmlContainer(staging).childNodes)
193
+ return fragment
194
+ }
195
+
196
+ function rawHtmlContainer(element: HTMLElement): HTMLElement | DocumentFragment {
197
+ return element instanceof HTMLTemplateElement ? element.content : element
198
+ }
199
+
200
+ function adoptRawHtml(element: HTMLElement, staged: DocumentFragment): DocumentFragment {
201
+ if (staged.ownerDocument === element.ownerDocument) return staged
202
+ const adopted = element.ownerDocument.createDocumentFragment()
203
+ adopted.append(...staged.childNodes)
204
+ element.ownerDocument.defaultView?.customElements.upgrade(adopted)
205
+ return adopted
206
+ }
package/src/ref.ts ADDED
@@ -0,0 +1,13 @@
1
+ export interface MutableRef<T> {
2
+ current: T
3
+ }
4
+
5
+ /** Creates the plain ref object used by dependency initialization outside component hooks. */
6
+ export function createRef<T>(): MutableRef<T | null> {
7
+ return { current: null }
8
+ }
9
+
10
+ /** Creates the stable ref cell captured by a component's one-time execution. */
11
+ export function useRef<T>(initialValue: T): MutableRef<T> {
12
+ return { current: initialValue }
13
+ }
@@ -0,0 +1,63 @@
1
+ export const RENDERABLE = Symbol.for('vidact.v1.Renderable')
2
+
3
+ export interface RenderableProtocol {
4
+ readonly [RENDERABLE]: {
5
+ readonly identity: unknown
6
+ readonly input: unknown
7
+ readonly reconcile: boolean
8
+ readonly construct: (input: never) => unknown
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Cloning a renderable pulls in the element-construction module, which most
14
+ * compiled apps never touch. The renderable module registers its clone the
15
+ * first time an element capability is created, so `jsx` can reach it without
16
+ * importing it, and bundles that never build elements leave it out.
17
+ */
18
+ export type RenderableClone = (
19
+ value: RenderableProtocol,
20
+ overrides: Record<string, unknown>,
21
+ ) => unknown
22
+
23
+ let renderableClone: RenderableClone | undefined
24
+
25
+ export function installRenderableClone(clone: RenderableClone): void {
26
+ renderableClone = clone
27
+ }
28
+
29
+ export function renderableCloneHandler(): RenderableClone | undefined {
30
+ return renderableClone
31
+ }
32
+
33
+ export function isRenderableProtocol(value: unknown): value is RenderableProtocol {
34
+ return (
35
+ typeof value === 'object' &&
36
+ value !== null &&
37
+ Object.hasOwn(value, RENDERABLE) &&
38
+ typeof (value as RenderableProtocol)[RENDERABLE]?.construct === 'function'
39
+ )
40
+ }
41
+
42
+ export function materializeRenderable(value: RenderableProtocol): unknown {
43
+ const internals = value[RENDERABLE]
44
+ return internals.construct(internals.input as never)
45
+ }
46
+
47
+ export function materializeRenderableWithInput(value: RenderableProtocol, input: unknown): unknown {
48
+ return value[RENDERABLE].construct(input as never)
49
+ }
50
+
51
+ export function renderableIdentity(value: RenderableProtocol): unknown {
52
+ return value[RENDERABLE].identity
53
+ }
54
+
55
+ export function canReconcileRenderable(value: RenderableProtocol): boolean {
56
+ return value[RENDERABLE].reconcile
57
+ }
58
+
59
+ export function renderablePropsSnapshot(value: RenderableProtocol): Record<PropertyKey, unknown> {
60
+ const props = (value as RenderableProtocol & { readonly props: Record<PropertyKey, unknown> })
61
+ .props
62
+ return Object.fromEntries(Reflect.ownKeys(props).map((name) => [name, Reflect.get(props, name)]))
63
+ }