declare var global: typeof globalThis;

declare var process: NodeJS.Process;
declare var console: Console;

interface ErrorConstructor {
    /**
     * Creates a `.stack` property on `targetObject`, which when accessed returns
     * a string representing the location in the code at which
     * `Error.captureStackTrace()` was called.
     *
     * ```js
     * const myObject = {};
     * Error.captureStackTrace(myObject);
     * myObject.stack;  // Similar to `new Error().stack`
     * ```
     *
     * The first line of the trace will be prefixed with
     * `${myObject.name}: ${myObject.message}`.
     *
     * The optional `constructorOpt` argument accepts a function. If given, all frames
     * above `constructorOpt`, including `constructorOpt`, will be omitted from the
     * generated stack trace.
     *
     * The `constructorOpt` argument is useful for hiding implementation
     * details of error generation from the user. For instance:
     *
     * ```js
     * function a() {
     *   b();
     * }
     *
     * function b() {
     *   c();
     * }
     *
     * function c() {
     *   // Create an error without stack trace to avoid calculating the stack trace twice.
     *   const { stackTraceLimit } = Error;
     *   Error.stackTraceLimit = 0;
     *   const error = new Error();
     *   Error.stackTraceLimit = stackTraceLimit;
     *
     *   // Capture the stack trace above function b
     *   Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
     *   throw error;
     * }
     *
     * a();
     * ```
     */
    captureStackTrace(targetObject: object, constructorOpt?: Function): void;
    /**
     * @see https://v8.dev/docs/stack-trace-api#customizing-stack-traces
     */
    prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
    /**
     * The `Error.stackTraceLimit` property specifies the number of stack frames
     * collected by a stack trace (whether generated by `new Error().stack` or
     * `Error.captureStackTrace(obj)`).
     *
     * The default value is `10` but may be set to any valid JavaScript number. Changes
     * will affect any stack trace captured _after_ the value has been changed.
     *
     * If set to a non-number value, or set to a negative number, stack traces will
     * not capture any frames.
     */
    stackTraceLimit: number;
}

/**
 * Enable this API with the `--expose-gc` CLI flag.
 */
declare var gc: NodeJS.GCFunction | undefined;

declare namespace NodeJS {
    interface CallSite {
        getColumnNumber(): number | null;
        getEnclosingColumnNumber(): number | null;
        getEnclosingLineNumber(): number | null;
        getEvalOrigin(): string | undefined;
        getFileName(): string | null;
        getFunction(): Function | undefined;
        getFunctionName(): string | null;
        getLineNumber(): number | null;
        getMethodName(): string | null;
        getPosition(): number;
        getPromiseIndex(): number | null;
        getScriptHash(): string;
        getScriptNameOrSourceURL(): string | null;
        getThis(): unknown;
        getTypeName(): string | null;
        isAsync(): boolean;
        isConstructor(): boolean;
        isEval(): boolean;
        isNative(): boolean;
        isPromiseAll(): boolean;
        isToplevel(): boolean;
    }

    interface ErrnoException extends Error {
        errno?: number | undefined;
        code?: string | undefined;
        path?: string | undefined;
        syscall?: string | undefined;
    }

    interface ReadableStream extends EventEmitter {
        readable: boolean;
        read(size?: number): string | Buffer;
        setEncoding(encoding: BufferEncoding): this;
        pause(): this;
        resume(): this;
        isPaused(): boolean;
        pipe<T extends WritableStream>(destination: T, options?: { end?: boolean | undefined }): T;
        unpipe(destination?: WritableStream): this;
        unshift(chunk: string | Uint8Array, encoding?: BufferEncoding): void;
        wrap(oldStream: ReadableStream): this;
        [Symbol.asyncIterator](): AsyncIterableIterator<string | Buffer>;
    }

    interface WritableStream extends EventEmitter {
        writable: boolean;
        write(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
        write(str: string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
        end(cb?: () => void): this;
        end(data: string | Uint8Array, cb?: () => void): this;
        end(str: string, encoding?: BufferEncoding, cb?: () => void): this;
    }

    interface ReadWriteStream extends ReadableStream, WritableStream {}

    interface RefCounted {
        ref(): this;
        unref(): this;
    }

    interface Dict<T> {
        [key: string]: T | undefined;
    }

    interface ReadOnlyDict<T> {
        readonly [key: string]: T | undefined;
    }

    type PartialOptions<T> = { [K in keyof T]?: T[K] | undefined };

    interface GCFunction {
        (minor?: boolean): void;
        (options: NodeJS.GCOptions & { execution: "async" }): Promise<void>;
        (options: NodeJS.GCOptions): void;
    }

    interface GCOptions {
        execution?: "sync" | "async" | undefined;
        flavor?: "regular" | "last-resort" | undefined;
        type?: "major-snapshot" | "major" | "minor" | undefined;
        filename?: string | undefined;
    }

    /** An iterable iterator returned by the Node.js API. */
    interface Iterator<T, TReturn = undefined, TNext = any> extends IteratorObject<T, TReturn, TNext> {
        [Symbol.iterator](): NodeJS.Iterator<T, TReturn, TNext>;
    }

    /** An async iterable iterator returned by the Node.js API. */
    interface AsyncIterator<T, TReturn = undefined, TNext = any> extends AsyncIteratorObject<T, TReturn, TNext> {
        [Symbol.asyncIterator](): NodeJS.AsyncIterator<T, TReturn, TNext>;
    }
}
