11 Commits

Author SHA1 Message Date
Steve Faulkner c796d8b771 fix: strip Set-Cookie headers from fetch cache entries (#598)
When storing fetch responses in the cache, all response headers were
included without filtering. This meant Set-Cookie headers from the
original response would be replayed to subsequent requests served from
cache, which is incorrect since Set-Cookie is per-response and should
not persist across different requests.

Both the primary cache write path and the stale-while-revalidate
background refresh path now skip Set-Cookie when collecting response
headers for the cache entry.

Adds a test verifying that the original response retains Set-Cookie
but the cached response does not.
2026-03-19 19:55:24 -05:00
Stephen Zhou c17d6941be chore: migrate to vite plus (#535)
* chore: migrate to vite plus

* Disable typeAware and typeCheck

* Update CI

* Fix CI

* Fix test

* Clean

* Run test with vp

* Try revert

* react: false In test

* Fix test

* Revert "Try revert"

This reverts commit 009da10473.

* Update

* Update

* Try revert ci changes

* revert

* Run vp migrate

* Disable typeAware and typeCheck for now

* Better resolve for test

* Use vp dev instead of vite

* Update expect

* Fix NormalizeManifestModuleId

* Try increase timeout

* Update to use vp

* Try new check

* Bring back npx vp

* Migrate CI

* Make next-intl resolvable

* Update

* Update

* Update
2026-03-15 10:50:13 +00:00
Divanshu Chauhan (divkix) e6439ffce8 refactor: phase 1/2 unify per-request ALS into a shared request context (#450)
* refactor: consolidate 5 nested ALS scopes into unified request context

Every App Router request previously ran through 5-6 nested
AsyncLocalStorage.run() calls (headers, navigation, cache-state,
private-cache, fetch-cache, plus optional execution-context). Each
ALS scope push/pop has measurable cost on Workers isolates.

Introduce a single UnifiedRequestContext that holds all per-request
state in one flat object, backed by one ALS instance. Each shim module
checks isInsideUnifiedScope() first and reads its sub-fields from the
unified store, falling back to its own standalone ALS when outside
(SSR environment, Pages Router, tests).

- Create unified-request-context.ts with createRequestContext(),
  runWithRequestContext(), getRequestContext(), isInsideUnifiedScope()
- Rewire _getState() in headers, navigation-state, cache, cache-runtime,
  fetch-cache, and request-context to dual-path (unified first, own ALS
  fallback)
- Make each runWith*() a no-op inside unified scope
- Replace 5-deep nesting in app-rsc-entry.ts (main handler + ISR regen)
  with single _runWithUnifiedCtx() call
- Export ensureFetchPatch() from fetch-cache for standalone patch install
- Add 17 unit tests for unified context (isolation, nesting, concurrency)

* test: update entry-templates snapshots for unified ALS context

Snapshots were stale after the unified request context refactor changed
the generated entry code (new imports, removed 5-deep nesting).

* test: update app router waitUntil assertion

* refactor: unify request ALS across router flows

* fix: address unified request context review feedback

* fix: address unified ALS review feedback

* fix: address remaining unified ALS review feedback

* fix: wrap Pages Router ISR regeneration in unified context

Ensures patched fetch and ALS-based context are available during
background regeneration, matching App Router behavior.

- Creates fresh unified context with execution context for regen
- Calls ensureFetchPatch() to enable cache tagging
- Adds test verifying _runWithUnifiedCtx and ensureFetchPatch presence
- Updates snapshot

Part of Phase 1.5: minimal Pages Router fix for ISR parity.

* Fix dev ISR unified context parity

* fix: address unified ALS review followups

* Fix pages router unified execution context seeding

* Fix Pages Router ISR rerender state isolation

* fix: address unified ALS review feedback
2026-03-13 07:19:13 +00:00
Nathan Nguyen dcff154aa3 fix: dedup stale background refetches in fetch cache (#498)
* fix: dedup stale background refetches in fetch cache

Every concurrent request hitting a stale cache entry was independently
firing its own originalFetch(), creating a thundering herd on popular
endpoints. Add a per-cache-key dedup map (pendingRefetches) shared
across RSC/SSR environments via Symbol.for(), matching the existing
ISR regeneration dedup pattern in isr-cache.ts.

Also adds a 60s timeout safety net that force-cleans dedup entries
when upstream fetches hang, with identity checks in both .finally()
and setTimeout to prevent slot-ownership races.

* test: add fetch cache stale dedup concurrency tests

- Concurrent stale hits trigger only one background refetch
- Completed refetches allow new ones (lifecycle test)
- Failed refetches clean up dedup entry (error-path test)
- Hung fetches are force-cleaned after timeout
- Late-settling hung fetch does not evict replacement refetch
- _resetPendingRefetches() called in beforeEach for test isolation

* Update packages/vinext/src/shims/fetch-cache.ts

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

* fix: repair broken setTimeout structure from review suggestion

The applied suggestion duplicated the setTimeout block and broke
brace nesting. Fix the structure and also clear the timeout in
.finally() so completed refetches don't leave dangling timers.

* fix: wrap fake timer tests in try/finally for safety

If a test throws before vi.useRealTimers(), subsequent tests would
run under fake timers and break. Wrap both fake timer tests in
try/finally to guarantee cleanup.

* fix: guard background revalidation against caching error responses

The stale-while-revalidate background refetch unconditionally cached
whatever response came back, including 500s. A transient server error
would permanently replace good cached data. Add a `response.ok` guard
matching the existing cache-miss path.

* fix: tighten cache guard to status === 200

Per review feedback, narrow the cache guard from `response.ok` (2xx)
to `response.status === 200` in both the background revalidation path
and the cache-miss path. Non-200 success codes (201, 204, 206) are
not meaningful to cache in the fetch cache context.

---------

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
2026-03-12 17:58:49 +00:00
Divanshu Chauhan (divkix) 29649068f4 fix(fetch-cache): register stale-while-revalidate refetch with waitUntil() (#432)
* fix(fetch-cache): register stale-while-revalidate refetch with waitUntil()

The fetch cache's background refetch on stale entries was fire-and-forget.
On Cloudflare Workers, the isolate terminates after the response is sent,
killing in-flight refetches so stale entries never get refreshed.

Register the refetch promise with ExecutionContext.waitUntil() via the
existing getRequestExecutionContext() ALS accessor, matching the pattern
already used by ISR in isr-cache.ts.

* ci: re-trigger CI
2026-03-11 07:47:39 +00:00
James Anderson 764a496ce7 add oxfmt formatter (#380)
* add oxfmt formatter: config, scripts, CI, editor setup, docs

* rebuild lockfile

* fix: add Format to required checks list, remove dead ignore pattern

* run fmt

* add format to agents.md again
2026-03-09 14:56:14 +00:00
Jared Stowell 397a8dceda Fix fetch cache key collisions for Request and FormData bodies (#332)
* Fix fetch cache key collisions for Request and FormData bodies

* Add additional tests

* Address codex feedback by reading incrementally with early exit on content-length check + regression test

* Add regression test for already-consumed body

* Harden fetch cache keying for Request form bodies + tests

* Preserve FormData insertion order + add file name/type into cache key payload + tests

* Fix PR feedback + additional regression tests
2026-03-08 09:34:32 +00:00
Dane e9fdc7b5b0 fix: bound fetch cache key body serialization (#176)
* fix: bound fetch cache key body serialization

* Safeguard cache key serialization from large bodies

Bump cache key version to v2 and add safeguards to serializeBody to avoid OOM when processing large request bodies. Track cumulative raw byte size of streamed chunks and throw BodyTooLargeForCacheKeyError when exceeding the 1 MiB limit, call reader.cancel on errors, and add a cheap string-length pre-check. Adds tests covering oversized Uint8Array, string, multi-chunk ReadableStream, and FormData/File cases to verify such requests bypass the cache and still perform fetches.

---------

Co-authored-by: Sunil Pai <spai@cloudflare.com>
2026-02-27 22:30:02 +00:00
Sunil Pai c4f50b616f fix: fetch cache key generation — handle all body types, include all headers minus blocklist (#111)
* fix: fetch cache key generation — handle all body types, include all headers minus blocklist

- Make buildFetchCacheKey async to support stream/blob body serialization
- Handle all BodyInit types: string, Uint8Array, ReadableStream, FormData, Blob, URLSearchParams
- Switch header strategy from allowlist (3 auth headers) to blocklist (traceparent/tracestate), matching Next.js
- Include RequestInit fields (mode, redirect, credentials, etc.) in cache key
- SHA-256 hash the cache key for compact, deterministic storage
- Fix spent stream bug: restore _ogBody in stripNextFromInit so originalFetch gets usable body
- Add 19 new tests covering body types, header inclusion, body restoration, and URLSearchParams

* Improve fetch cache key generation

Replace simple auth-header extraction with a unified header collector, add a header blocklist and cache version prefix, and introduce async cache key building. serializeBody now handles various body types (Uint8Array, streams, URLSearchParams, FormData, Blob, string), preserves the original body on init._ogBody, and body content is included in the cache key. The cache key is generated by normalizing URL, method, headers and init options and hashing them with SHA-256. buildFetchCacheKey is now async and awaited where used, and stripNextFromInit restores the preserved original body. Also add hasAuthHeaders and move auth header list into a constant.

* PR #111 approved. 52 tests pass, 3 fixes correct.

Co-authored-by: threepointone <threepointone@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: threepointone <threepointone@users.noreply.github.com>
2026-02-26 12:33:37 +00:00
Steve Faulkner dda10590d7 fix: include relevant headers in fetch cache key (#93) 2026-02-26 04:17:18 +00:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00