node-ejs-renderer/node_modules/memfs/lib/node-to-fsa/NodeFileSystemWritableFileStream.js

172 lines
6.8 KiB
JavaScript
Raw Normal View History

2024-06-09 13:55:01 -04:00
"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