driftsql 1.0.19 → 1.0.21

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/src/pull.ts DELETED
@@ -1,318 +0,0 @@
1
- import { PostgresDriver, LibSQLDriver, MySQLDriver, SqliteDriver, SQLClient } from '.'
2
- import type { DatabaseDriver } from './types'
3
- import fs from 'node:fs/promises'
4
-
5
- interface InspectOptions {
6
- driver: DatabaseDriver
7
- outputFile?: string
8
- }
9
-
10
- // Helper function to add timeout to promises
11
- const withTimeout = <T>(promise: Promise<T>, timeoutMs = 30_000): Promise<T> => {
12
- return Promise.race([promise, new Promise<never>((_, reject) => setTimeout(() => reject(new Error(`Query timeout after ${timeoutMs}ms`)), timeoutMs))])
13
- }
14
-
15
- // Helper function to retry queries with exponential backoff
16
- const retryQuery = async <T>(queryFn: () => Promise<T>, maxRetries = 3, baseDelay = 1000): Promise<T> => {
17
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
18
- try {
19
- return await queryFn()
20
- } catch (error) {
21
- if (attempt === maxRetries) {
22
- throw error
23
- }
24
- const delay = baseDelay * Math.pow(2, attempt - 1)
25
- console.warn(`Query attempt ${attempt} failed, retrying in ${delay}ms...`, error)
26
- await new Promise((resolve) => setTimeout(resolve, delay))
27
- }
28
- }
29
- throw new Error('Max retries exceeded')
30
- }
31
-
32
- const mapDatabaseTypeToTypeScript = (dataType: string, isNullable: boolean = false, driverType: string = 'postgres'): string => {
33
- const nullable = isNullable ? ' | null' : ''
34
- const lowerType = dataType.toLowerCase()
35
-
36
- // Common types that work for all databases
37
- switch (lowerType) {
38
- case 'uuid': {
39
- return `string${nullable}`
40
- }
41
- case 'character varying':
42
- case 'varchar':
43
- case 'text':
44
- case 'char':
45
- case 'character':
46
- case 'longtext':
47
- case 'mediumtext':
48
- case 'tinytext': {
49
- return `string${nullable}`
50
- }
51
- case 'integer':
52
- case 'int':
53
- case 'int4':
54
- case 'smallint':
55
- case 'int2':
56
- case 'bigint':
57
- case 'int8':
58
- case 'serial':
59
- case 'bigserial':
60
- case 'numeric':
61
- case 'decimal':
62
- case 'real':
63
- case 'float4':
64
- case 'double precision':
65
- case 'float8':
66
- case 'tinyint':
67
- case 'mediumint':
68
- case 'float':
69
- case 'double': {
70
- return `number${nullable}`
71
- }
72
- case 'boolean':
73
- case 'bool':
74
- case 'bit': {
75
- return `boolean${nullable}`
76
- }
77
- case 'timestamp':
78
- case 'timestamp with time zone':
79
- case 'timestamp without time zone':
80
- case 'timestamptz':
81
- case 'date':
82
- case 'time':
83
- case 'time with time zone':
84
- case 'time without time zone':
85
- case 'timetz':
86
- case 'interval':
87
- case 'datetime':
88
- case 'year': {
89
- return `Date${nullable}`
90
- }
91
- case 'json':
92
- case 'jsonb': {
93
- return `any${nullable}`
94
- }
95
- case 'array': {
96
- return `any[]${nullable}`
97
- }
98
- case 'bytea':
99
- case 'binary':
100
- case 'varbinary':
101
- case 'blob':
102
- case 'longblob':
103
- case 'mediumblob':
104
- case 'tinyblob': {
105
- return `Buffer${nullable}`
106
- }
107
- case 'enum':
108
- case 'set': {
109
- return `string${nullable}`
110
- }
111
- default: {
112
- console.warn(`Unknown ${driverType} type: ${dataType}, defaulting to 'any'`)
113
- return `any${nullable}`
114
- }
115
- }
116
- }
117
-
118
- const getDriverType = (driver: DatabaseDriver): string => {
119
- if (driver instanceof PostgresDriver) return 'postgres'
120
- if (driver instanceof LibSQLDriver) return 'libsql'
121
- if (driver instanceof MySQLDriver) return 'mysql'
122
- if (driver instanceof SqliteDriver) return 'sqlite'
123
- return 'unknown'
124
- }
125
-
126
- export const inspectDB = async (options: InspectOptions) => {
127
- const { driver, outputFile = 'db-types.ts' } = options
128
- const driverType = getDriverType(driver)
129
-
130
- console.log(`Inspecting database using ${driverType} driver`)
131
-
132
- const client = new SQLClient({ driver })
133
- let generatedTypes = ''
134
-
135
- try {
136
- // Use different queries based on the driver type
137
- let tablesQuery: string
138
- let tableSchemaFilter: string | undefined
139
-
140
- if (driverType === 'mysql') {
141
- // For MySQL, get the current database name first
142
- const dbResult = await withTimeout(
143
- retryQuery(() => client.query<{ database: string }>('SELECT DATABASE() as `database`', [])),
144
- 10_000,
145
- )
146
- const currentDatabase = dbResult.rows[0]?.database
147
-
148
- if (!currentDatabase) {
149
- throw new Error('Could not determine current MySQL database name')
150
- }
151
-
152
- console.log(`Using MySQL database: ${currentDatabase}`)
153
- tablesQuery = `SELECT TABLE_NAME as table_name
154
- FROM information_schema.tables
155
- WHERE TABLE_SCHEMA = ?
156
- AND TABLE_TYPE = 'BASE TABLE'
157
- ORDER BY TABLE_NAME`
158
- tableSchemaFilter = currentDatabase
159
- } else if (driverType === 'postgres') {
160
- // PostgreSQL
161
- tablesQuery = `SELECT table_name
162
- FROM information_schema.tables
163
- WHERE table_schema = $1
164
- AND table_type = 'BASE TABLE'
165
- ORDER BY table_name`
166
- tableSchemaFilter = 'public'
167
- } else if (driverType === 'libsql' || driverType === 'sqlite') {
168
- // LibSQL/SQLite
169
- tablesQuery = `SELECT name as table_name
170
- FROM sqlite_master
171
- WHERE type = 'table'
172
- ORDER BY name`
173
- tableSchemaFilter = undefined
174
- } else {
175
- throw new Error(`Unsupported driver type: ${driverType}`)
176
- }
177
-
178
- const tables = await withTimeout(
179
- retryQuery(() => client.query<{ table_name: string }>(tablesQuery, tableSchemaFilter ? [tableSchemaFilter] : [])),
180
- 30_000,
181
- )
182
-
183
- console.log('Tables in the database:', tables.rows.map((t) => t.table_name).join(', '))
184
-
185
- let processedTables = 0
186
- const totalTables = tables.rows.length
187
-
188
- for (const table of tables.rows) {
189
- const tableName = table.table_name
190
- processedTables++
191
- console.log(`[${processedTables}/${totalTables}] Inspecting table: ${tableName}`)
192
-
193
- try {
194
- // Get columns with nullability information
195
- let columnsQuery: string
196
- let queryParams: (string | null)[]
197
-
198
- if (driverType === 'mysql') {
199
- columnsQuery = `
200
- SELECT
201
- COLUMN_NAME as column_name,
202
- DATA_TYPE as data_type,
203
- IS_NULLABLE as is_nullable,
204
- COLUMN_DEFAULT as column_default
205
- FROM information_schema.columns
206
- WHERE TABLE_NAME = ?
207
- AND TABLE_SCHEMA = ?
208
- ORDER BY ORDINAL_POSITION
209
- `
210
- queryParams = [tableName, tableSchemaFilter!]
211
- } else if (driverType === 'postgres') {
212
- columnsQuery = `
213
- SELECT
214
- column_name,
215
- data_type,
216
- is_nullable,
217
- column_default
218
- FROM information_schema.columns
219
- WHERE table_name = $1
220
- AND table_schema = $2
221
- ORDER BY ordinal_position
222
- `
223
- queryParams = [tableName, tableSchemaFilter!]
224
- } else {
225
- // LibSQL/SQLite
226
- columnsQuery = `
227
- SELECT
228
- name as column_name,
229
- type as data_type,
230
- CASE WHEN "notnull" = 0 THEN 'YES' ELSE 'NO' END as is_nullable,
231
- dflt_value as column_default
232
- FROM pragma_table_info(?)
233
- ORDER BY cid
234
- `
235
- queryParams = [tableName]
236
- }
237
-
238
- const columns = await withTimeout(
239
- retryQuery(() =>
240
- client.query<{
241
- column_name: string
242
- data_type: string
243
- is_nullable: string
244
- column_default: string | null
245
- }>(columnsQuery, queryParams),
246
- ),
247
- 15_000,
248
- )
249
-
250
- if (columns.rows.length === 0) {
251
- console.log(`No columns found for table: ${tableName}`)
252
- continue
253
- }
254
-
255
- console.log(`Columns in ${tableName}:`, columns.rows.map((c) => `${c.column_name} (${c.data_type}${c.is_nullable === 'YES' ? ', nullable' : ''})`).join(', '))
256
-
257
- // Deduplicate columns by name
258
- const uniqueColumns = new Map<string, (typeof columns.rows)[0]>()
259
- columns.rows.forEach((col) => {
260
- if (!uniqueColumns.has(col.column_name)) {
261
- uniqueColumns.set(col.column_name, col)
262
- }
263
- })
264
-
265
- generatedTypes += `export interface ${tableName.charAt(0).toUpperCase() + tableName.slice(1)} {\n`
266
- for (const col of uniqueColumns.values()) {
267
- const tsType = mapDatabaseTypeToTypeScript(col.data_type, col.is_nullable === 'YES', driverType)
268
- generatedTypes += ` ${col.column_name}: ${tsType};\n`
269
- }
270
- generatedTypes += '}\n\n'
271
- } catch (error) {
272
- console.error(`Failed to process table ${tableName}:`, error)
273
- console.log(`Skipping table ${tableName} and continuing...`)
274
- continue
275
- }
276
- }
277
-
278
- // Generate the Database interface
279
- generatedTypes += 'export interface Database {\n'
280
- for (const table of tables.rows) {
281
- const interfaceName = table.table_name.charAt(0).toUpperCase() + table.table_name.slice(1)
282
- generatedTypes += ` ${table.table_name}: ${interfaceName};\n`
283
- }
284
- generatedTypes += '}\n\n'
285
-
286
- await fs.writeFile(outputFile, generatedTypes, 'utf8')
287
- console.log(`TypeScript types written to ${outputFile}`)
288
- console.log(`Successfully processed ${processedTables} tables`)
289
- } catch (error) {
290
- console.error('Fatal error during database inspection:', error)
291
- throw error
292
- } finally {
293
- await client.close()
294
- }
295
- }
296
-
297
- // Convenience functions for each driver type
298
- export const inspectPostgres = async (config: { connectionString?: string; experimental?: { http?: { url: string; apiKey?: string } } }, outputFile?: string) => {
299
- const driver = new PostgresDriver(config)
300
- return inspectDB({ driver, outputFile })
301
- }
302
-
303
- export const inspectLibSQL = async (config: { url: string; authToken?: string; useTursoServerlessDriver?: boolean }, outputFile?: string) => {
304
- const driver = new LibSQLDriver(config)
305
- return inspectDB({ driver, outputFile })
306
- }
307
-
308
- export const inspectMySQL = async (config: { connectionString: string }, outputFile?: string) => {
309
- const driver = new MySQLDriver(config)
310
- return inspectDB({ driver, outputFile })
311
- }
312
-
313
- export const inspectSQLite = async (config: { filename: string; readonly?: boolean }, outputFile?: string) => {
314
- const driver = new SqliteDriver(config)
315
- return inspectDB({ driver, outputFile })
316
- }
317
-
318
- export default inspectDB
package/src/types.ts DELETED
@@ -1,73 +0,0 @@
1
- // Core result type for all database operations
2
- export interface QueryResult<T = any> {
3
- rows: T[]
4
- rowCount: number
5
- command?: string
6
- fields?: QueryField[]
7
- }
8
-
9
- // Field information for query results
10
- export interface QueryField {
11
- name: string
12
- dataTypeID: number
13
- }
14
-
15
- // Base driver interface that all drivers must implement
16
- export interface DatabaseDriver {
17
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>
18
- close(): Promise<void>
19
- }
20
-
21
- // Optional interfaces for advanced features
22
- export interface TransactionCapable {
23
- transaction<T>(callback: (driver: DatabaseDriver) => Promise<T>): Promise<T>
24
- }
25
-
26
- export interface PreparedStatementCapable {
27
- prepare(sql: string): Promise<PreparedStatement>
28
- }
29
-
30
- export interface PreparedStatement {
31
- execute<T = any>(params?: any[]): Promise<QueryResult<T>>
32
- finalize(): Promise<void>
33
- }
34
-
35
- // Type guards
36
- export function hasTransactionSupport(driver: DatabaseDriver): driver is DatabaseDriver & TransactionCapable {
37
- return 'transaction' in driver && typeof (driver as any).transaction === 'function'
38
- }
39
-
40
- export function hasPreparedStatementSupport(driver: DatabaseDriver): driver is DatabaseDriver & PreparedStatementCapable {
41
- return 'prepare' in driver && typeof (driver as any).prepare === 'function'
42
- }
43
-
44
- // Error classes
45
- export class DatabaseError extends Error {
46
- constructor(
47
- message: string,
48
- public readonly driverType: string,
49
- public readonly originalError?: Error,
50
- ) {
51
- super(message)
52
- this.name = 'DatabaseError'
53
- }
54
- }
55
-
56
- export class QueryError extends DatabaseError {
57
- constructor(driverType: string, sql: string, originalError?: Error) {
58
- super(`Query failed: ${sql}`, driverType, originalError)
59
- this.name = 'QueryError'
60
- }
61
- }
62
-
63
- export class ConnectionError extends DatabaseError {
64
- constructor(driverType: string, originalError?: Error) {
65
- super(`Failed to connect to ${driverType}`, driverType, originalError)
66
- this.name = 'ConnectionError'
67
- }
68
- }
69
-
70
- // Generic driver options
71
- export interface DriverOptions {
72
- [key: string]: any
73
- }