mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-27 15:16:53 +09:00
**Summary** Migrates Builder bootstrap data loading from the legacy `/rest/data/:projectId` endpoint to `build.loadData` tRPC, while keeping the old REST route as a no-store compatibility shim. Hardens authenticated Builder/dashboard responses by explicitly sending: - `Cache-Control: private, no-store, max-age=0` - `Pragma: no-cache` - `Expires: 0` - `Vary: Cookie` **Details** - Added `build.loadData` tRPC query for full Builder state. - Switched Builder client bootstrap loading to tRPC. - Added shared cache-control helpers for private no-store responses. - Made tRPC responses private/no-store by default unless anonymous and explicitly public-cacheable. - Added no-store headers to Builder/dashboard/auth/resource/asset-related responses. - Updated redirect handling to preserve headers while enforcing no-store. - Prevented stale dashboard account data from flashing by applying user-scoped loader data before paint and clearing stores on teardown. - Added tests for cache headers, tRPC response metadata, and no-store redirects. **Validation** - `pnpm --filter @webstudio-is/builder test -- app/services/cache-control.server.test.ts app/services/trpc-response-meta.server.test.ts app/services/no-store-redirect.test.ts app/routes/rest.assets.test.ts app/shared/sync/project-queue.test.ts app/dashboard/dashboard.test.ts` - `pnpm exec oxlint ...` on touched files - `git diff --check` `pnpm --filter @webstudio-is/builder typecheck` still fails on existing unrelated Tailwind/css-engine errors.
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
import {
|
|
appendVaryHeader,
|
|
createPrivateNoStoreHeaders,
|
|
privateNoStoreResponseHeaders,
|
|
} from "./cache-control.server";
|
|
|
|
describe("private no-store cache headers", () => {
|
|
test("marks user-scoped responses as non-cacheable and cookie-varying", () => {
|
|
expect(privateNoStoreResponseHeaders).toMatchObject({
|
|
"Cache-Control": "private, no-store, max-age=0",
|
|
Pragma: "no-cache",
|
|
Expires: "0",
|
|
Vary: "Cookie",
|
|
});
|
|
});
|
|
|
|
test("preserves unrelated headers while enforcing cache policy", () => {
|
|
const headers = createPrivateNoStoreHeaders({
|
|
"Cache-Control": "public, max-age=600",
|
|
"Set-Cookie": "session=value",
|
|
});
|
|
|
|
expect(headers.get("Cache-Control")).toBe(
|
|
privateNoStoreResponseHeaders["Cache-Control"]
|
|
);
|
|
expect(headers.get("Vary")).toBe(privateNoStoreResponseHeaders.Vary);
|
|
expect(headers.get("Set-Cookie")).toBe("session=value");
|
|
});
|
|
|
|
test("preserves existing vary values while adding cookie", () => {
|
|
const headers = createPrivateNoStoreHeaders({
|
|
Vary: "Origin",
|
|
});
|
|
|
|
expect(headers.get("Vary")).toBe("Origin, Cookie");
|
|
});
|
|
|
|
test("does not duplicate vary values", () => {
|
|
const headers = new Headers({
|
|
Vary: "Origin, Cookie",
|
|
});
|
|
|
|
appendVaryHeader(headers, "Origin");
|
|
appendVaryHeader(headers, "Cookie");
|
|
|
|
expect(headers.get("Vary")).toBe("Origin, Cookie");
|
|
});
|
|
});
|