driftsql 1.0.12 → 1.0.13

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.mjs CHANGED
@@ -4,284 +4,74 @@ import ky from 'ky';
4
4
  import { createClient as createClient$1 } from '@libsql/client';
5
5
  import { createClient } from '@tursodatabase/serverless/compat';
6
6
  import * as mysql from 'mysql2/promise';
7
- import fs from 'node:fs/promises';
7
+ import Database from 'better-sqlite3';
8
8
 
9
- const supportedDrivers = ["postgres", "postgresHTTP", "mysql", "libsql"];
10
- const withTimeout = (promise, timeoutMs = 3e4) => {
11
- return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error(`Query timeout after ${timeoutMs}ms`)), timeoutMs))]);
12
- };
13
- const retryQuery = async (queryFn, maxRetries = 3, baseDelay = 1e3) => {
14
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
15
- try {
16
- return await queryFn();
17
- } catch (error) {
18
- if (attempt === maxRetries) {
19
- throw error;
20
- }
21
- const delay = baseDelay * Math.pow(2, attempt - 1);
22
- console.warn(`Query attempt ${attempt} failed, retrying in ${delay}ms...`, error);
23
- await new Promise((resolve) => setTimeout(resolve, delay));
24
- }
9
+ function hasTransactionSupport(driver) {
10
+ return "transaction" in driver && typeof driver.transaction === "function";
11
+ }
12
+ function hasPreparedStatementSupport(driver) {
13
+ return "prepare" in driver && typeof driver.prepare === "function";
14
+ }
15
+ class DatabaseError extends Error {
16
+ constructor(message, driverType, originalError) {
17
+ super(message);
18
+ this.driverType = driverType;
19
+ this.originalError = originalError;
20
+ this.name = "DatabaseError";
25
21
  }
26
- throw new Error("Max retries exceeded");
27
- };
28
- const mapDatabaseTypeToTypeScript = (dataType, isNullable = false, driverType = "postgres") => {
29
- const nullable = isNullable ? " | null" : "";
30
- const lowerType = dataType.toLowerCase();
31
- switch (lowerType) {
32
- case "uuid": {
33
- return `string${nullable}`;
34
- }
35
- case "character varying":
36
- case "varchar":
37
- case "text":
38
- case "char":
39
- case "character":
40
- case "longtext":
41
- case "mediumtext":
42
- case "tinytext": {
43
- return `string${nullable}`;
44
- }
45
- case "integer":
46
- case "int":
47
- case "int4":
48
- case "smallint":
49
- case "int2":
50
- case "bigint":
51
- case "int8":
52
- case "serial":
53
- case "bigserial":
54
- case "numeric":
55
- case "decimal":
56
- case "real":
57
- case "float4":
58
- case "double precision":
59
- case "float8":
60
- case "tinyint":
61
- case "mediumint":
62
- case "float":
63
- case "double": {
64
- return `number${nullable}`;
65
- }
66
- case "boolean":
67
- case "bool":
68
- case "bit": {
69
- return `boolean${nullable}`;
70
- }
71
- case "timestamp":
72
- case "timestamp with time zone":
73
- case "timestamp without time zone":
74
- case "timestamptz":
75
- case "date":
76
- case "time":
77
- case "time with time zone":
78
- case "time without time zone":
79
- case "timetz":
80
- case "interval":
81
- case "datetime":
82
- case "year": {
83
- return `Date${nullable}`;
84
- }
85
- case "json":
86
- case "jsonb": {
87
- return `any${nullable}`;
88
- }
89
- case "array": {
90
- return `any[]${nullable}`;
91
- }
92
- case "bytea":
93
- case "binary":
94
- case "varbinary":
95
- case "blob":
96
- case "longblob":
97
- case "mediumblob":
98
- case "tinyblob": {
99
- return `Buffer${nullable}`;
100
- }
101
- case "enum":
102
- case "set": {
103
- return `string${nullable}`;
104
- }
105
- default: {
106
- console.warn(`Unknown ${driverType} type: ${dataType}, defaulting to 'any'`);
107
- return `any${nullable}`;
108
- }
22
+ }
23
+ class QueryError extends DatabaseError {
24
+ constructor(driverType, sql, originalError) {
25
+ super(`Query failed: ${sql}`, driverType, originalError);
26
+ this.name = "QueryError";
109
27
  }
110
- };
111
- const inspectDB = async (drivers) => {
112
- if (!drivers) throw new Error("No drivers provided for inspection");
113
- const configuredDrivers = Object.keys(drivers).filter((key) => drivers[key] !== void 0);
114
- if (configuredDrivers.length === 0) {
115
- throw new Error("No drivers are configured");
116
- }
117
- const supportedConfiguredDrivers = configuredDrivers.filter((driver) => supportedDrivers.includes(driver));
118
- if (supportedConfiguredDrivers.length === 0) {
119
- throw new Error(`No supported drivers found. Configured: ${configuredDrivers.join(", ")}. Supported: ${supportedDrivers.join(", ")}`);
120
- }
121
- console.log(`Found supported drivers: ${supportedConfiguredDrivers.join(", ")}`);
122
- const activeDriver = supportedConfiguredDrivers[0];
123
- let generatedTypes = "";
124
- const client = new DriftSQLClient({ drivers });
125
- try {
126
- let tablesQuery;
127
- let tableSchemaFilter;
128
- if (activeDriver === "mysql") {
129
- const dbResult = await withTimeout(
130
- retryQuery(() => client.query("SELECT DATABASE() as `database`", [])),
131
- 1e4
132
- );
133
- const currentDatabase = dbResult.rows[0]?.database;
134
- if (!currentDatabase) {
135
- throw new Error("Could not determine current MySQL database name");
136
- }
137
- console.log(`Using MySQL database: ${currentDatabase}`);
138
- tablesQuery = `SELECT TABLE_NAME as table_name
139
- FROM information_schema.tables
140
- WHERE TABLE_SCHEMA = ?
141
- AND TABLE_TYPE = 'BASE TABLE'
142
- ORDER BY TABLE_NAME`;
143
- tableSchemaFilter = currentDatabase;
144
- } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
145
- tablesQuery = `SELECT table_name
146
- FROM information_schema.tables
147
- WHERE table_schema = $1
148
- AND table_type = 'BASE TABLE'
149
- ORDER BY table_name`;
150
- tableSchemaFilter = "public";
151
- } else {
152
- tablesQuery = `SELECT name as table_name
153
- FROM sqlite_master
154
- WHERE type = 'table'
155
- ORDER BY name`;
156
- tableSchemaFilter = void 0;
157
- }
158
- const tables = await withTimeout(
159
- retryQuery(() => client.query(tablesQuery, tableSchemaFilter ? [tableSchemaFilter] : [])),
160
- 3e4
161
- );
162
- console.log("Tables in the database:", tables.rows.map((t) => t.table_name).join(", "));
163
- let processedTables = 0;
164
- const totalTables = tables.rows.length;
165
- for (const table of tables.rows) {
166
- const tableName = table.table_name;
167
- processedTables++;
168
- console.log(`[${processedTables}/${totalTables}] Inspecting table: ${tableName}`);
169
- try {
170
- let columnsQuery;
171
- let queryParams;
172
- if (activeDriver === "mysql") {
173
- columnsQuery = `
174
- SELECT
175
- COLUMN_NAME as column_name,
176
- DATA_TYPE as data_type,
177
- IS_NULLABLE as is_nullable,
178
- COLUMN_DEFAULT as column_default
179
- FROM information_schema.columns
180
- WHERE TABLE_NAME = ?
181
- AND TABLE_SCHEMA = ?
182
- ORDER BY ORDINAL_POSITION
183
- `;
184
- queryParams = [tableName, tableSchemaFilter];
185
- } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
186
- columnsQuery = `
187
- SELECT
188
- column_name,
189
- data_type,
190
- is_nullable,
191
- column_default
192
- FROM information_schema.columns
193
- WHERE table_name = $1
194
- AND table_schema = $2
195
- ORDER BY ordinal_position
196
- `;
197
- queryParams = [tableName, tableSchemaFilter];
198
- } else {
199
- columnsQuery = `
200
- SELECT
201
- name as column_name,
202
- type as data_type,
203
- CASE WHEN "notnull" = 0 THEN 'YES' ELSE 'NO' END as is_nullable,
204
- dflt_value as column_default
205
- FROM pragma_table_info(?)
206
- ORDER BY cid
207
- `;
208
- queryParams = [tableName];
209
- }
210
- const columns = await withTimeout(
211
- retryQuery(
212
- () => client.query(columnsQuery, queryParams)
213
- ),
214
- 15e3
215
- // Shorter timeout for individual table queries
216
- );
217
- if (columns.rows.length === 0) {
218
- console.log(`No columns found for table: ${tableName}`);
219
- continue;
220
- }
221
- console.log(`Columns in ${tableName}:`, columns.rows.map((c) => `${c.column_name} (${c.data_type}${c.is_nullable === "YES" ? ", nullable" : ""})`).join(", "));
222
- const uniqueColumns = /* @__PURE__ */ new Map();
223
- columns.rows.forEach((col) => {
224
- if (!uniqueColumns.has(col.column_name)) {
225
- uniqueColumns.set(col.column_name, col);
226
- }
227
- });
228
- generatedTypes += `export interface ${tableName.charAt(0).toUpperCase() + tableName.slice(1)} {
229
- `;
230
- for (const col of uniqueColumns.values()) {
231
- const tsType = mapDatabaseTypeToTypeScript(col.data_type, col.is_nullable === "YES", activeDriver);
232
- generatedTypes += ` ${col.column_name}: ${tsType};
233
- `;
234
- }
235
- generatedTypes += "}\n\n";
236
- } catch (error) {
237
- console.error(`Failed to process table ${tableName}:`, error);
238
- console.log(`Skipping table ${tableName} and continuing...`);
239
- continue;
240
- }
241
- }
242
- generatedTypes += "export interface Database {\n";
243
- for (const table of tables.rows) {
244
- const tableName = table.table_name.charAt(0).toUpperCase() + table.table_name.slice(1);
245
- generatedTypes += ` ${tableName}: ${tableName};
246
- `;
247
- }
248
- generatedTypes += "}\n\n";
249
- await fs.writeFile("db-types.ts", generatedTypes, "utf8");
250
- console.log("TypeScript types written to db-types.ts");
251
- console.log(`Successfully processed ${processedTables} tables`);
252
- } catch (error) {
253
- console.error("Fatal error during database inspection:", error);
254
- process.exit(1);
255
- }
256
- process.exit(0);
257
- };
28
+ }
29
+ class ConnectionError extends DatabaseError {
30
+ constructor(driverType, originalError) {
31
+ super(`Failed to connect to ${driverType}`, driverType, originalError);
32
+ this.name = "ConnectionError";
33
+ }
34
+ }
258
35
 
259
36
  class PostgresDriver {
260
- constructor(options) {
261
- this.options = options;
262
- if (this.options.options?.experimental?.http) {
263
- this.client = new PostgresHTTPDriver(this.options.options.experimental);
264
- } else {
265
- this.client = postgres(this.options.connectionString || "");
37
+ client;
38
+ constructor(config) {
39
+ try {
40
+ if (config.experimental?.http) {
41
+ this.client = new PostgresHTTPDriver(config.experimental.http);
42
+ } else {
43
+ this.client = postgres(config.connectionString || "");
44
+ }
45
+ } catch (error) {
46
+ throw new ConnectionError("postgres", error);
266
47
  }
267
48
  }
268
- client;
269
- async query(query, params) {
49
+ async query(sql, params) {
270
50
  try {
271
51
  if (this.client instanceof PostgresHTTPDriver) {
272
- return await this.client.query(query, params);
52
+ return await this.client.query(sql, params);
273
53
  }
274
- const result = await this.client.unsafe(query, params);
54
+ const result = await this.client.unsafe(sql, params || []);
275
55
  return {
276
56
  rows: result,
277
- rowCount: result.length,
57
+ rowCount: Array.isArray(result) ? result.length : 0,
278
58
  command: void 0
279
59
  };
280
60
  } catch (error) {
281
- console.error("Postgres query error:", error);
282
- throw error;
61
+ throw new QueryError("postgres", sql, error);
283
62
  }
284
63
  }
64
+ async transaction(callback) {
65
+ if (this.client instanceof PostgresHTTPDriver) {
66
+ throw new Error("Transactions not supported with HTTP driver");
67
+ }
68
+ const result = await this.client.begin(async (sql) => {
69
+ const transactionDriver = new PostgresDriver({ connectionString: "" });
70
+ transactionDriver.client = sql;
71
+ return await callback(transactionDriver);
72
+ });
73
+ return result;
74
+ }
285
75
  async close() {
286
76
  try {
287
77
  if (this.client instanceof PostgresHTTPDriver) {
@@ -290,30 +80,23 @@ class PostgresDriver {
290
80
  await this.client.end();
291
81
  } catch (error) {
292
82
  console.error("Error closing Postgres client:", error);
293
- throw error;
294
83
  }
295
84
  }
296
85
  }
297
86
  class PostgresHTTPDriver {
298
87
  httpClient;
299
- constructor(options) {
300
- if (!options || !options.http || !options.http.url) {
301
- throw new Error("Postgres HTTP driver requires a valid URL in options.http");
302
- }
88
+ constructor(config) {
303
89
  this.httpClient = ky.create({
304
- prefixUrl: options.http.url,
90
+ prefixUrl: config.url,
305
91
  headers: {
306
- Authorization: `Bearer ${options.http.apiKey || ""}`
92
+ Authorization: `Bearer ${config.apiKey || ""}`
307
93
  }
308
94
  });
309
95
  }
310
- async query(query, params) {
96
+ async query(sql, params) {
311
97
  try {
312
98
  const response = await this.httpClient.post("query", {
313
- json: {
314
- query,
315
- params
316
- }
99
+ json: { query: sql, params }
317
100
  }).json();
318
101
  return {
319
102
  rows: response.rows,
@@ -321,8 +104,7 @@ class PostgresHTTPDriver {
321
104
  command: void 0
322
105
  };
323
106
  } catch (error) {
324
- console.error("Postgres HTTP query error:", error);
325
- throw error;
107
+ throw new QueryError("postgres-http", sql, error);
326
108
  }
327
109
  }
328
110
  async close() {
@@ -331,17 +113,40 @@ class PostgresHTTPDriver {
331
113
  }
332
114
 
333
115
  class LibSQLDriver {
334
- constructor(options) {
335
- this.options = options;
336
- this.client = this.options?.options?.experimental?.useTursoServerlessDriver ? createClient({
337
- url: this.options.url,
338
- ...this.options.authToken ? { authToken: this.options.authToken } : {}
339
- }) : createClient$1({
340
- url: this.options.url,
341
- ...this.options.authToken ? { authToken: this.options.authToken } : {}
342
- });
343
- }
344
116
  client;
117
+ constructor(config) {
118
+ try {
119
+ this.client = config.useTursoServerlessDriver ? createClient({
120
+ url: config.url,
121
+ ...config.authToken ? { authToken: config.authToken } : {}
122
+ }) : createClient$1({
123
+ url: config.url,
124
+ ...config.authToken ? { authToken: config.authToken } : {}
125
+ });
126
+ } catch (error) {
127
+ throw new ConnectionError("libsql", error);
128
+ }
129
+ }
130
+ async query(sql, params) {
131
+ try {
132
+ const result = await this.client.execute(sql, params);
133
+ return this.convertLibsqlResult(result);
134
+ } catch (error) {
135
+ throw new QueryError("libsql", sql, error);
136
+ }
137
+ }
138
+ async transaction(callback) {
139
+ const transactionDriver = new LibSQLDriver({ url: "", authToken: "" });
140
+ transactionDriver.client = this.client;
141
+ return await callback(transactionDriver);
142
+ }
143
+ async close() {
144
+ try {
145
+ this.client.close();
146
+ } catch (error) {
147
+ console.error("Error closing LibSQL client:", error);
148
+ }
149
+ }
345
150
  convertLibsqlResult(result) {
346
151
  const rows = result.rows.map((row) => {
347
152
  const obj = {};
@@ -357,158 +162,241 @@ class LibSQLDriver {
357
162
  fields: result.columns.map((col) => ({ name: col, dataTypeID: 0 }))
358
163
  };
359
164
  }
360
- async query(query, params) {
165
+ }
166
+
167
+ class MySQLDriver {
168
+ client;
169
+ constructor(config) {
170
+ consola.warn("MySQL client is experimental and may not be compatible with the helper functions, since they originally designed for PostgreSQL and libsql. But .query() method should work.");
171
+ try {
172
+ this.client = mysql.createConnection(config.connectionString);
173
+ } catch (error) {
174
+ throw new ConnectionError("mysql", error);
175
+ }
176
+ }
177
+ async query(sql, params) {
361
178
  try {
362
- const result = await this.client.execute(query, params);
363
- return this.convertLibsqlResult(result);
179
+ const [rows, fields] = await (await this.client).execute(sql, params || []);
180
+ const rowCount = Array.isArray(rows) ? rows.length : 0;
181
+ const normalizedFields = fields.map((field) => ({
182
+ name: field.name,
183
+ dataTypeID: field.columnType
184
+ }));
185
+ return {
186
+ rows,
187
+ rowCount,
188
+ command: void 0,
189
+ fields: normalizedFields
190
+ };
191
+ } catch (error) {
192
+ throw new QueryError("mysql", sql, error);
193
+ }
194
+ }
195
+ async transaction(callback) {
196
+ const connection = await this.client;
197
+ try {
198
+ await connection.beginTransaction();
199
+ const transactionDriver = new MySQLDriver({ connectionString: "" });
200
+ transactionDriver.client = Promise.resolve(connection);
201
+ const result = await callback(transactionDriver);
202
+ await connection.commit();
203
+ return result;
364
204
  } catch (error) {
365
- console.error("LibSQL query error:", error);
205
+ await connection.rollback();
366
206
  throw error;
367
207
  }
368
208
  }
369
209
  async close() {
370
210
  try {
371
- this.client.close();
211
+ await (await this.client).end();
372
212
  } catch (error) {
373
- console.error("Error closing LibSQL client:", error);
213
+ consola.error("Error closing MySQL client:", error);
374
214
  }
375
215
  }
376
216
  }
377
217
 
378
- class MySQLDriver {
379
- constructor(options) {
380
- this.options = options;
381
- consola.warn("MySQL client is experimental and may not be compatible with the helper functions, since they originally designed for PostgreSQL and libsql. But .query() method should work.");
382
- this.client = mysql.createConnection(this.options.connectionString);
383
- }
218
+ class SqliteDriver {
384
219
  client;
385
- async query(query, params) {
220
+ constructor(config) {
386
221
  try {
387
- const [rows, fields] = await (await this.client).execute(query, params || []);
388
- const rowCount = Array.isArray(rows) ? rows.length : 0;
222
+ this.client = new Database(config.filename, {
223
+ readonly: config.readonly || false,
224
+ fileMustExist: config.readonly || false
225
+ });
226
+ } catch (error) {
227
+ throw new ConnectionError("sqlite", error);
228
+ }
229
+ }
230
+ async query(sql, params) {
231
+ try {
232
+ const stmt = this.client.prepare(sql);
233
+ const rows = stmt.all(params || []);
234
+ const fields = rows.length > 0 && typeof rows[0] === "object" && rows[0] !== null ? Object.keys(rows[0]).map((name) => ({ name, dataTypeID: 0 })) : [];
389
235
  return {
390
236
  rows,
391
- rowCount,
237
+ rowCount: rows.length,
392
238
  command: void 0,
393
- fields: fields.map((field) => ({ name: field.name, dataTypeID: field.columnType }))
239
+ fields
394
240
  };
395
241
  } catch (error) {
396
- consola.error("MySQL query error:", error);
397
- throw error;
242
+ throw new QueryError("sqlite", sql, error);
398
243
  }
399
244
  }
245
+ async transaction(callback) {
246
+ const transaction = this.client.transaction(() => {
247
+ const transactionDriver = new SqliteDriver({ filename: "" });
248
+ transactionDriver.client = this.client;
249
+ return callback(transactionDriver);
250
+ });
251
+ return await transaction();
252
+ }
253
+ async prepare(sql) {
254
+ return new SqlitePreparedStatement(this.client.prepare(sql));
255
+ }
400
256
  async close() {
401
257
  try {
402
- await (await this.client).end();
258
+ this.client.close();
403
259
  } catch (error) {
404
- consola.error("Error closing MySQL client:", error);
260
+ console.error("Error closing SQLite client:", error);
405
261
  }
406
262
  }
407
- }
408
-
409
- class DriftSQLClient {
410
- postgres;
411
- libsql;
412
- mysql;
413
- drivers;
414
- constructor(options) {
415
- this.postgres = options.drivers?.postgres ? new PostgresDriver({ connectionString: options.drivers.postgres.connectionString }) : void 0;
416
- this.libsql = options.drivers?.libsql ? new LibSQLDriver(options.drivers.libsql) : void 0;
417
- this.mysql = options.drivers?.mysql ? new MySQLDriver(options.drivers.mysql) : void 0;
418
- this.drivers = options.drivers || {};
419
- }
420
- inspect = async () => {
421
- return inspectDB(this.drivers);
422
- };
423
- async query(query, args) {
424
- if (this.postgres) {
263
+ // SQLite-specific methods
264
+ exec(sql) {
265
+ this.client.exec(sql);
266
+ }
267
+ backup(filename) {
268
+ return new Promise((resolve, reject) => {
425
269
  try {
426
- const result = await this.postgres.query(query, args || []);
427
- return {
428
- rows: result.rows,
429
- rowCount: result.rowCount || 0,
430
- command: result.command,
431
- fields: result.fields?.map((field) => ({ name: field.name, dataTypeID: field.dataTypeID })) || []
432
- };
433
- } catch (error2) {
434
- consola.error("Failed to execute query with PostgreSQL:", error2);
435
- throw error2;
270
+ this.client.backup(filename);
271
+ resolve();
272
+ } catch (error) {
273
+ reject(error);
436
274
  }
275
+ });
276
+ }
277
+ pragma(pragma) {
278
+ return this.client.pragma(pragma);
279
+ }
280
+ }
281
+ class SqlitePreparedStatement {
282
+ constructor(stmt) {
283
+ this.stmt = stmt;
284
+ }
285
+ async execute(params) {
286
+ try {
287
+ const rows = this.stmt.all(params || []);
288
+ const fields = rows.length > 0 && typeof rows[0] === "object" && rows[0] !== null ? Object.keys(rows[0]).map((name) => ({ name, dataTypeID: 0 })) : [];
289
+ return {
290
+ rows,
291
+ rowCount: rows.length,
292
+ command: void 0,
293
+ fields
294
+ };
295
+ } catch (error) {
296
+ throw new QueryError("sqlite", "prepared statement", error);
437
297
  }
438
- if (this.mysql) {
298
+ }
299
+ async finalize() {
300
+ return Promise.resolve();
301
+ }
302
+ // SQLite-specific methods for prepared statements
303
+ run(params) {
304
+ return this.stmt.run(params || []);
305
+ }
306
+ get(params) {
307
+ return this.stmt.get(params || []);
308
+ }
309
+ all(params) {
310
+ return this.stmt.all(params || []);
311
+ }
312
+ }
313
+
314
+ class SQLClient {
315
+ primaryDriver;
316
+ fallbackDrivers;
317
+ constructor(options) {
318
+ this.primaryDriver = options.driver;
319
+ this.fallbackDrivers = options.fallbackDrivers || [];
320
+ }
321
+ async query(sql, params) {
322
+ const drivers = [this.primaryDriver, ...this.fallbackDrivers];
323
+ let lastError;
324
+ for (const driver of drivers) {
439
325
  try {
440
- const result = await this.mysql.query(query, args || []);
441
- return {
442
- rows: result.rows,
443
- rowCount: result.rowCount || 0,
444
- command: result.command,
445
- fields: result.fields?.map((field) => ({ name: field.name, dataTypeID: field.dataTypeID })) || []
446
- };
447
- } catch (error2) {
448
- consola.error("Failed to execute query with MySQL:", error2);
449
- throw error2;
326
+ return await driver.query(sql, params);
327
+ } catch (error) {
328
+ lastError = error;
329
+ consola.warn(`Query failed with ${driver.constructor.name}:`, error);
330
+ continue;
450
331
  }
451
332
  }
452
- if (this.libsql) {
453
- try {
454
- const result = await this.libsql.query(query, args || []);
455
- return {
456
- rows: result.rows,
457
- rowCount: result.rowCount || 0,
458
- command: result.command,
459
- fields: void 0
460
- };
461
- } catch (error2) {
462
- consola.error("Failed to execute query with LibSQL:", error2);
463
- throw error2;
464
- }
333
+ throw lastError || new DatabaseError("All drivers failed to execute query", "unknown");
334
+ }
335
+ async transaction(callback) {
336
+ if (!hasTransactionSupport(this.primaryDriver)) {
337
+ throw new DatabaseError("Primary driver does not support transactions", this.primaryDriver.constructor.name);
338
+ }
339
+ return await this.primaryDriver.transaction(async (transactionDriver) => {
340
+ const transactionClient = new SQLClient({
341
+ driver: transactionDriver,
342
+ fallbackDrivers: []
343
+ });
344
+ return await callback(transactionClient);
345
+ });
346
+ }
347
+ async prepare(sql) {
348
+ if (!hasPreparedStatementSupport(this.primaryDriver)) {
349
+ throw new DatabaseError("Primary driver does not support prepared statements", this.primaryDriver.constructor.name);
465
350
  }
466
- const error = new Error("No database driver is configured or all drivers failed to execute the query");
467
- consola.error(error);
468
- throw error;
351
+ return await this.primaryDriver.prepare(sql);
469
352
  }
353
+ // Helper methods for common database operations
470
354
  async findFirst(table, where) {
471
355
  const tableName = String(table);
472
356
  const whereEntries = Object.entries(where || {});
473
- let query = `SELECT * FROM ${tableName}`;
474
- let args = [];
357
+ let sql = `SELECT * FROM ${tableName}`;
358
+ let params = [];
475
359
  if (whereEntries.length > 0) {
476
- const whereClause = whereEntries.map(([key], index) => `${key} = $${index + 1}`).join(" AND ");
477
- query += ` WHERE ${whereClause}`;
478
- args = whereEntries.map(([, value]) => value);
360
+ const whereClause = whereEntries.map((_, index) => `${whereEntries[index]?.[0]} = $${index + 1}`).join(" AND ");
361
+ sql += ` WHERE ${whereClause}`;
362
+ params = whereEntries.map(([, value]) => value);
479
363
  }
480
- query += " LIMIT 1";
481
- const result = await this.query(query, args);
364
+ sql += " LIMIT 1";
365
+ const result = await this.query(sql, params);
482
366
  return result.rows[0] || null;
483
367
  }
484
368
  async findMany(table, options) {
485
369
  const tableName = String(table);
486
- const { where, limit } = options || {};
370
+ const { where, limit, offset } = options || {};
487
371
  const whereEntries = Object.entries(where || {});
488
- let query = `SELECT * FROM ${tableName}`;
489
- let args = [];
372
+ let sql = `SELECT * FROM ${tableName}`;
373
+ let params = [];
490
374
  if (whereEntries.length > 0) {
491
- const whereClause = whereEntries.map(([key], index) => `${key} = $${index + 1}`).join(" AND ");
492
- query += ` WHERE ${whereClause}`;
493
- args = whereEntries.map(([, value]) => value);
375
+ const whereClause = whereEntries.map((_, index) => `${whereEntries[index]?.[0]} = $${index + 1}`).join(" AND ");
376
+ sql += ` WHERE ${whereClause}`;
377
+ params = whereEntries.map(([, value]) => value);
494
378
  }
495
379
  if (typeof limit === "number" && limit > 0) {
496
- query += ` LIMIT $${args.length + 1}`;
497
- args.push(limit);
380
+ sql += ` LIMIT $${params.length + 1}`;
381
+ params.push(limit);
382
+ }
383
+ if (typeof offset === "number" && offset > 0) {
384
+ sql += ` OFFSET $${params.length + 1}`;
385
+ params.push(offset);
498
386
  }
499
- const result = await this.query(query, args);
387
+ const result = await this.query(sql, params);
500
388
  return result.rows;
501
389
  }
502
390
  async insert(table, data) {
503
391
  const tableName = String(table);
504
392
  const keys = Object.keys(data);
505
- const values = Object.values(data).map((value) => value);
393
+ const values = Object.values(data);
506
394
  if (keys.length === 0) {
507
395
  throw new Error("No data provided for insert");
508
396
  }
509
397
  const placeholders = keys.map((_, index) => `$${index + 1}`).join(", ");
510
- const query = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders}) RETURNING *`;
511
- const result = await this.query(query, values);
398
+ const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders}) RETURNING *`;
399
+ const result = await this.query(sql, values);
512
400
  if (!result.rows[0]) {
513
401
  throw new Error("Insert failed: No data returned");
514
402
  }
@@ -524,11 +412,11 @@ class DriftSQLClient {
524
412
  if (whereEntries.length === 0) {
525
413
  throw new Error("No conditions provided for update");
526
414
  }
527
- const setClause = setEntries.map(([key], index) => `${key} = $${index + 1}`).join(", ");
528
- const whereClause = whereEntries.map(([key], index) => `${key} = $${setEntries.length + index + 1}`).join(" AND ");
529
- const query = `UPDATE ${tableName} SET ${setClause} WHERE ${whereClause} RETURNING *`;
530
- const args = [...setEntries.map(([, value]) => value), ...whereEntries.map(([, value]) => value)];
531
- const result = await this.query(query, args);
415
+ const setClause = setEntries.map((_, index) => `${setEntries[index]?.[0]} = $${index + 1}`).join(", ");
416
+ const whereClause = whereEntries.map((_, index) => `${whereEntries[index]?.[0]} = $${setEntries.length + index + 1}`).join(" AND ");
417
+ const sql = `UPDATE ${tableName} SET ${setClause} WHERE ${whereClause} RETURNING *`;
418
+ const params = [...setEntries.map(([, value]) => value), ...whereEntries.map(([, value]) => value)];
419
+ const result = await this.query(sql, params);
532
420
  return result.rows[0] || null;
533
421
  }
534
422
  async delete(table, where) {
@@ -537,26 +425,44 @@ class DriftSQLClient {
537
425
  if (whereEntries.length === 0) {
538
426
  throw new Error("No conditions provided for delete");
539
427
  }
540
- const whereClause = whereEntries.map(([key], index) => `${key} = $${index + 1}`).join(" AND ");
541
- const query = `DELETE FROM ${tableName} WHERE ${whereClause}`;
542
- const args = whereEntries.map(([, value]) => value);
543
- const result = await this.query(query, args);
544
- return (result.rowCount || 0) > 0;
428
+ const whereClause = whereEntries.map((_, index) => `${whereEntries[index]?.[0]} = $${index + 1}`).join(" AND ");
429
+ const sql = `DELETE FROM ${tableName} WHERE ${whereClause}`;
430
+ const params = whereEntries.map(([, value]) => value);
431
+ const result = await this.query(sql, params);
432
+ return result.rowCount || 0;
545
433
  }
546
- deleteFirst(table, where) {
547
- return this.delete(table, where);
434
+ // Get the primary driver (useful for driver-specific operations)
435
+ getDriver() {
436
+ return this.primaryDriver;
437
+ }
438
+ // Check driver capabilities
439
+ supportsTransactions() {
440
+ return hasTransactionSupport(this.primaryDriver);
441
+ }
442
+ supportsPreparedStatements() {
443
+ return hasPreparedStatementSupport(this.primaryDriver);
548
444
  }
549
445
  async close() {
550
- if (this.postgres) {
551
- await this.postgres.close();
552
- }
553
- if (this.libsql) {
554
- this.libsql.close();
555
- }
556
- if (this.mysql) {
557
- await this.mysql.close();
558
- }
446
+ const drivers = [this.primaryDriver, ...this.fallbackDrivers];
447
+ await Promise.all(drivers.map((driver) => driver.close().catch((err) => consola.warn(`Error closing ${driver.constructor.name}:`, err))));
559
448
  }
560
449
  }
450
+ function createPostgresClient(config) {
451
+ const { PostgresDriver: PostgresDriver2 } = require("./drivers/postgres");
452
+ return new SQLClient({ driver: new PostgresDriver2(config) });
453
+ }
454
+ function createLibSQLClient(config) {
455
+ const { LibSQLDriver: LibSQLDriver2 } = require("./drivers/libsql");
456
+ return new SQLClient({ driver: new LibSQLDriver2(config) });
457
+ }
458
+ function createMySQLClient(config) {
459
+ const { MySQLDriver: MySQLDriver2 } = require("./drivers/mysql");
460
+ return new SQLClient({ driver: new MySQLDriver2(config) });
461
+ }
462
+ function createSqliteClient(config) {
463
+ const { SqliteDriver: SqliteDriver2 } = require("./drivers/sqlite");
464
+ return new SQLClient({ driver: new SqliteDriver2(config) });
465
+ }
466
+ const DriftSQLClient = SQLClient;
561
467
 
562
- export { DriftSQLClient, inspectDB };
468
+ export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient };