Commit Graph

4 Commits

Author SHA1 Message Date
Nathan Nguyen 6b7e7557a9 fix: support revalidate = false for App route segment config (#1046)
* fix: support revalidate = false for App route segment config (#1041)

- Build-time: extractExportConstNumber now matches false and returns Infinity
- Classifiers: classifyLayoutSegmentConfig and classifyAppRoute correctly
  classify revalidate = false as static (indefinite caching)
- Runtime: getAppRouteHandlerRevalidateSeconds returns Infinity for false,
  enabling ISR cache gates that emit STATIC cache-control instead of SWR
- Template: RSC entry resolves revalidate = false from page exports as Infinity
- Cache-Control: buildRouteHandlerCacheControl emits STATIC cache-control
  when revalidateSeconds is Infinity, matching the page-path behavior

Ported from Next.js segment config validation:
https://github.com/vercel/next.js/blob/canary/packages/next/src/build/segment-config/app/app-segment-config.ts

* fix: exclude Infinity from route handler ISR read/write gates

* Update packages/vinext/src/build/report.ts

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
2026-05-04 17:04:39 +01:00
Nathan Nguyen 18962c51d6 fix(app-router): action redirects (#910)
* Fix app form action redirects

* Trigger CI rerun

* Refactor progressive action handling

* Address app action review feedback

* Trigger CI rerun
2026-04-27 20:40:03 +01:00
Nathan Nguyen f6b5b8a9d6 fix: emit no-store Cache-Control for revalidate = 0 route handlers (#865)
* test: cover revalidate = 0 route handler never-cache semantics

Route handlers with `export const revalidate = 0` currently emit no
Cache-Control header at all. Downstream gates treat the 0 value as
equivalent to "no revalidate configured" and skip the header write,
which lets CDNs and browsers apply heuristic caching to a response the
author explicitly opted out of.

The existing policy test asserted the buggy collapse (`toBeNull()` for
revalidate = 0), protecting the behaviour instead of verifying it.
Rewrite the policy test to assert the preserved 0 and add coverage for
the read/write/header gates for revalidate = 0. Add a response-level
test asserting the exact Cache-Control string Next.js emits for
revalidate = 0 (private, no-cache, no-store, max-age=0, must-revalidate).

These tests fail against the current implementation and will pass once
the policy helper preserves 0 and the header builder branches on 0.

* fix: emit no-store Cache-Control for revalidate = 0 route handlers

A route handler exporting `export const revalidate = 0` currently
sends no Cache-Control header at all. Downstream CDNs and browsers
fall back to heuristic caching, so a response the author explicitly
opted out of caching can still be cached and replayed.

getAppRouteHandlerRevalidateSeconds filtered on `> 0`, collapsing the
"never cache" signal (0) and the "no revalidate configured" signal
(null) into the same null return. The three gates downstream
(shouldReadAppRouteHandlerCache, shouldApplyAppRouteHandlerRevalidateHeader,
shouldWriteAppRouteHandlerCache) all short-circuit when revalidateSeconds
is null, so the header was never applied and no cache entry was written.
That second part was accidentally correct; the first part left the
response vulnerable.

Preserve 0 as a distinct value, using Number.isFinite to keep NaN and
Infinity out of the number branch along with negatives. Keep the ISR
read and write gates on `> 0` so a never-cache handler never serves or
persists ISR entries. Keep the header gate on `!== null` so 0 still
triggers a Cache-Control write, and branch buildRouteHandlerCacheControl
on 0 to emit Next.js's canonical no-store string (matches
packages/next/src/server/lib/cache-control.ts).
2026-04-21 17:53:30 +01:00
Steve Faulkner 2ae1eaaa1a refactor: extract app route handler policy helpers (#622) 2026-03-21 08:56:41 -05:00