@voltro/cli 0.33.0 → 0.34.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 (337) hide show
  1. package/CHANGELOG.md +1801 -0
  2. package/bin/nodeEnvironment.d.mts +30 -0
  3. package/bin/nodeEnvironment.mjs +158 -0
  4. package/bin/voltro.mjs +69 -5
  5. package/dist/addCommand-BNeoeSxe.js +124 -0
  6. package/dist/addCommand-aXSQveak.js +2 -0
  7. package/dist/agentsMd-BTchIZku.js +2 -0
  8. package/dist/agentsMd-mhQMF1bx.js +254 -0
  9. package/dist/apiBuild-BICVGnEC.js +2 -0
  10. package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DZF_f0_I.js} +46 -46
  11. package/dist/appPort-B_HpJ_ck.js +48 -0
  12. package/dist/baselineCommand-C2ClWZN3.js +2 -0
  13. package/dist/baselineCommand-DIttzO8A.js +227 -0
  14. package/dist/bin.js +71 -28
  15. package/dist/build-CE7Ca9gm.js +711 -0
  16. package/dist/cacheCommand-DA4OH9xt.js +42 -0
  17. package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
  18. package/dist/checkCommand-Dv8r9tGO.js +231 -0
  19. package/dist/checkCommand-YZDmkAGd.js +2 -0
  20. package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
  21. package/dist/cliError-BmdYnghb.js +10 -0
  22. package/dist/cliOutput-D1tSBoRM.js +15 -0
  23. package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
  24. package/dist/cloudClient-DWL-Hw_T.js +67 -0
  25. package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
  26. package/dist/clusterCmd-CNXKlcvD.js +54 -0
  27. package/dist/codegen-CYM3Zqrf.js +605 -0
  28. package/dist/codegen-ChBi_hVa.js +2 -0
  29. package/dist/codegenCommand-DC6w2tNZ.js +30 -0
  30. package/dist/codemodRunner-DRRqXR74.js +5243 -0
  31. package/dist/commandRunner-BLAEFLjp.js +47 -0
  32. package/dist/commands-gutsz-Ac.js +808 -0
  33. package/dist/connectionConfig-UFlIEiys.js +66 -0
  34. package/dist/dashboardCommand-3YG8p-UA.js +25 -0
  35. package/dist/dataCommand-qL0r7fPO.js +535 -0
  36. package/dist/dataProfile-dW-PsfLB.js +15 -0
  37. package/dist/{dbCommand-DTLKAfbA.js → dbCommand-B6X0FZbq.js} +551 -401
  38. package/dist/dbCommand-CpdKLeQq.js +2 -0
  39. package/dist/{dev-C_P8FLSx.js → dev-CerMd0mW.js} +3122 -3129
  40. package/dist/dev-CoG-ZPx8.js +3 -0
  41. package/dist/devActivity-Dx_3nnGv.js +100 -0
  42. package/dist/devActivity.js +1 -1
  43. package/dist/dialectDriver-CgXnDfec.js +39 -0
  44. package/dist/discover-C9XKJDco.js +25 -0
  45. package/dist/doctorCommand-BvqGBwNG.js +2 -0
  46. package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-CnDXQxfa.js} +520 -1278
  47. package/dist/dormancyCommand-Dszo57d6.js +69 -0
  48. package/dist/e2eCmd-BRabZww-.js +147 -0
  49. package/dist/embeddingsCommand-C0sKVRo1.js +73 -0
  50. package/dist/envCommand-DPgdV1Bq.js +60 -0
  51. package/dist/evalCommand-6RUfPen4.js +118 -0
  52. package/dist/evolveCommand-DHpkgjgH.js +281 -0
  53. package/dist/fileTaxonomy-CJfgOllU.js +457 -0
  54. package/dist/frameworkTableAssembly-BGHmck-x.js +2 -0
  55. package/dist/{frameworkTableAssembly-BwIrO5nv.js → frameworkTableAssembly-DkkP6BgC.js} +184 -148
  56. package/dist/generateCommand-oibemh97.js +147 -0
  57. package/dist/index.d.ts +45 -0
  58. package/dist/index.js +4 -3
  59. package/dist/infoCommand-BJw9nLUR.js +60 -0
  60. package/dist/{inspect-BUUjt773.js → inspect-CBqFtAKA.js} +82 -40
  61. package/dist/inspect-C_T_WGvl.js +2 -0
  62. package/dist/inspectCmd-Bppy-GGw.js +224 -0
  63. package/dist/inspectFetch-Cm8_wVvp.js +151 -0
  64. package/dist/inspectMetrics-CfdKLh6t.js +72 -0
  65. package/dist/loadEnv-D9nEOClM.js +44 -0
  66. package/dist/logFileSink-C_D2wRN1.js +105 -0
  67. package/dist/logsCmd-CCca3KRZ.js +260 -0
  68. package/dist/manifestBuild-ChsKAhmn.js +2 -0
  69. package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-sxpwdKY1.js} +1 -1
  70. package/dist/metaCommands-7MJfZ5cf.js +196 -0
  71. package/dist/migrate-CBwOt_iV.js +83 -0
  72. package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
  73. package/dist/newCommand-COWOJ1_E.js +156 -0
  74. package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
  75. package/dist/packageCommand-Cug_3Ogl.js +271 -0
  76. package/dist/pageConvention-cEiRxdab.js +5 -0
  77. package/dist/privacyCommand-C-Df56U_.js +146 -0
  78. package/dist/projectScaffold-DmzEKHib.js +2 -0
  79. package/dist/projectScaffold-LMMtaavR.js +814 -0
  80. package/dist/renderModeScan-D7J1B7Kw.js +105 -0
  81. package/dist/renderProfile-1OWWAAtx.js +81 -0
  82. package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
  83. package/dist/runtimeTrace-CRxalXTs.js +91 -0
  84. package/dist/scheduleCmd--jksTrf6.js +69 -0
  85. package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
  86. package/dist/schemaIr-UJybUUZW.js +103 -0
  87. package/dist/{sdkgen-C81QIkiL.js → sdkgen-CYJscZC7.js} +111 -209
  88. package/dist/seedRunner-TFHHiToI.js +329 -0
  89. package/dist/serveCommand-B_isw7q4.js +1647 -0
  90. package/dist/serveCommand-DOvbgRnQ.js +2 -0
  91. package/dist/serveEntry.js +5 -5
  92. package/dist/serverlessCommand-CfJZy6dS.js +482 -0
  93. package/dist/start-9LiUOfES.js +1087 -0
  94. package/dist/start-B-9Nsp-S.js +3 -0
  95. package/dist/startEntry.js +2 -2
  96. package/dist/staticCommand-Dr2M6tpU.js +304 -0
  97. package/dist/storageCommand-Co6NfLqN.js +42 -0
  98. package/dist/templates-De8IR5-c.js +102 -0
  99. package/dist/test-CI6iDsYc.js +115 -0
  100. package/dist/tracesCmd-CkEZQrtt.js +232 -0
  101. package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
  102. package/dist/tsxLoader-EuXmSJ1K.js +51 -0
  103. package/dist/typecheckCommand-BlsWiCNq.js +61 -0
  104. package/dist/updateCommand-Bkptutss.js +585 -0
  105. package/dist/updateCommand-us1_hdIC.js +2 -0
  106. package/dist/{inspectMetrics-1xzTKAFx.js → webDev-CBYvPqQr.js} +975 -1559
  107. package/dist/webDev-Cg-fFiyd2.js +2 -0
  108. package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
  109. package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
  110. package/dist/webhooksCommand-CID96Rga.js +267 -0
  111. package/dist/workflowsCmd-D1VTmLMY.js +608 -0
  112. package/package.json +179 -17
  113. package/templates/AGENTS.core.md +58 -3
  114. package/templates/AGENTS.md +64 -7
  115. package/templates/agent-docs/_index.md +6 -4
  116. package/templates/agent-docs/_manifest.json +22 -5
  117. package/templates/agent-docs/ai.md +370 -0
  118. package/templates/agent-docs/authentication.md +265 -31
  119. package/templates/agent-docs/caching.md +6 -0
  120. package/templates/agent-docs/cli.md +794 -50
  121. package/templates/agent-docs/data.md +550 -11
  122. package/templates/agent-docs/database/migrations.md +174 -25
  123. package/templates/agent-docs/database/misc.md +156 -40
  124. package/templates/agent-docs/database/querying.md +19 -1
  125. package/templates/agent-docs/database/scaling.md +60 -0
  126. package/templates/agent-docs/database/schema.md +1 -0
  127. package/templates/agent-docs/database/seedsdialects.md +208 -19
  128. package/templates/agent-docs/database/transactions.md +68 -0
  129. package/templates/agent-docs/deployment.md +69 -3
  130. package/templates/agent-docs/introduction.md +87 -16
  131. package/templates/agent-docs/local-first-mobile.md +79 -4
  132. package/templates/agent-docs/multi-tenancy.md +95 -20
  133. package/templates/agent-docs/observability.md +58 -3
  134. package/templates/agent-docs/plugins/ai-flows.md +161 -2
  135. package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
  136. package/templates/agent-docs/plugins/audit.md +37 -1
  137. package/templates/agent-docs/plugins/auth-social.md +143 -0
  138. package/templates/agent-docs/plugins/auth-workos.md +4 -2
  139. package/templates/agent-docs/plugins/auth.md +131 -6
  140. package/templates/agent-docs/plugins/billing.md +132 -15
  141. package/templates/agent-docs/plugins/cdc-out.md +46 -7
  142. package/templates/agent-docs/plugins/clickhouse.md +1 -1
  143. package/templates/agent-docs/plugins/duckdb.md +1 -1
  144. package/templates/agent-docs/plugins/flags.md +132 -0
  145. package/templates/agent-docs/plugins/governance.md +105 -7
  146. package/templates/agent-docs/plugins/multitenancy.md +9 -4
  147. package/templates/agent-docs/plugins/presence.md +13 -2
  148. package/templates/agent-docs/plugins/ratelimit.md +9 -0
  149. package/templates/agent-docs/plugins/search.md +157 -6
  150. package/templates/agent-docs/plugins/sso-saml.md +47 -8
  151. package/templates/agent-docs/plugins/webhooks.md +105 -0
  152. package/templates/agent-docs/plugins.md +150 -18
  153. package/templates/agent-docs/reference.md +60 -3
  154. package/templates/agent-docs/releases.md +1117 -0
  155. package/templates/agent-docs/routing.md +43 -25
  156. package/templates/agent-docs/scheduling.md +10 -0
  157. package/templates/agent-docs/schema-driven-ui.md +92 -12
  158. package/templates/agent-docs/security.md +426 -0
  159. package/templates/agent-docs/templates/apibackends.md +87 -18
  160. package/templates/agent-docs/templates/appshells.md +32 -14
  161. package/templates/agent-docs/templates/overview.md +13 -8
  162. package/templates/agent-docs/testing.md +211 -14
  163. package/templates/agent-docs/whats-new.md +1700 -104
  164. package/templates/agent-docs/workflows.md +130 -14
  165. package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
  166. package/templates/apps/api-ai/package.json +8 -7
  167. package/templates/apps/api-auth/actions/me.action.ts +13 -0
  168. package/templates/apps/api-auth/package.json +9 -8
  169. package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
  170. package/templates/apps/api-backend/package.json +12 -8
  171. package/templates/apps/api-backend/queries/notes.query.ts +30 -8
  172. package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
  173. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
  174. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
  175. package/templates/apps/api-backend-deactivation/package.json +8 -7
  176. package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
  177. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
  178. package/templates/apps/api-backend-mail/package.json +9 -8
  179. package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
  180. package/templates/apps/api-backend-mariadb/.env.example +14 -0
  181. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
  182. package/templates/apps/api-backend-mariadb/package.json +10 -9
  183. package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
  184. package/templates/apps/api-backend-sqlite/.env.example +14 -0
  185. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
  186. package/templates/apps/api-backend-sqlite/package.json +9 -8
  187. package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
  188. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
  189. package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
  190. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
  191. package/templates/apps/api-backend-storage/package.json +9 -8
  192. package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
  193. package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
  194. package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
  195. package/templates/apps/api-cms/actions/me.action.ts +13 -0
  196. package/templates/apps/api-cms/app.config.ts +19 -0
  197. package/templates/apps/api-cms/authz.ts +63 -0
  198. package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
  199. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
  200. package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
  201. package/templates/apps/api-cms/package.json +11 -10
  202. package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
  203. package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
  204. package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
  205. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
  206. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
  207. package/templates/apps/api-collab/package.json +9 -8
  208. package/templates/apps/api-collab/queries/documents.query.ts +19 -7
  209. package/templates/apps/api-data-advanced/package.json +9 -8
  210. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
  211. package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
  212. package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
  213. package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
  214. package/templates/apps/api-durable/package.json +9 -8
  215. package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
  216. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
  217. package/templates/apps/api-feature-flags/package.json +10 -9
  218. package/templates/apps/api-governance/README.md +30 -8
  219. package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
  220. package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
  221. package/templates/apps/api-governance/database/schema.ts +16 -4
  222. package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
  223. package/templates/apps/api-governance/package.json +9 -8
  224. package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
  225. package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
  226. package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
  227. package/templates/apps/api-kv/package.json +9 -8
  228. package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
  229. package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
  230. package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
  231. package/templates/apps/api-moderation/package.json +9 -8
  232. package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
  233. package/templates/apps/api-observability/package.json +9 -8
  234. package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
  235. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
  236. package/templates/apps/api-ratelimit/package.json +9 -8
  237. package/templates/apps/api-rbac/package.json +9 -8
  238. package/templates/apps/api-rest/package.json +8 -7
  239. package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
  240. package/templates/apps/api-saas/package.json +12 -11
  241. package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
  242. package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
  243. package/templates/apps/api-saas-starter/app.config.ts +19 -0
  244. package/templates/apps/api-saas-starter/authz.ts +75 -0
  245. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
  246. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
  247. package/templates/apps/api-saas-starter/package.json +15 -11
  248. package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
  249. package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
  250. package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
  251. package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
  252. package/templates/apps/api-search/package.json +9 -8
  253. package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
  254. package/templates/apps/api-status/README.md +10 -3
  255. package/templates/apps/api-status/app.config.ts +8 -3
  256. package/templates/apps/api-status/authz.ts +5 -3
  257. package/templates/apps/api-status/package.json +9 -8
  258. package/templates/apps/api-status/queries/components.list.query.ts +14 -6
  259. package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
  260. package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
  261. package/templates/apps/api-status/tests/status.test.ts +9 -1
  262. package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
  263. package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
  264. package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
  265. package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
  266. package/templates/apps/api-versioning/package.json +9 -8
  267. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
  268. package/templates/apps/api-webhooks/package.json +10 -9
  269. package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
  270. package/templates/apps/changelog/package.json +8 -6
  271. package/templates/apps/edge-functions/package.json +2 -2
  272. package/templates/apps/frontend-admin/package.json +10 -8
  273. package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
  274. package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
  275. package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
  276. package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
  277. package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
  278. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
  279. package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
  280. package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
  281. package/templates/apps/frontend-app/app.config.ts +4 -3
  282. package/templates/apps/frontend-app/package.json +11 -8
  283. package/templates/apps/frontend-app/src/lib/api.ts +25 -0
  284. package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
  285. package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
  286. package/templates/apps/frontend-auth/package.json +10 -8
  287. package/templates/apps/frontend-blank/package.json +9 -7
  288. package/templates/apps/frontend-cms/package.json +11 -9
  289. package/templates/apps/frontend-collab/package.json +12 -9
  290. package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
  291. package/templates/apps/frontend-contact/package.json +9 -7
  292. package/templates/apps/frontend-dashboard/package.json +9 -7
  293. package/templates/apps/frontend-docs/package.json +9 -7
  294. package/templates/apps/frontend-i18n/package.json +8 -6
  295. package/templates/apps/frontend-landing/package.json +9 -7
  296. package/templates/apps/frontend-portal/package.json +10 -8
  297. package/templates/apps/frontend-saas/app.config.ts +10 -6
  298. package/templates/apps/frontend-saas/package.json +10 -8
  299. package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
  300. package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
  301. package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
  302. package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
  303. package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
  304. package/templates/apps/frontend-spa/package.json +9 -7
  305. package/templates/apps/frontend-ssr/package.json +9 -7
  306. package/templates/apps/frontend-ssr-api/package.json +10 -8
  307. package/templates/apps/frontend-static-blog/package.json +8 -6
  308. package/templates/apps/frontend-status/package.json +10 -8
  309. package/templates/apps/mobile-app/README.md +1 -0
  310. package/templates/apps/mobile-app/package.json +4 -2
  311. package/templates/apps/mobile-app/src/app/index.tsx +22 -12
  312. package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
  313. package/templates/apps/mobile-app/src/lib/api.ts +34 -0
  314. package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
  315. package/templates/baselines/bare/.env.example +14 -0
  316. package/templates/baselines/bare/baseline.json +4 -4
  317. package/templates/baselines/compose/.env.example +14 -0
  318. package/templates/baselines/compose/README.md +1 -1
  319. package/templates/baselines/compose/baseline.json +5 -5
  320. package/templates/baselines/compose-mariadb/.env.example +14 -0
  321. package/templates/baselines/compose-mariadb/README.md +1 -1
  322. package/templates/baselines/compose-mariadb/baseline.json +5 -5
  323. package/templates/baselines/helm/.env.example +14 -0
  324. package/templates/baselines/helm/baseline.json +4 -4
  325. package/dist/apiBuild-C-x9YacA.js +0 -2
  326. package/dist/checkCommand-DRovTKza.js +0 -2
  327. package/dist/commands-CJfepbm4.js +0 -11541
  328. package/dist/dbCommand-b1gum4td.js +0 -2
  329. package/dist/dev-iiMtlkfs.js +0 -3
  330. package/dist/devActivity-BhIu6ncs.js +0 -159
  331. package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
  332. package/dist/inspect-mmBuRXmy.js +0 -2
  333. package/dist/manifestBuild-Dj8Jjoto.js +0 -2
  334. package/dist/seedRunner-Bqxgp7HZ.js +0 -230
  335. package/dist/serveCommand-DdaM4Hup.js +0 -1608
  336. package/dist/start-C0koT0UO.js +0 -1084
  337. /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
@@ -1,720 +1,18 @@
1
- import { Ct as e, et as t, f as n, n as r, nt as i, o as a, rt as o, tt as s, z as c } from "./inspectMetrics-1xzTKAFx.js";
2
- import { Gt as l, Wt as u } from "./dev-C_P8FLSx.js";
3
- import { C as d, E as f, O as p, T as m, d as h, h as g, i as _, l as v, m as y, r as ee, u as te } from "./fileConventions-Cof68_BL.js";
4
- import { r as ne, u as re } from "./apiBuild-h9VHtnlw.js";
5
- import { r as ie } from "./appGraph-gQ_6GkQQ.js";
6
- import { dirname as b, isAbsolute as ae, join as x, relative as S, resolve as C, sep as w } from "node:path";
7
- import { pathToFileURL as oe } from "node:url";
8
- import { routeDiagnosticsToStderr as se } from "@voltro/logger";
9
- import { existsSync as T, readFileSync as E, readdirSync as D, statSync as O, writeFileSync as ce } from "node:fs";
10
- import { Node as k, Project as A, SyntaxKind as j } from "ts-morph";
11
- //#region src/cliOutput.ts
12
- var le = () => {
13
- se();
14
- }, M = (e, t = {}) => {
15
- console.log(JSON.stringify(e, null, t.pretty === !1 ? 0 : 2));
16
- }, ue = (e) => {
17
- for (let t = 0; t < e.length; t++) {
18
- let n = e[t];
19
- if (n === "--json" || n === "--format=json" || n === "--format" && e[t + 1] === "json") return !0;
20
- }
21
- return !1;
22
- }, de = (e, t, n = !1) => {
23
- let r = { ...e ?? {} }, i = t ?? process.env.VOLTRO_INSPECT_TOKEN;
24
- if (i !== void 0 && i !== "" && (r.authorization = `Bearer ${i}`), n) {
25
- let e = process.env.VOLTRO_INSPECT_WRITE_TOKEN;
26
- e !== void 0 && e !== "" && (r["x-voltro-inspect-write"] = e);
27
- }
28
- return r;
29
- }, fe = /* @__PURE__ */ new Set([
30
- "POST",
31
- "PUT",
32
- "PATCH",
33
- "DELETE"
34
- ]), N = async (e, t, n) => {
35
- let r = `${e.url.replace(/\/+$/, "")}${t.startsWith("/") ? t : `/${t}`}`, i = n?.method ?? "GET", a = n?.body !== void 0;
36
- try {
37
- let t = await fetch(r, {
38
- method: i,
39
- headers: de(a ? { "content-type": "application/json" } : void 0, e.inspectToken, fe.has(i)),
40
- ...a ? { body: JSON.stringify(n.body) } : {}
41
- }), o = await t.text(), s;
42
- if (o.length > 0) try {
43
- s = JSON.parse(o);
44
- } catch {
45
- s = o;
46
- }
47
- if (!t.ok) {
48
- let n = s, r = typeof n == "object" && n ? String(n.message ?? n.reason ?? n.error ?? `HTTP ${t.status}`) : `HTTP ${t.status}`, a = fe.has(i) && (process.env.VOLTRO_INSPECT_WRITE_TOKEN ?? "") === "" && /write/i.test(r);
49
- return {
50
- kind: "err",
51
- name: e.name,
52
- error: a ? `${r}\n VOLTRO_INSPECT_WRITE_TOKEN is not set in this shell. \`voltro dev\` mints it into the project's \`.env.local\`; export it (or run from a shell that loaded it).` : r
53
- };
54
- }
55
- return {
56
- kind: "ok",
57
- name: e.name,
58
- status: t.status,
59
- data: s
60
- };
61
- } catch (t) {
62
- return {
63
- kind: "err",
64
- name: e.name,
65
- error: t.message || String(t)
66
- };
67
- }
68
- }, P = (e, t = process.env) => {
69
- let n = [], r, i;
70
- for (let t = 0; t < e.length; t++) {
71
- let a = e[t];
72
- if (a === "--url") {
73
- r = e[++t];
74
- continue;
75
- }
76
- if (a.startsWith("--url=")) {
77
- r = a.slice(6);
78
- continue;
79
- }
80
- if (a === "--token") {
81
- i = e[++t];
82
- continue;
83
- }
84
- if (a.startsWith("--token=")) {
85
- i = a.slice(8);
86
- continue;
87
- }
88
- n.push(a);
89
- }
90
- if (r ??= t.VOLTRO_INSPECT_URL, r === void 0 || r === "") return {
91
- target: void 0,
92
- rest: n
93
- };
94
- let a;
95
- try {
96
- a = new URL(r);
97
- } catch {
98
- return {
99
- target: void 0,
100
- rest: n,
101
- error: `--url: not a valid URL: "${r}"`
102
- };
103
- }
104
- let o = i ?? t.VOLTRO_INSPECT_TOKEN;
105
- return {
106
- target: {
107
- url: r.replace(/\/+$/, ""),
108
- name: a.host,
109
- ...o !== void 0 && o !== "" ? { token: o } : {}
110
- },
111
- rest: n
112
- };
113
- }, pe = (e) => ({
114
- id: `remote:${e.url}`,
115
- name: e.name,
116
- kind: "api",
117
- url: e.url,
118
- pid: 0,
119
- registeredAt: (/* @__PURE__ */ new Date(0)).toISOString(),
120
- heartbeatAt: (/* @__PURE__ */ new Date(0)).toISOString(),
121
- status: "live",
122
- logFile: "",
123
- ...e.token === void 0 ? {} : { inspectToken: e.token }
124
- }), me = async (t, n) => n === void 0 ? (await e()).apps.filter((e) => e.kind === "api" && e.status === "live" && e.url.length > 0 && (t === void 0 || e.name === t)) : [pe(n)], he = (e) => {
125
- let t = "pretty", n = [];
126
- for (let r = 0; r < e.length; r++) {
127
- let i = e[r];
128
- if (i === "--format") {
129
- let n = e[++r];
130
- if (n !== "pretty" && n !== "json") return { error: "--format: must be 'pretty' | 'json'" };
131
- t = n;
132
- continue;
133
- }
134
- if (i === "--json" || i === "--format=json") {
135
- t = "json";
136
- continue;
137
- }
138
- if (i === "--format=pretty") {
139
- t = "pretty";
140
- continue;
141
- }
142
- n.push(i);
143
- }
144
- return {
145
- format: t,
146
- rest: n
147
- };
148
- }, ge = (e, t = process.env) => {
149
- let { target: n, rest: r, error: i } = P(e, t), a, o = [];
150
- for (let e = 0; e < r.length; e++) {
151
- let t = r[e];
152
- if (t === "--process") {
153
- a = r[++e];
154
- continue;
155
- }
156
- if (t.startsWith("--process=")) {
157
- a = t.slice(10);
158
- continue;
159
- }
160
- o.push(t);
161
- }
162
- return {
163
- process: a,
164
- target: n,
165
- rest: o,
166
- ...i === void 0 ? {} : { error: i }
167
- };
168
- }, _e = (e) => {
169
- let t = "", n = !1;
170
- for (let r = 0; r < e.length; r++) {
171
- let i = e[r];
172
- if (n) {
173
- if (t += i, i === "\\") {
174
- t += e[r + 1] ?? "", r++;
175
- continue;
176
- }
177
- i === "\"" && (n = !1);
178
- continue;
179
- }
180
- if (i === "\"") {
181
- n = !0, t += i;
182
- continue;
183
- }
184
- if (i === "/" && e[r + 1] === "*") {
185
- let t = e.indexOf("*/", r + 2);
186
- r = t === -1 ? e.length : t + 1;
187
- continue;
188
- }
189
- if (i === "/" && e[r + 1] === "/") {
190
- let t = e.indexOf("\n", r);
191
- r = t === -1 ? e.length : t - 1;
192
- continue;
193
- }
194
- t += i;
195
- }
196
- return JSON.parse(ve(t));
197
- }, ve = (e) => {
198
- let t = "", n = !1;
199
- for (let r = 0; r < e.length; r++) {
200
- let i = e[r];
201
- if (n) {
202
- if (t += i, i === "\\") {
203
- t += e[r + 1] ?? "", r++;
204
- continue;
205
- }
206
- i === "\"" && (n = !1);
207
- continue;
208
- }
209
- if (i === "\"") {
210
- n = !0, t += i;
211
- continue;
212
- }
213
- if (i === ",") {
214
- let t = e.slice(r + 1);
215
- if (/^\s*[}\]]/.exec(t)) continue;
216
- }
217
- t += i;
218
- }
219
- return t;
220
- }, F = (e, t) => {
221
- if (t.has(e) || !T(e)) return;
222
- t.add(e);
223
- let n;
224
- try {
225
- n = _e(E(e, "utf8"));
226
- } catch {
227
- return;
228
- }
229
- if (n.extends === void 0) return n;
230
- let r = n.extends;
231
- if (!r.startsWith(".")) return n;
232
- let i = F(C(b(e), r), t);
233
- return i === void 0 ? n : { compilerOptions: {
234
- ...i.compilerOptions,
235
- ...n.compilerOptions,
236
- paths: {
237
- ...i.compilerOptions?.paths,
238
- ...n.compilerOptions?.paths
239
- }
240
- } };
241
- }, ye = (e, t = "tsconfig.json") => {
242
- let n = ae(t) ? t : C(e, t), r = F(n, /* @__PURE__ */ new Set()), i = r?.compilerOptions?.paths;
243
- if (i === void 0) return;
244
- let a = {};
245
- for (let [e, t] of Object.entries(i)) Array.isArray(t) && (a[e] = t.filter((e) => typeof e == "string"));
246
- if (Object.keys(a).length !== 0) return {
247
- baseUrl: C(b(n), r?.compilerOptions?.baseUrl ?? "."),
248
- paths: a
249
- };
250
- }, I = (e, t = "tsconfig.json") => {
251
- let n = ae(t) ? t : C(e, t), r = F(n, /* @__PURE__ */ new Set()), i = r?.compilerOptions?.paths;
252
- if (i === void 0) return [];
253
- let a = C(b(n), r?.compilerOptions?.baseUrl ?? "."), o = [];
254
- for (let [e, t] of Object.entries(i)) {
255
- let n = t[0];
256
- if (typeof n != "string") continue;
257
- let r = e.endsWith("*");
258
- o.push({
259
- prefix: r ? e.slice(0, -1) : e,
260
- target: C(a, r ? n.replace(/\*$/, "") : n),
261
- wildcard: r
262
- });
263
- }
264
- return o.sort((e, t) => t.prefix.length - e.prefix.length);
265
- }, be = (e, t) => {
266
- for (let n of t) if (n.wildcard) {
267
- if (e.startsWith(n.prefix)) return x(n.target, e.slice(n.prefix.length));
268
- } else if (e === n.prefix) return n.target;
269
- }, L = ".voltro-vendored", xe = (e, t) => {
270
- let n = [], r = /* @__PURE__ */ new Set(), i = (e, t) => {
271
- let i = C(e);
272
- r.has(i) || !T(i) || (r.add(i), n.push({
273
- dir: i,
274
- source: t
275
- }));
276
- }, a = x(e, "components.json");
277
- if (T(a)) try {
278
- let t = JSON.parse(E(a, "utf8"));
279
- for (let n of ["ui"]) {
280
- let r = t.aliases?.[n];
281
- if (typeof r != "string" || r === "") continue;
282
- let a = r.replace(/^@\//, "").replace(/^~\//, "");
283
- for (let t of [x(e, a), x(e, "src", a)]) if (T(t)) {
284
- i(t, `components.json (aliases.${n})`);
285
- break;
286
- }
287
- }
288
- } catch {}
289
- for (let e of t) {
290
- let t = e.slice(0, e.lastIndexOf(w)), n = x(t, L);
291
- if (!r.has(C(t)) && T(n)) {
292
- let e = "";
293
- try {
294
- e = E(n, "utf8").trim().split("\n")[0] ?? "";
295
- } catch {}
296
- i(t, e === "" ? L : `${L}: ${e}`);
297
- }
298
- }
299
- return n;
300
- }, Se = (e, t) => t.some((t) => e === t.dir || e.startsWith(`${t.dir}${w}`)), Ce = (e, t) => t.map((t) => `${S(e, t.dir) || "."} (${t.source})`), we = [
301
- {
302
- id: "component.ui",
303
- re: _,
304
- needsTest: !0
305
- },
306
- {
307
- id: "component",
308
- re: ee,
309
- needsTest: !0
310
- },
311
- {
312
- id: "hook",
313
- re: te,
314
- needsTest: !0
315
- },
316
- {
317
- id: "tracking",
318
- re: f,
319
- needsTest: !0
320
- },
321
- {
322
- id: "store",
323
- re: d,
324
- needsTest: !0
325
- },
326
- {
327
- id: "internal",
328
- re: h,
329
- needsTest: !0
330
- },
331
- {
332
- id: "fixture",
333
- re: v,
334
- needsTest: !1
335
- },
336
- {
337
- id: "types",
338
- re: p,
339
- needsTest: !1
340
- }
341
- ], R = (e) => we.find((t) => t.re.test(e))?.id ?? null, Te = (e) => m.test(e), Ee = /* @__PURE__ */ new Set([
342
- "useMutation",
343
- "useAction",
344
- "useUpload",
345
- "useUndo",
346
- "useOutbox",
347
- "useAgentChat",
348
- "useAgentStream",
349
- "useResumableAgentStream",
350
- "useWorkflow"
351
- ]), De = /* @__PURE__ */ new Set(["defineTracking"]), Oe = /* @__PURE__ */ new Set([
352
- "useSubscription",
353
- "useWindowedSubscription",
354
- "useRecord",
355
- "useQuery"
356
- ]), ke = /* @__PURE__ */ new Set([
357
- "node_modules",
358
- "dist",
359
- "build",
360
- ".framework",
361
- ".turbo",
362
- ".vite",
363
- ".next",
364
- ".git",
365
- "coverage",
366
- ".publish"
367
- ]), Ae = /\.tsx?$/, je = (e, t = [], n = 0) => {
368
- if (n > 12) return t;
369
- let r;
370
- try {
371
- r = D(e);
372
- } catch {
373
- return t;
374
- }
375
- for (let i of r) {
376
- if (ke.has(i)) continue;
377
- let r = x(e, i), a;
378
- try {
379
- a = O(r);
380
- } catch {
381
- continue;
382
- }
383
- a.isDirectory() ? je(r, t, n + 1) : a.isFile() && Ae.test(i) && !i.endsWith(".d.ts") && t.push(r);
384
- }
385
- return t;
386
- }, Me = /\b(?:FC|FunctionComponent|ComponentType|ForwardRefExoticComponent|MemoExoticComponent)\b/, Ne = /\b(?:memo|forwardRef|observer|styled)\b/, Pe = (e) => k.isArrowFunction(e) || k.isFunctionExpression(e) || k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isClassExpression(e), z = (e) => {
387
- if (Pe(e)) return !0;
388
- if (k.isExportAssignment(e) || k.isAsExpression(e) || k.isSatisfiesExpression(e) || k.isParenthesizedExpression(e)) return z(e.getExpression());
389
- if (k.isVariableDeclaration(e)) {
390
- let t = e.getTypeNode();
391
- if (t !== void 0 && Me.test(t.getText())) return !0;
392
- let n = e.getInitializer();
393
- return n === void 0 || z(n);
394
- }
395
- return k.isCallExpression(e) ? Ne.test(e.getExpression().getText()) ? !0 : e.getArguments().some((e) => Pe(e) || k.isIdentifier(e)) : k.isTaggedTemplateExpression(e) ? !0 : !(k.isObjectLiteralExpression(e) || k.isArrayLiteralExpression(e) || k.isStringLiteral(e) || k.isNoSubstitutionTemplateLiteral(e) || k.isNumericLiteral(e) || k.isTemplateExpression(e) || k.isNewExpression(e) || k.isPropertyAccessExpression(e) || e.getKind() === j.TrueKeyword || e.getKind() === j.FalseKeyword || e.getKind() === j.NullKeyword);
396
- }, Fe = (e) => {
397
- let t = [], n = [], r = [], i = /* @__PURE__ */ new Set();
398
- for (let [a, o] of e.getExportedDeclarations()) {
399
- let e = o[0];
400
- if (e === void 0 || k.isInterfaceDeclaration(e) || k.isTypeAliasDeclaration(e) || i.has(e)) continue;
401
- i.add(e);
402
- let s = a === "default" ? (k.isFunctionDeclaration(e) || k.isClassDeclaration(e) || k.isVariableDeclaration(e) ? e.getName() : void 0) ?? "Default" : a;
403
- if (/^use[A-Z]/.test(s)) {
404
- n.push(s);
405
- continue;
406
- }
407
- if (/^[A-Z]/.test(s) && z(e)) {
408
- t.push(s);
409
- continue;
410
- }
411
- r.push(s);
412
- }
413
- return {
414
- components: t,
415
- hooks: n,
416
- others: r,
417
- hasRuntimeExport: t.length + n.length + r.length > 0
418
- };
419
- }, B = [], Ie = (e, t, n) => {
420
- let r = b(e), i = [];
421
- for (let e = r;; e = b(e)) {
422
- let r = n.get(e);
423
- if (r !== void 0) {
424
- for (let e of i) n.set(e, r);
425
- return r;
426
- }
427
- if (i.push(e), T(x(e, "tsconfig.json"))) {
428
- let t = I(e);
429
- if (t.length > 0) {
430
- for (let e of i) n.set(e, t);
431
- return t;
432
- }
433
- }
434
- if (e === t || b(e) === e) break;
435
- }
436
- for (let e of i) n.set(e, B);
437
- return B;
438
- }, Le = (e, t, n) => {
439
- let r = t.startsWith(".") ? C(b(e), t) : be(t, n);
440
- if (r === void 0) return null;
441
- for (let e of [
442
- r,
443
- `${r}.tsx`,
444
- `${r}.ts`,
445
- x(r, "index.tsx"),
446
- x(r, "index.ts")
447
- ]) try {
448
- if (O(e).isFile()) return e;
449
- } catch {}
450
- return null;
451
- }, Re = (e, t, n) => {
452
- let r = [], i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Map(), o = (e) => {
453
- let i = Le(t, e, n);
454
- return i !== null && r.push(i), i;
455
- };
456
- for (let t of e.getImportDeclarations()) if (o(t.getModuleSpecifierValue()), !t.isTypeOnly()) for (let e of t.getNamedImports()) e.isTypeOnly() || i.add(e.getName());
457
- for (let t of e.getExportDeclarations()) {
458
- let e = t.getModuleSpecifierValue();
459
- if (e === void 0) continue;
460
- let n = o(e);
461
- if (n === null || t.isTypeOnly()) continue;
462
- let r = t.getNamedExports();
463
- if (r.length === 0) {
464
- a.set(n, "*");
465
- continue;
466
- }
467
- let i = r.filter((e) => !e.isTypeOnly()).map((e) => e.getAliasNode()?.getText() ?? e.getName()), s = a.get(n);
468
- a.set(n, s === "*" ? "*" : [...s ?? [], ...i]);
469
- }
470
- for (let t of e.getDescendantsOfKind(j.CallExpression)) {
471
- if (t.getExpression().getKind() !== j.ImportKeyword) continue;
472
- let e = t.getArguments()[0];
473
- e !== void 0 && k.isStringLiteral(e) && o(e.getLiteralValue());
474
- }
475
- return {
476
- localTargets: r,
477
- namedBindings: i,
478
- reExports: a
479
- };
480
- }, ze = (e) => {
481
- let t = /* @__PURE__ */ new Map();
482
- for (let n of e.getDescendantsOfKind(j.CallExpression)) {
483
- let e = n.getExpression(), r = k.isPropertyAccessExpression(e) ? e.getName() : e.getText();
484
- t.set(r, (t.get(r) ?? 0) + 1);
485
- }
486
- return t;
487
- }, Be = (e) => {
488
- if (e.length <= 1) return e.length === 1;
489
- let t = [...e].sort((e, t) => e.length - t.length)[0];
490
- return e.every((e) => e.startsWith(t));
491
- }, Ve = (e, t) => {
492
- let n = b(t);
493
- return e.startsWith(`${n}${w}`) || b(e) === n;
494
- }, V = (e) => {
495
- let t = je(e);
496
- if (t.length === 0) return {
497
- findings: [],
498
- declared: 0,
499
- scanned: 0,
500
- vendored: []
501
- };
502
- let n = xe(e, t), r = t.filter((e) => !Se(e, n));
503
- if (r.length === 0) return {
504
- findings: [],
505
- declared: 0,
506
- scanned: 0,
507
- vendored: n
508
- };
509
- let i = new A({
510
- useInMemoryFileSystem: !0,
511
- skipAddingFilesFromTsConfig: !0
512
- }), a = [], o = (t) => S(e, t), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
513
- for (let t of r) {
514
- let n;
515
- try {
516
- n = E(t, "utf8");
517
- } catch {
518
- continue;
519
- }
520
- let r;
521
- try {
522
- r = i.createSourceFile(t.endsWith("x") ? "/scan.tsx" : "/scan.ts", n, { overwrite: !0 });
523
- } catch {
524
- continue;
525
- }
526
- s.set(t, Fe(r)), c.set(t, Re(r, t, Ie(t, e, u))), l.set(t, ze(r));
527
- }
528
- let d = /* @__PURE__ */ new Map();
529
- for (let [e, t] of c) for (let n of t.localTargets) {
530
- let t = d.get(n) ?? [];
531
- t.push(e), d.set(n, t);
532
- }
533
- let f = (e) => {
534
- let t = s.get(e), n = /* @__PURE__ */ new Set([
535
- ...t?.components ?? [],
536
- ...t?.hooks ?? [],
537
- ...t?.others ?? []
538
- ]), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set([e]), a = (e, t) => {
539
- for (let o of d.get(e) ?? []) {
540
- if (i.has(o)) continue;
541
- let s = c.get(o);
542
- if (s === void 0) continue;
543
- let l = s.reExports.get(e);
544
- if (l !== void 0) {
545
- i.add(o), r.add(o);
546
- let e = l === "*" ? t ?? n : new Set(l), s = t === null ? e : new Set([...e].filter((e) => t.has(e)));
547
- s.size > 0 && a(o, s);
548
- continue;
549
- }
550
- (t === null || [...s.namedBindings].some((e) => t.has(e))) && r.add(o);
551
- }
552
- };
553
- return a(e, null), [...r];
554
- }, p = 0;
555
- for (let e of r) {
556
- let t = R(e), n = s.get(e), r = c.get(e);
557
- if (n === void 0 || r === void 0) continue;
558
- let i = Te(e);
559
- t !== null && !i && (p += 1);
560
- let u = d.get(e) ?? [];
561
- if ((t === "component" || t === "component.ui") && !i) {
562
- let t = Be(n.components);
563
- (n.components.length === 0 || !t) && a.push({
564
- rule: "component/one-per-file",
565
- file: o(e),
566
- severity: "error",
567
- detail: n.components.length === 0 ? "declares a component file but exports no component" : `exports ${n.components.length} unrelated components: ${n.components.join(", ")}`,
568
- fix: "a component file exports ONE component — or one NAMESPACED family sharing its name (`Card`, `CardHeader`, `CardTitle`), which is one component with parts. Types alongside are fine. Unrelated components belong in their own files"
569
- }), n.hooks.length > 0 && a.push({
570
- rule: "component/no-hook-export",
571
- file: o(e),
572
- severity: "error",
573
- detail: `also exports hook(s): ${n.hooks.join(", ")}`,
574
- fix: "move the hook into its own *.hook.ts — a file exporting both is two files wearing one name, and neither can be reused without dragging the other"
575
- });
576
- }
577
- if (t === "hook" && !i && (n.hooks.length !== 1 && a.push({
578
- rule: "hook/one-per-file",
579
- file: o(e),
580
- severity: "error",
581
- detail: n.hooks.length === 0 ? "declares a hook file but exports no use* hook" : `exports ${n.hooks.length} hooks: ${n.hooks.join(", ")}`,
582
- fix: "a hook file exports exactly ONE use* hook (types alongside are fine)"
583
- }), n.components.length > 0 && a.push({
584
- rule: "hook/no-component-export",
585
- file: o(e),
586
- severity: "error",
587
- detail: `also exports component(s): ${n.components.join(", ")}`,
588
- fix: "move the component into its own *.component.tsx"
589
- })), t === "types" && n.hasRuntimeExport && a.push({
590
- rule: "types/runtime-export",
591
- file: o(e),
592
- severity: "error",
593
- detail: `exports runtime value(s): ${[
594
- ...n.components,
595
- ...n.hooks,
596
- ...n.others
597
- ].join(", ")}`,
598
- fix: "a *.types.ts has NO runtime export. That is what makes importing it provably free AND makes it impossible for it to sit in a runtime import cycle — the guarantee is the point, so move the value to a sibling module"
599
- }), t === "internal") {
600
- let t = u.filter((t) => !Ve(t, e));
601
- t.length > 0 && a.push({
602
- rule: "internal/foreign-import",
603
- file: o(e),
604
- severity: "error",
605
- detail: `imported from outside its own directory by: ${t.slice(0, 5).map(o).join(", ")}${t.length > 5 ? ` (+${t.length - 5})` : ""}`,
606
- fix: "export it through the feature's public module, or move the importer inside. `.internal` is the promise that refactoring within this directory breaks nobody — a foreign import revokes it silently, which is how a feature boundary rots without a single review comment"
607
- });
608
- }
609
- if (t === "fixture") {
610
- let t = u.filter((e) => !Te(e) && R(e) !== "fixture");
611
- t.length > 0 && a.push({
612
- rule: "fixture/production-import",
613
- file: o(e),
614
- severity: "error",
615
- detail: `imported by non-test file(s): ${t.slice(0, 5).map(o).join(", ")}`,
616
- fix: "test material must not be reachable from a production path — it ships in the bundle, and it is written to be convenient rather than correct"
617
- });
618
- }
619
- if (t === "component.ui" && !i) {
620
- let t = [...r.namedBindings].filter((e) => Ee.has(e)).sort();
621
- t.length > 0 && a.push({
622
- rule: "ui/no-write",
623
- file: o(e),
624
- severity: "error",
625
- detail: `imports write hook(s): ${t.join(", ")}`,
626
- fix: "a *.component.ui.tsx renders; it does not write. Lift the mutation into the *.component.tsx that owns it and pass a handler down. Reading (useT / useCan / usePermissions) stays allowed — threading those through props is prop-drilling, not reusability"
627
- });
628
- let n = f(e), i = n.filter((e) => {
629
- let t = R(e);
630
- return t === "component" || t === "component.ui" || y.test(e) || g.has(e.slice(e.lastIndexOf(w) + 1));
631
- });
632
- n.length === 0 ? a.push({
633
- rule: "ui/orphaned",
634
- file: o(e),
635
- severity: "warn",
636
- detail: "nothing imports it",
637
- fix: "render it from a *.component.tsx or a page, or delete it — an unrendered presentational component is carried, reviewed and refactored forever without reaching a user"
638
- }) : i.length === 0 && a.push({
639
- rule: "ui/unlinked",
640
- file: o(e),
641
- severity: "warn",
642
- detail: `reached only by: ${n.slice(0, 5).map(o).join(", ")}${n.length > 5 ? ` (+${n.length - 5})` : ""} — barrels resolved through`,
643
- fix: "a presentational component should be reached from a *.component.tsx, another *.component.ui.tsx, or a page"
644
- });
645
- }
646
- if (t === "store" && !i) {
647
- let t = l.get(e)?.get("defineStore") ?? 0;
648
- t !== 1 && a.push({
649
- rule: "store/one-per-file",
650
- file: o(e),
651
- severity: "error",
652
- detail: t === 0 ? "declares a store file but calls no defineStore()" : `declares ${t} stores`,
653
- fix: "a store file defines exactly ONE store — the SSR seed pass and devtools address stores by name, so two in a file makes both harder to find and neither easier to reason about"
654
- });
655
- let n = [...l.get(e)?.keys() ?? []].filter((e) => Oe.has(e)).sort();
656
- n.length > 0 && a.push({
657
- rule: "store/mirrors-server-state",
658
- file: o(e),
659
- severity: "error",
660
- detail: `reads server state here: ${n.join(", ")}`,
661
- fix: "a store holds CLIENT state. A subscription is already live server state; copying it into a store gives you a second copy that does not live, and the page then shows the stale one — a bug that reads as \"reactivity is broken\". Read the subscription where you render it"
662
- });
663
- }
664
- if (t !== "tracking" && !i) {
665
- let t = [...l.get(e)?.keys() ?? []].filter((e) => De.has(e)).sort();
666
- t.length > 0 && a.push({
667
- rule: "tracking/outside-tracking-file",
668
- file: o(e),
669
- severity: "error",
670
- detail: `calls ${t.join(", ")} outside a *.tracking.ts`,
671
- fix: "move the defineTracking(...) spec into a *.tracking.ts and import it here. The event catalogue — names, property bags, the decisions about what personal data goes out — belongs in one place you can list; calling useTracking with a spec from there is fine"
672
- });
673
- }
674
- if (we.find((e) => e.id === t)?.needsTest === !0 && !i) {
675
- let t = e.replace(/\.tsx?$/, ""), n = e.endsWith("x") ? "tsx" : "ts";
676
- ["test", "spec"].flatMap((e) => [`${t}.${e}.ts`, `${t}.${e}.tsx`]).some((e) => {
677
- try {
678
- return O(e).isFile();
679
- } catch {
680
- return !1;
681
- }
682
- }) || a.push({
683
- rule: "convention/missing-test",
684
- file: o(e),
685
- severity: "warn",
686
- detail: "no test beside it",
687
- fix: `add ${o(t)}.test.${n} — the name is derivable, so write it with the file. A shallow test earns its place: what it buys is that something MOUNTS the component, and a render loop, a crashing effect or a missing provider is invisible until then. One app wrote 251 of these and the first run found a render loop that made a page take 423 s and exhaust the heap — in a browser it had looked usable`
688
- });
689
- }
690
- }
691
- return a.sort((e, t) => e.file === t.file ? e.rule.localeCompare(t.rule) : e.file.localeCompare(t.file)), {
692
- findings: a,
693
- declared: p,
694
- scanned: r.length,
695
- vendored: n
696
- };
697
- }, He = 10, Ue = (e) => {
698
- let { findings: t, declared: n, scanned: r, vendored: i } = V(e);
699
- console.log("");
700
- for (let t of Ce(e, i)) console.log(`· not ours, exempt: ${t}`);
701
- if (r === 0) return console.log("• file-taxonomy scan: no sources here — nothing to check."), 0;
702
- if (t.length === 0) return console.log(`✓ file-taxonomy scan: ${n} declared file(s) keep their contract (${r} scanned).`), 0;
703
- let a = /* @__PURE__ */ new Map();
704
- for (let e of t) {
705
- let t = a.get(e.rule) ?? [];
706
- t.push(e), a.set(e.rule, t);
707
- }
708
- let o = t.filter((e) => e.severity === "error").length;
709
- console.log(`• file taxonomy: ${t.length} finding(s) — ${o} error, ${t.length - o} advisory (${n} declared / ${r} scanned):`);
710
- for (let [e, t] of [...a].sort()) {
711
- console.log(` ${t[0].severity === "error" ? "✗" : "·"} ${e} (${t.length})`);
712
- for (let e of t.slice(0, He)) console.log(` ${e.file} — ${e.detail}`);
713
- let n = t.length - He;
714
- n > 0 && console.log(` (+${n} more — \`voltro doctor --json\` prints every one.)`), console.log(` → ${t[0].fix}`);
715
- }
716
- return t.length;
717
- }, We = /* @__PURE__ */ new Set([
1
+ import { Ft as e, Nt as t, Pt as n } from "./dev-CerMd0mW.js";
2
+ import { $ as r, Q as i, X as a, Z as o, s, t as c, y as l } from "./webDev-CBYvPqQr.js";
3
+ import { w as u } from "./frameworkTableAssembly-DkkP6BgC.js";
4
+ import { n as d } from "./pageConvention-cEiRxdab.js";
5
+ import { r as f, u as p } from "./apiBuild-DZF_f0_I.js";
6
+ import { n as m, r as h, t as g } from "./cliOutput-D1tSBoRM.js";
7
+ import { n as ee, t as te } from "./tsconfigPaths-BWXBWgcl.js";
8
+ import { n as ne, r as re } from "./fileTaxonomy-CJfgOllU.js";
9
+ import { basename as ie, dirname as ae, join as _, relative as v, resolve as y, sep as b } from "node:path";
10
+ import { existsSync as x, readFileSync as S, readdirSync as C, statSync as w, writeFileSync as oe } from "node:fs";
11
+ import { pathToFileURL as se } from "node:url";
12
+ import { createLogger as ce } from "@voltro/logger";
13
+ import { Node as T, Project as E, SyntaxKind as D } from "ts-morph";
14
+ //#region src/predicateColumnAudit.ts
15
+ var le = /* @__PURE__ */ new Set([
718
16
  "eq",
719
17
  "ne",
720
18
  "gt",
@@ -732,43 +30,43 @@ var le = () => {
732
30
  "endsWith",
733
31
  "between",
734
32
  "jsonField"
735
- ]), Ge = (e) => {
736
- if (k.isPropertyAccessExpression(e)) return e.getName();
737
- if (k.isIdentifier(e)) return e.getText();
738
- }, Ke = (e) => {
33
+ ]), ue = (e) => {
34
+ if (T.isPropertyAccessExpression(e)) return e.getName();
35
+ if (T.isIdentifier(e)) return e.getText();
36
+ }, de = (e) => {
739
37
  let t = e;
740
- for (; k.isCallExpression(t);) {
38
+ for (; T.isCallExpression(t);) {
741
39
  let e = t.getExpression();
742
- if (!k.isPropertyAccessExpression(e)) break;
40
+ if (!T.isPropertyAccessExpression(e)) break;
743
41
  t = e.getExpression();
744
42
  }
745
43
  return t;
746
- }, qe = (e, t, n) => {
747
- let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new A({
44
+ }, fe = (e, t, n) => {
45
+ let r = new Map(t.map((e) => [e.name, new Set(e.columns)])), i = new Map(t.map((e) => [e.name, e.columns])), a = [], o = 0, s = new E({
748
46
  useInMemoryFileSystem: !0,
749
47
  skipAddingFilesFromTsConfig: !0
750
48
  });
751
49
  for (let t of e) {
752
50
  let e;
753
51
  try {
754
- let n = E(t, "utf8");
52
+ let n = S(t, "utf8");
755
53
  if (!n.includes(".where(") && !n.includes(".select(")) continue;
756
54
  e = s.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
757
55
  } catch {
758
56
  continue;
759
57
  }
760
- let c = S(n, t);
761
- for (let t of e.getDescendantsOfKind(j.CallExpression)) {
58
+ let c = v(n, t);
59
+ for (let t of e.getDescendantsOfKind(D.CallExpression)) {
762
60
  let e = t.getExpression();
763
- if (!k.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
764
- let n = Ge(Ke(e.getExpression()));
61
+ if (!T.isPropertyAccessExpression(e) || e.getName() !== "where") continue;
62
+ let n = ue(de(e.getExpression()));
765
63
  if (n === void 0 || !r.has(n)) {
766
64
  let t = e.getExpression();
767
- if (k.isCallExpression(t)) {
65
+ if (T.isCallExpression(t)) {
768
66
  let e = t.getExpression();
769
- if (k.isPropertyAccessExpression(e) && e.getName() === "select") {
67
+ if (T.isPropertyAccessExpression(e) && e.getName() === "select") {
770
68
  let e = t.getArguments()[0];
771
- e !== void 0 && k.isStringLiteral(e) && (n = e.getLiteralValue());
69
+ e !== void 0 && T.isStringLiteral(e) && (n = e.getLiteralValue());
772
70
  }
773
71
  }
774
72
  }
@@ -777,16 +75,16 @@ var le = () => {
777
75
  if (s === void 0) continue;
778
76
  let l = [], [u] = t.getArguments();
779
77
  if (u !== void 0) {
780
- if (k.isStringLiteral(u)) l.push({
78
+ if (T.isStringLiteral(u)) l.push({
781
79
  column: u.getLiteralValue(),
782
80
  line: u.getStartLineNumber()
783
81
  });
784
- else for (let e of [u, ...u.getDescendantsOfKind(j.CallExpression)]) {
785
- if (!k.isCallExpression(e)) continue;
786
- let t = e.getExpression(), n = k.isIdentifier(t) ? t.getText() : k.isPropertyAccessExpression(t) ? t.getName() : void 0;
787
- if (n === void 0 || !We.has(n)) continue;
82
+ else for (let e of [u, ...u.getDescendantsOfKind(D.CallExpression)]) {
83
+ if (!T.isCallExpression(e)) continue;
84
+ let t = e.getExpression(), n = T.isIdentifier(t) ? t.getText() : T.isPropertyAccessExpression(t) ? t.getName() : void 0;
85
+ if (n === void 0 || !le.has(n)) continue;
788
86
  let r = e.getArguments()[0];
789
- r !== void 0 && k.isStringLiteral(r) && l.push({
87
+ r !== void 0 && T.isStringLiteral(r) && l.push({
790
88
  column: r.getLiteralValue(),
791
89
  line: r.getStartLineNumber()
792
90
  });
@@ -805,16 +103,16 @@ var le = () => {
805
103
  issues: a,
806
104
  checked: o
807
105
  };
808
- }, H = 12, Je = (e) => {
106
+ }, O = 12, pe = (e) => {
809
107
  if (e.checked === 0) return null;
810
108
  if (e.issues.length === 0) return `predicate columns: ${e.checked} checked — every filtered column exists on its table`;
811
109
  let t = [`predicate columns: ${e.issues.length} filter${e.issues.length, "s"} on a column that does not exist (${e.checked} checked)`];
812
110
  for (let n of e.issues) {
813
- let e = n.available.slice(0, H).join(", "), r = n.available.length > H ? `, … (${n.available.length - H} more)` : "";
111
+ let e = n.available.slice(0, O).join(", "), r = n.available.length > O ? `, … (${n.available.length - O} more)` : "";
814
112
  t.push(` ${n.file}:${n.line} '${n.table}' has no column '${n.column}'`), t.push(` columns: ${e}${r}`);
815
113
  }
816
114
  return t.push(" This type-checks today and fails at runtime as a bare SQL error."), t.join("\n");
817
- }, Ye = /* @__PURE__ */ new Set([
115
+ }, me = /* @__PURE__ */ new Set([
818
116
  "id",
819
117
  "tenantId",
820
118
  "tenant_id",
@@ -834,7 +132,7 @@ var le = () => {
834
132
  "idempotency_id",
835
133
  "idempotencyKey",
836
134
  "idempotency_key"
837
- ]), Xe = /* @__PURE__ */ new Set([
135
+ ]), he = /* @__PURE__ */ new Set([
838
136
  "id",
839
137
  "createdAt",
840
138
  "created_at",
@@ -844,10 +142,10 @@ var le = () => {
844
142
  "deleted_at",
845
143
  "tenantId",
846
144
  "tenant_id"
847
- ]), Ze = (e) => Ye.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), Qe = (e) => e.type === "reference", $e = (e) => {
145
+ ]), ge = (e) => me.has(e) ? !1 : /[a-z0-9]Id$/.test(e) || /_id$/.test(e), _e = (e) => e.type === "reference", ve = (e) => {
848
146
  let t = [];
849
147
  for (let n of e) {
850
- let e = n.columns.filter((e) => Ze(e.name)), r = e.filter((e) => !Qe(e)).map((e) => e.name);
148
+ let e = n.columns.filter((e) => ge(e.name)), r = e.filter((e) => !_e(e)).map((e) => e.name);
851
149
  if (r.length === 0) continue;
852
150
  let i = new Set(n.primaryKey), a = r.filter((e) => i.has(e));
853
151
  if (n.primaryKey.length >= 2 && a.length > 0) {
@@ -858,7 +156,7 @@ var le = () => {
858
156
  });
859
157
  continue;
860
158
  }
861
- if (e.some(Qe) && e.length >= 2) {
159
+ if (e.some(_e) && e.length >= 2) {
862
160
  t.push({
863
161
  table: n.name,
864
162
  columns: r,
@@ -866,7 +164,7 @@ var le = () => {
866
164
  });
867
165
  continue;
868
166
  }
869
- e.length >= 2 && n.columns.filter((e) => !Ze(e.name) && !Xe.has(e.name)).length === 0 && t.push({
167
+ e.length >= 2 && n.columns.filter((e) => !ge(e.name) && !he.has(e.name)).length === 0 && t.push({
870
168
  table: n.name,
871
169
  columns: r,
872
170
  signal: "link-only"
@@ -876,20 +174,20 @@ var le = () => {
876
174
  issues: t,
877
175
  tablesChecked: e.length
878
176
  };
879
- }, et = {
177
+ }, ye = {
880
178
  "composite-pk": "part of a composite primary key",
881
179
  "reference-sibling": "sits beside a wired reference() on this table",
882
180
  "link-only": "this table is nothing but link columns"
883
- }, tt = (e) => {
181
+ }, be = (e) => {
884
182
  if (e.tablesChecked === 0) return null;
885
183
  if (e.issues.length === 0) return `junction FKs: ${e.tablesChecked} table${e.tablesChecked === 1 ? "" : "s"} checked — every link column is a wired reference()`;
886
184
  let t = e.issues.length, n = [`junction FKs: ${t} junction table${t === 1 ? "" : "s"} with an id column that is a plain text() and not a reference()`];
887
185
  for (let t of e.issues) {
888
186
  let e = t.columns.map((e) => `'${e}'`).join(", ");
889
- n.push(` '${t.table}': ${e} (${et[t.signal]})`);
187
+ n.push(` '${t.table}': ${e} (${ye[t.signal]})`);
890
188
  }
891
189
  return n.push(" Declare each as reference(() => <table>): the FK is enforced, the column is auto-indexed,"), n.push(" and the relationship becomes walkable (a plain text() id column is an invisible edge)."), n.join("\n");
892
- }, nt = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), rt = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), it = (e) => rt(e).some((e) => nt.has(e)), at = (e) => {
190
+ }, xe = /* @__PURE__ */ new Set(/* @__PURE__ */ "position.positions.move.moved.moving.cursor.pointer.mouse.drag.dragging.hover.frame.frames.tick.ticks.ping.heartbeat.typing.scroll.scrolling.zoom.pan.coord.coords.coordinate.coordinates.telemetry.sensor.sample.samples.viewport".split(".")), Se = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter((e) => e.length > 0).map((e) => e.toLowerCase()), Ce = (e) => Se(e).some((e) => xe.has(e)), we = (e) => {
893
191
  let t = [], n = [];
894
192
  for (let r of e) t.push({
895
193
  event: r.name,
@@ -898,7 +196,7 @@ var le = () => {
898
196
  event: r.name,
899
197
  kind: "wide-key",
900
198
  keyFieldCount: r.keyFieldCount
901
- }), r.hasWebhook && it(r.name) && n.push({
199
+ }), r.hasWebhook && Ce(r.name) && n.push({
902
200
  event: r.name,
903
201
  kind: "webhook-high-rate"
904
202
  });
@@ -907,45 +205,45 @@ var le = () => {
907
205
  modes: t,
908
206
  warnings: n
909
207
  };
910
- }, ot = (e) => {
208
+ }, Te = (e) => {
911
209
  if (e.eventsChecked === 0) return null;
912
210
  let t = e.eventsChecked, n = [`event delivery: ${t} declared event${t === 1 ? "" : "s"} — the mode decides what a MISSING message means`];
913
211
  for (let t of e.modes) n.push(` '${t.event}': ${t.delivery}`);
914
212
  return n.push(" each — every delivery matters; a slow subscriber loses the oldest and is TOLD how many (the default)."), n.push(" latest — a newer delivery supersedes a pending one; a slow subscriber gets the current value, told nothing."), n.push(" Two events with identical route/subscriber/buffer numbers mean OPPOSITE things about a drop — the mode is the difference."), n.join("\n");
915
- }, st = (e) => {
213
+ }, Ee = (e) => {
916
214
  if (e.warnings.length === 0) return null;
917
215
  let t = e.warnings.length, n = [`events that may not scale as declared: ${t} finding${t === 1 ? "" : "s"}`];
918
216
  for (let t of e.warnings) t.kind === "wide-key" ? (n.push(` '${t.event}': routing key has ${t.keyFieldCount ?? 0} fields — every field FRAGMENTS the subscriber set (distinct routes are the PRODUCT of the fields’ value spaces).`), n.push(" Check each is an ADDRESS the delivery is decided by (arenaId), not a discriminator the handler reads (gameType) — the latter is payload, not key.")) : (n.push(` '${t.event}': a webhook block on a per-frame-looking event — every publish is N HTTP deliveries per second per subscribed target.`), n.push(" The webhook rate limit DEFERS the excess as pending rows rather than failing, so the symptom is a growing table. Publish a coarser event (a summary / state change) for the outside world."));
919
217
  return n.push(" WARNING, not a fault — a producer may intend the fan-out. `voltro doctor --json` prints every finding."), n.join("\n");
920
- }, ct = /* @__PURE__ */ new Set([
218
+ }, De = /* @__PURE__ */ new Set([
921
219
  "start",
922
220
  "run",
923
221
  "child"
924
- ]), lt = (e) => k.isCallExpression(e) && (() => {
222
+ ]), Oe = (e) => T.isCallExpression(e) && (() => {
925
223
  let t = e.getExpression();
926
- if (!k.isPropertyAccessExpression(t) || !ct.has(t.getName())) return !1;
224
+ if (!T.isPropertyAccessExpression(t) || !De.has(t.getName())) return !1;
927
225
  let n = t.getExpression();
928
- return k.isPropertyAccessExpression(n) ? n.getName() === "workflows" : k.isIdentifier(n) && n.getText() === "workflows";
929
- })(), ut = (e, t, n) => {
930
- let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new A({
226
+ return T.isPropertyAccessExpression(n) ? n.getName() === "workflows" : T.isIdentifier(n) && n.getText() === "workflows";
227
+ })(), ke = (e, t, n) => {
228
+ let r = new Map(t.map((e) => [e.name, e])), i = [...r.keys()].sort(), a = [], o = [], s = 0, c = 0, l = new E({
931
229
  useInMemoryFileSystem: !0,
932
230
  skipAddingFilesFromTsConfig: !0
933
231
  });
934
232
  for (let t of e) {
935
233
  let e;
936
234
  try {
937
- let n = E(t, "utf8");
235
+ let n = S(t, "utf8");
938
236
  if (!n.includes("workflows.")) continue;
939
237
  e = l.createSourceFile(`/scan${t.endsWith("x") ? ".tsx" : ".ts"}`, n, { overwrite: !0 });
940
238
  } catch {
941
239
  continue;
942
240
  }
943
- let u = S(n, t);
944
- for (let t of e.getDescendantsOfKind(j.CallExpression)) {
945
- if (!lt(t)) continue;
241
+ let u = v(n, t);
242
+ for (let t of e.getDescendantsOfKind(D.CallExpression)) {
243
+ if (!Oe(t)) continue;
946
244
  c++;
947
245
  let e = t.getStartLineNumber(), [n, l] = t.getArguments();
948
- if (n === void 0 || !k.isStringLiteral(n)) {
246
+ if (n === void 0 || !T.isStringLiteral(n)) {
949
247
  o.push({
950
248
  file: u,
951
249
  line: e,
@@ -964,7 +262,7 @@ var le = () => {
964
262
  });
965
263
  continue;
966
264
  }
967
- if (l === void 0 || !k.isObjectLiteralExpression(l)) {
265
+ if (l === void 0 || !T.isObjectLiteralExpression(l)) {
968
266
  o.push({
969
267
  file: u,
970
268
  line: e,
@@ -972,7 +270,7 @@ var le = () => {
972
270
  });
973
271
  continue;
974
272
  }
975
- if (l.getProperties().some((e) => k.isSpreadAssignment(e))) {
273
+ if (l.getProperties().some((e) => T.isSpreadAssignment(e))) {
976
274
  o.push({
977
275
  file: u,
978
276
  line: e,
@@ -981,14 +279,14 @@ var le = () => {
981
279
  continue;
982
280
  }
983
281
  let p = /* @__PURE__ */ new Set(), m = !1;
984
- for (let e of l.getProperties()) if (k.isPropertyAssignment(e) || k.isShorthandPropertyAssignment(e)) {
985
- let t = (k.isPropertyAssignment(e), e.getNameNode());
986
- if (k.isComputedPropertyName(t)) {
282
+ for (let e of l.getProperties()) if (T.isPropertyAssignment(e) || T.isShorthandPropertyAssignment(e)) {
283
+ let t = (T.isPropertyAssignment(e), e.getNameNode());
284
+ if (T.isComputedPropertyName(t)) {
987
285
  m = !0;
988
286
  break;
989
287
  }
990
288
  p.add(e.getName().replace(/^['"]|['"]$/g, ""));
991
- } else (k.isMethodDeclaration(e) || k.isGetAccessorDeclaration(e)) && p.add(e.getName());
289
+ } else (T.isMethodDeclaration(e) || T.isGetAccessorDeclaration(e)) && p.add(e.getName());
992
290
  if (m) {
993
291
  o.push({
994
292
  file: u,
@@ -1026,7 +324,7 @@ var le = () => {
1026
324
  checked: s,
1027
325
  callSites: c
1028
326
  };
1029
- }, dt = (e) => {
327
+ }, Ae = (e) => {
1030
328
  if (e.callSites === 0) return null;
1031
329
  let t = [];
1032
330
  t.push(`workflow starts: ${e.checked}/${e.callSites} call sites checked`);
@@ -1039,7 +337,7 @@ var le = () => {
1039
337
  for (let n of e.unchecked) t.push(` ${n.file}:${n.line} — ${n.reason}`);
1040
338
  }
1041
339
  return e.issues.length === 0 && e.unchecked.length === 0 && t.push(" every call site matches its workflow"), t.join("\n");
1042
- }, ft = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, U = "VOLTRO_FIELD_ENCRYPTION_KEY", pt = (e) => {
340
+ }, je = /(^|[/\\])(\.env(\.[\w.-]+)?|.*\.ya?ml|Dockerfile|.*\.tf|.*\.toml|Procfile)$/i, k = "VOLTRO_FIELD_ENCRYPTION_KEY", Me = (e) => {
1043
341
  let t;
1044
342
  for (let [n, r] of e.sources) if (t === void 0 && /\bfieldEncryption\b/.test(r) && (t = {
1045
343
  reason: "fieldEncryption",
@@ -1047,13 +345,13 @@ var le = () => {
1047
345
  }), t === void 0 && /\.encrypted\s*\(/.test(r) && (t = {
1048
346
  reason: "encrypted-column",
1049
347
  file: n
1050
- }), r.includes(U)) return;
348
+ }), r.includes(k)) return;
1051
349
  if (t !== void 0) {
1052
- for (let [, t] of e.envFiles) if (t.includes(U)) return;
350
+ for (let [, t] of e.envFiles) if (t.includes(k)) return;
1053
351
  return t;
1054
352
  }
1055
- }, mt = (e) => [
1056
- `✗ ${U} is named nowhere under this app root, and the app encrypts columns at rest`,
353
+ }, Ne = (e) => [
354
+ `✗ ${k} is named nowhere under this app root, and the app encrypts columns at rest`,
1057
355
  e.reason === "fieldEncryption" ? ` (\`fieldEncryption\` in ${e.file})` : ` (an \`.encrypted()\` column in ${e.file})`,
1058
356
  "",
1059
357
  " A deploy without it does not degrade — it refuses to boot at `onActivate`,",
@@ -1069,14 +367,14 @@ var le = () => {
1069
367
  " reaches your deploy target is not knowable from here — a secret manager",
1070
368
  " or a CI variable is not a file. Naming it nowhere is conclusive; naming",
1071
369
  " it somewhere is not a guarantee."
1072
- ].join("\n"), ht = (e) => {
370
+ ].join("\n"), Pe = (e) => {
1073
371
  if (e.catalogCodes.length === 0) return;
1074
372
  let t = new Set(e.declaredLocales), n = e.catalogCodes.filter((e) => !t.has(e)).sort();
1075
373
  if (n.length !== 0) return {
1076
374
  orphaned: n,
1077
375
  noneDeclared: t.size === 0
1078
376
  };
1079
- }, gt = (e, t) => {
377
+ }, Fe = (e, t) => {
1080
378
  let n = e.orphaned.join(", ");
1081
379
  return e.noneDeclared ? [
1082
380
  `⚠ ${t} has ${e.orphaned.length} catalog(s) — ${n} — and the app declares no \`locales:\`.`,
@@ -1088,33 +386,33 @@ var le = () => {
1088
386
  " …or delete the files. A catalog that is never loaded looks from the inside",
1089
387
  " exactly like one that works, which is why this is worth a line."
1090
388
  ] : [`⚠ ${t} has catalog(s) not in \`locales:\` — ${n} — so they are never imported.`, " Add the code(s) to `locales:` in your app config, or delete the file(s)."];
1091
- }, _t = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), vt = /* @__PURE__ */ new Set([
389
+ }, A = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), j = /* @__PURE__ */ new Set([
1092
390
  "requireScope",
1093
391
  "requireAnyScope",
1094
392
  "requireAuth",
1095
393
  "assertScope",
1096
394
  "assertCan",
1097
395
  "can"
1098
- ]), yt = (e) => {
396
+ ]), Ie = (e) => {
1099
397
  let t = /* @__PURE__ */ new Set();
1100
- for (let n of e) for (let e of _t(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
398
+ for (let n of e) for (let e of A(n).matchAll(/\bexport\s+(?:const|function|async\s+function)\s+((?:require|assert)[A-Z]\w*)/g)) t.add(e[1]);
1101
399
  return t;
1102
- }, bt = (e, t) => {
400
+ }, Le = (e, t) => {
1103
401
  for (let n of t) if (RegExp(`\\b${n}\\s*\\(`).test(e)) return n;
1104
- }, xt = (e) => {
402
+ }, Re = (e) => {
1105
403
  let t = /\bsubject\s*\.\s*id\b[^\n]*?(?:!==|===|!=|==)|(?:!==|===|!=|==)[^\n]*?\bsubject\s*\.\s*id\b/.test(e), n = /(?:Error|Denied|Forbidden|Unauthorized)\b|\b(?:fail|die|throw)\b/.test(e);
1106
404
  return t && n;
1107
- }, St = (e, t, n) => {
405
+ }, ze = (e, t, n) => {
1108
406
  if (e.hasDeclaredGuards) return { kind: "declared-guard" };
1109
- let r = _t(e.src), i = bt(r, t);
1110
- return i === void 0 ? xt(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
407
+ let r = A(e.src), i = Le(r, t);
408
+ return i === void 0 ? Re(r) ? { kind: "inline-ownership" } : n.has(e.tag) ? { kind: "allowlisted" } : { kind: "unchecked" } : {
1111
409
  kind: "vocabulary-guard",
1112
410
  name: i
1113
411
  };
1114
- }, Ct = (e, t, n) => {
412
+ }, Be = (e, t, n) => {
1115
413
  let r = e.op === "delete" ? 3 : e.op === "update" ? 2 : 1;
1116
414
  return e.table !== void 0 && (t.has(e.table) && (r += 2), n.has(e.table) && e.op === "delete" && (r += 2)), r;
1117
- }, wt = (e, t, n) => {
415
+ }, Ve = (e, t, n) => {
1118
416
  switch (t.kind) {
1119
417
  case "declared-guard": return "guards: on the descriptor";
1120
418
  case "vocabulary-guard": return `calls ${t.name}()`;
@@ -1122,15 +420,15 @@ var le = () => {
1122
420
  case "allowlisted": return "accepted by a previous run";
1123
421
  case "unchecked": return n ? e.op === "read" ? "no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt" : `${e.op}s${e.table ? ` ${e.table}` : ""} with no check in this file — this app registers a row filter; VERIFY it covers this table, then record it as reviewed= rather than debt` : e.op === "read" ? "returns a row the client selected, with nothing compared to the caller" : `${e.op}s${e.table ? ` ${e.table}` : ""} with nothing constraining WHICH row`;
1124
422
  }
1125
- }, Tt = (e) => {
423
+ }, He = (e) => {
1126
424
  let t = e.executors.map((t) => {
1127
- let n = St(t, e.guardVocabulary, e.allowlist);
425
+ let n = ze(t, e.guardVocabulary, e.allowlist);
1128
426
  return {
1129
427
  tag: t.tag,
1130
428
  path: t.path,
1131
429
  verdict: n,
1132
- severity: Ct(t, e.tenantScopedTables, e.referencedTables),
1133
- why: wt(t, n, e.rowFilterRegistered === !0)
430
+ severity: Be(t, e.tenantScopedTables, e.referencedTables),
431
+ why: Ve(t, n, e.rowFilterRegistered === !0)
1134
432
  };
1135
433
  }), n = (e) => t.filter((t) => t.verdict.kind === e).length;
1136
434
  return {
@@ -1144,7 +442,7 @@ var le = () => {
1144
442
  },
1145
443
  unchecked: t.filter((e) => e.verdict.kind === "unchecked").sort((e, t) => t.severity - e.severity || e.tag.localeCompare(t.tag))
1146
444
  };
1147
- }, Et = [
445
+ }, Ue = [
1148
446
  "# voltro doctor — authz allowlist",
1149
447
  "#",
1150
448
  "# Two KINDS of line, and the difference is the point:",
@@ -1169,19 +467,19 @@ var le = () => {
1169
467
  "# `--write-authz-allowlist` PRESERVES reviewed= lines. It only ever adds debt",
1170
468
  "# lines for executors that are newly unchecked.",
1171
469
  ""
1172
- ], Dt = (e) => {
470
+ ], We = (e) => {
1173
471
  let t = [...e].map((e) => typeof e == "string" ? { tag: e } : e), n = t.filter((e) => e.reviewed === void 0).map((e) => e.tag).sort(), r = t.filter((e) => e.reviewed !== void 0).sort((e, t) => e.tag.localeCompare(t.tag));
1174
472
  return [
1175
- ...Et,
473
+ ...Ue,
1176
474
  ...n,
1177
475
  ...r.length > 0 ? ["", "# reviewed — read by a human, open on purpose"] : [],
1178
476
  ...r.map((e) => `${e.tag} reviewed=${e.reviewed}`),
1179
477
  ""
1180
478
  ].join("\n");
1181
- }, Ot = (e, t) => {
479
+ }, Ge = (e, t) => {
1182
480
  let n = [...new Set(t)].sort();
1183
- if (e === void 0) return Dt(n.map((e) => ({ tag: e })));
1184
- let r = W(e).tags, i = n.filter((e) => !r.has(e));
481
+ if (e === void 0) return We(n.map((e) => ({ tag: e })));
482
+ let r = M(e).tags, i = n.filter((e) => !r.has(e));
1185
483
  return i.length === 0 ? e : [
1186
484
  e.replace(/\n+$/, ""),
1187
485
  "",
@@ -1190,25 +488,25 @@ var le = () => {
1190
488
  ...i,
1191
489
  ""
1192
490
  ].join("\n");
1193
- }, kt = (e, t) => {
491
+ }, Ke = (e, t) => {
1194
492
  let n = [...new Set(t)];
1195
493
  if (e === void 0) return {
1196
494
  added: n.length,
1197
495
  kept: 0,
1198
496
  removed: 0
1199
497
  };
1200
- let r = W(e).tags;
498
+ let r = M(e).tags;
1201
499
  return {
1202
500
  added: n.filter((e) => !r.has(e)).length,
1203
501
  kept: r.size,
1204
502
  removed: 0
1205
503
  };
1206
- }, At = class extends Error {
504
+ }, qe = class extends Error {
1207
505
  tag;
1208
506
  constructor(e) {
1209
507
  super(`authz allowlist: '${e}' is marked reviewed with no reason. Write \`${e} reviewed=<why it is genuinely open>\`. A bare tag is the other, honest option — it records the executor as DEBT, which is what an unexplained exemption is.`), this.tag = e, this.name = "AllowlistReasonMissingError";
1210
508
  }
1211
- }, W = (e) => {
509
+ }, M = (e) => {
1212
510
  let t = [];
1213
511
  for (let n of e.split("\n")) {
1214
512
  let e = n.trim();
@@ -1221,7 +519,7 @@ var le = () => {
1221
519
  continue;
1222
520
  }
1223
521
  let a = r[2].trim();
1224
- if (a.length === 0) throw new At(i);
522
+ if (a.length === 0) throw new qe(i);
1225
523
  t.push({
1226
524
  tag: i,
1227
525
  reviewed: a
@@ -1231,13 +529,13 @@ var le = () => {
1231
529
  tags: new Set(t.map((e) => e.tag)),
1232
530
  entries: t
1233
531
  };
1234
- }, jt = (e) => {
532
+ }, N = (e) => {
1235
533
  let t = e.indexOf(".");
1236
534
  return t <= 0 ? void 0 : e.slice(0, t);
1237
- }, Mt = (e, t) => {
535
+ }, Je = (e, t) => {
1238
536
  let n = new Set(t.tables), r = new Set(t.rpcTags), i = /* @__PURE__ */ new Map();
1239
537
  for (let e of t.rpcTags) {
1240
- let t = jt(e);
538
+ let t = N(e);
1241
539
  if (t === void 0) continue;
1242
540
  let n = i.get(t);
1243
541
  n ? n.push(e) : i.set(t, [e]);
@@ -1266,7 +564,7 @@ var le = () => {
1266
564
  });
1267
565
  continue;
1268
566
  }
1269
- let s = new Set(o.rpcTags.map(jt).filter((e) => e !== void 0));
567
+ let s = new Set(o.rpcTags.map(N).filter((e) => e !== void 0));
1270
568
  for (let e of s) {
1271
569
  let t = i.get(e);
1272
570
  t !== void 0 && a.push({
@@ -1279,40 +577,83 @@ var le = () => {
1279
577
  }
1280
578
  }
1281
579
  return a;
1282
- }, Nt = (e, t) => {
580
+ }, Ye = (e, t) => {
1283
581
  let n = e.reduce((e, t) => e + t.tables.length, 0), r = [`plugin surface · ${e.length} plugin(s) contribute ${n} table(s)`];
1284
582
  if (t.length === 0) return r.push(" ✓ no overlap with your own tables or rpc namespaces"), r;
1285
583
  for (let e of t) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.plugin} — ${e.detail}`), r.push(` ${e.fix}`);
1286
584
  return r;
1287
- }, Pt = (e) => T(C(e, "app.config.ts")) || T(C(e, "app.config.js")), Ft = (e) => {
1288
- let t = re(e), n = ne(e);
585
+ }, Xe = async (e) => {
586
+ if (!await e.hasWorkflows().catch(() => !1)) return { kind: "not-applicable" };
587
+ let t;
588
+ try {
589
+ t = await e.openStore();
590
+ } catch (e) {
591
+ return {
592
+ kind: "skipped",
593
+ reason: e instanceof Error ? e.message : String(e)
594
+ };
595
+ }
596
+ try {
597
+ return {
598
+ kind: "checked",
599
+ result: await e.sweep(t.store)
600
+ };
601
+ } catch (e) {
602
+ return {
603
+ kind: "skipped",
604
+ reason: e instanceof Error ? e.message : String(e)
605
+ };
606
+ } finally {
607
+ await t.close().catch(() => {});
608
+ }
609
+ }, Ze = (e) => {
610
+ let t = Math.round(e / 6e4);
611
+ if (t < 90) return `${t}m`;
612
+ let n = Math.round(t / 60);
613
+ return n < 48 ? `${n}h` : `${Math.round(n / 24)}d`;
614
+ }, Qe = (e) => {
615
+ if (e.kind === "not-applicable") return null;
616
+ if (e.kind === "skipped") return `• stuck runs: NOT CHECKED — ${e.reason}\n This is the one doctor rule that reads the database rather than your source.
617
+ Run it where the app's DB env vars are set, or ignore this line locally.`;
618
+ let { result: t } = e, n = [];
619
+ if (t.stalled.length === 0) n.push(`✓ stuck runs: none (${t.examined} live run(s) old enough to examine${t.waitingOnTimer > 0 ? `, ${t.waitingOnTimer} waiting on a durable timer` : ""}${t.alreadyReported > 0 ? `, ${t.alreadyReported} already reported` : ""})`);
620
+ else {
621
+ n.push(`✗ stuck runs: ${t.stalled.length} run(s) have made no progress`);
622
+ for (let e of t.stalled.slice(0, 20)) n.push(` ${e.tag} ${e.runId} idle ${Ze(e.idleMs)} ${e.reason}`);
623
+ t.stalled.length > 20 && n.push(` … and ${t.stalled.length - 20} more`), n.push(" Nothing was changed — this is a SUSPICION, not a verdict. `awaiting-signal` usually means the sender never came.");
624
+ }
625
+ t.sawFullRunPage && n.push(" (the examined page filled up — more old runs exist than were read this pass)");
626
+ for (let e of t.failures) n.push(` ! could not examine ${e.subject}: ${e.detail}`);
627
+ return n.join("\n");
628
+ }, $e = (e) => x(y(e, "app.config.ts")) || x(y(e, "app.config.js")), P = (e) => {
629
+ let t = p(e), n = f(e);
1289
630
  return {
1290
631
  root: e,
1291
632
  serveBundle: t,
1292
- serveBundlePresent: T(t),
633
+ serveBundlePresent: x(t),
1293
634
  apiBundle: n,
1294
- apiBundlePresent: T(n)
635
+ apiBundlePresent: x(n)
1295
636
  };
1296
- }, It = [
637
+ }, et = [
1297
638
  "Production `voltro serve` requires a precompiled serve bundle and never transpiles on demand.",
1298
639
  "Add a `voltro build .` step BEFORE `voltro serve .` in your Dockerfile / start script.",
1299
640
  "(Generated Dockerfiles from `voltro create-project` already do this.)"
1300
- ].join("\n "), Lt = (e, t = !1) => {
1301
- let n = Ft(e);
1302
- if (console.log(`voltro doctor — serve preflight for ${n.root}`), !Pt(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
641
+ ].join("\n "), tt = (e, t = !1) => {
642
+ let n = P(e);
643
+ if (console.log(`voltro doctor — serve preflight for ${n.root}`), !$e(e)) return console.log("• no app.config found here — nothing to check (run against an app root)."), 0;
1303
644
  console.log(`${n.serveBundlePresent ? "✓" : "✗"} serve bundle: ${n.serveBundlePresent ? "present" : "MISSING"} (${n.serveBundle})`), console.log(`${n.apiBundlePresent ? "✓" : "•"} api bundle: ${n.apiBundlePresent ? "present" : "absent"} (${n.apiBundle})`);
1304
645
  let r = process.env.NODE_ENV === "production";
1305
646
  if (n.serveBundlePresent) return console.log("✓ serve preflight: ready for `voltro serve`."), 0;
1306
647
  let i = t || r;
1307
- return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${It}`), +!!i;
1308
- }, Rt = (e, t, n) => {
1309
- let r = new Set(e), i = I(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
648
+ return console.error(`${i ? "✗" : "•"} serve bundle missing — ${i ? "production `voltro serve` will FAIL." : "a non-production `voltro serve` still runs via tsx."}`), console.error(` ${et}`), +!!i;
649
+ }, nt = (e, t, n) => {
650
+ let r = new Set(e), i = ee(n), a = (e) => (/\.(tsx?|jsx?)$/.test(e) ? [e] : [
1310
651
  `${e}.ts`,
1311
652
  `${e}.tsx`,
1312
653
  `${e}.js`,
1313
654
  `${e}.jsx`,
1314
- x(e, "index.ts"),
1315
- x(e, "index.tsx")
655
+ _(e, "index.ts"),
656
+ _(e, "index.tsx")
1316
657
  ]).find((e) => r.has(e)), o = (e) => {
1317
658
  let n = (t.get(e) ?? "").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1"), r = [];
1318
659
  for (let e of [
@@ -1325,13 +666,13 @@ var le = () => {
1325
666
  }
1326
667
  let o = [];
1327
668
  for (let t of r) {
1328
- let n = t.startsWith(".") ? C(b(e), t) : be(t, i);
669
+ let n = t.startsWith(".") ? y(ae(e), t) : te(t, i);
1329
670
  if (n === void 0) continue;
1330
671
  let r = a(n);
1331
672
  r !== void 0 && o.push(r);
1332
673
  }
1333
674
  return o;
1334
- }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => Wt.test(S(n, e))), l = new Set(c);
675
+ }, s = /* @__PURE__ */ new Set(), c = e.filter((e) => st.test(v(n, e))), l = new Set(c);
1335
676
  for (; c.length > 0;) {
1336
677
  let e = c.shift();
1337
678
  if (!s.has(e)) {
@@ -1344,11 +685,11 @@ var le = () => {
1344
685
  let d = /* @__PURE__ */ new Set();
1345
686
  for (let e of s) l.has(e) || u.has(e) || d.add(e);
1346
687
  return d;
1347
- }, zt = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), Bt = (e, t) => (e.match(t) ?? []).length, G = (e, t) => e.getDescendantsOfKind(j.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, Vt = (e, t) => {
688
+ }, F = (e) => e.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1").replace(/'(?:[^'\\\n]|\\.)*'/g, "''").replace(/"(?:[^"\\\n]|\\.)*"/g, "\"\""), rt = (e, t) => (e.match(t) ?? []).length, I = (e, t) => e.getDescendantsOfKind(D.CallExpression).filter((e) => t.test(e.getExpression().getText())).length, it = (e, t) => {
1348
689
  if (e.src.includes(t)) return !0;
1349
690
  let n = t.replace(/^@voltro\/plugin-/, "");
1350
- return n !== t && G(e.file, RegExp(`^${n}Plugin$`)) > 0;
1351
- }, Ht = (e, t) => {
691
+ return n !== t && I(e.file, RegExp(`^${n}Plugin$`)) > 0;
692
+ }, at = (e, t) => {
1352
693
  let n = [], r = RegExp(`\\b${t}\\s*(?:<[^()]*>)?\\s*\\(`, "g"), i;
1353
694
  for (; (i = r.exec(e)) !== null;) {
1354
695
  let t = i.index + i[0].length, r = 1, a = t;
@@ -1356,10 +697,10 @@ var le = () => {
1356
697
  n.push(e.slice(t, a - 1));
1357
698
  }
1358
699
  return n;
1359
- }, Ut = (e, t) => {
700
+ }, ot = (e, t) => {
1360
701
  let n = t.getSymbol()?.getDeclarations()?.[0];
1361
- return n === void 0 ? [] : e.getDescendantsOfKind(j.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
1362
- }, Wt = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, Gt = /\.(query|mutation|action|stream)\.server\.tsx?$/, Kt = [
702
+ return n === void 0 ? [] : e.getDescendantsOfKind(D.Identifier).filter((e) => e.getSymbol()?.getDeclarations()?.[0] === n);
703
+ }, st = /\.(server|cron|workflow\.server|subscribe|reaction|outbox|startup|trigger|webhook|migration|seed)\.tsx?$/, ct = /\.(query|mutation|action|stream)\.server\.tsx?$/, lt = [
1363
704
  "Id",
1364
705
  "ID",
1365
706
  "Hash",
@@ -1385,7 +726,7 @@ var le = () => {
1385
726
  "Comment",
1386
727
  "Provider",
1387
728
  "Format"
1388
- ], qt = RegExp(`(?:${Kt.join("|")})$|_(?:${Kt.map((e) => e.toLowerCase()).join("|")})$`), Jt = [
729
+ ], ut = RegExp(`(?:${lt.join("|")})$|_(?:${lt.map((e) => e.toLowerCase()).join("|")})$`), dt = [
1389
730
  {
1390
731
  id: "hardcoded-framework-cookie",
1391
732
  scope: "client",
@@ -1398,7 +739,7 @@ var le = () => {
1398
739
  scope: "server",
1399
740
  smell: "an executor that never names its own descriptor — its input type is hand-written, so it can drift from the contract it implements",
1400
741
  use: "import the descriptor and type the input as `ExecutorInput<typeof descriptor>`, so the wire contract and the handler cannot disagree",
1401
- match: ({ src: e, path: t }) => Gt.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
742
+ match: ({ src: e, path: t }) => ct.test(t) && !/\bimport\s[^;]*\bfrom\s*['"][^'"]*['"]/.test((e.match(/^\s*import\s[^;]*from\s*['"]\.[^'"]*['"]/gm) ?? []).join("\n"))
1402
743
  },
1403
744
  {
1404
745
  id: "subject-write-no-guard",
@@ -1406,8 +747,8 @@ var le = () => {
1406
747
  smell: "writes ctx.request.subject.id into the database with no guard on the descriptor — an anonymous caller reaches the write and gets a raw statement failure instead of a typed refusal",
1407
748
  use: "add a guard (e.g. `.guard(requireScope('…'))`) or an explicit auth check, so an unauthenticated caller is refused BEFORE the write",
1408
749
  match: ({ src: e, path: t }) => {
1409
- if (!Gt.test(t)) return !1;
1410
- let n = zt(e);
750
+ if (!ct.test(t)) return !1;
751
+ let n = F(e);
1411
752
  return /\bsubject\s*\.\s*id\b/.test(n) && /\b(?:insert|update|upsert|insertMany|updateMany|insertRow|upsertRow|updateManyRow)\s*\(/.test(n) && !/\b(?:guard|guards|requireScope|requireAnyScope|assertCan|assertScope|requireAuth|Unauthenticated)\b/.test(n);
1412
753
  }
1413
754
  },
@@ -1416,14 +757,14 @@ var le = () => {
1416
757
  scope: "server",
1417
758
  smell: "raw fetch() for outbound HTTP on the server (no traceparent, no retry, and no SSRF guard where the URL is not a constant)",
1418
759
  use: "yield* HttpClient — the framework client. It injects traceparent, retries, and honours `http.allowHosts`. Where the URL is built from anything a caller supplied it ALSO blocks loopback / RFC-1918 / link-local targets (incl. 169.254.169.254) on the initial request AND every redirect hop — for a hard-coded host that half is redundant, and the first three are still the reason to switch",
1419
- match: ({ file: e, path: t, serverOnly: n }) => (Wt.test(t) || n) && G(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
760
+ match: ({ file: e, path: t, serverOnly: n }) => (st.test(t) || n) && I(e, /^(?:globalThis\.|window\.|self\.)?fetch$/) > 0
1420
761
  },
1421
762
  {
1422
763
  id: "form",
1423
764
  scope: "client",
1424
765
  smell: "per-field useState + a submit flag (hand-rolled form)",
1425
766
  use: "useFormBinding — fields + validation from the mutation input Schema; server ValidationError({ field }) routes to the field",
1426
- match: ({ src: e, file: t }) => G(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
767
+ match: ({ src: e, file: t }) => I(t, /^useState$/) >= 4 && /onSubmit|handleSubmit|isSubmitting/.test(e)
1427
768
  },
1428
769
  {
1429
770
  id: "table",
@@ -1451,7 +792,7 @@ var le = () => {
1451
792
  scope: "client",
1452
793
  smell: "useMemo fanning in multiple subscriptions (hand-maintained dep array)",
1453
794
  use: "useDerived — dependency-tracked, referentially stable",
1454
- match: ({ src: e }) => Bt(e, /\buseSubscription\s*[(<]/g) >= 3 && Bt(e, /\buseMemo\s*\(/g) >= 3
795
+ match: ({ src: e }) => rt(e, /\buseSubscription\s*[(<]/g) >= 3 && rt(e, /\buseMemo\s*\(/g) >= 3
1455
796
  },
1456
797
  {
1457
798
  id: "next-shim",
@@ -1473,16 +814,16 @@ var le = () => {
1473
814
  scope: "client",
1474
815
  smell: "checking a subscription's `data === undefined` (or `!data`) instead of its `loading` / `idle` flag",
1475
816
  use: "branch on `loading` (and `idle` when you pass `skip`) — `loading` narrows `data`, so the settled branch needs no `?? []` and no `!`",
1476
- match: ({ file: e }) => e.getDescendantsOfKind(j.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
1477
- let t = e.getFirstAncestorByKind(j.VariableDeclaration)?.getNameNode();
1478
- return t === void 0 ? [] : k.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
1479
- }).some((t) => Ut(e, t).some((e) => {
817
+ match: ({ file: e }) => e.getDescendantsOfKind(D.CallExpression).filter((e) => /(^|\.)useSubscription$/.test(e.getExpression().getText())).flatMap((e) => {
818
+ let t = e.getFirstAncestorByKind(D.VariableDeclaration)?.getNameNode();
819
+ return t === void 0 ? [] : T.isObjectBindingPattern(t) ? t.getElements().filter((e) => (e.getPropertyNameNode() ?? e.getNameNode()).getText() === "data").map((e) => e.getNameNode()) : [];
820
+ }).some((t) => ot(e, t).some((e) => {
1480
821
  let t = e.getParent();
1481
- if (k.isBinaryExpression(t)) {
822
+ if (T.isBinaryExpression(t)) {
1482
823
  let n = t.getOperatorToken().getText(), r = t.getLeft() === e ? t.getRight() : t.getLeft();
1483
824
  return /^[!=]==?$/.test(n) && /^(undefined|null)$/.test(r.getText());
1484
825
  }
1485
- return k.isPrefixUnaryExpression(t) && t.getOperatorToken() === j.ExclamationToken;
826
+ return T.isPrefixUnaryExpression(t) && t.getOperatorToken() === D.ExclamationToken;
1486
827
  }))
1487
828
  },
1488
829
  {
@@ -1504,7 +845,7 @@ var le = () => {
1504
845
  scope: "server",
1505
846
  smell: "several sequential store reads assembling related data (an N+1 you are writing by hand)",
1506
847
  use: "reads are a parent→child walk on ONE key → relations() in a *.relations.ts + .with({ … }) (ONE JSON-aggregate query, every dialect); reads collect ids from SEVERAL sources → keep the assembly and run the independent leading reads under Effect.all (same queries, same results, only concurrent — zero parity risk)",
1507
- match: (e) => G(e.file, /\bstore\.query$/) >= 3 || G(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
848
+ match: (e) => I(e.file, /\bstore\.query$/) >= 3 || I(e.file, /\bstore\.query$/) >= 2 && /\bnew Map\s*\(/.test(e.src)
1508
849
  },
1509
850
  {
1510
851
  id: "store-effect-lift",
@@ -1527,7 +868,7 @@ var le = () => {
1527
868
  use: ".encrypted() on the column — boot fails loudly if no cipher is configured, so it can never silently persist plaintext (a *Hash column and a vault* handle are NOT plaintext secrets: the hash IS the protection — often a unique lookup key — and the handle only names a secret held elsewhere, so both are skipped)",
1528
869
  match: ({ src: e }) => e.split("\n").some((e) => [...e.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*(?:text|varchar|string|longtext)\s*\(/g)].some((t) => {
1529
870
  let n = t[1];
1530
- return !/token|secret|credential|password|apikey|accesskey/i.test(n) || qt.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
871
+ return !/token|secret|credential|password|apikey|accesskey/i.test(n) || ut.test(n) || /^vault/i.test(n) || /\.unique\s*\(/.test(e) ? !1 : !/\.encrypted\s*\(/.test(e);
1531
872
  }))
1532
873
  },
1533
874
  {
@@ -1537,7 +878,7 @@ var le = () => {
1537
878
  use: "you cannot look a value up by equality AND encrypt it non-deterministically — `.encrypted()` would make this column unfindable. Store the HASH as the unique lookup key and keep the secret itself either `.encrypted()` in a second column or nowhere (the framework's own `_voltro_api_keys.hashedKey` is the shape: the presented value is hashed and compared, never read back). A deterministic cipher is the other option and buys equality lookup at the cost of leaking which rows share a value",
1538
879
  match: ({ src: e }) => e.split("\n").some((e) => [...e.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*(?:text|varchar|string|longtext)\s*\(/g)].some((t) => {
1539
880
  let n = t[1];
1540
- return !/token|secret|credential|password|apikey|accesskey/i.test(n) || qt.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
881
+ return !/token|secret|credential|password|apikey|accesskey/i.test(n) || ut.test(n) || /^vault/i.test(n) ? !1 : /\.unique\s*\(/.test(e) && !/\.encrypted\s*\(/.test(e);
1541
882
  }))
1542
883
  },
1543
884
  {
@@ -1601,9 +942,9 @@ var le = () => {
1601
942
  scope: "server",
1602
943
  smell: "setRowFilter is registered, but no test passes `rowFilter:` to makeTestContext",
1603
944
  use: "wire the filter into at least one test (`makeTestContext({ subject, store, rowFilter })`) and sabotage the predicate once to watch it go red — an untested row filter is a visibility rule nothing verifies",
1604
- match: ({ file: e, testSources: t }) => G(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
1605
- let t = zt(e);
1606
- return Ht(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
945
+ match: ({ file: e, testSources: t }) => I(e, /^setRowFilter$/) > 0 && ![...t.values()].some((e) => {
946
+ let t = F(e);
947
+ return at(t, "makeTestContext").length > 0 && /\browFilter\b/.test(t);
1607
948
  })
1608
949
  },
1609
950
  {
@@ -1613,84 +954,84 @@ var le = () => {
1613
954
  use: "timestampMs / timestampMsOrNull from @voltro/database/wire in the descriptor's output struct — `output` IS the serializer (it is the rpc success schema), so a Date field declared as timestampMs crosses as epoch ms on its own; declaring Schema.Number describes the WIRE type and leaves nothing to convert",
1614
955
  match: ({ src: e }) => /\.(getTime|toISOString)\s*\(\s*\)/.test(e) && /\.map\s*\(|=>\s*\(\{/.test(e) && /\bexport\s+default\b/.test(e)
1615
956
  }
1616
- ], Yt = (e) => q(e), Xt = (e, t) => {
957
+ ], ft = (e) => R(e), pt = (e, t) => {
1617
958
  let n = /* @__PURE__ */ new Set();
1618
959
  for (let r of t) {
1619
- let t = S(e, r), i = t.split(/[\\/]/)[0];
960
+ let t = v(e, r), i = t.split(/[\\/]/)[0];
1620
961
  n.add(i !== void 0 && i !== t ? i : ".");
1621
962
  }
1622
963
  return [...n].sort();
1623
- }, Zt = /\.(tsx?|jsx?)$/, K = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, Qt = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, $t = (e, t = [], n = 0) => {
1624
- if (n > 12 || K.test(e)) return t;
964
+ }, mt = /\.(tsx?|jsx?)$/, L = /(^|\/)(node_modules|dist|build|\.framework|\.next|coverage)(\/|$)/, ht = /(^|[\\/])[^\\/]*\.generated\.[cm]?[jt]sx?$|\.d\.[cm]?ts$/, gt = (e, t = [], n = 0) => {
965
+ if (n > 12 || L.test(e)) return t;
1625
966
  let r;
1626
967
  try {
1627
- r = D(e, { withFileTypes: !0 });
968
+ r = C(e, { withFileTypes: !0 });
1628
969
  } catch {
1629
970
  return t;
1630
971
  }
1631
972
  for (let i of r) {
1632
- let r = x(e, i.name);
973
+ let r = _(e, i.name);
1633
974
  if (i.isDirectory()) {
1634
975
  if (i.name.startsWith(".")) continue;
1635
- $t(r, t, n + 1);
1636
- } else ft.test(i.name) && t.push(r);
976
+ gt(r, t, n + 1);
977
+ } else je.test(i.name) && t.push(r);
1637
978
  }
1638
979
  return t;
1639
- }, q = (e, t = [], n = 0) => {
1640
- if (n > 12 || K.test(e)) return t;
980
+ }, R = (e, t = [], n = 0) => {
981
+ if (n > 12 || L.test(e)) return t;
1641
982
  let r;
1642
983
  try {
1643
- r = D(e);
984
+ r = C(e);
1644
985
  } catch {
1645
986
  return t;
1646
987
  }
1647
988
  for (let i of r) {
1648
- let r = x(e, i), a;
989
+ let r = _(e, i), a;
1649
990
  try {
1650
- a = O(r);
991
+ a = w(r);
1651
992
  } catch {
1652
993
  continue;
1653
994
  }
1654
- a.isDirectory() ? q(r, t, n + 1) : Zt.test(i) && !/\.(test|spec)\./.test(i) && !Qt.test(i) && t.push(r);
995
+ a.isDirectory() ? R(r, t, n + 1) : mt.test(i) && !/\.(test|spec)\./.test(i) && !ht.test(i) && t.push(r);
1655
996
  }
1656
997
  return t;
1657
- }, en = (e, t = [], n = 0) => {
1658
- if (n > 12 || K.test(e)) return t;
998
+ }, _t = (e, t = [], n = 0) => {
999
+ if (n > 12 || L.test(e)) return t;
1659
1000
  let r;
1660
1001
  try {
1661
- r = D(e);
1002
+ r = C(e);
1662
1003
  } catch {
1663
1004
  return t;
1664
1005
  }
1665
1006
  for (let i of r) {
1666
- let r = x(e, i), a;
1007
+ let r = _(e, i), a;
1667
1008
  try {
1668
- a = O(r);
1009
+ a = w(r);
1669
1010
  } catch {
1670
1011
  continue;
1671
1012
  }
1672
- a.isDirectory() ? en(r, t, n + 1) : Zt.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1013
+ a.isDirectory() ? _t(r, t, n + 1) : mt.test(i) && /\.(test|spec)\./.test(i) && t.push(r);
1673
1014
  }
1674
1015
  return t;
1675
- }, tn = (e) => {
1676
- let t = Yt(e);
1016
+ }, vt = (e) => {
1017
+ let t = ft(e);
1677
1018
  return {
1678
- dirs: Xt(e, t),
1019
+ dirs: pt(e, t),
1679
1020
  fileCount: t.length
1680
1021
  };
1681
- }, nn = (e) => {
1682
- let t = Yt(e);
1022
+ }, yt = (e) => {
1023
+ let t = ft(e);
1683
1024
  if (t.length === 0) return [];
1684
- let n = new A({
1025
+ let n = new E({
1685
1026
  useInMemoryFileSystem: !0,
1686
1027
  skipAddingFilesFromTsConfig: !0
1687
1028
  }), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
1688
1029
  for (let e of t) try {
1689
- i.set(e, E(e, "utf8"));
1030
+ i.set(e, S(e, "utf8"));
1690
1031
  } catch {}
1691
- let a = Rt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1692
- for (let t of en(e)) try {
1693
- o.set(t, E(t, "utf8"));
1032
+ let a = nt([...i.keys()], i, e), o = /* @__PURE__ */ new Map();
1033
+ for (let t of _t(e)) try {
1034
+ o.set(t, S(t, "utf8"));
1694
1035
  } catch {}
1695
1036
  for (let s of t) {
1696
1037
  let t = i.get(s);
@@ -1700,30 +1041,30 @@ var le = () => {
1700
1041
  c = {
1701
1042
  src: t,
1702
1043
  file: n.createSourceFile(`/scan${s.endsWith("x") ? ".tsx" : ".ts"}`, t, { overwrite: !0 }),
1703
- path: S(e, s),
1044
+ path: v(e, s),
1704
1045
  serverOnly: a.has(s),
1705
1046
  testSources: o
1706
1047
  };
1707
1048
  } catch {
1708
1049
  continue;
1709
1050
  }
1710
- for (let t of Jt) {
1711
- if (t.plugin !== void 0 && Vt(c, t.plugin) || !t.match(c)) continue;
1051
+ for (let t of dt) {
1052
+ if (t.plugin !== void 0 && it(c, t.plugin) || !t.match(c)) continue;
1712
1053
  let n = r.get(t.id) ?? [];
1713
- n.push(S(e, s)), r.set(t.id, n);
1054
+ n.push(v(e, s)), r.set(t.id, n);
1714
1055
  }
1715
1056
  }
1716
- return Jt.filter((e) => r.has(e.id)).map((e) => ({
1057
+ return dt.filter((e) => r.has(e.id)).map((e) => ({
1717
1058
  rule: e,
1718
1059
  files: r.get(e.id)
1719
1060
  })).sort((e, t) => t.files.length - e.files.length || e.rule.id.localeCompare(t.rule.id));
1720
- }, rn = (e) => {
1721
- let t = tn(e);
1061
+ }, z = (e) => {
1062
+ let t = vt(e);
1722
1063
  return {
1723
1064
  root: e,
1724
1065
  scannedFiles: t.fileCount,
1725
1066
  scannedDirs: t.dirs,
1726
- findings: nn(e).map(({ rule: e, files: t }) => ({
1067
+ findings: yt(e).map(({ rule: e, files: t }) => ({
1727
1068
  id: e.id,
1728
1069
  scope: e.scope,
1729
1070
  smell: e.smell,
@@ -1731,22 +1072,22 @@ var le = () => {
1731
1072
  files: t
1732
1073
  }))
1733
1074
  };
1734
- }, an = 3, on = .2, sn = 50, cn = (e, t) => e >= sn && t > 0 && e / t >= on, ln = (e) => {
1075
+ }, bt = 3, xt = .2, St = 50, Ct = (e, t) => e >= St && t > 0 && e / t >= xt, wt = (e) => {
1735
1076
  let t = /* @__PURE__ */ new Map();
1736
- for (let n of q(e)) try {
1737
- t.set(S(e, n), E(n, "utf8"));
1077
+ for (let n of R(e)) try {
1078
+ t.set(v(e, n), S(n, "utf8"));
1738
1079
  } catch {}
1739
1080
  let n = /* @__PURE__ */ new Map();
1740
- for (let t of $t(e)) try {
1741
- n.set(S(e, t), E(t, "utf8"));
1081
+ for (let t of gt(e)) try {
1082
+ n.set(v(e, t), S(t, "utf8"));
1742
1083
  } catch {}
1743
- let r = pt({
1084
+ let r = Me({
1744
1085
  sources: t,
1745
1086
  envFiles: n
1746
1087
  });
1747
- r !== void 0 && (console.log(""), console.log(mt(r)));
1748
- }, un = (e) => {
1749
- let t = rn(e);
1088
+ r !== void 0 && (console.log(""), console.log(Ne(r)));
1089
+ }, Tt = (e) => {
1090
+ let t = z(e);
1750
1091
  if (console.log(""), t.scannedFiles === 0) return console.log(`\u2022 hand-roll scan: no source directories found under ${e}.`), console.log(" Walked the app root recursively (node_modules / dist / .framework pruned). Run `voltro doctor <app>` against an app root."), 0;
1751
1092
  if (t.findings.length === 0) return console.log(`\u2713 hand-roll scan: ${t.scannedFiles} file${t.scannedFiles === 1 ? "" : "s"} across ${t.scannedDirs.join(", ")} \u2014 no hand-rolled primitives found.`), 0;
1752
1093
  console.log(`\u2022 Shipped primitives you may be hand-rolling (scanned ${t.scannedFiles} files across ${t.scannedDirs.join(", ")}):`);
@@ -1757,56 +1098,56 @@ var le = () => {
1757
1098
  console.log(` [${e}]`);
1758
1099
  let i = [...r].sort((e, t) => e.files.length - t.files.length);
1759
1100
  for (let e of i) {
1760
- if (cn(e.files.length, t.scannedFiles)) {
1101
+ if (Ct(e.files.length, t.scannedFiles)) {
1761
1102
  let n = Math.round(e.files.length / t.scannedFiles * 100);
1762
1103
  console.log(` ${e.smell} \u2014 ${e.files.length} file(s), ${n}% of those scanned: a codebase-wide pattern rather than a work list`), console.log(` \u2192 ${e.use}`), console.log(" (paths omitted on purpose at this scale — `voltro doctor --json` has every one.)");
1763
1104
  continue;
1764
1105
  }
1765
- let r = e.files.slice(0, an).join(", "), i = e.files.length - an;
1106
+ let r = e.files.slice(0, bt).join(", "), i = e.files.length - bt;
1766
1107
  i > 0 && (n += i);
1767
1108
  let a = i > 0 ? `, +${i} more` : "";
1768
1109
  console.log(` ${e.smell} \u2014 ${e.files.length} file(s): ${r}${a}`), console.log(` \u2192 ${e.use}`);
1769
1110
  }
1770
1111
  }
1771
1112
  return n > 0 && console.log(` (${n} more file path${n === 1 ? "" : "s"} not shown \u2014 \`voltro doctor --json\` prints every one.)`), console.log(" (See the \"Pick the SERVER primitive\" / \"Pick the CLIENT primitive\" rubrics in AGENTS.md / CLAUDE.md.)"), t.findings.length;
1772
- }, dn = "renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.", fn = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, pn = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, mn = (e) => {
1773
- let t = fn.exec(e)?.[1];
1113
+ }, Et = "renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.", Dt = /export\s+const\s+renderMode\s*(?::[^=]+)?=\s*['"]([^'"]*)['"]/, Ot = /export\s+const\s+dynamic\s*=\s*['"]force-(dynamic|static)['"]/, kt = (e) => {
1114
+ let t = Dt.exec(e)?.[1];
1774
1115
  if (t !== void 0) return t === "ssr" ? "ssr" : null;
1775
- let n = pn.exec(e)?.[1];
1116
+ let n = Ot.exec(e)?.[1];
1776
1117
  return n === "dynamic" ? "ssr" : n === "static" ? null : "default";
1777
- }, hn = async (e) => {
1778
- let t = x(e, "src", "pages");
1779
- if (!T(t)) return [];
1780
- let { pages: i, dirs: o } = await n(t), s = [];
1781
- for (let n of i) {
1782
- if (!a(o, n.dirChain).some((e) => e.hasLayout)) continue;
1783
- let i;
1118
+ }, B = async (e) => {
1119
+ let t = _(e, "src", "pages");
1120
+ if (!x(t)) return [];
1121
+ let { pages: n, dirs: r } = await l(t), i = [];
1122
+ for (let a of n) {
1123
+ if (!s(r, a.dirChain).some((e) => e.hasLayout)) continue;
1124
+ let n;
1784
1125
  try {
1785
- i = E(x(t, n.file), "utf8");
1126
+ n = S(_(t, a.file), "utf8");
1786
1127
  } catch {
1787
1128
  continue;
1788
1129
  }
1789
- if (r.test(i)) continue;
1790
- let c = mn(i);
1791
- c !== null && s.push({
1792
- file: S(e, x(t, n.file)),
1793
- pattern: n.pattern,
1794
- currentMode: c
1130
+ if (c.test(n)) continue;
1131
+ let o = kt(n);
1132
+ o !== null && i.push({
1133
+ file: v(e, _(t, a.file)),
1134
+ pattern: a.pattern,
1135
+ currentMode: o
1795
1136
  });
1796
1137
  }
1797
- return s;
1798
- }, gn = 10, _n = async (e) => {
1799
- if (console.log(""), !T(x(e, "src", "pages"))) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
1800
- let t = await hn(e);
1138
+ return i;
1139
+ }, At = 10, jt = async (e) => {
1140
+ if (console.log(""), !x(_(e, "src", "pages"))) return console.log("• spa-candidate scan: no src/pages here — nothing to check (web apps only)."), 0;
1141
+ let t = await B(e);
1801
1142
  if (t.length === 0) return console.log("✓ spa-candidate scan: no loader-free, layout-backed pages to flag."), 0;
1802
1143
  console.log(`• renderMode:'spa' candidates (${t.length} page${t.length === 1 ? "" : "s"} — loader-free, under a layout, currently ssr/default):`);
1803
- for (let e of t.slice(0, gn)) {
1144
+ for (let e of t.slice(0, At)) {
1804
1145
  let t = e.currentMode === "ssr" ? "renderMode:'ssr'" : "default renderMode";
1805
1146
  console.log(` ${e.file} (${e.pattern}) — ${t}`);
1806
1147
  }
1807
- let n = t.length - gn;
1808
- return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${dn}`), t.length;
1809
- }, vn = /* @__PURE__ */ new Set([
1148
+ let n = t.length - At;
1149
+ return n > 0 && console.log(` (+${n} more page${n === 1 ? "" : "s"} not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${Et}`), t.length;
1150
+ }, Mt = /* @__PURE__ */ new Set([
1810
1151
  "node_modules",
1811
1152
  "dist",
1812
1153
  "build",
@@ -1814,69 +1155,69 @@ var le = () => {
1814
1155
  ".vite",
1815
1156
  ".framework",
1816
1157
  ".git"
1817
- ]), yn = (e, t, n) => {
1158
+ ]), Nt = (e, t, n) => {
1818
1159
  let r;
1819
1160
  try {
1820
- r = D(e, { withFileTypes: !0 });
1161
+ r = C(e, { withFileTypes: !0 });
1821
1162
  } catch {
1822
1163
  return;
1823
1164
  }
1824
1165
  for (let i of r) {
1825
- if (vn.has(i.name)) continue;
1826
- let r = x(e, i.name);
1827
- i.isDirectory() ? yn(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(S(t, r));
1166
+ if (Mt.has(i.name)) continue;
1167
+ let r = _(e, i.name);
1168
+ i.isDirectory() ? Nt(r, t, n) : i.isFile() && i.name.endsWith(".tsx") && n.push(v(t, r));
1828
1169
  }
1829
- }, bn = (e, t) => {
1170
+ }, Pt = (e, t) => {
1830
1171
  let n = /* @__PURE__ */ new Set(), r = /(?:from|import)\s*\(?\s*['"](\.[^'"]*)['"]/g;
1831
1172
  for (let i of t) {
1832
1173
  let t;
1833
1174
  try {
1834
- t = E(x(e, i), "utf8");
1175
+ t = S(_(e, i), "utf8");
1835
1176
  } catch {
1836
1177
  continue;
1837
1178
  }
1838
- let a = b(x(e, i));
1179
+ let a = ae(_(e, i));
1839
1180
  for (let i of t.matchAll(r)) {
1840
- let t = C(a, i[1]);
1181
+ let t = y(a, i[1]);
1841
1182
  for (let r of [
1842
1183
  t,
1843
1184
  `${t}.tsx`,
1844
1185
  `${t}.ts`,
1845
- x(t, "index.tsx"),
1846
- x(t, "index.ts")
1847
- ]) if (T(r) && O(r).isFile()) {
1848
- n.add(S(e, r));
1186
+ _(t, "index.tsx"),
1187
+ _(t, "index.ts")
1188
+ ]) if (x(r) && w(r).isFile()) {
1189
+ n.add(v(e, r));
1849
1190
  break;
1850
1191
  }
1851
1192
  }
1852
1193
  }
1853
1194
  return n;
1854
- }, xn = (e, t) => {
1855
- let n = e.slice(t.length).split(w).slice(0, -1)[0];
1195
+ }, Ft = (e, t) => {
1196
+ let n = e.slice(t.length).split(b).slice(0, -1)[0];
1856
1197
  return n !== void 0 && n.startsWith("[");
1857
- }, J = (e) => {
1858
- let t = [], n = x(e, "src", "pages"), r = x(e, "src");
1859
- if (!T(r)) return t;
1198
+ }, V = (e) => {
1199
+ let t = [], n = _(e, "src", "pages"), r = _(e, "src");
1200
+ if (!x(r)) return t;
1860
1201
  let i = [];
1861
- yn(r, e, i), i.sort();
1862
- let a = `${S(e, n)}${w}`;
1863
- bn(e, i);
1202
+ Nt(r, e, i), i.sort();
1203
+ let a = `${v(e, n)}${b}`;
1204
+ Pt(e, i);
1864
1205
  for (let n of i) {
1865
- let r = n.slice(n.lastIndexOf(w) + 1);
1206
+ let r = n.slice(n.lastIndexOf(b) + 1);
1866
1207
  if (n.startsWith(a)) {
1867
1208
  if (r !== "page.tsx") continue;
1868
- T(x(e, c(n))) || t.push({
1209
+ x(_(e, d(n))) || t.push({
1869
1210
  kind: "page-without-test",
1870
1211
  file: n
1871
1212
  });
1872
1213
  try {
1873
- let r = E(x(e, n), "utf8");
1214
+ let r = S(_(e, n), "utf8");
1874
1215
  !/\bexport\s+(?:const|let|var|function)\s+meta\b/.test(r) && !/\bexport\s*\{[^}]*\bmeta\b/.test(r) && t.push({
1875
1216
  kind: "page-without-meta",
1876
1217
  file: n
1877
1218
  });
1878
1219
  } catch {}
1879
- xn(n, a) && t.push({
1220
+ Ft(n, a) && t.push({
1880
1221
  kind: "top-level-dynamic-route",
1881
1222
  file: n
1882
1223
  });
@@ -1888,33 +1229,33 @@ var le = () => {
1888
1229
  });
1889
1230
  }
1890
1231
  return t;
1891
- }, Sn = 10, Y = (e, t, n) => {
1232
+ }, It = 10, H = (e, t, n) => {
1892
1233
  if (t.length === 0) return;
1893
1234
  console.log(`• ${e} (${t.length}):`);
1894
- for (let e of t.slice(0, Sn)) console.log(` ${e}`);
1895
- let r = t.length - Sn;
1235
+ for (let e of t.slice(0, It)) console.log(` ${e}`);
1236
+ let r = t.length - It;
1896
1237
  r > 0 && console.log(` (+${r} more not shown — \`voltro doctor --json\` prints every one.)`), console.log(` → ${n}`);
1897
- }, Cn = (e) => {
1898
- if (console.log(""), !T(x(e, "src", "pages"))) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
1899
- let t = J(e);
1238
+ }, Lt = (e) => {
1239
+ if (console.log(""), !x(_(e, "src", "pages"))) return console.log("• page-convention scan: no src/pages here — nothing to check (web apps only)."), 0;
1240
+ let t = V(e);
1900
1241
  if (t.length === 0) return console.log("✓ page-convention scan: every route is a page.tsx in its own directory, with a test beside it."), 0;
1901
1242
  let n = (e) => t.filter((t) => t.kind === e).map((e) => e.file);
1902
- return Y("will NEVER route — page.tsx outside src/pages/", n("suffixed-outside-pages"), "the name says \"route\", the location says otherwise, and the location wins: the URL comes from the directory chain under src/pages/. Move it into the directory whose route it serves, or rename it."), Y("pages with no test beside them", n("page-without-test"), "a page and its test belong together — `users/[id].page.tsx` → `users/[id].page.test.tsx`. Advisory only; a missing test is a gap, not a defect."), Y("routes whose FIRST segment is dynamic — they claim every URL of that depth", n("top-level-dynamic-route"), "a route like `[id]/[code]` answers `/api/health`, `/internal/readiness` and anything else with that many segments on this origin. That is what the pattern means, so it is not a bug — but it is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Give the route a literal first segment (`scan/[id]/[code]`) if you did not intend to own the whole origin at that depth. Advisory."), Y("pages with no `meta` export — they serve the APP name as their title", n("page-without-meta"), "a page with no `meta` sets no title, so the response carries `<title>{your app.config name}</title>` — the same string on every such page, to every reader that does not run JavaScript: link previews, crawlers, a bookmark taken before hydration, a screen reader announcing the document. The client router fixes the tab after hydration, which is exactly why this is easy to miss. Add `export const meta = { title: \"…\" }` (or a function of params/loaderData). If such a page also has no real body — it redirects from an effect, say — the missing title is the symptom rather than the bug: on an `ssr` route the server answers 200 with an empty document and the redirect happens only once JavaScript runs. Move it into the loader (`redirect(...)`, a real 303). Advisory."), t.length;
1903
- }, wn = "voltro doctor [app] [flags]\n\nPreflight an app root: verify it can boot under production `voltro serve`,\nreport shipped primitives the source looks to be hand-rolling, and flag pages\nthat are safe `renderMode:'spa'` candidates.\n\nArguments:\n [app] App directory to check (default: the current directory).\n\nFlags:\n --strict Treat a missing serve bundle as blocking regardless of NODE_ENV\n (the `voltro serve --preflight` behavior). Without it, a missing\n bundle only fails when NODE_ENV=production.\n --json Print the complete scan as JSON — hand-roll findings AND spa\n candidates, every file path, nothing elided. Skips the preflight output.\n --write-authz-allowlist\n Record today's executors that reference NO access check into\n voltro-authz-allowlist.txt, so every later run fails only on\n ADDITIONS. The list is DEBT, not approval — an entry stops\n mattering as soon as its executor gains a guard.\n -h, --help Print this help.", Tn = /* @__PURE__ */ new Map(), X = (e) => {
1904
- let t = Tn.get(e);
1243
+ return H("will NEVER route — page.tsx outside src/pages/", n("suffixed-outside-pages"), "the name says \"route\", the location says otherwise, and the location wins: the URL comes from the directory chain under src/pages/. Move it into the directory whose route it serves, or rename it."), H("pages with no test beside them", n("page-without-test"), "a page and its test belong together — `users/[id].page.tsx` → `users/[id].page.test.tsx`. Advisory only; a missing test is a gap, not a defect."), H("routes whose FIRST segment is dynamic — they claim every URL of that depth", n("top-level-dynamic-route"), "a route like `[id]/[code]` answers `/api/health`, `/internal/readiness` and anything else with that many segments on this origin. That is what the pattern means, so it is not a bug — but it is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Give the route a literal first segment (`scan/[id]/[code]`) if you did not intend to own the whole origin at that depth. Advisory."), H("pages with no `meta` export — they serve the APP name as their title", n("page-without-meta"), "a page with no `meta` sets no title, so the response carries `<title>{your app.config name}</title>` — the same string on every such page, to every reader that does not run JavaScript: link previews, crawlers, a bookmark taken before hydration, a screen reader announcing the document. The client router fixes the tab after hydration, which is exactly why this is easy to miss. Add `export const meta = { title: \"…\" }` (or a function of params/loaderData). If such a page also has no real body — it redirects from an effect, say — the missing title is the symptom rather than the bug: on an `ssr` route the server answers 200 with an empty document and the redirect happens only once JavaScript runs. Move it into the loader (`redirect(...)`, a real 303). Advisory."), t.length;
1244
+ }, Rt = "voltro doctor [app] [flags]\n\nPreflight an app root: verify it can boot under production `voltro serve`,\nreport shipped primitives the source looks to be hand-rolling, and flag pages\nthat are safe `renderMode:'spa'` candidates.\n\nArguments:\n [app] App directory to check (default: the current directory).\n\nFlags:\n --strict Treat a missing serve bundle as blocking regardless of NODE_ENV\n (the `voltro serve --preflight` behavior). Without it, a missing\n bundle only fails when NODE_ENV=production.\n --json Print the complete scan as JSON — hand-roll findings AND spa\n candidates, every file path, nothing elided. Skips the preflight output.\n --write-authz-allowlist\n Record today's executors that reference NO access check into\n voltro-authz-allowlist.txt, so every later run fails only on\n ADDITIONS. The list is DEBT, not approval — an entry stops\n mattering as soon as its executor gains a guard.\n -h, --help Print this help.", zt = /* @__PURE__ */ new Map(), U = (e) => {
1245
+ let t = zt.get(e);
1905
1246
  if (t !== void 0) return t;
1906
1247
  let n = (async () => {
1907
- let { walk: t, loadDiscovered: n } = await import("./dev-iiMtlkfs.js"), r = await t(e);
1248
+ let { walk: t, loadDiscovered: n } = await import("./dev-CoG-ZPx8.js"), r = await t(e);
1908
1249
  return {
1909
1250
  files: r,
1910
1251
  discovered: await n(r)
1911
1252
  };
1912
1253
  })();
1913
- return Tn.set(e, n), n;
1914
- }, En = async (e) => {
1254
+ return zt.set(e, n), n;
1255
+ }, Bt = async (e) => {
1915
1256
  let t;
1916
1257
  try {
1917
- let { files: n, discovered: r } = await X(e), i = r.tables.map((e) => {
1258
+ let { files: n, discovered: r } = await U(e), i = r.tables.map((e) => {
1918
1259
  let t = e;
1919
1260
  return {
1920
1261
  name: t.tableName,
@@ -1922,12 +1263,12 @@ var le = () => {
1922
1263
  };
1923
1264
  });
1924
1265
  if (i.length === 0) return;
1925
- t = Je(qe(n, i, e));
1266
+ t = pe(fe(n, i, e));
1926
1267
  } catch {
1927
1268
  return;
1928
1269
  }
1929
1270
  t !== null && (console.log(""), console.log(`${t.includes("does not exist") ? "✗" : "✓"} ${t}`));
1930
- }, Dn = (e) => e.map((e) => {
1271
+ }, Vt = (e) => e.map((e) => {
1931
1272
  let t = e, n = Object.entries(t.fields ?? {}).map(([e, t]) => ({
1932
1273
  name: e,
1933
1274
  type: typeof t?.type == "string" ? t.type : ""
@@ -1937,22 +1278,22 @@ var le = () => {
1937
1278
  columns: n,
1938
1279
  primaryKey: t.appliedPrimaryKey ?? []
1939
1280
  };
1940
- }), Z = async (e) => {
1281
+ }), W = async (e) => {
1941
1282
  try {
1942
- let { discovered: t } = await X(e);
1943
- return $e(Dn(t.tables));
1283
+ let { discovered: t } = await U(e);
1284
+ return ve(Vt(t.tables));
1944
1285
  } catch {
1945
1286
  return null;
1946
1287
  }
1947
- }, On = async (e) => {
1948
- let t = await Z(e);
1288
+ }, Ht = async (e) => {
1289
+ let t = await W(e);
1949
1290
  if (t === null || t.tablesChecked === 0) return;
1950
- let n = tt(t);
1291
+ let n = be(t);
1951
1292
  n !== null && (console.log(""), console.log(`${t.issues.length > 0 ? "✗" : "✓"} ${n}`));
1952
- }, kn = async (e) => {
1293
+ }, G = async (e) => {
1953
1294
  try {
1954
- let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await X(e);
1955
- return at(n.events.map((e) => {
1295
+ let { payloadPropertyNames: t } = await import("@voltro/runtime"), { discovered: n } = await U(e);
1296
+ return we(n.events.map((e) => {
1956
1297
  let n = e.descriptor, r = t(n.key);
1957
1298
  return {
1958
1299
  name: n.name,
@@ -1964,19 +1305,19 @@ var le = () => {
1964
1305
  } catch {
1965
1306
  return null;
1966
1307
  }
1967
- }, An = async (e) => {
1968
- let t = await kn(e);
1308
+ }, Ut = async (e) => {
1309
+ let t = await G(e);
1969
1310
  if (t === null || t.eventsChecked === 0) return;
1970
- let n = ot(t);
1311
+ let n = Te(t);
1971
1312
  n !== null && (console.log(""), console.log(`• ${n}`));
1972
- let r = st(t);
1313
+ let r = Ee(t);
1973
1314
  r !== null && (console.log(""), console.log(`⚠️ ${r}`));
1974
- }, jn = (e) => {
1315
+ }, K = (e) => {
1975
1316
  let t = e, n = typeof t?.message == "string" && t.message !== "" ? t.message : String(e), r = typeof t?.code == "string" ? t.code : void 0;
1976
1317
  return r === void 0 ? n : `${r}: ${n}`;
1977
- }, Mn = async (e) => {
1318
+ }, q = async (e) => {
1978
1319
  try {
1979
- let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await X(e)).discovered.serverOnlyLeaks, r = t(n);
1320
+ let { formatServerOnlyLeaks: t } = await import("@voltro/runtime"), n = (await U(e)).discovered.serverOnlyLeaks, r = t(n);
1980
1321
  return {
1981
1322
  status: {
1982
1323
  checked: !0,
@@ -1987,17 +1328,114 @@ var le = () => {
1987
1328
  } catch (e) {
1988
1329
  return { status: {
1989
1330
  checked: !1,
1990
- reason: jn(e)
1331
+ reason: K(e)
1991
1332
  } };
1992
1333
  }
1993
- }, Nn = async (e) => {
1994
- let { status: t, message: n } = await Mn(e);
1334
+ }, J = async (r) => {
1335
+ try {
1336
+ let { loadApiConfig: i } = await import("./dev-CoG-ZPx8.js"), { discovered: a } = await U(r), o = n(await i(r)), s = {
1337
+ queries: a.queries,
1338
+ mutations: a.mutations,
1339
+ actions: a.actions,
1340
+ streams: a.streams
1341
+ };
1342
+ return {
1343
+ checked: !0,
1344
+ defaultDeny: o,
1345
+ undecided: e(s),
1346
+ openAccess: t(s)
1347
+ };
1348
+ } catch (e) {
1349
+ return {
1350
+ checked: !1,
1351
+ reason: K(e),
1352
+ defaultDeny: !0,
1353
+ undecided: [],
1354
+ openAccess: []
1355
+ };
1356
+ }
1357
+ }, Wt = async (e) => {
1358
+ let t = await J(e);
1359
+ if (!t.checked) return console.log(""), console.log("• access decisions: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
1360
+ console.log(""), console.log(`access decisions · security.defaultDeny ${t.defaultDeny ? "ON" : "OFF"}`), console.log(` ✗ no access decision ${String(t.undecided.length).padStart(4)}`), console.log(` ✓ openAccess, declared on purpose ${String(t.openAccess.length).padStart(4)}`);
1361
+ for (let e of t.openAccess) console.log(` ${e.tag} — ${e.openAccess ?? ""}`);
1362
+ if (t.undecided.length === 0) return !1;
1363
+ console.log("");
1364
+ for (let e of t.undecided) console.log(` ✗ ${e.tag} (${e.kind})${e.file === void 0 ? "" : `\n ${e.file}`}`);
1365
+ return console.log(""), t.defaultDeny ? (console.log(" `voltro dev` and `voltro serve` REFUSE to boot on these. Give each a decision:"), console.log(" `guards: [{ scope: '…' }]`, or `openAccess: '<why anyone may call it>'`."), !0) : (console.log(" This app sets `security: { defaultDeny: false }`, so these BOOT — and each one is"), console.log(" callable by any authenticated session. Advisory here for that reason only."), !1);
1366
+ }, Y = async (e) => {
1367
+ try {
1368
+ let { files: t } = await U(e), { WEBHOOK_PATTERN: n, loadWebhookFiles: r } = await import("./webhookDiscovery-CrGAfhIG.js"), { incomingWebhookVerification: i } = await import("@voltro/plugin-webhooks"), a = t.filter((e) => n.test(ie(e)));
1369
+ if (a.length === 0) return {
1370
+ checked: !0,
1371
+ unverified: [],
1372
+ open: [],
1373
+ verified: 0
1374
+ };
1375
+ let o = await r(a, ce({ scope: "voltro:doctor" })), s = [], c = [], l = 0;
1376
+ for (let e of o.incoming) {
1377
+ let t = i(e.descriptor);
1378
+ t === null ? s.push({
1379
+ id: e.descriptor.id,
1380
+ file: e.file
1381
+ }) : t === "none" ? c.push({
1382
+ id: e.descriptor.id,
1383
+ file: e.file
1384
+ }) : l += 1;
1385
+ }
1386
+ return {
1387
+ checked: !0,
1388
+ unverified: s,
1389
+ open: c,
1390
+ verified: l
1391
+ };
1392
+ } catch (e) {
1393
+ return {
1394
+ checked: !1,
1395
+ reason: K(e),
1396
+ unverified: [],
1397
+ open: [],
1398
+ verified: 0
1399
+ };
1400
+ }
1401
+ }, Gt = async (e) => {
1402
+ let t = await Y(e);
1403
+ if (!t.checked) return console.log(""), console.log("• webhook verification: NOT CHECKED — the app's webhooks could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), !1;
1404
+ if (t.verified + t.open.length + t.unverified.length === 0) return !1;
1405
+ console.log(""), console.log("incoming webhook verification"), console.log(` ✓ signature / provider verified ${String(t.verified).padStart(4)}`), console.log(` ! deliberately public (none) ${String(t.open.length).padStart(4)}`), console.log(` ✗ nothing verifies the caller ${String(t.unverified.length).padStart(4)}`);
1406
+ for (let e of t.open) console.log(` ${e.id} — \`verification: 'none'\`; a gateway / IP allow-list owns this URL`), console.log(` ${e.file}`);
1407
+ if (t.unverified.length === 0) return !1;
1408
+ console.log("");
1409
+ for (let e of t.unverified) console.log(` ✗ ${e.id}\n ${e.file}`);
1410
+ return console.log(""), console.log(" These are public POSTs that run your application code, and `voltro dev` /"), console.log(" `voltro serve` REFUSE to mount them. Declare one of: `provider:` (a preset),"), console.log(" `signature:` (your own HMAC scheme), `verification: 'provider'` (your handler"), console.log(" verifies), or `verification: 'none'` (deliberately public)."), !0;
1411
+ }, Kt = async (e) => {
1412
+ let { status: t, message: n } = await q(e);
1995
1413
  return t.checked ? n === void 0 ? !1 : (console.log(""), console.log(`✗ [serverOnly] ${n}`), !0) : (console.log(""), console.log("• serverOnly: NOT CHECKED — the app's descriptors could not be loaded (not a pass)"), console.log(` reason: ${t.reason ?? "unknown"}`), console.log(" This audit is the automated check that a hash or a token cannot reach the wire, so"), console.log(" an app that leans on `.serverOnly()` should treat a persistent skip as a failure."), console.log(" `voltro doctor --json` reports it as `serverOnly.checked: false` for a CI gate."), !1);
1996
- }, Pn = async (e) => {
1414
+ }, qt = async (e) => Xe({
1415
+ hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
1416
+ openStore: async () => {
1417
+ let { loadApiConfig: t, buildStore: n } = await import("./dev-CoG-ZPx8.js"), r = await t(e);
1418
+ if (r === null) throw Error("no app.config.ts in this directory");
1419
+ let i = await n(r, void 0);
1420
+ return {
1421
+ store: i,
1422
+ close: async () => {
1423
+ await i.close?.();
1424
+ }
1425
+ };
1426
+ },
1427
+ sweep: async (e) => {
1428
+ let { sweepStalledRuns: t } = await import("@voltro/workflow");
1429
+ return t({ store: e });
1430
+ }
1431
+ }), Jt = async (e) => {
1432
+ let t = Qe(await qt(e));
1433
+ t !== null && (console.log(""), console.log(t));
1434
+ }, Yt = async (e) => {
1997
1435
  let t;
1998
1436
  try {
1999
- let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await X(e);
2000
- t = ut(r, i.workflows.map((e) => {
1437
+ let { payloadPropertyNames: n } = await import("@voltro/runtime"), { files: r, discovered: i } = await U(e);
1438
+ t = ke(r, i.workflows.map((e) => {
2001
1439
  let t = e.definition, r = t.payloadSchema === void 0 ? {
2002
1440
  required: [],
2003
1441
  optional: []
@@ -2011,68 +1449,68 @@ var le = () => {
2011
1449
  } catch {
2012
1450
  return;
2013
1451
  }
2014
- let n = dt(t);
1452
+ let n = Ae(t);
2015
1453
  if (n === null) return;
2016
1454
  console.log("");
2017
1455
  let r = t.issues.length > 0 ? "✗" : t.unchecked.length > 0 ? "•" : "✓";
2018
1456
  console.log(`${r} ${n}`);
2019
- }, Q = "voltro-authz-allowlist.txt", Fn = (e, t = (e) => {
1457
+ }, X = "voltro-authz-allowlist.txt", Xt = (e, t = (e) => {
2020
1458
  try {
2021
- return E(e, "utf8");
1459
+ return S(e, "utf8");
2022
1460
  } catch {
2023
1461
  return "";
2024
1462
  }
2025
- }) => /* @__PURE__ */ new Set([...vt, ...yt(q(e).map(t))]), In = (e, t) => {
1463
+ }) => /* @__PURE__ */ new Set([...j, ...Ie(R(e).map(t))]), Zt = (e, t) => {
2026
1464
  let n = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2027
1465
  return new RegExp(String.raw`(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*=\s*define[A-Za-z]*Event\s*\(\s*\{[^}]*?name\s*:\s*['"\`]${n}['"\`]`, "s").exec(e)?.[1];
2028
- }, Ln = async (e) => {
1466
+ }, Z = async (e) => {
2029
1467
  try {
2030
- let { discovered: t } = await X(e), n = t.events.map((e) => ({
1468
+ let { discovered: t } = await U(e), n = t.events.map((e) => ({
2031
1469
  name: e.descriptor.name,
2032
1470
  file: e.file
2033
1471
  })).filter((e) => typeof e.name == "string");
2034
1472
  if (n.length === 0) return [];
2035
1473
  let r = new Set(t.events.map((e) => e.file)), i = (e) => {
2036
1474
  try {
2037
- return E(e, "utf8");
1475
+ return S(e, "utf8");
2038
1476
  } catch {
2039
1477
  return "";
2040
1478
  }
2041
- }, a = q(e).filter((e) => !r.has(e)).map(i).join("\n");
1479
+ }, a = R(e).filter((e) => !r.has(e)).map(i).join("\n");
2042
1480
  return n.filter(({ name: e, file: t }) => {
2043
1481
  if (a.includes(e)) return !1;
2044
- let n = In(i(t), e);
1482
+ let n = Zt(i(t), e);
2045
1483
  return n === void 0 || !new RegExp(String.raw`\b${n}\b`).test(a);
2046
1484
  }).map((e) => e.name).sort();
2047
1485
  } catch {
2048
1486
  return [];
2049
1487
  }
2050
- }, Rn = async (e) => {
2051
- let t = await Ln(e);
1488
+ }, Qt = async (e) => {
1489
+ let t = await Z(e);
2052
1490
  if (t.length !== 0) {
2053
1491
  console.log(""), console.log(`• ${t.length} declared event(s) with no emit call site found:`);
2054
1492
  for (let e of t) console.log(` ${e}`);
2055
1493
  console.log(""), console.log(" An event you declare is an event partners can subscribe to. One that"), console.log(" never fires is indistinguishable, from their side, from a broken"), console.log(" endpoint of their own — so they debug your contract instead of"), console.log(" reading it."), console.log(" ADVISORY: this looks for the event name AND the identifier its"), console.log(" declaration binds it to, so `emit(ctx, apiKeyCreated, …)` counts. An"), console.log(" emit through an ASSEMBLED name still reads as missing here.");
2056
1494
  }
2057
- }, zn = async (e) => {
1495
+ }, $t = async (e) => {
2058
1496
  try {
2059
- let { loadApiConfig: t } = await import("./dev-iiMtlkfs.js"), n = (await t(e))?.doctor?.scopeVocabulary;
1497
+ let { loadApiConfig: t } = await import("./dev-CoG-ZPx8.js"), n = (await t(e))?.doctor?.scopeVocabulary;
2060
1498
  if (typeof n != "string") return [];
2061
1499
  let [r, i] = n.split("#");
2062
1500
  if (r === void 0 || i === void 0) return [];
2063
- let a = (await import(oe(C(e, r)).href))[i];
1501
+ let a = (await import(se(y(e, r)).href))[i];
2064
1502
  return Array.isArray(a) ? a.filter((e) => typeof e == "string") : [];
2065
1503
  } catch {
2066
1504
  return [];
2067
1505
  }
2068
- }, Bn = async (e) => {
1506
+ }, Q = async (e) => {
2069
1507
  let t = {
2070
1508
  evaluated: !1,
2071
1509
  vocabularySize: 0,
2072
1510
  findings: []
2073
1511
  };
2074
1512
  try {
2075
- let { offlineManifest: n, toInput: r } = await import("./checkCommand-DRovTKza.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-CvQCte0z.js"), o = await n(e), s = r(o.manifest, o.tables), c = await zn(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
1513
+ let { offlineManifest: n, toInput: r } = await import("./checkCommand-YZDmkAGd.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-CvQCte0z.js"), o = await n(e), s = r(o.manifest, o.tables), c = await $t(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
2076
1514
  if (l.length === 0) return t;
2077
1515
  let u = i({
2078
1516
  ...s,
@@ -2091,8 +1529,8 @@ var le = () => {
2091
1529
  } catch {
2092
1530
  return t;
2093
1531
  }
2094
- }, Vn = async (e) => {
2095
- let { findings: t, evaluated: n } = await Bn(e);
1532
+ }, en = async (e) => {
1533
+ let { findings: t, evaluated: n } = await Q(e);
2096
1534
  if (!n) {
2097
1535
  console.log(""), console.log("• scope vocabulary: none found — the unknown-scope rule did NOT run."), console.log(" It compares each procedure's required scopes against the scopes your"), console.log(" roles actually grant, and needs somewhere to read that set from."), console.log(" `@voltro/plugin-rbac` publishes it from its `roles` map; an app doing"), console.log(" RBAC without the plugin can point at its own:"), console.log(""), console.log(" // app.config.ts"), console.log(" doctor: { scopeVocabulary: './lib/teamRoles.ts#ALL_TEAM_SCOPES' }"), console.log(""), console.log(" Until then this section is NOT a clean bill of health — a procedure"), console.log(" requiring a scope no role grants is uncallable by everyone, and"), console.log(" nothing here would have told you.");
2098
1536
  return;
@@ -2102,35 +1540,35 @@ var le = () => {
2102
1540
  for (let e of t) console.log(` ${e.node}${e.file === void 0 ? "" : ` ${e.file}`}`), console.log(` ${e.detail}`);
2103
1541
  console.log(""), console.log(" Each of these is uncallable by every user in every role — not"), console.log(" misconfigured, DEAD — and nothing fails at boot to say so. The"), console.log(" usual cause is a typo or a rename that moved one side only."), console.log(` ${t[0].fix}`);
2104
1542
  }
2105
- }, Hn = async (e) => {
1543
+ }, tn = async (e) => {
2106
1544
  let t = ["src/locales", "locales"], n, r = [];
2107
1545
  for (let i of t) {
2108
- let t = x(e, i);
2109
- if (T(t)) try {
2110
- r = D(t).filter((e) => /\.tsx?$/.test(e) && !e.endsWith(".d.ts")).map((e) => e.replace(/\.tsx?$/, "")), n = i;
1546
+ let t = _(e, i);
1547
+ if (x(t)) try {
1548
+ r = C(t).filter((e) => /\.tsx?$/.test(e) && !e.endsWith(".d.ts")).map((e) => e.replace(/\.tsx?$/, "")), n = i;
2111
1549
  break;
2112
1550
  } catch {}
2113
1551
  }
2114
1552
  if (n === void 0 || r.length === 0) return;
2115
1553
  let i = [];
2116
1554
  try {
2117
- let { loadApiConfig: t } = await import("./dev-iiMtlkfs.js");
1555
+ let { loadApiConfig: t } = await import("./dev-CoG-ZPx8.js");
2118
1556
  i = (await t(e))?.locales ?? [];
2119
1557
  } catch {
2120
1558
  return;
2121
1559
  }
2122
- let a = ht({
1560
+ let a = Pe({
2123
1561
  catalogCodes: r,
2124
1562
  declaredLocales: i
2125
1563
  });
2126
1564
  if (a !== void 0) {
2127
1565
  console.log("");
2128
- for (let e of gt(a, n)) console.log(e);
1566
+ for (let e of Fe(a, n)) console.log(e);
2129
1567
  }
2130
- }, Un = async (e) => {
2131
- let { discovered: t } = await X(e), n = [];
1568
+ }, nn = async (e) => {
1569
+ let { discovered: t } = await U(e), n = [];
2132
1570
  try {
2133
- let { loadApiConfig: t } = await import("./dev-iiMtlkfs.js");
1571
+ let { loadApiConfig: t } = await import("./dev-CoG-ZPx8.js");
2134
1572
  n = ((await t(e))?.plugins ?? []).map((e) => {
2135
1573
  let t = (e.extendSchema?.tables ?? []).map((e) => {
2136
1574
  let t = e;
@@ -2138,16 +1576,13 @@ var le = () => {
2138
1576
  tableName: typeof t.tableName == "string" ? t.tableName : "",
2139
1577
  renamedFrom: typeof t.renamedFrom == "string" ? t.renamedFrom : void 0
2140
1578
  };
2141
- }).filter((e) => e.tableName !== ""), n = (e.rpcClientDescriptors ?? []).map((e) => {
2142
- let t = e, n = t.descriptor?.name ?? t.name;
2143
- return typeof n == "string" ? n : "";
2144
- }).filter((e) => e !== "");
1579
+ }).filter((e) => e.tableName !== ""), n = (e.rpcClientDescriptors ?? []).map((t) => typeof t.tag == "string" ? u(e, t.tag) : "").filter((e) => e !== ""), r = e.baseName ?? e.name;
2145
1580
  return {
2146
1581
  plugin: e.name,
2147
1582
  tables: t,
2148
1583
  rpcTags: n,
2149
- canDisableTables: Wn.has(e.name),
2150
- canAlias: Gn.has(e.name)
1584
+ canDisableTables: rn.has(r),
1585
+ canAlias: an.has(r)
2151
1586
  };
2152
1587
  });
2153
1588
  } catch {
@@ -2159,14 +1594,36 @@ var le = () => {
2159
1594
  ...t.mutations,
2160
1595
  ...t.actions,
2161
1596
  ...t.streams ?? []
2162
- ].map((e) => String(e.descriptor.name)), i = Mt(n, {
1597
+ ].map((e) => String(e.descriptor.name)), i = Je(n, {
2163
1598
  tables: t.tables.map((e) => e.tableName),
2164
1599
  rpcTags: r
2165
1600
  });
2166
1601
  console.log();
2167
- for (let e of Nt(n, i)) console.log(e);
2168
- }, Wn = /* @__PURE__ */ new Set(["@voltro/plugin-rbac"]), Gn = /* @__PURE__ */ new Set(["@voltro/plugin-ai-flows"]), $ = 20, Kn = async (e) => {
2169
- let t = await qn(e), n = new Set(t.reviewed.map((e) => e.tag)), r = t.result.findings.filter((e) => e.verdict.kind === "allowlisted" && !n.has(e.tag)).length;
1602
+ for (let e of Ye(n, i)) console.log(e);
1603
+ }, rn = /* @__PURE__ */ new Set([
1604
+ "@voltro/plugin-rbac",
1605
+ "@voltro/plugin-audit",
1606
+ "@voltro/plugin-notifications",
1607
+ "aiFlows"
1608
+ ]), an = /* @__PURE__ */ new Set([
1609
+ "aiFlows",
1610
+ "@voltro/plugin-audit",
1611
+ "@voltro/plugin-billing",
1612
+ "@voltro/plugin-cdc-out",
1613
+ "@voltro/plugin-flags",
1614
+ "@voltro/plugin-governance",
1615
+ "@voltro/plugin-mail",
1616
+ "@voltro/plugin-moderation",
1617
+ "@voltro/plugin-notifications",
1618
+ "@voltro/plugin-presence",
1619
+ "@voltro/plugin-rbac",
1620
+ "@voltro/plugin-scim",
1621
+ "@voltro/plugin-search",
1622
+ "@voltro/plugin-sso-saml",
1623
+ "@voltro/plugin-storage",
1624
+ "@voltro/plugin-versioning"
1625
+ ]), $ = 20, on = async (e) => {
1626
+ let t = await sn(e), n = new Set(t.reviewed.map((e) => e.tag)), r = t.result.findings.filter((e) => e.verdict.kind === "allowlisted" && !n.has(e.tag)).length;
2170
1627
  return {
2171
1628
  executors: t.executorCount,
2172
1629
  guardVocabulary: [...t.vocabulary].sort(),
@@ -2178,15 +1635,15 @@ var le = () => {
2178
1635
  },
2179
1636
  unchecked: t.result.unchecked
2180
1637
  };
2181
- }, qn = async (e) => {
2182
- let { discovered: t } = await X(e), n = /* @__PURE__ */ new Map(), r = (e) => {
1638
+ }, sn = async (e) => {
1639
+ let { discovered: t } = await U(e), n = /* @__PURE__ */ new Map(), r = (e) => {
2183
1640
  if (!n.has(e)) try {
2184
- n.set(e, E(e, "utf8"));
1641
+ n.set(e, S(e, "utf8"));
2185
1642
  } catch {
2186
1643
  n.set(e, "");
2187
1644
  }
2188
1645
  return n.get(e);
2189
- }, i = Fn(e, r), a = /* @__PURE__ */ new Set();
1646
+ }, i = Xt(e, r), a = /* @__PURE__ */ new Set();
2190
1647
  for (let e of t.tables) for (let t of Object.values(e.fields ?? {})) try {
2191
1648
  let e = t.references?.();
2192
1649
  e && a.add(e.tableName);
@@ -2218,12 +1675,12 @@ var le = () => {
2218
1675
  op: a?.op === "delete" || a?.op === "insert" ? a.op : t,
2219
1676
  ...a?.table === void 0 ? {} : { table: a.table }
2220
1677
  };
2221
- })), s = x(e, Q), c = T(s) ? W(E(s, "utf8")) : void 0, l = !1;
2222
- for (let t of q(e)) if (/\bsetRowFilter\s*[<(]/.test(r(t))) {
1678
+ })), s = _(e, X), c = x(s) ? M(S(s, "utf8")) : void 0, l = !1;
1679
+ for (let t of R(e)) if (/\bsetRowFilter\s*[<(]/.test(r(t))) {
2223
1680
  l = !0;
2224
1681
  break;
2225
1682
  }
2226
- let u = Tt({
1683
+ let u = He({
2227
1684
  executors: o,
2228
1685
  guardVocabulary: i,
2229
1686
  tenantScopedTables: t.tenantScopedTables,
@@ -2240,261 +1697,46 @@ var le = () => {
2240
1697
  executorCount: o.length,
2241
1698
  allowPath: s
2242
1699
  };
2243
- }, Jn = async (e, t) => {
2244
- let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await qn(e), l = n.counts;
1700
+ }, cn = async (e, t) => {
1701
+ let { result: n, vocabulary: r, existing: i, reviewed: a, executorCount: o, allowPath: s, rowFilterRegistered: c } = await sn(e), l = n.counts;
2245
1702
  console.log(`\nauthz scan · ${o} executor(s)`);
2246
- let u = [...r].filter((e) => !vt.has(e)).sort();
1703
+ let u = [...r].filter((e) => !j.has(e)).sort();
2247
1704
  console.log(u.length === 0 ? " guard vocabulary: framework names only — no exported require*/assert* found in this app" : ` guard vocabulary: ${u.length} from your source (${u.slice(0, 6).join(", ")}${u.length > 6 ? ", …" : ""})`), console.log(` ✗ no access check ${String(l.unchecked).padStart(4)}`), console.log(` ⚠ inline ownership check, no named guard${String(l.inlineOwnership).padStart(4)} (informational)`), console.log(` ✓ guards: on the descriptor ${String(l.declaredGuard).padStart(4)}`), console.log(` ✓ calls a guard from the vocabulary ${String(l.vocabularyGuard).padStart(4)}`);
2248
1705
  let d = new Set(a.map((e) => e.tag)), f = n.findings.filter((e) => e.verdict.kind === "allowlisted" && !d.has(e.tag)).length, p = l.allowlisted - f;
2249
- if (console.log(` – accepted as recorded debt ${String(f).padStart(4)} (${Q})`), console.log(` ✓ reviewed, open on purpose ${String(p).padStart(4)} (\`reviewed=<why>\` in that file)`), c && console.log(" ⓘ this app registers setRowFilter — the scan CANNOT see through it. A finding below may\n already be constrained; verify the filter covers that table and record it as `reviewed=`."), t) {
2250
- let e = T(s) ? E(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = kt(e, t);
2251
- return ce(s, Ot(e, t), "utf8"), console.log(`\n${Q}: +${r.added} new, ${r.kept} kept, ${r.removed} removed. Doctor now fails on ADDITIONS only.`), r.added === 0 && console.log(" (nothing new was unchecked — the file is unchanged.)"), !1;
1706
+ if (console.log(` – accepted as recorded debt ${String(f).padStart(4)} (${X})`), console.log(` ✓ reviewed, open on purpose ${String(p).padStart(4)} (\`reviewed=<why>\` in that file)`), c && console.log(" ⓘ this app registers setRowFilter — the scan CANNOT see through it. A finding below may\n already be constrained; verify the filter covers that table and record it as `reviewed=`."), t) {
1707
+ let e = x(s) ? S(s, "utf8") : void 0, t = n.unchecked.map((e) => e.tag), r = Ke(e, t);
1708
+ return oe(s, Ge(e, t), "utf8"), console.log(`\n${X}: +${r.added} new, ${r.kept} kept, ${r.removed} removed. Doctor now fails on ADDITIONS only.`), r.added === 0 && console.log(" (nothing new was unchecked — the file is unchanged.)"), !1;
2252
1709
  }
2253
1710
  if (n.unchecked.length === 0) return !1;
2254
1711
  console.log("");
2255
1712
  for (let e of n.unchecked.slice(0, $)) console.log(` ✗ ${e.tag} — ${e.why}\n ${e.path}`);
2256
- return n.unchecked.length > $ && console.log(` … and ${n.unchecked.length - $} more — \`voltro doctor --json\` prints every one (\`authz.unchecked\`).`), i === void 0 ? (console.log(`\nNo ${Q} yet, so this run is advisory. To start the ratchet:`), console.log("\n voltro doctor --write-authz-allowlist\n"), console.log("That records today's unchecked handlers as DEBT and fails every run after on"), console.log("anything NEW. Fix a handler and its line stops mattering — the scan reports it as"), console.log("guarded whether or not the line is still there."), !1) : (console.log(`\n${n.unchecked.length} executor(s) with no access check are not in ${Q}.`), console.log("Add a guard, or add the tag to that file if it is genuinely open."), console.log("\nBefore hand-rolling another check: `guards: [{ action, resourceType, resource }]`"), console.log("answers \"may this subject act on THIS row\" declaratively, and an app whose"), console.log("relationships live in its own tables registers its own tuple source rather than"), console.log("copying data into a framework table. See the authorization docs."), !0);
2257
- }, Yn = async (e) => {
2258
- if (e.includes("--help") || e.includes("-h")) return console.log(wn), 0;
2259
- let n = e.includes("--strict"), r = C(e.find((e) => !e.startsWith("-")) ?? process.cwd());
2260
- if (ue(e)) return le(), M({
2261
- ...rn(r),
2262
- spaCandidates: await hn(r),
2263
- pageConvention: J(r),
2264
- fileTaxonomy: V(r).findings,
2265
- junctionFks: (await Z(r))?.issues ?? [],
2266
- eventDelivery: await kn(r),
2267
- duplicateInstances: t(r),
2268
- versionSkew: s(r) ?? null,
2269
- serverOnly: (await Mn(r)).status,
2270
- authz: await Kn(r),
2271
- ungrantedScopes: await Bn(r),
2272
- neverEmittedEvents: await Ln(r)
1713
+ return n.unchecked.length > $ && console.log(` … and ${n.unchecked.length - $} more — \`voltro doctor --json\` prints every one (\`authz.unchecked\`).`), i === void 0 ? (console.log(`\nNo ${X} yet, so this run is advisory. To start the ratchet:`), console.log("\n voltro doctor --write-authz-allowlist\n"), console.log("That records today's unchecked handlers as DEBT and fails every run after on"), console.log("anything NEW. Fix a handler and its line stops mattering — the scan reports it as"), console.log("guarded whether or not the line is still there."), !1) : (console.log(`\n${n.unchecked.length} executor(s) with no access check are not in ${X}.`), console.log("Add a guard, or add the tag to that file if it is genuinely open."), console.log("\nBefore hand-rolling another check: `guards: [{ action, resourceType, resource }]`"), console.log("answers \"may this subject act on THIS row\" declaratively, and an app whose"), console.log("relationships live in its own tables registers its own tuple source rather than"), console.log("copying data into a framework table. See the authorization docs."), !0);
1714
+ }, ln = async (e) => {
1715
+ if (e.includes("--help") || e.includes("-h")) return console.log(Rt), 0;
1716
+ let t = e.includes("--strict"), n = y(e.find((e) => !e.startsWith("-")) ?? process.cwd());
1717
+ if (h(e)) return g(), m({
1718
+ ...z(n),
1719
+ spaCandidates: await B(n),
1720
+ pageConvention: V(n),
1721
+ fileTaxonomy: re(n).findings,
1722
+ junctionFks: (await W(n))?.issues ?? [],
1723
+ eventDelivery: await G(n),
1724
+ duplicateInstances: a(n),
1725
+ versionSkew: o(n) ?? null,
1726
+ serverOnly: (await q(n)).status,
1727
+ accessDecisions: await J(n),
1728
+ webhookVerification: await Y(n),
1729
+ authz: await on(n),
1730
+ ungrantedScopes: await Q(n),
1731
+ neverEmittedEvents: await Z(n),
1732
+ stalledRuns: await qt(n)
2273
1733
  }), 0;
2274
- let a = Lt(r, n);
2275
- un(r), ln(r), Cn(r), Ue(r), await _n(r), await Pn(r), await En(r), await On(r), await An(r), await Un(r), await Vn(r), await Rn(r), await Hn(r);
2276
- let c = await Nn(r), l = await Jn(r, e.includes("--write-authz-allowlist")), u = o(s(r));
2277
- u !== void 0 && (console.log(""), console.log(u));
2278
- let d = i(t(r));
2279
- return d !== void 0 && console.log(`\n${d}`), c || l ? Math.max(a, 1) : a;
2280
- }, Xn = (e) => e === void 0 ? [] : typeof e == "string" ? [e] : e, Zn = /* @__PURE__ */ new Set([
2281
- "insert",
2282
- "update",
2283
- "delete",
2284
- "upsert"
2285
- ]), Qn = (e, t) => {
2286
- let n = [], r = new Set(t.exercised), i = /* @__PURE__ */ new Map();
2287
- for (let e of t.edges) {
2288
- let t = i.get(e.tag);
2289
- t === void 0 ? i.set(e.tag, [e]) : t.push(e);
2290
- }
2291
- let a = [], o = [];
2292
- for (let t of e) {
2293
- if (!r.has(t.tag)) {
2294
- a.push(t.tag);
2295
- continue;
2296
- }
2297
- let e = `${t.kind}(${t.tag})`, s = t.file, c = i.get(t.tag) ?? [], l = new Set(Xn(t.source)), u = new Map((t.targets ?? []).map((e) => [e.table, e.op]));
2298
- for (let t of c) if (Zn.has(t.op)) {
2299
- let r = u.get(t.table);
2300
- r === void 0 ? n.push({
2301
- rule: "observed/undeclared-write",
2302
- node: e,
2303
- ...s === void 0 ? {} : { file: s },
2304
- breaks: [`writes '${t.table}' (${t.op}) but declares no target for it`, "subscriptions on that table are not invalidated by this mutation"],
2305
- fix: `add { table: '${t.table}', op: '${t.op}' } to this procedure's targets`,
2306
- severity: "warn"
2307
- }) : r !== t.op && n.push({
2308
- rule: "observed/wrong-op",
2309
- node: e,
2310
- ...s === void 0 ? {} : { file: s },
2311
- breaks: [`declares op '${r}' on '${t.table}' but performed '${t.op}'`, "the synthesized optimistic patch has the wrong shape"],
2312
- fix: `change the target op for '${t.table}' to '${t.op}'`,
2313
- severity: "warn"
2314
- });
2315
- } else !l.has(t.table) && !u.has(t.table) && n.push({
2316
- rule: "observed/undeclared-read",
2317
- node: e,
2318
- ...s === void 0 ? {} : { file: s },
2319
- breaks: [`reads '${t.table}' but declares no source for it`],
2320
- fix: `add '${t.table}' to this procedure's source`,
2321
- severity: "warn"
2322
- });
2323
- if (c.length === 0) {
2324
- o.push(t.tag);
2325
- continue;
2326
- }
2327
- let d = new Set(c.map((e) => e.table));
2328
- for (let t of l) d.has(t) || n.push({
2329
- rule: "observed/declared-but-unobserved",
2330
- node: e,
2331
- ...s === void 0 ? {} : { file: s },
2332
- breaks: [`declares source '${t}' but never read it while running`],
2333
- fix: `remove '${t}' from source, or check whether the handler still reads it`,
2334
- severity: "warn"
2335
- });
2336
- for (let [t] of u) d.has(t) || n.push({
2337
- rule: "observed/declared-but-unobserved",
2338
- node: e,
2339
- ...s === void 0 ? {} : { file: s },
2340
- breaks: [`declares target '${t}' but never wrote it while running`],
2341
- fix: `remove the '${t}' target, or check whether the handler still writes it`,
2342
- severity: "warn"
2343
- });
2344
- }
2345
- return {
2346
- diagnostics: n,
2347
- unexercised: a.sort(),
2348
- unobservedReads: o.sort(),
2349
- coverage: {
2350
- exercised: e.length - a.length - o.length,
2351
- total: e.length
2352
- }
2353
- };
2354
- }, $n = (e, t = [], n = 0) => {
2355
- if (n > 12) return t;
2356
- let r;
2357
- try {
2358
- r = D(e);
2359
- } catch {
2360
- return t;
2361
- }
2362
- for (let i of r) {
2363
- if ([
2364
- "node_modules",
2365
- "dist",
2366
- "build",
2367
- ".framework",
2368
- ".turbo",
2369
- ".git",
2370
- "coverage"
2371
- ].includes(i)) continue;
2372
- let r = `${e}/${i}`, a;
2373
- try {
2374
- a = O(r);
2375
- } catch {
2376
- continue;
2377
- }
2378
- a.isDirectory() ? $n(r, t, n + 1) : a.isFile() && u.test(i) && t.push(r);
2379
- }
2380
- return t;
2381
- }, er = /* @__PURE__ */ new Set(["actors", "tenants"]), tr = async (e) => {
2382
- try {
2383
- let { readFile: t } = await import("node:fs/promises"), { join: n } = await import("node:path"), r = await t(n(e, "app.graph.observed.generated.json"), "utf8"), i = JSON.parse(r);
2384
- return i?.version !== 1 || !Array.isArray(i.edges) || !Array.isArray(i.exercised) ? null : i;
2385
- } catch {
2386
- return null;
2387
- }
2388
- }, nr = (e) => {
2389
- let { exercised: t, total: n } = e.coverage, r = `\nobserved: ${t}/${n} procedures exercised (${n === 0 ? 0 : Math.round(t / n * 100)}%)`;
2390
- if (e.diagnostics.length === 0) return `${r}\n no declared/observed mismatches${e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : ""}`;
2391
- let i = e.diagnostics.map((e) => ` ${e.node}${e.file === void 0 ? "" : ` (${e.file})`}\n` + e.breaks.map((e) => ` ${e}`).join("\n") + `\n fix: ${e.fix}`), a = e.unexercised.length > 0 ? `\n ${e.unexercised.length} unexercised — no recording, so nothing is claimed about them` : "";
2392
- return `${r}\n${i.join("\n")}${a}`;
2393
- }, rr = (e, t) => ({
2394
- tables: (t.tables ?? []).map((e) => e.name),
2395
- frameworkOwnedTables: (t.tables ?? []).filter((e) => e.framework === !0 || e.name.startsWith("_voltro_") || er.has(e.name)).map((e) => e.name),
2396
- scopes: e.scopes ?? [],
2397
- procedures: (e.procedures ?? []).map((e) => {
2398
- let t = (e.guards ?? []).flatMap((e) => e.kind === "scope" ? e.scope ?? [] : []);
2399
- return {
2400
- tag: e.tag,
2401
- kind: e.kind,
2402
- ...e.source === void 0 ? {} : { source: e.source },
2403
- ...e.targets === void 0 ? {} : { targets: e.targets },
2404
- ...t.length > 0 ? { requiresScopes: [...new Set(t)] } : {},
2405
- ...e.guards === void 0 ? {} : { hasGuards: e.guards.length > 0 }
2406
- };
2407
- })
2408
- }), ir = (e) => e.length === 0 ? "✓ voltro check: no issues" : e.map((e) => {
2409
- let t = e.severity === "error" ? "✗ error" : "⚠ warn", n = e.file ? ` (${e.file})` : "";
2410
- return `${t} ${e.rule} ${e.node}${n}\n ${e.breaks.join("; ")}\n fix: ${e.fix}`;
2411
- }).join("\n"), ar = async (e) => {
2412
- let { walk: t, loadDiscovered: n, loadApiConfig: r } = await import("./dev-iiMtlkfs.js"), { buildRpcEntry: i } = await import("./manifestBuild-Dj8Jjoto.js"), { buildCapabilityManifest: a } = await import("./inspect-mmBuRXmy.js"), { relative: o } = await import("node:path"), s = await n(await t(e)), c = (t, n, r) => i(t, n, o(e, r)), l = [
2413
- ...s.queries.map((e) => c(e.descriptor, "query", e.file)),
2414
- ...s.mutations.map((e) => c(e.descriptor, "mutation", e.file)),
2415
- ...s.actions.map((e) => c(e.descriptor, "action", e.file)),
2416
- ...s.streams.map((e) => c(e.descriptor, "stream", e.file))
2417
- ], { assembleFrameworkTables: u } = await import("./frameworkTableAssembly-D-EebUQX.js"), d = await u({ root: e }).catch((e) => (console.warn(`\n⚠ plugin tables could not be collected — this report is INCOMPLETE.\n ${e instanceof Error ? e.message.split("\n")[0] : String(e)}\n`), [])), f = new Set(s.tables.map((e) => e.tableName)), p = [...[...f].map((e) => ({ name: e })), ...d.map((e) => e.tableName).filter((e) => !f.has(e)).map((e) => ({
2418
- name: e,
2419
- framework: !0
2420
- }))], m = await r(e).then((e) => e?.plugins ?? []).catch(() => []);
2421
- return {
2422
- manifest: a(l, s.workflows.map((t) => ({
2423
- tag: t.definition.name ?? "",
2424
- file: o(e, t.file)
2425
- })), [], m),
2426
- tables: { tables: p }
2427
- };
2428
- }, or = async (e) => {
2429
- let t = he(e.flatMap((e) => e === "--json" ? ["--format", "json"] : [e]));
2430
- if ("error" in t) return console.error(`voltro check: ${t.error}`), 1;
2431
- let { process: n, target: r, rest: i } = ge(t.rest), a;
2432
- for (let e = 0; e < i.length; e++) if (i[e] === "--diff") {
2433
- let [t, n] = (i[++e] ?? "").split(":");
2434
- if (t === "removeTable" && n) a = {
2435
- kind: "removeTable",
2436
- name: n
2437
- };
2438
- else if (t === "removeProcedure" && n) a = {
2439
- kind: "removeProcedure",
2440
- tag: n
2441
- };
2442
- else if (t === "removeScope" && n) a = {
2443
- kind: "removeScope",
2444
- name: n
2445
- };
2446
- else return console.error("voltro check --diff: expected removeTable:<name> | removeProcedure:<tag> | removeScope:<name>"), 1;
2447
- }
2448
- let o = i.find((e) => !e.startsWith("-")) ?? process.cwd(), s = i.includes("--offline"), c = s ? [] : await me(n, r), u, d;
2449
- if (c.length === 0) {
2450
- if (!s && r !== void 0) return console.error("voltro check: --url given but no api reachable there."), 1;
2451
- try {
2452
- let e = await ar(C(o));
2453
- u = e.manifest, d = e.tables;
2454
- } catch (e) {
2455
- return console.error(`voltro check: could not assemble the graph from source: ${e.message}`), console.error(" Start `voltro dev` to check against a running api instead."), 1;
2456
- }
2457
- } else {
2458
- let e = c[0], [t, n] = await Promise.all([N(e, "/_voltro/inspect/manifest"), N(e, "/_voltro/inspect/data/tables")]);
2459
- if (t.kind === "err") return console.error(`voltro check: manifest fetch failed: ${t.error}`), 1;
2460
- u = t.data, d = n.kind === "ok" ? n.data : { tables: [] };
2461
- }
2462
- let f = rr(u, d), p = ie(f, a), m = [...V(o).findings.map((e) => ({
2463
- rule: e.rule,
2464
- node: e.file,
2465
- file: e.file,
2466
- breaks: [e.detail],
2467
- fix: e.fix,
2468
- severity: e.severity
2469
- })), ...J(o).map((e) => ({
2470
- rule: `page/${e.kind}`,
2471
- node: e.file,
2472
- file: e.file,
2473
- breaks: [e.kind === "suffixed-outside-pages" ? "a page.tsx outside src/pages/ never routes" : "no test beside it"],
2474
- fix: e.kind === "suffixed-outside-pages" ? "move it under src/pages/ into the directory whose route it serves, or rename it — page.tsx is reserved" : "write the test with the file — the name is derivable",
2475
- severity: e.kind === "suffixed-outside-pages" ? "error" : "warn"
2476
- }))], h = (await l(o, $n(o))).map((e) => ({
2477
- rule: "client/not-browser-safe",
2478
- node: e.file,
2479
- file: e.file,
2480
- breaks: [`reaches the server-only '${e.violation.specifier}' — chain: ${e.violation.chain.join(" → ")}`],
2481
- fix: "move the server-only code behind a *.server.ts, or drop the .client suffix — an unmarked file makes no claim",
2482
- severity: "error"
2483
- })), g = [
2484
- ...p.diagnostics,
2485
- ...m,
2486
- ...h
2487
- ], _ = p.ok && !m.some((e) => e.severity === "error") && h.length === 0, v = await tr(C(o)), y = v === null ? null : Qn(f.procedures, v);
2488
- return t.format === "json" ? M(y === null ? {
2489
- ...p,
2490
- ok: _,
2491
- diagnostics: g
2492
- } : {
2493
- ...p,
2494
- ok: _,
2495
- diagnostics: g,
2496
- observed: y
2497
- }) : (console.log(ir(g)), y !== null && console.log(nr(y)), console.log(_ ? "\nvoltro check: OK" : "\nvoltro check: FAILED (errors above)")), +!_;
1734
+ let s = tt(n, t);
1735
+ Tt(n), wt(n), Lt(n), ne(n), await jt(n), await Yt(n), await Jt(n), await Bt(n), await Ht(n), await Ut(n), await nn(n), await en(n), await Qt(n), await tn(n);
1736
+ let c = await Kt(n), l = await cn(n, e.includes("--write-authz-allowlist")), u = await Wt(n), d = await Gt(n), f = r(o(n));
1737
+ f !== void 0 && (console.log(""), console.log(f));
1738
+ let p = i(a(n));
1739
+ return p !== void 0 && console.log(`\n${p}`), c || l || u || d ? Math.max(s, 1) : s;
2498
1740
  };
2499
1741
  //#endregion
2500
- export { le as _, Lt as a, Se as c, N as d, he as f, me as g, pe as h, Yn as i, I as l, P as m, or as n, Be as o, ge as p, rr as r, xe as s, ar as t, ye as u, M as v, ue as y };
1742
+ export { q as C, B as S, Y as T, jt as _, on as a, yt as b, G as c, vt as d, P as f, Lt as g, Tt as h, J as i, Xt as l, wt as m, et as n, Z as o, W as p, Et as r, Zt as s, Rt as t, z as u, ln as v, Q as w, V as x, tt as y };