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
@@ -1,59 +1,48 @@
1
- import { TableDef } from '../schema/table';
2
- import { ColumnDef } from '../schema/column';
3
- import { ColumnNode } from '../ast/expression';
4
- import { CompiledQuery, InsertCompiler } from '../dialect/abstract';
5
- import { InsertQueryNode } from '../ast/query';
6
- import { InsertQueryState } from './insert-query-state';
7
-
8
- const buildColumnNode = (table: TableDef, column: ColumnDef | ColumnNode): ColumnNode => {
9
- if ((column as ColumnNode).type === 'Column') {
10
- return column as ColumnNode;
11
- }
12
- const def = column as ColumnDef;
13
- return {
14
- type: 'Column',
15
- table: def.table || table.name,
16
- name: def.name
17
- };
18
- };
19
-
20
- /**
21
- * Builder for INSERT queries
22
- */
23
- export class InsertQueryBuilder<T> {
24
- private readonly table: TableDef;
25
- private readonly state: InsertQueryState;
26
-
27
- constructor(table: TableDef, state?: InsertQueryState) {
28
- this.table = table;
29
- this.state = state ?? new InsertQueryState(table);
30
- }
31
-
32
- private clone(state: InsertQueryState): InsertQueryBuilder<T> {
33
- return new InsertQueryBuilder(this.table, state);
34
- }
35
-
36
- values(rowOrRows: Record<string, unknown> | Record<string, unknown>[]): InsertQueryBuilder<T> {
37
- const rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
38
- if (!rows.length) return this;
39
- return this.clone(this.state.withValues(rows));
40
- }
41
-
42
- returning(...columns: (ColumnDef | ColumnNode)[]): InsertQueryBuilder<T> {
43
- if (!columns.length) return this;
44
- const nodes = columns.map(column => buildColumnNode(this.table, column));
45
- return this.clone(this.state.withReturning(nodes));
46
- }
47
-
48
- compile(compiler: InsertCompiler): CompiledQuery {
49
- return compiler.compileInsert(this.state.ast);
50
- }
51
-
52
- toSql(compiler: InsertCompiler): string {
53
- return this.compile(compiler).sql;
54
- }
55
-
56
- getAST(): InsertQueryNode {
57
- return this.state.ast;
58
- }
59
- }
1
+ import { TableDef } from '../schema/table.js';
2
+ import { ColumnDef } from '../schema/column.js';
3
+ import { ColumnNode } from '../core/ast/expression.js';
4
+ import { CompiledQuery, InsertCompiler } from '../core/dialect/abstract.js';
5
+ import { InsertQueryNode } from '../core/ast/query.js';
6
+ import { InsertQueryState } from './insert-query-state.js';
7
+ import { buildColumnNode } from '../core/ast/builders.js';
8
+
9
+ /**
10
+ * Builder for INSERT queries
11
+ */
12
+ export class InsertQueryBuilder<T> {
13
+ private readonly table: TableDef;
14
+ private readonly state: InsertQueryState;
15
+
16
+ constructor(table: TableDef, state?: InsertQueryState) {
17
+ this.table = table;
18
+ this.state = state ?? new InsertQueryState(table);
19
+ }
20
+
21
+ private clone(state: InsertQueryState): InsertQueryBuilder<T> {
22
+ return new InsertQueryBuilder(this.table, state);
23
+ }
24
+
25
+ values(rowOrRows: Record<string, unknown> | Record<string, unknown>[]): InsertQueryBuilder<T> {
26
+ const rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
27
+ if (!rows.length) return this;
28
+ return this.clone(this.state.withValues(rows));
29
+ }
30
+
31
+ returning(...columns: (ColumnDef | ColumnNode)[]): InsertQueryBuilder<T> {
32
+ if (!columns.length) return this;
33
+ const nodes = columns.map(column => buildColumnNode(this.table, column));
34
+ return this.clone(this.state.withReturning(nodes));
35
+ }
36
+
37
+ compile(compiler: InsertCompiler): CompiledQuery {
38
+ return compiler.compileInsert(this.state.ast);
39
+ }
40
+
41
+ toSql(compiler: InsertCompiler): string {
42
+ return this.compile(compiler).sql;
43
+ }
44
+
45
+ getAST(): InsertQueryNode {
46
+ return this.state.ast;
47
+ }
48
+ }
@@ -1,226 +1,208 @@
1
- import { TableDef } from '../schema/table';
2
- import { ColumnDef } from '../schema/column';
3
- import { SelectQueryNode, CommonTableExpressionNode } from '../ast/query';
4
- import {
5
- ColumnNode,
6
- ExpressionNode,
7
- FunctionNode,
8
- CaseExpressionNode,
9
- WindowFunctionNode,
10
- ScalarSubqueryNode,
11
- and,
12
- isExpressionSelectionNode
13
- } from '../ast/expression';
14
- import { JoinNode } from '../ast/join';
15
- import { SelectQueryState, ProjectionNode } from './select-query-state';
16
- import { OrderDirection } from '../constants/sql';
17
- import { parseRawColumn } from '../utils/raw-column-parser';
18
-
19
- /**
20
- * Builds a column node from a column definition or existing column node
21
- * @param table - Table definition
22
- * @param col - Column definition or column node
23
- * @returns Column node
24
- */
25
- export const buildColumnNode = (table: TableDef, col: ColumnDef | ColumnNode): ColumnNode => {
26
- if ((col as ColumnNode).type === 'Column') {
27
- return col as ColumnNode;
28
- }
29
-
30
- const def = col as ColumnDef;
31
- return {
32
- type: 'Column',
33
- table: def.table || table.name,
34
- name: def.name
35
- };
36
- };
37
-
38
- /**
39
- * Result of column selection operation
40
- */
41
- export interface ColumnSelectionResult {
42
- /**
43
- * Updated query state
44
- */
45
- state: SelectQueryState;
46
- /**
47
- * Columns that were added
48
- */
49
- addedColumns: ProjectionNode[];
50
- }
51
-
52
- /**
53
- * Service for manipulating query AST (Abstract Syntax Tree)
54
- */
55
- export class QueryAstService {
56
- /**
57
- * Creates a new QueryAstService instance
58
- * @param table - Table definition
59
- * @param state - Current query state
60
- */
61
- constructor(private readonly table: TableDef, private readonly state: SelectQueryState) {}
62
-
63
- /**
64
- * Selects columns for the query
65
- * @param columns - Columns to select (key: alias, value: column definition or expression)
66
- * @returns Column selection result with updated state and added columns
67
- */
68
- select(
69
- columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>
70
- ): ColumnSelectionResult {
71
- const existingAliases = new Set(
72
- this.state.ast.columns.map(c => (c as ColumnNode).alias || (c as ColumnNode).name)
73
- );
74
-
75
- const newCols = Object.entries(columns).reduce<ProjectionNode[]>((acc, [alias, val]) => {
76
- if (existingAliases.has(alias)) return acc;
77
-
78
- if (isExpressionSelectionNode(val)) {
79
- acc.push({ ...(val as FunctionNode | CaseExpressionNode | WindowFunctionNode), alias } as ProjectionNode);
80
- return acc;
81
- }
82
-
83
- const colDef = val as ColumnDef;
84
- acc.push({
85
- type: 'Column',
86
- table: colDef.table || this.table.name,
87
- name: colDef.name,
88
- alias
89
- } as ColumnNode);
90
- return acc;
91
- }, []);
92
-
93
- const nextState = this.state.withColumns(newCols);
94
- return { state: nextState, addedColumns: newCols };
95
- }
96
-
97
- /**
98
- * Selects raw column expressions (best-effort parser for simple references/functions)
99
- * @param cols - Raw column expressions
100
- * @returns Column selection result with updated state and added columns
101
- */
102
- selectRaw(cols: string[]): ColumnSelectionResult {
103
- const newCols = cols.map(col => parseRawColumn(col, this.table.name, this.state.ast.ctes));
104
- const nextState = this.state.withColumns(newCols);
105
- return { state: nextState, addedColumns: newCols };
106
- }
107
-
108
- /**
109
- * Adds a Common Table Expression (CTE) to the query
110
- * @param name - Name of the CTE
111
- * @param query - Query for the CTE
112
- * @param columns - Optional column names for the CTE
113
- * @param recursive - Whether the CTE is recursive
114
- * @returns Updated query state with CTE
115
- */
116
- withCte(name: string, query: SelectQueryNode, columns?: string[], recursive = false): SelectQueryState {
117
- const cte: CommonTableExpressionNode = {
118
- type: 'CommonTableExpression',
119
- name,
120
- query,
121
- columns,
122
- recursive
123
- };
124
-
125
- return this.state.withCte(cte);
126
- }
127
-
128
- /**
129
- * Selects a subquery as a column
130
- * @param alias - Alias for the subquery
131
- * @param query - Subquery to select
132
- * @returns Updated query state with subquery selection
133
- */
134
- selectSubquery(alias: string, query: SelectQueryNode): SelectQueryState {
135
- const node: ScalarSubqueryNode = { type: 'ScalarSubquery', query, alias };
136
- return this.state.withColumns([node]);
137
- }
138
-
139
- /**
140
- * Adds a JOIN clause to the query
141
- * @param join - Join node to add
142
- * @returns Updated query state with JOIN
143
- */
144
- withJoin(join: JoinNode): SelectQueryState {
145
- return this.state.withJoin(join);
146
- }
147
-
148
- /**
149
- * Adds a WHERE clause to the query
150
- * @param expr - Expression for the WHERE clause
151
- * @returns Updated query state with WHERE clause
152
- */
153
- withWhere(expr: ExpressionNode): SelectQueryState {
154
- const combined = this.combineExpressions(this.state.ast.where, expr);
155
- return this.state.withWhere(combined);
156
- }
157
-
158
- /**
159
- * Adds a GROUP BY clause to the query
160
- * @param col - Column to group by
161
- * @returns Updated query state with GROUP BY clause
162
- */
163
- withGroupBy(col: ColumnDef | ColumnNode): SelectQueryState {
164
- const node = buildColumnNode(this.table, col);
165
- return this.state.withGroupBy([node]);
166
- }
167
-
168
- /**
169
- * Adds a HAVING clause to the query
170
- * @param expr - Expression for the HAVING clause
171
- * @returns Updated query state with HAVING clause
172
- */
173
- withHaving(expr: ExpressionNode): SelectQueryState {
174
- const combined = this.combineExpressions(this.state.ast.having, expr);
175
- return this.state.withHaving(combined);
176
- }
177
-
178
- /**
179
- * Adds an ORDER BY clause to the query
180
- * @param col - Column to order by
181
- * @param direction - Order direction (ASC/DESC)
182
- * @returns Updated query state with ORDER BY clause
183
- */
184
- withOrderBy(col: ColumnDef | ColumnNode, direction: OrderDirection): SelectQueryState {
185
- const node = buildColumnNode(this.table, col);
186
- return this.state.withOrderBy([{ type: 'OrderBy', column: node, direction }]);
187
- }
188
-
189
- /**
190
- * Adds a DISTINCT clause to the query
191
- * @param cols - Columns to make distinct
192
- * @returns Updated query state with DISTINCT clause
193
- */
194
- withDistinct(cols: ColumnNode[]): SelectQueryState {
195
- return this.state.withDistinct(cols);
196
- }
197
-
198
- /**
199
- * Adds a LIMIT clause to the query
200
- * @param limit - Maximum number of rows to return
201
- * @returns Updated query state with LIMIT clause
202
- */
203
- withLimit(limit: number): SelectQueryState {
204
- return this.state.withLimit(limit);
205
- }
206
-
207
- /**
208
- * Adds an OFFSET clause to the query
209
- * @param offset - Number of rows to skip
210
- * @returns Updated query state with OFFSET clause
211
- */
212
- withOffset(offset: number): SelectQueryState {
213
- return this.state.withOffset(offset);
214
- }
215
-
216
- /**
217
- * Combines expressions with AND operator
218
- * @param existing - Existing expression
219
- * @param next - New expression to combine
220
- * @returns Combined expression
221
- */
222
- private combineExpressions(existing: ExpressionNode | undefined, next: ExpressionNode): ExpressionNode {
223
- return existing ? and(existing, next) : next;
224
- }
225
-
226
- }
1
+ import { TableDef } from '../schema/table.js';
2
+ import { ColumnDef } from '../schema/column.js';
3
+ import { SelectQueryNode, CommonTableExpressionNode } from '../core/ast/query.js';
4
+ import { buildColumnNode } from '../core/ast/builders.js';
5
+ import {
6
+ ColumnNode,
7
+ ExpressionNode,
8
+ FunctionNode,
9
+ CaseExpressionNode,
10
+ WindowFunctionNode,
11
+ ScalarSubqueryNode,
12
+ and,
13
+ isExpressionSelectionNode
14
+ } from '../core/ast/expression.js';
15
+ import { JoinNode } from '../core/ast/join.js';
16
+ import { SelectQueryState, ProjectionNode } from './select-query-state.js';
17
+ import { OrderDirection } from '../core/sql/sql.js';
18
+ import { parseRawColumn } from './raw-column-parser.js';
19
+
20
+ /**
21
+ * Result of column selection operation
22
+ */
23
+ export interface ColumnSelectionResult {
24
+ /**
25
+ * Updated query state
26
+ */
27
+ state: SelectQueryState;
28
+ /**
29
+ * Columns that were added
30
+ */
31
+ addedColumns: ProjectionNode[];
32
+ }
33
+
34
+ /**
35
+ * Service for manipulating query AST (Abstract Syntax Tree)
36
+ */
37
+ export class QueryAstService {
38
+ /**
39
+ * Creates a new QueryAstService instance
40
+ * @param table - Table definition
41
+ * @param state - Current query state
42
+ */
43
+ constructor(private readonly table: TableDef, private readonly state: SelectQueryState) {}
44
+
45
+ /**
46
+ * Selects columns for the query
47
+ * @param columns - Columns to select (key: alias, value: column definition or expression)
48
+ * @returns Column selection result with updated state and added columns
49
+ */
50
+ select(
51
+ columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>
52
+ ): ColumnSelectionResult {
53
+ const existingAliases = new Set(
54
+ this.state.ast.columns.map(c => (c as ColumnNode).alias || (c as ColumnNode).name)
55
+ );
56
+
57
+ const newCols = Object.entries(columns).reduce<ProjectionNode[]>((acc, [alias, val]) => {
58
+ if (existingAliases.has(alias)) return acc;
59
+
60
+ if (isExpressionSelectionNode(val)) {
61
+ acc.push({ ...(val as FunctionNode | CaseExpressionNode | WindowFunctionNode), alias } as ProjectionNode);
62
+ return acc;
63
+ }
64
+
65
+ const colDef = val as ColumnDef;
66
+ acc.push({
67
+ type: 'Column',
68
+ table: colDef.table || this.table.name,
69
+ name: colDef.name,
70
+ alias
71
+ } as ColumnNode);
72
+ return acc;
73
+ }, []);
74
+
75
+ const nextState = this.state.withColumns(newCols);
76
+ return { state: nextState, addedColumns: newCols };
77
+ }
78
+
79
+ /**
80
+ * Selects raw column expressions (best-effort parser for simple references/functions)
81
+ * @param cols - Raw column expressions
82
+ * @returns Column selection result with updated state and added columns
83
+ */
84
+ selectRaw(cols: string[]): ColumnSelectionResult {
85
+ const newCols = cols.map(col => parseRawColumn(col, this.table.name, this.state.ast.ctes));
86
+ const nextState = this.state.withColumns(newCols);
87
+ return { state: nextState, addedColumns: newCols };
88
+ }
89
+
90
+ /**
91
+ * Adds a Common Table Expression (CTE) to the query
92
+ * @param name - Name of the CTE
93
+ * @param query - Query for the CTE
94
+ * @param columns - Optional column names for the CTE
95
+ * @param recursive - Whether the CTE is recursive
96
+ * @returns Updated query state with CTE
97
+ */
98
+ withCte(name: string, query: SelectQueryNode, columns?: string[], recursive = false): SelectQueryState {
99
+ const cte: CommonTableExpressionNode = {
100
+ type: 'CommonTableExpression',
101
+ name,
102
+ query,
103
+ columns,
104
+ recursive
105
+ };
106
+
107
+ return this.state.withCte(cte);
108
+ }
109
+
110
+ /**
111
+ * Selects a subquery as a column
112
+ * @param alias - Alias for the subquery
113
+ * @param query - Subquery to select
114
+ * @returns Updated query state with subquery selection
115
+ */
116
+ selectSubquery(alias: string, query: SelectQueryNode): SelectQueryState {
117
+ const node: ScalarSubqueryNode = { type: 'ScalarSubquery', query, alias };
118
+ return this.state.withColumns([node]);
119
+ }
120
+
121
+ /**
122
+ * Adds a JOIN clause to the query
123
+ * @param join - Join node to add
124
+ * @returns Updated query state with JOIN
125
+ */
126
+ withJoin(join: JoinNode): SelectQueryState {
127
+ return this.state.withJoin(join);
128
+ }
129
+
130
+ /**
131
+ * Adds a WHERE clause to the query
132
+ * @param expr - Expression for the WHERE clause
133
+ * @returns Updated query state with WHERE clause
134
+ */
135
+ withWhere(expr: ExpressionNode): SelectQueryState {
136
+ const combined = this.combineExpressions(this.state.ast.where, expr);
137
+ return this.state.withWhere(combined);
138
+ }
139
+
140
+ /**
141
+ * Adds a GROUP BY clause to the query
142
+ * @param col - Column to group by
143
+ * @returns Updated query state with GROUP BY clause
144
+ */
145
+ withGroupBy(col: ColumnDef | ColumnNode): SelectQueryState {
146
+ const node = buildColumnNode(this.table, col);
147
+ return this.state.withGroupBy([node]);
148
+ }
149
+
150
+ /**
151
+ * Adds a HAVING clause to the query
152
+ * @param expr - Expression for the HAVING clause
153
+ * @returns Updated query state with HAVING clause
154
+ */
155
+ withHaving(expr: ExpressionNode): SelectQueryState {
156
+ const combined = this.combineExpressions(this.state.ast.having, expr);
157
+ return this.state.withHaving(combined);
158
+ }
159
+
160
+ /**
161
+ * Adds an ORDER BY clause to the query
162
+ * @param col - Column to order by
163
+ * @param direction - Order direction (ASC/DESC)
164
+ * @returns Updated query state with ORDER BY clause
165
+ */
166
+ withOrderBy(col: ColumnDef | ColumnNode, direction: OrderDirection): SelectQueryState {
167
+ const node = buildColumnNode(this.table, col);
168
+ return this.state.withOrderBy([{ type: 'OrderBy', column: node, direction }]);
169
+ }
170
+
171
+ /**
172
+ * Adds a DISTINCT clause to the query
173
+ * @param cols - Columns to make distinct
174
+ * @returns Updated query state with DISTINCT clause
175
+ */
176
+ withDistinct(cols: ColumnNode[]): SelectQueryState {
177
+ return this.state.withDistinct(cols);
178
+ }
179
+
180
+ /**
181
+ * Adds a LIMIT clause to the query
182
+ * @param limit - Maximum number of rows to return
183
+ * @returns Updated query state with LIMIT clause
184
+ */
185
+ withLimit(limit: number): SelectQueryState {
186
+ return this.state.withLimit(limit);
187
+ }
188
+
189
+ /**
190
+ * Adds an OFFSET clause to the query
191
+ * @param offset - Number of rows to skip
192
+ * @returns Updated query state with OFFSET clause
193
+ */
194
+ withOffset(offset: number): SelectQueryState {
195
+ return this.state.withOffset(offset);
196
+ }
197
+
198
+ /**
199
+ * Combines expressions with AND operator
200
+ * @param existing - Existing expression
201
+ * @param next - New expression to combine
202
+ * @returns Combined expression
203
+ */
204
+ private combineExpressions(existing: ExpressionNode | undefined, next: ExpressionNode): ExpressionNode {
205
+ return existing ? and(existing, next) : next;
206
+ }
207
+
208
+ }
@@ -1,32 +1,32 @@
1
- import { ColumnNode } from '../ast/expression';
2
- import { CommonTableExpressionNode } from '../ast/query';
3
-
4
- /**
5
- * Best-effort helper that tries to convert a raw column expression into a `ColumnNode`.
6
- * This parser is intentionally limited; use it only for simple references or function calls.
7
- */
8
- export const parseRawColumn = (
9
- col: string,
10
- tableName: string,
11
- ctes?: CommonTableExpressionNode[]
12
- ): ColumnNode => {
13
- if (col.includes('(')) {
14
- const [fn, rest] = col.split('(');
15
- const colName = rest.replace(')', '');
16
- const [table, name] = colName.includes('.') ? colName.split('.') : [tableName, colName];
17
- return { type: 'Column', table, name, alias: col };
18
- }
19
-
20
- if (col.includes('.')) {
21
- const [potentialCteName, columnName] = col.split('.');
22
- const hasCte = ctes?.some(cte => cte.name === potentialCteName);
23
-
24
- if (hasCte) {
25
- return { type: 'Column', table: tableName, name: col };
26
- }
27
-
28
- return { type: 'Column', table: potentialCteName, name: columnName };
29
- }
30
-
31
- return { type: 'Column', table: tableName, name: col };
32
- };
1
+ import { ColumnNode } from '../core/ast/expression.js';
2
+ import { CommonTableExpressionNode } from '../core/ast/query.js';
3
+
4
+ /**
5
+ * Best-effort helper that tries to convert a raw column expression into a `ColumnNode`.
6
+ * This parser is intentionally limited; use it only for simple references or function calls.
7
+ */
8
+ export const parseRawColumn = (
9
+ col: string,
10
+ tableName: string,
11
+ ctes?: CommonTableExpressionNode[]
12
+ ): ColumnNode => {
13
+ if (col.includes('(')) {
14
+ const [fn, rest] = col.split('(');
15
+ const colName = rest.replace(')', '');
16
+ const [table, name] = colName.includes('.') ? colName.split('.') : [tableName, colName];
17
+ return { type: 'Column', table, name, alias: col };
18
+ }
19
+
20
+ if (col.includes('.')) {
21
+ const [potentialCteName, columnName] = col.split('.');
22
+ const hasCte = ctes?.some(cte => cte.name === potentialCteName);
23
+
24
+ if (hasCte) {
25
+ return { type: 'Column', table: tableName, name: col };
26
+ }
27
+
28
+ return { type: 'Column', table: potentialCteName, name: columnName };
29
+ }
30
+
31
+ return { type: 'Column', table: tableName, name: col };
32
+ };