"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeFileSystemWritableFileStream = exports.createSwapFile = void 0; const buffer_1 = require("../internal/buffer"); /** * When Chrome writes to the file, it creates a copy of the file with extension * `.crswap` and then replaces the original file with the copy only when the * `close()` method is called. If the `abort()` method is called, the `.crswap` * file is deleted. * * If a file name with with extension `.crswap` is already taken, it * creates a new swap file with extension `.1.crswap` and so on. */ const createSwapFile = async (fs, path, keepExistingData) => { let handle; let swapPath = path + '.crswap'; try { handle = await fs.promises.open(swapPath, 'ax'); } catch (error) { if (!error || typeof error !== 'object' || error.code !== 'EEXIST') throw error; } if (!handle) { for (let i = 1; i < 1000; i++) { try { swapPath = `${path}.${i}.crswap`; handle = await fs.promises.open(swapPath, 'ax'); break; } catch (error) { if (!error || typeof error !== 'object' || error.code !== 'EEXIST') throw error; } } } if (!handle) throw new Error(`Could not create a swap file for "${path}".`); if (keepExistingData) await fs.promises.copyFile(path, swapPath, fs.constants.COPYFILE_FICLONE); return [handle, swapPath]; }; exports.createSwapFile = createSwapFile; /** * Is a WritableStream object with additional convenience methods, which * operates on a single file on disk. The interface is accessed through the * `FileSystemFileHandle.createWritable()` method. * * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream */ class NodeFileSystemWritableFileStream extends WritableStream { constructor(fs, path, keepExistingData) { const swap = { handle: undefined, path: '', offset: 0 }; super({ async start() { const promise = (0, exports.createSwapFile)(fs, path, keepExistingData); swap.ready = promise.then(() => undefined); const [handle, swapPath] = await promise; swap.handle = handle; swap.path = swapPath; }, async write(chunk) { await swap.ready; const handle = swap.handle; if (!handle) throw new Error('Invalid state'); const buffer = buffer_1.Buffer.from(typeof chunk === 'string' ? chunk : chunk instanceof Blob ? await chunk.arrayBuffer() : chunk); const { bytesWritten } = await handle.write(buffer, 0, buffer.length, swap.offset); swap.offset += bytesWritten; }, async close() { await swap.ready; const handle = swap.handle; if (!handle) return; await handle.close(); await fs.promises.rename(swap.path, path); }, async abort() { await swap.ready; const handle = swap.handle; if (!handle) return; await handle.close(); await fs.promises.unlink(swap.path); }, }); this.fs = fs; this.path = path; this.swap = swap; } /** * @sse https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/seek * @param position An `unsigned long` describing the byte position from the top * (beginning) of the file. */ async seek(position) { this.swap.offset = position; } /** * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/truncate * @param size An `unsigned long` of the amount of bytes to resize the stream to. */ async truncate(size) { await this.swap.ready; const handle = this.swap.handle; if (!handle) throw new Error('Invalid state'); await handle.truncate(size); if (this.swap.offset > size) this.swap.offset = size; } async writeBase(chunk) { const writer = this.getWriter(); try { await writer.write(chunk); } finally { writer.releaseLock(); } } async write(params) { if (!params) throw new TypeError('Missing required argument: params'); switch (typeof params) { case 'string': { return this.writeBase(params); } case 'object': { const constructor = params.constructor; switch (constructor) { case ArrayBuffer: case Blob: case DataView: return this.writeBase(params); default: { if (ArrayBuffer.isView(params)) { return this.writeBase(params); } else { const options = params; switch (options.type) { case 'write': { if (typeof options.position === 'number') await this.seek(options.position); return this.writeBase(params.data); } case 'truncate': { if (typeof params.size !== 'number') throw new TypeError('Missing required argument: size'); if (this.swap.offset > params.size) this.swap.offset = params.size; return this.truncate(params.size); } case 'seek': if (typeof params.position !== 'number') throw new TypeError('Missing required argument: position'); return this.seek(params.position); default: throw new TypeError('Invalid argument: params'); } } } } } default: throw new TypeError('Invalid argument: params'); } } } exports.NodeFileSystemWritableFileStream = NodeFileSystemWritableFileStream; //# sourceMappingURL=NodeFileSystemWritableFileStream.js.map