spindb 0.36.2 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -8
- package/cli/commands/create.ts +7 -0
- package/cli/commands/databases.ts +17 -12
- package/cli/commands/delete.ts +3 -0
- package/cli/commands/engines.ts +59 -3
- package/cli/commands/info.ts +5 -0
- package/cli/commands/list.ts +2 -0
- package/cli/commands/menu/backup-handlers.ts +2 -0
- package/cli/commands/menu/settings-handlers.ts +3 -0
- package/cli/commands/menu/shell-handlers.ts +23 -0
- package/cli/commands/restore.ts +3 -0
- package/cli/commands/start.ts +3 -0
- package/cli/commands/url.ts +4 -0
- package/cli/constants.ts +4 -0
- package/cli/helpers.ts +93 -0
- package/config/backup-formats.ts +14 -0
- package/config/engine-defaults.ts +13 -0
- package/config/engines.json +17 -0
- package/core/config-manager.ts +5 -0
- package/core/dependency-manager.ts +2 -0
- package/core/docker-exporter.ts +17 -0
- package/core/library-env.ts +2 -4
- package/engines/base-engine.ts +8 -0
- package/engines/index.ts +4 -0
- package/engines/mariadb/index.ts +5 -4
- package/engines/redis/index.ts +15 -4
- package/engines/tigerbeetle/README.md +61 -0
- package/engines/tigerbeetle/backup.ts +49 -0
- package/engines/tigerbeetle/binary-manager.ts +95 -0
- package/engines/tigerbeetle/binary-urls.ts +62 -0
- package/engines/tigerbeetle/hostdb-releases.ts +26 -0
- package/engines/tigerbeetle/index.ts +746 -0
- package/engines/tigerbeetle/restore.ts +130 -0
- package/engines/tigerbeetle/version-maps.ts +68 -0
- package/engines/tigerbeetle/version-validator.ts +126 -0
- package/engines/valkey/index.ts +15 -4
- package/package.json +2 -1
- package/types/index.ts +9 -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 20 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
|
|
@@ -28,7 +28,7 @@ spindb create cache --engine redis
|
|
|
28
28
|
|
|
29
29
|
## Supported Engines & Platforms
|
|
30
30
|
|
|
31
|
-
SpinDB supports **
|
|
31
|
+
SpinDB supports **20 database engines** across **5 platform architectures**—all with a consistent API.
|
|
32
32
|
|
|
33
33
|
| Engine | Type | macOS ARM | macOS Intel | Linux x64 | Linux ARM | Windows |
|
|
34
34
|
|--------|------|:---------:|:-----------:|:---------:|:---------:|:-------:|
|
|
@@ -51,8 +51,9 @@ SpinDB supports **19 database engines** across **5 platform architectures**—al
|
|
|
51
51
|
| 🤖 **TypeDB** | Knowledge Graph | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
52
52
|
| 📈 **InfluxDB** | Time-Series | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
53
53
|
| 🔮 **Weaviate** | Vector Database | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
54
|
+
| 🐯 **TigerBeetle** | Financial Ledger | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
54
55
|
|
|
55
|
-
**
|
|
56
|
+
**100 combinations. One CLI. Zero configuration.**
|
|
56
57
|
|
|
57
58
|
> ClickHouse is available on Windows via WSL. FerretDB v1 is natively supported on Windows (uses plain PostgreSQL backend); v2 requires macOS/Linux.
|
|
58
59
|
|
|
@@ -128,6 +129,15 @@ spindb connect vectors # Open web dashboard
|
|
|
128
129
|
|
|
129
130
|
> Weaviate is an AI-native vector database. REST API on default port 8080, gRPC on port+1. Uses classes/collections.
|
|
130
131
|
|
|
132
|
+
### TigerBeetle
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
spindb create ledger --engine tigerbeetle --start
|
|
136
|
+
spindb connect ledger # Open REPL
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> TigerBeetle is a high-performance financial ledger database. Custom binary protocol on default port 3000. Uses REPL for interaction.
|
|
140
|
+
|
|
131
141
|
### Enhanced Shells & Visual Tools
|
|
132
142
|
|
|
133
143
|
```bash
|
|
@@ -140,7 +150,7 @@ spindb connect mydb --ui # Built-in Web UI (DuckDB)
|
|
|
140
150
|
### Any Engine
|
|
141
151
|
|
|
142
152
|
```bash
|
|
143
|
-
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb|cockroachdb|surrealdb|questdb|typedb|influxdb|weaviate]
|
|
153
|
+
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|ferretdb|redis|valkey|clickhouse|sqlite|duckdb|qdrant|meilisearch|couchdb|cockroachdb|surrealdb|questdb|typedb|influxdb|weaviate|tigerbeetle]
|
|
144
154
|
spindb start mydb
|
|
145
155
|
spindb connect mydb
|
|
146
156
|
spindb backup mydb
|
|
@@ -182,7 +192,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
182
192
|
|
|
183
193
|
| Feature | SpinDB | DBngin | Postgres.app | Laragon |
|
|
184
194
|
|---------|--------|--------|--------------|---------|
|
|
185
|
-
| **Engines supported** |
|
|
195
|
+
| **Engines supported** | 20 | 3 (PG/MySQL/Redis) | 1 (PostgreSQL) | 4 (PG/MySQL/MariaDB/MongoDB) |
|
|
186
196
|
| CLI-first | ✅ | ❌ GUI-only | ❌ GUI-only | ⚠️ Limited CLI |
|
|
187
197
|
| Multi-version support | ✅ | ✅ | ✅ | ✅ |
|
|
188
198
|
| Built-in backup/restore | ✅ | ✅ | ❌ | ⚠️ Manual |
|
|
@@ -198,7 +208,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
198
208
|
|
|
199
209
|
| Feature | SpinDB | Docker Desktop | Podman | OrbStack |
|
|
200
210
|
|---------|--------|----------------|--------|----------|
|
|
201
|
-
| **Engines supported** |
|
|
211
|
+
| **Engines supported** | 20 unified | Any (manual setup) | Any (manual setup) | Any (manual setup) |
|
|
202
212
|
| Daemon required | ❌ | ✅ | ❌ (rootless) | ✅ |
|
|
203
213
|
| Resource overhead | Native | VM + containers | VM + containers | VM + containers |
|
|
204
214
|
| Built-in backup/restore | ✅ | ❌ Manual | ❌ Manual | ❌ Manual |
|
|
@@ -214,7 +224,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
214
224
|
|
|
215
225
|
| Feature | SpinDB | Homebrew | apt/winget | asdf-vm |
|
|
216
226
|
|---------|--------|----------|------------|---------|
|
|
217
|
-
| **Engines supported** |
|
|
227
|
+
| **Engines supported** | 20 unified | Many (separate formulas) | Many (separate packages) | Many (plugins) |
|
|
218
228
|
| Multi-version side-by-side | ✅ | ⚠️ Complex | ❌ | ✅ |
|
|
219
229
|
| Isolated data directories | ✅ | ❌ System-wide | ❌ System-wide | ❌ |
|
|
220
230
|
| Built-in backup/restore | ✅ | ❌ | ❌ | ❌ |
|
|
@@ -285,6 +295,7 @@ See [DEPLOY.md](DEPLOY.md) for comprehensive deployment documentation.
|
|
|
285
295
|
- **ClickHouse Windows** - Not supported (hostdb doesn't build for Windows).
|
|
286
296
|
- **FerretDB Windows** - v1 supported natively (plain PostgreSQL backend). v2 not supported (postgresql-documentdb has startup issues); use WSL for v2.
|
|
287
297
|
- **Qdrant, Meilisearch, CouchDB, Weaviate** - Use REST API instead of CLI shell. Access via HTTP at the configured port.
|
|
298
|
+
- **TigerBeetle** - Custom binary protocol only. No SQL or REST API. Interact via REPL (`spindb connect`) or client libraries.
|
|
288
299
|
|
|
289
300
|
---
|
|
290
301
|
|
|
@@ -405,7 +416,7 @@ See [ENGINE_CHECKLIST.md](ENGINE_CHECKLIST.md) for adding new database engines.
|
|
|
405
416
|
|
|
406
417
|
SpinDB is powered by:
|
|
407
418
|
|
|
408
|
-
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for
|
|
419
|
+
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for 20 engines across all major platforms. Makes Docker-free multi-version database support possible.
|
|
409
420
|
|
|
410
421
|
---
|
|
411
422
|
|
package/cli/commands/create.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
isV1 as isFerretDBv1,
|
|
29
29
|
} from '../../engines/ferretdb/version-maps'
|
|
30
30
|
import type { BaseEngine } from '../../engines/base-engine'
|
|
31
|
+
import { getEngineMetadata } from '../helpers'
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Simplified SQLite container creation flow
|
|
@@ -160,6 +161,7 @@ async function createSqliteContainer(
|
|
|
160
161
|
|
|
161
162
|
// Display success
|
|
162
163
|
if (json) {
|
|
164
|
+
const metadata = await getEngineMetadata('sqlite')
|
|
163
165
|
console.log(
|
|
164
166
|
JSON.stringify({
|
|
165
167
|
success: true,
|
|
@@ -170,6 +172,7 @@ async function createSqliteContainer(
|
|
|
170
172
|
database: containerName,
|
|
171
173
|
connectionString,
|
|
172
174
|
restored: !!restoreLocation,
|
|
175
|
+
...metadata,
|
|
173
176
|
}),
|
|
174
177
|
)
|
|
175
178
|
} else {
|
|
@@ -329,6 +332,7 @@ async function createDuckDBContainer(
|
|
|
329
332
|
|
|
330
333
|
// Display success
|
|
331
334
|
if (json) {
|
|
335
|
+
const metadata = await getEngineMetadata('duckdb')
|
|
332
336
|
console.log(
|
|
333
337
|
JSON.stringify({
|
|
334
338
|
success: true,
|
|
@@ -339,6 +343,7 @@ async function createDuckDBContainer(
|
|
|
339
343
|
database: containerName,
|
|
340
344
|
connectionString,
|
|
341
345
|
restored: !!restoreLocation,
|
|
346
|
+
...metadata,
|
|
342
347
|
}),
|
|
343
348
|
)
|
|
344
349
|
} else {
|
|
@@ -1096,6 +1101,7 @@ export const createCommand = new Command('create')
|
|
|
1096
1101
|
const connectionString = dbEngine.getConnectionString(finalConfig)
|
|
1097
1102
|
|
|
1098
1103
|
if (options.json) {
|
|
1104
|
+
const metadata = await getEngineMetadata(finalConfig.engine)
|
|
1099
1105
|
console.log(
|
|
1100
1106
|
JSON.stringify({
|
|
1101
1107
|
success: true,
|
|
@@ -1107,6 +1113,7 @@ export const createCommand = new Command('create')
|
|
|
1107
1113
|
connectionString,
|
|
1108
1114
|
status: finalConfig.status,
|
|
1109
1115
|
restored: !!restoreLocation,
|
|
1116
|
+
...metadata,
|
|
1110
1117
|
}),
|
|
1111
1118
|
)
|
|
1112
1119
|
} else {
|
|
@@ -2,6 +2,7 @@ import { Command } from 'commander'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import { containerManager } from '../../core/container-manager'
|
|
4
4
|
import { uiError, uiSuccess } from '../ui/theme'
|
|
5
|
+
import { getEngineMetadata } from '../helpers'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* CLI command for managing database tracking within containers.
|
|
@@ -59,16 +60,20 @@ databasesCommand
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
if (options.json) {
|
|
62
|
-
const result =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
const result = await Promise.all(
|
|
64
|
+
containers.map(async (c) => {
|
|
65
|
+
const rawDatabases = c.databases || []
|
|
66
|
+
const databases = [...new Set([c.database, ...rawDatabases])]
|
|
67
|
+
const metadata = await getEngineMetadata(c.engine)
|
|
68
|
+
return {
|
|
69
|
+
container: c.name,
|
|
70
|
+
engine: c.engine,
|
|
71
|
+
primary: c.database,
|
|
72
|
+
databases,
|
|
73
|
+
...metadata,
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
)
|
|
72
77
|
console.log(JSON.stringify(result, null, 2))
|
|
73
78
|
} else {
|
|
74
79
|
console.log()
|
|
@@ -121,8 +126,8 @@ databasesCommand
|
|
|
121
126
|
const databases = [...new Set([config.database, ...rawDatabases])]
|
|
122
127
|
|
|
123
128
|
if (options.json) {
|
|
124
|
-
|
|
125
|
-
console.log(JSON.stringify(config, null, 2))
|
|
129
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
130
|
+
console.log(JSON.stringify({ ...config, ...metadata }, null, 2))
|
|
126
131
|
} else {
|
|
127
132
|
console.log()
|
|
128
133
|
console.log(chalk.bold(`Databases in "${container}":`))
|
package/cli/commands/delete.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getEngine } from '../../engines'
|
|
|
6
6
|
import { promptContainerSelect, promptConfirm } from '../ui/prompts'
|
|
7
7
|
import { createSpinner } from '../ui/spinner'
|
|
8
8
|
import { uiWarning } from '../ui/theme'
|
|
9
|
+
import { getEngineMetadata } from '../helpers'
|
|
9
10
|
|
|
10
11
|
export const deleteCommand = new Command('delete')
|
|
11
12
|
.alias('rm')
|
|
@@ -113,12 +114,14 @@ export const deleteCommand = new Command('delete')
|
|
|
113
114
|
deleteSpinner?.succeed(`Container "${containerName}" deleted`)
|
|
114
115
|
|
|
115
116
|
if (options.json) {
|
|
117
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
116
118
|
console.log(
|
|
117
119
|
JSON.stringify({
|
|
118
120
|
success: true,
|
|
119
121
|
deleted: containerName,
|
|
120
122
|
container: containerName,
|
|
121
123
|
engine: config.engine,
|
|
124
|
+
...metadata,
|
|
122
125
|
}),
|
|
123
126
|
)
|
|
124
127
|
}
|
package/cli/commands/engines.ts
CHANGED
|
@@ -30,7 +30,11 @@ import { promptConfirm } from '../ui/prompts'
|
|
|
30
30
|
import { createSpinner } from '../ui/spinner'
|
|
31
31
|
import { uiError, uiWarning, uiInfo, uiSuccess, formatBytes } from '../ui/theme'
|
|
32
32
|
import { getEngineIcon } from '../constants'
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
getInstalledEngines,
|
|
35
|
+
getInstalledPostgresEngines,
|
|
36
|
+
getEngineMetadata,
|
|
37
|
+
} from '../helpers'
|
|
34
38
|
import { Engine, Platform } from '../../types'
|
|
35
39
|
import {
|
|
36
40
|
loadEnginesJson,
|
|
@@ -55,6 +59,7 @@ import { questdbBinaryManager } from '../../engines/questdb/binary-manager'
|
|
|
55
59
|
import { typedbBinaryManager } from '../../engines/typedb/binary-manager'
|
|
56
60
|
import { influxdbBinaryManager } from '../../engines/influxdb/binary-manager'
|
|
57
61
|
import { weaviateBinaryManager } from '../../engines/weaviate/binary-manager'
|
|
62
|
+
import { tigerbeetleBinaryManager } from '../../engines/tigerbeetle/binary-manager'
|
|
58
63
|
import {
|
|
59
64
|
DEFAULT_DOCUMENTDB_VERSION,
|
|
60
65
|
DEFAULT_V1_POSTGRESQL_VERSION,
|
|
@@ -397,7 +402,13 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
397
402
|
const engines = await getInstalledEngines()
|
|
398
403
|
|
|
399
404
|
if (options.json) {
|
|
400
|
-
|
|
405
|
+
const enginesWithMetadata = await Promise.all(
|
|
406
|
+
engines.map(async (e) => ({
|
|
407
|
+
...e,
|
|
408
|
+
...(await getEngineMetadata(e.engine)),
|
|
409
|
+
})),
|
|
410
|
+
)
|
|
411
|
+
console.log(JSON.stringify(enginesWithMetadata, null, 2))
|
|
401
412
|
return
|
|
402
413
|
}
|
|
403
414
|
|
|
@@ -473,6 +484,7 @@ async function listEngines(options: { json?: boolean }): Promise<void> {
|
|
|
473
484
|
typedb: 'TypeDB',
|
|
474
485
|
valkey: 'Valkey',
|
|
475
486
|
weaviate: 'Weaviate',
|
|
487
|
+
tigerbeetle: 'TigerBeetle',
|
|
476
488
|
}
|
|
477
489
|
|
|
478
490
|
// Group engines by name for summary
|
|
@@ -1775,9 +1787,53 @@ enginesCommand
|
|
|
1775
1787
|
return
|
|
1776
1788
|
}
|
|
1777
1789
|
|
|
1790
|
+
if (['tigerbeetle', 'tb'].includes(normalizedEngine)) {
|
|
1791
|
+
if (!version) {
|
|
1792
|
+
console.error(uiError('TigerBeetle requires a version (e.g., 0.16)'))
|
|
1793
|
+
process.exit(1)
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
const engine = getEngine(Engine.TigerBeetle)
|
|
1797
|
+
|
|
1798
|
+
const spinner = createSpinner(
|
|
1799
|
+
`Checking TigerBeetle ${version} binaries...`,
|
|
1800
|
+
)
|
|
1801
|
+
spinner.start()
|
|
1802
|
+
|
|
1803
|
+
let wasCached = false
|
|
1804
|
+
await engine.ensureBinaries(version, ({ stage, message }) => {
|
|
1805
|
+
if (stage === 'cached') {
|
|
1806
|
+
wasCached = true
|
|
1807
|
+
spinner.text = `TigerBeetle ${version} binaries ready (cached)`
|
|
1808
|
+
} else {
|
|
1809
|
+
spinner.text = message
|
|
1810
|
+
}
|
|
1811
|
+
})
|
|
1812
|
+
|
|
1813
|
+
if (wasCached) {
|
|
1814
|
+
spinner.succeed(`TigerBeetle ${version} binaries already installed`)
|
|
1815
|
+
} else {
|
|
1816
|
+
spinner.succeed(`TigerBeetle ${version} binaries downloaded`)
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// Show the path for reference
|
|
1820
|
+
const { platform: tbPlatform, arch: tbArch } =
|
|
1821
|
+
platformService.getPlatformInfo()
|
|
1822
|
+
const tbFullVersion = tigerbeetleBinaryManager.getFullVersion(version)
|
|
1823
|
+
const binPath = paths.getBinaryPath({
|
|
1824
|
+
engine: 'tigerbeetle',
|
|
1825
|
+
version: tbFullVersion,
|
|
1826
|
+
platform: tbPlatform,
|
|
1827
|
+
arch: tbArch,
|
|
1828
|
+
})
|
|
1829
|
+
console.log(chalk.gray(` Location: ${binPath}`))
|
|
1830
|
+
|
|
1831
|
+
return
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1778
1834
|
console.error(
|
|
1779
1835
|
uiError(
|
|
1780
|
-
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb, influxdb, weaviate`,
|
|
1836
|
+
`Unknown engine "${engineName}". Supported: postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb, influxdb, weaviate, tigerbeetle`,
|
|
1781
1837
|
),
|
|
1782
1838
|
)
|
|
1783
1839
|
process.exit(1)
|
package/cli/commands/info.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { getEngine } from '../../engines'
|
|
|
10
10
|
import { uiError, uiInfo, header } from '../ui/theme'
|
|
11
11
|
import { getEngineIcon } from '../constants'
|
|
12
12
|
import { isFileBasedEngine, type ContainerConfig } from '../../types'
|
|
13
|
+
import { getEngineMetadata } from '../helpers'
|
|
13
14
|
|
|
14
15
|
function formatDate(dateString: string): string {
|
|
15
16
|
const date = new Date(dateString)
|
|
@@ -43,6 +44,7 @@ async function displayContainerInfo(
|
|
|
43
44
|
})
|
|
44
45
|
|
|
45
46
|
if (options.json) {
|
|
47
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
46
48
|
console.log(
|
|
47
49
|
JSON.stringify(
|
|
48
50
|
{
|
|
@@ -50,6 +52,7 @@ async function displayContainerInfo(
|
|
|
50
52
|
status: actualStatus,
|
|
51
53
|
connectionString,
|
|
52
54
|
dataDir,
|
|
55
|
+
...metadata,
|
|
53
56
|
},
|
|
54
57
|
null,
|
|
55
58
|
2,
|
|
@@ -147,11 +150,13 @@ async function displayAllContainersInfo(
|
|
|
147
150
|
const dataDir = paths.getContainerDataPath(config.name, {
|
|
148
151
|
engine: config.engine,
|
|
149
152
|
})
|
|
153
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
150
154
|
return {
|
|
151
155
|
...config,
|
|
152
156
|
status: actualStatus,
|
|
153
157
|
connectionString,
|
|
154
158
|
dataDir,
|
|
159
|
+
...metadata,
|
|
155
160
|
}
|
|
156
161
|
}),
|
|
157
162
|
)
|
package/cli/commands/list.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getRegistryForEngine,
|
|
15
15
|
type UnregisteredFile,
|
|
16
16
|
} from '../../engines/file-based-utils'
|
|
17
|
+
import { getEngineMetadata } from '../helpers'
|
|
17
18
|
|
|
18
19
|
type UnregisteredFileWithEngine = UnregisteredFile & { engine: Engine }
|
|
19
20
|
|
|
@@ -159,6 +160,7 @@ export const listCommand = new Command('list')
|
|
|
159
160
|
const containersWithSize = await Promise.all(
|
|
160
161
|
containers.map(async (container) => ({
|
|
161
162
|
...container,
|
|
163
|
+
...(await getEngineMetadata(container.engine)),
|
|
162
164
|
sizeBytes: await getContainerSize(container),
|
|
163
165
|
})),
|
|
164
166
|
)
|
|
@@ -228,6 +228,8 @@ function validateConnectionString(
|
|
|
228
228
|
return 'Connection string must start with http:// or https://'
|
|
229
229
|
}
|
|
230
230
|
break
|
|
231
|
+
case Engine.TigerBeetle:
|
|
232
|
+
return 'TigerBeetle does not support remote dumps (custom binary protocol)'
|
|
231
233
|
case Engine.SQLite:
|
|
232
234
|
case Engine.DuckDB:
|
|
233
235
|
return 'File-based engines do not support remote connection strings'
|
|
@@ -49,6 +49,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
49
49
|
[Engine.TypeDB]: '[TB]',
|
|
50
50
|
[Engine.InfluxDB]: '[IX]',
|
|
51
51
|
[Engine.Weaviate]: '[WV]',
|
|
52
|
+
[Engine.TigerBeetle]: '[TT]',
|
|
52
53
|
}
|
|
53
54
|
const icons = PREVIEW_ENGINES.map((engine) => {
|
|
54
55
|
const icon = ASCII_ICONS[engine] || '[??]'
|
|
@@ -79,6 +80,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
79
80
|
[Engine.TypeDB]: '\ue706',
|
|
80
81
|
[Engine.InfluxDB]: '\udb85\udf95',
|
|
81
82
|
[Engine.Weaviate]: '\uf0e8',
|
|
83
|
+
[Engine.TigerBeetle]: '\uf0d6',
|
|
82
84
|
}
|
|
83
85
|
const icons = PREVIEW_ENGINES.map((engine) => {
|
|
84
86
|
const icon = NERD_ICONS[engine] || '\ue706'
|
|
@@ -109,6 +111,7 @@ function generatePreviewLine(mode: IconMode): string {
|
|
|
109
111
|
[Engine.TypeDB]: '\u{1F916}',
|
|
110
112
|
[Engine.InfluxDB]: '\u{1F4C8}',
|
|
111
113
|
[Engine.Weaviate]: '\u{1F52E}',
|
|
114
|
+
[Engine.TigerBeetle]: '\u{1F42F}',
|
|
112
115
|
}
|
|
113
116
|
const icons = PREVIEW_ENGINES.map((engine) => EMOJI_ICONS[engine] || '\u25A3')
|
|
114
117
|
return icons.join(' ')
|
|
@@ -288,6 +288,13 @@ export async function handleOpenShell(
|
|
|
288
288
|
engineSpecificInstalled = false
|
|
289
289
|
engineSpecificValue = null
|
|
290
290
|
engineSpecificInstallValue = null
|
|
291
|
+
} else if (config.engine === 'tigerbeetle') {
|
|
292
|
+
// TigerBeetle uses tigerbeetle repl command
|
|
293
|
+
defaultShellName = 'tigerbeetle repl'
|
|
294
|
+
engineSpecificCli = null
|
|
295
|
+
engineSpecificInstalled = false
|
|
296
|
+
engineSpecificValue = null
|
|
297
|
+
engineSpecificInstallValue = null
|
|
291
298
|
} else {
|
|
292
299
|
defaultShellName = 'psql'
|
|
293
300
|
engineSpecificCli = 'pgcli'
|
|
@@ -1737,6 +1744,22 @@ async function launchShell(
|
|
|
1737
1744
|
shellArgs = ['console', ...getConsoleBaseArgs(config.port)]
|
|
1738
1745
|
}
|
|
1739
1746
|
installHint = 'spindb engines download typedb'
|
|
1747
|
+
} else if (config.engine === 'tigerbeetle') {
|
|
1748
|
+
// TigerBeetle uses tigerbeetle repl command
|
|
1749
|
+
// Cluster ID 0 is the default for local single-node development.
|
|
1750
|
+
// TigerBeetle format/start also use cluster 0 (see engines/tigerbeetle/index.ts).
|
|
1751
|
+
const clusterId = 0
|
|
1752
|
+
const engine = getEngine(config.engine)
|
|
1753
|
+
const tigerbeetlePath = await engine
|
|
1754
|
+
.getTigerBeetlePath(config.version)
|
|
1755
|
+
.catch(() => null)
|
|
1756
|
+
shellCmd = tigerbeetlePath || 'tigerbeetle'
|
|
1757
|
+
shellArgs = [
|
|
1758
|
+
'repl',
|
|
1759
|
+
`--cluster=${clusterId}`,
|
|
1760
|
+
`--addresses=127.0.0.1:${config.port}`,
|
|
1761
|
+
]
|
|
1762
|
+
installHint = 'spindb engines download tigerbeetle'
|
|
1740
1763
|
} else {
|
|
1741
1764
|
// PostgreSQL default shell - look up downloaded binary path
|
|
1742
1765
|
const psqlPath = await configManager.getBinaryPath('psql')
|
package/cli/commands/restore.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { platformService } from '../../core/platform-service'
|
|
|
20
20
|
import { TransactionManager } from '../../core/transaction-manager'
|
|
21
21
|
import { isFileBasedEngine } from '../../types'
|
|
22
22
|
import { logDebug } from '../../core/error-handler'
|
|
23
|
+
import { getEngineMetadata } from '../helpers'
|
|
23
24
|
|
|
24
25
|
export const restoreCommand = new Command('restore')
|
|
25
26
|
.description('Restore a backup to a container')
|
|
@@ -502,6 +503,7 @@ export const restoreCommand = new Command('restore')
|
|
|
502
503
|
)
|
|
503
504
|
|
|
504
505
|
if (options.json) {
|
|
506
|
+
const metadata = await getEngineMetadata(engineName)
|
|
505
507
|
console.log(
|
|
506
508
|
JSON.stringify({
|
|
507
509
|
success: true,
|
|
@@ -512,6 +514,7 @@ export const restoreCommand = new Command('restore')
|
|
|
512
514
|
sourceType: options.fromUrl ? 'remote' : 'file',
|
|
513
515
|
connectionString,
|
|
514
516
|
overwritten: databaseExists,
|
|
517
|
+
...metadata,
|
|
515
518
|
}),
|
|
516
519
|
)
|
|
517
520
|
} else {
|
package/cli/commands/start.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { createSpinner } from '../ui/spinner'
|
|
|
11
11
|
import { uiWarning } from '../ui/theme'
|
|
12
12
|
import { Engine, isFileBasedEngine } from '../../types'
|
|
13
13
|
import { exitWithError, logDebug } from '../../core/error-handler'
|
|
14
|
+
import { getEngineMetadata } from '../helpers'
|
|
14
15
|
|
|
15
16
|
export const startCommand = new Command('start')
|
|
16
17
|
.description('Start a container')
|
|
@@ -189,6 +190,7 @@ export const startCommand = new Command('start')
|
|
|
189
190
|
const connectionString = engine.getConnectionString(config)
|
|
190
191
|
|
|
191
192
|
if (options.json) {
|
|
193
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
192
194
|
console.log(
|
|
193
195
|
JSON.stringify({
|
|
194
196
|
success: true,
|
|
@@ -197,6 +199,7 @@ export const startCommand = new Command('start')
|
|
|
197
199
|
port: result.finalPort,
|
|
198
200
|
connectionString,
|
|
199
201
|
portChanged: result.retriesUsed > 0,
|
|
202
|
+
...metadata,
|
|
200
203
|
}),
|
|
201
204
|
)
|
|
202
205
|
} else {
|
package/cli/commands/url.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { platformService } from '../../core/platform-service'
|
|
|
4
4
|
import { getEngine } from '../../engines'
|
|
5
5
|
import { promptContainerSelect } from '../ui/prompts'
|
|
6
6
|
import { uiError, uiWarning, uiSuccess } from '../ui/theme'
|
|
7
|
+
import { getEngineMetadata } from '../helpers'
|
|
7
8
|
|
|
8
9
|
export const urlCommand = new Command('url')
|
|
9
10
|
.alias('connection-string')
|
|
@@ -55,6 +56,7 @@ export const urlCommand = new Command('url')
|
|
|
55
56
|
)
|
|
56
57
|
|
|
57
58
|
if (options.json) {
|
|
59
|
+
const metadata = await getEngineMetadata(config.engine)
|
|
58
60
|
const jsonOutput =
|
|
59
61
|
config.engine === 'sqlite'
|
|
60
62
|
? {
|
|
@@ -62,6 +64,7 @@ export const urlCommand = new Command('url')
|
|
|
62
64
|
path: databaseName,
|
|
63
65
|
engine: config.engine,
|
|
64
66
|
container: config.name,
|
|
67
|
+
...metadata,
|
|
65
68
|
}
|
|
66
69
|
: {
|
|
67
70
|
connectionString,
|
|
@@ -71,6 +74,7 @@ export const urlCommand = new Command('url')
|
|
|
71
74
|
user: config.engine === 'postgresql' ? 'postgres' : 'root',
|
|
72
75
|
engine: config.engine,
|
|
73
76
|
container: config.name,
|
|
77
|
+
...metadata,
|
|
74
78
|
}
|
|
75
79
|
console.log(JSON.stringify(jsonOutput, null, 2))
|
|
76
80
|
return
|
package/cli/constants.ts
CHANGED
|
@@ -78,6 +78,7 @@ export const ENGINE_BRAND_COLORS: Record<Engine, BrandColor> = {
|
|
|
78
78
|
[Engine.TypeDB]: { foreground: '#FFFFFF', background: '#7B2D8E' }, // White on purple
|
|
79
79
|
[Engine.InfluxDB]: { foreground: '#FFFFFF', background: '#9394FF' }, // White on indigo/purple
|
|
80
80
|
[Engine.Weaviate]: { foreground: '#FFFFFF', background: '#00D1A8' }, // White on green
|
|
81
|
+
[Engine.TigerBeetle]: { foreground: '#FFFFFF', background: '#FF6600' }, // White on orange
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
// ASCII fallback icons - work in any terminal
|
|
@@ -101,6 +102,7 @@ const ASCII_ICONS: Record<Engine, string> = {
|
|
|
101
102
|
[Engine.TypeDB]: '[TB]',
|
|
102
103
|
[Engine.InfluxDB]: '[IX]',
|
|
103
104
|
[Engine.Weaviate]: '[WV]',
|
|
105
|
+
[Engine.TigerBeetle]: '[TT]',
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
// Nerd Font icons - require a patched font
|
|
@@ -125,6 +127,7 @@ const NERD_ICONS: Record<Engine, string> = {
|
|
|
125
127
|
[Engine.TypeDB]: '\ue706', // nf-dev-database (knowledge graph)
|
|
126
128
|
[Engine.InfluxDB]: '\udb85\udf95', // nf-md-chart-line (time-series)
|
|
127
129
|
[Engine.Weaviate]: '\uf0e8', // nf-fa-sitemap (vector graph)
|
|
130
|
+
[Engine.TigerBeetle]: '\uf0d6', // nf-fa-money (financial ledger)
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
// Emoji icons - original icons, inconsistent width across terminals
|
|
@@ -148,6 +151,7 @@ const EMOJI_ICONS: Record<Engine, string> = {
|
|
|
148
151
|
[Engine.TypeDB]: '🤖',
|
|
149
152
|
[Engine.InfluxDB]: '📈',
|
|
150
153
|
[Engine.Weaviate]: '🔮',
|
|
154
|
+
[Engine.TigerBeetle]: '🐯',
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
const DEFAULT_ICONS: Record<IconMode, string> = {
|