driftsql 1.0.13 → 1.0.14

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
@@ -81,6 +81,33 @@ declare class SqliteDriver implements DatabaseDriver, TransactionCapable, Prepar
81
81
  pragma(pragma: string): any;
82
82
  }
83
83
 
84
+ interface InspectOptions {
85
+ driver: DatabaseDriver;
86
+ outputFile?: string;
87
+ }
88
+ declare const inspectDB: (options: InspectOptions) => Promise<void>;
89
+ declare const inspectPostgres: (config: {
90
+ connectionString?: string;
91
+ experimental?: {
92
+ http?: {
93
+ url: string;
94
+ apiKey?: string;
95
+ };
96
+ };
97
+ }, outputFile?: string) => Promise<void>;
98
+ declare const inspectLibSQL: (config: {
99
+ url: string;
100
+ authToken?: string;
101
+ useTursoServerlessDriver?: boolean;
102
+ }, outputFile?: string) => Promise<void>;
103
+ declare const inspectMySQL: (config: {
104
+ connectionString: string;
105
+ }, outputFile?: string) => Promise<void>;
106
+ declare const inspectSQLite: (config: {
107
+ filename: string;
108
+ readonly?: boolean;
109
+ }, outputFile?: string) => Promise<void>;
110
+
84
111
  interface ClientOptions<T extends DatabaseDriver = DatabaseDriver> {
85
112
  driver: T;
86
113
  fallbackDrivers?: DatabaseDriver[];
@@ -129,5 +156,5 @@ declare function createSqliteClient<DT = any>(config: {
129
156
  }): SQLClient<DT>;
130
157
  declare const DriftSQLClient: typeof SQLClient;
131
158
 
132
- export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient };
159
+ export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient, inspectDB, inspectLibSQL, inspectMySQL, inspectPostgres, inspectSQLite };
133
160
  export type { ClientOptions, DatabaseDriver, QueryResult };
package/dist/index.d.ts CHANGED
@@ -81,6 +81,33 @@ declare class SqliteDriver implements DatabaseDriver, TransactionCapable, Prepar
81
81
  pragma(pragma: string): any;
82
82
  }
83
83
 
84
+ interface InspectOptions {
85
+ driver: DatabaseDriver;
86
+ outputFile?: string;
87
+ }
88
+ declare const inspectDB: (options: InspectOptions) => Promise<void>;
89
+ declare const inspectPostgres: (config: {
90
+ connectionString?: string;
91
+ experimental?: {
92
+ http?: {
93
+ url: string;
94
+ apiKey?: string;
95
+ };
96
+ };
97
+ }, outputFile?: string) => Promise<void>;
98
+ declare const inspectLibSQL: (config: {
99
+ url: string;
100
+ authToken?: string;
101
+ useTursoServerlessDriver?: boolean;
102
+ }, outputFile?: string) => Promise<void>;
103
+ declare const inspectMySQL: (config: {
104
+ connectionString: string;
105
+ }, outputFile?: string) => Promise<void>;
106
+ declare const inspectSQLite: (config: {
107
+ filename: string;
108
+ readonly?: boolean;
109
+ }, outputFile?: string) => Promise<void>;
110
+
84
111
  interface ClientOptions<T extends DatabaseDriver = DatabaseDriver> {
85
112
  driver: T;
86
113
  fallbackDrivers?: DatabaseDriver[];
@@ -129,5 +156,5 @@ declare function createSqliteClient<DT = any>(config: {
129
156
  }): SQLClient<DT>;
130
157
  declare const DriftSQLClient: typeof SQLClient;
131
158
 
132
- export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient };
159
+ export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient, inspectDB, inspectLibSQL, inspectMySQL, inspectPostgres, inspectSQLite };
133
160
  export type { ClientOptions, DatabaseDriver, QueryResult };
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ 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
7
  import Database from 'better-sqlite3';
8
+ import fs from 'node:fs/promises';
8
9
 
9
10
  function hasTransactionSupport(driver) {
10
11
  return "transaction" in driver && typeof driver.transaction === "function";
@@ -311,6 +312,272 @@ class SqlitePreparedStatement {
311
312
  }
312
313
  }
313
314
 
315
+ const withTimeout = (promise, timeoutMs = 3e4) => {
316
+ return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error(`Query timeout after ${timeoutMs}ms`)), timeoutMs))]);
317
+ };
318
+ const retryQuery = async (queryFn, maxRetries = 3, baseDelay = 1e3) => {
319
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
320
+ try {
321
+ return await queryFn();
322
+ } catch (error) {
323
+ if (attempt === maxRetries) {
324
+ throw error;
325
+ }
326
+ const delay = baseDelay * Math.pow(2, attempt - 1);
327
+ console.warn(`Query attempt ${attempt} failed, retrying in ${delay}ms...`, error);
328
+ await new Promise((resolve) => setTimeout(resolve, delay));
329
+ }
330
+ }
331
+ throw new Error("Max retries exceeded");
332
+ };
333
+ const mapDatabaseTypeToTypeScript = (dataType, isNullable = false, driverType = "postgres") => {
334
+ const nullable = isNullable ? " | null" : "";
335
+ const lowerType = dataType.toLowerCase();
336
+ switch (lowerType) {
337
+ case "uuid": {
338
+ return `string${nullable}`;
339
+ }
340
+ case "character varying":
341
+ case "varchar":
342
+ case "text":
343
+ case "char":
344
+ case "character":
345
+ case "longtext":
346
+ case "mediumtext":
347
+ case "tinytext": {
348
+ return `string${nullable}`;
349
+ }
350
+ case "integer":
351
+ case "int":
352
+ case "int4":
353
+ case "smallint":
354
+ case "int2":
355
+ case "bigint":
356
+ case "int8":
357
+ case "serial":
358
+ case "bigserial":
359
+ case "numeric":
360
+ case "decimal":
361
+ case "real":
362
+ case "float4":
363
+ case "double precision":
364
+ case "float8":
365
+ case "tinyint":
366
+ case "mediumint":
367
+ case "float":
368
+ case "double": {
369
+ return `number${nullable}`;
370
+ }
371
+ case "boolean":
372
+ case "bool":
373
+ case "bit": {
374
+ return `boolean${nullable}`;
375
+ }
376
+ case "timestamp":
377
+ case "timestamp with time zone":
378
+ case "timestamp without time zone":
379
+ case "timestamptz":
380
+ case "date":
381
+ case "time":
382
+ case "time with time zone":
383
+ case "time without time zone":
384
+ case "timetz":
385
+ case "interval":
386
+ case "datetime":
387
+ case "year": {
388
+ return `Date${nullable}`;
389
+ }
390
+ case "json":
391
+ case "jsonb": {
392
+ return `any${nullable}`;
393
+ }
394
+ case "array": {
395
+ return `any[]${nullable}`;
396
+ }
397
+ case "bytea":
398
+ case "binary":
399
+ case "varbinary":
400
+ case "blob":
401
+ case "longblob":
402
+ case "mediumblob":
403
+ case "tinyblob": {
404
+ return `Buffer${nullable}`;
405
+ }
406
+ case "enum":
407
+ case "set": {
408
+ return `string${nullable}`;
409
+ }
410
+ default: {
411
+ console.warn(`Unknown ${driverType} type: ${dataType}, defaulting to 'any'`);
412
+ return `any${nullable}`;
413
+ }
414
+ }
415
+ };
416
+ const getDriverType = (driver) => {
417
+ if (driver instanceof PostgresDriver) return "postgres";
418
+ if (driver instanceof LibSQLDriver) return "libsql";
419
+ if (driver instanceof MySQLDriver) return "mysql";
420
+ if (driver instanceof SqliteDriver) return "sqlite";
421
+ return "unknown";
422
+ };
423
+ const inspectDB = async (options) => {
424
+ const { driver, outputFile = "db-types.ts" } = options;
425
+ const driverType = getDriverType(driver);
426
+ console.log(`Inspecting database using ${driverType} driver`);
427
+ const client = new SQLClient({ driver });
428
+ let generatedTypes = "";
429
+ try {
430
+ let tablesQuery;
431
+ let tableSchemaFilter;
432
+ if (driverType === "mysql") {
433
+ const dbResult = await withTimeout(
434
+ retryQuery(() => client.query("SELECT DATABASE() as `database`", [])),
435
+ 1e4
436
+ );
437
+ const currentDatabase = dbResult.rows[0]?.database;
438
+ if (!currentDatabase) {
439
+ throw new Error("Could not determine current MySQL database name");
440
+ }
441
+ console.log(`Using MySQL database: ${currentDatabase}`);
442
+ tablesQuery = `SELECT TABLE_NAME as table_name
443
+ FROM information_schema.tables
444
+ WHERE TABLE_SCHEMA = ?
445
+ AND TABLE_TYPE = 'BASE TABLE'
446
+ ORDER BY TABLE_NAME`;
447
+ tableSchemaFilter = currentDatabase;
448
+ } else if (driverType === "postgres") {
449
+ tablesQuery = `SELECT table_name
450
+ FROM information_schema.tables
451
+ WHERE table_schema = $1
452
+ AND table_type = 'BASE TABLE'
453
+ ORDER BY table_name`;
454
+ tableSchemaFilter = "public";
455
+ } else if (driverType === "libsql" || driverType === "sqlite") {
456
+ tablesQuery = `SELECT name as table_name
457
+ FROM sqlite_master
458
+ WHERE type = 'table'
459
+ ORDER BY name`;
460
+ tableSchemaFilter = void 0;
461
+ } else {
462
+ throw new Error(`Unsupported driver type: ${driverType}`);
463
+ }
464
+ const tables = await withTimeout(
465
+ retryQuery(() => client.query(tablesQuery, tableSchemaFilter ? [tableSchemaFilter] : [])),
466
+ 3e4
467
+ );
468
+ console.log("Tables in the database:", tables.rows.map((t) => t.table_name).join(", "));
469
+ let processedTables = 0;
470
+ const totalTables = tables.rows.length;
471
+ for (const table of tables.rows) {
472
+ const tableName = table.table_name;
473
+ processedTables++;
474
+ console.log(`[${processedTables}/${totalTables}] Inspecting table: ${tableName}`);
475
+ try {
476
+ let columnsQuery;
477
+ let queryParams;
478
+ if (driverType === "mysql") {
479
+ columnsQuery = `
480
+ SELECT
481
+ COLUMN_NAME as column_name,
482
+ DATA_TYPE as data_type,
483
+ IS_NULLABLE as is_nullable,
484
+ COLUMN_DEFAULT as column_default
485
+ FROM information_schema.columns
486
+ WHERE TABLE_NAME = ?
487
+ AND TABLE_SCHEMA = ?
488
+ ORDER BY ORDINAL_POSITION
489
+ `;
490
+ queryParams = [tableName, tableSchemaFilter];
491
+ } else if (driverType === "postgres") {
492
+ columnsQuery = `
493
+ SELECT
494
+ column_name,
495
+ data_type,
496
+ is_nullable,
497
+ column_default
498
+ FROM information_schema.columns
499
+ WHERE table_name = $1
500
+ AND table_schema = $2
501
+ ORDER BY ordinal_position
502
+ `;
503
+ queryParams = [tableName, tableSchemaFilter];
504
+ } else {
505
+ columnsQuery = `
506
+ SELECT
507
+ name as column_name,
508
+ type as data_type,
509
+ CASE WHEN "notnull" = 0 THEN 'YES' ELSE 'NO' END as is_nullable,
510
+ dflt_value as column_default
511
+ FROM pragma_table_info(?)
512
+ ORDER BY cid
513
+ `;
514
+ queryParams = [tableName];
515
+ }
516
+ const columns = await withTimeout(
517
+ retryQuery(
518
+ () => client.query(columnsQuery, queryParams)
519
+ ),
520
+ 15e3
521
+ );
522
+ if (columns.rows.length === 0) {
523
+ console.log(`No columns found for table: ${tableName}`);
524
+ continue;
525
+ }
526
+ console.log(`Columns in ${tableName}:`, columns.rows.map((c) => `${c.column_name} (${c.data_type}${c.is_nullable === "YES" ? ", nullable" : ""})`).join(", "));
527
+ const uniqueColumns = /* @__PURE__ */ new Map();
528
+ columns.rows.forEach((col) => {
529
+ if (!uniqueColumns.has(col.column_name)) {
530
+ uniqueColumns.set(col.column_name, col);
531
+ }
532
+ });
533
+ generatedTypes += `export interface ${tableName.charAt(0).toUpperCase() + tableName.slice(1)} {
534
+ `;
535
+ for (const col of uniqueColumns.values()) {
536
+ const tsType = mapDatabaseTypeToTypeScript(col.data_type, col.is_nullable === "YES", driverType);
537
+ generatedTypes += ` ${col.column_name}: ${tsType};
538
+ `;
539
+ }
540
+ generatedTypes += "}\n\n";
541
+ } catch (error) {
542
+ console.error(`Failed to process table ${tableName}:`, error);
543
+ console.log(`Skipping table ${tableName} and continuing...`);
544
+ continue;
545
+ }
546
+ }
547
+ generatedTypes += "export interface Database {\n";
548
+ for (const table of tables.rows) {
549
+ const interfaceName = table.table_name.charAt(0).toUpperCase() + table.table_name.slice(1);
550
+ generatedTypes += ` ${table.table_name}: ${interfaceName};
551
+ `;
552
+ }
553
+ generatedTypes += "}\n\n";
554
+ await fs.writeFile(outputFile, generatedTypes, "utf8");
555
+ console.log(`TypeScript types written to ${outputFile}`);
556
+ console.log(`Successfully processed ${processedTables} tables`);
557
+ } catch (error) {
558
+ console.error("Fatal error during database inspection:", error);
559
+ throw error;
560
+ } finally {
561
+ await client.close();
562
+ }
563
+ };
564
+ const inspectPostgres = async (config, outputFile) => {
565
+ const driver = new PostgresDriver(config);
566
+ return inspectDB({ driver, outputFile });
567
+ };
568
+ const inspectLibSQL = async (config, outputFile) => {
569
+ const driver = new LibSQLDriver(config);
570
+ return inspectDB({ driver, outputFile });
571
+ };
572
+ const inspectMySQL = async (config, outputFile) => {
573
+ const driver = new MySQLDriver(config);
574
+ return inspectDB({ driver, outputFile });
575
+ };
576
+ const inspectSQLite = async (config, outputFile) => {
577
+ const driver = new SqliteDriver(config);
578
+ return inspectDB({ driver, outputFile });
579
+ };
580
+
314
581
  class SQLClient {
315
582
  primaryDriver;
316
583
  fallbackDrivers;
@@ -465,4 +732,4 @@ function createSqliteClient(config) {
465
732
  }
466
733
  const DriftSQLClient = SQLClient;
467
734
 
468
- export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient };
735
+ export { DriftSQLClient, LibSQLDriver, MySQLDriver, PostgresDriver, SQLClient, SqliteDriver, createLibSQLClient, createMySQLClient, createPostgresClient, createSqliteClient, inspectDB, inspectLibSQL, inspectMySQL, inspectPostgres, inspectSQLite };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "driftsql",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "author": "lasse vestergaard",
5
5
  "description": "A lightweight SQL client for TypeScript",
6
6
  "repository": "lassejlv/driftsql",