driftsql 1.0.8 → 1.0.9

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.d.mts CHANGED
@@ -1,7 +1,10 @@
1
- import { PoolConfig } from 'pg';
2
1
  import { Config } from '@libsql/client';
3
2
  import { ConnectionOptions } from 'mysql2/promise';
4
3
 
4
+ interface PostgresConfig {
5
+ connectionString: string;
6
+ }
7
+
5
8
  type Drivers = ClientOptions['drivers'];
6
9
  declare const inspectDB: (drivers: Drivers) => Promise<never>;
7
10
 
@@ -15,21 +18,9 @@ type UnifiedQueryResult<T extends Record<string, any>> = {
15
18
  }>;
16
19
  };
17
20
  interface ClientOptions {
18
- /**
19
- * @deprecated Since version 1.0.8 this option is deprecated and will be removed in future versions. Use `drivers.postgresHTTP.url` instead.
20
- */
21
- url?: string;
22
- /**
23
- * @deprecated Since version 1.0.8 this option is deprecated and will be removed in future versions. Use `drivers.postgresHTTP.url` instead.
24
- */
25
- password?: string;
26
21
  drivers?: {
27
22
  libsql?: Config;
28
- postgres?: PoolConfig;
29
- postgresHTTP?: {
30
- url: string;
31
- password: string;
32
- };
23
+ postgres?: PostgresConfig;
33
24
  mysql?: ConnectionOptions;
34
25
  };
35
26
  options?: {
@@ -37,8 +28,7 @@ interface ClientOptions {
37
28
  };
38
29
  }
39
30
  declare class DriftSQLClient<DT> {
40
- private client;
41
- private pool?;
31
+ private postgres?;
42
32
  private mysqlClient?;
43
33
  private libsqlClient?;
44
34
  private postgresClient?;
@@ -47,10 +37,6 @@ declare class DriftSQLClient<DT> {
47
37
  private convertLibsqlResult;
48
38
  readonly inspect: () => Promise<void>;
49
39
  query<T extends Record<string, any>>(query: string, args?: (string | number | boolean | null)[]): Promise<UnifiedQueryResult<T>>;
50
- status(): Promise<{
51
- ok: boolean;
52
- ping: number;
53
- }>;
54
40
  findFirst<K extends keyof DT>(table: K, where?: Partial<DT[K]>): Promise<DT[K] | null>;
55
41
  findMany<K extends keyof DT>(table: K, options?: {
56
42
  where?: Partial<DT[K]>;
@@ -64,4 +50,4 @@ declare class DriftSQLClient<DT> {
64
50
  }
65
51
 
66
52
  export { DriftSQLClient, inspectDB };
67
- export type { ClientOptions };
53
+ export type { ClientOptions, UnifiedQueryResult };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
- import { PoolConfig } from 'pg';
2
1
  import { Config } from '@libsql/client';
3
2
  import { ConnectionOptions } from 'mysql2/promise';
4
3
 
4
+ interface PostgresConfig {
5
+ connectionString: string;
6
+ }
7
+
5
8
  type Drivers = ClientOptions['drivers'];
6
9
  declare const inspectDB: (drivers: Drivers) => Promise<never>;
7
10
 
@@ -15,21 +18,9 @@ type UnifiedQueryResult<T extends Record<string, any>> = {
15
18
  }>;
16
19
  };
17
20
  interface ClientOptions {
18
- /**
19
- * @deprecated Since version 1.0.8 this option is deprecated and will be removed in future versions. Use `drivers.postgresHTTP.url` instead.
20
- */
21
- url?: string;
22
- /**
23
- * @deprecated Since version 1.0.8 this option is deprecated and will be removed in future versions. Use `drivers.postgresHTTP.url` instead.
24
- */
25
- password?: string;
26
21
  drivers?: {
27
22
  libsql?: Config;
28
- postgres?: PoolConfig;
29
- postgresHTTP?: {
30
- url: string;
31
- password: string;
32
- };
23
+ postgres?: PostgresConfig;
33
24
  mysql?: ConnectionOptions;
34
25
  };
35
26
  options?: {
@@ -37,8 +28,7 @@ interface ClientOptions {
37
28
  };
38
29
  }
39
30
  declare class DriftSQLClient<DT> {
40
- private client;
41
- private pool?;
31
+ private postgres?;
42
32
  private mysqlClient?;
43
33
  private libsqlClient?;
44
34
  private postgresClient?;
@@ -47,10 +37,6 @@ declare class DriftSQLClient<DT> {
47
37
  private convertLibsqlResult;
48
38
  readonly inspect: () => Promise<void>;
49
39
  query<T extends Record<string, any>>(query: string, args?: (string | number | boolean | null)[]): Promise<UnifiedQueryResult<T>>;
50
- status(): Promise<{
51
- ok: boolean;
52
- ping: number;
53
- }>;
54
40
  findFirst<K extends keyof DT>(table: K, where?: Partial<DT[K]>): Promise<DT[K] | null>;
55
41
  findMany<K extends keyof DT>(table: K, options?: {
56
42
  where?: Partial<DT[K]>;
@@ -64,4 +50,4 @@ declare class DriftSQLClient<DT> {
64
50
  }
65
51
 
66
52
  export { DriftSQLClient, inspectDB };
67
- export type { ClientOptions };
53
+ export type { ClientOptions, UnifiedQueryResult };
package/dist/index.mjs CHANGED
@@ -1,11 +1,28 @@
1
1
  import consola from 'consola';
2
- import ky from 'ky';
3
- import { Pool } from 'pg';
4
2
  import { createClient } from '@libsql/client';
5
3
  import mysql from 'mysql2/promise';
4
+ import postgres from 'postgres';
6
5
  import fs from 'node:fs/promises';
7
6
 
8
7
  const supportedDrivers = ["postgres", "postgresHTTP", "mysql", "libsql"];
8
+ const withTimeout = (promise, timeoutMs = 3e4) => {
9
+ return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error(`Query timeout after ${timeoutMs}ms`)), timeoutMs))]);
10
+ };
11
+ const retryQuery = async (queryFn, maxRetries = 3, baseDelay = 1e3) => {
12
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
13
+ try {
14
+ return await queryFn();
15
+ } catch (error) {
16
+ if (attempt === maxRetries) {
17
+ throw error;
18
+ }
19
+ const delay = baseDelay * Math.pow(2, attempt - 1);
20
+ console.warn(`Query attempt ${attempt} failed, retrying in ${delay}ms...`, error);
21
+ await new Promise((resolve) => setTimeout(resolve, delay));
22
+ }
23
+ }
24
+ throw new Error("Max retries exceeded");
25
+ };
9
26
  const mapDatabaseTypeToTypeScript = (dataType, isNullable = false, driverType = "postgres") => {
10
27
  const nullable = isNullable ? " | null" : "";
11
28
  const lowerType = dataType.toLowerCase();
@@ -103,140 +120,177 @@ const inspectDB = async (drivers) => {
103
120
  const activeDriver = supportedConfiguredDrivers[0];
104
121
  let generatedTypes = "";
105
122
  const client = new DriftSQLClient({ drivers });
106
- let tablesQuery;
107
- let tableSchemaFilter;
108
- if (activeDriver === "mysql") {
109
- const dbResult = await client.query("SELECT DATABASE() as `database`", []);
110
- const currentDatabase = dbResult.rows[0]?.database;
111
- if (!currentDatabase) {
112
- throw new Error("Could not determine current MySQL database name");
113
- }
114
- console.log(`Using MySQL database: ${currentDatabase}`);
115
- tablesQuery = `SELECT TABLE_NAME as table_name
123
+ try {
124
+ let tablesQuery;
125
+ let tableSchemaFilter;
126
+ if (activeDriver === "mysql") {
127
+ const dbResult = await withTimeout(
128
+ retryQuery(() => client.query("SELECT DATABASE() as `database`", [])),
129
+ 1e4
130
+ );
131
+ const currentDatabase = dbResult.rows[0]?.database;
132
+ if (!currentDatabase) {
133
+ throw new Error("Could not determine current MySQL database name");
134
+ }
135
+ console.log(`Using MySQL database: ${currentDatabase}`);
136
+ tablesQuery = `SELECT TABLE_NAME as table_name
116
137
  FROM information_schema.tables
117
138
  WHERE TABLE_SCHEMA = ?
118
139
  AND TABLE_TYPE = 'BASE TABLE'
119
140
  ORDER BY TABLE_NAME`;
120
- tableSchemaFilter = currentDatabase;
121
- } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
122
- tablesQuery = `SELECT table_name
141
+ tableSchemaFilter = currentDatabase;
142
+ } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
143
+ tablesQuery = `SELECT table_name
123
144
  FROM information_schema.tables
124
145
  WHERE table_schema = $1
125
146
  AND table_type = 'BASE TABLE'
126
147
  ORDER BY table_name`;
127
- tableSchemaFilter = "public";
128
- } else {
129
- tablesQuery = `SELECT name as table_name
148
+ tableSchemaFilter = "public";
149
+ } else {
150
+ tablesQuery = `SELECT name as table_name
130
151
  FROM sqlite_master
131
152
  WHERE type = 'table'
132
153
  ORDER BY name`;
133
- tableSchemaFilter = void 0;
134
- }
135
- const tables = await client.query(tablesQuery, tableSchemaFilter ? [tableSchemaFilter] : []);
136
- console.log("Tables in the database:", tables.rows.map((t) => t.table_name).join(", "));
137
- for (const table of tables.rows) {
138
- const tableName = table.table_name;
139
- console.log(`Inspecting table: ${tableName}`);
140
- let columnsQuery;
141
- let queryParams;
142
- if (activeDriver === "mysql") {
143
- columnsQuery = `
144
- SELECT
145
- COLUMN_NAME as column_name,
146
- DATA_TYPE as data_type,
147
- IS_NULLABLE as is_nullable,
148
- COLUMN_DEFAULT as column_default
149
- FROM information_schema.columns
150
- WHERE TABLE_NAME = ?
151
- AND TABLE_SCHEMA = ?
152
- ORDER BY ORDINAL_POSITION
153
- `;
154
- queryParams = [tableName, tableSchemaFilter];
155
- } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
156
- columnsQuery = `
157
- SELECT
158
- column_name,
159
- data_type,
160
- is_nullable,
161
- column_default
162
- FROM information_schema.columns
163
- WHERE table_name = $1
164
- AND table_schema = $2
165
- ORDER BY ordinal_position
166
- `;
167
- queryParams = [tableName, tableSchemaFilter];
168
- } else {
169
- columnsQuery = `
170
- SELECT
171
- name as column_name,
172
- type as data_type,
173
- CASE WHEN "notnull" = 0 THEN 'YES' ELSE 'NO' END as is_nullable,
174
- dflt_value as column_default
175
- FROM pragma_table_info(?)
176
- ORDER BY cid
177
- `;
178
- queryParams = [tableName];
179
- }
180
- const columns = await client.query(columnsQuery, queryParams);
181
- if (columns.rows.length === 0) {
182
- console.log(`No columns found for table: ${tableName}`);
183
- continue;
184
- }
185
- console.log(`Columns in ${tableName}:`, columns.rows.map((c) => `${c.column_name} (${c.data_type}${c.is_nullable === "YES" ? ", nullable" : ""})`).join(", "));
186
- const uniqueColumns = /* @__PURE__ */ new Map();
187
- columns.rows.forEach((col) => {
188
- if (!uniqueColumns.has(col.column_name)) {
189
- uniqueColumns.set(col.column_name, col);
190
- }
191
- });
192
- generatedTypes += `export interface ${tableName.charAt(0).toUpperCase() + tableName.slice(1)} {
154
+ tableSchemaFilter = void 0;
155
+ }
156
+ const tables = await withTimeout(
157
+ retryQuery(() => client.query(tablesQuery, tableSchemaFilter ? [tableSchemaFilter] : [])),
158
+ 3e4
159
+ );
160
+ console.log("Tables in the database:", tables.rows.map((t) => t.table_name).join(", "));
161
+ let processedTables = 0;
162
+ const totalTables = tables.rows.length;
163
+ for (const table of tables.rows) {
164
+ const tableName = table.table_name;
165
+ processedTables++;
166
+ console.log(`[${processedTables}/${totalTables}] Inspecting table: ${tableName}`);
167
+ try {
168
+ let columnsQuery;
169
+ let queryParams;
170
+ if (activeDriver === "mysql") {
171
+ columnsQuery = `
172
+ SELECT
173
+ COLUMN_NAME as column_name,
174
+ DATA_TYPE as data_type,
175
+ IS_NULLABLE as is_nullable,
176
+ COLUMN_DEFAULT as column_default
177
+ FROM information_schema.columns
178
+ WHERE TABLE_NAME = ?
179
+ AND TABLE_SCHEMA = ?
180
+ ORDER BY ORDINAL_POSITION
181
+ `;
182
+ queryParams = [tableName, tableSchemaFilter];
183
+ } else if (activeDriver === "postgres" || activeDriver === "postgresHTTP") {
184
+ columnsQuery = `
185
+ SELECT
186
+ column_name,
187
+ data_type,
188
+ is_nullable,
189
+ column_default
190
+ FROM information_schema.columns
191
+ WHERE table_name = $1
192
+ AND table_schema = $2
193
+ ORDER BY ordinal_position
194
+ `;
195
+ queryParams = [tableName, tableSchemaFilter];
196
+ } else {
197
+ columnsQuery = `
198
+ SELECT
199
+ name as column_name,
200
+ type as data_type,
201
+ CASE WHEN "notnull" = 0 THEN 'YES' ELSE 'NO' END as is_nullable,
202
+ dflt_value as column_default
203
+ FROM pragma_table_info(?)
204
+ ORDER BY cid
205
+ `;
206
+ queryParams = [tableName];
207
+ }
208
+ const columns = await withTimeout(
209
+ retryQuery(
210
+ () => client.query(columnsQuery, queryParams)
211
+ ),
212
+ 15e3
213
+ // Shorter timeout for individual table queries
214
+ );
215
+ if (columns.rows.length === 0) {
216
+ console.log(`No columns found for table: ${tableName}`);
217
+ continue;
218
+ }
219
+ console.log(`Columns in ${tableName}:`, columns.rows.map((c) => `${c.column_name} (${c.data_type}${c.is_nullable === "YES" ? ", nullable" : ""})`).join(", "));
220
+ const uniqueColumns = /* @__PURE__ */ new Map();
221
+ columns.rows.forEach((col) => {
222
+ if (!uniqueColumns.has(col.column_name)) {
223
+ uniqueColumns.set(col.column_name, col);
224
+ }
225
+ });
226
+ generatedTypes += `export interface ${tableName.charAt(0).toUpperCase() + tableName.slice(1)} {
193
227
  `;
194
- for (const col of uniqueColumns.values()) {
195
- const tsType = mapDatabaseTypeToTypeScript(col.data_type, col.is_nullable === "YES", activeDriver);
196
- generatedTypes += ` ${col.column_name}: ${tsType};
228
+ for (const col of uniqueColumns.values()) {
229
+ const tsType = mapDatabaseTypeToTypeScript(col.data_type, col.is_nullable === "YES", activeDriver);
230
+ generatedTypes += ` ${col.column_name}: ${tsType};
197
231
  `;
232
+ }
233
+ generatedTypes += "}\n\n";
234
+ } catch (error) {
235
+ console.error(`Failed to process table ${tableName}:`, error);
236
+ console.log(`Skipping table ${tableName} and continuing...`);
237
+ continue;
238
+ }
198
239
  }
199
- generatedTypes += "}\n\n";
200
- }
201
- generatedTypes += "export interface Database {\n";
202
- for (const table of tables.rows) {
203
- const tableName = table.table_name.charAt(0).toUpperCase() + table.table_name.slice(1);
204
- generatedTypes += ` ${tableName}: ${tableName};
240
+ generatedTypes += "export interface Database {\n";
241
+ for (const table of tables.rows) {
242
+ const tableName = table.table_name.charAt(0).toUpperCase() + table.table_name.slice(1);
243
+ generatedTypes += ` ${tableName}: ${tableName};
205
244
  `;
245
+ }
246
+ generatedTypes += "}\n\n";
247
+ await fs.writeFile("db-types.ts", generatedTypes, "utf8");
248
+ console.log("TypeScript types written to db-types.ts");
249
+ console.log(`Successfully processed ${processedTables} tables`);
250
+ } catch (error) {
251
+ console.error("Fatal error during database inspection:", error);
252
+ process.exit(1);
206
253
  }
207
- generatedTypes += "}\n\n";
208
- await fs.writeFile("db-types.ts", generatedTypes, "utf8");
209
- console.log("TypeScript types written to db-types.ts");
210
254
  process.exit(0);
211
255
  };
212
256
 
213
- class DriftSQLClient {
257
+ class PostgresDriver {
258
+ constructor(options) {
259
+ this.options = options;
260
+ this.client = postgres(this.options.connectionString);
261
+ }
214
262
  client;
215
- pool;
263
+ async query(query, params) {
264
+ try {
265
+ const result = await this.client.unsafe(query, params);
266
+ return {
267
+ rows: result,
268
+ rowCount: result.length,
269
+ command: void 0
270
+ };
271
+ } catch (error) {
272
+ console.error("Postgres query error:", error);
273
+ throw error;
274
+ }
275
+ }
276
+ async close() {
277
+ try {
278
+ await this.client.end();
279
+ } catch (error) {
280
+ console.error("Error closing Postgres client:", error);
281
+ throw error;
282
+ }
283
+ }
284
+ }
285
+
286
+ class DriftSQLClient {
287
+ postgres;
216
288
  mysqlClient;
217
289
  libsqlClient;
218
290
  postgresClient;
219
291
  drivers;
220
292
  constructor(options) {
221
- this.client = ky.create({
222
- prefixUrl: options.drivers?.postgresHTTP.url || options.url || "http://localhost:3000",
223
- headers: {
224
- Authorization: `Bearer ${options.drivers?.postgresHTTP?.password || options.password || ""}`
225
- },
226
- timeout: options.options?.defaultTimeout || 5e3,
227
- hooks: {
228
- afterResponse: [
229
- async (request, options2, response) => {
230
- if (!response.ok) {
231
- const errorText = await response.text();
232
- throw new Error(`HTTP Error: ${response.status} - ${errorText}`);
233
- }
234
- return response;
235
- }
236
- ]
237
- }
238
- });
239
- this.pool = options.drivers?.postgres ? new Pool(options.drivers.postgres) : void 0;
293
+ this.postgres = options.drivers?.postgres ? new PostgresDriver({ connectionString: options.drivers.postgres.connectionString }) : void 0;
240
294
  this.libsqlClient = options.drivers?.libsql ? createClient(options.drivers.libsql) : void 0;
241
295
  this.mysqlClient = options.drivers?.mysql ? mysql.createConnection(options.drivers.mysql) : void 0;
242
296
  this.drivers = options.drivers || {};
@@ -262,22 +316,22 @@ class DriftSQLClient {
262
316
  async query(query, args) {
263
317
  const driversCount = Object.keys(this.drivers).filter((key) => this.drivers[key] !== void 0).length;
264
318
  if (driversCount > 1) {
265
- const error = new Error("Multiple drivers are configured. Please use only one driver at a time.");
266
- consola.error(error);
267
- throw error;
319
+ const error2 = new Error("Multiple drivers are configured. Please use only one driver at a time.");
320
+ consola.error(error2);
321
+ throw error2;
268
322
  }
269
- if (this.pool) {
323
+ if (this.postgres) {
270
324
  try {
271
- await this.pool.connect();
272
- const result = await this.pool.query(query, args || []);
325
+ const result = await this.postgres.query(query, args || []);
273
326
  return {
274
327
  rows: result.rows,
275
328
  rowCount: result.rowCount || 0,
276
329
  command: result.command,
277
- fields: result.fields
330
+ fields: result.fields?.map((field) => ({ name: field.name, dataTypeID: field.dataTypeID })) || []
278
331
  };
279
- } catch (error) {
280
- consola.error("Failed to connect to PostgreSQL pool:", error);
332
+ } catch (error2) {
333
+ consola.error("Failed to execute query with PostgreSQL:", error2);
334
+ throw error2;
281
335
  }
282
336
  }
283
337
  if (this.mysqlClient) {
@@ -292,25 +346,9 @@ class DriftSQLClient {
292
346
  // MySQL does not return command info
293
347
  fields: fields.map((field) => ({ name: field.name, dataTypeID: field.columnType }))
294
348
  };
295
- } catch (error) {
296
- consola.error("Failed to execute query with MySQL:", error);
297
- throw error;
298
- }
299
- }
300
- if (this.postgresClient) {
301
- try {
302
- const result = await this.postgresClient.unsafe(query, args || []);
303
- return {
304
- // @ts-ignore - postgres library returns rows directly
305
- rows: result,
306
- rowCount: result.length,
307
- command: void 0,
308
- fields: []
309
- // postgres library does not provide field info
310
- };
311
- } catch (error) {
312
- consola.error("Failed to execute query with postgres:", error);
313
- throw error;
349
+ } catch (error2) {
350
+ consola.error("Failed to execute query with MySQL:", error2);
351
+ throw error2;
314
352
  }
315
353
  }
316
354
  if (this.libsqlClient) {
@@ -320,29 +358,14 @@ class DriftSQLClient {
320
358
  args: args || []
321
359
  });
322
360
  return this.convertLibsqlResult(result);
323
- } catch (error) {
324
- consola.error("Failed to execute query with libsql:", error);
325
- throw error;
361
+ } catch (error2) {
362
+ consola.error("Failed to execute query with libsql:", error2);
363
+ throw error2;
326
364
  }
327
365
  }
328
- try {
329
- const response = await this.client.post("query", {
330
- json: { query, args: args || [] }
331
- });
332
- return response.json();
333
- } catch (error) {
334
- if (error instanceof Error) {
335
- throw error;
336
- }
337
- throw new Error(`Query failed: ${JSON.stringify(error)}`);
338
- }
339
- }
340
- async status() {
341
- if (!this.client) {
342
- throw new Error("HTTP client is not configured");
343
- }
344
- const response = await this.client.get("status");
345
- return response.json();
366
+ const error = new Error("No database driver is configured or all drivers failed to execute the query");
367
+ consola.error(error);
368
+ throw error;
346
369
  }
347
370
  async findFirst(table, where) {
348
371
  const tableName = String(table);
@@ -424,8 +447,8 @@ class DriftSQLClient {
424
447
  return this.delete(table, where);
425
448
  }
426
449
  async close() {
427
- if (this.pool) {
428
- await this.pool.end();
450
+ if (this.postgres) {
451
+ await this.postgres.close();
429
452
  }
430
453
  if (this.libsqlClient) {
431
454
  this.libsqlClient.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "driftsql",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "author": "lasse vestergaard",
5
5
  "description": "A lightweight SQL client for TypeScript, supporting multiple databases like PostgreSQL, MySQL, and LibSQL.",
6
6
  "repository": "lassejlv/driftsql",