spindb 0.37.1 → 0.37.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.
@@ -22,7 +22,7 @@ import { startWithRetry } from '../../core/start-with-retry'
22
22
  import { TransactionManager } from '../../core/transaction-manager'
23
23
  import { isValidDatabaseName, exitWithError } from '../../core/error-handler'
24
24
  import { resolve } from 'path'
25
- import { Engine, Platform } from '../../types'
25
+ import { Engine, Platform, ALL_ENGINES } from '../../types'
26
26
  import {
27
27
  FERRETDB_VERSION_MAP,
28
28
  isV1 as isFerretDBv1,
@@ -436,7 +436,7 @@ export const createCommand = new Command('create')
436
436
  .argument('[name]', 'Container name')
437
437
  .option(
438
438
  '-e, --engine <engine>',
439
- 'Database engine (postgresql, mysql, mariadb, sqlite, duckdb, mongodb, ferretdb, redis, valkey, clickhouse, qdrant, meilisearch, couchdb, cockroachdb, surrealdb, questdb, typedb, influxdb)',
439
+ `Database engine (${ALL_ENGINES.join(', ')})`,
440
440
  )
441
441
  .option('--db-version <version>', 'Database version (e.g., 17, 8.0)')
442
442
  .option('-d, --database <database>', 'Database name')
@@ -35,7 +35,7 @@ import {
35
35
  getInstalledPostgresEngines,
36
36
  getEngineMetadata,
37
37
  } from '../helpers'
38
- import { Engine, Platform } from '../../types'
38
+ import { Engine, Platform, ALL_ENGINES } from '../../types'
39
39
  import {
40
40
  loadEnginesJson,
41
41
  filterEnginesByPlatform,
@@ -1833,7 +1833,7 @@ enginesCommand
1833
1833
 
1834
1834
  console.error(
1835
1835
  uiError(
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`,
1836
+ `Unknown engine "${engineName}". Supported: ${ALL_ENGINES.join(', ')}`,
1837
1837
  ),
1838
1838
  )
1839
1839
  process.exit(1)
@@ -27,7 +27,9 @@ export async function handleCheckUpdate(): Promise<void> {
27
27
  spinner.fail('Could not reach npm registry')
28
28
  console.log()
29
29
  console.log(uiInfo('Check your internet connection and try again.'))
30
- console.log(chalk.gray(' Manual update: npm install -g spindb@latest'))
30
+ const pm = await updateManager.detectPackageManager()
31
+ const installCmd = updateManager.getInstallCommand(pm)
32
+ console.log(chalk.gray(` Manual update: ${installCmd}`))
31
33
  console.log()
32
34
  await pressEnterToContinue()
33
35
  return
@@ -82,7 +84,9 @@ export async function handleCheckUpdate(): Promise<void> {
82
84
  console.log()
83
85
  console.log(uiError(updateResult.error || 'Unknown error'))
84
86
  console.log()
85
- console.log(uiInfo('Manual update: npm install -g spindb@latest'))
87
+ const failPm = await updateManager.detectPackageManager()
88
+ const failCmd = updateManager.getInstallCommand(failPm)
89
+ console.log(uiInfo(`Manual update: ${failCmd}`))
86
90
  }
87
91
  await pressEnterToContinue()
88
92
  } else if (action === 'disable') {
@@ -2,6 +2,7 @@ import { exec } from 'child_process'
2
2
  import { promisify } from 'util'
3
3
  import { createRequire } from 'module'
4
4
  import { configManager } from './config-manager'
5
+ import { logDebug } from './error-handler'
5
6
 
6
7
  const execAsync = promisify(exec)
7
8
  const require = createRequire(import.meta.url)
@@ -11,6 +12,17 @@ const CHECK_THROTTLE_MS = 24 * 60 * 60 * 1000 // 24 hours
11
12
 
12
13
  type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
13
14
 
15
+ const KNOWN_PACKAGE_MANAGERS: PackageManager[] = ['pnpm', 'yarn', 'bun', 'npm']
16
+
17
+ export function parseUserAgent(
18
+ userAgent: string | undefined,
19
+ ): PackageManager | null {
20
+ if (!userAgent) return null
21
+ const firstToken = userAgent.split('/')[0]?.toLowerCase().trim()
22
+ if (!firstToken) return null
23
+ return KNOWN_PACKAGE_MANAGERS.find((pm) => pm === firstToken) ?? null
24
+ }
25
+
14
26
  export type UpdateCheckResult = {
15
27
  currentVersion: string
16
28
  latestVersion: string
@@ -75,49 +87,59 @@ export class UpdateManager {
75
87
  }
76
88
  }
77
89
 
78
- // Checks pnpm, yarn, bun first since npm is the fallback
90
+ // Checks all PMs in parallel, falls back to npm_config_user_agent, then npm
79
91
  async detectPackageManager(): Promise<PackageManager> {
80
- try {
81
- const { stdout } = await execAsync('pnpm list -g spindb --json', {
82
- timeout: 5000,
83
- cwd: '/',
84
- })
85
- const data = JSON.parse(stdout) as Array<{
86
- dependencies?: { spindb?: unknown }
87
- }>
88
- if (data[0]?.dependencies?.spindb) {
89
- return 'pnpm'
90
- }
91
- } catch {
92
- // pnpm not installed or spindb not found
92
+ const checks = await Promise.all([
93
+ this.checkGlobalInstall(
94
+ 'pnpm',
95
+ 'pnpm list -g spindb --json',
96
+ (stdout) => {
97
+ const data = JSON.parse(stdout) as Array<{
98
+ dependencies?: { spindb?: unknown }
99
+ }>
100
+ return !!data[0]?.dependencies?.spindb
101
+ },
102
+ ),
103
+ this.checkGlobalInstall('yarn', 'yarn global list --json', (stdout) => {
104
+ return stdout.includes('"spindb@')
105
+ }),
106
+ this.checkGlobalInstall('bun', 'bun pm ls -g', (stdout) => {
107
+ return stdout.includes('spindb@')
108
+ }),
109
+ this.checkGlobalInstall('npm', 'npm list -g spindb --json', (stdout) => {
110
+ const data = JSON.parse(stdout) as {
111
+ dependencies?: { spindb?: unknown }
112
+ }
113
+ return !!data.dependencies?.spindb
114
+ }),
115
+ ])
116
+
117
+ const globalPm = checks.find((result) => result !== null)
118
+ if (globalPm) {
119
+ logDebug(`Detected global install via ${globalPm}`)
120
+ return globalPm
93
121
  }
94
122
 
95
- try {
96
- const { stdout } = await execAsync('yarn global list --json', {
97
- timeout: 5000,
98
- cwd: '/',
99
- })
100
- // yarn outputs newline-delimited JSON, look for spindb in any line
101
- if (stdout.includes('"spindb@')) {
102
- return 'yarn'
103
- }
104
- } catch {
105
- // yarn not installed or spindb not found
123
+ const agentPm = parseUserAgent(process.env.npm_config_user_agent)
124
+ if (agentPm) {
125
+ logDebug(`Detected package manager from user agent: ${agentPm}`)
126
+ return agentPm
106
127
  }
107
128
 
129
+ return 'npm'
130
+ }
131
+
132
+ private async checkGlobalInstall(
133
+ pm: PackageManager,
134
+ command: string,
135
+ checkOutput: (stdout: string) => boolean,
136
+ ): Promise<PackageManager | null> {
108
137
  try {
109
- const { stdout } = await execAsync('bun pm ls -g', {
110
- timeout: 5000,
111
- cwd: '/',
112
- })
113
- if (stdout.includes('spindb@')) {
114
- return 'bun'
115
- }
138
+ const { stdout } = await execAsync(command, { timeout: 5000, cwd: '/' })
139
+ return checkOutput(stdout) ? pm : null
116
140
  } catch {
117
- // bun not installed or spindb not found
141
+ return null
118
142
  }
119
-
120
- return 'npm'
121
143
  }
122
144
 
123
145
  getInstallCommand(pm: PackageManager): string {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spindb",
3
- "version": "0.37.1",
3
+ "version": "0.37.2",
4
4
  "author": "Bob Bass <bob@bbass.co>",
5
5
  "license": "PolyForm-Noncommercial-1.0.0",
6
6
  "description": "Zero-config Docker-free local database containers. Create, backup, and clone a variety of popular databases.",