Skip to content

fix: detect Bun runtime so fs operations use node:fs#1281

Open
ZLeventer wants to merge 1 commit into
forcedotcom:mainfrom
ZLeventer:fix/bun-runtime-detection-3535
Open

fix: detect Bun runtime so fs operations use node:fs#1281
ZLeventer wants to merge 1 commit into
forcedotcom:mainfrom
ZLeventer:fix/bun-runtime-detection-3535

Conversation

@ZLeventer

Copy link
Copy Markdown

What

Fixes forcedotcom/cli#3535.

Bun exposes self on globalThis as an alias for globalThis itself for Web API compatibility. The previous isWeb() check in src/fs/fs.ts treated any environment with self on globalThis as a web environment, which caused all filesystem calls under Bun to be routed through memfs instead of node:fs. The first visible symptom is keychain access failing with MissingCredentialProgramError: Unable to find required security software: /usr/bin/security because memfs has no /usr/bin/security.

How

isWeb() now short-circuits to false when either process.versions.node or process.versions.bun is set, so server runtimes are never mistaken for the browser. The FORCE_MEMFS escape hatch and the 'window' in globalThis || 'self' in globalThis detection for real web/worker environments are preserved.

const isWeb = (): boolean => {
  if (process.env.FORCE_MEMFS === 'true') return true;
  if (process.versions?.node || process.versions?.bun) return false;
  return 'window' in globalThis || 'self' in globalThis;
};

Tests

Added test/unit/fs/fs.test.ts with three behavioral tests that write/read through the real filesystem vs. memfs:

  1. Real filesystem under Node (sanity check).
  2. Real filesystem under Bun even when self is on globalThis and process.versions.bun is set (regression test for #3535).
  3. memfs when FORCE_MEMFS=true (escape hatch preserved).

Verification on this branch:

  • yarn compile — clean
  • yarn lint — clean (5 pre-existing warnings in unrelated files)
  • Unit tests — 1145 passing, 0 failing
  • NUTs (test/nut/**) including the existing virtualfs.nut.ts — all passing

Reproduction (before the fix)

bun -e "console.log('self' in globalThis)"  # true
node -e "console.log('self' in globalThis)" # false

Under Bun, the previous isWeb() returned true, so any call into @salesforce/core that touched the filesystem (auth, keychain, config) failed.

Bun exposes `self` on globalThis as an alias for globalThis itself for
Web API compatibility. The previous `'self' in globalThis` check
incorrectly identified Bun as a web environment, routing all filesystem
calls through memfs and breaking keychain access (and any other code
that touches the real filesystem).

isWeb() now short-circuits when process.versions.node or
process.versions.bun is present, so server runtimes are never mistaken
for the browser. The FORCE_MEMFS escape hatch and the existing
window/self detection for real web environments are preserved.

Fixes forcedotcom/cli#3535
@ZLeventer ZLeventer requested a review from a team as a code owner April 26, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

isWeb() in fs.js incorrectly detects Bun as web environment, breaking keychain access

1 participant