import { Suspense } from 'react'
import Link from 'next/link'

export default async function Page() {
  return (

          This page has has a slow to fill cache. When not bypassing the dev
          cache the initial load should be slow while the cache warms up. You
          won't see the Suspense fallback because the entire response is blocked
          until the cache warms up. Subsequent loads should be fast because the
          cache is warm and nothing needs to

pense fallback={
Loading from cache...
}>
          <CachedData />
        </Suspense>

      <Link href="/">Go back home</Link>

  )
}

async function CachedData() {
  'use cache'

  await new Promise((r) => setTimeout(r, 2

 Suspend When bypassing caches in
          dev with "disable cache" the request should instantly show a fallback
          UI and show the final content after the delay.
        </p>
      </section>
      <section>
        <Sus


tuck on shared state from the outer render scope. The same function completed when run in isolation, which usually means a module-scoped value (for example a top-level Map used to dedupe fetches) is joining a promise created outside the cache. "use cache" already dedupes calls with the same arguments — within a request and across requests on the same server instance — so the surrounding dedupe layer is both unnecessary and the likely cause. Remove it and rely on "use cache" alone for deduping.'

describe('u

 auto-start.
    skipStart: process.env.NEXT_TEST_MODE !== 'dev',
  })

  if (skipped) {
    return
  }

  if (!isNextDev) {
    it('is a dev-only suite', () => {})
    return
  }

  describe('when a "use cache" fill hangs in the static stage due to module

se-cache-deadlock-probe', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    // Probe behavior is dev-only; skip the production server start, but
    // let dev mode


pense } from 'react'
import { getData, preload } from '../shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=recovery-stuck'

// `Quick` emits a chunk during the first probe's window, which suppresses
// that probe's verdict via the mid-probe recovery check. `Stuck` is
// deadlocked on outer-scope state, so the cache body never settles —
// after the chunk, the scheduler reschedules the idle timer and a second
// probe fires once the fill has been quiet for another thres

nc function Stuck() {
  const data = await getData(sharedUrl).then((res) => res.text())
  return
{data}

}

async function getCachedData() {
  'use cache'

  return (
    <>
      <Suspense fallback={null}>
        <Quick />
      </Suspen

hold. That
// second probe sees no chunks during its own window and correctly
// reports the deadlock.
async function Quick() {
  await new Promise((resolve) => setTimeout(resolve, 11_000))
  return
quick

}

asy


./shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=static'

async function getCachedData(): Promise<string> {
  'use cache'

  // Joins the outer fetch via the module-scoped dedupe map (see note in
  // ../shared.ts).
  return getData(sharedUrl).then((res) => res.text())
}

async function Cached() {
  // The timeout error must also be shown in the Next.js DevTools when the
  // invocation is wrapped in a try/catch.
  try {
    const data = await getCachedData()

    re

return
Error: {error.message}

  }
}

export default function Page() {
  // Simulate another part of the tree (e.g. a sibling component or a shared
  // data loader) kicking off the same fetch in outer scope before `Cached`
  // runs.


rom 'next/server'
import { ReactNode, Suspense } from 'react'

// A cache fill in the Dynamic stage (after `connection()`) should not be
// subject to the dev fill timeout — and the probe-watchdog is skipped for
// the same reason. Both are gated on `outerWorkUnitStore.cacheSignal` /
// the Dynamic-stage check in `generateCacheEntryImpl`. The sleep is longer
// than the 10s probe idle threshold so the test would fail if the
// watchdog were armed.
async function Cached(): Promise<ReactNode> {
  'use cache'

, 12_000))

  return
cached

}

async function Dynamic(): Promise<ReactNode> {
  await connection()

  return <Cached />
}

export default function Page() {
  return (
    <Suspense fallback="Loading...">
      <Dynamic />
    </Suspense>

await new Promise((resolve) => setTimeout(resolve


pped,
  } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  if (isDev) {
    // TODO-APP: re-enable after reworking the error overlay.
    describe.skip('Missing required tags', () => {
      it('should error on page load', async () => {
        const browser = await next.browser('/missing-tags', {
          waitHydration: false,
        })

        await waitForRedbox(browser)
        expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`

  const browser = await next.browser('/has-tags', {
          waitHydration: false,
        })
        await browser.elementByCss('a').click()

        await waitForRedbox(browser)
        expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`

     "Please make sure to include the following tags in your root layout: <html>, <body>.

        Missing required root layout tags: html, body"
      `)
      })

      it('should error on page navigation', async () => {


-without-exports', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  describe('"type": "commonjs" in package.json', () => {
    it('should render without errors: import cjs', async () => {
      const $ = await next.render$('/import-cjs')
      expect($('p').text()).toContain('lib-cjs: esm')
    })

    it('should render without errors: require cjs', async () => {
      const $ = await next.render$('/require-cjs')
      expect($('p').text()).toContain('lib-cjs: cjs')
    })
  })

  de

   it('should render without errors: import esm', async () => {
      const $ = await next.render$('/import-esm')
      expect($('p').text()).toContain('lib-esm: esm')
    })

    it('should render without errors: require esm', async () => {
      const $

scribe('"type": "module" in package.json', () => {


from 'e2e-utils'
import { waitForNoRedbox, retry } from 'next-test-utils'

describe('app-dir - action-in-pages-router', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
  })

  it('should not error on fake server action in pages router', async () => {
    const browser = await next.browser('/foo')
    const button = await browser.elementByCss('button')
    await button.click()

    await retry(async () => {
      const browserLogText = (await browser.log())
        .map((item) =

art) {
    // Disabling for turbopack because the chunk path are different
    if (!process.env.IS_TURBOPACK_TEST) {
      it('should not contain server action in page bundle', async () => {
        const pageBundle = await next.readFile('.next/server/page

> item.message)
        .join('')
      // This is a fake server action, a simple function so it can still work
      expect(browserLogText).toContain('action:foo')
      await waitForNoRedbox(browser)
    })
  })

  if (isNextSt


import { nextTestSetup } from 'e2e-utils'
import { check } from 'next-test-utils'

describe('app dir - emotion-js', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      '@emotion/react': 'latest',
      '@emotion/cache': 'latest',
    },
  })

  if (skipped) {
    return
  }

  it('should render emotion-js css with compiler.emotion option correctly', async () => {
    const browser = await next.browser('/')
    const el = browser.eleme

    const el2 = browser.elementByCss('p')
    expect(await el2.text()).toBe('Red')
    await check(
      async () =>
        await browser.eval(
          `window.getComputedStyle(document.querySelector('p')).color`
        ),
      'rgb(255, 0, 0)'

ntByCss('h1')
    expect(await el.text()).toBe('Blue')
    await check(
      async () =>
        await browser.eval(
          `window.getComputedStyle(document.querySelector('h1')).color`
        ),
      'rgb(0, 0, 255)'
    )


dy.on('end', resolve)
  })
  return result
}

describe('app dir - external dependency', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    dependencies: {
      swr: '2.2.5',
      undici: '6.21.0',
    },
    packageJson: {
      scripts: {
        build: 'next build',
        dev: 'next dev',
        start: 'next start',
      },
    },
    installCommand: 'pnpm i',
    startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start',
    buildCommand: 'pnpm build',
    skipD

      const result = await resolveStreamResponse(response)
      expect(result).toContain('Server: index.default')
      expect(result).toContain('Server subpath: subpath.default')
      expect(result).toContain('Client: index.default')
      expect(resul

eployment: true,
  })

  if (skipped) {
    return
  }

  it('should be able to opt-out 3rd party packages being bundled in server components', async () => {
    await next.fetch('/react-server/optout').then(async (response) => {


_cacheLife, cacheLife } from 'next/cache'

export default async function Page() {
  const stable = await stableLife()
  const unstable1 = await unstableLife1()
  const unstable2 = await unstableLife2()
  return (
    <>

        This page calls a "use cache" function that uses the unstable_cacheLife
        API twice. We expect to see a warning once per server lifetime when
        using unstable_cacheLife.

{stable}

{unstable1}

{unstable2}

 {
  'use cache'
  unstable_cacheLife('minutes')
  return Math.random()
}

async function unstableLife2() {
  'use cache'
  unstable_cacheLife('minutes')
  return Math.random()
}

async function stableLife() {
  'use cache'
  cacheLife('minutes')
  return

</div>
    </>
  )
}

async function unstableLife1()


a realistic migration hazard. The documented "preload"
// pattern (see the "Preloading data" section of the
// caching-without-cache-components guide in the Next.js docs) wraps the loader
// in `React.cache`, so its store is tied to the ALS snapshot — `'use cache'`
// bodies run in a clean snapshot and miss, re-executing the loader in cache
// scope. That path is safe during migration.
//
// What's modeled here is the common-in-the-wild variant that uses a
// module-scoped `Map` instead of `React.cache`. Th

ed fetch in the outer scope returns a
//     hanging promise that intentionally never resolves.
//   - In dev, the outer fetch is parked on the Dynamic stage, which can't
//     advance until the cache fills. The cache awaits the outer fetch via the
//

e map lives above ALS, so a
// `'use cache'` body joining it reuses the outer-scope promise (request scope
// or prerender scope) — a promise that was never meant to resolve for the
// cache:
//   - During prerendering, an uncach


ort { cookies } from 'next/headers'
import { Suspense } from 'react'
import { getData, preload } from '../shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=runtime'

async function getCachedData(): Promise<string> {
  'use cache'

  // Joins the outer fetch via the module-scoped dedupe map (see note in
  // ../shared.ts).
  return getData(sharedUrl).then((res) => res.text())
}

async function Cached() {
  // The timeout error must also be shown in the Next.js DevTools w

n Runtime() {
  await cookies()

  // Simulate another part of the tree (e.g. a sibling component or a shared
  // data loader) kicking off the same fetch in outer scope before `Cached`
  // runs.
  preload(sharedUrl)

  return <Cached />
}

export default

hen the
  // invocation is wrapped in a try/catch.
  try {
    const data = await getCachedData()

    return
{data}

  } catch (error) {
    return
Error: {error.message}

  }
}

async functio


ployment: true,
    env: {
      NEXT_TEST_LOG_VALIDATION: '1',
    },
  })
  if (skipped) return

  if (isNextStart && !isTurbopack) {
    it.skip('TODO: snapshot tests for webpack', () => {})
    return
  }

  if (isNextStart) {
    beforeAll(async () => {
      await next.build({ args: ['--experimental-build-mode', 'compile'] })
    })
    afterEach(async () => {
      await next.stop()
    })
  } else {
    beforeAll(async () => {
      await next.start()
    })
  }

  const prerender = async (pathname:

arning'. Implicit validation fires on bare
  // pages in dev only — build does not validate unless a segment
  // explicitly escalates with `level: 'experimental-error'`. Per-segment overrides
  // (`level`, `true`, `false`) layer on top of this.
  //
  //

 string) => {
    return await next.build({
      args: [
        '--experimental-build-mode',
        'generate',
        '--debug-build-paths',
        `app${pathname}/page.tsx`,
      ],
    })
  }

  // Validation level is 'w


om 'next/headers'
import { refresh } from 'next/cache'
import { redirect } from 'next/navigation'

export async function addTodoEntry(entry: string) {
  // simulate some latency
  await new Promise((resolve) => setTimeout(resolve, 500))

  const cookieStore = await cookies()
  const existing = cookieStore.get('todo-entries')?.value || '[]'
  const entries = JSON.parse(existing)
  entries.push(entry)
  cookieStore.set('todo-entries', JSON.stringify(entries))
}

export async function getTodoEntries() {
  cons

ookieStore.get('todo-entries')?.value || '[]'
  return JSON.parse(existing) as string[]
}

export async function addEntryAndRefresh(formData: FormData) {
  const entry = formData.get('entry') as string

  await addTodoEntry(entry)

  refresh()
  redirect('


pense fallback={
Loading from cache...
}>
          <CachedData />
        </Suspense>

      <Link href="/other">/Other</Link>

  )
}

async function CachedData() {
  'use cache'

  await new Promise((r) => setTimeout(r, 20


e-events', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should not call onLoad multiple times', async () => {
    const imageRequests = []
    const browser = await next.browser('/fulfilled', {
      beforePageLoad(page) {
        page.on('request', (request) => {
          if (request.resourceType() === 'image') {
            imageRequests.push(request.url())
          }
        })
      },
    })

    let logsIdx = 0
    await retry(async () => {
      const logs = await br

 logs.length
    })
    expect(imageRequests).toEqual([expect.stringContaining('test')])
    imageRequests.length = 0

    await browser.locator(':text("Show Client image")').click()

    await retry(async () => {
      const logs = await browser.log()

owser.log()
      expect(
        logs.slice(logsIdx).filter(({ source }) => source === 'error')
      ).toEqual([
        {
          source: 'error',
          message: 'hydrated image load',
        },
      ])
      logsIdx =


utils'
import { getClientReferenceManifest } from 'next-test-utils'

describe('client-reference-chunking', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  it('should use the same chunks for client references across routes', async () => {
    const browser = await next.browser('/')
    await browser.elementByCss('a[href="/issue"]').click()

    expect(await browser.elementByCss('body').text()).toContain(
      'Welcome to the Issue Page'
    )

    let rootM

    let issueManifest = getClientReferenceManifest(next, '/issue/page')

    // These two routes have the same client component references, so these should be exactly the
    // same (especially the `chunks` field)
    expect(rootManifest.clientModules).to

anifest = getClientReferenceManifest(next, '/page')


rver'
import { Suspense } from 'react'

export default async function Page({ params }) {
  return (
    <>
      <p>
        This page accesses params synchronously. This does not trigger dynamic,
        and the build should succeed. In dev mode, we do log an error for the
        sync access though.
      </p>
      <ParamsReadingComponent params={params} />
      <Suspense>
        <Dynamic />
      </Suspense>
    </>
  )
}

async function ParamsReadingComponent({ params }) {
  return (
    <div>

' '}
      {String(params.slug)}

  )
}

// This component ensures that we're creating a partially prerendered page, so
// that we also test that there are no sync params defined during the
// resume.
async function Dynami

this component read the `slug` param synchronously:{


next/headers'
import { Suspense } from 'react'
import { getData, preload } from '../shared'

const sharedUrl =
  'https://next-data-api-endpoint.vercel.app/api/random?page=private-cookies'

// A `'use cache: private'` body that legitimately reads `cookies()` AND
// joins the module-scoped hanging promise from `shared.ts`. The probe must:
//
//   1. Forward the real cookie values into the worker's request store so
//      `cookies()` returns the same values as it would in the real fill
//      (rather than t

napshot
// forwarding regresses, this fixture catches it before any "no-snapshot"
// fallback path.
async function getCachedData(): Promise<string> {
  'use cache: private'

  // Read a cookie — exercises the worker's reconstructed `cookies` object.
  cons

hrowing).
//   2. Run the body in a fresh module scope so `getData(sharedUrl)` returns
//      a fresh fetch instead of joining the outer scope's hanging promise.
//
// Both pieces have to work for this test to pass; if request-s


pect from 'expect'

describe('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should not have route specific errors', async () => {
    expect(next.cliOutput).not.toMatch('Error: Route "/')
    expect(next.cliOutput).not.toMatch('Error occurred prerendering page')
  })

  it('should prerender pages with cached `Date.now()` calls', async () => {
    let $ = await next.render$('/date/

).text()).toBe('at buildtime')
      expect($('#page').text()).toBe('at buildtime')
      expect($('#value').text()).toMatch(/^\d+$/)
    }
  })

  it('should prerender pages with cached `Date()` calls', async () => {
    let $ = await next.render$('/date/

now/cached', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#value').text()).toMatch(/^\d+$/)
    } else {
      expect($('#layout'


pense } from 'react'

import { cookies } from 'next/headers'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  return (
    <>

This page calls `cookies()` in a child component.

        With PPR this page can be partially static because the dynamic API usage
        is inside a suspense boundary.

        Without PPR this page is fully dynamic because a dynamic API was used.

      <Suspense fallback="load

SentinelValue()}</div>
    </>
  )
}

async function ComponentThatReadsCookies() {
  let sentinelCookie
  try {
    const cookie = (await cookies()).get('x-sentinel')
    if (cookie) {
      sentinelCookie = cookie.value
    } else {
      sentinelCookie =

ing...">
        <ComponentThatReadsCookies />
      </Suspense>
      <Suspense fallback="loading too...">
        <OtherComponent />

{getSentinelValue()}

      </Suspense>

{get


'react'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders two components. The first calls a simulated IO
        function wrapped in `"use cache""`. The second calls the simulated IO
        function without being wrapped in `"use cache""`.

Each component is wrapped in a Suspense boundary

        With PPR this page should have a static shell that includes the cac

/>
      </Suspense>
      <Suspense fallback="loading too...">
        <ComponentTwo />

{getSentinelValue()}

      </Suspense>

{getSentinelValue()}

    </>
  )
}

async function ComponentOne() {
  r

hed
        IO result and a loading state for the boundary surrounding the uncached
        IO result.

Without PPR this page should be dynamic.

      <Suspense fallback="loading...">
        <ComponentOne


'react'
import { unstable_cache as cache } from 'next/cache'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders two components. The first calls a simulated IO
        function wrapped in `unstable_cache`. The second calls the simulated IO
        function without being wrapped in `unstable_cache`.

Each component is wrapped in a Suspense boundary

        With PPR t

<Suspense fallback="loading...">
        <ComponentOne />
      </Suspense>
      <Suspense fallback="loading too...">
        <ComponentTwo />

{getSentinelValue()}

      </Suspense>

{getSentinelValue()}

his page should have a static shell that includes the cached
        IO result and a loading state for the boundary surrounding the uncached
        IO result.

Without PPR this page should be dynamic.


port { Suspense } from 'react'

export async function generateStaticParams() {
  const set = new Set()
  set.add(await fetchRandom('a'))
  set.add(await fetchRandom('a'))

  return Array.from(set).map((value) => {
    return {
      slug: ('' + value).slice(2),
    }
  })
}

export default async function Layout({ children, params }) {
  return (
    <Suspense fallback="loading">
      <Inner params={params}>{children}</Inner>
    </Suspense>
  )
}

async function Inner({ children, params }) {
  return (

{(await params).slug}

{children}

    </>
  )
}

const fetchRandom = async (entropy: string) => {
  const response = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b=' + entropy
  )
  return response.tex


./../../../../../getSentinelValue'

export default async function Page({
  params,
  children,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
  children: React.ReactNode
}) {
  return (

        This Layout does key checking of the params prop in a server component

        page lowcard:{' '}

          {'' + Reflect.has(await params, 'lowcard')}

        page highcard:{'

{'' + Reflect.has(await params, 'highcard')}

        page foo:{' '}
        {'' + Reflect.has(await params, 'foo')}

      {getSentinelValue()}

'}
        <span id="param-has-highcard">


/getSentinelValue'

export default function Page({
  params,
  children,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
  children: React.ReactNode
}) {
  return (

        This Layout does key checking of the params prop in a client component

        page lowcard:{' '}

          {'' + Reflect.has(use(params), 'lowcard')}

        page highcard:{' '}
        <span id="pa

arams), 'highcard')}

        page foo:{' '}
        {'' + Reflect.has(use(params), 'foo')}

      {getSentinelValue()}
      {children}
    </sectio

ram-has-highcard">
          {'' + Reflect.has(use(p


rt { getSentinelValue } from '../../../../../../getSentinelValue'

export default async function Page({
  params,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
}) {
  return (

        This Page does key checking of the params prop in a server component

        page lowcard:{' '}

          {'' + Reflect.has(await params, 'lowcard')}

        page highcard:{' '}

ct.has(await params, 'highcard')}

        page foo:{' '}
        {'' + Reflect.has(await params, 'foo')}

      {getSentinelValue()}

<span id="param-has-highcard">
          {'' + Refle


ort { use } from 'react'

import { getSentinelValue } from '../../../../../../getSentinelValue'

export default function Page({
  params,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
}) {
  return (

        This Page does key checking of the params prop in a client component

        page lowcard:{' '}

          {'' + Reflect.has(use(params), 'lowcard')}

        page

>
          {'' + Reflect.has(use(params), 'highcard')}

        page foo:{' '}
        {'' + Reflect.has(use(params), 'foo')}

      {getSentinelValue()}</

highcard:{' '}
        <span id="param-has-highcard"


e } from '../../../../../../getSentinelValue'

export default async function Page({
  params,
  children,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
  children: React.ReactNode
}) {
  const copied = { ...(await params) }
  return (

        This Layout spreads params in a server component after awaiting them

        page lowcard: {copied.lowcard}

        page highcard: <span id="p

ghcard">{copied.highcard}

        param key count:{' '}
        {Object.keys(copied).length}

      {getSentinelValue()}
      {children}


use } from 'react'

import { getSentinelValue } from '../../../../../../getSentinelValue'

export default function Page({
  params,
  children,
}: {
  params: Promise<{ lowcard: string; highcard: string }>
  children: React.ReactNode
}) {
  const copied = { ...use(params) }
  return (

        This Layout spreads params in a client component after `use`ing them

        page lowcard: {copied.lowcard}

ard">{copied.highcard}

        param key count:{' '}
        {Object.keys(copied).length}

      {getSentinelValue()}
      {children}

  )
}

page highcard: <span id="param-copied-highc


SentinelValue } from '../../../../../../../../getSentinelValue'

export default async function Page({
  params,
}: {
  params: Promise<{ dyn: string; then: string; value: string; status: string }>
}) {
  const copied = { ...(await params) }
  return (

        This Page accesses params that have name collisions with Promise
        properties. When synchronous access is available we assert that you can
        access non colliding param names directly and all params if you await

it params).then)}

          value:{' '}
          {getValueAsString((await params).value)}

          status:{' '}

            {get

          dyn:{' '}
          {getValueAsString((await params).dyn)}

          then:{' '}
          {getValueAsString((awa


import { use } from 'react'

import { getSentinelValue } from '../../../../../../../../getSentinelValue'

export default function Page({
  params,
}: {
  params: Promise<{ dyn: string; then: string; value: string; status: string }>
}) {
  const copied = { ...use(params) }
  return (

        This Page accesses params that have name collisions with Promise
        properties. When synchronous access is available we assert that you can
        access non colliding param names directly

lueAsString(use(params).then)}

          value:{' '}
          {getValueAsString(use(params).value)}

          status:{' '}
          {getVal

and all params if you await

          dyn: {getValueAsString(use(params).dyn)}

          then:{' '}
          {getVa


SentinelValue } from '../../../../../../../../getSentinelValue'

export default async function Page({
  params,
  children,
}: {
  params: Promise<{ dyn: string; then: string; value: string; status: string }>
  children: React.ReactNode
}) {
  const copied = { ...(await params) }
  return (

        This Layout accesses params that have name collisions with Promise
        properties. When synchronous access is available we assert that you can
        access non colliding param names

pan id="param-then">{getValueAsString((await params).then)}

          value:{' '}
          {getValueAsString((await params).value)}

          status:{' '}

directly and all params if you await

          dyn:{' '}
          {getValueAsString((await params).dyn)}

          then:{' '}
          <s


import { use } from 'react'

import { getSentinelValue } from '../../../../../../../../getSentinelValue'

export default function Layout({
  params,
  children,
}: {
  params: Promise<{ dyn: string; then: string; value: string; status: string }>
  children: React.ReactNode
}) {
  const copied = { ...use(params) }
  return (

        This Layout accesses params that have name collisions with Promise
        properties. When synchronous access is available we assert that you can

{' '}
          {getValueAsString(use(params).then)}

          value:{' '}
          {getValueAsString(use(params).value)}

          status:{'

access non colliding param names directly and all params if you await

          dyn: {getValueAsString(use(params).dyn)}

          then:


{ Suspense } from 'react'

import { getSentinelValue } from '../../../getSentinelValue'

type AnySearchParams = { [key: string]: string | string[] | undefined }

export default async function Page({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  return (
    <>

        This page awaits the searchParams promise before accessing a property on
        it.

The `use` is inside a Suspense boundary

With PPR we expect the page to have a partially sta

ense>
        <ComponentTwo />
      </Suspense>
    </>
  )
}

async function Component({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  const params = await searchParams
  return (
    <>
      <div>
        This component accessed

tic page

Without PPR we expect the page to be dynamic

      <Suspense fallback="outer loading...">
        <Suspense fallback="inner loading...">
          <Component searchParams={searchParams} />
        </Susp


{ getSentinelValue } from '../../../getSentinelValue'

import { Suspense, use } from 'react'

type AnySearchParams = { [key: string]: string | string[] | undefined }

export default async function Page({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  return (
    <>

        This page `use`'s the searchParams promise before accessing a property
        on it.

With PPR we expect the page to have an empty shell

Without PPR we expect the page to

)
}

function Component({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  const params = use(searchParams)
  return (
    <>

        This component accessed `searchParams.sentinel`: "
        {params.sen

be dynamic</p>
      <Suspense fallback="outer loading...">
        <Suspense fallback="inner loading...">
          <Component searchParams={searchParams} />
        </Suspense>
        <ComponentTwo />
      </Suspense>
    </>


lient'

import { Suspense, use } from 'react'

import { getSentinelValue } from '../../../getSentinelValue'

type AnySearchParams = { [key: string]: string | string[] | undefined }

export default function Page({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  return (
    <>

        This page `use`'s the searchParams promise before accessing a property
        on it.

The `use` is inside a Suspense boundary

With PPR we expect the page to have a

        </Suspense>
        <ComponentTwo />
      </Suspense>
    </>
  )
}

function Component({
  searchParams,
}: {
  searchParams: Promise<AnySearchParams>
}) {
  const params = use(searchParams)
  return (
    <>
      <div>
        This component ac

partially static page

Without PPR we expect the page to be dynamic

      <Suspense fallback="outer loading...">
        <Suspense fallback="inner loading...">
          <Component searchParams={searchParams} />


lValue } from '../../../getSentinelValue'
/**
 * This test case is constructed to demonstrate how using the async form of cookies can lead to a better
 * prerender with cache components when PPR is on. There is no difference when PPR is off. When PPR is on the second component
 * can finish rendering before the prerender completes and so we can produce a static shell where the Fallback closest
 * to Cookies access is read
 */
export default async function Page() {
  return (
    <>
      <Suspense fallback=

>
      <ComponentTwo />

{getSentinelValue()}

    </>
  )
}

async function Component() {
  await connection()
  return (

      cookie foo

  )
}

function ComponentTwo() {
  return
fo

"loading...">
        <Component />
      </Suspense


{ Suspense } from 'react'
import { connection } from 'next/server'

import { getSentinelValue } from '../../../getSentinelValue'

export default async function Page() {
  const pendingConnection = connection()
  return (

Deep Connection Reader

        This component was passed the connection promise returned by
        `connection()`. It is rendered inside a Suspense boundary.

        If cacheComponents is turned off the `connection()` call would

{getSentinelValue()}

          </>
        }
      >
        <DeepConnectionReader pendingConnection={pendingConnection} />
      </Suspense>

  )
}

async function DeepConnectionReader({
  pendingConnection,
}

trigger a
        dynamic point at the callsite and the suspense boundary would also be
        blocked for over one second

      <Suspense
        fallback={
          <>

loading connection...


ies
  expression: string
}) {
  return (
    <>
      <ForOf cookies={cookies} expression={expression} />
      <Spread cookies={cookies} expression={expression} />
      <Size cookies={cookies} expression={expression} />
      <GetAndGetAll cookies={cookies} expression={expression} />
      <Has cookies={cookies} expression={expression} />
      <Set cookies={cookies} expression={expression} />
      <Delete cookies={cookies} expression={expression} />
      <Clear cookies={cookies} expression={expression}

cookie] of cookies) {
    if (cookieName.startsWith('x-sentinel')) {
      output.push(

{print(cookie)}

      )
    }
  }

  return (

for

 />
      <ToString cookies={cookies} expression={expression} />
    </>
  )
}

function ForOf({
  cookies,
  expression,
}: {
  cookies: ReadonlyRequestCookies
  expression: string
}) {
  let output = []
  for (let [cookieName,


ort { Suspense } from 'react'
import { cookies } from 'next/headers'

import { getSentinelValue } from '../../getSentinelValue'
/**
 * This test case is constructed to demonstrate how using the async form of cookies can lead to a better
 * prerender with cache components when PPR is on. There is no difference when PPR is off. When PPR is on the second component
 * can finish rendering before the prerender completes and so we can produce a static shell where the Fallback closest
 * to Cookies access is read

nelValue()}

    </Suspense>
  )
}

async function Component() {
  const cookie = (await cookies()).get('x-sentinel')
  if (cookie && cookie.value) {
    return (

        cookie {cookie.value}

 */
export default async function Page() {
  return (
    <Suspense fallback="loading...">
      <Suspense fallback="inner loading...">
        <Component />
      </Suspense>
      <ComponentTwo />
      <div id="page">{getSenti


'react'
import { cookies } from 'next/headers'

import { getSentinelValue } from '../../../getSentinelValue'

export default async function Page() {
  const pendingCookies = cookies()
  return (

Deep Cookie Reader

        This component was passed the cookies promise returned by `cookies()`.
        It is rendered inside a Suspense boundary and it takes a second to
        resolve so when rendering the page you should see the Suspense fallback
        content before re

spense boundary would also be
        blocked for over one second

      <Suspense
        fallback={
          <>

loading cookie data...

{getSentinelValue()}

          </>
        }
      >

vealing the cookie value even though cookies was called
        at the page root.
      </p>
      <p>
        If cacheComponents is turned off the `cookies()` call would trigger a
        dynamic point at the callsite and the su


aders={headers} expression={expression} />
      <Delete headers={headers} expression={expression} />
      <Get headers={headers} expression={expression} />
      <Has headers={headers} expression={expression} />
      <SetExercise headers={headers} expression={expression} />
      <GetSetCookie headers={headers} expression={expression} />
      <ForEach headers={headers} expression={expression} />
      <Keys headers={headers} expression={expression} />
      <Values
        headers={headers}
        expr

on={expression} />
    </>
  )
}

function Append({
  headers,
  expression,
}: {
  headers: Headers
  expression: string
}) {
  let result: string
  try {
    headers.append('x-sentinel', ' world')
    result = 'no error'
  } catch (e) {
    result = e.me

ession={expression}
        xSentinelValues={xSentinelValues}
      />
      <Entries headers={headers} expression={expression} />
      <ForOf headers={headers} expression={expression} />
      <Spread headers={headers} expressi


om '../../getSentinelValue'
import { AllComponents } from './components'

export default async function Page() {
  const allHeaders = await headers()

  const xSentinelValues = new Set<string>()
  for (let [headerName, headerValue] of allHeaders.entries()) {
    if (headerName.startsWith('x-sentinel')) {
      xSentinelValues.add(headerValue)
    }
  }

  return (
    <>
      <p>
        This page will exercise a number of APIs on the headers() instance by
        first awaiting it. This is the correct way

y exists to ensure the behavior between sync and async
        access is consistent for the time where you are permitted to do either
      </p>
      <AllComponents
        headers={allHeaders}
        xSentinelValues={xSentinelValues}
        expression=

 to consume headers() and this
        test partiall


 { Suspense } from 'react'
import { headers } from 'next/headers'

import { getSentinelValue } from '../../getSentinelValue'
/**
 * This test case is constructed to demonstrate how using the async form of cookies can lead to a better
 * prerender with cache components when PPR is on. There is no difference when PPR is off. When PPR is on the second component
 * can finish rendering before the prerender completes and so we can produce a static shell where the Fallback closest
 * to Cookies access is read
 */

Value()}

    </Suspense>
  )
}

async function Component() {
  const hasHeader = (await headers()).has('x-sentinel')
  if (hasHeader) {
    return (

        header{' '}
        {(await headers()).get('x-sentinel')}</

export default async function Page() {
  return (
    <Suspense fallback="loading...">
      <Suspense fallback="inner loading...">
        <Component />
      </Suspense>
      <ComponentTwo />
      <div id="page">{getSentinel


'react'
import { headers } from 'next/headers'

import { getSentinelValue } from '../../../getSentinelValue'

export default async function Page() {
  const pendingHeaders = headers()
  return (

Deep Header Reader

        This component was passed the headers promise returned by `headers()`.
        It is rendered inside a Suspense boundary and it takes a second to
        resolve so when rendering the page you should see the Suspense fallback
        content before re

spense boundary would also be
        blocked for over one second

      <Suspense
        fallback={
          <>

loading header data...

{getSentinelValue()}

          </>
        }
      >

vealing the header value even though headers was called
        at the page root.
      </p>
      <p>
        If cacheComponents is turned off the `headers()` call would trigger a
        dynamic point at the callsite and the su


export default async function Page() {
  const first = await getRandomIntBetween(1)
  const second = await getRandomIntBetween(2)
  return (

[first] require('node:crypto').randomInt(min, max)

{first}

[second] require('node:crypto').randomInt(min, max)

{second}

        <SentinelValue />

  )
}

async function getRandomIntBetween(nonce: nu

 want to exercise the case where the function arguments are length 3
    // but the third arg is still not a callback so it runs sync
    return crypto.randomInt(0, 2 ** 48 - 1, undefined) as undefined as number
  } else {
    return crypto.randomInt(0, 2

mber) {
  'use cache'
  if (nonce === 2) {
    // We


xport default async function Page() {
  const first = await getGeneratedKeyPair(1)
  const second = await getGeneratedKeyPair(2)
  return (

          [first] require('node:crypto').generateKeyPairSync(type, options)

{first.publicKey}

          [second] require('node:crypto').generateKeyPairSync(type, options)

{second.publicKey}

        <Sentin

ction getGeneratedKeyPair(_nonce: number) {
  'use cache'
  return crypto.generateKeyPairSync('rsa', {
    modulusLength: 512,
    publicKeyEncoding: {
      type: 'spki',
      format: 'pem',
    },
    privateKeyEncoding: {
      type: 'pkcs8',
      for

elValue />
      </span>
    </div>
  )
}

async fun


mport { nextTestSetup } from 'e2e-utils'

describe('app-dir - server components externals', () => {
  const { next, isTurbopack, skipped } = nextTestSetup({
    // This test is skipped when deployed because it relies on manually patched `node_modules`
    skipDeployment: true,
    files: __dirname,
  })

  if (skipped) return

  it('should have externals for those in config.serverExternalPackages', async () => {
    const $ = await next.render$('/')

    const text = $('#directory').text()
    const subpath

  )
  })

  it('uses externals for predefined list in server-external-packages.json', async () => {
    const $ = await next.render$('/predefined')

    const text = $('#directory').text()
    expect(text).toBe(path.join(next.testDir, 'node_modules', 'sqli

 = $('#subdirectory').text()
    expect(text).toBe(
      path.join(next.testDir, 'node_modules', 'external-package')
    )
    expect(subpath).toBe(
      path.join(next.testDir, 'node_modules', 'external-package', 'subpath')


Start, isNextDev } = nextTestSetup({
    files: path.join(__dirname, 'bundled'),
  })

  if (isNextStart) {
    it('does not cause any routes to become (partially) dynamic', async () => {
      const prerenderManifest = JSON.parse(
        await next.readFile('.next/prerender-manifest.json')
      )

      // For the purpose of this test we don't consider an incomplete shell.
      const prerenderedRoutes = Object.keys(prerenderManifest.routes)
        .sort()
        .filter((route) => {
          const fi

      if ('code' in err && err.code === 'ENOENT') {
              // the route was prerendered, but we didn't find a HTML file for it.
              // this means it must be a GET route handler, not a page
              return true
            } else {

lename = route.replace(/^\//, '').replace(/^$/, 'index')
          try {
            return next
              .readFileSync(`.next/server/app/${filename}.html`)
              .endsWith('</html>')
          } catch (err) {


xtTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should handle ssr: false in pages when appDir is enabled', async () => {
    const $ = await next.render$('/legacy/no-ssr')
    expect($.html()).not.toContain('navigator')

    const browser = await next.browser('/legacy/no-ssr')
    expect(await browser.waitForElementByCss('#pure-client').text()).toContain(
      'navigator'
    )
  })

  it('should handle next/dynamic in SSR correctly', async () => {

y
    expect(serverContent).toContain('next-dynamic lazy')
    // should support `dynamic` in both server and client components
    expect(serverContent).toContain('next-dynamic dynamic on server')
    expect(serverContent).toContain('next-dynamic dynamic

    const $ = await next.render$('/dynamic')
    // filter out the script
    const selector = 'body div'
    const serverContent = $(selector).text()
    // should load chunks generated via async import correctly with React.laz


Dev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should partially prerender pages that use headers', async () => {
    let $ = await next.render$('/headers/static-behavior', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#x-sentinel').text()).toBe('hello')
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect

 () => {
    const $ = await next.render$('/headers/static-behavior/pass-deeply')
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#fallback').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runt

($('#page').text()).toBe('at buildtime')
      expect($('#x-sentinel').text()).toBe('hello')
    }
  })

  it('should be able to pass headers as a promise to another component and trigger an intermediate Suspense boundary', async


      $ = await next.render$('/params/semantics/one/run/layout-access/server')
      if (isNextDev) {
        expect($('#layout').text()).toBe('at runtime')

        expect($('#lowcard').text()).toBe('at runtime')
        expect($('#highcard').text()).toBe('at runtime')
        expect($('#page').text()).toBe('at runtime')
        expect($('#param-lowcard').text()).toBe('one')
        expect($('#param-highcard').text()).toBe('run')
        expect(getLines('Route "/params')).toEqual([])
      } else {

 highcard children')
        expect($('#page').text()).toBe('at runtime')
        expect($('#param-lowcard').text()).toBe('one')
        expect($('#param-highcard').text()).toBe('run')
        expect(getLines('Route "/params')).toEqual([])
      }

      $

   expect($('#layout').text()).toBe('at buildtime')
        expect($('#lowcard').text()).toBe('at buildtime')
        expect($('#highcard').text()).toBe('at buildtime')
        expect($('#highcard-fallback').text()).toBe('loading


{
    return
  }

  let cliIndex = 0
  beforeEach(() => {
    cliIndex = next.cliOutput.length
  })
  function getLines(containing: string): Array<string> {
    const warnings = next.cliOutput
      .slice(cliIndex)
      .split('\n')
      .filter((l) => l.includes(containing))

    cliIndex = next.cliOutput.length
    return warnings
  }

  it('should not prerender GET route handlers that use dynamic APIs', async () => {
    let str = await next.render('/routes/dynamic-cookies', {})
    let json = JSON.pa

    expect(json.type).toEqual('headers')

    str = await next.render('/routes/dynamic-stream', {})
    json = JSON.parse(str)

    expect(json.value).toEqual('at runtime')
    expect(json.message).toEqual('dynamic stream')

    str = await next.render('/r

rse(str)

    expect(json.value).toEqual('at runtime')
    expect(json.type).toEqual('cookies')

    str = await next.render('/routes/dynamic-headers', {})
    json = JSON.parse(str)

    expect(json.value).toEqual('at runtime')


from 'e2e-utils'
import { waitForNoRedbox, retry } from 'next-test-utils'

describe('cache-components', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  it('should not fail decoding server action arguments', async () => {
    const browser = await next.browser('/server-action')
    expect(await browser.elementByCss('p').text()).toBe('initial')
    await browser.elementByCss('button').click()

    await retry(async () => {
      expect(await browser.elementByCss('p').text()

er.elementByCss('p').text()).toBe('initial')
    if (isNextDev) {
      await waitForNoRedbox(browser)
    }

    await browser.elementByCss('button').click()
    await retry(async () => {
      expect(await browser.elementByCss('p').text()).toBe(

).toBe('result')
    })
  })

  it('should not have cache components errors when encoding bound args for inline server actions', async () => {
    const browser = await next.browser('/server-action-inline')
    expect(await brows


from 'e2e-utils'

describe('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should not have route specific errors', async () => {
    expect(next.cliOutput).not.toMatch('Error: Route "/')
    expect(next.cliOutput).not.toMatch('Error occurred prerendering page')
  })

  it('should prerender pages with cached `crypto.getRandomValues(...)` calls', async () => {
    let $ = await next.

ond').text())
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect($('#page').text()).toBe('at buildtime')
      expect($('#first').text()).not.toEqual($('#second').text())
    }
  })

  it('should prerender pages with cached `c

render$('/web-crypto/get-random-values/cached', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#first').text()).not.toEqual($('#sec


t } from 'next/server'

import { getSentinelValue } from '../../getSentinelValue'

export async function GET(request: NextRequest) {
  const fetcheda = await fetchRandomCached('a')
  const fetchedb = await fetchRandomUncached('b')
  return new Response(
    JSON.stringify({
      value: getSentinelValue(),
      random1: fetcheda,
      random2: fetchedb,
    })
  )
}

const fetchRandomCached = async (entropy: string) => {
  const response = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/ra

ropy,
    { cache: 'force-cache' }
  )
  return response.text()
}

const fetchRandomUncached = async (entropy: string) => {
  const response = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b=' + entropy
  )
  return response.text()


ort { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders two components. Both call a simulated IO function
        wrapped in `"use cache"`.

Niether component is wrapped in a Suspense boundary

        With PPR this page should be entirely static because all IO is cached.

Without PPR this page should be static because all IO is cached.

      <Componen

nentTwo() {
  return (
    <>

message 2: {await getCachedMessage('hello cached fast', 0)}

message 3: {await getCachedMessage('hello cached slow', 20)}

    </>
  )
}

async function getCachedMessage(echo, delay) {
  'use ca

tOne />
      <ComponentTwo />

{getSentinelValue()}

    </>
  )
}

async function ComponentOne() {
  return
message 1: {await getCachedMessage('hello cached fast', 2)}

}

async function Compo


spense, use } from 'react'

import { getSentinelValue } from '../../getSentinelValue'

export default function Page({ searchParams }: { searchParams: Promise<any> }) {
  return (
    <>

        This page reads `searchParams.foo` in a client component context with a
        parent Suspense boundary.

        With PPR this page can be partially static because the dynamic API usage
        is inside a suspense boundary.

        Without PPR this page is fully d

llback>loading too...</Fallback>}>
        <ComponentTwo />
      </Suspense>

{getSentinelValue()}

    </>
  )
}

function ComponentOne({ searchParams }: { searchParams: Promise<any> }) {
  let sentinelSearch
  const sp = use(sea

ynamic because a dynamic API was used.

      <Suspense fallback={
loading...
}>
        <ComponentOne searchParams={searchParams} />
      </Suspense>
      <Suspense fallback={<Fa


pense } from 'react'

import { unstable_noStore as noStore } from 'next/cache'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  // We wait for metadata to finish before rendering this component which will trigger
  // a synchronous abort
  await new Promise((r) => process.nextTick(r))
  return (
    <>

        This page calls `unstable_noStore()` in a child component with a parent
        Suspense boundary.

        With PPR t

fallback="loading...">
        <ComponentThatCallsNoStore />
      </Suspense>
      <Suspense fallback="loading too...">
        <OtherComponent />

{getSentinelValue()}

      </Suspense>

{getSentinelValue

his page can be partially static because the dynamic API usage
        is inside a suspense boundary.
      </p>
      <p>
        Without PPR this page is fully dynamic because a dynamic API was used.
      </p>
      <Suspense


pense } from 'react'

import { headers } from 'next/headers'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  return (
    <>

This page calls `headers()` in a child component.

        With PPR this page can be partially static because the dynamic API usage
        is inside a suspense boundary.

        Without PPR this page is fully dynamic because a dynamic API was used.

      <Suspense fallback="load

SentinelValue()}</div>
    </>
  )
}

async function ComponentThatReadsHeaders() {
  let sentinelHeader
  try {
    sentinelHeader = (await headers()).get('x-sentinel')
    if (!sentinelHeader) {
      sentinelHeader = '~not-found~'
    }
  } catch (e) {

ing...">
        <ComponentThatReadsHeaders />
      </Suspense>
      <Suspense fallback="loading too...">
        <OtherComponent />

{getSentinelValue()}

      </Suspense>

{get


'react'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page fetches using cache inside one component and without cache
        inside another component.

Each component is wrapped in a Suspense boundary.

        With PPR this page should have a static shell that includes the cached
        fetch result and a loading state for the boundary surrounding the
        uncached fe

{getSentinelValue()}

      </Suspense>

{getSentinelValue()}

    </>
  )
}

async function ComponentOne() {
  return
message 1: {await fetchRandomCached('a')}

}

async function ComponentTwo()

tch result.

Without PPR this page should be dynamic.

      <Suspense fallback="loading...">
        <ComponentOne />
      </Suspense>
      <Suspense fallback="loading too...">
        <ComponentTwo />


tResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function proxy(request: NextRequest) {
  const pathname = request.nextUrl.pathname

  // Rewrite /rewritten/[slug] to /actual/[slug]
  // This creates a mismatch between URL and route structure that should
  // mark the route as having a dynamic rewrite.
  if (pathname.startsWith('/rewritten/')) {
    const slug = pathname.replace('/rewritten/', '')
    const url = request.nextUrl.clone()
    url.pathname = `/actual/${slug

destination pages are fully static, so if we incorrectly use a cached
  // pattern, we'd show the wrong content.
  if (pathname === '/search-rewrite') {
    const v = request.nextUrl.searchParams.get('v')
    const url = request.nextUrl.clone()
    url.pat

}`
    return NextResponse.rewrite(url)
  }

  // Rewrite /search-rewrite?v=alpha to /rewrite-alpha
  // Rewrite /search-rewrite?v=beta to /rewrite-beta
  // This tests that search param rewrites are correctly detected.
  // The


eState } from 'react'
import { useParams, usePathname, useSearchParams } from 'next/navigation'

type RenderedRouteEntry = {
  url: string
  params: Record<string, string | string[] | undefined>
}

/**
 * Accumulates every [url, params] pair rendered throughout the app's lifetime.
 * Uses the setState-in-render pattern to capture changes synchronously,
 * before React commits. This catches any intermediate/wrong route state that
 * might flash briefly due to incorrect route prediction.
 */
export function R

const entry: RenderedRouteEntry = { url, params }
  const entryJson = JSON.stringify(entry)

  const [history, setHistory] = useState<string[]>([entryJson])
  const [prevEntry, setPrevEntry] = useState(entryJson)

  // setState-in-render pattern: update st

enderedRouteHistory() {
  const params = useParams()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  const search = searchParams.toString()
  const url = search ? `${pathname}?${search}` : pathname


n (

Optimistic Routing Test

        This test verifies that route prediction works correctly. When a route
        pattern is learned from one URL, navigating to a different URL with the
        same pattern should show the loading state instantly.

Basic Dynamic Route

          <LinkAccordion href="/blog/post-1">Blog Post 1</LinkAccordion>

          <LinkAccordion href="/blog/post-2" prefetch={fa

cordion>

Nested Dynamic Route

          <LinkAccordion href="/products/electronics/phone-1">
            Electronics - Phone 1
          </LinkAccordion>

lse}>
            Blog Post 2 (prefetch disabled)
          </LinkAccordion>
        </li>
        <li>
          <LinkAccordion href="/blog/post-3" prefetch={false}>
            Blog Post 3 (prefetch disabled)
          </LinkAc


tTestSetup } from 'e2e-utils'

describe('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should partially prerender pages that use connection', async () => {
    let $ = await next.render$('/connection/static-behavior/boundary', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#foo').text

on as a promise to another component and trigger an intermediate Suspense boundary', async () => {
    const $ = await next.render$('/connection/static-behavior/pass-deeply')
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      /

()).toBe('foo')
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect($('#page').text()).toBe('at buildtime')
      expect($('#foo').text()).toBe('foo')
    }
  })

  it('should be able to pass connecti


from 'e2e-utils'

describe('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  let cliIndex = 0
  beforeEach(() => {
    cliIndex = next.cliOutput.length
  })
  function getLines(containing: string): Array<string> {
    const warnings = next.cliOutput
      .slice(cliIndex)
      .split('\n')
      .filter((l) => l.includes(containing))

    cliIndex = next.cliOutput.length
    return warn

.text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#draft-mode').text()).toBe('false')
      expect(getLines('Route "/draftmode')).toEqual([])
    } else {
      expect($('#layout').text()).toBe('at buildtime')

ings
  }

  it('should fully prerender pages that use draftMode', async () => {
    expect(getLines('Route "/draftmode')).toEqual([])
    let $ = await next.render$('/draftmode', {})
    if (isNextDev) {
      expect($('#layout')


{
    return
  }

  it('should not have route specific errors', async () => {
    expect(next.cliOutput).not.toMatch('Error: Route "/')
    expect(next.cliOutput).not.toMatch('Error occurred prerendering page')
  })

  it("should prerender pages with cached `require('node:crypto').getRandomValues(...)` calls", async () => {
    let $ = await next.render$('/node-crypto/get-random-values/cached', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe

xt()).not.toEqual($('#second').text())
    }
  })

  it("should prerender pages with cached `require('node:crypto').randomUUID()` calls", async () => {
    let $ = await next.render$('/node-crypto/random-uuid/cached', {})
    if (isNextDev) {
      expect(

('at runtime')
      expect($('#first').text()).not.toEqual($('#second').text())
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect($('#page').text()).toBe('at buildtime')
      expect($('#first').te


('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should partially prerender pages that await searchParams in a server component', async () => {
    let $ = await next.render$('/search/server/await?sentinel=hello')
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#value').text()).toBe('hello')
      expect($('#page').text()).toBe('at runtime'

toBe('hello')
      expect($('#page').text()).toBe('at runtime')
    }
  })

  it('should partially prerender pages that `use` searchParams in a server component', async () => {
    let $ = await next.render$('/search/server/use?sentinel=hello')
    if (is

)
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect($('main').text()).toContain('inner loading...')
      expect($('main').text()).not.toContain('outer loading...')
      expect($('#value').text()).


   await next.browser('/cases/not-found')
      const cliOutput = next.cliOutput.slice(cliOutputLength)
      expect(cliOutput).not.toMatch('Error: NEXT_HTTP_ERROR_FALLBACK;404')
      expect(cliOutput).not.toMatch('unhandledRejection')
    })
  } else {
    it('should not warn about potential memory leak for even listeners on AbortSignal', async () => {
      expect(next.cliOutput).not.toMatch('MaxListenersExceededWarning')
    })
  }

  it('should prerender fully static pages', async () => {
    let $ = a

')
      expect($('#page').text()).toBe('at buildtime')
    }

    $ = await next.render$('/cases/static_async', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
    } else {

wait next.render$('/cases/static', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
    } else {
      expect($('#layout').text()).toBe('at buildtime


ache'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page({
  searchParams: _unused,
}: {
  searchParams: Promise<Record<string, string | string[] | undefined>>
}) {
  return (
    <>

        This page renders two components. Both call a simulated IO function. The
        whole page is wrapped in "use cache".

Niether component is wrapped in a Suspense boundary

        With PPR this page should be entirely static because a

ComponentOne() {
  return
message 1: {await getMessage('hello cached fast', 2)}

}

async function ComponentTwo() {
  return (
    <>

message 2: {await getMessage('hello cached fast', 0)}

message 3: {await getMessage(

ll IO is cached.

Without PPR this page should be static because all IO is cached.

      <ComponentOne />
      <ComponentTwo />

{getSentinelValue()}

    </>
  )
}

async function


{ unstable_cache as cache } from 'next/cache'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders two components. Both call a simulated IO function
        wrapped in `unstable_cache`.

Niether component is wrapped in a Suspense boundary

        With PPR this page should be entirely static because all IO is cached.

Without PPR this page should b

'hello cached fast', 2)}

}

async function ComponentTwo() {
  return (
    <>

message 2: {await getCachedMessage('hello cached fast', 0)}

message 3: {await getCachedMessage('hello cached slow', 20)}

    </>
  )
}

as

e static because all IO is cached.

      <ComponentOne />
      <ComponentTwo />

{getSentinelValue()}

    </>
  )
}

async function ComponentOne() {
  return
message 1: {await getCachedMessage(


ort { getSentinelValue } from '../../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders two components each performing one or more cached
        fetches. There is no uncached IO in this example

Niether component is wrapped in a Suspense boundary.

With PPR this page should be entirely static.

Without PPR this page should be static.

      <ComponentOne />
      <ComponentTwo />
      <div

etchRandomCached('b')}

message 2: {await fetchRandomCached('c')}

    </>
  )
}

const fetchRandomCached = async (entropy: string) => {
  const response = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b=' + en

id="page">{getSentinelValue()}

    </>
  )
}

async function ComponentOne() {
  return
message 1: {await fetchRandomCached('a')}

}

async function ComponentTwo() {
  return (
    <>

message 2: {await f


 client'

import Link, { type LinkProps } from 'next/link'
import { useState } from 'react'

export function LinkAccordion({
  href,
  children,
  prefetch,
}: {
  href: LinkProps['href']
  children: React.ReactNode
  prefetch?: LinkProps['prefetch']
}) {
  // This component controls when prefetching happens by hiding the Link until
  // the checkbox is clicked. Prefetch triggers when the Link enters the viewport.
  const [isVisible, setIsVisible] = useState(false)
  return (
    <>
      <input
        typ

hange={() => setIsVisible(!isVisible)}
        data-link-accordion={href}
      />
      {isVisible ? (
        <Link href={href} prefetch={prefetch}>
          {children}
        </Link>
      ) : (
        `${children} (link is hidden)`
      )}
    </>

e="checkbox"
        checked={isVisible}
        onC


from 'e2e-utils'

describe('client-reference-side-effects', () => {
  const { next, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  it('side effect behavior when only importing', async () => {
    const browser = await next.browser('/imported')

    expect(await browser.elementByCss('body').text()).toContain('Server')

    let client = await browser.eval('window.client')
    let client_sideeffect_reexport = await browser.eval(
      'window.client_sideeffect_reexport'

ffect_reexport).toBeUndefined()
    expect(client_sideeffect_only).toBeUndefined()
  })

  it('side effect behavior when rendering', async () => {
    const browser = await next.browser('/rendered')

    const body = await browser.elementByCss('body').text

    )
    let client_sideeffect_only = await browser.eval(
      'window.client_sideeffect_only'
    )

    // No client references are rendered, so nothing is executed.
    expect(client).toBeUndefined()
    expect(client_sidee


Dev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should partially prerender pages that use cookies', async () => {
    let $ = await next.render$('/cookies/static-behavior', {})
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
      expect($('#x-sentinel').text()).toBe('hello')
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect

 () => {
    const $ = await next.render$('/cookies/static-behavior/pass-deeply')
    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#fallback').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runt

($('#page').text()).toBe('at buildtime')
      expect($('#x-sentinel').text()).toBe('hello')
    }
  })

  it('should be able to pass cookies as a promise to another component and trigger an intermediate Suspense boundary', async


 } from 'next/server'
import { NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  if (
    request.nextUrl.pathname.startsWith('/headers/') ||
    request.nextUrl.pathname.endsWith('dynamic_api_headers')
  ) {
    // Clone the request headers and set a new header `x-hello-from-middleware1`
    const requestHeaders = new Headers(request.headers)
    requestHeaders.set('x-sentinel', 'hello')
    requestHeaders.set('x-sentinel-path', request.nextUrl.pathname)
    requestHea

nst response = NextResponse.next()

  if (
    request.nextUrl.pathname.startsWith('/cookies/') ||
    request.nextUrl.pathname.endsWith('dynamic_api_cookies')
  ) {
    response.cookies.set('x-sentinel', 'hello', {
      httpOnly: true,
      sameSite: 's

ders.set(
      'x-sentinel-rand',
      ((Math.random() * 100000) | 0).toString(16)
    )

    return NextResponse.next({
      request: {
        // New request headers
        headers: requestHeaders,
      },
    })
  }

  co


./../getSentinelValue'

export default async function Page() {
  await 1
  return (
    <>

        This page renders a deep tree of deeply async components that still
        resolve within microtasks.

With PPR this page should be entirely static.

Without PPR this page should be static.

      <Foo>
        <Bar>1</Bar>
        <Bar>2</Bar>
        <Bar>
          <Foo>
            <Bar>a</Bar>
            <Bar>b</Bar>
            <Bar>c</Bar>
          </Foo>

ar>
      </Foo>

{getSentinelValue()}

    </>
  )
}

async function Foo({ children }) {
  await 1
  await 1
  return
Foo: {children}

}

async function Bar({ children }) {
  let length = 7
  for (let i = 0; i < length; i+


xport default async function Page() {
  await 1
  return (
    <>

        This page renders a component that requires async IO. It is simulated
        using setTimeout(f, 1000).

        This component simulated IO component is rendered inside a Suspense
        boundary

        With PPR this page should be partially static, showing the fallback of
        the Suspense boundary surrounding our simulated IO.

Without PPR this page should

">
        <ComponentWithIO />

{getSentinelValue()}

      </Suspense>

{getSentinelValue()}

    </>
  )
}

async function ComponentWithIO() {
  await new Promise<void>((r) => setTimeout(r, 1000))
  re

be dynamic.</p>
      <Suspense fallback="loading...


mport { Suspense } from 'react'
import { connection } from 'next/server'
import { LinkAccordion } from '../../components/link-accordion'

async function HubContent() {
  await connection()
  return
Hub

}

// Hub page used by rewrite-detection tests as a navigation target instead of
// `browser.back()`. Each visit lands on a fresh page where all accordions
// start closed, so previously-revealed Links can't be re-mounted by BFCache
// and trigger uncontrolled prefetches outside an

 prefetch={false}>
            Rewritten Second
          </LinkAccordion>
        </li>
        <li>
          <LinkAccordion href="/search-rewrite?v=beta" prefetch={false}>
            Search Rewrite Beta (v=beta → content=beta)
          </LinkAccordion

y `act` scope.
export default function HubPage() {
  return (

      <Suspense fallback="Loading hub...">
        <HubContent />
      </Suspense>

          <LinkAccordion href="/rewritten/second"


'react'

import { getSentinelValue } from '../../getSentinelValue'

export default async function Page({
  searchParams,
}: {
  searchParams: Promise<any>
}) {
  return (
    <>

        This page reads `searchParams.foo` in a client component context with a
        parent Suspense boundary.

        With PPR this page can be partially static because the dynamic API usage
        is inside a suspense boundary.

        Without PPR this page is fully dynamic b

uspense>

{getSentinelValue()}

    </>
  )
}

async function ComponentOne({ searchParams }: { searchParams: Promise<any> }) {
  let sentinelSearch
  const sp = await searchParams
  try {
    if (sp.sentinel) {
      sentinelSearch

ecause a dynamic API was used.
      </p>
      <Suspense fallback="loading...">
        <ComponentOne searchParams={searchParams} />
      </Suspense>
      <Suspense fallback="loading too...">
        <ComponentTwo />
      </S


 from 'e2e-utils'

describe('cache-components', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should not have route specific errors', async () => {
    expect(next.cliOutput).not.toMatch('Error: Route "/')
    expect(next.cliOutput).not.toMatch('Error occurred prerendering page')
  })

  it('should prerender pages with cached Math.random() calls', async () => {
    let $ = await next.render$('/rando

    if (isNextDev) {
      expect($('#layout').text()).toBe('at runtime')
      expect($('#page').text()).toBe('at runtime')
    } else {
      expect($('#layout').text()).toBe('at buildtime')
      expect($('#page').text()).toBe('at buildtime')
    }
  }


n }: { children: React.ReactNode }) {
  return (

                <Link id="go-to-index" href="/">
                  Index
                </Link>

                <Link id="go-to-dynamic" href="/dynamic/foo">
                  Dynamic page
                </Link>

                <Link id="go-to-404" href="/does-not-exist">
                  No

/li>
              <li>
                <Link id="go-to-dynamic-404" href="/dynamic/404">
                  Dynamic not found page
                </Link>
              </li>
            </ul>
          </div>
        </nav>
        {children}
      </body

t found page
                </Link>
              <


etup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  function getLogFilePath(): string {
    const logFilePath = path.join(
      next.testDir,
      next.distDir,
      'logs',
      'next-development.log'
    )
    return logFilePath
  }

  function hasLogFile(): boolean {
    const logPath = getLogFilePath()

    return fs.existsSync(logPath)
  }

  function readLogFile(): string {
    const logPath = getLogFilePath()
    if (fs.existsSync(logPath)) {
      retu

t DevTools"
        .split('\n')
        .filter((line) => {
          if (!line.trim()) return false
          // Parse JSON and filter out the noise logs
          try {
            const log = JSON.parse(line)
            if (
              /Download th

rn fs.readFileSync(logPath, 'utf8')
    }
    return ''
  }

  let previousLogContent = ''

  function normalizeLogContent(content: string): string {
    return (
      content
        // Strip lines containing "Download the Reac


rom 'internal-pkg'
import { runInternalSourceMapped } from 'internal-pkg/sourcemapped'
import { runInternalIgnored } from 'internal-pkg/ignored'
import { runExternal } from 'external-pkg'
import { runExternalSourceMapped } from 'external-pkg/sourcemapped'

function logError() {
  const error = new Error('rsc-error-log-ignore-listed')
  console.error(error)
}

export default async function Page() {
  runInternal(function runWithInternal() {
    runInternalSourceMapped(function runWithInternalSourceMapped() {

rnal(function runWithExternal() {
        runExternalSourceMapped(function runWithExternalSourceMapped() {
          runInternalIgnored(function runWithInternalIgnored() {
            logError()
          })
        })
      })
    })
  })
  return null
}


source: '/config-rewrite-before',
        destination: '/config-rewrite-after',
      },
    ]
  },
  async redirects() {
    return [
      {
        source: '/config-redirect-before',
        destination: '/config-redirect-after',
        permanent: true,
      },
      {
        source: '/config-redirect-catchall-before/:path*',
        destination: '/config-redirect-catchall-after/:path*',
        permanent: true,
      },
      {
        source: '/config-redirect-itms-apps-slashes',
        destination

/id497799835?l=en-GB&mt=12',
        permanent: true,
      },
      {
        source: '/config-redirect-itms-apps-no-slashes',
        destination:
          'itms-apps:apps.apple.com/de/app/xcode/id497799835?l=en-GB&mt=12',
        permanent: true,

:
          'itms-apps://apps.apple.com/de/app/xcode


 = nextTestSetup({
    files: __dirname,
    dependencies: {
      typescript: 'latest',
      '@types/react': 'latest',
      '@types/node': 'latest',
    },
  })

  // TODO: investigate test failures on deploy
  if ((global as any).isNextDeploy) {
    it('should skip for deploy', () => {})
    return
  }
  /**
   * All test will use a link/button to navigate to '/*-before' which should be redirected by correct redirect/rewrite to '/*-after'
   */
  describe.each(['link', 'button'])('navigation using %s',

aitForElementByCss('.page_middleware-rewrite-after')
      const url = new URL(await browser.url())
      expect(url.pathname).toEndWith('-before')
    })

    it('should redirect from middleware correctly', async () => {
      const browser = await next.b

(testType) => {
    it('should rewrite from middleware correctly', async () => {
      const browser = await next.browser('/')
      await browser
        .elementById(`${testType}-middleware-rewrite`)
        .click()
        .w


import Link from 'next/link'
import { useRouter } from 'next/navigation'

const Test = ({ page, href }: { page: string; href?: string }) => {
  const router = useRouter()
  href ??= `/${page}-before`

  return (
    <>
      <Link id={`link-${page}`} href={href}>
        Link to /{page}-before
      </Link>
      <button id={`button-${page}`} onClick={() => router.push(href)}>
        Button to /{page}-before
      </button>
    </>
  )
}

export default function Page() {
  return (
    <>
      <Test pag

rewrite" />
      <Test page="middleware-redirect" />
      <Test page="config-rewrite" />
      <Test page="config-redirect" />
      <Test
        page="config-redirect-catchall"
        href="/config-redirect-catchall-before/thing"
      />
    </>
  )


from 'e2e-utils'
import path from 'path'

describe('ppr-missing-root-params (single)', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: path.join(__dirname, 'fixtures/single'),
    skipStart: true,
    skipDeployment: true,
  })
  if (skipped) return

  beforeAll(async () => {
    try {
      await next.start()
    } catch {}
  })

  it('should result in a build error', async () => {
    if (isNextDev) {
      await next.fetch('/en')
    }

    expect(next.cliOutput).toContain(

 skipped } = nextTestSetup({
    files: path.join(__dirname, 'fixtures/multiple'),
    skipStart: true,
    skipDeployment: true,
  })
  if (skipped) return

  beforeAll(async () => {
    try {
      await next.start()
    } catch {}
  })

  it('should res

  `Error: A required root parameter (lang) was not provided in generateStaticParams for /[lang], please provide at least one value.`
    )
  })
})

describe('ppr-missing-root-params (multiple)', () => {
  const { next, isNextDev,


('app dir - layout params', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  describe('basic params', () => {
    it('check layout without params get no params', async () => {
      const $ = await next.render$('/base/something/another')
      const ids = ['#root-layout', '#lvl1-layout']
      ids.forEach((divId) => {
        const params = $(`${divId} > div`)
        expect(params.length).toBe(0)
      })
    })

    it("check layout renders just it's params", async () => {
      co

c () => {
      const $ = await next.render$('/base/something/another')

      expect($('#lvl3-layout > div').length).toBe(2)
      expect($('#lvl3-param1').text()).toBe('"something"')
      expect($('#lvl3-param2').text()).toBe('"another"')
    })
  })

nst $ = await next.render$('/base/something/another')

      expect($('#lvl2-layout > div').length).toBe(1)
      expect($('#lvl2-param1').text()).toBe('"something"')
    })

    it('check topmost layout renders all params', asyn


e('/without-io/world')

      expect(await browser.elementById('slug').text()).toBe('Hello /world')
      const headers = response.headers()

      // If we didn't use the fallback shell, then we didn't postpone the
      // response, and therefore shouldn't have sent the postponed header.
      expect(headers['x-nextjs-postponed']).not.toBe('1')
    })
  })

  describe('with cached IO', () => {
    describe('with generateStaticParams', () => {
      describe('and the page wrapped in Suspense', () => {

ched-io/with-static-params/with-suspense/params-in-page/bar'
            )

            const lastModified = await browser
              .elementById('last-modified')
              .text()
            expect(lastModified).toInclude('Page /bar')

    describe('and the params accessed in the cached page', () => {
          it('resumes a postponed fallback shell', async () => {
            const { browser, response } = await next.browserWithResponse(
              '/with-ca


check } from 'next-test-utils'

describe('app-invalid-revalidate', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should error properly for invalid revalidate at layout', async () => {
    await next.stop().catch(() => {})
    const origText = await next.readFile('app/layout.tsx')

    try {
      await next.patchFile(
        'app/layout.tsx',
        origText.replace('// export

" on "\/", must be a non-negative number or false/)
    } finally {
      await next.patchFile('app/layout.tsx', origText)
    }
  })

  it('should error properly for invalid revalidate at page', async () => {
    await next.stop().catch(() => {})
    cons

', 'export')
      )
      await next.start().catch(() => {})

      await check(async () => {
        if (isNextDev) {
          await next.fetch('/')
        }
        return next.cliOutput
      }, /Invalid revalidate value "1


s/promises')

/**
 * A persistent cache handler that does NOT drop entries at revalidate time.
 * This allows the framework's SWR code path to trigger, unlike the default
 * in-memory handler which expires entries at revalidate time.
 */

/** @type {Map<string, import('next/dist/server/lib/cache-handlers/types').CacheEntry>} */
const store = new Map()

/** @type {Map<string, Promise<void>>} */
const pendingSets = new Map()

/**
 * @type {import('next/dist/server/lib/cache-handlers/types').CacheHandler}
 */

che.
    await setTimeout(200)
    const entry = store.get(cacheKey)
    if (!entry) {
      console.log('PersistentCacheHandler::get', cacheKey, softTags, '-> miss')
      return undefined
    }

    const [returnStream, savedStream] = entry.value.tee()

const cacheHandler = {
  async get(cacheKey, softTags) {
    const pendingPromise = pendingSets.get(cacheKey)
    if (pendingPromise) {
      await pendingPromise
    }

    // Simulate some latency when fetching from a remote ca


rue,
  })

  if (skipped) return

  let outputIndex: number

  beforeEach(() => {
    outputIndex = next.cliOutput.length
  })

  it('should serve stale data and then pre-warmed data on subsequent request', async () => {
    const browser = await next.browser('/')
    const initialOuter = await browser.elementById('outer-data').text()
    expect(initialOuter).toBeDateString()

    // Wait for the outer cache to go stale (revalidate: 5).
    await new Promise((resolve) => setTimeout(resolve, 6000))

    // R

s the stale entry,
    // the framework serves it to the client, and kicks off a background regen.
    await browser.refresh()
    const afterStale = await browser.elementById('outer-data').text()

    // The stale data should be the same as the initial da

eset output index so the regen set we poll for below can't be satisfied
    // by the initial cold-fill set from the first fetch.
    outputIndex = next.cliOutput.length

    // This request should trigger SWR: the handler return


fetchCache of force-no-store when force-dynamic is used', async () => {
      const $ = await next.render$('/force-dynamic-fetch-cache/no-fetch-cache')
      const initData = $('#data').text()
      await retry(async () => {
        const $2 = await next.render$(
          '/force-dynamic-fetch-cache/no-fetch-cache'
        )
        expect($2('#data').text()).toBeTruthy()
        expect($2('#data').text()).not.toBe(initData)
      })

      // Check route handlers as well
      const initFetchData = await

che/route')
        ).json()
        expect(newFetchData).toBeTruthy()
        expect(newFetchData).not.toEqual(initFetchData)
      })
    })

    it('should infer a fetch cache of "force-cache" when force-dynamic is used on a fetch with revalidate', asyn

(
        await next.fetch('/force-dynamic-fetch-cache/no-fetch-cache/route')
      ).json()

      await retry(async () => {
        const newFetchData = await (
          await next.fetch('/force-dynamic-fetch-cache/no-fetch-ca


 fetchCache = 'force-cache'

export default async function Page() {
  const dataNoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a1',
    {
      cache: 'no-cache',
    }
  ).then((res) => res.text())

  const dataForceCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b2',
    {
      cache: 'force-cache',
    }
  ).then((res) => res.text())

  const dataRevalidateCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?c3',

  revalidate: 3,
      },
      cache: 'force-cache',
    }
  ).then((res) => res.text())

  const dataAutoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?d4'
  ).then((res) => res.text())

  return (
    <>
      <p>/force-c

    {
      next: {
        revalidate: 3,
      },
    }
  ).then((res) => res.text())

  const dataRevalidateAndFetchCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?d4',
    {
      next: {


Link from 'next/link'

export const dynamicParams = false

export default async function Page(props) {
  const params = await props.params
  await fetch('https://example.vercel.sh', {
    next: { revalidate: 10 },
  })
  return (
    <>

/blog/[author]

{JSON.stringify(params)}

{Date.now()}

      <Link href="/blog/tim" id="author-1">
        /blog/tim
      </Link>

      <Link href="/blog/seb" id="author-2">
        /blog/seb

      <Link href="/blog/seb/second-post" id="author-2-post-1">
        /blog/seb/second-post
      </Link>

      <Link href="/blog/styfle/first-post" id="author-3-post-1">
        /blog/styfle/first-post
      </Link>

</Link>

      <Link href="/blog/styfle" id="author-3">
        /blog/styfle
      </Link>

      <Link href="/blog/tim/first-post" id="author-1-post-1">
        /blog/tim/first-post
      </Link>


import { notFound } from 'next/navigation'

export const dynamicParams = true

export default async function Page(props) {
  const params = await props.params
  if (params.author === 'shu') {
    notFound()
  }

  return (
    <>

/blog/[author]/[slug]

{JSON.stringify(params)}

{Date.now()}

    </>
  )
}

export function generateStaticParams({ params }) {
  console.log(
    '/blog/[author]/[slug] generateStaticParams',
    JSON.stringify(p

    }
    case 'styfle': {
      return [
        {
          slug: 'first-post',
        },
        {
          slug: 'second-post',
        },
      ]
    }
    default: {
      throw new Error(`unexpected author param received ${params.author}`)
    }

arams)
  )

  switch (params.author) {
    case 'tim': {
      return [
        {
          slug: 'first-post',
        },
      ]
    }
    case 'seb': {
      return [
        {
          slug: 'second-post',
        },
      ]


table_cache } from 'next/cache'

export default async function Page() {
  const data = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?page',
    {
      next: { revalidate: 360, tags: ['thankyounext'] },
    }
  ).then((res) => res.text())

  const data2 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a=b',
    {
      next: { revalidate: 360, tags: ['thankyounext', 'justputit'] },
    }
  ).then((res) => res.text())

  const cachedData = await unstable_cache

fetchedRandom,
      }
    },
    ['random'],
    {
      tags: ['thankyounext', 'unstable_cache_tag1'],
      revalidate: 10,
    }
  )()

  return (
    <>

/variable-revalidate/revalidate-360

revalid

(
    async () => {
      const fetchedRandom = await fetch(
        'https://next-data-api-endpoint.vercel.app/api/random'
      ).then((res) => res.json())
      return {
        now: Date.now(),
        random: Math.random(),


chRetry } from '../../../lib/fetch-retry'

export const revalidate = 0

export default async function Page() {
  const data = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random?status=404',
    {
      next: {
        revalidate: 3,
      },
    }
  ).then(async (res) => {
    return {
      status: res.status,
      text: await res.text(),
    }
  })

  const data2 = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random?status=204'
  ).then(async (res) => {

status: res.status,
      text: await res.text(),
    }
  })

  return (
    <>

/variable-revalidate/status-code

{JSON.stringify(data)}

{JSON.stringify(data2)}

    </>
  )
}


y'

export const dynamic = 'force-dynamic'

export default async function Page() {
  const data = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      next: {
        revalidate: 10,
      },
    }
  ).then((res) => res.text())

  const dataWithBody1 = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      body: JSON.stringify({ hello: 'world' }),
      next: {
        revalidate: 10,
      },

          controller.enqueue(JSON.stringify({ another: 'one' }))
          controller.close()
        },
      }),
      duplex: 'half',
      next: {
        revalidate: 10,
      },
    }
  ).then((res) => res.text())

  const dataWithBody3 = await fetch

    }
  ).then((res) => res.text())

  const dataWithBody2 = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      body: new ReadableStream({
        start(controller) {


 const revalidate = 0

export default async function Page() {
  const data = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?page',
    {
      next: { revalidate: 360, tags: ['thankyounext'] },
    }
  ).then((res) => res.text())

  const data2 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a=b',
    {
      next: { revalidate: 360, tags: ['thankyounext', 'justputit'] },
    }
  ).then((res) => res.text())

  const cachedData = await unstable_cache(
    asyn

ndpoint.vercel.app/api/random',
        {
          cache: 'no-store',
        }
      ).then((res) => res.json())

      const fetchedRandomRevalidateZero = await fetch(
        'https://next-data-api-endpoint.vercel.app/api/random',
        {
          n

c () => {
      const fetchedRandom = await fetch(
        'https://next-data-api-endpoint.vercel.app/api/random'
      ).then((res) => res.json())

      const fetchedRandomNoStore = await fetch(
        'https://next-data-api-e


y'

export const runtime = 'edge'

export default async function Page() {
  const data = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      next: {
        revalidate: 10,
      },
    }
  ).then((res) => res.text())

  const dataWithBody1 = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      body: JSON.stringify({ hello: 'world' }),
      next: {
        revalidate: 10,
      },
    }

 controller.enqueue(JSON.stringify({ another: 'one' }))
          controller.close()
        },
      }),
      duplex: 'half',
      next: {
        revalidate: 10,
      },
    }
  ).then((res) => res.text())

  const formData = new FormData()
  formData

).then((res) => res.text())

  const dataWithBody2 = await fetchRetry(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      method: 'POST',
      body: new ReadableStream({
        start(controller) {


force-static'

export default async function Page() {
  const data = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?page',
    {
      cache: 'no-store',
    }
  ).then((res) => res.text())

  const data2 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?page',
    {
      next: {
        revalidate: 0,
      },
    }
  ).then((res) => res.text())

  const data3 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?page',
    {
      cache: '

}
  ).then((res) => res.text())

  return (
    <>

/force-static-fetch-no-store

{data}

{data2}

{data3}

{Date.now()}

    </>


 'default-cache'

export default async function Page() {
  const dataNoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?no-cache',
    {
      cache: 'no-cache',
    }
  ).then((res) => res.text())

  const dataForceCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?force-cache',
    {
      cache: 'force-cache',
    }
  ).then((res) => res.text())

  const dataRevalidate0 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?reval

xt: {
        revalidate: 3,
      },
    }
  ).then((res) => res.text())

  const dataRevalidateAndFetchCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?revalidate-3-force-cache',
    {
      next: {
        revalidate: 3,

idate-0',
    {
      next: {
        revalidate: 0,
      },
    }
  ).then((res) => res.text())

  const dataRevalidateCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?revalidate-3',
    {
      ne


import { unstable_noStore as noStore } from 'next/cache'

export default async function Page() {
  // this page is using unstable_noStore() to opt into dynamic rendering
  // meaning the page will bail from ISR cache and hint to patch-fetch
  // that it's in a dynamic scope and shouldn't cache the fetch.
  noStore()
  const data1 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?1',
    { cache: 'default' }
  ).then((res) => res.text())
  const data2 = await fetch(
    new Request('ht

/default-cache-fetch

"cache: default" {data1}

        "cache: default" (Request constructor) {data2}

"cache: auto" {data3}

    </>

tps://next-data-api-endpoint.vercel.app/api/random?2')
  ).then((res) => res.text())

  const data3 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?3'
  ).then((res) => res.text())

  return (
    <>


lt async function Page() {
  const dataNoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a2',
    {
      cache: 'no-cache',
    }
  ).then((res) => res.text())

  const dataForceCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b3',
    {
      cache: 'force-cache',
    }
  ).then((res) => res.text())

  const dataRevalidateCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?c4',
    {
      next: {
        revalidate: 3

orce-cache',
    }
  ).then((res) => res.text())

  const dataAutoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?d6'
  ).then((res) => res.text())

  return (
    <>

/fetch-no-cache

"

,
      },
    }
  ).then((res) => res.text())

  const dataRevalidateAndFetchCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?d5',
    {
      next: {
        revalidate: 3,
      },
      cache: 'f


rt default async function Page() {
  const [urlDefaultCache, valueDefaultCache] = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a1'
  ).then((res) => Promise.all([res.url, res.text()]))

  const urlNoCache = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?b2',
    { cache: 'no-store' }
  ).then((res) => res.url)

  const [urlCached, valueCached] = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random?a1'
  ).then((res) => Promise.all([res.url,

url-default-cache">{urlDefaultCache}

{valueDefaultCache}

{urlNoCache}

{urlCached}

{valueCached}

    </>

 res.text()]))

  return (
    <>
      <p id="data-


 { updateTag, unstable_cache } from 'next/cache'
import { RevalidateButton } from '../revalidate-button'

export const dynamic = 'force-dynamic'

export default async function Page() {
  async function revalidate() {
    'use server'
    await updateTag('random-value-data')
  }

  const cachedData = await unstable_cache(
    async () => {
      return {
        random: Math.random(),
        draftModeEnabled: (await draftMode()).isEnabled,
      }
    },
    ['random-value'],
    {
      tags: ['random-valu

>random: {Math.random()}

cachedData: {cachedData.random}

        draft mode enabled: {cachedData.draftModeEnabled.toString()}

      <RevalidateButton onClick={revalidate} />

e-data'],
    }
  )()

  return (
    <div>
      <p


lt async function Page() {
  const traceparent1 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      headers: { traceparent: 'A' },
      next: { revalidate: 50 },
    }
  ).then((res) => res.text())

  const traceparent2 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random',
    {
      headers: { traceparent: 'B' },
      next: { revalidate: 50 },
    }
  ).then((res) => res.text())

  const tracestate1 = await fetch(
    'https://next-data-api-endpoin

',
    {
      headers: { tracestate: 'B' },
      next: { revalidate: 50 },
    }
  ).then((res) => res.text())

  const echoedHeaders = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/echo-headers',
    {
      headers: { traceparent: 'A'

t.vercel.app/api/random',
    {
      headers: { tracestate: 'B' },
      next: { revalidate: 50 },
    }
  ).then((res) => res.text())

  const tracestate2 = await fetch(
    'https://next-data-api-endpoint.vercel.app/api/random


test-utils'

describe('with babel', () => {
  const { next, isNextStart, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should support babel in app dir', async () => {
    const $ = await next.render$('/')
    expect($('h1').text()).toBe('hello')
  })

  if (isNextStart) {
    // Turbopack always runs SWC, so this shouldn't be an issue, but this test
    // refers to a webpack-specific output path.
    // https://github.c

skip : it)(
      'should contain og package files in middleware',
      async () => {
        await retry(async () => {
          const middleware = await next.readFile('.next/server/middleware.js')
          // @vercel/og default font should be bundled

om/vercel/next.js/pull/51067
    ;(isTurbopack ? it.


from 'e2e-utils'

describe('@next/third-parties basic usage', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    dependencies: {
      '@next/third-parties': 'canary',
    },
  })

  it('renders YoutubeEmbed', async () => {
    const $ = await next.render$('/youtube-embed')

    const baseContainer = $('[data-ntpc="YouTubeEmbed"]')
    const youtubeContainer = $('lite-youtube')
    expect(baseContainer.length).toBe(1)
    expect(youtubeContainer.length).toBe(1)
  })

  it('renders GoogleMa

y=XYZ"]'
    )
    expect(baseContainer.length).toBe(1)
    expect(mapContainer.length).toBe(1)
  })

  it('renders GTM', async () => {
    const browser = await next.browser('/gtm')

    await browser.waitForElementByCss('script#_next-gtm')
    await brow

psEmbed', async () => {
    const $ = await next.render$('/google-maps-embed')

    const baseContainer = $('[data-ntpc="GoogleMapsEmbed"]')

    const mapContainer = $(
      '[src^="https://www.google.com/maps/embed/v1/place?ke