spindb 0.1.0 → 0.2.1

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.
@@ -5,13 +5,14 @@ import { processManager } from '@/core/process-manager'
5
5
  import { portManager } from '@/core/port-manager'
6
6
  import type { ContainerConfig } from '@/types'
7
7
 
8
- export interface CreateOptions {
8
+ export type CreateOptions = {
9
9
  engine: string
10
10
  version: string
11
11
  port: number
12
+ database: string
12
13
  }
13
14
 
14
- export interface DeleteOptions {
15
+ export type DeleteOptions = {
15
16
  force?: boolean
16
17
  }
17
18
 
@@ -20,7 +21,7 @@ export class ContainerManager {
20
21
  * Create a new container
21
22
  */
22
23
  async create(name: string, options: CreateOptions): Promise<ContainerConfig> {
23
- const { engine, version, port } = options
24
+ const { engine, version, port, database } = options
24
25
 
25
26
  // Validate container name
26
27
  if (!this.isValidName(name)) {
@@ -47,6 +48,7 @@ export class ContainerManager {
47
48
  engine,
48
49
  version,
49
50
  port,
51
+ database,
50
52
  created: new Date().toISOString(),
51
53
  status: 'created',
52
54
  }
@@ -203,8 +205,8 @@ export class ContainerManager {
203
205
  config.created = new Date().toISOString()
204
206
  config.clonedFrom = sourceName
205
207
 
206
- // Assign new port
207
- const { port } = await portManager.findAvailablePort()
208
+ // Assign new port (excluding ports already used by other containers)
209
+ const { port } = await portManager.findAvailablePortExcludingContainers()
208
210
  config.port = port
209
211
 
210
212
  await this.saveConfig(targetName, config)
@@ -212,6 +214,52 @@ export class ContainerManager {
212
214
  return config
213
215
  }
214
216
 
217
+ /**
218
+ * Rename a container
219
+ */
220
+ async rename(oldName: string, newName: string): Promise<ContainerConfig> {
221
+ // Validate new name
222
+ if (!this.isValidName(newName)) {
223
+ throw new Error(
224
+ 'Container name must be alphanumeric with hyphens/underscores only',
225
+ )
226
+ }
227
+
228
+ // Check source exists
229
+ if (!(await this.exists(oldName))) {
230
+ throw new Error(`Container "${oldName}" not found`)
231
+ }
232
+
233
+ // Check target doesn't exist
234
+ if (await this.exists(newName)) {
235
+ throw new Error(`Container "${newName}" already exists`)
236
+ }
237
+
238
+ // Check container is not running
239
+ const running = await processManager.isRunning(oldName)
240
+ if (running) {
241
+ throw new Error(`Container "${oldName}" is running. Stop it first`)
242
+ }
243
+
244
+ // Rename directory
245
+ const oldPath = paths.getContainerPath(oldName)
246
+ const newPath = paths.getContainerPath(newName)
247
+
248
+ await cp(oldPath, newPath, { recursive: true })
249
+ await rm(oldPath, { recursive: true, force: true })
250
+
251
+ // Update config with new name
252
+ const config = await this.getConfig(newName)
253
+ if (!config) {
254
+ throw new Error('Failed to read renamed container config')
255
+ }
256
+
257
+ config.name = newName
258
+ await this.saveConfig(newName, config)
259
+
260
+ return config
261
+ }
262
+
215
263
  /**
216
264
  * Validate container name
217
265
  */
@@ -1,8 +1,11 @@
1
1
  import net from 'net'
2
2
  import { exec } from 'child_process'
3
3
  import { promisify } from 'util'
4
+ import { existsSync } from 'fs'
5
+ import { readdir, readFile } from 'fs/promises'
4
6
  import { defaults } from '@/config/defaults'
5
- import type { PortResult } from '@/types'
7
+ import { paths } from '@/config/paths'
8
+ import type { ContainerConfig, PortResult } from '@/types'
6
9
 
7
10
  const execAsync = promisify(exec)
8
11
 
@@ -79,6 +82,78 @@ export class PortManager {
79
82
  return null
80
83
  }
81
84
  }
85
+
86
+ /**
87
+ * Get all ports currently assigned to containers
88
+ */
89
+ async getContainerPorts(): Promise<number[]> {
90
+ const containersDir = paths.containers
91
+
92
+ if (!existsSync(containersDir)) {
93
+ return []
94
+ }
95
+
96
+ const ports: number[] = []
97
+ const entries = await readdir(containersDir, { withFileTypes: true })
98
+
99
+ for (const entry of entries) {
100
+ if (entry.isDirectory()) {
101
+ const configPath = `${containersDir}/${entry.name}/container.json`
102
+ if (existsSync(configPath)) {
103
+ try {
104
+ const content = await readFile(configPath, 'utf8')
105
+ const config = JSON.parse(content) as ContainerConfig
106
+ ports.push(config.port)
107
+ } catch {
108
+ // Skip invalid configs
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ return ports
115
+ }
116
+
117
+ /**
118
+ * Find an available port that's not in use by any process AND not assigned to any container
119
+ */
120
+ async findAvailablePortExcludingContainers(
121
+ preferredPort: number = defaults.port,
122
+ ): Promise<PortResult> {
123
+ const containerPorts = await this.getContainerPorts()
124
+
125
+ // First try the preferred port
126
+ if (
127
+ !containerPorts.includes(preferredPort) &&
128
+ (await this.isPortAvailable(preferredPort))
129
+ ) {
130
+ return {
131
+ port: preferredPort,
132
+ isDefault: preferredPort === defaults.port,
133
+ }
134
+ }
135
+
136
+ // Scan for available ports in the range
137
+ for (
138
+ let port = defaults.portRange.start;
139
+ port <= defaults.portRange.end;
140
+ port++
141
+ ) {
142
+ if (containerPorts.includes(port)) continue // Skip ports used by containers
143
+ if (port === preferredPort) continue // Already tried this one
144
+
145
+ if (await this.isPortAvailable(port)) {
146
+ return {
147
+ port,
148
+ isDefault: false,
149
+ }
150
+ }
151
+ }
152
+
153
+ throw new Error(
154
+ `No available ports found in range ${defaults.portRange.start}-${defaults.portRange.end}`,
155
+ )
156
+ }
82
157
  }
83
158
 
84
159
  export const portManager = new PortManager()
@@ -0,0 +1,499 @@
1
+ import { exec } from 'child_process'
2
+ import { promisify } from 'util'
3
+ import chalk from 'chalk'
4
+ import { createSpinner } from '@/cli/ui/spinner'
5
+ import { warning, error as themeError, success } from '@/cli/ui/theme'
6
+
7
+ const execAsync = promisify(exec)
8
+
9
+ export type BinaryInfo = {
10
+ command: string
11
+ version: string
12
+ path: string
13
+ packageManager?: string
14
+ isCompatible: boolean
15
+ requiredVersion?: string
16
+ }
17
+
18
+ export type PackageManager = {
19
+ name: string
20
+ checkCommand: string
21
+ installCommand: (binary: string) => string
22
+ updateCommand: (binary: string) => string
23
+ versionCheckCommand: (binary: string) => string
24
+ }
25
+
26
+ /**
27
+ * Detect which package manager is available on the system
28
+ */
29
+ export async function detectPackageManager(): Promise<PackageManager | null> {
30
+ const managers: PackageManager[] = [
31
+ {
32
+ name: 'brew',
33
+ checkCommand: 'brew --version',
34
+ installCommand: () =>
35
+ 'brew install postgresql@17 && brew link --overwrite postgresql@17',
36
+ updateCommand: () =>
37
+ 'brew link --overwrite postgresql@17 || brew install postgresql@17 && brew link --overwrite postgresql@17',
38
+ versionCheckCommand: () =>
39
+ 'brew info postgresql@17 | grep "postgresql@17:" | head -1',
40
+ },
41
+ {
42
+ name: 'apt',
43
+ checkCommand: 'apt --version',
44
+ installCommand: () =>
45
+ 'sudo apt update && sudo apt install -y postgresql-client',
46
+ updateCommand: () =>
47
+ 'sudo apt update && sudo apt upgrade -y postgresql-client',
48
+ versionCheckCommand: () => 'apt show postgresql-client | grep Version',
49
+ },
50
+ {
51
+ name: 'yum',
52
+ checkCommand: 'yum --version',
53
+ installCommand: () => 'sudo yum install -y postgresql',
54
+ updateCommand: () => 'sudo yum update -y postgresql',
55
+ versionCheckCommand: () => 'yum info postgresql | grep Version',
56
+ },
57
+ {
58
+ name: 'dnf',
59
+ checkCommand: 'dnf --version',
60
+ installCommand: () => 'sudo dnf install -y postgresql',
61
+ updateCommand: () => 'sudo dnf upgrade -y postgresql',
62
+ versionCheckCommand: () => 'dnf info postgresql | grep Version',
63
+ },
64
+ ]
65
+
66
+ for (const manager of managers) {
67
+ try {
68
+ await execAsync(manager.checkCommand)
69
+ return manager
70
+ } catch {
71
+ // Manager not available
72
+ }
73
+ }
74
+
75
+ return null
76
+ }
77
+
78
+ /**
79
+ * Get PostgreSQL version from pg_restore or psql
80
+ */
81
+ export async function getPostgresVersion(
82
+ binary: 'pg_restore' | 'psql',
83
+ ): Promise<string | null> {
84
+ try {
85
+ const { stdout } = await execAsync(`${binary} --version`)
86
+ const match = stdout.match(/(\d+\.\d+)/)
87
+ return match ? match[1] : null
88
+ } catch {
89
+ return null
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Find binary path using which/where command
95
+ */
96
+ export async function findBinaryPath(binary: string): Promise<string | null> {
97
+ try {
98
+ const command = process.platform === 'win32' ? 'where' : 'which'
99
+ const { stdout } = await execAsync(`${command} ${binary}`)
100
+ return stdout.trim().split('\n')[0] || null
101
+ } catch {
102
+ return null
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Find binary path with fallback to refresh PATH cache
108
+ */
109
+ export async function findBinaryPathFresh(
110
+ binary: string,
111
+ ): Promise<string | null> {
112
+ // Try normal lookup first
113
+ const path = await findBinaryPath(binary)
114
+ if (path) return path
115
+
116
+ // If not found, try to refresh PATH cache (especially after package updates)
117
+ try {
118
+ // Force shell to re-evaluate PATH
119
+ const shell = process.env.SHELL || '/bin/bash'
120
+ const { stdout } = await execWithTimeout(
121
+ `${shell} -c 'source ~/.${shell.endsWith('zsh') ? 'zshrc' : 'bashrc'} && which ${binary}'`,
122
+ )
123
+ return stdout.trim().split('\n')[0] || null
124
+ } catch {
125
+ return null
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Execute command with timeout
131
+ */
132
+ async function execWithTimeout(
133
+ command: string,
134
+ timeoutMs: number = 60000,
135
+ ): Promise<{ stdout: string; stderr: string }> {
136
+ return new Promise((resolve, reject) => {
137
+ const child = exec(
138
+ command,
139
+ { timeout: timeoutMs },
140
+ (error, stdout, stderr) => {
141
+ if (error) {
142
+ reject(error)
143
+ } else {
144
+ resolve({ stdout, stderr })
145
+ }
146
+ },
147
+ )
148
+
149
+ // Additional timeout safety
150
+ setTimeout(() => {
151
+ child.kill('SIGTERM')
152
+ reject(new Error(`Command timed out after ${timeoutMs}ms: ${command}`))
153
+ }, timeoutMs)
154
+ })
155
+ }
156
+
157
+ /**
158
+ * Parse dump file to get required PostgreSQL version
159
+ */
160
+ export async function getDumpRequiredVersion(
161
+ dumpPath: string,
162
+ ): Promise<string | null> {
163
+ try {
164
+ // Try to read pg_dump custom format header
165
+ const { stdout } = await execAsync(`file "${dumpPath}"`)
166
+ if (stdout.includes('PostgreSQL custom database dump')) {
167
+ // For custom format, we need to check the version in the dump
168
+ try {
169
+ const { stdout: hexdump } = await execAsync(
170
+ `hexdump -C "${dumpPath}" | head -5`,
171
+ )
172
+ // Look for version info in the header (simplified approach)
173
+ const versionMatch = hexdump.match(/(\d+)\.(\d+)/)
174
+ if (versionMatch) {
175
+ // If it's a recent dump, assume it needs the latest PostgreSQL
176
+ const majorVersion = parseInt(versionMatch[1])
177
+ if (majorVersion >= 15) {
178
+ return '15.0' // Minimum version for recent dumps
179
+ }
180
+ }
181
+ } catch {
182
+ // If hexdump fails, fall back to checking error patterns
183
+ }
184
+ }
185
+
186
+ // Fallback: if we can't determine, assume it needs a recent version
187
+ return '15.0'
188
+ } catch {
189
+ return null
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Check if a PostgreSQL version is compatible with a dump
195
+ */
196
+ export function isVersionCompatible(
197
+ currentVersion: string,
198
+ requiredVersion: string,
199
+ ): boolean {
200
+ const current = parseFloat(currentVersion)
201
+ const required = parseFloat(requiredVersion)
202
+
203
+ // Current version should be >= required version
204
+ // But not too far ahead (major version compatibility)
205
+ return current >= required && Math.floor(current) === Math.floor(required)
206
+ }
207
+
208
+ /**
209
+ * Get binary information including version and compatibility
210
+ */
211
+ export async function getBinaryInfo(
212
+ binary: 'pg_restore' | 'psql',
213
+ dumpPath?: string,
214
+ ): Promise<BinaryInfo | null> {
215
+ const path = await findBinaryPath(binary)
216
+ if (!path) {
217
+ return null
218
+ }
219
+
220
+ const version = await getPostgresVersion(binary)
221
+ if (!version) {
222
+ return null
223
+ }
224
+
225
+ let requiredVersion: string | undefined
226
+ let isCompatible = true
227
+
228
+ if (dumpPath) {
229
+ const dumpVersion = await getDumpRequiredVersion(dumpPath)
230
+ requiredVersion = dumpVersion || undefined
231
+ if (requiredVersion) {
232
+ isCompatible = isVersionCompatible(version, requiredVersion)
233
+ }
234
+ }
235
+
236
+ // Try to detect which package manager installed this binary
237
+ let packageManager: string | undefined
238
+ try {
239
+ if (process.platform === 'darwin') {
240
+ // On macOS, check if it's from Homebrew
241
+ const { stdout } = await execAsync(
242
+ 'brew list postgresql@* 2>/dev/null || brew list libpq 2>/dev/null || true',
243
+ )
244
+ if (stdout.includes('postgresql') || stdout.includes('libpq')) {
245
+ packageManager = 'brew'
246
+ }
247
+ } else {
248
+ // On Linux, check common package managers
249
+ try {
250
+ await execAsync('dpkg -S $(which pg_restore) 2>/dev/null')
251
+ packageManager = 'apt'
252
+ } catch {
253
+ try {
254
+ await execAsync('rpm -qf $(which pg_restore) 2>/dev/null')
255
+ packageManager = 'yum/dnf'
256
+ } catch {
257
+ // Could be from source or other installation method
258
+ }
259
+ }
260
+ }
261
+ } catch {
262
+ // Could not determine package manager
263
+ }
264
+
265
+ return {
266
+ command: binary,
267
+ version,
268
+ path,
269
+ packageManager,
270
+ isCompatible,
271
+ requiredVersion,
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Install PostgreSQL client tools
277
+ */
278
+ export async function installPostgresBinaries(): Promise<boolean> {
279
+ const spinner = createSpinner('Checking package manager...')
280
+ spinner.start()
281
+
282
+ const packageManager = await detectPackageManager()
283
+ if (!packageManager) {
284
+ spinner.fail('No supported package manager found')
285
+ console.log(themeError('Please install PostgreSQL client tools manually:'))
286
+ console.log(' macOS: brew install libpq')
287
+ console.log(' Ubuntu/Debian: sudo apt install postgresql-client')
288
+ console.log(' CentOS/RHEL/Fedora: sudo yum install postgresql')
289
+ return false
290
+ }
291
+
292
+ spinner.succeed(`Found package manager: ${packageManager.name}`)
293
+
294
+ const installSpinner = createSpinner(
295
+ `Installing PostgreSQL client tools with ${packageManager.name}...`,
296
+ )
297
+ installSpinner.start()
298
+
299
+ try {
300
+ await execWithTimeout(packageManager.installCommand('postgresql'), 120000) // 2 minute timeout
301
+ installSpinner.succeed('PostgreSQL client tools installed')
302
+ console.log(success('Installation completed successfully'))
303
+ return true
304
+ } catch (error: unknown) {
305
+ installSpinner.fail('Installation failed')
306
+ console.log(themeError('Failed to install PostgreSQL client tools'))
307
+ console.log(warning('Please install manually:'))
308
+ console.log(` ${packageManager.installCommand('postgresql')}`)
309
+ if (error instanceof Error) {
310
+ console.log(chalk.gray(`Error details: ${error.message}`))
311
+ }
312
+ return false
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Update individual PostgreSQL client tools to resolve conflicts
318
+ */
319
+ export async function updatePostgresClientTools(): Promise<boolean> {
320
+ const spinner = createSpinner('Updating PostgreSQL client tools...')
321
+ spinner.start()
322
+
323
+ const packageManager = await detectPackageManager()
324
+ if (!packageManager) {
325
+ spinner.fail('No supported package manager found')
326
+ return false
327
+ }
328
+
329
+ spinner.succeed(`Found package manager: ${packageManager.name}`)
330
+
331
+ try {
332
+ if (packageManager.name === 'brew') {
333
+ // Handle brew conflicts and dependency issues
334
+ const commands = [
335
+ 'brew unlink postgresql@14 2>/dev/null || true', // Unlink old version if exists
336
+ 'brew unlink postgresql@15 2>/dev/null || true', // Unlink other old versions
337
+ 'brew unlink postgresql@16 2>/dev/null || true',
338
+ 'brew link --overwrite postgresql@17', // Link postgresql@17 with overwrite
339
+ 'brew upgrade icu4c 2>/dev/null || true', // Fix ICU dependency issues
340
+ ]
341
+
342
+ for (const command of commands) {
343
+ await execWithTimeout(command, 60000)
344
+ }
345
+
346
+ spinner.succeed('PostgreSQL client tools updated')
347
+ console.log(success('Client tools successfully linked to PostgreSQL 17'))
348
+ console.log(chalk.gray('ICU dependencies have been updated'))
349
+ return true
350
+ } else {
351
+ // For other package managers, use the standard update
352
+ await execWithTimeout(packageManager.updateCommand('postgresql'), 120000)
353
+ spinner.succeed('PostgreSQL client tools updated')
354
+ console.log(success('Update completed successfully'))
355
+ return true
356
+ }
357
+ } catch (error: unknown) {
358
+ spinner.fail('Update failed')
359
+ console.log(themeError('Failed to update PostgreSQL client tools'))
360
+ console.log(warning('Please update manually:'))
361
+
362
+ if (packageManager.name === 'brew') {
363
+ console.log(chalk.yellow(' macOS:'))
364
+ console.log(
365
+ chalk.yellow(
366
+ ' brew unlink postgresql@14 postgresql@15 postgresql@16',
367
+ ),
368
+ )
369
+ console.log(chalk.yellow(' brew link --overwrite postgresql@17'))
370
+ console.log(
371
+ chalk.yellow(' brew upgrade icu4c # Fix ICU dependency issues'),
372
+ )
373
+ console.log(
374
+ chalk.gray(
375
+ ' This will update: pg_restore, pg_dump, psql, and fix dependency issues',
376
+ ),
377
+ )
378
+ } else {
379
+ console.log(` ${packageManager.updateCommand('postgresql')}`)
380
+ }
381
+
382
+ if (error instanceof Error) {
383
+ console.log(chalk.gray(`Error details: ${error.message}`))
384
+ }
385
+ return false
386
+ }
387
+ }
388
+ export async function updatePostgresBinaries(): Promise<boolean> {
389
+ const spinner = createSpinner('Checking package manager...')
390
+ spinner.start()
391
+
392
+ const packageManager = await detectPackageManager()
393
+ if (!packageManager) {
394
+ spinner.fail('No supported package manager found')
395
+ return false
396
+ }
397
+
398
+ spinner.succeed(`Found package manager: ${packageManager.name}`)
399
+
400
+ const updateSpinner = createSpinner(
401
+ `Updating PostgreSQL client tools with ${packageManager.name}...`,
402
+ )
403
+ updateSpinner.start()
404
+
405
+ try {
406
+ await execWithTimeout(packageManager.updateCommand('postgresql'), 120000) // 2 minute timeout
407
+ updateSpinner.succeed('PostgreSQL client tools updated')
408
+ console.log(success('Update completed successfully'))
409
+ return true
410
+ } catch (error: unknown) {
411
+ updateSpinner.fail('Update failed')
412
+ console.log(themeError('Failed to update PostgreSQL client tools'))
413
+ console.log(warning('Please update manually:'))
414
+ console.log(` ${packageManager.updateCommand('postgresql')}`)
415
+ if (error instanceof Error) {
416
+ console.log(chalk.gray(`Error details: ${error.message}`))
417
+ }
418
+ return false
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Ensure PostgreSQL binary is available and compatible
424
+ */
425
+ export async function ensurePostgresBinary(
426
+ binary: 'pg_restore' | 'psql',
427
+ dumpPath?: string,
428
+ options: { autoInstall?: boolean; autoUpdate?: boolean } = {},
429
+ ): Promise<{ success: boolean; info: BinaryInfo | null; action?: string }> {
430
+ const { autoInstall = true, autoUpdate = true } = options
431
+
432
+ console.log(
433
+ `[DEBUG] ensurePostgresBinary called for ${binary}, dumpPath: ${dumpPath}`,
434
+ )
435
+
436
+ // Check if binary exists
437
+ const info = await getBinaryInfo(binary, dumpPath)
438
+
439
+ console.log(`[DEBUG] getBinaryInfo result:`, info)
440
+
441
+ if (!info) {
442
+ if (!autoInstall) {
443
+ return { success: false, info: null, action: 'install_required' }
444
+ }
445
+
446
+ console.log(warning(`${binary} not found on your system`))
447
+ const success = await installPostgresBinaries()
448
+ if (!success) {
449
+ return { success: false, info: null, action: 'install_failed' }
450
+ }
451
+
452
+ // Check again after installation
453
+ const newInfo = await getBinaryInfo(binary, dumpPath)
454
+ if (!newInfo) {
455
+ return { success: false, info: null, action: 'install_failed' }
456
+ }
457
+
458
+ return { success: true, info: newInfo, action: 'installed' }
459
+ }
460
+
461
+ // Check version compatibility
462
+ if (dumpPath && !info.isCompatible) {
463
+ console.log(
464
+ `[DEBUG] Version incompatible: current=${info.version}, required=${info.requiredVersion}`,
465
+ )
466
+
467
+ if (!autoUpdate) {
468
+ return { success: false, info, action: 'update_required' }
469
+ }
470
+
471
+ console.log(
472
+ warning(
473
+ `Your ${binary} version (${info.version}) is incompatible with the dump file`,
474
+ ),
475
+ )
476
+ if (info.requiredVersion) {
477
+ console.log(
478
+ warning(`Required version: ${info.requiredVersion} or compatible`),
479
+ )
480
+ }
481
+
482
+ const success = await updatePostgresBinaries()
483
+ if (!success) {
484
+ return { success: false, info, action: 'update_failed' }
485
+ }
486
+
487
+ // Check again after update
488
+ const updatedInfo = await getBinaryInfo(binary, dumpPath)
489
+ if (!updatedInfo || !updatedInfo.isCompatible) {
490
+ console.log(`[DEBUG] Update failed or still incompatible:`, updatedInfo)
491
+ return { success: false, info: updatedInfo, action: 'update_failed' }
492
+ }
493
+
494
+ return { success: true, info: updatedInfo, action: 'updated' }
495
+ }
496
+
497
+ console.log(`[DEBUG] Binary is compatible, returning success`)
498
+ return { success: true, info, action: 'compatible' }
499
+ }
@@ -7,23 +7,23 @@ import type { ProcessResult, StatusResult } from '@/types'
7
7
 
8
8
  const execAsync = promisify(exec)
9
9
 
10
- export interface InitdbOptions {
10
+ export type InitdbOptions = {
11
11
  superuser?: string
12
12
  }
13
13
 
14
- export interface StartOptions {
14
+ export type StartOptions = {
15
15
  port?: number
16
16
  logFile?: string
17
17
  }
18
18
 
19
- export interface PsqlOptions {
19
+ export type PsqlOptions = {
20
20
  port: number
21
21
  database?: string
22
22
  user?: string
23
23
  command?: string
24
24
  }
25
25
 
26
- export interface PgRestoreOptions {
26
+ export type PgRestoreOptions = {
27
27
  port: number
28
28
  database: string
29
29
  user?: string