aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/builtins/js/ProcessObjectInternals.js
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-03 08:25:15 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-03 08:26:53 -0800
commitbdc43c1696f0d759d28cfb63a2eb1aed5273d55f (patch)
tree604b87032f77ae1902c76a149b98ad7d40b1a2b1 /src/bun.js/builtins/js/ProcessObjectInternals.js
parentbc028168a815977830460983f28aefa4f7605c5e (diff)
downloadbun-bdc43c1696f0d759d28cfb63a2eb1aed5273d55f.tar.gz
bun-bdc43c1696f0d759d28cfb63a2eb1aed5273d55f.tar.zst
bun-bdc43c1696f0d759d28cfb63a2eb1aed5273d55f.zip
`process.stdout` and `process.stderr`
Diffstat (limited to 'src/bun.js/builtins/js/ProcessObjectInternals.js')
-rw-r--r--src/bun.js/builtins/js/ProcessObjectInternals.js600
1 files changed, 600 insertions, 0 deletions
diff --git a/src/bun.js/builtins/js/ProcessObjectInternals.js b/src/bun.js/builtins/js/ProcessObjectInternals.js
new file mode 100644
index 000000000..580152678
--- /dev/null
+++ b/src/bun.js/builtins/js/ProcessObjectInternals.js
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2022 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.
+ */
+
+// @internal
+
+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 {
+ 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" ||
+ normalized === ""
+ );
+ }
+ 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));
+ }
+
+ 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();
+ return !!(writeResult || flushResult);
+ }
+
+ #writeWithEncoding(chunk, encoding) {
+ if (!isFastEncoding(encoding)) {
+ this.#ensureInnerStream();
+ return this.#innerStream.write(chunk, encoding);
+ }
+
+ return this.#write1(chunk);
+ }
+
+ #performCallback(cb, err) {
+ 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_);
+}
+
+function getStdinStream(fd, rawRequire, Bun) {
+ var module = { path: "node:process", require: rawRequire };
+ var require = (path) => module.require(path);
+
+ var { Readable, Duplex, eos, destroy } = require("node:stream");
+
+ var StdinStream = class StdinStream extends Duplex {
+ #readStream;
+ #writeStream;
+
+ #readable = true;
+ #writable = true;
+
+ #onFinish;
+ #onClose;
+ #onDrain;
+ #onReadable;
+
+ get isTTY() {
+ return require("tty").isatty(fd);
+ }
+
+ get fd() {
+ return fd_;
+ }
+
+ constructor() {
+ super({ readable: true, writable: true });
+
+ this.#onReadable = this._read.bind(this);
+ }
+
+ #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.#readStream) destroy(this.#readStream, err);
+ if (this.#writeStream) destroy(this.#writeStream, err);
+ }
+ }
+
+ on(ev, cb) {
+ super.on(ev, cb);
+ if (!this.#readStream && (ev === "readable" || ev === "data")) {
+ this.#loadReadStream();
+ }
+ }
+
+ #loadReadStream() {
+ var readStream = (this.#readStream = Readable.fromWeb(
+ Bun.stdin.stream(),
+ ));
+
+ readStream.on("readable", () => {
+ const cb = this.#onReadable;
+ this.#onReadable = null;
+ cb();
+ });
+
+ readStream.on("end", () => {
+ this.push(null);
+ });
+
+ eos(readStream, (err) => {
+ this.#readable = false;
+ if (err) {
+ destroy(readStream, err);
+ }
+ this.#onFinished(err);
+ });
+ }
+
+ _read() {
+ var readStream = this.#readStream;
+ while (true) {
+ const buf = readStream.read();
+ if (buf === null || !this.push(buf)) {
+ this.#onReadable = this._read.bind(this);
+ return;
+ }
+ }
+ }
+
+ #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 = callback.bind(this);
+ }
+ };
+
+ return new StdinStream();
+}