-
Notifications
You must be signed in to change notification settings - Fork 84
isWeb() in fs.js incorrectly detects Bun as web environment, breaking keychain access #3535
Description
Summary
When running @salesforce/core under the Bun runtime, the isWeb() check in lib/fs/fs.js returns true, causing the library to use memfs (in-memory filesystem) instead of node:fs. This breaks all filesystem-dependent functionality, including keychain access for org authentication.
Root Cause
In lib/fs/fs.js, line 37:
const isWeb = () => process.env.FORCE_MEMFS === 'true' || 'window' in globalThis || 'self' in globalThis;Bun exposes self in globalThis as an alias for globalThis itself (for Web API compatibility), similar to how browsers expose self. This causes 'self' in globalThis to return true, making isWeb() incorrectly identify Bun as a web/browser environment.
Impact
All fs operations go through memfs instead of the real filesystem. The first visible symptom is typically:
MissingCredentialProgramError: Unable to find required security software: /usr/bin/security
This happens because memfs.statSync('/usr/bin/security') throws (the file doesn't exist in the in-memory filesystem), even though /usr/bin/security exists on disk.
Reproduction
- Install a project that depends on
@salesforce/core@^8.28.0 - Run it with Bun (e.g.,
bun run my-cli-command) - Any operation that triggers keychain/crypto access will fail with
MissingCredentialProgramError
Verification:
// Bun exposes self
bun -e "console.log('self' in globalThis)" // true
// Node.js does not
node -e "console.log('self' in globalThis)" // falseSuggested Fix
The isWeb() check should use a more reliable detection method. Options:
-
Check for
window(DOM) specifically, sinceselfexists in both Web Workers and Bun:const isWeb = () => process.env.FORCE_MEMFS === 'true' || (typeof window !== 'undefined' && typeof document !== 'undefined');
-
Exclude known server runtimes:
const isWeb = () => process.env.FORCE_MEMFS === 'true' || (('window' in globalThis || 'self' in globalThis) && !('process' in globalThis && process.versions?.node || process.versions?.bun));
-
Check for the absence of
node:fsavailability instead of checking globals.
Environment
@salesforce/core: 8.28.0- Runtime: Bun 1.3.9
- OS: macOS (Darwin arm64)
Related Repository
The affected code lives in forcedotcom/sfdx-core (src/fs/fs.ts).