aboutsummaryrefslogtreecommitdiff
path: root/src/js/node
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/node')
-rw-r--r--src/js/node/fs.js489
-rw-r--r--src/js/node/stream.js186
2 files changed, 332 insertions, 343 deletions
diff --git a/src/js/node/fs.js b/src/js/node/fs.js
index e3630c461..b4165311f 100644
--- a/src/js/node/fs.js
+++ b/src/js/node/fs.js
@@ -6,6 +6,9 @@ const promises = require("node:fs/promises");
const Stream = require("node:stream");
const { isArrayBufferView } = require("node:util/types");
+var _writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath");
+var _fs = Symbol.for("#fs");
+
const constants = $processBindingConstants.fs;
var fs = Bun.fs();
@@ -820,302 +823,290 @@ var defaultWriteStreamOptions = {
},
};
-var WriteStreamClass;
-WriteStream = (function (InternalWriteStream) {
- WriteStreamClass = InternalWriteStream;
- Object.defineProperty(WriteStreamClass.prototype, Symbol.toStringTag, {
- value: "WriteStream",
- enumerable: false,
- });
-
- function WriteStream(path, options) {
- return new InternalWriteStream(path, options);
+var WriteStreamClass = (WriteStream = function WriteStream(path, options = defaultWriteStreamOptions) {
+ if (!(this instanceof WriteStream)) {
+ return new WriteStream(path, options);
}
- WriteStream.prototype = InternalWriteStream.prototype;
- return Object.defineProperty(WriteStream, Symbol.hasInstance, {
- value(instance) {
- return instance instanceof InternalWriteStream;
- },
- });
-})(
- class WriteStream extends Stream.NativeWritable {
- constructor(path, options = defaultWriteStreamOptions) {
- if (!options) {
- throw new TypeError("Expected options to be an object");
- }
- var {
- fs = defaultWriteStreamOptions.fs,
- start = defaultWriteStreamOptions.start,
- flags = defaultWriteStreamOptions.flags,
- mode = defaultWriteStreamOptions.mode,
- autoClose = true,
- emitClose = false,
- autoDestroy = autoClose,
- encoding = defaultWriteStreamOptions.encoding,
- fd = defaultWriteStreamOptions.fd,
- pos = defaultWriteStreamOptions.pos,
- } = options;
-
- var tempThis = {};
- if (fd != null) {
- if (typeof fd !== "number") {
- throw new Error("Expected options.fd to be a number");
- }
- tempThis.fd = fd;
- tempThis[writeStreamPathFastPathSymbol] = false;
- } else if (typeof path === "string") {
- if (path.length === 0) {
- throw new TypeError("Expected a non-empty path");
- }
+ if (!options) {
+ throw new TypeError("Expected options to be an object");
+ }
- if (path.startsWith("file:")) {
- path = Bun.fileURLToPath(path);
- }
+ var {
+ fs = defaultWriteStreamOptions.fs,
+ start = defaultWriteStreamOptions.start,
+ flags = defaultWriteStreamOptions.flags,
+ mode = defaultWriteStreamOptions.mode,
+ autoClose = true,
+ emitClose = false,
+ autoDestroy = autoClose,
+ encoding = defaultWriteStreamOptions.encoding,
+ fd = defaultWriteStreamOptions.fd,
+ pos = defaultWriteStreamOptions.pos,
+ } = options;
+
+ var tempThis = {};
+ if (fd != null) {
+ if (typeof fd !== "number") {
+ throw new Error("Expected options.fd to be a number");
+ }
+ tempThis.fd = fd;
+ tempThis[_writeStreamPathFastPathSymbol] = false;
+ } else if (typeof path === "string") {
+ if (path.length === 0) {
+ throw new TypeError("Expected a non-empty path");
+ }
- tempThis.path = path;
- tempThis.fd = null;
- tempThis[writeStreamPathFastPathSymbol] =
- autoClose &&
- (start === undefined || start === 0) &&
- fs.write === defaultWriteStreamOptions.fs.write &&
- fs.close === defaultWriteStreamOptions.fs.close;
- }
+ if (path.startsWith("file:")) {
+ path = Bun.fileURLToPath(path);
+ }
- if (tempThis.fd == null) {
- tempThis.fd = fs.openSync(path, flags, mode);
- }
+ tempThis.path = path;
+ tempThis.fd = null;
+ tempThis[_writeStreamPathFastPathSymbol] =
+ autoClose &&
+ (start === undefined || start === 0) &&
+ fs.write === defaultWriteStreamOptions.fs.write &&
+ fs.close === defaultWriteStreamOptions.fs.close;
+ }
- super(tempThis.fd, {
- ...options,
- decodeStrings: false,
- autoDestroy,
- emitClose,
- fd: tempThis,
- });
- Object.assign(this, tempThis);
+ if (tempThis.fd == null) {
+ tempThis.fd = fs.openSync(path, flags, mode);
+ }
- if (typeof fs?.write !== "function") {
- throw new TypeError("Expected fs.write to be a function");
- }
+ NativeWritable.call(this, tempThis.fd, {
+ ...options,
+ decodeStrings: false,
+ autoDestroy,
+ emitClose,
+ fd: tempThis,
+ });
+ Object.assign(this, tempThis);
- if (typeof fs?.close !== "function") {
- throw new TypeError("Expected fs.close to be a function");
- }
+ if (typeof fs?.write !== "function") {
+ throw new TypeError("Expected fs.write to be a function");
+ }
- if (typeof fs?.open !== "function") {
- throw new TypeError("Expected fs.open to be a function");
- }
+ if (typeof fs?.close !== "function") {
+ throw new TypeError("Expected fs.close to be a function");
+ }
- if (typeof path === "object" && path) {
- if (path instanceof URL) {
- path = Bun.fileURLToPath(path);
- }
- }
+ if (typeof fs?.open !== "function") {
+ throw new TypeError("Expected fs.open to be a function");
+ }
- if (typeof path !== "string" && typeof fd !== "number") {
- throw new TypeError("Expected a path or file descriptor");
- }
+ if (typeof path === "object" && path) {
+ if (path instanceof URL) {
+ path = Bun.fileURLToPath(path);
+ }
+ }
- this.start = start;
- this.#fs = fs;
- this.flags = flags;
- this.mode = mode;
+ if (typeof path !== "string" && typeof fd !== "number") {
+ throw new TypeError("Expected a path or file descriptor");
+ }
- if (this.start !== undefined) {
- this.pos = this.start;
- }
+ this.start = start;
+ this[_fs] = fs;
+ this.flags = flags;
+ this.mode = mode;
+ this.bytesWritten = 0;
+ this[writeStreamSymbol] = true;
+ this[kIoDone] = false;
+ // _write = undefined;
+ // _writev = undefined;
+
+ if (this.start !== undefined) {
+ this.pos = this.start;
+ }
- if (encoding !== defaultWriteStreamOptions.encoding) {
- this.setDefaultEncoding(encoding);
- if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") {
- this[writeStreamPathFastPathSymbol] = false;
- }
- }
+ if (encoding !== defaultWriteStreamOptions.encoding) {
+ this.setDefaultEncoding(encoding);
+ if (encoding !== "buffer" && encoding !== "utf8" && encoding !== "utf-8" && encoding !== "binary") {
+ this[_writeStreamPathFastPathSymbol] = false;
}
+ }
- get autoClose() {
- return this._writableState.autoDestroy;
- }
+ return this;
+});
+const NativeWritable = Stream.NativeWritable;
+const WriteStreamPrototype = (WriteStream.prototype = Object.create(NativeWritable.prototype));
- set autoClose(val) {
+Object.defineProperties(WriteStreamPrototype, {
+ autoClose: {
+ get() {
+ return this._writableState.autoDestroy;
+ },
+ set(val) {
this._writableState.autoDestroy = val;
- }
+ },
+ },
+ pending: {
+ get() {
+ return this.fd === null;
+ },
+ },
+});
- destroySoon = this.end; // TODO: what is this for?
+// TODO: what is this for?
+WriteStreamPrototype.destroySoon = WriteStreamPrototype.end;
- // noop, node has deprecated this
- open() {}
+// noop, node has deprecated this
+WriteStreamPrototype.open = function open() {};
- path;
- fd;
- flags;
- mode;
- #fs;
- bytesWritten = 0;
- pos;
- [writeStreamPathFastPathSymbol];
- [writeStreamSymbol] = true;
- start;
+WriteStreamPrototype[writeStreamPathFastPathCallSymbol] = function WriteStreamPathFastPathCallSymbol(
+ readStream,
+ pipeOpts,
+) {
+ if (!this[_writeStreamPathFastPathSymbol]) {
+ return false;
+ }
- [writeStreamPathFastPathCallSymbol](readStream, pipeOpts) {
- if (!this[writeStreamPathFastPathSymbol]) {
- return false;
- }
+ if (this.fd !== null) {
+ this[_writeStreamPathFastPathSymbol] = false;
+ return false;
+ }
- if (this.fd !== null) {
- this[writeStreamPathFastPathSymbol] = false;
- return false;
- }
+ this[kIoDone] = false;
+ readStream[kIoDone] = false;
+ return Bun.write(this[_writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then(
+ bytesWritten => {
+ readStream[kIoDone] = this[kIoDone] = true;
+ this.bytesWritten += bytesWritten;
+ readStream.bytesRead += bytesWritten;
+ this.end();
+ readStream.close();
+ },
+ err => {
+ readStream[kIoDone] = this[kIoDone] = true;
+ WriteStream_errorOrDestroy.call(this, err);
+ readStream.emit("error", err);
+ },
+ );
+};
- this[kIoDone] = false;
- readStream[kIoDone] = false;
- return Bun.write(this[writeStreamPathFastPathSymbol], readStream[readStreamPathOrFdSymbol]).then(
- bytesWritten => {
- readStream[kIoDone] = this[kIoDone] = true;
- this.bytesWritten += bytesWritten;
- readStream.bytesRead += bytesWritten;
- this.end();
- readStream.close();
- },
- err => {
- readStream[kIoDone] = this[kIoDone] = true;
- this.#errorOrDestroy(err);
- readStream.emit("error", err);
- },
- );
- }
+WriteStreamPrototype.isBunFastPathEnabled = function isBunFastPathEnabled() {
+ return this[_writeStreamPathFastPathSymbol];
+};
- isBunFastPathEnabled() {
- return this[writeStreamPathFastPathSymbol];
- }
+WriteStreamPrototype.disableBunFastPath = function disableBunFastPath() {
+ this[_writeStreamPathFastPathSymbol] = false;
+};
- disableBunFastPath() {
- this[writeStreamPathFastPathSymbol] = false;
- }
+function WriteStream_handleWrite(er, bytes) {
+ if (er) {
+ return WriteStream_errorOrDestroy.call(this, er);
+ }
- #handleWrite(er, bytes) {
- if (er) {
- return this.#errorOrDestroy(er);
- }
+ this.bytesWritten += bytes;
+}
- this.bytesWritten += bytes;
- }
+function WriteStream_internalClose(err, cb) {
+ this[_writeStreamPathFastPathSymbol] = false;
+ var fd = this.fd;
+ this[_fs].close(fd, er => {
+ this.fd = null;
+ cb(err || er);
+ });
+}
- #internalClose(err, cb) {
- this[writeStreamPathFastPathSymbol] = false;
- var fd = this.fd;
- this.#fs.close(fd, er => {
- this.fd = null;
- cb(err || er);
- });
- }
+WriteStreamPrototype._construct = function _construct(callback) {
+ if (typeof this.fd === "number") {
+ callback();
+ return;
+ }
- _construct(callback) {
- if (typeof this.fd === "number") {
- callback();
- return;
- }
+ callback();
+ this.emit("open", this.fd);
+ this.emit("ready");
+};
- callback();
- this.emit("open", this.fd);
- this.emit("ready");
- }
+WriteStreamPrototype._destroy = function _destroy(err, cb) {
+ if (this.fd === null) {
+ return cb(err);
+ }
- _destroy(err, cb) {
- if (this.fd === null) {
- return cb(err);
- }
+ if (this[kIoDone]) {
+ this.once(kIoDone, () => WriteStream_internalClose.call(this, err, cb));
+ return;
+ }
- if (this[kIoDone]) {
- this.once(kIoDone, () => this.#internalClose(err, cb));
- return;
- }
+ WriteStream_internalClose.call(this, err, cb);
+};
- this.#internalClose(err, cb);
+WriteStreamPrototype.close = function close(cb) {
+ if (cb) {
+ if (this.closed) {
+ process.nextTick(cb);
+ return;
}
+ this.on("close", cb);
+ }
- [kIoDone] = false;
-
- close(cb) {
- if (cb) {
- if (this.closed) {
- process.nextTick(cb);
- return;
- }
- this.on("close", cb);
- }
-
- // If we are not autoClosing, we should call
- // destroy on 'finish'.
- if (!this.autoClose) {
- this.on("finish", this.destroy);
- }
+ // If we are not autoClosing, we should call
+ // destroy on 'finish'.
+ if (!this.autoClose) {
+ this.on("finish", this.destroy);
+ }
- // We use end() instead of destroy() because of
- // https://github.com/nodejs/node/issues/2006
- this.end();
- }
+ // We use end() instead of destroy() because of
+ // https://github.com/nodejs/node/issues/2006
+ this.end();
+};
- write(chunk, encoding = this._writableState.defaultEncoding, cb) {
- this[writeStreamPathFastPathSymbol] = false;
- if (typeof chunk === "string") {
- chunk = Buffer.from(chunk, encoding);
- }
+WriteStreamPrototype.write = function write(chunk, encoding, cb) {
+ encoding ??= this._writableState?.defaultEncoding;
+ this[_writeStreamPathFastPathSymbol] = false;
+ if (typeof chunk === "string") {
+ chunk = Buffer.from(chunk, encoding);
+ }
- // TODO: Replace this when something like lseek is available
- var native = this.pos === undefined;
- const callback = native
- ? (err, bytes) => {
- this[kIoDone] = false;
- this.#handleWrite(err, bytes);
- this.emit(kIoDone);
- if (cb) !err ? cb() : cb(err);
- }
- : () => {};
- this[kIoDone] = true;
- if (this._write) {
- return this._write(chunk, encoding, callback);
- } else {
- return super.write(chunk, encoding, callback, native);
+ // TODO: Replace this when something like lseek is available
+ var native = this.pos === undefined;
+ const callback = native
+ ? (err, bytes) => {
+ this[kIoDone] = false;
+ WriteStream_handleWrite.call(this, err, bytes);
+ this.emit(kIoDone);
+ if (cb) !err ? cb() : cb(err);
}
- }
-
- end(chunk, encoding, cb) {
- var native = this.pos === undefined;
- return super.end(chunk, encoding, cb, native);
- }
+ : () => {};
+ this[kIoDone] = true;
+ if (this._write) {
+ return this._write(chunk, encoding, callback);
+ } else {
+ return NativeWritable.prototype.write.call(this, chunk, encoding, callback, native);
+ }
+};
- _write = undefined;
- _writev = undefined;
+// Do not inherit
+WriteStreamPrototype._write = undefined;
+WriteStreamPrototype._writev = undefined;
- get pending() {
- return this.fd === null;
- }
+WriteStreamPrototype.end = function end(chunk, encoding, cb) {
+ var native = this.pos === undefined;
+ return NativeWritable.prototype.end.call(this, chunk, encoding, cb, native);
+};
- _destroy(err, cb) {
- this.close(err, cb);
- }
+WriteStreamPrototype._destroy = function _destroy(err, cb) {
+ this.close(err, cb);
+};
- #errorOrDestroy(err) {
- var {
- _readableState: r = { destroyed: false, autoDestroy: false },
- _writableState: w = { destroyed: false, autoDestroy: false },
- } = this;
+function WriteStream_errorOrDestroy(err) {
+ var {
+ _readableState: r = { destroyed: false, autoDestroy: false },
+ _writableState: w = { destroyed: false, autoDestroy: false },
+ } = this;
- if (w?.destroyed || r?.destroyed) {
- return this;
- }
- if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
- else if (err) {
- this.emit("error", err);
- }
- }
- },
-);
+ if (w?.destroyed || r?.destroyed) {
+ return this;
+ }
+ if (r?.autoDestroy || w?.autoDestroy) this.destroy(err);
+ else if (err) {
+ this.emit("error", err);
+ }
+}
function createWriteStream(path, options) {
- // const WriteStream = getLazyWriteStream();
return new WriteStream(path, options);
}
diff --git a/src/js/node/stream.js b/src/js/node/stream.js
index d7d984cb8..f9d4377b5 100644
--- a/src/js/node/stream.js
+++ b/src/js/node/stream.js
@@ -5472,123 +5472,121 @@ function getNativeReadableStream(Readable, stream, options) {
/** --- Bun native stream wrapper --- */
var Writable = require_writable();
-var NativeWritable = class NativeWritable extends Writable {
- #pathOrFdOrSink;
- #fileSink;
- #native = true;
- _construct;
- _destroy;
- _final;
+const _pathOrFdOrSink = Symbol("pathOrFdOrSink");
+const _fileSink = Symbol("fileSink");
+const _native = Symbol("native");
- constructor(pathOrFdOrSink, options = {}) {
- super(options);
+function NativeWritable(pathOrFdOrSink, options = {}) {
+ Writable.call(this, options);
- this._construct = this.#internalConstruct;
- this._destroy = this.#internalDestroy;
- this._final = this.#internalFinal;
+ this[_native] = true;
- this.#pathOrFdOrSink = pathOrFdOrSink;
- }
+ this._construct = NativeWritable_internalConstruct;
+ this._destroy = NativeWritable_internalDestroy;
+ this._final = NativeWritable_internalFinal;
- // These are confusingly two different fns for construct which initially were the same thing because
- // `_construct` is part of the lifecycle of Writable and is not called lazily,
- // so we need to separate our _construct for Writable state and actual construction of the write stream
- #internalConstruct(cb) {
- this._writableState.constructed = true;
- this.constructed = true;
- if (typeof cb === "function") cb();
- process.nextTick(() => {
- this.emit("open", this.fd);
- this.emit("ready");
- });
- }
+ this[_pathOrFdOrSink] = pathOrFdOrSink;
+}
+NativeWritable.prototype = Object.create(Writable.prototype);
+
+// These are confusingly two different fns for construct which initially were the same thing because
+// `_construct` is part of the lifecycle of Writable and is not called lazily,
+// so we need to separate our _construct for Writable state and actual construction of the write stream
+function NativeWritable_internalConstruct(cb) {
+ this._writableState.constructed = true;
+ this.constructed = true;
+ if (typeof cb === "function") cb();
+ process.nextTick(() => {
+ this.emit("open", this.fd);
+ this.emit("ready");
+ });
+}
- #lazyConstruct() {
- // TODO: Turn this check into check for instanceof FileSink
- if (typeof this.#pathOrFdOrSink === "object") {
- if (typeof this.#pathOrFdOrSink.write === "function") {
- this.#fileSink = this.#pathOrFdOrSink;
- } else {
- throw new Error("Invalid FileSink");
- }
+function NativeWritable_lazyConstruct(stream) {
+ // TODO: Turn this check into check for instanceof FileSink
+ var sink = stream[_pathOrFdOrSink];
+ if (typeof sink === "object") {
+ if (typeof sink.write === "function") {
+ return (stream[_fileSink] = sink);
} else {
- this.#fileSink = Bun.file(this.#pathOrFdOrSink).writer();
+ throw new Error("Invalid FileSink");
}
+ } else {
+ return (stream[_fileSink] = Bun.file(sink).writer());
}
+}
- write(chunk, encoding, cb, native = this.#native) {
- if (!native) {
- this.#native = false;
- return super.write(chunk, encoding, cb);
- }
-
- if (!this.#fileSink) {
- this.#lazyConstruct();
- }
- var fileSink = this.#fileSink;
- var result = fileSink.write(chunk);
-
- if ($isPromise(result)) {
- // var writePromises = this.#writePromises;
- // var i = writePromises.length;
- // writePromises[i] = result;
- result.then(() => {
- this.emit("drain");
- fileSink.flush(true);
- // // We can't naively use i here because we don't know when writes will resolve necessarily
- // writePromises.splice(writePromises.indexOf(result), 1);
- });
- return false;
- }
- fileSink.flush(true);
- // TODO: Should we just have a calculation based on encoding and length of chunk?
- if (cb) cb(null, chunk.byteLength);
- return true;
+const WritablePrototypeWrite = Writable.prototype.write;
+NativeWritable.prototype.write = function NativeWritablePrototypeWrite(chunk, encoding, cb, native) {
+ if (!(native ?? this[_native])) {
+ this[_native] = false;
+ return WritablePrototypeWrite.call(this, chunk, encoding, cb);
}
- end(chunk, encoding, cb, native = this.#native) {
- return super.end(chunk, encoding, cb, native);
+ var fileSink = this[_fileSink] ?? NativeWritable_lazyConstruct(this);
+ var result = fileSink.write(chunk);
+
+ if ($isPromise(result)) {
+ // var writePromises = this.#writePromises;
+ // var i = writePromises.length;
+ // writePromises[i] = result;
+ result.then(() => {
+ this.emit("drain");
+ fileSink.flush(true);
+ // // We can't naively use i here because we don't know when writes will resolve necessarily
+ // writePromises.splice(writePromises.indexOf(result), 1);
+ });
+ return false;
}
+ fileSink.flush(true);
+ // TODO: Should we just have a calculation based on encoding and length of chunk?
+ if (cb) cb(null, chunk.byteLength);
+ return true;
+};
+const WritablePrototypeEnd = Writable.prototype.end;
+NativeWritable.prototype.end = function end(chunk, encoding, cb, native) {
+ return WritablePrototypeEnd.call(this, chunk, encoding, cb, native ?? this[_native]);
+};
- #internalDestroy(error, cb) {
- const w = this._writableState;
- const r = this._readableState;
+function NativeWritable_internalDestroy(error, cb) {
+ const w = this._writableState;
+ const r = this._readableState;
- if (w) {
- w.destroyed = true;
- w.closeEmitted = true;
- }
- if (r) {
- r.destroyed = true;
- r.closeEmitted = true;
- }
+ if (w) {
+ w.destroyed = true;
+ w.closeEmitted = true;
+ }
+ if (r) {
+ r.destroyed = true;
+ r.closeEmitted = true;
+ }
- if (typeof cb === "function") cb(error);
+ if (typeof cb === "function") cb(error);
- if (w?.closeEmitted || r?.closeEmitted) {
- this.emit("close");
- }
+ if (w?.closeEmitted || r?.closeEmitted) {
+ this.emit("close");
}
+}
- #internalFinal(cb) {
- if (this.#fileSink) {
- this.#fileSink.end();
- }
- if (cb) cb();
+function NativeWritable_internalFinal(cb) {
+ var sink = this[_fileSink];
+ if (sink) {
+ sink.end();
}
+ if (cb) cb();
+}
- ref() {
- if (!this.#fileSink) {
- this.#lazyConstruct();
- }
- this.#fileSink.ref();
+NativeWritable.prototype.ref = function ref() {
+ var sink = this[_fileSink];
+ if (!sink) {
+ this.NativeWritable_lazyConstruct();
}
+ sink.ref();
+};
- unref() {
- if (!this.#fileSink) return;
- this.#fileSink.unref();
- }
+NativeWritable.prototype.unref = function unref() {
+ this[_fileSink]?.unref();
};
const exports = require_stream();