aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/builtins/ts/ProcessObjectInternals.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/builtins/ts/ProcessObjectInternals.ts')
-rw-r--r--src/bun.js/builtins/ts/ProcessObjectInternals.ts676
1 files changed, 676 insertions, 0 deletions
diff --git a/src/bun.js/builtins/ts/ProcessObjectInternals.ts b/src/bun.js/builtins/ts/ProcessObjectInternals.ts
new file mode 100644
index 000000000..8b24e68ba
--- /dev/null
+++ b/src/bun.js/builtins/ts/ProcessObjectInternals.ts
@@ -0,0 +1,676 @@
+/*
+ * Copyright 2023 Codeblog Corp. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+export function binding(bindingName) {
+ if (bindingName !== "constants")
+ throw new TypeError(
+ "process.binding() is not supported in Bun. If that breaks something, please file an issue and include a reproducible code sample.",
+ );
+
+ var cache = globalThis.Symbol.for("process.bindings.constants");
+ var constants = globalThis[cache];
+ if (!constants) {
+ // TODO: make this less hacky.
+ // This calls require("node:fs").constants
+ // except, outside an ESM module.
+ const { constants: fs } = globalThis[globalThis.Symbol.for("Bun.lazy")]("createImportMeta", "node:process").require(
+ "node:fs",
+ );
+ constants = {
+ fs,
+ zlib: {},
+ crypto: {},
+ os: Bun._Os().constants,
+ };
+ globalThis[cache] = constants;
+ }
+ return constants;
+}
+
+export function getStdioWriteStream(fd_, rawRequire) {
+ var module = { path: "node:process", require: rawRequire };
+ var require = path => module.require(path);
+
+ function createStdioWriteStream(fd_) {
+ var { Duplex, eos, destroy } = require("node:stream");
+ var StdioWriteStream = class StdioWriteStream extends Duplex {
+ #writeStream;
+ #readStream;
+
+ #readable = true;
+ #writable = true;
+ #fdPath;
+
+ #onClose;
+ #onDrain;
+ #onFinish;
+ #onReadable;
+ #isTTY;
+
+ get isTTY() {
+ return (this.#isTTY ??= require("node:tty").isatty(fd_));
+ }
+
+ get fd() {
+ return fd_;
+ }
+
+ constructor(fd) {
+ super({ readable: true, writable: true });
+ this.#fdPath = `/dev/fd/${fd}`;
+ }
+
+ #onFinished(err) {
+ const cb = this.#onClose;
+ this.#onClose = null;
+
+ if (cb) {
+ cb(err);
+ } else if (err) {
+ this.destroy(err);
+ } else if (!this.#readable && !this.#writable) {
+ this.destroy();
+ }
+ }
+
+ _destroy(err, callback) {
+ if (!err && this.#onClose !== null) {
+ var AbortError = class AbortError extends Error {
+ code: string;
+ name: string;
+ constructor(message = "The operation was aborted", options = void 0) {
+ if (options !== void 0 && typeof options !== "object") {
+ throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ }
+ super(message, options);
+ this.code = "ABORT_ERR";
+ this.name = "AbortError";
+ }
+ };
+ err = new AbortError();
+ }
+
+ this.#onDrain = null;
+ this.#onFinish = null;
+ if (this.#onClose === null) {
+ callback(err);
+ } else {
+ this.#onClose = callback;
+ if (this.#writeStream) destroy(this.#writeStream, err);
+ if (this.#readStream) destroy(this.#readStream, err);
+ }
+ }
+
+ _write(chunk, encoding, callback) {
+ if (!this.#writeStream) {
+ var { createWriteStream } = require("node:fs");
+ var stream = (this.#writeStream = createWriteStream(this.#fdPath));
+
+ stream.on("finish", () => {
+ if (this.#onFinish) {
+ const cb = this.#onFinish;
+ this.#onFinish = null;
+ cb();
+ }
+ });
+
+ stream.on("drain", () => {
+ if (this.#onDrain) {
+ const cb = this.#onDrain;
+ this.#onDrain = null;
+ cb();
+ }
+ });
+
+ eos(stream, err => {
+ this.#writable = false;
+ if (err) {
+ destroy(stream, err);
+ }
+ this.#onFinished(err);
+ });
+ }
+ if (stream.write(chunk, encoding)) {
+ callback();
+ } else {
+ this.#onDrain = callback;
+ }
+ }
+
+ _final(callback) {
+ this.#writeStream && this.#writeStream.end();
+ this.#onFinish = callback;
+ }
+
+ #loadReadStream() {
+ var { createReadStream } = require("node:fs");
+
+ var readStream = (this.#readStream = createReadStream(this.#fdPath));
+
+ readStream.on("readable", () => {
+ if (this.#onReadable) {
+ const cb = this.#onReadable;
+ this.#onReadable = null;
+ cb();
+ } else {
+ this.read();
+ }
+ });
+
+ readStream.on("end", () => {
+ this.push(null);
+ });
+
+ eos(readStream, err => {
+ this.#readable = false;
+ if (err) {
+ destroy(readStream, err);
+ }
+ this.#onFinished(err);
+ });
+ return readStream;
+ }
+
+ _read() {
+ var stream = this.#readStream;
+ if (!stream) {
+ stream = this.#loadReadStream();
+ }
+
+ while (true) {
+ const buf = stream.read();
+ if (buf === null || !this.push(buf)) {
+ return;
+ }
+ }
+ }
+ };
+ return new StdioWriteStream(fd_);
+ }
+
+ var { EventEmitter } = require("node:events");
+
+ function isFastEncoding(encoding) {
+ if (!encoding) return true;
+
+ var normalied = encoding.toLowerCase();
+ return normalied === "utf8" || normalied === "utf-8" || normalied === "buffer" || normalied === "binary";
+ }
+
+ var readline;
+
+ var FastStdioWriteStream = class StdioWriteStream extends EventEmitter {
+ #fd;
+ #innerStream;
+ #writer;
+ #isTTY;
+
+ bytesWritten = 0;
+
+ setDefaultEncoding(encoding) {
+ if (this.#innerStream || !isFastEncoding(encoding)) {
+ this.#ensureInnerStream();
+ return this.#innerStream.setDefaultEncoding(encoding);
+ }
+ }
+
+ #createWriter() {
+ switch (this.#fd) {
+ case 1: {
+ var writer = Bun.stdout.writer({ highWaterMark: 0 });
+ writer.unref();
+ return writer;
+ }
+
+ case 2: {
+ var writer = Bun.stderr.writer({ highWaterMark: 0 });
+ writer.unref();
+ return writer;
+ }
+ default: {
+ throw new Error("Unsupported writer");
+ }
+ }
+ }
+
+ #getWriter() {
+ return (this.#writer ??= this.#createWriter());
+ }
+
+ constructor(fd_) {
+ super();
+ this.#fd = fd_;
+ }
+
+ get fd() {
+ return this.#fd;
+ }
+
+ get isTTY() {
+ return (this.#isTTY ??= require("node:tty").isatty(this.#fd));
+ }
+
+ cursorTo(x, y, callback) {
+ return (readline ??= require("readline")).cursorTo(this, x, y, callback);
+ }
+
+ moveCursor(dx, dy, callback) {
+ return (readline ??= require("readline")).moveCursor(this, dx, dy, callback);
+ }
+
+ clearLine(dir, callback) {
+ return (readline ??= require("readline")).clearLine(this, dir, callback);
+ }
+
+ clearScreenDown(callback) {
+ return (readline ??= require("readline")).clearScreenDown(this, callback);
+ }
+
+ // TODO: once implemented this.columns and this.rows should be uncommented
+ // getWindowSize() {
+ // return [this.columns, this.rows];
+ // }
+
+ ref() {
+ this.#getWriter().ref();
+ }
+
+ unref() {
+ this.#getWriter().unref();
+ }
+
+ on(event, listener) {
+ if (event === "close" || event === "finish") {
+ this.#ensureInnerStream();
+ return this.#innerStream.on(event, listener);
+ }
+
+ if (event === "drain") {
+ return super.on("drain", listener);
+ }
+
+ if (event === "error") {
+ return super.on("error", listener);
+ }
+
+ return super.on(event, listener);
+ }
+
+ get _writableState() {
+ this.#ensureInnerStream();
+ return this.#innerStream._writableState;
+ }
+
+ get _readableState() {
+ this.#ensureInnerStream();
+ return this.#innerStream._readableState;
+ }
+
+ pipe(destination) {
+ this.#ensureInnerStream();
+ return this.#innerStream.pipe(destination);
+ }
+
+ unpipe(destination) {
+ this.#ensureInnerStream();
+ return this.#innerStream.unpipe(destination);
+ }
+
+ #ensureInnerStream() {
+ if (this.#innerStream) return;
+ this.#innerStream = createStdioWriteStream(this.#fd);
+ const events = this.eventNames();
+ for (const event of events) {
+ this.#innerStream.on(event, (...args) => {
+ this.emit(event, ...args);
+ });
+ }
+ }
+
+ #write1(chunk) {
+ var writer = this.#getWriter();
+ const writeResult = writer.write(chunk);
+ this.bytesWritten += writeResult;
+ const flushResult = writer.flush(false);
+ return !!(writeResult || flushResult);
+ }
+
+ #writeWithEncoding(chunk, encoding) {
+ if (!isFastEncoding(encoding)) {
+ this.#ensureInnerStream();
+ return this.#innerStream.write(chunk, encoding);
+ }
+
+ return this.#write1(chunk);
+ }
+
+ #performCallback(cb, err?: any) {
+ if (err) {
+ this.emit("error", err);
+ }
+
+ try {
+ cb(err ? err : null);
+ } catch (err2) {
+ this.emit("error", err2);
+ }
+ }
+
+ #writeWithCallbackAndEncoding(chunk, encoding, callback) {
+ if (!isFastEncoding(encoding)) {
+ this.#ensureInnerStream();
+ return this.#innerStream.write(chunk, encoding, callback);
+ }
+
+ var writer = this.#getWriter();
+ const writeResult = writer.write(chunk);
+ const flushResult = writer.flush(true);
+ if (flushResult?.then) {
+ flushResult.then(
+ () => {
+ this.#performCallback(callback);
+ this.emit("drain");
+ },
+ err => this.#performCallback(callback, err),
+ );
+ return false;
+ }
+
+ queueMicrotask(() => {
+ this.#performCallback(callback);
+ });
+
+ return !!(writeResult || flushResult);
+ }
+
+ write(chunk, encoding, callback) {
+ const result = this._write(chunk, encoding, callback);
+
+ if (result) {
+ this.emit("drain");
+ }
+
+ return result;
+ }
+
+ get hasColors() {
+ return Bun.tty[this.#fd].hasColors;
+ }
+
+ _write(chunk, encoding, callback) {
+ var inner = this.#innerStream;
+ if (inner) {
+ return inner.write(chunk, encoding, callback);
+ }
+
+ switch (arguments.length) {
+ case 0: {
+ var error = new Error("Invalid arguments");
+ error.code = "ERR_INVALID_ARG_TYPE";
+ throw error;
+ }
+ case 1: {
+ return this.#write1(chunk);
+ }
+ case 2: {
+ if (typeof encoding === "function") {
+ return this.#writeWithCallbackAndEncoding(chunk, "", encoding);
+ } else if (typeof encoding === "string") {
+ return this.#writeWithEncoding(chunk, encoding);
+ }
+ }
+ default: {
+ if (
+ (typeof encoding !== "undefined" && typeof encoding !== "string") ||
+ (typeof callback !== "undefined" && typeof callback !== "function")
+ ) {
+ var error = new Error("Invalid arguments");
+ error.code = "ERR_INVALID_ARG_TYPE";
+ throw error;
+ }
+
+ if (typeof callback === "undefined") {
+ return this.#writeWithEncoding(chunk, encoding);
+ }
+
+ return this.#writeWithCallbackAndEncoding(chunk, encoding, callback);
+ }
+ }
+ }
+
+ destroy() {
+ return this;
+ }
+
+ end() {
+ return this;
+ }
+ };
+
+ return new FastStdioWriteStream(fd_);
+}
+
+export function getStdinStream(fd_, rawRequire, Bun) {
+ var module = { path: "node:process", require: rawRequire };
+ var require = path => module.require(path);
+
+ var { Duplex, eos, destroy } = require("node:stream");
+
+ var StdinStream = class StdinStream extends Duplex {
+ #reader;
+ // TODO: investigate https://github.com/oven-sh/bun/issues/1607
+
+ #readRef;
+ #writeStream;
+
+ #readable = true;
+ #unrefOnRead = false;
+ #writable = true;
+
+ #onFinish;
+ #onClose;
+ #onDrain;
+
+ get isTTY() {
+ return require("tty").isatty(fd_);
+ }
+
+ get fd() {
+ return fd_;
+ }
+
+ constructor() {
+ super({ readable: true, writable: true });
+ }
+
+ #onFinished(err?) {
+ const cb = this.#onClose;
+ this.#onClose = null;
+
+ if (cb) {
+ cb(err);
+ } else if (err) {
+ this.destroy(err);
+ } else if (!this.#readable && !this.#writable) {
+ this.destroy();
+ }
+ }
+
+ _destroy(err, callback) {
+ if (!err && this.#onClose !== null) {
+ var AbortError = class AbortError extends Error {
+ constructor(message = "The operation was aborted", options = void 0) {
+ if (options !== void 0 && typeof options !== "object") {
+ throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ }
+ super(message, options);
+ this.code = "ABORT_ERR";
+ this.name = "AbortError";
+ }
+ };
+ err = new AbortError();
+ }
+
+ if (this.#onClose === null) {
+ callback(err);
+ } else {
+ this.#onClose = callback;
+ if (this.#writeStream) destroy(this.#writeStream, err);
+ }
+ }
+
+ setRawMode(mode) {}
+
+ on(name, callback) {
+ // Streams don't generally required to present any data when only
+ // `readable` events are present, i.e. `readableFlowing === false`
+ //
+ // However, Node.js has a this quirk whereby `process.stdin.read()`
+ // blocks under TTY mode, thus looping `.read()` in this particular
+ // case would not result in truncation.
+ //
+ // Therefore the following hack is only specific to `process.stdin`
+ // and does not apply to the underlying Stream implementation.
+ if (name === "readable") {
+ this.ref();
+ this.#unrefOnRead = true;
+ }
+ return super.on(name, callback);
+ }
+
+ pause() {
+ this.unref();
+ return super.pause();
+ }
+
+ resume() {
+ this.ref();
+ return super.resume();
+ }
+
+ ref() {
+ this.#reader ??= Bun.stdin.stream().getReader();
+ this.#readRef ??= setInterval(() => {}, 1 << 30);
+ }
+
+ unref() {
+ if (this.#readRef) {
+ clearInterval(this.#readRef);
+ this.#readRef = null;
+ }
+ }
+
+ async #readInternal() {
+ try {
+ var done, value;
+ const read = this.#reader.readMany();
+
+ // read same-tick if possible
+ if (!read?.then) {
+ ({ done, value } = read);
+ } else {
+ ({ done, value } = await read);
+ }
+
+ if (!done) {
+ this.push(value[0]);
+
+ // shouldn't actually happen, but just in case
+ const length = value.length;
+ for (let i = 1; i < length; i++) {
+ this.push(value[i]);
+ }
+ } else {
+ this.push(null);
+ this.pause();
+ this.#readable = false;
+ this.#onFinished();
+ }
+ } catch (err) {
+ this.#readable = false;
+ this.#onFinished(err);
+ }
+ }
+
+ _read(size) {
+ if (this.#unrefOnRead) {
+ this.unref();
+ this.#unrefOnRead = false;
+ }
+ this.#readInternal();
+ }
+
+ #constructWriteStream() {
+ var { createWriteStream } = require("node:fs");
+ var writeStream = (this.#writeStream = createWriteStream("/dev/fd/0"));
+
+ writeStream.on("finish", () => {
+ if (this.#onFinish) {
+ const cb = this.#onFinish;
+ this.#onFinish = null;
+ cb();
+ }
+ });
+
+ writeStream.on("drain", () => {
+ if (this.#onDrain) {
+ const cb = this.#onDrain;
+ this.#onDrain = null;
+ cb();
+ }
+ });
+
+ eos(writeStream, err => {
+ this.#writable = false;
+ if (err) {
+ destroy(writeStream, err);
+ }
+ this.#onFinished(err);
+ });
+
+ return writeStream;
+ }
+
+ _write(chunk, encoding, callback) {
+ var writeStream = this.#writeStream;
+ if (!writeStream) {
+ writeStream = this.#constructWriteStream();
+ }
+
+ if (writeStream.write(chunk, encoding)) {
+ callback();
+ } else {
+ this.#onDrain = callback;
+ }
+ }
+
+ _final(callback) {
+ this.#writeStream.end();
+ this.#onFinish = (...args) => callback(...args);
+ }
+ };
+
+ return new StdinStream();
+}