spindb 0.9.1 → 0.9.2

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.
Files changed (44) hide show
  1. package/cli/commands/backup.ts +13 -11
  2. package/cli/commands/clone.ts +14 -10
  3. package/cli/commands/config.ts +29 -29
  4. package/cli/commands/connect.ts +51 -39
  5. package/cli/commands/create.ts +59 -32
  6. package/cli/commands/delete.ts +8 -8
  7. package/cli/commands/deps.ts +17 -15
  8. package/cli/commands/doctor.ts +11 -11
  9. package/cli/commands/edit.ts +108 -55
  10. package/cli/commands/engines.ts +17 -15
  11. package/cli/commands/info.ts +8 -6
  12. package/cli/commands/list.ts +31 -16
  13. package/cli/commands/logs.ts +15 -11
  14. package/cli/commands/menu/backup-handlers.ts +52 -47
  15. package/cli/commands/menu/container-handlers.ts +120 -78
  16. package/cli/commands/menu/engine-handlers.ts +21 -11
  17. package/cli/commands/menu/index.ts +4 -4
  18. package/cli/commands/menu/shell-handlers.ts +34 -31
  19. package/cli/commands/menu/sql-handlers.ts +22 -16
  20. package/cli/commands/menu/update-handlers.ts +19 -17
  21. package/cli/commands/restore.ts +22 -20
  22. package/cli/commands/run.ts +20 -18
  23. package/cli/commands/self-update.ts +5 -5
  24. package/cli/commands/start.ts +11 -9
  25. package/cli/commands/stop.ts +9 -9
  26. package/cli/commands/url.ts +12 -9
  27. package/cli/helpers.ts +9 -4
  28. package/cli/ui/prompts.ts +12 -5
  29. package/cli/ui/spinner.ts +4 -4
  30. package/cli/ui/theme.ts +4 -4
  31. package/core/binary-manager.ts +5 -1
  32. package/core/container-manager.ts +5 -5
  33. package/core/platform-service.ts +3 -3
  34. package/core/start-with-retry.ts +6 -6
  35. package/core/transaction-manager.ts +6 -6
  36. package/engines/mysql/index.ts +11 -11
  37. package/engines/mysql/restore.ts +4 -4
  38. package/engines/mysql/version-validator.ts +2 -2
  39. package/engines/postgresql/binary-manager.ts +17 -17
  40. package/engines/postgresql/index.ts +7 -2
  41. package/engines/postgresql/restore.ts +2 -2
  42. package/engines/postgresql/version-validator.ts +2 -2
  43. package/engines/sqlite/index.ts +21 -8
  44. package/package.json +1 -1
@@ -450,8 +450,8 @@ export class MySQLEngine extends BaseEngine {
450
450
  await this.cleanupPidFile(pidFile)
451
451
  return null
452
452
  }
453
- } catch (err) {
454
- const e = err as NodeJS.ErrnoException
453
+ } catch (error) {
454
+ const e = error as NodeJS.ErrnoException
455
455
  if (e.code !== 'ENOENT') {
456
456
  logWarning(`Failed to read PID file: ${e.message}`, {
457
457
  pidFile,
@@ -479,8 +479,8 @@ export class MySQLEngine extends BaseEngine {
479
479
  `"${mysqladmin}" -h 127.0.0.1 -P ${port} -u root shutdown`,
480
480
  { timeout: 5000 },
481
481
  )
482
- } catch (err) {
483
- const e = err as Error
482
+ } catch (error) {
483
+ const e = error as Error
484
484
  logDebug(`mysqladmin shutdown failed: ${e.message}`)
485
485
  // Continue to wait for process to die or send SIGTERM
486
486
  }
@@ -542,8 +542,8 @@ export class MySQLEngine extends BaseEngine {
542
542
  await this.cleanupPidFile(pidFile)
543
543
  return
544
544
  }
545
- } catch (err) {
546
- const e = err as NodeJS.ErrnoException
545
+ } catch (error) {
546
+ const e = error as NodeJS.ErrnoException
547
547
  if (e.code === 'ESRCH') {
548
548
  // Process already dead
549
549
  await this.cleanupPidFile(pidFile)
@@ -575,9 +575,9 @@ export class MySQLEngine extends BaseEngine {
575
575
  logDebug(`Process ${pid} terminated after SIGKILL`)
576
576
  await this.cleanupPidFile(pidFile)
577
577
  }
578
- } catch (err) {
579
- if (err instanceof SpinDBError) throw err
580
- const e = err as NodeJS.ErrnoException
578
+ } catch (error) {
579
+ if (error instanceof SpinDBError) throw error
580
+ const e = error as NodeJS.ErrnoException
581
581
  if (e.code === 'ESRCH') {
582
582
  // Process already dead
583
583
  await this.cleanupPidFile(pidFile)
@@ -594,8 +594,8 @@ export class MySQLEngine extends BaseEngine {
594
594
  try {
595
595
  await unlink(pidFile)
596
596
  logDebug('PID file cleaned up')
597
- } catch (err) {
598
- const e = err as NodeJS.ErrnoException
597
+ } catch (error) {
598
+ const e = error as NodeJS.ErrnoException
599
599
  if (e.code !== 'ENOENT') {
600
600
  logDebug(`Failed to clean up PID file: ${e.message}`)
601
601
  }
@@ -174,13 +174,13 @@ export async function restoreBackup(
174
174
  if (validateVersion) {
175
175
  try {
176
176
  await validateRestoreCompatibility({ dumpPath: backupPath })
177
- } catch (err) {
177
+ } catch (error) {
178
178
  // Re-throw SpinDBError, log and continue for other errors
179
- if (err instanceof Error && err.name === 'SpinDBError') {
180
- throw err
179
+ if (error instanceof Error && error.name === 'SpinDBError') {
180
+ throw error
181
181
  }
182
182
  logDebug('Version validation failed, proceeding anyway', {
183
- error: err instanceof Error ? err.message : String(err),
183
+ error: error instanceof Error ? error.message : String(error),
184
184
  })
185
185
  }
186
186
  }
@@ -197,10 +197,10 @@ export async function parseDumpVersion(dumpPath: string): Promise<DumpInfo> {
197
197
  }
198
198
 
199
199
  return { version: null, variant }
200
- } catch (err) {
200
+ } catch (error) {
201
201
  logDebug('Failed to parse dump version', {
202
202
  dumpPath,
203
- error: err instanceof Error ? err.message : String(err),
203
+ error: error instanceof Error ? error.message : String(error),
204
204
  })
205
205
  return { version: null, variant: 'unknown' }
206
206
  }
@@ -2,7 +2,7 @@ import { exec } from 'child_process'
2
2
  import { promisify } from 'util'
3
3
  import chalk from 'chalk'
4
4
  import { createSpinner } from '../../cli/ui/spinner'
5
- import { warning, error as themeError, success } from '../../cli/ui/theme'
5
+ import { uiWarning, uiError, uiSuccess } from '../../cli/ui/theme'
6
6
  import {
7
7
  detectPackageManager as detectPM,
8
8
  installEngineDependencies,
@@ -311,7 +311,7 @@ export async function installPostgresBinaries(): Promise<boolean> {
311
311
  const packageManager = await detectPM()
312
312
  if (!packageManager) {
313
313
  spinner.fail('No supported package manager found')
314
- console.log(themeError('Please install PostgreSQL client tools manually:'))
314
+ console.log(uiError('Please install PostgreSQL client tools manually:'))
315
315
 
316
316
  // Show platform-specific instructions from the registry
317
317
  const platform = getCurrentPlatform()
@@ -348,21 +348,21 @@ export async function installPostgresBinaries(): Promise<boolean> {
348
348
 
349
349
  if (allSuccess) {
350
350
  console.log()
351
- console.log(success('PostgreSQL client tools installed successfully'))
351
+ console.log(uiSuccess('PostgreSQL client tools installed successfully'))
352
352
  return true
353
353
  } else {
354
354
  const failed = results.filter((r) => !r.success)
355
355
  console.log()
356
- console.log(themeError('Some installations failed:'))
356
+ console.log(uiError('Some installations failed:'))
357
357
  for (const f of failed) {
358
- console.log(themeError(` ${f.dependency.name}: ${f.error}`))
358
+ console.log(uiError(` ${f.dependency.name}: ${f.error}`))
359
359
  }
360
360
  return false
361
361
  }
362
362
  } catch (error: unknown) {
363
363
  console.log()
364
- console.log(themeError('Failed to install PostgreSQL client tools'))
365
- console.log(warning('Please install manually'))
364
+ console.log(uiError('Failed to install PostgreSQL client tools'))
365
+ console.log(uiWarning('Please install manually'))
366
366
  if (error instanceof Error) {
367
367
  console.log(chalk.gray(`Error details: ${error.message}`))
368
368
  }
@@ -408,7 +408,7 @@ export async function updatePostgresClientTools(): Promise<boolean> {
408
408
 
409
409
  spinner.succeed('PostgreSQL client tools updated')
410
410
  console.log(
411
- success(
411
+ uiSuccess(
412
412
  `Client tools successfully linked to PostgreSQL ${latestMajor}`,
413
413
  ),
414
414
  )
@@ -418,13 +418,13 @@ export async function updatePostgresClientTools(): Promise<boolean> {
418
418
  // For other package managers, use the standard update
419
419
  await execWithTimeout(packageManager.updateCommand('postgresql'), 120000)
420
420
  spinner.succeed('PostgreSQL client tools updated')
421
- console.log(success('Update completed successfully'))
421
+ console.log(uiSuccess('Update completed successfully'))
422
422
  return true
423
423
  }
424
424
  } catch (error: unknown) {
425
425
  spinner.fail('Update failed')
426
- console.log(themeError('Failed to update PostgreSQL client tools'))
427
- console.log(warning('Please update manually:'))
426
+ console.log(uiError('Failed to update PostgreSQL client tools'))
427
+ console.log(uiWarning('Please update manually:'))
428
428
 
429
429
  if (packageManager.name === 'brew') {
430
430
  const olderVersions = ['14', '15', '16'].filter((v) => v !== latestMajor)
@@ -473,12 +473,12 @@ export async function updatePostgresBinaries(): Promise<boolean> {
473
473
  try {
474
474
  await execWithTimeout(packageManager.updateCommand('postgresql'), 120000) // 2 minute timeout
475
475
  updateSpinner.succeed('PostgreSQL client tools updated')
476
- console.log(success('Update completed successfully'))
476
+ console.log(uiSuccess('Update completed successfully'))
477
477
  return true
478
478
  } catch (error: unknown) {
479
479
  updateSpinner.fail('Update failed')
480
- console.log(themeError('Failed to update PostgreSQL client tools'))
481
- console.log(warning('Please update manually:'))
480
+ console.log(uiError('Failed to update PostgreSQL client tools'))
481
+ console.log(uiWarning('Please update manually:'))
482
482
  console.log(` ${packageManager.updateCommand('postgresql')}`)
483
483
  if (error instanceof Error) {
484
484
  console.log(chalk.gray(`Error details: ${error.message}`))
@@ -505,7 +505,7 @@ export async function ensurePostgresBinary(
505
505
  return { success: false, info: null, action: 'install_required' }
506
506
  }
507
507
 
508
- console.log(warning(`${binary} not found on your system`))
508
+ console.log(uiWarning(`${binary} not found on your system`))
509
509
  const success = await installPostgresBinaries()
510
510
  if (!success) {
511
511
  return { success: false, info: null, action: 'install_failed' }
@@ -527,13 +527,13 @@ export async function ensurePostgresBinary(
527
527
  }
528
528
 
529
529
  console.log(
530
- warning(
530
+ uiWarning(
531
531
  `Your ${binary} version (${info.version}) is incompatible with the dump file`,
532
532
  ),
533
533
  )
534
534
  if (info.requiredVersion) {
535
535
  console.log(
536
- warning(`Required version: ${info.requiredVersion} or compatible`),
536
+ uiWarning(`Required version: ${info.requiredVersion} or compatible`),
537
537
  )
538
538
  }
539
539
 
@@ -155,8 +155,13 @@ export class PostgreSQLEngine extends BaseEngine {
155
155
 
156
156
  // Configure max_connections after initdb creates postgresql.conf
157
157
  const maxConnections =
158
- (options.maxConnections as number) || getEngineDefaults('postgresql').maxConnections
159
- await this.setConfigValue(dataDir, 'max_connections', String(maxConnections))
158
+ (options.maxConnections as number) ||
159
+ getEngineDefaults('postgresql').maxConnections
160
+ await this.setConfigValue(
161
+ dataDir,
162
+ 'max_connections',
163
+ String(maxConnections),
164
+ )
160
165
 
161
166
  return dataDir
162
167
  }
@@ -219,8 +219,8 @@ export async function restoreBackup(
219
219
  format: detectedFormat,
220
220
  ...result,
221
221
  }
222
- } catch (err) {
223
- const e = err as Error & { stdout?: string; stderr?: string }
222
+ } catch (error) {
223
+ const e = error as Error & { stdout?: string; stderr?: string }
224
224
  // pg_restore often returns non-zero even on partial success
225
225
  return {
226
226
  format: detectedFormat,
@@ -135,11 +135,11 @@ export async function parseDumpVersion(
135
135
  }
136
136
  }
137
137
  }
138
- } catch (err) {
138
+ } catch (error) {
139
139
  logDebug('Failed to parse dump version', {
140
140
  dumpPath,
141
141
  format,
142
- error: err instanceof Error ? err.message : String(err),
142
+ error: error instanceof Error ? error.message : String(error),
143
143
  })
144
144
  }
145
145
 
@@ -538,15 +538,28 @@ export class SQLiteEngine extends BaseEngine {
538
538
  * Download a file from HTTP/HTTPS URL
539
539
  */
540
540
  private async downloadFile(url: string, destPath: string): Promise<void> {
541
- const response = await fetch(url)
542
- if (!response.ok) {
543
- throw new Error(
544
- `Failed to download: ${response.status} ${response.statusText}`,
545
- )
546
- }
541
+ const controller = new AbortController()
542
+ const timeoutMs = 5 * 60 * 1000 // 5 minutes
543
+ const timeout = setTimeout(() => controller.abort(), timeoutMs)
544
+
545
+ try {
546
+ const response = await fetch(url, { signal: controller.signal })
547
+ if (!response.ok) {
548
+ throw new Error(
549
+ `Failed to download: ${response.status} ${response.statusText}`,
550
+ )
551
+ }
547
552
 
548
- const buffer = await response.arrayBuffer()
549
- await writeFile(destPath, Buffer.from(buffer))
553
+ const buffer = await response.arrayBuffer()
554
+ await writeFile(destPath, Buffer.from(buffer))
555
+ } catch (error) {
556
+ if (error instanceof Error && error.name === 'AbortError') {
557
+ throw new Error('Download timed out after 5 minutes')
558
+ }
559
+ throw error
560
+ } finally {
561
+ clearTimeout(timeout)
562
+ }
550
563
  }
551
564
 
552
565
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spindb",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Spin up local database containers without Docker. A DBngin-like CLI for PostgreSQL and MySQL.",
5
5
  "type": "module",
6
6
  "bin": {