metal-orm 1.0.7 → 1.0.9

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 (153) hide show
  1. package/README.md +133 -121
  2. package/dist/decorators/index.cjs +2564 -0
  3. package/dist/decorators/index.cjs.map +1 -0
  4. package/dist/decorators/index.d.cts +53 -0
  5. package/dist/decorators/index.d.ts +53 -0
  6. package/dist/decorators/index.js +2530 -0
  7. package/dist/decorators/index.js.map +1 -0
  8. package/dist/index.cjs +4227 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +701 -0
  11. package/dist/index.d.ts +701 -0
  12. package/dist/index.js +4131 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/select-654m4qy8.d.cts +1522 -0
  15. package/dist/select-654m4qy8.d.ts +1522 -0
  16. package/package.json +27 -20
  17. package/src/codegen/typescript.ts +405 -393
  18. package/src/core/ast/aggregate-functions.ts +30 -0
  19. package/src/core/ast/builders.ts +43 -0
  20. package/src/core/ast/expression-builders.ts +310 -0
  21. package/src/core/ast/expression-nodes.ts +211 -0
  22. package/src/core/ast/expression-visitor.ts +99 -0
  23. package/src/core/ast/expression.ts +5 -0
  24. package/src/{utils → core/ast}/join-node.ts +20 -20
  25. package/src/{ast → core/ast}/join.ts +18 -18
  26. package/src/{ast → core/ast}/query.ts +113 -113
  27. package/src/core/ast/window-functions.ts +140 -0
  28. package/src/{dialect → core/dialect}/abstract.ts +94 -94
  29. package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
  30. package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
  31. package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
  32. package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
  33. package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
  34. package/src/decorators/bootstrap.ts +126 -0
  35. package/src/decorators/column.ts +78 -0
  36. package/src/decorators/entity.ts +36 -0
  37. package/src/decorators/index.ts +4 -0
  38. package/src/decorators/relations.ts +107 -0
  39. package/src/global.d.ts +1 -0
  40. package/src/index.ts +22 -22
  41. package/src/orm/db-executor.ts +11 -0
  42. package/src/orm/domain-event-bus.ts +52 -0
  43. package/src/{runtime → orm}/entity-meta.ts +52 -52
  44. package/src/orm/entity-metadata.ts +140 -0
  45. package/src/{runtime → orm}/entity.ts +252 -252
  46. package/src/{runtime → orm}/execute.ts +36 -36
  47. package/src/{runtime → orm}/hydration.ts +103 -103
  48. package/src/orm/identity-map.ts +37 -0
  49. package/src/{runtime → orm}/lazy-batch.ts +205 -205
  50. package/src/orm/orm-context.ts +154 -0
  51. package/src/orm/relation-change-processor.ts +140 -0
  52. package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
  53. package/src/{runtime → orm}/relations/has-many.ts +111 -111
  54. package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
  55. package/src/orm/runtime-types.ts +39 -0
  56. package/src/orm/transaction-runner.ts +17 -0
  57. package/src/orm/unit-of-work.ts +232 -0
  58. package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
  59. package/src/{builder → query-builder}/delete-query-state.ts +38 -42
  60. package/src/{builder → query-builder}/delete.ts +46 -57
  61. package/src/{builder → query-builder}/hydration-manager.ts +87 -87
  62. package/src/{builder → query-builder}/hydration-planner.ts +182 -182
  63. package/src/{builder → query-builder}/insert-query-state.ts +51 -62
  64. package/src/{builder → query-builder}/insert.ts +48 -59
  65. package/src/{builder → query-builder}/query-ast-service.ts +208 -226
  66. package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
  67. package/src/{builder → query-builder}/relation-conditions.ts +112 -112
  68. package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
  69. package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
  70. package/src/{builder → query-builder}/relation-service.ts +284 -284
  71. package/src/{builder → query-builder}/relation-types.ts +21 -21
  72. package/src/{builder → query-builder}/relation-utils.ts +12 -12
  73. package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
  74. package/src/{builder → query-builder}/select-query-state.ts +179 -179
  75. package/src/{builder → query-builder}/select.ts +78 -69
  76. package/src/{builder → query-builder}/update-query-state.ts +55 -59
  77. package/src/{builder → query-builder}/update.ts +50 -61
  78. package/src/schema/column.ts +25 -25
  79. package/src/schema/relation.ts +116 -116
  80. package/src/schema/table.ts +34 -34
  81. package/src/schema/types.ts +76 -76
  82. package/.github/workflows/publish-metal-orm.yml +0 -38
  83. package/ROADMAP.md +0 -125
  84. package/docs/CHANGES.md +0 -104
  85. package/docs/advanced-features.md +0 -176
  86. package/docs/api-reference.md +0 -31
  87. package/docs/dml-operations.md +0 -156
  88. package/docs/getting-started.md +0 -171
  89. package/docs/hydration.md +0 -115
  90. package/docs/index.md +0 -36
  91. package/docs/multi-dialect-support.md +0 -59
  92. package/docs/query-builder.md +0 -135
  93. package/docs/runtime.md +0 -105
  94. package/docs/schema-definition.md +0 -112
  95. package/metadata.json +0 -5
  96. package/playground/api/playground-api.ts +0 -94
  97. package/playground/index.html +0 -15
  98. package/playground/src/App.css +0 -1
  99. package/playground/src/App.tsx +0 -114
  100. package/playground/src/components/CodeDisplay.tsx +0 -43
  101. package/playground/src/components/QueryExecutor.tsx +0 -189
  102. package/playground/src/components/ResultsTable.tsx +0 -67
  103. package/playground/src/components/ResultsTabs.tsx +0 -105
  104. package/playground/src/components/ScenarioList.tsx +0 -56
  105. package/playground/src/components/logo.svg +0 -45
  106. package/playground/src/data/scenarios.ts +0 -2
  107. package/playground/src/main.tsx +0 -9
  108. package/playground/src/services/PlaygroundApiService.ts +0 -60
  109. package/postcss.config.cjs +0 -5
  110. package/sql_sql-ansi-cheatsheet-2025.md +0 -264
  111. package/src/ast/expression.ts +0 -658
  112. package/src/builder/operations/cte-manager.ts +0 -34
  113. package/src/builder/operations/filter-manager.ts +0 -68
  114. package/src/builder/operations/join-manager.ts +0 -36
  115. package/src/builder/operations/pagination-manager.ts +0 -36
  116. package/src/playground/features/playground/api/types.ts +0 -16
  117. package/src/playground/features/playground/clients/MockClient.ts +0 -17
  118. package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
  119. package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
  120. package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
  121. package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
  122. package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
  123. package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
  124. package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
  125. package/src/playground/features/playground/data/scenarios/index.ts +0 -29
  126. package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
  127. package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
  128. package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
  129. package/src/playground/features/playground/data/scenarios/types.ts +0 -70
  130. package/src/playground/features/playground/data/schema.ts +0 -91
  131. package/src/playground/features/playground/data/seed.ts +0 -104
  132. package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
  133. package/src/runtime/orm-context.ts +0 -539
  134. package/tests/belongs-to-many.test.ts +0 -57
  135. package/tests/between.test.ts +0 -43
  136. package/tests/case-expression.test.ts +0 -58
  137. package/tests/complex-exists.test.ts +0 -230
  138. package/tests/cte.test.ts +0 -118
  139. package/tests/dml.test.ts +0 -206
  140. package/tests/exists.test.ts +0 -127
  141. package/tests/like.test.ts +0 -33
  142. package/tests/orm-runtime.test.ts +0 -254
  143. package/tests/postgres.test.ts +0 -30
  144. package/tests/right-join.test.ts +0 -89
  145. package/tests/subquery-having.test.ts +0 -193
  146. package/tests/window-function.test.ts +0 -151
  147. package/tsconfig.json +0 -30
  148. package/tsup.config.ts +0 -10
  149. package/vite.config.ts +0 -22
  150. package/vitest.config.ts +0 -14
  151. /package/src/{constants → core/sql}/sql.ts +0 -0
  152. /package/src/{runtime → orm}/als.ts +0 -0
  153. /package/src/{utils → query-builder}/relation-alias.ts +0 -0
@@ -0,0 +1,30 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { ColumnNode, FunctionNode } from './expression-nodes.js';
3
+ import { columnOperand } from './expression-builders.js';
4
+
5
+ const buildAggregate = (name: string) => (col: ColumnDef | ColumnNode): FunctionNode => ({
6
+ type: 'Function',
7
+ name,
8
+ args: [columnOperand(col)]
9
+ });
10
+
11
+ /**
12
+ * Creates a COUNT function expression
13
+ * @param col - Column to count
14
+ * @returns Function node with COUNT
15
+ */
16
+ export const count = buildAggregate('COUNT');
17
+
18
+ /**
19
+ * Creates a SUM function expression
20
+ * @param col - Column to sum
21
+ * @returns Function node with SUM
22
+ */
23
+ export const sum = buildAggregate('SUM');
24
+
25
+ /**
26
+ * Creates an AVG function expression
27
+ * @param col - Column to average
28
+ * @returns Function node with AVG
29
+ */
30
+ export const avg = buildAggregate('AVG');
@@ -0,0 +1,43 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { TableDef } from '../../schema/table.js';
3
+ import { ColumnNode } from './expression-nodes.js';
4
+ import { TableNode } from './query.js';
5
+
6
+ /**
7
+ * Builds or normalizes a column AST node from a column definition or existing node
8
+ * @param table - Table definition providing a default table name
9
+ * @param column - Column definition or existing column node
10
+ */
11
+ export const buildColumnNode = (table: TableDef, column: ColumnDef | ColumnNode): ColumnNode => {
12
+ if ((column as ColumnNode).type === 'Column') {
13
+ return column as ColumnNode;
14
+ }
15
+
16
+ const def = column as ColumnDef;
17
+ return {
18
+ type: 'Column',
19
+ table: def.table || table.name,
20
+ name: def.name
21
+ };
22
+ };
23
+
24
+ /**
25
+ * Builds column AST nodes for a list of column names
26
+ * @param table - Table definition providing the table name
27
+ * @param names - Column names
28
+ */
29
+ export const buildColumnNodes = (table: TableDef, names: string[]): ColumnNode[] =>
30
+ names.map(name => ({
31
+ type: 'Column',
32
+ table: table.name,
33
+ name
34
+ }));
35
+
36
+ /**
37
+ * Builds a table AST node for the provided table definition
38
+ * @param table - Table definition
39
+ */
40
+ export const createTableNode = (table: TableDef): TableNode => ({
41
+ type: 'Table',
42
+ name: table.name
43
+ });
@@ -0,0 +1,310 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { SelectQueryNode } from './query.js';
3
+ import { SqlOperator } from '../sql/sql.js';
4
+ import {
5
+ ColumnNode,
6
+ FunctionNode,
7
+ LiteralNode,
8
+ JsonPathNode,
9
+ OperandNode,
10
+ CaseExpressionNode,
11
+ WindowFunctionNode,
12
+ BinaryExpressionNode,
13
+ ExpressionNode,
14
+ LogicalExpressionNode,
15
+ NullExpressionNode,
16
+ InExpressionNode,
17
+ ExistsExpressionNode,
18
+ BetweenExpressionNode,
19
+ isOperandNode
20
+ } from './expression-nodes.js';
21
+
22
+ /**
23
+ * Converts a primitive or existing operand into an operand node
24
+ * @param value - Value or operand to normalize
25
+ * @returns OperandNode representing the value
26
+ */
27
+ export const valueToOperand = (value: unknown): OperandNode => {
28
+ if (
29
+ value === null ||
30
+ value === undefined ||
31
+ typeof value === 'string' ||
32
+ typeof value === 'number' ||
33
+ typeof value === 'boolean'
34
+ ) {
35
+ return { type: 'Literal', value: value === undefined ? null : value } as LiteralNode;
36
+ }
37
+ return value as OperandNode;
38
+ };
39
+
40
+ const toNode = (col: ColumnDef | OperandNode): OperandNode => {
41
+ if (isOperandNode(col)) return col as OperandNode;
42
+ const def = col as ColumnDef;
43
+ return { type: 'Column', table: def.table || 'unknown', name: def.name };
44
+ };
45
+
46
+ const toLiteralNode = (value: string | number | boolean | null): LiteralNode => ({
47
+ type: 'Literal',
48
+ value
49
+ });
50
+
51
+ const toOperand = (val: OperandNode | ColumnDef | string | number | boolean | null): OperandNode => {
52
+ if (val === null) return { type: 'Literal', value: null };
53
+ if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
54
+ return { type: 'Literal', value: val };
55
+ }
56
+ return toNode(val as OperandNode | ColumnDef);
57
+ };
58
+
59
+ export const columnOperand = (col: ColumnDef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
60
+
61
+ const createBinaryExpression = (
62
+ operator: SqlOperator,
63
+ left: OperandNode | ColumnDef,
64
+ right: OperandNode | ColumnDef | string | number | boolean | null,
65
+ escape?: string
66
+ ): BinaryExpressionNode => {
67
+ const node: BinaryExpressionNode = {
68
+ type: 'BinaryExpression',
69
+ left: toNode(left),
70
+ operator,
71
+ right: toOperand(right)
72
+ };
73
+
74
+ if (escape !== undefined) {
75
+ node.escape = toLiteralNode(escape);
76
+ }
77
+
78
+ return node;
79
+ };
80
+
81
+ /**
82
+ * Creates an equality expression (left = right)
83
+ * @param left - Left operand
84
+ * @param right - Right operand
85
+ * @returns Binary expression node with equality operator
86
+ */
87
+ export const eq = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
88
+ createBinaryExpression('=', left, right);
89
+
90
+ /**
91
+ * Creates a not equal expression (left != right)
92
+ */
93
+ export const neq = (
94
+ left: OperandNode | ColumnDef,
95
+ right: OperandNode | ColumnDef | string | number
96
+ ): BinaryExpressionNode => createBinaryExpression('!=', left, right);
97
+
98
+ /**
99
+ * Creates a greater-than expression (left > right)
100
+ * @param left - Left operand
101
+ * @param right - Right operand
102
+ * @returns Binary expression node with greater-than operator
103
+ */
104
+ export const gt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
105
+ createBinaryExpression('>', left, right);
106
+
107
+ /**
108
+ * Creates a greater than or equal expression (left >= right)
109
+ */
110
+ export const gte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
111
+ createBinaryExpression('>=', left, right);
112
+
113
+ /**
114
+ * Creates a less-than expression (left < right)
115
+ * @param left - Left operand
116
+ * @param right - Right operand
117
+ * @returns Binary expression node with less-than operator
118
+ */
119
+ export const lt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
120
+ createBinaryExpression('<', left, right);
121
+
122
+ /**
123
+ * Creates a less than or equal expression (left <= right)
124
+ */
125
+ export const lte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
126
+ createBinaryExpression('<=', left, right);
127
+
128
+ /**
129
+ * Creates a LIKE pattern matching expression
130
+ * @param left - Left operand
131
+ * @param pattern - Pattern to match
132
+ * @param escape - Optional escape character
133
+ * @returns Binary expression node with LIKE operator
134
+ */
135
+ export const like = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
136
+ createBinaryExpression('LIKE', left, pattern, escape);
137
+
138
+ /**
139
+ * Creates a NOT LIKE pattern matching expression
140
+ * @param left - Left operand
141
+ * @param pattern - Pattern to match
142
+ * @param escape - Optional escape character
143
+ * @returns Binary expression node with NOT LIKE operator
144
+ */
145
+ export const notLike = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
146
+ createBinaryExpression('NOT LIKE', left, pattern, escape);
147
+
148
+ /**
149
+ * Creates a logical AND expression
150
+ * @param operands - Expressions to combine with AND
151
+ * @returns Logical expression node with AND operator
152
+ */
153
+ export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
154
+ type: 'LogicalExpression',
155
+ operator: 'AND',
156
+ operands
157
+ });
158
+
159
+ /**
160
+ * Creates a logical OR expression
161
+ * @param operands - Expressions to combine with OR
162
+ * @returns Logical expression node with OR operator
163
+ */
164
+ export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
165
+ type: 'LogicalExpression',
166
+ operator: 'OR',
167
+ operands
168
+ });
169
+
170
+ /**
171
+ * Creates an IS NULL expression
172
+ * @param left - Operand to check for null
173
+ * @returns Null expression node with IS NULL operator
174
+ */
175
+ export const isNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
176
+ type: 'NullExpression',
177
+ left: toNode(left),
178
+ operator: 'IS NULL'
179
+ });
180
+
181
+ /**
182
+ * Creates an IS NOT NULL expression
183
+ * @param left - Operand to check for non-null
184
+ * @returns Null expression node with IS NOT NULL operator
185
+ */
186
+ export const isNotNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
187
+ type: 'NullExpression',
188
+ left: toNode(left),
189
+ operator: 'IS NOT NULL'
190
+ });
191
+
192
+ const createInExpression = (
193
+ operator: 'IN' | 'NOT IN',
194
+ left: OperandNode | ColumnDef,
195
+ values: (string | number | LiteralNode)[]
196
+ ): InExpressionNode => ({
197
+ type: 'InExpression',
198
+ left: toNode(left),
199
+ operator,
200
+ right: values.map(v => toOperand(v))
201
+ });
202
+
203
+ /**
204
+ * Creates an IN expression (value IN list)
205
+ * @param left - Operand to check
206
+ * @param values - Values to check against
207
+ * @returns IN expression node
208
+ */
209
+ export const inList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
210
+ createInExpression('IN', left, values);
211
+
212
+ /**
213
+ * Creates a NOT IN expression (value NOT IN list)
214
+ * @param left - Operand to check
215
+ * @param values - Values to check against
216
+ * @returns NOT IN expression node
217
+ */
218
+ export const notInList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
219
+ createInExpression('NOT IN', left, values);
220
+
221
+ const createBetweenExpression = (
222
+ operator: 'BETWEEN' | 'NOT BETWEEN',
223
+ left: OperandNode | ColumnDef,
224
+ lower: OperandNode | ColumnDef | string | number,
225
+ upper: OperandNode | ColumnDef | string | number
226
+ ): BetweenExpressionNode => ({
227
+ type: 'BetweenExpression',
228
+ left: toNode(left),
229
+ operator,
230
+ lower: toOperand(lower),
231
+ upper: toOperand(upper)
232
+ });
233
+
234
+ /**
235
+ * Creates a BETWEEN expression (value BETWEEN lower AND upper)
236
+ * @param left - Operand to check
237
+ * @param lower - Lower bound
238
+ * @param upper - Upper bound
239
+ * @returns BETWEEN expression node
240
+ */
241
+ export const between = (
242
+ left: OperandNode | ColumnDef,
243
+ lower: OperandNode | ColumnDef | string | number,
244
+ upper: OperandNode | ColumnDef | string | number
245
+ ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
246
+
247
+ /**
248
+ * Creates a NOT BETWEEN expression (value NOT BETWEEN lower AND upper)
249
+ * @param left - Operand to check
250
+ * @param lower - Lower bound
251
+ * @param upper - Upper bound
252
+ * @returns NOT BETWEEN expression node
253
+ */
254
+ export const notBetween = (
255
+ left: OperandNode | ColumnDef,
256
+ lower: OperandNode | ColumnDef | string | number,
257
+ upper: OperandNode | ColumnDef | string | number
258
+ ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
259
+
260
+ /**
261
+ * Creates a JSON path expression
262
+ * @param col - Source column
263
+ * @param path - JSON path expression
264
+ * @returns JSON path node
265
+ */
266
+ export const jsonPath = (col: ColumnDef | ColumnNode, path: string): JsonPathNode => ({
267
+ type: 'JsonPath',
268
+ column: columnOperand(col),
269
+ path
270
+ });
271
+
272
+ /**
273
+ * Creates a CASE expression
274
+ * @param conditions - Array of WHEN-THEN conditions
275
+ * @param elseValue - Optional ELSE value
276
+ * @returns CASE expression node
277
+ */
278
+ export const caseWhen = (
279
+ conditions: { when: ExpressionNode; then: OperandNode | ColumnDef | string | number | boolean | null }[],
280
+ elseValue?: OperandNode | ColumnDef | string | number | boolean | null
281
+ ): CaseExpressionNode => ({
282
+ type: 'CaseExpression',
283
+ conditions: conditions.map(c => ({
284
+ when: c.when,
285
+ then: toOperand(c.then)
286
+ })),
287
+ else: elseValue !== undefined ? toOperand(elseValue) : undefined
288
+ });
289
+
290
+ /**
291
+ * Creates an EXISTS expression
292
+ * @param subquery - Subquery to check for existence
293
+ * @returns EXISTS expression node
294
+ */
295
+ export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
296
+ type: 'ExistsExpression',
297
+ operator: 'EXISTS',
298
+ subquery
299
+ });
300
+
301
+ /**
302
+ * Creates a NOT EXISTS expression
303
+ * @param subquery - Subquery to check for non-existence
304
+ * @returns NOT EXISTS expression node
305
+ */
306
+ export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
307
+ type: 'ExistsExpression',
308
+ operator: 'NOT EXISTS',
309
+ subquery
310
+ });
@@ -0,0 +1,211 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import type { SelectQueryNode, OrderByNode } from './query.js';
3
+ import { SqlOperator } from '../sql/sql.js';
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 =
98
+ | ColumnNode
99
+ | LiteralNode
100
+ | FunctionNode
101
+ | JsonPathNode
102
+ | ScalarSubqueryNode
103
+ | CaseExpressionNode
104
+ | WindowFunctionNode;
105
+
106
+ const operandTypes = new Set<OperandNode['type']>([
107
+ 'Column',
108
+ 'Literal',
109
+ 'Function',
110
+ 'JsonPath',
111
+ 'ScalarSubquery',
112
+ 'CaseExpression',
113
+ 'WindowFunction'
114
+ ]);
115
+
116
+ export const isOperandNode = (node: any): node is OperandNode => node && operandTypes.has(node.type);
117
+
118
+ export const isFunctionNode = (node: any): node is FunctionNode => node?.type === 'Function';
119
+ export const isCaseExpressionNode = (node: any): node is CaseExpressionNode => node?.type === 'CaseExpression';
120
+ export const isWindowFunctionNode = (node: any): node is WindowFunctionNode => node?.type === 'WindowFunction';
121
+ export const isExpressionSelectionNode = (
122
+ node: ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode
123
+ ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
124
+ isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
125
+
126
+ /**
127
+ * AST node representing a binary expression (e.g., column = value)
128
+ */
129
+ export interface BinaryExpressionNode {
130
+ type: 'BinaryExpression';
131
+ /** Left operand */
132
+ left: OperandNode;
133
+ /** Comparison operator */
134
+ operator: SqlOperator;
135
+ /** Right operand */
136
+ right: OperandNode;
137
+ /** Optional escape character for LIKE expressions */
138
+ escape?: LiteralNode;
139
+ }
140
+
141
+ /**
142
+ * AST node representing a logical expression (AND/OR)
143
+ */
144
+ export interface LogicalExpressionNode {
145
+ type: 'LogicalExpression';
146
+ /** Logical operator (AND or OR) */
147
+ operator: 'AND' | 'OR';
148
+ /** Operands to combine */
149
+ operands: ExpressionNode[];
150
+ }
151
+
152
+ /**
153
+ * AST node representing a null check expression
154
+ */
155
+ export interface NullExpressionNode {
156
+ type: 'NullExpression';
157
+ /** Operand to check for null */
158
+ left: OperandNode;
159
+ /** Null check operator */
160
+ operator: 'IS NULL' | 'IS NOT NULL';
161
+ }
162
+
163
+ /**
164
+ * AST node representing an IN/NOT IN expression
165
+ */
166
+ export interface InExpressionNode {
167
+ type: 'InExpression';
168
+ /** Left operand to check */
169
+ left: OperandNode;
170
+ /** IN/NOT IN operator */
171
+ operator: 'IN' | 'NOT IN';
172
+ /** Values to check against */
173
+ right: OperandNode[];
174
+ }
175
+
176
+ /**
177
+ * AST node representing an EXISTS/NOT EXISTS expression
178
+ */
179
+ export interface ExistsExpressionNode {
180
+ type: 'ExistsExpression';
181
+ /** EXISTS/NOT EXISTS operator */
182
+ operator: SqlOperator;
183
+ /** Subquery to check */
184
+ subquery: SelectQueryNode;
185
+ }
186
+
187
+ /**
188
+ * AST node representing a BETWEEN/NOT BETWEEN expression
189
+ */
190
+ export interface BetweenExpressionNode {
191
+ type: 'BetweenExpression';
192
+ /** Operand to check */
193
+ left: OperandNode;
194
+ /** BETWEEN/NOT BETWEEN operator */
195
+ operator: 'BETWEEN' | 'NOT BETWEEN';
196
+ /** Lower bound */
197
+ lower: OperandNode;
198
+ /** Upper bound */
199
+ upper: OperandNode;
200
+ }
201
+
202
+ /**
203
+ * Union type representing any supported expression node
204
+ */
205
+ export type ExpressionNode =
206
+ | BinaryExpressionNode
207
+ | LogicalExpressionNode
208
+ | NullExpressionNode
209
+ | InExpressionNode
210
+ | ExistsExpressionNode
211
+ | BetweenExpressionNode;
@@ -0,0 +1,99 @@
1
+ import {
2
+ BinaryExpressionNode,
3
+ LogicalExpressionNode,
4
+ NullExpressionNode,
5
+ InExpressionNode,
6
+ ExistsExpressionNode,
7
+ BetweenExpressionNode,
8
+ ExpressionNode,
9
+ OperandNode,
10
+ ColumnNode,
11
+ LiteralNode,
12
+ FunctionNode,
13
+ JsonPathNode,
14
+ ScalarSubqueryNode,
15
+ CaseExpressionNode,
16
+ WindowFunctionNode
17
+ } from './expression-nodes.js';
18
+
19
+ /**
20
+ * Visitor for expression nodes
21
+ */
22
+ export interface ExpressionVisitor<R> {
23
+ visitBinaryExpression(node: BinaryExpressionNode): R;
24
+ visitLogicalExpression(node: LogicalExpressionNode): R;
25
+ visitNullExpression(node: NullExpressionNode): R;
26
+ visitInExpression(node: InExpressionNode): R;
27
+ visitExistsExpression(node: ExistsExpressionNode): R;
28
+ visitBetweenExpression(node: BetweenExpressionNode): R;
29
+ }
30
+
31
+ /**
32
+ * Visitor for operand nodes
33
+ */
34
+ export interface OperandVisitor<R> {
35
+ visitColumn(node: ColumnNode): R;
36
+ visitLiteral(node: LiteralNode): R;
37
+ visitFunction(node: FunctionNode): R;
38
+ visitJsonPath(node: JsonPathNode): R;
39
+ visitScalarSubquery(node: ScalarSubqueryNode): R;
40
+ visitCaseExpression(node: CaseExpressionNode): R;
41
+ visitWindowFunction(node: WindowFunctionNode): R;
42
+ }
43
+
44
+ const unsupportedExpression = (node: ExpressionNode): never => {
45
+ throw new Error(`Unsupported expression type "${(node as any)?.type ?? 'unknown'}"`);
46
+ };
47
+
48
+ const unsupportedOperand = (node: OperandNode): never => {
49
+ throw new Error(`Unsupported operand type "${(node as any)?.type ?? 'unknown'}"`);
50
+ };
51
+ /**
52
+ * Dispatches an expression node to the visitor
53
+ * @param node - Expression node to visit
54
+ * @param visitor - Visitor implementation
55
+ */
56
+ export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>): R => {
57
+ switch (node.type) {
58
+ case 'BinaryExpression':
59
+ return visitor.visitBinaryExpression(node);
60
+ case 'LogicalExpression':
61
+ return visitor.visitLogicalExpression(node);
62
+ case 'NullExpression':
63
+ return visitor.visitNullExpression(node);
64
+ case 'InExpression':
65
+ return visitor.visitInExpression(node);
66
+ case 'ExistsExpression':
67
+ return visitor.visitExistsExpression(node);
68
+ case 'BetweenExpression':
69
+ return visitor.visitBetweenExpression(node);
70
+ default:
71
+ return unsupportedExpression(node);
72
+ }
73
+ };
74
+
75
+ /**
76
+ * Dispatches an operand node to the visitor
77
+ * @param node - Operand node to visit
78
+ * @param visitor - Visitor implementation
79
+ */
80
+ export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>): R => {
81
+ switch (node.type) {
82
+ case 'Column':
83
+ return visitor.visitColumn(node);
84
+ case 'Literal':
85
+ return visitor.visitLiteral(node);
86
+ case 'Function':
87
+ return visitor.visitFunction(node);
88
+ case 'JsonPath':
89
+ return visitor.visitJsonPath(node);
90
+ case 'ScalarSubquery':
91
+ return visitor.visitScalarSubquery(node);
92
+ case 'CaseExpression':
93
+ return visitor.visitCaseExpression(node);
94
+ case 'WindowFunction':
95
+ return visitor.visitWindowFunction(node);
96
+ default:
97
+ return unsupportedOperand(node);
98
+ }
99
+ };
@@ -0,0 +1,5 @@
1
+ export * from './expression-nodes.js';
2
+ export * from './expression-builders.js';
3
+ export * from './window-functions.js';
4
+ export * from './aggregate-functions.js';
5
+ export * from './expression-visitor.js';