spindb 0.11.2 → 0.12.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.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  **The first npm CLI for running local databases without Docker.**
9
9
 
10
- Spin up PostgreSQL, MySQL, and SQLite instances for local development. No Docker daemon, no container networking, no volume mounts. Just databases running on localhost, ready in seconds.
10
+ Spin up PostgreSQL, MySQL, SQLite, and MongoDB instances for local development. No Docker daemon, no container networking, no volume mounts. Just databases running on localhost, ready in seconds.
11
11
 
12
12
  ---
13
13
 
@@ -210,12 +210,50 @@ spindb connect mydb --litecli
210
210
 
211
211
  **Note:** Unlike server databases, SQLite databases don't need to be "started" or "stopped"—they're always available as long as the file exists.
212
212
 
213
+ #### MongoDB
214
+
215
+ | | |
216
+ |---|---|
217
+ | Versions | 6.0, 7.0, 8.0 |
218
+ | Default port | 27017 |
219
+ | Default user | None (no auth by default) |
220
+ | Binary source | System installation |
221
+
222
+ Like MySQL, SpinDB uses your system's MongoDB installation. MongoDB binaries aren't available as a single cross-platform download, so you'll need to install MongoDB via your package manager.
223
+
224
+ ```bash
225
+ # macOS
226
+ brew tap mongodb/brew
227
+ brew install mongodb-community mongosh mongodb-database-tools
228
+
229
+ # Ubuntu/Debian (follow MongoDB's official guide)
230
+ # https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/
231
+
232
+ # Windows (Chocolatey)
233
+ choco install mongodb mongodb-shell mongodb-database-tools
234
+
235
+ # Check if SpinDB can find MongoDB
236
+ spindb deps check --engine mongodb
237
+ ```
238
+
239
+ MongoDB uses JavaScript for queries instead of SQL. When using `spindb run`, pass JavaScript code:
240
+
241
+ ```bash
242
+ # Insert a document
243
+ spindb run mydb --sql "db.users.insertOne({name: 'Alice', email: 'alice@example.com'})"
244
+
245
+ # Query documents
246
+ spindb run mydb --sql "db.users.find().pretty()"
247
+
248
+ # Run a JavaScript file
249
+ spindb run mydb --file ./scripts/seed.js
250
+ ```
251
+
213
252
  ### Planned Engines
214
253
 
215
254
  | Engine | Type | Status |
216
255
  |--------|------|--------|
217
256
  | Redis | In-memory key-value | Planned for v1.2 |
218
- | MongoDB | Document database | Planned for v1.2 |
219
257
 
220
258
  ---
221
259
 
@@ -317,12 +355,16 @@ spindb connect mydb --install-mycli
317
355
  spindb connect mydb --install-tui
318
356
  ```
319
357
 
320
- #### `run` - Execute SQL
358
+ #### `run` - Execute SQL/scripts
321
359
 
322
360
  ```bash
323
361
  spindb run mydb script.sql # Run a SQL file
324
362
  spindb run mydb --sql "SELECT * FROM users" # Run inline SQL
325
363
  spindb run mydb seed.sql --database my_app # Target specific database
364
+
365
+ # MongoDB uses JavaScript instead of SQL
366
+ spindb run mydb seed.js # Run a JavaScript file
367
+ spindb run mydb --sql "db.users.find().pretty()" # Run inline JavaScript
326
368
  ```
327
369
 
328
370
  #### `url` - Get connection string
@@ -553,8 +595,8 @@ SpinDB supports enhanced database shells that provide features like auto-complet
553
595
  | PostgreSQL | `psql` | `pgcli` | `usql` |
554
596
  | MySQL | `mysql` | `mycli` | `usql` |
555
597
  | SQLite | `sqlite3` | `litecli` | `usql` |
598
+ | MongoDB | `mongosh` | - | `usql` |
556
599
  | Redis (planned) | `redis-cli` | `iredis` | - |
557
- | MongoDB (planned) | `mongosh` | - | - |
558
600
 
559
601
  **pgcli / mycli** provide:
560
602
  - Intelligent auto-completion (tables, columns, keywords)
@@ -587,7 +629,7 @@ SpinDB uses the term "container" loosely—there's no Docker involved. When you
587
629
  Each "container" is just:
588
630
  - A configuration file (`container.json`)
589
631
  - A data directory (`data/`)
590
- - A log file (`postgres.log` or `mysql.log`)
632
+ - A log file (`postgres.log`, `mysql.log`, or `mongodb.log`)
591
633
 
592
634
  Native processes mean instant startup and no virtualization overhead.
593
635
 
@@ -57,8 +57,6 @@ export async function handleCreate(): Promise<'main' | void> {
57
57
  let selectedEngine: string | null = null
58
58
  let selectedVersion: string | null = null
59
59
  let containerName: string | null = null
60
- let selectedPort: number | null = null
61
- let selectedDatabase: string | null = null
62
60
  let sqlitePath: string | undefined = undefined
63
61
 
64
62
  // Step 1: Engine selection (back returns to main menu)
@@ -71,7 +69,6 @@ export async function handleCreate(): Promise<'main' | void> {
71
69
 
72
70
  // Step 2: Version selection (back returns to engine)
73
71
  while (selectedVersion === null) {
74
- // selectedEngine is guaranteed non-null here (loop doesn't exit until it's set)
75
72
  const result = await promptVersion(selectedEngine!, { includeBack: true })
76
73
  if (result === MAIN_MENU_VALUE) return 'main'
77
74
  if (result === BACK_VALUE) {
@@ -91,49 +88,28 @@ export async function handleCreate(): Promise<'main' | void> {
91
88
  containerName = result
92
89
  }
93
90
 
94
- // Step 4: Database name (back returns to container name)
95
- while (selectedDatabase === null) {
96
- // containerName and selectedEngine are guaranteed non-null here
97
- const result = await promptDatabaseName(containerName!, selectedEngine!, {
98
- allowBack: true,
99
- })
100
- if (result === null) {
101
- containerName = null
102
- continue
103
- }
104
- selectedDatabase = result
105
- }
91
+ // At this point, all wizard values are guaranteed to be set
92
+ const engine = selectedEngine!
93
+ const version = selectedVersion!
94
+ const name = containerName!
95
+
96
+ // Step 4: Database name (defaults to container name, sanitized)
97
+ const database = await promptDatabaseName(name, engine)
106
98
 
107
- // Step 5: Port or SQLite path (back returns to database name)
108
- const isSQLite = selectedEngine === 'sqlite'
99
+ // Step 5: Port or SQLite path
100
+ const isSQLite = engine === 'sqlite'
101
+ let port: number
109
102
  if (isSQLite) {
110
103
  // SQLite doesn't need a port, but needs a path
111
- // containerName is guaranteed non-null here
112
- sqlitePath = await promptSqlitePath(containerName!)
113
- // promptSqlitePath doesn't support back yet, but file path is optional
114
- selectedPort = 0
104
+ sqlitePath = await promptSqlitePath(name)
105
+ port = 0
115
106
  } else {
116
- while (selectedPort === null) {
117
- // selectedEngine is guaranteed non-null here
118
- const engineDefaults = getEngineDefaults(selectedEngine!)
119
- const result = await promptPort(engineDefaults.defaultPort, {
120
- allowBack: true,
121
- })
122
- if (result === null) {
123
- selectedDatabase = null
124
- continue
125
- }
126
- selectedPort = result
127
- }
107
+ const engineDefaults = getEngineDefaults(engine)
108
+ port = await promptPort(engineDefaults.defaultPort)
128
109
  }
129
110
 
130
111
  // Now we have all values - proceed with container creation
131
- // At this point, all values are guaranteed to be set (wizard doesn't exit until they are)
132
- const engine = selectedEngine!
133
- const version = selectedVersion!
134
- const port = selectedPort!
135
- const database = selectedDatabase!
136
- let containerNameFinal = containerName!
112
+ let containerNameFinal = name
137
113
 
138
114
  console.log()
139
115
  console.log(header('Creating Database Container'))
@@ -186,6 +162,13 @@ export async function handleCreate(): Promise<'main' | void> {
186
162
  )
187
163
 
188
164
  if (!installed) {
165
+ console.log()
166
+ console.log(
167
+ uiWarning(
168
+ 'Container creation cancelled - required tools not installed.',
169
+ ),
170
+ )
171
+ await pressEnterToContinue()
189
172
  return
190
173
  }
191
174
 
@@ -196,6 +179,7 @@ export async function handleCreate(): Promise<'main' | void> {
196
179
  `Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
197
180
  ),
198
181
  )
182
+ await pressEnterToContinue()
199
183
  return
200
184
  }
201
185
 
@@ -615,12 +599,15 @@ export async function showContainerSubmenu(
615
599
  : 'Start container first',
616
600
  })
617
601
 
618
- // Run SQL - always enabled for SQLite (if file exists), server databases need to be running
602
+ // Run SQL/script - always enabled for SQLite (if file exists), server databases need to be running
619
603
  const canRunSql = isSQLite ? existsSync(config.database) : isRunning
604
+ // MongoDB uses JavaScript scripts, not SQL
605
+ const runScriptLabel =
606
+ config.engine === 'mongodb' ? 'Run script file' : 'Run SQL file'
620
607
  actionChoices.push({
621
608
  name: canRunSql
622
- ? `${chalk.yellow('▷')} Run SQL file`
623
- : chalk.gray('▷ Run SQL file'),
609
+ ? `${chalk.yellow('▷')} ${runScriptLabel}`
610
+ : chalk.gray(`▷ ${runScriptLabel}`),
624
611
  value: 'run-sql',
625
612
  disabled: canRunSql
626
613
  ? false
@@ -95,10 +95,10 @@ export async function handleOpenShell(containerName: string): Promise<void> {
95
95
 
96
96
  // Engine-specific shell names
97
97
  let defaultShellName: string
98
- let engineSpecificCli: string
98
+ let engineSpecificCli: string | null
99
99
  let engineSpecificInstalled: boolean
100
- let engineSpecificValue: ShellChoice
101
- let engineSpecificInstallValue: ShellChoice
100
+ let engineSpecificValue: ShellChoice | null
101
+ let engineSpecificInstallValue: ShellChoice | null
102
102
 
103
103
  if (config.engine === 'sqlite') {
104
104
  defaultShellName = 'sqlite3'
@@ -112,6 +112,13 @@ export async function handleOpenShell(containerName: string): Promise<void> {
112
112
  engineSpecificInstalled = mycliInstalled
113
113
  engineSpecificValue = 'mycli'
114
114
  engineSpecificInstallValue = 'install-mycli'
115
+ } else if (config.engine === 'mongodb') {
116
+ defaultShellName = 'mongosh'
117
+ // mongosh IS the enhanced shell for MongoDB (no separate enhanced CLI like pgcli/mycli)
118
+ engineSpecificCli = null
119
+ engineSpecificInstalled = false
120
+ engineSpecificValue = null
121
+ engineSpecificInstallValue = null
115
122
  } else {
116
123
  defaultShellName = 'psql'
117
124
  engineSpecificCli = 'pgcli'
@@ -129,16 +136,19 @@ export async function handleOpenShell(containerName: string): Promise<void> {
129
136
  },
130
137
  ]
131
138
 
132
- if (engineSpecificInstalled) {
133
- choices.push({
134
- name: `⚡ Use ${engineSpecificCli} (enhanced features, recommended)`,
135
- value: engineSpecificValue,
136
- })
137
- } else {
138
- choices.push({
139
- name: `↓ Install ${engineSpecificCli} (enhanced features, recommended)`,
140
- value: engineSpecificInstallValue,
141
- })
139
+ // Only show engine-specific CLI option if one exists (MongoDB's mongosh IS the default)
140
+ if (engineSpecificCli !== null) {
141
+ if (engineSpecificInstalled) {
142
+ choices.push({
143
+ name: `⚡ Use ${engineSpecificCli} (enhanced features, recommended)`,
144
+ value: engineSpecificValue!,
145
+ })
146
+ } else {
147
+ choices.push({
148
+ name: `↓ Install ${engineSpecificCli} (enhanced features, recommended)`,
149
+ value: engineSpecificInstallValue!,
150
+ })
151
+ }
142
152
  }
143
153
 
144
154
  // usql supports SQLite too
@@ -367,6 +377,10 @@ async function launchShell(
367
377
  config.database,
368
378
  ]
369
379
  installHint = 'brew install mysql-client'
380
+ } else if (config.engine === 'mongodb') {
381
+ shellCmd = 'mongosh'
382
+ shellArgs = [connectionString]
383
+ installHint = 'brew install mongosh'
370
384
  } else {
371
385
  shellCmd = 'psql'
372
386
  shellArgs = [connectionString]
@@ -56,6 +56,11 @@ export async function handleRunSql(containerName: string): Promise<void> {
56
56
  // Strip quotes that terminals add when drag-and-dropping files
57
57
  const stripQuotes = (path: string) => path.replace(/^['"]|['"]$/g, '').trim()
58
58
 
59
+ // MongoDB uses JavaScript scripts, not SQL
60
+ const isMongoDB = config.engine === 'mongodb'
61
+ const scriptType = isMongoDB ? 'Script' : 'SQL'
62
+ const scriptTypeLower = isMongoDB ? 'script' : 'SQL'
63
+
59
64
  // Prompt for file path (empty input = go back)
60
65
  console.log(
61
66
  chalk.gray(
@@ -68,7 +73,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
68
73
  {
69
74
  type: 'input',
70
75
  name: 'filePath',
71
- message: 'SQL file path:',
76
+ message: `${scriptType} file path:`,
72
77
  validate: (input: string) => {
73
78
  if (!input) return true // Empty = go back
74
79
  const cleanPath = stripQuotes(input)
@@ -90,14 +95,16 @@ export async function handleRunSql(containerName: string): Promise<void> {
90
95
  if (databases.length > 1) {
91
96
  databaseName = await promptDatabaseSelect(
92
97
  databases,
93
- 'Select database to run SQL against:',
98
+ `Select database to run ${scriptTypeLower} against:`,
94
99
  )
95
100
  } else {
96
101
  databaseName = databases[0]
97
102
  }
98
103
 
99
104
  console.log()
100
- console.log(uiInfo(`Running SQL file against "${databaseName}"...`))
105
+ console.log(
106
+ uiInfo(`Running ${scriptTypeLower} file against "${databaseName}"...`),
107
+ )
101
108
  console.log()
102
109
 
103
110
  try {
@@ -106,11 +113,11 @@ export async function handleRunSql(containerName: string): Promise<void> {
106
113
  database: databaseName,
107
114
  })
108
115
  console.log()
109
- console.log(uiSuccess('SQL file executed successfully'))
116
+ console.log(uiSuccess(`${scriptType} file executed successfully`))
110
117
  } catch (error) {
111
118
  const e = error as Error
112
119
  console.log()
113
- console.log(uiError(`SQL execution failed: ${e.message}`))
120
+ console.log(uiError(`${scriptType} execution failed: ${e.message}`))
114
121
  }
115
122
 
116
123
  console.log()
package/cli/ui/prompts.ts CHANGED
@@ -38,15 +38,11 @@ export async function promptContainerName(
38
38
  defaultName?: string,
39
39
  options?: { allowBack?: boolean },
40
40
  ): Promise<string | null> {
41
- const message = options?.allowBack
42
- ? 'Container name (empty to go back):'
43
- : 'Container name:'
44
-
45
41
  const { name } = await inquirer.prompt<{ name: string }>([
46
42
  {
47
43
  type: 'input',
48
44
  name: 'name',
49
- message,
45
+ message: 'Container name:',
50
46
  default: options?.allowBack ? undefined : defaultName,
51
47
  validate: (input: string) => {
52
48
  if (options?.allowBack && !input) return true // Allow empty for back
@@ -237,41 +233,28 @@ export async function promptVersion(
237
233
  /**
238
234
  * Prompt for port
239
235
  * @param defaultPort - Default port number
240
- * @param options.allowBack - Allow empty input to go back (returns null)
241
236
  */
242
- export function promptPort(
243
- defaultPort?: number,
244
- options?: { allowBack?: false },
245
- ): Promise<number>
246
- export function promptPort(
247
- defaultPort: number | undefined,
248
- options: { allowBack: true },
249
- ): Promise<number | null>
250
237
  export async function promptPort(
251
238
  defaultPort: number = defaults.port,
252
- options?: { allowBack?: boolean },
253
- ): Promise<number | null> {
254
- const message = options?.allowBack ? 'Port (empty to go back):' : 'Port:'
255
-
256
- const { port } = await inquirer.prompt<{ port: string }>([
239
+ ): Promise<number> {
240
+ const { port } = await inquirer.prompt<{ port: number }>([
257
241
  {
258
242
  type: 'input',
259
243
  name: 'port',
260
- message,
261
- default: options?.allowBack ? undefined : String(defaultPort),
244
+ message: 'Port:',
245
+ default: String(defaultPort),
262
246
  validate: (input: string) => {
263
- if (options?.allowBack && !input) return true // Allow empty for back
264
247
  const num = parseInt(input, 10)
265
248
  if (isNaN(num) || num < 1 || num > 65535) {
266
249
  return 'Port must be a number between 1 and 65535'
267
250
  }
268
251
  return true
269
252
  },
253
+ filter: (input: string) => parseInt(input, 10),
270
254
  },
271
255
  ])
272
256
 
273
- if (options?.allowBack && !port) return null
274
- return parseInt(port, 10)
257
+ return port
275
258
  }
276
259
 
277
260
  /**
@@ -405,15 +388,18 @@ export async function promptDatabaseName(
405
388
  // MySQL uses "schema" terminology (database and schema are synonymous)
406
389
  const baseLabel =
407
390
  engine === 'mysql' ? 'Database (schema) name' : 'Database name'
408
- const label = options?.allowBack
409
- ? `${baseLabel} (empty to go back):`
410
- : `${baseLabel}:`
411
391
 
412
392
  // Sanitize the default name to ensure it's valid
413
393
  const sanitizedDefault = defaultName
414
394
  ? sanitizeDatabaseName(defaultName)
415
395
  : undefined
416
396
 
397
+ // When allowBack is true, show the default in the message (since we can't use inquirer's default)
398
+ const label =
399
+ options?.allowBack && sanitizedDefault
400
+ ? `${baseLabel} [${sanitizedDefault}]:`
401
+ : `${baseLabel}:`
402
+
417
403
  const { database } = await inquirer.prompt<{ database: string }>([
418
404
  {
419
405
  type: 'input',
@@ -570,9 +556,10 @@ export async function promptBackupFilename(
570
556
  defaultName: string,
571
557
  options?: { allowBack?: boolean },
572
558
  ): Promise<string | null> {
559
+ // Show the default in the message when allowBack is true
573
560
  const message = options?.allowBack
574
- ? 'Backup filename (without extension, empty to go back):'
575
- : 'Backup filename (without extension):'
561
+ ? `Backup filename [${defaultName}]:`
562
+ : 'Backup filename:'
576
563
 
577
564
  const { filename } = await inquirer.prompt<{ filename: string }>([
578
565
  {
@@ -73,6 +73,20 @@ export const engineDefaults: Record<string, EngineDefaults> = {
73
73
  clientTools: ['sqlite3'],
74
74
  maxConnections: 0, // N/A - file-based
75
75
  },
76
+ mongodb: {
77
+ defaultVersion: '8.0',
78
+ defaultPort: 27017,
79
+ portRange: { start: 27017, end: 27100 },
80
+ supportedVersions: ['6.0', '7.0', '8.0'],
81
+ latestVersion: '8.0',
82
+ superuser: '', // No auth by default for local dev
83
+ connectionScheme: 'mongodb',
84
+ logFileName: 'mongodb.log',
85
+ pidFileName: 'mongod.pid',
86
+ dataSubdir: 'data',
87
+ clientTools: ['mongosh', 'mongodump', 'mongorestore'],
88
+ maxConnections: 0, // Not applicable for MongoDB
89
+ },
76
90
  }
77
91
 
78
92
  /**
@@ -411,6 +411,122 @@ const sqliteDependencies: EngineDependencies = {
411
411
  ],
412
412
  }
413
413
 
414
+ // =============================================================================
415
+ // MongoDB Dependencies
416
+ // =============================================================================
417
+
418
+ const mongodbDependencies: EngineDependencies = {
419
+ engine: 'mongodb',
420
+ displayName: 'MongoDB',
421
+ dependencies: [
422
+ {
423
+ name: 'mongod',
424
+ binary: 'mongod',
425
+ description: 'MongoDB server daemon',
426
+ packages: {
427
+ brew: {
428
+ package: 'mongodb/brew/mongodb-community',
429
+ preInstall: ['brew tap mongodb/brew'],
430
+ },
431
+ // MongoDB requires their own apt repository, not available in default repos
432
+ choco: { package: 'mongodb' },
433
+ winget: { package: 'MongoDB.Server' },
434
+ scoop: { package: 'mongodb' },
435
+ },
436
+ manualInstall: {
437
+ darwin: [
438
+ 'Install Homebrew: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
439
+ 'Add MongoDB tap: brew tap mongodb/brew',
440
+ 'Then run: brew install mongodb-community',
441
+ 'For specific versions: brew install mongodb-community@7.0',
442
+ ],
443
+ linux: [
444
+ 'MongoDB requires adding their official repository.',
445
+ 'Ubuntu/Debian: Follow https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/',
446
+ 'RHEL/CentOS: Follow https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/',
447
+ ],
448
+ win32: [
449
+ 'Using Chocolatey: choco install mongodb',
450
+ 'Using winget: winget install MongoDB.Server',
451
+ 'Or download from: https://www.mongodb.com/try/download/community',
452
+ ],
453
+ },
454
+ },
455
+ {
456
+ name: 'mongosh',
457
+ binary: 'mongosh',
458
+ description: 'MongoDB Shell (modern interactive shell)',
459
+ packages: {
460
+ brew: { package: 'mongosh' },
461
+ choco: { package: 'mongodb-shell' },
462
+ winget: { package: 'MongoDB.Shell' },
463
+ scoop: { package: 'mongosh' },
464
+ },
465
+ manualInstall: {
466
+ darwin: [
467
+ 'Install with Homebrew: brew install mongosh',
468
+ 'Or download from: https://www.mongodb.com/try/download/shell',
469
+ ],
470
+ linux: [
471
+ 'Download from: https://www.mongodb.com/try/download/shell',
472
+ 'Or install via npm: npm install -g mongosh',
473
+ ],
474
+ win32: [
475
+ 'Using Chocolatey: choco install mongodb-shell',
476
+ 'Using winget: winget install MongoDB.Shell',
477
+ 'Or download from: https://www.mongodb.com/try/download/shell',
478
+ ],
479
+ },
480
+ },
481
+ {
482
+ name: 'mongodump',
483
+ binary: 'mongodump',
484
+ description: 'MongoDB database backup utility',
485
+ packages: {
486
+ brew: { package: 'mongodb-database-tools' },
487
+ choco: { package: 'mongodb-database-tools' },
488
+ },
489
+ manualInstall: {
490
+ darwin: [
491
+ 'Install with Homebrew: brew install mongodb-database-tools',
492
+ 'Or download from: https://www.mongodb.com/try/download/database-tools',
493
+ ],
494
+ linux: [
495
+ 'Download from: https://www.mongodb.com/try/download/database-tools',
496
+ 'Extract and add to PATH',
497
+ ],
498
+ win32: [
499
+ 'Using Chocolatey: choco install mongodb-database-tools',
500
+ 'Or download from: https://www.mongodb.com/try/download/database-tools',
501
+ ],
502
+ },
503
+ },
504
+ {
505
+ name: 'mongorestore',
506
+ binary: 'mongorestore',
507
+ description: 'MongoDB database restore utility',
508
+ packages: {
509
+ brew: { package: 'mongodb-database-tools' },
510
+ choco: { package: 'mongodb-database-tools' },
511
+ },
512
+ manualInstall: {
513
+ darwin: [
514
+ 'Install with Homebrew: brew install mongodb-database-tools',
515
+ 'Or download from: https://www.mongodb.com/try/download/database-tools',
516
+ ],
517
+ linux: [
518
+ 'Download from: https://www.mongodb.com/try/download/database-tools',
519
+ 'Extract and add to PATH',
520
+ ],
521
+ win32: [
522
+ 'Using Chocolatey: choco install mongodb-database-tools',
523
+ 'Or download from: https://www.mongodb.com/try/download/database-tools',
524
+ ],
525
+ },
526
+ },
527
+ ],
528
+ }
529
+
414
530
  // =============================================================================
415
531
  // Optional Tools (engine-agnostic)
416
532
  // =============================================================================
@@ -543,6 +659,7 @@ export const engineDependencies: EngineDependencies[] = [
543
659
  postgresqlDependencies,
544
660
  mysqlDependencies,
545
661
  sqliteDependencies,
662
+ mongodbDependencies,
546
663
  ]
547
664
 
548
665
  /**
@@ -321,10 +321,13 @@ async function switchVersionLinux(
321
321
  }
322
322
 
323
323
  // No action required - Linux uses versioned paths directly, no symlink switching needed
324
- logDebug('Linux: Version verified, no switching required (using direct path)', {
325
- version: targetMajor,
326
- binPath: target.binPath,
327
- })
324
+ logDebug(
325
+ 'Linux: Version verified, no switching required (using direct path)',
326
+ {
327
+ version: targetMajor,
328
+ binPath: target.binPath,
329
+ },
330
+ )
328
331
 
329
332
  return {
330
333
  success: true,
@@ -105,6 +105,15 @@ export abstract class BaseEngine {
105
105
  throw new Error('mysqladmin not found')
106
106
  }
107
107
 
108
+ /**
109
+ * Get the path to the mongosh client if available
110
+ * Default implementation throws; engines that can provide a bundled or
111
+ * configured mongosh should override this method.
112
+ */
113
+ async getMongoshPath(): Promise<string> {
114
+ throw new Error('mongosh not found')
115
+ }
116
+
108
117
  /**
109
118
  * Open an interactive shell/CLI connection
110
119
  */
package/engines/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { postgresqlEngine } from './postgresql'
2
2
  import { mysqlEngine } from './mysql'
3
3
  import { sqliteEngine } from './sqlite'
4
+ import { mongodbEngine } from './mongodb'
4
5
  import type { BaseEngine } from './base-engine'
5
6
  import type { EngineInfo } from '../types'
6
7
 
@@ -18,6 +19,9 @@ export const engines: Record<string, BaseEngine> = {
18
19
  // SQLite and aliases
19
20
  sqlite: sqliteEngine,
20
21
  lite: sqliteEngine,
22
+ // MongoDB and aliases
23
+ mongodb: mongodbEngine,
24
+ mongo: mongodbEngine,
21
25
  }
22
26
 
23
27
  /**