metal-orm 1.0.17 → 1.0.18

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 (39) hide show
  1. package/README.md +4 -3
  2. package/dist/decorators/index.cjs +192 -46
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -1
  5. package/dist/decorators/index.d.ts +1 -1
  6. package/dist/decorators/index.js +192 -46
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +245 -66
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +16 -29
  11. package/dist/index.d.ts +16 -29
  12. package/dist/index.js +243 -66
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BPCn6MOH.d.cts → select-BuMpVcVt.d.cts} +83 -11
  15. package/dist/{select-BPCn6MOH.d.ts → select-BuMpVcVt.d.ts} +83 -11
  16. package/package.json +4 -1
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/builders.ts +23 -3
  19. package/src/core/ast/expression-builders.ts +14 -1
  20. package/src/core/ast/expression-nodes.ts +11 -9
  21. package/src/core/ast/join-node.ts +5 -3
  22. package/src/core/ast/join.ts +16 -16
  23. package/src/core/ast/query.ts +44 -29
  24. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  25. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  26. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  28. package/src/core/dialect/base/sql-dialect.ts +58 -46
  29. package/src/core/dialect/mssql/index.ts +53 -28
  30. package/src/core/dialect/sqlite/index.ts +22 -13
  31. package/src/query-builder/column-selector.ts +9 -7
  32. package/src/query-builder/query-ast-service.ts +59 -38
  33. package/src/query-builder/relation-conditions.ts +38 -34
  34. package/src/query-builder/relation-manager.ts +8 -3
  35. package/src/query-builder/relation-service.ts +59 -46
  36. package/src/query-builder/select-query-state.ts +19 -7
  37. package/src/query-builder/select.ts +215 -135
  38. package/src/schema/column.ts +75 -39
  39. package/src/schema/types.ts +1 -0
@@ -6,27 +6,30 @@ import { SelectQueryNode, SetOperationKind } from '../core/ast/query.js';
6
6
 
7
7
  import { HydrationPlan } from '../core/hydration/types.js';
8
8
 
9
- import {
10
-
11
- ColumnNode,
12
-
13
- ExpressionNode,
14
-
15
- FunctionNode,
16
-
17
- LiteralNode,
18
-
19
- BinaryExpressionNode,
20
-
21
- CaseExpressionNode,
22
-
23
- WindowFunctionNode,
24
-
25
- exists,
26
-
27
- notExists
28
-
29
- } from '../core/ast/expression.js';
9
+ import {
10
+
11
+ ColumnNode,
12
+
13
+ ExpressionNode,
14
+
15
+ FunctionNode,
16
+
17
+ LiteralNode,
18
+
19
+ BinaryExpressionNode,
20
+
21
+ CaseExpressionNode,
22
+
23
+ WindowFunctionNode,
24
+
25
+ and,
26
+
27
+ exists,
28
+
29
+ notExists
30
+
31
+ } from '../core/ast/expression.js';
32
+ import { derivedTable } from '../core/ast/builders.js';
30
33
 
31
34
  import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
32
35
 
@@ -87,6 +90,14 @@ type DeepSelectConfig<TTable extends TableDef> = {
87
90
  )[];
88
91
  };
89
92
 
93
+ type WhereHasOptions = {
94
+ correlate?: ExpressionNode;
95
+ };
96
+
97
+ type RelationCallback = <TChildTable extends TableDef>(
98
+ qb: SelectQueryBuilder<any, TChildTable>
99
+ ) => SelectQueryBuilder<any, TChildTable>;
100
+
90
101
 
91
102
  /**
92
103
 
@@ -166,29 +177,52 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
166
177
 
167
178
 
168
179
 
169
- private clone(
170
-
171
- context: SelectQueryBuilderContext = this.context,
172
-
173
- lazyRelations = new Set(this.lazyRelations)
174
-
175
- ): SelectQueryBuilder<T, TTable> {
176
-
177
- return new SelectQueryBuilder(this.env.table as TTable, context.state, context.hydration, this.env.deps, lazyRelations);
178
-
179
- }
180
-
181
-
182
-
183
- private resolveQueryNode(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryNode {
184
-
185
- return typeof (query as any).getAST === 'function'
180
+ private clone(
181
+
182
+ context: SelectQueryBuilderContext = this.context,
183
+
184
+ lazyRelations = new Set(this.lazyRelations)
185
+
186
+ ): SelectQueryBuilder<T, TTable> {
187
+
188
+ return new SelectQueryBuilder(this.env.table as TTable, context.state, context.hydration, this.env.deps, lazyRelations);
189
+
190
+ }
191
+
192
+ /**
193
+ * Applies an alias to the root FROM table.
194
+ * @param alias - Alias to apply
195
+ */
196
+ as(alias: string): SelectQueryBuilder<T, TTable> {
197
+ const from = this.context.state.ast.from;
198
+ if (from.type !== 'Table') {
199
+ throw new Error('Cannot alias non-table FROM sources');
200
+ }
201
+ const nextFrom = { ...from, alias };
202
+ const nextContext = this.applyAst(this.context, service => service.withFrom(nextFrom));
203
+ return this.clone(nextContext);
204
+ }
186
205
 
187
- ? (query as SelectQueryBuilder<any, TableDef<any>>).getAST()
188
206
 
189
- : (query as SelectQueryNode);
190
207
 
191
- }
208
+ private resolveQueryNode(query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryNode {
209
+
210
+ return typeof (query as any).getAST === 'function'
211
+
212
+ ? (query as SelectQueryBuilder<any, TableDef<any>>).getAST()
213
+
214
+ : (query as SelectQueryNode);
215
+
216
+ }
217
+
218
+ private applyCorrelation(ast: SelectQueryNode, correlation?: ExpressionNode): SelectQueryNode {
219
+ if (!correlation) return ast;
220
+ const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
221
+ return {
222
+ ...ast,
223
+ where: combinedWhere
224
+ };
225
+ }
192
226
 
193
227
 
194
228
 
@@ -349,15 +383,34 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
349
383
 
350
384
  */
351
385
 
352
- withRecursive(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
353
-
354
- const subAst = this.resolveQueryNode(query);
355
-
356
- const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, true));
357
-
358
- return this.clone(nextContext);
359
-
360
- }
386
+ withRecursive(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
387
+
388
+ const subAst = this.resolveQueryNode(query);
389
+
390
+ const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, true));
391
+
392
+ return this.clone(nextContext);
393
+
394
+ }
395
+
396
+
397
+ /**
398
+ * Replaces the FROM clause with a derived table (subquery with alias)
399
+ * @param subquery - Subquery to use as the FROM source
400
+ * @param alias - Alias for the derived table
401
+ * @param columnAliases - Optional column alias list
402
+ * @returns New query builder instance with updated FROM
403
+ */
404
+ fromSubquery(
405
+ subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode,
406
+ alias: string,
407
+ columnAliases?: string[]
408
+ ): SelectQueryBuilder<T, TTable> {
409
+ const subAst = this.resolveQueryNode(subquery);
410
+ const fromNode = derivedTable(subAst, alias, columnAliases);
411
+ const nextContext = this.applyAst(this.context, service => service.withFrom(fromNode));
412
+ return this.clone(nextContext);
413
+ }
361
414
 
362
415
 
363
416
 
@@ -373,14 +426,37 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
373
426
 
374
427
  */
375
428
 
376
- selectSubquery(alias: string, sub: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
377
-
378
- const query = this.resolveQueryNode(sub);
379
-
380
- return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
381
-
382
- }
383
-
429
+ selectSubquery(alias: string, sub: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
430
+
431
+ const query = this.resolveQueryNode(sub);
432
+
433
+ return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
434
+
435
+ }
436
+
437
+
438
+ /**
439
+ * Adds a JOIN against a derived table (subquery with alias)
440
+ * @param subquery - Subquery to join
441
+ * @param alias - Alias for the derived table
442
+ * @param condition - Join condition expression
443
+ * @param joinKind - Join kind (defaults to INNER)
444
+ * @param columnAliases - Optional column alias list for the derived table
445
+ * @returns New query builder instance with the derived-table join
446
+ */
447
+ joinSubquery(
448
+ subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode,
449
+ alias: string,
450
+ condition: BinaryExpressionNode,
451
+ joinKind: JoinKind = JOIN_KINDS.INNER,
452
+ columnAliases?: string[]
453
+ ): SelectQueryBuilder<T, TTable> {
454
+ const subAst = this.resolveQueryNode(subquery);
455
+ const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
456
+ const nextContext = this.applyAst(this.context, service => service.withJoin(joinNode));
457
+ return this.clone(nextContext);
458
+ }
459
+
384
460
 
385
461
 
386
462
  /**
@@ -852,13 +928,14 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
852
928
 
853
929
  */
854
930
 
855
- whereExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
856
-
857
- const subAst = this.resolveQueryNode(subquery);
858
-
859
- return this.where(exists(subAst));
860
-
861
- }
931
+ whereExists(
932
+ subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode,
933
+ correlate?: ExpressionNode
934
+ ): SelectQueryBuilder<T, TTable> {
935
+ const subAst = this.resolveQueryNode(subquery);
936
+ const correlated = this.applyCorrelation(subAst, correlate);
937
+ return this.where(exists(correlated));
938
+ }
862
939
 
863
940
 
864
941
 
@@ -872,13 +949,14 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
872
949
 
873
950
  */
874
951
 
875
- whereNotExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
876
-
877
- const subAst = this.resolveQueryNode(subquery);
878
-
879
- return this.where(notExists(subAst));
880
-
881
- }
952
+ whereNotExists(
953
+ subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode,
954
+ correlate?: ExpressionNode
955
+ ): SelectQueryBuilder<T, TTable> {
956
+ const subAst = this.resolveQueryNode(subquery);
957
+ const correlated = this.applyCorrelation(subAst, correlate);
958
+ return this.where(notExists(correlated));
959
+ }
882
960
 
883
961
 
884
962
 
@@ -894,45 +972,46 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
894
972
 
895
973
  */
896
974
 
897
- whereHas(
898
-
899
- relationName: string,
900
-
901
- callback?: <TChildTable extends TableDef>(
902
-
903
- qb: SelectQueryBuilder<any, TChildTable>
904
-
905
- ) => SelectQueryBuilder<any, TChildTable>
906
-
907
- ): SelectQueryBuilder<T, TTable> {
908
-
909
- const relation = this.env.table.relations[relationName];
910
-
911
- if (!relation) {
912
-
975
+ whereHas(
976
+
977
+ relationName: string,
978
+
979
+ callbackOrOptions?: RelationCallback | WhereHasOptions,
980
+
981
+ maybeOptions?: WhereHasOptions
982
+
983
+ ): SelectQueryBuilder<T, TTable> {
984
+
985
+ const relation = this.env.table.relations[relationName];
986
+
987
+ if (!relation) {
988
+
913
989
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
914
990
 
915
991
  }
916
992
 
917
993
 
918
994
 
919
- let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
920
-
921
- if (callback) {
922
-
923
- subQb = callback(subQb);
924
-
925
- }
926
-
927
-
928
-
929
- const subAst = subQb.getAST();
995
+ const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
996
+ const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
997
+
998
+ let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
999
+
1000
+ if (callback) {
1001
+
1002
+ subQb = callback(subQb);
1003
+
1004
+ }
930
1005
 
931
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
932
1006
 
933
- return this.where(exists(finalSubAst));
934
1007
 
935
- }
1008
+ const subAst = subQb.getAST();
1009
+
1010
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
1011
+
1012
+ return this.where(exists(finalSubAst));
1013
+
1014
+ }
936
1015
 
937
1016
 
938
1017
 
@@ -948,45 +1027,46 @@ export class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
948
1027
 
949
1028
  */
950
1029
 
951
- whereHasNot(
952
-
953
- relationName: string,
954
-
955
- callback?: <TChildTable extends TableDef>(
956
-
957
- qb: SelectQueryBuilder<any, TChildTable>
958
-
959
- ) => SelectQueryBuilder<any, TChildTable>
960
-
961
- ): SelectQueryBuilder<T, TTable> {
962
-
963
- const relation = this.env.table.relations[relationName];
964
-
965
- if (!relation) {
966
-
1030
+ whereHasNot(
1031
+
1032
+ relationName: string,
1033
+
1034
+ callbackOrOptions?: RelationCallback | WhereHasOptions,
1035
+
1036
+ maybeOptions?: WhereHasOptions
1037
+
1038
+ ): SelectQueryBuilder<T, TTable> {
1039
+
1040
+ const relation = this.env.table.relations[relationName];
1041
+
1042
+ if (!relation) {
1043
+
967
1044
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
968
1045
 
969
1046
  }
970
1047
 
971
1048
 
972
1049
 
973
- let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
974
-
975
- if (callback) {
976
-
977
- subQb = callback(subQb);
978
-
979
- }
980
-
981
-
982
-
983
- const subAst = subQb.getAST();
984
-
985
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
986
-
987
- return this.where(notExists(finalSubAst));
988
-
989
- }
1050
+ const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
1051
+ const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
1052
+
1053
+ let subQb = this.createChildBuilder<any, typeof relation.target>(relation.target);
1054
+
1055
+ if (callback) {
1056
+
1057
+ subQb = callback(subQb);
1058
+
1059
+ }
1060
+
1061
+
1062
+
1063
+ const subAst = subQb.getAST();
1064
+
1065
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
1066
+
1067
+ return this.where(notExists(finalSubAst));
1068
+
1069
+ }
990
1070
 
991
1071
 
992
1072
 
@@ -1,39 +1,47 @@
1
1
  /**
2
2
  * Supported column data types for database schema definitions
3
3
  */
4
- export type ColumnType =
5
- | 'INT'
6
- | 'INTEGER'
7
- | 'BIGINT'
8
- | 'VARCHAR'
9
- | 'TEXT'
10
- | 'JSON'
11
- | 'ENUM'
12
- | 'DECIMAL'
13
- | 'FLOAT'
14
- | 'DOUBLE'
15
- | 'UUID'
16
- | 'DATE'
17
- | 'DATETIME'
18
- | 'TIMESTAMP'
19
- | 'TIMESTAMPTZ'
20
- | 'BOOLEAN'
21
- | 'int'
22
- | 'integer'
23
- | 'bigint'
24
- | 'varchar'
25
- | 'text'
26
- | 'json'
27
- | 'enum'
28
- | 'decimal'
29
- | 'float'
30
- | 'double'
31
- | 'uuid'
32
- | 'date'
33
- | 'datetime'
34
- | 'timestamp'
35
- | 'timestamptz'
36
- | 'boolean';
4
+ export type ColumnType =
5
+ | 'INT'
6
+ | 'INTEGER'
7
+ | 'BIGINT'
8
+ | 'VARCHAR'
9
+ | 'TEXT'
10
+ | 'JSON'
11
+ | 'ENUM'
12
+ | 'DECIMAL'
13
+ | 'FLOAT'
14
+ | 'DOUBLE'
15
+ | 'UUID'
16
+ | 'BINARY'
17
+ | 'VARBINARY'
18
+ | 'BLOB'
19
+ | 'BYTEA'
20
+ | 'DATE'
21
+ | 'DATETIME'
22
+ | 'TIMESTAMP'
23
+ | 'TIMESTAMPTZ'
24
+ | 'BOOLEAN'
25
+ | 'int'
26
+ | 'integer'
27
+ | 'bigint'
28
+ | 'varchar'
29
+ | 'text'
30
+ | 'json'
31
+ | 'enum'
32
+ | 'decimal'
33
+ | 'float'
34
+ | 'double'
35
+ | 'uuid'
36
+ | 'binary'
37
+ | 'varbinary'
38
+ | 'blob'
39
+ | 'bytea'
40
+ | 'date'
41
+ | 'datetime'
42
+ | 'timestamp'
43
+ | 'timestamptz'
44
+ | 'boolean';
37
45
 
38
46
  export type ReferentialAction =
39
47
  | 'NO ACTION'
@@ -138,12 +146,40 @@ export const col = {
138
146
  /**
139
147
  * Creates a UUID column definition
140
148
  */
141
- uuid: (): ColumnDef<'UUID'> => ({ name: '', type: 'UUID' }),
142
-
143
- /**
144
- * Creates a timestamp column definition
145
- */
146
- timestamp: (): ColumnDef<'TIMESTAMP'> => ({ name: '', type: 'TIMESTAMP' }),
149
+ uuid: (): ColumnDef<'UUID'> => ({ name: '', type: 'UUID' }),
150
+
151
+ /**
152
+ * Creates a binary large object column definition
153
+ */
154
+ blob: (): ColumnDef<'BLOB'> => ({ name: '', type: 'BLOB' }),
155
+
156
+ /**
157
+ * Creates a fixed-length binary column definition
158
+ */
159
+ binary: (length?: number): ColumnDef<'BINARY'> => ({
160
+ name: '',
161
+ type: 'BINARY',
162
+ args: length !== undefined ? [length] : undefined
163
+ }),
164
+
165
+ /**
166
+ * Creates a variable-length binary column definition
167
+ */
168
+ varbinary: (length?: number): ColumnDef<'VARBINARY'> => ({
169
+ name: '',
170
+ type: 'VARBINARY',
171
+ args: length !== undefined ? [length] : undefined
172
+ }),
173
+
174
+ /**
175
+ * Creates a Postgres bytea column definition
176
+ */
177
+ bytea: (): ColumnDef<'BYTEA'> => ({ name: '', type: 'BYTEA' }),
178
+
179
+ /**
180
+ * Creates a timestamp column definition
181
+ */
182
+ timestamp: (): ColumnDef<'TIMESTAMP'> => ({ name: '', type: 'TIMESTAMP' }),
147
183
 
148
184
  /**
149
185
  * Creates a timestamptz column definition
@@ -27,6 +27,7 @@ export type ColumnToTs<T extends ColumnDef> =
27
27
  T['type'] extends 'DECIMAL' | 'decimal' | 'FLOAT' | 'float' | 'DOUBLE' | 'double' ? number :
28
28
  T['type'] extends 'BOOLEAN' | 'boolean' ? boolean :
29
29
  T['type'] extends 'JSON' | 'json' ? unknown :
30
+ T['type'] extends 'BLOB' | 'blob' | 'BINARY' | 'binary' | 'VARBINARY' | 'varbinary' | 'BYTEA' | 'bytea' ? Buffer :
30
31
  T['type'] extends 'DATE' | 'date' | 'DATETIME' | 'datetime' | 'TIMESTAMP' | 'timestamp' | 'TIMESTAMPTZ' | 'timestamptz' ? string :
31
32
  string;
32
33