UNPKG

inngest

Version:

Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.

114 lines (112 loc) 3.93 kB
const require_streaming = require('../../components/execution/streaming.cjs'); //#region src/experimental/durable-endpoints/client.ts /** * Entrypoint file for client-side Durable Endpoint utilities. */ /** * Fetch a durable endpoint URL and consume its SSE stream, dispatching * lifecycle callbacks (metadata, data, commit, rollback) as * events arrive. Returns the final `Response` reconstructed from the * terminal `inngest.response` SSE event. * * If the server does not respond with `text/event-stream`, the raw * `Response` is returned as-is (non-streaming path). */ async function fetchWithStream(url, opts) { const fetchFn = opts?.fetch ?? globalThis.fetch; const baseHeaders = {}; if (opts?.fetchOpts?.headers) new Headers(opts.fetchOpts.headers).forEach((value, key) => { baseHeaders[key] = value; }); const initialRes = await fetchFn(url, { ...opts?.fetchOpts, headers: { ...baseHeaders, Accept: "text/event-stream" } }); if (!(initialRes.headers.get("content-type") ?? "").includes("text/event-stream")) return initialRes; if (!initialRes.body) throw new Error("No response body"); let resp; const source = iterSseFollowRedirects(initialRes.body, fetchFn, opts?.fetchOpts?.signal ?? void 0); outer: for await (const sseEvent of source) switch (sseEvent.type) { case "inngest.stream": opts?.onData?.({ data: sseEvent.data, hashedStepId: sseEvent.hashedStepId ?? null }); break; case "inngest.commit": opts?.onCommit?.({ hashedStepId: sseEvent.hashedStepId }); break; case "inngest.rollback": opts?.onRollback?.({ hashedStepId: sseEvent.hashedStepId }); break; case "inngest.response": resp = new Response(sseEvent.response.body, { status: sseEvent.response.statusCode, headers: sseEvent.response.headers }); break outer; case "inngest.metadata": opts?.onMetadata?.({ runId: sseEvent.runId }); break; default: break; } if (!resp) throw new Error("No response"); return resp; } /** * Async generator that yields parsed SSE events from an already-fetched * response body, following `inngest.redirect_info` redirects. * * When a redirect event arrives, the redirect URL is fetched eagerly in the * background so the connection is already established by the time the direct * stream closes. This minimizes the window for late-joiner data loss. */ async function* iterSseFollowRedirects(body, fetchFn, signal) { const fetchOpts = { headers: { Accept: "text/event-stream" }, signal }; let redirectUrl; let eagerResponse; try { for await (const raw of require_streaming.iterSse(body)) { const sseEvent = require_streaming.parseSseEvent(raw); if (!sseEvent) continue; if (sseEvent.type === "inngest.redirect_info") { redirectUrl = sseEvent.url; if (sseEvent.url && !eagerResponse) eagerResponse = fetchFn(sseEvent.url, fetchOpts).catch(() => void 0); yield sseEvent; continue; } yield sseEvent; } if (!redirectUrl) return; let redirectRes; if (eagerResponse) { const eager = await eagerResponse; if (eager?.ok && eager.body) redirectRes = eager; else await eager?.body?.cancel(); eagerResponse = void 0; } if (!redirectRes) { if (signal?.aborted) throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError"); const fallback = await fetchFn(redirectUrl, fetchOpts); if (!fallback.ok) throw new Error(`Stream request failed: ${fallback.status} ${fallback.statusText}`); if (!fallback.body) throw new Error("No response body"); redirectRes = fallback; } for await (const raw of require_streaming.iterSse(redirectRes.body)) { const sseEvent = require_streaming.parseSseEvent(raw); if (!sseEvent) continue; yield sseEvent; } } finally { if (eagerResponse) eagerResponse.then((r) => r?.body?.cancel()).catch(() => {}); } } //#endregion exports.fetchWithStream = fetchWithStream; //# sourceMappingURL=client.cjs.map