spindb 0.7.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -294
- package/cli/commands/config.ts +7 -1
- package/cli/commands/connect.ts +1 -0
- package/cli/commands/edit.ts +10 -0
- package/cli/commands/engines.ts +10 -188
- package/cli/commands/info.ts +7 -14
- package/cli/commands/list.ts +2 -9
- package/cli/commands/logs.ts +130 -0
- package/cli/commands/menu/backup-handlers.ts +798 -0
- package/cli/commands/menu/container-handlers.ts +832 -0
- package/cli/commands/menu/engine-handlers.ts +382 -0
- package/cli/commands/menu/index.ts +184 -0
- package/cli/commands/menu/shared.ts +26 -0
- package/cli/commands/menu/shell-handlers.ts +331 -0
- package/cli/commands/menu/sql-handlers.ts +197 -0
- package/cli/commands/menu/update-handlers.ts +94 -0
- package/cli/commands/run.ts +150 -0
- package/cli/commands/url.ts +19 -5
- package/cli/constants.ts +10 -0
- package/cli/helpers.ts +152 -0
- package/cli/index.ts +5 -2
- package/cli/ui/prompts.ts +3 -11
- package/core/dependency-manager.ts +0 -163
- package/core/error-handler.ts +0 -26
- package/core/platform-service.ts +60 -40
- package/core/start-with-retry.ts +3 -28
- package/core/transaction-manager.ts +0 -8
- package/engines/base-engine.ts +10 -0
- package/engines/mysql/binary-detection.ts +1 -1
- package/engines/mysql/index.ts +78 -2
- package/engines/postgresql/index.ts +49 -0
- package/package.json +1 -1
- package/cli/commands/menu.ts +0 -2670
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { existsSync } from 'fs'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { containerManager } from '../../core/container-manager'
|
|
5
|
+
import { processManager } from '../../core/process-manager'
|
|
6
|
+
import { getEngine } from '../../engines'
|
|
7
|
+
import { promptInstallDependencies } from '../ui/prompts'
|
|
8
|
+
import { error, warning } from '../ui/theme'
|
|
9
|
+
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
10
|
+
|
|
11
|
+
export const runCommand = new Command('run')
|
|
12
|
+
.description('Run SQL file or statement against a container')
|
|
13
|
+
.argument('<name>', 'Container name')
|
|
14
|
+
.argument('[file]', 'Path to SQL file')
|
|
15
|
+
.option('-d, --database <name>', 'Target database (defaults to primary)')
|
|
16
|
+
.option('--sql <statement>', 'SQL statement to execute (alternative to file)')
|
|
17
|
+
.action(
|
|
18
|
+
async (
|
|
19
|
+
name: string,
|
|
20
|
+
file: string | undefined,
|
|
21
|
+
options: { database?: string; sql?: string },
|
|
22
|
+
) => {
|
|
23
|
+
try {
|
|
24
|
+
const containerName = name
|
|
25
|
+
|
|
26
|
+
// Get container config
|
|
27
|
+
const config = await containerManager.getConfig(containerName)
|
|
28
|
+
if (!config) {
|
|
29
|
+
console.error(error(`Container "${containerName}" not found`))
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { engine: engineName } = config
|
|
34
|
+
|
|
35
|
+
// Check if running
|
|
36
|
+
const running = await processManager.isRunning(containerName, {
|
|
37
|
+
engine: engineName,
|
|
38
|
+
})
|
|
39
|
+
if (!running) {
|
|
40
|
+
console.error(
|
|
41
|
+
error(
|
|
42
|
+
`Container "${containerName}" is not running. Start it first with: spindb start ${containerName}`,
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
process.exit(1)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Validate: must have either file or --sql, not both
|
|
49
|
+
if (file && options.sql) {
|
|
50
|
+
console.error(
|
|
51
|
+
error('Cannot specify both a file and --sql option. Choose one.'),
|
|
52
|
+
)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!file && !options.sql) {
|
|
57
|
+
console.error(error('Must provide either a SQL file or --sql option'))
|
|
58
|
+
console.log(
|
|
59
|
+
chalk.gray(' Usage: spindb run <container> <file.sql>'),
|
|
60
|
+
)
|
|
61
|
+
console.log(
|
|
62
|
+
chalk.gray(' or: spindb run <container> --sql "SELECT ..."'),
|
|
63
|
+
)
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Validate file exists
|
|
68
|
+
if (file && !existsSync(file)) {
|
|
69
|
+
console.error(error(`SQL file not found: ${file}`))
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Get engine
|
|
74
|
+
const engine = getEngine(engineName)
|
|
75
|
+
|
|
76
|
+
// Check for required client tools
|
|
77
|
+
let missingDeps = await getMissingDependencies(engineName)
|
|
78
|
+
if (missingDeps.length > 0) {
|
|
79
|
+
console.log(
|
|
80
|
+
warning(
|
|
81
|
+
`Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// Offer to install
|
|
86
|
+
const installed = await promptInstallDependencies(
|
|
87
|
+
missingDeps[0].binary,
|
|
88
|
+
engineName,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if (!installed) {
|
|
92
|
+
process.exit(1)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Verify installation worked
|
|
96
|
+
missingDeps = await getMissingDependencies(engineName)
|
|
97
|
+
if (missingDeps.length > 0) {
|
|
98
|
+
console.error(
|
|
99
|
+
error(
|
|
100
|
+
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
process.exit(1)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(chalk.green(' ✓ All required tools are now available'))
|
|
107
|
+
console.log()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Determine target database
|
|
111
|
+
const database = options.database || config.database
|
|
112
|
+
|
|
113
|
+
// Run the SQL
|
|
114
|
+
await engine.runScript(config, {
|
|
115
|
+
file,
|
|
116
|
+
sql: options.sql,
|
|
117
|
+
database,
|
|
118
|
+
})
|
|
119
|
+
} catch (err) {
|
|
120
|
+
const e = err as Error
|
|
121
|
+
|
|
122
|
+
// Check if this is a missing tool error
|
|
123
|
+
const missingToolPatterns = [
|
|
124
|
+
'psql not found',
|
|
125
|
+
'mysql not found',
|
|
126
|
+
'mysql client not found',
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
const matchingPattern = missingToolPatterns.find((p) =>
|
|
130
|
+
e.message.toLowerCase().includes(p.toLowerCase()),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if (matchingPattern) {
|
|
134
|
+
const missingTool = matchingPattern
|
|
135
|
+
.replace(' not found', '')
|
|
136
|
+
.replace(' client', '')
|
|
137
|
+
const installed = await promptInstallDependencies(missingTool)
|
|
138
|
+
if (installed) {
|
|
139
|
+
console.log(
|
|
140
|
+
chalk.yellow(' Please re-run your command to continue.'),
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
process.exit(1)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.error(error(e.message))
|
|
147
|
+
process.exit(1)
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
)
|
package/cli/commands/url.ts
CHANGED
|
@@ -11,10 +11,11 @@ export const urlCommand = new Command('url')
|
|
|
11
11
|
.argument('[name]', 'Container name')
|
|
12
12
|
.option('-c, --copy', 'Copy to clipboard')
|
|
13
13
|
.option('-d, --database <database>', 'Use different database name')
|
|
14
|
+
.option('--json', 'Output as JSON with additional connection info')
|
|
14
15
|
.action(
|
|
15
16
|
async (
|
|
16
17
|
name: string | undefined,
|
|
17
|
-
options: { copy?: boolean; database?: string },
|
|
18
|
+
options: { copy?: boolean; database?: string; json?: boolean },
|
|
18
19
|
) => {
|
|
19
20
|
try {
|
|
20
21
|
let containerName = name
|
|
@@ -45,10 +46,23 @@ export const urlCommand = new Command('url')
|
|
|
45
46
|
|
|
46
47
|
// Get connection string
|
|
47
48
|
const engine = getEngine(config.engine)
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
const databaseName = options.database || config.database
|
|
50
|
+
const connectionString = engine.getConnectionString(config, databaseName)
|
|
51
|
+
|
|
52
|
+
// JSON output
|
|
53
|
+
if (options.json) {
|
|
54
|
+
const jsonOutput = {
|
|
55
|
+
connectionString,
|
|
56
|
+
host: '127.0.0.1',
|
|
57
|
+
port: config.port,
|
|
58
|
+
database: databaseName,
|
|
59
|
+
user: config.engine === 'postgresql' ? 'postgres' : 'root',
|
|
60
|
+
engine: config.engine,
|
|
61
|
+
container: config.name,
|
|
62
|
+
}
|
|
63
|
+
console.log(JSON.stringify(jsonOutput, null, 2))
|
|
64
|
+
return
|
|
65
|
+
}
|
|
52
66
|
|
|
53
67
|
// Copy to clipboard if requested
|
|
54
68
|
if (options.copy) {
|
package/cli/constants.ts
ADDED
package/cli/helpers.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { existsSync } from 'fs'
|
|
2
|
+
import { readdir, lstat } from 'fs/promises'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { exec } from 'child_process'
|
|
5
|
+
import { promisify } from 'util'
|
|
6
|
+
import { paths } from '../config/paths'
|
|
7
|
+
import {
|
|
8
|
+
getMysqldPath,
|
|
9
|
+
getMysqlVersion,
|
|
10
|
+
isMariaDB,
|
|
11
|
+
} from '../engines/mysql/binary-detection'
|
|
12
|
+
|
|
13
|
+
const execAsync = promisify(exec)
|
|
14
|
+
|
|
15
|
+
export type InstalledPostgresEngine = {
|
|
16
|
+
engine: 'postgresql'
|
|
17
|
+
version: string
|
|
18
|
+
platform: string
|
|
19
|
+
arch: string
|
|
20
|
+
path: string
|
|
21
|
+
sizeBytes: number
|
|
22
|
+
source: 'downloaded'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type InstalledMysqlEngine = {
|
|
26
|
+
engine: 'mysql'
|
|
27
|
+
version: string
|
|
28
|
+
path: string
|
|
29
|
+
source: 'system'
|
|
30
|
+
isMariaDB: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type InstalledEngine = InstalledPostgresEngine | InstalledMysqlEngine
|
|
34
|
+
|
|
35
|
+
async function getPostgresVersion(binPath: string): Promise<string | null> {
|
|
36
|
+
const postgresPath = join(binPath, 'bin', 'postgres')
|
|
37
|
+
if (!existsSync(postgresPath)) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const { stdout } = await execAsync(`"${postgresPath}" --version`)
|
|
43
|
+
const match = stdout.match(/\(PostgreSQL\)\s+([\d.]+)/)
|
|
44
|
+
return match ? match[1] : null
|
|
45
|
+
} catch {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getInstalledPostgresEngines(): Promise<InstalledPostgresEngine[]> {
|
|
51
|
+
const binDir = paths.bin
|
|
52
|
+
|
|
53
|
+
if (!existsSync(binDir)) {
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const entries = await readdir(binDir, { withFileTypes: true })
|
|
58
|
+
const engines: InstalledPostgresEngine[] = []
|
|
59
|
+
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
const match = entry.name.match(/^(\w+)-(.+)-(\w+)-(\w+)$/)
|
|
63
|
+
if (match && match[1] === 'postgresql') {
|
|
64
|
+
const [, , majorVersion, platform, arch] = match
|
|
65
|
+
const dirPath = join(binDir, entry.name)
|
|
66
|
+
|
|
67
|
+
const actualVersion =
|
|
68
|
+
(await getPostgresVersion(dirPath)) || majorVersion
|
|
69
|
+
|
|
70
|
+
let sizeBytes = 0
|
|
71
|
+
try {
|
|
72
|
+
const files = await readdir(dirPath, { recursive: true })
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
try {
|
|
75
|
+
const filePath = join(dirPath, file.toString())
|
|
76
|
+
const fileStat = await lstat(filePath)
|
|
77
|
+
if (fileStat.isFile()) {
|
|
78
|
+
sizeBytes += fileStat.size
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// Skip files we can't stat
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// Skip directories we can't read
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
engines.push({
|
|
89
|
+
engine: 'postgresql',
|
|
90
|
+
version: actualVersion,
|
|
91
|
+
platform,
|
|
92
|
+
arch,
|
|
93
|
+
path: dirPath,
|
|
94
|
+
sizeBytes,
|
|
95
|
+
source: 'downloaded',
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
engines.sort((a, b) => compareVersions(b.version, a.version))
|
|
102
|
+
|
|
103
|
+
return engines
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function getInstalledMysqlEngine(): Promise<InstalledMysqlEngine | null> {
|
|
107
|
+
const mysqldPath = await getMysqldPath()
|
|
108
|
+
if (!mysqldPath) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const version = await getMysqlVersion(mysqldPath)
|
|
113
|
+
if (!version) {
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const mariadb = await isMariaDB()
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
engine: 'mysql',
|
|
121
|
+
version,
|
|
122
|
+
path: mysqldPath,
|
|
123
|
+
source: 'system',
|
|
124
|
+
isMariaDB: mariadb,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function compareVersions(a: string, b: string): number {
|
|
129
|
+
const partsA = a.split('.').map((p) => parseInt(p, 10) || 0)
|
|
130
|
+
const partsB = b.split('.').map((p) => parseInt(p, 10) || 0)
|
|
131
|
+
|
|
132
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
133
|
+
const numA = partsA[i] || 0
|
|
134
|
+
const numB = partsB[i] || 0
|
|
135
|
+
if (numA !== numB) return numA - numB
|
|
136
|
+
}
|
|
137
|
+
return 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function getInstalledEngines(): Promise<InstalledEngine[]> {
|
|
141
|
+
const engines: InstalledEngine[] = []
|
|
142
|
+
|
|
143
|
+
const pgEngines = await getInstalledPostgresEngines()
|
|
144
|
+
engines.push(...pgEngines)
|
|
145
|
+
|
|
146
|
+
const mysqlEngine = await getInstalledMysqlEngine()
|
|
147
|
+
if (mysqlEngine) {
|
|
148
|
+
engines.push(mysqlEngine)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return engines
|
|
152
|
+
}
|
package/cli/index.ts
CHANGED
|
@@ -22,6 +22,8 @@ import { urlCommand } from './commands/url'
|
|
|
22
22
|
import { infoCommand } from './commands/info'
|
|
23
23
|
import { selfUpdateCommand } from './commands/self-update'
|
|
24
24
|
import { versionCommand } from './commands/version'
|
|
25
|
+
import { runCommand } from './commands/run'
|
|
26
|
+
import { logsCommand } from './commands/logs'
|
|
25
27
|
import { updateManager } from '../core/update-manager'
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -119,11 +121,12 @@ export async function run(): Promise<void> {
|
|
|
119
121
|
program.addCommand(infoCommand)
|
|
120
122
|
program.addCommand(selfUpdateCommand)
|
|
121
123
|
program.addCommand(versionCommand)
|
|
124
|
+
program.addCommand(runCommand)
|
|
125
|
+
program.addCommand(logsCommand)
|
|
122
126
|
|
|
123
127
|
// If no arguments provided, show interactive menu
|
|
124
128
|
if (process.argv.length <= 2) {
|
|
125
|
-
|
|
126
|
-
await menu.parseAsync([])
|
|
129
|
+
await menuCommand.parseAsync([])
|
|
127
130
|
return
|
|
128
131
|
}
|
|
129
132
|
|
package/cli/ui/prompts.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
installEngineDependencies,
|
|
12
12
|
} from '../../core/dependency-manager'
|
|
13
13
|
import { getEngineDependencies } from '../../config/os-dependencies'
|
|
14
|
+
import { getEngineIcon } from '../constants'
|
|
14
15
|
import type { ContainerConfig } from '../../types'
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -37,23 +38,14 @@ export async function promptContainerName(
|
|
|
37
38
|
return name
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
/**
|
|
41
|
-
* Engine icons for display
|
|
42
|
-
*/
|
|
43
|
-
const engineIcons: Record<string, string> = {
|
|
44
|
-
postgresql: '🐘',
|
|
45
|
-
mysql: '🐬',
|
|
46
|
-
}
|
|
47
|
-
|
|
48
41
|
/**
|
|
49
42
|
* Prompt for database engine selection
|
|
50
43
|
*/
|
|
51
44
|
export async function promptEngine(): Promise<string> {
|
|
52
45
|
const engines = listEngines()
|
|
53
46
|
|
|
54
|
-
// Build choices from available engines
|
|
55
47
|
const choices = engines.map((e) => ({
|
|
56
|
-
name: `${
|
|
48
|
+
name: `${getEngineIcon(e.name)} ${e.displayName} ${chalk.gray(`(versions: ${e.supportedVersions.join(', ')})`)}`,
|
|
57
49
|
value: e.name,
|
|
58
50
|
short: e.displayName,
|
|
59
51
|
}))
|
|
@@ -227,7 +219,7 @@ export async function promptContainerSelect(
|
|
|
227
219
|
name: 'container',
|
|
228
220
|
message,
|
|
229
221
|
choices: containers.map((c) => ({
|
|
230
|
-
name: `${c.name} ${chalk.gray(`(${
|
|
222
|
+
name: `${c.name} ${chalk.gray(`(${getEngineIcon(c.engine)} ${c.engine} ${c.version}, port ${c.port})`)} ${
|
|
231
223
|
c.status === 'running'
|
|
232
224
|
? chalk.green('● running')
|
|
233
225
|
: chalk.gray('○ stopped')
|
|
@@ -43,13 +43,6 @@ export type InstallResult = {
|
|
|
43
43
|
error?: string
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// =============================================================================
|
|
47
|
-
// Package Manager Detection
|
|
48
|
-
// =============================================================================
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Detect which package manager is available on the current system
|
|
52
|
-
*/
|
|
53
46
|
export async function detectPackageManager(): Promise<DetectedPackageManager | null> {
|
|
54
47
|
const { platform } = platformService.getPlatformInfo()
|
|
55
48
|
|
|
@@ -81,13 +74,6 @@ export function getCurrentPlatform(): Platform {
|
|
|
81
74
|
return platformService.getPlatformInfo().platform as Platform
|
|
82
75
|
}
|
|
83
76
|
|
|
84
|
-
// =============================================================================
|
|
85
|
-
// Dependency Checking
|
|
86
|
-
// =============================================================================
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Check if a binary is installed and get its path
|
|
90
|
-
*/
|
|
91
77
|
export async function findBinary(
|
|
92
78
|
binary: string,
|
|
93
79
|
): Promise<{ path: string; version?: string } | null> {
|
|
@@ -164,13 +150,6 @@ export async function getAllMissingDependencies(): Promise<Dependency[]> {
|
|
|
164
150
|
return statuses.filter((s) => !s.installed).map((s) => s.dependency)
|
|
165
151
|
}
|
|
166
152
|
|
|
167
|
-
// =============================================================================
|
|
168
|
-
// Installation
|
|
169
|
-
// =============================================================================
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Check if stdin is a TTY (interactive terminal)
|
|
173
|
-
*/
|
|
174
153
|
function hasTTY(): boolean {
|
|
175
154
|
return process.stdin.isTTY === true
|
|
176
155
|
}
|
|
@@ -362,13 +341,6 @@ export async function installAllDependencies(
|
|
|
362
341
|
return results
|
|
363
342
|
}
|
|
364
343
|
|
|
365
|
-
// =============================================================================
|
|
366
|
-
// Manual Installation Instructions
|
|
367
|
-
// =============================================================================
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Get manual installation instructions for a dependency
|
|
371
|
-
*/
|
|
372
344
|
export function getManualInstallInstructions(
|
|
373
345
|
dependency: Dependency,
|
|
374
346
|
platform: Platform = getCurrentPlatform(),
|
|
@@ -376,188 +348,53 @@ export function getManualInstallInstructions(
|
|
|
376
348
|
return dependency.manualInstall[platform] || []
|
|
377
349
|
}
|
|
378
350
|
|
|
379
|
-
/**
|
|
380
|
-
* Get manual installation instructions for all missing dependencies of an engine
|
|
381
|
-
*/
|
|
382
|
-
export function getEngineManualInstallInstructions(
|
|
383
|
-
engine: string,
|
|
384
|
-
missingDeps: Dependency[],
|
|
385
|
-
platform: Platform = getCurrentPlatform(),
|
|
386
|
-
): string[] {
|
|
387
|
-
// Since all deps usually come from the same package, just get instructions from the first one
|
|
388
|
-
if (missingDeps.length === 0) return []
|
|
389
|
-
|
|
390
|
-
return getManualInstallInstructions(missingDeps[0], platform)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// =============================================================================
|
|
394
|
-
// High-Level API
|
|
395
|
-
// =============================================================================
|
|
396
|
-
|
|
397
|
-
export type DependencyCheckResult = {
|
|
398
|
-
engine: string
|
|
399
|
-
allInstalled: boolean
|
|
400
|
-
installed: DependencyStatus[]
|
|
401
|
-
missing: DependencyStatus[]
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Get a complete dependency report for an engine
|
|
406
|
-
*/
|
|
407
|
-
export async function getDependencyReport(
|
|
408
|
-
engine: string,
|
|
409
|
-
): Promise<DependencyCheckResult> {
|
|
410
|
-
const statuses = await checkEngineDependencies(engine)
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
engine,
|
|
414
|
-
allInstalled: statuses.every((s) => s.installed),
|
|
415
|
-
installed: statuses.filter((s) => s.installed),
|
|
416
|
-
missing: statuses.filter((s) => !s.installed),
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Get dependency reports for all engines
|
|
422
|
-
*/
|
|
423
|
-
export async function getAllDependencyReports(): Promise<
|
|
424
|
-
DependencyCheckResult[]
|
|
425
|
-
> {
|
|
426
|
-
const engines = ['postgresql', 'mysql']
|
|
427
|
-
const reports = await Promise.all(
|
|
428
|
-
engines.map((engine) => getDependencyReport(engine)),
|
|
429
|
-
)
|
|
430
|
-
return reports
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// =============================================================================
|
|
434
|
-
// usql (Enhanced Shell) Support
|
|
435
|
-
// =============================================================================
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Check if usql is installed
|
|
439
|
-
*/
|
|
440
351
|
export async function isUsqlInstalled(): Promise<boolean> {
|
|
441
352
|
const status = await checkDependency(usqlDependency)
|
|
442
353
|
return status.installed
|
|
443
354
|
}
|
|
444
355
|
|
|
445
|
-
/**
|
|
446
|
-
* Get usql dependency status
|
|
447
|
-
*/
|
|
448
|
-
export async function getUsqlStatus(): Promise<DependencyStatus> {
|
|
449
|
-
return checkDependency(usqlDependency)
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Install usql using the detected package manager
|
|
454
|
-
*/
|
|
455
356
|
export async function installUsql(
|
|
456
357
|
packageManager: DetectedPackageManager,
|
|
457
358
|
): Promise<InstallResult> {
|
|
458
359
|
return installDependency(usqlDependency, packageManager)
|
|
459
360
|
}
|
|
460
361
|
|
|
461
|
-
/**
|
|
462
|
-
* Get usql manual installation instructions
|
|
463
|
-
*/
|
|
464
362
|
export function getUsqlManualInstructions(
|
|
465
363
|
platform: Platform = getCurrentPlatform(),
|
|
466
364
|
): string[] {
|
|
467
365
|
return getManualInstallInstructions(usqlDependency, platform)
|
|
468
366
|
}
|
|
469
367
|
|
|
470
|
-
/**
|
|
471
|
-
* Get the usql dependency definition
|
|
472
|
-
*/
|
|
473
|
-
export function getUsqlDependency(): Dependency {
|
|
474
|
-
return usqlDependency
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// =============================================================================
|
|
478
|
-
// pgcli (PostgreSQL Enhanced Shell) Support
|
|
479
|
-
// =============================================================================
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Check if pgcli is installed
|
|
483
|
-
*/
|
|
484
368
|
export async function isPgcliInstalled(): Promise<boolean> {
|
|
485
369
|
const status = await checkDependency(pgcliDependency)
|
|
486
370
|
return status.installed
|
|
487
371
|
}
|
|
488
372
|
|
|
489
|
-
/**
|
|
490
|
-
* Get pgcli dependency status
|
|
491
|
-
*/
|
|
492
|
-
export async function getPgcliStatus(): Promise<DependencyStatus> {
|
|
493
|
-
return checkDependency(pgcliDependency)
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Install pgcli using the detected package manager
|
|
498
|
-
*/
|
|
499
373
|
export async function installPgcli(
|
|
500
374
|
packageManager: DetectedPackageManager,
|
|
501
375
|
): Promise<InstallResult> {
|
|
502
376
|
return installDependency(pgcliDependency, packageManager)
|
|
503
377
|
}
|
|
504
378
|
|
|
505
|
-
/**
|
|
506
|
-
* Get pgcli manual installation instructions
|
|
507
|
-
*/
|
|
508
379
|
export function getPgcliManualInstructions(
|
|
509
380
|
platform: Platform = getCurrentPlatform(),
|
|
510
381
|
): string[] {
|
|
511
382
|
return getManualInstallInstructions(pgcliDependency, platform)
|
|
512
383
|
}
|
|
513
384
|
|
|
514
|
-
/**
|
|
515
|
-
* Get the pgcli dependency definition
|
|
516
|
-
*/
|
|
517
|
-
export function getPgcliDependency(): Dependency {
|
|
518
|
-
return pgcliDependency
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// =============================================================================
|
|
522
|
-
// mycli (MySQL Enhanced Shell) Support
|
|
523
|
-
// =============================================================================
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Check if mycli is installed
|
|
527
|
-
*/
|
|
528
385
|
export async function isMycliInstalled(): Promise<boolean> {
|
|
529
386
|
const status = await checkDependency(mycliDependency)
|
|
530
387
|
return status.installed
|
|
531
388
|
}
|
|
532
389
|
|
|
533
|
-
/**
|
|
534
|
-
* Get mycli dependency status
|
|
535
|
-
*/
|
|
536
|
-
export async function getMycliStatus(): Promise<DependencyStatus> {
|
|
537
|
-
return checkDependency(mycliDependency)
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Install mycli using the detected package manager
|
|
542
|
-
*/
|
|
543
390
|
export async function installMycli(
|
|
544
391
|
packageManager: DetectedPackageManager,
|
|
545
392
|
): Promise<InstallResult> {
|
|
546
393
|
return installDependency(mycliDependency, packageManager)
|
|
547
394
|
}
|
|
548
395
|
|
|
549
|
-
/**
|
|
550
|
-
* Get mycli manual installation instructions
|
|
551
|
-
*/
|
|
552
396
|
export function getMycliManualInstructions(
|
|
553
397
|
platform: Platform = getCurrentPlatform(),
|
|
554
398
|
): string[] {
|
|
555
399
|
return getManualInstallInstructions(mycliDependency, platform)
|
|
556
400
|
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Get the mycli dependency definition
|
|
560
|
-
*/
|
|
561
|
-
export function getMycliDependency(): Dependency {
|
|
562
|
-
return mycliDependency
|
|
563
|
-
}
|