feat(core): carry the request context in an AsyncLocalStorage

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 02:46:24 +05:30
co-authored by Claude Opus 5
parent 953b1cd692
commit 680ea73975
4 changed files with 151 additions and 61 deletions
@@ -0,0 +1,49 @@
import { expect, test } from "bun:test";
import {
getRequestContext,
requireRequestContext,
runWithRequestContext,
} from "../src/request-context.ts";
const ctx = { marker: "the-request" } as never;
test("the context is visible inside the run", () => {
runWithRequestContext(ctx, () => {
expect(getRequestContext()).toBe(ctx);
});
});
test("the context is visible across an await", async () => {
// The whole point is that it survives async boundaries a caller cannot see.
await runWithRequestContext(ctx, async () => {
await new Promise((resolve) => setTimeout(resolve, 1));
expect(getRequestContext()).toBe(ctx);
});
});
test("there is no context outside a run", () => {
expect(getRequestContext()).toBeUndefined();
});
test("requireRequestContext throws a message naming the caller", () => {
expect(() => requireRequestContext("api.searchUsers")).toThrow(/api\.searchUsers/);
});
test("concurrent runs do not see each other's context", async () => {
const first = { id: 1 } as never;
const second = { id: 2 } as never;
const seen: unknown[] = [];
await Promise.all([
runWithRequestContext(first, async () => {
await new Promise((resolve) => setTimeout(resolve, 5));
seen.push(getRequestContext());
}),
runWithRequestContext(second, async () => {
seen.push(getRequestContext());
}),
]);
expect(seen).toContain(first);
expect(seen).toContain(second);
});