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.cjs CHANGED
@@ -55,6 +55,7 @@ __export(index_exports, {
55
55
  CPF: () => CPF,
56
56
  Capitalize: () => Capitalize,
57
57
  Column: () => Column,
58
+ ConflictBuilder: () => ConflictBuilder,
58
59
  ConstructorMaterializationStrategy: () => ConstructorMaterializationStrategy,
59
60
  DEFAULT_TREE_CONFIG: () => DEFAULT_TREE_CONFIG,
60
61
  DateTimeTypeStrategy: () => DateTimeTypeStrategy,
@@ -87,8 +88,10 @@ __export(index_exports, {
87
88
  Pool: () => Pool,
88
89
  PostgresDialect: () => PostgresDialect,
89
90
  PrimaryKey: () => PrimaryKey,
91
+ ProcedureCallBuilder: () => ProcedureCallBuilder,
90
92
  PrototypeMaterializationStrategy: () => PrototypeMaterializationStrategy,
91
93
  QueryCacheManager: () => QueryCacheManager,
94
+ RedisCacheAdapter: () => RedisCacheAdapter,
92
95
  RelationKinds: () => RelationKinds,
93
96
  STANDARD_COLUMN_TYPES: () => STANDARD_COLUMN_TYPES,
94
97
  SelectQueryBuilder: () => SelectQueryBuilder,
@@ -135,6 +138,7 @@ __export(index_exports, {
135
138
  buildScopeConditions: () => buildScopeConditions,
136
139
  calculateRowDepths: () => calculateRowDepths,
137
140
  calculateTotalPages: () => calculateTotalPages,
141
+ callProcedure: () => callProcedure,
138
142
  canonicalizeSchema: () => canonicalizeSchema,
139
143
  caseWhen: () => caseWhen,
140
144
  cast: () => cast,
@@ -207,6 +211,7 @@ __export(index_exports, {
207
211
  executeHydratedPlain: () => executeHydratedPlain,
208
212
  executeHydratedPlainWithContexts: () => executeHydratedPlainWithContexts,
209
213
  executeHydratedWithContexts: () => executeHydratedWithContexts,
214
+ executeProcedureAst: () => executeProcedureAst,
210
215
  executeSchemaSql: () => executeSchemaSql,
211
216
  executeSchemaSqlFor: () => executeSchemaSqlFor,
212
217
  exists: () => exists,
@@ -334,6 +339,7 @@ __export(index_exports, {
334
339
  paginationParamsSchema: () => paginationParamsSchema,
335
340
  parameterToRef: () => parameterToRef,
336
341
  parseDuration: () => parseDuration,
342
+ payloadResultSets: () => payloadResultSets,
337
343
  pi: () => pi,
338
344
  pick: () => pick,
339
345
  position: () => position,
@@ -393,6 +399,7 @@ __export(index_exports, {
393
399
  threadResults: () => threadResults,
394
400
  threadedNodeToOpenApiSchema: () => threadedNodeToOpenApiSchema,
395
401
  toColumnRef: () => toColumnRef,
402
+ toExecutionPayload: () => toExecutionPayload,
396
403
  toPagedResponse: () => toPagedResponse,
397
404
  toPagedResponseBuilder: () => toPagedResponseBuilder,
398
405
  toPaginationParams: () => toPaginationParams,
@@ -1834,6 +1841,9 @@ var Dialect = class _Dialect {
1834
1841
  compileDeleteAst() {
1835
1842
  throw new Error("Not implemented");
1836
1843
  }
1844
+ compileProcedureCall() {
1845
+ throw new Error("Not implemented");
1846
+ }
1837
1847
  }
1838
1848
  return new TestDialect(functionStrategy, tableFunctionStrategy);
1839
1849
  }
@@ -2296,8 +2306,14 @@ var SqlDialectBase = class extends Dialect {
2296
2306
  const table = this.compileTableName(ast.into);
2297
2307
  const columnList = this.compileInsertColumnList(ast.columns);
2298
2308
  const source = this.compileInsertSource(ast.source, ctx);
2309
+ const upsert = this.compileUpsertClause(ast, ctx);
2299
2310
  const returning = this.compileReturning(ast.returning, ctx);
2300
- return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
2311
+ return `INSERT INTO ${table} (${columnList}) ${source}${upsert}${returning}`;
2312
+ }
2313
+ compileUpsertClause(ast, _ctx) {
2314
+ void _ctx;
2315
+ if (!ast.onConflict) return "";
2316
+ throw new Error(`UPSERT/ON CONFLICT is not supported by dialect "${this.dialect}".`);
2301
2317
  }
2302
2318
  compileReturning(returning, ctx) {
2303
2319
  return this.returningStrategy.compileReturning(returning, ctx);
@@ -2316,6 +2332,11 @@ var SqlDialectBase = class extends Dialect {
2316
2332
  compileInsertColumnList(columns) {
2317
2333
  return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
2318
2334
  }
2335
+ ensureConflictColumns(clause, message) {
2336
+ if (!clause.target.columns.length) {
2337
+ throw new Error(message);
2338
+ }
2339
+ }
2319
2340
  compileSelectCore(ast, ctx) {
2320
2341
  const columns = this.compileSelectColumns(ast, ctx);
2321
2342
  const from = this.compileFrom(ast.from, ctx);
@@ -2698,9 +2719,52 @@ var PostgresDialect = class extends SqlDialectBase {
2698
2719
  const columns = this.formatReturningColumns(returning);
2699
2720
  return ` RETURNING ${columns}`;
2700
2721
  }
2722
+ compileUpsertClause(ast, ctx) {
2723
+ if (!ast.onConflict) return "";
2724
+ const clause = ast.onConflict;
2725
+ const target = clause.target.constraint ? ` ON CONFLICT ON CONSTRAINT ${this.quoteIdentifier(clause.target.constraint)}` : (() => {
2726
+ this.ensureConflictColumns(
2727
+ clause,
2728
+ "PostgreSQL ON CONFLICT requires conflict columns or a constraint name."
2729
+ );
2730
+ const cols = clause.target.columns.map((col2) => this.quoteIdentifier(col2.name)).join(", ");
2731
+ return ` ON CONFLICT (${cols})`;
2732
+ })();
2733
+ if (clause.action.type === "DoNothing") {
2734
+ return `${target} DO NOTHING`;
2735
+ }
2736
+ if (!clause.action.set.length) {
2737
+ throw new Error("PostgreSQL ON CONFLICT DO UPDATE requires at least one assignment.");
2738
+ }
2739
+ const assignments = this.compileUpdateAssignments(clause.action.set, ast.into, ctx);
2740
+ const where = clause.action.where ? ` WHERE ${this.compileExpression(clause.action.where, ctx)}` : "";
2741
+ return `${target} DO UPDATE SET ${assignments}${where}`;
2742
+ }
2701
2743
  supportsDmlReturningClause() {
2702
2744
  return true;
2703
2745
  }
2746
+ compileProcedureCall(ast) {
2747
+ const ctx = this.createCompilerContext();
2748
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
2749
+ const args = [];
2750
+ for (const param of ast.params) {
2751
+ if (param.direction === "out") continue;
2752
+ if (!param.value) {
2753
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "${param.direction}".`);
2754
+ }
2755
+ args.push(this.compileOperand(param.value, ctx));
2756
+ }
2757
+ const outNames = ast.params.filter((param) => param.direction === "out" || param.direction === "inout").map((param) => param.name);
2758
+ const rawSql = `CALL ${qualifiedName}(${args.join(", ")})`;
2759
+ return {
2760
+ sql: `${rawSql};`,
2761
+ params: [...ctx.params],
2762
+ outParams: {
2763
+ source: outNames.length ? "firstResultSet" : "none",
2764
+ names: outNames
2765
+ }
2766
+ };
2767
+ }
2704
2768
  /**
2705
2769
  * PostgreSQL requires unqualified column names in SET clause
2706
2770
  */
@@ -2800,6 +2864,7 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
2800
2864
  };
2801
2865
 
2802
2866
  // src/core/dialect/mysql/index.ts
2867
+ var sanitizeVariableSuffix = (value) => value.replace(/[^a-zA-Z0-9_]/g, "_");
2803
2868
  var MySqlDialect = class extends SqlDialectBase {
2804
2869
  dialect = "mysql";
2805
2870
  /**
@@ -2825,6 +2890,73 @@ var MySqlDialect = class extends SqlDialectBase {
2825
2890
  const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
2826
2891
  return `${col2}->'${node.path}'`;
2827
2892
  }
2893
+ compileUpsertClause(ast, ctx) {
2894
+ if (!ast.onConflict) return "";
2895
+ const clause = ast.onConflict;
2896
+ if (clause.action.type === "DoNothing") {
2897
+ const noOpColumn = clause.target.columns[0] ?? ast.columns[0];
2898
+ if (!noOpColumn) {
2899
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE requires at least one target column.");
2900
+ }
2901
+ const col2 = this.quoteIdentifier(noOpColumn.name);
2902
+ return ` ON DUPLICATE KEY UPDATE ${col2} = ${col2}`;
2903
+ }
2904
+ if (clause.action.where) {
2905
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE does not support a WHERE clause.");
2906
+ }
2907
+ if (!clause.action.set.length) {
2908
+ throw new Error("MySQL ON DUPLICATE KEY UPDATE requires at least one assignment.");
2909
+ }
2910
+ const assignments = clause.action.set.map((assignment) => {
2911
+ const target = this.quoteIdentifier(assignment.column.name);
2912
+ const value = this.compileOperand(assignment.value, ctx);
2913
+ return `${target} = ${value}`;
2914
+ }).join(", ");
2915
+ return ` ON DUPLICATE KEY UPDATE ${assignments}`;
2916
+ }
2917
+ compileProcedureCall(ast) {
2918
+ const ctx = this.createCompilerContext();
2919
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
2920
+ const prelude = [];
2921
+ const callArgs = [];
2922
+ const outVars = [];
2923
+ ast.params.forEach((param, index) => {
2924
+ const suffix = sanitizeVariableSuffix(param.name || `p${index + 1}`);
2925
+ const variable = `@__metal_${suffix}_${index + 1}`;
2926
+ if (param.direction === "in") {
2927
+ if (!param.value) {
2928
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
2929
+ }
2930
+ callArgs.push(this.compileOperand(param.value, ctx));
2931
+ return;
2932
+ }
2933
+ if (param.direction === "inout") {
2934
+ if (!param.value) {
2935
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
2936
+ }
2937
+ prelude.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
2938
+ }
2939
+ callArgs.push(variable);
2940
+ outVars.push({ variable, name: param.name });
2941
+ });
2942
+ const statements = [];
2943
+ if (prelude.length) {
2944
+ statements.push(...prelude);
2945
+ }
2946
+ statements.push(`CALL ${qualifiedName}(${callArgs.join(", ")});`);
2947
+ if (outVars.length) {
2948
+ const selectOut = outVars.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`).join(", ");
2949
+ statements.push(`SELECT ${selectOut};`);
2950
+ }
2951
+ return {
2952
+ sql: statements.join(" "),
2953
+ params: [...ctx.params],
2954
+ outParams: {
2955
+ source: outVars.length ? "lastResultSet" : "none",
2956
+ names: outVars.map((item) => item.name)
2957
+ }
2958
+ };
2959
+ }
2828
2960
  };
2829
2961
 
2830
2962
  // src/core/dialect/sqlite/functions.ts
@@ -3013,9 +3145,32 @@ var SqliteDialect = class extends SqlDialectBase {
3013
3145
  return `${this.quoteIdentifier(column.name)}${alias}`;
3014
3146
  }).join(", ");
3015
3147
  }
3148
+ compileUpsertClause(ast, ctx) {
3149
+ if (!ast.onConflict) return "";
3150
+ const clause = ast.onConflict;
3151
+ if (clause.target.constraint) {
3152
+ throw new Error("SQLite ON CONFLICT does not support named constraints.");
3153
+ }
3154
+ this.ensureConflictColumns(clause, "SQLite ON CONFLICT requires conflict columns.");
3155
+ const cols = clause.target.columns.map((col2) => this.quoteIdentifier(col2.name)).join(", ");
3156
+ const target = ` ON CONFLICT (${cols})`;
3157
+ if (clause.action.type === "DoNothing") {
3158
+ return `${target} DO NOTHING`;
3159
+ }
3160
+ if (!clause.action.set.length) {
3161
+ throw new Error("SQLite ON CONFLICT DO UPDATE requires at least one assignment.");
3162
+ }
3163
+ const assignments = this.compileUpdateAssignments(clause.action.set, ast.into, ctx);
3164
+ const where = clause.action.where ? ` WHERE ${this.compileExpression(clause.action.where, ctx)}` : "";
3165
+ return `${target} DO UPDATE SET ${assignments}${where}`;
3166
+ }
3016
3167
  supportsDmlReturningClause() {
3017
3168
  return true;
3018
3169
  }
3170
+ compileProcedureCall(_ast) {
3171
+ void _ast;
3172
+ throw new Error("Stored procedures are not supported by the SQLite dialect.");
3173
+ }
3019
3174
  };
3020
3175
 
3021
3176
  // src/core/dialect/mssql/functions.ts
@@ -3134,6 +3289,8 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
3134
3289
  };
3135
3290
 
3136
3291
  // src/core/dialect/mssql/index.ts
3292
+ var sanitizeVariableSuffix2 = (value) => value.replace(/[^a-zA-Z0-9_]/g, "_");
3293
+ var toProcedureParamReference = (value) => value.startsWith("@") ? value : `@${value}`;
3137
3294
  var SqlServerDialect = class extends SqlDialectBase {
3138
3295
  dialect = "mssql";
3139
3296
  /**
@@ -3289,12 +3446,58 @@ var SqlServerDialect = class extends SqlDialectBase {
3289
3446
  if (!ast.columns.length) {
3290
3447
  throw new Error("INSERT queries must specify columns.");
3291
3448
  }
3449
+ if (ast.onConflict) {
3450
+ return this.compileMergeInsert(ast, ctx);
3451
+ }
3292
3452
  const table = this.compileTableName(ast.into);
3293
3453
  const columnList = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3294
3454
  const output = this.compileReturning(ast.returning, ctx);
3295
3455
  const source = this.compileInsertValues(ast, ctx);
3296
3456
  return `INSERT INTO ${table} (${columnList})${output} ${source}`;
3297
3457
  }
3458
+ compileMergeInsert(ast, ctx) {
3459
+ const clause = ast.onConflict;
3460
+ if (clause.target.constraint) {
3461
+ throw new Error("MSSQL MERGE does not support conflict target by constraint name.");
3462
+ }
3463
+ this.ensureConflictColumns(clause, "MSSQL MERGE requires conflict columns for the ON clause.");
3464
+ const table = this.compileTableName(ast.into);
3465
+ const targetRef = this.quoteIdentifier(ast.into.alias ?? ast.into.name);
3466
+ const sourceAlias = this.quoteIdentifier("src");
3467
+ const sourceColumns = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3468
+ const usingSource = this.compileMergeUsingSource(ast, ctx);
3469
+ const onClause = clause.target.columns.map((column) => `${targetRef}.${this.quoteIdentifier(column.name)} = ${sourceAlias}.${this.quoteIdentifier(column.name)}`).join(" AND ");
3470
+ const branches = [];
3471
+ if (clause.action.type === "DoUpdate") {
3472
+ if (!clause.action.set.length) {
3473
+ throw new Error("MSSQL MERGE WHEN MATCHED UPDATE requires at least one assignment.");
3474
+ }
3475
+ const assignments = clause.action.set.map((assignment) => {
3476
+ const target = `${targetRef}.${this.quoteIdentifier(assignment.column.name)}`;
3477
+ const value = this.compileOperand(assignment.value, ctx);
3478
+ return `${target} = ${value}`;
3479
+ }).join(", ");
3480
+ const guard = clause.action.where ? ` AND ${this.compileExpression(clause.action.where, ctx)}` : "";
3481
+ branches.push(`WHEN MATCHED${guard} THEN UPDATE SET ${assignments}`);
3482
+ }
3483
+ const insertColumns = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3484
+ const insertValues = ast.columns.map((column) => `${sourceAlias}.${this.quoteIdentifier(column.name)}`).join(", ");
3485
+ branches.push(`WHEN NOT MATCHED THEN INSERT (${insertColumns}) VALUES (${insertValues})`);
3486
+ const output = this.compileReturning(ast.returning, ctx);
3487
+ return `MERGE INTO ${table} USING ${usingSource} AS ${sourceAlias} (${sourceColumns}) ON ${onClause} ${branches.join(" ")}${output}`;
3488
+ }
3489
+ compileMergeUsingSource(ast, ctx) {
3490
+ if (ast.source.type === "InsertValues") {
3491
+ if (!ast.source.rows.length) {
3492
+ throw new Error("INSERT ... VALUES requires at least one row.");
3493
+ }
3494
+ const rows = ast.source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
3495
+ return `(VALUES ${rows})`;
3496
+ }
3497
+ const normalized = this.normalizeSelectAst(ast.source.query);
3498
+ const selectSql = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
3499
+ return `(${selectSql})`;
3500
+ }
3298
3501
  compileInsertValues(ast, ctx) {
3299
3502
  const source = ast.source;
3300
3503
  if (source.type === "InsertValues") {
@@ -3317,6 +3520,57 @@ var SqlServerDialect = class extends SqlDialectBase {
3317
3520
  }).join(", ");
3318
3521
  return `WITH ${defs} `;
3319
3522
  }
3523
+ compileProcedureCall(ast) {
3524
+ const ctx = this.createCompilerContext();
3525
+ const qualifiedName = ast.ref.schema ? `${this.quoteIdentifier(ast.ref.schema)}.${this.quoteIdentifier(ast.ref.name)}` : this.quoteIdentifier(ast.ref.name);
3526
+ const declarations = [];
3527
+ const assignments = [];
3528
+ const execArgs = [];
3529
+ const outVars = [];
3530
+ ast.params.forEach((param, index) => {
3531
+ const targetParam = toProcedureParamReference(param.name);
3532
+ if (param.direction === "in") {
3533
+ if (!param.value) {
3534
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "in".`);
3535
+ }
3536
+ execArgs.push(`${targetParam} = ${this.compileOperand(param.value, ctx)}`);
3537
+ return;
3538
+ }
3539
+ if (!param.dbType) {
3540
+ throw new Error(
3541
+ `MSSQL procedure parameter "${param.name}" requires "dbType" for direction "${param.direction}".`
3542
+ );
3543
+ }
3544
+ const suffix = sanitizeVariableSuffix2(param.name || `p${index + 1}`);
3545
+ const variable = `@__metal_${suffix}_${index + 1}`;
3546
+ declarations.push(`DECLARE ${variable} ${param.dbType};`);
3547
+ if (param.direction === "inout") {
3548
+ if (!param.value) {
3549
+ throw new Error(`Procedure parameter "${param.name}" requires a value for direction "inout".`);
3550
+ }
3551
+ assignments.push(`SET ${variable} = ${this.compileOperand(param.value, ctx)};`);
3552
+ }
3553
+ execArgs.push(`${targetParam} = ${variable} OUTPUT`);
3554
+ outVars.push({ variable, name: param.name });
3555
+ });
3556
+ const statements = [];
3557
+ if (declarations.length) statements.push(...declarations);
3558
+ if (assignments.length) statements.push(...assignments);
3559
+ const argsSql = execArgs.length ? ` ${execArgs.join(", ")}` : "";
3560
+ statements.push(`EXEC ${qualifiedName}${argsSql};`);
3561
+ if (outVars.length) {
3562
+ const selectOut = outVars.map(({ variable, name }) => `${variable} AS ${this.quoteIdentifier(name)}`).join(", ");
3563
+ statements.push(`SELECT ${selectOut};`);
3564
+ }
3565
+ return {
3566
+ sql: statements.join(" "),
3567
+ params: [...ctx.params],
3568
+ outParams: {
3569
+ source: outVars.length ? "lastResultSet" : "none",
3570
+ names: outVars.map((item) => item.name)
3571
+ }
3572
+ };
3573
+ }
3320
3574
  };
3321
3575
 
3322
3576
  // src/core/dialect/dialect-factory.ts
@@ -7022,6 +7276,56 @@ var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
7022
7276
  }
7023
7277
  };
7024
7278
 
7279
+ // src/core/execution/db-executor.ts
7280
+ var toExecutionPayload = (resultSets) => {
7281
+ const payload = resultSets;
7282
+ payload.resultSets = resultSets;
7283
+ return payload;
7284
+ };
7285
+ var payloadResultSets = (payload) => payload.resultSets ?? payload;
7286
+ function rowsToQueryResult(rows) {
7287
+ if (rows.length === 0) {
7288
+ return { columns: [], values: [] };
7289
+ }
7290
+ const columns = Object.keys(rows[0]);
7291
+ const values = rows.map((row) => columns.map((c) => row[c]));
7292
+ return { columns, values };
7293
+ }
7294
+ function createExecutorFromQueryRunner(runner) {
7295
+ const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
7296
+ return {
7297
+ capabilities: {
7298
+ transactions: supportsTransactions
7299
+ },
7300
+ async executeSql(sql, params) {
7301
+ const rows = await runner.query(sql, params);
7302
+ const result = rowsToQueryResult(rows);
7303
+ return toExecutionPayload([result]);
7304
+ },
7305
+ async beginTransaction() {
7306
+ if (!supportsTransactions) {
7307
+ throw new Error("Transactions are not supported by this executor");
7308
+ }
7309
+ await runner.beginTransaction.call(runner);
7310
+ },
7311
+ async commitTransaction() {
7312
+ if (!supportsTransactions) {
7313
+ throw new Error("Transactions are not supported by this executor");
7314
+ }
7315
+ await runner.commitTransaction.call(runner);
7316
+ },
7317
+ async rollbackTransaction() {
7318
+ if (!supportsTransactions) {
7319
+ throw new Error("Transactions are not supported by this executor");
7320
+ }
7321
+ await runner.rollbackTransaction.call(runner);
7322
+ },
7323
+ async dispose() {
7324
+ await runner.dispose?.call(runner);
7325
+ }
7326
+ };
7327
+ }
7328
+
7025
7329
  // src/orm/execute.ts
7026
7330
  var flattenResults = (results) => {
7027
7331
  const rows = [];
@@ -7041,7 +7345,7 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
7041
7345
  const ast = qb.getAST();
7042
7346
  const compiled = execCtx.dialect.compileSelect(ast);
7043
7347
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7044
- const rows = flattenResults(executed);
7348
+ const rows = flattenResults(payloadResultSets(executed));
7045
7349
  const lazyRelations = qb.getLazyRelations();
7046
7350
  const lazyRelationOptions = qb.getLazyRelationOptions();
7047
7351
  const includeTree = qb.getIncludeTree();
@@ -7061,7 +7365,7 @@ var executePlainWithContexts = async (execCtx, qb) => {
7061
7365
  const ast = qb.getAST();
7062
7366
  const compiled = execCtx.dialect.compileSelect(ast);
7063
7367
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7064
- const rows = flattenResults(executed);
7368
+ const rows = flattenResults(payloadResultSets(executed));
7065
7369
  if (ast.setOps && ast.setOps.length > 0) {
7066
7370
  return rows;
7067
7371
  }
@@ -7329,7 +7633,8 @@ async function executeCount(context, env, session) {
7329
7633
  };
7330
7634
  const execCtx = session.getExecutionContext();
7331
7635
  const compiled = execCtx.dialect.compileSelect(countQuery);
7332
- const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7636
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7637
+ const results = payloadResultSets(payload);
7333
7638
  const value = results[0]?.values?.[0]?.[0];
7334
7639
  if (typeof value === "number") return value;
7335
7640
  if (typeof value === "bigint") return Number(value);
@@ -7361,7 +7666,8 @@ async function executeCountRows(context, env, session) {
7361
7666
  };
7362
7667
  const execCtx = session.getExecutionContext();
7363
7668
  const compiled = execCtx.dialect.compileSelect(countQuery);
7364
- const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7669
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
7670
+ const results = payloadResultSets(payload);
7365
7671
  const value = results[0]?.values?.[0]?.[0];
7366
7672
  if (typeof value === "number") return value;
7367
7673
  if (typeof value === "bigint") return Number(value);
@@ -8335,6 +8641,45 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
8335
8641
  * // rows is EntityInstance<UserTable>[] (plain objects)
8336
8642
  * rows[0] instanceof User; // false
8337
8643
  */
8644
+ /**
8645
+ * Executes the query with LIMIT 1 and returns the first result.
8646
+ * Throws if no record is found.
8647
+ *
8648
+ * @param ctx - ORM session context
8649
+ * @returns Promise of a single entity instance
8650
+ * @throws Error if no results are found
8651
+ * @example
8652
+ * const user = await selectFromEntity(User)
8653
+ * .where(eq(users.email, 'alice@example.com'))
8654
+ * .firstOrFail(session);
8655
+ */
8656
+ async firstOrFail(ctx) {
8657
+ const rows = await this.limit(1).execute(ctx);
8658
+ if (rows.length === 0) {
8659
+ throw new Error("No results found");
8660
+ }
8661
+ return rows[0];
8662
+ }
8663
+ /**
8664
+ * Executes the query with LIMIT 1 and returns the first result as a plain object (POJO).
8665
+ * Throws if no record is found.
8666
+ *
8667
+ * @param ctx - ORM session context
8668
+ * @returns Promise of a single plain object
8669
+ * @throws Error if no results are found
8670
+ * @example
8671
+ * const user = await selectFromEntity(User)
8672
+ * .where(eq(users.email, 'alice@example.com'))
8673
+ * .firstOrFailPlain(session);
8674
+ * // user is a plain object, not an instance of User
8675
+ */
8676
+ async firstOrFailPlain(ctx) {
8677
+ const rows = await this.limit(1).executePlain(ctx);
8678
+ if (rows.length === 0) {
8679
+ throw new Error("No results found");
8680
+ }
8681
+ return rows[0];
8682
+ }
8338
8683
  async executePlain(ctx) {
8339
8684
  const builder = this.ensureDefaultSelection();
8340
8685
  const rows = await executeHydratedPlain(ctx, builder);
@@ -9034,9 +9379,95 @@ var InsertQueryState = class _InsertQueryState {
9034
9379
  returning: [...columns]
9035
9380
  });
9036
9381
  }
9382
+ /**
9383
+ * Adds an UPSERT conflict clause to the INSERT query
9384
+ * @param clause - Conflict clause and action
9385
+ * @returns A new InsertQueryState with conflict handling configured
9386
+ */
9387
+ withOnConflict(clause) {
9388
+ return this.clone({
9389
+ ...this.ast,
9390
+ onConflict: {
9391
+ target: {
9392
+ columns: [...clause.target.columns],
9393
+ constraint: clause.target.constraint
9394
+ },
9395
+ action: clause.action.type === "DoUpdate" ? {
9396
+ type: "DoUpdate",
9397
+ set: clause.action.set.map((assignment) => ({
9398
+ column: { ...assignment.column },
9399
+ value: assignment.value
9400
+ })),
9401
+ where: clause.action.where
9402
+ } : { type: "DoNothing" }
9403
+ }
9404
+ });
9405
+ }
9037
9406
  };
9038
9407
 
9039
9408
  // src/query-builder/insert.ts
9409
+ var ConflictBuilder = class {
9410
+ table;
9411
+ columns;
9412
+ constraint;
9413
+ applyClause;
9414
+ constructor(table, columns, constraint, applyClause) {
9415
+ this.table = table;
9416
+ this.columns = columns;
9417
+ this.constraint = constraint;
9418
+ this.applyClause = applyClause;
9419
+ }
9420
+ /**
9421
+ * Adds ON CONFLICT ... DO UPDATE
9422
+ * @param set - Column assignments for update branch
9423
+ * @param where - Optional filter for update branch
9424
+ * @returns InsertQueryBuilder with the upsert clause configured
9425
+ */
9426
+ doUpdate(set, where) {
9427
+ const assignments = this.buildAssignments(set);
9428
+ return this.applyClause({
9429
+ target: this.buildTarget(),
9430
+ action: {
9431
+ type: "DoUpdate",
9432
+ set: assignments,
9433
+ where
9434
+ }
9435
+ });
9436
+ }
9437
+ /**
9438
+ * Adds ON CONFLICT ... DO NOTHING
9439
+ * @returns InsertQueryBuilder with the upsert clause configured
9440
+ */
9441
+ doNothing() {
9442
+ return this.applyClause({
9443
+ target: this.buildTarget(),
9444
+ action: { type: "DoNothing" }
9445
+ });
9446
+ }
9447
+ buildTarget() {
9448
+ return {
9449
+ columns: [...this.columns],
9450
+ constraint: this.constraint
9451
+ };
9452
+ }
9453
+ buildAssignments(set) {
9454
+ const entries = Object.entries(set);
9455
+ if (!entries.length) {
9456
+ throw new Error("ON CONFLICT DO UPDATE requires at least one assignment.");
9457
+ }
9458
+ return entries.map(([columnName, rawValue]) => {
9459
+ if (!isValueOperandInput(rawValue)) {
9460
+ throw new Error(
9461
+ `Invalid upsert value for column "${columnName}": only string, number, boolean, Date, Buffer, null, or OperandNodes are allowed`
9462
+ );
9463
+ }
9464
+ return {
9465
+ column: buildColumnNode(this.table, { name: columnName, table: this.table.name }),
9466
+ value: valueToOperand(rawValue)
9467
+ };
9468
+ });
9469
+ }
9470
+ };
9040
9471
  var InsertQueryBuilder = class _InsertQueryBuilder {
9041
9472
  table;
9042
9473
  state;
@@ -9052,6 +9483,9 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
9052
9483
  clone(state) {
9053
9484
  return new _InsertQueryBuilder(this.table, state);
9054
9485
  }
9486
+ withOnConflict(clause) {
9487
+ return this.clone(this.state.withOnConflict(clause));
9488
+ }
9055
9489
  /**
9056
9490
  * Adds VALUES to the INSERT query
9057
9491
  * @param rowOrRows - Single row object or array of row objects to insert
@@ -9083,6 +9517,21 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
9083
9517
  const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
9084
9518
  return this.clone(this.state.withSelect(ast, nodes));
9085
9519
  }
9520
+ /**
9521
+ * Configures UPSERT conflict handling for INSERT.
9522
+ * @param columns - Conflict target columns (ignored by MySQL)
9523
+ * @param constraint - Named unique/primary constraint (PostgreSQL only)
9524
+ * @returns ConflictBuilder for selecting action (DO UPDATE / DO NOTHING)
9525
+ */
9526
+ onConflict(columns = [], constraint) {
9527
+ const resolvedColumns = columns.length ? this.resolveColumnNodes(columns) : [];
9528
+ return new ConflictBuilder(
9529
+ this.table,
9530
+ resolvedColumns,
9531
+ constraint,
9532
+ (clause) => this.withOnConflict(clause)
9533
+ );
9534
+ }
9086
9535
  /**
9087
9536
  * Adds a RETURNING clause to the INSERT query
9088
9537
  * @param columns - Columns to return after insertion
@@ -9360,7 +9809,8 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
9360
9809
  async execute(session) {
9361
9810
  const execCtx = session.getExecutionContext();
9362
9811
  const compiled = this.compile(execCtx.dialect);
9363
- return execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9812
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
9813
+ return payloadResultSets(payload);
9364
9814
  }
9365
9815
  /**
9366
9816
  * Returns the Abstract Syntax Tree (AST) representation of the query
@@ -9550,7 +10000,8 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
9550
10000
  async execute(session) {
9551
10001
  const execCtx = session.getExecutionContext();
9552
10002
  const compiled = this.compile(execCtx.dialect);
9553
- return execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
10003
+ const payload = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
10004
+ return payloadResultSets(payload);
9554
10005
  }
9555
10006
  /**
9556
10007
  * Returns the Abstract Syntax Tree (AST) representation of the query
@@ -9562,6 +10013,149 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
9562
10013
  };
9563
10014
  var isTableSourceNode2 = (source) => typeof source.type === "string";
9564
10015
 
10016
+ // src/orm/execute-procedure.ts
10017
+ var resolveColumnIndex = (columns, expectedName) => {
10018
+ const exact = columns.findIndex((column) => column === expectedName);
10019
+ if (exact >= 0) return exact;
10020
+ const lowerExpected = expectedName.toLowerCase();
10021
+ return columns.findIndex((column) => column.toLowerCase() === lowerExpected);
10022
+ };
10023
+ var extractOutValues = (compiled, resultSets) => {
10024
+ if (!compiled.outParams.names.length || compiled.outParams.source === "none") {
10025
+ return {};
10026
+ }
10027
+ const sourceSet = compiled.outParams.source === "firstResultSet" ? resultSets[0] : resultSets[resultSets.length - 1];
10028
+ if (!sourceSet) {
10029
+ throw new Error(
10030
+ `Procedure expected OUT parameters in ${compiled.outParams.source}, but no result set was returned.`
10031
+ );
10032
+ }
10033
+ if (!sourceSet.values.length) {
10034
+ throw new Error(
10035
+ `Procedure expected OUT parameters in ${compiled.outParams.source}, but the result set has no rows.`
10036
+ );
10037
+ }
10038
+ const firstRow = sourceSet.values[0];
10039
+ const out = {};
10040
+ for (const expectedName of compiled.outParams.names) {
10041
+ const columnIndex = resolveColumnIndex(sourceSet.columns, expectedName);
10042
+ if (columnIndex < 0) {
10043
+ const available = sourceSet.columns.length ? sourceSet.columns.join(", ") : "(none)";
10044
+ throw new Error(
10045
+ `Procedure OUT parameter "${expectedName}" was not found in ${compiled.outParams.source}. Available columns: ${available}.`
10046
+ );
10047
+ }
10048
+ out[expectedName] = firstRow[columnIndex];
10049
+ }
10050
+ return out;
10051
+ };
10052
+ var executeProcedureAst = async (session, ast) => {
10053
+ const execCtx = session.getExecutionContext();
10054
+ const compiled = execCtx.dialect.compileProcedureCall(ast);
10055
+ const payload = await execCtx.interceptors.run(
10056
+ { sql: compiled.sql, params: compiled.params },
10057
+ execCtx.executor
10058
+ );
10059
+ const resultSets = payloadResultSets(payload);
10060
+ return {
10061
+ resultSets,
10062
+ out: extractOutValues(compiled, resultSets)
10063
+ };
10064
+ };
10065
+
10066
+ // src/query-builder/procedure-call.ts
10067
+ var cloneParam = (param) => ({
10068
+ ...param,
10069
+ value: param.value ? { ...param.value } : void 0
10070
+ });
10071
+ var ProcedureCallBuilder = class _ProcedureCallBuilder {
10072
+ ast;
10073
+ constructor(name, options, ast) {
10074
+ this.ast = ast ?? {
10075
+ type: "ProcedureCall",
10076
+ ref: {
10077
+ name,
10078
+ schema: options?.schema
10079
+ },
10080
+ params: []
10081
+ };
10082
+ }
10083
+ clone(nextParams) {
10084
+ return new _ProcedureCallBuilder(
10085
+ this.ast.ref.name,
10086
+ { schema: this.ast.ref.schema },
10087
+ {
10088
+ ...this.ast,
10089
+ ref: { ...this.ast.ref },
10090
+ params: nextParams
10091
+ }
10092
+ );
10093
+ }
10094
+ in(name, value) {
10095
+ return this.clone([
10096
+ ...this.ast.params.map(cloneParam),
10097
+ {
10098
+ name,
10099
+ direction: "in",
10100
+ value: valueToOperand(value)
10101
+ }
10102
+ ]);
10103
+ }
10104
+ out(name, options) {
10105
+ return this.clone([
10106
+ ...this.ast.params.map(cloneParam),
10107
+ {
10108
+ name,
10109
+ direction: "out",
10110
+ dbType: options?.dbType
10111
+ }
10112
+ ]);
10113
+ }
10114
+ inOut(name, value, options) {
10115
+ return this.clone([
10116
+ ...this.ast.params.map(cloneParam),
10117
+ {
10118
+ name,
10119
+ direction: "inout",
10120
+ value: valueToOperand(value),
10121
+ dbType: options?.dbType
10122
+ }
10123
+ ]);
10124
+ }
10125
+ compile(dialect) {
10126
+ const resolved = resolveDialectInput(dialect);
10127
+ this.validateMssqlOutDbType(resolved);
10128
+ return resolved.compileProcedureCall(this.getAST());
10129
+ }
10130
+ toSql(dialect) {
10131
+ return this.compile(dialect).sql;
10132
+ }
10133
+ getAST() {
10134
+ return {
10135
+ ...this.ast,
10136
+ ref: { ...this.ast.ref },
10137
+ params: this.ast.params.map(cloneParam)
10138
+ };
10139
+ }
10140
+ async execute(session) {
10141
+ this.validateMssqlOutDbType(session.getExecutionContext().dialect);
10142
+ return executeProcedureAst(session, this.getAST());
10143
+ }
10144
+ validateMssqlOutDbType(dialect) {
10145
+ const isMssqlDialect = dialect.constructor.name === "SqlServerDialect";
10146
+ if (!isMssqlDialect) return;
10147
+ for (const param of this.ast.params) {
10148
+ const needsDbType = param.direction === "out" || param.direction === "inout";
10149
+ if (needsDbType && !param.dbType) {
10150
+ throw new Error(
10151
+ `MSSQL requires "dbType" for procedure parameter "${param.name}" with direction "${param.direction}".`
10152
+ );
10153
+ }
10154
+ }
10155
+ }
10156
+ };
10157
+ var callProcedure = (name, options) => new ProcedureCallBuilder(name, options);
10158
+
9565
10159
  // src/query/target.ts
9566
10160
  var resolveEntityTarget = (ctor) => {
9567
10161
  const table = getTableDefFromEntity(ctor);
@@ -12740,7 +13334,8 @@ var UnitOfWork = class {
12740
13334
  * @returns Query results
12741
13335
  */
12742
13336
  async executeCompiled(compiled) {
12743
- return this.executor.executeSql(compiled.sql, compiled.params);
13337
+ const payload = await this.executor.executeSql(compiled.sql, compiled.params);
13338
+ return payloadResultSets(payload);
12744
13339
  }
12745
13340
  /**
12746
13341
  * Gets columns for RETURNING clause.
@@ -13884,9 +14479,9 @@ var InterceptorPipeline = class {
13884
14479
  const dispatch = async () => {
13885
14480
  const interceptor = this.interceptors[i++];
13886
14481
  if (!interceptor) {
13887
- return executor.executeSql(ctx.sql, ctx.params);
14482
+ return toExecutionPayload(await executor.executeSql(ctx.sql, ctx.params));
13888
14483
  }
13889
- return interceptor(ctx, dispatch);
14484
+ return toExecutionPayload(await interceptor(ctx, dispatch));
13890
14485
  };
13891
14486
  return dispatch();
13892
14487
  }
@@ -14009,6 +14604,11 @@ function isValidDuration(value) {
14009
14604
  // src/cache/adapters/memory-cache-adapter.ts
14010
14605
  var MemoryCacheAdapter = class {
14011
14606
  name = "memory";
14607
+ capabilities = {
14608
+ tags: true,
14609
+ prefix: true,
14610
+ ttl: true
14611
+ };
14012
14612
  storage = /* @__PURE__ */ new Map();
14013
14613
  tagIndex = /* @__PURE__ */ new Map();
14014
14614
  async get(key) {
@@ -15135,50 +15735,6 @@ function CEP(options) {
15135
15735
  };
15136
15736
  }
15137
15737
 
15138
- // src/core/execution/db-executor.ts
15139
- function rowsToQueryResult(rows) {
15140
- if (rows.length === 0) {
15141
- return { columns: [], values: [] };
15142
- }
15143
- const columns = Object.keys(rows[0]);
15144
- const values = rows.map((row) => columns.map((c) => row[c]));
15145
- return { columns, values };
15146
- }
15147
- function createExecutorFromQueryRunner(runner) {
15148
- const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
15149
- return {
15150
- capabilities: {
15151
- transactions: supportsTransactions
15152
- },
15153
- async executeSql(sql, params) {
15154
- const rows = await runner.query(sql, params);
15155
- const result = rowsToQueryResult(rows);
15156
- return [result];
15157
- },
15158
- async beginTransaction() {
15159
- if (!supportsTransactions) {
15160
- throw new Error("Transactions are not supported by this executor");
15161
- }
15162
- await runner.beginTransaction.call(runner);
15163
- },
15164
- async commitTransaction() {
15165
- if (!supportsTransactions) {
15166
- throw new Error("Transactions are not supported by this executor");
15167
- }
15168
- await runner.commitTransaction.call(runner);
15169
- },
15170
- async rollbackTransaction() {
15171
- if (!supportsTransactions) {
15172
- throw new Error("Transactions are not supported by this executor");
15173
- }
15174
- await runner.rollbackTransaction.call(runner);
15175
- },
15176
- async dispose() {
15177
- await runner.dispose?.call(runner);
15178
- }
15179
- };
15180
- }
15181
-
15182
15738
  // src/core/execution/pooling/pool.ts
15183
15739
  var deferred = () => {
15184
15740
  let resolve;
@@ -15406,6 +15962,36 @@ function createPostgresExecutor(client) {
15406
15962
  }
15407
15963
 
15408
15964
  // src/core/execution/executors/mysql-executor.ts
15965
+ var isRowObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15966
+ var isRowObjectArray = (value) => Array.isArray(value) && value.every(isRowObject);
15967
+ var isMysqlResultHeader = (value) => isRowObject(value) && ("affectedRows" in value || "insertId" in value || "warningStatus" in value || "serverStatus" in value);
15968
+ var headerToQueryResult = (header) => ({
15969
+ columns: [],
15970
+ values: [],
15971
+ meta: {
15972
+ insertId: header.insertId,
15973
+ rowsAffected: header.affectedRows
15974
+ }
15975
+ });
15976
+ var normalizeMysqlResults = (rows) => {
15977
+ if (!Array.isArray(rows)) {
15978
+ return isMysqlResultHeader(rows) ? [headerToQueryResult(rows)] : [rowsToQueryResult([])];
15979
+ }
15980
+ if (isRowObjectArray(rows)) {
15981
+ return [rowsToQueryResult(rows)];
15982
+ }
15983
+ const normalized = [];
15984
+ for (const chunk of rows) {
15985
+ if (isRowObjectArray(chunk)) {
15986
+ normalized.push(rowsToQueryResult(chunk));
15987
+ continue;
15988
+ }
15989
+ if (isMysqlResultHeader(chunk)) {
15990
+ normalized.push(headerToQueryResult(chunk));
15991
+ }
15992
+ }
15993
+ return normalized.length ? normalized : [rowsToQueryResult([])];
15994
+ };
15409
15995
  function createMysqlExecutor(client) {
15410
15996
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
15411
15997
  return {
@@ -15414,21 +16000,7 @@ function createMysqlExecutor(client) {
15414
16000
  },
15415
16001
  async executeSql(sql, params) {
15416
16002
  const [rows] = await client.query(sql, params);
15417
- if (!Array.isArray(rows)) {
15418
- const header = rows;
15419
- return [{
15420
- columns: [],
15421
- values: [],
15422
- meta: {
15423
- insertId: header.insertId,
15424
- rowsAffected: header.affectedRows
15425
- }
15426
- }];
15427
- }
15428
- const result = rowsToQueryResult(
15429
- rows
15430
- );
15431
- return [result];
16003
+ return toExecutionPayload(normalizeMysqlResults(rows));
15432
16004
  },
15433
16005
  async beginTransaction() {
15434
16006
  if (!supportsTransactions) {
@@ -15463,7 +16035,7 @@ function createSqliteExecutor(client) {
15463
16035
  async executeSql(sql, params) {
15464
16036
  const rows = await client.all(sql, params);
15465
16037
  const result = rowsToQueryResult(rows);
15466
- return [result];
16038
+ return toExecutionPayload([result]);
15467
16039
  },
15468
16040
  async beginTransaction() {
15469
16041
  if (!supportsTransactions) {
@@ -15496,9 +16068,9 @@ function createMssqlExecutor(client) {
15496
16068
  transactions: supportsTransactions
15497
16069
  },
15498
16070
  async executeSql(sql, params) {
15499
- const { recordset } = await client.query(sql, params);
15500
- const result = rowsToQueryResult(recordset ?? []);
15501
- return [result];
16071
+ const { recordset, recordsets } = await client.query(sql, params);
16072
+ const sets = Array.isArray(recordsets) ? recordsets : [recordset ?? []];
16073
+ return toExecutionPayload(sets.map((set) => rowsToQueryResult(set ?? [])));
15502
16074
  },
15503
16075
  async beginTransaction() {
15504
16076
  if (!supportsTransactions) {
@@ -15564,7 +16136,7 @@ function createTediousMssqlClient(connection, { Request, TYPES }, options) {
15564
16136
  connection.execSql(request);
15565
16137
  }
15566
16138
  );
15567
- return { recordset: rows };
16139
+ return { recordset: rows, recordsets: [rows] };
15568
16140
  },
15569
16141
  beginTransaction: connection.beginTransaction ? () => new Promise((resolve, reject) => {
15570
16142
  connection.beginTransaction(
@@ -15589,6 +16161,8 @@ function createTediousExecutor(connection, module2, options) {
15589
16161
  }
15590
16162
 
15591
16163
  // src/orm/pooled-executor-factory.ts
16164
+ var isQueryResult = (value) => typeof value === "object" && value !== null && Array.isArray(value.columns) && Array.isArray(value.values);
16165
+ var isRowArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item));
15592
16166
  function createPooledExecutorFactory(opts) {
15593
16167
  const { pool, adapter } = opts;
15594
16168
  const makeExecutor = (mode) => {
@@ -15600,7 +16174,13 @@ function createPooledExecutorFactory(opts) {
15600
16174
  };
15601
16175
  const executeWithConn = async (conn, sql, params) => {
15602
16176
  const rows = await adapter.query(conn, sql, params);
15603
- return [rowsToQueryResult(rows)];
16177
+ if (Array.isArray(rows) && rows.length > 0 && rows.every(isQueryResult)) {
16178
+ return toExecutionPayload(rows);
16179
+ }
16180
+ if (Array.isArray(rows) && rows.length > 0 && rows.every(isRowArray)) {
16181
+ return toExecutionPayload(rows.map((set) => rowsToQueryResult(set)));
16182
+ }
16183
+ return toExecutionPayload([rowsToQueryResult(rows)]);
15604
16184
  };
15605
16185
  return {
15606
16186
  capabilities: { transactions: true },
@@ -18862,6 +19442,11 @@ var KeyvCacheAdapter = class {
18862
19442
  this.keyv = keyv;
18863
19443
  }
18864
19444
  name = "keyv";
19445
+ capabilities = {
19446
+ tags: false,
19447
+ prefix: true,
19448
+ ttl: true
19449
+ };
18865
19450
  async get(key) {
18866
19451
  return this.keyv.get(key);
18867
19452
  }
@@ -18905,6 +19490,152 @@ var KeyvCacheAdapter = class {
18905
19490
  }
18906
19491
  };
18907
19492
 
19493
+ // src/cache/adapters/redis-cache-adapter.ts
19494
+ var RedisCacheAdapter = class {
19495
+ name = "redis";
19496
+ capabilities = {
19497
+ tags: true,
19498
+ prefix: true,
19499
+ ttl: true
19500
+ };
19501
+ redis;
19502
+ ownsConnection;
19503
+ tagPrefix;
19504
+ /**
19505
+ * Cria um adapter Redis
19506
+ *
19507
+ * @param redis - Instância do ioredis OU opções de conexão
19508
+ * @param options - Opções adicionais
19509
+ * @param options.tagPrefix - Prefixo para chaves de tag (default: 'tag:')
19510
+ *
19511
+ * Exemplos:
19512
+ *
19513
+ * // Com instância existente (recomendado para connection pooling):
19514
+ * const redis = new Redis({ host: 'localhost', port: 6379 });
19515
+ * const adapter = new RedisCacheAdapter(redis);
19516
+ *
19517
+ * // Com opções (adapter gerencia conexão):
19518
+ * const adapter = new RedisCacheAdapter({ host: 'localhost', port: 6379 });
19519
+ *
19520
+ * // Para testes com ioredis-mock:
19521
+ * import Redis from 'ioredis-mock';
19522
+ * const adapter = new RedisCacheAdapter(new Redis());
19523
+ */
19524
+ constructor(redis, options) {
19525
+ this.tagPrefix = options?.tagPrefix ?? "tag:";
19526
+ if (this.isRedisInstance(redis)) {
19527
+ this.redis = redis;
19528
+ this.ownsConnection = false;
19529
+ } else {
19530
+ this.redis = this.createRedis(redis);
19531
+ this.ownsConnection = true;
19532
+ }
19533
+ }
19534
+ isRedisInstance(obj) {
19535
+ return typeof obj === "object" && obj !== null && "get" in obj && "set" in obj && "del" in obj && typeof obj.get === "function";
19536
+ }
19537
+ createRedis(options) {
19538
+ try {
19539
+ const Redis = require("ioredis");
19540
+ return new Redis(options);
19541
+ } catch {
19542
+ throw new Error(
19543
+ "ioredis is required for RedisCacheAdapter. Install it with: npm install ioredis"
19544
+ );
19545
+ }
19546
+ }
19547
+ async get(key) {
19548
+ const value = await this.redis.get(key);
19549
+ if (value === null) {
19550
+ return void 0;
19551
+ }
19552
+ try {
19553
+ return JSON.parse(value);
19554
+ } catch {
19555
+ return void 0;
19556
+ }
19557
+ }
19558
+ async has(key) {
19559
+ const value = await this.redis.get(key);
19560
+ return value !== null;
19561
+ }
19562
+ async set(key, value, ttlMs, tags) {
19563
+ const serialized = JSON.stringify(value);
19564
+ if (ttlMs) {
19565
+ await this.redis.set(key, serialized, "PX", ttlMs);
19566
+ } else {
19567
+ await this.redis.set(key, serialized);
19568
+ }
19569
+ if (tags && tags.length > 0) {
19570
+ await this.registerTags(key, tags);
19571
+ }
19572
+ }
19573
+ async delete(key) {
19574
+ await this.redis.del(key);
19575
+ }
19576
+ async invalidate(key) {
19577
+ await this.delete(key);
19578
+ }
19579
+ async invalidateTags(tags) {
19580
+ const keysToDelete = /* @__PURE__ */ new Set();
19581
+ for (const tag of tags) {
19582
+ const tagKey = `${this.tagPrefix}${tag}`;
19583
+ const keys = await this.redis.smembers(tagKey);
19584
+ for (const key of keys) {
19585
+ keysToDelete.add(key);
19586
+ }
19587
+ await this.redis.del(tagKey);
19588
+ }
19589
+ if (keysToDelete.size > 0) {
19590
+ await this.redis.del(...Array.from(keysToDelete));
19591
+ }
19592
+ }
19593
+ async invalidatePrefix(prefix) {
19594
+ const keysToDelete = [];
19595
+ let cursor = "0";
19596
+ do {
19597
+ const [nextCursor, keys] = await this.redis.scan(
19598
+ cursor,
19599
+ "MATCH",
19600
+ `${prefix}*`,
19601
+ "COUNT",
19602
+ 100
19603
+ );
19604
+ cursor = nextCursor;
19605
+ keysToDelete.push(...keys);
19606
+ } while (cursor !== "0");
19607
+ if (keysToDelete.length > 0) {
19608
+ const batchSize = 1e3;
19609
+ for (let i = 0; i < keysToDelete.length; i += batchSize) {
19610
+ const batch = keysToDelete.slice(i, i + batchSize);
19611
+ await this.redis.del(...batch);
19612
+ }
19613
+ }
19614
+ }
19615
+ async registerTags(key, tags) {
19616
+ for (const tag of tags) {
19617
+ const tagKey = `${this.tagPrefix}${tag}`;
19618
+ await this.redis.sadd(tagKey, key);
19619
+ }
19620
+ }
19621
+ async dispose() {
19622
+ if (this.ownsConnection) {
19623
+ try {
19624
+ await this.redis.quit();
19625
+ } catch {
19626
+ this.redis.disconnect?.();
19627
+ }
19628
+ }
19629
+ }
19630
+ /**
19631
+ * Retorna a instância Redis subjacente
19632
+ * Útil para operações avançadas ou health checks
19633
+ */
19634
+ getRedis() {
19635
+ return this.redis;
19636
+ }
19637
+ };
19638
+
18908
19639
  // src/cache/tag-index.ts
18909
19640
  var TagIndex = class {
18910
19641
  tagToKeys = /* @__PURE__ */ new Map();
@@ -19025,6 +19756,7 @@ var TagIndex = class {
19025
19756
  CPF,
19026
19757
  Capitalize,
19027
19758
  Column,
19759
+ ConflictBuilder,
19028
19760
  ConstructorMaterializationStrategy,
19029
19761
  DEFAULT_TREE_CONFIG,
19030
19762
  DateTimeTypeStrategy,
@@ -19057,8 +19789,10 @@ var TagIndex = class {
19057
19789
  Pool,
19058
19790
  PostgresDialect,
19059
19791
  PrimaryKey,
19792
+ ProcedureCallBuilder,
19060
19793
  PrototypeMaterializationStrategy,
19061
19794
  QueryCacheManager,
19795
+ RedisCacheAdapter,
19062
19796
  RelationKinds,
19063
19797
  STANDARD_COLUMN_TYPES,
19064
19798
  SelectQueryBuilder,
@@ -19105,6 +19839,7 @@ var TagIndex = class {
19105
19839
  buildScopeConditions,
19106
19840
  calculateRowDepths,
19107
19841
  calculateTotalPages,
19842
+ callProcedure,
19108
19843
  canonicalizeSchema,
19109
19844
  caseWhen,
19110
19845
  cast,
@@ -19177,6 +19912,7 @@ var TagIndex = class {
19177
19912
  executeHydratedPlain,
19178
19913
  executeHydratedPlainWithContexts,
19179
19914
  executeHydratedWithContexts,
19915
+ executeProcedureAst,
19180
19916
  executeSchemaSql,
19181
19917
  executeSchemaSqlFor,
19182
19918
  exists,
@@ -19304,6 +20040,7 @@ var TagIndex = class {
19304
20040
  paginationParamsSchema,
19305
20041
  parameterToRef,
19306
20042
  parseDuration,
20043
+ payloadResultSets,
19307
20044
  pi,
19308
20045
  pick,
19309
20046
  position,
@@ -19363,6 +20100,7 @@ var TagIndex = class {
19363
20100
  threadResults,
19364
20101
  threadedNodeToOpenApiSchema,
19365
20102
  toColumnRef,
20103
+ toExecutionPayload,
19366
20104
  toPagedResponse,
19367
20105
  toPagedResponseBuilder,
19368
20106
  toPaginationParams,