@ezdevlol/memfs
Version:
In-memory file-system with Node's fs API.
164 lines (163 loc) • 5.34 kB
JavaScript
import { assertName } from '../node-to-fsa/util';
import { assertType } from '../crud/util';
import { newExistsError, newFile404Error, newFolder404Error, newMissingError } from './util';
export class FsaCrud {
root;
constructor(root) {
this.root = root;
}
async getDir(collection, create) {
let parent = undefined;
let dir = await this.root;
try {
for (const name of collection) {
const child = await dir.getDirectoryHandle(name, { create });
parent = dir;
dir = child;
}
return [dir, parent];
}
catch (error) {
if (error.name === 'NotFoundError')
throw newFolder404Error(collection);
throw error;
}
}
async getFile(collection, id) {
const [dir] = await this.getDir(collection, false);
try {
const file = await dir.getFileHandle(id, { create: false });
return [dir, file];
}
catch (error) {
if (error.name === 'NotFoundError')
throw newFile404Error(collection, id);
throw error;
}
}
put = async (collection, id, data, options) => {
assertType(collection, 'put', 'crudfs');
assertName(id, 'put', 'crudfs');
const [dir] = await this.getDir(collection, true);
let file;
switch (options?.throwIf) {
case 'exists': {
try {
file = await dir.getFileHandle(id, { create: false });
throw newExistsError();
}
catch (e) {
if (e.name !== 'NotFoundError')
throw e;
file = await dir.getFileHandle(id, { create: true });
}
break;
}
case 'missing': {
try {
file = await dir.getFileHandle(id, { create: false });
}
catch (e) {
if (e.name === 'NotFoundError')
throw newMissingError();
throw e;
}
break;
}
default: {
file = await dir.getFileHandle(id, { create: true });
}
}
const writable = await file.createWritable();
await writable.write(data);
await writable.close();
};
get = async (collection, id) => {
assertType(collection, 'get', 'crudfs');
assertName(id, 'get', 'crudfs');
const [, file] = await this.getFile(collection, id);
const blob = await file.getFile();
const buffer = await blob.arrayBuffer();
return new Uint8Array(buffer);
};
del = async (collection, id, silent) => {
assertType(collection, 'del', 'crudfs');
assertName(id, 'del', 'crudfs');
try {
const [dir] = await this.getFile(collection, id);
await dir.removeEntry(id, { recursive: false });
}
catch (error) {
if (!silent)
throw error;
}
};
info = async (collection, id) => {
assertType(collection, 'info', 'crudfs');
if (id) {
assertName(id, 'info', 'crudfs');
const [, file] = await this.getFile(collection, id);
const blob = await file.getFile();
return {
type: 'resource',
id,
size: blob.size,
modified: blob.lastModified,
};
}
else {
await this.getDir(collection, false);
return {
type: 'collection',
id: '',
};
}
};
drop = async (collection, silent) => {
assertType(collection, 'drop', 'crudfs');
try {
const [dir, parent] = await this.getDir(collection, false);
if (parent) {
await parent.removeEntry(dir.name, { recursive: true });
}
else {
const root = await this.root;
for await (const name of root.keys())
await root.removeEntry(name, { recursive: true });
}
}
catch (error) {
if (!silent)
throw error;
}
};
scan = async function* (collection) {
assertType(collection, 'scan', 'crudfs');
const [dir] = await this.getDir(collection, false);
for await (const [id, handle] of dir.entries()) {
if (handle.kind === 'file') {
yield {
type: 'resource',
id,
};
}
else if (handle.kind === 'directory') {
yield {
type: 'collection',
id,
};
}
}
};
list = async (collection) => {
const entries = [];
for await (const entry of this.scan(collection))
entries.push(entry);
return entries;
};
from = async (collection) => {
assertType(collection, 'from', 'crudfs');
const [dir] = await this.getDir(collection, true);
return new FsaCrud(dir);
};
}