Skip to content

Add Bun.isStandaloneExecutable - #32583

Merged
Jarred-Sumner merged 4 commits into
mainfrom
farm/78b1f9f2/is-standalone-executable
Jun 22, 2026
Merged

Jarred-Sumner merged 4 commits into
mainfrom
farm/78b1f9f2/is-standalone-executable

Conversation

@robobun

@robobun robobun commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds Bun.isStandaloneExecutable, a read-only boolean that is true when the current process is a bun build --compile standalone executable and false otherwise.

Why

The only public way to detect standalone mode today is Bun.embeddedFiles.length > 0, but Bun.embeddedFiles materializes every embedded file as a heap-backed Blob (via dupe_with_content_type). For binaries that embed large native addons this allocates megabytes just to answer a yes/no question.

The other workaround, Bun.main.startsWith('/$bunfs/') (plus the Windows variant), couples user code to an internal path prefix that isn't documented as a stable detection signal.

Implementation

The getter reads global_this.bun_vm().standalone_module_graph.is_some() and returns jsBoolean. Wired as a lazy PropertyCallback on the Bun object alongside isMainThread.

How did you verify your code works?

Two tests in test/bundler/bundler_compile.test.ts:

  • compile/Bun.isStandaloneExecutable: compiles a binary with one embedded asset, asserts Bun.isStandaloneExecutable === true, and verifies via heapStats().objectTypeCounts.Blob that reading the property allocates zero Blob objects (whereas reading Bun.embeddedFiles afterwards does).
  • Bun.isStandaloneExecutable is false when not compiled: spawns bun -e and asserts { value: false, type: 'boolean' }.

Both tests fail on the released bun (undefined) and pass with this change.

A zero-cost boolean indicating whether the current process is a
`bun build --compile` standalone executable.

The existing way to detect this (`Bun.embeddedFiles.length > 0`)
materializes every embedded file as a heap-backed Blob, which is
wasteful for binaries embedding large assets that only need a yes/no
answer. The other workaround (`Bun.main.startsWith('/$bunfs/')`)
couples user code to an internal path format.
@robobun
robobun requested a review from alii as a code owner June 22, 2026 05:32
@mintlify

mintlify Bot commented Jun 22, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
bun 🟢 Ready View Preview Jun 22, 2026, 5:34 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@robobun

robobun commented Jun 22, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 11:51 PM PT - Jun 21st, 2026

❌ @robobun, your commit 13d2c5f has 2 failures in Build #63866 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 32583

That installs a local version of the PR into your bun-32583 executable, so you can run:

bun-32583 --bun

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 61ee5bda-05d7-4de6-8772-bdff9b15d6c6

📥 Commits

Reviewing files that changed from the base of the PR and between bc123c3 and 4baa8c5.

📒 Files selected for processing (1)
  • test/bundler/bundler_compile.test.ts

Walkthrough

Adds Bun.isStandaloneExecutable, a read-only boolean lazy property returning true when the process runs from a bun build --compile binary. The implementation spans a Rust getter checking standalone_module_graph, C++ property table registration, a TypeScript type declaration, two tests, and a documentation section.

Changes

Bun.isStandaloneExecutable property

Layer / File(s) Summary
Rust getter, C++ property wiring, and TS type
src/runtime/api/BunObject.rs, src/jsc/bindings/BunObject+exports.h, src/jsc/bindings/BunObject.cpp, packages/bun-types/bun.d.ts
get_is_standalone_executable returns a JS boolean from standalone_module_graph.is_some(); registered in the export_lazy_prop_callbacks! table, added to FOR_EACH_GETTER, and inserted into bunObjectTable as ReadOnly|DontDelete|PropertyCallback. The TypeScript declaration adds isStandaloneExecutable: boolean to the bun module with JSDoc noting it does not materialize embedded file blobs.
Tests and documentation
test/bundler/bundler_compile.test.ts, docs/bundler/executables.mdx
A compile test builds a standalone executable and asserts isStandaloneExecutable === true, then verifies heapStats() Blob count increases only after reading embeddedFiles. A non-compile test spawns bun -e and asserts the value is false. Documentation adds a "Detecting standalone mode at runtime" subsection with a code example and a note comparing startup cost to Bun.embeddedFiles.length > 0.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding the Bun.isStandaloneExecutable property.
Description check ✅ Passed The description covers both required template sections with complete information: explains what the feature does, why it's needed, how it was implemented, and how it was verified with two specific tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/bundler/bundler_compile.test.ts`:
- Line 475: The regex pattern in the stdout assertion for the run object is too
restrictive by hardcoding before=0. Instead of pinning the baseline Blob count
to exactly zero, modify the regex pattern to accept any non-negative number for
the before value while maintaining the real invariant that after is greater than
before. Change the pattern to allow any digit sequence for the before parameter
instead of just 0, ensuring the test only validates the meaningful condition
(after > before) rather than an arbitrary baseline value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca2a3973-a426-4556-8c9b-71fa278582b2

📥 Commits

Reviewing files that changed from the base of the PR and between 7a5293c and bc123c3.

📒 Files selected for processing (6)
  • docs/bundler/executables.mdx
  • packages/bun-types/bun.d.ts
  • src/jsc/bindings/BunObject+exports.h
  • src/jsc/bindings/BunObject.cpp
  • src/runtime/api/BunObject.rs
  • test/bundler/bundler_compile.test.ts

Comment thread test/bundler/bundler_compile.test.ts Outdated
Measure the Blob count before and after reading isStandaloneExecutable
and assert it is unchanged, rather than asserting it is exactly 0. This
keeps the assertion strong (reading the property allocates no Blobs)
without coupling to unrelated runtime allocation changes.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any issues — the implementation is a straightforward one-line getter following the isMainThread pattern — but since this adds new public API surface and touches packages/bun-types/ (CODEOWNER-protected), it should get a human sign-off.

Extended reasoning...

Overview

This PR adds Bun.isStandaloneExecutable, a read-only boolean on the Bun global that reports whether the process is running as a bun build --compile binary. The change spans six files: a one-line Rust getter (JSValue::js_boolean(global_this.bun_vm().standalone_module_graph.is_some())), C++ property-table wiring in BunObject+exports.h / BunObject.cpp, a TypeScript declaration in packages/bun-types/bun.d.ts, a docs section in executables.mdx, and two tests in bundler_compile.test.ts.

Security risks

None. The getter reads a single boolean from existing VM state (standalone_module_graph.is_some()) — the same field get_embedded_files already branches on directly below it. No user input, no new privilege, no allocation.

Level of scrutiny

Low for correctness — the implementation is mechanical and mirrors isMainThread exactly (same ReadOnly|DontDelete|PropertyCallback attributes, same lazy-prop macro registration). The CodeRabbit nit about hardcoding before=0 in the test was addressed in 4baa8c5. The robobun CI failures are infra-only (Windows arm64 / musl agent provisioning), not test failures.

However, this introduces new public API on the Bun global, and packages/bun-types/ is CODEOWNER-protected. API naming/shape decisions — even tiny ones — are the kind of thing a maintainer should explicitly approve rather than a bot.

Other factors

Test coverage is solid (both the true and false paths, plus a heap-stats assertion that the property doesn't materialize Blobs). No outstanding reviewer comments. Deferring solely because of the new-public-API + CODEOWNER combination, not because of any concern with the code itself.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation looks correct and follows the established lazy-property pattern, but since this adds a new documented public API to the Bun global it's worth a human sign-off on the name/shape.

Extended reasoning...

Overview

This PR adds Bun.isStandaloneExecutable, a read-only boolean that reports whether the current process is a bun build --compile binary. The change spans six files: a one-line Rust getter that returns js_boolean(vm.standalone_module_graph.is_some()), registration in the export_lazy_prop_callbacks! table, the FOR_EACH_GETTER macro and bunObjectTable entry on the C++ side, a TypeScript declaration, a docs section, and two tests (compiled-true with a Blob-allocation invariant, and a bun -e false case).

Security risks

None. The getter exposes a single boolean derived from existing process state and takes no input. No auth, crypto, filesystem, or network paths are touched.

Level of scrutiny

The implementation is mechanical and mirrors the existing isMainThread / embeddedFiles wiring exactly — low risk of regression. However, the change itself is a new, documented, typed public API on the Bun global, which is a forward-compatibility commitment. Naming and shape decisions for public API surface are product decisions that warrant a maintainer's eyes rather than bot approval.

Other factors

  • The one CodeRabbit nit (hardcoded before=0 baseline) was addressed in 4baa8c5 and the thread is resolved.
  • The single CI failure (test-tls-client-destroy-soon.js on macOS aarch64) is unrelated to this change.
  • Tests cover both the true and false branches plus the no-Blob-allocation guarantee that motivates the feature.
  • No bugs were found by the bug-hunting system.

@robobun

robobun commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

The diff is ready for review. The two new tests (compile/Bun.isStandaloneExecutable and Bun.isStandaloneExecutable is false when not compiled) pass on every lane that ran them.

CI red in #63864 and #63866 is unrelated to this change:

  • test/js/node/test/parallel/test-tls-client-destroy-soon.js on macOS 14 aarch64: TLS byte-count race (2097152 vs 2048000), failed identically on both runs.
  • test/integration/next-pages/test/dev-server.test.ts on macOS 26 aarch64: puppeteer failed to download chrome-headless-shell (external download).
  • bun-install.test.ts / terminal-platform-gaps.test.ts on Windows: flagged as flaky by the runner and auto-retried.

None of these touch BunObject, the standalone module graph, or bundler_compile.test.ts.

@Jarred-Sumner
Jarred-Sumner merged commit 7dd427e into main Jun 22, 2026
79 of 82 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/78b1f9f2/is-standalone-executable branch June 22, 2026 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants