@nifrajs/cli 3.3.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 (425) 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 +7 -15
  22. package/dist/check-diagnostics.d.ts.map +1 -1
  23. package/dist/check-diagnostics.js +162 -713
  24. package/dist/check-diagnostics.js.map +1 -1
  25. package/dist/check-scan.d.ts +58 -7
  26. package/dist/check-scan.d.ts.map +1 -1
  27. package/dist/check-scan.js +323 -62
  28. package/dist/check-scan.js.map +1 -1
  29. package/dist/check.d.ts +1 -0
  30. package/dist/check.d.ts.map +1 -1
  31. package/dist/check.js +145 -54
  32. package/dist/check.js.map +1 -1
  33. package/dist/cli.d.ts +42 -16
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +306 -87
  36. package/dist/cli.js.map +1 -1
  37. package/dist/command-catalog.d.ts +60 -5
  38. package/dist/command-catalog.d.ts.map +1 -1
  39. package/dist/command-catalog.js +298 -33
  40. package/dist/command-catalog.js.map +1 -1
  41. package/dist/config-target.d.ts +8 -0
  42. package/dist/config-target.d.ts.map +1 -0
  43. package/dist/config-target.js +45 -0
  44. package/dist/config-target.js.map +1 -0
  45. package/dist/contracts.d.ts +3 -2
  46. package/dist/contracts.d.ts.map +1 -1
  47. package/dist/contracts.js +56 -16
  48. package/dist/contracts.js.map +1 -1
  49. package/dist/db-audit.d.ts +37 -0
  50. package/dist/db-audit.d.ts.map +1 -0
  51. package/dist/db-audit.js +99 -0
  52. package/dist/db-audit.js.map +1 -0
  53. package/dist/db-child.d.ts +64 -0
  54. package/dist/db-child.d.ts.map +1 -0
  55. package/dist/db-child.js +213 -0
  56. package/dist/db-child.js.map +1 -0
  57. package/dist/db-config.d.ts +60 -0
  58. package/dist/db-config.d.ts.map +1 -0
  59. package/dist/db-config.js +176 -0
  60. package/dist/db-config.js.map +1 -0
  61. package/dist/db-tool.d.ts +84 -0
  62. package/dist/db-tool.d.ts.map +1 -0
  63. package/dist/db-tool.js +537 -0
  64. package/dist/db-tool.js.map +1 -0
  65. package/dist/dev-bun-config.d.ts +20 -13
  66. package/dist/dev-bun-config.d.ts.map +1 -1
  67. package/dist/dev-bun-config.js +98 -41
  68. package/dist/dev-bun-config.js.map +1 -1
  69. package/dist/dev-feed-tool.d.ts +106 -0
  70. package/dist/dev-feed-tool.d.ts.map +1 -0
  71. package/dist/dev-feed-tool.js +713 -0
  72. package/dist/dev-feed-tool.js.map +1 -0
  73. package/dist/dev-server-client.d.ts +67 -0
  74. package/dist/dev-server-client.d.ts.map +1 -0
  75. package/dist/dev-server-client.js +195 -0
  76. package/dist/dev-server-client.js.map +1 -0
  77. package/dist/diagnostics.d.ts +86 -0
  78. package/dist/diagnostics.d.ts.map +1 -1
  79. package/dist/diagnostics.js +85 -0
  80. package/dist/diagnostics.js.map +1 -1
  81. package/dist/diff-tool.d.ts +3 -3
  82. package/dist/diff-tool.d.ts.map +1 -1
  83. package/dist/diff-tool.js +6 -5
  84. package/dist/diff-tool.js.map +1 -1
  85. package/dist/docs-search.d.ts.map +1 -1
  86. package/dist/docs-search.js +12 -4
  87. package/dist/docs-search.js.map +1 -1
  88. package/dist/doctor.d.ts +17 -0
  89. package/dist/doctor.d.ts.map +1 -1
  90. package/dist/doctor.js +76 -15
  91. package/dist/doctor.js.map +1 -1
  92. package/dist/env-file.d.ts +15 -0
  93. package/dist/env-file.d.ts.map +1 -1
  94. package/dist/env-file.js +73 -1
  95. package/dist/env-file.js.map +1 -1
  96. package/dist/fix-recipes.d.ts.map +1 -1
  97. package/dist/fix-recipes.js +41 -21
  98. package/dist/fix-recipes.js.map +1 -1
  99. package/dist/frontend-guidance.js +5 -5
  100. package/dist/frontend-guidance.js.map +1 -1
  101. package/dist/i18n-check.d.ts +16 -0
  102. package/dist/i18n-check.d.ts.map +1 -0
  103. package/dist/i18n-check.js +154 -0
  104. package/dist/i18n-check.js.map +1 -0
  105. package/dist/init-agents.d.ts +49 -13
  106. package/dist/init-agents.d.ts.map +1 -1
  107. package/dist/init-agents.js +239 -34
  108. package/dist/init-agents.js.map +1 -1
  109. package/dist/internal/code-unit-order.d.ts +4 -0
  110. package/dist/internal/code-unit-order.d.ts.map +1 -0
  111. package/dist/internal/code-unit-order.js +6 -0
  112. package/dist/internal/code-unit-order.js.map +1 -0
  113. package/dist/internal/script-kind.d.ts +7 -0
  114. package/dist/internal/script-kind.d.ts.map +1 -0
  115. package/dist/internal/script-kind.js +12 -0
  116. package/dist/internal/script-kind.js.map +1 -0
  117. package/dist/internal/source-facts.d.ts +5 -0
  118. package/dist/internal/source-facts.d.ts.map +1 -1
  119. package/dist/internal/source-facts.js +61 -5
  120. package/dist/internal/source-facts.js.map +1 -1
  121. package/dist/internal/typescript-import.d.ts +49 -24
  122. package/dist/internal/typescript-import.d.ts.map +1 -1
  123. package/dist/internal/typescript-import.js +507 -67
  124. package/dist/internal/typescript-import.js.map +1 -1
  125. package/dist/introspect.d.ts +6 -4
  126. package/dist/introspect.d.ts.map +1 -1
  127. package/dist/introspect.js +93 -74
  128. package/dist/introspect.js.map +1 -1
  129. package/dist/learn.js +3 -3
  130. package/dist/learn.js.map +1 -1
  131. package/dist/levels-tool.d.ts +10 -0
  132. package/dist/levels-tool.d.ts.map +1 -1
  133. package/dist/levels-tool.js +33 -3
  134. package/dist/levels-tool.js.map +1 -1
  135. package/dist/load.d.ts +71 -16
  136. package/dist/load.d.ts.map +1 -1
  137. package/dist/load.js +78 -18
  138. package/dist/load.js.map +1 -1
  139. package/dist/manifest-tool.d.ts.map +1 -1
  140. package/dist/manifest-tool.js +3 -2
  141. package/dist/manifest-tool.js.map +1 -1
  142. package/dist/mcp-app-tool.d.ts.map +1 -1
  143. package/dist/mcp-app-tool.js +2 -0
  144. package/dist/mcp-app-tool.js.map +1 -1
  145. package/dist/mcp-context.d.ts.map +1 -1
  146. package/dist/mcp-context.js +5 -6
  147. package/dist/mcp-context.js.map +1 -1
  148. package/dist/mcp-delegate.d.ts +57 -0
  149. package/dist/mcp-delegate.d.ts.map +1 -0
  150. package/dist/mcp-delegate.js +130 -0
  151. package/dist/mcp-delegate.js.map +1 -0
  152. package/dist/mcp-docs-tools.d.ts.map +1 -1
  153. package/dist/mcp-docs-tools.js +23 -5
  154. package/dist/mcp-docs-tools.js.map +1 -1
  155. package/dist/mcp-exec.d.ts +3 -0
  156. package/dist/mcp-exec.d.ts.map +1 -1
  157. package/dist/mcp-exec.js +263 -131
  158. package/dist/mcp-exec.js.map +1 -1
  159. package/dist/mcp-http.d.ts.map +1 -1
  160. package/dist/mcp-http.js +20 -5
  161. package/dist/mcp-http.js.map +1 -1
  162. package/dist/mcp-io.d.ts +26 -0
  163. package/dist/mcp-io.d.ts.map +1 -1
  164. package/dist/mcp-io.js +113 -0
  165. package/dist/mcp-io.js.map +1 -1
  166. package/dist/mcp-isolate.d.ts +48 -0
  167. package/dist/mcp-isolate.d.ts.map +1 -0
  168. package/dist/mcp-isolate.js +222 -0
  169. package/dist/mcp-isolate.js.map +1 -0
  170. package/dist/mcp-path.d.ts +10 -0
  171. package/dist/mcp-path.d.ts.map +1 -0
  172. package/dist/mcp-path.js +51 -0
  173. package/dist/mcp-path.js.map +1 -0
  174. package/dist/mcp-project-child.d.ts +74 -0
  175. package/dist/mcp-project-child.d.ts.map +1 -0
  176. package/dist/mcp-project-child.js +130 -0
  177. package/dist/mcp-project-child.js.map +1 -0
  178. package/dist/mcp-reflect.d.ts.map +1 -1
  179. package/dist/mcp-reflect.js +2 -2
  180. package/dist/mcp-reflect.js.map +1 -1
  181. package/dist/mcp-render.d.ts.map +1 -1
  182. package/dist/mcp-render.js +64 -45
  183. package/dist/mcp-render.js.map +1 -1
  184. package/dist/mcp-root.d.ts +37 -9
  185. package/dist/mcp-root.d.ts.map +1 -1
  186. package/dist/mcp-root.js +124 -25
  187. package/dist/mcp-root.js.map +1 -1
  188. package/dist/mcp-run.d.ts +18 -1
  189. package/dist/mcp-run.d.ts.map +1 -1
  190. package/dist/mcp-run.js +159 -55
  191. package/dist/mcp-run.js.map +1 -1
  192. package/dist/mcp-ws.js +4 -1
  193. package/dist/mcp-ws.js.map +1 -1
  194. package/dist/mcp.d.ts +18 -5
  195. package/dist/mcp.d.ts.map +1 -1
  196. package/dist/mcp.js +165 -82
  197. package/dist/mcp.js.map +1 -1
  198. package/dist/migrate-layout.d.ts +44 -0
  199. package/dist/migrate-layout.d.ts.map +1 -0
  200. package/dist/migrate-layout.js +982 -0
  201. package/dist/migrate-layout.js.map +1 -0
  202. package/dist/openapi-tool.d.ts +2 -2
  203. package/dist/openapi-tool.d.ts.map +1 -1
  204. package/dist/openapi-tool.js.map +1 -1
  205. package/dist/openapi-types.d.ts +2 -1
  206. package/dist/openapi-types.d.ts.map +1 -1
  207. package/dist/openapi-types.js +240 -12
  208. package/dist/openapi-types.js.map +1 -1
  209. package/dist/pipeline-report.d.ts +3 -2
  210. package/dist/pipeline-report.d.ts.map +1 -1
  211. package/dist/pipeline-report.js +19 -16
  212. package/dist/pipeline-report.js.map +1 -1
  213. package/dist/port.d.ts +5 -4
  214. package/dist/port.d.ts.map +1 -1
  215. package/dist/port.js +46 -29
  216. package/dist/port.js.map +1 -1
  217. package/dist/project-facts.d.ts +28 -4
  218. package/dist/project-facts.d.ts.map +1 -1
  219. package/dist/project-facts.js +12 -2
  220. package/dist/project-facts.js.map +1 -1
  221. package/dist/recipes/4.0.0.d.ts +9 -0
  222. package/dist/recipes/4.0.0.d.ts.map +1 -0
  223. package/dist/recipes/4.0.0.js +34 -0
  224. package/dist/recipes/4.0.0.js.map +1 -0
  225. package/dist/recipes/index.d.ts.map +1 -1
  226. package/dist/recipes/index.js +2 -0
  227. package/dist/recipes/index.js.map +1 -1
  228. package/dist/release-verification.d.ts +11 -1
  229. package/dist/release-verification.d.ts.map +1 -1
  230. package/dist/release-verification.js +47 -4
  231. package/dist/release-verification.js.map +1 -1
  232. package/dist/review.d.ts +21 -0
  233. package/dist/review.d.ts.map +1 -0
  234. package/dist/review.js +823 -0
  235. package/dist/review.js.map +1 -0
  236. package/dist/route-types.d.ts +28 -0
  237. package/dist/route-types.d.ts.map +1 -0
  238. package/dist/route-types.js +106 -0
  239. package/dist/route-types.js.map +1 -0
  240. package/dist/rules/codes.d.ts +9 -0
  241. package/dist/rules/codes.d.ts.map +1 -1
  242. package/dist/rules/codes.js +9 -0
  243. package/dist/rules/codes.js.map +1 -1
  244. package/dist/rules/data-guard.d.ts +12 -0
  245. package/dist/rules/data-guard.d.ts.map +1 -0
  246. package/dist/rules/data-guard.js +213 -0
  247. package/dist/rules/data-guard.js.map +1 -0
  248. package/dist/rules/index.d.ts +3 -0
  249. package/dist/rules/index.d.ts.map +1 -1
  250. package/dist/rules/index.js +17 -5
  251. package/dist/rules/index.js.map +1 -1
  252. package/dist/rules/islands.d.ts.map +1 -1
  253. package/dist/rules/islands.js +7 -4
  254. package/dist/rules/islands.js.map +1 -1
  255. package/dist/rules/legacy.d.ts +3 -1
  256. package/dist/rules/legacy.d.ts.map +1 -1
  257. package/dist/rules/legacy.js +829 -34
  258. package/dist/rules/legacy.js.map +1 -1
  259. package/dist/rules/nano.d.ts.map +1 -1
  260. package/dist/rules/nano.js +15 -6
  261. package/dist/rules/nano.js.map +1 -1
  262. package/dist/rules/pages.d.ts +15 -0
  263. package/dist/rules/pages.d.ts.map +1 -0
  264. package/dist/rules/pages.js +88 -0
  265. package/dist/rules/pages.js.map +1 -0
  266. package/dist/rules/routes.d.ts +11 -0
  267. package/dist/rules/routes.d.ts.map +1 -1
  268. package/dist/rules/routes.js +180 -2
  269. package/dist/rules/routes.js.map +1 -1
  270. package/dist/rules/secrets.d.ts +10 -0
  271. package/dist/rules/secrets.d.ts.map +1 -0
  272. package/dist/rules/secrets.js +169 -0
  273. package/dist/rules/secrets.js.map +1 -0
  274. package/dist/rules/security.d.ts +7 -0
  275. package/dist/rules/security.d.ts.map +1 -1
  276. package/dist/rules/security.js +171 -65
  277. package/dist/rules/security.js.map +1 -1
  278. package/dist/rules/typescript.d.ts +16 -0
  279. package/dist/rules/typescript.d.ts.map +1 -0
  280. package/dist/rules/typescript.js +59 -0
  281. package/dist/rules/typescript.js.map +1 -0
  282. package/dist/rules/zones.d.ts +17 -0
  283. package/dist/rules/zones.d.ts.map +1 -0
  284. package/dist/rules/zones.js +174 -0
  285. package/dist/rules/zones.js.map +1 -0
  286. package/dist/scaffold.d.ts +25 -12
  287. package/dist/scaffold.d.ts.map +1 -1
  288. package/dist/scaffold.js +154 -90
  289. package/dist/scaffold.js.map +1 -1
  290. package/dist/sdk.d.ts +17 -3
  291. package/dist/sdk.d.ts.map +1 -1
  292. package/dist/sdk.js +941 -153
  293. package/dist/sdk.js.map +1 -1
  294. package/dist/search-terms.d.ts +2 -0
  295. package/dist/search-terms.d.ts.map +1 -1
  296. package/dist/search-terms.js +23 -8
  297. package/dist/search-terms.js.map +1 -1
  298. package/dist/smoke.d.ts +79 -0
  299. package/dist/smoke.d.ts.map +1 -0
  300. package/dist/smoke.js +494 -0
  301. package/dist/smoke.js.map +1 -0
  302. package/dist/sync-manifest.d.ts.map +1 -1
  303. package/dist/sync-manifest.js +2 -1
  304. package/dist/sync-manifest.js.map +1 -1
  305. package/dist/sync-routes.d.ts +4 -3
  306. package/dist/sync-routes.d.ts.map +1 -1
  307. package/dist/sync-routes.js +4 -3
  308. package/dist/sync-routes.js.map +1 -1
  309. package/dist/test-tool.d.ts.map +1 -1
  310. package/dist/test-tool.js +30 -11
  311. package/dist/test-tool.js.map +1 -1
  312. package/dist/types-search.d.ts.map +1 -1
  313. package/dist/types-search.js +11 -5
  314. package/dist/types-search.js.map +1 -1
  315. package/dist/upgrade.d.ts +36 -6
  316. package/dist/upgrade.d.ts.map +1 -1
  317. package/dist/upgrade.js +167 -29
  318. package/dist/upgrade.js.map +1 -1
  319. package/dist/verification-plan.d.ts.map +1 -1
  320. package/dist/verification-plan.js +8 -0
  321. package/dist/verification-plan.js.map +1 -1
  322. package/dist/verification.d.ts.map +1 -1
  323. package/dist/verification.js +45 -12
  324. package/dist/verification.js.map +1 -1
  325. package/dist/web-app-options.d.ts +19 -0
  326. package/dist/web-app-options.d.ts.map +1 -0
  327. package/dist/web-app-options.js +49 -0
  328. package/dist/web-app-options.js.map +1 -0
  329. package/dist/work-graph.d.ts +3 -0
  330. package/dist/work-graph.d.ts.map +1 -1
  331. package/dist/work-graph.js +33 -16
  332. package/dist/work-graph.js.map +1 -1
  333. package/docs/examples.json +185 -29
  334. package/docs/llms-full.txt +4123 -548
  335. package/docs/types.json +14391 -10060
  336. package/package.json +19 -11
  337. package/src/app-files.ts +40 -0
  338. package/src/assure-hydration.ts +139 -46
  339. package/src/assure.ts +17 -2
  340. package/src/capabilities-tool.ts +35 -9
  341. package/src/cdn-check.ts +268 -0
  342. package/src/check-diagnostics.ts +199 -797
  343. package/src/check-scan.ts +394 -63
  344. package/src/check.ts +146 -58
  345. package/src/cli.ts +347 -92
  346. package/src/command-catalog.ts +407 -45
  347. package/src/config-target.ts +47 -0
  348. package/src/contracts.ts +66 -15
  349. package/src/db-audit.ts +128 -0
  350. package/src/db-child.ts +285 -0
  351. package/src/db-config.ts +224 -0
  352. package/src/db-tool.ts +690 -0
  353. package/src/dev-bun-config.ts +116 -40
  354. package/src/dev-feed-tool.ts +912 -0
  355. package/src/dev-server-client.ts +267 -0
  356. package/src/diagnostics.ts +179 -0
  357. package/src/diff-tool.ts +6 -5
  358. package/src/docs-search.ts +10 -3
  359. package/src/doctor.ts +98 -15
  360. package/src/env-file.ts +78 -1
  361. package/src/fix-recipes.ts +44 -31
  362. package/src/frontend-guidance.ts +5 -5
  363. package/src/i18n-check.ts +179 -0
  364. package/src/init-agents.ts +301 -38
  365. package/src/internal/code-unit-order.ts +5 -0
  366. package/src/internal/script-kind.ts +11 -0
  367. package/src/internal/source-facts.ts +96 -22
  368. package/src/internal/typescript-import.ts +663 -68
  369. package/src/introspect.ts +93 -82
  370. package/src/learn.ts +3 -3
  371. package/src/levels-tool.ts +47 -2
  372. package/src/load.ts +144 -32
  373. package/src/manifest-tool.ts +3 -2
  374. package/src/mcp-app-tool.ts +2 -0
  375. package/src/mcp-context.ts +5 -7
  376. package/src/mcp-delegate.ts +162 -0
  377. package/src/mcp-docs-tools.ts +23 -5
  378. package/src/mcp-exec.ts +301 -161
  379. package/src/mcp-http.ts +22 -5
  380. package/src/mcp-io.ts +130 -0
  381. package/src/mcp-isolate.ts +287 -0
  382. package/src/mcp-path.ts +50 -0
  383. package/src/mcp-project-child.ts +182 -0
  384. package/src/mcp-reflect.ts +3 -2
  385. package/src/mcp-render.ts +72 -42
  386. package/src/mcp-root.ts +130 -24
  387. package/src/mcp-run.ts +190 -56
  388. package/src/mcp-ws.ts +4 -1
  389. package/src/mcp.ts +193 -91
  390. package/src/migrate-layout.ts +1121 -0
  391. package/src/openapi-tool.ts +7 -3
  392. package/src/openapi-types.ts +315 -13
  393. package/src/pipeline-report.ts +22 -18
  394. package/src/port.ts +53 -30
  395. package/src/project-facts.ts +42 -8
  396. package/src/recipes/4.0.0.ts +35 -0
  397. package/src/recipes/index.ts +2 -0
  398. package/src/release-verification.ts +74 -5
  399. package/src/review.ts +988 -0
  400. package/src/route-types.ts +133 -0
  401. package/src/rules/codes.ts +9 -0
  402. package/src/rules/data-guard.ts +230 -0
  403. package/src/rules/index.ts +18 -5
  404. package/src/rules/islands.ts +12 -4
  405. package/src/rules/legacy.ts +947 -35
  406. package/src/rules/nano.ts +18 -6
  407. package/src/rules/pages.ts +99 -0
  408. package/src/rules/routes.ts +181 -2
  409. package/src/rules/secrets.ts +174 -0
  410. package/src/rules/security.ts +198 -70
  411. package/src/rules/typescript.ts +77 -0
  412. package/src/rules/zones.ts +195 -0
  413. package/src/scaffold.ts +175 -98
  414. package/src/sdk.ts +1215 -158
  415. package/src/search-terms.ts +24 -7
  416. package/src/smoke.ts +607 -0
  417. package/src/sync-manifest.ts +2 -1
  418. package/src/sync-routes.ts +4 -3
  419. package/src/test-tool.ts +32 -11
  420. package/src/types-search.ts +11 -4
  421. package/src/upgrade.ts +205 -25
  422. package/src/verification-plan.ts +27 -0
  423. package/src/verification.ts +50 -12
  424. package/src/web-app-options.ts +57 -0
  425. package/src/work-graph.ts +40 -16
@@ -0,0 +1,99 @@
1
+ /**
2
+ * `.nifra/db-audit.jsonl`: one line per `nifra db` call - what ran, never what came back. Each entry
3
+ * carries the SQL (secrets redacted), its fingerprint, the row count, the duration and the refusal
4
+ * code. The file is owner-only, never followed through a symlink, and rotates at 1 MB to
5
+ * `db-audit.1.jsonl`, so it stays bounded.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ import { chmodSync, closeSync, constants, existsSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, writeSync, } from "node:fs";
9
+ import { resolve } from "node:path";
10
+ export const DB_AUDIT_FILE = ".nifra/db-audit.jsonl";
11
+ export const DB_AUDIT_ROTATED_FILE = ".nifra/db-audit.1.jsonl";
12
+ export const DB_AUDIT_MAX_BYTES = 1024 * 1024;
13
+ /** A statement's shape: literals become `?`, whitespace collapses, case folds; hashed. */
14
+ export function sqlFingerprint(sql) {
15
+ const shape = sql
16
+ .replace(/--[^\n]*/g, " ")
17
+ .replace(/\/\*[\s\S]*?\*\//g, " ")
18
+ .replace(/'(?:[^']|'')*'/g, "?")
19
+ .replace(/\b\d+(?:\.\d+)?\b/g, "?")
20
+ .replace(/\s+/g, " ")
21
+ .trim()
22
+ .toLowerCase();
23
+ return createHash("sha256").update(shape).digest("hex").slice(0, 16);
24
+ }
25
+ /** True when `path` exists and is a symlink: the audit never writes through one. */
26
+ function isSymlink(path) {
27
+ try {
28
+ return lstatSync(path).isSymbolicLink();
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ /**
35
+ * Append one entry. Returns false (and writes nothing) when `.nifra` or the log is a symlink or the
36
+ * write fails - an audit problem never fails the call it records.
37
+ */
38
+ export function appendDbAudit(root, entry) {
39
+ const dir = resolve(root, ".nifra");
40
+ const file = resolve(root, DB_AUDIT_FILE);
41
+ try {
42
+ if (isSymlink(dir))
43
+ return false;
44
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
45
+ if (isSymlink(file))
46
+ return false;
47
+ const line = `${JSON.stringify(entry)}\n`;
48
+ if (existsSync(file) && lstatSync(file).size + Buffer.byteLength(line) > DB_AUDIT_MAX_BYTES) {
49
+ const rotated = resolve(root, DB_AUDIT_ROTATED_FILE);
50
+ if (isSymlink(rotated))
51
+ return false;
52
+ renameSync(file, rotated);
53
+ }
54
+ const fd = openSync(file, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW, 0o600);
55
+ try {
56
+ writeSync(fd, line);
57
+ }
58
+ finally {
59
+ closeSync(fd);
60
+ }
61
+ chmodSync(file, 0o600);
62
+ return true;
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
68
+ /** True for a line `appendDbAudit` wrote (its required fields, at least). */
69
+ export const isDbAuditEntry = (value) => typeof value === "object" &&
70
+ value !== null &&
71
+ "at" in value &&
72
+ typeof value.at === "string" &&
73
+ "tool" in value &&
74
+ typeof value.tool === "string" &&
75
+ "ok" in value &&
76
+ typeof value.ok === "boolean";
77
+ /** The newest `limit` entries, oldest first, across the current and the rotated file. */
78
+ export function readDbAudit(root, limit) {
79
+ const entries = [];
80
+ for (const name of [DB_AUDIT_ROTATED_FILE, DB_AUDIT_FILE]) {
81
+ const path = resolve(root, name);
82
+ if (!existsSync(path) || isSymlink(path))
83
+ continue;
84
+ for (const line of readFileSync(path, "utf8").split("\n")) {
85
+ if (line.trim() === "")
86
+ continue;
87
+ try {
88
+ const parsed = JSON.parse(line);
89
+ if (isDbAuditEntry(parsed))
90
+ entries.push(parsed);
91
+ }
92
+ catch {
93
+ // A torn last line from a killed writer is skipped, not fatal.
94
+ }
95
+ }
96
+ }
97
+ return entries.slice(-limit);
98
+ }
99
+ //# sourceMappingURL=db-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-audit.js","sourceRoot":"","sources":["../src/db-audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The `nifra db` subprocess. The parent (`db-tool.ts`) spawns a fresh one per call: it applies the
3
+ * project's `.env` files, imports `nifra.config.ts`, reports the declared deadline, opens the database,
4
+ * runs one operation, prints the answer and exits. The parent kills it at the deadline, so the deadline
5
+ * bounds the work and not only the response.
6
+ *
7
+ * Protocol: one JSON request on stdin; answers on stdout as lines prefixed with the request's token,
8
+ * so anything the config prints cannot be read as an answer.
9
+ */
10
+ import { type DbPlan, type DbRedaction, type DbRefusal, type DbRows, type DbSchemaReport } from "@nifrajs/mcp-db/engine";
11
+ import { type PostgresRoleSql } from "@nifrajs/mcp-db/postgres";
12
+ import { type DevDatabase } from "./db-config.js";
13
+ /** The one operation a subprocess runs. */
14
+ export interface DbChildRequest {
15
+ readonly op: "schema" | "query" | "explain" | "role";
16
+ readonly sql?: string | undefined;
17
+ readonly table?: string | undefined;
18
+ readonly analyze?: boolean | undefined;
19
+ }
20
+ /** What an operation produced, before the parent adds timing and the untrusted-data note. */
21
+ export type DbChildAnswer = {
22
+ readonly ok: true;
23
+ readonly engine: DevDatabase["kind"];
24
+ readonly database: string;
25
+ readonly durationMs: number;
26
+ readonly schema?: DbSchemaReport;
27
+ readonly rows?: DbRows;
28
+ readonly plan?: DbPlan;
29
+ readonly role?: Omit<PostgresRoleSql, "serverVersionNum"> | {
30
+ readonly statements: [];
31
+ };
32
+ } | {
33
+ readonly ok: false;
34
+ readonly engine?: DevDatabase["kind"] | undefined;
35
+ readonly database?: string | undefined;
36
+ readonly durationMs: number;
37
+ readonly refusal: DbRefusal;
38
+ };
39
+ /** The lines a subprocess writes, after its token. */
40
+ export type DbChildMessage = {
41
+ readonly type: "ready";
42
+ readonly timeoutMs: number;
43
+ } | {
44
+ readonly type: "answer";
45
+ readonly answer: DbChildAnswer;
46
+ };
47
+ /**
48
+ * The masked columns a query may not read (the credential-name classifier, plus `redactColumns`,
49
+ * minus `revealColumns`) and the dev feed's text redactor for every string value.
50
+ */
51
+ export declare function dbRedaction(root: string, db: DevDatabase, env?: Readonly<Record<string, string | undefined>>): DbRedaction;
52
+ /** Run one operation against a loaded declaration. Every refusal message is redacted too. */
53
+ export declare function runDbOperation(root: string, request: DbChildRequest, db: DevDatabase): Promise<DbChildAnswer>;
54
+ /** Validate the parent's request (it crosses a process boundary). */
55
+ export declare function parseDbChildRequest(text: string): (DbChildRequest & {
56
+ readonly token: string;
57
+ }) | undefined;
58
+ /**
59
+ * Serve one request: read it from `input`, load the declaration, report the deadline, run the
60
+ * operation and write the answer through `write`, each line prefixed with the request's token.
61
+ * Returns false (writing nothing) for a malformed request.
62
+ */
63
+ export declare function serveDbChild(root: string, input: ReadableStream<Uint8Array>, write: (line: string) => Promise<void>): Promise<boolean>;
64
+ //# sourceMappingURL=db-child.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-child.d.ts","sourceRoot":"","sources":["../src/db-child.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,cAAc,EAQpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAGL,KAAK,eAAe,EAKrB,MAAM,0BAA0B,CAAA;AAGjC,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,gBAAgB,CAAA;AAGlE,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CACvC;AAED,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GACrB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAA;KAAE,CAAA;CACxF,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAC5B,CAAA;AAEL,sDAAsD;AACtD,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAI/D;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,WAAW,EACf,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,WAAW,CASb;AAwFD,6FAA6F;AAC7F,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,WAAW,GACd,OAAO,CAAC,aAAa,CAAC,CAoBxB;AAID,qEAAqE;AACrE,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,GACX,CAAC,cAAc,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAoB3D;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,EACjC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACrC,OAAO,CAAC,OAAO,CAAC,CAyBlB"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * The `nifra db` subprocess. The parent (`db-tool.ts`) spawns a fresh one per call: it applies the
3
+ * project's `.env` files, imports `nifra.config.ts`, reports the declared deadline, opens the database,
4
+ * runs one operation, prints the answer and exits. The parent kills it at the deadline, so the deadline
5
+ * bounds the work and not only the response.
6
+ *
7
+ * Protocol: one JSON request on stdin; answers on stdout as lines prefixed with the request's token,
8
+ * so anything the config prints cannot be read as an answer.
9
+ */
10
+ import { dbRefusal, explainSqlite, isDbRefusal, openReadOnlySqlite, querySqlite, readSqliteSchema, resolveSqliteFile, } from "@nifrajs/mcp-db/engine";
11
+ import { connectPostgres, explainPostgres, parsePostgresUrl, postgresRoleSql, queryPostgres, readPostgresSchema, } from "@nifrajs/mcp-db/postgres";
12
+ import { createDevFeed } from "@nifrajs/web/dev-feed";
13
+ import { isSensitiveFieldName } from "@nifrajs/web/zones";
14
+ import { loadDevDatabase } from "./db-config.js";
15
+ import { CHILD_INPUT_MAX_BYTES, readBoundedStream } from "./mcp-io.js";
16
+ const lower = (name) => name.toLowerCase();
17
+ /**
18
+ * The masked columns a query may not read (the credential-name classifier, plus `redactColumns`,
19
+ * minus `revealColumns`) and the dev feed's text redactor for every string value.
20
+ */
21
+ export function dbRedaction(root, db, env = process.env) {
22
+ const redact = new Set(db.redactColumns.map(lower));
23
+ const reveal = new Set(db.revealColumns.map(lower));
24
+ const feed = createDevFeed({ root, persist: false, env });
25
+ return {
26
+ column: (name) => !reveal.has(lower(name)) && (isSensitiveFieldName(name) || redact.has(lower(name))),
27
+ text: (value) => feed.redact(value),
28
+ };
29
+ }
30
+ const QUERY_OFF = () => dbRefusal("NIFRA_DB_QUERY_OFF", "devDatabase.query is false: only the schema tool is on");
31
+ async function runSqlite(root, request, db, redaction) {
32
+ if (request.op === "role") {
33
+ return { ok: true, engine: "sqlite", database: db.file, role: { statements: [] } };
34
+ }
35
+ if (request.op !== "schema" && !db.query)
36
+ return QUERY_OFF();
37
+ const file = resolveSqliteFile(root, db.file, db.allowFiles);
38
+ if (isDbRefusal(file))
39
+ return file;
40
+ let handle;
41
+ try {
42
+ handle = await openReadOnlySqlite(file);
43
+ }
44
+ catch (error) {
45
+ return dbRefusal("NIFRA_DB_DRIVER", `could not open ${db.file}: ${String(error)}`);
46
+ }
47
+ try {
48
+ const base = { ok: true, engine: "sqlite", database: db.file };
49
+ if (request.op === "schema") {
50
+ const schema = readSqliteSchema(handle, {
51
+ exclude: db.exclude,
52
+ redaction,
53
+ table: request.table,
54
+ });
55
+ return isDbRefusal(schema) ? schema : { ...base, schema };
56
+ }
57
+ if (request.op === "explain") {
58
+ const plan = explainSqlite(handle, request.sql ?? "", db);
59
+ return isDbRefusal(plan) ? plan : { ...base, plan };
60
+ }
61
+ const rows = querySqlite(handle, request.sql ?? "", { ...db, redaction });
62
+ return isDbRefusal(rows) ? rows : { ...base, rows };
63
+ }
64
+ finally {
65
+ handle.close();
66
+ }
67
+ }
68
+ async function runPostgres(request, db, redaction) {
69
+ if ((request.op === "query" || request.op === "explain") && !db.query)
70
+ return QUERY_OFF();
71
+ const target = parsePostgresUrl(db.url);
72
+ if (isDbRefusal(target))
73
+ return target;
74
+ const database = `${target.host ?? target.socket}:${target.port}/${target.database}`;
75
+ const client = connectPostgres(target, db);
76
+ if (isDbRefusal(client))
77
+ return client;
78
+ const base = { ok: true, engine: "postgres", database };
79
+ try {
80
+ if (request.op === "schema") {
81
+ const schema = await readPostgresSchema(client, {
82
+ ...db,
83
+ redaction,
84
+ table: request.table,
85
+ });
86
+ return isDbRefusal(schema) ? schema : { ...base, schema };
87
+ }
88
+ if (request.op === "role") {
89
+ const role = await postgresRoleSql(client, db);
90
+ if (isDbRefusal(role))
91
+ return role;
92
+ return {
93
+ ...base,
94
+ role: { role: role.role, database: role.database, statements: role.statements },
95
+ };
96
+ }
97
+ if (request.op === "explain") {
98
+ const plan = await explainPostgres(client, request.sql ?? "", {
99
+ ...db,
100
+ redaction,
101
+ analyze: request.analyze === true,
102
+ });
103
+ return isDbRefusal(plan) ? plan : { ...base, plan };
104
+ }
105
+ const rows = await queryPostgres(client, request.sql ?? "", { ...db, redaction });
106
+ return isDbRefusal(rows) ? rows : { ...base, rows };
107
+ }
108
+ finally {
109
+ await client.close({ timeout: 0 }).catch(() => { });
110
+ }
111
+ }
112
+ /** Run one operation against a loaded declaration. Every refusal message is redacted too. */
113
+ export async function runDbOperation(root, request, db) {
114
+ const started = performance.now();
115
+ const redaction = dbRedaction(root, db);
116
+ let outcome;
117
+ try {
118
+ outcome =
119
+ db.kind === "sqlite"
120
+ ? await runSqlite(root, request, db, redaction)
121
+ : await runPostgres(request, db, redaction);
122
+ }
123
+ catch (error) {
124
+ outcome = dbRefusal("NIFRA_DB_DRIVER", error instanceof Error ? error.message : String(error));
125
+ }
126
+ const durationMs = Math.round(performance.now() - started);
127
+ if (!isDbRefusal(outcome))
128
+ return { ...outcome, durationMs };
129
+ return {
130
+ ok: false,
131
+ engine: db.kind,
132
+ durationMs,
133
+ refusal: { ...outcome, message: redaction.text?.(outcome.message) ?? outcome.message },
134
+ };
135
+ }
136
+ const OPS = ["schema", "query", "explain", "role"];
137
+ /** Validate the parent's request (it crosses a process boundary). */
138
+ export function parseDbChildRequest(text) {
139
+ let value;
140
+ try {
141
+ value = JSON.parse(text);
142
+ }
143
+ catch {
144
+ return undefined;
145
+ }
146
+ if (typeof value !== "object" || value === null)
147
+ return undefined;
148
+ const field = (key) => Reflect.get(value, key);
149
+ const token = field("token");
150
+ const op = OPS.find((name) => name === field("op"));
151
+ const sql = field("sql");
152
+ const table = field("table");
153
+ const analyze = field("analyze");
154
+ if (typeof token !== "string" || !/^[0-9a-f-]{16,64}$/.test(token))
155
+ return undefined;
156
+ if (op === undefined)
157
+ return undefined;
158
+ if (sql !== undefined && typeof sql !== "string")
159
+ return undefined;
160
+ if (table !== undefined && typeof table !== "string")
161
+ return undefined;
162
+ if (analyze !== undefined && typeof analyze !== "boolean")
163
+ return undefined;
164
+ return { token, op, sql, table, analyze };
165
+ }
166
+ /**
167
+ * Serve one request: read it from `input`, load the declaration, report the deadline, run the
168
+ * operation and write the answer through `write`, each line prefixed with the request's token.
169
+ * Returns false (writing nothing) for a malformed request.
170
+ */
171
+ export async function serveDbChild(root, input, write) {
172
+ const text = await readBoundedStream(input, CHILD_INPUT_MAX_BYTES);
173
+ const request = text.truncated ? undefined : parseDbChildRequest(text.text);
174
+ if (request === undefined)
175
+ return false;
176
+ const send = (message) => write(`${request.token} ${JSON.stringify(message)}\n`);
177
+ const started = performance.now();
178
+ let answer;
179
+ try {
180
+ const db = await loadDevDatabase(root);
181
+ if (isDbRefusal(db)) {
182
+ answer = { ok: false, durationMs: Math.round(performance.now() - started), refusal: db };
183
+ }
184
+ else {
185
+ await send({ type: "ready", timeoutMs: db.timeoutMs });
186
+ answer = await runDbOperation(root, request, db);
187
+ }
188
+ }
189
+ catch (error) {
190
+ answer = {
191
+ ok: false,
192
+ durationMs: Math.round(performance.now() - started),
193
+ refusal: dbRefusal("NIFRA_DB_DRIVER", error instanceof Error ? error.message : String(error)),
194
+ };
195
+ }
196
+ await send({ type: "answer", answer });
197
+ return true;
198
+ }
199
+ if (import.meta.main) {
200
+ // Bound before the config loads, so nothing it patches can reach the protocol channel.
201
+ const stdout = process.stdout.write.bind(process.stdout);
202
+ const toStderr = (...args) => {
203
+ process.stderr.write(`${args.map((arg) => String(arg)).join(" ")}\n`);
204
+ };
205
+ console.log = toStderr;
206
+ console.info = toStderr;
207
+ console.debug = toStderr;
208
+ const served = await serveDbChild(process.argv[2] ?? process.cwd(), Bun.stdin.stream(), (line) => new Promise((done) => stdout(line, () => done())));
209
+ if (!served)
210
+ process.stderr.write("nifra db: invalid request\n");
211
+ process.exit(served ? 0 : 2);
212
+ }
213
+ //# sourceMappingURL=db-child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-child.js","sourceRoot":"","sources":["../src/db-child.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The `devDatabase` declaration in `nifra.config.ts`: the one database `nifra db` / `nifra_db_*` may
3
+ * read. Declared explicitly or not at all - `DATABASE_URL` is never read on its own, because it often
4
+ * points at production.
5
+ *
6
+ * export const devDatabase = { kind: "sqlite", file: "./data/app.db" }
7
+ * export const devDatabase = {
8
+ * kind: "postgres",
9
+ * url: process.env.NIFRA_DEV_DATABASE_URL,
10
+ * exclude: ["audit_log"],
11
+ * }
12
+ *
13
+ * The config is imported only inside the query subprocess (`db-child.ts`), after `.env` files load.
14
+ */
15
+ import { type DbRefusal } from "@nifrajs/mcp-db/engine";
16
+ /** Fields both engines share, with their defaults applied. */
17
+ export interface DevDatabaseCommon {
18
+ /** Tables a query may not read (`name`, or `schema.name` on Postgres). */
19
+ readonly exclude: readonly string[];
20
+ readonly maxRows: number;
21
+ readonly maxResultBytes: number;
22
+ readonly timeoutMs: number;
23
+ /** Columns a query may not read, on top of the credential-name classifier. */
24
+ readonly redactColumns: readonly string[];
25
+ /** Columns the classifier names that a query may read anyway. */
26
+ readonly revealColumns: readonly string[];
27
+ /** `nifra db query` is on for a declared database unless this is false. */
28
+ readonly query: boolean;
29
+ }
30
+ export interface DevSqliteDatabase extends DevDatabaseCommon {
31
+ readonly kind: "sqlite";
32
+ /** Relative to the project root; its real path must stay inside the root. */
33
+ readonly file: string;
34
+ /** Paths outside the root that `file` may resolve to. */
35
+ readonly allowFiles: readonly string[];
36
+ }
37
+ export interface DevPostgresDatabase extends DevDatabaseCommon {
38
+ readonly kind: "postgres";
39
+ readonly url: string;
40
+ /** Remote hosts the URL may name; loopback, `*.localhost` and unix sockets need no entry. */
41
+ readonly allowHosts: readonly string[];
42
+ /** Extensions or untrusted languages the role may use without the query being refused. */
43
+ readonly allowExtensions: readonly string[];
44
+ /** Schemas a query may read (default `["public"]`). */
45
+ readonly schemas: readonly string[];
46
+ }
47
+ export type DevDatabase = DevSqliteDatabase | DevPostgresDatabase;
48
+ /** The `.env` files the subprocess applies, lowest precedence first; the process env wins over all. */
49
+ export declare const DEV_DATABASE_ENV_FILES: readonly [".env", ".env.development", ".env.local", ".env.development.local"];
50
+ /** Validate a `devDatabase` export and apply defaults. Unknown fields are refused, never ignored. */
51
+ export declare function parseDevDatabase(value: unknown): DevDatabase | DbRefusal;
52
+ /** The project's `.env` files as one record, for redacting with the values the subprocess sees. */
53
+ export declare function readProjectEnv(root: string): Promise<Record<string, string>>;
54
+ /**
55
+ * Apply the project's `.env` files (never overriding the process environment), then import
56
+ * `nifra.config.ts` and validate its `devDatabase` export. Runs in the query subprocess only: the
57
+ * config is code, and importing it in a long-lived process would pin its modules.
58
+ */
59
+ export declare function loadDevDatabase(root: string): Promise<DevDatabase | DbRefusal>;
60
+ //# sourceMappingURL=db-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-config.d.ts","sourceRoot":"","sources":["../src/db-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,wBAAwB,CAAA;AAIlE,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,iEAAiE;IACjE,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yDAAyD;IACzD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,0FAA0F;IAC1F,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,CAAA;AAqBjE,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,+EAKzB,CAAA;AAwCV,qGAAqG;AACrG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CA0DxE;AAED,mGAAmG;AACnG,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQlF;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAwBpF"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * The `devDatabase` declaration in `nifra.config.ts`: the one database `nifra db` / `nifra_db_*` may
3
+ * read. Declared explicitly or not at all - `DATABASE_URL` is never read on its own, because it often
4
+ * points at production.
5
+ *
6
+ * export const devDatabase = { kind: "sqlite", file: "./data/app.db" }
7
+ * export const devDatabase = {
8
+ * kind: "postgres",
9
+ * url: process.env.NIFRA_DEV_DATABASE_URL,
10
+ * exclude: ["audit_log"],
11
+ * }
12
+ *
13
+ * The config is imported only inside the query subprocess (`db-child.ts`), after `.env` files load.
14
+ */
15
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
16
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
17
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
18
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
19
+ });
20
+ }
21
+ return path;
22
+ };
23
+ import { existsSync } from "node:fs";
24
+ import { resolve } from "node:path";
25
+ import { dbRefusal } from "@nifrajs/mcp-db/engine";
26
+ import { CONFIG_FILE } from "./app-files.js";
27
+ import { applyEnvFiles, parseEnvFile } from "./env-file.js";
28
+ const COMMON_FIELDS = [
29
+ "kind",
30
+ "exclude",
31
+ "maxRows",
32
+ "maxResultBytes",
33
+ "timeoutMs",
34
+ "redactColumns",
35
+ "revealColumns",
36
+ "query",
37
+ ];
38
+ const SQLITE_FIELDS = new Set([...COMMON_FIELDS, "file", "allowFiles"]);
39
+ const POSTGRES_FIELDS = new Set([
40
+ ...COMMON_FIELDS,
41
+ "url",
42
+ "allowHosts",
43
+ "allowExtensions",
44
+ "schemas",
45
+ ]);
46
+ /** The `.env` files the subprocess applies, lowest precedence first; the process env wins over all. */
47
+ export const DEV_DATABASE_ENV_FILES = [
48
+ ".env",
49
+ ".env.development",
50
+ ".env.local",
51
+ ".env.development.local",
52
+ ];
53
+ class InvalidDeclaration extends Error {
54
+ }
55
+ function invalid(message) {
56
+ throw new InvalidDeclaration(message);
57
+ }
58
+ function strings(fields, field) {
59
+ const value = fields(field);
60
+ if (value === undefined)
61
+ return [];
62
+ if (!Array.isArray(value))
63
+ invalid(`devDatabase.${field} must be an array of non-empty strings`);
64
+ const out = [];
65
+ for (const item of value) {
66
+ if (typeof item !== "string" || item === "") {
67
+ invalid(`devDatabase.${field} must be an array of non-empty strings`);
68
+ }
69
+ out.push(item);
70
+ }
71
+ return out;
72
+ }
73
+ function integer(fields, field, fallback, min, max) {
74
+ const value = fields(field);
75
+ if (value === undefined)
76
+ return fallback;
77
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) {
78
+ invalid(`devDatabase.${field} must be an integer from ${min} to ${max}`);
79
+ }
80
+ return value;
81
+ }
82
+ /** Validate a `devDatabase` export and apply defaults. Unknown fields are refused, never ignored. */
83
+ export function parseDevDatabase(value) {
84
+ try {
85
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
86
+ invalid('devDatabase must be an object such as { kind: "sqlite", file: "./data/app.db" }');
87
+ }
88
+ const fields = (field) => Reflect.get(value, field);
89
+ const declared = fields("kind");
90
+ const kind = declared === "sqlite" || declared === "postgres"
91
+ ? declared
92
+ : invalid('devDatabase.kind must be "sqlite" or "postgres"');
93
+ const known = kind === "sqlite" ? SQLITE_FIELDS : POSTGRES_FIELDS;
94
+ for (const field of Object.keys(value)) {
95
+ if (!known.has(field))
96
+ invalid(`devDatabase has no field ${JSON.stringify(field)} for kind ${kind}`);
97
+ }
98
+ const query = fields("query");
99
+ if (query !== undefined && typeof query !== "boolean") {
100
+ invalid("devDatabase.query must be a boolean");
101
+ }
102
+ const common = {
103
+ exclude: strings(fields, "exclude"),
104
+ maxRows: integer(fields, "maxRows", 100, 1, 10_000),
105
+ // The answer crosses the subprocess pipe as one line, bounded at 1 MiB with its envelope.
106
+ maxResultBytes: integer(fields, "maxResultBytes", 100 * 1024, 1024, 512 * 1024),
107
+ timeoutMs: integer(fields, "timeoutMs", 5_000, 100, 120_000),
108
+ redactColumns: strings(fields, "redactColumns"),
109
+ revealColumns: strings(fields, "revealColumns"),
110
+ query: query !== false,
111
+ };
112
+ if (kind === "sqlite") {
113
+ const file = fields("file");
114
+ if (typeof file !== "string" || file === "" || file.startsWith(":memory:")) {
115
+ invalid("devDatabase.file must be the path of a SQLite database file");
116
+ }
117
+ return { ...common, kind, file, allowFiles: strings(fields, "allowFiles") };
118
+ }
119
+ const url = fields("url");
120
+ if (url === undefined) {
121
+ invalid(`devDatabase.url is undefined: the environment variable it reads is not set (nifra applies ${DEV_DATABASE_ENV_FILES.join(", ")} first)`);
122
+ }
123
+ if (typeof url !== "string" || url === "") {
124
+ invalid("devDatabase.url must be a postgres:// URL");
125
+ }
126
+ return {
127
+ ...common,
128
+ kind,
129
+ url,
130
+ allowHosts: strings(fields, "allowHosts"),
131
+ allowExtensions: strings(fields, "allowExtensions"),
132
+ schemas: fields("schemas") === undefined ? ["public"] : strings(fields, "schemas"),
133
+ };
134
+ }
135
+ catch (error) {
136
+ if (error instanceof InvalidDeclaration)
137
+ return dbRefusal("NIFRA_DB_CONFIG", error.message);
138
+ throw error;
139
+ }
140
+ }
141
+ /** The project's `.env` files as one record, for redacting with the values the subprocess sees. */
142
+ export async function readProjectEnv(root) {
143
+ const merged = {};
144
+ for (const file of DEV_DATABASE_ENV_FILES) {
145
+ const path = resolve(root, file);
146
+ if (!existsSync(path))
147
+ continue;
148
+ Object.assign(merged, parseEnvFile(await Bun.file(path).text()));
149
+ }
150
+ return merged;
151
+ }
152
+ /**
153
+ * Apply the project's `.env` files (never overriding the process environment), then import
154
+ * `nifra.config.ts` and validate its `devDatabase` export. Runs in the query subprocess only: the
155
+ * config is code, and importing it in a long-lived process would pin its modules.
156
+ */
157
+ export async function loadDevDatabase(root) {
158
+ await applyEnvFiles(root, DEV_DATABASE_ENV_FILES.filter((file) => existsSync(resolve(root, file))));
159
+ const configPath = resolve(root, CONFIG_FILE);
160
+ if (!existsSync(configPath)) {
161
+ return dbRefusal("NIFRA_DB_NOT_DECLARED", `${root} has no ${CONFIG_FILE} declaring devDatabase`);
162
+ }
163
+ let config;
164
+ try {
165
+ config = await import(__rewriteRelativeImportExtension(configPath));
166
+ }
167
+ catch (error) {
168
+ return dbRefusal("NIFRA_DB_CONFIG", `${CONFIG_FILE} failed to load: ${error instanceof Error ? error.message : String(error)}`);
169
+ }
170
+ const declared = typeof config === "object" && config !== null ? Reflect.get(config, "devDatabase") : undefined;
171
+ if (declared === undefined) {
172
+ return dbRefusal("NIFRA_DB_NOT_DECLARED", `${CONFIG_FILE} does not export devDatabase`);
173
+ }
174
+ return parseDevDatabase(declared);
175
+ }
176
+ //# sourceMappingURL=db-config.js.map