okmodel 0.1.1 → 0.2.1

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 (181) hide show
  1. package/README.md +32 -16
  2. package/dist/adapters/capabilities.d.ts +34 -0
  3. package/dist/adapters/pg/bunsql-extra.d.ts +17 -0
  4. package/dist/adapters/pg/bunsql.d.ts +60 -0
  5. package/dist/adapters/pg/call.d.ts +2 -12
  6. package/dist/adapters/pg/nodepostgres-extra.d.ts +18 -0
  7. package/dist/adapters/pg/nodepostgres.d.ts +57 -0
  8. package/dist/adapters/pg/postgres-extra.d.ts +50 -1
  9. package/dist/adapters/pg/postgresjs.d.ts +0 -4
  10. package/dist/adapters/pg/session.d.ts +94 -0
  11. package/dist/adapters/pg/watch.d.ts +18 -0
  12. package/dist/contracts/catalog/object.d.ts +1 -0
  13. package/dist/contracts/catalog/types.d.ts +15 -8
  14. package/dist/contracts/connection.d.ts +13 -0
  15. package/dist/contracts/driver.d.ts +18 -5
  16. package/dist/contracts/error.d.ts +17 -1
  17. package/dist/contracts/generator.d.ts +63 -0
  18. package/dist/contracts/index.d.ts +2 -2
  19. package/dist/contracts/index.js +1 -1
  20. package/dist/contracts/internal.js +11 -9
  21. package/dist/contracts/location.d.ts +24 -0
  22. package/dist/contracts/rows.d.ts +15 -0
  23. package/dist/dialects/pg/archive-bind.d.ts +22 -0
  24. package/dist/dialects/pg/column.d.ts +126 -9
  25. package/dist/dialects/pg/compile.d.ts +1 -0
  26. package/dist/dialects/pg/custom.d.ts +6 -0
  27. package/dist/dialects/pg/filter-parse.d.ts +19 -0
  28. package/dist/dialects/pg/filters.d.ts +19 -0
  29. package/dist/dialects/pg/index.d.ts +11 -2
  30. package/dist/dialects/pg/index.js +159 -1063
  31. package/dist/dialects/pg/keys.d.ts +19 -3
  32. package/dist/dialects/pg/model.d.ts +223 -2
  33. package/dist/dialects/pg/operator-fit.d.ts +86 -0
  34. package/dist/dialects/pg/operators.d.ts +39 -1
  35. package/dist/dialects/pg/ops/arr-append.d.ts +9 -0
  36. package/dist/dialects/pg/ops/arr-ns.d.ts +7 -0
  37. package/dist/dialects/pg/ops/arr-remove.d.ts +9 -0
  38. package/dist/dialects/pg/ops/containedBy.d.ts +9 -0
  39. package/dist/dialects/pg/ops/contains.d.ts +8 -4
  40. package/dist/dialects/pg/ops/hasAnyKey.d.ts +8 -0
  41. package/dist/dialects/pg/ops/hasKey.d.ts +8 -0
  42. package/dist/dialects/pg/ops/json-ns.d.ts +6 -0
  43. package/dist/dialects/pg/ops/json-set.d.ts +12 -0
  44. package/dist/dialects/pg/ops/matches.d.ts +16 -0
  45. package/dist/dialects/pg/ops/overlaps.d.ts +9 -0
  46. package/dist/dialects/pg/ops/path.d.ts +13 -0
  47. package/dist/dialects/pg/preset.d.ts +76 -0
  48. package/dist/dialects/pg/relations.d.ts +59 -2
  49. package/dist/dialects/pg/schema.d.ts +109 -5
  50. package/dist/dialects/pg/table.d.ts +130 -11
  51. package/dist/dialects/pg/temporal.d.ts +9 -7
  52. package/dist/dialects/pg/tenancy.d.ts +94 -0
  53. package/dist/dialects/pg/through.d.ts +21 -0
  54. package/dist/dialects/pg/trait.d.ts +141 -0
  55. package/dist/dialects/pg/where.d.ts +37 -0
  56. package/dist/runtime/aggregate.d.ts +25 -0
  57. package/dist/runtime/archive-handle.d.ts +18 -0
  58. package/dist/runtime/archive-rules.d.ts +12 -0
  59. package/dist/runtime/archive.d.ts +73 -0
  60. package/dist/runtime/batch.d.ts +20 -0
  61. package/dist/runtime/client.d.ts +114 -5
  62. package/dist/runtime/conflict.d.ts +2 -1
  63. package/dist/runtime/exposure.d.ts +27 -0
  64. package/dist/runtime/hookm.d.ts +39 -0
  65. package/dist/runtime/ids/index.d.ts +11 -0
  66. package/dist/runtime/ids/index.js +193 -0
  67. package/dist/runtime/ids/okid-extended.d.ts +19 -0
  68. package/dist/runtime/ids/okid-generator.d.ts +21 -0
  69. package/dist/runtime/ids/okid-shared.d.ts +89 -0
  70. package/dist/runtime/ids/okid.d.ts +24 -0
  71. package/dist/runtime/ids/uuid.d.ts +21 -0
  72. package/dist/runtime/lock.d.ts +22 -0
  73. package/dist/runtime/operator-sql.d.ts +8 -0
  74. package/dist/runtime/page.d.ts +25 -0
  75. package/dist/runtime/pg/bun.d.ts +30 -0
  76. package/dist/runtime/pg/bun.js +52 -0
  77. package/dist/runtime/pg/pg.d.ts +29 -0
  78. package/dist/runtime/pg/pg.js +52 -0
  79. package/dist/runtime/pg/pglite.js +16 -17
  80. package/dist/runtime/pg/postgresjs.js +14 -24
  81. package/dist/runtime/plan.d.ts +146 -7
  82. package/dist/runtime/preset-stack.d.ts +17 -0
  83. package/dist/runtime/presets.d.ts +48 -0
  84. package/dist/runtime/read-stream.d.ts +26 -0
  85. package/dist/runtime/safety/index.d.ts +159 -0
  86. package/dist/runtime/safety/index.js +294 -0
  87. package/dist/runtime/safety-hook.d.ts +42 -0
  88. package/dist/runtime/server-check.d.ts +34 -0
  89. package/dist/runtime/tenancy/index.d.ts +37 -0
  90. package/dist/runtime/tenancy/index.js +365 -0
  91. package/dist/runtime/trait-read.d.ts +30 -0
  92. package/dist/runtime/traits/index.d.ts +128 -0
  93. package/dist/runtime/traits/index.js +509 -0
  94. package/dist/runtime/transaction.d.ts +19 -0
  95. package/dist/runtime/tx.d.ts +22 -7
  96. package/dist/runtime/types.d.ts +376 -27
  97. package/dist/runtime/validate/bind.d.ts +11 -0
  98. package/dist/runtime/validate/closed.d.ts +16 -0
  99. package/dist/runtime/validate/engine.d.ts +41 -0
  100. package/dist/runtime/validate/hook.d.ts +17 -0
  101. package/dist/runtime/validate/index.d.ts +21 -0
  102. package/dist/runtime/validate/index.js +175 -0
  103. package/dist/runtime/validate/load.d.ts +13 -0
  104. package/dist/runtime/validate/places.d.ts +41 -0
  105. package/dist/runtime/validate/rules.d.ts +108 -0
  106. package/dist/runtime/validate/surface.d.ts +102 -0
  107. package/dist/runtime/write.d.ts +30 -5
  108. package/dist/shared/{fgqgsb5z.js → 0qkxbm1f.js} +2 -2
  109. package/dist/shared/0v1g424p.js +26 -0
  110. package/dist/shared/10tjenmd.js +210 -0
  111. package/dist/shared/{xqbhg6cn.js → 1dp5knt7.js} +95 -10
  112. package/dist/shared/38crxbgn.js +229 -0
  113. package/dist/shared/3a5ne750.js +158 -0
  114. package/dist/shared/4b9da9d7.js +138 -0
  115. package/dist/shared/4e4c6vq2.js +62 -0
  116. package/dist/shared/{x5x59srr.js → 4jzzghr6.js} +416 -83
  117. package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
  118. package/dist/shared/{05w44d8g.js → 5z4q2zda.js} +567 -18
  119. package/dist/shared/672t5vym.js +12 -0
  120. package/dist/shared/7m88x3e9.js +45 -0
  121. package/dist/shared/7mht49yd.js +1091 -0
  122. package/dist/shared/{mtkwv8wx.js → 7p2wbz7f.js} +1 -1
  123. package/dist/shared/83eaefwr.js +10 -0
  124. package/dist/shared/96ps4g1z.js +32 -0
  125. package/dist/shared/97g2jnrw.js +76 -0
  126. package/dist/shared/{xy79j5hq.js → 9k6vetvf.js} +25 -36
  127. package/dist/shared/{w8samvk1.js → a1gdp7j9.js} +1 -1
  128. package/dist/shared/a5sxz52y.js +13 -0
  129. package/dist/shared/{dynq16vb.js → b066g80h.js} +8 -6
  130. package/dist/shared/cstgkzdx.js +154 -0
  131. package/dist/shared/{523ft3e8.js → d04j53v8.js} +6 -5
  132. package/dist/shared/{y9m15dfv.js → d2whjz4k.js} +15 -9
  133. package/dist/shared/dd4f8mnx.js +307 -0
  134. package/dist/shared/e6wg00wd.js +164 -0
  135. package/dist/shared/e7a268fy.js +195 -0
  136. package/dist/shared/{kprcwaha.js → eaxwrgby.js} +4 -3
  137. package/dist/shared/{dkn3y60a.js → ecdzej21.js} +76 -112
  138. package/dist/shared/{hadrfb9h.js → edwqqryd.js} +17 -14
  139. package/dist/shared/{m3ypagh5.js → eqmtn1bj.js} +57 -5
  140. package/dist/shared/g1b5zzj9.js +75 -0
  141. package/dist/shared/g9f0zqce.js +376 -0
  142. package/dist/shared/ge03bfca.js +64 -0
  143. package/dist/shared/{yd1kzwn2.js → jns2wx4t.js} +55 -12
  144. package/dist/shared/k3ted12y.js +33 -0
  145. package/dist/shared/k7dwkhvq.js +31 -0
  146. package/dist/shared/{hd293j9g.js → kyxb62mc.js} +14 -2
  147. package/dist/shared/m9tv7xn7.js +404 -0
  148. package/dist/shared/mrq77sgw.js +55 -0
  149. package/dist/shared/mxmcdsh5.js +450 -0
  150. package/dist/shared/n442h54c.js +14 -0
  151. package/dist/shared/n47rd821.js +442 -0
  152. package/dist/shared/n4jqhpqz.js +27 -0
  153. package/dist/shared/{mkjmqjv4.js → nfzc5z0w.js} +2 -104
  154. package/dist/shared/nsva2a6b.js +42 -0
  155. package/dist/shared/ps43134h.js +69 -0
  156. package/dist/shared/{8b04d28c.js → py3nh7jy.js} +2 -2
  157. package/dist/shared/qa5dsc7z.js +86 -0
  158. package/dist/shared/{g5fhqmwe.js → qxa0yb9j.js} +6 -4
  159. package/dist/shared/r6nwkxnt.js +86 -0
  160. package/dist/shared/{jjb7yht9.js → rtnaqre0.js} +3 -3
  161. package/dist/shared/rwmrejjz.js +26 -0
  162. package/dist/shared/{zhc5vmgf.js → s6gqfn5j.js} +2 -2
  163. package/dist/shared/sacqha1y.js +70 -0
  164. package/dist/shared/{qk3hxfja.js → sfh4pk75.js} +5 -5
  165. package/dist/shared/{j5cn2963.js → t4tbsy9h.js} +13 -64
  166. package/dist/shared/{ngk8gj61.js → tbszx8mz.js} +15 -1
  167. package/dist/shared/v18jva5b.js +90 -0
  168. package/dist/shared/x9v1qgeb.js +201 -0
  169. package/dist/shared/xatfg0gn.js +193 -0
  170. package/dist/shared/xkz8g6by.js +225 -0
  171. package/dist/shared/{rgs0st28.js → xrfnq5k8.js} +237 -434
  172. package/dist/shared/ytg3xf9p.js +149 -0
  173. package/dist/shared/yw5c610b.js +48 -0
  174. package/dist/shared/zd93g3rq.js +31 -0
  175. package/dist/shared/zt8y0wzb.js +132 -0
  176. package/dist/shared/{p0cbr3g2.js → zwmqxb0g.js} +1 -1
  177. package/dist/tooling/cli.js +14 -10
  178. package/dist/tooling/migrate/commands.d.ts +10 -0
  179. package/dist/tooling/migrate/engine.d.ts +36 -0
  180. package/package.json +41 -4
  181. package/dist/shared/gk7cs7sp.js +0 -566
@@ -1,9 +1,11 @@
1
1
  /**
2
- * `one` and `many` relations.
2
+ * `one`, `many`, and `manyThrough` relations.
3
3
  *
4
4
  * They name a table. `schema()` resolves the foreign key. An ambiguous key
5
- * is OKM1021. `manyThrough` arrives in 0.2. `morph` stays reserved.
5
+ * is OKM1021. `manyThrough` names the join table as well and carries its own
6
+ * resolver, so a schema without one does not pay for it. `morph` stays reserved.
6
7
  */
8
+ import type { RelationModel } from "./model.js";
7
9
  /** A to-one relation. `field` is the local foreign key when the table has several. */
8
10
  export type OneRelation<TTable extends string = string> = {
9
11
  readonly kind: "one";
@@ -16,6 +18,46 @@ export type ManyRelation<TTable extends string = string> = {
16
18
  readonly table: TTable;
17
19
  readonly field: string | undefined;
18
20
  };
21
+ /** A foreign key `schema()` resolved, as a relation resolver sees it. */
22
+ export type RelationEdge = {
23
+ readonly fromTable: string;
24
+ readonly fromField: string;
25
+ readonly toTable: string;
26
+ /** Local SQL names, one column or a composite key. */
27
+ readonly local: readonly string[];
28
+ /** Referenced SQL names, in the same order. */
29
+ readonly remote: readonly string[];
30
+ };
31
+ /** What a relation's own resolver reads from `schema()`. */
32
+ export type RelationScope = {
33
+ /** The table that declares the relation. */
34
+ readonly owner: string;
35
+ /** The relation name. */
36
+ readonly name: string;
37
+ readonly edges: readonly RelationEdge[];
38
+ /** Every table name in the schema, sorted. */
39
+ readonly tables: readonly string[];
40
+ };
41
+ /**
42
+ * A to-many relation through a join table.
43
+ *
44
+ * It is a {@link ManyRelation} to the types, so `include`, `has`, `none`, and
45
+ * `every` read it as one. `resolve` is what `schema()` calls in place of the
46
+ * foreign-key search that `many` uses.
47
+ */
48
+ export type ManyThroughRelation<TTable extends string = string> = ManyRelation<TTable> & {
49
+ /** The join table. */
50
+ readonly through: string;
51
+ /** Turns this declaration into a resolved model. */
52
+ readonly resolve: (scope: RelationScope) => RelationModel;
53
+ };
54
+ /** Join-table foreign keys, named only when more than one points at a table. */
55
+ export type ThroughKeys = {
56
+ /** The join table field that references the declaring table. */
57
+ readonly from?: string;
58
+ /** The join table field that references the related table. */
59
+ readonly to?: string;
60
+ };
19
61
  /** A relation `table()` stores. */
20
62
  export type RelationCall = OneRelation | ManyRelation;
21
63
  /**
@@ -43,3 +85,18 @@ export declare function many<const TTable extends string>(table: TTable, field?:
43
85
  * @returns `true` for `one` and `many`
44
86
  */
45
87
  export declare function isRelationCall(value: unknown): value is RelationCall;
88
+ /**
89
+ * Declares a to-many relation through a join table.
90
+ *
91
+ * The join table has one foreign key to the declaring table and one to the
92
+ * related table. Name `from` and `to` when a table has more than one. Tenancy
93
+ * and archivable apply to the join rows and to the related rows alike.
94
+ *
95
+ * @typeParam TTable - Related table
96
+ * @param table - Related table name
97
+ * @param options - The join table, and its foreign-key fields when ambiguous
98
+ * @returns The relation `schema()` resolves
99
+ */
100
+ export declare function manyThrough<const TTable extends string>(table: TTable, options: {
101
+ readonly through: string;
102
+ } & ThroughKeys): ManyThroughRelation<TTable>;
@@ -4,8 +4,11 @@
4
4
  * Work happens here, not in `table()` and not at import.
5
5
  */
6
6
  import type { Catalog } from "../../contracts/catalog/types.js";
7
- import { type TableModel } from "./model.js";
8
- import { type AnyTable } from "./table.js";
7
+ import { type ClientGenerator } from "../../contracts/generator.js";
8
+ import { type SchemaHook, type TableModel } from "./model.js";
9
+ import type { FieldsOfList, HasArchive, PresetsOfList, Trait } from "./trait.js";
10
+ import { type ColumnTenancy, type TenantFields } from "./tenancy.js";
11
+ import { type AnyTable, type InsertFrom, type RowFrom, type UpdateFrom } from "./table.js";
9
12
  /** Codec defaults from spec section 6.6. */
10
13
  export type SchemaCodecs = {
11
14
  readonly bigint: "string" | "number" | "bigint";
@@ -33,8 +36,28 @@ export type SchemaInput<TTables extends readonly AnyTable[]> = {
33
36
  readonly timestamps?: SchemaCodecs["timestamps"];
34
37
  };
35
38
  readonly types?: "emitted" | "inferred";
36
- readonly traits?: unknown;
37
- readonly tenancy?: unknown;
39
+ /**
40
+ * What a bare `t.id()` means.
41
+ *
42
+ * `"uuidv7"` and `"uuidv4"` are database defaults. A client generator is
43
+ * filled on insert and stays out of the catalog. A column option wins.
44
+ */
45
+ readonly defaults?: {
46
+ readonly id?: "uuidv7" | "uuidv4" | ClientGenerator<string>;
47
+ };
48
+ /**
49
+ * Traits applied to every table.
50
+ *
51
+ * A table opts out with `omitDefaults` and a reason. A table's own `traits`
52
+ * are added as well.
53
+ */
54
+ readonly traits?: readonly Trait[];
55
+ /**
56
+ * Column tenancy.
57
+ *
58
+ * Every table gains the key unless it passes `global("reason")`.
59
+ */
60
+ readonly tenancy?: ColumnTenancy;
38
61
  readonly validation?: unknown;
39
62
  readonly extensions?: unknown;
40
63
  readonly functions?: unknown;
@@ -59,11 +82,68 @@ export type BuiltSchema<TTables extends readonly AnyTable[]> = {
59
82
  readonly codecs: SchemaCodecs;
60
83
  readonly requires: SchemaRequires | undefined;
61
84
  readonly tables: TTables;
85
+ /**
86
+ * Schema traits, when the call passed a non-empty list.
87
+ *
88
+ * Omitted otherwise, so a schema that uses no traits does not carry the key.
89
+ */
90
+ readonly traits?: readonly Trait[];
91
+ /**
92
+ * Column tenancy, when the call passed it.
93
+ *
94
+ * Omitted otherwise, so a schema with no tenancy does not carry the key.
95
+ */
96
+ readonly tenancy?: ColumnTenancy;
62
97
  /** Query model. Built once, beside the catalog document. */
63
98
  readonly model: {
64
99
  readonly [T in TTables[number] as T["~name"]]: TableModel;
65
100
  };
66
101
  };
102
+ /**
103
+ * A schema whose tenant tables carry the tenant key on the row.
104
+ *
105
+ * A table marked `~global` keeps its own type. No tenancy leaves the schema
106
+ * type unchanged.
107
+ *
108
+ * @typeParam S - Schema type before tenancy
109
+ * @typeParam TTables - Tables in declaration order
110
+ * @typeParam TTenancy - Column tenancy, or `undefined`
111
+ */
112
+ export type TenancySchema<S, TTables extends readonly AnyTable[], TTenancy> = TTenancy extends ColumnTenancy ? Omit<S, "~byName"> & {
113
+ readonly tenancy: TTenancy;
114
+ readonly "~byName": {
115
+ readonly [T in TTables[number] as T["~name"]]: T extends {
116
+ readonly "~global": string;
117
+ } ? T : T & {
118
+ readonly "~row": RowFrom<TenantFields<TTenancy["key"]>>;
119
+ readonly "~insert": InsertFrom<TenantFields<TTenancy["key"]>>;
120
+ readonly "~update": UpdateFrom<TenantFields<TTenancy["key"]>>;
121
+ };
122
+ };
123
+ } : S;
124
+ /**
125
+ * A schema whose default traits are part of every table's row type.
126
+ *
127
+ * A table with `omitDefaults` keeps its own type. An empty trait list does not
128
+ * change the schema type.
129
+ *
130
+ * @typeParam TTables - Tables in declaration order
131
+ * @typeParam TTraits - Schema default traits
132
+ */
133
+ export type SchemaWithTraits<TTables extends readonly AnyTable[], TTraits extends readonly {
134
+ readonly fields: Readonly<Record<string, object>>;
135
+ }[]> = [keyof FieldsOfList<TTraits> | keyof PresetsOfList<TTraits>] extends [never] ? BuiltSchema<TTables> : Omit<BuiltSchema<TTables>, "~byName"> & {
136
+ readonly "~byName": {
137
+ readonly [T in TTables[number] as T["~name"]]: ApplySchemaTraits<T, FieldsOfList<TTraits>, TTraits>;
138
+ };
139
+ };
140
+ type ApplySchemaTraits<TTable, TFields, TTraits> = TTable extends {
141
+ readonly "~omitDefaults": true;
142
+ } ? TTable : TTable & {
143
+ readonly "~row": RowFrom<TFields>;
144
+ readonly "~insert": InsertFrom<TFields>;
145
+ readonly "~update": UpdateFrom<TFields>;
146
+ } & HasArchive<TTraits> & PresetsOfList<TTraits>;
67
147
  /**
68
148
  * Compiles tables into a catalog.
69
149
  *
@@ -74,4 +154,28 @@ export type BuiltSchema<TTables extends readonly AnyTable[]> = {
74
154
  * @param config - Tables, casing, codecs, and the row-type mode
75
155
  * @returns The schema and its catalog
76
156
  */
77
- export declare function schema<const TTables extends readonly AnyTable[]>(config: SchemaInput<TTables>): BuiltSchema<TTables>;
157
+ export declare function schema<const TTables extends readonly AnyTable[], const TTraits extends readonly {
158
+ readonly fields: Readonly<Record<string, object>>;
159
+ }[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined>(config: Omit<SchemaInput<TTables>, "traits" | "tenancy" | "validation"> & {
160
+ readonly traits: TTraits;
161
+ readonly tenancy?: TTenancy;
162
+ readonly validation?: TValidation;
163
+ }): [TValidation] extends [undefined] ? TenancySchema<SchemaWithTraits<TTables, TTraits>, TTables, TTenancy> : TenancySchema<SchemaWithTraits<TTables, TTraits>, TTables, TTenancy> & {
164
+ readonly "~validation": TValidation;
165
+ };
166
+ export declare function schema<const TTables extends readonly AnyTable[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined>(config: Omit<SchemaInput<TTables>, "tenancy" | "validation"> & {
167
+ readonly tenancy?: TTenancy;
168
+ readonly validation?: TValidation;
169
+ }): [TValidation] extends [undefined] ? TenancySchema<BuiltSchema<TTables>, TTables, TTenancy> : TenancySchema<BuiltSchema<TTables>, TTables, TTenancy> & {
170
+ readonly "~validation": TValidation;
171
+ };
172
+ /**
173
+ * Registers a hook from an opt-in module.
174
+ *
175
+ * Core does not import that module. A featureless app never calls this, so
176
+ * the module that loads the validation engine stays out of its graph.
177
+ *
178
+ * @param hook - Called for the client and for each table
179
+ */
180
+ export declare function addSchemaHook(hook: SchemaHook): void;
181
+ export {};
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import { type ColumnInsertOf, type ColumnRowOf, type ColumnUpdateOf } from "./column.js";
8
8
  import { type RelationCall } from "./relations.js";
9
+ import type { PresetMap } from "./preset.js";
10
+ import { type FieldsOfList, type HasArchive, type PresetsOfList, type Trait } from "./trait.js";
9
11
  /**
10
12
  * A column as seen by `indexes` and `checks`.
11
13
  *
@@ -20,6 +22,12 @@ export type ColumnHandle = {
20
22
  export type IndexCall = {
21
23
  readonly columns: readonly string[];
22
24
  readonly isUnique: boolean;
25
+ /**
26
+ * Partial-index predicate, stored as the catalog wrote it.
27
+ *
28
+ * Absent on an ordinary index.
29
+ */
30
+ readonly predicate?: string;
23
31
  /**
24
32
  * Marks the index unique.
25
33
  *
@@ -52,16 +60,50 @@ export type TableOptions<TColumns> = {
52
60
  readonly unique?: {
53
61
  readonly [name: string]: readonly (keyof TColumns & string)[];
54
62
  };
63
+ /**
64
+ * Composite primary key, in column order.
65
+ *
66
+ * A single-column key can use this or `.primaryKey()` on the column.
67
+ * One table has one primary key.
68
+ */
69
+ readonly primaryKey?: readonly (keyof TColumns & string)[];
55
70
  readonly sqlName?: string;
56
71
  readonly renamedFrom?: string;
57
72
  readonly comment?: string;
58
73
  readonly validate?: unknown;
59
74
  readonly validation?: unknown;
60
- readonly traits?: unknown;
61
- readonly omitDefaults?: unknown;
62
- readonly tenancy?: unknown;
75
+ /**
76
+ * Traits added to this table.
77
+ *
78
+ * Schema default traits are added as well, unless `omitDefaults` is set.
79
+ */
80
+ readonly traits?: readonly Trait[];
81
+ /**
82
+ * Skip schema default traits.
83
+ *
84
+ * The value is the reason. An empty reason is rejected when the schema is built.
85
+ */
86
+ readonly omitDefaults?: string;
87
+ /**
88
+ * Opts this table out of schema tenancy.
89
+ *
90
+ * `global("reason")` is the opt-out. `{ via }` arrives later.
91
+ */
92
+ readonly tenancy?: {
93
+ readonly kind: "global";
94
+ readonly reason: string;
95
+ } | {
96
+ readonly via: string;
97
+ };
63
98
  readonly computed?: unknown;
64
- readonly presets?: unknown;
99
+ /**
100
+ * Named, typed refinements called as `tasks.pending()`.
101
+ *
102
+ * A preset gets a builder and its own arguments and returns the builder.
103
+ * The builder only adds predicates. A name that is a client method or is
104
+ * reserved is OKM1040.
105
+ */
106
+ readonly presets?: PresetMap<RowFrom<TColumns>>;
65
107
  readonly policies?: unknown;
66
108
  readonly reference?: unknown;
67
109
  /** `one` and `many`. Other relation kinds stay reserved. */
@@ -85,16 +127,39 @@ export type Handles<TColumns> = {
85
127
  * @typeParam TColumns - Column builders
86
128
  * @typeParam TRelations - Relations declared on this table
87
129
  */
88
- export type Table<TName extends string, TColumns, TRelations extends Readonly<Record<string, RelationCall>> = Readonly<Record<string, never>>> = {
130
+ export type Table<TName extends string, TColumns, TRelations extends Readonly<Record<string, RelationCall>> = Readonly<Record<string, never>>, TKey extends string = never, TOptions = object> = {
89
131
  readonly "~name": TName;
90
132
  readonly "~columns": TColumns;
91
133
  readonly "~row": RowFrom<TColumns>;
92
134
  readonly "~insert": InsertFrom<TColumns>;
93
- readonly "~update": UpdateFrom<TColumns>;
135
+ readonly "~update": UpdateFrom<TColumns, TKey>;
94
136
  readonly "~relations": TRelations;
95
137
  readonly name: TName;
96
138
  readonly columns: TColumns;
97
- readonly options?: TableOptions<TColumns>;
139
+ readonly options?: TOptions;
140
+ /**
141
+ * Allowlisted client filters.
142
+ *
143
+ * A hidden field in `allow`, `sort`, or `relations` throws OKM1123 here.
144
+ * `parse` loads the parser on first use.
145
+ *
146
+ * @param spec - Fields, operators, and sorts the caller may send
147
+ * @returns A parser for one request object
148
+ */
149
+ filters(spec: FilterSpec): {
150
+ parse(query: unknown): Promise<ParsedFilters>;
151
+ };
152
+ };
153
+ /** What {@link Table.filters} accepts. */
154
+ export type FilterSpec = {
155
+ readonly allow?: Readonly<Record<string, readonly string[]>>;
156
+ readonly sort?: readonly string[];
157
+ readonly relations?: Readonly<Record<string, readonly string[]>>;
158
+ };
159
+ /** `where` and `orderBy` a caller can spread into `find`. */
160
+ export type ParsedFilters = {
161
+ readonly where: Readonly<Record<string, unknown>>;
162
+ readonly orderBy?: Readonly<Record<string, "asc" | "desc">>;
98
163
  };
99
164
  /**
100
165
  * Any table `schema()` can compile.
@@ -108,6 +173,8 @@ export type AnyTable = {
108
173
  readonly name: string;
109
174
  readonly columns: Readonly<Record<string, object>>;
110
175
  readonly options?: object;
176
+ /** `file.ts:line` of the `table()` call. Not part of the catalog hash. */
177
+ readonly source?: string;
111
178
  };
112
179
  /**
113
180
  * Row shape. Hidden columns are omitted.
@@ -130,8 +197,8 @@ export type InsertFrom<TColumns> = {
130
197
  *
131
198
  * @typeParam TColumns - Column builders
132
199
  */
133
- export type UpdateFrom<TColumns> = {
134
- readonly [K in keyof TColumns as ColumnUpdateOf<TColumns[K]> extends never ? never : K]: ColumnUpdateOf<TColumns[K]>;
200
+ export type UpdateFrom<TColumns, TKey extends string = never> = {
201
+ readonly [K in keyof TColumns as K extends TKey ? never : ColumnUpdateOf<TColumns[K]> extends never ? never : K]: ColumnUpdateOf<TColumns[K]>;
135
202
  };
136
203
  /**
137
204
  * Declares a table.
@@ -145,8 +212,8 @@ export type UpdateFrom<TColumns> = {
145
212
  * @param options - Indexes, checks, unique constraints, and names
146
213
  * @returns The table
147
214
  */
148
- export declare function table<const TName extends string, const TColumns extends Readonly<Record<string, object>>>(name: TName, columns: TColumns): Table<TName, TColumns, Readonly<Record<string, never>>>;
149
- export declare function table<const TName extends string, const TColumns extends Readonly<Record<string, object>>, const TOptions extends TableOptions<TColumns>>(name: TName, columns: TColumns, options: TOptions): Table<TName, TColumns, RelationsOf<TOptions>>;
215
+ export declare function table<const TName extends string, const TColumns extends Readonly<Record<string, object>>>(name: TName, columns: TColumns): Table<TName, TColumns, Readonly<Record<string, never>>, never>;
216
+ export declare function table<const TName extends string, const TColumns extends Readonly<Record<string, object>>, const TOptions extends TableOptions<TColumns>>(name: TName, columns: TColumns, options: TOptions): DeclaredTable<TName, TColumns, TOptions>;
150
217
  /**
151
218
  * Relation map stored on a table.
152
219
  *
@@ -158,6 +225,58 @@ export declare function table<const TName extends string, const TColumns extends
158
225
  type RelationsOf<TOptions> = TOptions extends {
159
226
  readonly relations: infer R;
160
227
  } ? R extends Readonly<Record<string, RelationCall>> ? R : Readonly<Record<string, never>> : Readonly<Record<string, never>>;
228
+ /**
229
+ * Column names listed in a table's `primaryKey` option.
230
+ *
231
+ * Absent when the option is omitted, so a table without it does not change
232
+ * its update shape.
233
+ *
234
+ * @typeParam TOptions - Options argument
235
+ */
236
+ type PrimaryOf<TOptions> = TOptions extends {
237
+ readonly primaryKey: infer K;
238
+ } ? K extends readonly (infer F)[] ? Extract<F, string> : never : never;
239
+ /**
240
+ * Table type callers see, including trait fields and an opt-out of schema traits.
241
+ *
242
+ * @typeParam TName - Table name
243
+ * @typeParam TColumns - Column builders
244
+ * @typeParam TOptions - Options argument
245
+ */
246
+ type DeclaredTable<TName extends string, TColumns, TOptions> = Table<TName, TColumns, RelationsOf<TOptions>, PrimaryOf<TOptions>, NoInfer<TOptions>> & TraitShapes<TColumns, TOptions> & ArchiveFlag<TOptions> & PresetFlag<TOptions> & OmitFlag<TOptions> & GlobalFlag<TOptions>;
247
+ type ArchiveFlag<TOptions> = TOptions extends {
248
+ readonly traits: infer TTraits;
249
+ } ? HasArchive<TTraits> : unknown;
250
+ /** `{ "~presets" }` from the table's own `presets` and its traits' presets, when there are any. */
251
+ type PresetFlag<TOptions> = (TOptions extends {
252
+ readonly presets: infer TPresets;
253
+ } ? {
254
+ readonly "~presets": TPresets;
255
+ } : unknown) & (TOptions extends {
256
+ readonly traits: infer TTraits;
257
+ } ? PresetsOfList<TTraits> : unknown);
258
+ type OmitFlag<TOptions> = TOptions extends {
259
+ readonly omitDefaults: string;
260
+ } ? {
261
+ readonly "~omitDefaults": true;
262
+ } : unknown;
263
+ type GlobalFlag<TOptions> = TOptions extends {
264
+ readonly tenancy: {
265
+ readonly kind: "global";
266
+ readonly reason: infer TReason;
267
+ };
268
+ } ? {
269
+ readonly "~global": TReason;
270
+ } : unknown;
271
+ type TraitShapes<TColumns, TOptions> = TOptions extends {
272
+ readonly traits: infer TTraits;
273
+ } ? TTraits extends readonly {
274
+ readonly fields: Readonly<Record<string, object>>;
275
+ }[] ? keyof FieldsOfList<TTraits> extends never ? unknown : {
276
+ readonly "~row": RowFrom<TColumns> & RowFrom<FieldsOfList<TTraits>>;
277
+ readonly "~insert": InsertFrom<TColumns> & InsertFrom<FieldsOfList<TTraits>>;
278
+ readonly "~update": UpdateFrom<TColumns, PrimaryOf<TOptions>> & UpdateFrom<FieldsOfList<TTraits>>;
279
+ } : unknown : unknown;
161
280
  /**
162
281
  * Declares an index from column handles.
163
282
  *
@@ -86,21 +86,23 @@ export declare function encodeTimeZone(value: TimeWithOffset, precision?: TimePr
86
86
  /**
87
87
  * Decodes a time with offset.
88
88
  *
89
- * @param wire - Time text plus an offset
90
- * @returns Time and offset
89
+ * @param wire - Time text plus an offset, `±HH` as Postgres sends it or `±HH:MM`
90
+ * @returns Time and offset, the offset as `±HH:MM`
91
91
  */
92
92
  export declare function decodeTimeZone(wire: string): TimeWithOffset;
93
93
  /**
94
94
  * Encodes a duration as an ISO-8601 duration.
95
95
  *
96
96
  * @param value - Duration
97
- * @returns ISO-8601 text Postgres accepts for interval
97
+ * @returns ISO-8601 text Postgres accepts for interval, with a sign on each field when negative
98
98
  */
99
99
  export declare function encodeDuration(value: Temporal.Duration): string;
100
100
  /**
101
- * Decodes an ISO-8601 duration.
101
+ * Decodes an interval as Postgres sends it: `1 year 2 mons 3 days 04:05:06.5`,
102
+ * `01:30:00`, or an ISO-8601 duration. A part may carry a sign. Parts of
103
+ * different signs (`1 day -01:00:00`) cannot be one `Temporal.Duration`.
102
104
  *
103
- * @param wire - Duration text
105
+ * @param wire - Interval text
104
106
  * @returns A duration
105
107
  */
106
108
  export declare function decodeDuration(wire: string): Temporal.Duration;
@@ -202,10 +204,10 @@ declare global {
202
204
  };
203
205
  readonly Duration: {
204
206
  /**
205
- * @param item - ISO-8601 duration
207
+ * @param item - ISO-8601 duration, or numeric fields
206
208
  * @returns A duration
207
209
  */
208
- from(item: string): Temporal.Duration;
210
+ from(item: string | Readonly<Record<string, number>>): Temporal.Duration;
209
211
  };
210
212
  };
211
213
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The tenancy object `schema()` calls once.
3
+ *
4
+ * The methods live on the value `columnTenancy()` returns from `okmodel/tenancy`.
5
+ * This file only names them. An application that never imports that subpath
6
+ * does not load them.
7
+ */
8
+ import type { ColumnBuilder, FlagTrue, PlainFlags } from "./column.js";
9
+ import type { SchemaHookCtx } from "./model.js";
10
+ import type { AnyTable } from "./table.js";
11
+ /**
12
+ * A guarded uuid column, one per tenant table.
13
+ *
14
+ * Insert and update omit it. The client fills it from the scope.
15
+ */
16
+ export type TenantFields<Key extends string> = {
17
+ readonly [K in Key]: ColumnBuilder<string, FlagTrue<PlainFlags, "guarded">>;
18
+ };
19
+ /** One tenant value, or a reason for leaving the scope. The value is not a cache key. */
20
+ export type TenantCall = {
21
+ readonly value: string;
22
+ } | {
23
+ readonly unscoped: string;
24
+ };
25
+ /** Text the scope predicate writes. The planner's sink has these methods. */
26
+ export type TenancyText = {
27
+ text(value: string): void;
28
+ param(encoded: string): void;
29
+ mark(token: string): void;
30
+ };
31
+ /** `for()` and `unscoped()` plus the table names the root client keeps. */
32
+ export type TenancyClient = {
33
+ readonly names: readonly string[];
34
+ readonly for?: (input: unknown) => unknown;
35
+ readonly unscoped?: (reason: string) => unknown;
36
+ };
37
+ /** One inspect rule the tenancy object recorded. */
38
+ export type TenancyRule = {
39
+ readonly rule: string;
40
+ readonly contribution: string;
41
+ readonly provenance: string;
42
+ readonly source?: string;
43
+ };
44
+ /**
45
+ * Column tenancy passed to `schema({ tenancy })`.
46
+ *
47
+ * `columnTenancy()` from `okmodel/tenancy` builds it. `rewrite` runs once,
48
+ * before compile. The other methods are the planner and the client.
49
+ */
50
+ export type ColumnTenancy = {
51
+ readonly key: string;
52
+ readonly type: "uuid";
53
+ readonly strategy: "column";
54
+ /** Tables with the tenant column, widened uniques, and composite references. */
55
+ rewrite(tables: readonly AnyTable[]): readonly AnyTable[];
56
+ /** The guarded uuid column, for emitted row types. */
57
+ column(): object;
58
+ predicate(input: {
59
+ readonly table: string;
60
+ readonly fieldSql: (field: string) => string | undefined;
61
+ readonly encode: ((value: unknown) => string) | undefined;
62
+ readonly alias: string;
63
+ readonly appended: boolean;
64
+ readonly scope: TenantCall | undefined;
65
+ readonly sink: TenancyText;
66
+ }): boolean;
67
+ guard(table: string, field: string, kind: "where" | "insert" | "update"): void;
68
+ stamp(table: string, rows: Record<string, unknown>[], scope: TenantCall | undefined): void;
69
+ client(input: {
70
+ readonly names: readonly string[];
71
+ readonly scoped: boolean;
72
+ readonly open: (scope: TenantCall) => unknown;
73
+ }): TenancyClient;
74
+ rules(table: string, source: string | undefined, scope: TenantCall | undefined): readonly TenancyRule[];
75
+ /**
76
+ * Attaches `for()` and `unscoped()` on the root client.
77
+ *
78
+ * The same hook archivable uses for its table methods.
79
+ *
80
+ * @param target - The client object
81
+ * @param ctx - Names, scope, and the table map
82
+ */
83
+ hook(target: Record<string, unknown>, ctx: SchemaHookCtx): void;
84
+ missing(table: string): never;
85
+ };
86
+ /**
87
+ * Reads `schema({ tenancy })`.
88
+ *
89
+ * Omitted stays `undefined`. Anything else must be `columnTenancy()`.
90
+ *
91
+ * @param value - The option the caller passed
92
+ * @returns The tenancy object, or `undefined`
93
+ */
94
+ export declare function readTenancy(value: unknown): ColumnTenancy | undefined;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolver and emitter of a `manyThrough` relation.
3
+ *
4
+ * The relation object carries both. `schema()` calls the resolver, and the
5
+ * planner calls the emitter with its own helpers, so a schema that has no
6
+ * `manyThrough` ships none of this. A join row or target in another tenant or
7
+ * archived never shows: the same planner predicates apply to both tables.
8
+ */
9
+ import type { RelationScope } from "./relations.js";
10
+ import type { RelationModel } from "./model.js";
11
+ /**
12
+ * Resolves a `manyThrough` declaration against the schema's foreign keys.
13
+ *
14
+ * @param scope - What `schema()` knows
15
+ * @param table - Related table name
16
+ * @param through - Join table name
17
+ * @param from - Join table field that references the declaring table, when ambiguous
18
+ * @param to - Join table field that references the related table, when ambiguous
19
+ * @returns The resolved relation, carrying its emitter
20
+ */
21
+ export declare function resolveThrough(scope: RelationScope, table: string, through: string, from: string | undefined, to: string | undefined): RelationModel;