@nextlyhq/adapter-mysql 0.0.2-alpha.6 → 0.0.2-alpha.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +110 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.mjs +111 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
package/dist/index.cjs
CHANGED
|
@@ -85,6 +85,14 @@ function delay(ms) {
|
|
|
85
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
86
86
|
}
|
|
87
87
|
var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
88
|
+
// getDrizzle memoization: drizzle v1's constructor builds a relational
|
|
89
|
+
// query builder per table in the relations config (~40 tables), and the
|
|
90
|
+
// service layer resolves an instance on every db access — construct once
|
|
91
|
+
// per relations object (identity-stable: the schema registry caches it
|
|
92
|
+
// and hands out a NEW object on invalidation, which naturally misses
|
|
93
|
+
// this cache and produces a fresh instance).
|
|
94
|
+
drizzleByRelations = /* @__PURE__ */ new WeakMap();
|
|
95
|
+
drizzleBare;
|
|
88
96
|
/**
|
|
89
97
|
* The database dialect - always 'mysql' for this adapter.
|
|
90
98
|
*/
|
|
@@ -164,16 +172,19 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
164
172
|
* It waits for all connections to be released before shutting down.
|
|
165
173
|
*/
|
|
166
174
|
async disconnect() {
|
|
167
|
-
|
|
175
|
+
const pool = this.pool;
|
|
176
|
+
this.pool = null;
|
|
177
|
+
this.drizzleBare = void 0;
|
|
178
|
+
this.drizzleByRelations = /* @__PURE__ */ new WeakMap();
|
|
179
|
+
if (!pool) {
|
|
168
180
|
return;
|
|
169
181
|
}
|
|
170
182
|
try {
|
|
171
|
-
await
|
|
183
|
+
await pool.end();
|
|
172
184
|
if (this.config.logger?.info) {
|
|
173
185
|
this.config.logger.info("MySQL connection closed");
|
|
174
186
|
}
|
|
175
187
|
} finally {
|
|
176
|
-
this.pool = null;
|
|
177
188
|
this.connected = false;
|
|
178
189
|
}
|
|
179
190
|
}
|
|
@@ -282,6 +293,7 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
282
293
|
await connection.query("ROLLBACK").catch(() => {
|
|
283
294
|
});
|
|
284
295
|
lastError = error;
|
|
296
|
+
if (types.isApplicationError(error)) throw error;
|
|
285
297
|
const mysqlError = error;
|
|
286
298
|
const isRetryable = mysqlError.errno === 1213;
|
|
287
299
|
if (isRetryable && attempt < maxAttempts) {
|
|
@@ -391,13 +403,22 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
391
403
|
* @returns Drizzle ORM instance wrapping the mysql2 pool connection
|
|
392
404
|
* @throws {Error} If called in browser or not connected
|
|
393
405
|
*/
|
|
394
|
-
|
|
395
|
-
getDrizzle(schema) {
|
|
406
|
+
getDrizzle(relations) {
|
|
396
407
|
if (typeof window !== "undefined") {
|
|
397
408
|
throw new Error("getDrizzle() is server-only");
|
|
398
409
|
}
|
|
399
410
|
const pool = this.ensurePool();
|
|
400
|
-
|
|
411
|
+
const client = pool.pool;
|
|
412
|
+
if (!relations) {
|
|
413
|
+
this.drizzleBare ??= mysql2.drizzle({ client });
|
|
414
|
+
return this.drizzleBare;
|
|
415
|
+
}
|
|
416
|
+
let cached = this.drizzleByRelations.get(relations);
|
|
417
|
+
if (!cached) {
|
|
418
|
+
cached = mysql2.drizzle({ client, relations });
|
|
419
|
+
this.drizzleByRelations.set(relations, cached);
|
|
420
|
+
}
|
|
421
|
+
return cached;
|
|
401
422
|
}
|
|
402
423
|
/**
|
|
403
424
|
* Builds mysql2 Pool configuration from adapter config.
|
|
@@ -475,6 +496,11 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
475
496
|
* as savepoints are disabled in this adapter per approved approach.
|
|
476
497
|
*/
|
|
477
498
|
createTransactionContext(connection) {
|
|
499
|
+
const buildTxExecutor = () => mysql2.drizzle({
|
|
500
|
+
client: connection.connection
|
|
501
|
+
});
|
|
502
|
+
let txExecutor;
|
|
503
|
+
const txDb = () => txExecutor ??= buildTxExecutor();
|
|
478
504
|
return {
|
|
479
505
|
execute: async (sql, params = []) => {
|
|
480
506
|
const [rows] = await connection.query(
|
|
@@ -483,34 +509,76 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
483
509
|
);
|
|
484
510
|
return rows;
|
|
485
511
|
},
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
512
|
+
// Run on the transaction-bound Drizzle instance rather than the pool, so
|
|
513
|
+
// the statement is part of this transaction and sees its uncommitted rows.
|
|
514
|
+
runStatement: async (statement) => {
|
|
515
|
+
await txDb().execute(statement);
|
|
516
|
+
},
|
|
517
|
+
// mysql2 answers a `[rows, fields]` tuple; the transaction-bound instance
|
|
518
|
+
// keeps the read inside this transaction so it sees its uncommitted writes.
|
|
519
|
+
queryStatement: async (statement) => {
|
|
520
|
+
const result = await txDb().execute(statement);
|
|
521
|
+
if (!Array.isArray(result)) {
|
|
522
|
+
throw this.createDatabaseError(
|
|
523
|
+
"query",
|
|
524
|
+
"Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.",
|
|
525
|
+
void 0
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
return result[0];
|
|
529
|
+
},
|
|
530
|
+
lockRow: async (table, id) => {
|
|
531
|
+
const idColumn = this.escapeIdentifier("id");
|
|
532
|
+
await connection.query(
|
|
533
|
+
`SELECT ${idColumn} FROM ${this.escapeIdentifier(table)} WHERE ${idColumn} = ? FOR UPDATE`,
|
|
534
|
+
[id]
|
|
535
|
+
);
|
|
536
|
+
},
|
|
537
|
+
insert: async (table, data, options) => {
|
|
538
|
+
const mapped = this.mapRowToRawSql(this.getTableObject(table), data);
|
|
539
|
+
const columns = Object.keys(mapped);
|
|
540
|
+
const values = Object.values(mapped);
|
|
489
541
|
const placeholders = this.buildPlaceholders(values.length, 0);
|
|
490
542
|
const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map((c) => this.escapeIdentifier(c)).join(", ")}) VALUES (${placeholders})`;
|
|
491
543
|
const [result] = await connection.query(sql, values);
|
|
492
|
-
|
|
544
|
+
const ret = options?.returning;
|
|
545
|
+
if (Array.isArray(ret) && ret.length === 0) {
|
|
546
|
+
return void 0;
|
|
547
|
+
}
|
|
548
|
+
const insertTableObj = this.getTableObject(table);
|
|
549
|
+
const aliases = this.dateWallClockAliases(insertTableObj, ret ?? "*");
|
|
550
|
+
const spelled = aliases.map(
|
|
551
|
+
(a) => `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`
|
|
552
|
+
).join(", ");
|
|
553
|
+
const projected = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(insertTableObj, ret).map((c) => this.escapeIdentifier(c)).join(", ");
|
|
554
|
+
const selectList = spelled ? `${projected}, ${spelled}` : projected;
|
|
555
|
+
const idValue = result.insertId ? result.insertId : mapped.id;
|
|
556
|
+
if (idValue !== void 0) {
|
|
493
557
|
const [rows2] = await connection.query(
|
|
494
|
-
`SELECT
|
|
495
|
-
[
|
|
558
|
+
`SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE id = ?`,
|
|
559
|
+
[idValue]
|
|
496
560
|
);
|
|
497
|
-
return rows2[0];
|
|
561
|
+
return this.mapRowFromRawSql(insertTableObj, rows2[0], aliases);
|
|
498
562
|
}
|
|
499
563
|
const whereClauses = columns.map(
|
|
500
564
|
(c) => `${this.escapeIdentifier(c)} = ?`
|
|
501
565
|
);
|
|
502
566
|
const [rows] = await connection.query(
|
|
503
|
-
`SELECT
|
|
567
|
+
`SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(" AND ")} LIMIT 1`,
|
|
504
568
|
values
|
|
505
569
|
);
|
|
506
|
-
return rows[0];
|
|
570
|
+
return this.mapRowFromRawSql(insertTableObj, rows[0], aliases);
|
|
507
571
|
},
|
|
508
|
-
insertMany: async (table, data,
|
|
572
|
+
insertMany: async (table, data, options) => {
|
|
509
573
|
if (data.length === 0) return [];
|
|
510
|
-
const
|
|
574
|
+
const retMany = options?.returning;
|
|
575
|
+
const skipReread = Array.isArray(retMany) && retMany.length === 0;
|
|
576
|
+
const tableObj = this.getTableObject(table);
|
|
577
|
+
const mappedRecords = data.map((r) => this.mapRowToRawSql(tableObj, r));
|
|
578
|
+
const columns = Object.keys(mappedRecords[0]);
|
|
511
579
|
const allValues = [];
|
|
512
580
|
const valuesClauses = [];
|
|
513
|
-
for (const record of
|
|
581
|
+
for (const record of mappedRecords) {
|
|
514
582
|
const placeholders = [];
|
|
515
583
|
for (const col of columns) {
|
|
516
584
|
allValues.push(record[col]);
|
|
@@ -523,41 +591,53 @@ var MySqlAdapter = class extends adapterDrizzle.DrizzleAdapter {
|
|
|
523
591
|
sql,
|
|
524
592
|
allValues
|
|
525
593
|
);
|
|
526
|
-
if (result.insertId && result.affectedRows > 0) {
|
|
594
|
+
if (!skipReread && result.insertId && result.affectedRows > 0) {
|
|
527
595
|
const ids = [];
|
|
528
596
|
for (let i = 0; i < result.affectedRows; i++) {
|
|
529
597
|
ids.push(result.insertId + i);
|
|
530
598
|
}
|
|
531
599
|
const placeholders = ids.map(() => "?").join(", ");
|
|
600
|
+
const bulkAliases = this.dateWallClockAliases(tableObj, "*");
|
|
601
|
+
const bulkSpelled = bulkAliases.map(
|
|
602
|
+
(a) => `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`
|
|
603
|
+
).join(", ");
|
|
532
604
|
const [rows] = await connection.query(
|
|
533
|
-
`SELECT
|
|
605
|
+
`SELECT *${bulkSpelled ? `, ${bulkSpelled}` : ""} FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,
|
|
534
606
|
ids
|
|
535
607
|
);
|
|
536
|
-
return rows
|
|
608
|
+
return rows.map(
|
|
609
|
+
(r) => this.mapRowFromRawSql(tableObj, r, bulkAliases)
|
|
610
|
+
);
|
|
537
611
|
}
|
|
538
612
|
return [];
|
|
539
613
|
},
|
|
540
|
-
// TransactionContext CRUD methods delegate to the adapter's CRUD
|
|
541
|
-
//
|
|
614
|
+
// TransactionContext CRUD methods delegate to the adapter's Drizzle CRUD
|
|
615
|
+
// but pass the transaction-bound executor so they run inside this
|
|
616
|
+
// transaction rather than on the pool.
|
|
542
617
|
select: async (table, options) => {
|
|
543
|
-
return this.select(table, options);
|
|
618
|
+
return this.select(table, options, txDb());
|
|
544
619
|
},
|
|
545
620
|
selectOne: async (table, options) => {
|
|
546
|
-
return this.selectOne(table, options);
|
|
621
|
+
return this.selectOne(table, options, txDb());
|
|
547
622
|
},
|
|
548
623
|
update: async (table, data, where, options) => {
|
|
549
|
-
return this.update(table, data, where, options);
|
|
624
|
+
return this.update(table, data, where, options, txDb());
|
|
550
625
|
},
|
|
551
|
-
delete: async (table, where,
|
|
552
|
-
return this.delete(table, where);
|
|
626
|
+
delete: async (table, where, options) => {
|
|
627
|
+
return this.delete(table, where, options, txDb());
|
|
553
628
|
},
|
|
554
629
|
upsert: async (table, data, options) => {
|
|
555
|
-
return this.upsert(table, data, options);
|
|
630
|
+
return this.upsert(table, data, options, txDb());
|
|
556
631
|
},
|
|
557
632
|
// Savepoints disabled per approved approach
|
|
558
633
|
savepoint: void 0,
|
|
559
634
|
rollbackToSavepoint: void 0,
|
|
560
|
-
releaseSavepoint: void 0
|
|
635
|
+
releaseSavepoint: void 0,
|
|
636
|
+
// Expose the transaction-bound Drizzle instance so callers can run
|
|
637
|
+
// Drizzle sql templates inside this transaction (junction-table writes
|
|
638
|
+
// need this to be atomic with the entry write). Reuses the memoized
|
|
639
|
+
// txDb() built for the delegated CRUD methods.
|
|
640
|
+
getDrizzle: () => txDb()
|
|
561
641
|
};
|
|
562
642
|
}
|
|
563
643
|
/**
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["DrizzleAdapter","mysql","checkDialectVersion","createDatabaseError","drizzle","rows","isDatabaseError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @nextlyhq/adapter-mysql\n *\n * MySQL database adapter for Nextly.\n * Extends DrizzleAdapter from @nextlyhq/adapter-drizzle to provide MySQL-specific functionality.\n *\n * @remarks\n * This adapter uses the mysql2 package for database connectivity and provides:\n * - Connection pooling via mysql2 Pool\n * - Full transaction support with isolation levels\n * - CRUD operations with workarounds for missing RETURNING clause\n * - MySQL-specific error classification\n * - Automatic retry for deadlocks (error 1213)\n *\n * @example\n * ```typescript\n * import { createMySqlAdapter } from '@nextlyhq/adapter-mysql';\n *\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * });\n *\n * await adapter.connect();\n *\n * // Query data\n * const users = await adapter.select('users', {\n * where: { and: [{ column: 'status', op: '=', value: 'active' }] },\n * limit: 10,\n * });\n *\n * await adapter.disconnect();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { DrizzleAdapter } from \"@nextlyhq/adapter-drizzle\";\n// F17: connect-time DB version check shared across all adapters.\nimport {\n createDatabaseError,\n isDatabaseError,\n type MySqlAdapterConfig,\n type DatabaseCapabilities,\n type PoolStats,\n type TransactionContext,\n type TransactionOptions,\n type SqlParam,\n type WhereClause,\n type WhereCondition,\n type WhereOperator,\n type SelectOptions,\n type InsertOptions,\n type UpdateOptions,\n type DeleteOptions,\n type UpsertOptions,\n type OrderBySpec,\n type JoinSpec,\n type DatabaseError,\n type DatabaseErrorKind,\n type BaseAdapterConfig,\n type AdapterLogger,\n type PoolConfig,\n type SslConfig,\n} from \"@nextlyhq/adapter-drizzle/types\";\nimport { checkDialectVersion } from \"@nextlyhq/adapter-drizzle/version-check\";\nimport { drizzle, type MySql2Database } from \"drizzle-orm/mysql2\";\nimport mysql from \"mysql2/promise\";\nimport type {\n PoolOptions,\n RowDataPacket,\n ResultSetHeader,\n} from \"mysql2/promise\";\n\n// mysql2 type definitions use mixin patterns that TypeScript struggles with.\n// We define explicit interfaces for the methods we need.\n\n/**\n * Query result type - either rows or a result header\n */\ntype QueryResult = RowDataPacket[] | RowDataPacket[][] | ResultSetHeader;\n\n/**\n * Queryable interface for mysql2 connections\n */\ninterface Queryable {\n query<T extends QueryResult>(sql: string): Promise<[T, unknown]>;\n query<T extends QueryResult>(\n sql: string,\n values: unknown[]\n ): Promise<[T, unknown]>;\n}\n\n/**\n * mysql2 Pool interface with query method\n */\ninterface Pool extends Queryable {\n getConnection(): Promise<PoolConnection>;\n end(): Promise<void>;\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n}\n\n/**\n * mysql2 PoolConnection interface with query and release methods\n */\ninterface PoolConnection extends Queryable {\n release(): void;\n}\n\n// Re-export types for convenience\nexport type {\n MySqlAdapterConfig,\n DatabaseCapabilities,\n PoolStats,\n TransactionContext,\n TransactionOptions,\n SqlParam,\n WhereClause,\n WhereCondition,\n WhereOperator,\n SelectOptions,\n InsertOptions,\n UpdateOptions,\n DeleteOptions,\n UpsertOptions,\n OrderBySpec,\n JoinSpec,\n DatabaseError,\n DatabaseErrorKind,\n BaseAdapterConfig,\n AdapterLogger,\n PoolConfig,\n SslConfig,\n};\n\n/**\n * Package version\n */\nexport const VERSION = \"0.1.0\";\n\n/**\n * Default pool configuration values.\n */\nconst DEFAULT_POOL_CONFIG = {\n min: 2,\n max: 10,\n idleTimeoutMs: 30000,\n connectionTimeoutMs: 10000,\n};\n\n/**\n * MySQL error codes mapping to DatabaseErrorKind.\n *\n * @see https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html\n */\nconst MYSQL_ERROR_CODES: Record<number, DatabaseErrorKind> = {\n // Unique/Duplicate key violations\n 1022: \"unique_violation\", // ER_DUP_KEY\n 1062: \"unique_violation\", // ER_DUP_ENTRY\n 1169: \"unique_violation\", // ER_DUP_UNIQUE\n 1586: \"unique_violation\", // ER_DUP_ENTRY_WITH_KEY_NAME\n\n // Foreign key violations\n 1216: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW\n 1217: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED\n 1451: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED_2\n 1452: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW_2\n\n // Not null violations\n 1048: \"not_null_violation\", // ER_BAD_NULL_ERROR\n 1364: \"not_null_violation\", // ER_NO_DEFAULT_FOR_FIELD\n\n // Check constraint violations (MySQL 8.0.16+)\n 3819: \"check_violation\", // ER_CHECK_CONSTRAINT_VIOLATED\n\n // Deadlock\n 1213: \"deadlock\", // ER_LOCK_DEADLOCK\n\n // Timeout\n 1205: \"timeout\", // ER_LOCK_WAIT_TIMEOUT\n\n // Connection errors\n 1040: \"connection\", // ER_CON_COUNT_ERROR - Too many connections\n 1042: \"connection\", // ER_BAD_HOST_ERROR\n 1043: \"connection\", // ER_HANDSHAKE_ERROR\n 1044: \"connection\", // ER_DBACCESS_DENIED_ERROR\n 1045: \"connection\", // ER_ACCESS_DENIED_ERROR\n 1129: \"connection\", // ER_HOST_IS_BLOCKED\n 1130: \"connection\", // ER_HOST_NOT_PRIVILEGED\n 2002: \"connection\", // CR_CONNECTION_ERROR\n 2003: \"connection\", // CR_CONN_HOST_ERROR\n 2006: \"connection\", // CR_SERVER_GONE_ERROR\n 2013: \"connection\", // CR_SERVER_LOST\n\n // Query errors\n 1064: \"query\", // ER_PARSE_ERROR\n 1146: \"query\", // ER_NO_SUCH_TABLE\n 1054: \"query\", // ER_BAD_FIELD_ERROR\n};\n\n/**\n * Delay helper for retry logic.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * MySQL database adapter for Nextly.\n *\n * Extends the base DrizzleAdapter to provide MySQL-specific functionality\n * using the mysql2 package.\n *\n * @remarks\n * MySQL has some limitations compared to PostgreSQL:\n * - No native RETURNING clause (requires INSERT then SELECT)\n * - No native ILIKE (uses LOWER() LIKE workaround)\n * - No native JSONB (uses JSON type)\n * - No array types\n * - Savepoints disabled for safety (MySQL has nested transaction quirks)\n *\n * @example\n * ```typescript\n * const adapter = new MySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * pool: { max: 20 },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport class MySqlAdapter extends DrizzleAdapter {\n /**\n * The database dialect - always 'mysql' for this adapter.\n */\n readonly dialect = \"mysql\" as const;\n\n /**\n * Adapter configuration.\n */\n protected readonly config: MySqlAdapterConfig;\n\n /**\n * Connection pool instance.\n */\n private pool: Pool | null = null;\n\n /**\n * Connection state flag.\n */\n private connected = false;\n\n /**\n * Creates a new MySQL adapter instance.\n *\n * @param config - Adapter configuration\n */\n constructor(config: MySqlAdapterConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the MySQL database.\n * Creates a connection pool using mysql2.\n *\n * @remarks\n * This method initializes the connection pool and verifies connectivity\n * by executing a simple query. It is idempotent - calling it multiple\n * times will not create multiple pools.\n *\n * @throws {DatabaseError} If connection fails\n */\n async connect(): Promise<void> {\n if (this.connected && this.pool) {\n return;\n }\n\n try {\n const poolConfig = this.buildPoolConfig();\n // Cast to our Pool interface - mysql2's mixin types don't resolve properly\n this.pool = mysql.createPool(poolConfig) as unknown as Pool;\n\n // Verify connection with smoke test, then check dialect version.\n // Why: F17 hard-fails at connect on real MySQL <8.0 (no variant\n // token detected). Recognized variants (MariaDB, TiDB, Aurora,\n // PlanetScale, Vitess) log a warning via the adapter logger and\n // proceed. Truly unparseable strings hard-fail so users see the\n // issue at boot rather than mid-apply.\n const connection = await this.pool.getConnection();\n try {\n await connection.query(\"SELECT 1\");\n await checkDialectVersion(connection, \"mysql\", {\n // Why: route variant warnings through the adapter's logger so\n // users see a single, consistent log surface.\n onWarning: msg => this.config.logger?.warn?.(msg),\n });\n this.connected = true;\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection established\", {\n host: this.config.host ?? \"from URL\",\n database: this.config.database ?? \"from URL\",\n });\n }\n } finally {\n connection.release();\n }\n } catch (error) {\n // Clean up on failure\n if (this.pool) {\n await this.pool.end().catch(() => {});\n this.pool = null;\n }\n throw this.classifyError(error);\n }\n }\n\n /**\n * Disconnect from the MySQL database.\n * Gracefully closes the connection pool.\n *\n * @remarks\n * This method is idempotent - calling it multiple times is safe.\n * It waits for all connections to be released before shutting down.\n */\n async disconnect(): Promise<void> {\n if (!this.pool) {\n return;\n }\n\n try {\n await this.pool.end();\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection closed\");\n }\n } finally {\n this.pool = null;\n this.connected = false;\n }\n }\n\n /**\n * Check if connected to the database.\n */\n isConnected(): boolean {\n return this.connected && this.pool !== null;\n }\n\n /**\n * Get connection pool statistics.\n * Returns null if not connected.\n *\n * @remarks\n * MySQL2 pool exposes different stats than pg:\n * - _allConnections: all connections\n * - _freeConnections: idle connections\n * - _connectionQueue: waiting requests\n */\n getPoolStats(): PoolStats | null {\n if (!this.pool) {\n return null;\n }\n\n // mysql2 Pool internal properties (cast to access internals)\n const poolInternal = this.pool as unknown as {\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n };\n\n const internal = poolInternal.pool;\n if (!internal) {\n return {\n total: 0,\n idle: 0,\n waiting: 0,\n active: 0,\n };\n }\n\n const total = internal._allConnections?.length ?? 0;\n const idle = internal._freeConnections?.length ?? 0;\n const waiting = internal._connectionQueue?.length ?? 0;\n\n return {\n total,\n idle,\n waiting,\n active: total - idle,\n };\n }\n\n /**\n * Execute a raw SQL query.\n *\n * @param sql - SQL query string with ? placeholders\n * @param params - Query parameters\n * @returns Query results\n *\n * @throws {DatabaseError} If query execution fails\n */\n async executeQuery<T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> {\n const pool = this.ensurePool();\n const startTime = Date.now();\n\n try {\n const [rows] = await pool.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n\n // Log query if logger configured\n if (this.config.logger?.query) {\n const durationMs = Date.now() - startTime;\n this.config.logger.query(sql, params, durationMs);\n }\n\n return rows as T[];\n } catch (error) {\n throw this.classifyError(error, sql);\n }\n }\n\n /**\n * Execute work within a transaction.\n *\n * @param work - Function containing transactional operations\n * @param options - Transaction options (isolation level, timeout, retry)\n * @returns Result of the work function\n *\n * @remarks\n * MySQL transactions support isolation levels. Automatic retry is\n * implemented for deadlocks (error 1213) when `retryCount` is specified.\n *\n * Note: Savepoints are disabled in this adapter for safety due to\n * MySQL's quirks with nested transactions.\n */\n async transaction<T>(\n work: (tx: TransactionContext) => Promise<T>,\n options?: TransactionOptions\n ): Promise<T> {\n const pool = this.ensurePool();\n const maxAttempts = (options?.retryCount ?? 0) + 1;\n const retryDelayMs = options?.retryDelayMs ?? 100;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const connection = await pool.getConnection();\n const startTime = Date.now();\n\n try {\n // Begin transaction with options\n await this.beginTransaction(connection, options);\n\n // Create transaction context\n const ctx = this.createTransactionContext(connection);\n\n // Execute callback\n const result = await work(ctx);\n\n // Commit transaction\n await connection.query(\"COMMIT\");\n\n // Log success\n if (this.config.logger?.debug) {\n const durationMs = Date.now() - startTime;\n this.config.logger.debug(\"Transaction committed\", {\n attempt,\n durationMs,\n });\n }\n\n return result;\n } catch (error) {\n // Rollback transaction\n await connection.query(\"ROLLBACK\").catch(() => {});\n\n lastError = error;\n\n // Check if error is retryable (deadlock only per approved approach)\n const mysqlError = error as { errno?: number; code?: string };\n const isRetryable = mysqlError.errno === 1213; // ER_LOCK_DEADLOCK\n\n if (isRetryable && attempt < maxAttempts) {\n if (this.config.logger?.warn) {\n this.config.logger.warn(\n `Transaction failed with deadlock, retrying (${attempt}/${maxAttempts})`,\n { errno: mysqlError.errno, attempt }\n );\n }\n await delay(retryDelayMs * attempt); // Exponential backoff\n continue;\n }\n\n throw this.classifyError(error);\n } finally {\n connection.release();\n }\n }\n\n // Should not reach here, but handle just in case\n throw this.classifyError(lastError);\n }\n\n /**\n * Get MySQL database capabilities.\n *\n * @remarks\n * MySQL has some limitations:\n * - No JSONB (uses JSON)\n * - No arrays\n * - No native ILIKE\n * - No RETURNING clause\n * - Savepoints disabled for safety\n */\n getCapabilities(): DatabaseCapabilities {\n return {\n dialect: \"mysql\",\n supportsJsonb: false, // MySQL uses JSON, not JSONB\n supportsJson: true,\n supportsArrays: false, // MySQL doesn't support array types\n supportsGeneratedColumns: true, // MySQL 5.7.6+\n supportsFts: true, // MySQL has FULLTEXT indexes\n supportsIlike: false, // No native ILIKE, use LOWER() LIKE\n supportsReturning: false, // No RETURNING clause in MySQL\n supportsSavepoints: false, // Disabled for safety per approved approach\n supportsOnConflict: true, // ON DUPLICATE KEY UPDATE\n maxParamsPerQuery: 65535, // MySQL limit\n maxIdentifierLength: 64, // MySQL limit\n };\n }\n\n /**\n * Build a placeholder for MySQL (uses ? instead of $1, $2, etc.)\n *\n * @param _index - Parameter index (ignored for MySQL)\n * @returns The ? placeholder\n */\n protected buildPlaceholder(_index: number): string {\n return \"?\";\n }\n\n /**\n * Build multiple placeholders for MySQL.\n *\n * @param count - Number of placeholders needed\n * @param _startIndex - Starting index (ignored for MySQL)\n * @returns Comma-separated ? placeholders\n */\n protected buildPlaceholders(count: number, _startIndex: number = 0): string {\n return Array(count).fill(\"?\").join(\", \");\n }\n\n /**\n * Escape an identifier for MySQL (uses backticks instead of double quotes).\n *\n * @param identifier - The identifier to escape\n * @returns Escaped identifier with backticks\n */\n protected escapeIdentifier(identifier: string): string {\n // MySQL uses backticks for identifiers\n return `\\`${identifier.replace(/`/g, \"``\")}\\``;\n }\n\n // ============================================================\n // Protected Helper Methods\n // ============================================================\n\n /**\n * Ensures pool is connected and returns it.\n *\n * @throws {DatabaseError} If not connected\n */\n private ensurePool(): Pool {\n if (!this.pool) {\n throw createDatabaseError({\n kind: \"connection\",\n message: \"MySqlAdapter is not connected. Call connect() first.\",\n });\n }\n return this.pool;\n }\n\n /**\n * Return the typed Drizzle instance for MySQL.\n * Guarded for server-only usage and requires an active connection.\n *\n * @param schema - Optional schema for relational queries (db.query.*)\n * @returns Drizzle ORM instance wrapping the mysql2 pool connection\n * @throws {Error} If called in browser or not connected\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n getDrizzle<T = MySql2Database<any>>(schema?: Record<string, unknown>): T {\n if (typeof window !== \"undefined\") {\n throw new Error(\"getDrizzle() is server-only\");\n }\n const pool = this.ensurePool();\n // Cast needed because mysql2/promise Pool type differs from drizzle's expected type\n // MySQL requires mode when schema is provided\n\n /* eslint-disable @typescript-eslint/no-explicit-any */\n return (\n schema\n ? drizzle({ client: pool as any, schema, mode: \"default\" })\n : drizzle(pool as any)\n ) as T;\n /* eslint-enable @typescript-eslint/no-explicit-any */\n }\n\n /**\n * Builds mysql2 Pool configuration from adapter config.\n */\n private buildPoolConfig(): PoolOptions {\n const config: PoolOptions = {};\n\n // Connection string or explicit options\n if (this.config.url) {\n config.uri = this.config.url;\n } else {\n if (this.config.host) config.host = this.config.host;\n if (this.config.port) config.port = this.config.port;\n if (this.config.database) config.database = this.config.database;\n if (this.config.user) config.user = this.config.user;\n if (this.config.password) config.password = this.config.password;\n }\n\n // Pool settings - mysql2 uses different property names\n config.connectionLimit = this.config.pool?.max ?? DEFAULT_POOL_CONFIG.max;\n config.idleTimeout =\n this.config.pool?.idleTimeoutMs ?? DEFAULT_POOL_CONFIG.idleTimeoutMs;\n config.connectTimeout =\n this.config.pool?.connectionTimeoutMs ??\n DEFAULT_POOL_CONFIG.connectionTimeoutMs;\n\n // Enable waiting for connections when pool is full\n config.waitForConnections = true;\n config.queueLimit = 0; // Unlimited queue\n\n // SSL configuration\n if (this.config.ssl) {\n if (typeof this.config.ssl === \"boolean\") {\n config.ssl = this.config.ssl ? {} : undefined;\n } else {\n config.ssl = {\n rejectUnauthorized: this.config.ssl.rejectUnauthorized,\n ca: this.config.ssl.ca,\n cert: this.config.ssl.cert,\n key: this.config.ssl.key,\n };\n }\n }\n\n // MySQL-specific options\n if (this.config.timezone) {\n config.timezone = this.config.timezone;\n }\n\n if (this.config.charset) {\n config.charset = this.config.charset;\n }\n\n // Enable multiple statements if needed (disabled by default for security)\n config.multipleStatements = false;\n\n // Date handling\n config.dateStrings = false; // Return Date objects\n\n return config;\n }\n\n /**\n * Begins a transaction with the specified options.\n */\n private async beginTransaction(\n connection: PoolConnection,\n options?: TransactionOptions\n ): Promise<void> {\n // Set isolation level if specified (must be done before BEGIN)\n if (options?.isolationLevel) {\n const isolationMap: Record<string, string> = {\n \"read uncommitted\": \"READ UNCOMMITTED\",\n \"read committed\": \"READ COMMITTED\",\n \"repeatable read\": \"REPEATABLE READ\",\n serializable: \"SERIALIZABLE\",\n };\n const level = isolationMap[options.isolationLevel];\n if (level) {\n await connection.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);\n }\n }\n\n // Set read-only mode if specified\n if (options?.readOnly) {\n await connection.query(\"SET TRANSACTION READ ONLY\");\n }\n\n // Begin the transaction\n await connection.query(\"START TRANSACTION\");\n\n // Set lock wait timeout if specified\n if (options?.timeoutMs) {\n // MySQL uses seconds for lock_wait_timeout\n const timeoutSeconds = Math.ceil(options.timeoutMs / 1000);\n await connection.query(\n `SET SESSION innodb_lock_wait_timeout = ${timeoutSeconds}`\n );\n }\n }\n\n /**\n * Creates a TransactionContext for the given connection.\n *\n * @remarks\n * Note: Savepoint methods are not implemented (set to undefined)\n * as savepoints are disabled in this adapter per approved approach.\n */\n private createTransactionContext(\n connection: PoolConnection\n ): TransactionContext {\n return {\n execute: async <T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> => {\n const [rows] = await connection.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n return rows as T[];\n },\n\n insert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n _options?: InsertOptions\n ): Promise<T> => {\n const columns = Object.keys(data);\n const values = Object.values(data);\n const placeholders = this.buildPlaceholders(values.length, 0);\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES (${placeholders})`;\n\n const [result] = await connection.query<ResultSetHeader>(sql, values);\n\n // MySQL doesn't have RETURNING, so we need to SELECT the inserted row\n // Use insertId if available (auto-increment), otherwise use all inserted values\n if (result.insertId) {\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE id = ?`,\n [result.insertId]\n );\n return rows[0] as T;\n }\n\n // Fallback: SELECT by all inserted values\n const whereClauses = columns.map(\n c => `${this.escapeIdentifier(c)} = ?`\n );\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(\" AND \")} LIMIT 1`,\n values\n );\n return rows[0] as T;\n },\n\n insertMany: async <T = unknown>(\n table: string,\n data: Record<string, unknown>[],\n _options?: InsertOptions\n ): Promise<T[]> => {\n if (data.length === 0) return [];\n\n const columns = Object.keys(data[0]);\n const allValues: unknown[] = [];\n const valuesClauses: string[] = [];\n\n for (const record of data) {\n const placeholders: string[] = [];\n for (const col of columns) {\n allValues.push(record[col]);\n placeholders.push(\"?\");\n }\n valuesClauses.push(`(${placeholders.join(\", \")})`);\n }\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES ${valuesClauses.join(\", \")}`;\n\n const [result] = await connection.query<ResultSetHeader>(\n sql,\n allValues\n );\n\n // For bulk insert, we need to SELECT the inserted rows\n // MySQL's insertId gives the first auto-increment ID\n if (result.insertId && result.affectedRows > 0) {\n const ids: number[] = [];\n for (let i = 0; i < result.affectedRows; i++) {\n ids.push(result.insertId + i);\n }\n const placeholders = ids.map(() => \"?\").join(\", \");\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,\n ids\n );\n return rows as T[];\n }\n\n // Fallback: return empty if we can't determine inserted rows\n return [];\n },\n\n // TransactionContext CRUD methods delegate to the adapter's CRUD\n // which uses Drizzle query API via the TableResolver.\n select: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T[]> => {\n return this.select<T>(table, options);\n },\n\n selectOne: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T | null> => {\n return this.selectOne<T>(table, options);\n },\n\n update: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n where: WhereClause,\n options?: UpdateOptions\n ): Promise<T[]> => {\n return this.update<T>(table, data, where, options);\n },\n\n delete: async (\n table: string,\n where: WhereClause,\n _options?: DeleteOptions\n ): Promise<number> => {\n return this.delete(table, where);\n },\n\n upsert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options: UpsertOptions\n ): Promise<T> => {\n return this.upsert<T>(table, data, options);\n },\n\n // Savepoints disabled per approved approach\n savepoint: undefined,\n rollbackToSavepoint: undefined,\n releaseSavepoint: undefined,\n };\n }\n\n /**\n * Classifies a MySQL error into a DatabaseError.\n *\n * @param error - Original error from mysql2\n * @param sql - SQL statement that caused the error (optional)\n * @returns DatabaseError with proper classification\n */\n private classifyError(error: unknown, sql?: string): DatabaseError {\n // Why short-circuit on existing DatabaseError: F17's\n // UnsupportedDialectVersionError is already a typed DatabaseError with\n // kind: \"unsupported_version\" plus detectedVersion/requiredVersion\n // fields. Re-wrapping it here would erase those fields and re-tag it\n // as kind: \"unknown\".\n if (isDatabaseError(error)) return error;\n\n const mysqlError = error as {\n errno?: number;\n code?: string;\n sqlState?: string;\n message?: string;\n sql?: string;\n };\n\n // Determine error kind from MySQL error number\n const kind: DatabaseErrorKind =\n (mysqlError.errno && MYSQL_ERROR_CODES[mysqlError.errno]) || \"unknown\";\n\n // Build error message\n let message = mysqlError.message ?? String(error);\n if (sql && kind === \"query\") {\n message = `Query failed: ${message}`;\n }\n\n return createDatabaseError({\n kind,\n message,\n code: mysqlError.code ?? mysqlError.errno?.toString(),\n detail: mysqlError.sql,\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Override handleQueryError to use MySQL-specific classification.\n */\n protected override handleQueryError(\n error: unknown,\n operation: string,\n table: string\n ): DatabaseError {\n const dbError = this.classifyError(error);\n\n // Add operation context if not already present\n if (!dbError.message.includes(operation)) {\n dbError.message = `${operation} operation failed on table '${table}': ${dbError.message}`;\n }\n\n if (!dbError.table) {\n dbError.table = table;\n }\n\n return dbError;\n }\n}\n\n/**\n * Create a MySQL database adapter.\n *\n * @param config - MySQL adapter configuration\n * @returns A new MySqlAdapter instance\n *\n * @example\n * ```typescript\n * // Simple usage with URL\n * const adapter = createMySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * });\n *\n * // Full configuration\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * pool: {\n * min: 2,\n * max: 20,\n * idleTimeoutMs: 30000,\n * connectionTimeoutMs: 10000,\n * },\n * ssl: {\n * rejectUnauthorized: true,\n * },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport function createMySqlAdapter(config: MySqlAdapterConfig): MySqlAdapter {\n return new MySqlAdapter(config);\n}\n\n/**\n * Type guard to check if a value is a MySqlAdapter.\n *\n * @param value - Value to check\n * @returns True if value is a MySqlAdapter instance\n *\n * @example\n * ```typescript\n * if (isMySqlAdapter(adapter)) {\n * // TypeScript knows adapter is MySqlAdapter\n * console.log('Using MySQL');\n * }\n * ```\n */\nexport function isMySqlAdapter(value: unknown): value is MySqlAdapter {\n return value instanceof MySqlAdapter;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["DrizzleAdapter","mysql","checkDialectVersion","isApplicationError","createDatabaseError","drizzle","rows","isDatabaseError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @nextlyhq/adapter-mysql\n *\n * MySQL database adapter for Nextly.\n * Extends DrizzleAdapter from @nextlyhq/adapter-drizzle to provide MySQL-specific functionality.\n *\n * @remarks\n * This adapter uses the mysql2 package for database connectivity and provides:\n * - Connection pooling via mysql2 Pool\n * - Full transaction support with isolation levels\n * - CRUD operations with workarounds for missing RETURNING clause\n * - MySQL-specific error classification\n * - Automatic retry for deadlocks (error 1213)\n *\n * @example\n * ```typescript\n * import { createMySqlAdapter } from '@nextlyhq/adapter-mysql';\n *\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * });\n *\n * await adapter.connect();\n *\n * // Query data\n * const users = await adapter.select('users', {\n * where: { and: [{ column: 'status', op: '=', value: 'active' }] },\n * limit: 10,\n * });\n *\n * await adapter.disconnect();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { DrizzleAdapter } from \"@nextlyhq/adapter-drizzle\";\n// F17: connect-time DB version check shared across all adapters.\nimport {\n createDatabaseError,\n isDatabaseError,\n type MySqlAdapterConfig,\n type DatabaseCapabilities,\n type PoolStats,\n type TransactionContext,\n type TransactionOptions,\n type SqlParam,\n type WhereClause,\n type WhereCondition,\n type WhereOperator,\n type SelectOptions,\n type InsertOptions,\n type UpdateOptions,\n type DeleteOptions,\n type UpsertOptions,\n type OrderBySpec,\n type JoinSpec,\n type DatabaseError,\n type DatabaseErrorKind,\n type BaseAdapterConfig,\n type AdapterLogger,\n type PoolConfig,\n type SslConfig,\n isApplicationError,\n} from \"@nextlyhq/adapter-drizzle/types\";\nimport { checkDialectVersion } from \"@nextlyhq/adapter-drizzle/version-check\";\nimport type { AnyRelations, SQL } from \"drizzle-orm\";\nimport { drizzle, type MySql2Database } from \"drizzle-orm/mysql2\";\nimport type {\n Pool as CallbackPool,\n Connection as CallbackConnection,\n} from \"mysql2\";\nimport mysql from \"mysql2/promise\";\nimport type {\n PoolOptions,\n RowDataPacket,\n ResultSetHeader,\n} from \"mysql2/promise\";\n\n// mysql2 type definitions use mixin patterns that TypeScript struggles with.\n// We define explicit interfaces for the methods we need.\n\n/**\n * Query result type - either rows or a result header\n */\ntype QueryResult = RowDataPacket[] | RowDataPacket[][] | ResultSetHeader;\n\n/**\n * Queryable interface for mysql2 connections\n */\ninterface Queryable {\n query<T extends QueryResult>(sql: string): Promise<[T, unknown]>;\n query<T extends QueryResult>(\n sql: string,\n values: unknown[]\n ): Promise<[T, unknown]>;\n}\n\n/**\n * mysql2 Pool interface with query method\n */\ninterface Pool extends Queryable {\n getConnection(): Promise<PoolConnection>;\n end(): Promise<void>;\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n}\n\n/**\n * mysql2 PoolConnection interface with query and release methods\n */\ninterface PoolConnection extends Queryable {\n release(): void;\n}\n\n// Re-export types for convenience\nexport type {\n MySqlAdapterConfig,\n DatabaseCapabilities,\n PoolStats,\n TransactionContext,\n TransactionOptions,\n SqlParam,\n WhereClause,\n WhereCondition,\n WhereOperator,\n SelectOptions,\n InsertOptions,\n UpdateOptions,\n DeleteOptions,\n UpsertOptions,\n OrderBySpec,\n JoinSpec,\n DatabaseError,\n DatabaseErrorKind,\n BaseAdapterConfig,\n AdapterLogger,\n PoolConfig,\n SslConfig,\n};\n\n/**\n * Package version\n */\nexport const VERSION = \"0.1.0\";\n\n/**\n * Default pool configuration values.\n */\nconst DEFAULT_POOL_CONFIG = {\n min: 2,\n max: 10,\n idleTimeoutMs: 30000,\n connectionTimeoutMs: 10000,\n};\n\n/**\n * MySQL error codes mapping to DatabaseErrorKind.\n *\n * @see https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html\n */\nconst MYSQL_ERROR_CODES: Record<number, DatabaseErrorKind> = {\n // Unique/Duplicate key violations\n 1022: \"unique_violation\", // ER_DUP_KEY\n 1062: \"unique_violation\", // ER_DUP_ENTRY\n 1169: \"unique_violation\", // ER_DUP_UNIQUE\n 1586: \"unique_violation\", // ER_DUP_ENTRY_WITH_KEY_NAME\n\n // Foreign key violations\n 1216: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW\n 1217: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED\n 1451: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED_2\n 1452: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW_2\n\n // Not null violations\n 1048: \"not_null_violation\", // ER_BAD_NULL_ERROR\n 1364: \"not_null_violation\", // ER_NO_DEFAULT_FOR_FIELD\n\n // Check constraint violations (MySQL 8.0.16+)\n 3819: \"check_violation\", // ER_CHECK_CONSTRAINT_VIOLATED\n\n // Deadlock\n 1213: \"deadlock\", // ER_LOCK_DEADLOCK\n\n // Timeout\n 1205: \"timeout\", // ER_LOCK_WAIT_TIMEOUT\n\n // Connection errors\n 1040: \"connection\", // ER_CON_COUNT_ERROR - Too many connections\n 1042: \"connection\", // ER_BAD_HOST_ERROR\n 1043: \"connection\", // ER_HANDSHAKE_ERROR\n 1044: \"connection\", // ER_DBACCESS_DENIED_ERROR\n 1045: \"connection\", // ER_ACCESS_DENIED_ERROR\n 1129: \"connection\", // ER_HOST_IS_BLOCKED\n 1130: \"connection\", // ER_HOST_NOT_PRIVILEGED\n 2002: \"connection\", // CR_CONNECTION_ERROR\n 2003: \"connection\", // CR_CONN_HOST_ERROR\n 2006: \"connection\", // CR_SERVER_GONE_ERROR\n 2013: \"connection\", // CR_SERVER_LOST\n\n // Query errors\n 1064: \"query\", // ER_PARSE_ERROR\n 1146: \"query\", // ER_NO_SUCH_TABLE\n 1054: \"query\", // ER_BAD_FIELD_ERROR\n};\n\n/**\n * Delay helper for retry logic.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * MySQL database adapter for Nextly.\n *\n * Extends the base DrizzleAdapter to provide MySQL-specific functionality\n * using the mysql2 package.\n *\n * @remarks\n * MySQL has some limitations compared to PostgreSQL:\n * - No native RETURNING clause (requires INSERT then SELECT)\n * - No native ILIKE (uses LOWER() LIKE workaround)\n * - No native JSONB (uses JSON type)\n * - No array types\n * - Savepoints disabled for safety (MySQL has nested transaction quirks)\n *\n * @example\n * ```typescript\n * const adapter = new MySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * pool: { max: 20 },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport class MySqlAdapter extends DrizzleAdapter {\n // getDrizzle memoization: drizzle v1's constructor builds a relational\n // query builder per table in the relations config (~40 tables), and the\n // service layer resolves an instance on every db access — construct once\n // per relations object (identity-stable: the schema registry caches it\n // and hands out a NEW object on invalidation, which naturally misses\n // this cache and produces a fresh instance).\n private drizzleByRelations = new WeakMap<AnyRelations, unknown>();\n private drizzleBare: unknown;\n\n /**\n * The database dialect - always 'mysql' for this adapter.\n */\n readonly dialect = \"mysql\" as const;\n\n /**\n * Adapter configuration.\n */\n protected readonly config: MySqlAdapterConfig;\n\n /**\n * Connection pool instance.\n */\n private pool: Pool | null = null;\n\n /**\n * Connection state flag.\n */\n private connected = false;\n\n /**\n * Creates a new MySQL adapter instance.\n *\n * @param config - Adapter configuration\n */\n constructor(config: MySqlAdapterConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the MySQL database.\n * Creates a connection pool using mysql2.\n *\n * @remarks\n * This method initializes the connection pool and verifies connectivity\n * by executing a simple query. It is idempotent - calling it multiple\n * times will not create multiple pools.\n *\n * @throws {DatabaseError} If connection fails\n */\n async connect(): Promise<void> {\n if (this.connected && this.pool) {\n return;\n }\n\n try {\n const poolConfig = this.buildPoolConfig();\n // Cast to our Pool interface - mysql2's mixin types don't resolve properly\n this.pool = mysql.createPool(poolConfig) as unknown as Pool;\n\n // Verify connection with smoke test, then check dialect version.\n // Why: F17 hard-fails at connect on real MySQL <8.0 (no variant\n // token detected). Recognized variants (MariaDB, TiDB, Aurora,\n // PlanetScale, Vitess) log a warning via the adapter logger and\n // proceed. Truly unparseable strings hard-fail so users see the\n // issue at boot rather than mid-apply.\n const connection = await this.pool.getConnection();\n try {\n await connection.query(\"SELECT 1\");\n await checkDialectVersion(connection, \"mysql\", {\n // Why: route variant warnings through the adapter's logger so\n // users see a single, consistent log surface.\n onWarning: msg => this.config.logger?.warn?.(msg),\n });\n this.connected = true;\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection established\", {\n host: this.config.host ?? \"from URL\",\n database: this.config.database ?? \"from URL\",\n });\n }\n } finally {\n connection.release();\n }\n } catch (error) {\n // Clean up on failure\n if (this.pool) {\n await this.pool.end().catch(() => {});\n this.pool = null;\n }\n throw this.classifyError(error);\n }\n }\n\n /**\n * Disconnect from the MySQL database.\n * Gracefully closes the connection pool.\n *\n * @remarks\n * This method is idempotent - calling it multiple times is safe.\n * It waits for all connections to be released before shutting down.\n */\n async disconnect(): Promise<void> {\n // Detach the pool FIRST, then drop the memoized drizzle instances —\n // this closes the repopulation window where a concurrent getDrizzle()\n // call during the (async) pool.end() could cache an instance wrapping\n // the closing pool.\n const pool = this.pool;\n this.pool = null;\n this.drizzleBare = undefined;\n this.drizzleByRelations = new WeakMap();\n if (!pool) {\n return;\n }\n\n try {\n await pool.end();\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection closed\");\n }\n } finally {\n this.connected = false;\n }\n }\n\n /**\n * Check if connected to the database.\n */\n isConnected(): boolean {\n return this.connected && this.pool !== null;\n }\n\n /**\n * Get connection pool statistics.\n * Returns null if not connected.\n *\n * @remarks\n * MySQL2 pool exposes different stats than pg:\n * - _allConnections: all connections\n * - _freeConnections: idle connections\n * - _connectionQueue: waiting requests\n */\n getPoolStats(): PoolStats | null {\n if (!this.pool) {\n return null;\n }\n\n // mysql2 Pool internal properties (cast to access internals)\n const poolInternal = this.pool as unknown as {\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n };\n\n const internal = poolInternal.pool;\n if (!internal) {\n return {\n total: 0,\n idle: 0,\n waiting: 0,\n active: 0,\n };\n }\n\n const total = internal._allConnections?.length ?? 0;\n const idle = internal._freeConnections?.length ?? 0;\n const waiting = internal._connectionQueue?.length ?? 0;\n\n return {\n total,\n idle,\n waiting,\n active: total - idle,\n };\n }\n\n /**\n * Execute a raw SQL query.\n *\n * @param sql - SQL query string with ? placeholders\n * @param params - Query parameters\n * @returns Query results\n *\n * @throws {DatabaseError} If query execution fails\n */\n async executeQuery<T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> {\n const pool = this.ensurePool();\n const startTime = Date.now();\n\n try {\n const [rows] = await pool.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n\n // Log query if logger configured\n if (this.config.logger?.query) {\n const durationMs = Date.now() - startTime;\n this.config.logger.query(sql, params, durationMs);\n }\n\n return rows as T[];\n } catch (error) {\n throw this.classifyError(error, sql);\n }\n }\n\n /**\n * Execute work within a transaction.\n *\n * @param work - Function containing transactional operations\n * @param options - Transaction options (isolation level, timeout, retry)\n * @returns Result of the work function\n *\n * @remarks\n * MySQL transactions support isolation levels. Automatic retry is\n * implemented for deadlocks (error 1213) when `retryCount` is specified.\n *\n * Note: Savepoints are disabled in this adapter for safety due to\n * MySQL's quirks with nested transactions.\n */\n async transaction<T>(\n work: (tx: TransactionContext) => Promise<T>,\n options?: TransactionOptions\n ): Promise<T> {\n const pool = this.ensurePool();\n const maxAttempts = (options?.retryCount ?? 0) + 1;\n const retryDelayMs = options?.retryDelayMs ?? 100;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const connection = await pool.getConnection();\n const startTime = Date.now();\n\n try {\n // Begin transaction with options\n await this.beginTransaction(connection, options);\n\n // Create transaction context\n const ctx = this.createTransactionContext(connection);\n\n // Execute callback\n const result = await work(ctx);\n\n // Commit transaction\n await connection.query(\"COMMIT\");\n\n // Log success\n if (this.config.logger?.debug) {\n const durationMs = Date.now() - startTime;\n this.config.logger.debug(\"Transaction committed\", {\n attempt,\n durationMs,\n });\n }\n\n return result;\n } catch (error) {\n // Rollback transaction\n await connection.query(\"ROLLBACK\").catch(() => {});\n\n lastError = error;\n\n // Work inside a transaction may throw to roll the write back — a\n // refused value, a denied permission — and that is the application's\n // verdict, not the driver's failure. Rethrown before the retry check as\n // well as before classification: a refusal re-run is the same refusal,\n // and the caller must receive the code and payload it raised rather\n // than a generic database error with the detail stripped out.\n if (isApplicationError(error)) throw error;\n\n // Check if error is retryable (deadlock only per approved approach)\n const mysqlError = error as { errno?: number; code?: string };\n const isRetryable = mysqlError.errno === 1213; // ER_LOCK_DEADLOCK\n\n if (isRetryable && attempt < maxAttempts) {\n if (this.config.logger?.warn) {\n this.config.logger.warn(\n `Transaction failed with deadlock, retrying (${attempt}/${maxAttempts})`,\n { errno: mysqlError.errno, attempt }\n );\n }\n await delay(retryDelayMs * attempt); // Exponential backoff\n continue;\n }\n\n throw this.classifyError(error);\n } finally {\n connection.release();\n }\n }\n\n // Should not reach here, but handle just in case\n throw this.classifyError(lastError);\n }\n\n /**\n * Get MySQL database capabilities.\n *\n * @remarks\n * MySQL has some limitations:\n * - No JSONB (uses JSON)\n * - No arrays\n * - No native ILIKE\n * - No RETURNING clause\n * - Savepoints disabled for safety\n */\n getCapabilities(): DatabaseCapabilities {\n return {\n dialect: \"mysql\",\n supportsJsonb: false, // MySQL uses JSON, not JSONB\n supportsJson: true,\n supportsArrays: false, // MySQL doesn't support array types\n supportsGeneratedColumns: true, // MySQL 5.7.6+\n supportsFts: true, // MySQL has FULLTEXT indexes\n supportsIlike: false, // No native ILIKE, use LOWER() LIKE\n supportsReturning: false, // No RETURNING clause in MySQL\n supportsSavepoints: false, // Disabled for safety per approved approach\n supportsOnConflict: true, // ON DUPLICATE KEY UPDATE\n maxParamsPerQuery: 65535, // MySQL limit\n maxIdentifierLength: 64, // MySQL limit\n };\n }\n\n /**\n * Build a placeholder for MySQL (uses ? instead of $1, $2, etc.)\n *\n * @param _index - Parameter index (ignored for MySQL)\n * @returns The ? placeholder\n */\n protected buildPlaceholder(_index: number): string {\n return \"?\";\n }\n\n /**\n * Build multiple placeholders for MySQL.\n *\n * @param count - Number of placeholders needed\n * @param _startIndex - Starting index (ignored for MySQL)\n * @returns Comma-separated ? placeholders\n */\n protected buildPlaceholders(count: number, _startIndex: number = 0): string {\n return Array(count).fill(\"?\").join(\", \");\n }\n\n /**\n * Escape an identifier for MySQL (uses backticks instead of double quotes).\n *\n * @param identifier - The identifier to escape\n * @returns Escaped identifier with backticks\n */\n protected escapeIdentifier(identifier: string): string {\n // MySQL uses backticks for identifiers\n return `\\`${identifier.replace(/`/g, \"``\")}\\``;\n }\n\n // ============================================================\n // Protected Helper Methods\n // ============================================================\n\n /**\n * Ensures pool is connected and returns it.\n *\n * @throws {DatabaseError} If not connected\n */\n private ensurePool(): Pool {\n if (!this.pool) {\n throw createDatabaseError({\n kind: \"connection\",\n message: \"MySqlAdapter is not connected. Call connect() first.\",\n });\n }\n return this.pool;\n }\n\n /**\n * Return the typed Drizzle instance for MySQL.\n * Guarded for server-only usage and requires an active connection.\n *\n * @param schema - Optional schema for relational queries (db.query.*)\n * @returns Drizzle ORM instance wrapping the mysql2 pool connection\n * @throws {Error} If called in browser or not connected\n */\n getDrizzle<T = MySql2Database<AnyRelations>>(relations?: AnyRelations): T {\n if (typeof window !== \"undefined\") {\n throw new Error(\"getDrizzle() is server-only\");\n }\n const pool = this.ensurePool();\n // drizzle v1's mysql2 driver accepts the CALLBACK pool — handing it the\n // mysql2/promise wrapper throws (\"Cannot set properties of undefined\n // (setting 'supportBigNumbers')\"), so unwrap to the underlying pool.\n // The pre-v1 `mode` option no longer exists.\n const client = (pool as unknown as { pool: CallbackPool }).pool;\n if (!relations) {\n this.drizzleBare ??= drizzle({ client });\n return this.drizzleBare as T;\n }\n let cached = this.drizzleByRelations.get(relations);\n if (!cached) {\n cached = drizzle({ client, relations });\n this.drizzleByRelations.set(relations, cached);\n }\n return cached as T;\n }\n\n /**\n * Builds mysql2 Pool configuration from adapter config.\n */\n private buildPoolConfig(): PoolOptions {\n const config: PoolOptions = {};\n\n // Connection string or explicit options\n if (this.config.url) {\n config.uri = this.config.url;\n } else {\n if (this.config.host) config.host = this.config.host;\n if (this.config.port) config.port = this.config.port;\n if (this.config.database) config.database = this.config.database;\n if (this.config.user) config.user = this.config.user;\n if (this.config.password) config.password = this.config.password;\n }\n\n // Pool settings - mysql2 uses different property names\n config.connectionLimit = this.config.pool?.max ?? DEFAULT_POOL_CONFIG.max;\n config.idleTimeout =\n this.config.pool?.idleTimeoutMs ?? DEFAULT_POOL_CONFIG.idleTimeoutMs;\n config.connectTimeout =\n this.config.pool?.connectionTimeoutMs ??\n DEFAULT_POOL_CONFIG.connectionTimeoutMs;\n\n // Enable waiting for connections when pool is full\n config.waitForConnections = true;\n config.queueLimit = 0; // Unlimited queue\n\n // SSL configuration\n if (this.config.ssl) {\n if (typeof this.config.ssl === \"boolean\") {\n config.ssl = this.config.ssl ? {} : undefined;\n } else {\n config.ssl = {\n rejectUnauthorized: this.config.ssl.rejectUnauthorized,\n ca: this.config.ssl.ca,\n cert: this.config.ssl.cert,\n key: this.config.ssl.key,\n };\n }\n }\n\n // MySQL-specific options\n if (this.config.timezone) {\n config.timezone = this.config.timezone;\n }\n\n if (this.config.charset) {\n config.charset = this.config.charset;\n }\n\n // Enable multiple statements if needed (disabled by default for security)\n config.multipleStatements = false;\n\n // Date handling\n config.dateStrings = false; // Return Date objects\n\n return config;\n }\n\n /**\n * Begins a transaction with the specified options.\n */\n private async beginTransaction(\n connection: PoolConnection,\n options?: TransactionOptions\n ): Promise<void> {\n // Set isolation level if specified (must be done before BEGIN)\n if (options?.isolationLevel) {\n const isolationMap: Record<string, string> = {\n \"read uncommitted\": \"READ UNCOMMITTED\",\n \"read committed\": \"READ COMMITTED\",\n \"repeatable read\": \"REPEATABLE READ\",\n serializable: \"SERIALIZABLE\",\n };\n const level = isolationMap[options.isolationLevel];\n if (level) {\n await connection.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);\n }\n }\n\n // Set read-only mode if specified\n if (options?.readOnly) {\n await connection.query(\"SET TRANSACTION READ ONLY\");\n }\n\n // Begin the transaction\n await connection.query(\"START TRANSACTION\");\n\n // Set lock wait timeout if specified\n if (options?.timeoutMs) {\n // MySQL uses seconds for lock_wait_timeout\n const timeoutSeconds = Math.ceil(options.timeoutMs / 1000);\n await connection.query(\n `SET SESSION innodb_lock_wait_timeout = ${timeoutSeconds}`\n );\n }\n }\n\n /**\n * Creates a TransactionContext for the given connection.\n *\n * @remarks\n * Note: Savepoint methods are not implemented (set to undefined)\n * as savepoints are disabled in this adapter per approved approach.\n */\n private createTransactionContext(\n connection: PoolConnection\n ): TransactionContext {\n // Bind a Drizzle instance to this transaction's checked-out connection so\n // the delegated CRUD methods run inside the transaction and see its\n // uncommitted rows. drizzle's mysql2 driver needs the underlying CALLBACK\n // connection, which the mysql2/promise wrapper exposes on `.connection`\n // (mirrors the `.pool` unwrap in getDrizzle()); getDrizzle() itself wraps\n // the pool, which would use a different connection. Built lazily and\n // memoized: transactions that use only raw execute/insert never construct\n // it.\n const buildTxExecutor = () =>\n drizzle({\n client: (connection as unknown as { connection: CallbackConnection })\n .connection,\n });\n let txExecutor: ReturnType<typeof buildTxExecutor> | undefined;\n const txDb = () => (txExecutor ??= buildTxExecutor());\n return {\n execute: async <T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> => {\n const [rows] = await connection.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n return rows as T[];\n },\n\n // Run on the transaction-bound Drizzle instance rather than the pool, so\n // the statement is part of this transaction and sees its uncommitted rows.\n runStatement: async (statement: SQL): Promise<void> => {\n await txDb().execute(statement);\n },\n\n // mysql2 answers a `[rows, fields]` tuple; the transaction-bound instance\n // keeps the read inside this transaction so it sees its uncommitted writes.\n queryStatement: async <T = Record<string, unknown>>(\n statement: SQL\n ): Promise<T[]> => {\n const result = await txDb().execute(statement);\n // A tuple's first element is the rows. Anything else was not understood,\n // and must not reach a caller as \"there is nothing there\" — the same\n // reason the pooled `queryStatement` refuses rather than answering\n // empty.\n if (!Array.isArray(result)) {\n throw this.createDatabaseError(\n \"query\",\n \"Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.\",\n undefined\n );\n }\n return result[0] as unknown as T[];\n },\n\n lockRow: async (table: string, id: SqlParam): Promise<void> => {\n const idColumn = this.escapeIdentifier(\"id\");\n await connection.query(\n `SELECT ${idColumn} FROM ${this.escapeIdentifier(table)} ` +\n `WHERE ${idColumn} = ? FOR UPDATE`,\n [id] as unknown[]\n );\n },\n\n insert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options?: InsertOptions\n ): Promise<T> => {\n const mapped = this.mapRowToRawSql(this.getTableObject(table), data);\n const columns = Object.keys(mapped);\n const values = Object.values(mapped);\n const placeholders = this.buildPlaceholders(values.length, 0);\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES (${placeholders})`;\n\n const [result] = await connection.query<ResultSetHeader>(sql, values);\n\n const ret = options?.returning;\n // No columns requested: skip the select-back reread entirely.\n if (Array.isArray(ret) && ret.length === 0) {\n return undefined as T;\n }\n\n // MySQL has no RETURNING; select the inserted row back. Project only the\n // requested columns so a large JSON snapshot is not read unless asked.\n const insertTableObj = this.getTableObject(table);\n // Each timestamp's wall clock, spelled out by the database. mysql2\n // turns one into a `Date` in the LOCAL zone before this code sees it,\n // and that conversion cannot be undone: a wall clock inside a\n // daylight-saving gap is normalized away.\n const aliases = this.dateWallClockAliases(insertTableObj, ret ?? \"*\");\n const spelled = aliases\n .map(\n a =>\n `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`\n )\n .join(\", \");\n const projected =\n !ret || ret === \"*\"\n ? \"*\"\n : this.mapColumnNamesToSql(insertTableObj, ret)\n .map(c => this.escapeIdentifier(c))\n .join(\", \");\n const selectList = spelled ? `${projected}, ${spelled}` : projected;\n\n // Prefer the primary key: auto-increment via insertId, otherwise a\n // supplied id (manually-keyed tables like nextly_versions). Matching by\n // all values is a last resort because `col = NULL` never matches, so a\n // row with nullable columns would not be found by that path.\n const idValue = result.insertId ? result.insertId : mapped.id;\n if (idValue !== undefined) {\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE id = ?`,\n [idValue]\n );\n return this.mapRowFromRawSql(insertTableObj, rows[0] as T, aliases);\n }\n\n const whereClauses = columns.map(\n c => `${this.escapeIdentifier(c)} = ?`\n );\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(\" AND \")} LIMIT 1`,\n values\n );\n return this.mapRowFromRawSql(insertTableObj, rows[0] as T, aliases);\n },\n\n insertMany: async <T = unknown>(\n table: string,\n data: Record<string, unknown>[],\n options?: InsertOptions\n ): Promise<T[]> => {\n if (data.length === 0) return [];\n const retMany = options?.returning;\n const skipReread = Array.isArray(retMany) && retMany.length === 0;\n\n const tableObj = this.getTableObject(table);\n const mappedRecords = data.map(r => this.mapRowToRawSql(tableObj, r));\n const columns = Object.keys(mappedRecords[0]);\n const allValues: unknown[] = [];\n const valuesClauses: string[] = [];\n\n for (const record of mappedRecords) {\n const placeholders: string[] = [];\n for (const col of columns) {\n allValues.push(record[col]);\n placeholders.push(\"?\");\n }\n valuesClauses.push(`(${placeholders.join(\", \")})`);\n }\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES ${valuesClauses.join(\", \")}`;\n\n const [result] = await connection.query<ResultSetHeader>(\n sql,\n allValues\n );\n\n // For bulk insert, we need to SELECT the inserted rows\n // MySQL's insertId gives the first auto-increment ID\n if (!skipReread && result.insertId && result.affectedRows > 0) {\n const ids: number[] = [];\n for (let i = 0; i < result.affectedRows; i++) {\n ids.push(result.insertId + i);\n }\n const placeholders = ids.map(() => \"?\").join(\", \");\n const bulkAliases = this.dateWallClockAliases(tableObj, \"*\");\n const bulkSpelled = bulkAliases\n .map(\n a =>\n `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`\n )\n .join(\", \");\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT *${bulkSpelled ? `, ${bulkSpelled}` : \"\"} FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,\n ids\n );\n return (rows as T[]).map(r =>\n this.mapRowFromRawSql(tableObj, r, bulkAliases)\n );\n }\n\n // Fallback: return empty if we can't determine inserted rows\n return [];\n },\n\n // TransactionContext CRUD methods delegate to the adapter's Drizzle CRUD\n // but pass the transaction-bound executor so they run inside this\n // transaction rather than on the pool.\n select: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T[]> => {\n return this.select<T>(table, options, txDb());\n },\n\n selectOne: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T | null> => {\n return this.selectOne<T>(table, options, txDb());\n },\n\n update: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n where: WhereClause,\n options?: UpdateOptions\n ): Promise<T[]> => {\n return this.update<T>(table, data, where, options, txDb());\n },\n\n delete: async (\n table: string,\n where: WhereClause,\n options?: DeleteOptions\n ): Promise<number> => {\n return this.delete(table, where, options, txDb());\n },\n\n upsert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options: UpsertOptions\n ): Promise<T> => {\n return this.upsert<T>(table, data, options, txDb());\n },\n\n // Savepoints disabled per approved approach\n savepoint: undefined,\n rollbackToSavepoint: undefined,\n releaseSavepoint: undefined,\n\n // Expose the transaction-bound Drizzle instance so callers can run\n // Drizzle sql templates inside this transaction (junction-table writes\n // need this to be atomic with the entry write). Reuses the memoized\n // txDb() built for the delegated CRUD methods.\n getDrizzle: <T = unknown>(): T => txDb() as T,\n };\n }\n\n /**\n * Classifies a MySQL error into a DatabaseError.\n *\n * @param error - Original error from mysql2\n * @param sql - SQL statement that caused the error (optional)\n * @returns DatabaseError with proper classification\n */\n private classifyError(error: unknown, sql?: string): DatabaseError {\n // Why short-circuit on existing DatabaseError: F17's\n // UnsupportedDialectVersionError is already a typed DatabaseError with\n // kind: \"unsupported_version\" plus detectedVersion/requiredVersion\n // fields. Re-wrapping it here would erase those fields and re-tag it\n // as kind: \"unknown\".\n if (isDatabaseError(error)) return error;\n\n const mysqlError = error as {\n errno?: number;\n code?: string;\n sqlState?: string;\n message?: string;\n sql?: string;\n };\n\n // Determine error kind from MySQL error number\n const kind: DatabaseErrorKind =\n (mysqlError.errno && MYSQL_ERROR_CODES[mysqlError.errno]) || \"unknown\";\n\n // Build error message\n let message = mysqlError.message ?? String(error);\n if (sql && kind === \"query\") {\n message = `Query failed: ${message}`;\n }\n\n return createDatabaseError({\n kind,\n message,\n code: mysqlError.code ?? mysqlError.errno?.toString(),\n detail: mysqlError.sql,\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Override handleQueryError to use MySQL-specific classification.\n */\n protected override handleQueryError(\n error: unknown,\n operation: string,\n table: string\n ): DatabaseError {\n const dbError = this.classifyError(error);\n\n // Add operation context if not already present\n if (!dbError.message.includes(operation)) {\n dbError.message = `${operation} operation failed on table '${table}': ${dbError.message}`;\n }\n\n if (!dbError.table) {\n dbError.table = table;\n }\n\n return dbError;\n }\n}\n\n/**\n * Create a MySQL database adapter.\n *\n * @param config - MySQL adapter configuration\n * @returns A new MySqlAdapter instance\n *\n * @example\n * ```typescript\n * // Simple usage with URL\n * const adapter = createMySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * });\n *\n * // Full configuration\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * pool: {\n * min: 2,\n * max: 20,\n * idleTimeoutMs: 30000,\n * connectionTimeoutMs: 10000,\n * },\n * ssl: {\n * rejectUnauthorized: true,\n * },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport function createMySqlAdapter(config: MySqlAdapterConfig): MySqlAdapter {\n return new MySqlAdapter(config);\n}\n\n/**\n * Type guard to check if a value is a MySqlAdapter.\n *\n * @param value - Value to check\n * @returns True if value is a MySqlAdapter instance\n *\n * @example\n * ```typescript\n * if (isMySqlAdapter(adapter)) {\n * // TypeScript knows adapter is MySqlAdapter\n * console.log('Using MySQL');\n * }\n * ```\n */\nexport function isMySqlAdapter(value: unknown): value is MySqlAdapter {\n return value instanceof MySqlAdapter;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DrizzleAdapter } from '@nextlyhq/adapter-drizzle';
|
|
2
2
|
import { MySqlAdapterConfig, PoolStats, SqlParam, TransactionContext, TransactionOptions, DatabaseCapabilities, DatabaseError } from '@nextlyhq/adapter-drizzle/types';
|
|
3
3
|
export { AdapterLogger, BaseAdapterConfig, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, MySqlAdapterConfig, OrderBySpec, PoolConfig, PoolStats, SelectOptions, SqlParam, SslConfig, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from '@nextlyhq/adapter-drizzle/types';
|
|
4
|
+
import { AnyRelations } from 'drizzle-orm';
|
|
4
5
|
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -68,6 +69,8 @@ declare const VERSION = "0.1.0";
|
|
|
68
69
|
* ```
|
|
69
70
|
*/
|
|
70
71
|
declare class MySqlAdapter extends DrizzleAdapter {
|
|
72
|
+
private drizzleByRelations;
|
|
73
|
+
private drizzleBare;
|
|
71
74
|
/**
|
|
72
75
|
* The database dialect - always 'mysql' for this adapter.
|
|
73
76
|
*/
|
|
@@ -199,7 +202,7 @@ declare class MySqlAdapter extends DrizzleAdapter {
|
|
|
199
202
|
* @returns Drizzle ORM instance wrapping the mysql2 pool connection
|
|
200
203
|
* @throws {Error} If called in browser or not connected
|
|
201
204
|
*/
|
|
202
|
-
getDrizzle<T = MySql2Database<
|
|
205
|
+
getDrizzle<T = MySql2Database<AnyRelations>>(relations?: AnyRelations): T;
|
|
203
206
|
/**
|
|
204
207
|
* Builds mysql2 Pool configuration from adapter config.
|
|
205
208
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DrizzleAdapter } from '@nextlyhq/adapter-drizzle';
|
|
2
2
|
import { MySqlAdapterConfig, PoolStats, SqlParam, TransactionContext, TransactionOptions, DatabaseCapabilities, DatabaseError } from '@nextlyhq/adapter-drizzle/types';
|
|
3
3
|
export { AdapterLogger, BaseAdapterConfig, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, MySqlAdapterConfig, OrderBySpec, PoolConfig, PoolStats, SelectOptions, SqlParam, SslConfig, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from '@nextlyhq/adapter-drizzle/types';
|
|
4
|
+
import { AnyRelations } from 'drizzle-orm';
|
|
4
5
|
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -68,6 +69,8 @@ declare const VERSION = "0.1.0";
|
|
|
68
69
|
* ```
|
|
69
70
|
*/
|
|
70
71
|
declare class MySqlAdapter extends DrizzleAdapter {
|
|
72
|
+
private drizzleByRelations;
|
|
73
|
+
private drizzleBare;
|
|
71
74
|
/**
|
|
72
75
|
* The database dialect - always 'mysql' for this adapter.
|
|
73
76
|
*/
|
|
@@ -199,7 +202,7 @@ declare class MySqlAdapter extends DrizzleAdapter {
|
|
|
199
202
|
* @returns Drizzle ORM instance wrapping the mysql2 pool connection
|
|
200
203
|
* @throws {Error} If called in browser or not connected
|
|
201
204
|
*/
|
|
202
|
-
getDrizzle<T = MySql2Database<
|
|
205
|
+
getDrizzle<T = MySql2Database<AnyRelations>>(relations?: AnyRelations): T;
|
|
203
206
|
/**
|
|
204
207
|
* Builds mysql2 Pool configuration from adapter config.
|
|
205
208
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DrizzleAdapter } from '@nextlyhq/adapter-drizzle';
|
|
2
|
-
import { createDatabaseError, isDatabaseError } from '@nextlyhq/adapter-drizzle/types';
|
|
2
|
+
import { isApplicationError, createDatabaseError, isDatabaseError } from '@nextlyhq/adapter-drizzle/types';
|
|
3
3
|
import { checkDialectVersion } from '@nextlyhq/adapter-drizzle/version-check';
|
|
4
4
|
import { drizzle } from 'drizzle-orm/mysql2';
|
|
5
5
|
import mysql from 'mysql2/promise';
|
|
@@ -79,6 +79,14 @@ function delay(ms) {
|
|
|
79
79
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
80
80
|
}
|
|
81
81
|
var MySqlAdapter = class extends DrizzleAdapter {
|
|
82
|
+
// getDrizzle memoization: drizzle v1's constructor builds a relational
|
|
83
|
+
// query builder per table in the relations config (~40 tables), and the
|
|
84
|
+
// service layer resolves an instance on every db access — construct once
|
|
85
|
+
// per relations object (identity-stable: the schema registry caches it
|
|
86
|
+
// and hands out a NEW object on invalidation, which naturally misses
|
|
87
|
+
// this cache and produces a fresh instance).
|
|
88
|
+
drizzleByRelations = /* @__PURE__ */ new WeakMap();
|
|
89
|
+
drizzleBare;
|
|
82
90
|
/**
|
|
83
91
|
* The database dialect - always 'mysql' for this adapter.
|
|
84
92
|
*/
|
|
@@ -158,16 +166,19 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
158
166
|
* It waits for all connections to be released before shutting down.
|
|
159
167
|
*/
|
|
160
168
|
async disconnect() {
|
|
161
|
-
|
|
169
|
+
const pool = this.pool;
|
|
170
|
+
this.pool = null;
|
|
171
|
+
this.drizzleBare = void 0;
|
|
172
|
+
this.drizzleByRelations = /* @__PURE__ */ new WeakMap();
|
|
173
|
+
if (!pool) {
|
|
162
174
|
return;
|
|
163
175
|
}
|
|
164
176
|
try {
|
|
165
|
-
await
|
|
177
|
+
await pool.end();
|
|
166
178
|
if (this.config.logger?.info) {
|
|
167
179
|
this.config.logger.info("MySQL connection closed");
|
|
168
180
|
}
|
|
169
181
|
} finally {
|
|
170
|
-
this.pool = null;
|
|
171
182
|
this.connected = false;
|
|
172
183
|
}
|
|
173
184
|
}
|
|
@@ -276,6 +287,7 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
276
287
|
await connection.query("ROLLBACK").catch(() => {
|
|
277
288
|
});
|
|
278
289
|
lastError = error;
|
|
290
|
+
if (isApplicationError(error)) throw error;
|
|
279
291
|
const mysqlError = error;
|
|
280
292
|
const isRetryable = mysqlError.errno === 1213;
|
|
281
293
|
if (isRetryable && attempt < maxAttempts) {
|
|
@@ -385,13 +397,22 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
385
397
|
* @returns Drizzle ORM instance wrapping the mysql2 pool connection
|
|
386
398
|
* @throws {Error} If called in browser or not connected
|
|
387
399
|
*/
|
|
388
|
-
|
|
389
|
-
getDrizzle(schema) {
|
|
400
|
+
getDrizzle(relations) {
|
|
390
401
|
if (typeof window !== "undefined") {
|
|
391
402
|
throw new Error("getDrizzle() is server-only");
|
|
392
403
|
}
|
|
393
404
|
const pool = this.ensurePool();
|
|
394
|
-
|
|
405
|
+
const client = pool.pool;
|
|
406
|
+
if (!relations) {
|
|
407
|
+
this.drizzleBare ??= drizzle({ client });
|
|
408
|
+
return this.drizzleBare;
|
|
409
|
+
}
|
|
410
|
+
let cached = this.drizzleByRelations.get(relations);
|
|
411
|
+
if (!cached) {
|
|
412
|
+
cached = drizzle({ client, relations });
|
|
413
|
+
this.drizzleByRelations.set(relations, cached);
|
|
414
|
+
}
|
|
415
|
+
return cached;
|
|
395
416
|
}
|
|
396
417
|
/**
|
|
397
418
|
* Builds mysql2 Pool configuration from adapter config.
|
|
@@ -469,6 +490,11 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
469
490
|
* as savepoints are disabled in this adapter per approved approach.
|
|
470
491
|
*/
|
|
471
492
|
createTransactionContext(connection) {
|
|
493
|
+
const buildTxExecutor = () => drizzle({
|
|
494
|
+
client: connection.connection
|
|
495
|
+
});
|
|
496
|
+
let txExecutor;
|
|
497
|
+
const txDb = () => txExecutor ??= buildTxExecutor();
|
|
472
498
|
return {
|
|
473
499
|
execute: async (sql, params = []) => {
|
|
474
500
|
const [rows] = await connection.query(
|
|
@@ -477,34 +503,76 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
477
503
|
);
|
|
478
504
|
return rows;
|
|
479
505
|
},
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
506
|
+
// Run on the transaction-bound Drizzle instance rather than the pool, so
|
|
507
|
+
// the statement is part of this transaction and sees its uncommitted rows.
|
|
508
|
+
runStatement: async (statement) => {
|
|
509
|
+
await txDb().execute(statement);
|
|
510
|
+
},
|
|
511
|
+
// mysql2 answers a `[rows, fields]` tuple; the transaction-bound instance
|
|
512
|
+
// keeps the read inside this transaction so it sees its uncommitted writes.
|
|
513
|
+
queryStatement: async (statement) => {
|
|
514
|
+
const result = await txDb().execute(statement);
|
|
515
|
+
if (!Array.isArray(result)) {
|
|
516
|
+
throw this.createDatabaseError(
|
|
517
|
+
"query",
|
|
518
|
+
"Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.",
|
|
519
|
+
void 0
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
return result[0];
|
|
523
|
+
},
|
|
524
|
+
lockRow: async (table, id) => {
|
|
525
|
+
const idColumn = this.escapeIdentifier("id");
|
|
526
|
+
await connection.query(
|
|
527
|
+
`SELECT ${idColumn} FROM ${this.escapeIdentifier(table)} WHERE ${idColumn} = ? FOR UPDATE`,
|
|
528
|
+
[id]
|
|
529
|
+
);
|
|
530
|
+
},
|
|
531
|
+
insert: async (table, data, options) => {
|
|
532
|
+
const mapped = this.mapRowToRawSql(this.getTableObject(table), data);
|
|
533
|
+
const columns = Object.keys(mapped);
|
|
534
|
+
const values = Object.values(mapped);
|
|
483
535
|
const placeholders = this.buildPlaceholders(values.length, 0);
|
|
484
536
|
const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map((c) => this.escapeIdentifier(c)).join(", ")}) VALUES (${placeholders})`;
|
|
485
537
|
const [result] = await connection.query(sql, values);
|
|
486
|
-
|
|
538
|
+
const ret = options?.returning;
|
|
539
|
+
if (Array.isArray(ret) && ret.length === 0) {
|
|
540
|
+
return void 0;
|
|
541
|
+
}
|
|
542
|
+
const insertTableObj = this.getTableObject(table);
|
|
543
|
+
const aliases = this.dateWallClockAliases(insertTableObj, ret ?? "*");
|
|
544
|
+
const spelled = aliases.map(
|
|
545
|
+
(a) => `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`
|
|
546
|
+
).join(", ");
|
|
547
|
+
const projected = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(insertTableObj, ret).map((c) => this.escapeIdentifier(c)).join(", ");
|
|
548
|
+
const selectList = spelled ? `${projected}, ${spelled}` : projected;
|
|
549
|
+
const idValue = result.insertId ? result.insertId : mapped.id;
|
|
550
|
+
if (idValue !== void 0) {
|
|
487
551
|
const [rows2] = await connection.query(
|
|
488
|
-
`SELECT
|
|
489
|
-
[
|
|
552
|
+
`SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE id = ?`,
|
|
553
|
+
[idValue]
|
|
490
554
|
);
|
|
491
|
-
return rows2[0];
|
|
555
|
+
return this.mapRowFromRawSql(insertTableObj, rows2[0], aliases);
|
|
492
556
|
}
|
|
493
557
|
const whereClauses = columns.map(
|
|
494
558
|
(c) => `${this.escapeIdentifier(c)} = ?`
|
|
495
559
|
);
|
|
496
560
|
const [rows] = await connection.query(
|
|
497
|
-
`SELECT
|
|
561
|
+
`SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(" AND ")} LIMIT 1`,
|
|
498
562
|
values
|
|
499
563
|
);
|
|
500
|
-
return rows[0];
|
|
564
|
+
return this.mapRowFromRawSql(insertTableObj, rows[0], aliases);
|
|
501
565
|
},
|
|
502
|
-
insertMany: async (table, data,
|
|
566
|
+
insertMany: async (table, data, options) => {
|
|
503
567
|
if (data.length === 0) return [];
|
|
504
|
-
const
|
|
568
|
+
const retMany = options?.returning;
|
|
569
|
+
const skipReread = Array.isArray(retMany) && retMany.length === 0;
|
|
570
|
+
const tableObj = this.getTableObject(table);
|
|
571
|
+
const mappedRecords = data.map((r) => this.mapRowToRawSql(tableObj, r));
|
|
572
|
+
const columns = Object.keys(mappedRecords[0]);
|
|
505
573
|
const allValues = [];
|
|
506
574
|
const valuesClauses = [];
|
|
507
|
-
for (const record of
|
|
575
|
+
for (const record of mappedRecords) {
|
|
508
576
|
const placeholders = [];
|
|
509
577
|
for (const col of columns) {
|
|
510
578
|
allValues.push(record[col]);
|
|
@@ -517,41 +585,53 @@ var MySqlAdapter = class extends DrizzleAdapter {
|
|
|
517
585
|
sql,
|
|
518
586
|
allValues
|
|
519
587
|
);
|
|
520
|
-
if (result.insertId && result.affectedRows > 0) {
|
|
588
|
+
if (!skipReread && result.insertId && result.affectedRows > 0) {
|
|
521
589
|
const ids = [];
|
|
522
590
|
for (let i = 0; i < result.affectedRows; i++) {
|
|
523
591
|
ids.push(result.insertId + i);
|
|
524
592
|
}
|
|
525
593
|
const placeholders = ids.map(() => "?").join(", ");
|
|
594
|
+
const bulkAliases = this.dateWallClockAliases(tableObj, "*");
|
|
595
|
+
const bulkSpelled = bulkAliases.map(
|
|
596
|
+
(a) => `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`
|
|
597
|
+
).join(", ");
|
|
526
598
|
const [rows] = await connection.query(
|
|
527
|
-
`SELECT
|
|
599
|
+
`SELECT *${bulkSpelled ? `, ${bulkSpelled}` : ""} FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,
|
|
528
600
|
ids
|
|
529
601
|
);
|
|
530
|
-
return rows
|
|
602
|
+
return rows.map(
|
|
603
|
+
(r) => this.mapRowFromRawSql(tableObj, r, bulkAliases)
|
|
604
|
+
);
|
|
531
605
|
}
|
|
532
606
|
return [];
|
|
533
607
|
},
|
|
534
|
-
// TransactionContext CRUD methods delegate to the adapter's CRUD
|
|
535
|
-
//
|
|
608
|
+
// TransactionContext CRUD methods delegate to the adapter's Drizzle CRUD
|
|
609
|
+
// but pass the transaction-bound executor so they run inside this
|
|
610
|
+
// transaction rather than on the pool.
|
|
536
611
|
select: async (table, options) => {
|
|
537
|
-
return this.select(table, options);
|
|
612
|
+
return this.select(table, options, txDb());
|
|
538
613
|
},
|
|
539
614
|
selectOne: async (table, options) => {
|
|
540
|
-
return this.selectOne(table, options);
|
|
615
|
+
return this.selectOne(table, options, txDb());
|
|
541
616
|
},
|
|
542
617
|
update: async (table, data, where, options) => {
|
|
543
|
-
return this.update(table, data, where, options);
|
|
618
|
+
return this.update(table, data, where, options, txDb());
|
|
544
619
|
},
|
|
545
|
-
delete: async (table, where,
|
|
546
|
-
return this.delete(table, where);
|
|
620
|
+
delete: async (table, where, options) => {
|
|
621
|
+
return this.delete(table, where, options, txDb());
|
|
547
622
|
},
|
|
548
623
|
upsert: async (table, data, options) => {
|
|
549
|
-
return this.upsert(table, data, options);
|
|
624
|
+
return this.upsert(table, data, options, txDb());
|
|
550
625
|
},
|
|
551
626
|
// Savepoints disabled per approved approach
|
|
552
627
|
savepoint: void 0,
|
|
553
628
|
rollbackToSavepoint: void 0,
|
|
554
|
-
releaseSavepoint: void 0
|
|
629
|
+
releaseSavepoint: void 0,
|
|
630
|
+
// Expose the transaction-bound Drizzle instance so callers can run
|
|
631
|
+
// Drizzle sql templates inside this transaction (junction-table writes
|
|
632
|
+
// need this to be atomic with the entry write). Reuses the memoized
|
|
633
|
+
// txDb() built for the delegated CRUD methods.
|
|
634
|
+
getDrizzle: () => txDb()
|
|
555
635
|
};
|
|
556
636
|
}
|
|
557
637
|
/**
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["rows"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @nextlyhq/adapter-mysql\n *\n * MySQL database adapter for Nextly.\n * Extends DrizzleAdapter from @nextlyhq/adapter-drizzle to provide MySQL-specific functionality.\n *\n * @remarks\n * This adapter uses the mysql2 package for database connectivity and provides:\n * - Connection pooling via mysql2 Pool\n * - Full transaction support with isolation levels\n * - CRUD operations with workarounds for missing RETURNING clause\n * - MySQL-specific error classification\n * - Automatic retry for deadlocks (error 1213)\n *\n * @example\n * ```typescript\n * import { createMySqlAdapter } from '@nextlyhq/adapter-mysql';\n *\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * });\n *\n * await adapter.connect();\n *\n * // Query data\n * const users = await adapter.select('users', {\n * where: { and: [{ column: 'status', op: '=', value: 'active' }] },\n * limit: 10,\n * });\n *\n * await adapter.disconnect();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { DrizzleAdapter } from \"@nextlyhq/adapter-drizzle\";\n// F17: connect-time DB version check shared across all adapters.\nimport {\n createDatabaseError,\n isDatabaseError,\n type MySqlAdapterConfig,\n type DatabaseCapabilities,\n type PoolStats,\n type TransactionContext,\n type TransactionOptions,\n type SqlParam,\n type WhereClause,\n type WhereCondition,\n type WhereOperator,\n type SelectOptions,\n type InsertOptions,\n type UpdateOptions,\n type DeleteOptions,\n type UpsertOptions,\n type OrderBySpec,\n type JoinSpec,\n type DatabaseError,\n type DatabaseErrorKind,\n type BaseAdapterConfig,\n type AdapterLogger,\n type PoolConfig,\n type SslConfig,\n} from \"@nextlyhq/adapter-drizzle/types\";\nimport { checkDialectVersion } from \"@nextlyhq/adapter-drizzle/version-check\";\nimport { drizzle, type MySql2Database } from \"drizzle-orm/mysql2\";\nimport mysql from \"mysql2/promise\";\nimport type {\n PoolOptions,\n RowDataPacket,\n ResultSetHeader,\n} from \"mysql2/promise\";\n\n// mysql2 type definitions use mixin patterns that TypeScript struggles with.\n// We define explicit interfaces for the methods we need.\n\n/**\n * Query result type - either rows or a result header\n */\ntype QueryResult = RowDataPacket[] | RowDataPacket[][] | ResultSetHeader;\n\n/**\n * Queryable interface for mysql2 connections\n */\ninterface Queryable {\n query<T extends QueryResult>(sql: string): Promise<[T, unknown]>;\n query<T extends QueryResult>(\n sql: string,\n values: unknown[]\n ): Promise<[T, unknown]>;\n}\n\n/**\n * mysql2 Pool interface with query method\n */\ninterface Pool extends Queryable {\n getConnection(): Promise<PoolConnection>;\n end(): Promise<void>;\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n}\n\n/**\n * mysql2 PoolConnection interface with query and release methods\n */\ninterface PoolConnection extends Queryable {\n release(): void;\n}\n\n// Re-export types for convenience\nexport type {\n MySqlAdapterConfig,\n DatabaseCapabilities,\n PoolStats,\n TransactionContext,\n TransactionOptions,\n SqlParam,\n WhereClause,\n WhereCondition,\n WhereOperator,\n SelectOptions,\n InsertOptions,\n UpdateOptions,\n DeleteOptions,\n UpsertOptions,\n OrderBySpec,\n JoinSpec,\n DatabaseError,\n DatabaseErrorKind,\n BaseAdapterConfig,\n AdapterLogger,\n PoolConfig,\n SslConfig,\n};\n\n/**\n * Package version\n */\nexport const VERSION = \"0.1.0\";\n\n/**\n * Default pool configuration values.\n */\nconst DEFAULT_POOL_CONFIG = {\n min: 2,\n max: 10,\n idleTimeoutMs: 30000,\n connectionTimeoutMs: 10000,\n};\n\n/**\n * MySQL error codes mapping to DatabaseErrorKind.\n *\n * @see https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html\n */\nconst MYSQL_ERROR_CODES: Record<number, DatabaseErrorKind> = {\n // Unique/Duplicate key violations\n 1022: \"unique_violation\", // ER_DUP_KEY\n 1062: \"unique_violation\", // ER_DUP_ENTRY\n 1169: \"unique_violation\", // ER_DUP_UNIQUE\n 1586: \"unique_violation\", // ER_DUP_ENTRY_WITH_KEY_NAME\n\n // Foreign key violations\n 1216: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW\n 1217: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED\n 1451: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED_2\n 1452: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW_2\n\n // Not null violations\n 1048: \"not_null_violation\", // ER_BAD_NULL_ERROR\n 1364: \"not_null_violation\", // ER_NO_DEFAULT_FOR_FIELD\n\n // Check constraint violations (MySQL 8.0.16+)\n 3819: \"check_violation\", // ER_CHECK_CONSTRAINT_VIOLATED\n\n // Deadlock\n 1213: \"deadlock\", // ER_LOCK_DEADLOCK\n\n // Timeout\n 1205: \"timeout\", // ER_LOCK_WAIT_TIMEOUT\n\n // Connection errors\n 1040: \"connection\", // ER_CON_COUNT_ERROR - Too many connections\n 1042: \"connection\", // ER_BAD_HOST_ERROR\n 1043: \"connection\", // ER_HANDSHAKE_ERROR\n 1044: \"connection\", // ER_DBACCESS_DENIED_ERROR\n 1045: \"connection\", // ER_ACCESS_DENIED_ERROR\n 1129: \"connection\", // ER_HOST_IS_BLOCKED\n 1130: \"connection\", // ER_HOST_NOT_PRIVILEGED\n 2002: \"connection\", // CR_CONNECTION_ERROR\n 2003: \"connection\", // CR_CONN_HOST_ERROR\n 2006: \"connection\", // CR_SERVER_GONE_ERROR\n 2013: \"connection\", // CR_SERVER_LOST\n\n // Query errors\n 1064: \"query\", // ER_PARSE_ERROR\n 1146: \"query\", // ER_NO_SUCH_TABLE\n 1054: \"query\", // ER_BAD_FIELD_ERROR\n};\n\n/**\n * Delay helper for retry logic.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * MySQL database adapter for Nextly.\n *\n * Extends the base DrizzleAdapter to provide MySQL-specific functionality\n * using the mysql2 package.\n *\n * @remarks\n * MySQL has some limitations compared to PostgreSQL:\n * - No native RETURNING clause (requires INSERT then SELECT)\n * - No native ILIKE (uses LOWER() LIKE workaround)\n * - No native JSONB (uses JSON type)\n * - No array types\n * - Savepoints disabled for safety (MySQL has nested transaction quirks)\n *\n * @example\n * ```typescript\n * const adapter = new MySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * pool: { max: 20 },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport class MySqlAdapter extends DrizzleAdapter {\n /**\n * The database dialect - always 'mysql' for this adapter.\n */\n readonly dialect = \"mysql\" as const;\n\n /**\n * Adapter configuration.\n */\n protected readonly config: MySqlAdapterConfig;\n\n /**\n * Connection pool instance.\n */\n private pool: Pool | null = null;\n\n /**\n * Connection state flag.\n */\n private connected = false;\n\n /**\n * Creates a new MySQL adapter instance.\n *\n * @param config - Adapter configuration\n */\n constructor(config: MySqlAdapterConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the MySQL database.\n * Creates a connection pool using mysql2.\n *\n * @remarks\n * This method initializes the connection pool and verifies connectivity\n * by executing a simple query. It is idempotent - calling it multiple\n * times will not create multiple pools.\n *\n * @throws {DatabaseError} If connection fails\n */\n async connect(): Promise<void> {\n if (this.connected && this.pool) {\n return;\n }\n\n try {\n const poolConfig = this.buildPoolConfig();\n // Cast to our Pool interface - mysql2's mixin types don't resolve properly\n this.pool = mysql.createPool(poolConfig) as unknown as Pool;\n\n // Verify connection with smoke test, then check dialect version.\n // Why: F17 hard-fails at connect on real MySQL <8.0 (no variant\n // token detected). Recognized variants (MariaDB, TiDB, Aurora,\n // PlanetScale, Vitess) log a warning via the adapter logger and\n // proceed. Truly unparseable strings hard-fail so users see the\n // issue at boot rather than mid-apply.\n const connection = await this.pool.getConnection();\n try {\n await connection.query(\"SELECT 1\");\n await checkDialectVersion(connection, \"mysql\", {\n // Why: route variant warnings through the adapter's logger so\n // users see a single, consistent log surface.\n onWarning: msg => this.config.logger?.warn?.(msg),\n });\n this.connected = true;\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection established\", {\n host: this.config.host ?? \"from URL\",\n database: this.config.database ?? \"from URL\",\n });\n }\n } finally {\n connection.release();\n }\n } catch (error) {\n // Clean up on failure\n if (this.pool) {\n await this.pool.end().catch(() => {});\n this.pool = null;\n }\n throw this.classifyError(error);\n }\n }\n\n /**\n * Disconnect from the MySQL database.\n * Gracefully closes the connection pool.\n *\n * @remarks\n * This method is idempotent - calling it multiple times is safe.\n * It waits for all connections to be released before shutting down.\n */\n async disconnect(): Promise<void> {\n if (!this.pool) {\n return;\n }\n\n try {\n await this.pool.end();\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection closed\");\n }\n } finally {\n this.pool = null;\n this.connected = false;\n }\n }\n\n /**\n * Check if connected to the database.\n */\n isConnected(): boolean {\n return this.connected && this.pool !== null;\n }\n\n /**\n * Get connection pool statistics.\n * Returns null if not connected.\n *\n * @remarks\n * MySQL2 pool exposes different stats than pg:\n * - _allConnections: all connections\n * - _freeConnections: idle connections\n * - _connectionQueue: waiting requests\n */\n getPoolStats(): PoolStats | null {\n if (!this.pool) {\n return null;\n }\n\n // mysql2 Pool internal properties (cast to access internals)\n const poolInternal = this.pool as unknown as {\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n };\n\n const internal = poolInternal.pool;\n if (!internal) {\n return {\n total: 0,\n idle: 0,\n waiting: 0,\n active: 0,\n };\n }\n\n const total = internal._allConnections?.length ?? 0;\n const idle = internal._freeConnections?.length ?? 0;\n const waiting = internal._connectionQueue?.length ?? 0;\n\n return {\n total,\n idle,\n waiting,\n active: total - idle,\n };\n }\n\n /**\n * Execute a raw SQL query.\n *\n * @param sql - SQL query string with ? placeholders\n * @param params - Query parameters\n * @returns Query results\n *\n * @throws {DatabaseError} If query execution fails\n */\n async executeQuery<T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> {\n const pool = this.ensurePool();\n const startTime = Date.now();\n\n try {\n const [rows] = await pool.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n\n // Log query if logger configured\n if (this.config.logger?.query) {\n const durationMs = Date.now() - startTime;\n this.config.logger.query(sql, params, durationMs);\n }\n\n return rows as T[];\n } catch (error) {\n throw this.classifyError(error, sql);\n }\n }\n\n /**\n * Execute work within a transaction.\n *\n * @param work - Function containing transactional operations\n * @param options - Transaction options (isolation level, timeout, retry)\n * @returns Result of the work function\n *\n * @remarks\n * MySQL transactions support isolation levels. Automatic retry is\n * implemented for deadlocks (error 1213) when `retryCount` is specified.\n *\n * Note: Savepoints are disabled in this adapter for safety due to\n * MySQL's quirks with nested transactions.\n */\n async transaction<T>(\n work: (tx: TransactionContext) => Promise<T>,\n options?: TransactionOptions\n ): Promise<T> {\n const pool = this.ensurePool();\n const maxAttempts = (options?.retryCount ?? 0) + 1;\n const retryDelayMs = options?.retryDelayMs ?? 100;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const connection = await pool.getConnection();\n const startTime = Date.now();\n\n try {\n // Begin transaction with options\n await this.beginTransaction(connection, options);\n\n // Create transaction context\n const ctx = this.createTransactionContext(connection);\n\n // Execute callback\n const result = await work(ctx);\n\n // Commit transaction\n await connection.query(\"COMMIT\");\n\n // Log success\n if (this.config.logger?.debug) {\n const durationMs = Date.now() - startTime;\n this.config.logger.debug(\"Transaction committed\", {\n attempt,\n durationMs,\n });\n }\n\n return result;\n } catch (error) {\n // Rollback transaction\n await connection.query(\"ROLLBACK\").catch(() => {});\n\n lastError = error;\n\n // Check if error is retryable (deadlock only per approved approach)\n const mysqlError = error as { errno?: number; code?: string };\n const isRetryable = mysqlError.errno === 1213; // ER_LOCK_DEADLOCK\n\n if (isRetryable && attempt < maxAttempts) {\n if (this.config.logger?.warn) {\n this.config.logger.warn(\n `Transaction failed with deadlock, retrying (${attempt}/${maxAttempts})`,\n { errno: mysqlError.errno, attempt }\n );\n }\n await delay(retryDelayMs * attempt); // Exponential backoff\n continue;\n }\n\n throw this.classifyError(error);\n } finally {\n connection.release();\n }\n }\n\n // Should not reach here, but handle just in case\n throw this.classifyError(lastError);\n }\n\n /**\n * Get MySQL database capabilities.\n *\n * @remarks\n * MySQL has some limitations:\n * - No JSONB (uses JSON)\n * - No arrays\n * - No native ILIKE\n * - No RETURNING clause\n * - Savepoints disabled for safety\n */\n getCapabilities(): DatabaseCapabilities {\n return {\n dialect: \"mysql\",\n supportsJsonb: false, // MySQL uses JSON, not JSONB\n supportsJson: true,\n supportsArrays: false, // MySQL doesn't support array types\n supportsGeneratedColumns: true, // MySQL 5.7.6+\n supportsFts: true, // MySQL has FULLTEXT indexes\n supportsIlike: false, // No native ILIKE, use LOWER() LIKE\n supportsReturning: false, // No RETURNING clause in MySQL\n supportsSavepoints: false, // Disabled for safety per approved approach\n supportsOnConflict: true, // ON DUPLICATE KEY UPDATE\n maxParamsPerQuery: 65535, // MySQL limit\n maxIdentifierLength: 64, // MySQL limit\n };\n }\n\n /**\n * Build a placeholder for MySQL (uses ? instead of $1, $2, etc.)\n *\n * @param _index - Parameter index (ignored for MySQL)\n * @returns The ? placeholder\n */\n protected buildPlaceholder(_index: number): string {\n return \"?\";\n }\n\n /**\n * Build multiple placeholders for MySQL.\n *\n * @param count - Number of placeholders needed\n * @param _startIndex - Starting index (ignored for MySQL)\n * @returns Comma-separated ? placeholders\n */\n protected buildPlaceholders(count: number, _startIndex: number = 0): string {\n return Array(count).fill(\"?\").join(\", \");\n }\n\n /**\n * Escape an identifier for MySQL (uses backticks instead of double quotes).\n *\n * @param identifier - The identifier to escape\n * @returns Escaped identifier with backticks\n */\n protected escapeIdentifier(identifier: string): string {\n // MySQL uses backticks for identifiers\n return `\\`${identifier.replace(/`/g, \"``\")}\\``;\n }\n\n // ============================================================\n // Protected Helper Methods\n // ============================================================\n\n /**\n * Ensures pool is connected and returns it.\n *\n * @throws {DatabaseError} If not connected\n */\n private ensurePool(): Pool {\n if (!this.pool) {\n throw createDatabaseError({\n kind: \"connection\",\n message: \"MySqlAdapter is not connected. Call connect() first.\",\n });\n }\n return this.pool;\n }\n\n /**\n * Return the typed Drizzle instance for MySQL.\n * Guarded for server-only usage and requires an active connection.\n *\n * @param schema - Optional schema for relational queries (db.query.*)\n * @returns Drizzle ORM instance wrapping the mysql2 pool connection\n * @throws {Error} If called in browser or not connected\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n getDrizzle<T = MySql2Database<any>>(schema?: Record<string, unknown>): T {\n if (typeof window !== \"undefined\") {\n throw new Error(\"getDrizzle() is server-only\");\n }\n const pool = this.ensurePool();\n // Cast needed because mysql2/promise Pool type differs from drizzle's expected type\n // MySQL requires mode when schema is provided\n\n /* eslint-disable @typescript-eslint/no-explicit-any */\n return (\n schema\n ? drizzle({ client: pool as any, schema, mode: \"default\" })\n : drizzle(pool as any)\n ) as T;\n /* eslint-enable @typescript-eslint/no-explicit-any */\n }\n\n /**\n * Builds mysql2 Pool configuration from adapter config.\n */\n private buildPoolConfig(): PoolOptions {\n const config: PoolOptions = {};\n\n // Connection string or explicit options\n if (this.config.url) {\n config.uri = this.config.url;\n } else {\n if (this.config.host) config.host = this.config.host;\n if (this.config.port) config.port = this.config.port;\n if (this.config.database) config.database = this.config.database;\n if (this.config.user) config.user = this.config.user;\n if (this.config.password) config.password = this.config.password;\n }\n\n // Pool settings - mysql2 uses different property names\n config.connectionLimit = this.config.pool?.max ?? DEFAULT_POOL_CONFIG.max;\n config.idleTimeout =\n this.config.pool?.idleTimeoutMs ?? DEFAULT_POOL_CONFIG.idleTimeoutMs;\n config.connectTimeout =\n this.config.pool?.connectionTimeoutMs ??\n DEFAULT_POOL_CONFIG.connectionTimeoutMs;\n\n // Enable waiting for connections when pool is full\n config.waitForConnections = true;\n config.queueLimit = 0; // Unlimited queue\n\n // SSL configuration\n if (this.config.ssl) {\n if (typeof this.config.ssl === \"boolean\") {\n config.ssl = this.config.ssl ? {} : undefined;\n } else {\n config.ssl = {\n rejectUnauthorized: this.config.ssl.rejectUnauthorized,\n ca: this.config.ssl.ca,\n cert: this.config.ssl.cert,\n key: this.config.ssl.key,\n };\n }\n }\n\n // MySQL-specific options\n if (this.config.timezone) {\n config.timezone = this.config.timezone;\n }\n\n if (this.config.charset) {\n config.charset = this.config.charset;\n }\n\n // Enable multiple statements if needed (disabled by default for security)\n config.multipleStatements = false;\n\n // Date handling\n config.dateStrings = false; // Return Date objects\n\n return config;\n }\n\n /**\n * Begins a transaction with the specified options.\n */\n private async beginTransaction(\n connection: PoolConnection,\n options?: TransactionOptions\n ): Promise<void> {\n // Set isolation level if specified (must be done before BEGIN)\n if (options?.isolationLevel) {\n const isolationMap: Record<string, string> = {\n \"read uncommitted\": \"READ UNCOMMITTED\",\n \"read committed\": \"READ COMMITTED\",\n \"repeatable read\": \"REPEATABLE READ\",\n serializable: \"SERIALIZABLE\",\n };\n const level = isolationMap[options.isolationLevel];\n if (level) {\n await connection.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);\n }\n }\n\n // Set read-only mode if specified\n if (options?.readOnly) {\n await connection.query(\"SET TRANSACTION READ ONLY\");\n }\n\n // Begin the transaction\n await connection.query(\"START TRANSACTION\");\n\n // Set lock wait timeout if specified\n if (options?.timeoutMs) {\n // MySQL uses seconds for lock_wait_timeout\n const timeoutSeconds = Math.ceil(options.timeoutMs / 1000);\n await connection.query(\n `SET SESSION innodb_lock_wait_timeout = ${timeoutSeconds}`\n );\n }\n }\n\n /**\n * Creates a TransactionContext for the given connection.\n *\n * @remarks\n * Note: Savepoint methods are not implemented (set to undefined)\n * as savepoints are disabled in this adapter per approved approach.\n */\n private createTransactionContext(\n connection: PoolConnection\n ): TransactionContext {\n return {\n execute: async <T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> => {\n const [rows] = await connection.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n return rows as T[];\n },\n\n insert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n _options?: InsertOptions\n ): Promise<T> => {\n const columns = Object.keys(data);\n const values = Object.values(data);\n const placeholders = this.buildPlaceholders(values.length, 0);\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES (${placeholders})`;\n\n const [result] = await connection.query<ResultSetHeader>(sql, values);\n\n // MySQL doesn't have RETURNING, so we need to SELECT the inserted row\n // Use insertId if available (auto-increment), otherwise use all inserted values\n if (result.insertId) {\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE id = ?`,\n [result.insertId]\n );\n return rows[0] as T;\n }\n\n // Fallback: SELECT by all inserted values\n const whereClauses = columns.map(\n c => `${this.escapeIdentifier(c)} = ?`\n );\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(\" AND \")} LIMIT 1`,\n values\n );\n return rows[0] as T;\n },\n\n insertMany: async <T = unknown>(\n table: string,\n data: Record<string, unknown>[],\n _options?: InsertOptions\n ): Promise<T[]> => {\n if (data.length === 0) return [];\n\n const columns = Object.keys(data[0]);\n const allValues: unknown[] = [];\n const valuesClauses: string[] = [];\n\n for (const record of data) {\n const placeholders: string[] = [];\n for (const col of columns) {\n allValues.push(record[col]);\n placeholders.push(\"?\");\n }\n valuesClauses.push(`(${placeholders.join(\", \")})`);\n }\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES ${valuesClauses.join(\", \")}`;\n\n const [result] = await connection.query<ResultSetHeader>(\n sql,\n allValues\n );\n\n // For bulk insert, we need to SELECT the inserted rows\n // MySQL's insertId gives the first auto-increment ID\n if (result.insertId && result.affectedRows > 0) {\n const ids: number[] = [];\n for (let i = 0; i < result.affectedRows; i++) {\n ids.push(result.insertId + i);\n }\n const placeholders = ids.map(() => \"?\").join(\", \");\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT * FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,\n ids\n );\n return rows as T[];\n }\n\n // Fallback: return empty if we can't determine inserted rows\n return [];\n },\n\n // TransactionContext CRUD methods delegate to the adapter's CRUD\n // which uses Drizzle query API via the TableResolver.\n select: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T[]> => {\n return this.select<T>(table, options);\n },\n\n selectOne: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T | null> => {\n return this.selectOne<T>(table, options);\n },\n\n update: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n where: WhereClause,\n options?: UpdateOptions\n ): Promise<T[]> => {\n return this.update<T>(table, data, where, options);\n },\n\n delete: async (\n table: string,\n where: WhereClause,\n _options?: DeleteOptions\n ): Promise<number> => {\n return this.delete(table, where);\n },\n\n upsert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options: UpsertOptions\n ): Promise<T> => {\n return this.upsert<T>(table, data, options);\n },\n\n // Savepoints disabled per approved approach\n savepoint: undefined,\n rollbackToSavepoint: undefined,\n releaseSavepoint: undefined,\n };\n }\n\n /**\n * Classifies a MySQL error into a DatabaseError.\n *\n * @param error - Original error from mysql2\n * @param sql - SQL statement that caused the error (optional)\n * @returns DatabaseError with proper classification\n */\n private classifyError(error: unknown, sql?: string): DatabaseError {\n // Why short-circuit on existing DatabaseError: F17's\n // UnsupportedDialectVersionError is already a typed DatabaseError with\n // kind: \"unsupported_version\" plus detectedVersion/requiredVersion\n // fields. Re-wrapping it here would erase those fields and re-tag it\n // as kind: \"unknown\".\n if (isDatabaseError(error)) return error;\n\n const mysqlError = error as {\n errno?: number;\n code?: string;\n sqlState?: string;\n message?: string;\n sql?: string;\n };\n\n // Determine error kind from MySQL error number\n const kind: DatabaseErrorKind =\n (mysqlError.errno && MYSQL_ERROR_CODES[mysqlError.errno]) || \"unknown\";\n\n // Build error message\n let message = mysqlError.message ?? String(error);\n if (sql && kind === \"query\") {\n message = `Query failed: ${message}`;\n }\n\n return createDatabaseError({\n kind,\n message,\n code: mysqlError.code ?? mysqlError.errno?.toString(),\n detail: mysqlError.sql,\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Override handleQueryError to use MySQL-specific classification.\n */\n protected override handleQueryError(\n error: unknown,\n operation: string,\n table: string\n ): DatabaseError {\n const dbError = this.classifyError(error);\n\n // Add operation context if not already present\n if (!dbError.message.includes(operation)) {\n dbError.message = `${operation} operation failed on table '${table}': ${dbError.message}`;\n }\n\n if (!dbError.table) {\n dbError.table = table;\n }\n\n return dbError;\n }\n}\n\n/**\n * Create a MySQL database adapter.\n *\n * @param config - MySQL adapter configuration\n * @returns A new MySqlAdapter instance\n *\n * @example\n * ```typescript\n * // Simple usage with URL\n * const adapter = createMySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * });\n *\n * // Full configuration\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * pool: {\n * min: 2,\n * max: 20,\n * idleTimeoutMs: 30000,\n * connectionTimeoutMs: 10000,\n * },\n * ssl: {\n * rejectUnauthorized: true,\n * },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport function createMySqlAdapter(config: MySqlAdapterConfig): MySqlAdapter {\n return new MySqlAdapter(config);\n}\n\n/**\n * Type guard to check if a value is a MySqlAdapter.\n *\n * @param value - Value to check\n * @returns True if value is a MySqlAdapter instance\n *\n * @example\n * ```typescript\n * if (isMySqlAdapter(adapter)) {\n * // TypeScript knows adapter is MySqlAdapter\n * console.log('Using MySQL');\n * }\n * ```\n */\nexport function isMySqlAdapter(value: unknown): value is MySqlAdapter {\n return value instanceof MySqlAdapter;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["rows"],"mappings":";;;;;;;AAmJO,IAAM,OAAA,GAAU;AAKvB,IAAM,mBAAA,GAAsB;AAAA,EAE1B,GAAA,EAAK,EAAA;AAAA,EACL,aAAA,EAAe,GAAA;AAAA,EACf,mBAAA,EAAqB;AACvB,CAAA;AAOA,IAAM,iBAAA,GAAuD;AAAA;AAAA,EAE3D,IAAA,EAAM,kBAAA;AAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,uBAAA;AAAA;AAAA,EACN,IAAA,EAAM,uBAAA;AAAA;AAAA,EACN,IAAA,EAAM,uBAAA;AAAA;AAAA,EACN,IAAA,EAAM,uBAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,oBAAA;AAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,iBAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,UAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,SAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA;AAAA;AAAA,EAGN,IAAA,EAAM,OAAA;AAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA;AAAA,EACN,IAAA,EAAM;AAAA;AACR,CAAA;AAKA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACvD;AA0BO,IAAM,YAAA,GAAN,cAA2B,cAAA,CAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOvC,kBAAA,uBAAyB,OAAA,EAA+B;AAAA,EACxD,WAAA;AAAA;AAAA;AAAA;AAAA,EAKC,OAAA,GAAU,OAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA;AAAA;AAAA;AAAA;AAAA,EAKX,IAAA,GAAoB,IAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,SAAA,GAAY,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,YAAY,MAAA,EAA4B;AACtC,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,OAAA,GAAyB;AAC7B,IAAA,IAAI,IAAA,CAAK,SAAA,IAAa,IAAA,CAAK,IAAA,EAAM;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,UAAA,GAAa,KAAK,eAAA,EAAgB;AAExC,MAAA,IAAA,CAAK,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,UAAU,CAAA;AAQvC,MAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,IAAA,CAAK,aAAA,EAAc;AACjD,MAAA,IAAI;AACF,QAAA,MAAM,UAAA,CAAW,MAAM,UAAU,CAAA;AACjC,QAAA,MAAM,mBAAA,CAAoB,YAAY,OAAA,EAAS;AAAA;AAAA;AAAA,UAG7C,WAAW,CAAA,GAAA,KAAO,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,OAAO,GAAG;AAAA,SACjD,CAAA;AACD,QAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAEjB,QAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM;AAC5B,UAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,8BAAA,EAAgC;AAAA,YACtD,IAAA,EAAM,IAAA,CAAK,MAAA,CAAO,IAAA,IAAQ,UAAA;AAAA,YAC1B,QAAA,EAAU,IAAA,CAAK,MAAA,CAAO,QAAA,IAAY;AAAA,WACnC,CAAA;AAAA,QACH;AAAA,MACF,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,OAAA,EAAQ;AAAA,MACrB;AAAA,IACF,SAAS,KAAA,EAAO;AAEd,MAAA,IAAI,KAAK,IAAA,EAAM;AACb,QAAA,MAAM,IAAA,CAAK,IAAA,CAAK,GAAA,EAAI,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AACpC,QAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,MACd;AACA,MAAA,MAAM,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,UAAA,GAA4B;AAKhC,IAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,WAAA,GAAc,MAAA;AACnB,IAAA,IAAA,CAAK,kBAAA,uBAAyB,OAAA,EAAQ;AACtC,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,KAAK,GAAA,EAAI;AAEf,MAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM;AAC5B,QAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,yBAAyB,CAAA;AAAA,MACnD;AAAA,IACF,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,SAAA,IAAa,IAAA,CAAK,IAAA,KAAS,IAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAA,GAAiC;AAC/B,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AACd,MAAA,OAAO,IAAA;AAAA,IACT;AAGA,IAAA,MAAM,eAAe,IAAA,CAAK,IAAA;AAQ1B,IAAA,MAAM,WAAW,YAAA,CAAa,IAAA;AAC9B,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,CAAA;AAAA,QACP,IAAA,EAAM,CAAA;AAAA,QACN,OAAA,EAAS,CAAA;AAAA,QACT,MAAA,EAAQ;AAAA,OACV;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,eAAA,EAAiB,MAAA,IAAU,CAAA;AAClD,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,gBAAA,EAAkB,MAAA,IAAU,CAAA;AAClD,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,gBAAA,EAAkB,MAAA,IAAU,CAAA;AAErD,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAQ,KAAA,GAAQ;AAAA,KAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,YAAA,CACJ,GAAA,EACA,MAAA,GAAqB,EAAC,EACR;AACd,IAAA,MAAM,IAAA,GAAO,KAAK,UAAA,EAAW;AAC7B,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,IAAA,IAAI;AACF,MAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,IAAA,CAAK,KAAA;AAAA,QACxB,GAAA;AAAA,QACA;AAAA,OACF;AAGA,MAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO;AAC7B,QAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAChC,QAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK,QAAQ,UAAU,CAAA;AAAA,MAClD;AAEA,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAA,CAAK,aAAA,CAAc,KAAA,EAAO,GAAG,CAAA;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,WAAA,CACJ,IAAA,EACA,OAAA,EACY;AACZ,IAAA,MAAM,IAAA,GAAO,KAAK,UAAA,EAAW;AAC7B,IAAA,MAAM,WAAA,GAAA,CAAe,OAAA,EAAS,UAAA,IAAc,CAAA,IAAK,CAAA;AACjD,IAAA,MAAM,YAAA,GAAe,SAAS,YAAA,IAAgB,GAAA;AAE9C,IAAA,IAAI,SAAA;AAEJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,WAAA,EAAa,OAAA,EAAA,EAAW;AACvD,MAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,aAAA,EAAc;AAC5C,MAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,MAAA,IAAI;AAEF,QAAA,MAAM,IAAA,CAAK,gBAAA,CAAiB,UAAA,EAAY,OAAO,CAAA;AAG/C,QAAA,MAAM,GAAA,GAAM,IAAA,CAAK,wBAAA,CAAyB,UAAU,CAAA;AAGpD,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,GAAG,CAAA;AAG7B,QAAA,MAAM,UAAA,CAAW,MAAM,QAAQ,CAAA;AAG/B,QAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO;AAC7B,UAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAChC,UAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,uBAAA,EAAyB;AAAA,YAChD,OAAA;AAAA,YACA;AAAA,WACD,CAAA;AAAA,QACH;AAEA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,KAAA,EAAO;AAEd,QAAA,MAAM,UAAA,CAAW,KAAA,CAAM,UAAU,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAEjD,QAAA,SAAA,GAAY,KAAA;AAQZ,QAAA,IAAI,kBAAA,CAAmB,KAAK,CAAA,EAAG,MAAM,KAAA;AAGrC,QAAA,MAAM,UAAA,GAAa,KAAA;AACnB,QAAA,MAAM,WAAA,GAAc,WAAW,KAAA,KAAU,IAAA;AAEzC,QAAA,IAAI,WAAA,IAAe,UAAU,WAAA,EAAa;AACxC,UAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM;AAC5B,YAAA,IAAA,CAAK,OAAO,MAAA,CAAO,IAAA;AAAA,cACjB,CAAA,4CAAA,EAA+C,OAAO,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA,CAAA;AAAA,cACrE,EAAE,KAAA,EAAO,UAAA,CAAW,KAAA,EAAO,OAAA;AAAQ,aACrC;AAAA,UACF;AACA,UAAA,MAAM,KAAA,CAAM,eAAe,OAAO,CAAA;AAClC,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,MAChC,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,OAAA,EAAQ;AAAA,MACrB;AAAA,IACF;AAGA,IAAA,MAAM,IAAA,CAAK,cAAc,SAAS,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,eAAA,GAAwC;AACtC,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,OAAA;AAAA,MACT,aAAA,EAAe,KAAA;AAAA;AAAA,MACf,YAAA,EAAc,IAAA;AAAA,MACd,cAAA,EAAgB,KAAA;AAAA;AAAA,MAChB,wBAAA,EAA0B,IAAA;AAAA;AAAA,MAC1B,WAAA,EAAa,IAAA;AAAA;AAAA,MACb,aAAA,EAAe,KAAA;AAAA;AAAA,MACf,iBAAA,EAAmB,KAAA;AAAA;AAAA,MACnB,kBAAA,EAAoB,KAAA;AAAA;AAAA,MACpB,kBAAA,EAAoB,IAAA;AAAA;AAAA,MACpB,iBAAA,EAAmB,KAAA;AAAA;AAAA,MACnB,mBAAA,EAAqB;AAAA;AAAA,KACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,iBAAiB,MAAA,EAAwB;AACjD,IAAA,OAAO,GAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,iBAAA,CAAkB,KAAA,EAAe,WAAA,GAAsB,CAAA,EAAW;AAC1E,IAAA,OAAO,MAAM,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,iBAAiB,UAAA,EAA4B;AAErD,IAAA,OAAO,CAAA,EAAA,EAAK,UAAA,CAAW,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAC,CAAA,EAAA,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,UAAA,GAAmB;AACzB,IAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AACd,MAAA,MAAM,mBAAA,CAAoB;AAAA,QACxB,IAAA,EAAM,YAAA;AAAA,QACN,OAAA,EAAS;AAAA,OACV,CAAA;AAAA,IACH;AACA,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,WAA6C,SAAA,EAA6B;AACxE,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,MAAM,IAAI,MAAM,6BAA6B,CAAA;AAAA,IAC/C;AACA,IAAA,MAAM,IAAA,GAAO,KAAK,UAAA,EAAW;AAK7B,IAAA,MAAM,SAAU,IAAA,CAA2C,IAAA;AAC3D,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,IAAA,CAAK,WAAA,KAAgB,OAAA,CAAQ,EAAE,MAAA,EAAQ,CAAA;AACvC,MAAA,OAAO,IAAA,CAAK,WAAA;AAAA,IACd;AACA,IAAA,IAAI,MAAA,GAAS,IAAA,CAAK,kBAAA,CAAmB,GAAA,CAAI,SAAS,CAAA;AAClD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAA,GAAS,OAAA,CAAQ,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAA;AACtC,MAAA,IAAA,CAAK,kBAAA,CAAmB,GAAA,CAAI,SAAA,EAAW,MAAM,CAAA;AAAA,IAC/C;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAA,GAA+B;AACrC,IAAA,MAAM,SAAsB,EAAC;AAG7B,IAAA,IAAI,IAAA,CAAK,OAAO,GAAA,EAAK;AACnB,MAAA,MAAA,CAAO,GAAA,GAAM,KAAK,MAAA,CAAO,GAAA;AAAA,IAC3B,CAAA,MAAO;AACL,MAAA,IAAI,KAAK,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,IAAA,GAAO,KAAK,MAAA,CAAO,IAAA;AAChD,MAAA,IAAI,KAAK,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,IAAA,GAAO,KAAK,MAAA,CAAO,IAAA;AAChD,MAAA,IAAI,KAAK,MAAA,CAAO,QAAA,EAAU,MAAA,CAAO,QAAA,GAAW,KAAK,MAAA,CAAO,QAAA;AACxD,MAAA,IAAI,KAAK,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,IAAA,GAAO,KAAK,MAAA,CAAO,IAAA;AAChD,MAAA,IAAI,KAAK,MAAA,CAAO,QAAA,EAAU,MAAA,CAAO,QAAA,GAAW,KAAK,MAAA,CAAO,QAAA;AAAA,IAC1D;AAGA,IAAA,MAAA,CAAO,eAAA,GAAkB,IAAA,CAAK,MAAA,CAAO,IAAA,EAAM,OAAO,mBAAA,CAAoB,GAAA;AACtE,IAAA,MAAA,CAAO,WAAA,GACL,IAAA,CAAK,MAAA,CAAO,IAAA,EAAM,iBAAiB,mBAAA,CAAoB,aAAA;AACzD,IAAA,MAAA,CAAO,cAAA,GACL,IAAA,CAAK,MAAA,CAAO,IAAA,EAAM,uBAClB,mBAAA,CAAoB,mBAAA;AAGtB,IAAA,MAAA,CAAO,kBAAA,GAAqB,IAAA;AAC5B,IAAA,MAAA,CAAO,UAAA,GAAa,CAAA;AAGpB,IAAA,IAAI,IAAA,CAAK,OAAO,GAAA,EAAK;AACnB,MAAA,IAAI,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,KAAQ,SAAA,EAAW;AACxC,QAAA,MAAA,CAAO,GAAA,GAAM,IAAA,CAAK,MAAA,CAAO,GAAA,GAAM,EAAC,GAAI,MAAA;AAAA,MACtC,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,GAAA,GAAM;AAAA,UACX,kBAAA,EAAoB,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,kBAAA;AAAA,UACpC,EAAA,EAAI,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,EAAA;AAAA,UACpB,IAAA,EAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA;AAAA,UACtB,GAAA,EAAK,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI;AAAA,SACvB;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,IAAA,CAAK,OAAO,QAAA,EAAU;AACxB,MAAA,MAAA,CAAO,QAAA,GAAW,KAAK,MAAA,CAAO,QAAA;AAAA,IAChC;AAEA,IAAA,IAAI,IAAA,CAAK,OAAO,OAAA,EAAS;AACvB,MAAA,MAAA,CAAO,OAAA,GAAU,KAAK,MAAA,CAAO,OAAA;AAAA,IAC/B;AAGA,IAAA,MAAA,CAAO,kBAAA,GAAqB,KAAA;AAG5B,IAAA,MAAA,CAAO,WAAA,GAAc,KAAA;AAErB,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAA,CACZ,UAAA,EACA,OAAA,EACe;AAEf,IAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,MAAA,MAAM,YAAA,GAAuC;AAAA,QAC3C,kBAAA,EAAoB,kBAAA;AAAA,QACpB,gBAAA,EAAkB,gBAAA;AAAA,QAClB,iBAAA,EAAmB,iBAAA;AAAA,QACnB,YAAA,EAAc;AAAA,OAChB;AACA,MAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,CAAQ,cAAc,CAAA;AACjD,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,MAAM,UAAA,CAAW,KAAA,CAAM,CAAA,gCAAA,EAAmC,KAAK,CAAA,CAAE,CAAA;AAAA,MACnE;AAAA,IACF;AAGA,IAAA,IAAI,SAAS,QAAA,EAAU;AACrB,MAAA,MAAM,UAAA,CAAW,MAAM,2BAA2B,CAAA;AAAA,IACpD;AAGA,IAAA,MAAM,UAAA,CAAW,MAAM,mBAAmB,CAAA;AAG1C,IAAA,IAAI,SAAS,SAAA,EAAW;AAEtB,MAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,YAAY,GAAI,CAAA;AACzD,MAAA,MAAM,UAAA,CAAW,KAAA;AAAA,QACf,0CAA0C,cAAc,CAAA;AAAA,OAC1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,yBACN,UAAA,EACoB;AASpB,IAAA,MAAM,eAAA,GAAkB,MACtB,OAAA,CAAQ;AAAA,MACN,QAAS,UAAA,CACN;AAAA,KACJ,CAAA;AACH,IAAA,IAAI,UAAA;AACJ,IAAA,MAAM,IAAA,GAAO,MAAO,UAAA,KAAe,eAAA,EAAgB;AACnD,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,OACP,GAAA,EACA,MAAA,GAAqB,EAAC,KACL;AACjB,QAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA;AAAA,UAC9B,GAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,OAAO,IAAA;AAAA,MACT,CAAA;AAAA;AAAA;AAAA,MAIA,YAAA,EAAc,OAAO,SAAA,KAAkC;AACrD,QAAA,MAAM,IAAA,EAAK,CAAE,OAAA,CAAQ,SAAS,CAAA;AAAA,MAChC,CAAA;AAAA;AAAA;AAAA,MAIA,cAAA,EAAgB,OACd,SAAA,KACiB;AACjB,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,EAAK,CAAE,QAAQ,SAAS,CAAA;AAK7C,QAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAA,CAAK,mBAAA;AAAA,YACT,OAAA;AAAA,YACA,sHAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AACA,QAAA,OAAO,OAAO,CAAC,CAAA;AAAA,MACjB,CAAA;AAAA,MAEA,OAAA,EAAS,OAAO,KAAA,EAAe,EAAA,KAAgC;AAC7D,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,gBAAA,CAAiB,IAAI,CAAA;AAC3C,QAAA,MAAM,UAAA,CAAW,KAAA;AAAA,UACf,CAAA,OAAA,EAAU,QAAQ,CAAA,MAAA,EAAS,IAAA,CAAK,iBAAiB,KAAK,CAAC,UAC5C,QAAQ,CAAA,eAAA,CAAA;AAAA,UACnB,CAAC,EAAE;AAAA,SACL;AAAA,MACF,CAAA;AAAA,MAEA,MAAA,EAAQ,OACN,KAAA,EACA,IAAA,EACA,OAAA,KACe;AACf,QAAA,MAAM,SAAS,IAAA,CAAK,cAAA,CAAe,KAAK,cAAA,CAAe,KAAK,GAAG,IAAI,CAAA;AACnE,QAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAClC,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA;AACnC,QAAA,MAAM,YAAA,GAAe,IAAA,CAAK,iBAAA,CAAkB,MAAA,CAAO,QAAQ,CAAC,CAAA;AAE5D,QAAA,MAAM,MAAM,CAAA,YAAA,EAAe,IAAA,CAAK,iBAAiB,KAAK,CAAC,KAAK,OAAA,CAAQ,GAAA,CAAI,OAAK,IAAA,CAAK,gBAAA,CAAiB,CAAC,CAAC,CAAA,CAAE,KAAK,IAAI,CAAC,aAAa,YAAY,CAAA,CAAA,CAAA;AAE1I,QAAA,MAAM,CAAC,MAAM,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA,CAAuB,KAAK,MAAM,CAAA;AAEpE,QAAA,MAAM,MAAM,OAAA,EAAS,SAAA;AAErB,QAAA,IAAI,MAAM,OAAA,CAAQ,GAAG,CAAA,IAAK,GAAA,CAAI,WAAW,CAAA,EAAG;AAC1C,UAAA,OAAO,MAAA;AAAA,QACT;AAIA,QAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,cAAA,CAAe,KAAK,CAAA;AAKhD,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,oBAAA,CAAqB,cAAA,EAAgB,OAAO,GAAG,CAAA;AACpE,QAAA,MAAM,UAAU,OAAA,CACb,GAAA;AAAA,UACC,CAAA,CAAA,KACE,CAAA,YAAA,EAAe,IAAA,CAAK,gBAAA,CAAiB,CAAA,CAAE,OAAO,CAAC,CAAA,6BAAA,EAAgC,IAAA,CAAK,gBAAA,CAAiB,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,SACjH,CACC,KAAK,IAAI,CAAA;AACZ,QAAA,MAAM,YACJ,CAAC,GAAA,IAAO,QAAQ,GAAA,GACZ,GAAA,GACA,KAAK,mBAAA,CAAoB,cAAA,EAAgB,GAAG,CAAA,CACzC,GAAA,CAAI,OAAK,IAAA,CAAK,gBAAA,CAAiB,CAAC,CAAC,CAAA,CACjC,KAAK,IAAI,CAAA;AAClB,QAAA,MAAM,aAAa,OAAA,GAAU,CAAA,EAAG,SAAS,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA,GAAK,SAAA;AAM1D,QAAA,MAAM,OAAA,GAAU,MAAA,CAAO,QAAA,GAAW,MAAA,CAAO,WAAW,MAAA,CAAO,EAAA;AAC3D,QAAA,IAAI,YAAY,MAAA,EAAW;AACzB,UAAA,MAAM,CAACA,KAAI,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA;AAAA,YAC9B,UAAU,UAAU,CAAA,MAAA,EAAS,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAC,CAAA,aAAA,CAAA;AAAA,YACzD,CAAC,OAAO;AAAA,WACV;AACA,UAAA,OAAO,KAAK,gBAAA,CAAiB,cAAA,EAAgBA,KAAAA,CAAK,CAAC,GAAQ,OAAO,CAAA;AAAA,QACpE;AAEA,QAAA,MAAM,eAAe,OAAA,CAAQ,GAAA;AAAA,UAC3B,CAAA,CAAA,KAAK,CAAA,EAAG,IAAA,CAAK,gBAAA,CAAiB,CAAC,CAAC,CAAA,IAAA;AAAA,SAClC;AACA,QAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA;AAAA,UAC9B,CAAA,OAAA,EAAU,UAAU,CAAA,MAAA,EAAS,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAC,CAAA,OAAA,EAAU,YAAA,CAAa,IAAA,CAAK,OAAO,CAAC,CAAA,QAAA,CAAA;AAAA,UAC7F;AAAA,SACF;AACA,QAAA,OAAO,KAAK,gBAAA,CAAiB,cAAA,EAAgB,IAAA,CAAK,CAAC,GAAQ,OAAO,CAAA;AAAA,MACpE,CAAA;AAAA,MAEA,UAAA,EAAY,OACV,KAAA,EACA,IAAA,EACA,OAAA,KACiB;AACjB,QAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC/B,QAAA,MAAM,UAAU,OAAA,EAAS,SAAA;AACzB,QAAA,MAAM,aAAa,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,QAAQ,MAAA,KAAW,CAAA;AAEhE,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,cAAA,CAAe,KAAK,CAAA;AAC1C,QAAA,MAAM,aAAA,GAAgB,KAAK,GAAA,CAAI,CAAA,CAAA,KAAK,KAAK,cAAA,CAAe,QAAA,EAAU,CAAC,CAAC,CAAA;AACpE,QAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,aAAA,CAAc,CAAC,CAAC,CAAA;AAC5C,QAAA,MAAM,YAAuB,EAAC;AAC9B,QAAA,MAAM,gBAA0B,EAAC;AAEjC,QAAA,KAAA,MAAW,UAAU,aAAA,EAAe;AAClC,UAAA,MAAM,eAAyB,EAAC;AAChC,UAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,YAAA,SAAA,CAAU,IAAA,CAAK,MAAA,CAAO,GAAG,CAAC,CAAA;AAC1B,YAAA,YAAA,CAAa,KAAK,GAAG,CAAA;AAAA,UACvB;AACA,UAAA,aAAA,CAAc,KAAK,CAAA,CAAA,EAAI,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,QACnD;AAEA,QAAA,MAAM,GAAA,GAAM,eAAe,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAC,CAAA,EAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,IAAA,CAAK,iBAAiB,CAAC,CAAC,EAAE,IAAA,CAAK,IAAI,CAAC,CAAA,SAAA,EAAY,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAErJ,QAAA,MAAM,CAAC,MAAM,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA;AAAA,UAChC,GAAA;AAAA,UACA;AAAA,SACF;AAIA,QAAA,IAAI,CAAC,UAAA,IAAc,MAAA,CAAO,QAAA,IAAY,MAAA,CAAO,eAAe,CAAA,EAAG;AAC7D,UAAA,MAAM,MAAgB,EAAC;AACvB,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,cAAc,CAAA,EAAA,EAAK;AAC5C,YAAA,GAAA,CAAI,IAAA,CAAK,MAAA,CAAO,QAAA,GAAW,CAAC,CAAA;AAAA,UAC9B;AACA,UAAA,MAAM,eAAe,GAAA,CAAI,GAAA,CAAI,MAAM,GAAG,CAAA,CAAE,KAAK,IAAI,CAAA;AACjD,UAAA,MAAM,WAAA,GAAc,IAAA,CAAK,oBAAA,CAAqB,QAAA,EAAU,GAAG,CAAA;AAC3D,UAAA,MAAM,cAAc,WAAA,CACjB,GAAA;AAAA,YACC,CAAA,CAAA,KACE,CAAA,YAAA,EAAe,IAAA,CAAK,gBAAA,CAAiB,CAAA,CAAE,OAAO,CAAC,CAAA,6BAAA,EAAgC,IAAA,CAAK,gBAAA,CAAiB,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA,WACjH,CACC,KAAK,IAAI,CAAA;AACZ,UAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,UAAA,CAAW,KAAA;AAAA,YAC9B,CAAA,QAAA,EAAW,WAAA,GAAc,CAAA,EAAA,EAAK,WAAW,CAAA,CAAA,GAAK,EAAE,CAAA,MAAA,EAAS,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAC,CAAA,cAAA,EAAiB,YAAY,CAAA,CAAA,CAAA;AAAA,YAClH;AAAA,WACF;AACA,UAAA,OAAQ,IAAA,CAAa,GAAA;AAAA,YAAI,CAAA,CAAA,KACvB,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU,GAAG,WAAW;AAAA,WAChD;AAAA,QACF;AAGA,QAAA,OAAO,EAAC;AAAA,MACV,CAAA;AAAA;AAAA;AAAA;AAAA,MAKA,MAAA,EAAQ,OACN,KAAA,EACA,OAAA,KACiB;AACjB,QAAA,OAAO,IAAA,CAAK,MAAA,CAAU,KAAA,EAAO,OAAA,EAAS,MAAM,CAAA;AAAA,MAC9C,CAAA;AAAA,MAEA,SAAA,EAAW,OACT,KAAA,EACA,OAAA,KACsB;AACtB,QAAA,OAAO,IAAA,CAAK,SAAA,CAAa,KAAA,EAAO,OAAA,EAAS,MAAM,CAAA;AAAA,MACjD,CAAA;AAAA,MAEA,MAAA,EAAQ,OACN,KAAA,EACA,IAAA,EACA,OACA,OAAA,KACiB;AACjB,QAAA,OAAO,KAAK,MAAA,CAAU,KAAA,EAAO,MAAM,KAAA,EAAO,OAAA,EAAS,MAAM,CAAA;AAAA,MAC3D,CAAA;AAAA,MAEA,MAAA,EAAQ,OACN,KAAA,EACA,KAAA,EACA,OAAA,KACoB;AACpB,QAAA,OAAO,KAAK,MAAA,CAAO,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,MAAM,CAAA;AAAA,MAClD,CAAA;AAAA,MAEA,MAAA,EAAQ,OACN,KAAA,EACA,IAAA,EACA,OAAA,KACe;AACf,QAAA,OAAO,KAAK,MAAA,CAAU,KAAA,EAAO,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAAA,MACpD,CAAA;AAAA;AAAA,MAGA,SAAA,EAAW,MAAA;AAAA,MACX,mBAAA,EAAqB,MAAA;AAAA,MACrB,gBAAA,EAAkB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMlB,UAAA,EAAY,MAAsB,IAAA;AAAK,KACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,aAAA,CAAc,OAAgB,GAAA,EAA6B;AAMjE,IAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG,OAAO,KAAA;AAEnC,IAAA,MAAM,UAAA,GAAa,KAAA;AASnB,IAAA,MAAM,OACH,UAAA,CAAW,KAAA,IAAS,iBAAA,CAAkB,UAAA,CAAW,KAAK,CAAA,IAAM,SAAA;AAG/D,IAAA,IAAI,OAAA,GAAU,UAAA,CAAW,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AAChD,IAAA,IAAI,GAAA,IAAO,SAAS,OAAA,EAAS;AAC3B,MAAA,OAAA,GAAU,iBAAiB,OAAO,CAAA,CAAA;AAAA,IACpC;AAEA,IAAA,OAAO,mBAAA,CAAoB;AAAA,MACzB,IAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA,EAAM,UAAA,CAAW,IAAA,IAAQ,UAAA,CAAW,OAAO,QAAA,EAAS;AAAA,MACpD,QAAQ,UAAA,CAAW,GAAA;AAAA,MACnB,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,GAAQ;AAAA,KACzC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKmB,gBAAA,CACjB,KAAA,EACA,SAAA,EACA,KAAA,EACe;AACf,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,aAAA,CAAc,KAAK,CAAA;AAGxC,IAAA,IAAI,CAAC,OAAA,CAAQ,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,EAAG;AACxC,MAAA,OAAA,CAAQ,UAAU,CAAA,EAAG,SAAS,+BAA+B,KAAK,CAAA,GAAA,EAAM,QAAQ,OAAO,CAAA,CAAA;AAAA,IACzF;AAEA,IAAA,IAAI,CAAC,QAAQ,KAAA,EAAO;AAClB,MAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA;AAAA,IAClB;AAEA,IAAA,OAAO,OAAA;AAAA,EACT;AACF;AAgCO,SAAS,mBAAmB,MAAA,EAA0C;AAC3E,EAAA,OAAO,IAAI,aAAa,MAAM,CAAA;AAChC;AAgBO,SAAS,eAAe,KAAA,EAAuC;AACpE,EAAA,OAAO,KAAA,YAAiB,YAAA;AAC1B","file":"index.mjs","sourcesContent":["/**\n * @nextlyhq/adapter-mysql\n *\n * MySQL database adapter for Nextly.\n * Extends DrizzleAdapter from @nextlyhq/adapter-drizzle to provide MySQL-specific functionality.\n *\n * @remarks\n * This adapter uses the mysql2 package for database connectivity and provides:\n * - Connection pooling via mysql2 Pool\n * - Full transaction support with isolation levels\n * - CRUD operations with workarounds for missing RETURNING clause\n * - MySQL-specific error classification\n * - Automatic retry for deadlocks (error 1213)\n *\n * @example\n * ```typescript\n * import { createMySqlAdapter } from '@nextlyhq/adapter-mysql';\n *\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * });\n *\n * await adapter.connect();\n *\n * // Query data\n * const users = await adapter.select('users', {\n * where: { and: [{ column: 'status', op: '=', value: 'active' }] },\n * limit: 10,\n * });\n *\n * await adapter.disconnect();\n * ```\n *\n * @packageDocumentation\n */\n\nimport { DrizzleAdapter } from \"@nextlyhq/adapter-drizzle\";\n// F17: connect-time DB version check shared across all adapters.\nimport {\n createDatabaseError,\n isDatabaseError,\n type MySqlAdapterConfig,\n type DatabaseCapabilities,\n type PoolStats,\n type TransactionContext,\n type TransactionOptions,\n type SqlParam,\n type WhereClause,\n type WhereCondition,\n type WhereOperator,\n type SelectOptions,\n type InsertOptions,\n type UpdateOptions,\n type DeleteOptions,\n type UpsertOptions,\n type OrderBySpec,\n type JoinSpec,\n type DatabaseError,\n type DatabaseErrorKind,\n type BaseAdapterConfig,\n type AdapterLogger,\n type PoolConfig,\n type SslConfig,\n isApplicationError,\n} from \"@nextlyhq/adapter-drizzle/types\";\nimport { checkDialectVersion } from \"@nextlyhq/adapter-drizzle/version-check\";\nimport type { AnyRelations, SQL } from \"drizzle-orm\";\nimport { drizzle, type MySql2Database } from \"drizzle-orm/mysql2\";\nimport type {\n Pool as CallbackPool,\n Connection as CallbackConnection,\n} from \"mysql2\";\nimport mysql from \"mysql2/promise\";\nimport type {\n PoolOptions,\n RowDataPacket,\n ResultSetHeader,\n} from \"mysql2/promise\";\n\n// mysql2 type definitions use mixin patterns that TypeScript struggles with.\n// We define explicit interfaces for the methods we need.\n\n/**\n * Query result type - either rows or a result header\n */\ntype QueryResult = RowDataPacket[] | RowDataPacket[][] | ResultSetHeader;\n\n/**\n * Queryable interface for mysql2 connections\n */\ninterface Queryable {\n query<T extends QueryResult>(sql: string): Promise<[T, unknown]>;\n query<T extends QueryResult>(\n sql: string,\n values: unknown[]\n ): Promise<[T, unknown]>;\n}\n\n/**\n * mysql2 Pool interface with query method\n */\ninterface Pool extends Queryable {\n getConnection(): Promise<PoolConnection>;\n end(): Promise<void>;\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n}\n\n/**\n * mysql2 PoolConnection interface with query and release methods\n */\ninterface PoolConnection extends Queryable {\n release(): void;\n}\n\n// Re-export types for convenience\nexport type {\n MySqlAdapterConfig,\n DatabaseCapabilities,\n PoolStats,\n TransactionContext,\n TransactionOptions,\n SqlParam,\n WhereClause,\n WhereCondition,\n WhereOperator,\n SelectOptions,\n InsertOptions,\n UpdateOptions,\n DeleteOptions,\n UpsertOptions,\n OrderBySpec,\n JoinSpec,\n DatabaseError,\n DatabaseErrorKind,\n BaseAdapterConfig,\n AdapterLogger,\n PoolConfig,\n SslConfig,\n};\n\n/**\n * Package version\n */\nexport const VERSION = \"0.1.0\";\n\n/**\n * Default pool configuration values.\n */\nconst DEFAULT_POOL_CONFIG = {\n min: 2,\n max: 10,\n idleTimeoutMs: 30000,\n connectionTimeoutMs: 10000,\n};\n\n/**\n * MySQL error codes mapping to DatabaseErrorKind.\n *\n * @see https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html\n */\nconst MYSQL_ERROR_CODES: Record<number, DatabaseErrorKind> = {\n // Unique/Duplicate key violations\n 1022: \"unique_violation\", // ER_DUP_KEY\n 1062: \"unique_violation\", // ER_DUP_ENTRY\n 1169: \"unique_violation\", // ER_DUP_UNIQUE\n 1586: \"unique_violation\", // ER_DUP_ENTRY_WITH_KEY_NAME\n\n // Foreign key violations\n 1216: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW\n 1217: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED\n 1451: \"foreign_key_violation\", // ER_ROW_IS_REFERENCED_2\n 1452: \"foreign_key_violation\", // ER_NO_REFERENCED_ROW_2\n\n // Not null violations\n 1048: \"not_null_violation\", // ER_BAD_NULL_ERROR\n 1364: \"not_null_violation\", // ER_NO_DEFAULT_FOR_FIELD\n\n // Check constraint violations (MySQL 8.0.16+)\n 3819: \"check_violation\", // ER_CHECK_CONSTRAINT_VIOLATED\n\n // Deadlock\n 1213: \"deadlock\", // ER_LOCK_DEADLOCK\n\n // Timeout\n 1205: \"timeout\", // ER_LOCK_WAIT_TIMEOUT\n\n // Connection errors\n 1040: \"connection\", // ER_CON_COUNT_ERROR - Too many connections\n 1042: \"connection\", // ER_BAD_HOST_ERROR\n 1043: \"connection\", // ER_HANDSHAKE_ERROR\n 1044: \"connection\", // ER_DBACCESS_DENIED_ERROR\n 1045: \"connection\", // ER_ACCESS_DENIED_ERROR\n 1129: \"connection\", // ER_HOST_IS_BLOCKED\n 1130: \"connection\", // ER_HOST_NOT_PRIVILEGED\n 2002: \"connection\", // CR_CONNECTION_ERROR\n 2003: \"connection\", // CR_CONN_HOST_ERROR\n 2006: \"connection\", // CR_SERVER_GONE_ERROR\n 2013: \"connection\", // CR_SERVER_LOST\n\n // Query errors\n 1064: \"query\", // ER_PARSE_ERROR\n 1146: \"query\", // ER_NO_SUCH_TABLE\n 1054: \"query\", // ER_BAD_FIELD_ERROR\n};\n\n/**\n * Delay helper for retry logic.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * MySQL database adapter for Nextly.\n *\n * Extends the base DrizzleAdapter to provide MySQL-specific functionality\n * using the mysql2 package.\n *\n * @remarks\n * MySQL has some limitations compared to PostgreSQL:\n * - No native RETURNING clause (requires INSERT then SELECT)\n * - No native ILIKE (uses LOWER() LIKE workaround)\n * - No native JSONB (uses JSON type)\n * - No array types\n * - Savepoints disabled for safety (MySQL has nested transaction quirks)\n *\n * @example\n * ```typescript\n * const adapter = new MySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * pool: { max: 20 },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport class MySqlAdapter extends DrizzleAdapter {\n // getDrizzle memoization: drizzle v1's constructor builds a relational\n // query builder per table in the relations config (~40 tables), and the\n // service layer resolves an instance on every db access — construct once\n // per relations object (identity-stable: the schema registry caches it\n // and hands out a NEW object on invalidation, which naturally misses\n // this cache and produces a fresh instance).\n private drizzleByRelations = new WeakMap<AnyRelations, unknown>();\n private drizzleBare: unknown;\n\n /**\n * The database dialect - always 'mysql' for this adapter.\n */\n readonly dialect = \"mysql\" as const;\n\n /**\n * Adapter configuration.\n */\n protected readonly config: MySqlAdapterConfig;\n\n /**\n * Connection pool instance.\n */\n private pool: Pool | null = null;\n\n /**\n * Connection state flag.\n */\n private connected = false;\n\n /**\n * Creates a new MySQL adapter instance.\n *\n * @param config - Adapter configuration\n */\n constructor(config: MySqlAdapterConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Connect to the MySQL database.\n * Creates a connection pool using mysql2.\n *\n * @remarks\n * This method initializes the connection pool and verifies connectivity\n * by executing a simple query. It is idempotent - calling it multiple\n * times will not create multiple pools.\n *\n * @throws {DatabaseError} If connection fails\n */\n async connect(): Promise<void> {\n if (this.connected && this.pool) {\n return;\n }\n\n try {\n const poolConfig = this.buildPoolConfig();\n // Cast to our Pool interface - mysql2's mixin types don't resolve properly\n this.pool = mysql.createPool(poolConfig) as unknown as Pool;\n\n // Verify connection with smoke test, then check dialect version.\n // Why: F17 hard-fails at connect on real MySQL <8.0 (no variant\n // token detected). Recognized variants (MariaDB, TiDB, Aurora,\n // PlanetScale, Vitess) log a warning via the adapter logger and\n // proceed. Truly unparseable strings hard-fail so users see the\n // issue at boot rather than mid-apply.\n const connection = await this.pool.getConnection();\n try {\n await connection.query(\"SELECT 1\");\n await checkDialectVersion(connection, \"mysql\", {\n // Why: route variant warnings through the adapter's logger so\n // users see a single, consistent log surface.\n onWarning: msg => this.config.logger?.warn?.(msg),\n });\n this.connected = true;\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection established\", {\n host: this.config.host ?? \"from URL\",\n database: this.config.database ?? \"from URL\",\n });\n }\n } finally {\n connection.release();\n }\n } catch (error) {\n // Clean up on failure\n if (this.pool) {\n await this.pool.end().catch(() => {});\n this.pool = null;\n }\n throw this.classifyError(error);\n }\n }\n\n /**\n * Disconnect from the MySQL database.\n * Gracefully closes the connection pool.\n *\n * @remarks\n * This method is idempotent - calling it multiple times is safe.\n * It waits for all connections to be released before shutting down.\n */\n async disconnect(): Promise<void> {\n // Detach the pool FIRST, then drop the memoized drizzle instances —\n // this closes the repopulation window where a concurrent getDrizzle()\n // call during the (async) pool.end() could cache an instance wrapping\n // the closing pool.\n const pool = this.pool;\n this.pool = null;\n this.drizzleBare = undefined;\n this.drizzleByRelations = new WeakMap();\n if (!pool) {\n return;\n }\n\n try {\n await pool.end();\n\n if (this.config.logger?.info) {\n this.config.logger.info(\"MySQL connection closed\");\n }\n } finally {\n this.connected = false;\n }\n }\n\n /**\n * Check if connected to the database.\n */\n isConnected(): boolean {\n return this.connected && this.pool !== null;\n }\n\n /**\n * Get connection pool statistics.\n * Returns null if not connected.\n *\n * @remarks\n * MySQL2 pool exposes different stats than pg:\n * - _allConnections: all connections\n * - _freeConnections: idle connections\n * - _connectionQueue: waiting requests\n */\n getPoolStats(): PoolStats | null {\n if (!this.pool) {\n return null;\n }\n\n // mysql2 Pool internal properties (cast to access internals)\n const poolInternal = this.pool as unknown as {\n pool?: {\n _allConnections?: { length: number };\n _freeConnections?: { length: number };\n _connectionQueue?: { length: number };\n };\n };\n\n const internal = poolInternal.pool;\n if (!internal) {\n return {\n total: 0,\n idle: 0,\n waiting: 0,\n active: 0,\n };\n }\n\n const total = internal._allConnections?.length ?? 0;\n const idle = internal._freeConnections?.length ?? 0;\n const waiting = internal._connectionQueue?.length ?? 0;\n\n return {\n total,\n idle,\n waiting,\n active: total - idle,\n };\n }\n\n /**\n * Execute a raw SQL query.\n *\n * @param sql - SQL query string with ? placeholders\n * @param params - Query parameters\n * @returns Query results\n *\n * @throws {DatabaseError} If query execution fails\n */\n async executeQuery<T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> {\n const pool = this.ensurePool();\n const startTime = Date.now();\n\n try {\n const [rows] = await pool.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n\n // Log query if logger configured\n if (this.config.logger?.query) {\n const durationMs = Date.now() - startTime;\n this.config.logger.query(sql, params, durationMs);\n }\n\n return rows as T[];\n } catch (error) {\n throw this.classifyError(error, sql);\n }\n }\n\n /**\n * Execute work within a transaction.\n *\n * @param work - Function containing transactional operations\n * @param options - Transaction options (isolation level, timeout, retry)\n * @returns Result of the work function\n *\n * @remarks\n * MySQL transactions support isolation levels. Automatic retry is\n * implemented for deadlocks (error 1213) when `retryCount` is specified.\n *\n * Note: Savepoints are disabled in this adapter for safety due to\n * MySQL's quirks with nested transactions.\n */\n async transaction<T>(\n work: (tx: TransactionContext) => Promise<T>,\n options?: TransactionOptions\n ): Promise<T> {\n const pool = this.ensurePool();\n const maxAttempts = (options?.retryCount ?? 0) + 1;\n const retryDelayMs = options?.retryDelayMs ?? 100;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const connection = await pool.getConnection();\n const startTime = Date.now();\n\n try {\n // Begin transaction with options\n await this.beginTransaction(connection, options);\n\n // Create transaction context\n const ctx = this.createTransactionContext(connection);\n\n // Execute callback\n const result = await work(ctx);\n\n // Commit transaction\n await connection.query(\"COMMIT\");\n\n // Log success\n if (this.config.logger?.debug) {\n const durationMs = Date.now() - startTime;\n this.config.logger.debug(\"Transaction committed\", {\n attempt,\n durationMs,\n });\n }\n\n return result;\n } catch (error) {\n // Rollback transaction\n await connection.query(\"ROLLBACK\").catch(() => {});\n\n lastError = error;\n\n // Work inside a transaction may throw to roll the write back — a\n // refused value, a denied permission — and that is the application's\n // verdict, not the driver's failure. Rethrown before the retry check as\n // well as before classification: a refusal re-run is the same refusal,\n // and the caller must receive the code and payload it raised rather\n // than a generic database error with the detail stripped out.\n if (isApplicationError(error)) throw error;\n\n // Check if error is retryable (deadlock only per approved approach)\n const mysqlError = error as { errno?: number; code?: string };\n const isRetryable = mysqlError.errno === 1213; // ER_LOCK_DEADLOCK\n\n if (isRetryable && attempt < maxAttempts) {\n if (this.config.logger?.warn) {\n this.config.logger.warn(\n `Transaction failed with deadlock, retrying (${attempt}/${maxAttempts})`,\n { errno: mysqlError.errno, attempt }\n );\n }\n await delay(retryDelayMs * attempt); // Exponential backoff\n continue;\n }\n\n throw this.classifyError(error);\n } finally {\n connection.release();\n }\n }\n\n // Should not reach here, but handle just in case\n throw this.classifyError(lastError);\n }\n\n /**\n * Get MySQL database capabilities.\n *\n * @remarks\n * MySQL has some limitations:\n * - No JSONB (uses JSON)\n * - No arrays\n * - No native ILIKE\n * - No RETURNING clause\n * - Savepoints disabled for safety\n */\n getCapabilities(): DatabaseCapabilities {\n return {\n dialect: \"mysql\",\n supportsJsonb: false, // MySQL uses JSON, not JSONB\n supportsJson: true,\n supportsArrays: false, // MySQL doesn't support array types\n supportsGeneratedColumns: true, // MySQL 5.7.6+\n supportsFts: true, // MySQL has FULLTEXT indexes\n supportsIlike: false, // No native ILIKE, use LOWER() LIKE\n supportsReturning: false, // No RETURNING clause in MySQL\n supportsSavepoints: false, // Disabled for safety per approved approach\n supportsOnConflict: true, // ON DUPLICATE KEY UPDATE\n maxParamsPerQuery: 65535, // MySQL limit\n maxIdentifierLength: 64, // MySQL limit\n };\n }\n\n /**\n * Build a placeholder for MySQL (uses ? instead of $1, $2, etc.)\n *\n * @param _index - Parameter index (ignored for MySQL)\n * @returns The ? placeholder\n */\n protected buildPlaceholder(_index: number): string {\n return \"?\";\n }\n\n /**\n * Build multiple placeholders for MySQL.\n *\n * @param count - Number of placeholders needed\n * @param _startIndex - Starting index (ignored for MySQL)\n * @returns Comma-separated ? placeholders\n */\n protected buildPlaceholders(count: number, _startIndex: number = 0): string {\n return Array(count).fill(\"?\").join(\", \");\n }\n\n /**\n * Escape an identifier for MySQL (uses backticks instead of double quotes).\n *\n * @param identifier - The identifier to escape\n * @returns Escaped identifier with backticks\n */\n protected escapeIdentifier(identifier: string): string {\n // MySQL uses backticks for identifiers\n return `\\`${identifier.replace(/`/g, \"``\")}\\``;\n }\n\n // ============================================================\n // Protected Helper Methods\n // ============================================================\n\n /**\n * Ensures pool is connected and returns it.\n *\n * @throws {DatabaseError} If not connected\n */\n private ensurePool(): Pool {\n if (!this.pool) {\n throw createDatabaseError({\n kind: \"connection\",\n message: \"MySqlAdapter is not connected. Call connect() first.\",\n });\n }\n return this.pool;\n }\n\n /**\n * Return the typed Drizzle instance for MySQL.\n * Guarded for server-only usage and requires an active connection.\n *\n * @param schema - Optional schema for relational queries (db.query.*)\n * @returns Drizzle ORM instance wrapping the mysql2 pool connection\n * @throws {Error} If called in browser or not connected\n */\n getDrizzle<T = MySql2Database<AnyRelations>>(relations?: AnyRelations): T {\n if (typeof window !== \"undefined\") {\n throw new Error(\"getDrizzle() is server-only\");\n }\n const pool = this.ensurePool();\n // drizzle v1's mysql2 driver accepts the CALLBACK pool — handing it the\n // mysql2/promise wrapper throws (\"Cannot set properties of undefined\n // (setting 'supportBigNumbers')\"), so unwrap to the underlying pool.\n // The pre-v1 `mode` option no longer exists.\n const client = (pool as unknown as { pool: CallbackPool }).pool;\n if (!relations) {\n this.drizzleBare ??= drizzle({ client });\n return this.drizzleBare as T;\n }\n let cached = this.drizzleByRelations.get(relations);\n if (!cached) {\n cached = drizzle({ client, relations });\n this.drizzleByRelations.set(relations, cached);\n }\n return cached as T;\n }\n\n /**\n * Builds mysql2 Pool configuration from adapter config.\n */\n private buildPoolConfig(): PoolOptions {\n const config: PoolOptions = {};\n\n // Connection string or explicit options\n if (this.config.url) {\n config.uri = this.config.url;\n } else {\n if (this.config.host) config.host = this.config.host;\n if (this.config.port) config.port = this.config.port;\n if (this.config.database) config.database = this.config.database;\n if (this.config.user) config.user = this.config.user;\n if (this.config.password) config.password = this.config.password;\n }\n\n // Pool settings - mysql2 uses different property names\n config.connectionLimit = this.config.pool?.max ?? DEFAULT_POOL_CONFIG.max;\n config.idleTimeout =\n this.config.pool?.idleTimeoutMs ?? DEFAULT_POOL_CONFIG.idleTimeoutMs;\n config.connectTimeout =\n this.config.pool?.connectionTimeoutMs ??\n DEFAULT_POOL_CONFIG.connectionTimeoutMs;\n\n // Enable waiting for connections when pool is full\n config.waitForConnections = true;\n config.queueLimit = 0; // Unlimited queue\n\n // SSL configuration\n if (this.config.ssl) {\n if (typeof this.config.ssl === \"boolean\") {\n config.ssl = this.config.ssl ? {} : undefined;\n } else {\n config.ssl = {\n rejectUnauthorized: this.config.ssl.rejectUnauthorized,\n ca: this.config.ssl.ca,\n cert: this.config.ssl.cert,\n key: this.config.ssl.key,\n };\n }\n }\n\n // MySQL-specific options\n if (this.config.timezone) {\n config.timezone = this.config.timezone;\n }\n\n if (this.config.charset) {\n config.charset = this.config.charset;\n }\n\n // Enable multiple statements if needed (disabled by default for security)\n config.multipleStatements = false;\n\n // Date handling\n config.dateStrings = false; // Return Date objects\n\n return config;\n }\n\n /**\n * Begins a transaction with the specified options.\n */\n private async beginTransaction(\n connection: PoolConnection,\n options?: TransactionOptions\n ): Promise<void> {\n // Set isolation level if specified (must be done before BEGIN)\n if (options?.isolationLevel) {\n const isolationMap: Record<string, string> = {\n \"read uncommitted\": \"READ UNCOMMITTED\",\n \"read committed\": \"READ COMMITTED\",\n \"repeatable read\": \"REPEATABLE READ\",\n serializable: \"SERIALIZABLE\",\n };\n const level = isolationMap[options.isolationLevel];\n if (level) {\n await connection.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);\n }\n }\n\n // Set read-only mode if specified\n if (options?.readOnly) {\n await connection.query(\"SET TRANSACTION READ ONLY\");\n }\n\n // Begin the transaction\n await connection.query(\"START TRANSACTION\");\n\n // Set lock wait timeout if specified\n if (options?.timeoutMs) {\n // MySQL uses seconds for lock_wait_timeout\n const timeoutSeconds = Math.ceil(options.timeoutMs / 1000);\n await connection.query(\n `SET SESSION innodb_lock_wait_timeout = ${timeoutSeconds}`\n );\n }\n }\n\n /**\n * Creates a TransactionContext for the given connection.\n *\n * @remarks\n * Note: Savepoint methods are not implemented (set to undefined)\n * as savepoints are disabled in this adapter per approved approach.\n */\n private createTransactionContext(\n connection: PoolConnection\n ): TransactionContext {\n // Bind a Drizzle instance to this transaction's checked-out connection so\n // the delegated CRUD methods run inside the transaction and see its\n // uncommitted rows. drizzle's mysql2 driver needs the underlying CALLBACK\n // connection, which the mysql2/promise wrapper exposes on `.connection`\n // (mirrors the `.pool` unwrap in getDrizzle()); getDrizzle() itself wraps\n // the pool, which would use a different connection. Built lazily and\n // memoized: transactions that use only raw execute/insert never construct\n // it.\n const buildTxExecutor = () =>\n drizzle({\n client: (connection as unknown as { connection: CallbackConnection })\n .connection,\n });\n let txExecutor: ReturnType<typeof buildTxExecutor> | undefined;\n const txDb = () => (txExecutor ??= buildTxExecutor());\n return {\n execute: async <T = unknown>(\n sql: string,\n params: SqlParam[] = []\n ): Promise<T[]> => {\n const [rows] = await connection.query<RowDataPacket[]>(\n sql,\n params as unknown[]\n );\n return rows as T[];\n },\n\n // Run on the transaction-bound Drizzle instance rather than the pool, so\n // the statement is part of this transaction and sees its uncommitted rows.\n runStatement: async (statement: SQL): Promise<void> => {\n await txDb().execute(statement);\n },\n\n // mysql2 answers a `[rows, fields]` tuple; the transaction-bound instance\n // keeps the read inside this transaction so it sees its uncommitted writes.\n queryStatement: async <T = Record<string, unknown>>(\n statement: SQL\n ): Promise<T[]> => {\n const result = await txDb().execute(statement);\n // A tuple's first element is the rows. Anything else was not understood,\n // and must not reach a caller as \"there is nothing there\" — the same\n // reason the pooled `queryStatement` refuses rather than answering\n // empty.\n if (!Array.isArray(result)) {\n throw this.createDatabaseError(\n \"query\",\n \"Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.\",\n undefined\n );\n }\n return result[0] as unknown as T[];\n },\n\n lockRow: async (table: string, id: SqlParam): Promise<void> => {\n const idColumn = this.escapeIdentifier(\"id\");\n await connection.query(\n `SELECT ${idColumn} FROM ${this.escapeIdentifier(table)} ` +\n `WHERE ${idColumn} = ? FOR UPDATE`,\n [id] as unknown[]\n );\n },\n\n insert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options?: InsertOptions\n ): Promise<T> => {\n const mapped = this.mapRowToRawSql(this.getTableObject(table), data);\n const columns = Object.keys(mapped);\n const values = Object.values(mapped);\n const placeholders = this.buildPlaceholders(values.length, 0);\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES (${placeholders})`;\n\n const [result] = await connection.query<ResultSetHeader>(sql, values);\n\n const ret = options?.returning;\n // No columns requested: skip the select-back reread entirely.\n if (Array.isArray(ret) && ret.length === 0) {\n return undefined as T;\n }\n\n // MySQL has no RETURNING; select the inserted row back. Project only the\n // requested columns so a large JSON snapshot is not read unless asked.\n const insertTableObj = this.getTableObject(table);\n // Each timestamp's wall clock, spelled out by the database. mysql2\n // turns one into a `Date` in the LOCAL zone before this code sees it,\n // and that conversion cannot be undone: a wall clock inside a\n // daylight-saving gap is normalized away.\n const aliases = this.dateWallClockAliases(insertTableObj, ret ?? \"*\");\n const spelled = aliases\n .map(\n a =>\n `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`\n )\n .join(\", \");\n const projected =\n !ret || ret === \"*\"\n ? \"*\"\n : this.mapColumnNamesToSql(insertTableObj, ret)\n .map(c => this.escapeIdentifier(c))\n .join(\", \");\n const selectList = spelled ? `${projected}, ${spelled}` : projected;\n\n // Prefer the primary key: auto-increment via insertId, otherwise a\n // supplied id (manually-keyed tables like nextly_versions). Matching by\n // all values is a last resort because `col = NULL` never matches, so a\n // row with nullable columns would not be found by that path.\n const idValue = result.insertId ? result.insertId : mapped.id;\n if (idValue !== undefined) {\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE id = ?`,\n [idValue]\n );\n return this.mapRowFromRawSql(insertTableObj, rows[0] as T, aliases);\n }\n\n const whereClauses = columns.map(\n c => `${this.escapeIdentifier(c)} = ?`\n );\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT ${selectList} FROM ${this.escapeIdentifier(table)} WHERE ${whereClauses.join(\" AND \")} LIMIT 1`,\n values\n );\n return this.mapRowFromRawSql(insertTableObj, rows[0] as T, aliases);\n },\n\n insertMany: async <T = unknown>(\n table: string,\n data: Record<string, unknown>[],\n options?: InsertOptions\n ): Promise<T[]> => {\n if (data.length === 0) return [];\n const retMany = options?.returning;\n const skipReread = Array.isArray(retMany) && retMany.length === 0;\n\n const tableObj = this.getTableObject(table);\n const mappedRecords = data.map(r => this.mapRowToRawSql(tableObj, r));\n const columns = Object.keys(mappedRecords[0]);\n const allValues: unknown[] = [];\n const valuesClauses: string[] = [];\n\n for (const record of mappedRecords) {\n const placeholders: string[] = [];\n for (const col of columns) {\n allValues.push(record[col]);\n placeholders.push(\"?\");\n }\n valuesClauses.push(`(${placeholders.join(\", \")})`);\n }\n\n const sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map(c => this.escapeIdentifier(c)).join(\", \")}) VALUES ${valuesClauses.join(\", \")}`;\n\n const [result] = await connection.query<ResultSetHeader>(\n sql,\n allValues\n );\n\n // For bulk insert, we need to SELECT the inserted rows\n // MySQL's insertId gives the first auto-increment ID\n if (!skipReread && result.insertId && result.affectedRows > 0) {\n const ids: number[] = [];\n for (let i = 0; i < result.affectedRows; i++) {\n ids.push(result.insertId + i);\n }\n const placeholders = ids.map(() => \"?\").join(\", \");\n const bulkAliases = this.dateWallClockAliases(tableObj, \"*\");\n const bulkSpelled = bulkAliases\n .map(\n a =>\n `DATE_FORMAT(${this.escapeIdentifier(a.sqlName)}, '%Y-%m-%dT%H:%i:%s.%f') AS ${this.escapeIdentifier(a.alias)}`\n )\n .join(\", \");\n const [rows] = await connection.query<RowDataPacket[]>(\n `SELECT *${bulkSpelled ? `, ${bulkSpelled}` : \"\"} FROM ${this.escapeIdentifier(table)} WHERE id IN (${placeholders})`,\n ids\n );\n return (rows as T[]).map(r =>\n this.mapRowFromRawSql(tableObj, r, bulkAliases)\n );\n }\n\n // Fallback: return empty if we can't determine inserted rows\n return [];\n },\n\n // TransactionContext CRUD methods delegate to the adapter's Drizzle CRUD\n // but pass the transaction-bound executor so they run inside this\n // transaction rather than on the pool.\n select: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T[]> => {\n return this.select<T>(table, options, txDb());\n },\n\n selectOne: async <T = unknown>(\n table: string,\n options?: SelectOptions\n ): Promise<T | null> => {\n return this.selectOne<T>(table, options, txDb());\n },\n\n update: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n where: WhereClause,\n options?: UpdateOptions\n ): Promise<T[]> => {\n return this.update<T>(table, data, where, options, txDb());\n },\n\n delete: async (\n table: string,\n where: WhereClause,\n options?: DeleteOptions\n ): Promise<number> => {\n return this.delete(table, where, options, txDb());\n },\n\n upsert: async <T = unknown>(\n table: string,\n data: Record<string, unknown>,\n options: UpsertOptions\n ): Promise<T> => {\n return this.upsert<T>(table, data, options, txDb());\n },\n\n // Savepoints disabled per approved approach\n savepoint: undefined,\n rollbackToSavepoint: undefined,\n releaseSavepoint: undefined,\n\n // Expose the transaction-bound Drizzle instance so callers can run\n // Drizzle sql templates inside this transaction (junction-table writes\n // need this to be atomic with the entry write). Reuses the memoized\n // txDb() built for the delegated CRUD methods.\n getDrizzle: <T = unknown>(): T => txDb() as T,\n };\n }\n\n /**\n * Classifies a MySQL error into a DatabaseError.\n *\n * @param error - Original error from mysql2\n * @param sql - SQL statement that caused the error (optional)\n * @returns DatabaseError with proper classification\n */\n private classifyError(error: unknown, sql?: string): DatabaseError {\n // Why short-circuit on existing DatabaseError: F17's\n // UnsupportedDialectVersionError is already a typed DatabaseError with\n // kind: \"unsupported_version\" plus detectedVersion/requiredVersion\n // fields. Re-wrapping it here would erase those fields and re-tag it\n // as kind: \"unknown\".\n if (isDatabaseError(error)) return error;\n\n const mysqlError = error as {\n errno?: number;\n code?: string;\n sqlState?: string;\n message?: string;\n sql?: string;\n };\n\n // Determine error kind from MySQL error number\n const kind: DatabaseErrorKind =\n (mysqlError.errno && MYSQL_ERROR_CODES[mysqlError.errno]) || \"unknown\";\n\n // Build error message\n let message = mysqlError.message ?? String(error);\n if (sql && kind === \"query\") {\n message = `Query failed: ${message}`;\n }\n\n return createDatabaseError({\n kind,\n message,\n code: mysqlError.code ?? mysqlError.errno?.toString(),\n detail: mysqlError.sql,\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Override handleQueryError to use MySQL-specific classification.\n */\n protected override handleQueryError(\n error: unknown,\n operation: string,\n table: string\n ): DatabaseError {\n const dbError = this.classifyError(error);\n\n // Add operation context if not already present\n if (!dbError.message.includes(operation)) {\n dbError.message = `${operation} operation failed on table '${table}': ${dbError.message}`;\n }\n\n if (!dbError.table) {\n dbError.table = table;\n }\n\n return dbError;\n }\n}\n\n/**\n * Create a MySQL database adapter.\n *\n * @param config - MySQL adapter configuration\n * @returns A new MySqlAdapter instance\n *\n * @example\n * ```typescript\n * // Simple usage with URL\n * const adapter = createMySqlAdapter({\n * url: 'mysql://user:pass@localhost:3306/mydb',\n * });\n *\n * // Full configuration\n * const adapter = createMySqlAdapter({\n * url: process.env.DATABASE_URL!,\n * pool: {\n * min: 2,\n * max: 20,\n * idleTimeoutMs: 30000,\n * connectionTimeoutMs: 10000,\n * },\n * ssl: {\n * rejectUnauthorized: true,\n * },\n * });\n *\n * await adapter.connect();\n * ```\n */\nexport function createMySqlAdapter(config: MySqlAdapterConfig): MySqlAdapter {\n return new MySqlAdapter(config);\n}\n\n/**\n * Type guard to check if a value is a MySqlAdapter.\n *\n * @param value - Value to check\n * @returns True if value is a MySqlAdapter instance\n *\n * @example\n * ```typescript\n * if (isMySqlAdapter(adapter)) {\n * // TypeScript knows adapter is MySqlAdapter\n * console.log('Using MySQL');\n * }\n * ```\n */\nexport function isMySqlAdapter(value: unknown): value is MySqlAdapter {\n return value instanceof MySqlAdapter;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextlyhq/adapter-mysql",
|
|
3
|
-
"version": "0.0.2-alpha.
|
|
3
|
+
"version": "0.0.2-alpha.60",
|
|
4
4
|
"description": "MySQL database adapter for Nextly - extends @nextlyhq/adapter-drizzle",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,24 +18,24 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": "
|
|
21
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"drizzle-orm": "
|
|
25
|
-
"mysql2": "^3.
|
|
26
|
-
"@nextlyhq/adapter-drizzle": "0.0.2-alpha.
|
|
24
|
+
"drizzle-orm": "1.0.0-rc.4",
|
|
25
|
+
"mysql2": "^3.15.0",
|
|
26
|
+
"@nextlyhq/adapter-drizzle": "0.0.2-alpha.60"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@vitest/coverage-v8": "^4.0
|
|
30
|
-
"@vitest/ui": "^4.0
|
|
31
|
-
"eslint": "^9.
|
|
29
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
30
|
+
"@vitest/ui": "^4.1.0",
|
|
31
|
+
"eslint": "^9.39.1",
|
|
32
32
|
"mysql2": "^3.15.0",
|
|
33
33
|
"tsup": "^8.5.0",
|
|
34
34
|
"typescript": "^5.9.3",
|
|
35
35
|
"vite-tsconfig-paths": "^5.1.4",
|
|
36
|
-
"vitest": "^4.0
|
|
37
|
-
"@nextlyhq/eslint-config": "0.0.2-alpha.
|
|
38
|
-
"@nextlyhq/tsconfig": "0.0.2-alpha.
|
|
36
|
+
"vitest": "^4.1.0",
|
|
37
|
+
"@nextlyhq/eslint-config": "0.0.2-alpha.60",
|
|
38
|
+
"@nextlyhq/tsconfig": "0.0.2-alpha.60"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"nextly",
|