@nifrajs/web 3.5.0 → 4.0.0

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 +111 -15
  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 +485 -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 +386 -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 +72 -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 +187 -36
  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 +111 -18
  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 +623 -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 +670 -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 +110 -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 +207 -36
  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
@@ -1,83 +1,20 @@
1
1
  /**
2
- * The server/client boundary, owned in one place.
2
+ * The client-build transforms that every pipeline must apply identically - the Bun client build, the
3
+ * Vite dev server, the Vite production build, and `nifra dev --bun`:
3
4
  *
4
- * The client build must never ship server-only code, or server-only config, to a browser. Three
5
- * conventions enforce that, and each has to mean the SAME thing in every pipeline that builds the client
6
- * - the Bun client build, the Vite dev server, the Vite production build, and `nifra dev --bun`:
7
- *
8
- * - `*.server` modules - emptied, so a secret / `node:` / native import subtree never reaches the client.
9
5
  * - `*.fn` modules - replaced with HTTP stubs, so the function bodies (and their imports) stay server-side.
10
6
  * - public-env prefix - which environment variables the client bundle is allowed to inline.
11
7
  *
12
- * When each pipeline re-encoded one of these rules, they drifted. A hand-written glob
13
- * `**\/*.server.{ts,tsx,js,jsx}` missed the extensionless `db.server` and the `.mts`/`.cts`/`.mjs`/`.cjs`
14
- * forms the regex matches, waving a module the build empties straight into the browser. So the matchers,
15
- * the replacement bodies, and the env mapping get exactly one definition here; every pipeline imports it,
16
- * and parity tests assert the Bun and Vite halves emit identical bytes. A matcher that decides whether a
17
- * secret can reach a browser gets one owner, not four.
8
+ * Which files may reach a browser at all is the zone classifier's job (`zones.ts`). These two get one
9
+ * definition here so the Bun and Vite halves emit identical bytes; a copied-then-edited second version
10
+ * is how a client that works in dev and 404s in production happens.
18
11
  */
19
12
 
20
- // Both module conventions match a filename SUFFIX with the identical optional source-extension tail -
21
- // `.ts`, `.tsx`, `.mts`, `.cts`, `.mjs`, `.cjs`, `.js`, `.jsx`, or none (the extensionless `db.server` /
22
- // `todos.fn`). Kept adjacent so the shared shape is visible, and a parity test pins that the two never
23
- // diverge on which extensions count - a copied-then-edited second tail is exactly how the old glob drifted.
24
-
25
- /** Matches `db.server.ts`, `auth.server.tsx`, `x.server.mjs`, and the extensionless `foo.server`. */
26
- export const SERVER_ONLY_MODULE = /\.server(\.[cm]?[jt]sx?)?$/
27
- /** Modules whose exports become client stubs. Mirrors the `.server` convention's shape. */
13
+ /** Modules whose exports become client stubs: `todos.fn.ts`, `x.fn.mjs`, or the extensionless `foo.fn`. */
28
14
  export const SERVER_FN_MODULE = /\.fn(\.[cm]?[jt]sx?)?$/
29
15
 
30
- // ---------------------------------------------------------------------------------------------------
31
- // `.server` - empty a server-only module in the client build.
32
- // ---------------------------------------------------------------------------------------------------
33
-
34
- /**
35
- * The replacement body for an emptied module. A Proxy rather than `export {}` so any named OR default
36
- * import resolves to `undefined` instead of failing the bundle with a missing-export error - the client
37
- * degrades at the call site it wrote, not at link time in a file it never named.
38
- *
39
- * Shared so the Bun and Vite pipelines emit the same bytes; a parity test asserts it.
40
- */
41
- export const SERVER_ONLY_REPLACEMENT = "module.exports = new Proxy({}, { get: () => undefined })"
42
-
43
16
  import { jsStringLiteral } from "./js-string.ts"
44
17
 
45
- const EXPORTED_DECLARATION =
46
- /\bexport\s+(?:declare\s+)?(?:async\s+)?(?:const|let|var|function|class|enum)\s+([A-Za-z_$][\w$]*)/g
47
- const EXPORTED_LIST = /\bexport\s*\{([^}]*)\}(?:\s*from\s*["'][^"']+["'])?/g
48
-
49
- /**
50
- * Vite dev serves native ESM, so the Bun/CommonJS proxy above is invalid there. Emit inert ESM
51
- * bindings derived from the source's public names while discarding the implementation and imports.
52
- * An unsupported exotic export fails closed at ESM link time; server code is never served as fallback.
53
- */
54
- export function viteServerOnlyReplacement(source: string): string {
55
- const names = new Set<string>()
56
- let hasDefault = /\bexport\s+default\b/.test(source)
57
-
58
- for (const match of source.matchAll(EXPORTED_DECLARATION)) names.add(match[1] as string)
59
- for (const match of source.matchAll(EXPORTED_LIST)) {
60
- for (const raw of (match[1] ?? "").split(",")) {
61
- // Collapse runs of whitespace first (linear), so the ` as ` split below needs no `\s+as\s+`
62
- // regex - adjacent unbounded quantifiers around a literal backtrack quadratically on crafted
63
- // all-whitespace input.
64
- const item = raw
65
- .trim()
66
- .replace(/\s+/g, " ")
67
- .replace(/^type /, "")
68
- if (item === "") continue
69
- const parts = item.split(" as ")
70
- const exported = (parts[1] ?? parts[0] ?? "").trim()
71
- if (exported === "default") hasDefault = true
72
- else if (/^[A-Za-z_$][\w$]*$/.test(exported)) names.add(exported)
73
- }
74
- }
75
-
76
- const bindings = [...names].sort().map((name) => `export const ${name} = undefined`)
77
- if (hasDefault) bindings.push("export default undefined")
78
- return `// Generated by @nifrajs/web: server-only implementation removed from the browser.\n${bindings.join("\n")}\n`
79
- }
80
-
81
18
  // ---------------------------------------------------------------------------------------------------
82
19
  // `.fn` - turn a server-function module into the client stubs that call it.
83
20
  // ---------------------------------------------------------------------------------------------------
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Source map v3 decoding, encoding and lookup, shared by the browser-frame mapper, the dev server's
3
+ * SSR frame remap and the SFC plugins that join several compiler outputs into one module. Node-free.
4
+ */
5
+
6
+ /** A source map as its v3 JSON object. */
7
+ export interface RawSourceMap {
8
+ /** `3`; some compilers type it as the string. */
9
+ readonly version: number | string
10
+ readonly sources: readonly string[]
11
+ readonly sourcesContent?: readonly (string | null)[] | undefined
12
+ readonly names?: readonly string[] | undefined
13
+ readonly mappings: string
14
+ readonly file?: string | undefined
15
+ }
16
+
17
+ /** One generated line's segments, flattened: [column, source, line, column] per segment, all absolute. */
18
+ export type MappingLine = Int32Array
19
+
20
+ const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
21
+ const BASE64_VALUE = new Int8Array(128).fill(-1)
22
+ for (let i = 0; i < BASE64.length; i++) BASE64_VALUE[BASE64.charCodeAt(i)] = i
23
+
24
+ /** Decode a source map v3 `mappings` string. Segments without a source are dropped. */
25
+ export function decodeMappings(mappings: string): MappingLine[] {
26
+ const lines: MappingLine[] = []
27
+ let segment: number[] = []
28
+ let current: number[] = []
29
+ let generatedColumn = 0
30
+ let source = 0
31
+ let sourceLine = 0
32
+ let sourceColumn = 0
33
+ let value = 0
34
+ let shift = 0
35
+ const endSegment = (): void => {
36
+ if (segment.length === 0) return
37
+ generatedColumn += segment[0] ?? 0
38
+ if (segment.length >= 4) {
39
+ source += segment[1] ?? 0
40
+ sourceLine += segment[2] ?? 0
41
+ sourceColumn += segment[3] ?? 0
42
+ current.push(generatedColumn, source, sourceLine, sourceColumn)
43
+ }
44
+ segment = []
45
+ }
46
+ for (let i = 0; i < mappings.length; i++) {
47
+ const code = mappings.charCodeAt(i)
48
+ if (code === 44 /* , */) {
49
+ endSegment()
50
+ continue
51
+ }
52
+ if (code === 59 /* ; */) {
53
+ endSegment()
54
+ lines.push(Int32Array.from(current))
55
+ current = []
56
+ generatedColumn = 0
57
+ continue
58
+ }
59
+ const digit = code < 128 ? (BASE64_VALUE[code] ?? -1) : -1
60
+ if (digit === -1) throw new Error(`invalid VLQ character at ${i}`)
61
+ value += (digit & 31) << shift
62
+ if (digit & 32) {
63
+ shift += 5
64
+ continue
65
+ }
66
+ segment.push(value & 1 ? -(value >>> 1) : value >>> 1)
67
+ value = 0
68
+ shift = 0
69
+ }
70
+ endSegment()
71
+ lines.push(Int32Array.from(current))
72
+ return lines
73
+ }
74
+
75
+ function vlq(value: number): string {
76
+ let rest = value < 0 ? (-value << 1) | 1 : value << 1
77
+ let out = ""
78
+ do {
79
+ let digit = rest & 31
80
+ rest >>>= 5
81
+ if (rest > 0) digit |= 32
82
+ out += BASE64[digit]
83
+ } while (rest > 0)
84
+ return out
85
+ }
86
+
87
+ /** Encode decoded lines back into a `mappings` string. */
88
+ export function encodeMappings(lines: readonly MappingLine[]): string {
89
+ let source = 0
90
+ let sourceLine = 0
91
+ let sourceColumn = 0
92
+ return lines
93
+ .map((segments) => {
94
+ let generatedColumn = 0
95
+ const parts: string[] = []
96
+ for (let i = 0; i + 3 < segments.length; i += 4) {
97
+ const column = segments[i] ?? 0
98
+ const s = segments[i + 1] ?? 0
99
+ const l = segments[i + 2] ?? 0
100
+ const c = segments[i + 3] ?? 0
101
+ parts.push(
102
+ vlq(column - generatedColumn) +
103
+ vlq(s - source) +
104
+ vlq(l - sourceLine) +
105
+ vlq(c - sourceColumn),
106
+ )
107
+ generatedColumn = column
108
+ source = s
109
+ sourceLine = l
110
+ sourceColumn = c
111
+ }
112
+ return parts.join(",")
113
+ })
114
+ .join(";")
115
+ }
116
+
117
+ /** The source position for a 0-based generated line/column: the last segment at or before it. */
118
+ export function lookupPosition(
119
+ lines: readonly (MappingLine | undefined)[],
120
+ line: number,
121
+ column: number,
122
+ ): { readonly source: number; readonly line: number; readonly column: number } | undefined {
123
+ const segments = lines[line]
124
+ if (segments === undefined || segments.length === 0) return undefined
125
+ let low = 0
126
+ let high = segments.length / 4 - 1
127
+ let found = -1
128
+ while (low <= high) {
129
+ const mid = (low + high) >>> 1
130
+ if ((segments[mid * 4] ?? 0) <= column) {
131
+ found = mid
132
+ low = mid + 1
133
+ } else high = mid - 1
134
+ }
135
+ if (found === -1) found = 0
136
+ return {
137
+ source: segments[found * 4 + 1] ?? 0,
138
+ line: (segments[found * 4 + 2] ?? 0) + 1,
139
+ column: (segments[found * 4 + 3] ?? 0) + 1,
140
+ }
141
+ }
142
+
143
+ const decoded = new WeakMap<RawSourceMap, MappingLine[]>()
144
+
145
+ /** Where a 1-based generated line/column came from in `map`'s first-listed sources, 1-based. */
146
+ export function originalPosition(
147
+ map: RawSourceMap,
148
+ line: number,
149
+ column: number,
150
+ ): { readonly line: number; readonly column: number } | undefined {
151
+ let lines = decoded.get(map)
152
+ if (lines === undefined) {
153
+ lines = decodeMappings(map.mappings)
154
+ decoded.set(map, lines)
155
+ }
156
+ const found = lookupPosition(lines, line - 1, column - 1)
157
+ return found === undefined ? undefined : { line: found.line, column: found.column }
158
+ }
159
+
160
+ const INLINE_MAP = /\/\/[#@] sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)\s*$/
161
+
162
+ /** The map a module carries as a trailing base64 `sourceMappingURL` comment, when it does. */
163
+ export function inlineSourceMap(code: string): RawSourceMap | undefined {
164
+ const found = INLINE_MAP.exec(code.slice(-4 * 1024 * 1024))
165
+ if (found === null) return undefined
166
+ let parsed: unknown
167
+ try {
168
+ parsed = JSON.parse(atob(found[1] ?? ""))
169
+ } catch {
170
+ return undefined
171
+ }
172
+ if (typeof parsed !== "object" || parsed === null) return undefined
173
+ const mappings: unknown = Reflect.get(parsed, "mappings")
174
+ const sources: unknown = Reflect.get(parsed, "sources")
175
+ if (typeof mappings !== "string" || !Array.isArray(sources)) return undefined
176
+ return {
177
+ version: 3,
178
+ sources: sources.map((source) => (typeof source === "string" ? source : "")),
179
+ mappings,
180
+ }
181
+ }
182
+
183
+ /**
184
+ * One map over parts joined with no separator, each part either mapped by its own map or unmapped
185
+ * (generated glue). Sources are merged by name.
186
+ */
187
+ export function concatSourceMaps(
188
+ parts: readonly { readonly code: string; readonly map?: RawSourceMap | undefined }[],
189
+ ): RawSourceMap {
190
+ const sources: string[] = []
191
+ const sourcesContent: (string | null)[] = []
192
+ const out: number[][] = [[]]
193
+ for (const part of parts) {
194
+ const lineOffset = out.length - 1
195
+ // A part that starts mid-line shifts its first line's columns by what is already on that line.
196
+ const columnOffset = lastLineLength(parts, part)
197
+ if (part.map !== undefined) {
198
+ const index = part.map.sources.map((source, i) => {
199
+ let at = sources.indexOf(source)
200
+ if (at === -1) {
201
+ at = sources.push(source) - 1
202
+ sourcesContent.push(part.map?.sourcesContent?.[i] ?? null)
203
+ }
204
+ return at
205
+ })
206
+ decodeMappings(part.map.mappings).forEach((segments, line) => {
207
+ const target = out[lineOffset + line] ?? []
208
+ out[lineOffset + line] = target
209
+ for (let i = 0; i + 3 < segments.length; i += 4) {
210
+ target.push(
211
+ (segments[i] ?? 0) + (line === 0 ? columnOffset : 0),
212
+ index[segments[i + 1] ?? 0] ?? 0,
213
+ segments[i + 2] ?? 0,
214
+ segments[i + 3] ?? 0,
215
+ )
216
+ }
217
+ })
218
+ }
219
+ const newlines = part.code.split("\n").length - 1
220
+ for (let i = 0; i < newlines; i++) out[lineOffset + 1 + i] ??= []
221
+ }
222
+ return {
223
+ version: 3,
224
+ sources,
225
+ sourcesContent,
226
+ names: [],
227
+ mappings: encodeMappings(out.map((line) => Int32Array.from(line))),
228
+ }
229
+ }
230
+
231
+ function lastLineLength(
232
+ parts: readonly { readonly code: string }[],
233
+ until: { readonly code: string },
234
+ ): number {
235
+ let length = 0
236
+ for (const part of parts) {
237
+ if (part === until) return length
238
+ const newline = part.code.lastIndexOf("\n")
239
+ length = newline === -1 ? length + part.code.length : part.code.length - newline - 1
240
+ }
241
+ return length
242
+ }
243
+
244
+ const REGISTRY = Symbol.for("nifra.dev.ssrSourceMaps")
245
+
246
+ /**
247
+ * The maps of modules a dev plugin compiled for SSR, by file path. Bun's runtime ignores a plugin's
248
+ * inline map, so a server stack names the compiled line; the dev server remaps through this.
249
+ */
250
+ export function ssrSourceMaps(): Map<string, RawSourceMap> {
251
+ const existing: unknown = Reflect.get(globalThis, REGISTRY)
252
+ if (existing instanceof Map) return existing
253
+ const created = new Map<string, RawSourceMap>()
254
+ Reflect.set(globalThis, REGISTRY, created)
255
+ return created
256
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Which runtime built-ins a server bundle can load on its target. Separate from the zone rules: a
3
+ * backend module may import `node:fs`, but an edge worker cannot run it, and a Node server has no
4
+ * `bun:` modules. Same refusal and wording as the Vite edge build's guard.
5
+ */
6
+ import type { ClientModuleGraph, GraphImport } from "../module-graph.ts"
7
+
8
+ export type ServerBundleTarget = "browser" | "node" | "bun"
9
+
10
+ /** Built-in prefixes each target cannot load. The `browser` target is the edge (workerd, edge-light). */
11
+ const UNSUPPORTED: Readonly<Record<ServerBundleTarget, readonly string[]>> = {
12
+ browser: ["node:", "bun:"],
13
+ node: ["bun:"],
14
+ bun: [],
15
+ }
16
+
17
+ /**
18
+ * The `node:` built-ins every edge runtime nifra deploys to provides: Cloudflare Workers with
19
+ * `nodejs_compat` (the scaffold's wrangler.toml sets it), Vercel Edge and Deno. Svelte's server renderer
20
+ * imports `node:async_hooks` for AsyncLocalStorage.
21
+ */
22
+ export const EDGE_NODE_BUILTINS: ReadonlySet<string> = new Set([
23
+ "node:assert",
24
+ "node:async_hooks",
25
+ "node:buffer",
26
+ "node:events",
27
+ "node:util",
28
+ ])
29
+
30
+ /** Whether a server bundle for `target` cannot load the built-in `specifier`. */
31
+ export function unsupportedBuiltin(specifier: string, target: ServerBundleTarget): boolean {
32
+ if (target === "browser" && EDGE_NODE_BUILTINS.has(specifier)) return false
33
+ return UNSUPPORTED[target].some((prefix) => specifier.startsWith(prefix))
34
+ }
35
+
36
+ /**
37
+ * The built-ins a server bundle still loads that its target cannot, each with the modules that import
38
+ * them. A tree-shaken library module that imports `node:fs` is not one: only what the output keeps can
39
+ * fail. Two kinds are kept: an import the emitted code still makes (a dynamic `import()` that survived),
40
+ * and a static import of a built-in the bundler left external in a module that ships, which Bun's
41
+ * browser target replaces with an empty object that throws on first use. A chunk without `imports`
42
+ * evidence falls back to every module import, so missing evidence refuses.
43
+ */
44
+ export function unsupportedBuiltins(
45
+ graph: ClientModuleGraph,
46
+ target: ServerBundleTarget,
47
+ labelOf: (id: string) => string,
48
+ ): ReadonlyMap<string, readonly string[]> {
49
+ if (UNSUPPORTED[target].length === 0) return new Map()
50
+ const unsupported = (spec: string) => unsupportedBuiltin(spec, target)
51
+ const specOf = (im: GraphImport) => im.original ?? im.path ?? ""
52
+ const chunks = Object.values(graph.chunks)
53
+ const evidence = chunks.every((chunk) => chunk.imports !== undefined)
54
+ const shipped = evidence
55
+ ? new Set(chunks.flatMap((chunk) => chunk.modules))
56
+ : new Set(Object.keys(graph.modules))
57
+ const found = new Map<string, Set<string>>()
58
+ const keep = (builtin: string) => {
59
+ if (unsupported(builtin) && !found.has(builtin)) found.set(builtin, new Set())
60
+ }
61
+ for (const chunk of chunks) for (const spec of chunk.imports ?? []) keep(spec)
62
+ for (const id of shipped) {
63
+ for (const im of graph.modules[id]?.imports ?? []) {
64
+ if (!evidence || (im.external === true && im.dynamic !== true)) keep(specOf(im))
65
+ }
66
+ }
67
+ for (const id of shipped) {
68
+ for (const im of graph.modules[id]?.imports ?? []) found.get(specOf(im))?.add(labelOf(id))
69
+ }
70
+ return new Map([...found].map(([builtin, importers]) => [builtin, [...importers].sort()]))
71
+ }
72
+
73
+ /** The build-failing message for {@link unsupportedBuiltins}, or `undefined` when there are none. */
74
+ export function formatUnsupportedBuiltins(
75
+ found: ReadonlyMap<string, readonly string[]>,
76
+ target: ServerBundleTarget,
77
+ ): string | undefined {
78
+ if (found.size === 0) return undefined
79
+ const builtins = [...found.keys()].sort()
80
+ const importers = [...new Set([...found.values()].flat())].sort().join(", ") || "unknown module"
81
+ if (target === "browser") {
82
+ return (
83
+ `[nifra/web] Node built-in(s) reached an edge server bundle: ${builtins.join(", ")}. ` +
84
+ `Imported by: ${importers}. ` +
85
+ "Move the import behind a Node/Bun target or replace it with an edge-compatible API."
86
+ )
87
+ }
88
+ return (
89
+ `[nifra/web] Bun built-in(s) reached a Node server bundle: ${builtins.join(", ")}. ` +
90
+ `Imported by: ${importers}. Build for the Bun target or replace it with a Node API.`
91
+ )
92
+ }
@@ -22,3 +22,16 @@ export function timingSafeEqual(a: string, b: string): boolean {
22
22
  for (let i = 0; i < ba.length; i++) diff |= (ba[i] as number) ^ (bb[i] as number)
23
23
  return diff === 0
24
24
  }
25
+
26
+ /**
27
+ * Boot-time guard for a shared bearer token checked with {@link timingSafeEqual}. A missing header or
28
+ * query parameter is compared as `""`, so an empty (or unset, then defaulted) secret would authorize
29
+ * every request that simply omits the token. Throws rather than letting that endpoint exist.
30
+ */
31
+ export function assertTokenSecret(secret: unknown, label: string): asserts secret is string {
32
+ if (typeof secret !== "string" || secret.trim() === "") {
33
+ throw new TypeError(
34
+ `[nifra] ${label} requires a non-empty secret; an empty one would accept requests that omit the token`,
35
+ )
36
+ }
37
+ }
@@ -0,0 +1,53 @@
1
+ /** `@nifrajs/web/vitals` with the metric library passed in, so tests can use a fake. */
2
+ import type { MetricType, ReportOpts } from "web-vitals"
3
+ import { getBrowserRouter } from "../navigation.ts"
4
+ import type { ReportWebVitalsOptions, WebVitalsMetric } from "../vitals.ts"
5
+
6
+ type OnMetric = (report: (metric: MetricType) => void, options?: ReportOpts) => void
7
+
8
+ /** The `web-vitals` functions the reporter registers with. */
9
+ export interface VitalsLibrary {
10
+ readonly onCLS: OnMetric
11
+ readonly onFCP: OnMetric
12
+ readonly onINP: OnMetric
13
+ readonly onLCP: OnMetric
14
+ readonly onTTFB: OnMetric
15
+ }
16
+
17
+ /** The route id a measured URL matches, through the router `installHistory` registered. A metric of a
18
+ * client navigation names that navigation's URL; any other names the document's, and a library that
19
+ * names none falls back to the current one. */
20
+ function routeOf(url: string | undefined): string | undefined {
21
+ const router = getBrowserRouter()
22
+ if (router === undefined) return undefined
23
+ const target = new URL(url ?? location.href, location.href)
24
+ if (target.origin !== location.origin) return undefined
25
+ return router.match(target.pathname + target.search)?.routeId
26
+ }
27
+
28
+ /** Register `report` with every metric `library` measures. Each report is a fresh object, so a metric
29
+ * kept by the caller does not change when the library updates its own copy. On the server there is
30
+ * nothing to measure, and the returned stop function is a no-op. */
31
+ export function reportWith(
32
+ library: VitalsLibrary,
33
+ report: (metric: WebVitalsMetric) => void,
34
+ options: ReportWebVitalsOptions,
35
+ ): () => void {
36
+ if (typeof document === "undefined") return () => {}
37
+ let active = true
38
+ const opts: ReportOpts = {
39
+ reportAllChanges: options.reportAllChanges === true,
40
+ reportSoftNavs: options.softNavigations === true,
41
+ }
42
+ const forward = (metric: MetricType): void => {
43
+ if (active) report({ ...metric, route: routeOf(metric.navigationURL) })
44
+ }
45
+ library.onCLS(forward, opts)
46
+ library.onFCP(forward, opts)
47
+ library.onINP(forward, opts)
48
+ library.onLCP(forward, opts)
49
+ library.onTTFB(forward, opts)
50
+ return () => {
51
+ active = false
52
+ }
53
+ }