68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
/**
|
|
* EmDash Request Context
|
|
*
|
|
* Uses AsyncLocalStorage to provide request-scoped state to query functions
|
|
* without requiring explicit parameter passing. The middleware wraps next()
|
|
* in als.run(), making the context available to all code during rendering.
|
|
*
|
|
* For logged-out users with no CMS signals (no edit cookie, no preview param),
|
|
* the middleware skips ALS entirely — zero overhead for normal traffic.
|
|
*
|
|
* The AsyncLocalStorage instance is stored on globalThis with a Symbol key
|
|
* to guarantee a singleton even when bundlers duplicate this module across
|
|
* code-split chunks. Without this, Rollup/Vite may inline the module into
|
|
* multiple chunks (e.g. middleware and page components), each with its own
|
|
* ALS instance — breaking request-scoped state propagation.
|
|
*/
|
|
|
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
|
export interface EmDashRequestContext {
|
|
/** Whether the current request is in visual editing mode */
|
|
editMode: boolean;
|
|
/** Preview token info, if this is a preview request */
|
|
preview?: {
|
|
collection: string;
|
|
id: string;
|
|
};
|
|
/** Current locale from Astro's i18n routing (when configured) */
|
|
locale?: string;
|
|
/**
|
|
* Per-request database override.
|
|
*
|
|
* Set by middleware when D1 read replica sessions are enabled.
|
|
* The runtime's `db` getter checks this first, falling back to
|
|
* the singleton instance. Also used by the DO preview pattern.
|
|
*/
|
|
db?: unknown;
|
|
}
|
|
|
|
const ALS_KEY = Symbol.for("emdash:request-context");
|
|
|
|
const storage: AsyncLocalStorage<EmDashRequestContext> =
|
|
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- globalThis singleton pattern
|
|
((globalThis as Record<symbol, unknown>)[ALS_KEY] as
|
|
| AsyncLocalStorage<EmDashRequestContext>
|
|
| undefined) ??
|
|
(() => {
|
|
const als = new AsyncLocalStorage<EmDashRequestContext>();
|
|
(globalThis as Record<symbol, unknown>)[ALS_KEY] = als;
|
|
return als;
|
|
})();
|
|
|
|
/**
|
|
* Run a function within an EmDash request context.
|
|
* Called by middleware to wrap next().
|
|
*/
|
|
export function runWithContext<T>(ctx: EmDashRequestContext, fn: () => T): T {
|
|
return storage.run(ctx, fn);
|
|
}
|
|
|
|
/**
|
|
* Get the current request context.
|
|
* Returns undefined if no context is set (logged-out fast path).
|
|
*/
|
|
export function getRequestContext(): EmDashRequestContext | undefined {
|
|
return storage.getStore();
|
|
}
|