UNPKG

fs-zoo

Version:

File system abstractions and implementations

265 lines (264 loc) 9.36 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; } 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 && typeof error === 'object' && 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 && typeof error === 'object' && 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(); } async write(path, options) { const [collection, id] = (0, util_1.parseId)(path); (0, util_1.assertType)(collection, 'write', 'crudfs'); (0, util_1.assertName)(id, 'write', 'crudfs'); const [dir] = await this._dir(collection, true); let file; switch (options?.throwIf) { case 'exists': { try { file = await dir.getFileHandle(id, { create: false }); throw (0, util_2.newExistsError)(); } catch (e) { if (e && typeof e === 'object' && 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 && typeof e === 'object' && 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: pos !== void 0 }); if (pos === -1) { const blob = await file.getFile(); pos = blob.size; } else pos ?? (pos = 0); return new WritableStream({ write: async (chunk) => { await writable.write({ type: 'write', data: chunk, position: pos }); pos += chunk.length; }, close: async () => { await writable.close(); }, }); } async dir(path, options) { const [collection, id] = (0, util_1.parseId)(path); (0, util_1.assertType)(collection, 'dir', 'crudfs'); (0, util_1.assertName)(id, 'dir', 'crudfs'); let info; try { info = await this.info(path); } catch (e) { if (e && typeof e === 'object') { const name = e.name; if (name !== 'ResourceNotFound' && name !== 'CollectionNotFound') throw e; } } if (info && info.type !== 'collection') throw (0, util_2.newExistsError)(); const throwIf = options?.throwIf; if (throwIf === 'exists') { if (info) throw (0, util_2.newExistsError)(); return; } if (throwIf === 'missing') { if (!info) throw (0, util_2.newMissingError)(); } const [dir] = await this._dir(collection, true); await dir.getDirectoryHandle(id, { create: true }); } async put(path, data, options) { const writable = await this.write(path, options); const writer = writable.getWriter(); await writer.write(data); await writer.close(); } async read(path) { const [collection, id] = (0, util_1.parseId)(path); const file = await this._get(collection, id); return file.stream(); } async file(path) { const [collection, id] = (0, util_1.parseId)(path); (0, util_1.assertType)(collection, 'get', 'crudfs'); (0, util_1.assertName)(id, 'get', 'crudfs'); return await this._get(collection, id); } async del(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; } } async info(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 && typeof error === 'object' && error.name === 'NotFoundError') throw (0, util_2.newFile404Error)(collection, id); if (error && typeof error === 'object' && 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 && typeof error === 'object' && error.name === 'NotFoundError') throw (0, util_2.newFile404Error)(collection, id); throw e; } } } async drop(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; } } async *scan(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, }; } } } async list(path) { const entries = []; for await (const entry of this.scan(path)) entries.push(entry); return entries; } async from(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); } } exports.FsaCrud = FsaCrud;