201 lines
8.1 KiB
JavaScript
201 lines
8.1 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.WriteStream = exports.ReadStream = exports.ReadAfterReleasedError = exports.ReadAfterDestroyedError = void 0;
|
|
const crypto_1 = __importDefault(require("crypto"));
|
|
const fs_1 = __importDefault(require("fs"));
|
|
const os_1 = __importDefault(require("os"));
|
|
const path_1 = __importDefault(require("path"));
|
|
const stream_1 = require("stream");
|
|
class ReadAfterDestroyedError extends Error {
|
|
}
|
|
exports.ReadAfterDestroyedError = ReadAfterDestroyedError;
|
|
class ReadAfterReleasedError extends Error {
|
|
}
|
|
exports.ReadAfterReleasedError = ReadAfterReleasedError;
|
|
class ReadStream extends stream_1.Readable {
|
|
constructor(writeStream, options) {
|
|
super({
|
|
highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
|
|
encoding: options === null || options === void 0 ? void 0 : options.encoding,
|
|
autoDestroy: true,
|
|
});
|
|
this._pos = 0;
|
|
this._writeStream = writeStream;
|
|
}
|
|
_read(n) {
|
|
if (this.destroyed)
|
|
return;
|
|
if (typeof this._writeStream["_fd"] !== "number") {
|
|
this._writeStream.once("ready", () => this._read(n));
|
|
return;
|
|
}
|
|
// Using `allocUnsafe` here is OK because we return a slice the length of
|
|
// `bytesRead`, and discard the rest. This prevents node from having to zero
|
|
// out the entire allocation first.
|
|
const buf = Buffer.allocUnsafe(n);
|
|
fs_1.default.read(this._writeStream["_fd"], buf, 0, n, this._pos, (error, bytesRead) => {
|
|
if (error)
|
|
this.destroy(error);
|
|
// Push any read bytes into the local stream buffer.
|
|
if (bytesRead) {
|
|
this._pos += bytesRead;
|
|
this.push(buf.slice(0, bytesRead));
|
|
return;
|
|
}
|
|
// If there were no more bytes to read and the write stream is finished,
|
|
// than this stream has reached the end.
|
|
if (this._writeStream._writableState.finished) {
|
|
this.push(null);
|
|
return;
|
|
}
|
|
// Otherwise, wait for the write stream to add more data or finish.
|
|
const retry = () => {
|
|
this._writeStream.removeListener("finish", retry);
|
|
this._writeStream.removeListener("write", retry);
|
|
this._read(n);
|
|
};
|
|
this._writeStream.addListener("finish", retry);
|
|
this._writeStream.addListener("write", retry);
|
|
});
|
|
}
|
|
}
|
|
exports.ReadStream = ReadStream;
|
|
class WriteStream extends stream_1.Writable {
|
|
constructor(options) {
|
|
super({
|
|
highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
|
|
defaultEncoding: options === null || options === void 0 ? void 0 : options.defaultEncoding,
|
|
autoDestroy: false,
|
|
});
|
|
this._fd = null;
|
|
this._path = null;
|
|
this._pos = 0;
|
|
this._readStreams = new Set();
|
|
this._released = false;
|
|
this._cleanupSync = () => {
|
|
process.removeListener("exit", this._cleanupSync);
|
|
if (typeof this._fd === "number")
|
|
try {
|
|
fs_1.default.closeSync(this._fd);
|
|
}
|
|
catch (error) {
|
|
// An error here probably means the fd was already closed, but we can
|
|
// still try to unlink the file.
|
|
}
|
|
try {
|
|
if (this._path)
|
|
fs_1.default.unlinkSync(this._path);
|
|
}
|
|
catch (error) {
|
|
// If we are unable to unlink the file, the operating system will clean
|
|
// up on next restart, since we use store thes in `os.tmpdir()`
|
|
}
|
|
};
|
|
// Generate a random filename.
|
|
crypto_1.default.randomBytes(16, (error, buffer) => {
|
|
if (error) {
|
|
this.destroy(error);
|
|
return;
|
|
}
|
|
this._path = path_1.default.join(os_1.default.tmpdir(), `capacitor-${buffer.toString("hex")}.tmp`);
|
|
// Create a file in the OS's temporary files directory.
|
|
fs_1.default.open(this._path, "wx+", 0o600, (error, fd) => {
|
|
if (error) {
|
|
this.destroy(error);
|
|
return;
|
|
}
|
|
// Cleanup when the process exits or is killed.
|
|
process.addListener("exit", this._cleanupSync);
|
|
this._fd = fd;
|
|
this.emit("ready");
|
|
});
|
|
});
|
|
}
|
|
_final(callback) {
|
|
if (typeof this._fd !== "number") {
|
|
this.once("ready", () => this._final(callback));
|
|
return;
|
|
}
|
|
callback();
|
|
}
|
|
_write(chunk, encoding, callback) {
|
|
if (typeof this._fd !== "number") {
|
|
this.once("ready", () => this._write(chunk, encoding, callback));
|
|
return;
|
|
}
|
|
fs_1.default.write(this._fd, chunk, 0, chunk.length, this._pos, (error) => {
|
|
if (error) {
|
|
callback(error);
|
|
return;
|
|
}
|
|
// It's safe to increment `this._pos` after flushing to the filesystem
|
|
// because node streams ensure that only one `_write()` is active at a
|
|
// time. If this assumption is broken, the behavior of this library is
|
|
// undefined, regardless of where this is incremented. Relocating this
|
|
// to increment syncronously would result in correct file contents, but
|
|
// the out-of-order writes would still open the potential for read streams
|
|
// to scan positions that have not yet been written.
|
|
this._pos += chunk.length;
|
|
this.emit("write");
|
|
callback();
|
|
});
|
|
}
|
|
release() {
|
|
this._released = true;
|
|
if (this._readStreams.size === 0)
|
|
this.destroy();
|
|
}
|
|
_destroy(error, callback) {
|
|
const fd = this._fd;
|
|
const path = this._path;
|
|
if (typeof fd !== "number" || typeof path !== "string") {
|
|
this.once("ready", () => this._destroy(error, callback));
|
|
return;
|
|
}
|
|
// Close the file descriptor.
|
|
fs_1.default.close(fd, (closeError) => {
|
|
// An error here probably means the fd was already closed, but we can
|
|
// still try to unlink the file.
|
|
fs_1.default.unlink(path, (unlinkError) => {
|
|
// If we are unable to unlink the file, the operating system will
|
|
// clean up on next restart, since we use store thes in `os.tmpdir()`
|
|
this._fd = null;
|
|
// We avoid removing this until now in case an exit occurs while
|
|
// asyncronously cleaning up.
|
|
process.removeListener("exit", this._cleanupSync);
|
|
callback(unlinkError || closeError || error);
|
|
});
|
|
});
|
|
// Destroy all attached read streams.
|
|
for (const readStream of this._readStreams)
|
|
readStream.destroy(error || undefined);
|
|
}
|
|
createReadStream(options) {
|
|
if (this.destroyed)
|
|
throw new ReadAfterDestroyedError("A ReadStream cannot be created from a destroyed WriteStream.");
|
|
if (this._released)
|
|
throw new ReadAfterReleasedError("A ReadStream cannot be created from a released WriteStream.");
|
|
const readStream = new ReadStream(this, options);
|
|
this._readStreams.add(readStream);
|
|
const remove = () => {
|
|
readStream.removeListener("close", remove);
|
|
this._readStreams.delete(readStream);
|
|
if (this._released && this._readStreams.size === 0) {
|
|
this.destroy();
|
|
}
|
|
};
|
|
readStream.addListener("close", remove);
|
|
return readStream;
|
|
}
|
|
}
|
|
exports.WriteStream = WriteStream;
|
|
exports.default = {
|
|
WriteStream,
|
|
ReadStream,
|
|
ReadAfterDestroyedError,
|
|
ReadAfterReleasedError,
|
|
};
|
|
//# sourceMappingURL=index.js.map
|