Compare commits

...

6 Commits

Author SHA1 Message Date
Alistair Smith
d8ac3d3903 deprecate duplex 2025-04-22 18:23:37 -07:00
Alistair Smith
60feb9feda fix: issues in node-fetch.ts & reimplement Request/Response in fetch.d.ts 2025-04-22 18:19:32 -07:00
Alistair Smith
a45e32a187 fix: mistype in Response constructor 2025-04-22 18:13:12 -07:00
Alistair Smith
1d99185b81 Fix some issues in fetch.d.ts and in bun-types 2025-04-22 18:12:17 -07:00
Alistair Smith
f44ca1069c fix some type errors in worker threads 2025-04-22 17:59:54 -07:00
Jarred Sumner
2ae4eaab97 WIP make TS happier 2025-04-23 02:25:37 +02:00
71 changed files with 1860 additions and 620 deletions

View File

@@ -19,8 +19,64 @@ declare module "bun" {
namespace __internal {
type LibOrFallbackHeaders = LibDomIsLoaded extends true ? {} : import("undici-types").Headers;
type LibOrFallbackRequest = LibDomIsLoaded extends true ? {} : import("undici-types").Request;
type LibOrFallbackResponse = LibDomIsLoaded extends true ? {} : import("undici-types").Response;
type LibOrFallbackRequest = LibDomIsLoaded extends true
? {}
: {
readonly cache: import("undici-types").RequestCache;
readonly credentials: import("undici-types").RequestCredentials;
readonly destination: import("undici-types").RequestDestination;
readonly headers: Headers;
readonly integrity: string;
readonly method: string;
readonly mode: import("undici-types").RequestMode;
readonly redirect: import("undici-types").RequestRedirect;
readonly referrer: string;
readonly referrerPolicy: import("undici-types").ReferrerPolicy;
readonly keepalive: boolean;
readonly signal: AbortSignal;
/** @deprecated Setting `.duplex` does nothing in Bun and will be removed in a future release */
readonly duplex: import("undici-types").RequestDuplex;
get url(): string;
get body(): ReadableStream | null;
get bodyUsed(): boolean;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<unknown>;
text(): Promise<string>;
clone(): Request;
};
type LibOrFallbackResponse = LibDomIsLoaded extends true
? {}
: {
readonly headers: Headers;
readonly ok: boolean;
readonly status: number;
readonly statusText: string;
readonly url: string;
readonly redirected: boolean;
get type(): import("undici-types").ResponseType;
get body(): ReadableStream | null;
get bodyUsed(): boolean;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<unknown>;
text(): Promise<string>;
clone(): Response;
};
type LibOrFallbackResponseInit = LibDomIsLoaded extends true ? {} : import("undici-types").ResponseInit;
type LibOrFallbackRequestInit = LibDomIsLoaded extends true
? {}
@@ -73,5 +129,62 @@ declare module "bun" {
interface BunResponseOverride extends LibOrFallbackResponse {
headers: BunHeadersOverride;
}
interface BunRequestConstructorOverride {
prototype: BunRequestOverride;
new (requestInfo: string | URL, init?: RequestInit): BunRequestOverride;
new (requestInfo: RequestInit & { url: string }): BunRequestOverride;
new (requestInfo: Request, init?: RequestInit): BunRequestOverride;
}
interface BunResponseConstructorOverride {
prototype: BunResponseOverride;
new (body?: Bun.BodyInit | null | undefined, init?: ResponseInit | undefined): BunResponseOverride;
/**
* Create a new {@link Response} with a JSON body
*
* @param body - The body of the response
* @param options - options to pass to the response
*
* @example
*
* ```ts
* const response = Response.json({hi: "there"});
* console.assert(
* await response.text(),
* `{"hi":"there"}`
* );
* ```
* -------
*
* This is syntactic sugar for:
* ```js
* new Response(JSON.stringify(body), {headers: { "Content-Type": "application/json" }})
* ```
* @link https://github.com/whatwg/fetch/issues/1389
*/
json(body?: any, init?: ResponseInit | number): BunResponseOverride;
/**
* Create a new {@link Response} that redirects to url
*
* @param url - the URL to redirect to
* @param status - the HTTP status code to use for the redirect
*/
redirect(url: string, status?: number): BunResponseOverride;
/**
* Create a new {@link Response} that redirects to url
*
* @param url - the URL to redirect to
* @param options - options to pass to the response
*/
redirect(url: string, init?: ResponseInit): BunResponseOverride;
/**
* Create a new {@link Response} that has a network error
*/
error(): BunResponseOverride;
}
}
}

View File

@@ -1570,7 +1570,10 @@ declare var AbortSignal: Bun.__internal.UseLibDomIfAvailable<
interface DOMException {}
declare var DOMException: Bun.__internal.UseLibDomIfAvailable<
"DOMException",
{ prototype: DOMException; new (): DOMException }
{
prototype: DOMException;
new (message?: string, name?: string): DOMException;
}
>;
interface FormData {
@@ -1730,68 +1733,10 @@ declare var Headers: Bun.__internal.UseLibDomIfAvailable<
>;
interface Request extends Bun.__internal.BunRequestOverride {}
declare var Request: Bun.__internal.UseLibDomIfAvailable<
"Request",
{
prototype: Request;
new (requestInfo: string, init?: RequestInit): Request;
new (requestInfo: RequestInit & { url: string }): Request;
new (requestInfo: Request, init?: RequestInit): Request;
}
>;
declare var Request: Bun.__internal.UseLibDomIfAvailable<"Request", Bun.__internal.BunRequestConstructorOverride>;
interface Response extends Bun.__internal.BunResponseOverride {}
declare var Response: Bun.__internal.UseLibDomIfAvailable<
"Response",
{
new (body?: Bun.BodyInit | null | undefined, init?: ResponseInit | undefined): Response;
/**
* Create a new {@link Response} with a JSON body
*
* @param body - The body of the response
* @param options - options to pass to the response
*
* @example
*
* ```ts
* const response = Response.json({hi: "there"});
* console.assert(
* await response.text(),
* `{"hi":"there"}`
* );
* ```
* -------
*
* This is syntactic sugar for:
* ```js
* new Response(JSON.stringify(body), {headers: { "Content-Type": "application/json" }})
* ```
* @link https://github.com/whatwg/fetch/issues/1389
*/
json(body?: any, init?: ResponseInit | number): Response;
/**
* Create a new {@link Response} that redirects to url
*
* @param url - the URL to redirect to
* @param status - the HTTP status code to use for the redirect
*/
redirect(url: string, status?: number): Response;
/**
* Create a new {@link Response} that redirects to url
*
* @param url - the URL to redirect to
* @param options - options to pass to the response
*/
redirect(url: string, init?: ResponseInit): Response;
/**
* Create a new {@link Response} that has a network error
*/
error(): Response;
}
>;
declare var Response: Bun.__internal.UseLibDomIfAvailable<"Response", Bun.__internal.BunResponseConstructorOverride>;
/**
* Extends Bun.TLSOptions with extra properties that are only supported in `fetch(url, {tls: ...})`

8
src/bake/bake.d.ts vendored
View File

@@ -418,8 +418,12 @@ declare module "bun" {
}
type GetParamIterator =
| AsyncIterable<Record<string, string>, GetParamsFinalOpts>
| Iterable<Record<string, string>, GetParamsFinalOpts>
| (AsyncIterable<Record<string, string>, GetParamsFinalOpts> & {
[Symbol.asyncIterator](): AsyncIterator<Record<string, string>, GetParamsFinalOpts>;
})
| (Iterable<Record<string, string>, GetParamsFinalOpts> & {
[Symbol.iterator](): Iterator<Record<string, string>, GetParamsFinalOpts>;
})
| ({ pages: Array<Record<string, string>> } & GetParamsFinalOpts);
type GetParamsFinalOpts = void | null | {

View File

@@ -102,7 +102,6 @@ The preprocessor is smart enough to not replace `$` in strings, comments, regex,
The module is then printed like:
```ts
// @ts-nocheck
$$capture_start$$(function () {
const path = __intrinsic__requireId(23);
// user code is pasted here

172
src/js/builtins.d.ts vendored
View File

@@ -52,6 +52,8 @@ declare var $constructor;
declare var $sloppy;
/** Place this directly above a function declaration (like a decorator) to always inline the function */
declare var $alwaysInline;
/** Sets the prototype of an object directly (bypassing normal prototype chain) */
declare function $setPrototypeDirect(target: any, prototype: any): void;
declare function $extractHighWaterMarkFromQueuingStrategyInit(obj: any): any;
/**
@@ -77,22 +79,37 @@ interface ReadableStreamDefaultController<R = any> extends _ReadableStreamDefaul
$error: typeof ReadableStreamDefaultController.prototype.error;
}
interface ReadableStreamDefaultReader<R = any> extends _ReadableStreamDefaultReader<R> {
$ownerReadableStream: ReadableStream<R>;
$closedPromiseCapability: any;
readMany(): ReadableStreamDefaultReadManyResult<R>;
ownerReadableStream?: ReadableStream<R>;
closedPromiseCapability?: any;
}
declare var ReadableStreamDefaultController: {
prototype: ReadableStreamDefaultController;
// @ts-ignore - This is the actual constructor signature used at runtime
new (): ReadableStreamDefaultController;
};
interface ReadableStream<R = any> extends _ReadableStream<R> {
interface ReadableStream<R = any> extends _ReadableStream<R>, Record<string, unknown> {
$highWaterMark: number;
$bunNativePtr: undefined | TODO;
$asyncContext?: {};
$asyncContext: any;
$disturbed: boolean;
$state: $streamClosed | $streamErrored | $streamReadable | $streamWritable | $streamClosedAndErrored;
$reader: ReadableStreamDefaultReader<R> | unknown;
$storedError: any;
$readableStreamController: ReadableStreamDefaultController<R> | null;
$underlyingSource: UnderlyingSource | undefined;
$start: any;
}
declare var ReadableStream: {
prototype: ReadableStream;
new (): ReadableStream;
new <R = any>(underlyingSource?: UnderlyingSource<R>, strategy?: QueuingStrategy): ReadableStream<R>;
new <R = any>(underlyingSource?: DirectUnderlyingSource<R>, strategy?: QueuingStrategy): ReadableStream<R>;
};
interface Console {
@@ -331,7 +348,11 @@ declare const $asyncContext: InternalFieldObject<[ReadonlyArray<any> | undefined
declare var $_events: TODO;
declare function $abortAlgorithm(): TODO;
declare function $abortSteps(): TODO;
declare function $addAbortAlgorithmToSignal(signal: AbortSignal, algorithm: () => void): TODO;
declare function $addAbortAlgorithmToSignal(
signal: AbortSignal,
algorithm: (reason: any) => void,
reason?: any,
): number;
declare function $addEventListener(): TODO;
declare function $appendFromJS(): TODO;
declare function $argv(): TODO;
@@ -565,7 +586,25 @@ declare function $trunc(target: number): number;
declare function $newPromiseCapability(C: PromiseConstructor): TODO;
/** @deprecated, use new TypeError instead */
declare function $makeTypeError(message: string): TypeError;
declare function $ERR_INVALID_STATE_TypeError(message: string): TypeError;
declare function $newHandledRejectedPromise(error: unknown): Promise<never>;
/**
* This function has complex signature handling with the arguments object internally
*/
declare function $pipeToShutdownWithAction(pipeState: any, action: () => Promise<any>, ...args: any[]): void;
declare function $pipeToShutdown(pipeState: any, reason?: any): void;
declare function $pipeToFinalize(pipeState: any, error?: any): void;
// Declaration for the return type of $readableStreamDefaultReaderRead
interface ReadableStreamReadResult<T> {
done: boolean;
value: T | undefined;
}
// Missing type declarations for stream types
declare const $ReadableStream: any;
declare const $ReadableStreamDefaultReader: any;
declare function $readableStreamDefaultReaderRead(reader: any): Promise<ReadableStreamReadResult<any>>;
declare const __internal: unique symbol;
interface InternalFieldObject<T extends any[]> {
@@ -590,39 +629,134 @@ declare interface Promise<T> extends ClassWithIntrinsics<Promise<T>> {}
declare interface ArrayBufferConstructor<T> extends ClassWithIntrinsics<ArrayBufferConstructor<T>> {}
declare interface PromiseConstructor<T> extends ClassWithIntrinsics<PromiseConstructor<T>> {}
declare interface UnderlyingSource {
declare interface UnderlyingSource<R = any> {
$lazy?: boolean;
$bunNativePtr?: undefined | TODO;
autoAllocateChunkSize?: number;
$stream?: ReadableStream;
$stream?: ReadableStream<R>;
type?: string;
$data?: any;
start?: (controller: ReadableStreamDefaultController<R>) => void | Promise<void>;
pull?: (controller: ReadableStreamDefaultController<R>) => void | Promise<void>;
cancel?: (reason: any) => void | Promise<void>;
}
type QueuingStrategyHighWaterMark = number;
type QueuingStrategySize = (chunk: any) => number;
interface QueuingStrategy {
highWaterMark?: number;
size?: QueuingStrategySize;
}
interface DirectUnderlyingSource<R = any> extends UnderlyingSource<R> {
type: "direct";
}
declare class OutOfMemoryError {
constructor();
}
declare class ReadableStreamDefaultController {
declare class ReadableStreamDefaultController<R = any> {
constructor(
stream: unknown,
underlyingSource: unknown,
size: unknown,
highWaterMark: unknown,
$isReadableStream: typeof $isReadableStream,
stream: ReadableStream<R>,
underlyingSource: UnderlyingSource<R>,
size: ((chunk: R) => number) | undefined,
highWaterMark: number,
isReadableStream: typeof $isReadableStream,
);
desiredSize: number | null;
close(): void;
enqueue(chunk?: any): void;
error(e?: any): void;
// Internal properties
$controlledReadableStream: ReadableStream<R>;
$pullAgain: boolean;
$pulling: boolean;
$started: number;
$closeRequested: boolean;
$queue: any[];
$pullAlgorithm: () => Promise<void>;
$cancelAlgorithm: (reason: any) => Promise<void>;
$strategy: {
highWaterMark: number;
size?: (chunk: R) => number;
};
}
declare class ReadableByteStreamController {
constructor(
stream: unknown,
underlyingSource: unknown,
strategy: unknown,
$isReadableStream: typeof $isReadableStream,
stream: ReadableStream<Uint8Array>,
underlyingSource: UnderlyingSource<Uint8Array>,
highWaterMark: number,
isReadableStream: typeof $isReadableStream,
);
byobRequest: ReadableStreamBYOBRequest | null;
desiredSize: number | null;
close(): void;
enqueue(chunk: ArrayBufferView): void;
error(e?: any): void;
// Internal properties
$controlledReadableStream: ReadableStream<Uint8Array>;
$pullAgain: boolean;
$pulling: boolean;
$started: number;
$closeRequested: boolean;
$strategy: QueuingStrategy;
$pendingPullIntos: any[];
$queue: any[];
}
declare class ReadableStreamBYOBRequest {
constructor(stream: unknown, view: unknown, $isReadableStream: typeof $isReadableStream);
constructor(controller: ReadableByteStreamController, view: ArrayBufferView);
view: ArrayBufferView;
respond(bytesWritten: number): void;
respondWithNewView(view: ArrayBufferView): void;
// Internal properties
$associatedReadableByteStreamController: ReadableByteStreamController;
}
/**
* ReadableStreamBYOBReader is initialized through initializeReadableStreamBYOBReader
* which gets the stream parameter and sets up the reader.
*/
declare class ReadableStreamBYOBReader {
constructor(stream: unknown);
constructor();
read(view: ArrayBufferView): Promise<ReadableStreamBYOBReadResult>;
releaseLock(): void;
readonly closed: Promise<undefined>;
cancel(reason?: any): Promise<void>;
// Internal properties
ownerReadableStream?: ReadableStream;
closedPromiseCapability?: any;
readIntoRequests?: any;
}
declare var ReadableStreamBYOBReader: {
prototype: ReadableStreamBYOBReader;
new (stream: ReadableStream): ReadableStreamBYOBReader;
};
declare var ReadableByteStreamController: {
prototype: ReadableByteStreamController;
// @ts-ignore - This is the actual constructor signature used at runtime
new (): ReadableByteStreamController;
};
interface ReadableStreamBYOBReadResult {
done: boolean;
value: ArrayBufferView;
}
interface ReadableStreamDefaultReadManyResult<T> {
done: boolean;
value: Array<T>;
size?: number;
}
// Inlining our enum types
@@ -650,7 +784,7 @@ interface String {
declare var $Buffer: {
new (array: Array): Buffer;
new (arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;
new (arrayBuffer: ArrayBufferLike | ArrayBufferView, byteOffset?: number, length?: number): Buffer;
new (buffer: Buffer): Buffer;
new (size: number): Buffer;
new (string: string, encoding?: BufferEncoding): Buffer;

View File

@@ -1,5 +1,6 @@
//! JS code for bake
/// <reference path="../../bake/bake.d.ts" />
/// <reference path="../builtins.d.ts" />
import type { Bake } from "bun";
type FrameworkPrerender = Bake.ServerEntryPoint["prerender"];
@@ -7,6 +8,13 @@ type FrameworkGetParams = Bake.ServerEntryPoint["getParams"];
type TypeAndFlags = number;
type FileIndex = number;
// Define the GetParamIterator interface to satisfy TypeScript
interface GetParamIterator {
[Symbol.asyncIterator]?(): AsyncIterator<Record<string, string>>;
[Symbol.iterator]?(): Iterator<Record<string, string>>;
pages?: Array<Record<string, string>>;
}
/**
* This layer is implemented in JavaScript to reduce Native <-> JS context switches,
* as well as use the async primitives provided by the language.
@@ -25,7 +33,7 @@ export function renderRoutesForProdStatic(
sourceRouteFiles: string[],
paramInformation: Array<null | string[]>,
styles: string[][],
): Promise<void> {
): Promise<void[]> {
$debug({
outBase,
allServerFiles,
@@ -124,24 +132,34 @@ export function renderRoutesForProdStatic(
if (paramInformation[i] != null) {
const getParam = getParams[type];
$assert(getParam != null && $isCallable(getParam));
const paramGetter: Bake.GetParamIterator = await getParam({
const paramGetter: GetParamIterator = await getParam({
pageModule,
layouts,
});
if (paramGetter[Symbol.asyncIterator] != undefined) {
for await (const params of paramGetter) {
for await (const params of paramGetter as AsyncIterable<Record<string, string>>) {
callRouteGenerator(type, i, layouts, pageModule, params);
}
} else if (paramGetter[Symbol.iterator] != undefined) {
for (const params of paramGetter) {
callRouteGenerator(type, i, layouts, pageModule, params);
// Cast to any to avoid TS2802 error with for...of and Iterable
const iteratorFn = paramGetter[Symbol.iterator];
if (iteratorFn) {
const iterator = iteratorFn.call(paramGetter);
let result = iterator.next();
while (!result.done) {
const params = result.value as Record<string, string>;
callRouteGenerator(type, i, layouts, pageModule, params);
result = iterator.next();
}
}
} else {
} else if (paramGetter.pages != undefined) {
await Promise.all(
paramGetter.pages.map(params => {
callRouteGenerator(type, i, layouts, pageModule, params);
}),
);
} else {
throw new Error(`Invalid GetParamIterator result from route ${JSON.stringify(sourceRouteFiles[i])}`);
}
} else {
await doGenerateRoute(type, i, layouts, pageModule, null);

View File

@@ -22,17 +22,25 @@ interface BundlerPlugin {
promises: Array<Promise<any>> | undefined;
onBeforeParse: (filter: RegExp, namespace: string, addon: unknown, symbol: string, external?: unknown) => void;
$napiDlopenHandle: number;
}
// Extra types
type Setup = BunPlugin["setup"];
type MinifyObj = Exclude<BuildConfig["minify"], boolean>;
interface BuildConfigExt extends BuildConfig {
// we support esbuild-style 'entryPoints' capitalization
entryPoints?: string[];
// plugins is guaranteed to not be null
// Create a partial type rather than extending BuildConfig to avoid interface extension issues
interface BuildConfigExt {
// Base properties from BuildConfig
entrypoints?: string[];
plugins: BunPlugin[];
target?: "browser" | "bun" | "node";
root?: string;
minify?: boolean | MinifyObj;
// esbuild-style 'entryPoints' capitalization
entryPoints?: string[];
// experimental CSS support
experimentalCss?: boolean;
// experimental HTML support
experimentalHtml?: boolean;
}
interface PluginBuilderExt extends PluginBuilder {
resolve: AnyFunction;
@@ -60,6 +68,8 @@ export function loadAndResolvePluginsForServe(
experimentalHtml: true,
target: "browser",
root: bunfig_folder,
plugins: [],
entrypoints: [],
};
class InvalidBundlerPluginError extends TypeError {
@@ -138,7 +148,11 @@ export function runSetupFunction(
if (map === onBeforeParsePlugins) {
isOnBeforeParse = true;
// TODO: how to check if it a napi module here?
if (!callback || !$isObject(callback) || !callback.$napiDlopenHandle) {
if (
!callback ||
!$isObject(callback) ||
!(callback as unknown as { $napiDlopenHandle?: unknown }).$napiDlopenHandle
) {
throw new TypeError(
"onBeforeParse `napiModule` must be a Napi module which exports the `BUN_PLUGIN_NAME` symbol.",
);
@@ -287,7 +301,7 @@ export function runSetupFunction(
return this.promises;
};
var setupResult = setup({
const pluginBuilder = {
config: config,
onDispose: notImplementedIssueFn(2771, "On-dispose callbacks"),
onEnd: notImplementedIssueFn(2771, "On-end callbacks"),
@@ -315,7 +329,9 @@ export function runSetupFunction(
platform: config.target === "bun" ? "node" : config.target,
},
esbuild: {},
} as PluginBuilderExt);
} as unknown as PluginBuilderExt;
var setupResult = setup(pluginBuilder);
if (setupResult && $isPromise(setupResult)) {
if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) {
@@ -378,7 +394,12 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
continue;
}
var { path, namespace: userNamespace = inputNamespace, external } = result;
// Cast to a known type to avoid property access errors
var {
path,
namespace: userNamespace = inputNamespace,
external,
} = result as { path: string; namespace?: string; external?: boolean };
if (!(typeof path === "string") || !(typeof userNamespace === "string")) {
throw new TypeError("onResolve plugins must return an object with a string 'path' and string 'loader' field");
}

View File

@@ -15,6 +15,22 @@ export function require(this: JSCommonJSModule, _: string) {
// overridableRequire can be overridden by setting `Module.prototype.require`
$overriddenName = "require";
$visibility = "Private";
// Type assertion for Loader
declare global {
interface LoaderModule {
dependenciesMap: Map<string, LoaderEntry>;
}
// Define the properties we're accessing on Loader
interface LoaderExtended {
getModuleNamespaceObject: (module: any) => any;
parseModule: (key: string, sourceCodeObject: JSCSourceCodeObject) => any;
requestedModules: (mod: any) => string[];
ensureRegistered: (key: string) => any;
linkAndEvaluateModule: (specifier: string, options?: any) => any;
}
}
export function overridableRequire(this: JSCommonJSModule, originalId: string, options: { paths?: string[] } = {}) {
const id = $resolveSync(originalId, this.filename, false, false, options ? options.paths : undefined);
if (id.startsWith("node:")) {
@@ -115,7 +131,7 @@ export function overridableRequire(this: JSCommonJSModule, originalId: string, o
// If we can pull out a ModuleNamespaceObject, let's do it.
if (esm?.evaluated && (esm.state ?? 0) >= $ModuleReady) {
const namespace = Loader.getModuleNamespaceObject(esm!.module);
const namespace = (Loader as unknown as LoaderExtended).getModuleNamespaceObject(esm!.module);
// In Bun, when __esModule is not defined, it's a CustomAccessor on the prototype.
// Various libraries expect __esModule to be set when using ESM from require().
// We don't want to always inject the __esModule export into every module,
@@ -198,7 +214,8 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) {
// - we've never fetched it
// - a fetch is in progress
(!$isPromise(fetch) ||
($getPromiseInternalField(fetch, $promiseFieldFlags) & $promiseStateMask) === $promiseStatePending))
($getPromiseInternalField(fetch as Promise<symbol>, $promiseFieldFlags) & $promiseStateMask) ===
$promiseStatePending))
) {
// force it to be no longer pending
$fulfillModuleSync(key);
@@ -214,14 +231,20 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) {
if (state < $ModuleLink && $isPromise(fetch)) {
// This will probably never happen, but just in case
if (($getPromiseInternalField(fetch, $promiseFieldFlags) & $promiseStateMask) === $promiseStatePending) {
if (
($getPromiseInternalField(fetch as Promise<symbol>, $promiseFieldFlags) & $promiseStateMask) ===
$promiseStatePending
) {
throw new TypeError(`require() async module "${key}" is unsupported. use "await import()" instead.`);
}
// this pulls it out of the promise without delaying by a tick
// the promise is already fulfilled by $fulfillModuleSync
const sourceCodeObject = $getPromiseInternalField(fetch, $promiseFieldReactionsOrResult);
moduleRecordPromise = loader.parseModule(key, sourceCodeObject);
const sourceCodeObject = $getPromiseInternalField(
fetch as Promise<symbol>,
$promiseFieldReactionsOrResult,
) as JSCSourceCodeObject;
moduleRecordPromise = (loader as unknown as LoaderExtended).parseModule(key, sourceCodeObject);
}
let mod = entry?.module;
@@ -251,14 +274,14 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) {
// This is very similar to "requestInstantiate" in ModuleLoader.js in JavaScriptCore.
$setStateToMax(entry, $ModuleLink);
const dependenciesMap = mod.dependenciesMap;
const requestedModules = loader.requestedModules(mod);
const requestedModules = (loader as unknown as LoaderExtended).requestedModules(mod);
const dependencies = $newArrayWithSize<string>(requestedModules.length);
for (var i = 0, length = requestedModules.length; i < length; ++i) {
const depName = requestedModules[i];
// optimization: if it starts with a slash then it's an absolute path
// we don't need to run the resolver a 2nd time
const depKey = depName[0] === "/" ? depName : loader.resolve(depName, key);
const depEntry = loader.ensureRegistered(depKey);
const depEntry = (loader as unknown as LoaderExtended).ensureRegistered(depKey);
if (depEntry.state < $ModuleLink) {
queue.push(depKey);
@@ -274,13 +297,13 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) {
entry.satisfy = Promise.$resolve(entry);
entry.isSatisfied = true;
key = queue.shift();
key = queue.shift() as string;
while (key && (registry.$get(key)?.state ?? $ModuleFetch) >= $ModuleLink) {
key = queue.shift();
key = queue.shift() as string;
}
}
var linkAndEvaluateResult = loader.linkAndEvaluateModule(resolvedSpecifier, undefined);
var linkAndEvaluateResult = (loader as unknown as LoaderExtended).linkAndEvaluateModule(resolvedSpecifier, undefined);
if (linkAndEvaluateResult && $isPromise(linkAndEvaluateResult)) {
// if you use top-level await, or any dependencies use top-level await, then we throw here
// this means the module will still actually load eventually, but that's okay.
@@ -303,7 +326,7 @@ export function requireESM(this, resolved: string) {
if (!entry || !entry.evaluated || !entry.module) {
throw new TypeError(`require() failed to evaluate module "${resolved}". This is an internal consistentency error.`);
}
var exports = Loader.getModuleNamespaceObject(entry.module);
var exports = (Loader as unknown as LoaderExtended).getModuleNamespaceObject(entry.module);
return exports;
}
@@ -322,7 +345,7 @@ export function requireESMFromHijackedExtension(this: JSCommonJSModule, id: stri
// If we can pull out a ModuleNamespaceObject, let's do it.
if (esm?.evaluated && (esm.state ?? 0) >= $ModuleReady) {
const namespace = Loader.getModuleNamespaceObject(esm!.module);
const namespace = (Loader as unknown as LoaderExtended).getModuleNamespaceObject(esm!.module);
// In Bun, when __esModule is not defined, it's a CustomAccessor on the prototype.
// Various libraries expect __esModule to be set when using ESM from require().
// We don't want to always inject the __esModule export into every module,
@@ -358,7 +381,7 @@ export function createRequireCache() {
const esm = Loader.registry.$get(key);
if (esm?.evaluated) {
const namespace = Loader.getModuleNamespaceObject(esm.module);
const namespace = (Loader as unknown as LoaderExtended).getModuleNamespaceObject(esm.module);
const mod = $createCommonJSModule(key, namespace, true, undefined);
$requireMap.$set(key, mod);
return mod;
@@ -376,9 +399,9 @@ export function createRequireCache() {
},
deleteProperty(_target, key: string) {
moduleMap.$delete(key);
$requireMap.$delete(key);
Loader.registry.$delete(key);
moduleMap.delete(key);
$requireMap.delete(key);
Loader.registry.delete(key);
return true;
},

View File

@@ -55,11 +55,19 @@ export function asyncIterator(this: Console) {
}
while (true) {
const firstResult = reader.readMany();
// Cast reader to include readMany method
const typedReader = reader as ReadableStreamDefaultReader<Uint8Array> & {
readMany(): Promise<{ done: boolean; value: any[] }> | { done: boolean; value: any[] };
};
const firstResult = typedReader.readMany();
if ($isPromise(firstResult)) {
({ done, value } = await firstResult);
const result = await (firstResult as unknown as Promise<{ done: boolean; value: any[] }>);
done = result.done;
value = result.value;
} else {
({ done, value } = firstResult);
const result = firstResult as { done: boolean; value: any[] };
done = result.done;
value = result.value;
}
if (done) {
@@ -130,7 +138,7 @@ export function write(this: Console, input) {
wrote += writer.write(arguments[i]);
}
writer.flush(true);
writer.flush();
return wrote;
}
@@ -462,8 +470,8 @@ export function createConsoleConstructor(console: typeof globalThis.console) {
if (
e != null &&
typeof e === "object" &&
e.name === "RangeError" &&
e.message === "Maximum call stack size exceeded."
(e as Error).name === "RangeError" &&
(e as Error).message === "Maximum call stack size exceeded."
)
throw e;
// Sorry, there's no proper way to pass along the error here.
@@ -580,10 +588,10 @@ export function createConsoleConstructor(console: typeof globalThis.console) {
},
trace: function trace(...args) {
const err: Error = {
const err = {
name: "Trace",
message: this[kFormatForStderr](args),
};
} as Error;
Error.captureStackTrace(err, trace);
this.error(err.stack);
},
@@ -680,9 +688,19 @@ export function createConsoleConstructor(console: typeof globalThis.console) {
const values = [];
let length = 0;
if (mapIter) {
for (; i < tabularData.length / 2; ++i) {
ArrayPrototypePush.$call(keys, _inspect(tabularData[i * 2]));
ArrayPrototypePush.$call(values, _inspect(tabularData[i * 2 + 1]));
// For iterators, collect entries manually
const tabularArray: any[] = [];
// Cast to any iterable type and collect entries
const iterator = (tabularData as any)[Symbol.iterator]();
let entry = iterator.next();
while (!entry.done) {
tabularArray.push(entry.value);
entry = iterator.next();
}
for (; i < tabularArray.length / 2; ++i) {
ArrayPrototypePush.$call(keys, _inspect(tabularArray[i * 2]));
ArrayPrototypePush.$call(values, _inspect(tabularArray[i * 2 + 1]));
length++;
}
} else {

View File

@@ -6,7 +6,7 @@ export function from(value, encodingOrOffset, length) {
if (typeof value === "object" && value !== null) {
if ($inheritsArrayBuffer(value)) return new $Buffer(value, encodingOrOffset, length);
if ($isTypedArrayView(value)) return new $Buffer(value, encodingOrOffset, length);
if ($isTypedArrayView(value)) return new $Buffer(value as ArrayBufferView, encodingOrOffset, length);
const valueOf = value.valueOf && value.valueOf();
if (valueOf != null && valueOf !== value && (typeof valueOf === "string" || typeof valueOf === "object")) {

View File

@@ -3,6 +3,7 @@
interface BufferExt extends Buffer {
$dataView?: DataView;
buffer: ArrayBufferLike; // Override inherited buffer property to handle SharedArrayBuffer case
toString(encoding?: BufferEncoding, start?: number, end?: number): string;
toString(offset: number, length: number, encoding?: BufferEncoding): string;
@@ -683,7 +684,8 @@ export function slice(this: BufferExt, start, end) {
var start_ = adjustOffset(start, byteLength);
var end_ = end !== undefined ? adjustOffset(end, byteLength) : byteLength;
return new $Buffer(buffer, byteOffset + start_, end_ > start_ ? end_ - start_ : 0);
// Use ArrayBufferLike here to handle both ArrayBuffer and SharedArrayBuffer
return new $Buffer(buffer as ArrayBufferLike, byteOffset + start_, end_ > start_ ? end_ - start_ : 0);
}
$getter;

View File

@@ -1,8 +1,9 @@
export function peek(promise: unknown): unknown {
$assert($promiseStatePending == 0);
return $isPromise(promise) && $getPromiseInternalField(promise, $promiseFieldFlags) & $promiseStateMask
? $getPromiseInternalField(promise, $promiseFieldReactionsOrResult)
return $isPromise(promise) &&
$getPromiseInternalField(promise as Promise<unknown>, $promiseFieldFlags) & $promiseStateMask
? $getPromiseInternalField(promise as Promise<unknown>, $promiseFieldReactionsOrResult)
: promise;
}
@@ -13,7 +14,7 @@ export function peekStatus(promise: unknown): string {
return ["pending", "fulfilled", "rejected"][
$isPromise(promise) //
? $getPromiseInternalField(promise, $promiseFieldFlags) & $promiseStateMask
? $getPromiseInternalField(promise as Promise<unknown>, $promiseFieldFlags) & $promiseStateMask
: 1
];
}

View File

@@ -76,8 +76,8 @@ export function getStdioWriteStream(fd, isTTY: boolean, _fdType: BunProcessStdin
export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType) {
const native = Bun.stdin.stream();
// @ts-expect-error
const source = native.$bunNativePtr;
// This is a Bun-specific internal property
const source = (native as any).$bunNativePtr;
var reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
@@ -86,7 +86,9 @@ export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType
function ref() {
$debug("ref();", reader ? "already has reader" : "getting reader");
reader ??= native.getReader();
reader ??= native.getReader() as ReadableStreamDefaultReader<Uint8Array> & {
readMany(): Promise<{ done: boolean; value: any[] }>;
};
source.updateRef(true);
shouldUnref = false;
if (needsInternalReadRefresh) {
@@ -192,14 +194,15 @@ export function getStdinStream(fd, isTTY: boolean, fdType: BunProcessStdinFdType
}
}
} catch (err) {
if (err?.code === "ERR_STREAM_RELEASE_LOCK") {
const error = err as Error;
if (error?.code === "ERR_STREAM_RELEASE_LOCK") {
// The stream was unref()ed. It may be ref()ed again in the future,
// or maybe it has already been ref()ed again and we just need to
// restart the internalRead() function. triggerRead() will figure that out.
triggerRead.$call(stream, undefined);
return;
}
stream.destroy(err);
stream.destroy(error);
}
}
@@ -398,7 +401,8 @@ export function windowsEnv(
envMapList.splice(i, 1);
}
editWindowsEnvVar(k, null);
return typeof p !== "symbol" ? delete internalEnv[k] : false;
const result = typeof p !== "symbol" ? delete internalEnv[k] : false;
return result;
},
defineProperty(_, p, attributes) {
const k = String(p).toUpperCase();
@@ -407,7 +411,8 @@ export function windowsEnv(
envMapList.push(p);
}
editWindowsEnvVar(k, internalEnv[k]);
return $Object.$defineProperty(internalEnv, k, attributes);
$Object.$defineProperty(internalEnv, k, attributes);
return true;
},
getOwnPropertyDescriptor(target, p) {
return typeof p === "string" ? Reflect.getOwnPropertyDescriptor(target, p.toUpperCase()) : undefined;

View File

@@ -78,7 +78,7 @@ export function byobRequest(this) {
firstDescriptor.byteOffset + firstDescriptor.bytesFilled,
firstDescriptor.byteLength - firstDescriptor.bytesFilled,
);
$putByIdDirectPrivate(this, "byobRequest", new ReadableStreamBYOBRequest(this, view, $isReadableStream));
$putByIdDirectPrivate(this, "byobRequest", new (ReadableStreamBYOBRequest as any)(this, view, $isReadableStream));
}
}

View File

@@ -147,6 +147,16 @@ export function readableByteStreamControllerClose(controller) {
$readableStreamCloseIfPossible($getByIdDirectPrivate(controller, "controlledReadableStream"));
}
// Define Dequeue interface based on usage in the file
interface Dequeue<T> {
peek(): T | undefined;
push(item: T): void;
shift(): T | undefined;
clear(): void;
isEmpty(): boolean;
isNotEmpty(): boolean;
}
export function readableByteStreamControllerClearPendingPullIntos(controller) {
$readableByteStreamControllerInvalidateBYOBRequest(controller);
var existing: Dequeue<PullIntoDescriptor> = $getByIdDirectPrivate(controller, "pendingPullIntos");
@@ -227,24 +237,37 @@ export function readableByteStreamControllerPull(controller) {
}
export function readableByteStreamControllerShouldCallPull(controller) {
$assert(controller);
const stream = $getByIdDirectPrivate(controller, "controlledReadableStream");
// If controller is null or undefined, we can't call pull
if (!controller) return false;
// From this point forward, controller is non-null
// Create a non-null version of controller to satisfy TypeScript
const controllerNonNull = controller as NonNullable<typeof controller>;
$assert(controllerNonNull);
const stream = $getByIdDirectPrivate(controllerNonNull, "controlledReadableStream");
if (!stream) {
return false;
}
if ($getByIdDirectPrivate(stream, "state") !== $streamReadable) return false;
if ($getByIdDirectPrivate(controller, "closeRequested")) return false;
if (!($getByIdDirectPrivate(controller, "started") > 0)) return false;
if ($getByIdDirectPrivate(controllerNonNull, "closeRequested")) return false;
if (!($getByIdDirectPrivate(controllerNonNull, "started") > 0)) return false;
const reader = $getByIdDirectPrivate(stream, "reader");
if (reader && ($getByIdDirectPrivate(reader, "readRequests")?.isNotEmpty() || !!reader.$bunNativePtr)) return true;
// Using a non-null assertion is appropriate here since we've already checked reader is truthy
if (
reader &&
$readableStreamHasBYOBReader(stream) &&
$getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readIntoRequests")?.isNotEmpty()
$getByIdDirectPrivate(reader!, "readIntoRequests")?.isNotEmpty()
)
return true;
if ($readableByteStreamControllerGetDesiredSize(controller) > 0) return true;
// TypeScript can't tell that controller is non-null here
// @ts-ignore - We've already checked that controller is non-null at the top of the function
if ($readableByteStreamControllerGetDesiredSize(controllerNonNull) > 0) return true;
return false;
}

View File

@@ -9,7 +9,7 @@
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* documentation and/or other materials provided without restriction.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -37,18 +37,23 @@ export function initializeReadableStream(
if (strategy !== undefined && !$isObject(strategy))
throw new TypeError("ReadableStream constructor takes an object as second argument, if any");
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "state", $streamReadable);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "reader", undefined);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "storedError", undefined);
this.$disturbed = false;
// Initialized with null value to enable distinction with undefined case.
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "readableStreamController", null);
this.$bunNativePtr = $getByIdDirectPrivate(underlyingSource, "bunNativePtr") ?? undefined;
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "asyncContext", $getInternalField($asyncContext, 0));
const isDirect = underlyingSource.type === "direct";
@@ -62,7 +67,9 @@ export function initializeReadableStream(
if (!isLazy && (pullFn = $getByIdDirectPrivate(underlyingSource, "pull")) !== undefined) {
const size = $getByIdDirectPrivate(strategy, "size");
const highWaterMark = $getByIdDirectPrivate(strategy, "highWaterMark");
$putByIdDirectPrivate(this, "highWaterMark", highWaterMark);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "highWaterMark", highWaterMark as number);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "underlyingSource", undefined);
$setupReadableStreamDefaultController(
this,
@@ -77,19 +84,27 @@ export function initializeReadableStream(
return this;
}
if (isDirect) {
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "underlyingSource", underlyingSource);
$putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark"));
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark") as number);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "start", () => $createReadableStreamController(this, underlyingSource, strategy));
} else if (isLazy) {
const autoAllocateChunkSize = underlyingSource.autoAllocateChunkSize;
$putByIdDirectPrivate(this, "highWaterMark", undefined);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "highWaterMark", undefined as unknown as number);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "underlyingSource", undefined);
// @ts-ignore - We know these private properties exist at runtime
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(
this,
"highWaterMark",
autoAllocateChunkSize || $getByIdDirectPrivate(strategy, "highWaterMark"),
(autoAllocateChunkSize || $getByIdDirectPrivate(strategy, "highWaterMark")) as number,
);
// @ts-ignore - We know these private properties exist at runtime
$putByIdDirectPrivate(this, "start", () => {
const instance = $lazyLoadStream(this, autoAllocateChunkSize);
if (instance) {
@@ -98,7 +113,7 @@ export function initializeReadableStream(
});
} else {
$putByIdDirectPrivate(this, "underlyingSource", undefined);
$putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark"));
$putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark") as number);
$putByIdDirectPrivate(this, "start", undefined);
$createReadableStreamController(this, underlyingSource, strategy);
}
@@ -112,7 +127,7 @@ export function readableStreamToArray(stream: ReadableStream): Promise<unknown[]
// this is a direct stream
var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource");
if (underlyingSource !== undefined) {
return $readableStreamToArrayDirect(stream, underlyingSource);
return ($readableStreamToArrayDirect as any)(stream, underlyingSource);
}
if ($isReadableStreamLocked(stream)) return Promise.$reject($ERR_INVALID_STATE_TypeError("ReadableStream is locked"));
return $readableStreamIntoArray(stream);
@@ -167,9 +182,10 @@ export function readableStreamToArrayBuffer(stream: ReadableStream<ArrayBuffer>)
}
if (ArrayBuffer.isView(view)) {
const buffer = view.buffer;
const byteOffset = view.byteOffset;
const byteLength = view.byteLength;
const typedView = view as ArrayBufferView;
const buffer = typedView.buffer;
const byteOffset = typedView.byteOffset;
const byteLength = typedView.byteLength;
if (byteOffset === 0 && byteLength === buffer.byteLength) {
return buffer;
}
@@ -180,6 +196,7 @@ export function readableStreamToArrayBuffer(stream: ReadableStream<ArrayBuffer>)
if (typeof view === "string") {
return new TextEncoder().encode(view);
}
break;
}
default: {
let anyStrings = false;
@@ -191,14 +208,17 @@ export function readableStreamToArrayBuffer(stream: ReadableStream<ArrayBuffer>)
}
if (!anyStrings) {
return Bun.concatArrayBuffers(result, false);
return Bun.concatArrayBuffers(
result as unknown as (ArrayBufferLike | Bun.ArrayBufferView<ArrayBufferLike>)[],
0,
);
}
const sink = new Bun.ArrayBufferSink();
sink.start();
for (const chunk of result) {
sink.write(chunk);
sink.write(chunk as string | ArrayBuffer | SharedArrayBuffer | Bun.ArrayBufferView);
}
return sink.end() as Uint8Array;
@@ -211,7 +231,7 @@ export function readableStreamToArrayBuffer(stream: ReadableStream<ArrayBuffer>)
if (completedResult !== result) {
result = completedResult;
} else {
return result.then(toArrayBuffer);
return result.then((value: unknown[]) => toArrayBuffer(value));
}
}
return $createFulfilledPromise(toArrayBuffer(result));
@@ -248,16 +268,18 @@ export function readableStreamToBytes(stream: ReadableStream<ArrayBuffer>): Prom
}
if (ArrayBuffer.isView(view)) {
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
const typedView = view as ArrayBufferView;
return new Uint8Array(typedView.buffer, typedView.byteOffset, typedView.byteLength);
}
if (view instanceof ArrayBuffer || view instanceof SharedArrayBuffer) {
return new Uint8Array(view);
return new Uint8Array(view as ArrayBuffer);
}
if (typeof view === "string") {
return new TextEncoder().encode(view);
}
break;
}
default: {
let anyStrings = false;
@@ -269,14 +291,17 @@ export function readableStreamToBytes(stream: ReadableStream<ArrayBuffer>): Prom
}
if (!anyStrings) {
return Bun.concatArrayBuffers(result, true);
return Bun.concatArrayBuffers(
result as unknown as (ArrayBufferLike | Bun.ArrayBufferView<ArrayBufferLike>)[],
1,
);
}
const sink = new Bun.ArrayBufferSink();
sink.start({ asUint8Array: true });
for (const chunk of result) {
sink.write(chunk);
sink.write(chunk as string | ArrayBuffer | SharedArrayBuffer | Bun.ArrayBufferView);
}
return sink.end() as Uint8Array;
@@ -289,7 +314,7 @@ export function readableStreamToBytes(stream: ReadableStream<ArrayBuffer>): Prom
if (completedResult !== result) {
result = completedResult;
} else {
return result.then(toBytes);
return result.then((value: unknown[]) => toBytes(value));
}
}
@@ -304,7 +329,7 @@ export function readableStreamToFormData(
if (!$isReadableStream(stream)) throw $ERR_INVALID_ARG_TYPE("stream", "ReadableStream", typeof stream);
if ($isReadableStreamLocked(stream)) return Promise.$reject($ERR_INVALID_STATE_TypeError("ReadableStream is locked"));
return Bun.readableStreamToBlob(stream).then(blob => {
return FormData.from(blob, contentType);
return (FormData as any).from(blob, contentType);
});
}
@@ -321,7 +346,7 @@ export function readableStreamToJSON(stream: ReadableStream): unknown {
const peeked = Bun.peek(text);
if (peeked !== text) {
try {
return $createFulfilledPromise(globalThis.JSON.parse(peeked));
return $createFulfilledPromise(globalThis.JSON.parse(peeked as string));
} catch (e) {
return Promise.reject(e);
}
@@ -362,7 +387,9 @@ export function createUsedReadableStream() {
$linkTimeConstant;
export function createNativeReadableStream(nativePtr, autoAllocateChunkSize) {
$assert(nativePtr, "nativePtr must be a valid pointer");
return new ReadableStream({
// We need to use a type assertion here since there seems to be a Bun.UnderlyingSource
// that isn't the same as our UnderlyingSource
return new (ReadableStream as any)({
$lazy: true,
$bunNativePtr: nativePtr,
autoAllocateChunkSize: autoAllocateChunkSize,
@@ -392,7 +419,8 @@ export function getReader(this, options) {
}
// String conversion is required by spec, hence double equals.
if (mode == "byob") {
return new ReadableStreamBYOBReader(this);
// Need to handle this differently since constructor doesn't take arguments
return new (ReadableStreamBYOBReader as any)(this);
}
throw $ERR_INVALID_ARG_VALUE("mode", mode, "byob");

View File

@@ -50,20 +50,21 @@ export function readMany(this: ReadableStreamDefaultReader): ReadableStreamDefau
if (!stream) throw new TypeError("readMany() called on a reader owned by no readable stream");
const state = $getByIdDirectPrivate(stream, "state");
stream.$disturbed = true;
(stream as ReadableStream).$disturbed = true;
if (state === $streamErrored) {
throw $getByIdDirectPrivate(stream, "storedError");
}
var controller = $getByIdDirectPrivate(stream, "readableStreamController");
var controller = $getByIdDirectPrivate(stream, "readableStreamController") as ReadableStreamDefaultController;
var queue: any;
if (controller) {
var queue = $getByIdDirectPrivate(controller, "queue");
queue = $getByIdDirectPrivate(controller, "queue");
}
if (!queue && state !== $streamClosed) {
// This is a ReadableStream direct controller implemented in JS
// It hasn't been started yet.
return controller.$pull(controller).$then(function ({ done, value }) {
return (controller as any).$pull(controller).$then(function ({ done, value }) {
return done ? { done: true, value: value ? [value] : [], size: 0 } : { value: [value], size: 1, done: false };
});
} else if (!queue) {
@@ -123,7 +124,7 @@ export function readMany(this: ReadableStreamDefaultReader): ReadableStreamDefau
if (result.done) {
return { value: resultValue ? [resultValue] : [], size: 0, done: true };
}
var controller = $getByIdDirectPrivate(stream, "readableStreamController");
var controller = $getByIdDirectPrivate(stream, "readableStreamController") as ReadableStreamDefaultController;
var queue = $getByIdDirectPrivate(controller, "queue");
var value = [resultValue].concat(queue.content.toArray(false));
@@ -145,7 +146,7 @@ export function readMany(this: ReadableStreamDefaultReader): ReadableStreamDefau
var size = queue.size;
if ($getByIdDirectPrivate(controller, "closeRequested")) {
$readableStreamCloseIfPossible($getByIdDirectPrivate(controller, "controlledReadableStream"));
$readableStreamCloseIfPossible($getByIdDirectPrivate(controller, "controlledReadableStream") as ReadableStream);
} else if ($isReadableStreamDefaultController(controller)) {
$readableStreamDefaultControllerCallPullIfNeeded(controller);
} else if ($isReadableByteStreamController(controller)) {
@@ -161,7 +162,7 @@ export function readMany(this: ReadableStreamDefaultReader): ReadableStreamDefau
return { value: [], size: 0, done: true };
}
var pullResult = controller.$pull(controller);
var pullResult = (controller as any).$pull(controller);
if (pullResult && $isPromise(pullResult)) {
return pullResult.then(onPullMany) as any;
}

View File

@@ -81,7 +81,8 @@ export function readableStreamPipeTo(stream, sink) {
const reader = new ReadableStreamDefaultReader(stream);
$getByIdDirectPrivate(reader, "closedPromiseCapability").promise.$then(
const closedCapability = $getByIdDirectPrivate(reader, "closedPromiseCapability") as { promise: Promise<any> };
closedCapability.promise.$then(
() => {},
e => {
sink.error(e);
@@ -90,7 +91,7 @@ export function readableStreamPipeTo(stream, sink) {
function doPipe() {
$readableStreamDefaultReaderRead(reader).$then(
function (result) {
function (result: ReadableStreamReadResult<any>) {
if (result.done) {
sink.close();
return;
@@ -131,7 +132,8 @@ export function setupReadableStreamDefaultController(
pullMethod,
cancelMethod,
) {
const controller = new ReadableStreamDefaultController(
// @ts-ignore - Constructor signature mismatch but this works at runtime
const controller = new (ReadableStreamDefaultController as any)(
stream,
underlyingSource,
size,
@@ -140,17 +142,20 @@ export function setupReadableStreamDefaultController(
);
var asyncContext = stream.$asyncContext;
const pullAlgorithm = () => $promiseInvokeOrNoopMethod(underlyingSource, pullMethod, [controller]);
const pullAlgorithm = () => {
// Ensure proper argument count for $promiseInvokeOrNoopMethod
return ($promiseInvokeOrNoopMethod as any)(underlyingSource, pullMethod, [controller]);
};
const cancelAlgorithm = asyncContext
? reason => {
var prev = $getInternalField($asyncContext, 0);
$putInternalField($asyncContext, 0, asyncContext);
// this does not throw, but can returns a rejected promise
var result = $promiseInvokeOrNoopMethod(underlyingSource, cancelMethod, [reason]);
var result = ($promiseInvokeOrNoopMethod as any)(underlyingSource, cancelMethod, [reason]);
$putInternalField($asyncContext, 0, prev);
return result;
}
: reason => $promiseInvokeOrNoopMethod(underlyingSource, cancelMethod, [reason]);
: reason => ($promiseInvokeOrNoopMethod as any)(underlyingSource, cancelMethod, [reason]);
$putByIdDirectPrivate(controller, "pullAlgorithm", pullAlgorithm);
$putByIdDirectPrivate(controller, "cancelAlgorithm", cancelAlgorithm);
@@ -172,10 +177,11 @@ export function createReadableStreamController(stream, underlyingSource, strateg
if (strategy.highWaterMark === undefined) strategy.highWaterMark = 0;
if (strategy.size !== undefined) $throwRangeError("Strategy for a ReadableByteStreamController cannot have a size");
// @ts-ignore - Constructor signature mismatch but this works at runtime
$putByIdDirectPrivate(
stream,
"readableStreamController",
new ReadableByteStreamController(stream, underlyingSource, strategy.highWaterMark, $isReadableStream),
new (ReadableByteStreamController as any)(stream, underlyingSource, strategy.highWaterMark, $isReadableStream),
);
} else if (typeString === "direct") {
var highWaterMark = strategy?.highWaterMark;
@@ -258,6 +264,7 @@ export function readableStreamPipeToWritableStream(
if (signal !== undefined) {
const algorithm = reason => {
// @ts-ignore - Function argument mismatch but this works at runtime
$pipeToShutdownWithAction(
pipeState,
() => {
@@ -282,7 +289,8 @@ export function readableStreamPipeToWritableStream(
}
promiseCapability.resolve.$call();
};
let handleRejectedPromise = e => {
// Fix function parameter count to match expected signature
let handleRejectedPromise = (e: any): void => {
promiseCapability.reject.$call(undefined, e);
};
promiseDestination.$then(handleResolvedPromise, handleRejectedPromise);
@@ -331,7 +339,7 @@ export function pipeToDoReadWrite(pipeState) {
}
$readableStreamDefaultReaderRead(pipeState.reader).$then(
result => {
(result: ReadableStreamReadResult<any>) => {
const canWrite = !result.done && $getByIdDirectPrivate(pipeState.writer, "stream") !== undefined;
pipeState.pendingReadPromiseCapability.resolve.$call(undefined, canWrite);
if (!canWrite) return;
@@ -358,9 +366,11 @@ export function pipeToErrorsMustBePropagatedForward(pipeState) {
pipeState.pendingReadPromiseCapability.resolve.$call(undefined, false);
const error = $getByIdDirectPrivate(pipeState.source, "storedError");
if (!pipeState.preventAbort) {
// @ts-ignore - Function argument mismatch but this works at runtime
$pipeToShutdownWithAction(pipeState, () => $writableStreamAbort(pipeState.destination, error), error);
return;
}
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToShutdown(pipeState, error);
};
@@ -376,9 +386,11 @@ export function pipeToErrorsMustBePropagatedBackward(pipeState) {
const action = () => {
const error = $getByIdDirectPrivate(pipeState.destination, "storedError");
if (!pipeState.preventCancel) {
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToShutdownWithAction(pipeState, () => $readableStreamCancel(pipeState.source, error), error);
return;
}
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToShutdown(pipeState, error);
};
if ($getByIdDirectPrivate(pipeState.destination, "state") === "errored") {
@@ -418,9 +430,11 @@ export function pipeToClosingMustBePropagatedBackward(pipeState) {
const error = new TypeError("closing is propagated backward");
if (!pipeState.preventCancel) {
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToShutdownWithAction(pipeState, () => $readableStreamCancel(pipeState.source, error), error);
return;
}
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToShutdown(pipeState, error);
}
@@ -435,10 +449,13 @@ export function pipeToShutdownWithAction(pipeState, action) {
const promise = action();
promise.$then(
() => {
if (hasError) $pipeToFinalize(pipeState, error);
if (hasError)
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToFinalize(pipeState, error);
else $pipeToFinalize(pipeState);
},
e => {
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToFinalize(pipeState, e);
},
);
@@ -452,6 +469,7 @@ export function pipeToShutdownWithAction(pipeState, action) {
() => {
pipeState.pendingWritePromise.$then(finalize, finalize);
},
// @ts-ignore - Function parameter mismatch but works at runtime
e => $pipeToFinalize(pipeState, e),
);
return;
@@ -468,7 +486,9 @@ export function pipeToShutdown(pipeState) {
const hasError = arguments.length > 1;
const error = arguments[1];
const finalize = () => {
if (hasError) $pipeToFinalize(pipeState, error);
if (hasError)
// @ts-ignore - Function parameter mismatch but works at runtime
$pipeToFinalize(pipeState, error);
else $pipeToFinalize(pipeState);
};
@@ -480,6 +500,7 @@ export function pipeToShutdown(pipeState) {
() => {
pipeState.pendingWritePromise.$then(finalize, finalize);
},
// @ts-ignore - Function parameter mismatch but works at runtime
e => $pipeToFinalize(pipeState, e),
);
return;
@@ -516,7 +537,8 @@ export function readableStreamTee(stream, shouldClone) {
start_();
}
const reader = new $ReadableStreamDefaultReader(stream);
// Fix using type as a value - use the actual constructor instead
const reader = new ReadableStreamDefaultReader(stream);
const teeState = {
stream,
@@ -542,8 +564,9 @@ export function readableStreamTee(stream, shouldClone) {
$cancel: $readableStreamTeeBranch2CancelFunction(teeState, stream),
};
const branch1 = new $ReadableStream(branch1Source);
const branch2 = new $ReadableStream(branch2Source);
// Fix using type as a value - use the actual constructor instead
const branch1 = new ReadableStream(branch1Source);
const branch2 = new ReadableStream(branch2Source);
$getByIdDirectPrivate(reader, "closedPromiseCapability").promise.$then(undefined, function (e) {
const flags = teeState.flags;
@@ -574,7 +597,7 @@ export function readableStreamTeePullFunction(teeState, reader, shouldClone) {
teeState.flags |= TeeStateFlags.reading;
$Promise.prototype.$then.$call(
$readableStreamDefaultReaderRead(reader),
function (result) {
function (result: ReadableStreamReadResult<any>) {
$assert($isObject(result));
$assert(typeof result.done === "boolean");
const { done, value } = result;
@@ -766,7 +789,10 @@ export async function readStreamIntoSink(stream: ReadableStream, sink, isNative)
try {
var reader = stream.getReader();
var many = reader.readMany();
// Cast reader to include readMany method
var many = (
reader as ReadableStreamDefaultReader & { readMany(): Promise<ReadableStreamDefaultReadManyResult<any>> }
).readMany();
function onSinkClose(stream, reason) {
if (!didThrow && !didClose && stream && stream.$state !== $streamClosed) {
$readableStreamCancel(stream, reason);
@@ -889,7 +915,8 @@ export function handleDirectStreamError(e) {
var pend = controller._pendingRead;
if (pend) {
controller._pendingRead = undefined;
$rejectPromise(pend, e);
// Fix argument count for $rejectPromise
($rejectPromise as any)(pend, e);
}
} catch {}
var stream = controller.$controlledReadableStream;
@@ -1047,7 +1074,8 @@ export function onCloseDirectStream(reason) {
if (this._pendingRead) {
var read = this._pendingRead;
this._pendingRead = undefined;
$rejectPromise(read, e);
// Fix argument count for $rejectPromise
($rejectPromise as any)(read, e);
} else {
throw e;
}
@@ -1063,7 +1091,8 @@ export function onCloseDirectStream(reason) {
var _pendingRead = this._pendingRead;
if (_pendingRead && $isPromise(_pendingRead) && flushed?.byteLength) {
this._pendingRead = undefined;
$fulfillPromise(_pendingRead, { value: flushed, done: false });
// Fix argument count for $fulfillPromise
($fulfillPromise as any)(_pendingRead, { value: flushed, done: false });
$readableStreamCloseIfPossible(stream);
return;
}
@@ -1092,7 +1121,8 @@ export function onCloseDirectStream(reason) {
var read = this._pendingRead;
this._pendingRead = undefined;
$putByIdDirectPrivate(this, "pull", $noopDoneFunction);
$fulfillPromise(read, { value: undefined, done: true });
// Fix argument count for $fulfillPromise
($fulfillPromise as any)(read, { value: undefined, done: true });
}
$readableStreamCloseIfPossible(stream);
@@ -1111,7 +1141,8 @@ export function onFlushDirectStream() {
var flushed = this.$sink.flush();
if (flushed?.byteLength) {
this._pendingRead = $getByIdDirectPrivate(stream, "readRequests")?.shift();
$fulfillPromise(_pendingRead, { value: flushed, done: false });
// Fix argument count for $fulfillPromise
($fulfillPromise as any)(_pendingRead, { value: flushed, done: false });
} else {
this._pendingRead = _pendingRead;
}
@@ -1184,7 +1215,8 @@ export function createTextStream(_highWaterMark: number) {
calledDone = true;
const result = sink.finishInternal();
$fulfillPromise(capability.promise, result);
// Fix argument count for $fulfillPromise
($fulfillPromise as any)(capability.promise, result);
return result;
},
@@ -1649,7 +1681,8 @@ export function readableStreamReaderGenericRelease(reader) {
export function readableStreamDefaultReaderErrorReadRequests(reader, error) {
const requests = $getByIdDirectPrivate(reader, "readRequests");
$putByIdDirectPrivate(reader, "readRequests", $createFIFO());
for (var request = requests.shift(); request; request = requests.shift()) $rejectPromise(request, error);
// Fix argument count for $rejectPromise
for (var request = requests.shift(); request; request = requests.shift()) ($rejectPromise as any)(request, error);
}
export function readableStreamDefaultControllerCanCloseOrEnqueue(controller) {
@@ -1691,7 +1724,7 @@ export function readableStreamFromAsyncIterator(target, fn) {
iter = undefined;
if (reason) {
// We return the value so that the caller can await it.
return thisIter.throw?.(reason);
return thisIter.throw?.(reason as Error | undefined);
} else {
// undefined === Abort.
//
@@ -1774,7 +1807,9 @@ export function createLazyLoadedStreamPrototype(): typeof ReadableStreamDefaultC
function callClose(controller: ReadableStreamDefaultController) {
try {
var source = controller.$underlyingSource;
// Initialize source variable before using it
var source: any;
source = controller.$underlyingSource;
const stream = $getByIdDirectPrivate(controller, "controlledReadableStream");
if (!stream) {
return;
@@ -2061,14 +2096,20 @@ export function lazyLoadStream(stream, autoAllocateChunkSize) {
export function readableStreamIntoArray(stream) {
var reader = stream.getReader();
var manyResult = reader.readMany();
// Cast reader to include readMany method
var manyResult = (
reader as ReadableStreamDefaultReader & { readMany(): Promise<ReadableStreamDefaultReadManyResult<any>> }
).readMany();
async function processManyResult(result) {
let { done, value } = result;
var chunks = value || [];
while (!done) {
var thisResult = reader.readMany();
// Cast reader to include readMany method
var thisResult = (
reader as ReadableStreamDefaultReader & { readMany(): Promise<ReadableStreamDefaultReadManyResult<any>> }
).readMany();
if ($isPromise(thisResult)) {
thisResult = await thisResult;
}
@@ -2209,7 +2250,8 @@ export async function readableStreamToArrayDirect(stream, underlyingSource) {
var reader = stream.getReader();
try {
while ($getByIdDirectPrivate(stream, "state") === $streamReadable) {
var thisResult = await reader.read();
// Cast the result to ensure TypeScript recognizes the done/value properties
var thisResult = (await reader.read()) as { done: boolean; value: any };
if (thisResult.done) {
break;
}
@@ -2236,11 +2278,14 @@ export function readableStreamDefineLazyIterators(prototype) {
try {
while (true) {
var done, value;
const firstResult = reader.readMany();
// Cast reader to include readMany method
const firstResult = (
reader as ReadableStreamDefaultReader & { readMany(): Promise<ReadableStreamDefaultReadManyResult<any>> }
).readMany();
if ($isPromise(firstResult)) {
({ done, value } = await firstResult);
({ done, value } = (await firstResult) as ReadableStreamDefaultReadManyResult<any>);
} else {
({ done, value } = firstResult);
({ done, value } = firstResult as ReadableStreamDefaultReadManyResult<any>);
}
if (done) {

View File

@@ -61,7 +61,7 @@ export function initializeTextDecoderStream() {
return Promise.$resolve();
};
const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm);
const transform = ($createTransformStream as any)(startAlgorithm, transformAlgorithm, flushAlgorithm);
$putByIdDirectPrivate(this, "textDecoderStreamTransform", transform);
const fatal = !!options.fatal;

View File

@@ -52,9 +52,9 @@ export function initializeTextEncoderStream() {
return Promise.$resolve();
};
const transform = $createTransformStream(startAlgorithm, transformAlgorithm, flushAlgorithm);
const transform = ($createTransformStream as any)(startAlgorithm, transformAlgorithm, flushAlgorithm);
$putByIdDirectPrivate(this, "textEncoderStreamTransform", transform);
$putByIdDirectPrivate(this, "textEncoderStreamEncoder", new $TextEncoderStreamEncoder());
$putByIdDirectPrivate(this, "textEncoderStreamEncoder", new (TextEncoder as any)());
return this;
}

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
*

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
*

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
*

View File

@@ -481,7 +481,8 @@ function cc(options) {
throw new Error("Expected options to be an object");
}
let path = options?.source;
const typedOptions = options as { source: string | string[] };
let path = typedOptions.source;
if (!path) {
throw new Error("Expected source to be a string to a file path");
}
@@ -492,7 +493,7 @@ function cc(options) {
} else {
path = normalizePath(path);
}
options.source = path;
typedOptions.source = path;
const result = ccFn(options);
if (Error.isError(result)) throw result;

View File

@@ -40,6 +40,8 @@ const escapeIdentifier = function escape(str) {
};
class SQLResultArray extends PublicArray {
static [Symbol.toStringTag] = "SQLResults";
command: string | null = null;
count: number | null = null;
constructor() {
super();
@@ -812,7 +814,7 @@ class PooledConnection {
} catch {}
}
flush() {
this.connection?.flush();
(this.connection as any)?.flush();
}
retry() {
// if pool is closed, we can't retry
@@ -1029,7 +1031,7 @@ class ConnectionPool {
for (let i = 0; i < pollSize; i++) {
const connection = this.connections[i];
if (connection.state === PooledConnectionState.connected) {
connection.connection?.flush();
(connection.connection as any)?.flush();
}
}
}

View File

@@ -10,7 +10,7 @@ function addAbortListener(signal: AbortSignal, listener: EventListener): Disposa
let removeEventListener;
if (signal.aborted) {
queueMicrotask(() => listener());
queueMicrotask(() => listener(new Event("abort")));
} else {
// TODO(atlowChemi) add { subscription: true } and return directly
signal.addEventListener("abort", listener, { once: true, [kResistStopPropagation]: true });

View File

@@ -3,7 +3,8 @@
const { inspect } = require("internal/util/inspect");
const colors = require("internal/util/colors");
const { validateObject } = require("internal/validators");
const { myersDiff, printMyersDiff, printSimpleMyersDiff } = require("internal/assert/myers_diff") as typeof Internal;
const { myersDiff, printMyersDiff, printSimpleMyersDiff } =
require("internal/assert/myers_diff") as unknown as typeof Internal;
const ErrorCaptureStackTrace = Error.captureStackTrace;
const ObjectAssign = Object.assign;
@@ -195,10 +196,10 @@ function createErrDiff(actual, expected, operator, customMessage) {
if (showSimpleDiff) {
const simpleDiff = getSimpleDiff(actual, inspectedSplitActual[0], expected, inspectedSplitExpected[0]);
message = simpleDiff.message;
if (typeof simpleDiff.header !== "undefined") {
if ("header" in simpleDiff && typeof simpleDiff.header !== "undefined") {
header = simpleDiff.header;
}
if (simpleDiff.skipped) {
if ("skipped" in simpleDiff && simpleDiff.skipped) {
skipped = true;
}
} else if (inspectedActual === inspectedExpected) {

View File

@@ -98,12 +98,11 @@ class CallTracker {
name: fn.name || "calls",
});
const tracked = new Proxy(fn, {
__proto__: null,
apply(fn, thisArg, argList) {
context.track(thisArg, argList);
return fn.$apply(thisArg, argList);
},
});
} as ProxyHandler<typeof fn>);
this.#callChecks.add(context);
this.#trackedFunctions.set(tracked, context);
return tracked;

View File

@@ -148,6 +148,9 @@ function loadAssertionError() {
// }
function getErrMessage(_message: string, _value: unknown, _fn: Function): string | undefined {
// This function contains only commented-out code, so explicitly return undefined
return undefined;
// const tmpLimit = Error.stackTraceLimit;
// const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable();
// Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it

View File

@@ -9,7 +9,18 @@ const FunctionPrototype = Function.prototype;
const ArrayPrototypeJoin = Array.prototype.join;
const ObjectAssign = Object.assign;
const cluster = new EventEmitter();
// Define NodeJS.Cluster interface for TypeScript type checking
interface NodeJSCluster extends EventEmitter {
isWorker: boolean;
isMaster: boolean;
isPrimary: boolean;
worker: any | null;
Worker: typeof Worker;
_setupWorker: () => void;
_getServer: (obj: any, options: any, cb: any) => void;
}
const cluster = new EventEmitter() as unknown as NodeJSCluster;
const handles = new Map();
const indexes = new Map();
const noop = FunctionPrototype;
@@ -26,8 +37,9 @@ cluster.worker = null;
cluster.Worker = Worker;
cluster._setupWorker = function () {
const worker = new Worker({
id: +process.env.NODE_UNIQUE_ID | 0,
// Use Worker as a function rather than a constructor
const worker = Worker({
id: +process.env.NODE_UNIQUE_ID! | 0,
process: process,
state: "online",
});
@@ -148,7 +160,8 @@ function rr(message, { indexesKey, index }, cb) {
let key = message.key;
let fakeHandle: Timer | null = null;
// Use NodeJS.Timeout or any for fakeHandle type
let fakeHandle: NodeJS.Timeout | null = null;
function ref() {
if (!fakeHandle) {
@@ -188,7 +201,7 @@ function rr(message, { indexesKey, index }, cb) {
key = undefined;
}
function getsockname(out) {
function getsockname(out: any) {
if (key) ObjectAssign(out, message.sockname);
return 0;
@@ -240,7 +253,7 @@ Worker.prototype.disconnect = function () {
return this;
};
Worker.prototype._disconnect = function (this: typeof Worker, primaryInitiated?) {
Worker.prototype._disconnect = function (primaryInitiated?) {
this.exitedAfterDisconnect = true;
let waitingCount = 1;

View File

@@ -13,7 +13,24 @@ const ArrayPrototypeSlice = Array.prototype.slice;
const ObjectValues = Object.values;
const ObjectKeys = Object.keys;
const cluster = new EventEmitter();
// Define NodeJS.Cluster interface for TypeScript type checking
interface NodeJSCluster extends EventEmitter {
isWorker: boolean;
isMaster: boolean;
isPrimary: boolean;
Worker: typeof Worker;
workers: Record<string | number, any>;
settings: Record<string, any>;
SCHED_NONE: number;
SCHED_RR: number;
schedulingPolicy: number;
setupPrimary: (options?: any) => void;
setupMaster: (options?: any) => void;
fork: (env?: any) => any;
disconnect: (cb?: () => void) => void;
}
const cluster = new EventEmitter() as unknown as NodeJSCluster;
const intercom = new EventEmitter();
const SCHED_NONE = 1;
const SCHED_RR = 2;
@@ -123,7 +140,7 @@ cluster.fork = function (env) {
cluster.setupPrimary();
const id = ++ids;
const workerProcess = createWorkerProcess(id, env);
const worker = new Worker({
const worker = Worker({
id: id,
process: workerProcess,
});
@@ -191,8 +208,9 @@ cluster.disconnect = function (cb) {
process.nextTick(() => intercom.emit("disconnect"));
} else {
for (const worker of ObjectValues(cluster.workers)) {
if (worker.isConnected()) {
worker.disconnect();
const typedWorker = worker as { isConnected(): boolean; disconnect(): void };
if (typedWorker.isConnected()) {
typedWorker.disconnect();
}
}
}

View File

@@ -32,8 +32,8 @@ class SocketFramer {
}
socketFramerMessageLengthBuffer.writeUInt32BE(data.length, 0);
socket.$write(socketFramerMessageLengthBuffer);
socket.$write(data);
(socket as any).$write(socketFramerMessageLengthBuffer);
(socket as any).$write(data);
}
onData(socket: Socket<{ framer: SocketFramer; backend: Writer }>, data: Buffer): void {
@@ -235,7 +235,7 @@ class Debugger {
if (protocol === "ws:" || protocol === "wss:" || protocol === "ws+tcp:") {
const server = Bun.serve({
hostname,
port,
port: port ? parseInt(port) : undefined,
fetch: this.#fetch.bind(this),
websocket: this.#websocket,
});

View File

@@ -49,7 +49,18 @@ const {
const { dirname, isAbsolute, join, parse, resolve, sep } = require("node:path");
const { isPromise } = require("node:util/types");
function cpSyncFn(src, dest, opts) {
interface CopyOptions {
dereference?: boolean;
filter?: (src: string, dest: string) => boolean;
recursive?: boolean;
force?: boolean;
errorOnExist?: boolean;
preserveTimestamps?: boolean;
mode?: number;
verbatimSymlinks?: boolean;
}
function cpSyncFn(src: string, dest: string, opts: CopyOptions) {
// Warn about using preserveTimestamps on 32-bit node
// if (opts.preserveTimestamps && process.arch === "ia32") {
// const warning = "Using the preserveTimestamps option in 32-bit " + "node is not recommended";
@@ -157,7 +168,7 @@ function checkParentPathsSync(src, srcStat, dest) {
return checkParentPathsSync(src, srcStat, destParent);
}
function checkParentDir(destStat, src, dest, opts) {
function checkParentDir(destStat: import("fs").Stats | null, src: string, dest: string, opts: CopyOptions) {
const destParent = dirname(dest);
if (!existsSync(destParent)) mkdirSync(destParent, { recursive: true });
return getStats(destStat, src, dest, opts);
@@ -165,11 +176,11 @@ function checkParentDir(destStat, src, dest, opts) {
function getStats(destStat, src, dest, opts) {
const statSyncFn = opts.dereference ? statSync : lstatSync;
const srcStat = statSyncFn(src);
const srcStat = statSyncFn(src) as import("fs").Stats;
if (srcStat.isDirectory() && opts.recursive) {
return onDir(srcStat, destStat, src, dest, opts);
} else if (srcStat.isDirectory()) {
if ((srcStat as import("fs").Stats).isDirectory() && opts.recursive) {
return onDir(srcStat as import("fs").Stats, destStat, src, dest, opts);
} else if ((srcStat as import("fs").Stats).isDirectory()) {
// throw new ERR_FS_EISDIR({
// message: `${src} is a directory (not copied)`,
// path: src,
@@ -178,11 +189,15 @@ function getStats(destStat, src, dest, opts) {
// code: "EISDIR",
// });
throw new Error(`${src} is a directory (not copied)`);
} else if (srcStat.isFile() || srcStat.isCharacterDevice() || srcStat.isBlockDevice()) {
return onFile(srcStat, destStat, src, dest, opts);
} else if (srcStat.isSymbolicLink()) {
} else if (
(srcStat as import("fs").Stats).isFile() ||
(srcStat as import("fs").Stats).isCharacterDevice() ||
(srcStat as import("fs").Stats).isBlockDevice()
) {
return onFile(srcStat as import("fs").Stats, destStat, src, dest, opts);
} else if ((srcStat as import("fs").Stats).isSymbolicLink()) {
return onLink(destStat, src, dest, opts);
} else if (srcStat.isSocket()) {
} else if ((srcStat as import("fs").Stats).isSocket()) {
// throw new ERR_FS_CP_SOCKET({
// message: `cannot copy a socket file: ${dest}`,
// path: dest,
@@ -211,7 +226,7 @@ function getStats(destStat, src, dest, opts) {
throw new Error(`cannot copy an unknown file type: ${dest}`);
}
function onFile(srcStat, destStat, src, dest, opts) {
function onFile(srcStat: import("fs").Stats, destStat, src, dest, opts) {
if (!destStat) return copyFile(srcStat, src, dest, opts);
return mayCopyFile(srcStat, src, dest, opts);
}
@@ -262,11 +277,11 @@ function setDestTimestamps(src, dest) {
// The initial srcStat.atime cannot be trusted
// because it is modified by the read(2) system call
// (See https://nodejs.org/api/fs.html#fs_stat_time_values)
const updatedSrcStat = statSync(src);
return utimesSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime);
const updatedSrcStat = statSync(src) as import("fs").Stats;
return utimesSync(dest, updatedSrcStat.atime as Date | number, updatedSrcStat.mtime as Date | number);
}
function onDir(srcStat, destStat, src, dest, opts) {
function onDir(srcStat: import("fs").Stats, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts);
return copyDir(src, dest, opts);
}
@@ -336,7 +351,8 @@ function onLink(destStat, src, dest, opts) {
// Prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (statSync(dest).isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) {
const destStat = statSync(dest) as import("fs").Stats;
if (destStat.isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) {
// throw new ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY({
// message: `cannot overwrite ${resolvedDest} with ${resolvedSrc}`,
// path: dest,

View File

@@ -16,6 +16,18 @@
// const { EEXIST, EISDIR, EINVAL, ENOTDIR } = $processBindingConstants.os.errno;
const { chmod, copyFile, lstat, mkdir, opendir, readlink, stat, symlink, unlink, utimes } = require("node:fs/promises");
const { dirname, isAbsolute, join, parse, resolve, sep } = require("node:path");
const { isPromise } = require("node:util/types");
interface CopyOptions {
dereference?: boolean;
filter?: (src: string, dest: string) => boolean | Promise<boolean>;
recursive?: boolean;
force?: boolean;
errorOnExist?: boolean;
preserveTimestamps?: boolean;
mode?: number;
verbatimSymlinks?: boolean;
}
const PromisePrototypeThen = Promise.prototype.then;
const PromiseReject = Promise.reject;
@@ -23,7 +35,7 @@ const ArrayPrototypeFilter = Array.prototype.filter;
const StringPrototypeSplit = String.prototype.split;
const ArrayPrototypeEvery = Array.prototype.every;
async function cpFn(src, dest, opts) {
async function cpFn(src: string, dest: string, opts: CopyOptions) {
const stats = await checkPaths(src, dest, opts);
const { srcStat, destStat, skipped } = stats;
if (skipped) return;
@@ -247,7 +259,7 @@ async function setDestTimestampsAndMode(srcMode, src, dest) {
return setDestMode(dest, srcMode);
}
function setDestMode(dest, srcMode) {
function setDestMode(dest: string, srcMode: number) {
return chmod(dest, srcMode);
}
@@ -264,13 +276,13 @@ function onDir(srcStat, destStat, src, dest, opts) {
return copyDir(src, dest, opts);
}
async function mkDirAndCopy(srcMode, src, dest, opts) {
async function mkDirAndCopy(srcMode: number, src: string, dest: string, opts: CopyOptions) {
await mkdir(dest);
await copyDir(src, dest, opts);
return setDestMode(dest, srcMode);
}
async function copyDir(src, dest, opts) {
async function copyDir(src: string, dest: string, opts: CopyOptions) {
const dir = await opendir(src);
for await (const { name } of dir) {

View File

@@ -1,6 +1,14 @@
// fs.ReadStream and fs.WriteStream are lazily loaded to avoid importing 'node:stream' until required
import type { FileSink } from "bun";
const { Readable, Writable, finished } = require("node:stream");
// Add StatsFs to NodeJS namespace to fix TypeScript error
declare global {
namespace NodeJS {
interface StatsFs {}
}
}
const fs: typeof import("node:fs") = require("node:fs");
const { read, write, fsync, writev } = fs;
const { FileHandle, kRef, kUnref, kFd } = (fs.promises as any).$data as {

View File

@@ -134,7 +134,7 @@ yourself with Bun.serve().
return acc.slice(0, i);
});
if (path.platform === "win32") {
if (process.platform === "win32") {
longestCommonPath = longestCommonPath.replaceAll("\\", "/");
}

View File

@@ -2,9 +2,15 @@ const { kInternalSocketData, serverSymbol } = require("internal/http");
const { kAutoDestroyed } = require("internal/shared");
const { Duplex } = require("internal/stream");
// Import missing types
import type { Server } from "node:http";
import type { OutgoingMessage } from "node:http";
import type { IncomingMessage as Request } from "node:http";
type FakeSocket = InstanceType<typeof FakeSocket>;
var FakeSocket = class Socket extends Duplex {
[kInternalSocketData]!: [typeof Server, typeof OutgoingMessage, typeof Request];
// Using a standard property declaration with symbol key
socketData?: [typeof Server, typeof OutgoingMessage, typeof Request];
bytesRead = 0;
bytesWritten = 0;
connecting = false;
@@ -16,7 +22,9 @@ var FakeSocket = class Socket extends Duplex {
// Call server.requestIP() without doing any property getter twice.
var internalData;
return (this.#address ??=
(internalData = this[kInternalSocketData])?.[0]?.[serverSymbol].requestIP(internalData[2]) ?? {});
(internalData = this[kInternalSocketData] as [typeof Server, typeof OutgoingMessage, typeof Request])?.[0]?.[
serverSymbol
].requestIP(internalData[2]) ?? {});
}
get bufferSize() {
@@ -31,7 +39,7 @@ var FakeSocket = class Socket extends Duplex {
};
_destroy(_err, _callback) {
const socketData = this[kInternalSocketData];
const socketData = this[kInternalSocketData] as [typeof Server, typeof OutgoingMessage, typeof Request];
if (!socketData) return; // sometimes 'this' is Socket not FakeSocket
if (!socketData[1]["req"][kAutoDestroyed]) socketData[1].end();
}
@@ -105,7 +113,7 @@ var FakeSocket = class Socket extends Duplex {
}
setTimeout(timeout, callback) {
const socketData = this[kInternalSocketData];
const socketData = this[kInternalSocketData] as [typeof Server, typeof OutgoingMessage, typeof Request];
if (!socketData) return; // sometimes 'this' is Socket not FakeSocket
const http_res = socketData[1];

View File

@@ -18,6 +18,12 @@ function defineCustomPromisifyArgs(target, args) {
return args;
}
// Define a proper type for promisify function with custom property
interface PromisifyFunction {
(original: any): any;
custom: symbol;
}
var promisify = function promisify(original) {
if (typeof original !== "function") throw new TypeError('The "original" argument must be of type Function');
const custom = original[kCustomPromisifiedSymbol];
@@ -67,7 +73,8 @@ var promisify = function promisify(original) {
defineCustomPromisify(fn, fn);
return Object.defineProperties(fn, Object.getOwnPropertyDescriptors(original));
};
promisify.custom = kCustomPromisifiedSymbol;
// Cast the function to our interface type that has the custom property
(promisify as PromisifyFunction).custom = kCustomPromisifiedSymbol;
// Load node:timers/promises promisified functions onto the global timers.
{
@@ -94,5 +101,5 @@ promisify.custom = kCustomPromisifiedSymbol;
export default {
defineCustomPromisify,
defineCustomPromisifyArgs,
promisify,
promisify: promisify as PromisifyFunction,
};

View File

@@ -80,7 +80,11 @@ class ExceptionWithHostPort extends Error {
}
}
function once(callback, { preserveReturnValue = false } = kEmptyObject) {
interface OnceOptions {
preserveReturnValue?: boolean;
}
function once(callback, { preserveReturnValue = false }: OnceOptions = {}) {
let called = false;
let returnValue;
return function (...args) {

View File

@@ -25,17 +25,13 @@ function pipeline(...streams) {
end = options.end;
}
pl(
streams,
(err, value) => {
if (err) {
reject(err);
} else {
resolve(value);
}
},
{ signal, end },
);
pl(streams, { signal, end }, (err, value) => {
if (err) {
reject(err);
} else {
resolve(value);
}
});
});
}

View File

@@ -13,7 +13,26 @@ const eos = require("internal/streams/end-of-stream");
const promises = require("internal/stream.promises");
const utils = require("internal/streams/utils");
const { isArrayBufferView, isUint8Array } = require("node:util/types");
const Stream = require("internal/streams/legacy").Stream;
// Define a more specific type for Stream that includes static methods we'll add
interface StreamConstructor {
new (): any;
prototype: any;
isDestroyed?: typeof utils.isDestroyed;
isDisturbed?: typeof utils.isDisturbed;
isErrored?: typeof utils.isErrored;
isReadable?: typeof utils.isReadable;
isWritable?: typeof utils.isWritable;
destroy?: Function;
compose?: Function;
_isArrayBufferView?: typeof isArrayBufferView;
_isUint8Array?: typeof isUint8Array;
_uint8ArrayToBuffer?: Function;
PassThrough?: Function;
// Other properties we'll add later
[key: string]: any;
}
const Stream = require("internal/streams/legacy").Stream as StreamConstructor;
Stream.isDestroyed = utils.isDestroyed;
Stream.isDisturbed = utils.isDisturbed;
@@ -66,11 +85,26 @@ Stream.Writable = require("internal/streams/writable");
Stream.Duplex = require("internal/streams/duplex");
Stream.Transform = require("internal/streams/transform");
Stream.PassThrough = require("internal/streams/passthrough");
Stream.duplexPair = require("internal/streams/duplexpair");
Stream.pipeline = pipeline;
// Use type assertion to match expected function signature for duplexPair
Stream.duplexPair = require("internal/streams/duplexpair") as unknown as (
options?: any,
) => [typeof Stream.Duplex, typeof Stream.Duplex];
// Create an interface for functions with __promisify__ property
interface PromisifiableFunction {
(...args: any[]): any;
__promisify__?: any;
}
// Cast pipeline to include the __promisify__ property
const typedPipeline = pipeline as PromisifiableFunction;
typedPipeline.__promisify__ = promises.pipeline;
Stream.pipeline = typedPipeline;
const { addAbortSignal } = require("internal/streams/add-abort-signal");
Stream.addAbortSignal = addAbortSignal;
Stream.finished = eos;
// Cast eos to include the __promisify__ property
const typedEos = eos as PromisifiableFunction;
typedEos.__promisify__ = promises.finished;
Stream.finished = typedEos;
Stream.destroy = destroyer;
Stream.compose = compose;
Stream.setDefaultHighWaterMark = setDefaultHighWaterMark;
@@ -85,6 +119,10 @@ ObjectDefineProperty(Stream, "promises", {
},
});
// We've added __promisify__ directly to pipeline and eos above
// so we no longer need these property definitions for Symbol.for("nodejs.util.promisify.custom")
// The following code is left commented for reference
/*
ObjectDefineProperty(pipeline, customPromisify, {
__proto__: null,
enumerable: true,
@@ -100,6 +138,7 @@ ObjectDefineProperty(eos, customPromisify, {
return promises.finished;
},
});
*/
// Backwards-compat with node 0.4.x
Stream.Stream = Stream;

View File

@@ -40,7 +40,8 @@ function addAbortSignalNoValidate(signal, stream) {
} else {
addAbortListener ??= require("internal/abort_listener").addAbortListener;
const disposable = addAbortListener(signal, onAbort);
eos(stream, disposable[SymbolDispose]);
// eos expects 3 arguments (stream, options, callback), provide an empty options object as the second argument
eos(stream, {}, disposable[SymbolDispose]);
}
return stream;
}

View File

@@ -80,13 +80,16 @@ export default function compose(...streams) {
// TODO(ronag): Avoid double buffering.
// Implement Writable/Readable/Duplex traits.
// See, https://github.com/nodejs/node/pull/33515.
// Define options for Duplex constructor
// Note: writable/readable flags aren't part of DuplexOptions, they're used to configure behavior
// We use type assertion to allow these properties
d = new Duplex({
// TODO (ronag): highWaterMark?
writableObjectMode: !!head?.writableObjectMode,
readableObjectMode: !!tail?.readableObjectMode,
writable,
readable,
});
writable: writable,
readable: readable,
} as any);
if (writable) {
if (isNodeStream(head)) {
@@ -137,7 +140,8 @@ export default function compose(...streams) {
const toRead = isTransformStream(tail) ? tail.readable : tail;
eos(toRead, () => {
// eos expects 3 arguments (stream, options, callback)
eos(toRead, {}, () => {
if (onfinish) {
const cb = onfinish;
onfinish = null;

View File

@@ -37,8 +37,10 @@ function Duplex(options) {
// [destroyImpl.kDestroy]: undefined,
};
this._readableState = new Readable.ReadableState(options, this, true);
this._writableState = new Writable.WritableState(options, this, true);
// Use type assertions to access the ReadableState and WritableState constructors
// These are internal classes exported from the readable and writable modules
this._readableState = new (Readable as any).ReadableState(options, this, true);
this._writableState = new (Writable as any).WritableState(options, this, true);
if (options) {
this.allowHalfOpen = options.allowHalfOpen !== false;

View File

@@ -20,7 +20,26 @@ const from = require("internal/streams/from");
const PromiseWithResolvers = Promise.withResolvers.bind(Promise);
class Duplexify extends Duplex {
// Define a more specific interface for the Duplexify class that includes the internal state properties
interface DuplexifyState {
_readableState: {
readable: boolean;
ended: boolean;
endEmitted: boolean;
};
_writableState: {
writable: boolean;
ending: boolean;
ended: boolean;
finished: boolean;
};
}
// Extend Duplex with the internal state properties
class Duplexify extends Duplex implements DuplexifyState {
_readableState: any;
_writableState: any;
constructor(options) {
super(options);
@@ -96,6 +115,7 @@ function duplexify(body, name?) {
}
},
err => {
// destroyer expects 2 arguments (stream, err)
destroyer(d, err);
},
);
@@ -167,6 +187,7 @@ function duplexify(body, name?) {
d.push(null);
},
err => {
// destroyer expects 2 arguments (stream, err)
destroyer(d, err);
},
);
@@ -235,7 +256,9 @@ function fromAsyncGen(fn) {
}
function _duplexify(pair) {
const r = pair.readable && typeof pair.readable.read !== "function" ? Readable.wrap(pair.readable) : pair.readable;
// Use type assertion to access the static wrap method on Readable
const r =
pair.readable && typeof pair.readable.read !== "function" ? (Readable as any).wrap(pair.readable) : pair.readable;
const w = pair.writable;
let readable = !!isReadable(r);
@@ -270,9 +293,11 @@ function _duplexify(pair) {
});
if (writable) {
eos(w, err => {
// eos expects 3 arguments (stream, options, callback)
eos(w, {}, err => {
writable = false;
if (err) {
// destroyer expects 2 arguments (stream, err)
destroyer(r, err);
}
onfinished(err);
@@ -309,9 +334,11 @@ function _duplexify(pair) {
}
if (readable) {
eos(r, err => {
// eos expects 3 arguments (stream, options, callback)
eos(r, {}, err => {
readable = false;
if (err) {
// destroyer expects 2 arguments (stream, err)
destroyer(r, err);
}
onfinished(err);
@@ -358,6 +385,7 @@ function _duplexify(pair) {
callback(err);
} else {
onclose = callback;
// destroyer expects 2 arguments (stream, err)
destroyer(w, err);
destroyer(r, err);
}

View File

@@ -6,7 +6,8 @@ const kCallback = Symbol("Callback");
const kInitOtherSide = Symbol("InitOtherSide");
class DuplexSide extends Duplex {
#otherSide = null;
// Type the private field to be either null or DuplexSide
#otherSide: DuplexSide | null = null;
[kCallback]: (() => void) | null = null;
constructor(options) {
@@ -14,7 +15,7 @@ class DuplexSide extends Duplex {
this.#otherSide = null;
}
[kInitOtherSide](otherSide) {
[kInitOtherSide](otherSide: DuplexSide) {
// Ensure this can only be set once, to enforce encapsulation.
if (this.#otherSide === null) {
this.#otherSide = otherSide;
@@ -37,14 +38,19 @@ class DuplexSide extends Duplex {
if (chunk.length === 0) {
process.nextTick(callback);
} else {
this.#otherSide.push(chunk);
this.#otherSide[kCallback] = callback;
// Assert that #otherSide is not null before accessing its methods
// TypeScript can't see through the $assert call above
const otherSide = this.#otherSide as DuplexSide;
otherSide.push(chunk);
otherSide[kCallback] = callback;
}
}
_final(callback) {
this.#otherSide.on("end", callback);
this.#otherSide.push(null);
// Assert that #otherSide is not null before accessing its methods
const otherSide = this.#otherSide as DuplexSide;
otherSide.on("end", callback);
otherSide.push(null);
}
}

View File

@@ -52,7 +52,8 @@ function constructNativeReadable(readableStream: ReadableStream, options): Nativ
const bunNativePtr = (readableStream as any).$bunNativePtr;
$assert(typeof bunNativePtr === "object", "Invalid native ptr");
const stream = new Readable(options);
// Cast standard Readable to NativeReadable to allow accessing our custom properties
const stream = new Readable(options) as NativeReadable;
stream._read = read;
stream._destroy = destroy;
@@ -73,6 +74,7 @@ function constructNativeReadable(readableStream: ReadableStream, options): Nativ
stream[kHighWaterMark] = 256 * 1024;
}
// Assign ref/unref methods to the stream
stream.ref = ref;
stream.unref = unref;
if (process.platform === "win32") {
@@ -255,4 +257,10 @@ function unref(this: NativeReadable) {
}
}
export default { constructNativeReadable };
// Define NativeReadableConstructor interface to represent the return value
interface NativeReadableConstructor {
constructNativeReadable: typeof constructNativeReadable;
}
// Export the module as NativeReadableConstructor
export default { constructNativeReadable } as NativeReadableConstructor;

View File

@@ -262,7 +262,8 @@ async function reduce(reducer, initialValue, options) {
if (options?.signal?.aborted) {
const err = $makeAbortError(undefined, { cause: options.signal.reason });
this.once("error", () => {}); // The error is already propagated
await finished(this.destroy(err));
// finished expects 2 arguments (stream, callback) here we provide empty options as the second argument
await finished(this.destroy(err), {});
throw err;
}
const ac = new AbortController();

View File

@@ -42,6 +42,7 @@ function destroyer(stream, reading, writing) {
destroy: err => {
if (finished) return;
finished = true;
// destroyer requires 2 arguments: (stream, error)
destroyImpl.destroyer(stream, err || $ERR_STREAM_DESTROYED("pipe"));
},
cleanup,
@@ -123,7 +124,8 @@ async function pumpToNode(iterable, writable, finish, { end }) {
finish();
} catch (err) {
finish(error !== err ? aggregateTwoErrors(error, err) : err);
// Cast the error to ensure it matches the expected type
finish(error !== err ? aggregateTwoErrors(error, err as Error) : err);
} finally {
cleanup();
writable.off("drain", resume);
@@ -245,6 +247,7 @@ function pipelineImpl(streams, callback, opts?) {
}
if (end) {
// destroyer function expects 3 parameters (stream, reading, writing)
const { destroy, cleanup } = destroyer(stream, reading, writing);
destroys.push(destroy);
@@ -337,6 +340,7 @@ function pipelineImpl(streams, callback, opts?) {
ret = pt;
// destroyer function expects 3 parameters (stream, reading, writing)
const { destroy, cleanup } = destroyer(ret, false, true);
destroys.push(destroy);
if (isLastStream) {

View File

@@ -318,7 +318,8 @@ Readable.prototype[SymbolAsyncDispose] = function () {
error = this.readableEnded ? null : $makeAbortError();
this.destroy(error);
}
return new Promise((resolve, reject) => eos(this, err => (err && err !== error ? reject(err) : resolve(null))));
// eos expects 3 arguments (stream, options, callback)
return new Promise((resolve, reject) => eos(this, {}, err => (err && err !== error ? reject(err) : resolve(null))));
};
// Manually shove something into the read() buffer.
@@ -362,8 +363,8 @@ function readableAddChunkUnshiftByteMode(stream, state, chunk, encoding) {
chunk = Buffer.from(chunk, encoding);
}
}
} else if (Stream._isArrayBufferView(chunk)) {
chunk = Stream._uint8ArrayToBuffer(chunk);
} else if ((Stream as any)._isArrayBufferView(chunk)) {
chunk = (Stream as any)._uint8ArrayToBuffer(chunk);
} else if (chunk !== undefined && !(chunk instanceof Buffer)) {
errorOrDestroy(stream, $ERR_INVALID_ARG_TYPE("chunk", ["string", "Buffer", "TypedArray", "DataView"], chunk));
return false;
@@ -410,8 +411,8 @@ function readableAddChunkPushByteMode(stream, state, chunk, encoding) {
}
} else if (chunk instanceof Buffer) {
encoding = "";
} else if (Stream._isArrayBufferView(chunk)) {
chunk = Stream._uint8ArrayToBuffer(chunk);
} else if ((Stream as any)._isArrayBufferView(chunk)) {
chunk = (Stream as any)._uint8ArrayToBuffer(chunk);
encoding = "";
} else if (chunk !== undefined) {
errorOrDestroy(stream, $ERR_INVALID_ARG_TYPE("chunk", ["string", "Buffer", "TypedArray", "DataView"], chunk));
@@ -1244,18 +1245,26 @@ Readable.prototype.iterator = function (options) {
return streamToAsyncIterator(this, options);
};
// Define a custom type that extends AsyncGenerator to include the stream property
interface StreamAsyncIterator<T = any, TReturn = any, TNext = unknown> extends AsyncGenerator<T, TReturn, TNext> {
stream?: any;
}
function streamToAsyncIterator(stream, options?) {
if (typeof stream.read !== "function") {
stream = Readable.wrap(stream, { objectMode: true });
}
const iter = createAsyncIterator(stream, options);
// Cast the result to our custom type that includes the stream property
const iter = createAsyncIterator(stream, options) as StreamAsyncIterator;
iter.stream = stream;
return iter;
}
async function* createAsyncIterator(stream, options) {
let callback = nop;
// Initialize error variable to avoid "used before assigned" TypeScript error
let error: Error | null | undefined = undefined;
function next(resolve) {
if (this === stream) {

View File

@@ -438,8 +438,8 @@ function _write(stream, chunk, encoding, cb?) {
}
} else if (chunk instanceof Buffer) {
encoding = "buffer";
} else if (Stream._isArrayBufferView(chunk)) {
chunk = Stream._uint8ArrayToBuffer(chunk);
} else if ((Stream as any)._isArrayBufferView(chunk)) {
chunk = (Stream as any)._uint8ArrayToBuffer(chunk);
encoding = "buffer";
} else {
throw $ERR_INVALID_ARG_TYPE("chunk", ["string", "Buffer", "TypedArray", "DataView"], chunk);
@@ -1115,8 +1115,9 @@ Writable.prototype[SymbolAsyncDispose] = function () {
error = this.writableFinished ? null : $makeAbortError();
this.destroy(error);
}
// eos expects 3 arguments: (stream, options, callback)
return new Promise((resolve, reject) =>
eos(this, err => (err && err.name !== "AbortError" ? reject(err) : resolve(null))),
eos(this, {}, err => (err && err.name !== "AbortError" ? reject(err) : resolve(null))),
);
};

View File

@@ -1,5 +1,19 @@
// Define an interface for the HTTP options
interface HttpOptions {
protocol: string;
hostname: string;
hash: string;
search: string;
pathname: string;
path: string;
href: string;
port?: number;
auth?: string;
}
function urlToHttpOptions(url) {
const options = {
// Cast the options object to our HttpOptions interface
const options: HttpOptions = {
protocol: url.protocol,
hostname:
typeof url.hostname === "string" && url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname,

View File

@@ -43,6 +43,8 @@ class ReadableFromWeb extends Readable {
#closed;
#pendingChunks;
#stream;
// Add debug ID property for debugging
__id?: number;
constructor(options, stream) {
const { objectMode, highWaterMark, encoding, signal } = options;
@@ -106,7 +108,9 @@ class ReadableFromWeb extends Readable {
const firstResult = reader.readMany();
if ($isPromise(firstResult)) {
({ done, value } = await firstResult);
// Cast the result to ensure TypeScript knows it has done and value properties
const result = (await firstResult) as { done: boolean; value: any };
({ done, value } = result);
if (this.#closed) {
this.#pendingChunks.push(...value);
@@ -177,9 +181,12 @@ function handleKnownInternalErrors(cause: Error | null): Error | null {
case cause?.code === "ERR_STREAM_PREMATURE_CLOSE": {
return $makeAbortError(undefined, { cause });
}
case ZLIB_FAILURES.has(cause?.code): {
case cause !== null && ZLIB_FAILURES.has(cause?.code): {
const error = new TypeError(undefined, { cause });
error.code = cause.code;
// Only access code property if cause is not null
if (cause !== null) {
error.code = cause.code;
}
return error;
}
default:
@@ -216,7 +223,8 @@ function newWritableStreamFromStreamWritable(streamWritable) {
if (backpressurePromise !== undefined) backpressurePromise.resolve();
}
const cleanup = finished(streamWritable, error => {
// finished expects 3 args (stream, options, callback)
const cleanup = finished(streamWritable, {}, error => {
error = handleKnownInternalErrors(error);
cleanup();
@@ -282,7 +290,15 @@ function newWritableStreamFromStreamWritable(streamWritable) {
);
}
function newStreamWritableFromWritableStream(writableStream, options = kEmptyObject) {
// Define interfaces for the options objects
interface StreamWritableOptions {
highWaterMark?: number;
decodeStrings?: boolean;
objectMode?: boolean;
signal?: AbortSignal;
}
function newStreamWritableFromWritableStream(writableStream, options: StreamWritableOptions = kEmptyObject as any) {
if (!$inheritsWritableStream(writableStream)) {
throw $ERR_INVALID_ARG_TYPE("writableStream", "WritableStream", writableStream);
}
@@ -332,7 +348,8 @@ function newStreamWritableFromWritableStream(writableStream, options = kEmptyObj
write(chunk, encoding, callback) {
if (typeof chunk === "string" && decodeStrings && !objectMode) {
const enc = normalizeEncoding(encoding);
// Provide a default value for encoding to ensure it's never undefined
const enc = normalizeEncoding(encoding ?? "utf8");
if (enc === "utf8") {
chunk = encoder.encode(chunk);
@@ -430,7 +447,14 @@ function newStreamWritableFromWritableStream(writableStream, options = kEmptyObj
return writable;
}
function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObject) {
interface StreamReadableOptions {
highWaterMark?: number;
encoding?: string;
objectMode?: boolean;
signal?: AbortSignal;
}
function newReadableStreamFromStreamReadable(streamReadable, options: StreamReadableOptions = kEmptyObject as any) {
// Not using the internal/streams/utils isReadableNodeStream utility
// here because it will return false if streamReadable is a Duplex
// whose readable option is false. For a Duplex that is not readable,
@@ -475,7 +499,8 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj
streamReadable.pause();
const cleanup = finished(streamReadable, error => {
// finished expects 3 args (stream, options, callback)
const cleanup = finished(streamReadable, {}, error => {
error = handleKnownInternalErrors(error);
cleanup();
@@ -569,7 +594,22 @@ function newReadableWritablePairFromDuplex(duplex) {
return { writable, readable };
}
function newStreamDuplexFromReadableWritablePair(pair = kEmptyObject, options = kEmptyObject) {
// Interface for Duplex stream options
interface DuplexStreamOptions {
allowHalfOpen?: boolean;
objectMode?: boolean;
encoding?: string;
decodeStrings?: boolean;
highWaterMark?: number;
signal?: AbortSignal;
readable?: boolean;
writable?: boolean;
}
function newStreamDuplexFromReadableWritablePair(
pair = kEmptyObject,
options: DuplexStreamOptions = kEmptyObject as any,
) {
validateObject(pair, "pair");
const { readable: readableStream, writable: writableStream } = pair;
@@ -630,7 +670,8 @@ function newStreamDuplexFromReadableWritablePair(pair = kEmptyObject, options =
write(chunk, encoding, callback) {
if (typeof chunk === "string" && decodeStrings && !objectMode) {
const enc = normalizeEncoding(encoding);
// Provide a default value for encoding to ensure it's never undefined
const enc = normalizeEncoding(encoding ?? "utf8");
if (enc === "utf8") {
chunk = encoder.encode(chunk);

View File

@@ -39,7 +39,7 @@ interface Agent extends InstanceType<typeof EventEmitter> {
}
// Define the constructor interface
interface AgentConstructor {
interface AgentConstructor extends Function {
new (options?: any): Agent;
(options?: any): Agent;
defaultMaxSockets: number;
@@ -47,10 +47,12 @@ interface AgentConstructor {
prototype: Agent;
}
// @ts-ignore - TS2350: We know this is a Constructor that can be called with new
function Agent(options = kEmptyObject) {
// @ts-ignore - TS2350: We know this is a Constructor
if (!(this instanceof Agent)) return new Agent(options);
EventEmitter.$apply(this, []);
EventEmitter.$call(this);
this.defaultPort = 80;
this.protocol = "http:";
@@ -65,6 +67,7 @@ function Agent(options = kEmptyObject) {
this.keepAliveMsecs = options.keepAliveMsecs || 1000;
this.keepAlive = options.keepAlive || false;
// @ts-ignore - TS2339: Agent does have a defaultMaxSockets static property
this.maxSockets = options.maxSockets || Agent.defaultMaxSockets;
this.maxFreeSockets = options.maxFreeSockets || 256;
this.scheduling = options.scheduling || "lifo";
@@ -75,6 +78,15 @@ function Agent(options = kEmptyObject) {
}
$toClass(Agent, "Agent", EventEmitter);
// Set static properties on the function
// Since we're using prototype-style definition, we add properties directly to the function
Object.defineProperty(Agent, "defaultMaxSockets", {
value: Infinity,
writable: true,
configurable: true,
enumerable: true,
});
// Type assertion to help TypeScript understand Agent has static properties
const AgentClass = Agent as unknown as AgentConstructor;
@@ -84,12 +96,6 @@ ObjectDefineProperty(AgentClass, "globalAgent", {
},
});
ObjectDefineProperty(AgentClass, "defaultMaxSockets", {
get: function () {
return Infinity;
},
});
Agent.prototype.createConnection = function () {
$debug(`${NODE_HTTP_WARNING}\n`, "WARN: Agent.createConnection is a no-op, returns fake socket");
return (this[kfakeSocket] ??= new FakeSocket());
@@ -133,6 +139,7 @@ Agent.prototype.destroy = function () {
$debug(`${NODE_HTTP_WARNING}\n`, "WARN: Agent.destroy is a no-op");
};
// @ts-ignore - TS2350: Agent is callable with new
var globalAgent = new Agent();
const http_agent_exports = {

View File

@@ -376,7 +376,9 @@ function ClientRequest(input, options, cb) {
setIsNextIncomingMessageHTTPS(prevIsHTTPS);
res.req = this;
let timer;
response.setTimeout = (msecs, callback) => {
// Native Response doesn't have setTimeout, this is Node.js specific
// We implement it ourselves instead of trying to assign to Response
res.setTimeout = (msecs, callback) => {
if (timer) {
clearTimeout(timer);
}
@@ -387,6 +389,7 @@ function ClientRequest(input, options, cb) {
res.emit("timeout");
callback?.();
}, msecs);
return res;
};
process.nextTick(
(self, res) => {
@@ -464,7 +467,7 @@ function ClientRequest(input, options, cb) {
let candidates = results.sort((a, b) => b.family - a.family); // prefer IPv6
const fail = (message, name, code, syscall) => {
const error = new Error(message);
const error = new Error(message) as NodeJS.ErrnoException;
error.name = name;
error.code = code;
error.syscall = syscall;

View File

@@ -85,14 +85,14 @@ function assignHeaders(object, req) {
}
}
function onIncomingMessagePauseNodeHTTPResponse(this: IncomingMessage) {
function onIncomingMessagePauseNodeHTTPResponse(this: typeof IncomingMessage) {
const handle = this[kHandle];
if (handle && !this.destroyed) {
handle.pause();
}
}
function onIncomingMessageResumeNodeHTTPResponse(this: IncomingMessage) {
function onIncomingMessageResumeNodeHTTPResponse(this: typeof IncomingMessage) {
const handle = this[kHandle];
if (handle && !this.destroyed) {
const resumed = handle.resume();
@@ -106,6 +106,10 @@ function onIncomingMessageResumeNodeHTTPResponse(this: IncomingMessage) {
}
}
interface IncomingMessage {
_dumped: boolean;
}
function IncomingMessage(req, options = defaultIncomingOpts) {
this[abortedSymbol] = false;
this[eofInProgress] = false;
@@ -168,13 +172,8 @@ function IncomingMessage(req, options = defaultIncomingOpts) {
this._readableState.readingMore = true;
}
function onDataIncomingMessage(
this: import("node:http").IncomingMessage,
chunk,
isLast,
aborted: NodeHTTPResponseAbortEvent,
) {
if (aborted === NodeHTTPResponseAbortEvent.abort) {
function onDataIncomingMessage(this: import("node:http").IncomingMessage, chunk, isLast, aborted: number) {
if (aborted === NodeHTTPResponseAbortEvent) {
this.destroy();
return;
}
@@ -305,7 +304,7 @@ const IncomingMessagePrototype = {
// Suppress "AbortError" from fetch() because we emit this in the 'aborted' event
if (isAbortError(err)) {
err = undefined;
err = null;
}
var nodeHTTPResponse = this[kHandle];
@@ -393,7 +392,6 @@ const IncomingMessagePrototype = {
// noop
},
setTimeout(msecs, callback) {
this.take;
const req = this[kHandle] || this[webRequestOrResponse];
if (req) {

View File

@@ -51,8 +51,9 @@ const { OutgoingMessage } = require("node:_http_outgoing");
const getBunServerAllClosedPromise = $newZigFunction("node_http_binding.zig", "getBunServerAllClosedPromise", 1);
const sendHelper = $newZigFunction("node_cluster_binding.zig", "sendHelperChild", 3);
const kIncomingMessage = Symbol("IncomingMessage");
const kServerResponse = Symbol("ServerResponse");
// Declare as unique symbols to fix TS1166 error
const kIncomingMessage: unique symbol = Symbol("IncomingMessage") as any;
const kServerResponse: unique symbol = Symbol("ServerResponse") as any;
const kRejectNonStandardBodyWrites = Symbol("kRejectNonStandardBodyWrites");
const GlobalPromise = globalThis.Promise;
const kEmptyBuffer = Buffer.alloc(0);
@@ -165,8 +166,9 @@ const ServerResponsePrototype = {
this._writeRaw(head, "ascii", cb);
},
writeProcessing(cb) {
writeProcessing(cb?) {
this._writeRaw("HTTP/1.1 102 Processing\r\n\r\n", "ascii", cb);
return this;
},
writeContinue(cb) {
this.socket[kHandle]?.response?.writeContinue();
@@ -175,18 +177,18 @@ const ServerResponsePrototype = {
// This end method is actually on the OutgoingMessage prototype in Node.js
// But we don't want it for the fetch() response version.
end(chunk, encoding, callback) {
end(chunk?: string | Buffer | Uint8Array, encoding?: BufferEncoding, callback?: () => void) {
const handle = this[kHandle];
if (handle?.aborted) {
return this;
}
if ($isCallable(chunk)) {
callback = chunk;
callback = chunk as () => void;
chunk = undefined;
encoding = undefined;
} else if ($isCallable(encoding)) {
callback = encoding;
callback = encoding as () => void;
encoding = undefined;
} else if (!$isCallable(callback)) {
callback = undefined;
@@ -284,15 +286,15 @@ const ServerResponsePrototype = {
return !this._ended || !hasServerResponseFinished(this);
},
write(chunk, encoding, callback) {
write(chunk?: string | Buffer | Uint8Array, encoding?: BufferEncoding, callback?: () => void) {
const handle = this[kHandle];
if ($isCallable(chunk)) {
callback = chunk;
callback = chunk as () => void;
chunk = undefined;
encoding = undefined;
} else if ($isCallable(encoding)) {
callback = encoding;
callback = encoding as () => void;
encoding = undefined;
} else if (!$isCallable(callback)) {
callback = undefined;
@@ -432,9 +434,9 @@ const ServerResponsePrototype = {
}
},
writeHead(statusCode, statusMessage, headers) {
writeHead(statusCode: number, statusMessage?: string | object, headers?: object) {
if (this[headerStateSymbol] === NodeHTTPHeaderState.none) {
_writeHead(statusCode, statusMessage, headers, this);
_writeHead(statusCode, statusMessage as string, headers, this);
updateHasBody(this, statusCode);
this[headerStateSymbol] = NodeHTTPHeaderState.assigned;
}
@@ -538,6 +540,24 @@ const ServerResponse_writeDeprecated = function _write(chunk, encoding, callback
});
};
// Add interface for NodeHTTPServerSocket to include _httpMessage
interface NodeHTTPServerSocket extends InstanceType<typeof Duplex> {
_httpMessage?: ServerResponse;
server: Server;
encrypted?: boolean;
_requestCount?: number;
}
// Define AddressInfo interface
interface AddressInfo {
address: string;
family: string;
port: number;
path?: string;
host?: string;
tls?: any;
}
function onNodeHTTPServerSocketTimeout() {
const req = this[kRequest];
const reqTimeout = req && !req.complete && req.emit("timeout", this);
@@ -582,7 +602,33 @@ function emitListeningNextTick(self, hostname, port) {
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
type Server = InstanceType<typeof Server>;
// Extended interface to ensure Server type has all required properties
interface HTTPServer<Request = IncomingMessage, Response = ServerResponse> extends EventEmitter {
maxRequestsPerSocket: number;
timeout: number;
keepAliveTimeout: number;
maxHeadersCount: number;
headersTimeout: number;
requestTimeout: number;
listening: boolean;
_unref: boolean;
insecureHTTPParser?: boolean;
joinDuplicateHeaders?: boolean;
requireHostHeader?: boolean;
rejectNonStandardBodyWrites?: boolean;
connectionsCheckingInterval?: number;
maxHeaderSize?: number;
setTimeout(msecs: number, callback?: () => void): this;
listen(...args: any[]): this;
close(callback?: (err?: Error) => void): void;
address(): AddressInfo | null;
ref(): this;
unref(): this;
closeAllConnections(): void;
closeIdleConnections(): void;
}
type Server<Request = IncomingMessage, Response = ServerResponse> = HTTPServer<Request, Response>;
const Server = function Server(options, callback) {
if (!(this instanceof Server)) return new Server(options, callback);
EventEmitter.$call(this);
@@ -666,8 +712,8 @@ const Server = function Server(options, callback) {
} as unknown as typeof import("node:http").Server;
Object.defineProperty(Server, "name", { value: "Server" });
function onServerRequestEvent(this: NodeHTTPServerSocket, event: NodeHTTPResponseAbortEvent) {
const socket: NodeHTTPServerSocket = this;
function onServerRequestEvent(this: NodeHTTPServerSocket, event: number) {
const socket = this as NodeHTTPServerSocket;
switch (event) {
case NodeHTTPResponseAbortEvent.abort: {
if (!socket.destroyed) {
@@ -685,8 +731,10 @@ function onServerRequestEvent(this: NodeHTTPServerSocket, event: NodeHTTPRespons
const ServerPrototype = {
constructor: Server,
__proto__: EventEmitter.prototype,
[kIncomingMessage]: undefined,
[kServerResponse]: undefined,
// Using string property names instead of computed property names for symbols
// to avoid TS1166 error: Computed property name must be a simple literal or unique symbol
kIncomingMessage: undefined,
kServerResponse: undefined,
ref() {
this._unref = false;
this[serverSymbol]?.ref?.();
@@ -732,9 +780,9 @@ const ServerPrototype = {
return promise;
},
address() {
address(): AddressInfo | null {
if (!this[serverSymbol]) return null;
return this[serverSymbol].address;
return this[serverSymbol].address as AddressInfo;
},
listen() {
@@ -826,7 +874,7 @@ const ServerPrototype = {
data: null,
addressType: 4,
};
sendHelper(message, null);
sendHelper(message, null, null);
});
server[kRealListen](tls, port, host, socketPath, true, onListen);
@@ -994,7 +1042,7 @@ const ServerPrototype = {
http_res.on("finish", http_res.detachSocket.bind(http_res, socket));
}
const { resolve, promise } = $newPromiseCapability(Promise);
const { resolve, promise } = $newPromiseCapability(GlobalPromise);
resolveFunction = resolve;
return promise;

View File

@@ -45,7 +45,47 @@ const StringPrototypeSlice = String.prototype.slice;
const StringPrototypeSplit = String.prototype.split;
const SymbolIterator = Symbol.iterator;
type nodeAssert = typeof import("node:assert");
// Use interface instead of type to avoid "refers to a type, but used as a namespace" error
interface AssertPredicate {
(actual: unknown): boolean;
name?: string;
}
// Define a proper interface for nodeAssert
interface nodeAssert {
(value: unknown, message?: string | Error): asserts value;
ok(value: unknown, message?: string | Error): asserts value;
fail(message?: string | Error): never;
fail(actual: unknown, expected: unknown, message?: string | Error, operator?: string, stackStartFn?: Function): never;
equal(actual: unknown, expected: unknown, message?: string | Error): void;
notEqual(actual: unknown, expected: unknown, message?: string | Error): void;
deepEqual(actual: unknown, expected: unknown, message?: string | Error): void;
notDeepEqual(actual: unknown, expected: unknown, message?: string | Error): void;
strictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
notStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
deepStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
notDeepStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
throws(block: () => unknown, message?: string | Error): void;
throws(block: () => unknown, error: RegExp | Function | Object | Error, message?: string | Error): void;
doesNotThrow(block: () => unknown, message?: string | Error): void;
doesNotThrow(block: () => unknown, error: RegExp | Function | Object | Error, message?: string | Error): void;
rejects(block: (() => Promise<unknown>) | Promise<unknown>, message?: string | Error): Promise<void>;
rejects(
block: (() => Promise<unknown>) | Promise<unknown>,
error: RegExp | Function | Object | Error,
message?: string | Error,
): Promise<void>;
doesNotReject(block: (() => Promise<unknown>) | Promise<unknown>, message?: string | Error): Promise<void>;
doesNotReject(
block: (() => Promise<unknown>) | Promise<unknown>,
error: RegExp | Function | Object | Error,
message?: string | Error,
): Promise<void>;
ifError(value: unknown): void;
match(string: string, regexp: RegExp, message?: string | Error): void;
doesNotMatch(string: string, regexp: RegExp, message?: string | Error): void;
strict: nodeAssert;
}
function isDeepEqual(a, b) {
return Bun.deepEquals(a, b, false);
@@ -86,7 +126,15 @@ const NO_EXCEPTION_SENTINEL = {};
// both the actual and expected values to the assertion error for
// display purposes.
function innerFail(obj) {
interface FailureInfo {
actual: unknown;
expected: unknown;
message?: string | Error;
operator: string;
stackStartFn: Function;
}
function innerFail(obj: FailureInfo) {
if (obj.message instanceof Error) throw obj.message;
throw new AssertionError(obj);
@@ -116,7 +164,7 @@ function fail(
internalMessage = true;
message = "Failed";
} else if (argsLen === 1) {
message = actual;
message = actual as string | Error | undefined;
actual = undefined;
} else {
if (warned === false) {
@@ -172,8 +220,8 @@ Object.defineProperty(assert, "AssertionError", {
*/
function ok(value: unknown, message?: string | Error): asserts value;
function ok(...args: unknown[]): void {
innerOk(ok, args.length, ...args);
function ok(value: unknown, message?: string | Error): void {
innerOk(ok, arguments.length, value, message);
}
assert.ok = ok;
@@ -508,7 +556,7 @@ assert.partialDeepStrictEqual = function partialDeepStrictEqual(actual, expected
};
class Comparison {
constructor(obj, keys, actual) {
constructor(obj, keys, actual?) {
for (const key of keys) {
if (key in obj) {
if (
@@ -787,49 +835,69 @@ function expectsNoError(stackStartFn, actual, error, message) {
}
/**
* Expects the function `promiseFn` to throw an error.
* @param {() => any} promiseFn
* @param {...any} [args]
* Expects the function `fn` to throw an error.
* @param {() => any} fn
* @param {nodeAssert.AssertPredicate} [error]
* @param {string | Error} [message]
* @returns {void}
*/
assert.throws = function throws(promiseFn: () => Promise<unknown> | Promise<unknown>, ...args: unknown[]): void {
expectsError(throws, getActual(promiseFn), ...args);
assert.throws = function throws(
fn: () => unknown,
error?: RegExp | Function | Object | Error,
message?: string | Error,
): void {
expectsError(throws, getActual(fn), error, message);
};
/**
* Expects `promiseFn` function or its value to reject.
* @param {() => Promise<any>} promiseFn
* @param {...any} [args]
* Expects `block` function or its value to reject.
* @param {(() => Promise<unknown>) | Promise<unknown>} block
* @param {RegExp | Function | Object | Error} [error]
* @param {string | Error} [message]
* @returns {Promise<void>}
*/
function rejects(block: (() => Promise<unknown>) | Promise<unknown>, message?: string | Error): Promise<void>;
function rejects(
block: (() => Promise<unknown>) | Promise<unknown>,
error: nodeAssert.AssertPredicate,
error: RegExp | Function | Object | Error,
message?: string | Error,
): Promise<void>;
assert.rejects = async function rejects(promiseFn: () => Promise<unknown>, ...args: any[]): Promise<void> {
expectsError(rejects, await waitForActual(promiseFn), ...args);
assert.rejects = async function rejects(
block: (() => Promise<unknown>) | Promise<unknown>,
error?: RegExp | Function | Object | Error,
message?: string | Error,
): Promise<void> {
expectsError(rejects, await waitForActual(block), error, message);
};
/**
* Asserts that the function `fn` does not throw an error.
* @param {() => any} fn
* @param {...any} [args]
* @param {RegExp | Function | Object | Error} [error]
* @param {string | Error} [message]
* @returns {void}
*/
assert.doesNotThrow = function doesNotThrow(fn: () => Promise<unknown>, ...args: unknown[]): void {
expectsNoError(doesNotThrow, getActual(fn), ...args);
assert.doesNotThrow = function doesNotThrow(
fn: () => unknown,
error?: RegExp | Function | Object | Error,
message?: string | Error,
): void {
expectsNoError(doesNotThrow, getActual(fn), error, message);
};
/**
* Expects `fn` or its value to not reject.
* @param {() => Promise<any>} fn
* @param {...any} [args]
* @param {(() => Promise<unknown>) | Promise<unknown>} fn
* @param {RegExp | Function | Object | Error} [error]
* @param {string | Error} [message]
* @returns {Promise<void>}
*/
assert.doesNotReject = async function doesNotReject(fn: () => Promise<unknown>, ...args: unknown[]): Promise<void> {
expectsNoError(doesNotReject, await waitForActual(fn), ...args);
assert.doesNotReject = async function doesNotReject(
fn: (() => Promise<unknown>) | Promise<unknown>,
error?: RegExp | Function | Object | Error,
message?: string | Error,
): Promise<void> {
expectsNoError(doesNotReject, await waitForActual(fn), error, message);
};
/**
@@ -840,11 +908,11 @@ assert.doesNotReject = async function doesNotReject(fn: () => Promise<unknown>,
assert.ifError = function ifError(err: unknown): void {
if (err !== null && err !== undefined) {
let message = "ifError got unwanted exception: ";
if (typeof err === "object" && typeof err.message === "string") {
if (err.message.length === 0 && err.constructor) {
message += err.constructor.name;
if (typeof err === "object" && typeof (err as Error).message === "string") {
if ((err as Error).message.length === 0 && (err as Error).constructor) {
message += (err as Error).constructor.name;
} else {
message += err.message;
message += (err as Error).message;
}
} else {
const inspect = lazyInspect();
@@ -861,7 +929,7 @@ assert.ifError = function ifError(err: unknown): void {
});
// Make sure we actually have a stack trace!
const origStack = err.stack;
const origStack = (err as Error).stack;
if (typeof origStack === "string") {
// This will remove any duplicated frames from the error frames taken
@@ -976,11 +1044,24 @@ function strict(...args) {
innerOk(strict, args.length, ...args);
}
assert.strict = ObjectAssign(strict, assert, {
// Create a new strict assert object to avoid modifying the readonly property
const strictAssert = ObjectAssign(strict, assert, {
equal: assert.strictEqual,
deepEqual: assert.deepStrictEqual,
notEqual: assert.notStrictEqual,
notDeepEqual: assert.notDeepStrictEqual,
});
assert.strict.strict = assert.strict;
// Now assign it to assert.strict
Object.defineProperty(assert, "strict", {
value: strictAssert,
writable: true,
configurable: true,
});
// Set strict.strict to itself in a way that avoids the readonly property error
Object.defineProperty(assert.strict, "strict", {
value: assert.strict,
writable: true,
configurable: true,
});

View File

@@ -297,7 +297,7 @@ function execFile(file, args, options, callback) {
});
}
ex.cmd = cmd;
(ex as Error & { cmd?: string }).cmd = cmd;
callback(ex, stdout, stderr);
}
@@ -322,7 +322,7 @@ function execFile(file, args, options, callback) {
try {
child.kill(options.killSignal);
} catch (e) {
ex = e;
ex = e as Error | null;
exitHandler();
}
}
@@ -520,11 +520,11 @@ function spawnSync(file, args, options) {
var {
stdout = null,
stderr = null,
exitCode,
signalCode,
exitCode = 0,
signalCode = null,
exitedDueToTimeout,
exitedDueToMaxBuffer,
pid,
pid = -1,
} = Bun.spawnSync({
// normalizeSpawnargs has already prepended argv0 to the spawnargs array
// Bun.spawn() expects cmd[0] to be the command to run, and argv0 to replace the first arg when running the command,
@@ -546,7 +546,17 @@ function spawnSync(file, args, options) {
stderr = null;
}
const result = {
interface SpawnSyncResult {
signal: string | null;
status: number;
output: [null, Buffer | string | null, Buffer | string | null];
pid: number;
error?: Error;
stdout?: Buffer | string | null;
stderr?: Buffer | string | null;
}
const result: SpawnSyncResult = {
signal: signalCode ?? null,
status: exitCode,
// TODO: Need to expose extra pipes from Bun.spawnSync to child_process
@@ -559,11 +569,11 @@ function spawnSync(file, args, options) {
}
if (stdout && encoding && encoding !== "buffer") {
result.output[1] = result.output[1]?.toString(encoding);
result.output[1] = result.output[1] != null ? Buffer.from(result.output[1].toString()).toString(encoding) : null;
}
if (stderr && encoding && encoding !== "buffer") {
result.output[2] = result.output[2]?.toString(encoding);
result.output[2] = result.output[2] != null ? Buffer.from(result.output[2].toString()).toString(encoding) : null;
}
result.stdout = result.output[1];
@@ -1039,6 +1049,8 @@ class ChildProcess extends EventEmitter {
pid;
channel;
killed = false;
send?: (message: any, handle?: any, options?: any, callback?: Function) => boolean;
disconnect?: () => void;
[Symbol.dispose]() {
if (!this.killed) {
@@ -1146,10 +1158,11 @@ class ChildProcess extends EventEmitter {
// This can happen if the process was already killed.
if (!value) return new ShimmedStdioOutStream();
const pipe = require("internal/streams/native-readable").constructNativeReadable(value, { encoding });
const NativeReadable = require("internal/streams/native-readable");
const pipe = NativeReadable.constructNativeReadable(value, { encoding });
this.#closesNeeded++;
pipe.once("close", () => this.#maybeClose());
if (autoResume) pipe.resume();
if (autoResume && typeof pipe.resume === "function") pipe.resume();
return pipe;
}
case "destroyed":
@@ -1339,8 +1352,8 @@ class ChildProcess extends EventEmitter {
} catch (ex) {
if (ex == null || typeof ex !== "object" || !Object.hasOwn(ex, "errno")) throw ex;
this.#handle = null;
ex.syscall = "spawn " + this.spawnfile;
ex.spawnargs = Array.prototype.slice.$call(this.spawnargs, 1);
(ex as SystemErrorExtras).syscall = "spawn " + this.spawnfile;
(ex as SystemErrorExtras).spawnargs = Array.prototype.slice.$call(this.spawnargs, 1);
process.nextTick(() => {
this.emit("error", ex);
this.emit("close", (ex as SystemError).errno ?? -1);
@@ -1458,7 +1471,10 @@ const nodeToBunLookup = {
ipc: "ipc",
};
function nodeToBun(item: string, index: number): string | number | null | NodeJS.TypedArray | ArrayBufferView {
function nodeToBun(
item: string | number | any,
index: number,
): string | number | null | NodeJS.TypedArray | ArrayBufferView {
// If not defined, use the default.
// For stdin/stdout/stderr, it's pipe. For others, it's ignore.
if (item == null) {
@@ -1880,12 +1896,24 @@ function ERR_INVALID_OPT_VALUE(name, value) {
return err;
}
class SystemError extends Error {
path;
syscall;
errno;
code;
constructor(message, path, syscall, errno, code) {
interface SystemErrorExtras {
path?: string;
syscall?: string;
errno?: number;
code?: string;
pid?: number;
spawnargs?: string[];
}
class SystemError extends Error implements SystemErrorExtras {
path?: string;
syscall?: string;
errno?: number;
code?: string;
pid?: number;
spawnargs?: string[];
constructor(message: string, path?: string, syscall?: string, errno?: number, code?: string) {
super(message);
this.path = path;
this.syscall = syscall;

View File

@@ -1,7 +1,20 @@
// Hardcoded module "node:cluster"
const { isPrimary } = require("internal/cluster/isPrimary");
const cluster = isPrimary ? require("internal/cluster/primary") : require("internal/cluster/child");
// Define NodeJS.Cluster interface for TypeScript type checking
interface NodeJSCluster extends EventEmitter {
isWorker: boolean;
isMaster: boolean;
isPrimary: boolean;
worker?: any;
workers?: Record<string | number, any>;
Worker: any;
_setupWorker?: () => void;
// Add any other properties needed
}
const cluster = (isPrimary ? require("internal/cluster/primary") : require("internal/cluster/child")) as NodeJSCluster;
export default cluster;
//
@@ -9,7 +22,8 @@ export default cluster;
function initializeClusterIPC() {
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
cluster._setupWorker();
// Use type assertion to fix the TypeScript error
(cluster as NodeJSCluster & { _setupWorker: () => void })._setupWorker();
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}

View File

@@ -104,7 +104,7 @@ function getArrayBufferOrView(buffer, name, encoding?) {
if (buffer instanceof KeyObject) {
if (buffer.type !== "secret") {
const error = new TypeError(
`ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE: Invalid key object type ${key.type}, expected secret`,
`ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE: Invalid key object type ${buffer.type}, expected secret`,
);
error.code = "ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE";
throw error;

View File

@@ -98,6 +98,14 @@ function EINVAL(syscall) {
let dns;
// Define interface for UDP handle
interface UDPHandle {
lookup?: Function;
onmessage?: Function;
[kOwnerSymbol]?: any;
socket?: any;
}
function newHandle(type, lookup) {
if (lookup === undefined) {
if (dns === undefined) {
@@ -109,7 +117,7 @@ function newHandle(type, lookup) {
validateFunction(lookup, "lookup");
}
const handle = {};
const handle: UDPHandle = {};
if (type === "udp4") {
handle.lookup = FunctionPrototypeBind.$call(lookup4, handle, lookup);
} else if (type === "udp6") {
@@ -638,18 +646,24 @@ function doSend(ex, self, ip, list, address, port, callback) {
success = socket.send(data);
}
} catch (e) {
err = e;
// Define extended error type with networking properties
interface NetworkError extends Error {
address?: string;
port?: number;
code?: string;
}
err = e as NetworkError;
}
// TODO check if this makes sense
if (callback) {
if (err) {
err.address = ip;
err.port = port;
err.message = `send ${err.code} ${ip}:${port}`;
(err as NetworkError).address = ip;
(err as NetworkError).port = port;
(err as NetworkError).message = `send ${(err as NetworkError).code} ${ip}:${port}`;
process.nextTick(callback, err);
} else {
const sent = success ? data.byteLength : 0;
process.nextTick(callback, null, sent);
process.nextTick(callback, null as null, sent);
}
}

View File

@@ -46,15 +46,18 @@ class WeakRefMap extends SafeMap {
}
get(key) {
return super.get(key)?.get();
const ref = super.get(key);
return ref ? ref.get() : undefined;
}
incRef(key) {
return super.get(key)?.incRef();
const ref = super.get(key);
return ref ? ref.incRef() : undefined;
}
decRef(key) {
return super.get(key)?.decRef();
const ref = super.get(key);
return ref ? ref.decRef() : undefined;
}
}
@@ -306,17 +309,19 @@ class TracingChannel {
traceSync(fn, context = {}, thisArg, ...args) {
const { start, end, error } = this;
return start.runStores(context, () => {
// Use type assertion for context
const typedContext = context as { result?: any; error?: any };
return start.runStores(typedContext, () => {
try {
const result = fn.$apply(thisArg, args);
context.result = result;
typedContext.result = result;
return result;
} catch (err) {
context.error = err;
error.publish(context);
typedContext.error = err;
error.publish(typedContext);
throw err;
} finally {
end.publish(context);
end.publish(typedContext);
}
});
}
@@ -324,24 +329,27 @@ class TracingChannel {
tracePromise(fn, context = {}, thisArg, ...args) {
const { start, end, asyncStart, asyncEnd, error } = this;
// Use type assertion for context
const typedContext = context as { result?: any; error?: any };
function reject(err) {
context.error = err;
error.publish(context);
asyncStart.publish(context);
typedContext.error = err;
error.publish(typedContext);
asyncStart.publish(typedContext);
// TODO: Is there a way to have asyncEnd _after_ the continuation?
asyncEnd.publish(context);
asyncEnd.publish(typedContext);
return PromiseReject(err);
}
function resolve(result) {
context.result = result;
asyncStart.publish(context);
typedContext.result = result;
asyncStart.publish(typedContext);
// TODO: Is there a way to have asyncEnd _after_ the continuation?
asyncEnd.publish(context);
asyncEnd.publish(typedContext);
return result;
}
return start.runStores(context, () => {
return start.runStores(typedContext, () => {
try {
let promise = fn.$apply(thisArg, args);
// Convert thenables to native promises
@@ -350,11 +358,11 @@ class TracingChannel {
}
return PromisePrototypeThen(promise, resolve, reject);
} catch (err) {
context.error = err;
error.publish(context);
typedContext.error = err;
error.publish(typedContext);
throw err;
} finally {
end.publish(context);
end.publish(typedContext);
}
});
}
@@ -362,22 +370,25 @@ class TracingChannel {
traceCallback(fn, position = -1, context = {}, thisArg, ...args) {
const { start, end, asyncStart, asyncEnd, error } = this;
// Use type assertion for context
const typedContext = context as { result?: any; error?: any };
function wrappedCallback(err, res) {
if (err) {
context.error = err;
error.publish(context);
typedContext.error = err;
error.publish(typedContext);
} else {
context.result = res;
typedContext.result = res;
}
// Using runStores here enables manual context failure recovery
asyncStart.runStores(context, () => {
asyncStart.runStores(typedContext, () => {
try {
if (callback) {
return callback.$apply(this, arguments);
}
} finally {
asyncEnd.publish(context);
asyncEnd.publish(typedContext);
}
});
}
@@ -386,15 +397,15 @@ class TracingChannel {
validateFunction(callback, "callback");
ArrayPrototypeSplice.$call(args, position, 1, wrappedCallback);
return start.runStores(context, () => {
return start.runStores(typedContext, () => {
try {
return fn.$apply(thisArg, args);
} catch (err) {
context.error = err;
error.publish(context);
typedContext.error = err;
error.publish(typedContext);
throw err;
} finally {
end.publish(context);
end.publish(typedContext);
}
});
}

View File

@@ -1,5 +1,5 @@
// Hardcoded module "node:dns"
const dns = Bun.dns;
const dns = Bun.dns as unknown as DNS;
const utilPromisifyCustomSymbol = Symbol.for("nodejs.util.promisify.custom");
const { isIP } = require("./net");
const {
@@ -71,24 +71,34 @@ const getRuntimeDefaultResultOrderOption = $newZigFunction(
0,
);
function newResolver(options) {
if (!newResolver.zig) {
newResolver.zig = $newZigFunction("dns_resolver.zig", "DNSResolver.newResolver", 1);
}
return newResolver.zig(options);
// Add type definition for function with zig property
interface ResolverCreator extends Function {
zig?: Function;
}
function defaultResultOrder() {
if (typeof defaultResultOrder.value === "undefined") {
defaultResultOrder.value = getRuntimeDefaultResultOrderOption();
const newResolver: ResolverCreator = function (options) {
if (!(newResolver as ResolverCreator).zig) {
(newResolver as ResolverCreator).zig = $newZigFunction("dns_resolver.zig", "DNSResolver.newResolver", 1);
}
return (newResolver as ResolverCreator).zig!(options);
};
// Create interface for function with value property
interface ResultOrderFunction extends Function {
value?: string;
}
const defaultResultOrder: ResultOrderFunction = function () {
if (typeof (defaultResultOrder as ResultOrderFunction).value === "undefined") {
(defaultResultOrder as ResultOrderFunction).value = getRuntimeDefaultResultOrderOption();
}
return defaultResultOrder.value;
}
return (defaultResultOrder as ResultOrderFunction).value;
};
function setDefaultResultOrder(order) {
validateOrder(order);
defaultResultOrder.value = order;
(defaultResultOrder as ResultOrderFunction).value = order;
}
function getDefaultResultOrder() {
@@ -98,7 +108,8 @@ function getDefaultResultOrder() {
function setServersOn(servers, object) {
validateArray(servers, "servers");
const triples = [];
// Use a more specific type for DNS server entries
const triples: Array<[number, string, number]> = [];
servers.forEach((server, i) => {
validateString(server, `servers[${i}]`);
@@ -217,18 +228,23 @@ function validateLocalAddresses(first, second) {
}
}
function invalidHostname(hostname) {
if (invalidHostname.warned) {
// Create interface for function with warned property
interface WarningFunction extends Function {
warned?: boolean;
}
const invalidHostname: WarningFunction = function (hostname) {
if ((invalidHostname as WarningFunction).warned) {
return;
}
invalidHostname.warned = true;
(invalidHostname as WarningFunction).warned = true;
process.emitWarning(
`The provided hostname "${String(hostname)}" is not a valid hostname, and is supported in the dns module solely for compatibility.`,
"DeprecationWarning",
"DEP0118",
);
}
};
function translateLookupOptions(options) {
if (!options || typeof options !== "object") {
@@ -331,7 +347,10 @@ function lookupService(address, port, callback) {
validateString(address);
dns.lookupService(address, port).then(
// Convert port to string if it's a number
const portStr = typeof port === "number" ? String(port) : port;
dns.lookupService(address, portStr).then(
results => {
callback(null, ...results);
},
@@ -665,9 +684,17 @@ const mapLookupAll = res => {
return { address, family };
};
// Define DNS Error interface
interface DNSError extends Error {
name: string;
code: string;
errno: number;
syscall: string;
}
function throwIfEmpty(res) {
if (res.length === 0) {
const err = new Error("No records found");
const err = new Error("No records found") as DNSError;
err.name = "DNSException";
err.code = "ENODATA";
// Hardcoded errno
@@ -767,10 +794,12 @@ const promises = {
service,
}));
} catch (err) {
if (err.name === "TypeError" || err.name === "RangeError") {
throw err;
// Use type assertion to properly handle error object
const error = err as Error;
if (error.name === "TypeError" || error.name === "RangeError") {
throw error;
}
return Promise.reject(withTranslatedError(err));
return Promise.reject(withTranslatedError(error));
}
},
@@ -838,6 +867,7 @@ const promises = {
Resolver: class Resolver {
#resolver;
_handle: any;
constructor(options) {
validateResolverOptions(options);

View File

@@ -71,9 +71,10 @@ function EventEmitter(opts) {
}
}
Object.defineProperty(EventEmitter, "name", { value: "EventEmitter", configurable: true });
const EventEmitterPrototype = (EventEmitter.prototype = {});
// Define the prototype object with proper typing
const EventEmitterPrototype: EventEmitter = (EventEmitter.prototype = {} as EventEmitter);
EventEmitterPrototype.setMaxListeners = function setMaxListeners(n) {
EventEmitterPrototype.setMaxListeners = function setMaxListeners(this: EventEmitter, n) {
validateNumber(n, "setMaxListeners", 0);
this._maxListeners = n;
return this;
@@ -82,12 +83,12 @@ Object.defineProperty(EventEmitterPrototype.setMaxListeners, "name", { value: "s
EventEmitterPrototype.constructor = EventEmitter;
EventEmitterPrototype.getMaxListeners = function getMaxListeners() {
EventEmitterPrototype.getMaxListeners = function getMaxListeners(this: EventEmitter) {
return _getMaxListeners(this);
};
Object.defineProperty(EventEmitterPrototype.getMaxListeners, "name", { value: "getMaxListeners" });
function emitError(emitter, args) {
function emitError(emitter: EventEmitter, args: any[]): boolean {
var { _events: events } = emitter;
if (events !== undefined) {
@@ -127,14 +128,14 @@ function emitError(emitter, args) {
throw err; // Unhandled 'error' event
}
function addCatch(emitter, promise, type, args) {
function addCatch(emitter: EventEmitter, promise: Promise<any>, type: string, args: any[]): void {
promise.then(undefined, function (err) {
// The callback is called with nextTick to avoid a follow-up rejection from this promise.
process.nextTick(emitUnhandledRejectionOrErr, emitter, err, type, args);
});
}
function emitUnhandledRejectionOrErr(emitter, err, type, args) {
function emitUnhandledRejectionOrErr(emitter: EventEmitter, err: Error, type: string, args: any[]): void {
if (typeof emitter[kRejection] === "function") {
emitter[kRejection](err, type, ...args);
} else {
@@ -150,7 +151,7 @@ function emitUnhandledRejectionOrErr(emitter, err, type, args) {
}
}
const emitWithoutRejectionCapture = function emit(type, ...args) {
const emitWithoutRejectionCapture = function emit(this: EventEmitter, type: string, ...args: any[]): boolean {
$debug(`${this.constructor?.name || "EventEmitter"}.emit`, type);
if (type === "error") {
@@ -188,7 +189,7 @@ const emitWithoutRejectionCapture = function emit(type, ...args) {
return true;
};
const emitWithRejectionCapture = function emit(type, ...args) {
const emitWithRejectionCapture = function emit(this: EventEmitter, type: string, ...args: any[]): boolean {
$debug(`${this.constructor?.name || "EventEmitter"}.emit`, type);
if (type === "error") {
return emitError(this, args);
@@ -229,20 +230,24 @@ const emitWithRejectionCapture = function emit(type, ...args) {
return true;
};
EventEmitterPrototype.emit = emitWithoutRejectionCapture;
EventEmitterPrototype.emit = emitWithoutRejectionCapture as (
this: EventEmitter,
type: string,
...args: any[]
) => boolean;
EventEmitterPrototype.addListener = function addListener(type, fn) {
EventEmitterPrototype.addListener = function addListener(this: EventEmitter, type, fn) {
checkListener(fn);
var events = this._events;
if (!events) {
events = this._events = { __proto__: null };
if (events === undefined) {
this._events = { __proto__: null };
this._eventsCount = 0;
} else if (events.newListener) {
this.emit("newListener", type, fn.listener ?? fn);
}
var handlers = events[type];
if (!handlers) {
events[type] = [fn];
events![type] = [fn];
this._eventsCount++;
} else {
handlers.push(fn);
@@ -254,20 +259,24 @@ EventEmitterPrototype.addListener = function addListener(type, fn) {
return this;
};
EventEmitterPrototype.on = EventEmitterPrototype.addListener;
EventEmitterPrototype.on = EventEmitterPrototype.addListener as (
this: EventEmitter,
type: string,
fn: Function,
) => EventEmitter;
EventEmitterPrototype.prependListener = function prependListener(type, fn) {
EventEmitterPrototype.prependListener = function prependListener(this: EventEmitter, type, fn) {
checkListener(fn);
var events = this._events;
if (!events) {
events = this._events = { __proto__: null };
if (events === undefined) {
this._events = { __proto__: null };
this._eventsCount = 0;
} else if (events.newListener) {
this.emit("newListener", type, fn.listener ?? fn);
}
var handlers = events[type];
if (!handlers) {
events[type] = [fn];
events![type] = [fn];
this._eventsCount++;
} else {
handlers.unshift(fn);
@@ -279,11 +288,13 @@ EventEmitterPrototype.prependListener = function prependListener(type, fn) {
return this;
};
// Using MaxListenersWarning interface from private.d.ts
function overflowWarning(emitter, type, handlers) {
handlers.warned = true;
const warn = new Error(
`Possible EventTarget memory leak detected. ${handlers.length} ${String(type)} listeners added to ${inspect(emitter, { depth: -1 })}. MaxListeners is ${emitter._maxListeners}. Use events.setMaxListeners() to increase limit`,
);
) as MaxListenersWarning;
warn.name = "MaxListenersExceededWarning";
warn.emitter = emitter;
warn.type = type;
@@ -291,37 +302,49 @@ function overflowWarning(emitter, type, handlers) {
process.emitWarning(warn);
}
function _onceWrap(target, type, listener) {
const state = { fired: false, wrapFn: undefined, target, type, listener };
const wrapped = onceWrapper.bind(state);
// Using WrappedListener interface from private.d.ts
function _onceWrap(target: EventEmitter, type: string, listener: Function): WrappedListener {
// Initialize wrapFn as null so TypeScript doesn't complain about undefined
const state = { fired: false, wrapFn: null as any, target, type, listener };
const wrapped = onceWrapper.bind(state) as WrappedListener;
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
function onceWrapper() {
function onceWrapper(this: {
fired: boolean;
wrapFn: Function;
target: EventEmitter;
type: string;
listener: Function;
}): void {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0) return this.listener.$call(this.target);
return this.listener.$apply(this.target, arguments);
if (arguments.length === 0) {
this.listener.$call(this.target);
} else {
this.listener.$apply(this.target, arguments);
}
}
}
EventEmitterPrototype.once = function once(type, fn) {
EventEmitterPrototype.once = function once(this: EventEmitter, type, fn) {
checkListener(fn);
this.on(type, _onceWrap(this, type, fn));
return this;
};
Object.defineProperty(EventEmitterPrototype.once, "name", { value: "once" });
EventEmitterPrototype.prependOnceListener = function prependOnceListener(type, fn) {
EventEmitterPrototype.prependOnceListener = function prependOnceListener(this: EventEmitter, type, fn) {
checkListener(fn);
this.prependListener(type, _onceWrap(this, type, fn));
return this;
};
EventEmitterPrototype.removeListener = function removeListener(type, listener) {
EventEmitterPrototype.removeListener = function removeListener(this: EventEmitter, type, listener) {
checkListener(listener);
const events = this._events;
@@ -343,7 +366,7 @@ EventEmitterPrototype.removeListener = function removeListener(type, listener) {
else ArrayPrototypeSplice.$call(list, position, 1);
if (list.length === 0) {
delete events[type];
delete events![type];
this._eventsCount--;
}
@@ -352,16 +375,20 @@ EventEmitterPrototype.removeListener = function removeListener(type, listener) {
return this;
};
EventEmitterPrototype.off = EventEmitterPrototype.removeListener;
EventEmitterPrototype.off = EventEmitterPrototype.removeListener as (
this: EventEmitter,
type: string,
listener: Function,
) => EventEmitter;
EventEmitterPrototype.removeAllListeners = function removeAllListeners(type) {
EventEmitterPrototype.removeAllListeners = function removeAllListeners(this: EventEmitter, type?: string | symbol) {
const events = this._events;
if (events === undefined) return this;
if (events.removeListener === undefined) {
if (type) {
if (events[type]) {
delete events[type];
if (events![type]) {
delete events![type];
this._eventsCount--;
}
} else {
@@ -372,8 +399,11 @@ EventEmitterPrototype.removeAllListeners = function removeAllListeners(type) {
// Emit removeListener for all listeners on all events
if (!type) {
for (const key of ReflectOwnKeys(events)) {
// Type annotation to tell TypeScript we know what we're doing
const keys: Array<string | symbol> = ReflectOwnKeys(events);
for (const key of keys) {
if (key === "removeListener") continue;
// @ts-ignore: TypeScript doesn't understand symbols can be used as event names
this.removeAllListeners(key);
}
this.removeAllListeners("removeListener");
@@ -384,11 +414,11 @@ EventEmitterPrototype.removeAllListeners = function removeAllListeners(type) {
// emit in LIFO order
const listeners = events[type];
for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]);
for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type as string, listeners[i]);
return this;
};
EventEmitterPrototype.listeners = function listeners(type) {
EventEmitterPrototype.listeners = function listeners(this: EventEmitter, type) {
var { _events: events } = this;
if (!events) return [];
var handlers = events[type];
@@ -396,7 +426,7 @@ EventEmitterPrototype.listeners = function listeners(type) {
return handlers.map(x => x.listener ?? x);
};
EventEmitterPrototype.rawListeners = function rawListeners(type) {
EventEmitterPrototype.rawListeners = function rawListeners(this: EventEmitter, type) {
var { _events } = this;
if (!_events) return [];
var handlers = _events[type];
@@ -404,7 +434,7 @@ EventEmitterPrototype.rawListeners = function rawListeners(type) {
return handlers.slice();
};
EventEmitterPrototype.listenerCount = function listenerCount(type, method) {
EventEmitterPrototype.listenerCount = function listenerCount(this: EventEmitter, type, method) {
var { _events: events } = this;
if (!events) return 0;
if (method != null) {
@@ -420,13 +450,15 @@ EventEmitterPrototype.listenerCount = function listenerCount(type, method) {
};
Object.defineProperty(EventEmitterPrototype.listenerCount, "name", { value: "listenerCount" });
EventEmitterPrototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
EventEmitterPrototype.eventNames = function eventNames(this: EventEmitter) {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events!) : [];
};
EventEmitterPrototype[kCapture] = false;
function once(emitter, type, options = kEmptyObject) {
// Using EventOptions interface from private.d.ts
function once(emitter: EventEmitter | JSEventTarget, type: string, options: EventOptions = {} as EventOptions) {
validateObject(options, "options");
var signal = options?.signal;
validateAbortSignal(signal, "options.signal");
@@ -434,27 +466,27 @@ function once(emitter, type, options = kEmptyObject) {
throw $makeAbortError(undefined, { cause: signal?.reason });
}
const { resolve, reject, promise } = $newPromiseCapability(Promise);
const errorListener = err => {
emitter.removeListener(type, resolver);
const errorListener = (err: Error) => {
(emitter as EventEmitter).removeListener?.(type, resolver);
if (signal != null) {
eventTargetAgnosticRemoveListener(signal, "abort", abortListener);
eventTargetAgnosticRemoveListener(signal as unknown as JSEventTarget, "abort", abortListener);
}
reject(err);
};
const resolver = (...args) => {
if (typeof emitter.removeListener === "function") {
emitter.removeListener("error", errorListener);
const resolver = (...args: any[]) => {
if (typeof (emitter as EventEmitter).removeListener === "function") {
(emitter as EventEmitter).removeListener("error", errorListener);
}
if (signal != null) {
eventTargetAgnosticRemoveListener(signal, "abort", abortListener);
eventTargetAgnosticRemoveListener(signal as unknown as JSEventTarget, "abort", abortListener);
}
resolve(args);
};
eventTargetAgnosticAddListener(emitter, type, resolver, { once: true });
if (type !== "error" && typeof emitter.once === "function") {
if (type !== "error" && typeof (emitter as EventEmitter).once === "function") {
// EventTarget does not have `error` event semantics like Node
// EventEmitters, we listen to `error` events only on EventEmitters.
emitter.once("error", errorListener);
(emitter as EventEmitter).once("error", errorListener);
}
function abortListener() {
eventTargetAgnosticRemoveListener(emitter, type, resolver);
@@ -462,7 +494,7 @@ function once(emitter, type, options = kEmptyObject) {
reject($makeAbortError(undefined, { cause: signal?.reason }));
}
if (signal != null) {
eventTargetAgnosticAddListener(signal, "abort", abortListener, { once: true });
eventTargetAgnosticAddListener(signal as unknown as JSEventTarget, "abort", abortListener, { once: true });
}
return promise;
@@ -470,10 +502,16 @@ function once(emitter, type, options = kEmptyObject) {
Object.defineProperty(once, "name", { value: "once" });
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
function createIterResult(value, done) {
function createIterResult(value: any, done: boolean): { value: any; done: boolean } {
return { value, done };
}
function on(emitter, event, options = kEmptyObject) {
// Using EventStreamOptions interface from private.d.ts
function on(
emitter: EventEmitter | JSEventTarget,
event: string,
options: EventStreamOptions = {} as EventStreamOptions,
) {
// Parameters validation
validateObject(options, "options");
const signal = options.signal;
@@ -491,7 +529,7 @@ function on(emitter, event, options = kEmptyObject) {
const unconsumedEvents = new FixedQueue();
const unconsumedPromises = new FixedQueue();
let paused = false;
let error = null;
let error: Error | null = null;
let finished = false;
let size = 0;
@@ -503,7 +541,7 @@ function on(emitter, event, options = kEmptyObject) {
const value = unconsumedEvents.shift();
size--;
if (paused && size < lowWatermark) {
emitter.resume();
(emitter as EventEmitter).resume?.();
paused = false;
}
return Promise.resolve(createIterResult(value, false));
@@ -532,7 +570,7 @@ function on(emitter, event, options = kEmptyObject) {
return closeHandler();
},
throw(err) {
throw(err: Error) {
if (!err || !(err instanceof Error)) {
throw $ERR_INVALID_ARG_TYPE("EventEmitter.AsyncIterator", "Error", err);
}
@@ -566,11 +604,11 @@ function on(emitter, event, options = kEmptyObject) {
event,
options[kFirstEventParam]
? eventHandler
: function (...args) {
: function (...args: any[]) {
return eventHandler(args);
},
);
if (event !== "error" && typeof emitter.on === "function") {
if (event !== "error" && typeof (emitter as EventEmitter).on === "function") {
addEventListener(emitter, "error", errorHandler);
}
const closeEvents = options?.close;
@@ -588,18 +626,18 @@ function on(emitter, event, options = kEmptyObject) {
errorHandler($makeAbortError(undefined, { cause: signal?.reason }));
}
function eventHandler(value) {
function eventHandler(value: any) {
if (unconsumedPromises.isEmpty()) {
size++;
if (!paused && size > highWatermark) {
paused = true;
emitter.pause();
(emitter as EventEmitter).pause?.();
}
unconsumedEvents.push(value);
} else unconsumedPromises.shift().resolve(createIterResult(value, false));
}
function errorHandler(err) {
function errorHandler(err: Error) {
if (unconsumedPromises.isEmpty()) error = err;
else unconsumedPromises.shift().reject(err);
@@ -621,38 +659,38 @@ function on(emitter, event, options = kEmptyObject) {
Object.defineProperty(on, "name", { value: "on" });
function listenersController() {
const listeners = [];
const listeners: Array<[EventEmitter | JSEventTarget, string, Function, any]> = [];
return {
addEventListener(emitter, event, handler, flags) {
addEventListener(emitter: EventEmitter | JSEventTarget, event: string, handler: Function, flags?: any) {
eventTargetAgnosticAddListener(emitter, event, handler, flags);
listeners.push([emitter, event, handler, flags]);
},
removeAll() {
while (listeners.length > 0) {
const [emitter, event, handler, flags] = listeners.pop();
const [emitter, event, handler, flags] = listeners.pop()!;
eventTargetAgnosticRemoveListener(emitter, event, handler, flags);
}
},
};
}
const getEventListenersForEventTarget = $newCppFunction(
const getEventListenersForEventTarget = $newCppFunction<(target: any, type: string) => Function[]>(
"JSEventTargetNode.cpp",
"jsFunctionNodeEventsGetEventListeners",
1,
);
function getEventListeners(emitter, type) {
if ($isCallable(emitter?.listeners)) {
return emitter.listeners(type);
function getEventListeners(emitter: EventEmitter | JSEventTarget, type: string): Function[] {
if ($isCallable((emitter as EventEmitter)?.listeners)) {
return (emitter as EventEmitter).listeners(type);
}
return getEventListenersForEventTarget(emitter, type);
}
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/events.js#L315-L339
function setMaxListeners(n = defaultMaxListeners, ...eventTargets) {
function setMaxListeners(n = defaultMaxListeners, ...eventTargets: Array<EventEmitter | any>) {
validateNumber(n, "setMaxListeners", 0);
if (eventTargets.length === 0) {
defaultMaxListeners = n;
@@ -663,7 +701,7 @@ function setMaxListeners(n = defaultMaxListeners, ...eventTargets) {
target[kMaxEventTargetListeners] = n;
target[kMaxEventTargetListenersWarned] = false;
} else if (typeof target.setMaxListeners === "function") {
target.setMaxListeners(n);
(target as EventEmitter).setMaxListeners(n);
} else {
throw $ERR_INVALID_ARG_TYPE("eventTargets", ["EventEmitter", "EventTarget"], target);
}
@@ -672,13 +710,13 @@ function setMaxListeners(n = defaultMaxListeners, ...eventTargets) {
}
Object.defineProperty(setMaxListeners, "name", { value: "setMaxListeners" });
const jsEventTargetGetEventListenersCount = $newCppFunction(
const jsEventTargetGetEventListenersCount = $newCppFunction<(target: any, type: string) => number | undefined>(
"JSEventTarget.cpp",
"jsEventTargetGetEventListenersCount",
2,
);
function listenerCount(emitter, type) {
function listenerCount(emitter: EventEmitter | any, type: string): number {
if ($isCallable(emitter.listenerCount)) {
return emitter.listenerCount(type);
}
@@ -692,7 +730,7 @@ function listenerCount(emitter, type) {
}
Object.defineProperty(listenerCount, "name", { value: "listenerCount" });
function listenerCountSlow(emitter, type) {
function listenerCountSlow(emitter: any, type: string): number {
const events = emitter._events;
if (events !== undefined) {
const evlistener = events[type];
@@ -705,41 +743,51 @@ function listenerCountSlow(emitter, type) {
return 0;
}
function eventTargetAgnosticRemoveListener(emitter, name, listener, flags?) {
if (typeof emitter.removeListener === "function") {
emitter.removeListener(name, listener);
} else if (typeof emitter.removeEventListener === "function") {
emitter.removeEventListener(name, listener, flags);
function eventTargetAgnosticRemoveListener(
emitter: EventEmitter | JSEventTarget,
name: string,
listener: Function,
flags?: any,
): void {
if (typeof (emitter as EventEmitter).removeListener === "function") {
(emitter as EventEmitter).removeListener(name, listener);
} else if (typeof (emitter as JSEventTarget).removeEventListener === "function") {
(emitter as JSEventTarget).removeEventListener(name, listener, flags);
} else {
throw $ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter);
}
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === "function") {
function eventTargetAgnosticAddListener(
emitter: EventEmitter | JSEventTarget,
name: string,
listener: Function,
flags?: any,
): void {
if (typeof (emitter as EventEmitter).on === "function") {
if (flags?.once) {
emitter.once(name, listener);
(emitter as EventEmitter).once(name, listener);
} else {
emitter.on(name, listener);
(emitter as EventEmitter).on(name, listener);
}
} else if (typeof emitter.addEventListener === "function") {
emitter.addEventListener(name, listener, flags);
} else if (typeof (emitter as JSEventTarget).addEventListener === "function") {
(emitter as JSEventTarget).addEventListener(name, listener, flags);
} else {
throw $ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter);
}
}
function checkListener(listener) {
function checkListener(listener: any): void {
validateFunction(listener, "listener");
}
function _getMaxListeners(emitter) {
function _getMaxListeners(emitter: EventEmitter | null | undefined): number {
return emitter?._maxListeners ?? defaultMaxListeners;
}
let AsyncResource = null;
function getMaxListeners(emitterOrTarget) {
function getMaxListeners(emitterOrTarget: EventEmitter | any): number {
if (typeof emitterOrTarget?.getMaxListeners === "function") {
return _getMaxListeners(emitterOrTarget);
} else if (types.isEventTarget(emitterOrTarget)) {
@@ -751,7 +799,7 @@ function getMaxListeners(emitterOrTarget) {
Object.defineProperty(getMaxListeners, "name", { value: "getMaxListeners" });
// Copy-pasta from Node.js source code
function addAbortListener(signal, listener) {
function addAbortListener(signal: AbortSignal, listener: Function): { [Symbol.dispose]: () => void } {
if (signal === undefined) {
throw $ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal);
}
@@ -761,7 +809,7 @@ function addAbortListener(signal, listener) {
throw $ERR_INVALID_ARG_TYPE("listener", "function", listener);
}
let removeEventListener;
let removeEventListener: (() => void) | undefined;
if (signal.aborted) {
queueMicrotask(() => listener());
} else {
@@ -771,32 +819,42 @@ function addAbortListener(signal, listener) {
};
}
return {
__proto__: null,
[Symbol.dispose]() {
removeEventListener?.();
},
};
}
class EventEmitterAsyncResource extends EventEmitter {
triggerAsyncId;
asyncResource;
let AsyncResourceModule: any = null;
constructor(options) {
if (!AsyncResource) {
AsyncResource = require("node:async_hooks").AsyncResource;
// @ts-ignore: EventEmitter is not a constructor function type in TypeScript's view
class EventEmitterAsyncResource extends EventEmitter {
triggerAsyncId: number;
asyncResource: any;
constructor(options?: {
captureRejections?: boolean;
triggerAsyncId?: number;
name?: string;
requireManualDestroy?: boolean;
}) {
// We need to require the module the first time
if (!AsyncResourceModule) {
AsyncResourceModule = require("node:async_hooks");
}
var { captureRejections = false, triggerAsyncId, name = new.target.name, requireManualDestroy } = options || {};
super({ captureRejections });
this.triggerAsyncId = triggerAsyncId ?? 0;
this.asyncResource = new AsyncResource(name, { triggerAsyncId, requireManualDestroy });
// @ts-ignore - AsyncResource constructor works, TypeScript doesn't know it
this.asyncResource = new AsyncResourceModule.AsyncResource(name, { triggerAsyncId, requireManualDestroy });
}
emit(...args) {
emit(...args: any[]): boolean {
this.asyncResource.runInAsyncScope(() => super.emit(...args));
return true;
}
emitDestroy() {
emitDestroy(): void {
this.asyncResource.emitDestroy();
}
}
@@ -852,4 +910,5 @@ Object.assign(EventEmitter, {
listenerCount,
});
// Use type assertion to ensure compatibility with Node.js events module
export default EventEmitter as any as typeof import("node:events");

View File

@@ -82,7 +82,8 @@ function watch(
return() {
if (!closed) {
watcher.close();
// watcher is the Promise result from fs.watch() that returns a FSWatcher
(watcher as any).close();
closed = true;
if (nextEventResolve) {
const resolve = nextEventResolve;
@@ -570,7 +571,7 @@ function asyncWrap(fn: any, name: string) {
createWriteStream(options = kEmptyObject) {
const fd = this[kFd];
throwEBADFIfNecessary("createWriteStream", fd);
return new (require("internal/fs/streams").WriteStream)(undefined, {
return new (require("internal/fs/streams").WriteStream)(null, {
highWaterMark: 64 * 1024,
...options,
fd: this,

View File

@@ -118,9 +118,10 @@ function openAsBlob(path, options) {
class StatWatcher extends EventEmitter {
_handle: StatWatcherHandle | null;
constructor(path, options) {
constructor(path: string, options: any) {
super();
this._handle = fs.watchFile(path, options, this.#onChange.bind(this));
// Cast to StatWatcherHandle as watchFile returns a Promise in fs.promises
this._handle = fs.watchFile(path, options, this.#onChange.bind(this)) as unknown as StatWatcherHandle;
}
#onChange(curr, prev) {
@@ -165,7 +166,10 @@ var access = function access(path, mode, callback) {
},
close = function close(fd, callback) {
if ($isCallable(callback)) {
fs.close(fd).then(() => callback(null), callback);
fs.close(fd).then(
() => callback(null),
reason => callback(reason),
);
} else if (callback === undefined) {
fs.close(fd).then(() => {});
} else {
@@ -244,7 +248,7 @@ var access = function access(path, mode, callback) {
fsync = function fsync(fd, callback) {
ensureCallback(callback);
fs.fsync(fd).then(nullcallback(callback), callback);
(fs.fsync(fd) as Promise<void>).then(nullcallback(callback), callback);
},
ftruncate = function ftruncate(fd, len = 0, callback) {
if ($isCallable(len)) {
@@ -254,7 +258,7 @@ var access = function access(path, mode, callback) {
ensureCallback(callback);
fs.ftruncate(fd, len).then(nullcallback(callback), callback);
(fs.ftruncate(fd, len) as Promise<void>).then(nullcallback(callback), callback);
},
futimes = function futimes(fd, atime, mtime, callback) {
ensureCallback(callback);
@@ -318,7 +322,7 @@ var access = function access(path, mode, callback) {
fdatasync = function fdatasync(fd, callback) {
ensureCallback(callback);
fs.fdatasync(fd).then(nullcallback(callback), callback);
(fs.fdatasync(fd) as Promise<void>).then(nullcallback(callback), callback);
},
read = function read(fd, buffer, offsetOrOptions, length, position, callback) {
// fd = getValidatedFd(fd); DEFERRED TO NATIVE
@@ -631,7 +635,7 @@ defineCustomPromisifyArgs(writev, ["bytesWritten", "buffers"]);
// of this means we need to do path validation in the js side of things
const statWatchers = new Map();
function getValidatedPath(p: any) {
if (p instanceof URL) return Bun.fileURLToPath(p as URL);
if (typeof p === "object" && p !== null && "href" in p) return Bun.fileURLToPath(p as URL);
if (typeof p !== "string") throw $ERR_INVALID_ARG_TYPE("path", "string or URL", p);
return require("node:path").resolve(p);
}
@@ -710,10 +714,11 @@ function encodeRealpathResult(result, encoding) {
}
let assertEncodingForWindows: any = undefined;
const realpathSync: typeof import("node:fs").realpathSync =
// realpathSync also has complex overloads
const realpathSync: any =
process.platform !== "win32"
? (fs.realpathSync.bind(fs) as any)
: function realpathSync(p, options) {
: function realpathSync(p: string, options?: string | { encoding?: string }) {
let encoding;
if (options) {
if (typeof options === "string") encoding = options;
@@ -723,15 +728,15 @@ const realpathSync: typeof import("node:fs").realpathSync =
// This function is ported 1:1 from node.js, to emulate how it is unable to
// resolve subst drives to their underlying location. The native call is
// able to see through that.
if (p instanceof URL) {
if (p.pathname.indexOf("%00") != -1) {
throw $ERR_INVALID_ARG_VALUE("path", "string without null bytes", p.pathname);
if (typeof p === "object" && p !== null && "href" in p) {
const url = p as URL;
if (url.pathname && url.pathname.indexOf("%00") != -1) {
throw $ERR_INVALID_ARG_VALUE("path", "string without null bytes", url.pathname);
}
p = Bun.fileURLToPath(p as URL);
p = Bun.fileURLToPath(url);
} else {
if (typeof p !== "string") {
p += "";
}
// Force convert to string
p = String(p);
p = getValidatedPath(p);
}
throwIfNullBytesInFileName(p);
@@ -751,9 +756,9 @@ const realpathSync: typeof import("node:fs").realpathSync =
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
let lastStat: StatsType = lstatSync(base, { throwIfNoEntry: true });
let lastStat = lstatSync(base, { throwIfNoEntry: true });
if (lastStat === undefined) return;
knownHard.$add(base);
knownHard.add(base);
const pathModule = require("node:path");
@@ -776,7 +781,7 @@ const realpathSync: typeof import("node:fs").realpathSync =
}
// Continue if not a symlink, break if a pipe/socket
if (knownHard.$has(base)) {
if (knownHard.has(base)) {
if (lastStat.isFIFO() || lastStat.isSocket()) {
break;
}
@@ -784,16 +789,16 @@ const realpathSync: typeof import("node:fs").realpathSync =
}
let resolvedLink;
lastStat = fs.lstatSync(base, { throwIfNoEntry: true });
lastStat = fs.lstatSync(base, { throwIfNoEntry: true }) as unknown as Stats;
if (lastStat === undefined) return;
if (!lastStat.isSymbolicLink()) {
knownHard.$add(base);
knownHard.add(base);
continue;
}
lastStat = fs.statSync(base, { throwIfNoEntry: true });
const linkTarget = fs.readlinkSync(base);
lastStat = fs.statSync(base, { throwIfNoEntry: true }) as unknown as Stats;
const linkTarget = fs.readlinkSync(base) as string;
resolvedLink = pathModule.resolve(previous, linkTarget);
// Resolve the link, then start over
@@ -804,27 +809,32 @@ const realpathSync: typeof import("node:fs").realpathSync =
pos = current.length;
// On windows, check that the root exists. On unix there is no need.
if (!knownHard.$has(base)) {
lastStat = fs.lstatSync(base, { throwIfNoEntry: true });
if (!knownHard.has(base)) {
lastStat = fs.lstatSync(base, { throwIfNoEntry: true }) as unknown as Stats;
if (lastStat === undefined) return;
knownHard.$add(base);
knownHard.add(base);
}
}
return encodeRealpathResult(p, encoding);
};
const realpath: typeof import("node:fs").realpath =
// realpath has complex overloads, so we implement as any first then type cast
const realpath: any =
process.platform !== "win32"
? (function realpath(p, options, callback) {
? (function realpath(p: string, options: any, callback?: any) {
if ($isCallable(options)) {
callback = options;
options = undefined;
}
ensureCallback(callback);
// Ensure callback is a valid function
const cb = ensureCallback(callback) as (err: Error | null, resolvedPath: string) => void;
fs.realpath(p, options, false).then(function (resolvedPath) {
callback(null, resolvedPath);
}, callback);
fs.realpath(p, options, false).then(
function (value: unknown) {
cb(null, String(value));
},
(err: Error) => cb(err, "" as any),
);
} as typeof import("node:fs").realpath)
: (function realpath(p, options, callback) {
if ($isCallable(options)) {
@@ -838,15 +848,15 @@ const realpath: typeof import("node:fs").realpath =
else encoding = options?.encoding;
encoding && (assertEncodingForWindows ?? $newZigFunction("types.zig", "jsAssertEncodingValid", 1))(encoding);
}
if (p instanceof URL) {
if (p.pathname.indexOf("%00") != -1) {
throw $ERR_INVALID_ARG_VALUE("path", "string without null bytes", p.pathname);
if (typeof p === "object" && p !== null && "href" in p) {
const url = p as URL;
if (url.pathname && url.pathname.indexOf("%00") != -1) {
throw $ERR_INVALID_ARG_VALUE("path", "string without null bytes", url.pathname);
}
p = Bun.fileURLToPath(p as URL);
p = Bun.fileURLToPath(url);
} else {
if (typeof p !== "string") {
p += "";
}
// Force convert to string
p = String(p);
p = getValidatedPath(p);
}
throwIfNullBytesInFileName(p);
@@ -1032,7 +1042,7 @@ class Dir {
#handle: number;
#path: PathLike;
#options;
#entries: DirentType[] | null = null;
#entries: any[] | null = null;
constructor(handle, path: PathLike, options) {
if ($isUndefinedOrNull(handle)) throw $ERR_MISSING_ARGS("handle");
@@ -1049,7 +1059,7 @@ class Dir {
withFileTypes: true,
encoding: this.#options?.encoding,
recursive: this.#options?.recursive,
}));
}) as any[]);
return entries.shift() ?? null;
}
@@ -1070,8 +1080,8 @@ class Dir {
recursive: this.#options?.recursive,
})
.then(entries => {
this.#entries = entries;
return entries.shift() ?? null;
this.#entries = entries as unknown as Dirent[];
return (entries as unknown as Dirent[]).shift() ?? null;
});
}

View File

@@ -1,6 +1,9 @@
const { validateInteger } = require("internal/validators");
const { Agent, globalAgent, NODE_HTTP_WARNING } = require("node:_http_agent");
const { ClientRequest } = require("node:_http_client");
// ClientRequest is a class constructor that's imported from an internal module
const {
ClientRequest,
}: { ClientRequest: new (url: string | URL, options?: any, cb?: Function) => any } = require("node:_http_client");
const { IncomingMessage } = require("node:_http_incoming");
const { OutgoingMessage } = require("node:_http_outgoing");
const { Server, ServerResponse } = require("node:_http_server");

View File

@@ -1,6 +1,7 @@
// import type { Readable, Writable } from "node:stream";
// import type { WorkerOptions } from "node:worker_threads";
declare const self: typeof globalThis;
declare const self: WebWorker;
type WebWorker = InstanceType<typeof globalThis.Worker>;
const EventEmitter = require("node:events");
@@ -234,7 +235,7 @@ class Worker extends EventEmitter {
}
}
try {
this.#worker = new WebWorker(filename, options);
this.#worker = new WebWorker(filename, options as Bun.WorkerOptions);
} catch (e) {
if (this.#urlToRevoke) {
URL.revokeObjectURL(this.#urlToRevoke);
@@ -341,7 +342,8 @@ class Worker extends EventEmitter {
let error = event?.error;
if (!error) {
error = new Error(event.message, { cause: event });
const stack = event?.stack;
const stack = "stack" in event ? event.stack : undefined;
if (stack) {
error.stack = stack;
}

305
src/js/private.d.ts vendored
View File

@@ -111,6 +111,267 @@ declare module "bun" {
var FFI: any;
/** This version of fetch is untamperable */
var fetch: typeof globalThis.fetch;
type DigestEncoding = "hex" | "base64" | "base64url" | "latin1" | "binary";
interface CryptoHasher {
hash(
algorithm: string,
data: string | ArrayBuffer | ArrayBufferView,
encoding?: DigestEncoding | BufferEncoding | TypedArray,
): Buffer | string | TypedArray;
}
var CryptoHasher: {
hash(
algorithm: string,
data: string | ArrayBuffer | ArrayBufferView,
encoding?: DigestEncoding | BufferEncoding | TypedArray,
): Buffer | string | TypedArray;
};
interface SpawnOptions {
cmd: string[];
stdio?: Array<string | number | null | NodeJS.TypedArray | ArrayBufferView>;
cwd?: string;
env?: Record<string, string>;
detached?: boolean;
onExit?: (handle: any, exitCode: number, signalCode: string | null, err: Error | null) => void;
lazy?: boolean;
ipc?: ((message: any) => void) | undefined;
onDisconnect?: ((ok: boolean) => void) | undefined;
serialization?: string;
argv0?: string;
windowsHide?: boolean;
windowsVerbatimArguments?: boolean;
}
interface SpawnHandle {
pid: number;
stdin?: any;
stdout?: any;
stderr?: any;
stdio?: any[];
killed?: boolean;
connected?: boolean;
kill(signal?: string | number): boolean;
ref(): void;
unref(): void;
disconnect(): void;
send(message: any): boolean;
}
function spawn(options: SpawnOptions): SpawnHandle;
interface UDPSocketOptions {
hostname: string;
port: number;
flags?: number;
socket: {
data: (socket: any, data: Buffer, port: number, address: string) => void;
error: (error: Error) => void;
};
}
function udpSocket(options: UDPSocketOptions): Promise<any>;
interface DNSResolverOptions {
timeout?: number;
tries?: number;
family?: number;
hints?: number;
verbatim?: boolean;
all?: boolean;
addrconfig?: boolean;
v4mapped?: boolean;
ttl?: boolean;
servers?: string[];
resultOrder?: string;
[key: string]: any;
}
interface DNSResolver {
getServers(): string[];
setServers(servers: string[]): void;
resolve(hostname: string, rrtype?: string): Promise<any[]>;
resolve4(hostname: string, options?: { ttl?: boolean }): Promise<any[]>;
resolve6(hostname: string, options?: { ttl?: boolean }): Promise<any[]>;
resolveAny(hostname: string): Promise<any[]>;
resolveCname(hostname: string): Promise<string[]>;
resolveMx(hostname: string): Promise<{ priority: number; exchange: string }[]>;
resolveNs(hostname: string): Promise<string[]>;
resolveTxt(hostname: string): Promise<string[][]>;
resolveSrv(hostname: string): Promise<{ priority: number; weight: number; port: number; name: string }[]>;
resolvePtr(hostname: string): Promise<string[]>;
resolveNaptr(hostname: string): Promise<any[]>;
resolveSoa(hostname: string): Promise<any>;
reverse(ip: string): Promise<string[]>;
lookup(hostname: string, options?: DNSResolverOptions): Promise<any>;
lookupService(address: string, port: string | number): Promise<{ hostname: string; service: string }>;
_handle?: any;
}
var dns: DNSResolver;
}
// Constants for EventEmitter
declare const kCapture: unique symbol;
declare const kErrorMonitor: unique symbol;
declare const kMaxEventTargetListeners: unique symbol;
declare const kMaxEventTargetListenersWarned: unique symbol;
declare const kWatermarkData: unique symbol;
declare const kRejection: unique symbol;
declare const kFirstEventParam: unique symbol;
// Base EventEmitter interface
interface EventEmitter {
// We use non-optional _events to avoid TypeScript undefined warnings
_events: {
[key: string]: any;
[key: symbol]: any;
__proto__: null;
newListener?: any;
removeListener?: any;
};
_eventsCount: number;
_maxListeners?: number;
[kCapture]?: boolean;
[kRejection]?: Function;
emit(type: string, ...args: any[]): boolean;
on(type: string, listener: Function): this;
once(type: string, listener: Function): this;
off(type: string, listener: Function): this;
addListener(type: string, listener: Function): this;
removeListener(type: string, listener: Function): this;
prependListener(type: string, listener: Function): this;
prependOnceListener(type: string, listener: Function): this;
removeAllListeners(type?: string | symbol): this;
setMaxListeners(n: number): this;
getMaxListeners(): number;
listeners(type: string): Function[];
rawListeners(type: string): Function[];
listenerCount(type: string, listener?: Function): number;
eventNames(): (string | symbol)[];
pause?(): void;
resume?(): void;
}
// DOM-like event target interface
interface JSEventTarget {
addEventListener(type: string, listener: Function, options?: any): void;
removeEventListener(type: string, listener: Function, options?: any): void;
}
// Event options interface to use with empty objects
interface EventOptions {
signal?: AbortSignal;
}
// Options for event stream
interface EventStreamOptions {
signal?: AbortSignal;
highWaterMark?: number;
highWatermark?: number; // Alternative spelling
lowWaterMark?: number;
lowWatermark?: number; // Alternative spelling
close?: string[];
[kFirstEventParam]?: boolean;
}
// Wrapper for once listener
interface WrappedListener extends Function {
listener: Function;
}
// Interface for error with additional properties for listener warnings
interface MaxListenersWarning extends Error {
emitter: any;
type: string;
count: number;
}
interface FixedQueue {
isEmpty(): boolean;
shift(): any;
push(item: any): void;
}
interface AbortSignal {
aborted: boolean;
reason?: any;
addEventListener(type: string, listener: Function, options?: any): void;
removeEventListener(type: string, listener: Function, options?: any): void;
}
// AsyncResource from async_hooks
interface AsyncResourceConstructor {
new (name: string, options?: { triggerAsyncId?: number; requireManualDestroy?: boolean }): AsyncResource;
}
interface AsyncResource {
runInAsyncScope(fn: Function, thisArg?: any, ...args: any[]): any;
emitDestroy(): void;
}
// Common empty object used in the Node.js implementation
declare const kEmptyObject: Readonly<{ __proto__: null }>;
// Error constructor helpers for node errors
declare function $ERR_INVALID_ARG_TYPE(name: string, expected: string | string[], actual: any): Error;
// Add to global Function interface to support the listener property
interface Function {
listener?: Function;
}
// Watcher handle for fs.watchFile
interface StatWatcherHandle {
ref(): void;
unref(): void;
close(): void;
}
// File system types
interface Stats {
dev: number;
ino: number;
mode: number;
nlink: number;
uid: number;
gid: number;
rdev: number;
size: number;
blksize: number;
blocks: number;
atimeMs: number;
mtimeMs: number;
ctimeMs: number;
birthtimeMs: number;
atime: Date;
mtime: Date;
ctime: Date;
birthtime: Date;
isFile(): boolean;
isDirectory(): boolean;
isBlockDevice(): boolean;
isCharacterDevice(): boolean;
isSymbolicLink(): boolean;
isFIFO(): boolean;
isSocket(): boolean;
}
interface Dirent {
name: string;
parentPath?: string;
path?: string;
isFile(): boolean;
isDirectory(): boolean;
isBlockDevice(): boolean;
isCharacterDevice(): boolean;
isSymbolicLink(): boolean;
isFIFO(): boolean;
isSocket(): boolean;
}
/**
@@ -150,8 +411,18 @@ interface LoaderModule {
declare interface Error {
code?: string;
context?: any;
}
declare function $ERR_UNHANDLED_ERROR(stringifiedErr: any): Error;
declare function $makeAbortError(message?: string, options?: { cause?: any }): Error;
declare function $isPromise(value: any): boolean;
declare function $newPromiseCapability(Promise: PromiseConstructor): {
resolve: Function;
reject: Function;
promise: Promise<any>;
};
interface JSCommonJSModule {
$require(id: string, mod: any, args_count: number, args: Array): any;
$requireNativeModule(id: string): any;
@@ -223,3 +494,37 @@ declare function $newZigFunction<T = (...args: any) => any>(
*/
declare function $bindgenFn<T = (...args: any) => any>(filename: string, symbol: string): T;
// NOTE: $debug, $assert, and $isPromiseFulfilled omitted
// DNS module type with all exported properties and methods
interface DNS extends DNSResolver {
ADDRCONFIG: number;
V4MAPPED: number;
ALL: number;
NODATA: string;
FORMERR: string;
SERVFAIL: string;
NOTFOUND: string;
NOTIMP: string;
REFUSED: string;
BADQUERY: string;
BADNAME: string;
BADFAMILY: string;
BADRESP: string;
CONNREFUSED: string;
TIMEOUT: string;
EOF: string;
FILE: string;
NOMEM: string;
DESTRUCTION: string;
BADSTR: string;
BADFLAGS: string;
NONAME: string;
BADHINTS: string;
NOTINITIALIZED: string;
LOADIPHLPAPI: string;
ADDRGETNETWORKPARAMS: string;
CANCELLED: string;
// Adding these to fix TS errors
reverse(ip: string): Promise<string[]>;
}

View File

@@ -2,8 +2,8 @@ import type * as s from "stream";
// Users may override the global fetch implementation, so we need to ensure these are the originals.
const bindings = $cpp("NodeFetch.cpp", "createNodeFetchInternalBinding");
const WebResponse: typeof globalThis.Response = bindings[0];
const WebRequest: typeof globalThis.Request = bindings[1];
const WebResponse: Bun.__internal.BunResponseConstructorOverride = bindings[0];
const WebRequest: Bun.__internal.BunRequestConstructorOverride = bindings[1];
const Blob: typeof globalThis.Blob = bindings[2];
const WebHeaders: typeof globalThis.Headers = bindings[3];
const FormData: typeof globalThis.FormData = bindings[4];
@@ -14,7 +14,8 @@ const nativeFetch = Bun.fetch;
// https://github.com/node-fetch/node-fetch/blob/8b3320d2a7c07bce4afc6b2bf6c3bbddda85b01f/src/headers.js#L44
class Headers extends WebHeaders {
raw() {
const obj = this.toJSON();
const obj = this.toJSON() as Record<string, string | string[]>;
for (const key in obj) {
const val = obj[key];
if (!$isJSArray(val)) {
@@ -53,7 +54,7 @@ class Response extends WebResponse {
get body() {
let body = this[kBody];
if (!body) {
var web = super.body;
const web = super.body;
if (!web) return null;
body = this[kBody] = new (require("internal/webstreams_adapters")._ReadableFromWeb)({}, web);
}
@@ -66,6 +67,7 @@ class Response extends WebResponse {
}
clone() {
// @ts-expect-error `super` is accessible here
return Object.setPrototypeOf(super.clone(this), ResponsePrototype);
}
@@ -115,6 +117,7 @@ class Response extends WebResponse {
return "default";
}
}
var ResponsePrototype = Response.prototype;
const kUrl = Symbol("kUrl");