Swadhin Biswas Feature/ai code reviewer (#79) · 8/16/2026
cfd400d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* Per-request context (correlation IDs).
*
* Every request runs inside an AsyncLocalStorage scope carrying a
* requestId (+ userId when authenticated). The logger binds these onto
* every log line, so a single request's logs can be traced end-to-end
* through DB, git, and CI operations.
*
* `node:async_hooks` is initialized ONCE at server boot (middleware calls
* ensureRequestStorage); after that all reads are synchronous. The dynamic
* import keeps the Vite browser build from choking on the node builtin.
*/
import type { AsyncLocalStorage as AsyncLocalStorageType } from "node:async_hooks";
export interface RequestContext {
requestId: string;
userId?: string;
route?: string;
}
let storageImpl: AsyncLocalStorageType<RequestContext> | null = null;
let storagePromise: Promise<void> | null = null;
/** Initialize the ALS storage once (server boot). Safe to call repeatedly. */
export function ensureRequestStorage(): Promise<void> {
if (storageImpl) return Promise.resolve();
if (!storagePromise) {
storagePromise = import("node:async_hooks")
.then((mod) => {
storageImpl = new mod.AsyncLocalStorage<RequestContext>();
})
.catch((err) => {
storagePromise = null;
// In browser bundles this import never resolves — fail soft
console.error("[request-context] async_hooks init failed", err);
});
}
return storagePromise;
}
/** Sync accessor — valid only after ensureRequestStorage resolved. */
export function getRequestContext(): RequestContext | undefined {
return storageImpl?.getStore();
}
export function withRequestContext<T>(
ctx: RequestContext,
fn: () => T,
): T {
if (!storageImpl) return fn(); // not initialized (browser/test) — passthrough
return storageImpl.run(ctx, fn);
}
export function newRequestId(): string {
return `req_${crypto.randomUUID().slice(0, 18)}`;
}