metal-orm 1.0.2 → 1.0.4

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 (37) hide show
  1. package/README.md +20 -0
  2. package/package.json +1 -1
  3. package/src/ast/expression.ts +433 -175
  4. package/src/ast/join.ts +8 -1
  5. package/src/ast/query.ts +64 -9
  6. package/src/builder/hydration-manager.ts +42 -11
  7. package/src/builder/hydration-planner.ts +80 -31
  8. package/src/builder/operations/column-selector.ts +37 -1
  9. package/src/builder/operations/cte-manager.ts +16 -0
  10. package/src/builder/operations/filter-manager.ts +32 -0
  11. package/src/builder/operations/join-manager.ts +17 -7
  12. package/src/builder/operations/pagination-manager.ts +19 -0
  13. package/src/builder/operations/relation-manager.ts +58 -3
  14. package/src/builder/query-ast-service.ts +100 -29
  15. package/src/builder/relation-conditions.ts +30 -1
  16. package/src/builder/relation-projection-helper.ts +43 -1
  17. package/src/builder/relation-service.ts +68 -13
  18. package/src/builder/relation-types.ts +6 -0
  19. package/src/builder/select-query-builder-deps.ts +64 -3
  20. package/src/builder/select-query-state.ts +72 -0
  21. package/src/builder/select.ts +166 -0
  22. package/src/codegen/typescript.ts +142 -44
  23. package/src/constants/sql-operator-config.ts +36 -0
  24. package/src/constants/sql.ts +125 -58
  25. package/src/dialect/abstract.ts +97 -22
  26. package/src/dialect/mssql/index.ts +27 -0
  27. package/src/dialect/mysql/index.ts +22 -0
  28. package/src/dialect/postgres/index.ts +22 -0
  29. package/src/dialect/sqlite/index.ts +22 -0
  30. package/src/runtime/als.ts +15 -1
  31. package/src/runtime/hydration.ts +20 -15
  32. package/src/schema/column.ts +45 -5
  33. package/src/schema/relation.ts +49 -2
  34. package/src/schema/table.ts +27 -3
  35. package/src/utils/join-node.ts +20 -0
  36. package/src/utils/raw-column-parser.ts +32 -0
  37. package/src/utils/relation-alias.ts +43 -0
@@ -1,106 +1,188 @@
1
- import { ColumnDef } from '../schema/column';
2
- import type { SelectQueryNode, OrderByNode } from './query';
3
- import { OrderDirection } from '../constants/sql';
4
-
5
- export interface LiteralNode {
6
- type: 'Literal';
7
- value: string | number | boolean | null;
8
- }
9
-
10
- export interface ColumnNode {
11
- type: 'Column';
12
- table: string;
13
- name: string;
14
- alias?: string;
15
- }
16
-
17
- export interface FunctionNode {
18
- type: 'Function';
19
- name: string;
20
- args: (ColumnNode | LiteralNode | JsonPathNode)[]; // Allow JSON args
21
- alias?: string;
22
- }
23
-
24
- export interface JsonPathNode {
25
- type: 'JsonPath';
26
- column: ColumnNode;
27
- path: string;
28
- alias?: string;
29
- }
30
-
31
- export interface ScalarSubqueryNode {
32
- type: 'ScalarSubquery';
33
- query: SelectQueryNode;
34
- alias?: string;
35
- }
36
-
37
- export interface CaseExpressionNode {
38
- type: 'CaseExpression';
39
- conditions: { when: ExpressionNode; then: OperandNode }[];
40
- else?: OperandNode;
41
- alias?: string;
42
- }
43
-
44
- export interface WindowFunctionNode {
45
- type: 'WindowFunction';
46
- name: string;
47
- args: (ColumnNode | LiteralNode | JsonPathNode)[];
48
- partitionBy?: ColumnNode[];
49
- orderBy?: OrderByNode[];
50
- alias?: string;
51
- }
52
-
53
- export type OperandNode = ColumnNode | LiteralNode | FunctionNode | JsonPathNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode;
54
-
55
- export interface BinaryExpressionNode {
56
- type: 'BinaryExpression';
57
- left: OperandNode;
58
- operator: string;
59
- right: OperandNode;
60
- escape?: LiteralNode;
61
- }
62
-
63
- export interface LogicalExpressionNode {
64
- type: 'LogicalExpression';
65
- operator: 'AND' | 'OR';
66
- operands: ExpressionNode[];
67
- }
68
-
69
- export interface NullExpressionNode {
70
- type: 'NullExpression';
71
- left: OperandNode;
72
- operator: 'IS NULL' | 'IS NOT NULL';
73
- }
74
-
75
- export interface InExpressionNode {
76
- type: 'InExpression';
77
- left: OperandNode;
78
- operator: 'IN' | 'NOT IN';
79
- right: OperandNode[];
80
- }
81
-
82
- export interface ExistsExpressionNode {
83
- type: 'ExistsExpression';
84
- operator: 'EXISTS' | 'NOT EXISTS';
85
- subquery: SelectQueryNode;
86
- }
87
-
88
- export interface BetweenExpressionNode {
89
- type: 'BetweenExpression';
90
- left: OperandNode;
91
- operator: 'BETWEEN' | 'NOT BETWEEN';
92
- lower: OperandNode;
93
- upper: OperandNode;
94
- }
95
-
96
- export type ExpressionNode =
97
- | BinaryExpressionNode
98
- | LogicalExpressionNode
99
- | NullExpressionNode
100
- | InExpressionNode
101
- | ExistsExpressionNode
102
- | BetweenExpressionNode;
103
-
1
+ import { ColumnDef } from '../schema/column';
2
+ import type { SelectQueryNode, OrderByNode } from './query';
3
+ import { OrderDirection, SqlOperator } from '../constants/sql';
4
+
5
+ /**
6
+ * AST node representing a literal value
7
+ */
8
+ export interface LiteralNode {
9
+ type: 'Literal';
10
+ /** The literal value (string, number, boolean, or null) */
11
+ value: string | number | boolean | null;
12
+ }
13
+
14
+ /**
15
+ * AST node representing a column reference
16
+ */
17
+ export interface ColumnNode {
18
+ type: 'Column';
19
+ /** Table name the column belongs to */
20
+ table: string;
21
+ /** Column name */
22
+ name: string;
23
+ /** Optional alias for the column */
24
+ alias?: string;
25
+ }
26
+
27
+ /**
28
+ * AST node representing a function call
29
+ */
30
+ export interface FunctionNode {
31
+ type: 'Function';
32
+ /** Function name (e.g., COUNT, SUM) */
33
+ name: string;
34
+ /** Function arguments */
35
+ args: (ColumnNode | LiteralNode | JsonPathNode)[];
36
+ /** Optional alias for the function result */
37
+ alias?: string;
38
+ }
39
+
40
+ /**
41
+ * AST node representing a JSON path expression
42
+ */
43
+ export interface JsonPathNode {
44
+ type: 'JsonPath';
45
+ /** Source column */
46
+ column: ColumnNode;
47
+ /** JSON path expression */
48
+ path: string;
49
+ /** Optional alias for the result */
50
+ alias?: string;
51
+ }
52
+
53
+ /**
54
+ * AST node representing a scalar subquery
55
+ */
56
+ export interface ScalarSubqueryNode {
57
+ type: 'ScalarSubquery';
58
+ /** Subquery to execute */
59
+ query: SelectQueryNode;
60
+ /** Optional alias for the subquery result */
61
+ alias?: string;
62
+ }
63
+
64
+ /**
65
+ * AST node representing a CASE expression
66
+ */
67
+ export interface CaseExpressionNode {
68
+ type: 'CaseExpression';
69
+ /** WHEN-THEN conditions */
70
+ conditions: { when: ExpressionNode; then: OperandNode }[];
71
+ /** Optional ELSE clause */
72
+ else?: OperandNode;
73
+ /** Optional alias for the result */
74
+ alias?: string;
75
+ }
76
+
77
+ /**
78
+ * AST node representing a window function
79
+ */
80
+ export interface WindowFunctionNode {
81
+ type: 'WindowFunction';
82
+ /** Window function name (e.g., ROW_NUMBER, RANK) */
83
+ name: string;
84
+ /** Function arguments */
85
+ args: (ColumnNode | LiteralNode | JsonPathNode)[];
86
+ /** Optional PARTITION BY clause */
87
+ partitionBy?: ColumnNode[];
88
+ /** Optional ORDER BY clause */
89
+ orderBy?: OrderByNode[];
90
+ /** Optional alias for the result */
91
+ alias?: string;
92
+ }
93
+
94
+ /**
95
+ * Union type representing any operand that can be used in expressions
96
+ */
97
+ export type OperandNode = ColumnNode | LiteralNode | FunctionNode | JsonPathNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode;
98
+
99
+ /**
100
+ * AST node representing a binary expression (e.g., column = value)
101
+ */
102
+ export interface BinaryExpressionNode {
103
+ type: 'BinaryExpression';
104
+ /** Left operand */
105
+ left: OperandNode;
106
+ /** Comparison operator */
107
+ operator: SqlOperator;
108
+ /** Right operand */
109
+ right: OperandNode;
110
+ /** Optional escape character for LIKE expressions */
111
+ escape?: LiteralNode;
112
+ }
113
+
114
+ /**
115
+ * AST node representing a logical expression (AND/OR)
116
+ */
117
+ export interface LogicalExpressionNode {
118
+ type: 'LogicalExpression';
119
+ /** Logical operator (AND or OR) */
120
+ operator: 'AND' | 'OR';
121
+ /** Operands to combine */
122
+ operands: ExpressionNode[];
123
+ }
124
+
125
+ /**
126
+ * AST node representing a null check expression
127
+ */
128
+ export interface NullExpressionNode {
129
+ type: 'NullExpression';
130
+ /** Operand to check for null */
131
+ left: OperandNode;
132
+ /** Null check operator */
133
+ operator: 'IS NULL' | 'IS NOT NULL';
134
+ }
135
+
136
+ /**
137
+ * AST node representing an IN/NOT IN expression
138
+ */
139
+ export interface InExpressionNode {
140
+ type: 'InExpression';
141
+ /** Left operand to check */
142
+ left: OperandNode;
143
+ /** IN/NOT IN operator */
144
+ operator: 'IN' | 'NOT IN';
145
+ /** Values to check against */
146
+ right: OperandNode[];
147
+ }
148
+
149
+ /**
150
+ * AST node representing an EXISTS/NOT EXISTS expression
151
+ */
152
+ export interface ExistsExpressionNode {
153
+ type: 'ExistsExpression';
154
+ /** EXISTS/NOT EXISTS operator */
155
+ operator: SqlOperator;
156
+ /** Subquery to check */
157
+ subquery: SelectQueryNode;
158
+ }
159
+
160
+ /**
161
+ * AST node representing a BETWEEN/NOT BETWEEN expression
162
+ */
163
+ export interface BetweenExpressionNode {
164
+ type: 'BetweenExpression';
165
+ /** Operand to check */
166
+ left: OperandNode;
167
+ /** BETWEEN/NOT BETWEEN operator */
168
+ operator: 'BETWEEN' | 'NOT BETWEEN';
169
+ /** Lower bound */
170
+ lower: OperandNode;
171
+ /** Upper bound */
172
+ upper: OperandNode;
173
+ }
174
+
175
+ /**
176
+ * Union type representing any supported expression node
177
+ */
178
+ export type ExpressionNode =
179
+ | BinaryExpressionNode
180
+ | LogicalExpressionNode
181
+ | NullExpressionNode
182
+ | InExpressionNode
183
+ | ExistsExpressionNode
184
+ | BetweenExpressionNode;
185
+
104
186
  const operandTypes = new Set<OperandNode['type']>([
105
187
  'Column',
106
188
  'Literal',
@@ -114,19 +196,27 @@ const operandTypes = new Set<OperandNode['type']>([
114
196
  const isOperandNode = (node: any): node is OperandNode => {
115
197
  return node && operandTypes.has(node.type);
116
198
  };
117
-
118
- // Helper to convert Schema definition to AST Node
119
- const toNode = (col: ColumnDef | OperandNode): OperandNode => {
120
- if (isOperandNode(col)) return col as OperandNode;
121
- const def = col as ColumnDef;
122
- return { type: 'Column', table: def.table || 'unknown', name: def.name };
123
- };
124
-
125
- const toLiteralNode = (value: string | number | boolean | null): LiteralNode => ({
126
- type: 'Literal',
127
- value
128
- });
129
-
199
+
200
+ export const isFunctionNode = (node: any): node is FunctionNode => node?.type === 'Function';
201
+ export const isCaseExpressionNode = (node: any): node is CaseExpressionNode => node?.type === 'CaseExpression';
202
+ export const isWindowFunctionNode = (node: any): node is WindowFunctionNode => node?.type === 'WindowFunction';
203
+ export const isExpressionSelectionNode = (
204
+ node: ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode
205
+ ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
206
+ isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
207
+
208
+ // Helper to convert Schema definition to AST Node
209
+ const toNode = (col: ColumnDef | OperandNode): OperandNode => {
210
+ if (isOperandNode(col)) return col as OperandNode;
211
+ const def = col as ColumnDef;
212
+ return { type: 'Column', table: def.table || 'unknown', name: def.name };
213
+ };
214
+
215
+ const toLiteralNode = (value: string | number | boolean | null): LiteralNode => ({
216
+ type: 'Literal',
217
+ value
218
+ });
219
+
130
220
  const toOperand = (val: OperandNode | ColumnDef | string | number | boolean | null): OperandNode => {
131
221
  if (val === null) return { type: 'Literal', value: null };
132
222
  if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
@@ -134,10 +224,10 @@ const toOperand = (val: OperandNode | ColumnDef | string | number | boolean | nu
134
224
  }
135
225
  return toNode(val as OperandNode | ColumnDef);
136
226
  };
137
-
227
+
138
228
  // Factories
139
229
  const createBinaryExpression = (
140
- operator: string,
230
+ operator: SqlOperator,
141
231
  left: OperandNode | ColumnDef,
142
232
  right: OperandNode | ColumnDef | string | number | boolean | null,
143
233
  escape?: string
@@ -156,39 +246,91 @@ const createBinaryExpression = (
156
246
  return node;
157
247
  };
158
248
 
249
+ /**
250
+ * Creates an equality expression (left = right)
251
+ * @param left - Left operand
252
+ * @param right - Right operand
253
+ * @returns Binary expression node with equality operator
254
+ */
159
255
  export const eq = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
160
256
  createBinaryExpression('=', left, right);
161
257
 
258
+ /**
259
+ * Creates a greater-than expression (left > right)
260
+ * @param left - Left operand
261
+ * @param right - Right operand
262
+ * @returns Binary expression node with greater-than operator
263
+ */
162
264
  export const gt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
163
265
  createBinaryExpression('>', left, right);
164
266
 
267
+ /**
268
+ * Creates a less-than expression (left < right)
269
+ * @param left - Left operand
270
+ * @param right - Right operand
271
+ * @returns Binary expression node with less-than operator
272
+ */
165
273
  export const lt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
166
274
  createBinaryExpression('<', left, right);
167
275
 
276
+ /**
277
+ * Creates a LIKE pattern matching expression
278
+ * @param left - Left operand
279
+ * @param pattern - Pattern to match
280
+ * @param escape - Optional escape character
281
+ * @returns Binary expression node with LIKE operator
282
+ */
168
283
  export const like = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
169
284
  createBinaryExpression('LIKE', left, pattern, escape);
170
285
 
286
+ /**
287
+ * Creates a NOT LIKE pattern matching expression
288
+ * @param left - Left operand
289
+ * @param pattern - Pattern to match
290
+ * @param escape - Optional escape character
291
+ * @returns Binary expression node with NOT LIKE operator
292
+ */
171
293
  export const notLike = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
172
294
  createBinaryExpression('NOT LIKE', left, pattern, escape);
173
-
174
- export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
175
- type: 'LogicalExpression',
176
- operator: 'AND',
177
- operands
178
- });
179
-
180
- export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
181
- type: 'LogicalExpression',
182
- operator: 'OR',
183
- operands
184
- });
185
-
186
- export const isNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
187
- type: 'NullExpression',
188
- left: toNode(left),
189
- operator: 'IS NULL'
190
- });
191
-
295
+
296
+ /**
297
+ * Creates a logical AND expression
298
+ * @param operands - Expressions to combine with AND
299
+ * @returns Logical expression node with AND operator
300
+ */
301
+ export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
302
+ type: 'LogicalExpression',
303
+ operator: 'AND',
304
+ operands
305
+ });
306
+
307
+ /**
308
+ * Creates a logical OR expression
309
+ * @param operands - Expressions to combine with OR
310
+ * @returns Logical expression node with OR operator
311
+ */
312
+ export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
313
+ type: 'LogicalExpression',
314
+ operator: 'OR',
315
+ operands
316
+ });
317
+
318
+ /**
319
+ * Creates an IS NULL expression
320
+ * @param left - Operand to check for null
321
+ * @returns Null expression node with IS NULL operator
322
+ */
323
+ export const isNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
324
+ type: 'NullExpression',
325
+ left: toNode(left),
326
+ operator: 'IS NULL'
327
+ });
328
+
329
+ /**
330
+ * Creates an IS NOT NULL expression
331
+ * @param left - Operand to check for non-null
332
+ * @returns Null expression node with IS NOT NULL operator
333
+ */
192
334
  export const isNotNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
193
335
  type: 'NullExpression',
194
336
  left: toNode(left),
@@ -206,9 +348,21 @@ const createInExpression = (
206
348
  right: values.map(v => toOperand(v))
207
349
  });
208
350
 
351
+ /**
352
+ * Creates an IN expression (value IN list)
353
+ * @param left - Operand to check
354
+ * @param values - Values to check against
355
+ * @returns IN expression node
356
+ */
209
357
  export const inList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
210
358
  createInExpression('IN', left, values);
211
359
 
360
+ /**
361
+ * Creates a NOT IN expression (value NOT IN list)
362
+ * @param left - Operand to check
363
+ * @param values - Values to check against
364
+ * @returns NOT IN expression node
365
+ */
212
366
  export const notInList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
213
367
  createInExpression('NOT IN', left, values);
214
368
 
@@ -225,54 +379,105 @@ const createBetweenExpression = (
225
379
  upper: toOperand(upper)
226
380
  });
227
381
 
382
+ /**
383
+ * Creates a BETWEEN expression (value BETWEEN lower AND upper)
384
+ * @param left - Operand to check
385
+ * @param lower - Lower bound
386
+ * @param upper - Upper bound
387
+ * @returns BETWEEN expression node
388
+ */
228
389
  export const between = (
229
390
  left: OperandNode | ColumnDef,
230
391
  lower: OperandNode | ColumnDef | string | number,
231
392
  upper: OperandNode | ColumnDef | string | number
232
393
  ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
233
394
 
395
+ /**
396
+ * Creates a NOT BETWEEN expression (value NOT BETWEEN lower AND upper)
397
+ * @param left - Operand to check
398
+ * @param lower - Lower bound
399
+ * @param upper - Upper bound
400
+ * @returns NOT BETWEEN expression node
401
+ */
234
402
  export const notBetween = (
235
403
  left: OperandNode | ColumnDef,
236
404
  lower: OperandNode | ColumnDef | string | number,
237
405
  upper: OperandNode | ColumnDef | string | number
238
406
  ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
239
-
240
- export const jsonPath = (col: ColumnDef | ColumnNode, path: string): JsonPathNode => ({
241
- type: 'JsonPath',
242
- column: toNode(col) as ColumnNode,
243
- path
244
- });
245
-
246
- export const count = (col: ColumnDef | ColumnNode): FunctionNode => ({
247
- type: 'Function',
248
- name: 'COUNT',
249
- args: [toNode(col) as ColumnNode]
250
- });
251
-
252
- export const sum = (col: ColumnDef | ColumnNode): FunctionNode => ({
253
- type: 'Function',
254
- name: 'SUM',
255
- args: [toNode(col) as ColumnNode]
256
- });
257
-
258
- export const avg = (col: ColumnDef | ColumnNode): FunctionNode => ({
259
- type: 'Function',
260
- name: 'AVG',
261
- args: [toNode(col) as ColumnNode]
262
- });
263
-
264
- export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
265
- type: 'ExistsExpression',
266
- operator: 'EXISTS',
267
- subquery
268
- });
269
-
270
- export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
271
- type: 'ExistsExpression',
272
- operator: 'NOT EXISTS',
273
- subquery
274
- });
275
-
407
+
408
+ /**
409
+ * Creates a JSON path expression
410
+ * @param col - Source column
411
+ * @param path - JSON path expression
412
+ * @returns JSON path node
413
+ */
414
+ export const jsonPath = (col: ColumnDef | ColumnNode, path: string): JsonPathNode => ({
415
+ type: 'JsonPath',
416
+ column: toNode(col) as ColumnNode,
417
+ path
418
+ });
419
+
420
+ /**
421
+ * Creates a COUNT function expression
422
+ * @param col - Column to count
423
+ * @returns Function node with COUNT
424
+ */
425
+ export const count = (col: ColumnDef | ColumnNode): FunctionNode => ({
426
+ type: 'Function',
427
+ name: 'COUNT',
428
+ args: [toNode(col) as ColumnNode]
429
+ });
430
+
431
+ /**
432
+ * Creates a SUM function expression
433
+ * @param col - Column to sum
434
+ * @returns Function node with SUM
435
+ */
436
+ export const sum = (col: ColumnDef | ColumnNode): FunctionNode => ({
437
+ type: 'Function',
438
+ name: 'SUM',
439
+ args: [toNode(col) as ColumnNode]
440
+ });
441
+
442
+ /**
443
+ * Creates an AVG function expression
444
+ * @param col - Column to average
445
+ * @returns Function node with AVG
446
+ */
447
+ export const avg = (col: ColumnDef | ColumnNode): FunctionNode => ({
448
+ type: 'Function',
449
+ name: 'AVG',
450
+ args: [toNode(col) as ColumnNode]
451
+ });
452
+
453
+ /**
454
+ * Creates an EXISTS expression
455
+ * @param subquery - Subquery to check for existence
456
+ * @returns EXISTS expression node
457
+ */
458
+ export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
459
+ type: 'ExistsExpression',
460
+ operator: 'EXISTS',
461
+ subquery
462
+ });
463
+
464
+ /**
465
+ * Creates a NOT EXISTS expression
466
+ * @param subquery - Subquery to check for non-existence
467
+ * @returns NOT EXISTS expression node
468
+ */
469
+ export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
470
+ type: 'ExistsExpression',
471
+ operator: 'NOT EXISTS',
472
+ subquery
473
+ });
474
+
475
+ /**
476
+ * Creates a CASE expression
477
+ * @param conditions - Array of WHEN-THEN conditions
478
+ * @param elseValue - Optional ELSE value
479
+ * @returns CASE expression node
480
+ */
276
481
  export const caseWhen = (
277
482
  conditions: { when: ExpressionNode; then: OperandNode | ColumnDef | string | number | boolean | null }[],
278
483
  elseValue?: OperandNode | ColumnDef | string | number | boolean | null
@@ -284,7 +489,7 @@ export const caseWhen = (
284
489
  })),
285
490
  else: elseValue !== undefined ? toOperand(elseValue) : undefined
286
491
  });
287
-
492
+
288
493
  // Window function factories
289
494
  const buildWindowFunction = (
290
495
  name: string,
@@ -309,13 +514,40 @@ const buildWindowFunction = (
309
514
  return node;
310
515
  };
311
516
 
517
+ /**
518
+ * Creates a ROW_NUMBER window function
519
+ * @returns Window function node for ROW_NUMBER
520
+ */
312
521
  export const rowNumber = (): WindowFunctionNode => buildWindowFunction('ROW_NUMBER');
522
+
523
+ /**
524
+ * Creates a RANK window function
525
+ * @returns Window function node for RANK
526
+ */
313
527
  export const rank = (): WindowFunctionNode => buildWindowFunction('RANK');
528
+
529
+ /**
530
+ * Creates a DENSE_RANK window function
531
+ * @returns Window function node for DENSE_RANK
532
+ */
314
533
  export const denseRank = (): WindowFunctionNode => buildWindowFunction('DENSE_RANK');
534
+
535
+ /**
536
+ * Creates an NTILE window function
537
+ * @param n - Number of buckets
538
+ * @returns Window function node for NTILE
539
+ */
315
540
  export const ntile = (n: number): WindowFunctionNode => buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
316
541
 
317
542
  const columnOperand = (col: ColumnDef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
318
543
 
544
+ /**
545
+ * Creates a LAG window function
546
+ * @param col - Column to lag
547
+ * @param offset - Offset (defaults to 1)
548
+ * @param defaultValue - Default value if no row exists
549
+ * @returns Window function node for LAG
550
+ */
319
551
  export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
320
552
  const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [columnOperand(col), { type: 'Literal', value: offset }];
321
553
  if (defaultValue !== undefined) {
@@ -324,6 +556,13 @@ export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValu
324
556
  return buildWindowFunction('LAG', args);
325
557
  };
326
558
 
559
+ /**
560
+ * Creates a LEAD window function
561
+ * @param col - Column to lead
562
+ * @param offset - Offset (defaults to 1)
563
+ * @param defaultValue - Default value if no row exists
564
+ * @returns Window function node for LEAD
565
+ */
327
566
  export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
328
567
  const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [columnOperand(col), { type: 'Literal', value: offset }];
329
568
  if (defaultValue !== undefined) {
@@ -332,9 +571,28 @@ export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultVal
332
571
  return buildWindowFunction('LEAD', args);
333
572
  };
334
573
 
574
+ /**
575
+ * Creates a FIRST_VALUE window function
576
+ * @param col - Column to get first value from
577
+ * @returns Window function node for FIRST_VALUE
578
+ */
335
579
  export const firstValue = (col: ColumnDef | ColumnNode): WindowFunctionNode => buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
580
+
581
+ /**
582
+ * Creates a LAST_VALUE window function
583
+ * @param col - Column to get last value from
584
+ * @returns Window function node for LAST_VALUE
585
+ */
336
586
  export const lastValue = (col: ColumnDef | ColumnNode): WindowFunctionNode => buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
337
587
 
588
+ /**
589
+ * Creates a custom window function
590
+ * @param name - Window function name
591
+ * @param args - Function arguments
592
+ * @param partitionBy - Optional PARTITION BY columns
593
+ * @param orderBy - Optional ORDER BY clauses
594
+ * @returns Window function node
595
+ */
338
596
  export const windowFunction = (
339
597
  name: string,
340
598
  args: (ColumnDef | ColumnNode | LiteralNode | JsonPathNode)[] = [],