UNPKG

mastercache

Version:

Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers

348 lines (347 loc) 10.1 kB
// ../../node_modules/.pnpm/async-mutex@0.5.0/node_modules/async-mutex/index.mjs var E_TIMEOUT = new Error("timeout while waiting for mutex to become available"); var E_ALREADY_LOCKED = new Error("mutex already locked"); var E_CANCELED = new Error("request for lock canceled"); var __awaiter$2 = function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var Semaphore = class { constructor(_value, _cancelError = E_CANCELED) { this._value = _value; this._cancelError = _cancelError; this._queue = []; this._weightedWaiters = []; } acquire(weight = 1, priority = 0) { if (weight <= 0) throw new Error(`invalid weight ${weight}: must be positive`); return new Promise((resolve, reject) => { const task = { resolve, reject, weight, priority }; const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority); if (i === -1 && weight <= this._value) { this._dispatchItem(task); } else { this._queue.splice(i + 1, 0, task); } }); } runExclusive(callback_1) { return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) { const [value, release] = yield this.acquire(weight, priority); try { return yield callback(value); } finally { release(); } }); } waitForUnlock(weight = 1, priority = 0) { if (weight <= 0) throw new Error(`invalid weight ${weight}: must be positive`); if (this._couldLockImmediately(weight, priority)) { return Promise.resolve(); } else { return new Promise((resolve) => { if (!this._weightedWaiters[weight - 1]) this._weightedWaiters[weight - 1] = []; insertSorted(this._weightedWaiters[weight - 1], { resolve, priority }); }); } } isLocked() { return this._value <= 0; } getValue() { return this._value; } setValue(value) { this._value = value; this._dispatchQueue(); } release(weight = 1) { if (weight <= 0) throw new Error(`invalid weight ${weight}: must be positive`); this._value += weight; this._dispatchQueue(); } cancel() { this._queue.forEach((entry) => entry.reject(this._cancelError)); this._queue = []; } _dispatchQueue() { this._drainUnlockWaiters(); while (this._queue.length > 0 && this._queue[0].weight <= this._value) { this._dispatchItem(this._queue.shift()); this._drainUnlockWaiters(); } } _dispatchItem(item) { const previousValue = this._value; this._value -= item.weight; item.resolve([previousValue, this._newReleaser(item.weight)]); } _newReleaser(weight) { let called = false; return () => { if (called) return; called = true; this.release(weight); }; } _drainUnlockWaiters() { if (this._queue.length === 0) { for (let weight = this._value; weight > 0; weight--) { const waiters = this._weightedWaiters[weight - 1]; if (!waiters) continue; waiters.forEach((waiter) => waiter.resolve()); this._weightedWaiters[weight - 1] = []; } } else { const queuedPriority = this._queue[0].priority; for (let weight = this._value; weight > 0; weight--) { const waiters = this._weightedWaiters[weight - 1]; if (!waiters) continue; const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority); (i === -1 ? waiters : waiters.splice(0, i)).forEach((waiter) => waiter.resolve()); } } } _couldLockImmediately(weight, priority) { return (this._queue.length === 0 || this._queue[0].priority < priority) && weight <= this._value; } }; function insertSorted(a, v) { const i = findIndexFromEnd(a, (other) => v.priority <= other.priority); a.splice(i + 1, 0, v); } function findIndexFromEnd(a, predicate) { for (let i = a.length - 1; i >= 0; i--) { if (predicate(a[i])) { return i; } } return -1; } var __awaiter$1 = function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var Mutex = class { constructor(cancelError) { this._semaphore = new Semaphore(1, cancelError); } acquire() { return __awaiter$1(this, arguments, void 0, function* (priority = 0) { const [, releaser] = yield this._semaphore.acquire(1, priority); return releaser; }); } runExclusive(callback, priority = 0) { return this._semaphore.runExclusive(() => callback(), 1, priority); } isLocked() { return this._semaphore.isLocked(); } waitForUnlock(priority = 0) { return this._semaphore.waitForUnlock(1, priority); } release() { if (this._semaphore.isLocked()) this._semaphore.release(); } cancel() { return this._semaphore.cancel(); } }; var __awaiter = function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; function withTimeout(sync, timeout, timeoutError = E_TIMEOUT) { return { acquire: (weightOrPriority, priority) => { let weight; if (isSemaphore(sync)) { weight = weightOrPriority; } else { weight = void 0; priority = weightOrPriority; } if (weight !== void 0 && weight <= 0) { throw new Error(`invalid weight ${weight}: must be positive`); } return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { let isTimeout = false; const handle = setTimeout(() => { isTimeout = true; reject(timeoutError); }, timeout); try { const ticket = yield isSemaphore(sync) ? sync.acquire(weight, priority) : sync.acquire(priority); if (isTimeout) { const release = Array.isArray(ticket) ? ticket[1] : ticket; release(); } else { clearTimeout(handle); resolve(ticket); } } catch (e) { if (!isTimeout) { clearTimeout(handle); reject(e); } } })); }, runExclusive(callback, weight, priority) { return __awaiter(this, void 0, void 0, function* () { let release = () => void 0; try { const ticket = yield this.acquire(weight, priority); if (Array.isArray(ticket)) { release = ticket[1]; return yield callback(ticket[0]); } else { release = ticket; return yield callback(); } } finally { release(); } }); }, release(weight) { sync.release(weight); }, cancel() { return sync.cancel(); }, waitForUnlock: (weightOrPriority, priority) => { let weight; if (isSemaphore(sync)) { weight = weightOrPriority; } else { weight = void 0; priority = weightOrPriority; } if (weight !== void 0 && weight <= 0) { throw new Error(`invalid weight ${weight}: must be positive`); } return new Promise((resolve, reject) => { const handle = setTimeout(() => reject(timeoutError), timeout); (isSemaphore(sync) ? sync.waitForUnlock(weight, priority) : sync.waitForUnlock(priority)).then(() => { clearTimeout(handle); resolve(); }); }); }, isLocked: () => sync.isLocked(), getValue: () => sync.getValue(), setValue: (value) => sync.setValue(value) }; } function isSemaphore(sync) { return sync.getValue !== void 0; } // src/cache/locks.ts var Locks = class { /** * A map that will hold active locks for each key */ #locks = /* @__PURE__ */ new Map(); /** * For a given key, get or create a new lock * * @param key Key to get or create a lock for * @param timeout Time to wait to acquire the lock */ getOrCreateForKey(key, timeout) { let lock = this.#locks.get(key); if (!lock) { lock = new Mutex(); this.#locks.set(key, lock); } return timeout ? withTimeout(lock, timeout) : lock; } release(key, releaser) { releaser(); this.#locks.delete(key); } }; export { Locks }; //# sourceMappingURL=locks.js.map