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
@@ -1,7 +1,14 @@
1
1
  import { Command } from 'commander'
2
2
  import chalk from 'chalk'
3
3
  import inquirer from 'inquirer'
4
- import { existsSync, renameSync, mkdirSync, statSync, unlinkSync, copyFileSync } from 'fs'
4
+ import {
5
+ existsSync,
6
+ renameSync,
7
+ mkdirSync,
8
+ statSync,
9
+ unlinkSync,
10
+ copyFileSync,
11
+ } from 'fs'
5
12
  import { dirname, resolve, basename, join } from 'path'
6
13
  import { homedir } from 'os'
7
14
  import { containerManager } from '../../core/container-manager'
@@ -12,7 +19,7 @@ import { sqliteRegistry } from '../../engines/sqlite/registry'
12
19
  import { paths } from '../../config/paths'
13
20
  import { promptContainerSelect } from '../ui/prompts'
14
21
  import { createSpinner } from '../ui/spinner'
15
- import { error, warning, success, info } from '../ui/theme'
22
+ import { uiError, uiWarning, uiSuccess, uiInfo } from '../ui/theme'
16
23
  import { Engine } from '../../types'
17
24
 
18
25
  function isValidName(name: string): boolean {
@@ -25,9 +32,7 @@ function isValidName(name: string): boolean {
25
32
  async function promptEditAction(
26
33
  engine: string,
27
34
  ): Promise<'name' | 'port' | 'config' | 'relocate' | null> {
28
- const choices = [
29
- { name: 'Rename container', value: 'name' },
30
- ]
35
+ const choices = [{ name: 'Rename container', value: 'name' }]
31
36
 
32
37
  // SQLite: show relocate instead of port
33
38
  if (engine === Engine.SQLite) {
@@ -38,7 +43,10 @@ async function promptEditAction(
38
43
 
39
44
  // Only show config option for engines that support it
40
45
  if (engine === Engine.PostgreSQL) {
41
- choices.push({ name: 'Edit database config (postgresql.conf)', value: 'config' })
46
+ choices.push({
47
+ name: 'Edit database config (postgresql.conf)',
48
+ value: 'config',
49
+ })
42
50
  }
43
51
 
44
52
  choices.push({ name: chalk.gray('Cancel'), value: 'cancel' })
@@ -74,7 +82,7 @@ async function promptNewName(currentName: string): Promise<string | null> {
74
82
  ])
75
83
 
76
84
  if (newName === currentName) {
77
- console.log(warning('Name unchanged'))
85
+ console.log(uiWarning('Name unchanged'))
78
86
  return null
79
87
  }
80
88
 
@@ -83,17 +91,36 @@ async function promptNewName(currentName: string): Promise<string | null> {
83
91
 
84
92
  // Common PostgreSQL config settings that users might want to edit
85
93
  const COMMON_PG_SETTINGS = [
86
- { name: 'max_connections', description: 'Maximum concurrent connections', default: '200' },
87
- { name: 'shared_buffers', description: 'Memory for shared buffers', default: '128MB' },
94
+ {
95
+ name: 'max_connections',
96
+ description: 'Maximum concurrent connections',
97
+ default: '200',
98
+ },
99
+ {
100
+ name: 'shared_buffers',
101
+ description: 'Memory for shared buffers',
102
+ default: '128MB',
103
+ },
88
104
  { name: 'work_mem', description: 'Memory per operation', default: '4MB' },
89
- { name: 'maintenance_work_mem', description: 'Memory for maintenance ops', default: '64MB' },
90
- { name: 'effective_cache_size', description: 'Planner cache size estimate', default: '4GB' },
105
+ {
106
+ name: 'maintenance_work_mem',
107
+ description: 'Memory for maintenance ops',
108
+ default: '64MB',
109
+ },
110
+ {
111
+ name: 'effective_cache_size',
112
+ description: 'Planner cache size estimate',
113
+ default: '4GB',
114
+ },
91
115
  ]
92
116
 
93
117
  /**
94
118
  * Prompt for PostgreSQL config setting to edit
95
119
  */
96
- async function promptConfigSetting(): Promise<{ key: string; value: string } | null> {
120
+ async function promptConfigSetting(): Promise<{
121
+ key: string
122
+ value: string
123
+ } | null> {
97
124
  const choices = COMMON_PG_SETTINGS.map((s) => ({
98
125
  name: `${s.name.padEnd(25)} ${chalk.gray(s.description)}`,
99
126
  value: s.name,
@@ -121,7 +148,8 @@ async function promptConfigSetting(): Promise<{ key: string; value: string } | n
121
148
  message: 'Setting name:',
122
149
  validate: (input: string) => {
123
150
  if (!input.trim()) return 'Setting name is required'
124
- if (!/^[a-z_]+$/.test(input)) return 'Setting names are lowercase with underscores'
151
+ if (!/^[a-z_]+$/.test(input))
152
+ return 'Setting names are lowercase with underscores'
125
153
  return true
126
154
  },
127
155
  },
@@ -129,7 +157,8 @@ async function promptConfigSetting(): Promise<{ key: string; value: string } | n
129
157
  key = customKey
130
158
  }
131
159
 
132
- const defaultValue = COMMON_PG_SETTINGS.find((s) => s.name === key)?.default || ''
160
+ const defaultValue =
161
+ COMMON_PG_SETTINGS.find((s) => s.name === key)?.default || ''
133
162
  const { value } = await inquirer.prompt<{ value: string }>([
134
163
  {
135
164
  type: 'input',
@@ -168,14 +197,14 @@ async function promptNewPort(currentPort: number): Promise<number | null> {
168
197
  ])
169
198
 
170
199
  if (newPort === currentPort) {
171
- console.log(warning('Port unchanged'))
200
+ console.log(uiWarning('Port unchanged'))
172
201
  return null
173
202
  }
174
203
 
175
204
  const portAvailable = await portManager.isPortAvailable(newPort)
176
205
  if (!portAvailable) {
177
206
  console.log(
178
- warning(
207
+ uiWarning(
179
208
  `Note: Port ${newPort} is currently in use. It will be used when the container starts.`,
180
209
  ),
181
210
  )
@@ -190,7 +219,9 @@ async function promptNewPort(currentPort: number): Promise<number | null> {
190
219
  async function promptNewLocation(currentPath: string): Promise<string | null> {
191
220
  console.log()
192
221
  console.log(chalk.gray(` Current location: ${currentPath}`))
193
- console.log(chalk.gray(' Enter an absolute path or relative to current directory.'))
222
+ console.log(
223
+ chalk.gray(' Enter an absolute path or relative to current directory.'),
224
+ )
194
225
  console.log()
195
226
 
196
227
  const { newPath } = await inquirer.prompt<{ newPath: string }>([
@@ -202,7 +233,11 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
202
233
  validate: (input: string) => {
203
234
  if (!input.trim()) return 'Path is required'
204
235
  const resolvedPath = resolve(input).toLowerCase()
205
- if (!resolvedPath.endsWith('.sqlite') && !resolvedPath.endsWith('.db') && !resolvedPath.endsWith('.sqlite3')) {
236
+ if (
237
+ !resolvedPath.endsWith('.sqlite') &&
238
+ !resolvedPath.endsWith('.db') &&
239
+ !resolvedPath.endsWith('.sqlite3')
240
+ ) {
206
241
  return 'Path should end with .sqlite, .sqlite3, or .db'
207
242
  }
208
243
  return true
@@ -213,7 +248,7 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
213
248
  const resolvedPath = resolve(newPath)
214
249
 
215
250
  if (resolvedPath === currentPath) {
216
- console.log(warning('Location unchanged'))
251
+ console.log(uiWarning('Location unchanged'))
217
252
  return null
218
253
  }
219
254
 
@@ -228,7 +263,7 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
228
263
  },
229
264
  ])
230
265
  if (!overwrite) {
231
- console.log(warning('Relocate cancelled'))
266
+ console.log(uiWarning('Relocate cancelled'))
232
267
  return null
233
268
  }
234
269
  }
@@ -237,7 +272,9 @@ async function promptNewLocation(currentPath: string): Promise<string | null> {
237
272
  }
238
273
 
239
274
  export const editCommand = new Command('edit')
240
- .description('Edit container properties (rename, port, relocate, or database config)')
275
+ .description(
276
+ 'Edit container properties (rename, port, relocate, or database config)',
277
+ )
241
278
  .argument('[name]', 'Container name')
242
279
  .option('-n, --name <newName>', 'New container name')
243
280
  .option('-p, --port <port>', 'New port number', parseInt)
@@ -256,7 +293,13 @@ export const editCommand = new Command('edit')
256
293
  .action(
257
294
  async (
258
295
  name: string | undefined,
259
- options: { name?: string; port?: number; relocate?: string; overwrite?: boolean; setConfig?: string },
296
+ options: {
297
+ name?: string
298
+ port?: number
299
+ relocate?: string
300
+ overwrite?: boolean
301
+ setConfig?: string
302
+ },
260
303
  ) => {
261
304
  try {
262
305
  let containerName = name
@@ -265,7 +308,7 @@ export const editCommand = new Command('edit')
265
308
  const containers = await containerManager.list()
266
309
 
267
310
  if (containers.length === 0) {
268
- console.log(warning('No containers found'))
311
+ console.log(uiWarning('No containers found'))
269
312
  return
270
313
  }
271
314
 
@@ -279,7 +322,7 @@ export const editCommand = new Command('edit')
279
322
 
280
323
  const config = await containerManager.getConfig(containerName)
281
324
  if (!config) {
282
- console.error(error(`Container "${containerName}" not found`))
325
+ console.error(uiError(`Container "${containerName}" not found`))
283
326
  process.exit(1)
284
327
  }
285
328
 
@@ -327,7 +370,7 @@ export const editCommand = new Command('edit')
327
370
  if (options.name) {
328
371
  if (!isValidName(options.name)) {
329
372
  console.error(
330
- error(
373
+ uiError(
331
374
  'Name must start with a letter and contain only letters, numbers, hyphens, and underscores',
332
375
  ),
333
376
  )
@@ -338,7 +381,7 @@ export const editCommand = new Command('edit')
338
381
  engine: config.engine,
339
382
  })
340
383
  if (exists) {
341
- console.error(error(`Container "${options.name}" already exists`))
384
+ console.error(uiError(`Container "${options.name}" already exists`))
342
385
  process.exit(1)
343
386
  }
344
387
 
@@ -347,7 +390,7 @@ export const editCommand = new Command('edit')
347
390
  })
348
391
  if (running) {
349
392
  console.error(
350
- error(
393
+ uiError(
351
394
  `Container "${containerName}" is running. Stop it first to rename.`,
352
395
  ),
353
396
  )
@@ -368,14 +411,14 @@ export const editCommand = new Command('edit')
368
411
 
369
412
  if (options.port !== undefined) {
370
413
  if (options.port < 1 || options.port > 65535) {
371
- console.error(error('Port must be between 1 and 65535'))
414
+ console.error(uiError('Port must be between 1 and 65535'))
372
415
  process.exit(1)
373
416
  }
374
417
 
375
418
  const portAvailable = await portManager.isPortAvailable(options.port)
376
419
  if (!portAvailable) {
377
420
  console.log(
378
- warning(
421
+ uiWarning(
379
422
  `Port ${options.port} is currently in use. The container will use this port on next start.`,
380
423
  ),
381
424
  )
@@ -400,7 +443,7 @@ export const editCommand = new Command('edit')
400
443
  if (options.relocate) {
401
444
  if (config.engine !== Engine.SQLite) {
402
445
  console.error(
403
- error('Relocate is only available for SQLite containers'),
446
+ uiError('Relocate is only available for SQLite containers'),
404
447
  )
405
448
  process.exit(1)
406
449
  }
@@ -425,13 +468,17 @@ export const editCommand = new Command('edit')
425
468
  // - ends with /
426
469
  // - exists and is a directory
427
470
  // - doesn't have a database file extension
428
- const isDirectory = expandedPath.endsWith('/') ||
429
- (existsSync(expandedPath) && statSync(expandedPath).isDirectory()) ||
471
+ const isDirectory =
472
+ expandedPath.endsWith('/') ||
473
+ (existsSync(expandedPath) &&
474
+ statSync(expandedPath).isDirectory()) ||
430
475
  !hasDbExtension
431
476
 
432
477
  let newPath: string
433
478
  if (isDirectory) {
434
- const dirPath = expandedPath.endsWith('/') ? expandedPath.slice(0, -1) : expandedPath
479
+ const dirPath = expandedPath.endsWith('/')
480
+ ? expandedPath.slice(0, -1)
481
+ : expandedPath
435
482
  const currentFileName = basename(config.database)
436
483
  newPath = join(dirPath, currentFileName)
437
484
  } else {
@@ -441,7 +488,7 @@ export const editCommand = new Command('edit')
441
488
  // Check source file exists
442
489
  if (!existsSync(config.database)) {
443
490
  console.error(
444
- error(`Source database file not found: ${config.database}`),
491
+ uiError(`Source database file not found: ${config.database}`),
445
492
  )
446
493
  process.exit(1)
447
494
  }
@@ -451,12 +498,14 @@ export const editCommand = new Command('edit')
451
498
  if (options.overwrite) {
452
499
  // Remove existing file before move
453
500
  unlinkSync(newPath)
454
- console.log(warning(`Overwriting existing file: ${newPath}`))
501
+ console.log(uiWarning(`Overwriting existing file: ${newPath}`))
455
502
  } else {
456
503
  console.error(
457
- error(`Destination file already exists: ${newPath}`),
504
+ uiError(`Destination file already exists: ${newPath}`),
505
+ )
506
+ console.log(
507
+ uiInfo('Use --overwrite to replace the existing file'),
458
508
  )
459
- console.log(info('Use --overwrite to replace the existing file'))
460
509
  process.exit(1)
461
510
  }
462
511
  }
@@ -465,12 +514,10 @@ export const editCommand = new Command('edit')
465
514
  const targetDir = dirname(newPath)
466
515
  if (!existsSync(targetDir)) {
467
516
  mkdirSync(targetDir, { recursive: true })
468
- console.log(info(`Created directory: ${targetDir}`))
517
+ console.log(uiInfo(`Created directory: ${targetDir}`))
469
518
  }
470
519
 
471
- const spinner = createSpinner(
472
- `Moving database to ${newPath}...`,
473
- )
520
+ const spinner = createSpinner(`Moving database to ${newPath}...`)
474
521
  spinner.start()
475
522
 
476
523
  try {
@@ -509,9 +556,9 @@ export const editCommand = new Command('edit')
509
556
  await sqliteRegistry.update(containerName, { filePath: newPath })
510
557
 
511
558
  spinner.succeed(`Database relocated to ${newPath}`)
512
- } catch (err) {
559
+ } catch (error) {
513
560
  spinner.fail('Failed to relocate database')
514
- throw err
561
+ throw error
515
562
  }
516
563
  }
517
564
 
@@ -520,7 +567,9 @@ export const editCommand = new Command('edit')
520
567
  // Only PostgreSQL supports config editing for now
521
568
  if (config.engine !== Engine.PostgreSQL) {
522
569
  console.error(
523
- error(`Config editing is only supported for PostgreSQL containers`),
570
+ uiError(
571
+ `Config editing is only supported for PostgreSQL containers`,
572
+ ),
524
573
  )
525
574
  process.exit(1)
526
575
  }
@@ -529,7 +578,7 @@ export const editCommand = new Command('edit')
529
578
  const match = options.setConfig.match(/^([a-z_]+)=(.+)$/)
530
579
  if (!match) {
531
580
  console.error(
532
- error(
581
+ uiError(
533
582
  'Invalid config format. Use: --set-config key=value (e.g., max_connections=200)',
534
583
  ),
535
584
  )
@@ -551,11 +600,15 @@ export const editCommand = new Command('edit')
551
600
 
552
601
  // Use the PostgreSQL engine's setConfigValue method
553
602
  if ('setConfigValue' in engine) {
554
- await (engine as { setConfigValue: (dataDir: string, key: string, value: string) => Promise<void> }).setConfigValue(
555
- dataDir,
556
- configKey,
557
- configValue,
558
- )
603
+ await (
604
+ engine as {
605
+ setConfigValue: (
606
+ dataDir: string,
607
+ key: string,
608
+ value: string,
609
+ ) => Promise<void>
610
+ }
611
+ ).setConfigValue(dataDir, configKey, configValue)
559
612
  spinner.succeed(`Set ${configKey} = ${configValue}`)
560
613
  } else {
561
614
  spinner.fail('Config editing not supported for this engine')
@@ -568,7 +621,7 @@ export const editCommand = new Command('edit')
568
621
  })
569
622
  if (running) {
570
623
  console.log(
571
- info(
624
+ uiInfo(
572
625
  ' Note: Restart the container for changes to take effect.',
573
626
  ),
574
627
  )
@@ -587,10 +640,10 @@ export const editCommand = new Command('edit')
587
640
  }
588
641
 
589
642
  console.log()
590
- console.log(success('Container updated successfully'))
591
- } catch (err) {
592
- const e = err as Error
593
- console.error(error(e.message))
643
+ console.log(uiSuccess('Container updated successfully'))
644
+ } catch (error) {
645
+ const e = error as Error
646
+ console.error(uiError(e.message))
594
647
  process.exit(1)
595
648
  }
596
649
  },
@@ -5,7 +5,7 @@ import inquirer from 'inquirer'
5
5
  import { containerManager } from '../../core/container-manager'
6
6
  import { promptConfirm } from '../ui/prompts'
7
7
  import { createSpinner } from '../ui/spinner'
8
- import { error, warning, info, formatBytes } from '../ui/theme'
8
+ import { uiError, uiWarning, uiInfo, formatBytes } from '../ui/theme'
9
9
  import { getEngineIcon, ENGINE_ICONS } from '../constants'
10
10
  import {
11
11
  getInstalledEngines,
@@ -36,7 +36,7 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
36
36
  }
37
37
 
38
38
  if (engines.length === 0) {
39
- console.log(info('No engines installed yet.'))
39
+ console.log(uiInfo('No engines installed yet.'))
40
40
  console.log(
41
41
  chalk.gray(
42
42
  ' PostgreSQL engines are downloaded automatically when you create a container.',
@@ -134,7 +134,9 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
134
134
  console.log(chalk.gray(` MySQL: system-installed at ${mysqlEngine.path}`))
135
135
  }
136
136
  if (sqliteEngine) {
137
- console.log(chalk.gray(` SQLite: system-installed at ${sqliteEngine.path}`))
137
+ console.log(
138
+ chalk.gray(` SQLite: system-installed at ${sqliteEngine.path}`),
139
+ )
138
140
  }
139
141
  console.log()
140
142
  }
@@ -151,7 +153,7 @@ async function deleteEngine(
151
153
  const pgEngines = await getInstalledPostgresEngines()
152
154
 
153
155
  if (pgEngines.length === 0) {
154
- console.log(warning('No deletable engines found.'))
156
+ console.log(uiWarning('No deletable engines found.'))
155
157
  console.log(
156
158
  chalk.gray(
157
159
  ' MySQL is system-installed and cannot be deleted via spindb.',
@@ -190,7 +192,7 @@ async function deleteEngine(
190
192
  )
191
193
 
192
194
  if (!targetEngine) {
193
- console.error(error(`Engine "${engineName} ${engineVersion}" not found`))
195
+ console.error(uiError(`Engine "${engineName} ${engineVersion}" not found`))
194
196
  process.exit(1)
195
197
  }
196
198
 
@@ -202,7 +204,7 @@ async function deleteEngine(
202
204
 
203
205
  if (usingContainers.length > 0) {
204
206
  console.error(
205
- error(
207
+ uiError(
206
208
  `Cannot delete: ${usingContainers.length} container(s) are using ${engineName} ${engineVersion}`,
207
209
  ),
208
210
  )
@@ -224,7 +226,7 @@ async function deleteEngine(
224
226
  )
225
227
 
226
228
  if (!confirmed) {
227
- console.log(warning('Deletion cancelled'))
229
+ console.log(uiWarning('Deletion cancelled'))
228
230
  return
229
231
  }
230
232
  }
@@ -236,8 +238,8 @@ async function deleteEngine(
236
238
  try {
237
239
  await rm(targetEngine.path, { recursive: true, force: true })
238
240
  spinner.succeed(`Deleted ${engineName} ${engineVersion}`)
239
- } catch (err) {
240
- const e = err as Error
241
+ } catch (error) {
242
+ const e = error as Error
241
243
  spinner.fail(`Failed to delete: ${e.message}`)
242
244
  process.exit(1)
243
245
  }
@@ -250,9 +252,9 @@ export const enginesCommand = new Command('engines')
250
252
  .action(async (options: { json?: boolean }) => {
251
253
  try {
252
254
  await listEngines(options)
253
- } catch (err) {
254
- const e = err as Error
255
- console.error(error(e.message))
255
+ } catch (error) {
256
+ const e = error as Error
257
+ console.error(uiError(e.message))
256
258
  process.exit(1)
257
259
  }
258
260
  })
@@ -270,9 +272,9 @@ enginesCommand
270
272
  ) => {
271
273
  try {
272
274
  await deleteEngine(engine, version, options)
273
- } catch (err) {
274
- const e = err as Error
275
- console.error(error(e.message))
275
+ } catch (error) {
276
+ const e = error as Error
277
+ console.error(uiError(e.message))
276
278
  process.exit(1)
277
279
  }
278
280
  },
@@ -7,7 +7,7 @@ import { containerManager } from '../../core/container-manager'
7
7
  import { processManager } from '../../core/process-manager'
8
8
  import { paths } from '../../config/paths'
9
9
  import { getEngine } from '../../engines'
10
- import { error, info, header } from '../ui/theme'
10
+ import { uiError, uiInfo, header } from '../ui/theme'
11
11
  import { getEngineIcon } from '../constants'
12
12
  import { Engine, type ContainerConfig } from '../../types'
13
13
 
@@ -255,14 +255,16 @@ export const infoCommand = new Command('info')
255
255
  const containers = await containerManager.list()
256
256
 
257
257
  if (containers.length === 0) {
258
- console.log(info('No containers found. Create one with: spindb create'))
258
+ console.log(
259
+ uiInfo('No containers found. Create one with: spindb create'),
260
+ )
259
261
  return
260
262
  }
261
263
 
262
264
  if (name) {
263
265
  const config = await containerManager.getConfig(name)
264
266
  if (!config) {
265
- console.error(error(`Container "${name}" not found`))
267
+ console.error(uiError(`Container "${name}" not found`))
266
268
  process.exit(1)
267
269
  }
268
270
  await displayContainerInfo(config, options)
@@ -299,9 +301,9 @@ export const infoCommand = new Command('info')
299
301
  }
300
302
 
301
303
  await displayAllContainersInfo(containers, options)
302
- } catch (err) {
303
- const e = err as Error
304
- console.error(error(e.message))
304
+ } catch (error) {
305
+ const e = error as Error
306
+ console.error(uiError(e.message))
305
307
  process.exit(1)
306
308
  }
307
309
  })
@@ -2,7 +2,7 @@ import { Command } from 'commander'
2
2
  import chalk from 'chalk'
3
3
  import { containerManager } from '../../core/container-manager'
4
4
  import { getEngine } from '../../engines'
5
- import { info, error, formatBytes } from '../ui/theme'
5
+ import { uiInfo, uiError, formatBytes } from '../ui/theme'
6
6
  import { getEngineIcon } from '../constants'
7
7
  import { Engine } from '../../types'
8
8
  import { basename } from 'path'
@@ -62,7 +62,9 @@ export const listCommand = new Command('list')
62
62
  }
63
63
 
64
64
  if (containers.length === 0) {
65
- console.log(info('No containers found. Create one with: spindb create'))
65
+ console.log(
66
+ uiInfo('No containers found. Create one with: spindb create'),
67
+ )
66
68
  return
67
69
  }
68
70
 
@@ -108,7 +110,8 @@ export const listCommand = new Command('list')
108
110
  if (container.engine === Engine.SQLite) {
109
111
  const fileName = basename(container.database)
110
112
  // Truncate if longer than 8 chars to fit in 8-char column
111
- portOrPath = fileName.length > 8 ? fileName.slice(0, 7) + '…' : fileName
113
+ portOrPath =
114
+ fileName.length > 8 ? fileName.slice(0, 7) + '…' : fileName
112
115
  } else {
113
116
  portOrPath = String(container.port)
114
117
  }
@@ -126,31 +129,43 @@ export const listCommand = new Command('list')
126
129
 
127
130
  console.log()
128
131
 
129
- const serverContainers = containers.filter((c) => c.engine !== Engine.SQLite)
130
- const sqliteContainers = containers.filter((c) => c.engine === Engine.SQLite)
132
+ const serverContainers = containers.filter(
133
+ (c) => c.engine !== Engine.SQLite,
134
+ )
135
+ const sqliteContainers = containers.filter(
136
+ (c) => c.engine === Engine.SQLite,
137
+ )
131
138
 
132
- const running = serverContainers.filter((c) => c.status === 'running').length
133
- const stopped = serverContainers.filter((c) => c.status !== 'running').length
134
- const available = sqliteContainers.filter((c) => c.status === 'running').length
135
- const missing = sqliteContainers.filter((c) => c.status !== 'running').length
139
+ const running = serverContainers.filter(
140
+ (c) => c.status === 'running',
141
+ ).length
142
+ const stopped = serverContainers.filter(
143
+ (c) => c.status !== 'running',
144
+ ).length
145
+ const available = sqliteContainers.filter(
146
+ (c) => c.status === 'running',
147
+ ).length
148
+ const missing = sqliteContainers.filter(
149
+ (c) => c.status !== 'running',
150
+ ).length
136
151
 
137
152
  const parts: string[] = []
138
153
  if (serverContainers.length > 0) {
139
154
  parts.push(`${running} running, ${stopped} stopped`)
140
155
  }
141
156
  if (sqliteContainers.length > 0) {
142
- parts.push(`${available} SQLite available${missing > 0 ? `, ${missing} missing` : ''}`)
157
+ parts.push(
158
+ `${available} SQLite available${missing > 0 ? `, ${missing} missing` : ''}`,
159
+ )
143
160
  }
144
161
 
145
162
  console.log(
146
- chalk.gray(
147
- ` ${containers.length} container(s): ${parts.join('; ')}`,
148
- ),
163
+ chalk.gray(` ${containers.length} container(s): ${parts.join('; ')}`),
149
164
  )
150
165
  console.log()
151
- } catch (err) {
152
- const e = err as Error
153
- console.error(error(e.message))
166
+ } catch (error) {
167
+ const e = error as Error
168
+ console.error(uiError(e.message))
154
169
  process.exit(1)
155
170
  }
156
171
  })