UNPKG

fs-zoo

Version:

File system abstractions and implementations

277 lines (276 loc) 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FsaCrud = void 0; const util_1 = require("../crud/util"); const util_2 = require("./util"); class FsaCrud { constructor(root) { this.root = root; this.put = async (path, data, options) => { const [collection, id] = (0, util_1.parseId)(path); (0, util_1.assertType)(collection, 'put', 'crudfs'); (0, util_1.assertName)(id, 'put', 'crudfs'); const [dir] = await this._dir(collection, true); if (typeof data === 'undefined') { const throwIf = options?.throwIf; // Determine entry type let entry = 'none'; try { await dir.getFileHandle(id, { create: false }); entry = 'file'; } catch (e) { if (e.name === 'NotFoundError') { // no file, maybe directory try { await dir.getDirectoryHandle(id, { create: false }); entry = 'dir'; } catch (e2) { if (e2.name === 'NotFoundError') entry = 'none'; else throw e2; } } else if (e.name === 'TypeMismatchError') { // a directory exists at this path entry = 'dir'; } else { throw e; } } switch (throwIf) { case 'exists': { if (entry !== 'none') throw (0, util_2.newExistsError)(); await dir.getDirectoryHandle(id, { create: true }); return; } case 'missing': { if (entry !== 'dir') throw (0, util_2.newMissingError)(); return; // directory exists, nothing to do } default: { if (entry === 'none') await dir.getDirectoryHandle(id, { create: true }); else if (entry === 'file') throw (0, util_2.newExistsError)(); return; } } } let file; switch (options?.throwIf) { case 'exists': { try { file = await dir.getFileHandle(id, { create: false }); throw (0, util_2.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 (0, util_2.newMissingError)(); throw e; } break; } default: { file = await dir.getFileHandle(id, { create: true }); } } let pos = options?.pos; const writable = await file.createWritable({ keepExistingData: typeof pos !== 'undefined' }); switch (typeof pos) { case 'undefined': { await writable.write(data); break; } case 'number': { if (pos === -1) { const blob = await file.getFile(); pos = blob.size; } await writable.write({ type: 'write', data, position: pos }); break; } default: { throw new Error(`Invalid position: ${pos}`); } } await writable.close(); }; this.getStream = async (path) => { const [collection, id] = (0, util_1.parseId)(path); const file = await this._get(collection, id); return file.stream(); }; this.del = async (path, silent) => { const [collection, id] = (0, util_1.parseId)(path); (0, util_1.assertType)(collection, 'del', 'crudfs'); (0, util_1.assertName)(id, 'del', 'crudfs'); try { const [dir] = await this._file(collection, id); await dir.removeEntry(id, { recursive: false }); } catch (error) { if (!silent) throw error; } }; this.info = async (path) => { const [collection, id] = (0, util_1.parseId)(path); const isRootPath = !collection.length && !id; if (!isRootPath) { (0, util_1.assertType)(collection, 'info', 'crudfs'); (0, util_1.assertName)(id, 'info', 'crudfs'); } const permissions = async (item) => { const [read, write] = await Promise.all([ item.queryPermission?.({ mode: 'read' }), item.queryPermission?.({ mode: 'readwrite' }), ]); return { readable: read?.state === 'granted', writable: write?.state === 'granted', }; }; try { const [dir] = await this._dir(collection, false); if (isRootPath) { return { type: 'collection', id: '', ...(await permissions(dir)), }; } const file = await dir.getFileHandle(id); const [blob, perms] = await Promise.all([file.getFile(), permissions(file)]); return { type: 'resource', id, size: blob.size, modified: blob.lastModified, ...perms, }; } catch (error) { if (error.name === 'NotFoundError') throw (0, util_2.newFile404Error)(collection, id); if (error.name !== 'TypeMismatchError') throw error; try { const [cdir] = await this._dir([...collection, id], false); return { type: 'collection', id: '', ...(await permissions(cdir)), }; } catch (e) { if (error.name === 'NotFoundError') throw (0, util_2.newFile404Error)(collection, id); throw e; } } }; this.drop = async (path, silent) => { const collection = (0, util_1.parseParts)(path); (0, util_1.assertType)(collection, 'drop', 'crudfs'); try { const [dir, parent] = await this._dir(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; } }; this.scan = async function* (path) { const collection = (0, util_1.parseParts)(path); (0, util_1.assertType)(collection, 'scan', 'crudfs'); const [dir] = await this._dir(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, }; } } }; this.list = async (path) => { const entries = []; for await (const entry of this.scan(path)) entries.push(entry); return entries; }; this.from = async (path) => { const collection = (0, util_1.parseParts)(path); (0, util_1.assertType)(collection, 'from', 'crudfs'); const [dir] = await this._dir(collection, true); return new FsaCrud(dir); }; } async _dir(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 (0, util_2.newFolder404Error)(collection); throw error; } } async _file(collection, id) { const [dir] = await this._dir(collection, false); try { const file = await dir.getFileHandle(id, { create: false }); return [dir, file]; } catch (error) { if (error.name === 'NotFoundError') throw (0, util_2.newFile404Error)(collection, id); throw error; } } async _get(collection, id) { (0, util_1.assertType)(collection, 'get', 'crudfs'); (0, util_1.assertName)(id, 'get', 'crudfs'); const [, file] = await this._file(collection, id); return await file.getFile(); } } exports.FsaCrud = FsaCrud;