metal-orm 1.0.40 → 1.0.42

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 (45) hide show
  1. package/README.md +53 -14
  2. package/dist/index.cjs +1298 -126
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +676 -30
  5. package/dist/index.d.ts +676 -30
  6. package/dist/index.js +1293 -126
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/codegen/typescript.ts +6 -2
  10. package/src/core/ast/expression-builders.ts +25 -4
  11. package/src/core/ast/expression-nodes.ts +3 -1
  12. package/src/core/ast/expression.ts +2 -2
  13. package/src/core/ast/query.ts +24 -2
  14. package/src/core/dialect/abstract.ts +6 -2
  15. package/src/core/dialect/base/join-compiler.ts +9 -12
  16. package/src/core/dialect/base/sql-dialect.ts +98 -17
  17. package/src/core/dialect/mssql/index.ts +30 -62
  18. package/src/core/dialect/sqlite/index.ts +39 -34
  19. package/src/core/execution/db-executor.ts +46 -6
  20. package/src/core/execution/executors/mssql-executor.ts +39 -22
  21. package/src/core/execution/executors/mysql-executor.ts +23 -6
  22. package/src/core/execution/executors/sqlite-executor.ts +29 -3
  23. package/src/core/execution/pooling/pool-types.ts +30 -0
  24. package/src/core/execution/pooling/pool.ts +268 -0
  25. package/src/decorators/bootstrap.ts +7 -7
  26. package/src/index.ts +6 -0
  27. package/src/orm/domain-event-bus.ts +49 -0
  28. package/src/orm/entity-metadata.ts +9 -9
  29. package/src/orm/entity.ts +58 -0
  30. package/src/orm/orm-session.ts +465 -270
  31. package/src/orm/orm.ts +61 -11
  32. package/src/orm/pooled-executor-factory.ts +131 -0
  33. package/src/orm/query-logger.ts +6 -12
  34. package/src/orm/relation-change-processor.ts +75 -0
  35. package/src/orm/relations/many-to-many.ts +4 -2
  36. package/src/orm/save-graph.ts +303 -0
  37. package/src/orm/transaction-runner.ts +3 -3
  38. package/src/orm/unit-of-work.ts +128 -0
  39. package/src/query-builder/delete-query-state.ts +67 -38
  40. package/src/query-builder/delete.ts +37 -1
  41. package/src/query-builder/insert-query-state.ts +131 -61
  42. package/src/query-builder/insert.ts +27 -1
  43. package/src/query-builder/update-query-state.ts +114 -77
  44. package/src/query-builder/update.ts +38 -1
  45. package/src/schema/table.ts +210 -115
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,
@@ -105,6 +106,7 @@ __export(index_exports, {
105
106
  createLiteral: () => createLiteral,
106
107
  createMssqlExecutor: () => createMssqlExecutor,
107
108
  createMysqlExecutor: () => createMysqlExecutor,
109
+ createPooledExecutorFactory: () => createPooledExecutorFactory,
108
110
  createPostgresExecutor: () => createPostgresExecutor,
109
111
  createQueryLoggingExecutor: () => createQueryLoggingExecutor,
110
112
  createSqliteExecutor: () => createSqliteExecutor,
@@ -146,6 +148,7 @@ __export(index_exports, {
146
148
  hasOne: () => hasOne,
147
149
  hydrateRows: () => hydrateRows,
148
150
  inList: () => inList,
151
+ inSubquery: () => inSubquery,
149
152
  instr: () => instr,
150
153
  introspectSchema: () => introspectSchema,
151
154
  isCaseExpressionNode: () => isCaseExpressionNode,
@@ -186,6 +189,7 @@ __export(index_exports, {
186
189
  notBetween: () => notBetween,
187
190
  notExists: () => notExists,
188
191
  notInList: () => notInList,
192
+ notInSubquery: () => notInSubquery,
189
193
  notLike: () => notLike,
190
194
  now: () => now,
191
195
  ntile: () => ntile,
@@ -221,6 +225,7 @@ __export(index_exports, {
221
225
  substr: () => substr,
222
226
  sum: () => sum,
223
227
  synchronizeSchema: () => synchronizeSchema,
228
+ tableRef: () => tableRef,
224
229
  tan: () => tan,
225
230
  toColumnRef: () => toColumnRef,
226
231
  toTableRef: () => toTableRef,
@@ -260,6 +265,54 @@ var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
260
265
  collation: options.collation
261
266
  };
262
267
  };
268
+ var TABLE_REF_CACHE = /* @__PURE__ */ new WeakMap();
269
+ var withColumnProps = (table) => {
270
+ const cached = TABLE_REF_CACHE.get(table);
271
+ if (cached) return cached;
272
+ const proxy = new Proxy(table, {
273
+ get(target, prop, receiver) {
274
+ if (prop === "$") return target.columns;
275
+ if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
276
+ if (typeof prop === "string" && prop in target.columns) return target.columns[prop];
277
+ return void 0;
278
+ },
279
+ has(target, prop) {
280
+ return prop === "$" || Reflect.has(target, prop) || typeof prop === "string" && prop in target.columns;
281
+ },
282
+ ownKeys(target) {
283
+ const base = Reflect.ownKeys(target);
284
+ const cols = Object.keys(target.columns);
285
+ for (const k of cols) {
286
+ if (!base.includes(k)) base.push(k);
287
+ }
288
+ if (!base.includes("$")) base.push("$");
289
+ return base;
290
+ },
291
+ getOwnPropertyDescriptor(target, prop) {
292
+ if (prop === "$") {
293
+ return {
294
+ configurable: true,
295
+ enumerable: false,
296
+ get() {
297
+ return target.columns;
298
+ }
299
+ };
300
+ }
301
+ if (typeof prop === "string" && prop in target.columns && !Reflect.has(target, prop)) {
302
+ return {
303
+ configurable: true,
304
+ enumerable: true,
305
+ value: target.columns[prop],
306
+ writable: false
307
+ };
308
+ }
309
+ return Reflect.getOwnPropertyDescriptor(target, prop);
310
+ }
311
+ });
312
+ TABLE_REF_CACHE.set(table, proxy);
313
+ return proxy;
314
+ };
315
+ var tableRef = (table) => withColumnProps(table);
263
316
 
264
317
  // src/schema/column.ts
265
318
  var col = {
@@ -497,6 +550,12 @@ var toOperand = (val) => {
497
550
  }
498
551
  return toNode(val);
499
552
  };
553
+ var hasQueryAst = (value) => typeof value.getAST === "function";
554
+ var resolveSelectQueryNode = (query) => hasQueryAst(query) ? query.getAST() : query;
555
+ var toScalarSubqueryNode = (query) => ({
556
+ type: "ScalarSubquery",
557
+ query: resolveSelectQueryNode(query)
558
+ });
500
559
  var columnOperand = (col2) => toNode(col2);
501
560
  var outerRef = (col2) => ({
502
561
  ...columnOperand(col2),
@@ -547,14 +606,16 @@ var isNotNull = (left2) => ({
547
606
  left: toNode(left2),
548
607
  operator: "IS NOT NULL"
549
608
  });
550
- var createInExpression = (operator, left2, values) => ({
609
+ var createInExpression = (operator, left2, right2) => ({
551
610
  type: "InExpression",
552
611
  left: toNode(left2),
553
612
  operator,
554
- right: values.map((v) => toOperand(v))
613
+ right: right2
555
614
  });
556
- var inList = (left2, values) => createInExpression("IN", left2, values);
557
- var notInList = (left2, values) => createInExpression("NOT IN", left2, values);
615
+ var inList = (left2, values) => createInExpression("IN", left2, values.map((v) => toOperand(v)));
616
+ var notInList = (left2, values) => createInExpression("NOT IN", left2, values.map((v) => toOperand(v)));
617
+ var inSubquery = (left2, subquery) => createInExpression("IN", left2, toScalarSubqueryNode(subquery));
618
+ var notInSubquery = (left2, subquery) => createInExpression("NOT IN", left2, toScalarSubqueryNode(subquery));
558
619
  var createBetweenExpression = (operator, left2, lower2, upper2) => ({
559
620
  type: "BetweenExpression",
560
621
  left: toNode(left2),
@@ -1237,8 +1298,12 @@ var Dialect = class _Dialect {
1237
1298
  });
1238
1299
  this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
1239
1300
  const left2 = this.compileOperand(inExpr.left, ctx);
1240
- const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
1241
- return `${left2} ${inExpr.operator} (${values})`;
1301
+ if (Array.isArray(inExpr.right)) {
1302
+ const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
1303
+ return `${left2} ${inExpr.operator} (${values})`;
1304
+ }
1305
+ const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
1306
+ return `${left2} ${inExpr.operator} (${subquerySql})`;
1242
1307
  });
1243
1308
  this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
1244
1309
  const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
@@ -1496,17 +1561,9 @@ var NoReturningStrategy = class {
1496
1561
 
1497
1562
  // src/core/dialect/base/join-compiler.ts
1498
1563
  var JoinCompiler = class {
1499
- /**
1500
- * Compiles all JOIN clauses from a SELECT query AST.
1501
- * @param ast - The SELECT query AST containing join definitions.
1502
- * @param ctx - The compiler context for expression compilation.
1503
- * @param compileFrom - Function to compile table sources (tables or subqueries).
1504
- * @param compileExpression - Function to compile join condition expressions.
1505
- * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
1506
- */
1507
- static compileJoins(ast, ctx, compileFrom, compileExpression) {
1508
- if (!ast.joins || ast.joins.length === 0) return "";
1509
- const parts = ast.joins.map((j) => {
1564
+ static compileJoins(joins, ctx, compileFrom, compileExpression) {
1565
+ if (!joins || joins.length === 0) return "";
1566
+ const parts = joins.map((j) => {
1510
1567
  const table = compileFrom(j.table, ctx);
1511
1568
  const cond = compileExpression(j.condition, ctx);
1512
1569
  return `${j.kind} JOIN ${table} ON ${cond}`;
@@ -1589,25 +1646,41 @@ var SqlDialectBase = class extends Dialect {
1589
1646
  return `${ctes}${combined}${orderBy}${pagination}`;
1590
1647
  }
1591
1648
  compileInsertAst(ast, ctx) {
1649
+ if (!ast.columns.length) {
1650
+ throw new Error("INSERT queries must specify columns.");
1651
+ }
1592
1652
  const table = this.compileTableName(ast.into);
1593
1653
  const columnList = this.compileInsertColumnList(ast.columns);
1594
- const values = this.compileInsertValues(ast.values, ctx);
1654
+ const source = this.compileInsertSource(ast.source, ctx);
1595
1655
  const returning = this.compileReturning(ast.returning, ctx);
1596
- return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
1656
+ return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
1597
1657
  }
1598
1658
  compileReturning(returning, ctx) {
1599
1659
  return this.returningStrategy.compileReturning(returning, ctx);
1600
1660
  }
1661
+ compileInsertSource(source, ctx) {
1662
+ if (source.type === "InsertValues") {
1663
+ if (!source.rows.length) {
1664
+ throw new Error("INSERT ... VALUES requires at least one row.");
1665
+ }
1666
+ const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1667
+ return `VALUES ${values}`;
1668
+ }
1669
+ const normalized = this.normalizeSelectAst(source.query);
1670
+ return this.compileSelectAst(normalized, ctx).trim();
1671
+ }
1601
1672
  compileInsertColumnList(columns) {
1602
1673
  return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1603
1674
  }
1604
- compileInsertValues(values, ctx) {
1605
- return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
1606
- }
1607
1675
  compileSelectCore(ast, ctx) {
1608
1676
  const columns = this.compileSelectColumns(ast, ctx);
1609
1677
  const from = this.compileFrom(ast.from, ctx);
1610
- const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
1678
+ const joins = JoinCompiler.compileJoins(
1679
+ ast.joins,
1680
+ ctx,
1681
+ this.compileFrom.bind(this),
1682
+ this.compileExpression.bind(this)
1683
+ );
1611
1684
  const whereClause = this.compileWhere(ast.where, ctx);
1612
1685
  const groupBy = GroupByCompiler.compileGroupBy(ast, (term) => this.compileOrderingTerm(term, ctx));
1613
1686
  const having = this.compileHaving(ast, ctx);
@@ -1621,25 +1694,37 @@ var SqlDialectBase = class extends Dialect {
1621
1694
  return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
1622
1695
  }
1623
1696
  compileUpdateAst(ast, ctx) {
1624
- const table = this.compileTableName(ast.table);
1625
- const assignments = this.compileUpdateAssignments(ast.set, ctx);
1697
+ const target = this.compileTableReference(ast.table);
1698
+ const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
1699
+ const fromClause = this.compileUpdateFromClause(ast, ctx);
1626
1700
  const whereClause = this.compileWhere(ast.where, ctx);
1627
1701
  const returning = this.compileReturning(ast.returning, ctx);
1628
- return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
1702
+ return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
1629
1703
  }
1630
- compileUpdateAssignments(assignments, ctx) {
1704
+ compileUpdateAssignments(assignments, table, ctx) {
1631
1705
  return assignments.map((assignment) => {
1632
1706
  const col2 = assignment.column;
1633
- const target = this.quoteIdentifier(col2.name);
1707
+ const target = this.compileQualifiedColumn(col2, table);
1634
1708
  const value = this.compileOperand(assignment.value, ctx);
1635
1709
  return `${target} = ${value}`;
1636
1710
  }).join(", ");
1637
1711
  }
1712
+ compileQualifiedColumn(column, table) {
1713
+ const baseTableName = table.name;
1714
+ const alias = table.alias;
1715
+ const columnTable = column.table ?? alias ?? baseTableName;
1716
+ const tableQualifier = alias && column.table === baseTableName ? alias : columnTable;
1717
+ if (!tableQualifier) {
1718
+ return this.quoteIdentifier(column.name);
1719
+ }
1720
+ return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
1721
+ }
1638
1722
  compileDeleteAst(ast, ctx) {
1639
- const table = this.compileTableName(ast.from);
1723
+ const target = this.compileTableReference(ast.from);
1724
+ const usingClause = this.compileDeleteUsingClause(ast, ctx);
1640
1725
  const whereClause = this.compileWhere(ast.where, ctx);
1641
1726
  const returning = this.compileReturning(ast.returning, ctx);
1642
- return `DELETE FROM ${table}${whereClause}${returning}`;
1727
+ return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
1643
1728
  }
1644
1729
  formatReturningColumns(returning) {
1645
1730
  return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
@@ -1694,6 +1779,38 @@ var SqlDialectBase = class extends Dialect {
1694
1779
  }
1695
1780
  return this.quoteIdentifier(table.name);
1696
1781
  }
1782
+ compileTableReference(table) {
1783
+ const base = this.compileTableName(table);
1784
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1785
+ }
1786
+ compileUpdateFromClause(ast, ctx) {
1787
+ if (!ast.from && (!ast.joins || ast.joins.length === 0)) return "";
1788
+ if (!ast.from) {
1789
+ throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
1790
+ }
1791
+ const from = this.compileFrom(ast.from, ctx);
1792
+ const joins = JoinCompiler.compileJoins(
1793
+ ast.joins,
1794
+ ctx,
1795
+ this.compileFrom.bind(this),
1796
+ this.compileExpression.bind(this)
1797
+ );
1798
+ return ` FROM ${from}${joins}`;
1799
+ }
1800
+ compileDeleteUsingClause(ast, ctx) {
1801
+ if (!ast.using && (!ast.joins || ast.joins.length === 0)) return "";
1802
+ if (!ast.using) {
1803
+ throw new Error("DELETE with JOINs requires a USING clause.");
1804
+ }
1805
+ const usingTable = this.compileFrom(ast.using, ctx);
1806
+ const joins = JoinCompiler.compileJoins(
1807
+ ast.joins,
1808
+ ctx,
1809
+ this.compileFrom.bind(this),
1810
+ this.compileExpression.bind(this)
1811
+ );
1812
+ return ` USING ${usingTable}${joins}`;
1813
+ }
1697
1814
  compileHaving(ast, ctx) {
1698
1815
  if (!ast.having) return "";
1699
1816
  return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
@@ -2074,6 +2191,9 @@ var SqliteDialect = class extends SqlDialectBase {
2074
2191
  const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
2075
2192
  return `json_extract(${col2}, '${node.path}')`;
2076
2193
  }
2194
+ compileQualifiedColumn(column, _table) {
2195
+ return this.quoteIdentifier(column.name);
2196
+ }
2077
2197
  compileReturning(returning, ctx) {
2078
2198
  if (!returning || returning.length === 0) return "";
2079
2199
  const columns = this.formatReturningColumns(returning);
@@ -2179,7 +2299,7 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
2179
2299
  };
2180
2300
 
2181
2301
  // src/core/dialect/mssql/index.ts
2182
- var SqlServerDialect = class extends Dialect {
2302
+ var SqlServerDialect = class extends SqlDialectBase {
2183
2303
  /**
2184
2304
  * Creates a new SqlServerDialect instance
2185
2305
  */
@@ -2222,7 +2342,7 @@ var SqlServerDialect = class extends Dialect {
2222
2342
  const hasSetOps = !!(ast.setOps && ast.setOps.length);
2223
2343
  const ctes = this.compileCtes(ast, ctx);
2224
2344
  const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
2225
- const baseSelect = this.compileSelectCore(baseAst, ctx);
2345
+ const baseSelect = this.compileSelectCoreForMssql(baseAst, ctx);
2226
2346
  if (!hasSetOps) {
2227
2347
  return `${ctes}${baseSelect}`;
2228
2348
  }
@@ -2233,32 +2353,26 @@ var SqlServerDialect = class extends Dialect {
2233
2353
  const tail = pagination || orderBy;
2234
2354
  return `${ctes}${combined}${tail}`;
2235
2355
  }
2236
- compileInsertAst(ast, ctx) {
2237
- const table = this.quoteIdentifier(ast.into.name);
2238
- const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
2239
- const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
2240
- return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
2241
- }
2242
- compileUpdateAst(ast, ctx) {
2243
- const table = this.quoteIdentifier(ast.table.name);
2244
- const assignments = ast.set.map((assignment) => {
2245
- const col2 = assignment.column;
2246
- const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
2247
- const value = this.compileOperand(assignment.value, ctx);
2248
- return `${target} = ${value}`;
2249
- }).join(", ");
2250
- const whereClause = this.compileWhere(ast.where, ctx);
2251
- return `UPDATE ${table} SET ${assignments}${whereClause};`;
2252
- }
2253
2356
  compileDeleteAst(ast, ctx) {
2357
+ if (ast.using) {
2358
+ throw new Error("DELETE ... USING is not supported in the MSSQL dialect; use join() instead.");
2359
+ }
2254
2360
  if (ast.from.type !== "Table") {
2255
2361
  throw new Error("DELETE only supports base tables in the MSSQL dialect.");
2256
2362
  }
2257
- const table = this.quoteIdentifier(ast.from.name);
2363
+ const alias = ast.from.alias ?? ast.from.name;
2364
+ const target = this.compileTableReference(ast.from);
2365
+ const joins = JoinCompiler.compileJoins(
2366
+ ast.joins,
2367
+ ctx,
2368
+ this.compileFrom.bind(this),
2369
+ this.compileExpression.bind(this)
2370
+ );
2258
2371
  const whereClause = this.compileWhere(ast.where, ctx);
2259
- return `DELETE FROM ${table}${whereClause};`;
2372
+ const returning = this.compileReturning(ast.returning, ctx);
2373
+ return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}${returning}`;
2260
2374
  }
2261
- compileSelectCore(ast, ctx) {
2375
+ compileSelectCoreForMssql(ast, ctx) {
2262
2376
  const columns = ast.columns.map((c) => {
2263
2377
  let expr = "";
2264
2378
  if (c.type === "Function") {
@@ -2277,9 +2391,9 @@ var SqlServerDialect = class extends Dialect {
2277
2391
  return expr;
2278
2392
  }).join(", ");
2279
2393
  const distinct = ast.distinct ? "DISTINCT " : "";
2280
- const from = this.compileTableSource(ast.from, ctx);
2394
+ const from = this.compileTableSource(ast.from);
2281
2395
  const joins = ast.joins.map((j) => {
2282
- const table = this.compileTableSource(j.table, ctx);
2396
+ const table = this.compileTableSource(j.table);
2283
2397
  const cond = this.compileExpression(j.condition, ctx);
2284
2398
  return `${j.kind} JOIN ${table} ON ${cond}`;
2285
2399
  }).join(" ");
@@ -2313,27 +2427,6 @@ var SqlServerDialect = class extends Dialect {
2313
2427
  }
2314
2428
  return pagination;
2315
2429
  }
2316
- renderOrderByNulls(order) {
2317
- return order.nulls ? ` NULLS ${order.nulls}` : "";
2318
- }
2319
- renderOrderByCollation(order) {
2320
- return order.collation ? ` COLLATE ${order.collation}` : "";
2321
- }
2322
- compileTableSource(table, ctx) {
2323
- if (table.type === "FunctionTable") {
2324
- return FunctionTableFormatter.format(table, ctx, this);
2325
- }
2326
- if (table.type === "DerivedTable") {
2327
- return this.compileDerivedTable(table, ctx);
2328
- }
2329
- const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
2330
- return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
2331
- }
2332
- compileDerivedTable(table, ctx) {
2333
- const sub2 = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
2334
- const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
2335
- return `(${sub2}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
2336
- }
2337
2430
  compileCtes(ast, ctx) {
2338
2431
  if (!ast.ctes || ast.ctes.length === 0) return "";
2339
2432
  const defs = ast.ctes.map((cte) => {
@@ -2344,10 +2437,6 @@ var SqlServerDialect = class extends Dialect {
2344
2437
  }).join(", ");
2345
2438
  return `WITH ${defs} `;
2346
2439
  }
2347
- wrapSetOperand(sql) {
2348
- const trimmed = sql.trim().replace(/;$/, "");
2349
- return `(${trimmed})`;
2350
- }
2351
2440
  };
2352
2441
 
2353
2442
  // src/core/dialect/dialect-factory.ts
@@ -3174,7 +3263,7 @@ var QueryAstService = class {
3174
3263
  }
3175
3264
  normalizeOrderingTerm(term) {
3176
3265
  const from = this.state.ast.from;
3177
- const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
3266
+ const tableRef2 = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
3178
3267
  const termType = term?.type;
3179
3268
  if (termType === "Column") {
3180
3269
  return term;
@@ -3188,7 +3277,7 @@ var QueryAstService = class {
3188
3277
  if (termType === "BinaryExpression" || termType === "LogicalExpression" || termType === "NullExpression" || termType === "InExpression" || termType === "ExistsExpression" || termType === "BetweenExpression" || termType === "ArithmeticExpression") {
3189
3278
  return term;
3190
3279
  }
3191
- return buildColumnNode(tableRef, term);
3280
+ return buildColumnNode(tableRef2, term);
3192
3281
  }
3193
3282
  };
3194
3283
 
@@ -3594,8 +3683,8 @@ var ColumnSelector = class {
3594
3683
  */
3595
3684
  distinct(context, columns) {
3596
3685
  const from = context.state.ast.from;
3597
- const tableRef = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
3598
- const nodes = columns.map((col2) => buildColumnNode(tableRef, col2));
3686
+ const tableRef2 = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
3687
+ const nodes = columns.map((col2) => buildColumnNode(tableRef2, col2));
3599
3688
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3600
3689
  const nextState = astService.withDistinct(nodes);
3601
3690
  return { state: nextState, hydration: context.hydration };
@@ -4142,8 +4231,10 @@ var DefaultManyToManyCollection = class {
4142
4231
  attach(target) {
4143
4232
  const entity = this.ensureEntity(target);
4144
4233
  const id = this.extractId(entity);
4145
- if (id == null) return;
4146
- if (this.items.some((item) => this.extractId(item) === id)) {
4234
+ if (id != null && this.items.some((item) => this.extractId(item) === id)) {
4235
+ return;
4236
+ }
4237
+ if (id == null && this.items.includes(entity)) {
4147
4238
  return;
4148
4239
  }
4149
4240
  this.items.push(entity);
@@ -5466,15 +5557,36 @@ var InsertQueryState = class _InsertQueryState {
5466
5557
  type: "InsertQuery",
5467
5558
  into: createTableNode(table),
5468
5559
  columns: [],
5469
- values: []
5560
+ source: {
5561
+ type: "InsertValues",
5562
+ rows: []
5563
+ }
5470
5564
  };
5471
5565
  }
5472
5566
  clone(nextAst) {
5473
5567
  return new _InsertQueryState(this.table, nextAst);
5474
5568
  }
5569
+ ensureColumnsFromRow(rows) {
5570
+ if (this.ast.columns.length) return this.ast.columns;
5571
+ return buildColumnNodes(this.table, Object.keys(rows[0]));
5572
+ }
5573
+ appendValues(rows) {
5574
+ if (this.ast.source.type === "InsertValues") {
5575
+ return [...this.ast.source.rows, ...rows];
5576
+ }
5577
+ return rows;
5578
+ }
5579
+ getTableColumns() {
5580
+ const names = Object.keys(this.table.columns);
5581
+ if (!names.length) return [];
5582
+ return buildColumnNodes(this.table, names);
5583
+ }
5475
5584
  withValues(rows) {
5476
5585
  if (!rows.length) return this;
5477
- const definedColumns = this.ast.columns.length ? this.ast.columns : buildColumnNodes(this.table, Object.keys(rows[0]));
5586
+ if (this.ast.source.type === "InsertSelect") {
5587
+ throw new Error("Cannot mix INSERT ... VALUES with INSERT ... SELECT source.");
5588
+ }
5589
+ const definedColumns = this.ensureColumnsFromRow(rows);
5478
5590
  const newRows = rows.map(
5479
5591
  (row, rowIndex) => definedColumns.map((column) => {
5480
5592
  const rawValue = row[column.name];
@@ -5489,7 +5601,34 @@ var InsertQueryState = class _InsertQueryState {
5489
5601
  return this.clone({
5490
5602
  ...this.ast,
5491
5603
  columns: definedColumns,
5492
- values: [...this.ast.values, ...newRows]
5604
+ source: {
5605
+ type: "InsertValues",
5606
+ rows: this.appendValues(newRows)
5607
+ }
5608
+ });
5609
+ }
5610
+ withColumns(columns) {
5611
+ if (!columns.length) return this;
5612
+ return this.clone({
5613
+ ...this.ast,
5614
+ columns: [...columns]
5615
+ });
5616
+ }
5617
+ withSelect(query, columns) {
5618
+ const targetColumns = columns.length ? columns : this.ast.columns.length ? this.ast.columns : this.getTableColumns();
5619
+ if (!targetColumns.length) {
5620
+ throw new Error("INSERT ... SELECT requires specifying destination columns.");
5621
+ }
5622
+ if (this.ast.source.type === "InsertValues" && this.ast.source.rows.length) {
5623
+ throw new Error("Cannot mix INSERT ... SELECT with INSERT ... VALUES source.");
5624
+ }
5625
+ return this.clone({
5626
+ ...this.ast,
5627
+ columns: [...targetColumns],
5628
+ source: {
5629
+ type: "InsertSelect",
5630
+ query
5631
+ }
5493
5632
  });
5494
5633
  }
5495
5634
  withReturning(columns) {
@@ -5514,11 +5653,27 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
5514
5653
  if (!rows.length) return this;
5515
5654
  return this.clone(this.state.withValues(rows));
5516
5655
  }
5656
+ columns(...columns) {
5657
+ if (!columns.length) return this;
5658
+ return this.clone(this.state.withColumns(this.resolveColumnNodes(columns)));
5659
+ }
5660
+ fromSelect(query, columns = []) {
5661
+ const ast = this.resolveSelectQuery(query);
5662
+ const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
5663
+ return this.clone(this.state.withSelect(ast, nodes));
5664
+ }
5517
5665
  returning(...columns) {
5518
5666
  if (!columns.length) return this;
5519
5667
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5520
5668
  return this.clone(this.state.withReturning(nodes));
5521
5669
  }
5670
+ // Helpers for column/AST resolution
5671
+ resolveColumnNodes(columns) {
5672
+ return columns.map((column) => buildColumnNode(this.table, column));
5673
+ }
5674
+ resolveSelectQuery(query) {
5675
+ return typeof query.getAST === "function" ? query.getAST() : query;
5676
+ }
5522
5677
  compile(arg) {
5523
5678
  if (typeof arg.compileInsert === "function") {
5524
5679
  return arg.compileInsert(this.state.ast);
@@ -5552,7 +5707,8 @@ var UpdateQueryState = class _UpdateQueryState {
5552
5707
  this.ast = ast ?? {
5553
5708
  type: "UpdateQuery",
5554
5709
  table: createTableNode(table),
5555
- set: []
5710
+ set: [],
5711
+ joins: []
5556
5712
  };
5557
5713
  }
5558
5714
  clone(nextAst) {
@@ -5591,6 +5747,27 @@ var UpdateQueryState = class _UpdateQueryState {
5591
5747
  returning: [...columns]
5592
5748
  });
5593
5749
  }
5750
+ withFrom(from) {
5751
+ return this.clone({
5752
+ ...this.ast,
5753
+ from
5754
+ });
5755
+ }
5756
+ withJoin(join) {
5757
+ return this.clone({
5758
+ ...this.ast,
5759
+ joins: [...this.ast.joins ?? [], join]
5760
+ });
5761
+ }
5762
+ withTableAlias(alias) {
5763
+ return this.clone({
5764
+ ...this.ast,
5765
+ table: {
5766
+ ...this.ast.table,
5767
+ alias
5768
+ }
5769
+ });
5770
+ }
5594
5771
  };
5595
5772
 
5596
5773
  // src/query-builder/update.ts
@@ -5602,6 +5779,18 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5602
5779
  clone(state) {
5603
5780
  return new _UpdateQueryBuilder(this.table, state);
5604
5781
  }
5782
+ as(alias) {
5783
+ return this.clone(this.state.withTableAlias(alias));
5784
+ }
5785
+ from(source) {
5786
+ const tableSource = this.resolveTableSource(source);
5787
+ return this.clone(this.state.withFrom(tableSource));
5788
+ }
5789
+ join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
5790
+ const joinTarget = this.resolveJoinTarget(table);
5791
+ const joinNode = createJoinNode(kind, joinTarget, condition, relationName);
5792
+ return this.clone(this.state.withJoin(joinNode));
5793
+ }
5605
5794
  set(values) {
5606
5795
  return this.clone(this.state.withSet(values));
5607
5796
  }
@@ -5613,6 +5802,16 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5613
5802
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5614
5803
  return this.clone(this.state.withReturning(nodes));
5615
5804
  }
5805
+ resolveTableSource(source) {
5806
+ if (isTableSourceNode(source)) {
5807
+ return source;
5808
+ }
5809
+ return { type: "Table", name: source.name, schema: source.schema };
5810
+ }
5811
+ resolveJoinTarget(table) {
5812
+ if (typeof table === "string") return table;
5813
+ return this.resolveTableSource(table);
5814
+ }
5616
5815
  compile(arg) {
5617
5816
  if (typeof arg.compileUpdate === "function") {
5618
5817
  return arg.compileUpdate(this.state.ast);
@@ -5627,6 +5826,7 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
5627
5826
  return this.state.ast;
5628
5827
  }
5629
5828
  };
5829
+ var isTableSourceNode = (source) => typeof source.type === "string";
5630
5830
 
5631
5831
  // src/query-builder/delete-query-state.ts
5632
5832
  var DeleteQueryState = class _DeleteQueryState {
@@ -5634,7 +5834,8 @@ var DeleteQueryState = class _DeleteQueryState {
5634
5834
  this.table = table;
5635
5835
  this.ast = ast ?? {
5636
5836
  type: "DeleteQuery",
5637
- from: createTableNode(table)
5837
+ from: createTableNode(table),
5838
+ joins: []
5638
5839
  };
5639
5840
  }
5640
5841
  clone(nextAst) {
@@ -5652,6 +5853,27 @@ var DeleteQueryState = class _DeleteQueryState {
5652
5853
  returning: [...columns]
5653
5854
  });
5654
5855
  }
5856
+ withUsing(source) {
5857
+ return this.clone({
5858
+ ...this.ast,
5859
+ using: source
5860
+ });
5861
+ }
5862
+ withJoin(join) {
5863
+ return this.clone({
5864
+ ...this.ast,
5865
+ joins: [...this.ast.joins ?? [], join]
5866
+ });
5867
+ }
5868
+ withTableAlias(alias) {
5869
+ return this.clone({
5870
+ ...this.ast,
5871
+ from: {
5872
+ ...this.ast.from,
5873
+ alias
5874
+ }
5875
+ });
5876
+ }
5655
5877
  };
5656
5878
 
5657
5879
  // src/query-builder/delete.ts
@@ -5666,11 +5888,32 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
5666
5888
  where(expr) {
5667
5889
  return this.clone(this.state.withWhere(expr));
5668
5890
  }
5891
+ as(alias) {
5892
+ return this.clone(this.state.withTableAlias(alias));
5893
+ }
5894
+ using(source) {
5895
+ return this.clone(this.state.withUsing(this.resolveTableSource(source)));
5896
+ }
5897
+ join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
5898
+ const target = this.resolveJoinTarget(table);
5899
+ const joinNode = createJoinNode(kind, target, condition, relationName);
5900
+ return this.clone(this.state.withJoin(joinNode));
5901
+ }
5669
5902
  returning(...columns) {
5670
5903
  if (!columns.length) return this;
5671
5904
  const nodes = columns.map((column) => buildColumnNode(this.table, column));
5672
5905
  return this.clone(this.state.withReturning(nodes));
5673
5906
  }
5907
+ resolveTableSource(source) {
5908
+ if (isTableSourceNode2(source)) {
5909
+ return source;
5910
+ }
5911
+ return { type: "Table", name: source.name, schema: source.schema };
5912
+ }
5913
+ resolveJoinTarget(table) {
5914
+ if (typeof table === "string") return table;
5915
+ return this.resolveTableSource(table);
5916
+ }
5674
5917
  compile(arg) {
5675
5918
  if (typeof arg.compileDelete === "function") {
5676
5919
  return arg.compileDelete(this.state.ast);
@@ -5685,6 +5928,7 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
5685
5928
  return this.state.ast;
5686
5929
  }
5687
5930
  };
5931
+ var isTableSourceNode2 = (source) => typeof source.type === "string";
5688
5932
 
5689
5933
  // src/core/ddl/sql-writing.ts
5690
5934
  var resolvePrimaryKey = (table) => {
@@ -7033,9 +7277,13 @@ var TypeScriptGenerator = class {
7033
7277
  */
7034
7278
  printInExpression(inExpr) {
7035
7279
  const left2 = this.printOperand(inExpr.left);
7036
- const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
7037
7280
  const fn4 = this.mapOp(inExpr.operator);
7038
- return `${fn4}(${left2}, [${values}])`;
7281
+ if (Array.isArray(inExpr.right)) {
7282
+ const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
7283
+ return `${fn4}(${left2}, [${values}])`;
7284
+ }
7285
+ const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
7286
+ return `${fn4}(${left2}, (${subquery}))`;
7039
7287
  }
7040
7288
  /**
7041
7289
  * Prints a null expression to TypeScript code
@@ -7220,6 +7468,13 @@ var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
7220
7468
 
7221
7469
  // src/orm/unit-of-work.ts
7222
7470
  var UnitOfWork = class {
7471
+ /**
7472
+ * Creates a new UnitOfWork instance.
7473
+ * @param dialect - The database dialect
7474
+ * @param executor - The database executor
7475
+ * @param identityMap - The identity map
7476
+ * @param hookContext - Function to get the hook context
7477
+ */
7223
7478
  constructor(dialect, executor, identityMap, hookContext) {
7224
7479
  this.dialect = dialect;
7225
7480
  this.executor = executor;
@@ -7227,21 +7482,50 @@ var UnitOfWork = class {
7227
7482
  this.hookContext = hookContext;
7228
7483
  this.trackedEntities = /* @__PURE__ */ new Map();
7229
7484
  }
7485
+ /**
7486
+ * Gets the identity buckets map.
7487
+ */
7230
7488
  get identityBuckets() {
7231
7489
  return this.identityMap.bucketsMap;
7232
7490
  }
7491
+ /**
7492
+ * Gets all tracked entities.
7493
+ * @returns Array of tracked entities
7494
+ */
7233
7495
  getTracked() {
7234
7496
  return Array.from(this.trackedEntities.values());
7235
7497
  }
7498
+ /**
7499
+ * Gets an entity by table and primary key.
7500
+ * @param table - The table definition
7501
+ * @param pk - The primary key value
7502
+ * @returns The entity or undefined if not found
7503
+ */
7236
7504
  getEntity(table, pk) {
7237
7505
  return this.identityMap.getEntity(table, pk);
7238
7506
  }
7507
+ /**
7508
+ * Gets all tracked entities for a specific table.
7509
+ * @param table - The table definition
7510
+ * @returns Array of tracked entities
7511
+ */
7239
7512
  getEntitiesForTable(table) {
7240
7513
  return this.identityMap.getEntitiesForTable(table);
7241
7514
  }
7515
+ /**
7516
+ * Finds a tracked entity.
7517
+ * @param entity - The entity to find
7518
+ * @returns The tracked entity or undefined if not found
7519
+ */
7242
7520
  findTracked(entity) {
7243
7521
  return this.trackedEntities.get(entity);
7244
7522
  }
7523
+ /**
7524
+ * Sets an entity in the identity map.
7525
+ * @param table - The table definition
7526
+ * @param pk - The primary key value
7527
+ * @param entity - The entity instance
7528
+ */
7245
7529
  setEntity(table, pk, entity) {
7246
7530
  if (pk === null || pk === void 0) return;
7247
7531
  let tracked = this.trackedEntities.get(entity);
@@ -7259,6 +7543,12 @@ var UnitOfWork = class {
7259
7543
  }
7260
7544
  this.registerIdentity(tracked);
7261
7545
  }
7546
+ /**
7547
+ * Tracks a new entity.
7548
+ * @param table - The table definition
7549
+ * @param entity - The entity instance
7550
+ * @param pk - Optional primary key value
7551
+ */
7262
7552
  trackNew(table, entity, pk) {
7263
7553
  const tracked = {
7264
7554
  table,
@@ -7272,6 +7562,12 @@ var UnitOfWork = class {
7272
7562
  this.registerIdentity(tracked);
7273
7563
  }
7274
7564
  }
7565
+ /**
7566
+ * Tracks a managed entity.
7567
+ * @param table - The table definition
7568
+ * @param pk - The primary key value
7569
+ * @param entity - The entity instance
7570
+ */
7275
7571
  trackManaged(table, pk, entity) {
7276
7572
  const tracked = {
7277
7573
  table,
@@ -7283,17 +7579,28 @@ var UnitOfWork = class {
7283
7579
  this.trackedEntities.set(entity, tracked);
7284
7580
  this.registerIdentity(tracked);
7285
7581
  }
7582
+ /**
7583
+ * Marks an entity as dirty (modified).
7584
+ * @param entity - The entity to mark as dirty
7585
+ */
7286
7586
  markDirty(entity) {
7287
7587
  const tracked = this.trackedEntities.get(entity);
7288
7588
  if (!tracked) return;
7289
7589
  if (tracked.status === "new" /* New */ || tracked.status === "removed" /* Removed */) return;
7290
7590
  tracked.status = "dirty" /* Dirty */;
7291
7591
  }
7592
+ /**
7593
+ * Marks an entity as removed.
7594
+ * @param entity - The entity to mark as removed
7595
+ */
7292
7596
  markRemoved(entity) {
7293
7597
  const tracked = this.trackedEntities.get(entity);
7294
7598
  if (!tracked) return;
7295
7599
  tracked.status = "removed" /* Removed */;
7296
7600
  }
7601
+ /**
7602
+ * Flushes pending changes to the database.
7603
+ */
7297
7604
  async flush() {
7298
7605
  const toFlush = Array.from(this.trackedEntities.values());
7299
7606
  for (const tracked of toFlush) {
@@ -7312,10 +7619,17 @@ var UnitOfWork = class {
7312
7619
  }
7313
7620
  }
7314
7621
  }
7622
+ /**
7623
+ * Resets the unit of work by clearing all tracked entities and identity map.
7624
+ */
7315
7625
  reset() {
7316
7626
  this.trackedEntities.clear();
7317
7627
  this.identityMap.clear();
7318
7628
  }
7629
+ /**
7630
+ * Flushes an insert operation for a new entity.
7631
+ * @param tracked - The tracked entity to insert
7632
+ */
7319
7633
  async flushInsert(tracked) {
7320
7634
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
7321
7635
  const payload = this.extractColumns(tracked.table, tracked.entity);
@@ -7332,6 +7646,10 @@ var UnitOfWork = class {
7332
7646
  this.registerIdentity(tracked);
7333
7647
  await this.runHook(tracked.table.hooks?.afterInsert, tracked);
7334
7648
  }
7649
+ /**
7650
+ * Flushes an update operation for a modified entity.
7651
+ * @param tracked - The tracked entity to update
7652
+ */
7335
7653
  async flushUpdate(tracked) {
7336
7654
  if (tracked.pk == null) return;
7337
7655
  const changes = this.computeChanges(tracked);
@@ -7354,6 +7672,10 @@ var UnitOfWork = class {
7354
7672
  this.registerIdentity(tracked);
7355
7673
  await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
7356
7674
  }
7675
+ /**
7676
+ * Flushes a delete operation for a removed entity.
7677
+ * @param tracked - The tracked entity to delete
7678
+ */
7357
7679
  async flushDelete(tracked) {
7358
7680
  if (tracked.pk == null) return;
7359
7681
  await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
@@ -7367,10 +7689,20 @@ var UnitOfWork = class {
7367
7689
  this.identityMap.remove(tracked);
7368
7690
  await this.runHook(tracked.table.hooks?.afterDelete, tracked);
7369
7691
  }
7692
+ /**
7693
+ * Runs a table hook if defined.
7694
+ * @param hook - The hook function
7695
+ * @param tracked - The tracked entity
7696
+ */
7370
7697
  async runHook(hook, tracked) {
7371
7698
  if (!hook) return;
7372
7699
  await hook(this.hookContext(), tracked.entity);
7373
7700
  }
7701
+ /**
7702
+ * Computes changes between current entity state and original snapshot.
7703
+ * @param tracked - The tracked entity
7704
+ * @returns Object with changed column values
7705
+ */
7374
7706
  computeChanges(tracked) {
7375
7707
  const snapshot = tracked.original ?? {};
7376
7708
  const changes = {};
@@ -7382,6 +7714,12 @@ var UnitOfWork = class {
7382
7714
  }
7383
7715
  return changes;
7384
7716
  }
7717
+ /**
7718
+ * Extracts column values from an entity.
7719
+ * @param table - The table definition
7720
+ * @param entity - The entity instance
7721
+ * @returns Object with column values
7722
+ */
7385
7723
  extractColumns(table, entity) {
7386
7724
  const payload = {};
7387
7725
  for (const column of Object.keys(table.columns)) {
@@ -7390,9 +7728,19 @@ var UnitOfWork = class {
7390
7728
  }
7391
7729
  return payload;
7392
7730
  }
7731
+ /**
7732
+ * Executes a compiled query.
7733
+ * @param compiled - The compiled query
7734
+ * @returns Query results
7735
+ */
7393
7736
  async executeCompiled(compiled) {
7394
7737
  return this.executor.executeSql(compiled.sql, compiled.params);
7395
7738
  }
7739
+ /**
7740
+ * Gets columns for RETURNING clause.
7741
+ * @param table - The table definition
7742
+ * @returns Array of column nodes
7743
+ */
7396
7744
  getReturningColumns(table) {
7397
7745
  return Object.values(table.columns).map((column) => ({
7398
7746
  type: "Column",
@@ -7401,6 +7749,11 @@ var UnitOfWork = class {
7401
7749
  alias: column.name
7402
7750
  }));
7403
7751
  }
7752
+ /**
7753
+ * Applies RETURNING clause results to the tracked entity.
7754
+ * @param tracked - The tracked entity
7755
+ * @param results - Query results
7756
+ */
7404
7757
  applyReturningResults(tracked, results) {
7405
7758
  if (!this.dialect.supportsReturning()) return;
7406
7759
  const first = results[0];
@@ -7412,15 +7765,30 @@ var UnitOfWork = class {
7412
7765
  tracked.entity[columnName] = row[i];
7413
7766
  }
7414
7767
  }
7768
+ /**
7769
+ * Normalizes a column name by removing quotes and table prefixes.
7770
+ * @param column - The column name to normalize
7771
+ * @returns Normalized column name
7772
+ */
7415
7773
  normalizeColumnName(column) {
7416
7774
  const parts = column.split(".");
7417
7775
  const candidate = parts[parts.length - 1];
7418
7776
  return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
7419
7777
  }
7778
+ /**
7779
+ * Registers an entity in the identity map.
7780
+ * @param tracked - The tracked entity to register
7781
+ */
7420
7782
  registerIdentity(tracked) {
7421
7783
  if (tracked.pk == null) return;
7422
7784
  this.identityMap.register(tracked);
7423
7785
  }
7786
+ /**
7787
+ * Creates a snapshot of an entity's current state.
7788
+ * @param table - The table definition
7789
+ * @param entity - The entity instance
7790
+ * @returns Object with entity state
7791
+ */
7424
7792
  createSnapshot(table, entity) {
7425
7793
  const snapshot = {};
7426
7794
  for (const column of Object.keys(table.columns)) {
@@ -7428,6 +7796,11 @@ var UnitOfWork = class {
7428
7796
  }
7429
7797
  return snapshot;
7430
7798
  }
7799
+ /**
7800
+ * Gets the primary key value from a tracked entity.
7801
+ * @param tracked - The tracked entity
7802
+ * @returns Primary key value or null
7803
+ */
7431
7804
  getPrimaryKeyValue(tracked) {
7432
7805
  const key = findPrimaryKey(tracked.table);
7433
7806
  const val = tracked.entity[key];
@@ -7438,6 +7811,10 @@ var UnitOfWork = class {
7438
7811
 
7439
7812
  // src/orm/domain-event-bus.ts
7440
7813
  var DomainEventBus = class {
7814
+ /**
7815
+ * Creates a new DomainEventBus instance.
7816
+ * @param initialHandlers - Optional initial event handlers
7817
+ */
7441
7818
  constructor(initialHandlers) {
7442
7819
  this.handlers = /* @__PURE__ */ new Map();
7443
7820
  if (initialHandlers) {
@@ -7448,15 +7825,32 @@ var DomainEventBus = class {
7448
7825
  }
7449
7826
  }
7450
7827
  }
7828
+ /**
7829
+ * Registers an event handler for a specific event type.
7830
+ * @template TType - The event type
7831
+ * @param type - The event type
7832
+ * @param handler - The event handler
7833
+ */
7451
7834
  on(type, handler) {
7452
7835
  const key = type;
7453
7836
  const existing = this.handlers.get(key) ?? [];
7454
7837
  existing.push(handler);
7455
7838
  this.handlers.set(key, existing);
7456
7839
  }
7840
+ /**
7841
+ * Registers an event handler for a specific event type (alias for on).
7842
+ * @template TType - The event type
7843
+ * @param type - The event type
7844
+ * @param handler - The event handler
7845
+ */
7457
7846
  register(type, handler) {
7458
7847
  this.on(type, handler);
7459
7848
  }
7849
+ /**
7850
+ * Dispatches domain events for tracked entities.
7851
+ * @param trackedEntities - Iterable of tracked entities
7852
+ * @param ctx - The context to pass to handlers
7853
+ */
7460
7854
  async dispatch(trackedEntities, ctx) {
7461
7855
  for (const tracked of trackedEntities) {
7462
7856
  const entity = tracked.entity;
@@ -7481,18 +7875,34 @@ var addDomainEvent = (entity, event) => {
7481
7875
 
7482
7876
  // src/orm/relation-change-processor.ts
7483
7877
  var RelationChangeProcessor = class {
7878
+ /**
7879
+ * Creates a new RelationChangeProcessor instance.
7880
+ * @param unitOfWork - The unit of work instance
7881
+ * @param dialect - The database dialect
7882
+ * @param executor - The database executor
7883
+ */
7484
7884
  constructor(unitOfWork, dialect, executor) {
7485
7885
  this.unitOfWork = unitOfWork;
7486
7886
  this.dialect = dialect;
7487
7887
  this.executor = executor;
7488
7888
  this.relationChanges = [];
7489
7889
  }
7890
+ /**
7891
+ * Registers a relation change for processing.
7892
+ * @param entry - The relation change entry
7893
+ */
7490
7894
  registerChange(entry) {
7491
7895
  this.relationChanges.push(entry);
7492
7896
  }
7897
+ /**
7898
+ * Resets the relation change processor by clearing all pending changes.
7899
+ */
7493
7900
  reset() {
7494
7901
  this.relationChanges.length = 0;
7495
7902
  }
7903
+ /**
7904
+ * Processes all pending relation changes.
7905
+ */
7496
7906
  async process() {
7497
7907
  if (!this.relationChanges.length) return;
7498
7908
  const entries = [...this.relationChanges];
@@ -7514,6 +7924,10 @@ var RelationChangeProcessor = class {
7514
7924
  }
7515
7925
  }
7516
7926
  }
7927
+ /**
7928
+ * Handles changes for has-many relations.
7929
+ * @param entry - The relation change entry
7930
+ */
7517
7931
  async handleHasManyChange(entry) {
7518
7932
  const relation = entry.relation;
7519
7933
  const target = entry.change.entity;
@@ -7532,6 +7946,10 @@ var RelationChangeProcessor = class {
7532
7946
  this.detachHasManyChild(tracked.entity, relation);
7533
7947
  }
7534
7948
  }
7949
+ /**
7950
+ * Handles changes for has-one relations.
7951
+ * @param entry - The relation change entry
7952
+ */
7535
7953
  async handleHasOneChange(entry) {
7536
7954
  const relation = entry.relation;
7537
7955
  const target = entry.change.entity;
@@ -7550,8 +7968,16 @@ var RelationChangeProcessor = class {
7550
7968
  this.detachHasOneChild(tracked.entity, relation);
7551
7969
  }
7552
7970
  }
7971
+ /**
7972
+ * Handles changes for belongs-to relations.
7973
+ * @param _entry - The relation change entry (reserved for future use)
7974
+ */
7553
7975
  async handleBelongsToChange(_entry) {
7554
7976
  }
7977
+ /**
7978
+ * Handles changes for belongs-to-many relations.
7979
+ * @param entry - The relation change entry
7980
+ */
7555
7981
  async handleBelongsToManyChange(entry) {
7556
7982
  const relation = entry.relation;
7557
7983
  const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
@@ -7570,11 +7996,22 @@ var RelationChangeProcessor = class {
7570
7996
  }
7571
7997
  }
7572
7998
  }
7999
+ /**
8000
+ * Assigns a foreign key for has-many relations.
8001
+ * @param child - The child entity
8002
+ * @param relation - The has-many relation
8003
+ * @param rootValue - The root entity's primary key value
8004
+ */
7573
8005
  assignHasManyForeignKey(child, relation, rootValue) {
7574
8006
  const current = child[relation.foreignKey];
7575
8007
  if (current === rootValue) return;
7576
8008
  child[relation.foreignKey] = rootValue;
7577
8009
  }
8010
+ /**
8011
+ * Detaches a child entity from has-many relations.
8012
+ * @param child - The child entity
8013
+ * @param relation - The has-many relation
8014
+ */
7578
8015
  detachHasManyChild(child, relation) {
7579
8016
  if (relation.cascade === "all" || relation.cascade === "remove") {
7580
8017
  this.unitOfWork.markRemoved(child);
@@ -7583,11 +8020,22 @@ var RelationChangeProcessor = class {
7583
8020
  child[relation.foreignKey] = null;
7584
8021
  this.unitOfWork.markDirty(child);
7585
8022
  }
8023
+ /**
8024
+ * Assigns a foreign key for has-one relations.
8025
+ * @param child - The child entity
8026
+ * @param relation - The has-one relation
8027
+ * @param rootValue - The root entity's primary key value
8028
+ */
7586
8029
  assignHasOneForeignKey(child, relation, rootValue) {
7587
8030
  const current = child[relation.foreignKey];
7588
8031
  if (current === rootValue) return;
7589
8032
  child[relation.foreignKey] = rootValue;
7590
8033
  }
8034
+ /**
8035
+ * Detaches a child entity from has-one relations.
8036
+ * @param child - The child entity
8037
+ * @param relation - The has-one relation
8038
+ */
7591
8039
  detachHasOneChild(child, relation) {
7592
8040
  if (relation.cascade === "all" || relation.cascade === "remove") {
7593
8041
  this.unitOfWork.markRemoved(child);
@@ -7596,6 +8044,12 @@ var RelationChangeProcessor = class {
7596
8044
  child[relation.foreignKey] = null;
7597
8045
  this.unitOfWork.markDirty(child);
7598
8046
  }
8047
+ /**
8048
+ * Inserts a pivot row for belongs-to-many relations.
8049
+ * @param relation - The belongs-to-many relation
8050
+ * @param rootId - The root entity's primary key value
8051
+ * @param targetId - The target entity's primary key value
8052
+ */
7599
8053
  async insertPivotRow(relation, rootId, targetId) {
7600
8054
  const payload = {
7601
8055
  [relation.pivotForeignKeyToRoot]: rootId,
@@ -7605,6 +8059,12 @@ var RelationChangeProcessor = class {
7605
8059
  const compiled = builder.compile(this.dialect);
7606
8060
  await this.executor.executeSql(compiled.sql, compiled.params);
7607
8061
  }
8062
+ /**
8063
+ * Deletes a pivot row for belongs-to-many relations.
8064
+ * @param relation - The belongs-to-many relation
8065
+ * @param rootId - The root entity's primary key value
8066
+ * @param targetId - The target entity's primary key value
8067
+ */
7608
8068
  async deletePivotRow(relation, rootId, targetId) {
7609
8069
  const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
7610
8070
  const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
@@ -7615,6 +8075,12 @@ var RelationChangeProcessor = class {
7615
8075
  const compiled = builder.compile(this.dialect);
7616
8076
  await this.executor.executeSql(compiled.sql, compiled.params);
7617
8077
  }
8078
+ /**
8079
+ * Resolves the primary key value from an entity.
8080
+ * @param entity - The entity
8081
+ * @param table - The table definition
8082
+ * @returns The primary key value or null
8083
+ */
7618
8084
  resolvePrimaryKeyValue(entity, table) {
7619
8085
  if (!entity) return null;
7620
8086
  const key = findPrimaryKey(table);
@@ -7630,42 +8096,231 @@ var createQueryLoggingExecutor = (executor, logger) => {
7630
8096
  return executor;
7631
8097
  }
7632
8098
  const wrapped = {
8099
+ capabilities: executor.capabilities,
7633
8100
  async executeSql(sql, params) {
7634
8101
  logger({ sql, params });
7635
8102
  return executor.executeSql(sql, params);
7636
- }
8103
+ },
8104
+ beginTransaction: () => executor.beginTransaction(),
8105
+ commitTransaction: () => executor.commitTransaction(),
8106
+ rollbackTransaction: () => executor.rollbackTransaction(),
8107
+ dispose: () => executor.dispose()
7637
8108
  };
7638
- if (executor.beginTransaction) {
7639
- wrapped.beginTransaction = executor.beginTransaction.bind(executor);
7640
- }
7641
- if (executor.commitTransaction) {
7642
- wrapped.commitTransaction = executor.commitTransaction.bind(executor);
7643
- }
7644
- if (executor.rollbackTransaction) {
7645
- wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
7646
- }
7647
8109
  return wrapped;
7648
8110
  };
7649
8111
 
7650
8112
  // src/orm/transaction-runner.ts
7651
8113
  var runInTransaction = async (executor, action) => {
7652
- if (!executor.beginTransaction) {
8114
+ if (!executor.capabilities.transactions) {
7653
8115
  await action();
7654
8116
  return;
7655
8117
  }
7656
8118
  await executor.beginTransaction();
7657
8119
  try {
7658
8120
  await action();
7659
- await executor.commitTransaction?.();
8121
+ await executor.commitTransaction();
7660
8122
  } catch (error) {
7661
- await executor.rollbackTransaction?.();
8123
+ await executor.rollbackTransaction();
7662
8124
  throw error;
7663
8125
  }
7664
8126
  };
7665
8127
 
8128
+ // src/orm/save-graph.ts
8129
+ var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
8130
+ var pickColumns = (table, payload) => {
8131
+ const columns = {};
8132
+ for (const key of Object.keys(table.columns)) {
8133
+ if (payload[key] !== void 0) {
8134
+ columns[key] = payload[key];
8135
+ }
8136
+ }
8137
+ return columns;
8138
+ };
8139
+ var ensureEntity = (session, table, payload) => {
8140
+ const pk = findPrimaryKey(table);
8141
+ const row = pickColumns(table, payload);
8142
+ const pkValue = payload[pk];
8143
+ if (pkValue !== void 0 && pkValue !== null) {
8144
+ const tracked = session.getEntity(table, pkValue);
8145
+ if (tracked) {
8146
+ return tracked;
8147
+ }
8148
+ if (row[pk] === void 0) {
8149
+ row[pk] = pkValue;
8150
+ }
8151
+ }
8152
+ return createEntityFromRow(session, table, row);
8153
+ };
8154
+ var assignColumns = (table, entity, payload) => {
8155
+ for (const key of Object.keys(table.columns)) {
8156
+ if (payload[key] !== void 0) {
8157
+ entity[key] = payload[key];
8158
+ }
8159
+ }
8160
+ };
8161
+ var isEntityInCollection = (items, pkName, entity) => {
8162
+ if (items.includes(entity)) return true;
8163
+ const entityPk = entity[pkName];
8164
+ if (entityPk === void 0 || entityPk === null) return false;
8165
+ return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
8166
+ };
8167
+ var findInCollectionByPk = (items, pkName, pkValue) => {
8168
+ if (pkValue === void 0 || pkValue === null) return void 0;
8169
+ return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
8170
+ };
8171
+ var handleHasMany = async (session, root, relationName, relation, payload, options) => {
8172
+ if (!Array.isArray(payload)) return;
8173
+ const collection = root[relationName];
8174
+ await collection.load();
8175
+ const targetTable = relation.target;
8176
+ const targetPk = findPrimaryKey(targetTable);
8177
+ const existing = collection.getItems();
8178
+ const seen = /* @__PURE__ */ new Set();
8179
+ for (const item of payload) {
8180
+ if (item === null || item === void 0) continue;
8181
+ const asObj = typeof item === "object" ? item : { [targetPk]: item };
8182
+ const pkValue = asObj[targetPk];
8183
+ const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
8184
+ const entity = current ?? ensureEntity(session, targetTable, asObj);
8185
+ assignColumns(targetTable, entity, asObj);
8186
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
8187
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
8188
+ collection.attach(entity);
8189
+ }
8190
+ if (pkValue !== void 0 && pkValue !== null) {
8191
+ seen.add(toKey8(pkValue));
8192
+ }
8193
+ }
8194
+ if (options.pruneMissing) {
8195
+ for (const item of [...collection.getItems()]) {
8196
+ const pkValue = item[targetPk];
8197
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
8198
+ collection.remove(item);
8199
+ }
8200
+ }
8201
+ }
8202
+ };
8203
+ var handleHasOne = async (session, root, relationName, relation, payload, options) => {
8204
+ const ref = root[relationName];
8205
+ if (payload === void 0) return;
8206
+ if (payload === null) {
8207
+ ref.set(null);
8208
+ return;
8209
+ }
8210
+ const pk = findPrimaryKey(relation.target);
8211
+ if (typeof payload === "number" || typeof payload === "string") {
8212
+ const entity = ref.set({ [pk]: payload });
8213
+ if (entity) {
8214
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
8215
+ }
8216
+ return;
8217
+ }
8218
+ const attached = ref.set(payload);
8219
+ if (attached) {
8220
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
8221
+ }
8222
+ };
8223
+ var handleBelongsTo = async (session, root, relationName, relation, payload, options) => {
8224
+ const ref = root[relationName];
8225
+ if (payload === void 0) return;
8226
+ if (payload === null) {
8227
+ ref.set(null);
8228
+ return;
8229
+ }
8230
+ const pk = relation.localKey || findPrimaryKey(relation.target);
8231
+ if (typeof payload === "number" || typeof payload === "string") {
8232
+ const entity = ref.set({ [pk]: payload });
8233
+ if (entity) {
8234
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
8235
+ }
8236
+ return;
8237
+ }
8238
+ const attached = ref.set(payload);
8239
+ if (attached) {
8240
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
8241
+ }
8242
+ };
8243
+ var handleBelongsToMany = async (session, root, relationName, relation, payload, options) => {
8244
+ if (!Array.isArray(payload)) return;
8245
+ const collection = root[relationName];
8246
+ await collection.load();
8247
+ const targetTable = relation.target;
8248
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
8249
+ const seen = /* @__PURE__ */ new Set();
8250
+ for (const item of payload) {
8251
+ if (item === null || item === void 0) continue;
8252
+ if (typeof item === "number" || typeof item === "string") {
8253
+ const id = item;
8254
+ collection.attach(id);
8255
+ seen.add(toKey8(id));
8256
+ continue;
8257
+ }
8258
+ const asObj = item;
8259
+ const pkValue = asObj[targetPk];
8260
+ const entity = pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj) : ensureEntity(session, targetTable, asObj);
8261
+ assignColumns(targetTable, entity, asObj);
8262
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
8263
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
8264
+ collection.attach(entity);
8265
+ }
8266
+ if (pkValue !== void 0 && pkValue !== null) {
8267
+ seen.add(toKey8(pkValue));
8268
+ }
8269
+ }
8270
+ if (options.pruneMissing) {
8271
+ for (const item of [...collection.getItems()]) {
8272
+ const pkValue = item[targetPk];
8273
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
8274
+ collection.detach(item);
8275
+ }
8276
+ }
8277
+ }
8278
+ };
8279
+ var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
8280
+ switch (relation.type) {
8281
+ case RelationKinds.HasMany:
8282
+ return handleHasMany(session, entity, relationName, relation, payload, options);
8283
+ case RelationKinds.HasOne:
8284
+ return handleHasOne(session, entity, relationName, relation, payload, options);
8285
+ case RelationKinds.BelongsTo:
8286
+ return handleBelongsTo(session, entity, relationName, relation, payload, options);
8287
+ case RelationKinds.BelongsToMany:
8288
+ return handleBelongsToMany(session, entity, relationName, relation, payload, options);
8289
+ }
8290
+ };
8291
+ var applyGraphToEntity = async (session, table, entity, payload, options) => {
8292
+ assignColumns(table, entity, payload);
8293
+ for (const [relationName, relation] of Object.entries(table.relations)) {
8294
+ if (!(relationName in payload)) continue;
8295
+ await applyRelation(session, table, entity, relationName, relation, payload[relationName], options);
8296
+ }
8297
+ };
8298
+ var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
8299
+ const table = getTableDefFromEntity(entityClass);
8300
+ if (!table) {
8301
+ throw new Error("Entity metadata has not been bootstrapped");
8302
+ }
8303
+ const root = ensureEntity(session, table, payload);
8304
+ await applyGraphToEntity(session, table, root, payload, options);
8305
+ return root;
8306
+ };
8307
+
7666
8308
  // src/orm/orm-session.ts
7667
8309
  var OrmSession = class {
8310
+ /**
8311
+ * Creates a new OrmSession instance.
8312
+ * @param opts - Session options
8313
+ */
7668
8314
  constructor(opts) {
8315
+ /**
8316
+ * Registers a relation change.
8317
+ * @param root - The root entity
8318
+ * @param relationKey - The relation key
8319
+ * @param rootTable - The root table definition
8320
+ * @param relationName - The relation name
8321
+ * @param relation - The relation definition
8322
+ * @param change - The relation change
8323
+ */
7669
8324
  this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
7670
8325
  this.relationChanges.registerChange(
7671
8326
  buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
@@ -7679,42 +8334,117 @@ var OrmSession = class {
7679
8334
  this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
7680
8335
  this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
7681
8336
  }
8337
+ /**
8338
+ * Releases resources associated with this session (executor/pool leases) and resets tracking.
8339
+ * Must be safe to call multiple times.
8340
+ */
8341
+ async dispose() {
8342
+ try {
8343
+ await this.executor.dispose();
8344
+ } finally {
8345
+ this.unitOfWork.reset();
8346
+ this.relationChanges.reset();
8347
+ }
8348
+ }
8349
+ /**
8350
+ * Gets the database dialect.
8351
+ */
7682
8352
  get dialect() {
7683
8353
  return this.orm.dialect;
7684
8354
  }
8355
+ /**
8356
+ * Gets the identity buckets map.
8357
+ */
7685
8358
  get identityBuckets() {
7686
8359
  return this.unitOfWork.identityBuckets;
7687
8360
  }
8361
+ /**
8362
+ * Gets all tracked entities.
8363
+ */
7688
8364
  get tracked() {
7689
8365
  return this.unitOfWork.getTracked();
7690
8366
  }
8367
+ /**
8368
+ * Gets an entity by table and primary key.
8369
+ * @param table - The table definition
8370
+ * @param pk - The primary key value
8371
+ * @returns The entity or undefined if not found
8372
+ */
7691
8373
  getEntity(table, pk) {
7692
8374
  return this.unitOfWork.getEntity(table, pk);
7693
8375
  }
8376
+ /**
8377
+ * Sets an entity in the identity map.
8378
+ * @param table - The table definition
8379
+ * @param pk - The primary key value
8380
+ * @param entity - The entity instance
8381
+ */
7694
8382
  setEntity(table, pk, entity) {
7695
8383
  this.unitOfWork.setEntity(table, pk, entity);
7696
8384
  }
8385
+ /**
8386
+ * Tracks a new entity.
8387
+ * @param table - The table definition
8388
+ * @param entity - The entity instance
8389
+ * @param pk - Optional primary key value
8390
+ */
7697
8391
  trackNew(table, entity, pk) {
7698
8392
  this.unitOfWork.trackNew(table, entity, pk);
7699
8393
  }
8394
+ /**
8395
+ * Tracks a managed entity.
8396
+ * @param table - The table definition
8397
+ * @param pk - The primary key value
8398
+ * @param entity - The entity instance
8399
+ */
7700
8400
  trackManaged(table, pk, entity) {
7701
8401
  this.unitOfWork.trackManaged(table, pk, entity);
7702
8402
  }
8403
+ /**
8404
+ * Marks an entity as dirty (modified).
8405
+ * @param entity - The entity to mark as dirty
8406
+ */
7703
8407
  markDirty(entity) {
7704
8408
  this.unitOfWork.markDirty(entity);
7705
8409
  }
8410
+ /**
8411
+ * Marks an entity as removed.
8412
+ * @param entity - The entity to mark as removed
8413
+ */
7706
8414
  markRemoved(entity) {
7707
8415
  this.unitOfWork.markRemoved(entity);
7708
8416
  }
8417
+ /**
8418
+ * Gets all tracked entities for a specific table.
8419
+ * @param table - The table definition
8420
+ * @returns Array of tracked entities
8421
+ */
7709
8422
  getEntitiesForTable(table) {
7710
8423
  return this.unitOfWork.getEntitiesForTable(table);
7711
8424
  }
8425
+ /**
8426
+ * Registers an interceptor for flush lifecycle hooks.
8427
+ * @param interceptor - The interceptor to register
8428
+ */
7712
8429
  registerInterceptor(interceptor) {
7713
8430
  this.interceptors.push(interceptor);
7714
8431
  }
8432
+ /**
8433
+ * Registers a domain event handler.
8434
+ * @param type - The event type
8435
+ * @param handler - The event handler
8436
+ */
7715
8437
  registerDomainEventHandler(type, handler) {
7716
8438
  this.domainEvents.on(type, handler);
7717
8439
  }
8440
+ /**
8441
+ * Finds an entity by its primary key.
8442
+ * @template TCtor - The entity constructor type
8443
+ * @param entityClass - The entity constructor
8444
+ * @param id - The primary key value
8445
+ * @returns The entity instance or null if not found
8446
+ * @throws If entity metadata is not bootstrapped or table has no primary key
8447
+ */
7718
8448
  async find(entityClass, id) {
7719
8449
  const table = getTableDefFromEntity(entityClass);
7720
8450
  if (!table) {
@@ -7733,14 +8463,46 @@ var OrmSession = class {
7733
8463
  const rows = await executeHydrated(this, qb);
7734
8464
  return rows[0] ?? null;
7735
8465
  }
8466
+ /**
8467
+ * Finds a single entity using a query builder.
8468
+ * @template TTable - The table type
8469
+ * @param qb - The query builder
8470
+ * @returns The first entity instance or null if not found
8471
+ */
7736
8472
  async findOne(qb) {
7737
8473
  const limited = qb.limit(1);
7738
8474
  const rows = await executeHydrated(this, limited);
7739
8475
  return rows[0] ?? null;
7740
8476
  }
8477
+ /**
8478
+ * Finds multiple entities using a query builder.
8479
+ * @template TTable - The table type
8480
+ * @param qb - The query builder
8481
+ * @returns Array of entity instances
8482
+ */
7741
8483
  async findMany(qb) {
7742
8484
  return executeHydrated(this, qb);
7743
8485
  }
8486
+ /**
8487
+ * Saves an entity graph (root + nested relations) based on a DTO-like payload.
8488
+ * @param entityClass - Root entity constructor
8489
+ * @param payload - DTO payload containing column values and nested relations
8490
+ * @param options - Graph save options
8491
+ * @returns The root entity instance
8492
+ */
8493
+ async saveGraph(entityClass, payload, options) {
8494
+ const { transactional = true, ...graphOptions } = options ?? {};
8495
+ const execute = () => saveGraphInternal(this, entityClass, payload, graphOptions);
8496
+ if (!transactional) {
8497
+ return execute();
8498
+ }
8499
+ return this.transaction(() => execute());
8500
+ }
8501
+ /**
8502
+ * Persists an entity (either inserts or updates).
8503
+ * @param entity - The entity to persist
8504
+ * @throws If entity metadata is not bootstrapped
8505
+ */
7744
8506
  async persist(entity) {
7745
8507
  if (this.unitOfWork.findTracked(entity)) {
7746
8508
  return;
@@ -7757,12 +8519,22 @@ var OrmSession = class {
7757
8519
  this.trackNew(table, entity);
7758
8520
  }
7759
8521
  }
8522
+ /**
8523
+ * Marks an entity for removal.
8524
+ * @param entity - The entity to remove
8525
+ */
7760
8526
  async remove(entity) {
7761
8527
  this.markRemoved(entity);
7762
8528
  }
8529
+ /**
8530
+ * Flushes pending changes to the database.
8531
+ */
7763
8532
  async flush() {
7764
8533
  await this.unitOfWork.flush();
7765
8534
  }
8535
+ /**
8536
+ * Flushes pending changes with interceptors and relation processing.
8537
+ */
7766
8538
  async flushWithHooks() {
7767
8539
  for (const interceptor of this.interceptors) {
7768
8540
  await interceptor.beforeFlush?.(this);
@@ -7774,14 +8546,24 @@ var OrmSession = class {
7774
8546
  await interceptor.afterFlush?.(this);
7775
8547
  }
7776
8548
  }
8549
+ /**
8550
+ * Commits the current transaction.
8551
+ */
7777
8552
  async commit() {
7778
8553
  await runInTransaction(this.executor, async () => {
7779
8554
  await this.flushWithHooks();
7780
8555
  });
7781
8556
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
7782
8557
  }
8558
+ /**
8559
+ * Executes a function within a transaction.
8560
+ * @template T - The return type
8561
+ * @param fn - The function to execute
8562
+ * @returns The result of the function
8563
+ * @throws If the transaction fails
8564
+ */
7783
8565
  async transaction(fn4) {
7784
- if (!this.executor.beginTransaction) {
8566
+ if (!this.executor.capabilities.transactions) {
7785
8567
  const result = await fn4(this);
7786
8568
  await this.commit();
7787
8569
  return result;
@@ -7790,7 +8572,7 @@ var OrmSession = class {
7790
8572
  try {
7791
8573
  const result = await fn4(this);
7792
8574
  await this.flushWithHooks();
7793
- await this.executor.commitTransaction?.();
8575
+ await this.executor.commitTransaction();
7794
8576
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
7795
8577
  return result;
7796
8578
  } catch (err) {
@@ -7798,11 +8580,20 @@ var OrmSession = class {
7798
8580
  throw err;
7799
8581
  }
7800
8582
  }
8583
+ /**
8584
+ * Rolls back the current transaction.
8585
+ */
7801
8586
  async rollback() {
7802
- await this.executor.rollbackTransaction?.();
8587
+ if (this.executor.capabilities.transactions) {
8588
+ await this.executor.rollbackTransaction();
8589
+ }
7803
8590
  this.unitOfWork.reset();
7804
8591
  this.relationChanges.reset();
7805
8592
  }
8593
+ /**
8594
+ * Gets the execution context.
8595
+ * @returns The execution context
8596
+ */
7806
8597
  getExecutionContext() {
7807
8598
  return {
7808
8599
  dialect: this.orm.dialect,
@@ -7810,6 +8601,10 @@ var OrmSession = class {
7810
8601
  interceptors: this.orm.interceptors
7811
8602
  };
7812
8603
  }
8604
+ /**
8605
+ * Gets the hydration context.
8606
+ * @returns The hydration context
8607
+ */
7813
8608
  getHydrationContext() {
7814
8609
  return {
7815
8610
  identityMap: this.identityMap,
@@ -7852,29 +8647,49 @@ var InterceptorPipeline = class {
7852
8647
 
7853
8648
  // src/orm/orm.ts
7854
8649
  var Orm = class {
8650
+ /**
8651
+ * Creates a new ORM instance.
8652
+ * @param opts - ORM options
8653
+ */
7855
8654
  constructor(opts) {
7856
8655
  this.dialect = opts.dialect;
7857
8656
  this.interceptors = opts.interceptors ?? new InterceptorPipeline();
7858
8657
  this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
7859
8658
  this.executorFactory = opts.executorFactory;
7860
8659
  }
7861
- createSession(options) {
7862
- const executor = this.executorFactory.createExecutor(options?.tx);
8660
+ /**
8661
+ * Creates a new ORM session.
8662
+ * @param options - Optional session options
8663
+ * @returns The ORM session
8664
+ */
8665
+ createSession() {
8666
+ const executor = this.executorFactory.createExecutor();
7863
8667
  return new OrmSession({ orm: this, executor });
7864
8668
  }
8669
+ /**
8670
+ * Executes a function within a transaction.
8671
+ * @template T - The return type
8672
+ * @param fn - The function to execute
8673
+ * @returns The result of the function
8674
+ * @throws If the transaction fails
8675
+ */
7865
8676
  async transaction(fn4) {
7866
8677
  const executor = this.executorFactory.createTransactionalExecutor();
7867
8678
  const session = new OrmSession({ orm: this, executor });
7868
8679
  try {
7869
- const result = await fn4(session);
7870
- await session.commit();
7871
- return result;
8680
+ return await session.transaction(() => fn4(session));
7872
8681
  } catch (err) {
7873
- await session.rollback();
7874
8682
  throw err;
7875
8683
  } finally {
8684
+ await session.dispose();
7876
8685
  }
7877
8686
  }
8687
+ /**
8688
+ * Shuts down the ORM and releases underlying resources (pools, timers).
8689
+ */
8690
+ async dispose() {
8691
+ await this.executorFactory.dispose();
8692
+ }
7878
8693
  };
7879
8694
 
7880
8695
  // src/decorators/decorator-metadata.ts
@@ -8133,18 +8948,245 @@ function rowsToQueryResult(rows) {
8133
8948
  return { columns, values };
8134
8949
  }
8135
8950
  function createExecutorFromQueryRunner(runner) {
8951
+ const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
8136
8952
  return {
8953
+ capabilities: {
8954
+ transactions: supportsTransactions
8955
+ },
8137
8956
  async executeSql(sql, params) {
8138
8957
  const rows = await runner.query(sql, params);
8139
8958
  const result = rowsToQueryResult(rows);
8140
8959
  return [result];
8141
8960
  },
8142
- beginTransaction: runner.beginTransaction?.bind(runner),
8143
- commitTransaction: runner.commitTransaction?.bind(runner),
8144
- rollbackTransaction: runner.rollbackTransaction?.bind(runner)
8961
+ async beginTransaction() {
8962
+ if (!supportsTransactions) {
8963
+ throw new Error("Transactions are not supported by this executor");
8964
+ }
8965
+ await runner.beginTransaction.call(runner);
8966
+ },
8967
+ async commitTransaction() {
8968
+ if (!supportsTransactions) {
8969
+ throw new Error("Transactions are not supported by this executor");
8970
+ }
8971
+ await runner.commitTransaction.call(runner);
8972
+ },
8973
+ async rollbackTransaction() {
8974
+ if (!supportsTransactions) {
8975
+ throw new Error("Transactions are not supported by this executor");
8976
+ }
8977
+ await runner.rollbackTransaction.call(runner);
8978
+ },
8979
+ async dispose() {
8980
+ await runner.dispose?.call(runner);
8981
+ }
8145
8982
  };
8146
8983
  }
8147
8984
 
8985
+ // src/core/execution/pooling/pool.ts
8986
+ var deferred = () => {
8987
+ let resolve;
8988
+ let reject;
8989
+ const promise = new Promise((res, rej) => {
8990
+ resolve = res;
8991
+ reject = rej;
8992
+ });
8993
+ return { promise, resolve, reject };
8994
+ };
8995
+ var Pool = class {
8996
+ constructor(adapter, options) {
8997
+ this.destroyed = false;
8998
+ this.creating = 0;
8999
+ this.leased = 0;
9000
+ this.idle = [];
9001
+ this.waiters = [];
9002
+ this.reapTimer = null;
9003
+ if (!Number.isFinite(options.max) || options.max <= 0) {
9004
+ throw new Error("Pool options.max must be a positive number");
9005
+ }
9006
+ this.adapter = adapter;
9007
+ this.options = { max: options.max, ...options };
9008
+ const idleTimeout = this.options.idleTimeoutMillis;
9009
+ if (idleTimeout && idleTimeout > 0) {
9010
+ const interval = this.options.reapIntervalMillis ?? Math.max(1e3, Math.floor(idleTimeout / 2));
9011
+ this.reapTimer = setInterval(() => {
9012
+ void this.reapIdle();
9013
+ }, interval);
9014
+ this.reapTimer.unref?.();
9015
+ }
9016
+ const min2 = this.options.min ?? 0;
9017
+ if (min2 > 0) {
9018
+ void this.warm(min2);
9019
+ }
9020
+ }
9021
+ /**
9022
+ * Acquire a resource lease.
9023
+ * The returned lease MUST be released or destroyed.
9024
+ */
9025
+ async acquire() {
9026
+ if (this.destroyed) {
9027
+ throw new Error("Pool is destroyed");
9028
+ }
9029
+ const idle = await this.takeIdleValidated();
9030
+ if (idle) {
9031
+ this.leased++;
9032
+ return this.makeLease(idle);
9033
+ }
9034
+ if (this.totalLive() < this.options.max) {
9035
+ this.creating++;
9036
+ try {
9037
+ const created = await this.adapter.create();
9038
+ this.leased++;
9039
+ return this.makeLease(created);
9040
+ } finally {
9041
+ this.creating--;
9042
+ }
9043
+ }
9044
+ const waiter = deferred();
9045
+ this.waiters.push(waiter);
9046
+ const timeout = this.options.acquireTimeoutMillis;
9047
+ let timer = null;
9048
+ if (timeout && timeout > 0) {
9049
+ timer = setTimeout(() => {
9050
+ const idx = this.waiters.indexOf(waiter);
9051
+ if (idx >= 0) this.waiters.splice(idx, 1);
9052
+ waiter.reject(new Error("Pool acquire timeout"));
9053
+ }, timeout);
9054
+ timer.unref?.();
9055
+ }
9056
+ try {
9057
+ return await waiter.promise;
9058
+ } finally {
9059
+ if (timer) clearTimeout(timer);
9060
+ }
9061
+ }
9062
+ /** Destroy pool and all idle resources; waits for in-flight creations to settle. */
9063
+ async destroy() {
9064
+ if (this.destroyed) return;
9065
+ this.destroyed = true;
9066
+ if (this.reapTimer) {
9067
+ clearInterval(this.reapTimer);
9068
+ this.reapTimer = null;
9069
+ }
9070
+ while (this.waiters.length) {
9071
+ this.waiters.shift().reject(new Error("Pool destroyed"));
9072
+ }
9073
+ while (this.idle.length) {
9074
+ const entry = this.idle.shift();
9075
+ await this.adapter.destroy(entry.resource);
9076
+ }
9077
+ }
9078
+ totalLive() {
9079
+ return this.idle.length + this.leased + this.creating;
9080
+ }
9081
+ makeLease(resource) {
9082
+ let done = false;
9083
+ return {
9084
+ resource,
9085
+ release: async () => {
9086
+ if (done) return;
9087
+ done = true;
9088
+ await this.releaseResource(resource);
9089
+ },
9090
+ destroy: async () => {
9091
+ if (done) return;
9092
+ done = true;
9093
+ await this.destroyResource(resource);
9094
+ }
9095
+ };
9096
+ }
9097
+ async releaseResource(resource) {
9098
+ this.leased = Math.max(0, this.leased - 1);
9099
+ if (this.destroyed) {
9100
+ await this.adapter.destroy(resource);
9101
+ return;
9102
+ }
9103
+ const next = this.waiters.shift();
9104
+ if (next) {
9105
+ this.leased++;
9106
+ next.resolve(this.makeLease(resource));
9107
+ return;
9108
+ }
9109
+ this.idle.push({ resource, lastUsedAt: Date.now() });
9110
+ await this.trimToMinMax();
9111
+ }
9112
+ async destroyResource(resource) {
9113
+ this.leased = Math.max(0, this.leased - 1);
9114
+ await this.adapter.destroy(resource);
9115
+ if (!this.destroyed && this.waiters.length && this.totalLive() < this.options.max) {
9116
+ const waiter = this.waiters.shift();
9117
+ this.creating++;
9118
+ try {
9119
+ const created = await this.adapter.create();
9120
+ this.leased++;
9121
+ waiter.resolve(this.makeLease(created));
9122
+ } catch (err) {
9123
+ waiter.reject(err);
9124
+ } finally {
9125
+ this.creating--;
9126
+ }
9127
+ }
9128
+ }
9129
+ async takeIdleValidated() {
9130
+ while (this.idle.length) {
9131
+ const entry = this.idle.pop();
9132
+ if (!this.adapter.validate) {
9133
+ return entry.resource;
9134
+ }
9135
+ const ok = await this.adapter.validate(entry.resource);
9136
+ if (ok) {
9137
+ return entry.resource;
9138
+ }
9139
+ await this.adapter.destroy(entry.resource);
9140
+ }
9141
+ return null;
9142
+ }
9143
+ async reapIdle() {
9144
+ if (this.destroyed) return;
9145
+ const idleTimeout = this.options.idleTimeoutMillis;
9146
+ if (!idleTimeout || idleTimeout <= 0) return;
9147
+ const now2 = Date.now();
9148
+ const min2 = this.options.min ?? 0;
9149
+ const keep = [];
9150
+ const kill = [];
9151
+ for (const entry of this.idle) {
9152
+ const expired = now2 - entry.lastUsedAt >= idleTimeout;
9153
+ if (expired) kill.push(entry);
9154
+ else keep.push(entry);
9155
+ }
9156
+ while (keep.length < min2 && kill.length) {
9157
+ keep.push(kill.pop());
9158
+ }
9159
+ this.idle.length = 0;
9160
+ this.idle.push(...keep);
9161
+ for (const entry of kill) {
9162
+ await this.adapter.destroy(entry.resource);
9163
+ }
9164
+ }
9165
+ async warm(targetMin) {
9166
+ const min2 = Math.max(0, targetMin);
9167
+ while (!this.destroyed && this.idle.length < min2 && this.totalLive() < this.options.max) {
9168
+ this.creating++;
9169
+ try {
9170
+ const created = await this.adapter.create();
9171
+ this.idle.push({ resource: created, lastUsedAt: Date.now() });
9172
+ } catch {
9173
+ break;
9174
+ } finally {
9175
+ this.creating--;
9176
+ }
9177
+ }
9178
+ }
9179
+ async trimToMinMax() {
9180
+ const max2 = this.options.max;
9181
+ const min2 = this.options.min ?? 0;
9182
+ while (this.totalLive() > max2 && this.idle.length > min2) {
9183
+ const entry = this.idle.shift();
9184
+ if (!entry) break;
9185
+ await this.adapter.destroy(entry.resource);
9186
+ }
9187
+ }
9188
+ };
9189
+
8148
9190
  // src/core/execution/executors/postgres-executor.ts
8149
9191
  function createPostgresExecutor(client) {
8150
9192
  return createExecutorFromQueryRunner({
@@ -8166,7 +9208,11 @@ function createPostgresExecutor(client) {
8166
9208
 
8167
9209
  // src/core/execution/executors/mysql-executor.ts
8168
9210
  function createMysqlExecutor(client) {
9211
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
8169
9212
  return {
9213
+ capabilities: {
9214
+ transactions: supportsTransactions
9215
+ },
8170
9216
  async executeSql(sql, params) {
8171
9217
  const [rows] = await client.query(sql, params);
8172
9218
  if (!Array.isArray(rows)) {
@@ -8178,53 +9224,94 @@ function createMysqlExecutor(client) {
8178
9224
  return [result];
8179
9225
  },
8180
9226
  async beginTransaction() {
8181
- if (!client.beginTransaction) return;
9227
+ if (!supportsTransactions) {
9228
+ throw new Error("Transactions are not supported by this executor");
9229
+ }
8182
9230
  await client.beginTransaction();
8183
9231
  },
8184
9232
  async commitTransaction() {
8185
- if (!client.commit) return;
9233
+ if (!supportsTransactions) {
9234
+ throw new Error("Transactions are not supported by this executor");
9235
+ }
8186
9236
  await client.commit();
8187
9237
  },
8188
9238
  async rollbackTransaction() {
8189
- if (!client.rollback) return;
9239
+ if (!supportsTransactions) {
9240
+ throw new Error("Transactions are not supported by this executor");
9241
+ }
8190
9242
  await client.rollback();
9243
+ },
9244
+ async dispose() {
8191
9245
  }
8192
9246
  };
8193
9247
  }
8194
9248
 
8195
9249
  // src/core/execution/executors/sqlite-executor.ts
8196
9250
  function createSqliteExecutor(client) {
9251
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
8197
9252
  return {
9253
+ capabilities: {
9254
+ transactions: supportsTransactions
9255
+ },
8198
9256
  async executeSql(sql, params) {
8199
9257
  const rows = await client.all(sql, params);
8200
9258
  const result = rowsToQueryResult(rows);
8201
9259
  return [result];
8202
9260
  },
8203
- beginTransaction: client.beginTransaction?.bind(client),
8204
- commitTransaction: client.commitTransaction?.bind(client),
8205
- rollbackTransaction: client.rollbackTransaction?.bind(client)
9261
+ async beginTransaction() {
9262
+ if (!supportsTransactions) {
9263
+ throw new Error("Transactions are not supported by this executor");
9264
+ }
9265
+ await client.beginTransaction();
9266
+ },
9267
+ async commitTransaction() {
9268
+ if (!supportsTransactions) {
9269
+ throw new Error("Transactions are not supported by this executor");
9270
+ }
9271
+ await client.commitTransaction();
9272
+ },
9273
+ async rollbackTransaction() {
9274
+ if (!supportsTransactions) {
9275
+ throw new Error("Transactions are not supported by this executor");
9276
+ }
9277
+ await client.rollbackTransaction();
9278
+ },
9279
+ async dispose() {
9280
+ }
8206
9281
  };
8207
9282
  }
8208
9283
 
8209
9284
  // src/core/execution/executors/mssql-executor.ts
8210
9285
  function createMssqlExecutor(client) {
9286
+ const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
8211
9287
  return {
9288
+ capabilities: {
9289
+ transactions: supportsTransactions
9290
+ },
8212
9291
  async executeSql(sql, params) {
8213
9292
  const { recordset } = await client.query(sql, params);
8214
9293
  const result = rowsToQueryResult(recordset ?? []);
8215
9294
  return [result];
8216
9295
  },
8217
9296
  async beginTransaction() {
8218
- if (!client.beginTransaction) return;
9297
+ if (!supportsTransactions) {
9298
+ throw new Error("Transactions are not supported by this executor");
9299
+ }
8219
9300
  await client.beginTransaction();
8220
9301
  },
8221
9302
  async commitTransaction() {
8222
- if (!client.commit) return;
9303
+ if (!supportsTransactions) {
9304
+ throw new Error("Transactions are not supported by this executor");
9305
+ }
8223
9306
  await client.commit();
8224
9307
  },
8225
9308
  async rollbackTransaction() {
8226
- if (!client.rollback) return;
9309
+ if (!supportsTransactions) {
9310
+ throw new Error("Transactions are not supported by this executor");
9311
+ }
8227
9312
  await client.rollback();
9313
+ },
9314
+ async dispose() {
8228
9315
  }
8229
9316
  };
8230
9317
  }
@@ -8293,6 +9380,86 @@ function createTediousExecutor(connection, module2, options) {
8293
9380
  const client = createTediousMssqlClient(connection, module2, options);
8294
9381
  return createMssqlExecutor(client);
8295
9382
  }
9383
+
9384
+ // src/orm/pooled-executor-factory.ts
9385
+ function createPooledExecutorFactory(opts) {
9386
+ const { pool, adapter } = opts;
9387
+ const makeExecutor = (mode) => {
9388
+ let lease = null;
9389
+ const getLease = async () => {
9390
+ if (lease) return lease;
9391
+ lease = await pool.acquire();
9392
+ return lease;
9393
+ };
9394
+ const executeWithConn = async (conn, sql, params) => {
9395
+ const rows = await adapter.query(conn, sql, params);
9396
+ return [rowsToQueryResult(rows)];
9397
+ };
9398
+ return {
9399
+ capabilities: { transactions: true },
9400
+ async executeSql(sql, params) {
9401
+ if (mode === "sticky") {
9402
+ const l2 = await getLease();
9403
+ return executeWithConn(l2.resource, sql, params);
9404
+ }
9405
+ if (lease) {
9406
+ return executeWithConn(lease.resource, sql, params);
9407
+ }
9408
+ const l = await pool.acquire();
9409
+ try {
9410
+ return await executeWithConn(l.resource, sql, params);
9411
+ } finally {
9412
+ await l.release();
9413
+ }
9414
+ },
9415
+ async beginTransaction() {
9416
+ const l = await getLease();
9417
+ await adapter.beginTransaction(l.resource);
9418
+ },
9419
+ async commitTransaction() {
9420
+ if (!lease) {
9421
+ throw new Error("commitTransaction called without an active transaction");
9422
+ }
9423
+ const l = lease;
9424
+ try {
9425
+ await adapter.commitTransaction(l.resource);
9426
+ } finally {
9427
+ lease = null;
9428
+ await l.release();
9429
+ }
9430
+ },
9431
+ async rollbackTransaction() {
9432
+ if (!lease) {
9433
+ return;
9434
+ }
9435
+ const l = lease;
9436
+ try {
9437
+ await adapter.rollbackTransaction(l.resource);
9438
+ } finally {
9439
+ lease = null;
9440
+ await l.release();
9441
+ }
9442
+ },
9443
+ async dispose() {
9444
+ if (!lease) return;
9445
+ const l = lease;
9446
+ lease = null;
9447
+ await l.release();
9448
+ }
9449
+ };
9450
+ };
9451
+ return {
9452
+ createExecutor() {
9453
+ return makeExecutor("session");
9454
+ },
9455
+ createTransactionalExecutor() {
9456
+ return makeExecutor("sticky");
9457
+ },
9458
+ async dispose() {
9459
+ await pool.destroy();
9460
+ }
9461
+ };
9462
+ }
8296
9463
  // Annotate the CommonJS export names for ESM import in node:
8297
9464
  0 && (module.exports = {
8298
9465
  AsyncLocalStorage,
@@ -8312,6 +9479,7 @@ function createTediousExecutor(connection, module2, options) {
8312
9479
  MySqlDialect,
8313
9480
  Orm,
8314
9481
  OrmSession,
9482
+ Pool,
8315
9483
  PostgresDialect,
8316
9484
  PrimaryKey,
8317
9485
  RelationKinds,
@@ -8357,6 +9525,7 @@ function createTediousExecutor(connection, module2, options) {
8357
9525
  createLiteral,
8358
9526
  createMssqlExecutor,
8359
9527
  createMysqlExecutor,
9528
+ createPooledExecutorFactory,
8360
9529
  createPostgresExecutor,
8361
9530
  createQueryLoggingExecutor,
8362
9531
  createSqliteExecutor,
@@ -8398,6 +9567,7 @@ function createTediousExecutor(connection, module2, options) {
8398
9567
  hasOne,
8399
9568
  hydrateRows,
8400
9569
  inList,
9570
+ inSubquery,
8401
9571
  instr,
8402
9572
  introspectSchema,
8403
9573
  isCaseExpressionNode,
@@ -8438,6 +9608,7 @@ function createTediousExecutor(connection, module2, options) {
8438
9608
  notBetween,
8439
9609
  notExists,
8440
9610
  notInList,
9611
+ notInSubquery,
8441
9612
  notLike,
8442
9613
  now,
8443
9614
  ntile,
@@ -8473,6 +9644,7 @@ function createTediousExecutor(connection, module2, options) {
8473
9644
  substr,
8474
9645
  sum,
8475
9646
  synchronizeSchema,
9647
+ tableRef,
8476
9648
  tan,
8477
9649
  toColumnRef,
8478
9650
  toTableRef,