metal-orm 1.0.8 → 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 +12 -1
  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
package/dist/index.js ADDED
@@ -0,0 +1,4131 @@
1
+ // src/schema/table.ts
2
+ var defineTable = (name, columns, relations = {}, hooks) => {
3
+ const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
4
+ acc[key] = { ...def, name: key, table: name };
5
+ return acc;
6
+ }, {});
7
+ return { name, columns: colsWithNames, relations, hooks };
8
+ };
9
+
10
+ // src/schema/column.ts
11
+ var col = {
12
+ /**
13
+ * Creates an integer column definition
14
+ * @returns ColumnDef with INT type
15
+ */
16
+ int: () => ({ name: "", type: "INT" }),
17
+ /**
18
+ * Creates a variable character column definition
19
+ * @param length - Maximum length of the string
20
+ * @returns ColumnDef with VARCHAR type
21
+ */
22
+ varchar: (length) => ({ name: "", type: "VARCHAR", args: [length] }),
23
+ /**
24
+ * Creates a JSON column definition
25
+ * @returns ColumnDef with JSON type
26
+ */
27
+ json: () => ({ name: "", type: "JSON" }),
28
+ /**
29
+ * Creates a boolean column definition
30
+ * @returns ColumnDef with BOOLEAN type
31
+ */
32
+ boolean: () => ({ name: "", type: "BOOLEAN" }),
33
+ /**
34
+ * Marks a column definition as a primary key
35
+ * @param def - Column definition to modify
36
+ * @returns Modified ColumnDef with primary: true
37
+ */
38
+ primaryKey: (def) => ({ ...def, primary: true })
39
+ };
40
+
41
+ // src/schema/relation.ts
42
+ var RelationKinds = {
43
+ /** One-to-many relationship */
44
+ HasMany: "HAS_MANY",
45
+ /** Many-to-one relationship */
46
+ BelongsTo: "BELONGS_TO",
47
+ /** Many-to-many relationship with pivot metadata */
48
+ BelongsToMany: "BELONGS_TO_MANY"
49
+ };
50
+ var hasMany = (target, foreignKey, localKey, cascade) => ({
51
+ type: RelationKinds.HasMany,
52
+ target,
53
+ foreignKey,
54
+ localKey,
55
+ cascade
56
+ });
57
+ var belongsTo = (target, foreignKey, localKey, cascade) => ({
58
+ type: RelationKinds.BelongsTo,
59
+ target,
60
+ foreignKey,
61
+ localKey,
62
+ cascade
63
+ });
64
+ var belongsToMany = (target, pivotTable, options) => ({
65
+ type: RelationKinds.BelongsToMany,
66
+ target,
67
+ pivotTable,
68
+ pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
69
+ pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
70
+ localKey: options.localKey,
71
+ targetKey: options.targetKey,
72
+ pivotPrimaryKey: options.pivotPrimaryKey,
73
+ defaultPivotColumns: options.defaultPivotColumns,
74
+ cascade: options.cascade
75
+ });
76
+
77
+ // src/core/ast/expression-nodes.ts
78
+ var operandTypes = /* @__PURE__ */ new Set([
79
+ "Column",
80
+ "Literal",
81
+ "Function",
82
+ "JsonPath",
83
+ "ScalarSubquery",
84
+ "CaseExpression",
85
+ "WindowFunction"
86
+ ]);
87
+ var isOperandNode = (node) => node && operandTypes.has(node.type);
88
+ var isFunctionNode = (node) => node?.type === "Function";
89
+ var isCaseExpressionNode = (node) => node?.type === "CaseExpression";
90
+ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
91
+ var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
92
+
93
+ // src/core/ast/expression-builders.ts
94
+ var valueToOperand = (value) => {
95
+ if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
96
+ return { type: "Literal", value: value === void 0 ? null : value };
97
+ }
98
+ return value;
99
+ };
100
+ var toNode = (col2) => {
101
+ if (isOperandNode(col2)) return col2;
102
+ const def = col2;
103
+ return { type: "Column", table: def.table || "unknown", name: def.name };
104
+ };
105
+ var toLiteralNode = (value) => ({
106
+ type: "Literal",
107
+ value
108
+ });
109
+ var toOperand = (val) => {
110
+ if (val === null) return { type: "Literal", value: null };
111
+ if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
112
+ return { type: "Literal", value: val };
113
+ }
114
+ return toNode(val);
115
+ };
116
+ var columnOperand = (col2) => toNode(col2);
117
+ var createBinaryExpression = (operator, left, right, escape) => {
118
+ const node = {
119
+ type: "BinaryExpression",
120
+ left: toNode(left),
121
+ operator,
122
+ right: toOperand(right)
123
+ };
124
+ if (escape !== void 0) {
125
+ node.escape = toLiteralNode(escape);
126
+ }
127
+ return node;
128
+ };
129
+ var eq = (left, right) => createBinaryExpression("=", left, right);
130
+ var neq = (left, right) => createBinaryExpression("!=", left, right);
131
+ var gt = (left, right) => createBinaryExpression(">", left, right);
132
+ var gte = (left, right) => createBinaryExpression(">=", left, right);
133
+ var lt = (left, right) => createBinaryExpression("<", left, right);
134
+ var lte = (left, right) => createBinaryExpression("<=", left, right);
135
+ var like = (left, pattern, escape) => createBinaryExpression("LIKE", left, pattern, escape);
136
+ var notLike = (left, pattern, escape) => createBinaryExpression("NOT LIKE", left, pattern, escape);
137
+ var and = (...operands) => ({
138
+ type: "LogicalExpression",
139
+ operator: "AND",
140
+ operands
141
+ });
142
+ var or = (...operands) => ({
143
+ type: "LogicalExpression",
144
+ operator: "OR",
145
+ operands
146
+ });
147
+ var isNull = (left) => ({
148
+ type: "NullExpression",
149
+ left: toNode(left),
150
+ operator: "IS NULL"
151
+ });
152
+ var isNotNull = (left) => ({
153
+ type: "NullExpression",
154
+ left: toNode(left),
155
+ operator: "IS NOT NULL"
156
+ });
157
+ var createInExpression = (operator, left, values) => ({
158
+ type: "InExpression",
159
+ left: toNode(left),
160
+ operator,
161
+ right: values.map((v) => toOperand(v))
162
+ });
163
+ var inList = (left, values) => createInExpression("IN", left, values);
164
+ var notInList = (left, values) => createInExpression("NOT IN", left, values);
165
+ var createBetweenExpression = (operator, left, lower, upper) => ({
166
+ type: "BetweenExpression",
167
+ left: toNode(left),
168
+ operator,
169
+ lower: toOperand(lower),
170
+ upper: toOperand(upper)
171
+ });
172
+ var between = (left, lower, upper) => createBetweenExpression("BETWEEN", left, lower, upper);
173
+ var notBetween = (left, lower, upper) => createBetweenExpression("NOT BETWEEN", left, lower, upper);
174
+ var jsonPath = (col2, path) => ({
175
+ type: "JsonPath",
176
+ column: columnOperand(col2),
177
+ path
178
+ });
179
+ var caseWhen = (conditions, elseValue) => ({
180
+ type: "CaseExpression",
181
+ conditions: conditions.map((c) => ({
182
+ when: c.when,
183
+ then: toOperand(c.then)
184
+ })),
185
+ else: elseValue !== void 0 ? toOperand(elseValue) : void 0
186
+ });
187
+ var exists = (subquery) => ({
188
+ type: "ExistsExpression",
189
+ operator: "EXISTS",
190
+ subquery
191
+ });
192
+ var notExists = (subquery) => ({
193
+ type: "ExistsExpression",
194
+ operator: "NOT EXISTS",
195
+ subquery
196
+ });
197
+
198
+ // src/core/ast/window-functions.ts
199
+ var buildWindowFunction = (name, args = [], partitionBy, orderBy) => {
200
+ const node = {
201
+ type: "WindowFunction",
202
+ name,
203
+ args
204
+ };
205
+ if (partitionBy && partitionBy.length) {
206
+ node.partitionBy = partitionBy;
207
+ }
208
+ if (orderBy && orderBy.length) {
209
+ node.orderBy = orderBy;
210
+ }
211
+ return node;
212
+ };
213
+ var rowNumber = () => buildWindowFunction("ROW_NUMBER");
214
+ var rank = () => buildWindowFunction("RANK");
215
+ var denseRank = () => buildWindowFunction("DENSE_RANK");
216
+ var ntile = (n) => buildWindowFunction("NTILE", [{ type: "Literal", value: n }]);
217
+ var lag = (col2, offset = 1, defaultValue) => {
218
+ const args = [
219
+ columnOperand(col2),
220
+ { type: "Literal", value: offset }
221
+ ];
222
+ if (defaultValue !== void 0) {
223
+ args.push({ type: "Literal", value: defaultValue });
224
+ }
225
+ return buildWindowFunction("LAG", args);
226
+ };
227
+ var lead = (col2, offset = 1, defaultValue) => {
228
+ const args = [
229
+ columnOperand(col2),
230
+ { type: "Literal", value: offset }
231
+ ];
232
+ if (defaultValue !== void 0) {
233
+ args.push({ type: "Literal", value: defaultValue });
234
+ }
235
+ return buildWindowFunction("LEAD", args);
236
+ };
237
+ var firstValue = (col2) => buildWindowFunction("FIRST_VALUE", [columnOperand(col2)]);
238
+ var lastValue = (col2) => buildWindowFunction("LAST_VALUE", [columnOperand(col2)]);
239
+ var windowFunction = (name, args = [], partitionBy, orderBy) => {
240
+ const nodeArgs = args.map((arg) => {
241
+ if (typeof arg.value !== "undefined") {
242
+ return arg;
243
+ }
244
+ if ("path" in arg) {
245
+ return arg;
246
+ }
247
+ return columnOperand(arg);
248
+ });
249
+ const partitionNodes = partitionBy?.map((col2) => columnOperand(col2)) ?? void 0;
250
+ const orderNodes = orderBy?.map((o) => ({
251
+ type: "OrderBy",
252
+ column: columnOperand(o.column),
253
+ direction: o.direction
254
+ }));
255
+ return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
256
+ };
257
+
258
+ // src/core/ast/aggregate-functions.ts
259
+ var buildAggregate = (name) => (col2) => ({
260
+ type: "Function",
261
+ name,
262
+ args: [columnOperand(col2)]
263
+ });
264
+ var count = buildAggregate("COUNT");
265
+ var sum = buildAggregate("SUM");
266
+ var avg = buildAggregate("AVG");
267
+
268
+ // src/core/ast/expression-visitor.ts
269
+ var unsupportedExpression = (node) => {
270
+ throw new Error(`Unsupported expression type "${node?.type ?? "unknown"}"`);
271
+ };
272
+ var unsupportedOperand = (node) => {
273
+ throw new Error(`Unsupported operand type "${node?.type ?? "unknown"}"`);
274
+ };
275
+ var visitExpression = (node, visitor) => {
276
+ switch (node.type) {
277
+ case "BinaryExpression":
278
+ return visitor.visitBinaryExpression(node);
279
+ case "LogicalExpression":
280
+ return visitor.visitLogicalExpression(node);
281
+ case "NullExpression":
282
+ return visitor.visitNullExpression(node);
283
+ case "InExpression":
284
+ return visitor.visitInExpression(node);
285
+ case "ExistsExpression":
286
+ return visitor.visitExistsExpression(node);
287
+ case "BetweenExpression":
288
+ return visitor.visitBetweenExpression(node);
289
+ default:
290
+ return unsupportedExpression(node);
291
+ }
292
+ };
293
+ var visitOperand = (node, visitor) => {
294
+ switch (node.type) {
295
+ case "Column":
296
+ return visitor.visitColumn(node);
297
+ case "Literal":
298
+ return visitor.visitLiteral(node);
299
+ case "Function":
300
+ return visitor.visitFunction(node);
301
+ case "JsonPath":
302
+ return visitor.visitJsonPath(node);
303
+ case "ScalarSubquery":
304
+ return visitor.visitScalarSubquery(node);
305
+ case "CaseExpression":
306
+ return visitor.visitCaseExpression(node);
307
+ case "WindowFunction":
308
+ return visitor.visitWindowFunction(node);
309
+ default:
310
+ return unsupportedOperand(node);
311
+ }
312
+ };
313
+
314
+ // src/query-builder/select-query-state.ts
315
+ var SelectQueryState = class _SelectQueryState {
316
+ /**
317
+ * Creates a new SelectQueryState instance
318
+ * @param table - Table definition
319
+ * @param ast - Optional existing AST
320
+ */
321
+ constructor(table, ast) {
322
+ this.table = table;
323
+ this.ast = ast ?? {
324
+ type: "SelectQuery",
325
+ from: { type: "Table", name: table.name },
326
+ columns: [],
327
+ joins: []
328
+ };
329
+ }
330
+ /**
331
+ * Creates a new SelectQueryState with updated AST
332
+ * @param nextAst - Updated AST
333
+ * @returns New SelectQueryState instance
334
+ */
335
+ clone(nextAst) {
336
+ return new _SelectQueryState(this.table, nextAst);
337
+ }
338
+ /**
339
+ * Adds columns to the query
340
+ * @param newCols - Columns to add
341
+ * @returns New SelectQueryState with added columns
342
+ */
343
+ withColumns(newCols) {
344
+ return this.clone({
345
+ ...this.ast,
346
+ columns: [...this.ast.columns ?? [], ...newCols]
347
+ });
348
+ }
349
+ /**
350
+ * Adds a join to the query
351
+ * @param join - Join node to add
352
+ * @returns New SelectQueryState with added join
353
+ */
354
+ withJoin(join) {
355
+ return this.clone({
356
+ ...this.ast,
357
+ joins: [...this.ast.joins ?? [], join]
358
+ });
359
+ }
360
+ /**
361
+ * Adds a WHERE clause to the query
362
+ * @param predicate - WHERE predicate expression
363
+ * @returns New SelectQueryState with WHERE clause
364
+ */
365
+ withWhere(predicate) {
366
+ return this.clone({
367
+ ...this.ast,
368
+ where: predicate
369
+ });
370
+ }
371
+ /**
372
+ * Adds a HAVING clause to the query
373
+ * @param predicate - HAVING predicate expression
374
+ * @returns New SelectQueryState with HAVING clause
375
+ */
376
+ withHaving(predicate) {
377
+ return this.clone({
378
+ ...this.ast,
379
+ having: predicate
380
+ });
381
+ }
382
+ /**
383
+ * Adds GROUP BY columns to the query
384
+ * @param columns - Columns to group by
385
+ * @returns New SelectQueryState with GROUP BY clause
386
+ */
387
+ withGroupBy(columns) {
388
+ return this.clone({
389
+ ...this.ast,
390
+ groupBy: [...this.ast.groupBy ?? [], ...columns]
391
+ });
392
+ }
393
+ /**
394
+ * Adds ORDER BY clauses to the query
395
+ * @param orderBy - ORDER BY nodes
396
+ * @returns New SelectQueryState with ORDER BY clause
397
+ */
398
+ withOrderBy(orderBy) {
399
+ return this.clone({
400
+ ...this.ast,
401
+ orderBy: [...this.ast.orderBy ?? [], ...orderBy]
402
+ });
403
+ }
404
+ /**
405
+ * Adds DISTINCT columns to the query
406
+ * @param columns - Columns to make distinct
407
+ * @returns New SelectQueryState with DISTINCT clause
408
+ */
409
+ withDistinct(columns) {
410
+ return this.clone({
411
+ ...this.ast,
412
+ distinct: [...this.ast.distinct ?? [], ...columns]
413
+ });
414
+ }
415
+ /**
416
+ * Adds a LIMIT clause to the query
417
+ * @param limit - Maximum number of rows to return
418
+ * @returns New SelectQueryState with LIMIT clause
419
+ */
420
+ withLimit(limit) {
421
+ return this.clone({
422
+ ...this.ast,
423
+ limit
424
+ });
425
+ }
426
+ /**
427
+ * Adds an OFFSET clause to the query
428
+ * @param offset - Number of rows to skip
429
+ * @returns New SelectQueryState with OFFSET clause
430
+ */
431
+ withOffset(offset) {
432
+ return this.clone({
433
+ ...this.ast,
434
+ offset
435
+ });
436
+ }
437
+ /**
438
+ * Adds a Common Table Expression (CTE) to the query
439
+ * @param cte - CTE node to add
440
+ * @returns New SelectQueryState with CTE
441
+ */
442
+ withCte(cte) {
443
+ return this.clone({
444
+ ...this.ast,
445
+ ctes: [...this.ast.ctes ?? [], cte]
446
+ });
447
+ }
448
+ };
449
+
450
+ // src/query-builder/hydration-manager.ts
451
+ var HydrationManager = class _HydrationManager {
452
+ /**
453
+ * Creates a new HydrationManager instance
454
+ * @param table - Table definition
455
+ * @param planner - Hydration planner
456
+ */
457
+ constructor(table, planner) {
458
+ this.table = table;
459
+ this.planner = planner;
460
+ }
461
+ /**
462
+ * Creates a new HydrationManager with updated planner
463
+ * @param nextPlanner - Updated hydration planner
464
+ * @returns New HydrationManager instance
465
+ */
466
+ clone(nextPlanner) {
467
+ return new _HydrationManager(this.table, nextPlanner);
468
+ }
469
+ /**
470
+ * Handles column selection for hydration planning
471
+ * @param state - Current query state
472
+ * @param newColumns - Newly selected columns
473
+ * @returns Updated HydrationManager with captured columns
474
+ */
475
+ onColumnsSelected(state, newColumns) {
476
+ const updated = this.planner.captureRootColumns(newColumns);
477
+ return this.clone(updated);
478
+ }
479
+ /**
480
+ * Handles relation inclusion for hydration planning
481
+ * @param state - Current query state
482
+ * @param relation - Relation definition
483
+ * @param relationName - Name of the relation
484
+ * @param aliasPrefix - Alias prefix for the relation
485
+ * @param targetColumns - Target columns to include
486
+ * @returns Updated HydrationManager with included relation
487
+ */
488
+ onRelationIncluded(state, relation, relationName, aliasPrefix, targetColumns, pivot) {
489
+ const withRoots = this.planner.captureRootColumns(state.ast.columns);
490
+ const next = withRoots.includeRelation(relation, relationName, aliasPrefix, targetColumns, pivot);
491
+ return this.clone(next);
492
+ }
493
+ /**
494
+ * Applies hydration plan to the AST
495
+ * @param ast - Query AST to modify
496
+ * @returns AST with hydration metadata
497
+ */
498
+ applyToAst(ast) {
499
+ const plan = this.planner.getPlan();
500
+ if (!plan) return ast;
501
+ return {
502
+ ...ast,
503
+ meta: {
504
+ ...ast.meta || {},
505
+ hydration: plan
506
+ }
507
+ };
508
+ }
509
+ /**
510
+ * Gets the current hydration plan
511
+ * @returns Hydration plan or undefined if none exists
512
+ */
513
+ getPlan() {
514
+ return this.planner.getPlan();
515
+ }
516
+ };
517
+
518
+ // src/query-builder/relation-alias.ts
519
+ var RELATION_SEPARATOR = "__";
520
+ var makeRelationAlias = (relationName, columnName) => `${relationName}${RELATION_SEPARATOR}${columnName}`;
521
+ var isRelationAlias = (alias) => !!alias && alias.includes(RELATION_SEPARATOR);
522
+
523
+ // src/query-builder/relation-utils.ts
524
+ var buildDefaultPivotColumns = (rel, pivotPk) => {
525
+ const excluded = /* @__PURE__ */ new Set([pivotPk, rel.pivotForeignKeyToRoot, rel.pivotForeignKeyToTarget]);
526
+ return Object.keys(rel.pivotTable.columns).filter((col2) => !excluded.has(col2));
527
+ };
528
+
529
+ // src/query-builder/hydration-planner.ts
530
+ var findPrimaryKey = (table) => {
531
+ const pk = Object.values(table.columns).find((c) => c.primary);
532
+ return pk?.name || "id";
533
+ };
534
+ var HydrationPlanner = class _HydrationPlanner {
535
+ /**
536
+ * Creates a new HydrationPlanner instance
537
+ * @param table - Table definition
538
+ * @param plan - Optional existing hydration plan
539
+ */
540
+ constructor(table, plan) {
541
+ this.table = table;
542
+ this.plan = plan;
543
+ }
544
+ /**
545
+ * Captures root table columns for hydration planning
546
+ * @param columns - Columns to capture
547
+ * @returns Updated HydrationPlanner with captured columns
548
+ */
549
+ captureRootColumns(columns) {
550
+ const currentPlan = this.getPlanOrDefault();
551
+ const rootCols = new Set(currentPlan.rootColumns);
552
+ let changed = false;
553
+ columns.forEach((node) => {
554
+ if (node.type !== "Column") return;
555
+ if (node.table !== this.table.name) return;
556
+ const alias = node.alias || node.name;
557
+ if (isRelationAlias(alias)) return;
558
+ if (!rootCols.has(alias)) {
559
+ rootCols.add(alias);
560
+ changed = true;
561
+ }
562
+ });
563
+ if (!changed) return this;
564
+ return new _HydrationPlanner(this.table, {
565
+ ...currentPlan,
566
+ rootColumns: Array.from(rootCols)
567
+ });
568
+ }
569
+ /**
570
+ * Includes a relation in the hydration plan
571
+ * @param rel - Relation definition
572
+ * @param relationName - Name of the relation
573
+ * @param aliasPrefix - Alias prefix for relation columns
574
+ * @param columns - Columns to include from the relation
575
+ * @returns Updated HydrationPlanner with included relation
576
+ */
577
+ includeRelation(rel, relationName, aliasPrefix, columns, pivot) {
578
+ const currentPlan = this.getPlanOrDefault();
579
+ const relations = currentPlan.relations.filter((r) => r.name !== relationName);
580
+ relations.push(this.buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot));
581
+ return new _HydrationPlanner(this.table, {
582
+ ...currentPlan,
583
+ relations
584
+ });
585
+ }
586
+ /**
587
+ * Gets the current hydration plan
588
+ * @returns Current hydration plan or undefined
589
+ */
590
+ getPlan() {
591
+ return this.plan;
592
+ }
593
+ /**
594
+ * Gets the current hydration plan or creates a default one
595
+ * @returns Current hydration plan or default plan
596
+ */
597
+ getPlanOrDefault() {
598
+ return this.plan ?? buildDefaultHydrationPlan(this.table);
599
+ }
600
+ /**
601
+ * Builds a relation plan for hydration
602
+ * @param rel - Relation definition
603
+ * @param relationName - Name of the relation
604
+ * @param aliasPrefix - Alias prefix for relation columns
605
+ * @param columns - Columns to include from the relation
606
+ * @returns Hydration relation plan
607
+ */
608
+ buildRelationPlan(rel, relationName, aliasPrefix, columns, pivot) {
609
+ switch (rel.type) {
610
+ case RelationKinds.HasMany: {
611
+ const localKey = rel.localKey || findPrimaryKey(this.table);
612
+ return {
613
+ name: relationName,
614
+ aliasPrefix,
615
+ type: rel.type,
616
+ targetTable: rel.target.name,
617
+ targetPrimaryKey: findPrimaryKey(rel.target),
618
+ foreignKey: rel.foreignKey,
619
+ localKey,
620
+ columns
621
+ };
622
+ }
623
+ case RelationKinds.BelongsTo: {
624
+ const localKey = rel.localKey || findPrimaryKey(rel.target);
625
+ return {
626
+ name: relationName,
627
+ aliasPrefix,
628
+ type: rel.type,
629
+ targetTable: rel.target.name,
630
+ targetPrimaryKey: findPrimaryKey(rel.target),
631
+ foreignKey: rel.foreignKey,
632
+ localKey,
633
+ columns
634
+ };
635
+ }
636
+ case RelationKinds.BelongsToMany: {
637
+ const many = rel;
638
+ const localKey = many.localKey || findPrimaryKey(this.table);
639
+ const targetPk = many.targetKey || findPrimaryKey(many.target);
640
+ const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
641
+ const pivotAliasPrefix = pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
642
+ const pivotColumns = pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
643
+ return {
644
+ name: relationName,
645
+ aliasPrefix,
646
+ type: rel.type,
647
+ targetTable: many.target.name,
648
+ targetPrimaryKey: targetPk,
649
+ foreignKey: many.pivotForeignKeyToRoot,
650
+ localKey,
651
+ columns,
652
+ pivot: {
653
+ table: many.pivotTable.name,
654
+ primaryKey: pivotPk,
655
+ aliasPrefix: pivotAliasPrefix,
656
+ columns: pivotColumns
657
+ }
658
+ };
659
+ }
660
+ }
661
+ }
662
+ };
663
+ var buildDefaultHydrationPlan = (table) => ({
664
+ rootTable: table.name,
665
+ rootPrimaryKey: findPrimaryKey(table),
666
+ rootColumns: [],
667
+ relations: []
668
+ });
669
+
670
+ // src/core/ast/builders.ts
671
+ var buildColumnNode = (table, column) => {
672
+ if (column.type === "Column") {
673
+ return column;
674
+ }
675
+ const def = column;
676
+ return {
677
+ type: "Column",
678
+ table: def.table || table.name,
679
+ name: def.name
680
+ };
681
+ };
682
+ var buildColumnNodes = (table, names) => names.map((name) => ({
683
+ type: "Column",
684
+ table: table.name,
685
+ name
686
+ }));
687
+ var createTableNode = (table) => ({
688
+ type: "Table",
689
+ name: table.name
690
+ });
691
+
692
+ // src/query-builder/raw-column-parser.ts
693
+ var parseRawColumn = (col2, tableName, ctes) => {
694
+ if (col2.includes("(")) {
695
+ const [fn, rest] = col2.split("(");
696
+ const colName = rest.replace(")", "");
697
+ const [table, name] = colName.includes(".") ? colName.split(".") : [tableName, colName];
698
+ return { type: "Column", table, name, alias: col2 };
699
+ }
700
+ if (col2.includes(".")) {
701
+ const [potentialCteName, columnName] = col2.split(".");
702
+ const hasCte = ctes?.some((cte) => cte.name === potentialCteName);
703
+ if (hasCte) {
704
+ return { type: "Column", table: tableName, name: col2 };
705
+ }
706
+ return { type: "Column", table: potentialCteName, name: columnName };
707
+ }
708
+ return { type: "Column", table: tableName, name: col2 };
709
+ };
710
+
711
+ // src/query-builder/query-ast-service.ts
712
+ var QueryAstService = class {
713
+ /**
714
+ * Creates a new QueryAstService instance
715
+ * @param table - Table definition
716
+ * @param state - Current query state
717
+ */
718
+ constructor(table, state) {
719
+ this.table = table;
720
+ this.state = state;
721
+ }
722
+ /**
723
+ * Selects columns for the query
724
+ * @param columns - Columns to select (key: alias, value: column definition or expression)
725
+ * @returns Column selection result with updated state and added columns
726
+ */
727
+ select(columns) {
728
+ const existingAliases = new Set(
729
+ this.state.ast.columns.map((c) => c.alias || c.name)
730
+ );
731
+ const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
732
+ if (existingAliases.has(alias)) return acc;
733
+ if (isExpressionSelectionNode(val)) {
734
+ acc.push({ ...val, alias });
735
+ return acc;
736
+ }
737
+ const colDef = val;
738
+ acc.push({
739
+ type: "Column",
740
+ table: colDef.table || this.table.name,
741
+ name: colDef.name,
742
+ alias
743
+ });
744
+ return acc;
745
+ }, []);
746
+ const nextState = this.state.withColumns(newCols);
747
+ return { state: nextState, addedColumns: newCols };
748
+ }
749
+ /**
750
+ * Selects raw column expressions (best-effort parser for simple references/functions)
751
+ * @param cols - Raw column expressions
752
+ * @returns Column selection result with updated state and added columns
753
+ */
754
+ selectRaw(cols) {
755
+ const newCols = cols.map((col2) => parseRawColumn(col2, this.table.name, this.state.ast.ctes));
756
+ const nextState = this.state.withColumns(newCols);
757
+ return { state: nextState, addedColumns: newCols };
758
+ }
759
+ /**
760
+ * Adds a Common Table Expression (CTE) to the query
761
+ * @param name - Name of the CTE
762
+ * @param query - Query for the CTE
763
+ * @param columns - Optional column names for the CTE
764
+ * @param recursive - Whether the CTE is recursive
765
+ * @returns Updated query state with CTE
766
+ */
767
+ withCte(name, query, columns, recursive = false) {
768
+ const cte = {
769
+ type: "CommonTableExpression",
770
+ name,
771
+ query,
772
+ columns,
773
+ recursive
774
+ };
775
+ return this.state.withCte(cte);
776
+ }
777
+ /**
778
+ * Selects a subquery as a column
779
+ * @param alias - Alias for the subquery
780
+ * @param query - Subquery to select
781
+ * @returns Updated query state with subquery selection
782
+ */
783
+ selectSubquery(alias, query) {
784
+ const node = { type: "ScalarSubquery", query, alias };
785
+ return this.state.withColumns([node]);
786
+ }
787
+ /**
788
+ * Adds a JOIN clause to the query
789
+ * @param join - Join node to add
790
+ * @returns Updated query state with JOIN
791
+ */
792
+ withJoin(join) {
793
+ return this.state.withJoin(join);
794
+ }
795
+ /**
796
+ * Adds a WHERE clause to the query
797
+ * @param expr - Expression for the WHERE clause
798
+ * @returns Updated query state with WHERE clause
799
+ */
800
+ withWhere(expr) {
801
+ const combined = this.combineExpressions(this.state.ast.where, expr);
802
+ return this.state.withWhere(combined);
803
+ }
804
+ /**
805
+ * Adds a GROUP BY clause to the query
806
+ * @param col - Column to group by
807
+ * @returns Updated query state with GROUP BY clause
808
+ */
809
+ withGroupBy(col2) {
810
+ const node = buildColumnNode(this.table, col2);
811
+ return this.state.withGroupBy([node]);
812
+ }
813
+ /**
814
+ * Adds a HAVING clause to the query
815
+ * @param expr - Expression for the HAVING clause
816
+ * @returns Updated query state with HAVING clause
817
+ */
818
+ withHaving(expr) {
819
+ const combined = this.combineExpressions(this.state.ast.having, expr);
820
+ return this.state.withHaving(combined);
821
+ }
822
+ /**
823
+ * Adds an ORDER BY clause to the query
824
+ * @param col - Column to order by
825
+ * @param direction - Order direction (ASC/DESC)
826
+ * @returns Updated query state with ORDER BY clause
827
+ */
828
+ withOrderBy(col2, direction) {
829
+ const node = buildColumnNode(this.table, col2);
830
+ return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
831
+ }
832
+ /**
833
+ * Adds a DISTINCT clause to the query
834
+ * @param cols - Columns to make distinct
835
+ * @returns Updated query state with DISTINCT clause
836
+ */
837
+ withDistinct(cols) {
838
+ return this.state.withDistinct(cols);
839
+ }
840
+ /**
841
+ * Adds a LIMIT clause to the query
842
+ * @param limit - Maximum number of rows to return
843
+ * @returns Updated query state with LIMIT clause
844
+ */
845
+ withLimit(limit) {
846
+ return this.state.withLimit(limit);
847
+ }
848
+ /**
849
+ * Adds an OFFSET clause to the query
850
+ * @param offset - Number of rows to skip
851
+ * @returns Updated query state with OFFSET clause
852
+ */
853
+ withOffset(offset) {
854
+ return this.state.withOffset(offset);
855
+ }
856
+ /**
857
+ * Combines expressions with AND operator
858
+ * @param existing - Existing expression
859
+ * @param next - New expression to combine
860
+ * @returns Combined expression
861
+ */
862
+ combineExpressions(existing, next) {
863
+ return existing ? and(existing, next) : next;
864
+ }
865
+ };
866
+
867
+ // src/query-builder/relation-projection-helper.ts
868
+ var RelationProjectionHelper = class {
869
+ /**
870
+ * Creates a new RelationProjectionHelper instance
871
+ * @param table - Table definition
872
+ * @param selectColumns - Callback for selecting columns
873
+ */
874
+ constructor(table, selectColumns) {
875
+ this.table = table;
876
+ this.selectColumns = selectColumns;
877
+ }
878
+ /**
879
+ * Ensures base projection is included in the query
880
+ * @param state - Current query state
881
+ * @param hydration - Hydration manager
882
+ * @returns Relation result with updated state and hydration
883
+ */
884
+ ensureBaseProjection(state, hydration) {
885
+ const primaryKey = findPrimaryKey(this.table);
886
+ if (!this.hasBaseProjection(state)) {
887
+ return this.selectColumns(state, hydration, this.getBaseColumns());
888
+ }
889
+ if (primaryKey && !this.hasPrimarySelected(state, primaryKey) && this.table.columns[primaryKey]) {
890
+ return this.selectColumns(state, hydration, {
891
+ [primaryKey]: this.table.columns[primaryKey]
892
+ });
893
+ }
894
+ return { state, hydration };
895
+ }
896
+ /**
897
+ * Checks if base projection exists in the query
898
+ * @param state - Current query state
899
+ * @returns True if base projection exists
900
+ */
901
+ hasBaseProjection(state) {
902
+ return state.ast.columns.some((col2) => !isRelationAlias(col2.alias));
903
+ }
904
+ /**
905
+ * Checks if primary key is selected in the query
906
+ * @param state - Current query state
907
+ * @param primaryKey - Primary key name
908
+ * @returns True if primary key is selected
909
+ */
910
+ hasPrimarySelected(state, primaryKey) {
911
+ return state.ast.columns.some((col2) => {
912
+ const alias = col2.alias;
913
+ const name = alias || col2.name;
914
+ return !isRelationAlias(alias) && name === primaryKey;
915
+ });
916
+ }
917
+ /**
918
+ * Gets all base columns for the table
919
+ * @returns Record of all table columns
920
+ */
921
+ getBaseColumns() {
922
+ return Object.keys(this.table.columns).reduce((acc, key) => {
923
+ acc[key] = this.table.columns[key];
924
+ return acc;
925
+ }, {});
926
+ }
927
+ };
928
+
929
+ // src/core/ast/join-node.ts
930
+ var createJoinNode = (kind, tableName, condition, relationName) => ({
931
+ type: "Join",
932
+ kind,
933
+ table: { type: "Table", name: tableName },
934
+ condition,
935
+ relationName
936
+ });
937
+
938
+ // src/query-builder/relation-conditions.ts
939
+ var assertNever = (value) => {
940
+ throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
941
+ };
942
+ var baseRelationCondition = (root, relation) => {
943
+ const defaultLocalKey = relation.type === RelationKinds.HasMany ? findPrimaryKey(root) : findPrimaryKey(relation.target);
944
+ const localKey = relation.localKey || defaultLocalKey;
945
+ switch (relation.type) {
946
+ case RelationKinds.HasMany:
947
+ return eq(
948
+ { type: "Column", table: relation.target.name, name: relation.foreignKey },
949
+ { type: "Column", table: root.name, name: localKey }
950
+ );
951
+ case RelationKinds.BelongsTo:
952
+ return eq(
953
+ { type: "Column", table: relation.target.name, name: localKey },
954
+ { type: "Column", table: root.name, name: relation.foreignKey }
955
+ );
956
+ case RelationKinds.BelongsToMany:
957
+ throw new Error("BelongsToMany relations do not support the standard join condition builder");
958
+ default:
959
+ return assertNever(relation);
960
+ }
961
+ };
962
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
963
+ const rootKey = relation.localKey || findPrimaryKey(root);
964
+ const targetKey = relation.targetKey || findPrimaryKey(relation.target);
965
+ const pivotCondition = eq(
966
+ { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
967
+ { type: "Column", table: root.name, name: rootKey }
968
+ );
969
+ const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
970
+ let targetCondition = eq(
971
+ { type: "Column", table: relation.target.name, name: targetKey },
972
+ { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
973
+ );
974
+ if (extra) {
975
+ targetCondition = and(targetCondition, extra);
976
+ }
977
+ const targetJoin = createJoinNode(
978
+ joinKind,
979
+ relation.target.name,
980
+ targetCondition,
981
+ relationName
982
+ );
983
+ return [pivotJoin, targetJoin];
984
+ };
985
+ var buildRelationJoinCondition = (root, relation, extra) => {
986
+ const base = baseRelationCondition(root, relation);
987
+ return extra ? and(base, extra) : base;
988
+ };
989
+ var buildRelationCorrelation = (root, relation) => {
990
+ return baseRelationCondition(root, relation);
991
+ };
992
+
993
+ // src/core/sql/sql.ts
994
+ var SQL_OPERATORS = {
995
+ /** Equality operator */
996
+ EQUALS: "=",
997
+ /** Not equals operator */
998
+ NOT_EQUALS: "!=",
999
+ /** Greater than operator */
1000
+ GREATER_THAN: ">",
1001
+ /** Greater than or equal operator */
1002
+ GREATER_OR_EQUAL: ">=",
1003
+ /** Less than operator */
1004
+ LESS_THAN: "<",
1005
+ /** Less than or equal operator */
1006
+ LESS_OR_EQUAL: "<=",
1007
+ /** LIKE pattern matching operator */
1008
+ LIKE: "LIKE",
1009
+ /** NOT LIKE pattern matching operator */
1010
+ NOT_LIKE: "NOT LIKE",
1011
+ /** IN membership operator */
1012
+ IN: "IN",
1013
+ /** NOT IN membership operator */
1014
+ NOT_IN: "NOT IN",
1015
+ /** BETWEEN range operator */
1016
+ BETWEEN: "BETWEEN",
1017
+ /** NOT BETWEEN range operator */
1018
+ NOT_BETWEEN: "NOT BETWEEN",
1019
+ /** IS NULL null check operator */
1020
+ IS_NULL: "IS NULL",
1021
+ /** IS NOT NULL null check operator */
1022
+ IS_NOT_NULL: "IS NOT NULL",
1023
+ /** Logical AND operator */
1024
+ AND: "AND",
1025
+ /** Logical OR operator */
1026
+ OR: "OR",
1027
+ /** EXISTS operator */
1028
+ EXISTS: "EXISTS",
1029
+ /** NOT EXISTS operator */
1030
+ NOT_EXISTS: "NOT EXISTS"
1031
+ };
1032
+ var JOIN_KINDS = {
1033
+ /** INNER JOIN type */
1034
+ INNER: "INNER",
1035
+ /** LEFT JOIN type */
1036
+ LEFT: "LEFT",
1037
+ /** RIGHT JOIN type */
1038
+ RIGHT: "RIGHT",
1039
+ /** CROSS JOIN type */
1040
+ CROSS: "CROSS"
1041
+ };
1042
+ var ORDER_DIRECTIONS = {
1043
+ /** Ascending order */
1044
+ ASC: "ASC",
1045
+ /** Descending order */
1046
+ DESC: "DESC"
1047
+ };
1048
+
1049
+ // src/query-builder/relation-service.ts
1050
+ var RelationService = class {
1051
+ /**
1052
+ * Creates a new RelationService instance
1053
+ * @param table - Table definition
1054
+ * @param state - Current query state
1055
+ * @param hydration - Hydration manager
1056
+ */
1057
+ constructor(table, state, hydration, createQueryAstService) {
1058
+ this.table = table;
1059
+ this.state = state;
1060
+ this.hydration = hydration;
1061
+ this.createQueryAstService = createQueryAstService;
1062
+ this.projectionHelper = new RelationProjectionHelper(
1063
+ table,
1064
+ (state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
1065
+ );
1066
+ }
1067
+ /**
1068
+ * Joins a relation to the query
1069
+ * @param relationName - Name of the relation to join
1070
+ * @param joinKind - Type of join to use
1071
+ * @param extraCondition - Additional join condition
1072
+ * @returns Relation result with updated state and hydration
1073
+ */
1074
+ joinRelation(relationName, joinKind, extraCondition) {
1075
+ const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
1076
+ return { state: nextState, hydration: this.hydration };
1077
+ }
1078
+ /**
1079
+ * Matches records based on a relation with an optional predicate
1080
+ * @param relationName - Name of the relation to match
1081
+ * @param predicate - Optional predicate expression
1082
+ * @returns Relation result with updated state and hydration
1083
+ */
1084
+ match(relationName, predicate) {
1085
+ const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
1086
+ const pk = findPrimaryKey(this.table);
1087
+ const distinctCols = [{ type: "Column", table: this.table.name, name: pk }];
1088
+ const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
1089
+ const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
1090
+ return { state: nextState, hydration: joined.hydration };
1091
+ }
1092
+ /**
1093
+ * Includes a relation in the query result
1094
+ * @param relationName - Name of the relation to include
1095
+ * @param options - Options for relation inclusion
1096
+ * @returns Relation result with updated state and hydration
1097
+ */
1098
+ include(relationName, options) {
1099
+ let state = this.state;
1100
+ let hydration = this.hydration;
1101
+ const relation = this.getRelation(relationName);
1102
+ const aliasPrefix = options?.aliasPrefix ?? relationName;
1103
+ const alreadyJoined = state.ast.joins.some((j) => j.relationName === relationName);
1104
+ if (!alreadyJoined) {
1105
+ const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
1106
+ state = joined.state;
1107
+ }
1108
+ const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
1109
+ state = projectionResult.state;
1110
+ hydration = projectionResult.hydration;
1111
+ const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
1112
+ const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
1113
+ return keys.reduce((acc, key) => {
1114
+ const def = columns[key];
1115
+ if (!def) {
1116
+ throw new Error(missingMsg(key));
1117
+ }
1118
+ acc[makeRelationAlias(prefix, key)] = def;
1119
+ return acc;
1120
+ }, {});
1121
+ };
1122
+ const targetSelection = buildTypedSelection(
1123
+ relation.target.columns,
1124
+ aliasPrefix,
1125
+ targetColumns,
1126
+ (key) => `Column '${key}' not found on relation '${relationName}'`
1127
+ );
1128
+ if (relation.type !== RelationKinds.BelongsToMany) {
1129
+ const relationSelectionResult2 = this.selectColumns(state, hydration, targetSelection);
1130
+ state = relationSelectionResult2.state;
1131
+ hydration = relationSelectionResult2.hydration;
1132
+ hydration = hydration.onRelationIncluded(
1133
+ state,
1134
+ relation,
1135
+ relationName,
1136
+ aliasPrefix,
1137
+ targetColumns
1138
+ );
1139
+ return { state, hydration };
1140
+ }
1141
+ const many = relation;
1142
+ const pivotAliasPrefix = options?.pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
1143
+ const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
1144
+ const pivotColumns = options?.pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
1145
+ const pivotSelection = buildTypedSelection(
1146
+ many.pivotTable.columns,
1147
+ pivotAliasPrefix,
1148
+ pivotColumns,
1149
+ (key) => `Column '${key}' not found on pivot table '${many.pivotTable.name}'`
1150
+ );
1151
+ const combinedSelection = {
1152
+ ...targetSelection,
1153
+ ...pivotSelection
1154
+ };
1155
+ const relationSelectionResult = this.selectColumns(state, hydration, combinedSelection);
1156
+ state = relationSelectionResult.state;
1157
+ hydration = relationSelectionResult.hydration;
1158
+ hydration = hydration.onRelationIncluded(
1159
+ state,
1160
+ relation,
1161
+ relationName,
1162
+ aliasPrefix,
1163
+ targetColumns,
1164
+ { aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
1165
+ );
1166
+ return { state, hydration };
1167
+ }
1168
+ /**
1169
+ * Applies relation correlation to a query AST
1170
+ * @param relationName - Name of the relation
1171
+ * @param ast - Query AST to modify
1172
+ * @returns Modified query AST with relation correlation
1173
+ */
1174
+ applyRelationCorrelation(relationName, ast) {
1175
+ const relation = this.getRelation(relationName);
1176
+ const correlation = buildRelationCorrelation(this.table, relation);
1177
+ const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
1178
+ return {
1179
+ ...ast,
1180
+ where: whereInSubquery
1181
+ };
1182
+ }
1183
+ /**
1184
+ * Creates a join node for a relation
1185
+ * @param state - Current query state
1186
+ * @param relationName - Name of the relation
1187
+ * @param joinKind - Type of join to use
1188
+ * @param extraCondition - Additional join condition
1189
+ * @returns Updated query state with join
1190
+ */
1191
+ withJoin(state, relationName, joinKind, extraCondition) {
1192
+ const relation = this.getRelation(relationName);
1193
+ if (relation.type === RelationKinds.BelongsToMany) {
1194
+ const joins = buildBelongsToManyJoins(
1195
+ this.table,
1196
+ relationName,
1197
+ relation,
1198
+ joinKind,
1199
+ extraCondition
1200
+ );
1201
+ return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
1202
+ }
1203
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
1204
+ const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
1205
+ return this.astService(state).withJoin(joinNode);
1206
+ }
1207
+ /**
1208
+ * Selects columns for a relation
1209
+ * @param state - Current query state
1210
+ * @param hydration - Hydration manager
1211
+ * @param columns - Columns to select
1212
+ * @returns Relation result with updated state and hydration
1213
+ */
1214
+ selectColumns(state, hydration, columns) {
1215
+ const { state: nextState, addedColumns } = this.astService(state).select(columns);
1216
+ return {
1217
+ state: nextState,
1218
+ hydration: hydration.onColumnsSelected(nextState, addedColumns)
1219
+ };
1220
+ }
1221
+ /**
1222
+ * Gets a relation definition by name
1223
+ * @param relationName - Name of the relation
1224
+ * @returns Relation definition
1225
+ * @throws Error if relation is not found
1226
+ */
1227
+ getRelation(relationName) {
1228
+ const relation = this.table.relations[relationName];
1229
+ if (!relation) {
1230
+ throw new Error(`Relation '${relationName}' not found on table '${this.table.name}'`);
1231
+ }
1232
+ return relation;
1233
+ }
1234
+ /**
1235
+ * Creates a QueryAstService instance
1236
+ * @param state - Current query state
1237
+ * @returns QueryAstService instance
1238
+ */
1239
+ astService(state = this.state) {
1240
+ return this.createQueryAstService(this.table, state);
1241
+ }
1242
+ };
1243
+
1244
+ // src/query-builder/select-query-builder-deps.ts
1245
+ var defaultCreateQueryAstService = (table, state) => new QueryAstService(table, state);
1246
+ var defaultCreateHydrationPlanner = (table) => new HydrationPlanner(table);
1247
+ var defaultCreateHydration = (table, plannerFactory) => new HydrationManager(table, plannerFactory(table));
1248
+ var resolveSelectQueryBuilderDependencies = (overrides = {}) => {
1249
+ const createQueryAstService = overrides.createQueryAstService ?? defaultCreateQueryAstService;
1250
+ const createHydrationPlanner = overrides.createHydrationPlanner ?? defaultCreateHydrationPlanner;
1251
+ const createHydration = overrides.createHydration ?? ((table) => defaultCreateHydration(table, createHydrationPlanner));
1252
+ const createRelationService = overrides.createRelationService ?? ((table, state, hydration) => new RelationService(table, state, hydration, createQueryAstService));
1253
+ return {
1254
+ createState: overrides.createState ?? ((table) => new SelectQueryState(table)),
1255
+ createHydration,
1256
+ createHydrationPlanner,
1257
+ createQueryAstService,
1258
+ createRelationService
1259
+ };
1260
+ };
1261
+ var defaultSelectQueryBuilderDependencies = resolveSelectQueryBuilderDependencies();
1262
+
1263
+ // src/query-builder/column-selector.ts
1264
+ var ColumnSelector = class {
1265
+ /**
1266
+ * Creates a new ColumnSelector instance
1267
+ * @param env - Query builder environment
1268
+ */
1269
+ constructor(env) {
1270
+ this.env = env;
1271
+ }
1272
+ /**
1273
+ * Selects columns for the query
1274
+ * @param context - Current query context
1275
+ * @param columns - Columns to select
1276
+ * @returns Updated query context with selected columns
1277
+ */
1278
+ select(context, columns) {
1279
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1280
+ const { state: nextState, addedColumns } = astService.select(columns);
1281
+ return {
1282
+ state: nextState,
1283
+ hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
1284
+ };
1285
+ }
1286
+ /**
1287
+ * Selects raw column expressions
1288
+ * @param context - Current query context
1289
+ * @param columns - Raw column expressions
1290
+ * @returns Updated query context with raw column selections
1291
+ */
1292
+ selectRaw(context, columns) {
1293
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1294
+ const nextState = astService.selectRaw(columns).state;
1295
+ return { state: nextState, hydration: context.hydration };
1296
+ }
1297
+ /**
1298
+ * Selects a subquery as a column
1299
+ * @param context - Current query context
1300
+ * @param alias - Alias for the subquery
1301
+ * @param query - Subquery to select
1302
+ * @returns Updated query context with subquery selection
1303
+ */
1304
+ selectSubquery(context, alias, query) {
1305
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1306
+ const nextState = astService.selectSubquery(alias, query);
1307
+ return { state: nextState, hydration: context.hydration };
1308
+ }
1309
+ /**
1310
+ * Adds DISTINCT clause to the query
1311
+ * @param context - Current query context
1312
+ * @param columns - Columns to make distinct
1313
+ * @returns Updated query context with DISTINCT clause
1314
+ */
1315
+ distinct(context, columns) {
1316
+ const nodes = columns.map((col2) => buildColumnNode(this.env.table, col2));
1317
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
1318
+ const nextState = astService.withDistinct(nodes);
1319
+ return { state: nextState, hydration: context.hydration };
1320
+ }
1321
+ };
1322
+
1323
+ // src/query-builder/relation-manager.ts
1324
+ var RelationManager = class {
1325
+ /**
1326
+ * Creates a new RelationManager instance
1327
+ * @param env - Query builder environment
1328
+ */
1329
+ constructor(env) {
1330
+ this.env = env;
1331
+ }
1332
+ /**
1333
+ * Matches records based on a relation with an optional predicate
1334
+ * @param context - Current query context
1335
+ * @param relationName - Name of the relation to match
1336
+ * @param predicate - Optional predicate expression
1337
+ * @returns Updated query context with relation match
1338
+ */
1339
+ match(context, relationName, predicate) {
1340
+ const result = this.createService(context).match(relationName, predicate);
1341
+ return { state: result.state, hydration: result.hydration };
1342
+ }
1343
+ /**
1344
+ * Joins a relation to the query
1345
+ * @param context - Current query context
1346
+ * @param relationName - Name of the relation to join
1347
+ * @param joinKind - Type of join to use
1348
+ * @param extraCondition - Additional join condition
1349
+ * @returns Updated query context with relation join
1350
+ */
1351
+ joinRelation(context, relationName, joinKind, extraCondition) {
1352
+ const result = this.createService(context).joinRelation(relationName, joinKind, extraCondition);
1353
+ return { state: result.state, hydration: result.hydration };
1354
+ }
1355
+ /**
1356
+ * Includes a relation in the query result
1357
+ * @param context - Current query context
1358
+ * @param relationName - Name of the relation to include
1359
+ * @param options - Options for relation inclusion
1360
+ * @returns Updated query context with included relation
1361
+ */
1362
+ include(context, relationName, options) {
1363
+ const result = this.createService(context).include(relationName, options);
1364
+ return { state: result.state, hydration: result.hydration };
1365
+ }
1366
+ /**
1367
+ * Applies relation correlation to a query AST
1368
+ * @param context - Current query context
1369
+ * @param relationName - Name of the relation
1370
+ * @param ast - Query AST to modify
1371
+ * @returns Modified query AST with relation correlation
1372
+ */
1373
+ applyRelationCorrelation(context, relationName, ast) {
1374
+ return this.createService(context).applyRelationCorrelation(relationName, ast);
1375
+ }
1376
+ /**
1377
+ * Creates a relation service instance
1378
+ * @param context - Current query context
1379
+ * @returns Relation service instance
1380
+ */
1381
+ createService(context) {
1382
+ return this.env.deps.createRelationService(this.env.table, context.state, context.hydration);
1383
+ }
1384
+ };
1385
+
1386
+ // src/orm/hydration.ts
1387
+ var hydrateRows = (rows, plan) => {
1388
+ if (!plan || !rows.length) return rows;
1389
+ const rootMap = /* @__PURE__ */ new Map();
1390
+ const relationIndex = /* @__PURE__ */ new Map();
1391
+ const getOrCreateParent = (row) => {
1392
+ const rootId = row[plan.rootPrimaryKey];
1393
+ if (rootId === void 0) return void 0;
1394
+ if (!rootMap.has(rootId)) {
1395
+ rootMap.set(rootId, createBaseRow(row, plan));
1396
+ }
1397
+ return rootMap.get(rootId);
1398
+ };
1399
+ const getRelationSeenSet = (rootId, relationName) => {
1400
+ let byRelation = relationIndex.get(rootId);
1401
+ if (!byRelation) {
1402
+ byRelation = {};
1403
+ relationIndex.set(rootId, byRelation);
1404
+ }
1405
+ let seen = byRelation[relationName];
1406
+ if (!seen) {
1407
+ seen = /* @__PURE__ */ new Set();
1408
+ byRelation[relationName] = seen;
1409
+ }
1410
+ return seen;
1411
+ };
1412
+ for (const row of rows) {
1413
+ const rootId = row[plan.rootPrimaryKey];
1414
+ if (rootId === void 0) continue;
1415
+ const parent = getOrCreateParent(row);
1416
+ if (!parent) continue;
1417
+ for (const rel of plan.relations) {
1418
+ const childPkKey = makeRelationAlias(rel.aliasPrefix, rel.targetPrimaryKey);
1419
+ const childPk = row[childPkKey];
1420
+ if (childPk === null || childPk === void 0) continue;
1421
+ const seen = getRelationSeenSet(rootId, rel.name);
1422
+ if (seen.has(childPk)) continue;
1423
+ seen.add(childPk);
1424
+ const bucket = parent[rel.name];
1425
+ bucket.push(buildChild(row, rel));
1426
+ }
1427
+ }
1428
+ return Array.from(rootMap.values());
1429
+ };
1430
+ var createBaseRow = (row, plan) => {
1431
+ const base = {};
1432
+ const baseKeys = plan.rootColumns.length ? plan.rootColumns : Object.keys(row).filter((k) => !isRelationAlias(k));
1433
+ for (const key of baseKeys) {
1434
+ base[key] = row[key];
1435
+ }
1436
+ for (const rel of plan.relations) {
1437
+ base[rel.name] = [];
1438
+ }
1439
+ return base;
1440
+ };
1441
+ var buildChild = (row, rel) => {
1442
+ const child = {};
1443
+ for (const col2 of rel.columns) {
1444
+ const key = makeRelationAlias(rel.aliasPrefix, col2);
1445
+ child[col2] = row[key];
1446
+ }
1447
+ const pivot = buildPivot(row, rel);
1448
+ if (pivot) {
1449
+ child._pivot = pivot;
1450
+ }
1451
+ return child;
1452
+ };
1453
+ var buildPivot = (row, rel) => {
1454
+ if (!rel.pivot) return void 0;
1455
+ const pivot = {};
1456
+ for (const col2 of rel.pivot.columns) {
1457
+ const key = makeRelationAlias(rel.pivot.aliasPrefix, col2);
1458
+ pivot[col2] = row[key];
1459
+ }
1460
+ const hasValue = Object.values(pivot).some((v) => v !== null && v !== void 0);
1461
+ return hasValue ? pivot : void 0;
1462
+ };
1463
+
1464
+ // src/orm/entity-meta.ts
1465
+ var ENTITY_META = Symbol("EntityMeta");
1466
+ var toKey = (value) => value === null || value === void 0 ? "" : String(value);
1467
+ var getHydrationRows = (meta, relationName, key) => {
1468
+ const map = meta.relationHydration.get(relationName);
1469
+ if (!map) return void 0;
1470
+ const rows = map.get(toKey(key));
1471
+ if (!rows) return void 0;
1472
+ return Array.isArray(rows) ? rows : void 0;
1473
+ };
1474
+ var getHydrationRecord = (meta, relationName, key) => {
1475
+ const map = meta.relationHydration.get(relationName);
1476
+ if (!map) return void 0;
1477
+ const value = map.get(toKey(key));
1478
+ if (!value) return void 0;
1479
+ if (Array.isArray(value)) {
1480
+ return value[0];
1481
+ }
1482
+ return value;
1483
+ };
1484
+ var getEntityMeta = (entity) => {
1485
+ if (!entity || typeof entity !== "object") return void 0;
1486
+ return entity[ENTITY_META];
1487
+ };
1488
+ var hasEntityMeta = (entity) => {
1489
+ return Boolean(getEntityMeta(entity));
1490
+ };
1491
+
1492
+ // src/orm/relations/has-many.ts
1493
+ var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
1494
+ var DefaultHasManyCollection = class {
1495
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
1496
+ this.ctx = ctx;
1497
+ this.meta = meta;
1498
+ this.root = root;
1499
+ this.relationName = relationName;
1500
+ this.relation = relation;
1501
+ this.rootTable = rootTable;
1502
+ this.loader = loader;
1503
+ this.createEntity = createEntity;
1504
+ this.localKey = localKey;
1505
+ this.loaded = false;
1506
+ this.items = [];
1507
+ this.added = /* @__PURE__ */ new Set();
1508
+ this.removed = /* @__PURE__ */ new Set();
1509
+ this.hydrateFromCache();
1510
+ }
1511
+ async load() {
1512
+ if (this.loaded) return this.items;
1513
+ const map = await this.loader();
1514
+ const key = toKey2(this.root[this.localKey]);
1515
+ const rows = map.get(key) ?? [];
1516
+ this.items = rows.map((row) => this.createEntity(row));
1517
+ this.loaded = true;
1518
+ return this.items;
1519
+ }
1520
+ getItems() {
1521
+ return this.items;
1522
+ }
1523
+ add(data) {
1524
+ const keyValue = this.root[this.localKey];
1525
+ const childRow = {
1526
+ ...data,
1527
+ [this.relation.foreignKey]: keyValue
1528
+ };
1529
+ const entity = this.createEntity(childRow);
1530
+ this.added.add(entity);
1531
+ this.items.push(entity);
1532
+ this.ctx.registerRelationChange(
1533
+ this.root,
1534
+ this.relationKey,
1535
+ this.rootTable,
1536
+ this.relationName,
1537
+ this.relation,
1538
+ { kind: "add", entity }
1539
+ );
1540
+ return entity;
1541
+ }
1542
+ attach(entity) {
1543
+ const keyValue = this.root[this.localKey];
1544
+ entity[this.relation.foreignKey] = keyValue;
1545
+ this.ctx.markDirty(entity);
1546
+ this.items.push(entity);
1547
+ this.ctx.registerRelationChange(
1548
+ this.root,
1549
+ this.relationKey,
1550
+ this.rootTable,
1551
+ this.relationName,
1552
+ this.relation,
1553
+ { kind: "attach", entity }
1554
+ );
1555
+ }
1556
+ remove(entity) {
1557
+ this.items = this.items.filter((item) => item !== entity);
1558
+ this.removed.add(entity);
1559
+ this.ctx.registerRelationChange(
1560
+ this.root,
1561
+ this.relationKey,
1562
+ this.rootTable,
1563
+ this.relationName,
1564
+ this.relation,
1565
+ { kind: "remove", entity }
1566
+ );
1567
+ }
1568
+ clear() {
1569
+ for (const entity of [...this.items]) {
1570
+ this.remove(entity);
1571
+ }
1572
+ }
1573
+ get relationKey() {
1574
+ return `${this.rootTable.name}.${this.relationName}`;
1575
+ }
1576
+ hydrateFromCache() {
1577
+ const keyValue = this.root[this.localKey];
1578
+ if (keyValue === void 0 || keyValue === null) return;
1579
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
1580
+ if (!rows?.length) return;
1581
+ this.items = rows.map((row) => this.createEntity(row));
1582
+ this.loaded = true;
1583
+ }
1584
+ };
1585
+
1586
+ // src/orm/relations/belongs-to.ts
1587
+ var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
1588
+ var DefaultBelongsToReference = class {
1589
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
1590
+ this.ctx = ctx;
1591
+ this.meta = meta;
1592
+ this.root = root;
1593
+ this.relationName = relationName;
1594
+ this.relation = relation;
1595
+ this.rootTable = rootTable;
1596
+ this.loader = loader;
1597
+ this.createEntity = createEntity;
1598
+ this.targetKey = targetKey;
1599
+ this.loaded = false;
1600
+ this.current = null;
1601
+ this.populateFromHydrationCache();
1602
+ }
1603
+ async load() {
1604
+ if (this.loaded) return this.current;
1605
+ const map = await this.loader();
1606
+ const fkValue = this.root[this.relation.foreignKey];
1607
+ if (fkValue === null || fkValue === void 0) {
1608
+ this.current = null;
1609
+ } else {
1610
+ const row = map.get(toKey3(fkValue));
1611
+ this.current = row ? this.createEntity(row) : null;
1612
+ }
1613
+ this.loaded = true;
1614
+ return this.current;
1615
+ }
1616
+ get() {
1617
+ return this.current;
1618
+ }
1619
+ set(data) {
1620
+ if (data === null) {
1621
+ const previous = this.current;
1622
+ this.root[this.relation.foreignKey] = null;
1623
+ this.current = null;
1624
+ this.ctx.registerRelationChange(
1625
+ this.root,
1626
+ this.relationKey,
1627
+ this.rootTable,
1628
+ this.relationName,
1629
+ this.relation,
1630
+ { kind: "remove", entity: previous }
1631
+ );
1632
+ return null;
1633
+ }
1634
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
1635
+ const pkValue = entity[this.targetKey];
1636
+ if (pkValue !== void 0) {
1637
+ this.root[this.relation.foreignKey] = pkValue;
1638
+ }
1639
+ this.current = entity;
1640
+ this.ctx.registerRelationChange(
1641
+ this.root,
1642
+ this.relationKey,
1643
+ this.rootTable,
1644
+ this.relationName,
1645
+ this.relation,
1646
+ { kind: "attach", entity }
1647
+ );
1648
+ return entity;
1649
+ }
1650
+ get relationKey() {
1651
+ return `${this.rootTable.name}.${this.relationName}`;
1652
+ }
1653
+ populateFromHydrationCache() {
1654
+ const fkValue = this.root[this.relation.foreignKey];
1655
+ if (fkValue === void 0 || fkValue === null) return;
1656
+ const row = getHydrationRecord(this.meta, this.relationName, fkValue);
1657
+ if (!row) return;
1658
+ this.current = this.createEntity(row);
1659
+ this.loaded = true;
1660
+ }
1661
+ };
1662
+
1663
+ // src/orm/relations/many-to-many.ts
1664
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
1665
+ var DefaultManyToManyCollection = class {
1666
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
1667
+ this.ctx = ctx;
1668
+ this.meta = meta;
1669
+ this.root = root;
1670
+ this.relationName = relationName;
1671
+ this.relation = relation;
1672
+ this.rootTable = rootTable;
1673
+ this.loader = loader;
1674
+ this.createEntity = createEntity;
1675
+ this.localKey = localKey;
1676
+ this.loaded = false;
1677
+ this.items = [];
1678
+ this.hydrateFromCache();
1679
+ }
1680
+ async load() {
1681
+ if (this.loaded) return this.items;
1682
+ const map = await this.loader();
1683
+ const key = toKey4(this.root[this.localKey]);
1684
+ const rows = map.get(key) ?? [];
1685
+ this.items = rows.map((row) => {
1686
+ const entity = this.createEntity(row);
1687
+ if (row._pivot) {
1688
+ entity._pivot = row._pivot;
1689
+ }
1690
+ return entity;
1691
+ });
1692
+ this.loaded = true;
1693
+ return this.items;
1694
+ }
1695
+ getItems() {
1696
+ return this.items;
1697
+ }
1698
+ attach(target) {
1699
+ const entity = this.ensureEntity(target);
1700
+ const id = this.extractId(entity);
1701
+ if (id == null) return;
1702
+ if (this.items.some((item) => this.extractId(item) === id)) {
1703
+ return;
1704
+ }
1705
+ this.items.push(entity);
1706
+ this.ctx.registerRelationChange(
1707
+ this.root,
1708
+ this.relationKey,
1709
+ this.rootTable,
1710
+ this.relationName,
1711
+ this.relation,
1712
+ { kind: "attach", entity }
1713
+ );
1714
+ }
1715
+ detach(target) {
1716
+ const id = typeof target === "number" || typeof target === "string" ? target : this.extractId(target);
1717
+ if (id == null) return;
1718
+ const existing = this.items.find((item) => this.extractId(item) === id);
1719
+ if (!existing) return;
1720
+ this.items = this.items.filter((item) => this.extractId(item) !== id);
1721
+ this.ctx.registerRelationChange(
1722
+ this.root,
1723
+ this.relationKey,
1724
+ this.rootTable,
1725
+ this.relationName,
1726
+ this.relation,
1727
+ { kind: "detach", entity: existing }
1728
+ );
1729
+ }
1730
+ async syncByIds(ids) {
1731
+ await this.load();
1732
+ const targetKey = this.relation.targetKey || findPrimaryKey(this.relation.target);
1733
+ const normalized = new Set(ids.map((id) => toKey4(id)));
1734
+ const currentIds = new Set(this.items.map((item) => toKey4(this.extractId(item))));
1735
+ for (const id of normalized) {
1736
+ if (!currentIds.has(id)) {
1737
+ this.attach(id);
1738
+ }
1739
+ }
1740
+ for (const item of [...this.items]) {
1741
+ const itemId = toKey4(this.extractId(item));
1742
+ if (!normalized.has(itemId)) {
1743
+ this.detach(item);
1744
+ }
1745
+ }
1746
+ }
1747
+ ensureEntity(target) {
1748
+ if (typeof target === "number" || typeof target === "string") {
1749
+ const stub = {
1750
+ [this.targetKey]: target
1751
+ };
1752
+ return this.createEntity(stub);
1753
+ }
1754
+ return target;
1755
+ }
1756
+ extractId(entity) {
1757
+ if (entity === null || entity === void 0) return null;
1758
+ if (typeof entity === "number" || typeof entity === "string") {
1759
+ return entity;
1760
+ }
1761
+ return entity[this.targetKey] ?? null;
1762
+ }
1763
+ get relationKey() {
1764
+ return `${this.rootTable.name}.${this.relationName}`;
1765
+ }
1766
+ get targetKey() {
1767
+ return this.relation.targetKey || findPrimaryKey(this.relation.target);
1768
+ }
1769
+ hydrateFromCache() {
1770
+ const keyValue = this.root[this.localKey];
1771
+ if (keyValue === void 0 || keyValue === null) return;
1772
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
1773
+ if (!rows?.length) return;
1774
+ this.items = rows.map((row) => {
1775
+ const entity = this.createEntity(row);
1776
+ if (row._pivot) {
1777
+ entity._pivot = row._pivot;
1778
+ }
1779
+ return entity;
1780
+ });
1781
+ this.loaded = true;
1782
+ }
1783
+ };
1784
+
1785
+ // src/orm/lazy-batch.ts
1786
+ var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
1787
+ acc[name] = def;
1788
+ return acc;
1789
+ }, {});
1790
+ var rowsFromResults = (results) => {
1791
+ const rows = [];
1792
+ for (const result of results) {
1793
+ const { columns, values } = result;
1794
+ for (const valueRow of values) {
1795
+ const row = {};
1796
+ columns.forEach((column, idx) => {
1797
+ row[column] = valueRow[idx];
1798
+ });
1799
+ rows.push(row);
1800
+ }
1801
+ }
1802
+ return rows;
1803
+ };
1804
+ var executeQuery = async (ctx, qb) => {
1805
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
1806
+ const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
1807
+ return rowsFromResults(results);
1808
+ };
1809
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
1810
+ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
1811
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
1812
+ const roots = ctx.getEntitiesForTable(rootTable);
1813
+ const keys = /* @__PURE__ */ new Set();
1814
+ for (const tracked of roots) {
1815
+ const value = tracked.entity[localKey];
1816
+ if (value !== null && value !== void 0) {
1817
+ keys.add(value);
1818
+ }
1819
+ }
1820
+ if (!keys.size) {
1821
+ return /* @__PURE__ */ new Map();
1822
+ }
1823
+ const selectMap = selectAllColumns(relation.target);
1824
+ const fb = new SelectQueryBuilder(relation.target).select(selectMap);
1825
+ const fkColumn = relation.target.columns[relation.foreignKey];
1826
+ if (!fkColumn) return /* @__PURE__ */ new Map();
1827
+ fb.where(inList(fkColumn, Array.from(keys)));
1828
+ const rows = await executeQuery(ctx, fb);
1829
+ const grouped = /* @__PURE__ */ new Map();
1830
+ for (const row of rows) {
1831
+ const fkValue = row[relation.foreignKey];
1832
+ if (fkValue === null || fkValue === void 0) continue;
1833
+ const key = toKey5(fkValue);
1834
+ const bucket = grouped.get(key) ?? [];
1835
+ bucket.push(row);
1836
+ grouped.set(key, bucket);
1837
+ }
1838
+ return grouped;
1839
+ };
1840
+ var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
1841
+ const roots = ctx.getEntitiesForTable(rootTable);
1842
+ const foreignKeys = /* @__PURE__ */ new Set();
1843
+ for (const tracked of roots) {
1844
+ const value = tracked.entity[relation.foreignKey];
1845
+ if (value !== null && value !== void 0) {
1846
+ foreignKeys.add(value);
1847
+ }
1848
+ }
1849
+ if (!foreignKeys.size) {
1850
+ return /* @__PURE__ */ new Map();
1851
+ }
1852
+ const selectMap = selectAllColumns(relation.target);
1853
+ const qb = new SelectQueryBuilder(relation.target).select(selectMap);
1854
+ const targetKey = relation.localKey || findPrimaryKey(relation.target);
1855
+ const pkColumn = relation.target.columns[targetKey];
1856
+ if (!pkColumn) return /* @__PURE__ */ new Map();
1857
+ qb.where(inList(pkColumn, Array.from(foreignKeys)));
1858
+ const rows = await executeQuery(ctx, qb);
1859
+ const map = /* @__PURE__ */ new Map();
1860
+ for (const row of rows) {
1861
+ const keyValue = row[targetKey];
1862
+ if (keyValue === null || keyValue === void 0) continue;
1863
+ map.set(toKey5(keyValue), row);
1864
+ }
1865
+ return map;
1866
+ };
1867
+ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
1868
+ const rootKey = relation.localKey || findPrimaryKey(rootTable);
1869
+ const roots = ctx.getEntitiesForTable(rootTable);
1870
+ const rootIds = /* @__PURE__ */ new Set();
1871
+ for (const tracked of roots) {
1872
+ const value = tracked.entity[rootKey];
1873
+ if (value !== null && value !== void 0) {
1874
+ rootIds.add(value);
1875
+ }
1876
+ }
1877
+ if (!rootIds.size) {
1878
+ return /* @__PURE__ */ new Map();
1879
+ }
1880
+ const pivotSelect = selectAllColumns(relation.pivotTable);
1881
+ const pivotQb = new SelectQueryBuilder(relation.pivotTable).select(pivotSelect);
1882
+ const pivotFkCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
1883
+ if (!pivotFkCol) return /* @__PURE__ */ new Map();
1884
+ pivotQb.where(inList(pivotFkCol, Array.from(rootIds)));
1885
+ const pivotRows = await executeQuery(ctx, pivotQb);
1886
+ const rootLookup = /* @__PURE__ */ new Map();
1887
+ const targetIds = /* @__PURE__ */ new Set();
1888
+ for (const pivot of pivotRows) {
1889
+ const rootValue = pivot[relation.pivotForeignKeyToRoot];
1890
+ const targetValue = pivot[relation.pivotForeignKeyToTarget];
1891
+ if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
1892
+ continue;
1893
+ }
1894
+ const bucket = rootLookup.get(toKey5(rootValue)) ?? [];
1895
+ bucket.push({
1896
+ targetId: targetValue,
1897
+ pivot: { ...pivot }
1898
+ });
1899
+ rootLookup.set(toKey5(rootValue), bucket);
1900
+ targetIds.add(targetValue);
1901
+ }
1902
+ if (!targetIds.size) {
1903
+ return /* @__PURE__ */ new Map();
1904
+ }
1905
+ const targetSelect = selectAllColumns(relation.target);
1906
+ const targetKey = relation.targetKey || findPrimaryKey(relation.target);
1907
+ const targetPkColumn = relation.target.columns[targetKey];
1908
+ if (!targetPkColumn) return /* @__PURE__ */ new Map();
1909
+ const targetQb = new SelectQueryBuilder(relation.target).select(targetSelect);
1910
+ targetQb.where(inList(targetPkColumn, Array.from(targetIds)));
1911
+ const targetRows = await executeQuery(ctx, targetQb);
1912
+ const targetMap = /* @__PURE__ */ new Map();
1913
+ for (const row of targetRows) {
1914
+ const pkValue = row[targetKey];
1915
+ if (pkValue === null || pkValue === void 0) continue;
1916
+ targetMap.set(toKey5(pkValue), row);
1917
+ }
1918
+ const result = /* @__PURE__ */ new Map();
1919
+ for (const [rootId, entries] of rootLookup.entries()) {
1920
+ const bucket = [];
1921
+ for (const entry of entries) {
1922
+ const targetRow = targetMap.get(toKey5(entry.targetId));
1923
+ if (!targetRow) continue;
1924
+ bucket.push({
1925
+ ...targetRow,
1926
+ _pivot: entry.pivot
1927
+ });
1928
+ }
1929
+ result.set(rootId, bucket);
1930
+ }
1931
+ return result;
1932
+ };
1933
+
1934
+ // src/orm/entity.ts
1935
+ var relationLoaderCache = (meta, relationName, factory) => {
1936
+ if (meta.relationCache.has(relationName)) {
1937
+ return meta.relationCache.get(relationName);
1938
+ }
1939
+ const promise = factory().then((value) => {
1940
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
1941
+ const otherMeta = getEntityMeta(tracked.entity);
1942
+ if (!otherMeta) continue;
1943
+ otherMeta.relationHydration.set(relationName, value);
1944
+ }
1945
+ return value;
1946
+ });
1947
+ meta.relationCache.set(relationName, promise);
1948
+ for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
1949
+ const otherMeta = getEntityMeta(tracked.entity);
1950
+ if (!otherMeta) continue;
1951
+ otherMeta.relationCache.set(relationName, promise);
1952
+ }
1953
+ return promise;
1954
+ };
1955
+ var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
1956
+ const target = { ...row };
1957
+ const meta = {
1958
+ ctx,
1959
+ table,
1960
+ lazyRelations: [...lazyRelations],
1961
+ relationCache: /* @__PURE__ */ new Map(),
1962
+ relationHydration: /* @__PURE__ */ new Map(),
1963
+ relationWrappers: /* @__PURE__ */ new Map()
1964
+ };
1965
+ Object.defineProperty(target, ENTITY_META, {
1966
+ value: meta,
1967
+ enumerable: false,
1968
+ writable: false
1969
+ });
1970
+ let proxy;
1971
+ const handler = {
1972
+ get(targetObj, prop, receiver) {
1973
+ if (prop === ENTITY_META) {
1974
+ return meta;
1975
+ }
1976
+ if (prop === "$load") {
1977
+ return async (relationName) => {
1978
+ const wrapper = getRelationWrapper(meta, relationName, proxy);
1979
+ if (wrapper && typeof wrapper.load === "function") {
1980
+ return wrapper.load();
1981
+ }
1982
+ return void 0;
1983
+ };
1984
+ }
1985
+ if (typeof prop === "string" && table.relations[prop]) {
1986
+ return getRelationWrapper(meta, prop, proxy);
1987
+ }
1988
+ return Reflect.get(targetObj, prop, receiver);
1989
+ },
1990
+ set(targetObj, prop, value, receiver) {
1991
+ const result = Reflect.set(targetObj, prop, value, receiver);
1992
+ if (typeof prop === "string" && table.columns[prop]) {
1993
+ ctx.markDirty(proxy);
1994
+ }
1995
+ return result;
1996
+ }
1997
+ };
1998
+ proxy = new Proxy(target, handler);
1999
+ populateHydrationCache(proxy, row, meta);
2000
+ return proxy;
2001
+ };
2002
+ var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
2003
+ const pkName = findPrimaryKey(table);
2004
+ const pkValue = row[pkName];
2005
+ if (pkValue !== void 0 && pkValue !== null) {
2006
+ const tracked = ctx.getEntity(table, pkValue);
2007
+ if (tracked) return tracked;
2008
+ }
2009
+ const entity = createEntityProxy(ctx, table, row, lazyRelations);
2010
+ if (pkValue !== void 0 && pkValue !== null) {
2011
+ ctx.trackManaged(table, pkValue, entity);
2012
+ } else {
2013
+ ctx.trackNew(table, entity);
2014
+ }
2015
+ return entity;
2016
+ };
2017
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
2018
+ var populateHydrationCache = (entity, row, meta) => {
2019
+ for (const relationName of Object.keys(meta.table.relations)) {
2020
+ const relation = meta.table.relations[relationName];
2021
+ const data = row[relationName];
2022
+ if (!Array.isArray(data)) continue;
2023
+ if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
2024
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
2025
+ const rootValue = entity[localKey];
2026
+ if (rootValue === void 0 || rootValue === null) continue;
2027
+ const cache = /* @__PURE__ */ new Map();
2028
+ cache.set(toKey6(rootValue), data);
2029
+ meta.relationHydration.set(relationName, cache);
2030
+ meta.relationCache.set(relationName, Promise.resolve(cache));
2031
+ continue;
2032
+ }
2033
+ if (relation.type === RelationKinds.BelongsTo) {
2034
+ const targetKey = relation.localKey || findPrimaryKey(relation.target);
2035
+ const cache = /* @__PURE__ */ new Map();
2036
+ for (const item of data) {
2037
+ const pkValue = item[targetKey];
2038
+ if (pkValue === void 0 || pkValue === null) continue;
2039
+ cache.set(toKey6(pkValue), item);
2040
+ }
2041
+ if (cache.size) {
2042
+ meta.relationHydration.set(relationName, cache);
2043
+ meta.relationCache.set(relationName, Promise.resolve(cache));
2044
+ }
2045
+ }
2046
+ }
2047
+ };
2048
+ var getRelationWrapper = (meta, relationName, owner) => {
2049
+ if (meta.relationWrappers.has(relationName)) {
2050
+ return meta.relationWrappers.get(relationName);
2051
+ }
2052
+ const relation = meta.table.relations[relationName];
2053
+ if (!relation) return void 0;
2054
+ const wrapper = instantiateWrapper(meta, relationName, relation, owner);
2055
+ if (wrapper) {
2056
+ meta.relationWrappers.set(relationName, wrapper);
2057
+ }
2058
+ return wrapper;
2059
+ };
2060
+ var instantiateWrapper = (meta, relationName, relation, owner) => {
2061
+ switch (relation.type) {
2062
+ case RelationKinds.HasMany: {
2063
+ const hasMany2 = relation;
2064
+ const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
2065
+ const loader = () => relationLoaderCache(
2066
+ meta,
2067
+ relationName,
2068
+ () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
2069
+ );
2070
+ return new DefaultHasManyCollection(
2071
+ meta.ctx,
2072
+ meta,
2073
+ owner,
2074
+ relationName,
2075
+ hasMany2,
2076
+ meta.table,
2077
+ loader,
2078
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2079
+ localKey
2080
+ );
2081
+ }
2082
+ case RelationKinds.BelongsTo: {
2083
+ const belongsTo2 = relation;
2084
+ const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
2085
+ const loader = () => relationLoaderCache(
2086
+ meta,
2087
+ relationName,
2088
+ () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
2089
+ );
2090
+ return new DefaultBelongsToReference(
2091
+ meta.ctx,
2092
+ meta,
2093
+ owner,
2094
+ relationName,
2095
+ belongsTo2,
2096
+ meta.table,
2097
+ loader,
2098
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2099
+ targetKey
2100
+ );
2101
+ }
2102
+ case RelationKinds.BelongsToMany: {
2103
+ const many = relation;
2104
+ const localKey = many.localKey || findPrimaryKey(meta.table);
2105
+ const loader = () => relationLoaderCache(
2106
+ meta,
2107
+ relationName,
2108
+ () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
2109
+ );
2110
+ return new DefaultManyToManyCollection(
2111
+ meta.ctx,
2112
+ meta,
2113
+ owner,
2114
+ relationName,
2115
+ many,
2116
+ meta.table,
2117
+ loader,
2118
+ (row) => createEntityFromRow(meta.ctx, relation.target, row),
2119
+ localKey
2120
+ );
2121
+ }
2122
+ default:
2123
+ return void 0;
2124
+ }
2125
+ };
2126
+
2127
+ // src/orm/execute.ts
2128
+ var flattenResults = (results) => {
2129
+ const rows = [];
2130
+ for (const result of results) {
2131
+ const { columns, values } = result;
2132
+ for (const valueRow of values) {
2133
+ const row = {};
2134
+ columns.forEach((column, idx) => {
2135
+ row[column] = valueRow[idx];
2136
+ });
2137
+ rows.push(row);
2138
+ }
2139
+ }
2140
+ return rows;
2141
+ };
2142
+ async function executeHydrated(ctx, qb) {
2143
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
2144
+ const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
2145
+ const rows = flattenResults(executed);
2146
+ const hydrated = hydrateRows(rows, qb.getHydrationPlan());
2147
+ return hydrated.map(
2148
+ (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
2149
+ );
2150
+ }
2151
+
2152
+ // src/query-builder/select.ts
2153
+ var SelectQueryBuilder = class _SelectQueryBuilder {
2154
+ /**
2155
+ * Creates a new SelectQueryBuilder instance
2156
+ * @param table - Table definition to query
2157
+ * @param state - Optional initial query state
2158
+ * @param hydration - Optional hydration manager
2159
+ * @param dependencies - Optional query builder dependencies
2160
+ */
2161
+ constructor(table, state, hydration, dependencies, lazyRelations) {
2162
+ const deps = resolveSelectQueryBuilderDependencies(dependencies);
2163
+ this.env = { table, deps };
2164
+ const initialState = state ?? deps.createState(table);
2165
+ const initialHydration = hydration ?? deps.createHydration(table);
2166
+ this.context = {
2167
+ state: initialState,
2168
+ hydration: initialHydration
2169
+ };
2170
+ this.lazyRelations = new Set(lazyRelations ?? []);
2171
+ this.columnSelector = new ColumnSelector(this.env);
2172
+ this.relationManager = new RelationManager(this.env);
2173
+ }
2174
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
2175
+ return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
2176
+ }
2177
+ resolveQueryNode(query) {
2178
+ return typeof query.getAST === "function" ? query.getAST() : query;
2179
+ }
2180
+ createChildBuilder(table) {
2181
+ return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
2182
+ }
2183
+ applyAst(context, mutator) {
2184
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
2185
+ const nextState = mutator(astService);
2186
+ return { state: nextState, hydration: context.hydration };
2187
+ }
2188
+ applyJoin(context, table, condition, kind) {
2189
+ const joinNode = createJoinNode(kind, table.name, condition);
2190
+ return this.applyAst(context, (service) => service.withJoin(joinNode));
2191
+ }
2192
+ /**
2193
+ * Selects specific columns for the query
2194
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
2195
+ * @returns New query builder instance with selected columns
2196
+ */
2197
+ select(columns) {
2198
+ return this.clone(this.columnSelector.select(this.context, columns));
2199
+ }
2200
+ /**
2201
+ * Selects raw column expressions
2202
+ * @param cols - Column expressions as strings
2203
+ * @returns New query builder instance with raw column selections
2204
+ */
2205
+ selectRaw(...cols) {
2206
+ return this.clone(this.columnSelector.selectRaw(this.context, cols));
2207
+ }
2208
+ /**
2209
+ * Adds a Common Table Expression (CTE) to the query
2210
+ * @param name - Name of the CTE
2211
+ * @param query - Query builder or query node for the CTE
2212
+ * @param columns - Optional column names for the CTE
2213
+ * @returns New query builder instance with the CTE
2214
+ */
2215
+ with(name, query, columns) {
2216
+ const subAst = this.resolveQueryNode(query);
2217
+ const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
2218
+ return this.clone(nextContext);
2219
+ }
2220
+ /**
2221
+ * Adds a recursive Common Table Expression (CTE) to the query
2222
+ * @param name - Name of the CTE
2223
+ * @param query - Query builder or query node for the CTE
2224
+ * @param columns - Optional column names for the CTE
2225
+ * @returns New query builder instance with the recursive CTE
2226
+ */
2227
+ withRecursive(name, query, columns) {
2228
+ const subAst = this.resolveQueryNode(query);
2229
+ const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
2230
+ return this.clone(nextContext);
2231
+ }
2232
+ /**
2233
+ * Selects a subquery as a column
2234
+ * @param alias - Alias for the subquery column
2235
+ * @param sub - Query builder or query node for the subquery
2236
+ * @returns New query builder instance with the subquery selection
2237
+ */
2238
+ selectSubquery(alias, sub) {
2239
+ const query = this.resolveQueryNode(sub);
2240
+ return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
2241
+ }
2242
+ /**
2243
+ * Adds an INNER JOIN to the query
2244
+ * @param table - Table to join
2245
+ * @param condition - Join condition expression
2246
+ * @returns New query builder instance with the INNER JOIN
2247
+ */
2248
+ innerJoin(table, condition) {
2249
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
2250
+ return this.clone(nextContext);
2251
+ }
2252
+ /**
2253
+ * Adds a LEFT JOIN to the query
2254
+ * @param table - Table to join
2255
+ * @param condition - Join condition expression
2256
+ * @returns New query builder instance with the LEFT JOIN
2257
+ */
2258
+ leftJoin(table, condition) {
2259
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
2260
+ return this.clone(nextContext);
2261
+ }
2262
+ /**
2263
+ * Adds a RIGHT JOIN to the query
2264
+ * @param table - Table to join
2265
+ * @param condition - Join condition expression
2266
+ * @returns New query builder instance with the RIGHT JOIN
2267
+ */
2268
+ rightJoin(table, condition) {
2269
+ const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
2270
+ return this.clone(nextContext);
2271
+ }
2272
+ /**
2273
+ * Matches records based on a relationship
2274
+ * @param relationName - Name of the relationship to match
2275
+ * @param predicate - Optional predicate expression
2276
+ * @returns New query builder instance with the relationship match
2277
+ */
2278
+ match(relationName, predicate) {
2279
+ const nextContext = this.relationManager.match(this.context, relationName, predicate);
2280
+ return this.clone(nextContext);
2281
+ }
2282
+ /**
2283
+ * Joins a related table
2284
+ * @param relationName - Name of the relationship to join
2285
+ * @param joinKind - Type of join (defaults to INNER)
2286
+ * @param extraCondition - Optional additional join condition
2287
+ * @returns New query builder instance with the relationship join
2288
+ */
2289
+ joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
2290
+ const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
2291
+ return this.clone(nextContext);
2292
+ }
2293
+ /**
2294
+ * Includes related data in the query results
2295
+ * @param relationName - Name of the relationship to include
2296
+ * @param options - Optional include options
2297
+ * @returns New query builder instance with the relationship inclusion
2298
+ */
2299
+ include(relationName, options) {
2300
+ const nextContext = this.relationManager.include(this.context, relationName, options);
2301
+ return this.clone(nextContext);
2302
+ }
2303
+ includeLazy(relationName) {
2304
+ const nextLazy = new Set(this.lazyRelations);
2305
+ nextLazy.add(relationName);
2306
+ return this.clone(this.context, nextLazy);
2307
+ }
2308
+ getLazyRelations() {
2309
+ return Array.from(this.lazyRelations);
2310
+ }
2311
+ getTable() {
2312
+ return this.env.table;
2313
+ }
2314
+ async execute(ctx) {
2315
+ return executeHydrated(ctx, this);
2316
+ }
2317
+ /**
2318
+ * Adds a WHERE condition to the query
2319
+ * @param expr - Expression for the WHERE clause
2320
+ * @returns New query builder instance with the WHERE condition
2321
+ */
2322
+ where(expr) {
2323
+ const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
2324
+ return this.clone(nextContext);
2325
+ }
2326
+ /**
2327
+ * Adds a GROUP BY clause to the query
2328
+ * @param col - Column definition or column node to group by
2329
+ * @returns New query builder instance with the GROUP BY clause
2330
+ */
2331
+ groupBy(col2) {
2332
+ const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col2));
2333
+ return this.clone(nextContext);
2334
+ }
2335
+ /**
2336
+ * Adds a HAVING condition to the query
2337
+ * @param expr - Expression for the HAVING clause
2338
+ * @returns New query builder instance with the HAVING condition
2339
+ */
2340
+ having(expr) {
2341
+ const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
2342
+ return this.clone(nextContext);
2343
+ }
2344
+ /**
2345
+ * Adds an ORDER BY clause to the query
2346
+ * @param col - Column definition or column node to order by
2347
+ * @param direction - Order direction (defaults to ASC)
2348
+ * @returns New query builder instance with the ORDER BY clause
2349
+ */
2350
+ orderBy(col2, direction = ORDER_DIRECTIONS.ASC) {
2351
+ const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col2, direction));
2352
+ return this.clone(nextContext);
2353
+ }
2354
+ /**
2355
+ * Adds a DISTINCT clause to the query
2356
+ * @param cols - Columns to make distinct
2357
+ * @returns New query builder instance with the DISTINCT clause
2358
+ */
2359
+ distinct(...cols) {
2360
+ return this.clone(this.columnSelector.distinct(this.context, cols));
2361
+ }
2362
+ /**
2363
+ * Adds a LIMIT clause to the query
2364
+ * @param n - Maximum number of rows to return
2365
+ * @returns New query builder instance with the LIMIT clause
2366
+ */
2367
+ limit(n) {
2368
+ const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
2369
+ return this.clone(nextContext);
2370
+ }
2371
+ /**
2372
+ * Adds an OFFSET clause to the query
2373
+ * @param n - Number of rows to skip
2374
+ * @returns New query builder instance with the OFFSET clause
2375
+ */
2376
+ offset(n) {
2377
+ const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
2378
+ return this.clone(nextContext);
2379
+ }
2380
+ /**
2381
+ * Adds a WHERE EXISTS condition to the query
2382
+ * @param subquery - Subquery to check for existence
2383
+ * @returns New query builder instance with the WHERE EXISTS condition
2384
+ */
2385
+ whereExists(subquery) {
2386
+ const subAst = this.resolveQueryNode(subquery);
2387
+ return this.where(exists(subAst));
2388
+ }
2389
+ /**
2390
+ * Adds a WHERE NOT EXISTS condition to the query
2391
+ * @param subquery - Subquery to check for non-existence
2392
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
2393
+ */
2394
+ whereNotExists(subquery) {
2395
+ const subAst = this.resolveQueryNode(subquery);
2396
+ return this.where(notExists(subAst));
2397
+ }
2398
+ /**
2399
+ * Adds a WHERE EXISTS condition based on a relationship
2400
+ * @param relationName - Name of the relationship to check
2401
+ * @param callback - Optional callback to modify the relationship query
2402
+ * @returns New query builder instance with the relationship existence check
2403
+ */
2404
+ whereHas(relationName, callback) {
2405
+ const relation = this.env.table.relations[relationName];
2406
+ if (!relation) {
2407
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
2408
+ }
2409
+ let subQb = this.createChildBuilder(relation.target);
2410
+ if (callback) {
2411
+ subQb = callback(subQb);
2412
+ }
2413
+ const subAst = subQb.getAST();
2414
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
2415
+ return this.where(exists(finalSubAst));
2416
+ }
2417
+ /**
2418
+ * Adds a WHERE NOT EXISTS condition based on a relationship
2419
+ * @param relationName - Name of the relationship to check
2420
+ * @param callback - Optional callback to modify the relationship query
2421
+ * @returns New query builder instance with the relationship non-existence check
2422
+ */
2423
+ whereHasNot(relationName, callback) {
2424
+ const relation = this.env.table.relations[relationName];
2425
+ if (!relation) {
2426
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
2427
+ }
2428
+ let subQb = this.createChildBuilder(relation.target);
2429
+ if (callback) {
2430
+ subQb = callback(subQb);
2431
+ }
2432
+ const subAst = subQb.getAST();
2433
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
2434
+ return this.where(notExists(finalSubAst));
2435
+ }
2436
+ /**
2437
+ * Compiles the query to SQL for a specific dialect
2438
+ * @param dialect - Database dialect to compile for
2439
+ * @returns Compiled query with SQL and parameters
2440
+ */
2441
+ compile(dialect) {
2442
+ return dialect.compileSelect(this.context.state.ast);
2443
+ }
2444
+ /**
2445
+ * Converts the query to SQL string for a specific dialect
2446
+ * @param dialect - Database dialect to generate SQL for
2447
+ * @returns SQL string representation of the query
2448
+ */
2449
+ toSql(dialect) {
2450
+ return this.compile(dialect).sql;
2451
+ }
2452
+ /**
2453
+ * Gets the hydration plan for the query
2454
+ * @returns Hydration plan or undefined if none exists
2455
+ */
2456
+ getHydrationPlan() {
2457
+ return this.context.hydration.getPlan();
2458
+ }
2459
+ /**
2460
+ * Gets the Abstract Syntax Tree (AST) representation of the query
2461
+ * @returns Query AST with hydration applied
2462
+ */
2463
+ getAST() {
2464
+ return this.context.hydration.applyToAst(this.context.state.ast);
2465
+ }
2466
+ };
2467
+ var createColumn = (table, name) => ({ type: "Column", table, name });
2468
+ var createLiteral = (val) => ({ type: "Literal", value: val });
2469
+
2470
+ // src/query-builder/insert-query-state.ts
2471
+ var InsertQueryState = class _InsertQueryState {
2472
+ constructor(table, ast) {
2473
+ this.table = table;
2474
+ this.ast = ast ?? {
2475
+ type: "InsertQuery",
2476
+ into: createTableNode(table),
2477
+ columns: [],
2478
+ values: []
2479
+ };
2480
+ }
2481
+ clone(nextAst) {
2482
+ return new _InsertQueryState(this.table, nextAst);
2483
+ }
2484
+ withValues(rows) {
2485
+ if (!rows.length) return this;
2486
+ const definedColumns = this.ast.columns.length ? this.ast.columns : buildColumnNodes(this.table, Object.keys(rows[0]));
2487
+ const newRows = rows.map(
2488
+ (row) => definedColumns.map((column) => valueToOperand(row[column.name]))
2489
+ );
2490
+ return this.clone({
2491
+ ...this.ast,
2492
+ columns: definedColumns,
2493
+ values: [...this.ast.values, ...newRows]
2494
+ });
2495
+ }
2496
+ withReturning(columns) {
2497
+ return this.clone({
2498
+ ...this.ast,
2499
+ returning: [...columns]
2500
+ });
2501
+ }
2502
+ };
2503
+
2504
+ // src/query-builder/insert.ts
2505
+ var InsertQueryBuilder = class _InsertQueryBuilder {
2506
+ constructor(table, state) {
2507
+ this.table = table;
2508
+ this.state = state ?? new InsertQueryState(table);
2509
+ }
2510
+ clone(state) {
2511
+ return new _InsertQueryBuilder(this.table, state);
2512
+ }
2513
+ values(rowOrRows) {
2514
+ const rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
2515
+ if (!rows.length) return this;
2516
+ return this.clone(this.state.withValues(rows));
2517
+ }
2518
+ returning(...columns) {
2519
+ if (!columns.length) return this;
2520
+ const nodes = columns.map((column) => buildColumnNode(this.table, column));
2521
+ return this.clone(this.state.withReturning(nodes));
2522
+ }
2523
+ compile(compiler) {
2524
+ return compiler.compileInsert(this.state.ast);
2525
+ }
2526
+ toSql(compiler) {
2527
+ return this.compile(compiler).sql;
2528
+ }
2529
+ getAST() {
2530
+ return this.state.ast;
2531
+ }
2532
+ };
2533
+
2534
+ // src/query-builder/update-query-state.ts
2535
+ var UpdateQueryState = class _UpdateQueryState {
2536
+ constructor(table, ast) {
2537
+ this.table = table;
2538
+ this.ast = ast ?? {
2539
+ type: "UpdateQuery",
2540
+ table: createTableNode(table),
2541
+ set: []
2542
+ };
2543
+ }
2544
+ clone(nextAst) {
2545
+ return new _UpdateQueryState(this.table, nextAst);
2546
+ }
2547
+ withSet(values) {
2548
+ const assignments = Object.entries(values).map(([column, value]) => ({
2549
+ column: {
2550
+ type: "Column",
2551
+ table: this.table.name,
2552
+ name: column
2553
+ },
2554
+ value: valueToOperand(value)
2555
+ }));
2556
+ return this.clone({
2557
+ ...this.ast,
2558
+ set: assignments
2559
+ });
2560
+ }
2561
+ withWhere(expr) {
2562
+ return this.clone({
2563
+ ...this.ast,
2564
+ where: expr
2565
+ });
2566
+ }
2567
+ withReturning(columns) {
2568
+ return this.clone({
2569
+ ...this.ast,
2570
+ returning: [...columns]
2571
+ });
2572
+ }
2573
+ };
2574
+
2575
+ // src/query-builder/update.ts
2576
+ var UpdateQueryBuilder = class _UpdateQueryBuilder {
2577
+ constructor(table, state) {
2578
+ this.table = table;
2579
+ this.state = state ?? new UpdateQueryState(table);
2580
+ }
2581
+ clone(state) {
2582
+ return new _UpdateQueryBuilder(this.table, state);
2583
+ }
2584
+ set(values) {
2585
+ return this.clone(this.state.withSet(values));
2586
+ }
2587
+ where(expr) {
2588
+ return this.clone(this.state.withWhere(expr));
2589
+ }
2590
+ returning(...columns) {
2591
+ if (!columns.length) return this;
2592
+ const nodes = columns.map((column) => buildColumnNode(this.table, column));
2593
+ return this.clone(this.state.withReturning(nodes));
2594
+ }
2595
+ compile(compiler) {
2596
+ return compiler.compileUpdate(this.state.ast);
2597
+ }
2598
+ toSql(compiler) {
2599
+ return this.compile(compiler).sql;
2600
+ }
2601
+ getAST() {
2602
+ return this.state.ast;
2603
+ }
2604
+ };
2605
+
2606
+ // src/query-builder/delete-query-state.ts
2607
+ var DeleteQueryState = class _DeleteQueryState {
2608
+ constructor(table, ast) {
2609
+ this.table = table;
2610
+ this.ast = ast ?? {
2611
+ type: "DeleteQuery",
2612
+ from: createTableNode(table)
2613
+ };
2614
+ }
2615
+ clone(nextAst) {
2616
+ return new _DeleteQueryState(this.table, nextAst);
2617
+ }
2618
+ withWhere(expr) {
2619
+ return this.clone({
2620
+ ...this.ast,
2621
+ where: expr
2622
+ });
2623
+ }
2624
+ withReturning(columns) {
2625
+ return this.clone({
2626
+ ...this.ast,
2627
+ returning: [...columns]
2628
+ });
2629
+ }
2630
+ };
2631
+
2632
+ // src/query-builder/delete.ts
2633
+ var DeleteQueryBuilder = class _DeleteQueryBuilder {
2634
+ constructor(table, state) {
2635
+ this.table = table;
2636
+ this.state = state ?? new DeleteQueryState(table);
2637
+ }
2638
+ clone(state) {
2639
+ return new _DeleteQueryBuilder(this.table, state);
2640
+ }
2641
+ where(expr) {
2642
+ return this.clone(this.state.withWhere(expr));
2643
+ }
2644
+ returning(...columns) {
2645
+ if (!columns.length) return this;
2646
+ const nodes = columns.map((column) => buildColumnNode(this.table, column));
2647
+ return this.clone(this.state.withReturning(nodes));
2648
+ }
2649
+ compile(compiler) {
2650
+ return compiler.compileDelete(this.state.ast);
2651
+ }
2652
+ toSql(compiler) {
2653
+ return this.compile(compiler).sql;
2654
+ }
2655
+ getAST() {
2656
+ return this.state.ast;
2657
+ }
2658
+ };
2659
+
2660
+ // src/core/dialect/abstract.ts
2661
+ var Dialect = class {
2662
+ /**
2663
+ * Compiles a SELECT query AST to SQL
2664
+ * @param ast - Query AST to compile
2665
+ * @returns Compiled query with SQL and parameters
2666
+ */
2667
+ compileSelect(ast) {
2668
+ const ctx = this.createCompilerContext();
2669
+ const rawSql = this.compileSelectAst(ast, ctx).trim();
2670
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
2671
+ return {
2672
+ sql,
2673
+ params: [...ctx.params]
2674
+ };
2675
+ }
2676
+ compileInsert(ast) {
2677
+ const ctx = this.createCompilerContext();
2678
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
2679
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
2680
+ return {
2681
+ sql,
2682
+ params: [...ctx.params]
2683
+ };
2684
+ }
2685
+ compileUpdate(ast) {
2686
+ const ctx = this.createCompilerContext();
2687
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
2688
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
2689
+ return {
2690
+ sql,
2691
+ params: [...ctx.params]
2692
+ };
2693
+ }
2694
+ compileDelete(ast) {
2695
+ const ctx = this.createCompilerContext();
2696
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
2697
+ const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
2698
+ return {
2699
+ sql,
2700
+ params: [...ctx.params]
2701
+ };
2702
+ }
2703
+ /**
2704
+ * Compiles a WHERE clause
2705
+ * @param where - WHERE expression
2706
+ * @param ctx - Compiler context
2707
+ * @returns SQL WHERE clause or empty string
2708
+ */
2709
+ compileWhere(where, ctx) {
2710
+ if (!where) return "";
2711
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
2712
+ }
2713
+ compileReturning(returning, ctx) {
2714
+ if (!returning || returning.length === 0) return "";
2715
+ throw new Error("RETURNING is not supported by this dialect.");
2716
+ }
2717
+ /**
2718
+ * Generates subquery for EXISTS expressions
2719
+ * Rule: Always forces SELECT 1, ignoring column list
2720
+ * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
2721
+ * Does not add ';' at the end
2722
+ * @param ast - Query AST
2723
+ * @param ctx - Compiler context
2724
+ * @returns SQL for EXISTS subquery
2725
+ */
2726
+ compileSelectForExists(ast, ctx) {
2727
+ const full = this.compileSelectAst(ast, ctx).trim().replace(/;$/, "");
2728
+ const upper = full.toUpperCase();
2729
+ const fromIndex = upper.indexOf(" FROM ");
2730
+ if (fromIndex === -1) {
2731
+ return full;
2732
+ }
2733
+ const tail = full.slice(fromIndex);
2734
+ return `SELECT 1${tail}`;
2735
+ }
2736
+ /**
2737
+ * Creates a new compiler context
2738
+ * @returns Compiler context with parameter management
2739
+ */
2740
+ createCompilerContext() {
2741
+ const params = [];
2742
+ let counter = 0;
2743
+ return {
2744
+ params,
2745
+ addParameter: (value) => {
2746
+ counter += 1;
2747
+ params.push(value);
2748
+ return this.formatPlaceholder(counter);
2749
+ }
2750
+ };
2751
+ }
2752
+ /**
2753
+ * Formats a parameter placeholder
2754
+ * @param index - Parameter index
2755
+ * @returns Formatted placeholder string
2756
+ */
2757
+ formatPlaceholder(index) {
2758
+ return "?";
2759
+ }
2760
+ constructor() {
2761
+ this.expressionCompilers = /* @__PURE__ */ new Map();
2762
+ this.operandCompilers = /* @__PURE__ */ new Map();
2763
+ this.registerDefaultOperandCompilers();
2764
+ this.registerDefaultExpressionCompilers();
2765
+ }
2766
+ /**
2767
+ * Registers an expression compiler for a specific node type
2768
+ * @param type - Expression node type
2769
+ * @param compiler - Compiler function
2770
+ */
2771
+ registerExpressionCompiler(type, compiler) {
2772
+ this.expressionCompilers.set(type, compiler);
2773
+ }
2774
+ /**
2775
+ * Registers an operand compiler for a specific node type
2776
+ * @param type - Operand node type
2777
+ * @param compiler - Compiler function
2778
+ */
2779
+ registerOperandCompiler(type, compiler) {
2780
+ this.operandCompilers.set(type, compiler);
2781
+ }
2782
+ /**
2783
+ * Compiles an expression node
2784
+ * @param node - Expression node to compile
2785
+ * @param ctx - Compiler context
2786
+ * @returns Compiled SQL expression
2787
+ */
2788
+ compileExpression(node, ctx) {
2789
+ const compiler = this.expressionCompilers.get(node.type);
2790
+ if (!compiler) {
2791
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
2792
+ }
2793
+ return compiler(node, ctx);
2794
+ }
2795
+ /**
2796
+ * Compiles an operand node
2797
+ * @param node - Operand node to compile
2798
+ * @param ctx - Compiler context
2799
+ * @returns Compiled SQL operand
2800
+ */
2801
+ compileOperand(node, ctx) {
2802
+ const compiler = this.operandCompilers.get(node.type);
2803
+ if (!compiler) {
2804
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
2805
+ }
2806
+ return compiler(node, ctx);
2807
+ }
2808
+ registerDefaultExpressionCompilers() {
2809
+ this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
2810
+ const left = this.compileOperand(binary.left, ctx);
2811
+ const right = this.compileOperand(binary.right, ctx);
2812
+ const base = `${left} ${binary.operator} ${right}`;
2813
+ if (binary.escape) {
2814
+ const escapeOperand = this.compileOperand(binary.escape, ctx);
2815
+ return `${base} ESCAPE ${escapeOperand}`;
2816
+ }
2817
+ return base;
2818
+ });
2819
+ this.registerExpressionCompiler("LogicalExpression", (logical, ctx) => {
2820
+ if (logical.operands.length === 0) return "";
2821
+ const parts = logical.operands.map((op) => {
2822
+ const compiled = this.compileExpression(op, ctx);
2823
+ return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
2824
+ });
2825
+ return parts.join(` ${logical.operator} `);
2826
+ });
2827
+ this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
2828
+ const left = this.compileOperand(nullExpr.left, ctx);
2829
+ return `${left} ${nullExpr.operator}`;
2830
+ });
2831
+ this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
2832
+ const left = this.compileOperand(inExpr.left, ctx);
2833
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
2834
+ return `${left} ${inExpr.operator} (${values})`;
2835
+ });
2836
+ this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
2837
+ const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
2838
+ return `${existsExpr.operator} (${subquerySql})`;
2839
+ });
2840
+ this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
2841
+ const left = this.compileOperand(betweenExpr.left, ctx);
2842
+ const lower = this.compileOperand(betweenExpr.lower, ctx);
2843
+ const upper = this.compileOperand(betweenExpr.upper, ctx);
2844
+ return `${left} ${betweenExpr.operator} ${lower} AND ${upper}`;
2845
+ });
2846
+ }
2847
+ registerDefaultOperandCompilers() {
2848
+ this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
2849
+ this.registerOperandCompiler("Column", (column, _ctx) => {
2850
+ return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
2851
+ });
2852
+ this.registerOperandCompiler("Function", (fnNode, ctx) => {
2853
+ const args = fnNode.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
2854
+ return `${fnNode.name}(${args})`;
2855
+ });
2856
+ this.registerOperandCompiler("JsonPath", (path, _ctx) => this.compileJsonPath(path));
2857
+ this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
2858
+ const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
2859
+ return `(${sql})`;
2860
+ });
2861
+ this.registerOperandCompiler("CaseExpression", (node, ctx) => {
2862
+ const parts = ["CASE"];
2863
+ for (const { when, then } of node.conditions) {
2864
+ parts.push(`WHEN ${this.compileExpression(when, ctx)} THEN ${this.compileOperand(then, ctx)}`);
2865
+ }
2866
+ if (node.else) {
2867
+ parts.push(`ELSE ${this.compileOperand(node.else, ctx)}`);
2868
+ }
2869
+ parts.push("END");
2870
+ return parts.join(" ");
2871
+ });
2872
+ this.registerOperandCompiler("WindowFunction", (node, ctx) => {
2873
+ let result = `${node.name}(`;
2874
+ if (node.args.length > 0) {
2875
+ result += node.args.map((arg) => this.compileOperand(arg, ctx)).join(", ");
2876
+ }
2877
+ result += ") OVER (";
2878
+ const parts = [];
2879
+ if (node.partitionBy && node.partitionBy.length > 0) {
2880
+ const partitionClause = "PARTITION BY " + node.partitionBy.map(
2881
+ (col2) => `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`
2882
+ ).join(", ");
2883
+ parts.push(partitionClause);
2884
+ }
2885
+ if (node.orderBy && node.orderBy.length > 0) {
2886
+ const orderClause = "ORDER BY " + node.orderBy.map(
2887
+ (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
2888
+ ).join(", ");
2889
+ parts.push(orderClause);
2890
+ }
2891
+ result += parts.join(" ");
2892
+ result += ")";
2893
+ return result;
2894
+ });
2895
+ }
2896
+ // Default fallback, should be overridden by dialects if supported
2897
+ compileJsonPath(node) {
2898
+ throw new Error("JSON Path not supported by this dialect");
2899
+ }
2900
+ };
2901
+
2902
+ // src/core/dialect/mysql/index.ts
2903
+ var MySqlDialect = class extends Dialect {
2904
+ /**
2905
+ * Creates a new MySqlDialect instance
2906
+ */
2907
+ constructor() {
2908
+ super();
2909
+ }
2910
+ /**
2911
+ * Quotes an identifier using MySQL backtick syntax
2912
+ * @param id - Identifier to quote
2913
+ * @returns Quoted identifier
2914
+ */
2915
+ quoteIdentifier(id) {
2916
+ return `\`${id}\``;
2917
+ }
2918
+ /**
2919
+ * Compiles JSON path expression using MySQL syntax
2920
+ * @param node - JSON path node
2921
+ * @returns MySQL JSON path expression
2922
+ */
2923
+ compileJsonPath(node) {
2924
+ const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
2925
+ return `${col2}->'${node.path}'`;
2926
+ }
2927
+ /**
2928
+ * Compiles SELECT query AST to MySQL SQL
2929
+ * @param ast - Query AST
2930
+ * @param ctx - Compiler context
2931
+ * @returns MySQL SQL string
2932
+ */
2933
+ compileSelectAst(ast, ctx) {
2934
+ const columns = ast.columns.map((c) => {
2935
+ let expr = "";
2936
+ if (c.type === "Function") {
2937
+ expr = this.compileOperand(c, ctx);
2938
+ } else if (c.type === "Column") {
2939
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
2940
+ } else if (c.type === "ScalarSubquery") {
2941
+ expr = this.compileOperand(c, ctx);
2942
+ } else if (c.type === "WindowFunction") {
2943
+ expr = this.compileOperand(c, ctx);
2944
+ }
2945
+ if (c.alias) {
2946
+ if (c.alias.includes("(")) return c.alias;
2947
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
2948
+ }
2949
+ return expr;
2950
+ }).join(", ");
2951
+ const distinct = ast.distinct ? "DISTINCT " : "";
2952
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
2953
+ const joins = ast.joins.map((j) => {
2954
+ const table = this.quoteIdentifier(j.table.name);
2955
+ const cond = this.compileExpression(j.condition, ctx);
2956
+ return `${j.kind} JOIN ${table} ON ${cond}`;
2957
+ }).join(" ");
2958
+ const whereClause = this.compileWhere(ast.where, ctx);
2959
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
2960
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
2961
+ const orderBy = ast.orderBy && ast.orderBy.length > 0 ? " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ") : "";
2962
+ const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
2963
+ const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
2964
+ const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
2965
+ const hasRecursive = ast.ctes.some((cte) => cte.recursive);
2966
+ const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
2967
+ const cteDefs = ast.ctes.map((cte) => {
2968
+ const name = this.quoteIdentifier(cte.name);
2969
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
2970
+ const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
2971
+ return `${name}${cols} AS (${query})`;
2972
+ }).join(", ");
2973
+ return prefix + cteDefs + " ";
2974
+ })() : "";
2975
+ return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
2976
+ }
2977
+ compileInsertAst(ast, ctx) {
2978
+ const table = this.quoteIdentifier(ast.into.name);
2979
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
2980
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
2981
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
2982
+ }
2983
+ compileUpdateAst(ast, ctx) {
2984
+ const table = this.quoteIdentifier(ast.table.name);
2985
+ const assignments = ast.set.map((assignment) => {
2986
+ const col2 = assignment.column;
2987
+ const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
2988
+ const value = this.compileOperand(assignment.value, ctx);
2989
+ return `${target} = ${value}`;
2990
+ }).join(", ");
2991
+ const whereClause = this.compileWhere(ast.where, ctx);
2992
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
2993
+ }
2994
+ compileDeleteAst(ast, ctx) {
2995
+ const table = this.quoteIdentifier(ast.from.name);
2996
+ const whereClause = this.compileWhere(ast.where, ctx);
2997
+ return `DELETE FROM ${table}${whereClause};`;
2998
+ }
2999
+ };
3000
+
3001
+ // src/core/dialect/mssql/index.ts
3002
+ var SqlServerDialect = class extends Dialect {
3003
+ /**
3004
+ * Creates a new SqlServerDialect instance
3005
+ */
3006
+ constructor() {
3007
+ super();
3008
+ }
3009
+ /**
3010
+ * Quotes an identifier using SQL Server bracket syntax
3011
+ * @param id - Identifier to quote
3012
+ * @returns Quoted identifier
3013
+ */
3014
+ quoteIdentifier(id) {
3015
+ return `[${id}]`;
3016
+ }
3017
+ /**
3018
+ * Compiles JSON path expression using SQL Server syntax
3019
+ * @param node - JSON path node
3020
+ * @returns SQL Server JSON path expression
3021
+ */
3022
+ compileJsonPath(node) {
3023
+ const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
3024
+ return `JSON_VALUE(${col2}, '${node.path}')`;
3025
+ }
3026
+ /**
3027
+ * Formats parameter placeholders using SQL Server named parameter syntax
3028
+ * @param index - Parameter index
3029
+ * @returns Named parameter placeholder
3030
+ */
3031
+ formatPlaceholder(index) {
3032
+ return `@p${index}`;
3033
+ }
3034
+ /**
3035
+ * Compiles SELECT query AST to SQL Server SQL
3036
+ * @param ast - Query AST
3037
+ * @param ctx - Compiler context
3038
+ * @returns SQL Server SQL string
3039
+ */
3040
+ compileSelectAst(ast, ctx) {
3041
+ const columns = ast.columns.map((c) => {
3042
+ let expr = "";
3043
+ if (c.type === "Function") {
3044
+ expr = this.compileOperand(c, ctx);
3045
+ } else if (c.type === "Column") {
3046
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
3047
+ } else if (c.type === "ScalarSubquery") {
3048
+ expr = this.compileOperand(c, ctx);
3049
+ } else if (c.type === "WindowFunction") {
3050
+ expr = this.compileOperand(c, ctx);
3051
+ }
3052
+ if (c.alias) {
3053
+ if (c.alias.includes("(")) return c.alias;
3054
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
3055
+ }
3056
+ return expr;
3057
+ }).join(", ");
3058
+ const distinct = ast.distinct ? "DISTINCT " : "";
3059
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
3060
+ const joins = ast.joins.map((j) => {
3061
+ const table = this.quoteIdentifier(j.table.name);
3062
+ const cond = this.compileExpression(j.condition, ctx);
3063
+ return `${j.kind} JOIN ${table} ON ${cond}`;
3064
+ }).join(" ");
3065
+ const whereClause = this.compileWhere(ast.where, ctx);
3066
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
3067
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
3068
+ const orderBy = ast.orderBy && ast.orderBy.length > 0 ? " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ") : "";
3069
+ let pagination = "";
3070
+ if (ast.limit || ast.offset) {
3071
+ const off = ast.offset || 0;
3072
+ const orderClause = orderBy || " ORDER BY (SELECT NULL)";
3073
+ pagination = `${orderClause} OFFSET ${off} ROWS`;
3074
+ if (ast.limit) {
3075
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
3076
+ }
3077
+ return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination};`;
3078
+ }
3079
+ const ctes = ast.ctes && ast.ctes.length > 0 ? "WITH " + ast.ctes.map((cte) => {
3080
+ const name = this.quoteIdentifier(cte.name);
3081
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
3082
+ const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
3083
+ return `${name}${cols} AS (${query})`;
3084
+ }).join(", ") + " " : "";
3085
+ return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy};`;
3086
+ }
3087
+ compileInsertAst(ast, ctx) {
3088
+ const table = this.quoteIdentifier(ast.into.name);
3089
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
3090
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
3091
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
3092
+ }
3093
+ compileUpdateAst(ast, ctx) {
3094
+ const table = this.quoteIdentifier(ast.table.name);
3095
+ const assignments = ast.set.map((assignment) => {
3096
+ const col2 = assignment.column;
3097
+ const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
3098
+ const value = this.compileOperand(assignment.value, ctx);
3099
+ return `${target} = ${value}`;
3100
+ }).join(", ");
3101
+ const whereClause = this.compileWhere(ast.where, ctx);
3102
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
3103
+ }
3104
+ compileDeleteAst(ast, ctx) {
3105
+ const table = this.quoteIdentifier(ast.from.name);
3106
+ const whereClause = this.compileWhere(ast.where, ctx);
3107
+ return `DELETE FROM ${table}${whereClause};`;
3108
+ }
3109
+ };
3110
+
3111
+ // src/core/dialect/sqlite/index.ts
3112
+ var SqliteDialect = class extends Dialect {
3113
+ /**
3114
+ * Creates a new SqliteDialect instance
3115
+ */
3116
+ constructor() {
3117
+ super();
3118
+ }
3119
+ /**
3120
+ * Quotes an identifier using SQLite double-quote syntax
3121
+ * @param id - Identifier to quote
3122
+ * @returns Quoted identifier
3123
+ */
3124
+ quoteIdentifier(id) {
3125
+ return `"${id}"`;
3126
+ }
3127
+ /**
3128
+ * Compiles JSON path expression using SQLite syntax
3129
+ * @param node - JSON path node
3130
+ * @returns SQLite JSON path expression
3131
+ */
3132
+ compileJsonPath(node) {
3133
+ const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
3134
+ return `json_extract(${col2}, '${node.path}')`;
3135
+ }
3136
+ /**
3137
+ * Compiles SELECT query AST to SQLite SQL
3138
+ * @param ast - Query AST
3139
+ * @param ctx - Compiler context
3140
+ * @returns SQLite SQL string
3141
+ */
3142
+ compileSelectAst(ast, ctx) {
3143
+ const columns = ast.columns.map((c) => {
3144
+ let expr = "";
3145
+ if (c.type === "Function") {
3146
+ expr = this.compileOperand(c, ctx);
3147
+ } else if (c.type === "Column") {
3148
+ expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
3149
+ } else if (c.type === "ScalarSubquery") {
3150
+ expr = this.compileOperand(c, ctx);
3151
+ } else if (c.type === "CaseExpression") {
3152
+ expr = this.compileOperand(c, ctx);
3153
+ } else if (c.type === "WindowFunction") {
3154
+ expr = this.compileOperand(c, ctx);
3155
+ }
3156
+ if (c.alias) {
3157
+ if (c.alias.includes("(")) return c.alias;
3158
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
3159
+ }
3160
+ return expr;
3161
+ }).join(", ");
3162
+ const distinct = ast.distinct ? "DISTINCT " : "";
3163
+ const from = `${this.quoteIdentifier(ast.from.name)}`;
3164
+ const joins = ast.joins.map((j) => {
3165
+ const table = this.quoteIdentifier(j.table.name);
3166
+ const cond = this.compileExpression(j.condition, ctx);
3167
+ return `${j.kind} JOIN ${table} ON ${cond}`;
3168
+ }).join(" ");
3169
+ const whereClause = this.compileWhere(ast.where, ctx);
3170
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
3171
+ const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
3172
+ const orderBy = ast.orderBy && ast.orderBy.length > 0 ? " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ") : "";
3173
+ const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
3174
+ const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
3175
+ const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
3176
+ const hasRecursive = ast.ctes.some((cte) => cte.recursive);
3177
+ const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
3178
+ const cteDefs = ast.ctes.map((cte) => {
3179
+ const name = this.quoteIdentifier(cte.name);
3180
+ const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
3181
+ const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
3182
+ return `${name}${cols} AS (${query})`;
3183
+ }).join(", ");
3184
+ return prefix + cteDefs + " ";
3185
+ })() : "";
3186
+ return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
3187
+ }
3188
+ compileInsertAst(ast, ctx) {
3189
+ const table = this.quoteIdentifier(ast.into.name);
3190
+ const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
3191
+ const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
3192
+ const returning = this.compileReturning(ast.returning, ctx);
3193
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
3194
+ }
3195
+ compileUpdateAst(ast, ctx) {
3196
+ const table = this.quoteIdentifier(ast.table.name);
3197
+ const assignments = ast.set.map((assignment) => {
3198
+ const col2 = assignment.column;
3199
+ const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
3200
+ const value = this.compileOperand(assignment.value, ctx);
3201
+ return `${target} = ${value}`;
3202
+ }).join(", ");
3203
+ const whereClause = this.compileWhere(ast.where, ctx);
3204
+ const returning = this.compileReturning(ast.returning, ctx);
3205
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
3206
+ }
3207
+ compileDeleteAst(ast, ctx) {
3208
+ const table = this.quoteIdentifier(ast.from.name);
3209
+ const whereClause = this.compileWhere(ast.where, ctx);
3210
+ const returning = this.compileReturning(ast.returning, ctx);
3211
+ return `DELETE FROM ${table}${whereClause}${returning};`;
3212
+ }
3213
+ compileReturning(returning, ctx) {
3214
+ if (!returning || returning.length === 0) return "";
3215
+ const columns = returning.map((column) => {
3216
+ const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : "";
3217
+ return `${tablePart}${this.quoteIdentifier(column.name)}`;
3218
+ }).join(", ");
3219
+ return ` RETURNING ${columns}`;
3220
+ }
3221
+ };
3222
+
3223
+ // src/orm/als.ts
3224
+ var AsyncLocalStorage = class {
3225
+ /**
3226
+ * Executes a callback with the specified store value
3227
+ * @param store - Value to store during callback execution
3228
+ * @param callback - Function to execute with the store value
3229
+ * @returns Result of the callback function
3230
+ */
3231
+ run(store, callback) {
3232
+ this.store = store;
3233
+ try {
3234
+ return callback();
3235
+ } finally {
3236
+ this.store = void 0;
3237
+ }
3238
+ }
3239
+ /**
3240
+ * Gets the currently stored value
3241
+ * @returns Current store value or undefined if none exists
3242
+ */
3243
+ getStore() {
3244
+ return this.store;
3245
+ }
3246
+ };
3247
+
3248
+ // src/core/sql/sql-operator-config.ts
3249
+ var SQL_OPERATOR_REGISTRY = {
3250
+ [SQL_OPERATORS.EQUALS]: { sql: SQL_OPERATORS.EQUALS, tsName: "eq" },
3251
+ [SQL_OPERATORS.NOT_EQUALS]: { sql: SQL_OPERATORS.NOT_EQUALS, tsName: "neq" },
3252
+ [SQL_OPERATORS.GREATER_THAN]: { sql: SQL_OPERATORS.GREATER_THAN, tsName: "gt" },
3253
+ [SQL_OPERATORS.GREATER_OR_EQUAL]: { sql: SQL_OPERATORS.GREATER_OR_EQUAL, tsName: "gte" },
3254
+ [SQL_OPERATORS.LESS_THAN]: { sql: SQL_OPERATORS.LESS_THAN, tsName: "lt" },
3255
+ [SQL_OPERATORS.LESS_OR_EQUAL]: { sql: SQL_OPERATORS.LESS_OR_EQUAL, tsName: "lte" },
3256
+ [SQL_OPERATORS.LIKE]: { sql: SQL_OPERATORS.LIKE, tsName: "like" },
3257
+ [SQL_OPERATORS.NOT_LIKE]: { sql: SQL_OPERATORS.NOT_LIKE, tsName: "notLike" },
3258
+ [SQL_OPERATORS.IN]: { sql: SQL_OPERATORS.IN, tsName: "inList" },
3259
+ [SQL_OPERATORS.NOT_IN]: { sql: SQL_OPERATORS.NOT_IN, tsName: "notInList" },
3260
+ [SQL_OPERATORS.IS_NULL]: { sql: SQL_OPERATORS.IS_NULL, tsName: "isNull" },
3261
+ [SQL_OPERATORS.IS_NOT_NULL]: { sql: SQL_OPERATORS.IS_NOT_NULL, tsName: "isNotNull" },
3262
+ [SQL_OPERATORS.AND]: { sql: SQL_OPERATORS.AND, tsName: "and" },
3263
+ [SQL_OPERATORS.OR]: { sql: SQL_OPERATORS.OR, tsName: "or" },
3264
+ [SQL_OPERATORS.BETWEEN]: { sql: SQL_OPERATORS.BETWEEN, tsName: "between" },
3265
+ [SQL_OPERATORS.NOT_BETWEEN]: { sql: SQL_OPERATORS.NOT_BETWEEN, tsName: "notBetween" },
3266
+ [SQL_OPERATORS.EXISTS]: { sql: SQL_OPERATORS.EXISTS, tsName: "exists" },
3267
+ [SQL_OPERATORS.NOT_EXISTS]: { sql: SQL_OPERATORS.NOT_EXISTS, tsName: "notExists" }
3268
+ };
3269
+
3270
+ // src/codegen/typescript.ts
3271
+ var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
3272
+ var assertNever2 = (value) => {
3273
+ throw new Error(`Unhandled SQL operator: ${value}`);
3274
+ };
3275
+ var TypeScriptGenerator = class {
3276
+ /**
3277
+ * Generates TypeScript code from a query AST
3278
+ * @param ast - Query AST to generate code from
3279
+ * @returns Generated TypeScript code
3280
+ */
3281
+ generate(ast) {
3282
+ const chainLines = this.buildSelectLines(ast);
3283
+ const lines = chainLines.map((line, index) => index === 0 ? `const query = ${line}` : line);
3284
+ lines.push(";", "", "await query.execute();");
3285
+ return lines.join("\n");
3286
+ }
3287
+ /**
3288
+ * Builds TypeScript method chain lines from query AST
3289
+ * @param ast - Query AST
3290
+ * @returns Array of TypeScript method chain lines
3291
+ */
3292
+ buildSelectLines(ast) {
3293
+ const lines = [];
3294
+ const hydration = ast.meta?.hydration;
3295
+ const hydratedRelations = new Set(hydration?.relations?.map((r) => r.name) ?? []);
3296
+ const selections = ast.columns.filter((col2) => !(hydration && isRelationAlias(col2.alias))).map((col2) => {
3297
+ const key = col2.alias || col2.name;
3298
+ const operand = col2;
3299
+ return `${key}: ${this.printOperand(operand)}`;
3300
+ });
3301
+ lines.push(`db.select({`);
3302
+ selections.forEach((sel, index) => {
3303
+ lines.push(` ${sel}${index < selections.length - 1 ? "," : ""}`);
3304
+ });
3305
+ lines.push(`})`);
3306
+ lines.push(`.from(${capitalize(ast.from.name)})`);
3307
+ if (ast.distinct && ast.distinct.length) {
3308
+ const cols = ast.distinct.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
3309
+ lines.push(`.distinct(${cols})`);
3310
+ }
3311
+ ast.joins.forEach((join) => {
3312
+ if (join.relationName && hydratedRelations.has(join.relationName)) {
3313
+ return;
3314
+ }
3315
+ if (join.relationName) {
3316
+ if (join.kind === "INNER") {
3317
+ lines.push(`.joinRelation('${join.relationName}')`);
3318
+ } else {
3319
+ lines.push(`.joinRelation('${join.relationName}', '${join.kind}')`);
3320
+ }
3321
+ } else {
3322
+ const table = capitalize(join.table.name);
3323
+ const cond = this.printExpression(join.condition);
3324
+ let method = "innerJoin";
3325
+ if (join.kind === "LEFT") method = "leftJoin";
3326
+ if (join.kind === "RIGHT") method = "rightJoin";
3327
+ lines.push(`.${method}(${table}, ${cond})`);
3328
+ }
3329
+ });
3330
+ if (hydration?.relations?.length) {
3331
+ hydration.relations.forEach((rel) => {
3332
+ const options = [];
3333
+ if (rel.columns.length) options.push(`columns: [${rel.columns.map((c) => `'${c}'`).join(", ")}]`);
3334
+ if (rel.aliasPrefix !== rel.name) options.push(`aliasPrefix: '${rel.aliasPrefix}'`);
3335
+ const opts = options.length ? `, { ${options.join(", ")} }` : "";
3336
+ lines.push(`.include('${rel.name}'${opts})`);
3337
+ });
3338
+ }
3339
+ if (ast.where) {
3340
+ lines.push(`.where(${this.printExpression(ast.where)})`);
3341
+ }
3342
+ if (ast.groupBy && ast.groupBy.length) {
3343
+ const cols = ast.groupBy.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
3344
+ lines.push(`.groupBy(${cols})`);
3345
+ }
3346
+ if (ast.having) {
3347
+ lines.push(`.having(${this.printExpression(ast.having)})`);
3348
+ }
3349
+ if (ast.orderBy && ast.orderBy.length) {
3350
+ ast.orderBy.forEach((o) => {
3351
+ lines.push(`.orderBy(${capitalize(o.column.table)}.${o.column.name}, '${o.direction}')`);
3352
+ });
3353
+ }
3354
+ if (ast.limit) lines.push(`.limit(${ast.limit})`);
3355
+ if (ast.offset) lines.push(`.offset(${ast.offset})`);
3356
+ return lines;
3357
+ }
3358
+ /**
3359
+ * Prints an expression node to TypeScript code
3360
+ * @param expr - Expression node to print
3361
+ * @returns TypeScript code representation
3362
+ */
3363
+ printExpression(expr) {
3364
+ return visitExpression(expr, this);
3365
+ }
3366
+ /**
3367
+ * Prints an operand node to TypeScript code
3368
+ * @param node - Operand node to print
3369
+ * @returns TypeScript code representation
3370
+ */
3371
+ printOperand(node) {
3372
+ return visitOperand(node, this);
3373
+ }
3374
+ visitBinaryExpression(binary) {
3375
+ return this.printBinaryExpression(binary);
3376
+ }
3377
+ visitLogicalExpression(logical) {
3378
+ return this.printLogicalExpression(logical);
3379
+ }
3380
+ visitNullExpression(nullExpr) {
3381
+ return this.printNullExpression(nullExpr);
3382
+ }
3383
+ visitInExpression(inExpr) {
3384
+ return this.printInExpression(inExpr);
3385
+ }
3386
+ visitExistsExpression(existsExpr) {
3387
+ return this.printExistsExpression(existsExpr);
3388
+ }
3389
+ visitBetweenExpression(betweenExpr) {
3390
+ return this.printBetweenExpression(betweenExpr);
3391
+ }
3392
+ visitColumn(node) {
3393
+ return this.printColumnOperand(node);
3394
+ }
3395
+ visitLiteral(node) {
3396
+ return this.printLiteralOperand(node);
3397
+ }
3398
+ visitFunction(node) {
3399
+ return this.printFunctionOperand(node);
3400
+ }
3401
+ visitJsonPath(node) {
3402
+ return this.printJsonPathOperand(node);
3403
+ }
3404
+ visitScalarSubquery(node) {
3405
+ return this.printScalarSubqueryOperand(node);
3406
+ }
3407
+ visitCaseExpression(node) {
3408
+ return this.printCaseExpressionOperand(node);
3409
+ }
3410
+ visitWindowFunction(node) {
3411
+ return this.printWindowFunctionOperand(node);
3412
+ }
3413
+ /**
3414
+ * Prints a binary expression to TypeScript code
3415
+ * @param binary - Binary expression node
3416
+ * @returns TypeScript code representation
3417
+ */
3418
+ printBinaryExpression(binary) {
3419
+ const left = this.printOperand(binary.left);
3420
+ const right = this.printOperand(binary.right);
3421
+ const fn = this.mapOp(binary.operator);
3422
+ const args = [left, right];
3423
+ if (binary.escape) {
3424
+ args.push(this.printOperand(binary.escape));
3425
+ }
3426
+ return `${fn}(${args.join(", ")})`;
3427
+ }
3428
+ /**
3429
+ * Prints a logical expression to TypeScript code
3430
+ * @param logical - Logical expression node
3431
+ * @returns TypeScript code representation
3432
+ */
3433
+ printLogicalExpression(logical) {
3434
+ if (logical.operands.length === 0) return "";
3435
+ const parts = logical.operands.map((op) => {
3436
+ const compiled = this.printExpression(op);
3437
+ return op.type === "LogicalExpression" ? `(${compiled})` : compiled;
3438
+ });
3439
+ return `${this.mapOp(logical.operator)}(
3440
+ ${parts.join(",\n ")}
3441
+ )`;
3442
+ }
3443
+ /**
3444
+ * Prints an IN expression to TypeScript code
3445
+ * @param inExpr - IN expression node
3446
+ * @returns TypeScript code representation
3447
+ */
3448
+ printInExpression(inExpr) {
3449
+ const left = this.printOperand(inExpr.left);
3450
+ const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
3451
+ const fn = this.mapOp(inExpr.operator);
3452
+ return `${fn}(${left}, [${values}])`;
3453
+ }
3454
+ /**
3455
+ * Prints a null expression to TypeScript code
3456
+ * @param nullExpr - Null expression node
3457
+ * @returns TypeScript code representation
3458
+ */
3459
+ printNullExpression(nullExpr) {
3460
+ const left = this.printOperand(nullExpr.left);
3461
+ const fn = this.mapOp(nullExpr.operator);
3462
+ return `${fn}(${left})`;
3463
+ }
3464
+ /**
3465
+ * Prints a BETWEEN expression to TypeScript code
3466
+ * @param betweenExpr - BETWEEN expression node
3467
+ * @returns TypeScript code representation
3468
+ */
3469
+ printBetweenExpression(betweenExpr) {
3470
+ const left = this.printOperand(betweenExpr.left);
3471
+ const lower = this.printOperand(betweenExpr.lower);
3472
+ const upper = this.printOperand(betweenExpr.upper);
3473
+ return `${this.mapOp(betweenExpr.operator)}(${left}, ${lower}, ${upper})`;
3474
+ }
3475
+ /**
3476
+ * Prints an EXISTS expression to TypeScript code
3477
+ * @param existsExpr - EXISTS expression node
3478
+ * @returns TypeScript code representation
3479
+ */
3480
+ printExistsExpression(existsExpr) {
3481
+ const subquery = this.inlineChain(this.buildSelectLines(existsExpr.subquery));
3482
+ return `${this.mapOp(existsExpr.operator)}(${subquery})`;
3483
+ }
3484
+ /**
3485
+ * Prints a column operand to TypeScript code
3486
+ * @param column - Column node
3487
+ * @returns TypeScript code representation
3488
+ */
3489
+ printColumnOperand(column) {
3490
+ return `${capitalize(column.table)}.${column.name}`;
3491
+ }
3492
+ /**
3493
+ * Prints a literal operand to TypeScript code
3494
+ * @param literal - Literal node
3495
+ * @returns TypeScript code representation
3496
+ */
3497
+ printLiteralOperand(literal) {
3498
+ if (literal.value === null) return "null";
3499
+ return typeof literal.value === "string" ? `'${literal.value}'` : String(literal.value);
3500
+ }
3501
+ /**
3502
+ * Prints a function operand to TypeScript code
3503
+ * @param fn - Function node
3504
+ * @returns TypeScript code representation
3505
+ */
3506
+ printFunctionOperand(fn) {
3507
+ const args = fn.args.map((a) => this.printOperand(a)).join(", ");
3508
+ return `${fn.name.toLowerCase()}(${args})`;
3509
+ }
3510
+ /**
3511
+ * Prints a JSON path operand to TypeScript code
3512
+ * @param json - JSON path node
3513
+ * @returns TypeScript code representation
3514
+ */
3515
+ printJsonPathOperand(json) {
3516
+ return `jsonPath(${capitalize(json.column.table)}.${json.column.name}, '${json.path}')`;
3517
+ }
3518
+ /**
3519
+ * Prints a scalar subquery operand to TypeScript code
3520
+ * @param node - Scalar subquery node
3521
+ * @returns TypeScript code representation
3522
+ */
3523
+ printScalarSubqueryOperand(node) {
3524
+ const subquery = this.inlineChain(this.buildSelectLines(node.query));
3525
+ return `(${subquery})`;
3526
+ }
3527
+ /**
3528
+ * Prints a CASE expression operand to TypeScript code
3529
+ * @param node - CASE expression node
3530
+ * @returns TypeScript code representation
3531
+ */
3532
+ printCaseExpressionOperand(node) {
3533
+ const clauses = node.conditions.map(
3534
+ (condition) => `{ when: ${this.printExpression(condition.when)}, then: ${this.printOperand(condition.then)} }`
3535
+ );
3536
+ const elseValue = node.else ? `, ${this.printOperand(node.else)}` : "";
3537
+ return `caseWhen([${clauses.join(", ")}]${elseValue})`;
3538
+ }
3539
+ /**
3540
+ * Prints a window function operand to TypeScript code
3541
+ * @param node - Window function node
3542
+ * @returns TypeScript code representation
3543
+ */
3544
+ printWindowFunctionOperand(node) {
3545
+ let result = `${node.name}(`;
3546
+ if (node.args.length > 0) {
3547
+ result += node.args.map((arg) => this.printOperand(arg)).join(", ");
3548
+ }
3549
+ result += ") OVER (";
3550
+ const parts = [];
3551
+ if (node.partitionBy && node.partitionBy.length > 0) {
3552
+ const partitionClause = "PARTITION BY " + node.partitionBy.map((col2) => `${capitalize(col2.table)}.${col2.name}`).join(", ");
3553
+ parts.push(partitionClause);
3554
+ }
3555
+ if (node.orderBy && node.orderBy.length > 0) {
3556
+ const orderClause = "ORDER BY " + node.orderBy.map((o) => `${capitalize(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
3557
+ parts.push(orderClause);
3558
+ }
3559
+ result += parts.join(" ");
3560
+ result += ")";
3561
+ return result;
3562
+ }
3563
+ /**
3564
+ * Converts method chain lines to inline format
3565
+ * @param lines - Method chain lines
3566
+ * @returns Inline method chain string
3567
+ */
3568
+ inlineChain(lines) {
3569
+ return lines.map((line) => line.trim()).filter((line) => line.length > 0).join(" ");
3570
+ }
3571
+ /**
3572
+ * Maps SQL operators to TypeScript function names
3573
+ * @param op - SQL operator
3574
+ * @returns TypeScript function name
3575
+ */
3576
+ mapOp(op) {
3577
+ const config = SQL_OPERATOR_REGISTRY[op];
3578
+ if (!config) {
3579
+ return assertNever2(op);
3580
+ }
3581
+ return config.tsName;
3582
+ }
3583
+ };
3584
+
3585
+ // src/orm/domain-event-bus.ts
3586
+ var DomainEventBus = class {
3587
+ constructor(initialHandlers) {
3588
+ this.handlers = /* @__PURE__ */ new Map();
3589
+ const handlers = initialHandlers ?? {};
3590
+ Object.entries(handlers).forEach(([name, list]) => {
3591
+ this.handlers.set(name, [...list]);
3592
+ });
3593
+ }
3594
+ register(name, handler) {
3595
+ const existing = this.handlers.get(name) ?? [];
3596
+ existing.push(handler);
3597
+ this.handlers.set(name, existing);
3598
+ }
3599
+ async dispatch(trackedEntities, ctx) {
3600
+ for (const tracked of trackedEntities) {
3601
+ const entity = tracked.entity;
3602
+ if (!entity.domainEvents || !entity.domainEvents.length) continue;
3603
+ for (const event of entity.domainEvents) {
3604
+ const eventName = this.getEventName(event);
3605
+ const handlers = this.handlers.get(eventName);
3606
+ if (!handlers) continue;
3607
+ for (const handler of handlers) {
3608
+ await handler(event, ctx);
3609
+ }
3610
+ }
3611
+ entity.domainEvents = [];
3612
+ }
3613
+ }
3614
+ getEventName(event) {
3615
+ if (!event) return "Unknown";
3616
+ if (typeof event === "string") return event;
3617
+ return event.constructor?.name ?? "Unknown";
3618
+ }
3619
+ };
3620
+ var addDomainEvent = (entity, event) => {
3621
+ if (!entity.domainEvents) {
3622
+ entity.domainEvents = [];
3623
+ }
3624
+ entity.domainEvents.push(event);
3625
+ };
3626
+
3627
+ // src/orm/identity-map.ts
3628
+ var IdentityMap = class {
3629
+ constructor() {
3630
+ this.buckets = /* @__PURE__ */ new Map();
3631
+ }
3632
+ get bucketsMap() {
3633
+ return this.buckets;
3634
+ }
3635
+ getEntity(table, pk) {
3636
+ const bucket = this.buckets.get(table.name);
3637
+ return bucket?.get(this.toIdentityKey(pk))?.entity;
3638
+ }
3639
+ register(tracked) {
3640
+ if (tracked.pk == null) return;
3641
+ const bucket = this.buckets.get(tracked.table.name) ?? /* @__PURE__ */ new Map();
3642
+ bucket.set(this.toIdentityKey(tracked.pk), tracked);
3643
+ this.buckets.set(tracked.table.name, bucket);
3644
+ }
3645
+ remove(tracked) {
3646
+ if (tracked.pk == null) return;
3647
+ const bucket = this.buckets.get(tracked.table.name);
3648
+ bucket?.delete(this.toIdentityKey(tracked.pk));
3649
+ }
3650
+ getEntitiesForTable(table) {
3651
+ const bucket = this.buckets.get(table.name);
3652
+ return bucket ? Array.from(bucket.values()) : [];
3653
+ }
3654
+ toIdentityKey(pk) {
3655
+ return String(pk);
3656
+ }
3657
+ };
3658
+
3659
+ // src/orm/relation-change-processor.ts
3660
+ var RelationChangeProcessor = class {
3661
+ constructor(unitOfWork, dialect, executor) {
3662
+ this.unitOfWork = unitOfWork;
3663
+ this.dialect = dialect;
3664
+ this.executor = executor;
3665
+ this.relationChanges = [];
3666
+ }
3667
+ registerChange(entry) {
3668
+ this.relationChanges.push(entry);
3669
+ }
3670
+ async process() {
3671
+ if (!this.relationChanges.length) return;
3672
+ const entries = [...this.relationChanges];
3673
+ this.relationChanges.length = 0;
3674
+ for (const entry of entries) {
3675
+ switch (entry.relation.type) {
3676
+ case RelationKinds.HasMany:
3677
+ await this.handleHasManyChange(entry);
3678
+ break;
3679
+ case RelationKinds.BelongsToMany:
3680
+ await this.handleBelongsToManyChange(entry);
3681
+ break;
3682
+ case RelationKinds.BelongsTo:
3683
+ await this.handleBelongsToChange(entry);
3684
+ break;
3685
+ }
3686
+ }
3687
+ }
3688
+ async handleHasManyChange(entry) {
3689
+ const relation = entry.relation;
3690
+ const target = entry.change.entity;
3691
+ if (!target) return;
3692
+ const tracked = this.unitOfWork.findTracked(target);
3693
+ if (!tracked) return;
3694
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
3695
+ const rootValue = entry.root[localKey];
3696
+ if (rootValue === void 0 || rootValue === null) return;
3697
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
3698
+ this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
3699
+ this.unitOfWork.markDirty(tracked.entity);
3700
+ return;
3701
+ }
3702
+ if (entry.change.kind === "remove") {
3703
+ this.detachHasManyChild(tracked.entity, relation);
3704
+ }
3705
+ }
3706
+ async handleBelongsToChange(_entry) {
3707
+ }
3708
+ async handleBelongsToManyChange(entry) {
3709
+ const relation = entry.relation;
3710
+ const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
3711
+ const rootId = entry.root[rootKey];
3712
+ if (rootId === void 0 || rootId === null) return;
3713
+ const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
3714
+ if (targetId === null) return;
3715
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
3716
+ await this.insertPivotRow(relation, rootId, targetId);
3717
+ return;
3718
+ }
3719
+ if (entry.change.kind === "detach" || entry.change.kind === "remove") {
3720
+ await this.deletePivotRow(relation, rootId, targetId);
3721
+ if (relation.cascade === "all" || relation.cascade === "remove") {
3722
+ this.unitOfWork.markRemoved(entry.change.entity);
3723
+ }
3724
+ }
3725
+ }
3726
+ assignHasManyForeignKey(child, relation, rootValue) {
3727
+ const current = child[relation.foreignKey];
3728
+ if (current === rootValue) return;
3729
+ child[relation.foreignKey] = rootValue;
3730
+ }
3731
+ detachHasManyChild(child, relation) {
3732
+ if (relation.cascade === "all" || relation.cascade === "remove") {
3733
+ this.unitOfWork.markRemoved(child);
3734
+ return;
3735
+ }
3736
+ child[relation.foreignKey] = null;
3737
+ this.unitOfWork.markDirty(child);
3738
+ }
3739
+ async insertPivotRow(relation, rootId, targetId) {
3740
+ const payload = {
3741
+ [relation.pivotForeignKeyToRoot]: rootId,
3742
+ [relation.pivotForeignKeyToTarget]: targetId
3743
+ };
3744
+ const builder = new InsertQueryBuilder(relation.pivotTable).values(payload);
3745
+ const compiled = builder.compile(this.dialect);
3746
+ await this.executor.executeSql(compiled.sql, compiled.params);
3747
+ }
3748
+ async deletePivotRow(relation, rootId, targetId) {
3749
+ const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
3750
+ const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
3751
+ if (!rootCol || !targetCol) return;
3752
+ const builder = new DeleteQueryBuilder(relation.pivotTable).where(
3753
+ and(eq(rootCol, rootId), eq(targetCol, targetId))
3754
+ );
3755
+ const compiled = builder.compile(this.dialect);
3756
+ await this.executor.executeSql(compiled.sql, compiled.params);
3757
+ }
3758
+ resolvePrimaryKeyValue(entity, table) {
3759
+ if (!entity) return null;
3760
+ const key = findPrimaryKey(table);
3761
+ const value = entity[key];
3762
+ if (value === void 0 || value === null) return null;
3763
+ return value;
3764
+ }
3765
+ };
3766
+
3767
+ // src/orm/transaction-runner.ts
3768
+ var runInTransaction = async (executor, action) => {
3769
+ if (!executor.beginTransaction) {
3770
+ await action();
3771
+ return;
3772
+ }
3773
+ await executor.beginTransaction();
3774
+ try {
3775
+ await action();
3776
+ await executor.commitTransaction?.();
3777
+ } catch (error) {
3778
+ await executor.rollbackTransaction?.();
3779
+ throw error;
3780
+ }
3781
+ };
3782
+
3783
+ // src/orm/runtime-types.ts
3784
+ var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
3785
+ EntityStatus2["New"] = "new";
3786
+ EntityStatus2["Managed"] = "managed";
3787
+ EntityStatus2["Dirty"] = "dirty";
3788
+ EntityStatus2["Removed"] = "removed";
3789
+ EntityStatus2["Detached"] = "detached";
3790
+ return EntityStatus2;
3791
+ })(EntityStatus || {});
3792
+
3793
+ // src/orm/unit-of-work.ts
3794
+ var UnitOfWork = class {
3795
+ constructor(dialect, executor, identityMap, hookContext) {
3796
+ this.dialect = dialect;
3797
+ this.executor = executor;
3798
+ this.identityMap = identityMap;
3799
+ this.hookContext = hookContext;
3800
+ this.trackedEntities = /* @__PURE__ */ new Map();
3801
+ }
3802
+ get identityBuckets() {
3803
+ return this.identityMap.bucketsMap;
3804
+ }
3805
+ getTracked() {
3806
+ return Array.from(this.trackedEntities.values());
3807
+ }
3808
+ getEntity(table, pk) {
3809
+ return this.identityMap.getEntity(table, pk);
3810
+ }
3811
+ getEntitiesForTable(table) {
3812
+ return this.identityMap.getEntitiesForTable(table);
3813
+ }
3814
+ findTracked(entity) {
3815
+ return this.trackedEntities.get(entity);
3816
+ }
3817
+ setEntity(table, pk, entity) {
3818
+ if (pk === null || pk === void 0) return;
3819
+ let tracked = this.trackedEntities.get(entity);
3820
+ if (!tracked) {
3821
+ tracked = {
3822
+ table,
3823
+ entity,
3824
+ pk,
3825
+ status: "managed" /* Managed */,
3826
+ original: this.createSnapshot(table, entity)
3827
+ };
3828
+ this.trackedEntities.set(entity, tracked);
3829
+ } else {
3830
+ tracked.pk = pk;
3831
+ }
3832
+ this.registerIdentity(tracked);
3833
+ }
3834
+ trackNew(table, entity, pk) {
3835
+ const tracked = {
3836
+ table,
3837
+ entity,
3838
+ pk: pk ?? null,
3839
+ status: "new" /* New */,
3840
+ original: null
3841
+ };
3842
+ this.trackedEntities.set(entity, tracked);
3843
+ if (pk != null) {
3844
+ this.registerIdentity(tracked);
3845
+ }
3846
+ }
3847
+ trackManaged(table, pk, entity) {
3848
+ const tracked = {
3849
+ table,
3850
+ entity,
3851
+ pk,
3852
+ status: "managed" /* Managed */,
3853
+ original: this.createSnapshot(table, entity)
3854
+ };
3855
+ this.trackedEntities.set(entity, tracked);
3856
+ this.registerIdentity(tracked);
3857
+ }
3858
+ markDirty(entity) {
3859
+ const tracked = this.trackedEntities.get(entity);
3860
+ if (!tracked) return;
3861
+ if (tracked.status === "new" /* New */ || tracked.status === "removed" /* Removed */) return;
3862
+ tracked.status = "dirty" /* Dirty */;
3863
+ }
3864
+ markRemoved(entity) {
3865
+ const tracked = this.trackedEntities.get(entity);
3866
+ if (!tracked) return;
3867
+ tracked.status = "removed" /* Removed */;
3868
+ }
3869
+ async flush() {
3870
+ const toFlush = Array.from(this.trackedEntities.values());
3871
+ for (const tracked of toFlush) {
3872
+ switch (tracked.status) {
3873
+ case "new" /* New */:
3874
+ await this.flushInsert(tracked);
3875
+ break;
3876
+ case "dirty" /* Dirty */:
3877
+ await this.flushUpdate(tracked);
3878
+ break;
3879
+ case "removed" /* Removed */:
3880
+ await this.flushDelete(tracked);
3881
+ break;
3882
+ default:
3883
+ break;
3884
+ }
3885
+ }
3886
+ }
3887
+ async flushInsert(tracked) {
3888
+ await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
3889
+ const payload = this.extractColumns(tracked.table, tracked.entity);
3890
+ const builder = new InsertQueryBuilder(tracked.table).values(payload);
3891
+ const compiled = builder.compile(this.dialect);
3892
+ await this.executeCompiled(compiled);
3893
+ tracked.status = "managed" /* Managed */;
3894
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity);
3895
+ tracked.pk = this.getPrimaryKeyValue(tracked);
3896
+ this.registerIdentity(tracked);
3897
+ await this.runHook(tracked.table.hooks?.afterInsert, tracked);
3898
+ }
3899
+ async flushUpdate(tracked) {
3900
+ if (tracked.pk == null) return;
3901
+ const changes = this.computeChanges(tracked);
3902
+ if (!Object.keys(changes).length) {
3903
+ tracked.status = "managed" /* Managed */;
3904
+ return;
3905
+ }
3906
+ await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
3907
+ const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
3908
+ if (!pkColumn) return;
3909
+ const builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
3910
+ const compiled = builder.compile(this.dialect);
3911
+ await this.executeCompiled(compiled);
3912
+ tracked.status = "managed" /* Managed */;
3913
+ tracked.original = this.createSnapshot(tracked.table, tracked.entity);
3914
+ this.registerIdentity(tracked);
3915
+ await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
3916
+ }
3917
+ async flushDelete(tracked) {
3918
+ if (tracked.pk == null) return;
3919
+ await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
3920
+ const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
3921
+ if (!pkColumn) return;
3922
+ const builder = new DeleteQueryBuilder(tracked.table).where(eq(pkColumn, tracked.pk));
3923
+ const compiled = builder.compile(this.dialect);
3924
+ await this.executeCompiled(compiled);
3925
+ tracked.status = "detached" /* Detached */;
3926
+ this.trackedEntities.delete(tracked.entity);
3927
+ this.identityMap.remove(tracked);
3928
+ await this.runHook(tracked.table.hooks?.afterDelete, tracked);
3929
+ }
3930
+ async runHook(hook, tracked) {
3931
+ if (!hook) return;
3932
+ await hook(this.hookContext(), tracked.entity);
3933
+ }
3934
+ computeChanges(tracked) {
3935
+ const snapshot = tracked.original ?? {};
3936
+ const changes = {};
3937
+ for (const column of Object.keys(tracked.table.columns)) {
3938
+ const current = tracked.entity[column];
3939
+ if (snapshot[column] !== current) {
3940
+ changes[column] = current;
3941
+ }
3942
+ }
3943
+ return changes;
3944
+ }
3945
+ extractColumns(table, entity) {
3946
+ const payload = {};
3947
+ for (const column of Object.keys(table.columns)) {
3948
+ payload[column] = entity[column];
3949
+ }
3950
+ return payload;
3951
+ }
3952
+ async executeCompiled(compiled) {
3953
+ await this.executor.executeSql(compiled.sql, compiled.params);
3954
+ }
3955
+ registerIdentity(tracked) {
3956
+ if (tracked.pk == null) return;
3957
+ this.identityMap.register(tracked);
3958
+ }
3959
+ createSnapshot(table, entity) {
3960
+ const snapshot = {};
3961
+ for (const column of Object.keys(table.columns)) {
3962
+ snapshot[column] = entity[column];
3963
+ }
3964
+ return snapshot;
3965
+ }
3966
+ getPrimaryKeyValue(tracked) {
3967
+ const key = findPrimaryKey(tracked.table);
3968
+ const val = tracked.entity[key];
3969
+ if (val === void 0 || val === null) return null;
3970
+ return val;
3971
+ }
3972
+ };
3973
+
3974
+ // src/orm/orm-context.ts
3975
+ var OrmContext = class {
3976
+ constructor(options) {
3977
+ this.options = options;
3978
+ this.identityMap = new IdentityMap();
3979
+ this.interceptors = [...options.interceptors ?? []];
3980
+ this.unitOfWork = new UnitOfWork(
3981
+ options.dialect,
3982
+ options.executor,
3983
+ this.identityMap,
3984
+ () => this
3985
+ );
3986
+ this.relationChanges = new RelationChangeProcessor(
3987
+ this.unitOfWork,
3988
+ options.dialect,
3989
+ options.executor
3990
+ );
3991
+ this.domainEvents = new DomainEventBus(options.domainEventHandlers);
3992
+ }
3993
+ get dialect() {
3994
+ return this.options.dialect;
3995
+ }
3996
+ get executor() {
3997
+ return this.options.executor;
3998
+ }
3999
+ get identityBuckets() {
4000
+ return this.unitOfWork.identityBuckets;
4001
+ }
4002
+ get tracked() {
4003
+ return this.unitOfWork.getTracked();
4004
+ }
4005
+ getEntity(table, pk) {
4006
+ return this.unitOfWork.getEntity(table, pk);
4007
+ }
4008
+ setEntity(table, pk, entity) {
4009
+ this.unitOfWork.setEntity(table, pk, entity);
4010
+ }
4011
+ trackNew(table, entity, pk) {
4012
+ this.unitOfWork.trackNew(table, entity, pk);
4013
+ }
4014
+ trackManaged(table, pk, entity) {
4015
+ this.unitOfWork.trackManaged(table, pk, entity);
4016
+ }
4017
+ markDirty(entity) {
4018
+ this.unitOfWork.markDirty(entity);
4019
+ }
4020
+ markRemoved(entity) {
4021
+ this.unitOfWork.markRemoved(entity);
4022
+ }
4023
+ registerRelationChange(root, relationKey, rootTable, relationName, relation, change) {
4024
+ const entry = {
4025
+ root,
4026
+ relationKey,
4027
+ rootTable,
4028
+ relationName,
4029
+ relation,
4030
+ change
4031
+ };
4032
+ this.relationChanges.registerChange(entry);
4033
+ }
4034
+ registerInterceptor(interceptor) {
4035
+ this.interceptors.push(interceptor);
4036
+ }
4037
+ registerDomainEventHandler(name, handler) {
4038
+ this.domainEvents.register(name, handler);
4039
+ }
4040
+ async saveChanges() {
4041
+ await runInTransaction(this.executor, async () => {
4042
+ for (const interceptor of this.interceptors) {
4043
+ await interceptor.beforeFlush?.(this);
4044
+ }
4045
+ await this.unitOfWork.flush();
4046
+ await this.relationChanges.process();
4047
+ await this.unitOfWork.flush();
4048
+ for (const interceptor of this.interceptors) {
4049
+ await interceptor.afterFlush?.(this);
4050
+ }
4051
+ });
4052
+ await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
4053
+ }
4054
+ getEntitiesForTable(table) {
4055
+ return this.unitOfWork.getEntitiesForTable(table);
4056
+ }
4057
+ };
4058
+ export {
4059
+ AsyncLocalStorage,
4060
+ DefaultBelongsToReference,
4061
+ DefaultHasManyCollection,
4062
+ DefaultManyToManyCollection,
4063
+ DeleteQueryBuilder,
4064
+ EntityStatus,
4065
+ InsertQueryBuilder,
4066
+ MySqlDialect,
4067
+ OrmContext,
4068
+ RelationKinds,
4069
+ SelectQueryBuilder,
4070
+ SqlServerDialect,
4071
+ SqliteDialect,
4072
+ TypeScriptGenerator,
4073
+ UpdateQueryBuilder,
4074
+ addDomainEvent,
4075
+ and,
4076
+ avg,
4077
+ belongsTo,
4078
+ belongsToMany,
4079
+ between,
4080
+ caseWhen,
4081
+ col,
4082
+ columnOperand,
4083
+ count,
4084
+ createColumn,
4085
+ createEntityFromRow,
4086
+ createEntityProxy,
4087
+ createLiteral,
4088
+ defineTable,
4089
+ denseRank,
4090
+ eq,
4091
+ executeHydrated,
4092
+ exists,
4093
+ firstValue,
4094
+ gt,
4095
+ gte,
4096
+ hasMany,
4097
+ hydrateRows,
4098
+ inList,
4099
+ isCaseExpressionNode,
4100
+ isExpressionSelectionNode,
4101
+ isFunctionNode,
4102
+ isNotNull,
4103
+ isNull,
4104
+ isOperandNode,
4105
+ isWindowFunctionNode,
4106
+ jsonPath,
4107
+ lag,
4108
+ lastValue,
4109
+ lead,
4110
+ like,
4111
+ loadBelongsToManyRelation,
4112
+ loadBelongsToRelation,
4113
+ loadHasManyRelation,
4114
+ lt,
4115
+ lte,
4116
+ neq,
4117
+ notBetween,
4118
+ notExists,
4119
+ notInList,
4120
+ notLike,
4121
+ ntile,
4122
+ or,
4123
+ rank,
4124
+ rowNumber,
4125
+ sum,
4126
+ valueToOperand,
4127
+ visitExpression,
4128
+ visitOperand,
4129
+ windowFunction
4130
+ };
4131
+ //# sourceMappingURL=index.js.map