mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
Compare commits
7 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96ee763c93 | ||
|
|
bfe40e8760 | ||
|
|
bcaae48a95 | ||
|
|
6b3403a2b4 | ||
|
|
70fe76209b | ||
|
|
ab3df344b8 | ||
|
|
4680e89a91 |
2
.github/workflows/update-cares.yml
vendored
2
.github/workflows/update-cares.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update c-ares to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update c-ares to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-cares-${{ github.run_number }}
|
||||
branch: deps/update-cares
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-hdrhistogram.yml
vendored
2
.github/workflows/update-hdrhistogram.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
commit-message: "deps: update hdrhistogram to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update hdrhistogram to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-hdrhistogram-${{ github.run_number }}
|
||||
branch: deps/update-hdrhistogram
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-highway.yml
vendored
2
.github/workflows/update-highway.yml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
commit-message: "deps: update highway to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update highway to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-highway-${{ github.run_number }}
|
||||
branch: deps/update-highway
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-libarchive.yml
vendored
2
.github/workflows/update-libarchive.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update libarchive to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update libarchive to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-libarchive-${{ github.run_number }}
|
||||
branch: deps/update-libarchive
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-libdeflate.yml
vendored
2
.github/workflows/update-libdeflate.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-libdeflate-${{ github.run_number }}
|
||||
branch: deps/update-libdeflate
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-lolhtml.yml
vendored
2
.github/workflows/update-lolhtml.yml
vendored
@@ -100,7 +100,7 @@ jobs:
|
||||
commit-message: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-lolhtml-${{ github.run_number }}
|
||||
branch: deps/update-lolhtml
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-lshpack.yml
vendored
2
.github/workflows/update-lshpack.yml
vendored
@@ -105,7 +105,7 @@ jobs:
|
||||
commit-message: "deps: update lshpack to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update lshpack to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-lshpack-${{ github.run_number }}
|
||||
branch: deps/update-lshpack
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-root-certs.yml
vendored
2
.github/workflows/update-root-certs.yml
vendored
@@ -74,7 +74,7 @@ jobs:
|
||||
```
|
||||
${{ env.changed_files }}
|
||||
```
|
||||
branch: certs/update-root-certs-${{ github.run_number }}
|
||||
branch: certs/update-root-certs
|
||||
base: main
|
||||
delete-branch: true
|
||||
labels:
|
||||
|
||||
2
.github/workflows/update-sqlite3.yml
vendored
2
.github/workflows/update-sqlite3.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
commit-message: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}"
|
||||
title: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-sqlite-${{ steps.check-version.outputs.latest }}
|
||||
branch: deps/update-sqlite
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-vendor.yml
vendored
2
.github/workflows/update-vendor.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
commit-message: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-${{ matrix.package }}-${{ github.run_number }}
|
||||
branch: deps/update-${{ matrix.package }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-zstd.yml
vendored
2
.github/workflows/update-zstd.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update zstd to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update zstd to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-zstd-${{ github.run_number }}
|
||||
branch: deps/update-zstd
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
d64457d9ff0143deef025d5df7e8586092b9afb7
|
||||
e9e16dca48dd4a8ffbc77642bc4be60407585f11
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
|
||||
@@ -6,7 +6,8 @@ endif()
|
||||
|
||||
optionx(BUILDKITE_ORGANIZATION_SLUG STRING "The organization slug to use on Buildkite" DEFAULT "bun")
|
||||
optionx(BUILDKITE_PIPELINE_SLUG STRING "The pipeline slug to use on Buildkite" DEFAULT "bun")
|
||||
optionx(BUILDKITE_BUILD_ID STRING "The build ID to use on Buildkite")
|
||||
optionx(BUILDKITE_BUILD_ID STRING "The build ID (UUID) to use on Buildkite")
|
||||
optionx(BUILDKITE_BUILD_NUMBER STRING "The build number to use on Buildkite")
|
||||
optionx(BUILDKITE_GROUP_ID STRING "The group ID to use on Buildkite")
|
||||
|
||||
if(ENABLE_BASELINE)
|
||||
@@ -32,7 +33,13 @@ if(NOT BUILDKITE_BUILD_ID)
|
||||
return()
|
||||
endif()
|
||||
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID})
|
||||
# Use BUILDKITE_BUILD_NUMBER for the URL if available, as the UUID format causes a 302 redirect
|
||||
# that CMake's file(DOWNLOAD) doesn't follow, resulting in empty response.
|
||||
if(BUILDKITE_BUILD_NUMBER)
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_NUMBER})
|
||||
else()
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID})
|
||||
endif()
|
||||
setx(BUILDKITE_BUILD_PATH ${BUILDKITE_BUILDS_PATH}/builds/${BUILDKITE_BUILD_ID})
|
||||
|
||||
file(
|
||||
|
||||
@@ -216,6 +216,23 @@ pub const RunCommand = struct {
|
||||
silent: bool,
|
||||
use_system_shell: bool,
|
||||
) !void {
|
||||
// Add node_modules/.bin directories to PATH (walking up from cwd)
|
||||
const original_path = env.get("PATH") orelse "";
|
||||
var PATH: bun.EnvPath(.{}) = try .initCapacity(allocator, original_path.len + 1 + "node_modules/.bin".len + cwd.len + 1);
|
||||
defer PATH.deinit();
|
||||
|
||||
var parent: ?string = cwd;
|
||||
while (parent) |dir| {
|
||||
var builder = PATH.pathComponentBuilder();
|
||||
builder.append(dir);
|
||||
builder.append("node_modules/.bin");
|
||||
try builder.apply();
|
||||
parent = std.fs.path.dirname(dir);
|
||||
}
|
||||
|
||||
try PATH.append(original_path);
|
||||
try env.map.put("PATH", PATH.slice());
|
||||
|
||||
const shell_bin = findShell(env.get("PATH") orelse "", cwd) orelse return error.MissingShell;
|
||||
env.map.put("npm_lifecycle_event", name) catch unreachable;
|
||||
env.map.put("npm_lifecycle_script", original_script) catch unreachable;
|
||||
|
||||
@@ -1534,7 +1534,7 @@ var _Interface = class Interface extends InterfaceConstructor {
|
||||
prefix +
|
||||
StringPrototypeSlice.$call(this.line, this.cursor, this.line.length);
|
||||
this.cursor = this.cursor - completeOn.length + prefix.length;
|
||||
this._refreshLine();
|
||||
this[kRefreshLine]();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -964,6 +964,7 @@ fn NewPrinter(
|
||||
}
|
||||
|
||||
if (import.default_name) |default| {
|
||||
p.printSemicolonIfNeeded();
|
||||
p.print("var ");
|
||||
p.printSymbol(default.ref.?);
|
||||
if (comptime Statement == void) {
|
||||
@@ -984,6 +985,7 @@ fn NewPrinter(
|
||||
}
|
||||
|
||||
if (import.items.len > 0) {
|
||||
p.printSemicolonIfNeeded();
|
||||
p.printWhitespacer(ws("var {"));
|
||||
|
||||
if (!import.is_single_line) {
|
||||
|
||||
@@ -1109,15 +1109,15 @@ describe("bundler", () => {
|
||||
"/entry.js": /* js */ `
|
||||
// Test all equality operators with typeof undefined
|
||||
console.log(typeof x !== 'undefined');
|
||||
console.log(typeof x != 'undefined');
|
||||
console.log(typeof x != 'undefined');
|
||||
console.log('undefined' !== typeof x);
|
||||
console.log('undefined' != typeof x);
|
||||
|
||||
|
||||
console.log(typeof x === 'undefined');
|
||||
console.log(typeof x == 'undefined');
|
||||
console.log('undefined' === typeof x);
|
||||
console.log('undefined' == typeof x);
|
||||
|
||||
|
||||
// These should not be optimized
|
||||
console.log(typeof x === 'string');
|
||||
console.log(x === 'undefined');
|
||||
@@ -1135,4 +1135,61 @@ describe("bundler", () => {
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/26371
|
||||
// Minified bundler output missing semicolon between statements when
|
||||
// using both default and named imports from "bun" module
|
||||
itBundled("minify/BunImportSemicolonInsertion", {
|
||||
files: {
|
||||
"/entry.js": /* js */ `
|
||||
import bun, { embeddedFiles } from "bun"
|
||||
console.log(typeof embeddedFiles)
|
||||
console.log(typeof bun.argv)
|
||||
`,
|
||||
},
|
||||
minifySyntax: true,
|
||||
minifyWhitespace: true,
|
||||
minifyIdentifiers: true,
|
||||
target: "bun",
|
||||
run: {
|
||||
stdout: "object\nobject",
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("minify/BunImportNamespaceAndNamed", {
|
||||
files: {
|
||||
"/entry.js": /* js */ `
|
||||
import * as bun from "bun"
|
||||
import { embeddedFiles } from "bun"
|
||||
console.log(typeof embeddedFiles)
|
||||
console.log(typeof bun.argv)
|
||||
`,
|
||||
},
|
||||
minifySyntax: true,
|
||||
minifyWhitespace: true,
|
||||
minifyIdentifiers: true,
|
||||
target: "bun",
|
||||
run: {
|
||||
stdout: "object\nobject",
|
||||
},
|
||||
});
|
||||
|
||||
itBundled("minify/BunImportDefaultNamespaceAndNamed", {
|
||||
files: {
|
||||
"/entry.js": /* js */ `
|
||||
import bun, * as bunNs from "bun"
|
||||
import { embeddedFiles } from "bun"
|
||||
console.log(typeof embeddedFiles)
|
||||
console.log(typeof bun.argv)
|
||||
console.log(typeof bunNs.argv)
|
||||
`,
|
||||
},
|
||||
minifySyntax: true,
|
||||
minifyWhitespace: true,
|
||||
minifyIdentifiers: true,
|
||||
target: "bun",
|
||||
run: {
|
||||
stdout: "object\nobject\nobject",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
157
test/regression/issue/17493.test.ts
Normal file
157
test/regression/issue/17493.test.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
|
||||
test("prepack script has node_modules/.bin in PATH", async () => {
|
||||
// Create a test package that verifies rimraf is accessible from prepack script
|
||||
using dir = tempDir("pack-bin-path", {
|
||||
"package.json": JSON.stringify(
|
||||
{
|
||||
name: "test-bin-path",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
// Use which to find rimraf, or check if it's in PATH
|
||||
prepack: "node check-path.js",
|
||||
},
|
||||
devDependencies: {
|
||||
rimraf: "^3.0.2",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"check-path.js": `
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Check if node_modules/.bin is in PATH
|
||||
const pathDirs = process.env.PATH.split(path.delimiter);
|
||||
const binDir = path.join(process.cwd(), 'node_modules', '.bin');
|
||||
const hasBinInPath = pathDirs.some(dir => {
|
||||
// Normalize paths for comparison
|
||||
const normalizedDir = path.normalize(dir);
|
||||
const normalizedBinDir = path.normalize(binDir);
|
||||
return normalizedDir === normalizedBinDir || normalizedDir.endsWith(path.join('node_modules', '.bin'));
|
||||
});
|
||||
|
||||
if (!hasBinInPath) {
|
||||
console.error('ERROR: node_modules/.bin is NOT in PATH');
|
||||
console.error('PATH directories:', pathDirs);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Also verify rimraf executable exists in node_modules/.bin
|
||||
const rimrafPath = path.join(binDir, process.platform === 'win32' ? 'rimraf.cmd' : 'rimraf');
|
||||
if (!fs.existsSync(rimrafPath)) {
|
||||
// Try the bunx shim path
|
||||
const bunxShimPath = path.join(binDir, 'rimraf');
|
||||
if (!fs.existsSync(bunxShimPath)) {
|
||||
console.error('ERROR: rimraf not found in node_modules/.bin');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('SUCCESS: node_modules/.bin is in PATH');
|
||||
`,
|
||||
});
|
||||
|
||||
// First install dependencies
|
||||
await using installProc = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [, , installExitCode] = await Promise.all([
|
||||
installProc.stdout.text(),
|
||||
installProc.stderr.text(),
|
||||
installProc.exited,
|
||||
]);
|
||||
expect(installExitCode).toBe(0);
|
||||
|
||||
// Now run bun pm pack and verify prepack script can find binaries
|
||||
await using packProc = Bun.spawn({
|
||||
cmd: [bunExe(), "pm", "pack"],
|
||||
cwd: String(dir),
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
packProc.stdout.text(),
|
||||
packProc.stderr.text(),
|
||||
packProc.exited,
|
||||
]);
|
||||
|
||||
// The check-path.js script should succeed, meaning node_modules/.bin was in PATH
|
||||
expect(stdout + stderr).toContain("SUCCESS: node_modules/.bin is in PATH");
|
||||
expect(stdout + stderr).not.toContain("ERROR:");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("bun run script has node_modules/.bin in PATH (control test)", async () => {
|
||||
// This verifies that `bun run` works correctly (as a control)
|
||||
using dir = tempDir("run-bin-path", {
|
||||
"package.json": JSON.stringify(
|
||||
{
|
||||
name: "test-run-bin-path",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
"check-path": "node check-path.js",
|
||||
},
|
||||
devDependencies: {
|
||||
rimraf: "^3.0.2",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"check-path.js": `
|
||||
const path = require('path');
|
||||
|
||||
// Check if node_modules/.bin is in PATH
|
||||
const pathDirs = process.env.PATH.split(path.delimiter);
|
||||
const hasBinInPath = pathDirs.some(dir => dir.endsWith(path.join('node_modules', '.bin')));
|
||||
|
||||
if (!hasBinInPath) {
|
||||
console.error('ERROR: node_modules/.bin is NOT in PATH');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('SUCCESS: node_modules/.bin is in PATH');
|
||||
`,
|
||||
});
|
||||
|
||||
// First install dependencies
|
||||
await using installProc = Bun.spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: String(dir),
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [, , installExitCode] = await Promise.all([
|
||||
installProc.stdout.text(),
|
||||
installProc.stderr.text(),
|
||||
installProc.exited,
|
||||
]);
|
||||
expect(installExitCode).toBe(0);
|
||||
|
||||
// Run the check-path script via bun run
|
||||
await using runProc = Bun.spawn({
|
||||
cmd: [bunExe(), "run", "check-path"],
|
||||
cwd: String(dir),
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([runProc.stdout.text(), runProc.stderr.text(), runProc.exited]);
|
||||
|
||||
expect(stdout + stderr).toContain("SUCCESS: node_modules/.bin is in PATH");
|
||||
expect(stdout + stderr).not.toContain("ERROR:");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
@@ -1,86 +0,0 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/26394
|
||||
// Race condition in Bun.serve() where requests can arrive before routes are fully registered,
|
||||
// causing the default "Welcome to Bun!" response instead of the configured handler's response.
|
||||
|
||||
test("concurrent Bun.serve instances should not return Welcome to Bun", async () => {
|
||||
const serverCount = 60;
|
||||
const servers: ReturnType<typeof Bun.serve>[] = [];
|
||||
|
||||
try {
|
||||
// Create many servers concurrently
|
||||
for (let i = 0; i < serverCount; i++) {
|
||||
servers.push(
|
||||
Bun.serve({
|
||||
port: 0,
|
||||
fetch: () => new Response("OK"),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Make concurrent requests to all servers
|
||||
const responses = await Promise.all(
|
||||
servers.map(async server => {
|
||||
const res = await fetch(`http://127.0.0.1:${server.port}/`);
|
||||
return res.text();
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify no "Welcome to Bun!" responses - check for both debug mode message and production mode
|
||||
for (let i = 0; i < responses.length; i++) {
|
||||
expect(responses[i]).not.toContain("Welcome to Bun");
|
||||
expect(responses[i]).not.toBe(""); // Production mode returns empty for renderMissing
|
||||
expect(responses[i]).toBe("OK");
|
||||
}
|
||||
} finally {
|
||||
// Clean up - guaranteed to run even if assertions fail
|
||||
for (const server of servers) {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("Bun.serve should be ready to handle requests immediately after returning", async () => {
|
||||
// Test a single server with immediate fetch - this tests if the server is ready synchronously
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: () => new Response("handler response"),
|
||||
});
|
||||
|
||||
// Immediately fetch - if there's a race condition, this might return "Welcome to Bun!"
|
||||
const response = await fetch(`http://127.0.0.1:${server.port}/`);
|
||||
const text = await response.text();
|
||||
|
||||
expect(text).toBe("handler response");
|
||||
});
|
||||
|
||||
test("multiple sequential Bun.serve instances with immediate requests", async () => {
|
||||
// Create servers sequentially and immediately request from each
|
||||
const results: string[] = [];
|
||||
const servers: ReturnType<typeof Bun.serve>[] = [];
|
||||
|
||||
try {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: () => new Response(`server-${i}`),
|
||||
});
|
||||
servers.push(server);
|
||||
|
||||
// Immediately fetch from the server
|
||||
const response = await fetch(`http://127.0.0.1:${server.port}/`);
|
||||
results.push(await response.text());
|
||||
}
|
||||
|
||||
// Verify all responses match expected
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
expect(results[i]).toBe(`server-${i}`);
|
||||
}
|
||||
} finally {
|
||||
// Clean up - guaranteed to run even if assertions fail
|
||||
for (const server of servers) {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
40
test/regression/issue/26411.test.ts
Normal file
40
test/regression/issue/26411.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/26411
|
||||
// Tab completion with node:readline/promises threw
|
||||
// "TypeError: this._refreshLine is not a function"
|
||||
test("tab completion works with node:readline/promises", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [
|
||||
bunExe(),
|
||||
"-e",
|
||||
`
|
||||
import readline from "node:readline/promises";
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: true,
|
||||
completer: (line) => [["FOO", "FOOBAR"], line]
|
||||
});
|
||||
rl.line = "foo";
|
||||
rl.cursor = 3;
|
||||
setTimeout(() => {
|
||||
rl.close();
|
||||
console.log("OK");
|
||||
process.exit(0);
|
||||
}, 100);
|
||||
rl.write("", { name: "tab" });
|
||||
`,
|
||||
],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
||||
|
||||
expect(stderr).not.toContain("this._refreshLine is not a function");
|
||||
expect(stdout).toContain("OK");
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
Reference in New Issue
Block a user