Open
Conversation
The esbuild bundle step places both the Python worker (worker.js) and
the js-exec worker (js-exec-worker.js) in the same dist/bin/chunks/
directory. However, js-exec.ts references `new URL("./worker.js",
import.meta.url)` which resolves to the Python worker at runtime.
The Python worker expects `workerData` (passed at Worker constructor),
but js-exec sends input via `postMessage`. The Python worker receives
no workerData, does nothing, and hangs until the timeout fires —
producing "Worker exited unexpectedly" on every js-exec call.
Fix: rename the js-exec worker output to js-exec-worker.js (matching
the existing dist copy target) and update the source reference.
Verified empirically:
- Before: `js-exec -c "console.log(42)"` → exit=124, 10s timeout, 100% failure
- After: `js-exec -c "console.log(42)"` → exit=0, stdout="42", ~100ms
Tested on: Bun 1.3.6, Bun 1.3.11, Node.js v25.4.0 (macOS arm64)
Fixes vercel-labs#159
Made-with: Cursor
|
@engelberger is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
1ae2169 to
1894e07
Compare
The static `import { stripTypeScriptTypes } from "node:module"` causes
a link-time crash in Bun because Bun's node:module does not export this
symbol (it is a Node.js 23.2+ experimental API).
The worker thread crashes immediately on import -- before QuickJS even
initializes -- producing "Worker exited unexpectedly" on every call.
Fix: replace the static named import with a dynamic `require()` wrapped
in try/catch that falls back to a no-op when the export is missing.
Uses `require()` instead of `await import()` because Bun Worker threads
load bundled .js files in a script context where top-level await is not
supported (produces "await is only valid in async functions").
TypeScript type stripping becomes unavailable on runtimes without this
API, but plain JavaScript execution works normally.
Fixes vercel-labs#159
Made-with: Cursor
1894e07 to
5a5e053
Compare
There was a problem hiding this comment.
Additional Suggestions:
- The js-exec worker output path was renamed to
js-exec-worker.jsin the build script but three other files still reference the old pathworker.js, causingpnpm check:worker-sync(part ofpnpm validate) to fail.
- biome.json excludes the old worker path
src/commands/js-exec/worker.jsinstead of the actual build outputsrc/commands/js-exec/js-exec-worker.js, so the generated bundle will be linted and cause lint errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix js-exec failing on both Bun and Node.js
Fixes #159
Problem
js-execcommands hang for exactlymaxJsTimeoutMs(default 10s) and exit with code 124 on every invocation, regardless of input complexity. Evenjs-exec -c "console.log(42)"fails.Affected runtimes: Bun (all versions), Node.js LTS (intermittently, due to bug #1)
Root Cause
Two independent bugs compound to break js-exec:
Bug 1: Worker URL resolves to the wrong file after esbuild bundling
js-exec.tsreferences the worker via:After esbuild bundles
js-exec.tsintodist/bin/chunks/js-exec-XXXX.js, the relative./worker.jsresolves todist/bin/chunks/worker.jsat runtime -- which is the Python worker (copied there bybuild:worker). The js-exec worker exists asdist/bin/chunks/js-exec-worker.jsbut is never loaded.The Python worker expects
workerData(passed atnew Worker(path, { workerData })constructor time). The js-exec protocol sends input viapostMessage(). SinceworkerDatais undefined, the Python worker does nothing and hangs until the timeout fires.Diagram of the name collision:
Bug 2: Static import of
stripTypeScriptTypescrashes Bun workersworker.tsline 12:stripTypeScriptTypesis a Node.js 23.2+ experimental API. Bun'snode:moduledoes not export this symbol. Since this is a static ESM named import, it causes a link-time error that crashes the worker thread before any code runs:Fix
Change 1: Rename worker output to avoid name collision (
js-exec.ts+package.json)The
build:workerscript already producesjs-exec-worker.jsin the chunks directories. This change makes the source reference match.The build script is also updated to output esbuild to
js-exec-worker.jsdirectly (instead ofworker.jswith a rename), keeping the source and build output consistent.Change 2: Dynamic require with fallback (
worker.ts)Uses
require()instead ofawait import()because Bun Worker threads load bundled.jsfiles in a script context where top-levelawaitis not supported (produces"await is only valid in async functions").When
stripTypeScriptTypesis unavailable (Bun, older Node.js), the fallback returns the source code unmodified. TypeScript type stripping (.ts/.mtsfiles,--strip-typesflag) becomes unavailable, but plain JavaScript execution works normally -- which is the common case.Verification
All tests run against the built
dist/output (not source), matching what npm consumers receive.Node.js v25.4.0
js-exec -c "console.log(42)""42"require("fs").readFileSync"{\"k\":1}"[1,2,3].reduce((a,b)=>a+b)"6".tsfile auto-detection"42"(stripTypeScriptTypes works)Bun 1.3.11
js-exec -c "console.log(42)""42"require("fs").readFileSync"{\"k\":1}"[1,2,3].reduce((a,b)=>a+b)"6"fs.writeFileSynccross-call persistence"hi"Before this fix: 0% success rate (exit=124 timeout on every call).
After this fix: 100% success rate on both runtimes.
Files Changed
src/commands/js-exec/js-exec.ts./worker.js->./js-exec-worker.jssrc/commands/js-exec/worker.tsrequire()with try/catch fallbackpackage.jsonbuild:worker: esbuild output tojs-exec-worker.js.gitignorejs-exec-worker.jsNotes
stripTypeScriptTypesis loaded normally via the dynamic import pathworker.js) path and behavior are unchanged.ts/.mtsexecution will show the raw TypeScript source (type annotations are not stripped). This could be documented or addressed separately with a Bun-native TS strip implementationrequire()instead ofawait import()because Bun Worker threads load bundled.jsfiles in a script context where top-levelawaitis not supported