Skip to content

Add PPR support and improve caching mechanisms#24

Open
conico974 wants to merge 16 commits intomainfrom
conico/ppr-support
Open

Add PPR support and improve caching mechanisms#24
conico974 wants to merge 16 commits intomainfrom
conico/ppr-support

Conversation

@conico974
Copy link
Contributor

@conico974 conico974 commented Feb 15, 2026

Add PPR support in both cloudflare and aws.
Left to do :

  • Fix use cache in cloudflare
  • Figure out why use cache doesn't work in ISR Let's do that in a follow up PR
  • Re enable experimental test in cloudflare

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 15, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@opennextjs/cloudflare@24
npm i https://pkg.pr.new/@opennextjs/aws@24

commit: f1e93f4

@conico974 conico974 marked this pull request as ready for review March 21, 2026 16:57
Copilot AI review requested due to automatic review settings March 21, 2026 16:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Partial Prerendering (PPR) plumbing to OpenNext’s cache interception flow and updates Cloudflare/AWS adapters to support newer Next.js caching APIs, along with test and example updates to validate the behavior.

Changes:

  • Introduces PartialResult / initialResponse to support PPR prelude + resume-request handling across routing/request handling.
  • Extends cache serialization/deserialization for App Router cached entries with postponed + segmentData.
  • Updates Next.js versions and adjusts e2e/unit tests and example apps to exercise PPR and composable cache behavior.

Reviewed changes

Copilot reviewed 36 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Bumps catalog Next.js versions (aws/e2e) to 16.2.1.
pnpm-lock.yaml Lockfile updates for Next.js + transitive deps.
packages/tests-unit/tests/adapters/cache.test.ts Adds unit coverage for APP_PAGE segmentData + postponed.
packages/tests-e2e/tests/experimental/use-cache.test.ts Skips ISR case pending investigation.
packages/tests-e2e/tests/experimental/ppr.test.ts Updates PPR expectations (postponed/prerender/cache headers).
packages/tests-e2e/tests/appRouter/revalidateTag.test.ts Updates expected x-nextjs-cache behavior.
packages/tests-e2e/tests/appRouter/og.test.ts Skips OG tests pending fixes.
packages/tests-e2e/playwright.config.js Re-enables the experimental e2e project.
packages/open-next/src/types/open-next.ts Adds PartialResult and RoutingResult.initialResponse.
packages/open-next/src/types/cache.ts Adds segmentData to APP_PAGE cache value type.
packages/open-next/src/overrides/converters/edge.ts Allows returning { initialResponse, request } in dangerous mode for PPR.
packages/open-next/src/core/routingHandler.ts Handles PartialResult from cache interception and surfaces initialResponse.
packages/open-next/src/core/routing/cacheInterceptor.ts Implements PPR cache interception/resume-request behavior and segment prefetch handling.
packages/open-next/src/core/requestHandler.ts Streams initialResponse then resumes via POST (next-resume) in Node handler.
packages/open-next/src/adapters/middleware.ts Propagates initialResponse through middleware adapter result.
packages/open-next/src/adapters/cache.ts Writes postponed + segmentData into incremental cache storage.
packages/open-next/src/adapter.ts Sets top-level cacheHandlers (default/remote) for composable cache.
packages/cloudflare/src/cli/templates/worker.ts Adds Cloudflare worker handling for PPR prelude + resume streaming.
packages/cloudflare/src/cli/build/patches/plugins/route-module.ts Patches Next route-module to require composable cache handler module.
packages/cloudflare/src/cli/build/patches/plugins/route-module.spec.ts Notes missing test coverage for the new composable cache patch.
packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts Treats certain manifests as optional to avoid worker crashes.
packages/cloudflare/src/cli/adapter.ts Sets top-level cacheHandlers (default/remote) for composable cache.
examples/experimental/tsconfig.json Switches JSX mode and includes additional .next dev types.
examples/experimental/src/proxy.ts Renames default export and removes explicit runtime config.
examples/experimental/src/components/cached.tsx Updates to cacheLife/cacheTag APIs and sets cache lifetime.
examples/experimental/src/app/ppr/page.tsx Removes experimental_ppr flag export.
examples/experimental/src/app/api/revalidate/route.ts Adjusts revalidateTag usage in the example.
examples/experimental/package.json Updates example to Next 16.2 canary + React 19.2.4 and adds local scripts/overrides.
examples/experimental/open-next.config.ts Consolidates local/dev OpenNext config and enables cache interception.
examples/experimental/open-next.config.local.ts Removes local config file (merged into main config).
examples/experimental/next.config.ts Updates Next config to use cacheComponents.
examples-cloudflare/e2e/experimental/package.json Renames CF scripts to *:cf.
examples-cloudflare/e2e/experimental/open-next.config.ts Enables cache interception for CF e2e experimental app.
examples-cloudflare/e2e/experimental/e2e/use-cache.test.ts Skips ISR-related cache tests pending investigation.
examples-cloudflare/e2e/experimental/e2e/ppr.test.ts Updates PPR expectations to match new header behavior.
examples-cloudflare/e2e/app-router/e2e/revalidateTag.test.ts Updates expected x-nextjs-cache behavior.
examples-cloudflare/e2e/app-router/e2e/og.test.ts Skips OG test pending fixes.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +334 to +342
if (isDynamicISR && !isStaticRoute) {
pathToUse = Object.entries(PrerenderManifest?.dynamicRoutes ?? {}).find(([, dr]) => {
const regex = new RegExp(dr.routeRegex);
return regex.test(localizedPath);
})?.[1].fallback! as string;
} else if (localizedPath === "") {
pathToUse = "/index";
}
const cachedData = await globalThis.incrementalCache.get(pathToUse);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrerenderManifest.dynamicRoutes[...].fallback can be string | false | null (per types). Casting with ?.[1].fallback! as string can produce false/null/undefined, which then gets passed to incrementalCache.get(pathToUse) and can throw or create invalid cache keys for dynamic ISR routes. Guard this by checking typeof fallback === "string" before using it, and fall back to localizedPath (or return event) when fallback is not a string.

Copilot uses AI. Check for mistakes.
Comment on lines +221 to +235
} else if (isPartialResult(cacheInterceptionResult)) {
// We need to apply the headers to both the result (the streamed response) and the resume request
applyMiddlewareHeaders(cacheInterceptionResult.result, headers);
applyMiddlewareHeaders(cacheInterceptionResult.resumeRequest, headers);
return {
internalEvent: cacheInterceptionResult.resumeRequest,
isExternalRewrite: false,
origin: false,
isISR: false,
resolvedRoutes,
initialURL: event.url,
locale: NextConfig.i18n ? detectLocale(eventOrResult, NextConfig.i18n) : undefined,
rewriteStatusCode: middlewareEventOrResult.rewriteStatusCode,
initialResponse: cacheInterceptionResult.result,
};
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the PartialResult path, middleware headers are applied to cacheInterceptionResult.result (an InternalResult). applyMiddlewareHeaders currently flattens string[] values via .join(","), which breaks multi-value headers like Set-Cookie (commas are not a valid delimiter there). For PPR initial responses this can corrupt cookies. Consider updating the middleware-header application logic to preserve string[] values on InternalResult.headers (at least for set-cookie) instead of joining them.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +110
headers.set("content-encoding", "identity"); // To fix PPR locally

return new Response(body, {
status: reqOrResp.initialResponse.statusCode,
headers: headers,
});
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers.set("content-encoding", "identity") is applied unconditionally for all PPR responses. This disables compression and may negatively affect bandwidth/caching behavior in production. If this is only needed for wrangler dev/localhost, gate it on the request hostname or an env flag (similar to the conditional workaround in cloudflare-node wrapper).

Copilot uses AI. Check for mistakes.
Comment on lines 357 to 359
test("patch the createSnapshot function", () => {
//TODO: add the test for the composable cache handler
expect(
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createComposableCacheHandlersRule is introduced/used in the patcher, but there’s currently no assertion covering it (note the TODO). Adding a targeted test that snapshots the diff produced by createComposableCacheHandlersRule would help prevent silent breakage across Next.js versions/minified output changes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants