drizzle-orm 0.38.3-e6823b4 → 0.38.3-efed06b

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 (91) hide show
  1. package/libsql/session.cjs +4 -8
  2. package/libsql/session.cjs.map +1 -1
  3. package/libsql/session.js +4 -8
  4. package/libsql/session.js.map +1 -1
  5. package/mysql-core/dialect.cjs +29 -11
  6. package/mysql-core/dialect.cjs.map +1 -1
  7. package/mysql-core/dialect.d.cts +3 -1
  8. package/mysql-core/dialect.d.ts +3 -1
  9. package/mysql-core/dialect.js +29 -11
  10. package/mysql-core/dialect.js.map +1 -1
  11. package/mysql-proxy/driver.cjs.map +1 -1
  12. package/mysql-proxy/driver.d.cts +1 -1
  13. package/mysql-proxy/driver.d.ts +1 -1
  14. package/mysql-proxy/driver.js.map +1 -1
  15. package/mysql-proxy/session.cjs +3 -23
  16. package/mysql-proxy/session.cjs.map +1 -1
  17. package/mysql-proxy/session.js +3 -23
  18. package/mysql-proxy/session.js.map +1 -1
  19. package/mysql2/session.cjs +1 -22
  20. package/mysql2/session.cjs.map +1 -1
  21. package/mysql2/session.js +1 -22
  22. package/mysql2/session.js.map +1 -1
  23. package/package.json +1 -1
  24. package/pg-core/dialect.cjs +20 -11
  25. package/pg-core/dialect.cjs.map +1 -1
  26. package/pg-core/dialect.d.cts +2 -1
  27. package/pg-core/dialect.d.ts +2 -1
  28. package/pg-core/dialect.js +20 -11
  29. package/pg-core/dialect.js.map +1 -1
  30. package/planetscale-serverless/session.cjs +1 -24
  31. package/planetscale-serverless/session.cjs.map +1 -1
  32. package/planetscale-serverless/session.js +1 -24
  33. package/planetscale-serverless/session.js.map +1 -1
  34. package/relations.cjs +141 -22
  35. package/relations.cjs.map +1 -1
  36. package/relations.d.cts +39 -26
  37. package/relations.d.ts +39 -26
  38. package/relations.js +149 -24
  39. package/relations.js.map +1 -1
  40. package/sql/sql.cjs +1 -1
  41. package/sql/sql.cjs.map +1 -1
  42. package/sql/sql.js +1 -1
  43. package/sql/sql.js.map +1 -1
  44. package/sql-js/driver.cjs.map +1 -1
  45. package/sql-js/driver.d.cts +1 -1
  46. package/sql-js/driver.d.ts +1 -1
  47. package/sql-js/driver.js.map +1 -1
  48. package/sql-js/session.cjs +18 -0
  49. package/sql-js/session.cjs.map +1 -1
  50. package/sql-js/session.d.cts +1 -0
  51. package/sql-js/session.d.ts +1 -0
  52. package/sql-js/session.js +18 -0
  53. package/sql-js/session.js.map +1 -1
  54. package/sqlite-core/db.cjs +4 -2
  55. package/sqlite-core/db.cjs.map +1 -1
  56. package/sqlite-core/db.d.cts +2 -1
  57. package/sqlite-core/db.d.ts +2 -1
  58. package/sqlite-core/db.js +4 -2
  59. package/sqlite-core/db.js.map +1 -1
  60. package/sqlite-core/dialect.cjs +19 -7
  61. package/sqlite-core/dialect.cjs.map +1 -1
  62. package/sqlite-core/dialect.d.cts +2 -1
  63. package/sqlite-core/dialect.d.ts +2 -1
  64. package/sqlite-core/dialect.js +19 -7
  65. package/sqlite-core/dialect.js.map +1 -1
  66. package/sqlite-core/query-builders/query.cjs +27 -9
  67. package/sqlite-core/query-builders/query.cjs.map +1 -1
  68. package/sqlite-core/query-builders/query.d.cts +5 -3
  69. package/sqlite-core/query-builders/query.d.ts +5 -3
  70. package/sqlite-core/query-builders/query.js +27 -9
  71. package/sqlite-core/query-builders/query.js.map +1 -1
  72. package/sqlite-core/session.cjs +2 -2
  73. package/sqlite-core/session.cjs.map +1 -1
  74. package/sqlite-core/session.d.cts +1 -1
  75. package/sqlite-core/session.d.ts +1 -1
  76. package/sqlite-core/session.js +2 -2
  77. package/sqlite-core/session.js.map +1 -1
  78. package/sqlite-proxy/driver.cjs +3 -2
  79. package/sqlite-proxy/driver.cjs.map +1 -1
  80. package/sqlite-proxy/driver.d.cts +2 -2
  81. package/sqlite-proxy/driver.d.ts +2 -2
  82. package/sqlite-proxy/driver.js +3 -2
  83. package/sqlite-proxy/driver.js.map +1 -1
  84. package/sqlite-proxy/session.cjs +15 -14
  85. package/sqlite-proxy/session.cjs.map +1 -1
  86. package/sqlite-proxy/session.js +15 -14
  87. package/sqlite-proxy/session.js.map +1 -1
  88. package/version.cjs +1 -1
  89. package/version.d.cts +1 -1
  90. package/version.d.ts +1 -1
  91. package/version.js +1 -1
package/relations.d.cts CHANGED
@@ -2,7 +2,7 @@ import { type AnyTable, type InferModelFromColumns, Table } from "./table.cjs";
2
2
  import { type AnyColumn, Column } from "./column.cjs";
3
3
  import { entityKind } from "./entity.cjs";
4
4
  import { and, asc, between, desc, exists, ilike, inArray, isNotNull, isNull, like, not, notBetween, notExists, notIlike, notInArray, notLike, or } from "./sql/expressions/index.cjs";
5
- import { Placeholder, SQL, sql, type SQLWrapper } from "./sql/sql.cjs";
5
+ import { type Placeholder, SQL, sql, type SQLWrapper } from "./sql/sql.cjs";
6
6
  import { type Assume, type Equal, type Simplify, type ValueOrArray, type Writable } from "./utils.cjs";
7
7
  export declare class Relations<TSchema extends Record<string, unknown> = Record<string, unknown>, TTables extends Record<string, Table> = Record<string, Table>, TConfig extends RelationsBuilderConfig<TTables> = RelationsBuilderConfig<TTables>> {
8
8
  readonly schema: TSchema;
@@ -35,6 +35,12 @@ export declare abstract class Relation<TSourceTableName extends string = string,
35
35
  sourceTable: AnyTable<{
36
36
  name: TSourceTableName;
37
37
  }>;
38
+ through?: {
39
+ source: AnyColumn[];
40
+ target: AnyColumn[];
41
+ };
42
+ throughTable?: Table;
43
+ isReversed?: boolean;
38
44
  constructor(targetTable: AnyTable<{
39
45
  name: TTargetTableName;
40
46
  }>);
@@ -122,7 +128,7 @@ export type DBQueryConfig<TRelationType extends 'one' | 'many' = 'one' | 'many',
122
128
  [K in keyof TTableConfig['columns']]?: boolean | undefined;
123
129
  } | undefined;
124
130
  with?: {
125
- [K in keyof TTableConfig['relations']]?: true | (TTableConfig['relations'][K] extends Relation ? DBQueryConfig<TTableConfig['relations'][K] extends One<string, string> ? 'one' : 'many', TSchema, FindTableInRelationalConfig<TSchema, TTableConfig['relations'][K]['targetTable']>> : never) | undefined;
131
+ [K in keyof TTableConfig['relations']]?: boolean | (TTableConfig['relations'][K] extends Relation ? DBQueryConfig<TTableConfig['relations'][K] extends One<string, string> ? 'one' : 'many', TSchema, FindTableInRelationalConfig<TSchema, TTableConfig['relations'][K]['targetTable']>> : never) | undefined;
126
132
  } | undefined;
127
133
  extras?: Record<string, SQLWrapper> | ((table: Simplify<AnyTable<TTableConfig> & TTableConfig['columns']>, operators: SQLOperator) => Record<string, SQLWrapper>) | undefined;
128
134
  offset?: number | Placeholder | undefined;
@@ -164,11 +170,14 @@ export type ExtractTablesWithRelations<TRelations extends Relations, TTables ext
164
170
  };
165
171
  export type ReturnTypeOrValue<T> = T extends (...args: any[]) => infer R ? R : T;
166
172
  export type BuildRelationResult<TConfig extends TablesRelationalConfig, TInclude, TRelations extends Record<string, RelationsBuilderEntry>> = {
167
- [K in NonUndefinedKeysOnly<TInclude> & keyof TRelations]: TRelations[K] extends infer TRel extends Relation ? BuildQueryResult<TConfig, FindTableInRelationalConfig<TConfig, TRel['targetTable']>, Assume<TInclude[K], true | Record<string, unknown>>> extends infer TResult ? TRel extends One<string, string> ? TResult | (Equal<TRel['optional'], true> extends true ? null : never) : TResult[] : never : TRelations[K] extends AggregatedField<infer TData> ? TData : never;
173
+ [K in TruthyKeysOnly<TInclude> & keyof TRelations]: TRelations[K] extends infer TRel extends Relation ? BuildQueryResult<TConfig, FindTableInRelationalConfig<TConfig, TRel['targetTable']>, Assume<TInclude[K], true | Record<string, unknown>>> extends infer TResult ? TRel extends One<string, string> ? TResult | (Equal<TRel['optional'], true> extends true ? null : TInclude[K] extends Record<string, unknown> ? TInclude[K]['where'] extends Record<string, any> ? null : never : never) : TResult[] : never : TRelations[K] extends AggregatedField<infer TData> ? TData : never;
168
174
  };
169
175
  export type NonUndefinedKeysOnly<T> = ExtractObjectValues<{
170
176
  [K in keyof T as T[K] extends undefined ? never : K]: K;
171
177
  }> & keyof T;
178
+ export type TruthyKeysOnly<T> = ExtractObjectValues<{
179
+ [K in keyof T as T[K] extends undefined | false ? never : K]: K;
180
+ }> & keyof T;
172
181
  export type BuildQueryResult<TSchema extends TablesRelationalConfig, TTableConfig extends TableRelationalConfig, TFullSelection extends true | Record<string, unknown>> = Equal<TFullSelection, true> extends true ? InferModelFromColumns<TTableConfig['columns']> : TFullSelection extends Record<string, unknown> ? Simplify<(TFullSelection['columns'] extends Record<string, unknown> ? InferModelFromColumns<{
173
182
  [K in Equal<Exclude<TFullSelection['columns'][keyof TFullSelection['columns'] & keyof TTableConfig['columns']], undefined>, false> extends true ? Exclude<keyof TTableConfig['columns'], NonUndefinedKeysOnly<TFullSelection['columns']>> : {
174
183
  [K in keyof TFullSelection['columns']]: Equal<TFullSelection['columns'][K], true> extends true ? K : never;
@@ -186,12 +195,13 @@ export interface BuildRelationalQueryResult {
186
195
  field: Column | Table | SQL | SQL.Aliased | SQLWrapper | AggregatedField;
187
196
  isArray?: boolean;
188
197
  selection?: BuildRelationalQueryResult['selection'];
198
+ isOptional?: boolean;
189
199
  }[];
190
200
  sql: SQL;
191
201
  }
192
202
  export declare function mapRelationalRow(row: Record<string, unknown>, buildQueryResultSelection: BuildRelationalQueryResult['selection'], mapColumnValue?: (value: unknown) => unknown,
193
203
  /** Needed for SQLite as it returns JSON values as strings */
194
- parseJson?: boolean): Record<string, unknown>;
204
+ parseJson?: boolean, path?: string): Record<string, unknown>;
195
205
  export declare class RelationsBuilderTable<TTableName extends string = string> implements SQLWrapper {
196
206
  static readonly [entityKind]: string;
197
207
  readonly _: {
@@ -211,7 +221,7 @@ export type RelationsBuilderColumnConfig<TTableName extends string = string, TDa
211
221
  readonly column: AnyColumn<{
212
222
  tableName: TTableName;
213
223
  }>;
214
- through?: RelationsBuilderColumnBase;
224
+ readonly through?: RelationsBuilderColumnBase;
215
225
  };
216
226
  export type RelationsBuilderColumnBase<TTableName extends string = string, TData = unknown> = {
217
227
  _: RelationsBuilderColumnConfig<TTableName, TData>;
@@ -224,33 +234,33 @@ export declare class RelationsBuilderColumn<TTableName extends string = string,
224
234
  readonly column: AnyColumn<{
225
235
  tableName: TTableName;
226
236
  }>;
227
- through?: RelationsBuilderColumnBase;
237
+ readonly through?: RelationsBuilderColumnBase;
228
238
  };
229
239
  constructor(column: AnyColumn<{
230
240
  tableName: TTableName;
231
- }>);
232
- through(column: RelationsBuilderColumnBase<string, TData>): Omit<this, 'through'>;
241
+ }>, through?: RelationsBuilderColumn);
242
+ through(column: RelationsBuilderColumn): RelationsBuilderColumnBase<TTableName, TData>;
233
243
  getSQL(): SQL;
234
244
  }
235
245
  export type RelationFieldsFilterInternals<T> = {
236
- eq?: T;
237
- ne?: T;
238
- gt?: T;
239
- gte?: T;
240
- lt?: T;
241
- lte?: T;
242
- in?: T[];
243
- notIn?: T[];
244
- like?: string;
245
- ilike?: string;
246
- notLike?: string;
247
- notIlike?: string;
246
+ eq?: T | Placeholder;
247
+ ne?: T | Placeholder;
248
+ gt?: T | Placeholder;
249
+ gte?: T | Placeholder;
250
+ lt?: T | Placeholder;
251
+ lte?: T | Placeholder;
252
+ in?: (T | Placeholder)[] | Placeholder;
253
+ notIn?: (T | Placeholder)[] | Placeholder;
254
+ like?: string | Placeholder;
255
+ ilike?: string | Placeholder;
256
+ notLike?: string | Placeholder;
257
+ notIlike?: string | Placeholder;
248
258
  isNull?: true;
249
259
  isNotNull?: true;
250
260
  NOT?: RelationsFieldFilter<T>;
251
261
  OR?: RelationsFieldFilter<T>[];
252
262
  };
253
- export type RelationsFieldFilter<T> = T | RelationFieldsFilterInternals<T | Placeholder>;
263
+ export type RelationsFieldFilter<T> = RelationFieldsFilterInternals<T> | (T extends Record<string, any> ? never : T);
254
264
  export type RelationsFilter<TColumns extends Record<string, Column>> = {
255
265
  [K in keyof TColumns]?: RelationsFieldFilter<TColumns[K]['_']['data']>;
256
266
  } & {
@@ -258,9 +268,7 @@ export type RelationsFilter<TColumns extends Record<string, Column>> = {
258
268
  NOT?: RelationsFilter<TColumns>;
259
269
  RAW?: (table: Simplify<AnyTable<{
260
270
  columns: TColumns;
261
- }> & {
262
- [K in keyof TColumns]: TColumns[K];
263
- }>, operators: Operators) => SQL;
271
+ }> & TColumns>, operators: Operators) => SQL;
264
272
  };
265
273
  export interface OneConfig<TSchema extends Record<string, Table>, TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | Readonly<RelationsBuilderColumnBase>, TTargetTableName extends string, TOptional extends boolean> {
266
274
  from?: TSourceColumns | Writable<TSourceColumns>;
@@ -282,7 +290,7 @@ export interface ManyConfig<TSchema extends Record<string, Table>, TSourceColumn
282
290
  }
283
291
  export type AnyManyConfig = ManyConfig<Record<string, Table>, Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | Readonly<RelationsBuilderColumnBase>, string>;
284
292
  export interface OneFn<TTables extends Record<string, Table>, TTargetTableName extends string> {
285
- <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any, TOptional extends boolean = false>(config?: OneConfig<TTables, TSourceColumns, TTargetTableName, TOptional>): One<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName, TOptional>;
293
+ <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any, TOptional extends boolean = true>(config?: OneConfig<TTables, TSourceColumns, TTargetTableName, TOptional>): One<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName, TOptional>;
286
294
  }
287
295
  export interface ManyFn<TTables extends Record<string, Table>, TTargetTableName extends string> {
288
296
  <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any>(config?: ManyConfig<TTables, TSourceColumns, TTargetTableName>): Many<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName>;
@@ -341,7 +349,12 @@ export declare function relationExtrasToSQL(table: Table, extras: Extras): {
341
349
  field: Column | Table | SQL | SQL.Aliased | SQLWrapper | AggregatedField;
342
350
  isArray?: boolean;
343
351
  selection?: BuildRelationalQueryResult["selection"];
352
+ isOptional?: boolean;
344
353
  }[];
345
354
  };
346
- export declare function relationToSQL(relation: Relation, sourceTable: Table, targetTable: Table): SQL | undefined;
355
+ export type BuiltRelationFilters = {
356
+ filter?: SQL;
357
+ joinCondition?: SQL;
358
+ };
359
+ export declare function relationToSQL(relation: Relation, sourceTable: Table, targetTable: Table, throughTable?: Table): BuiltRelationFilters;
347
360
  export {};
package/relations.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type AnyTable, type InferModelFromColumns, Table } from "./table.js";
2
2
  import { type AnyColumn, Column } from "./column.js";
3
3
  import { entityKind } from "./entity.js";
4
4
  import { and, asc, between, desc, exists, ilike, inArray, isNotNull, isNull, like, not, notBetween, notExists, notIlike, notInArray, notLike, or } from "./sql/expressions/index.js";
5
- import { Placeholder, SQL, sql, type SQLWrapper } from "./sql/sql.js";
5
+ import { type Placeholder, SQL, sql, type SQLWrapper } from "./sql/sql.js";
6
6
  import { type Assume, type Equal, type Simplify, type ValueOrArray, type Writable } from "./utils.js";
7
7
  export declare class Relations<TSchema extends Record<string, unknown> = Record<string, unknown>, TTables extends Record<string, Table> = Record<string, Table>, TConfig extends RelationsBuilderConfig<TTables> = RelationsBuilderConfig<TTables>> {
8
8
  readonly schema: TSchema;
@@ -35,6 +35,12 @@ export declare abstract class Relation<TSourceTableName extends string = string,
35
35
  sourceTable: AnyTable<{
36
36
  name: TSourceTableName;
37
37
  }>;
38
+ through?: {
39
+ source: AnyColumn[];
40
+ target: AnyColumn[];
41
+ };
42
+ throughTable?: Table;
43
+ isReversed?: boolean;
38
44
  constructor(targetTable: AnyTable<{
39
45
  name: TTargetTableName;
40
46
  }>);
@@ -122,7 +128,7 @@ export type DBQueryConfig<TRelationType extends 'one' | 'many' = 'one' | 'many',
122
128
  [K in keyof TTableConfig['columns']]?: boolean | undefined;
123
129
  } | undefined;
124
130
  with?: {
125
- [K in keyof TTableConfig['relations']]?: true | (TTableConfig['relations'][K] extends Relation ? DBQueryConfig<TTableConfig['relations'][K] extends One<string, string> ? 'one' : 'many', TSchema, FindTableInRelationalConfig<TSchema, TTableConfig['relations'][K]['targetTable']>> : never) | undefined;
131
+ [K in keyof TTableConfig['relations']]?: boolean | (TTableConfig['relations'][K] extends Relation ? DBQueryConfig<TTableConfig['relations'][K] extends One<string, string> ? 'one' : 'many', TSchema, FindTableInRelationalConfig<TSchema, TTableConfig['relations'][K]['targetTable']>> : never) | undefined;
126
132
  } | undefined;
127
133
  extras?: Record<string, SQLWrapper> | ((table: Simplify<AnyTable<TTableConfig> & TTableConfig['columns']>, operators: SQLOperator) => Record<string, SQLWrapper>) | undefined;
128
134
  offset?: number | Placeholder | undefined;
@@ -164,11 +170,14 @@ export type ExtractTablesWithRelations<TRelations extends Relations, TTables ext
164
170
  };
165
171
  export type ReturnTypeOrValue<T> = T extends (...args: any[]) => infer R ? R : T;
166
172
  export type BuildRelationResult<TConfig extends TablesRelationalConfig, TInclude, TRelations extends Record<string, RelationsBuilderEntry>> = {
167
- [K in NonUndefinedKeysOnly<TInclude> & keyof TRelations]: TRelations[K] extends infer TRel extends Relation ? BuildQueryResult<TConfig, FindTableInRelationalConfig<TConfig, TRel['targetTable']>, Assume<TInclude[K], true | Record<string, unknown>>> extends infer TResult ? TRel extends One<string, string> ? TResult | (Equal<TRel['optional'], true> extends true ? null : never) : TResult[] : never : TRelations[K] extends AggregatedField<infer TData> ? TData : never;
173
+ [K in TruthyKeysOnly<TInclude> & keyof TRelations]: TRelations[K] extends infer TRel extends Relation ? BuildQueryResult<TConfig, FindTableInRelationalConfig<TConfig, TRel['targetTable']>, Assume<TInclude[K], true | Record<string, unknown>>> extends infer TResult ? TRel extends One<string, string> ? TResult | (Equal<TRel['optional'], true> extends true ? null : TInclude[K] extends Record<string, unknown> ? TInclude[K]['where'] extends Record<string, any> ? null : never : never) : TResult[] : never : TRelations[K] extends AggregatedField<infer TData> ? TData : never;
168
174
  };
169
175
  export type NonUndefinedKeysOnly<T> = ExtractObjectValues<{
170
176
  [K in keyof T as T[K] extends undefined ? never : K]: K;
171
177
  }> & keyof T;
178
+ export type TruthyKeysOnly<T> = ExtractObjectValues<{
179
+ [K in keyof T as T[K] extends undefined | false ? never : K]: K;
180
+ }> & keyof T;
172
181
  export type BuildQueryResult<TSchema extends TablesRelationalConfig, TTableConfig extends TableRelationalConfig, TFullSelection extends true | Record<string, unknown>> = Equal<TFullSelection, true> extends true ? InferModelFromColumns<TTableConfig['columns']> : TFullSelection extends Record<string, unknown> ? Simplify<(TFullSelection['columns'] extends Record<string, unknown> ? InferModelFromColumns<{
173
182
  [K in Equal<Exclude<TFullSelection['columns'][keyof TFullSelection['columns'] & keyof TTableConfig['columns']], undefined>, false> extends true ? Exclude<keyof TTableConfig['columns'], NonUndefinedKeysOnly<TFullSelection['columns']>> : {
174
183
  [K in keyof TFullSelection['columns']]: Equal<TFullSelection['columns'][K], true> extends true ? K : never;
@@ -186,12 +195,13 @@ export interface BuildRelationalQueryResult {
186
195
  field: Column | Table | SQL | SQL.Aliased | SQLWrapper | AggregatedField;
187
196
  isArray?: boolean;
188
197
  selection?: BuildRelationalQueryResult['selection'];
198
+ isOptional?: boolean;
189
199
  }[];
190
200
  sql: SQL;
191
201
  }
192
202
  export declare function mapRelationalRow(row: Record<string, unknown>, buildQueryResultSelection: BuildRelationalQueryResult['selection'], mapColumnValue?: (value: unknown) => unknown,
193
203
  /** Needed for SQLite as it returns JSON values as strings */
194
- parseJson?: boolean): Record<string, unknown>;
204
+ parseJson?: boolean, path?: string): Record<string, unknown>;
195
205
  export declare class RelationsBuilderTable<TTableName extends string = string> implements SQLWrapper {
196
206
  static readonly [entityKind]: string;
197
207
  readonly _: {
@@ -211,7 +221,7 @@ export type RelationsBuilderColumnConfig<TTableName extends string = string, TDa
211
221
  readonly column: AnyColumn<{
212
222
  tableName: TTableName;
213
223
  }>;
214
- through?: RelationsBuilderColumnBase;
224
+ readonly through?: RelationsBuilderColumnBase;
215
225
  };
216
226
  export type RelationsBuilderColumnBase<TTableName extends string = string, TData = unknown> = {
217
227
  _: RelationsBuilderColumnConfig<TTableName, TData>;
@@ -224,33 +234,33 @@ export declare class RelationsBuilderColumn<TTableName extends string = string,
224
234
  readonly column: AnyColumn<{
225
235
  tableName: TTableName;
226
236
  }>;
227
- through?: RelationsBuilderColumnBase;
237
+ readonly through?: RelationsBuilderColumnBase;
228
238
  };
229
239
  constructor(column: AnyColumn<{
230
240
  tableName: TTableName;
231
- }>);
232
- through(column: RelationsBuilderColumnBase<string, TData>): Omit<this, 'through'>;
241
+ }>, through?: RelationsBuilderColumn);
242
+ through(column: RelationsBuilderColumn): RelationsBuilderColumnBase<TTableName, TData>;
233
243
  getSQL(): SQL;
234
244
  }
235
245
  export type RelationFieldsFilterInternals<T> = {
236
- eq?: T;
237
- ne?: T;
238
- gt?: T;
239
- gte?: T;
240
- lt?: T;
241
- lte?: T;
242
- in?: T[];
243
- notIn?: T[];
244
- like?: string;
245
- ilike?: string;
246
- notLike?: string;
247
- notIlike?: string;
246
+ eq?: T | Placeholder;
247
+ ne?: T | Placeholder;
248
+ gt?: T | Placeholder;
249
+ gte?: T | Placeholder;
250
+ lt?: T | Placeholder;
251
+ lte?: T | Placeholder;
252
+ in?: (T | Placeholder)[] | Placeholder;
253
+ notIn?: (T | Placeholder)[] | Placeholder;
254
+ like?: string | Placeholder;
255
+ ilike?: string | Placeholder;
256
+ notLike?: string | Placeholder;
257
+ notIlike?: string | Placeholder;
248
258
  isNull?: true;
249
259
  isNotNull?: true;
250
260
  NOT?: RelationsFieldFilter<T>;
251
261
  OR?: RelationsFieldFilter<T>[];
252
262
  };
253
- export type RelationsFieldFilter<T> = T | RelationFieldsFilterInternals<T | Placeholder>;
263
+ export type RelationsFieldFilter<T> = RelationFieldsFilterInternals<T> | (T extends Record<string, any> ? never : T);
254
264
  export type RelationsFilter<TColumns extends Record<string, Column>> = {
255
265
  [K in keyof TColumns]?: RelationsFieldFilter<TColumns[K]['_']['data']>;
256
266
  } & {
@@ -258,9 +268,7 @@ export type RelationsFilter<TColumns extends Record<string, Column>> = {
258
268
  NOT?: RelationsFilter<TColumns>;
259
269
  RAW?: (table: Simplify<AnyTable<{
260
270
  columns: TColumns;
261
- }> & {
262
- [K in keyof TColumns]: TColumns[K];
263
- }>, operators: Operators) => SQL;
271
+ }> & TColumns>, operators: Operators) => SQL;
264
272
  };
265
273
  export interface OneConfig<TSchema extends Record<string, Table>, TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | Readonly<RelationsBuilderColumnBase>, TTargetTableName extends string, TOptional extends boolean> {
266
274
  from?: TSourceColumns | Writable<TSourceColumns>;
@@ -282,7 +290,7 @@ export interface ManyConfig<TSchema extends Record<string, Table>, TSourceColumn
282
290
  }
283
291
  export type AnyManyConfig = ManyConfig<Record<string, Table>, Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | Readonly<RelationsBuilderColumnBase>, string>;
284
292
  export interface OneFn<TTables extends Record<string, Table>, TTargetTableName extends string> {
285
- <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any, TOptional extends boolean = false>(config?: OneConfig<TTables, TSourceColumns, TTargetTableName, TOptional>): One<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName, TOptional>;
293
+ <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any, TOptional extends boolean = true>(config?: OneConfig<TTables, TSourceColumns, TTargetTableName, TOptional>): One<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName, TOptional>;
286
294
  }
287
295
  export interface ManyFn<TTables extends Record<string, Table>, TTargetTableName extends string> {
288
296
  <TSourceColumns extends Readonly<[RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]]> | RelationsBuilderColumnBase = any>(config?: ManyConfig<TTables, TSourceColumns, TTargetTableName>): Many<TSourceColumns extends [RelationsBuilderColumnBase, ...RelationsBuilderColumnBase[]] ? TSourceColumns[number]['_']['tableName'] : Assume<TSourceColumns, RelationsBuilderColumnBase>['_']['tableName'], TTargetTableName>;
@@ -341,7 +349,12 @@ export declare function relationExtrasToSQL(table: Table, extras: Extras): {
341
349
  field: Column | Table | SQL | SQL.Aliased | SQLWrapper | AggregatedField;
342
350
  isArray?: boolean;
343
351
  selection?: BuildRelationalQueryResult["selection"];
352
+ isOptional?: boolean;
344
353
  }[];
345
354
  };
346
- export declare function relationToSQL(relation: Relation, sourceTable: Table, targetTable: Table): SQL | undefined;
355
+ export type BuiltRelationFilters = {
356
+ filter?: SQL;
357
+ joinCondition?: SQL;
358
+ };
359
+ export declare function relationToSQL(relation: Relation, sourceTable: Table, targetTable: Table, throughTable?: Table): BuiltRelationFilters;
347
360
  export {};
package/relations.js CHANGED
@@ -1,4 +1,10 @@
1
- import { getTableUniqueName, IsAlias, Schema, Table } from "./table.js";
1
+ import {
2
+ getTableUniqueName,
3
+ IsAlias,
4
+ OriginalName,
5
+ Schema,
6
+ Table
7
+ } from "./table.js";
2
8
  import { Columns, getTableName } from "./table.js";
3
9
  import { Column } from "./column.js";
4
10
  import { entityKind, is } from "./entity.js";
@@ -28,7 +34,7 @@ import {
28
34
  notLike,
29
35
  or
30
36
  } from "./sql/expressions/index.js";
31
- import { Placeholder, SQL, sql } from "./sql/sql.js";
37
+ import { SQL, sql } from "./sql/sql.js";
32
38
  import { getTableColumns } from "./utils.js";
33
39
  class Relations {
34
40
  constructor(schema, tables, config) {
@@ -89,9 +95,30 @@ class Relations {
89
95
  if (relation.sourceColumns && relation.targetColumns) {
90
96
  if (relation.sourceColumns.length !== relation.targetColumns.length) {
91
97
  throw new Error(
92
- `${relationPrintName}: "from" and "to" arrays must have the same length`
98
+ `${relationPrintName}: "from" and "to" fields must have the same length`
93
99
  );
94
100
  }
101
+ if (relation.through) {
102
+ if (relation.through.source.length !== relation.through.target.length || relation.through.source.length !== relation.sourceColumns.length || relation.through.target.length !== relation.targetColumns.length) {
103
+ throw new Error(
104
+ `${relationPrintName}: ".through(column)" must be used either on all columns in "from" and "to" or not defined on any of them`
105
+ );
106
+ }
107
+ for (const column of relation.through.source) {
108
+ if (column.table !== relation.throughTable) {
109
+ throw new Error(
110
+ `${relationPrintName}: ".through(column)" must be used on the same table by all columns of the relation`
111
+ );
112
+ }
113
+ }
114
+ for (const column of relation.through.target) {
115
+ if (column.table !== relation.throughTable) {
116
+ throw new Error(
117
+ `${relationPrintName}: ".through(column)" must be used on the same table by all columns of the relation`
118
+ );
119
+ }
120
+ }
121
+ }
95
122
  continue;
96
123
  }
97
124
  if (relation.sourceColumns || relation.targetColumns) {
@@ -151,7 +178,13 @@ Hint: you can specify "alias" on both sides of the relation with the same value`
151
178
  }
152
179
  relation.sourceColumns = reverseRelation.targetColumns;
153
180
  relation.targetColumns = reverseRelation.sourceColumns;
154
- relation.where = reverseRelation.where;
181
+ relation.through = reverseRelation.through ? {
182
+ source: reverseRelation.through.target,
183
+ target: reverseRelation.through.source
184
+ } : void 0;
185
+ relation.throughTable = reverseRelation.throughTable;
186
+ relation.isReversed = !relation.where;
187
+ relation.where = relation.where ?? reverseRelation.where;
155
188
  }
156
189
  }
157
190
  }
@@ -171,6 +204,9 @@ class Relation {
171
204
  alias;
172
205
  where;
173
206
  sourceTable;
207
+ through;
208
+ throughTable;
209
+ isReversed;
174
210
  }
175
211
  class One extends Relation {
176
212
  static [entityKind] = "OneV2";
@@ -180,12 +216,30 @@ class One extends Relation {
180
216
  this.alias = config?.alias;
181
217
  this.where = config?.where;
182
218
  if (config?.from) {
183
- this.sourceColumns = Array.isArray(config.from) ? config.from.map((it) => it._.column) : [config.from._.column];
219
+ this.sourceColumns = (Array.isArray(config.from) ? config.from : [config.from]).map((it) => {
220
+ this.throughTable ??= it._.through?._.column.table;
221
+ return it._.column;
222
+ });
184
223
  }
185
224
  if (config?.to) {
186
- this.targetColumns = Array.isArray(config.to) ? config.to.map((it) => it._.column) : [config.to._.column];
225
+ this.targetColumns = (Array.isArray(config.to) ? config.to : [config.to]).map((it) => {
226
+ this.throughTable ??= it._.through?._.column.table;
227
+ return it._.column;
228
+ });
229
+ }
230
+ if (this.throughTable) {
231
+ this.through = Array.isArray(config?.from) ? {
232
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain -- in case it's undefined, error will be thrown in Relations constructor
233
+ source: config.from.map((e) => e._.through?._.column),
234
+ target: (config.to ?? []).map((e) => e._.column)
235
+ } : {
236
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain -- in case it's undefined, error will be thrown in Relations constructor
237
+ source: [config?.from?._.through?._.column],
238
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
239
+ target: [config?.to?._.through?._.column]
240
+ };
187
241
  }
188
- this.optional = config?.optional ?? false;
242
+ this.optional = config?.optional ?? true;
189
243
  }
190
244
  }
191
245
  class Many extends Relation {
@@ -195,10 +249,28 @@ class Many extends Relation {
195
249
  this.alias = config?.alias;
196
250
  this.where = config?.where;
197
251
  if (config?.from) {
198
- this.sourceColumns = Array.isArray(config.from) ? config.from.map((it) => it._.column) : [config.from._.column];
252
+ this.sourceColumns = (Array.isArray(config.from) ? config.from : [config.from]).map((it) => {
253
+ this.throughTable ??= it._.through?._.column.table;
254
+ return it._.column;
255
+ });
199
256
  }
200
257
  if (config?.to) {
201
- this.targetColumns = Array.isArray(config.to) ? config.to.map((it) => it._.column) : [config.to._.column];
258
+ this.targetColumns = (Array.isArray(config.to) ? config.to : [config.to]).map((it) => {
259
+ this.throughTable ??= it._.through?._.column.table;
260
+ return it._.column;
261
+ });
262
+ }
263
+ if (this.throughTable) {
264
+ this.through = Array.isArray(config?.from) ? {
265
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain -- in case it's undefined, error will be thrown in Relations constructor
266
+ source: config.from.map((e) => e._.through?._.column),
267
+ target: (config.to ?? []).map((e) => e._.column)
268
+ } : {
269
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain -- in case it's undefined, error will be thrown in Relations constructor
270
+ source: [config?.from?._.through?._.column],
271
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
272
+ target: [config?.to?._.through?._.column]
273
+ };
202
274
  }
203
275
  }
204
276
  static [entityKind] = "ManyV2";
@@ -218,7 +290,8 @@ class Count extends AggregatedField {
218
290
  if (!this.query) {
219
291
  if (!this.table)
220
292
  throw new Error("Table must be set before building aggregate field");
221
- this.query = sql`select count(*) as ${sql.identifier("r")} from ${this.table}`.mapWith(Number);
293
+ const table = this.table;
294
+ this.query = sql`select count(*) as ${sql.identifier("r")} from ${table[IsAlias] ? sql`${sql`${sql.identifier(table[Schema] ?? "")}.`.if(table[Schema])}${sql.identifier(table[OriginalName])} as ${table}` : table}`.mapWith(Number);
222
295
  }
223
296
  return this.query;
224
297
  }
@@ -255,24 +328,33 @@ const orderByOperators = {
255
328
  function getOrderByOperators() {
256
329
  return orderByOperators;
257
330
  }
258
- function mapRelationalRow(row, buildQueryResultSelection, mapColumnValue = (value) => value, parseJson = false) {
331
+ function mapRelationalRow(row, buildQueryResultSelection, mapColumnValue = (value) => value, parseJson = false, path) {
259
332
  for (const selectionItem of buildQueryResultSelection) {
260
333
  const field = selectionItem.field;
261
334
  if (is(field, Table)) {
335
+ const currentPath = `${path ? `${path}.` : ""}${selectionItem.key}`;
262
336
  if (row[selectionItem.key] === null)
263
337
  continue;
264
338
  if (parseJson)
265
339
  row[selectionItem.key] = JSON.parse(row[selectionItem.key]);
266
340
  if (selectionItem.isArray) {
267
341
  for (const item of row[selectionItem.key]) {
268
- mapRelationalRow(item, selectionItem.selection, mapColumnValue);
342
+ mapRelationalRow(
343
+ item,
344
+ selectionItem.selection,
345
+ mapColumnValue,
346
+ false,
347
+ currentPath
348
+ );
269
349
  }
270
350
  continue;
271
351
  }
272
352
  mapRelationalRow(
273
353
  row[selectionItem.key],
274
354
  selectionItem.selection,
275
- mapColumnValue
355
+ mapColumnValue,
356
+ false,
357
+ currentPath
276
358
  );
277
359
  continue;
278
360
  }
@@ -309,16 +391,16 @@ class RelationsBuilderTable {
309
391
  class RelationsBuilderColumn {
310
392
  static [entityKind] = "RelationsBuilderColumn";
311
393
  _;
312
- constructor(column) {
394
+ constructor(column, through) {
313
395
  this._ = {
314
396
  tableName: getTableName(column.table),
315
397
  data: void 0,
316
- column
398
+ column,
399
+ through
317
400
  };
318
401
  }
319
402
  through(column) {
320
- this._.through = column;
321
- return this;
403
+ return new RelationsBuilderColumn(this._.column, column);
322
404
  }
323
405
  getSQL() {
324
406
  return this._.column.getSQL();
@@ -382,7 +464,7 @@ function defineRelations(schema, relations) {
382
464
  );
383
465
  }
384
466
  function relationsFieldFilterToSQL(column, filter) {
385
- if (typeof filter !== "object" || is(filter, Placeholder))
467
+ if (typeof filter !== "object")
386
468
  return eq(column, filter);
387
469
  const entries = Object.entries(filter);
388
470
  if (!entries.length)
@@ -409,6 +491,21 @@ function relationsFieldFilterToSQL(column, filter) {
409
491
  );
410
492
  continue;
411
493
  }
494
+ case "isNotNull":
495
+ case "isNull": {
496
+ if (!value)
497
+ continue;
498
+ parts.push(operators[target](column));
499
+ continue;
500
+ }
501
+ case "in": {
502
+ parts.push(operators.inArray(column, value));
503
+ continue;
504
+ }
505
+ case "notIn": {
506
+ parts.push(operators.notInArray(column, value));
507
+ continue;
508
+ }
412
509
  default: {
413
510
  parts.push(
414
511
  operators[target](
@@ -436,7 +533,7 @@ function relationsFilterToSQL(table, filter) {
436
533
  case "RAW": {
437
534
  if (value) {
438
535
  parts.push(
439
- value(table[Columns], operators)
536
+ value(table, operators)
440
537
  );
441
538
  }
442
539
  continue;
@@ -506,16 +603,44 @@ function relationExtrasToSQL(table, extras) {
506
603
  selection
507
604
  };
508
605
  }
509
- function relationToSQL(relation, sourceTable, targetTable) {
606
+ function relationToSQL(relation, sourceTable, targetTable, throughTable) {
607
+ if (relation.through) {
608
+ const outerColumnWhere = relation.sourceColumns.map((s, i) => {
609
+ const t = relation.through.source[i];
610
+ return eq(
611
+ sql`${sourceTable}.${sql.identifier(s.name)}`,
612
+ sql`${throughTable}.${sql.identifier(t.name)}`
613
+ );
614
+ });
615
+ const innerColumnWhere = relation.targetColumns.map((s, i) => {
616
+ const t = relation.through.target[i];
617
+ return eq(
618
+ sql`${throughTable}.${sql.identifier(t.name)}`,
619
+ sql`${targetTable}.${sql.identifier(s.name)}`
620
+ );
621
+ });
622
+ return {
623
+ filter: and(
624
+ relation.where ? relation.isReversed ? relationsFilterToSQL(targetTable, relation.where) : relationsFilterToSQL(sourceTable, relation.where) : void 0
625
+ ),
626
+ joinCondition: and(
627
+ ...outerColumnWhere,
628
+ ...innerColumnWhere
629
+ )
630
+ };
631
+ }
510
632
  const columnWhere = relation.sourceColumns.map((s, i) => {
511
633
  const t = relation.targetColumns[i];
512
634
  return eq(
513
- sql`${sql`${sql`${sql.identifier(sourceTable[Schema] ?? "")}.`.if(sourceTable[Schema] && !sourceTable[IsAlias])}`}${sourceTable}.${sql.identifier(s.name)}`,
514
- sql`${sql`${sql`${sql.identifier(targetTable[Schema] ?? "")}.`.if(targetTable[Schema] && !targetTable[IsAlias])}`}${targetTable}.${sql.identifier(t.name)}`
635
+ sql`${sourceTable}.${sql.identifier(s.name)}`,
636
+ sql`${targetTable}.${sql.identifier(t.name)}`
515
637
  );
516
638
  });
517
- const targetWhere = relation.where ? and(...columnWhere, relationsFilterToSQL(sourceTable, relation.where)) : and(...columnWhere);
518
- return targetWhere;
639
+ const fullWhere = and(
640
+ ...columnWhere,
641
+ relation.where ? relation.isReversed ? relationsFilterToSQL(targetTable, relation.where) : relationsFilterToSQL(sourceTable, relation.where) : void 0
642
+ );
643
+ return { filter: fullWhere };
519
644
  }
520
645
  export {
521
646
  AggregatedField,