release: WRNexusJS 0.8.0
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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 ?? {}) },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user