feat: centralize application framework primitives
Quality / quality (ubuntu-latest) (push) Failing after 14m38s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-08-22 23:07:46 +05:30
1 parent 96e082b943
commit a3ddd39b7b
73 files changed
+1429 -84

No files matched your search

+41 -6
View File
@@ -22,7 +22,20 @@ import {
/** Translate a key for the active language, interpolating `{param}` placeholders. */
export type TFunction = (key: string, params?: Record<string, string | number>) => string;
export type Context = {
export interface PaginationOptions {
defaultLimit?: number;
maxLimit?: number;
}
export interface PaginationInput {
limit: number;
cursor?: string;
}
export interface Context<
Params extends Record<string, string> = Record<string, string>,
User = unknown,
> {
/** The raw incoming web-standard Request. */
req: Request;
/** Parsed URL of the request (pathname, query, etc.). */
@@ -32,17 +45,19 @@ export type Context = {
/** Translate a key for the active language (identity until the runtime sets it). */
t: TFunction;
/** Dynamic route params, e.g. `/users/[id]` -> `{ id: "42" }`. */
params: Record<string, string>;
params: Params;
/**
* Per-request scratch space. Middleware can attach values here
* (e.g. the authenticated user) and downstream handlers can read them.
*/
locals: Record<string, unknown>;
/** Resource resolved by a declarative route guard, when present. */
resource?: unknown;
/**
* The authenticated user for this request, or null when anonymous. Populated
* by the `sessionAuth` middleware (or `logIn`); read via `getUser(ctx)`.
*/
user?: unknown;
user?: User | null;
/** Active tenant/workspace resolved by tenant middleware. */
tenant?: Tenant;
/** Request tracer installed by observability middleware. */
@@ -59,7 +74,9 @@ export type Context = {
session: SessionStore;
/** Read-only localStorage snapshot sent by the browser for CSR data bindings. */
localStorage: LocalStorageSnapshot;
};
/** Parse and bound the standard `limit` and `cursor` query parameters. */
pagination(options?: PaginationOptions): PaginationInput;
}
/** Calls the next middleware in the chain (or the final route handler). */
export type Next = () => Promise<Response> | Response;
@@ -99,12 +116,15 @@ export type PageMeta = SeoConfig;
export type PageComponent = (ctx: Context) => string | Promise<string>;
/** Create a fresh context for an incoming request. */
export function createContext(req: Request, url: URL): Context {
export function createContext<Params extends Record<string, string> = Record<string, string>>(
req: Request,
url: URL,
): Context<Params> {
const cookies = createCookieStore(req);
return {
req,
url,
params: {},
params: {} as Params,
locals: {},
lang: "",
t: (key) => key,
@@ -113,9 +133,24 @@ export function createContext(req: Request, url: URL): Context {
// cookies get `Secure` behind a TLS-terminating proxy (matches CSRF cookies).
session: createSessionStore(cookies, req, undefined, url.protocol === "https:"),
localStorage: createLocalStorageSnapshot(req),
pagination(options = {}) {
const defaultLimit = positivePaginationInteger(options.defaultLimit, 25);
const maxLimit = positivePaginationInteger(options.maxLimit, 100);
const requested = Number(url.searchParams.get("limit"));
const limit =
Number.isInteger(requested) && requested > 0
? Math.min(requested, maxLimit)
: Math.min(defaultLimit, maxLimit);
const cursor = url.searchParams.get("cursor")?.trim() || undefined;
return { limit, cursor };
},
};
}
function positivePaginationInteger(value: number | undefined, fallback: number): number {
return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
}
/** Apply headers accumulated on the context, such as Set-Cookie. */
export function withContextHeaders(ctx: Context, res: Response): Response {
const headers = new Headers(res.headers);