spindb 0.6.0 → 0.7.3

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 (38) hide show
  1. package/README.md +421 -294
  2. package/cli/commands/config.ts +7 -1
  3. package/cli/commands/connect.ts +1 -0
  4. package/cli/commands/create.ts +7 -7
  5. package/cli/commands/edit.ts +10 -0
  6. package/cli/commands/engines.ts +10 -188
  7. package/cli/commands/info.ts +7 -14
  8. package/cli/commands/list.ts +2 -9
  9. package/cli/commands/logs.ts +130 -0
  10. package/cli/commands/menu/backup-handlers.ts +798 -0
  11. package/cli/commands/menu/container-handlers.ts +832 -0
  12. package/cli/commands/menu/engine-handlers.ts +382 -0
  13. package/cli/commands/menu/index.ts +184 -0
  14. package/cli/commands/menu/shared.ts +26 -0
  15. package/cli/commands/menu/shell-handlers.ts +331 -0
  16. package/cli/commands/menu/sql-handlers.ts +197 -0
  17. package/cli/commands/menu/update-handlers.ts +94 -0
  18. package/cli/commands/run.ts +150 -0
  19. package/cli/commands/url.ts +19 -5
  20. package/cli/constants.ts +10 -0
  21. package/cli/helpers.ts +152 -0
  22. package/cli/index.ts +5 -2
  23. package/cli/ui/prompts.ts +3 -11
  24. package/config/defaults.ts +5 -29
  25. package/core/binary-manager.ts +2 -2
  26. package/core/container-manager.ts +3 -2
  27. package/core/dependency-manager.ts +0 -163
  28. package/core/error-handler.ts +0 -26
  29. package/core/platform-service.ts +60 -40
  30. package/core/start-with-retry.ts +3 -28
  31. package/core/transaction-manager.ts +0 -8
  32. package/engines/base-engine.ts +10 -0
  33. package/engines/mysql/binary-detection.ts +1 -1
  34. package/engines/mysql/index.ts +78 -2
  35. package/engines/postgresql/index.ts +49 -0
  36. package/package.json +1 -1
  37. package/types/index.ts +7 -4
  38. package/cli/commands/menu.ts +0 -2670
@@ -43,13 +43,6 @@ export type InstallResult = {
43
43
  error?: string
44
44
  }
45
45
 
46
- // =============================================================================
47
- // Package Manager Detection
48
- // =============================================================================
49
-
50
- /**
51
- * Detect which package manager is available on the current system
52
- */
53
46
  export async function detectPackageManager(): Promise<DetectedPackageManager | null> {
54
47
  const { platform } = platformService.getPlatformInfo()
55
48
 
@@ -81,13 +74,6 @@ export function getCurrentPlatform(): Platform {
81
74
  return platformService.getPlatformInfo().platform as Platform
82
75
  }
83
76
 
84
- // =============================================================================
85
- // Dependency Checking
86
- // =============================================================================
87
-
88
- /**
89
- * Check if a binary is installed and get its path
90
- */
91
77
  export async function findBinary(
92
78
  binary: string,
93
79
  ): Promise<{ path: string; version?: string } | null> {
@@ -164,13 +150,6 @@ export async function getAllMissingDependencies(): Promise<Dependency[]> {
164
150
  return statuses.filter((s) => !s.installed).map((s) => s.dependency)
165
151
  }
166
152
 
167
- // =============================================================================
168
- // Installation
169
- // =============================================================================
170
-
171
- /**
172
- * Check if stdin is a TTY (interactive terminal)
173
- */
174
153
  function hasTTY(): boolean {
175
154
  return process.stdin.isTTY === true
176
155
  }
@@ -362,13 +341,6 @@ export async function installAllDependencies(
362
341
  return results
363
342
  }
364
343
 
365
- // =============================================================================
366
- // Manual Installation Instructions
367
- // =============================================================================
368
-
369
- /**
370
- * Get manual installation instructions for a dependency
371
- */
372
344
  export function getManualInstallInstructions(
373
345
  dependency: Dependency,
374
346
  platform: Platform = getCurrentPlatform(),
@@ -376,188 +348,53 @@ export function getManualInstallInstructions(
376
348
  return dependency.manualInstall[platform] || []
377
349
  }
378
350
 
379
- /**
380
- * Get manual installation instructions for all missing dependencies of an engine
381
- */
382
- export function getEngineManualInstallInstructions(
383
- engine: string,
384
- missingDeps: Dependency[],
385
- platform: Platform = getCurrentPlatform(),
386
- ): string[] {
387
- // Since all deps usually come from the same package, just get instructions from the first one
388
- if (missingDeps.length === 0) return []
389
-
390
- return getManualInstallInstructions(missingDeps[0], platform)
391
- }
392
-
393
- // =============================================================================
394
- // High-Level API
395
- // =============================================================================
396
-
397
- export type DependencyCheckResult = {
398
- engine: string
399
- allInstalled: boolean
400
- installed: DependencyStatus[]
401
- missing: DependencyStatus[]
402
- }
403
-
404
- /**
405
- * Get a complete dependency report for an engine
406
- */
407
- export async function getDependencyReport(
408
- engine: string,
409
- ): Promise<DependencyCheckResult> {
410
- const statuses = await checkEngineDependencies(engine)
411
-
412
- return {
413
- engine,
414
- allInstalled: statuses.every((s) => s.installed),
415
- installed: statuses.filter((s) => s.installed),
416
- missing: statuses.filter((s) => !s.installed),
417
- }
418
- }
419
-
420
- /**
421
- * Get dependency reports for all engines
422
- */
423
- export async function getAllDependencyReports(): Promise<
424
- DependencyCheckResult[]
425
- > {
426
- const engines = ['postgresql', 'mysql']
427
- const reports = await Promise.all(
428
- engines.map((engine) => getDependencyReport(engine)),
429
- )
430
- return reports
431
- }
432
-
433
- // =============================================================================
434
- // usql (Enhanced Shell) Support
435
- // =============================================================================
436
-
437
- /**
438
- * Check if usql is installed
439
- */
440
351
  export async function isUsqlInstalled(): Promise<boolean> {
441
352
  const status = await checkDependency(usqlDependency)
442
353
  return status.installed
443
354
  }
444
355
 
445
- /**
446
- * Get usql dependency status
447
- */
448
- export async function getUsqlStatus(): Promise<DependencyStatus> {
449
- return checkDependency(usqlDependency)
450
- }
451
-
452
- /**
453
- * Install usql using the detected package manager
454
- */
455
356
  export async function installUsql(
456
357
  packageManager: DetectedPackageManager,
457
358
  ): Promise<InstallResult> {
458
359
  return installDependency(usqlDependency, packageManager)
459
360
  }
460
361
 
461
- /**
462
- * Get usql manual installation instructions
463
- */
464
362
  export function getUsqlManualInstructions(
465
363
  platform: Platform = getCurrentPlatform(),
466
364
  ): string[] {
467
365
  return getManualInstallInstructions(usqlDependency, platform)
468
366
  }
469
367
 
470
- /**
471
- * Get the usql dependency definition
472
- */
473
- export function getUsqlDependency(): Dependency {
474
- return usqlDependency
475
- }
476
-
477
- // =============================================================================
478
- // pgcli (PostgreSQL Enhanced Shell) Support
479
- // =============================================================================
480
-
481
- /**
482
- * Check if pgcli is installed
483
- */
484
368
  export async function isPgcliInstalled(): Promise<boolean> {
485
369
  const status = await checkDependency(pgcliDependency)
486
370
  return status.installed
487
371
  }
488
372
 
489
- /**
490
- * Get pgcli dependency status
491
- */
492
- export async function getPgcliStatus(): Promise<DependencyStatus> {
493
- return checkDependency(pgcliDependency)
494
- }
495
-
496
- /**
497
- * Install pgcli using the detected package manager
498
- */
499
373
  export async function installPgcli(
500
374
  packageManager: DetectedPackageManager,
501
375
  ): Promise<InstallResult> {
502
376
  return installDependency(pgcliDependency, packageManager)
503
377
  }
504
378
 
505
- /**
506
- * Get pgcli manual installation instructions
507
- */
508
379
  export function getPgcliManualInstructions(
509
380
  platform: Platform = getCurrentPlatform(),
510
381
  ): string[] {
511
382
  return getManualInstallInstructions(pgcliDependency, platform)
512
383
  }
513
384
 
514
- /**
515
- * Get the pgcli dependency definition
516
- */
517
- export function getPgcliDependency(): Dependency {
518
- return pgcliDependency
519
- }
520
-
521
- // =============================================================================
522
- // mycli (MySQL Enhanced Shell) Support
523
- // =============================================================================
524
-
525
- /**
526
- * Check if mycli is installed
527
- */
528
385
  export async function isMycliInstalled(): Promise<boolean> {
529
386
  const status = await checkDependency(mycliDependency)
530
387
  return status.installed
531
388
  }
532
389
 
533
- /**
534
- * Get mycli dependency status
535
- */
536
- export async function getMycliStatus(): Promise<DependencyStatus> {
537
- return checkDependency(mycliDependency)
538
- }
539
-
540
- /**
541
- * Install mycli using the detected package manager
542
- */
543
390
  export async function installMycli(
544
391
  packageManager: DetectedPackageManager,
545
392
  ): Promise<InstallResult> {
546
393
  return installDependency(mycliDependency, packageManager)
547
394
  }
548
395
 
549
- /**
550
- * Get mycli manual installation instructions
551
- */
552
396
  export function getMycliManualInstructions(
553
397
  platform: Platform = getCurrentPlatform(),
554
398
  ): string[] {
555
399
  return getManualInstallInstructions(mycliDependency, platform)
556
400
  }
557
-
558
- /**
559
- * Get the mycli dependency definition
560
- */
561
- export function getMycliDependency(): Dependency {
562
- return mycliDependency
563
- }
@@ -17,10 +17,6 @@ function getSpinDBRoot(): string {
17
17
  return join(home, '.spindb')
18
18
  }
19
19
 
20
- // =============================================================================
21
- // Types
22
- // =============================================================================
23
-
24
20
  export type ErrorSeverity = 'fatal' | 'error' | 'warning' | 'info'
25
21
 
26
22
  export type SpinDBErrorInfo = {
@@ -31,10 +27,6 @@ export type SpinDBErrorInfo = {
31
27
  context?: Record<string, unknown>
32
28
  }
33
29
 
34
- // =============================================================================
35
- // Error Codes
36
- // =============================================================================
37
-
38
30
  export const ErrorCodes = {
39
31
  // Port errors
40
32
  PORT_IN_USE: 'PORT_IN_USE',
@@ -83,10 +75,6 @@ export const ErrorCodes = {
83
75
 
84
76
  export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes]
85
77
 
86
- // =============================================================================
87
- // SpinDBError Class
88
- // =============================================================================
89
-
90
78
  export class SpinDBError extends Error {
91
79
  public readonly code: string
92
80
  public readonly severity: ErrorSeverity
@@ -131,13 +119,6 @@ export class SpinDBError extends Error {
131
119
  }
132
120
  }
133
121
 
134
- // =============================================================================
135
- // Logging Functions
136
- // =============================================================================
137
-
138
- /**
139
- * Get the path to the log file
140
- */
141
122
  function getLogPath(): string {
142
123
  return join(getSpinDBRoot(), 'spindb.log')
143
124
  }
@@ -264,13 +245,6 @@ export function logDebug(
264
245
  })
265
246
  }
266
247
 
267
- // =============================================================================
268
- // Error Creation Helpers
269
- // =============================================================================
270
-
271
- /**
272
- * Create a port-in-use error with helpful suggestion
273
- */
274
248
  export function createPortInUseError(port: number): SpinDBError {
275
249
  return new SpinDBError(
276
250
  ErrorCodes.PORT_IN_USE,
@@ -17,13 +17,47 @@ import { existsSync } from 'fs'
17
17
 
18
18
  const execAsync = promisify(exec)
19
19
 
20
- // =============================================================================
21
- // Types
22
- // =============================================================================
23
-
24
20
  export type Platform = 'darwin' | 'linux' | 'win32'
25
21
  export type Architecture = 'arm64' | 'x64'
26
22
 
23
+ /**
24
+ * Options for resolving home directory under sudo
25
+ */
26
+ export type ResolveHomeDirOptions = {
27
+ sudoUser: string | null
28
+ getentResult: string | null
29
+ platform: 'darwin' | 'linux'
30
+ defaultHome: string
31
+ }
32
+
33
+ /**
34
+ * Resolve the correct home directory, handling sudo scenarios.
35
+ * This is extracted as a pure function for testability.
36
+ *
37
+ * When running under sudo, we need to use the original user's home directory,
38
+ * not root's home. This prevents ~/.spindb from being created in /root/.
39
+ */
40
+ export function resolveHomeDir(options: ResolveHomeDirOptions): string {
41
+ const { sudoUser, getentResult, platform, defaultHome } = options
42
+
43
+ // Not running under sudo - use default
44
+ if (!sudoUser) {
45
+ return defaultHome
46
+ }
47
+
48
+ // Try to parse home from getent passwd output
49
+ // Format: username:password:uid:gid:gecos:home:shell
50
+ if (getentResult) {
51
+ const parts = getentResult.trim().split(':')
52
+ if (parts.length >= 6 && parts[5]) {
53
+ return parts[5]
54
+ }
55
+ }
56
+
57
+ // Fallback to platform-specific default
58
+ return platform === 'darwin' ? `/Users/${sudoUser}` : `/home/${sudoUser}`
59
+ }
60
+
27
61
  export type PlatformInfo = {
28
62
  platform: Platform
29
63
  arch: Architecture
@@ -54,10 +88,6 @@ export type PackageManagerInfo = {
54
88
  updateCommand: string
55
89
  }
56
90
 
57
- // =============================================================================
58
- // Abstract Base Class
59
- // =============================================================================
60
-
61
91
  export abstract class BasePlatformService {
62
92
  protected cachedPlatformInfo: PlatformInfo | null = null
63
93
 
@@ -165,33 +195,31 @@ export abstract class BasePlatformService {
165
195
  }
166
196
  }
167
197
 
168
- // =============================================================================
169
- // Darwin (macOS) Implementation
170
- // =============================================================================
171
-
172
198
  class DarwinPlatformService extends BasePlatformService {
173
199
  getPlatformInfo(): PlatformInfo {
174
200
  if (this.cachedPlatformInfo) return this.cachedPlatformInfo
175
201
 
176
202
  const sudoUser = process.env.SUDO_USER || null
177
- let homeDir: string
178
203
 
204
+ // Try to get home from getent passwd (may fail on macOS)
205
+ let getentResult: string | null = null
179
206
  if (sudoUser) {
180
- // Running under sudo - get original user's home
181
207
  try {
182
- const result = execSync(`getent passwd ${sudoUser}`, {
208
+ getentResult = execSync(`getent passwd ${sudoUser}`, {
183
209
  encoding: 'utf-8',
184
210
  })
185
- const parts = result.trim().split(':')
186
- homeDir =
187
- parts.length >= 6 && parts[5] ? parts[5] : `/Users/${sudoUser}`
188
211
  } catch {
189
- homeDir = `/Users/${sudoUser}`
212
+ // getent may not be available on macOS
190
213
  }
191
- } else {
192
- homeDir = homedir()
193
214
  }
194
215
 
216
+ const homeDir = resolveHomeDir({
217
+ sudoUser,
218
+ getentResult,
219
+ platform: 'darwin',
220
+ defaultHome: homedir(),
221
+ })
222
+
195
223
  this.cachedPlatformInfo = {
196
224
  platform: 'darwin',
197
225
  arch: osArch() as Architecture,
@@ -309,31 +337,31 @@ class DarwinPlatformService extends BasePlatformService {
309
337
  }
310
338
  }
311
339
 
312
- // =============================================================================
313
- // Linux Implementation
314
- // =============================================================================
315
-
316
340
  class LinuxPlatformService extends BasePlatformService {
317
341
  getPlatformInfo(): PlatformInfo {
318
342
  if (this.cachedPlatformInfo) return this.cachedPlatformInfo
319
343
 
320
344
  const sudoUser = process.env.SUDO_USER || null
321
- let homeDir: string
322
345
 
346
+ // Try to get home from getent passwd
347
+ let getentResult: string | null = null
323
348
  if (sudoUser) {
324
349
  try {
325
- const result = execSync(`getent passwd ${sudoUser}`, {
350
+ getentResult = execSync(`getent passwd ${sudoUser}`, {
326
351
  encoding: 'utf-8',
327
352
  })
328
- const parts = result.trim().split(':')
329
- homeDir = parts.length >= 6 && parts[5] ? parts[5] : `/home/${sudoUser}`
330
353
  } catch {
331
- homeDir = `/home/${sudoUser}`
354
+ // getent failed
332
355
  }
333
- } else {
334
- homeDir = homedir()
335
356
  }
336
357
 
358
+ const homeDir = resolveHomeDir({
359
+ sudoUser,
360
+ getentResult,
361
+ platform: 'linux',
362
+ defaultHome: homedir(),
363
+ })
364
+
337
365
  // Check if running in WSL
338
366
  let isWSL = false
339
367
  try {
@@ -494,10 +522,6 @@ class LinuxPlatformService extends BasePlatformService {
494
522
  }
495
523
  }
496
524
 
497
- // =============================================================================
498
- // Windows Implementation (Stub for future support)
499
- // =============================================================================
500
-
501
525
  class Win32PlatformService extends BasePlatformService {
502
526
  getPlatformInfo(): PlatformInfo {
503
527
  if (this.cachedPlatformInfo) return this.cachedPlatformInfo
@@ -608,10 +632,6 @@ class Win32PlatformService extends BasePlatformService {
608
632
  }
609
633
  }
610
634
 
611
- // =============================================================================
612
- // Factory and Singleton
613
- // =============================================================================
614
-
615
635
  /**
616
636
  * Create the appropriate platform service for the current OS
617
637
  */
@@ -9,12 +9,9 @@ import { portManager } from './port-manager'
9
9
  import { containerManager } from './container-manager'
10
10
  import { logWarning, logDebug } from './error-handler'
11
11
  import type { BaseEngine } from '../engines/base-engine'
12
+ import { getEngineDefaults } from '../config/defaults'
12
13
  import type { ContainerConfig } from '../types'
13
14
 
14
- // =============================================================================
15
- // Types
16
- // =============================================================================
17
-
18
15
  export type StartWithRetryOptions = {
19
16
  engine: BaseEngine
20
17
  config: ContainerConfig
@@ -29,13 +26,6 @@ export type StartWithRetryResult = {
29
26
  error?: Error
30
27
  }
31
28
 
32
- // =============================================================================
33
- // Port Error Detection
34
- // =============================================================================
35
-
36
- /**
37
- * Check if an error is a port-in-use error
38
- */
39
29
  function isPortInUseError(err: unknown): boolean {
40
30
  const message = (err as Error)?.message?.toLowerCase() || ''
41
31
  return (
@@ -47,10 +37,6 @@ function isPortInUseError(err: unknown): boolean {
47
37
  )
48
38
  }
49
39
 
50
- // =============================================================================
51
- // Start with Retry Implementation
52
- // =============================================================================
53
-
54
40
  /**
55
41
  * Start a database container with automatic port retry on conflict
56
42
  *
@@ -129,20 +115,9 @@ export async function startWithRetry(
129
115
  }
130
116
  }
131
117
 
132
- /**
133
- * Get the port range for an engine
134
- */
135
118
  function getEnginePortRange(engine: string): { start: number; end: number } {
136
- // Import defaults dynamically to avoid circular dependency
137
- // These are the standard ranges from config/defaults.ts
138
- if (engine === 'postgresql') {
139
- return { start: 5432, end: 5500 }
140
- }
141
- if (engine === 'mysql') {
142
- return { start: 3306, end: 3400 }
143
- }
144
- // Default fallback range
145
- return { start: 5432, end: 6000 }
119
+ const engineDefaults = getEngineDefaults(engine)
120
+ return engineDefaults.portRange
146
121
  }
147
122
 
148
123
  /**
@@ -7,19 +7,11 @@
7
7
 
8
8
  import { logError, logDebug, ErrorCodes } from './error-handler'
9
9
 
10
- // =============================================================================
11
- // Types
12
- // =============================================================================
13
-
14
10
  export type RollbackAction = {
15
11
  description: string
16
12
  execute: () => Promise<void>
17
13
  }
18
14
 
19
- // =============================================================================
20
- // Transaction Manager Implementation
21
- // =============================================================================
22
-
23
15
  /**
24
16
  * Manages a stack of rollback actions for transactional operations.
25
17
  *
@@ -151,4 +151,14 @@ export abstract class BaseEngine {
151
151
  outputPath: string,
152
152
  options: BackupOptions,
153
153
  ): Promise<BackupResult>
154
+
155
+ /**
156
+ * Run a SQL file or inline SQL statement against the database
157
+ * @param container - The container configuration
158
+ * @param options - Options including file path or SQL statement, and target database
159
+ */
160
+ abstract runScript(
161
+ container: ContainerConfig,
162
+ options: { file?: string; sql?: string; database?: string },
163
+ ): Promise<void>
154
164
  }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { exec } from 'child_process'
7
+ import { existsSync } from 'fs'
7
8
  import { promisify } from 'util'
8
9
  import { platformService } from '../../core/platform-service'
9
10
 
@@ -129,7 +130,6 @@ export async function detectInstalledVersions(): Promise<
129
130
  '/usr/local/opt/mysql@8.4/bin/mysqld',
130
131
  ]
131
132
 
132
- const { existsSync } = await import('fs')
133
133
  for (const path of homebrewPaths) {
134
134
  if (existsSync(path)) {
135
135
  const version = await getMysqlVersion(path)
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { spawn, exec } from 'child_process'
7
7
  import { promisify } from 'util'
8
- import { existsSync } from 'fs'
8
+ import { existsSync, createReadStream } from 'fs'
9
9
  import { mkdir, writeFile, readFile, unlink } from 'fs/promises'
10
10
  import { join } from 'path'
11
11
  import { BaseEngine } from '../base-engine'
@@ -289,8 +289,9 @@ export class MySQLEngine extends BaseEngine {
289
289
  // Write PID file manually since we're running detached
290
290
  try {
291
291
  await writeFile(pidFile, String(proc.pid))
292
- } catch {
292
+ } catch (error) {
293
293
  // PID file might be written by mysqld itself
294
+ logDebug(`Could not write PID file (mysqld may write it): ${error}`)
294
295
  }
295
296
 
296
297
  // Wait for MySQL to be ready
@@ -842,6 +843,81 @@ export class MySQLEngine extends BaseEngine {
842
843
  ): Promise<BackupResult> {
843
844
  return createBackup(container, outputPath, options)
844
845
  }
846
+
847
+ /**
848
+ * Run a SQL file or inline SQL statement against the database
849
+ * CLI wrapper: mysql -h 127.0.0.1 -P {port} -u root {db} < {file}
850
+ * CLI wrapper: mysql -h 127.0.0.1 -P {port} -u root {db} -e "{sql}"
851
+ */
852
+ async runScript(
853
+ container: ContainerConfig,
854
+ options: { file?: string; sql?: string; database?: string },
855
+ ): Promise<void> {
856
+ const { port } = container
857
+ const db = options.database || container.database || 'mysql'
858
+
859
+ const mysql = await getMysqlClientPath()
860
+ if (!mysql) {
861
+ throw new Error(
862
+ 'mysql client not found. Install MySQL client tools:\n' +
863
+ ' macOS: brew install mysql-client\n' +
864
+ ' Ubuntu/Debian: sudo apt install mysql-client',
865
+ )
866
+ }
867
+
868
+ const args = [
869
+ '-h',
870
+ '127.0.0.1',
871
+ '-P',
872
+ String(port),
873
+ '-u',
874
+ engineDef.superuser,
875
+ db,
876
+ ]
877
+
878
+ if (options.sql) {
879
+ // For inline SQL, use -e flag
880
+ args.push('-e', options.sql)
881
+ return new Promise((resolve, reject) => {
882
+ const proc = spawn(mysql, args, { stdio: 'inherit' })
883
+
884
+ proc.on('error', reject)
885
+ proc.on('close', (code) => {
886
+ if (code === 0) {
887
+ resolve()
888
+ } else {
889
+ reject(new Error(`mysql exited with code ${code}`))
890
+ }
891
+ })
892
+ })
893
+ } else if (options.file) {
894
+ // For file input, pipe the file to mysql stdin
895
+ return new Promise((resolve, reject) => {
896
+ const fileStream = createReadStream(options.file!)
897
+ const proc = spawn(mysql, args, {
898
+ stdio: ['pipe', 'inherit', 'inherit'],
899
+ })
900
+
901
+ fileStream.pipe(proc.stdin)
902
+
903
+ fileStream.on('error', (err) => {
904
+ proc.kill()
905
+ reject(err)
906
+ })
907
+
908
+ proc.on('error', reject)
909
+ proc.on('close', (code) => {
910
+ if (code === 0) {
911
+ resolve()
912
+ } else {
913
+ reject(new Error(`mysql exited with code ${code}`))
914
+ }
915
+ })
916
+ })
917
+ } else {
918
+ throw new Error('Either file or sql option must be provided')
919
+ }
920
+ }
845
921
  }
846
922
 
847
923
  export const mysqlEngine = new MySQLEngine()