[PR #961] [MERGED] fix(isr): honor route expire ceilings #985

Closed
opened 2026-05-06 13:11:23 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/961
Author: @NathanDrake2406
Created: 4/29/2026
Status: Merged
Merged: 5/2/2026
Merged by: @james-elicx

Base: mainHead: nathan/fix-isr-expire


📝 Commits (10+)

  • 04f69de fix(isr): honor route expire ceilings
  • d0019fe fix(isr): address cache life review follow-ups
  • 67b1a01 chore: address latest review follow-ups
  • 7df506a ci: pin vite-plus setup version
  • aba9632 Revert "ci: pin vite-plus setup version"
  • c9a90eb fix: avoid blocking ISR page streams on cache metadata
  • ea48bb9 fix: preserve headers for speculative cacheLife probes
  • f04bef9 fix: preserve prerender cacheLife metadata
  • 1ab1802 fix: preserve legacy ISR cache metadata behavior
  • 095d6dc fix: preserve prerender seed revalidate context

📊 Changes

41 files changed (+1525 additions, -159 deletions)

View changed files

📝 packages/vinext/src/build/prerender.ts (+114 -11)
📝 packages/vinext/src/cloudflare/kv-cache-handler.ts (+42 -10)
📝 packages/vinext/src/config/next-config.ts (+7 -0)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+7 -0)
📝 packages/vinext/src/entries/pages-server-entry.ts (+5 -2)
📝 packages/vinext/src/index.ts (+1 -0)
📝 packages/vinext/src/server/app-page-cache.ts (+103 -19)
📝 packages/vinext/src/server/app-page-dispatch.ts (+28 -6)
📝 packages/vinext/src/server/app-page-render.ts (+108 -16)
📝 packages/vinext/src/server/app-page-response.ts (+8 -9)
📝 packages/vinext/src/server/app-route-handler-cache.ts (+12 -1)
📝 packages/vinext/src/server/app-route-handler-dispatch.ts (+3 -0)
📝 packages/vinext/src/server/app-route-handler-execution.ts (+10 -2)
📝 packages/vinext/src/server/app-route-handler-response.ts (+17 -12)
packages/vinext/src/server/cache-control.ts (+57 -0)
📝 packages/vinext/src/server/isr-cache.ts (+10 -0)
📝 packages/vinext/src/server/pages-page-data.ts (+30 -7)
📝 packages/vinext/src/server/pages-page-response.ts (+7 -1)
📝 packages/vinext/src/server/seed-cache.ts (+27 -6)
📝 packages/vinext/src/shims/cache-runtime.ts (+29 -8)

...and 21 more files

📄 Description

Summary

Closes https://github.com/cloudflare/vinext/issues/957.

Next.js now treats a route-level expire ceiling as the boundary between stale-while-revalidate and blocking regeneration. Vinext was only tracking revalidate, so every time-expired ISR entry stayed eligible for stale serving forever. This PR adds the missing expire dimension and wires it through App Router pages, App Route handlers, Pages Router routes, prerender seeding, and cached function writes.

Next.js references

What changed

  • Memory and KV cache entries now store both revalidateAt and expireAt.
  • ISR reads return a hard miss once an entry is beyond expire, while preserving SWR for entries that are stale but still within expire.
  • next.config expireTime is resolved with the Next.js one-year default and passed through generated App and Pages entry wiring.
  • App pages, App route handlers, Pages routes, prerender manifests, seeded prerenders, and cacheLife-backed cached functions now pass expire alongside revalidate.
  • Cache-Control response helpers now emit finite stale-while-revalidate windows when expire is known, matching Next.js getCacheControlHeader.
  • The App Router generated entry only describes app shape and delegates ISR mechanics to server/isr-cache.ts.

Validation

  • vp check
  • vp test run tests/cache-control.test.ts tests/isr-cache.test.ts tests/kv-cache-handler.test.ts tests/app-page-cache.test.ts tests/app-page-response.test.ts tests/app-page-render.test.ts tests/app-route-handler-response.test.ts tests/app-route-handler-cache.test.ts tests/app-route-handler-execution.test.ts tests/pages-page-data.test.ts tests/pages-page-response.test.ts tests/next-config.test.ts tests/prerender.test.ts tests/deploy.test.ts tests/entry-templates.test.ts

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/961 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/29/2026 **Status:** ✅ Merged **Merged:** 5/2/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/fix-isr-expire` --- ### 📝 Commits (10+) - [`04f69de`](https://github.com/cloudflare/vinext/commit/04f69decaba274fac5b6f05df8b219ec584653ac) fix(isr): honor route expire ceilings - [`d0019fe`](https://github.com/cloudflare/vinext/commit/d0019fe70c408b4198e499045a7b23b39eb17bac) fix(isr): address cache life review follow-ups - [`67b1a01`](https://github.com/cloudflare/vinext/commit/67b1a015694f73397240b2110c6cb611aa4664dd) chore: address latest review follow-ups - [`7df506a`](https://github.com/cloudflare/vinext/commit/7df506a76e7a28d4033d83e457013d6c76c52ab8) ci: pin vite-plus setup version - [`aba9632`](https://github.com/cloudflare/vinext/commit/aba96320a7abe63e2caea40c7bd9319b2cc93b52) Revert "ci: pin vite-plus setup version" - [`c9a90eb`](https://github.com/cloudflare/vinext/commit/c9a90eb27625c2a5a323239a3ba87a64e36b1322) fix: avoid blocking ISR page streams on cache metadata - [`ea48bb9`](https://github.com/cloudflare/vinext/commit/ea48bb96de478b59fbf8c6b29eb9584bded5e603) fix: preserve headers for speculative cacheLife probes - [`f04bef9`](https://github.com/cloudflare/vinext/commit/f04bef937931d4c585dbbcb40e32838650e0fed2) fix: preserve prerender cacheLife metadata - [`1ab1802`](https://github.com/cloudflare/vinext/commit/1ab18027ff655820055eedad0764316a09dbd4a2) fix: preserve legacy ISR cache metadata behavior - [`095d6dc`](https://github.com/cloudflare/vinext/commit/095d6dcf4aeff6adaee755d59c1efe7077e2f509) fix: preserve prerender seed revalidate context ### 📊 Changes **41 files changed** (+1525 additions, -159 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/build/prerender.ts` (+114 -11) 📝 `packages/vinext/src/cloudflare/kv-cache-handler.ts` (+42 -10) 📝 `packages/vinext/src/config/next-config.ts` (+7 -0) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+7 -0) 📝 `packages/vinext/src/entries/pages-server-entry.ts` (+5 -2) 📝 `packages/vinext/src/index.ts` (+1 -0) 📝 `packages/vinext/src/server/app-page-cache.ts` (+103 -19) 📝 `packages/vinext/src/server/app-page-dispatch.ts` (+28 -6) 📝 `packages/vinext/src/server/app-page-render.ts` (+108 -16) 📝 `packages/vinext/src/server/app-page-response.ts` (+8 -9) 📝 `packages/vinext/src/server/app-route-handler-cache.ts` (+12 -1) 📝 `packages/vinext/src/server/app-route-handler-dispatch.ts` (+3 -0) 📝 `packages/vinext/src/server/app-route-handler-execution.ts` (+10 -2) 📝 `packages/vinext/src/server/app-route-handler-response.ts` (+17 -12) ➕ `packages/vinext/src/server/cache-control.ts` (+57 -0) 📝 `packages/vinext/src/server/isr-cache.ts` (+10 -0) 📝 `packages/vinext/src/server/pages-page-data.ts` (+30 -7) 📝 `packages/vinext/src/server/pages-page-response.ts` (+7 -1) 📝 `packages/vinext/src/server/seed-cache.ts` (+27 -6) 📝 `packages/vinext/src/shims/cache-runtime.ts` (+29 -8) _...and 21 more files_ </details> ### 📄 Description ## Summary Closes https://github.com/cloudflare/vinext/issues/957. Next.js now treats a route-level expire ceiling as the boundary between stale-while-revalidate and blocking regeneration. Vinext was only tracking revalidate, so every time-expired ISR entry stayed eligible for stale serving forever. This PR adds the missing expire dimension and wires it through App Router pages, App Route handlers, Pages Router routes, prerender seeding, and cached function writes. ## Next.js references - Upstream fix: https://github.com/vercel/next.js/commit/8e4cfc50627cd3e321ef2db4f798e46698a2fbb8 - Upstream PR: https://github.com/vercel/next.js/pull/93211 - Next.js cache header semantics: https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/lib/cache-control.ts#L17-L31 - App page fallback to nextConfig.expireTime: https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/build/templates/app-page.ts#L1598-L1609 - App route cacheControl carries revalidate and expire: https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/build/templates/app-route.ts#L381-L402 - Pages handler fallback to nextConfig.expireTime: https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/route-modules/pages/pages-handler.ts#L606-L613 ## What changed - Memory and KV cache entries now store both revalidateAt and expireAt. - ISR reads return a hard miss once an entry is beyond expire, while preserving SWR for entries that are stale but still within expire. - next.config expireTime is resolved with the Next.js one-year default and passed through generated App and Pages entry wiring. - App pages, App route handlers, Pages routes, prerender manifests, seeded prerenders, and cacheLife-backed cached functions now pass expire alongside revalidate. - Cache-Control response helpers now emit finite stale-while-revalidate windows when expire is known, matching Next.js getCacheControlHeader. - The App Router generated entry only describes app shape and delegates ISR mechanics to server/isr-cache.ts. ## Validation - vp check - vp test run tests/cache-control.test.ts tests/isr-cache.test.ts tests/kv-cache-handler.test.ts tests/app-page-cache.test.ts tests/app-page-response.test.ts tests/app-page-render.test.ts tests/app-route-handler-response.test.ts tests/app-route-handler-cache.test.ts tests/app-route-handler-execution.test.ts tests/pages-page-data.test.ts tests/pages-page-response.test.ts tests/next-config.test.ts tests/prerender.test.ts tests/deploy.test.ts tests/entry-templates.test.ts --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:23 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#985
No description provided.