Files
emdash-patch-imageupload/packages/core/src/after.ts
kunthawat 2d1be52177 Emdash source with visual editor image upload fix
Fixes:
1. media.ts: wrap placeholder generation in try-catch
2. toolbar.ts: check r.ok, display error message in popover
2026-05-03 10:44:54 +07:00

63 lines
2.4 KiB
TypeScript

/**
* Defer work past the HTTP response.
*
* Use for bookkeeping that doesn't need to complete before the client
* gets bytes — writes that record state, maintenance queries, cache
* refreshes. `after()` hands the promise to the host's lifetime
* extender when one is available (Cloudflare's `waitUntil` under
* workerd), or fires-and-forgets on Node (the process lives for the
* next request anyway).
*
* Host binding is resolved lazily via a dynamic import of the
* `virtual:emdash/wait-until` virtual module. Lazy — rather than a
* static top-level import — so tools that walk the dist in a plain
* Node loader (`astro check`, Vitest, etc.) don't trip over the
* `virtual:` scheme: they'd only fail if they actually called
* `after()`, which they don't during type-checking.
*/
export type WaitUntilFn = (promise: Promise<unknown>) => void;
// Resolves to the host's waitUntil if the adapter provided one, or
// null otherwise. Kicked off once at module load; subsequent `after()`
// calls see the cached result without re-importing.
const waitUntilReady: Promise<WaitUntilFn | null> = (async () => {
try {
// @ts-ignore - virtual module, generated by the Astro integration
const mod = (await import("virtual:emdash/wait-until")) as {
waitUntil?: WaitUntilFn;
};
return mod.waitUntil ?? null;
} catch {
// No virtual module available (Node-side tooling, tests without the
// integration in scope). Fire-and-forget is the safe fallback.
return null;
}
})();
// Surface rejections without making the module-load fail.
waitUntilReady.catch(() => {});
/**
* Schedule `fn` to run without blocking the response.
*
* Errors are caught and logged — a deferred task should never surface
* as an unhandled rejection because the response is long gone. Callers
* that care about errors should handle them inside `fn`.
*/
export function after(fn: () => void | Promise<void>): void {
const promise = Promise.resolve()
.then(fn)
.catch((error) => {
console.error("[emdash] deferred task failed:", error);
});
// Defer the lifetime-extender handoff to the microtask that resolves
// waitUntilReady. On workerd this is effectively instant (the virtual
// module is already loaded in the bundle); on Node the promise
// resolves to null, so this is just one extra microtask and no-op.
void waitUntilReady.then((waitUntil) => {
if (waitUntil) waitUntil(promise);
return null;
});
}