okengine 0.15.2 → 0.17.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 (256) hide show
  1. package/manifest.v1.schema.json +21 -2
  2. package/package.json +1 -1
  3. package/site/content/docs/elements/clock.mdx +7 -0
  4. package/site/content/docs/elements/flow.mdx +20 -12
  5. package/site/content/docs/elements/gate.mdx +108 -3
  6. package/site/content/docs/elements/signal.mdx +46 -17
  7. package/site/content/docs/elements/store.mdx +36 -32
  8. package/site/content/docs/elements/vault.mdx +26 -0
  9. package/site/content/docs/get-started/project-structure.mdx +4 -4
  10. package/site/content/docs/reference/cli.md +1 -1
  11. package/site/content/docs/reference/client.mdx +75 -17
  12. package/site/content/docs/reference/configuration.mdx +7 -4
  13. package/site/content/docs/reference/errors.mdx +24 -17
  14. package/site/content/docs/reference/fx.mdx +39 -22
  15. package/site/content/docs/reference/security.md +1 -1
  16. package/src/auth/api-key-sql.test.ts +47 -0
  17. package/src/auth/api-key-sql.ts +261 -0
  18. package/src/auth/api-keys.ts +276 -14
  19. package/src/auth/auth.test.ts +56 -0
  20. package/src/auth/config.ts +24 -5
  21. package/src/auth/index.ts +62 -1
  22. package/src/auth/plugin.ts +6 -1
  23. package/src/auth/schema.ts +8 -0
  24. package/src/auth/sessions.ts +18 -0
  25. package/src/auth/tables.ts +32 -0
  26. package/src/auth/tenant-config.ts +74 -0
  27. package/src/auth/tenant-tables.ts +11 -0
  28. package/src/auth/tenants.test.ts +63 -0
  29. package/src/auth/tenants.ts +360 -0
  30. package/src/cli/attach-host-console.test.ts +37 -0
  31. package/src/cli/attach-host-console.ts +6 -0
  32. package/src/cli/build.ts +2 -2
  33. package/src/client/budget.test.ts +1 -1
  34. package/src/client/create.ts +75 -5
  35. package/src/client/index.ts +13 -0
  36. package/src/client/live.test.ts +422 -0
  37. package/src/client/live.ts +389 -0
  38. package/src/client/notes-contract.test.ts +41 -0
  39. package/src/client/types.ts +85 -6
  40. package/src/client-react/index.ts +85 -1
  41. package/src/client-react/use-live.test.ts +129 -0
  42. package/src/compiler/effects-infer.ts +37 -2
  43. package/src/compiler/extract.test.ts +143 -11
  44. package/src/compiler/extract.ts +210 -30
  45. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +1 -2
  46. package/src/compiler/fixtures/skyport.expected.json +2 -3
  47. package/src/compiler/response.ts +41 -11
  48. package/src/console/server/access.test.ts +116 -8
  49. package/src/console/server/access.ts +70 -23
  50. package/src/console/server/app.ts +45 -11
  51. package/src/console/server/console.test.ts +6 -8
  52. package/src/console/server/dev-identities.test.ts +127 -0
  53. package/src/console/server/dev-identities.ts +229 -0
  54. package/src/console/server/flows-invoke.test.ts +21 -4
  55. package/src/console/server/flows.ts +113 -58
  56. package/src/console/server/gates.test.ts +24 -0
  57. package/src/console/server/gates.ts +10 -30
  58. package/src/console/server/index.ts +1 -0
  59. package/src/console/server/live.test.ts +26 -1
  60. package/src/console/server/live.ts +4 -2
  61. package/src/console/server/operator-db.ts +2 -0
  62. package/src/console/server/state.ts +29 -55
  63. package/src/console/server/store.test.ts +17 -0
  64. package/src/console/server/store.ts +43 -1
  65. package/src/console/ui-next/dist/assets/access-page-CXVWWMmD.js +4 -0
  66. package/src/console/ui-next/dist/assets/{agent-disclosure-C0X1fbWF.js → agent-disclosure-BP0Y0Sux.js} +1 -1
  67. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
  68. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
  69. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
  70. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
  71. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
  72. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
  73. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
  74. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
  75. package/src/console/ui-next/dist/assets/flows-page-XDpAJO8f.js +1 -0
  76. package/src/console/ui-next/dist/assets/{highlighted-json-CS_O8L-r.js → highlighted-json-Awq7gYdu.js} +1 -1
  77. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
  78. package/src/console/ui-next/dist/assets/index-BBj2QJCu.js +66 -0
  79. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
  80. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
  81. package/src/console/ui-next/dist/assets/observability-page-BFaay44m.js +4 -0
  82. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
  83. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
  84. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
  85. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
  86. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +1 -0
  87. package/src/console/ui-next/dist/assets/store-page-CS5-aETQ.js +41 -0
  88. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
  89. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +55 -0
  90. package/src/console/ui-next/dist/assets/units-page-Ca2Z-E52.js +1 -0
  91. package/src/console/ui-next/dist/assets/{use-vault-list-BLbZhzyF.js → use-vault-list-uk4WVboC.js} +1 -1
  92. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
  93. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
  94. package/src/console/ui-next/dist/assets/vault-page-BmOeAFwg.js +2 -0
  95. package/src/console/ui-next/dist/index.html +11 -9
  96. package/src/console/ui-next/index.html +3 -3
  97. package/src/console/ui-next/src/client.ts +167 -0
  98. package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
  99. package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
  100. package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
  101. package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
  102. package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
  103. package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
  104. package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
  105. package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
  106. package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
  107. package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
  108. package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
  109. package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
  110. package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
  111. package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
  112. package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
  113. package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
  114. package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
  115. package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
  116. package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
  117. package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
  118. package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
  119. package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
  120. package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
  121. package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
  122. package/src/console/ui-next/src/features/flows/fixture.ts +0 -1
  123. package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
  124. package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +5 -1
  125. package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +15 -4
  126. package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
  127. package/src/console/ui-next/src/lib/document-meta.ts +2 -1
  128. package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
  129. package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
  130. package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
  131. package/src/console/ui-next/src/lib/shortcut.ts +3 -0
  132. package/src/console/ui-next/src/router.tsx +10 -1
  133. package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
  134. package/src/console/ui-next/ui-next-seed-manifest-surface.ts +2 -9
  135. package/src/console/ui-next/ui-next-seed-manifest.ts +0 -1
  136. package/src/console/ui-next/ui-next-seed-store.ts +43 -15
  137. package/src/console/xss.gate.test.ts +1 -0
  138. package/src/drivers/index.ts +2 -0
  139. package/src/drivers/journal-postgres.ts +12 -3
  140. package/src/drivers/pg-rls.ts +26 -2
  141. package/src/drivers/pg-vault-rls.ts +68 -0
  142. package/src/drivers/signal-engine.ts +69 -15
  143. package/src/drivers/signal-live-iter.ts +65 -0
  144. package/src/drivers/signal-nats.ts +2 -1
  145. package/src/drivers/signal-postgres.ts +95 -12
  146. package/src/drivers/signal-redis.ts +2 -1
  147. package/src/drivers/signal-retention.ts +64 -0
  148. package/src/drivers/signal-types.ts +35 -5
  149. package/src/elements/clock/declare.ts +64 -2
  150. package/src/elements/clock/reconcile.ts +98 -25
  151. package/src/elements/clock/runtime.ts +13 -2
  152. package/src/elements/clock.test.ts +28 -0
  153. package/src/elements/clock.ts +9 -1
  154. package/src/elements/gate/declare.ts +2 -0
  155. package/src/elements/gate/permissions.ts +12 -0
  156. package/src/elements/gate.ts +6 -1
  157. package/src/elements/signal/declare.ts +44 -10
  158. package/src/elements/signal/delivery-modes.test.ts +90 -4
  159. package/src/elements/signal/order-lifecycle.test.ts +20 -4
  160. package/src/elements/signal/runtime.ts +42 -0
  161. package/src/elements/signal.test.ts +21 -8
  162. package/src/elements/signal.ts +1 -1
  163. package/src/elements/store/cache.ts +1 -1
  164. package/src/elements/store/declare.ts +7 -0
  165. package/src/elements/store/rls-identity.test.ts +29 -0
  166. package/src/elements/store/rls-identity.ts +3 -0
  167. package/src/elements/store/runtime.ts +14 -0
  168. package/src/elements/store/schema-decl.ts +162 -44
  169. package/src/elements/store/schema-tenant.ts +41 -0
  170. package/src/elements/store/sql-rls-isolation.test.ts +39 -0
  171. package/src/elements/store/sql-select.types.test.ts +71 -0
  172. package/src/elements/store/sql-session.ts +54 -32
  173. package/src/elements/store.ts +5 -0
  174. package/src/elements/vault/builtin-adapter.ts +15 -2
  175. package/src/elements/vault/declare.ts +8 -0
  176. package/src/elements/vault/runtime.ts +7 -0
  177. package/src/elements/vault/sql-rls-isolation.test.ts +145 -0
  178. package/src/elements/vault/storage.ts +9 -0
  179. package/src/elements/vault/test-helpers.ts +2 -1
  180. package/src/i18n/catalogs/ar.ts +19 -0
  181. package/src/i18n/catalogs/en.ts +19 -0
  182. package/src/index.ts +2 -0
  183. package/src/kernel/adopt-routes.ts +56 -8
  184. package/src/kernel/api-key-host-persist.test.ts +143 -0
  185. package/src/kernel/app-tenant.ts +122 -0
  186. package/src/kernel/app.ts +196 -57
  187. package/src/kernel/auth-resolve.ts +13 -5
  188. package/src/kernel/boot-bind/clock.ts +9 -3
  189. package/src/kernel/boot.test.ts +4 -4
  190. package/src/kernel/boot.ts +2 -0
  191. package/src/kernel/budget.test.ts +1 -1
  192. package/src/kernel/clock-durable.ts +8 -0
  193. package/src/kernel/clock-per-tenant-name.ts +5 -0
  194. package/src/kernel/clock-reconcile.ts +8 -0
  195. package/src/kernel/errors-live-resume.ts +15 -0
  196. package/src/kernel/errors-tenant.ts +29 -0
  197. package/src/kernel/errors.registry.test.ts +31 -3
  198. package/src/kernel/errors.ts +43 -3
  199. package/src/kernel/flow.ts +26 -5
  200. package/src/kernel/fx-auth-keys.test.ts +88 -0
  201. package/src/kernel/fx-auth-keys.ts +203 -0
  202. package/src/kernel/fx-auth-tenants.test.ts +87 -0
  203. package/src/kernel/fx-auth-tenants.ts +286 -0
  204. package/src/kernel/fx-live-stream.ts +149 -0
  205. package/src/kernel/fx-live.test.ts +157 -0
  206. package/src/kernel/fx-runtime.ts +15 -0
  207. package/src/kernel/fx-tenant-store.ts +213 -0
  208. package/src/kernel/fx.test.ts +108 -1
  209. package/src/kernel/fx.ts +206 -28
  210. package/src/kernel/hooks.ts +2 -2
  211. package/src/kernel/http-resource.ts +9 -18
  212. package/src/kernel/index.ts +2 -0
  213. package/src/kernel/journal.ts +12 -0
  214. package/src/kernel/live-http.test.ts +78 -0
  215. package/src/kernel/live-http.ts +114 -0
  216. package/src/kernel/live-resume.test.ts +125 -0
  217. package/src/kernel/on.ts +51 -0
  218. package/src/kernel/pipeline-api-key.test.ts +192 -7
  219. package/src/kernel/pipeline-tenant.ts +49 -0
  220. package/src/kernel/pipeline.test.ts +1 -1
  221. package/src/kernel/pipeline.ts +41 -4
  222. package/src/kernel/resource-mount.test.ts +10 -27
  223. package/src/kernel/tenant-resolve.test.ts +101 -0
  224. package/src/kernel/tenant-resolve.ts +124 -0
  225. package/src/kernel/tenant-roles.test.ts +87 -0
  226. package/src/kernel/triggers.ts +59 -21
  227. package/src/manifest/diff.test.ts +11 -2
  228. package/src/manifest/diff.ts +53 -11
  229. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  230. package/src/manifest/fixtures/skyport.manifest.json +0 -1
  231. package/src/manifest/types.ts +58 -6
  232. package/src/release/build-lib.ts +14 -1
  233. package/src/release/limits.ts +2 -2
  234. package/src/release/measure.ts +55 -1
  235. package/src/client/live-gap.test.ts +0 -35
  236. package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +0 -1
  237. package/src/console/ui-next/dist/assets/call-pii-button-DEDCl_j3.js +0 -1
  238. package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +0 -1
  239. package/src/console/ui-next/dist/assets/confirm-sheet-3ptDzXbz.js +0 -1
  240. package/src/console/ui-next/dist/assets/explorer-empty-BvYrygyO.js +0 -1
  241. package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +0 -1
  242. package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +0 -1
  243. package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +0 -66
  244. package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +0 -2
  245. package/src/console/ui-next/dist/assets/link-COeyggt-.js +0 -1
  246. package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +0 -4
  247. package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +0 -18
  248. package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +0 -1
  249. package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
  250. package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +0 -1
  251. package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +0 -1
  252. package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +0 -41
  253. package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +0 -2
  254. package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +0 -54
  255. package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +0 -1
  256. package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +0 -2
@@ -34,6 +34,15 @@ import {
34
34
 
35
35
  export type { WhereMap } from "./sql-condition.ts";
36
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
+
37
46
  /** Serialize stamp frames on a shared connection so concurrent identities cannot interleave. */
38
47
  const rlsStampTails = new WeakMap<SqlConnection, Promise<unknown>>();
39
48
 
@@ -97,44 +106,44 @@ export interface SqlSessionOptions {
97
106
  * Continuations after `.orderBy(...)` — awaitable, optional `.limit` /
98
107
  * `.offset`.
99
108
  */
100
- export interface SelectOrderBuilder extends PromiseLike<SqlRow[]> {
109
+ export interface SelectOrderBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
101
110
  /**
102
111
  * Cap the number of returned rows.
103
112
  *
104
113
  * @param n - Max rows
105
114
  */
106
- limit(n: number): Promise<SqlRow[]>;
115
+ limit(n: number): Promise<TRow[]>;
107
116
  /**
108
117
  * Skip the first `n` rows (`LIMIT … OFFSET …`).
109
118
  *
110
119
  * @param n - Rows to skip
111
120
  */
112
- offset(n: number): Promise<SqlRow[]>;
121
+ offset(n: number): Promise<TRow[]>;
113
122
  }
114
123
 
115
124
  /**
116
125
  * Continuations after `.where(...)` on a select — awaitable, optional
117
126
  * `.orderBy(...)` / `.limit(...)` / `.offset(...)`.
118
127
  */
119
- export interface SelectWhereBuilder extends PromiseLike<SqlRow[]> {
128
+ export interface SelectWhereBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
120
129
  /**
121
130
  * Order rows with Drizzle `asc()` / `desc()` terms.
122
131
  *
123
132
  * @param orders - Order terms
124
133
  */
125
- orderBy(...orders: readonly unknown[]): SelectOrderBuilder;
134
+ orderBy(...orders: readonly unknown[]): SelectOrderBuilder<TRow>;
126
135
  /**
127
136
  * Cap the number of returned rows.
128
137
  *
129
138
  * @param n - Max rows
130
139
  */
131
- limit(n: number): Promise<SqlRow[]>;
140
+ limit(n: number): Promise<TRow[]>;
132
141
  /**
133
142
  * Skip the first `n` rows.
134
143
  *
135
144
  * @param n - Rows to skip
136
145
  */
137
- offset(n: number): Promise<SqlRow[]>;
146
+ offset(n: number): Promise<TRow[]>;
138
147
  }
139
148
 
140
149
  /**
@@ -142,42 +151,49 @@ export interface SelectWhereBuilder extends PromiseLike<SqlRow[]> {
142
151
  * `.where(...)` / `.orderBy(...)` / `.limit(...)` / `.offset(...)` in any
143
152
  * order.
144
153
  */
145
- export interface SelectFromBuilder extends PromiseLike<SqlRow[]> {
154
+ export interface SelectFromBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
146
155
  /**
147
156
  * Filter with a plain equality map or a Drizzle SQL condition
148
157
  * (`eq`, `and`, `or`, `lt`, `like`, …).
149
158
  *
150
159
  * @param where - Condition
151
160
  */
152
- where(where: unknown): SelectWhereBuilder;
161
+ where(where: unknown): SelectWhereBuilder<TRow>;
153
162
  /**
154
163
  * Order rows with Drizzle `asc()` / `desc()` terms.
155
164
  *
156
165
  * @param orders - Order terms
157
166
  */
158
- orderBy(...orders: readonly unknown[]): SelectOrderBuilder;
167
+ orderBy(...orders: readonly unknown[]): SelectOrderBuilder<TRow>;
159
168
  /**
160
169
  * Cap the number of returned rows.
161
170
  *
162
171
  * @param n - Max rows
163
172
  */
164
- limit(n: number): Promise<SqlRow[]>;
173
+ limit(n: number): Promise<TRow[]>;
165
174
  /**
166
175
  * Skip the first `n` rows.
167
176
  *
168
177
  * @param n - Rows to skip
169
178
  */
170
- offset(n: number): Promise<SqlRow[]>;
179
+ offset(n: number): Promise<TRow[]>;
171
180
  }
172
181
 
173
- /** Fluent select builder. */
174
- 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> {
175
189
  /**
176
190
  * Choose the source table.
177
191
  *
178
192
  * @param table - Table handle or Drizzle table
179
193
  */
180
- from(table: TableHandle | unknown): SelectFromBuilder;
194
+ from<TTable>(
195
+ table: TTable,
196
+ ): SelectFromBuilder<TLocked extends undefined ? InferSelectRow<TTable> : TLocked>;
181
197
  }
182
198
 
183
199
  /** Fluent insert builder. */
@@ -244,12 +260,13 @@ export interface SqlStoreHandle {
244
260
  /** Underlying driver id. */
245
261
  readonly driverId: SqlConnection["driverId"];
246
262
  /**
247
- * Start a select. Optional column map projects / aliases columns
248
- * (`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}.
249
265
  *
250
266
  * @param columns - Optional `{ alias: drizzleColumn }` map
251
267
  */
252
- select(columns?: unknown): SelectBuilder;
268
+ select(): SelectBuilder<undefined>;
269
+ select(columns: unknown): SelectBuilder<SqlRow>;
253
270
  /**
254
271
  * Start an insert into `table`.
255
272
  *
@@ -600,20 +617,20 @@ export function createSqlStoreHandle(
600
617
  };
601
618
  }
602
619
 
603
- const handle: SqlStoreHandle = {
620
+ const handle = {
604
621
  ref,
605
622
  routedRole: options.routedRole,
606
623
  driverId: connection.driverId,
607
624
 
608
- select(columns?) {
625
+ select(columns?: unknown) {
609
626
  return {
610
- from(table) {
627
+ from(table: TableHandle | unknown) {
611
628
  return selectFrom(table, columns);
612
629
  },
613
630
  };
614
631
  },
615
632
 
616
- insert(table): InsertBuilder {
633
+ insert(table: TableHandle | unknown): InsertBuilder {
617
634
  const name = resolveTableName(table);
618
635
  return {
619
636
  values(row) {
@@ -648,7 +665,7 @@ export function createSqlStoreHandle(
648
665
  };
649
666
  },
650
667
 
651
- update(table): UpdateBuilder {
668
+ update(table: TableHandle | unknown): UpdateBuilder {
652
669
  const name = resolveTableName(table);
653
670
  return {
654
671
  set(row) {
@@ -675,7 +692,7 @@ export function createSqlStoreHandle(
675
692
  };
676
693
  },
677
694
 
678
- async findById(table, idValue) {
695
+ async findById(table: TableHandle | unknown, idValue: string) {
679
696
  await ensureFromMeta(table);
680
697
  const name = resolveTableName(table);
681
698
  const pk = resolvePkColumn(table);
@@ -716,7 +733,7 @@ export function createSqlStoreHandle(
716
733
  return builder;
717
734
  }) as SqlStoreHandle["delete"],
718
735
 
719
- async exists(table, idOrWhere) {
736
+ async exists(table: TableHandle | unknown, idOrWhere: string | WhereMap) {
720
737
  await ensureFromMeta(table);
721
738
  const name = resolveTableName(table);
722
739
  const where: WhereMap =
@@ -734,7 +751,12 @@ export function createSqlStoreHandle(
734
751
  return rows.length > 0;
735
752
  },
736
753
 
737
- 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
+ ) {
738
760
  await ensureFromMeta(table);
739
761
  const name = resolveTableName(table);
740
762
  const compiled = compileTableWhere(table, matchOn);
@@ -766,7 +788,7 @@ export function createSqlStoreHandle(
766
788
  return { status: "changed" as const };
767
789
  },
768
790
 
769
- async increment(table, idValue, column, by = 1) {
791
+ async increment(table: TableHandle | unknown, idValue: string, column: string, by = 1) {
770
792
  await ensureFromMeta(table);
771
793
  const name = resolveTableName(table);
772
794
  const pk = resolvePkColumn(table);
@@ -785,7 +807,7 @@ export function createSqlStoreHandle(
785
807
  return asNumber(row[sqlCol], sqlCol);
786
808
  },
787
809
 
788
- async raw(sql, params = []) {
810
+ async raw(sql: string, params: readonly unknown[] = []) {
789
811
  const table = tableFromSql(sql);
790
812
  // Memory SQL (console-next seed) implements DML on `exec`, not `query`.
791
813
  // SELECT / RETURNING still go through `query` so callers get rows.
@@ -797,7 +819,7 @@ export function createSqlStoreHandle(
797
819
  return mask(rows, table);
798
820
  },
799
821
 
800
- async count(table, where) {
822
+ async count(table: TableHandle | unknown, where?: unknown) {
801
823
  await ensureFromMeta(table);
802
824
  const name = resolveTableName(table);
803
825
  let sql = `SELECT COUNT(*) AS "count" FROM ${quoteIdent(name)}`;
@@ -813,7 +835,7 @@ export function createSqlStoreHandle(
813
835
  return asNumber(rows[0]?.count ?? rows[0]?.COUNT ?? 0, "count");
814
836
  },
815
837
 
816
- async page(table, options) {
838
+ async page(table: TableHandle | unknown, options: SqlPageOptions) {
817
839
  if (options.after !== undefined && options.before !== undefined) {
818
840
  throw new Error("page(): after and before (keyset) cannot combine");
819
841
  }
@@ -838,7 +860,7 @@ export function createSqlStoreHandle(
838
860
  });
839
861
  },
840
862
 
841
- async ensureTable(table) {
863
+ async ensureTable(table: TableHandle) {
842
864
  const cols = Object.values(table.columns);
843
865
  const pk = resolvePkColumn(table);
844
866
  const intType =
@@ -863,7 +885,7 @@ export function createSqlStoreHandle(
863
885
  },
864
886
  };
865
887
 
866
- return handle;
888
+ return handle as SqlStoreHandle;
867
889
  }
868
890
 
869
891
  /**
@@ -31,6 +31,8 @@ export {
31
31
  schemaPolicyGate,
32
32
  schemaPolicyOwner,
33
33
  schemaPolicyScope,
34
+ schemaPolicyTenant,
35
+ schemaUnscoped,
34
36
  isSchemaTableDecl,
35
37
  isSchemaColumnDecl,
36
38
  isSchemaRelationsDecl,
@@ -47,6 +49,8 @@ export type {
47
49
  SchemaColumnInput,
48
50
  SchemaTableDecl,
49
51
  SchemaTableWithColumns,
52
+ InferColumnJs,
53
+ FinalizeColumn,
50
54
  SchemaRelationsDecl,
51
55
  SchemaPolicyDecl,
52
56
  SchemaTableExtra,
@@ -121,6 +125,7 @@ export type {
121
125
  SelectFromBuilder,
122
126
  SelectWhereBuilder,
123
127
  SelectOrderBuilder,
128
+ InferSelectRow,
124
129
  InsertBuilder,
125
130
  InsertValuesBuilder,
126
131
  SqlSessionOptions,
@@ -204,6 +204,18 @@ export interface BuiltinVaultAdapter extends VaultAdapter {
204
204
  /** Numeric column as any supported driver may hand it back. */
205
205
  type SqlNumber = number | string | bigint;
206
206
 
207
+ /**
208
+ * First path segment when the contract is stored as `{tenantId}/{name}`.
209
+ *
210
+ * @param path - Canonical vault path
211
+ */
212
+ function tenantIdFromVaultPath(path: string): string | null {
213
+ const i = path.indexOf("/");
214
+ if (i <= 0) return null;
215
+ const id = path.slice(0, i);
216
+ return id.length > 0 ? id : null;
217
+ }
218
+
207
219
  /** `oke_vault_secrets` row as returned by the driver. */
208
220
  interface SecretRow {
209
221
  id: string;
@@ -539,8 +551,8 @@ export function createBuiltinVaultAdapter(opts: CreateBuiltinVaultOptions): Buil
539
551
  updated_at: Date | string;
540
552
  }>(
541
553
  `INSERT INTO oke_vault_secrets
542
- (path, encrypted_value, iv, auth_tag, version, metadata, algorithm, kek_version, expires_at)
543
- VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8, $9)
554
+ (path, encrypted_value, iv, auth_tag, version, metadata, algorithm, kek_version, expires_at, tenant_id)
555
+ VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8, $9, $10)
544
556
  RETURNING id, created_at, updated_at`,
545
557
  [
546
558
  path,
@@ -552,6 +564,7 @@ export function createBuiltinVaultAdapter(opts: CreateBuiltinVaultOptions): Buil
552
564
  ALGORITHM,
553
565
  kekVersion,
554
566
  expiresAt ?? null,
567
+ tenantIdFromVaultPath(path),
555
568
  ],
556
569
  );
557
570
  const row = inserted[0];
@@ -24,6 +24,11 @@ export interface VaultSecretOptions {
24
24
  * in the clear. Defaults to `true` for secrets and `false` for config.
25
25
  */
26
26
  readonly sensitive?: boolean;
27
+ /**
28
+ * Resolve per `fx.tenant.id` at request time (not boot).
29
+ * Default true when `gate.auth.tenant` is on.
30
+ */
31
+ readonly perTenant?: boolean;
27
32
  }
28
33
 
29
34
  /** Declared vault contract handle. */
@@ -38,6 +43,8 @@ export interface VaultSecretDecl {
38
43
  readonly dev?: string;
39
44
  /** Whether cleartext must never leave the runtime (default by kind). */
40
45
  readonly sensitive: boolean;
46
+ /** Request-time tenant path when true. */
47
+ readonly perTenant?: boolean;
41
48
  }
42
49
 
43
50
  /**
@@ -82,6 +89,7 @@ function declareSecret(name: string, options: VaultSecretOptions = {}): VaultSec
82
89
  ...(options.rotate !== undefined ? { rotate: options.rotate } : {}),
83
90
  ...(options.schema !== undefined ? { schema: options.schema } : {}),
84
91
  ...(options.dev !== undefined ? { dev: options.dev } : {}),
92
+ ...(options.perTenant !== undefined ? { perTenant: options.perTenant } : {}),
85
93
  };
86
94
  secretRegistry.push(decl);
87
95
  return decl;
@@ -95,6 +95,10 @@ export interface CreateVaultRuntimeOptions {
95
95
  readonly allowGaps?: boolean;
96
96
  /** Clock for last-read timestamps (tests). */
97
97
  readonly now?: () => number;
98
+ /**
99
+ * When true, contracts with `perTenant !== false` are not boot-gap fatal.
100
+ */
101
+ readonly tenantEnabled?: boolean;
98
102
  }
99
103
 
100
104
  /** Vault runtime surface. */
@@ -244,6 +248,9 @@ export function createVaultRuntime(options: CreateVaultRuntimeOptions = {}): Vau
244
248
 
245
249
  const gaps: VaultGap[] = [];
246
250
  for (const c of contracts.values()) {
251
+ const perTenant =
252
+ c.perTenant === true || (options.tenantEnabled === true && c.perTenant !== false);
253
+ if (perTenant) continue;
247
254
  const value = merged.get(c.name);
248
255
  if (value === undefined || value.length === 0) {
249
256
  gaps.push({
@@ -0,0 +1,145 @@
1
+ /**
2
+ * `oke_vault_secrets` tenant RLS — same concurrent-identity proof as
3
+ * domain tables: two tenants, one shared PGlite connection, row-level
4
+ * isolation via `oke.tenant()`.
5
+ *
6
+ * Policy is installed by production DDL ({@link ensureVaultTables} /
7
+ * {@link installOkeRlsHelpers}), not by this file.
8
+ */
9
+
10
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
11
+ import { connectPglite } from "../../drivers/pglite.ts";
12
+ import { installOkeRlsHelpers } from "../../drivers/pg-rls.ts";
13
+ import type { SqlConnection } from "../../drivers/types.ts";
14
+ import { createSqlStoreHandle, type SqlStoreHandle } from "../store/sql-session.ts";
15
+ import { sqlConnectionAsExec } from "./builtin-adapter.ts";
16
+ import { ensureVaultTables } from "./storage.ts";
17
+
18
+ const CIPHER = String.raw`'\x00'::bytea`;
19
+
20
+ const SEED = `
21
+ INSERT INTO oke_vault_secrets (path, encrypted_value, iv, auth_tag, version, tenant_id)
22
+ VALUES
23
+ ('acme/stripe', ${CIPHER}, ${CIPHER}, ${CIPHER}, 1, 'acme'),
24
+ ('globex/stripe', ${CIPHER}, ${CIPHER}, ${CIPHER}, 1, 'globex'),
25
+ ('GLOBAL_KEY', ${CIPHER}, ${CIPHER}, ${CIPHER}, 1, NULL)
26
+ `;
27
+
28
+ /** File-scoped warmed PGlite. */
29
+ let conn: SqlConnection;
30
+ /** Stamped as tenant acme. */
31
+ let asAcme: SqlStoreHandle;
32
+ /** Stamped as tenant globex. */
33
+ let asGlobex: SqlStoreHandle;
34
+ /** No stamp — table-owner / superuser bypass (ENABLE without FORCE). */
35
+ let asOwner: SqlStoreHandle;
36
+
37
+ beforeAll(async () => {
38
+ conn = await connectPglite({ url: "memory://vault-sql-rls-isolation", role: "primary" });
39
+ await installOkeRlsHelpers((sql) => conn.exec(sql));
40
+ await ensureVaultTables(sqlConnectionAsExec(conn));
41
+ await conn.exec(SEED);
42
+ asAcme = createSqlStoreHandle("sql:app", {
43
+ connection: conn,
44
+ classifications: new Map(),
45
+ routedRole: "primary",
46
+ domainDdl: "off",
47
+ rls: { gate: "member", userId: "u1", scopes: ["member"], tenantId: "acme" },
48
+ });
49
+ asGlobex = createSqlStoreHandle("sql:app", {
50
+ connection: conn,
51
+ classifications: new Map(),
52
+ routedRole: "primary",
53
+ domainDdl: "off",
54
+ rls: { gate: "member", userId: "u1", scopes: ["member"], tenantId: "globex" },
55
+ });
56
+ asOwner = createSqlStoreHandle("sql:app", {
57
+ connection: conn,
58
+ classifications: new Map(),
59
+ routedRole: "primary",
60
+ domainDdl: "off",
61
+ });
62
+ }, 15_000);
63
+
64
+ afterAll(async () => {
65
+ await conn.close();
66
+ });
67
+
68
+ describe("oke_vault_secrets tenant RLS (pglite)", () => {
69
+ test("production DDL installed oke_vault_secrets_tenant", async () => {
70
+ const rows = await conn.query(
71
+ `SELECT policyname FROM pg_policies
72
+ WHERE tablename = 'oke_vault_secrets' AND policyname = 'oke_vault_secrets_tenant'`,
73
+ );
74
+ expect(rows).toEqual([{ policyname: "oke_vault_secrets_tenant" }]);
75
+ });
76
+
77
+ test("sequential identities see own tenant plus global NULL rows", async () => {
78
+ const acme = await asAcme.raw(`SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`);
79
+ const globex = await asGlobex.raw(
80
+ `SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`,
81
+ );
82
+ expect(acme).toEqual([
83
+ { path: "GLOBAL_KEY", tenant_id: null },
84
+ { path: "acme/stripe", tenant_id: "acme" },
85
+ ]);
86
+ expect(globex).toEqual([
87
+ { path: "GLOBAL_KEY", tenant_id: null },
88
+ { path: "globex/stripe", tenant_id: "globex" },
89
+ ]);
90
+ });
91
+
92
+ test("concurrent identities do not leak across the shared connection", async () => {
93
+ const [acme, globex, acmeAgain] = await Promise.all([
94
+ asAcme.raw(`SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`),
95
+ asGlobex.raw(`SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`),
96
+ asAcme.raw(`SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`),
97
+ ]);
98
+ expect(acme).toEqual([
99
+ { path: "GLOBAL_KEY", tenant_id: null },
100
+ { path: "acme/stripe", tenant_id: "acme" },
101
+ ]);
102
+ expect(globex).toEqual([
103
+ { path: "GLOBAL_KEY", tenant_id: null },
104
+ { path: "globex/stripe", tenant_id: "globex" },
105
+ ]);
106
+ expect(acmeAgain).toEqual([
107
+ { path: "GLOBAL_KEY", tenant_id: null },
108
+ { path: "acme/stripe", tenant_id: "acme" },
109
+ ]);
110
+ });
111
+
112
+ test("unstamped owner still sees every ciphertext row", async () => {
113
+ const rows = await asOwner.raw(`SELECT path, tenant_id FROM oke_vault_secrets ORDER BY path`);
114
+ expect(rows).toEqual([
115
+ { path: "GLOBAL_KEY", tenant_id: null },
116
+ { path: "acme/stripe", tenant_id: "acme" },
117
+ { path: "globex/stripe", tenant_id: "globex" },
118
+ ]);
119
+ });
120
+ });
121
+
122
+ describe("oke_vault_secrets tenant RLS — tables before helpers", () => {
123
+ test("helper install attaches the policy after the table exists", async () => {
124
+ const late = await connectPglite({
125
+ url: "memory://vault-sql-rls-late-helpers",
126
+ role: "primary",
127
+ });
128
+ try {
129
+ await ensureVaultTables(sqlConnectionAsExec(late));
130
+ const before = await late.query(
131
+ `SELECT COUNT(*)::text AS n FROM pg_policies
132
+ WHERE tablename = 'oke_vault_secrets' AND policyname = 'oke_vault_secrets_tenant'`,
133
+ );
134
+ expect(before[0]?.n).toBe("0");
135
+ await installOkeRlsHelpers((sql) => late.exec(sql));
136
+ const after = await late.query(
137
+ `SELECT COUNT(*)::text AS n FROM pg_policies
138
+ WHERE tablename = 'oke_vault_secrets' AND policyname = 'oke_vault_secrets_tenant'`,
139
+ );
140
+ expect(after[0]?.n).toBe("1");
141
+ } finally {
142
+ await late.close();
143
+ }
144
+ });
145
+ });
@@ -14,6 +14,7 @@ import type { AuditAction, AuditEntry, AuditWriter } from "./audit.ts";
14
14
  import { VaultError } from "./errors.ts";
15
15
  import type { VaultErrorCode } from "./errors.ts";
16
16
  import type { VaultActorType } from "./types.ts";
17
+ import { ensureVaultSecretsTenantRls } from "../../drivers/pg-vault-rls.ts";
17
18
 
18
19
  /**
19
20
  * Minimal SQL surface the Vault needs.
@@ -75,6 +76,7 @@ CREATE TABLE IF NOT EXISTS oke_vault_secrets (
75
76
  updated_at timestamptz NOT NULL DEFAULT now(),
76
77
  expires_at timestamptz,
77
78
  deleted_at timestamptz,
79
+ tenant_id text,
78
80
  CONSTRAINT oke_vault_secrets_path_version_key UNIQUE (path, version)
79
81
  )`;
80
82
 
@@ -168,6 +170,8 @@ const POST_DDL_STATEMENTS: readonly string[] = [
168
170
  `ALTER TABLE oke_vault_status ADD COLUMN IF NOT EXISTS rewrap_key_hash text`,
169
171
  `ALTER TABLE oke_vault_status ADD COLUMN IF NOT EXISTS rotate_locked_by text`,
170
172
  `ALTER TABLE oke_vault_status ADD COLUMN IF NOT EXISTS rotate_lease_expires_at bigint`,
173
+ `ALTER TABLE oke_vault_secrets ADD COLUMN IF NOT EXISTS tenant_id text`,
174
+ `CREATE INDEX IF NOT EXISTS oke_vault_secrets_tenant_idx ON oke_vault_secrets (tenant_id)`,
171
175
  `CREATE INDEX IF NOT EXISTS oke_vault_audit_seq_idx ON oke_vault_audit (seq)`,
172
176
  `CREATE INDEX IF NOT EXISTS oke_vault_audit_created_idx ON oke_vault_audit (created_at, id)`,
173
177
  `CREATE INDEX IF NOT EXISTS oke_vault_audit_path_idx ON oke_vault_audit (path)`,
@@ -200,6 +204,11 @@ export async function ensureVaultTables(db: SqlExec): Promise<void> {
200
204
  throw new VaultError("BACKEND_ERROR", "vault: failed to ensure vault tables");
201
205
  }
202
206
  }
207
+ try {
208
+ await ensureVaultSecretsTenantRls((sql: string) => db.execute(sql));
209
+ } catch {
210
+ throw new VaultError("BACKEND_ERROR", "vault: failed to ensure vault tables");
211
+ }
203
212
  }
204
213
 
205
214
  /**
@@ -251,7 +251,8 @@ function isDdl(text: string): boolean {
251
251
  return (
252
252
  text.startsWith("create table") ||
253
253
  text.startsWith("alter table") ||
254
- text.startsWith("create index")
254
+ text.startsWith("create index") ||
255
+ text.startsWith("do $oke_vault_rls$")
255
256
  );
256
257
  }
257
258
 
@@ -36,6 +36,22 @@ export const builtinAr = {
36
36
  cause: 'التدفق "{flow}" يُصدِر الإشارة "{resource}" بلا مشترك.',
37
37
  fix: "أضف on({resource}, …) أو عيّن الإشارة '{'optional: true'}'.",
38
38
  },
39
+ "1014": {
40
+ cause: 'مؤشر البث "{afterId}" ليس على شريط "{signal}".',
41
+ fix: "أعد الاتصال بدون Last-Event-ID لإعادة تشغيل الشريط المتبقي.",
42
+ },
43
+ "1015": {
44
+ cause: "هذه العملية تحتاج مستأجراً، ولم يُحدَّد مستأجر لهذا الطلب.",
45
+ fix: "استدعِ fx.auth.switchTenant(id) أو أرسل مطالبة tid موقّعة أو رأس المستأجر.",
46
+ },
47
+ "1016": {
48
+ cause: 'المتصل ليس عضواً في المستأجر "{tenant}".',
49
+ fix: "اختر مستأجراً من fx.auth.listTenants() أو أضف المستخدم كعضو.",
50
+ },
51
+ "1017": {
52
+ cause: 'نطاق دور المستأجر "{scope}" ليس نطاقاً تطبيقياً معلناً.',
53
+ fix: "استخدم اسماً من كتالوج البيان لهذا التطبيق (وليس console:*).",
54
+ },
39
55
  "1101": {
40
56
  cause: "جدول النطاق غير موجود — لم تُطبَّق الترحيلات.",
41
57
  fix: "شغّل `oke db migrate` على هذه البيئة.",
@@ -62,6 +78,9 @@ export const builtinAr = {
62
78
  "Forbidden.ip_denied": "عنوان IP الخاص بك محظور.",
63
79
  "Forbidden.ip_not_allowed": "عنوان IP الخاص بك غير مسموح.",
64
80
  "Forbidden.policy_denied": "رفضت السياسة هذا الطلب.",
81
+ "Forbidden.tenant_required": "هذا الطلب يحتاج مستأجراً.",
82
+ "Forbidden.not_member": "لست عضواً في ذلك المستأجر.",
83
+ "Forbidden.unknown_scope": "نطاق دور المستأجر هذا ليس نطاقاً تطبيقياً معلناً.",
65
84
  "AuthRateLimited.rate_limited": "محاولات مصادقة كثيرة جداً. حاول لاحقاً.",
66
85
  },
67
86
  } as const;
@@ -36,6 +36,22 @@ export const builtinEn = {
36
36
  cause: 'Flow "{flow}" emits signal "{resource}" with no subscriber.',
37
37
  fix: "Add on({resource}, …) or mark the signal '{'optional: true'}'.",
38
38
  },
39
+ "1014": {
40
+ cause: 'Live cursor "{afterId}" is not on the tape for "{signal}".',
41
+ fix: "Reconnect without Last-Event-ID to replay the remaining tape.",
42
+ },
43
+ "1015": {
44
+ cause: "This operation needs a tenant, but none is resolved for this request.",
45
+ fix: "Call fx.auth.switchTenant(id), send a signed tid claim, or pass the tenant header.",
46
+ },
47
+ "1016": {
48
+ cause: 'The caller is not a member of tenant "{tenant}".',
49
+ fix: "Pick a tenant from fx.auth.listTenants() or add the user as a member.",
50
+ },
51
+ "1017": {
52
+ cause: 'Tenant role scope "{scope}" is not a declared application scope.',
53
+ fix: "Use a name from this app's Manifest catalog (never console:*).",
54
+ },
39
55
  "1101": {
40
56
  cause: "domain table not found — migrations have not been applied.",
41
57
  fix: "run `oke db migrate` against this environment.",
@@ -63,6 +79,9 @@ export const builtinEn = {
63
79
  "Forbidden.ip_denied": "Your IP address is blocked.",
64
80
  "Forbidden.ip_not_allowed": "Your IP address is not allowed.",
65
81
  "Forbidden.policy_denied": "Policy denied this request.",
82
+ "Forbidden.tenant_required": "This request needs a tenant.",
83
+ "Forbidden.not_member": "You are not a member of that tenant.",
84
+ "Forbidden.unknown_scope": "That tenant role scope is not a declared application scope.",
66
85
  "AuthRateLimited.rate_limited": "Too many authentication attempts. Try again later.",
67
86
  },
68
87
  } as const;
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ export {
62
62
  type StoreDecl,
63
63
  type StoreRuntime,
64
64
  type SqlStoreHandle,
65
+ type InferSelectRow,
65
66
  type TableHandle,
66
67
  type SchemaTableDecl,
67
68
  type SchemaColumnDecl,
@@ -79,6 +80,7 @@ export {
79
80
  createSignalRuntime,
80
81
  type SignalDecl,
81
82
  type SignalOptions,
83
+ type SignalRetention,
82
84
  type SignalRuntime,
83
85
  type DeadLetter,
84
86
  type SignalFailureReason,