okengine 0.2.5 → 0.2.8

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 (584) hide show
  1. package/AGENTS.md +24 -23
  2. package/README.md +52 -346
  3. package/docs/spec/console.md +165 -142
  4. package/docs/spec/example.md +490 -312
  5. package/docs/spec/four-applications.md +495 -314
  6. package/docs/spec/unified-theory.md +184 -175
  7. package/package.json +46 -38
  8. package/spec/manifest.v1.schema.json +40 -9
  9. package/src/auth/api-keys.ts +2 -5
  10. package/src/auth/attenuation.ts +2 -6
  11. package/src/auth/auth.test.ts +53 -31
  12. package/src/auth/invoke-as.ts +1 -3
  13. package/src/auth/operator.ts +8 -26
  14. package/src/auth/planes.ts +2 -7
  15. package/src/auth/plugin.ts +4 -7
  16. package/src/auth/roles.ts +1 -5
  17. package/src/auth/sessions.ts +10 -39
  18. package/src/cli/args.ts +1 -5
  19. package/src/cli/ask-dev-mode.ts +40 -0
  20. package/src/cli/build.ts +1 -2
  21. package/src/cli/client-add.test.ts +8 -15
  22. package/src/cli/client-add.ts +5 -14
  23. package/src/cli/completion.test.ts +1 -5
  24. package/src/cli/completion.ts +4 -17
  25. package/src/cli/db-auto-push.test.ts +64 -0
  26. package/src/cli/db-auto-push.ts +72 -0
  27. package/src/cli/db.test.ts +189 -0
  28. package/src/cli/db.ts +386 -0
  29. package/src/cli/dev-app-runner.ts +2 -8
  30. package/src/cli/dev-db-push.test.ts +163 -0
  31. package/src/cli/dev-mode.test.ts +45 -0
  32. package/src/cli/dev-mode.ts +78 -0
  33. package/src/cli/dev.test.ts +148 -21
  34. package/src/cli/dev.ts +208 -134
  35. package/src/cli/doc-drift.test.ts +3 -2
  36. package/src/cli/doc-drift.ts +21 -50
  37. package/src/cli/doc-staleness.test.ts +69 -0
  38. package/src/cli/docker-cli.test.ts +7 -8
  39. package/src/cli/docker.ts +1 -3
  40. package/src/cli/doctor-diff-examples.ts +1 -3
  41. package/src/cli/doctor-diff.test.ts +1 -5
  42. package/src/cli/doctor-diff.ts +17 -46
  43. package/src/cli/doctor-pii.ts +2 -7
  44. package/src/cli/doctor.test.ts +26 -4
  45. package/src/cli/doctor.ts +35 -11
  46. package/src/cli/eval.ts +4 -14
  47. package/src/cli/gates-list.ts +1 -4
  48. package/src/cli/hero-meta.test.ts +17 -19
  49. package/src/cli/hero-meta.ts +20 -30
  50. package/src/cli/images.ts +3 -8
  51. package/src/cli/index.ts +11 -2
  52. package/src/cli/load-config.images.test.ts +14 -14
  53. package/src/cli/load-config.ts +41 -47
  54. package/src/cli/mcp-from-console.ts +1 -4
  55. package/src/cli/mode.ts +45 -0
  56. package/src/cli/ports.test.ts +4 -13
  57. package/src/cli/ports.ts +1 -3
  58. package/src/cli/privacy-erase.ts +1 -3
  59. package/src/cli/registry.ts +75 -25
  60. package/src/cli/schema.ts +3 -10
  61. package/src/cli/stack.ts +2 -7
  62. package/src/cli/start.ts +4 -15
  63. package/src/cli/upgrade.ts +1 -4
  64. package/src/client/budget-entry.ts +1 -6
  65. package/src/client/create.test.ts +6 -12
  66. package/src/client/create.ts +2 -8
  67. package/src/client/errors.ts +1 -4
  68. package/src/client/index.ts +1 -6
  69. package/src/client/notes-contract.test.ts +4 -13
  70. package/src/client/transport.ts +9 -30
  71. package/src/client/types.ts +8 -29
  72. package/src/compiler/aot.test.ts +2 -9
  73. package/src/compiler/aot.ts +1 -4
  74. package/src/compiler/differential.test.ts +3 -11
  75. package/src/compiler/dynamic.ts +1 -4
  76. package/src/compiler/effects-infer.ts +8 -34
  77. package/src/compiler/emit.ts +1 -3
  78. package/src/compiler/extract.test.ts +96 -30
  79. package/src/compiler/extract.ts +362 -156
  80. package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
  81. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
  82. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
  83. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
  84. package/src/compiler/fixtures/skyport.expected.json +24 -83
  85. package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
  86. package/src/compiler/http-parse.ts +2 -9
  87. package/src/compiler/index.ts +1 -5
  88. package/src/compiler/response.ts +4 -7
  89. package/src/compiler/sucrose.ts +11 -31
  90. package/src/config/define-config.test.ts +57 -34
  91. package/src/config/index.ts +181 -37
  92. package/src/config/resolve-driver.test.ts +28 -17
  93. package/src/console/budget.test.ts +1 -4
  94. package/src/console/index.ts +1 -4
  95. package/src/console/server/access.test.ts +15 -43
  96. package/src/console/server/access.ts +19 -49
  97. package/src/console/server/ai.test.ts +3 -11
  98. package/src/console/server/ai.ts +23 -46
  99. package/src/console/server/app.ts +14 -45
  100. package/src/console/server/auth-rate.test.ts +4 -12
  101. package/src/console/server/bind.ts +1 -5
  102. package/src/console/server/channels.test.ts +2 -6
  103. package/src/console/server/channels.ts +10 -35
  104. package/src/console/server/claim.ts +2 -8
  105. package/src/console/server/clock.test.ts +2 -6
  106. package/src/console/server/clock.ts +8 -26
  107. package/src/console/server/console.test.ts +1 -3
  108. package/src/console/server/diff.test.ts +5 -17
  109. package/src/console/server/diff.ts +10 -31
  110. package/src/console/server/dry-run.audit.test.ts +2 -8
  111. package/src/console/server/flows.ts +64 -233
  112. package/src/console/server/gates.test.ts +14 -50
  113. package/src/console/server/gates.ts +13 -48
  114. package/src/console/server/index.ts +2 -9
  115. package/src/console/server/live.ts +3 -12
  116. package/src/console/server/operator-db.test.ts +4 -15
  117. package/src/console/server/operator-db.ts +13 -32
  118. package/src/console/server/plugin.ts +50 -53
  119. package/src/console/server/plugins.test.ts +2 -6
  120. package/src/console/server/plugins.ts +17 -56
  121. package/src/console/server/runs-pii.ts +2 -7
  122. package/src/console/server/security.gate.test.ts +18 -40
  123. package/src/console/server/serve.ts +10 -25
  124. package/src/console/server/signals.test.ts +1 -3
  125. package/src/console/server/signals.ts +17 -40
  126. package/src/console/server/state.ts +21 -59
  127. package/src/console/server/store.test.ts +20 -22
  128. package/src/console/server/store.ts +31 -76
  129. package/src/console/server/vault.test.ts +12 -29
  130. package/src/console/server/vault.ts +23 -60
  131. package/src/console/ui/access/AccessA11yView.tsx +6 -21
  132. package/src/console/ui/access/a11y.test.tsx +3 -12
  133. package/src/console/ui/access/acknowledgement.test.ts +1 -4
  134. package/src/console/ui/access/acknowledgement.ts +1 -3
  135. package/src/console/ui/access/blast-radius.ts +1 -3
  136. package/src/console/ui/access/confirmation.test.ts +1 -5
  137. package/src/console/ui/access/confirmation.ts +1 -4
  138. package/src/console/ui/access/fixture.ts +1 -5
  139. package/src/console/ui/access/grantable.test.ts +1 -3
  140. package/src/console/ui/access/grantable.ts +2 -7
  141. package/src/console/ui/access/index.ts +2 -9
  142. package/src/console/ui/access/provenance.test.ts +1 -3
  143. package/src/console/ui/access/provenance.ts +2 -6
  144. package/src/console/ui/access/search.test.ts +1 -5
  145. package/src/console/ui/access/search.ts +2 -6
  146. package/src/console/ui/ai/AiA11yView.tsx +6 -17
  147. package/src/console/ui/ai/a11y.test.tsx +2 -5
  148. package/src/console/ui/ai/fixture.ts +2 -7
  149. package/src/console/ui/ai/format.ts +1 -3
  150. package/src/console/ui/ai/group.test.ts +1 -3
  151. package/src/console/ui/ai/group.ts +4 -12
  152. package/src/console/ui/ai/index.ts +2 -9
  153. package/src/console/ui/ai/promotion.test.ts +2 -7
  154. package/src/console/ui/ai/promotion.ts +4 -13
  155. package/src/console/ui/ai/search.test.ts +2 -11
  156. package/src/console/ui/ai/search.ts +3 -13
  157. package/src/console/ui/ai/types.ts +1 -8
  158. package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
  159. package/src/console/ui/architecture/a11y.test.tsx +1 -2
  160. package/src/console/ui/architecture/declared.ts +1 -5
  161. package/src/console/ui/architecture/index.ts +7 -35
  162. package/src/console/ui/architecture/layout.ts +1 -3
  163. package/src/console/ui/architecture/pathologies.ts +10 -19
  164. package/src/console/ui/architecture/search.ts +6 -15
  165. package/src/console/ui/architecture/traffic.ts +4 -14
  166. package/src/console/ui/architecture/types.ts +2 -10
  167. package/src/console/ui/architecture/view.test.ts +7 -19
  168. package/src/console/ui/architecture/view.ts +9 -36
  169. package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
  170. package/src/console/ui/channels/a11y.test.tsx +1 -2
  171. package/src/console/ui/channels/confirmation.test.ts +1 -4
  172. package/src/console/ui/channels/confirmation.ts +1 -4
  173. package/src/console/ui/channels/findings.ts +1 -5
  174. package/src/console/ui/channels/index.ts +2 -8
  175. package/src/console/ui/channels/locale.test.ts +1 -3
  176. package/src/console/ui/channels/locale.ts +2 -8
  177. package/src/console/ui/channels/search.ts +4 -13
  178. package/src/console/ui/channels/taxonomy.ts +1 -3
  179. package/src/console/ui/clock/ClockA11yView.tsx +9 -26
  180. package/src/console/ui/clock/a11y.test.tsx +2 -5
  181. package/src/console/ui/clock/confirmation.test.ts +1 -3
  182. package/src/console/ui/clock/confirmation.ts +1 -4
  183. package/src/console/ui/clock/findings.ts +1 -4
  184. package/src/console/ui/clock/health.ts +2 -7
  185. package/src/console/ui/clock/index.ts +2 -10
  186. package/src/console/ui/clock/search.test.ts +1 -3
  187. package/src/console/ui/clock/search.ts +2 -6
  188. package/src/console/ui/clock/timeline.test.ts +1 -3
  189. package/src/console/ui/clock/waiting-on.test.ts +1 -5
  190. package/src/console/ui/clock/waiting-on.ts +2 -7
  191. package/src/console/ui/diff/DiffA11yView.tsx +8 -17
  192. package/src/console/ui/diff/a11y.test.tsx +2 -5
  193. package/src/console/ui/diff/group.test.ts +3 -13
  194. package/src/console/ui/diff/group.ts +2 -8
  195. package/src/console/ui/diff/index.ts +2 -8
  196. package/src/console/ui/diff/search.test.ts +1 -5
  197. package/src/console/ui/diff/search.ts +4 -16
  198. package/src/console/ui/diff/summary.ts +1 -2
  199. package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
  200. package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
  201. package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
  202. package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
  203. package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
  204. package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
  205. package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
  206. package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
  207. package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
  208. package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
  209. package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
  210. package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
  211. package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
  212. package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
  213. package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
  214. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
  215. package/src/console/ui/dist/index.html +1 -1
  216. package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
  217. package/src/console/ui/flows/a11y.test.tsx +2 -8
  218. package/src/console/ui/flows/buffer.test.ts +3 -11
  219. package/src/console/ui/flows/buffer.ts +4 -11
  220. package/src/console/ui/flows/confirmation.ts +1 -3
  221. package/src/console/ui/flows/contract.test.ts +3 -7
  222. package/src/console/ui/flows/contract.ts +12 -36
  223. package/src/console/ui/flows/graph.test.ts +3 -11
  224. package/src/console/ui/flows/graph.ts +5 -15
  225. package/src/console/ui/flows/index.ts +1 -6
  226. package/src/console/ui/flows/save-as-test.ts +1 -3
  227. package/src/console/ui/flows/search.ts +8 -25
  228. package/src/console/ui/flows/tiers.ts +2 -9
  229. package/src/console/ui/gates/GatesA11yView.tsx +4 -13
  230. package/src/console/ui/gates/a11y.test.tsx +2 -5
  231. package/src/console/ui/gates/audit.ts +1 -3
  232. package/src/console/ui/gates/denial.test.ts +1 -3
  233. package/src/console/ui/gates/denial.ts +4 -12
  234. package/src/console/ui/gates/findings.ts +1 -3
  235. package/src/console/ui/gates/group.test.ts +3 -3
  236. package/src/console/ui/gates/group.ts +3 -11
  237. package/src/console/ui/gates/index.ts +2 -10
  238. package/src/console/ui/gates/search.ts +3 -10
  239. package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
  240. package/src/console/ui/overview/a11y.test.tsx +1 -2
  241. package/src/console/ui/overview/busiest.ts +1 -3
  242. package/src/console/ui/overview/compose.ts +1 -13
  243. package/src/console/ui/overview/cost.ts +34 -38
  244. package/src/console/ui/overview/fixture.ts +21 -26
  245. package/src/console/ui/overview/golden.ts +4 -14
  246. package/src/console/ui/overview/index.ts +2 -9
  247. package/src/console/ui/overview/rank.test.ts +3 -10
  248. package/src/console/ui/overview/rank.ts +2 -7
  249. package/src/console/ui/overview/slo.test.ts +5 -9
  250. package/src/console/ui/overview/slo.ts +6 -23
  251. package/src/console/ui/overview/verdict.ts +3 -10
  252. package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
  253. package/src/console/ui/plugins/a11y.test.tsx +1 -2
  254. package/src/console/ui/plugins/fixture.ts +2 -4
  255. package/src/console/ui/plugins/group.test.ts +1 -3
  256. package/src/console/ui/plugins/group.ts +1 -6
  257. package/src/console/ui/plugins/index.ts +3 -13
  258. package/src/console/ui/plugins/search.ts +7 -19
  259. package/src/console/ui/runs/RunsA11yView.tsx +5 -20
  260. package/src/console/ui/runs/a11y.test.tsx +2 -5
  261. package/src/console/ui/runs/explain.ts +2 -6
  262. package/src/console/ui/runs/group.ts +3 -12
  263. package/src/console/ui/runs/histogram.test.ts +2 -11
  264. package/src/console/ui/runs/histogram.ts +1 -4
  265. package/src/console/ui/runs/index.ts +4 -18
  266. package/src/console/ui/runs/project.ts +2 -6
  267. package/src/console/ui/runs/query.test.ts +1 -3
  268. package/src/console/ui/runs/query.ts +12 -35
  269. package/src/console/ui/runs/search.ts +5 -19
  270. package/src/console/ui/runs/trace-link.test.ts +2 -6
  271. package/src/console/ui/runs/trace-link.ts +4 -16
  272. package/src/console/ui/runs/types.ts +1 -3
  273. package/src/console/ui/shell/App.tsx +2 -10
  274. package/src/console/ui/shell/client.ts +22 -115
  275. package/src/console/ui/shell/components/ui.tsx +1 -4
  276. package/src/console/ui/shell/layout/Shell.tsx +2 -4
  277. package/src/console/ui/shell/main.tsx +31 -96
  278. package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
  279. package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
  280. package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
  281. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
  282. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
  283. package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
  284. package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
  285. package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
  286. package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
  287. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
  288. package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
  289. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
  290. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
  291. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
  292. package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
  293. package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
  294. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
  295. package/src/console/ui/shell/setup/Wizard.tsx +3 -5
  296. package/src/console/ui/shell/styles.css +1 -2
  297. package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
  298. package/src/console/ui/signals/a11y.test.tsx +2 -5
  299. package/src/console/ui/signals/confirmation.test.ts +1 -5
  300. package/src/console/ui/signals/confirmation.ts +2 -6
  301. package/src/console/ui/signals/dry-run.ts +1 -3
  302. package/src/console/ui/signals/durable.ts +1 -3
  303. package/src/console/ui/signals/findings.ts +1 -3
  304. package/src/console/ui/signals/group.test.ts +2 -8
  305. package/src/console/ui/signals/group.ts +2 -10
  306. package/src/console/ui/signals/index.ts +1 -4
  307. package/src/console/ui/signals/monitor.ts +2 -7
  308. package/src/console/ui/signals/schema-form.test.ts +3 -14
  309. package/src/console/ui/signals/schema-form.ts +1 -4
  310. package/src/console/ui/signals/search.ts +4 -13
  311. package/src/console/ui/store/StoreA11yView.tsx +3 -9
  312. package/src/console/ui/store/a11y.test.tsx +2 -5
  313. package/src/console/ui/store/confirmation.test.ts +1 -3
  314. package/src/console/ui/store/confirmation.ts +1 -4
  315. package/src/console/ui/store/dry-run.ts +2 -5
  316. package/src/console/ui/store/fixture.ts +1 -2
  317. package/src/console/ui/store/group.test.ts +1 -6
  318. package/src/console/ui/store/group.ts +1 -4
  319. package/src/console/ui/store/index.ts +1 -5
  320. package/src/console/ui/store/search.ts +2 -6
  321. package/src/console/ui/store/will-not-fire.ts +1 -2
  322. package/src/console/ui/traces/TracesA11yView.tsx +10 -29
  323. package/src/console/ui/traces/a11y.test.tsx +1 -2
  324. package/src/console/ui/traces/chain.test.ts +5 -19
  325. package/src/console/ui/traces/chain.ts +1 -3
  326. package/src/console/ui/traces/critical-path.test.ts +1 -3
  327. package/src/console/ui/traces/critical-path.ts +2 -6
  328. package/src/console/ui/traces/filter.test.ts +2 -6
  329. package/src/console/ui/traces/filter.ts +5 -13
  330. package/src/console/ui/traces/fold.test.ts +4 -12
  331. package/src/console/ui/traces/fold.ts +1 -3
  332. package/src/console/ui/traces/mini.ts +1 -3
  333. package/src/console/ui/traces/replay.test.ts +1 -3
  334. package/src/console/ui/traces/replay.ts +1 -3
  335. package/src/console/ui/traces/sampling.ts +1 -4
  336. package/src/console/ui/traces/search.ts +4 -17
  337. package/src/console/ui/traces/tier.ts +1 -3
  338. package/src/console/ui/vault/VaultA11yView.tsx +5 -17
  339. package/src/console/ui/vault/a11y.test.tsx +1 -2
  340. package/src/console/ui/vault/blast-radius.ts +1 -3
  341. package/src/console/ui/vault/confirmation.test.ts +1 -5
  342. package/src/console/ui/vault/confirmation.ts +1 -4
  343. package/src/console/ui/vault/dormant.test.ts +1 -4
  344. package/src/console/ui/vault/export-safe.test.ts +1 -5
  345. package/src/console/ui/vault/export-safe.ts +1 -4
  346. package/src/console/ui/vault/fixture.ts +3 -3
  347. package/src/console/ui/vault/group.ts +1 -2
  348. package/src/console/ui/vault/index.ts +2 -10
  349. package/src/console/ui/vault/search.ts +2 -6
  350. package/src/console/ui/vault/types.ts +1 -1
  351. package/src/console/xss.gate.test.ts +1 -3
  352. package/src/docker/compose.ts +112 -20
  353. package/src/docker/derive.ts +5 -19
  354. package/src/docker/docker.test.ts +15 -15
  355. package/src/docker/dockerfile.integration.test.ts +12 -21
  356. package/src/docker/helpers.ts +5 -4
  357. package/src/docker/index.ts +4 -11
  358. package/src/docker/pin.ts +1 -9
  359. package/src/docker/recipes/index.ts +5 -6
  360. package/src/docker/recipes/mailpit.ts +22 -0
  361. package/src/docker/recipes/postgres.ts +17 -6
  362. package/src/docker/recipes/redis.ts +6 -1
  363. package/src/docker/recipes/rustfs.ts +33 -0
  364. package/src/docker/stack-id.test.ts +33 -0
  365. package/src/docker/stack-id.ts +25 -13
  366. package/src/docker/stack.integration.test.ts +6 -21
  367. package/src/docker/types.ts +13 -4
  368. package/src/drivers/ai-anthropic.ts +5 -14
  369. package/src/drivers/ai-mock.ts +2 -5
  370. package/src/drivers/ai-openai-compatible.ts +4 -10
  371. package/src/drivers/ai-providers.test.ts +6 -13
  372. package/src/drivers/bun-native-completeness.test.ts +6 -15
  373. package/src/drivers/channel-console.ts +6 -7
  374. package/src/drivers/channel-fcm.ts +20 -30
  375. package/src/drivers/channel-resend.ts +1 -3
  376. package/src/drivers/channel-smtp.ts +2 -6
  377. package/src/drivers/channel-types.ts +2 -12
  378. package/src/drivers/channel-unifonic.ts +2 -6
  379. package/src/drivers/channel-wa-cloud.ts +2 -6
  380. package/src/drivers/channel-webpush.ts +21 -60
  381. package/src/drivers/conformance.test.ts +2 -10
  382. package/src/drivers/conformance.ts +8 -21
  383. package/src/drivers/fs.ts +2 -8
  384. package/src/drivers/index.ts +12 -56
  385. package/src/drivers/memory.ts +14 -41
  386. package/src/drivers/pgvector.ts +7 -11
  387. package/src/drivers/postgres.ts +7 -13
  388. package/src/drivers/redis.ts +13 -37
  389. package/src/drivers/s3.ts +5 -17
  390. package/src/drivers/signal-engine.ts +12 -49
  391. package/src/drivers/signal-memory.ts +2 -8
  392. package/src/drivers/signal-nats.ts +4 -9
  393. package/src/drivers/signal-postgres.ts +62 -129
  394. package/src/drivers/signal-redis.ts +7 -21
  395. package/src/drivers/signal-types.ts +1 -4
  396. package/src/drivers/sqlite.ts +3 -12
  397. package/src/drivers/types.ts +4 -23
  398. package/src/drivers/vault-env.ts +2 -6
  399. package/src/drivers/vault-infisical.ts +1 -5
  400. package/src/drivers/vault-managed.ts +1 -5
  401. package/src/drivers/vault-memory.ts +1 -5
  402. package/src/drivers/vault-openbao.ts +1 -5
  403. package/src/drivers/vault-sops.ts +10 -35
  404. package/src/drivers/vault-types.ts +1 -7
  405. package/src/elements/ai/declare.ts +4 -14
  406. package/src/elements/ai/eval.ts +2 -6
  407. package/src/elements/ai/pii.ts +1 -3
  408. package/src/elements/ai/runtime.ts +12 -50
  409. package/src/elements/ai/schema.ts +4 -15
  410. package/src/elements/ai.test.ts +13 -24
  411. package/src/elements/ai.ts +1 -5
  412. package/src/elements/channel/consent.ts +3 -12
  413. package/src/elements/channel/costs.ts +4 -15
  414. package/src/elements/channel/declare.ts +1 -4
  415. package/src/elements/channel/email-auth.ts +1 -4
  416. package/src/elements/channel/locale.ts +2 -10
  417. package/src/elements/channel/mask.ts +1 -3
  418. package/src/elements/channel/mime.ts +2 -12
  419. package/src/elements/channel/outcomes.test.ts +1 -5
  420. package/src/elements/channel/outcomes.ts +5 -15
  421. package/src/elements/channel/receipts.ts +2 -7
  422. package/src/elements/channel/runtime.ts +28 -74
  423. package/src/elements/channel/suppression.ts +8 -14
  424. package/src/elements/channel.test.ts +8 -31
  425. package/src/elements/channel.ts +3 -14
  426. package/src/elements/clock/actions.ts +6 -18
  427. package/src/elements/clock/declare.ts +1 -3
  428. package/src/elements/clock/dst.ts +1 -5
  429. package/src/elements/clock/durable.ts +4 -16
  430. package/src/elements/clock/health.ts +2 -7
  431. package/src/elements/clock/leader.ts +2 -9
  432. package/src/elements/clock/reconcile.ts +4 -15
  433. package/src/elements/clock/runtime.ts +2 -5
  434. package/src/elements/clock/schedule.ts +4 -20
  435. package/src/elements/clock.test.ts +2 -7
  436. package/src/elements/clock.ts +5 -23
  437. package/src/elements/gate/permissions.ts +1 -4
  438. package/src/elements/gate/runtime.ts +7 -28
  439. package/src/elements/gate.ts +1 -5
  440. package/src/elements/index.ts +9 -7
  441. package/src/elements/signal/declare.ts +2 -7
  442. package/src/elements/signal/reconcile.test.ts +1 -4
  443. package/src/elements/signal/runtime.ts +1 -3
  444. package/src/elements/signal.test.ts +3 -15
  445. package/src/elements/signal.ts +2 -8
  446. package/src/elements/store/cache.test.ts +46 -0
  447. package/src/elements/store/cache.ts +3 -11
  448. package/src/elements/store/classify.ts +5 -22
  449. package/src/elements/store/declare.ts +8 -17
  450. package/src/elements/store/domain-ddl.test.ts +86 -0
  451. package/src/elements/store/emit-drizzle.ts +363 -0
  452. package/src/elements/store/files-policy.test.ts +1 -5
  453. package/src/elements/store/files-policy.ts +4 -12
  454. package/src/elements/store/load-plugin-tables.ts +103 -0
  455. package/src/elements/store/missing-relation.test.ts +83 -0
  456. package/src/elements/store/missing-relation.ts +38 -0
  457. package/src/elements/store/replica.ts +1 -4
  458. package/src/elements/store/runtime.ts +25 -28
  459. package/src/elements/store/schema-decl.test.ts +227 -0
  460. package/src/elements/store/schema-decl.ts +516 -0
  461. package/src/elements/store/sql-condition.ts +11 -21
  462. package/src/elements/store/sql-session.test.ts +50 -0
  463. package/src/elements/store/sql-session.ts +60 -63
  464. package/src/elements/store/table.ts +35 -19
  465. package/src/elements/store.test.ts +17 -39
  466. package/src/elements/store.ts +37 -31
  467. package/src/elements/vault/chain.ts +34 -0
  468. package/src/elements/vault/declare.ts +22 -32
  469. package/src/elements/vault/fingerprint.ts +2 -7
  470. package/src/elements/vault/redact.ts +2 -6
  471. package/src/elements/vault/runtime.ts +18 -32
  472. package/src/elements/vault.test.ts +12 -28
  473. package/src/elements/vault.ts +8 -12
  474. package/src/index.ts +5 -2
  475. package/src/kernel/adopt-routes.ts +15 -29
  476. package/src/kernel/app.ts +34 -94
  477. package/src/kernel/auth-resolve.ts +2 -9
  478. package/src/kernel/boot-bind/ai.ts +1 -4
  479. package/src/kernel/boot-bind/channel.ts +2 -8
  480. package/src/kernel/boot-bind/clock.ts +2 -6
  481. package/src/kernel/boot-bind/gate.ts +2 -8
  482. package/src/kernel/boot-bind/runs.ts +1 -3
  483. package/src/kernel/boot-bind/signal.ts +3 -10
  484. package/src/kernel/boot-bind/store.test.ts +17 -17
  485. package/src/kernel/boot-bind/store.ts +44 -50
  486. package/src/kernel/boot-bind/vault.ts +2 -8
  487. package/src/kernel/boot.test.ts +4 -9
  488. package/src/kernel/boot.ts +29 -76
  489. package/src/kernel/budget.test.ts +1 -3
  490. package/src/kernel/capability.ts +3 -10
  491. package/src/kernel/dry-run.test.ts +1 -4
  492. package/src/kernel/dry-run.ts +3 -11
  493. package/src/kernel/effects.test.ts +1 -5
  494. package/src/kernel/effects.ts +4 -11
  495. package/src/kernel/errors.registry.test.ts +2 -8
  496. package/src/kernel/errors.ts +23 -32
  497. package/src/kernel/flow.test.ts +12 -25
  498. package/src/kernel/flow.ts +5 -25
  499. package/src/kernel/fx.test.ts +15 -44
  500. package/src/kernel/fx.ts +27 -79
  501. package/src/kernel/hook-timing.test.ts +3 -6
  502. package/src/kernel/hook-timing.ts +5 -14
  503. package/src/kernel/hooks.test.ts +10 -10
  504. package/src/kernel/hooks.ts +3 -12
  505. package/src/kernel/index.ts +4 -17
  506. package/src/kernel/journal.ts +7 -31
  507. package/src/kernel/on.ts +1 -6
  508. package/src/kernel/pipeline.test.ts +10 -19
  509. package/src/kernel/pipeline.ts +5 -17
  510. package/src/kernel/plug.ts +4 -11
  511. package/src/kernel/plugin/conflicts.test.ts +13 -25
  512. package/src/kernel/plugin/decorate.test.ts +8 -13
  513. package/src/kernel/plugin/scoping.test.ts +4 -11
  514. package/src/kernel/plugin.ts +48 -24
  515. package/src/kernel/registry.ts +80 -29
  516. package/src/kernel/router.test.ts +15 -13
  517. package/src/kernel/router.ts +9 -29
  518. package/src/kernel/routing-budget.test.ts +1 -3
  519. package/src/kernel/run-telemetry.ts +1 -3
  520. package/src/kernel/triggers.ts +4 -21
  521. package/src/manifest/diff.test.ts +26 -91
  522. package/src/manifest/diff.ts +95 -225
  523. package/src/manifest/index.ts +2 -9
  524. package/src/manifest/types.ts +29 -5
  525. package/src/manifest/undeclared.test.ts +4 -12
  526. package/src/manifest/undeclared.ts +1 -2
  527. package/src/manifest/validate.test.ts +2 -6
  528. package/src/manifest/validate.ts +4 -14
  529. package/src/mcp/authorization.ts +8 -27
  530. package/src/mcp/confirmation.ts +3 -10
  531. package/src/mcp/data.ts +2 -6
  532. package/src/mcp/docs-index.ts +4 -7
  533. package/src/mcp/docs-mcp.test.ts +3 -12
  534. package/src/mcp/docs-server.ts +7 -27
  535. package/src/mcp/docs-tools.ts +2 -6
  536. package/src/mcp/index.ts +1 -6
  537. package/src/mcp/injection.gate.test.ts +4 -18
  538. package/src/mcp/mcp.test.ts +18 -49
  539. package/src/mcp/protocol.ts +3 -10
  540. package/src/mcp/server.ts +11 -52
  541. package/src/mcp/session.ts +4 -14
  542. package/src/mcp/tools.ts +14 -50
  543. package/src/plugins/catalogue.test.ts +3 -7
  544. package/src/plugins/catalogue.ts +1 -3
  545. package/src/plugins/node-import-scan.ts +2 -5
  546. package/src/plugins/supply-chain.ts +5 -17
  547. package/src/release/exports.ts +3 -8
  548. package/src/release/measure.exports.test.ts +1 -3
  549. package/src/release/measure.ts +15 -43
  550. package/src/runs/bench.test.ts +1 -3
  551. package/src/runs/collect.ts +8 -25
  552. package/src/runs/drivers/clickhouse.ts +2 -9
  553. package/src/runs/drivers/files.ts +9 -31
  554. package/src/runs/drivers/memory.ts +5 -22
  555. package/src/runs/drivers/postgres.ts +8 -20
  556. package/src/runs/duckdb.ts +2 -8
  557. package/src/runs/index.ts +3 -14
  558. package/src/runs/outlier.ts +3 -10
  559. package/src/runs/parquet.ts +8 -26
  560. package/src/runs/privacy.ts +2 -8
  561. package/src/runs/runs.test.ts +3 -9
  562. package/src/runs/runtime.ts +7 -32
  563. package/src/runs/shred.ts +10 -29
  564. package/src/runs/types.ts +1 -4
  565. package/src/runtime/bun.ts +4 -15
  566. package/src/runtime/cold-start.bench.ts +1 -3
  567. package/src/runtime/dev-request-log.test.ts +1 -4
  568. package/src/runtime/dev-request-log.ts +1 -4
  569. package/src/runtime/index.ts +1 -6
  570. package/src/runtime/primitives.ts +5 -4
  571. package/src/runtime/security.ts +3 -13
  572. package/src/runtime/serve.test.ts +4 -7
  573. package/src/runtime/types.ts +1 -4
  574. package/src/runtime/web-standard.ts +3 -15
  575. package/src/term.test.ts +4 -6
  576. package/src/term.ts +49 -98
  577. package/src/test/create-test-app.test.ts +4 -14
  578. package/src/test/create-test-app.ts +13 -31
  579. package/src/test/provisions.integration.test.ts +3 -11
  580. package/src/upgrade/codemods.test.ts +25 -6
  581. package/src/upgrade/codemods.ts +116 -3
  582. package/src/validation/standard-schema.test.ts +2 -5
  583. package/src/validation/standard-schema.ts +13 -33
  584. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Domain DDL policy — ensure vs migrate; missing schema → OKE1101.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { Database } from "bun:sqlite";
7
+ import { OkeError, OKE_ERRORS } from "../../kernel/errors.ts";
8
+ import { sqliteDriver } from "../../drivers/sqlite.ts";
9
+ import { defineTable } from "./table.ts";
10
+ import { createSqlStoreHandle } from "./sql-session.ts";
11
+
12
+ const notes = defineTable("notes", { id: true, title: true });
13
+
14
+ describe("domain DDL policy", () => {
15
+ test("ensure mode creates tables on first touch", async () => {
16
+ const db = new Database(":memory:");
17
+ const conn = await sqliteDriver.connect({ client: db, role: "primary" });
18
+ const handle = createSqlStoreHandle("sql:app", {
19
+ connection: conn,
20
+ classifications: new Map(),
21
+ routedRole: "primary",
22
+ domainDdl: "ensure",
23
+ });
24
+ await handle.insert(notes).values({ id: "1", title: "hi" });
25
+ const rows = await handle.select().from(notes);
26
+ expect(rows).toHaveLength(1);
27
+ await conn.close();
28
+ });
29
+
30
+ test("prod/off mode never CREATE TABLE IF NOT EXISTS", async () => {
31
+ const db = new Database(":memory:");
32
+ const execSql: string[] = [];
33
+ const base = await sqliteDriver.connect({ client: db, role: "primary" });
34
+ const conn = {
35
+ ...base,
36
+ driverId: "sqlite" as const,
37
+ role: "primary" as const,
38
+ async query(sql: string, params?: readonly unknown[]) {
39
+ execSql.push(sql);
40
+ return base.query(sql, params);
41
+ },
42
+ async exec(sql: string, params?: readonly unknown[]) {
43
+ execSql.push(sql);
44
+ return base.exec(sql, params);
45
+ },
46
+ close: () => base.close(),
47
+ };
48
+ const handle = createSqlStoreHandle("sql:app", {
49
+ connection: conn,
50
+ classifications: new Map(),
51
+ routedRole: "primary",
52
+ domainDdl: "off",
53
+ });
54
+ try {
55
+ await handle.select().from(notes);
56
+ } catch {
57
+ /* expected — table missing */
58
+ }
59
+ expect(execSql.some((s) => /CREATE TABLE IF NOT EXISTS/i.test(s))).toBe(false);
60
+ await conn.close();
61
+ });
62
+
63
+ test("off mode remaps missing table to DOMAIN_SCHEMA_MISSING", async () => {
64
+ const db = new Database(":memory:");
65
+ const conn = await sqliteDriver.connect({ client: db, role: "primary" });
66
+ const handle = createSqlStoreHandle("sql:app", {
67
+ connection: conn,
68
+ classifications: new Map(),
69
+ routedRole: "primary",
70
+ domainDdl: "off",
71
+ });
72
+ let err: unknown;
73
+ try {
74
+ await handle.select().from(notes);
75
+ } catch (e) {
76
+ err = e;
77
+ }
78
+ expect(err).toBeInstanceOf(OkeError);
79
+ const oke = err as OkeError;
80
+ expect(oke.code).toBe(OKE_ERRORS.DOMAIN_SCHEMA_MISSING.code);
81
+ expect(oke.causeText).toContain("migrations have not been applied");
82
+ expect(oke.fix).toContain("oke db migrate");
83
+ expect(oke.docsUrl).toContain(`/e/${OKE_ERRORS.DOMAIN_SCHEMA_MISSING.code}`);
84
+ await conn.close();
85
+ });
86
+ });
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Emit dialect-specific Drizzle schema TypeScript from abstract declarations.
3
+ *
4
+ * Consumed as a pre-step inside `oke db push|generate|migrate` (and `oke dev`
5
+ * watch) when abstract decls exist — not a third schema CLI.
6
+ */
7
+
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import type { TableContribution } from "../../kernel/plugin.ts";
11
+ import {
12
+ isFieldBuilder,
13
+ isSchemaColumnDecl,
14
+ isSchemaTableDecl,
15
+ relationsFromExports,
16
+ schemaTable,
17
+ tablesFromExports,
18
+ type RelationColumnRef,
19
+ type SchemaColumnDecl,
20
+ type SchemaColumnInput,
21
+ type SchemaRelationEntry,
22
+ type SchemaRelationsDecl,
23
+ type SchemaTableDecl,
24
+ } from "./schema-decl.ts";
25
+
26
+ /** Supported Drizzle dialects for domain schema emit. */
27
+ export type SqlDialect = "sqlite" | "postgres";
28
+
29
+ /** Header written at the top of generated files. */
30
+ export const GENERATED_SCHEMA_HEADER = "// generated by oke — do not edit";
31
+
32
+ /** Default output path (relative to project root). */
33
+ export const DEFAULT_GENERATED_SCHEMA_REL = "src/schema.generated.ts";
34
+
35
+ /** Default declaration module path (relative to project root). */
36
+ export const DEFAULT_DECLARE_SCHEMA_REL = "src/schema.decl.ts";
37
+
38
+ /**
39
+ * Map a store.sql driver id to an emit dialect.
40
+ *
41
+ * @param driverId - Protocol driver id (`sqlite` · `postgres` · `memory` · …)
42
+ */
43
+ export function dialectFromDriverId(driverId: string | undefined): SqlDialect {
44
+ if (driverId === "postgres") return "postgres";
45
+ return "sqlite";
46
+ }
47
+
48
+ /**
49
+ * Serialize one column to a Drizzle builder chain.
50
+ *
51
+ * @param col - Column decl
52
+ * @param dialect - Target dialect (affects import names only at file level)
53
+ */
54
+ export function emitColumnSource(col: SchemaColumnDecl, _dialect: SqlDialect): string {
55
+ const fn = col.sqlType === "integer" ? "integer" : "text";
56
+ let expr = `${fn}(${JSON.stringify(col.sqlName)})`;
57
+ if (col.primaryKey) expr += ".primaryKey()";
58
+ else if (col.notNull) expr += ".notNull()";
59
+ if (col.unique) expr += ".unique()";
60
+ if (col.defaultValue !== undefined) {
61
+ expr += `.default(${literalSource(col.defaultValue)})`;
62
+ }
63
+ if (col.defaultFnKind === "id") expr += ".$defaultFn(id)";
64
+ else if (col.defaultFnKind === "now") expr += ".$defaultFn(now)";
65
+ if (col.references) {
66
+ const target = col.references.ref();
67
+ const targetTable = exportNameForTable(target.tableName ?? "unknown");
68
+ const targetCol = target.key;
69
+ expr += `.references(() => ${targetTable}.${targetCol}`;
70
+ const actions = col.references.actions;
71
+ if (actions && (actions.onDelete || actions.onUpdate)) {
72
+ const parts: string[] = [];
73
+ if (actions.onDelete) parts.push(`onDelete: ${JSON.stringify(actions.onDelete)}`);
74
+ if (actions.onUpdate) parts.push(`onUpdate: ${JSON.stringify(actions.onUpdate)}`);
75
+ expr += `, { ${parts.join(", ")} }`;
76
+ }
77
+ expr += ")";
78
+ }
79
+ return expr;
80
+ }
81
+
82
+ function literalSource(value: string | number | boolean | null): string {
83
+ if (value === null) return "null";
84
+ if (typeof value === "string") return JSON.stringify(value);
85
+ if (typeof value === "boolean") return value ? "true" : "false";
86
+ return String(value);
87
+ }
88
+
89
+ function emitColRef(ref: RelationColumnRef): string {
90
+ return `r.${ref.table}.${ref.column}`;
91
+ }
92
+
93
+ function emitColRefs(
94
+ value: RelationColumnRef | readonly RelationColumnRef[] | undefined,
95
+ ): string | undefined {
96
+ if (value === undefined) return undefined;
97
+ if (Array.isArray(value)) {
98
+ const refs = value as readonly RelationColumnRef[];
99
+ if (refs.length === 1) return emitColRef(refs[0]!);
100
+ return `[${refs.map(emitColRef).join(", ")}]`;
101
+ }
102
+ return emitColRef(value as RelationColumnRef);
103
+ }
104
+
105
+ function emitRelationEntry(entry: SchemaRelationEntry): string {
106
+ const helper = entry.kind === "one" ? "one" : "many";
107
+ const parts: string[] = [];
108
+ const from = emitColRefs(entry.from);
109
+ const to = emitColRefs(entry.to);
110
+ if (from !== undefined) parts.push(`from: ${from}`);
111
+ if (to !== undefined) parts.push(`to: ${to}`);
112
+ if (entry.kind === "one" && entry.optional !== undefined) {
113
+ parts.push(`optional: ${entry.optional ? "true" : "false"}`);
114
+ }
115
+ if (entry.alias !== undefined) parts.push(`alias: ${JSON.stringify(entry.alias)}`);
116
+ const config = parts.length > 0 ? `{ ${parts.join(", ")} }` : "";
117
+ return `r.${helper}.${entry.target}(${config})`;
118
+ }
119
+
120
+ /**
121
+ * Emit a `defineRelations(...)` block from abstract relations decls.
122
+ *
123
+ * @param relations - Relations declarations (typically one)
124
+ */
125
+ export function emitRelationsSource(relations: readonly SchemaRelationsDecl[]): string {
126
+ if (relations.length === 0) return "";
127
+ const blocks: string[] = [];
128
+ for (const rel of relations) {
129
+ const schemaKeys = rel.tableKeys.join(", ");
130
+ const tableBlocks: string[] = [];
131
+ for (const [tableKey, tableRels] of Object.entries(rel.config)) {
132
+ const relLines = Object.entries(tableRels).map(
133
+ ([relName, entry]) => ` ${relName}: ${emitRelationEntry(entry)},`,
134
+ );
135
+ tableBlocks.push(` ${tableKey}: {\n${relLines.join("\n")}\n },`);
136
+ }
137
+ blocks.push(
138
+ `export const relations = defineRelations({ ${schemaKeys} }, (r) => ({\n${tableBlocks.join("\n")}\n}));`,
139
+ );
140
+ }
141
+ return blocks.join("\n\n");
142
+ }
143
+
144
+ /** Options for {@link emitDrizzleSource}. */
145
+ export interface EmitDrizzleSourceOptions {
146
+ readonly relations?: readonly SchemaRelationsDecl[];
147
+ }
148
+
149
+ /**
150
+ * Emit a full Drizzle schema module from abstract table decls.
151
+ *
152
+ * @param tables - App + plugin tables (already merged)
153
+ * @param dialect - sqlite → sqliteTable · postgres → pgTable
154
+ * @param options - Optional relations to emit as `defineRelations`
155
+ */
156
+ export function emitDrizzleSource(
157
+ tables: readonly SchemaTableDecl[],
158
+ dialect: SqlDialect,
159
+ options: EmitDrizzleSourceOptions = {},
160
+ ): string {
161
+ const tableFn = dialect === "postgres" ? "pgTable" : "sqliteTable";
162
+ const coreImport =
163
+ dialect === "postgres"
164
+ ? `import { pgTable, text, integer } from "drizzle-orm/pg-core";`
165
+ : `import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";`;
166
+
167
+ const needsId = tables.some((t) =>
168
+ Object.values(t.columns).some((c) => c.defaultFnKind === "id"),
169
+ );
170
+ const needsNow = tables.some((t) =>
171
+ Object.values(t.columns).some((c) => c.defaultFnKind === "now"),
172
+ );
173
+ const helperNames = [needsId ? "id" : null, needsNow ? "now" : null].filter(
174
+ (x): x is string => x !== null,
175
+ );
176
+ const helperImport =
177
+ helperNames.length > 0 ? `import { ${helperNames.join(", ")} } from "okengine/store";\n` : "";
178
+
179
+ const relations = options.relations ?? [];
180
+ const relationsImport =
181
+ relations.length > 0 ? `import { defineRelations } from "drizzle-orm";\n` : "";
182
+
183
+ const blocks = tables.map((table) => {
184
+ const lines = Object.values(table.columns).map(
185
+ (col) => ` ${col.key}: ${emitColumnSource(col, dialect)},`,
186
+ );
187
+ return `export const ${exportNameForTable(table.name)} = ${tableFn}(${JSON.stringify(table.name)}, {\n${lines.join("\n")}\n});`;
188
+ });
189
+
190
+ const relationsBlock = emitRelationsSource(relations);
191
+
192
+ return [
193
+ GENERATED_SCHEMA_HEADER,
194
+ coreImport,
195
+ relationsImport.trimEnd(),
196
+ helperImport.trimEnd(),
197
+ "",
198
+ blocks.join("\n\n"),
199
+ relationsBlock ? `\n${relationsBlock}` : "",
200
+ "",
201
+ ]
202
+ .filter((line, i, arr) => !(line === "" && arr[i - 1] === ""))
203
+ .join("\n");
204
+ }
205
+
206
+ function exportNameForTable(name: string): string {
207
+ // Keep simple SQL names as-is; camelCase odd names.
208
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) return name;
209
+ return name.replace(/[^a-zA-Z0-9_]+/g, "_");
210
+ }
211
+
212
+ /**
213
+ * Build a {@link SchemaTableDecl} from a plugin column map (builders or decls).
214
+ *
215
+ * @param name - Table name
216
+ * @param columns - Column map
217
+ */
218
+ export function schemaTableFromPluginColumns(
219
+ name: string,
220
+ columns: Readonly<Record<string, SchemaColumnInput>>,
221
+ ): SchemaTableDecl {
222
+ return schemaTable(name, columns);
223
+ }
224
+
225
+ /**
226
+ * Whether a record looks like an abstract column map (not `{ plane }` metadata).
227
+ *
228
+ * @param value - Unknown second arg to plugin.table
229
+ */
230
+ export function isAbstractColumnMap(
231
+ value: unknown,
232
+ ): value is Readonly<Record<string, SchemaColumnInput>> {
233
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
234
+ const entries = Object.entries(value);
235
+ if (entries.length === 0) return true;
236
+ // Legacy `{ plane: "operator" }` — not columns.
237
+ if (entries.length === 1 && entries[0]![0] === "plane" && typeof entries[0]![1] === "string") {
238
+ return false;
239
+ }
240
+ return entries.every(([, v]) => isFieldBuilder(v) || isSchemaColumnDecl(v));
241
+ }
242
+
243
+ /**
244
+ * Extract schema tables from plugin contributions that supply `field.*` columns.
245
+ *
246
+ * Contributions without columns (name + plane only) are skipped — auth/console
247
+ * still use hand-written SQL for those tables.
248
+ *
249
+ * @param contributions - Plugin table list
250
+ */
251
+ export function tablesFromPluginContributions(
252
+ contributions: readonly TableContribution[],
253
+ ): SchemaTableDecl[] {
254
+ const out: SchemaTableDecl[] = [];
255
+ for (const c of contributions) {
256
+ const columns = c.columns;
257
+ if (!columns || Object.keys(columns).length === 0) continue;
258
+ if (!isAbstractColumnMap(columns)) continue;
259
+ out.push(schemaTableFromPluginColumns(c.name, columns));
260
+ }
261
+ return out;
262
+ }
263
+
264
+ /**
265
+ * Merge app + plugin abstract tables. Duplicate names throw.
266
+ *
267
+ * @param appTables - App declarations
268
+ * @param pluginTables - Plugin contributions with columns
269
+ */
270
+ export function mergeSchemaTables(
271
+ appTables: readonly SchemaTableDecl[],
272
+ pluginTables: readonly SchemaTableDecl[] = [],
273
+ ): SchemaTableDecl[] {
274
+ const byName = new Map<string, SchemaTableDecl>();
275
+ for (const t of [...appTables, ...pluginTables]) {
276
+ if (byName.has(t.name)) {
277
+ throw new Error(
278
+ `store schema: duplicate table "${t.name}" (plugins may add whole tables only — no column injection into app-owned tables)`,
279
+ );
280
+ }
281
+ byName.set(t.name, t);
282
+ }
283
+ return [...byName.values()];
284
+ }
285
+
286
+ /** Options for {@link maybeEmitDomainSchema}. */
287
+ export interface EmitDomainSchemaOptions {
288
+ readonly cwd: string;
289
+ readonly dialect: SqlDialect;
290
+ /** Declaration module path (absolute or cwd-relative). */
291
+ readonly declarePath?: string;
292
+ /** Output path (absolute or cwd-relative). */
293
+ readonly outPath?: string;
294
+ /** Live plugin registry table contributions (from a plugged `oke()` app). */
295
+ readonly pluginTables?: readonly TableContribution[];
296
+ readonly write?: (text: string) => void;
297
+ }
298
+
299
+ /**
300
+ * When abstract decls and/or plugin `field.*` tables exist, write the generated
301
+ * Drizzle schema. Returns whether a file was written.
302
+ *
303
+ * @param options - Paths / dialect / plugins
304
+ */
305
+ export async function maybeEmitDomainSchema(
306
+ options: EmitDomainSchemaOptions,
307
+ ): Promise<{ readonly emitted: boolean; readonly path?: string; readonly tableCount?: number }> {
308
+ const cwd = options.cwd;
309
+ const declareRel = options.declarePath ?? DEFAULT_DECLARE_SCHEMA_REL;
310
+ const declareAbs = resolve(cwd, declareRel);
311
+ const declareFile = Bun.file(declareAbs);
312
+
313
+ let appTables: SchemaTableDecl[] = [];
314
+ let appRelations: SchemaRelationsDecl[] = [];
315
+ if (await declareFile.exists()) {
316
+ let mod: Record<string, unknown>;
317
+ try {
318
+ mod = (await import(declareAbs)) as Record<string, unknown>;
319
+ } catch (err) {
320
+ options.write?.(
321
+ `oke db: failed to load schema declare module ${declareRel}: ${err instanceof Error ? err.message : String(err)}\n`,
322
+ );
323
+ throw err;
324
+ }
325
+ appTables = tablesFromExports(mod);
326
+ appRelations = relationsFromExports(mod);
327
+ }
328
+
329
+ const fromPlugins = tablesFromPluginContributions(options.pluginTables ?? []);
330
+ if (appTables.length === 0 && fromPlugins.length === 0) {
331
+ return { emitted: false };
332
+ }
333
+
334
+ const tables = mergeSchemaTables(appTables, fromPlugins);
335
+ const outRel = options.outPath ?? DEFAULT_GENERATED_SCHEMA_REL;
336
+ const outAbs = resolve(cwd, outRel);
337
+ const source = emitDrizzleSource(tables, options.dialect, { relations: appRelations });
338
+ await mkdir(dirname(outAbs), { recursive: true });
339
+ await writeFile(outAbs, source, "utf8");
340
+ options.write?.(
341
+ `oke db: emitted ${outRel} (${tables.length} table${tables.length === 1 ? "" : "s"}, ${options.dialect})\n`,
342
+ );
343
+ return { emitted: true, path: outAbs, tableCount: tables.length };
344
+ }
345
+
346
+ /**
347
+ * Resolve declare/out paths from an optional db config slice.
348
+ *
349
+ * @param cwd - Project root
350
+ * @param db - Config `db` block
351
+ */
352
+ export function resolveEmitPaths(
353
+ cwd: string,
354
+ db?: { readonly declare?: string; readonly generated?: string },
355
+ ): { readonly declarePath: string; readonly outPath: string } {
356
+ return {
357
+ declarePath: join(cwd, db?.declare ?? DEFAULT_DECLARE_SCHEMA_REL),
358
+ outPath: join(cwd, db?.generated ?? DEFAULT_GENERATED_SCHEMA_REL),
359
+ };
360
+ }
361
+
362
+ /** Re-export for callers that only import the emitter. */
363
+ export { isSchemaTableDecl, tablesFromExports, relationsFromExports };
@@ -1,9 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import {
3
- contentAddressedKey,
4
- fileKeyWarnings,
5
- projectFileKeys,
6
- } from "./files-policy.ts";
2
+ import { contentAddressedKey, fileKeyWarnings, projectFileKeys } from "./files-policy.ts";
7
3
 
8
4
  describe("files-policy", () => {
9
5
  test("flags non-ASCII keys for signed-URL risk", () => {
@@ -28,8 +28,7 @@ export function fileKeyWarnings(key: string): readonly FileKeyWarning[] {
28
28
  if (/[^\x00-\x7F]/.test(key)) {
29
29
  warnings.push({
30
30
  code: "non_ascii_key",
31
- message:
32
- "Non-ASCII object key — signed URL encoding may break on S3-compatible stores.",
31
+ message: "Non-ASCII object key — signed URL encoding may break on S3-compatible stores.",
33
32
  key,
34
33
  });
35
34
  }
@@ -41,17 +40,10 @@ export function fileKeyWarnings(key: string): readonly FileKeyWarning[] {
41
40
  *
42
41
  * @param data - Object bytes or UTF-8 string
43
42
  */
44
- export async function contentAddressedKey(
45
- data: Uint8Array | string,
46
- ): Promise<string> {
47
- const bytes =
48
- typeof data === "string"
49
- ? new TextEncoder().encode(data)
50
- : new Uint8Array(data);
43
+ export async function contentAddressedKey(data: Uint8Array | string): Promise<string> {
44
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
51
45
  const digest = await crypto.subtle.digest("SHA-256", bytes);
52
- return [...new Uint8Array(digest)]
53
- .map((b) => b.toString(16).padStart(2, "0"))
54
- .join("");
46
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
55
47
  }
56
48
 
57
49
  /**
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Load plugin table contributions from a live `oke()` app entry.
3
+ *
4
+ * Used by `oke db` emit so `.plug()`-registered `field.*` tables merge into
5
+ * `schema.generated.ts` — not only at unit-test merge call sites.
6
+ */
7
+
8
+ import { resolve } from "node:path";
9
+ import type { TableContribution } from "../../kernel/plugin.ts";
10
+ import type { PluginRegistry } from "../../kernel/registry.ts";
11
+
12
+ /** Duck-type for an app that exposes a plugin registry. */
13
+ export interface AppWithPlugins {
14
+ readonly plugins: PluginRegistry;
15
+ }
16
+
17
+ /**
18
+ * Whether `value` looks like an {@link AppWithPlugins}.
19
+ *
20
+ * @param value - Candidate export
21
+ */
22
+ export function isAppWithPlugins(value: unknown): value is AppWithPlugins {
23
+ if (!value || typeof value !== "object") return false;
24
+ const plugins = (value as { plugins?: unknown }).plugins;
25
+ if (!plugins || typeof plugins !== "object") return false;
26
+ return typeof (plugins as PluginRegistry).tableContributions === "function";
27
+ }
28
+
29
+ /**
30
+ * Find an `oke()` app export on a module (`app`, `default`, or first match).
31
+ *
32
+ * @param mod - Imported module namespace
33
+ */
34
+ export function findAppWithPlugins(mod: Record<string, unknown>): AppWithPlugins | undefined {
35
+ for (const key of ["app", "default", ...Object.keys(mod)]) {
36
+ const value = mod[key];
37
+ if (isAppWithPlugins(value)) return value;
38
+ }
39
+ return undefined;
40
+ }
41
+
42
+ /**
43
+ * Resolve the app entry used to collect plugged plugin tables.
44
+ *
45
+ * @param cwd - Project root
46
+ * @param explicit - Optional relative/absolute entry override
47
+ */
48
+ export async function resolveAppEntryForPluginTables(
49
+ cwd: string,
50
+ explicit?: string,
51
+ ): Promise<string | undefined> {
52
+ if (explicit) return resolve(cwd, explicit);
53
+ const pkgPath = resolve(cwd, "package.json");
54
+ if (await Bun.file(pkgPath).exists()) {
55
+ const pkg = (await Bun.file(pkgPath).json()) as {
56
+ main?: string;
57
+ okengine?: { entry?: string };
58
+ };
59
+ if (pkg.okengine?.entry) return resolve(cwd, pkg.okengine.entry);
60
+ if (pkg.main) return resolve(cwd, pkg.main);
61
+ }
62
+ for (const candidate of ["src/app.ts", "src/index.ts", "index.ts", "app.ts"]) {
63
+ const path = resolve(cwd, candidate);
64
+ if (await Bun.file(path).exists()) return path;
65
+ }
66
+ return undefined;
67
+ }
68
+
69
+ /**
70
+ * Import the app entry and return table contributions from its live registry.
71
+ *
72
+ * Does not boot the app — `.plug()` at module load is enough for capture.
73
+ *
74
+ * @param cwd - Project root
75
+ * @param options - Entry override / write for diagnostics
76
+ */
77
+ export async function loadPluginTablesFromAppEntry(
78
+ cwd: string,
79
+ options: {
80
+ readonly entry?: string;
81
+ readonly write?: (text: string) => void;
82
+ } = {},
83
+ ): Promise<readonly TableContribution[]> {
84
+ const entryAbs = await resolveAppEntryForPluginTables(cwd, options.entry);
85
+ if (!entryAbs) return [];
86
+ if (!(await Bun.file(entryAbs).exists())) return [];
87
+
88
+ let mod: Record<string, unknown>;
89
+ try {
90
+ mod = (await import(entryAbs)) as Record<string, unknown>;
91
+ } catch (err) {
92
+ options.write?.(
93
+ `oke db: failed to load app entry for plugin tables (${entryAbs}): ${
94
+ err instanceof Error ? err.message : String(err)
95
+ }\n`,
96
+ );
97
+ throw err;
98
+ }
99
+
100
+ const app = findAppWithPlugins(mod);
101
+ if (!app) return [];
102
+ return app.plugins.tableContributions();
103
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Per-driver missing table/column signature matcher.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { isMissingDomainRelationError } from "./missing-relation.ts";
7
+
8
+ describe("isMissingDomainRelationError", () => {
9
+ test("postgres SQLSTATE 42P01 / 42703", () => {
10
+ expect(
11
+ isMissingDomainRelationError({
12
+ name: "PostgresError",
13
+ code: "42P01",
14
+ message: 'relation "notes" does not exist',
15
+ }),
16
+ ).toBe(true);
17
+ expect(
18
+ isMissingDomainRelationError({
19
+ name: "PostgresError",
20
+ code: "42703",
21
+ message: 'column "body" does not exist',
22
+ }),
23
+ ).toBe(true);
24
+ });
25
+
26
+ test("does not match Bun connection ERR_POSTGRES_* codes", () => {
27
+ expect(
28
+ isMissingDomainRelationError({
29
+ name: "PostgresError",
30
+ code: "ERR_POSTGRES_CONNECTION_CLOSED",
31
+ message: "Connection closed",
32
+ }),
33
+ ).toBe(false);
34
+ });
35
+
36
+ test("pglite SQLSTATE shapes", () => {
37
+ expect(
38
+ isMissingDomainRelationError({
39
+ name: "error",
40
+ code: "42P01",
41
+ message: 'relation "definitely_missing" does not exist',
42
+ }),
43
+ ).toBe(true);
44
+ expect(
45
+ isMissingDomainRelationError({
46
+ name: "error",
47
+ code: "42703",
48
+ message: 'column "nope" does not exist',
49
+ }),
50
+ ).toBe(true);
51
+ });
52
+
53
+ test("sqlite SQLiteError message prefixes", () => {
54
+ expect(
55
+ isMissingDomainRelationError({
56
+ name: "SQLiteError",
57
+ message: "no such table: definitely_missing",
58
+ errno: 1,
59
+ }),
60
+ ).toBe(true);
61
+ expect(
62
+ isMissingDomainRelationError({
63
+ name: "SQLiteError",
64
+ message: "no such column: nope",
65
+ errno: 1,
66
+ }),
67
+ ).toBe(true);
68
+ });
69
+
70
+ test("sqlite errno alone is not enough", () => {
71
+ expect(
72
+ isMissingDomainRelationError({
73
+ name: "SQLiteError",
74
+ message: "constraint failed",
75
+ errno: 1,
76
+ }),
77
+ ).toBe(false);
78
+ });
79
+
80
+ test("memory driver no such table", () => {
81
+ expect(isMissingDomainRelationError(new Error("no such table: notes"))).toBe(true);
82
+ });
83
+ });