alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
110 lines (109 loc) • 4.18 kB
JavaScript
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