spindb 0.4.1 → 0.5.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.
Files changed (44) hide show
  1. package/README.md +207 -101
  2. package/cli/commands/clone.ts +3 -1
  3. package/cli/commands/connect.ts +54 -24
  4. package/cli/commands/create.ts +309 -189
  5. package/cli/commands/delete.ts +3 -1
  6. package/cli/commands/deps.ts +19 -4
  7. package/cli/commands/edit.ts +245 -0
  8. package/cli/commands/engines.ts +434 -0
  9. package/cli/commands/info.ts +279 -0
  10. package/cli/commands/list.ts +14 -3
  11. package/cli/commands/menu.ts +510 -198
  12. package/cli/commands/restore.ts +66 -43
  13. package/cli/commands/start.ts +50 -19
  14. package/cli/commands/stop.ts +3 -1
  15. package/cli/commands/url.ts +79 -0
  16. package/cli/index.ts +9 -3
  17. package/cli/ui/prompts.ts +99 -34
  18. package/config/defaults.ts +40 -15
  19. package/config/engine-defaults.ts +107 -0
  20. package/config/os-dependencies.ts +119 -124
  21. package/config/paths.ts +82 -56
  22. package/core/binary-manager.ts +44 -6
  23. package/core/config-manager.ts +17 -5
  24. package/core/container-manager.ts +124 -60
  25. package/core/dependency-manager.ts +9 -15
  26. package/core/error-handler.ts +336 -0
  27. package/core/platform-service.ts +634 -0
  28. package/core/port-manager.ts +51 -32
  29. package/core/process-manager.ts +26 -8
  30. package/core/start-with-retry.ts +167 -0
  31. package/core/transaction-manager.ts +170 -0
  32. package/engines/index.ts +7 -2
  33. package/engines/mysql/binary-detection.ts +325 -0
  34. package/engines/mysql/index.ts +808 -0
  35. package/engines/mysql/restore.ts +257 -0
  36. package/engines/mysql/version-validator.ts +373 -0
  37. package/{core/postgres-binary-manager.ts → engines/postgresql/binary-manager.ts} +63 -23
  38. package/engines/postgresql/binary-urls.ts +5 -3
  39. package/engines/postgresql/index.ts +17 -9
  40. package/engines/postgresql/restore.ts +54 -5
  41. package/engines/postgresql/version-validator.ts +262 -0
  42. package/package.json +9 -3
  43. package/types/index.ts +29 -5
  44. package/cli/commands/postgres-tools.ts +0 -216
@@ -0,0 +1,325 @@
1
+ /**
2
+ * MySQL binary detection for system-installed MySQL
3
+ * Detects MySQL installations from Homebrew, apt, or other package managers
4
+ */
5
+
6
+ import { exec } from 'child_process'
7
+ import { promisify } from 'util'
8
+ import { platformService } from '../../core/platform-service'
9
+
10
+ const execAsync = promisify(exec)
11
+
12
+ /**
13
+ * Find a MySQL binary by name using the platform service
14
+ */
15
+ export async function findMysqlBinary(name: string): Promise<string | null> {
16
+ return platformService.findToolPath(name)
17
+ }
18
+
19
+ /**
20
+ * Get the path to mysqld (MySQL server)
21
+ */
22
+ export async function getMysqldPath(): Promise<string | null> {
23
+ return findMysqlBinary('mysqld')
24
+ }
25
+
26
+ /**
27
+ * Get the path to mysql client
28
+ */
29
+ export async function getMysqlClientPath(): Promise<string | null> {
30
+ return findMysqlBinary('mysql')
31
+ }
32
+
33
+ /**
34
+ * Get the path to mysqladmin
35
+ */
36
+ export async function getMysqladminPath(): Promise<string | null> {
37
+ return findMysqlBinary('mysqladmin')
38
+ }
39
+
40
+ /**
41
+ * Get the path to mysqldump
42
+ */
43
+ export async function getMysqldumpPath(): Promise<string | null> {
44
+ return findMysqlBinary('mysqldump')
45
+ }
46
+
47
+ /**
48
+ * Get the path to mysql_install_db (MariaDB initialization script)
49
+ */
50
+ export async function getMysqlInstallDbPath(): Promise<string | null> {
51
+ return findMysqlBinary('mysql_install_db')
52
+ }
53
+
54
+ /**
55
+ * Get the path to mariadb-install-db (alternative MariaDB initialization)
56
+ */
57
+ export async function getMariadbInstallDbPath(): Promise<string | null> {
58
+ return findMysqlBinary('mariadb-install-db')
59
+ }
60
+
61
+ /**
62
+ * Detect if the installed MySQL is actually MariaDB
63
+ */
64
+ export async function isMariaDB(): Promise<boolean> {
65
+ const mysqld = await getMysqldPath()
66
+ if (!mysqld) return false
67
+
68
+ try {
69
+ const { stdout } = await execAsync(`"${mysqld}" --version`)
70
+ return stdout.toLowerCase().includes('mariadb')
71
+ } catch {
72
+ return false
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Get the MySQL server version from a mysqld binary
78
+ */
79
+ export async function getMysqlVersion(
80
+ mysqldPath: string,
81
+ ): Promise<string | null> {
82
+ try {
83
+ const { stdout } = await execAsync(`"${mysqldPath}" --version`)
84
+ // Example output: mysqld Ver 8.0.35 for macos14.0 on arm64 (Homebrew)
85
+ const match = stdout.match(/Ver\s+(\d+\.\d+\.\d+)/)
86
+ return match ? match[1] : null
87
+ } catch {
88
+ return null
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Get the major version from a full version string
94
+ * e.g., "8.0.35" -> "8.0"
95
+ */
96
+ export function getMajorVersion(fullVersion: string): string {
97
+ const parts = fullVersion.split('.')
98
+ return `${parts[0]}.${parts[1]}`
99
+ }
100
+
101
+ /**
102
+ * Detect all installed MySQL versions
103
+ * Returns a map of major version -> full version string
104
+ */
105
+ export async function detectInstalledVersions(): Promise<
106
+ Record<string, string>
107
+ > {
108
+ const versions: Record<string, string> = {}
109
+ const { platform } = platformService.getPlatformInfo()
110
+
111
+ // Check default mysqld
112
+ const defaultMysqld = await getMysqldPath()
113
+ if (defaultMysqld) {
114
+ const version = await getMysqlVersion(defaultMysqld)
115
+ if (version) {
116
+ const major = getMajorVersion(version)
117
+ versions[major] = version
118
+ }
119
+ }
120
+
121
+ // Check versioned Homebrew installations (macOS only)
122
+ if (platform === 'darwin') {
123
+ const homebrewPaths = [
124
+ '/opt/homebrew/opt/mysql@5.7/bin/mysqld',
125
+ '/opt/homebrew/opt/mysql@8.0/bin/mysqld',
126
+ '/opt/homebrew/opt/mysql@8.4/bin/mysqld',
127
+ '/usr/local/opt/mysql@5.7/bin/mysqld',
128
+ '/usr/local/opt/mysql@8.0/bin/mysqld',
129
+ '/usr/local/opt/mysql@8.4/bin/mysqld',
130
+ ]
131
+
132
+ const { existsSync } = await import('fs')
133
+ for (const path of homebrewPaths) {
134
+ if (existsSync(path)) {
135
+ const version = await getMysqlVersion(path)
136
+ if (version) {
137
+ const major = getMajorVersion(version)
138
+ if (!versions[major]) {
139
+ versions[major] = version
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ return versions
147
+ }
148
+
149
+ /**
150
+ * Get install instructions for MySQL
151
+ */
152
+ export function getInstallInstructions(): string {
153
+ const { platform } = platformService.getPlatformInfo()
154
+
155
+ if (platform === 'darwin') {
156
+ return (
157
+ 'MySQL server not found. Install MySQL:\n' +
158
+ ' brew install mysql\n' +
159
+ ' # or for a specific version:\n' +
160
+ ' brew install mysql@8.0'
161
+ )
162
+ }
163
+
164
+ if (platform === 'linux') {
165
+ return (
166
+ 'MySQL server not found. Install MySQL:\n' +
167
+ ' Ubuntu/Debian: sudo apt install mysql-server\n' +
168
+ ' RHEL/CentOS: sudo yum install mysql-server'
169
+ )
170
+ }
171
+
172
+ return (
173
+ 'MySQL server not found. Please install MySQL from:\n' +
174
+ ' https://dev.mysql.com/downloads/mysql/'
175
+ )
176
+ }
177
+
178
+ export type MysqlPackageManager =
179
+ | 'homebrew'
180
+ | 'apt'
181
+ | 'yum'
182
+ | 'dnf'
183
+ | 'pacman'
184
+ | 'unknown'
185
+
186
+ export type MysqlInstallInfo = {
187
+ packageManager: MysqlPackageManager
188
+ packageName: string
189
+ path: string
190
+ uninstallCommand: string
191
+ isMariaDB: boolean
192
+ }
193
+
194
+ /**
195
+ * Detect which package manager installed MySQL and get uninstall info
196
+ */
197
+ export async function getMysqlInstallInfo(
198
+ mysqldPath: string,
199
+ ): Promise<MysqlInstallInfo> {
200
+ const { platform } = platformService.getPlatformInfo()
201
+ const mariadb = await isMariaDB()
202
+
203
+ // macOS: Check if path is in Homebrew directories
204
+ if (platform === 'darwin') {
205
+ if (
206
+ mysqldPath.includes('/opt/homebrew/') ||
207
+ mysqldPath.includes('/usr/local/Cellar/')
208
+ ) {
209
+ // Extract package name from path
210
+ // e.g., /opt/homebrew/opt/mysql@8.0/bin/mysqld -> mysql@8.0
211
+ // e.g., /opt/homebrew/bin/mysqld -> mysql (linked)
212
+ let packageName = mariadb ? 'mariadb' : 'mysql'
213
+
214
+ const versionMatch = mysqldPath.match(/mysql@(\d+\.\d+)/)
215
+ if (versionMatch) {
216
+ packageName = `mysql@${versionMatch[1]}`
217
+ } else {
218
+ // Try to get from Homebrew directly
219
+ try {
220
+ const { stdout } = await execAsync('brew list --formula')
221
+ const packages = stdout.split('\n')
222
+ const mysqlPackage = packages.find(
223
+ (p) =>
224
+ p.startsWith('mysql') ||
225
+ p.startsWith('mariadb') ||
226
+ p === 'percona-server',
227
+ )
228
+ if (mysqlPackage) {
229
+ packageName = mysqlPackage
230
+ }
231
+ } catch {
232
+ // Ignore errors
233
+ }
234
+ }
235
+
236
+ return {
237
+ packageManager: 'homebrew',
238
+ packageName,
239
+ path: mysqldPath,
240
+ uninstallCommand: `brew uninstall ${packageName}`,
241
+ isMariaDB: mariadb,
242
+ }
243
+ }
244
+ }
245
+
246
+ // Linux: Detect package manager from path or check installed packages
247
+ if (platform === 'linux') {
248
+ // Check for apt (Debian/Ubuntu)
249
+ try {
250
+ const { stdout } = await execAsync('which apt 2>/dev/null')
251
+ if (stdout.trim()) {
252
+ const packageName = mariadb ? 'mariadb-server' : 'mysql-server'
253
+ return {
254
+ packageManager: 'apt',
255
+ packageName,
256
+ path: mysqldPath,
257
+ uninstallCommand: `sudo apt remove ${packageName}`,
258
+ isMariaDB: mariadb,
259
+ }
260
+ }
261
+ } catch {
262
+ // Not apt
263
+ }
264
+
265
+ // Check for dnf (Fedora/RHEL 8+)
266
+ try {
267
+ const { stdout } = await execAsync('which dnf 2>/dev/null')
268
+ if (stdout.trim()) {
269
+ const packageName = mariadb ? 'mariadb-server' : 'mysql-server'
270
+ return {
271
+ packageManager: 'dnf',
272
+ packageName,
273
+ path: mysqldPath,
274
+ uninstallCommand: `sudo dnf remove ${packageName}`,
275
+ isMariaDB: mariadb,
276
+ }
277
+ }
278
+ } catch {
279
+ // Not dnf
280
+ }
281
+
282
+ // Check for yum (CentOS/RHEL 7)
283
+ try {
284
+ const { stdout } = await execAsync('which yum 2>/dev/null')
285
+ if (stdout.trim()) {
286
+ const packageName = mariadb ? 'mariadb-server' : 'mysql-server'
287
+ return {
288
+ packageManager: 'yum',
289
+ packageName,
290
+ path: mysqldPath,
291
+ uninstallCommand: `sudo yum remove ${packageName}`,
292
+ isMariaDB: mariadb,
293
+ }
294
+ }
295
+ } catch {
296
+ // Not yum
297
+ }
298
+
299
+ // Check for pacman (Arch Linux)
300
+ try {
301
+ const { stdout } = await execAsync('which pacman 2>/dev/null')
302
+ if (stdout.trim()) {
303
+ const packageName = mariadb ? 'mariadb' : 'mysql'
304
+ return {
305
+ packageManager: 'pacman',
306
+ packageName,
307
+ path: mysqldPath,
308
+ uninstallCommand: `sudo pacman -Rs ${packageName}`,
309
+ isMariaDB: mariadb,
310
+ }
311
+ }
312
+ } catch {
313
+ // Not pacman
314
+ }
315
+ }
316
+
317
+ // Unknown package manager
318
+ return {
319
+ packageManager: 'unknown',
320
+ packageName: mariadb ? 'mariadb' : 'mysql',
321
+ path: mysqldPath,
322
+ uninstallCommand: 'Use your system package manager to uninstall',
323
+ isMariaDB: mariadb,
324
+ }
325
+ }