feat(core): carry the request context in an AsyncLocalStorage
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
isSafeRequestPath,
|
||||
renderError,
|
||||
renderNotFound,
|
||||
runWithRequestContext,
|
||||
withContextHeaders,
|
||||
withSecurityHeaders,
|
||||
resolveRequestUrl,
|
||||
@@ -1266,71 +1267,73 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
return secure(new Response("Expected a WebSocket upgrade request", { status: 426 }));
|
||||
}
|
||||
|
||||
try {
|
||||
const ctx = createContext(req, url);
|
||||
initializeRequestCache(ctx);
|
||||
ctx.ip = server.requestIP?.(req)?.address ?? undefined;
|
||||
ctx.locals.cspNonce = nonce; // available to pages for their own inline scripts
|
||||
if (!["GET", "HEAD", "OPTIONS"].includes(req.method.toUpperCase())) {
|
||||
const contentType = req.headers.get("content-type") ?? "";
|
||||
if (contentType.includes("form")) {
|
||||
try {
|
||||
const form = await req.clone().formData();
|
||||
const token = form.get("_csrf");
|
||||
if (typeof token === "string") ctx.locals._csrf = token;
|
||||
} catch {
|
||||
// The endpoint will return its normal malformed-input response.
|
||||
const ctx = createContext(req, url);
|
||||
return runWithRequestContext(ctx, async () => {
|
||||
try {
|
||||
initializeRequestCache(ctx);
|
||||
ctx.ip = server.requestIP?.(req)?.address ?? undefined;
|
||||
ctx.locals.cspNonce = nonce; // available to pages for their own inline scripts
|
||||
if (!["GET", "HEAD", "OPTIONS"].includes(req.method.toUpperCase())) {
|
||||
const contentType = req.headers.get("content-type") ?? "";
|
||||
if (contentType.includes("form")) {
|
||||
try {
|
||||
const form = await req.clone().formData();
|
||||
const token = form.get("_csrf");
|
||||
if (typeof token === "string") ctx.locals._csrf = token;
|
||||
} catch {
|
||||
// The endpoint will return its normal malformed-input response.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Resolve the request language so both pages and API can translate.
|
||||
if (deps.i18n) {
|
||||
ctx.lang = resolveLang(
|
||||
deps.i18n,
|
||||
ctx.cookies.get(deps.i18n.cookie.name),
|
||||
req.headers.get("accept-language"),
|
||||
// Resolve the request language so both pages and API can translate.
|
||||
if (deps.i18n) {
|
||||
ctx.lang = resolveLang(
|
||||
deps.i18n,
|
||||
ctx.cookies.get(deps.i18n.cookie.name),
|
||||
req.headers.get("accept-language"),
|
||||
);
|
||||
ctx.t = makeT(deps.i18n, ctx.lang);
|
||||
}
|
||||
const mws = await resolveMiddleware();
|
||||
const res = secure(
|
||||
withContextHeaders(ctx, await runMiddleware(mws, ctx, () => dispatch(ctx))),
|
||||
);
|
||||
ctx.t = makeT(deps.i18n, ctx.lang);
|
||||
return compressResponse(req, res);
|
||||
} catch (err) {
|
||||
const app = process.env.WRNEXUS_APP_NAME ?? "app";
|
||||
let detail =
|
||||
err instanceof Error ? (err.stack ?? `${err.name}: ${err.message}`) : String(err);
|
||||
const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
||||
// AggregateError (e.g. Bun.build() "Bundle failed") hides its real cause in
|
||||
// `.errors` — the top-level message/stack alone is useless for diagnosing a
|
||||
// failed bundle. Print every nested error so the actual failure is visible.
|
||||
const nested = (err as { errors?: unknown[] } | undefined)?.errors;
|
||||
if (Array.isArray(nested) && nested.length) {
|
||||
detail +=
|
||||
"\n caused by:\n" +
|
||||
nested
|
||||
.map((e, i) => ` [${i}] ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
|
||||
.join("\n");
|
||||
}
|
||||
console.error(
|
||||
`[wrnexus] unhandled request error (${app}) ${req.method} ${url.pathname}\n${detail}`,
|
||||
);
|
||||
deps.devToolbar?.collector.add(
|
||||
issueFromError(err, {
|
||||
ruleId: "server/request-error",
|
||||
category: "server",
|
||||
title: "Request processing failed",
|
||||
pathname: url.pathname,
|
||||
}),
|
||||
);
|
||||
const response = secure(renderError(err, mode));
|
||||
// Gateway-managed production apps bind to loopback. Carry a bounded,
|
||||
// encoded diagnostic to the parent gateway so centralized log collectors
|
||||
// can explain child failures; the gateway always strips this header.
|
||||
response.headers.set("x-wrnexus-internal-error", encodeURIComponent(message.slice(0, 500)));
|
||||
return compressResponse(req, response);
|
||||
}
|
||||
const mws = await resolveMiddleware();
|
||||
const res = secure(
|
||||
withContextHeaders(ctx, await runMiddleware(mws, ctx, () => dispatch(ctx))),
|
||||
);
|
||||
return compressResponse(req, res);
|
||||
} catch (err) {
|
||||
const app = process.env.WRNEXUS_APP_NAME ?? "app";
|
||||
let detail =
|
||||
err instanceof Error ? (err.stack ?? `${err.name}: ${err.message}`) : String(err);
|
||||
const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
||||
// AggregateError (e.g. Bun.build() "Bundle failed") hides its real cause in
|
||||
// `.errors` — the top-level message/stack alone is useless for diagnosing a
|
||||
// failed bundle. Print every nested error so the actual failure is visible.
|
||||
const nested = (err as { errors?: unknown[] } | undefined)?.errors;
|
||||
if (Array.isArray(nested) && nested.length) {
|
||||
detail +=
|
||||
"\n caused by:\n" +
|
||||
nested
|
||||
.map((e, i) => ` [${i}] ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
|
||||
.join("\n");
|
||||
}
|
||||
console.error(
|
||||
`[wrnexus] unhandled request error (${app}) ${req.method} ${url.pathname}\n${detail}`,
|
||||
);
|
||||
deps.devToolbar?.collector.add(
|
||||
issueFromError(err, {
|
||||
ruleId: "server/request-error",
|
||||
category: "server",
|
||||
title: "Request processing failed",
|
||||
pathname: url.pathname,
|
||||
}),
|
||||
);
|
||||
const response = secure(renderError(err, mode));
|
||||
// Gateway-managed production apps bind to loopback. Carry a bounded,
|
||||
// encoded diagnostic to the parent gateway so centralized log collectors
|
||||
// can explain child failures; the gateway always strips this header.
|
||||
response.headers.set("x-wrnexus-internal-error", encodeURIComponent(message.slice(0, 500)));
|
||||
return compressResponse(req, response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function dispatch(ctx: Context): Promise<Response> {
|
||||
|
||||
Reference in New Issue
Block a user