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

100 lines 3.65 kB
/** * Lifecycle callback firing + dedupe. * * The same thrown error can travel through multiple layers that each * want to surface it to the consumer's `onError`: * - LifecycleMiddleware's `wrapGenerate` / `wrapStream` catch * - `BaseProvider.wrapStreamWithLifecycleCallbacks` (raw-fetch * streaming providers that bypass AI SDK middleware) * - `BaseProvider.fireLifecycleErrorCallback` (top-level provider catch) * - `NeuroLink.generate()` / `NeuroLink.stream()` (early-resolution * failures, before the language model is wrapped) * * Without a shared dedupe these layers would fire `onError` multiple * times for one logical failure. This module stamps a non-enumerable * `Symbol.for("neurolink.onErrorFired")` on the error the first time * a firing site is reached; subsequent sites observe the stamp and * skip their own fire. * * `Symbol.for` (rather than a local Symbol) so the same key works * across modules — anyone who can read the symbol can read the stamp. * * Frozen / sealed / non-extensible errors: `Object.defineProperty` * throws, we catch and proceed. Worst case is a single duplicate fire * (the pre-shared-marker behaviour). `WeakSet`-based bookkeeping * would handle this case cleanly but the symbol stamp is preferred * for cross-realm consistency: a Symbol.for-keyed property survives * structuredClone / cross-realm postMessage where a closed-over * WeakSet does not. */ const ON_ERROR_FIRED = Symbol.for("neurolink.onErrorFired"); function stampFired(error) { try { Object.defineProperty(error, ON_ERROR_FIRED, { value: true, enumerable: false, writable: false, configurable: false, }); } catch { // Non-extensible — fall through; worst case is a duplicate fire. } } /** * Returns true when `markLifecycleErrorFired` or a previous * `fireOnErrorOnce` call has already stamped this error. */ export function hasLifecycleErrorFired(error) { if (error === null || typeof error !== "object") { return false; } return error[ON_ERROR_FIRED] === true; } /** * Stamps the error as already-fired without invoking any callback. * Use this from sites that already invoked `onError` via their own * path (e.g. a provider-specific raw-fetch stream wrapper) so the * shared dedupe still works. */ export function markLifecycleErrorFired(error) { if (error === null || typeof error !== "object") { return; } if (error[ON_ERROR_FIRED] === true) { return; } stampFired(error); } /** * Fire the consumer's `onError` once per logical failure. * * - No-op when `onError` is missing. * - No-op when the error is already stamped (any prior layer fired). * - Otherwise: stamps the error, then invokes the callback. * * The callback is fire-and-forget; rejections are swallowed so a * faulty handler can't mask the original throw. Callers that need * to AWAIT the callback (e.g. to enforce a timeout) should use * `hasLifecycleErrorFired` + `markLifecycleErrorFired` directly and * run the callback themselves. */ export function fireOnErrorOnce(onError, error, payload) { if (typeof onError !== "function") { return; } if (hasLifecycleErrorFired(error)) { return; } if (error !== null && typeof error === "object") { stampFired(error); } try { const result = onError(payload); Promise.resolve(result).catch(() => undefined); } catch { // Consumer callback errors must not poison the original throw. } } //# sourceMappingURL=lifecycleCallbacks.js.map