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.
- package/README.md +133 -121
- package/dist/decorators/index.cjs +2564 -0
- package/dist/decorators/index.cjs.map +1 -0
- package/dist/decorators/index.d.cts +53 -0
- package/dist/decorators/index.d.ts +53 -0
- package/dist/decorators/index.js +2530 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/index.cjs +4227 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +701 -0
- package/dist/index.d.ts +701 -0
- package/dist/index.js +4131 -0
- package/dist/index.js.map +1 -0
- package/dist/select-654m4qy8.d.cts +1522 -0
- package/dist/select-654m4qy8.d.ts +1522 -0
- package/package.json +27 -20
- package/src/codegen/typescript.ts +405 -393
- package/src/core/ast/aggregate-functions.ts +30 -0
- package/src/core/ast/builders.ts +43 -0
- package/src/core/ast/expression-builders.ts +310 -0
- package/src/core/ast/expression-nodes.ts +211 -0
- package/src/core/ast/expression-visitor.ts +99 -0
- package/src/core/ast/expression.ts +5 -0
- package/src/{utils → core/ast}/join-node.ts +20 -20
- package/src/{ast → core/ast}/join.ts +18 -18
- package/src/{ast → core/ast}/query.ts +113 -113
- package/src/core/ast/window-functions.ts +140 -0
- package/src/{dialect → core/dialect}/abstract.ts +94 -94
- package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
- package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
- package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
- package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
- package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
- package/src/decorators/bootstrap.ts +126 -0
- package/src/decorators/column.ts +78 -0
- package/src/decorators/entity.ts +36 -0
- package/src/decorators/index.ts +4 -0
- package/src/decorators/relations.ts +107 -0
- package/src/global.d.ts +1 -0
- package/src/index.ts +22 -22
- package/src/orm/db-executor.ts +11 -0
- package/src/orm/domain-event-bus.ts +52 -0
- package/src/{runtime → orm}/entity-meta.ts +52 -52
- package/src/orm/entity-metadata.ts +140 -0
- package/src/{runtime → orm}/entity.ts +252 -252
- package/src/{runtime → orm}/execute.ts +36 -36
- package/src/{runtime → orm}/hydration.ts +103 -103
- package/src/orm/identity-map.ts +37 -0
- package/src/{runtime → orm}/lazy-batch.ts +205 -205
- package/src/orm/orm-context.ts +154 -0
- package/src/orm/relation-change-processor.ts +140 -0
- package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
- package/src/{runtime → orm}/relations/has-many.ts +111 -111
- package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
- package/src/orm/runtime-types.ts +39 -0
- package/src/orm/transaction-runner.ts +17 -0
- package/src/orm/unit-of-work.ts +232 -0
- package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
- package/src/{builder → query-builder}/delete-query-state.ts +38 -42
- package/src/{builder → query-builder}/delete.ts +46 -57
- package/src/{builder → query-builder}/hydration-manager.ts +87 -87
- package/src/{builder → query-builder}/hydration-planner.ts +182 -182
- package/src/{builder → query-builder}/insert-query-state.ts +51 -62
- package/src/{builder → query-builder}/insert.ts +48 -59
- package/src/{builder → query-builder}/query-ast-service.ts +208 -226
- package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
- package/src/{builder → query-builder}/relation-conditions.ts +112 -112
- package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
- package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
- package/src/{builder → query-builder}/relation-service.ts +284 -284
- package/src/{builder → query-builder}/relation-types.ts +21 -21
- package/src/{builder → query-builder}/relation-utils.ts +12 -12
- package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
- package/src/{builder → query-builder}/select-query-state.ts +179 -179
- package/src/{builder → query-builder}/select.ts +78 -69
- package/src/{builder → query-builder}/update-query-state.ts +55 -59
- package/src/{builder → query-builder}/update.ts +50 -61
- package/src/schema/column.ts +25 -25
- package/src/schema/relation.ts +116 -116
- package/src/schema/table.ts +34 -34
- package/src/schema/types.ts +76 -76
- package/.github/workflows/publish-metal-orm.yml +0 -38
- package/ROADMAP.md +0 -125
- package/docs/CHANGES.md +0 -104
- package/docs/advanced-features.md +0 -176
- package/docs/api-reference.md +0 -31
- package/docs/dml-operations.md +0 -156
- package/docs/getting-started.md +0 -171
- package/docs/hydration.md +0 -115
- package/docs/index.md +0 -36
- package/docs/multi-dialect-support.md +0 -59
- package/docs/query-builder.md +0 -135
- package/docs/runtime.md +0 -105
- package/docs/schema-definition.md +0 -112
- package/metadata.json +0 -5
- package/playground/api/playground-api.ts +0 -94
- package/playground/index.html +0 -15
- package/playground/src/App.css +0 -1
- package/playground/src/App.tsx +0 -114
- package/playground/src/components/CodeDisplay.tsx +0 -43
- package/playground/src/components/QueryExecutor.tsx +0 -189
- package/playground/src/components/ResultsTable.tsx +0 -67
- package/playground/src/components/ResultsTabs.tsx +0 -105
- package/playground/src/components/ScenarioList.tsx +0 -56
- package/playground/src/components/logo.svg +0 -45
- package/playground/src/data/scenarios.ts +0 -2
- package/playground/src/main.tsx +0 -9
- package/playground/src/services/PlaygroundApiService.ts +0 -60
- package/postcss.config.cjs +0 -5
- package/sql_sql-ansi-cheatsheet-2025.md +0 -264
- package/src/ast/expression.ts +0 -658
- package/src/builder/operations/cte-manager.ts +0 -34
- package/src/builder/operations/filter-manager.ts +0 -68
- package/src/builder/operations/join-manager.ts +0 -36
- package/src/builder/operations/pagination-manager.ts +0 -36
- package/src/playground/features/playground/api/types.ts +0 -16
- package/src/playground/features/playground/clients/MockClient.ts +0 -17
- package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
- package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
- package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
- package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
- package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
- package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
- package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
- package/src/playground/features/playground/data/scenarios/index.ts +0 -29
- package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
- package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
- package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
- package/src/playground/features/playground/data/scenarios/types.ts +0 -70
- package/src/playground/features/playground/data/schema.ts +0 -91
- package/src/playground/features/playground/data/seed.ts +0 -104
- package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
- package/src/runtime/orm-context.ts +0 -539
- package/tests/belongs-to-many.test.ts +0 -57
- package/tests/between.test.ts +0 -43
- package/tests/case-expression.test.ts +0 -58
- package/tests/complex-exists.test.ts +0 -230
- package/tests/cte.test.ts +0 -118
- package/tests/dml.test.ts +0 -206
- package/tests/exists.test.ts +0 -127
- package/tests/like.test.ts +0 -33
- package/tests/orm-runtime.test.ts +0 -254
- package/tests/postgres.test.ts +0 -30
- package/tests/right-join.test.ts +0 -89
- package/tests/subquery-having.test.ts +0 -193
- package/tests/window-function.test.ts +0 -151
- package/tsconfig.json +0 -30
- package/tsup.config.ts +0 -10
- package/vite.config.ts +0 -22
- package/vitest.config.ts +0 -14
- /package/src/{constants → core/sql}/sql.ts +0 -0
- /package/src/{runtime → orm}/als.ts +0 -0
- /package/src/{utils → query-builder}/relation-alias.ts +0 -0
|
@@ -1,393 +1,405 @@
|
|
|
1
|
-
import { SelectQueryNode } from '../ast/query';
|
|
2
|
-
import {
|
|
3
|
-
ExpressionNode,
|
|
4
|
-
OperandNode,
|
|
5
|
-
BinaryExpressionNode,
|
|
6
|
-
LogicalExpressionNode,
|
|
7
|
-
InExpressionNode,
|
|
8
|
-
NullExpressionNode,
|
|
9
|
-
JsonPathNode,
|
|
10
|
-
ExistsExpressionNode,
|
|
11
|
-
BetweenExpressionNode,
|
|
12
|
-
ScalarSubqueryNode,
|
|
13
|
-
CaseExpressionNode,
|
|
14
|
-
WindowFunctionNode,
|
|
15
|
-
ColumnNode,
|
|
16
|
-
LiteralNode,
|
|
17
|
-
FunctionNode
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return `${
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Prints
|
|
243
|
-
* @param
|
|
244
|
-
* @returns TypeScript code representation
|
|
245
|
-
*/
|
|
246
|
-
private
|
|
247
|
-
const left = this.printOperand(
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
*
|
|
255
|
-
* @
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
return `${
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Prints
|
|
266
|
-
* @param
|
|
267
|
-
* @returns TypeScript code representation
|
|
268
|
-
*/
|
|
269
|
-
private
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
*
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
*
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
*
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
*
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
*
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
*
|
|
383
|
-
* @param
|
|
384
|
-
* @returns
|
|
385
|
-
*/
|
|
386
|
-
private
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
1
|
+
import { SelectQueryNode } from '../core/ast/query.js';
|
|
2
|
+
import {
|
|
3
|
+
ExpressionNode,
|
|
4
|
+
OperandNode,
|
|
5
|
+
BinaryExpressionNode,
|
|
6
|
+
LogicalExpressionNode,
|
|
7
|
+
InExpressionNode,
|
|
8
|
+
NullExpressionNode,
|
|
9
|
+
JsonPathNode,
|
|
10
|
+
ExistsExpressionNode,
|
|
11
|
+
BetweenExpressionNode,
|
|
12
|
+
ScalarSubqueryNode,
|
|
13
|
+
CaseExpressionNode,
|
|
14
|
+
WindowFunctionNode,
|
|
15
|
+
ColumnNode,
|
|
16
|
+
LiteralNode,
|
|
17
|
+
FunctionNode,
|
|
18
|
+
ExpressionVisitor,
|
|
19
|
+
OperandVisitor,
|
|
20
|
+
visitExpression,
|
|
21
|
+
visitOperand
|
|
22
|
+
} from '../core/ast/expression.js';
|
|
23
|
+
import { SQL_OPERATOR_REGISTRY } from '../core/sql/sql-operator-config.js';
|
|
24
|
+
import { SqlOperator } from '../core/sql/sql.js';
|
|
25
|
+
import { isRelationAlias } from '../query-builder/relation-alias.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Capitalizes the first letter of a string
|
|
29
|
+
* @param s - String to capitalize
|
|
30
|
+
* @returns Capitalized string
|
|
31
|
+
*/
|
|
32
|
+
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
33
|
+
|
|
34
|
+
const assertNever = (value: never): never => {
|
|
35
|
+
throw new Error(`Unhandled SQL operator: ${value}`);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generates TypeScript code from query AST nodes
|
|
40
|
+
*/
|
|
41
|
+
export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVisitor<string> {
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generates TypeScript code from a query AST
|
|
45
|
+
* @param ast - Query AST to generate code from
|
|
46
|
+
* @returns Generated TypeScript code
|
|
47
|
+
*/
|
|
48
|
+
generate(ast: SelectQueryNode): string {
|
|
49
|
+
const chainLines = this.buildSelectLines(ast);
|
|
50
|
+
const lines = chainLines.map((line, index) => (index === 0 ? `const query = ${line}` : line));
|
|
51
|
+
lines.push(';', '', 'await query.execute();');
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Builds TypeScript method chain lines from query AST
|
|
57
|
+
* @param ast - Query AST
|
|
58
|
+
* @returns Array of TypeScript method chain lines
|
|
59
|
+
*/
|
|
60
|
+
private buildSelectLines(ast: SelectQueryNode): string[] {
|
|
61
|
+
const lines: string[] = [];
|
|
62
|
+
const hydration = ast.meta?.hydration;
|
|
63
|
+
const hydratedRelations = new Set(hydration?.relations?.map(r => r.name) ?? []);
|
|
64
|
+
|
|
65
|
+
const selections = ast.columns
|
|
66
|
+
.filter(col => !(hydration && isRelationAlias((col as any).alias)))
|
|
67
|
+
.map(col => {
|
|
68
|
+
const key = (col as any).alias || (col as any).name;
|
|
69
|
+
const operand = col as OperandNode;
|
|
70
|
+
return `${key}: ${this.printOperand(operand)}`;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
lines.push(`db.select({`);
|
|
74
|
+
selections.forEach((sel, index) => {
|
|
75
|
+
lines.push(` ${sel}${index < selections.length - 1 ? ',' : ''}`);
|
|
76
|
+
});
|
|
77
|
+
lines.push(`})`);
|
|
78
|
+
lines.push(`.from(${capitalize(ast.from.name)})`);
|
|
79
|
+
|
|
80
|
+
if (ast.distinct && ast.distinct.length) {
|
|
81
|
+
const cols = ast.distinct.map(c => `${capitalize(c.table)}.${c.name}`).join(', ');
|
|
82
|
+
lines.push(`.distinct(${cols})`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
ast.joins.forEach(join => {
|
|
86
|
+
if (join.relationName && hydratedRelations.has(join.relationName)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (join.relationName) {
|
|
91
|
+
if (join.kind === 'INNER') {
|
|
92
|
+
lines.push(`.joinRelation('${join.relationName}')`);
|
|
93
|
+
} else {
|
|
94
|
+
lines.push(`.joinRelation('${join.relationName}', '${join.kind}')`);
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
const table = capitalize(join.table.name);
|
|
98
|
+
const cond = this.printExpression(join.condition);
|
|
99
|
+
let method = 'innerJoin';
|
|
100
|
+
if (join.kind === 'LEFT') method = 'leftJoin';
|
|
101
|
+
if (join.kind === 'RIGHT') method = 'rightJoin';
|
|
102
|
+
lines.push(`.${method}(${table}, ${cond})`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (hydration?.relations?.length) {
|
|
107
|
+
hydration.relations.forEach(rel => {
|
|
108
|
+
const options: string[] = [];
|
|
109
|
+
if (rel.columns.length) options.push(`columns: [${rel.columns.map(c => `'${c}'`).join(', ')}]`);
|
|
110
|
+
if (rel.aliasPrefix !== rel.name) options.push(`aliasPrefix: '${rel.aliasPrefix}'`);
|
|
111
|
+
const opts = options.length ? `, { ${options.join(', ')} }` : '';
|
|
112
|
+
lines.push(`.include('${rel.name}'${opts})`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (ast.where) {
|
|
117
|
+
lines.push(`.where(${this.printExpression(ast.where)})`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (ast.groupBy && ast.groupBy.length) {
|
|
121
|
+
const cols = ast.groupBy.map(c => `${capitalize(c.table)}.${c.name}`).join(', ');
|
|
122
|
+
lines.push(`.groupBy(${cols})`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (ast.having) {
|
|
126
|
+
lines.push(`.having(${this.printExpression(ast.having)})`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (ast.orderBy && ast.orderBy.length) {
|
|
130
|
+
ast.orderBy.forEach(o => {
|
|
131
|
+
lines.push(`.orderBy(${capitalize(o.column.table)}.${o.column.name}, '${o.direction}')`);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (ast.limit) lines.push(`.limit(${ast.limit})`);
|
|
136
|
+
if (ast.offset) lines.push(`.offset(${ast.offset})`);
|
|
137
|
+
|
|
138
|
+
return lines;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Prints an expression node to TypeScript code
|
|
143
|
+
* @param expr - Expression node to print
|
|
144
|
+
* @returns TypeScript code representation
|
|
145
|
+
*/
|
|
146
|
+
private printExpression(expr: ExpressionNode): string {
|
|
147
|
+
return visitExpression(expr, this);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Prints an operand node to TypeScript code
|
|
152
|
+
* @param node - Operand node to print
|
|
153
|
+
* @returns TypeScript code representation
|
|
154
|
+
*/
|
|
155
|
+
private printOperand(node: OperandNode): string {
|
|
156
|
+
return visitOperand(node, this);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public visitBinaryExpression(binary: BinaryExpressionNode): string {
|
|
160
|
+
return this.printBinaryExpression(binary);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public visitLogicalExpression(logical: LogicalExpressionNode): string {
|
|
164
|
+
return this.printLogicalExpression(logical);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public visitNullExpression(nullExpr: NullExpressionNode): string {
|
|
168
|
+
return this.printNullExpression(nullExpr);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public visitInExpression(inExpr: InExpressionNode): string {
|
|
172
|
+
return this.printInExpression(inExpr);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public visitExistsExpression(existsExpr: ExistsExpressionNode): string {
|
|
176
|
+
return this.printExistsExpression(existsExpr);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public visitBetweenExpression(betweenExpr: BetweenExpressionNode): string {
|
|
180
|
+
return this.printBetweenExpression(betweenExpr);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public visitColumn(node: ColumnNode): string {
|
|
184
|
+
return this.printColumnOperand(node);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public visitLiteral(node: LiteralNode): string {
|
|
188
|
+
return this.printLiteralOperand(node);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public visitFunction(node: FunctionNode): string {
|
|
192
|
+
return this.printFunctionOperand(node);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public visitJsonPath(node: JsonPathNode): string {
|
|
196
|
+
return this.printJsonPathOperand(node);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public visitScalarSubquery(node: ScalarSubqueryNode): string {
|
|
200
|
+
return this.printScalarSubqueryOperand(node);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public visitCaseExpression(node: CaseExpressionNode): string {
|
|
204
|
+
return this.printCaseExpressionOperand(node);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public visitWindowFunction(node: WindowFunctionNode): string {
|
|
208
|
+
return this.printWindowFunctionOperand(node);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Prints a binary expression to TypeScript code
|
|
213
|
+
* @param binary - Binary expression node
|
|
214
|
+
* @returns TypeScript code representation
|
|
215
|
+
*/
|
|
216
|
+
private printBinaryExpression(binary: BinaryExpressionNode): string {
|
|
217
|
+
const left = this.printOperand(binary.left);
|
|
218
|
+
const right = this.printOperand(binary.right);
|
|
219
|
+
const fn = this.mapOp(binary.operator);
|
|
220
|
+
const args = [left, right];
|
|
221
|
+
if (binary.escape) {
|
|
222
|
+
args.push(this.printOperand(binary.escape));
|
|
223
|
+
}
|
|
224
|
+
return `${fn}(${args.join(', ')})`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Prints a logical expression to TypeScript code
|
|
229
|
+
* @param logical - Logical expression node
|
|
230
|
+
* @returns TypeScript code representation
|
|
231
|
+
*/
|
|
232
|
+
private printLogicalExpression(logical: LogicalExpressionNode): string {
|
|
233
|
+
if (logical.operands.length === 0) return '';
|
|
234
|
+
const parts = logical.operands.map(op => {
|
|
235
|
+
const compiled = this.printExpression(op);
|
|
236
|
+
return op.type === 'LogicalExpression' ? `(${compiled})` : compiled;
|
|
237
|
+
});
|
|
238
|
+
return `${this.mapOp(logical.operator)}(\n ${parts.join(',\n ')}\n )`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Prints an IN expression to TypeScript code
|
|
243
|
+
* @param inExpr - IN expression node
|
|
244
|
+
* @returns TypeScript code representation
|
|
245
|
+
*/
|
|
246
|
+
private printInExpression(inExpr: InExpressionNode): string {
|
|
247
|
+
const left = this.printOperand(inExpr.left);
|
|
248
|
+
const values = inExpr.right.map(v => this.printOperand(v)).join(', ');
|
|
249
|
+
const fn = this.mapOp(inExpr.operator);
|
|
250
|
+
return `${fn}(${left}, [${values}])`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Prints a null expression to TypeScript code
|
|
255
|
+
* @param nullExpr - Null expression node
|
|
256
|
+
* @returns TypeScript code representation
|
|
257
|
+
*/
|
|
258
|
+
private printNullExpression(nullExpr: NullExpressionNode): string {
|
|
259
|
+
const left = this.printOperand(nullExpr.left);
|
|
260
|
+
const fn = this.mapOp(nullExpr.operator);
|
|
261
|
+
return `${fn}(${left})`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Prints a BETWEEN expression to TypeScript code
|
|
266
|
+
* @param betweenExpr - BETWEEN expression node
|
|
267
|
+
* @returns TypeScript code representation
|
|
268
|
+
*/
|
|
269
|
+
private printBetweenExpression(betweenExpr: BetweenExpressionNode): string {
|
|
270
|
+
const left = this.printOperand(betweenExpr.left);
|
|
271
|
+
const lower = this.printOperand(betweenExpr.lower);
|
|
272
|
+
const upper = this.printOperand(betweenExpr.upper);
|
|
273
|
+
return `${this.mapOp(betweenExpr.operator)}(${left}, ${lower}, ${upper})`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Prints an EXISTS expression to TypeScript code
|
|
278
|
+
* @param existsExpr - EXISTS expression node
|
|
279
|
+
* @returns TypeScript code representation
|
|
280
|
+
*/
|
|
281
|
+
private printExistsExpression(existsExpr: ExistsExpressionNode): string {
|
|
282
|
+
const subquery = this.inlineChain(this.buildSelectLines(existsExpr.subquery));
|
|
283
|
+
return `${this.mapOp(existsExpr.operator)}(${subquery})`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Prints a column operand to TypeScript code
|
|
288
|
+
* @param column - Column node
|
|
289
|
+
* @returns TypeScript code representation
|
|
290
|
+
*/
|
|
291
|
+
private printColumnOperand(column: ColumnNode): string {
|
|
292
|
+
return `${capitalize(column.table)}.${column.name}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Prints a literal operand to TypeScript code
|
|
297
|
+
* @param literal - Literal node
|
|
298
|
+
* @returns TypeScript code representation
|
|
299
|
+
*/
|
|
300
|
+
private printLiteralOperand(literal: LiteralNode): string {
|
|
301
|
+
if (literal.value === null) return 'null';
|
|
302
|
+
return typeof literal.value === 'string' ? `'${literal.value}'` : String(literal.value);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Prints a function operand to TypeScript code
|
|
307
|
+
* @param fn - Function node
|
|
308
|
+
* @returns TypeScript code representation
|
|
309
|
+
*/
|
|
310
|
+
private printFunctionOperand(fn: FunctionNode): string {
|
|
311
|
+
const args = fn.args.map(a => this.printOperand(a)).join(', ');
|
|
312
|
+
return `${fn.name.toLowerCase()}(${args})`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Prints a JSON path operand to TypeScript code
|
|
317
|
+
* @param json - JSON path node
|
|
318
|
+
* @returns TypeScript code representation
|
|
319
|
+
*/
|
|
320
|
+
private printJsonPathOperand(json: JsonPathNode): string {
|
|
321
|
+
return `jsonPath(${capitalize(json.column.table)}.${json.column.name}, '${json.path}')`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Prints a scalar subquery operand to TypeScript code
|
|
326
|
+
* @param node - Scalar subquery node
|
|
327
|
+
* @returns TypeScript code representation
|
|
328
|
+
*/
|
|
329
|
+
private printScalarSubqueryOperand(node: ScalarSubqueryNode): string {
|
|
330
|
+
const subquery = this.inlineChain(this.buildSelectLines(node.query));
|
|
331
|
+
return `(${subquery})`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Prints a CASE expression operand to TypeScript code
|
|
336
|
+
* @param node - CASE expression node
|
|
337
|
+
* @returns TypeScript code representation
|
|
338
|
+
*/
|
|
339
|
+
private printCaseExpressionOperand(node: CaseExpressionNode): string {
|
|
340
|
+
const clauses = node.conditions.map(
|
|
341
|
+
condition =>
|
|
342
|
+
`{ when: ${this.printExpression(condition.when)}, then: ${this.printOperand(condition.then)} }`
|
|
343
|
+
);
|
|
344
|
+
const elseValue = node.else ? `, ${this.printOperand(node.else)}` : '';
|
|
345
|
+
return `caseWhen([${clauses.join(', ')}]${elseValue})`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Prints a window function operand to TypeScript code
|
|
350
|
+
* @param node - Window function node
|
|
351
|
+
* @returns TypeScript code representation
|
|
352
|
+
*/
|
|
353
|
+
private printWindowFunctionOperand(node: WindowFunctionNode): string {
|
|
354
|
+
let result = `${node.name}(`;
|
|
355
|
+
if (node.args.length > 0) {
|
|
356
|
+
result += node.args.map(arg => this.printOperand(arg)).join(', ');
|
|
357
|
+
}
|
|
358
|
+
result += ') OVER (';
|
|
359
|
+
|
|
360
|
+
const parts: string[] = [];
|
|
361
|
+
|
|
362
|
+
if (node.partitionBy && node.partitionBy.length > 0) {
|
|
363
|
+
const partitionClause =
|
|
364
|
+
'PARTITION BY ' + node.partitionBy.map(col => `${capitalize(col.table)}.${col.name}`).join(', ');
|
|
365
|
+
parts.push(partitionClause);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (node.orderBy && node.orderBy.length > 0) {
|
|
369
|
+
const orderClause =
|
|
370
|
+
'ORDER BY ' +
|
|
371
|
+
node.orderBy.map(o => `${capitalize(o.column.table)}.${o.column.name} ${o.direction}`).join(', ');
|
|
372
|
+
parts.push(orderClause);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
result += parts.join(' ');
|
|
376
|
+
result += ')';
|
|
377
|
+
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Converts method chain lines to inline format
|
|
383
|
+
* @param lines - Method chain lines
|
|
384
|
+
* @returns Inline method chain string
|
|
385
|
+
*/
|
|
386
|
+
private inlineChain(lines: string[]): string {
|
|
387
|
+
return lines
|
|
388
|
+
.map(line => line.trim())
|
|
389
|
+
.filter(line => line.length > 0)
|
|
390
|
+
.join(' ');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Maps SQL operators to TypeScript function names
|
|
395
|
+
* @param op - SQL operator
|
|
396
|
+
* @returns TypeScript function name
|
|
397
|
+
*/
|
|
398
|
+
private mapOp(op: SqlOperator): string {
|
|
399
|
+
const config = SQL_OPERATOR_REGISTRY[op];
|
|
400
|
+
if (!config) {
|
|
401
|
+
return assertNever(op as never);
|
|
402
|
+
}
|
|
403
|
+
return config.tsName;
|
|
404
|
+
}
|
|
405
|
+
}
|