/** * Async lock utility - Promise-based mutex per key. * Ensures only one async operation executes per key at a time. */ const locks = new Map() /** * Get or create a queue for a lock key. * @param {string} lockKey - Lock key * @returns {Promise} Existing or new queue promise */ const getOrCreateQueue = (lockKey) => { const existingQueue = locks.get(lockKey) if (existingQueue) return existingQueue const newQueue = Promise.resolve() locks.set(lockKey, newQueue) return newQueue } /** * Acquire a lock for a key and execute callback. * Only one callback per key executes at a time. * @param {string} key - Lock key * @param {Function} callback - Async function to execute * @returns {Promise} Result of callback */ export async function acquire(key, callback) { const lockKey = String(key) const queue = getOrCreateQueue(lockKey) const next = queue.then(() => callback()).finally(() => { if (locks.get(lockKey) === next) { locks.delete(lockKey) } }) locks.set(lockKey, next) return next } /** * Clear all locks (for testing). */ export function clearLocks() { locks.clear() }