export interface Locker { lock(domain: string, resource: string): Promise; } export class SingletonLocker implements Locker { lockedResource = new Map(); constructor() {} async lock(domain: string, resource: string) { const key = `${domain}:${resource}`; let lock = this.lockedResource.get(key); if (!lock) { lock = new Lock(); this.lockedResource.set(key, lock); } await lock.acquire(); return lock; } } export class Lock { private inner: Promise = Promise.resolve(); private release: () => void = () => {}; async acquire() { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion let release: () => void = null!; const nextLock = new Promise(resolve => { release = resolve; }); await this.inner; this.inner = nextLock; this.release = release; } [Symbol.asyncDispose]() { this.release(); return Promise.resolve(); } }