UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

110 lines (109 loc) 4.18 kB
import { $hook, $inject, $module, Alepha } from "alepha"; import { $logger } from "alepha/logger"; //#region ../../src/background/providers/BackgroundTaskProvider.ts /** * Runs fire-and-forget work that should outlive the request that scheduled it * (sending a welcome email, an audit write, a webhook) without blocking the * response. * * On a long-lived runtime (Node, Vercel) the event loop keeps the promise * alive, so the only job here is to detach the task from the caller and never * leak an unhandled rejection. On serverless/edge runtimes that freeze the * isolate once the response is returned (Cloudflare Workers) the * {@link WorkerdBackgroundTaskProvider} variant additionally wraps the task in * `executionCtx.waitUntil` — call sites stay platform-agnostic and only ever * call {@link defer}. * * In-flight tasks are tracked and awaited on shutdown (`flush`), so graceful * stop, `run({ once })`, and unit tests don't silently drop work. */ var BackgroundTaskProvider = class { alepha = $inject(Alepha); log = $logger(); /** In-flight deferred tasks, awaited on stop. */ pending = /* @__PURE__ */ new Set(); /** * Schedule `task` to run in the background. Returns immediately — the task is * detached onto a microtask, so the caller (e.g. an HTTP handler) is never * blocked and a synchronous throw inside `task` can't escape into it. Errors * are caught and logged. */ defer(task) { const promise = Promise.resolve().then(task).then(() => void 0, (error) => { this.log.warn("Background task failed", error); }); this.pending.add(promise); promise.finally(() => { this.pending.delete(promise); }); this.keepAlive(promise); } /** * Hook for platform variants to keep the runtime alive until `promise` * settles. No-op on long-lived runtimes (the event loop already does). The * Cloudflare variant overrides this with `executionCtx.waitUntil`. */ keepAlive(_promise) {} /** * Await all in-flight tasks (best-effort). Called on `stop` so shutdown and * tests don't drop deferred work. */ async flush() { await Promise.allSettled([...this.pending]); } onStop = $hook({ on: "stop", handler: () => this.flush() }); }; //#endregion //#region ../../src/background/providers/WorkerdBackgroundTaskProvider.ts /** * Cloudflare Workers variant of {@link BackgroundTaskProvider}. * * Cloudflare freezes the isolate once the response is returned, so a plain * fire-and-forget promise would be killed mid-flight. The platform exposes a * per-request `executionCtx.waitUntil(promise)` that keeps the isolate alive * until `promise` settles; the request entry point (generated by the Cloudflare * build) stashes it in the Alepha store under `cloudflare.waitUntil`. * * This is the ONE place that reads that key — call sites use * {@link BackgroundTaskProvider.defer} and stay platform-agnostic. * * `waitUntil` is request-scoped, so `defer()` must be called within the * request's async context (which is where background work is scheduled). Outside * a request (e.g. a cron tick) the key is absent and we fall back to the base * fire-and-track behaviour. */ var WorkerdBackgroundTaskProvider = class extends BackgroundTaskProvider { keepAlive(promise) { const waitUntil = this.alepha.store.get("cloudflare.waitUntil"); if (typeof waitUntil !== "function") return; try { waitUntil(promise); } catch (error) { this.log.debug("waitUntil rejected — falling back to fire-and-track", error); } } }; //#endregion //#region ../../src/background/index.workerd.ts /** * Cloudflare Workers build of {@link AlephaBackground}: swaps * {@link BackgroundTaskProvider} for {@link WorkerdBackgroundTaskProvider}, * which keeps the isolate alive past the response via `executionCtx.waitUntil`. * * @module alepha.background */ const AlephaBackground = $module({ name: "alepha.background", services: [BackgroundTaskProvider], variants: [WorkerdBackgroundTaskProvider], register: (alepha) => alepha.with({ provide: BackgroundTaskProvider, use: WorkerdBackgroundTaskProvider }) }); //#endregion export { AlephaBackground, BackgroundTaskProvider, WorkerdBackgroundTaskProvider }; //# sourceMappingURL=index.workerd.js.map