alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
81 lines (71 loc) • 3 kB
text/typescript
import { $atom, $inject, Alepha, z } from "alepha";
import { $action, AlephaServer } from "alepha/server";
import { describe, expect, it } from "vitest";
import { $cookie, AlephaServerCookies } from "../index.ts";
/**
* Regression coverage for the cross-request contamination found in
* `StateManager.register()`.
*
* `$cookie({ key: atom.key })` writes the raw cookie into the request's ALS
* layer during `server:onRequest`, BEFORE anything ever reads the atom. So on
* a request that carries the cookie, the atom's very first registration
* happens inside a fork that already holds its key — and the app-level store
* never received the declared default. Every later, cookie-LESS request then
* resolved `undefined` (no fork value, no app value) instead of the default:
* one user's cookied request permanently defined what every cookie-less user
* saw.
*
* The atom is deliberately NOT declared with `$state(...)` here: it must
* register lazily, from inside the request, which is exactly what the
* framework's own `uiAtom` does during SSR.
*/
const themeAtom = $atom({
name: "test.cookie.binding.theme",
schema: z.object({ theme: z.string() }),
default: { theme: "light" },
});
class CookieBoundApp {
protected readonly alepha = $inject(Alepha);
theme = $cookie({
key: themeAtom.key,
name: "test-cookie-binding-theme",
schema: z.object({ theme: z.string() }),
});
read = $action({
schema: {
response: z.object({ theme: z.string() }),
},
// Lazy: the atom is first registered here, inside the request.
handler: () => ({ theme: this.alepha.store.get(themeAtom).theme }),
});
}
const alepha = Alepha.create().with(AlephaServer).with(AlephaServerCookies);
const app = alepha.inject(CookieBoundApp);
const cookieHeader = `test-cookie-binding-theme=${encodeURIComponent(
JSON.stringify({ theme: "dark" }),
)}`;
describe("atom bound to a $cookie, first registered inside a request", () => {
it("serves the default to a cookie-less request that follows a cookied one", async () => {
// Request 1 — WITH the cookie. This is the request that registers the
// atom, from inside a fork that already carries its key.
const first = await app.read.fetch(
{},
{
key: "binding-with-cookie",
request: { headers: { cookie: cookieHeader } },
},
);
expect(first.data.theme).toBe("dark");
// Request 2 — WITHOUT any cookie. Before the fix this resolved
// `undefined` (the app store never got the default), which then blew up
// as a TypeError on any direct `.theme` read.
const second = await app.read.fetch(
{},
{ key: "binding-no-cookie", request: { headers: { cookie: "" } } },
);
expect(second.data.theme).toBe("light");
// The app-level store holds the declared DEFAULT — not `undefined`, and
// not the first request's cookie value.
expect(alepha.store.get(themeAtom, "app")).toEqual({ theme: "light" });
});
});