"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