okengine 0.14.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/package.json +3 -4
  2. package/site/content/docs/elements/ai.mdx +2 -1
  3. package/site/content/docs/elements/channel.mdx +1 -0
  4. package/site/content/docs/elements/clock.mdx +3 -0
  5. package/site/content/docs/elements/flow.mdx +9 -0
  6. package/site/content/docs/elements/gate.mdx +80 -22
  7. package/site/content/docs/elements/signal.mdx +4 -4
  8. package/site/content/docs/elements/store.mdx +84 -27
  9. package/site/content/docs/elements/vault.mdx +4 -0
  10. package/site/content/docs/get-started/basic-usage.mdx +22 -13
  11. package/site/content/docs/get-started/index.mdx +5 -0
  12. package/site/content/docs/get-started/introduction.mdx +11 -3
  13. package/site/content/docs/get-started/meta.json +1 -1
  14. package/site/content/docs/get-started/project-structure.mdx +932 -0
  15. package/site/content/docs/reference/cli.md +13 -10
  16. package/site/content/docs/reference/client.mdx +28 -21
  17. package/site/content/docs/reference/fx.mdx +29 -18
  18. package/site/content/docs/reference/security.md +2 -2
  19. package/src/auth/api-key-sql.test.ts +47 -0
  20. package/src/auth/api-key-sql.ts +254 -0
  21. package/src/auth/api-keys.ts +273 -14
  22. package/src/auth/auth.test.ts +56 -0
  23. package/src/auth/bindings.ts +14 -5
  24. package/src/auth/config.ts +5 -5
  25. package/src/auth/cross-plane.ts +10 -3
  26. package/src/auth/index.ts +25 -1
  27. package/src/auth/schema.ts +8 -0
  28. package/src/cli/ai-setup/ai-setup.test.ts +62 -0
  29. package/src/cli/ai-setup/apply.ts +32 -1
  30. package/src/cli/attach-host-console.test.ts +40 -2
  31. package/src/cli/attach-host-console.ts +9 -0
  32. package/src/cli/build.test.ts +4 -2
  33. package/src/cli/db-seed.ts +48 -15
  34. package/src/cli/db.test.ts +104 -0
  35. package/src/cli/db.ts +122 -10
  36. package/src/cli/dev-controls.test.ts +20 -0
  37. package/src/cli/dev-controls.ts +13 -0
  38. package/src/cli/dev.test.ts +15 -4
  39. package/src/cli/dev.ts +102 -29
  40. package/src/cli/ensure-drizzle-config.ts +2 -1
  41. package/src/cli/tui/DevLive.tsx +12 -2
  42. package/src/cli/tui/keys.test.ts +2 -1
  43. package/src/cli/tui/keys.ts +3 -2
  44. package/src/compiler/aot.test.ts +1 -1
  45. package/src/compiler/effects-infer.ts +17 -2
  46. package/src/compiler/extract.test.ts +409 -33
  47. package/src/compiler/extract.ts +318 -67
  48. package/src/compiler/flow-path.test.ts +96 -0
  49. package/src/compiler/flow-path.ts +173 -0
  50. package/src/compiler/generate-adopt.test.ts +112 -3
  51. package/src/compiler/generate-adopt.ts +280 -26
  52. package/src/compiler/schema-from-ast.ts +609 -0
  53. package/src/console/server/access.test.ts +116 -8
  54. package/src/console/server/access.ts +70 -23
  55. package/src/console/server/app.ts +1 -0
  56. package/src/console/server/bind.ts +2 -2
  57. package/src/console/server/console.test.ts +3 -0
  58. package/src/console/server/dev-identities.test.ts +127 -0
  59. package/src/console/server/dev-identities.ts +229 -0
  60. package/src/console/server/flows-invoke.test.ts +21 -4
  61. package/src/console/server/flows.ts +233 -98
  62. package/src/console/server/gates.test.ts +24 -0
  63. package/src/console/server/gates.ts +8 -21
  64. package/src/console/server/index.ts +1 -0
  65. package/src/console/server/invoke-user-flow.test.ts +3 -1
  66. package/src/console/server/invoke-user-flow.ts +17 -2
  67. package/src/console/server/iso-at.test.ts +9 -0
  68. package/src/console/server/iso-at.ts +11 -0
  69. package/src/console/server/live.test.ts +26 -1
  70. package/src/console/server/live.ts +4 -2
  71. package/src/console/server/operator-db.ts +2 -0
  72. package/src/console/server/security-headers.ts +6 -3
  73. package/src/console/server/spa-proxy.test.ts +1 -0
  74. package/src/console/server/sql-catalog.ts +147 -7
  75. package/src/console/server/sql-rls.test.ts +16 -0
  76. package/src/console/server/state.ts +37 -55
  77. package/src/console/server/store.test.ts +7 -4
  78. package/src/console/server/store.ts +142 -58
  79. package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
  80. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
  81. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
  82. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
  83. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
  84. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
  85. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
  86. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
  87. package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
  88. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
  89. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
  90. package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
  91. package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
  92. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
  93. package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
  94. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
  95. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
  96. package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
  97. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
  98. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
  99. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
  100. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
  101. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
  102. package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
  103. package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
  104. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
  105. package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
  106. package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
  107. package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
  108. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
  109. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
  110. package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
  111. package/src/console/ui-next/dist/index.html +12 -10
  112. package/src/console/ui-next/index.html +3 -3
  113. package/src/console/ui-next/src/client.ts +202 -7
  114. package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
  115. package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
  116. package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
  117. package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
  118. package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
  119. package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
  120. package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
  121. package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
  122. package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
  123. package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
  124. package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
  125. package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
  126. package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
  127. package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
  128. package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
  129. package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
  130. package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
  131. package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
  132. package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
  133. package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
  134. package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
  135. package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
  136. package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
  137. package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
  138. package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
  139. package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
  140. package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
  141. package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
  142. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
  143. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
  144. package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
  145. package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
  146. package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
  147. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
  148. package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
  149. package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
  150. package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
  151. package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
  152. package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
  153. package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
  154. package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
  155. package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
  156. package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
  157. package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
  158. package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
  159. package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
  160. package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
  161. package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
  162. package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
  163. package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
  164. package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
  165. package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
  166. package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
  167. package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
  168. package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
  169. package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
  170. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
  171. package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
  172. package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
  173. package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
  174. package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
  175. package/src/console/ui-next/src/lib/document-meta.ts +2 -1
  176. package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
  177. package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
  178. package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
  179. package/src/console/ui-next/src/lib/shortcut.ts +3 -0
  180. package/src/console/ui-next/src/router.tsx +10 -1
  181. package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
  182. package/src/console/ui-next/ui-next-seed-store.ts +43 -15
  183. package/src/console/xss.gate.test.ts +1 -0
  184. package/src/docker/compose-health.test.ts +27 -0
  185. package/src/docker/compose-health.ts +19 -3
  186. package/src/docker/docker.test.ts +3 -0
  187. package/src/docker/recipes/pgdog.ts +2 -0
  188. package/src/drivers/clock-postgres.ts +13 -9
  189. package/src/drivers/instances-postgres.ts +4 -6
  190. package/src/drivers/journal-postgres.ts +13 -9
  191. package/src/drivers/pg-rls.test.ts +87 -0
  192. package/src/drivers/pg-rls.ts +233 -7
  193. package/src/drivers/pglite.ts +26 -3
  194. package/src/drivers/postgres.test.ts +23 -0
  195. package/src/drivers/postgres.ts +133 -9
  196. package/src/drivers/types.ts +10 -0
  197. package/src/elements/clock/declare.ts +26 -1
  198. package/src/elements/gate/boot.ts +5 -5
  199. package/src/elements/gate/declare.ts +38 -4
  200. package/src/elements/gate/flatten.ts +1 -1
  201. package/src/elements/gate/runtime.ts +1 -1
  202. package/src/elements/store/cache.ts +1 -1
  203. package/src/elements/store/declare.ts +9 -6
  204. package/src/elements/store/emit-drizzle.ts +44 -8
  205. package/src/elements/store/resource.test.ts +1 -1
  206. package/src/elements/store/rls-identity.test.ts +95 -0
  207. package/src/elements/store/rls-identity.ts +116 -0
  208. package/src/elements/store/runtime.ts +18 -0
  209. package/src/elements/store/schema-decl.test.ts +37 -0
  210. package/src/elements/store/schema-decl.ts +260 -41
  211. package/src/elements/store/seed.ts +1 -1
  212. package/src/elements/store/sql-condition.test.ts +12 -0
  213. package/src/elements/store/sql-condition.ts +26 -2
  214. package/src/elements/store/sql-rls-isolation.test.ts +165 -0
  215. package/src/elements/store/sql-rls-stamp.test.ts +117 -0
  216. package/src/elements/store/sql-select.types.test.ts +71 -0
  217. package/src/elements/store/sql-session.ts +135 -35
  218. package/src/elements/store/upsert-app.test.ts +1 -1
  219. package/src/elements/store.ts +17 -0
  220. package/src/http.ts +20 -3
  221. package/src/index.ts +11 -1
  222. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  223. package/src/kernel/adopt-routes.ts +30 -2
  224. package/src/kernel/api-key-host-persist.test.ts +143 -0
  225. package/src/kernel/app.ts +145 -16
  226. package/src/kernel/auth-resolve.ts +10 -5
  227. package/src/kernel/auto-cache.test.ts +8 -8
  228. package/src/kernel/auto-registry.test.ts +49 -9
  229. package/src/kernel/boot.test.ts +5 -5
  230. package/src/kernel/boot.ts +2 -2
  231. package/src/kernel/effects-stamping.test.ts +3 -3
  232. package/src/kernel/element-registries.ts +12 -5
  233. package/src/kernel/errors.ts +24 -0
  234. package/src/kernel/flow-units.ts +64 -0
  235. package/src/kernel/flow.ts +29 -17
  236. package/src/kernel/fx-auth-keys.test.ts +88 -0
  237. package/src/kernel/fx-auth-keys.ts +198 -0
  238. package/src/kernel/fx.test.ts +17 -1
  239. package/src/kernel/fx.ts +59 -18
  240. package/src/kernel/horizontal-child.ts +4 -4
  241. package/src/kernel/http-path-pending.ts +20 -0
  242. package/src/kernel/http-resource.ts +4 -0
  243. package/src/kernel/http-stream.test.ts +4 -4
  244. package/src/kernel/pipeline-api-key.test.ts +192 -7
  245. package/src/kernel/pipeline.test.ts +2 -2
  246. package/src/kernel/pipeline.ts +4 -2
  247. package/src/kernel/plugin-elements.test.ts +1 -1
  248. package/src/kernel/plugin-needs.test.ts +1 -1
  249. package/src/kernel/ready.test.ts +2 -2
  250. package/src/kernel/resource-mount.test.ts +14 -0
  251. package/src/kernel/run-duration.test.ts +2 -2
  252. package/src/kernel/stamp-http.test.ts +79 -0
  253. package/src/kernel/stamp-http.ts +75 -0
  254. package/src/kernel/triggers.ts +52 -27
  255. package/src/manifest/types.ts +22 -3
  256. package/src/release/build-lib.ts +1 -0
  257. package/src/release/measure.ts +2 -2
  258. package/src/runtime/json-code-block.test.ts +2 -2
  259. package/src/runtime/serve.test.ts +4 -4
  260. package/src/term.test.ts +52 -4
  261. package/src/term.ts +125 -17
  262. package/src/test/reset-element-registries.ts +8 -3
  263. package/src/cli/ask-seed.test.ts +0 -96
  264. package/src/cli/ask-seed.ts +0 -82
  265. package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
  266. package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
  267. package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
  268. package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
  269. package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
  270. package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
  271. package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
  272. package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
  273. package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
  274. package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
  275. package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
  276. package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
  277. package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
  278. package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
  279. package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
  280. package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
  281. package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
  282. package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
  283. package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
  284. package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
  285. package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
@@ -0,0 +1,117 @@
1
+ /**
2
+ * RLS stamps on pooled postgres must `reserve()` one slot, then BEGIN/SET
3
+ * on that slot. `begin()` + parent `unsafe()` checkouts a second slot and
4
+ * deadlocks a tiny pool (`checkout timeout` from PgDog).
5
+ */
6
+
7
+ import { describe, expect, test } from "bun:test";
8
+ import { connectPostgres, type PostgresClientLike } from "../../drivers/postgres.ts";
9
+ import { createSqlStoreHandle } from "./sql-session.ts";
10
+
11
+ /**
12
+ * Two-slot pool. `reserve()` holds a slot until `release()`. Unreserved
13
+ * `BEGIN` occupies a slot until COMMIT on that same checkout.
14
+ */
15
+ function createTinyPoolClient(size = 2): PostgresClientLike & {
16
+ readonly reserveCalls: { n: number };
17
+ readonly rootBegins: { n: number };
18
+ } {
19
+ const slots = Array.from({ length: size }, () => ({ held: false }));
20
+ const reserveCalls = { n: 0 };
21
+ const rootBegins = { n: 0 };
22
+
23
+ function checkout(): { held: boolean } {
24
+ const idle = slots.find((slot) => !slot.held);
25
+ if (!idle) {
26
+ throw new Error("checkout timeout");
27
+ }
28
+ return idle;
29
+ }
30
+
31
+ function rowsFor(sql: string): Record<string, unknown>[] {
32
+ return /select/i.test(sql) && !/set_config/i.test(sql) ? [{ id: "a", owner: "alice" }] : [];
33
+ }
34
+
35
+ const client: PostgresClientLike & {
36
+ readonly reserveCalls: { n: number };
37
+ readonly rootBegins: { n: number };
38
+ } = {
39
+ reserveCalls,
40
+ rootBegins,
41
+ async reserve() {
42
+ reserveCalls.n += 1;
43
+ const slot = checkout();
44
+ slot.held = true;
45
+ return {
46
+ async unsafe(sql) {
47
+ return rowsFor(sql);
48
+ },
49
+ release() {
50
+ slot.held = false;
51
+ },
52
+ };
53
+ },
54
+ async unsafe(sql) {
55
+ const slot = checkout();
56
+ const head = sql.trim().split(/\s+/)[0]?.toUpperCase();
57
+ if (head === "BEGIN") {
58
+ rootBegins.n += 1;
59
+ slot.held = true;
60
+ return [];
61
+ }
62
+ if (head === "COMMIT" || head === "ROLLBACK") {
63
+ slot.held = false;
64
+ return [];
65
+ }
66
+ return rowsFor(sql);
67
+ },
68
+ };
69
+ return client;
70
+ }
71
+
72
+ describe("sql-session RLS stamp on pooled postgres", () => {
73
+ test("pins the stamp through reserve — no leaked root BEGIN", async () => {
74
+ const pool = createTinyPoolClient(2);
75
+ const connection = await connectPostgres({ client: pool, role: "primary" });
76
+ const handle = createSqlStoreHandle("sql:app", {
77
+ connection,
78
+ classifications: new Map(),
79
+ routedRole: "primary",
80
+ domainDdl: "off",
81
+ rls: { gate: "member", userId: "alice", scopes: ["member"] },
82
+ });
83
+
84
+ const [a, b] = await Promise.all([
85
+ handle.raw(`SELECT id, owner FROM notes`),
86
+ handle.raw(`SELECT id, owner FROM notes`),
87
+ ]);
88
+
89
+ expect(a).toEqual([{ id: "a", owner: "alice" }]);
90
+ expect(b).toEqual([{ id: "a", owner: "alice" }]);
91
+ expect(pool.reserveCalls.n).toBe(2);
92
+ expect(pool.rootBegins.n).toBe(0);
93
+ });
94
+
95
+ test("table browse SELECT pins through reserve like any other stamped query", async () => {
96
+ const pool = createTinyPoolClient(2);
97
+ const connection = await connectPostgres({ client: pool, role: "primary" });
98
+ const handle = createSqlStoreHandle("sql:app", {
99
+ connection,
100
+ classifications: new Map(),
101
+ routedRole: "primary",
102
+ domainDdl: "off",
103
+ rls: { gate: "member", userId: "alice", scopes: ["member"] },
104
+ });
105
+ const rows = await handle.raw(`SELECT * FROM "notes" LIMIT 50`);
106
+ expect(rows).toEqual([{ id: "a", owner: "alice" }]);
107
+ expect(pool.reserveCalls.n).toBe(1);
108
+ expect(pool.rootBegins.n).toBe(0);
109
+ });
110
+
111
+ test("standalone BEGIN on a 2-slot pool reproduces checkout timeout", async () => {
112
+ const pool = createTinyPoolClient(2);
113
+ await expect(pool.unsafe("BEGIN")).resolves.toEqual([]);
114
+ await expect(pool.unsafe("BEGIN")).resolves.toEqual([]);
115
+ await expect(pool.unsafe("SET LOCAL ROLE oke_app")).rejects.toThrow("checkout timeout");
116
+ });
117
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Type-level proof: `select().from(store.schema.table())` resolves to the
3
+ * declared column JS shape — not `SqlRow`.
4
+ */
5
+ import { describe, expect, test } from "bun:test";
6
+
7
+ import type { SqlRow } from "../../drivers/types.ts";
8
+ import { field, schemaTable } from "./schema-decl.ts";
9
+ import type { InferSelectRow, SqlStoreHandle } from "./sql-session.ts";
10
+ import { defineTable } from "./table.ts";
11
+
12
+ /** Compile-time equality. */
13
+ type Eq<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
14
+ type Assert<T extends true> = T;
15
+
16
+ const items = schemaTable("items", {
17
+ id: field.text().primaryKey(),
18
+ title: field.text().notNull(),
19
+ note: field.text(),
20
+ count: field.integer().notNull(),
21
+ score: field.integer(),
22
+ });
23
+
24
+ type ExpectedRow = {
25
+ id: string;
26
+ title: string;
27
+ note: string | null;
28
+ count: number;
29
+ score: number | null;
30
+ };
31
+
32
+ async function selectAll(db: SqlStoreHandle) {
33
+ return db.select().from(items);
34
+ }
35
+
36
+ async function selectWhere(db: SqlStoreHandle) {
37
+ return db.select().from(items).where({ id: "x" });
38
+ }
39
+
40
+ async function selectOrder(db: SqlStoreHandle) {
41
+ return db.select().from(items).orderBy(items.title);
42
+ }
43
+
44
+ async function selectLimit(db: SqlStoreHandle) {
45
+ return db.select().from(items).limit(1);
46
+ }
47
+
48
+ async function selectProj(db: SqlStoreHandle) {
49
+ return db.select({ title: items.title }).from(items);
50
+ }
51
+
52
+ const bare = defineTable("bare", { id: { name: "id" } });
53
+
54
+ async function selectBare(db: SqlStoreHandle) {
55
+ return db.select().from(bare);
56
+ }
57
+
58
+ type _Infer = Assert<Eq<InferSelectRow<typeof items>, ExpectedRow>>;
59
+ type _All = Assert<Eq<Awaited<ReturnType<typeof selectAll>>, ExpectedRow[]>>;
60
+ type _Where = Assert<Eq<Awaited<ReturnType<typeof selectWhere>>, ExpectedRow[]>>;
61
+ type _Order = Assert<Eq<Awaited<ReturnType<typeof selectOrder>>, ExpectedRow[]>>;
62
+ type _Limit = Assert<Eq<Awaited<ReturnType<typeof selectLimit>>, ExpectedRow[]>>;
63
+ type _Proj = Assert<Eq<Awaited<ReturnType<typeof selectProj>>, SqlRow[]>>;
64
+ type _Bare = Assert<Eq<Awaited<ReturnType<typeof selectBare>>, SqlRow[]>>;
65
+
66
+ describe("select().from() row inference", () => {
67
+ test("type-level row shape matches declared columns", () => {
68
+ const _keep: _Infer & _All & _Where & _Order & _Limit & _Proj & _Bare = true;
69
+ expect(_keep).toBe(true);
70
+ });
71
+ });
@@ -7,6 +7,12 @@
7
7
 
8
8
  import type { DomainDdlMode } from "../../config/index.ts";
9
9
  import type { ClassificationMap, SqlConnection, SqlRow } from "../../drivers/types.ts";
10
+ import {
11
+ buildRlsIdentityPreludeSql,
12
+ installOkeRlsHelpers,
13
+ RLS_CONTEXT_DRIVERS,
14
+ type RlsIdentity,
15
+ } from "../../drivers/pg-rls.ts";
10
16
  import { throwOke } from "../../kernel/errors.ts";
11
17
  import { maskRows, tableFromSql } from "./classify.ts";
12
18
  import { isMissingDomainRelationError } from "./missing-relation.ts";
@@ -28,6 +34,54 @@ import {
28
34
 
29
35
  export type { WhereMap } from "./sql-condition.ts";
30
36
 
37
+ /**
38
+ * Inferred select-row shape for a table handle.
39
+ *
40
+ * Uses `$inferSelect` when present (OKE `store.schema.table()` and Drizzle
41
+ * `pgTable()` / `sqliteTable()`). Bare {@link TableHandle} / `unknown` stay
42
+ * {@link SqlRow}.
43
+ */
44
+ export type InferSelectRow<T> = T extends { readonly $inferSelect: infer R } ? R : SqlRow;
45
+
46
+ /** Serialize stamp frames on a shared connection so concurrent identities cannot interleave. */
47
+ const rlsStampTails = new WeakMap<SqlConnection, Promise<unknown>>();
48
+
49
+ /**
50
+ * True when SQL must not enter an RLS identity frame (txn control or DDL).
51
+ *
52
+ * @param sql - Statement
53
+ */
54
+ export function isRlsStampExemptSql(sql: string): boolean {
55
+ return /^\s*(begin|commit|rollback|create|alter|drop|truncate|grant|revoke|comment)\b/i.test(sql);
56
+ }
57
+
58
+ /**
59
+ * Run `fn` after any in-flight stamp on `connection` finishes.
60
+ *
61
+ * @param connection - Shared SQL connection
62
+ * @param fn - Stamp frame
63
+ */
64
+ async function withRlsStampLock<T>(connection: SqlConnection, fn: () => Promise<T>): Promise<T> {
65
+ const prev = rlsStampTails.get(connection) ?? Promise.resolve();
66
+ let release!: () => void;
67
+ const gate = new Promise<void>((resolve) => {
68
+ release = resolve;
69
+ });
70
+ rlsStampTails.set(
71
+ connection,
72
+ prev.then(
73
+ () => gate,
74
+ () => gate,
75
+ ),
76
+ );
77
+ await prev.catch(() => undefined);
78
+ try {
79
+ return await fn();
80
+ } finally {
81
+ release();
82
+ }
83
+ }
84
+
31
85
  /** Options for a SQL session. */
32
86
  export interface SqlSessionOptions {
33
87
  /** Open connection (already role-routed). */
@@ -44,50 +98,52 @@ export interface SqlSessionOptions {
44
98
  * and local+autoPush). Default `ensure` for backward-compatible tests.
45
99
  */
46
100
  readonly domainDdl?: DomainDdlMode;
101
+ /** Gate identity — stamps `oke.*` on postgres / pglite. */
102
+ readonly rls?: RlsIdentity;
47
103
  }
48
104
 
49
105
  /**
50
106
  * Continuations after `.orderBy(...)` — awaitable, optional `.limit` /
51
107
  * `.offset`.
52
108
  */
53
- export interface SelectOrderBuilder extends PromiseLike<SqlRow[]> {
109
+ export interface SelectOrderBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
54
110
  /**
55
111
  * Cap the number of returned rows.
56
112
  *
57
113
  * @param n - Max rows
58
114
  */
59
- limit(n: number): Promise<SqlRow[]>;
115
+ limit(n: number): Promise<TRow[]>;
60
116
  /**
61
117
  * Skip the first `n` rows (`LIMIT … OFFSET …`).
62
118
  *
63
119
  * @param n - Rows to skip
64
120
  */
65
- offset(n: number): Promise<SqlRow[]>;
121
+ offset(n: number): Promise<TRow[]>;
66
122
  }
67
123
 
68
124
  /**
69
125
  * Continuations after `.where(...)` on a select — awaitable, optional
70
126
  * `.orderBy(...)` / `.limit(...)` / `.offset(...)`.
71
127
  */
72
- export interface SelectWhereBuilder extends PromiseLike<SqlRow[]> {
128
+ export interface SelectWhereBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
73
129
  /**
74
130
  * Order rows with Drizzle `asc()` / `desc()` terms.
75
131
  *
76
132
  * @param orders - Order terms
77
133
  */
78
- orderBy(...orders: readonly unknown[]): SelectOrderBuilder;
134
+ orderBy(...orders: readonly unknown[]): SelectOrderBuilder<TRow>;
79
135
  /**
80
136
  * Cap the number of returned rows.
81
137
  *
82
138
  * @param n - Max rows
83
139
  */
84
- limit(n: number): Promise<SqlRow[]>;
140
+ limit(n: number): Promise<TRow[]>;
85
141
  /**
86
142
  * Skip the first `n` rows.
87
143
  *
88
144
  * @param n - Rows to skip
89
145
  */
90
- offset(n: number): Promise<SqlRow[]>;
146
+ offset(n: number): Promise<TRow[]>;
91
147
  }
92
148
 
93
149
  /**
@@ -95,42 +151,49 @@ export interface SelectWhereBuilder extends PromiseLike<SqlRow[]> {
95
151
  * `.where(...)` / `.orderBy(...)` / `.limit(...)` / `.offset(...)` in any
96
152
  * order.
97
153
  */
98
- export interface SelectFromBuilder extends PromiseLike<SqlRow[]> {
154
+ export interface SelectFromBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
99
155
  /**
100
156
  * Filter with a plain equality map or a Drizzle SQL condition
101
157
  * (`eq`, `and`, `or`, `lt`, `like`, …).
102
158
  *
103
159
  * @param where - Condition
104
160
  */
105
- where(where: unknown): SelectWhereBuilder;
161
+ where(where: unknown): SelectWhereBuilder<TRow>;
106
162
  /**
107
163
  * Order rows with Drizzle `asc()` / `desc()` terms.
108
164
  *
109
165
  * @param orders - Order terms
110
166
  */
111
- orderBy(...orders: readonly unknown[]): SelectOrderBuilder;
167
+ orderBy(...orders: readonly unknown[]): SelectOrderBuilder<TRow>;
112
168
  /**
113
169
  * Cap the number of returned rows.
114
170
  *
115
171
  * @param n - Max rows
116
172
  */
117
- limit(n: number): Promise<SqlRow[]>;
173
+ limit(n: number): Promise<TRow[]>;
118
174
  /**
119
175
  * Skip the first `n` rows.
120
176
  *
121
177
  * @param n - Rows to skip
122
178
  */
123
- offset(n: number): Promise<SqlRow[]>;
179
+ offset(n: number): Promise<TRow[]>;
124
180
  }
125
181
 
126
- /** Fluent select builder. */
127
- export interface SelectBuilder {
182
+ /**
183
+ * Fluent select builder.
184
+ *
185
+ * `TLocked` is `undefined` for `select()` (row type inferred from `.from`)
186
+ * and {@link SqlRow} for `select({ alias: col })` projections.
187
+ */
188
+ export interface SelectBuilder<TLocked extends SqlRow | undefined = undefined> {
128
189
  /**
129
190
  * Choose the source table.
130
191
  *
131
192
  * @param table - Table handle or Drizzle table
132
193
  */
133
- from(table: TableHandle | unknown): SelectFromBuilder;
194
+ from<TTable>(
195
+ table: TTable,
196
+ ): SelectFromBuilder<TLocked extends undefined ? InferSelectRow<TTable> : TLocked>;
134
197
  }
135
198
 
136
199
  /** Fluent insert builder. */
@@ -197,12 +260,13 @@ export interface SqlStoreHandle {
197
260
  /** Underlying driver id. */
198
261
  readonly driverId: SqlConnection["driverId"];
199
262
  /**
200
- * Start a select. Optional column map projects / aliases columns
201
- * (`select({ clicks: links.clicks })`).
263
+ * Start a select. No-arg form infers the row from `.from(table)`.
264
+ * A column map (`select({ clicks: links.clicks })`) stays {@link SqlRow}.
202
265
  *
203
266
  * @param columns - Optional `{ alias: drizzleColumn }` map
204
267
  */
205
- select(columns?: unknown): SelectBuilder;
268
+ select(): SelectBuilder<undefined>;
269
+ select(columns: unknown): SelectBuilder<SqlRow>;
206
270
  /**
207
271
  * Start an insert into `table`.
208
272
  *
@@ -339,8 +403,9 @@ export function createSqlStoreHandle(
339
403
  ref: `sql:${string}`,
340
404
  options: SqlSessionOptions,
341
405
  ): SqlStoreHandle {
342
- const { connection, classifications, revealPii } = options;
406
+ const { connection, classifications, revealPii, rls } = options;
343
407
  const domainDdl: DomainDdlMode = options.domainDdl ?? "ensure";
408
+ let helpersReady = false;
344
409
 
345
410
  function mask(rows: SqlRow[], table?: string): SqlRow[] {
346
411
  return maskRows(rows, { classifications, table, revealPii });
@@ -362,11 +427,41 @@ export function createSqlStoreHandle(
362
427
  }
363
428
 
364
429
  function query(sql: string, params: readonly unknown[] = []): Promise<SqlRow[]> {
365
- return withSchemaGuard(() => connection.query(sql, params));
430
+ return withSchemaGuard(() => withRlsStamp((conn) => conn.query(sql, params), sql));
366
431
  }
367
432
 
368
433
  function exec(sql: string, params: readonly unknown[] = []): Promise<{ changes: number }> {
369
- return withSchemaGuard(() => connection.exec(sql, params));
434
+ return withSchemaGuard(() => withRlsStamp((conn) => conn.exec(sql, params), sql));
435
+ }
436
+
437
+ async function withRlsStamp<T>(fn: (conn: SqlConnection) => Promise<T>, sql: string): Promise<T> {
438
+ if (!rls || !RLS_CONTEXT_DRIVERS.has(connection.driverId)) return fn(connection);
439
+ if (isRlsStampExemptSql(sql)) return fn(connection);
440
+ const run = (): Promise<T> => applyRlsStamp(fn);
441
+ // PGlite is one backend session — concurrent identities must not interleave.
442
+ // Pooled postgres pins each stamp via `transaction()` instead.
443
+ if (connection.driverId === "pglite") {
444
+ return withRlsStampLock(connection, run);
445
+ }
446
+ return run();
447
+ }
448
+
449
+ async function applyRlsStamp<T>(fn: (conn: SqlConnection) => Promise<T>): Promise<T> {
450
+ if (!rls) return fn(connection);
451
+ if (!helpersReady) {
452
+ await installOkeRlsHelpers((sql) => connection.exec(sql));
453
+ helpersReady = true;
454
+ }
455
+ const frame = async (tx: SqlConnection): Promise<T> => {
456
+ for (const stmt of buildRlsIdentityPreludeSql(rls)) {
457
+ await tx.exec(stmt.sql, stmt.params ?? []);
458
+ }
459
+ return fn(tx);
460
+ };
461
+ if (!connection.transaction) {
462
+ throw new Error("RLS stamp needs SqlConnection.transaction to pin SET LOCAL");
463
+ }
464
+ return connection.transaction(frame);
370
465
  }
371
466
 
372
467
  async function ensureFromMeta(table: TableHandle | unknown): Promise<void> {
@@ -522,20 +617,20 @@ export function createSqlStoreHandle(
522
617
  };
523
618
  }
524
619
 
525
- const handle: SqlStoreHandle = {
620
+ const handle = {
526
621
  ref,
527
622
  routedRole: options.routedRole,
528
623
  driverId: connection.driverId,
529
624
 
530
- select(columns?) {
625
+ select(columns?: unknown) {
531
626
  return {
532
- from(table) {
627
+ from(table: TableHandle | unknown) {
533
628
  return selectFrom(table, columns);
534
629
  },
535
630
  };
536
631
  },
537
632
 
538
- insert(table): InsertBuilder {
633
+ insert(table: TableHandle | unknown): InsertBuilder {
539
634
  const name = resolveTableName(table);
540
635
  return {
541
636
  values(row) {
@@ -570,7 +665,7 @@ export function createSqlStoreHandle(
570
665
  };
571
666
  },
572
667
 
573
- update(table): UpdateBuilder {
668
+ update(table: TableHandle | unknown): UpdateBuilder {
574
669
  const name = resolveTableName(table);
575
670
  return {
576
671
  set(row) {
@@ -597,7 +692,7 @@ export function createSqlStoreHandle(
597
692
  };
598
693
  },
599
694
 
600
- async findById(table, idValue) {
695
+ async findById(table: TableHandle | unknown, idValue: string) {
601
696
  await ensureFromMeta(table);
602
697
  const name = resolveTableName(table);
603
698
  const pk = resolvePkColumn(table);
@@ -638,7 +733,7 @@ export function createSqlStoreHandle(
638
733
  return builder;
639
734
  }) as SqlStoreHandle["delete"],
640
735
 
641
- async exists(table, idOrWhere) {
736
+ async exists(table: TableHandle | unknown, idOrWhere: string | WhereMap) {
642
737
  await ensureFromMeta(table);
643
738
  const name = resolveTableName(table);
644
739
  const where: WhereMap =
@@ -656,7 +751,12 @@ export function createSqlStoreHandle(
656
751
  return rows.length > 0;
657
752
  },
658
753
 
659
- async upsert(table, matchOn, values, upsertOptions) {
754
+ async upsert(
755
+ table: TableHandle | unknown,
756
+ matchOn: WhereMap | unknown,
757
+ values: SqlRow,
758
+ upsertOptions?: { readonly onExisting?: "update" },
759
+ ) {
660
760
  await ensureFromMeta(table);
661
761
  const name = resolveTableName(table);
662
762
  const compiled = compileTableWhere(table, matchOn);
@@ -688,7 +788,7 @@ export function createSqlStoreHandle(
688
788
  return { status: "changed" as const };
689
789
  },
690
790
 
691
- async increment(table, idValue, column, by = 1) {
791
+ async increment(table: TableHandle | unknown, idValue: string, column: string, by = 1) {
692
792
  await ensureFromMeta(table);
693
793
  const name = resolveTableName(table);
694
794
  const pk = resolvePkColumn(table);
@@ -707,7 +807,7 @@ export function createSqlStoreHandle(
707
807
  return asNumber(row[sqlCol], sqlCol);
708
808
  },
709
809
 
710
- async raw(sql, params = []) {
810
+ async raw(sql: string, params: readonly unknown[] = []) {
711
811
  const table = tableFromSql(sql);
712
812
  // Memory SQL (console-next seed) implements DML on `exec`, not `query`.
713
813
  // SELECT / RETURNING still go through `query` so callers get rows.
@@ -719,7 +819,7 @@ export function createSqlStoreHandle(
719
819
  return mask(rows, table);
720
820
  },
721
821
 
722
- async count(table, where) {
822
+ async count(table: TableHandle | unknown, where?: unknown) {
723
823
  await ensureFromMeta(table);
724
824
  const name = resolveTableName(table);
725
825
  let sql = `SELECT COUNT(*) AS "count" FROM ${quoteIdent(name)}`;
@@ -735,7 +835,7 @@ export function createSqlStoreHandle(
735
835
  return asNumber(rows[0]?.count ?? rows[0]?.COUNT ?? 0, "count");
736
836
  },
737
837
 
738
- async page(table, options) {
838
+ async page(table: TableHandle | unknown, options: SqlPageOptions) {
739
839
  if (options.after !== undefined && options.before !== undefined) {
740
840
  throw new Error("page(): after and before (keyset) cannot combine");
741
841
  }
@@ -760,7 +860,7 @@ export function createSqlStoreHandle(
760
860
  });
761
861
  },
762
862
 
763
- async ensureTable(table) {
863
+ async ensureTable(table: TableHandle) {
764
864
  const cols = Object.values(table.columns);
765
865
  const pk = resolvePkColumn(table);
766
866
  const intType =
@@ -785,7 +885,7 @@ export function createSqlStoreHandle(
785
885
  },
786
886
  };
787
887
 
788
- return handle;
888
+ return handle as SqlStoreHandle;
789
889
  }
790
890
 
791
891
  /**
@@ -45,7 +45,7 @@ describe("fx.store(db).upsert via app.fetch (real boot)", () => {
45
45
  const db = store.sql("app", { schema: { notes } });
46
46
 
47
47
  const seed = on(
48
- http.post("/seed").gate.public,
48
+ http.post("/seed").public(),
49
49
  flow("notes.seed", {
50
50
  in: UpsertIn,
51
51
  out: UpsertOut,
@@ -26,6 +26,11 @@ export {
26
26
  schema,
27
27
  schemaTable,
28
28
  schemaRelations,
29
+ schemaRls,
30
+ schemaPolicy,
31
+ schemaPolicyGate,
32
+ schemaPolicyOwner,
33
+ schemaPolicyScope,
29
34
  isSchemaTableDecl,
30
35
  isSchemaColumnDecl,
31
36
  isSchemaRelationsDecl,
@@ -42,7 +47,11 @@ export type {
42
47
  SchemaColumnInput,
43
48
  SchemaTableDecl,
44
49
  SchemaTableWithColumns,
50
+ InferColumnJs,
51
+ FinalizeColumn,
45
52
  SchemaRelationsDecl,
53
+ SchemaPolicyDecl,
54
+ SchemaTableExtra,
46
55
  SchemaRelationEntry,
47
56
  SchemaRelationOne,
48
57
  SchemaRelationMany,
@@ -114,6 +123,7 @@ export type {
114
123
  SelectFromBuilder,
115
124
  SelectWhereBuilder,
116
125
  SelectOrderBuilder,
126
+ InferSelectRow,
117
127
  InsertBuilder,
118
128
  InsertValuesBuilder,
119
129
  SqlSessionOptions,
@@ -123,6 +133,13 @@ export type {
123
133
  WhereMap,
124
134
  } from "./store/sql-session.ts";
125
135
 
136
+ export {
137
+ resolveRlsIdentity,
138
+ rlsIdentityFromAuth,
139
+ firstPolicyOrPublic,
140
+ policyGateForScopes,
141
+ } from "./store/rls-identity.ts";
142
+ export type { RlsIdentityRow, ResolveRlsIdentityInput } from "./store/rls-identity.ts";
126
143
  export { createStoreRuntime, putTier2 } from "./store/runtime.ts";
127
144
  export type {
128
145
  StoreRuntime,
package/src/http.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * ```ts
9
9
  * import { on, flow, http, gate, oke, createBunRuntime } from "okengine/http";
10
10
  *
11
- * on(http.get("/").gate.public, flow("ping", { do: () => "Hi" }));
11
+ * on(http.get("/").public(), flow("ping", { do: () => "Hi" }));
12
12
  * const app = oke({ name: "ping" });
13
13
  * createBunRuntime().serve(app);
14
14
  * ```
@@ -24,15 +24,32 @@ export { fail, type FlowFailure } from "./kernel/errors.ts";
24
24
  export { isFlowFailure } from "./kernel/hooks.ts";
25
25
  export { plugin, isPlugin, type PluginDef } from "./kernel/plugin.ts";
26
26
  export { createBunRuntime, APP_PORT, type Runtime, type ServeOptions } from "./runtime/index.ts";
27
- export type { OkeApp, OkeOptions, ReadyState } from "./kernel/app.ts";
27
+ export type {
28
+ OkeApp,
29
+ OkeOptions,
30
+ ReadyState,
31
+ RegisteredFlowUnits,
32
+ RoutesFromRegisteredUnits,
33
+ } from "./kernel/app.ts";
34
+ export { registerFlowUnits } from "./kernel/flow-units.ts";
35
+ export { stampFlowName, stampHttpPath } from "./kernel/stamp-http.ts";
28
36
 
29
- import { oke as okeCore, type OkeApp, type OkeOptions } from "./kernel/app.ts";
37
+ import {
38
+ oke as okeCore,
39
+ type OkeApp,
40
+ type OkeOptions,
41
+ type RoutesFromRegisteredUnits,
42
+ } from "./kernel/app.ts";
30
43
 
31
44
  /**
32
45
  * Create an HTTP-oriented app — defaults `router` to `"edge"` when omitted.
33
46
  *
34
47
  * @param options - Application options
35
48
  */
49
+ export function oke(
50
+ options: OkeOptions & { readonly registry: "ignore" },
51
+ ): OkeApp<Record<string, never>, Record<string, never>>;
52
+ export function oke(options: OkeOptions): OkeApp<{}, RoutesFromRegisteredUnits>;
36
53
  export function oke(options: OkeOptions): OkeApp {
37
54
  return okeCore({
38
55
  ...options,
package/src/index.ts CHANGED
@@ -14,10 +14,19 @@
14
14
  * @module
15
15
  */
16
16
 
17
- export { oke, type OkeApp, type OkeOptions, type ReadyState } from "./kernel/app.ts";
17
+ export {
18
+ oke,
19
+ type OkeApp,
20
+ type OkeOptions,
21
+ type ReadyState,
22
+ type RegisteredFlowUnits,
23
+ type RoutesFromRegisteredUnits,
24
+ } from "./kernel/app.ts";
18
25
  export { on, type Binding } from "./kernel/on.ts";
19
26
  export { flow, isFlow, type FlowDef } from "./kernel/flow.ts";
20
27
  export { http, every, internal, table } from "./kernel/triggers.ts";
28
+ export { registerFlowUnits } from "./kernel/flow-units.ts";
29
+ export { stampFlowName, stampHttpPath } from "./kernel/stamp-http.ts";
21
30
  export { fail, type FlowFailure, type FlowErrorValue } from "./kernel/errors.ts";
22
31
  export { isFlowFailure } from "./kernel/hooks.ts";
23
32
  export { plugin, isPlugin, type PluginDef, type PluginCapabilities } from "./kernel/plugin.ts";
@@ -53,6 +62,7 @@ export {
53
62
  type StoreDecl,
54
63
  type StoreRuntime,
55
64
  type SqlStoreHandle,
65
+ type InferSelectRow,
56
66
  type TableHandle,
57
67
  type SchemaTableDecl,
58
68
  type SchemaColumnDecl,