10
0

NodeFileSystemWritableFileStream.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.NodeFileSystemWritableFileStream = exports.createSwapFile = void 0;
  4. const buffer_1 = require("../internal/buffer");
  5. /**
  6. * When Chrome writes to the file, it creates a copy of the file with extension
  7. * `.crswap` and then replaces the original file with the copy only when the
  8. * `close()` method is called. If the `abort()` method is called, the `.crswap`
  9. * file is deleted.
  10. *
  11. * If a file name with with extension `.crswap` is already taken, it
  12. * creates a new swap file with extension `.1.crswap` and so on.
  13. */
  14. const createSwapFile = async (fs, path, keepExistingData) => {
  15. let handle;
  16. let swapPath = path + '.crswap';
  17. try {
  18. handle = await fs.promises.open(swapPath, 'ax');
  19. }
  20. catch (error) {
  21. if (!error || typeof error !== 'object' || error.code !== 'EEXIST')
  22. throw error;
  23. }
  24. if (!handle) {
  25. for (let i = 1; i < 1000; i++) {
  26. try {
  27. swapPath = `${path}.${i}.crswap`;
  28. handle = await fs.promises.open(swapPath, 'ax');
  29. break;
  30. }
  31. catch (error) {
  32. if (!error || typeof error !== 'object' || error.code !== 'EEXIST')
  33. throw error;
  34. }
  35. }
  36. }
  37. if (!handle)
  38. throw new Error(`Could not create a swap file for "${path}".`);
  39. if (keepExistingData)
  40. await fs.promises.copyFile(path, swapPath, fs.constants.COPYFILE_FICLONE);
  41. return [handle, swapPath];
  42. };
  43. exports.createSwapFile = createSwapFile;
  44. /**
  45. * Is a WritableStream object with additional convenience methods, which
  46. * operates on a single file on disk. The interface is accessed through the
  47. * `FileSystemFileHandle.createWritable()` method.
  48. *
  49. * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream
  50. */
  51. class NodeFileSystemWritableFileStream extends WritableStream {
  52. constructor(fs, path, keepExistingData) {
  53. const swap = { handle: undefined, path: '', offset: 0 };
  54. super({
  55. async start() {
  56. const promise = (0, exports.createSwapFile)(fs, path, keepExistingData);
  57. swap.ready = promise.then(() => undefined);
  58. const [handle, swapPath] = await promise;
  59. swap.handle = handle;
  60. swap.path = swapPath;
  61. },
  62. async write(chunk) {
  63. await swap.ready;
  64. const handle = swap.handle;
  65. if (!handle)
  66. throw new Error('Invalid state');
  67. const buffer = buffer_1.Buffer.from(typeof chunk === 'string' ? chunk : chunk instanceof Blob ? await chunk.arrayBuffer() : chunk);
  68. const { bytesWritten } = await handle.write(buffer, 0, buffer.length, swap.offset);
  69. swap.offset += bytesWritten;
  70. },
  71. async close() {
  72. await swap.ready;
  73. const handle = swap.handle;
  74. if (!handle)
  75. return;
  76. await handle.close();
  77. await fs.promises.rename(swap.path, path);
  78. },
  79. async abort() {
  80. await swap.ready;
  81. const handle = swap.handle;
  82. if (!handle)
  83. return;
  84. await handle.close();
  85. await fs.promises.unlink(swap.path);
  86. },
  87. });
  88. this.fs = fs;
  89. this.path = path;
  90. this.swap = swap;
  91. }
  92. /**
  93. * @sse https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/seek
  94. * @param position An `unsigned long` describing the byte position from the top
  95. * (beginning) of the file.
  96. */
  97. async seek(position) {
  98. this.swap.offset = position;
  99. }
  100. /**
  101. * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/truncate
  102. * @param size An `unsigned long` of the amount of bytes to resize the stream to.
  103. */
  104. async truncate(size) {
  105. await this.swap.ready;
  106. const handle = this.swap.handle;
  107. if (!handle)
  108. throw new Error('Invalid state');
  109. await handle.truncate(size);
  110. if (this.swap.offset > size)
  111. this.swap.offset = size;
  112. }
  113. async writeBase(chunk) {
  114. const writer = this.getWriter();
  115. try {
  116. await writer.write(chunk);
  117. }
  118. finally {
  119. writer.releaseLock();
  120. }
  121. }
  122. async write(params) {
  123. if (!params)
  124. throw new TypeError('Missing required argument: params');
  125. switch (typeof params) {
  126. case 'string': {
  127. return this.writeBase(params);
  128. }
  129. case 'object': {
  130. const constructor = params.constructor;
  131. switch (constructor) {
  132. case ArrayBuffer:
  133. case Blob:
  134. case DataView:
  135. return this.writeBase(params);
  136. default: {
  137. if (ArrayBuffer.isView(params)) {
  138. return this.writeBase(params);
  139. }
  140. else {
  141. const options = params;
  142. switch (options.type) {
  143. case 'write': {
  144. if (typeof options.position === 'number')
  145. await this.seek(options.position);
  146. return this.writeBase(params.data);
  147. }
  148. case 'truncate': {
  149. if (typeof params.size !== 'number')
  150. throw new TypeError('Missing required argument: size');
  151. if (this.swap.offset > params.size)
  152. this.swap.offset = params.size;
  153. return this.truncate(params.size);
  154. }
  155. case 'seek':
  156. if (typeof params.position !== 'number')
  157. throw new TypeError('Missing required argument: position');
  158. return this.seek(params.position);
  159. default:
  160. throw new TypeError('Invalid argument: params');
  161. }
  162. }
  163. }
  164. }
  165. }
  166. default:
  167. throw new TypeError('Invalid argument: params');
  168. }
  169. }
  170. }
  171. exports.NodeFileSystemWritableFileStream = NodeFileSystemWritableFileStream;
  172. //# sourceMappingURL=NodeFileSystemWritableFileStream.js.map