Files
WRNexusJS/packages/core/src/execution-context.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

140 lines
4.2 KiB
TypeScript

import type { Context } from "./context.ts";
import type { Tenant } from "./tenant.ts";
import type { Tracer } from "./observability.ts";
export type ExecutionKind =
"http" | "api" | "action" | "loader" | "middleware" | "realtime" | "queue" | "cron" | "webhook";
export interface ResponseContext {
status: number;
headers: Headers;
setStatus(status: number): void;
}
export interface ExecutionContext {
kind: ExecutionKind;
id: string;
request: Request;
response: ResponseContext;
user: unknown | null;
session: unknown | null;
tenant: Tenant | null;
locale: string;
timezone: string;
db?: unknown;
cache?: unknown;
logger?: unknown;
trace?: Tracer;
signal: AbortSignal;
deadline: Date | null;
metadata: Record<string, unknown>;
authorize(permission: string): void | Promise<void>;
}
export interface ExecutionContextInput extends Partial<
Omit<
ExecutionContext,
"kind" | "id" | "request" | "response" | "signal" | "deadline" | "metadata" | "authorize"
>
> {
kind: ExecutionKind;
id?: string;
request?: Request;
response?: Partial<Pick<ResponseContext, "status">> & { headers?: HeadersInit };
signal?: AbortSignal;
deadline?: Date | number | null;
timeoutMs?: number;
metadata?: Record<string, unknown>;
authorize?: (permission: string) => void | Promise<void>;
}
export function createExecutionContext(input: ExecutionContextInput): ExecutionContext {
const controller = new AbortController();
const source = input.signal;
if (source?.aborted) controller.abort(source.reason);
else source?.addEventListener("abort", () => controller.abort(source.reason), { once: true });
const deadline =
input.deadline instanceof Date
? input.deadline
: typeof input.deadline === "number"
? new Date(input.deadline)
: input.timeoutMs !== undefined
? new Date(Date.now() + input.timeoutMs)
: null;
let timer: ReturnType<typeof setTimeout> | undefined;
if (deadline) {
const delay = deadline.getTime() - Date.now();
if (delay <= 0)
controller.abort(new DOMException("Execution deadline exceeded", "TimeoutError"));
else {
timer = setTimeout(
() => controller.abort(new DOMException("Execution deadline exceeded", "TimeoutError")),
delay,
);
timer.unref?.();
}
}
controller.signal.addEventListener(
"abort",
() => {
if (timer) clearTimeout(timer);
},
{ once: true },
);
const response: ResponseContext = {
status: input.response?.status ?? 200,
headers: new Headers(input.response?.headers),
setStatus(status) {
if (!Number.isInteger(status) || status < 100 || status > 599)
throw new RangeError("response status must be an HTTP status code");
this.status = status;
},
};
return {
kind: input.kind,
id: input.id ?? crypto.randomUUID(),
request: input.request ?? new Request(`https://execution.wrnexus.invalid/${input.kind}`),
response,
user: input.user ?? null,
session: input.session ?? null,
tenant: input.tenant ?? null,
locale: input.locale ?? "en",
timezone: input.timezone ?? "UTC",
db: input.db,
cache: input.cache,
logger: input.logger,
trace: input.trace,
signal: controller.signal,
deadline,
metadata: { ...(input.metadata ?? {}) },
authorize:
input.authorize ??
(() => {
throw new Error("WRN-AUTHORIZATION-NOT-CONFIGURED");
}),
};
}
export function executionContextFromHttp(
context: Context,
kind: Extract<
ExecutionKind,
"http" | "api" | "action" | "loader" | "middleware" | "webhook"
> = "http",
input: Omit<
ExecutionContextInput,
"kind" | "request" | "user" | "tenant" | "locale" | "trace"
> = {},
): ExecutionContext {
return createExecutionContext({
...input,
kind,
request: context.req,
user: context.user ?? null,
session: context.session,
tenant: context.tenant ?? null,
locale: context.lang || "en",
trace: context.tracer,
signal: input.signal ?? context.req.signal,
db: input.db ?? context.locals.db,
cache: input.cache ?? context.locals.cache,
logger: input.logger ?? context.locals.logger,
metadata: { ...context.locals, ...(input.metadata ?? {}) },
});
}