metal-orm 1.0.7 → 1.0.9

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