diff --git a/examples/e2e/app-router/sst-env.d.ts b/examples/e2e/app-router/sst-env.d.ts
deleted file mode 100644
index 59d33569..00000000
--- a/examples/e2e/app-router/sst-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/examples/e2e/pages-router/.env.production b/examples/e2e/pages-router/.env.production
new file mode 100644
index 00000000..82e6f9e7
--- /dev/null
+++ b/examples/e2e/pages-router/.env.production
@@ -0,0 +1 @@
+SOME_PROD_VAR=bar
\ No newline at end of file
diff --git a/examples/e2e/pages-router/.gitignore b/examples/e2e/pages-router/.gitignore
new file mode 100644
index 00000000..93e8ff69
--- /dev/null
+++ b/examples/e2e/pages-router/.gitignore
@@ -0,0 +1,43 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+.open-next
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# playwright
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/examples/e2e/pages-router/README.md b/examples/e2e/pages-router/README.md
new file mode 100644
index 00000000..ecdf4f78
--- /dev/null
+++ b/examples/e2e/pages-router/README.md
@@ -0,0 +1,3 @@
+# Pages Router
+
+This project uses the Pages Router exclusively.
diff --git a/examples/e2e/pages-router/e2e/404.test.ts b/examples/e2e/pages-router/e2e/404.test.ts
new file mode 100644
index 00000000..31605e83
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/404.test.ts
@@ -0,0 +1,9 @@
+import { expect, test } from "@playwright/test";
+
+test("should return 404 on a route not corresponding to any route", async ({ page }) => {
+ const result = await page.goto("/not-existing/route");
+ expect(result).toBeDefined();
+ expect(result?.status()).toBe(404);
+ const headers = result?.headers();
+ expect(headers?.["cache-control"]).toBe("private, no-cache, no-store, max-age=0, must-revalidate");
+});
diff --git a/examples/e2e/pages-router/e2e/data.test.ts b/examples/e2e/pages-router/e2e/data.test.ts
new file mode 100644
index 00000000..919fcc6c
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/data.test.ts
@@ -0,0 +1,21 @@
+import { expect, test } from "@playwright/test";
+
+test("fix _next/data", async ({ page }) => {
+ await page.goto("/");
+
+ const isrJson = page.waitForResponse("/_next/data/*/en/isr.json");
+ await page.locator('[href="/isr/"]').click();
+ const response = await isrJson;
+ expect(response.ok()).toBe(true);
+ expect(response.request().url()).toMatch(/\/_next\/data\/.*\/en\/isr\.json$/);
+ await page.waitForURL("/isr/");
+
+ const homeJson = page.waitForResponse("/_next/data/*/en.json");
+ await page.locator('[href="/"]').click();
+ const response2 = await homeJson;
+ expect(response2.ok()).toBe(true);
+ expect(response2.request().url()).toMatch(/\/_next\/data\/.*\/en\.json$/);
+ await page.waitForURL("/");
+ const body = await response2.json();
+ expect(body).toEqual({ pageProps: { hello: "world" }, __N_SSG: true });
+});
diff --git a/examples/e2e/pages-router/e2e/header.test.ts b/examples/e2e/pages-router/e2e/header.test.ts
new file mode 100644
index 00000000..7811be1b
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/header.test.ts
@@ -0,0 +1,12 @@
+import { expect, test } from "@playwright/test";
+
+test("should test if poweredByHeader adds the correct headers ", async ({ page }) => {
+ const result = await page.goto("/");
+ expect(result).toBeDefined();
+ expect(result?.status()).toBe(200);
+ const headers = result?.headers();
+
+ // Both these headers should be present cause poweredByHeader is true in pagesRouter
+ expect(headers?.["x-powered-by"]).toBe("Next.js");
+ expect(headers?.["x-opennext"]).toBe("1");
+});
diff --git a/examples/e2e/pages-router/e2e/i18n.test.ts b/examples/e2e/pages-router/e2e/i18n.test.ts
new file mode 100644
index 00000000..4271f93d
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/i18n.test.ts
@@ -0,0 +1,17 @@
+import { expect, test } from "@playwright/test";
+
+test("Next config headers with i18n", async ({ page }) => {
+ const responsePromise = page.waitForResponse((response) => {
+ return response.status() === 200;
+ });
+ await page.goto("/");
+
+ const response = await responsePromise;
+ // Response header should be set
+ const headers = response.headers();
+ // Headers from next.config.js should be set
+ expect(headers["x-custom-header"]).toEqual("my custom header value");
+
+ // Headers from middleware should be set
+ expect(headers["x-from-middleware"]).toEqual("true");
+});
diff --git a/examples/e2e/pages-router/e2e/isr.test.ts b/examples/e2e/pages-router/e2e/isr.test.ts
new file mode 100644
index 00000000..aac897d2
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/isr.test.ts
@@ -0,0 +1,41 @@
+import { expect, test } from "@playwright/test";
+
+test("Incremental Static Regeneration", async ({ page }) => {
+ test.setTimeout(45000);
+ await page.goto("/");
+ await page.locator("[href='/isr/']").click();
+ await page.waitForURL("/isr/");
+ // Load the page a couple times to regenerate ISR
+
+ let el = page.getByText("Time:");
+ // Track the static time
+ let time = await el.textContent();
+ let newTime: typeof time;
+ let tempTime = time;
+ do {
+ await page.waitForTimeout(1000);
+ await page.reload();
+ time = tempTime;
+ el = page.getByText("Time:");
+ newTime = await el.textContent();
+ tempTime = newTime;
+ } while (time !== newTime);
+ await page.reload();
+ await page.waitForTimeout(1000);
+ el = page.getByText("Time:");
+ const midTime = await el.textContent();
+ // Expect that the time is still stale
+ expect(midTime).toEqual(newTime);
+
+ // Wait 10 + 1 seconds for ISR to regenerate time
+ await page.waitForTimeout(11000);
+ let finalTime = newTime;
+ do {
+ await page.waitForTimeout(2000);
+ el = page.getByText("Time:");
+ finalTime = await el.textContent();
+ await page.reload();
+ } while (newTime === finalTime);
+
+ expect(newTime).not.toEqual(finalTime);
+});
diff --git a/examples/e2e/pages-router/e2e/playwright.config.ts b/examples/e2e/pages-router/e2e/playwright.config.ts
new file mode 100644
index 00000000..602e4861
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/playwright.config.ts
@@ -0,0 +1,54 @@
+import { defineConfig, devices } from "@playwright/test";
+import type nodeProcess from "node:process";
+
+declare const process: typeof nodeProcess;
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: "./",
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: "html",
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: "http://localhost:8791",
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: "on-first-retry",
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: "chromium",
+ use: { ...devices["Desktop Chrome"] },
+ },
+ // TODO(vicb): enable all browsers
+ // {
+ // name: "firefox",
+ // use: { ...devices["Desktop Firefox"] },
+ // },
+ // {
+ // name: "webkit",
+ // use: { ...devices["Desktop Safari"] },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: {
+ command: "pnpm preview",
+ url: "http://localhost:8791",
+ reuseExistingServer: !process.env.CI,
+ timeout: 70_000,
+ },
+});
diff --git a/examples/e2e/pages-router/e2e/redirect.test.ts b/examples/e2e/pages-router/e2e/redirect.test.ts
new file mode 100644
index 00000000..3a8b290a
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/redirect.test.ts
@@ -0,0 +1,25 @@
+import { expect, test } from "@playwright/test";
+
+test("Single redirect", async ({ page }) => {
+ await page.goto("/next-config-redirect-without-locale-support/");
+
+ await page.waitForURL("https://opennext.js.org/");
+ const el = page.getByRole("heading", { name: "OpenNext" });
+ await expect(el).toBeVisible();
+});
+
+test("Redirect with default locale support", async ({ page }) => {
+ await page.goto("/redirect-with-locale/");
+
+ await page.waitForURL("/ssr/");
+ const el = page.getByText("SSR");
+ await expect(el).toBeVisible();
+});
+
+test("Redirect with locale support", async ({ page }) => {
+ await page.goto("/nl/redirect-with-locale/");
+
+ await page.waitForURL("/nl/ssr/");
+ const el = page.getByText("SSR");
+ await expect(el).toBeVisible();
+});
diff --git a/examples/e2e/pages-router/e2e/rewrite.test.ts b/examples/e2e/pages-router/e2e/rewrite.test.ts
new file mode 100644
index 00000000..668668d5
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/rewrite.test.ts
@@ -0,0 +1,25 @@
+import { expect, test } from "@playwright/test";
+import { validateMd5 } from "../../utils";
+
+const EXT_PNG_MD5 = "405f45cc3397b09717a13ebd6f1e027b";
+
+test("Single Rewrite", async ({ page }) => {
+ await page.goto("/rewrite");
+
+ const el = page.getByText("Nextjs Pages Router");
+ await expect(el).toBeVisible();
+});
+
+test("Rewrite with query", async ({ page }) => {
+ await page.goto("/rewriteUsingQuery?d=ssr");
+
+ const el = page.getByText("SSR");
+ await expect(el).toBeVisible();
+});
+
+test("Rewrite to external image", async ({ request }) => {
+ const response = await request.get("/external-on-image");
+ expect(response.status()).toBe(200);
+ expect(response.headers()["content-type"]).toBe("image/png");
+ expect(validateMd5(await response.body(), EXT_PNG_MD5)).toBe(true);
+});
diff --git a/examples/e2e/pages-router/e2e/ssr.test.ts b/examples/e2e/pages-router/e2e/ssr.test.ts
new file mode 100644
index 00000000..93c88f2c
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/ssr.test.ts
@@ -0,0 +1,33 @@
+import { expect, test } from "@playwright/test";
+
+test("Server Side Render", async ({ page }) => {
+ await page.goto("/");
+ await page.locator('[href="/ssr/"]').click();
+
+ await page.waitForURL("/ssr/");
+ let el = page.getByText("Time:");
+ await expect(el).toBeVisible();
+ let time = await el.textContent();
+
+ await page.reload();
+
+ el = page.getByText("Time:");
+ let newTime = await el.textContent();
+ await expect(el).toBeVisible();
+
+ for (let i = 0; i < 5; i++) {
+ await page.reload();
+ el = page.getByText("Time:");
+ newTime = await el.textContent();
+ await expect(el).toBeVisible();
+ expect(time).not.toEqual(newTime);
+ time = newTime;
+ await page.waitForTimeout(250);
+ }
+});
+
+test("Server Side Render with env", async ({ page }) => {
+ await page.goto("/ssr/");
+ const el = page.getByText("Env:");
+ expect(await el.textContent()).toEqual("Env: bar");
+});
diff --git a/examples/e2e/pages-router/e2e/trailing.test.ts b/examples/e2e/pages-router/e2e/trailing.test.ts
new file mode 100644
index 00000000..4a9179e2
--- /dev/null
+++ b/examples/e2e/pages-router/e2e/trailing.test.ts
@@ -0,0 +1,15 @@
+import { expect, test } from "@playwright/test";
+
+test("trailingSlash redirect", async ({ page }) => {
+ const response = await page.goto("/ssr");
+
+ expect(response?.request().redirectedFrom()?.url()).toMatch(/\/ssr$/);
+ expect(response?.request().url()).toMatch(/\/ssr\/$/);
+});
+
+test("trailingSlash redirect with search parameters", async ({ page }) => {
+ const response = await page.goto("/ssr?happy=true");
+
+ expect(response?.request().redirectedFrom()?.url()).toMatch(/\/ssr\?happy=true$/);
+ expect(response?.request().url()).toMatch(/\/ssr\/\?happy=true$/);
+});
diff --git a/examples/e2e/pages-router/next.config.ts b/examples/e2e/pages-router/next.config.ts
new file mode 100644
index 00000000..ea3e7858
--- /dev/null
+++ b/examples/e2e/pages-router/next.config.ts
@@ -0,0 +1,67 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ transpilePackages: ["@example/shared", "react", "react-dom"],
+ i18n: {
+ locales: ["en", "nl"],
+ defaultLocale: "en",
+ },
+ cleanDistDir: true,
+ reactStrictMode: true,
+ output: "standalone",
+ // outputFileTracingRoot: "../sst",
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+ headers: async () => [
+ {
+ source: "/",
+ headers: [
+ {
+ key: "x-custom-header",
+ value: "my custom header value",
+ },
+ ],
+ },
+ ],
+ rewrites: async () => [
+ { source: "/rewrite", destination: "/", locale: false },
+ {
+ source: "/rewriteUsingQuery",
+ destination: "/:destination/",
+ locale: false,
+ has: [
+ {
+ type: "query",
+ key: "d",
+ value: "(?\\w+)",
+ },
+ ],
+ },
+ {
+ source: "/external-on-image",
+ destination: "https://opennext.js.org/share.png",
+ },
+ ],
+ redirects: async () => [
+ {
+ source: "/next-config-redirect-without-locale-support/",
+ destination: "https://opennext.js.org/",
+ permanent: false,
+ basePath: false,
+ locale: false,
+ },
+ {
+ source: "/redirect-with-locale/",
+ destination: "/ssr/",
+ permanent: false,
+ },
+ ],
+ trailingSlash: true,
+ poweredByHeader: true,
+};
+
+export default nextConfig;
diff --git a/examples/e2e/pages-router/open-next.config.ts b/examples/e2e/pages-router/open-next.config.ts
new file mode 100644
index 00000000..0f7794ff
--- /dev/null
+++ b/examples/e2e/pages-router/open-next.config.ts
@@ -0,0 +1,25 @@
+import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";
+
+const config: OpenNextConfig = {
+ default: {
+ override: {
+ wrapper: "cloudflare-node",
+ converter: "edge",
+ // Unused implementation
+ incrementalCache: "dummy",
+ tagCache: "dummy",
+ queue: "dummy",
+ },
+ },
+
+ middleware: {
+ external: true,
+ override: {
+ wrapper: "cloudflare-edge",
+ converter: "edge",
+ proxyExternalRequest: "fetch",
+ },
+ },
+};
+
+export default config;
diff --git a/examples/e2e/pages-router/package.json b/examples/e2e/pages-router/package.json
new file mode 100644
index 00000000..933cdc4c
--- /dev/null
+++ b/examples/e2e/pages-router/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "pages-router",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "openbuild": "node ../../packages/open-next/dist/index.js build --build-command \"npx turbo build\"",
+ "dev": "next dev --turbopack --port 3002",
+ "build": "next build",
+ "start": "next start --port 3002",
+ "lint": "next lint",
+ "clean": "rm -rf .turbo node_modules .next .open-next",
+ "build:worker": "pnpm opennextjs-cloudflare",
+ "dev:worker": "wrangler dev --port 8791 --inspector-port 9351",
+ "preview": "pnpm build:worker && pnpm dev:worker",
+ "e2e-fix": "playwright test -c e2e/playwright.config.ts"
+ },
+ "dependencies": {
+ "@opennextjs/cloudflare": "workspace:*",
+ "@example/shared": "workspace:*",
+ "next": "catalog:e2e",
+ "react": "catalog:e2e",
+ "react-dom": "catalog:e2e"
+ },
+ "devDependencies": {
+ "@playwright/test": "catalog:",
+ "@types/node": "catalog:e2e",
+ "@types/react": "catalog:e2e",
+ "@types/react-dom": "catalog:e2e",
+ "autoprefixer": "catalog:e2e",
+ "postcss": "catalog:e2e",
+ "tailwindcss": "catalog:e2e",
+ "typescript": "catalog:default"
+ }
+}
diff --git a/examples/e2e/pages-router/postcss.config.js b/examples/e2e/pages-router/postcss.config.js
new file mode 100644
index 00000000..aae516f5
--- /dev/null
+++ b/examples/e2e/pages-router/postcss.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {
+ content: ["./src/**/*.{js,ts,jsx,tsx,mdx}", "../../examples/shared/**/*.{jsx,tsx}"],
+ },
+ autoprefixer: {},
+ },
+};
diff --git a/examples/e2e/pages-router/public/favicon.ico b/examples/e2e/pages-router/public/favicon.ico
new file mode 100644
index 00000000..4ba005f2
Binary files /dev/null and b/examples/e2e/pages-router/public/favicon.ico differ
diff --git a/examples/e2e/pages-router/public/static/frank.webp b/examples/e2e/pages-router/public/static/frank.webp
new file mode 100644
index 00000000..b2cc67f0
Binary files /dev/null and b/examples/e2e/pages-router/public/static/frank.webp differ
diff --git a/examples/e2e/pages-router/src/middleware.ts b/examples/e2e/pages-router/src/middleware.ts
new file mode 100644
index 00000000..682614fd
--- /dev/null
+++ b/examples/e2e/pages-router/src/middleware.ts
@@ -0,0 +1,14 @@
+import type { NextRequest } from "next/server";
+import { NextResponse } from "next/server";
+
+export function middleware(request: NextRequest) {
+ return NextResponse.next({
+ headers: {
+ "x-from-middleware": "true",
+ },
+ });
+}
+
+export const config = {
+ matcher: ["/"],
+};
diff --git a/examples/e2e/pages-router/src/pages/_app.tsx b/examples/e2e/pages-router/src/pages/_app.tsx
new file mode 100644
index 00000000..a9d6fd8d
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/_app.tsx
@@ -0,0 +1,7 @@
+import "@/styles/globals.css";
+
+import type { AppProps } from "next/app";
+
+export default function App({ Component, pageProps }: AppProps) {
+ return ;
+}
diff --git a/examples/e2e/pages-router/src/pages/_document.tsx b/examples/e2e/pages-router/src/pages/_document.tsx
new file mode 100644
index 00000000..ffc3f3cc
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/_document.tsx
@@ -0,0 +1,13 @@
+import { Head, Html, Main, NextScript } from "next/document";
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/e2e/pages-router/src/pages/api/hello.ts b/examples/e2e/pages-router/src/pages/api/hello.ts
new file mode 100644
index 00000000..41b59cbc
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/api/hello.ts
@@ -0,0 +1,10 @@
+// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
+import type { NextApiRequest, NextApiResponse } from "next";
+
+type Data = {
+ hello: string;
+};
+
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+ res.status(200).json({ hello: "world" });
+}
diff --git a/examples/e2e/pages-router/src/pages/index.tsx b/examples/e2e/pages-router/src/pages/index.tsx
new file mode 100644
index 00000000..d72ee99e
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/index.tsx
@@ -0,0 +1,34 @@
+import Nav from "@example/shared/components/Nav";
+import Head from "next/head";
+
+// Not used, but necessary to get prefetching to work
+export function getStaticProps() {
+ return {
+ props: {
+ hello: "world",
+ },
+ };
+}
+
+export default function Home() {
+ return (
+ <>
+
+ Nextjs Pages Router
+
+
+
+
+ Nextjs Pages Router
+
+
+
+
+
+ >
+ );
+}
diff --git a/examples/e2e/pages-router/src/pages/isr/index.tsx b/examples/e2e/pages-router/src/pages/isr/index.tsx
new file mode 100644
index 00000000..5980b914
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/isr/index.tsx
@@ -0,0 +1,20 @@
+import type { InferGetStaticPropsType } from "next";
+import Link from "next/link";
+
+export async function getStaticProps() {
+ return {
+ props: {
+ time: new Date().toISOString(),
+ },
+ revalidate: 10,
+ };
+}
+
+export default function Page({ time }: InferGetStaticPropsType) {
+ return (
+
+ );
+}
diff --git a/examples/e2e/pages-router/src/pages/ssr/index.tsx b/examples/e2e/pages-router/src/pages/ssr/index.tsx
new file mode 100644
index 00000000..86eb0e42
--- /dev/null
+++ b/examples/e2e/pages-router/src/pages/ssr/index.tsx
@@ -0,0 +1,20 @@
+import type { InferGetServerSidePropsType } from "next";
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ time: new Date().toISOString(),
+ envVar: process.env.SOME_PROD_VAR,
+ },
+ };
+}
+
+export default function Page({ time, envVar }: InferGetServerSidePropsType) {
+ return (
+ <>
+ SSR
+ Time: {time}
+ Env: {envVar}
+ >
+ );
+}
diff --git a/examples/e2e/pages-router/src/styles/globals.css b/examples/e2e/pages-router/src/styles/globals.css
new file mode 100644
index 00000000..cb7c88f8
--- /dev/null
+++ b/examples/e2e/pages-router/src/styles/globals.css
@@ -0,0 +1,95 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --max-width: 1100px;
+ --border-radius: 12px;
+ --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono",
+ "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
+
+ --foreground-rgb: 0, 0, 0;
+ --background-start-rgb: 214, 219, 220;
+ --background-end-rgb: 255, 255, 255;
+
+ --primary-glow: conic-gradient(
+ from 180deg at 50% 50%,
+ #16abff33 0deg,
+ #0885ff33 55deg,
+ #54d6ff33 120deg,
+ #0071ff33 160deg,
+ transparent 360deg
+ );
+ --secondary-glow: radial-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
+
+ --tile-start-rgb: 239, 245, 249;
+ --tile-end-rgb: 228, 232, 233;
+ --tile-border: conic-gradient(#00000080, #00000040, #00000030, #00000020, #00000010, #00000010, #00000080);
+
+ --callout-rgb: 238, 240, 241;
+ --callout-border-rgb: 172, 175, 176;
+ --card-rgb: 180, 185, 188;
+ --card-border-rgb: 131, 134, 135;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
+
+ --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
+ --secondary-glow: linear-gradient(
+ to bottom right,
+ rgba(1, 65, 255, 0),
+ rgba(1, 65, 255, 0),
+ rgba(1, 65, 255, 0.3)
+ );
+
+ --tile-start-rgb: 2, 13, 46;
+ --tile-end-rgb: 2, 5, 19;
+ --tile-border: conic-gradient(
+ #ffffff80,
+ #ffffff40,
+ #ffffff30,
+ #ffffff20,
+ #ffffff10,
+ #ffffff10,
+ #ffffff80
+ );
+
+ --callout-rgb: 20, 20, 20;
+ --callout-border-rgb: 108, 108, 108;
+ --card-rgb: 100, 100, 100;
+ --card-border-rgb: 200, 200, 200;
+ }
+}
+
+* {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+html,
+body {
+ max-width: 100vw;
+ overflow-x: hidden;
+}
+
+body {
+ color: rgb(var(--foreground-rgb));
+ background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))
+ rgb(var(--background-start-rgb));
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+@media (prefers-color-scheme: dark) {
+ html {
+ color-scheme: dark;
+ }
+}
diff --git a/examples/e2e/pages-router/tsconfig.json b/examples/e2e/pages-router/tsconfig.json
new file mode 100644
index 00000000..7c3c0ed0
--- /dev/null
+++ b/examples/e2e/pages-router/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ "@example/shared": ["../shared"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "../utils.ts"],
+ "exclude": ["node_modules", "open-next.config.ts"]
+}
diff --git a/examples/e2e/pages-router/wrangler.json b/examples/e2e/pages-router/wrangler.json
new file mode 100644
index 00000000..57ad53ee
--- /dev/null
+++ b/examples/e2e/pages-router/wrangler.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "node_modules/wrangler/config-schema.json",
+ "main": ".open-next/worker.js",
+ "name": "pages-router",
+ "compatibility_date": "2024-12-30",
+ "compatibility_flags": ["nodejs_compat"],
+ "assets": {
+ "directory": ".open-next/assets",
+ "binding": "ASSETS"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 556549f1..1a712c40 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -219,6 +219,49 @@ importers:
specifier: 'catalog:'
version: 3.105.0(@cloudflare/workers-types@4.20250109.0)
+ examples/e2e/app-pages-router:
+ dependencies:
+ '@example/shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@opennextjs/cloudflare':
+ specifier: workspace:*
+ version: link:../../../packages/cloudflare
+ next:
+ specifier: catalog:e2e
+ version: 15.1.0(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ react:
+ specifier: catalog:e2e
+ version: 19.0.0
+ react-dom:
+ specifier: catalog:e2e
+ version: 19.0.0(react@19.0.0)
+ devDependencies:
+ '@playwright/test':
+ specifier: 'catalog:'
+ version: 1.47.0
+ '@types/node':
+ specifier: catalog:e2e
+ version: 20.17.6
+ '@types/react':
+ specifier: catalog:e2e
+ version: 19.0.0
+ '@types/react-dom':
+ specifier: catalog:e2e
+ version: 19.0.0
+ autoprefixer:
+ specifier: catalog:e2e
+ version: 10.4.15(postcss@8.4.27)
+ postcss:
+ specifier: catalog:e2e
+ version: 8.4.27
+ tailwindcss:
+ specifier: catalog:e2e
+ version: 3.3.3
+ typescript:
+ specifier: catalog:default
+ version: 5.7.3
+
examples/e2e/app-router:
dependencies:
'@example/shared':
@@ -259,6 +302,49 @@ importers:
specifier: catalog:default
version: 5.7.3
+ examples/e2e/pages-router:
+ dependencies:
+ '@example/shared':
+ specifier: workspace:*
+ version: link:../shared
+ '@opennextjs/cloudflare':
+ specifier: workspace:*
+ version: link:../../../packages/cloudflare
+ next:
+ specifier: catalog:e2e
+ version: 15.1.0(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ react:
+ specifier: catalog:e2e
+ version: 19.0.0
+ react-dom:
+ specifier: catalog:e2e
+ version: 19.0.0(react@19.0.0)
+ devDependencies:
+ '@playwright/test':
+ specifier: 'catalog:'
+ version: 1.47.0
+ '@types/node':
+ specifier: catalog:e2e
+ version: 20.17.6
+ '@types/react':
+ specifier: catalog:e2e
+ version: 19.0.0
+ '@types/react-dom':
+ specifier: catalog:e2e
+ version: 19.0.0
+ autoprefixer:
+ specifier: catalog:e2e
+ version: 10.4.15(postcss@8.4.27)
+ postcss:
+ specifier: catalog:e2e
+ version: 8.4.27
+ tailwindcss:
+ specifier: catalog:e2e
+ version: 3.3.3
+ typescript:
+ specifier: catalog:default
+ version: 5.7.3
+
examples/e2e/shared:
dependencies:
next: