spindb 0.11.2 → 0.12.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.
- package/README.md +47 -5
- package/cli/commands/menu/container-handlers.ts +29 -42
- package/cli/commands/menu/shell-handlers.ts +27 -13
- package/cli/commands/menu/sql-handlers.ts +12 -5
- package/cli/ui/prompts.ts +16 -29
- package/config/engine-defaults.ts +14 -0
- package/config/os-dependencies.ts +117 -0
- package/core/homebrew-version-manager.ts +7 -4
- package/engines/base-engine.ts +9 -0
- package/engines/index.ts +4 -0
- package/engines/mongodb/backup.ts +130 -0
- package/engines/mongodb/binary-detection.ts +250 -0
- package/engines/mongodb/index.ts +778 -0
- package/engines/mongodb/restore.ts +296 -0
- package/engines/mongodb/version-validator.ts +162 -0
- package/package.json +10 -8
- package/types/index.ts +11 -0
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
|
|
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 `
|
|
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
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
108
|
-
const isSQLite =
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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('▷')}
|
|
623
|
-
: chalk.gray(
|
|
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 (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
253
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
?
|
|
575
|
-
: 'Backup filename
|
|
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(
|
|
325
|
-
|
|
326
|
-
|
|
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,
|
package/engines/base-engine.ts
CHANGED
|
@@ -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
|
/**
|