effect-qb 4.0.0-beta.66 → 4.0.0-beta.98

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 (129) hide show
  1. package/README.md +5 -2
  2. package/dist/index.js +9376 -0
  3. package/dist/mysql.js +3921 -2573
  4. package/dist/postgres/metadata.js +2491 -1373
  5. package/dist/postgres.js +5453 -5155
  6. package/dist/sqlite.js +6895 -5529
  7. package/dist/standard.js +9330 -0
  8. package/package.json +9 -2
  9. package/src/casing.ts +71 -0
  10. package/src/index.ts +2 -0
  11. package/src/internal/casing.ts +89 -0
  12. package/src/internal/coercion/rules.ts +13 -1
  13. package/src/internal/column-state.ts +21 -15
  14. package/src/internal/column.ts +44 -7
  15. package/src/internal/datatypes/define.ts +7 -1
  16. package/src/internal/datatypes/enrich.ts +23 -0
  17. package/src/internal/datatypes/lookup.ts +81 -25
  18. package/src/internal/datatypes/matrix.ts +903 -0
  19. package/src/internal/datatypes/shape.ts +2 -0
  20. package/src/internal/derived-table.ts +4 -36
  21. package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/mysql.ts} +552 -361
  22. package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +657 -400
  23. package/src/{sqlite/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +505 -347
  24. package/src/internal/dialect.ts +35 -0
  25. package/src/internal/dsl-mutation-runtime.ts +12 -162
  26. package/src/internal/dsl-plan-runtime.ts +10 -138
  27. package/src/internal/dsl-query-runtime.ts +5 -79
  28. package/src/internal/dsl-transaction-ddl-runtime.ts +41 -65
  29. package/src/internal/executor.ts +14 -16
  30. package/src/internal/grouping-key.ts +87 -20
  31. package/src/internal/implication-runtime.ts +1 -1
  32. package/src/internal/json/path-access.ts +351 -0
  33. package/src/internal/predicate/runtime.ts +3 -0
  34. package/src/internal/query.d.ts +38 -11
  35. package/src/internal/query.ts +64 -25
  36. package/src/internal/renderer.ts +26 -14
  37. package/src/internal/runtime/normalize.ts +12 -5
  38. package/src/internal/scalar.ts +7 -1
  39. package/src/internal/schema-derivation.d.ts +7 -7
  40. package/src/internal/schema-derivation.ts +9 -9
  41. package/src/internal/schema-expression.ts +2 -2
  42. package/src/internal/sql-expression-renderer.ts +19 -0
  43. package/src/internal/standard-dsl.ts +6978 -0
  44. package/src/internal/table-options.ts +126 -66
  45. package/src/internal/table.ts +652 -219
  46. package/src/mysql/column-extension.ts +3 -0
  47. package/src/mysql/column.ts +8 -9
  48. package/src/mysql/datatypes/index.ts +4 -2
  49. package/src/mysql/datatypes/spec.ts +6 -176
  50. package/src/mysql/errors/normalize.ts +0 -1
  51. package/src/mysql/executor.ts +7 -7
  52. package/src/mysql/internal/dialect.ts +9 -4
  53. package/src/mysql/internal/dsl.ts +312 -154
  54. package/src/mysql/internal/renderer.ts +6 -2
  55. package/src/mysql/json.ts +7 -2
  56. package/src/mysql/query-extension.ts +16 -0
  57. package/src/mysql/renderer.ts +37 -3
  58. package/src/mysql/type.ts +60 -0
  59. package/src/mysql.ts +7 -13
  60. package/src/postgres/check.ts +1 -0
  61. package/src/postgres/column-extension.ts +28 -0
  62. package/src/postgres/column.ts +2 -8
  63. package/src/postgres/datatypes/index.d.ts +2 -1
  64. package/src/postgres/datatypes/index.ts +4 -2
  65. package/src/postgres/datatypes/spec.ts +6 -260
  66. package/src/postgres/errors/normalize.ts +0 -1
  67. package/src/postgres/executor.ts +7 -7
  68. package/src/postgres/foreign-key.ts +24 -0
  69. package/src/postgres/function/core.ts +1 -3
  70. package/src/postgres/function/index.ts +1 -17
  71. package/src/postgres/index.ts +1 -0
  72. package/src/postgres/internal/dialect.ts +9 -4
  73. package/src/postgres/internal/dsl.ts +306 -163
  74. package/src/postgres/internal/renderer.ts +6 -2
  75. package/src/postgres/internal/schema-ddl.ts +22 -10
  76. package/src/postgres/internal/schema-model.ts +238 -7
  77. package/src/postgres/json-extension.ts +7 -0
  78. package/src/postgres/json.ts +762 -173
  79. package/src/postgres/jsonb.ts +37 -0
  80. package/src/postgres/primary-key.ts +24 -0
  81. package/src/postgres/query-extension.ts +2 -0
  82. package/src/postgres/renderer.ts +37 -3
  83. package/src/postgres/schema-management.ts +12 -11
  84. package/src/postgres/schema.ts +106 -15
  85. package/src/postgres/table.ts +205 -530
  86. package/src/postgres/type.ts +93 -10
  87. package/src/postgres/unique.ts +32 -0
  88. package/src/postgres.ts +20 -16
  89. package/src/sqlite/column-extension.ts +3 -0
  90. package/src/sqlite/column.ts +8 -9
  91. package/src/sqlite/datatypes/index.ts +4 -2
  92. package/src/sqlite/datatypes/spec.ts +6 -94
  93. package/src/sqlite/errors/normalize.ts +0 -1
  94. package/src/sqlite/executor.ts +7 -7
  95. package/src/sqlite/internal/dialect.ts +9 -4
  96. package/src/sqlite/internal/dsl.ts +301 -154
  97. package/src/sqlite/internal/renderer.ts +6 -2
  98. package/src/sqlite/json.ts +8 -2
  99. package/src/sqlite/query-extension.ts +2 -0
  100. package/src/sqlite/renderer.ts +37 -3
  101. package/src/sqlite/type.ts +40 -0
  102. package/src/sqlite.ts +7 -13
  103. package/src/standard/cast.ts +113 -0
  104. package/src/standard/check.ts +17 -0
  105. package/src/standard/column.ts +163 -0
  106. package/src/standard/datatypes/index.ts +83 -0
  107. package/src/standard/datatypes/spec.ts +12 -0
  108. package/src/standard/dialect.ts +40 -0
  109. package/src/standard/foreign-key.ts +37 -0
  110. package/src/standard/function/aggregate.ts +2 -0
  111. package/src/standard/function/core.ts +2 -0
  112. package/src/standard/function/index.ts +18 -0
  113. package/src/standard/function/string.ts +2 -0
  114. package/src/standard/function/temporal.ts +78 -0
  115. package/src/standard/function/window.ts +2 -0
  116. package/src/standard/index.ts +17 -0
  117. package/src/standard/internal/renderer.ts +45 -0
  118. package/src/standard/json.ts +883 -0
  119. package/src/standard/primary-key.ts +17 -0
  120. package/src/standard/query.ts +152 -0
  121. package/src/standard/renderer.ts +49 -0
  122. package/src/standard/table.ts +151 -0
  123. package/src/standard/unique.ts +17 -0
  124. package/src/standard.ts +32 -0
  125. package/src/internal/aggregation-validation.ts +0 -57
  126. package/src/internal/table.d.ts +0 -180
  127. package/src/mysql/table.ts +0 -186
  128. package/src/postgres/cast.ts +0 -45
  129. package/src/sqlite/table.ts +0 -175
@@ -5,16 +5,15 @@ import * as Plan from "./row-set.js"
5
5
  import type { Any as AnyExpression } from "./scalar.js"
6
6
  import type { TrueFormula } from "./predicate/formula.js"
7
7
  import type { BoundColumnFrom } from "./column-state.js"
8
- import { bindColumn, type AnyColumnDefinition } from "./column-state.js"
8
+ import { bindColumn, BoundColumnTypeId, type AnyBoundColumn, type AnyColumnDefinition } from "./column-state.js"
9
9
  import {
10
10
  collectInlineOptions,
11
- normalizeColumnList,
12
11
  resolvePrimaryKeyColumns,
12
+ type ColumnList,
13
13
  type DdlExpressionLike,
14
14
  type IndexKeySpec,
15
- type MatchingColumnArityInput,
16
- type NonEmptyColumnInput,
17
- type NormalizeColumns,
15
+ type LiteralStringInput,
16
+ type NonEmptyStringInput,
18
17
  type ReferentialAction,
19
18
  type TableOptionSpec,
20
19
  type ValidateForeignKeyOptionColumns,
@@ -33,6 +32,7 @@ import {
33
32
  type TableSchemaVariant,
34
33
  type UpdateRow
35
34
  } from "./schema-derivation.js"
35
+ import * as Casing from "./casing.js"
36
36
 
37
37
  /** Symbol used to attach table-definition metadata. */
38
38
  export const TypeId: unique symbol = Symbol.for("effect-qb/Table")
@@ -44,16 +44,60 @@ export const options: unique symbol = Symbol.for("effect-qb/Table/declaredOption
44
44
  const CacheSymbol: unique symbol = Symbol.for("effect-qb/Table/cache")
45
45
  const SchemaCacheSymbol: unique symbol = Symbol.for("effect-qb/Table/schemaCache")
46
46
  const DeclaredOptionsSymbol: unique symbol = Symbol.for("effect-qb/Table/factoryDeclaredOptions")
47
+ const ResolveOptionSymbol: unique symbol = Symbol.for("effect-qb/Table/resolveOption")
47
48
 
48
49
  type InlinePrimaryKeyKeys<Fields extends TableFieldMap> = Extract<{
49
50
  [K in keyof Fields]: Fields[K]["metadata"]["primaryKey"] extends true ? K : never
50
51
  }[keyof Fields], string>
51
52
 
52
- type TableDialect<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
53
+ type FieldDialects<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
54
+ type ConcreteFieldDialects<Fields extends TableFieldMap> = Exclude<FieldDialects<Fields>, "standard">
55
+ type TableDialect<Fields extends TableFieldMap> = [ConcreteFieldDialects<Fields>] extends [never]
56
+ ? "standard"
57
+ : ConcreteFieldDialects<Fields>
53
58
  type TableKind = "schema" | "alias"
54
59
  type DefaultSchemaName = "public"
60
+ type NonEmptyFieldMap<Fields extends TableFieldMap> =
61
+ string extends keyof Fields ? Fields : "" extends keyof Fields ? never : Fields
55
62
  type FieldColumnName<Fields extends TableFieldMap> = Extract<keyof Fields, string>
56
63
  type FieldColumnList<Fields extends TableFieldMap> = readonly [FieldColumnName<Fields>, ...FieldColumnName<Fields>[]]
64
+ export type SchemaTableDefinition = TableDefinition<any, any, any, "schema", any, any>
65
+ type LooseTableSelection = any
66
+ type ConcreteSelector<
67
+ Table extends SchemaTableDefinition,
68
+ Selection extends AnyColumnSelection
69
+ > = SchemaTableDefinition extends Table ? never : (table: Table) => Selection
70
+ type ColumnNameOfBound<Column> = Column extends {
71
+ readonly [BoundColumnTypeId]: {
72
+ readonly columnName: infer ColumnName extends string
73
+ }
74
+ } ? ColumnName : never
75
+ type BoundColumnTupleNames<Columns extends readonly AnyBoundColumn[]> =
76
+ Columns extends readonly [infer Head extends AnyBoundColumn, ...infer Tail extends AnyBoundColumn[]]
77
+ ? readonly [ColumnNameOfBound<Head>, ...BoundColumnTupleNames<Tail>]
78
+ : readonly []
79
+ type ColumnSelectionNames<Selection> =
80
+ Selection extends AnyBoundColumn
81
+ ? readonly [ColumnNameOfBound<Selection>]
82
+ : Selection extends readonly [infer Head extends AnyBoundColumn, ...infer Tail extends AnyBoundColumn[]]
83
+ ? readonly [ColumnNameOfBound<Head>, ...BoundColumnTupleNames<Tail>]
84
+ : never
85
+ export type TableColumnSelection<Table extends SchemaTableDefinition> =
86
+ TableColumn<Table> | readonly [TableColumn<Table>, ...TableColumn<Table>[]]
87
+ export type TableColumn<Table extends SchemaTableDefinition> =
88
+ SchemaTableDefinition extends Table ? AnyBoundColumn : Table extends TableDefinition<infer Name, infer Fields, any, "schema", any>
89
+ ? BoundColumns<Name, Fields>[Extract<keyof Fields, string>]
90
+ : never
91
+ export type AnyColumnSelection = AnyBoundColumn | readonly [AnyBoundColumn, ...AnyBoundColumn[]]
92
+ export type SelectedColumns<Selection extends AnyColumnSelection> = ColumnSelectionNames<Selection>
93
+ type MatchingSelectionArityInput<
94
+ Left extends AnyColumnSelection,
95
+ Right extends AnyColumnSelection
96
+ > = SelectedColumns<Left>["length"] extends SelectedColumns<Right>["length"]
97
+ ? SelectedColumns<Right>["length"] extends SelectedColumns<Left>["length"]
98
+ ? unknown
99
+ : never
100
+ : never
57
101
  type FieldIndexKeySpec<Fields extends TableFieldMap> =
58
102
  | (Extract<IndexKeySpec, { readonly kind: "column" }> & { readonly column: FieldColumnName<Fields> })
59
103
  | Extract<IndexKeySpec, { readonly kind: "expression" }>
@@ -71,53 +115,162 @@ type ClassOptionSpec<Fields extends TableFieldMap = TableFieldMap> =
71
115
  })
72
116
  | Extract<TableOptionSpec, { readonly kind: "check" }>
73
117
  interface TableOptionBuilderLike<
74
- Spec extends TableOptionSpec = TableOptionSpec
118
+ Spec extends TableOptionSpec = TableOptionSpec,
119
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
75
120
  > {
76
121
  readonly option: Spec
77
122
  }
78
123
 
79
- type ClassTableOption<Fields extends TableFieldMap> = TableOptionBuilderLike<ClassOptionSpec<Fields>>
124
+ type ClassTableOption<Fields extends TableFieldMap> = TableOption<
125
+ ClassOptionSpec<Fields>,
126
+ TableDefinition<string, Fields, keyof Fields & string, "schema", any>
127
+ >
80
128
  type ClassDeclaredTableOptions<Fields extends TableFieldMap> = readonly ClassTableOption<Fields>[]
129
+ type TableNameOf<Table extends TableDefinition<any, any, any, "schema", any>> =
130
+ Table extends TableDefinition<infer Name, any, any, "schema", any> ? Name : never
131
+ type TableFieldsOf<Table extends TableDefinition<any, any, any, "schema", any>> =
132
+ Table extends TableDefinition<any, infer Fields, any, "schema", any> ? Fields : never
133
+ type TablePrimaryKeyOf<Table extends TableDefinition<any, any, any, "schema", any>> =
134
+ Table extends TableDefinition<any, any, infer PrimaryKeyColumns, "schema", any> ? PrimaryKeyColumns : never
135
+ type TableSchemaNameOf<Table extends TableDefinition<any, any, any, "schema", any>> =
136
+ Table extends TableDefinition<any, any, any, "schema", infer SchemaName> ? SchemaName : never
137
+ type HasBroadColumns<Columns extends readonly string[]> = string extends Columns[number] ? true : false
138
+
139
+ export type ConflictArbiterScope = "unconditional" | "partial"
140
+ export type ConflictArbiter<
141
+ Columns extends ColumnList = ColumnList,
142
+ Scope extends ConflictArbiterScope = ConflictArbiterScope,
143
+ Name extends string | undefined = string | undefined,
144
+ Constraint extends boolean = boolean
145
+ > = {
146
+ readonly columns: Columns
147
+ readonly scope: Scope
148
+ readonly name?: Name
149
+ readonly constraint: Constraint
150
+ }
151
+
152
+ type InlineConflictArbiters<Fields extends TableFieldMap> = Extract<{
153
+ [K in keyof Fields]: Fields[K] extends AnyColumnDefinition
154
+ ? Fields[K]["metadata"]["primaryKey"] extends true
155
+ ? ConflictArbiter<readonly [Extract<K, string>], "unconditional", undefined, true>
156
+ : Fields[K]["metadata"]["unique"] extends true
157
+ ? ConflictArbiter<
158
+ readonly [Extract<K, string>],
159
+ "unconditional",
160
+ Fields[K]["metadata"]["uniqueConstraint"] extends { readonly name?: infer Name extends string } ? Name : undefined,
161
+ true
162
+ >
163
+ : never
164
+ : never
165
+ }[keyof Fields], ConflictArbiter>
166
+
167
+ type TableConflictArbitersOf<Table extends TableDefinition<any, any, any, any, any, any>> =
168
+ Table extends TableDefinition<any, any, any, any, any, infer ConflictArbiters> ? ConflictArbiters : never
169
+
170
+ type NormalizeArbiterColumns<Columns> =
171
+ Columns extends readonly [infer Head extends string, ...infer Tail extends string[]]
172
+ ? readonly [Head, ...Tail]
173
+ : Columns extends string
174
+ ? readonly [Columns]
175
+ : never
176
+
177
+ type OptionColumns<Spec extends TableOptionSpec> =
178
+ Spec extends { readonly columns?: infer Columns } ? NormalizeArbiterColumns<Columns> : never
179
+
180
+ type OptionName<Spec extends TableOptionSpec> =
181
+ Spec extends { readonly name: infer Name extends string } ? Name : undefined
182
+
183
+ type ConflictArbiterFromOption<Spec extends TableOptionSpec> =
184
+ OptionColumns<Spec> extends infer Columns extends ColumnList
185
+ ? HasBroadColumns<Columns> extends true
186
+ ? never
187
+ : Spec extends { readonly kind: "primaryKey" | "unique" }
188
+ ? ConflictArbiter<Columns, "unconditional", OptionName<Spec>, true>
189
+ : Spec extends { readonly kind: "index"; readonly unique: true }
190
+ ? ConflictArbiter<Columns, Spec extends { readonly predicate: DdlExpressionLike } ? "partial" : "unconditional", OptionName<Spec>, false>
191
+ : never
192
+ : never
81
193
 
82
194
  type BuildPrimaryKey<
83
- Table extends TableDefinition<any, any, any, "schema", any>,
195
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
84
196
  Spec extends TableOptionSpec
85
197
  > = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
86
- ? Columns[number] & keyof Table[typeof TypeId]["fields"] & string
87
- : Table[typeof TypeId]["primaryKey"][number]
198
+ ? HasBroadColumns<Columns> extends true
199
+ ? TablePrimaryKeyOf<Table>
200
+ : Columns[number] & keyof TableFieldsOf<Table> & string
201
+ : TablePrimaryKeyOf<Table>
88
202
 
89
- type OptionInputTable<
90
- Table extends TableDefinition<any, any, any, "schema", any>,
203
+ type OptionInputConstraint<
204
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
91
205
  Spec extends TableOptionSpec
92
206
  > = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
93
- ? ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
207
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : unknown
94
208
  : Spec extends { readonly kind: "index" }
95
- ? ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
209
+ ? Spec extends { readonly columns: infer Columns extends readonly string[] }
210
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
211
+ : ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
96
212
  : Spec extends { readonly kind: "foreignKey" }
97
- ? ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
213
+ ? Spec extends { readonly columns: infer Columns extends readonly string[] }
214
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
215
+ : ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
98
216
  : Spec extends { readonly columns: infer Columns extends readonly string[] }
99
- ? ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
100
- : Table
217
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : unknown
218
+ : unknown
219
+
220
+ type OptionInputTable<
221
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
222
+ Spec extends TableOptionSpec
223
+ > = Table & OptionInputConstraint<Table, Spec>
224
+
225
+ type OptionValidationArgs<
226
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
227
+ Spec extends TableOptionSpec
228
+ > = OptionInputConstraint<Table, Spec> extends never ? [never] : []
101
229
 
102
230
  type ApplyOption<
103
- Table extends TableDefinition<any, any, any, "schema", any>,
231
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
104
232
  Spec extends TableOptionSpec
105
233
  > = Spec extends { readonly kind: "primaryKey" }
106
234
  ? TableDefinition<
107
- Table[typeof TypeId]["name"],
108
- Table[typeof TypeId]["fields"],
235
+ TableNameOf<Table>,
236
+ TableFieldsOf<Table>,
109
237
  BuildPrimaryKey<Table, Spec>,
110
- "schema"
238
+ "schema",
239
+ TableSchemaNameOf<Table>,
240
+ TableConflictArbitersOf<Table> | ConflictArbiterFromOption<Spec>
111
241
  >
112
242
  : TableDefinition<
113
- Table[typeof TypeId]["name"],
114
- Table[typeof TypeId]["fields"],
115
- Table[typeof TypeId]["primaryKey"][number],
116
- "schema"
243
+ TableNameOf<Table>,
244
+ TableFieldsOf<Table>,
245
+ TablePrimaryKeyOf<Table>,
246
+ "schema",
247
+ TableSchemaNameOf<Table>,
248
+ TableConflictArbitersOf<Table> | ConflictArbiterFromOption<Spec>
117
249
  >
118
250
 
251
+ type ApplyTableOption<
252
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
253
+ Spec extends TableOptionSpec
254
+ > = ApplyOption<Table, Spec>
255
+
256
+ type TableOptionPipe<
257
+ Name extends string,
258
+ Fields extends TableFieldMap,
259
+ PrimaryKeyColumns extends keyof Fields & string,
260
+ Kind extends TableKind,
261
+ SchemaName extends string | undefined,
262
+ ConflictArbiters extends ConflictArbiter
263
+ > = Kind extends "schema"
264
+ ? {
265
+ pipe<Spec extends TableOptionSpec>(
266
+ option: TableOption<Spec, TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>>,
267
+ ...validation: OptionValidationArgs<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>, Spec>
268
+ ): ApplyTableOption<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>, Spec>
269
+ }
270
+ : {}
271
+
119
272
  export type ValidateDeclaredOptions<
120
- Table extends TableDefinition<any, any, any, "schema", any>,
273
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
121
274
  Options extends DeclaredTableOptions
122
275
  > = {
123
276
  readonly [K in keyof Options]: Options[K] extends TableOptionBuilderLike<infer Spec>
@@ -126,7 +279,7 @@ export type ValidateDeclaredOptions<
126
279
  }
127
280
 
128
281
  export type ApplyDeclaredOptions<
129
- Table extends TableDefinition<any, any, any, "schema", any>,
282
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
130
283
  Options extends DeclaredTableOptions
131
284
  > = Options extends readonly [infer Head, ...infer Tail]
132
285
  ? Head extends TableOptionBuilderLike<infer Spec>
@@ -152,9 +305,15 @@ export interface TableSchemas<
152
305
  Fields extends TableFieldMap,
153
306
  PrimaryKeyColumns extends keyof Fields & string
154
307
  > {
155
- readonly select: Schema.Decoder<SelectRow<Name, Fields>, never>
156
- readonly insert: Schema.Decoder<InsertRow<Name, Fields>, never>
157
- readonly update: Schema.Decoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
308
+ readonly select: Schema.ConstraintDecoder<SelectRow<Name, Fields>, never>
309
+ readonly insert: Schema.ConstraintDecoder<InsertRow<Name, Fields>, never>
310
+ readonly update: Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
311
+ }
312
+
313
+ type AnyTableSchemas = {
314
+ readonly select: Schema.Schema<any>
315
+ readonly insert: Schema.Schema<any>
316
+ readonly update: Schema.Schema<any>
158
317
  }
159
318
 
160
319
  type TableSchemaCache<
@@ -170,45 +329,37 @@ interface TableState<
170
329
  Fields extends TableFieldMap,
171
330
  PrimaryKeyColumns extends keyof Fields & string,
172
331
  Kind extends TableKind = "schema",
173
- SchemaName extends string | undefined = DefaultSchemaName
332
+ SchemaName extends string | undefined = DefaultSchemaName,
333
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
174
334
  > {
175
335
  readonly name: Name
176
336
  readonly baseName: string
177
337
  readonly schemaName: SchemaName
178
338
  readonly fields: Fields
179
339
  readonly primaryKey: readonly PrimaryKeyColumns[]
340
+ readonly conflictArbiters: readonly ConflictArbiters[]
180
341
  readonly kind: Kind
181
- }
182
-
183
- /** Namespace-scoped table builder. */
184
- export interface TableSchemaNamespace<SchemaName extends string> {
185
- readonly schemaName: SchemaName
186
- readonly table: <
187
- Name extends string,
188
- Fields extends TableFieldMap,
189
- const Options extends DeclaredTableOptions,
190
- PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
191
- >(
192
- name: Name,
193
- fields: Fields,
194
- ...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
195
- ) => ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
342
+ readonly casing?: Casing.Options
196
343
  }
197
344
 
198
345
  export type DeclaredTableOptions = readonly TableOptionBuilderLike[]
199
- export type { DdlExpressionLike, IndexKeySpec, MatchingColumnArityInput, NonEmptyColumnInput, NormalizeColumns, ReferentialAction } from "./table-options.js"
346
+ export type { DdlExpressionLike, IndexKeySpec, LiteralStringInput, MatchingColumnArityInput, NonEmptyColumnInput, NonEmptyStringInput, NormalizeColumns, ReferentialAction } from "./table-options.js"
347
+ export type { NonEmptyFieldMap }
348
+ export type NonEmptySchemaNameInput<Value extends string | undefined> =
349
+ Value extends string ? NonEmptyStringInput<Value> : Value
200
350
 
201
351
  export type TableDefinition<
202
352
  Name extends string,
203
353
  Fields extends TableFieldMap,
204
354
  PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
205
355
  Kind extends TableKind = "schema",
206
- SchemaName extends string | undefined = DefaultSchemaName
207
- > = Pipeable & {
356
+ SchemaName extends string | undefined = DefaultSchemaName,
357
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
358
+ > = Pipeable & TableOptionPipe<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters> & {
208
359
  readonly name: Name
209
360
  readonly columns: BoundColumns<Name, Fields>
210
- readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns>
211
- readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, Kind, SchemaName>
361
+ readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns> & AnyTableSchemas
362
+ readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters>
212
363
  readonly [Plan.TypeId]: Plan.State<
213
364
  BoundColumns<Name, Fields>,
214
365
  never,
@@ -234,11 +385,12 @@ export type TableClassStatic<
234
385
  Name extends string,
235
386
  Fields extends TableFieldMap,
236
387
  PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
237
- SchemaName extends string | undefined = DefaultSchemaName
388
+ SchemaName extends string | undefined = DefaultSchemaName,
389
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
238
390
  > = (abstract new (...args: any[]) => any) & Pipeable & {
239
391
  readonly columns: BoundColumns<Name, Fields>
240
392
  readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns>
241
- readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
393
+ readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>
242
394
  readonly [Plan.TypeId]: Plan.State<
243
395
  BoundColumns<Name, Fields>,
244
396
  never,
@@ -258,30 +410,37 @@ export type TableClassStatic<
258
410
 
259
411
  /** Minimal structural table-like contract used across helper APIs. */
260
412
  export type AnyTable<Dialect extends string = string> = {
261
- readonly [TypeId]: TableState<string, TableFieldMap, string, TableKind, string | undefined>
413
+ readonly [TypeId]: TableState<string, TableFieldMap, string, TableKind, string | undefined, ConflictArbiter>
262
414
  readonly [OptionsSymbol]: readonly TableOptionSpec[]
263
415
  } & Plan.RowSet<any, any, Record<string, Plan.AnySource>, Dialect>
264
416
 
265
- type FieldsOfAnyTable<Table extends AnyTable> = Table[typeof TypeId]["fields"]
266
-
267
- type ColumnNamesOfAnyTable<Table extends AnyTable> = Extract<keyof FieldsOfAnyTable<Table>, string>
417
+ type CheckPredicateTable = any
418
+ type CheckPredicate = (table: CheckPredicateTable) => DdlExpressionLike
268
419
 
269
420
  /** Public table-option builder type used by `Table.index`, `Table.primaryKey`, and friends. */
270
421
  export type TableOption<
271
- Spec extends TableOptionSpec = TableOptionSpec
272
- > = {
273
- <
274
- Name extends string,
275
- Fields extends TableFieldMap,
276
- PrimaryKeyColumns extends keyof Fields & string
277
- >(
278
- table: OptionInputTable<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
279
- ): ApplyOption<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
422
+ Spec extends TableOptionSpec = TableOptionSpec,
423
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
424
+ > = Pipeable & {
425
+ readonly pipe: Pipeable["pipe"]
280
426
  readonly option: Spec
281
- }
427
+ readonly [ResolveOptionSymbol]?: (table: TableDefinition<any, any, any, "schema", any>) => Spec
428
+ } & (Spec extends { readonly kind: "primaryKey" }
429
+ ? {
430
+ <Table extends TableContext>(
431
+ table: Table,
432
+ ...validation: OptionValidationArgs<Table, Spec>
433
+ ): ApplyOption<Table, Spec>
434
+ }
435
+ : {
436
+ <Table extends TableContext>(
437
+ table: Table,
438
+ ...validation: OptionValidationArgs<Table, Spec>
439
+ ): ApplyOption<Table, Spec>
440
+ })
282
441
 
283
442
  const TableProto = {
284
- pipe(this: unknown) {
443
+ pipe(this: Pipeable) {
285
444
  return pipeArguments(this, arguments)
286
445
  }
287
446
  }
@@ -290,7 +449,7 @@ const attachPipe = <Value extends object>(value: Value): Value => {
290
449
  Object.defineProperty(value, "pipe", {
291
450
  configurable: true,
292
451
  writable: true,
293
- value: function(this: unknown) {
452
+ value: function(this: Value) {
294
453
  return pipeArguments(value, arguments)
295
454
  }
296
455
  })
@@ -305,8 +464,42 @@ type BuildArtifacts<
305
464
  readonly columns: BoundColumns<Name, Fields>
306
465
  readonly normalizedOptions: readonly TableOptionSpec[]
307
466
  readonly primaryKey: readonly PrimaryKeyColumns[]
467
+ readonly conflictArbiters: readonly ConflictArbiter[]
308
468
  }
309
469
 
470
+ const conflictArbitersFromOptions = (
471
+ options: readonly TableOptionSpec[]
472
+ ): readonly ConflictArbiter[] =>
473
+ options.flatMap((option): readonly ConflictArbiter[] => {
474
+ if (typeof option !== "object" || option === null) {
475
+ return []
476
+ }
477
+ if (!("columns" in option) || !Array.isArray(option.columns) || option.columns.length === 0) {
478
+ return []
479
+ }
480
+ switch (option.kind) {
481
+ case "primaryKey":
482
+ case "unique":
483
+ return [{
484
+ columns: option.columns as ColumnList,
485
+ scope: "unconditional",
486
+ name: option.name,
487
+ constraint: true
488
+ }]
489
+ case "index":
490
+ return option.unique === true
491
+ ? [{
492
+ columns: option.columns as ColumnList,
493
+ scope: option.predicate === undefined ? "unconditional" : "partial",
494
+ name: option.name,
495
+ constraint: false
496
+ }]
497
+ : []
498
+ default:
499
+ return []
500
+ }
501
+ })
502
+
310
503
  const buildArtifacts = <
311
504
  Name extends string,
312
505
  Fields extends TableFieldMap,
@@ -315,19 +508,22 @@ const buildArtifacts = <
315
508
  name: Name,
316
509
  fields: Fields,
317
510
  declaredOptions: readonly TableOptionSpec[],
318
- schemaName: SchemaName
511
+ schemaName: SchemaName,
512
+ casing?: Casing.Options
319
513
  ): BuildArtifacts<Name, Fields, keyof Fields & string> => {
320
514
  const normalizedOptions = [...collectInlineOptions(fields), ...declaredOptions]
321
515
  validateFieldDialects(name, fields)
322
516
  validateOptions(name, fields, declaredOptions)
323
517
  const primaryKey = resolvePrimaryKeyColumns(fields, declaredOptions) as readonly (keyof Fields & string)[]
518
+ const conflictArbiters = conflictArbitersFromOptions(normalizedOptions)
324
519
  const columns = Object.fromEntries(
325
- Object.entries(fields).map(([key, column]) => [key, bindColumn(name, key, column, name, schemaName)])
520
+ Object.entries(fields).map(([key, column]) => [key, bindColumn(name, key, column, name, schemaName, casing)])
326
521
  ) as BoundColumns<Name, Fields>
327
522
  return {
328
523
  columns,
329
524
  normalizedOptions,
330
- primaryKey
525
+ primaryKey,
526
+ conflictArbiters
331
527
  }
332
528
  }
333
529
 
@@ -338,7 +534,7 @@ const getSchemaCache = <
338
534
  >(
339
535
  table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
340
536
  ): TableSchemaCache<Name, Fields, PrimaryKeyColumns> => {
341
- const target = table as unknown as {
537
+ const target = table as {
342
538
  [SchemaCacheSymbol]?: TableSchemaCache<Name, Fields, PrimaryKeyColumns>
343
539
  }
344
540
  if (target[SchemaCacheSymbol] !== undefined) {
@@ -397,8 +593,8 @@ export function selectSchema<
397
593
  PrimaryKeyColumns extends keyof Fields & string
398
594
  >(
399
595
  table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
400
- ): Schema.Decoder<SelectRow<Name, Fields>, never> {
401
- return schemaFor(table, "select") as Schema.Decoder<SelectRow<Name, Fields>, never>
596
+ ): Schema.ConstraintDecoder<SelectRow<Name, Fields>, never> {
597
+ return schemaFor(table, "select") as Schema.ConstraintDecoder<SelectRow<Name, Fields>, never>
402
598
  }
403
599
 
404
600
  export function insertSchema<
@@ -407,8 +603,8 @@ export function insertSchema<
407
603
  PrimaryKeyColumns extends keyof Fields & string
408
604
  >(
409
605
  table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
410
- ): Schema.Decoder<InsertRow<Name, Fields>, never> {
411
- return schemaFor(table, "insert") as Schema.Decoder<InsertRow<Name, Fields>, never>
606
+ ): Schema.ConstraintDecoder<InsertRow<Name, Fields>, never> {
607
+ return schemaFor(table, "insert") as Schema.ConstraintDecoder<InsertRow<Name, Fields>, never>
412
608
  }
413
609
 
414
610
  export function updateSchema<
@@ -417,8 +613,8 @@ export function updateSchema<
417
613
  PrimaryKeyColumns extends keyof Fields & string
418
614
  >(
419
615
  table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
420
- ): Schema.Decoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never> {
421
- return schemaFor(table, "update") as Schema.Decoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
616
+ ): Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never> {
617
+ return schemaFor(table, "update") as Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
422
618
  }
423
619
 
424
620
  const schemasFor = <
@@ -472,7 +668,8 @@ const makeTable = <
472
668
  Fields extends TableFieldMap,
473
669
  PrimaryKeyColumns extends keyof Fields & string,
474
670
  Kind extends TableKind = "schema",
475
- SchemaName extends string | undefined = DefaultSchemaName
671
+ SchemaName extends string | undefined = DefaultSchemaName,
672
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
476
673
  >(
477
674
  name: Name,
478
675
  fields: Fields,
@@ -480,12 +677,13 @@ const makeTable = <
480
677
  baseName: string = name,
481
678
  kind: Kind = "schema" as Kind,
482
679
  schemaName?: SchemaName,
483
- schemaMode: "default" | "explicit" = "default"
484
- ): TableDefinition<Name, Fields, PrimaryKeyColumns, Kind, SchemaName> => {
680
+ schemaMode: "default" | "explicit" = "default",
681
+ casing?: Casing.Options
682
+ ): TableDefinition<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters> => {
485
683
  const resolvedSchemaName = schemaMode === "explicit"
486
684
  ? schemaName
487
685
  : ("public" as SchemaName)
488
- const artifacts = buildArtifacts(name, fields, declaredOptions, resolvedSchemaName)
686
+ const artifacts = buildArtifacts(name, fields, declaredOptions, resolvedSchemaName, casing)
489
687
  const dialect = resolveFieldDialect(fields)
490
688
  const table = attachPipe(Object.create(TableProto))
491
689
  table.name = name
@@ -497,7 +695,9 @@ const makeTable = <
497
695
  schemaName: resolvedSchemaName,
498
696
  fields,
499
697
  primaryKey: artifacts.primaryKey,
500
- kind
698
+ conflictArbiters: artifacts.conflictArbiters as readonly ConflictArbiters[],
699
+ kind,
700
+ casing
501
701
  }
502
702
  table[Plan.TypeId] = {
503
703
  selection: artifacts.columns,
@@ -544,23 +744,17 @@ const applyDeclaredOptions = <
544
744
  ) as unknown as Table
545
745
  }
546
746
 
547
- const validateClassOptions = (declaredOptions: readonly TableOptionSpec[]): void => {
548
- for (const option of declaredOptions) {
549
- if (option.kind === "primaryKey") {
550
- throw new Error("Table.Class does not support table-level primary keys; declare primary keys inline on columns")
551
- }
552
- }
553
- }
554
-
555
747
  const resolveFieldDialect = (fields: TableFieldMap): string => {
556
748
  const dialects = [...new Set(Object.values(fields).map((field) => field.metadata.dbType.dialect))]
557
749
  if (dialects.length === 0) {
558
750
  throw new Error("Cannot infer table dialect from an empty field set")
559
751
  }
560
- if (dialects.length > 1) {
752
+ const concreteDialects = dialects.filter((dialect) => dialect !== "standard")
753
+ const uniqueConcreteDialects = [...new Set(concreteDialects)]
754
+ if (uniqueConcreteDialects.length > 1) {
561
755
  throw new Error(`Mixed table dialects are not supported: ${dialects.join(", ")}`)
562
756
  }
563
- return dialects[0]!
757
+ return uniqueConcreteDialects[0] ?? "standard"
564
758
  }
565
759
 
566
760
  const validateFieldDialects = (tableName: string, fields: TableFieldMap): void => {
@@ -586,7 +780,6 @@ const ensureClassArtifacts = <
586
780
  }
587
781
  const state = self[TypeId]
588
782
  const classOptions = self[options]
589
- validateClassOptions(extractDeclaredOptions(classOptions))
590
783
  const table = applyDeclaredOptions(
591
784
  makeTable(
592
785
  state.name,
@@ -595,14 +788,16 @@ const ensureClassArtifacts = <
595
788
  state.name,
596
789
  "schema",
597
790
  state.schemaName,
598
- state.schemaName === undefined || state.schemaName === "public" ? "default" : "explicit"
791
+ state.schemaName === undefined || state.schemaName === "public" ? "default" : "explicit",
792
+ state.casing
599
793
  ) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", typeof state.schemaName>,
600
794
  classOptions
601
795
  )
602
796
  const artifacts = {
603
797
  columns: table.columns,
604
798
  normalizedOptions: table[OptionsSymbol],
605
- primaryKey: table[TypeId].primaryKey as readonly PrimaryKeyColumns[]
799
+ primaryKey: table[TypeId].primaryKey as readonly PrimaryKeyColumns[],
800
+ conflictArbiters: table[TypeId].conflictArbiters
606
801
  } satisfies BuildArtifacts<Name, Fields, PrimaryKeyColumns>
607
802
  Object.defineProperty(self, CacheSymbol, {
608
803
  configurable: true,
@@ -619,9 +814,6 @@ const appendOption = <
619
814
  option: Spec
620
815
  ): ApplyOption<Table, Spec> => {
621
816
  const state = table[TypeId]
622
- if (state.kind !== "schema") {
623
- throw new Error("Table options can only be applied to schema tables, not aliased query sources")
624
- }
625
817
  return makeTable(
626
818
  state.name,
627
819
  state.fields,
@@ -629,49 +821,113 @@ const appendOption = <
629
821
  state.baseName,
630
822
  state.kind,
631
823
  state.schemaName,
632
- "explicit"
824
+ "explicit",
825
+ state.casing
633
826
  ) as unknown as ApplyOption<Table, Spec>
634
827
  }
635
828
 
636
- const makeOption = <Spec extends TableOptionSpec>(option: Spec): TableOption<Spec> => {
637
- const builder = ((table: TableDefinition<any, any, any, "schema", any>) =>
638
- appendOption(table, option)) as unknown as TableOption<Spec>
639
- ;(builder as { option: Spec }).option = option
640
- return builder
829
+ const makeOption = <
830
+ Spec extends TableOptionSpec,
831
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
832
+ >(option: Spec): TableOption<Spec, TableContext> => {
833
+ return attachPipe(Object.assign(
834
+ <Table extends TableDefinition<any, any, any, "schema", any>>(
835
+ table: Table,
836
+ ..._validation: OptionValidationArgs<Table, Spec>
837
+ ): ApplyTableOption<Table, Spec> =>
838
+ appendOption(table, option) as unknown as ApplyTableOption<Table, Spec>,
839
+ { option }
840
+ )) as unknown as TableOption<Spec, TableContext>
641
841
  }
642
842
 
643
- const makeResolvedOption = <Spec extends TableOptionSpec>(
843
+ const makeResolvedOption = <
844
+ Spec extends TableOptionSpec,
845
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
846
+ >(
644
847
  option: Spec,
645
848
  resolve: (table: TableDefinition<any, any, any, "schema", any>) => Spec
646
- ): TableOption<Spec> => {
647
- const builder = ((table: TableDefinition<any, any, any, "schema", any>) =>
648
- appendOption(table, resolve(table))) as unknown as TableOption<Spec>
649
- ;(builder as { option: Spec }).option = option
650
- return builder
849
+ ): TableOption<Spec, TableContext> => {
850
+ return attachPipe(Object.assign(
851
+ <Table extends TableDefinition<any, any, any, "schema", any>>(
852
+ table: Table,
853
+ ..._validation: OptionValidationArgs<Table, Spec>
854
+ ): ApplyTableOption<Table, Spec> =>
855
+ appendOption(
856
+ table,
857
+ resolve(table)
858
+ ) as unknown as ApplyTableOption<Table, Spec>,
859
+ {
860
+ option,
861
+ [ResolveOptionSymbol]: resolve
862
+ }
863
+ )) as unknown as TableOption<Spec, TableContext>
651
864
  }
652
865
 
653
866
  export const option = <Spec extends TableOptionSpec>(spec: Spec): TableOption<Spec> =>
654
867
  makeOption(spec)
655
868
 
656
- export const optionFromTable = <Spec extends TableOptionSpec>(
869
+ export const optionFromTable = <
870
+ Spec extends TableOptionSpec,
871
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
872
+ >(
657
873
  spec: Spec,
658
874
  resolve: (table: TableDefinition<any, any, any, "schema", any>) => Spec
659
- ): TableOption<Spec> =>
875
+ ): TableOption<Spec, TableContext> =>
660
876
  makeResolvedOption(spec, resolve)
661
877
 
878
+ export const resolveOption = <
879
+ Spec extends TableOptionSpec,
880
+ TableContext extends SchemaTableDefinition
881
+ >(
882
+ option: TableOption<Spec, TableContext>,
883
+ table: TableContext
884
+ ): Spec => {
885
+ const resolve = option[ResolveOptionSymbol]
886
+ return resolve === undefined ? option.option : resolve(table)
887
+ }
888
+
889
+ export const mapOption = <
890
+ Spec extends TableOptionSpec,
891
+ Next extends TableOptionSpec,
892
+ TableContext extends SchemaTableDefinition
893
+ >(
894
+ option: TableOption<Spec, TableContext>,
895
+ map: (spec: Spec) => Next
896
+ ): TableOption<Next, TableContext> => {
897
+ const resolve = option[ResolveOptionSymbol]
898
+ return resolve === undefined
899
+ ? makeOption<Next, TableContext>(map(option.option))
900
+ : makeResolvedOption<Next, TableContext>(
901
+ map(option.option),
902
+ (table) => map(resolve(table))
903
+ )
904
+ }
905
+
662
906
  /** Creates a table definition from a name and field map. */
907
+ export function make<
908
+ Name extends string,
909
+ Fields extends TableFieldMap
910
+ >(
911
+ name: NonEmptyStringInput<Name>,
912
+ fields: Fields & NonEmptyFieldMap<Fields>
913
+ ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", DefaultSchemaName>
663
914
  export function make<
664
915
  Name extends string,
665
916
  Fields extends TableFieldMap,
666
- SchemaName extends string | undefined = DefaultSchemaName
917
+ const SchemaName extends string
667
918
  >(
668
- name: Name,
669
- fields: Fields,
670
- schemaName?: SchemaName
671
- ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", SchemaName> {
919
+ name: NonEmptyStringInput<Name>,
920
+ fields: Fields & NonEmptyFieldMap<Fields>,
921
+ schemaName: NonEmptySchemaNameInput<SchemaName>
922
+ ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", SchemaName>
923
+ export function make(
924
+ name: string,
925
+ fields: TableFieldMap,
926
+ schemaName?: string
927
+ ): any {
672
928
  const resolvedSchemaName = arguments.length >= 3
673
929
  ? schemaName
674
- : ("public" as SchemaName)
930
+ : "public"
675
931
  return makeTable(
676
932
  name,
677
933
  fields,
@@ -683,38 +939,58 @@ export function make<
683
939
  )
684
940
  }
685
941
 
686
- /**
687
- * Creates a namespace-scoped builder for a concrete SQL schema/database.
688
- */
689
- export const schema = <SchemaName extends string>(
690
- schemaName: SchemaName
691
- ): TableSchemaNamespace<SchemaName> => {
692
- const table = <
693
- Name extends string,
694
- Fields extends TableFieldMap,
695
- const Options extends DeclaredTableOptions,
696
- PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
697
- >(
698
- name: Name,
699
- fields: Fields,
700
- ...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
701
- ): ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options> =>
702
- applyDeclaredOptions(
703
- makeTable(
704
- name,
705
- fields,
706
- [],
707
- name,
708
- "schema",
709
- schemaName,
710
- "explicit"
711
- ) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>,
712
- options as unknown as Options
713
- ) as ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
714
- return {
942
+ export const withCasing = <
943
+ Table extends TableDefinition<any, any, any, any, any>
944
+ >(
945
+ table: Table,
946
+ casing: Casing.Options
947
+ ): Table => {
948
+ const state = table[TypeId]
949
+ return makeTable(
950
+ state.name,
951
+ state.fields,
952
+ table[DeclaredOptionsSymbol],
953
+ state.baseName,
954
+ state.kind,
955
+ state.schemaName,
956
+ "explicit",
957
+ Casing.merge(state.casing, casing)
958
+ ) as Table
959
+ }
960
+
961
+ export const withSchema = <
962
+ Table extends TableDefinition<any, any, any, any, any>,
963
+ SchemaName extends string
964
+ >(
965
+ table: Table,
966
+ schemaName: SchemaName,
967
+ schemaCasing?: Casing.Options
968
+ ): TableDefinition<
969
+ Table[typeof TypeId]["name"],
970
+ Table[typeof TypeId]["fields"],
971
+ Table[typeof TypeId]["primaryKey"][number],
972
+ Table[typeof TypeId]["kind"],
973
+ SchemaName,
974
+ Table[typeof TypeId]["conflictArbiters"][number]
975
+ > => {
976
+ const state = table[TypeId]
977
+ return makeTable(
978
+ state.name,
979
+ state.fields,
980
+ table[DeclaredOptionsSymbol],
981
+ state.baseName,
982
+ state.kind,
715
983
  schemaName,
716
- table
717
- } as unknown as TableSchemaNamespace<SchemaName>
984
+ "explicit",
985
+ Casing.merge(schemaCasing, state.casing)
986
+ ) as TableDefinition<
987
+ Table[typeof TypeId]["name"],
988
+ Table[typeof TypeId]["fields"],
989
+ Table[typeof TypeId]["primaryKey"][number],
990
+ Table[typeof TypeId]["kind"],
991
+ SchemaName,
992
+ Table[typeof TypeId]["conflictArbiters"][number]
993
+ >
718
994
  }
719
995
 
720
996
  /**
@@ -729,39 +1005,43 @@ export const alias = <
729
1005
  Fields extends TableFieldMap,
730
1006
  PrimaryKeyColumns extends keyof Fields & string,
731
1007
  SchemaName extends string,
1008
+ ConflictArbiters extends ConflictArbiter,
732
1009
  AliasName extends string
733
1010
  >(
734
- table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, SchemaName> | TableClassStatic<Name, Fields, PrimaryKeyColumns, SchemaName>,
735
- aliasName: AliasName
1011
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, SchemaName, ConflictArbiters> | TableClassStatic<Name, Fields, PrimaryKeyColumns, SchemaName, ConflictArbiters>,
1012
+ aliasName: LiteralStringInput<AliasName>
736
1013
  ): TableDefinition<
737
1014
  AliasName,
738
1015
  Fields,
739
1016
  PrimaryKeyColumns,
740
1017
  "alias",
741
- SchemaName
1018
+ SchemaName,
1019
+ ConflictArbiters
742
1020
  > => {
743
1021
  const state = table[TypeId]
744
1022
  const columns = Object.fromEntries(
745
- Object.entries(state.fields).map(([key, column]) => [key, bindColumn(aliasName, key, column as AnyColumnDefinition, state.baseName, state.schemaName)])
1023
+ Object.entries(state.fields).map(([key, column]) => [key, bindColumn(aliasName as AliasName, key, column as AnyColumnDefinition, state.baseName, state.schemaName, state.casing)])
746
1024
  ) as BoundColumns<AliasName, Fields>
747
1025
  const aliased = attachPipe(Object.create(TableProto))
748
- aliased.name = aliasName
1026
+ aliased.name = aliasName as AliasName
749
1027
  aliased.columns = columns
750
1028
  defineSchemasGetter(aliased)
751
1029
  aliased[TypeId] = {
752
- name: aliasName,
1030
+ name: aliasName as AliasName,
753
1031
  baseName: state.baseName,
754
1032
  schemaName: state.schemaName,
755
1033
  fields: state.fields,
756
1034
  primaryKey: state.primaryKey,
757
- kind: "alias"
1035
+ conflictArbiters: state.conflictArbiters,
1036
+ kind: "alias",
1037
+ casing: state.casing
758
1038
  }
759
1039
  aliased[Plan.TypeId] = {
760
1040
  selection: columns,
761
1041
  required: undefined as never,
762
1042
  available: {
763
1043
  [aliasName]: {
764
- name: aliasName,
1044
+ name: aliasName as AliasName,
765
1045
  mode: "required",
766
1046
  baseName: state.baseName
767
1047
  }
@@ -785,19 +1065,38 @@ export const alias = <
785
1065
  * The returned base class can be extended and configured with
786
1066
  * `static readonly [Table.options]`.
787
1067
  */
1068
+ export function Class<Self = never>(
1069
+ name: "",
1070
+ schemaName?: string | undefined
1071
+ ): never
1072
+ export function Class<Self = never>(
1073
+ name: string,
1074
+ schemaName: ""
1075
+ ): never
788
1076
  export function Class<
789
1077
  Self = never,
790
- SchemaName extends string | undefined = DefaultSchemaName
1078
+ const SchemaName extends string | undefined = DefaultSchemaName,
1079
+ const Name extends string = string
1080
+ >(
1081
+ name: NonEmptyStringInput<Name>,
1082
+ schemaName?: NonEmptySchemaNameInput<SchemaName>
1083
+ ): <Fields extends TableFieldMap>(fields: Fields & NonEmptyFieldMap<Fields>) => [Self] extends [never]
1084
+ ? MissingSelfGeneric
1085
+ : TableClassStatic<Name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName>
1086
+ export function Class<
1087
+ Self = never,
1088
+ SchemaName extends string | undefined = DefaultSchemaName,
1089
+ Name extends string = string
791
1090
  >(
792
1091
  name: string,
793
1092
  schemaName?: SchemaName
794
- ) {
1093
+ ): any {
795
1094
  const resolvedSchemaName = arguments.length >= 2
796
1095
  ? schemaName
797
1096
  : ("public" as SchemaName)
798
- return <Fields extends TableFieldMap>(fields: Fields): [Self] extends [never]
1097
+ return <Fields extends TableFieldMap>(fields: Fields & NonEmptyFieldMap<Fields>): [Self] extends [never]
799
1098
  ? MissingSelfGeneric
800
- : TableClassStatic<typeof name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName> => {
1099
+ : TableClassStatic<Name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName> => {
801
1100
  abstract class TableClassBase {
802
1101
  static readonly tableName = name
803
1102
 
@@ -810,14 +1109,15 @@ export function Class<
810
1109
  }
811
1110
 
812
1111
  static get [TypeId]() {
813
- const declaredOptions = extractDeclaredOptions((this as unknown as TableClassStatic<typeof name, Fields>)[options])
814
- validateClassOptions(declaredOptions)
1112
+ const declaredOptions = extractDeclaredOptions((this as unknown as TableClassStatic<Name, Fields>)[options])
1113
+ const normalizedOptions = [...collectInlineOptions(fields), ...declaredOptions]
815
1114
  return {
816
1115
  name,
817
1116
  baseName: name,
818
1117
  schemaName: resolvedSchemaName,
819
1118
  fields,
820
1119
  primaryKey: resolvePrimaryKeyColumns(fields, collectInlineOptions(fields)),
1120
+ conflictArbiters: conflictArbitersFromOptions(normalizedOptions),
821
1121
  kind: "schema"
822
1122
  }
823
1123
  }
@@ -842,7 +1142,7 @@ export function Class<
842
1142
  return ensureClassArtifacts(this as any).normalizedOptions
843
1143
  }
844
1144
 
845
- static pipe(this: unknown) {
1145
+ static pipe(this: Pipeable) {
846
1146
  return pipeArguments(this, arguments)
847
1147
  }
848
1148
  }
@@ -861,87 +1161,220 @@ export function Class<
861
1161
  }
862
1162
  }
863
1163
 
1164
+ const selectionArray = (selection: AnyColumnSelection): readonly AnyBoundColumn[] =>
1165
+ (Array.isArray(selection) ? selection : [selection]) as readonly AnyBoundColumn[]
1166
+
1167
+ export const selectedColumnList = <Selection extends AnyColumnSelection>(
1168
+ selection: Selection
1169
+ ): SelectedColumns<Selection> =>
1170
+ selectionArray(selection).map((column) => column[BoundColumnTypeId].columnName) as unknown as SelectedColumns<Selection>
1171
+
1172
+ const referenceFromSelection = <Selection extends AnyColumnSelection>(selection: Selection) => {
1173
+ const columns = selectionArray(selection)
1174
+ const first = columns[0]!
1175
+ const bound = first[BoundColumnTypeId]
1176
+ return {
1177
+ tableName: bound.baseTableName,
1178
+ schemaName: bound.schemaName,
1179
+ casing: bound.casing,
1180
+ columns: columns.map((column) => column[BoundColumnTypeId].columnName) as unknown as SelectedColumns<Selection>
1181
+ }
1182
+ }
1183
+
864
1184
  /** Declares a table-level primary key. */
865
- export const primaryKey = <
866
- const Columns extends string | readonly string[]
1185
+ export function primaryKey<
1186
+ Table extends SchemaTableDefinition,
1187
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
867
1188
  >(
868
- columns: Columns & NonEmptyColumnInput<Columns>
1189
+ columns: ConcreteSelector<Table, Selection>
1190
+ ): TableOption<{
1191
+ readonly kind: "primaryKey"
1192
+ readonly columns: SelectedColumns<Selection>
1193
+ }, Table>
1194
+ export function primaryKey<
1195
+ Selection extends AnyColumnSelection
1196
+ >(
1197
+ columns: (table: LooseTableSelection) => Selection
1198
+ ): TableOption<{
1199
+ readonly kind: "primaryKey"
1200
+ readonly columns: SelectedColumns<Selection>
1201
+ }>
1202
+ export function primaryKey(
1203
+ columns: (table: any) => AnyColumnSelection
869
1204
  ): TableOption<{
870
1205
  readonly kind: "primaryKey"
871
- readonly columns: NormalizeColumns<Columns>
872
- }> => makeOption({
873
- kind: "primaryKey",
874
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
875
- })
1206
+ readonly columns: readonly [string, ...string[]]
1207
+ }> {
1208
+ return makeResolvedOption({
1209
+ kind: "primaryKey",
1210
+ columns: [] as unknown as readonly [string, ...string[]]
1211
+ }, (table) => ({
1212
+ kind: "primaryKey",
1213
+ columns: selectedColumnList(columns(table))
1214
+ }))
1215
+ }
876
1216
 
877
1217
  /** Declares a table-level unique constraint. */
878
- export const unique = <
879
- const Columns extends string | readonly string[]
1218
+ export function unique<
1219
+ Table extends SchemaTableDefinition,
1220
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
880
1221
  >(
881
- columns: Columns & NonEmptyColumnInput<Columns>
1222
+ columns: ConcreteSelector<Table, Selection>
1223
+ ): TableOption<{
1224
+ readonly kind: "unique"
1225
+ readonly columns: SelectedColumns<Selection>
1226
+ }, Table>
1227
+ export function unique<
1228
+ Selection extends AnyColumnSelection
1229
+ >(
1230
+ columns: (table: LooseTableSelection) => Selection
1231
+ ): TableOption<{
1232
+ readonly kind: "unique"
1233
+ readonly columns: SelectedColumns<Selection>
1234
+ }>
1235
+ export function unique(
1236
+ columns: (table: any) => AnyColumnSelection
882
1237
  ): TableOption<{
883
1238
  readonly kind: "unique"
884
- readonly columns: NormalizeColumns<Columns>
885
- }> => makeOption({
886
- kind: "unique",
887
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
888
- })
1239
+ readonly columns: readonly [string, ...string[]]
1240
+ }> {
1241
+ return makeResolvedOption({
1242
+ kind: "unique",
1243
+ columns: [] as unknown as readonly [string, ...string[]]
1244
+ }, (table) => ({
1245
+ kind: "unique",
1246
+ columns: selectedColumnList(columns(table))
1247
+ }))
1248
+ }
889
1249
 
890
1250
  /** Declares a table-level index. */
891
- export const index = <
892
- const Columns extends string | readonly string[]
1251
+ export function index<
1252
+ Table extends SchemaTableDefinition,
1253
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
1254
+ >(
1255
+ columns: ConcreteSelector<Table, Selection>
1256
+ ): TableOption<{
1257
+ readonly kind: "index"
1258
+ readonly columns: SelectedColumns<Selection>
1259
+ }, Table>
1260
+ export function index<
1261
+ Selection extends AnyColumnSelection
893
1262
  >(
894
- columns: Columns & NonEmptyColumnInput<Columns>
1263
+ columns: (table: LooseTableSelection) => Selection
895
1264
  ): TableOption<{
896
1265
  readonly kind: "index"
897
- readonly columns: NormalizeColumns<Columns>
898
- }> => makeOption({
899
- kind: "index",
900
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
901
- })
1266
+ readonly columns: SelectedColumns<Selection>
1267
+ }>
1268
+ export function index(
1269
+ columns: (table: any) => AnyColumnSelection
1270
+ ): TableOption<{
1271
+ readonly kind: "index"
1272
+ readonly columns: readonly [string, ...string[]]
1273
+ }> {
1274
+ return makeResolvedOption({
1275
+ kind: "index",
1276
+ columns: [] as unknown as readonly [string, ...string[]]
1277
+ }, (table) => ({
1278
+ kind: "index",
1279
+ columns: selectedColumnList(columns(table))
1280
+ }))
1281
+ }
902
1282
 
903
1283
  /** Declares a table-level foreign key. */
904
- export const foreignKey = <
905
- const LocalColumns extends string | readonly string[],
906
- TargetTable extends AnyTable,
907
- const TargetColumns extends string | readonly string[]
908
- >(
909
- columns: LocalColumns & NonEmptyColumnInput<LocalColumns>,
910
- target: () => TargetTable,
911
- referencedColumns: TargetColumns & NonEmptyColumnInput<TargetColumns> & MatchingColumnArityInput<LocalColumns, TargetColumns>
1284
+ export function foreignKey<
1285
+ Table extends SchemaTableDefinition,
1286
+ LocalSelection extends TableColumnSelection<Table> = TableColumnSelection<Table>,
1287
+ TargetSelection extends AnyColumnSelection = AnyColumnSelection
1288
+ >(
1289
+ columns: ConcreteSelector<Table, LocalSelection>,
1290
+ target: () => TargetSelection & MatchingSelectionArityInput<LocalSelection, TargetSelection>
912
1291
  ): TableOption<{
913
1292
  readonly kind: "foreignKey"
914
- readonly columns: NormalizeColumns<LocalColumns>
1293
+ readonly columns: SelectedColumns<LocalSelection>
915
1294
  readonly references: () => {
916
1295
  readonly tableName: string
917
1296
  readonly schemaName?: string
918
- readonly columns: NormalizeColumns<TargetColumns>
919
- readonly knownColumns: readonly ColumnNamesOfAnyTable<TargetTable>[]
1297
+ readonly casing?: Casing.Options
1298
+ readonly columns: SelectedColumns<TargetSelection>
920
1299
  }
921
- }> => makeOption({
922
- kind: "foreignKey",
923
- columns: normalizeColumnList(columns) as NormalizeColumns<LocalColumns>,
924
- references: () => ({
925
- tableName: target()[TypeId].baseName,
926
- schemaName: target()[TypeId].schemaName,
927
- columns: normalizeColumnList(referencedColumns) as NormalizeColumns<TargetColumns>,
928
- knownColumns: Object.keys(target()[TypeId].fields) as unknown as readonly ColumnNamesOfAnyTable<TargetTable>[]
929
- })
930
- })
1300
+ }, Table>
1301
+ export function foreignKey<
1302
+ LocalSelection extends AnyColumnSelection,
1303
+ TargetSelection extends AnyColumnSelection
1304
+ >(
1305
+ columns: (table: LooseTableSelection) => LocalSelection,
1306
+ target: () => TargetSelection
1307
+ ): TableOption<{
1308
+ readonly kind: "foreignKey"
1309
+ readonly columns: SelectedColumns<LocalSelection>
1310
+ readonly references: () => {
1311
+ readonly tableName: string
1312
+ readonly schemaName?: string
1313
+ readonly casing?: Casing.Options
1314
+ readonly columns: SelectedColumns<TargetSelection>
1315
+ }
1316
+ }>
1317
+ export function foreignKey(
1318
+ columns: (table: any) => AnyColumnSelection,
1319
+ target: () => AnyColumnSelection
1320
+ ): TableOption<{
1321
+ readonly kind: "foreignKey"
1322
+ readonly columns: readonly [string, ...string[]]
1323
+ readonly references: () => {
1324
+ readonly tableName: string
1325
+ readonly schemaName?: string
1326
+ readonly casing?: Casing.Options
1327
+ readonly columns: readonly [string, ...string[]]
1328
+ }
1329
+ }> {
1330
+ return makeResolvedOption({
1331
+ kind: "foreignKey",
1332
+ columns: [] as unknown as readonly [string, ...string[]],
1333
+ references: () => referenceFromSelection(target())
1334
+ }, (table) => ({
1335
+ kind: "foreignKey",
1336
+ columns: selectedColumnList(columns(table)),
1337
+ references: () => referenceFromSelection(target())
1338
+ }))
1339
+ }
931
1340
 
932
1341
  /** Declares a check constraint expression. */
933
- export const check = <Name extends string>(
934
- name: Name,
1342
+ export function check<const Name extends string>(
1343
+ name: NonEmptyStringInput<Name>,
935
1344
  predicate: DdlExpressionLike
936
1345
  ): TableOption<{
937
1346
  readonly kind: "check"
938
1347
  readonly name: Name
939
1348
  readonly predicate: DdlExpressionLike
940
- }> => makeOption({
941
- kind: "check",
942
- name,
943
- predicate
944
- })
1349
+ }>
1350
+ export function check<const Name extends string>(
1351
+ name: NonEmptyStringInput<Name>,
1352
+ predicate: CheckPredicate
1353
+ ): TableOption<{
1354
+ readonly kind: "check"
1355
+ readonly name: Name
1356
+ readonly predicate: DdlExpressionLike
1357
+ }>
1358
+ export function check(
1359
+ name: string,
1360
+ predicate: DdlExpressionLike | CheckPredicate
1361
+ ): TableOption<{
1362
+ readonly kind: "check"
1363
+ readonly name: string
1364
+ readonly predicate: DdlExpressionLike
1365
+ }> {
1366
+ const spec = {
1367
+ kind: "check",
1368
+ name,
1369
+ predicate: predicate as DdlExpressionLike
1370
+ } as const
1371
+ return typeof predicate === "function"
1372
+ ? makeResolvedOption(spec, (table) => ({
1373
+ ...spec,
1374
+ predicate: predicate(table as CheckPredicateTable)
1375
+ }))
1376
+ : makeOption(spec)
1377
+ }
945
1378
 
946
1379
  /** Extracts the row type produced by `selectSchema(table)`. */
947
1380
  export type SelectOf<Table extends AnyTable> = Table[typeof TypeId] extends {