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
@@ -5,7 +5,7 @@ import { readFile } from 'fs/promises'
5
5
  import { containerManager } from '../../core/container-manager'
6
6
  import { paths } from '../../config/paths'
7
7
  import { promptContainerSelect } from '../ui/prompts'
8
- import { error, warning, info } from '../ui/theme'
8
+ import { uiError, uiWarning, uiInfo } from '../ui/theme'
9
9
 
10
10
  function getLastNLines(content: string, n: number): string {
11
11
  const lines = content.split('\n')
@@ -32,7 +32,7 @@ export const logsCommand = new Command('logs')
32
32
  const containers = await containerManager.list()
33
33
 
34
34
  if (containers.length === 0) {
35
- console.log(warning('No containers found'))
35
+ console.log(uiWarning('No containers found'))
36
36
  return
37
37
  }
38
38
 
@@ -46,7 +46,7 @@ export const logsCommand = new Command('logs')
46
46
 
47
47
  const config = await containerManager.getConfig(containerName)
48
48
  if (!config) {
49
- console.error(error(`Container "${containerName}" not found`))
49
+ console.error(uiError(`Container "${containerName}" not found`))
50
50
  process.exit(1)
51
51
  }
52
52
 
@@ -56,7 +56,7 @@ export const logsCommand = new Command('logs')
56
56
 
57
57
  if (!existsSync(logPath)) {
58
58
  console.log(
59
- info(
59
+ uiInfo(
60
60
  `No log file found for "${containerName}". The container may not have been started yet.`,
61
61
  ),
62
62
  )
@@ -84,9 +84,13 @@ export const logsCommand = new Command('logs')
84
84
 
85
85
  if (options.follow) {
86
86
  const lineCount = parseInt(options.lines || '50', 10)
87
- const child = spawn('tail', ['-n', String(lineCount), '-f', logPath], {
88
- stdio: 'inherit',
89
- })
87
+ const child = spawn(
88
+ 'tail',
89
+ ['-n', String(lineCount), '-f', logPath],
90
+ {
91
+ stdio: 'inherit',
92
+ },
93
+ )
90
94
 
91
95
  // Use named handler so we can remove it to prevent listener leaks
92
96
  const sigintHandler = () => {
@@ -109,15 +113,15 @@ export const logsCommand = new Command('logs')
109
113
  const content = await readFile(logPath, 'utf-8')
110
114
 
111
115
  if (content.trim() === '') {
112
- console.log(info('Log file is empty'))
116
+ console.log(uiInfo('Log file is empty'))
113
117
  return
114
118
  }
115
119
 
116
120
  const output = getLastNLines(content, lineCount)
117
121
  console.log(output)
118
- } catch (err) {
119
- const e = err as Error
120
- console.error(error(e.message))
122
+ } catch (error) {
123
+ const e = error as Error
124
+ console.error(uiError(e.message))
121
125
  process.exit(1)
122
126
  }
123
127
  },
@@ -25,14 +25,15 @@ import {
25
25
  import { createSpinner } from '../../ui/spinner'
26
26
  import {
27
27
  header,
28
- success,
29
- error,
30
- warning,
28
+ uiSuccess,
29
+ uiError,
30
+ uiWarning,
31
31
  connectionBox,
32
32
  formatBytes,
33
33
  } from '../../ui/theme'
34
34
  import { getEngineIcon } from '../../constants'
35
35
  import { type Engine } from '../../../types'
36
+ import { pressEnterToContinue } from './shared'
36
37
 
37
38
  function generateBackupTimestamp(): string {
38
39
  const now = new Date()
@@ -65,7 +66,7 @@ export async function handleCreateForRestore(): Promise<{
65
66
  const portAvailable = await portManager.isPortAvailable(port)
66
67
  if (!portAvailable) {
67
68
  console.log(
68
- error(`Port ${port} is in use. Please choose a different port.`),
69
+ uiError(`Port ${port} is in use. Please choose a different port.`),
69
70
  )
70
71
  return null
71
72
  }
@@ -77,13 +78,17 @@ export async function handleCreateForRestore(): Promise<{
77
78
 
78
79
  const isInstalled = await dbEngine.isBinaryInstalled(version)
79
80
  if (isInstalled) {
80
- binarySpinner.succeed(`${dbEngine.displayName} ${version} binaries ready (cached)`)
81
+ binarySpinner.succeed(
82
+ `${dbEngine.displayName} ${version} binaries ready (cached)`,
83
+ )
81
84
  } else {
82
85
  binarySpinner.text = `Downloading ${dbEngine.displayName} ${version} binaries...`
83
86
  await dbEngine.ensureBinaries(version, ({ message }) => {
84
87
  binarySpinner.text = message
85
88
  })
86
- binarySpinner.succeed(`${dbEngine.displayName} ${version} binaries downloaded`)
89
+ binarySpinner.succeed(
90
+ `${dbEngine.displayName} ${version} binaries downloaded`,
91
+ )
87
92
  }
88
93
 
89
94
  while (await containerManager.exists(containerName)) {
@@ -136,7 +141,7 @@ export async function handleCreateForRestore(): Promise<{
136
141
  }
137
142
 
138
143
  console.log()
139
- console.log(success('Container ready for restore'))
144
+ console.log(uiSuccess('Container ready for restore'))
140
145
  console.log()
141
146
 
142
147
  return { name: containerName, config }
@@ -184,7 +189,7 @@ export async function handleRestore(): Promise<void> {
184
189
  containerName = selectedContainer
185
190
  config = await containerManager.getConfig(containerName)
186
191
  if (!config) {
187
- console.error(error(`Container "${containerName}" not found`))
192
+ console.error(uiError(`Container "${containerName}" not found`))
188
193
  return
189
194
  }
190
195
  }
@@ -210,7 +215,7 @@ export async function handleRestore(): Promise<void> {
210
215
  missingDeps = await getMissingDependencies(config.engine)
211
216
  if (missingDeps.length > 0) {
212
217
  console.log(
213
- error(
218
+ uiError(
214
219
  `Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
215
220
  ),
216
221
  )
@@ -301,8 +306,8 @@ export async function handleRestore(): Promise<void> {
301
306
  backupPath = tempDumpPath
302
307
  isTempFile = true
303
308
  dumpSuccess = true
304
- } catch (err) {
305
- const e = err as Error
309
+ } catch (error) {
310
+ const e = error as Error
306
311
  dumpSpinner.fail('Failed to create dump')
307
312
 
308
313
  if (
@@ -313,15 +318,19 @@ export async function handleRestore(): Promise<void> {
313
318
  const missingTool = e.message.includes('mysqldump')
314
319
  ? 'mysqldump'
315
320
  : 'pg_dump'
316
- const toolEngine = missingTool === 'mysqldump' ? 'mysql' : 'postgresql'
317
- const installed = await promptInstallDependencies(missingTool, toolEngine as Engine)
321
+ const toolEngine =
322
+ missingTool === 'mysqldump' ? 'mysql' : 'postgresql'
323
+ const installed = await promptInstallDependencies(
324
+ missingTool,
325
+ toolEngine as Engine,
326
+ )
318
327
  if (installed) {
319
328
  continue
320
329
  }
321
330
  } else {
322
331
  const dumpTool = config.engine === 'mysql' ? 'mysqldump' : 'pg_dump'
323
332
  console.log()
324
- console.log(error(`${dumpTool} error:`))
333
+ console.log(uiError(`${dumpTool} error:`))
325
334
  console.log(chalk.gray(` ${e.message}`))
326
335
  console.log()
327
336
  }
@@ -344,7 +353,7 @@ export async function handleRestore(): Promise<void> {
344
353
  }
345
354
 
346
355
  if (!dumpSuccess) {
347
- console.log(error('Failed to create dump after retries'))
356
+ console.log(uiError('Failed to create dump after retries'))
348
357
  return
349
358
  }
350
359
  } else {
@@ -415,9 +424,9 @@ export async function handleRestore(): Promise<void> {
415
424
  ) {
416
425
  restoreSpinner.fail('Version compatibility detected')
417
426
  console.log()
418
- console.log(error('PostgreSQL version incompatibility detected:'))
427
+ console.log(uiError('PostgreSQL version incompatibility detected:'))
419
428
  console.log(
420
- warning('Your pg_restore version is too old for this backup file.'),
429
+ uiWarning('Your pg_restore version is too old for this backup file.'),
421
430
  )
422
431
 
423
432
  console.log(chalk.yellow('Cleaning up failed database...'))
@@ -467,23 +476,20 @@ export async function handleRestore(): Promise<void> {
467
476
  upgradeSpinner.succeed('PostgreSQL client tools upgraded')
468
477
  console.log()
469
478
  console.log(
470
- success('Please try the restore again with the updated tools.'),
479
+ uiSuccess('Please try the restore again with the updated tools.'),
471
480
  )
472
- await new Promise((resolve) => {
473
- console.log(chalk.gray('Press Enter to continue...'))
474
- process.stdin.once('data', resolve)
475
- })
481
+ await pressEnterToContinue()
476
482
  return
477
483
  } else {
478
484
  upgradeSpinner.fail('Upgrade failed')
479
485
  console.log()
480
486
  console.log(
481
- error('Automatic upgrade failed. Please upgrade manually:'),
487
+ uiError('Automatic upgrade failed. Please upgrade manually:'),
482
488
  )
483
489
  const pgPackage = getPostgresHomebrewPackage()
484
490
  const latestMajor = pgPackage.split('@')[1]
485
491
  console.log(
486
- warning(
492
+ uiWarning(
487
493
  ` macOS: brew install ${pgPackage} && brew link --force ${pgPackage}`,
488
494
  ),
489
495
  )
@@ -493,7 +499,7 @@ export async function handleRestore(): Promise<void> {
493
499
  ),
494
500
  )
495
501
  console.log(
496
- warning(
502
+ uiWarning(
497
503
  ` Ubuntu/Debian: sudo apt update && sudo apt install postgresql-client-${latestMajor}`,
498
504
  ),
499
505
  )
@@ -502,15 +508,12 @@ export async function handleRestore(): Promise<void> {
502
508
  ` This installs PostgreSQL ${latestMajor} client tools: pg_restore, pg_dump, psql, and libpq`,
503
509
  ),
504
510
  )
505
- await new Promise((resolve) => {
506
- console.log(chalk.gray('Press Enter to continue...'))
507
- process.stdin.once('data', resolve)
508
- })
511
+ await pressEnterToContinue()
509
512
  return
510
513
  }
511
514
  } catch {
512
515
  upgradeSpinner.fail('Upgrade failed')
513
- console.log(error('Failed to upgrade PostgreSQL client tools'))
516
+ console.log(uiError('Failed to upgrade PostgreSQL client tools'))
514
517
  console.log(
515
518
  chalk.gray(
516
519
  'Manual upgrade may be required for pg_restore, pg_dump, and psql',
@@ -525,7 +528,7 @@ export async function handleRestore(): Promise<void> {
525
528
  } else {
526
529
  console.log()
527
530
  console.log(
528
- warning(
531
+ uiWarning(
529
532
  'Restore cancelled. Please upgrade PostgreSQL client tools manually and try again.',
530
533
  ),
531
534
  )
@@ -555,7 +558,7 @@ export async function handleRestore(): Promise<void> {
555
558
  if (result.code === 0) {
556
559
  const connectionString = engine.getConnectionString(config, databaseName)
557
560
  console.log()
558
- console.log(success(`Database "${databaseName}" restored`))
561
+ console.log(uiSuccess(`Database "${databaseName}" restored`))
559
562
  console.log(chalk.gray(' Connection string:'))
560
563
  console.log(chalk.cyan(` ${connectionString}`))
561
564
 
@@ -591,7 +594,7 @@ export async function handleBackup(): Promise<void> {
591
594
  const running = containers.filter((c) => c.status === 'running')
592
595
 
593
596
  if (running.length === 0) {
594
- console.log(warning('No running containers. Start a container first.'))
597
+ console.log(uiWarning('No running containers. Start a container first.'))
595
598
  await inquirer.prompt([
596
599
  {
597
600
  type: 'input',
@@ -611,7 +614,7 @@ export async function handleBackup(): Promise<void> {
611
614
 
612
615
  const config = await containerManager.getConfig(containerName)
613
616
  if (!config) {
614
- console.log(error(`Container "${containerName}" not found`))
617
+ console.log(uiError(`Container "${containerName}" not found`))
615
618
  return
616
619
  }
617
620
 
@@ -638,7 +641,7 @@ export async function handleBackup(): Promise<void> {
638
641
  missingDeps = await getMissingDependencies(config.engine)
639
642
  if (missingDeps.length > 0) {
640
643
  console.log(
641
- error(
644
+ uiError(
642
645
  `Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
643
646
  ),
644
647
  )
@@ -685,17 +688,17 @@ export async function handleBackup(): Promise<void> {
685
688
  backupSpinner.succeed('Backup created successfully')
686
689
 
687
690
  console.log()
688
- console.log(success('Backup complete'))
691
+ console.log(uiSuccess('Backup complete'))
689
692
  console.log()
690
693
  console.log(chalk.gray(' File:'), chalk.cyan(result.path))
691
694
  console.log(chalk.gray(' Size:'), chalk.white(formatBytes(result.size)))
692
695
  console.log(chalk.gray(' Format:'), chalk.white(result.format))
693
696
  console.log()
694
- } catch (err) {
695
- const e = err as Error
697
+ } catch (error) {
698
+ const e = error as Error
696
699
  backupSpinner.fail('Backup failed')
697
700
  console.log()
698
- console.log(error(e.message))
701
+ console.log(uiError(e.message))
699
702
  console.log()
700
703
  }
701
704
 
@@ -713,13 +716,13 @@ export async function handleClone(): Promise<void> {
713
716
  const stopped = containers.filter((c) => c.status !== 'running')
714
717
 
715
718
  if (containers.length === 0) {
716
- console.log(warning('No containers found'))
719
+ console.log(uiWarning('No containers found'))
717
720
  return
718
721
  }
719
722
 
720
723
  if (stopped.length === 0) {
721
724
  console.log(
722
- warning(
725
+ uiWarning(
723
726
  'All containers are running. Stop a container first to clone it.',
724
727
  ),
725
728
  )
@@ -735,7 +738,7 @@ export async function handleClone(): Promise<void> {
735
738
 
736
739
  const sourceConfig = await containerManager.getConfig(sourceName)
737
740
  if (!sourceConfig) {
738
- console.log(error(`Container "${sourceName}" not found`))
741
+ console.log(uiError(`Container "${sourceName}" not found`))
739
742
  return
740
743
  }
741
744
 
@@ -756,8 +759,10 @@ export async function handleClone(): Promise<void> {
756
759
  ])
757
760
 
758
761
  // Check if target container already exists
759
- if (await containerManager.exists(targetName, { engine: sourceConfig.engine })) {
760
- console.log(error(`Container "${targetName}" already exists`))
762
+ if (
763
+ await containerManager.exists(targetName, { engine: sourceConfig.engine })
764
+ ) {
765
+ console.log(uiError(`Container "${targetName}" already exists`))
761
766
  return
762
767
  }
763
768
 
@@ -774,9 +779,9 @@ export async function handleClone(): Promise<void> {
774
779
 
775
780
  console.log()
776
781
  console.log(connectionBox(targetName, connectionString, newConfig.port))
777
- } catch (err) {
778
- const e = err as Error
782
+ } catch (error) {
783
+ const e = error as Error
779
784
  spinner.fail(`Failed to clone "${sourceName}"`)
780
- console.log(error(e.message))
785
+ console.log(uiError(e.message))
781
786
  }
782
787
  }