UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

61 lines 2.1 kB
/** * Promise-based async mutex for serializing concurrent async operations. * No external dependencies — uses a simple FIFO queue of pending resolvers. */ export class AsyncMutex { /** Default timeout for lock-held operations: 30 seconds */ static DEFAULT_TIMEOUT_MS = 30_000; _locked = false; _queue = []; isLocked() { return this._locked; } async runExclusive(fn, timeoutMs = AsyncMutex.DEFAULT_TIMEOUT_MS) { await this._acquire(); let timer; // Store fn()'s promise so we can ensure it settles before releasing the lock, // even when the timeout fires first. const fnPromise = fn(); try { return await Promise.race([ fnPromise, new Promise((_resolve, reject) => { timer = setTimeout(() => reject(new Error(`AsyncMutex: lock-held operation timed out after ${timeoutMs}ms`)), timeoutMs); }), ]); } finally { if (timer !== undefined) { clearTimeout(timer); } // Wait for fn() to settle before releasing the lock. // If the timeout fired, fn() may still be running — we must not // let another caller acquire the lock until fn() completes. await fnPromise.catch((_settleErr) => { // Intentionally swallowed — we only await settlement to keep // the lock held until fn() finishes. The caller already received // the timeout rejection from Promise.race above. }); this._release(); } } _acquire() { if (!this._locked) { this._locked = true; return Promise.resolve(); } return new Promise((resolve) => { this._queue.push(resolve); }); } _release() { const next = this._queue.shift(); if (next) { next(); } else { this._locked = false; } } } //# sourceMappingURL=asyncMutex.js.map