metal-orm 1.0.39 → 1.0.41

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 (52) hide show
  1. package/dist/index.cjs +1466 -189
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +723 -51
  4. package/dist/index.d.ts +723 -51
  5. package/dist/index.js +1457 -189
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/codegen/typescript.ts +66 -5
  9. package/src/core/ast/aggregate-functions.ts +15 -15
  10. package/src/core/ast/expression-builders.ts +378 -316
  11. package/src/core/ast/expression-nodes.ts +210 -186
  12. package/src/core/ast/expression-visitor.ts +40 -30
  13. package/src/core/ast/query.ts +164 -132
  14. package/src/core/ast/window-functions.ts +86 -86
  15. package/src/core/dialect/abstract.ts +509 -479
  16. package/src/core/dialect/base/groupby-compiler.ts +6 -6
  17. package/src/core/dialect/base/join-compiler.ts +9 -12
  18. package/src/core/dialect/base/orderby-compiler.ts +20 -6
  19. package/src/core/dialect/base/sql-dialect.ts +237 -138
  20. package/src/core/dialect/mssql/index.ts +164 -185
  21. package/src/core/dialect/sqlite/index.ts +39 -34
  22. package/src/core/execution/db-executor.ts +46 -6
  23. package/src/core/execution/executors/mssql-executor.ts +39 -22
  24. package/src/core/execution/executors/mysql-executor.ts +23 -6
  25. package/src/core/execution/executors/sqlite-executor.ts +29 -3
  26. package/src/core/execution/pooling/pool-types.ts +30 -0
  27. package/src/core/execution/pooling/pool.ts +268 -0
  28. package/src/core/functions/standard-strategy.ts +46 -37
  29. package/src/decorators/bootstrap.ts +7 -7
  30. package/src/index.ts +6 -0
  31. package/src/orm/domain-event-bus.ts +49 -0
  32. package/src/orm/entity-metadata.ts +9 -9
  33. package/src/orm/entity.ts +58 -0
  34. package/src/orm/orm-session.ts +465 -270
  35. package/src/orm/orm.ts +61 -11
  36. package/src/orm/pooled-executor-factory.ts +131 -0
  37. package/src/orm/query-logger.ts +6 -12
  38. package/src/orm/relation-change-processor.ts +75 -0
  39. package/src/orm/relations/many-to-many.ts +4 -2
  40. package/src/orm/save-graph.ts +303 -0
  41. package/src/orm/transaction-runner.ts +3 -3
  42. package/src/orm/unit-of-work.ts +128 -0
  43. package/src/query-builder/delete-query-state.ts +67 -38
  44. package/src/query-builder/delete.ts +37 -1
  45. package/src/query-builder/hydration-manager.ts +93 -79
  46. package/src/query-builder/insert-query-state.ts +131 -61
  47. package/src/query-builder/insert.ts +27 -1
  48. package/src/query-builder/query-ast-service.ts +207 -170
  49. package/src/query-builder/select-query-state.ts +169 -162
  50. package/src/query-builder/select.ts +15 -23
  51. package/src/query-builder/update-query-state.ts +114 -77
  52. package/src/query-builder/update.ts +38 -1
package/dist/index.cjs CHANGED
@@ -60,6 +60,7 @@ __export(index_exports, {
60
60
  MySqlDialect: () => MySqlDialect,
61
61
  Orm: () => Orm,
62
62
  OrmSession: () => OrmSession,
63
+ Pool: () => Pool,
63
64
  PostgresDialect: () => PostgresDialect,
64
65
  PrimaryKey: () => PrimaryKey,
65
66
  RelationKinds: () => RelationKinds,
@@ -70,7 +71,9 @@ __export(index_exports, {
70
71
  UpdateQueryBuilder: () => UpdateQueryBuilder,
71
72
  abs: () => abs,
72
73
  acos: () => acos,
74
+ add: () => add,
73
75
  addDomainEvent: () => addDomainEvent,
76
+ aliasRef: () => aliasRef,
74
77
  and: () => and,
75
78
  ascii: () => ascii,
76
79
  asin: () => asin,
@@ -103,6 +106,7 @@ __export(index_exports, {
103
106
  createLiteral: () => createLiteral,
104
107
  createMssqlExecutor: () => createMssqlExecutor,
105
108
  createMysqlExecutor: () => createMysqlExecutor,
109
+ createPooledExecutorFactory: () => createPooledExecutorFactory,
106
110
  createPostgresExecutor: () => createPostgresExecutor,
107
111
  createQueryLoggingExecutor: () => createQueryLoggingExecutor,
108
112
  createSqliteExecutor: () => createSqliteExecutor,
@@ -121,6 +125,7 @@ __export(index_exports, {
121
125
  degrees: () => degrees,
122
126
  denseRank: () => denseRank,
123
127
  diffSchema: () => diffSchema,
128
+ div: () => div,
124
129
  endOfMonth: () => endOfMonth,
125
130
  eq: () => eq,
126
131
  esel: () => esel,
@@ -143,6 +148,7 @@ __export(index_exports, {
143
148
  hasOne: () => hasOne,
144
149
  hydrateRows: () => hydrateRows,
145
150
  inList: () => inList,
151
+ inSubquery: () => inSubquery,
146
152
  instr: () => instr,
147
153
  introspectSchema: () => introspectSchema,
148
154
  isCaseExpressionNode: () => isCaseExpressionNode,
@@ -178,10 +184,12 @@ __export(index_exports, {
178
184
  min: () => min,
179
185
  mod: () => mod,
180
186
  month: () => month,
187
+ mul: () => mul,
181
188
  neq: () => neq,
182
189
  notBetween: () => notBetween,
183
190
  notExists: () => notExists,
184
191
  notInList: () => notInList,
192
+ notInSubquery: () => notInSubquery,
185
193
  notLike: () => notLike,
186
194
  now: () => now,
187
195
  ntile: () => ntile,
@@ -213,6 +221,7 @@ __export(index_exports, {
213
221
  sin: () => sin,
214
222
  space: () => space,
215
223
  sqrt: () => sqrt,
224
+ sub: () => sub,
216
225
  substr: () => substr,
217
226
  sum: () => sum,
218
227
  synchronizeSchema: () => synchronizeSchema,
@@ -450,6 +459,7 @@ var belongsToMany = (target, pivotTable, options) => ({
450
459
 
451
460
  // src/core/ast/expression-nodes.ts
452
461
  var operandTypes = /* @__PURE__ */ new Set([
462
+ "AliasRef",
453
463
  "Column",
454
464
  "Literal",
455
465
  "Function",
@@ -491,11 +501,21 @@ var toOperand = (val) => {
491
501
  }
492
502
  return toNode(val);
493
503
  };
504
+ var hasQueryAst = (value) => typeof value.getAST === "function";
505
+ var resolveSelectQueryNode = (query) => hasQueryAst(query) ? query.getAST() : query;
506
+ var toScalarSubqueryNode = (query) => ({
507
+ type: "ScalarSubquery",
508
+ query: resolveSelectQueryNode(query)
509
+ });
494
510
  var columnOperand = (col2) => toNode(col2);
495
511
  var outerRef = (col2) => ({
496
512
  ...columnOperand(col2),
497
513
  scope: "outer"
498
514
  });
515
+ var aliasRef = (name) => ({
516
+ type: "AliasRef",
517
+ name
518
+ });
499
519
  var correlateBy = (table, column) => outerRef({ name: column, table });
500
520
  var createBinaryExpression = (operator, left2, right2, escape) => {
501
521
  const node = {
@@ -537,14 +557,16 @@ var isNotNull = (left2) => ({
537
557
  left: toNode(left2),
538
558
  operator: "IS NOT NULL"
539
559
  });
540
- var createInExpression = (operator, left2, values) => ({
560
+ var createInExpression = (operator, left2, right2) => ({
541
561
  type: "InExpression",
542
562
  left: toNode(left2),
543
563
  operator,
544
- right: values.map((v) => toOperand(v))
564
+ right: right2
545
565
  });
546
- var inList = (left2, values) => createInExpression("IN", left2, values);
547
- var notInList = (left2, values) => createInExpression("NOT IN", left2, values);
566
+ var inList = (left2, values) => createInExpression("IN", left2, values.map((v) => toOperand(v)));
567
+ var notInList = (left2, values) => createInExpression("NOT IN", left2, values.map((v) => toOperand(v)));
568
+ var inSubquery = (left2, subquery) => createInExpression("IN", left2, toScalarSubqueryNode(subquery));
569
+ var notInSubquery = (left2, subquery) => createInExpression("NOT IN", left2, toScalarSubqueryNode(subquery));
548
570
  var createBetweenExpression = (operator, left2, lower2, upper2) => ({
549
571
  type: "BetweenExpression",
550
572
  left: toNode(left2),
@@ -554,6 +576,16 @@ var createBetweenExpression = (operator, left2, lower2, upper2) => ({
554
576
  });
555
577
  var between = (left2, lower2, upper2) => createBetweenExpression("BETWEEN", left2, lower2, upper2);
556
578
  var notBetween = (left2, lower2, upper2) => createBetweenExpression("NOT BETWEEN", left2, lower2, upper2);
579
+ var createArithmeticExpression = (operator, left2, right2) => ({
580
+ type: "ArithmeticExpression",
581
+ left: toOperand(left2),
582
+ operator,
583
+ right: toOperand(right2)
584
+ });
585
+ var add = (left2, right2) => createArithmeticExpression("+", left2, right2);
586
+ var sub = (left2, right2) => createArithmeticExpression("-", left2, right2);
587
+ var mul = (left2, right2) => createArithmeticExpression("*", left2, right2);
588
+ var div = (left2, right2) => createArithmeticExpression("/", left2, right2);
557
589
  var jsonPath = (col2, path) => ({
558
590
  type: "JsonPath",
559
591
  column: columnOperand(col2),
@@ -632,7 +664,7 @@ var windowFunction = (name, args = [], partitionBy, orderBy) => {
632
664
  const partitionNodes = partitionBy?.map((col2) => columnOperand(col2)) ?? void 0;
633
665
  const orderNodes = orderBy?.map((o) => ({
634
666
  type: "OrderBy",
635
- column: columnOperand(o.column),
667
+ term: columnOperand(o.column),
636
668
  direction: o.direction
637
669
  }));
638
670
  return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
@@ -707,7 +739,7 @@ var min = buildAggregate("MIN");
707
739
  var max = buildAggregate("MAX");
708
740
  var toOrderByNode = (order) => ({
709
741
  type: "OrderBy",
710
- column: columnOperand(order.column),
742
+ term: columnOperand(order.column),
711
743
  direction: order.direction ?? ORDER_DIRECTIONS.ASC
712
744
  });
713
745
  var groupConcat = (col2, options) => ({
@@ -757,6 +789,9 @@ var visitExpression = (node, visitor) => {
757
789
  case "BetweenExpression":
758
790
  if (visitor.visitBetweenExpression) return visitor.visitBetweenExpression(node);
759
791
  break;
792
+ case "ArithmeticExpression":
793
+ if (visitor.visitArithmeticExpression) return visitor.visitArithmeticExpression(node);
794
+ break;
760
795
  default:
761
796
  break;
762
797
  }
@@ -788,6 +823,9 @@ var visitOperand = (node, visitor) => {
788
823
  case "WindowFunction":
789
824
  if (visitor.visitWindowFunction) return visitor.visitWindowFunction(node);
790
825
  break;
826
+ case "AliasRef":
827
+ if (visitor.visitAliasRef) return visitor.visitAliasRef(node);
828
+ break;
791
829
  default:
792
830
  break;
793
831
  }
@@ -904,7 +942,14 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
904
942
  if (!orderBy || orderBy.length === 0) {
905
943
  return "";
906
944
  }
907
- const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
945
+ const parts = orderBy.map((order) => {
946
+ const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
947
+ throw new Error("ORDER BY expressions inside functions must be operands");
948
+ })();
949
+ const collation = order.collation ? ` COLLATE ${order.collation}` : "";
950
+ const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
951
+ return `${term} ${order.direction}${collation}${nulls}`;
952
+ });
908
953
  return `ORDER BY ${parts.join(", ")}`;
909
954
  }
910
955
  formatGroupConcatSeparator(ctx) {
@@ -1169,6 +1214,16 @@ var Dialect = class _Dialect {
1169
1214
  }
1170
1215
  return compiler(node, ctx);
1171
1216
  }
1217
+ /**
1218
+ * Compiles an ordering term (operand, expression, or alias reference).
1219
+ */
1220
+ compileOrderingTerm(term, ctx) {
1221
+ if (isOperandNode(term)) {
1222
+ return this.compileOperand(term, ctx);
1223
+ }
1224
+ const expr = this.compileExpression(term, ctx);
1225
+ return `(${expr})`;
1226
+ }
1172
1227
  registerDefaultExpressionCompilers() {
1173
1228
  this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
1174
1229
  const left2 = this.compileOperand(binary.left, ctx);
@@ -1194,8 +1249,12 @@ var Dialect = class _Dialect {
1194
1249
  });
1195
1250
  this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
1196
1251
  const left2 = this.compileOperand(inExpr.left, ctx);
1197
- const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
1198
- return `${left2} ${inExpr.operator} (${values})`;
1252
+ if (Array.isArray(inExpr.right)) {
1253
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
1254
+ return `${left2} ${inExpr.operator} (${values})`;
1255
+ }
1256
+ const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
1257
+ return `${left2} ${inExpr.operator} (${subquerySql})`;
1199
1258
  });
1200
1259
  this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
1201
1260
  const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
@@ -1207,9 +1266,15 @@ var Dialect = class _Dialect {
1207
1266
  const upper2 = this.compileOperand(betweenExpr.upper, ctx);
1208
1267
  return `${left2} ${betweenExpr.operator} ${lower2} AND ${upper2}`;
1209
1268
  });
1269
+ this.registerExpressionCompiler("ArithmeticExpression", (arith, ctx) => {
1270
+ const left2 = this.compileOperand(arith.left, ctx);
1271
+ const right2 = this.compileOperand(arith.right, ctx);
1272
+ return `${left2} ${arith.operator} ${right2}`;
1273
+ });
1210
1274
  }
1211
1275
  registerDefaultOperandCompilers() {
1212
1276
  this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
1277
+ this.registerOperandCompiler("AliasRef", (alias, _ctx) => this.quoteIdentifier(alias.name));
1213
1278
  this.registerOperandCompiler("Column", (column, _ctx) => {
1214
1279
  return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
1215
1280
  });
@@ -1247,9 +1312,12 @@ var Dialect = class _Dialect {
1247
1312
  parts.push(partitionClause);
1248
1313
  }
1249
1314
  if (node.orderBy && node.orderBy.length > 0) {
1250
- const orderClause = "ORDER BY " + node.orderBy.map(
1251
- (o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`
1252
- ).join(", ");
1315
+ const orderClause = "ORDER BY " + node.orderBy.map((o) => {
1316
+ const term = this.compileOrderingTerm(o.term, ctx);
1317
+ const collation = o.collation ? ` COLLATE ${o.collation}` : "";
1318
+ const nulls = o.nulls ? ` NULLS ${o.nulls}` : "";
1319
+ return `${term} ${o.direction}${collation}${nulls}`;
1320
+ }).join(", ");
1253
1321
  parts.push(orderClause);
1254
1322
  }
1255
1323
  result += parts.join(" ");
@@ -1444,17 +1512,9 @@ var NoReturningStrategy = class {
1444
1512
 
1445
1513
  // src/core/dialect/base/join-compiler.ts
1446
1514
  var JoinCompiler = class {
1447
- /**
1448
- * Compiles all JOIN clauses from a SELECT query AST.
1449
- * @param ast - The SELECT query AST containing join definitions.
1450
- * @param ctx - The compiler context for expression compilation.
1451
- * @param compileFrom - Function to compile table sources (tables or subqueries).
1452
- * @param compileExpression - Function to compile join condition expressions.
1453
- * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
1454
- */
1455
- static compileJoins(ast, ctx, compileFrom, compileExpression) {
1456
- if (!ast.joins || ast.joins.length === 0) return "";
1457
- const parts = ast.joins.map((j) => {
1515
+ static compileJoins(joins, ctx, compileFrom, compileExpression) {
1516
+ if (!joins || joins.length === 0) return "";
1517
+ const parts = joins.map((j) => {
1458
1518
  const table = compileFrom(j.table, ctx);
1459
1519
  const cond = compileExpression(j.condition, ctx);
1460
1520
  return `${j.kind} JOIN ${table} ON ${cond}`;
@@ -1468,12 +1528,12 @@ var GroupByCompiler = class {
1468
1528
  /**
1469
1529
  * Compiles GROUP BY clause from a SELECT query AST.
1470
1530
  * @param ast - The SELECT query AST containing grouping columns.
1471
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1531
+ * @param renderTerm - Function to render a grouping term.
1472
1532
  * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
1473
1533
  */
1474
- static compileGroupBy(ast, quoteIdentifier) {
1534
+ static compileGroupBy(ast, renderTerm) {
1475
1535
  if (!ast.groupBy || ast.groupBy.length === 0) return "";
1476
- const cols = ast.groupBy.map((c) => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`).join(", ");
1536
+ const cols = ast.groupBy.map(renderTerm).join(", ");
1477
1537
  return ` GROUP BY ${cols}`;
1478
1538
  }
1479
1539
  };
@@ -1483,12 +1543,19 @@ var OrderByCompiler = class {
1483
1543
  /**
1484
1544
  * Compiles ORDER BY clause from a SELECT query AST.
1485
1545
  * @param ast - The SELECT query AST containing sort specifications.
1486
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
1546
+ * @param renderTerm - Function to render an ordering term.
1547
+ * @param renderNulls - Optional function to render NULLS FIRST/LAST.
1548
+ * @param renderCollation - Optional function to render COLLATE clause.
1487
1549
  * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
1488
1550
  */
1489
- static compileOrderBy(ast, quoteIdentifier) {
1551
+ static compileOrderBy(ast, renderTerm, renderNulls, renderCollation) {
1490
1552
  if (!ast.orderBy || ast.orderBy.length === 0) return "";
1491
- const parts = ast.orderBy.map((o) => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
1553
+ const parts = ast.orderBy.map((o) => {
1554
+ const term = renderTerm(o.term);
1555
+ const collation = renderCollation ? renderCollation(o) : o.collation ? ` COLLATE ${o.collation}` : "";
1556
+ const nulls = renderNulls ? renderNulls(o) : o.nulls ? ` NULLS ${o.nulls}` : "";
1557
+ return `${term} ${o.direction}${collation}${nulls}`;
1558
+ }).join(", ");
1492
1559
  return ` ORDER BY ${parts}`;
1493
1560
  }
1494
1561
  };
@@ -1519,58 +1586,96 @@ var SqlDialectBase = class extends Dialect {
1519
1586
  }
1520
1587
  compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
1521
1588
  const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
1522
- const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1589
+ const orderBy = OrderByCompiler.compileOrderBy(
1590
+ ast,
1591
+ (term) => this.compileOrderingTerm(term, ctx),
1592
+ this.renderOrderByNulls.bind(this),
1593
+ this.renderOrderByCollation.bind(this)
1594
+ );
1523
1595
  const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1524
1596
  const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
1525
1597
  return `${ctes}${combined}${orderBy}${pagination}`;
1526
1598
  }
1527
1599
  compileInsertAst(ast, ctx) {
1600
+ if (!ast.columns.length) {
1601
+ throw new Error("INSERT queries must specify columns.");
1602
+ }
1528
1603
  const table = this.compileTableName(ast.into);
1529
1604
  const columnList = this.compileInsertColumnList(ast.columns);
1530
- const values = this.compileInsertValues(ast.values, ctx);
1605
+ const source = this.compileInsertSource(ast.source, ctx);
1531
1606
  const returning = this.compileReturning(ast.returning, ctx);
1532
- return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
1607
+ return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
1533
1608
  }
1534
1609
  compileReturning(returning, ctx) {
1535
1610
  return this.returningStrategy.compileReturning(returning, ctx);
1536
1611
  }
1612
+ compileInsertSource(source, ctx) {
1613
+ if (source.type === "InsertValues") {
1614
+ if (!source.rows.length) {
1615
+ throw new Error("INSERT ... VALUES requires at least one row.");
1616
+ }
1617
+ const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1618
+ return `VALUES ${values}`;
1619
+ }
1620
+ const normalized = this.normalizeSelectAst(source.query);
1621
+ return this.compileSelectAst(normalized, ctx).trim();
1622
+ }
1537
1623
  compileInsertColumnList(columns) {
1538
1624
  return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1539
1625
  }
1540
- compileInsertValues(values, ctx) {
1541
- return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1542
- }
1543
1626
  compileSelectCore(ast, ctx) {
1544
1627
  const columns = this.compileSelectColumns(ast, ctx);
1545
1628
  const from = this.compileFrom(ast.from, ctx);
1546
- const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
1629
+ const joins = JoinCompiler.compileJoins(
1630
+ ast.joins,
1631
+ ctx,
1632
+ this.compileFrom.bind(this),
1633
+ this.compileExpression.bind(this)
1634
+ );
1547
1635
  const whereClause = this.compileWhere(ast.where, ctx);
1548
- const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
1636
+ const groupBy = GroupByCompiler.compileGroupBy(ast, (term) => this.compileOrderingTerm(term, ctx));
1549
1637
  const having = this.compileHaving(ast, ctx);
1550
- const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
1638
+ const orderBy = OrderByCompiler.compileOrderBy(
1639
+ ast,
1640
+ (term) => this.compileOrderingTerm(term, ctx),
1641
+ this.renderOrderByNulls.bind(this),
1642
+ this.renderOrderByCollation.bind(this)
1643
+ );
1551
1644
  const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
1552
1645
  return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
1553
1646
  }
1554
1647
  compileUpdateAst(ast, ctx) {
1555
- const table = this.compileTableName(ast.table);
1556
- const assignments = this.compileUpdateAssignments(ast.set, ctx);
1648
+ const target = this.compileTableReference(ast.table);
1649
+ const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
1650
+ const fromClause = this.compileUpdateFromClause(ast, ctx);
1557
1651
  const whereClause = this.compileWhere(ast.where, ctx);
1558
1652
  const returning = this.compileReturning(ast.returning, ctx);
1559
- return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
1653
+ return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
1560
1654
  }
1561
- compileUpdateAssignments(assignments, ctx) {
1655
+ compileUpdateAssignments(assignments, table, ctx) {
1562
1656
  return assignments.map((assignment) => {
1563
1657
  const col2 = assignment.column;
1564
- const target = this.quoteIdentifier(col2.name);
1658
+ const target = this.compileQualifiedColumn(col2, table);
1565
1659
  const value = this.compileOperand(assignment.value, ctx);
1566
1660
  return `${target} = ${value}`;
1567
1661
  }).join(", ");
1568
1662
  }
1663
+ compileQualifiedColumn(column, table) {
1664
+ const baseTableName = table.name;
1665
+ const alias = table.alias;
1666
+ const columnTable = column.table ?? alias ?? baseTableName;
1667
+ const tableQualifier = alias && column.table === baseTableName ? alias : columnTable;
1668
+ if (!tableQualifier) {
1669
+ return this.quoteIdentifier(column.name);
1670
+ }
1671
+ return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
1672
+ }
1569
1673
  compileDeleteAst(ast, ctx) {
1570
- const table = this.compileTableName(ast.from);
1674
+ const target = this.compileTableReference(ast.from);
1675
+ const usingClause = this.compileDeleteUsingClause(ast, ctx);
1571
1676
  const whereClause = this.compileWhere(ast.where, ctx);
1572
1677
  const returning = this.compileReturning(ast.returning, ctx);
1573
- return `DELETE FROM ${table}${whereClause}${returning}`;
1678
+ return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
1574
1679
  }
1575
1680
  formatReturningColumns(returning) {
1576
1681
  return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
@@ -1625,6 +1730,38 @@ var SqlDialectBase = class extends Dialect {
1625
1730
  }
1626
1731
  return this.quoteIdentifier(table.name);
1627
1732
  }
1733
+ compileTableReference(table) {
1734
+ const base = this.compileTableName(table);
1735
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1736
+ }
1737
+ compileUpdateFromClause(ast, ctx) {
1738
+ if (!ast.from && (!ast.joins || ast.joins.length === 0)) return "";
1739
+ if (!ast.from) {
1740
+ throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
1741
+ }
1742
+ const from = this.compileFrom(ast.from, ctx);
1743
+ const joins = JoinCompiler.compileJoins(
1744
+ ast.joins,
1745
+ ctx,
1746
+ this.compileFrom.bind(this),
1747
+ this.compileExpression.bind(this)
1748
+ );
1749
+ return ` FROM ${from}${joins}`;
1750
+ }
1751
+ compileDeleteUsingClause(ast, ctx) {
1752
+ if (!ast.using && (!ast.joins || ast.joins.length === 0)) return "";
1753
+ if (!ast.using) {
1754
+ throw new Error("DELETE with JOINs requires a USING clause.");
1755
+ }
1756
+ const usingTable = this.compileFrom(ast.using, ctx);
1757
+ const joins = JoinCompiler.compileJoins(
1758
+ ast.joins,
1759
+ ctx,
1760
+ this.compileFrom.bind(this),
1761
+ this.compileExpression.bind(this)
1762
+ );
1763
+ return ` USING ${usingTable}${joins}`;
1764
+ }
1628
1765
  compileHaving(ast, ctx) {
1629
1766
  if (!ast.having) return "";
1630
1767
  return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
@@ -1636,6 +1773,12 @@ var SqlDialectBase = class extends Dialect {
1636
1773
  const trimmed = this.stripTrailingSemicolon(sql);
1637
1774
  return `(${trimmed})`;
1638
1775
  }
1776
+ renderOrderByNulls(order) {
1777
+ return order.nulls ? ` NULLS ${order.nulls}` : "";
1778
+ }
1779
+ renderOrderByCollation(order) {
1780
+ return order.collation ? ` COLLATE ${order.collation}` : "";
1781
+ }
1639
1782
  };
1640
1783
 
1641
1784
  // src/core/dialect/postgres/functions.ts
@@ -1999,6 +2142,9 @@ var SqliteDialect = class extends SqlDialectBase {
1999
2142
  const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
2000
2143
  return `json_extract(${col2}, '${node.path}')`;
2001
2144
  }
2145
+ compileQualifiedColumn(column, _table) {
2146
+ return this.quoteIdentifier(column.name);
2147
+ }
2002
2148
  compileReturning(returning, ctx) {
2003
2149
  if (!returning || returning.length === 0) return "";
2004
2150
  const columns = this.formatReturningColumns(returning);
@@ -2104,7 +2250,7 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
2104
2250
  };
2105
2251
 
2106
2252
  // src/core/dialect/mssql/index.ts
2107
- var SqlServerDialect = class extends Dialect {
2253
+ var SqlServerDialect = class extends SqlDialectBase {
2108
2254
  /**
2109
2255
  * Creates a new SqlServerDialect instance
2110
2256
  */
@@ -2147,43 +2293,37 @@ var SqlServerDialect = class extends Dialect {
2147
2293
  const hasSetOps = !!(ast.setOps && ast.setOps.length);
2148
2294
  const ctes = this.compileCtes(ast, ctx);
2149
2295
  const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
2150
- const baseSelect = this.compileSelectCore(baseAst, ctx);
2296
+ const baseSelect = this.compileSelectCoreForMssql(baseAst, ctx);
2151
2297
  if (!hasSetOps) {
2152
2298
  return `${ctes}${baseSelect}`;
2153
2299
  }
2154
2300
  const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
2155
- const orderBy = this.compileOrderBy(ast);
2301
+ const orderBy = this.compileOrderBy(ast, ctx);
2156
2302
  const pagination = this.compilePagination(ast, orderBy);
2157
2303
  const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
2158
2304
  const tail = pagination || orderBy;
2159
2305
  return `${ctes}${combined}${tail}`;
2160
2306
  }
2161
- compileInsertAst(ast, ctx) {
2162
- const table = this.quoteIdentifier(ast.into.name);
2163
- const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
2164
- const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
2165
- return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
2166
- }
2167
- compileUpdateAst(ast, ctx) {
2168
- const table = this.quoteIdentifier(ast.table.name);
2169
- const assignments = ast.set.map((assignment) => {
2170
- const col2 = assignment.column;
2171
- const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
2172
- const value = this.compileOperand(assignment.value, ctx);
2173
- return `${target} = ${value}`;
2174
- }).join(", ");
2175
- const whereClause = this.compileWhere(ast.where, ctx);
2176
- return `UPDATE ${table} SET ${assignments}${whereClause};`;
2177
- }
2178
2307
  compileDeleteAst(ast, ctx) {
2308
+ if (ast.using) {
2309
+ throw new Error("DELETE ... USING is not supported in the MSSQL dialect; use join() instead.");
2310
+ }
2179
2311
  if (ast.from.type !== "Table") {
2180
2312
  throw new Error("DELETE only supports base tables in the MSSQL dialect.");
2181
2313
  }
2182
- const table = this.quoteIdentifier(ast.from.name);
2314
+ const alias = ast.from.alias ?? ast.from.name;
2315
+ const target = this.compileTableReference(ast.from);
2316
+ const joins = JoinCompiler.compileJoins(
2317
+ ast.joins,
2318
+ ctx,
2319
+ this.compileFrom.bind(this),
2320
+ this.compileExpression.bind(this)
2321
+ );
2183
2322
  const whereClause = this.compileWhere(ast.where, ctx);
2184
- return `DELETE FROM ${table}${whereClause};`;
2323
+ const returning = this.compileReturning(ast.returning, ctx);
2324
+ return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}${returning}`;
2185
2325
  }
2186
- compileSelectCore(ast, ctx) {
2326
+ compileSelectCoreForMssql(ast, ctx) {
2187
2327
  const columns = ast.columns.map((c) => {
2188
2328
  let expr = "";
2189
2329
  if (c.type === "Function") {
@@ -2202,25 +2342,29 @@ var SqlServerDialect = class extends Dialect {
2202
2342
  return expr;
2203
2343
  }).join(", ");
2204
2344
  const distinct = ast.distinct ? "DISTINCT " : "";
2205
- const from = this.compileTableSource(ast.from, ctx);
2345
+ const from = this.compileTableSource(ast.from);
2206
2346
  const joins = ast.joins.map((j) => {
2207
- const table = this.compileTableSource(j.table, ctx);
2347
+ const table = this.compileTableSource(j.table);
2208
2348
  const cond = this.compileExpression(j.condition, ctx);
2209
2349
  return `${j.kind} JOIN ${table} ON ${cond}`;
2210
2350
  }).join(" ");
2211
2351
  const whereClause = this.compileWhere(ast.where, ctx);
2212
- const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
2352
+ const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((term) => this.compileOrderingTerm(term, ctx)).join(", ") : "";
2213
2353
  const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
2214
- const orderBy = this.compileOrderBy(ast);
2354
+ const orderBy = this.compileOrderBy(ast, ctx);
2215
2355
  const pagination = this.compilePagination(ast, orderBy);
2216
2356
  if (pagination) {
2217
2357
  return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
2218
2358
  }
2219
2359
  return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
2220
2360
  }
2221
- compileOrderBy(ast) {
2222
- if (!ast.orderBy || ast.orderBy.length === 0) return "";
2223
- return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
2361
+ compileOrderBy(ast, ctx) {
2362
+ return OrderByCompiler.compileOrderBy(
2363
+ ast,
2364
+ (term) => this.compileOrderingTerm(term, ctx),
2365
+ this.renderOrderByNulls.bind(this),
2366
+ this.renderOrderByCollation.bind(this)
2367
+ );
2224
2368
  }
2225
2369
  compilePagination(ast, orderBy) {
2226
2370
  const hasLimit = ast.limit !== void 0;
@@ -2234,21 +2378,6 @@ var SqlServerDialect = class extends Dialect {
2234
2378
  }
2235
2379
  return pagination;
2236
2380
  }
2237
- compileTableSource(table, ctx) {
2238
- if (table.type === "FunctionTable") {
2239
- return FunctionTableFormatter.format(table, ctx, this);
2240
- }
2241
- if (table.type === "DerivedTable") {
2242
- return this.compileDerivedTable(table, ctx);
2243
- }
2244
- const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
2245
- return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
2246
- }
2247
- compileDerivedTable(table, ctx) {
2248
- const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
2249
- const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
2250
- return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
2251
- }
2252
2381
  compileCtes(ast, ctx) {
2253
2382
  if (!ast.ctes || ast.ctes.length === 0) return "";
2254
2383
  const defs = ast.ctes.map((cte) => {
@@ -2259,10 +2388,6 @@ var SqlServerDialect = class extends Dialect {
2259
2388
  }).join(", ");
2260
2389
  return `WITH ${defs} `;
2261
2390
  }
2262
- wrapSetOperand(sql) {
2263
- const trimmed = sql.trim().replace(/;$/, "");
2264
- return `(${trimmed})`;
2265
- }
2266
2391
  };
2267
2392
 
2268
2393
  // src/core/dialect/dialect-factory.ts
@@ -2412,7 +2537,7 @@ var SelectQueryState = class _SelectQueryState {
2412
2537
  }
2413
2538
  /**
2414
2539
  * Adds GROUP BY columns to the query
2415
- * @param columns - Columns to group by
2540
+ * @param columns - Terms to group by
2416
2541
  * @returns New SelectQueryState with GROUP BY clause
2417
2542
  */
2418
2543
  withGroupBy(columns) {
@@ -2695,31 +2820,38 @@ var HydrationManager = class _HydrationManager {
2695
2820
  }
2696
2821
  const mapped = [];
2697
2822
  for (const ob of orderBy) {
2698
- if (ob.column.table !== plan.rootTable) {
2699
- return null;
2700
- }
2701
- const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
2702
- if (!availableColumns.has(alias)) {
2703
- return null;
2704
- }
2705
- mapped.push({
2706
- type: "OrderBy",
2707
- column: { type: "Column", table: baseAlias, name: alias },
2708
- direction: ob.direction
2709
- });
2823
+ const mappedTerm = this.mapOrderingTerm(ob.term, plan, projectionAliases, baseAlias, availableColumns);
2824
+ if (!mappedTerm) return null;
2825
+ mapped.push({ ...ob, term: mappedTerm });
2710
2826
  }
2711
2827
  return mapped;
2712
2828
  }
2829
+ mapOrderingTerm(term, plan, projectionAliases, baseAlias, availableColumns) {
2830
+ if (term.type === "Column") {
2831
+ const col2 = term;
2832
+ if (col2.table !== plan.rootTable) return null;
2833
+ const alias = projectionAliases.get(`${col2.table}.${col2.name}`) ?? col2.name;
2834
+ if (!availableColumns.has(alias)) return null;
2835
+ return { type: "Column", table: baseAlias, name: alias };
2836
+ }
2837
+ if (term.type === "AliasRef") {
2838
+ const aliasName = term.name;
2839
+ if (!availableColumns.has(aliasName)) return null;
2840
+ return { type: "Column", table: baseAlias, name: aliasName };
2841
+ }
2842
+ return null;
2843
+ }
2713
2844
  buildPagingColumns(primaryKey, orderBy, tableAlias) {
2714
2845
  const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
2715
2846
  if (!orderBy) return columns;
2716
2847
  for (const ob of orderBy) {
2717
- if (!columns.some((col2) => col2.name === ob.column.name)) {
2848
+ const term = ob.term;
2849
+ if (!columns.some((col2) => col2.name === term.name)) {
2718
2850
  columns.push({
2719
2851
  type: "Column",
2720
2852
  table: tableAlias,
2721
- name: ob.column.name,
2722
- alias: ob.column.name
2853
+ name: term.name,
2854
+ alias: term.name
2723
2855
  });
2724
2856
  }
2725
2857
  }
@@ -3025,10 +3157,8 @@ var QueryAstService = class {
3025
3157
  * @returns Updated query state with GROUP BY clause
3026
3158
  */
3027
3159
  withGroupBy(col2) {
3028
- const from = this.state.ast.from;
3029
- const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
3030
- const node = buildColumnNode(tableRef, col2);
3031
- return this.state.withGroupBy([node]);
3160
+ const term = this.normalizeOrderingTerm(col2);
3161
+ return this.state.withGroupBy([term]);
3032
3162
  }
3033
3163
  /**
3034
3164
  * Adds a HAVING clause to the query
@@ -3045,11 +3175,9 @@ var QueryAstService = class {
3045
3175
  * @param direction - Order direction (ASC/DESC)
3046
3176
  * @returns Updated query state with ORDER BY clause
3047
3177
  */
3048
- withOrderBy(col2, direction) {
3049
- const from = this.state.ast.from;
3050
- const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
3051
- const node = buildColumnNode(tableRef, col2);
3052
- return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
3178
+ withOrderBy(term, direction, nulls, collation) {
3179
+ const normalized = this.normalizeOrderingTerm(term);
3180
+ return this.state.withOrderBy([{ type: "OrderBy", term: normalized, direction, nulls, collation }]);
3053
3181
  }
3054
3182
  /**
3055
3183
  * Adds a DISTINCT clause to the query
@@ -3084,6 +3212,24 @@ var QueryAstService = class {
3084
3212
  combineExpressions(existing, next) {
3085
3213
  return existing ? and(existing, next) : next;
3086
3214
  }
3215
+ normalizeOrderingTerm(term) {
3216
+ const from = this.state.ast.from;
3217
+ const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
3218
+ const termType = term?.type;
3219
+ if (termType === "Column") {
3220
+ return term;
3221
+ }
3222
+ if (termType === "AliasRef") {
3223
+ return term;
3224
+ }
3225
+ if (isOperandNode(term)) {
3226
+ return term;
3227
+ }
3228
+ if (termType === "BinaryExpression" || termType === "LogicalExpression" || termType === "NullExpression" || termType === "InExpression" || termType === "ExistsExpression" || termType === "BetweenExpression" || termType === "ArithmeticExpression") {
3229
+ return term;
3230
+ }
3231
+ return buildColumnNode(tableRef, term);
3232
+ }
3087
3233
  };
3088
3234
 
3089
3235
  // src/query-builder/relation-projection-helper.ts
@@ -4036,8 +4182,10 @@ var DefaultManyToManyCollection = class {
4036
4182
  attach(target) {
4037
4183
  const entity = this.ensureEntity(target);
4038
4184
  const id = this.extractId(entity);
4039
- if (id == null) return;
4040
- if (this.items.some((item) => this.extractId(item) === id)) {
4185
+ if (id != null && this.items.some((item) => this.extractId(item) === id)) {
4186
+ return;
4187
+ }
4188
+ if (id == null && this.items.includes(entity)) {
4041
4189
  return;
4042
4190
  }
4043
4191
  this.items.push(entity);
@@ -4733,8 +4881,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4733
4881
  * @returns New query builder instance with the subquery selection
4734
4882
 
4735
4883
  */
4736
- selectSubquery(alias, sub) {
4737
- const query = this.resolveQueryNode(sub);
4884
+ selectSubquery(alias, sub2) {
4885
+ const query = this.resolveQueryNode(sub2);
4738
4886
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4739
4887
  }
4740
4888
  /**
@@ -4916,16 +5064,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4916
5064
  return this.clone(nextContext);
4917
5065
  }
4918
5066
  /**
4919
-
4920
- * Adds a GROUP BY clause to the query
4921
-
4922
- * @param col - Column definition or column node to group by
4923
-
4924
- * @returns New query builder instance with the GROUP BY clause
4925
-
4926
- */
4927
- groupBy(col2) {
4928
- const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col2));
5067
+ * Adds a GROUP BY clause to the query
5068
+ * @param term - Column definition or ordering term to group by
5069
+ * @returns New query builder instance with the GROUP BY clause
5070
+ */
5071
+ groupBy(term) {
5072
+ const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(term));
4929
5073
  return this.clone(nextContext);
4930
5074
  }
4931
5075
  /**
@@ -4942,18 +5086,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4942
5086
  return this.clone(nextContext);
4943
5087
  }
4944
5088
  /**
4945
-
4946
- * Adds an ORDER BY clause to the query
4947
-
4948
- * @param col - Column definition or column node to order by
4949
-
4950
- * @param direction - Order direction (defaults to ASC)
4951
-
4952
- * @returns New query builder instance with the ORDER BY clause
4953
-
4954
- */
4955
- orderBy(col2, direction = ORDER_DIRECTIONS.ASC) {
4956
- const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col2, direction));
5089
+ * Adds an ORDER BY clause to the query
5090
+ * @param term - Column definition or ordering term to order by
5091
+ * @param directionOrOptions - Order direction or options (defaults to ASC)
5092
+ * @returns New query builder instance with the ORDER BY clause
5093
+ */
5094
+ orderBy(term, directionOrOptions = ORDER_DIRECTIONS.ASC) {
5095
+ const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
5096
+ const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
5097
+ const nextContext = this.applyAst(
5098
+ this.context,
5099
+ (service) => service.withOrderBy(term, dir, options.nulls, options.collation)
5100
+ );
4957
5101
  return this.clone(nextContext);
4958
5102
  }
4959
5103
  /**
@@ -5364,15 +5508,36 @@ var InsertQueryState = class _InsertQueryState {
5364
5508
  type: "InsertQuery",
5365
5509
  into: createTableNode(table),
5366
5510
  columns: [],
5367
- values: []
5511
+ source: {
5512
+ type: "InsertValues",
5513
+ rows: []
5514
+ }
5368
5515
  };
5369
5516
  }
5370
5517
  clone(nextAst) {
5371
5518
  return new _InsertQueryState(this.table, nextAst);
5372
5519
  }
5520
+ ensureColumnsFromRow(rows) {
5521
+ if (this.ast.columns.length) return this.ast.columns;
5522
+ return buildColumnNodes(this.table, Object.keys(rows[0]));
5523
+ }
5524
+ appendValues(rows) {
5525
+ if (this.ast.source.type === "InsertValues") {
5526
+ return [...this.ast.source.rows, ...rows];
5527
+ }
5528
+ return rows;
5529
+ }
5530
+ getTableColumns() {
5531
+ const names = Object.keys(this.table.columns);
5532
+ if (!names.length) return [];
5533
+ return buildColumnNodes(this.table, names);
5534
+ }
5373
5535
  withValues(rows) {
5374
5536
  if (!rows.length) return this;
5375
- const definedColumns = this.ast.columns.length ? this.ast.columns : buildColumnNodes(this.table, Object.keys(rows[0]));
5537
+ if (this.ast.source.type === "InsertSelect") {
5538
+ throw new Error("Cannot mix INSERT ... VALUES with INSERT ... SELECT source.");
5539
+ }
5540
+ const definedColumns = this.ensureColumnsFromRow(rows);
5376
5541
  const newRows = rows.map(
5377
5542
  (row, rowIndex) => definedColumns.map((column) => {
5378
5543
  const rawValue = row[column.name];
@@ -5387,7 +5552,34 @@ var InsertQueryState = class _InsertQueryState {
5387
5552
  return this.clone({
5388
5553
  ...this.ast,
5389
5554
  columns: definedColumns,
5390
- values: [...this.ast.values, ...newRows]
5555
+ source: {
5556
+ type: "InsertValues",
5557
+ rows: this.appendValues(newRows)
5558
+ }
5559
+ });
5560
+ }
5561
+ withColumns(columns) {
5562
+ if (!columns.length) return this;
5563
+ return this.clone({
5564
+ ...this.ast,
5565
+ columns: [...columns]
5566
+ });
5567
+ }
5568
+ withSelect(query, columns) {
5569
+ const targetColumns = columns.length ? columns : this.ast.columns.length ? this.ast.columns : this.getTableColumns();
5570
+ if (!targetColumns.length) {
5571
+ throw new Error("INSERT ... SELECT requires specifying destination columns.");
5572
+ }
5573
+ if (this.ast.source.type === "InsertValues" && this.ast.source.rows.length) {
5574
+ throw new Error("Cannot mix INSERT ... SELECT with INSERT ... VALUES source.");
5575
+ }
5576
+ return this.clone({
5577
+ ...this.ast,
5578
+ columns: [...targetColumns],
5579
+ source: {
5580
+ type: "InsertSelect",
5581
+ query
5582
+ }
5391
5583
  });
5392
5584
  }
5393
5585
  withReturning(columns) {
@@ -5412,11 +5604,27 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
5412
5604
  if (!rows.length) return this;
5413
5605
  return this.clone(this.state.withValues(rows));
5414
5606
  }
5607
+ columns(...columns) {
5608
+ if (!columns.length) return this;
5609
+ return this.clone(this.state.withColumns(this.resolveColumnNodes(columns)));
5610
+ }
5611
+ fromSelect(query, columns = []) {
5612
+ const ast = this.resolveSelectQuery(query);
5613
+ const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
5614
+ return this.clone(this.state.withSelect(ast, nodes));
5615
+ }
5415
5616
  returning(...columns) {
5416
5617
  if (!columns.length) return this;
5417
5618
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5418
5619
  return this.clone(this.state.withReturning(nodes));
5419
5620
  }
5621
+ // Helpers for column/AST resolution
5622
+ resolveColumnNodes(columns) {
5623
+ return columns.map((column) => buildColumnNode(this.table, column));
5624
+ }
5625
+ resolveSelectQuery(query) {
5626
+ return typeof query.getAST === "function" ? query.getAST() : query;
5627
+ }
5420
5628
  compile(arg) {
5421
5629
  if (typeof arg.compileInsert === "function") {
5422
5630
  return arg.compileInsert(this.state.ast);
@@ -5450,7 +5658,8 @@ var UpdateQueryState = class _UpdateQueryState {
5450
5658
  this.ast = ast ?? {
5451
5659
  type: "UpdateQuery",
5452
5660
  table: createTableNode(table),
5453
- set: []
5661
+ set: [],
5662
+ joins: []
5454
5663
  };
5455
5664
  }
5456
5665
  clone(nextAst) {
@@ -5489,6 +5698,27 @@ var UpdateQueryState = class _UpdateQueryState {
5489
5698
  returning: [...columns]
5490
5699
  });
5491
5700
  }
5701
+ withFrom(from) {
5702
+ return this.clone({
5703
+ ...this.ast,
5704
+ from
5705
+ });
5706
+ }
5707
+ withJoin(join) {
5708
+ return this.clone({
5709
+ ...this.ast,
5710
+ joins: [...this.ast.joins ?? [], join]
5711
+ });
5712
+ }
5713
+ withTableAlias(alias) {
5714
+ return this.clone({
5715
+ ...this.ast,
5716
+ table: {
5717
+ ...this.ast.table,
5718
+ alias
5719
+ }
5720
+ });
5721
+ }
5492
5722
  };
5493
5723
 
5494
5724
  // src/query-builder/update.ts
@@ -5500,6 +5730,18 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5500
5730
  clone(state) {
5501
5731
  return new _UpdateQueryBuilder(this.table, state);
5502
5732
  }
5733
+ as(alias) {
5734
+ return this.clone(this.state.withTableAlias(alias));
5735
+ }
5736
+ from(source) {
5737
+ const tableSource = this.resolveTableSource(source);
5738
+ return this.clone(this.state.withFrom(tableSource));
5739
+ }
5740
+ join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
5741
+ const joinTarget = this.resolveJoinTarget(table);
5742
+ const joinNode = createJoinNode(kind, joinTarget, condition, relationName);
5743
+ return this.clone(this.state.withJoin(joinNode));
5744
+ }
5503
5745
  set(values) {
5504
5746
  return this.clone(this.state.withSet(values));
5505
5747
  }
@@ -5511,6 +5753,16 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5511
5753
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5512
5754
  return this.clone(this.state.withReturning(nodes));
5513
5755
  }
5756
+ resolveTableSource(source) {
5757
+ if (isTableSourceNode(source)) {
5758
+ return source;
5759
+ }
5760
+ return { type: "Table", name: source.name, schema: source.schema };
5761
+ }
5762
+ resolveJoinTarget(table) {
5763
+ if (typeof table === "string") return table;
5764
+ return this.resolveTableSource(table);
5765
+ }
5514
5766
  compile(arg) {
5515
5767
  if (typeof arg.compileUpdate === "function") {
5516
5768
  return arg.compileUpdate(this.state.ast);
@@ -5525,6 +5777,7 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5525
5777
  return this.state.ast;
5526
5778
  }
5527
5779
  };
5780
+ var isTableSourceNode = (source) => typeof source.type === "string";
5528
5781
 
5529
5782
  // src/query-builder/delete-query-state.ts
5530
5783
  var DeleteQueryState = class _DeleteQueryState {
@@ -5532,7 +5785,8 @@ var DeleteQueryState = class _DeleteQueryState {
5532
5785
  this.table = table;
5533
5786
  this.ast = ast ?? {
5534
5787
  type: "DeleteQuery",
5535
- from: createTableNode(table)
5788
+ from: createTableNode(table),
5789
+ joins: []
5536
5790
  };
5537
5791
  }
5538
5792
  clone(nextAst) {
@@ -5550,6 +5804,27 @@ var DeleteQueryState = class _DeleteQueryState {
5550
5804
  returning: [...columns]
5551
5805
  });
5552
5806
  }
5807
+ withUsing(source) {
5808
+ return this.clone({
5809
+ ...this.ast,
5810
+ using: source
5811
+ });
5812
+ }
5813
+ withJoin(join) {
5814
+ return this.clone({
5815
+ ...this.ast,
5816
+ joins: [...this.ast.joins ?? [], join]
5817
+ });
5818
+ }
5819
+ withTableAlias(alias) {
5820
+ return this.clone({
5821
+ ...this.ast,
5822
+ from: {
5823
+ ...this.ast.from,
5824
+ alias
5825
+ }
5826
+ });
5827
+ }
5553
5828
  };
5554
5829
 
5555
5830
  // src/query-builder/delete.ts
@@ -5564,11 +5839,32 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
5564
5839
  where(expr) {
5565
5840
  return this.clone(this.state.withWhere(expr));
5566
5841
  }
5842
+ as(alias) {
5843
+ return this.clone(this.state.withTableAlias(alias));
5844
+ }
5845
+ using(source) {
5846
+ return this.clone(this.state.withUsing(this.resolveTableSource(source)));
5847
+ }
5848
+ join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
5849
+ const target = this.resolveJoinTarget(table);
5850
+ const joinNode = createJoinNode(kind, target, condition, relationName);
5851
+ return this.clone(this.state.withJoin(joinNode));
5852
+ }
5567
5853
  returning(...columns) {
5568
5854
  if (!columns.length) return this;
5569
5855
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5570
5856
  return this.clone(this.state.withReturning(nodes));
5571
5857
  }
5858
+ resolveTableSource(source) {
5859
+ if (isTableSourceNode2(source)) {
5860
+ return source;
5861
+ }
5862
+ return { type: "Table", name: source.name, schema: source.schema };
5863
+ }
5864
+ resolveJoinTarget(table) {
5865
+ if (typeof table === "string") return table;
5866
+ return this.resolveTableSource(table);
5867
+ }
5572
5868
  compile(arg) {
5573
5869
  if (typeof arg.compileDelete === "function") {
5574
5870
  return arg.compileDelete(this.state.ast);
@@ -5583,6 +5879,7 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
5583
5879
  return this.state.ast;
5584
5880
  }
5585
5881
  };
5882
+ var isTableSourceNode2 = (source) => typeof source.type === "string";
5586
5883
 
5587
5884
  // src/core/ddl/sql-writing.ts
5588
5885
  var resolvePrimaryKey = (table) => {
@@ -6781,7 +7078,7 @@ var TypeScriptGenerator = class {
6781
7078
  lines.push(`.where(${this.printExpression(ast.where)})`);
6782
7079
  }
6783
7080
  if (ast.groupBy && ast.groupBy.length) {
6784
- const cols = ast.groupBy.map((c) => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(", ");
7081
+ const cols = ast.groupBy.map((term) => this.printOrderingTerm(term)).join(", ");
6785
7082
  lines.push(`.groupBy(${cols})`);
6786
7083
  }
6787
7084
  if (ast.having) {
@@ -6789,7 +7086,16 @@ var TypeScriptGenerator = class {
6789
7086
  }
6790
7087
  if (ast.orderBy && ast.orderBy.length) {
6791
7088
  ast.orderBy.forEach((o) => {
6792
- lines.push(`.orderBy(${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name}, '${o.direction}')`);
7089
+ const term = this.printOrderingTerm(o.term);
7090
+ const opts = [`direction: '${o.direction}'`];
7091
+ if (o.nulls) opts.push(`nulls: '${o.nulls}'`);
7092
+ if (o.collation) opts.push(`collation: '${o.collation}'`);
7093
+ const hasOpts = opts.length > 1;
7094
+ if (hasOpts) {
7095
+ lines.push(`.orderBy(${term}, { ${opts.join(", ")} })`);
7096
+ } else {
7097
+ lines.push(`.orderBy(${term}, '${o.direction}')`);
7098
+ }
6793
7099
  });
6794
7100
  }
6795
7101
  if (ast.limit) lines.push(`.limit(${ast.limit})`);
@@ -6812,6 +7118,29 @@ var TypeScriptGenerator = class {
6812
7118
  printOperand(node) {
6813
7119
  return visitOperand(node, this);
6814
7120
  }
7121
+ /**
7122
+ * Prints an ordering term (operand/expression/alias) to TypeScript code.
7123
+ */
7124
+ printOrderingTerm(term) {
7125
+ if (!term || !term.type) {
7126
+ throw new Error("Unsupported ordering term");
7127
+ }
7128
+ switch (term.type) {
7129
+ case "Column":
7130
+ return `${this.namingStrategy.tableToSymbol(term.table)}.${term.name}`;
7131
+ case "AliasRef":
7132
+ return this.visitAliasRef(term);
7133
+ case "Literal":
7134
+ case "Function":
7135
+ case "JsonPath":
7136
+ case "ScalarSubquery":
7137
+ case "CaseExpression":
7138
+ case "WindowFunction":
7139
+ return this.printOperand(term);
7140
+ default:
7141
+ return this.printExpression(term);
7142
+ }
7143
+ }
6815
7144
  visitBinaryExpression(binary) {
6816
7145
  return this.printBinaryExpression(binary);
6817
7146
  }
@@ -6830,6 +7159,9 @@ var TypeScriptGenerator = class {
6830
7159
  visitBetweenExpression(betweenExpr) {
6831
7160
  return this.printBetweenExpression(betweenExpr);
6832
7161
  }
7162
+ visitArithmeticExpression(arithExpr) {
7163
+ return this.printArithmeticExpression(arithExpr);
7164
+ }
6833
7165
  visitColumn(node) {
6834
7166
  return this.printColumnOperand(node);
6835
7167
  }
@@ -6851,6 +7183,9 @@ var TypeScriptGenerator = class {
6851
7183
  visitWindowFunction(node) {
6852
7184
  return this.printWindowFunctionOperand(node);
6853
7185
  }
7186
+ visitAliasRef(node) {
7187
+ return `aliasRef('${node.name}')`;
7188
+ }
6854
7189
  /**
6855
7190
  * Prints a binary expression to TypeScript code
6856
7191
  * @param binary - Binary expression node
@@ -6881,6 +7216,11 @@ var TypeScriptGenerator = class {
6881
7216
  ${parts.join(",\n ")}
6882
7217
  )`;
6883
7218
  }
7219
+ printArithmeticExpression(expr) {
7220
+ const left2 = this.printOperand(expr.left);
7221
+ const right2 = this.printOperand(expr.right);
7222
+ return `${left2} ${expr.operator} ${right2}`;
7223
+ }
6884
7224
  /**
6885
7225
  * Prints an IN expression to TypeScript code
6886
7226
  * @param inExpr - IN expression node
@@ -6888,9 +7228,13 @@ var TypeScriptGenerator = class {
6888
7228
  */
6889
7229
  printInExpression(inExpr) {
6890
7230
  const left2 = this.printOperand(inExpr.left);
6891
- const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
6892
7231
  const fn4 = this.mapOp(inExpr.operator);
6893
- return `${fn4}(${left2}, [${values}])`;
7232
+ if (Array.isArray(inExpr.right)) {
7233
+ const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
7234
+ return `${fn4}(${left2}, [${values}])`;
7235
+ }
7236
+ const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
7237
+ return `${fn4}(${left2}, (${subquery}))`;
6894
7238
  }
6895
7239
  /**
6896
7240
  * Prints a null expression to TypeScript code
@@ -6994,7 +7338,12 @@ var TypeScriptGenerator = class {
6994
7338
  parts.push(partitionClause);
6995
7339
  }
6996
7340
  if (node.orderBy && node.orderBy.length > 0) {
6997
- const orderClause = "ORDER BY " + node.orderBy.map((o) => `${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
7341
+ const orderClause = "ORDER BY " + node.orderBy.map((o) => {
7342
+ const term = this.printOrderingTerm(o.term);
7343
+ const collation = o.collation ? ` COLLATE ${o.collation}` : "";
7344
+ const nulls = o.nulls ? ` NULLS ${o.nulls}` : "";
7345
+ return `${term} ${o.direction}${collation}${nulls}`;
7346
+ }).join(", ");
6998
7347
  parts.push(orderClause);
6999
7348
  }
7000
7349
  result += parts.join(" ");
@@ -7070,6 +7419,13 @@ var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
7070
7419
 
7071
7420
  // src/orm/unit-of-work.ts
7072
7421
  var UnitOfWork = class {
7422
+ /**
7423
+ * Creates a new UnitOfWork instance.
7424
+ * @param dialect - The database dialect
7425
+ * @param executor - The database executor
7426
+ * @param identityMap - The identity map
7427
+ * @param hookContext - Function to get the hook context
7428
+ */
7073
7429
  constructor(dialect, executor, identityMap, hookContext) {
7074
7430
  this.dialect = dialect;
7075
7431
  this.executor = executor;
@@ -7077,21 +7433,50 @@ var UnitOfWork = class {
7077
7433
  this.hookContext = hookContext;
7078
7434
  this.trackedEntities = /* @__PURE__ */ new Map();
7079
7435
  }
7436
+ /**
7437
+ * Gets the identity buckets map.
7438
+ */
7080
7439
  get identityBuckets() {
7081
7440
  return this.identityMap.bucketsMap;
7082
7441
  }
7442
+ /**
7443
+ * Gets all tracked entities.
7444
+ * @returns Array of tracked entities
7445
+ */
7083
7446
  getTracked() {
7084
7447
  return Array.from(this.trackedEntities.values());
7085
7448
  }
7449
+ /**
7450
+ * Gets an entity by table and primary key.
7451
+ * @param table - The table definition
7452
+ * @param pk - The primary key value
7453
+ * @returns The entity or undefined if not found
7454
+ */
7086
7455
  getEntity(table, pk) {
7087
7456
  return this.identityMap.getEntity(table, pk);
7088
7457
  }
7458
+ /**
7459
+ * Gets all tracked entities for a specific table.
7460
+ * @param table - The table definition
7461
+ * @returns Array of tracked entities
7462
+ */
7089
7463
  getEntitiesForTable(table) {
7090
7464
  return this.identityMap.getEntitiesForTable(table);
7091
7465
  }
7466
+ /**
7467
+ * Finds a tracked entity.
7468
+ * @param entity - The entity to find
7469
+ * @returns The tracked entity or undefined if not found
7470
+ */
7092
7471
  findTracked(entity) {
7093
7472
  return this.trackedEntities.get(entity);
7094
7473
  }
7474
+ /**
7475
+ * Sets an entity in the identity map.
7476
+ * @param table - The table definition
7477
+ * @param pk - The primary key value
7478
+ * @param entity - The entity instance
7479
+ */
7095
7480
  setEntity(table, pk, entity) {
7096
7481
  if (pk === null || pk === void 0) return;
7097
7482
  let tracked = this.trackedEntities.get(entity);
@@ -7109,6 +7494,12 @@ var UnitOfWork = class {
7109
7494
  }
7110
7495
  this.registerIdentity(tracked);
7111
7496
  }
7497
+ /**
7498
+ * Tracks a new entity.
7499
+ * @param table - The table definition
7500
+ * @param entity - The entity instance
7501
+ * @param pk - Optional primary key value
7502
+ */
7112
7503
  trackNew(table, entity, pk) {
7113
7504
  const tracked = {
7114
7505
  table,
@@ -7122,6 +7513,12 @@ var UnitOfWork = class {
7122
7513
  this.registerIdentity(tracked);
7123
7514
  }
7124
7515
  }
7516
+ /**
7517
+ * Tracks a managed entity.
7518
+ * @param table - The table definition
7519
+ * @param pk - The primary key value
7520
+ * @param entity - The entity instance
7521
+ */
7125
7522
  trackManaged(table, pk, entity) {
7126
7523
  const tracked = {
7127
7524
  table,
@@ -7133,17 +7530,28 @@ var UnitOfWork = class {
7133
7530
  this.trackedEntities.set(entity, tracked);
7134
7531
  this.registerIdentity(tracked);
7135
7532
  }
7533
+ /**
7534
+ * Marks an entity as dirty (modified).
7535
+ * @param entity - The entity to mark as dirty
7536
+ */
7136
7537
  markDirty(entity) {
7137
7538
  const tracked = this.trackedEntities.get(entity);
7138
7539
  if (!tracked) return;
7139
7540
  if (tracked.status === "new" /* New */ || tracked.status === "removed" /* Removed */) return;
7140
7541
  tracked.status = "dirty" /* Dirty */;
7141
7542
  }
7543
+ /**
7544
+ * Marks an entity as removed.
7545
+ * @param entity - The entity to mark as removed
7546
+ */
7142
7547
  markRemoved(entity) {
7143
7548
  const tracked = this.trackedEntities.get(entity);
7144
7549
  if (!tracked) return;
7145
7550
  tracked.status = "removed" /* Removed */;
7146
7551
  }
7552
+ /**
7553
+ * Flushes pending changes to the database.
7554
+ */
7147
7555
  async flush() {
7148
7556
  const toFlush = Array.from(this.trackedEntities.values());
7149
7557
  for (const tracked of toFlush) {
@@ -7162,10 +7570,17 @@ var UnitOfWork = class {
7162
7570
  }
7163
7571
  }
7164
7572
  }
7573
+ /**
7574
+ * Resets the unit of work by clearing all tracked entities and identity map.
7575
+ */
7165
7576
  reset() {
7166
7577
  this.trackedEntities.clear();
7167
7578
  this.identityMap.clear();
7168
7579
  }
7580
+ /**
7581
+ * Flushes an insert operation for a new entity.
7582
+ * @param tracked - The tracked entity to insert
7583
+ */
7169
7584
  async flushInsert(tracked) {
7170
7585
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
7171
7586
  const payload = this.extractColumns(tracked.table, tracked.entity);
@@ -7182,6 +7597,10 @@ var UnitOfWork = class {
7182
7597
  this.registerIdentity(tracked);
7183
7598
  await this.runHook(tracked.table.hooks?.afterInsert, tracked);
7184
7599
  }
7600
+ /**
7601
+ * Flushes an update operation for a modified entity.
7602
+ * @param tracked - The tracked entity to update
7603
+ */
7185
7604
  async flushUpdate(tracked) {
7186
7605
  if (tracked.pk == null) return;
7187
7606
  const changes = this.computeChanges(tracked);
@@ -7204,6 +7623,10 @@ var UnitOfWork = class {
7204
7623
  this.registerIdentity(tracked);
7205
7624
  await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
7206
7625
  }
7626
+ /**
7627
+ * Flushes a delete operation for a removed entity.
7628
+ * @param tracked - The tracked entity to delete
7629
+ */
7207
7630
  async flushDelete(tracked) {
7208
7631
  if (tracked.pk == null) return;
7209
7632
  await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
@@ -7217,10 +7640,20 @@ var UnitOfWork = class {
7217
7640
  this.identityMap.remove(tracked);
7218
7641
  await this.runHook(tracked.table.hooks?.afterDelete, tracked);
7219
7642
  }
7643
+ /**
7644
+ * Runs a table hook if defined.
7645
+ * @param hook - The hook function
7646
+ * @param tracked - The tracked entity
7647
+ */
7220
7648
  async runHook(hook, tracked) {
7221
7649
  if (!hook) return;
7222
7650
  await hook(this.hookContext(), tracked.entity);
7223
7651
  }
7652
+ /**
7653
+ * Computes changes between current entity state and original snapshot.
7654
+ * @param tracked - The tracked entity
7655
+ * @returns Object with changed column values
7656
+ */
7224
7657
  computeChanges(tracked) {
7225
7658
  const snapshot = tracked.original ?? {};
7226
7659
  const changes = {};
@@ -7232,6 +7665,12 @@ var UnitOfWork = class {
7232
7665
  }
7233
7666
  return changes;
7234
7667
  }
7668
+ /**
7669
+ * Extracts column values from an entity.
7670
+ * @param table - The table definition
7671
+ * @param entity - The entity instance
7672
+ * @returns Object with column values
7673
+ */
7235
7674
  extractColumns(table, entity) {
7236
7675
  const payload = {};
7237
7676
  for (const column of Object.keys(table.columns)) {
@@ -7240,9 +7679,19 @@ var UnitOfWork = class {
7240
7679
  }
7241
7680
  return payload;
7242
7681
  }
7682
+ /**
7683
+ * Executes a compiled query.
7684
+ * @param compiled - The compiled query
7685
+ * @returns Query results
7686
+ */
7243
7687
  async executeCompiled(compiled) {
7244
7688
  return this.executor.executeSql(compiled.sql, compiled.params);
7245
7689
  }
7690
+ /**
7691
+ * Gets columns for RETURNING clause.
7692
+ * @param table - The table definition
7693
+ * @returns Array of column nodes
7694
+ */
7246
7695
  getReturningColumns(table) {
7247
7696
  return Object.values(table.columns).map((column) => ({
7248
7697
  type: "Column",
@@ -7251,6 +7700,11 @@ var UnitOfWork = class {
7251
7700
  alias: column.name
7252
7701
  }));
7253
7702
  }
7703
+ /**
7704
+ * Applies RETURNING clause results to the tracked entity.
7705
+ * @param tracked - The tracked entity
7706
+ * @param results - Query results
7707
+ */
7254
7708
  applyReturningResults(tracked, results) {
7255
7709
  if (!this.dialect.supportsReturning()) return;
7256
7710
  const first = results[0];
@@ -7262,15 +7716,30 @@ var UnitOfWork = class {
7262
7716
  tracked.entity[columnName] = row[i];
7263
7717
  }
7264
7718
  }
7719
+ /**
7720
+ * Normalizes a column name by removing quotes and table prefixes.
7721
+ * @param column - The column name to normalize
7722
+ * @returns Normalized column name
7723
+ */
7265
7724
  normalizeColumnName(column) {
7266
7725
  const parts = column.split(".");
7267
7726
  const candidate = parts[parts.length - 1];
7268
7727
  return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
7269
7728
  }
7729
+ /**
7730
+ * Registers an entity in the identity map.
7731
+ * @param tracked - The tracked entity to register
7732
+ */
7270
7733
  registerIdentity(tracked) {
7271
7734
  if (tracked.pk == null) return;
7272
7735
  this.identityMap.register(tracked);
7273
7736
  }
7737
+ /**
7738
+ * Creates a snapshot of an entity's current state.
7739
+ * @param table - The table definition
7740
+ * @param entity - The entity instance
7741
+ * @returns Object with entity state
7742
+ */
7274
7743
  createSnapshot(table, entity) {
7275
7744
  const snapshot = {};
7276
7745
  for (const column of Object.keys(table.columns)) {
@@ -7278,6 +7747,11 @@ var UnitOfWork = class {
7278
7747
  }
7279
7748
  return snapshot;
7280
7749
  }
7750
+ /**
7751
+ * Gets the primary key value from a tracked entity.
7752
+ * @param tracked - The tracked entity
7753
+ * @returns Primary key value or null
7754
+ */
7281
7755
  getPrimaryKeyValue(tracked) {
7282
7756
  const key = findPrimaryKey(tracked.table);
7283
7757
  const val = tracked.entity[key];
@@ -7288,6 +7762,10 @@ var UnitOfWork = class {
7288
7762
 
7289
7763
  // src/orm/domain-event-bus.ts
7290
7764
  var DomainEventBus = class {
7765
+ /**
7766
+ * Creates a new DomainEventBus instance.
7767
+ * @param initialHandlers - Optional initial event handlers
7768
+ */
7291
7769
  constructor(initialHandlers) {
7292
7770
  this.handlers = /* @__PURE__ */ new Map();
7293
7771
  if (initialHandlers) {
@@ -7298,15 +7776,32 @@ var DomainEventBus = class {
7298
7776
  }
7299
7777
  }
7300
7778
  }
7779
+ /**
7780
+ * Registers an event handler for a specific event type.
7781
+ * @template TType - The event type
7782
+ * @param type - The event type
7783
+ * @param handler - The event handler
7784
+ */
7301
7785
  on(type, handler) {
7302
7786
  const key = type;
7303
7787
  const existing = this.handlers.get(key) ?? [];
7304
7788
  existing.push(handler);
7305
7789
  this.handlers.set(key, existing);
7306
7790
  }
7791
+ /**
7792
+ * Registers an event handler for a specific event type (alias for on).
7793
+ * @template TType - The event type
7794
+ * @param type - The event type
7795
+ * @param handler - The event handler
7796
+ */
7307
7797
  register(type, handler) {
7308
7798
  this.on(type, handler);
7309
7799
  }
7800
+ /**
7801
+ * Dispatches domain events for tracked entities.
7802
+ * @param trackedEntities - Iterable of tracked entities
7803
+ * @param ctx - The context to pass to handlers
7804
+ */
7310
7805
  async dispatch(trackedEntities, ctx) {
7311
7806
  for (const tracked of trackedEntities) {
7312
7807
  const entity = tracked.entity;
@@ -7331,18 +7826,34 @@ var addDomainEvent = (entity, event) => {
7331
7826
 
7332
7827
  // src/orm/relation-change-processor.ts
7333
7828
  var RelationChangeProcessor = class {
7829
+ /**
7830
+ * Creates a new RelationChangeProcessor instance.
7831
+ * @param unitOfWork - The unit of work instance
7832
+ * @param dialect - The database dialect
7833
+ * @param executor - The database executor
7834
+ */
7334
7835
  constructor(unitOfWork, dialect, executor) {
7335
7836
  this.unitOfWork = unitOfWork;
7336
7837
  this.dialect = dialect;
7337
7838
  this.executor = executor;
7338
7839
  this.relationChanges = [];
7339
7840
  }
7841
+ /**
7842
+ * Registers a relation change for processing.
7843
+ * @param entry - The relation change entry
7844
+ */
7340
7845
  registerChange(entry) {
7341
7846
  this.relationChanges.push(entry);
7342
7847
  }
7848
+ /**
7849
+ * Resets the relation change processor by clearing all pending changes.
7850
+ */
7343
7851
  reset() {
7344
7852
  this.relationChanges.length = 0;
7345
7853
  }
7854
+ /**
7855
+ * Processes all pending relation changes.
7856
+ */
7346
7857
  async process() {
7347
7858
  if (!this.relationChanges.length) return;
7348
7859
  const entries = [...this.relationChanges];
@@ -7364,6 +7875,10 @@ var RelationChangeProcessor = class {
7364
7875
  }
7365
7876
  }
7366
7877
  }
7878
+ /**
7879
+ * Handles changes for has-many relations.
7880
+ * @param entry - The relation change entry
7881
+ */
7367
7882
  async handleHasManyChange(entry) {
7368
7883
  const relation = entry.relation;
7369
7884
  const target = entry.change.entity;
@@ -7382,6 +7897,10 @@ var RelationChangeProcessor = class {
7382
7897
  this.detachHasManyChild(tracked.entity, relation);
7383
7898
  }
7384
7899
  }
7900
+ /**
7901
+ * Handles changes for has-one relations.
7902
+ * @param entry - The relation change entry
7903
+ */
7385
7904
  async handleHasOneChange(entry) {
7386
7905
  const relation = entry.relation;
7387
7906
  const target = entry.change.entity;
@@ -7400,8 +7919,16 @@ var RelationChangeProcessor = class {
7400
7919
  this.detachHasOneChild(tracked.entity, relation);
7401
7920
  }
7402
7921
  }
7922
+ /**
7923
+ * Handles changes for belongs-to relations.
7924
+ * @param _entry - The relation change entry (reserved for future use)
7925
+ */
7403
7926
  async handleBelongsToChange(_entry) {
7404
7927
  }
7928
+ /**
7929
+ * Handles changes for belongs-to-many relations.
7930
+ * @param entry - The relation change entry
7931
+ */
7405
7932
  async handleBelongsToManyChange(entry) {
7406
7933
  const relation = entry.relation;
7407
7934
  const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
@@ -7420,11 +7947,22 @@ var RelationChangeProcessor = class {
7420
7947
  }
7421
7948
  }
7422
7949
  }
7950
+ /**
7951
+ * Assigns a foreign key for has-many relations.
7952
+ * @param child - The child entity
7953
+ * @param relation - The has-many relation
7954
+ * @param rootValue - The root entity's primary key value
7955
+ */
7423
7956
  assignHasManyForeignKey(child, relation, rootValue) {
7424
7957
  const current = child[relation.foreignKey];
7425
7958
  if (current === rootValue) return;
7426
7959
  child[relation.foreignKey] = rootValue;
7427
7960
  }
7961
+ /**
7962
+ * Detaches a child entity from has-many relations.
7963
+ * @param child - The child entity
7964
+ * @param relation - The has-many relation
7965
+ */
7428
7966
  detachHasManyChild(child, relation) {
7429
7967
  if (relation.cascade === "all" || relation.cascade === "remove") {
7430
7968
  this.unitOfWork.markRemoved(child);
@@ -7433,11 +7971,22 @@ var RelationChangeProcessor = class {
7433
7971
  child[relation.foreignKey] = null;
7434
7972
  this.unitOfWork.markDirty(child);
7435
7973
  }
7974
+ /**
7975
+ * Assigns a foreign key for has-one relations.
7976
+ * @param child - The child entity
7977
+ * @param relation - The has-one relation
7978
+ * @param rootValue - The root entity's primary key value
7979
+ */
7436
7980
  assignHasOneForeignKey(child, relation, rootValue) {
7437
7981
  const current = child[relation.foreignKey];
7438
7982
  if (current === rootValue) return;
7439
7983
  child[relation.foreignKey] = rootValue;
7440
7984
  }
7985
+ /**
7986
+ * Detaches a child entity from has-one relations.
7987
+ * @param child - The child entity
7988
+ * @param relation - The has-one relation
7989
+ */
7441
7990
  detachHasOneChild(child, relation) {
7442
7991
  if (relation.cascade === "all" || relation.cascade === "remove") {
7443
7992
  this.unitOfWork.markRemoved(child);
@@ -7446,6 +7995,12 @@ var RelationChangeProcessor = class {
7446
7995
  child[relation.foreignKey] = null;
7447
7996
  this.unitOfWork.markDirty(child);
7448
7997
  }
7998
+ /**
7999
+ * Inserts a pivot row for belongs-to-many relations.
8000
+ * @param relation - The belongs-to-many relation
8001
+ * @param rootId - The root entity's primary key value
8002
+ * @param targetId - The target entity's primary key value
8003
+ */
7449
8004
  async insertPivotRow(relation, rootId, targetId) {
7450
8005
  const payload = {
7451
8006
  [relation.pivotForeignKeyToRoot]: rootId,
@@ -7455,6 +8010,12 @@ var RelationChangeProcessor = class {
7455
8010
  const compiled = builder.compile(this.dialect);
7456
8011
  await this.executor.executeSql(compiled.sql, compiled.params);
7457
8012
  }
8013
+ /**
8014
+ * Deletes a pivot row for belongs-to-many relations.
8015
+ * @param relation - The belongs-to-many relation
8016
+ * @param rootId - The root entity's primary key value
8017
+ * @param targetId - The target entity's primary key value
8018
+ */
7458
8019
  async deletePivotRow(relation, rootId, targetId) {
7459
8020
  const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
7460
8021
  const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
@@ -7465,6 +8026,12 @@ var RelationChangeProcessor = class {
7465
8026
  const compiled = builder.compile(this.dialect);
7466
8027
  await this.executor.executeSql(compiled.sql, compiled.params);
7467
8028
  }
8029
+ /**
8030
+ * Resolves the primary key value from an entity.
8031
+ * @param entity - The entity
8032
+ * @param table - The table definition
8033
+ * @returns The primary key value or null
8034
+ */
7468
8035
  resolvePrimaryKeyValue(entity, table) {
7469
8036
  if (!entity) return null;
7470
8037
  const key = findPrimaryKey(table);
@@ -7480,42 +8047,231 @@ var createQueryLoggingExecutor = (executor, logger) => {
7480
8047
  return executor;
7481
8048
  }
7482
8049
  const wrapped = {
8050
+ capabilities: executor.capabilities,
7483
8051
  async executeSql(sql, params) {
7484
8052
  logger({ sql, params });
7485
8053
  return executor.executeSql(sql, params);
7486
- }
8054
+ },
8055
+ beginTransaction: () => executor.beginTransaction(),
8056
+ commitTransaction: () => executor.commitTransaction(),
8057
+ rollbackTransaction: () => executor.rollbackTransaction(),
8058
+ dispose: () => executor.dispose()
7487
8059
  };
7488
- if (executor.beginTransaction) {
7489
- wrapped.beginTransaction = executor.beginTransaction.bind(executor);
7490
- }
7491
- if (executor.commitTransaction) {
7492
- wrapped.commitTransaction = executor.commitTransaction.bind(executor);
7493
- }
7494
- if (executor.rollbackTransaction) {
7495
- wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
7496
- }
7497
8060
  return wrapped;
7498
8061
  };
7499
8062
 
7500
8063
  // src/orm/transaction-runner.ts
7501
8064
  var runInTransaction = async (executor, action) => {
7502
- if (!executor.beginTransaction) {
8065
+ if (!executor.capabilities.transactions) {
7503
8066
  await action();
7504
8067
  return;
7505
8068
  }
7506
8069
  await executor.beginTransaction();
7507
8070
  try {
7508
8071
  await action();
7509
- await executor.commitTransaction?.();
8072
+ await executor.commitTransaction();
7510
8073
  } catch (error) {
7511
- await executor.rollbackTransaction?.();
8074
+ await executor.rollbackTransaction();
7512
8075
  throw error;
7513
8076
  }
7514
8077
  };
7515
8078
 
8079
+ // src/orm/save-graph.ts
8080
+ var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
8081
+ var pickColumns = (table, payload) => {
8082
+ const columns = {};
8083
+ for (const key of Object.keys(table.columns)) {
8084
+ if (payload[key] !== void 0) {
8085
+ columns[key] = payload[key];
8086
+ }
8087
+ }
8088
+ return columns;
8089
+ };
8090
+ var ensureEntity = (session, table, payload) => {
8091
+ const pk = findPrimaryKey(table);
8092
+ const row = pickColumns(table, payload);
8093
+ const pkValue = payload[pk];
8094
+ if (pkValue !== void 0 && pkValue !== null) {
8095
+ const tracked = session.getEntity(table, pkValue);
8096
+ if (tracked) {
8097
+ return tracked;
8098
+ }
8099
+ if (row[pk] === void 0) {
8100
+ row[pk] = pkValue;
8101
+ }
8102
+ }
8103
+ return createEntityFromRow(session, table, row);
8104
+ };
8105
+ var assignColumns = (table, entity, payload) => {
8106
+ for (const key of Object.keys(table.columns)) {
8107
+ if (payload[key] !== void 0) {
8108
+ entity[key] = payload[key];
8109
+ }
8110
+ }
8111
+ };
8112
+ var isEntityInCollection = (items, pkName, entity) => {
8113
+ if (items.includes(entity)) return true;
8114
+ const entityPk = entity[pkName];
8115
+ if (entityPk === void 0 || entityPk === null) return false;
8116
+ return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
8117
+ };
8118
+ var findInCollectionByPk = (items, pkName, pkValue) => {
8119
+ if (pkValue === void 0 || pkValue === null) return void 0;
8120
+ return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
8121
+ };
8122
+ var handleHasMany = async (session, root, relationName, relation, payload, options) => {
8123
+ if (!Array.isArray(payload)) return;
8124
+ const collection = root[relationName];
8125
+ await collection.load();
8126
+ const targetTable = relation.target;
8127
+ const targetPk = findPrimaryKey(targetTable);
8128
+ const existing = collection.getItems();
8129
+ const seen = /* @__PURE__ */ new Set();
8130
+ for (const item of payload) {
8131
+ if (item === null || item === void 0) continue;
8132
+ const asObj = typeof item === "object" ? item : { [targetPk]: item };
8133
+ const pkValue = asObj[targetPk];
8134
+ const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
8135
+ const entity = current ?? ensureEntity(session, targetTable, asObj);
8136
+ assignColumns(targetTable, entity, asObj);
8137
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
8138
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
8139
+ collection.attach(entity);
8140
+ }
8141
+ if (pkValue !== void 0 && pkValue !== null) {
8142
+ seen.add(toKey8(pkValue));
8143
+ }
8144
+ }
8145
+ if (options.pruneMissing) {
8146
+ for (const item of [...collection.getItems()]) {
8147
+ const pkValue = item[targetPk];
8148
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
8149
+ collection.remove(item);
8150
+ }
8151
+ }
8152
+ }
8153
+ };
8154
+ var handleHasOne = async (session, root, relationName, relation, payload, options) => {
8155
+ const ref = root[relationName];
8156
+ if (payload === void 0) return;
8157
+ if (payload === null) {
8158
+ ref.set(null);
8159
+ return;
8160
+ }
8161
+ const pk = findPrimaryKey(relation.target);
8162
+ if (typeof payload === "number" || typeof payload === "string") {
8163
+ const entity = ref.set({ [pk]: payload });
8164
+ if (entity) {
8165
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
8166
+ }
8167
+ return;
8168
+ }
8169
+ const attached = ref.set(payload);
8170
+ if (attached) {
8171
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
8172
+ }
8173
+ };
8174
+ var handleBelongsTo = async (session, root, relationName, relation, payload, options) => {
8175
+ const ref = root[relationName];
8176
+ if (payload === void 0) return;
8177
+ if (payload === null) {
8178
+ ref.set(null);
8179
+ return;
8180
+ }
8181
+ const pk = relation.localKey || findPrimaryKey(relation.target);
8182
+ if (typeof payload === "number" || typeof payload === "string") {
8183
+ const entity = ref.set({ [pk]: payload });
8184
+ if (entity) {
8185
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
8186
+ }
8187
+ return;
8188
+ }
8189
+ const attached = ref.set(payload);
8190
+ if (attached) {
8191
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
8192
+ }
8193
+ };
8194
+ var handleBelongsToMany = async (session, root, relationName, relation, payload, options) => {
8195
+ if (!Array.isArray(payload)) return;
8196
+ const collection = root[relationName];
8197
+ await collection.load();
8198
+ const targetTable = relation.target;
8199
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
8200
+ const seen = /* @__PURE__ */ new Set();
8201
+ for (const item of payload) {
8202
+ if (item === null || item === void 0) continue;
8203
+ if (typeof item === "number" || typeof item === "string") {
8204
+ const id = item;
8205
+ collection.attach(id);
8206
+ seen.add(toKey8(id));
8207
+ continue;
8208
+ }
8209
+ const asObj = item;
8210
+ const pkValue = asObj[targetPk];
8211
+ const entity = pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj) : ensureEntity(session, targetTable, asObj);
8212
+ assignColumns(targetTable, entity, asObj);
8213
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
8214
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
8215
+ collection.attach(entity);
8216
+ }
8217
+ if (pkValue !== void 0 && pkValue !== null) {
8218
+ seen.add(toKey8(pkValue));
8219
+ }
8220
+ }
8221
+ if (options.pruneMissing) {
8222
+ for (const item of [...collection.getItems()]) {
8223
+ const pkValue = item[targetPk];
8224
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
8225
+ collection.detach(item);
8226
+ }
8227
+ }
8228
+ }
8229
+ };
8230
+ var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
8231
+ switch (relation.type) {
8232
+ case RelationKinds.HasMany:
8233
+ return handleHasMany(session, entity, relationName, relation, payload, options);
8234
+ case RelationKinds.HasOne:
8235
+ return handleHasOne(session, entity, relationName, relation, payload, options);
8236
+ case RelationKinds.BelongsTo:
8237
+ return handleBelongsTo(session, entity, relationName, relation, payload, options);
8238
+ case RelationKinds.BelongsToMany:
8239
+ return handleBelongsToMany(session, entity, relationName, relation, payload, options);
8240
+ }
8241
+ };
8242
+ var applyGraphToEntity = async (session, table, entity, payload, options) => {
8243
+ assignColumns(table, entity, payload);
8244
+ for (const [relationName, relation] of Object.entries(table.relations)) {
8245
+ if (!(relationName in payload)) continue;
8246
+ await applyRelation(session, table, entity, relationName, relation, payload[relationName], options);
8247
+ }
8248
+ };
8249
+ var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
8250
+ const table = getTableDefFromEntity(entityClass);
8251
+ if (!table) {
8252
+ throw new Error("Entity metadata has not been bootstrapped");
8253
+ }
8254
+ const root = ensureEntity(session, table, payload);
8255
+ await applyGraphToEntity(session, table, root, payload, options);
8256
+ return root;
8257
+ };
8258
+
7516
8259
  // src/orm/orm-session.ts
7517
8260
  var OrmSession = class {
8261
+ /**
8262
+ * Creates a new OrmSession instance.
8263
+ * @param opts - Session options
8264
+ */
7518
8265
  constructor(opts) {
8266
+ /**
8267
+ * Registers a relation change.
8268
+ * @param root - The root entity
8269
+ * @param relationKey - The relation key
8270
+ * @param rootTable - The root table definition
8271
+ * @param relationName - The relation name
8272
+ * @param relation - The relation definition
8273
+ * @param change - The relation change
8274
+ */
7519
8275
  this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
7520
8276
  this.relationChanges.registerChange(
7521
8277
  buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
@@ -7529,42 +8285,117 @@ var OrmSession = class {
7529
8285
  this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
7530
8286
  this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
7531
8287
  }
8288
+ /**
8289
+ * Releases resources associated with this session (executor/pool leases) and resets tracking.
8290
+ * Must be safe to call multiple times.
8291
+ */
8292
+ async dispose() {
8293
+ try {
8294
+ await this.executor.dispose();
8295
+ } finally {
8296
+ this.unitOfWork.reset();
8297
+ this.relationChanges.reset();
8298
+ }
8299
+ }
8300
+ /**
8301
+ * Gets the database dialect.
8302
+ */
7532
8303
  get dialect() {
7533
8304
  return this.orm.dialect;
7534
8305
  }
8306
+ /**
8307
+ * Gets the identity buckets map.
8308
+ */
7535
8309
  get identityBuckets() {
7536
8310
  return this.unitOfWork.identityBuckets;
7537
8311
  }
8312
+ /**
8313
+ * Gets all tracked entities.
8314
+ */
7538
8315
  get tracked() {
7539
8316
  return this.unitOfWork.getTracked();
7540
8317
  }
8318
+ /**
8319
+ * Gets an entity by table and primary key.
8320
+ * @param table - The table definition
8321
+ * @param pk - The primary key value
8322
+ * @returns The entity or undefined if not found
8323
+ */
7541
8324
  getEntity(table, pk) {
7542
8325
  return this.unitOfWork.getEntity(table, pk);
7543
8326
  }
8327
+ /**
8328
+ * Sets an entity in the identity map.
8329
+ * @param table - The table definition
8330
+ * @param pk - The primary key value
8331
+ * @param entity - The entity instance
8332
+ */
7544
8333
  setEntity(table, pk, entity) {
7545
8334
  this.unitOfWork.setEntity(table, pk, entity);
7546
8335
  }
8336
+ /**
8337
+ * Tracks a new entity.
8338
+ * @param table - The table definition
8339
+ * @param entity - The entity instance
8340
+ * @param pk - Optional primary key value
8341
+ */
7547
8342
  trackNew(table, entity, pk) {
7548
8343
  this.unitOfWork.trackNew(table, entity, pk);
7549
8344
  }
8345
+ /**
8346
+ * Tracks a managed entity.
8347
+ * @param table - The table definition
8348
+ * @param pk - The primary key value
8349
+ * @param entity - The entity instance
8350
+ */
7550
8351
  trackManaged(table, pk, entity) {
7551
8352
  this.unitOfWork.trackManaged(table, pk, entity);
7552
8353
  }
8354
+ /**
8355
+ * Marks an entity as dirty (modified).
8356
+ * @param entity - The entity to mark as dirty
8357
+ */
7553
8358
  markDirty(entity) {
7554
8359
  this.unitOfWork.markDirty(entity);
7555
8360
  }
8361
+ /**
8362
+ * Marks an entity as removed.
8363
+ * @param entity - The entity to mark as removed
8364
+ */
7556
8365
  markRemoved(entity) {
7557
8366
  this.unitOfWork.markRemoved(entity);
7558
8367
  }
8368
+ /**
8369
+ * Gets all tracked entities for a specific table.
8370
+ * @param table - The table definition
8371
+ * @returns Array of tracked entities
8372
+ */
7559
8373
  getEntitiesForTable(table) {
7560
8374
  return this.unitOfWork.getEntitiesForTable(table);
7561
8375
  }
8376
+ /**
8377
+ * Registers an interceptor for flush lifecycle hooks.
8378
+ * @param interceptor - The interceptor to register
8379
+ */
7562
8380
  registerInterceptor(interceptor) {
7563
8381
  this.interceptors.push(interceptor);
7564
8382
  }
8383
+ /**
8384
+ * Registers a domain event handler.
8385
+ * @param type - The event type
8386
+ * @param handler - The event handler
8387
+ */
7565
8388
  registerDomainEventHandler(type, handler) {
7566
8389
  this.domainEvents.on(type, handler);
7567
8390
  }
8391
+ /**
8392
+ * Finds an entity by its primary key.
8393
+ * @template TCtor - The entity constructor type
8394
+ * @param entityClass - The entity constructor
8395
+ * @param id - The primary key value
8396
+ * @returns The entity instance or null if not found
8397
+ * @throws If entity metadata is not bootstrapped or table has no primary key
8398
+ */
7568
8399
  async find(entityClass, id) {
7569
8400
  const table = getTableDefFromEntity(entityClass);
7570
8401
  if (!table) {
@@ -7583,14 +8414,46 @@ var OrmSession = class {
7583
8414
  const rows = await executeHydrated(this, qb);
7584
8415
  return rows[0] ?? null;
7585
8416
  }
8417
+ /**
8418
+ * Finds a single entity using a query builder.
8419
+ * @template TTable - The table type
8420
+ * @param qb - The query builder
8421
+ * @returns The first entity instance or null if not found
8422
+ */
7586
8423
  async findOne(qb) {
7587
8424
  const limited = qb.limit(1);
7588
8425
  const rows = await executeHydrated(this, limited);
7589
8426
  return rows[0] ?? null;
7590
8427
  }
8428
+ /**
8429
+ * Finds multiple entities using a query builder.
8430
+ * @template TTable - The table type
8431
+ * @param qb - The query builder
8432
+ * @returns Array of entity instances
8433
+ */
7591
8434
  async findMany(qb) {
7592
8435
  return executeHydrated(this, qb);
7593
8436
  }
8437
+ /**
8438
+ * Saves an entity graph (root + nested relations) based on a DTO-like payload.
8439
+ * @param entityClass - Root entity constructor
8440
+ * @param payload - DTO payload containing column values and nested relations
8441
+ * @param options - Graph save options
8442
+ * @returns The root entity instance
8443
+ */
8444
+ async saveGraph(entityClass, payload, options) {
8445
+ const { transactional = true, ...graphOptions } = options ?? {};
8446
+ const execute = () => saveGraphInternal(this, entityClass, payload, graphOptions);
8447
+ if (!transactional) {
8448
+ return execute();
8449
+ }
8450
+ return this.transaction(() => execute());
8451
+ }
8452
+ /**
8453
+ * Persists an entity (either inserts or updates).
8454
+ * @param entity - The entity to persist
8455
+ * @throws If entity metadata is not bootstrapped
8456
+ */
7594
8457
  async persist(entity) {
7595
8458
  if (this.unitOfWork.findTracked(entity)) {
7596
8459
  return;
@@ -7607,12 +8470,22 @@ var OrmSession = class {
7607
8470
  this.trackNew(table, entity);
7608
8471
  }
7609
8472
  }
8473
+ /**
8474
+ * Marks an entity for removal.
8475
+ * @param entity - The entity to remove
8476
+ */
7610
8477
  async remove(entity) {
7611
8478
  this.markRemoved(entity);
7612
8479
  }
8480
+ /**
8481
+ * Flushes pending changes to the database.
8482
+ */
7613
8483
  async flush() {
7614
8484
  await this.unitOfWork.flush();
7615
8485
  }
8486
+ /**
8487
+ * Flushes pending changes with interceptors and relation processing.
8488
+ */
7616
8489
  async flushWithHooks() {
7617
8490
  for (const interceptor of this.interceptors) {
7618
8491
  await interceptor.beforeFlush?.(this);
@@ -7624,14 +8497,24 @@ var OrmSession = class {
7624
8497
  await interceptor.afterFlush?.(this);
7625
8498
  }
7626
8499
  }
8500
+ /**
8501
+ * Commits the current transaction.
8502
+ */
7627
8503
  async commit() {
7628
8504
  await runInTransaction(this.executor, async () => {
7629
8505
  await this.flushWithHooks();
7630
8506
  });
7631
8507
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
7632
8508
  }
8509
+ /**
8510
+ * Executes a function within a transaction.
8511
+ * @template T - The return type
8512
+ * @param fn - The function to execute
8513
+ * @returns The result of the function
8514
+ * @throws If the transaction fails
8515
+ */
7633
8516
  async transaction(fn4) {
7634
- if (!this.executor.beginTransaction) {
8517
+ if (!this.executor.capabilities.transactions) {
7635
8518
  const result = await fn4(this);
7636
8519
  await this.commit();
7637
8520
  return result;
@@ -7640,7 +8523,7 @@ var OrmSession = class {
7640
8523
  try {
7641
8524
  const result = await fn4(this);
7642
8525
  await this.flushWithHooks();
7643
- await this.executor.commitTransaction?.();
8526
+ await this.executor.commitTransaction();
7644
8527
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
7645
8528
  return result;
7646
8529
  } catch (err) {
@@ -7648,11 +8531,20 @@ var OrmSession = class {
7648
8531
  throw err;
7649
8532
  }
7650
8533
  }
8534
+ /**
8535
+ * Rolls back the current transaction.
8536
+ */
7651
8537
  async rollback() {
7652
- await this.executor.rollbackTransaction?.();
8538
+ if (this.executor.capabilities.transactions) {
8539
+ await this.executor.rollbackTransaction();
8540
+ }
7653
8541
  this.unitOfWork.reset();
7654
8542
  this.relationChanges.reset();
7655
8543
  }
8544
+ /**
8545
+ * Gets the execution context.
8546
+ * @returns The execution context
8547
+ */
7656
8548
  getExecutionContext() {
7657
8549
  return {
7658
8550
  dialect: this.orm.dialect,
@@ -7660,6 +8552,10 @@ var OrmSession = class {
7660
8552
  interceptors: this.orm.interceptors
7661
8553
  };
7662
8554
  }
8555
+ /**
8556
+ * Gets the hydration context.
8557
+ * @returns The hydration context
8558
+ */
7663
8559
  getHydrationContext() {
7664
8560
  return {
7665
8561
  identityMap: this.identityMap,
@@ -7702,29 +8598,49 @@ var InterceptorPipeline = class {
7702
8598
 
7703
8599
  // src/orm/orm.ts
7704
8600
  var Orm = class {
8601
+ /**
8602
+ * Creates a new ORM instance.
8603
+ * @param opts - ORM options
8604
+ */
7705
8605
  constructor(opts) {
7706
8606
  this.dialect = opts.dialect;
7707
8607
  this.interceptors = opts.interceptors ?? new InterceptorPipeline();
7708
8608
  this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
7709
8609
  this.executorFactory = opts.executorFactory;
7710
8610
  }
7711
- createSession(options) {
7712
- const executor = this.executorFactory.createExecutor(options?.tx);
8611
+ /**
8612
+ * Creates a new ORM session.
8613
+ * @param options - Optional session options
8614
+ * @returns The ORM session
8615
+ */
8616
+ createSession() {
8617
+ const executor = this.executorFactory.createExecutor();
7713
8618
  return new OrmSession({ orm: this, executor });
7714
8619
  }
8620
+ /**
8621
+ * Executes a function within a transaction.
8622
+ * @template T - The return type
8623
+ * @param fn - The function to execute
8624
+ * @returns The result of the function
8625
+ * @throws If the transaction fails
8626
+ */
7715
8627
  async transaction(fn4) {
7716
8628
  const executor = this.executorFactory.createTransactionalExecutor();
7717
8629
  const session = new OrmSession({ orm: this, executor });
7718
8630
  try {
7719
- const result = await fn4(session);
7720
- await session.commit();
7721
- return result;
8631
+ return await session.transaction(() => fn4(session));
7722
8632
  } catch (err) {
7723
- await session.rollback();
7724
8633
  throw err;
7725
8634
  } finally {
8635
+ await session.dispose();
7726
8636
  }
7727
8637
  }
8638
+ /**
8639
+ * Shuts down the ORM and releases underlying resources (pools, timers).
8640
+ */
8641
+ async dispose() {
8642
+ await this.executorFactory.dispose();
8643
+ }
7728
8644
  };
7729
8645
 
7730
8646
  // src/decorators/decorator-metadata.ts
@@ -7983,18 +8899,245 @@ function rowsToQueryResult(rows) {
7983
8899
  return { columns, values };
7984
8900
  }
7985
8901
  function createExecutorFromQueryRunner(runner) {
8902
+ const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
7986
8903
  return {
8904
+ capabilities: {
8905
+ transactions: supportsTransactions
8906
+ },
7987
8907
  async executeSql(sql, params) {
7988
8908
  const rows = await runner.query(sql, params);
7989
8909
  const result = rowsToQueryResult(rows);
7990
8910
  return [result];
7991
8911
  },
7992
- beginTransaction: runner.beginTransaction?.bind(runner),
7993
- commitTransaction: runner.commitTransaction?.bind(runner),
7994
- rollbackTransaction: runner.rollbackTransaction?.bind(runner)
8912
+ async beginTransaction() {
8913
+ if (!supportsTransactions) {
8914
+ throw new Error("Transactions are not supported by this executor");
8915
+ }
8916
+ await runner.beginTransaction.call(runner);
8917
+ },
8918
+ async commitTransaction() {
8919
+ if (!supportsTransactions) {
8920
+ throw new Error("Transactions are not supported by this executor");
8921
+ }
8922
+ await runner.commitTransaction.call(runner);
8923
+ },
8924
+ async rollbackTransaction() {
8925
+ if (!supportsTransactions) {
8926
+ throw new Error("Transactions are not supported by this executor");
8927
+ }
8928
+ await runner.rollbackTransaction.call(runner);
8929
+ },
8930
+ async dispose() {
8931
+ await runner.dispose?.call(runner);
8932
+ }
7995
8933
  };
7996
8934
  }
7997
8935
 
8936
+ // src/core/execution/pooling/pool.ts
8937
+ var deferred = () => {
8938
+ let resolve;
8939
+ let reject;
8940
+ const promise = new Promise((res, rej) => {
8941
+ resolve = res;
8942
+ reject = rej;
8943
+ });
8944
+ return { promise, resolve, reject };
8945
+ };
8946
+ var Pool = class {
8947
+ constructor(adapter, options) {
8948
+ this.destroyed = false;
8949
+ this.creating = 0;
8950
+ this.leased = 0;
8951
+ this.idle = [];
8952
+ this.waiters = [];
8953
+ this.reapTimer = null;
8954
+ if (!Number.isFinite(options.max) || options.max <= 0) {
8955
+ throw new Error("Pool options.max must be a positive number");
8956
+ }
8957
+ this.adapter = adapter;
8958
+ this.options = { max: options.max, ...options };
8959
+ const idleTimeout = this.options.idleTimeoutMillis;
8960
+ if (idleTimeout && idleTimeout > 0) {
8961
+ const interval = this.options.reapIntervalMillis ?? Math.max(1e3, Math.floor(idleTimeout / 2));
8962
+ this.reapTimer = setInterval(() => {
8963
+ void this.reapIdle();
8964
+ }, interval);
8965
+ this.reapTimer.unref?.();
8966
+ }
8967
+ const min2 = this.options.min ?? 0;
8968
+ if (min2 > 0) {
8969
+ void this.warm(min2);
8970
+ }
8971
+ }
8972
+ /**
8973
+ * Acquire a resource lease.
8974
+ * The returned lease MUST be released or destroyed.
8975
+ */
8976
+ async acquire() {
8977
+ if (this.destroyed) {
8978
+ throw new Error("Pool is destroyed");
8979
+ }
8980
+ const idle = await this.takeIdleValidated();
8981
+ if (idle) {
8982
+ this.leased++;
8983
+ return this.makeLease(idle);
8984
+ }
8985
+ if (this.totalLive() < this.options.max) {
8986
+ this.creating++;
8987
+ try {
8988
+ const created = await this.adapter.create();
8989
+ this.leased++;
8990
+ return this.makeLease(created);
8991
+ } finally {
8992
+ this.creating--;
8993
+ }
8994
+ }
8995
+ const waiter = deferred();
8996
+ this.waiters.push(waiter);
8997
+ const timeout = this.options.acquireTimeoutMillis;
8998
+ let timer = null;
8999
+ if (timeout && timeout > 0) {
9000
+ timer = setTimeout(() => {
9001
+ const idx = this.waiters.indexOf(waiter);
9002
+ if (idx >= 0) this.waiters.splice(idx, 1);
9003
+ waiter.reject(new Error("Pool acquire timeout"));
9004
+ }, timeout);
9005
+ timer.unref?.();
9006
+ }
9007
+ try {
9008
+ return await waiter.promise;
9009
+ } finally {
9010
+ if (timer) clearTimeout(timer);
9011
+ }
9012
+ }
9013
+ /** Destroy pool and all idle resources; waits for in-flight creations to settle. */
9014
+ async destroy() {
9015
+ if (this.destroyed) return;
9016
+ this.destroyed = true;
9017
+ if (this.reapTimer) {
9018
+ clearInterval(this.reapTimer);
9019
+ this.reapTimer = null;
9020
+ }
9021
+ while (this.waiters.length) {
9022
+ this.waiters.shift().reject(new Error("Pool destroyed"));
9023
+ }
9024
+ while (this.idle.length) {
9025
+ const entry = this.idle.shift();
9026
+ await this.adapter.destroy(entry.resource);
9027
+ }
9028
+ }
9029
+ totalLive() {
9030
+ return this.idle.length + this.leased + this.creating;
9031
+ }
9032
+ makeLease(resource) {
9033
+ let done = false;
9034
+ return {
9035
+ resource,
9036
+ release: async () => {
9037
+ if (done) return;
9038
+ done = true;
9039
+ await this.releaseResource(resource);
9040
+ },
9041
+ destroy: async () => {
9042
+ if (done) return;
9043
+ done = true;
9044
+ await this.destroyResource(resource);
9045
+ }
9046
+ };
9047
+ }
9048
+ async releaseResource(resource) {
9049
+ this.leased = Math.max(0, this.leased - 1);
9050
+ if (this.destroyed) {
9051
+ await this.adapter.destroy(resource);
9052
+ return;
9053
+ }
9054
+ const next = this.waiters.shift();
9055
+ if (next) {
9056
+ this.leased++;
9057
+ next.resolve(this.makeLease(resource));
9058
+ return;
9059
+ }
9060
+ this.idle.push({ resource, lastUsedAt: Date.now() });
9061
+ await this.trimToMinMax();
9062
+ }
9063
+ async destroyResource(resource) {
9064
+ this.leased = Math.max(0, this.leased - 1);
9065
+ await this.adapter.destroy(resource);
9066
+ if (!this.destroyed && this.waiters.length && this.totalLive() < this.options.max) {
9067
+ const waiter = this.waiters.shift();
9068
+ this.creating++;
9069
+ try {
9070
+ const created = await this.adapter.create();
9071
+ this.leased++;
9072
+ waiter.resolve(this.makeLease(created));
9073
+ } catch (err) {
9074
+ waiter.reject(err);
9075
+ } finally {
9076
+ this.creating--;
9077
+ }
9078
+ }
9079
+ }
9080
+ async takeIdleValidated() {
9081
+ while (this.idle.length) {
9082
+ const entry = this.idle.pop();
9083
+ if (!this.adapter.validate) {
9084
+ return entry.resource;
9085
+ }
9086
+ const ok = await this.adapter.validate(entry.resource);
9087
+ if (ok) {
9088
+ return entry.resource;
9089
+ }
9090
+ await this.adapter.destroy(entry.resource);
9091
+ }
9092
+ return null;
9093
+ }
9094
+ async reapIdle() {
9095
+ if (this.destroyed) return;
9096
+ const idleTimeout = this.options.idleTimeoutMillis;
9097
+ if (!idleTimeout || idleTimeout <= 0) return;
9098
+ const now2 = Date.now();
9099
+ const min2 = this.options.min ?? 0;
9100
+ const keep = [];
9101
+ const kill = [];
9102
+ for (const entry of this.idle) {
9103
+ const expired = now2 - entry.lastUsedAt >= idleTimeout;
9104
+ if (expired) kill.push(entry);
9105
+ else keep.push(entry);
9106
+ }
9107
+ while (keep.length < min2 && kill.length) {
9108
+ keep.push(kill.pop());
9109
+ }
9110
+ this.idle.length = 0;
9111
+ this.idle.push(...keep);
9112
+ for (const entry of kill) {
9113
+ await this.adapter.destroy(entry.resource);
9114
+ }
9115
+ }
9116
+ async warm(targetMin) {
9117
+ const min2 = Math.max(0, targetMin);
9118
+ while (!this.destroyed && this.idle.length < min2 && this.totalLive() < this.options.max) {
9119
+ this.creating++;
9120
+ try {
9121
+ const created = await this.adapter.create();
9122
+ this.idle.push({ resource: created, lastUsedAt: Date.now() });
9123
+ } catch {
9124
+ break;
9125
+ } finally {
9126
+ this.creating--;
9127
+ }
9128
+ }
9129
+ }
9130
+ async trimToMinMax() {
9131
+ const max2 = this.options.max;
9132
+ const min2 = this.options.min ?? 0;
9133
+ while (this.totalLive() > max2 && this.idle.length > min2) {
9134
+ const entry = this.idle.shift();
9135
+ if (!entry) break;
9136
+ await this.adapter.destroy(entry.resource);
9137
+ }
9138
+ }
9139
+ };
9140
+
7998
9141
  // src/core/execution/executors/postgres-executor.ts
7999
9142
  function createPostgresExecutor(client) {
8000
9143
  return createExecutorFromQueryRunner({
@@ -8016,7 +9159,11 @@ function createPostgresExecutor(client) {
8016
9159
 
8017
9160
  // src/core/execution/executors/mysql-executor.ts
8018
9161
  function createMysqlExecutor(client) {
9162
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
8019
9163
  return {
9164
+ capabilities: {
9165
+ transactions: supportsTransactions
9166
+ },
8020
9167
  async executeSql(sql, params) {
8021
9168
  const [rows] = await client.query(sql, params);
8022
9169
  if (!Array.isArray(rows)) {
@@ -8028,53 +9175,94 @@ function createMysqlExecutor(client) {
8028
9175
  return [result];
8029
9176
  },
8030
9177
  async beginTransaction() {
8031
- if (!client.beginTransaction) return;
9178
+ if (!supportsTransactions) {
9179
+ throw new Error("Transactions are not supported by this executor");
9180
+ }
8032
9181
  await client.beginTransaction();
8033
9182
  },
8034
9183
  async commitTransaction() {
8035
- if (!client.commit) return;
9184
+ if (!supportsTransactions) {
9185
+ throw new Error("Transactions are not supported by this executor");
9186
+ }
8036
9187
  await client.commit();
8037
9188
  },
8038
9189
  async rollbackTransaction() {
8039
- if (!client.rollback) return;
9190
+ if (!supportsTransactions) {
9191
+ throw new Error("Transactions are not supported by this executor");
9192
+ }
8040
9193
  await client.rollback();
9194
+ },
9195
+ async dispose() {
8041
9196
  }
8042
9197
  };
8043
9198
  }
8044
9199
 
8045
9200
  // src/core/execution/executors/sqlite-executor.ts
8046
9201
  function createSqliteExecutor(client) {
9202
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
8047
9203
  return {
9204
+ capabilities: {
9205
+ transactions: supportsTransactions
9206
+ },
8048
9207
  async executeSql(sql, params) {
8049
9208
  const rows = await client.all(sql, params);
8050
9209
  const result = rowsToQueryResult(rows);
8051
9210
  return [result];
8052
9211
  },
8053
- beginTransaction: client.beginTransaction?.bind(client),
8054
- commitTransaction: client.commitTransaction?.bind(client),
8055
- rollbackTransaction: client.rollbackTransaction?.bind(client)
9212
+ async beginTransaction() {
9213
+ if (!supportsTransactions) {
9214
+ throw new Error("Transactions are not supported by this executor");
9215
+ }
9216
+ await client.beginTransaction();
9217
+ },
9218
+ async commitTransaction() {
9219
+ if (!supportsTransactions) {
9220
+ throw new Error("Transactions are not supported by this executor");
9221
+ }
9222
+ await client.commitTransaction();
9223
+ },
9224
+ async rollbackTransaction() {
9225
+ if (!supportsTransactions) {
9226
+ throw new Error("Transactions are not supported by this executor");
9227
+ }
9228
+ await client.rollbackTransaction();
9229
+ },
9230
+ async dispose() {
9231
+ }
8056
9232
  };
8057
9233
  }
8058
9234
 
8059
9235
  // src/core/execution/executors/mssql-executor.ts
8060
9236
  function createMssqlExecutor(client) {
9237
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
8061
9238
  return {
9239
+ capabilities: {
9240
+ transactions: supportsTransactions
9241
+ },
8062
9242
  async executeSql(sql, params) {
8063
9243
  const { recordset } = await client.query(sql, params);
8064
9244
  const result = rowsToQueryResult(recordset ?? []);
8065
9245
  return [result];
8066
9246
  },
8067
9247
  async beginTransaction() {
8068
- if (!client.beginTransaction) return;
9248
+ if (!supportsTransactions) {
9249
+ throw new Error("Transactions are not supported by this executor");
9250
+ }
8069
9251
  await client.beginTransaction();
8070
9252
  },
8071
9253
  async commitTransaction() {
8072
- if (!client.commit) return;
9254
+ if (!supportsTransactions) {
9255
+ throw new Error("Transactions are not supported by this executor");
9256
+ }
8073
9257
  await client.commit();
8074
9258
  },
8075
9259
  async rollbackTransaction() {
8076
- if (!client.rollback) return;
9260
+ if (!supportsTransactions) {
9261
+ throw new Error("Transactions are not supported by this executor");
9262
+ }
8077
9263
  await client.rollback();
9264
+ },
9265
+ async dispose() {
8078
9266
  }
8079
9267
  };
8080
9268
  }
@@ -8143,6 +9331,86 @@ function createTediousExecutor(connection, module2, options) {
8143
9331
  const client = createTediousMssqlClient(connection, module2, options);
8144
9332
  return createMssqlExecutor(client);
8145
9333
  }
9334
+
9335
+ // src/orm/pooled-executor-factory.ts
9336
+ function createPooledExecutorFactory(opts) {
9337
+ const { pool, adapter } = opts;
9338
+ const makeExecutor = (mode) => {
9339
+ let lease = null;
9340
+ const getLease = async () => {
9341
+ if (lease) return lease;
9342
+ lease = await pool.acquire();
9343
+ return lease;
9344
+ };
9345
+ const executeWithConn = async (conn, sql, params) => {
9346
+ const rows = await adapter.query(conn, sql, params);
9347
+ return [rowsToQueryResult(rows)];
9348
+ };
9349
+ return {
9350
+ capabilities: { transactions: true },
9351
+ async executeSql(sql, params) {
9352
+ if (mode === "sticky") {
9353
+ const l2 = await getLease();
9354
+ return executeWithConn(l2.resource, sql, params);
9355
+ }
9356
+ if (lease) {
9357
+ return executeWithConn(lease.resource, sql, params);
9358
+ }
9359
+ const l = await pool.acquire();
9360
+ try {
9361
+ return await executeWithConn(l.resource, sql, params);
9362
+ } finally {
9363
+ await l.release();
9364
+ }
9365
+ },
9366
+ async beginTransaction() {
9367
+ const l = await getLease();
9368
+ await adapter.beginTransaction(l.resource);
9369
+ },
9370
+ async commitTransaction() {
9371
+ if (!lease) {
9372
+ throw new Error("commitTransaction called without an active transaction");
9373
+ }
9374
+ const l = lease;
9375
+ try {
9376
+ await adapter.commitTransaction(l.resource);
9377
+ } finally {
9378
+ lease = null;
9379
+ await l.release();
9380
+ }
9381
+ },
9382
+ async rollbackTransaction() {
9383
+ if (!lease) {
9384
+ return;
9385
+ }
9386
+ const l = lease;
9387
+ try {
9388
+ await adapter.rollbackTransaction(l.resource);
9389
+ } finally {
9390
+ lease = null;
9391
+ await l.release();
9392
+ }
9393
+ },
9394
+ async dispose() {
9395
+ if (!lease) return;
9396
+ const l = lease;
9397
+ lease = null;
9398
+ await l.release();
9399
+ }
9400
+ };
9401
+ };
9402
+ return {
9403
+ createExecutor() {
9404
+ return makeExecutor("session");
9405
+ },
9406
+ createTransactionalExecutor() {
9407
+ return makeExecutor("sticky");
9408
+ },
9409
+ async dispose() {
9410
+ await pool.destroy();
9411
+ }
9412
+ };
9413
+ }
8146
9414
  // Annotate the CommonJS export names for ESM import in node:
8147
9415
  0 && (module.exports = {
8148
9416
  AsyncLocalStorage,
@@ -8162,6 +9430,7 @@ function createTediousExecutor(connection, module2, options) {
8162
9430
  MySqlDialect,
8163
9431
  Orm,
8164
9432
  OrmSession,
9433
+ Pool,
8165
9434
  PostgresDialect,
8166
9435
  PrimaryKey,
8167
9436
  RelationKinds,
@@ -8172,7 +9441,9 @@ function createTediousExecutor(connection, module2, options) {
8172
9441
  UpdateQueryBuilder,
8173
9442
  abs,
8174
9443
  acos,
9444
+ add,
8175
9445
  addDomainEvent,
9446
+ aliasRef,
8176
9447
  and,
8177
9448
  ascii,
8178
9449
  asin,
@@ -8205,6 +9476,7 @@ function createTediousExecutor(connection, module2, options) {
8205
9476
  createLiteral,
8206
9477
  createMssqlExecutor,
8207
9478
  createMysqlExecutor,
9479
+ createPooledExecutorFactory,
8208
9480
  createPostgresExecutor,
8209
9481
  createQueryLoggingExecutor,
8210
9482
  createSqliteExecutor,
@@ -8223,6 +9495,7 @@ function createTediousExecutor(connection, module2, options) {
8223
9495
  degrees,
8224
9496
  denseRank,
8225
9497
  diffSchema,
9498
+ div,
8226
9499
  endOfMonth,
8227
9500
  eq,
8228
9501
  esel,
@@ -8245,6 +9518,7 @@ function createTediousExecutor(connection, module2, options) {
8245
9518
  hasOne,
8246
9519
  hydrateRows,
8247
9520
  inList,
9521
+ inSubquery,
8248
9522
  instr,
8249
9523
  introspectSchema,
8250
9524
  isCaseExpressionNode,
@@ -8280,10 +9554,12 @@ function createTediousExecutor(connection, module2, options) {
8280
9554
  min,
8281
9555
  mod,
8282
9556
  month,
9557
+ mul,
8283
9558
  neq,
8284
9559
  notBetween,
8285
9560
  notExists,
8286
9561
  notInList,
9562
+ notInSubquery,
8287
9563
  notLike,
8288
9564
  now,
8289
9565
  ntile,
@@ -8315,6 +9591,7 @@ function createTediousExecutor(connection, module2, options) {
8315
9591
  sin,
8316
9592
  space,
8317
9593
  sqrt,
9594
+ sub,
8318
9595
  substr,
8319
9596
  sum,
8320
9597
  synchronizeSchema,