From 3c70abbf4bf8715b4ac62cc90d50079ff2dc07f0 Mon Sep 17 00:00:00 2001 From: Samuel Jensen <44519206+nichtsam@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:55:44 +0200 Subject: [PATCH] better headers --- app/utils/remix.server.test.ts | 53 ++++++++++++++++++++++++++++++++++ app/utils/remix.server.ts | 40 +++++++++++++++++-------- 2 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 app/utils/remix.server.test.ts diff --git a/app/utils/remix.server.test.ts b/app/utils/remix.server.test.ts new file mode 100644 index 0000000..def3899 --- /dev/null +++ b/app/utils/remix.server.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, test } from "vitest"; +import { getConservativeCacheControl } from "./remix.server"; +import cacheControl from "cache-control-parser"; + +describe("getConservativeCacheControl", () => { + test("it works for basic usecase", () => { + const result = getConservativeCacheControl( + "max-age=3600", + "max-age=1800, s-maxage=600", + "private, max-age=86400", + ); + + expect(result).toEqual( + cacheControl.stringify({ + "max-age": 1800, + "s-maxage": 600, + private: true, + }), + ); + }); + test("retains boolean directive", () => { + const result = cacheControl.parse( + getConservativeCacheControl("private", "no-cache,no-store"), + ); + + expect(result.private).toEqual(true); + expect(result["no-cache"]).toEqual(true); + expect(result["no-store"]).toEqual(true); + }); + test("get shortest number directive", () => { + const result = cacheControl.parse( + getConservativeCacheControl( + "max-age=10, s-maxage=300", + "max-age=300, s-maxage=600", + ), + ); + + expect(result["max-age"]).toEqual(10); + expect(result["s-maxage"]).toEqual(300); + }); + test("lets unset directives remain unset", () => { + const result = cacheControl.parse( + getConservativeCacheControl( + "max-age=3600", + "max-age=1800, s-maxage=600", + "private, max-age=86400", + ), + ); + + expect(result["must-revalidate"]).toBeUndefined(); + expect(result["stale-while-revalidate"]).toBeUndefined(); + }); +}); diff --git a/app/utils/remix.server.ts b/app/utils/remix.server.ts index 9df8d03..78ec420 100644 --- a/app/utils/remix.server.ts +++ b/app/utils/remix.server.ts @@ -62,28 +62,44 @@ export function pipeHeaders({ return headers; } -function getConservativeCacheControl( +export function getConservativeCacheControl( ...cacheControlHeaders: Array ): string { return cacheControl.stringify( cacheControlHeaders .filter(Boolean) .map((header) => cacheControl.parse(header)) - .reduce((final, current) => { - if (current.private) { - final.private = true; - } + .reduce((acc, current) => { + let directive: keyof CacheControl; + for (directive in current) { + const currentValue = current[directive]; + + switch (typeof currentValue) { + case "boolean": { + if (currentValue) { + // @ts-expect-error + acc[directive] = true; + } + break; + } + case "number": { + const accValue = acc[directive] as number; - const finalMaxAge = final["max-age"]; - const currentMaxAge = current["max-age"]; + if (accValue !== undefined && currentValue !== undefined) { + const result = Math.min(accValue, currentValue); + // @ts-expect-error + acc[directive] = result; + } else if (accValue === undefined) { + // @ts-expect-error + acc[directive] = currentValue; + } - if (finalMaxAge !== undefined && currentMaxAge !== undefined) { - final["max-age"] = Math.min(finalMaxAge, currentMaxAge); - } else if (finalMaxAge === undefined) { - final["max-age"] = currentMaxAge; + break; + } + } } - return final; + return acc; }, {}), ); }