mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
763736eae0
* refactor(app-rsc-entry): delegate pure helpers to typed server modules The generated RSC entry template contained five inline implementations of pure runtime helpers: post-middleware request context construction, RSC error handler creation, request context cleanup, RSC stream preload hint normalization, and hook warning suppression. These inline blocks repeated runtime behavior across multiple call sites and could not be unit-tested independently. Move each helper to a dedicated typed module under server/ so the generated entry imports and wires them instead of owning the logic: - buildPostMwRequestContext → server/app-post-middleware-context.ts - createRscOnErrorHandler → server/app-rsc-error-handler.ts - __clearRequestContext + setNavigationContext → server/app-request-context.ts - renderToReadableStream preload hints → server/rsc-stream-hints.ts (createRscRenderer) - hook warning console.error patch → server/app-hook-warning-suppression.ts Each helper module has focused unit tests. The entry template is 55 lines shorter and now describes the app shape; the typed modules own behaviour. * fix: remove redundant as cast and fix test module caching app-hook-warning-suppression.ts contained an `as string` cast that was redundant because the typeof check on the prior line already narrows the type. The hook warning suppression tests in tests 2-4 were passing vacuously because dynamic import() returned the cached module whose console.error patch was no longer active after test 1 restored the original. Use vi.resetModules() before each test to force fresh module evaluation. clearAppRequestContext now delegates to setAppNavigationContext(null) instead of calling the raw navigation and root-params setters directly, matching the DRY pattern from the original inline code. * fix: resolve entry helper rebase conflicts * ci: rerun checks (font-google flake) * Update packages/vinext/src/entries/app-rsc-entry.ts Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com> --------- Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
import { describe, expect, it, afterEach } from "vite-plus/test";
|
|
import { buildPostMwRequestContext } from "../packages/vinext/src/server/app-post-middleware-context.js";
|
|
import { setHeadersContext } from "../packages/vinext/src/shims/headers.js";
|
|
|
|
function makeRequest(
|
|
url = "https://example.com/test?q=1",
|
|
headers?: Record<string, string>,
|
|
): Request {
|
|
return new Request(url, { headers: new Headers(headers) });
|
|
}
|
|
|
|
describe("buildPostMwRequestContext", () => {
|
|
afterEach(() => {
|
|
setHeadersContext(null);
|
|
});
|
|
|
|
it("returns the same shape as requestContextFromRequest when middleware did not run", () => {
|
|
setHeadersContext(null);
|
|
const req = makeRequest("https://example.com/path?x=1", {
|
|
host: "example.com",
|
|
cookie: "session=abc",
|
|
});
|
|
const ctx = buildPostMwRequestContext(req);
|
|
|
|
// Contract: result must satisfy the RequestContext shape consumed by
|
|
// config-matchers — headers, cookies, query, host — all present.
|
|
expect(ctx.headers).toBeInstanceOf(Headers);
|
|
expect(ctx.headers.get("host")).toBe("example.com");
|
|
expect(typeof ctx.cookies).toBe("object");
|
|
expect(ctx.cookies).not.toBeInstanceOf(Map);
|
|
expect(ctx.cookies).toEqual({ session: "abc" });
|
|
expect(ctx.query.get("x")).toBe("1");
|
|
expect(ctx.host).toBe("example.com");
|
|
});
|
|
|
|
it("reads from the live ALS HeadersContext when middleware set request headers", () => {
|
|
const mwHeaders = new Headers();
|
|
mwHeaders.set("x-middleware-request-geo", "DE");
|
|
mwHeaders.set("host", "mw.example.com");
|
|
const mwCookies = new Map([
|
|
["token", "xyz"],
|
|
["lang", "en"],
|
|
]);
|
|
|
|
setHeadersContext({ headers: mwHeaders, cookies: mwCookies });
|
|
|
|
const req = makeRequest("https://original.example.com/path");
|
|
const ctx = buildPostMwRequestContext(req);
|
|
|
|
// Middleware headers take precedence over the physical request headers.
|
|
expect(ctx.headers.get("x-middleware-request-geo")).toBe("DE");
|
|
expect(ctx.host).toBe("mw.example.com");
|
|
// Cookies come from the middleware context, not the request Cookie header.
|
|
expect(ctx.cookies).toEqual({ token: "xyz", lang: "en" });
|
|
});
|
|
|
|
it("preserves query parameters from the original request URL", () => {
|
|
setHeadersContext({
|
|
headers: new Headers({ host: "x.com" }),
|
|
cookies: new Map(),
|
|
});
|
|
const req = makeRequest("https://original.example.com/blog?page=2&sort=desc");
|
|
const ctx = buildPostMwRequestContext(req);
|
|
|
|
expect(ctx.query.get("page")).toBe("2");
|
|
expect(ctx.query.get("sort")).toBe("desc");
|
|
});
|
|
|
|
it("handles an empty middleware cookie map without error", () => {
|
|
setHeadersContext({
|
|
headers: new Headers({ host: "x.com" }),
|
|
cookies: new Map(),
|
|
});
|
|
const req = makeRequest();
|
|
const ctx = buildPostMwRequestContext(req);
|
|
|
|
expect(ctx.cookies).toEqual({});
|
|
});
|
|
|
|
it("normalizes the host from middleware headers, falling back to the request URL hostname", () => {
|
|
// No Host header in middleware → normalizeHost falls back to url.hostname
|
|
setHeadersContext({
|
|
headers: new Headers(),
|
|
cookies: new Map(),
|
|
});
|
|
const req = makeRequest("https://host-from-url.example.com/page");
|
|
const ctx = buildPostMwRequestContext(req);
|
|
|
|
expect(ctx.host).toBe("host-from-url.example.com");
|
|
});
|
|
});
|