UNPKG

@zenfs/core

Version:

A filesystem, anywhere

472 lines (471 loc) 15.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; } function next() { while (env.stack.length) { var rec = env.stack.pop(); try { var result = rec.dispose && rec.dispose.call(rec.value); if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } catch (e) { fail(e); } } 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 { ErrnoError } from '../error.js'; import '../polyfills.js'; export class MutexLock { get isLocked() { return this._isLocked; } constructor(previous) { this.previous = previous; this.current = Promise.withResolvers(); this._isLocked = true; } async done() { await this.previous?.done(); await this.current.promise; } unlock() { this.current.resolve(); this._isLocked = false; } [Symbol.dispose]() { this.unlock(); } } /** * @hidden */ export class _MutexedFS { async ready() { return await this._fs.ready(); } metadata() { return this._fs.metadata(); } /** * 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(path, syscall) { const previous = this.currentLock; const lock = this.addLock(); const stack = new Error().stack; setTimeout(() => { if (lock.isLocked) { const error = ErrnoError.With('EDEADLK', path, syscall); error.stack += stack?.slice('Error'.length); throw error; } }, 5000); await previous?.done(); return lock; } /** * Locks `path` asynchronously. * If the path is currently locked, an error will be thrown * @internal */ lockSync(path, syscall) { if (this.currentLock?.isLocked) { throw ErrnoError.With('EBUSY', path, syscall); } 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(oldPath, 'rename'), 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(oldPath, 'rename'), 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(path, 'stat'), 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(path, 'stat'), false); return this._fs.statSync(path); } catch (e_4) { env_4.error = e_4; env_4.hasError = true; } finally { __disposeResources(env_4); } } async openFile(path, flag) { const env_5 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_5, await this.lock(path, 'openFile'), false); const file = await this._fs.openFile(path, flag); file.fs = this; return file; } catch (e_5) { env_5.error = e_5; env_5.hasError = true; } finally { __disposeResources(env_5); } } openFileSync(path, flag) { const env_6 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_6, this.lockSync(path, 'openFile'), false); const file = this._fs.openFileSync(path, flag); file.fs = this; return file; } catch (e_6) { env_6.error = e_6; env_6.hasError = true; } finally { __disposeResources(env_6); } } async createFile(path, flag, mode) { const env_7 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_7, await this.lock(path, 'createFile'), false); const file = await this._fs.createFile(path, flag, mode); file.fs = this; return file; } catch (e_7) { env_7.error = e_7; env_7.hasError = true; } finally { __disposeResources(env_7); } } createFileSync(path, flag, mode) { const env_8 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_8, this.lockSync(path, 'createFile'), false); const file = this._fs.createFileSync(path, flag, mode); file.fs = this; return file; } 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(path, 'unlink'), 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(path, 'unlink'), 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(path, 'rmdir'), 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(path, 'rmdir'), 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, mode) { const env_13 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_13, await this.lock(path, 'mkdir'), false); await this._fs.mkdir(path, mode); } catch (e_13) { env_13.error = e_13; env_13.hasError = true; } finally { __disposeResources(env_13); } } mkdirSync(path, mode) { const env_14 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_14, this.lockSync(path, 'mkdir'), false); return this._fs.mkdirSync(path, mode); } 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(path, 'readdir'), 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(path, 'readdir'), 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(path, 'exists'), 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(path, 'exists'), 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(srcpath, 'link'), 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(srcpath, 'link'), 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(path, data, stats) { const env_21 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_21, await this.lock(path, 'sync'), false); await this._fs.sync(path, data, stats); } catch (e_21) { env_21.error = e_21; env_21.hasError = true; } finally { __disposeResources(env_21); } } syncSync(path, data, stats) { const env_22 = { stack: [], error: void 0, hasError: false }; try { const _ = __addDisposableResource(env_22, this.lockSync(path, 'sync'), false); return this._fs.syncSync(path, data, stats); } catch (e_22) { env_22.error = e_22; env_22.hasError = true; } finally { __disposeResources(env_22); } } } /** * 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 uses 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 * @internal */ export function Mutexed(FS) { class MutexedFS extends _MutexedFS { constructor(...args) { super(); this._fs = new FS(...args); } } return MutexedFS; }