Repository navigation
Conversation
…y into core adapter feat: enhance build process with additional server bundle customization options chore: update package.json scripts for improved build and testing workflow test: add unit tests for adapter build process and server bundle generation fix: ensure proper handling of external dependencies and edge configuration in server bundle
…ests for resolve plugin
…specific overrides
…udflare specific overrides" This reverts commit 37c4d90.
…ad of throwing - Extract ValidateConfigResult type with success/message/shouldThrow/level - Convert validateFunctionOptions and validateSplittedFunctionOptions to return result objects - Remove logger dependency from validateConfig.ts - Preserve compatibilityMatrix, TODO comment, @ts-expect-error pragmas - Add 5 characterization tests in validateConfig.spec.ts - No caller impact: compileConfig.ts is the sole importer (updated in T3)
- Export OpenNextOutput interface (was internal) - Extract buildOpenNextOutput(buildOpts) for construction-only (no fs write) - Keep legacy generateOutput as thin wrapper (construction + file write) - Preserve all construction logic verbatim, including @ts-expect-error - Add 3 characterization tests in generateOutput.spec.ts - Backward compatible: byte-equivalent output to today
- Replace bare validateConfig(config) call with result-handling block - Throw on shouldThrow:true (bad routes — preserves existing behavior) - Log at appropriate level on shouldThrow:false (level field from T1) - All 3 export signatures and edge-runtime detection block unchanged - Direct callers (aws/build.ts, cloudflare/utils.ts) unaffected
…OpenNextAdapterOptions - Make OpenNextAdapterOptions<T = OpenNextOutput> and buildAdapter<T> generic - Add validateConfig override hook (runs after callback in modifyConfig) - Add generateOutput override hook (returns T, gated by skipGenerateOutput) - buildAdapter serializes override return via fs.writeFileSync (override never touches fs) - Default path uses buildOpenNextOutput (extracted in T2) - Add 5 new tests covering override behaviors + default path + skipGenerateOutput - All 16 existing adapter tests preserved; AWS/Cloudflare adapters compile with default T
When an adapter config specifies full package-specifier paths (e.g., @opennextjs/aws/overrides/wrappers/aws-lambda.js), esbuild cannot resolve them during bundling. Use createRequire(args.path).resolve() in the openNextResolvePlugin to convert package specifiers to filesystem-relative paths at build time, falling back to the original value if resolution fails. This fixes the openbuild:local build error: ERROR: Could not resolve "@opennextjs/aws/overrides/wrappers/aws-lambda.js" ERROR: Could not resolve "@opennextjs/aws/overrides/tagCache/dynamodb.js" Added test I verifying resolution of a mock package in node_modules.
…solution and improve path handling
This was referenced Aug 16, 2026
Closed
Closed
commit: |
Add a standalone cache handler function that owns the incremental cache, the tag cache and CDN invalidation, and a `cache` override that other functions use to reach it. - `adapters/cache-adapter.ts` exposes `GET/PUT/DELETE /cache/*` and `POST /cache/revalidate-tags` through `createGenericHandler`. - `build/createCacheBundle.ts` bundles it to `.open-next/cache-function`, wired into `buildAdapter` behind the new `skipCache` option and reported as `cacheFunction` in the OpenNext output. - New top level `cacheHandler` config option carries the incremental cache, tag cache and cdn invalidation overrides for that function. - New `cache` override with `fetch`, `local` and `dummy` implementations, and a shared wire format in `utils/cache-get.ts` that splits the entry metadata into headers and the payload into the body. Nothing is removed here: the existing in-process `incrementalCache` and `tagCache` overrides keep working unchanged.
Build the OpenNext cache handler function as part of the worker and expose it through a named entrypoint, so the cache can later run behind a service binding - in this worker or in one of its own. - `service-cache.ts` is a `cache` override that speaks the cache handler HTTP API over the `NEXT_CACHE_SERVICE` binding. - `buildCacheFunction` emits the bundle from `beforeServerBundle`, because the worker imports the entrypoint; core's own cache step is skipped. - `runWithCloudflareContext` initialises the Cloudflare context outside of a request, for entrypoints invoked over RPC. The origin is now populated separately, on the first call coming from the fetch handler. - `withoutSelfEntrypointServices` strips the self referencing binding before handing the config to `getPlatformProxy`, which cannot resolve a named entrypoint of the worker it is configuring. - Drop `compile-cache-assets-manifest.ts`, unreferenced. `defineCloudflareConfig` is untouched: the entrypoint is built and exported but nothing routes to it yet.
The incremental cache and the tag cache stop running inside the server function. They only live in the cache handler function now, and the server, the middleware and the composable cache reach them through the `cache` override. - Remove `incrementalCache` and `tagCache` from `OverrideOptions`; they are only configured under `cacheHandler`. `resolveIncrementalCache` and `resolveTagCache` follow. The esbuild resolve plugin keeps its own fields, so adapter `defaultOverrides` are unaffected. - Move tag revalidation - `hasBeenRevalidated`, `writeTags` and CDN invalidation - out of `adapters/cache.ts`, `composable-cache.ts` and `cacheInterceptor.ts` and into the cache handler, which now applies them in `get`, `set` and `revalidateTags`. - `Cache.get` takes the additional tags to check, so the caller no longer needs the tag cache to resolve them. - `defineCloudflareConfig` wires `cache` to the `OpenNextCache` entrypoint and moves the incremental cache, the tag cache and the cdn invalidation to `cacheHandler`; `ensureCloudflareConfig`, `populateCache` and `isPurgeCacheEnabled` read the new location. - Examples move to `cache: "local"` with a `cacheHandler` block. BREAKING CHANGE: `default.override.incrementalCache` and `default.override.tagCache` are replaced by the top level `cacheHandler` option and `default.override.cache`.
conico974
force-pushed
the
conico/cache-4-swr-port
branch
from
August 16, 2026 13:47
18f4542 to
8672cf3
Compare
Ports opennextjs-aws #1122 and #1142 on top of the cache handler function.
- Tags carry optional `stale` and `expire` durations: `writeTags` accepts a
`{ tag, stale, expire }` input as well as a plain name, `OriginalTagCache`
entries gained the same fields, and both tag cache flavours gained an optional
`isStale`.
- `Cache.revalidateTags` takes the durations; the cache handler function turns
the `expire` delay into a timestamp, callers pass it through unchanged.
- The composable cache handler implements `updateTags`, added in Next.js 16.
- New per-request `RequestCache` on the OpenNext request context, so overrides
can deduplicate work within a request without touching global state, and
`globalThis.nextVersion` is injected in the esbuild banner.
- DynamoDB, fs-dev and the Cloudflare D1 / KV / sharded DO tag caches are
updated for the new signatures.
vicb
added this pull request to stack #52
September 21, 2026 07:15
# Conflicts: # create-cloudflare/next/wrangler.jsonc
Contributor
|
@conico974 ChaptGPT helped rebased the PR and fix a few findings. Could you please double check the additional commits Report
Detailed Updates
|
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical staleness handling and multiple moderate AWS, Cloudflare, and release compatibility issues remain.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (7)
Overwriting lastModified with the staleness marker · New Missing Cloudflare package release in changeset · New Treating invalid records as stale cache hits · New Marking expired records stale without a valid stale window · New Dropping SWR metadata during tag persistence · New Wrapper fails to forward the isStale method · New Breaking filter callback compatibility with union inputs · New
What changed in this PR
Ports SWR tag revalidation into the cache-handler stack across core, AWS, and Cloudflare adapters.
Changes:
- Adds stale/expiry metadata and Next.js 16
updateTagssupport. - Adds request-scoped cache deduplication and stale response handling.
- Updates cache overrides, tag caches, transports, and tests.
| File | Reviewed change |
|---|---|
packages/tests-unit/tests/overrides/tag-cache/next-mode.test.ts |
Tests next-mode SWR behavior. |
packages/tests-unit/tests/overrides/tag-cache/fs-dev.test.ts |
Tests filesystem SWR behavior. |
packages/tests-unit/tests/overrides/tag-cache/dynamodb.test.ts |
Tests DynamoDB expiry behavior. |
packages/tests-unit/tests/overrides/cache/local.test.ts |
Tests duration forwarding. |
packages/tests-unit/tests/overrides/cache/fetch.test.ts |
Tests HTTP duration forwarding. |
packages/tests-unit/tests/core/routing/cacheInterceptor.test.ts |
Tests stale response handling. |
packages/tests-unit/tests/adapters/composable-cache.test.ts |
Tests stale entries and updateTags. |
packages/tests-unit/tests/adapters/cache-adapter.test.ts |
Tests duration validation and conversion. |
packages/core/src/utils/requestCache.ts |
Adds per-request cache storage. |
packages/core/src/utils/promise.ts |
Initializes request cache context. |
packages/core/src/utils/cache.ts |
Adds stale checks and duration-aware writes. |
packages/core/src/types/overrides.ts |
Extends tag-cache and cache interfaces. |
packages/core/src/types/global.ts |
Adds request cache context typing. |
packages/core/src/types/cache.ts |
Adds updateTags. |
packages/core/src/overrides/tagCache/fs-dev.ts |
Adds filesystem SWR metadata handling. |
packages/core/src/overrides/tagCache/fs-dev-nextMode.ts |
Adds next-mode SWR handling. |
packages/core/src/overrides/tagCache/dummy.ts |
Adds dummy stale behavior. |
packages/core/src/overrides/cache/local.ts |
Forwards revalidation durations locally. |
packages/core/src/overrides/cache/fetch.ts |
Forwards revalidation durations remotely. |
packages/core/src/core/routing/cacheInterceptor.ts |
Serves stale entries and queues revalidation. |
packages/core/src/build/helper.ts |
Injects Next.js version metadata. |
packages/core/src/build/generateOutput.ts |
Updates override extraction commentary. |
packages/core/src/adapters/composable-cache.ts |
Implements stale results and updateTags. |
packages/core/src/adapters/cache.ts |
Accepts revalidation durations. |
packages/core/src/adapters/cache-handler.ts |
Validates and applies SWR durations. |
packages/cloudflare/src/api/overrides/tag-cache/tag-cache-filter.ts |
Updates tag filtering for structured inputs. |
packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.ts |
Updates KV tag writes. |
packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.spec.ts |
Adjusts KV mocks. |
packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts |
Updates sharded DO tag writes. |
packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.ts |
Updates D1 tag writes. |
packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.spec.ts |
Adjusts D1 mocks. |
packages/cloudflare/src/api/overrides/internal.ts |
Normalizes structured tag inputs. |
packages/cloudflare/src/api/overrides/cache/service-cache.ts |
Forwards revalidation durations. |
packages/cloudflare/src/api/overrides/cache/service-cache.spec.ts |
Tests service duration forwarding. |
packages/aws/src/overrides/tagCache/dynamodb.ts |
Adds DynamoDB SWR logic and request caching. |
packages/aws/src/overrides/tagCache/dynamodb-nextMode.ts |
Adds next-mode DynamoDB SWR handling. |
packages/aws/src/overrides/tagCache/dynamodb-lite.ts |
Adds lightweight DynamoDB SWR handling. |
.changeset/swr-tag-revalidation.md |
Documents the feature release. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Replace the lastModified=1 transport sentinel with explicit isStale metadata. Preserve generation times through Next cache adapters, request context, queue messages, and deduplication; retain revalidatedAt=1 for newly associated tags. Patch Next incremental-cache results to consume the stale flag for fetches and routes, including revalidate:false, while preserving blocking invalidation. Test against installed Next source plus wire parsing, adapters, the interceptor, and Cloudflare queue timestamp comparisons. Verification: pnpm fmt:fix, pnpm lint:fix, pnpm ts:check, pnpm test. Addresses Copilot discussion_r4091697951.
Require a newer stale timestamp and an unexpired or indefinite SWR window instead of treating every DynamoDB query result as stale. This excludes legacy hard invalidations, delayed writes for superseded events, and elapsed windows. Share queried records between lightweight hard and stale checks, re-evaluating expiry rather than caching a time-dependent classification. Add 16 adapter-level cases across SDK and lightweight backends, asserting one query per request. Verification: formatter, linter, workspace type checks and full tests passed. Addresses Copilot discussion_r4091698102 and discussion_r4091698162.
Keep filterFn string-based and normalize only the predicate argument. Forward accepted write objects unchanged so stale and expiry metadata reach the backend. This also fixes ordinary invalidation objects, not just SWR. Add mixed-input runtime coverage and a TypeScript semantic check of the consumer test under strictFunctionTypes, since the package build excludes specs. All formatting, lint, type checks and full tests pass. Addresses discussion_r4091698333.
Delegate optional isStale calls through withFilter, preserving the backend receiver and entry timestamp. Do not call the backend for an empty filtered tag list or advertise support when the backend has no stale method. Add regression coverage for positive delegation, excluded tags, receiver binding, and absent support. Formatting, lint, type checks and full tests pass. Addresses discussion_r4091698274.
Establish an OpenNext request context when a cache service invocation has no caller context. Preserve existing local contexts and waitUntil so GET hard/stale checks can share request-local DynamoDB records without leaking across invocations. Cover concurrent standalone request isolation and local context reuse. Update the test context fixture with RequestCache. Formatting, lint, workspace type checks and full tests pass.
Distinguish DynamoDB UnprocessedKeys from confirmed missing records before populating the request cache. Retry only pending keys with bounded exponential backoff and throw after three retries rather than treating unread invalidation records as absent. Retain processed hits and misses for reuse between hard and stale checks. Test mixed partial responses, retry payloads, shared results, and retry exhaustion without poisoned negative cache entries. Formatting, lint, type checks and full tests pass.
Mirror the existing original-mode owning-path hard-invalidation fallback when checking tag staleness. Fetch entries without explicit hard tags now inherit an active stale window from the owning path, retaining their generation timestamp. Add paired tests proving the fallback applies without hard tags and stays disabled when an explicit hard tag is supplied. Formatting, lint, type checks and full tests pass.
Reject duration-aware tag invalidations before D1, KV, or sharded Durable Object mutations instead of discarding stale and expiry metadata and silently performing a hard invalidation. Continue accepting plain tags and structured hard invalidations. Mark unsupported operations structurally so the cache handler can return HTTP 501 across bundle boundaries, expose the reason through the cache transport, and surface it from local, fetch, and Cloudflare service clients. Document the limitation and cover each backend and transport. Formatting, lint, type checks, full tests, and focused tests pass.
Apply the repository formatter to the installed-Next tag staleness regression added during review. This is formatting-only and fixes the remote fmt check; runtime behavior and assertions are unchanged.
Preserve the unsupported-operation marker when local, fetch, and Cloudflare service cache clients reconstruct cache-handler failures. Rethrow marked failures from the incremental and composable Next adapters while continuing to tolerate ordinary cache errors. Update the core max-profile E2E to wait for background SWR regeneration and the Cloudflare E2E to verify unsupported SWR leaves cached content unchanged rather than silently hard-invalidating it. Add marker propagation tests. Code checks, full unit tests, selected builds, and focused core/Cloudflare E2E tests pass.
Treat an explicit expire duration of zero as immediate hard invalidation and omit stale metadata. This preserves the supported structured hard-write shape for Cloudflare tag caches while positive and indefinite durations remain SWR and are rejected there. Cover both next and original tag-cache modes. Code checks, full unit tests, and the focused Cloudflare revalidateTag/after E2E tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Ports opennextjs-aws#1122 and opennextjs-aws#1142 on top of the cache handler function.
staleandexpiredurations:writeTagsaccepts a{ tag, stale, expire }input as well as a plain name,OriginalTagCacheentries gained the same fields, and both tag cache flavours gained an optionalisStale.Cache.revalidateTagstakes the durations. The cache handler turns theexpiredelay into a timestamp, callers pass it through unchanged.updateTags, added in Next.js 16.RequestCacheon the OpenNext request context, so overrides can deduplicate work within a request without touching global state.globalThis.nextVersionis injected in the esbuild banner.Supersedes #30, which was based on the pre-restructure layout (
packages/open-next/).