okengine 0.17.2 → 0.18.4

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 (209) hide show
  1. package/AGENTS.md +5 -3
  2. package/README.md +14 -10
  3. package/manifest.v1.schema.json +61 -2
  4. package/package.json +19 -5
  5. package/site/content/docs/ai/index.mdx +1 -1
  6. package/site/content/docs/ai/mcp.mdx +10 -1
  7. package/site/content/docs/ai/skills.mdx +9 -9
  8. package/site/content/docs/elements/ai.mdx +1 -1
  9. package/site/content/docs/elements/clock.mdx +1 -1
  10. package/site/content/docs/elements/flow.mdx +25 -1
  11. package/site/content/docs/elements/gate.mdx +3 -2
  12. package/site/content/docs/elements/store.mdx +289 -341
  13. package/site/content/docs/elements/vault.mdx +5 -5
  14. package/site/content/docs/get-started/basic-usage.mdx +3 -10
  15. package/site/content/docs/get-started/index.mdx +1 -1
  16. package/site/content/docs/get-started/installation.mdx +2 -3
  17. package/site/content/docs/get-started/introduction.mdx +58 -121
  18. package/site/content/docs/get-started/meta.json +9 -1
  19. package/site/content/docs/get-started/project-structure.mdx +4 -11
  20. package/site/content/docs/get-started/testing.mdx +328 -0
  21. package/site/content/docs/get-started/why.mdx +93 -71
  22. package/site/content/docs/index.mdx +44 -11
  23. package/site/content/docs/meta.json +8 -5
  24. package/site/content/docs/plugins/apple.mdx +151 -0
  25. package/site/content/docs/plugins/discord.mdx +139 -0
  26. package/site/content/docs/plugins/facebook.mdx +134 -0
  27. package/site/content/docs/plugins/figma.mdx +138 -0
  28. package/site/content/docs/plugins/github.mdx +138 -0
  29. package/site/content/docs/plugins/google.mdx +153 -0
  30. package/site/content/docs/plugins/index.mdx +47 -1
  31. package/site/content/docs/plugins/meta.json +10 -0
  32. package/site/content/docs/plugins/microsoft.mdx +151 -0
  33. package/site/content/docs/plugins/oauth.mdx +188 -0
  34. package/site/content/docs/plugins/x.mdx +125 -0
  35. package/site/content/docs/providers/index.mdx +2 -0
  36. package/site/content/docs/recipes/index.mdx +2 -0
  37. package/site/content/docs/reference/cli.md +3 -2
  38. package/site/content/docs/reference/client.mdx +58 -1
  39. package/site/content/docs/reference/configuration.mdx +2 -4
  40. package/site/content/docs/reference/fx.mdx +3 -1
  41. package/site/content/docs/reference/index.mdx +0 -5
  42. package/site/content/docs/reference/meta.json +2 -2
  43. package/site/content/docs/reference/okid.mdx +137 -0
  44. package/src/auth/bindings.ts +1 -1
  45. package/src/auth/config.ts +9 -0
  46. package/src/auth/identity-sql.ts +314 -0
  47. package/src/auth/identity.ts +140 -2
  48. package/src/auth/index.ts +17 -1
  49. package/src/auth/method-context.ts +3 -0
  50. package/src/auth/oauth-as/cimd.ts +132 -0
  51. package/src/auth/oauth-as/crypto.test.ts +101 -0
  52. package/src/auth/oauth-as/crypto.ts +393 -0
  53. package/src/auth/oauth-as/errors.ts +68 -0
  54. package/src/auth/oauth-as/http.test.ts +419 -0
  55. package/src/auth/oauth-as/http.ts +842 -0
  56. package/src/auth/oauth-as/stores.ts +61 -0
  57. package/src/auth/oauth-as/tables.ts +142 -0
  58. package/src/auth/tables.ts +0 -11
  59. package/src/bench/README.md +83 -0
  60. package/src/bench/REPORT.md +176 -0
  61. package/src/bench/g01-rls-stamp.bench.ts +194 -0
  62. package/src/bench/g02-clock-per-tenant.bench.ts +158 -0
  63. package/src/bench/g03-signal-once.bench.ts +157 -0
  64. package/src/bench/g03-signal-reconnect.bench.ts +254 -0
  65. package/src/bench/g03-signal-sse-memory.bench.ts +191 -0
  66. package/src/bench/g04-auth-vault-hotpath.bench.ts +170 -0
  67. package/src/bench/g05-sustained-full.bench.ts +265 -0
  68. package/src/bench/g06-mixed-load.bench.ts +260 -0
  69. package/src/bench/g07-vault-crypto.bench.ts +100 -0
  70. package/src/bench/g07-vault-rotate-under-read.bench.ts +285 -0
  71. package/src/bench/g08-conn-oversubscribe.bench.ts +194 -0
  72. package/src/bench/g08-store-kv-durable.bench.ts +133 -0
  73. package/src/bench/g08-store-sql.bench.ts +178 -0
  74. package/src/bench/g09-journal-sustained.bench.ts +203 -0
  75. package/src/bench/g10-observability-contention.bench.ts +246 -0
  76. package/src/bench/g11-cold-start-cycle.bench.ts +164 -0
  77. package/src/bench/g13-elements.bench.ts +427 -0
  78. package/src/bench/g14-graceful-shutdown.bench.ts +244 -0
  79. package/src/bench/g15-postgres-degradation.bench.ts +264 -0
  80. package/src/bench/g16-live-query-fanout.bench.ts +206 -0
  81. package/src/bench/lib/event-loop-lag.ts +26 -0
  82. package/src/bench/lib/infra.ts +60 -0
  83. package/src/bench/lib/report.ts +52 -0
  84. package/src/bench/lib/rss-sampler.ts +61 -0
  85. package/src/bench/lib/signal-pg.ts +88 -0
  86. package/src/bench/load-app.ts +337 -0
  87. package/src/bench/load-child.ts +108 -0
  88. package/src/bench/smoke.bench.ts +43 -0
  89. package/src/cli/competitor-mention-removal.test.ts +28 -0
  90. package/src/cli/doctor-fd.ts +117 -0
  91. package/src/cli/doctor.test.ts +192 -0
  92. package/src/cli/doctor.ts +129 -1
  93. package/src/client/create.ts +95 -1
  94. package/src/client/index.ts +9 -2
  95. package/src/client/transport.ts +11 -4
  96. package/src/client/use-live-query.ts +154 -0
  97. package/src/client-react/index.ts +15 -1
  98. package/src/client-react/live-resource.ts +246 -0
  99. package/src/client-react/use-live-query.test.ts +475 -0
  100. package/src/client-react/use-live-query.ts +530 -0
  101. package/src/compiler/extract.test.ts +518 -0
  102. package/src/compiler/extract.ts +386 -19
  103. package/src/console/server/invoke-user-flow.ts +2 -1
  104. package/src/console/ui-next/dist/assets/{access-page-DnWbnGzq.js → access-page-De7Lc2JC.js} +1 -1
  105. package/src/console/ui-next/dist/assets/{flows-page-BiZ4-6yQ.js → flows-page-RGy7VEA_.js} +1 -1
  106. package/src/console/ui-next/dist/assets/{index-C8NRK2R-.js → index-_rgpdVzo.js} +3 -3
  107. package/src/console/ui-next/dist/assets/{observability-page-CrB6vd1T.js → observability-page-Ds6pcnh-.js} +1 -1
  108. package/src/console/ui-next/dist/assets/{store-page-CS5-aETQ.js → store-page-02xOiqIK.js} +3 -3
  109. package/src/console/ui-next/dist/assets/{units-page-CjtdlW8l.js → units-page-4rHOePuE.js} +1 -1
  110. package/src/console/ui-next/dist/assets/{vault-page-C6Xxm9SA.js → vault-page-DISPgxLM.js} +1 -1
  111. package/src/console/ui-next/dist/index.html +1 -1
  112. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +36 -2
  113. package/src/drivers/cdc-outbox.ts +389 -0
  114. package/src/drivers/memory.ts +20 -0
  115. package/src/drivers/oauth-apple.ts +156 -0
  116. package/src/drivers/oauth-discord.ts +79 -0
  117. package/src/drivers/oauth-facebook.ts +80 -0
  118. package/src/drivers/oauth-figma.ts +116 -0
  119. package/src/drivers/oauth-github.ts +92 -0
  120. package/src/drivers/oauth-google.ts +142 -0
  121. package/src/drivers/oauth-microsoft.ts +174 -0
  122. package/src/drivers/oauth-oidc.ts +293 -0
  123. package/src/drivers/oauth-shared.ts +326 -0
  124. package/src/drivers/oauth-types.ts +159 -0
  125. package/src/drivers/oauth-x.ts +77 -0
  126. package/src/drivers/oauth2-common.ts +95 -0
  127. package/src/drivers/oauth2-token.ts +61 -0
  128. package/src/drivers/pg-rls-row-passes.ts +251 -0
  129. package/src/drivers/pg-rls.ts +2 -0
  130. package/src/drivers/postgres.ts +45 -2
  131. package/src/drivers/signal-postgres.ts +2 -1
  132. package/src/elements/channel/runtime.ts +29 -2
  133. package/src/elements/channel.test.ts +52 -0
  134. package/src/elements/gate/boot.ts +29 -2
  135. package/src/elements/store/emit-drizzle.ts +147 -14
  136. package/src/elements/store/field-ddl.test.ts +118 -0
  137. package/src/elements/store/field-types.test.ts +455 -0
  138. package/src/elements/store/list-query.golden.json +777 -0
  139. package/src/elements/store/list-query.parity.test.ts +396 -0
  140. package/src/elements/store/list-query.ts +792 -0
  141. package/src/elements/store/live-default.test.ts +136 -0
  142. package/src/elements/store/live-http.test.ts +160 -0
  143. package/src/elements/store/live-isolation.test.ts +291 -0
  144. package/src/elements/store/live-query-runtime.test.ts +323 -0
  145. package/src/elements/store/live-query-runtime.ts +403 -0
  146. package/src/elements/store/live-query-server.test.ts +377 -0
  147. package/src/elements/store/live-query-server.ts +102 -0
  148. package/src/elements/store/live-query.ts +97 -0
  149. package/src/elements/store/resource.ts +189 -680
  150. package/src/elements/store/rls-row-passes-policies.parity.test.ts +665 -0
  151. package/src/elements/store/schema-decl.ts +539 -41
  152. package/src/elements/store/sql-rls-stamp.test.ts +27 -0
  153. package/src/elements/store/sql-session.ts +297 -35
  154. package/src/elements/store/table.ts +102 -21
  155. package/src/elements/store.test.ts +3 -1
  156. package/src/elements/store.ts +12 -1
  157. package/src/elements/vault/chaos-child.ts +74 -1
  158. package/src/elements/vault/chaos.test.ts +4 -2
  159. package/src/elements/vault/storage.ts +4 -2
  160. package/src/index.ts +5 -2
  161. package/src/kernel/app-auth.ts +1 -0
  162. package/src/kernel/app.ts +116 -2
  163. package/src/kernel/auth-sharing.test.ts +196 -0
  164. package/src/kernel/boot.test.ts +3 -3
  165. package/src/kernel/errors.ts +8 -0
  166. package/src/kernel/fx.test.ts +1 -0
  167. package/src/kernel/fx.ts +14 -2
  168. package/src/kernel/horizontal-child.ts +2 -1
  169. package/src/kernel/http-resource.ts +33 -7
  170. package/src/kernel/identity-host-persist.test.ts +119 -0
  171. package/src/kernel/instance-id.ts +4 -2
  172. package/src/kernel/journal.ts +2 -1
  173. package/src/kernel/mcp-tool.test.ts +95 -0
  174. package/src/kernel/on.ts +9 -0
  175. package/src/kernel/realtime-bind.ts +326 -0
  176. package/src/kernel/resource-live.ts +117 -0
  177. package/src/kernel/triggers.ts +86 -4
  178. package/src/manifest/diff.ts +37 -0
  179. package/src/manifest/types.ts +64 -2
  180. package/src/okid.bench.test.ts +64 -0
  181. package/src/okid.test.ts +338 -0
  182. package/src/okid.ts +245 -0
  183. package/src/plugins/anonymous.ts +19 -1
  184. package/src/plugins/auth/shared.ts +15 -0
  185. package/src/plugins/index.ts +2 -0
  186. package/src/plugins/magic-link.ts +10 -8
  187. package/src/plugins/mcp-oauth.ts +208 -0
  188. package/src/plugins/oauth/flow-store.ts +117 -0
  189. package/src/plugins/oauth/link.ts +69 -0
  190. package/src/plugins/oauth/shared.ts +108 -0
  191. package/src/plugins/oauth/token-vault.ts +100 -0
  192. package/src/plugins/oauth.security.test.ts +535 -0
  193. package/src/plugins/oauth.ts +532 -0
  194. package/src/plugins/otp.ts +48 -6
  195. package/src/plugins/passkey.ts +20 -1
  196. package/src/plugins/two-factor.ts +11 -0
  197. package/src/plugins/username.ts +40 -7
  198. package/src/release/build-lib.ts +7 -1
  199. package/src/release/measure.ts +1 -0
  200. package/src/release/official-plugins.ts +4 -1
  201. package/src/runs/collect.ts +2 -1
  202. package/src/runs/drivers/files.ts +2 -1
  203. package/src/test/create-test-app.ts +114 -5
  204. package/src/test/export-bundle.test.ts +33 -0
  205. package/src/test/live-signals.test.ts +83 -0
  206. package/src/test/tenant-isolation.test.ts +175 -0
  207. package/src/testing.ts +26 -0
  208. package/src/upgrade/codemods.ts +1 -1
  209. package/site/content/docs/reference/migrating-environments.mdx +0 -158
@@ -7,14 +7,106 @@
7
7
 
8
8
  import type { ColumnClassification } from "../../manifest/types.ts";
9
9
  import { lazyRequire } from "../../kernel/lazy-require.ts";
10
+ import type { PolicyGateDecl } from "../gate/declare.ts";
10
11
  import type { ColumnDef, TableHandle } from "./table.ts";
11
- import { id as idHelper, now as nowHelper } from "./table.ts";
12
+ import {
13
+ id as idHelper,
14
+ now as nowHelper,
15
+ nowIso as nowIsoHelper,
16
+ nowDate as nowDateHelper,
17
+ } from "./table.ts";
12
18
 
13
- /** SQL type primitives supported in v1. */
14
- export type FieldSqlType = "text" | "integer";
19
+ /**
20
+ * SQL type primitives the full Drizzle Postgres column-type surface.
21
+ * Factory names mirror `drizzle-orm/pg-core` exactly.
22
+ */
23
+ export type FieldSqlType =
24
+ | "text"
25
+ | "varchar"
26
+ | "char"
27
+ | "boolean"
28
+ | "smallint"
29
+ | "integer"
30
+ | "bigint"
31
+ | "serial"
32
+ | "smallserial"
33
+ | "bigserial"
34
+ | "numeric"
35
+ | "real"
36
+ | "doublePrecision"
37
+ | "json"
38
+ | "jsonb"
39
+ | "uuid"
40
+ | "time"
41
+ | "timestamp"
42
+ | "date"
43
+ | "interval"
44
+ | "point"
45
+ | "line"
46
+ | "bytea"
47
+ | "inet"
48
+ | "cidr"
49
+ | "macaddr"
50
+ | "macaddr8";
51
+
52
+ /** Timestamp / time precision — mirrors drizzle-orm rc.5 `Precision` union. */
53
+ export type FieldPrecision = 0 | 1 | 2 | 3 | 4 | 5 | 6;
54
+
55
+ /** JS mapping for temporal (`timestamp` / `date`) columns. */
56
+ export type TemporalMode = "date" | "string";
57
+
58
+ /** JS mapping for `bigint` columns. */
59
+ export type BigIntMode = "number" | "bigint" | "string";
60
+
61
+ /** JS mapping for `numeric` columns. */
62
+ export type NumericMode = "string" | "number" | "bigint";
63
+
64
+ /** Tuple vs object mapping for geometric `point` columns. */
65
+ export type PointMode = "tuple" | "xy";
66
+
67
+ /** Tuple vs equation-object mapping for geometric `line` columns. */
68
+ export type LineMode = "tuple" | "abc";
69
+
70
+ /** Postgres `interval` field subset — mirrors drizzle-orm rc.5 `IntervalConfig["fields"]`. */
71
+ export type IntervalField =
72
+ | "year"
73
+ | "month"
74
+ | "day"
75
+ | "hour"
76
+ | "minute"
77
+ | "second"
78
+ | "year to month"
79
+ | "day to hour"
80
+ | "day to minute"
81
+ | "day to second"
82
+ | "hour to minute"
83
+ | "hour to second"
84
+ | "minute to second";
85
+
86
+ /**
87
+ * Per-type options recorded on a column (`length`, `precision`, `scale`,
88
+ * `withTimezone`, `mode`, `fields`, `enumValues`). Only the keys relevant to
89
+ * {@link SchemaColumnDecl.sqlType} are populated.
90
+ */
91
+ export interface FieldTypeOptions {
92
+ /** `varchar` / `char` max length. */
93
+ readonly length?: number;
94
+ /** `time` / `timestamp` fractional-second digits (0–6). */
95
+ readonly precision?: number;
96
+ /** `numeric(p, s)` scale digits. */
97
+ readonly scale?: number;
98
+ /** `time` / `timestamp` timezone flavor. */
99
+ readonly withTimezone?: boolean;
100
+ /** JS mapping selector (`TemporalMode` / `BigIntMode` / `NumericMode` / `PointMode` / `LineMode`). */
101
+ readonly mode?: string;
102
+ /** `interval` field qualifier. */
103
+ readonly fields?: IntervalField;
104
+ /** Allowed string values (`text` / `varchar` / `char` enum). */
105
+ readonly enumValues?: readonly string[];
106
+ }
15
107
 
16
108
  /** Known `$defaultFn` helpers recognized by the Drizzle emitter. */
17
- export type DefaultFnKind = "id" | "now" | "custom";
109
+ export type DefaultFnKind = "id" | "now" | "nowIso" | "nowDate" | "custom";
18
110
 
19
111
  /** FK action — mirrors Drizzle `UpdateDeleteAction`. */
20
112
  export type ReferenceAction = "cascade" | "restrict" | "no action" | "set null" | "set default";
@@ -62,7 +154,7 @@ export interface SchemaColumnDecl<
62
154
  readonly $infer?: TData;
63
155
  readonly unique: boolean;
64
156
  /** Literal `.default(v)` when set. */
65
- readonly defaultValue?: string | number | boolean | null;
157
+ readonly defaultValue?: string | number | boolean | Date | Buffer | null;
66
158
  /** Runtime default applied on insert when the key is missing. */
67
159
  readonly defaultFn?: () => unknown;
68
160
  /** Emitter hint for `$defaultFn(id|now)`. */
@@ -71,6 +163,8 @@ export interface SchemaColumnDecl<
71
163
  readonly tableName?: string;
72
164
  /** Foreign key when `.references()` was used. */
73
165
  readonly references?: ColumnReference;
166
+ /** Per-type options (`length`, `precision`, `mode`, `enumValues`, …). */
167
+ readonly typeOptions?: FieldTypeOptions;
74
168
  /** Optional human description for Console / docs (falls back to the JS key). */
75
169
  readonly description?: string;
76
170
  /**
@@ -111,8 +205,22 @@ export interface SchemaTenantScopedDecl {
111
205
  readonly tenantScoped: false;
112
206
  }
113
207
 
208
+ /**
209
+ * Opt out of the project-wide `store.live` default for a schema table when
210
+ * `oke({ store: { live: true } })` is on. Mirrors {@link SchemaTenantScopedDecl}
211
+ * (`unscoped()`) — a per-table escape hatch for a project-level default flip.
212
+ */
213
+ export interface SchemaLiveDecl {
214
+ readonly kind: "schema-live";
215
+ readonly live: false;
216
+ }
217
+
114
218
  /** Third-arg extra for {@link schemaTable}. */
115
- export type SchemaTableExtra = SchemaPolicyDecl | SchemaRlsEnableDecl | SchemaTenantScopedDecl;
219
+ export type SchemaTableExtra =
220
+ | SchemaPolicyDecl
221
+ | SchemaRlsEnableDecl
222
+ | SchemaTenantScopedDecl
223
+ | SchemaLiveDecl;
116
224
 
117
225
  /** Options for {@link schemaPolicy} / Gate helpers. */
118
226
  export interface SchemaPolicyOptions {
@@ -138,6 +246,8 @@ export interface SchemaTableDecl extends TableHandle {
138
246
  readonly policies?: readonly SchemaPolicyDecl[];
139
247
  /** When `false`, skip fail-loud tenant-policy requirement. */
140
248
  readonly tenantScoped?: boolean;
249
+ /** When `false`, opt out of the project-wide `store.live` default. */
250
+ readonly live?: boolean;
141
251
  }
142
252
 
143
253
  /**
@@ -150,8 +260,42 @@ export interface FieldBuilder<TData = unknown, TNotNull extends boolean = false>
150
260
  primaryKey(): FieldBuilder<TData, true>;
151
261
  notNull(): FieldBuilder<TData, true>;
152
262
  unique(): FieldBuilder<TData, TNotNull>;
153
- default(value: string | number | boolean | null): FieldBuilder<TData, TNotNull>;
154
- defaultFn(fn: () => unknown): FieldBuilder<TData, TNotNull>;
263
+ default(value: TData | null): FieldBuilder<TData, TNotNull>;
264
+ defaultFn(fn: () => TData): FieldBuilder<TData, TNotNull>;
265
+ /**
266
+ * Prepared default — a fresh OKID string on every insert. Sugar for
267
+ * `defaultFn(okid)`; the emitter compiles it to Drizzle `$defaultFn(id)`.
268
+ *
269
+ * Only string-typed columns expose it (a non-string builder types the
270
+ * member as `undefined`, so misuse fails TypeScript with a "possibly
271
+ * undefined" error).
272
+ *
273
+ * ```ts
274
+ * id: field.text().primaryKey().okid(),
275
+ * ```
276
+ */
277
+ okid: TData extends string ? () => FieldBuilder<TData, TNotNull> : undefined;
278
+ /**
279
+ * Prepared default — the current instant, shaped to this column.
280
+ *
281
+ * Sugar for `defaultFn(now | nowIso | nowDate)`; the emitter compiles it to
282
+ * Drizzle `$defaultFn(...)`. The resolved helper follows the column's SQL
283
+ * type and temporal mode:
284
+ *
285
+ * - number columns → epoch-ms (`now`)
286
+ * - `timestamp` / `date` (default `{ mode: "date" }`) → `Date` (`nowDate`)
287
+ * - `timestamp` / `date` with `{ mode: "string" }` → ISO-8601 string
288
+ * (`nowIso`)
289
+ *
290
+ * Non-temporal string columns also resolve to an ISO string.
291
+ *
292
+ * ```ts
293
+ * createdAt: field.integer().notNull().now(), // epoch-ms number
294
+ * bornAt: field.timestamp().notNull().now(), // Date object
295
+ * seenAt: field.timestamp({ mode: "string" }).now(), // ISO string
296
+ * ```
297
+ */
298
+ now: TData extends number | Date | string ? () => FieldBuilder<TData, TNotNull> : undefined;
155
299
  pii(): FieldBuilder<TData, TNotNull>;
156
300
  sensitive(): FieldBuilder<TData, TNotNull>;
157
301
  retain(duration: string): FieldBuilder<TData, TNotNull>;
@@ -159,6 +303,14 @@ export interface FieldBuilder<TData = unknown, TNotNull extends boolean = false>
159
303
  as(sqlName: string): FieldBuilder<TData, TNotNull>;
160
304
  /** Optional human description for Console / docs (falls back to the JS key). */
161
305
  describe(description: string): FieldBuilder<TData, TNotNull>;
306
+ /**
307
+ * Override the inferred JS value type (mirrors Drizzle `$type<T>()`).
308
+ *
309
+ * ```ts
310
+ * field.uuid().type<UserId & { __brand: "user_id" }>()
311
+ * ```
312
+ */
313
+ type<T>(): FieldBuilder<T, TNotNull>;
162
314
  /**
163
315
  * Declare a foreign key to another column (dialect-agnostic).
164
316
  *
@@ -182,13 +334,14 @@ interface FieldState {
182
334
  readonly primaryKey: boolean;
183
335
  readonly notNull: boolean;
184
336
  readonly unique: boolean;
185
- readonly defaultValue?: string | number | boolean | null;
337
+ readonly defaultValue?: string | number | boolean | Date | Buffer | null;
186
338
  readonly defaultFn?: () => unknown;
187
339
  readonly defaultFnKind?: DefaultFnKind;
188
340
  readonly classification?: ColumnClassification;
189
341
  readonly sqlName?: string;
190
342
  readonly description?: string;
191
343
  readonly references?: ColumnReference;
344
+ readonly typeOptions?: FieldTypeOptions;
192
345
  }
193
346
 
194
347
  function camelToSnake(key: string): string {
@@ -201,9 +354,14 @@ function camelToSnake(key: string): string {
201
354
  function defaultFnKindOf(fn: () => unknown): DefaultFnKind {
202
355
  if (fn === idHelper) return "id";
203
356
  if (fn === nowHelper) return "now";
357
+ if (fn === nowIsoHelper) return "nowIso";
358
+ if (fn === nowDateHelper) return "nowDate";
204
359
  const name = typeof fn.name === "string" ? fn.name : "";
205
360
  if (name === "id") return "id";
361
+ if (name === "okid") return "id";
206
362
  if (name === "now") return "now";
363
+ if (name === "nowIso") return "nowIso";
364
+ if (name === "nowDate") return "nowDate";
207
365
  return "custom";
208
366
  }
209
367
 
@@ -224,12 +382,42 @@ function createBuilder<TData, TNotNull extends boolean>(
224
382
  primaryKey: () => next<true>({ primaryKey: true, notNull: true }),
225
383
  notNull: () => next<true>({ notNull: true }),
226
384
  unique: () => next<TNotNull>({ unique: true }),
227
- default: (value) => next<TNotNull>({ defaultValue: value }),
385
+ default: (value) => next<TNotNull>({ defaultValue: value as FieldState["defaultValue"] }),
228
386
  defaultFn: (fn) =>
229
387
  next<TNotNull>({
230
- defaultFn: fn,
231
- defaultFnKind: defaultFnKindOf(fn),
388
+ defaultFn: fn as () => unknown,
389
+ defaultFnKind: defaultFnKindOf(fn as () => unknown),
232
390
  }),
391
+ okid: (() =>
392
+ next<TNotNull>({
393
+ defaultFn: idHelper,
394
+ defaultFnKind: "id",
395
+ })) as FieldBuilder<TData, TNotNull>["okid"],
396
+ now: (() => {
397
+ const sqlType = state.sqlType;
398
+ const mode = state.typeOptions?.mode;
399
+ // Number-typed columns — epoch-ms default.
400
+ const numericTypes = new Set<FieldSqlType>([
401
+ "smallint",
402
+ "integer",
403
+ "bigint",
404
+ "serial",
405
+ "smallserial",
406
+ "bigserial",
407
+ "numeric",
408
+ "real",
409
+ "doublePrecision",
410
+ ]);
411
+ if (numericTypes.has(sqlType)) {
412
+ return next<TNotNull>({ defaultFn: nowHelper, defaultFnKind: "now" });
413
+ }
414
+ // Date-object temporal columns `{ mode: "date" }`.
415
+ if (mode === "date" && (sqlType === "timestamp" || sqlType === "date")) {
416
+ return next<TNotNull>({ defaultFn: nowDateHelper, defaultFnKind: "nowDate" });
417
+ }
418
+ // Everything else — ISO-8601 string default.
419
+ return next<TNotNull>({ defaultFn: nowIsoHelper, defaultFnKind: "nowIso" });
420
+ }) as FieldBuilder<TData, TNotNull>["now"],
233
421
  pii: () =>
234
422
  next<TNotNull>({ classification: mergeClassification(state.classification, { pii: true }) }),
235
423
  sensitive: () =>
@@ -242,6 +430,7 @@ function createBuilder<TData, TNotNull extends boolean>(
242
430
  }),
243
431
  as: (sqlName) => next<TNotNull>({ sqlName }),
244
432
  describe: (description) => next<TNotNull>({ description }),
433
+ type: () => createBuilder<unknown, TNotNull>(state) as FieldBuilder<never, TNotNull> as never,
245
434
  references: (ref, actions) =>
246
435
  next<TNotNull>({
247
436
  references: actions ? { ref, actions } : { ref },
@@ -262,6 +451,9 @@ function createBuilder<TData, TNotNull extends boolean>(
262
451
  ...(state.classification ? { classification: state.classification } : {}),
263
452
  ...(state.description !== undefined ? { description: state.description } : {}),
264
453
  ...(state.references ? { references: state.references } : {}),
454
+ ...(state.typeOptions !== undefined && Object.keys(state.typeOptions).length > 0
455
+ ? { typeOptions: state.typeOptions }
456
+ : {}),
265
457
  getSQL(): never {
266
458
  throw new Error(
267
459
  `SchemaColumnDecl.getSQL("${sqlName}") is a type bridge — OKE compiles columns directly`,
@@ -273,38 +465,321 @@ function createBuilder<TData, TNotNull extends boolean>(
273
465
  }
274
466
 
275
467
  /**
276
- * Field builders — `field.text()` / `field.integer()`.
468
+ * Field builders — the full Drizzle Postgres column-type surface.
469
+ *
470
+ * Factory names mirror `drizzle-orm/pg-core` exactly (`field.text()` ·
471
+ * `field.timestamp()` · …). Options bags carry the per-type knobs (`length`,
472
+ * `precision`, `mode`, …) and JS inference follows Drizzle's mapping rules —
473
+ * with OKE defaults: temporals infer `Date` unless `{ mode: "string" }`,
474
+ * and the serial family is NOT NULL by SQL physics.
277
475
  */
278
476
  export const field = {
279
- text: (): FieldBuilder<string, false> =>
280
- createBuilder<string, false>({
281
- sqlType: "text",
282
- primaryKey: false,
283
- notNull: false,
284
- unique: false,
285
- }),
286
- integer: (): FieldBuilder<number, false> =>
287
- createBuilder<number, false>({
288
- sqlType: "integer",
289
- primaryKey: false,
290
- notNull: false,
291
- unique: false,
292
- }),
293
- } as const;
477
+ /**
478
+ * Text id column with the default generation id pre-applied.
479
+ *
480
+ * The "default generation id" currently resolves to OKID, so
481
+ * `field.id()` ≡ `field.text().okid()` — it infers `string` and emits a
482
+ * `$defaultFn(id)` to be minted on insert.
483
+ *
484
+ * ```ts
485
+ * id: field.id().primaryKey(),
486
+ * ```
487
+ */
488
+ id: (() => fieldOf("text", { defaultFn: idHelper, defaultFnKind: "id" })) as FieldApi["id"],
489
+
490
+ /**
491
+ * Text id column explicitly generated with OK ID.
492
+ *
493
+ * Unlike {@link field.id} (which follows whatever the default generation id
494
+ * becomes), this pins OK ID. Today that is the same generator.
495
+ *
496
+ * ```ts
497
+ * id: field.okid().primaryKey(),
498
+ * ```
499
+ */
500
+ okid: (() => fieldOf("text", { defaultFn: idHelper, defaultFnKind: "id" })) as FieldApi["okid"],
501
+
502
+ /** Variable-length string; `{ enum: [...] }` narrows to a literal union. */
503
+ text: ((options?: { readonly enum?: readonly string[] }) =>
504
+ fieldOf("text", mergeOptions({ enumValues: options?.enum }))) as FieldApi["text"],
505
+
506
+ /** Bounded string; `{ length }` emits `varchar(n)`, `{ enum }` narrows literals. */
507
+ varchar: ((options?: { readonly length?: number; readonly enum?: readonly string[] }) =>
508
+ fieldOf(
509
+ "varchar",
510
+ mergeOptions({ length: options?.length }, { enumValues: options?.enum }),
511
+ )) as FieldApi["varchar"],
512
+
513
+ /** Fixed-length blank-padded string; same knobs as {@link field.varchar}. */
514
+ char: ((options?: { readonly length?: number; readonly enum?: readonly string[] }) =>
515
+ fieldOf(
516
+ "char",
517
+ mergeOptions({ length: options?.length }, { enumValues: options?.enum }),
518
+ )) as FieldApi["char"],
519
+
520
+ boolean: (() => fieldOf("boolean")) as FieldApi["boolean"],
521
+ smallint: (() => fieldOf("smallint")) as FieldApi["smallint"],
522
+ integer: (() => fieldOf("integer")) as FieldApi["integer"],
523
+
524
+ /** `bigint` — defaults to JS `number`; rc.5 requires an explicit mode upstream, ours absorbs it. */
525
+ bigint: ((options?: { readonly mode?: "number" | "bigint" | "string" }) =>
526
+ fieldOf("bigint", mergeOptions({ mode: options?.mode ?? "number" }))) as FieldApi["bigint"],
527
+
528
+ /** Auto-incrementing int4 — NOT NULL by SQL physics. */
529
+ serial: (() => notNullFieldOf("serial")) as FieldApi["serial"],
530
+ /** Auto-incrementing int2 — NOT NULL by SQL physics. */
531
+ smallserial: (() => notNullFieldOf("smallserial")) as FieldApi["smallserial"],
532
+
533
+ /** Auto-incrementing int8 — NOT NULL; defaults to JS `number`. */
534
+ bigserial: ((options?: { readonly mode?: "number" | "bigint" }) =>
535
+ notNullFieldOf(
536
+ "bigserial",
537
+ mergeOptions({ mode: options?.mode ?? "number" }),
538
+ )) as FieldApi["bigserial"],
539
+
540
+ /**
541
+ * Exact-decimal `numeric(p, s)` — infers `string` by default (Drizzle
542
+ * physics: avoids float error); opt into `number` / `bigint` via `mode`.
543
+ */
544
+ numeric: ((options?: {
545
+ readonly precision?: number;
546
+ readonly scale?: number;
547
+ readonly mode?: "string" | "number" | "bigint";
548
+ }) =>
549
+ fieldOf(
550
+ "numeric",
551
+ mergeOptions(
552
+ { precision: options?.precision },
553
+ { scale: options?.scale },
554
+ { mode: options?.mode },
555
+ ),
556
+ )) as FieldApi["numeric"],
557
+
558
+ /** Alias of {@link field.numeric} — mirrors drizzle-orm (`decimal === numeric`). */
559
+ decimal: undefined as unknown as FieldApi["decimal"],
560
+
561
+ /** Single-precision float4 (~6 significant digits). */
562
+ real: (() => fieldOf("real")) as FieldApi["real"],
563
+ /** Double-precision float8 (~15 significant digits). */
564
+ doublePrecision: (() => fieldOf("doublePrecision")) as FieldApi["doublePrecision"],
565
+
566
+ /** Textual JSON — narrow the payload with `field.json<MyShape>()`. */
567
+ json: (<T = unknown>() => fieldOf<T>("json")) as FieldApi["json"],
568
+ /** Binary JSON (decomposed, indexable) — narrow with `field.jsonb<MyShape>()`. */
569
+ jsonb: (<T = unknown>() => fieldOf<T>("jsonb")) as FieldApi["jsonb"],
570
+
571
+ uuid: (() => fieldOf("uuid")) as FieldApi["uuid"],
572
+
573
+ /** Time of day — always infers `string` (Drizzle `'string time'`). */
574
+ time: ((options?: { readonly precision?: FieldPrecision; readonly withTimezone?: boolean }) =>
575
+ fieldOf(
576
+ "time",
577
+ mergeOptions({ precision: options?.precision }, { withTimezone: options?.withTimezone }),
578
+ )) as FieldApi["time"],
579
+
580
+ /**
581
+ * Date + time — infers `Date` objects by default; `{ mode: "string" }`
582
+ * opts into ISO strings. OKE pins the mode explicitly when emitting
583
+ * Drizzle schemas.
584
+ */
585
+ timestamp: ((options?: {
586
+ readonly mode?: TemporalMode;
587
+ readonly precision?: FieldPrecision;
588
+ readonly withTimezone?: boolean;
589
+ }) =>
590
+ fieldOf(
591
+ "timestamp",
592
+ mergeOptions(
593
+ { mode: options?.mode ?? "date" },
594
+ { precision: options?.precision },
595
+ { withTimezone: options?.withTimezone },
596
+ ),
597
+ )) as FieldApi["timestamp"],
598
+
599
+ /** Calendar date — infers `Date` by default; `{ mode: "string" }` opts into ISO `YYYY-MM-DD`. */
600
+ date: ((options?: { readonly mode?: TemporalMode }) =>
601
+ fieldOf("date", mergeOptions({ mode: options?.mode ?? "date" }))) as FieldApi["date"],
602
+
603
+ /** Time span — infers `string`. */
604
+ interval: ((options?: { readonly fields?: IntervalField; readonly precision?: number }) =>
605
+ fieldOf(
606
+ "interval",
607
+ mergeOptions({ fields: options?.fields }, { precision: options?.precision }),
608
+ )) as FieldApi["interval"],
609
+
610
+ /** Geometric point — `[number, number]` tuple by default; `{ mode: "xy" }` for `{ x, y }`. */
611
+ point: ((options?: { readonly mode?: PointMode }) =>
612
+ fieldOf("point", mergeOptions({ mode: options?.mode ?? "tuple" }))) as FieldApi["point"],
613
+
614
+ /** Geometric line — `[n, n, n]` tuple by default; `{ mode: "abc" }` for the equation object. */
615
+ line: ((options?: { readonly mode?: LineMode }) =>
616
+ fieldOf("line", mergeOptions({ mode: options?.mode ?? "tuple" }))) as FieldApi["line"],
617
+
618
+ /** Binary data — infers `Buffer` (a `Uint8Array`). */
619
+ bytea: (() => fieldOf("bytea")) as FieldApi["bytea"],
620
+
621
+ inet: (() => fieldOf("inet")) as FieldApi["inet"],
622
+ cidr: (() => fieldOf("cidr")) as FieldApi["cidr"],
623
+ macaddr: (() => fieldOf("macaddr")) as FieldApi["macaddr"],
624
+ macaddr8: (() => fieldOf("macaddr8")) as FieldApi["macaddr8"],
625
+ } as FieldApi;
626
+
627
+ // `decimal` is the exact alias drizzle-orm ships (`export const decimal = numeric`)
628
+ // — same runtime, same overloads. Assigned post-literal because the literal
629
+ // references `field.numeric` before `field` is initialized.
630
+ (field.decimal as FieldApi["numeric"]) = field.numeric;
631
+
632
+ /** Precise public signatures for {@link field} (overloads + inference). */
633
+ interface FieldApi {
634
+ /** Text id column with the default generation id (currently OK ID) pre-applied. */
635
+ readonly id: () => FieldBuilder<string, false>;
636
+ /** Text id column explicitly generated with OK ID. */
637
+ readonly okid: () => FieldBuilder<string, false>;
638
+ readonly text: {
639
+ (): FieldBuilder<string, false>;
640
+ <const E extends readonly [string, ...string[]]>(options: {
641
+ readonly enum: E;
642
+ }): FieldBuilder<E[number], false>;
643
+ };
644
+ readonly varchar: {
645
+ (): FieldBuilder<string, false>;
646
+ (options: { readonly length: number }): FieldBuilder<string, false>;
647
+ <const E extends readonly [string, ...string[]]>(options: {
648
+ readonly length?: number;
649
+ readonly enum: E;
650
+ }): FieldBuilder<E[number], false>;
651
+ };
652
+ readonly char: FieldApi["varchar"];
653
+ readonly boolean: () => FieldBuilder<boolean, false>;
654
+ readonly smallint: () => FieldBuilder<number, false>;
655
+ readonly integer: () => FieldBuilder<number, false>;
656
+ readonly bigint: {
657
+ (): FieldBuilder<number, false>;
658
+ (options: { readonly mode: "bigint" }): FieldBuilder<bigint, false>;
659
+ (options: { readonly mode: "string" }): FieldBuilder<string, false>;
660
+ };
661
+ readonly serial: () => FieldBuilder<number, true>;
662
+ readonly smallserial: () => FieldBuilder<number, true>;
663
+ readonly bigserial: {
664
+ (): FieldBuilder<number, true>;
665
+ (options: { readonly mode: "bigint" }): FieldBuilder<bigint, true>;
666
+ };
667
+ readonly numeric: {
668
+ (): FieldBuilder<string, false>;
669
+ (options: {
670
+ readonly precision?: number;
671
+ readonly scale?: number;
672
+ readonly mode?: "string";
673
+ }): FieldBuilder<string, false>;
674
+ (options: {
675
+ readonly precision?: number;
676
+ readonly scale?: number;
677
+ readonly mode: "number";
678
+ }): FieldBuilder<number, false>;
679
+ (options: {
680
+ readonly precision?: number;
681
+ readonly scale?: number;
682
+ readonly mode: "bigint";
683
+ }): FieldBuilder<bigint, false>;
684
+ };
685
+ readonly decimal: FieldApi["numeric"];
686
+ readonly real: () => FieldBuilder<number, false>;
687
+ readonly doublePrecision: () => FieldBuilder<number, false>;
688
+ readonly json: <T = unknown>() => FieldBuilder<T, false>;
689
+ readonly jsonb: <T = unknown>() => FieldBuilder<T, false>;
690
+ readonly uuid: () => FieldBuilder<string, false>;
691
+ readonly time: (options?: {
692
+ readonly precision?: FieldPrecision;
693
+ readonly withTimezone?: boolean;
694
+ }) => FieldBuilder<string, false>;
695
+ readonly timestamp: {
696
+ (options?: {
697
+ readonly mode?: "date";
698
+ readonly precision?: FieldPrecision;
699
+ readonly withTimezone?: boolean;
700
+ }): FieldBuilder<Date, false>;
701
+ (options: {
702
+ readonly mode: "string";
703
+ readonly precision?: FieldPrecision;
704
+ readonly withTimezone?: boolean;
705
+ }): FieldBuilder<string, false>;
706
+ };
707
+ readonly date: {
708
+ (options?: { readonly mode?: "date" }): FieldBuilder<Date, false>;
709
+ (options: { readonly mode: "string" }): FieldBuilder<string, false>;
710
+ };
711
+ readonly interval: (options?: {
712
+ readonly fields?: IntervalField;
713
+ readonly precision?: number;
714
+ }) => FieldBuilder<string, false>;
715
+ readonly point: {
716
+ (options?: { readonly mode?: "tuple" }): FieldBuilder<[number, number], false>;
717
+ (options: { readonly mode: "xy" }): FieldBuilder<{ x: number; y: number }, false>;
718
+ };
719
+ readonly line: {
720
+ (options?: { readonly mode?: "tuple" }): FieldBuilder<[number, number, number], false>;
721
+ (options: { readonly mode: "abc" }): FieldBuilder<{ a: number; b: number; c: number }, false>;
722
+ };
723
+ readonly bytea: () => FieldBuilder<Buffer, false>;
724
+ readonly inet: () => FieldBuilder<string, false>;
725
+ readonly cidr: () => FieldBuilder<string, false>;
726
+ readonly macaddr: () => FieldBuilder<string, false>;
727
+ readonly macaddr8: () => FieldBuilder<string, false>;
728
+ }
729
+
730
+ /**
731
+ * Merge defined per-type option entries into a {@link Partial<FieldState>}
732
+ * patch — empty when nothing was set.
733
+ */
734
+ function mergeOptions(
735
+ ...parts: readonly (Partial<FieldTypeOptions> | undefined)[]
736
+ ): Partial<FieldState> {
737
+ const merged: Record<string, unknown> = {};
738
+ for (const part of parts) {
739
+ if (!part) continue;
740
+ for (const [key, value] of Object.entries(part)) {
741
+ if (value !== undefined) merged[key] = value;
742
+ }
743
+ }
744
+ return Object.keys(merged).length > 0 ? { typeOptions: merged as FieldTypeOptions } : {};
745
+ }
746
+
747
+ function fieldOf<TData>(
748
+ sqlType: FieldSqlType,
749
+ extra: Partial<FieldState> = {},
750
+ ): FieldBuilder<TData, false> {
751
+ return createBuilder<TData, false>({
752
+ sqlType,
753
+ primaryKey: false,
754
+ notNull: false,
755
+ unique: false,
756
+ ...extra,
757
+ });
758
+ }
759
+
760
+ function notNullFieldOf<TData>(
761
+ sqlType: FieldSqlType,
762
+ extra: Partial<FieldState> = {},
763
+ ): FieldBuilder<TData, true> {
764
+ return createBuilder<TData, true>({
765
+ sqlType,
766
+ primaryKey: false,
767
+ notNull: true,
768
+ unique: false,
769
+ ...extra,
770
+ });
771
+ }
294
772
 
295
773
  /**
296
774
  * Column map input for {@link schemaTable}.
297
775
  *
298
- * Explicit `string` / `number` × nullability variants so `field.text()`
299
- * (`FieldBuilder<string, false>`) remains assignable methodful builders
300
- * are invariant in their type parameters.
776
+ * Structural over enumerated: methodful builders are invariant in their type
777
+ * parameters, so an explicit `string | number × false | true` union would
778
+ * reject the widened `field.*` surface (`Date`, `bigint`, tuples, generics).
779
+ * `FieldBuilder<unknown, boolean>` accepts every builder via covariance of
780
+ * the phantom `$infer` property.
301
781
  */
302
- export type SchemaColumnInput =
303
- | FieldBuilder<string, false>
304
- | FieldBuilder<string, true>
305
- | FieldBuilder<number, false>
306
- | FieldBuilder<number, true>
307
- | SchemaColumnDecl;
782
+ export type SchemaColumnInput = FieldBuilder<unknown, boolean> | SchemaColumnDecl;
308
783
 
309
784
  /**
310
785
  * Whether a value is a finalized {@link SchemaColumnDecl}.
@@ -436,6 +911,7 @@ export function schemaTable<C extends Record<string, SchemaColumnInput>>(
436
911
  const unscoped = extras.some(
437
912
  (extra) => extra.kind === "schema-tenant-scoped" && extra.tenantScoped === false,
438
913
  );
914
+ const live = extras.some((extra) => extra.kind === "schema-live") ? false : undefined;
439
915
  const table = {
440
916
  name,
441
917
  columns: stamped,
@@ -443,6 +919,7 @@ export function schemaTable<C extends Record<string, SchemaColumnInput>>(
443
919
  ...(rls ? { rls: true } : {}),
444
920
  ...(policies.length > 0 ? { policies } : {}),
445
921
  ...(unscoped ? { tenantScoped: false } : {}),
922
+ ...(live !== undefined ? { live: false } : {}),
446
923
  };
447
924
  // Survive columns named `name` or `kind` (they would otherwise shadow
448
925
  // the table discriminant / SQL name).
@@ -458,6 +935,22 @@ export function schemaRls(): SchemaRlsEnableDecl {
458
935
  return { kind: "schema-rls" };
459
936
  }
460
937
 
938
+ /**
939
+ * Opt a table out of the project-wide live-by-default posture
940
+ * (`oke({ store: { live: true } })`). The project flag makes every NEW
941
+ * `store.schema.table` declaration live by default; pass
942
+ * `store.schema.live(false)` in the table's extras array to declare a
943
+ * specific table as NOT live. No-op when the project flag is off (that
944
+ * path is already explicit-only).
945
+ *
946
+ * Mirrors `store.schema.unscoped()`, the per-table opt-out for the
947
+ * `gate.auth.tenant` default flip. Changes declaration defaulting only —
948
+ * never the underlying CDC / RLS-per-event cost model.
949
+ */
950
+ export function schemaLive(live: false): SchemaLiveDecl {
951
+ return { kind: "schema-live", live };
952
+ }
953
+
461
954
  /**
462
955
  * Raw Drizzle-shaped policy extra.
463
956
  *
@@ -546,18 +1039,22 @@ export function schemaPolicyOwner(
546
1039
  /**
547
1040
  * Scope policy — `oke.has_scope('booking:create')`.
548
1041
  *
549
- * @param scope - Scope string
1042
+ * Accepts a bare scope string or a `gate.scope(...)` declaration; the decl
1043
+ * form keeps the scope string single-sourced (TS catches dangling gate refs).
1044
+ *
1045
+ * @param scope - Scope string or `PolicyGateDecl` from `gate.scope(...)`
550
1046
  * @param options - Command (default `insert`)
551
1047
  */
552
1048
  export function schemaPolicyScope(
553
- scope: string,
1049
+ scope: string | PolicyGateDecl,
554
1050
  options: Pick<SchemaPolicyOptions, "for" | "as" | "to"> = {},
555
1051
  ): SchemaPolicyDecl {
1052
+ const resolved = typeof scope === "string" ? scope : (scope.scopes?.[0] ?? scope.name);
556
1053
  const command = options.for ?? "insert";
557
- return schemaPolicy(helperPolicyName("scope", scope, command), {
1054
+ return schemaPolicy(helperPolicyName("scope", resolved, command), {
558
1055
  ...options,
559
1056
  for: command,
560
- ...policyPredicates(command, `oke.has_scope(${sqlStringLiteral(scope)})`),
1057
+ ...policyPredicates(command, `oke.has_scope(${sqlStringLiteral(resolved)})`),
561
1058
  });
562
1059
  }
563
1060
 
@@ -806,6 +1303,7 @@ export const schema = {
806
1303
  rls: schemaRls,
807
1304
  policy: schemaPolicyApi,
808
1305
  unscoped: schemaUnscoped,
1306
+ live: schemaLive,
809
1307
  } as const;
810
1308
 
811
1309
  /**