mirror of
https://github.com/oven-sh/bun
synced 2026-02-18 14:51:52 +00:00
Compare commits
37 Commits
codex/fix-
...
codex/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad71db75c3 | ||
|
|
bc91844960 | ||
|
|
73357cce64 | ||
|
|
0c0dfd2272 | ||
|
|
2e7d9a6112 | ||
|
|
7d47053d46 | ||
|
|
2aa7c59727 | ||
|
|
7765b61038 | ||
|
|
8a06ddb1fb | ||
|
|
2e76e69939 | ||
|
|
aa404b14c4 | ||
|
|
a4819b41e9 | ||
|
|
f5bfda9699 | ||
|
|
9f5adfefe3 | ||
|
|
316c8d6c48 | ||
|
|
da87890532 | ||
|
|
576f66c149 | ||
|
|
cd0756c95c | ||
|
|
c92f3f7b72 | ||
|
|
f1226c9767 | ||
|
|
b111e6db02 | ||
|
|
ffffb634c6 | ||
|
|
d109183d3e | ||
|
|
14c9165d6f | ||
|
|
c42539b0bf | ||
|
|
022a567af0 | ||
|
|
cfb8956ac5 | ||
|
|
2bb36ca6b4 | ||
|
|
24b3de1bc3 | ||
|
|
b01ffe6da8 | ||
|
|
579f2ecd51 | ||
|
|
627b0010e0 | ||
|
|
3369e25a70 | ||
|
|
06a40f0b29 | ||
|
|
7989352b39 | ||
|
|
e1ab6fe36b | ||
|
|
14f59568cc |
@@ -309,6 +309,19 @@ function getCppAgent(platform, options) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Platform}
|
||||
*/
|
||||
function getZigPlatform() {
|
||||
return {
|
||||
os: "linux",
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
distro: "alpine",
|
||||
release: "3.21",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @param {PipelineOptions} options
|
||||
@@ -322,19 +335,9 @@ function getZigAgent(platform, options) {
|
||||
// queue: "build-zig",
|
||||
// };
|
||||
|
||||
return getEc2Agent(
|
||||
{
|
||||
os: "linux",
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
distro: "alpine",
|
||||
release: "3.21",
|
||||
},
|
||||
options,
|
||||
{
|
||||
instanceType: "r8g.large",
|
||||
},
|
||||
);
|
||||
return getEc2Agent(getZigPlatform(), options, {
|
||||
instanceType: "r8g.large",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1105,6 +1108,11 @@ async function getPipeline(options = {}) {
|
||||
steps.push(
|
||||
...relevantBuildPlatforms.map(target => {
|
||||
const imageKey = getImageKey(target);
|
||||
const zigImageKey = getImageKey(getZigPlatform());
|
||||
const dependsOn = imagePlatforms.has(zigImageKey) ? [`${zigImageKey}-build-image`] : [];
|
||||
if (imagePlatforms.has(imageKey)) {
|
||||
dependsOn.push(`${imageKey}-build-image`);
|
||||
}
|
||||
|
||||
return getStepWithDependsOn(
|
||||
{
|
||||
@@ -1114,7 +1122,7 @@ async function getPipeline(options = {}) {
|
||||
? [getBuildBunStep(target, options)]
|
||||
: [getBuildCppStep(target, options), getBuildZigStep(target, options), getLinkBunStep(target, options)],
|
||||
},
|
||||
imagePlatforms.has(imageKey) ? `${imageKey}-build-image` : undefined,
|
||||
...dependsOn,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
---
|
||||
description: How to build Bun
|
||||
globs:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# How to build Bun
|
||||
|
||||
## CMake
|
||||
Run:
|
||||
|
||||
Bun is built using CMake, which you can find in `CMakeLists.txt` and in the `cmake/` directory.
|
||||
|
||||
* `CMakeLists.txt`
|
||||
* `cmake/`
|
||||
* `Globals.cmake` - macros and functions used by all the other files
|
||||
* `Options.cmake` - build options for configuring the build (e.g. debug/release mode)
|
||||
* `CompilerFlags.cmake` - compiler and linker flags used by all the targets
|
||||
* `tools/` - setup scripts for various build tools (e.g. llvm, zig, webkit, rust, etc.)
|
||||
* `targets/` - targets for bun and its dependencies (e.g. brotli, boringssl, libuv, etc.)
|
||||
|
||||
## How to
|
||||
|
||||
There are `package.json` scripts that make it easy to build Bun without calling CMake directly, for example:
|
||||
|
||||
```sh
|
||||
bun run build # builds a debug build: `build/debug/bun-debug`
|
||||
bun run build:release # builds a release build: `build/release/bun`
|
||||
bun run build:assert # builds a release build with debug assertions: `build/assert/bun`
|
||||
```bash
|
||||
bun bd
|
||||
```
|
||||
|
||||
45
.github/workflows/codex-test-sync.yml
vendored
Normal file
45
.github/workflows/codex-test-sync.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Codex Test Sync
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, opened]
|
||||
|
||||
env:
|
||||
BUN_VERSION: "canary"
|
||||
|
||||
jobs:
|
||||
sync-node-tests:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'codex') ||
|
||||
(github.event.action == 'opened' && contains(github.event.pull_request.labels.*.name, 'codex'))
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
with:
|
||||
bun-version: ${{ env.BUN_VERSION }}
|
||||
|
||||
- name: Get changed files and sync tests
|
||||
run: |
|
||||
# Get the list of changed files from the PR
|
||||
git diff --name-only origin/main...HEAD | while read -r file; do
|
||||
if [[ "$file" =~ ^test/js/node/test/(parallel|sequential)/(.+)\.js$ ]]; then
|
||||
test_name="${BASH_REMATCH[2]}"
|
||||
echo "Syncing test: $test_name"
|
||||
bun node:test:cp "$test_name"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "Sync Node.js tests with upstream"
|
||||
@@ -76,6 +76,7 @@
|
||||
"zig-format:check": "bun run analysis:no-llvm --target zig-format-check",
|
||||
"prettier": "bunx prettier@latest --plugin=prettier-plugin-organize-imports --config .prettierrc --write scripts packages src docs 'test/**/*.{test,spec}.{ts,tsx,js,jsx,mts,mjs,cjs,cts}' '!test/**/*fixture*.*'",
|
||||
"node:test": "node ./scripts/runner.node.mjs --quiet --exec-path=$npm_execpath --node-tests ",
|
||||
"node:test:cp": "bun ./scripts/fetch-node-test.ts ",
|
||||
"clean:zig": "rm -rf build/debug/cache/zig build/debug/CMakeCache.txt 'build/debug/*.o' .zig-cache zig-out || true",
|
||||
"sync-webkit-source": "bun ./scripts/sync-webkit-source.ts"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Version: 7
|
||||
# Version: 8
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on Windows 10 or newer with PowerShell.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Version: 10
|
||||
# Version: 11
|
||||
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on macOS and Linux with a POSIX shell.
|
||||
|
||||
112
scripts/fetch-node-test.ts
Normal file
112
scripts/fetch-node-test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { mkdirSync, writeFileSync } from "fs";
|
||||
import path, { dirname, join } from "path";
|
||||
|
||||
const options: RequestInit = {};
|
||||
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
options.headers = {
|
||||
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchNodeTest(testName: string) {
|
||||
const nodeRepoUrl = "https://raw.githubusercontent.com/nodejs/node/main";
|
||||
const extensions = ["js", "mjs", "ts"];
|
||||
const testDirs = ["test/parallel", "test/sequential"];
|
||||
|
||||
// Try different combinations of test name patterns
|
||||
const testNameVariations = [
|
||||
testName,
|
||||
testName.startsWith("test-") ? testName : `test-${testName}`,
|
||||
testName.replace(/^test-/, ""),
|
||||
];
|
||||
|
||||
for (const testDir of testDirs) {
|
||||
for (const nameVariation of testNameVariations) {
|
||||
// Try with extensions
|
||||
for (const ext of extensions) {
|
||||
const testPath = `${testDir}/${nameVariation}.${ext}`;
|
||||
const url = `${nodeRepoUrl}/${testPath}`;
|
||||
|
||||
try {
|
||||
console.log(`Trying: ${url}`);
|
||||
const response = await fetch(url, options);
|
||||
if (response.ok) {
|
||||
const content = await response.text();
|
||||
const localPath = join("test/js/node", testPath);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
mkdirSync(dirname(localPath), { recursive: true });
|
||||
|
||||
// Write the file
|
||||
writeFileSync(localPath, content);
|
||||
console.log(
|
||||
`✅ Successfully fetched and saved: ${localPath} (${new Intl.NumberFormat("en-US", {
|
||||
notation: "compact",
|
||||
unit: "kilobyte",
|
||||
}).format(Buffer.byteLength(content, "utf-8"))})`,
|
||||
);
|
||||
return localPath;
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue to next variation
|
||||
}
|
||||
}
|
||||
|
||||
// Try without extension
|
||||
const testPath = `${testDir}/${nameVariation}`;
|
||||
const url = `${nodeRepoUrl}/${testPath}`;
|
||||
|
||||
try {
|
||||
console.log(`Trying: ${url}`);
|
||||
const response = await fetch(url, options);
|
||||
if (response.ok) {
|
||||
const content = await response.text();
|
||||
const localPath = join("test/js/node", testPath);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
mkdirSync(dirname(localPath), { recursive: true });
|
||||
|
||||
// Write the file
|
||||
writeFileSync(localPath, content);
|
||||
console.log(
|
||||
`✅ Successfully fetched and saved: ${localPath} (${new Intl.NumberFormat("en-US", {
|
||||
notation: "compact",
|
||||
unit: "kilobyte",
|
||||
}).format(Buffer.byteLength(content, "utf-8"))})`,
|
||||
);
|
||||
return localPath;
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue to next variation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`❌ Could not find test: ${testName}`);
|
||||
}
|
||||
|
||||
// Get test name from command line arguments
|
||||
let testName = process.argv[2];
|
||||
|
||||
if (testName.startsWith(path.join(import.meta.dirname, ".."))) {
|
||||
testName = testName.slice(path.join(import.meta.dirname, "..").length);
|
||||
}
|
||||
|
||||
if (testName.startsWith("test/parallel/")) {
|
||||
testName = testName.replace("test/parallel/", "");
|
||||
} else if (testName.startsWith("test/sequential/")) {
|
||||
testName = testName.replace("test/sequential/", "");
|
||||
}
|
||||
|
||||
if (!testName) {
|
||||
console.error("Usage: bun scripts/fetch-node-test.ts <test-name>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await fetchNodeTest(testName);
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -858,7 +858,8 @@ function getSshKeys() {
|
||||
const sshFiles = readdirSync(sshPath, { withFileTypes: true, encoding: "utf-8" });
|
||||
const publicPaths = sshFiles
|
||||
.filter(entry => entry.isFile() && entry.name.endsWith(".pub"))
|
||||
.map(({ name }) => join(sshPath, name));
|
||||
.map(({ name }) => join(sshPath, name))
|
||||
.filter(path => !readFile(path, { cache: true }).startsWith("ssh-ed25519"));
|
||||
|
||||
sshKeys.push(
|
||||
...publicPaths.map(publicPath => ({
|
||||
|
||||
@@ -290,7 +290,7 @@ export async function spawn(command, options = {}) {
|
||||
if (exitCode !== 0 && isWindows) {
|
||||
const exitReason = getWindowsExitReason(exitCode);
|
||||
if (exitReason) {
|
||||
exitCode = exitReason;
|
||||
signalCode = exitReason;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ export function spawnSync(command, options = {}) {
|
||||
if (exitCode !== 0 && isWindows) {
|
||||
const exitReason = getWindowsExitReason(exitCode);
|
||||
if (exitReason) {
|
||||
exitCode = exitReason;
|
||||
signalCode = exitReason;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,9 +442,37 @@ export function spawnSyncSafe(command, options = {}) {
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getWindowsExitReason(exitCode) {
|
||||
const ntStatusPath = "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.22621.0\\shared\\ntstatus.h";
|
||||
const nthStatus = readFile(ntStatusPath, { cache: true });
|
||||
const windowsKitPath = "C:\\Program Files (x86)\\Windows Kits";
|
||||
if (!existsSync(windowsKitPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const windowsKitPaths = readdirSync(windowsKitPath)
|
||||
.filter(filename => isFinite(parseInt(filename)))
|
||||
.sort((a, b) => parseInt(b) - parseInt(a));
|
||||
|
||||
let ntStatusPath;
|
||||
for (const windowsKitPath of windowsKitPaths) {
|
||||
const includePath = `${windowsKitPath}\\Include`;
|
||||
if (!existsSync(includePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const windowsSdkPaths = readdirSync(includePath).sort();
|
||||
for (const windowsSdkPath of windowsSdkPaths) {
|
||||
const statusPath = `${includePath}\\${windowsSdkPath}\\shared\\ntstatus.h`;
|
||||
if (existsSync(statusPath)) {
|
||||
ntStatusPath = statusPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ntStatusPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nthStatus = readFile(ntStatusPath, { cache: true });
|
||||
const match = nthStatus.match(new RegExp(`(STATUS_\\w+).*0x${exitCode?.toString(16)}`, "i"));
|
||||
if (match) {
|
||||
const [, exitReason] = match;
|
||||
|
||||
@@ -2030,13 +2030,14 @@ enum class BunProcessStdinFdType : int32_t {
|
||||
extern "C" BunProcessStdinFdType Bun__Process__getStdinFdType(void*, int fd);
|
||||
|
||||
extern "C" void Bun__ForceFileSinkToBeSynchronousForProcessObjectStdio(JSC::JSGlobalObject*, JSC::EncodedJSValue);
|
||||
static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd)
|
||||
static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, JSC::JSObject* processObject, int fd)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
|
||||
JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject);
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(processObject);
|
||||
args.append(JSC::jsNumber(fd));
|
||||
args.append(jsBoolean(bun_stdio_tty[fd]));
|
||||
BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), fd);
|
||||
@@ -2045,8 +2046,11 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int
|
||||
JSC::CallData callData = JSC::getCallData(getStdioWriteStream);
|
||||
|
||||
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject->globalThis(), args);
|
||||
scope.assertNoExceptionExceptTermination();
|
||||
CLEAR_AND_RETURN_IF_EXCEPTION(scope, jsUndefined());
|
||||
if (auto* exception = scope.exception()) {
|
||||
Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception);
|
||||
scope.clearException();
|
||||
return jsUndefined();
|
||||
}
|
||||
|
||||
ASSERT_WITH_MESSAGE(JSC::isJSArray(result), "Expected an array from getStdioWriteStream");
|
||||
JSC::JSArray* resultObject = JSC::jsCast<JSC::JSArray*>(result);
|
||||
@@ -2077,12 +2081,12 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int
|
||||
|
||||
static JSValue constructStdout(VM& vm, JSObject* processObject)
|
||||
{
|
||||
return constructStdioWriteStream(processObject->globalObject(), 1);
|
||||
return constructStdioWriteStream(processObject->globalObject(), processObject, 1);
|
||||
}
|
||||
|
||||
static JSValue constructStderr(VM& vm, JSObject* processObject)
|
||||
{
|
||||
return constructStdioWriteStream(processObject->globalObject(), 2);
|
||||
return constructStdioWriteStream(processObject->globalObject(), processObject, 2);
|
||||
}
|
||||
|
||||
#if OS(WINDOWS)
|
||||
@@ -2092,17 +2096,22 @@ static JSValue constructStderr(VM& vm, JSObject* processObject)
|
||||
static JSValue constructStdin(VM& vm, JSObject* processObject)
|
||||
{
|
||||
auto* globalObject = processObject->globalObject();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
JSC::JSFunction* getStdinStream = JSC::JSFunction::create(vm, globalObject, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject);
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
args.append(processObject);
|
||||
args.append(JSC::jsNumber(STDIN_FILENO));
|
||||
args.append(jsBoolean(bun_stdio_tty[STDIN_FILENO]));
|
||||
BunProcessStdinFdType fdType = Bun__Process__getStdinFdType(Bun::vm(vm), STDIN_FILENO);
|
||||
args.append(jsNumber(static_cast<int32_t>(fdType)));
|
||||
JSC::CallData callData = JSC::getCallData(getStdioWriteStream);
|
||||
JSC::CallData callData = JSC::getCallData(getStdinStream);
|
||||
|
||||
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdioWriteStream, callData, globalObject, args);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
auto result = JSC::profiledCall(globalObject, ProfilingReason::API, getStdinStream, callData, globalObject, args);
|
||||
if (auto* exception = scope.exception()) {
|
||||
Zig::GlobalObject::reportUncaughtExceptionAtEventLoop(globalObject, exception);
|
||||
scope.clearException();
|
||||
return jsUndefined();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3130,6 +3139,21 @@ JSC_DEFINE_HOST_FUNCTION(Process_stubEmptyFunction, (JSGlobalObject * globalObje
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(Process_setSourceMapsEnabled, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
Zig::GlobalObject* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSValue arg0 = callFrame->argument(0);
|
||||
if (!arg0.isBoolean()) {
|
||||
return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, "enabled"_s, "boolean"_s, arg0);
|
||||
}
|
||||
|
||||
globalObject->processObject()->m_sourceMapsEnabled = arg0.toBoolean(globalObject);
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(Process_stubFunctionReturningArray, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
return JSValue::encode(JSC::constructEmptyArray(globalObject, nullptr));
|
||||
@@ -3636,7 +3660,7 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu
|
||||
resourceUsage Process_functionResourceUsage Function 0
|
||||
revision constructRevision PropertyCallback
|
||||
send constructProcessSend PropertyCallback
|
||||
setSourceMapsEnabled Process_stubEmptyFunction Function 1
|
||||
setSourceMapsEnabled Process_setSourceMapsEnabled Function 1
|
||||
setUncaughtExceptionCaptureCallback Process_setUncaughtExceptionCaptureCallback Function 1
|
||||
stderr constructStderr PropertyCallback
|
||||
stdin constructStdin PropertyCallback
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
~Process();
|
||||
|
||||
bool m_isExitCodeObservable = false;
|
||||
bool m_sourceMapsEnabled = false;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable;
|
||||
|
||||
|
||||
@@ -30,8 +30,13 @@ const enum BunProcessStdinFdType {
|
||||
socket = 2,
|
||||
}
|
||||
|
||||
export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdinFdType) {
|
||||
$assert(typeof fd === "number", `Expected fd to be a number, got ${typeof fd}`);
|
||||
export function getStdioWriteStream(
|
||||
process: typeof globalThis.process,
|
||||
fd: number,
|
||||
isTTY: boolean,
|
||||
_fdType: BunProcessStdinFdType,
|
||||
) {
|
||||
$assert(fd === 1 || fd === 2, `Expected fd to be 1 or 2, got ${fd}`);
|
||||
|
||||
let stream;
|
||||
if (isTTY) {
|
||||
@@ -74,9 +79,14 @@ export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdin
|
||||
return [stream, underlyingSink];
|
||||
}
|
||||
|
||||
export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType) {
|
||||
export function getStdinStream(
|
||||
process: typeof globalThis.process,
|
||||
fd: number,
|
||||
isTTY: boolean,
|
||||
fdType: BunProcessStdinFdType,
|
||||
) {
|
||||
$assert(fd === 0);
|
||||
const native = Bun.stdin.stream();
|
||||
// @ts-expect-error
|
||||
const source = native.$bunNativePtr;
|
||||
|
||||
var reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
||||
@@ -246,7 +256,12 @@ export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType
|
||||
|
||||
return stream;
|
||||
}
|
||||
export function initializeNextTickQueue(process, nextTickQueue, drainMicrotasksFn, reportUncaughtExceptionFn) {
|
||||
export function initializeNextTickQueue(
|
||||
process: typeof globalThis.process,
|
||||
nextTickQueue,
|
||||
drainMicrotasksFn,
|
||||
reportUncaughtExceptionFn,
|
||||
) {
|
||||
var queue;
|
||||
var process;
|
||||
var nextTickQueue = nextTickQueue;
|
||||
|
||||
@@ -345,6 +345,49 @@ const codes = {}; // exported from errors.js
|
||||
return error;
|
||||
};
|
||||
}
|
||||
{
|
||||
// Add ERR_INVALID_THIS error code
|
||||
const messages = new SafeMap();
|
||||
const sym = "ERR_INVALID_THIS";
|
||||
messages.set(sym, type => {
|
||||
return `Value of "this" must be of type ${type}`;
|
||||
});
|
||||
codes[sym] = function NodeError(...args) {
|
||||
const limit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 0;
|
||||
const error = new TypeError();
|
||||
Error.stackTraceLimit = limit; // Reset the limit and setting the name property.
|
||||
|
||||
const msg = messages.get(sym);
|
||||
assert(typeof msg === "function");
|
||||
assert(
|
||||
msg.length <= args.length, // Default options do not count.
|
||||
`Code: ${sym}; The provided arguments length (${args.length}) does not match the required ones (${msg.length}).`,
|
||||
);
|
||||
const message = msg.$apply(error, args);
|
||||
|
||||
ObjectDefineProperty(error, "message", { value: message, enumerable: false, writable: true, configurable: true });
|
||||
ObjectDefineProperty(error, "toString", {
|
||||
value() {
|
||||
return `${this.name} [${sym}]: ${this.message}`;
|
||||
},
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
// addCodeToName + captureLargerStackTrace
|
||||
let err = error;
|
||||
const userStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = Infinity;
|
||||
ErrorCaptureStackTrace(err);
|
||||
Error.stackTraceLimit = userStackTraceLimit; // Reset the limit
|
||||
err.name = `${TypeError.name} [${sym}]`; // Add the error code to the name to include it in the stack trace.
|
||||
err.stack; // Access the stack to generate the error message including the error code from the name.
|
||||
delete err.name; // Reset the name to the actual name.
|
||||
error.code = sym;
|
||||
return error;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {unknown} value
|
||||
* @param {string} name
|
||||
@@ -362,6 +405,14 @@ function isURL(value) {
|
||||
return typeof value.href === "string" && value instanceof URL;
|
||||
}
|
||||
|
||||
function isURLSearchParams(value) {
|
||||
return value !== null && typeof value === "object" && value[Symbol.toStringTag] === "URLSearchParams";
|
||||
}
|
||||
|
||||
function isURLSearchParamsIterator(value) {
|
||||
return value !== null && typeof value === "object" && value[Symbol.toStringTag] === "URLSearchParams Iterator";
|
||||
}
|
||||
|
||||
const builtInObjects = new SafeSet(
|
||||
ArrayPrototypeFilter(
|
||||
ObjectGetOwnPropertyNames(globalThis),
|
||||
@@ -1300,6 +1351,13 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
|
||||
: FunctionPrototypeBind(formatMap, null, MapPrototypeEntries(value));
|
||||
if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`;
|
||||
braces = [`${prefix}{`, "}"];
|
||||
} else if (isURLSearchParams(value)) {
|
||||
const size = value.size;
|
||||
const prefix = getPrefix(constructor, tag, "URLSearchParams");
|
||||
keys = getKeys(value, ctx.showHidden);
|
||||
formatter = FunctionPrototypeBind(formatMap, null, value);
|
||||
if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`;
|
||||
braces = [`${prefix}{`, "}"];
|
||||
} else if (isTypedArray(value)) {
|
||||
keys = getOwnNonIndexProperties(value, filter);
|
||||
let bound = value;
|
||||
@@ -1327,6 +1385,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
|
||||
braces = getIteratorBraces("Set", tag);
|
||||
// Add braces to the formatter parameters.
|
||||
formatter = FunctionPrototypeBind(formatIterator, null, braces);
|
||||
} else if (isURLSearchParamsIterator(value)) {
|
||||
keys = getKeys(value, ctx.showHidden);
|
||||
braces = getIteratorBraces("URLSearchParams", tag);
|
||||
formatter = FunctionPrototypeBind(formatURLSearchParamsIterator, null, braces);
|
||||
} else {
|
||||
noIterator = true;
|
||||
}
|
||||
@@ -2279,6 +2341,32 @@ function formatIterator(braces, ctx, value, recurseTimes) {
|
||||
return formatSetIterInner(ctx, recurseTimes, entries, kIterator);
|
||||
}
|
||||
|
||||
function formatURLSearchParamsIterator(braces, ctx, value, recurseTimes) {
|
||||
const items = [];
|
||||
let isEntries = false;
|
||||
|
||||
// Consume the iterator to determine the format
|
||||
for (const item of value) {
|
||||
items.push(item);
|
||||
// Check if the first item is an array (entries iterator)
|
||||
if (items.length === 1) {
|
||||
isEntries = Array.isArray(item) && item.length === 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEntries) {
|
||||
// Entries iterator: convert to flat array for formatMapIterInner
|
||||
const entries = [];
|
||||
for (const [key, val] of items) {
|
||||
entries.push(key, val);
|
||||
}
|
||||
return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries);
|
||||
} else {
|
||||
// Keys or values iterator: format as simple list
|
||||
return formatSetIterInner(ctx, recurseTimes, items, kIterator);
|
||||
}
|
||||
}
|
||||
|
||||
function formatPromise(ctx, value, recurseTimes) {
|
||||
let output;
|
||||
const { 0: state, 1: result } = getPromiseDetails(value);
|
||||
@@ -2746,6 +2834,38 @@ function internalGetConstructorName(val) {
|
||||
return m ? m[1] : "Object";
|
||||
}
|
||||
|
||||
// Add custom inspect method to URLSearchParams
|
||||
if (typeof URLSearchParams !== "undefined") {
|
||||
URLSearchParams.prototype[customInspectSymbol] = function urlSearchParamsCustomInspect(depth, options, inspect) {
|
||||
if (this == null || typeof this.forEach !== "function") {
|
||||
throw codes.ERR_INVALID_THIS("URLSearchParams");
|
||||
}
|
||||
|
||||
if (depth != null && depth < 0) {
|
||||
return "[Object]";
|
||||
}
|
||||
|
||||
const entries = [];
|
||||
this.forEach((value, key) => {
|
||||
entries.push(`'${key}' => '${value}'`);
|
||||
});
|
||||
|
||||
if (entries.length === 0) {
|
||||
return "URLSearchParams {}";
|
||||
}
|
||||
|
||||
const inner = entries.join(", ");
|
||||
|
||||
// Handle breakLength for multiline formatting
|
||||
if (options && options.breakLength != null && inner.length > options.breakLength) {
|
||||
const formattedEntries = entries.join(",\n ");
|
||||
return `URLSearchParams {\n ${formattedEntries}\n}`;
|
||||
}
|
||||
|
||||
return `URLSearchParams { ${inner} }`;
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
inspect,
|
||||
format,
|
||||
|
||||
@@ -408,50 +408,6 @@ const IncomingMessagePrototype = {
|
||||
set socket(value) {
|
||||
this[fakeSocketSymbol] = value;
|
||||
},
|
||||
_addHeaderLine(field: string, value: string | undefined, dest: any) {
|
||||
field = String(field);
|
||||
const key = field.toLowerCase();
|
||||
|
||||
if (key === "set-cookie") {
|
||||
if (dest[key] !== undefined) {
|
||||
dest[key].push(value);
|
||||
} else {
|
||||
dest[key] = [value];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "content-type":
|
||||
case "user-agent":
|
||||
case "referer":
|
||||
case "host":
|
||||
case "authorization":
|
||||
case "proxy-authorization":
|
||||
case "if-modified-since":
|
||||
case "if-unmodified-since":
|
||||
case "location":
|
||||
case "max-forwards":
|
||||
case "retry-after":
|
||||
case "etag":
|
||||
case "last-modified":
|
||||
case "server":
|
||||
case "age":
|
||||
case "expires":
|
||||
if (dest[key] === undefined) dest[key] = value;
|
||||
return;
|
||||
case "cookie":
|
||||
if (dest[key] === undefined) dest[key] = value;
|
||||
else dest[key] += "; " + value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dest[key] === undefined) {
|
||||
dest[key] = value;
|
||||
} else {
|
||||
dest[key] += ", " + value;
|
||||
}
|
||||
},
|
||||
} satisfies typeof import("node:http").IncomingMessage.prototype;
|
||||
IncomingMessage.prototype = IncomingMessagePrototype;
|
||||
$setPrototypeDirect.$call(IncomingMessage, Readable);
|
||||
|
||||
@@ -1071,12 +1071,19 @@ const ServerPrototype = {
|
||||
http_res.assignSocket(socket);
|
||||
}
|
||||
}
|
||||
} else if (http_req.headers.expect === "100-continue") {
|
||||
if (server.listenerCount("checkContinue") > 0) {
|
||||
server.emit("checkContinue", http_req, http_res);
|
||||
} else if (http_req.headers.expect !== undefined) {
|
||||
if (http_req.headers.expect === "100-continue") {
|
||||
if (server.listenerCount("checkContinue") > 0) {
|
||||
server.emit("checkContinue", http_req, http_res);
|
||||
} else {
|
||||
http_res.writeContinue();
|
||||
server.emit("request", http_req, http_res);
|
||||
}
|
||||
} else if (server.listenerCount("checkExpectation") > 0) {
|
||||
server.emit("checkExpectation", http_req, http_res);
|
||||
} else {
|
||||
http_res.writeContinue();
|
||||
server.emit("request", http_req, http_res);
|
||||
http_res.writeHead(417);
|
||||
http_res.end();
|
||||
}
|
||||
} else {
|
||||
server.emit("request", http_req, http_res);
|
||||
@@ -1408,6 +1415,12 @@ const NodeHTTPServerSocket = class Socket extends Duplex {
|
||||
return this;
|
||||
}
|
||||
|
||||
setEncoding(_encoding) {
|
||||
const err = new Error("Changing the socket encoding is not allowed per RFC7230 Section 3.");
|
||||
err.code = "ERR_HTTP_SOCKET_ENCODING";
|
||||
throw err;
|
||||
}
|
||||
|
||||
unref() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ function isIP(s): 0 | 4 | 6 {
|
||||
}
|
||||
|
||||
const bunTlsSymbol = Symbol.for("::buntls::");
|
||||
const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::");
|
||||
const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
|
||||
const owner_symbol = Symbol("owner_symbol");
|
||||
|
||||
@@ -118,11 +117,7 @@ function endNT(socket, callback, err) {
|
||||
callback(err);
|
||||
}
|
||||
function emitCloseNT(self, hasError) {
|
||||
if (hasError) {
|
||||
self.emit("close", hasError);
|
||||
} else {
|
||||
self.emit("close");
|
||||
}
|
||||
self.emit("close", hasError);
|
||||
}
|
||||
function detachSocket(self) {
|
||||
if (!self) self = this;
|
||||
@@ -233,6 +228,7 @@ const SocketHandlers: SocketHandler = {
|
||||
self[kwriteCallback] = null;
|
||||
callback(error);
|
||||
}
|
||||
|
||||
self.emit("error", error);
|
||||
},
|
||||
open(socket) {
|
||||
@@ -342,7 +338,7 @@ const ServerHandlers: SocketHandler = {
|
||||
const data = this.data;
|
||||
if (!data) return;
|
||||
|
||||
data.server[bunSocketServerConnections]--;
|
||||
data.server._connections--;
|
||||
{
|
||||
if (!data[kclosed]) {
|
||||
data[kclosed] = true;
|
||||
@@ -388,7 +384,7 @@ const ServerHandlers: SocketHandler = {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
|
||||
if (self.maxConnections != null && self._connections >= self.maxConnections) {
|
||||
const data = {
|
||||
localAddress: _socket.localAddress,
|
||||
localPort: _socket.localPort || this.localPort,
|
||||
@@ -407,7 +403,11 @@ const ServerHandlers: SocketHandler = {
|
||||
const bunTLS = _socket[bunTlsSymbol];
|
||||
const isTLS = typeof bunTLS === "function";
|
||||
|
||||
self[bunSocketServerConnections]++;
|
||||
self._connections++;
|
||||
|
||||
if (pauseOnConnect) {
|
||||
_socket.pause();
|
||||
}
|
||||
|
||||
if (typeof connectionListener === "function") {
|
||||
this.pauseOnConnect = pauseOnConnect;
|
||||
@@ -463,7 +463,9 @@ const ServerHandlers: SocketHandler = {
|
||||
// after secureConnection event we emmit secure and secureConnect
|
||||
self.emit("secure", self);
|
||||
self.emit("secureConnect", verifyError);
|
||||
if (!server.pauseOnConnect) {
|
||||
if (server.pauseOnConnect) {
|
||||
self.pause();
|
||||
} else {
|
||||
self.resume();
|
||||
}
|
||||
},
|
||||
@@ -517,6 +519,9 @@ const SocketHandlers2: SocketHandler<SocketHandleData> = {
|
||||
$debug("self[kupgraded]", String(self[kupgraded]));
|
||||
if (!self[kupgraded]) req!.oncomplete(0, self._handle, req, true, true);
|
||||
socket.data.req = undefined;
|
||||
if (self.pauseOnConnect) {
|
||||
self.pause();
|
||||
}
|
||||
if (self[kupgraded]) {
|
||||
self.connecting = false;
|
||||
const options = self[bunTLSConnectOptions];
|
||||
@@ -724,6 +729,8 @@ function Socket(options?) {
|
||||
this[kclosed] = false;
|
||||
this[kended] = false;
|
||||
this.connecting = false;
|
||||
this._host = undefined;
|
||||
this._port = undefined;
|
||||
this[bunTLSConnectOptions] = null;
|
||||
this.timeout = 0;
|
||||
this[kwriteCallback] = undefined;
|
||||
@@ -839,7 +846,9 @@ Object.defineProperty(Socket.prototype, "bytesWritten", {
|
||||
else bytes += Buffer.byteLength(chunk.chunk, chunk.encoding);
|
||||
}
|
||||
} else if (data) {
|
||||
bytes += data.byteLength;
|
||||
// Writes are either a string or a Buffer.
|
||||
if (typeof data !== "string") bytes += data.length;
|
||||
else bytes += Buffer.byteLength(data, this._pendingEncoding || "utf8");
|
||||
}
|
||||
return bytes;
|
||||
},
|
||||
@@ -857,7 +866,7 @@ Socket.prototype[kAttach] = function (port, socket) {
|
||||
}
|
||||
|
||||
if (this[kSetKeepAlive]) {
|
||||
socket.setKeepAlive(true, self[kSetKeepAliveInitialDelay]);
|
||||
socket.setKeepAlive(true, this[kSetKeepAliveInitialDelay]);
|
||||
}
|
||||
|
||||
if (!this[kupgraded]) {
|
||||
@@ -899,12 +908,14 @@ Socket.prototype.connect = function connect(...args) {
|
||||
}).catch(error => {
|
||||
if (!this.destroyed) {
|
||||
this.emit("error", error);
|
||||
this.emit("close");
|
||||
this.emit("close", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.pauseOnConnect = pauseOnConnect;
|
||||
if (!pauseOnConnect) {
|
||||
if (pauseOnConnect) {
|
||||
this.pause();
|
||||
} else {
|
||||
process.nextTick(() => {
|
||||
this.resume();
|
||||
});
|
||||
@@ -1151,7 +1162,7 @@ Socket.prototype._destroy = function _destroy(err, callback) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(err);
|
||||
process.nextTick(emitCloseNT, this);
|
||||
process.nextTick(emitCloseNT, this, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1546,6 +1557,7 @@ function lookupAndConnect(self, options) {
|
||||
$debug("connect: find host", host, addressType);
|
||||
$debug("connect: dns options", dnsopts);
|
||||
self._host = host;
|
||||
self._port = port;
|
||||
const lookup = options.lookup || dns.lookup;
|
||||
|
||||
if (dnsopts.family !== 4 && dnsopts.family !== 6 && !localAddress && autoSelectFamily) {
|
||||
@@ -2062,7 +2074,6 @@ function Server(options?, connectionListener?) {
|
||||
|
||||
// https://nodejs.org/api/net.html#netcreateserveroptions-connectionlistener
|
||||
const {
|
||||
maxConnections, //
|
||||
allowHalfOpen = false,
|
||||
keepAlive = false,
|
||||
keepAliveInitialDelay = 0,
|
||||
@@ -2079,7 +2090,6 @@ function Server(options?, connectionListener?) {
|
||||
this._unref = false;
|
||||
this.listeningId = 1;
|
||||
|
||||
this[bunSocketServerConnections] = 0;
|
||||
this[bunSocketServerOptions] = undefined;
|
||||
this.allowHalfOpen = allowHalfOpen;
|
||||
this.keepAlive = keepAlive;
|
||||
@@ -2087,7 +2097,6 @@ function Server(options?, connectionListener?) {
|
||||
this.highWaterMark = highWaterMark;
|
||||
this.pauseOnConnect = Boolean(pauseOnConnect);
|
||||
this.noDelay = noDelay;
|
||||
this.maxConnections = Number.isSafeInteger(maxConnections) && maxConnections > 0 ? maxConnections : 0;
|
||||
|
||||
options.connectionListener = connectionListener;
|
||||
this[bunSocketServerOptions] = options;
|
||||
@@ -2150,7 +2159,7 @@ Server.prototype[Symbol.asyncDispose] = function () {
|
||||
};
|
||||
|
||||
Server.prototype._emitCloseIfDrained = function _emitCloseIfDrained() {
|
||||
if (this._handle || this[bunSocketServerConnections] > 0) {
|
||||
if (this._handle || this._connections > 0) {
|
||||
return;
|
||||
}
|
||||
process.nextTick(() => {
|
||||
@@ -2179,12 +2188,13 @@ Server.prototype.getConnections = function getConnections(callback) {
|
||||
//in Bun case we will never error on getConnections
|
||||
//node only errors if in the middle of the couting the server got disconnected, what never happens in Bun
|
||||
//if disconnected will only pass null as well and 0 connected
|
||||
callback(null, this._handle ? this[bunSocketServerConnections] : 0);
|
||||
callback(null, this._handle ? this._connections : 0);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Server.prototype.listen = function listen(port, hostname, onListen) {
|
||||
const argsLength = arguments.length;
|
||||
if (typeof port === "string") {
|
||||
const numPort = Number(port);
|
||||
if (!Number.isNaN(numPort)) port = numPort;
|
||||
@@ -2212,9 +2222,15 @@ Server.prototype.listen = function listen(port, hostname, onListen) {
|
||||
hostname = undefined;
|
||||
port = undefined;
|
||||
} else {
|
||||
if (typeof hostname === "function") {
|
||||
if (typeof hostname === "number") {
|
||||
backlog = hostname;
|
||||
hostname = undefined;
|
||||
} else if (typeof hostname === "function") {
|
||||
onListen = hostname;
|
||||
hostname = undefined;
|
||||
} else if (typeof hostname === "string" && typeof onListen === "number") {
|
||||
backlog = onListen;
|
||||
onListen = argsLength > 3 ? arguments[3] : undefined;
|
||||
}
|
||||
|
||||
if (typeof port === "function") {
|
||||
@@ -2231,6 +2247,7 @@ Server.prototype.listen = function listen(port, hostname, onListen) {
|
||||
ipv6Only = options.ipv6Only;
|
||||
allowHalfOpen = options.allowHalfOpen;
|
||||
reusePort = options.reusePort;
|
||||
backlog = options.backlog;
|
||||
|
||||
if (typeof options.fd === "number" && options.fd >= 0) {
|
||||
fd = options.fd;
|
||||
@@ -2424,7 +2441,7 @@ function emitErrorNextTick(self, error) {
|
||||
|
||||
function emitErrorAndCloseNextTick(self, error) {
|
||||
self.emit("error", error);
|
||||
self.emit("close");
|
||||
self.emit("close", true);
|
||||
}
|
||||
|
||||
function addServerAbortSignalOption(self, options) {
|
||||
|
||||
@@ -7671,7 +7671,15 @@ fn NewParser_(
|
||||
ifStmtScopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.block, loc);
|
||||
}
|
||||
|
||||
const scopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.function_args, p.lexer.loc());
|
||||
var scopeIndex: usize = 0;
|
||||
var pushedScopeForFunctionArgs = false;
|
||||
// Push scope if the current lexer token is an open parenthesis token.
|
||||
// That is, the parser is about parsing function arguments
|
||||
if (p.lexer.token == .t_open_paren) {
|
||||
scopeIndex = try p.pushScopeForParsePass(js_ast.Scope.Kind.function_args, p.lexer.loc());
|
||||
pushedScopeForFunctionArgs = true;
|
||||
}
|
||||
|
||||
var func = try p.parseFn(name, FnOrArrowDataParse{
|
||||
.needs_async_loc = loc,
|
||||
.async_range = asyncRange orelse logger.Range.None,
|
||||
@@ -7687,7 +7695,7 @@ fn NewParser_(
|
||||
|
||||
if (comptime is_typescript_enabled) {
|
||||
// Don't output anything if it's just a forward declaration of a function
|
||||
if (opts.is_typescript_declare or func.flags.contains(.is_forward_declaration)) {
|
||||
if ((opts.is_typescript_declare or func.flags.contains(.is_forward_declaration)) and pushedScopeForFunctionArgs) {
|
||||
p.popAndDiscardScope(scopeIndex);
|
||||
|
||||
// Balance the fake block scope introduced above
|
||||
@@ -7703,7 +7711,9 @@ fn NewParser_(
|
||||
}
|
||||
}
|
||||
|
||||
p.popScope();
|
||||
if (pushedScopeForFunctionArgs) {
|
||||
p.popScope();
|
||||
}
|
||||
|
||||
// Only declare the function after we know if it had a body or not. Otherwise
|
||||
// TypeScript code such as this will double-declare the symbol:
|
||||
@@ -12605,14 +12615,19 @@ fn NewParser_(
|
||||
p.allow_in = true;
|
||||
|
||||
const loc = p.lexer.loc();
|
||||
_ = try p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
|
||||
defer p.popScope();
|
||||
var pushedScopeForFunctionBody = false;
|
||||
if (p.lexer.token == .t_open_brace) {
|
||||
_ = try p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
|
||||
pushedScopeForFunctionBody = true;
|
||||
}
|
||||
|
||||
try p.lexer.expect(.t_open_brace);
|
||||
var opts = ParseStatementOptions{};
|
||||
const stmts = try p.parseStmtsUpTo(.t_close_brace, &opts);
|
||||
try p.lexer.next();
|
||||
|
||||
if (pushedScopeForFunctionBody) p.popScope();
|
||||
|
||||
p.allow_in = oldAllowIn;
|
||||
p.fn_or_arrow_data_parse = oldFnOrArrowData;
|
||||
return G.FnBody{ .loc = loc, .stmts = stmts };
|
||||
|
||||
@@ -3462,6 +3462,18 @@ describe("await can only be used inside an async function message", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("malformed function definition does not crash due to invalid scope initialization", () => {
|
||||
it("fails with a parse error and exits cleanly", async () => {
|
||||
const tests = ["function:", "function a() {function:}"];
|
||||
for (const code of tests) {
|
||||
for (const loader of ["js", "ts"]) {
|
||||
const transpiler = new Bun.Transpiler({ loader });
|
||||
expect(() => transpiler.transformSync(code)).toThrow("Parse error");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not crash with 9 comments and typescript type skipping", () => {
|
||||
const cmd = [bunExe(), "build", "--minify-identifiers", join(import.meta.dir, "fixtures", "9-comments.ts")];
|
||||
const { stdout, stderr, exitCode } = Bun.spawnSync({
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const IncomingMessage = require('http').IncomingMessage;
|
||||
|
||||
function checkDest(field, result, value) {
|
||||
const dest = {};
|
||||
|
||||
const incomingMessage = new IncomingMessage(field);
|
||||
// Dest is changed by IncomingMessage._addHeaderLine
|
||||
if (value)
|
||||
incomingMessage._addHeaderLine(field, 'test', dest);
|
||||
incomingMessage._addHeaderLine(field, value, dest);
|
||||
assert.deepStrictEqual(dest, result);
|
||||
}
|
||||
|
||||
checkDest('', { '': undefined });
|
||||
checkDest('Content-Type', { 'content-type': undefined });
|
||||
checkDest('content-type', { 'content-type': 'test' }, 'value');
|
||||
checkDest('User-Agent', { 'user-agent': undefined });
|
||||
checkDest('user-agent', { 'user-agent': 'test' }, 'value');
|
||||
checkDest('Referer', { referer: undefined });
|
||||
checkDest('referer', { referer: 'test' }, 'value');
|
||||
checkDest('Host', { host: undefined });
|
||||
checkDest('host', { host: 'test' }, 'value');
|
||||
checkDest('Authorization', { authorization: undefined }, undefined);
|
||||
checkDest('authorization', { authorization: 'test' }, 'value');
|
||||
checkDest('Proxy-Authorization', { 'proxy-authorization': undefined });
|
||||
checkDest('proxy-authorization', { 'proxy-authorization': 'test' }, 'value');
|
||||
checkDest('If-Modified-Since', { 'if-modified-since': undefined });
|
||||
checkDest('if-modified-since', { 'if-modified-since': 'test' }, 'value');
|
||||
checkDest('If-Unmodified-Since', { 'if-unmodified-since': undefined });
|
||||
checkDest('if-unmodified-since', { 'if-unmodified-since': 'test' }, 'value');
|
||||
checkDest('Form', { form: undefined });
|
||||
checkDest('form', { form: 'test, value' }, 'value');
|
||||
checkDest('Location', { location: undefined });
|
||||
checkDest('location', { location: 'test' }, 'value');
|
||||
checkDest('Max-Forwards', { 'max-forwards': undefined });
|
||||
checkDest('max-forwards', { 'max-forwards': 'test' }, 'value');
|
||||
checkDest('Retry-After', { 'retry-after': undefined });
|
||||
checkDest('retry-after', { 'retry-after': 'test' }, 'value');
|
||||
checkDest('Etag', { etag: undefined });
|
||||
checkDest('etag', { etag: 'test' }, 'value');
|
||||
checkDest('Last-Modified', { 'last-modified': undefined });
|
||||
checkDest('last-modified', { 'last-modified': 'test' }, 'value');
|
||||
checkDest('Server', { server: undefined });
|
||||
checkDest('server', { server: 'test' }, 'value');
|
||||
checkDest('Age', { age: undefined });
|
||||
checkDest('age', { age: 'test' }, 'value');
|
||||
checkDest('Expires', { expires: undefined });
|
||||
checkDest('expires', { expires: 'test' }, 'value');
|
||||
checkDest('Set-Cookie', { 'set-cookie': [undefined] });
|
||||
checkDest('set-cookie', { 'set-cookie': ['test', 'value'] }, 'value');
|
||||
checkDest('Transfer-Encoding', { 'transfer-encoding': undefined });
|
||||
checkDest('transfer-encoding', { 'transfer-encoding': 'test, value' }, 'value');
|
||||
checkDest('Date', { date: undefined });
|
||||
checkDest('date', { date: 'test, value' }, 'value');
|
||||
checkDest('Connection', { connection: undefined });
|
||||
checkDest('connection', { connection: 'test, value' }, 'value');
|
||||
checkDest('Cache-Control', { 'cache-control': undefined });
|
||||
checkDest('cache-control', { 'cache-control': 'test, value' }, 'value');
|
||||
checkDest('Transfer-Encoding', { 'transfer-encoding': undefined });
|
||||
checkDest('transfer-encoding', { 'transfer-encoding': 'test, value' }, 'value');
|
||||
checkDest('Vary', { vary: undefined });
|
||||
checkDest('vary', { vary: 'test, value' }, 'value');
|
||||
checkDest('Content-Encoding', { 'content-encoding': undefined }, undefined);
|
||||
checkDest('content-encoding', { 'content-encoding': 'test, value' }, 'value');
|
||||
checkDest('Cookie', { cookie: undefined });
|
||||
checkDest('cookie', { cookie: 'test; value' }, 'value');
|
||||
checkDest('Origin', { origin: undefined });
|
||||
checkDest('origin', { origin: 'test, value' }, 'value');
|
||||
checkDest('Upgrade', { upgrade: undefined });
|
||||
checkDest('upgrade', { upgrade: 'test, value' }, 'value');
|
||||
checkDest('Expect', { expect: undefined });
|
||||
checkDest('expect', { expect: 'test, value' }, 'value');
|
||||
checkDest('If-Match', { 'if-match': undefined });
|
||||
checkDest('if-match', { 'if-match': 'test, value' }, 'value');
|
||||
checkDest('If-None-Match', { 'if-none-match': undefined });
|
||||
checkDest('if-none-match', { 'if-none-match': 'test, value' }, 'value');
|
||||
checkDest('Accept', { accept: undefined });
|
||||
checkDest('accept', { accept: 'test, value' }, 'value');
|
||||
checkDest('Accept-Encoding', { 'accept-encoding': undefined });
|
||||
checkDest('accept-encoding', { 'accept-encoding': 'test, value' }, 'value');
|
||||
checkDest('Accept-Language', { 'accept-language': undefined });
|
||||
checkDest('accept-language', { 'accept-language': 'test, value' }, 'value');
|
||||
checkDest('X-Forwarded-For', { 'x-forwarded-for': undefined });
|
||||
checkDest('x-forwarded-for', { 'x-forwarded-for': 'test, value' }, 'value');
|
||||
checkDest('X-Forwarded-Host', { 'x-forwarded-host': undefined });
|
||||
checkDest('x-forwarded-host', { 'x-forwarded-host': 'test, value' }, 'value');
|
||||
checkDest('X-Forwarded-Proto', { 'x-forwarded-proto': undefined });
|
||||
checkDest('x-forwarded-proto', { 'x-forwarded-proto': 'test, value' }, 'value');
|
||||
checkDest('X-Foo', { 'x-foo': undefined });
|
||||
checkDest('x-foo', { 'x-foo': 'test, value' }, 'value');
|
||||
26
test/js/node/parallel/test-http-socket-encoding-error.js
Normal file
26
test/js/node/parallel/test-http-socket-encoding-error.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
|
||||
const server = http.createServer().listen(0, connectToServer);
|
||||
|
||||
server.on('connection', common.mustCall((socket) => {
|
||||
assert.throws(
|
||||
() => {
|
||||
socket.setEncoding('');
|
||||
},
|
||||
{
|
||||
code: 'ERR_HTTP_SOCKET_ENCODING',
|
||||
name: 'Error',
|
||||
message: 'Changing the socket encoding is not allowed per RFC7230 Section 3.'
|
||||
}
|
||||
);
|
||||
|
||||
socket.end();
|
||||
}));
|
||||
|
||||
function connectToServer() {
|
||||
const client = new http.Agent().createConnection(this.address().port, () => {
|
||||
client.end();
|
||||
}).on('end', () => server.close());
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
|
||||
const sp = new URLSearchParams('?a=a&b=b&b=c');
|
||||
assert.strictEqual(util.inspect(sp),
|
||||
"URLSearchParams { 'a' => 'a', 'b' => 'b', 'b' => 'c' }");
|
||||
assert.strictEqual(util.inspect(sp, { depth: -1 }), '[Object]');
|
||||
assert.strictEqual(
|
||||
util.inspect(sp, { breakLength: 1 }),
|
||||
"URLSearchParams {\n 'a' => 'a',\n 'b' => 'b',\n 'b' => 'c' }"
|
||||
);
|
||||
assert.strictEqual(util.inspect(sp.keys()),
|
||||
"URLSearchParams Iterator { 'a', 'b', 'b' }");
|
||||
assert.strictEqual(util.inspect(sp.values()),
|
||||
"URLSearchParams Iterator { 'a', 'b', 'c' }");
|
||||
assert.strictEqual(util.inspect(sp.keys(), { breakLength: 1 }),
|
||||
"URLSearchParams Iterator {\n 'a',\n 'b',\n 'b' }");
|
||||
assert.throws(() => sp[util.inspect.custom].call(), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
});
|
||||
|
||||
const iterator = sp.entries();
|
||||
assert.strictEqual(util.inspect(iterator),
|
||||
"URLSearchParams Iterator { [ 'a', 'a' ], [ 'b', 'b' ], " +
|
||||
"[ 'b', 'c' ] }");
|
||||
iterator.next();
|
||||
assert.strictEqual(util.inspect(iterator),
|
||||
"URLSearchParams Iterator { [ 'b', 'b' ], [ 'b', 'c' ] }");
|
||||
iterator.next();
|
||||
iterator.next();
|
||||
assert.strictEqual(util.inspect(iterator),
|
||||
'URLSearchParams Iterator { }');
|
||||
const emptySp = new URLSearchParams();
|
||||
assert.strictEqual(util.inspect(emptySp), 'URLSearchParams {}');
|
||||
@@ -1114,3 +1114,20 @@ it("should handle user assigned `default` properties", async () => {
|
||||
|
||||
await promise;
|
||||
});
|
||||
|
||||
it.each(["stdin", "stdout", "stderr"])("%s stream accessor should handle exceptions without crashing", stream => {
|
||||
expect([
|
||||
/* js */ `
|
||||
const old = process;
|
||||
process = null;
|
||||
try {
|
||||
old.${stream};
|
||||
} catch {}
|
||||
if (typeof old.${stream} !== "undefined") {
|
||||
console.log("wrong");
|
||||
}
|
||||
`,
|
||||
"",
|
||||
1,
|
||||
]).toRunInlineFixture();
|
||||
});
|
||||
|
||||
55
test/js/node/test/parallel/test-http-expect-handling.js
Normal file
55
test/js/node/test/parallel/test-http-expect-handling.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// Spec documentation http://httpwg.github.io/specs/rfc7231.html#header.expect
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
|
||||
const tests = [417, 417];
|
||||
|
||||
let testsComplete = 0;
|
||||
let testIdx = 0;
|
||||
|
||||
const s = http.createServer((req, res) => {
|
||||
throw new Error('this should never be executed');
|
||||
});
|
||||
|
||||
s.listen(0, nextTest);
|
||||
|
||||
function nextTest() {
|
||||
const options = {
|
||||
port: s.address().port,
|
||||
headers: { 'Expect': 'meoww' }
|
||||
};
|
||||
|
||||
if (testIdx === tests.length) {
|
||||
return s.close();
|
||||
}
|
||||
|
||||
const test = tests[testIdx];
|
||||
|
||||
if (testIdx > 0) {
|
||||
s.on('checkExpectation', common.mustCall((req, res) => {
|
||||
res.statusCode = 417;
|
||||
res.end();
|
||||
}));
|
||||
}
|
||||
|
||||
http.get(options, (response) => {
|
||||
console.log(`client: expected status: ${test}`);
|
||||
console.log(`client: statusCode: ${response.statusCode}`);
|
||||
assert.strictEqual(response.statusCode, test);
|
||||
assert.strictEqual(response.statusMessage, 'Expectation Failed');
|
||||
|
||||
response.on('end', () => {
|
||||
testsComplete++;
|
||||
testIdx++;
|
||||
nextTest();
|
||||
});
|
||||
response.resume();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
process.on('exit', () => {
|
||||
assert.strictEqual(testsComplete, 2);
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { createMockedLookup } = require('../common/dns');
|
||||
|
||||
const assert = require('assert');
|
||||
const { createConnection, createServer, setDefaultAutoSelectFamily } = require('net');
|
||||
|
||||
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
|
||||
|
||||
// Test that IPV4 is reached by default if IPV6 is not reachable and the default is enabled
|
||||
{
|
||||
const ipv4Server = createServer((socket) => {
|
||||
socket.on('data', common.mustCall(() => {
|
||||
socket.write('response-ipv4');
|
||||
socket.end();
|
||||
}));
|
||||
});
|
||||
|
||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||
setDefaultAutoSelectFamily(true);
|
||||
|
||||
const connection = createConnection({
|
||||
host: 'example.org',
|
||||
port: ipv4Server.address().port,
|
||||
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||
autoSelectFamilyAttemptTimeout,
|
||||
});
|
||||
|
||||
let response = '';
|
||||
connection.setEncoding('utf-8');
|
||||
|
||||
connection.on('data', (chunk) => {
|
||||
response += chunk;
|
||||
});
|
||||
|
||||
connection.on('end', common.mustCall(() => {
|
||||
assert.strictEqual(response, 'response-ipv4');
|
||||
ipv4Server.close();
|
||||
}));
|
||||
|
||||
connection.write('request');
|
||||
}));
|
||||
}
|
||||
|
||||
// Test that IPV4 is not reached by default if IPV6 is not reachable and the default is disabled
|
||||
{
|
||||
const ipv4Server = createServer((socket) => {
|
||||
socket.on('data', common.mustCall(() => {
|
||||
socket.write('response-ipv4');
|
||||
socket.end();
|
||||
}));
|
||||
});
|
||||
|
||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||
setDefaultAutoSelectFamily(false);
|
||||
|
||||
const port = ipv4Server.address().port;
|
||||
|
||||
const connection = createConnection({
|
||||
host: 'example.org',
|
||||
port,
|
||||
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||
});
|
||||
|
||||
connection.on('ready', common.mustNotCall());
|
||||
connection.on('error', common.mustCall((error) => {
|
||||
if (common.hasIPv6) {
|
||||
assert.strictEqual(error.code, 'ECONNREFUSED');
|
||||
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
||||
} else if (error.code === 'EAFNOSUPPORT') {
|
||||
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
||||
} else if (error.code === 'EUNATCH') {
|
||||
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
||||
} else {
|
||||
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
||||
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
||||
}
|
||||
|
||||
ipv4Server.close();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
78
test/js/node/test/parallel/test-net-bytes-stats.js
Normal file
78
test/js/node/test/parallel/test-net-bytes-stats.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
let bytesRead = 0;
|
||||
let bytesWritten = 0;
|
||||
let count = 0;
|
||||
|
||||
const tcp = net.Server(function(s) {
|
||||
console.log('tcp server connection');
|
||||
|
||||
// trigger old mode.
|
||||
s.resume();
|
||||
|
||||
s.on('end', function() {
|
||||
bytesRead += s.bytesRead;
|
||||
console.log(`tcp socket disconnect #${count}`);
|
||||
});
|
||||
});
|
||||
|
||||
tcp.listen(0, function doTest() {
|
||||
console.error('listening');
|
||||
const socket = net.createConnection(this.address().port);
|
||||
|
||||
socket.on('connect', function() {
|
||||
count++;
|
||||
console.error('CLIENT connect #%d', count);
|
||||
|
||||
socket.write('foo', function() {
|
||||
console.error('CLIENT: write cb');
|
||||
socket.end('bar');
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('finish', function() {
|
||||
bytesWritten += socket.bytesWritten;
|
||||
console.error('CLIENT end event #%d', count);
|
||||
});
|
||||
|
||||
socket.on('close', function() {
|
||||
console.error('CLIENT close event #%d', count);
|
||||
console.log(`Bytes read: ${bytesRead}`);
|
||||
console.log(`Bytes written: ${bytesWritten}`);
|
||||
if (count < 2) {
|
||||
console.error('RECONNECTING');
|
||||
socket.connect(tcp.address().port);
|
||||
} else {
|
||||
tcp.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(bytesRead, 12);
|
||||
assert.strictEqual(bytesWritten, 12);
|
||||
});
|
||||
77
test/js/node/test/parallel/test-net-connect-buffer.js
Normal file
77
test/js/node/test/parallel/test-net-connect-buffer.js
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
const tcp = net.Server(common.mustCall((s) => {
|
||||
tcp.close();
|
||||
|
||||
let buf = '';
|
||||
s.setEncoding('utf8');
|
||||
s.on('data', function(d) {
|
||||
buf += d;
|
||||
});
|
||||
|
||||
s.on('end', common.mustCall(function() {
|
||||
console.error('SERVER: end', buf);
|
||||
assert.strictEqual(buf, "L'État, c'est moi");
|
||||
s.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
tcp.listen(0, common.mustCall(function() {
|
||||
const socket = net.Stream({ highWaterMark: 0 });
|
||||
|
||||
let connected = false;
|
||||
assert.strictEqual(socket.pending, true);
|
||||
socket.connect(this.address().port, common.mustCall(() => connected = true));
|
||||
|
||||
assert.strictEqual(socket.pending, true);
|
||||
assert.strictEqual(socket.connecting, true);
|
||||
assert.strictEqual(socket.readyState, 'opening');
|
||||
|
||||
// Write a string that contains a multi-byte character sequence to test that
|
||||
// `bytesWritten` is incremented with the # of bytes, not # of characters.
|
||||
const a = "L'État, c'est ";
|
||||
const b = 'moi';
|
||||
|
||||
// We're still connecting at this point so the datagram is first pushed onto
|
||||
// the connect queue. Make sure that it's not added to `bytesWritten` again
|
||||
// when the actual write happens.
|
||||
const r = socket.write(a, common.mustCall((er) => {
|
||||
console.error('write cb');
|
||||
assert.ok(connected);
|
||||
assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length);
|
||||
assert.strictEqual(socket.pending, false);
|
||||
}));
|
||||
socket.on('close', common.mustCall(() => {
|
||||
assert.strictEqual(socket.pending, true);
|
||||
}));
|
||||
|
||||
assert.strictEqual(socket.bytesWritten, Buffer.from(a).length);
|
||||
assert.strictEqual(r, false);
|
||||
socket.end(b);
|
||||
|
||||
assert.strictEqual(socket.readyState, 'opening');
|
||||
}));
|
||||
56
test/js/node/test/parallel/test-net-connect-buffer2.js
Normal file
56
test/js/node/test/parallel/test-net-connect-buffer2.js
Normal file
@@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
const tcp = net.Server(common.mustCall((s) => {
|
||||
tcp.close();
|
||||
|
||||
let buf = '';
|
||||
s.setEncoding('utf8');
|
||||
s.on('data', function(d) {
|
||||
buf += d;
|
||||
});
|
||||
|
||||
s.on('end', common.mustCall(function() {
|
||||
console.error('SERVER: end', buf);
|
||||
assert.strictEqual(buf, "L'État, c'est moi");
|
||||
s.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
tcp.listen(0, common.mustCall(function() {
|
||||
const socket = net.Stream({ highWaterMark: 0 });
|
||||
|
||||
let connected = false;
|
||||
assert.strictEqual(socket.pending, true);
|
||||
socket.connect(this.address().port, common.mustCall(() => connected = true));
|
||||
|
||||
assert.strictEqual(socket.pending, true);
|
||||
assert.strictEqual(socket.connecting, true);
|
||||
assert.strictEqual(socket.readyState, 'opening');
|
||||
|
||||
// Write a string that contains a multi-byte character sequence to test that
|
||||
// `bytesWritten` is incremented with the # of bytes, not # of characters.
|
||||
const a = "L'État, c'est ";
|
||||
const b = 'moi';
|
||||
|
||||
// We're still connecting at this point so the datagram is first pushed onto
|
||||
// the connect queue. Make sure that it's not added to `bytesWritten` again
|
||||
// when the actual write happens.
|
||||
const r = socket.write(a, common.mustCall((er) => {
|
||||
console.error('write cb');
|
||||
assert.ok(connected);
|
||||
assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length);
|
||||
assert.strictEqual(socket.pending, false);
|
||||
}));
|
||||
socket.on('close', common.mustCall(() => {
|
||||
assert.strictEqual(socket.pending, true);
|
||||
}));
|
||||
|
||||
assert.strictEqual(socket.bytesWritten, Buffer.from(a).length);
|
||||
assert.strictEqual(r, false);
|
||||
socket.end(b);
|
||||
|
||||
assert.strictEqual(socket.readyState, 'opening');
|
||||
}));
|
||||
88
test/js/node/test/parallel/test-net-reconnect.js
Normal file
88
test/js/node/test/parallel/test-net-reconnect.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const net = require('net');
|
||||
|
||||
const N = 50;
|
||||
let client_recv_count = 0;
|
||||
let client_end_count = 0;
|
||||
let disconnect_count = 0;
|
||||
|
||||
const server = net.createServer(function(socket) {
|
||||
console.error('SERVER: got socket connection');
|
||||
socket.resume();
|
||||
|
||||
console.error('SERVER connect, writing');
|
||||
socket.write('hello\r\n');
|
||||
|
||||
socket.on('end', () => {
|
||||
console.error('SERVER socket end, calling end()');
|
||||
socket.end();
|
||||
});
|
||||
|
||||
socket.on('close', (had_error) => {
|
||||
console.log(`SERVER had_error: ${JSON.stringify(had_error)}`);
|
||||
assert.strictEqual(had_error, false);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(0, function() {
|
||||
console.log('SERVER listening');
|
||||
const client = net.createConnection(this.address().port);
|
||||
|
||||
client.setEncoding('UTF8');
|
||||
|
||||
client.on('connect', () => {
|
||||
console.error('CLIENT connected', client._writableState);
|
||||
});
|
||||
|
||||
client.on('data', function(chunk) {
|
||||
client_recv_count += 1;
|
||||
console.log(`client_recv_count ${client_recv_count}`);
|
||||
assert.strictEqual(chunk, 'hello\r\n');
|
||||
console.error('CLIENT: calling end', client._writableState);
|
||||
client.end();
|
||||
});
|
||||
|
||||
client.on('end', () => {
|
||||
console.error('CLIENT end');
|
||||
client_end_count++;
|
||||
});
|
||||
|
||||
client.on('close', (had_error) => {
|
||||
console.log('CLIENT disconnect');
|
||||
assert.strictEqual(had_error, false);
|
||||
if (disconnect_count++ < N)
|
||||
client.connect(server.address().port); // reconnect
|
||||
else
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', () => {
|
||||
assert.strictEqual(disconnect_count, N + 1);
|
||||
assert.strictEqual(client_recv_count, N + 1);
|
||||
assert.strictEqual(client_end_count, N + 1);
|
||||
});
|
||||
45
test/js/node/test/parallel/test-net-server-close.js
Normal file
45
test/js/node/test/parallel/test-net-server-close.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
const sockets = [];
|
||||
|
||||
const server = net.createServer(function(c) {
|
||||
c.on('close', common.mustCall());
|
||||
|
||||
sockets.push(c);
|
||||
|
||||
if (sockets.length === 2) {
|
||||
assert.strictEqual(server.close(), server);
|
||||
sockets.forEach((c) => c.destroy());
|
||||
}
|
||||
});
|
||||
|
||||
server.on('close', common.mustCall());
|
||||
|
||||
assert.strictEqual(server, server.listen(0, () => {
|
||||
net.createConnection(server.address().port);
|
||||
net.createConnection(server.address().port);
|
||||
}));
|
||||
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
let firstSocket;
|
||||
const dormantServer = net.createServer(common.mustNotCall());
|
||||
const server = net.createServer(common.mustCall((socket) => {
|
||||
firstSocket = socket;
|
||||
}));
|
||||
|
||||
dormantServer.maxConnections = 0;
|
||||
server.maxConnections = 1;
|
||||
|
||||
dormantServer.on('drop', common.mustCall((data) => {
|
||||
assert.strictEqual(!!data.localAddress, true);
|
||||
assert.strictEqual(!!data.localPort, true);
|
||||
assert.strictEqual(!!data.remoteAddress, true);
|
||||
assert.strictEqual(!!data.remotePort, true);
|
||||
assert.strictEqual(!!data.remoteFamily, true);
|
||||
dormantServer.close();
|
||||
}));
|
||||
|
||||
server.on('drop', common.mustCall((data) => {
|
||||
assert.strictEqual(!!data.localAddress, true);
|
||||
assert.strictEqual(!!data.localPort, true);
|
||||
assert.strictEqual(!!data.remoteAddress, true);
|
||||
assert.strictEqual(!!data.remotePort, true);
|
||||
assert.strictEqual(!!data.remoteFamily, true);
|
||||
firstSocket.destroy();
|
||||
server.close();
|
||||
}));
|
||||
|
||||
dormantServer.listen(0, () => {
|
||||
net.createConnection(dormantServer.address().port);
|
||||
});
|
||||
|
||||
server.listen(0, () => {
|
||||
net.createConnection(server.address().port);
|
||||
net.createConnection(server.address().port);
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
const msg = 'test';
|
||||
let stopped = true;
|
||||
let server1Sock;
|
||||
|
||||
const server1ConnHandler = (socket) => {
|
||||
socket.on('data', function(data) {
|
||||
if (stopped) {
|
||||
assert.fail('data event should not have happened yet');
|
||||
}
|
||||
assert.strictEqual(data.toString(), msg);
|
||||
socket.end();
|
||||
server1.close();
|
||||
});
|
||||
|
||||
server1Sock = socket;
|
||||
};
|
||||
|
||||
const server1 = net.createServer({ pauseOnConnect: true }, server1ConnHandler);
|
||||
|
||||
const server2ConnHandler = (socket) => {
|
||||
socket.on('data', function(data) {
|
||||
assert.strictEqual(data.toString(), msg);
|
||||
socket.end();
|
||||
server2.close();
|
||||
|
||||
assert.strictEqual(server1Sock.bytesRead, 0);
|
||||
server1Sock.resume();
|
||||
stopped = false;
|
||||
});
|
||||
};
|
||||
|
||||
const server2 = net.createServer({ pauseOnConnect: false }, server2ConnHandler);
|
||||
|
||||
server1.listen(0, function() {
|
||||
const clientHandler = common.mustCall(function() {
|
||||
server2.listen(0, function() {
|
||||
net.createConnection({ port: this.address().port }).write(msg);
|
||||
});
|
||||
});
|
||||
net.createConnection({ port: this.address().port }).write(msg, clientHandler);
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(stopped, false);
|
||||
});
|
||||
41
test/js/node/test/parallel/test-net-socket-local-address.js
Normal file
41
test/js/node/test/parallel/test-net-socket-local-address.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
// Skip test in FreeBSD jails
|
||||
if (common.inFreeBSDJail)
|
||||
common.skip('In a FreeBSD jail');
|
||||
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
let conns = 0;
|
||||
const clientLocalPorts = [];
|
||||
const serverRemotePorts = [];
|
||||
const client = new net.Socket();
|
||||
const server = net.createServer((socket) => {
|
||||
serverRemotePorts.push(socket.remotePort);
|
||||
socket.end();
|
||||
});
|
||||
|
||||
server.on('close', common.mustCall(() => {
|
||||
// Client and server should agree on the ports used
|
||||
assert.deepStrictEqual(serverRemotePorts, clientLocalPorts);
|
||||
assert.strictEqual(conns, 2);
|
||||
}));
|
||||
|
||||
server.listen(0, common.localhostIPv4, connect);
|
||||
|
||||
function connect() {
|
||||
if (conns === 2) {
|
||||
server.close();
|
||||
return;
|
||||
}
|
||||
|
||||
conns++;
|
||||
client.once('close', connect);
|
||||
assert.strictEqual(
|
||||
client,
|
||||
client.connect(server.address().port, common.localhostIPv4, () => {
|
||||
clientLocalPorts.push(client.localPort);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const unexpectedValues = [
|
||||
undefined,
|
||||
null,
|
||||
1,
|
||||
{},
|
||||
() => {},
|
||||
];
|
||||
for (const it of unexpectedValues) {
|
||||
assert.throws(() => {
|
||||
process.setSourceMapsEnabled(it);
|
||||
}, /ERR_INVALID_ARG_TYPE/);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const net = require('net');
|
||||
const assert = require('assert');
|
||||
|
||||
const c = net.createConnection(common.PORT);
|
||||
|
||||
c.on('connect', common.mustNotCall());
|
||||
|
||||
c.on('error', common.mustCall(function(error) {
|
||||
// Family autoselection might be skipped if only a single address is returned by DNS.
|
||||
const failedAttempt = Array.isArray(error.errors) ? error.errors[0] : error;
|
||||
|
||||
assert.strictEqual(failedAttempt.code, 'ECONNREFUSED');
|
||||
assert.strictEqual(failedAttempt.port, common.PORT);
|
||||
assert.match(failedAttempt.address, /^(127\.0\.0\.1|::1)$/);
|
||||
}));
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const net = require('net');
|
||||
const assert = require('assert');
|
||||
|
||||
const c = net.createConnection(common.PORT);
|
||||
c.on('connect', common.mustNotCall());
|
||||
c.on('error', common.mustCall((e) => {
|
||||
assert.strictEqual(c.connecting, false);
|
||||
assert.strictEqual(e.code, 'ECONNREFUSED');
|
||||
}));
|
||||
64
test/js/node/test/sequential/test-net-server-bind.js
Normal file
64
test/js/node/test/sequential/test-net-server-bind.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
|
||||
// With only a callback, server should get a port assigned by the OS
|
||||
{
|
||||
const server = net.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(common.mustCall(function() {
|
||||
assert.ok(server.address().port > 100);
|
||||
server.close();
|
||||
}));
|
||||
}
|
||||
|
||||
// No callback to listen(), assume we can bind in 100 ms
|
||||
{
|
||||
const server = net.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(common.PORT);
|
||||
|
||||
setTimeout(function() {
|
||||
const address = server.address();
|
||||
assert.strictEqual(address.port, common.PORT);
|
||||
|
||||
if (address.family === 'IPv6')
|
||||
assert.strictEqual(server._connectionKey, `6::::${address.port}`);
|
||||
else
|
||||
assert.strictEqual(server._connectionKey, `4:0.0.0.0:${address.port}`);
|
||||
|
||||
server.close();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Callback to listen()
|
||||
{
|
||||
const server = net.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(common.PORT + 1, common.mustCall(function() {
|
||||
assert.strictEqual(server.address().port, common.PORT + 1);
|
||||
server.close();
|
||||
}));
|
||||
}
|
||||
|
||||
// Backlog argument
|
||||
{
|
||||
const server = net.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(common.PORT + 2, '0.0.0.0', 127, common.mustCall(function() {
|
||||
assert.strictEqual(server.address().port, common.PORT + 2);
|
||||
server.close();
|
||||
}));
|
||||
}
|
||||
|
||||
// Backlog argument without host argument
|
||||
{
|
||||
const server = net.createServer(common.mustNotCall());
|
||||
|
||||
server.listen(common.PORT + 3, 127, common.mustCall(function() {
|
||||
assert.strictEqual(server.address().port, common.PORT + 3);
|
||||
server.close();
|
||||
}));
|
||||
}
|
||||
93
test/js/node/util-inspect-urlsearchparams.test.ts
Normal file
93
test/js/node/util-inspect-urlsearchparams.test.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import util from "util";
|
||||
|
||||
describe("util.inspect URLSearchParams", () => {
|
||||
it("should format URLSearchParams with key-value pairs", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp)).toBe("URLSearchParams { 'a' => 'a', 'b' => 'b', 'b' => 'c' }");
|
||||
});
|
||||
|
||||
it("should format empty URLSearchParams", () => {
|
||||
const emptySp = new URLSearchParams();
|
||||
expect(util.inspect(emptySp)).toBe("URLSearchParams {}");
|
||||
});
|
||||
|
||||
it("should respect depth option", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp, { depth: -1 })).toBe("[Object]");
|
||||
});
|
||||
|
||||
it("should respect breakLength option for multiline formatting", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp, { breakLength: 1 })).toBe(
|
||||
"URLSearchParams {\n 'a' => 'a',\n 'b' => 'b',\n 'b' => 'c'\n}",
|
||||
);
|
||||
});
|
||||
|
||||
it("should format URLSearchParams keys iterator", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp.keys())).toBe("URLSearchParams Iterator { 'a', 'b', 'b' }");
|
||||
});
|
||||
|
||||
it("should format URLSearchParams values iterator", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp.values())).toBe("URLSearchParams Iterator { 'a', 'b', 'c' }");
|
||||
});
|
||||
|
||||
it("should format URLSearchParams keys iterator with breakLength", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
expect(util.inspect(sp.keys(), { breakLength: 1 })).toBe("URLSearchParams Iterator {\n 'a',\n 'b',\n 'b'\n}");
|
||||
});
|
||||
|
||||
it("should format URLSearchParams entries iterator", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
const iterator = sp.entries();
|
||||
expect(util.inspect(iterator)).toBe("URLSearchParams Iterator { [ 'a', 'a' ], [ 'b', 'b' ], [ 'b', 'c' ] }");
|
||||
});
|
||||
|
||||
it("should format URLSearchParams entries iterator after consuming entries", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b&b=c");
|
||||
const iterator = sp.entries();
|
||||
iterator.next(); // consume first entry
|
||||
expect(util.inspect(iterator)).toBe("URLSearchParams Iterator { [ 'b', 'b' ], [ 'b', 'c' ] }");
|
||||
|
||||
iterator.next(); // consume second entry
|
||||
iterator.next(); // consume third entry
|
||||
expect(util.inspect(iterator)).toBe("URLSearchParams Iterator { }");
|
||||
});
|
||||
|
||||
it("should throw error when custom inspect is called incorrectly", () => {
|
||||
const sp = new URLSearchParams("?a=a&b=b");
|
||||
expect(() => sp[util.inspect.custom].call()).toThrow({
|
||||
code: "ERR_INVALID_THIS",
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle URLSearchParams with special characters", () => {
|
||||
const sp = new URLSearchParams("?key%20with%20spaces=value%20with%20spaces&special=!@%23$%25");
|
||||
const result = util.inspect(sp);
|
||||
expect(result).toContain("URLSearchParams");
|
||||
expect(result).toContain("'key with spaces' => 'value with spaces'");
|
||||
expect(result).toContain("'special' => '!@#$%'");
|
||||
});
|
||||
|
||||
it("should handle URLSearchParams with unicode characters", () => {
|
||||
const sp = new URLSearchParams("?emoji=😀&unicode=🌟");
|
||||
const result = util.inspect(sp);
|
||||
expect(result).toContain("URLSearchParams");
|
||||
expect(result).toContain("'emoji' => '😀'");
|
||||
expect(result).toContain("'unicode' => '🌟'");
|
||||
});
|
||||
|
||||
it("should handle URLSearchParams with duplicate keys", () => {
|
||||
const sp = new URLSearchParams();
|
||||
sp.append("key", "value1");
|
||||
sp.append("key", "value2");
|
||||
sp.append("key", "value3");
|
||||
const result = util.inspect(sp);
|
||||
expect(result).toContain("URLSearchParams");
|
||||
expect(result).toContain("'key' => 'value1'");
|
||||
expect(result).toContain("'key' => 'value2'");
|
||||
expect(result).toContain("'key' => 'value3'");
|
||||
});
|
||||
});
|
||||
27
test/js/node/vm/script-leak.test.ts
Normal file
27
test/js/node/vm/script-leak.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
const vm = require("vm");
|
||||
const { describe, it, expect } = require("bun:test");
|
||||
|
||||
describe("vm.Script", () => {
|
||||
it("shouldn't leak memory", () => {
|
||||
const initialUsage = process.memoryUsage.rss();
|
||||
|
||||
{
|
||||
const source = `/*\n${Buffer.alloc(10000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`;
|
||||
|
||||
function go(i) {
|
||||
const script = new vm.Script(source + "//" + i);
|
||||
script.runInThisContext();
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10000; ++i) {
|
||||
go(i);
|
||||
}
|
||||
}
|
||||
|
||||
Bun.gc(true);
|
||||
|
||||
const finalUsage = process.memoryUsage.rss();
|
||||
const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100;
|
||||
expect(megabytes).toBeLessThan(200);
|
||||
});
|
||||
});
|
||||
30
test/js/node/vm/sourcetextmodule-leak.test.ts
Normal file
30
test/js/node/vm/sourcetextmodule-leak.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
const vm = require("vm");
|
||||
const { describe, it, expect } = require("bun:test");
|
||||
|
||||
describe("vm.SourceTextModule", () => {
|
||||
it("shouldn't leak memory", async () => {
|
||||
const initialUsage = process.memoryUsage.rss();
|
||||
|
||||
{
|
||||
const source = `/*\n${Buffer.alloc(50_000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`;
|
||||
|
||||
async function go(i) {
|
||||
const mod = new vm.SourceTextModule(source + "//" + i, {
|
||||
identifier: Buffer.alloc(64, i.toString()).toString("utf8"),
|
||||
});
|
||||
await mod.link(() => {});
|
||||
await mod.evaluate();
|
||||
}
|
||||
|
||||
for (let i = 0; i < 50_000; ++i) {
|
||||
await go(i);
|
||||
}
|
||||
}
|
||||
|
||||
Bun.gc(true);
|
||||
|
||||
const finalUsage = process.memoryUsage.rss();
|
||||
const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100;
|
||||
expect(megabytes).toBeLessThan(3000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user