metal-orm 1.0.40 → 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.
- package/dist/index.cjs +1244 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +655 -30
- package/dist/index.d.ts +655 -30
- package/dist/index.js +1240 -122
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen/typescript.ts +6 -2
- package/src/core/ast/expression-builders.ts +25 -4
- package/src/core/ast/expression-nodes.ts +3 -1
- package/src/core/ast/query.ts +24 -2
- package/src/core/dialect/abstract.ts +6 -2
- package/src/core/dialect/base/join-compiler.ts +9 -12
- package/src/core/dialect/base/sql-dialect.ts +98 -17
- package/src/core/dialect/mssql/index.ts +30 -62
- package/src/core/dialect/sqlite/index.ts +39 -34
- package/src/core/execution/db-executor.ts +46 -6
- package/src/core/execution/executors/mssql-executor.ts +39 -22
- package/src/core/execution/executors/mysql-executor.ts +23 -6
- package/src/core/execution/executors/sqlite-executor.ts +29 -3
- package/src/core/execution/pooling/pool-types.ts +30 -0
- package/src/core/execution/pooling/pool.ts +268 -0
- package/src/decorators/bootstrap.ts +7 -7
- package/src/index.ts +6 -0
- package/src/orm/domain-event-bus.ts +49 -0
- package/src/orm/entity-metadata.ts +9 -9
- package/src/orm/entity.ts +58 -0
- package/src/orm/orm-session.ts +465 -270
- package/src/orm/orm.ts +61 -11
- package/src/orm/pooled-executor-factory.ts +131 -0
- package/src/orm/query-logger.ts +6 -12
- package/src/orm/relation-change-processor.ts +75 -0
- package/src/orm/relations/many-to-many.ts +4 -2
- package/src/orm/save-graph.ts +303 -0
- package/src/orm/transaction-runner.ts +3 -3
- package/src/orm/unit-of-work.ts +128 -0
- package/src/query-builder/delete-query-state.ts +67 -38
- package/src/query-builder/delete.ts +37 -1
- package/src/query-builder/insert-query-state.ts +131 -61
- package/src/query-builder/insert.ts +27 -1
- package/src/query-builder/update-query-state.ts +114 -77
- package/src/query-builder/update.ts +38 -1
package/dist/index.js
CHANGED
|
@@ -287,6 +287,12 @@ var toOperand = (val) => {
|
|
|
287
287
|
}
|
|
288
288
|
return toNode(val);
|
|
289
289
|
};
|
|
290
|
+
var hasQueryAst = (value) => typeof value.getAST === "function";
|
|
291
|
+
var resolveSelectQueryNode = (query) => hasQueryAst(query) ? query.getAST() : query;
|
|
292
|
+
var toScalarSubqueryNode = (query) => ({
|
|
293
|
+
type: "ScalarSubquery",
|
|
294
|
+
query: resolveSelectQueryNode(query)
|
|
295
|
+
});
|
|
290
296
|
var columnOperand = (col2) => toNode(col2);
|
|
291
297
|
var outerRef = (col2) => ({
|
|
292
298
|
...columnOperand(col2),
|
|
@@ -337,14 +343,16 @@ var isNotNull = (left2) => ({
|
|
|
337
343
|
left: toNode(left2),
|
|
338
344
|
operator: "IS NOT NULL"
|
|
339
345
|
});
|
|
340
|
-
var createInExpression = (operator, left2,
|
|
346
|
+
var createInExpression = (operator, left2, right2) => ({
|
|
341
347
|
type: "InExpression",
|
|
342
348
|
left: toNode(left2),
|
|
343
349
|
operator,
|
|
344
|
-
right:
|
|
350
|
+
right: right2
|
|
345
351
|
});
|
|
346
|
-
var inList = (left2, values) => createInExpression("IN", left2, values);
|
|
347
|
-
var notInList = (left2, values) => createInExpression("NOT IN", left2, values);
|
|
352
|
+
var inList = (left2, values) => createInExpression("IN", left2, values.map((v) => toOperand(v)));
|
|
353
|
+
var notInList = (left2, values) => createInExpression("NOT IN", left2, values.map((v) => toOperand(v)));
|
|
354
|
+
var inSubquery = (left2, subquery) => createInExpression("IN", left2, toScalarSubqueryNode(subquery));
|
|
355
|
+
var notInSubquery = (left2, subquery) => createInExpression("NOT IN", left2, toScalarSubqueryNode(subquery));
|
|
348
356
|
var createBetweenExpression = (operator, left2, lower2, upper2) => ({
|
|
349
357
|
type: "BetweenExpression",
|
|
350
358
|
left: toNode(left2),
|
|
@@ -1027,8 +1035,12 @@ var Dialect = class _Dialect {
|
|
|
1027
1035
|
});
|
|
1028
1036
|
this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
|
|
1029
1037
|
const left2 = this.compileOperand(inExpr.left, ctx);
|
|
1030
|
-
|
|
1031
|
-
|
|
1038
|
+
if (Array.isArray(inExpr.right)) {
|
|
1039
|
+
const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
|
|
1040
|
+
return `${left2} ${inExpr.operator} (${values})`;
|
|
1041
|
+
}
|
|
1042
|
+
const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
|
|
1043
|
+
return `${left2} ${inExpr.operator} (${subquerySql})`;
|
|
1032
1044
|
});
|
|
1033
1045
|
this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
|
|
1034
1046
|
const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
|
|
@@ -1286,17 +1298,9 @@ var NoReturningStrategy = class {
|
|
|
1286
1298
|
|
|
1287
1299
|
// src/core/dialect/base/join-compiler.ts
|
|
1288
1300
|
var JoinCompiler = class {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
* @param ctx - The compiler context for expression compilation.
|
|
1293
|
-
* @param compileFrom - Function to compile table sources (tables or subqueries).
|
|
1294
|
-
* @param compileExpression - Function to compile join condition expressions.
|
|
1295
|
-
* @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
|
|
1296
|
-
*/
|
|
1297
|
-
static compileJoins(ast, ctx, compileFrom, compileExpression) {
|
|
1298
|
-
if (!ast.joins || ast.joins.length === 0) return "";
|
|
1299
|
-
const parts = ast.joins.map((j) => {
|
|
1301
|
+
static compileJoins(joins, ctx, compileFrom, compileExpression) {
|
|
1302
|
+
if (!joins || joins.length === 0) return "";
|
|
1303
|
+
const parts = joins.map((j) => {
|
|
1300
1304
|
const table = compileFrom(j.table, ctx);
|
|
1301
1305
|
const cond = compileExpression(j.condition, ctx);
|
|
1302
1306
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
@@ -1379,25 +1383,41 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1379
1383
|
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
1380
1384
|
}
|
|
1381
1385
|
compileInsertAst(ast, ctx) {
|
|
1386
|
+
if (!ast.columns.length) {
|
|
1387
|
+
throw new Error("INSERT queries must specify columns.");
|
|
1388
|
+
}
|
|
1382
1389
|
const table = this.compileTableName(ast.into);
|
|
1383
1390
|
const columnList = this.compileInsertColumnList(ast.columns);
|
|
1384
|
-
const
|
|
1391
|
+
const source = this.compileInsertSource(ast.source, ctx);
|
|
1385
1392
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1386
|
-
return `INSERT INTO ${table} (${columnList})
|
|
1393
|
+
return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
|
|
1387
1394
|
}
|
|
1388
1395
|
compileReturning(returning, ctx) {
|
|
1389
1396
|
return this.returningStrategy.compileReturning(returning, ctx);
|
|
1390
1397
|
}
|
|
1398
|
+
compileInsertSource(source, ctx) {
|
|
1399
|
+
if (source.type === "InsertValues") {
|
|
1400
|
+
if (!source.rows.length) {
|
|
1401
|
+
throw new Error("INSERT ... VALUES requires at least one row.");
|
|
1402
|
+
}
|
|
1403
|
+
const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
1404
|
+
return `VALUES ${values}`;
|
|
1405
|
+
}
|
|
1406
|
+
const normalized = this.normalizeSelectAst(source.query);
|
|
1407
|
+
return this.compileSelectAst(normalized, ctx).trim();
|
|
1408
|
+
}
|
|
1391
1409
|
compileInsertColumnList(columns) {
|
|
1392
1410
|
return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
|
|
1393
1411
|
}
|
|
1394
|
-
compileInsertValues(values, ctx) {
|
|
1395
|
-
return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
1396
|
-
}
|
|
1397
1412
|
compileSelectCore(ast, ctx) {
|
|
1398
1413
|
const columns = this.compileSelectColumns(ast, ctx);
|
|
1399
1414
|
const from = this.compileFrom(ast.from, ctx);
|
|
1400
|
-
const joins = JoinCompiler.compileJoins(
|
|
1415
|
+
const joins = JoinCompiler.compileJoins(
|
|
1416
|
+
ast.joins,
|
|
1417
|
+
ctx,
|
|
1418
|
+
this.compileFrom.bind(this),
|
|
1419
|
+
this.compileExpression.bind(this)
|
|
1420
|
+
);
|
|
1401
1421
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1402
1422
|
const groupBy = GroupByCompiler.compileGroupBy(ast, (term) => this.compileOrderingTerm(term, ctx));
|
|
1403
1423
|
const having = this.compileHaving(ast, ctx);
|
|
@@ -1411,25 +1431,37 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1411
1431
|
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
1412
1432
|
}
|
|
1413
1433
|
compileUpdateAst(ast, ctx) {
|
|
1414
|
-
const
|
|
1415
|
-
const assignments = this.compileUpdateAssignments(ast.set, ctx);
|
|
1434
|
+
const target = this.compileTableReference(ast.table);
|
|
1435
|
+
const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
|
|
1436
|
+
const fromClause = this.compileUpdateFromClause(ast, ctx);
|
|
1416
1437
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1417
1438
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1418
|
-
return `UPDATE ${
|
|
1439
|
+
return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
|
|
1419
1440
|
}
|
|
1420
|
-
compileUpdateAssignments(assignments, ctx) {
|
|
1441
|
+
compileUpdateAssignments(assignments, table, ctx) {
|
|
1421
1442
|
return assignments.map((assignment) => {
|
|
1422
1443
|
const col2 = assignment.column;
|
|
1423
|
-
const target = this.
|
|
1444
|
+
const target = this.compileQualifiedColumn(col2, table);
|
|
1424
1445
|
const value = this.compileOperand(assignment.value, ctx);
|
|
1425
1446
|
return `${target} = ${value}`;
|
|
1426
1447
|
}).join(", ");
|
|
1427
1448
|
}
|
|
1449
|
+
compileQualifiedColumn(column, table) {
|
|
1450
|
+
const baseTableName = table.name;
|
|
1451
|
+
const alias = table.alias;
|
|
1452
|
+
const columnTable = column.table ?? alias ?? baseTableName;
|
|
1453
|
+
const tableQualifier = alias && column.table === baseTableName ? alias : columnTable;
|
|
1454
|
+
if (!tableQualifier) {
|
|
1455
|
+
return this.quoteIdentifier(column.name);
|
|
1456
|
+
}
|
|
1457
|
+
return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
|
|
1458
|
+
}
|
|
1428
1459
|
compileDeleteAst(ast, ctx) {
|
|
1429
|
-
const
|
|
1460
|
+
const target = this.compileTableReference(ast.from);
|
|
1461
|
+
const usingClause = this.compileDeleteUsingClause(ast, ctx);
|
|
1430
1462
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1431
1463
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1432
|
-
return `DELETE FROM ${
|
|
1464
|
+
return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
|
|
1433
1465
|
}
|
|
1434
1466
|
formatReturningColumns(returning) {
|
|
1435
1467
|
return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
|
|
@@ -1484,6 +1516,38 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1484
1516
|
}
|
|
1485
1517
|
return this.quoteIdentifier(table.name);
|
|
1486
1518
|
}
|
|
1519
|
+
compileTableReference(table) {
|
|
1520
|
+
const base = this.compileTableName(table);
|
|
1521
|
+
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
1522
|
+
}
|
|
1523
|
+
compileUpdateFromClause(ast, ctx) {
|
|
1524
|
+
if (!ast.from && (!ast.joins || ast.joins.length === 0)) return "";
|
|
1525
|
+
if (!ast.from) {
|
|
1526
|
+
throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
|
|
1527
|
+
}
|
|
1528
|
+
const from = this.compileFrom(ast.from, ctx);
|
|
1529
|
+
const joins = JoinCompiler.compileJoins(
|
|
1530
|
+
ast.joins,
|
|
1531
|
+
ctx,
|
|
1532
|
+
this.compileFrom.bind(this),
|
|
1533
|
+
this.compileExpression.bind(this)
|
|
1534
|
+
);
|
|
1535
|
+
return ` FROM ${from}${joins}`;
|
|
1536
|
+
}
|
|
1537
|
+
compileDeleteUsingClause(ast, ctx) {
|
|
1538
|
+
if (!ast.using && (!ast.joins || ast.joins.length === 0)) return "";
|
|
1539
|
+
if (!ast.using) {
|
|
1540
|
+
throw new Error("DELETE with JOINs requires a USING clause.");
|
|
1541
|
+
}
|
|
1542
|
+
const usingTable = this.compileFrom(ast.using, ctx);
|
|
1543
|
+
const joins = JoinCompiler.compileJoins(
|
|
1544
|
+
ast.joins,
|
|
1545
|
+
ctx,
|
|
1546
|
+
this.compileFrom.bind(this),
|
|
1547
|
+
this.compileExpression.bind(this)
|
|
1548
|
+
);
|
|
1549
|
+
return ` USING ${usingTable}${joins}`;
|
|
1550
|
+
}
|
|
1487
1551
|
compileHaving(ast, ctx) {
|
|
1488
1552
|
if (!ast.having) return "";
|
|
1489
1553
|
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
@@ -1864,6 +1928,9 @@ var SqliteDialect = class extends SqlDialectBase {
|
|
|
1864
1928
|
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
1865
1929
|
return `json_extract(${col2}, '${node.path}')`;
|
|
1866
1930
|
}
|
|
1931
|
+
compileQualifiedColumn(column, _table) {
|
|
1932
|
+
return this.quoteIdentifier(column.name);
|
|
1933
|
+
}
|
|
1867
1934
|
compileReturning(returning, ctx) {
|
|
1868
1935
|
if (!returning || returning.length === 0) return "";
|
|
1869
1936
|
const columns = this.formatReturningColumns(returning);
|
|
@@ -1969,7 +2036,7 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
|
|
|
1969
2036
|
};
|
|
1970
2037
|
|
|
1971
2038
|
// src/core/dialect/mssql/index.ts
|
|
1972
|
-
var SqlServerDialect = class extends
|
|
2039
|
+
var SqlServerDialect = class extends SqlDialectBase {
|
|
1973
2040
|
/**
|
|
1974
2041
|
* Creates a new SqlServerDialect instance
|
|
1975
2042
|
*/
|
|
@@ -2012,7 +2079,7 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2012
2079
|
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
2013
2080
|
const ctes = this.compileCtes(ast, ctx);
|
|
2014
2081
|
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
2015
|
-
const baseSelect = this.
|
|
2082
|
+
const baseSelect = this.compileSelectCoreForMssql(baseAst, ctx);
|
|
2016
2083
|
if (!hasSetOps) {
|
|
2017
2084
|
return `${ctes}${baseSelect}`;
|
|
2018
2085
|
}
|
|
@@ -2023,32 +2090,26 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2023
2090
|
const tail = pagination || orderBy;
|
|
2024
2091
|
return `${ctes}${combined}${tail}`;
|
|
2025
2092
|
}
|
|
2026
|
-
compileInsertAst(ast, ctx) {
|
|
2027
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
2028
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
2029
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
2030
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
2031
|
-
}
|
|
2032
|
-
compileUpdateAst(ast, ctx) {
|
|
2033
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
2034
|
-
const assignments = ast.set.map((assignment) => {
|
|
2035
|
-
const col2 = assignment.column;
|
|
2036
|
-
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
2037
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
2038
|
-
return `${target} = ${value}`;
|
|
2039
|
-
}).join(", ");
|
|
2040
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
2041
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
2042
|
-
}
|
|
2043
2093
|
compileDeleteAst(ast, ctx) {
|
|
2094
|
+
if (ast.using) {
|
|
2095
|
+
throw new Error("DELETE ... USING is not supported in the MSSQL dialect; use join() instead.");
|
|
2096
|
+
}
|
|
2044
2097
|
if (ast.from.type !== "Table") {
|
|
2045
2098
|
throw new Error("DELETE only supports base tables in the MSSQL dialect.");
|
|
2046
2099
|
}
|
|
2047
|
-
const
|
|
2100
|
+
const alias = ast.from.alias ?? ast.from.name;
|
|
2101
|
+
const target = this.compileTableReference(ast.from);
|
|
2102
|
+
const joins = JoinCompiler.compileJoins(
|
|
2103
|
+
ast.joins,
|
|
2104
|
+
ctx,
|
|
2105
|
+
this.compileFrom.bind(this),
|
|
2106
|
+
this.compileExpression.bind(this)
|
|
2107
|
+
);
|
|
2048
2108
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
2049
|
-
|
|
2109
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
2110
|
+
return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}${returning}`;
|
|
2050
2111
|
}
|
|
2051
|
-
|
|
2112
|
+
compileSelectCoreForMssql(ast, ctx) {
|
|
2052
2113
|
const columns = ast.columns.map((c) => {
|
|
2053
2114
|
let expr = "";
|
|
2054
2115
|
if (c.type === "Function") {
|
|
@@ -2067,9 +2128,9 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2067
2128
|
return expr;
|
|
2068
2129
|
}).join(", ");
|
|
2069
2130
|
const distinct = ast.distinct ? "DISTINCT " : "";
|
|
2070
|
-
const from = this.compileTableSource(ast.from
|
|
2131
|
+
const from = this.compileTableSource(ast.from);
|
|
2071
2132
|
const joins = ast.joins.map((j) => {
|
|
2072
|
-
const table = this.compileTableSource(j.table
|
|
2133
|
+
const table = this.compileTableSource(j.table);
|
|
2073
2134
|
const cond = this.compileExpression(j.condition, ctx);
|
|
2074
2135
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
2075
2136
|
}).join(" ");
|
|
@@ -2103,27 +2164,6 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2103
2164
|
}
|
|
2104
2165
|
return pagination;
|
|
2105
2166
|
}
|
|
2106
|
-
renderOrderByNulls(order) {
|
|
2107
|
-
return order.nulls ? ` NULLS ${order.nulls}` : "";
|
|
2108
|
-
}
|
|
2109
|
-
renderOrderByCollation(order) {
|
|
2110
|
-
return order.collation ? ` COLLATE ${order.collation}` : "";
|
|
2111
|
-
}
|
|
2112
|
-
compileTableSource(table, ctx) {
|
|
2113
|
-
if (table.type === "FunctionTable") {
|
|
2114
|
-
return FunctionTableFormatter.format(table, ctx, this);
|
|
2115
|
-
}
|
|
2116
|
-
if (table.type === "DerivedTable") {
|
|
2117
|
-
return this.compileDerivedTable(table, ctx);
|
|
2118
|
-
}
|
|
2119
|
-
const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
|
|
2120
|
-
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
2121
|
-
}
|
|
2122
|
-
compileDerivedTable(table, ctx) {
|
|
2123
|
-
const sub2 = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
|
|
2124
|
-
const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
2125
|
-
return `(${sub2}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
|
|
2126
|
-
}
|
|
2127
2167
|
compileCtes(ast, ctx) {
|
|
2128
2168
|
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
2129
2169
|
const defs = ast.ctes.map((cte) => {
|
|
@@ -2134,10 +2174,6 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2134
2174
|
}).join(", ");
|
|
2135
2175
|
return `WITH ${defs} `;
|
|
2136
2176
|
}
|
|
2137
|
-
wrapSetOperand(sql) {
|
|
2138
|
-
const trimmed = sql.trim().replace(/;$/, "");
|
|
2139
|
-
return `(${trimmed})`;
|
|
2140
|
-
}
|
|
2141
2177
|
};
|
|
2142
2178
|
|
|
2143
2179
|
// src/core/dialect/dialect-factory.ts
|
|
@@ -3932,8 +3968,10 @@ var DefaultManyToManyCollection = class {
|
|
|
3932
3968
|
attach(target) {
|
|
3933
3969
|
const entity = this.ensureEntity(target);
|
|
3934
3970
|
const id = this.extractId(entity);
|
|
3935
|
-
if (id
|
|
3936
|
-
|
|
3971
|
+
if (id != null && this.items.some((item) => this.extractId(item) === id)) {
|
|
3972
|
+
return;
|
|
3973
|
+
}
|
|
3974
|
+
if (id == null && this.items.includes(entity)) {
|
|
3937
3975
|
return;
|
|
3938
3976
|
}
|
|
3939
3977
|
this.items.push(entity);
|
|
@@ -5256,15 +5294,36 @@ var InsertQueryState = class _InsertQueryState {
|
|
|
5256
5294
|
type: "InsertQuery",
|
|
5257
5295
|
into: createTableNode(table),
|
|
5258
5296
|
columns: [],
|
|
5259
|
-
|
|
5297
|
+
source: {
|
|
5298
|
+
type: "InsertValues",
|
|
5299
|
+
rows: []
|
|
5300
|
+
}
|
|
5260
5301
|
};
|
|
5261
5302
|
}
|
|
5262
5303
|
clone(nextAst) {
|
|
5263
5304
|
return new _InsertQueryState(this.table, nextAst);
|
|
5264
5305
|
}
|
|
5306
|
+
ensureColumnsFromRow(rows) {
|
|
5307
|
+
if (this.ast.columns.length) return this.ast.columns;
|
|
5308
|
+
return buildColumnNodes(this.table, Object.keys(rows[0]));
|
|
5309
|
+
}
|
|
5310
|
+
appendValues(rows) {
|
|
5311
|
+
if (this.ast.source.type === "InsertValues") {
|
|
5312
|
+
return [...this.ast.source.rows, ...rows];
|
|
5313
|
+
}
|
|
5314
|
+
return rows;
|
|
5315
|
+
}
|
|
5316
|
+
getTableColumns() {
|
|
5317
|
+
const names = Object.keys(this.table.columns);
|
|
5318
|
+
if (!names.length) return [];
|
|
5319
|
+
return buildColumnNodes(this.table, names);
|
|
5320
|
+
}
|
|
5265
5321
|
withValues(rows) {
|
|
5266
5322
|
if (!rows.length) return this;
|
|
5267
|
-
|
|
5323
|
+
if (this.ast.source.type === "InsertSelect") {
|
|
5324
|
+
throw new Error("Cannot mix INSERT ... VALUES with INSERT ... SELECT source.");
|
|
5325
|
+
}
|
|
5326
|
+
const definedColumns = this.ensureColumnsFromRow(rows);
|
|
5268
5327
|
const newRows = rows.map(
|
|
5269
5328
|
(row, rowIndex) => definedColumns.map((column) => {
|
|
5270
5329
|
const rawValue = row[column.name];
|
|
@@ -5279,7 +5338,34 @@ var InsertQueryState = class _InsertQueryState {
|
|
|
5279
5338
|
return this.clone({
|
|
5280
5339
|
...this.ast,
|
|
5281
5340
|
columns: definedColumns,
|
|
5282
|
-
|
|
5341
|
+
source: {
|
|
5342
|
+
type: "InsertValues",
|
|
5343
|
+
rows: this.appendValues(newRows)
|
|
5344
|
+
}
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
withColumns(columns) {
|
|
5348
|
+
if (!columns.length) return this;
|
|
5349
|
+
return this.clone({
|
|
5350
|
+
...this.ast,
|
|
5351
|
+
columns: [...columns]
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5354
|
+
withSelect(query, columns) {
|
|
5355
|
+
const targetColumns = columns.length ? columns : this.ast.columns.length ? this.ast.columns : this.getTableColumns();
|
|
5356
|
+
if (!targetColumns.length) {
|
|
5357
|
+
throw new Error("INSERT ... SELECT requires specifying destination columns.");
|
|
5358
|
+
}
|
|
5359
|
+
if (this.ast.source.type === "InsertValues" && this.ast.source.rows.length) {
|
|
5360
|
+
throw new Error("Cannot mix INSERT ... SELECT with INSERT ... VALUES source.");
|
|
5361
|
+
}
|
|
5362
|
+
return this.clone({
|
|
5363
|
+
...this.ast,
|
|
5364
|
+
columns: [...targetColumns],
|
|
5365
|
+
source: {
|
|
5366
|
+
type: "InsertSelect",
|
|
5367
|
+
query
|
|
5368
|
+
}
|
|
5283
5369
|
});
|
|
5284
5370
|
}
|
|
5285
5371
|
withReturning(columns) {
|
|
@@ -5304,11 +5390,27 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
|
|
|
5304
5390
|
if (!rows.length) return this;
|
|
5305
5391
|
return this.clone(this.state.withValues(rows));
|
|
5306
5392
|
}
|
|
5393
|
+
columns(...columns) {
|
|
5394
|
+
if (!columns.length) return this;
|
|
5395
|
+
return this.clone(this.state.withColumns(this.resolveColumnNodes(columns)));
|
|
5396
|
+
}
|
|
5397
|
+
fromSelect(query, columns = []) {
|
|
5398
|
+
const ast = this.resolveSelectQuery(query);
|
|
5399
|
+
const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
|
|
5400
|
+
return this.clone(this.state.withSelect(ast, nodes));
|
|
5401
|
+
}
|
|
5307
5402
|
returning(...columns) {
|
|
5308
5403
|
if (!columns.length) return this;
|
|
5309
5404
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5310
5405
|
return this.clone(this.state.withReturning(nodes));
|
|
5311
5406
|
}
|
|
5407
|
+
// Helpers for column/AST resolution
|
|
5408
|
+
resolveColumnNodes(columns) {
|
|
5409
|
+
return columns.map((column) => buildColumnNode(this.table, column));
|
|
5410
|
+
}
|
|
5411
|
+
resolveSelectQuery(query) {
|
|
5412
|
+
return typeof query.getAST === "function" ? query.getAST() : query;
|
|
5413
|
+
}
|
|
5312
5414
|
compile(arg) {
|
|
5313
5415
|
if (typeof arg.compileInsert === "function") {
|
|
5314
5416
|
return arg.compileInsert(this.state.ast);
|
|
@@ -5342,7 +5444,8 @@ var UpdateQueryState = class _UpdateQueryState {
|
|
|
5342
5444
|
this.ast = ast ?? {
|
|
5343
5445
|
type: "UpdateQuery",
|
|
5344
5446
|
table: createTableNode(table),
|
|
5345
|
-
set: []
|
|
5447
|
+
set: [],
|
|
5448
|
+
joins: []
|
|
5346
5449
|
};
|
|
5347
5450
|
}
|
|
5348
5451
|
clone(nextAst) {
|
|
@@ -5381,6 +5484,27 @@ var UpdateQueryState = class _UpdateQueryState {
|
|
|
5381
5484
|
returning: [...columns]
|
|
5382
5485
|
});
|
|
5383
5486
|
}
|
|
5487
|
+
withFrom(from) {
|
|
5488
|
+
return this.clone({
|
|
5489
|
+
...this.ast,
|
|
5490
|
+
from
|
|
5491
|
+
});
|
|
5492
|
+
}
|
|
5493
|
+
withJoin(join) {
|
|
5494
|
+
return this.clone({
|
|
5495
|
+
...this.ast,
|
|
5496
|
+
joins: [...this.ast.joins ?? [], join]
|
|
5497
|
+
});
|
|
5498
|
+
}
|
|
5499
|
+
withTableAlias(alias) {
|
|
5500
|
+
return this.clone({
|
|
5501
|
+
...this.ast,
|
|
5502
|
+
table: {
|
|
5503
|
+
...this.ast.table,
|
|
5504
|
+
alias
|
|
5505
|
+
}
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5384
5508
|
};
|
|
5385
5509
|
|
|
5386
5510
|
// src/query-builder/update.ts
|
|
@@ -5392,6 +5516,18 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5392
5516
|
clone(state) {
|
|
5393
5517
|
return new _UpdateQueryBuilder(this.table, state);
|
|
5394
5518
|
}
|
|
5519
|
+
as(alias) {
|
|
5520
|
+
return this.clone(this.state.withTableAlias(alias));
|
|
5521
|
+
}
|
|
5522
|
+
from(source) {
|
|
5523
|
+
const tableSource = this.resolveTableSource(source);
|
|
5524
|
+
return this.clone(this.state.withFrom(tableSource));
|
|
5525
|
+
}
|
|
5526
|
+
join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
|
|
5527
|
+
const joinTarget = this.resolveJoinTarget(table);
|
|
5528
|
+
const joinNode = createJoinNode(kind, joinTarget, condition, relationName);
|
|
5529
|
+
return this.clone(this.state.withJoin(joinNode));
|
|
5530
|
+
}
|
|
5395
5531
|
set(values) {
|
|
5396
5532
|
return this.clone(this.state.withSet(values));
|
|
5397
5533
|
}
|
|
@@ -5403,6 +5539,16 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5403
5539
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5404
5540
|
return this.clone(this.state.withReturning(nodes));
|
|
5405
5541
|
}
|
|
5542
|
+
resolveTableSource(source) {
|
|
5543
|
+
if (isTableSourceNode(source)) {
|
|
5544
|
+
return source;
|
|
5545
|
+
}
|
|
5546
|
+
return { type: "Table", name: source.name, schema: source.schema };
|
|
5547
|
+
}
|
|
5548
|
+
resolveJoinTarget(table) {
|
|
5549
|
+
if (typeof table === "string") return table;
|
|
5550
|
+
return this.resolveTableSource(table);
|
|
5551
|
+
}
|
|
5406
5552
|
compile(arg) {
|
|
5407
5553
|
if (typeof arg.compileUpdate === "function") {
|
|
5408
5554
|
return arg.compileUpdate(this.state.ast);
|
|
@@ -5417,6 +5563,7 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5417
5563
|
return this.state.ast;
|
|
5418
5564
|
}
|
|
5419
5565
|
};
|
|
5566
|
+
var isTableSourceNode = (source) => typeof source.type === "string";
|
|
5420
5567
|
|
|
5421
5568
|
// src/query-builder/delete-query-state.ts
|
|
5422
5569
|
var DeleteQueryState = class _DeleteQueryState {
|
|
@@ -5424,7 +5571,8 @@ var DeleteQueryState = class _DeleteQueryState {
|
|
|
5424
5571
|
this.table = table;
|
|
5425
5572
|
this.ast = ast ?? {
|
|
5426
5573
|
type: "DeleteQuery",
|
|
5427
|
-
from: createTableNode(table)
|
|
5574
|
+
from: createTableNode(table),
|
|
5575
|
+
joins: []
|
|
5428
5576
|
};
|
|
5429
5577
|
}
|
|
5430
5578
|
clone(nextAst) {
|
|
@@ -5442,6 +5590,27 @@ var DeleteQueryState = class _DeleteQueryState {
|
|
|
5442
5590
|
returning: [...columns]
|
|
5443
5591
|
});
|
|
5444
5592
|
}
|
|
5593
|
+
withUsing(source) {
|
|
5594
|
+
return this.clone({
|
|
5595
|
+
...this.ast,
|
|
5596
|
+
using: source
|
|
5597
|
+
});
|
|
5598
|
+
}
|
|
5599
|
+
withJoin(join) {
|
|
5600
|
+
return this.clone({
|
|
5601
|
+
...this.ast,
|
|
5602
|
+
joins: [...this.ast.joins ?? [], join]
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
withTableAlias(alias) {
|
|
5606
|
+
return this.clone({
|
|
5607
|
+
...this.ast,
|
|
5608
|
+
from: {
|
|
5609
|
+
...this.ast.from,
|
|
5610
|
+
alias
|
|
5611
|
+
}
|
|
5612
|
+
});
|
|
5613
|
+
}
|
|
5445
5614
|
};
|
|
5446
5615
|
|
|
5447
5616
|
// src/query-builder/delete.ts
|
|
@@ -5456,11 +5625,32 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
|
5456
5625
|
where(expr) {
|
|
5457
5626
|
return this.clone(this.state.withWhere(expr));
|
|
5458
5627
|
}
|
|
5628
|
+
as(alias) {
|
|
5629
|
+
return this.clone(this.state.withTableAlias(alias));
|
|
5630
|
+
}
|
|
5631
|
+
using(source) {
|
|
5632
|
+
return this.clone(this.state.withUsing(this.resolveTableSource(source)));
|
|
5633
|
+
}
|
|
5634
|
+
join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
|
|
5635
|
+
const target = this.resolveJoinTarget(table);
|
|
5636
|
+
const joinNode = createJoinNode(kind, target, condition, relationName);
|
|
5637
|
+
return this.clone(this.state.withJoin(joinNode));
|
|
5638
|
+
}
|
|
5459
5639
|
returning(...columns) {
|
|
5460
5640
|
if (!columns.length) return this;
|
|
5461
5641
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5462
5642
|
return this.clone(this.state.withReturning(nodes));
|
|
5463
5643
|
}
|
|
5644
|
+
resolveTableSource(source) {
|
|
5645
|
+
if (isTableSourceNode2(source)) {
|
|
5646
|
+
return source;
|
|
5647
|
+
}
|
|
5648
|
+
return { type: "Table", name: source.name, schema: source.schema };
|
|
5649
|
+
}
|
|
5650
|
+
resolveJoinTarget(table) {
|
|
5651
|
+
if (typeof table === "string") return table;
|
|
5652
|
+
return this.resolveTableSource(table);
|
|
5653
|
+
}
|
|
5464
5654
|
compile(arg) {
|
|
5465
5655
|
if (typeof arg.compileDelete === "function") {
|
|
5466
5656
|
return arg.compileDelete(this.state.ast);
|
|
@@ -5475,6 +5665,7 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
|
5475
5665
|
return this.state.ast;
|
|
5476
5666
|
}
|
|
5477
5667
|
};
|
|
5668
|
+
var isTableSourceNode2 = (source) => typeof source.type === "string";
|
|
5478
5669
|
|
|
5479
5670
|
// src/core/ddl/sql-writing.ts
|
|
5480
5671
|
var resolvePrimaryKey = (table) => {
|
|
@@ -6823,9 +7014,13 @@ var TypeScriptGenerator = class {
|
|
|
6823
7014
|
*/
|
|
6824
7015
|
printInExpression(inExpr) {
|
|
6825
7016
|
const left2 = this.printOperand(inExpr.left);
|
|
6826
|
-
const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
|
|
6827
7017
|
const fn4 = this.mapOp(inExpr.operator);
|
|
6828
|
-
|
|
7018
|
+
if (Array.isArray(inExpr.right)) {
|
|
7019
|
+
const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
|
|
7020
|
+
return `${fn4}(${left2}, [${values}])`;
|
|
7021
|
+
}
|
|
7022
|
+
const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
|
|
7023
|
+
return `${fn4}(${left2}, (${subquery}))`;
|
|
6829
7024
|
}
|
|
6830
7025
|
/**
|
|
6831
7026
|
* Prints a null expression to TypeScript code
|
|
@@ -7010,6 +7205,13 @@ var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
|
|
|
7010
7205
|
|
|
7011
7206
|
// src/orm/unit-of-work.ts
|
|
7012
7207
|
var UnitOfWork = class {
|
|
7208
|
+
/**
|
|
7209
|
+
* Creates a new UnitOfWork instance.
|
|
7210
|
+
* @param dialect - The database dialect
|
|
7211
|
+
* @param executor - The database executor
|
|
7212
|
+
* @param identityMap - The identity map
|
|
7213
|
+
* @param hookContext - Function to get the hook context
|
|
7214
|
+
*/
|
|
7013
7215
|
constructor(dialect, executor, identityMap, hookContext) {
|
|
7014
7216
|
this.dialect = dialect;
|
|
7015
7217
|
this.executor = executor;
|
|
@@ -7017,21 +7219,50 @@ var UnitOfWork = class {
|
|
|
7017
7219
|
this.hookContext = hookContext;
|
|
7018
7220
|
this.trackedEntities = /* @__PURE__ */ new Map();
|
|
7019
7221
|
}
|
|
7222
|
+
/**
|
|
7223
|
+
* Gets the identity buckets map.
|
|
7224
|
+
*/
|
|
7020
7225
|
get identityBuckets() {
|
|
7021
7226
|
return this.identityMap.bucketsMap;
|
|
7022
7227
|
}
|
|
7228
|
+
/**
|
|
7229
|
+
* Gets all tracked entities.
|
|
7230
|
+
* @returns Array of tracked entities
|
|
7231
|
+
*/
|
|
7023
7232
|
getTracked() {
|
|
7024
7233
|
return Array.from(this.trackedEntities.values());
|
|
7025
7234
|
}
|
|
7235
|
+
/**
|
|
7236
|
+
* Gets an entity by table and primary key.
|
|
7237
|
+
* @param table - The table definition
|
|
7238
|
+
* @param pk - The primary key value
|
|
7239
|
+
* @returns The entity or undefined if not found
|
|
7240
|
+
*/
|
|
7026
7241
|
getEntity(table, pk) {
|
|
7027
7242
|
return this.identityMap.getEntity(table, pk);
|
|
7028
7243
|
}
|
|
7244
|
+
/**
|
|
7245
|
+
* Gets all tracked entities for a specific table.
|
|
7246
|
+
* @param table - The table definition
|
|
7247
|
+
* @returns Array of tracked entities
|
|
7248
|
+
*/
|
|
7029
7249
|
getEntitiesForTable(table) {
|
|
7030
7250
|
return this.identityMap.getEntitiesForTable(table);
|
|
7031
7251
|
}
|
|
7252
|
+
/**
|
|
7253
|
+
* Finds a tracked entity.
|
|
7254
|
+
* @param entity - The entity to find
|
|
7255
|
+
* @returns The tracked entity or undefined if not found
|
|
7256
|
+
*/
|
|
7032
7257
|
findTracked(entity) {
|
|
7033
7258
|
return this.trackedEntities.get(entity);
|
|
7034
7259
|
}
|
|
7260
|
+
/**
|
|
7261
|
+
* Sets an entity in the identity map.
|
|
7262
|
+
* @param table - The table definition
|
|
7263
|
+
* @param pk - The primary key value
|
|
7264
|
+
* @param entity - The entity instance
|
|
7265
|
+
*/
|
|
7035
7266
|
setEntity(table, pk, entity) {
|
|
7036
7267
|
if (pk === null || pk === void 0) return;
|
|
7037
7268
|
let tracked = this.trackedEntities.get(entity);
|
|
@@ -7049,6 +7280,12 @@ var UnitOfWork = class {
|
|
|
7049
7280
|
}
|
|
7050
7281
|
this.registerIdentity(tracked);
|
|
7051
7282
|
}
|
|
7283
|
+
/**
|
|
7284
|
+
* Tracks a new entity.
|
|
7285
|
+
* @param table - The table definition
|
|
7286
|
+
* @param entity - The entity instance
|
|
7287
|
+
* @param pk - Optional primary key value
|
|
7288
|
+
*/
|
|
7052
7289
|
trackNew(table, entity, pk) {
|
|
7053
7290
|
const tracked = {
|
|
7054
7291
|
table,
|
|
@@ -7062,6 +7299,12 @@ var UnitOfWork = class {
|
|
|
7062
7299
|
this.registerIdentity(tracked);
|
|
7063
7300
|
}
|
|
7064
7301
|
}
|
|
7302
|
+
/**
|
|
7303
|
+
* Tracks a managed entity.
|
|
7304
|
+
* @param table - The table definition
|
|
7305
|
+
* @param pk - The primary key value
|
|
7306
|
+
* @param entity - The entity instance
|
|
7307
|
+
*/
|
|
7065
7308
|
trackManaged(table, pk, entity) {
|
|
7066
7309
|
const tracked = {
|
|
7067
7310
|
table,
|
|
@@ -7073,17 +7316,28 @@ var UnitOfWork = class {
|
|
|
7073
7316
|
this.trackedEntities.set(entity, tracked);
|
|
7074
7317
|
this.registerIdentity(tracked);
|
|
7075
7318
|
}
|
|
7319
|
+
/**
|
|
7320
|
+
* Marks an entity as dirty (modified).
|
|
7321
|
+
* @param entity - The entity to mark as dirty
|
|
7322
|
+
*/
|
|
7076
7323
|
markDirty(entity) {
|
|
7077
7324
|
const tracked = this.trackedEntities.get(entity);
|
|
7078
7325
|
if (!tracked) return;
|
|
7079
7326
|
if (tracked.status === "new" /* New */ || tracked.status === "removed" /* Removed */) return;
|
|
7080
7327
|
tracked.status = "dirty" /* Dirty */;
|
|
7081
7328
|
}
|
|
7329
|
+
/**
|
|
7330
|
+
* Marks an entity as removed.
|
|
7331
|
+
* @param entity - The entity to mark as removed
|
|
7332
|
+
*/
|
|
7082
7333
|
markRemoved(entity) {
|
|
7083
7334
|
const tracked = this.trackedEntities.get(entity);
|
|
7084
7335
|
if (!tracked) return;
|
|
7085
7336
|
tracked.status = "removed" /* Removed */;
|
|
7086
7337
|
}
|
|
7338
|
+
/**
|
|
7339
|
+
* Flushes pending changes to the database.
|
|
7340
|
+
*/
|
|
7087
7341
|
async flush() {
|
|
7088
7342
|
const toFlush = Array.from(this.trackedEntities.values());
|
|
7089
7343
|
for (const tracked of toFlush) {
|
|
@@ -7102,10 +7356,17 @@ var UnitOfWork = class {
|
|
|
7102
7356
|
}
|
|
7103
7357
|
}
|
|
7104
7358
|
}
|
|
7359
|
+
/**
|
|
7360
|
+
* Resets the unit of work by clearing all tracked entities and identity map.
|
|
7361
|
+
*/
|
|
7105
7362
|
reset() {
|
|
7106
7363
|
this.trackedEntities.clear();
|
|
7107
7364
|
this.identityMap.clear();
|
|
7108
7365
|
}
|
|
7366
|
+
/**
|
|
7367
|
+
* Flushes an insert operation for a new entity.
|
|
7368
|
+
* @param tracked - The tracked entity to insert
|
|
7369
|
+
*/
|
|
7109
7370
|
async flushInsert(tracked) {
|
|
7110
7371
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
7111
7372
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
@@ -7122,6 +7383,10 @@ var UnitOfWork = class {
|
|
|
7122
7383
|
this.registerIdentity(tracked);
|
|
7123
7384
|
await this.runHook(tracked.table.hooks?.afterInsert, tracked);
|
|
7124
7385
|
}
|
|
7386
|
+
/**
|
|
7387
|
+
* Flushes an update operation for a modified entity.
|
|
7388
|
+
* @param tracked - The tracked entity to update
|
|
7389
|
+
*/
|
|
7125
7390
|
async flushUpdate(tracked) {
|
|
7126
7391
|
if (tracked.pk == null) return;
|
|
7127
7392
|
const changes = this.computeChanges(tracked);
|
|
@@ -7144,6 +7409,10 @@ var UnitOfWork = class {
|
|
|
7144
7409
|
this.registerIdentity(tracked);
|
|
7145
7410
|
await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
|
|
7146
7411
|
}
|
|
7412
|
+
/**
|
|
7413
|
+
* Flushes a delete operation for a removed entity.
|
|
7414
|
+
* @param tracked - The tracked entity to delete
|
|
7415
|
+
*/
|
|
7147
7416
|
async flushDelete(tracked) {
|
|
7148
7417
|
if (tracked.pk == null) return;
|
|
7149
7418
|
await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
|
|
@@ -7157,10 +7426,20 @@ var UnitOfWork = class {
|
|
|
7157
7426
|
this.identityMap.remove(tracked);
|
|
7158
7427
|
await this.runHook(tracked.table.hooks?.afterDelete, tracked);
|
|
7159
7428
|
}
|
|
7429
|
+
/**
|
|
7430
|
+
* Runs a table hook if defined.
|
|
7431
|
+
* @param hook - The hook function
|
|
7432
|
+
* @param tracked - The tracked entity
|
|
7433
|
+
*/
|
|
7160
7434
|
async runHook(hook, tracked) {
|
|
7161
7435
|
if (!hook) return;
|
|
7162
7436
|
await hook(this.hookContext(), tracked.entity);
|
|
7163
7437
|
}
|
|
7438
|
+
/**
|
|
7439
|
+
* Computes changes between current entity state and original snapshot.
|
|
7440
|
+
* @param tracked - The tracked entity
|
|
7441
|
+
* @returns Object with changed column values
|
|
7442
|
+
*/
|
|
7164
7443
|
computeChanges(tracked) {
|
|
7165
7444
|
const snapshot = tracked.original ?? {};
|
|
7166
7445
|
const changes = {};
|
|
@@ -7172,6 +7451,12 @@ var UnitOfWork = class {
|
|
|
7172
7451
|
}
|
|
7173
7452
|
return changes;
|
|
7174
7453
|
}
|
|
7454
|
+
/**
|
|
7455
|
+
* Extracts column values from an entity.
|
|
7456
|
+
* @param table - The table definition
|
|
7457
|
+
* @param entity - The entity instance
|
|
7458
|
+
* @returns Object with column values
|
|
7459
|
+
*/
|
|
7175
7460
|
extractColumns(table, entity) {
|
|
7176
7461
|
const payload = {};
|
|
7177
7462
|
for (const column of Object.keys(table.columns)) {
|
|
@@ -7180,9 +7465,19 @@ var UnitOfWork = class {
|
|
|
7180
7465
|
}
|
|
7181
7466
|
return payload;
|
|
7182
7467
|
}
|
|
7468
|
+
/**
|
|
7469
|
+
* Executes a compiled query.
|
|
7470
|
+
* @param compiled - The compiled query
|
|
7471
|
+
* @returns Query results
|
|
7472
|
+
*/
|
|
7183
7473
|
async executeCompiled(compiled) {
|
|
7184
7474
|
return this.executor.executeSql(compiled.sql, compiled.params);
|
|
7185
7475
|
}
|
|
7476
|
+
/**
|
|
7477
|
+
* Gets columns for RETURNING clause.
|
|
7478
|
+
* @param table - The table definition
|
|
7479
|
+
* @returns Array of column nodes
|
|
7480
|
+
*/
|
|
7186
7481
|
getReturningColumns(table) {
|
|
7187
7482
|
return Object.values(table.columns).map((column) => ({
|
|
7188
7483
|
type: "Column",
|
|
@@ -7191,6 +7486,11 @@ var UnitOfWork = class {
|
|
|
7191
7486
|
alias: column.name
|
|
7192
7487
|
}));
|
|
7193
7488
|
}
|
|
7489
|
+
/**
|
|
7490
|
+
* Applies RETURNING clause results to the tracked entity.
|
|
7491
|
+
* @param tracked - The tracked entity
|
|
7492
|
+
* @param results - Query results
|
|
7493
|
+
*/
|
|
7194
7494
|
applyReturningResults(tracked, results) {
|
|
7195
7495
|
if (!this.dialect.supportsReturning()) return;
|
|
7196
7496
|
const first = results[0];
|
|
@@ -7202,15 +7502,30 @@ var UnitOfWork = class {
|
|
|
7202
7502
|
tracked.entity[columnName] = row[i];
|
|
7203
7503
|
}
|
|
7204
7504
|
}
|
|
7505
|
+
/**
|
|
7506
|
+
* Normalizes a column name by removing quotes and table prefixes.
|
|
7507
|
+
* @param column - The column name to normalize
|
|
7508
|
+
* @returns Normalized column name
|
|
7509
|
+
*/
|
|
7205
7510
|
normalizeColumnName(column) {
|
|
7206
7511
|
const parts = column.split(".");
|
|
7207
7512
|
const candidate = parts[parts.length - 1];
|
|
7208
7513
|
return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
|
|
7209
7514
|
}
|
|
7515
|
+
/**
|
|
7516
|
+
* Registers an entity in the identity map.
|
|
7517
|
+
* @param tracked - The tracked entity to register
|
|
7518
|
+
*/
|
|
7210
7519
|
registerIdentity(tracked) {
|
|
7211
7520
|
if (tracked.pk == null) return;
|
|
7212
7521
|
this.identityMap.register(tracked);
|
|
7213
7522
|
}
|
|
7523
|
+
/**
|
|
7524
|
+
* Creates a snapshot of an entity's current state.
|
|
7525
|
+
* @param table - The table definition
|
|
7526
|
+
* @param entity - The entity instance
|
|
7527
|
+
* @returns Object with entity state
|
|
7528
|
+
*/
|
|
7214
7529
|
createSnapshot(table, entity) {
|
|
7215
7530
|
const snapshot = {};
|
|
7216
7531
|
for (const column of Object.keys(table.columns)) {
|
|
@@ -7218,6 +7533,11 @@ var UnitOfWork = class {
|
|
|
7218
7533
|
}
|
|
7219
7534
|
return snapshot;
|
|
7220
7535
|
}
|
|
7536
|
+
/**
|
|
7537
|
+
* Gets the primary key value from a tracked entity.
|
|
7538
|
+
* @param tracked - The tracked entity
|
|
7539
|
+
* @returns Primary key value or null
|
|
7540
|
+
*/
|
|
7221
7541
|
getPrimaryKeyValue(tracked) {
|
|
7222
7542
|
const key = findPrimaryKey(tracked.table);
|
|
7223
7543
|
const val = tracked.entity[key];
|
|
@@ -7228,6 +7548,10 @@ var UnitOfWork = class {
|
|
|
7228
7548
|
|
|
7229
7549
|
// src/orm/domain-event-bus.ts
|
|
7230
7550
|
var DomainEventBus = class {
|
|
7551
|
+
/**
|
|
7552
|
+
* Creates a new DomainEventBus instance.
|
|
7553
|
+
* @param initialHandlers - Optional initial event handlers
|
|
7554
|
+
*/
|
|
7231
7555
|
constructor(initialHandlers) {
|
|
7232
7556
|
this.handlers = /* @__PURE__ */ new Map();
|
|
7233
7557
|
if (initialHandlers) {
|
|
@@ -7238,15 +7562,32 @@ var DomainEventBus = class {
|
|
|
7238
7562
|
}
|
|
7239
7563
|
}
|
|
7240
7564
|
}
|
|
7565
|
+
/**
|
|
7566
|
+
* Registers an event handler for a specific event type.
|
|
7567
|
+
* @template TType - The event type
|
|
7568
|
+
* @param type - The event type
|
|
7569
|
+
* @param handler - The event handler
|
|
7570
|
+
*/
|
|
7241
7571
|
on(type, handler) {
|
|
7242
7572
|
const key = type;
|
|
7243
7573
|
const existing = this.handlers.get(key) ?? [];
|
|
7244
7574
|
existing.push(handler);
|
|
7245
7575
|
this.handlers.set(key, existing);
|
|
7246
7576
|
}
|
|
7577
|
+
/**
|
|
7578
|
+
* Registers an event handler for a specific event type (alias for on).
|
|
7579
|
+
* @template TType - The event type
|
|
7580
|
+
* @param type - The event type
|
|
7581
|
+
* @param handler - The event handler
|
|
7582
|
+
*/
|
|
7247
7583
|
register(type, handler) {
|
|
7248
7584
|
this.on(type, handler);
|
|
7249
7585
|
}
|
|
7586
|
+
/**
|
|
7587
|
+
* Dispatches domain events for tracked entities.
|
|
7588
|
+
* @param trackedEntities - Iterable of tracked entities
|
|
7589
|
+
* @param ctx - The context to pass to handlers
|
|
7590
|
+
*/
|
|
7250
7591
|
async dispatch(trackedEntities, ctx) {
|
|
7251
7592
|
for (const tracked of trackedEntities) {
|
|
7252
7593
|
const entity = tracked.entity;
|
|
@@ -7271,18 +7612,34 @@ var addDomainEvent = (entity, event) => {
|
|
|
7271
7612
|
|
|
7272
7613
|
// src/orm/relation-change-processor.ts
|
|
7273
7614
|
var RelationChangeProcessor = class {
|
|
7615
|
+
/**
|
|
7616
|
+
* Creates a new RelationChangeProcessor instance.
|
|
7617
|
+
* @param unitOfWork - The unit of work instance
|
|
7618
|
+
* @param dialect - The database dialect
|
|
7619
|
+
* @param executor - The database executor
|
|
7620
|
+
*/
|
|
7274
7621
|
constructor(unitOfWork, dialect, executor) {
|
|
7275
7622
|
this.unitOfWork = unitOfWork;
|
|
7276
7623
|
this.dialect = dialect;
|
|
7277
7624
|
this.executor = executor;
|
|
7278
7625
|
this.relationChanges = [];
|
|
7279
7626
|
}
|
|
7627
|
+
/**
|
|
7628
|
+
* Registers a relation change for processing.
|
|
7629
|
+
* @param entry - The relation change entry
|
|
7630
|
+
*/
|
|
7280
7631
|
registerChange(entry) {
|
|
7281
7632
|
this.relationChanges.push(entry);
|
|
7282
7633
|
}
|
|
7634
|
+
/**
|
|
7635
|
+
* Resets the relation change processor by clearing all pending changes.
|
|
7636
|
+
*/
|
|
7283
7637
|
reset() {
|
|
7284
7638
|
this.relationChanges.length = 0;
|
|
7285
7639
|
}
|
|
7640
|
+
/**
|
|
7641
|
+
* Processes all pending relation changes.
|
|
7642
|
+
*/
|
|
7286
7643
|
async process() {
|
|
7287
7644
|
if (!this.relationChanges.length) return;
|
|
7288
7645
|
const entries = [...this.relationChanges];
|
|
@@ -7304,6 +7661,10 @@ var RelationChangeProcessor = class {
|
|
|
7304
7661
|
}
|
|
7305
7662
|
}
|
|
7306
7663
|
}
|
|
7664
|
+
/**
|
|
7665
|
+
* Handles changes for has-many relations.
|
|
7666
|
+
* @param entry - The relation change entry
|
|
7667
|
+
*/
|
|
7307
7668
|
async handleHasManyChange(entry) {
|
|
7308
7669
|
const relation = entry.relation;
|
|
7309
7670
|
const target = entry.change.entity;
|
|
@@ -7322,6 +7683,10 @@ var RelationChangeProcessor = class {
|
|
|
7322
7683
|
this.detachHasManyChild(tracked.entity, relation);
|
|
7323
7684
|
}
|
|
7324
7685
|
}
|
|
7686
|
+
/**
|
|
7687
|
+
* Handles changes for has-one relations.
|
|
7688
|
+
* @param entry - The relation change entry
|
|
7689
|
+
*/
|
|
7325
7690
|
async handleHasOneChange(entry) {
|
|
7326
7691
|
const relation = entry.relation;
|
|
7327
7692
|
const target = entry.change.entity;
|
|
@@ -7340,8 +7705,16 @@ var RelationChangeProcessor = class {
|
|
|
7340
7705
|
this.detachHasOneChild(tracked.entity, relation);
|
|
7341
7706
|
}
|
|
7342
7707
|
}
|
|
7708
|
+
/**
|
|
7709
|
+
* Handles changes for belongs-to relations.
|
|
7710
|
+
* @param _entry - The relation change entry (reserved for future use)
|
|
7711
|
+
*/
|
|
7343
7712
|
async handleBelongsToChange(_entry) {
|
|
7344
7713
|
}
|
|
7714
|
+
/**
|
|
7715
|
+
* Handles changes for belongs-to-many relations.
|
|
7716
|
+
* @param entry - The relation change entry
|
|
7717
|
+
*/
|
|
7345
7718
|
async handleBelongsToManyChange(entry) {
|
|
7346
7719
|
const relation = entry.relation;
|
|
7347
7720
|
const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
|
|
@@ -7360,11 +7733,22 @@ var RelationChangeProcessor = class {
|
|
|
7360
7733
|
}
|
|
7361
7734
|
}
|
|
7362
7735
|
}
|
|
7736
|
+
/**
|
|
7737
|
+
* Assigns a foreign key for has-many relations.
|
|
7738
|
+
* @param child - The child entity
|
|
7739
|
+
* @param relation - The has-many relation
|
|
7740
|
+
* @param rootValue - The root entity's primary key value
|
|
7741
|
+
*/
|
|
7363
7742
|
assignHasManyForeignKey(child, relation, rootValue) {
|
|
7364
7743
|
const current = child[relation.foreignKey];
|
|
7365
7744
|
if (current === rootValue) return;
|
|
7366
7745
|
child[relation.foreignKey] = rootValue;
|
|
7367
7746
|
}
|
|
7747
|
+
/**
|
|
7748
|
+
* Detaches a child entity from has-many relations.
|
|
7749
|
+
* @param child - The child entity
|
|
7750
|
+
* @param relation - The has-many relation
|
|
7751
|
+
*/
|
|
7368
7752
|
detachHasManyChild(child, relation) {
|
|
7369
7753
|
if (relation.cascade === "all" || relation.cascade === "remove") {
|
|
7370
7754
|
this.unitOfWork.markRemoved(child);
|
|
@@ -7373,11 +7757,22 @@ var RelationChangeProcessor = class {
|
|
|
7373
7757
|
child[relation.foreignKey] = null;
|
|
7374
7758
|
this.unitOfWork.markDirty(child);
|
|
7375
7759
|
}
|
|
7760
|
+
/**
|
|
7761
|
+
* Assigns a foreign key for has-one relations.
|
|
7762
|
+
* @param child - The child entity
|
|
7763
|
+
* @param relation - The has-one relation
|
|
7764
|
+
* @param rootValue - The root entity's primary key value
|
|
7765
|
+
*/
|
|
7376
7766
|
assignHasOneForeignKey(child, relation, rootValue) {
|
|
7377
7767
|
const current = child[relation.foreignKey];
|
|
7378
7768
|
if (current === rootValue) return;
|
|
7379
7769
|
child[relation.foreignKey] = rootValue;
|
|
7380
7770
|
}
|
|
7771
|
+
/**
|
|
7772
|
+
* Detaches a child entity from has-one relations.
|
|
7773
|
+
* @param child - The child entity
|
|
7774
|
+
* @param relation - The has-one relation
|
|
7775
|
+
*/
|
|
7381
7776
|
detachHasOneChild(child, relation) {
|
|
7382
7777
|
if (relation.cascade === "all" || relation.cascade === "remove") {
|
|
7383
7778
|
this.unitOfWork.markRemoved(child);
|
|
@@ -7386,6 +7781,12 @@ var RelationChangeProcessor = class {
|
|
|
7386
7781
|
child[relation.foreignKey] = null;
|
|
7387
7782
|
this.unitOfWork.markDirty(child);
|
|
7388
7783
|
}
|
|
7784
|
+
/**
|
|
7785
|
+
* Inserts a pivot row for belongs-to-many relations.
|
|
7786
|
+
* @param relation - The belongs-to-many relation
|
|
7787
|
+
* @param rootId - The root entity's primary key value
|
|
7788
|
+
* @param targetId - The target entity's primary key value
|
|
7789
|
+
*/
|
|
7389
7790
|
async insertPivotRow(relation, rootId, targetId) {
|
|
7390
7791
|
const payload = {
|
|
7391
7792
|
[relation.pivotForeignKeyToRoot]: rootId,
|
|
@@ -7395,6 +7796,12 @@ var RelationChangeProcessor = class {
|
|
|
7395
7796
|
const compiled = builder.compile(this.dialect);
|
|
7396
7797
|
await this.executor.executeSql(compiled.sql, compiled.params);
|
|
7397
7798
|
}
|
|
7799
|
+
/**
|
|
7800
|
+
* Deletes a pivot row for belongs-to-many relations.
|
|
7801
|
+
* @param relation - The belongs-to-many relation
|
|
7802
|
+
* @param rootId - The root entity's primary key value
|
|
7803
|
+
* @param targetId - The target entity's primary key value
|
|
7804
|
+
*/
|
|
7398
7805
|
async deletePivotRow(relation, rootId, targetId) {
|
|
7399
7806
|
const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
|
|
7400
7807
|
const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
|
|
@@ -7405,6 +7812,12 @@ var RelationChangeProcessor = class {
|
|
|
7405
7812
|
const compiled = builder.compile(this.dialect);
|
|
7406
7813
|
await this.executor.executeSql(compiled.sql, compiled.params);
|
|
7407
7814
|
}
|
|
7815
|
+
/**
|
|
7816
|
+
* Resolves the primary key value from an entity.
|
|
7817
|
+
* @param entity - The entity
|
|
7818
|
+
* @param table - The table definition
|
|
7819
|
+
* @returns The primary key value or null
|
|
7820
|
+
*/
|
|
7408
7821
|
resolvePrimaryKeyValue(entity, table) {
|
|
7409
7822
|
if (!entity) return null;
|
|
7410
7823
|
const key = findPrimaryKey(table);
|
|
@@ -7420,42 +7833,231 @@ var createQueryLoggingExecutor = (executor, logger) => {
|
|
|
7420
7833
|
return executor;
|
|
7421
7834
|
}
|
|
7422
7835
|
const wrapped = {
|
|
7836
|
+
capabilities: executor.capabilities,
|
|
7423
7837
|
async executeSql(sql, params) {
|
|
7424
7838
|
logger({ sql, params });
|
|
7425
7839
|
return executor.executeSql(sql, params);
|
|
7426
|
-
}
|
|
7840
|
+
},
|
|
7841
|
+
beginTransaction: () => executor.beginTransaction(),
|
|
7842
|
+
commitTransaction: () => executor.commitTransaction(),
|
|
7843
|
+
rollbackTransaction: () => executor.rollbackTransaction(),
|
|
7844
|
+
dispose: () => executor.dispose()
|
|
7427
7845
|
};
|
|
7428
|
-
if (executor.beginTransaction) {
|
|
7429
|
-
wrapped.beginTransaction = executor.beginTransaction.bind(executor);
|
|
7430
|
-
}
|
|
7431
|
-
if (executor.commitTransaction) {
|
|
7432
|
-
wrapped.commitTransaction = executor.commitTransaction.bind(executor);
|
|
7433
|
-
}
|
|
7434
|
-
if (executor.rollbackTransaction) {
|
|
7435
|
-
wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
|
|
7436
|
-
}
|
|
7437
7846
|
return wrapped;
|
|
7438
7847
|
};
|
|
7439
7848
|
|
|
7440
7849
|
// src/orm/transaction-runner.ts
|
|
7441
7850
|
var runInTransaction = async (executor, action) => {
|
|
7442
|
-
if (!executor.
|
|
7851
|
+
if (!executor.capabilities.transactions) {
|
|
7443
7852
|
await action();
|
|
7444
7853
|
return;
|
|
7445
7854
|
}
|
|
7446
7855
|
await executor.beginTransaction();
|
|
7447
7856
|
try {
|
|
7448
7857
|
await action();
|
|
7449
|
-
await executor.commitTransaction
|
|
7858
|
+
await executor.commitTransaction();
|
|
7450
7859
|
} catch (error) {
|
|
7451
|
-
await executor.rollbackTransaction
|
|
7860
|
+
await executor.rollbackTransaction();
|
|
7452
7861
|
throw error;
|
|
7453
7862
|
}
|
|
7454
7863
|
};
|
|
7455
7864
|
|
|
7865
|
+
// src/orm/save-graph.ts
|
|
7866
|
+
var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
7867
|
+
var pickColumns = (table, payload) => {
|
|
7868
|
+
const columns = {};
|
|
7869
|
+
for (const key of Object.keys(table.columns)) {
|
|
7870
|
+
if (payload[key] !== void 0) {
|
|
7871
|
+
columns[key] = payload[key];
|
|
7872
|
+
}
|
|
7873
|
+
}
|
|
7874
|
+
return columns;
|
|
7875
|
+
};
|
|
7876
|
+
var ensureEntity = (session, table, payload) => {
|
|
7877
|
+
const pk = findPrimaryKey(table);
|
|
7878
|
+
const row = pickColumns(table, payload);
|
|
7879
|
+
const pkValue = payload[pk];
|
|
7880
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
7881
|
+
const tracked = session.getEntity(table, pkValue);
|
|
7882
|
+
if (tracked) {
|
|
7883
|
+
return tracked;
|
|
7884
|
+
}
|
|
7885
|
+
if (row[pk] === void 0) {
|
|
7886
|
+
row[pk] = pkValue;
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
return createEntityFromRow(session, table, row);
|
|
7890
|
+
};
|
|
7891
|
+
var assignColumns = (table, entity, payload) => {
|
|
7892
|
+
for (const key of Object.keys(table.columns)) {
|
|
7893
|
+
if (payload[key] !== void 0) {
|
|
7894
|
+
entity[key] = payload[key];
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
};
|
|
7898
|
+
var isEntityInCollection = (items, pkName, entity) => {
|
|
7899
|
+
if (items.includes(entity)) return true;
|
|
7900
|
+
const entityPk = entity[pkName];
|
|
7901
|
+
if (entityPk === void 0 || entityPk === null) return false;
|
|
7902
|
+
return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
|
|
7903
|
+
};
|
|
7904
|
+
var findInCollectionByPk = (items, pkName, pkValue) => {
|
|
7905
|
+
if (pkValue === void 0 || pkValue === null) return void 0;
|
|
7906
|
+
return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
|
|
7907
|
+
};
|
|
7908
|
+
var handleHasMany = async (session, root, relationName, relation, payload, options) => {
|
|
7909
|
+
if (!Array.isArray(payload)) return;
|
|
7910
|
+
const collection = root[relationName];
|
|
7911
|
+
await collection.load();
|
|
7912
|
+
const targetTable = relation.target;
|
|
7913
|
+
const targetPk = findPrimaryKey(targetTable);
|
|
7914
|
+
const existing = collection.getItems();
|
|
7915
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7916
|
+
for (const item of payload) {
|
|
7917
|
+
if (item === null || item === void 0) continue;
|
|
7918
|
+
const asObj = typeof item === "object" ? item : { [targetPk]: item };
|
|
7919
|
+
const pkValue = asObj[targetPk];
|
|
7920
|
+
const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
|
|
7921
|
+
const entity = current ?? ensureEntity(session, targetTable, asObj);
|
|
7922
|
+
assignColumns(targetTable, entity, asObj);
|
|
7923
|
+
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
7924
|
+
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
7925
|
+
collection.attach(entity);
|
|
7926
|
+
}
|
|
7927
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
7928
|
+
seen.add(toKey8(pkValue));
|
|
7929
|
+
}
|
|
7930
|
+
}
|
|
7931
|
+
if (options.pruneMissing) {
|
|
7932
|
+
for (const item of [...collection.getItems()]) {
|
|
7933
|
+
const pkValue = item[targetPk];
|
|
7934
|
+
if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
|
|
7935
|
+
collection.remove(item);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
};
|
|
7940
|
+
var handleHasOne = async (session, root, relationName, relation, payload, options) => {
|
|
7941
|
+
const ref = root[relationName];
|
|
7942
|
+
if (payload === void 0) return;
|
|
7943
|
+
if (payload === null) {
|
|
7944
|
+
ref.set(null);
|
|
7945
|
+
return;
|
|
7946
|
+
}
|
|
7947
|
+
const pk = findPrimaryKey(relation.target);
|
|
7948
|
+
if (typeof payload === "number" || typeof payload === "string") {
|
|
7949
|
+
const entity = ref.set({ [pk]: payload });
|
|
7950
|
+
if (entity) {
|
|
7951
|
+
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
7952
|
+
}
|
|
7953
|
+
return;
|
|
7954
|
+
}
|
|
7955
|
+
const attached = ref.set(payload);
|
|
7956
|
+
if (attached) {
|
|
7957
|
+
await applyGraphToEntity(session, relation.target, attached, payload, options);
|
|
7958
|
+
}
|
|
7959
|
+
};
|
|
7960
|
+
var handleBelongsTo = async (session, root, relationName, relation, payload, options) => {
|
|
7961
|
+
const ref = root[relationName];
|
|
7962
|
+
if (payload === void 0) return;
|
|
7963
|
+
if (payload === null) {
|
|
7964
|
+
ref.set(null);
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
const pk = relation.localKey || findPrimaryKey(relation.target);
|
|
7968
|
+
if (typeof payload === "number" || typeof payload === "string") {
|
|
7969
|
+
const entity = ref.set({ [pk]: payload });
|
|
7970
|
+
if (entity) {
|
|
7971
|
+
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
7972
|
+
}
|
|
7973
|
+
return;
|
|
7974
|
+
}
|
|
7975
|
+
const attached = ref.set(payload);
|
|
7976
|
+
if (attached) {
|
|
7977
|
+
await applyGraphToEntity(session, relation.target, attached, payload, options);
|
|
7978
|
+
}
|
|
7979
|
+
};
|
|
7980
|
+
var handleBelongsToMany = async (session, root, relationName, relation, payload, options) => {
|
|
7981
|
+
if (!Array.isArray(payload)) return;
|
|
7982
|
+
const collection = root[relationName];
|
|
7983
|
+
await collection.load();
|
|
7984
|
+
const targetTable = relation.target;
|
|
7985
|
+
const targetPk = relation.targetKey || findPrimaryKey(targetTable);
|
|
7986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7987
|
+
for (const item of payload) {
|
|
7988
|
+
if (item === null || item === void 0) continue;
|
|
7989
|
+
if (typeof item === "number" || typeof item === "string") {
|
|
7990
|
+
const id = item;
|
|
7991
|
+
collection.attach(id);
|
|
7992
|
+
seen.add(toKey8(id));
|
|
7993
|
+
continue;
|
|
7994
|
+
}
|
|
7995
|
+
const asObj = item;
|
|
7996
|
+
const pkValue = asObj[targetPk];
|
|
7997
|
+
const entity = pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj) : ensureEntity(session, targetTable, asObj);
|
|
7998
|
+
assignColumns(targetTable, entity, asObj);
|
|
7999
|
+
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
8000
|
+
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
8001
|
+
collection.attach(entity);
|
|
8002
|
+
}
|
|
8003
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
8004
|
+
seen.add(toKey8(pkValue));
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
if (options.pruneMissing) {
|
|
8008
|
+
for (const item of [...collection.getItems()]) {
|
|
8009
|
+
const pkValue = item[targetPk];
|
|
8010
|
+
if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
|
|
8011
|
+
collection.detach(item);
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
};
|
|
8016
|
+
var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
|
|
8017
|
+
switch (relation.type) {
|
|
8018
|
+
case RelationKinds.HasMany:
|
|
8019
|
+
return handleHasMany(session, entity, relationName, relation, payload, options);
|
|
8020
|
+
case RelationKinds.HasOne:
|
|
8021
|
+
return handleHasOne(session, entity, relationName, relation, payload, options);
|
|
8022
|
+
case RelationKinds.BelongsTo:
|
|
8023
|
+
return handleBelongsTo(session, entity, relationName, relation, payload, options);
|
|
8024
|
+
case RelationKinds.BelongsToMany:
|
|
8025
|
+
return handleBelongsToMany(session, entity, relationName, relation, payload, options);
|
|
8026
|
+
}
|
|
8027
|
+
};
|
|
8028
|
+
var applyGraphToEntity = async (session, table, entity, payload, options) => {
|
|
8029
|
+
assignColumns(table, entity, payload);
|
|
8030
|
+
for (const [relationName, relation] of Object.entries(table.relations)) {
|
|
8031
|
+
if (!(relationName in payload)) continue;
|
|
8032
|
+
await applyRelation(session, table, entity, relationName, relation, payload[relationName], options);
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
|
|
8036
|
+
const table = getTableDefFromEntity(entityClass);
|
|
8037
|
+
if (!table) {
|
|
8038
|
+
throw new Error("Entity metadata has not been bootstrapped");
|
|
8039
|
+
}
|
|
8040
|
+
const root = ensureEntity(session, table, payload);
|
|
8041
|
+
await applyGraphToEntity(session, table, root, payload, options);
|
|
8042
|
+
return root;
|
|
8043
|
+
};
|
|
8044
|
+
|
|
7456
8045
|
// src/orm/orm-session.ts
|
|
7457
8046
|
var OrmSession = class {
|
|
8047
|
+
/**
|
|
8048
|
+
* Creates a new OrmSession instance.
|
|
8049
|
+
* @param opts - Session options
|
|
8050
|
+
*/
|
|
7458
8051
|
constructor(opts) {
|
|
8052
|
+
/**
|
|
8053
|
+
* Registers a relation change.
|
|
8054
|
+
* @param root - The root entity
|
|
8055
|
+
* @param relationKey - The relation key
|
|
8056
|
+
* @param rootTable - The root table definition
|
|
8057
|
+
* @param relationName - The relation name
|
|
8058
|
+
* @param relation - The relation definition
|
|
8059
|
+
* @param change - The relation change
|
|
8060
|
+
*/
|
|
7459
8061
|
this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
|
|
7460
8062
|
this.relationChanges.registerChange(
|
|
7461
8063
|
buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
|
|
@@ -7469,42 +8071,117 @@ var OrmSession = class {
|
|
|
7469
8071
|
this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
|
|
7470
8072
|
this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
|
|
7471
8073
|
}
|
|
8074
|
+
/**
|
|
8075
|
+
* Releases resources associated with this session (executor/pool leases) and resets tracking.
|
|
8076
|
+
* Must be safe to call multiple times.
|
|
8077
|
+
*/
|
|
8078
|
+
async dispose() {
|
|
8079
|
+
try {
|
|
8080
|
+
await this.executor.dispose();
|
|
8081
|
+
} finally {
|
|
8082
|
+
this.unitOfWork.reset();
|
|
8083
|
+
this.relationChanges.reset();
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
8086
|
+
/**
|
|
8087
|
+
* Gets the database dialect.
|
|
8088
|
+
*/
|
|
7472
8089
|
get dialect() {
|
|
7473
8090
|
return this.orm.dialect;
|
|
7474
8091
|
}
|
|
8092
|
+
/**
|
|
8093
|
+
* Gets the identity buckets map.
|
|
8094
|
+
*/
|
|
7475
8095
|
get identityBuckets() {
|
|
7476
8096
|
return this.unitOfWork.identityBuckets;
|
|
7477
8097
|
}
|
|
8098
|
+
/**
|
|
8099
|
+
* Gets all tracked entities.
|
|
8100
|
+
*/
|
|
7478
8101
|
get tracked() {
|
|
7479
8102
|
return this.unitOfWork.getTracked();
|
|
7480
8103
|
}
|
|
8104
|
+
/**
|
|
8105
|
+
* Gets an entity by table and primary key.
|
|
8106
|
+
* @param table - The table definition
|
|
8107
|
+
* @param pk - The primary key value
|
|
8108
|
+
* @returns The entity or undefined if not found
|
|
8109
|
+
*/
|
|
7481
8110
|
getEntity(table, pk) {
|
|
7482
8111
|
return this.unitOfWork.getEntity(table, pk);
|
|
7483
8112
|
}
|
|
8113
|
+
/**
|
|
8114
|
+
* Sets an entity in the identity map.
|
|
8115
|
+
* @param table - The table definition
|
|
8116
|
+
* @param pk - The primary key value
|
|
8117
|
+
* @param entity - The entity instance
|
|
8118
|
+
*/
|
|
7484
8119
|
setEntity(table, pk, entity) {
|
|
7485
8120
|
this.unitOfWork.setEntity(table, pk, entity);
|
|
7486
8121
|
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Tracks a new entity.
|
|
8124
|
+
* @param table - The table definition
|
|
8125
|
+
* @param entity - The entity instance
|
|
8126
|
+
* @param pk - Optional primary key value
|
|
8127
|
+
*/
|
|
7487
8128
|
trackNew(table, entity, pk) {
|
|
7488
8129
|
this.unitOfWork.trackNew(table, entity, pk);
|
|
7489
8130
|
}
|
|
8131
|
+
/**
|
|
8132
|
+
* Tracks a managed entity.
|
|
8133
|
+
* @param table - The table definition
|
|
8134
|
+
* @param pk - The primary key value
|
|
8135
|
+
* @param entity - The entity instance
|
|
8136
|
+
*/
|
|
7490
8137
|
trackManaged(table, pk, entity) {
|
|
7491
8138
|
this.unitOfWork.trackManaged(table, pk, entity);
|
|
7492
8139
|
}
|
|
8140
|
+
/**
|
|
8141
|
+
* Marks an entity as dirty (modified).
|
|
8142
|
+
* @param entity - The entity to mark as dirty
|
|
8143
|
+
*/
|
|
7493
8144
|
markDirty(entity) {
|
|
7494
8145
|
this.unitOfWork.markDirty(entity);
|
|
7495
8146
|
}
|
|
8147
|
+
/**
|
|
8148
|
+
* Marks an entity as removed.
|
|
8149
|
+
* @param entity - The entity to mark as removed
|
|
8150
|
+
*/
|
|
7496
8151
|
markRemoved(entity) {
|
|
7497
8152
|
this.unitOfWork.markRemoved(entity);
|
|
7498
8153
|
}
|
|
8154
|
+
/**
|
|
8155
|
+
* Gets all tracked entities for a specific table.
|
|
8156
|
+
* @param table - The table definition
|
|
8157
|
+
* @returns Array of tracked entities
|
|
8158
|
+
*/
|
|
7499
8159
|
getEntitiesForTable(table) {
|
|
7500
8160
|
return this.unitOfWork.getEntitiesForTable(table);
|
|
7501
8161
|
}
|
|
8162
|
+
/**
|
|
8163
|
+
* Registers an interceptor for flush lifecycle hooks.
|
|
8164
|
+
* @param interceptor - The interceptor to register
|
|
8165
|
+
*/
|
|
7502
8166
|
registerInterceptor(interceptor) {
|
|
7503
8167
|
this.interceptors.push(interceptor);
|
|
7504
8168
|
}
|
|
8169
|
+
/**
|
|
8170
|
+
* Registers a domain event handler.
|
|
8171
|
+
* @param type - The event type
|
|
8172
|
+
* @param handler - The event handler
|
|
8173
|
+
*/
|
|
7505
8174
|
registerDomainEventHandler(type, handler) {
|
|
7506
8175
|
this.domainEvents.on(type, handler);
|
|
7507
8176
|
}
|
|
8177
|
+
/**
|
|
8178
|
+
* Finds an entity by its primary key.
|
|
8179
|
+
* @template TCtor - The entity constructor type
|
|
8180
|
+
* @param entityClass - The entity constructor
|
|
8181
|
+
* @param id - The primary key value
|
|
8182
|
+
* @returns The entity instance or null if not found
|
|
8183
|
+
* @throws If entity metadata is not bootstrapped or table has no primary key
|
|
8184
|
+
*/
|
|
7508
8185
|
async find(entityClass, id) {
|
|
7509
8186
|
const table = getTableDefFromEntity(entityClass);
|
|
7510
8187
|
if (!table) {
|
|
@@ -7523,14 +8200,46 @@ var OrmSession = class {
|
|
|
7523
8200
|
const rows = await executeHydrated(this, qb);
|
|
7524
8201
|
return rows[0] ?? null;
|
|
7525
8202
|
}
|
|
8203
|
+
/**
|
|
8204
|
+
* Finds a single entity using a query builder.
|
|
8205
|
+
* @template TTable - The table type
|
|
8206
|
+
* @param qb - The query builder
|
|
8207
|
+
* @returns The first entity instance or null if not found
|
|
8208
|
+
*/
|
|
7526
8209
|
async findOne(qb) {
|
|
7527
8210
|
const limited = qb.limit(1);
|
|
7528
8211
|
const rows = await executeHydrated(this, limited);
|
|
7529
8212
|
return rows[0] ?? null;
|
|
7530
8213
|
}
|
|
8214
|
+
/**
|
|
8215
|
+
* Finds multiple entities using a query builder.
|
|
8216
|
+
* @template TTable - The table type
|
|
8217
|
+
* @param qb - The query builder
|
|
8218
|
+
* @returns Array of entity instances
|
|
8219
|
+
*/
|
|
7531
8220
|
async findMany(qb) {
|
|
7532
8221
|
return executeHydrated(this, qb);
|
|
7533
8222
|
}
|
|
8223
|
+
/**
|
|
8224
|
+
* Saves an entity graph (root + nested relations) based on a DTO-like payload.
|
|
8225
|
+
* @param entityClass - Root entity constructor
|
|
8226
|
+
* @param payload - DTO payload containing column values and nested relations
|
|
8227
|
+
* @param options - Graph save options
|
|
8228
|
+
* @returns The root entity instance
|
|
8229
|
+
*/
|
|
8230
|
+
async saveGraph(entityClass, payload, options) {
|
|
8231
|
+
const { transactional = true, ...graphOptions } = options ?? {};
|
|
8232
|
+
const execute = () => saveGraphInternal(this, entityClass, payload, graphOptions);
|
|
8233
|
+
if (!transactional) {
|
|
8234
|
+
return execute();
|
|
8235
|
+
}
|
|
8236
|
+
return this.transaction(() => execute());
|
|
8237
|
+
}
|
|
8238
|
+
/**
|
|
8239
|
+
* Persists an entity (either inserts or updates).
|
|
8240
|
+
* @param entity - The entity to persist
|
|
8241
|
+
* @throws If entity metadata is not bootstrapped
|
|
8242
|
+
*/
|
|
7534
8243
|
async persist(entity) {
|
|
7535
8244
|
if (this.unitOfWork.findTracked(entity)) {
|
|
7536
8245
|
return;
|
|
@@ -7547,12 +8256,22 @@ var OrmSession = class {
|
|
|
7547
8256
|
this.trackNew(table, entity);
|
|
7548
8257
|
}
|
|
7549
8258
|
}
|
|
8259
|
+
/**
|
|
8260
|
+
* Marks an entity for removal.
|
|
8261
|
+
* @param entity - The entity to remove
|
|
8262
|
+
*/
|
|
7550
8263
|
async remove(entity) {
|
|
7551
8264
|
this.markRemoved(entity);
|
|
7552
8265
|
}
|
|
8266
|
+
/**
|
|
8267
|
+
* Flushes pending changes to the database.
|
|
8268
|
+
*/
|
|
7553
8269
|
async flush() {
|
|
7554
8270
|
await this.unitOfWork.flush();
|
|
7555
8271
|
}
|
|
8272
|
+
/**
|
|
8273
|
+
* Flushes pending changes with interceptors and relation processing.
|
|
8274
|
+
*/
|
|
7556
8275
|
async flushWithHooks() {
|
|
7557
8276
|
for (const interceptor of this.interceptors) {
|
|
7558
8277
|
await interceptor.beforeFlush?.(this);
|
|
@@ -7564,14 +8283,24 @@ var OrmSession = class {
|
|
|
7564
8283
|
await interceptor.afterFlush?.(this);
|
|
7565
8284
|
}
|
|
7566
8285
|
}
|
|
8286
|
+
/**
|
|
8287
|
+
* Commits the current transaction.
|
|
8288
|
+
*/
|
|
7567
8289
|
async commit() {
|
|
7568
8290
|
await runInTransaction(this.executor, async () => {
|
|
7569
8291
|
await this.flushWithHooks();
|
|
7570
8292
|
});
|
|
7571
8293
|
await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
|
|
7572
8294
|
}
|
|
8295
|
+
/**
|
|
8296
|
+
* Executes a function within a transaction.
|
|
8297
|
+
* @template T - The return type
|
|
8298
|
+
* @param fn - The function to execute
|
|
8299
|
+
* @returns The result of the function
|
|
8300
|
+
* @throws If the transaction fails
|
|
8301
|
+
*/
|
|
7573
8302
|
async transaction(fn4) {
|
|
7574
|
-
if (!this.executor.
|
|
8303
|
+
if (!this.executor.capabilities.transactions) {
|
|
7575
8304
|
const result = await fn4(this);
|
|
7576
8305
|
await this.commit();
|
|
7577
8306
|
return result;
|
|
@@ -7580,7 +8309,7 @@ var OrmSession = class {
|
|
|
7580
8309
|
try {
|
|
7581
8310
|
const result = await fn4(this);
|
|
7582
8311
|
await this.flushWithHooks();
|
|
7583
|
-
await this.executor.commitTransaction
|
|
8312
|
+
await this.executor.commitTransaction();
|
|
7584
8313
|
await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
|
|
7585
8314
|
return result;
|
|
7586
8315
|
} catch (err) {
|
|
@@ -7588,11 +8317,20 @@ var OrmSession = class {
|
|
|
7588
8317
|
throw err;
|
|
7589
8318
|
}
|
|
7590
8319
|
}
|
|
8320
|
+
/**
|
|
8321
|
+
* Rolls back the current transaction.
|
|
8322
|
+
*/
|
|
7591
8323
|
async rollback() {
|
|
7592
|
-
|
|
8324
|
+
if (this.executor.capabilities.transactions) {
|
|
8325
|
+
await this.executor.rollbackTransaction();
|
|
8326
|
+
}
|
|
7593
8327
|
this.unitOfWork.reset();
|
|
7594
8328
|
this.relationChanges.reset();
|
|
7595
8329
|
}
|
|
8330
|
+
/**
|
|
8331
|
+
* Gets the execution context.
|
|
8332
|
+
* @returns The execution context
|
|
8333
|
+
*/
|
|
7596
8334
|
getExecutionContext() {
|
|
7597
8335
|
return {
|
|
7598
8336
|
dialect: this.orm.dialect,
|
|
@@ -7600,6 +8338,10 @@ var OrmSession = class {
|
|
|
7600
8338
|
interceptors: this.orm.interceptors
|
|
7601
8339
|
};
|
|
7602
8340
|
}
|
|
8341
|
+
/**
|
|
8342
|
+
* Gets the hydration context.
|
|
8343
|
+
* @returns The hydration context
|
|
8344
|
+
*/
|
|
7603
8345
|
getHydrationContext() {
|
|
7604
8346
|
return {
|
|
7605
8347
|
identityMap: this.identityMap,
|
|
@@ -7642,29 +8384,49 @@ var InterceptorPipeline = class {
|
|
|
7642
8384
|
|
|
7643
8385
|
// src/orm/orm.ts
|
|
7644
8386
|
var Orm = class {
|
|
8387
|
+
/**
|
|
8388
|
+
* Creates a new ORM instance.
|
|
8389
|
+
* @param opts - ORM options
|
|
8390
|
+
*/
|
|
7645
8391
|
constructor(opts) {
|
|
7646
8392
|
this.dialect = opts.dialect;
|
|
7647
8393
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
7648
8394
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
7649
8395
|
this.executorFactory = opts.executorFactory;
|
|
7650
8396
|
}
|
|
7651
|
-
|
|
7652
|
-
|
|
8397
|
+
/**
|
|
8398
|
+
* Creates a new ORM session.
|
|
8399
|
+
* @param options - Optional session options
|
|
8400
|
+
* @returns The ORM session
|
|
8401
|
+
*/
|
|
8402
|
+
createSession() {
|
|
8403
|
+
const executor = this.executorFactory.createExecutor();
|
|
7653
8404
|
return new OrmSession({ orm: this, executor });
|
|
7654
8405
|
}
|
|
8406
|
+
/**
|
|
8407
|
+
* Executes a function within a transaction.
|
|
8408
|
+
* @template T - The return type
|
|
8409
|
+
* @param fn - The function to execute
|
|
8410
|
+
* @returns The result of the function
|
|
8411
|
+
* @throws If the transaction fails
|
|
8412
|
+
*/
|
|
7655
8413
|
async transaction(fn4) {
|
|
7656
8414
|
const executor = this.executorFactory.createTransactionalExecutor();
|
|
7657
8415
|
const session = new OrmSession({ orm: this, executor });
|
|
7658
8416
|
try {
|
|
7659
|
-
|
|
7660
|
-
await session.commit();
|
|
7661
|
-
return result;
|
|
8417
|
+
return await session.transaction(() => fn4(session));
|
|
7662
8418
|
} catch (err) {
|
|
7663
|
-
await session.rollback();
|
|
7664
8419
|
throw err;
|
|
7665
8420
|
} finally {
|
|
8421
|
+
await session.dispose();
|
|
7666
8422
|
}
|
|
7667
8423
|
}
|
|
8424
|
+
/**
|
|
8425
|
+
* Shuts down the ORM and releases underlying resources (pools, timers).
|
|
8426
|
+
*/
|
|
8427
|
+
async dispose() {
|
|
8428
|
+
await this.executorFactory.dispose();
|
|
8429
|
+
}
|
|
7668
8430
|
};
|
|
7669
8431
|
|
|
7670
8432
|
// src/decorators/decorator-metadata.ts
|
|
@@ -7923,18 +8685,245 @@ function rowsToQueryResult(rows) {
|
|
|
7923
8685
|
return { columns, values };
|
|
7924
8686
|
}
|
|
7925
8687
|
function createExecutorFromQueryRunner(runner) {
|
|
8688
|
+
const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
|
|
7926
8689
|
return {
|
|
8690
|
+
capabilities: {
|
|
8691
|
+
transactions: supportsTransactions
|
|
8692
|
+
},
|
|
7927
8693
|
async executeSql(sql, params) {
|
|
7928
8694
|
const rows = await runner.query(sql, params);
|
|
7929
8695
|
const result = rowsToQueryResult(rows);
|
|
7930
8696
|
return [result];
|
|
7931
8697
|
},
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
8698
|
+
async beginTransaction() {
|
|
8699
|
+
if (!supportsTransactions) {
|
|
8700
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8701
|
+
}
|
|
8702
|
+
await runner.beginTransaction.call(runner);
|
|
8703
|
+
},
|
|
8704
|
+
async commitTransaction() {
|
|
8705
|
+
if (!supportsTransactions) {
|
|
8706
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8707
|
+
}
|
|
8708
|
+
await runner.commitTransaction.call(runner);
|
|
8709
|
+
},
|
|
8710
|
+
async rollbackTransaction() {
|
|
8711
|
+
if (!supportsTransactions) {
|
|
8712
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8713
|
+
}
|
|
8714
|
+
await runner.rollbackTransaction.call(runner);
|
|
8715
|
+
},
|
|
8716
|
+
async dispose() {
|
|
8717
|
+
await runner.dispose?.call(runner);
|
|
8718
|
+
}
|
|
7935
8719
|
};
|
|
7936
8720
|
}
|
|
7937
8721
|
|
|
8722
|
+
// src/core/execution/pooling/pool.ts
|
|
8723
|
+
var deferred = () => {
|
|
8724
|
+
let resolve;
|
|
8725
|
+
let reject;
|
|
8726
|
+
const promise = new Promise((res, rej) => {
|
|
8727
|
+
resolve = res;
|
|
8728
|
+
reject = rej;
|
|
8729
|
+
});
|
|
8730
|
+
return { promise, resolve, reject };
|
|
8731
|
+
};
|
|
8732
|
+
var Pool = class {
|
|
8733
|
+
constructor(adapter, options) {
|
|
8734
|
+
this.destroyed = false;
|
|
8735
|
+
this.creating = 0;
|
|
8736
|
+
this.leased = 0;
|
|
8737
|
+
this.idle = [];
|
|
8738
|
+
this.waiters = [];
|
|
8739
|
+
this.reapTimer = null;
|
|
8740
|
+
if (!Number.isFinite(options.max) || options.max <= 0) {
|
|
8741
|
+
throw new Error("Pool options.max must be a positive number");
|
|
8742
|
+
}
|
|
8743
|
+
this.adapter = adapter;
|
|
8744
|
+
this.options = { max: options.max, ...options };
|
|
8745
|
+
const idleTimeout = this.options.idleTimeoutMillis;
|
|
8746
|
+
if (idleTimeout && idleTimeout > 0) {
|
|
8747
|
+
const interval = this.options.reapIntervalMillis ?? Math.max(1e3, Math.floor(idleTimeout / 2));
|
|
8748
|
+
this.reapTimer = setInterval(() => {
|
|
8749
|
+
void this.reapIdle();
|
|
8750
|
+
}, interval);
|
|
8751
|
+
this.reapTimer.unref?.();
|
|
8752
|
+
}
|
|
8753
|
+
const min2 = this.options.min ?? 0;
|
|
8754
|
+
if (min2 > 0) {
|
|
8755
|
+
void this.warm(min2);
|
|
8756
|
+
}
|
|
8757
|
+
}
|
|
8758
|
+
/**
|
|
8759
|
+
* Acquire a resource lease.
|
|
8760
|
+
* The returned lease MUST be released or destroyed.
|
|
8761
|
+
*/
|
|
8762
|
+
async acquire() {
|
|
8763
|
+
if (this.destroyed) {
|
|
8764
|
+
throw new Error("Pool is destroyed");
|
|
8765
|
+
}
|
|
8766
|
+
const idle = await this.takeIdleValidated();
|
|
8767
|
+
if (idle) {
|
|
8768
|
+
this.leased++;
|
|
8769
|
+
return this.makeLease(idle);
|
|
8770
|
+
}
|
|
8771
|
+
if (this.totalLive() < this.options.max) {
|
|
8772
|
+
this.creating++;
|
|
8773
|
+
try {
|
|
8774
|
+
const created = await this.adapter.create();
|
|
8775
|
+
this.leased++;
|
|
8776
|
+
return this.makeLease(created);
|
|
8777
|
+
} finally {
|
|
8778
|
+
this.creating--;
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8781
|
+
const waiter = deferred();
|
|
8782
|
+
this.waiters.push(waiter);
|
|
8783
|
+
const timeout = this.options.acquireTimeoutMillis;
|
|
8784
|
+
let timer = null;
|
|
8785
|
+
if (timeout && timeout > 0) {
|
|
8786
|
+
timer = setTimeout(() => {
|
|
8787
|
+
const idx = this.waiters.indexOf(waiter);
|
|
8788
|
+
if (idx >= 0) this.waiters.splice(idx, 1);
|
|
8789
|
+
waiter.reject(new Error("Pool acquire timeout"));
|
|
8790
|
+
}, timeout);
|
|
8791
|
+
timer.unref?.();
|
|
8792
|
+
}
|
|
8793
|
+
try {
|
|
8794
|
+
return await waiter.promise;
|
|
8795
|
+
} finally {
|
|
8796
|
+
if (timer) clearTimeout(timer);
|
|
8797
|
+
}
|
|
8798
|
+
}
|
|
8799
|
+
/** Destroy pool and all idle resources; waits for in-flight creations to settle. */
|
|
8800
|
+
async destroy() {
|
|
8801
|
+
if (this.destroyed) return;
|
|
8802
|
+
this.destroyed = true;
|
|
8803
|
+
if (this.reapTimer) {
|
|
8804
|
+
clearInterval(this.reapTimer);
|
|
8805
|
+
this.reapTimer = null;
|
|
8806
|
+
}
|
|
8807
|
+
while (this.waiters.length) {
|
|
8808
|
+
this.waiters.shift().reject(new Error("Pool destroyed"));
|
|
8809
|
+
}
|
|
8810
|
+
while (this.idle.length) {
|
|
8811
|
+
const entry = this.idle.shift();
|
|
8812
|
+
await this.adapter.destroy(entry.resource);
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
totalLive() {
|
|
8816
|
+
return this.idle.length + this.leased + this.creating;
|
|
8817
|
+
}
|
|
8818
|
+
makeLease(resource) {
|
|
8819
|
+
let done = false;
|
|
8820
|
+
return {
|
|
8821
|
+
resource,
|
|
8822
|
+
release: async () => {
|
|
8823
|
+
if (done) return;
|
|
8824
|
+
done = true;
|
|
8825
|
+
await this.releaseResource(resource);
|
|
8826
|
+
},
|
|
8827
|
+
destroy: async () => {
|
|
8828
|
+
if (done) return;
|
|
8829
|
+
done = true;
|
|
8830
|
+
await this.destroyResource(resource);
|
|
8831
|
+
}
|
|
8832
|
+
};
|
|
8833
|
+
}
|
|
8834
|
+
async releaseResource(resource) {
|
|
8835
|
+
this.leased = Math.max(0, this.leased - 1);
|
|
8836
|
+
if (this.destroyed) {
|
|
8837
|
+
await this.adapter.destroy(resource);
|
|
8838
|
+
return;
|
|
8839
|
+
}
|
|
8840
|
+
const next = this.waiters.shift();
|
|
8841
|
+
if (next) {
|
|
8842
|
+
this.leased++;
|
|
8843
|
+
next.resolve(this.makeLease(resource));
|
|
8844
|
+
return;
|
|
8845
|
+
}
|
|
8846
|
+
this.idle.push({ resource, lastUsedAt: Date.now() });
|
|
8847
|
+
await this.trimToMinMax();
|
|
8848
|
+
}
|
|
8849
|
+
async destroyResource(resource) {
|
|
8850
|
+
this.leased = Math.max(0, this.leased - 1);
|
|
8851
|
+
await this.adapter.destroy(resource);
|
|
8852
|
+
if (!this.destroyed && this.waiters.length && this.totalLive() < this.options.max) {
|
|
8853
|
+
const waiter = this.waiters.shift();
|
|
8854
|
+
this.creating++;
|
|
8855
|
+
try {
|
|
8856
|
+
const created = await this.adapter.create();
|
|
8857
|
+
this.leased++;
|
|
8858
|
+
waiter.resolve(this.makeLease(created));
|
|
8859
|
+
} catch (err) {
|
|
8860
|
+
waiter.reject(err);
|
|
8861
|
+
} finally {
|
|
8862
|
+
this.creating--;
|
|
8863
|
+
}
|
|
8864
|
+
}
|
|
8865
|
+
}
|
|
8866
|
+
async takeIdleValidated() {
|
|
8867
|
+
while (this.idle.length) {
|
|
8868
|
+
const entry = this.idle.pop();
|
|
8869
|
+
if (!this.adapter.validate) {
|
|
8870
|
+
return entry.resource;
|
|
8871
|
+
}
|
|
8872
|
+
const ok = await this.adapter.validate(entry.resource);
|
|
8873
|
+
if (ok) {
|
|
8874
|
+
return entry.resource;
|
|
8875
|
+
}
|
|
8876
|
+
await this.adapter.destroy(entry.resource);
|
|
8877
|
+
}
|
|
8878
|
+
return null;
|
|
8879
|
+
}
|
|
8880
|
+
async reapIdle() {
|
|
8881
|
+
if (this.destroyed) return;
|
|
8882
|
+
const idleTimeout = this.options.idleTimeoutMillis;
|
|
8883
|
+
if (!idleTimeout || idleTimeout <= 0) return;
|
|
8884
|
+
const now2 = Date.now();
|
|
8885
|
+
const min2 = this.options.min ?? 0;
|
|
8886
|
+
const keep = [];
|
|
8887
|
+
const kill = [];
|
|
8888
|
+
for (const entry of this.idle) {
|
|
8889
|
+
const expired = now2 - entry.lastUsedAt >= idleTimeout;
|
|
8890
|
+
if (expired) kill.push(entry);
|
|
8891
|
+
else keep.push(entry);
|
|
8892
|
+
}
|
|
8893
|
+
while (keep.length < min2 && kill.length) {
|
|
8894
|
+
keep.push(kill.pop());
|
|
8895
|
+
}
|
|
8896
|
+
this.idle.length = 0;
|
|
8897
|
+
this.idle.push(...keep);
|
|
8898
|
+
for (const entry of kill) {
|
|
8899
|
+
await this.adapter.destroy(entry.resource);
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
async warm(targetMin) {
|
|
8903
|
+
const min2 = Math.max(0, targetMin);
|
|
8904
|
+
while (!this.destroyed && this.idle.length < min2 && this.totalLive() < this.options.max) {
|
|
8905
|
+
this.creating++;
|
|
8906
|
+
try {
|
|
8907
|
+
const created = await this.adapter.create();
|
|
8908
|
+
this.idle.push({ resource: created, lastUsedAt: Date.now() });
|
|
8909
|
+
} catch {
|
|
8910
|
+
break;
|
|
8911
|
+
} finally {
|
|
8912
|
+
this.creating--;
|
|
8913
|
+
}
|
|
8914
|
+
}
|
|
8915
|
+
}
|
|
8916
|
+
async trimToMinMax() {
|
|
8917
|
+
const max2 = this.options.max;
|
|
8918
|
+
const min2 = this.options.min ?? 0;
|
|
8919
|
+
while (this.totalLive() > max2 && this.idle.length > min2) {
|
|
8920
|
+
const entry = this.idle.shift();
|
|
8921
|
+
if (!entry) break;
|
|
8922
|
+
await this.adapter.destroy(entry.resource);
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
};
|
|
8926
|
+
|
|
7938
8927
|
// src/core/execution/executors/postgres-executor.ts
|
|
7939
8928
|
function createPostgresExecutor(client) {
|
|
7940
8929
|
return createExecutorFromQueryRunner({
|
|
@@ -7956,7 +8945,11 @@ function createPostgresExecutor(client) {
|
|
|
7956
8945
|
|
|
7957
8946
|
// src/core/execution/executors/mysql-executor.ts
|
|
7958
8947
|
function createMysqlExecutor(client) {
|
|
8948
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
|
|
7959
8949
|
return {
|
|
8950
|
+
capabilities: {
|
|
8951
|
+
transactions: supportsTransactions
|
|
8952
|
+
},
|
|
7960
8953
|
async executeSql(sql, params) {
|
|
7961
8954
|
const [rows] = await client.query(sql, params);
|
|
7962
8955
|
if (!Array.isArray(rows)) {
|
|
@@ -7968,53 +8961,94 @@ function createMysqlExecutor(client) {
|
|
|
7968
8961
|
return [result];
|
|
7969
8962
|
},
|
|
7970
8963
|
async beginTransaction() {
|
|
7971
|
-
if (!
|
|
8964
|
+
if (!supportsTransactions) {
|
|
8965
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8966
|
+
}
|
|
7972
8967
|
await client.beginTransaction();
|
|
7973
8968
|
},
|
|
7974
8969
|
async commitTransaction() {
|
|
7975
|
-
if (!
|
|
8970
|
+
if (!supportsTransactions) {
|
|
8971
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8972
|
+
}
|
|
7976
8973
|
await client.commit();
|
|
7977
8974
|
},
|
|
7978
8975
|
async rollbackTransaction() {
|
|
7979
|
-
if (!
|
|
8976
|
+
if (!supportsTransactions) {
|
|
8977
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8978
|
+
}
|
|
7980
8979
|
await client.rollback();
|
|
8980
|
+
},
|
|
8981
|
+
async dispose() {
|
|
7981
8982
|
}
|
|
7982
8983
|
};
|
|
7983
8984
|
}
|
|
7984
8985
|
|
|
7985
8986
|
// src/core/execution/executors/sqlite-executor.ts
|
|
7986
8987
|
function createSqliteExecutor(client) {
|
|
8988
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
|
|
7987
8989
|
return {
|
|
8990
|
+
capabilities: {
|
|
8991
|
+
transactions: supportsTransactions
|
|
8992
|
+
},
|
|
7988
8993
|
async executeSql(sql, params) {
|
|
7989
8994
|
const rows = await client.all(sql, params);
|
|
7990
8995
|
const result = rowsToQueryResult(rows);
|
|
7991
8996
|
return [result];
|
|
7992
8997
|
},
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
8998
|
+
async beginTransaction() {
|
|
8999
|
+
if (!supportsTransactions) {
|
|
9000
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9001
|
+
}
|
|
9002
|
+
await client.beginTransaction();
|
|
9003
|
+
},
|
|
9004
|
+
async commitTransaction() {
|
|
9005
|
+
if (!supportsTransactions) {
|
|
9006
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9007
|
+
}
|
|
9008
|
+
await client.commitTransaction();
|
|
9009
|
+
},
|
|
9010
|
+
async rollbackTransaction() {
|
|
9011
|
+
if (!supportsTransactions) {
|
|
9012
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9013
|
+
}
|
|
9014
|
+
await client.rollbackTransaction();
|
|
9015
|
+
},
|
|
9016
|
+
async dispose() {
|
|
9017
|
+
}
|
|
7996
9018
|
};
|
|
7997
9019
|
}
|
|
7998
9020
|
|
|
7999
9021
|
// src/core/execution/executors/mssql-executor.ts
|
|
8000
9022
|
function createMssqlExecutor(client) {
|
|
9023
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
|
|
8001
9024
|
return {
|
|
9025
|
+
capabilities: {
|
|
9026
|
+
transactions: supportsTransactions
|
|
9027
|
+
},
|
|
8002
9028
|
async executeSql(sql, params) {
|
|
8003
9029
|
const { recordset } = await client.query(sql, params);
|
|
8004
9030
|
const result = rowsToQueryResult(recordset ?? []);
|
|
8005
9031
|
return [result];
|
|
8006
9032
|
},
|
|
8007
9033
|
async beginTransaction() {
|
|
8008
|
-
if (!
|
|
9034
|
+
if (!supportsTransactions) {
|
|
9035
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9036
|
+
}
|
|
8009
9037
|
await client.beginTransaction();
|
|
8010
9038
|
},
|
|
8011
9039
|
async commitTransaction() {
|
|
8012
|
-
if (!
|
|
9040
|
+
if (!supportsTransactions) {
|
|
9041
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9042
|
+
}
|
|
8013
9043
|
await client.commit();
|
|
8014
9044
|
},
|
|
8015
9045
|
async rollbackTransaction() {
|
|
8016
|
-
if (!
|
|
9046
|
+
if (!supportsTransactions) {
|
|
9047
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9048
|
+
}
|
|
8017
9049
|
await client.rollback();
|
|
9050
|
+
},
|
|
9051
|
+
async dispose() {
|
|
8018
9052
|
}
|
|
8019
9053
|
};
|
|
8020
9054
|
}
|
|
@@ -8083,6 +9117,86 @@ function createTediousExecutor(connection, module, options) {
|
|
|
8083
9117
|
const client = createTediousMssqlClient(connection, module, options);
|
|
8084
9118
|
return createMssqlExecutor(client);
|
|
8085
9119
|
}
|
|
9120
|
+
|
|
9121
|
+
// src/orm/pooled-executor-factory.ts
|
|
9122
|
+
function createPooledExecutorFactory(opts) {
|
|
9123
|
+
const { pool, adapter } = opts;
|
|
9124
|
+
const makeExecutor = (mode) => {
|
|
9125
|
+
let lease = null;
|
|
9126
|
+
const getLease = async () => {
|
|
9127
|
+
if (lease) return lease;
|
|
9128
|
+
lease = await pool.acquire();
|
|
9129
|
+
return lease;
|
|
9130
|
+
};
|
|
9131
|
+
const executeWithConn = async (conn, sql, params) => {
|
|
9132
|
+
const rows = await adapter.query(conn, sql, params);
|
|
9133
|
+
return [rowsToQueryResult(rows)];
|
|
9134
|
+
};
|
|
9135
|
+
return {
|
|
9136
|
+
capabilities: { transactions: true },
|
|
9137
|
+
async executeSql(sql, params) {
|
|
9138
|
+
if (mode === "sticky") {
|
|
9139
|
+
const l2 = await getLease();
|
|
9140
|
+
return executeWithConn(l2.resource, sql, params);
|
|
9141
|
+
}
|
|
9142
|
+
if (lease) {
|
|
9143
|
+
return executeWithConn(lease.resource, sql, params);
|
|
9144
|
+
}
|
|
9145
|
+
const l = await pool.acquire();
|
|
9146
|
+
try {
|
|
9147
|
+
return await executeWithConn(l.resource, sql, params);
|
|
9148
|
+
} finally {
|
|
9149
|
+
await l.release();
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9152
|
+
async beginTransaction() {
|
|
9153
|
+
const l = await getLease();
|
|
9154
|
+
await adapter.beginTransaction(l.resource);
|
|
9155
|
+
},
|
|
9156
|
+
async commitTransaction() {
|
|
9157
|
+
if (!lease) {
|
|
9158
|
+
throw new Error("commitTransaction called without an active transaction");
|
|
9159
|
+
}
|
|
9160
|
+
const l = lease;
|
|
9161
|
+
try {
|
|
9162
|
+
await adapter.commitTransaction(l.resource);
|
|
9163
|
+
} finally {
|
|
9164
|
+
lease = null;
|
|
9165
|
+
await l.release();
|
|
9166
|
+
}
|
|
9167
|
+
},
|
|
9168
|
+
async rollbackTransaction() {
|
|
9169
|
+
if (!lease) {
|
|
9170
|
+
return;
|
|
9171
|
+
}
|
|
9172
|
+
const l = lease;
|
|
9173
|
+
try {
|
|
9174
|
+
await adapter.rollbackTransaction(l.resource);
|
|
9175
|
+
} finally {
|
|
9176
|
+
lease = null;
|
|
9177
|
+
await l.release();
|
|
9178
|
+
}
|
|
9179
|
+
},
|
|
9180
|
+
async dispose() {
|
|
9181
|
+
if (!lease) return;
|
|
9182
|
+
const l = lease;
|
|
9183
|
+
lease = null;
|
|
9184
|
+
await l.release();
|
|
9185
|
+
}
|
|
9186
|
+
};
|
|
9187
|
+
};
|
|
9188
|
+
return {
|
|
9189
|
+
createExecutor() {
|
|
9190
|
+
return makeExecutor("session");
|
|
9191
|
+
},
|
|
9192
|
+
createTransactionalExecutor() {
|
|
9193
|
+
return makeExecutor("sticky");
|
|
9194
|
+
},
|
|
9195
|
+
async dispose() {
|
|
9196
|
+
await pool.destroy();
|
|
9197
|
+
}
|
|
9198
|
+
};
|
|
9199
|
+
}
|
|
8086
9200
|
export {
|
|
8087
9201
|
AsyncLocalStorage,
|
|
8088
9202
|
BelongsTo,
|
|
@@ -8101,6 +9215,7 @@ export {
|
|
|
8101
9215
|
MySqlDialect,
|
|
8102
9216
|
Orm,
|
|
8103
9217
|
OrmSession,
|
|
9218
|
+
Pool,
|
|
8104
9219
|
PostgresDialect,
|
|
8105
9220
|
PrimaryKey,
|
|
8106
9221
|
RelationKinds,
|
|
@@ -8146,6 +9261,7 @@ export {
|
|
|
8146
9261
|
createLiteral,
|
|
8147
9262
|
createMssqlExecutor,
|
|
8148
9263
|
createMysqlExecutor,
|
|
9264
|
+
createPooledExecutorFactory,
|
|
8149
9265
|
createPostgresExecutor,
|
|
8150
9266
|
createQueryLoggingExecutor,
|
|
8151
9267
|
createSqliteExecutor,
|
|
@@ -8187,6 +9303,7 @@ export {
|
|
|
8187
9303
|
hasOne,
|
|
8188
9304
|
hydrateRows,
|
|
8189
9305
|
inList,
|
|
9306
|
+
inSubquery,
|
|
8190
9307
|
instr,
|
|
8191
9308
|
introspectSchema,
|
|
8192
9309
|
isCaseExpressionNode,
|
|
@@ -8227,6 +9344,7 @@ export {
|
|
|
8227
9344
|
notBetween,
|
|
8228
9345
|
notExists,
|
|
8229
9346
|
notInList,
|
|
9347
|
+
notInSubquery,
|
|
8230
9348
|
notLike,
|
|
8231
9349
|
now,
|
|
8232
9350
|
ntile,
|