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
This commit is contained in:
62
packages/core/src/after.ts
Normal file
62
packages/core/src/after.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user