metal-orm 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +728 -707
  2. package/dist/index.cjs +813 -75
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +277 -8
  5. package/dist/index.d.ts +277 -8
  6. package/dist/index.js +812 -75
  7. package/dist/index.js.map +1 -1
  8. package/package.json +8 -2
  9. package/scripts/naming-strategy.mjs +16 -1
  10. package/src/cache/adapters/keyv-cache-adapter.ts +5 -0
  11. package/src/cache/adapters/memory-cache-adapter.ts +5 -0
  12. package/src/cache/adapters/redis-cache-adapter.ts +233 -0
  13. package/src/cache/cache-interfaces.ts +11 -0
  14. package/src/cache/index.ts +2 -0
  15. package/src/core/ast/procedure.ts +21 -0
  16. package/src/core/ast/query.ts +47 -19
  17. package/src/core/ddl/introspect/utils.ts +56 -56
  18. package/src/core/dialect/abstract.ts +560 -547
  19. package/src/core/dialect/base/sql-dialect.ts +43 -29
  20. package/src/core/dialect/mssql/index.ts +369 -232
  21. package/src/core/dialect/mysql/index.ts +99 -7
  22. package/src/core/dialect/postgres/index.ts +121 -60
  23. package/src/core/dialect/sqlite/index.ts +97 -64
  24. package/src/core/execution/db-executor.ts +108 -90
  25. package/src/core/execution/executors/mssql-executor.ts +28 -24
  26. package/src/core/execution/executors/mysql-executor.ts +62 -27
  27. package/src/core/execution/executors/sqlite-executor.ts +10 -9
  28. package/src/index.ts +9 -6
  29. package/src/orm/execute-procedure.ts +77 -0
  30. package/src/orm/execute.ts +74 -73
  31. package/src/orm/interceptor-pipeline.ts +21 -17
  32. package/src/orm/pooled-executor-factory.ts +41 -20
  33. package/src/orm/unit-of-work.ts +6 -4
  34. package/src/query/index.ts +8 -5
  35. package/src/query-builder/delete.ts +3 -2
  36. package/src/query-builder/insert-query-state.ts +47 -19
  37. package/src/query-builder/insert.ts +142 -28
  38. package/src/query-builder/procedure-call.ts +122 -0
  39. package/src/query-builder/select/select-operations.ts +5 -2
  40. package/src/query-builder/select.ts +1146 -1105
  41. package/src/query-builder/update.ts +3 -2
  42. package/src/tree/tree-manager.ts +754 -754
package/dist/index.js CHANGED
@@ -1,5 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
  var __esm = (fn8, res) => function __init() {
4
10
  return fn8 && (res = (0, fn8[__getOwnPropNames(fn8)[0]])(fn8 = 0)), res;
5
11
  };
@@ -1434,6 +1440,9 @@ var Dialect = class _Dialect {
1434
1440
  compileDeleteAst() {
1435
1441
  throw new Error("Not implemented");
1436
1442
  }
1443
+ compileProcedureCall() {
1444
+ throw new Error("Not implemented");
1445
+ }
1437
1446
  }
1438
1447
  return new TestDialect(functionStrategy, tableFunctionStrategy);
1439
1448
  }
@@ -1896,8 +1905,14 @@ var SqlDialectBase = class extends Dialect {
1896
1905
  const table = this.compileTableName(ast.into);
1897
1906
  const columnList = this.compileInsertColumnList(ast.columns);
1898
1907
  const source = this.compileInsertSource(ast.source, ctx);
1908
+ const upsert = this.compileUpsertClause(ast, ctx);
1899
1909
  const returning = this.compileReturning(ast.returning, ctx);
1900
- return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
1910
+ return `INSERT INTO ${table} (${columnList}) ${source}${upsert}${returning}`;
1911
+ }
1912
+ compileUpsertClause(ast, _ctx) {
1913
+ void _ctx;
1914
+ if (!ast.onConflict) return "";
1915
+ throw new Error(`UPSERT/ON CONFLICT is not supported by dialect "${this.dialect}".`);
1901
1916
  }
1902
1917
  compileReturning(returning, ctx) {
1903
1918
  return this.returningStrategy.compileReturning(returning, ctx);
@@ -1916,6 +1931,11 @@ var SqlDialectBase = class extends Dialect {
1916
1931
  compileInsertColumnList(columns) {
1917
1932
  return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1918
1933
  }
1934
+ ensureConflictColumns(clause, message) {
1935
+ if (!clause.target.columns.length) {
1936
+ throw new Error(message);
1937
+ }
1938
+ }
1919
1939
  compileSelectCore(ast, ctx) {
1920
1940
  const columns = this.compileSelectColumns(ast, ctx);
1921
1941
  const from = this.compileFrom(ast.from, ctx);
@@ -2298,9 +2318,52 @@ var PostgresDialect = class extends SqlDialectBase {
2298
2318
  const columns = this.formatReturningColumns(returning);
2299
2319
  return ` RETURNING ${columns}`;
2300
2320
  }
2321
+ compileUpsertClause(ast, ctx) {
2322
+ if (!ast.onConflict) return "";
2323
+ const clause = ast.onConflict;
2324
+ const target = clause.target.constraint ? ` ON CONFLICT ON CONSTRAINT ${this.quoteIdentifier(clause.target.constraint)}` : (() => {
2325
+ this.ensureConflictColumns(
2326
+ clause,
2327
+ "PostgreSQL ON CONFLICT requires conflict columns or a constraint name."
2328
+ );
2329
+ const cols = clause.target.columns.map((col2) => this.quoteIdentifier(col2.name)).join(", ");
2330
+ return ` ON CONFLICT (${cols})`;
2331
+ })();
2332
+ if (clause.action.type === "DoNothing") {
2333
+ return `${target} DO NOTHING`;
2334
+ }
2335
+ if (!clause.action.set.length) {
2336
+ throw new Error("PostgreSQL ON CONFLICT DO UPDATE requires at least one assignment.");
2337
+ }
2338
+ const assignments = this.compileUpdateAssignments(clause.action.set, ast.into, ctx);
2339
+ const where = clause.action.where ? ` WHERE ${this.compileExpression(clause.action.where, ctx)}` : "";
2340
+ return `${target} DO UPDATE SET ${assignments}${where}`;
2341
+ }
2301
2342
  supportsDmlReturningClause() {
2302
2343
  return true;
2303
2344
  }
2345
+ compileProcedureCall(ast) {
2346
+ const ctx = this.createCompilerContext();
2347
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
2348
+ const args = [];
2349
+ for (const param of ast.params) {
2350
+ if (param.direction === "out") continue;
2351
+ if (!param.value) {
2352
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "${param.direction}".`);
2353
+ }
2354
+ args.push(this.compileOperand(param.value, ctx));
2355
+ }
2356
+ const outNames = ast.params.filter((param) => param.direction === "out" || param.direction === "inout").map((param) => param.name);
2357
+ const rawSql = `CALL ${qualifiedName}(${args.join(", ")})`;
2358
+ return {
2359
+ sql: `${rawSql};`,
2360
+ params: [...ctx.params],
2361
+ outParams: {
2362
+ source: outNames.length ? "firstResultSet" : "none",
2363
+ names: outNames
2364
+ }
2365
+ };
2366
+ }
2304
2367
  /**
2305
2368
  * PostgreSQL requires unqualified column names in SET clause
2306
2369
  */
@@ -2400,6 +2463,7 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
2400
2463
  };
2401
2464
 
2402
2465
  // src/core/dialect/mysql/index.ts
2466
+ var sanitizeVariableSuffix = (value) => value.replace(/[^a-zA-Z0-9_]/g, "_");
2403
2467
  var MySqlDialect = class extends SqlDialectBase {
2404
2468
  dialect = "mysql";
2405
2469
  /**
@@ -2425,6 +2489,73 @@ var MySqlDialect = class extends SqlDialectBase {
2425
2489
  const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
2426
2490
  return `${col2}->'${node.path}'`;
2427
2491
  }
2492
+ compileUpsertClause(ast, ctx) {
2493
+ if (!ast.onConflict) return "";
2494
+ const clause = ast.onConflict;
2495
+ if (clause.action.type === "DoNothing") {
2496
+ const noOpColumn = clause.target.columns[0] ?? ast.columns[0];
2497
+ if (!noOpColumn) {
2498
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE requires at least one target column.");
2499
+ }
2500
+ const col2 = this.quoteIdentifier(noOpColumn.name);
2501
+ return ` ON DUPLICATE KEY UPDATE ${col2} = ${col2}`;
2502
+ }
2503
+ if (clause.action.where) {
2504
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE does not support a WHERE clause.");
2505
+ }
2506
+ if (!clause.action.set.length) {
2507
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE requires at least one assignment.");
2508
+ }
2509
+ const assignments = clause.action.set.map((assignment) => {
2510
+ const target = this.quoteIdentifier(assignment.column.name);
2511
+ const value = this.compileOperand(assignment.value, ctx);
2512
+ return `${target} = ${value}`;
2513
+ }).join(", ");
2514
+ return ` ON DUPLICATE KEY UPDATE ${assignments}`;
2515
+ }
2516
+ compileProcedureCall(ast) {
2517
+ const ctx = this.createCompilerContext();
2518
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
2519
+ const prelude = [];
2520
+ const callArgs = [];
2521
+ const outVars = [];
2522
+ ast.params.forEach((param, index) => {
2523
+ const suffix = sanitizeVariableSuffix(param.name || `p${index + 1}`);
2524
+ const variable = `@__metal_${suffix}_${index + 1}`;
2525
+ if (param.direction === "in") {
2526
+ if (!param.value) {
2527
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
2528
+ }
2529
+ callArgs.push(this.compileOperand(param.value, ctx));
2530
+ return;
2531
+ }
2532
+ if (param.direction === "inout") {
2533
+ if (!param.value) {
2534
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
2535
+ }
2536
+ prelude.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
2537
+ }
2538
+ callArgs.push(variable);
2539
+ outVars.push({ variable, name: param.name });
2540
+ });
2541
+ const statements = [];
2542
+ if (prelude.length) {
2543
+ statements.push(...prelude);
2544
+ }
2545
+ statements.push(`CALL ${qualifiedName}(${callArgs.join(", ")});`);
2546
+ if (outVars.length) {
2547
+ const selectOut = outVars.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`).join(", ");
2548
+ statements.push(`SELECT ${selectOut};`);
2549
+ }
2550
+ return {
2551
+ sql: statements.join(" "),
2552
+ params: [...ctx.params],
2553
+ outParams: {
2554
+ source: outVars.length ? "lastResultSet" : "none",
2555
+ names: outVars.map((item) => item.name)
2556
+ }
2557
+ };
2558
+ }
2428
2559
  };
2429
2560
 
2430
2561
  // src/core/dialect/sqlite/functions.ts
@@ -2613,9 +2744,32 @@ var SqliteDialect = class extends SqlDialectBase {
2613
2744
  return `${this.quoteIdentifier(column.name)}${alias}`;
2614
2745
  }).join(", ");
2615
2746
  }
2747
+ compileUpsertClause(ast, ctx) {
2748
+ if (!ast.onConflict) return "";
2749
+ const clause = ast.onConflict;
2750
+ if (clause.target.constraint) {
2751
+ throw new Error("SQLite ON CONFLICT does not support named constraints.");
2752
+ }
2753
+ this.ensureConflictColumns(clause, "SQLite ON CONFLICT requires conflict columns.");
2754
+ const cols = clause.target.columns.map((col2) => this.quoteIdentifier(col2.name)).join(", ");
2755
+ const target = ` ON CONFLICT (${cols})`;
2756
+ if (clause.action.type === "DoNothing") {
2757
+ return `${target} DO NOTHING`;
2758
+ }
2759
+ if (!clause.action.set.length) {
2760
+ throw new Error("SQLite ON CONFLICT DO UPDATE requires at least one assignment.");
2761
+ }
2762
+ const assignments = this.compileUpdateAssignments(clause.action.set, ast.into, ctx);
2763
+ const where = clause.action.where ? ` WHERE ${this.compileExpression(clause.action.where, ctx)}` : "";
2764
+ return `${target} DO UPDATE SET ${assignments}${where}`;
2765
+ }
2616
2766
  supportsDmlReturningClause() {
2617
2767
  return true;
2618
2768
  }
2769
+ compileProcedureCall(_ast) {
2770
+ void _ast;
2771
+ throw new Error("Stored procedures are not supported by the SQLite dialect.");
2772
+ }
2619
2773
  };
2620
2774
 
2621
2775
  // src/core/dialect/mssql/functions.ts
@@ -2734,6 +2888,8 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
2734
2888
  };
2735
2889
 
2736
2890
  // src/core/dialect/mssql/index.ts
2891
+ var sanitizeVariableSuffix2 = (value) => value.replace(/[^a-zA-Z0-9_]/g, "_");
2892
+ var toProcedureParamReference = (value) => value.startsWith("@") ? value : `@${value}`;
2737
2893
  var SqlServerDialect = class extends SqlDialectBase {
2738
2894
  dialect = "mssql";
2739
2895
  /**
@@ -2889,12 +3045,58 @@ var SqlServerDialect = class extends SqlDialectBase {
2889
3045
  if (!ast.columns.length) {
2890
3046
  throw new Error("INSERT queries must specify columns.");
2891
3047
  }
3048
+ if (ast.onConflict) {
3049
+ return this.compileMergeInsert(ast, ctx);
3050
+ }
2892
3051
  const table = this.compileTableName(ast.into);
2893
3052
  const columnList = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
2894
3053
  const output = this.compileReturning(ast.returning, ctx);
2895
3054
  const source = this.compileInsertValues(ast, ctx);
2896
3055
  return `INSERT INTO ${table} (${columnList})${output} ${source}`;
2897
3056
  }
3057
+ compileMergeInsert(ast, ctx) {
3058
+ const clause = ast.onConflict;
3059
+ if (clause.target.constraint) {
3060
+ throw new Error("MSSQL MERGE does not support conflict target by constraint name.");
3061
+ }
3062
+ this.ensureConflictColumns(clause, "MSSQL MERGE requires conflict columns for the ON clause.");
3063
+ const table = this.compileTableName(ast.into);
3064
+ const targetRef = this.quoteIdentifier(ast.into.alias ?? ast.into.name);
3065
+ const sourceAlias = this.quoteIdentifier("src");
3066
+ const sourceColumns = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3067
+ const usingSource = this.compileMergeUsingSource(ast, ctx);
3068
+ const onClause = clause.target.columns.map((column) => `${targetRef}.${this.quoteIdentifier(column.name)} = ${sourceAlias}.${this.quoteIdentifier(column.name)}`).join(" AND ");
3069
+ const branches = [];
3070
+ if (clause.action.type === "DoUpdate") {
3071
+ if (!clause.action.set.length) {
3072
+ throw new Error("MSSQL MERGE WHEN MATCHED UPDATE requires at least one assignment.");
3073
+ }
3074
+ const assignments = clause.action.set.map((assignment) => {
3075
+ const target = `${targetRef}.${this.quoteIdentifier(assignment.column.name)}`;
3076
+ const value = this.compileOperand(assignment.value, ctx);
3077
+ return `${target} = ${value}`;
3078
+ }).join(", ");
3079
+ const guard = clause.action.where ? ` AND ${this.compileExpression(clause.action.where, ctx)}` : "";
3080
+ branches.push(`WHEN MATCHED${guard} THEN UPDATE SET ${assignments}`);
3081
+ }
3082
+ const insertColumns = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3083
+ const insertValues = ast.columns.map((column) => `${sourceAlias}.${this.quoteIdentifier(column.name)}`).join(", ");
3084
+ branches.push(`WHEN NOT MATCHED THEN INSERT (${insertColumns}) VALUES (${insertValues})`);
3085
+ const output = this.compileReturning(ast.returning, ctx);
3086
+ return `MERGE INTO ${table} USING ${usingSource} AS ${sourceAlias} (${sourceColumns}) ON ${onClause} ${branches.join(" ")}${output}`;
3087
+ }
3088
+ compileMergeUsingSource(ast, ctx) {
3089
+ if (ast.source.type === "InsertValues") {
3090
+ if (!ast.source.rows.length) {
3091
+ throw new Error("INSERT ... VALUES requires at least one row.");
3092
+ }
3093
+ const rows = ast.source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
3094
+ return `(VALUES ${rows})`;
3095
+ }
3096
+ const normalized = this.normalizeSelectAst(ast.source.query);
3097
+ const selectSql = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
3098
+ return `(${selectSql})`;
3099
+ }
2898
3100
  compileInsertValues(ast, ctx) {
2899
3101
  const source = ast.source;
2900
3102
  if (source.type === "InsertValues") {
@@ -2917,6 +3119,57 @@ var SqlServerDialect = class extends SqlDialectBase {
2917
3119
  }).join(", ");
2918
3120
  return `WITH ${defs} `;
2919
3121
  }
3122
+ compileProcedureCall(ast) {
3123
+ const ctx = this.createCompilerContext();
3124
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
3125
+ const declarations = [];
3126
+ const assignments = [];
3127
+ const execArgs = [];
3128
+ const outVars = [];
3129
+ ast.params.forEach((param, index) => {
3130
+ const targetParam = toProcedureParamReference(param.name);
3131
+ if (param.direction === "in") {
3132
+ if (!param.value) {
3133
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
3134
+ }
3135
+ execArgs.push(`${targetParam} = ${this.compileOperand(param.value, ctx)}`);
3136
+ return;
3137
+ }
3138
+ if (!param.dbType) {
3139
+ throw new Error(
3140
+ `MSSQL procedure parameter "${param.name}" requires "dbType" for direction "${param.direction}".`
3141
+ );
3142
+ }
3143
+ const suffix = sanitizeVariableSuffix2(param.name || `p${index + 1}`);
3144
+ const variable = `@__metal_${suffix}_${index + 1}`;
3145
+ declarations.push(`DECLARE ${variable} ${param.dbType};`);
3146
+ if (param.direction === "inout") {
3147
+ if (!param.value) {
3148
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
3149
+ }
3150
+ assignments.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
3151
+ }
3152
+ execArgs.push(`${targetParam} = ${variable} OUTPUT`);
3153
+ outVars.push({ variable, name: param.name });
3154
+ });
3155
+ const statements = [];
3156
+ if (declarations.length) statements.push(...declarations);
3157
+ if (assignments.length) statements.push(...assignments);
3158
+ const argsSql = execArgs.length ? ` ${execArgs.join(", ")}` : "";
3159
+ statements.push(`EXEC ${qualifiedName}${argsSql};`);
3160
+ if (outVars.length) {
3161
+ const selectOut = outVars.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`).join(", ");
3162
+ statements.push(`SELECT ${selectOut};`);
3163
+ }
3164
+ return {
3165
+ sql: statements.join(" "),
3166
+ params: [...ctx.params],
3167
+ outParams: {
3168
+ source: outVars.length ? "lastResultSet" : "none",
3169
+ names: outVars.map((item) => item.name)
3170
+ }
3171
+ };
3172
+ }
2920
3173
  };
2921
3174
 
2922
3175
  // src/core/dialect/dialect-factory.ts
@@ -6622,6 +6875,56 @@ var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
6622
6875
  }
6623
6876
  };
6624
6877
 
6878
+ // src/core/execution/db-executor.ts
6879
+ var toExecutionPayload = (resultSets) => {
6880
+ const payload = resultSets;
6881
+ payload.resultSets = resultSets;
6882
+ return payload;
6883
+ };
6884
+ var payloadResultSets = (payload) => payload.resultSets ?? payload;
6885
+ function rowsToQueryResult(rows) {
6886
+ if (rows.length === 0) {
6887
+ return { columns: [], values: [] };
6888
+ }
6889
+ const columns = Object.keys(rows[0]);
6890
+ const values = rows.map((row) => columns.map((c) => row[c]));
6891
+ return { columns, values };
6892
+ }
6893
+ function createExecutorFromQueryRunner(runner) {
6894
+ const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
6895
+ return {
6896
+ capabilities: {
6897
+ transactions: supportsTransactions
6898
+ },
6899
+ async executeSql(sql, params) {
6900
+ const rows = await runner.query(sql, params);
6901
+ const result = rowsToQueryResult(rows);
6902
+ return toExecutionPayload([result]);
6903
+ },
6904
+ async beginTransaction() {
6905
+ if (!supportsTransactions) {
6906
+ throw new Error("Transactions are not supported by this executor");
6907
+ }
6908
+ await runner.beginTransaction.call(runner);
6909
+ },
6910
+ async commitTransaction() {
6911
+ if (!supportsTransactions) {
6912
+ throw new Error("Transactions are not supported by this executor");
6913
+ }
6914
+ await runner.commitTransaction.call(runner);
6915
+ },
6916
+ async rollbackTransaction() {
6917
+ if (!supportsTransactions) {
6918
+ throw new Error("Transactions are not supported by this executor");
6919
+ }
6920
+ await runner.rollbackTransaction.call(runner);
6921
+ },
6922
+ async dispose() {
6923
+ await runner.dispose?.call(runner);
6924
+ }
6925
+ };
6926
+ }
6927
+
6625
6928
  // src/orm/execute.ts
6626
6929
  var flattenResults = (results) => {
6627
6930
  const rows = [];
@@ -6641,7 +6944,7 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
6641
6944
  const ast = qb.getAST();
6642
6945
  const compiled = execCtx.dialect.compileSelect(ast);
6643
6946
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6644
- const rows = flattenResults(executed);
6947
+ const rows = flattenResults(payloadResultSets(executed));
6645
6948
  const lazyRelations = qb.getLazyRelations();
6646
6949
  const lazyRelationOptions = qb.getLazyRelationOptions();
6647
6950
  const includeTree = qb.getIncludeTree();
@@ -6661,7 +6964,7 @@ var executePlainWithContexts = async (execCtx, qb) => {
6661
6964
  const ast = qb.getAST();
6662
6965
  const compiled = execCtx.dialect.compileSelect(ast);
6663
6966
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6664
- const rows = flattenResults(executed);
6967
+ const rows = flattenResults(payloadResultSets(executed));
6665
6968
  if (ast.setOps && ast.setOps.length > 0) {
6666
6969
  return rows;
6667
6970
  }
@@ -6929,7 +7232,8 @@ async function executeCount(context, env, session) {
6929
7232
  };
6930
7233
  const execCtx = session.getExecutionContext();
6931
7234
  const compiled = execCtx.dialect.compileSelect(countQuery);
6932
- const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7235
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7236
+ const results = payloadResultSets(payload);
6933
7237
  const value = results[0]?.values?.[0]?.[0];
6934
7238
  if (typeof value === "number") return value;
6935
7239
  if (typeof value === "bigint") return Number(value);
@@ -6961,7 +7265,8 @@ async function executeCountRows(context, env, session) {
6961
7265
  };
6962
7266
  const execCtx = session.getExecutionContext();
6963
7267
  const compiled = execCtx.dialect.compileSelect(countQuery);
6964
- const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7268
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7269
+ const results = payloadResultSets(payload);
6965
7270
  const value = results[0]?.values?.[0]?.[0];
6966
7271
  if (typeof value === "number") return value;
6967
7272
  if (typeof value === "bigint") return Number(value);
@@ -7935,6 +8240,45 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7935
8240
  * // rows is EntityInstance<UserTable>[] (plain objects)
7936
8241
  * rows[0] instanceof User; // false
7937
8242
  */
8243
+ /**
8244
+ * Executes the query with LIMIT 1 and returns the first result.
8245
+ * Throws if no record is found.
8246
+ *
8247
+ * @param ctx - ORM session context
8248
+ * @returns Promise of a single entity instance
8249
+ * @throws Error if no results are found
8250
+ * @example
8251
+ * const user = await selectFromEntity(User)
8252
+ * .where(eq(users.email, 'alice@example.com'))
8253
+ * .firstOrFail(session);
8254
+ */
8255
+ async firstOrFail(ctx) {
8256
+ const rows = await this.limit(1).execute(ctx);
8257
+ if (rows.length === 0) {
8258
+ throw new Error("No results found");
8259
+ }
8260
+ return rows[0];
8261
+ }
8262
+ /**
8263
+ * Executes the query with LIMIT 1 and returns the first result as a plain object (POJO).
8264
+ * Throws if no record is found.
8265
+ *
8266
+ * @param ctx - ORM session context
8267
+ * @returns Promise of a single plain object
8268
+ * @throws Error if no results are found
8269
+ * @example
8270
+ * const user = await selectFromEntity(User)
8271
+ * .where(eq(users.email, 'alice@example.com'))
8272
+ * .firstOrFailPlain(session);
8273
+ * // user is a plain object, not an instance of User
8274
+ */
8275
+ async firstOrFailPlain(ctx) {
8276
+ const rows = await this.limit(1).executePlain(ctx);
8277
+ if (rows.length === 0) {
8278
+ throw new Error("No results found");
8279
+ }
8280
+ return rows[0];
8281
+ }
7938
8282
  async executePlain(ctx) {
7939
8283
  const builder = this.ensureDefaultSelection();
7940
8284
  const rows = await executeHydratedPlain(ctx, builder);
@@ -8634,9 +8978,95 @@ var InsertQueryState = class _InsertQueryState {
8634
8978
  returning: [...columns]
8635
8979
  });
8636
8980
  }
8981
+ /**
8982
+ * Adds an UPSERT conflict clause to the INSERT query
8983
+ * @param clause - Conflict clause and action
8984
+ * @returns A new InsertQueryState with conflict handling configured
8985
+ */
8986
+ withOnConflict(clause) {
8987
+ return this.clone({
8988
+ ...this.ast,
8989
+ onConflict: {
8990
+ target: {
8991
+ columns: [...clause.target.columns],
8992
+ constraint: clause.target.constraint
8993
+ },
8994
+ action: clause.action.type === "DoUpdate" ? {
8995
+ type: "DoUpdate",
8996
+ set: clause.action.set.map((assignment) => ({
8997
+ column: { ...assignment.column },
8998
+ value: assignment.value
8999
+ })),
9000
+ where: clause.action.where
9001
+ } : { type: "DoNothing" }
9002
+ }
9003
+ });
9004
+ }
8637
9005
  };
8638
9006
 
8639
9007
  // src/query-builder/insert.ts
9008
+ var ConflictBuilder = class {
9009
+ table;
9010
+ columns;
9011
+ constraint;
9012
+ applyClause;
9013
+ constructor(table, columns, constraint, applyClause) {
9014
+ this.table = table;
9015
+ this.columns = columns;
9016
+ this.constraint = constraint;
9017
+ this.applyClause = applyClause;
9018
+ }
9019
+ /**
9020
+ * Adds ON CONFLICT ... DO UPDATE
9021
+ * @param set - Column assignments for update branch
9022
+ * @param where - Optional filter for update branch
9023
+ * @returns InsertQueryBuilder with the upsert clause configured
9024
+ */
9025
+ doUpdate(set, where) {
9026
+ const assignments = this.buildAssignments(set);
9027
+ return this.applyClause({
9028
+ target: this.buildTarget(),
9029
+ action: {
9030
+ type: "DoUpdate",
9031
+ set: assignments,
9032
+ where
9033
+ }
9034
+ });
9035
+ }
9036
+ /**
9037
+ * Adds ON CONFLICT ... DO NOTHING
9038
+ * @returns InsertQueryBuilder with the upsert clause configured
9039
+ */
9040
+ doNothing() {
9041
+ return this.applyClause({
9042
+ target: this.buildTarget(),
9043
+ action: { type: "DoNothing" }
9044
+ });
9045
+ }
9046
+ buildTarget() {
9047
+ return {
9048
+ columns: [...this.columns],
9049
+ constraint: this.constraint
9050
+ };
9051
+ }
9052
+ buildAssignments(set) {
9053
+ const entries = Object.entries(set);
9054
+ if (!entries.length) {
9055
+ throw new Error("ON CONFLICT DO UPDATE requires at least one assignment.");
9056
+ }
9057
+ return entries.map(([columnName, rawValue]) => {
9058
+ if (!isValueOperandInput(rawValue)) {
9059
+ throw new Error(
9060
+ `Invalid upsert value for column "${columnName}": only string, number, boolean, Date, Buffer, null, or OperandNodes are allowed`
9061
+ );
9062
+ }
9063
+ return {
9064
+ column: buildColumnNode(this.table, { name: columnName, table: this.table.name }),
9065
+ value: valueToOperand(rawValue)
9066
+ };
9067
+ });
9068
+ }
9069
+ };
8640
9070
  var InsertQueryBuilder = class _InsertQueryBuilder {
8641
9071
  table;
8642
9072
  state;
@@ -8652,6 +9082,9 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
8652
9082
  clone(state) {
8653
9083
  return new _InsertQueryBuilder(this.table, state);
8654
9084
  }
9085
+ withOnConflict(clause) {
9086
+ return this.clone(this.state.withOnConflict(clause));
9087
+ }
8655
9088
  /**
8656
9089
  * Adds VALUES to the INSERT query
8657
9090
  * @param rowOrRows - Single row object or array of row objects to insert
@@ -8683,6 +9116,21 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
8683
9116
  const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
8684
9117
  return this.clone(this.state.withSelect(ast, nodes));
8685
9118
  }
9119
+ /**
9120
+ * Configures UPSERT conflict handling for INSERT.
9121
+ * @param columns - Conflict target columns (ignored by MySQL)
9122
+ * @param constraint - Named unique/primary constraint (PostgreSQL only)
9123
+ * @returns ConflictBuilder for selecting action (DO UPDATE / DO NOTHING)
9124
+ */
9125
+ onConflict(columns = [], constraint) {
9126
+ const resolvedColumns = columns.length ? this.resolveColumnNodes(columns) : [];
9127
+ return new ConflictBuilder(
9128
+ this.table,
9129
+ resolvedColumns,
9130
+ constraint,
9131
+ (clause) => this.withOnConflict(clause)
9132
+ );
9133
+ }
8686
9134
  /**
8687
9135
  * Adds a RETURNING clause to the INSERT query
8688
9136
  * @param columns - Columns to return after insertion
@@ -8960,7 +9408,8 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
8960
9408
  async execute(session) {
8961
9409
  const execCtx = session.getExecutionContext();
8962
9410
  const compiled = this.compile(execCtx.dialect);
8963
- return execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9411
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9412
+ return payloadResultSets(payload);
8964
9413
  }
8965
9414
  /**
8966
9415
  * Returns the Abstract Syntax Tree (AST) representation of the query
@@ -9150,7 +9599,8 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
9150
9599
  async execute(session) {
9151
9600
  const execCtx = session.getExecutionContext();
9152
9601
  const compiled = this.compile(execCtx.dialect);
9153
- return execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9602
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9603
+ return payloadResultSets(payload);
9154
9604
  }
9155
9605
  /**
9156
9606
  * Returns the Abstract Syntax Tree (AST) representation of the query
@@ -9162,6 +9612,149 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
9162
9612
  };
9163
9613
  var isTableSourceNode2 = (source) => typeof source.type === "string";
9164
9614
 
9615
+ // src/orm/execute-procedure.ts
9616
+ var resolveColumnIndex = (columns, expectedName) => {
9617
+ const exact = columns.findIndex((column) => column === expectedName);
9618
+ if (exact >= 0) return exact;
9619
+ const lowerExpected = expectedName.toLowerCase();
9620
+ return columns.findIndex((column) => column.toLowerCase() === lowerExpected);
9621
+ };
9622
+ var extractOutValues = (compiled, resultSets) => {
9623
+ if (!compiled.outParams.names.length || compiled.outParams.source === "none") {
9624
+ return {};
9625
+ }
9626
+ const sourceSet = compiled.outParams.source === "firstResultSet" ? resultSets[0] : resultSets[resultSets.length - 1];
9627
+ if (!sourceSet) {
9628
+ throw new Error(
9629
+ `Procedure expected OUT parameters in ${compiled.outParams.source}, but no result set was returned.`
9630
+ );
9631
+ }
9632
+ if (!sourceSet.values.length) {
9633
+ throw new Error(
9634
+ `Procedure expected OUT parameters in ${compiled.outParams.source}, but the result set has no rows.`
9635
+ );
9636
+ }
9637
+ const firstRow = sourceSet.values[0];
9638
+ const out = {};
9639
+ for (const expectedName of compiled.outParams.names) {
9640
+ const columnIndex = resolveColumnIndex(sourceSet.columns, expectedName);
9641
+ if (columnIndex < 0) {
9642
+ const available = sourceSet.columns.length ? sourceSet.columns.join(", ") : "(none)";
9643
+ throw new Error(
9644
+ `Procedure OUT parameter "${expectedName}" was not found in ${compiled.outParams.source}. Available columns: ${available}.`
9645
+ );
9646
+ }
9647
+ out[expectedName] = firstRow[columnIndex];
9648
+ }
9649
+ return out;
9650
+ };
9651
+ var executeProcedureAst = async (session, ast) => {
9652
+ const execCtx = session.getExecutionContext();
9653
+ const compiled = execCtx.dialect.compileProcedureCall(ast);
9654
+ const payload = await execCtx.interceptors.run(
9655
+ { sql: compiled.sql, params: compiled.params },
9656
+ execCtx.executor
9657
+ );
9658
+ const resultSets = payloadResultSets(payload);
9659
+ return {
9660
+ resultSets,
9661
+ out: extractOutValues(compiled, resultSets)
9662
+ };
9663
+ };
9664
+
9665
+ // src/query-builder/procedure-call.ts
9666
+ var cloneParam = (param) => ({
9667
+ ...param,
9668
+ value: param.value ? { ...param.value } : void 0
9669
+ });
9670
+ var ProcedureCallBuilder = class _ProcedureCallBuilder {
9671
+ ast;
9672
+ constructor(name, options, ast) {
9673
+ this.ast = ast ?? {
9674
+ type: "ProcedureCall",
9675
+ ref: {
9676
+ name,
9677
+ schema: options?.schema
9678
+ },
9679
+ params: []
9680
+ };
9681
+ }
9682
+ clone(nextParams) {
9683
+ return new _ProcedureCallBuilder(
9684
+ this.ast.ref.name,
9685
+ { schema: this.ast.ref.schema },
9686
+ {
9687
+ ...this.ast,
9688
+ ref: { ...this.ast.ref },
9689
+ params: nextParams
9690
+ }
9691
+ );
9692
+ }
9693
+ in(name, value) {
9694
+ return this.clone([
9695
+ ...this.ast.params.map(cloneParam),
9696
+ {
9697
+ name,
9698
+ direction: "in",
9699
+ value: valueToOperand(value)
9700
+ }
9701
+ ]);
9702
+ }
9703
+ out(name, options) {
9704
+ return this.clone([
9705
+ ...this.ast.params.map(cloneParam),
9706
+ {
9707
+ name,
9708
+ direction: "out",
9709
+ dbType: options?.dbType
9710
+ }
9711
+ ]);
9712
+ }
9713
+ inOut(name, value, options) {
9714
+ return this.clone([
9715
+ ...this.ast.params.map(cloneParam),
9716
+ {
9717
+ name,
9718
+ direction: "inout",
9719
+ value: valueToOperand(value),
9720
+ dbType: options?.dbType
9721
+ }
9722
+ ]);
9723
+ }
9724
+ compile(dialect) {
9725
+ const resolved = resolveDialectInput(dialect);
9726
+ this.validateMssqlOutDbType(resolved);
9727
+ return resolved.compileProcedureCall(this.getAST());
9728
+ }
9729
+ toSql(dialect) {
9730
+ return this.compile(dialect).sql;
9731
+ }
9732
+ getAST() {
9733
+ return {
9734
+ ...this.ast,
9735
+ ref: { ...this.ast.ref },
9736
+ params: this.ast.params.map(cloneParam)
9737
+ };
9738
+ }
9739
+ async execute(session) {
9740
+ this.validateMssqlOutDbType(session.getExecutionContext().dialect);
9741
+ return executeProcedureAst(session, this.getAST());
9742
+ }
9743
+ validateMssqlOutDbType(dialect) {
9744
+ const isMssqlDialect = dialect.constructor.name === "SqlServerDialect";
9745
+ if (!isMssqlDialect) return;
9746
+ for (const param of this.ast.params) {
9747
+ const needsDbType = param.direction === "out" || param.direction === "inout";
9748
+ if (needsDbType && !param.dbType) {
9749
+ throw new Error(
9750
+ `MSSQL requires "dbType" for procedure parameter "${param.name}" with direction "${param.direction}".`
9751
+ );
9752
+ }
9753
+ }
9754
+ }
9755
+ };
9756
+ var callProcedure = (name, options) => new ProcedureCallBuilder(name, options);
9757
+
9165
9758
  // src/query/target.ts
9166
9759
  var resolveEntityTarget = (ctor) => {
9167
9760
  const table = getTableDefFromEntity(ctor);
@@ -12340,7 +12933,8 @@ var UnitOfWork = class {
12340
12933
  * @returns Query results
12341
12934
  */
12342
12935
  async executeCompiled(compiled) {
12343
- return this.executor.executeSql(compiled.sql, compiled.params);
12936
+ const payload = await this.executor.executeSql(compiled.sql, compiled.params);
12937
+ return payloadResultSets(payload);
12344
12938
  }
12345
12939
  /**
12346
12940
  * Gets columns for RETURNING clause.
@@ -13484,9 +14078,9 @@ var InterceptorPipeline = class {
13484
14078
  const dispatch = async () => {
13485
14079
  const interceptor = this.interceptors[i++];
13486
14080
  if (!interceptor) {
13487
- return executor.executeSql(ctx.sql, ctx.params);
14081
+ return toExecutionPayload(await executor.executeSql(ctx.sql, ctx.params));
13488
14082
  }
13489
- return interceptor(ctx, dispatch);
14083
+ return toExecutionPayload(await interceptor(ctx, dispatch));
13490
14084
  };
13491
14085
  return dispatch();
13492
14086
  }
@@ -13609,6 +14203,11 @@ function isValidDuration(value) {
13609
14203
  // src/cache/adapters/memory-cache-adapter.ts
13610
14204
  var MemoryCacheAdapter = class {
13611
14205
  name = "memory";
14206
+ capabilities = {
14207
+ tags: true,
14208
+ prefix: true,
14209
+ ttl: true
14210
+ };
13612
14211
  storage = /* @__PURE__ */ new Map();
13613
14212
  tagIndex = /* @__PURE__ */ new Map();
13614
14213
  async get(key) {
@@ -14735,50 +15334,6 @@ function CEP(options) {
14735
15334
  };
14736
15335
  }
14737
15336
 
14738
- // src/core/execution/db-executor.ts
14739
- function rowsToQueryResult(rows) {
14740
- if (rows.length === 0) {
14741
- return { columns: [], values: [] };
14742
- }
14743
- const columns = Object.keys(rows[0]);
14744
- const values = rows.map((row) => columns.map((c) => row[c]));
14745
- return { columns, values };
14746
- }
14747
- function createExecutorFromQueryRunner(runner) {
14748
- const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
14749
- return {
14750
- capabilities: {
14751
- transactions: supportsTransactions
14752
- },
14753
- async executeSql(sql, params) {
14754
- const rows = await runner.query(sql, params);
14755
- const result = rowsToQueryResult(rows);
14756
- return [result];
14757
- },
14758
- async beginTransaction() {
14759
- if (!supportsTransactions) {
14760
- throw new Error("Transactions are not supported by this executor");
14761
- }
14762
- await runner.beginTransaction.call(runner);
14763
- },
14764
- async commitTransaction() {
14765
- if (!supportsTransactions) {
14766
- throw new Error("Transactions are not supported by this executor");
14767
- }
14768
- await runner.commitTransaction.call(runner);
14769
- },
14770
- async rollbackTransaction() {
14771
- if (!supportsTransactions) {
14772
- throw new Error("Transactions are not supported by this executor");
14773
- }
14774
- await runner.rollbackTransaction.call(runner);
14775
- },
14776
- async dispose() {
14777
- await runner.dispose?.call(runner);
14778
- }
14779
- };
14780
- }
14781
-
14782
15337
  // src/core/execution/pooling/pool.ts
14783
15338
  var deferred = () => {
14784
15339
  let resolve;
@@ -15006,6 +15561,36 @@ function createPostgresExecutor(client) {
15006
15561
  }
15007
15562
 
15008
15563
  // src/core/execution/executors/mysql-executor.ts
15564
+ var isRowObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15565
+ var isRowObjectArray = (value) => Array.isArray(value) && value.every(isRowObject);
15566
+ var isMysqlResultHeader = (value) => isRowObject(value) && ("affectedRows" in value || "insertId" in value || "warningStatus" in value || "serverStatus" in value);
15567
+ var headerToQueryResult = (header) => ({
15568
+ columns: [],
15569
+ values: [],
15570
+ meta: {
15571
+ insertId: header.insertId,
15572
+ rowsAffected: header.affectedRows
15573
+ }
15574
+ });
15575
+ var normalizeMysqlResults = (rows) => {
15576
+ if (!Array.isArray(rows)) {
15577
+ return isMysqlResultHeader(rows) ? [headerToQueryResult(rows)] : [rowsToQueryResult([])];
15578
+ }
15579
+ if (isRowObjectArray(rows)) {
15580
+ return [rowsToQueryResult(rows)];
15581
+ }
15582
+ const normalized = [];
15583
+ for (const chunk of rows) {
15584
+ if (isRowObjectArray(chunk)) {
15585
+ normalized.push(rowsToQueryResult(chunk));
15586
+ continue;
15587
+ }
15588
+ if (isMysqlResultHeader(chunk)) {
15589
+ normalized.push(headerToQueryResult(chunk));
15590
+ }
15591
+ }
15592
+ return normalized.length ? normalized : [rowsToQueryResult([])];
15593
+ };
15009
15594
  function createMysqlExecutor(client) {
15010
15595
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
15011
15596
  return {
@@ -15014,21 +15599,7 @@ function createMysqlExecutor(client) {
15014
15599
  },
15015
15600
  async executeSql(sql, params) {
15016
15601
  const [rows] = await client.query(sql, params);
15017
- if (!Array.isArray(rows)) {
15018
- const header = rows;
15019
- return [{
15020
- columns: [],
15021
- values: [],
15022
- meta: {
15023
- insertId: header.insertId,
15024
- rowsAffected: header.affectedRows
15025
- }
15026
- }];
15027
- }
15028
- const result = rowsToQueryResult(
15029
- rows
15030
- );
15031
- return [result];
15602
+ return toExecutionPayload(normalizeMysqlResults(rows));
15032
15603
  },
15033
15604
  async beginTransaction() {
15034
15605
  if (!supportsTransactions) {
@@ -15063,7 +15634,7 @@ function createSqliteExecutor(client) {
15063
15634
  async executeSql(sql, params) {
15064
15635
  const rows = await client.all(sql, params);
15065
15636
  const result = rowsToQueryResult(rows);
15066
- return [result];
15637
+ return toExecutionPayload([result]);
15067
15638
  },
15068
15639
  async beginTransaction() {
15069
15640
  if (!supportsTransactions) {
@@ -15096,9 +15667,9 @@ function createMssqlExecutor(client) {
15096
15667
  transactions: supportsTransactions
15097
15668
  },
15098
15669
  async executeSql(sql, params) {
15099
- const { recordset } = await client.query(sql, params);
15100
- const result = rowsToQueryResult(recordset ?? []);
15101
- return [result];
15670
+ const { recordset, recordsets } = await client.query(sql, params);
15671
+ const sets = Array.isArray(recordsets) ? recordsets : [recordset ?? []];
15672
+ return toExecutionPayload(sets.map((set) => rowsToQueryResult(set ?? [])));
15102
15673
  },
15103
15674
  async beginTransaction() {
15104
15675
  if (!supportsTransactions) {
@@ -15164,7 +15735,7 @@ function createTediousMssqlClient(connection, { Request, TYPES }, options) {
15164
15735
  connection.execSql(request);
15165
15736
  }
15166
15737
  );
15167
- return { recordset: rows };
15738
+ return { recordset: rows, recordsets: [rows] };
15168
15739
  },
15169
15740
  beginTransaction: connection.beginTransaction ? () => new Promise((resolve, reject) => {
15170
15741
  connection.beginTransaction(
@@ -15189,6 +15760,8 @@ function createTediousExecutor(connection, module, options) {
15189
15760
  }
15190
15761
 
15191
15762
  // src/orm/pooled-executor-factory.ts
15763
+ var isQueryResult = (value) => typeof value === "object" && value !== null && Array.isArray(value.columns) && Array.isArray(value.values);
15764
+ var isRowArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item));
15192
15765
  function createPooledExecutorFactory(opts) {
15193
15766
  const { pool, adapter } = opts;
15194
15767
  const makeExecutor = (mode) => {
@@ -15200,7 +15773,13 @@ function createPooledExecutorFactory(opts) {
15200
15773
  };
15201
15774
  const executeWithConn = async (conn, sql, params) => {
15202
15775
  const rows = await adapter.query(conn, sql, params);
15203
- return [rowsToQueryResult(rows)];
15776
+ if (Array.isArray(rows) && rows.length > 0 && rows.every(isQueryResult)) {
15777
+ return toExecutionPayload(rows);
15778
+ }
15779
+ if (Array.isArray(rows) && rows.length > 0 && rows.every(isRowArray)) {
15780
+ return toExecutionPayload(rows.map((set) => rowsToQueryResult(set)));
15781
+ }
15782
+ return toExecutionPayload([rowsToQueryResult(rows)]);
15204
15783
  };
15205
15784
  return {
15206
15785
  capabilities: { transactions: true },
@@ -18462,6 +19041,11 @@ var KeyvCacheAdapter = class {
18462
19041
  this.keyv = keyv;
18463
19042
  }
18464
19043
  name = "keyv";
19044
+ capabilities = {
19045
+ tags: false,
19046
+ prefix: true,
19047
+ ttl: true
19048
+ };
18465
19049
  async get(key) {
18466
19050
  return this.keyv.get(key);
18467
19051
  }
@@ -18505,6 +19089,152 @@ var KeyvCacheAdapter = class {
18505
19089
  }
18506
19090
  };
18507
19091
 
19092
+ // src/cache/adapters/redis-cache-adapter.ts
19093
+ var RedisCacheAdapter = class {
19094
+ name = "redis";
19095
+ capabilities = {
19096
+ tags: true,
19097
+ prefix: true,
19098
+ ttl: true
19099
+ };
19100
+ redis;
19101
+ ownsConnection;
19102
+ tagPrefix;
19103
+ /**
19104
+ * Cria um adapter Redis
19105
+ *
19106
+ * @param redis - Instância do ioredis OU opções de conexão
19107
+ * @param options - Opções adicionais
19108
+ * @param options.tagPrefix - Prefixo para chaves de tag (default: 'tag:')
19109
+ *
19110
+ * Exemplos:
19111
+ *
19112
+ * // Com instância existente (recomendado para connection pooling):
19113
+ * const redis = new Redis({ host: 'localhost', port: 6379 });
19114
+ * const adapter = new RedisCacheAdapter(redis);
19115
+ *
19116
+ * // Com opções (adapter gerencia conexão):
19117
+ * const adapter = new RedisCacheAdapter({ host: 'localhost', port: 6379 });
19118
+ *
19119
+ * // Para testes com ioredis-mock:
19120
+ * import Redis from 'ioredis-mock';
19121
+ * const adapter = new RedisCacheAdapter(new Redis());
19122
+ */
19123
+ constructor(redis, options) {
19124
+ this.tagPrefix = options?.tagPrefix ?? "tag:";
19125
+ if (this.isRedisInstance(redis)) {
19126
+ this.redis = redis;
19127
+ this.ownsConnection = false;
19128
+ } else {
19129
+ this.redis = this.createRedis(redis);
19130
+ this.ownsConnection = true;
19131
+ }
19132
+ }
19133
+ isRedisInstance(obj) {
19134
+ return typeof obj === "object" && obj !== null && "get" in obj && "set" in obj && "del" in obj && typeof obj.get === "function";
19135
+ }
19136
+ createRedis(options) {
19137
+ try {
19138
+ const Redis = __require("ioredis");
19139
+ return new Redis(options);
19140
+ } catch {
19141
+ throw new Error(
19142
+ "ioredis is required for RedisCacheAdapter. Install it with: npm install ioredis"
19143
+ );
19144
+ }
19145
+ }
19146
+ async get(key) {
19147
+ const value = await this.redis.get(key);
19148
+ if (value === null) {
19149
+ return void 0;
19150
+ }
19151
+ try {
19152
+ return JSON.parse(value);
19153
+ } catch {
19154
+ return void 0;
19155
+ }
19156
+ }
19157
+ async has(key) {
19158
+ const value = await this.redis.get(key);
19159
+ return value !== null;
19160
+ }
19161
+ async set(key, value, ttlMs, tags) {
19162
+ const serialized = JSON.stringify(value);
19163
+ if (ttlMs) {
19164
+ await this.redis.set(key, serialized, "PX", ttlMs);
19165
+ } else {
19166
+ await this.redis.set(key, serialized);
19167
+ }
19168
+ if (tags && tags.length > 0) {
19169
+ await this.registerTags(key, tags);
19170
+ }
19171
+ }
19172
+ async delete(key) {
19173
+ await this.redis.del(key);
19174
+ }
19175
+ async invalidate(key) {
19176
+ await this.delete(key);
19177
+ }
19178
+ async invalidateTags(tags) {
19179
+ const keysToDelete = /* @__PURE__ */ new Set();
19180
+ for (const tag of tags) {
19181
+ const tagKey = `${this.tagPrefix}${tag}`;
19182
+ const keys = await this.redis.smembers(tagKey);
19183
+ for (const key of keys) {
19184
+ keysToDelete.add(key);
19185
+ }
19186
+ await this.redis.del(tagKey);
19187
+ }
19188
+ if (keysToDelete.size > 0) {
19189
+ await this.redis.del(...Array.from(keysToDelete));
19190
+ }
19191
+ }
19192
+ async invalidatePrefix(prefix) {
19193
+ const keysToDelete = [];
19194
+ let cursor = "0";
19195
+ do {
19196
+ const [nextCursor, keys] = await this.redis.scan(
19197
+ cursor,
19198
+ "MATCH",
19199
+ `${prefix}*`,
19200
+ "COUNT",
19201
+ 100
19202
+ );
19203
+ cursor = nextCursor;
19204
+ keysToDelete.push(...keys);
19205
+ } while (cursor !== "0");
19206
+ if (keysToDelete.length > 0) {
19207
+ const batchSize = 1e3;
19208
+ for (let i = 0; i < keysToDelete.length; i += batchSize) {
19209
+ const batch = keysToDelete.slice(i, i + batchSize);
19210
+ await this.redis.del(...batch);
19211
+ }
19212
+ }
19213
+ }
19214
+ async registerTags(key, tags) {
19215
+ for (const tag of tags) {
19216
+ const tagKey = `${this.tagPrefix}${tag}`;
19217
+ await this.redis.sadd(tagKey, key);
19218
+ }
19219
+ }
19220
+ async dispose() {
19221
+ if (this.ownsConnection) {
19222
+ try {
19223
+ await this.redis.quit();
19224
+ } catch {
19225
+ this.redis.disconnect?.();
19226
+ }
19227
+ }
19228
+ }
19229
+ /**
19230
+ * Retorna a instância Redis subjacente
19231
+ * Útil para operações avançadas ou health checks
19232
+ */
19233
+ getRedis() {
19234
+ return this.redis;
19235
+ }
19236
+ };
19237
+
18508
19238
  // src/cache/tag-index.ts
18509
19239
  var TagIndex = class {
18510
19240
  tagToKeys = /* @__PURE__ */ new Map();
@@ -18624,6 +19354,7 @@ export {
18624
19354
  CPF,
18625
19355
  Capitalize,
18626
19356
  Column,
19357
+ ConflictBuilder,
18627
19358
  ConstructorMaterializationStrategy,
18628
19359
  DEFAULT_TREE_CONFIG,
18629
19360
  DateTimeTypeStrategy,
@@ -18656,8 +19387,10 @@ export {
18656
19387
  Pool,
18657
19388
  PostgresDialect,
18658
19389
  PrimaryKey,
19390
+ ProcedureCallBuilder,
18659
19391
  PrototypeMaterializationStrategy,
18660
19392
  QueryCacheManager,
19393
+ RedisCacheAdapter,
18661
19394
  RelationKinds,
18662
19395
  STANDARD_COLUMN_TYPES,
18663
19396
  SelectQueryBuilder,
@@ -18704,6 +19437,7 @@ export {
18704
19437
  buildScopeConditions,
18705
19438
  calculateRowDepths,
18706
19439
  calculateTotalPages,
19440
+ callProcedure,
18707
19441
  canonicalizeSchema,
18708
19442
  caseWhen,
18709
19443
  cast,
@@ -18776,6 +19510,7 @@ export {
18776
19510
  executeHydratedPlain,
18777
19511
  executeHydratedPlainWithContexts,
18778
19512
  executeHydratedWithContexts,
19513
+ executeProcedureAst,
18779
19514
  executeSchemaSql,
18780
19515
  executeSchemaSqlFor,
18781
19516
  exists,
@@ -18903,6 +19638,7 @@ export {
18903
19638
  paginationParamsSchema,
18904
19639
  parameterToRef,
18905
19640
  parseDuration,
19641
+ payloadResultSets,
18906
19642
  pi,
18907
19643
  pick,
18908
19644
  position,
@@ -18962,6 +19698,7 @@ export {
18962
19698
  threadResults,
18963
19699
  threadedNodeToOpenApiSchema,
18964
19700
  toColumnRef,
19701
+ toExecutionPayload,
18965
19702
  toPagedResponse,
18966
19703
  toPagedResponseBuilder,
18967
19704
  toPaginationParams,