@nifrajs/cli 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 (404) hide show
  1. package/README.md +42 -3
  2. package/dist/app-files.d.ts +11 -0
  3. package/dist/app-files.d.ts.map +1 -0
  4. package/dist/app-files.js +34 -0
  5. package/dist/app-files.js.map +1 -0
  6. package/dist/assure-hydration.d.ts +7 -1
  7. package/dist/assure-hydration.d.ts.map +1 -1
  8. package/dist/assure-hydration.js +128 -43
  9. package/dist/assure-hydration.js.map +1 -1
  10. package/dist/assure.d.ts.map +1 -1
  11. package/dist/assure.js +16 -2
  12. package/dist/assure.js.map +1 -1
  13. package/dist/capabilities-tool.d.ts +5 -1
  14. package/dist/capabilities-tool.d.ts.map +1 -1
  15. package/dist/capabilities-tool.js +32 -10
  16. package/dist/capabilities-tool.js.map +1 -1
  17. package/dist/cdn-check.d.ts +41 -0
  18. package/dist/cdn-check.d.ts.map +1 -0
  19. package/dist/cdn-check.js +207 -0
  20. package/dist/cdn-check.js.map +1 -0
  21. package/dist/check-diagnostics.d.ts +4 -0
  22. package/dist/check-diagnostics.d.ts.map +1 -1
  23. package/dist/check-diagnostics.js +13 -0
  24. package/dist/check-diagnostics.js.map +1 -1
  25. package/dist/check-scan.d.ts +49 -7
  26. package/dist/check-scan.d.ts.map +1 -1
  27. package/dist/check-scan.js +291 -62
  28. package/dist/check-scan.js.map +1 -1
  29. package/dist/check.d.ts.map +1 -1
  30. package/dist/check.js +99 -63
  31. package/dist/check.js.map +1 -1
  32. package/dist/cli.d.ts +42 -16
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +262 -82
  35. package/dist/cli.js.map +1 -1
  36. package/dist/command-catalog.d.ts +62 -5
  37. package/dist/command-catalog.d.ts.map +1 -1
  38. package/dist/command-catalog.js +275 -35
  39. package/dist/command-catalog.js.map +1 -1
  40. package/dist/config-target.d.ts +8 -0
  41. package/dist/config-target.d.ts.map +1 -0
  42. package/dist/config-target.js +45 -0
  43. package/dist/config-target.js.map +1 -0
  44. package/dist/contracts.d.ts.map +1 -1
  45. package/dist/contracts.js +52 -13
  46. package/dist/contracts.js.map +1 -1
  47. package/dist/db-audit.d.ts +37 -0
  48. package/dist/db-audit.d.ts.map +1 -0
  49. package/dist/db-audit.js +99 -0
  50. package/dist/db-audit.js.map +1 -0
  51. package/dist/db-child.d.ts +64 -0
  52. package/dist/db-child.d.ts.map +1 -0
  53. package/dist/db-child.js +213 -0
  54. package/dist/db-child.js.map +1 -0
  55. package/dist/db-config.d.ts +60 -0
  56. package/dist/db-config.d.ts.map +1 -0
  57. package/dist/db-config.js +176 -0
  58. package/dist/db-config.js.map +1 -0
  59. package/dist/db-tool.d.ts +84 -0
  60. package/dist/db-tool.d.ts.map +1 -0
  61. package/dist/db-tool.js +537 -0
  62. package/dist/db-tool.js.map +1 -0
  63. package/dist/dev-bun-config.d.ts +20 -13
  64. package/dist/dev-bun-config.d.ts.map +1 -1
  65. package/dist/dev-bun-config.js +98 -41
  66. package/dist/dev-bun-config.js.map +1 -1
  67. package/dist/dev-feed-tool.d.ts +106 -0
  68. package/dist/dev-feed-tool.d.ts.map +1 -0
  69. package/dist/dev-feed-tool.js +713 -0
  70. package/dist/dev-feed-tool.js.map +1 -0
  71. package/dist/dev-server-client.d.ts +67 -0
  72. package/dist/dev-server-client.d.ts.map +1 -0
  73. package/dist/dev-server-client.js +195 -0
  74. package/dist/dev-server-client.js.map +1 -0
  75. package/dist/diff-tool.d.ts +3 -3
  76. package/dist/diff-tool.d.ts.map +1 -1
  77. package/dist/diff-tool.js +6 -5
  78. package/dist/diff-tool.js.map +1 -1
  79. package/dist/docs-search.d.ts.map +1 -1
  80. package/dist/docs-search.js +12 -4
  81. package/dist/docs-search.js.map +1 -1
  82. package/dist/doctor.d.ts +17 -0
  83. package/dist/doctor.d.ts.map +1 -1
  84. package/dist/doctor.js +76 -15
  85. package/dist/doctor.js.map +1 -1
  86. package/dist/env-file.d.ts +15 -0
  87. package/dist/env-file.d.ts.map +1 -1
  88. package/dist/env-file.js +73 -1
  89. package/dist/env-file.js.map +1 -1
  90. package/dist/fix-recipes.d.ts.map +1 -1
  91. package/dist/fix-recipes.js +48 -22
  92. package/dist/fix-recipes.js.map +1 -1
  93. package/dist/frontend-guidance.js +5 -5
  94. package/dist/frontend-guidance.js.map +1 -1
  95. package/dist/i18n-check.d.ts +16 -0
  96. package/dist/i18n-check.d.ts.map +1 -0
  97. package/dist/i18n-check.js +154 -0
  98. package/dist/i18n-check.js.map +1 -0
  99. package/dist/init-agents.d.ts +49 -13
  100. package/dist/init-agents.d.ts.map +1 -1
  101. package/dist/init-agents.js +239 -34
  102. package/dist/init-agents.js.map +1 -1
  103. package/dist/internal/code-unit-order.d.ts +4 -0
  104. package/dist/internal/code-unit-order.d.ts.map +1 -0
  105. package/dist/internal/code-unit-order.js +6 -0
  106. package/dist/internal/code-unit-order.js.map +1 -0
  107. package/dist/internal/script-kind.d.ts +7 -0
  108. package/dist/internal/script-kind.d.ts.map +1 -0
  109. package/dist/internal/script-kind.js +12 -0
  110. package/dist/internal/script-kind.js.map +1 -0
  111. package/dist/internal/source-facts.d.ts +5 -0
  112. package/dist/internal/source-facts.d.ts.map +1 -1
  113. package/dist/internal/source-facts.js +61 -5
  114. package/dist/internal/source-facts.js.map +1 -1
  115. package/dist/internal/typescript-import.d.ts +49 -24
  116. package/dist/internal/typescript-import.d.ts.map +1 -1
  117. package/dist/internal/typescript-import.js +507 -67
  118. package/dist/internal/typescript-import.js.map +1 -1
  119. package/dist/introspect.d.ts +5 -1
  120. package/dist/introspect.d.ts.map +1 -1
  121. package/dist/introspect.js +92 -27
  122. package/dist/introspect.js.map +1 -1
  123. package/dist/learn.js +3 -3
  124. package/dist/learn.js.map +1 -1
  125. package/dist/levels-tool.d.ts +10 -0
  126. package/dist/levels-tool.d.ts.map +1 -1
  127. package/dist/levels-tool.js +33 -3
  128. package/dist/levels-tool.js.map +1 -1
  129. package/dist/load.d.ts +67 -17
  130. package/dist/load.d.ts.map +1 -1
  131. package/dist/load.js +78 -18
  132. package/dist/load.js.map +1 -1
  133. package/dist/manifest-tool.d.ts.map +1 -1
  134. package/dist/manifest-tool.js +3 -2
  135. package/dist/manifest-tool.js.map +1 -1
  136. package/dist/mcp-app-tool.d.ts.map +1 -1
  137. package/dist/mcp-app-tool.js +2 -0
  138. package/dist/mcp-app-tool.js.map +1 -1
  139. package/dist/mcp-context.js +1 -1
  140. package/dist/mcp-context.js.map +1 -1
  141. package/dist/mcp-delegate.d.ts +57 -0
  142. package/dist/mcp-delegate.d.ts.map +1 -0
  143. package/dist/mcp-delegate.js +130 -0
  144. package/dist/mcp-delegate.js.map +1 -0
  145. package/dist/mcp-docs-tools.d.ts.map +1 -1
  146. package/dist/mcp-docs-tools.js +23 -5
  147. package/dist/mcp-docs-tools.js.map +1 -1
  148. package/dist/mcp-exec.d.ts +2 -0
  149. package/dist/mcp-exec.d.ts.map +1 -1
  150. package/dist/mcp-exec.js +132 -108
  151. package/dist/mcp-exec.js.map +1 -1
  152. package/dist/mcp-http.d.ts.map +1 -1
  153. package/dist/mcp-http.js +17 -5
  154. package/dist/mcp-http.js.map +1 -1
  155. package/dist/mcp-io.d.ts +14 -0
  156. package/dist/mcp-io.d.ts.map +1 -1
  157. package/dist/mcp-io.js +45 -0
  158. package/dist/mcp-io.js.map +1 -1
  159. package/dist/mcp-isolate.d.ts +48 -0
  160. package/dist/mcp-isolate.d.ts.map +1 -0
  161. package/dist/mcp-isolate.js +222 -0
  162. package/dist/mcp-isolate.js.map +1 -0
  163. package/dist/mcp-project-child.d.ts +74 -0
  164. package/dist/mcp-project-child.d.ts.map +1 -0
  165. package/dist/mcp-project-child.js +130 -0
  166. package/dist/mcp-project-child.js.map +1 -0
  167. package/dist/mcp-render.js +2 -2
  168. package/dist/mcp-render.js.map +1 -1
  169. package/dist/mcp-root.d.ts +37 -9
  170. package/dist/mcp-root.d.ts.map +1 -1
  171. package/dist/mcp-root.js +110 -23
  172. package/dist/mcp-root.js.map +1 -1
  173. package/dist/mcp-run.d.ts +18 -1
  174. package/dist/mcp-run.d.ts.map +1 -1
  175. package/dist/mcp-run.js +82 -9
  176. package/dist/mcp-run.js.map +1 -1
  177. package/dist/mcp-ws.js +4 -1
  178. package/dist/mcp-ws.js.map +1 -1
  179. package/dist/mcp.d.ts +16 -3
  180. package/dist/mcp.d.ts.map +1 -1
  181. package/dist/mcp.js +81 -53
  182. package/dist/mcp.js.map +1 -1
  183. package/dist/migrate-layout.d.ts +44 -0
  184. package/dist/migrate-layout.d.ts.map +1 -0
  185. package/dist/migrate-layout.js +1155 -0
  186. package/dist/migrate-layout.js.map +1 -0
  187. package/dist/openapi-tool.d.ts +2 -2
  188. package/dist/openapi-tool.d.ts.map +1 -1
  189. package/dist/openapi-tool.js.map +1 -1
  190. package/dist/openapi-types.d.ts +2 -1
  191. package/dist/openapi-types.d.ts.map +1 -1
  192. package/dist/openapi-types.js +240 -12
  193. package/dist/openapi-types.js.map +1 -1
  194. package/dist/pipeline-report.d.ts +3 -2
  195. package/dist/pipeline-report.d.ts.map +1 -1
  196. package/dist/pipeline-report.js +19 -16
  197. package/dist/pipeline-report.js.map +1 -1
  198. package/dist/port.d.ts +5 -4
  199. package/dist/port.d.ts.map +1 -1
  200. package/dist/port.js +46 -29
  201. package/dist/port.js.map +1 -1
  202. package/dist/project-facts.d.ts +5 -0
  203. package/dist/project-facts.d.ts.map +1 -1
  204. package/dist/project-facts.js.map +1 -1
  205. package/dist/recipes/4.0.0.d.ts +9 -0
  206. package/dist/recipes/4.0.0.d.ts.map +1 -0
  207. package/dist/recipes/4.0.0.js +36 -0
  208. package/dist/recipes/4.0.0.js.map +1 -0
  209. package/dist/recipes/index.d.ts.map +1 -1
  210. package/dist/recipes/index.js +2 -0
  211. package/dist/recipes/index.js.map +1 -1
  212. package/dist/release-verification.d.ts +11 -1
  213. package/dist/release-verification.d.ts.map +1 -1
  214. package/dist/release-verification.js +47 -4
  215. package/dist/release-verification.js.map +1 -1
  216. package/dist/review.d.ts.map +1 -1
  217. package/dist/review.js +12 -8
  218. package/dist/review.js.map +1 -1
  219. package/dist/route-types.d.ts +28 -0
  220. package/dist/route-types.d.ts.map +1 -0
  221. package/dist/route-types.js +106 -0
  222. package/dist/route-types.js.map +1 -0
  223. package/dist/rules/codes.d.ts +9 -0
  224. package/dist/rules/codes.d.ts.map +1 -1
  225. package/dist/rules/codes.js +9 -0
  226. package/dist/rules/codes.js.map +1 -1
  227. package/dist/rules/data-guard.d.ts +12 -0
  228. package/dist/rules/data-guard.d.ts.map +1 -0
  229. package/dist/rules/data-guard.js +213 -0
  230. package/dist/rules/data-guard.js.map +1 -0
  231. package/dist/rules/index.d.ts +3 -0
  232. package/dist/rules/index.d.ts.map +1 -1
  233. package/dist/rules/index.js +17 -5
  234. package/dist/rules/index.js.map +1 -1
  235. package/dist/rules/islands.d.ts.map +1 -1
  236. package/dist/rules/islands.js +7 -4
  237. package/dist/rules/islands.js.map +1 -1
  238. package/dist/rules/legacy.d.ts.map +1 -1
  239. package/dist/rules/legacy.js +71 -38
  240. package/dist/rules/legacy.js.map +1 -1
  241. package/dist/rules/nano.d.ts.map +1 -1
  242. package/dist/rules/nano.js +15 -6
  243. package/dist/rules/nano.js.map +1 -1
  244. package/dist/rules/pages.d.ts +15 -0
  245. package/dist/rules/pages.d.ts.map +1 -0
  246. package/dist/rules/pages.js +88 -0
  247. package/dist/rules/pages.js.map +1 -0
  248. package/dist/rules/routes.d.ts +11 -0
  249. package/dist/rules/routes.d.ts.map +1 -1
  250. package/dist/rules/routes.js +180 -2
  251. package/dist/rules/routes.js.map +1 -1
  252. package/dist/rules/secrets.d.ts +10 -0
  253. package/dist/rules/secrets.d.ts.map +1 -0
  254. package/dist/rules/secrets.js +169 -0
  255. package/dist/rules/secrets.js.map +1 -0
  256. package/dist/rules/security.d.ts +7 -0
  257. package/dist/rules/security.d.ts.map +1 -1
  258. package/dist/rules/security.js +171 -65
  259. package/dist/rules/security.js.map +1 -1
  260. package/dist/rules/typescript.d.ts +16 -0
  261. package/dist/rules/typescript.d.ts.map +1 -0
  262. package/dist/rules/typescript.js +59 -0
  263. package/dist/rules/typescript.js.map +1 -0
  264. package/dist/rules/zones.d.ts +17 -0
  265. package/dist/rules/zones.d.ts.map +1 -0
  266. package/dist/rules/zones.js +174 -0
  267. package/dist/rules/zones.js.map +1 -0
  268. package/dist/scaffold.d.ts +25 -12
  269. package/dist/scaffold.d.ts.map +1 -1
  270. package/dist/scaffold.js +132 -87
  271. package/dist/scaffold.js.map +1 -1
  272. package/dist/sdk.d.ts +17 -3
  273. package/dist/sdk.d.ts.map +1 -1
  274. package/dist/sdk.js +941 -153
  275. package/dist/sdk.js.map +1 -1
  276. package/dist/search-terms.d.ts +2 -0
  277. package/dist/search-terms.d.ts.map +1 -1
  278. package/dist/search-terms.js +23 -8
  279. package/dist/search-terms.js.map +1 -1
  280. package/dist/smoke.d.ts +79 -0
  281. package/dist/smoke.d.ts.map +1 -0
  282. package/dist/smoke.js +494 -0
  283. package/dist/smoke.js.map +1 -0
  284. package/dist/sync-manifest.d.ts +10 -2
  285. package/dist/sync-manifest.d.ts.map +1 -1
  286. package/dist/sync-manifest.js +44 -19
  287. package/dist/sync-manifest.js.map +1 -1
  288. package/dist/sync-routes.d.ts +4 -3
  289. package/dist/sync-routes.d.ts.map +1 -1
  290. package/dist/sync-routes.js +4 -3
  291. package/dist/sync-routes.js.map +1 -1
  292. package/dist/test-tool.d.ts.map +1 -1
  293. package/dist/test-tool.js +30 -11
  294. package/dist/test-tool.js.map +1 -1
  295. package/dist/types-search.d.ts.map +1 -1
  296. package/dist/types-search.js +11 -5
  297. package/dist/types-search.js.map +1 -1
  298. package/dist/upgrade.d.ts +36 -6
  299. package/dist/upgrade.d.ts.map +1 -1
  300. package/dist/upgrade.js +167 -29
  301. package/dist/upgrade.js.map +1 -1
  302. package/dist/verification-plan.d.ts.map +1 -1
  303. package/dist/verification-plan.js +4 -0
  304. package/dist/verification-plan.js.map +1 -1
  305. package/dist/verification.d.ts.map +1 -1
  306. package/dist/verification.js +45 -12
  307. package/dist/verification.js.map +1 -1
  308. package/dist/web-app-options.d.ts +19 -0
  309. package/dist/web-app-options.d.ts.map +1 -0
  310. package/dist/web-app-options.js +49 -0
  311. package/dist/web-app-options.js.map +1 -0
  312. package/dist/work-graph.d.ts.map +1 -1
  313. package/dist/work-graph.js +29 -13
  314. package/dist/work-graph.js.map +1 -1
  315. package/docs/examples.json +185 -29
  316. package/docs/llms-full.txt +3807 -580
  317. package/docs/types.json +14229 -10939
  318. package/package.json +19 -12
  319. package/src/app-files.ts +40 -0
  320. package/src/assure-hydration.ts +139 -46
  321. package/src/assure.ts +17 -2
  322. package/src/capabilities-tool.ts +35 -9
  323. package/src/cdn-check.ts +268 -0
  324. package/src/check-diagnostics.ts +17 -0
  325. package/src/check-scan.ts +358 -63
  326. package/src/check.ts +97 -62
  327. package/src/cli.ts +296 -87
  328. package/src/command-catalog.ts +389 -48
  329. package/src/config-target.ts +47 -0
  330. package/src/contracts.ts +54 -12
  331. package/src/db-audit.ts +128 -0
  332. package/src/db-child.ts +285 -0
  333. package/src/db-config.ts +224 -0
  334. package/src/db-tool.ts +690 -0
  335. package/src/dev-bun-config.ts +116 -40
  336. package/src/dev-feed-tool.ts +912 -0
  337. package/src/dev-server-client.ts +267 -0
  338. package/src/diff-tool.ts +6 -5
  339. package/src/docs-search.ts +10 -3
  340. package/src/doctor.ts +98 -15
  341. package/src/env-file.ts +78 -1
  342. package/src/fix-recipes.ts +53 -32
  343. package/src/frontend-guidance.ts +5 -5
  344. package/src/i18n-check.ts +179 -0
  345. package/src/init-agents.ts +301 -38
  346. package/src/internal/code-unit-order.ts +5 -0
  347. package/src/internal/script-kind.ts +11 -0
  348. package/src/internal/source-facts.ts +96 -22
  349. package/src/internal/typescript-import.ts +663 -68
  350. package/src/introspect.ts +90 -27
  351. package/src/learn.ts +3 -3
  352. package/src/levels-tool.ts +47 -2
  353. package/src/load.ts +140 -33
  354. package/src/manifest-tool.ts +3 -2
  355. package/src/mcp-app-tool.ts +2 -0
  356. package/src/mcp-context.ts +1 -1
  357. package/src/mcp-delegate.ts +162 -0
  358. package/src/mcp-docs-tools.ts +23 -5
  359. package/src/mcp-exec.ts +160 -132
  360. package/src/mcp-http.ts +19 -5
  361. package/src/mcp-io.ts +57 -0
  362. package/src/mcp-isolate.ts +287 -0
  363. package/src/mcp-project-child.ts +182 -0
  364. package/src/mcp-render.ts +2 -2
  365. package/src/mcp-root.ts +117 -22
  366. package/src/mcp-run.ts +104 -10
  367. package/src/mcp-ws.ts +4 -1
  368. package/src/mcp.ts +106 -64
  369. package/src/migrate-layout.ts +1338 -0
  370. package/src/openapi-tool.ts +7 -3
  371. package/src/openapi-types.ts +315 -13
  372. package/src/pipeline-report.ts +22 -18
  373. package/src/port.ts +53 -30
  374. package/src/project-facts.ts +5 -0
  375. package/src/recipes/4.0.0.ts +37 -0
  376. package/src/recipes/index.ts +2 -0
  377. package/src/release-verification.ts +74 -5
  378. package/src/review.ts +13 -8
  379. package/src/route-types.ts +133 -0
  380. package/src/rules/codes.ts +9 -0
  381. package/src/rules/data-guard.ts +230 -0
  382. package/src/rules/index.ts +18 -5
  383. package/src/rules/islands.ts +12 -4
  384. package/src/rules/legacy.ts +71 -39
  385. package/src/rules/nano.ts +18 -6
  386. package/src/rules/pages.ts +99 -0
  387. package/src/rules/routes.ts +181 -2
  388. package/src/rules/secrets.ts +174 -0
  389. package/src/rules/security.ts +198 -70
  390. package/src/rules/typescript.ts +77 -0
  391. package/src/rules/zones.ts +195 -0
  392. package/src/scaffold.ts +147 -95
  393. package/src/sdk.ts +1215 -158
  394. package/src/search-terms.ts +24 -7
  395. package/src/smoke.ts +607 -0
  396. package/src/sync-manifest.ts +50 -19
  397. package/src/sync-routes.ts +4 -3
  398. package/src/test-tool.ts +32 -11
  399. package/src/types-search.ts +11 -4
  400. package/src/upgrade.ts +205 -25
  401. package/src/verification-plan.ts +13 -0
  402. package/src/verification.ts +50 -12
  403. package/src/web-app-options.ts +57 -0
  404. package/src/work-graph.ts +27 -13
@@ -108,6 +108,12 @@ const GATES = Object.freeze([
108
108
  "Run `bun run check:middleware-overhead` and investigate the middleware-chain regression.",
109
109
  { workflowRequired: false },
110
110
  ),
111
+ gate(
112
+ "output-guard-performance",
113
+ [["run", "check:output-guard"]],
114
+ "Run `bun run check:output-guard` and investigate the data guard's projection cost.",
115
+ { workflowRequired: false },
116
+ ),
111
117
  gate(
112
118
  "edge-startup",
113
119
  [["run", "check:edge-startup"]],
@@ -152,6 +158,11 @@ const GATES = Object.freeze([
152
158
  [["run", "check:pipeline-parity"]],
153
159
  "Run `bun run check:pipeline-parity` and fix the development and production manifest drift.",
154
160
  ),
161
+ gate(
162
+ "leak-matrix",
163
+ [["run", "check:leak-matrix"]],
164
+ "Run `bun run check:leak-matrix` and fix the pipeline that let server code or a credential through.",
165
+ ),
155
166
  gate(
156
167
  "cli-isolation",
157
168
  [["run", "check:cli-isolation"]],
@@ -200,6 +211,7 @@ const RELEASE_PLAN = Object.freeze([
200
211
  "size",
201
212
  "core-performance",
202
213
  "middleware-performance",
214
+ "output-guard-performance",
203
215
  "edge-startup",
204
216
  "publish",
205
217
  "consumer",
@@ -208,6 +220,7 @@ const RELEASE_PLAN = Object.freeze([
208
220
  "cross-runtime-node",
209
221
  "workerd",
210
222
  "pipeline-parity",
223
+ "leak-matrix",
211
224
  "verification-parity",
212
225
  "changesets",
213
226
  ] as const)
@@ -19,7 +19,12 @@ import {
19
19
  type AssuranceReport,
20
20
  evaluateRouteAssurance,
21
21
  } from "@nifrajs/core/assurance"
22
- import { type ProjectEvidenceSnapshot, snapshotProjectEvidence } from "@nifrajs/core/evidence"
22
+ import {
23
+ type ProjectEvidenceSnapshot,
24
+ reflectedRoutesFromEvidence,
25
+ snapshotProjectEvidence,
26
+ } from "@nifrajs/core/evidence"
27
+ import { type BackendEvidenceProvider, NIFRA_BACKEND_EVIDENCE } from "@nifrajs/core/mount"
23
28
  import {
24
29
  type CapabilityProjectReport,
25
30
  collectCapabilityProjectReport,
@@ -60,6 +65,13 @@ export interface ProjectVerification {
60
65
  check(): Promise<CheckResult>
61
66
  }
62
67
 
68
+ function evidenceProviderOf(source: unknown): BackendEvidenceProvider | undefined {
69
+ if ((typeof source !== "object" && typeof source !== "function") || source === null)
70
+ return undefined
71
+ const provider = (source as { [NIFRA_BACKEND_EVIDENCE]?: unknown })[NIFRA_BACKEND_EVIDENCE]
72
+ return typeof provider === "function" ? (provider as BackendEvidenceProvider) : undefined
73
+ }
74
+
63
75
  /**
64
76
  * Run each verification policy once and return the unified value. The config is loaded (and reflected)
65
77
  * a single time; the resulting route-assurance + capability evidence is both returned for the assurance
@@ -87,18 +99,44 @@ export async function collectProjectVerification(
87
99
  } else {
88
100
  config = await loadAssuranceConfig(cwd, options.config)
89
101
  }
90
- routeAssurance = evaluateRouteAssurance(config.source, config.policy, {
91
- ...(config.capabilities !== undefined
92
- ? { definitions: config.capabilities.definitions }
93
- : {}),
94
- })
95
- if (config.capabilities !== undefined) {
96
- capability = await collectCapabilityProjectReport(cwd, config.source, config.capabilities)
102
+ const composedProvider = evidenceProviderOf(config.source)
103
+ const composedEvidence =
104
+ composedProvider === undefined ? undefined : await composedProvider.call(config.source)
105
+ if (composedEvidence === undefined) {
106
+ routeAssurance = evaluateRouteAssurance(config.source, config.policy, {
107
+ ...(config.capabilities !== undefined
108
+ ? { definitions: config.capabilities.definitions }
109
+ : {}),
110
+ })
111
+ if (config.capabilities !== undefined) {
112
+ capability = await collectCapabilityProjectReport(cwd, config.source, config.capabilities)
113
+ }
114
+ evidence = snapshotProjectEvidence(config.source, {
115
+ ...(routeAssurance !== undefined ? { assurance: routeAssurance } : {}),
116
+ ...(capability !== undefined ? { capabilities: capability.report } : {}),
117
+ })
118
+ } else {
119
+ const reflectedRoutes = reflectedRoutesFromEvidence(composedEvidence)
120
+ routeAssurance = evaluateRouteAssurance(config.source, config.policy, {
121
+ ...(config.capabilities !== undefined
122
+ ? { definitions: config.capabilities.definitions }
123
+ : {}),
124
+ routes: reflectedRoutes,
125
+ })
126
+ const mounts = composedEvidence.mounts ?? []
127
+ if (config.capabilities !== undefined) {
128
+ capability = await collectCapabilityProjectReport(cwd, config.source, config.capabilities, {
129
+ routes: reflectedRoutes,
130
+ mounts,
131
+ })
132
+ }
133
+ evidence = snapshotProjectEvidence(config.source, {
134
+ routes: reflectedRoutes,
135
+ mounts,
136
+ ...(routeAssurance !== undefined ? { assurance: routeAssurance } : {}),
137
+ ...(capability !== undefined ? { capabilities: capability.report } : {}),
138
+ })
97
139
  }
98
- evidence = snapshotProjectEvidence(config.source, {
99
- ...(routeAssurance !== undefined ? { assurance: routeAssurance } : {}),
100
- ...(capability !== undefined ? { capabilities: capability.report } : {}),
101
- })
102
140
  } catch (error) {
103
141
  configError = error
104
142
  config = undefined
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The `createWebApp` options a CLI-run app takes from its framework config and `backend/app.ts`,
3
+ * built in one place. `nifra dev`, the static prerender, `nifra mcp`'s render tool and the
4
+ * hydration gate each construct the app themselves, and the generated server entry constructs it in
5
+ * production; when each call site picked its own subset, the app they served drifted from the one
6
+ * that ships.
7
+ */
8
+ import { inProcessClient } from "@nifrajs/client"
9
+ import type { CreateWebAppOptions } from "@nifrajs/web"
10
+ import { FRAMEWORK_WEB_OPTIONS, type FrameworkWebOption, type NifraFramework } from "./load.ts"
11
+
12
+ type FrameworkWebAppOptions = Pick<
13
+ CreateWebAppOptions,
14
+ "use" | "api" | "apiPrefix" | "apiStrip" | "mounts" | "csp" | "nonce"
15
+ >
16
+
17
+ /** `use`, the backend as `api`, and every forwarded framework field the config sets. */
18
+ export function frameworkWebAppOptions(
19
+ fw: NifraFramework,
20
+ backend: unknown,
21
+ ): FrameworkWebAppOptions {
22
+ const options: Record<string, unknown> = {}
23
+ if (fw.use !== undefined) options.use = fw.use
24
+ if (backend !== undefined) options.api = inProcessClient(backend as never)
25
+ for (const name of FRAMEWORK_WEB_OPTIONS) {
26
+ if (fw[name] !== undefined) options[name] = fw[name]
27
+ }
28
+ return options as FrameworkWebAppOptions
29
+ }
30
+
31
+ /**
32
+ * The mount paths the app will serve ahead of page routing, read from the config alone (no app is
33
+ * built): each `mounts` entry, plus the backend at `apiPrefix` when `backend/app.ts` exports one. A
34
+ * mount added inside `use` is not visible here; `createWebApp` checks its own mount table at
35
+ * startup.
36
+ */
37
+ export function frameworkMountPaths(fw: NifraFramework, hasBackend: boolean): string[] {
38
+ const paths: string[] = []
39
+ for (const mount of fw.mounts ?? []) {
40
+ if (typeof mount?.path === "string") paths.push(mount.path)
41
+ }
42
+ const apiPrefix = fw.apiPrefix ?? "/api"
43
+ if (hasBackend && apiPrefix !== "") paths.push(apiPrefix)
44
+ return paths
45
+ }
46
+
47
+ /** The generated server entry's imports for the forwarded fields the config sets, all from the
48
+ * edge-safe framework module. */
49
+ export function frameworkOptionImports(
50
+ fw: NifraFramework,
51
+ frameworkFile: string,
52
+ ): { optionImports?: Partial<Record<FrameworkWebOption, string>> } {
53
+ const imports: Partial<Record<FrameworkWebOption, string>> = {}
54
+ for (const name of FRAMEWORK_WEB_OPTIONS)
55
+ if (fw[name] !== undefined) imports[name] = frameworkFile
56
+ return Object.keys(imports).length === 0 ? {} : { optionImports: imports }
57
+ }
package/src/work-graph.ts CHANGED
@@ -16,7 +16,9 @@ import {
16
16
  } from "@nifrajs/core/evidence"
17
17
  import type { ReflectedRoute } from "@nifrajs/core/reflection"
18
18
  import { Glob } from "bun"
19
+ import { BACKEND_APP_FILE, CONFIG_FILE, FRAMEWORK_FILE } from "./app-files.ts"
19
20
  import { digestRoute } from "./contracts.ts"
21
+ import { codeUnitOrder } from "./internal/code-unit-order.ts"
20
22
 
21
23
  export type WorkGraphNodeKind =
22
24
  | "route"
@@ -133,8 +135,11 @@ export interface ProjectWorkGraphResult {
133
135
  readonly evidence: EvidenceBundle
134
136
  }
135
137
 
136
- const SOURCE_GLOB = new Glob("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json}")
138
+ // Framework component, content, and style files are app source too: an edit to one makes a build stale.
139
+ const SOURCE_GLOB = new Glob("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json,svelte,vue,mdx,css}")
137
140
  const IGNORED = /(^|\/)(node_modules|dist|build|\.nifra|\.git|\.wrangler|coverage)\//
141
+ const APP_SOURCE_DIR = /^(?:routes|frontend|backend|shared)\//
142
+ const BACKEND_SOURCE_DIR = /^(?:backend|shared)\//
138
143
  const MANIFEST_NAMES = new Set([
139
144
  "server-manifest.ts",
140
145
  "nifra-manifest.json",
@@ -176,13 +181,13 @@ export function evaluateBuildFreshness(input: {
176
181
 
177
182
  export async function inspectBuildFreshness(cwd: string): Promise<BuildFreshness> {
178
183
  const sourcePaths: string[] = []
179
- for (const name of ["backend.ts", "framework.ts", "nifra.config.ts"]) {
184
+ for (const name of [BACKEND_APP_FILE, FRAMEWORK_FILE, CONFIG_FILE]) {
180
185
  const path = resolve(cwd, name)
181
186
  if (existsSync(path)) sourcePaths.push(path)
182
187
  }
183
188
  for await (const rawPath of SOURCE_GLOB.scan({ cwd, dot: false })) {
184
189
  const path = rawPath.replaceAll("\\", "/")
185
- if (!IGNORED.test(path) && path.startsWith("routes/")) sourcePaths.push(resolve(cwd, path))
190
+ if (!IGNORED.test(path) && APP_SOURCE_DIR.test(path)) sourcePaths.push(resolve(cwd, path))
186
191
  }
187
192
  const sourceTimes = await fileTimes(sourcePaths)
188
193
  for (const buildDirName of ["dist", "build"]) {
@@ -215,8 +220,8 @@ export async function collectProjectWorkGraph(
215
220
  ): Promise<ProjectWorkGraphResult> {
216
221
  const freshness = await inspectBuildFreshness(cwd)
217
222
  if (!freshness.ok) throw new StaleBuildError(freshness)
218
- const backendPath = resolve(cwd, "backend.ts")
219
- if (!existsSync(backendPath)) throw new Error(`[nifra] no backend.ts in ${cwd}`)
223
+ const backendPath = resolve(cwd, BACKEND_APP_FILE)
224
+ if (!existsSync(backendPath)) throw new Error(`[nifra] no ${BACKEND_APP_FILE} in ${cwd}`)
220
225
  const loadedValue: unknown = await import(`${backendPath}?nifra-work-graph=${Date.now()}`)
221
226
  const loaded = recordOf(loadedValue)
222
227
  if (loaded?.backend === undefined)
@@ -290,8 +295,8 @@ export async function buildWorkGraph(input: WorkGraphBuildInput): Promise<WorkGr
290
295
  })
291
296
  for (const file of routeFiles)
292
297
  addEdge({ from: `file:${file}`, to: routeId, relation: "implements" })
293
- if (routeFiles.length === 0 && sourceFiles.some((file) => file.path === "backend.ts"))
294
- addEdge({ from: "file:backend.ts", to: routeId, relation: "implements" })
298
+ if (routeFiles.length === 0 && sourceFiles.some((file) => file.path === BACKEND_APP_FILE))
299
+ addEdge({ from: `file:${BACKEND_APP_FILE}`, to: routeId, relation: "implements" })
295
300
  for (const [name, schema] of Object.entries(route.schema ?? {})) {
296
301
  if (schema === undefined || name === "errors") continue
297
302
  const schemaId = `schema:${routeKey}:${name}`
@@ -334,9 +339,9 @@ export async function buildWorkGraph(input: WorkGraphBuildInput): Promise<WorkGr
334
339
  for (const file of manifestFiles)
335
340
  addEdge({ from: `manifest:${file.path}`, to: routeId, relation: "describes" })
336
341
  }
337
- const sortedNodes = [...nodes.values()].sort((a, b) => a.id.localeCompare(b.id))
342
+ const sortedNodes = [...nodes.values()].sort((a, b) => codeUnitOrder(a.id, b.id))
338
343
  const sortedEdges = edges.sort((a, b) =>
339
- `${a.from}\n${a.to}\n${a.relation}`.localeCompare(`${b.from}\n${b.to}\n${b.relation}`),
344
+ codeUnitOrder(`${a.from}\n${a.to}\n${a.relation}`, `${b.from}\n${b.to}\n${b.relation}`),
340
345
  )
341
346
  const digest = await digestJson({ nodes: sortedNodes, edges: sortedEdges })
342
347
  return Object.freeze({
@@ -353,7 +358,9 @@ export function queryImpact(graph: WorkGraph, changedFiles: readonly string[]):
353
358
  ...new Set(changedFiles.map((file) => file.replace(/^\.\//, "").replaceAll("\\", "/"))),
354
359
  ]
355
360
  const seeds = new Set<string>()
361
+ const unmodeled: string[] = []
356
362
  for (const file of normalized) {
363
+ let seeded = false
357
364
  for (const node of graph.nodes) {
358
365
  if (
359
366
  (node.kind === "file" || node.kind === "test" || node.kind === "manifest") &&
@@ -361,11 +368,18 @@ export function queryImpact(graph: WorkGraph, changedFiles: readonly string[]):
361
368
  node.id === `test:${file}` ||
362
369
  node.id === `manifest:${file}` ||
363
370
  node.files.includes(file))
364
- )
371
+ ) {
365
372
  seeds.add(node.id)
373
+ seeded = true
374
+ }
366
375
  }
376
+ // An app file the graph does not model (deleted, or a type it does not read) has an unknown
377
+ // reach, so it stands for itself and impacts every route rather than proving nothing.
378
+ if (!seeded && APP_SOURCE_DIR.test(file)) unmodeled.push(file)
367
379
  }
368
- if (normalized.includes("backend.ts"))
380
+ for (const file of unmodeled) seeds.add(`file:${file}`)
381
+ // Any backend or shared module can reach every handler through backend/app.ts.
382
+ if (unmodeled.length > 0 || normalized.some((file) => BACKEND_SOURCE_DIR.test(file)))
369
383
  for (const node of graph.nodes) if (node.kind === "route") seeds.add(node.id)
370
384
  const impacted = new Set(seeds)
371
385
  const queue = [...seeds]
@@ -444,7 +458,7 @@ export function createEvidenceBundle(
444
458
  plan: ProofPlan,
445
459
  proofs: readonly ProofEvidence[] = [],
446
460
  ): EvidenceBundle {
447
- const evidence = [...proofs].sort((a, b) => a.id.localeCompare(b.id))
461
+ const evidence = [...proofs].sort((a, b) => codeUnitOrder(a.id, b.id))
448
462
  // Levels are not cumulative (assure does not typecheck), so every planned step needs its own
449
463
  // passing proof; a higher-level pass never subsumes a lower one.
450
464
  const done =
@@ -581,7 +595,7 @@ function filesForRoute(
581
595
  const matches = files
582
596
  .filter(
583
597
  (file) =>
584
- file.path === "backend.ts" ||
598
+ file.path === BACKEND_APP_FILE ||
585
599
  (file.path.startsWith("routes/") && file.content.includes(route.path)),
586
600
  )
587
601
  .map((file) => file.path)