spindb 0.24.0 → 0.26.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 +53 -14
- package/cli/commands/engines.ts +89 -1
- package/cli/commands/menu/backup-handlers.ts +19 -0
- package/cli/commands/menu/container-handlers.ts +4 -2
- package/cli/commands/menu/shell-handlers.ts +52 -2
- package/cli/commands/menu/sql-handlers.ts +7 -1
- package/cli/constants.ts +4 -0
- package/cli/helpers.ts +144 -0
- package/cli/index.ts +1 -1
- package/config/backup-formats.ts +28 -0
- package/config/engine-defaults.ts +26 -0
- package/config/engines.json +32 -0
- package/core/config-manager.ts +5 -0
- package/core/container-manager.ts +10 -4
- package/core/dependency-manager.ts +4 -0
- package/engines/base-engine.ts +16 -0
- package/engines/cockroachdb/backup.ts +363 -0
- package/engines/cockroachdb/binary-manager.ts +45 -0
- package/engines/cockroachdb/binary-urls.ts +37 -0
- package/engines/cockroachdb/cli-utils.ts +384 -0
- package/engines/cockroachdb/hostdb-releases.ts +111 -0
- package/engines/cockroachdb/index.ts +1052 -0
- package/engines/cockroachdb/restore.ts +448 -0
- package/engines/cockroachdb/version-maps.ts +42 -0
- package/engines/index.ts +8 -0
- package/engines/surrealdb/backup.ts +122 -0
- package/engines/surrealdb/binary-manager.ts +45 -0
- package/engines/surrealdb/binary-urls.ts +37 -0
- package/engines/surrealdb/cli-utils.ts +175 -0
- package/engines/surrealdb/hostdb-releases.ts +111 -0
- package/engines/surrealdb/index.ts +949 -0
- package/engines/surrealdb/restore.ts +297 -0
- package/engines/surrealdb/version-maps.ts +41 -0
- package/package.json +3 -1
- package/types/index.ts +18 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**One CLI for all your local databases.**
|
|
9
9
|
|
|
10
|
-
SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for
|
|
10
|
+
SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for 15 different database engines—all from a single command-line interface. No Docker, no VMs, no platform-specific installers. Just databases, running natively on your machine.
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
npm install -g spindb
|
|
@@ -48,7 +48,7 @@ One consistent interface across SQL databases, document stores, key-value stores
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# Same commands work for ANY database
|
|
51
|
-
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb]
|
|
51
|
+
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb|cockroachdb|surrealdb]
|
|
52
52
|
spindb start mydb
|
|
53
53
|
spindb connect mydb
|
|
54
54
|
spindb backup mydb
|
|
@@ -70,7 +70,7 @@ spindb run mydb -c "SELECT * FROM system.tables" # ClickHouse
|
|
|
70
70
|
|
|
71
71
|
## Platform Coverage
|
|
72
72
|
|
|
73
|
-
SpinDB works across **
|
|
73
|
+
SpinDB works across **15 database engines** and **5 platform architectures** with a **single, consistent API**.
|
|
74
74
|
|
|
75
75
|
| Database | macOS ARM64 | macOS Intel | Linux x64 | Linux ARM64 | Windows x64 |
|
|
76
76
|
|----------|:-----------:|:-----------:|:---------:|:-----------:|:-----------:|
|
|
@@ -87,8 +87,10 @@ SpinDB works across **13 database engines** and **5 platform architectures** wit
|
|
|
87
87
|
| 🧭 **Qdrant** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
88
88
|
| 🔍 **Meilisearch** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
89
89
|
| 🛋 **CouchDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
90
|
+
| 🪳 **CockroachDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
91
|
+
| 🌀 **SurrealDB** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
90
92
|
|
|
91
|
-
**
|
|
93
|
+
**73 combinations. One CLI. Zero configuration.**
|
|
92
94
|
|
|
93
95
|
---
|
|
94
96
|
|
|
@@ -166,13 +168,13 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
166
168
|
|
|
167
169
|
| Feature | SpinDB | Docker | DBngin | Postgres.app | XAMPP |
|
|
168
170
|
|---------|--------|--------|--------|--------------|-------|
|
|
171
|
+
| **All database types unified** | ✅ 15 engines | ❌ | ❌ | ❌ | ❌ |
|
|
169
172
|
| No Docker required | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
170
|
-
| Multiple DB engines | ✅ 13 engines | ✅ Unlimited | ✅ 3 engines | ❌ PostgreSQL only | ⚠️ MySQL only |
|
|
171
173
|
| CLI-first | ✅ | ✅ | ❌ GUI-first | ❌ GUI-first | ❌ GUI-first |
|
|
172
|
-
| Multiple versions | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
174
|
+
| Multiple versions side-by-side | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
173
175
|
| Clone databases | ✅ | Manual | ✅ | ❌ | ❌ |
|
|
174
176
|
| Backup/restore built-in | ✅ | Manual | ✅ | ❌ | ❌ |
|
|
175
|
-
| Low resource usage | ✅ Native | ❌ VM
|
|
177
|
+
| Low resource usage | ✅ Native | ❌ VM overhead | ✅ Native | ✅ Native | ✅ Native |
|
|
176
178
|
| Linux support | ✅ | ✅ | ❌ | ❌ | ✅ |
|
|
177
179
|
| ARM64 support | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
178
180
|
| Free for commercial use | ❌ | ⚠️ Paid for orgs | ✅ | ✅ | ✅ |
|
|
@@ -181,7 +183,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
181
183
|
|
|
182
184
|
## Supported Databases
|
|
183
185
|
|
|
184
|
-
SpinDB supports **
|
|
186
|
+
SpinDB supports **15 database engines** with **multiple versions** for each:
|
|
185
187
|
|
|
186
188
|
| Engine | Type | Versions | Default Port | Query Language |
|
|
187
189
|
|--------|------|----------|--------------|----------------|
|
|
@@ -198,10 +200,12 @@ SpinDB supports **13 database engines** with **multiple versions** for each:
|
|
|
198
200
|
| 🧭 **Qdrant** | Vector Search | 1 | 6333 (HTTP), 6334 (gRPC) | REST API |
|
|
199
201
|
| 🔍 **Meilisearch** | Full-Text Search | 1 | 7700 | REST API |
|
|
200
202
|
| 🛋 **CouchDB** | Document Store | 3 | 5984 | REST API |
|
|
203
|
+
| 🪳 **CockroachDB** | Distributed SQL | 25 | 26257 | SQL (PostgreSQL-compatible) |
|
|
204
|
+
| 🌀 **SurrealDB** | Multi-Model | 2 | 8000 | SurrealQL |
|
|
201
205
|
|
|
202
206
|
### Engine Categories
|
|
203
207
|
|
|
204
|
-
**Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, FerretDB, Redis, Valkey, ClickHouse, Qdrant, Meilisearch, CouchDB):
|
|
208
|
+
**Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, FerretDB, Redis, Valkey, ClickHouse, Qdrant, Meilisearch, CouchDB, CockroachDB, SurrealDB):
|
|
205
209
|
- Start/stop server processes
|
|
206
210
|
- Bind to localhost ports
|
|
207
211
|
- Data stored in `~/.spindb/containers/{engine}/{name}/`
|
|
@@ -427,6 +431,7 @@ Databases run as **native processes**, and **data persists across restarts**. Wh
|
|
|
427
431
|
| Redis | RDB snapshots (periodic) | May lose ~60 seconds on unexpected crash |
|
|
428
432
|
| Valkey | RDB snapshots (periodic) | May lose ~60 seconds on unexpected crash |
|
|
429
433
|
| ClickHouse | MergeTree storage | Committed transactions survive crashes |
|
|
434
|
+
| CockroachDB | Raft consensus | Strongly consistent, distributed replication |
|
|
430
435
|
|
|
431
436
|
---
|
|
432
437
|
|
|
@@ -583,6 +588,31 @@ curl http://127.0.0.1:6333/collections
|
|
|
583
588
|
**Query interface:** REST API (no CLI shell - use curl or API clients)
|
|
584
589
|
**Tools:** `qdrant` (included)
|
|
585
590
|
|
|
591
|
+
### CockroachDB 🪳
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
# Create CockroachDB database (distributed SQL)
|
|
595
|
+
spindb create cluster --engine cockroachdb
|
|
596
|
+
spindb start cluster
|
|
597
|
+
|
|
598
|
+
# PostgreSQL-compatible SQL
|
|
599
|
+
spindb run cluster -c "CREATE TABLE users (id INT PRIMARY KEY, name STRING)"
|
|
600
|
+
spindb run cluster -c "SELECT * FROM users"
|
|
601
|
+
|
|
602
|
+
# Connect with cockroach sql shell
|
|
603
|
+
spindb connect cluster
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Version:** 25 (25.4.2)
|
|
607
|
+
**Platforms:** macOS, Linux, Windows (all platforms)
|
|
608
|
+
**Ports:** 26257 (SQL), HTTP Admin UI on SQL port + 1 (default 26258)
|
|
609
|
+
**Query language:** SQL (PostgreSQL-compatible)
|
|
610
|
+
**Tools:** `cockroach` (included)
|
|
611
|
+
**Default user:** `root`
|
|
612
|
+
**Default database:** `defaultdb`
|
|
613
|
+
|
|
614
|
+
CockroachDB is a distributed SQL database with automatic replication and failover. Single-node mode is used for local development.
|
|
615
|
+
|
|
586
616
|
---
|
|
587
617
|
|
|
588
618
|
## Enhanced CLI Tools
|
|
@@ -604,6 +634,7 @@ SpinDB supports enhanced database shells with auto-completion, syntax highlighti
|
|
|
604
634
|
| Qdrant | REST API | - | - |
|
|
605
635
|
| Meilisearch | REST API | - | - |
|
|
606
636
|
| CouchDB | REST API | - | - |
|
|
637
|
+
| CockroachDB | `cockroach sql` | - | - |
|
|
607
638
|
|
|
608
639
|
Install and use in one command:
|
|
609
640
|
|
|
@@ -713,6 +744,16 @@ spindb backup mydb --format snapshot # Snapshot (only format)
|
|
|
713
744
|
spindb backup mydb --format snapshot # Snapshot (only format)
|
|
714
745
|
```
|
|
715
746
|
|
|
747
|
+
### CockroachDB
|
|
748
|
+
|
|
749
|
+
| Format | Extension | Tool | Use Case |
|
|
750
|
+
|--------|-----------|------|----------|
|
|
751
|
+
| sql | `.sql` | cockroach dump | Plain SQL dump |
|
|
752
|
+
|
|
753
|
+
```bash
|
|
754
|
+
spindb backup mydb --format sql # SQL dump (only format)
|
|
755
|
+
```
|
|
756
|
+
|
|
716
757
|
---
|
|
717
758
|
|
|
718
759
|
## Advanced Features
|
|
@@ -755,6 +796,7 @@ spindb restore mydb --from-url "postgresql://user:pass@prod-host:5432/production
|
|
|
755
796
|
| Qdrant | `qdrant://` or `http://` | `http://host:6333?api_key=KEY` |
|
|
756
797
|
| Meilisearch | `meilisearch://` or `http://` | `http://host:7700?api_key=KEY` |
|
|
757
798
|
| CouchDB | `couchdb://` or `http://` | `http://user:pass@host:5984/db` |
|
|
799
|
+
| CockroachDB | `postgresql://` or `postgres://` | `postgresql://root@host:26257/db?sslmode=disable` |
|
|
758
800
|
|
|
759
801
|
### Multi-Version Support
|
|
760
802
|
|
|
@@ -809,10 +851,7 @@ See [TODO.md](TODO.md) for the complete roadmap.
|
|
|
809
851
|
- Environment variable support in connection strings
|
|
810
852
|
- Secrets management with macOS Keychain integration
|
|
811
853
|
|
|
812
|
-
### v1.2 -
|
|
813
|
-
- **CockroachDB** - Distributed PostgreSQL-compatible database
|
|
814
|
-
|
|
815
|
-
### v1.3 - Advanced Features
|
|
854
|
+
### v1.2 - Advanced Features
|
|
816
855
|
- Container templates for common configurations
|
|
817
856
|
- Scheduled automated backups
|
|
818
857
|
- Import databases from Docker containers
|
|
@@ -910,7 +949,7 @@ See [FEATURE.md](FEATURE.md) for adding new database engines.
|
|
|
910
949
|
|
|
911
950
|
SpinDB is powered by:
|
|
912
951
|
|
|
913
|
-
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for
|
|
952
|
+
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for 15 engines across all major platforms. Makes Docker-free multi-version database support possible.
|
|
914
953
|
|
|
915
954
|
---
|
|
916
955
|
|
package/cli/commands/engines.ts
CHANGED
|
@@ -62,6 +62,8 @@ import { qdrantBinaryManager } from '../../engines/qdrant/binary-manager'
|
|
|
62
62
|
import { meilisearchBinaryManager } from '../../engines/meilisearch/binary-manager'
|
|
63
63
|
import { ferretdbBinaryManager } from '../../engines/ferretdb/binary-manager'
|
|
64
64
|
import { couchdbBinaryManager } from '../../engines/couchdb/binary-manager'
|
|
65
|
+
import { cockroachdbBinaryManager } from '../../engines/cockroachdb/binary-manager'
|
|
66
|
+
import { surrealdbBinaryManager } from '../../engines/surrealdb/binary-manager'
|
|
65
67
|
import {
|
|
66
68
|
DEFAULT_DOCUMENTDB_VERSION,
|
|
67
69
|
normalizeDocumentDBVersion,
|
|
@@ -1676,9 +1678,95 @@ enginesCommand
|
|
|
1676
1678
|
return
|
|
1677
1679
|
}
|
|
1678
1680
|
|
|
1681
|
+
if (['cockroachdb', 'crdb'].includes(normalizedEngine)) {
|
|
1682
|
+
if (!version) {
|
|
1683
|
+
console.error(uiError('CockroachDB requires a version (e.g., 25)'))
|
|
1684
|
+
process.exit(1)
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
const engine = getEngine(Engine.CockroachDB)
|
|
1688
|
+
|
|
1689
|
+
const spinner = createSpinner(`Checking CockroachDB ${version} binaries...`)
|
|
1690
|
+
spinner.start()
|
|
1691
|
+
|
|
1692
|
+
let wasCached = false
|
|
1693
|
+
await engine.ensureBinaries(version, ({ stage, message }) => {
|
|
1694
|
+
if (stage === 'cached') {
|
|
1695
|
+
wasCached = true
|
|
1696
|
+
spinner.text = `CockroachDB ${version} binaries ready (cached)`
|
|
1697
|
+
} else {
|
|
1698
|
+
spinner.text = message
|
|
1699
|
+
}
|
|
1700
|
+
})
|
|
1701
|
+
|
|
1702
|
+
if (wasCached) {
|
|
1703
|
+
spinner.succeed(`CockroachDB ${version} binaries already installed`)
|
|
1704
|
+
} else {
|
|
1705
|
+
spinner.succeed(`CockroachDB ${version} binaries downloaded`)
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// Show the path for reference
|
|
1709
|
+
const { platform: cockroachdbPlatform, arch: cockroachdbArch } =
|
|
1710
|
+
platformService.getPlatformInfo()
|
|
1711
|
+
const cockroachdbFullVersion = cockroachdbBinaryManager.getFullVersion(version)
|
|
1712
|
+
const binPath = paths.getBinaryPath({
|
|
1713
|
+
engine: 'cockroachdb',
|
|
1714
|
+
version: cockroachdbFullVersion,
|
|
1715
|
+
platform: cockroachdbPlatform,
|
|
1716
|
+
arch: cockroachdbArch,
|
|
1717
|
+
})
|
|
1718
|
+
console.log(chalk.gray(` Location: ${binPath}`))
|
|
1719
|
+
|
|
1720
|
+
// Skip client tools check for CockroachDB - the cockroach binary is both server and client
|
|
1721
|
+
return
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
if (['surrealdb', 'surreal'].includes(normalizedEngine)) {
|
|
1725
|
+
if (!version) {
|
|
1726
|
+
console.error(uiError('SurrealDB requires a version (e.g., 2)'))
|
|
1727
|
+
process.exit(1)
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const engine = getEngine(Engine.SurrealDB)
|
|
1731
|
+
|
|
1732
|
+
const spinner = createSpinner(`Checking SurrealDB ${version} binaries...`)
|
|
1733
|
+
spinner.start()
|
|
1734
|
+
|
|
1735
|
+
let wasCached = false
|
|
1736
|
+
await engine.ensureBinaries(version, ({ stage, message }) => {
|
|
1737
|
+
if (stage === 'cached') {
|
|
1738
|
+
wasCached = true
|
|
1739
|
+
spinner.text = `SurrealDB ${version} binaries ready (cached)`
|
|
1740
|
+
} else {
|
|
1741
|
+
spinner.text = message
|
|
1742
|
+
}
|
|
1743
|
+
})
|
|
1744
|
+
|
|
1745
|
+
if (wasCached) {
|
|
1746
|
+
spinner.succeed(`SurrealDB ${version} binaries already installed`)
|
|
1747
|
+
} else {
|
|
1748
|
+
spinner.succeed(`SurrealDB ${version} binaries downloaded`)
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// Show the path for reference
|
|
1752
|
+
const { platform: surrealdbPlatform, arch: surrealdbArch } =
|
|
1753
|
+
platformService.getPlatformInfo()
|
|
1754
|
+
const surrealdbFullVersion = surrealdbBinaryManager.getFullVersion(version)
|
|
1755
|
+
const binPath = paths.getBinaryPath({
|
|
1756
|
+
engine: 'surrealdb',
|
|
1757
|
+
version: surrealdbFullVersion,
|
|
1758
|
+
platform: surrealdbPlatform,
|
|
1759
|
+
arch: surrealdbArch,
|
|
1760
|
+
})
|
|
1761
|
+
console.log(chalk.gray(` Location: ${binPath}`))
|
|
1762
|
+
|
|
1763
|
+
// Skip client tools check for SurrealDB - the surreal binary is both server and client
|
|
1764
|
+
return
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1679
1767
|
console.error(
|
|
1680
1768
|
uiError(
|
|
1681
|
-
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb`,
|
|
1769
|
+
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb`,
|
|
1682
1770
|
),
|
|
1683
1771
|
)
|
|
1684
1772
|
process.exit(1)
|
|
@@ -174,6 +174,25 @@ function validateConnectionString(
|
|
|
174
174
|
return 'Connection string must start with couchdb://, http://, or https://'
|
|
175
175
|
}
|
|
176
176
|
break
|
|
177
|
+
case Engine.CockroachDB:
|
|
178
|
+
if (
|
|
179
|
+
!input.startsWith('postgresql://') &&
|
|
180
|
+
!input.startsWith('postgres://')
|
|
181
|
+
) {
|
|
182
|
+
return 'Connection string must start with postgresql:// or postgres://'
|
|
183
|
+
}
|
|
184
|
+
break
|
|
185
|
+
case Engine.SurrealDB:
|
|
186
|
+
if (
|
|
187
|
+
!input.startsWith('surrealdb://') &&
|
|
188
|
+
!input.startsWith('ws://') &&
|
|
189
|
+
!input.startsWith('wss://') &&
|
|
190
|
+
!input.startsWith('http://') &&
|
|
191
|
+
!input.startsWith('https://')
|
|
192
|
+
) {
|
|
193
|
+
return 'Connection string must start with surrealdb://, ws://, wss://, http://, or https://'
|
|
194
|
+
}
|
|
195
|
+
break
|
|
177
196
|
case Engine.SQLite:
|
|
178
197
|
case Engine.DuckDB:
|
|
179
198
|
return 'File-based engines do not support remote connection strings'
|
|
@@ -648,13 +648,15 @@ export async function showContainerSubmenu(
|
|
|
648
648
|
// REST API engines (Qdrant, Meilisearch, CouchDB) don't support script files - hide the option entirely
|
|
649
649
|
if (config.engine !== Engine.Qdrant && config.engine !== Engine.Meilisearch && config.engine !== Engine.CouchDB) {
|
|
650
650
|
const canRunSql = isFileBasedDB ? existsSync(config.database) : isRunning
|
|
651
|
-
// Engine-specific terminology: Redis/Valkey use commands, MongoDB/FerretDB use scripts, others use SQL
|
|
651
|
+
// Engine-specific terminology: Redis/Valkey use commands, MongoDB/FerretDB use scripts, SurrealDB uses SurrealQL, others use SQL
|
|
652
652
|
const runScriptLabel =
|
|
653
653
|
config.engine === Engine.Redis || config.engine === Engine.Valkey
|
|
654
654
|
? 'Run command file'
|
|
655
655
|
: config.engine === Engine.MongoDB || config.engine === Engine.FerretDB
|
|
656
656
|
? 'Run script file'
|
|
657
|
-
:
|
|
657
|
+
: config.engine === Engine.SurrealDB
|
|
658
|
+
? 'Run SurrealQL file'
|
|
659
|
+
: 'Run SQL file'
|
|
658
660
|
actionChoices.push({
|
|
659
661
|
name: canRunSql
|
|
660
662
|
? `${chalk.yellow('▷')} ${runScriptLabel}`
|
|
@@ -215,6 +215,20 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
215
215
|
engineSpecificInstalled = false
|
|
216
216
|
engineSpecificValue = null
|
|
217
217
|
engineSpecificInstallValue = null
|
|
218
|
+
} else if (config.engine === 'surrealdb') {
|
|
219
|
+
// SurrealDB uses surreal sql command
|
|
220
|
+
defaultShellName = 'surreal sql'
|
|
221
|
+
engineSpecificCli = null
|
|
222
|
+
engineSpecificInstalled = false
|
|
223
|
+
engineSpecificValue = null
|
|
224
|
+
engineSpecificInstallValue = null
|
|
225
|
+
} else if (config.engine === 'cockroachdb') {
|
|
226
|
+
// CockroachDB uses cockroach sql command
|
|
227
|
+
defaultShellName = 'cockroach sql'
|
|
228
|
+
engineSpecificCli = null
|
|
229
|
+
engineSpecificInstalled = false
|
|
230
|
+
engineSpecificValue = null
|
|
231
|
+
engineSpecificInstallValue = null
|
|
218
232
|
} else {
|
|
219
233
|
defaultShellName = 'psql'
|
|
220
234
|
engineSpecificCli = 'pgcli'
|
|
@@ -309,7 +323,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
309
323
|
}
|
|
310
324
|
}
|
|
311
325
|
|
|
312
|
-
// usql supports SQL databases (PostgreSQL, MySQL, SQLite) - skip for Redis, Valkey, MongoDB, FerretDB, Qdrant, Meilisearch, and
|
|
326
|
+
// usql supports SQL databases (PostgreSQL, MySQL, SQLite) - skip for Redis, Valkey, MongoDB, FerretDB, Qdrant, Meilisearch, CouchDB, and SurrealDB
|
|
313
327
|
const isNonSqlEngine =
|
|
314
328
|
config.engine === 'redis' ||
|
|
315
329
|
config.engine === 'valkey' ||
|
|
@@ -317,7 +331,8 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
317
331
|
config.engine === 'ferretdb' ||
|
|
318
332
|
config.engine === 'qdrant' ||
|
|
319
333
|
config.engine === 'meilisearch' ||
|
|
320
|
-
config.engine === 'couchdb'
|
|
334
|
+
config.engine === 'couchdb' ||
|
|
335
|
+
config.engine === 'surrealdb'
|
|
321
336
|
if (!isNonSqlEngine) {
|
|
322
337
|
if (usqlInstalled) {
|
|
323
338
|
choices.push({
|
|
@@ -869,6 +884,41 @@ async function launchShell(
|
|
|
869
884
|
|
|
870
885
|
openInBrowser(dashboardUrl)
|
|
871
886
|
return
|
|
887
|
+
} else if (config.engine === 'surrealdb') {
|
|
888
|
+
// SurrealDB uses surreal sql command
|
|
889
|
+
const engine = getEngine(config.engine)
|
|
890
|
+
const surrealPath = await engine.getSurrealPath(config.version).catch(() => 'surreal')
|
|
891
|
+
const namespace = config.name.replace(/-/g, '_')
|
|
892
|
+
const database = config.database || 'default'
|
|
893
|
+
shellCmd = surrealPath
|
|
894
|
+
shellArgs = [
|
|
895
|
+
'sql',
|
|
896
|
+
'--endpoint',
|
|
897
|
+
`ws://127.0.0.1:${config.port}`,
|
|
898
|
+
'--namespace',
|
|
899
|
+
namespace,
|
|
900
|
+
'--database',
|
|
901
|
+
database,
|
|
902
|
+
'--username',
|
|
903
|
+
'root',
|
|
904
|
+
'--password',
|
|
905
|
+
'root',
|
|
906
|
+
]
|
|
907
|
+
installHint = 'spindb engines download surrealdb'
|
|
908
|
+
} else if (config.engine === 'cockroachdb') {
|
|
909
|
+
// CockroachDB uses cockroach sql command
|
|
910
|
+
const engine = getEngine(config.engine)
|
|
911
|
+
const cockroachPath = await engine.getCockroachPath(config.version).catch(() => 'cockroach')
|
|
912
|
+
shellCmd = cockroachPath
|
|
913
|
+
shellArgs = [
|
|
914
|
+
'sql',
|
|
915
|
+
'--insecure',
|
|
916
|
+
'--host',
|
|
917
|
+
`127.0.0.1:${config.port}`,
|
|
918
|
+
'--database',
|
|
919
|
+
config.database,
|
|
920
|
+
]
|
|
921
|
+
installHint = 'spindb engines download cockroachdb'
|
|
872
922
|
} else {
|
|
873
923
|
shellCmd = 'psql'
|
|
874
924
|
shellArgs = [connectionString]
|
|
@@ -59,7 +59,8 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
59
59
|
|
|
60
60
|
// Get script type terminology based on engine
|
|
61
61
|
// IMPORTANT: When adding a new engine, update this function and FEATURE.md
|
|
62
|
-
// - SQL: PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse
|
|
62
|
+
// - SQL: PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse, CockroachDB
|
|
63
|
+
// - SurrealQL: SurrealDB (SQL-like but distinct language)
|
|
63
64
|
// - Script: MongoDB, FerretDB (JavaScript via mongosh), Qdrant, Meilisearch (REST API)
|
|
64
65
|
// - Command: Redis, Valkey (Redis commands)
|
|
65
66
|
const getScriptType = (engine: Engine): { type: string; lower: string } => {
|
|
@@ -79,6 +80,10 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
79
80
|
case Engine.CouchDB:
|
|
80
81
|
return { type: 'Script', lower: 'script' }
|
|
81
82
|
|
|
83
|
+
// SurrealDB uses SurrealQL (distinct from SQL)
|
|
84
|
+
case Engine.SurrealDB:
|
|
85
|
+
return { type: 'SurrealQL', lower: 'SurrealQL' }
|
|
86
|
+
|
|
82
87
|
// SQL engines use "SQL" terminology
|
|
83
88
|
case Engine.PostgreSQL:
|
|
84
89
|
case Engine.MySQL:
|
|
@@ -86,6 +91,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
86
91
|
case Engine.SQLite:
|
|
87
92
|
case Engine.DuckDB:
|
|
88
93
|
case Engine.ClickHouse:
|
|
94
|
+
case Engine.CockroachDB:
|
|
89
95
|
return { type: 'SQL', lower: 'sql' }
|
|
90
96
|
|
|
91
97
|
default:
|
package/cli/constants.ts
CHANGED
|
@@ -13,6 +13,8 @@ export const ENGINE_ICONS: Record<string, string> = {
|
|
|
13
13
|
qdrant: '🧭',
|
|
14
14
|
meilisearch: '🔍',
|
|
15
15
|
couchdb: '🛋',
|
|
16
|
+
cockroachdb: '🪳',
|
|
17
|
+
surrealdb: '🌀',
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
// Visual width of each icon in terminal columns
|
|
@@ -32,6 +34,8 @@ export const ENGINE_ICON_WIDTHS: Record<string, number> = {
|
|
|
32
34
|
qdrant: 2,
|
|
33
35
|
meilisearch: 2,
|
|
34
36
|
couchdb: 1, // 🛋 couch renders narrow
|
|
37
|
+
cockroachdb: 1, // 🪳 cockroach renders narrow
|
|
38
|
+
surrealdb: 2, // 🌀 cyclone renders at standard width
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export const DEFAULT_ENGINE_ICON = '▣'
|
package/cli/helpers.ts
CHANGED
|
@@ -194,6 +194,26 @@ export type InstalledCouchDBEngine = {
|
|
|
194
194
|
source: 'downloaded'
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
export type InstalledCockroachDBEngine = {
|
|
198
|
+
engine: 'cockroachdb'
|
|
199
|
+
version: string
|
|
200
|
+
platform: string
|
|
201
|
+
arch: string
|
|
202
|
+
path: string
|
|
203
|
+
sizeBytes: number
|
|
204
|
+
source: 'downloaded'
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export type InstalledSurrealDBEngine = {
|
|
208
|
+
engine: 'surrealdb'
|
|
209
|
+
version: string
|
|
210
|
+
platform: string
|
|
211
|
+
arch: string
|
|
212
|
+
path: string
|
|
213
|
+
sizeBytes: number
|
|
214
|
+
source: 'downloaded'
|
|
215
|
+
}
|
|
216
|
+
|
|
197
217
|
export type InstalledEngine =
|
|
198
218
|
| InstalledPostgresEngine
|
|
199
219
|
| InstalledMariadbEngine
|
|
@@ -208,6 +228,8 @@ export type InstalledEngine =
|
|
|
208
228
|
| InstalledQdrantEngine
|
|
209
229
|
| InstalledMeilisearchEngine
|
|
210
230
|
| InstalledCouchDBEngine
|
|
231
|
+
| InstalledCockroachDBEngine
|
|
232
|
+
| InstalledSurrealDBEngine
|
|
211
233
|
|
|
212
234
|
async function getPostgresVersion(binPath: string): Promise<string | null> {
|
|
213
235
|
const ext = platformService.getExecutableExtension()
|
|
@@ -876,6 +898,118 @@ async function getInstalledCouchDBEngines(): Promise<InstalledCouchDBEngine[]> {
|
|
|
876
898
|
return engines
|
|
877
899
|
}
|
|
878
900
|
|
|
901
|
+
// Get CockroachDB version from binary path
|
|
902
|
+
async function getCockroachDBVersion(binPath: string): Promise<string | null> {
|
|
903
|
+
const ext = platformService.getExecutableExtension()
|
|
904
|
+
const cockroachPath = join(binPath, 'bin', `cockroach${ext}`)
|
|
905
|
+
if (!existsSync(cockroachPath)) {
|
|
906
|
+
return null
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
try {
|
|
910
|
+
const { stdout } = await execFileAsync(cockroachPath, ['version'])
|
|
911
|
+
// Parse output like "Build Tag: v25.4.2" or "CockroachDB CCL v25.4.2"
|
|
912
|
+
const match = stdout.match(/v?(\d+\.\d+\.\d+)/)
|
|
913
|
+
return match ? match[1] : null
|
|
914
|
+
} catch {
|
|
915
|
+
return null
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Get installed CockroachDB engines from downloaded binaries
|
|
920
|
+
async function getInstalledCockroachDBEngines(): Promise<InstalledCockroachDBEngine[]> {
|
|
921
|
+
const binDir = paths.bin
|
|
922
|
+
|
|
923
|
+
if (!existsSync(binDir)) {
|
|
924
|
+
return []
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const entries = await readdir(binDir, { withFileTypes: true })
|
|
928
|
+
const engines: InstalledCockroachDBEngine[] = []
|
|
929
|
+
|
|
930
|
+
for (const entry of entries) {
|
|
931
|
+
if (!entry.isDirectory()) continue
|
|
932
|
+
if (!entry.name.startsWith('cockroachdb-')) continue
|
|
933
|
+
|
|
934
|
+
const parsed = parseEngineDirectory(entry.name, 'cockroachdb-', binDir)
|
|
935
|
+
if (!parsed) continue
|
|
936
|
+
|
|
937
|
+
const actualVersion =
|
|
938
|
+
(await getCockroachDBVersion(parsed.path)) || parsed.version
|
|
939
|
+
const sizeBytes = await calculateDirectorySize(parsed.path)
|
|
940
|
+
|
|
941
|
+
engines.push({
|
|
942
|
+
engine: 'cockroachdb',
|
|
943
|
+
version: actualVersion,
|
|
944
|
+
platform: parsed.platform,
|
|
945
|
+
arch: parsed.arch,
|
|
946
|
+
path: parsed.path,
|
|
947
|
+
sizeBytes,
|
|
948
|
+
source: 'downloaded',
|
|
949
|
+
})
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
engines.sort((a, b) => compareVersions(b.version, a.version))
|
|
953
|
+
|
|
954
|
+
return engines
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Get SurrealDB version from binary path
|
|
958
|
+
async function getSurrealDBVersion(binPath: string): Promise<string | null> {
|
|
959
|
+
const ext = platformService.getExecutableExtension()
|
|
960
|
+
const surrealPath = join(binPath, 'bin', `surreal${ext}`)
|
|
961
|
+
if (!existsSync(surrealPath)) {
|
|
962
|
+
return null
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
try {
|
|
966
|
+
const { stdout } = await execFileAsync(surrealPath, ['version'])
|
|
967
|
+
// Parse output like "surreal 2.3.2 for linux on x86_64" or "2.3.2"
|
|
968
|
+
const match = stdout.match(/(\d+\.\d+\.\d+)/)
|
|
969
|
+
return match ? match[1] : null
|
|
970
|
+
} catch {
|
|
971
|
+
return null
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Get installed SurrealDB engines from downloaded binaries
|
|
976
|
+
async function getInstalledSurrealDBEngines(): Promise<InstalledSurrealDBEngine[]> {
|
|
977
|
+
const binDir = paths.bin
|
|
978
|
+
|
|
979
|
+
if (!existsSync(binDir)) {
|
|
980
|
+
return []
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
const entries = await readdir(binDir, { withFileTypes: true })
|
|
984
|
+
const engines: InstalledSurrealDBEngine[] = []
|
|
985
|
+
|
|
986
|
+
for (const entry of entries) {
|
|
987
|
+
if (!entry.isDirectory()) continue
|
|
988
|
+
if (!entry.name.startsWith('surrealdb-')) continue
|
|
989
|
+
|
|
990
|
+
const parsed = parseEngineDirectory(entry.name, 'surrealdb-', binDir)
|
|
991
|
+
if (!parsed) continue
|
|
992
|
+
|
|
993
|
+
const actualVersion =
|
|
994
|
+
(await getSurrealDBVersion(parsed.path)) || parsed.version
|
|
995
|
+
const sizeBytes = await calculateDirectorySize(parsed.path)
|
|
996
|
+
|
|
997
|
+
engines.push({
|
|
998
|
+
engine: 'surrealdb',
|
|
999
|
+
version: actualVersion,
|
|
1000
|
+
platform: parsed.platform,
|
|
1001
|
+
arch: parsed.arch,
|
|
1002
|
+
path: parsed.path,
|
|
1003
|
+
sizeBytes,
|
|
1004
|
+
source: 'downloaded',
|
|
1005
|
+
})
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
engines.sort((a, b) => compareVersions(b.version, a.version))
|
|
1009
|
+
|
|
1010
|
+
return engines
|
|
1011
|
+
}
|
|
1012
|
+
|
|
879
1013
|
// Get FerretDB version from binary path
|
|
880
1014
|
async function getFerretDBVersion(binPath: string): Promise<string | null> {
|
|
881
1015
|
const ext = platformService.getExecutableExtension()
|
|
@@ -963,6 +1097,8 @@ const ENGINE_PREFIXES = [
|
|
|
963
1097
|
'qdrant-',
|
|
964
1098
|
'meilisearch-',
|
|
965
1099
|
'couchdb-',
|
|
1100
|
+
'cockroachdb-',
|
|
1101
|
+
'surrealdb-',
|
|
966
1102
|
] as const
|
|
967
1103
|
|
|
968
1104
|
/**
|
|
@@ -1006,6 +1142,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1006
1142
|
qdrantEngines,
|
|
1007
1143
|
meilisearchEngines,
|
|
1008
1144
|
couchdbEngines,
|
|
1145
|
+
cockroachdbEngines,
|
|
1146
|
+
surrealdbEngines,
|
|
1009
1147
|
] = await Promise.all([
|
|
1010
1148
|
getInstalledPostgresEngines(),
|
|
1011
1149
|
getInstalledMariadbEngines(),
|
|
@@ -1020,6 +1158,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1020
1158
|
getInstalledQdrantEngines(),
|
|
1021
1159
|
getInstalledMeilisearchEngines(),
|
|
1022
1160
|
getInstalledCouchDBEngines(),
|
|
1161
|
+
getInstalledCockroachDBEngines(),
|
|
1162
|
+
getInstalledSurrealDBEngines(),
|
|
1023
1163
|
])
|
|
1024
1164
|
|
|
1025
1165
|
return [
|
|
@@ -1036,6 +1176,8 @@ export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
|
1036
1176
|
...qdrantEngines,
|
|
1037
1177
|
...meilisearchEngines,
|
|
1038
1178
|
...couchdbEngines,
|
|
1179
|
+
...cockroachdbEngines,
|
|
1180
|
+
...surrealdbEngines,
|
|
1039
1181
|
]
|
|
1040
1182
|
}
|
|
1041
1183
|
|
|
@@ -1052,4 +1194,6 @@ export {
|
|
|
1052
1194
|
getInstalledQdrantEngines,
|
|
1053
1195
|
getInstalledMeilisearchEngines,
|
|
1054
1196
|
getInstalledCouchDBEngines,
|
|
1197
|
+
getInstalledCockroachDBEngines,
|
|
1198
|
+
getInstalledSurrealDBEngines,
|
|
1055
1199
|
}
|
package/cli/index.ts
CHANGED