metal-orm 1.1.10 → 1.1.16

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 (192) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs +106 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +106 -43
  7. package/dist/index.js.map +1 -1
  8. package/package.json +100 -96
  9. package/scripts/generate-entities/render.mjs +21 -12
  10. package/scripts/generate-entities/schema.mjs +209 -195
  11. package/scripts/generate-entities/tree-detection.mjs +159 -153
  12. package/scripts/inflection/pt-br.mjs +468 -468
  13. package/scripts/run-eslint.mjs +33 -34
  14. package/scripts/show-sql-seed.sql +102 -0
  15. package/scripts/show-sql.mjs +321 -325
  16. package/src/bulk/bulk-delete-executor.ts +3 -5
  17. package/src/bulk/bulk-insert-executor.ts +7 -7
  18. package/src/bulk/bulk-update-executor.ts +2 -2
  19. package/src/bulk/bulk-upsert-executor.ts +5 -7
  20. package/src/codegen/naming-strategy.ts +54 -54
  21. package/src/codegen/typescript.ts +482 -482
  22. package/src/core/ast/adapters.ts +1 -1
  23. package/src/core/ast/builders.ts +123 -123
  24. package/src/core/ast/expression-builders.ts +745 -745
  25. package/src/core/ast/expression-nodes.ts +304 -304
  26. package/src/core/ast/expression-visitor.ts +212 -212
  27. package/src/core/ast/expression.ts +20 -20
  28. package/src/core/ast/helpers.ts +14 -14
  29. package/src/core/ast/join.ts +18 -18
  30. package/src/core/ast/query.ts +200 -200
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  35. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  37. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  39. package/src/core/ddl/introspect/context.ts +15 -15
  40. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  41. package/src/core/ddl/introspect/mysql.ts +113 -36
  42. package/src/core/ddl/introspect/registry.ts +40 -40
  43. package/src/core/ddl/introspect/run-select.ts +35 -35
  44. package/src/core/ddl/introspect/utils.ts +56 -56
  45. package/src/core/ddl/naming-strategy.ts +16 -16
  46. package/src/core/ddl/schema-dialect.ts +56 -56
  47. package/src/core/ddl/schema-diff.ts +234 -234
  48. package/src/core/ddl/schema-generator.ts +1 -1
  49. package/src/core/ddl/schema-introspect.ts +25 -25
  50. package/src/core/ddl/sql-writing.ts +171 -171
  51. package/src/core/dialect/abstract.ts +541 -541
  52. package/src/core/dialect/base/cte-compiler.ts +33 -33
  53. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  54. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  55. package/src/core/dialect/base/join-compiler.ts +25 -25
  56. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  57. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  58. package/src/core/dialect/base/returning-strategy.ts +57 -57
  59. package/src/core/dialect/base/sql-dialect.ts +294 -294
  60. package/src/core/dialect/dialect-factory.ts +91 -91
  61. package/src/core/dialect/mssql/functions.ts +120 -120
  62. package/src/core/dialect/mssql/index.ts +317 -317
  63. package/src/core/dialect/mysql/functions.ts +103 -103
  64. package/src/core/dialect/mysql/index.ts +105 -105
  65. package/src/core/dialect/postgres/functions.ts +108 -108
  66. package/src/core/dialect/postgres/index.ts +91 -91
  67. package/src/core/dialect/sqlite/functions.ts +129 -129
  68. package/src/core/dialect/sqlite/index.ts +67 -67
  69. package/src/core/driver/database-driver.ts +19 -19
  70. package/src/core/driver/mssql-driver.ts +23 -23
  71. package/src/core/driver/mysql-driver.ts +23 -23
  72. package/src/core/driver/postgres-driver.ts +23 -23
  73. package/src/core/driver/sqlite-driver.ts +23 -23
  74. package/src/core/execution/db-executor.ts +92 -92
  75. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
  76. package/src/core/execution/executors/mssql-executor.ts +184 -184
  77. package/src/core/execution/executors/mysql-executor.ts +88 -88
  78. package/src/core/execution/executors/postgres-executor.ts +26 -26
  79. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  80. package/src/core/execution/pooling/pool-types.ts +30 -30
  81. package/src/core/execution/pooling/pool.ts +275 -275
  82. package/src/core/functions/array.ts +35 -35
  83. package/src/core/functions/control-flow.ts +79 -79
  84. package/src/core/functions/datetime.ts +237 -237
  85. package/src/core/functions/json.ts +70 -70
  86. package/src/core/functions/numeric.ts +297 -297
  87. package/src/core/functions/text.ts +334 -334
  88. package/src/core/functions/types.ts +33 -33
  89. package/src/core/hydration/types.ts +51 -51
  90. package/src/core/sql/sql-operator-config.ts +39 -39
  91. package/src/core/sql/sql.ts +152 -152
  92. package/src/decorators/column-decorator.ts +87 -87
  93. package/src/decorators/decorator-metadata.ts +143 -143
  94. package/src/decorators/entity.ts +93 -93
  95. package/src/decorators/index.ts +39 -39
  96. package/src/decorators/relations.ts +231 -231
  97. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  98. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  99. package/src/decorators/transformers/transformer-executor.ts +171 -171
  100. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  101. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  102. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  103. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  104. package/src/decorators/validators/country-validator-registry.ts +64 -64
  105. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  106. package/src/decorators/validators/country-validators.ts +105 -105
  107. package/src/decorators/validators/validator-metadata.ts +59 -59
  108. package/src/dto/dto-types.ts +226 -226
  109. package/src/dto/filter-types.ts +141 -141
  110. package/src/dto/index.ts +95 -95
  111. package/src/dto/openapi/generators/base.ts +29 -29
  112. package/src/dto/openapi/generators/column.ts +37 -37
  113. package/src/dto/openapi/generators/dto.ts +91 -91
  114. package/src/dto/openapi/generators/filter.ts +75 -75
  115. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  116. package/src/dto/openapi/generators/pagination.ts +111 -111
  117. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  118. package/src/dto/openapi/index.ts +12 -12
  119. package/src/dto/openapi/types.ts +101 -101
  120. package/src/dto/openapi/utilities.ts +95 -95
  121. package/src/dto/pagination-utils.ts +150 -150
  122. package/src/dto/transform.ts +197 -197
  123. package/src/index.ts +86 -86
  124. package/src/orm/als.ts +58 -58
  125. package/src/orm/domain-event-bus.ts +119 -119
  126. package/src/orm/entity-context.ts +86 -86
  127. package/src/orm/entity-materializer.ts +159 -159
  128. package/src/orm/entity-meta.ts +97 -97
  129. package/src/orm/entity-registry.ts +39 -39
  130. package/src/orm/entity-relation-cache.ts +39 -39
  131. package/src/orm/execute.ts +194 -194
  132. package/src/orm/execution-context.ts +18 -18
  133. package/src/orm/hydration-context.ts +26 -26
  134. package/src/orm/hydration.ts +112 -112
  135. package/src/orm/identity-map.ts +61 -61
  136. package/src/orm/interceptor-pipeline.ts +16 -16
  137. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  138. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  139. package/src/orm/lazy-batch/has-many.ts +69 -69
  140. package/src/orm/lazy-batch/has-one.ts +68 -68
  141. package/src/orm/lazy-batch/shared.ts +125 -125
  142. package/src/orm/orm-session.ts +620 -620
  143. package/src/orm/orm.ts +34 -34
  144. package/src/orm/pooled-executor-factory.ts +121 -121
  145. package/src/orm/query-logger.ts +40 -40
  146. package/src/orm/relation-change-processor.ts +420 -420
  147. package/src/orm/relations/belongs-to.ts +143 -143
  148. package/src/orm/relations/has-many.ts +204 -204
  149. package/src/orm/relations/has-one.ts +174 -174
  150. package/src/orm/relations/many-to-many.ts +288 -288
  151. package/src/orm/relations/morph-many.ts +156 -156
  152. package/src/orm/relations/morph-one.ts +151 -151
  153. package/src/orm/relations/morph-to.ts +162 -162
  154. package/src/orm/runtime-types.ts +92 -92
  155. package/src/orm/save-graph.ts +561 -561
  156. package/src/orm/transaction-runner.ts +24 -24
  157. package/src/orm/unit-of-work.ts +426 -426
  158. package/src/query/index.ts +69 -69
  159. package/src/query/target.ts +46 -46
  160. package/src/query-builder/column-selector.ts +81 -81
  161. package/src/query-builder/delete.ts +141 -141
  162. package/src/query-builder/insert-query-state.ts +149 -149
  163. package/src/query-builder/insert.ts +117 -117
  164. package/src/query-builder/query-ast-service.ts +287 -287
  165. package/src/query-builder/query-resolution.ts +78 -78
  166. package/src/query-builder/raw-column-parser.ts +38 -38
  167. package/src/query-builder/relation-filter-utils.ts +153 -153
  168. package/src/query-builder/relation-join-planner.ts +10 -10
  169. package/src/query-builder/relation-manager.ts +87 -87
  170. package/src/query-builder/relation-projection-helper.ts +102 -102
  171. package/src/query-builder/relation-types.ts +9 -9
  172. package/src/query-builder/relation-utils.ts +15 -15
  173. package/src/query-builder/select/cte-facet.ts +40 -40
  174. package/src/query-builder/select/from-facet.ts +80 -80
  175. package/src/query-builder/select/join-facet.ts +62 -62
  176. package/src/query-builder/select/predicate-facet.ts +104 -104
  177. package/src/query-builder/select/projection-facet.ts +70 -70
  178. package/src/query-builder/select/relation-facet.ts +81 -81
  179. package/src/query-builder/select/setop-facet.ts +36 -36
  180. package/src/query-builder/select-helpers.ts +1 -1
  181. package/src/query-builder/select-query-builder-deps.ts +130 -130
  182. package/src/query-builder/select-query-state.ts +207 -207
  183. package/src/query-builder/update.ts +151 -151
  184. package/src/schema/column-types.ts +322 -322
  185. package/src/schema/table-guards.ts +37 -37
  186. package/src/schema/table.ts +281 -281
  187. package/src/tree/index.ts +13 -13
  188. package/src/tree/nested-set-strategy.ts +545 -545
  189. package/src/tree/tree-decorator.ts +397 -397
  190. package/src/tree/tree-manager.ts +754 -754
  191. package/src/tree/tree-query.ts +427 -427
  192. package/src/tree/tree-types.ts +299 -299
@@ -1,222 +1,222 @@
1
- import { OrderingTerm, SelectQueryNode } from '../core/ast/query.js';
2
- import {
3
- ExpressionNode,
4
- OperandNode,
1
+ import { OrderingTerm, SelectQueryNode } from '../core/ast/query.js';
2
+ import {
3
+ ExpressionNode,
4
+ OperandNode,
5
5
  BinaryExpressionNode,
6
6
  LogicalExpressionNode,
7
7
  NotExpressionNode,
8
8
  InExpressionNode,
9
- NullExpressionNode,
10
- JsonPathNode,
11
- ExistsExpressionNode,
12
- BetweenExpressionNode,
13
- ScalarSubqueryNode,
14
- CaseExpressionNode,
15
- WindowFunctionNode,
16
- ArithmeticExpressionNode,
17
- ColumnNode,
18
- LiteralNode,
19
- FunctionNode,
20
- AliasRefNode,
21
- CastExpressionNode,
22
- CollateExpressionNode,
23
- ExpressionVisitor,
24
- OperandVisitor,
25
- visitExpression,
26
- visitOperand
27
- } from '../core/ast/expression.js';
28
- import { SQL_OPERATOR_REGISTRY } from '../core/sql/sql-operator-config.js';
29
- import { SqlOperator } from '../core/sql/sql.js';
30
- import { isRelationAlias } from '../query-builder/relation-alias.js';
31
- import { HydrationMetadata } from '../core/hydration/types.js';
32
- import { getJoinRelationName } from '../core/ast/join-metadata.js';
33
- import { NamingStrategy, DefaultNamingStrategy } from './naming-strategy.js';
34
-
35
- const assertNever = (value: never): never => {
36
- throw new Error(`Unhandled SQL operator: ${value}`);
37
- };
38
-
39
- /**
40
- * Generates TypeScript code from query AST nodes
41
- */
42
- type SelectionColumn =
43
- | ColumnNode
44
- | FunctionNode
45
- | ScalarSubqueryNode
46
- | CaseExpressionNode
47
- | WindowFunctionNode
48
- | CastExpressionNode;
49
-
50
- export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVisitor<string> {
51
- constructor(private namingStrategy: NamingStrategy = new DefaultNamingStrategy()) { }
52
-
53
- /**
54
- * Generates TypeScript code from a query AST
55
- * @param ast - Query AST to generate code from
56
- * @returns Generated TypeScript code
57
- */
58
- generate(ast: SelectQueryNode): string {
59
- const chainLines = this.buildSelectLines(ast);
60
- const lines = chainLines.map((line, index) => (index === 0 ? `const query = ${line}` : line));
61
- lines.push(';', '', 'await query.execute();');
62
- return lines.join('\n');
63
- }
64
-
65
- /**
66
- * Builds TypeScript method chain lines from query AST
67
- * @param ast - Query AST
68
- * @returns Array of TypeScript method chain lines
69
- */
70
- private buildSelectLines(ast: SelectQueryNode): string[] {
71
- const lines: string[] = [];
72
- const hydration = (ast.meta as HydrationMetadata | undefined)?.hydration;
73
- const hydratedRelations = new Set(hydration?.relations?.map(r => r.name) ?? []);
74
-
75
- const selections = ast.columns
76
- .filter((col): col is SelectionColumn => !(hydration && isRelationAlias(col.alias)))
77
- .map((col, index) => `${this.getSelectionKey(col, index)}: ${this.printOperand(col)}`);
78
-
79
- lines.push(`db.select({`);
80
- selections.forEach((sel, index) => {
81
- lines.push(` ${sel}${index < selections.length - 1 ? ',' : ''}`);
82
- });
83
- lines.push(`})`);
84
- lines.push(`.from(${this.namingStrategy.tableToSymbol(ast.from)})`);
85
-
86
- if (ast.distinct && ast.distinct.length) {
87
- const cols = ast.distinct.map(c => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(', ');
88
- lines.push(`.distinct(${cols})`);
89
- }
90
-
91
- ast.joins.forEach(join => {
92
- const relationName = getJoinRelationName(join);
93
- if (relationName && hydratedRelations.has(relationName)) {
94
- return;
95
- }
96
-
97
- if (relationName) {
98
- if (join.kind === 'INNER') {
99
- lines.push(`.joinRelation('${relationName}')`);
100
- } else {
101
- lines.push(`.joinRelation('${relationName}', '${join.kind}')`);
102
- }
103
- } else {
104
- const table = this.namingStrategy.tableToSymbol(join.table);
105
- const cond = this.printExpression(join.condition);
106
- let method = 'innerJoin';
107
- if (join.kind === 'LEFT') method = 'leftJoin';
108
- if (join.kind === 'RIGHT') method = 'rightJoin';
109
- lines.push(`.${method}(${table}, ${cond})`);
110
- }
111
- });
112
-
113
- if (hydration?.relations?.length) {
114
- hydration.relations.forEach(rel => {
115
- const options: string[] = [];
116
- if (rel.columns.length) options.push(`columns: [${rel.columns.map(c => `'${c}'`).join(', ')}]`);
117
- if (rel.aliasPrefix !== rel.name) options.push(`aliasPrefix: '${rel.aliasPrefix}'`);
118
- const opts = options.length ? `, { ${options.join(', ')} }` : '';
119
- lines.push(`.include('${rel.name}'${opts})`);
120
- });
121
- }
122
-
123
- if (ast.where) {
124
- lines.push(`.where(${this.printExpression(ast.where)})`);
125
- }
126
-
127
- if (ast.groupBy && ast.groupBy.length) {
128
- const cols = ast.groupBy.map(term => this.printOrderingTerm(term)).join(', ');
129
- lines.push(`.groupBy(${cols})`);
130
- }
131
-
132
- if (ast.having) {
133
- lines.push(`.having(${this.printExpression(ast.having)})`);
134
- }
135
-
136
- if (ast.orderBy && ast.orderBy.length) {
137
- ast.orderBy.forEach(o => {
138
- const term = this.printOrderingTerm(o.term);
139
- const opts: string[] = [`direction: '${o.direction}'`];
140
- if (o.nulls) opts.push(`nulls: '${o.nulls}'`);
141
- if (o.collation) opts.push(`collation: '${o.collation}'`);
142
- const hasOpts = opts.length > 1;
143
- if (hasOpts) {
144
- lines.push(`.orderBy(${term}, { ${opts.join(', ')} })`);
145
- } else {
146
- lines.push(`.orderBy(${term}, '${o.direction}')`);
147
- }
148
- });
149
- }
150
-
151
- if (ast.limit) lines.push(`.limit(${ast.limit})`);
152
- if (ast.offset) lines.push(`.offset(${ast.offset})`);
153
-
154
- return lines;
155
- }
156
-
157
- /**
158
- * Prints an expression node to TypeScript code
159
- * @param expr - Expression node to print
160
- * @returns TypeScript code representation
161
- */
162
- private printExpression(expr: ExpressionNode): string {
163
- return visitExpression(expr, this);
164
- }
165
-
166
- /**
167
- * Prints an operand node to TypeScript code
168
- * @param node - Operand node to print
169
- * @returns TypeScript code representation
170
- */
171
- private printOperand(node: OperandNode): string {
172
- return visitOperand(node, this);
173
- }
174
-
175
- /**
176
- * Prints an ordering term (operand/expression/alias) to TypeScript code.
177
- */
178
- private printOrderingTerm(term: OrderingTerm): string {
179
- if (!term || !('type' in term)) {
180
- throw new Error('Unsupported ordering term');
181
- }
182
-
183
- switch (term.type) {
184
- case 'Column':
185
- return `${this.namingStrategy.tableToSymbol(term.table)}.${term.name}`;
186
- case 'AliasRef':
187
- return this.visitAliasRef(term);
188
- case 'Literal':
189
- case 'Function':
190
- case 'JsonPath':
191
- case 'ScalarSubquery':
192
- case 'CaseExpression':
193
- case 'WindowFunction':
194
- case 'Cast':
195
- case 'Collate':
196
- return this.printOperand(term);
197
- default:
198
- return this.printExpression(term);
199
- }
200
- }
201
-
202
- private getSelectionKey(selection: SelectionColumn, index: number): string {
203
- if (selection.alias) {
204
- return selection.alias;
205
- }
206
- if (this.isNamedSelection(selection)) {
207
- return selection.name;
208
- }
209
- return `selection${index + 1}`;
210
- }
211
-
212
- private isNamedSelection(selection: SelectionColumn): selection is ColumnNode | FunctionNode | WindowFunctionNode {
213
- return 'name' in selection;
214
- }
215
-
216
- public visitBinaryExpression(binary: BinaryExpressionNode): string {
217
- return this.printBinaryExpression(binary);
218
- }
219
-
9
+ NullExpressionNode,
10
+ JsonPathNode,
11
+ ExistsExpressionNode,
12
+ BetweenExpressionNode,
13
+ ScalarSubqueryNode,
14
+ CaseExpressionNode,
15
+ WindowFunctionNode,
16
+ ArithmeticExpressionNode,
17
+ ColumnNode,
18
+ LiteralNode,
19
+ FunctionNode,
20
+ AliasRefNode,
21
+ CastExpressionNode,
22
+ CollateExpressionNode,
23
+ ExpressionVisitor,
24
+ OperandVisitor,
25
+ visitExpression,
26
+ visitOperand
27
+ } from '../core/ast/expression.js';
28
+ import { SQL_OPERATOR_REGISTRY } from '../core/sql/sql-operator-config.js';
29
+ import { SqlOperator } from '../core/sql/sql.js';
30
+ import { isRelationAlias } from '../query-builder/relation-alias.js';
31
+ import { HydrationMetadata } from '../core/hydration/types.js';
32
+ import { getJoinRelationName } from '../core/ast/join-metadata.js';
33
+ import { NamingStrategy, DefaultNamingStrategy } from './naming-strategy.js';
34
+
35
+ const assertNever = (value: never): never => {
36
+ throw new Error(`Unhandled SQL operator: ${value}`);
37
+ };
38
+
39
+ /**
40
+ * Generates TypeScript code from query AST nodes
41
+ */
42
+ type SelectionColumn =
43
+ | ColumnNode
44
+ | FunctionNode
45
+ | ScalarSubqueryNode
46
+ | CaseExpressionNode
47
+ | WindowFunctionNode
48
+ | CastExpressionNode;
49
+
50
+ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVisitor<string> {
51
+ constructor(private namingStrategy: NamingStrategy = new DefaultNamingStrategy()) { }
52
+
53
+ /**
54
+ * Generates TypeScript code from a query AST
55
+ * @param ast - Query AST to generate code from
56
+ * @returns Generated TypeScript code
57
+ */
58
+ generate(ast: SelectQueryNode): string {
59
+ const chainLines = this.buildSelectLines(ast);
60
+ const lines = chainLines.map((line, index) => (index === 0 ? `const query = ${line}` : line));
61
+ lines.push(';', '', 'await query.execute();');
62
+ return lines.join('\n');
63
+ }
64
+
65
+ /**
66
+ * Builds TypeScript method chain lines from query AST
67
+ * @param ast - Query AST
68
+ * @returns Array of TypeScript method chain lines
69
+ */
70
+ private buildSelectLines(ast: SelectQueryNode): string[] {
71
+ const lines: string[] = [];
72
+ const hydration = (ast.meta as HydrationMetadata | undefined)?.hydration;
73
+ const hydratedRelations = new Set(hydration?.relations?.map(r => r.name) ?? []);
74
+
75
+ const selections = ast.columns
76
+ .filter((col): col is SelectionColumn => !(hydration && isRelationAlias(col.alias)))
77
+ .map((col, index) => `${this.getSelectionKey(col, index)}: ${this.printOperand(col)}`);
78
+
79
+ lines.push(`db.select({`);
80
+ selections.forEach((sel, index) => {
81
+ lines.push(` ${sel}${index < selections.length - 1 ? ',' : ''}`);
82
+ });
83
+ lines.push(`})`);
84
+ lines.push(`.from(${this.namingStrategy.tableToSymbol(ast.from)})`);
85
+
86
+ if (ast.distinct && ast.distinct.length) {
87
+ const cols = ast.distinct.map(c => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(', ');
88
+ lines.push(`.distinct(${cols})`);
89
+ }
90
+
91
+ ast.joins.forEach(join => {
92
+ const relationName = getJoinRelationName(join);
93
+ if (relationName && hydratedRelations.has(relationName)) {
94
+ return;
95
+ }
96
+
97
+ if (relationName) {
98
+ if (join.kind === 'INNER') {
99
+ lines.push(`.joinRelation('${relationName}')`);
100
+ } else {
101
+ lines.push(`.joinRelation('${relationName}', '${join.kind}')`);
102
+ }
103
+ } else {
104
+ const table = this.namingStrategy.tableToSymbol(join.table);
105
+ const cond = this.printExpression(join.condition);
106
+ let method = 'innerJoin';
107
+ if (join.kind === 'LEFT') method = 'leftJoin';
108
+ if (join.kind === 'RIGHT') method = 'rightJoin';
109
+ lines.push(`.${method}(${table}, ${cond})`);
110
+ }
111
+ });
112
+
113
+ if (hydration?.relations?.length) {
114
+ hydration.relations.forEach(rel => {
115
+ const options: string[] = [];
116
+ if (rel.columns.length) options.push(`columns: [${rel.columns.map(c => `'${c}'`).join(', ')}]`);
117
+ if (rel.aliasPrefix !== rel.name) options.push(`aliasPrefix: '${rel.aliasPrefix}'`);
118
+ const opts = options.length ? `, { ${options.join(', ')} }` : '';
119
+ lines.push(`.include('${rel.name}'${opts})`);
120
+ });
121
+ }
122
+
123
+ if (ast.where) {
124
+ lines.push(`.where(${this.printExpression(ast.where)})`);
125
+ }
126
+
127
+ if (ast.groupBy && ast.groupBy.length) {
128
+ const cols = ast.groupBy.map(term => this.printOrderingTerm(term)).join(', ');
129
+ lines.push(`.groupBy(${cols})`);
130
+ }
131
+
132
+ if (ast.having) {
133
+ lines.push(`.having(${this.printExpression(ast.having)})`);
134
+ }
135
+
136
+ if (ast.orderBy && ast.orderBy.length) {
137
+ ast.orderBy.forEach(o => {
138
+ const term = this.printOrderingTerm(o.term);
139
+ const opts: string[] = [`direction: '${o.direction}'`];
140
+ if (o.nulls) opts.push(`nulls: '${o.nulls}'`);
141
+ if (o.collation) opts.push(`collation: '${o.collation}'`);
142
+ const hasOpts = opts.length > 1;
143
+ if (hasOpts) {
144
+ lines.push(`.orderBy(${term}, { ${opts.join(', ')} })`);
145
+ } else {
146
+ lines.push(`.orderBy(${term}, '${o.direction}')`);
147
+ }
148
+ });
149
+ }
150
+
151
+ if (ast.limit) lines.push(`.limit(${ast.limit})`);
152
+ if (ast.offset) lines.push(`.offset(${ast.offset})`);
153
+
154
+ return lines;
155
+ }
156
+
157
+ /**
158
+ * Prints an expression node to TypeScript code
159
+ * @param expr - Expression node to print
160
+ * @returns TypeScript code representation
161
+ */
162
+ private printExpression(expr: ExpressionNode): string {
163
+ return visitExpression(expr, this);
164
+ }
165
+
166
+ /**
167
+ * Prints an operand node to TypeScript code
168
+ * @param node - Operand node to print
169
+ * @returns TypeScript code representation
170
+ */
171
+ private printOperand(node: OperandNode): string {
172
+ return visitOperand(node, this);
173
+ }
174
+
175
+ /**
176
+ * Prints an ordering term (operand/expression/alias) to TypeScript code.
177
+ */
178
+ private printOrderingTerm(term: OrderingTerm): string {
179
+ if (!term || !('type' in term)) {
180
+ throw new Error('Unsupported ordering term');
181
+ }
182
+
183
+ switch (term.type) {
184
+ case 'Column':
185
+ return `${this.namingStrategy.tableToSymbol(term.table)}.${term.name}`;
186
+ case 'AliasRef':
187
+ return this.visitAliasRef(term);
188
+ case 'Literal':
189
+ case 'Function':
190
+ case 'JsonPath':
191
+ case 'ScalarSubquery':
192
+ case 'CaseExpression':
193
+ case 'WindowFunction':
194
+ case 'Cast':
195
+ case 'Collate':
196
+ return this.printOperand(term);
197
+ default:
198
+ return this.printExpression(term);
199
+ }
200
+ }
201
+
202
+ private getSelectionKey(selection: SelectionColumn, index: number): string {
203
+ if (selection.alias) {
204
+ return selection.alias;
205
+ }
206
+ if (this.isNamedSelection(selection)) {
207
+ return selection.name;
208
+ }
209
+ return `selection${index + 1}`;
210
+ }
211
+
212
+ private isNamedSelection(selection: SelectionColumn): selection is ColumnNode | FunctionNode | WindowFunctionNode {
213
+ return 'name' in selection;
214
+ }
215
+
216
+ public visitBinaryExpression(binary: BinaryExpressionNode): string {
217
+ return this.printBinaryExpression(binary);
218
+ }
219
+
220
220
  public visitLogicalExpression(logical: LogicalExpressionNode): string {
221
221
  return this.printLogicalExpression(logical);
222
222
  }
@@ -228,84 +228,84 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
228
228
  public visitNullExpression(nullExpr: NullExpressionNode): string {
229
229
  return this.printNullExpression(nullExpr);
230
230
  }
231
-
232
- public visitInExpression(inExpr: InExpressionNode): string {
233
- return this.printInExpression(inExpr);
234
- }
235
-
236
- public visitExistsExpression(existsExpr: ExistsExpressionNode): string {
237
- return this.printExistsExpression(existsExpr);
238
- }
239
-
240
- public visitBetweenExpression(betweenExpr: BetweenExpressionNode): string {
241
- return this.printBetweenExpression(betweenExpr);
242
- }
243
-
244
- public visitArithmeticExpression(arithExpr: ArithmeticExpressionNode): string {
245
- return this.printArithmeticExpression(arithExpr);
246
- }
247
-
248
- public visitColumn(node: ColumnNode): string {
249
- return this.printColumnOperand(node);
250
- }
251
-
252
- public visitLiteral(node: LiteralNode): string {
253
- return this.printLiteralOperand(node);
254
- }
255
-
256
- public visitFunction(node: FunctionNode): string {
257
- return this.printFunctionOperand(node);
258
- }
259
-
260
- public visitJsonPath(node: JsonPathNode): string {
261
- return this.printJsonPathOperand(node);
262
- }
263
-
264
- public visitScalarSubquery(node: ScalarSubqueryNode): string {
265
- return this.printScalarSubqueryOperand(node);
266
- }
267
-
268
- public visitCaseExpression(node: CaseExpressionNode): string {
269
- return this.printCaseExpressionOperand(node);
270
- }
271
-
272
- public visitWindowFunction(node: WindowFunctionNode): string {
273
- return this.printWindowFunctionOperand(node);
274
- }
275
-
276
- public visitCast(node: CastExpressionNode): string {
277
- return this.printCastOperand(node);
278
- }
279
-
280
- public visitCollate(node: CollateExpressionNode): string {
281
- return this.printCollateOperand(node);
282
- }
283
-
284
- public visitAliasRef(node: AliasRefNode): string {
285
- return `aliasRef('${node.name}')`;
286
- }
287
-
288
- /**
289
- * Prints a binary expression to TypeScript code
290
- * @param binary - Binary expression node
291
- * @returns TypeScript code representation
292
- */
293
- private printBinaryExpression(binary: BinaryExpressionNode): string {
294
- const left = this.printOperand(binary.left);
295
- const right = this.printOperand(binary.right);
296
- const fn = this.mapOp(binary.operator);
297
- const args = [left, right];
298
- if (binary.escape) {
299
- args.push(this.printOperand(binary.escape));
300
- }
301
- return `${fn}(${args.join(', ')})`;
302
- }
303
-
304
- /**
305
- * Prints a logical expression to TypeScript code
306
- * @param logical - Logical expression node
307
- * @returns TypeScript code representation
308
- */
231
+
232
+ public visitInExpression(inExpr: InExpressionNode): string {
233
+ return this.printInExpression(inExpr);
234
+ }
235
+
236
+ public visitExistsExpression(existsExpr: ExistsExpressionNode): string {
237
+ return this.printExistsExpression(existsExpr);
238
+ }
239
+
240
+ public visitBetweenExpression(betweenExpr: BetweenExpressionNode): string {
241
+ return this.printBetweenExpression(betweenExpr);
242
+ }
243
+
244
+ public visitArithmeticExpression(arithExpr: ArithmeticExpressionNode): string {
245
+ return this.printArithmeticExpression(arithExpr);
246
+ }
247
+
248
+ public visitColumn(node: ColumnNode): string {
249
+ return this.printColumnOperand(node);
250
+ }
251
+
252
+ public visitLiteral(node: LiteralNode): string {
253
+ return this.printLiteralOperand(node);
254
+ }
255
+
256
+ public visitFunction(node: FunctionNode): string {
257
+ return this.printFunctionOperand(node);
258
+ }
259
+
260
+ public visitJsonPath(node: JsonPathNode): string {
261
+ return this.printJsonPathOperand(node);
262
+ }
263
+
264
+ public visitScalarSubquery(node: ScalarSubqueryNode): string {
265
+ return this.printScalarSubqueryOperand(node);
266
+ }
267
+
268
+ public visitCaseExpression(node: CaseExpressionNode): string {
269
+ return this.printCaseExpressionOperand(node);
270
+ }
271
+
272
+ public visitWindowFunction(node: WindowFunctionNode): string {
273
+ return this.printWindowFunctionOperand(node);
274
+ }
275
+
276
+ public visitCast(node: CastExpressionNode): string {
277
+ return this.printCastOperand(node);
278
+ }
279
+
280
+ public visitCollate(node: CollateExpressionNode): string {
281
+ return this.printCollateOperand(node);
282
+ }
283
+
284
+ public visitAliasRef(node: AliasRefNode): string {
285
+ return `aliasRef('${node.name}')`;
286
+ }
287
+
288
+ /**
289
+ * Prints a binary expression to TypeScript code
290
+ * @param binary - Binary expression node
291
+ * @returns TypeScript code representation
292
+ */
293
+ private printBinaryExpression(binary: BinaryExpressionNode): string {
294
+ const left = this.printOperand(binary.left);
295
+ const right = this.printOperand(binary.right);
296
+ const fn = this.mapOp(binary.operator);
297
+ const args = [left, right];
298
+ if (binary.escape) {
299
+ args.push(this.printOperand(binary.escape));
300
+ }
301
+ return `${fn}(${args.join(', ')})`;
302
+ }
303
+
304
+ /**
305
+ * Prints a logical expression to TypeScript code
306
+ * @param logical - Logical expression node
307
+ * @returns TypeScript code representation
308
+ */
309
309
  private printLogicalExpression(logical: LogicalExpressionNode): string {
310
310
  if (logical.operands.length === 0) return '';
311
311
  const parts = logical.operands.map(op => {
@@ -321,192 +321,192 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
321
321
 
322
322
  private printArithmeticExpression(expr: ArithmeticExpressionNode): string {
323
323
  const left = this.printOperand(expr.left);
324
- const right = this.printOperand(expr.right);
325
- return `${left} ${expr.operator} ${right}`;
326
- }
327
-
328
- /**
329
- * Prints an IN expression to TypeScript code
330
- * @param inExpr - IN expression node
331
- * @returns TypeScript code representation
332
- */
333
- private printInExpression(inExpr: InExpressionNode): string {
334
- const left = this.printOperand(inExpr.left);
335
- const fn = this.mapOp(inExpr.operator);
336
- if (Array.isArray(inExpr.right)) {
337
- const values = inExpr.right.map(v => this.printOperand(v)).join(', ');
338
- return `${fn}(${left}, [${values}])`;
339
- }
340
- const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
341
- return `${fn}(${left}, (${subquery}))`;
342
- }
343
-
344
- /**
345
- * Prints a null expression to TypeScript code
346
- * @param nullExpr - Null expression node
347
- * @returns TypeScript code representation
348
- */
349
- private printNullExpression(nullExpr: NullExpressionNode): string {
350
- const left = this.printOperand(nullExpr.left);
351
- const fn = this.mapOp(nullExpr.operator);
352
- return `${fn}(${left})`;
353
- }
354
-
355
- /**
356
- * Prints a BETWEEN expression to TypeScript code
357
- * @param betweenExpr - BETWEEN expression node
358
- * @returns TypeScript code representation
359
- */
360
- private printBetweenExpression(betweenExpr: BetweenExpressionNode): string {
361
- const left = this.printOperand(betweenExpr.left);
362
- const lower = this.printOperand(betweenExpr.lower);
363
- const upper = this.printOperand(betweenExpr.upper);
364
- return `${this.mapOp(betweenExpr.operator)}(${left}, ${lower}, ${upper})`;
365
- }
366
-
367
- /**
368
- * Prints an EXISTS expression to TypeScript code
369
- * @param existsExpr - EXISTS expression node
370
- * @returns TypeScript code representation
371
- */
372
- private printExistsExpression(existsExpr: ExistsExpressionNode): string {
373
- const subquery = this.inlineChain(this.buildSelectLines(existsExpr.subquery));
374
- return `${this.mapOp(existsExpr.operator)}(${subquery})`;
375
- }
376
-
377
- /**
378
- * Prints a column operand to TypeScript code
379
- * @param column - Column node
380
- * @returns TypeScript code representation
381
- */
382
- private printColumnOperand(column: ColumnNode): string {
383
- return `${this.namingStrategy.tableToSymbol(column.table)}.${column.name}`;
384
- }
385
-
386
- /**
387
- * Prints a literal operand to TypeScript code
388
- * @param literal - Literal node
389
- * @returns TypeScript code representation
390
- */
391
- private printLiteralOperand(literal: LiteralNode): string {
392
- if (literal.value === null) return 'null';
393
- return typeof literal.value === 'string' ? `'${literal.value}'` : String(literal.value);
394
- }
395
-
396
- /**
397
- * Prints a function operand to TypeScript code
398
- * @param fn - Function node
399
- * @returns TypeScript code representation
400
- */
401
- private printFunctionOperand(fn: FunctionNode): string {
402
- const args = fn.args.map(a => this.printOperand(a)).join(', ');
403
- return `${fn.name.toLowerCase()}(${args})`;
404
- }
405
-
406
- /**
407
- * Prints a JSON path operand to TypeScript code
408
- * @param json - JSON path node
409
- * @returns TypeScript code representation
410
- */
411
- private printJsonPathOperand(json: JsonPathNode): string {
412
- return `jsonPath(${this.namingStrategy.tableToSymbol(json.column.table)}.${json.column.name}, '${json.path}')`;
413
- }
414
-
415
- /**
416
- * Prints a scalar subquery operand to TypeScript code
417
- * @param node - Scalar subquery node
418
- * @returns TypeScript code representation
419
- */
420
- private printScalarSubqueryOperand(node: ScalarSubqueryNode): string {
421
- const subquery = this.inlineChain(this.buildSelectLines(node.query));
422
- return `(${subquery})`;
423
- }
424
-
425
- /**
426
- * Prints a CASE expression operand to TypeScript code
427
- * @param node - CASE expression node
428
- * @returns TypeScript code representation
429
- */
430
- private printCaseExpressionOperand(node: CaseExpressionNode): string {
431
- const clauses = node.conditions.map(
432
- condition =>
433
- `{ when: ${this.printExpression(condition.when)}, then: ${this.printOperand(condition.then)} }`
434
- );
435
- const elseValue = node.else ? `, ${this.printOperand(node.else)}` : '';
436
- return `caseWhen([${clauses.join(', ')}]${elseValue})`;
437
- }
438
-
439
- /**
440
- * Prints a window function operand to TypeScript code
441
- * @param node - Window function node
442
- * @returns TypeScript code representation
443
- */
444
- private printWindowFunctionOperand(node: WindowFunctionNode): string {
445
- let result = `${node.name}(`;
446
- if (node.args.length > 0) {
447
- result += node.args.map(arg => this.printOperand(arg)).join(', ');
448
- }
449
- result += ') OVER (';
450
-
451
- const parts: string[] = [];
452
-
453
- if (node.partitionBy && node.partitionBy.length > 0) {
454
- const partitionClause =
455
- 'PARTITION BY ' + node.partitionBy.map(col => `${this.namingStrategy.tableToSymbol(col.table)}.${col.name}`).join(', ');
456
- parts.push(partitionClause);
457
- }
458
-
459
- if (node.orderBy && node.orderBy.length > 0) {
460
- const orderClause =
461
- 'ORDER BY ' +
462
- node.orderBy
463
- .map(o => {
464
- const term = this.printOrderingTerm(o.term);
465
- const collation = o.collation ? ` COLLATE ${o.collation}` : '';
466
- const nulls = o.nulls ? ` NULLS ${o.nulls}` : '';
467
- return `${term} ${o.direction}${collation}${nulls}`;
468
- })
469
- .join(', ');
470
- parts.push(orderClause);
471
- }
472
-
473
- result += parts.join(' ');
474
- result += ')';
475
-
476
- return result;
477
- }
478
-
479
- private printCastOperand(node: CastExpressionNode): string {
480
- const typeLiteral = node.castType.replace(/'/g, "\\'");
481
- return `cast(${this.printOperand(node.expression)}, '${typeLiteral}')`;
482
- }
483
-
484
- private printCollateOperand(node: CollateExpressionNode): string {
485
- return `collate(${this.printOperand(node.expression)}, '${node.collation}')`;
486
- }
487
-
488
- /**
489
- * Converts method chain lines to inline format
490
- * @param lines - Method chain lines
491
- * @returns Inline method chain string
492
- */
493
- private inlineChain(lines: string[]): string {
494
- return lines
495
- .map(line => line.trim())
496
- .filter(line => line.length > 0)
497
- .join(' ');
498
- }
499
-
500
- /**
501
- * Maps SQL operators to TypeScript function names
502
- * @param op - SQL operator
503
- * @returns TypeScript function name
504
- */
505
- private mapOp(op: SqlOperator): string {
506
- const config = SQL_OPERATOR_REGISTRY[op];
507
- if (!config) {
508
- return assertNever(op as never);
509
- }
510
- return config.tsName;
511
- }
512
- }
324
+ const right = this.printOperand(expr.right);
325
+ return `${left} ${expr.operator} ${right}`;
326
+ }
327
+
328
+ /**
329
+ * Prints an IN expression to TypeScript code
330
+ * @param inExpr - IN expression node
331
+ * @returns TypeScript code representation
332
+ */
333
+ private printInExpression(inExpr: InExpressionNode): string {
334
+ const left = this.printOperand(inExpr.left);
335
+ const fn = this.mapOp(inExpr.operator);
336
+ if (Array.isArray(inExpr.right)) {
337
+ const values = inExpr.right.map(v => this.printOperand(v)).join(', ');
338
+ return `${fn}(${left}, [${values}])`;
339
+ }
340
+ const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
341
+ return `${fn}(${left}, (${subquery}))`;
342
+ }
343
+
344
+ /**
345
+ * Prints a null expression to TypeScript code
346
+ * @param nullExpr - Null expression node
347
+ * @returns TypeScript code representation
348
+ */
349
+ private printNullExpression(nullExpr: NullExpressionNode): string {
350
+ const left = this.printOperand(nullExpr.left);
351
+ const fn = this.mapOp(nullExpr.operator);
352
+ return `${fn}(${left})`;
353
+ }
354
+
355
+ /**
356
+ * Prints a BETWEEN expression to TypeScript code
357
+ * @param betweenExpr - BETWEEN expression node
358
+ * @returns TypeScript code representation
359
+ */
360
+ private printBetweenExpression(betweenExpr: BetweenExpressionNode): string {
361
+ const left = this.printOperand(betweenExpr.left);
362
+ const lower = this.printOperand(betweenExpr.lower);
363
+ const upper = this.printOperand(betweenExpr.upper);
364
+ return `${this.mapOp(betweenExpr.operator)}(${left}, ${lower}, ${upper})`;
365
+ }
366
+
367
+ /**
368
+ * Prints an EXISTS expression to TypeScript code
369
+ * @param existsExpr - EXISTS expression node
370
+ * @returns TypeScript code representation
371
+ */
372
+ private printExistsExpression(existsExpr: ExistsExpressionNode): string {
373
+ const subquery = this.inlineChain(this.buildSelectLines(existsExpr.subquery));
374
+ return `${this.mapOp(existsExpr.operator)}(${subquery})`;
375
+ }
376
+
377
+ /**
378
+ * Prints a column operand to TypeScript code
379
+ * @param column - Column node
380
+ * @returns TypeScript code representation
381
+ */
382
+ private printColumnOperand(column: ColumnNode): string {
383
+ return `${this.namingStrategy.tableToSymbol(column.table)}.${column.name}`;
384
+ }
385
+
386
+ /**
387
+ * Prints a literal operand to TypeScript code
388
+ * @param literal - Literal node
389
+ * @returns TypeScript code representation
390
+ */
391
+ private printLiteralOperand(literal: LiteralNode): string {
392
+ if (literal.value === null) return 'null';
393
+ return typeof literal.value === 'string' ? `'${literal.value}'` : String(literal.value);
394
+ }
395
+
396
+ /**
397
+ * Prints a function operand to TypeScript code
398
+ * @param fn - Function node
399
+ * @returns TypeScript code representation
400
+ */
401
+ private printFunctionOperand(fn: FunctionNode): string {
402
+ const args = fn.args.map(a => this.printOperand(a)).join(', ');
403
+ return `${fn.name.toLowerCase()}(${args})`;
404
+ }
405
+
406
+ /**
407
+ * Prints a JSON path operand to TypeScript code
408
+ * @param json - JSON path node
409
+ * @returns TypeScript code representation
410
+ */
411
+ private printJsonPathOperand(json: JsonPathNode): string {
412
+ return `jsonPath(${this.namingStrategy.tableToSymbol(json.column.table)}.${json.column.name}, '${json.path}')`;
413
+ }
414
+
415
+ /**
416
+ * Prints a scalar subquery operand to TypeScript code
417
+ * @param node - Scalar subquery node
418
+ * @returns TypeScript code representation
419
+ */
420
+ private printScalarSubqueryOperand(node: ScalarSubqueryNode): string {
421
+ const subquery = this.inlineChain(this.buildSelectLines(node.query));
422
+ return `(${subquery})`;
423
+ }
424
+
425
+ /**
426
+ * Prints a CASE expression operand to TypeScript code
427
+ * @param node - CASE expression node
428
+ * @returns TypeScript code representation
429
+ */
430
+ private printCaseExpressionOperand(node: CaseExpressionNode): string {
431
+ const clauses = node.conditions.map(
432
+ condition =>
433
+ `{ when: ${this.printExpression(condition.when)}, then: ${this.printOperand(condition.then)} }`
434
+ );
435
+ const elseValue = node.else ? `, ${this.printOperand(node.else)}` : '';
436
+ return `caseWhen([${clauses.join(', ')}]${elseValue})`;
437
+ }
438
+
439
+ /**
440
+ * Prints a window function operand to TypeScript code
441
+ * @param node - Window function node
442
+ * @returns TypeScript code representation
443
+ */
444
+ private printWindowFunctionOperand(node: WindowFunctionNode): string {
445
+ let result = `${node.name}(`;
446
+ if (node.args.length > 0) {
447
+ result += node.args.map(arg => this.printOperand(arg)).join(', ');
448
+ }
449
+ result += ') OVER (';
450
+
451
+ const parts: string[] = [];
452
+
453
+ if (node.partitionBy && node.partitionBy.length > 0) {
454
+ const partitionClause =
455
+ 'PARTITION BY ' + node.partitionBy.map(col => `${this.namingStrategy.tableToSymbol(col.table)}.${col.name}`).join(', ');
456
+ parts.push(partitionClause);
457
+ }
458
+
459
+ if (node.orderBy && node.orderBy.length > 0) {
460
+ const orderClause =
461
+ 'ORDER BY ' +
462
+ node.orderBy
463
+ .map(o => {
464
+ const term = this.printOrderingTerm(o.term);
465
+ const collation = o.collation ? ` COLLATE ${o.collation}` : '';
466
+ const nulls = o.nulls ? ` NULLS ${o.nulls}` : '';
467
+ return `${term} ${o.direction}${collation}${nulls}`;
468
+ })
469
+ .join(', ');
470
+ parts.push(orderClause);
471
+ }
472
+
473
+ result += parts.join(' ');
474
+ result += ')';
475
+
476
+ return result;
477
+ }
478
+
479
+ private printCastOperand(node: CastExpressionNode): string {
480
+ const typeLiteral = node.castType.replace(/'/g, "\\'");
481
+ return `cast(${this.printOperand(node.expression)}, '${typeLiteral}')`;
482
+ }
483
+
484
+ private printCollateOperand(node: CollateExpressionNode): string {
485
+ return `collate(${this.printOperand(node.expression)}, '${node.collation}')`;
486
+ }
487
+
488
+ /**
489
+ * Converts method chain lines to inline format
490
+ * @param lines - Method chain lines
491
+ * @returns Inline method chain string
492
+ */
493
+ private inlineChain(lines: string[]): string {
494
+ return lines
495
+ .map(line => line.trim())
496
+ .filter(line => line.length > 0)
497
+ .join(' ');
498
+ }
499
+
500
+ /**
501
+ * Maps SQL operators to TypeScript function names
502
+ * @param op - SQL operator
503
+ * @returns TypeScript function name
504
+ */
505
+ private mapOp(op: SqlOperator): string {
506
+ const config = SQL_OPERATOR_REGISTRY[op];
507
+ if (!config) {
508
+ return assertNever(op as never);
509
+ }
510
+ return config.tsName;
511
+ }
512
+ }