UNPKG

@zenfs/core

Version:

A filesystem, anywhere

596 lines (595 loc) 18.2 kB
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { return function (env) { function fail(e) { env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while (r = env.stack.pop()) { try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }); import { withErrno } from 'kerium'; import { err } from 'kerium/log'; import '../polyfills.js'; /** * @category Internals * @internal */ export class MutexLock { previous; current = Promise.withResolvers(); _isLocked = true; get isLocked() { return this._isLocked; } constructor(previous) { this.previous = previous; } async done() { await this.previous?.done(); await this.current.promise; } unlock() { this.current.resolve(); this._isLocked = false; } [Symbol.dispose]() { this.unlock(); } } /** * @hidden * @category Internals */ export class _MutexedFS { /** * @internal */ _fs; get type() { return this._fs.type; } get name() { return this._fs.name; } get label() { return this._fs.label; } set label(value) { this._fs.label = value; } get attributes() { return this._fs.attributes; } get _uuid() { return this._fs._uuid; } set _uuid(value) { this._fs._uuid = value; } get uuid() { return this._fs.uuid; } async ready() { return await this._fs.ready(); } readySync() { return this._fs.readySync(); } usage() { return this._fs.usage(); } /** * The current locks */ currentLock; /** * Adds a lock for a path */ addLock() { const lock = new MutexLock(this.currentLock); this.currentLock = lock; return lock; } /** * Locks `path` asynchronously. * If the path is currently locked, waits for it to be unlocked. * @internal */ async lock(timeout = 5000) { const previous = this.currentLock; const lock = this.addLock(); const stack = new Error().stack; setTimeout(() => { if (lock.isLocked) { const error = withErrno('EDEADLK'); error.stack += stack?.slice('Error'.length); throw err(error); } }, timeout); await previous?.done(); return lock; } /** * Locks `path` asynchronously. * If the path is currently locked, an error will be thrown * @internal */ lockSync() { if (this.currentLock?.isLocked) { throw err(withErrno('EBUSY')); } return this.addLock(); } /** * Whether `path` is locked * @internal */ get isLocked() { return !!this.currentLock?.isLocked; } /* eslint-disable @typescript-eslint/no-unused-vars */ async rename(oldPath, newPath) { const env_1 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_1, await this.lock(), false); await this._fs.rename(oldPath, newPath); } catch (e_1) { env_1.error = e_1; env_1.hasError = true; } finally { __disposeResources(env_1); } } renameSync(oldPath, newPath) { const env_2 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_2, this.lockSync(), false); return this._fs.renameSync(oldPath, newPath); } catch (e_2) { env_2.error = e_2; env_2.hasError = true; } finally { __disposeResources(env_2); } } async stat(path) { const env_3 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_3, await this.lock(), false); return await this._fs.stat(path); } catch (e_3) { env_3.error = e_3; env_3.hasError = true; } finally { __disposeResources(env_3); } } statSync(path) { const env_4 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_4, this.lockSync(), false); return this._fs.statSync(path); } catch (e_4) { env_4.error = e_4; env_4.hasError = true; } finally { __disposeResources(env_4); } } async touch(path, metadata) { const env_5 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_5, await this.lock(), false); await this._fs.touch(path, metadata); } catch (e_5) { env_5.error = e_5; env_5.hasError = true; } finally { __disposeResources(env_5); } } touchSync(path, metadata) { const env_6 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_6, this.lockSync(), false); this._fs.touchSync(path, metadata); } catch (e_6) { env_6.error = e_6; env_6.hasError = true; } finally { __disposeResources(env_6); } } async createFile(path, options) { const env_7 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_7, await this.lock(), false); return await this._fs.createFile(path, options); } catch (e_7) { env_7.error = e_7; env_7.hasError = true; } finally { __disposeResources(env_7); } } createFileSync(path, options) { const env_8 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_8, this.lockSync(), false); return this._fs.createFileSync(path, options); } catch (e_8) { env_8.error = e_8; env_8.hasError = true; } finally { __disposeResources(env_8); } } async unlink(path) { const env_9 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_9, await this.lock(), false); await this._fs.unlink(path); } catch (e_9) { env_9.error = e_9; env_9.hasError = true; } finally { __disposeResources(env_9); } } unlinkSync(path) { const env_10 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_10, this.lockSync(), false); return this._fs.unlinkSync(path); } catch (e_10) { env_10.error = e_10; env_10.hasError = true; } finally { __disposeResources(env_10); } } async rmdir(path) { const env_11 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_11, await this.lock(), false); await this._fs.rmdir(path); } catch (e_11) { env_11.error = e_11; env_11.hasError = true; } finally { __disposeResources(env_11); } } rmdirSync(path) { const env_12 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_12, this.lockSync(), false); return this._fs.rmdirSync(path); } catch (e_12) { env_12.error = e_12; env_12.hasError = true; } finally { __disposeResources(env_12); } } async mkdir(path, options) { const env_13 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_13, await this.lock(), false); return await this._fs.mkdir(path, options); } catch (e_13) { env_13.error = e_13; env_13.hasError = true; } finally { __disposeResources(env_13); } } mkdirSync(path, options) { const env_14 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_14, this.lockSync(), false); return this._fs.mkdirSync(path, options); } catch (e_14) { env_14.error = e_14; env_14.hasError = true; } finally { __disposeResources(env_14); } } async readdir(path) { const env_15 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_15, await this.lock(), false); return await this._fs.readdir(path); } catch (e_15) { env_15.error = e_15; env_15.hasError = true; } finally { __disposeResources(env_15); } } readdirSync(path) { const env_16 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_16, this.lockSync(), false); return this._fs.readdirSync(path); } catch (e_16) { env_16.error = e_16; env_16.hasError = true; } finally { __disposeResources(env_16); } } async exists(path) { const env_17 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_17, await this.lock(), false); return await this._fs.exists(path); } catch (e_17) { env_17.error = e_17; env_17.hasError = true; } finally { __disposeResources(env_17); } } existsSync(path) { const env_18 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_18, this.lockSync(), false); return this._fs.existsSync(path); } catch (e_18) { env_18.error = e_18; env_18.hasError = true; } finally { __disposeResources(env_18); } } async link(srcpath, dstpath) { const env_19 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_19, await this.lock(), false); await this._fs.link(srcpath, dstpath); } catch (e_19) { env_19.error = e_19; env_19.hasError = true; } finally { __disposeResources(env_19); } } linkSync(srcpath, dstpath) { const env_20 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_20, this.lockSync(), false); return this._fs.linkSync(srcpath, dstpath); } catch (e_20) { env_20.error = e_20; env_20.hasError = true; } finally { __disposeResources(env_20); } } async sync() { const env_21 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_21, await this.lock(), false); await this._fs.sync(); } catch (e_21) { env_21.error = e_21; env_21.hasError = true; } finally { __disposeResources(env_21); } } syncSync() { const env_22 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_22, this.lockSync(), false); return this._fs.syncSync(); } catch (e_22) { env_22.error = e_22; env_22.hasError = true; } finally { __disposeResources(env_22); } } async read(path, buffer, offset, end) { const env_23 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_23, await this.lock(), false); return await this._fs.read(path, buffer, offset, end); } catch (e_23) { env_23.error = e_23; env_23.hasError = true; } finally { __disposeResources(env_23); } } readSync(path, buffer, offset, end) { const env_24 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_24, this.lockSync(), false); return this._fs.readSync(path, buffer, offset, end); } catch (e_24) { env_24.error = e_24; env_24.hasError = true; } finally { __disposeResources(env_24); } } async write(path, buffer, offset) { const env_25 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_25, await this.lock(), false); return await this._fs.write(path, buffer, offset); } catch (e_25) { env_25.error = e_25; env_25.hasError = true; } finally { __disposeResources(env_25); } } writeSync(path, buffer, offset) { const env_26 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_26, this.lockSync(), false); return this._fs.writeSync(path, buffer, offset); } catch (e_26) { env_26.error = e_26; env_26.hasError = true; } finally { __disposeResources(env_26); } } streamRead(path, options) { const env_27 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_27, this.lockSync(), false); return this._fs.streamRead(path, options); } catch (e_27) { env_27.error = e_27; env_27.hasError = true; } finally { __disposeResources(env_27); } } streamWrite(path, options) { const env_28 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_28, this.lockSync(), false); return this._fs.streamWrite(path, options); } catch (e_28) { env_28.error = e_28; env_28.hasError = true; } finally { __disposeResources(env_28); } } } /** * This serializes access to an underlying async filesystem. * For example, on an OverlayFS instance with an async lower * directory operations like rename and rmdir may involve multiple * requests involving both the upper and lower file systems -- they * are not executed in a single atomic step. OverlayFS used to use this * to avoid having to reason about the correctness of * multiple requests interleaving. * * @privateRemarks * Instead of extending the passed class, `MutexedFS` stores it internally. * This is to avoid a deadlock caused when a method calls another one * The problem is discussed extensively in [#78](https://github.com/zen-fs/core/issues/78) * Instead of extending `FileSystem`, * `MutexedFS` implements it in order to make sure all of the methods are passed through * * @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding * @category Internals * @internal */ export function Mutexed(FS) { class MutexedFS extends _MutexedFS { constructor(...args) { super(); this._fs = new FS(...args); } } return MutexedFS; }