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
@@ -54,11 +54,7 @@ describe("plugin scoping — attachment point is the scope", () => {
54
54
  await app.fetch(new Request("http://localhost/orders"));
55
55
  await app.fetch(new Request("http://localhost/payments"));
56
56
 
57
- expect(fired).toEqual([
58
- "rate-limit",
59
- "orders:handler",
60
- "payments:handler",
61
- ]);
57
+ expect(fired).toEqual(["rate-limit", "orders:handler", "payments:handler"]);
62
58
  });
63
59
 
64
60
  test("app-scoped plugin hook fires for every unit", async () => {
@@ -87,12 +83,9 @@ describe("plugin scoping — attachment point is the scope", () => {
87
83
  }),
88
84
  );
89
85
 
90
- const audit = plugin("audit", { version: "1.0.0" }).hook(
91
- "afterHandle",
92
- () => {
93
- fired.push("audit");
94
- },
95
- );
86
+ const audit = plugin("audit", { version: "1.0.0" }).hook("afterHandle", () => {
87
+ fired.push("audit");
88
+ });
96
89
 
97
90
  const app = oke({ name: "app-scope" }).plug(audit);
98
91
 
@@ -6,9 +6,9 @@
6
6
  * and records exactly what was requested (capability capture). We never
7
7
  * statically analyse plugin source to guess intent.
8
8
  *
9
- * Contributions: flows · hooks · context decorations · elements · drivers ·
10
- * image recipes · DB schema · typed errors · client extensions · CLI
11
- * commands · Console panels.
9
+ * Contributions: flows · hooks · edge handlers (unmatched requests) ·
10
+ * context decorations · elements · drivers · image recipes · DB schema ·
11
+ * typed errors · client extensions · CLI commands · Console panels.
12
12
  *
13
13
  * @see docs/spec/unified-theory.md §14
14
14
  * @see docs/spec/console.md §9.15
@@ -87,14 +87,45 @@ export interface ImageRecipeContribution {
87
87
  readonly recipe: string;
88
88
  }
89
89
 
90
+ /**
91
+ * Edge handler — runs for HTTP requests that match **no** flow (the prime
92
+ * example: a CORS preflight `OPTIONS` for a path bound to another method).
93
+ * Handlers run in install order; the first to return a {@link Response}
94
+ * answers, `undefined` passes to the next handler, then the plain 404.
95
+ */
96
+ export type EdgeHandler = (
97
+ request: Request,
98
+ info: { readonly method: string; readonly path: string },
99
+ ) => undefined | Response | Promise<undefined | Response>;
100
+
90
101
  /** Table / DB schema contribution. */
91
102
  export interface TableContribution {
92
103
  /** Table name — conflict namespace shared across plugins. */
93
104
  readonly name: string;
94
- /** Opaque schema descriptor. */
105
+ /**
106
+ * Abstract column map from `field.*` builders (merged into domain schema emit).
107
+ * Omit when the plugin only claims a name (DDL stays hand-written).
108
+ */
109
+ readonly columns?: Readonly<Record<string, unknown>>;
110
+ /**
111
+ * Non-column metadata (e.g. data plane). Prefer this over stuffing `plane`
112
+ * into the column map.
113
+ */
114
+ readonly options?: {
115
+ readonly plane?: "operator" | "user" | "shared" | string;
116
+ };
117
+ /**
118
+ * Opaque schema descriptor (legacy). Prefer {@link columns} + {@link options}.
119
+ * Still set for plane-only contributions so older readers keep working.
120
+ */
95
121
  readonly schema?: unknown;
96
122
  }
97
123
 
124
+ /** Options for {@link PluginApi.table} / {@link PluginDef.table}. */
125
+ export interface PluginTableOptions {
126
+ readonly plane?: "operator" | "user" | "shared" | string;
127
+ }
128
+
98
129
  /** Driver contribution. */
99
130
  export interface DriverContribution {
100
131
  /** Driver id (protocol-named) — conflict namespace shared across plugins. */
@@ -115,6 +146,12 @@ export interface PluginApi {
115
146
  * @param fn - Hook function
116
147
  */
117
148
  hook(stage: HookStage, fn: HookFn): PluginApi;
149
+ /**
150
+ * Register an edge handler (intercept — requests that match no flow).
151
+ *
152
+ * @param fn - Edge handler
153
+ */
154
+ edge(fn: EdgeHandler): PluginApi;
118
155
  /**
119
156
  * Decorate the invocation context (declare — once at boot).
120
157
  *
@@ -152,10 +189,7 @@ export interface PluginApi {
152
189
  * @param name - Command name
153
190
  * @param handler - Command handler
154
191
  */
155
- cli(
156
- name: string,
157
- handler: CliContribution["handler"],
158
- ): PluginApi;
192
+ cli(name: string, handler: CliContribution["handler"]): PluginApi;
159
193
  /**
160
194
  * Contribute a driver.
161
195
  *
@@ -173,10 +207,19 @@ export interface PluginApi {
173
207
  /**
174
208
  * Contribute a DB table / schema fragment.
175
209
  *
210
+ * Plugins may declare **whole tables** (with optional `field.*` columns)
211
+ * merged into the generated domain schema. Extending an existing app-owned
212
+ * table with plugin columns is not supported — contribute a separate table.
213
+ *
176
214
  * @param name - Table name
177
- * @param schema - Opaque schema
215
+ * @param columns - Abstract column map, or legacy `{ plane }` options
216
+ * @param options - Plane / metadata (preferred over embedding `plane` in columns)
178
217
  */
179
- table(name: string, schema?: unknown): PluginApi;
218
+ table(
219
+ name: string,
220
+ columns?: Readonly<Record<string, unknown>>,
221
+ options?: PluginTableOptions,
222
+ ): PluginApi;
180
223
  /**
181
224
  * Contribute a flow.
182
225
  *
@@ -216,6 +259,7 @@ export interface PluginCapabilities {
216
259
  export interface PluginRegistration {
217
260
  readonly capabilities: PluginCapabilities;
218
261
  readonly hooks: Partial<Record<HookStage, HookFn[]>>;
262
+ readonly edges: readonly EdgeHandler[];
219
263
  readonly decorations: Readonly<Record<string, unknown>>;
220
264
  readonly elements: readonly PluginElement[];
221
265
  readonly tables: readonly TableContribution[];
@@ -259,16 +303,19 @@ export interface PluginDef<D extends Record<string, unknown> = {}> {
259
303
  * @param fn - Hook function
260
304
  */
261
305
  hook(stage: HookStage, fn: HookFn): PluginDef<D>;
306
+ /**
307
+ * Queue an edge handler contribution (requests that match no flow).
308
+ *
309
+ * @param fn - Edge handler
310
+ */
311
+ edge(fn: EdgeHandler): PluginDef<D>;
262
312
  /**
263
313
  * Queue a context decoration. Accumulates types for `.plug()`.
264
314
  *
265
315
  * @param key - Decoration key
266
316
  * @param value - Value
267
317
  */
268
- decorate<K extends string, V>(
269
- key: K,
270
- value: V,
271
- ): PluginDef<D & { [P in K]: V }>;
318
+ decorate<K extends string, V>(key: K, value: V): PluginDef<D & { [P in K]: V }>;
272
319
  /**
273
320
  * Queue an element contribution.
274
321
  *
@@ -317,10 +364,17 @@ export interface PluginDef<D extends Record<string, unknown> = {}> {
317
364
  /**
318
365
  * Queue a table / schema contribution.
319
366
  *
367
+ * Whole tables only — no column injection into app-owned tables (v1).
368
+ *
320
369
  * @param name - Table name
321
- * @param schema - Schema
370
+ * @param columns - Abstract column map, or legacy `{ plane }` options
371
+ * @param options - Plane / metadata
322
372
  */
323
- table(name: string, schema?: unknown): PluginDef<D>;
373
+ table(
374
+ name: string,
375
+ columns?: Readonly<Record<string, unknown>>,
376
+ options?: PluginTableOptions,
377
+ ): PluginDef<D>;
324
378
  /**
325
379
  * Queue a flow contribution.
326
380
  *
@@ -343,8 +397,7 @@ export interface PluginDef<D extends Record<string, unknown> = {}> {
343
397
  }
344
398
 
345
399
  /** Extract accumulated decoration types from a {@link PluginDef}. */
346
- export type DecorationsOf<P> =
347
- P extends PluginDef<infer D> ? D : Record<string, never>;
400
+ export type DecorationsOf<P> = P extends PluginDef<infer D> ? D : Record<string, never>;
348
401
 
349
402
  /**
350
403
  * Define a plugin. Fluent methods queue a registration plan; the registry
@@ -353,10 +406,7 @@ export type DecorationsOf<P> =
353
406
  * @param name - Stable plugin id
354
407
  * @param options - Version and optional config snapshot
355
408
  */
356
- export function plugin(
357
- name: string,
358
- options: PluginOptions,
359
- ): PluginDef {
409
+ export function plugin(name: string, options: PluginOptions): PluginDef {
360
410
  const steps: Array<(api: PluginApi) => void> = [];
361
411
 
362
412
  const def: PluginDef = {
@@ -372,6 +422,12 @@ export function plugin(
372
422
  });
373
423
  return def;
374
424
  },
425
+ edge(fn) {
426
+ steps.push((api) => {
427
+ api.edge(fn);
428
+ });
429
+ return def;
430
+ },
375
431
  decorate(key, value) {
376
432
  steps.push((api) => {
377
433
  api.decorate(key, value);
@@ -390,9 +446,7 @@ export function plugin(
390
446
  api.element(el);
391
447
  } else if (typeof el.facet === "string" && typeof el.name === "string") {
392
448
  api.element({
393
- kind: el.facet.startsWith("store.")
394
- ? el.facet
395
- : `store.${el.facet}`,
449
+ kind: el.facet.startsWith("store.") ? el.facet : `store.${el.facet}`,
396
450
  name: el.name,
397
451
  });
398
452
  } else {
@@ -437,9 +491,9 @@ export function plugin(
437
491
  });
438
492
  return def;
439
493
  },
440
- table(tableName, schema) {
494
+ table(tableName, columns, options) {
441
495
  steps.push((api) => {
442
- api.table(tableName, schema);
496
+ api.table(tableName, columns, options);
443
497
  });
444
498
  return def;
445
499
  },
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Registry isolation — two `oke()` apps in one process must not share routes.
3
+ *
4
+ * This is the multi-app / future-`mount()` guarantee: constructing one app
5
+ * can never pull another app's flows through the process-global `on()`
6
+ * registry. Before the fix, `oke()` adopted the registry snapshot and left
7
+ * it intact, so App B below silently served App A's `/a` route (leakage).
8
+ * The default `registry: "consume"` mode drains the registry at construction.
9
+ */
10
+
11
+ import { afterEach, describe, expect, test } from "bun:test";
12
+ import { oke } from "./app.ts";
13
+ import { flow, resetFlowSeq } from "./flow.ts";
14
+ import { listBindings, on, resetBindings, type Binding } from "./on.ts";
15
+ import { http } from "./triggers.ts";
16
+
17
+ afterEach(() => {
18
+ resetBindings();
19
+ resetFlowSeq();
20
+ });
21
+
22
+ describe("registry isolation", () => {
23
+ test("consume (default): App B does not serve App A routes", async () => {
24
+ on(http.get("/a"), flow({ name: "app-a.ping", do: () => "a" }));
25
+ const appA = oke({ name: "app-a" });
26
+
27
+ // No resetBindings() between the apps — the pre-fix leak scenario.
28
+ on(http.get("/b"), flow({ name: "app-b.ping", do: () => "b" }));
29
+ const appB = oke({ name: "app-b" });
30
+
31
+ // Pre-fix this was 200: App B adopted App A's leftover binding.
32
+ expect((await appB.fetch(new Request("http://localhost/a"))).status).toBe(404);
33
+
34
+ const own = await appB.fetch(new Request("http://localhost/b"));
35
+ expect(own.status).toBe(200);
36
+ expect(await own.json()).toEqual({ data: "b", error: null });
37
+
38
+ // App A still serves its own route after App B was constructed.
39
+ const stillA = await appA.fetch(new Request("http://localhost/a"));
40
+ expect(stillA.status).toBe(200);
41
+ expect(await stillA.json()).toEqual({ data: "a", error: null });
42
+
43
+ // Consume drained the registry — nothing left for a third app.
44
+ expect(listBindings()).toHaveLength(0);
45
+ });
46
+
47
+ test("keep: today's behavior — registry survives construction", async () => {
48
+ on(http.get("/a"), flow({ name: "kept.ping", do: () => "a" }));
49
+ oke({ name: "app-a", registry: "keep" });
50
+ expect(listBindings()).toHaveLength(1);
51
+
52
+ const appB = oke({ name: "app-b", registry: "keep" });
53
+ expect((await appB.fetch(new Request("http://localhost/a"))).status).toBe(200);
54
+ });
55
+
56
+ test("ignore: only options.bindings are served", async () => {
57
+ on(http.get("/stray"), flow({ name: "stray.ping", do: () => "stray" }));
58
+
59
+ const onlyB: Binding = {
60
+ trigger: http.get("/b"),
61
+ flow: flow({ name: "only-b.ping", do: () => "b" }),
62
+ };
63
+ const app = oke({ name: "app-b", registry: "ignore", bindings: [onlyB] });
64
+
65
+ expect((await app.fetch(new Request("http://localhost/stray"))).status).toBe(404);
66
+
67
+ const res = await app.fetch(new Request("http://localhost/b"));
68
+ expect(res.status).toBe(200);
69
+ expect(await res.json()).toEqual({ data: "b", error: null });
70
+
71
+ // Ignore never touches the registry.
72
+ expect(listBindings()).toHaveLength(1);
73
+ });
74
+ });
@@ -20,12 +20,14 @@ import type {
20
20
  ClientExtensionContribution,
21
21
  ConsolePanelContribution,
22
22
  DriverContribution,
23
+ EdgeHandler,
23
24
  ImageRecipeContribution,
24
25
  PluginApi,
25
26
  PluginCapabilities,
26
27
  PluginDef,
27
28
  PluginElement,
28
29
  PluginRegistration,
30
+ PluginTableOptions,
29
31
  TableContribution,
30
32
  } from "./plugin.ts";
31
33
  import type { SchemaInput } from "../validation/standard-schema.ts";
@@ -56,11 +58,12 @@ interface NameClaim {
56
58
  *
57
59
  * @param identity - Plugin name + version for the capability record
58
60
  */
59
- export function createRecordingApi(identity: {
60
- readonly name: string;
61
- readonly version: string;
62
- }): { readonly api: PluginApi; snapshot(): PluginRegistration } {
61
+ export function createRecordingApi(identity: { readonly name: string; readonly version: string }): {
62
+ readonly api: PluginApi;
63
+ snapshot(): PluginRegistration;
64
+ } {
63
65
  const hooks: Partial<Record<HookStage, HookFn[]>> = {};
66
+ const edges: EdgeHandler[] = [];
64
67
  const decorations: Record<string, unknown> = {};
65
68
  const elements: PluginElement[] = [];
66
69
  const tables: TableContribution[] = [];
@@ -80,7 +83,7 @@ export function createRecordingApi(identity: {
80
83
  if (!declares.includes(cap)) declares.push(cap);
81
84
  }
82
85
 
83
- function pushIntercept(stage: HookStage): void {
86
+ function pushIntercept(stage: HookStage | "edge"): void {
84
87
  if (!intercepts.includes(stage)) intercepts.push(stage);
85
88
  }
86
89
 
@@ -92,6 +95,11 @@ export function createRecordingApi(identity: {
92
95
  pushIntercept(stage);
93
96
  return api;
94
97
  },
98
+ edge(fn) {
99
+ edges.push(fn);
100
+ pushIntercept("edge");
101
+ return api;
102
+ },
95
103
  decorate(key, value) {
96
104
  decorations[key] = value;
97
105
  pushDeclare(`decorate:${key}`);
@@ -133,8 +141,8 @@ export function createRecordingApi(identity: {
133
141
  pushDeclare(`image:${role}`);
134
142
  return api;
135
143
  },
136
- table(name, schema) {
137
- tables.push({ name, schema });
144
+ table(name, columns, options) {
145
+ tables.push(normalizeTableContribution(name, columns, options));
138
146
  pushDeclare(`table:${name}`);
139
147
  return api;
140
148
  },
@@ -167,6 +175,7 @@ export function createRecordingApi(identity: {
167
175
  needs: needs.slice(),
168
176
  },
169
177
  hooks: { ...hooks },
178
+ edges: edges.slice(),
170
179
  decorations: { ...decorations },
171
180
  elements: elements.slice(),
172
181
  tables: tables.slice(),
@@ -229,21 +238,25 @@ export interface PluginRegistry {
229
238
  * @param unit - Flow's unit name (for `unit` kind)
230
239
  * @param flowName - Flow name (for `flow` kind)
231
240
  */
232
- hooksAt(
233
- scopeKind: PluginScope["kind"],
234
- unit: string | undefined,
235
- flowName: string,
236
- ): HookMap;
241
+ hooksAt(scopeKind: PluginScope["kind"], unit: string | undefined, flowName: string): HookMap;
242
+ /**
243
+ * Edge handlers in install order. They answer requests that match no
244
+ * flow, so there is no flow/unit scope to filter by — every installed
245
+ * handler runs.
246
+ */
247
+ edgeHandlers(): readonly EdgeHandler[];
237
248
  /**
238
249
  * Merged decorations visible to a flow at the given unit.
239
250
  *
240
251
  * @param unit - Flow's unit name, if any
241
252
  * @param flowName - Flow name
242
253
  */
243
- decorationsFor(
244
- unit: string | undefined,
245
- flowName: string,
246
- ): Record<string, unknown>;
254
+ decorationsFor(unit: string | undefined, flowName: string): Record<string, unknown>;
255
+ /**
256
+ * Unique table contributions from installed plugins (first install per
257
+ * plugin name). Used by `oke db` emit to merge plugin `field.*` tables.
258
+ */
259
+ tableContributions(): readonly TableContribution[];
247
260
  }
248
261
 
249
262
  /**
@@ -276,10 +289,7 @@ export function createPluginRegistry(): PluginRegistry {
276
289
  }
277
290
  }
278
291
 
279
- function assertNoConflicts(
280
- pluginName: string,
281
- registration: PluginRegistration,
282
- ): void {
292
+ function assertNoConflicts(pluginName: string, registration: PluginRegistration): void {
283
293
  for (const t of registration.tables) {
284
294
  claim(tableClaims, "table", t.name, pluginName);
285
295
  }
@@ -301,12 +311,7 @@ export function createPluginRegistry(): PluginRegistry {
301
311
  const scopeKey = nameScopeKey(pluginDef.name, scope);
302
312
  const priorHere = byNameScope.get(scopeKey);
303
313
  if (priorHere) {
304
- if (
305
- samePluginConfig(
306
- priorHere.plugin.configSnapshot,
307
- pluginDef.configSnapshot,
308
- )
309
- ) {
314
+ if (samePluginConfig(priorHere.plugin.configSnapshot, pluginDef.configSnapshot)) {
310
315
  return undefined; // identity dedup — no-op
311
316
  }
312
317
  throw new Error(
@@ -318,10 +323,7 @@ export function createPluginRegistry(): PluginRegistry {
318
323
  const priorAnywhere = byName.get(pluginDef.name);
319
324
  if (
320
325
  priorAnywhere &&
321
- !samePluginConfig(
322
- priorAnywhere.plugin.configSnapshot,
323
- pluginDef.configSnapshot,
324
- )
326
+ !samePluginConfig(priorAnywhere.plugin.configSnapshot, pluginDef.configSnapshot)
325
327
  ) {
326
328
  throw new Error(
327
329
  `Plugin "${pluginDef.name}" registered twice with conflicting config ` +
@@ -392,7 +394,77 @@ export function createPluginRegistry(): PluginRegistry {
392
394
  }
393
395
  return out;
394
396
  },
397
+ edgeHandlers() {
398
+ const out: EdgeHandler[] = [];
399
+ for (const entry of installed) {
400
+ out.push(...entry.registration.edges);
401
+ }
402
+ return out;
403
+ },
404
+ tableContributions() {
405
+ const seenPlugins = new Set<string>();
406
+ const out: TableContribution[] = [];
407
+ for (const entry of installed) {
408
+ if (seenPlugins.has(entry.plugin.name)) continue;
409
+ seenPlugins.add(entry.plugin.name);
410
+ out.push(...entry.registration.tables);
411
+ }
412
+ return out;
413
+ },
395
414
  };
396
415
 
397
416
  return registry;
398
417
  }
418
+
419
+ /**
420
+ * Normalize plugin `.table()` args — supports columns + options, and legacy
421
+ * `{ plane }` as the second argument.
422
+ *
423
+ * @param name - Table name
424
+ * @param columnsOrOptions - Column map or legacy options
425
+ * @param options - Explicit options (preferred)
426
+ */
427
+ function normalizeTableContribution(
428
+ name: string,
429
+ columnsOrOptions?: Readonly<Record<string, unknown>>,
430
+ options?: PluginTableOptions,
431
+ ): TableContribution {
432
+ if (options !== undefined) {
433
+ const columns =
434
+ columnsOrOptions && Object.keys(columnsOrOptions).length > 0 ? columnsOrOptions : undefined;
435
+ return {
436
+ name,
437
+ ...(columns ? { columns } : {}),
438
+ options,
439
+ schema: options,
440
+ };
441
+ }
442
+
443
+ if (columnsOrOptions && isPlaneOnlyOptions(columnsOrOptions)) {
444
+ const planeOptions: PluginTableOptions = {
445
+ plane: columnsOrOptions.plane as string,
446
+ };
447
+ return {
448
+ name,
449
+ options: planeOptions,
450
+ schema: columnsOrOptions,
451
+ };
452
+ }
453
+
454
+ if (columnsOrOptions && Object.keys(columnsOrOptions).length > 0) {
455
+ return {
456
+ name,
457
+ columns: columnsOrOptions,
458
+ schema: columnsOrOptions,
459
+ };
460
+ }
461
+
462
+ return { name };
463
+ }
464
+
465
+ function isPlaneOnlyOptions(
466
+ value: Readonly<Record<string, unknown>>,
467
+ ): value is { readonly plane: string } {
468
+ const keys = Object.keys(value);
469
+ return keys.length === 1 && keys[0] === "plane" && typeof value.plane === "string";
470
+ }
@@ -93,22 +93,24 @@ describe("router — RegExp + Trie + Linear + Smart", () => {
93
93
  regexp.match("GET", target);
94
94
  }
95
95
 
96
- const iterations = 5_000;
96
+ const iterations = 20_000;
97
+ let best = 0;
98
+ for (let trial = 0; trial < 3; trial++) {
99
+ const t0 = performance.now();
100
+ for (let i = 0; i < iterations; i++) {
101
+ linear.match("GET", target);
102
+ }
103
+ const linearMs = performance.now() - t0;
97
104
 
98
- const t0 = performance.now();
99
- for (let i = 0; i < iterations; i++) {
100
- linear.match("GET", target);
101
- }
102
- const linearMs = performance.now() - t0;
103
-
104
- const t1 = performance.now();
105
- for (let i = 0; i < iterations; i++) {
106
- regexp.match("GET", target);
105
+ const t1 = performance.now();
106
+ for (let i = 0; i < iterations; i++) {
107
+ regexp.match("GET", target);
108
+ }
109
+ const regexpMs = performance.now() - t1;
110
+ best = Math.max(best, linearMs / regexpMs);
107
111
  }
108
- const regexpMs = performance.now() - t1;
109
112
 
110
- const speedup = linearMs / regexpMs;
111
113
  expect(regexp.match("GET", target)?.value).toBe(N - 1);
112
- expect(speedup).toBeGreaterThanOrEqual(10);
114
+ expect(best).toBeGreaterThanOrEqual(10);
113
115
  });
114
116
  });
@@ -50,9 +50,7 @@ export interface Router<T> {
50
50
  /** True when the pattern cannot be expressed by the RegExp strategy. */
51
51
  export function isUnsupportedByRegExp(path: string): boolean {
52
52
  // optional params, wildcards, custom regex segments, greedy rests
53
- return /\/:/.test(path) === false
54
- ? /[*?{}()]/.test(path)
55
- : /[*?{}]|\/:[^/]+\?/.test(path);
53
+ return /\/:/.test(path) === false ? /[*?{}()]/.test(path) : /[*?{}]|\/:[^/]+\?/.test(path);
56
54
  }
57
55
 
58
56
  function escapeRegex(segment: string): string {
@@ -85,17 +83,13 @@ function emptyTable<T>(): MethodTable<T> {
85
83
 
86
84
  /** First segment key used for dynamic bucketing (`""` if param/empty). */
87
85
  function bucketKey(path: string): string {
88
- const parts = path.startsWith("/")
89
- ? path.split("/").slice(1)
90
- : path.split("/");
86
+ const parts = path.startsWith("/") ? path.split("/").slice(1) : path.split("/");
91
87
  const first = parts[0] ?? "";
92
88
  if (first.startsWith(":") || first === "*" || first === "") return "";
93
89
  return first;
94
90
  }
95
91
 
96
- function compileDynamic<T>(
97
- routes: readonly { path: string; value: T }[],
98
- ): CompiledDynamic<T> {
92
+ function compileDynamic<T>(routes: readonly { path: string; value: T }[]): CompiledDynamic<T> {
99
93
  const alts: CompiledDynamic<T>["alts"][number][] = [];
100
94
  const parts: string[] = [];
101
95
  let group = 1;
@@ -121,18 +115,12 @@ function compileDynamic<T>(
121
115
  group += 1 + keys.length;
122
116
  }
123
117
 
124
- const regex =
125
- parts.length === 0
126
- ? /(?!)/
127
- : new RegExp(`^(?:${parts.join("|")})$`);
118
+ const regex = parts.length === 0 ? /(?!)/ : new RegExp(`^(?:${parts.join("|")})$`);
128
119
 
129
120
  return { regex, alts };
130
121
  }
131
122
 
132
- function matchCompiled<T>(
133
- dyn: CompiledDynamic<T>,
134
- path: string,
135
- ): RouteMatch<T> | undefined {
123
+ function matchCompiled<T>(dyn: CompiledDynamic<T>, path: string): RouteMatch<T> | undefined {
136
124
  const m = dyn.regex.exec(path);
137
125
  if (!m) return undefined;
138
126
 
@@ -272,9 +260,7 @@ export class TrieRouter<T> implements Router<T> {
272
260
  root = newTrieNode();
273
261
  this.#roots.set(method, root);
274
262
  }
275
- const parts = path.startsWith("/")
276
- ? path.split("/").slice(1)
277
- : path.split("/");
263
+ const parts = path.startsWith("/") ? path.split("/").slice(1) : path.split("/");
278
264
 
279
265
  let node = root;
280
266
  for (const seg of parts) {
@@ -306,9 +292,7 @@ export class TrieRouter<T> implements Router<T> {
306
292
  match(method: string, path: string): RouteMatch<T> | undefined {
307
293
  const root = this.#roots.get(method);
308
294
  if (!root) return undefined;
309
- const parts = path.startsWith("/")
310
- ? path.split("/").slice(1)
311
- : path.split("/");
295
+ const parts = path.startsWith("/") ? path.split("/").slice(1) : path.split("/");
312
296
  // trailing slash: empty last segment
313
297
  if (parts.length > 0 && parts[parts.length - 1] === "") {
314
298
  parts.pop();
@@ -369,9 +353,7 @@ export class LinearRouter<T> implements Router<T> {
369
353
  * @param value - Route value
370
354
  */
371
355
  add(method: string, path: string, value: T): void {
372
- const parts = path.startsWith("/")
373
- ? path.split("/").slice(1)
374
- : path.split("/");
356
+ const parts = path.startsWith("/") ? path.split("/").slice(1) : path.split("/");
375
357
  this.#routes.push({ method, path, value, parts });
376
358
  }
377
359
 
@@ -380,9 +362,7 @@ export class LinearRouter<T> implements Router<T> {
380
362
  * @param path - Pathname
381
363
  */
382
364
  match(method: string, path: string): RouteMatch<T> | undefined {
383
- const pathParts = path.startsWith("/")
384
- ? path.split("/").slice(1)
385
- : path.split("/");
365
+ const pathParts = path.startsWith("/") ? path.split("/").slice(1) : path.split("/");
386
366
  if (pathParts.length > 0 && pathParts[pathParts.length - 1] === "") {
387
367
  pathParts.pop();
388
368
  }
@@ -10,9 +10,7 @@ import { measureRoutingP99Ms } from "../release/measure.ts";
10
10
  describe("routing overhead budget", () => {
11
11
  test(`p99 match < ${ROUTING_P99_BUDGET_MS} ms`, () => {
12
12
  const p99 = measureRoutingP99Ms();
13
- console.log(
14
- `routing p99=${p99.toFixed(4)} ms budget=${ROUTING_P99_BUDGET_MS}`,
15
- );
13
+ console.log(`routing p99=${p99.toFixed(4)} ms budget=${ROUTING_P99_BUDGET_MS}`);
16
14
  expect(p99).toBeGreaterThanOrEqual(0);
17
15
  expect(p99).toBeLessThan(ROUTING_P99_BUDGET_MS);
18
16
  });