mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
## Summary Fixes compatibility issue with Node.js libraries that use `Object.assign(promise, childProcess)` pattern, specifically `tinyspawn` (used by `youtube-dl-exec`). ## Problem In Node.js, child process stdio properties (`stdin`, `stdout`, `stderr`, `stdio`) are enumerable own properties that can be copied by `Object.assign()`. In Bun, they were non-enumerable getters on the prototype, causing `Object.assign()` to fail copying them. This broke libraries like: - `tinyspawn` - uses `Object.assign(promise, childProcess)` to merge properties - `youtube-dl-exec` - depends on tinyspawn internally ## Solution Make stdio properties enumerable own properties during spawn while preserving: - ✅ Lazy initialization (streams created only when accessed) - ✅ Original getter functionality and caching - ✅ Performance (minimal overhead) ## Testing - Added comprehensive regression tests - Verified compatibility with `tinyspawn` and `youtube-dl-exec` - Existing child_process tests still pass ## Related - Fixes: https://github.com/microlinkhq/youtube-dl-exec/issues/246 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
// Regression test for https://github.com/microlinkhq/youtube-dl-exec/issues/246
|
|
// Child process stdio properties should be enumerable for Object.assign() compatibility
|
|
|
|
import { expect, test } from "bun:test";
|
|
import { spawn } from "child_process";
|
|
|
|
test("child process stdio properties should be enumerable for Object.assign()", () => {
|
|
const child = spawn(process.execPath, ["-e", 'console.log("hello")']);
|
|
|
|
// The real issue: stdio properties must be enumerable for Object.assign() to work
|
|
// This is what libraries like tinyspawn depend on
|
|
expect(Object.keys(child)).toContain("stdin");
|
|
expect(Object.keys(child)).toContain("stdout");
|
|
expect(Object.keys(child)).toContain("stderr");
|
|
expect(Object.keys(child)).toContain("stdio");
|
|
|
|
// Property descriptors should show enumerable: true
|
|
for (const key of ["stdin", "stdout", "stderr", "stdio"] as const) {
|
|
expect(Object.getOwnPropertyDescriptor(child, key)?.enumerable).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("Object.assign should copy child process stdio properties", () => {
|
|
const child = spawn(process.execPath, ["-e", 'console.log("hello")']);
|
|
|
|
// This is what tinyspawn does: Object.assign(promise, childProcess)
|
|
const merged = {};
|
|
Object.assign(merged, child);
|
|
|
|
// The merged object should have the stdio properties
|
|
expect(merged.stdout).toBeTruthy();
|
|
expect(merged.stderr).toBeTruthy();
|
|
expect(merged.stdin).toBeTruthy();
|
|
expect(merged.stdio).toBeTruthy();
|
|
|
|
// Should maintain stream functionality
|
|
expect(typeof merged.stdout.pipe).toBe("function");
|
|
expect(typeof merged.stdout.on).toBe("function");
|
|
});
|
|
|
|
test("tinyspawn-like library usage should work", () => {
|
|
// Simulate the exact pattern from tinyspawn library
|
|
let childProcess;
|
|
const promise = new Promise(resolve => {
|
|
childProcess = spawn(process.execPath, ["-e", 'console.log("test")']);
|
|
childProcess.on("exit", () => resolve(childProcess));
|
|
});
|
|
|
|
// This is the critical line that was failing in Bun
|
|
const subprocess = Object.assign(promise, childProcess);
|
|
|
|
// Should have stdio properties immediately after Object.assign
|
|
expect(subprocess.stdout).toBeTruthy();
|
|
expect(subprocess.stderr).toBeTruthy();
|
|
expect(subprocess.stdin).toBeTruthy();
|
|
|
|
// Should still be a Promise
|
|
expect(subprocess instanceof Promise).toBe(true);
|
|
|
|
// Should have stream methods available
|
|
expect(typeof subprocess.stdout.pipe).toBe("function");
|
|
expect(typeof subprocess.stdout.on).toBe("function");
|
|
});
|