metal-orm 1.0.62 → 1.0.64

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { ColumnDef } from '../../schema/column-types.js';
4
4
  import { columnOperand, valueToOperand } from '../ast/expression-builders.js';
5
- import { FunctionNode, OperandNode, isOperandNode } from '../ast/expression.js';
5
+ import { FunctionNode, OperandNode, isOperandNode, TypedExpression, asType } from '../ast/expression.js';
6
6
 
7
7
  type OperandInput = OperandNode | ColumnDef | string | number | boolean | null;
8
8
 
@@ -22,246 +22,313 @@ const fn = (key: string, args: OperandInput[]): FunctionNode => ({
22
22
  args: args.map(toOperand)
23
23
  });
24
24
 
25
+ const sfn = (key: string, args: OperandInput[]): TypedExpression<string> => asType<string>(fn(key, args));
26
+ const nfn = (key: string, args: OperandInput[]): TypedExpression<number> => asType<number>(fn(key, args));
27
+
25
28
  /**
26
29
  * Converts a string to lowercase.
27
- * @param value - The string value.
28
- * @returns A FunctionNode representing the LOWER SQL function.
30
+ *
31
+ * @param value - The string value or column.
32
+ * @returns A `TypedExpression<string>` representing the `LOWER` SQL function.
33
+ *
34
+ * @example
35
+ * lower(users.email);
29
36
  */
30
- export const lower = (value: OperandInput): FunctionNode => fn('LOWER', [value]);
37
+ export const lower = (value: OperandInput): TypedExpression<string> => sfn('LOWER', [value]);
31
38
 
32
39
  /**
33
40
  * Converts a string to uppercase.
34
- * @param value - The string value.
35
- * @returns A FunctionNode representing the UPPER SQL function.
41
+ *
42
+ * @param value - The string value or column.
43
+ * @returns A `TypedExpression<string>` representing the `UPPER` SQL function.
44
+ *
45
+ * @example
46
+ * upper(users.firstName);
36
47
  */
37
- export const upper = (value: OperandInput): FunctionNode => fn('UPPER', [value]);
48
+ export const upper = (value: OperandInput): TypedExpression<string> => sfn('UPPER', [value]);
38
49
 
39
50
  /**
40
51
  * Returns the ASCII code of the first character of a string.
41
- * @param value - The string value.
42
- * @returns A FunctionNode representing the ASCII SQL function.
52
+ *
53
+ * @param value - The string value or column.
54
+ * @returns A `TypedExpression<number>` representing the `ASCII` SQL function.
55
+ *
56
+ * @example
57
+ * ascii(users.initial);
43
58
  */
44
- export const ascii = (value: OperandInput): FunctionNode => fn('ASCII', [value]);
59
+ export const ascii = (value: OperandInput): TypedExpression<number> => nfn('ASCII', [value]);
45
60
 
46
61
  /**
47
62
  * Returns a string from one or more ASCII codes.
48
- * @param codes - The ASCII codes.
49
- * @returns A FunctionNode representing the CHAR SQL function.
63
+ *
64
+ * @param codes - One or more ASCII codes.
65
+ * @returns A `TypedExpression<string>` representing the `CHAR` SQL function.
66
+ *
67
+ * @example
68
+ * char(65, 66, 67); // 'ABC'
50
69
  */
51
- export const char = (...codes: OperandInput[]): FunctionNode => {
70
+ export const char = (...codes: OperandInput[]): TypedExpression<string> => {
52
71
  if (codes.length === 0) throw new Error('char() expects at least 1 argument');
53
- return fn('CHAR', codes);
72
+ return sfn('CHAR', codes);
54
73
  };
55
74
 
56
75
  /**
57
76
  * Returns the number of characters in a string.
58
- * @param value - The string value.
59
- * @returns A FunctionNode representing the CHAR_LENGTH SQL function.
77
+ *
78
+ * @param value - The string value or column.
79
+ * @returns A `TypedExpression<number>` representing the `CHAR_LENGTH` SQL function.
80
+ *
81
+ * @example
82
+ * charLength(users.bio);
60
83
  */
61
- export const charLength = (value: OperandInput): FunctionNode => fn('CHAR_LENGTH', [value]);
84
+ export const charLength = (value: OperandInput): TypedExpression<number> => nfn('CHAR_LENGTH', [value]);
62
85
 
63
86
  /**
64
87
  * Returns the length of a string in bytes or characters.
65
- * @param value - The string value.
66
- * @returns A FunctionNode representing the LENGTH SQL function.
88
+ *
89
+ * @param value - The string value or column.
90
+ * @returns A `TypedExpression<number>` representing the `LENGTH` SQL function.
91
+ *
92
+ * @example
93
+ * length(users.password);
67
94
  */
68
- export const length = (value: OperandInput): FunctionNode => fn('LENGTH', [value]);
95
+ export const length = (value: OperandInput): TypedExpression<number> => nfn('LENGTH', [value]);
69
96
 
70
97
  /**
71
98
  * Removes leading and trailing whitespace or specified characters from a string.
72
- * @param value - The string value.
73
- * @param chars - The characters to trim (optional).
74
- * @returns A FunctionNode representing the TRIM SQL function.
99
+ *
100
+ * @param value - The string value or column.
101
+ * @param chars - Optional characters to trim.
102
+ * @returns A `TypedExpression<string>` representing the `TRIM` SQL function.
103
+ *
104
+ * @example
105
+ * trim(users.name);
106
+ * trim(users.path, '/');
75
107
  */
76
- export const trim = (value: OperandInput, chars?: OperandInput): FunctionNode =>
77
- chars === undefined ? fn('TRIM', [value]) : fn('TRIM', [value, chars]);
108
+ export const trim = (value: OperandInput, chars?: OperandInput): TypedExpression<string> =>
109
+ chars === undefined ? sfn('TRIM', [value]) : sfn('TRIM', [value, chars]);
78
110
 
79
111
  /**
80
112
  * Removes leading whitespace from a string.
81
- * @param value - The string value.
82
- * @returns A FunctionNode representing the LTRIM SQL function.
113
+ *
114
+ * @param value - The string value or column.
115
+ * @returns A `TypedExpression<string>` representing the `LTRIM` SQL function.
83
116
  */
84
- export const ltrim = (value: OperandInput): FunctionNode => fn('LTRIM', [value]);
117
+ export const ltrim = (value: OperandInput): TypedExpression<string> => sfn('LTRIM', [value]);
85
118
 
86
119
  /**
87
120
  * Removes trailing whitespace from a string.
88
- * @param value - The string value.
89
- * @returns A FunctionNode representing the RTRIM SQL function.
121
+ *
122
+ * @param value - The string value or column.
123
+ * @returns A `TypedExpression<string>` representing the `RTRIM` SQL function.
90
124
  */
91
- export const rtrim = (value: OperandInput): FunctionNode => fn('RTRIM', [value]);
125
+ export const rtrim = (value: OperandInput): TypedExpression<string> => sfn('RTRIM', [value]);
92
126
 
93
127
  /**
94
128
  * Concatenates two or more strings.
95
- * @param args - The strings to concatenate.
96
- * @returns A FunctionNode representing the CONCAT SQL function.
129
+ *
130
+ * @param args - The strings or columns to concatenate.
131
+ * @returns A `TypedExpression<string>` representing the `CONCAT` SQL function.
132
+ *
133
+ * @example
134
+ * concat(users.firstName, ' ', users.lastName);
97
135
  */
98
- export const concat = (...args: OperandInput[]): FunctionNode => {
136
+ export const concat = (...args: OperandInput[]): TypedExpression<string> => {
99
137
  if (args.length < 2) throw new Error('concat() expects at least 2 arguments');
100
- return fn('CONCAT', args);
138
+ return sfn('CONCAT', args);
101
139
  };
102
140
 
103
141
  /**
104
142
  * Concatenates strings with a separator.
143
+ *
105
144
  * @param separator - The separator string.
106
- * @param args - The strings to concatenate.
107
- * @returns A FunctionNode representing the CONCAT_WS SQL function.
145
+ * @param args - The strings or columns to concatenate.
146
+ * @returns A `TypedExpression<string>` representing the `CONCAT_WS` SQL function.
147
+ *
148
+ * @example
149
+ * concatWs(', ', users.lastName, users.firstName);
108
150
  */
109
- export const concatWs = (separator: OperandInput, ...args: OperandInput[]): FunctionNode => {
151
+ export const concatWs = (separator: OperandInput, ...args: OperandInput[]): TypedExpression<string> => {
110
152
  if (args.length < 1) throw new Error('concatWs() expects at least 2 arguments including the separator');
111
- return fn('CONCAT_WS', [separator, ...args]);
153
+ return sfn('CONCAT_WS', [separator, ...args]);
112
154
  };
113
155
 
114
156
  /**
115
157
  * Extracts a substring from a string.
116
- * @param value - The string value.
117
- * @param start - The starting position.
118
- * @param length - The length of the substring (optional).
119
- * @returns A FunctionNode representing the SUBSTR SQL function.
158
+ *
159
+ * @param value - The input string or column.
160
+ * @param start - The starting position (1-indexed).
161
+ * @param length - Optional length of the substring.
162
+ * @returns A `TypedExpression<string>` representing the `SUBSTR` SQL function.
163
+ *
164
+ * @example
165
+ * substr(users.token, 1, 8);
120
166
  */
121
- export const substr = (value: OperandInput, start: OperandInput, length?: OperandInput): FunctionNode =>
122
- length === undefined ? fn('SUBSTR', [value, start]) : fn('SUBSTR', [value, start, length]);
167
+ export const substr = (value: OperandInput, start: OperandInput, length?: OperandInput): TypedExpression<string> =>
168
+ length === undefined ? sfn('SUBSTR', [value, start]) : sfn('SUBSTR', [value, start, length]);
123
169
 
124
170
  /**
125
171
  * Returns the leftmost characters of a string.
126
- * @param value - The string value.
127
- * @param len - The number of characters to return.
128
- * @returns A FunctionNode representing the LEFT SQL function.
172
+ *
173
+ * @param value - The string value or column.
174
+ * @param len - Number of characters to return.
175
+ * @returns A `TypedExpression<string>` representing the `LEFT` SQL function.
129
176
  */
130
- export const left = (value: OperandInput, len: OperandInput): FunctionNode => fn('LEFT', [value, len]);
177
+ export const left = (value: OperandInput, len: OperandInput): TypedExpression<string> => sfn('LEFT', [value, len]);
131
178
 
132
179
  /**
133
180
  * Returns the rightmost characters of a string.
134
- * @param value - The string value.
135
- * @param len - The number of characters to return.
136
- * @returns A FunctionNode representing the RIGHT SQL function.
181
+ *
182
+ * @param value - The string value or column.
183
+ * @param len - Number of characters to return.
184
+ * @returns A `TypedExpression<string>` representing the `RIGHT` SQL function.
137
185
  */
138
- export const right = (value: OperandInput, len: OperandInput): FunctionNode => fn('RIGHT', [value, len]);
186
+ export const right = (value: OperandInput, len: OperandInput): TypedExpression<string> => sfn('RIGHT', [value, len]);
139
187
 
140
188
  /**
141
189
  * Returns the position of a substring in a string.
190
+ *
142
191
  * @param substring - The substring to search for.
143
- * @param value - The string to search in.
144
- * @returns A FunctionNode representing the POSITION SQL function.
192
+ * @param value - The string or column to search within.
193
+ * @returns A `TypedExpression<number>` representing the `POSITION` SQL function.
145
194
  */
146
- export const position = (substring: OperandInput, value: OperandInput): FunctionNode => fn('POSITION', [substring, value]);
195
+ export const position = (substring: OperandInput, value: OperandInput): TypedExpression<number> => nfn('POSITION', [substring, value]);
147
196
 
148
197
  /**
149
198
  * Returns the position of a substring in a string.
150
- * @param value - The string to search in.
199
+ *
200
+ * @param value - The string or column to search within.
151
201
  * @param substring - The substring to search for.
152
- * @returns A FunctionNode representing the INSTR SQL function.
202
+ * @returns A `TypedExpression<number>` representing the `INSTR` SQL function.
153
203
  */
154
- export const instr = (value: OperandInput, substring: OperandInput): FunctionNode => fn('INSTR', [value, substring]);
204
+ export const instr = (value: OperandInput, substring: OperandInput): TypedExpression<number> => nfn('INSTR', [value, substring]);
155
205
 
156
206
  /**
157
207
  * Returns the position of a substring in a string, optionally starting from a position.
158
- * @param substring - The substring to search for.
159
- * @param value - The string to search in.
160
- * @param start - The starting position (optional).
161
- * @returns A FunctionNode representing the LOCATE SQL function.
208
+ *
209
+ * @param substring - Substring to find.
210
+ * @param value - String/column to search.
211
+ * @param start - Optional starting position.
212
+ * @returns A `TypedExpression<number>` representing the `LOCATE` SQL function.
162
213
  */
163
- export const locate = (substring: OperandInput, value: OperandInput, start?: OperandInput): FunctionNode =>
164
- start === undefined ? fn('LOCATE', [substring, value]) : fn('LOCATE', [substring, value, start]);
214
+ export const locate = (substring: OperandInput, value: OperandInput, start?: OperandInput): TypedExpression<number> =>
215
+ start === undefined ? nfn('LOCATE', [substring, value]) : nfn('LOCATE', [substring, value, start]);
165
216
 
166
217
  /**
167
218
  * Replaces occurrences of a substring in a string.
168
- * @param value - The string to search in.
169
- * @param search - The substring to replace.
170
- * @param replacement - The replacement string.
171
- * @returns A FunctionNode representing the REPLACE SQL function.
219
+ *
220
+ * @param value - The original string or column.
221
+ * @param search - Substring to search for.
222
+ * @param replacement - Replacement string.
223
+ * @returns A `TypedExpression<string>` representing the `REPLACE` SQL function.
224
+ *
225
+ * @example
226
+ * replace(users.email, 'old.com', 'new.com');
172
227
  */
173
- export const replace = (value: OperandInput, search: OperandInput, replacement: OperandInput): FunctionNode =>
174
- fn('REPLACE', [value, search, replacement]);
228
+ export const replace = (value: OperandInput, search: OperandInput, replacement: OperandInput): TypedExpression<string> =>
229
+ sfn('REPLACE', [value, search, replacement]);
175
230
 
176
231
  /**
177
232
  * Repeats a string a specified number of times.
233
+ *
178
234
  * @param value - The string to repeat.
179
- * @param count - The number of times to repeat.
180
- * @returns A FunctionNode representing the REPEAT SQL function.
235
+ * @param count - How many times to repeat.
236
+ * @returns A `TypedExpression<string>` representing the `REPEAT` SQL function.
181
237
  */
182
- export const repeat = (value: OperandInput, count: OperandInput): FunctionNode => fn('REPEAT', [value, count]);
238
+ export const repeat = (value: OperandInput, count: OperandInput): TypedExpression<string> => sfn('REPEAT', [value, count]);
183
239
 
184
240
  /**
185
241
  * Left-pads a string to a certain length with another string.
186
- * @param value - The string to pad.
187
- * @param len - The length to pad to.
188
- * @param pad - The padding string.
189
- * @returns A FunctionNode representing the LPAD SQL function.
242
+ *
243
+ * @param value - The string/column to pad.
244
+ * @param len - Target length.
245
+ * @param pad - Padding string.
246
+ * @returns A `TypedExpression<string>` representing the `LPAD` SQL function.
190
247
  */
191
- export const lpad = (value: OperandInput, len: OperandInput, pad: OperandInput): FunctionNode =>
192
- fn('LPAD', [value, len, pad]);
248
+ export const lpad = (value: OperandInput, len: OperandInput, pad: OperandInput): TypedExpression<string> =>
249
+ sfn('LPAD', [value, len, pad]);
193
250
 
194
251
  /**
195
252
  * Right-pads a string to a certain length with another string.
196
- * @param value - The string to pad.
197
- * @param len - The length to pad to.
198
- * @param pad - The padding string.
199
- * @returns A FunctionNode representing the RPAD SQL function.
253
+ *
254
+ * @param value - The string/column to pad.
255
+ * @param len - Target length.
256
+ * @param pad - Padding string.
257
+ * @returns A `TypedExpression<string>` representing the `RPAD` SQL function.
200
258
  */
201
- export const rpad = (value: OperandInput, len: OperandInput, pad: OperandInput): FunctionNode =>
202
- fn('RPAD', [value, len, pad]);
259
+ export const rpad = (value: OperandInput, len: OperandInput, pad: OperandInput): TypedExpression<string> =>
260
+ sfn('RPAD', [value, len, pad]);
203
261
 
204
262
  /**
205
263
  * Returns a string consisting of a specified number of spaces.
206
- * @param count - The number of spaces.
207
- * @returns A FunctionNode representing the SPACE SQL function.
264
+ *
265
+ * @param count - Number of spaces.
266
+ * @returns A `TypedExpression<string>` representing the `SPACE` SQL function.
208
267
  */
209
- export const space = (count: OperandInput): FunctionNode => fn('SPACE', [count]);
268
+ export const space = (count: OperandInput): TypedExpression<string> => sfn('SPACE', [count]);
210
269
 
211
270
  /**
212
271
  * Reverses a string.
213
- * @param value - The string value.
214
- * @returns A FunctionNode representing the REVERSE SQL function.
272
+ *
273
+ * @param value - The string value or column.
274
+ * @returns A `TypedExpression<string>` representing the `REVERSE` SQL function.
215
275
  */
216
- export const reverse = (value: OperandInput): FunctionNode => fn('REVERSE', [value]);
276
+ export const reverse = (value: OperandInput): TypedExpression<string> => sfn('REVERSE', [value]);
217
277
 
218
278
  /**
219
279
  * 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.
280
+ *
281
+ * @param value - The string value or column.
282
+ * @returns A `TypedExpression<string>` representing the `INITCAP` SQL function.
222
283
  */
223
- export const initcap = (value: OperandInput): FunctionNode => fn('INITCAP', [value]);
284
+ export const initcap = (value: OperandInput): TypedExpression<string> => sfn('INITCAP', [value]);
224
285
 
225
286
  /**
226
287
  * Returns the MD5 hash of a string.
227
- * @param value - The string value.
228
- * @returns A FunctionNode representing the MD5 SQL function.
288
+ *
289
+ * @param value - The string value or column.
290
+ * @returns A `TypedExpression<string>` representing the `MD5` SQL function.
229
291
  */
230
- export const md5 = (value: OperandInput): FunctionNode => fn('MD5', [value]);
292
+ export const md5 = (value: OperandInput): TypedExpression<string> => sfn('MD5', [value]);
231
293
 
232
294
  /**
233
295
  * Returns the SHA-1 hash of a string.
234
- * @param value - The string value.
235
- * @returns A FunctionNode representing the SHA1 SQL function.
296
+ *
297
+ * @param value - The string value or column.
298
+ * @returns A `TypedExpression<string>` representing the `SHA1` SQL function.
236
299
  */
237
- export const sha1 = (value: OperandInput): FunctionNode => fn('SHA1', [value]);
300
+ export const sha1 = (value: OperandInput): TypedExpression<string> => sfn('SHA1', [value]);
238
301
 
239
302
  /**
240
303
  * 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.
304
+ *
305
+ * @param value - The input.
306
+ * @param bits - Bit length (e.g., 256, 512).
307
+ * @returns A `TypedExpression<string>` representing the `SHA2` SQL function.
244
308
  */
245
- export const sha2 = (value: OperandInput, bits: OperandInput): FunctionNode => fn('SHA2', [value, bits]);
309
+ export const sha2 = (value: OperandInput, bits: OperandInput): TypedExpression<string> => sfn('SHA2', [value, bits]);
246
310
 
247
311
  /**
248
312
  * Returns the length of a string in bits.
249
- * @param value - The string value.
250
- * @returns A FunctionNode representing the BIT_LENGTH SQL function.
313
+ *
314
+ * @param value - String value or column.
315
+ * @returns A `TypedExpression<number>` representing the `BIT_LENGTH` SQL function.
251
316
  */
252
- export const bitLength = (value: OperandInput): FunctionNode => fn('BIT_LENGTH', [value]);
317
+ export const bitLength = (value: OperandInput): TypedExpression<number> => nfn('BIT_LENGTH', [value]);
253
318
 
254
319
  /**
255
320
  * Returns the length of a string in bytes.
256
- * @param value - The string value.
257
- * @returns A FunctionNode representing the OCTET_LENGTH SQL function.
321
+ *
322
+ * @param value - String value or column.
323
+ * @returns A `TypedExpression<number>` representing the `OCTET_LENGTH` SQL function.
258
324
  */
259
- export const octetLength = (value: OperandInput): FunctionNode => fn('OCTET_LENGTH', [value]);
325
+ export const octetLength = (value: OperandInput): TypedExpression<number> => nfn('OCTET_LENGTH', [value]);
260
326
 
261
327
  /**
262
328
  * Returns a string from an ASCII code.
263
- * @param code - The ASCII code.
264
- * @returns A FunctionNode representing the CHR/CHAR SQL function.
329
+ *
330
+ * @param code - ASCII code.
331
+ * @returns A `TypedExpression<string>` representing the `CHR` SQL function.
265
332
  */
266
- export const chr = (code: OperandInput): FunctionNode => fn('CHR', [code]);
333
+ export const chr = (code: OperandInput): TypedExpression<string> => sfn('CHR', [code]);
267
334
 
@@ -25,13 +25,14 @@ import {
25
25
 
26
26
  import { tableRef, type TableRef } from '../schema/table.js';
27
27
  import {
28
- SelectableKeys,
29
- ColumnDef,
30
- HasManyCollection,
31
- HasOneReference,
32
- BelongsToReference,
33
- ManyToManyCollection
34
- } from '../schema/types.js';
28
+ SelectableKeys,
29
+ ColumnDef,
30
+ HasManyCollection,
31
+ HasOneReference,
32
+ BelongsToReference,
33
+ ManyToManyCollection,
34
+ EntityInstance
35
+ } from '../schema/types.js';
35
36
 
36
37
  const unwrapTarget = (target: EntityOrTableTargetResolver): EntityOrTableTarget => {
37
38
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
@@ -196,9 +197,9 @@ type NonFunctionKeys<T> = {
196
197
  type RelationKeys<TEntity extends object> =
197
198
  Exclude<NonFunctionKeys<TEntity>, SelectableKeys<TEntity>> & string;
198
199
 
199
- type EntityTable<TEntity extends object> =
200
- Omit<TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>, 'relations'> & {
201
- relations: {
200
+ type EntityTable<TEntity extends object> =
201
+ Omit<TableDef<{ [K in SelectableKeys<TEntity>]: ColumnDef }>, 'relations'> & {
202
+ relations: {
202
203
  [K in RelationKeys<TEntity>]:
203
204
  NonNullable<TEntity[K]> extends HasManyCollection<infer TChild>
204
205
  ? HasManyRelation<EntityTable<NonNullable<TChild> & object>>
@@ -214,15 +215,18 @@ type EntityTable<TEntity extends object> =
214
215
  : NonNullable<TEntity[K]> extends object
215
216
  ? BelongsToRelation<EntityTable<NonNullable<TEntity[K]> & object>>
216
217
  : never;
217
- };
218
- };
219
-
220
- export const selectFromEntity = <TEntity extends object>(
221
- ctor: EntityConstructor<TEntity>
222
- ): SelectQueryBuilder<TEntity, EntityTable<TEntity>> => {
223
- const table = getTableDefFromEntity(ctor);
224
- if (!table) {
225
- throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
218
+ };
219
+ };
220
+
221
+ export type DecoratedEntityInstance<TEntity extends object> =
222
+ TEntity & EntityInstance<EntityTable<TEntity>>;
223
+
224
+ export const selectFromEntity = <TEntity extends object>(
225
+ ctor: EntityConstructor<TEntity>
226
+ ): SelectQueryBuilder<DecoratedEntityInstance<TEntity>, EntityTable<TEntity>> => {
227
+ const table = getTableDefFromEntity(ctor);
228
+ if (!table) {
229
+ throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
226
230
  }
227
231
  return new SelectQueryBuilder(
228
232
  table as unknown as EntityTable<TEntity>,
@@ -31,22 +31,20 @@ export class HydrationPlanner {
31
31
  * @param columns - Columns to capture
32
32
  * @returns Updated HydrationPlanner with captured columns
33
33
  */
34
- captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
35
- const currentPlan = this.getPlanOrDefault();
36
- const rootCols = new Set(currentPlan.rootColumns);
37
- let changed = false;
38
-
39
- columns.forEach(node => {
40
- if (node.type !== 'Column') return;
41
- if (node.table !== this.table.name) return;
42
-
43
- const alias = node.alias || node.name;
44
- if (isRelationAlias(alias)) return;
45
- if (!rootCols.has(alias)) {
46
- rootCols.add(alias);
47
- changed = true;
48
- }
49
- });
34
+ captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
35
+ const currentPlan = this.getPlanOrDefault();
36
+ const rootCols = new Set(currentPlan.rootColumns);
37
+ let changed = false;
38
+
39
+ columns.forEach(node => {
40
+ const alias = node.type === 'Column' ? (node.alias || node.name) : node.alias;
41
+ if (!alias || isRelationAlias(alias)) return;
42
+ if (node.type === 'Column' && node.table !== this.table.name) return;
43
+ if (!rootCols.has(alias)) {
44
+ rootCols.add(alias);
45
+ changed = true;
46
+ }
47
+ });
50
48
 
51
49
  if (!changed) return this;
52
50
  return new HydrationPlanner(this.table, {