@nifrajs/web 3.5.0 → 4.0.2

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 (390) hide show
  1. package/dist/backend-only.d.ts +15 -0
  2. package/dist/backend-only.d.ts.map +1 -0
  3. package/dist/backend-only.js +16 -0
  4. package/dist/backend-only.js.map +1 -0
  5. package/dist/boundary.d.ts +5 -0
  6. package/dist/boundary.d.ts.map +1 -1
  7. package/dist/boundary.js +11 -2
  8. package/dist/boundary.js.map +1 -1
  9. package/dist/build-plan.d.ts +18 -6
  10. package/dist/build-plan.d.ts.map +1 -1
  11. package/dist/build-plan.js +82 -34
  12. package/dist/build-plan.js.map +1 -1
  13. package/dist/build-vite.d.ts +4 -0
  14. package/dist/build-vite.d.ts.map +1 -1
  15. package/dist/build-vite.js +68 -13
  16. package/dist/build-vite.js.map +1 -1
  17. package/dist/build.d.ts +57 -23
  18. package/dist/build.d.ts.map +1 -1
  19. package/dist/build.js +356 -85
  20. package/dist/build.js.map +1 -1
  21. package/dist/bun-dev-entry.d.ts +1 -1
  22. package/dist/bun-dev-entry.js +1 -1
  23. package/dist/cdn.d.ts +74 -0
  24. package/dist/cdn.d.ts.map +1 -0
  25. package/dist/cdn.js +132 -0
  26. package/dist/cdn.js.map +1 -0
  27. package/dist/client.d.ts +3 -1
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +136 -35
  30. package/dist/client.js.map +1 -1
  31. package/dist/conformance.d.ts +4 -3
  32. package/dist/conformance.d.ts.map +1 -1
  33. package/dist/conformance.js +12 -3
  34. package/dist/conformance.js.map +1 -1
  35. package/dist/csp.d.ts +93 -0
  36. package/dist/csp.d.ts.map +1 -0
  37. package/dist/csp.js +122 -0
  38. package/dist/csp.js.map +1 -0
  39. package/dist/dev-client.d.ts +76 -0
  40. package/dist/dev-client.d.ts.map +1 -0
  41. package/dist/dev-client.js +280 -0
  42. package/dist/dev-client.js.map +1 -0
  43. package/dist/dev-diagnostics.d.ts +26 -2
  44. package/dist/dev-diagnostics.d.ts.map +1 -1
  45. package/dist/dev-diagnostics.js +38 -7
  46. package/dist/dev-diagnostics.js.map +1 -1
  47. package/dist/dev-error.d.ts +4 -13
  48. package/dist/dev-error.d.ts.map +1 -1
  49. package/dist/dev-error.js +46 -4
  50. package/dist/dev-error.js.map +1 -1
  51. package/dist/dev-feed.d.ts +298 -0
  52. package/dist/dev-feed.d.ts.map +1 -0
  53. package/dist/dev-feed.js +797 -0
  54. package/dist/dev-feed.js.map +1 -0
  55. package/dist/dev-indicator.d.ts +38 -0
  56. package/dist/dev-indicator.d.ts.map +1 -0
  57. package/dist/dev-indicator.js +93 -0
  58. package/dist/dev-indicator.js.map +1 -0
  59. package/dist/dev-session.d.ts +77 -0
  60. package/dist/dev-session.d.ts.map +1 -0
  61. package/dist/dev-session.js +580 -0
  62. package/dist/dev-session.js.map +1 -0
  63. package/dist/dev-sourcemap.d.ts +27 -0
  64. package/dist/dev-sourcemap.d.ts.map +1 -0
  65. package/dist/dev-sourcemap.js +235 -0
  66. package/dist/dev-sourcemap.js.map +1 -0
  67. package/dist/dev-ssr-graph.d.ts.map +1 -1
  68. package/dist/dev-ssr-graph.js +13 -5
  69. package/dist/dev-ssr-graph.js.map +1 -1
  70. package/dist/dev.d.ts +15 -2
  71. package/dist/dev.d.ts.map +1 -1
  72. package/dist/dev.js +69 -44
  73. package/dist/dev.js.map +1 -1
  74. package/dist/diagnostic-catalog.d.ts +35 -0
  75. package/dist/diagnostic-catalog.d.ts.map +1 -0
  76. package/dist/diagnostic-catalog.js +203 -0
  77. package/dist/diagnostic-catalog.js.map +1 -0
  78. package/dist/diagnostic-prompt.d.ts +49 -0
  79. package/dist/diagnostic-prompt.d.ts.map +1 -0
  80. package/dist/diagnostic-prompt.js +303 -0
  81. package/dist/diagnostic-prompt.js.map +1 -0
  82. package/dist/diagnostic.d.ts +17 -23
  83. package/dist/diagnostic.d.ts.map +1 -1
  84. package/dist/diagnostic.js +31 -39
  85. package/dist/diagnostic.js.map +1 -1
  86. package/dist/draft.d.ts.map +1 -1
  87. package/dist/draft.js +4 -1
  88. package/dist/draft.js.map +1 -1
  89. package/dist/fn.d.ts +5 -0
  90. package/dist/fn.d.ts.map +1 -1
  91. package/dist/fn.js +13 -3
  92. package/dist/fn.js.map +1 -1
  93. package/dist/fs.d.ts.map +1 -1
  94. package/dist/fs.js +4 -2
  95. package/dist/fs.js.map +1 -1
  96. package/dist/index.d.ts +13 -15
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +11 -12
  99. package/dist/index.js.map +1 -1
  100. package/dist/internal/cdn/cloudflare.d.ts +48 -0
  101. package/dist/internal/cdn/cloudflare.d.ts.map +1 -0
  102. package/dist/internal/cdn/cloudflare.js +94 -0
  103. package/dist/internal/cdn/cloudflare.js.map +1 -0
  104. package/dist/internal/cdn/core.d.ts +91 -0
  105. package/dist/internal/cdn/core.d.ts.map +1 -0
  106. package/dist/internal/cdn/core.js +220 -0
  107. package/dist/internal/cdn/core.js.map +1 -0
  108. package/dist/internal/cdn/fastly.d.ts +17 -0
  109. package/dist/internal/cdn/fastly.d.ts.map +1 -0
  110. package/dist/internal/cdn/fastly.js +43 -0
  111. package/dist/internal/cdn/fastly.js.map +1 -0
  112. package/dist/internal/cdn/vercel.d.ts +31 -0
  113. package/dist/internal/cdn/vercel.d.ts.map +1 -0
  114. package/dist/internal/cdn/vercel.js +56 -0
  115. package/dist/internal/cdn/vercel.js.map +1 -0
  116. package/dist/internal/code-unit-order.d.ts +4 -0
  117. package/dist/internal/code-unit-order.d.ts.map +1 -0
  118. package/dist/internal/code-unit-order.js +6 -0
  119. package/dist/internal/code-unit-order.js.map +1 -0
  120. package/dist/internal/codegen.d.ts.map +1 -1
  121. package/dist/internal/codegen.js +141 -25
  122. package/dist/internal/codegen.js.map +1 -1
  123. package/dist/internal/dev-csp.d.ts +24 -0
  124. package/dist/internal/dev-csp.d.ts.map +1 -0
  125. package/dist/internal/dev-csp.js +160 -0
  126. package/dist/internal/dev-csp.js.map +1 -0
  127. package/dist/internal/dev-reserved.d.ts +25 -0
  128. package/dist/internal/dev-reserved.d.ts.map +1 -0
  129. package/dist/internal/dev-reserved.js +39 -0
  130. package/dist/internal/dev-reserved.js.map +1 -0
  131. package/dist/internal/html-spans.d.ts +10 -0
  132. package/dist/internal/html-spans.d.ts.map +1 -0
  133. package/dist/internal/html-spans.js +46 -0
  134. package/dist/internal/html-spans.js.map +1 -0
  135. package/dist/internal/loading-runtime.d.ts +44 -0
  136. package/dist/internal/loading-runtime.d.ts.map +1 -0
  137. package/dist/internal/loading-runtime.js +206 -0
  138. package/dist/internal/loading-runtime.js.map +1 -0
  139. package/dist/internal/matches-runtime.d.ts +8 -0
  140. package/dist/internal/matches-runtime.d.ts.map +1 -0
  141. package/dist/internal/matches-runtime.js +61 -0
  142. package/dist/internal/matches-runtime.js.map +1 -0
  143. package/dist/internal/mount-shadow.d.ts +36 -0
  144. package/dist/internal/mount-shadow.d.ts.map +1 -0
  145. package/dist/internal/mount-shadow.js +65 -0
  146. package/dist/internal/mount-shadow.js.map +1 -0
  147. package/dist/internal/node-builtins.d.ts +3 -0
  148. package/dist/internal/node-builtins.d.ts.map +1 -0
  149. package/dist/internal/node-builtins.js +79 -0
  150. package/dist/internal/node-builtins.js.map +1 -0
  151. package/dist/internal/output-guard.d.ts +65 -0
  152. package/dist/internal/output-guard.d.ts.map +1 -0
  153. package/dist/internal/output-guard.js +630 -0
  154. package/dist/internal/output-guard.js.map +1 -0
  155. package/dist/internal/page-execution.d.ts +20 -8
  156. package/dist/internal/page-execution.d.ts.map +1 -1
  157. package/dist/internal/page-execution.js +571 -105
  158. package/dist/internal/page-execution.js.map +1 -1
  159. package/dist/internal/parity.d.ts +21 -0
  160. package/dist/internal/parity.d.ts.map +1 -1
  161. package/dist/internal/parity.js +104 -22
  162. package/dist/internal/parity.js.map +1 -1
  163. package/dist/internal/private-env.d.ts +12 -0
  164. package/dist/internal/private-env.d.ts.map +1 -0
  165. package/dist/internal/private-env.js +335 -0
  166. package/dist/internal/private-env.js.map +1 -0
  167. package/dist/internal/public-url.d.ts +3 -0
  168. package/dist/internal/public-url.d.ts.map +1 -0
  169. package/dist/internal/public-url.js +18 -0
  170. package/dist/internal/public-url.js.map +1 -0
  171. package/dist/internal/render-document.d.ts +25 -19
  172. package/dist/internal/render-document.d.ts.map +1 -1
  173. package/dist/internal/render-document.js +77 -61
  174. package/dist/internal/render-document.js.map +1 -1
  175. package/dist/internal/response-controls.d.ts +75 -0
  176. package/dist/internal/response-controls.d.ts.map +1 -0
  177. package/dist/internal/response-controls.js +256 -0
  178. package/dist/internal/response-controls.js.map +1 -0
  179. package/dist/internal/runtime-contract.d.ts +11 -16
  180. package/dist/internal/runtime-contract.d.ts.map +1 -1
  181. package/dist/internal/runtime-contract.js.map +1 -1
  182. package/dist/internal/secret-scan.d.ts +104 -0
  183. package/dist/internal/secret-scan.d.ts.map +1 -0
  184. package/dist/internal/secret-scan.js +503 -0
  185. package/dist/internal/secret-scan.js.map +1 -0
  186. package/dist/internal/server-boundary.d.ts +6 -29
  187. package/dist/internal/server-boundary.d.ts.map +1 -1
  188. package/dist/internal/server-boundary.js +6 -66
  189. package/dist/internal/server-boundary.js.map +1 -1
  190. package/dist/internal/source-map.d.ts +47 -0
  191. package/dist/internal/source-map.d.ts.map +1 -0
  192. package/dist/internal/source-map.js +225 -0
  193. package/dist/internal/source-map.js.map +1 -0
  194. package/dist/internal/target-compat.d.ts +27 -0
  195. package/dist/internal/target-compat.d.ts.map +1 -0
  196. package/dist/internal/target-compat.js +77 -0
  197. package/dist/internal/target-compat.js.map +1 -0
  198. package/dist/internal/timing-safe-equal.d.ts +6 -0
  199. package/dist/internal/timing-safe-equal.d.ts.map +1 -1
  200. package/dist/internal/timing-safe-equal.js +10 -0
  201. package/dist/internal/timing-safe-equal.js.map +1 -1
  202. package/dist/internal/vitals-runtime.d.ts +18 -0
  203. package/dist/internal/vitals-runtime.d.ts.map +1 -0
  204. package/dist/internal/vitals-runtime.js +38 -0
  205. package/dist/internal/vitals-runtime.js.map +1 -0
  206. package/dist/internal/web-app.d.ts +38 -9
  207. package/dist/internal/web-app.d.ts.map +1 -1
  208. package/dist/internal/web-app.js +199 -59
  209. package/dist/internal/web-app.js.map +1 -1
  210. package/dist/internal/zone-graph.d.ts +89 -0
  211. package/dist/internal/zone-graph.d.ts.map +1 -0
  212. package/dist/internal/zone-graph.js +492 -0
  213. package/dist/internal/zone-graph.js.map +1 -0
  214. package/dist/isr.d.ts +83 -6
  215. package/dist/isr.d.ts.map +1 -1
  216. package/dist/isr.js +263 -36
  217. package/dist/isr.js.map +1 -1
  218. package/dist/llms-txt.d.ts.map +1 -1
  219. package/dist/llms-txt.js +31 -6
  220. package/dist/llms-txt.js.map +1 -1
  221. package/dist/manifest.d.ts +220 -13
  222. package/dist/manifest.d.ts.map +1 -1
  223. package/dist/manifest.js +394 -9
  224. package/dist/manifest.js.map +1 -1
  225. package/dist/module-graph.d.ts +26 -5
  226. package/dist/module-graph.d.ts.map +1 -1
  227. package/dist/module-graph.js +17 -7
  228. package/dist/module-graph.js.map +1 -1
  229. package/dist/navigation.d.ts +17 -3
  230. package/dist/navigation.d.ts.map +1 -1
  231. package/dist/navigation.js +11 -5
  232. package/dist/navigation.js.map +1 -1
  233. package/dist/nonce.d.ts +47 -0
  234. package/dist/nonce.d.ts.map +1 -0
  235. package/dist/nonce.js +63 -0
  236. package/dist/nonce.js.map +1 -0
  237. package/dist/plugins/css-modules.d.ts.map +1 -1
  238. package/dist/plugins/css-modules.js +4 -3
  239. package/dist/plugins/css-modules.js.map +1 -1
  240. package/dist/plugins/kit.d.ts +9 -0
  241. package/dist/plugins/kit.d.ts.map +1 -1
  242. package/dist/plugins/kit.js +18 -0
  243. package/dist/plugins/kit.js.map +1 -1
  244. package/dist/plugins/svg.d.ts +22 -3
  245. package/dist/plugins/svg.d.ts.map +1 -1
  246. package/dist/plugins/svg.js +112 -1
  247. package/dist/plugins/svg.js.map +1 -1
  248. package/dist/plugins/vite-leak-guard.d.ts +107 -3
  249. package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
  250. package/dist/plugins/vite-leak-guard.js +378 -17
  251. package/dist/plugins/vite-leak-guard.js.map +1 -1
  252. package/dist/plugins/vite-zone-guard.d.ts +61 -0
  253. package/dist/plugins/vite-zone-guard.d.ts.map +1 -0
  254. package/dist/plugins/vite-zone-guard.js +208 -0
  255. package/dist/plugins/vite-zone-guard.js.map +1 -0
  256. package/dist/plugins/zone-guard.d.ts +27 -0
  257. package/dist/plugins/zone-guard.d.ts.map +1 -0
  258. package/dist/plugins/zone-guard.js +80 -0
  259. package/dist/plugins/zone-guard.js.map +1 -0
  260. package/dist/prerender.d.ts +10 -0
  261. package/dist/prerender.d.ts.map +1 -1
  262. package/dist/prerender.js +35 -3
  263. package/dist/prerender.js.map +1 -1
  264. package/dist/public-dir.d.ts +9 -0
  265. package/dist/public-dir.d.ts.map +1 -1
  266. package/dist/public-dir.js +67 -9
  267. package/dist/public-dir.js.map +1 -1
  268. package/dist/pwa-manifest.d.ts +68 -0
  269. package/dist/pwa-manifest.d.ts.map +1 -0
  270. package/dist/pwa-manifest.js +178 -0
  271. package/dist/pwa-manifest.js.map +1 -0
  272. package/dist/query.js +4 -4
  273. package/dist/query.js.map +1 -1
  274. package/dist/render-seam.d.ts +42 -2
  275. package/dist/render-seam.d.ts.map +1 -1
  276. package/dist/render-seam.js +6 -0
  277. package/dist/render-seam.js.map +1 -1
  278. package/dist/route-manifest.d.ts +3 -0
  279. package/dist/route-manifest.d.ts.map +1 -1
  280. package/dist/route-manifest.js +26 -3
  281. package/dist/route-manifest.js.map +1 -1
  282. package/dist/route-types.d.ts +24 -0
  283. package/dist/route-types.d.ts.map +1 -0
  284. package/dist/route-types.js +195 -0
  285. package/dist/route-types.js.map +1 -0
  286. package/dist/router.d.ts +28 -6
  287. package/dist/router.d.ts.map +1 -1
  288. package/dist/router.js +129 -40
  289. package/dist/router.js.map +1 -1
  290. package/dist/vitals.d.ts +33 -0
  291. package/dist/vitals.d.ts.map +1 -0
  292. package/dist/vitals.js +16 -0
  293. package/dist/vitals.js.map +1 -0
  294. package/dist/vite.d.ts +16 -1
  295. package/dist/vite.d.ts.map +1 -1
  296. package/dist/vite.js +248 -43
  297. package/dist/vite.js.map +1 -1
  298. package/dist/zones.d.ts +70 -0
  299. package/dist/zones.d.ts.map +1 -0
  300. package/dist/zones.js +365 -0
  301. package/dist/zones.js.map +1 -0
  302. package/package.json +67 -14
  303. package/src/backend-only.ts +16 -0
  304. package/src/boundary.ts +18 -5
  305. package/src/build-plan.ts +90 -35
  306. package/src/build-vite.ts +77 -11
  307. package/src/build.ts +466 -96
  308. package/src/bun-dev-entry.ts +1 -1
  309. package/src/cdn.ts +216 -0
  310. package/src/client.ts +135 -36
  311. package/src/conformance.ts +16 -3
  312. package/src/csp.ts +194 -0
  313. package/src/dev-client.ts +349 -0
  314. package/src/dev-diagnostics.ts +60 -7
  315. package/src/dev-error.ts +49 -5
  316. package/src/dev-feed.ts +1140 -0
  317. package/src/dev-indicator.ts +113 -0
  318. package/src/dev-session.ts +729 -0
  319. package/src/dev-sourcemap.ts +281 -0
  320. package/src/dev-ssr-graph.ts +16 -5
  321. package/src/dev.ts +86 -43
  322. package/src/diagnostic-catalog.ts +243 -0
  323. package/src/diagnostic-prompt.ts +355 -0
  324. package/src/diagnostic.ts +63 -66
  325. package/src/draft.ts +4 -1
  326. package/src/fn.ts +19 -3
  327. package/src/fs.ts +13 -2
  328. package/src/index.ts +42 -10
  329. package/src/internal/cdn/cloudflare.ts +146 -0
  330. package/src/internal/cdn/core.ts +316 -0
  331. package/src/internal/cdn/fastly.ts +62 -0
  332. package/src/internal/cdn/vercel.ts +95 -0
  333. package/src/internal/code-unit-order.ts +5 -0
  334. package/src/internal/codegen.ts +161 -24
  335. package/src/internal/dev-csp.ts +176 -0
  336. package/src/internal/dev-reserved.ts +52 -0
  337. package/src/internal/html-spans.ts +50 -0
  338. package/src/internal/loading-runtime.ts +244 -0
  339. package/src/internal/matches-runtime.ts +62 -0
  340. package/src/internal/mount-shadow.ts +88 -0
  341. package/src/internal/node-builtins.ts +79 -0
  342. package/src/internal/output-guard.ts +701 -0
  343. package/src/internal/page-execution.ts +795 -185
  344. package/src/internal/parity.ts +150 -36
  345. package/src/internal/private-env.ts +334 -0
  346. package/src/internal/public-url.ts +22 -0
  347. package/src/internal/render-document.ts +114 -67
  348. package/src/internal/response-controls.ts +284 -0
  349. package/src/internal/runtime-contract.ts +10 -15
  350. package/src/internal/secret-scan.ts +638 -0
  351. package/src/internal/server-boundary.ts +6 -69
  352. package/src/internal/source-map.ts +256 -0
  353. package/src/internal/target-compat.ts +92 -0
  354. package/src/internal/timing-safe-equal.ts +13 -0
  355. package/src/internal/vitals-runtime.ts +53 -0
  356. package/src/internal/web-app.ts +262 -84
  357. package/src/internal/zone-graph.ts +593 -0
  358. package/src/isr.ts +325 -37
  359. package/src/llms-txt.ts +28 -4
  360. package/src/manifest.ts +682 -16
  361. package/src/module-graph.ts +53 -9
  362. package/src/navigation.ts +29 -6
  363. package/src/nonce.ts +104 -0
  364. package/src/plugins/css-modules.ts +5 -3
  365. package/src/plugins/kit.ts +24 -0
  366. package/src/plugins/svg.ts +135 -4
  367. package/src/plugins/vite-leak-guard.ts +529 -16
  368. package/src/plugins/vite-zone-guard.ts +263 -0
  369. package/src/plugins/zone-guard.ts +119 -0
  370. package/src/prerender.ts +49 -3
  371. package/src/public-dir.ts +69 -8
  372. package/src/pwa-manifest.ts +226 -0
  373. package/src/query.ts +4 -4
  374. package/src/render-seam.ts +46 -1
  375. package/src/route-manifest.ts +15 -3
  376. package/src/route-types.ts +264 -0
  377. package/src/router.ts +163 -63
  378. package/src/vitals.ts +44 -0
  379. package/src/vite.ts +257 -42
  380. package/src/zones.ts +430 -0
  381. package/dist/plugins/vite-server-only.d.ts +0 -49
  382. package/dist/plugins/vite-server-only.d.ts.map +0 -1
  383. package/dist/plugins/vite-server-only.js +0 -57
  384. package/dist/plugins/vite-server-only.js.map +0 -1
  385. package/dist/server-only.d.ts +0 -21
  386. package/dist/server-only.d.ts.map +0 -1
  387. package/dist/server-only.js +0 -25
  388. package/dist/server-only.js.map +0 -1
  389. package/src/plugins/vite-server-only.ts +0 -75
  390. package/src/server-only.ts +0 -25
package/src/build-plan.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { basename as pathBasename } from "node:path"
2
2
  import type { CssLoadingMode } from "./css-contract.ts"
3
+ import { codeUnitOrder } from "./internal/code-unit-order.ts"
4
+ import { isBareNodeBuiltin } from "./internal/node-builtins.ts"
5
+ import type { SecretExemption } from "./internal/secret-scan.ts"
3
6
  import type { ClientModuleGraph } from "./module-graph.ts"
4
7
 
5
8
  export interface BuildManifest {
@@ -51,7 +54,7 @@ export interface ServerBuild {
51
54
  // ===================================================================================================
52
55
 
53
56
  /** A deploy target `nifra build --target <t>` can emit. `static` is pure SSG (no server). */
54
- export const BUILD_TARGETS = ["bun", "node", "deno", "cf-pages", "vercel", "static"] as const
57
+ export const BUILD_TARGETS = ["bun", "node", "deno", "cloudflare", "vercel", "static"] as const
55
58
  export type BuildTarget = (typeof BUILD_TARGETS)[number]
56
59
 
57
60
  /** A type guard narrowing an arbitrary string to a {@link BuildTarget}. */
@@ -59,6 +62,16 @@ export function isBuildTarget(value: string): value is BuildTarget {
59
62
  return (BUILD_TARGETS as readonly string[]).includes(value)
60
63
  }
61
64
 
65
+ /** `value` as a {@link BuildTarget}, or throw naming the valid ones (and the renamed `cf-pages`). */
66
+ export function parseBuildTarget(value: string, label = "target"): BuildTarget {
67
+ if (isBuildTarget(value)) return value
68
+ throw new Error(
69
+ value === "cf-pages"
70
+ ? `[nifra] the ${label} "cf-pages" is now "cloudflare"`
71
+ : `[nifra] unknown ${label} "${value}". Valid: ${BUILD_TARGETS.join(", ")}`,
72
+ )
73
+ }
74
+
62
75
  export type ServerBuildTarget = "browser" | "node" | "bun"
63
76
 
64
77
  export interface StaticBuildTargetPlan {
@@ -66,6 +79,7 @@ export interface StaticBuildTargetPlan {
66
79
  readonly kind: "static"
67
80
  readonly serverTarget: undefined
68
81
  readonly outputFile: undefined
82
+ readonly staticDir: ""
69
83
  readonly run: string
70
84
  }
71
85
 
@@ -73,8 +87,10 @@ export interface ServerBuildTargetPlan {
73
87
  readonly target: Exclude<BuildTarget, "static">
74
88
  readonly kind: "server"
75
89
  readonly serverTarget: ServerBuildTarget
76
- /** The worker's final filename inside the assembled deploy directory. */
77
- readonly outputFile: "_worker.js" | "index.js" | "server.js"
90
+ /** The worker's final path inside the assembled deploy directory. */
91
+ readonly outputFile: "_worker.js" | "functions/index.func/index.js" | "server.js"
92
+ /** Where the host serves static files from, relative to the deploy directory (`""` = its root). */
93
+ readonly staticDir: "" | "static"
78
94
  readonly run: string
79
95
  }
80
96
 
@@ -106,15 +122,17 @@ export function planBuildTarget(target: BuildTarget, outDir: string): BuildTarge
106
122
  kind: "static",
107
123
  serverTarget: undefined,
108
124
  outputFile: undefined,
125
+ staticDir: "",
109
126
  run: `static site → ${outDir} (serve the directory with any static host)`,
110
127
  }
111
128
  }
112
- if (target === "cf-pages") {
129
+ if (target === "cloudflare") {
113
130
  return {
114
131
  target,
115
132
  kind: "server",
116
133
  serverTarget: "browser",
117
134
  outputFile: "_worker.js",
135
+ staticDir: "",
118
136
  run: `Cloudflare Pages → ${outDir} (deploy: wrangler pages deploy ${finalPathSegment(outDir)})`,
119
137
  }
120
138
  }
@@ -123,8 +141,10 @@ export function planBuildTarget(target: BuildTarget, outDir: string): BuildTarge
123
141
  target,
124
142
  kind: "server",
125
143
  serverTarget: "browser",
126
- outputFile: "index.js",
127
- run: `Vercel edge function → ${outDir}/index.js (wrap with your vercel.json or Build Output API)`,
144
+ // Vercel's Build Output API: `vercel deploy --prebuilt` uploads this tree as it is.
145
+ outputFile: "functions/index.func/index.js",
146
+ staticDir: "static",
147
+ run: `Vercel (Build Output API) → ${outDir} (deploy: vercel deploy --prebuilt)`,
128
148
  }
129
149
  }
130
150
  return {
@@ -132,6 +152,7 @@ export function planBuildTarget(target: BuildTarget, outDir: string): BuildTarge
132
152
  kind: "server",
133
153
  serverTarget: target === "node" ? "node" : target === "bun" ? "bun" : "browser",
134
154
  outputFile: "server.js",
155
+ staticDir: "",
135
156
  run: `${target} server → ${outDir} (run: ${target === "node" ? "node" : target} ${finalPathSegment(outDir)}/server.js)`,
136
157
  }
137
158
  }
@@ -161,6 +182,8 @@ export interface Bundler {
161
182
  readonly cssLoading?: CssLoadingMode
162
183
  /** Project root (Vite needs it; the Bun strategy ignores it). */
163
184
  readonly root?: string
185
+ /** Reviewed false positives of the secret scan. */
186
+ readonly secretExemptions?: readonly SecretExemption[]
164
187
  }): Promise<BuildManifest>
165
188
  /** Build the server worker → the shared {@link ServerBuild}. */
166
189
  buildServer(input: {
@@ -178,12 +201,17 @@ export interface Bundler {
178
201
  /** CSS activation policy to bake into the generated server manifest. */
179
202
  readonly cssLoading?: CssLoadingMode
180
203
  readonly root?: string
204
+ /** Modules the zone rules treat as generated: the adapter module the generated entry imports. */
205
+ readonly generatedFiles?: readonly string[]
206
+ /** The public-env prefix browser code may read (default `"PUBLIC_"`). */
207
+ readonly publicEnvPrefix?: string
181
208
  }): Promise<ServerBuild>
182
209
  }
183
210
 
184
211
  interface GraphImport {
185
212
  readonly path?: string
186
213
  readonly original?: string
214
+ readonly external?: boolean
187
215
  }
188
216
 
189
217
  interface GraphInput {
@@ -195,7 +223,11 @@ const basename = (path: string): string => pathBasename(path)
195
223
  const nodeBuiltinOf = (im: GraphImport): string | undefined => {
196
224
  if (im.original?.startsWith("node:")) return im.original
197
225
  if (im.path?.startsWith("node:")) return im.path
198
- return undefined
226
+ // A bare built-in only counts when the bundler left it external: a bundled `fs` resolved to an
227
+ // installed package (or a polyfill keyed `node:fs`) is handled above or is not a built-in at all.
228
+ // Labeled with the prefix, so `fs/promises` reads the same from either bundler.
229
+ const spec = im.external === true ? (im.original ?? im.path) : undefined
230
+ return spec !== undefined && isBareNodeBuiltin(spec) ? `node:${spec}` : undefined
199
231
  }
200
232
 
201
233
  /** One `node:`-builtin-in-the-client finding: the offending builtin, the emitted chunk it landed in,
@@ -302,29 +334,43 @@ export function detectNodeBuiltinsInClient(
302
334
  chains.set(builtin, chain)
303
335
  return chain
304
336
  }
337
+ // (1b) Builtins the bundler left EXTERNAL: the import ships in the importer's chunk but the module
338
+ // is in no output, so (2) can't find it as a chunk member. Bun does this for a dynamic
339
+ // `import("node:fs")` in a browser build; the browser then fails to load it.
340
+ const externalsOf = new Map<string, string[]>()
341
+ for (const [inputKey, input] of Object.entries(inputs)) {
342
+ if (inputKey.startsWith("node:")) continue
343
+ for (const im of input.imports ?? []) {
344
+ if (im.external !== true) continue
345
+ const builtin = nodeBuiltinOf(im)
346
+ if (builtin === undefined) continue
347
+ const list = externalsOf.get(inputKey) ?? []
348
+ if (!list.includes(builtin)) list.push(builtin)
349
+ externalsOf.set(inputKey, list)
350
+ }
351
+ }
305
352
  // (2) Locate which emitted chunk each user-imported builtin reached, via the per-output `inputs`.
306
353
  const findings = new Map<string, NodeBuiltinFinding>()
354
+ const report = (builtin: string, outputPath: string): void => {
355
+ const chunk = basename(outputPath)
356
+ findings.set(`${builtin}\0${chunk}`, { builtin, chunk, chain: chainFor(builtin) })
357
+ }
307
358
  for (const [outputPath, output] of Object.entries(graph.chunks)) {
308
359
  for (const inputKey of output.modules) {
309
- if (!userImported.has(inputKey)) continue
310
- const chunk = basename(outputPath)
311
- findings.set(`${inputKey}\0${chunk}`, {
312
- builtin: inputKey,
313
- chunk,
314
- chain: chainFor(inputKey),
315
- })
360
+ if (userImported.has(inputKey)) report(inputKey, outputPath)
361
+ for (const builtin of externalsOf.get(inputKey) ?? []) report(builtin, outputPath)
316
362
  }
317
363
  }
318
364
  return [...findings.values()].sort((a, b) =>
319
- a.builtin === b.builtin ? a.chunk.localeCompare(b.chunk) : a.builtin.localeCompare(b.builtin),
365
+ a.builtin === b.builtin ? codeUnitOrder(a.chunk, b.chunk) : codeUnitOrder(a.builtin, b.builtin),
320
366
  )
321
367
  }
322
368
 
323
369
  // ---------------------------------------------------------------------------------------------------
324
- // `server-only` poison-import guard (§3.3/§5.1). The complement to the `.server` convention + the
370
+ // `backend-only` poison-import guard (§3.3/§5.1). The complement to the zones + the
325
371
  // node-builtin guard: a module of PURE server logic with NO `node:` import (a secret-bearing constant,
326
372
  // a server-only API call) that an author wants to FAIL LOUD if it reaches the client opts in with a
327
- // side-effect `import "@nifrajs/web/server-only"` (Next's `import "server-only"`). On the SERVER build
373
+ // side-effect `import "@nifrajs/web/backend-only"` (Next's `import "server-only"`). On the SERVER build
328
374
  // the marker is an empty no-op; the CLIENT build detects - via the SAME Bun metafile graph the
329
375
  // node-builtin guard walks - any module that imports the marker AND lands in a client chunk, and fails
330
376
  // the build with the import chain. Graph-based (never the emitted text), so it survives minification.
@@ -332,18 +378,18 @@ export function detectNodeBuiltinsInClient(
332
378
 
333
379
  /** The marker specifier an author imports to opt a module into the client-leak guard. Matched on the
334
380
  * import edge's *as-written* `original` first (the robust signal: it's exactly what the author typed,
335
- * before Bun resolves it to `src/server-only.ts` / `dist/server-only.js`). */
336
- export const SERVER_ONLY_MARKER = "@nifrajs/web/server-only"
381
+ * before Bun resolves it to `src/backend-only.ts` / `dist/backend-only.js`). */
382
+ export const SERVER_ONLY_MARKER = "@nifrajs/web/backend-only"
337
383
 
338
384
  const isServerOnlyMarkerImport = (im: GraphImport): boolean => {
339
385
  if (im.original === SERVER_ONLY_MARKER) return true
340
- return im.path !== undefined && /(^|\/)server-only\.[cm]?[jt]s$/.test(im.path)
386
+ return im.path !== undefined && /(^|\/)backend-only\.[cm]?[jt]s$/.test(im.path)
341
387
  }
342
388
 
343
- /** True when an INPUT graph key is the marker module file itself (`…/server-only.{ts,js}` under web).
389
+ /** True when an INPUT graph key is the marker module file itself (`…/backend-only.{ts,js}` under web).
344
390
  * Excluded from the "marked" set - the marker is the import target, not an opt-in module. */
345
391
  const isServerOnlyMarkerModule = (inputKey: string): boolean =>
346
- /(^|\/)server-only\.[cm]?[jt]s$/.test(inputKey)
392
+ /(^|\/)backend-only\.[cm]?[jt]s$/.test(inputKey)
347
393
 
348
394
  /** One `server-only`-module-in-the-client finding: the offending module (the as-written marker-import
349
395
  * chain's tail before the marker), the emitted chunk it landed in, and the shortest USER-module import
@@ -398,7 +444,7 @@ function shortestServerOnlyChain(
398
444
  ): string[] {
399
445
  // The label for the marked module's tail: its as-written specifier (filled when we cross the edge
400
446
  // that reaches it) suffixed with `(marked server-only)`; the entry case uses the entry key itself.
401
- const tail = (label: string): string => `${label} (marked server-only)`
447
+ const tail = (label: string): string => `${label} (marked backend-only)`
402
448
  // An entry that is ITSELF the marked module - the chain is just that one node.
403
449
  if (entryInputs.includes(markedModule)) return [tail(markedModule)]
404
450
  const seen = new Set(entryInputs)
@@ -427,7 +473,7 @@ function shortestServerOnlyChain(
427
473
 
428
474
  /**
429
475
  * Scan a build's metafile for any module that opts into the `server-only` marker (a side-effect
430
- * `import "@nifrajs/web/server-only"`) yet landed in a CLIENT output chunk, returning a sorted, deduped
476
+ * `import "@nifrajs/web/backend-only"`) yet landed in a CLIENT output chunk, returning a sorted, deduped
431
477
  * list of {@link ServerOnlyFinding}s. Mirrors {@link detectNodeBuiltinsInClient}: it reads the SAME
432
478
  * graph facts - which inputs import the marker (the "marked" modules), which chunk each landed in (the
433
479
  * per-output `inputs`), and the shortest import chain from a user entry to it. The marker module ITSELF
@@ -466,17 +512,28 @@ export function detectServerOnlyInClient(
466
512
  }
467
513
  // (2) Locate which emitted chunk each marked module reached, via the per-output `inputs`.
468
514
  const findings = new Map<string, ServerOnlyFinding>()
515
+ const placed = new Set<string>()
469
516
  for (const [outputPath, output] of Object.entries(graph.chunks)) {
470
517
  for (const inputKey of output.modules) {
471
518
  if (!marked.has(inputKey)) continue
472
519
  const chunk = basename(outputPath)
520
+ placed.add(inputKey)
473
521
  findings.set(`${inputKey}\0${chunk}`, { chunk, chain: chainFor(inputKey) })
474
522
  }
475
523
  }
524
+ // A marked module the build loaded but no chunk lists: the bundler inlined what it exports (a
525
+ // constant ships without its module) or dropped it. The import crossed the boundary either way.
526
+ for (const inputKey of marked) {
527
+ if (placed.has(inputKey)) continue
528
+ findings.set(`${inputKey}\0`, {
529
+ chunk: "none, inlined or shaken out",
530
+ chain: chainFor(inputKey),
531
+ })
532
+ }
476
533
  return [...findings.values()].sort((a, b) => {
477
534
  const am = a.chain[a.chain.length - 1] ?? ""
478
535
  const bm = b.chain[b.chain.length - 1] ?? ""
479
- return am === bm ? a.chunk.localeCompare(b.chunk) : am.localeCompare(bm)
536
+ return am === bm ? codeUnitOrder(a.chunk, b.chunk) : codeUnitOrder(am, bm)
480
537
  })
481
538
  }
482
539
 
@@ -498,9 +555,8 @@ export function formatNodeBuiltinLeak(
498
555
  : ` - ${finding.builtin} reached the client bundle via ${finding.chunk}`,
499
556
  )
500
557
  return (
501
- `[nifra/web] Node built-in(s) in the client bundle - move them behind a server-only path ` +
502
- `(a loader/action runs on the server; import the \`node:\` module there, not at a route's ` +
503
- `top level):\n${lines.join("\n")}`
558
+ `[nifra/web] Node built-in(s) in the client bundle - built-ins run on the server only: import ` +
559
+ `them in a route's backend half (x.backend.ts) or under backend/, never in browser code:\n${lines.join("\n")}`
504
560
  )
505
561
  }
506
562
 
@@ -511,14 +567,13 @@ export function formatServerOnlyLeak(
511
567
  if (findings.length === 0) return undefined
512
568
  const lines = findings.map((finding) =>
513
569
  finding.chain.length > 1
514
- ? ` - server-only module reached the client bundle via ${finding.chain.join(" → ")} (chunk: ${finding.chunk})`
515
- : ` - server-only module reached the client bundle via ${finding.chunk}`,
570
+ ? ` - backend-only module reached the client bundle via ${finding.chain.join(" → ")} (chunk: ${finding.chunk})`
571
+ : ` - backend-only module reached the client bundle via ${finding.chunk}`,
516
572
  )
517
573
  return (
518
- `[nifra/web] server-only module(s) in the client bundle - a module marked ` +
519
- `\`import "${SERVER_ONLY_MARKER}"\` reached the browser. Move it behind a server-only path ` +
520
- `(reach it via a loader/action, or rename it \`*.server.ts\`), so its server logic never ships ` +
521
- `to the client:\n${lines.join("\n")}`
574
+ `[nifra/web] backend-only module(s) in the client bundle - a module marked ` +
575
+ `\`import "${SERVER_ONLY_MARKER}"\` reached the browser. Move it under backend/ and reach it ` +
576
+ `from a route's backend half (x.backend.ts) or a *.fn.ts server function:\n${lines.join("\n")}`
522
577
  )
523
578
  }
524
579
 
@@ -555,7 +610,7 @@ export interface SizeReport {
555
610
  */
556
611
  export function aggregateSizeReport(chunks: readonly ChunkSize[]): SizeReport {
557
612
  const sorted = [...chunks].sort(
558
- (a, b) => b.gzip - a.gzip || b.bytes - a.bytes || a.name.localeCompare(b.name),
613
+ (a, b) => b.gzip - a.gzip || b.bytes - a.bytes || codeUnitOrder(a.name, b.name),
559
614
  )
560
615
  let totalBytes = 0
561
616
  let totalGzip = 0
package/src/build-vite.ts CHANGED
@@ -44,12 +44,17 @@ import {
44
44
  collectDevelopmentParityInput,
45
45
  } from "./internal/parity.ts"
46
46
  import { vitePublicEnvPrefix } from "./internal/server-boundary.ts"
47
+ import { unsupportedBuiltin } from "./internal/target-compat.ts"
47
48
  import { importVite, isViteUnresolved } from "./internal/vite-import.ts"
48
49
  import { scopedName } from "./plugins/css-modules.ts"
49
50
  import { reproduciblePath } from "./plugins/kit.ts"
50
- import { viteLeakGuard } from "./plugins/vite-leak-guard.ts"
51
+ import {
52
+ viteAssetUrlGuard,
53
+ viteBareBuiltinExternal,
54
+ viteLeakGuard,
55
+ viteServerZoneGuard,
56
+ } from "./plugins/vite-leak-guard.ts"
51
57
  import { viteServerFnStub } from "./plugins/vite-server-fn.ts"
52
- import { viteServerOnlyEmpty } from "./plugins/vite-server-only.ts"
53
58
 
54
59
  // ---------------------------------------------------------------------------------------------------
55
60
  // Structural Vite typings - no hard `vite` dependency (mirrors vite.ts). Only the build API is used.
@@ -174,6 +179,8 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
174
179
  ...routeManifest.routes.map((r) => r.file),
175
180
  ...Object.values(routeManifest.layouts).map((l) => l.file),
176
181
  ...(routeManifest.notFound ? [routeManifest.notFound.file] : []),
182
+ // A nested `_404` is an entry only for its stylesheet: it renders on the server, unhydrated.
183
+ ...Object.values(routeManifest.notFounds ?? {}).map((page) => page.file),
177
184
  ]),
178
185
  ].sort()
179
186
 
@@ -201,7 +208,28 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
201
208
  const buildEnv = (typeof Bun !== "undefined" ? Bun.env : undefined) ?? process.env
202
209
  const publicDefines = publicEnvDefines(options.publicEnvPrefix ?? "PUBLIC_", buildEnv)
203
210
  const vite = await loadVite()
204
- const leakGuard = viteLeakGuard()
211
+ // A worker is bundled by its own sub-build the main graph never sees, so it gets its own guard; the
212
+ // main guard then accepts the worker chunks that guard verified.
213
+ const verified = new Set<string>()
214
+ const exemptions = options.secretExemptions
215
+ const guardOptions = {
216
+ appRoot: dirname(routesDir),
217
+ routesDir,
218
+ root,
219
+ outDir: resolvePath(outDir),
220
+ generatedFiles: [entryFile],
221
+ verified,
222
+ ...(options.publicEnvPrefix !== undefined ? { publicEnvPrefix: options.publicEnvPrefix } : {}),
223
+ secrets: { env: buildEnv, ...(exemptions ? { exemptions } : {}) },
224
+ }
225
+ // nifra copies `public/` after the build; the main guard scans it with the bundle.
226
+ const publicDir = options.publicDir === false ? undefined : (options.publicDir ?? "public")
227
+ const leakGuard = viteLeakGuard({
228
+ ...guardOptions,
229
+ secrets: { ...guardOptions.secrets, ...(publicDir !== undefined ? { publicDir } : {}) },
230
+ })
231
+ const assetUrlGuard = viteAssetUrlGuard(guardOptions)
232
+ const workerGuards: { readonly leak?: string | undefined }[] = []
205
233
  try {
206
234
  await withSerializedNodeEnv(mode, () =>
207
235
  vite.build({
@@ -235,7 +263,17 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
235
263
  scopedName(reproduciblePath(filename), name),
236
264
  },
237
265
  },
238
- plugins: [...(options.vitePlugins ?? [])],
266
+ // First, so a bare built-in is named before Vite turns it into an anonymous stub.
267
+ // The asset URL guard last, so it reads each module after any framework plugin made it JavaScript.
268
+ plugins: [viteBareBuiltinExternal(), ...(options.vitePlugins ?? []), assetUrlGuard],
269
+ worker: {
270
+ plugins: () => {
271
+ const guard = viteLeakGuard(guardOptions)
272
+ const urlGuard = viteAssetUrlGuard(guardOptions)
273
+ workerGuards.push(guard, urlGuard)
274
+ return [viteBareBuiltinExternal(), viteServerFnStub(), guard, urlGuard]
275
+ },
276
+ },
239
277
  build: {
240
278
  outDir,
241
279
  emptyOutDir: false, // buildTargetWith owns outDir lifecycle; never let Vite wipe sibling files
@@ -251,7 +289,7 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
251
289
  external: [/^node:/],
252
290
  input,
253
291
  // The leak guard is a Rollup plugin - last, so it sees the final graph.
254
- plugins: [viteServerFnStub(), viteServerOnlyEmpty(), leakGuard],
292
+ plugins: [viteServerFnStub(), leakGuard],
255
293
  output: {
256
294
  entryFileNames: "[name]-[hash].js",
257
295
  chunkFileNames: "[name]-[hash].js",
@@ -267,7 +305,11 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
267
305
  // that construction fails, replacing a precise "node:crypto reached the client bundle" with an
268
306
  // internal complaint naming nothing. Raising it here puts the message beyond anything that can
269
307
  // rewrite it; the bundler's error is kept as `cause` for the rest of the context.
270
- if (leakGuard.leak !== undefined) throw new Error(leakGuard.leak, { cause: error })
308
+ const leak =
309
+ assetUrlGuard.leak ??
310
+ leakGuard.leak ??
311
+ workerGuards.find((guard) => guard.leak !== undefined)?.leak
312
+ if (leak !== undefined) throw new Error(leak, { cause: error })
271
313
  throw error
272
314
  } finally {
273
315
  rmSync(entryDir, { recursive: true, force: true })
@@ -336,6 +378,12 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
336
378
  if (css.length > 0 && cssCodeSplit) {
337
379
  for (const route of routeManifest.routes) routeStyles[route.id] = stylesFor(chainFiles(route))
338
380
  if (routeManifest.notFound) routeStyles._404 = stylesFor([routeManifest.notFound.file])
381
+ for (const [id, page] of Object.entries(routeManifest.notFounds ?? {})) {
382
+ routeStyles[id] = stylesFor([
383
+ ...page.layoutIds.map((layoutId) => routeManifest.layouts[layoutId]?.file ?? ""),
384
+ page.file,
385
+ ])
386
+ }
339
387
  }
340
388
 
341
389
  // assets - every emitted file + stylesheet across the manifest (chunks, entries, css).
@@ -346,7 +394,6 @@ export async function buildClientVite(options: BuildClientViteOptions): Promise<
346
394
  for (const asset of entry.assets ?? []) assets.add(url(asset))
347
395
  }
348
396
 
349
- const publicDir = options.publicDir === false ? undefined : (options.publicDir ?? "public")
350
397
  const publicFiles =
351
398
  publicDir !== undefined && existsSync(publicDir) ? await copyPublicDir(publicDir, outDir) : []
352
399
 
@@ -385,6 +432,10 @@ export interface BuildServerViteOptions {
385
432
  readonly target?: "browser" | "node" | "bun"
386
433
  /** Vite project root (default: the parent of `routesDir`). */
387
434
  readonly root?: string
435
+ /** Modules the build generated besides the entry and manifest (a target's adapter import). */
436
+ readonly generatedFiles?: readonly string[]
437
+ /** The public-env prefix browser code may read (default `"PUBLIC_"`). */
438
+ readonly publicEnvPrefix?: string
388
439
  }
389
440
 
390
441
  interface EdgeBundleChunk {
@@ -431,12 +482,13 @@ function edgeBuiltinGuard(): EdgeGuardPlugin {
431
482
  for (const output of Object.values(bundle)) {
432
483
  if (output.type !== "chunk") continue
433
484
  for (const specifier of [...(output.imports ?? []), ...(output.dynamicImports ?? [])]) {
434
- if (specifier.startsWith("node:")) builtins.add(specifier)
485
+ if (unsupportedBuiltin(specifier, "browser")) builtins.add(specifier)
435
486
  }
436
487
  for (const id of output.moduleIds ?? []) {
437
488
  const info = this.getModuleInfo(id)
438
489
  const imports = [...(info?.importedIds ?? []), ...(info?.dynamicallyImportedIds ?? [])]
439
- if (imports.some((specifier) => specifier.startsWith("node:"))) importers.add(id)
490
+ if (imports.some((specifier) => unsupportedBuiltin(specifier, "browser")))
491
+ importers.add(id)
440
492
  }
441
493
  }
442
494
  if (builtins.size === 0) return
@@ -497,6 +549,16 @@ export async function buildServerVite(options: BuildServerViteOptions): Promise<
497
549
  const mode = options.minify === false ? "development" : "production"
498
550
  const vite = await loadVite()
499
551
  const edgeGuard = edgeBuiltinGuard()
552
+ const zoneGuard = viteServerZoneGuard({
553
+ appRoot: root,
554
+ routesDir: resolvePath(routesDir),
555
+ generatedFiles: [
556
+ serverEntry,
557
+ join(entryDir, manifestFile),
558
+ ...(options.generatedFiles ?? []),
559
+ ].map((file) => resolvePath(file)),
560
+ ...(options.publicEnvPrefix !== undefined ? { publicEnvPrefix: options.publicEnvPrefix } : {}),
561
+ })
500
562
  try {
501
563
  await withSerializedNodeEnv(mode, () =>
502
564
  vite.build({
@@ -525,7 +587,7 @@ export async function buildServerVite(options: BuildServerViteOptions): Promise<
525
587
  // Keep builtins external so Node/Bun use their native implementations. Edge builds add a
526
588
  // generateBundle guard below, so an external specifier can never silently ship to workerd.
527
589
  external: [/^node:/],
528
- ...(edge ? { plugins: [edgeGuard] } : {}),
590
+ plugins: [...(edge ? [edgeGuard] : []), zoneGuard],
529
591
  // ONE self-contained `server.js`. `inlineDynamicImports` forces the ENTRY chunk to absorb
530
592
  // every module - the app, the adapter, react/react-dom, @nifrajs/* - so no second chunk is
531
593
  // emitted. It is NOT redundant with `ssr.noExternal`: `noExternal` decides what gets bundled,
@@ -550,7 +612,8 @@ export async function buildServerVite(options: BuildServerViteOptions): Promise<
550
612
  } catch (error) {
551
613
  // Re-raise the guard's own message from here, where no bundler error-reporting path can rewrite it
552
614
  // (see the same handling in buildClientVite).
553
- if (edgeGuard.leak !== undefined) throw new Error(edgeGuard.leak, { cause: error })
615
+ const leak = zoneGuard.leak ?? edgeGuard.leak
616
+ if (leak !== undefined) throw new Error(leak, { cause: error })
554
617
  throw error
555
618
  }
556
619
 
@@ -578,6 +641,7 @@ export const viteBundler: Bundler = {
578
641
  ...(input.cssLoading !== undefined ? { cssLoading: input.cssLoading } : {}),
579
642
  ...(input.publicDir !== undefined ? { publicDir: input.publicDir } : {}),
580
643
  ...(input.publicEnvPrefix !== undefined ? { publicEnvPrefix: input.publicEnvPrefix } : {}),
644
+ ...(input.secretExemptions !== undefined ? { secretExemptions: input.secretExemptions } : {}),
581
645
  ...(input.root ? { root: input.root } : {}),
582
646
  }),
583
647
  buildServer: (input) =>
@@ -593,6 +657,8 @@ export const viteBundler: Bundler = {
593
657
  ...(input.define ? { define: input.define } : {}),
594
658
  ...(input.cssLoading !== undefined ? { cssLoading: input.cssLoading } : {}),
595
659
  ...(input.root ? { root: input.root } : {}),
660
+ ...(input.generatedFiles !== undefined ? { generatedFiles: input.generatedFiles } : {}),
661
+ ...(input.publicEnvPrefix !== undefined ? { publicEnvPrefix: input.publicEnvPrefix } : {}),
596
662
  }),
597
663
  }
598
664