index.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.WriteStream = exports.ReadStream = exports.ReadAfterReleasedError = exports.ReadAfterDestroyedError = void 0;
  7. const crypto_1 = __importDefault(require("crypto"));
  8. const fs_1 = __importDefault(require("fs"));
  9. const os_1 = __importDefault(require("os"));
  10. const path_1 = __importDefault(require("path"));
  11. const stream_1 = require("stream");
  12. class ReadAfterDestroyedError extends Error {
  13. }
  14. exports.ReadAfterDestroyedError = ReadAfterDestroyedError;
  15. class ReadAfterReleasedError extends Error {
  16. }
  17. exports.ReadAfterReleasedError = ReadAfterReleasedError;
  18. class ReadStream extends stream_1.Readable {
  19. constructor(writeStream, options) {
  20. super({
  21. highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
  22. encoding: options === null || options === void 0 ? void 0 : options.encoding,
  23. autoDestroy: true,
  24. });
  25. this._pos = 0;
  26. this._writeStream = writeStream;
  27. }
  28. _read(n) {
  29. if (this.destroyed)
  30. return;
  31. if (typeof this._writeStream["_fd"] !== "number") {
  32. this._writeStream.once("ready", () => this._read(n));
  33. return;
  34. }
  35. // Using `allocUnsafe` here is OK because we return a slice the length of
  36. // `bytesRead`, and discard the rest. This prevents node from having to zero
  37. // out the entire allocation first.
  38. const buf = Buffer.allocUnsafe(n);
  39. fs_1.default.read(this._writeStream["_fd"], buf, 0, n, this._pos, (error, bytesRead) => {
  40. if (error)
  41. this.destroy(error);
  42. // Push any read bytes into the local stream buffer.
  43. if (bytesRead) {
  44. this._pos += bytesRead;
  45. this.push(buf.slice(0, bytesRead));
  46. return;
  47. }
  48. // If there were no more bytes to read and the write stream is finished,
  49. // than this stream has reached the end.
  50. if (this._writeStream._writableState.finished) {
  51. this.push(null);
  52. return;
  53. }
  54. // Otherwise, wait for the write stream to add more data or finish.
  55. const retry = () => {
  56. this._writeStream.removeListener("finish", retry);
  57. this._writeStream.removeListener("write", retry);
  58. this._read(n);
  59. };
  60. this._writeStream.addListener("finish", retry);
  61. this._writeStream.addListener("write", retry);
  62. });
  63. }
  64. }
  65. exports.ReadStream = ReadStream;
  66. class WriteStream extends stream_1.Writable {
  67. constructor(options) {
  68. super({
  69. highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
  70. defaultEncoding: options === null || options === void 0 ? void 0 : options.defaultEncoding,
  71. autoDestroy: false,
  72. });
  73. this._fd = null;
  74. this._path = null;
  75. this._pos = 0;
  76. this._readStreams = new Set();
  77. this._released = false;
  78. this._cleanupSync = () => {
  79. process.removeListener("exit", this._cleanupSync);
  80. if (typeof this._fd === "number")
  81. try {
  82. fs_1.default.closeSync(this._fd);
  83. }
  84. catch (error) {
  85. // An error here probably means the fd was already closed, but we can
  86. // still try to unlink the file.
  87. }
  88. try {
  89. if (this._path)
  90. fs_1.default.unlinkSync(this._path);
  91. }
  92. catch (error) {
  93. // If we are unable to unlink the file, the operating system will clean
  94. // up on next restart, since we use store thes in `os.tmpdir()`
  95. }
  96. };
  97. // Generate a random filename.
  98. crypto_1.default.randomBytes(16, (error, buffer) => {
  99. if (error) {
  100. this.destroy(error);
  101. return;
  102. }
  103. this._path = path_1.default.join(os_1.default.tmpdir(), `capacitor-${buffer.toString("hex")}.tmp`);
  104. // Create a file in the OS's temporary files directory.
  105. fs_1.default.open(this._path, "wx+", 0o600, (error, fd) => {
  106. if (error) {
  107. this.destroy(error);
  108. return;
  109. }
  110. // Cleanup when the process exits or is killed.
  111. process.addListener("exit", this._cleanupSync);
  112. this._fd = fd;
  113. this.emit("ready");
  114. });
  115. });
  116. }
  117. _final(callback) {
  118. if (typeof this._fd !== "number") {
  119. this.once("ready", () => this._final(callback));
  120. return;
  121. }
  122. callback();
  123. }
  124. _write(chunk, encoding, callback) {
  125. if (typeof this._fd !== "number") {
  126. this.once("ready", () => this._write(chunk, encoding, callback));
  127. return;
  128. }
  129. fs_1.default.write(this._fd, chunk, 0, chunk.length, this._pos, (error) => {
  130. if (error) {
  131. callback(error);
  132. return;
  133. }
  134. // It's safe to increment `this._pos` after flushing to the filesystem
  135. // because node streams ensure that only one `_write()` is active at a
  136. // time. If this assumption is broken, the behavior of this library is
  137. // undefined, regardless of where this is incremented. Relocating this
  138. // to increment syncronously would result in correct file contents, but
  139. // the out-of-order writes would still open the potential for read streams
  140. // to scan positions that have not yet been written.
  141. this._pos += chunk.length;
  142. this.emit("write");
  143. callback();
  144. });
  145. }
  146. release() {
  147. this._released = true;
  148. if (this._readStreams.size === 0)
  149. this.destroy();
  150. }
  151. _destroy(error, callback) {
  152. const fd = this._fd;
  153. const path = this._path;
  154. if (typeof fd !== "number" || typeof path !== "string") {
  155. this.once("ready", () => this._destroy(error, callback));
  156. return;
  157. }
  158. // Close the file descriptor.
  159. fs_1.default.close(fd, (closeError) => {
  160. // An error here probably means the fd was already closed, but we can
  161. // still try to unlink the file.
  162. fs_1.default.unlink(path, (unlinkError) => {
  163. // If we are unable to unlink the file, the operating system will
  164. // clean up on next restart, since we use store thes in `os.tmpdir()`
  165. this._fd = null;
  166. // We avoid removing this until now in case an exit occurs while
  167. // asyncronously cleaning up.
  168. process.removeListener("exit", this._cleanupSync);
  169. callback(unlinkError || closeError || error);
  170. });
  171. });
  172. // Destroy all attached read streams.
  173. for (const readStream of this._readStreams)
  174. readStream.destroy(error || undefined);
  175. }
  176. createReadStream(options) {
  177. if (this.destroyed)
  178. throw new ReadAfterDestroyedError("A ReadStream cannot be created from a destroyed WriteStream.");
  179. if (this._released)
  180. throw new ReadAfterReleasedError("A ReadStream cannot be created from a released WriteStream.");
  181. const readStream = new ReadStream(this, options);
  182. this._readStreams.add(readStream);
  183. const remove = () => {
  184. readStream.removeListener("close", remove);
  185. this._readStreams.delete(readStream);
  186. if (this._released && this._readStreams.size === 0) {
  187. this.destroy();
  188. }
  189. };
  190. readStream.addListener("close", remove);
  191. return readStream;
  192. }
  193. }
  194. exports.WriteStream = WriteStream;
  195. exports.default = {
  196. WriteStream,
  197. ReadStream,
  198. ReadAfterDestroyedError,
  199. ReadAfterReleasedError,
  200. };
  201. //# sourceMappingURL=index.js.map