fuzor 0.5.1 → 0.5.3

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 (314) hide show
  1. package/dist/browser.d.ts +2 -0
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +1 -0
  4. package/dist/browser.js.map +1 -1
  5. package/dist/cli.js +10 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/compiler/data-document.d.ts +20 -1
  8. package/dist/compiler/data-document.d.ts.map +1 -1
  9. package/dist/compiler/data-document.js +80 -7
  10. package/dist/compiler/data-document.js.map +1 -1
  11. package/dist/compiler/docs.d.ts.map +1 -1
  12. package/dist/compiler/docs.js +49 -11
  13. package/dist/compiler/docs.js.map +1 -1
  14. package/dist/compiler/index.d.ts +2 -0
  15. package/dist/compiler/index.d.ts.map +1 -1
  16. package/dist/compiler/index.js +23 -6
  17. package/dist/compiler/index.js.map +1 -1
  18. package/dist/compiler/islands.d.ts +1 -0
  19. package/dist/compiler/islands.d.ts.map +1 -1
  20. package/dist/compiler/native-authoring.d.ts +45 -0
  21. package/dist/compiler/native-authoring.d.ts.map +1 -0
  22. package/dist/compiler/native-authoring.js +569 -0
  23. package/dist/compiler/native-authoring.js.map +1 -0
  24. package/dist/compiler/native-manifest.d.ts +33 -0
  25. package/dist/compiler/native-manifest.d.ts.map +1 -0
  26. package/dist/compiler/native-manifest.js +53 -0
  27. package/dist/compiler/native-manifest.js.map +1 -0
  28. package/dist/compiler/native-ranges.d.ts +8 -0
  29. package/dist/compiler/native-ranges.d.ts.map +1 -0
  30. package/dist/compiler/native-ranges.js +92 -0
  31. package/dist/compiler/native-ranges.js.map +1 -0
  32. package/dist/compiler/native-scripts.js +2 -2
  33. package/dist/compiler/native-scripts.js.map +1 -1
  34. package/dist/compiler/page-scripts.d.ts +6 -0
  35. package/dist/compiler/page-scripts.d.ts.map +1 -1
  36. package/dist/compiler/page-scripts.js +25 -6
  37. package/dist/compiler/page-scripts.js.map +1 -1
  38. package/dist/compiler/route-policy.d.ts +1 -0
  39. package/dist/compiler/route-policy.d.ts.map +1 -1
  40. package/dist/compiler/route-policy.js +4 -1
  41. package/dist/compiler/route-policy.js.map +1 -1
  42. package/dist/compiler/select-options.d.ts +7 -0
  43. package/dist/compiler/select-options.d.ts.map +1 -0
  44. package/dist/compiler/select-options.js +22 -0
  45. package/dist/compiler/select-options.js.map +1 -0
  46. package/dist/compiler/server-actions.d.ts +9 -0
  47. package/dist/compiler/server-actions.d.ts.map +1 -0
  48. package/dist/compiler/server-actions.js +65 -0
  49. package/dist/compiler/server-actions.js.map +1 -0
  50. package/dist/data-bindings.d.ts +4 -0
  51. package/dist/data-bindings.d.ts.map +1 -0
  52. package/dist/data-bindings.js +18 -0
  53. package/dist/data-bindings.js.map +1 -0
  54. package/dist/dom-ownership.d.ts +5 -0
  55. package/dist/dom-ownership.d.ts.map +1 -0
  56. package/dist/dom-ownership.js +49 -0
  57. package/dist/dom-ownership.js.map +1 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -0
  61. package/dist/index.js.map +1 -1
  62. package/dist/internal-specifiers.d.ts +3 -0
  63. package/dist/internal-specifiers.d.ts.map +1 -0
  64. package/dist/internal-specifiers.js +22 -0
  65. package/dist/internal-specifiers.js.map +1 -0
  66. package/dist/islands/react.d.ts +0 -6
  67. package/dist/islands/react.d.ts.map +1 -1
  68. package/dist/islands/react.js +9 -26
  69. package/dist/islands/react.js.map +1 -1
  70. package/dist/json-value.d.ts +4 -0
  71. package/dist/json-value.d.ts.map +1 -0
  72. package/dist/json-value.js +10 -0
  73. package/dist/json-value.js.map +1 -0
  74. package/dist/native-expressions.d.ts +34 -0
  75. package/dist/native-expressions.d.ts.map +1 -0
  76. package/dist/native-expressions.js +118 -0
  77. package/dist/native-expressions.js.map +1 -0
  78. package/dist/native-manifest.d.ts +7 -0
  79. package/dist/native-manifest.d.ts.map +1 -0
  80. package/dist/native-manifest.js +4 -0
  81. package/dist/native-manifest.js.map +1 -0
  82. package/dist/native-ranges.d.ts +25 -0
  83. package/dist/native-ranges.d.ts.map +1 -0
  84. package/dist/native-ranges.js +51 -0
  85. package/dist/native-ranges.js.map +1 -0
  86. package/dist/native-state.d.ts +14 -0
  87. package/dist/native-state.d.ts.map +1 -0
  88. package/dist/native-state.js +27 -0
  89. package/dist/native-state.js.map +1 -0
  90. package/dist/route-path.d.ts +2 -0
  91. package/dist/route-path.d.ts.map +1 -1
  92. package/dist/route-path.js +2 -0
  93. package/dist/route-path.js.map +1 -1
  94. package/dist/runtime/binding-updates.d.ts +11 -0
  95. package/dist/runtime/binding-updates.d.ts.map +1 -0
  96. package/dist/runtime/binding-updates.js +45 -0
  97. package/dist/runtime/binding-updates.js.map +1 -0
  98. package/dist/runtime/data.d.ts +28 -1
  99. package/dist/runtime/data.d.ts.map +1 -1
  100. package/dist/runtime/data.js +101 -25
  101. package/dist/runtime/data.js.map +1 -1
  102. package/dist/runtime/docs-search.d.ts +20 -4
  103. package/dist/runtime/docs-search.d.ts.map +1 -1
  104. package/dist/runtime/docs-search.js +135 -159
  105. package/dist/runtime/docs-search.js.map +1 -1
  106. package/dist/runtime/docs-ui.d.ts +3 -1
  107. package/dist/runtime/docs-ui.d.ts.map +1 -1
  108. package/dist/runtime/docs-ui.js +19 -29
  109. package/dist/runtime/docs-ui.js.map +1 -1
  110. package/dist/runtime/index.d.ts +1 -0
  111. package/dist/runtime/index.d.ts.map +1 -1
  112. package/dist/runtime/islands.d.ts.map +1 -1
  113. package/dist/runtime/islands.js +2 -1
  114. package/dist/runtime/islands.js.map +1 -1
  115. package/dist/runtime/native-graph.d.ts +15 -0
  116. package/dist/runtime/native-graph.d.ts.map +1 -0
  117. package/dist/runtime/native-graph.js +103 -0
  118. package/dist/runtime/native-graph.js.map +1 -0
  119. package/dist/runtime/native-manifest.d.ts +17 -0
  120. package/dist/runtime/native-manifest.d.ts.map +1 -0
  121. package/dist/runtime/native-manifest.js +111 -0
  122. package/dist/runtime/native-manifest.js.map +1 -0
  123. package/dist/runtime/native-ranges.d.ts +8 -0
  124. package/dist/runtime/native-ranges.d.ts.map +1 -0
  125. package/dist/runtime/native-ranges.js +100 -0
  126. package/dist/runtime/native-ranges.js.map +1 -0
  127. package/dist/runtime/native-stream-reader.d.ts +7 -0
  128. package/dist/runtime/native-stream-reader.d.ts.map +1 -0
  129. package/dist/runtime/native-stream-reader.js +79 -0
  130. package/dist/runtime/native-stream-reader.js.map +1 -0
  131. package/dist/runtime/native-stream.d.ts +5 -0
  132. package/dist/runtime/native-stream.d.ts.map +1 -0
  133. package/dist/runtime/native-stream.js +14 -0
  134. package/dist/runtime/native-stream.js.map +1 -0
  135. package/dist/runtime/native.d.ts +41 -0
  136. package/dist/runtime/native.d.ts.map +1 -1
  137. package/dist/runtime/native.js +176 -55
  138. package/dist/runtime/native.js.map +1 -1
  139. package/dist/runtime/page-scripts.d.ts.map +1 -1
  140. package/dist/runtime/page-scripts.js +8 -3
  141. package/dist/runtime/page-scripts.js.map +1 -1
  142. package/dist/runtime/route-views.d.ts +15 -0
  143. package/dist/runtime/route-views.d.ts.map +1 -0
  144. package/dist/runtime/route-views.js +133 -0
  145. package/dist/runtime/route-views.js.map +1 -0
  146. package/dist/runtime/router-embedded.d.ts +1 -0
  147. package/dist/runtime/router-embedded.d.ts.map +1 -1
  148. package/dist/runtime/router-navigation-api.d.ts +1 -0
  149. package/dist/runtime/router-navigation-api.d.ts.map +1 -1
  150. package/dist/runtime/router.d.ts +1 -0
  151. package/dist/runtime/router.d.ts.map +1 -1
  152. package/dist/runtime/router.js +11 -2
  153. package/dist/runtime/router.js.map +1 -1
  154. package/dist/runtime/server-actions.d.ts +5 -0
  155. package/dist/runtime/server-actions.d.ts.map +1 -0
  156. package/dist/runtime/server-actions.js +79 -0
  157. package/dist/runtime/server-actions.js.map +1 -0
  158. package/dist/server-actions.d.ts +18 -0
  159. package/dist/server-actions.d.ts.map +1 -0
  160. package/dist/server-actions.js +138 -0
  161. package/dist/server-actions.js.map +1 -0
  162. package/dist/server-components/closures.d.ts +1 -12
  163. package/dist/server-components/closures.d.ts.map +1 -1
  164. package/dist/server-components/closures.js +0 -75
  165. package/dist/server-components/closures.js.map +1 -1
  166. package/dist/server-components/continuation.d.ts +7 -0
  167. package/dist/server-components/continuation.d.ts.map +1 -0
  168. package/dist/server-components/continuation.js +17 -0
  169. package/dist/server-components/continuation.js.map +1 -0
  170. package/dist/server-components/environment.js +1 -1
  171. package/dist/server-components/environment.js.map +1 -1
  172. package/dist/server-components/protocol.d.ts +5 -1
  173. package/dist/server-components/protocol.d.ts.map +1 -1
  174. package/dist/server-components/protocol.js +9 -1
  175. package/dist/server-components/protocol.js.map +1 -1
  176. package/dist/server-components/server.d.ts +4 -7
  177. package/dist/server-components/server.d.ts.map +1 -1
  178. package/dist/server-components/server.js +2 -185
  179. package/dist/server-components/server.js.map +1 -1
  180. package/dist/server-components/stream-server.d.ts +6 -0
  181. package/dist/server-components/stream-server.d.ts.map +1 -0
  182. package/dist/server-components/stream-server.js +197 -0
  183. package/dist/server-components/stream-server.js.map +1 -0
  184. package/dist/server-components/stream.d.ts +16 -3
  185. package/dist/server-components/stream.d.ts.map +1 -1
  186. package/dist/server-components/stream.js +31 -4
  187. package/dist/server-components/stream.js.map +1 -1
  188. package/dist/server-components/transport.d.ts +20 -0
  189. package/dist/server-components/transport.d.ts.map +1 -0
  190. package/dist/server-components/transport.js +2 -0
  191. package/dist/server-components/transport.js.map +1 -0
  192. package/dist/server-components/updates.d.ts +56 -0
  193. package/dist/server-components/updates.d.ts.map +1 -0
  194. package/dist/server-components/updates.js +319 -0
  195. package/dist/server-components/updates.js.map +1 -0
  196. package/dist/server-data-protocol.d.ts +14 -0
  197. package/dist/server-data-protocol.d.ts.map +1 -0
  198. package/dist/server-data-protocol.js +15 -0
  199. package/dist/server-data-protocol.js.map +1 -0
  200. package/dist/server-data.d.ts +18 -0
  201. package/dist/server-data.d.ts.map +1 -1
  202. package/dist/server-data.js.map +1 -1
  203. package/dist/server-document.d.ts +21 -0
  204. package/dist/server-document.d.ts.map +1 -0
  205. package/dist/server-document.js +3 -0
  206. package/dist/server-document.js.map +1 -0
  207. package/dist/server-native-document.d.ts +13 -0
  208. package/dist/server-native-document.d.ts.map +1 -0
  209. package/dist/server-native-document.js +154 -0
  210. package/dist/server-native-document.js.map +1 -0
  211. package/dist/server-native-stream.d.ts +5 -0
  212. package/dist/server-native-stream.d.ts.map +1 -0
  213. package/dist/server-native-stream.js +39 -0
  214. package/dist/server-native-stream.js.map +1 -0
  215. package/dist/server-native.d.ts +24 -0
  216. package/dist/server-native.d.ts.map +1 -0
  217. package/dist/server-native.js +70 -0
  218. package/dist/server-native.js.map +1 -0
  219. package/dist/server-reactivity.d.ts +13 -0
  220. package/dist/server-reactivity.d.ts.map +1 -0
  221. package/dist/server-reactivity.js +49 -0
  222. package/dist/server-reactivity.js.map +1 -0
  223. package/dist/server.d.ts +11 -5
  224. package/dist/server.d.ts.map +1 -1
  225. package/dist/server.js +68 -16
  226. package/dist/server.js.map +1 -1
  227. package/dist/ui/behaviors/accordion.d.ts.map +1 -1
  228. package/dist/ui/behaviors/accordion.js +4 -3
  229. package/dist/ui/behaviors/accordion.js.map +1 -1
  230. package/dist/ui/behaviors/collapsible.d.ts.map +1 -1
  231. package/dist/ui/behaviors/collapsible.js +2 -1
  232. package/dist/ui/behaviors/collapsible.js.map +1 -1
  233. package/dist/ui/behaviors/dialog.d.ts.map +1 -1
  234. package/dist/ui/behaviors/dialog.js +5 -4
  235. package/dist/ui/behaviors/dialog.js.map +1 -1
  236. package/dist/ui/behaviors/menu.d.ts.map +1 -1
  237. package/dist/ui/behaviors/menu.js +4 -3
  238. package/dist/ui/behaviors/menu.js.map +1 -1
  239. package/dist/ui/behaviors/select.d.ts +23 -1
  240. package/dist/ui/behaviors/select.d.ts.map +1 -1
  241. package/dist/ui/behaviors/select.js +81 -11
  242. package/dist/ui/behaviors/select.js.map +1 -1
  243. package/dist/ui/behaviors/switch.d.ts.map +1 -1
  244. package/dist/ui/behaviors/switch.js +2 -1
  245. package/dist/ui/behaviors/switch.js.map +1 -1
  246. package/dist/ui/behaviors/toolbar.d.ts.map +1 -1
  247. package/dist/ui/behaviors/toolbar.js +2 -1
  248. package/dist/ui/behaviors/toolbar.js.map +1 -1
  249. package/dist/ui/catalogue.d.ts +1 -1
  250. package/dist/ui/components/accordion/accordion.d.ts.map +1 -1
  251. package/dist/ui/components/accordion/accordion.js +3 -2
  252. package/dist/ui/components/accordion/accordion.js.map +1 -1
  253. package/dist/ui/components/collapsible/collapsible.d.ts.map +1 -1
  254. package/dist/ui/components/collapsible/collapsible.js +2 -1
  255. package/dist/ui/components/collapsible/collapsible.js.map +1 -1
  256. package/dist/ui/components/dialog/dialog.d.ts.map +1 -1
  257. package/dist/ui/components/dialog/dialog.js +4 -3
  258. package/dist/ui/components/dialog/dialog.js.map +1 -1
  259. package/dist/ui/components/navigation/breadcrumbs.d.ts.map +1 -1
  260. package/dist/ui/components/navigation/breadcrumbs.js +2 -1
  261. package/dist/ui/components/navigation/breadcrumbs.js.map +1 -1
  262. package/dist/ui/components/select/select.d.ts +20 -0
  263. package/dist/ui/components/select/select.d.ts.map +1 -1
  264. package/dist/ui/components/select/select.js +20 -0
  265. package/dist/ui/components/select/select.js.map +1 -1
  266. package/dist/ui/components/tabs/tabs.d.ts.map +1 -1
  267. package/dist/ui/components/tabs/tabs.js +6 -5
  268. package/dist/ui/components/tabs/tabs.js.map +1 -1
  269. package/dist/ui/core/brand.d.ts +33 -0
  270. package/dist/ui/core/brand.d.ts.map +1 -0
  271. package/dist/ui/core/brand.js +96 -0
  272. package/dist/ui/core/brand.js.map +1 -0
  273. package/dist/ui/core/component-styles.d.ts.map +1 -1
  274. package/dist/ui/core/component-styles.js +14 -8
  275. package/dist/ui/core/component-styles.js.map +1 -1
  276. package/dist/ui/core/element.d.ts.map +1 -1
  277. package/dist/ui/core/element.js +2 -3
  278. package/dist/ui/core/element.js.map +1 -1
  279. package/dist/ui/core/index.d.ts +1 -0
  280. package/dist/ui/core/index.d.ts.map +1 -1
  281. package/dist/ui/core/index.js +1 -0
  282. package/dist/ui/core/index.js.map +1 -1
  283. package/dist/ui/core/layout-styles.d.ts.map +1 -1
  284. package/dist/ui/core/layout-styles.js +12 -6
  285. package/dist/ui/core/layout-styles.js.map +1 -1
  286. package/dist/ui/core/parts.d.ts.map +1 -1
  287. package/dist/ui/core/parts.js +5 -4
  288. package/dist/ui/core/parts.js.map +1 -1
  289. package/dist/ui/core/portal.d.ts.map +1 -1
  290. package/dist/ui/core/portal.js +17 -1
  291. package/dist/ui/core/portal.js.map +1 -1
  292. package/dist/ui/machine-state.d.ts +11 -11
  293. package/dist/ui/state.d.ts +1 -1
  294. package/dist/vite.d.ts +5 -10
  295. package/dist/vite.d.ts.map +1 -1
  296. package/dist/vite.js +119 -116
  297. package/dist/vite.js.map +1 -1
  298. package/package.json +2 -12
  299. package/dist/server-components/browser.d.ts +0 -75
  300. package/dist/server-components/browser.d.ts.map +0 -1
  301. package/dist/server-components/browser.js +0 -558
  302. package/dist/server-components/browser.js.map +0 -1
  303. package/dist/server-components/compiler.d.ts +0 -6
  304. package/dist/server-components/compiler.d.ts.map +0 -1
  305. package/dist/server-components/compiler.js +0 -118
  306. package/dist/server-components/compiler.js.map +0 -1
  307. package/dist/server-components/navigation.d.ts +0 -10
  308. package/dist/server-components/navigation.d.ts.map +0 -1
  309. package/dist/server-components/navigation.js +0 -105
  310. package/dist/server-components/navigation.js.map +0 -1
  311. package/dist/server-components/references.d.ts +0 -22
  312. package/dist/server-components/references.d.ts.map +0 -1
  313. package/dist/server-components/references.js +0 -94
  314. package/dist/server-components/references.js.map +0 -1
package/dist/vite.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { inlineDocumentAssets } from "./compiler/inline-assets.js";
2
- import { compileDataDocument } from './compiler/data-document.js';
2
+ import { compileDataDocument, compileInitialDocument } from './compiler/data-document.js';
3
3
  import { parseRoutePolicy } from './compiler/route-policy.js';
4
4
  import { extractPageScripts, compilePageScript } from "./compiler/page-scripts.js";
5
5
  import { compileNativeScript } from "./compiler/native-scripts.js";
6
+ import { renderNativeBinding } from "./native-ranges.js";
7
+ import { compileAuthoredNativeScript, prepareNativeScripts, htmlServerPlugin, assertNoServerScripts, resolveHtmlServer, loadHtmlServer } from "./compiler/native-authoring.js";
8
+ import { emitNativeManifests } from "./compiler/native-manifest.js";
6
9
  import { compileWorkflows, workflowPlugin } from "./workflow/compiler.js";
10
+ import { resolveInternalSpecifier } from "./internal-specifiers.js";
7
11
  import { assertBrowserViewImport } from "./server-components/environment.js";
8
- import { replaceClosures } from "./server-components/closures.js";
9
- import { discoverViewReferences, browserReferencesCode } from "./server-components/references.js";
10
12
  import { randomUUID } from "node:crypto";
11
- import { compileFragment, compiledFragmentsPlugin } from "./server-components/compiler.js";
12
13
  import { makeRouteMatches } from "./runtime/route-matches.js";
13
14
  import { makeClientRouteMatcher } from "./runtime/client-routes.js";
14
15
  import { errorHtml, notFoundHtml } from "./boundaries.js";
@@ -35,6 +36,7 @@ import { toWebRequest, writeWebResponse } from "./server/node.js";
35
36
  import { routeTypes } from "./runtime/href.js";
36
37
  import { writeElectronScaffold } from "./electron.js";
37
38
  import { createEnvironmentModuleRuntime, detectViteEnvironmentApi, invalidateEnvironmentModule, normalizeEnvironmentConfiguration } from "./vite-environments.js";
39
+ import { validateBrandPrefix } from "./ui/core/brand.js";
38
40
  const virtualId = "virtual:fuzor/client";
39
41
  const rendererModules = {
40
42
  "virtual:fuzor/compiler/react": 'export { createElement } from "react"; export { renderToString, renderToStaticMarkup } from "react-dom/server";',
@@ -152,7 +154,12 @@ function serverManifest(project, target, template, base, fallbacks, finish = htm
152
154
  ? { document: finish(template), routeDocuments: Object.fromEntries(project.routes.map(route => [route.path, finish(applyDocumentMetadata(replaceOutlet(template, outletFor(project, "spa", route.path, fallbacks)), pageHead(fallbacks.head, route.metadata ?? {})))])), routes: Object.fromEntries(project.routes.map((route) => [route.path, true])), ...(clientRoutes ? { clientRoutes, ...(assetDirectory ? { assetDirectory } : {}) } : {}), ...shared }
153
155
  : { documents: Object.fromEntries(project.routes.map((route) => [route.path, finish(applyDocumentMetadata(replaceOutlet(template, outletFor(project, "mpa", route.path, fallbacks, mixed)), pageHead(fallbacks.head, route.metadata ?? {})))])), ...shared };
154
156
  const documents = "documents" in manifest ? manifest.documents : manifest.routeDocuments ?? {};
155
- return { ...manifest, dataBindings: Object.fromEntries(project.routes.filter(route => route.policy?.rendering === "request").map(route => [route.path, compileDataDocument(documents[route.path])])) };
157
+ const requestDocuments = project.routes.filter(route => route.policy?.rendering === "request");
158
+ return { ...manifest, dataBindings: Object.fromEntries(requestDocuments.map(route => [route.path, compileDataDocument(documents[route.path])])),
159
+ initialDocuments: Object.fromEntries(requestDocuments.flatMap(route => {
160
+ const program = compileInitialDocument(documents[route.path]);
161
+ return program ? [[route.path, program]] : [];
162
+ })) };
156
163
  }
157
164
  /** Loads each page's `server.ts` default export and checks it was declared with `defineServerData`. */
158
165
  async function loadRouteDataModules(project, load, root) {
@@ -287,13 +294,6 @@ function replaceOutlet(document, content) {
287
294
  throw new Error(`index.html must contain ${outletStart} and ${outletEnd}.`);
288
295
  return document.slice(0, start + outletStart.length) + content + document.slice(end);
289
296
  }
290
- function serverViewDocument(html) {
291
- let found = false;
292
- const document = html.replace(/(<fuzor-router\b[^>]*>)[\s\S]*?<\/fuzor-router>/, (_match, open) => { found = true; return open + "<!--fuzor-view--></fuzor-router>"; });
293
- if (!found || !document.includes("</body>"))
294
- throw new Error("Server views require a compiled outlet and a document body.");
295
- return document.replace("</body>", "<!--fuzor-snapshot--></body>");
296
- }
297
297
  /**
298
298
  * The plugin creates private Vite servers to load island and `paths` modules. Their config file loads
299
299
  * this plugin again; those nested instances only provide virtual modules and never compile the project.
@@ -304,17 +304,19 @@ const nestedServer = new AsyncLocalStorage();
304
304
  * open an HMR WebSocket (two would compete for its port, and a failed one never closes) and must not share the
305
305
  * dev server's dependency cache (each would re-optimize and invalidate the other's pre-bundled files).
306
306
  */
307
- export function nestedServerConfig(config) {
307
+ export function nestedServerConfig(config, nativeModules) {
308
308
  return {
309
309
  root: config.root,
310
310
  configFile: config.configFile ?? false,
311
- plugins: [workflowPlugin(), {
311
+ plugins: [{ name: "fuzor:internal-specifiers", enforce: "pre", resolveId: id => resolveInternalSpecifier(id) }, htmlServerPlugin(nativeModules), workflowPlugin(), {
312
312
  name: "fuzor:nested-renderers",
313
313
  resolveId(id) { if (Object.hasOwn(rendererModules, id))
314
314
  return `\0${id}`; },
315
315
  load(id) { if (id.startsWith("\0") && Object.hasOwn(rendererModules, id.slice(1)))
316
316
  return rendererModules[id.slice(1)]; }
317
317
  }],
318
+ // Fuzor's server runtime is one module graph: compiler-owned modules and `fuzor/server` must not load twice.
319
+ ssr: { noExternal: ["fuzor"] },
318
320
  server: { middlewareMode: true, hmr: false, ws: false },
319
321
  cacheDir: path.join(config.cacheDir, "fuzor-nested"),
320
322
  optimizeDeps: { noDiscovery: true, include: [] },
@@ -380,20 +382,6 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
380
382
  const electron = options.electron ?? envElectron ?? requestedTarget === "electron";
381
383
  const target = (requestedTarget === "electron" ? "spa" : requestedTarget);
382
384
  const serverReasons = serverModules.map(file => file.startsWith("api/") ? `${file} defines an API endpoint` : file.endsWith(".html") ? `${file} declares request rendering` : `${file} defines server data`);
383
- if (options.serverComponents) {
384
- makeClientRouteMatcher(Object.fromEntries(options.serverComponents.routes.map(route => [route, "/"])));
385
- for (const entry of [options.serverComponents.server, options.serverComponents.client, options.serverComponents.references].filter((value) => value !== undefined)) {
386
- if (!entry || path.isAbsolute(entry) || entry.split(/[\\/]/).includes(".."))
387
- throw new Error("Server component entries must be paths inside the project.");
388
- }
389
- if (!options.serverComponents.client && !options.serverComponents.references)
390
- throw new Error("Server components require a client entry or generated reference directory.");
391
- if (!options.serverComponents.routes.length)
392
- throw new Error("Server components need at least one route.");
393
- if (options.navigation === "embedded")
394
- throw new Error("Server components require HTTP navigation.");
395
- serverReasons.push(`${options.serverComponents.server} defines server components`);
396
- }
397
385
  const inline = electron || packaged ? true : options.inline ?? envInline ?? false;
398
386
  if (inline && target !== "spa")
399
387
  throw new Error("Inline output is only supported for the SPA target.");
@@ -418,27 +406,11 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
418
406
  return { target, mixed, backendOutDir: ".fuzor/dist-backend", platform, server, inline, outDir: electron ? ".fuzor/dist-desktop" : platform !== "web" ? `.fuzor/dist-${platform}` : mixed ? ".fuzor/web/dist" : `.fuzor/web/dist-${target}`, serverReasons, adapter, ...(electron ? { electron: true } : {}) };
419
407
  }
420
408
  export function fuzorPlugin(options = {}) {
409
+ if (options.ui?.brand !== undefined)
410
+ validateBrandPrefix(options.ui.brand);
421
411
  const nested = nestedServer.getStore() === true;
412
+ const nativeServerModules = new Map();
422
413
  let viewBuildId = randomUUID();
423
- let referencesPromise;
424
- const references = () => referencesPromise ??= discoverViewReferences(vite.root, options.serverComponents.references);
425
- const referenceStub = (file, refs) => {
426
- const relative = path.relative(vite.root, file).split(path.sep).join("/");
427
- const client = refs.clients.find(ref => ref.file === relative);
428
- if (client)
429
- return `export default ${JSON.stringify({ id: client.id })};`;
430
- const closures = refs.closures.filter(ref => ref.file === relative);
431
- if (closures.length)
432
- return readFile(file, "utf8").then(code => replaceClosures(code, closures));
433
- const handlers = refs.handlers.filter(ref => ref.file === relative);
434
- return handlers.length ? handlers.map(ref => `export const ${ref.name}=${JSON.stringify({ id: ref.id })};`).join("\n") : undefined;
435
- };
436
- const generatedViews = async (views) => {
437
- if (!options.serverComponents?.references)
438
- return views;
439
- const refs = await references();
440
- return { ...views, manifest: { buildId: viewBuildId, templates: refs.templates }, components: refs.clients.map(ref => ref.id), handlers: [...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)] };
441
- };
442
414
  const configuredEnvironments = normalizeEnvironmentConfiguration(options.experimental?.environments);
443
415
  const environmentNames = {
444
416
  client: configuredEnvironments.client ?? "client",
@@ -481,6 +453,8 @@ export function fuzorPlugin(options = {}) {
481
453
  let project;
482
454
  let pageScripts = [];
483
455
  const nativeHandlers = new Map();
456
+ const authoredNative = new Map();
457
+ let nativeAdmissions = new Map();
484
458
  let inlineModuleFiles = [];
485
459
  let scriptRoutes = [];
486
460
  const routeScriptId = (route) => `virtual:fuzor/route-client/${encodeURIComponent(route.path)}/${route.path === "/" ? "index" : route.path.split("/").filter(Boolean).join("-").replace(/[^a-zA-Z0-9_-]/g, "_")}-client.js`;
@@ -503,7 +477,7 @@ export function fuzorPlugin(options = {}) {
503
477
  let solidHydrationScript = "";
504
478
  const pending = (async () => {
505
479
  let owned;
506
- const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite)));
480
+ const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite, nativeServerModules)));
507
481
  const moduleServer = () => devServer ? Promise.resolve(devServer) : owned ??= createModuleServer();
508
482
  // client:none analysis reads client transforms without HMR code, which the dev server would add. It has its
509
483
  // own server: client transforms wait for pending requests to settle, and preview renders on the module
@@ -672,6 +646,8 @@ export function fuzorPlugin(options = {}) {
672
646
  const compileRaw = async (signal, loadModule) => {
673
647
  let compiled = await compileProject(vite.root, { ...(options.markdown ? { markdown: options.markdown } : {}), ...(options.redirects ? { redirects: options.redirects } : {}), platform: profile.platform, signal, loadModule, cache: compileCache })
674
648
  .catch(async (error) => { throw await withViteLocation(error); });
649
+ nativeServerModules.clear();
650
+ compiled = await prepareNativeScripts(compiled, vite.root, loadModule, nativeServerModules);
675
651
  const entry = options.entry ?? process.env.FUZOR_ENTRY;
676
652
  if (entry) {
677
653
  if (!inline || target !== "spa")
@@ -688,15 +664,20 @@ export function fuzorPlugin(options = {}) {
688
664
  if (!compiled.routes.some(route => route.path === "/"))
689
665
  throw new Error("Application entry must have a static root template; request-rendered routes require an HTTP rendering deployment.");
690
666
  }
667
+ const inferredServer = compiled.routes.filter(route => route.serverData || route.policy?.rendering === "request");
668
+ if (inferredServer.length) {
669
+ profile = resolveFuzorProfile(options, process.env, [...new Set([...serverModuleFiles, ...inferredServer.map(route => path.relative(vite.root, route.source).split(path.sep).join("/"))])]);
670
+ withServer = profile.server;
671
+ }
691
672
  const deployment = options.deployment ?? process.env.FUZOR_DEPLOYMENT;
692
673
  const requestRoutes = compiled.routes.filter(route => route.policy?.rendering === "request");
693
674
  if (deployment && !["static", "github-pages", "node", "cloudflare"].includes(deployment))
694
675
  throw new Error("Unknown Fuzor deployment target: " + deployment);
695
- if ((deployment === "static" || deployment === "github-pages") && (requestRoutes.length || options.serverComponents?.routes.length))
696
- throw new Error(`Route ${(requestRoutes[0]?.path ?? options.serverComponents.routes[0])} requires request-time server rendering. ${deployment} supports static files only. Pre-render this route, regenerate and republish its files, or choose node/cloudflare rendering. Remote API calls from static SPAs are allowed.`);
676
+ if ((deployment === "static" || deployment === "github-pages") && requestRoutes.length)
677
+ throw new Error(`Route ${requestRoutes[0].path} requires request-time server rendering. ${deployment} supports static files only. Pre-render this route, regenerate and republish its files, or choose node/cloudflare rendering. Remote API calls from static SPAs are allowed.`);
697
678
  for (const route of requestRoutes)
698
- if (route.requestPattern && route.policy?.navigation === "spa" && !options.serverComponents?.routes.some(pattern => makeClientRouteMatcher({ [pattern]: "/" })(route.path)))
699
- throw new Error(`${route.source}: unenumerated request routes inside an SPA require a server-component renderer. Use navigation=document or register serverComponents.`);
679
+ if (route.requestPattern && route.policy?.navigation === "spa" && !route.serverData?.startsWith("virtual:fuzor/native-data/"))
680
+ throw new Error(`${route.source}: unenumerated request routes inside an SPA need an HTML server component. Add one or use navigation=document.`);
700
681
  if (options.routeTree) {
701
682
  if (target !== "spa" && !profile.mixed)
702
683
  throw new Error("routeTree requires a SPA target.");
@@ -716,8 +697,8 @@ export function fuzorPlugin(options = {}) {
716
697
  check(options.routeTree);
717
698
  }
718
699
  if (options.clientRoutes && Object.keys(options.clientRoutes).length) {
719
- if (target !== "spa" || !options.serverComponents && (withServer || apiFiles.length > 0 || serverModuleFiles.length > 0))
720
- throw new Error("clientRoutes requires a static SPA target.");
700
+ if (target !== "spa")
701
+ throw new Error("clientRoutes requires an SPA target.");
721
702
  makeClientRouteMatcher(options.clientRoutes);
722
703
  for (const target of Object.values(options.clientRoutes)) {
723
704
  const route = compiled.routes.find(route => route.path === routeKey(target));
@@ -735,7 +716,18 @@ export function fuzorPlugin(options = {}) {
735
716
  const extracted = extractPageScripts(compiled, vite.root);
736
717
  pageScripts = extracted.scripts;
737
718
  scriptRoutes = extracted.routes;
738
- return extracted.project;
719
+ authoredNative.clear();
720
+ nativeHandlers.clear();
721
+ for (const script of pageScripts)
722
+ if (script.native) {
723
+ const compiled = compileAuthoredNativeScript(script.code ?? await readFile(script.source, "utf8"), script, viewBuildId);
724
+ authoredNative.set(script.id, compiled);
725
+ for (const handler of compiled.handlers)
726
+ nativeHandlers.set(handler.id, handler);
727
+ }
728
+ const emitted = emitNativeManifests(extracted.project, scriptRoutes, pageScripts, authoredNative, viewBuildId);
729
+ nativeAdmissions = emitted.admissions;
730
+ return emitted.project;
739
731
  };
740
732
  const scanIslands = async () => {
741
733
  return Effect.runPromise(scanIslandModulesEffect(vite.root, Object.keys(options.runtimes ?? {})));
@@ -743,7 +735,6 @@ export function fuzorPlugin(options = {}) {
743
735
  const refresh = async (server) => {
744
736
  const version = ++revision;
745
737
  viewBuildId = randomUUID();
746
- referencesPromise = undefined;
747
738
  compilation?.abort();
748
739
  project = undefined;
749
740
  projectPromise = undefined;
@@ -810,8 +801,8 @@ export function fuzorPlugin(options = {}) {
810
801
  profile = resolveFuzorProfile(options, process.env, serverModuleFiles);
811
802
  ({ target, inline } = profile);
812
803
  withServer = profile.server;
813
- if (options.clientRoutes && Object.keys(options.clientRoutes).length && (target !== "spa" || withServer && !options.serverComponents))
814
- throw new Error("clientRoutes requires a static SPA target.");
804
+ if (options.clientRoutes && Object.keys(options.clientRoutes).length && target !== "spa")
805
+ throw new Error("clientRoutes requires an SPA target.");
815
806
  const packageJson = await readFile(path.join(path.resolve(config.root ?? process.cwd()), "package.json"), "utf8").then(text => JSON.parse(text), () => ({}));
816
807
  const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
817
808
  const include = ["Cause", "Context", "Deferred", "Effect", "Exit", "Fiber", "Layer", "Schema", "Scope", "reactivity/Atom", "reactivity/AtomRegistry"].map(module => `fuzor > effect/${module}`);
@@ -850,11 +841,14 @@ export function fuzorPlugin(options = {}) {
850
841
  }
851
842
  },
852
843
  async resolveId(id, importer, resolveOptions) {
844
+ const internalFile = resolveInternalSpecifier(id);
845
+ if (internalFile)
846
+ return internalFile;
847
+ const serverModule = resolveHtmlServer(id, resolveOptions.ssr, nativeServerModules);
848
+ if (serverModule)
849
+ return serverModule;
853
850
  if (!resolveOptions.ssr)
854
851
  assertBrowserViewImport(id, importer);
855
- if (id === "fuzor/server-components/server" && !resolveOptions.ssr) {
856
- throw new Error("The server-component evaluator is server-only. Import browser runtime and protocol separately.");
857
- }
858
852
  if (id.startsWith("virtual:fuzor/route-client/"))
859
853
  return `\0${id}`;
860
854
  if (id.startsWith("virtual:fuzor/page-script/"))
@@ -945,6 +939,8 @@ export function fuzorPlugin(options = {}) {
945
939
  }
946
940
  },
947
941
  async load(id, loadOptions) {
942
+ if (id.includes("?fuzor-server-script=") || nativeServerModules.has(id))
943
+ return loadHtmlServer(id, nativeServerModules, file => this.addWatchFile(file));
948
944
  if (id.startsWith("\0virtual:fuzor/route-client/")) {
949
945
  await loadProject();
950
946
  const route = scriptRoutes.find(route => `\0${routeScriptId(route)}` === id);
@@ -959,6 +955,8 @@ export function fuzorPlugin(options = {}) {
959
955
  throw new Error("Unknown Fuzor page script");
960
956
  this.addWatchFile(script.source);
961
957
  const source = script.code ?? await readFile(script.source, "utf8");
958
+ if (script.native)
959
+ throw new Error("Authored native declarations are adopted from HTML, not executable descriptor modules");
962
960
  const native = compileNativeScript(source, script);
963
961
  if (native) {
964
962
  for (const handler of native.handlers)
@@ -974,31 +972,6 @@ export function fuzorPlugin(options = {}) {
974
972
  throw new Error("Unknown native handler");
975
973
  return (await import("vite")).transformWithEsbuild(handler.code, handler.source, { loader: handler.language, target: "es2022" });
976
974
  }
977
- if (id.startsWith("\0fuzor:closure:")) {
978
- const refs = await references();
979
- const closure = refs.closures.find(ref => id === `\0fuzor:closure:${ref.id}.ts`);
980
- if (!closure)
981
- throw new Error("Unknown resumable closure reference");
982
- return (await import("vite")).transformWithEsbuild(closure.code, id, { loader: "ts", target: "es2022" }).then(result => result.code);
983
- }
984
- if (options.serverComponents?.references) {
985
- const refs = await references();
986
- if (id === "\0virtual:fuzor/views") {
987
- if (loadOptions?.ssr)
988
- return `export const manifest=${JSON.stringify({ buildId: viewBuildId, templates: refs.templates })}; export const components=${JSON.stringify(refs.clients.map(ref => ref.id))}; export const handlers=${JSON.stringify([...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)])};`;
989
- return browserReferencesCode(refs, viewBuildId, file => "/" + file);
990
- }
991
- if (loadOptions?.ssr) {
992
- const stub = referenceStub(id.split("?")[0], refs);
993
- if (stub)
994
- return stub;
995
- }
996
- }
997
- if (id.startsWith("\0fuzor:fragment:")) {
998
- const file = id.slice("\0fuzor:fragment:".length, -3);
999
- this.addWatchFile(file);
1000
- return readFile(file, "utf8").then(html => `export default ${JSON.stringify(compileFragment(html))};`);
1001
- }
1002
975
  if (Object.hasOwn(rendererModules, id.slice(1)))
1003
976
  return rendererModules[id.slice(1)];
1004
977
  const runtimeModule = runtimeModules.get(id);
@@ -1007,7 +980,7 @@ export function fuzorPlugin(options = {}) {
1007
980
  if (!loadOptions?.ssr && !nested && vite) {
1008
981
  const file = id.split("?")[0];
1009
982
  const relative = path.relative(vite.root, file).split(path.sep).join("/");
1010
- if (options.serverComponents && path.resolve(vite.root, options.serverComponents.server) === file || serverModuleFiles.includes(relative) || relative.startsWith("api/") || /\.server\.[cm]?[jt]sx?$/.test(relative)) {
983
+ if (serverModuleFiles.includes(relative) || relative.startsWith("api/") || /\.server\.[cm]?[jt]sx?$/.test(relative)) {
1011
984
  throw new Error(`${relative} is server-only and cannot be imported by browser code. Read its data with <fuzor-data> or routeData() instead.`);
1012
985
  }
1013
986
  }
@@ -1015,7 +988,32 @@ export function fuzorPlugin(options = {}) {
1015
988
  return undefined;
1016
989
  await loadProject();
1017
990
  const mixedClient = profile.mixed && project?.routes.some(route => route.policy?.navigation === "spa");
1018
- const scriptRegistry = scriptRoutes.map(route => `${JSON.stringify(route.path)}: () => import(${JSON.stringify(routeScriptId(route))}).catch(reloadOnStaleDeployment)`).join(",\n");
991
+ const scriptRegistry = scriptRoutes.map(route => nativeAdmissions.has(route.path)
992
+ ? `${JSON.stringify(route.path)}: async () => readNativeManifest(outlet, ${JSON.stringify(route.path)}, ${JSON.stringify(viewBuildId)}, ${JSON.stringify(nativeAdmissions.get(route.path))}, nativeLoaders)`
993
+ : `${JSON.stringify(route.path)}: () => import(${JSON.stringify(routeScriptId(route))}).catch(reloadOnStaleDeployment)`).join(",\n");
994
+ const handlerLoaders = new Map();
995
+ const graphLoaders = new Map();
996
+ const cssImports = new Set();
997
+ for (const script of pageScripts) {
998
+ const compiled = authoredNative.get(script.id);
999
+ if (!compiled)
1000
+ continue;
1001
+ this.addWatchFile(script.source);
1002
+ for (const css of compiled.css) {
1003
+ const resolved = await this.resolve(css, script.source);
1004
+ if (!resolved)
1005
+ throw new Error(`${script.source}: cannot resolve native stylesheet ${css}`);
1006
+ cssImports.add(`import ${JSON.stringify(resolved.id)};`);
1007
+ }
1008
+ for (const [name, id] of compiled.actionIds)
1009
+ if ((script.native.events.some(event => event.action === name) || script.native.effects?.some(effect => effect.action === name)))
1010
+ handlerLoaders.set(id, `${JSON.stringify(id)}: {server: ${Object.hasOwn(script.native.serverActions ?? {}, name)}, load: () => import(${JSON.stringify(`virtual:fuzor/native-handler/${id}.js`)}).catch(reloadOnStaleDeployment)}`);
1011
+ if (compiled.graphId)
1012
+ graphLoaders.set(compiled.graphId, `${JSON.stringify(compiled.graphId)}: (definition, data) => import("fuzor/runtime/native-graph").then(module => module.nativeGraphModule(definition, data, ${JSON.stringify(script.native.computed ?? {})}, ${JSON.stringify(script.native.ranges ?? [])}, [${(script.native.effects ?? []).map(effect => `{dependencies:${JSON.stringify(effect.dependencies)},load:()=>import(${JSON.stringify(`virtual:fuzor/native-handler/${compiled.actionIds.get(effect.action)}.js`)})}`).join(",")}]))`);
1013
+ }
1014
+ const manifestScripts = new Set(scriptRoutes.filter(route => nativeAdmissions.has(route.path)).flatMap(route => route.bindings.filter(binding => !authoredNative.has(binding.id)).map(binding => binding.id)));
1015
+ const nativeData = pageScripts.some(script => script.native?.request);
1016
+ const nativeLoaders = nativeAdmissions.size ? `const nativeLoaders = { ${nativeData ? "data: routeData," : ""} handlers: {${[...handlerLoaders.values()].join(",")}}, graphs: {${[...graphLoaders.values()].join(",")}}, scripts: {${[...manifestScripts].map(id => `${JSON.stringify(id)}: () => import(${JSON.stringify(pageScriptId(pageScripts.find(script => script.id === id)))}).catch(reloadOnStaleDeployment)`).join(",")}} };` : "";
1019
1017
  const registry = islands.map((island) => {
1020
1018
  const load = island.file.endsWith(".vue")
1021
1019
  ? `Promise.all([import(${JSON.stringify(island.file)}), import("fuzor/islands/vue")]).then(([component, adapter]) => adapter.vueIsland(component.default)).catch(reloadOnStaleDeployment)`
@@ -1023,16 +1021,24 @@ export function fuzorPlugin(options = {}) {
1023
1021
  return `${JSON.stringify(island.name)}: () => ${load}`;
1024
1022
  }).join(",\n");
1025
1023
  const withData = target === "mpa" && !!project?.routes.some(route => route.serverData);
1024
+ // Keep every discovered module, including ones introduced by page scripts. An empty static
1025
+ // registry cannot activate an island; retain diagnostics for authored hosts and open server/dev paths.
1026
+ const withIslands = islands.length > 0 || withServer || vite.command === "serve"
1027
+ || [...(project?.routes ?? []), ...(project?.boundaries ?? [])].some(route => /<fuzor-island\b/i.test(route.html));
1028
+ const mountIslandsCode = withIslands ? "mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal })" : "Promise.resolve(() => {})";
1026
1029
  const hasDocs = (project?.routes ?? []).some(route => route.path === "/docs/" || route.path.startsWith("/docs/"));
1027
1030
  const routerModule = profile.electron || options.navigation === "embedded" ? "fuzor/runtime/router/embedded" : options.navigation === "navigation-api" ? "fuzor/runtime/router/navigation-api" : "fuzor/runtime";
1028
1031
  const imports = [
1032
+ ...cssImports,
1033
+ ...(nativeAdmissions.size ? ['import { readNativeManifest } from "fuzor/runtime/native-manifest";'] : []),
1034
+ ...(options.ui?.brand === undefined ? [] : ['import { defineBrand } from "fuzor/ui/core";']),
1029
1035
  (target === "spa" || mixedClient)
1030
1036
  ? `import { createRouter } from "${routerModule}"; import { reloadOnStaleDeployment } from "fuzor/runtime/islands";`
1031
- : 'import { mountIslands, islandErrorHandler, reloadOnStaleDeployment } from "fuzor/runtime/islands";',
1037
+ : `import { ${withIslands ? "mountIslands, islandErrorHandler, " : ""}reloadOnStaleDeployment } from "fuzor/runtime/islands";`,
1032
1038
  ...(target === "mpa" ? ['import { mountPageScripts } from "fuzor/runtime/page-scripts"; import { stripBase } from "fuzor/runtime";'] : []),
1033
- ...(options.serverComponents ? [options.serverComponents.references ? 'import generatedViewOptions from "virtual:fuzor/views";' : "", options.serverComponents.client ? `import suppliedViewOptions from ${JSON.stringify("/" + options.serverComponents.client)};` : "", `const viewOptions = { ${options.serverComponents.references ? "...generatedViewOptions" : ""}${options.serverComponents.references && options.serverComponents.client ? "," : ""}${options.serverComponents.client ? "...suppliedViewOptions" : ""} };`, (target === "spa" || mixedClient) ? 'import { makeClientRouteMatcher } from "fuzor/runtime"; import { serverViewRoutes } from "fuzor/server-components/navigation"; import { createViewRenderer } from "fuzor/server-components/browser";' : 'import { createViewRenderer } from "fuzor/server-components/browser";'] : []),
1034
- ...(mixedClient ? ['import { mountIslands, islandErrorHandler } from "fuzor/runtime/islands";'] : []),
1035
- ...(withData ? ['import { loadRouteData } from "fuzor/runtime/data";'] : []),
1039
+ ...(mixedClient && withIslands ? ['import { mountIslands, islandErrorHandler } from "fuzor/runtime/islands";'] : []),
1040
+ ...(withData || nativeData ? [`import { ${[withData ? "loadRouteData" : "", nativeData ? "routeData" : ""].filter(Boolean).join(",")} } from "fuzor/runtime/data";`] : []),
1041
+ ...(nativeData ? ['import { enableNativeStreams } from "fuzor/runtime/native-stream";'] : []),
1036
1042
  ...(hasDocs ? ['import { mountDocsUi } from "fuzor/runtime/docs-ui";'] : [])
1037
1043
  ];
1038
1044
  const docsInit = hasDocs ? `let stopDocsUi;
@@ -1042,17 +1048,16 @@ const disposeDocs = () => {
1042
1048
  if (docsDisposed) return;
1043
1049
  docsDisposed = true;
1044
1050
  window.removeEventListener("pagehide", hideDocs);
1045
- stopDocsUi?.();
1051
+ void stopDocsUi?.().catch(error => console.error(error));
1046
1052
  };
1047
1053
  void mountDocsUi({ document, base: import.meta.env.BASE_URL }).then(stop => {
1048
1054
  stopDocsUi = stop;
1049
- if (docsDisposed) stop();
1055
+ if (docsDisposed) void stop().catch(error => console.error(error));
1050
1056
  }, error => console.error(error));
1051
1057
  window.addEventListener("pagehide", hideDocs);
1052
1058
  if (import.meta.hot) import.meta.hot.dispose(disposeDocs);
1053
1059
  ` : "";
1054
- const effectiveViewOptions = `...viewOptions, manifest: { ...viewOptions.manifest, buildId: ${JSON.stringify(viewBuildId)} }`;
1055
- const spaStart = `export const router = (${mixedClient ? 'outlet.getAttribute("data-fuzor-navigation") === "spa" && ' : ""}!outlet.hasAttribute("data-fuzor-not-found") && !outlet.hasAttribute("data-fuzor-error")) ? createRouter(outlet, { islands, scripts, base: import.meta.env.BASE_URL${options.serverComponents ? `, renderer: serverViewRoutes({ ${effectiveViewOptions}, routes: ${JSON.stringify(options.serverComponents.routes)}, acceptsRoute: path => !${mixedClient} || [...document.querySelectorAll("template[data-fuzor-route]")].some(t => t.getAttribute("data-fuzor-route") === path || t.hasAttribute("data-fuzor-request-pattern") && makeClientRouteMatcher({[t.getAttribute("data-fuzor-request-pattern")]: "/"})(path)), base: import.meta.env.BASE_URL })` : ""}${options.clientRoutes ? `, clientRoutes: ${JSON.stringify(options.clientRoutes)}` : ""}${options.routeTree ? `, routeTree: ${JSON.stringify(options.routeTree)}` : ""}${options.viewTransitions ? ", viewTransitions: true" : ""}${options.prefetch === "intent" ? ', prefetch: "intent"' : ""} }) : undefined;
1060
+ const spaStart = `export const router = (${mixedClient ? 'outlet.getAttribute("data-fuzor-navigation") === "spa" && ' : ""}!outlet.hasAttribute("data-fuzor-not-found") && !outlet.hasAttribute("data-fuzor-error")) ? createRouter(outlet, { islands, scripts, base: import.meta.env.BASE_URL${options.clientRoutes ? `, clientRoutes: ${JSON.stringify(options.clientRoutes)}` : ""}${options.routeTree ? `, routeTree: ${JSON.stringify(options.routeTree)}` : ""}${options.viewTransitions ? ", viewTransitions: true" : ""}${options.prefetch === "intent" ? ', prefetch: "intent"' : ""} }) : undefined;
1056
1061
  export const ready = router ? router.start() : Promise.resolve(undefined);
1057
1062
  void ready.catch(error => console.error(error));
1058
1063
  if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });`;
@@ -1061,13 +1066,11 @@ if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });
1061
1066
  const lifetime = new AbortController();
1062
1067
  let cleanup;
1063
1068
  const dispose = () => { lifetime.abort(); void cleanup?.(); };
1064
- ${options.serverComponents ? `const viewRenderer = document.querySelector("script[data-fuzor-snapshot]") ? createViewRenderer(outlet, { ${effectiveViewOptions} }) : undefined;
1065
- const mountComponents = viewRenderer ? viewRenderer.resume().then(() => () => viewRenderer.dispose()) : mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });` : `const mountComponents = mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });`}
1066
- ${options.serverComponents ? "const viewRendererActive = !!viewRenderer;" : "const viewRendererActive = false;"}
1069
+ const mountComponents = ${mountIslandsCode};
1067
1070
  const pathname = stripBase(location.pathname, import.meta.env.BASE_URL) ?? location.pathname;
1068
1071
  const routePath = pathname.endsWith("/") ? pathname : pathname + "/";
1069
1072
  const scriptRoute = Object.hasOwn(scripts, routePath) ? routePath : outlet.getAttribute("data-fuzor-pattern") ?? "";
1070
- const mount = (async () => { const stopComponents = await mountComponents; try { const stopScripts = await mountPageScripts(outlet, viewRendererActive ? {} : scripts, { signal: lifetime.signal, route: scriptRoute }); return async () => { await stopScripts(); await stopComponents(); }; } catch (error) { await stopComponents(); throw error; } })();
1073
+ const mount = (async () => { const stopComponents = await mountComponents; try { const stopScripts = await mountPageScripts(outlet, scripts, { signal: lifetime.signal, route: scriptRoute }); return async () => { await stopScripts(); await stopComponents(); }; } catch (error) { await stopComponents(); throw error; } })();
1071
1074
  void mount.then(stop => {
1072
1075
  cleanup = stop;
1073
1076
  if (lifetime.signal.aborted) void stop();
@@ -1075,7 +1078,10 @@ void mount.then(stop => {
1075
1078
  window.addEventListener("pagehide", event => { if (!event.persisted) dispose(); });
1076
1079
  if (import.meta.hot) import.meta.hot.dispose(dispose);`;
1077
1080
  return `${imports.join("\n")}
1081
+ ${options.ui?.brand === undefined ? "" : `defineBrand(${JSON.stringify(options.ui.brand)});`}
1078
1082
  const islands = { ${registry} };
1083
+ ${nativeLoaders}
1084
+ ${nativeData ? `enableNativeStreams(${JSON.stringify(viewBuildId)});` : ""}
1079
1085
  const scripts = { ${scriptRegistry} };
1080
1086
  const outlet = document.querySelector("fuzor-router");
1081
1087
  if (!outlet) throw new Error("Fuzor outlet is missing");
@@ -1092,6 +1098,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1092
1098
  const pathname = stripBase(requested, pathBase) ?? requested;
1093
1099
  const currentPath = findRoute(compiled, pathname) ? pathname : "/";
1094
1100
  parseRoutePolicy(raw, path.join(vite.root, "index.html"), false);
1101
+ assertNoServerScripts(raw, path.join(vite.root, "index.html"));
1095
1102
  const shell = compilePlatformBlocks(raw, profile.platform, path.join(vite.root, "index.html"));
1096
1103
  shellHead = readDocumentHead(shell);
1097
1104
  let html = applyDocumentMetadata(replaceOutlet(shell, outletFor(compiled, target, currentPath, fallbacks(), profile.mixed)), pageHead(shellHead, findRoute(compiled, currentPath)?.metadata ?? {}));
@@ -1163,10 +1170,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1163
1170
  const compiled = await loadProject();
1164
1171
  const manifest = serverManifest(compiled, target, template, pathBase, fallbacks(), withStaticModuleImports, options.clientRoutes, `/${vite.build.assetsDir}/`, profile.mixed);
1165
1172
  const data = dataRequest || compiled.routes.some(route => route.policy?.rendering === "request") ? await loadRouteDataModules(compiled, file => createEnvironmentModuleRuntime(server, moduleEnvironment, environmentApiEnabled).loadModule(file), vite.root) : undefined;
1166
- const views = options.serverComponents ? await generatedViews((await createEnvironmentModuleRuntime(server, moduleEnvironment, environmentApiEnabled).loadModule("/" + options.serverComponents.server)).default) : undefined;
1167
- if (views && JSON.stringify(Object.keys(views.routes).sort()) !== JSON.stringify([...options.serverComponents.routes].sort()))
1168
- throw new Error("Server view routes must match serverComponents.routes.");
1169
- result = await createServerHandler(manifest, { ...(data ? { data } : {}), ...(views ? { views: { ...views, manifest: { ...views.manifest, buildId: viewBuildId }, documentPatterns: manifest.requestPatterns ?? {}, document: serverViewDocument(template), documents: Object.fromEntries(Object.entries("documents" in manifest ? manifest.documents : manifest.routeDocuments ?? {}).map(([route, html]) => [route, serverViewDocument(html)])) } } : {}), onError })(toWebRequest(request, { url, signal: controller.signal }));
1173
+ result = await createServerHandler({ ...manifest, buildId: viewBuildId }, { renderBinding: renderNativeBinding, ...(data ? { data } : {}), onError })(toWebRequest(request, { url, signal: controller.signal }));
1170
1174
  }
1171
1175
  await writeWebResponse(response, result, { method: request.method ?? "GET", signal: controller.signal });
1172
1176
  }
@@ -1207,7 +1211,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1207
1211
  }
1208
1212
  });
1209
1213
  server.watcher.on("add", (file) => {
1210
- if (pageScripts.some(script => script.source === file) || options.serverComponents && (file.endsWith(".fragment.html") || options.serverComponents.references && file.startsWith(path.resolve(vite.root, options.serverComponents.references) + path.sep) || file === path.resolve(vite.root, options.serverComponents.server) || options.serverComponents.client && file === path.resolve(vite.root, options.serverComponents.client)) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.replaceAll("\\", "/").includes(part))) {
1214
+ if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.replaceAll("\\", "/").includes(part))) {
1211
1215
  void refresh(server).catch((error) => server.config.logger.error(String(error)));
1212
1216
  }
1213
1217
  });
@@ -1320,8 +1324,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1320
1324
  await mkdir(serverDir, { recursive: true });
1321
1325
  await writeFile(path.join(serverDir, "fuzor.manifest.json"), JSON.stringify({ ...artifact, clientDirectory: path.relative(serverDir, outDir).split(path.sep).join("/") }, null, 2) + "\n");
1322
1326
  const dataRoutes = compiled.routes.filter(route => route.serverData);
1323
- const refs = options.serverComponents?.references ? await references() : undefined;
1324
- if (!dataRoutes.length && !apiFiles.length && !options.serverComponents && !options.serverExternal) {
1327
+ if (!dataRoutes.length && !apiFiles.length && !options.serverExternal) {
1325
1328
  await writeFile(path.join(serverDir, "entry.mjs"), `import { createServerHandler } from "fuzor/server";\n` +
1326
1329
  `export const manifest = ${JSON.stringify(manifest)};\n` +
1327
1330
  `export const handler = createServerHandler(manifest);\n`, "utf8");
@@ -1331,8 +1334,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1331
1334
  const files = [...new Set(dataRoutes.map(route => route.serverData))];
1332
1335
  const entryId = "virtual:fuzor/server-entry";
1333
1336
  const code = [
1334
- `import { createServerHandler, isServerData } from "fuzor/server";`,
1335
- ...(options.serverComponents ? [`import suppliedViews from ${JSON.stringify(path.resolve(vite.root, options.serverComponents.server))};`, `const views = { ...suppliedViews${refs ? `, manifest: ${JSON.stringify({ buildId: viewBuildId, templates: refs.templates })}, components: ${JSON.stringify(refs.clients.map(ref => ref.id))}, handlers: ${JSON.stringify([...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)])}` : ""} };`, `if (JSON.stringify(Object.keys(views.routes).sort()) !== ${JSON.stringify(JSON.stringify([...options.serverComponents.routes].sort()))}) throw new Error("Server view routes must match serverComponents.routes.");`] : []),
1337
+ `import { createServerHandler, isServerData${Object.values(manifest.dataBindings ?? {}).some(bindings => bindings.some(binding => binding.kind === "range")) ? ", renderNativeBinding" : ""} } from "fuzor/server";`,
1336
1338
  ...files.map((file, index) => `import data${index} from ${JSON.stringify(file)};`),
1337
1339
  ...apiFiles.map((route, index) => `import * as api${index} from ${JSON.stringify(route.file)};`),
1338
1340
  `const modules = [${files.map((_, index) => `data${index}`).join(", ")}];`,
@@ -1341,14 +1343,15 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1341
1343
  `export const manifest = ${JSON.stringify(manifest)};`,
1342
1344
  `const routes = ${JSON.stringify(dataRoutes.map(route => [route.path, files.indexOf(route.serverData), route.params ?? null]))};`,
1343
1345
  `const api = [${apiFiles.map((route, index) => `{ pattern: ${JSON.stringify(route.pattern)}, segments: ${JSON.stringify(route.segments)}, source: ${JSON.stringify(route.relative)}, module: api${index} }`).join(", ")}];`,
1344
- `export const handler = createServerHandler(manifest, { data: Object.fromEntries(routes.map(([route, index, params]) => [route, params ? { data: modules[index], params } : { data: modules[index] }])), api${options.serverComponents ? `, views: { ...views, manifest: { ...views.manifest, buildId: ${JSON.stringify(viewBuildId)} }, documentPatterns:${JSON.stringify(manifest.requestPatterns ?? {})}, document: ${JSON.stringify(serverViewDocument(rootHtml))}, documents:${JSON.stringify(Object.fromEntries(Object.entries("documents" in manifest ? manifest.documents : manifest.routeDocuments ?? {}).map(([route, html]) => [route, serverViewDocument(html)])))} }` : ""} });`
1346
+ `export const handler = createServerHandler(manifest, { data: Object.fromEntries(routes.map(([route, index, params]) => [route, params ? { data: modules[index], params } : { data: modules[index] }])), api${Object.values(manifest.dataBindings ?? {}).some(bindings => bindings.some(binding => binding.kind === "range")) ? ", renderBinding: renderNativeBinding" : ""} });`
1345
1347
  ].join("\n");
1346
1348
  await nestedServer.run(true, () => viteBuild({
1347
1349
  root: vite.root, mode: vite.mode, configFile: vite.configFile ?? false, logLevel: "error", publicDir: false,
1348
- // One runtime copy: the entry and server modules share fuzor/server and effect, either both imported from
1349
- // node_modules (the default) or, for what `serverExternal` does not name, bundled once into the output.
1350
- ssr: options.serverExternal ? { external: [...options.serverExternal], noExternal: true } : { external: ["fuzor", "effect"] },
1351
- plugins: [compiledFragmentsPlugin(), workflowPlugin(), ...(refs ? [{ name: "fuzor:server-references", load: (id) => referenceStub(id, refs) }] : []), { name: "fuzor:server-entry", resolveId: id => id === entryId ? `\0${entryId}` : undefined, load: id => id === `\0${entryId}` ? code : undefined }],
1350
+ // One runtime copy: Fuzor's own server runtime is always bundled into the entry (generated code reaches modules
1351
+ // that are not package entries), so the entry and server modules share one copy of it. `effect` is imported from
1352
+ // node_modules by default; with `serverExternal`, everything it does not name is bundled once.
1353
+ ssr: options.serverExternal ? { external: options.serverExternal.filter(name => name !== "fuzor"), noExternal: true } : { external: ["effect"], noExternal: ["fuzor"] },
1354
+ plugins: [{ name: "fuzor:internal-specifiers", enforce: "pre", resolveId: id => resolveInternalSpecifier(id) }, htmlServerPlugin(nativeServerModules), workflowPlugin(), { name: "fuzor:server-entry", resolveId: id => id === entryId ? `\0${entryId}` : undefined, load: id => id === `\0${entryId}` ? code : undefined }],
1352
1355
  build: { ssr: true, outDir: serverDir, emptyOutDir: false, copyPublicDir: false, minify: false,
1353
1356
  rollupOptions: { input: entryId, output: { entryFileNames: "entry.mjs", chunkFileNames: "chunks/[name]-[hash].mjs", format: "es" } } }
1354
1357
  }));
@@ -1356,7 +1359,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1356
1359
  // The same finished documents are also written to client/ below, so it can be served by a CDN or
1357
1360
  // static host with only /api and server data requests reaching the server.
1358
1361
  if (profile.adapter === "cloudflare")
1359
- await writeCloudflareOutput(serverDir, vite.root, target, pathBase, outDir, [...compiled.routes.filter(route => route.policy?.rendering === "request").map(route => route.requestPattern ? route.requestPattern.slice(0, route.requestPattern.indexOf(":")) + "*" : route.path), ...(options.serverComponents?.routes ?? []).map(pattern => pattern.includes(":") ? pattern.slice(0, pattern.indexOf(":")) + "*" : pattern)], path.relative(path.resolve(vite.root, profile.backendOutDir), process.env.FUZOR_STAGE_DIR && outDir.startsWith(path.resolve(process.env.FUZOR_STAGE_DIR) + path.sep) ? path.resolve(vite.root, path.relative(process.env.FUZOR_STAGE_DIR, outDir)) : outDir).split(path.sep).join("/"));
1362
+ await writeCloudflareOutput(serverDir, vite.root, target, pathBase, outDir, [...compiled.routes.filter(route => route.policy?.rendering === "request").map(route => route.requestPattern ? route.requestPattern.slice(0, route.requestPattern.indexOf(":")) + "*" : route.path)], path.relative(path.resolve(vite.root, profile.backendOutDir), process.env.FUZOR_STAGE_DIR && outDir.startsWith(path.resolve(process.env.FUZOR_STAGE_DIR) + path.sep) ? path.resolve(vite.root, path.relative(process.env.FUZOR_STAGE_DIR, outDir)) : outDir).split(path.sep).join("/"));
1360
1363
  }
1361
1364
  const index = searchIndex(compiled, options.searchIndex);
1362
1365
  if (index)
@@ -1406,7 +1409,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
1406
1409
  if (nested)
1407
1410
  return;
1408
1411
  const file = context.file.replaceAll("\\", "/");
1409
- if (pageScripts.some(script => script.source === file) || options.serverComponents && (file.endsWith(".fragment.html") || options.serverComponents.references && file.startsWith(path.resolve(vite.root, options.serverComponents.references) + path.sep) || file === path.resolve(vite.root, options.serverComponents.server) || options.serverComponents.client && file === path.resolve(vite.root, options.serverComponents.client)) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.includes(part))) {
1412
+ if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.includes(part))) {
1410
1413
  const environment = context.environment?.name;
1411
1414
  if (environment)
1412
1415
  invalidateEnvironmentModule(context.server, resolvedVirtualId, environment, environmentApiEnabled);