diff --git a/.changeset/clever-maps-approve.md b/.changeset/clever-maps-approve.md new file mode 100644 index 0000000..5a9e4dc --- /dev/null +++ b/.changeset/clever-maps-approve.md @@ -0,0 +1,5 @@ +--- +"cdn-cache-control": patch +--- + +Remove dependency on node:process diff --git a/.changeset/hot-planes-rush.md b/.changeset/hot-planes-rush.md new file mode 100644 index 0000000..c039209 --- /dev/null +++ b/.changeset/hot-planes-rush.md @@ -0,0 +1,5 @@ +--- +"cdn-cache-control": minor +--- + +Adds Fastly support diff --git a/.gitignore b/.gitignore index 5f4d7fe..3f3978b 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* -.tsup \ No newline at end of file +.tsup +.DS_Store diff --git a/fastly.test.js b/fastly.test.js new file mode 100644 index 0000000..3057a44 --- /dev/null +++ b/fastly.test.js @@ -0,0 +1,13 @@ +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { CacheHeaders } from "./dist/index.js"; + +describe("Fastly", () => { + it("merges cdn-cache-control header into cache-control", () => { + const headers = new CacheHeaders(undefined, "fastly").immutable(); + assert.strictEqual( + headers.get("Cache-Control"), + "public,s-maxage=31536000,max-age=31536000,immutable", + ); + }); +}); diff --git a/netlify.test.js b/netlify.test.js index eaded32..78d4668 100644 --- a/netlify.test.js +++ b/netlify.test.js @@ -1,5 +1,5 @@ import assert from "node:assert"; -import { before, describe, it } from "node:test"; +import { describe, it } from "node:test"; import { CacheHeaders } from "./dist/index.js"; describe("Netlify", () => { diff --git a/src/index.ts b/src/index.ts index 5638517..f71b347 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,10 @@ -import process from "node:process"; - -// TODO: Add more CDNs /** The CDN that the cache headers are being used with. Will work with other CDNs, but may miss platform-specific headers and directives. */ export type CDN = | "netlify" | "cloudflare" | "akamai" | "vercel" + | "fastly" | (string & {}); /** Number of seconds in one minute */ @@ -28,20 +26,17 @@ const cdnCacheControlHeaderNames = new Map([ ["cloudflare", "Cloudflare-CDN-Cache-Control"], ["akamai", "Akamai-Cache-Control"], ["vercel", "Vercel-CDN-Cache-Control"], + ["fastly", "Cache-Control"], ]); function detectCDN(): CDN | undefined { - if (process.env.CDN) { - return process.env.CDN as CDN; + if (globalThis?.process?.env?.CDN) { + return globalThis.process.env.CDN as CDN; } - if (process.env.VERCEL) { + if (globalThis?.process?.env.VERCEL) { return "vercel"; } - if ( - process.env.NETLIFY || - process.env.NETLIFY_LOCAL || - process.env.NETLIFY_BLOBS_CONTEXT - ) { + if ("Netlify" in globalThis) { return "netlify"; } @@ -98,10 +93,15 @@ export class CacheHeaders extends Headers { if (this.#cdn === "netlify") { cdnDirectives[tieredDirective] = ""; } - this.setCdnCacheControl(cdnDirectives); - directives.public = ""; - delete directives["s-maxage"]; + // If the CDN cache-control header is the same as the browser cache-control header, we merge the directives. + if (this.cdnCacheControlHeaderName === "Cache-Control") { + Object.assign(directives, cdnDirectives); + } else { + this.setCdnCacheControl(cdnDirectives); + delete directives["s-maxage"]; + directives.public = ""; + } if (!directives["max-age"]) { directives["max-age"] = "0"; @@ -208,6 +208,8 @@ export class CacheHeaders extends Headers { switch (this.#cdn) { case "netlify": return "Netlify-Cache-Tag"; + case "fastly": + return "Surrogate-Key"; default: return "Cache-Tag"; }