856 lines
39 KiB
JavaScript
856 lines
39 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.FsaNodeFs = void 0;
|
|
const tslib_1 = require("tslib");
|
|
const optHelpers = require("../node/options");
|
|
const util = require("../node/util");
|
|
const buffer_1 = require("../internal/buffer");
|
|
const FsPromises_1 = require("../node/FsPromises");
|
|
const util_1 = require("./util");
|
|
const constants_1 = require("../node/constants");
|
|
const encoding_1 = require("../encoding");
|
|
const FsaNodeDirent_1 = require("./FsaNodeDirent");
|
|
const constants_2 = require("../constants");
|
|
const FsaNodeStats_1 = require("./FsaNodeStats");
|
|
const queueMicrotask_1 = require("../queueMicrotask");
|
|
const FsaNodeWriteStream_1 = require("./FsaNodeWriteStream");
|
|
const FsaNodeReadStream_1 = require("./FsaNodeReadStream");
|
|
const FsaNodeCore_1 = require("./FsaNodeCore");
|
|
const FileHandle_1 = require("../node/FileHandle");
|
|
const notSupported = () => {
|
|
throw new Error('Method not supported by the File System Access API.');
|
|
};
|
|
const notImplemented = () => {
|
|
throw new Error('Not implemented');
|
|
};
|
|
const noop = () => { };
|
|
/**
|
|
* Constructs a Node.js `fs` API from a File System Access API
|
|
* [`FileSystemDirectoryHandle` object](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle).
|
|
*/
|
|
class FsaNodeFs extends FsaNodeCore_1.FsaNodeCore {
|
|
constructor() {
|
|
// ------------------------------------------------------------ FsPromisesApi
|
|
super(...arguments);
|
|
this.promises = new FsPromises_1.FsPromises(this, FileHandle_1.FileHandle);
|
|
// ------------------------------------------------------------ FsCallbackApi
|
|
this.open = (path, flags, a, b) => {
|
|
let mode = a;
|
|
let callback = b;
|
|
if (typeof a === 'function') {
|
|
mode = 438 /* MODE.DEFAULT */;
|
|
callback = a;
|
|
}
|
|
mode = mode || 438 /* MODE.DEFAULT */;
|
|
const modeNum = util.modeToNumber(mode);
|
|
const filename = util.pathToFilename(path);
|
|
const flagsNum = util.flagsToNumber(flags);
|
|
this.__open(filename, flagsNum, modeNum).then(openFile => callback(null, openFile.fd), error => callback(error));
|
|
};
|
|
this.close = (fd, callback) => {
|
|
util.validateFd(fd);
|
|
this.__close(fd).then(() => callback(null), error => callback(error));
|
|
};
|
|
this.read = (fd, buffer, offset, length, position, callback) => {
|
|
util.validateCallback(callback);
|
|
// This `if` branch is from Node.js
|
|
if (length === 0) {
|
|
return (0, queueMicrotask_1.default)(() => {
|
|
if (callback)
|
|
callback(null, 0, buffer);
|
|
});
|
|
}
|
|
(async () => {
|
|
const openFile = await this.getFileByFd(fd, 'read');
|
|
const file = await openFile.file.getFile();
|
|
const src = await file.arrayBuffer();
|
|
const slice = new Uint8Array(src, Number(position), Number(length));
|
|
const dest = new Uint8Array(buffer.buffer, buffer.byteOffset + offset, slice.length);
|
|
dest.set(slice, 0);
|
|
return slice.length;
|
|
})().then(bytesWritten => callback(null, bytesWritten, buffer), error => callback(error));
|
|
};
|
|
this.readFile = (id, a, b) => {
|
|
const [opts, callback] = optHelpers.optsAndCbGenerator(optHelpers.getReadFileOptions)(a, b);
|
|
const flagsNum = util.flagsToNumber(opts.flag);
|
|
(async () => {
|
|
let fd = typeof id === 'number' ? id : -1;
|
|
const originalFd = fd;
|
|
try {
|
|
if (fd === -1) {
|
|
const filename = util.pathToFilename(id);
|
|
fd = (await this.__open(filename, flagsNum, 0)).fd;
|
|
}
|
|
const handle = await this.__getFileById(fd, 'readFile');
|
|
const file = await handle.getFile();
|
|
const buffer = buffer_1.Buffer.from(await file.arrayBuffer());
|
|
return util.bufferToEncoding(buffer, opts.encoding);
|
|
}
|
|
finally {
|
|
try {
|
|
const idWasFd = typeof originalFd === 'number' && originalFd >= 0;
|
|
if (idWasFd)
|
|
await this.__close(originalFd);
|
|
}
|
|
catch (_a) { }
|
|
}
|
|
})()
|
|
.then(data => callback(null, data))
|
|
.catch(error => callback(error));
|
|
};
|
|
this.write = (fd, a, b, c, d, e) => {
|
|
const [, asStr, buf, offset, length, position, cb] = util.getWriteArgs(fd, a, b, c, d, e);
|
|
(async () => {
|
|
const openFile = await this.getFileByFd(fd, 'write');
|
|
const data = buf.subarray(offset, offset + length);
|
|
await openFile.write(data, position);
|
|
return length;
|
|
})().then(bytesWritten => cb(null, bytesWritten, asStr ? a : buf), error => cb(error));
|
|
};
|
|
this.writev = (fd, buffers, a, b) => {
|
|
util.validateFd(fd);
|
|
let position = null;
|
|
let callback;
|
|
if (typeof a === 'function') {
|
|
callback = a;
|
|
}
|
|
else {
|
|
position = Number(a);
|
|
callback = b;
|
|
}
|
|
util.validateCallback(callback);
|
|
(async () => {
|
|
const openFile = await this.getFileByFd(fd, 'writev');
|
|
const length = buffers.length;
|
|
let bytesWritten = 0;
|
|
for (let i = 0; i < length; i++) {
|
|
const data = buffers[i];
|
|
await openFile.write(data, position);
|
|
bytesWritten += data.byteLength;
|
|
position = null;
|
|
}
|
|
return bytesWritten;
|
|
})().then(bytesWritten => callback(null, bytesWritten, buffers), error => callback(error));
|
|
};
|
|
this.writeFile = (id, data, a, b) => {
|
|
let options = a;
|
|
let callback = b;
|
|
if (typeof a === 'function') {
|
|
options = optHelpers.writeFileDefaults;
|
|
callback = a;
|
|
}
|
|
const cb = util.validateCallback(callback);
|
|
const opts = optHelpers.getWriteFileOptions(options);
|
|
const flagsNum = util.flagsToNumber(opts.flag);
|
|
const modeNum = util.modeToNumber(opts.mode);
|
|
const buf = util.dataToBuffer(data, opts.encoding);
|
|
(async () => {
|
|
let fd = typeof id === 'number' ? id : -1;
|
|
const originalFd = fd;
|
|
try {
|
|
if (fd === -1) {
|
|
const filename = util.pathToFilename(id);
|
|
fd = (await this.__open(filename, flagsNum, modeNum)).fd;
|
|
}
|
|
const file = await this.__getFileById(fd, 'writeFile');
|
|
const writable = await file.createWritable({ keepExistingData: false });
|
|
await writable.write(buf);
|
|
await writable.close();
|
|
}
|
|
finally {
|
|
try {
|
|
const idWasFd = typeof originalFd === 'number' && originalFd >= 0;
|
|
if (idWasFd)
|
|
await this.__close(originalFd);
|
|
}
|
|
catch (_a) { }
|
|
}
|
|
})().then(() => cb(null), error => cb(error));
|
|
};
|
|
this.copyFile = (src, dest, a, b) => {
|
|
const srcFilename = util.pathToFilename(src);
|
|
const destFilename = util.pathToFilename(dest);
|
|
let flags;
|
|
let callback;
|
|
if (typeof a === 'function') {
|
|
flags = 0;
|
|
callback = a;
|
|
}
|
|
else {
|
|
flags = a;
|
|
callback = b;
|
|
}
|
|
util.validateCallback(callback);
|
|
const [oldFolder, oldName] = (0, util_1.pathToLocation)(srcFilename);
|
|
const [newFolder, newName] = (0, util_1.pathToLocation)(destFilename);
|
|
(async () => {
|
|
const oldFile = await this.getFile(oldFolder, oldName, 'copyFile');
|
|
const newDir = await this.getDir(newFolder, false, 'copyFile');
|
|
const newFile = await newDir.getFileHandle(newName, { create: true });
|
|
const writable = await newFile.createWritable({ keepExistingData: false });
|
|
const oldData = await oldFile.getFile();
|
|
await writable.write(await oldData.arrayBuffer());
|
|
await writable.close();
|
|
})().then(() => callback(null), error => callback(error));
|
|
};
|
|
/**
|
|
* @todo There is a proposal for native "self remove" operation.
|
|
* @see https://github.com/whatwg/fs/blob/main/proposals/Remove.md
|
|
*/
|
|
this.unlink = (path, callback) => {
|
|
const filename = util.pathToFilename(path);
|
|
const [folder, name] = (0, util_1.pathToLocation)(filename);
|
|
this.getDir(folder, false, 'unlink')
|
|
.then(dir => dir.removeEntry(name))
|
|
.then(() => callback(null), error => {
|
|
if (error && typeof error === 'object') {
|
|
switch (error.name) {
|
|
case 'NotFoundError': {
|
|
callback(util.createError('ENOENT', 'unlink', filename));
|
|
return;
|
|
}
|
|
case 'InvalidModificationError': {
|
|
callback(util.createError('EISDIR', 'unlink', filename));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
callback(error);
|
|
});
|
|
};
|
|
this.realpath = (path, a, b) => {
|
|
const [opts, callback] = optHelpers.getRealpathOptsAndCb(a, b);
|
|
let pathFilename = util.pathToFilename(path);
|
|
if (pathFilename[0] !== "/" /* FsaToNodeConstants.Separator */)
|
|
pathFilename = "/" /* FsaToNodeConstants.Separator */ + pathFilename;
|
|
callback(null, (0, encoding_1.strToEncoding)(pathFilename, opts.encoding));
|
|
};
|
|
this.stat = (path, a, b) => {
|
|
const [{ bigint = false, throwIfNoEntry = true }, callback] = optHelpers.getStatOptsAndCb(a, b);
|
|
const filename = util.pathToFilename(path);
|
|
const [folder, name] = (0, util_1.pathToLocation)(filename);
|
|
(async () => {
|
|
const handle = await this.getFileOrDir(folder, name, 'stat');
|
|
return await this.getHandleStats(bigint, handle);
|
|
})().then(stats => callback(null, stats), error => callback(error));
|
|
};
|
|
this.lstat = this.stat;
|
|
this.fstat = (fd, a, b) => {
|
|
const [{ bigint = false, throwIfNoEntry = true }, callback] = optHelpers.getStatOptsAndCb(a, b);
|
|
(async () => {
|
|
const openFile = await this.getFileByFd(fd, 'fstat');
|
|
return await this.getHandleStats(bigint, openFile.file);
|
|
})().then(stats => callback(null, stats), error => callback(error));
|
|
};
|
|
/**
|
|
* @todo There is a proposal for native move support.
|
|
* @see https://github.com/whatwg/fs/blob/main/proposals/MovingNonOpfsFiles.md
|
|
*/
|
|
this.rename = (oldPath, newPath, callback) => {
|
|
const oldPathFilename = util.pathToFilename(oldPath);
|
|
const newPathFilename = util.pathToFilename(newPath);
|
|
const [oldFolder, oldName] = (0, util_1.pathToLocation)(oldPathFilename);
|
|
const [newFolder, newName] = (0, util_1.pathToLocation)(newPathFilename);
|
|
(async () => {
|
|
const oldFile = await this.getFile(oldFolder, oldName, 'rename');
|
|
const newDir = await this.getDir(newFolder, false, 'rename');
|
|
const newFile = await newDir.getFileHandle(newName, { create: true });
|
|
const writable = await newFile.createWritable({ keepExistingData: false });
|
|
const oldData = await oldFile.getFile();
|
|
await writable.write(await oldData.arrayBuffer());
|
|
await writable.close();
|
|
const oldDir = await this.getDir(oldFolder, false, 'rename');
|
|
await oldDir.removeEntry(oldName);
|
|
})().then(() => callback(null), error => callback(error));
|
|
};
|
|
this.exists = (path, callback) => {
|
|
const filename = util.pathToFilename(path);
|
|
if (typeof callback !== 'function')
|
|
throw Error(constants_1.ERRSTR.CB);
|
|
this.access(path, 0 /* AMODE.F_OK */, error => callback(!error));
|
|
};
|
|
this.access = (path, a, b) => {
|
|
let mode = 0 /* AMODE.F_OK */;
|
|
let callback;
|
|
if (typeof a !== 'function') {
|
|
mode = a | 0; // cast to number
|
|
callback = util.validateCallback(b);
|
|
}
|
|
else {
|
|
callback = a;
|
|
}
|
|
const filename = util.pathToFilename(path);
|
|
const [folder, name] = (0, util_1.pathToLocation)(filename);
|
|
(async () => {
|
|
const node = folder.length || name ? await this.getFileOrDir(folder, name, 'access') : await this.root;
|
|
const checkIfCanExecute = mode & 1 /* AMODE.X_OK */;
|
|
if (checkIfCanExecute)
|
|
throw util.createError('EACCESS', 'access', filename);
|
|
const checkIfCanWrite = mode & 2 /* AMODE.W_OK */;
|
|
switch (node.kind) {
|
|
case 'file': {
|
|
if (checkIfCanWrite) {
|
|
try {
|
|
const file = node;
|
|
const writable = await file.createWritable();
|
|
await writable.close();
|
|
}
|
|
catch (_a) {
|
|
throw util.createError('EACCESS', 'access', filename);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'directory': {
|
|
if (checkIfCanWrite) {
|
|
const dir = node;
|
|
const canWrite = await (0, util_1.testDirectoryIsWritable)(dir);
|
|
if (!canWrite)
|
|
throw util.createError('EACCESS', 'access', filename);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
throw util.createError('EACCESS', 'access', filename);
|
|
}
|
|
}
|
|
})().then(() => callback(null), error => callback(error));
|
|
};
|
|
this.appendFile = (id, data, a, b) => {
|
|
const [opts, callback] = optHelpers.getAppendFileOptsAndCb(a, b);
|
|
const buffer = util.dataToBuffer(data, opts.encoding);
|
|
this.getFileByIdOrCreate(id, 'appendFile')
|
|
.then(file => (async () => {
|
|
const blob = await file.getFile();
|
|
const writable = await file.createWritable({ keepExistingData: true });
|
|
await writable.write({
|
|
type: 'write',
|
|
data: buffer,
|
|
position: blob.size,
|
|
});
|
|
await writable.close();
|
|
})())
|
|
.then(() => callback(null), error => callback(error));
|
|
};
|
|
this.readdir = (path, a, b) => {
|
|
const [options, callback] = optHelpers.getReaddirOptsAndCb(a, b);
|
|
const filename = util.pathToFilename(path);
|
|
const [folder, name] = (0, util_1.pathToLocation)(filename);
|
|
if (name)
|
|
folder.push(name);
|
|
this.getDir(folder, false, 'readdir')
|
|
.then(dir => (async () => {
|
|
var _a, e_1, _b, _c, _d, e_2, _e, _f;
|
|
if (options.withFileTypes) {
|
|
const list = [];
|
|
try {
|
|
for (var _g = true, _h = tslib_1.__asyncValues(dir.entries()), _j; _j = await _h.next(), _a = _j.done, !_a; _g = true) {
|
|
_c = _j.value;
|
|
_g = false;
|
|
const [name, handle] = _c;
|
|
const dirent = new FsaNodeDirent_1.FsaNodeDirent(name, handle.kind);
|
|
list.push(dirent);
|
|
}
|
|
}
|
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
finally {
|
|
try {
|
|
if (!_g && !_a && (_b = _h.return)) await _b.call(_h);
|
|
}
|
|
finally { if (e_1) throw e_1.error; }
|
|
}
|
|
if (!util.isWin && options.encoding !== 'buffer')
|
|
list.sort((a, b) => {
|
|
if (a.name < b.name)
|
|
return -1;
|
|
if (a.name > b.name)
|
|
return 1;
|
|
return 0;
|
|
});
|
|
return list;
|
|
}
|
|
else {
|
|
const list = [];
|
|
try {
|
|
for (var _k = true, _l = tslib_1.__asyncValues(dir.keys()), _m; _m = await _l.next(), _d = _m.done, !_d; _k = true) {
|
|
_f = _m.value;
|
|
_k = false;
|
|
const key = _f;
|
|
list.push(key);
|
|
}
|
|
}
|
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
finally {
|
|
try {
|
|
if (!_k && !_d && (_e = _l.return)) await _e.call(_l);
|
|
}
|
|
finally { if (e_2) throw e_2.error; }
|
|
}
|
|
if (!util.isWin && options.encoding !== 'buffer')
|
|
list.sort();
|
|
return list;
|
|
}
|
|
})())
|
|
.then(res => callback(null, res), err => callback(err));
|
|
};
|
|
this.readlink = (path, a, b) => {
|
|
const [opts, callback] = optHelpers.getDefaultOptsAndCb(a, b);
|
|
const filename = util.pathToFilename(path);
|
|
const buffer = buffer_1.Buffer.from(filename);
|
|
callback(null, util.bufferToEncoding(buffer, opts.encoding));
|
|
};
|
|
/** @todo Could this use `FileSystemSyncAccessHandle.flush` through a Worker thread? */
|
|
this.fsync = (fd, callback) => {
|
|
callback(null);
|
|
};
|
|
this.fdatasync = (fd, callback) => {
|
|
callback(null);
|
|
};
|
|
this.ftruncate = (fd, a, b) => {
|
|
const len = typeof a === 'number' ? a : 0;
|
|
const callback = util.validateCallback(typeof a === 'number' ? b : a);
|
|
this.getFileByFdAsync(fd)
|
|
.then(file => file.file.createWritable({ keepExistingData: true }))
|
|
.then(writable => writable.truncate(len).then(() => writable.close()))
|
|
.then(() => callback(null), error => callback(error));
|
|
};
|
|
this.truncate = (path, a, b) => {
|
|
const len = typeof a === 'number' ? a : 0;
|
|
const callback = util.validateCallback(typeof a === 'number' ? b : a);
|
|
this.open(path, 'r+', (error, fd) => {
|
|
if (error)
|
|
callback(error);
|
|
else {
|
|
this.ftruncate(fd, len, error => {
|
|
if (error)
|
|
this.close(fd, () => callback(error));
|
|
else
|
|
this.close(fd, callback);
|
|
});
|
|
}
|
|
});
|
|
};
|
|
this.futimes = (fd, atime, mtime, callback) => {
|
|
callback(null);
|
|
};
|
|
this.utimes = (path, atime, mtime, callback) => {
|
|
callback(null);
|
|
};
|
|
this.mkdir = (path, a, b) => {
|
|
var _a;
|
|
const opts = optHelpers.getMkdirOptions(a);
|
|
const callback = util.validateCallback(typeof a === 'function' ? a : b);
|
|
// const modeNum = modeToNumber(opts.mode, 0o777);
|
|
const filename = util.pathToFilename(path);
|
|
const [folder, name] = (0, util_1.pathToLocation)(filename);
|
|
// TODO: need to throw if directory already exists
|
|
this.getDir(folder, (_a = opts.recursive) !== null && _a !== void 0 ? _a : false)
|
|
.then(dir => dir.getDirectoryHandle(name, { create: true }))
|
|
.then(() => callback(null), error => {
|
|
if (error && typeof error === 'object') {
|
|
switch (error.name) {
|
|
case 'NotFoundError': {
|
|
const err = util.createError('ENOENT', 'mkdir', folder.join('/'));
|
|
callback(err);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
callback(error);
|
|
});
|
|
};
|
|
this.mkdtemp = (prefix, a, b) => {
|
|
const [{ encoding }, callback] = optHelpers.getDefaultOptsAndCb(a, b);
|
|
if (!prefix || typeof prefix !== 'string')
|
|
throw new TypeError('filename prefix is required');
|
|
if (!util.nullCheck(prefix))
|
|
return;
|
|
const filename = prefix + util.genRndStr6();
|
|
this.mkdir(filename, 511 /* MODE.DIR */, err => {
|
|
if (err)
|
|
callback(err);
|
|
else
|
|
callback(null, (0, encoding_1.strToEncoding)(filename, encoding));
|
|
});
|
|
};
|
|
this.rmdir = (path, a, b) => {
|
|
const options = optHelpers.getRmdirOptions(a);
|
|
const callback = util.validateCallback(typeof a === 'function' ? a : b);
|
|
const [folder, name] = (0, util_1.pathToLocation)(util.pathToFilename(path));
|
|
if (!name && options.recursive)
|
|
return this.rmAll(callback);
|
|
this.getDir(folder, false, 'rmdir')
|
|
.then(dir => dir.getDirectoryHandle(name).then(() => dir))
|
|
.then(dir => { var _a; return dir.removeEntry(name, { recursive: (_a = options.recursive) !== null && _a !== void 0 ? _a : false }); })
|
|
.then(() => callback(null), error => {
|
|
if (error && typeof error === 'object') {
|
|
switch (error.name) {
|
|
case 'NotFoundError': {
|
|
const err = util.createError('ENOENT', 'rmdir', folder.join('/'));
|
|
callback(err);
|
|
return;
|
|
}
|
|
case 'InvalidModificationError': {
|
|
const err = util.createError('ENOTEMPTY', 'rmdir', folder.join('/'));
|
|
callback(err);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
callback(error);
|
|
});
|
|
};
|
|
this.rm = (path, a, b) => {
|
|
const [options, callback] = optHelpers.getRmOptsAndCb(a, b);
|
|
const [folder, name] = (0, util_1.pathToLocation)(util.pathToFilename(path));
|
|
if (!name && options.recursive)
|
|
return this.rmAll(callback);
|
|
this.getDir(folder, false, 'rmdir')
|
|
.then(dir => { var _a; return dir.removeEntry(name, { recursive: (_a = options.recursive) !== null && _a !== void 0 ? _a : false }); })
|
|
.then(() => callback(null), error => {
|
|
if (options.force) {
|
|
callback(null);
|
|
return;
|
|
}
|
|
if (error && typeof error === 'object') {
|
|
switch (error.name) {
|
|
case 'NotFoundError': {
|
|
const err = util.createError('ENOENT', 'rmdir', folder.join('/'));
|
|
callback(err);
|
|
return;
|
|
}
|
|
case 'InvalidModificationError': {
|
|
const err = util.createError('ENOTEMPTY', 'rmdir', folder.join('/'));
|
|
callback(err);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
callback(error);
|
|
});
|
|
};
|
|
this.fchmod = (fd, mode, callback) => {
|
|
callback(null);
|
|
};
|
|
this.chmod = (path, mode, callback) => {
|
|
callback(null);
|
|
};
|
|
this.lchmod = (path, mode, callback) => {
|
|
callback(null);
|
|
};
|
|
this.fchown = (fd, uid, gid, callback) => {
|
|
callback(null);
|
|
};
|
|
this.chown = (path, uid, gid, callback) => {
|
|
callback(null);
|
|
};
|
|
this.lchown = (path, uid, gid, callback) => {
|
|
callback(null);
|
|
};
|
|
this.createWriteStream = (path, options) => {
|
|
var _a;
|
|
const defaults = {
|
|
encoding: 'utf8',
|
|
flags: 'w',
|
|
autoClose: true,
|
|
emitClose: true,
|
|
};
|
|
const optionsObj = optHelpers.getOptions(defaults, options);
|
|
const filename = util.pathToFilename(path);
|
|
const flags = util.flagsToNumber((_a = optionsObj.flags) !== null && _a !== void 0 ? _a : 'w');
|
|
const fd = optionsObj.fd ? (typeof optionsObj.fd === 'number' ? optionsObj.fd : optionsObj.fd.fd) : 0;
|
|
const handle = fd ? this.getFileByFdAsync(fd) : this.__open(filename, flags, 0);
|
|
const stream = new FsaNodeWriteStream_1.FsaNodeWriteStream(handle, filename, optionsObj);
|
|
if (optionsObj.autoClose) {
|
|
stream.once('finish', () => {
|
|
handle.then(file => this.close(file.fd, () => { }));
|
|
});
|
|
stream.once('error', () => {
|
|
handle.then(file => this.close(file.fd, () => { }));
|
|
});
|
|
}
|
|
return stream;
|
|
};
|
|
this.createReadStream = (path, options) => {
|
|
const defaults = {
|
|
flags: 'r',
|
|
fd: null,
|
|
mode: 0o666,
|
|
autoClose: true,
|
|
emitClose: true,
|
|
start: 0,
|
|
end: Infinity,
|
|
highWaterMark: 64 * 1024,
|
|
fs: null,
|
|
signal: null,
|
|
};
|
|
const optionsObj = optHelpers.getOptions(defaults, options);
|
|
const filename = util.pathToFilename(path);
|
|
const flags = util.flagsToNumber(optionsObj.flags);
|
|
const fd = optionsObj.fd ? (typeof optionsObj.fd === 'number' ? optionsObj.fd : optionsObj.fd.fd) : 0;
|
|
const handle = fd ? this.getFileByFdAsync(fd) : this.__open(filename, flags, 0);
|
|
const stream = new FsaNodeReadStream_1.FsaNodeReadStream(this, handle, filename, optionsObj);
|
|
return stream;
|
|
};
|
|
this.cp = notImplemented;
|
|
this.lutimes = notImplemented;
|
|
this.openAsBlob = notImplemented;
|
|
this.opendir = notImplemented;
|
|
this.readv = notImplemented;
|
|
this.statfs = notImplemented;
|
|
/**
|
|
* @todo Watchers could be implemented in the future on top of `FileSystemObserver`,
|
|
* which is currently a proposal.
|
|
* @see https://github.com/whatwg/fs/blob/main/proposals/FileSystemObserver.md
|
|
*/
|
|
this.watchFile = notSupported;
|
|
this.unwatchFile = notSupported;
|
|
this.watch = notSupported;
|
|
this.symlink = notSupported;
|
|
this.link = notSupported;
|
|
// --------------------------------------------------------- FsSynchronousApi
|
|
this.statSync = (path, options) => {
|
|
var _a;
|
|
const { bigint = true, throwIfNoEntry = true } = optHelpers.getStatOptions(options);
|
|
const filename = util.pathToFilename(path);
|
|
const location = (0, util_1.pathToLocation)(filename);
|
|
const adapter = this.getSyncAdapter();
|
|
const res = adapter.call('stat', location);
|
|
const stats = new FsaNodeStats_1.FsaNodeStats(bigint, (_a = res.size) !== null && _a !== void 0 ? _a : 0, res.kind);
|
|
return stats;
|
|
};
|
|
this.lstatSync = this.statSync;
|
|
this.fstatSync = (fd, options) => {
|
|
const filename = this.getFileName(fd);
|
|
return this.statSync(filename, options);
|
|
};
|
|
this.accessSync = (path, mode = 0 /* AMODE.F_OK */) => {
|
|
const filename = util.pathToFilename(path);
|
|
mode = mode | 0;
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('access', [filename, mode]);
|
|
};
|
|
this.readFileSync = (id, options) => {
|
|
const opts = optHelpers.getReadFileOptions(options);
|
|
const flagsNum = util.flagsToNumber(opts.flag);
|
|
const filename = this.getFileName(id);
|
|
const adapter = this.getSyncAdapter();
|
|
const uint8 = adapter.call('readFile', [filename, opts]);
|
|
const buffer = buffer_1.Buffer.from(uint8.buffer, uint8.byteOffset, uint8.byteLength);
|
|
return util.bufferToEncoding(buffer, opts.encoding);
|
|
};
|
|
this.writeFileSync = (id, data, options) => {
|
|
const opts = optHelpers.getWriteFileOptions(options);
|
|
const flagsNum = util.flagsToNumber(opts.flag);
|
|
const modeNum = util.modeToNumber(opts.mode);
|
|
const buf = util.dataToBuffer(data, opts.encoding);
|
|
const filename = this.getFileName(id);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('writeFile', [filename, util.bufToUint8(buf), opts]);
|
|
};
|
|
this.appendFileSync = (id, data, options) => {
|
|
const opts = optHelpers.getAppendFileOpts(options);
|
|
if (!opts.flag || util.isFd(id))
|
|
opts.flag = 'a';
|
|
const filename = this.getFileName(id);
|
|
const buf = util.dataToBuffer(data, opts.encoding);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('appendFile', [filename, util.bufToUint8(buf), opts]);
|
|
};
|
|
this.closeSync = (fd) => {
|
|
util.validateFd(fd);
|
|
const file = this.getFileByFd(fd, 'close');
|
|
file.close().catch(() => { });
|
|
this.fds.delete(fd);
|
|
this.releasedFds.push(fd);
|
|
};
|
|
this.existsSync = (path) => {
|
|
try {
|
|
this.statSync(path);
|
|
return true;
|
|
}
|
|
catch (_a) {
|
|
return false;
|
|
}
|
|
};
|
|
this.copyFileSync = (src, dest, flags) => {
|
|
const srcFilename = util.pathToFilename(src);
|
|
const destFilename = util.pathToFilename(dest);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('copy', [srcFilename, destFilename, flags]);
|
|
};
|
|
this.renameSync = (oldPath, newPath) => {
|
|
const srcFilename = util.pathToFilename(oldPath);
|
|
const destFilename = util.pathToFilename(newPath);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('move', [srcFilename, destFilename]);
|
|
};
|
|
this.rmdirSync = (path, opts) => {
|
|
const filename = util.pathToFilename(path);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('rmdir', [filename, opts]);
|
|
};
|
|
this.rmSync = (path, options) => {
|
|
const filename = util.pathToFilename(path);
|
|
const adapter = this.getSyncAdapter();
|
|
adapter.call('rm', [filename, options]);
|
|
};
|
|
this.mkdirSync = (path, options) => {
|
|
const opts = optHelpers.getMkdirOptions(options);
|
|
const modeNum = util.modeToNumber(opts.mode, 0o777);
|
|
const filename = util.pathToFilename(path);
|
|
return this.getSyncAdapter().call('mkdir', [filename, options]);
|
|
};
|
|
this.mkdtempSync = (prefix, options) => {
|
|
const { encoding } = optHelpers.getDefaultOpts(options);
|
|
if (!prefix || typeof prefix !== 'string')
|
|
throw new TypeError('filename prefix is required');
|
|
util.nullCheck(prefix);
|
|
const result = this.getSyncAdapter().call('mkdtemp', [prefix, options]);
|
|
return (0, encoding_1.strToEncoding)(result, encoding);
|
|
};
|
|
this.readlinkSync = (path, options) => {
|
|
const opts = optHelpers.getDefaultOpts(options);
|
|
const filename = util.pathToFilename(path);
|
|
const buffer = buffer_1.Buffer.from(filename);
|
|
return util.bufferToEncoding(buffer, opts.encoding);
|
|
};
|
|
this.truncateSync = (id, len) => {
|
|
if (util.isFd(id))
|
|
return this.ftruncateSync(id, len);
|
|
const filename = util.pathToFilename(id);
|
|
this.getSyncAdapter().call('trunc', [filename, Number(len) || 0]);
|
|
};
|
|
this.ftruncateSync = (fd, len) => {
|
|
const filename = this.getFileName(fd);
|
|
this.truncateSync(filename, len);
|
|
};
|
|
this.unlinkSync = (path) => {
|
|
const filename = util.pathToFilename(path);
|
|
this.getSyncAdapter().call('unlink', [filename]);
|
|
};
|
|
this.readdirSync = (path, options) => {
|
|
const opts = optHelpers.getReaddirOptions(options);
|
|
const filename = util.pathToFilename(path);
|
|
const adapter = this.getSyncAdapter();
|
|
const list = adapter.call('readdir', [filename]);
|
|
if (opts.withFileTypes) {
|
|
const res = [];
|
|
for (const entry of list)
|
|
res.push(new FsaNodeDirent_1.FsaNodeDirent(entry.name, entry.kind));
|
|
return res;
|
|
}
|
|
else {
|
|
const res = [];
|
|
for (const entry of list) {
|
|
const buffer = buffer_1.Buffer.from(entry.name);
|
|
res.push(util.bufferToEncoding(buffer, opts.encoding));
|
|
}
|
|
return res;
|
|
}
|
|
};
|
|
this.realpathSync = (path, options) => {
|
|
let filename = util.pathToFilename(path);
|
|
const { encoding } = optHelpers.getRealpathOptions(options);
|
|
if (filename[0] !== "/" /* FsaToNodeConstants.Separator */)
|
|
filename = "/" /* FsaToNodeConstants.Separator */ + filename;
|
|
return (0, encoding_1.strToEncoding)(filename, encoding);
|
|
};
|
|
this.readSync = (fd, buffer, offset, length, position) => {
|
|
util.validateFd(fd);
|
|
const filename = this.getFileName(fd);
|
|
const adapter = this.getSyncAdapter();
|
|
const uint8 = adapter.call('read', [filename, position, length]);
|
|
const dest = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
dest.set(uint8, offset);
|
|
return uint8.length;
|
|
};
|
|
this.writeSync = (fd, a, b, c, d) => {
|
|
const [, buf, offset, length, position] = util.getWriteSyncArgs(fd, a, b, c, d);
|
|
const filename = this.getFileName(fd);
|
|
const data = new Uint8Array(buf.buffer, buf.byteOffset + offset, length);
|
|
return this.getSyncAdapter().call('write', [filename, data, position || null]);
|
|
};
|
|
this.openSync = (path, flags, mode = 438 /* MODE.DEFAULT */) => {
|
|
const modeNum = util.modeToNumber(mode);
|
|
const filename = util.pathToFilename(path);
|
|
const flagsNum = util.flagsToNumber(flags);
|
|
const adapter = this.getSyncAdapter();
|
|
const handle = adapter.call('open', [filename, flagsNum, modeNum]);
|
|
const openFile = this.__open2(handle, filename, flagsNum, modeNum);
|
|
return openFile.fd;
|
|
};
|
|
this.writevSync = (fd, buffers, position) => {
|
|
if (buffers.length === 0)
|
|
return;
|
|
this.writeSync(fd, buffers[0], 0, buffers[0].byteLength, position);
|
|
for (let i = 1; i < buffers.length; i++) {
|
|
this.writeSync(fd, buffers[i], 0, buffers[i].byteLength, null);
|
|
}
|
|
};
|
|
this.fdatasyncSync = noop;
|
|
this.fsyncSync = noop;
|
|
this.chmodSync = noop;
|
|
this.chownSync = noop;
|
|
this.fchmodSync = noop;
|
|
this.fchownSync = noop;
|
|
this.futimesSync = noop;
|
|
this.lchmodSync = noop;
|
|
this.lchownSync = noop;
|
|
this.utimesSync = noop;
|
|
this.lutimesSync = noop;
|
|
this.cpSync = notImplemented;
|
|
this.opendirSync = notImplemented;
|
|
this.statfsSync = notImplemented;
|
|
this.readvSync = notImplemented;
|
|
this.symlinkSync = notSupported;
|
|
this.linkSync = notSupported;
|
|
// ---------------------------------------------------------- FsCommonObjects
|
|
this.F_OK = constants_2.constants.F_OK;
|
|
this.R_OK = constants_2.constants.R_OK;
|
|
this.W_OK = constants_2.constants.W_OK;
|
|
this.X_OK = constants_2.constants.X_OK;
|
|
this.constants = constants_2.constants;
|
|
this.Dirent = FsaNodeDirent_1.FsaNodeDirent;
|
|
this.Stats = (FsaNodeStats_1.FsaNodeStats);
|
|
this.WriteStream = FsaNodeWriteStream_1.FsaNodeWriteStream;
|
|
this.ReadStream = FsaNodeReadStream_1.FsaNodeReadStream;
|
|
this.StatFs = 0;
|
|
this.Dir = 0;
|
|
this.StatsWatcher = 0;
|
|
this.FSWatcher = 0;
|
|
}
|
|
async getHandleStats(bigint, handle) {
|
|
let size = 0;
|
|
if (handle.kind === 'file') {
|
|
const file = handle;
|
|
const fileData = await file.getFile();
|
|
size = fileData.size;
|
|
}
|
|
const stats = new FsaNodeStats_1.FsaNodeStats(bigint, bigint ? BigInt(size) : size, handle.kind);
|
|
return stats;
|
|
}
|
|
rmAll(callback) {
|
|
(async () => {
|
|
var _a, e_3, _b, _c;
|
|
const root = await this.root;
|
|
try {
|
|
for (var _d = true, _e = tslib_1.__asyncValues(root.keys()), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
|
|
_c = _f.value;
|
|
_d = false;
|
|
const name = _c;
|
|
await root.removeEntry(name, { recursive: true });
|
|
}
|
|
}
|
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
finally {
|
|
try {
|
|
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
}
|
|
finally { if (e_3) throw e_3.error; }
|
|
}
|
|
})().then(() => callback(null), error => callback(error));
|
|
}
|
|
}
|
|
exports.FsaNodeFs = FsaNodeFs;
|
|
//# sourceMappingURL=FsaNodeFs.js.map
|