okengine 0.2.6 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (681) hide show
  1. package/AGENTS.md +33 -30
  2. package/README.md +74 -340
  3. package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +40 -9
  4. package/package.json +47 -44
  5. package/site/content/docs/ai/llms-txt.mdx +54 -0
  6. package/site/content/docs/ai/mcp.mdx +123 -0
  7. package/site/content/docs/ai/meta.json +5 -0
  8. package/site/content/docs/ai/skills.mdx +53 -0
  9. package/site/content/docs/console/access.mdx +29 -0
  10. package/site/content/docs/console/ai.mdx +35 -0
  11. package/site/content/docs/console/architecture.mdx +35 -0
  12. package/site/content/docs/console/channels.mdx +37 -0
  13. package/site/content/docs/console/clock.mdx +31 -0
  14. package/site/content/docs/console/flows.mdx +31 -0
  15. package/site/content/docs/console/gates.mdx +35 -0
  16. package/site/content/docs/console/manifest-diff.mdx +34 -0
  17. package/site/content/docs/console/meta.json +23 -0
  18. package/site/content/docs/console/overview.mdx +40 -0
  19. package/site/content/docs/console/plugins.mdx +41 -0
  20. package/site/content/docs/console/privacy.mdx +32 -0
  21. package/site/content/docs/console/runs.mdx +40 -0
  22. package/site/content/docs/console/signals.mdx +31 -0
  23. package/site/content/docs/console/store.mdx +32 -0
  24. package/site/content/docs/console/tenancy.mdx +32 -0
  25. package/site/content/docs/console/traces.mdx +34 -0
  26. package/site/content/docs/console/vault.mdx +37 -0
  27. package/site/content/docs/elements/ai.mdx +180 -0
  28. package/site/content/docs/elements/channel.mdx +167 -0
  29. package/site/content/docs/elements/clock.mdx +182 -0
  30. package/site/content/docs/elements/flow.mdx +288 -0
  31. package/site/content/docs/elements/gate.mdx +171 -0
  32. package/site/content/docs/elements/meta.json +5 -0
  33. package/site/content/docs/elements/signal.mdx +171 -0
  34. package/site/content/docs/elements/store.mdx +320 -0
  35. package/site/content/docs/elements/vault.mdx +263 -0
  36. package/site/content/docs/get-started/basic-usage.mdx +124 -0
  37. package/site/content/docs/get-started/comparison.mdx +65 -0
  38. package/site/content/docs/get-started/installation.mdx +113 -0
  39. package/site/content/docs/get-started/introduction.mdx +123 -0
  40. package/site/content/docs/get-started/meta.json +5 -0
  41. package/site/content/docs/index.mdx +63 -0
  42. package/site/content/docs/meta.json +5 -0
  43. package/site/content/docs/plugins/compression.mdx +60 -0
  44. package/site/content/docs/plugins/cors.mdx +92 -0
  45. package/site/content/docs/plugins/csrf.mdx +96 -0
  46. package/site/content/docs/plugins/ip-allowlist.mdx +92 -0
  47. package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
  48. package/site/content/docs/plugins/meta.json +15 -0
  49. package/site/content/docs/plugins/security-headers.mdx +136 -0
  50. package/site/content/docs/reference/cli.md +101 -0
  51. package/site/content/docs/reference/configuration.mdx +159 -0
  52. package/site/content/docs/reference/environment-variables.mdx +87 -0
  53. package/site/content/docs/reference/errors.mdx +80 -0
  54. package/site/content/docs/reference/fx.mdx +117 -0
  55. package/site/content/docs/reference/meta.json +5 -0
  56. package/site/content/docs/reference/plugins.mdx +249 -0
  57. package/site/content/docs/reference/security.md +63 -0
  58. package/src/auth/api-keys.ts +2 -5
  59. package/src/auth/attenuation.ts +2 -6
  60. package/src/auth/auth.test.ts +56 -31
  61. package/src/auth/invoke-as.ts +1 -3
  62. package/src/auth/operator.ts +8 -26
  63. package/src/auth/planes.ts +2 -7
  64. package/src/auth/plugin.ts +4 -7
  65. package/src/auth/roles.ts +1 -5
  66. package/src/auth/sessions.ts +10 -39
  67. package/src/cli/args.ts +1 -5
  68. package/src/cli/ask-dev-mode.ts +40 -0
  69. package/src/cli/build.ts +1 -2
  70. package/src/cli/client-add.test.ts +8 -15
  71. package/src/cli/client-add.ts +5 -14
  72. package/src/cli/completion.test.ts +1 -5
  73. package/src/cli/completion.ts +4 -17
  74. package/src/cli/db-auto-push.test.ts +64 -0
  75. package/src/cli/db-auto-push.ts +72 -0
  76. package/src/cli/db.test.ts +189 -0
  77. package/src/cli/db.ts +456 -0
  78. package/src/cli/dev-app-runner.ts +2 -8
  79. package/src/cli/dev-db-push.test.ts +193 -0
  80. package/src/cli/dev-mode.test.ts +45 -0
  81. package/src/cli/dev-mode.ts +78 -0
  82. package/src/cli/dev-schema-sync.test.ts +66 -0
  83. package/src/cli/dev-schema-sync.ts +139 -0
  84. package/src/cli/dev.test.ts +264 -15
  85. package/src/cli/dev.ts +355 -139
  86. package/src/cli/doc-staleness.test.ts +69 -0
  87. package/src/cli/docker-cli.test.ts +27 -8
  88. package/src/cli/docker.ts +11 -3
  89. package/src/cli/doctor-diff.test.ts +1 -5
  90. package/src/cli/doctor-diff.ts +17 -46
  91. package/src/cli/doctor-pii.ts +2 -7
  92. package/src/cli/doctor.test.ts +26 -4
  93. package/src/cli/doctor.ts +35 -11
  94. package/src/cli/drizzle-env.test.ts +67 -0
  95. package/src/cli/drizzle-env.ts +78 -0
  96. package/src/cli/ensure-drizzle-config.ts +50 -0
  97. package/src/cli/eval.ts +4 -14
  98. package/src/cli/gates-list.ts +1 -4
  99. package/src/cli/hero-meta.test.ts +17 -19
  100. package/src/cli/hero-meta.ts +20 -30
  101. package/src/cli/images.ts +3 -8
  102. package/src/cli/index.ts +11 -2
  103. package/src/cli/load-config.images.test.ts +14 -14
  104. package/src/cli/load-config.ts +47 -47
  105. package/src/cli/mcp-from-console.ts +1 -4
  106. package/src/cli/mode.ts +65 -0
  107. package/src/cli/openbao-bootstrap.test.ts +147 -0
  108. package/src/cli/openbao-bootstrap.ts +280 -0
  109. package/src/cli/openbao-restart.integration.test.ts +136 -0
  110. package/src/cli/ports.test.ts +7 -14
  111. package/src/cli/ports.ts +7 -5
  112. package/src/cli/privacy-erase.ts +1 -3
  113. package/src/cli/registry.ts +75 -25
  114. package/src/cli/resolve-dev-sql-env.test.ts +48 -0
  115. package/src/cli/resolve-dev-sql-env.ts +42 -0
  116. package/src/cli/schema.ts +3 -10
  117. package/src/cli/stack.ts +8 -10
  118. package/src/cli/start.ts +4 -15
  119. package/src/cli/upgrade.ts +1 -4
  120. package/src/cli/vault-cmd.ts +63 -0
  121. package/src/client/budget-entry.ts +1 -6
  122. package/src/client/create.test.ts +6 -12
  123. package/src/client/create.ts +2 -8
  124. package/src/client/errors.ts +1 -4
  125. package/src/client/index.ts +1 -6
  126. package/src/client/notes-contract.test.ts +4 -13
  127. package/src/client/transport.ts +9 -30
  128. package/src/client/types.ts +15 -30
  129. package/src/compiler/aot.test.ts +2 -9
  130. package/src/compiler/aot.ts +1 -4
  131. package/src/compiler/differential.test.ts +3 -11
  132. package/src/compiler/dynamic.ts +1 -4
  133. package/src/compiler/effects-infer.ts +8 -34
  134. package/src/compiler/emit.ts +1 -3
  135. package/src/compiler/extract.test.ts +136 -30
  136. package/src/compiler/extract.ts +485 -157
  137. package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
  138. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
  139. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
  140. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
  141. package/src/compiler/fixtures/skyport.expected.json +24 -83
  142. package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
  143. package/src/compiler/http-parse.ts +2 -9
  144. package/src/compiler/index.ts +1 -5
  145. package/src/compiler/response.ts +16 -7
  146. package/src/compiler/sucrose.ts +11 -31
  147. package/src/config/define-config.test.ts +55 -34
  148. package/src/config/index.ts +177 -46
  149. package/src/config/resolve-driver.test.ts +28 -17
  150. package/src/console/budget.test.ts +1 -4
  151. package/src/console/index.ts +1 -4
  152. package/src/console/server/access.test.ts +15 -43
  153. package/src/console/server/access.ts +19 -49
  154. package/src/console/server/ai.test.ts +3 -11
  155. package/src/console/server/ai.ts +23 -46
  156. package/src/console/server/app.ts +16 -45
  157. package/src/console/server/auth-rate.test.ts +4 -12
  158. package/src/console/server/bind.ts +1 -5
  159. package/src/console/server/channels.test.ts +2 -6
  160. package/src/console/server/channels.ts +10 -35
  161. package/src/console/server/claim.ts +2 -8
  162. package/src/console/server/clock.test.ts +2 -6
  163. package/src/console/server/clock.ts +8 -26
  164. package/src/console/server/console.test.ts +1 -3
  165. package/src/console/server/diff.test.ts +5 -17
  166. package/src/console/server/diff.ts +10 -31
  167. package/src/console/server/dry-run.audit.test.ts +2 -8
  168. package/src/console/server/flows.ts +64 -233
  169. package/src/console/server/gates.test.ts +14 -50
  170. package/src/console/server/gates.ts +13 -48
  171. package/src/console/server/index.ts +2 -9
  172. package/src/console/server/live.ts +3 -12
  173. package/src/console/server/operator-db.test.ts +4 -15
  174. package/src/console/server/operator-db.ts +13 -32
  175. package/src/console/server/plugin.ts +50 -53
  176. package/src/console/server/plugins.test.ts +2 -6
  177. package/src/console/server/plugins.ts +17 -56
  178. package/src/console/server/runs-pii.ts +2 -7
  179. package/src/console/server/security.gate.test.ts +18 -40
  180. package/src/console/server/serve.ts +10 -25
  181. package/src/console/server/signals.test.ts +1 -3
  182. package/src/console/server/signals.ts +17 -40
  183. package/src/console/server/state.ts +21 -59
  184. package/src/console/server/store.test.ts +20 -22
  185. package/src/console/server/store.ts +31 -76
  186. package/src/console/server/vault.test.ts +12 -29
  187. package/src/console/server/vault.ts +44 -66
  188. package/src/console/ui/access/AccessA11yView.tsx +6 -21
  189. package/src/console/ui/access/a11y.test.tsx +3 -12
  190. package/src/console/ui/access/acknowledgement.test.ts +1 -4
  191. package/src/console/ui/access/acknowledgement.ts +1 -3
  192. package/src/console/ui/access/blast-radius.ts +1 -3
  193. package/src/console/ui/access/confirmation.test.ts +1 -5
  194. package/src/console/ui/access/confirmation.ts +1 -4
  195. package/src/console/ui/access/fixture.ts +1 -5
  196. package/src/console/ui/access/grantable.test.ts +1 -3
  197. package/src/console/ui/access/grantable.ts +2 -7
  198. package/src/console/ui/access/index.ts +2 -9
  199. package/src/console/ui/access/provenance.test.ts +1 -3
  200. package/src/console/ui/access/provenance.ts +2 -6
  201. package/src/console/ui/access/search.test.ts +1 -5
  202. package/src/console/ui/access/search.ts +2 -6
  203. package/src/console/ui/ai/AiA11yView.tsx +6 -17
  204. package/src/console/ui/ai/a11y.test.tsx +2 -5
  205. package/src/console/ui/ai/fixture.ts +2 -7
  206. package/src/console/ui/ai/format.ts +1 -3
  207. package/src/console/ui/ai/group.test.ts +1 -3
  208. package/src/console/ui/ai/group.ts +4 -12
  209. package/src/console/ui/ai/index.ts +2 -9
  210. package/src/console/ui/ai/promotion.test.ts +2 -7
  211. package/src/console/ui/ai/promotion.ts +4 -13
  212. package/src/console/ui/ai/search.test.ts +2 -11
  213. package/src/console/ui/ai/search.ts +3 -13
  214. package/src/console/ui/ai/types.ts +1 -8
  215. package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
  216. package/src/console/ui/architecture/a11y.test.tsx +1 -2
  217. package/src/console/ui/architecture/declared.ts +1 -5
  218. package/src/console/ui/architecture/index.ts +7 -35
  219. package/src/console/ui/architecture/layout.ts +1 -3
  220. package/src/console/ui/architecture/pathologies.ts +10 -19
  221. package/src/console/ui/architecture/search.ts +6 -15
  222. package/src/console/ui/architecture/traffic.ts +4 -14
  223. package/src/console/ui/architecture/types.ts +2 -10
  224. package/src/console/ui/architecture/view.test.ts +7 -19
  225. package/src/console/ui/architecture/view.ts +9 -36
  226. package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
  227. package/src/console/ui/channels/a11y.test.tsx +1 -2
  228. package/src/console/ui/channels/confirmation.test.ts +1 -4
  229. package/src/console/ui/channels/confirmation.ts +1 -4
  230. package/src/console/ui/channels/findings.ts +1 -5
  231. package/src/console/ui/channels/index.ts +2 -8
  232. package/src/console/ui/channels/locale.test.ts +1 -3
  233. package/src/console/ui/channels/locale.ts +2 -8
  234. package/src/console/ui/channels/search.ts +4 -13
  235. package/src/console/ui/channels/taxonomy.ts +1 -3
  236. package/src/console/ui/clock/ClockA11yView.tsx +9 -26
  237. package/src/console/ui/clock/a11y.test.tsx +2 -5
  238. package/src/console/ui/clock/confirmation.test.ts +1 -3
  239. package/src/console/ui/clock/confirmation.ts +1 -4
  240. package/src/console/ui/clock/findings.ts +1 -4
  241. package/src/console/ui/clock/health.ts +2 -7
  242. package/src/console/ui/clock/index.ts +2 -10
  243. package/src/console/ui/clock/search.test.ts +1 -3
  244. package/src/console/ui/clock/search.ts +2 -6
  245. package/src/console/ui/clock/timeline.test.ts +1 -3
  246. package/src/console/ui/clock/waiting-on.test.ts +1 -5
  247. package/src/console/ui/clock/waiting-on.ts +2 -7
  248. package/src/console/ui/diff/DiffA11yView.tsx +8 -17
  249. package/src/console/ui/diff/a11y.test.tsx +2 -5
  250. package/src/console/ui/diff/group.test.ts +3 -13
  251. package/src/console/ui/diff/group.ts +2 -8
  252. package/src/console/ui/diff/index.ts +2 -8
  253. package/src/console/ui/diff/search.test.ts +1 -5
  254. package/src/console/ui/diff/search.ts +4 -16
  255. package/src/console/ui/diff/summary.ts +1 -2
  256. package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
  257. package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
  258. package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
  259. package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
  260. package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
  261. package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
  262. package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
  263. package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
  264. package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
  265. package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
  266. package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
  267. package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
  268. package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
  269. package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
  270. package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
  271. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
  272. package/src/console/ui/dist/index.html +1 -1
  273. package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
  274. package/src/console/ui/flows/a11y.test.tsx +2 -8
  275. package/src/console/ui/flows/buffer.test.ts +3 -11
  276. package/src/console/ui/flows/buffer.ts +4 -11
  277. package/src/console/ui/flows/confirmation.ts +1 -3
  278. package/src/console/ui/flows/contract.test.ts +3 -7
  279. package/src/console/ui/flows/contract.ts +12 -36
  280. package/src/console/ui/flows/graph.test.ts +3 -11
  281. package/src/console/ui/flows/graph.ts +5 -15
  282. package/src/console/ui/flows/index.ts +1 -6
  283. package/src/console/ui/flows/save-as-test.ts +1 -3
  284. package/src/console/ui/flows/search.ts +8 -25
  285. package/src/console/ui/flows/tiers.ts +2 -9
  286. package/src/console/ui/gates/GatesA11yView.tsx +4 -13
  287. package/src/console/ui/gates/a11y.test.tsx +2 -5
  288. package/src/console/ui/gates/audit.ts +1 -3
  289. package/src/console/ui/gates/denial.test.ts +1 -3
  290. package/src/console/ui/gates/denial.ts +4 -12
  291. package/src/console/ui/gates/findings.ts +1 -3
  292. package/src/console/ui/gates/group.test.ts +3 -3
  293. package/src/console/ui/gates/group.ts +3 -11
  294. package/src/console/ui/gates/index.ts +2 -10
  295. package/src/console/ui/gates/search.ts +3 -10
  296. package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
  297. package/src/console/ui/overview/a11y.test.tsx +1 -2
  298. package/src/console/ui/overview/busiest.ts +1 -3
  299. package/src/console/ui/overview/compose.ts +1 -13
  300. package/src/console/ui/overview/cost.ts +34 -38
  301. package/src/console/ui/overview/fixture.ts +21 -26
  302. package/src/console/ui/overview/golden.ts +4 -14
  303. package/src/console/ui/overview/index.ts +2 -9
  304. package/src/console/ui/overview/rank.test.ts +3 -10
  305. package/src/console/ui/overview/rank.ts +2 -7
  306. package/src/console/ui/overview/slo.test.ts +5 -9
  307. package/src/console/ui/overview/slo.ts +6 -23
  308. package/src/console/ui/overview/verdict.ts +3 -10
  309. package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
  310. package/src/console/ui/plugins/a11y.test.tsx +1 -2
  311. package/src/console/ui/plugins/fixture.ts +2 -4
  312. package/src/console/ui/plugins/group.test.ts +1 -3
  313. package/src/console/ui/plugins/group.ts +1 -6
  314. package/src/console/ui/plugins/index.ts +3 -13
  315. package/src/console/ui/plugins/search.ts +7 -19
  316. package/src/console/ui/runs/RunsA11yView.tsx +5 -20
  317. package/src/console/ui/runs/a11y.test.tsx +2 -5
  318. package/src/console/ui/runs/explain.ts +2 -6
  319. package/src/console/ui/runs/group.ts +3 -12
  320. package/src/console/ui/runs/histogram.test.ts +2 -11
  321. package/src/console/ui/runs/histogram.ts +1 -4
  322. package/src/console/ui/runs/index.ts +4 -18
  323. package/src/console/ui/runs/project.ts +2 -6
  324. package/src/console/ui/runs/query.test.ts +1 -3
  325. package/src/console/ui/runs/query.ts +12 -35
  326. package/src/console/ui/runs/search.ts +5 -19
  327. package/src/console/ui/runs/trace-link.test.ts +2 -6
  328. package/src/console/ui/runs/trace-link.ts +4 -16
  329. package/src/console/ui/runs/types.ts +1 -3
  330. package/src/console/ui/shell/App.tsx +2 -10
  331. package/src/console/ui/shell/client.ts +22 -115
  332. package/src/console/ui/shell/components/ui.tsx +1 -4
  333. package/src/console/ui/shell/layout/Shell.tsx +2 -4
  334. package/src/console/ui/shell/main.tsx +31 -96
  335. package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
  336. package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
  337. package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
  338. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
  339. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
  340. package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
  341. package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
  342. package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
  343. package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
  344. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
  345. package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
  346. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
  347. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
  348. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
  349. package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
  350. package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
  351. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
  352. package/src/console/ui/shell/setup/Wizard.tsx +3 -5
  353. package/src/console/ui/shell/styles.css +1 -2
  354. package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
  355. package/src/console/ui/signals/a11y.test.tsx +2 -5
  356. package/src/console/ui/signals/confirmation.test.ts +1 -5
  357. package/src/console/ui/signals/confirmation.ts +2 -6
  358. package/src/console/ui/signals/dry-run.ts +1 -3
  359. package/src/console/ui/signals/durable.ts +1 -3
  360. package/src/console/ui/signals/findings.ts +1 -3
  361. package/src/console/ui/signals/group.test.ts +2 -8
  362. package/src/console/ui/signals/group.ts +2 -10
  363. package/src/console/ui/signals/index.ts +1 -4
  364. package/src/console/ui/signals/monitor.ts +2 -7
  365. package/src/console/ui/signals/schema-form.test.ts +3 -14
  366. package/src/console/ui/signals/schema-form.ts +1 -4
  367. package/src/console/ui/signals/search.ts +4 -13
  368. package/src/console/ui/store/StoreA11yView.tsx +3 -9
  369. package/src/console/ui/store/a11y.test.tsx +2 -5
  370. package/src/console/ui/store/confirmation.test.ts +1 -3
  371. package/src/console/ui/store/confirmation.ts +1 -4
  372. package/src/console/ui/store/dry-run.ts +2 -5
  373. package/src/console/ui/store/fixture.ts +1 -2
  374. package/src/console/ui/store/group.test.ts +1 -6
  375. package/src/console/ui/store/group.ts +1 -4
  376. package/src/console/ui/store/index.ts +1 -5
  377. package/src/console/ui/store/search.ts +2 -6
  378. package/src/console/ui/store/will-not-fire.ts +1 -2
  379. package/src/console/ui/traces/TracesA11yView.tsx +10 -29
  380. package/src/console/ui/traces/a11y.test.tsx +1 -2
  381. package/src/console/ui/traces/chain.test.ts +5 -19
  382. package/src/console/ui/traces/chain.ts +1 -3
  383. package/src/console/ui/traces/critical-path.test.ts +1 -3
  384. package/src/console/ui/traces/critical-path.ts +2 -6
  385. package/src/console/ui/traces/filter.test.ts +2 -6
  386. package/src/console/ui/traces/filter.ts +5 -13
  387. package/src/console/ui/traces/fold.test.ts +4 -12
  388. package/src/console/ui/traces/fold.ts +1 -3
  389. package/src/console/ui/traces/mini.ts +1 -3
  390. package/src/console/ui/traces/replay.test.ts +1 -3
  391. package/src/console/ui/traces/replay.ts +1 -3
  392. package/src/console/ui/traces/sampling.ts +1 -4
  393. package/src/console/ui/traces/search.ts +4 -17
  394. package/src/console/ui/traces/tier.ts +1 -3
  395. package/src/console/ui/vault/VaultA11yView.tsx +5 -17
  396. package/src/console/ui/vault/a11y.test.tsx +1 -2
  397. package/src/console/ui/vault/blast-radius.ts +1 -3
  398. package/src/console/ui/vault/confirmation.test.ts +1 -5
  399. package/src/console/ui/vault/confirmation.ts +1 -4
  400. package/src/console/ui/vault/dormant.test.ts +1 -4
  401. package/src/console/ui/vault/export-safe.test.ts +1 -5
  402. package/src/console/ui/vault/export-safe.ts +1 -4
  403. package/src/console/ui/vault/fixture.ts +3 -3
  404. package/src/console/ui/vault/group.ts +1 -2
  405. package/src/console/ui/vault/index.ts +2 -10
  406. package/src/console/ui/vault/search.ts +2 -6
  407. package/src/console/ui/vault/types.ts +1 -1
  408. package/src/console/xss.gate.test.ts +1 -3
  409. package/src/docker/compose.ts +215 -27
  410. package/src/docker/derive.ts +6 -14
  411. package/src/docker/docker.test.ts +118 -15
  412. package/src/docker/dockerfile.integration.test.ts +12 -21
  413. package/src/docker/helpers.ts +5 -4
  414. package/src/docker/index.ts +9 -6
  415. package/src/docker/pin.ts +1 -9
  416. package/src/docker/recipes/index.ts +6 -6
  417. package/src/docker/recipes/mailpit.ts +22 -0
  418. package/src/docker/recipes/openbao.ts +47 -0
  419. package/src/docker/recipes/postgres.ts +17 -6
  420. package/src/docker/recipes/redis.ts +11 -2
  421. package/src/docker/recipes/rustfs.ts +32 -0
  422. package/src/docker/stack-id.test.ts +73 -5
  423. package/src/docker/stack-id.ts +124 -33
  424. package/src/docker/stack.integration.test.ts +6 -21
  425. package/src/docker/stack.ts +36 -4
  426. package/src/docker/types.ts +16 -4
  427. package/src/docs-origin.ts +4 -4
  428. package/src/drivers/ai-anthropic.ts +5 -14
  429. package/src/drivers/ai-mock.ts +2 -5
  430. package/src/drivers/ai-openai-compatible.ts +4 -10
  431. package/src/drivers/ai-providers.test.ts +6 -13
  432. package/src/drivers/bun-native-completeness.test.ts +6 -15
  433. package/src/drivers/channel-console.ts +6 -7
  434. package/src/drivers/channel-fcm.ts +20 -30
  435. package/src/drivers/channel-resend.ts +1 -3
  436. package/src/drivers/channel-smtp.ts +2 -6
  437. package/src/drivers/channel-types.ts +2 -12
  438. package/src/drivers/channel-unifonic.ts +2 -6
  439. package/src/drivers/channel-wa-cloud.ts +2 -6
  440. package/src/drivers/channel-webpush.ts +21 -60
  441. package/src/drivers/conformance.test.ts +2 -10
  442. package/src/drivers/conformance.ts +8 -21
  443. package/src/drivers/drizzle-dialect.test.ts +20 -0
  444. package/src/drivers/drizzle-dialect.ts +37 -0
  445. package/src/drivers/fs.ts +2 -8
  446. package/src/drivers/index.ts +12 -57
  447. package/src/drivers/memory.ts +287 -75
  448. package/src/drivers/pgvector.ts +7 -11
  449. package/src/drivers/postgres.ts +7 -13
  450. package/src/drivers/redis.ts +13 -37
  451. package/src/drivers/s3.ts +15 -18
  452. package/src/drivers/signal-engine.ts +12 -49
  453. package/src/drivers/signal-memory.ts +2 -8
  454. package/src/drivers/signal-nats.ts +4 -9
  455. package/src/drivers/signal-postgres.ts +62 -129
  456. package/src/drivers/signal-redis.ts +7 -21
  457. package/src/drivers/signal-types.ts +1 -4
  458. package/src/drivers/sqlite.ts +3 -12
  459. package/src/drivers/types.ts +4 -23
  460. package/src/drivers/vault-driver-removal.test.ts +55 -0
  461. package/src/drivers/vault-env.ts +2 -6
  462. package/src/drivers/vault-infisical.ts +1 -5
  463. package/src/drivers/vault-managed.ts +1 -5
  464. package/src/drivers/vault-memory.ts +1 -5
  465. package/src/drivers/vault-openbao.test.ts +97 -0
  466. package/src/drivers/vault-openbao.ts +103 -40
  467. package/src/drivers/vault-types.ts +3 -16
  468. package/src/elements/ai/declare.ts +4 -14
  469. package/src/elements/ai/eval.ts +2 -6
  470. package/src/elements/ai/pii.ts +1 -3
  471. package/src/elements/ai/runtime.ts +12 -50
  472. package/src/elements/ai/schema.ts +4 -15
  473. package/src/elements/ai.test.ts +13 -24
  474. package/src/elements/ai.ts +1 -5
  475. package/src/elements/channel/consent.ts +3 -12
  476. package/src/elements/channel/costs.ts +4 -15
  477. package/src/elements/channel/declare.ts +1 -4
  478. package/src/elements/channel/email-auth.ts +1 -4
  479. package/src/elements/channel/locale.ts +2 -10
  480. package/src/elements/channel/mask.ts +1 -3
  481. package/src/elements/channel/mime.ts +2 -12
  482. package/src/elements/channel/outcomes.test.ts +1 -5
  483. package/src/elements/channel/outcomes.ts +5 -15
  484. package/src/elements/channel/receipts.ts +2 -7
  485. package/src/elements/channel/runtime.ts +28 -74
  486. package/src/elements/channel/suppression.ts +8 -14
  487. package/src/elements/channel.test.ts +8 -31
  488. package/src/elements/channel.ts +3 -14
  489. package/src/elements/clock/actions.ts +6 -18
  490. package/src/elements/clock/declare.ts +1 -3
  491. package/src/elements/clock/dst.ts +1 -5
  492. package/src/elements/clock/durable.ts +4 -16
  493. package/src/elements/clock/health.ts +2 -7
  494. package/src/elements/clock/leader.ts +2 -9
  495. package/src/elements/clock/reconcile.ts +4 -15
  496. package/src/elements/clock/runtime.ts +2 -5
  497. package/src/elements/clock/schedule.ts +4 -20
  498. package/src/elements/clock.test.ts +2 -7
  499. package/src/elements/clock.ts +5 -23
  500. package/src/elements/gate/permissions.ts +1 -4
  501. package/src/elements/gate/runtime.ts +7 -28
  502. package/src/elements/gate.ts +1 -5
  503. package/src/elements/index.ts +9 -7
  504. package/src/elements/signal/declare.ts +2 -7
  505. package/src/elements/signal/reconcile.test.ts +1 -4
  506. package/src/elements/signal/runtime.ts +1 -3
  507. package/src/elements/signal.test.ts +3 -15
  508. package/src/elements/signal.ts +2 -8
  509. package/src/elements/store/cache.test.ts +46 -0
  510. package/src/elements/store/cache.ts +3 -11
  511. package/src/elements/store/classify.ts +5 -22
  512. package/src/elements/store/declare.ts +11 -17
  513. package/src/elements/store/domain-ddl.test.ts +86 -0
  514. package/src/elements/store/emit-drizzle.ts +363 -0
  515. package/src/elements/store/files-policy.test.ts +1 -5
  516. package/src/elements/store/files-policy.ts +4 -12
  517. package/src/elements/store/load-plugin-tables.ts +103 -0
  518. package/src/elements/store/missing-relation.test.ts +83 -0
  519. package/src/elements/store/missing-relation.ts +38 -0
  520. package/src/elements/store/replica.ts +1 -4
  521. package/src/elements/store/resource-list-docs.fixture.ts +56 -0
  522. package/src/elements/store/resource-list-docs.test.ts +79 -0
  523. package/src/elements/store/resource.test.ts +253 -0
  524. package/src/elements/store/resource.ts +786 -0
  525. package/src/elements/store/runtime.ts +25 -28
  526. package/src/elements/store/schema-decl.test.ts +227 -0
  527. package/src/elements/store/schema-decl.ts +516 -0
  528. package/src/elements/store/sql-condition.test.ts +132 -0
  529. package/src/elements/store/sql-condition.ts +293 -65
  530. package/src/elements/store/sql-session.test.ts +135 -0
  531. package/src/elements/store/sql-session.ts +241 -84
  532. package/src/elements/store/table.ts +69 -23
  533. package/src/elements/store.test.ts +17 -39
  534. package/src/elements/store.ts +53 -31
  535. package/src/elements/vault/chain.ts +34 -0
  536. package/src/elements/vault/declare.ts +22 -32
  537. package/src/elements/vault/fingerprint.ts +2 -7
  538. package/src/elements/vault/redact.ts +2 -6
  539. package/src/elements/vault/runtime.ts +19 -33
  540. package/src/elements/vault.test.ts +12 -55
  541. package/src/elements/vault.ts +9 -13
  542. package/src/index.ts +5 -2
  543. package/src/kernel/adopt-routes.ts +15 -29
  544. package/src/kernel/app.ts +92 -118
  545. package/src/kernel/auth-resolve.ts +2 -9
  546. package/src/kernel/boot-bind/ai.ts +1 -4
  547. package/src/kernel/boot-bind/channel.test.ts +60 -0
  548. package/src/kernel/boot-bind/channel.ts +61 -5
  549. package/src/kernel/boot-bind/clock.ts +2 -6
  550. package/src/kernel/boot-bind/gate.ts +2 -8
  551. package/src/kernel/boot-bind/runs.ts +1 -3
  552. package/src/kernel/boot-bind/signal.ts +3 -10
  553. package/src/kernel/boot-bind/store.test.ts +27 -18
  554. package/src/kernel/boot-bind/store.ts +93 -52
  555. package/src/kernel/boot-bind/vault.ts +2 -8
  556. package/src/kernel/boot.test.ts +4 -10
  557. package/src/kernel/boot.ts +30 -77
  558. package/src/kernel/budget.test.ts +1 -3
  559. package/src/kernel/capability.ts +3 -10
  560. package/src/kernel/dry-run.test.ts +1 -4
  561. package/src/kernel/dry-run.ts +3 -11
  562. package/src/kernel/edge.test.ts +68 -0
  563. package/src/kernel/effects.test.ts +1 -5
  564. package/src/kernel/effects.ts +4 -11
  565. package/src/kernel/errors.registry.test.ts +2 -8
  566. package/src/kernel/errors.ts +23 -32
  567. package/src/kernel/flow.test.ts +12 -25
  568. package/src/kernel/flow.ts +13 -25
  569. package/src/kernel/fx.test.ts +31 -40
  570. package/src/kernel/fx.ts +135 -90
  571. package/src/kernel/hook-timing.test.ts +3 -6
  572. package/src/kernel/hook-timing.ts +5 -14
  573. package/src/kernel/hooks.test.ts +43 -10
  574. package/src/kernel/hooks.ts +25 -12
  575. package/src/kernel/index.ts +11 -17
  576. package/src/kernel/journal.ts +7 -31
  577. package/src/kernel/on.ts +45 -9
  578. package/src/kernel/pipeline.test.ts +10 -19
  579. package/src/kernel/pipeline.ts +5 -17
  580. package/src/kernel/plug.ts +4 -11
  581. package/src/kernel/plugin/conflicts.test.ts +13 -25
  582. package/src/kernel/plugin/decorate.test.ts +8 -13
  583. package/src/kernel/plugin/scoping.test.ts +4 -11
  584. package/src/kernel/plugin.ts +81 -27
  585. package/src/kernel/registry-isolation.test.ts +74 -0
  586. package/src/kernel/registry.ts +102 -30
  587. package/src/kernel/router.test.ts +15 -13
  588. package/src/kernel/router.ts +9 -29
  589. package/src/kernel/routing-budget.test.ts +1 -3
  590. package/src/kernel/run-telemetry.ts +1 -3
  591. package/src/kernel/triggers.ts +63 -21
  592. package/src/manifest/diff.test.ts +26 -91
  593. package/src/manifest/diff.ts +95 -225
  594. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  595. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  596. package/src/manifest/index.ts +3 -10
  597. package/src/manifest/types.ts +30 -6
  598. package/src/manifest/undeclared.test.ts +4 -12
  599. package/src/manifest/undeclared.ts +1 -2
  600. package/src/manifest/validate.test.ts +2 -6
  601. package/src/manifest/validate.ts +5 -15
  602. package/src/mcp/authorization.ts +8 -27
  603. package/src/mcp/confirmation.ts +3 -10
  604. package/src/mcp/data.ts +2 -6
  605. package/src/mcp/docs-index.ts +4 -7
  606. package/src/mcp/docs-mcp.test.ts +3 -12
  607. package/src/mcp/docs-server.ts +7 -27
  608. package/src/mcp/docs-tools.ts +2 -6
  609. package/src/mcp/index.ts +1 -6
  610. package/src/mcp/injection.gate.test.ts +4 -18
  611. package/src/mcp/mcp.test.ts +18 -49
  612. package/src/mcp/protocol.ts +3 -10
  613. package/src/mcp/server.ts +11 -52
  614. package/src/mcp/session.ts +4 -14
  615. package/src/mcp/tools.ts +14 -50
  616. package/src/plugins/catalogue.test.ts +3 -7
  617. package/src/plugins/catalogue.ts +1 -3
  618. package/src/plugins/compression.test.ts +127 -0
  619. package/src/plugins/compression.ts +94 -0
  620. package/src/plugins/config-source.test.ts +204 -0
  621. package/src/plugins/config-source.ts +209 -0
  622. package/src/plugins/cors.test.ts +138 -0
  623. package/src/plugins/cors.ts +129 -0
  624. package/src/plugins/csrf.test.ts +102 -0
  625. package/src/plugins/csrf.ts +86 -0
  626. package/src/plugins/headers.ts +54 -0
  627. package/src/plugins/index.ts +26 -0
  628. package/src/plugins/ip-allowlist.test.ts +105 -0
  629. package/src/plugins/ip-allowlist.ts +76 -0
  630. package/src/plugins/maintenance-mode.test.ts +91 -0
  631. package/src/plugins/maintenance-mode.ts +85 -0
  632. package/src/plugins/node-import-scan.ts +2 -5
  633. package/src/plugins/security-headers.test.ts +243 -0
  634. package/src/plugins/security-headers.ts +255 -0
  635. package/src/plugins/supply-chain.ts +5 -17
  636. package/src/release/exports.ts +3 -8
  637. package/src/release/measure.exports.test.ts +1 -3
  638. package/src/release/measure.ts +16 -45
  639. package/src/runs/bench.test.ts +1 -3
  640. package/src/runs/collect.ts +8 -25
  641. package/src/runs/drivers/clickhouse.ts +2 -9
  642. package/src/runs/drivers/files.ts +9 -31
  643. package/src/runs/drivers/memory.ts +5 -22
  644. package/src/runs/drivers/postgres.ts +8 -20
  645. package/src/runs/duckdb.ts +2 -8
  646. package/src/runs/index.ts +3 -14
  647. package/src/runs/outlier.ts +3 -10
  648. package/src/runs/parquet.ts +8 -26
  649. package/src/runs/privacy.ts +2 -8
  650. package/src/runs/runs.test.ts +3 -9
  651. package/src/runs/runtime.ts +7 -32
  652. package/src/runs/shred.ts +10 -29
  653. package/src/runs/types.ts +1 -4
  654. package/src/runtime/bun.ts +4 -15
  655. package/src/runtime/cold-start.bench.ts +1 -3
  656. package/src/runtime/dev-request-log.test.ts +1 -4
  657. package/src/runtime/dev-request-log.ts +1 -4
  658. package/src/runtime/index.ts +1 -6
  659. package/src/runtime/primitives.ts +5 -4
  660. package/src/runtime/security.ts +3 -13
  661. package/src/runtime/serve.test.ts +4 -7
  662. package/src/runtime/types.ts +1 -4
  663. package/src/runtime/web-standard.ts +3 -15
  664. package/src/term.test.ts +4 -6
  665. package/src/term.ts +49 -98
  666. package/src/test/create-test-app.test.ts +4 -14
  667. package/src/test/create-test-app.ts +27 -33
  668. package/src/test/provisions.integration.test.ts +3 -11
  669. package/src/upgrade/codemods.test.ts +25 -6
  670. package/src/upgrade/codemods.ts +116 -3
  671. package/src/validation/standard-schema.test.ts +2 -5
  672. package/src/validation/standard-schema.ts +13 -33
  673. package/docs/spec/console.md +0 -739
  674. package/docs/spec/example.md +0 -1196
  675. package/docs/spec/four-applications.md +0 -1195
  676. package/docs/spec/unified-theory.md +0 -489
  677. package/src/cli/doc-drift.test.ts +0 -146
  678. package/src/cli/doc-drift.ts +0 -430
  679. package/src/cli/doctor-diff-examples.ts +0 -92
  680. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
  681. package/src/drivers/vault-sops.ts +0 -271
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Compile Drizzle SQL operators (`eq`, `and`, `lt`, …) and plain equality
3
- * maps into `WHERE` SQL + bound params for {@link SqlConnection}.
2
+ * Compile Drizzle SQL operators (`eq`, `and`, `or`, `lt`, `like`, …) and plain
3
+ * equality maps into `WHERE` SQL + bound params for {@link SqlConnection}.
4
4
  *
5
5
  * Table-agnostic query-builder support — not domain helpers.
6
6
  */
@@ -11,7 +11,18 @@ export type WhereMap = Readonly<Record<string, unknown>>;
11
11
  /** One compiled predicate (`col op ?`). */
12
12
  export interface CompiledPredicate {
13
13
  readonly column: string;
14
- readonly op: "=" | "<" | ">" | "<=" | ">=" | "!=";
14
+ readonly op:
15
+ | "="
16
+ | "<"
17
+ | ">"
18
+ | "<="
19
+ | ">="
20
+ | "!="
21
+ | "like"
22
+ | "ilike"
23
+ | "in"
24
+ | "is null"
25
+ | "is not null";
15
26
  readonly value: unknown;
16
27
  }
17
28
 
@@ -22,6 +33,21 @@ export interface CompiledWhere {
22
33
  readonly predicates: readonly CompiledPredicate[];
23
34
  }
24
35
 
36
+ /** One compiled `ORDER BY` term. */
37
+ export interface CompiledOrder {
38
+ readonly column: string;
39
+ readonly direction: "ASC" | "DESC";
40
+ }
41
+
42
+ /** Intermediate compiled node — a leaf comparison or a joined group. */
43
+ interface CompiledNode {
44
+ readonly clause: string;
45
+ readonly params: readonly unknown[];
46
+ readonly predicates: readonly CompiledPredicate[];
47
+ }
48
+
49
+ const EMPTY_NODE: CompiledNode = { clause: "", params: [], predicates: [] };
50
+
25
51
  /**
26
52
  * True when `value` looks like a Drizzle `SQL` wrapper (`eq` / `and` / …).
27
53
  *
@@ -35,12 +61,37 @@ export function isDrizzleSql(value: unknown): boolean {
35
61
  );
36
62
  }
37
63
 
64
+ /** Marker for {@link andWhere} — avoids importing `drizzle-orm` here. */
65
+ export interface AndCondition {
66
+ readonly [andConditionBrand]: true;
67
+ readonly conditions: readonly unknown[];
68
+ }
69
+ const andConditionBrand: unique symbol = Symbol("oke.and");
70
+
71
+ /**
72
+ * Join conditions with `AND` without importing `drizzle-orm`.
73
+ * `undefined` entries are dropped; a single remaining condition compiles
74
+ * bare.
75
+ *
76
+ * @param conditions - Equality maps or Drizzle SQL
77
+ */
78
+ export function andWhere(...conditions: readonly unknown[]): unknown {
79
+ const kept = conditions.filter((c) => c !== undefined && c !== null);
80
+ if (kept.length === 1) return kept[0];
81
+ return { [andConditionBrand]: true, conditions: kept } satisfies AndCondition;
82
+ }
83
+
38
84
  /**
39
85
  * Compile a where input into SQL.
40
86
  *
41
87
  * Accepts:
42
88
  * - plain {@link WhereMap} equality maps
43
- * - Drizzle `SQL` from `eq` / `and` / `lt` / …
89
+ * - Drizzle `SQL` from `eq` / `and` / `or` / `lt` / `like` /
90
+ * - {@link AndCondition} from {@link andWhere}
91
+ *
92
+ * Nested `and(...)` / `or(...)` compile to parenthesized groups — `or` is
93
+ * never flattened into `AND`. Unsupported operators throw rather than
94
+ * silently dropping predicates.
44
95
  *
45
96
  * @param where - Condition
46
97
  */
@@ -50,40 +101,96 @@ export function compileWhere(where: unknown): CompiledWhere {
50
101
  }
51
102
 
52
103
  if (isDrizzleSql(where)) {
53
- const predicates = extractPredicates(where);
54
- return predicatesToWhere(predicates);
104
+ const node = compileSqlNode(where);
105
+ return { clause: node.clause, params: node.params, predicates: node.predicates };
106
+ }
107
+
108
+ if (isAndCondition(where)) {
109
+ const children = where.conditions.map(compileWhere).filter((c) => c.clause !== "");
110
+ if (children.length === 0) return { clause: "", params: [], predicates: [] };
111
+ if (children.length === 1) return children[0]!;
112
+ return {
113
+ clause: children.map((c) => `(${c.clause})`).join(" AND "),
114
+ params: children.flatMap((c) => c.params),
115
+ predicates: children.flatMap((c) => c.predicates),
116
+ };
55
117
  }
56
118
 
57
119
  if (typeof where === "object" && !Array.isArray(where)) {
58
- const predicates: CompiledPredicate[] = Object.entries(
59
- where as WhereMap,
60
- ).map(([column, value]) => ({
61
- column,
62
- op: "=" as const,
63
- value,
64
- }));
120
+ const predicates: CompiledPredicate[] = Object.entries(where as WhereMap).map(
121
+ ([column, value]) => ({
122
+ column,
123
+ op: "=" as const,
124
+ value,
125
+ }),
126
+ );
65
127
  return predicatesToWhere(predicates);
66
128
  }
67
129
 
68
- throw new TypeError(
69
- "sql where: expected equality map or Drizzle SQL condition",
130
+ throw new TypeError("sql where: expected equality map or Drizzle SQL condition");
131
+ }
132
+
133
+ function isAndCondition(value: unknown): value is AndCondition {
134
+ return (
135
+ typeof value === "object" &&
136
+ value !== null &&
137
+ (value as AndCondition)[andConditionBrand] === true
70
138
  );
71
139
  }
72
140
 
73
141
  /**
74
- * Turn predicates into `col = ? AND …` form.
142
+ * Compile Drizzle `asc()` / `desc()` terms into `ORDER BY` entries.
143
+ * Bare column references default to `ASC`.
144
+ *
145
+ * @param orders - Order terms (`asc(col)` / `desc(col)` SQL or columns)
146
+ */
147
+ export function compileOrderBy(orders: readonly unknown[]): readonly CompiledOrder[] {
148
+ return orders.map((order) => {
149
+ if (isDrizzleSql(order)) {
150
+ let column: string | undefined;
151
+ let text = "";
152
+ for (const chunk of chunksOf(order)) {
153
+ if (isDrizzleSql(chunk)) {
154
+ throw new TypeError("orderBy(): nested SQL is not supported");
155
+ }
156
+ const chunkString = chunkText(chunk);
157
+ if (chunkString !== undefined) {
158
+ text += chunkString;
159
+ continue;
160
+ }
161
+ const col = asColumn(chunk);
162
+ if (col === undefined) {
163
+ throw new TypeError("orderBy(): expected drizzle asc()/desc() SQL");
164
+ }
165
+ if (column !== undefined) {
166
+ throw new TypeError("orderBy(): one column per term");
167
+ }
168
+ column = col;
169
+ }
170
+ if (column === undefined) {
171
+ throw new TypeError("orderBy(): expected drizzle asc()/desc() SQL");
172
+ }
173
+ const dir = text.trim().toLowerCase();
174
+ if (dir === "" || dir === "asc") return { column, direction: "ASC" } as const;
175
+ if (dir === "desc") return { column, direction: "DESC" } as const;
176
+ throw new TypeError(`orderBy(): unsupported direction ${JSON.stringify(text.trim())}`);
177
+ }
178
+ const col = asColumn(order);
179
+ if (col !== undefined) return { column: col, direction: "ASC" } as const;
180
+ throw new TypeError("orderBy(): expected drizzle asc()/desc() SQL or a column");
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Turn predicates into `col = ? AND …` form (plain equality maps only).
75
186
  *
76
187
  * @param predicates - Ordered predicates
77
188
  */
78
- function predicatesToWhere(
79
- predicates: readonly CompiledPredicate[],
80
- ): CompiledWhere {
189
+ function predicatesToWhere(predicates: readonly CompiledPredicate[]): CompiledWhere {
81
190
  if (predicates.length === 0) {
82
191
  return { clause: "", params: [], predicates: [] };
83
192
  }
84
- const clause = predicates
85
- .map((p) => `${quoteIdent(p.column)} ${p.op} ?`)
86
- .join(" AND ");
193
+ const clause = predicates.map((p) => `${quoteIdent(p.column)} ${p.op} ?`).join(" AND ");
87
194
  return {
88
195
  clause,
89
196
  params: predicates.map((p) => p.value),
@@ -92,41 +199,145 @@ function predicatesToWhere(
92
199
  }
93
200
 
94
201
  /**
95
- * Walk Drizzle `queryChunks` and collect simple binary comparisons.
202
+ * Recursively compile one Drizzle `SQL` node. Nodes with nested `SQL`
203
+ * children are groups joined by `AND` / `OR` (detected from the separator
204
+ * chunks Drizzle emits); nodes without are leaf comparisons.
96
205
  *
97
- * @param node - Drizzle SQL or nested chunk
206
+ * @param node - Drizzle SQL wrapper
98
207
  */
99
- function extractPredicates(node: unknown): CompiledPredicate[] {
100
- const out: CompiledPredicate[] = [];
101
- walk(node, out);
102
- return out;
208
+ function compileSqlNode(node: unknown): CompiledNode {
209
+ const chunks = chunksOf(node);
210
+ const nested = chunks.filter(isDrizzleSql);
211
+
212
+ if (nested.length === 0) {
213
+ return compileLeaf(chunks);
214
+ }
215
+
216
+ // Group level: only structural parens and ` and ` / ` or ` separators may
217
+ // appear next to nested conditions — anything else is a loud failure.
218
+ let joiner: "AND" | "OR" | undefined;
219
+ for (const chunk of chunks) {
220
+ if (isDrizzleSql(chunk)) continue;
221
+ const text = chunkText(chunk);
222
+ if (text === undefined) {
223
+ throw new TypeError("sql where: unsupported mixed condition");
224
+ }
225
+ const token = text.trim().toLowerCase();
226
+ if (token === "" || token === "(" || token === ")") continue;
227
+ if (token === "and" || token === "or") {
228
+ const next = token.toUpperCase() as "AND" | "OR";
229
+ if (joiner !== undefined && joiner !== next) {
230
+ throw new TypeError("sql where: mixed AND/OR at one level — parenthesize explicitly");
231
+ }
232
+ joiner = next;
233
+ continue;
234
+ }
235
+ throw new TypeError(`sql where: unsupported fragment ${JSON.stringify(text)}`);
236
+ }
237
+
238
+ const children = nested.map(compileSqlNode).filter((c) => c.clause !== "");
239
+ if (children.length === 0) return EMPTY_NODE;
240
+ if (children.length === 1) return children[0] ?? EMPTY_NODE;
241
+ const sep = joiner ?? "AND";
242
+ return {
243
+ clause: children.map((c) => `(${c.clause})`).join(` ${sep} `),
244
+ params: children.flatMap((c) => c.params),
245
+ predicates: children.flatMap((c) => c.predicates),
246
+ };
103
247
  }
104
248
 
105
- function walk(node: unknown, out: CompiledPredicate[]): void {
106
- if (!node || typeof node !== "object") return;
107
- const chunks = (node as { queryChunks?: unknown[] }).queryChunks;
108
- if (!Array.isArray(chunks)) return;
249
+ /**
250
+ * Compile a leaf node (`col op ?`) from chunks with no nested `SQL`.
251
+ * Structural paren chunks are ignored; every other chunk must be part of the
252
+ * comparison or it throws. `is null` / `is not null` bind no value; `in`
253
+ * binds one placeholder per array element.
254
+ *
255
+ * @param chunks - Drizzle query chunks
256
+ */
257
+ function compileLeaf(chunks: readonly unknown[]): CompiledNode {
258
+ let column: string | undefined;
259
+ let op: CompiledPredicate["op"] | undefined;
260
+ let value: unknown;
261
+ let state: "start" | "column" | "op" | "done" = "start";
109
262
 
110
- // Flatten nested SQL wrappers first so we can scan for col / op / param.
111
- const flat: unknown[] = [];
112
- for (const c of chunks) {
113
- if (c && typeof c === "object" && "queryChunks" in (c as object)) {
114
- walk(c, out);
115
- } else {
116
- flat.push(c);
263
+ for (const chunk of chunks) {
264
+ if (Array.isArray(chunk)) {
265
+ if (state !== "op" || op !== "in") {
266
+ throw new TypeError("sql where: unsupported condition shape");
267
+ }
268
+ const values = chunk.map(asInArrayValue);
269
+ value = values;
270
+ state = "done";
271
+ continue;
272
+ }
273
+ const text = chunkText(chunk);
274
+ if (text !== undefined) {
275
+ const token = text.trim().replace(/\)+$/, "");
276
+ if (token === "" || token === "(" || token === ")") continue;
277
+ if (state === "column") {
278
+ op = asOperator(token);
279
+ if (op === undefined) {
280
+ throw new TypeError(`sql where: unsupported operator ${JSON.stringify(token)}`);
281
+ }
282
+ state = op === "is null" || op === "is not null" ? "done" : "op";
283
+ continue;
284
+ }
285
+ throw new TypeError(`sql where: unsupported fragment ${JSON.stringify(text)}`);
117
286
  }
287
+ const col = asColumn(chunk);
288
+ if (col !== undefined) {
289
+ if (state !== "start") {
290
+ throw new TypeError("sql where: unsupported condition shape");
291
+ }
292
+ column = col;
293
+ state = "column";
294
+ continue;
295
+ }
296
+ const param = asParamValue(chunk);
297
+ if (param.found) {
298
+ if (state !== "op") {
299
+ throw new TypeError("sql where: unsupported condition shape");
300
+ }
301
+ value = param.value;
302
+ state = "done";
303
+ continue;
304
+ }
305
+ throw new TypeError("sql where: unsupported condition chunk");
118
306
  }
119
307
 
120
- for (let i = 0; i < flat.length; i++) {
121
- const col = asColumn(flat[i]);
122
- if (!col) continue;
123
- const op = asOperator(flat[i + 1]);
124
- if (!op) continue;
125
- const param = asParam(flat[i + 2]);
126
- if (param === undefined) continue;
127
- out.push({ column: col, op, value: param });
128
- i += 2;
308
+ if (state === "start") return EMPTY_NODE;
309
+ if (state !== "done" || column === undefined || op === undefined) {
310
+ throw new TypeError("sql where: incomplete condition");
311
+ }
312
+ const predicate: CompiledPredicate = { column, op, value };
313
+ if (op === "is null" || op === "is not null") {
314
+ return { clause: `${quoteIdent(column)} ${op}`, params: [], predicates: [predicate] };
315
+ }
316
+ if (op === "in") {
317
+ const values = value as readonly unknown[];
318
+ const placeholders = values.map(() => "?").join(", ");
319
+ return {
320
+ clause: `${quoteIdent(column)} in (${placeholders})`,
321
+ params: [...values],
322
+ predicates: [predicate],
323
+ };
129
324
  }
325
+ return {
326
+ clause: `${quoteIdent(column)} ${op} ?`,
327
+ params: [value],
328
+ predicates: [predicate],
329
+ };
330
+ }
331
+
332
+ function chunksOf(node: unknown): readonly unknown[] {
333
+ return (node as { queryChunks: unknown[] }).queryChunks;
334
+ }
335
+
336
+ /** Joined text of a Drizzle `StringChunk` (`{ value: string[] }`). */
337
+ function chunkText(chunk: unknown): string | undefined {
338
+ if (!chunk || typeof chunk !== "object") return undefined;
339
+ const value = (chunk as { value?: unknown }).value;
340
+ return Array.isArray(value) ? value.join("") : undefined;
130
341
  }
131
342
 
132
343
  function asColumn(chunk: unknown): string | undefined {
@@ -135,16 +346,8 @@ function asColumn(chunk: unknown): string | undefined {
135
346
  return typeof name === "string" && name.length > 0 ? name : undefined;
136
347
  }
137
348
 
138
- function asOperator(chunk: unknown): CompiledPredicate["op"] | undefined {
139
- if (!chunk || typeof chunk !== "object") return undefined;
140
- const value = (chunk as { value?: unknown }).value;
141
- const text = Array.isArray(value)
142
- ? value.join("")
143
- : typeof value === "string"
144
- ? value
145
- : "";
146
- const trimmed = text.trim();
147
- switch (trimmed) {
349
+ function asOperator(token: string): CompiledPredicate["op"] | undefined {
350
+ switch (token.toLowerCase()) {
148
351
  case "=":
149
352
  return "=";
150
353
  case "<":
@@ -158,18 +361,43 @@ function asOperator(chunk: unknown): CompiledPredicate["op"] | undefined {
158
361
  case "!=":
159
362
  case "<>":
160
363
  return "!=";
364
+ case "like":
365
+ return "like";
366
+ case "ilike":
367
+ return "ilike";
368
+ case "in":
369
+ return "in";
370
+ case "is null":
371
+ return "is null";
372
+ case "is not null":
373
+ return "is not null";
161
374
  default:
162
375
  return undefined;
163
376
  }
164
377
  }
165
378
 
166
- function asParam(chunk: unknown): unknown {
167
- if (!chunk || typeof chunk !== "object") return undefined;
168
- if (!("value" in (chunk as object))) return undefined;
169
- // StringChunk also has `.value` (array of strings) — skip those.
170
- if (Array.isArray((chunk as { value: unknown }).value)) return undefined;
171
- if ((chunk as { name?: unknown }).name) return undefined;
172
- return (chunk as { value: unknown }).value;
379
+ /** Unwrap one element of a Drizzle `inArray` value array. */
380
+ function asInArrayValue(item: unknown): unknown {
381
+ const param = asParamValue(item);
382
+ if (!param.found) {
383
+ throw new TypeError("sql where: unsupported in-list value");
384
+ }
385
+ return param.value;
386
+ }
387
+
388
+ /**
389
+ * Param values arrive either as Drizzle `Param` instances (wrapped by
390
+ * `bindIfParam`) or as raw primitives interpolated by `sql` templates
391
+ * (`like`, `ilike`, …).
392
+ */
393
+ function asParamValue(chunk: unknown): { readonly found: boolean; readonly value?: unknown } {
394
+ if (chunk === undefined) return { found: false };
395
+ if (chunk === null || typeof chunk !== "object") return { found: true, value: chunk };
396
+ if (Array.isArray(chunk)) return { found: false };
397
+ if (!("value" in (chunk as object))) return { found: false };
398
+ if (Array.isArray((chunk as { value: unknown }).value)) return { found: false };
399
+ if ((chunk as { name?: unknown }).name) return { found: false };
400
+ return { found: true, value: (chunk as { value: unknown }).value };
173
401
  }
174
402
 
175
403
  /**
@@ -0,0 +1,135 @@
1
+ /**
2
+ * SqlStoreHandle surface lock — single-table SQL session only.
3
+ *
4
+ * Relational `with:` / Drizzle RQB (`db.query.*.findMany({ with })`) is a
5
+ * documented limitation: effects, cache keys, and PII masking assume one
6
+ * table per call, so no relational query surface may appear on the handle.
7
+ */
8
+
9
+ import { describe, expect, test } from "bun:test";
10
+ import { Database } from "bun:sqlite";
11
+ import { and, desc, eq, like, lt, or } from "drizzle-orm";
12
+ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
13
+ import { sqliteDriver } from "../../drivers/sqlite.ts";
14
+ import type { SqlConnection } from "../../drivers/types.ts";
15
+ import { createSqlStoreHandle, type SqlStoreHandle } from "./sql-session.ts";
16
+
17
+ const posts = sqliteTable("posts", {
18
+ id: text("id").primaryKey(),
19
+ title: text("title").notNull(),
20
+ createdAt: integer("created_at").notNull(),
21
+ });
22
+
23
+ async function openHandle(): Promise<{ handle: SqlStoreHandle; conn: SqlConnection }> {
24
+ const conn = await sqliteDriver.connect({
25
+ client: new Database(":memory:"),
26
+ role: "primary",
27
+ });
28
+ const handle = createSqlStoreHandle("sql:app", {
29
+ connection: conn,
30
+ classifications: new Map(),
31
+ routedRole: "primary",
32
+ domainDdl: "ensure",
33
+ });
34
+ return { handle, conn };
35
+ }
36
+
37
+ describe("SqlStoreHandle — no relational query surface (path b)", () => {
38
+ test("created handle exposes exactly the single-table surface", async () => {
39
+ const db = new Database(":memory:");
40
+ const conn = await sqliteDriver.connect({ client: db, role: "primary" });
41
+ const handle = createSqlStoreHandle("sql:app", {
42
+ connection: conn,
43
+ classifications: new Map(),
44
+ routedRole: "primary",
45
+ domainDdl: "ensure",
46
+ });
47
+
48
+ expect(Object.keys(handle).sort()).toEqual(
49
+ [
50
+ "ref",
51
+ "routedRole",
52
+ "driverId",
53
+ "select",
54
+ "insert",
55
+ "update",
56
+ "findById",
57
+ "delete",
58
+ "exists",
59
+ "increment",
60
+ "raw",
61
+ "count",
62
+ "page",
63
+ "ensureTable",
64
+ ].sort(),
65
+ );
66
+
67
+ // No Drizzle RQB / with: surface — ever.
68
+ expect("query" in handle).toBe(false);
69
+ expect("findMany" in handle).toBe(false);
70
+ expect("findFirst" in handle).toBe(false);
71
+ expect("with" in handle).toBe(false);
72
+
73
+ await conn.close();
74
+ });
75
+ });
76
+
77
+ describe("SqlStoreHandle — orderBy / limit select chain", () => {
78
+ test("orderBy + limit without where", async () => {
79
+ const { handle, conn } = await openHandle();
80
+ await handle.insert(posts).values({ id: "p1", title: "one", createdAt: 100 });
81
+ await handle.insert(posts).values({ id: "p2", title: "two", createdAt: 300 });
82
+ await handle.insert(posts).values({ id: "p3", title: "three", createdAt: 200 });
83
+
84
+ const rows = await handle.select().from(posts).orderBy(desc(posts.createdAt)).limit(2);
85
+ expect(rows.map((r) => r.id)).toEqual(["p2", "p3"]);
86
+ await conn.close();
87
+ });
88
+
89
+ test("limit directly on from() — no where required", async () => {
90
+ const { handle, conn } = await openHandle();
91
+ await handle.insert(posts).values({ id: "p1", title: "one", createdAt: 100 });
92
+ await handle.insert(posts).values({ id: "p2", title: "two", createdAt: 300 });
93
+
94
+ const rows = await handle.select().from(posts).limit(1);
95
+ expect(rows).toHaveLength(1);
96
+ await conn.close();
97
+ });
98
+
99
+ test("composite cursor predicate: where → orderBy → limit", async () => {
100
+ const { handle, conn } = await openHandle();
101
+ await handle.insert(posts).values({ id: "p1", title: "one", createdAt: 100 });
102
+ await handle.insert(posts).values({ id: "p2", title: "two", createdAt: 300 });
103
+ await handle.insert(posts).values({ id: "p3", title: "three", createdAt: 200 });
104
+ // Tie on createdAt=200 with an id that sorts after the cursor id.
105
+ await handle.insert(posts).values({ id: "p0", title: "four", createdAt: 200 });
106
+
107
+ const order = [desc(posts.createdAt), desc(posts.id)] as const;
108
+ const page1 = await handle
109
+ .select()
110
+ .from(posts)
111
+ .orderBy(...order)
112
+ .limit(2);
113
+ expect(page1.map((r) => r.id)).toEqual(["p2", "p3"]);
114
+
115
+ // (createdAt, id) < (200, "p3") — the second page of a keyset cursor.
116
+ const page2 = await handle
117
+ .select()
118
+ .from(posts)
119
+ .where(or(lt(posts.createdAt, 200), and(eq(posts.createdAt, 200), lt(posts.id, "p3"))))
120
+ .orderBy(...order)
121
+ .limit(2);
122
+ expect(page2.map((r) => r.id)).toEqual(["p0", "p1"]);
123
+ await conn.close();
124
+ });
125
+
126
+ test("like filters rows through the session (sqlite ASCII-insensitive)", async () => {
127
+ const { handle, conn } = await openHandle();
128
+ await handle.insert(posts).values({ id: "p1", title: "alpha", createdAt: 100 });
129
+ await handle.insert(posts).values({ id: "p2", title: "bravo", createdAt: 200 });
130
+
131
+ const rows = await handle.select().from(posts).where(like(posts.title, "A%"));
132
+ expect(rows.map((r) => r.id)).toEqual(["p1"]);
133
+ await conn.close();
134
+ });
135
+ });