Skip to content

Releases: unjs/ocache

v0.2.0

Choose a tag to compare

@pi0 pi0 released this 09 Jul 12:35

compare changes

This release tightens security defaults (cookies and SWR), fixes cache key collision issues, and adds several new customizations (getMaxAge, shouldCache, sendCacheControl) and adds binary bodies support.

✨ New features

👀 Noticable Changes

  • 🔒 Cookies are now ommited by default. Cookies are now stripped in both directions unless explicitly allowlisted (docs)
  • ⏱️ SWR (stale-while-revalidate) is now opt-in, not default. Expired entries block and re-resolve in the foreground by default; add swr: true to keep the old "serve stale, refresh in background" behavior.

⚠️ Breaking changes

Cookies excluded from caching by default

The Cookie request header is now stripped before your handler runs, and any Set-Cookie on the response is stripped before caching — unless you allowlist the cookie name. Read docs to learn more.

swr now defaults to false

Previously, an expired cache entry was served stale by default while refreshing in the background. Now it blocks and re-resolves in the foreground by default to be more predictable. If you want the old stale-while-revalidate behavior, add swr: true explicitly (pair with staleMaxAge). If you were asserting on the synthesized Cache-Control header in tests/CDN configs, note it now emits max-age=N instead of s-maxage=N, stale-while-revalidate unless swr: true is set.

Bypassed (non-GET/HEAD) responses now pass through untouched

Internal refactor so streaming/binary bodies survive on bypassed requests, and bypassed responses no longer get synthesized cache headers or spurious 304 handling. If you relied on non-GET/HEAD responses getting cache headers or 304 handling, that no longer happens — set headers in your handler if needed for those routes.

🚀 New features

Feature What it's for
getMaxAge hook (#39) Derive TTL from the resolved value itself (e.g. an OAuth token's expires_in). Runs once after the resolver; ≤0 disables caching for that entry.
serialize hook (#59) Write-side counterpart to transform — serialize a raw stream/class instance before storage. Runs exactly once, shared across deduplicated concurrent calls.
shouldCache (#48, #55) Extra caller-defined rule for what's cacheable (e.g. exclude redirects). ANDs with built-in checks — can only narrow, not override them.
sendCacheControl: false (#49, #67) Cache server-side (storage + SWR still work) without advertising Cache-Control to clients/CDNs. Solves the "cache internally but always revalidate at the edge" case.
Binary response bodies (#66) Images/protobuf/tiles now cache correctly — byte validity (not content-type) decides text vs. base64 storage.
allowQuery (#14, #29) Allowlist which query params vary the cache key/reach the handler, so tracking params don't fragment your cache.
Vary header emission (#56) Automatically emits Vary for your varies config so downstream CDNs store per-variant copies correctly.
x-cache status header (#40) New cacheStatusHeader option (default on) reports hit/stale/revalidated/miss per response.
Async validate (#44) validate can now return a Promise<boolean> — check against an external source before trusting a cached entry.
Bounded memory storage (#38) Built-in memory storage now caps at 10,000 entries with LRU eviction by default (maxSize: Infinity to opt out).
.expire()/.invalidate()/.resolveKeys() on handlers (#72) No more manually reconstructing cache keys to purge a defineCachedHandler route — call these directly with the event.

🩹 Notable fixes

  • Honors explicit Cache-Control: no-store/private (#42) — these are no longer cached (and no longer silently overwritten by ocache's own synthesized header). Fixes a potential cross-user leak for private responses.
  • Custom getKey collisions fixed (#60) — distinct custom keys that escaped to the same string (e.g. "user:1" vs "user1:") no longer collide. One-time cold cache for anyone who hit this.
  • shouldBypassCache actually works now (#50, #62) — it was previously silently ignored due to an option-overwrite bug. If you built a workaround inside your handler, you can remove it.
  • Transport headers stripped from cached responses (#74) — content-encoding/content-length/transfer-encoding no longer desync from the re-buffered body (was causing malformed cache-hit responses, nitro#2109).
  • Non-GET/HEAD bypass narrowing fixed (leftovers from #58) — bypassed requests with a body (e.g. POST) were briefly having their body dropped during the cookie/header-filtering rework; now fully untouched.

Upgrade checklist

  1. Audit handlers that read or set cookies → add allowCookies where needed; double-check nothing sensitive gets allowlisted.
  2. Decide on SWR per cached function/handler → add swr: true anywhere you want stale-serving back.
  3. Add explicit name to any defineCachedFunction/defineCachedHandler calls that share source code but close over different variables.
  4. Update custom validate hooks to the (entry, { args }) signature if you use call args.
  5. Check custom getMaxAge hooks on HTTP handlers — entry.value is now a live Response; don't consume its body.
  6. Expect a one-time cold cache for: unnamed functions (bug fix #63), colliding custom keys (#60).

❤️ Contributors

v0.1.5

Choose a tag to compare

@pi0 pi0 released this 10 Jun 17:34
b27eb98

compare changes

🚀 Enhancements

  • cache: Add expireCache and .expire() for SWR-friendly invalidation (#23)

🩹 Fixes

  • cache: Handle eviction promise rejections and use waitUntil (#16)

🏡 Chore

❤️ Contributors

v0.1.4

Choose a tag to compare

@pi0 pi0 released this 19 Mar 14:23

compare changes

🔥 Performance

  • cache: Skip writing to lower tiers on multi-tier cache hit (4fe0de7)

v0.1.3

Choose a tag to compare

@pi0 pi0 released this 19 Mar 13:57

compare changes

🚀 Enhancements

  • Respect staleMaxAge (8915db3)
  • cache: Add resolveCacheKey and .resolveKey() (#6)
  • storage: Nullish set deletes entry instead of storing dead weight (71d5f11)
  • Multi-tier cache base (#7)
  • cache: Add invalidateCache() and .invalidate() (#8)

🩹 Fixes

  • Respect zero ttl (#5)
  • http: Merge default options when partial opts are provided (#2)
  • cache: Catch sync storage errors in get/set (ee1bb02)
  • cache: Evict stale entry on SWR revalidation failure (#9)
  • storage: Proactively flush expired memory entries (#10)

❤️ Contributors