UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

65 lines (64 loc) 1.9 kB
//#region src/infra/net/guarded-body-stream.ts /** * Shared body-stream cleanup for guarded fetch consumers (`fetchWithSsrFGuard` * callers that re-wrap streaming responses). */ const guardedBodyCleanupRegistry = new FinalizationRegistry((held) => { held.finalize(); }); /** * Wraps a guarded response body so caller cleanup runs exactly once when the * stream completes, errors, is cancelled, or is garbage-collected unconsumed. * Cleanup failures are swallowed: releasing guard resources must never break * response consumption. */ function wrapGuardedBodyStream(params) { let reader; let finalized = false; const cleanupRegistrationToken = {}; const finalize = async (cancelReader = async () => { await reader?.cancel().catch(() => void 0); }) => { if (finalized) return; finalized = true; guardedBodyCleanupRegistry.unregister(cleanupRegistrationToken); const [cancellation, release] = await Promise.allSettled([cancelReader(), (async () => { try { reader?.releaseLock(); } finally { try { await params.cleanup(); } catch {} } })()]); if (release.status === "rejected") throw release.reason; if (cancellation.status === "rejected") throw cancellation.reason; }; const wrappedBody = new ReadableStream({ start() { reader = params.body.getReader(); }, async pull(controller) { try { const chunk = await reader?.read(); if (!chunk || chunk.done) { controller.close(); await finalize(); return; } params.refreshTimeout?.(); controller.enqueue(chunk.value); } catch (error) { controller.error(error); await finalize(); } }, async cancel(reason) { await finalize(async () => await reader?.cancel(reason)); } }); guardedBodyCleanupRegistry.register(wrappedBody, { finalize }, cleanupRegistrationToken); return wrappedBody; } //#endregion export { wrapGuardedBodyStream as t };