metal-orm 1.0.56 → 1.0.58

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 (82) hide show
  1. package/README.md +41 -33
  2. package/dist/index.cjs +1461 -195
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +541 -114
  5. package/dist/index.d.ts +541 -114
  6. package/dist/index.js +1424 -195
  7. package/dist/index.js.map +1 -1
  8. package/package.json +69 -69
  9. package/src/codegen/naming-strategy.ts +3 -1
  10. package/src/codegen/typescript.ts +20 -10
  11. package/src/core/ast/aggregate-functions.ts +14 -0
  12. package/src/core/ast/builders.ts +38 -20
  13. package/src/core/ast/expression-builders.ts +70 -2
  14. package/src/core/ast/expression-nodes.ts +305 -274
  15. package/src/core/ast/expression-visitor.ts +11 -1
  16. package/src/core/ast/expression.ts +4 -0
  17. package/src/core/ast/query.ts +3 -0
  18. package/src/core/ddl/introspect/catalogs/mysql.ts +5 -0
  19. package/src/core/ddl/introspect/catalogs/sqlite.ts +3 -0
  20. package/src/core/ddl/introspect/functions/mssql.ts +13 -0
  21. package/src/core/ddl/introspect/mssql.ts +4 -0
  22. package/src/core/ddl/introspect/mysql.ts +4 -0
  23. package/src/core/ddl/introspect/sqlite.ts +4 -0
  24. package/src/core/dialect/abstract.ts +552 -531
  25. package/src/core/dialect/base/function-table-formatter.ts +9 -30
  26. package/src/core/dialect/base/sql-dialect.ts +24 -0
  27. package/src/core/dialect/mssql/functions.ts +40 -2
  28. package/src/core/dialect/mysql/functions.ts +16 -2
  29. package/src/core/dialect/postgres/functions.ts +66 -2
  30. package/src/core/dialect/postgres/index.ts +17 -4
  31. package/src/core/dialect/postgres/table-functions.ts +27 -0
  32. package/src/core/dialect/sqlite/functions.ts +34 -0
  33. package/src/core/dialect/sqlite/index.ts +17 -1
  34. package/src/core/driver/database-driver.ts +9 -1
  35. package/src/core/driver/mssql-driver.ts +3 -0
  36. package/src/core/driver/mysql-driver.ts +3 -0
  37. package/src/core/driver/postgres-driver.ts +3 -0
  38. package/src/core/driver/sqlite-driver.ts +3 -0
  39. package/src/core/execution/executors/mssql-executor.ts +5 -0
  40. package/src/core/execution/executors/mysql-executor.ts +5 -0
  41. package/src/core/execution/executors/postgres-executor.ts +5 -0
  42. package/src/core/execution/executors/sqlite-executor.ts +5 -0
  43. package/src/core/functions/array.ts +26 -0
  44. package/src/core/functions/control-flow.ts +69 -0
  45. package/src/core/functions/datetime.ts +50 -0
  46. package/src/core/functions/definitions/aggregate.ts +16 -0
  47. package/src/core/functions/definitions/control-flow.ts +24 -0
  48. package/src/core/functions/definitions/datetime.ts +36 -0
  49. package/src/core/functions/definitions/helpers.ts +29 -0
  50. package/src/core/functions/definitions/json.ts +49 -0
  51. package/src/core/functions/definitions/numeric.ts +55 -0
  52. package/src/core/functions/definitions/string.ts +43 -0
  53. package/src/core/functions/function-registry.ts +48 -0
  54. package/src/core/functions/group-concat-helpers.ts +57 -0
  55. package/src/core/functions/json.ts +38 -0
  56. package/src/core/functions/numeric.ts +14 -0
  57. package/src/core/functions/standard-strategy.ts +86 -115
  58. package/src/core/functions/standard-table-strategy.ts +13 -0
  59. package/src/core/functions/table-types.ts +15 -0
  60. package/src/core/functions/text.ts +57 -0
  61. package/src/core/sql/sql.ts +59 -38
  62. package/src/decorators/bootstrap.ts +41 -4
  63. package/src/index.ts +18 -11
  64. package/src/orm/entity-meta.ts +6 -3
  65. package/src/orm/entity.ts +81 -14
  66. package/src/orm/execute.ts +87 -20
  67. package/src/orm/hydration-context.ts +10 -0
  68. package/src/orm/identity-map.ts +19 -0
  69. package/src/orm/interceptor-pipeline.ts +4 -0
  70. package/src/orm/lazy-batch.ts +237 -54
  71. package/src/orm/relations/belongs-to.ts +19 -2
  72. package/src/orm/relations/has-many.ts +23 -9
  73. package/src/orm/relations/has-one.ts +19 -2
  74. package/src/orm/relations/many-to-many.ts +59 -4
  75. package/src/orm/save-graph-types.ts +2 -2
  76. package/src/orm/save-graph.ts +18 -18
  77. package/src/query-builder/relation-conditions.ts +80 -59
  78. package/src/query-builder/relation-service.ts +399 -95
  79. package/src/query-builder/relation-types.ts +2 -2
  80. package/src/query-builder/select.ts +124 -106
  81. package/src/schema/table-guards.ts +6 -0
  82. package/src/schema/types.ts +109 -85
@@ -1,131 +1,102 @@
1
+
1
2
  import { FunctionStrategy, FunctionRenderer, FunctionRenderContext } from './types.js';
2
- import { LiteralNode, OperandNode, isOperandNode } from '../ast/expression.js';
3
+ import type { LiteralNode, OperandNode } from '../ast/expression.js';
4
+ import { FunctionRegistry } from './function-registry.js';
5
+ import type { FunctionDefinition } from './function-registry.js';
6
+ import { aggregateFunctionDefinitions } from './definitions/aggregate.js';
7
+ import { stringFunctionDefinitions } from './definitions/string.js';
8
+ import { dateTimeFunctionDefinitions } from './definitions/datetime.js';
9
+ import { numericFunctionDefinitions } from './definitions/numeric.js';
10
+ import { controlFlowFunctionDefinitions } from './definitions/control-flow.js';
11
+ import { jsonFunctionDefinitions } from './definitions/json.js';
12
+ import {
13
+ renderStandardGroupConcat,
14
+ buildGroupConcatOrderBy as buildGroupConcatOrderByClause,
15
+ formatGroupConcatSeparator as formatGroupConcatSeparatorClause,
16
+ getGroupConcatSeparatorOperand as resolveGroupConcatSeparatorOperand,
17
+ DEFAULT_GROUP_CONCAT_SEPARATOR as DEFAULT_GROUP_CONCAT_SEPARATOR_LITERAL
18
+ } from './group-concat-helpers.js';
3
19
 
4
20
  /**
5
21
  * Standard implementation of FunctionStrategy for ANSI SQL functions.
6
22
  */
7
23
  export class StandardFunctionStrategy implements FunctionStrategy {
8
- protected renderers: Map<string, FunctionRenderer> = new Map();
24
+ protected readonly registry: FunctionRegistry;
25
+
26
+ /**
27
+ * Creates a new StandardFunctionStrategy and registers standard functions.
28
+ */
29
+ constructor(registry?: FunctionRegistry) {
30
+ this.registry = registry ?? new FunctionRegistry();
31
+ this.registerStandard();
32
+ }
9
33
 
10
- /**
11
- * Creates a new StandardFunctionStrategy and registers standard functions.
12
- */
13
- constructor() {
14
- this.registerStandard();
15
- }
34
+ protected registerStandard(): void {
35
+ this.registerDefinitions(aggregateFunctionDefinitions);
36
+ this.registerDefinitions(stringFunctionDefinitions);
37
+ this.registerDefinitions(dateTimeFunctionDefinitions);
38
+ this.registerDefinitions(numericFunctionDefinitions);
39
+ this.registerDefinitions(controlFlowFunctionDefinitions);
40
+ this.registerDefinitions(jsonFunctionDefinitions);
41
+ this.add('GROUP_CONCAT', ctx => this.renderGroupConcat(ctx));
42
+ }
16
43
 
17
- protected registerStandard() {
18
- // Register ANSI standard implementations
19
- this.add('COUNT', ({ compiledArgs }) => compiledArgs.length ? `COUNT(${compiledArgs.join(', ')})` : 'COUNT(*)');
20
- this.add('SUM', ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
21
- this.add('AVG', ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
22
- this.add('MIN', ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
23
- this.add('MAX', ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
24
- this.add('ABS', ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
25
- this.add('UPPER', ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
26
- this.add('LOWER', ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
27
- this.add('LENGTH', ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
28
- this.add('TRIM', ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
29
- this.add('LTRIM', ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
30
- this.add('RTRIM', ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
31
- this.add('SUBSTRING', ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(', ')})`);
32
- this.add('CONCAT', ({ compiledArgs }) => `CONCAT(${compiledArgs.join(', ')})`);
33
- this.add('NOW', () => `NOW()`);
34
- this.add('CURRENT_DATE', () => `CURRENT_DATE`);
35
- this.add('CURRENT_TIME', () => `CURRENT_TIME`);
36
- this.add('EXTRACT', ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
37
- this.add('YEAR', ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
38
- this.add('MONTH', ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
39
- this.add('DAY', ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
40
- this.add('DATE_ADD', ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
41
- this.add('DATE_SUB', ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
42
- this.add('DATE_DIFF', ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
43
- this.add('DATE_FORMAT', ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
44
- this.add('UNIX_TIMESTAMP', () => `UNIX_TIMESTAMP()`);
45
- this.add('FROM_UNIXTIME', ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
46
- this.add('END_OF_MONTH', ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
47
- this.add('DAY_OF_WEEK', ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
48
- this.add('WEEK_OF_YEAR', ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
49
- this.add('DATE_TRUNC', ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
50
- this.add('GROUP_CONCAT', ctx => this.renderGroupConcat(ctx));
51
- }
44
+ protected registerDefinitions(definitions: FunctionDefinition[]): void {
45
+ this.registry.register(definitions);
46
+ }
52
47
 
53
- /**
54
- * Registers a renderer for a function name.
55
- * @param name - The function name.
56
- * @param renderer - The renderer function.
57
- */
58
- protected add(name: string, renderer: FunctionRenderer) {
59
- this.renderers.set(name, renderer);
60
- }
48
+ /**
49
+ * Registers a renderer for a function name.
50
+ * @param name - The function name.
51
+ * @param renderer - The renderer function.
52
+ */
53
+ protected add(name: string, renderer: FunctionRenderer): void {
54
+ this.registry.add(name, renderer);
55
+ }
61
56
 
62
- /**
63
- * @inheritDoc
64
- */
65
- getRenderer(name: string): FunctionRenderer | undefined {
66
- return this.renderers.get(name);
67
- }
57
+ /**
58
+ * @inheritDoc
59
+ */
60
+ getRenderer(name: string): FunctionRenderer | undefined {
61
+ return this.registry.get(name);
62
+ }
68
63
 
69
- /**
70
- * Renders the GROUP_CONCAT function with optional ORDER BY and SEPARATOR.
71
- * @param ctx - The function render context.
72
- * @returns The rendered SQL string.
73
- */
74
- private renderGroupConcat(ctx: FunctionRenderContext): string {
75
- const arg = ctx.compiledArgs[0];
76
- const orderClause = this.buildOrderByExpression(ctx);
77
- const orderSegment = orderClause ? ` ${orderClause}` : '';
78
- const separatorClause = this.formatGroupConcatSeparator(ctx);
79
- return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
80
- }
64
+ /**
65
+ * Renders the GROUP_CONCAT function with optional ORDER BY and SEPARATOR.
66
+ * @param ctx - The function render context.
67
+ * @returns The rendered SQL string.
68
+ */
69
+ private renderGroupConcat(ctx: FunctionRenderContext): string {
70
+ return renderStandardGroupConcat(ctx);
71
+ }
81
72
 
82
- /**
83
- * Builds the ORDER BY clause for functions like GROUP_CONCAT.
84
- * @param ctx - The function render context.
85
- * @returns The ORDER BY SQL clause or empty string.
86
- */
87
- protected buildOrderByExpression(ctx: FunctionRenderContext): string {
88
- const orderBy = ctx.node.orderBy;
89
- if (!orderBy || orderBy.length === 0) {
90
- return '';
91
- }
92
- const parts = orderBy.map(order => {
93
- const term = isOperandNode(order.term)
94
- ? ctx.compileOperand(order.term)
95
- : (() => {
96
- throw new Error('ORDER BY expressions inside functions must be operands');
97
- })();
98
- const collation = order.collation ? ` COLLATE ${order.collation}` : '';
99
- const nulls = order.nulls ? ` NULLS ${order.nulls}` : '';
100
- return `${term} ${order.direction}${collation}${nulls}`;
101
- });
102
- return `ORDER BY ${parts.join(', ')}`;
103
- }
73
+ /**
74
+ * Builds the ORDER BY clause for functions like GROUP_CONCAT.
75
+ * @param ctx - The function render context.
76
+ * @returns The ORDER BY SQL clause or empty string.
77
+ */
78
+ protected buildOrderByExpression(ctx: FunctionRenderContext): string {
79
+ return buildGroupConcatOrderByClause(ctx);
80
+ }
104
81
 
105
- /**
106
- * Formats the SEPARATOR clause for GROUP_CONCAT.
107
- * @param ctx - The function render context.
108
- * @returns The SEPARATOR SQL clause or empty string.
109
- */
110
- protected formatGroupConcatSeparator(ctx: FunctionRenderContext): string {
111
- if (!ctx.node.separator) {
112
- return '';
113
- }
114
- return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
115
- }
82
+ /**
83
+ * Formats the SEPARATOR clause for GROUP_CONCAT.
84
+ * @param ctx - The function render context.
85
+ * @returns The SEPARATOR SQL clause or empty string.
86
+ */
87
+ protected formatGroupConcatSeparator(ctx: FunctionRenderContext): string {
88
+ return formatGroupConcatSeparatorClause(ctx);
89
+ }
116
90
 
117
- /**
118
- * Gets the separator operand for GROUP_CONCAT, defaulting to comma.
119
- * @param ctx - The function render context.
120
- * @returns The separator operand.
121
- */
122
- protected getGroupConcatSeparatorOperand(ctx: FunctionRenderContext): OperandNode {
123
- return ctx.node.separator ?? StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
124
- }
91
+ /**
92
+ * Gets the separator operand for GROUP_CONCAT, defaulting to comma.
93
+ * @param ctx - The function render context.
94
+ * @returns The separator operand.
95
+ */
96
+ protected getGroupConcatSeparatorOperand(ctx: FunctionRenderContext): OperandNode {
97
+ return resolveGroupConcatSeparatorOperand(ctx);
98
+ }
125
99
 
126
- /** Default separator for GROUP_CONCAT, a comma. */
127
- protected static readonly DEFAULT_GROUP_CONCAT_SEPARATOR: LiteralNode = {
128
- type: 'Literal',
129
- value: ','
130
- };
100
+ /** Default separator for GROUP_CONCAT, a comma. */
101
+ protected static readonly DEFAULT_GROUP_CONCAT_SEPARATOR: LiteralNode = DEFAULT_GROUP_CONCAT_SEPARATOR_LITERAL;
131
102
  }
@@ -0,0 +1,13 @@
1
+ import type { TableFunctionRenderer, TableFunctionStrategy } from './table-types.js';
2
+
3
+ export class StandardTableFunctionStrategy implements TableFunctionStrategy {
4
+ protected renderers: Map<string, TableFunctionRenderer> = new Map();
5
+
6
+ protected add(key: string, renderer: TableFunctionRenderer) {
7
+ this.renderers.set(key, renderer);
8
+ }
9
+
10
+ getRenderer(key: string): TableFunctionRenderer | undefined {
11
+ return this.renderers.get(key);
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { OperandNode } from '../ast/expression.js';
2
+ import type { FunctionTableNode } from '../ast/query.js';
3
+
4
+ export interface TableFunctionRenderContext {
5
+ node: FunctionTableNode;
6
+ compiledArgs: string[];
7
+ compileOperand: (operand: OperandNode) => string;
8
+ quoteIdentifier: (id: string) => string;
9
+ }
10
+
11
+ export type TableFunctionRenderer = (ctx: TableFunctionRenderContext) => string;
12
+
13
+ export interface TableFunctionStrategy {
14
+ getRenderer(key: string): TableFunctionRenderer | undefined;
15
+ }
@@ -208,3 +208,60 @@ export const rpad = (value: OperandInput, len: OperandInput, pad: OperandInput):
208
208
  */
209
209
  export const space = (count: OperandInput): FunctionNode => fn('SPACE', [count]);
210
210
 
211
+ /**
212
+ * Reverses a string.
213
+ * @param value - The string value.
214
+ * @returns A FunctionNode representing the REVERSE SQL function.
215
+ */
216
+ export const reverse = (value: OperandInput): FunctionNode => fn('REVERSE', [value]);
217
+
218
+ /**
219
+ * Capitalizes the first letter of each word in a string.
220
+ * @param value - The string value.
221
+ * @returns A FunctionNode representing the INITCAP SQL function.
222
+ */
223
+ export const initcap = (value: OperandInput): FunctionNode => fn('INITCAP', [value]);
224
+
225
+ /**
226
+ * Returns the MD5 hash of a string.
227
+ * @param value - The string value.
228
+ * @returns A FunctionNode representing the MD5 SQL function.
229
+ */
230
+ export const md5 = (value: OperandInput): FunctionNode => fn('MD5', [value]);
231
+
232
+ /**
233
+ * Returns the SHA-1 hash of a string.
234
+ * @param value - The string value.
235
+ * @returns A FunctionNode representing the SHA1 SQL function.
236
+ */
237
+ export const sha1 = (value: OperandInput): FunctionNode => fn('SHA1', [value]);
238
+
239
+ /**
240
+ * Returns the SHA-2 hash of a string with a specified bit length.
241
+ * @param value - The string value.
242
+ * @param bits - The bit length (e.g., 224, 256, 384, 512).
243
+ * @returns A FunctionNode representing the SHA2 SQL function.
244
+ */
245
+ export const sha2 = (value: OperandInput, bits: OperandInput): FunctionNode => fn('SHA2', [value, bits]);
246
+
247
+ /**
248
+ * Returns the length of a string in bits.
249
+ * @param value - The string value.
250
+ * @returns A FunctionNode representing the BIT_LENGTH SQL function.
251
+ */
252
+ export const bitLength = (value: OperandInput): FunctionNode => fn('BIT_LENGTH', [value]);
253
+
254
+ /**
255
+ * Returns the length of a string in bytes.
256
+ * @param value - The string value.
257
+ * @returns A FunctionNode representing the OCTET_LENGTH SQL function.
258
+ */
259
+ export const octetLength = (value: OperandInput): FunctionNode => fn('OCTET_LENGTH', [value]);
260
+
261
+ /**
262
+ * Returns a string from an ASCII code.
263
+ * @param code - The ASCII code.
264
+ * @returns A FunctionNode representing the CHR/CHAR SQL function.
265
+ */
266
+ export const chr = (code: OperandInput): FunctionNode => fn('CHR', [code]);
267
+
@@ -33,44 +33,65 @@ export const SQL_KEYWORDS = {
33
33
  /**
34
34
  * SQL operators used in query conditions
35
35
  */
36
- export const SQL_OPERATORS = {
37
- /** Equality operator */
38
- EQUALS: '=',
39
- /** Not equals operator */
40
- NOT_EQUALS: '!=',
41
- /** Greater than operator */
42
- GREATER_THAN: '>',
43
- /** Greater than or equal operator */
44
- GREATER_OR_EQUAL: '>=',
45
- /** Less than operator */
46
- LESS_THAN: '<',
47
- /** Less than or equal operator */
48
- LESS_OR_EQUAL: '<=',
49
- /** LIKE pattern matching operator */
50
- LIKE: 'LIKE',
51
- /** NOT LIKE pattern matching operator */
52
- NOT_LIKE: 'NOT LIKE',
53
- /** IN membership operator */
54
- IN: 'IN',
55
- /** NOT IN membership operator */
56
- NOT_IN: 'NOT IN',
57
- /** BETWEEN range operator */
58
- BETWEEN: 'BETWEEN',
59
- /** NOT BETWEEN range operator */
60
- NOT_BETWEEN: 'NOT BETWEEN',
61
- /** IS NULL null check operator */
62
- IS_NULL: 'IS NULL',
63
- /** IS NOT NULL null check operator */
64
- IS_NOT_NULL: 'IS NOT NULL',
65
- /** Logical AND operator */
66
- AND: 'AND',
67
- /** Logical OR operator */
68
- OR: 'OR',
69
- /** EXISTS operator */
70
- EXISTS: 'EXISTS',
71
- /** NOT EXISTS operator */
72
- NOT_EXISTS: 'NOT EXISTS'
73
- } as const;
36
+ export const SQL_OPERATORS = {
37
+ /** Equality operator */
38
+ EQUALS: '=',
39
+ /** Not equals operator */
40
+ NOT_EQUALS: '!=',
41
+ /** Greater than operator */
42
+ GREATER_THAN: '>',
43
+ /** Greater than or equal operator */
44
+ GREATER_OR_EQUAL: '>=',
45
+ /** Less than operator */
46
+ LESS_THAN: '<',
47
+ /** Less than or equal operator */
48
+ LESS_OR_EQUAL: '<=',
49
+ /** LIKE pattern matching operator */
50
+ LIKE: 'LIKE',
51
+ /** NOT LIKE pattern matching operator */
52
+ NOT_LIKE: 'NOT LIKE',
53
+ /** IN membership operator */
54
+ IN: 'IN',
55
+ /** NOT IN membership operator */
56
+ NOT_IN: 'NOT IN',
57
+ /** BETWEEN range operator */
58
+ BETWEEN: 'BETWEEN',
59
+ /** NOT BETWEEN range operator */
60
+ NOT_BETWEEN: 'NOT BETWEEN',
61
+ /** IS NULL null check operator */
62
+ IS_NULL: 'IS NULL',
63
+ /** IS NOT NULL null check operator */
64
+ IS_NOT_NULL: 'IS NOT NULL',
65
+ /** Logical AND operator */
66
+ AND: 'AND',
67
+ /** Logical OR operator */
68
+ OR: 'OR',
69
+ /** EXISTS operator */
70
+ EXISTS: 'EXISTS',
71
+ /** NOT EXISTS operator */
72
+ NOT_EXISTS: 'NOT EXISTS'
73
+ } as const;
74
+
75
+ /**
76
+ * SQL bitwise operators
77
+ */
78
+ export const BITWISE_OPERATORS = {
79
+ /** Bitwise AND */
80
+ AND: '&',
81
+ /** Bitwise OR */
82
+ OR: '|',
83
+ /** Bitwise XOR */
84
+ XOR: '^',
85
+ /** Bitwise Shift Left */
86
+ SHIFT_LEFT: '<<',
87
+ /** Bitwise Shift Right */
88
+ SHIFT_RIGHT: '>>'
89
+ } as const;
90
+
91
+ /**
92
+ * Type representing supported bitwise operators
93
+ */
94
+ export type BitwiseOperator = (typeof BITWISE_OPERATORS)[keyof typeof BITWISE_OPERATORS];
74
95
 
75
96
  /**
76
97
  * Type representing any supported SQL operator
@@ -5,6 +5,10 @@ import {
5
5
  belongsTo,
6
6
  belongsToMany,
7
7
  RelationKinds,
8
+ type HasManyRelation,
9
+ type HasOneRelation,
10
+ type BelongsToRelation,
11
+ type BelongsToManyRelation,
8
12
  type RelationDef
9
13
  } from '../schema/relation.js';
10
14
  import { TableDef } from '../schema/table.js';
@@ -20,6 +24,14 @@ import {
20
24
  } from '../orm/entity-metadata.js';
21
25
 
22
26
  import { tableRef, type TableRef } from '../schema/table.js';
27
+ import {
28
+ SelectableKeys,
29
+ ColumnDef,
30
+ HasManyCollection,
31
+ HasOneReference,
32
+ BelongsToReference,
33
+ ManyToManyCollection
34
+ } from '../schema/types.js';
23
35
 
24
36
  const unwrapTarget = (target: EntityOrTableTargetResolver): EntityOrTableTarget => {
25
37
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
@@ -143,14 +155,39 @@ export const getTableDefFromEntity = <TTable extends TableDef = TableDef>(ctor:
143
155
  * @param ctor - The entity constructor.
144
156
  * @returns A select query builder for the entity.
145
157
  */
146
- export const selectFromEntity = <TTable extends TableDef = TableDef>(
147
- ctor: EntityConstructor
148
- ): SelectQueryBuilder<unknown, TTable> => {
158
+ type NonFunctionKeys<T> = {
159
+ [K in keyof T]-?: T[K] extends (...args: unknown[]) => unknown ? never : K
160
+ }[keyof T];
161
+
162
+ type RelationKeys<TEntity extends object> =
163
+ Exclude<NonFunctionKeys<TEntity>, SelectableKeys<TEntity>> & string;
164
+
165
+ type EntityTable<TEntity extends object> =
166
+ Omit<TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>, 'relations'> & {
167
+ relations: {
168
+ [K in RelationKeys<TEntity>]:
169
+ NonNullable<TEntity[K]> extends HasManyCollection<infer TChild>
170
+ ? HasManyRelation<EntityTable<NonNullable<TChild> & object>>
171
+ : NonNullable<TEntity[K]> extends ManyToManyCollection<infer TTarget>
172
+ ? BelongsToManyRelation<EntityTable<NonNullable<TTarget> & object>>
173
+ : NonNullable<TEntity[K]> extends HasOneReference<infer TChild>
174
+ ? HasOneRelation<EntityTable<NonNullable<TChild> & object>>
175
+ : NonNullable<TEntity[K]> extends BelongsToReference<infer TParent>
176
+ ? BelongsToRelation<EntityTable<NonNullable<TParent> & object>>
177
+ : NonNullable<TEntity[K]> extends object
178
+ ? BelongsToRelation<EntityTable<NonNullable<TEntity[K]> & object>>
179
+ : never;
180
+ };
181
+ };
182
+
183
+ export const selectFromEntity = <TEntity extends object>(
184
+ ctor: EntityConstructor<TEntity>
185
+ ): SelectQueryBuilder<unknown, EntityTable<TEntity>> => {
149
186
  const table = getTableDefFromEntity(ctor);
150
187
  if (!table) {
151
188
  throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
152
189
  }
153
- return new SelectQueryBuilder(table as TTable);
190
+ return new SelectQueryBuilder(table as unknown as EntityTable<TEntity>);
154
191
  };
155
192
 
156
193
  /**
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * MetalORM core exports.
3
+ * Provides schema definition, query building, and ORM capabilities.
4
+ */
1
5
  export * from './schema/table.js';
2
6
  export * from './schema/column-types.js';
3
7
  export * from './schema/relation.js';
@@ -19,16 +23,19 @@ export * from './core/ddl/schema-types.js';
19
23
  export * from './core/ddl/schema-diff.js';
20
24
  export * from './core/ddl/schema-introspect.js';
21
25
  export * from './core/ddl/introspect/registry.js';
22
- export * from './core/functions/text.js';
23
- export * from './core/functions/numeric.js';
24
- export * from './core/functions/datetime.js';
26
+ export * from './core/functions/text.js';
27
+ export * from './core/functions/numeric.js';
28
+ export * from './core/functions/datetime.js';
29
+ export * from './core/functions/control-flow.js';
30
+ export * from './core/functions/json.js';
31
+ export * from './core/functions/array.js';
25
32
  export * from './orm/als.js';
26
33
  export * from './orm/hydration.js';
27
34
  export * from './codegen/typescript.js';
28
- export * from './orm/orm-session.js';
29
- export * from './orm/orm.js';
30
- export * from './orm/entity.js';
31
- export * from './orm/lazy-batch.js';
35
+ export * from './orm/orm-session.js';
36
+ export * from './orm/orm.js';
37
+ export * from './orm/entity.js';
38
+ export * from './orm/lazy-batch.js';
32
39
  export * from './orm/relations/has-many.js';
33
40
  export * from './orm/relations/belongs-to.js';
34
41
  export * from './orm/relations/many-to-many.js';
@@ -38,10 +45,10 @@ export * from './orm/execution-context.js';
38
45
  export * from './orm/hydration-context.js';
39
46
  export * from './orm/domain-event-bus.js';
40
47
  export * from './orm/runtime-types.js';
41
- export * from './orm/query-logger.js';
42
- export * from './orm/jsonify.js';
43
- export * from './orm/save-graph-types.js';
44
- export * from './decorators/index.js';
48
+ export * from './orm/query-logger.js';
49
+ export * from './orm/jsonify.js';
50
+ export * from './orm/save-graph-types.js';
51
+ export * from './decorators/index.js';
45
52
 
46
53
  // NEW: execution abstraction + helpers
47
54
  export * from './core/execution/db-executor.js';
@@ -1,4 +1,5 @@
1
- import { TableDef } from '../schema/table.js';
1
+ import { TableDef } from '../schema/table.js';
2
+ import { RelationIncludeOptions } from '../query-builder/relation-types.js';
2
3
  import { EntityContext } from './entity-context.js';
3
4
  import { RelationMap } from '../schema/types.js';
4
5
 
@@ -18,8 +19,10 @@ export interface EntityMeta<TTable extends TableDef> {
18
19
  ctx: EntityContext;
19
20
  /** Table definition */
20
21
  table: TTable;
21
- /** Relations that should be loaded lazily */
22
- lazyRelations: (keyof RelationMap<TTable>)[];
22
+ /** Relations that should be loaded lazily */
23
+ lazyRelations: (keyof RelationMap<TTable>)[];
24
+ /** Include options for lazy relations */
25
+ lazyRelationOptions: Map<string, RelationIncludeOptions>;
23
26
  /** Cache for relation promises */
24
27
  relationCache: Map<string, Promise<unknown>>;
25
28
  /** Hydration data for relations */