@nifrajs/cli 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 (403) 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 +57 -5
  37. package/dist/command-catalog.d.ts.map +1 -1
  38. package/dist/command-catalog.js +270 -26
  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 +41 -21
  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 +982 -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 +34 -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.map +1 -1
  285. package/dist/sync-manifest.js +2 -1
  286. package/dist/sync-manifest.js.map +1 -1
  287. package/dist/sync-routes.d.ts +4 -3
  288. package/dist/sync-routes.d.ts.map +1 -1
  289. package/dist/sync-routes.js +4 -3
  290. package/dist/sync-routes.js.map +1 -1
  291. package/dist/test-tool.d.ts.map +1 -1
  292. package/dist/test-tool.js +30 -11
  293. package/dist/test-tool.js.map +1 -1
  294. package/dist/types-search.d.ts.map +1 -1
  295. package/dist/types-search.js +11 -5
  296. package/dist/types-search.js.map +1 -1
  297. package/dist/upgrade.d.ts +36 -6
  298. package/dist/upgrade.d.ts.map +1 -1
  299. package/dist/upgrade.js +167 -29
  300. package/dist/upgrade.js.map +1 -1
  301. package/dist/verification-plan.d.ts.map +1 -1
  302. package/dist/verification-plan.js +4 -0
  303. package/dist/verification-plan.js.map +1 -1
  304. package/dist/verification.d.ts.map +1 -1
  305. package/dist/verification.js +45 -12
  306. package/dist/verification.js.map +1 -1
  307. package/dist/web-app-options.d.ts +19 -0
  308. package/dist/web-app-options.d.ts.map +1 -0
  309. package/dist/web-app-options.js +49 -0
  310. package/dist/web-app-options.js.map +1 -0
  311. package/dist/work-graph.d.ts.map +1 -1
  312. package/dist/work-graph.js +29 -13
  313. package/dist/work-graph.js.map +1 -1
  314. package/docs/examples.json +185 -29
  315. package/docs/llms-full.txt +3807 -580
  316. package/docs/types.json +14229 -10939
  317. package/package.json +19 -12
  318. package/src/app-files.ts +40 -0
  319. package/src/assure-hydration.ts +139 -46
  320. package/src/assure.ts +17 -2
  321. package/src/capabilities-tool.ts +35 -9
  322. package/src/cdn-check.ts +268 -0
  323. package/src/check-diagnostics.ts +17 -0
  324. package/src/check-scan.ts +358 -63
  325. package/src/check.ts +97 -62
  326. package/src/cli.ts +296 -87
  327. package/src/command-catalog.ts +374 -38
  328. package/src/config-target.ts +47 -0
  329. package/src/contracts.ts +54 -12
  330. package/src/db-audit.ts +128 -0
  331. package/src/db-child.ts +285 -0
  332. package/src/db-config.ts +224 -0
  333. package/src/db-tool.ts +690 -0
  334. package/src/dev-bun-config.ts +116 -40
  335. package/src/dev-feed-tool.ts +912 -0
  336. package/src/dev-server-client.ts +267 -0
  337. package/src/diff-tool.ts +6 -5
  338. package/src/docs-search.ts +10 -3
  339. package/src/doctor.ts +98 -15
  340. package/src/env-file.ts +78 -1
  341. package/src/fix-recipes.ts +44 -31
  342. package/src/frontend-guidance.ts +5 -5
  343. package/src/i18n-check.ts +179 -0
  344. package/src/init-agents.ts +301 -38
  345. package/src/internal/code-unit-order.ts +5 -0
  346. package/src/internal/script-kind.ts +11 -0
  347. package/src/internal/source-facts.ts +96 -22
  348. package/src/internal/typescript-import.ts +663 -68
  349. package/src/introspect.ts +90 -27
  350. package/src/learn.ts +3 -3
  351. package/src/levels-tool.ts +47 -2
  352. package/src/load.ts +140 -33
  353. package/src/manifest-tool.ts +3 -2
  354. package/src/mcp-app-tool.ts +2 -0
  355. package/src/mcp-context.ts +1 -1
  356. package/src/mcp-delegate.ts +162 -0
  357. package/src/mcp-docs-tools.ts +23 -5
  358. package/src/mcp-exec.ts +160 -132
  359. package/src/mcp-http.ts +19 -5
  360. package/src/mcp-io.ts +57 -0
  361. package/src/mcp-isolate.ts +287 -0
  362. package/src/mcp-project-child.ts +182 -0
  363. package/src/mcp-render.ts +2 -2
  364. package/src/mcp-root.ts +117 -22
  365. package/src/mcp-run.ts +104 -10
  366. package/src/mcp-ws.ts +4 -1
  367. package/src/mcp.ts +106 -64
  368. package/src/migrate-layout.ts +1121 -0
  369. package/src/openapi-tool.ts +7 -3
  370. package/src/openapi-types.ts +315 -13
  371. package/src/pipeline-report.ts +22 -18
  372. package/src/port.ts +53 -30
  373. package/src/project-facts.ts +5 -0
  374. package/src/recipes/4.0.0.ts +35 -0
  375. package/src/recipes/index.ts +2 -0
  376. package/src/release-verification.ts +74 -5
  377. package/src/review.ts +13 -8
  378. package/src/route-types.ts +133 -0
  379. package/src/rules/codes.ts +9 -0
  380. package/src/rules/data-guard.ts +230 -0
  381. package/src/rules/index.ts +18 -5
  382. package/src/rules/islands.ts +12 -4
  383. package/src/rules/legacy.ts +71 -39
  384. package/src/rules/nano.ts +18 -6
  385. package/src/rules/pages.ts +99 -0
  386. package/src/rules/routes.ts +181 -2
  387. package/src/rules/secrets.ts +174 -0
  388. package/src/rules/security.ts +198 -70
  389. package/src/rules/typescript.ts +77 -0
  390. package/src/rules/zones.ts +195 -0
  391. package/src/scaffold.ts +147 -95
  392. package/src/sdk.ts +1215 -158
  393. package/src/search-terms.ts +24 -7
  394. package/src/smoke.ts +607 -0
  395. package/src/sync-manifest.ts +2 -1
  396. package/src/sync-routes.ts +4 -3
  397. package/src/test-tool.ts +32 -11
  398. package/src/types-search.ts +11 -4
  399. package/src/upgrade.ts +205 -25
  400. package/src/verification-plan.ts +13 -0
  401. package/src/verification.ts +50 -12
  402. package/src/web-app-options.ts +57 -0
  403. package/src/work-graph.ts +27 -13
package/src/check.ts CHANGED
@@ -17,7 +17,8 @@
17
17
  */
18
18
 
19
19
  import { existsSync, readFileSync } from "node:fs"
20
- import { dirname, isAbsolute, join } from "node:path"
20
+ import { dirname, isAbsolute, join, relative } from "node:path"
21
+ import { BACKEND_APP_FILE } from "./app-files.ts"
21
22
  import {
22
23
  type CheckAssuranceContext,
23
24
  type CheckConfig,
@@ -29,7 +30,7 @@ import {
29
30
  import { checkContractsLock, DEFAULT_CONTRACTS_LOCK } from "./contracts.ts"
30
31
  import { type Diagnostic, normalizeSeverity, toSarifLog } from "./diagnostics.ts"
31
32
  import { createSourceFacts } from "./internal/source-facts.ts"
32
- import { importProjectTypeScript, type TypeScriptApi } from "./internal/typescript-import.ts"
33
+ import { loadProjectTypeScript, type TypeScriptApi } from "./internal/typescript-import.ts"
33
34
  // Type-only: `pipeline-report.ts` imports this module's source scanners, so a value import here would
34
35
  // close a cycle. Doctor is what actually runs the collector (see the `pipeline` rule below).
35
36
  import type { ContractCheckFacts, ProjectFactsSeed } from "./project-facts.ts"
@@ -50,6 +51,7 @@ export type {
50
51
 
51
52
  import {
52
53
  createProjectSqlImports,
54
+ isBrowserSource,
53
55
  type ModuleReader,
54
56
  type ModuleResolver,
55
57
  resolveServerOnlyChains,
@@ -69,7 +71,6 @@ import {
69
71
 
70
72
  export * from "./check-scan.ts"
71
73
 
72
- const ROUTE_FILE = /(^|\/)routes\//
73
74
  interface TypecheckResult {
74
75
  readonly ran: boolean
75
76
  readonly ok: boolean
@@ -104,6 +105,9 @@ async function typecheck(cwd: string, signal?: AbortSignal): Promise<TypecheckRe
104
105
  const tsconfig = join(cwd, "tsconfig.json")
105
106
  if (!(await Bun.file(tsconfig).exists()))
106
107
  return { ran: false, ok: true, note: "no tsconfig.json" }
108
+ // Routes import their generated `./+types` modules; bring them up to date before tsc reads them.
109
+ const { refreshRouteTypes } = await import("./route-types.ts")
110
+ refreshRouteTypes(cwd, () => {})
107
111
  const tscBin = resolveTscBin(cwd)
108
112
  if (tscBin === undefined) {
109
113
  return {
@@ -114,7 +118,7 @@ async function typecheck(cwd: string, signal?: AbortSignal): Promise<TypecheckRe
114
118
  }
115
119
  }
116
120
  if (signal?.aborted) return { ran: true, ok: false, cancelled: true, output: "cancelled" }
117
- const proc = Bun.spawn(["bun", tscBin, "--noEmit", "-p", tsconfig], {
121
+ const proc = Bun.spawn([process.execPath, tscBin, "--noEmit", "-p", tsconfig], {
118
122
  cwd,
119
123
  stdout: "pipe",
120
124
  stderr: "pipe",
@@ -236,7 +240,7 @@ interface ProjectScan {
236
240
  }
237
241
 
238
242
  async function collectContractFacts(cwd: string): Promise<ContractCheckFacts> {
239
- const hasBackend = existsSync(join(cwd, "backend.ts"))
243
+ const hasBackend = existsSync(join(cwd, BACKEND_APP_FILE))
240
244
  const hasLock = existsSync(join(cwd, DEFAULT_CONTRACTS_LOCK))
241
245
  if (!hasBackend && !hasLock) return { present: false, vacuous: false, diagnostics: [] }
242
246
  try {
@@ -272,17 +276,13 @@ async function buildProjectScan(
272
276
  const serverImports: TransitiveServerImportFinding[] = []
273
277
  const responseRoutes: SourceFinding[] = []
274
278
  const interpolatedSql: SourceFinding[] = []
275
- const routeModules: Array<{ rel: string; content: string }> = []
279
+ const browserModules: Array<{ rel: string; content: string }> = []
276
280
  const sourceFiles: Array<{ file: string; content: string }> = []
277
281
  const {
278
282
  config: checkConfig,
279
283
  error: checkConfigError,
280
284
  warnings: checkConfigWarnings,
281
285
  } = await loadCheckConfig(cwd)
282
- const sqlCompiler = await (opts.loadTypeScript ?? (() => importProjectTypeScript(cwd)))()
283
- const sourceFacts = sqlCompiler === undefined ? undefined : createSourceFacts(sqlCompiler)
284
- const sqlImports = sqlCompiler === undefined ? undefined : createProjectSqlImports(cwd)
285
-
286
286
  const [typecheckResult, _, doctor, manifestDrift, contracts] = await Promise.all([
287
287
  opts.lintsOnly
288
288
  ? Promise.resolve<TypecheckResult>({ ran: false, ok: true, note: "lints-only mode" })
@@ -291,66 +291,89 @@ async function buildProjectScan(
291
291
  sourceFiles.push({ file: rel, content })
292
292
  fetches.push(...scanFetchText(rel, content, checkConfig.externalMounts))
293
293
  streams.push(...scanStreamText(rel, content, checkConfig.externalMounts))
294
- staticRoutes.push(...scanStaticRouteText(rel, content, sourceFacts))
295
294
  untypedClients.push(...scanUntypedClient(rel, content))
296
295
  removedImports.push(...scanRemovedImports(rel, content))
297
- if (ROUTE_FILE.test(rel)) routeModules.push({ rel, content })
298
- responseRoutes.push(...scanResponseRoutes(rel, content, sourceFacts))
299
- if (sqlCompiler !== undefined)
300
- interpolatedSql.push(...scanInterpolatedSql(rel, content, sqlCompiler, sqlImports))
296
+ if (isBrowserSource(rel)) browserModules.push({ rel, content })
301
297
  }),
302
298
  import("./doctor.ts").then((m) => m.collectDoctorResult(cwd)),
303
299
  scanServerManifestDrift(cwd),
304
300
  collectContractFacts(cwd),
305
301
  ])
306
302
 
307
- const resolveModule: ModuleResolver = (fromFile, specifier) => {
308
- try {
309
- const fromAbs = isAbsolute(fromFile) ? fromFile : join(cwd, fromFile)
310
- return Bun.resolveSync(specifier, dirname(fromAbs))
311
- } catch {
312
- return undefined
303
+ // TypeScript 7's unstable API materializes source files through one project session. Build the
304
+ // source index first so every syntax-backed scanner (including the independent rule registry)
305
+ // shares that session instead of spawning one compiler process per rule.
306
+ const source = sourceIndex(sourceFiles)
307
+ const loadedTypeScript = await loadProjectTypeScript(cwd, opts.loadTypeScript, source)
308
+ try {
309
+ const sqlCompiler = loadedTypeScript.compiler
310
+ const sourceFacts = sqlCompiler === undefined ? undefined : createSourceFacts(sqlCompiler)
311
+ const sqlImports = sqlCompiler === undefined ? undefined : createProjectSqlImports(cwd)
312
+ for (const { file, content } of sourceFiles) {
313
+ staticRoutes.push(...scanStaticRouteText(file, content, sourceFacts))
314
+ responseRoutes.push(...scanResponseRoutes(file, content, sourceFacts))
315
+ if (sqlCompiler !== undefined)
316
+ interpolatedSql.push(...scanInterpolatedSql(file, content, sqlCompiler, sqlImports))
313
317
  }
314
- }
315
- const readModule: ModuleReader = (absPath) => {
316
- try {
317
- return readFileSync(absPath, "utf8")
318
- } catch {
319
- return undefined
318
+
319
+ // Resolved modules stay project-relative, as the scanned files are: the zone of a path is read
320
+ // from its folders, and a checkout under a folder named `backend/` must not make it all backend.
321
+ const resolveModule: ModuleResolver = (fromFile, specifier) => {
322
+ try {
323
+ const fromAbs = isAbsolute(fromFile) ? fromFile : join(cwd, fromFile)
324
+ const resolved = Bun.resolveSync(specifier, dirname(fromAbs))
325
+ const rel = relative(cwd, resolved).replaceAll("\\", "/")
326
+ return rel.startsWith("../") || isAbsolute(rel) ? resolved : rel
327
+ } catch {
328
+ return undefined
329
+ }
330
+ }
331
+ const readModule: ModuleReader = (path) => {
332
+ try {
333
+ return readFileSync(isAbsolute(path) ? path : join(cwd, path), "utf8")
334
+ } catch {
335
+ return undefined
336
+ }
337
+ }
338
+ for (const { rel, content } of browserModules) {
339
+ serverImports.push(
340
+ ...resolveServerOnlyChains(rel, content, resolveModule, readModule, sourceFacts),
341
+ )
320
342
  }
321
- }
322
- for (const { rel, content } of routeModules) {
323
- serverImports.push(
324
- ...resolveServerOnlyChains(rel, content, resolveModule, readModule, sourceFacts),
325
- )
326
- }
327
343
 
328
- const source = sourceIndex(sourceFiles)
329
- const facts: ProjectFactsSeed = {
330
- source,
331
- routes: staticRoutes,
332
- importGraph: serverImports,
333
- packages: { doctor, manifestDrift },
334
- ...(doctor.pipeline === undefined ? {} : { pipeline: doctor.pipeline }),
335
- policies: { checkConfig, rulePacks: [] },
336
- check: {
337
- typecheck: typecheckResult,
338
- sqlCompilerAvailable: sqlCompiler !== undefined,
339
- ...(checkConfigError === undefined ? {} : { checkConfigError }),
340
- checkConfigWarnings,
341
- contracts,
342
- },
343
- sourceFindings: {
344
- fetches,
345
- streams,
346
- untypedClients,
347
- removedImports,
348
- responseRoutes,
349
- interpolatedSql,
350
- },
351
- }
352
- return {
353
- facts,
344
+ const facts: ProjectFactsSeed = {
345
+ source,
346
+ routes: staticRoutes,
347
+ importGraph: serverImports,
348
+ packages: { doctor, manifestDrift },
349
+ ...(doctor.pipeline === undefined ? {} : { pipeline: doctor.pipeline }),
350
+ policies: { checkConfig, rulePacks: [] },
351
+ check: {
352
+ typecheck: typecheckResult,
353
+ sqlCompilerAvailable: sqlCompiler !== undefined,
354
+ ...(loadedTypeScript.unsupported === undefined
355
+ ? {}
356
+ : { unsupportedTypeScriptVersion: loadedTypeScript.unsupported.version }),
357
+ ...(loadedTypeScript.session === undefined
358
+ ? {}
359
+ : { typescriptSession: loadedTypeScript.session }),
360
+ ...(checkConfigError === undefined ? {} : { checkConfigError }),
361
+ checkConfigWarnings,
362
+ contracts,
363
+ },
364
+ sourceFindings: {
365
+ fetches,
366
+ streams,
367
+ untypedClients,
368
+ removedImports,
369
+ responseRoutes,
370
+ interpolatedSql,
371
+ },
372
+ }
373
+ return { facts }
374
+ } catch (error) {
375
+ await loadedTypeScript.session?.close()
376
+ throw error
354
377
  }
355
378
  }
356
379
 
@@ -362,7 +385,11 @@ export async function collectCheckResult(
362
385
  opts: CheckCollectionOptions = {},
363
386
  ): Promise<CheckResult> {
364
387
  const scan = await buildProjectScan(cwd, opts)
365
- return collectCheckDiagnostics(cwd, scan, opts)
388
+ try {
389
+ return await collectCheckDiagnostics(cwd, scan, opts)
390
+ } finally {
391
+ await scan.facts.check.typescriptSession?.close()
392
+ }
366
393
  }
367
394
 
368
395
  /** The named rule sections of the human report, in print order. A rule absent from this list is NOT
@@ -372,7 +399,7 @@ const REPORT_SECTIONS = [
372
399
  ["typecheck", "typecheck"],
373
400
  ["typed-client", "hand-rolled fetch()/EventSource/WebSocket to your own API"],
374
401
  ["untyped-client", 'client("…") missing its <typeof app> type argument'],
375
- ["server-only-import", "server-only import in a route module"],
402
+ ["server-only-import", "backend code in browser code"],
376
403
  ["interpolated-sql", "SQL built by interpolating a value into the statement"],
377
404
  ["response-route", "route returns a raw Response (typed client → data: never)"],
378
405
  ["undeclared-dependency", "undeclared dependency in package.json"],
@@ -416,6 +443,14 @@ export function renderCheckReport(result: CheckResult): string[] {
416
443
  `• intentional external mounts (not typed-client checked): ${result.externalMounts.join(", ")}`,
417
444
  )
418
445
  }
446
+ for (const gap of result.knownGaps ?? []) {
447
+ lines.push(`• known gap, not capability-analyzed: ${gap.path} - ${gap.reason}`)
448
+ }
449
+ const deduplicated = result.identityPreflight?.deduplicated ?? []
450
+ if (deduplicated.length > 0) {
451
+ const packages = deduplicated.map((finding) => finding.package).join(", ")
452
+ lines.push(`• identity-sensitive duplicate installs handled by singleCopy: ${packages}`)
453
+ }
419
454
  if (result.ruleOverrides !== undefined) {
420
455
  const active = Object.entries(result.ruleOverrides).map(([rule, override]) => {
421
456
  const parts: string[] = []
@@ -542,7 +577,7 @@ export async function runCheck(
542
577
 
543
578
  console.log(renderCheckReport(result).join("\n"))
544
579
  // Discoverability nudge: a project with no `.mcp.json` hasn't wired its nifra MCP for coding agents.
545
- // `nifra init-agents` writes it (+ .cursor/mcp.json + a CLAUDE.md preamble), no-clobber. A non-fatal
580
+ // `nifra init-agents` writes it (+ .cursor/mcp.json + each agent's pointer to AGENTS.md), no-clobber. A non-fatal
546
581
  // one-line tip in the human report only (the `--json` path returns above, unaffected).
547
582
  if (!existsSync(join(cwd, ".mcp.json"))) {
548
583
  console.log(