Output: string) {
  let cliIndex = 0
  return function expectError(
    containing: string,
    withStackContaining?: string
  ) {
    const initialCliIndex = cliIndex
    let lines = cliOutput.slice(cliIndex).split('\n')

    let i = 0
    while (i < lines.length) {
      let line = lines[i++] + '\n'
      cliIndex += line.length
      if (line.includes(containing)) {
        if (typeof withStackContaining !== 'string') {
          return
        } else {
          while (i < lines.length) {
            le

return
            }
          }
        }
      }
    }

    expect(cliOutput.slice(initialCliIndex)).toContain(containing)
  }
}

describe(`Request Promises`, () => {
  describe('On Prerender Completion', () => {
    const { next, isNextDev, skipped } =

t stackLine = lines[i++] + '\n'
            if (!stackStart.test(stackLine)) {
              expect(stackLine).toContain(withStackContaining)
            }
            if (stackLine.includes(withStackContaining)) {


nnection } from 'next/server'

export default async function Page(props: {
  params: Promise<{}>
  searchParams: Promise<{}>
}) {
  props.params.catch(logReason)
  props.searchParams.catch(logReason)
  cookies().catch(logReason)
  headers().catch(logReason)
  draftMode().catch(logReason)
  connection().catch(logReason)
  return (
    <>
      <p>
        This page renders statically but it passes all of the Request Data
        promises (cookies(), etc...) to a setTimeout scope. This test asserts
        th

ute is
        synchronously dynamic (which this one is by rendering a Math.random()
        value)
      </p>
      <p>
        <TriggerSyncDynamic />
      </p>
    </>
  )
}

function logReason(reason: any) {
  console.log('Reason:', reason)
}

async fu

at these promises eventually reject even when the ro


ployment: true,
  })

  if (skipped) {
    return
  }

  it('should generate types for next/link', async () => {
    await retry(async () => {
      const dts = await next.readFile(`${next.distDir}/types/link.d.ts`)
      expect(dts).toContain(`declare module 'next/link'`)
    })
  })

  it('should include handler route from app/api-test/route.ts in generated link route definitions', async () => {
    await retry(async () => {
      const dts = await next.readFile(`${next.distDir}/types/link.d.ts`)
      //

  await next.stop()
      await next.patchFile(
        'app/valid-links.tsx',
        `
import Link from 'next/link'

export default function ValidLinks() {
  return (
    <div>
      <Link href="/">Simple Route</Link>
      <Link href="/dashboard">Simple

 Ensure the app route handler at app/api-test/route.ts ("/api-test") is present
      expect(dts).toContain('`/api-test`')
    })
  })

  if (isNextStart) {
    it('should pass type checking with valid routes', async () => {


tils'

const expectedDts = `
type AppRoutes = "/" | "/_shop/[[...category]]" | "/dashboard" | "/dashboard/settings" | "/docs/[...slug]" | "/gallery/photo/[id]" | "/project/[slug]"
type AppRouteHandlerRoutes = "/api-test" | "/api/docs/[...slug]" | "/api/shop/[[...category]]" | "/api/users/[id]"
type PageRoutes = "/about" | "/users/[id]"
type LayoutRoutes = "/" | "/dashboard"
type RedirectRoutes = "/blog/[category]/[[...slug]]"
type RewriteRoutes = "/api-legacy/[version]/[[...endpoint]]" | "/docs-old/[...path

dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should generate route types correctly', async () => {
    // Route type generation happens after the "Ready" log fires; give it time.
    await retry(async () => {
      const

]"
type Routes = AppRoutes | PageRoutes | LayoutRoutes | RedirectRoutes | RewriteRoutes | AppRouteHandlerRoutes
`

describe('typed-routes', () => {
  const { next, isNextDev, isNextStart, skipped } = nextTestSetup({
    files: __


ect all CSS reachable from a page (inline <style> + linked .css). */
async function collectPageCss(
  next: ReturnType<typeof nextTestSetup>['next'],
  path: string
): Promise<string> {
  const html = await (await next.fetch(path)).text()
  let css = ''
  // Inline <style> blocks
  for (const m of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)) {
    css += m[1]
  }
  // External <link> stylesheets (href may contain query strings like ?dpl=...)
  for (const m of html.matchAll(/<link[^>]*href="([^"]*\.css

tTestSetup({
      files: __dirname,
      dependencies: { lightningcss: '^1.23.0' },
      // Chrome 123 supports light-dark() natively — using it here proves that
      // the `include` flag forces transpilation regardless of browser support.
      packa

[^"]*?)"[^>]*>/g)) {
    const res = await next.fetch(m[1])
    if (res.ok) css += await res.text()
  }
  return css
}

describe('experimental-lightningcss-features', () => {
  describe('include', () => {
    const { next } = nex


mport { nextTestSetup } from 'e2e-utils'

describe('unstable-rethrow', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
  })

  it('should correctly trigger the not found page as not found', async () => {
    const browser = await next.browser('/not-found-page')
    expect(await browser.elementByCss('body').text()).toContain(
      'This page could not be found.'
    )
  })

  it('should handle an internal error that gets propagated to the `cause` field', async () => {
    const

liOutput).toContain('[test assertion]: checking error')
      expect(next.cliOutput).not.toContain('[test assertion]: error leaked')
    })

    it('should correctly mark the dynamic page as dynamic', async () => {
      expect(next.cliOutput).toContain('ƒ

 browser = await next.browser('/cause')
    expect(await browser.elementByCss('body').text()).toContain('hello world')
  })

  if (isNextStart) {
    it('should not log any errors at build time', async () => {
      expect(next.c


from 'e2e-utils'

describe('use-params', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should work for single dynamic param', async () => {
    const $ = await next.render$('/a/b')
    expect($('#param-id').text()).toBe('a')
  })
  it('should work for nested dynamic params', async () => {
    const $ = await next.render$('/a/b')
    expect($('#param-id').text()).toBe('a')
    expect($('#param-id2').text()).toBe('b')
  })

  it('should work for catch all params', async () => {

 next.browser('/')
    expect(
      await browser
        .elementByCss('#to-a')
        .click()
        .waitForElementByCss('#param-id')
        .text()
    ).toBe('a')
  })

  it('should work for nested dynamic params client navigating', async () => {

    const $ = await next.render$('/a/b/c/d/e/f/g')
    expect($('#params').text()).toBe('["a","b","c","d","e","f","g"]')
  })

  it('should work for single dynamic param client navigating', async () => {
    const browser = await


eRouterAct } from 'router-act'

describe('prefetch={true} with instant route', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })
  if (isNextDev) {
    it('is skipped', () => {})
    return
  }

  it('does not include dynamic content in the prefetch when the target route has instant', async () => {
    let page: Playwright.Page
    const browser = await next.browser('/', {
      beforePageLoad(p: Playwright.Page) {
        page = p
      },
    })
    const act = createRouterA

/ but NOT dynamic content.
    await act(async () => {
      const linkToggle = await browser.elementByCss(
        'input[data-link-accordion="/target-page"]'
      )
      await linkToggle.click()
    }, [
      {
        includes: 'Cached content',

ct(page)

    // Reveal the link to trigger a prefetch. Even though the Link has
    // prefetch={true}, the route has unstable_instant defined, so the
    // prefetch should be downgraded — it should include cached content
    /


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

export const unstable_instant = {
  unstable_samples: [{ cookies: [{ name: 'test', value: null }] }],
}
export const unstable_prefetch = 'force-runtime'

export default async function Page() {
  return (

      <Suspense fallback={
Loading cached...
}>
        <Cached />
      </Suspense>
      <Suspense fallback={
Loading dynamic...
}>
        <Dynamic />
      </Suspense>

cookieStore = await cookies()
  const value = cookieStore.get('test')?.value ?? 'default'
  return
Cached content: {value}

}

async function Dynamic() {
  await connection()
  return
Dynamic content<

   </main>
  )
}

async function Cached() {
  const


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

// No instant config on the page — only the layout has it.

export default async function Page() {
  return (

      <Suspense fallback={
Loading cached...
}>
        <CachedWrapper />
      </Suspense>
      <Suspense fallback={
Loading dynamic...
}>
        <Dynamic />
      </Suspense>

  )
}

async function CachedWrapper() {
  const cookieStore = await cookies()
  const

return <Cached value={value} />
}

async function Cached({ value }: { value: string }) {
  'use cache'
  return
Cached content: {value}

}

async function Dynamic() {
  await connection()
  return

value = cookieStore.get('test')?.value ?? 'default'


  getTitle,
  retry,
} from 'next-test-utils'

describe('app dir - metadata navigation', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  describe('navigation', () => {
    it('should render root not-found with default metadata', async () => {
      const $ = await next.render$('/does-not-exist')

      // Should contain default metadata and noindex tag
      const matchHtml = createMultiHtmlMatcher($)
      expect($('meta[charset="utf-8"]').length).toBe(1)
      matchHtml('meta', 'n

).toBe('Root not found')
    })

    it('should support notFound in generateMetadata', async () => {
      const res = await next.fetch('/async/not-found')
      // metadata is suspended in SSR, it won't affect the response status
      expect(res.status).

ame', 'content', {
        viewport: 'width=device-width, initial-scale=1',
        robots: 'noindex',
        // not found metadata
        description: 'Root not found description',
      })
      expect(await $('title').text()


tTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('prefetching-not-found', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should correctly navigate to/from a global 404 page when following links with prefetch=auto', async () => {
    let browser = await next.browser('/')
    expect(await browser.elementByCss('h1').text()).toBe('Home Page')

    await browser.elementByCss("[href='/fake-link']").click()

    await retry(async () => {
      expect(awa

s('h1').text()).toBe('Home Page')
    })

    // Assert the same behavior, but starting at the not found page. This is to
    // ensure that when we seed the prefetch cache, we don't have any cache
    // collisions that would cause the not-found page to r

it browser.elementByCss('body').text()).toContain(
        'This page could not be found.'
      )
    })

    await browser.elementByCss("[href='/']").click()

    await retry(async () => {
      expect(await browser.elementByCs


'app dir - not found with default 404 page', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should error on client notFound from root layout in browser', async () => {
    const browser = await next.browser('/')

    await browser.elementByCss('#trigger-not-found').click()

    if (isNextDev) {
      // TODO: Either allow or include original stack
      await expect(browser).toDisplayRedbox(`

     `)
    }
  })

  it('should render default 404 with root layout for non-existent page', async () => {
    const browser = await next.browser('/non-existent')
    await browser.waitForElementByCss('.next-error-h1')
    expect(await browser.elementByCss

{
         "code": "E192",
         "description": "notFound() is not allowed to use in root layout",
         "environmentLabel": null,
         "label": "Runtime Error",
         "source": null,
         "stack": [],
       }


import { nextTestSetup } from 'e2e-utils'

// Skip as Turbopack doesn't support the `!=!` Webpack syntax
;(process.env.IS_TURBOPACK_TEST ? describe.skip : describe)(
  'app dir - rsc webpack loader',
  () => {
    const { next } = nextTestSetup({
      files: __dirname,
      dependencies: {
        // TODO: Temporarily pinned due to https://github.com/styled-components/styled-components/issues/5667
        // which is breaking deployment tests
        'styled-components': '6.3.9',
        'server-only': 'l

loader rules', async () => {
      const browser = await next.browser('/loader-rule')

      expect(
        await browser.eval(
          `window.getComputedStyle(document.querySelector('#red')).color`
        )
      ).toBe('rgb(255, 0, 0)')
    })
  }
)

atest',
      },
      resolutions: {
        '@babel/core': '7.22.18',
        '@babel/parser': '7.22.16',
        '@babel/types': '7.22.17',
        '@babel/traverse': '7.22.18',
      },
    })

    it('should support webpack


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

describe('interception-routes-root-catchall', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should support having a root catch-all and a catch-all in a parallel route group', async () => {
    const browser = await next.browser('/')
    await browser.elementByCss('[href="/items/1"]').click()

    // this triggers the /items route interception handling
    await check(
      () => browser.elementById('slot')

=> browser.elementById('children').text(),
      /Regular Item Page. Id: 1/
    )
  })

  it('should handle non-intercepted catch-all pages', async () => {
    const browser = await next.browser('/')

    // there's no explicit page for /foobar. This will

.text(),
      /Intercepted Modal Page. Id: 1/
    )
    await browser.refresh()

    // no longer intercepted, using the page
    await check(() => browser.elementById('slot').text(), /default @modal/)
    await check(
      ()


 { nextTestSetup } from 'e2e-utils'

describe('transition-indicator', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  // TODO: Find testing pattern to assert that the browser's pending indicator is shown
  // These tests are just instructions for manual testing

  it('displays while loading content without fallback', async () => {
    const browser = await next.browser('/')

    await browser.elementByCss('a[href="/slow-without-fallback"]').click()

    // shows pending indicator in

tByCss('a[href="/slow-with-fallback"]').click()

    // fallback is shown, no pending indicator in browser tab
  })

  it('displays during any Transition that does not commit a pending state', async () => {
    const browser = await next.browser('/')

 browser tab while loading
    // Gets stuck due to usage of history.pushState
  })

  it('does not display while loading content with fallback', async () => {
    const browser = await next.browser('/')

    await browser.elemen


{ next, skipped, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      pino: '9.6.0',
      jspdf: '4.2.1',
    },
  })

  if (skipped) {
    return
  }

  // These tests are Turbopack-specific since they rely on Turbopack's worker bundling
  if (!isTurbopack) {
    it.skip('webpack doesnt support bundling worker-threads', () => {})
    return
  }

  it('should handle simple worker with relative path', async () => {
    const res = await next.fetch('/api/s

, import.meta.url)', async () => {
    const res = await next.fetch('/api/url-worker-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.message).toBe('pong from url worker')
  })

imple-worker-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.message).toBe('pong from simple worker')
  })

  it('should handle worker with new URL(...


read, parentPort } from 'node:worker_threads'
import { NextResponse } from 'next/server'

// This file tests self-referencing worker threads using __filename
// This pattern is used by libraries like @duckdb/duckdb-wasm

if (!isMainThread && parentPort) {
  // Worker thread - handle messages
  parentPort.on('message', (msg) => {
    if (msg === 'ping') {
      parentPort!.postMessage('pong')
    }
  })
}

export async function GET() {
  if (!isMainThread) {
    // If we're in a worker, don't try to create a

 try {
    // Create a worker using __filename - this is the pattern that triggers the cycle bug
    const worker = new Worker(__filename)

    const message = await new Promise<string>((resolve, reject) => {
      const timeout = setTimeout(() => {

nother worker
    return NextResponse.json({ error: 'Already in worker' }, { status: 500 })
  }

  // Log __filename for debugging
  console.log('__filename:', __filename)
  console.log('typeof __filename:', typeof __filename)


mport { Worker } from 'node:worker_threads'
import { NextResponse } from 'next/server'

export async function GET() {
  try {
    const worker = new Worker(
      new URL('../../worker-dir/url-worker.ts', import.meta.url)
    )

    const message = await new Promise<string>((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Worker timeout'))
      }, 5000)

      worker.on('message', (msg) => {
        clearTimeout(timeout)
        resolve(msg)
      })
      worker.on

de ${code}`))
        }
      })

      worker.postMessage('ping')
    })

    await worker.terminate()

    return NextResponse.json({ success: true, message })
  } catch (error) {
    return NextResponse.json(
      { success: false, error: String(error)

('error', (err) => {
        clearTimeout(timeout)
        reject(err)
      })
      worker.on('exit', (code) => {
        if (code !== 0) {
          clearTimeout(timeout)
          reject(new Error(`Worker stopped with exit co


ditional network requests.
   */
  async function isAccordionClosed(
    browser: Playwright,
    href: string
  ): Promise<boolean> {
    const selector = `[data-testid="link-accordion"][data-href="${href}"]`

    // Check if the button is already open
    return await browser.hasElementByCss(`${selector} button`)
  }

  /**
   * Helper to navigate via the LinkAccordion component.
   * Scrolls to the accordion, opens it, and clicks the link.
   */
  async function navigate(browser: Playwright, href: string

lick the "Open" button, it may already be open, so we don't need to
    // click it again.
    if (await isAccordionClosed(browser, href)) {
      const button = await browser.elementByCss(`${selector} button`)
      await button.click()
    }

    // Clic

) {
    const selector = `[data-testid="link-accordion"][data-href="${href}"]`

    // Find and scroll to accordion
    const accordion = await browser.elementByCss(selector)
    await accordion.scrollIntoViewIfNeeded()

    // C


rt default function Page() {
  return (

✅ Should Work WITHOUT default.tsx

Test Case 1a: Simple page (no parallel routes)

          <LinkAccordion href="/simple-page" />

            Interception route with just a page.tsx, no parallel routes at all.

Test Case 1b: Has page.tsx

          <LinkAccordion href="/has-page" />

outes/deeper" />

No @parallel routes exist, so no implicit layout created.

Test Case 3: Has both @sidebar AND page.tsx

          <LinkAccordion href="/has-both" />

Interception route has page.tsx at root level. No children slot
            exists.

Test Case 2: No parallel routes

          <LinkAccordion href="/no-parallel-r


m 'cheerio'

describe('app dir rendering', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it('should serve app/page.server.js at /', async () => {
    const html = await next.render('/')
    expect(html).toContain('app/page.server.js')
  })

  describe('SSR only', () => {
    it('should run data in layout and page', async () => {
      const $ = await next.render$('/ssr-only/nested')
      expect($('#lay

await next.render$('/ssr-only/slow')
      const endTime = Date.now()
      const duration = endTime - startTime
      // Each part takes 5 seconds so it should be below 10 seconds
      // Using 7 seconds to ensure external factors causing slight slowness

out-message').text()).toBe('hello from layout')
      expect($('#page-message').text()).toBe('hello from page')
    })

    it('should run data fetch in parallel', async () => {
      const startTime = Date.now()
      const $ =


'

describe('setting cookies', () => {
  const { next, isNextDeploy, skipped } = nextTestSetup({
    files: __dirname,
  })

  if (skipped) return

  let currentCliOutputIndex = 0
  beforeEach(() => {
    resetCliOutput()
  })

  const getCliOutput = () => {
    if (next.cliOutput.length < currentCliOutputIndex) {
      // cliOutput shrank since we started the test, so something (like a `sandbox`) reset the logs
      currentCliOutputIndex = 0
    }
    return next.cliOutput.slice(currentCliOutputIndex)
  }

n error occurred in a function passed to `after\(\)`: .+?: Cookies can only be modified in a Server Action or Route Handler\./

  describe('stops cookie mutations when changing phases', () => {
    it('from an action to a page render', async () => {

  const resetCliOutput = () => {
    currentCliOutputIndex = next.cliOutput.length
  }

  const EXPECTED_ERROR =
    /Cookies can only be modified in a Server Action or Route Handler\./

  const EXPECTED_ERROR_IN_AFTER =
    /A


 @type {string} */ key) {
  const path = `/timestamp/key/${encodeURIComponent(key)}`

  const sleepDuration = getSleepDuration()
  if (sleepDuration > 0) {
    console.log(`revalidateTimestampPage :: sleeping for ${sleepDuration} ms`)
    await sleep(sleepDuration)
  }

  console.log('revalidateTimestampPage :: revalidating', path)
  revalidatePath(path)
}

const WAIT_BEFORE_REVALIDATING_DEFAULT = 1000

function getSleepDuration() {
  const raw = process.env.WAIT_BEFORE_REVALIDATING
  if (!raw) return WAIT_

.parseInt(raw)
  if (Number.isNaN(parsed)) {
    throw new Error(
      `WAIT_BEFORE_REVALIDATING must be a valid number, got: ${JSON.stringify(raw)}`
    )
  }
  return parsed
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, m

BEFORE_REVALIDATING_DEFAULT

  const parsed = Number


import { Viewport } from 'next'

export async function generateViewport({
  params,
}: {
  params: Promise<{ color: string }>
}): Promise<Viewport> {
  'use cache: remote'

  // Use `eval` to side step compiler errors for using `arguments` in a 'use
  // cache' function.
  // eslint-disable-next-line no-eval
  const unusedParentArg = eval('arguments')[1]
  if (unusedParentArg !== undefined) {
    throw new Error(
      'Expected the unused parent argument to be omitted. Received: ' +
        unusedParentArg

though.
  const { color } = await params

  return {
    colorScheme: color === 'white' ? 'light' : 'dark',
    maximumScale: 1 + Math.random(),
  }
}

export default function Layout({ children }: { children: React.ReactNode }) {
  return <main>{children}<

    )
  }

  // We're reading params here. This makes the cache function dynamic during
  // prerendering. It also requires suspense above body, so nothing will
  // prerendered. The meta tag should still be cached on refreshes


t { connection } from 'next/server'

async function getRandomValue(offset: number) {
  'use cache: remote'
  return Math.random() + offset
}

let renderCount = 0

export default async function Page() {
  await connection()

  // Create the offsets array based on the render count to force a different
  // array on each render.
  const offsets = renderCount++ % 2 === 0 ? [0, 1] : [1, 0]

  // Pass the function reference into the map function, which will pass the
  // index and array as arguments into getRando

ill create cache
  // misses if the unused arguments are not properly ignored, because they would
  // be included in the cache keys.
  const randomNumbers = await Promise.all(offsets.map(getRandomValue))

  return <p id="numbers">{randomNumbers.sort().joi


mport { Button } from './button'

async function getCachedValue(
  iterable: Iterable<number>,
  fn: () => string
): Promise<[string, () => string]> {
  'use cache'

  // Make sure we're always receiving the arguments the same way, regardless of
  // whether draft mode is enabled or not. We're asserting that by checking
  // whether an iterable was serialized/deserialized into an array by React.
  if (!Array.isArray(iterable)) {
    throw new Error(
      'Expected iterable to be serialized to an array beca

({
  params,
}: {
  params: Promise<{ mode: string }>
}) {
  'use cache'

  const { mode } = await params
  const offset = 1000

  const cachedClosure = async () => {
    'use cache'
    return new Date(Date.now() + offset).toISOString()
  }

  const { isE

use it crossed the "use cache" boundary.'
    )
  }

  const date = new Date()
  date.setFullYear(date.getFullYear() + iterable.reduce((sum, n) => sum + n))

  return [date.toISOString(), fn]
}

export default async function Page


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

export async function generateMetadata(
  _: { searchParams: Promise<Record<string, string | string[] | undefined>> },
  parent: ResolvingMetadata
): Promise<Metadata> {
  'use cache'

  // Explicitly not reading search params here. The search params should be
  // omitted from the cache key, so that we ensure a cache hit when resuming the
  // partially prerendered page.

  const { metadataBase } = await parent

  re

dataBase: metadataBase?.replace('/bar', '/baz'),
    alternates: { canonical: '/qux' },
  }
}

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

async function Dynamic() {
  awa

turn {
    title: new Date().toISOString(),
    meta


mport { Metadata } from 'next'

export async function generateMetadata(_: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  'use cache'

  // Explicitly not reading params here. The description should appear in the
  // partially prerendered page. TODO: When resuming the page, we should get a
  // cache hit (from the RDC), but omitting unused params from cache keys (and
  // upgrading cache keys when they are used) is not yet implemented.

  // Make sure this cache doesn't resolve instantly,

 it causes a cache miss, it's noticeable.
  await new Promise((resolve) => setTimeout(resolve, 5))

  return { description: new Date().toISOString() }
}

export default function Layout({ children }: { children: React.ReactNode }) {
  return <>{children}</>


ams: Promise<{ slug: string }>
}): Promise<Metadata> {
  'use cache: remote'

  // Use `eval` to side step compiler errors for using `arguments` in a 'use
  // cache' function.
  // eslint-disable-next-line no-eval
  const unusedParentArg = eval('arguments')[1]
  if (unusedParentArg !== undefined) {
    throw new Error(
      'Expected the unused parent argument to be omitted. Received: ' +
        unusedParentArg
    )
  }

  // We're reading params here. This makes the cache function dynamic during
  // p

uded from the
  // partially prerendered page.
  const { slug } = await params

  return { description: new Date().toISOString(), category: slug }
}

export default function Layout({ children }: { children: React.ReactNode }) {
  return <main>{children}</m

rerendering, and thus the description should be excl


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

export async function generateMetadata(
  _: { params: Promise<{ slug: string }> },
  parent: ResolvingMetadata
): Promise<Metadata> {
  'use cache'

  // We're not reading params here, but we do define a canonical URL, which
  // leads to the pathname being read under the hood. This should make the
  // function dynamic when prerendering the fallback shell, and not lead to a
  // timeout error.

  const { metadataBas

adataBase?.replace('/bar', '/baz'),
    alternates: { canonical: '/qux' },
  }
}

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

async function Dynamic() {
  await connection

e } = await parent

  return {
    metadataBase: met


nstable_cache } from 'next/cache'
import { setTimeout } from 'node:timers/promises'

const getUnstableTime = unstable_cache(
  async () => {
    // Small artificial delay so the foreground-await is actually in flight when
    // the prospective prerender decides whether `cacheSignal` is ready.
    await setTimeout(100)
    return new Date().toISOString()
  },
  ['blocked-by-unstable-cache'],
  { tags: ['blocked-by-unstable-cache-tag'], revalidate: false }
)

async function Cached() {
  'use cache'
  return

background revalidation
// prerender. In its prospective phase, the `unstable_cache` lookup hits a stale
// entry and foreground-awaits the recompute. The downstream `<Cached />` must
// still be reached during prospective so its RDC entry is populated for

(

      Cached: {new Date().toISOString()}

  )
}

// Awaiting an `unstable_cache` BEFORE rendering a `'use cache'` component.
// After the tag is revalidated, the next render runs a


-compare */

async function getObject(arg: unknown) {
  'use cache'

  return { arg }
}

async function getObjectWithBoundArgs(arg: unknown) {
  async function getCachedObject() {
    'use cache'

    return { arg }
  }

  return getCachedObject()
}

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

With normal args

        Two "use cache" invocations
        with the same arg return the same result:{' '}
        <strong id="same-arg

nt results:{' '}

          {String((await getObject(1)) !== (await getObject(2)))}

With bound args

        Two "use cache"</span

">
          {String((await getObject(1)) === (await getObject(1)))}

        Two "use cache" invocations
        with different args return differe


 from 'react'
import { revalidatePath, updateTag } from 'next/cache'

export function RevalidateButtons() {
  return (
    <form>
      <button
        id="revalidate-a"
        formAction={async () => {
          'use server'
          updateTag('a')
        }}
      >
        revalidate a
      </button>{' '}
      <button
        id="revalidate-b"
        formAction={async () => {
          'use server'
          updateTag('b')
        }}
      >
        revalidate b
      </button>{' '}
      <button

ync () => {
          'use server'
          updateTag('f')
        }}
      >
        revalidate f
      </button>{' '}
      <button
        id="revalidate-r"
        formAction={async () => {
          'use server'
          updateTag('r')
        }}

      id="revalidate-c"
        formAction={async () => {
          'use server'
          updateTag('c')
        }}
      >
        revalidate c
      </button>{' '}
      <button
        id="revalidate-f"
        formAction={as


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

describe('app-prefetch-false-loading', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  it('should render loading for the initial render', async () => {
    const $ = await next.render$('/en/testing')

    expect($('#loading').text()).toBe('Loading...')
  })
  it('should not re-trigger loading state when navigating between pages that share a dynamic layout', async () => {
    const logStartIndex = next.cliOutput.

owser.hasElementByCssSelector('#loading')).toBeFalsy()

    await check(
      () => browser.hasElementByCssSelector('#nested-testing-page'),
      true
    )

    const newRandomNumber = await browser.elementById('random-number').text()

    expect(initia

length

    const browser = await next.browser('/en/testing')
    let initialRandomNumber = await browser.elementById('random-number').text()
    await browser.elementByCss('[href="/en/testing/test"]').click()
    expect(await br


 useState } from 'react'

export function LinkAccordion({ href }: { href: string }) {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <div
      data-testid="link-accordion"
      data-href={href}
      style={{
        border: '1px solid #e5e7eb',
        borderRadius: '12px',
        padding: '16px',
        margin: '12px 0',
        backgroundColor: 'white',
        boxShadow:
          '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
        transition: 'all 0.2s ease',

       style={{
            fontFamily: 'monospace',
            fontSize: '14px',
            color: '#374151',
            fontWeight: '500',
            flex: 1,
          }}
        >
          {href}
        </div>
        {!isOpen && (
          <but

       cursor: 'pointer',
      }}
    >
      <div
        style={{
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          gap: '12px',
        }}
      >
        <div


root-param-dynamic-child', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  async function createBrowserActor(
    url: string,
    {
      errorPage = false,
      waitForPrefetch = false,
    }: {
      errorPage?: boolean
      waitForPrefetch?: boolean
    } = {}
  ) {
    let act: ReturnType<typeof createRouterAct>
    const browser = await next.browser(url, {
      beforePageLoad(page) {
        act = createRouterAct(page, { allowErrorStatusCodes: [404] })
      },

{ act, browser }
    }

    if (waitForPrefetch) {
      // The page has navigation links that will be automatically prefetched.
      // Some routes will 404 (like /es which isn't in generateStaticParams),
      // so we allow 404 status codes. Let's reve

     // throttling the CPU to rule out flakiness based on how quickly the page loads
      cpuThrottleRate: 6,
    })

    // If we're on the error page, we don't have a nav element to wait for.
    if (errorPage) {
      return


'force-dynamic'

async function action() {
  'use server'
  // make sure we return an updated version of the page in the response
  ;(await cookies()).set('pleaseRenderThePage', Date.now() + '')
}

export default async function Page() {
  const timestamp = Date.now()
  const cookieStore = await cookies()
  const canSetCookies = (() => {
    try {
      cookieStore.set('illegalCookie', 'i-love-side-effects-in-render')
      return true
    } catch (err) {
      // we want assert on the error message, so don'

eturn false
    }
  })()
  return (
    <>

{timestamp}

{JSON.stringify(canSetCookies)}

      <form action={action}>
        <button type="submit">Submit</button>
      </form>
    </>
  )

t swallow the error
      console.error(err)
      r


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

export async function generateViewport({
  params,
}: {
  params: Promise<{ color: string }>
}): Promise<Viewport> {
  'use cache: remote'

  // Use `eval` to side step compiler errors for using `arguments` in a 'use
  // cache' function.
  // eslint-disable-next-line no-eval
  const unusedParentArg = eval('arguments')[1]
  if (unusedParentArg !== undefined) {
    throw new Error(
      'Expected the unused

  // prerendered. The meta tag should still be cached on refreshes though.
  const { color } = await params

  return { themeColor: color, initialScale: Math.random() }
}

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

parent argument to be omitted. Received: ' +
        unusedParentArg
    )
  }

  // We're reading params here. This makes the cache function dynamic during
  // prerendering. It also requires suspense above body, so nothing will


acheLife } from 'next/cache'
import { Suspense } from 'react'

async function revalidateZero() {
  'use cache: remote'
  cacheLife({ revalidate: 0 })

  return new Date().toISOString()
}

async function lowExpire() {
  'use cache: remote'
  cacheLife({ expire: 5 })

  return new Date().toISOString()
}

async function OuterCacheNoExplicit() {
  'use cache: remote'
  // No explicit cacheLife - this would error during prerendering, but is
  // allowed at request time (after connection()).

  return (
    <>

</>
  )
}

async function OuterCacheExplicitShort() {
  'use cache: remote'
  // Explicit short cacheLife - excluded from prerender, becomes a dynamic hole.
  cacheLife({ revalidate: 0, expire: 5 })

  return (
    <>

        Explicit rev

        revalidate=0:{' '}
        {await revalidateZero()}

        expire=5: {await lowExpire()}


r'
import { Suspense } from 'react'

export async function generateMetadata(_: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  'use cache'

  // Explicitly not reading params here. The title should appear in the
  // partially prerendered page. TODO: When resuming the page, we should get a
  // cache hit (from the RDC), but omitting unused params from cache keys (and
  // upgrading cache keys when they are used) is not yet implemented.

  // Make sure this cache doesn't resolve instantly,

e.
  await new Promise((resolve) => setTimeout(resolve, 5))

  return { title: new Date().toISOString() }
}

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

async function Dyn

// so that if it causes a cache miss, it's noticeabl


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

export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  'use cache: remote'

  // Use `eval` to side step compiler errors for using `arguments` in a 'use
  // cache' function.
  // eslint-disable-next-line no-eval
  const unusedParentArg = eval('arguments')[1]
  if (unusedParentArg !== undefined) {
    throw new Error(
      'Expected the unuse

ially
  // prerendered page.
  const { slug } = await params

  return { title: new Date().toISOString(), keywords: [slug] }
}

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

d parent argument to be omitted. Received: ' +
        unusedParentArg
    )
  }

  // We're reading params here. This makes the cache function dynamic during
  // prerendering, and thus the title should be excluded from the part


import { cacheTag } from 'next/cache'
import { RevalidateButtons } from './buttons'

async function getCachedWithTag({
  tag,
  fetchCache,
}: {
  tag: string
  fetchCache?: 'force' | 'revalidate'
}) {
  'use cache'
  cacheTag(tag, 'c')

  // If `force-cache` or `revalidate` is used for the fetch call, it creates
  // basically an inner cache, and revalidating tag 'c' won't revalidate the
  // fetch cache. If both are not used, the fetch is not cached at all in the
  // fetch cache, and is included in the

orce' ? 'force-cache' : undefined,
      next: { revalidate: fetchCache === 'revalidate' ? 42 : undefined },
    }
  )

  const fetchedValue = await response.text()

  return [Math.random(), fetchedValue]
}

export default async function Page() {
  const a

cached result of `getCachedWithTag`
  // instead, thus also affected by revalidating 'c'.
  const response = await fetch(
    `https://next-data-api-endpoint.vercel.app/api/random?tag=${tag}`,
    {
      cache: fetchCache === 'f


 isNextDeploy, isNextDev, nextTestSetup } from 'e2e-utils'
import { startExternalServer } from './external-server.mjs'

describe('middleware RSC external rewrite', () => {
  if (isNextDev || isNextDeploy) {
    test('should not run during dev or deploy test runs', () => {})
    return
  }

  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
  })

  let externalServerManager: {
    cleanup: () => Promise<void>
    getReceivedRequests: () => any[]
  }

  beforeAll(async () => {
    c

t.stop()
    await externalServerManager?.cleanup()
  })

  test('should forward _rsc parameter to external server on RSC navigation', async () => {
    const browser = await next.browser('/')

    try {
      const homeContent = await browser.elementById(

onst externalPort = await findPort()
    externalServerManager = await startExternalServer(externalPort)
    next.env.EXTERNAL_SERVER_PORT = String(externalPort)
    await next.start()
  })

  afterAll(async () => {
    await nex


retry } from 'next-test-utils'

const isCacheComponentsEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'

describe('revalidatePath with rewrites', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    buildArgs: [
      '--debug-build-paths',
      isCacheComponentsEnabled
        ? '!app/legacy/**/*'
        : '!app/cache-components/**/*',
    ],
  })

  describe('static page', () => {
    it('should revalidate a static page that was rewritten', async () => {
      const browser = awa

h()
      const refreshedRandomData = await browser
        .elementById('random-data')
        .text()
      expect(refreshedRandomData).toBe(initialRandomData)

      // Trigger revalidation via route handler
      const revalidateRes = await next.fetch(

it next.browser('/static')
      const initialRandomData = await browser.elementById('random-data').text()

      expect(initialRandomData).toBeTruthy()

      // Verify the data is cached after refresh
      await browser.refres


acheLife } from 'next/cache'

async function getCachedRandomNumber() {
  'use cache'
  cacheTag('revalidate-tag-test')
  cacheLife('max')

  // This should change on each cache refresh
  return Math.random().toString()
}

export default async function Page() {
  const randomNumber = await getCachedRandomNumber()

  return (

{randomNumber}

      <form>
        <button
          id="revalidate-tag-with-profile"
          formAction={async () => {
            'use server'

efresh
            // The client should continue showing stale data
            // Fresh data should only appear on next navigation/refresh
            revalidateTag('revalidate-tag-test', 'max')
          }}
        >
          Revalidate Tag (background)

// This should NOT cause immediate client r


rt { cacheLife } from 'next/cache'
import { connection } from 'next/server'
import { Suspense } from 'react'

async function outermost(id: string) {
  'use cache'
  return id + middle('middle')
}

async function middle(id: string) {
  'use cache'
  return id + innermost('inner')
}

async function innermost(id: string) {
  'use cache'
  return id
}

async function Short({ id }: { id: string }) {
  'use cache'
  cacheLife('seconds')
  return id
}

async function Dynamic() {
  await connection()
  return null

'outer')
  await innermost('inner')

  return (
    <Suspense>
      <Short id="short" />
    </Suspense>
  )
}

export default function Page() {
  return (
    <div>
      <CachedStuff />
      <Suspense>
        <Dynamic />
      </Suspense>
    </div>

}

async function CachedStuff() {
  await outermost(


st { next, skipped, isNextDev, isTurbopack } = nextTestSetup({
    files: __dirname + '/fixtures/default',
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  it("it should error when using segment configs that aren't supported by cacheComponents", async () => {
    try {
      await next.start()
    } catch {
      // we expect the build to fail
    }

    if (isNextDev) {
      const browser = await next.browser('/revalidate')
      await waitForRedbox(browser)
      co

e segment config "revalidate" is not compatible with \`nextConfig.cacheComponents\`. Please remove it."`
        )
      } else {
        expect(redbox.description).toMatchInlineSnapshot(
          `"  x Route segment config "revalidate" is not compatible

nst redbox = {
        description: await getRedboxDescription(browser),
        source: await getRedboxSource(browser),
      }

      if (isTurbopack) {
        expect(redbox.description).toMatchInlineSnapshot(
          `"Rout


React from 'react'
import { useActionState } from 'react'
import { unstable_isUnrecognizedActionError as isUnrecognizedActionError } from 'next/navigation'

export function FormWithArg<T>({
  action,
  argument,
  id,
  children,
}: {
  action: (state: string, argument: T) => Promise<string>
  argument: T
  id: string
  children?: React.ReactNode
}) {
  const [state, dispatch] = useActionState(
    // don't use `bind()`, we want to explicitly avoid getting a FormData argument
    // because that always resu

e}`}</span>
    </form>
  )
}

export function Form({
  action,
}: {
  action: (state: string, formData: FormData) => Promise<string>
}) {
  const [state, dispatch] = useActionState(action, 'initial-state')
  return (
    <form action={dispatch} id="form-d

lts in a FormData request
    (state) => action(state, argument),
    'initial-state'
  )
  return (
    <form id={id} action={dispatch}>
      <button type="submit">{children}</button>
      <span className="form-state">{`${stat


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

export async function generateViewport({
  params,
}: {
  params: Promise<{ color: string }>
}): Promise<Viewport> {
  'use cache'

  // Explicitly not reading params here. The meta tag should appear in the
  // partially prerendered page. TODO: When resuming the page, we should get a
  // cache hit (from the RDC), but omitting unused params from cache keys (and
  // upgrading cache keys when they are us

implemented.

  return { initialScale: Math.random() }
}

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

async function Dynamic() {
  await connection()

  return
Dynamic


ort { Worker } from 'node:worker_threads'
import { NextResponse } from 'next/server'

interface PngInfo {
  url: string
  width: number
  height: number
}

export async function GET() {
  try {
    const worker = new Worker('./app/worker-dir/png-worker.ts')

    const pngInfo = await new Promise<PngInfo>((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Worker timeout'))
      }, 5000)

      worker.on('message', (msg: PngInfo) => {
        clearTimeout(timeout)

t(new Error(`Worker stopped with exit code ${code}`))
        }
      })

      worker.postMessage('get-png-info')
    })

    await worker.terminate()

    return NextResponse.json({ success: true, pngInfo })
  } catch (error) {
    return NextResponse.js

   resolve(msg)
      })
      worker.on('error', (err) => {
        clearTimeout(timeout)
        reject(err)
      })
      worker.on('exit', (code) => {
        if (code !== 0) {
          clearTimeout(timeout)
          rejec


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

describe('app dir', () => {
  const { next, isNextDev, isNextStart, skipped } = nextTestSetup({
    files: __dirname,
    // This is skipped when deployed because there are no assertions outside of next start/next dev
    skipDeployment: true,
  })

  if (skipped) return

  if (isNextStart) {
    describe('Loading', () => {
      it('should render loading.js in initial html for slow page', async () => {
        const $ = await next.r

      const browser = await next.browser('/page-with-loading')

        await retry(async () => {
          const headline = await browser.elementByCss('h1').text()
          expect(headline).toBe('hello from slow page')
        })

        const cliOutput

ender$('/page-with-loading')
        expect($('#loading').text()).toBe('Loading...')
      })
    })
  }

  if (isNextDev) {
    describe('HMR', () => {
      it('should not cause error when removing loading.js', async () => {


CLI output to only keep Turbopack error header lines (starting with ⨯ or
// standalone file:line:col) to accurately count error occurrences without counting
// console.error, stack traces, or forwarded browser logs.
function filterToErrorHeaders(output: string): string {
  return output
    .split('\n')
    .filter(
      (line) =>
        !line.includes('[browser]') &&
        !line.includes('console.error') &&
        !line.includes('at <unknown>') &&
        !line.includes('at Object.') &&
        !line.

 so we only test this on Turbopack
;(process.env.IS_TURBOPACK_TEST ? describe : describe.skip)(
  'cache-components-edge-deduplication',
  () => {
    const { next, skipped, isNextDev } = nextTestSetup({
      files: __dirname + '/fixtures/edge-deduplicati

includes('at DevServer.') &&
        !line.includes('at DevBundlerService.') &&
        !line.includes('at async ')
    )
    .join('\n')
}

// Only Turbopack runs the transform on the layout once in edge and non-edge contexts
//


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

describe('app dir - instant-validation-client', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    skipStart: true,
  })

  if (skipped) {
    return
  }

  it('should error when a client component exports unstable_instant', async () => {
    const expectedErrMsg = `"unstable_instant" is a route segment config and can only be used when the segment is a Server Component module. Remove the "use clie

t.start().catch(() => {})
      await next.browser('/').catch(() => {})
      await retry(async () => {
        expect(next.cliOutput).toContain(expectedErrMsg)
      })
    } else {
      const { cliOutput } = await next.build()
      expect(cliOutput).to

nt" directive`

    if (isNextDev) {
      await nex


mport { Worker } from 'node:worker_threads'
import { NextResponse } from 'next/server'

export async function GET() {
  try {
    // Test with a relative path instead of __filename
    const worker = new Worker('./app/worker-dir/simple-worker.ts')

    const message = await new Promise<string>((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Worker timeout'))
      }, 5000)

      worker.on('message', (msg) => {
        clearTimeout(timeout)
        resolve(msg)

stopped with exit code ${code}`))
        }
      })

      worker.postMessage('ping')
    })

    await worker.terminate()

    return NextResponse.json({ success: true, message })
  } catch (error) {
    return NextResponse.json(
      { success: false,

  })
      worker.on('error', (err) => {
        clearTimeout(timeout)
        reject(err)
      })
      worker.on('exit', (code) => {
        if (code !== 0) {
          clearTimeout(timeout)
          reject(new Error(`Worker


 server actions', () => {
  const { next, isNextDeploy, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  let cliOutputPosition: number = 0
  beforeEach(() => {
    cliOutputPosition = next.cliOutput.length
  })
  const getLogs = () => {
    return next.cliOutput.slice(cliOutputPosition)
  }

  // This is disabled when deployed because the 404 page will be served as a static route
  // which will not support POST requests, and will return a 405 instead.
  if (!isNextDeploy) {
    it('should 404 wh

-urlencoded',
        },
        body: 'foo=bar',
      })

      const cliOutput = getLogs()
      expect(cliOutput).not.toContain('TypeError')
      expect(cliOutput).not.toContain(
        'Missing `origin` header from a forwarded Server Actions request

en POSTing a non-server-action request to a nonexistent page', async () => {
      const res = await next.fetch('/non-existent-route', {
        method: 'POST',
        headers: {
          'content-type': 'application/x-www-form


server'
import pino from 'pino'

export async function GET() {
  try {
    // Create a pino logger with a transport
    // This internally uses thread-stream which creates worker_threads
    const logger = pino({
      transport: {
        target: 'pino/file',
        options: { destination: 1 }, // stdout
      },
    })

    // Log a test message - this triggers the transport worker
    logger.info('pino test message')

    // Flush the logger to ensure the message is sent
    // Use nextTick to allow asy

mise((resolve) => process.nextTick(resolve))

    return NextResponse.json({
      success: true,
      message: 'pino logger with transport initialized successfully',
    })
  } catch (error) {
    return NextResponse.json(
      { success: false, error:

nc initialization of the transport
    await new Pro


    const $ = cheerio.load(html)

    expect(JSON.parse($('#params').text())).toEqual({
      slug: ['hello123'],
    })
  })

  it('should return normalized dynamic route params for catch-all edge page', async () => {
    const html = await next.render('/catch-all-edge/a/b/c')
    const $ = cheerio.load(html)

    expect(JSON.parse($('#params').text())).toEqual({
      slug: ['a', 'b', 'c'],
    })
  })

  it('should have correct searchParams and params (server)', async () => {
    const html = await next.

rch-params').text())).toEqual({
      query1: 'value2',
    })
  })

  it('should have correct searchParams and params (client)', async () => {
    const browser = await next.browser(
      '/dynamic-client/category-1/id-2?query1=value2'
    )
    const ht

render('/dynamic/category-1/id-2?query1=value2')
    const $ = cheerio.load(html)

    expect(JSON.parse($('#id-page-params').text())).toEqual({
      category: 'category-1',
      id: 'id-2',
    })
    expect(JSON.parse($('#sea


ver'

/**
 * @param {import('next/server').NextRequest} request
 * @returns {Promise<NextResponse | undefined>}
 */
export async function middleware(request) {
  if (request.nextUrl.pathname === '/searchparams-normalization-bug') {
    const headers = new Headers(request.headers)
    headers.set('test', request.nextUrl.searchParams.get('val') || '')
    const response = NextResponse.next({
      request: {
        headers,
      },
    })

    return response
  }
  if (request.nextUrl.pathname === '/exists-

  if (request.nextUrl.pathname === '/bootstrap/with-nonce') {
    // In a real app, crypto.randomUUID() would be used to generate a safe nonce.
    // React and Webpack use eval() in development mode, so we need to allow it.
    const csp = `script-src 'n

but-not-routed') {
    return NextResponse.rewrite(new URL('/dashboard', request.url))
  }

  if (request.nextUrl.pathname === '/middleware-to-dashboard') {
    return NextResponse.rewrite(new URL('/dashboard', request.url))
  }


xports = {
  env: {
    LEGACY_ENV_KEY: '1',
  },
  experimental: {
    clientRouterFilterRedirects: true,
    parallelServerCompiles: true,
    parallelServerBuildTraces: true,
    webpackBuildWorker: true,
  },
  // output: 'standalone',
  rewrites: async () => {
    return {
      beforeFiles: [
        {
          source: '/before-files-rewrite-with-empty-arrays',
          destination: '/',
          has: [],
          missing: [],
        },
      ],
      afterFiles: [
        {
          source: '/r

       },
        {
          source: '/search-params-prop-server-rewrite',
          destination:
            '/search-params-prop/server?first=value&second=other%20value&third',
        },
        {
          source: '/after-files-rewrite-with-empty-arra

ewritten-to-dashboard',
          destination: '/dashboard',
        },
        {
          source: '/search-params-prop-rewrite',
          destination:
            '/search-params-prop?first=value&second=other%20value&third',


 from 'fs-extra'
import os from 'os'
import path from 'path'
import {
  findPort,
  initNextServerScript,
  killApp,
  fetchViaHTTP,
} from 'next-test-utils'

if (!(globalThis as any).isNextStart) {
  it('should skip for non-next start', () => {})
} else {
  describe('output: standalone with getStaticProps', () => {
    const { next, skipped } = nextTestSetup({
      files: __dirname,
      skipStart: true,
      dependencies: {
        swr: 'latest',
        nanoid: '4.0.1',
      },
    })

    if (skippe

'pages/gsp.js',
        `
        import useSWR from 'swr'

        console.log(useSWR)

        export default function Home() {
          return
Hello

        }

        export async function getStaticProps() {
          return

d) {
      return
    }

    beforeAll(async () => {
      await next.patchFile(
        'next.config.js',
        (await next.readFile('next.config.js')).replace('// output', 'output')
      )

      await next.patchFile(


 from 'fs-extra'
import os from 'os'
import path from 'path'
import {
  findPort,
  initNextServerScript,
  killApp,
  fetchViaHTTP,
} from 'next-test-utils'

if (!(globalThis as any).isNextStart) {
  it('should skip for non-next start', () => {})
} else {
  describe('output: standalone with app dir', () => {
    const { next, skipped } = nextTestSetup({
      files: __dirname,
      skipStart: true,
      dependencies: {
        nanoid: '4.0.1',
      },
    })

    if (skipped) {
      return
    }

    b

s correctly for route groups (nodejs only)', async () => {
      expect(next.cliOutput).not.toContain('Failed to copy traced files')
      const serverDirPath = path.join(
        next.testDir,
        '.next/standalone/.next/server'
      )
      for (con

eforeAll(async () => {
      await next.patchFile(
        'next.config.js',
        (await next.readFile('next.config.js')).replace('// output', 'output')
      )
      await next.start()
    })

    it('should handle trace file


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

describe('useReportWebVitals hook', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    skipStart: true,
    env: {},
    dependencies: {
      nanoid: '4.0.1',
    },
  })

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

  // Analytics events are only sent in production
  it('should send web-vitals', async () => {
    await next.fetch('/report-web-vitals')

    let eventsCount = 0
    const browser = await

fresh will trigger CLS and LCP. When page loads FCP and TTFB will trigger:
    await browser.refresh()

    // After interaction LCP and FID will trigger
    await browser.elementById('btn').click()

    // Make sure all registered events in performance-re

next.browser('/report-web-vitals', {
      beforePageLoad: (page) => {
        page.route('https://example.vercel.sh/vitals', (route) => {
          eventsCount += 1
          route.fulfill()
        })
      },
    })

    // Re


t from 'next/script'

import Client from './client'

export default function Page() {
  return (

next/script

      <Client />

egy="beforeInteractive"
        id="2.5"
        dangerouslySetInnerHTML={{
          __html: `
        ;(window._script_order = window._script_order || []).push(2.5)
        console.log(window._script_order)
        `,
        }}
      />
      <Script

"1.5">{`
        ;(window._script_order = window._script_order || []).push(1.5)
        console.log(window._script_order)
      `}</Script>
      <Script strategy="beforeInteractive" src="/test2.js" />
      <Script
        strat


t'
import { useCallback } from 'react'
import { useState } from 'react'

export default function Page() {
  const [success, setSuccess] = useState(false)
  const [finishedRefresh, setFinishedRefresh] = useState(false)
  const router = useRouter()

  const handleClick = useCallback(() => {
    setSuccess(true)
    startTransition(() => {
      router.refresh()
      setFinishedRefresh(true)
    })
  }, [router])

  return (
    <>
      <button id="refresh-page" onClick={handleClick}>
        Trigger refresh

e={{
          backgroundColor: success ? '#228B22' : '#ccc',
          color: 'white',
        }}
      >
        The current page

      {finishedRefresh && (

Refreshed page successfully!

      )}
    </>
  )

 and setState
      </button>
      <h1
        styl


lient'

import { RefObject, Suspense, use, useEffect, useRef } from 'react'

// The monaco module uses `window` in the module scope, so when it executes it immediately throws.
// In order to avoid executing it on the server it's only imported in the browser.
// We leverage `use` to wait for the promise.
const maybeMonaco = typeof window === 'undefined' ? null : import('./monaco')

function LazyMonaco({
  editorRef,
}: {
  editorRef: RefObject<HTMLDivElement | null>
}) {
  const { monaco } = maybeMonaco ===

script',
        automaticLayout: true,
      })

      return () => {
        monacoEditor.dispose()
      }
    }
  }, [monaco, editorRef])

  return null
}

export function Editor() {
  const editorRef = useRef<HTMLDivElement>(null)
  return (
    <>

null ? {} : use(maybeMonaco)

  useEffect(() => {
    if (monaco && editorRef.current) {
      const monacoEditor = monaco.editor.create(editorRef.current, {
        value: "console.log('Hello, world!');",
        language: 'java


t/anchorSelect.js';
// import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js';
// import 'monaco-editor/esm/vs/editor/contrib/caretOperations/caretOperations.js';
// import 'monaco-editor/esm/vs/editor/contrib/caretOperations/transpose.js';
// import 'monaco-editor/esm/vs/editor/contrib/clipboard/clipboard.js';
// import 'monaco-editor/esm/vs/editor/contrib/codeAction/codeActionContributions.js';
// import 'monaco-editor/esm/vs/editor/contrib/codelens/codelensController.js';
// impo

 'monaco-editor/esm/vs/editor/contrib/cursorUndo/cursorUndo.js';
// import 'monaco-editor/esm/vs/editor/contrib/dnd/dnd.js';
// import 'monaco-editor/esm/vs/editor/contrib/documentSymbols/documentSymbols.js';
// import "monaco-editor/esm/vs/editor/contrib/

rt 'monaco-editor/esm/vs/editor/contrib/colorPicker/colorContributions.js';
// import 'monaco-editor/esm/vs/editor/contrib/comment/comment.js';
// import 'monaco-editor/esm/vs/editor/contrib/contextmenu/contextmenu.js';
// import


 { nextTestSetup } from 'e2e-utils'

describe('ppr-root-param-rsc-fallback', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  if (isNextDev) {
    it('skipped in dev', () => {})
    return
  }

  it('does not return HTML for a pregenerated root param full-route rsc request', async () => {
    const response = await next.fetch(`/tenant-a/samples`, {
      headers: {
        RSC: '1',
      },
    })
    const body = await response.text()
    const contentType = response.hea

 root param full-route rsc request', async () => {
    const tenant = `tenant-${Date.now()}`
    const response = await next.fetch(`/${tenant}/samples`, {
      headers: {
        RSC: '1',
      },
    })
    const body = await response.text()
    const c

ders.get('content-type') ?? ''

    expect(response.status).toBe(200)
    expect(contentType).toContain('text/x-component')
    expect(body).not.toContain('<!DOCTYPE html>')
  })

  it('does not return HTML for a non-pregenerated


'

export function generateStaticParams() {
  return [{ tenant: 'tenant-a' }, { tenant: 'tenant-b' }]
}

async function CachedTenantMarker({
  params,
}: {
  params: Promise<{ tenant: string }>
}) {
  'use cache'

  return (
    <div hidden id="cached-tenant-marker">
      {(await params).tenant}
    </div>
  )
}

async function EnsureTenant({
  params,
}: {
  params: Promise<{ tenant: string }>
}) {
  const { tenant } = await params

  if (typeof tenant !== 'string') {
    throw new Error('Failed to read `

efault function TenantLayout({
  children,
  params,
}: {
  children: ReactNode
  params: Promise<{ tenant: string }>
}) {
  return (
    <>
      <Suspense>{children}</Suspense>
      <Suspense fallback={null}>
        <CachedTenantMarker params={params}

tenant` from params')
  }

  return null
}

export d


pense } from 'react'
import { cacheLife, cacheTag } from 'next/cache'
import { connection } from 'next/server'

async function CachedShell({ tenant }: { tenant: string }) {
  'use cache'

  cacheLife({
    stale: 300,
    revalidate: 300,
    expire: 3600,
  })
  cacheTag(`sample-shell:${tenant}`)

  return (
    <>

        Tenant: {tenant}

        Cached shell token: {crypto.randomUUID()}

    </>

g>{tenant}</strong>.
    </p>
  )
}

export default function SamplesPage({
  params,
}: {
  params: Promise<{ tenant: string }>
}) {
  const shell = params.then(async ({ tenant }) => (
    <CachedShell tenant={tenant} />
  ))

  return (
    <main>
      <

 )
}

async function DynamicRequestContent({
  params,
}: {
  params: Promise<{ tenant: string }>
}) {
  const { tenant } = await params

  await connection()

  return (
    <p id="dynamic">
      Request-time content for <stron


('custom-cache-control', () => {
  const { next, isNextDev, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  if (isNextDeploy) {
    // customizing these headers won't apply on environments
    // where headers are applied outside of the Next.js server
    it('should skip for deploy', () => {})
    return
  }

  it('should have custom cache-control for app-ssg prerendered', async () => {
    const res = await next.fetch('/app-ssg/first')
    expect(res.headers.get('cache-control')).toBe(

rol')).toBe(
      isNextDev ? 'no-cache, must-revalidate' : 's-maxage=31'
    )
  })
  ;(process.env.__NEXT_CACHE_COMPONENTS ? it.skip : it)(
    'should have default cache-control for app-ssg another',
    async () => {
      const res = await next.fetch

 isNextDev ? 'no-cache, must-revalidate' : 's-maxage=30'
    )
  })

  it('should have custom cache-control for app-ssg lazy', async () => {
    const res = await next.fetch('/app-ssg/lazy')
    expect(res.headers.get('cache-cont


 {import('next').NextConfig}
 */
const nextConfig = {
  headers() {
    return [
      {
        source: '/app-ssg/first',
        headers: [
          {
            key: 'Cache-Control',
            value: 's-maxage=30',
          },
        ],
      },
      {
        source: '/app-ssg/lazy',
        headers: [
          {
            key: 'Cache-Control',
            value: 's-maxage=31',
          },
        ],
      },
      {
        source: '/app-ssr',
        headers: [
          {
            key:

          },
        ],
      },
      {
        source: '/pages-ssg/first',
        headers: [
          {
            key: 'Cache-Control',
            value: 's-maxage=34',
          },
        ],
      },
      {
        source: '/pages-ssg/lazy',

'Cache-Control',
            value: 's-maxage=32',
          },
        ],
      },
      {
        source: '/pages-auto-static',
        headers: [
          {
            key: 'Cache-Control',
            value: 's-maxage=33',


matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
}

export default function middleware(req) {
  const url = new URL(req.url)

  if (url.pathname === '/hello/wyatt') {
    return NextResponse.rewrite(new URL('/h

 === '/hello/bob') {
    return NextResponse.rewrite(new URL('/hello/bobby', url))
  }

  if (url.pathname === '/hello/john') {
    return NextResponse.rewrite(new URL('/hello/john?key=value', url))
  }

  if (url.pathname === '/hello/vercel') {
    return

ello/admin?key=value', url))
  }

  if (url.pathname


'?: '0' | '1' | '2'
    'next-router-segment-prefetch'?: string
  }
  expected: Record<Target, string | null>
}[] = [
  {
    name: 'static HTML',
    pathname: '/',
    expected: {
      'x-nextjs-rewritten-path': null,
      'x-nextjs-rewritten-query': null,
    },
  },
  {
    name: 'static RSC',
    pathname: '/',
    headers: {
      rsc: '1',
    },
    expected: {
      'x-nextjs-rewritten-path': null,
      'x-nextjs-rewritten-query': null,
    },
  },
  {
    name: 'static Prefetch RSC',
    pathna

},
    expected: {
      'x-nextjs-rewritten-path': null,
      'x-nextjs-rewritten-query': null,
    },
  },
  {
    name: 'prerender HTML',
    pathname: '/hello/world',
    expected: {
      'x-nextjs-rewritten-path': null,
      'x-nextjs-rewritten-que

me: '/',
    headers: {
      rsc: '1',
      'next-router-prefetch': '1',
      ...(process.env.__NEXT_CACHE_COMPONENTS === 'true'
        ? {
            'next-router-segment-prefetch': '/_tree',
          }
        : {}),


('turbopack-postcss-multiple-configs', () => {
  const { next, isTurbopack, skipped } = nextTestSetup({
    files: __dirname,
    // Per-directory PostCSS config resolution is a Turbopack-only feature
    // (turbopackLocalPostcssConfig). Webpack does not support this feature and
    // does not accept function-valued PostCSS plugins, so skip non-Turbopack runs.
    skipStart: true,
    skipDeployment: true,
  })

  if (skipped) return

  if (!isTurbopack) {
    it('should only run with Turbopack', () => {}

oduction mode the CSS minifier may shorten named colors to hex
  // (e.g. blue → #00f), so we match on patterns that cover both forms.
  const DIR_COLORS: Record<number, string | RegExp> = {
    1: /blue|#00f/,
    2: /purple|#800080/,
    3: /orange|#ffa5

)
    return
  }

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

  // Each directory's postcss.config.js passes a unique color option to the
  // shared plugin, which replaces `color: red` with the given color.
  // In pr


utils'

describe('css-chunking', () => {
  const { next } = nextTestSetup({ files: __dirname })

  // this test asserts that all the emitted CSS files for the index page
  // do not contain styles for the `/other` page, which can happen
  // when the CSSChunkingPlugin is enabled and styles are shared across
  // both routes.
  ;(process.env.IS_TURBOPACK_TEST ? it.skip : it)(
    'should be possible to disable the chunking plugin',
    async () => {
      const $ = await next.render$('/')
      const stylesh

.each(async (_, element) => {
        const href = element.attribs.href
        const result = await next.fetch(href)
        const css = await result.text()

        // eslint-disable-next-line jest/no-standalone-expect
        expect(css).not.toContain('

eets = $('link[rel="stylesheet"]')
      stylesheets


from 'e2e-utils'
import { outdent } from 'outdent'

describe('headers-static-bailout', () => {
  const { next, isNextStart } = nextTestSetup({
    files: __dirname,
    dependencies: {
      nanoid: '4.0.1',
    },
  })

  if (!isNextStart) {
    it('should skip', () => {})
    return
  }

  it('should bailout when using an import from next/headers', async () => {
    const url = '/page-with-headers'
    const $ = await next.render$(url)
    expect($('h1').text()).toBe('Dynamic Page')

    // Check if the p

async () => {
    const url = '/page-without-headers'

    const $ = await next.render$(url)
    expect($('h1').text()).toBe('Static Page')

    // Check if the page is not statically generated.
    const id = $('#nanoid').text()
    const $2 = await next.

age is not statically generated.
    const id = $('#nanoid').text()
    const $2 = await next.render$(url)
    const id2 = $2('#nanoid').text()
    expect(id).not.toBe(id2)
  })

  it('should not bailout when not using headers',


Link from 'next/link'

/** Add your relevant code here for the issue to reproduce */
export default function Home() {
  return (

Subpage linking issue reproduction

Reproducing:

Press the "Go to blog" link

Press the "Go to post" link

          Expected behavior: link should take you to the blog post

          Actual behavior: the browser fetches the data for the page

/blog to /blog/a-post (/[slug]{' '}
        where slug is blog and /blog/[slug] where slug is a-post)

      <Link href="/blog" style={{ display: 'block' }} id="to-blog">
        Go to

         but never navigates
        </li>
      </ol>
      <p>
        Reloading and pressing the "Go to another page" link and then going to
        the blog post does work however, suggesting the issue is navigating from{' '}


from 'e2e-utils'

describe('app-dir - no server actions', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  it('should error when triggering a fetch action on an app with no server actions', async () => {
    const res = await next.fetch('/', {
      method: 'POST',
      headers: {
        'Next-Action': 'abc123',
      },
    })

    expect(res.status).toBe(404)
    expect(res.headers.get('x-nextjs-action-not-found')).toBe('1')

    // Runtime logs and custom headers a

ttps://nextjs.org/docs/messages/failed-to-find-server-action'
      )
    }
  })

  it('should error when triggering an MPA action on an app with no server actions', async () => {
    const formData = new FormData()
    formData.append('test', 'value')

re not forwarded to the client when deployed.
    if (!isNextDeploy) {
      expect(next.cliOutput).toContain(
        'Failed to find Server Action "abc123". This request might be from an older or newer deployment.\nRead more: h


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

const originalNextConfig = fs.readFileSync(
  __dirname + '/next.config.js',
  'utf8'
)
const importMetaResolveNextConfig = `export default {
  cacheHandler: import.meta.resolve('./cache-handler-esm.js'),
}`

function runTests(
  exportType: string,
  { next, isNextDev }: { next: NextInstance; isNextDev: boolean }
) {
  describe(exportType, () => {
    it('should have logs from cache-handler', async () => {
      if (isNextDev)

ct(next.cliOutput).toContain('cache-handler get')
        expect(next.cliOutput).toContain('cache-handler set')
        return 'success'
      }, 'success')
    })
  })
}

describe('app-dir - custom-cache-handler - cjs', () => {
  const { next, isNextDev,

{
        await next.fetch('/')
      }
      await check(() => {
        expect(next.cliOutput).toContain('cache handler - ' + exportType)
        expect(next.cliOutput).toContain('initialized custom cache-handler')
        expe


retry } from 'next-test-utils'

describe('expire-time', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  // On Vercel, ISR cache decisions happen at the Proxy layer. The Vercel
  // builder reads the route's `expire` value (Next.js's `initialExpireSeconds`
  // in the prerender manifest, which the build derives from `expireTime` when
  // no explicit `cacheLife` expire is set) and passes it to the Proxy as
  // `staleExpiration`. The Proxy is also expected, once impleme

rves stale with a
  // background revalidation instead of a blocking prerender. When Proxy is
  // updated to honor the expire value, this test will start passing in deploy
  // mode and `it.failing` will itself fail. That's the signal to flip it back
  //

nted, to read
  // updated values from Next.js's `stale-while-revalidate` response header on
  // subsequent revalidations. Today the Proxy ignores the expire value entirely
  // and treats it as one year. Past `expireTime` it se


/ This page is validating that despite multiple unsuccessful responses to the same, potentially cached URLs,
// the build locks are resolved and the page is still built successfully.
export async function generateStaticParams() {
  return [
    {
      slug: 'slug-0',
    },
    {
      slug: 'slug-1',
    },
    {
      slug: 'slug-2',
    },
    {
      slug: 'slug-3',
    },
    {
      slug: 'slug-4',
    },
  ]
}

export default async function Page() {
  const data = await fetch(
    `http://localhost:

TEST_SERVER_PORT}?status=404`
  ).then((res) => res.text())
  await fetch(
    `http://localhost:${process.env.TEST_SERVER_PORT}?status=404`
  ).then((res) => res.text())

  return (
    <>

hello world

{data}

    </>
  )


not actually
// used by that server module).
// This is a known limitation of flight-client-entry-plugin and we will improve
// this in the future.

const getAttrs = (elems: Cheerio) =>
  Array.from(elems)
    .map((elem) => elem.attribs)
    // There is something weord that causes different machines to have different order of things
    // My machine behaves differently to CI
    .sort((a, b) => (a.href < b.href ? -1 : 1))

describe('app dir - next/font', () => {
  describe('app dir - next-font', () => {

w FileRef(join(__dirname, 'node_modules')),
        'next.config.js': new FileRef(join(__dirname, 'next.config.js')),
      },
      dependencies: {
        '@next/font': 'canary',
      },
      skipDeployment: true,
    })

    if (skipped) {
      retur

   const {
      next,
      isNextDev: isDev,
      skipped,
    } = nextTestSetup({
      files: {
        app: new FileRef(join(__dirname, 'app')),
        fonts: new FileRef(join(__dirname, 'fonts')),
        node_modules: ne


port { nextTestSetup } from 'e2e-utils'

const GENERIC_RSC_ERROR =
  'Minified React error #441; visit https://react.dev/errors/441 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.'

export function runTest({ next, isNextDev }) {
  it('should error when passing process env to client component', async () => {
    const browser = await next.browser('/')
    expect(await browser.waitForElementByCss('#error-component').text()).toBe(
      isNextDev

t pass process.env to Client Components since it will leak sensitive data'
        : GENERIC_RSC_ERROR
    )
  })
}

describe('app dir - taint', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  runTest({ next, isNextDev }


eact'
import { FormWithArg, Form, UnrecognizedActionBoundary } from './client'

const action = async (...args: any[]) => {
  'use server'
  console.log('hello from server', ...args)
  return 'state-from-server'
}

// simulate client-side version skew by changing the action ID to something the server won't recognize
setServerActionId(action, 'decafc0ffeebad01')

export default function Page() {
  return (

        <UnrecognizedActionBoundary>
          <Form action={action} />
        </

            Submit client form with simple argument
          </FormWithArg>
        </UnrecognizedActionBoundary>
      </div>
      <div>
        <UnrecognizedActionBoundary>
          <FormWithArg
            action={action}
            id="form-complex

UnrecognizedActionBoundary>
      </div>
      <div>
        <UnrecognizedActionBoundary>
          <FormWithArg
            action={action}
            id="form-simple-argument"
            argument={{ foo: 'bar' }}
          >


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

describe('app-root-param-getters - generateStaticParams error', () => {
  const { next, isNextDev, skipped } = nextTestSetup({
    files: path.join(__dirname, 'fixtures', 'generate-static-params-error'),
    skipStart: true,
    skipDeployment: true,
  })
  if (skipped) return

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

  if (isNextDev) {
    it('should error when reading a root param inside the generateStaticParams that define

on": "Route /[lang]/[locale] used \`import('next/root-params').lang()\` inside \`generateStaticParams\`, but the \`lang\` parameter was not provided by a parent \`generateStaticParams\`. In \`generateStaticParams\`, root params are only available for segme

s it - dev', async () => {
      const browser = await next.browser('/en/us')

      // TODO: This is not the correct error code.
      await expect(browser).toDisplayRedbox(`
       {
         "code": "E394",
         "descripti


ils'

describe('app-root-param-getters - multiple roots', () => {
  const { next, isNextDev, isTurbopack } = nextTestSetup({
    files: join(__dirname, 'fixtures', 'multiple-roots'),
  })

  it('should have root params on dashboard pages', async () => {
    const $ = await next.render$('/dashboard/1')
    expect($('body').text()).toContain('Dashboard Root')
    expect($('p').text()).toBe(`hello world ${JSON.stringify({ id: '1' })}`)
  })

  it('should not have root params on marketing pages', async () => {

added or renamed', async () => {
      // Start on the dashboard page, which uses root param getters.
      // This forces the bundler to generate 'next/root-params'.
      await using sandbox = await createSandbox(next, undefined, `/dashboard/1`)
      co

    const $ = await next.render$('/landing')
    expect($('body').text()).toContain('Marketing Root')
    expect($('p').text()).toBe('hello world {}')
  })

  if (isNextDev) {
    it('should add getters when new root layouts are


tTestSetup } from 'e2e-utils'
import execa from 'execa'
import { join } from 'path'
import { retry } from 'next-test-utils'

// Each fixture has a typecheck-validation.ts that imports from 'next/root-params'
// and uses @ts-expect-error to assert correct and incorrect type assignments.
// Running `tsc --noEmit` verifies the generated root-params.d.ts is wired in
// and produces the expected types.

describe.each([{ fixture: 'simple' }, { fixture: 'multiple-roots' }])(
  'app-root-param-getters - typecheck (

check with generated root-params types', async () => {
      await retry(async () => {
        await next.readFile(`${next.distDir}/types/root-params.d.ts`)
      })

      await next.stop()
      try {
        const { stdout, stderr } = await execa('pnpm'

$fixture)',
  ({ fixture }) => {
    const { next, skipped } = nextTestSetup({
      files: join(__dirname, 'fixtures', fixture),
      skipDeployment: true,
    })

    if (skipped) {
      return
    }

    it('should pass type


This file is NOT a React component — it only exists to validate
// the generated root-params.d.ts types via `tsc --noEmit`.
// Lines marked @ts-expect-error must produce a type error; if they
// don't, tsc itself will fail ("Unused '@ts-expect-error' directive").

import { lang, locale, path } from 'next/root-params'
// @ts-expect-error — nonexistent is not a root param
import { nonexistent as _nonexistent } from 'next/root-params'

async function _validate() {
  // --- lang: Promise<string | undefined> ---

ng | undefined = await locale() // ok
  // @ts-expect-error — locale() does not return a number
  const _localeBad: number = await locale()

  // --- path: Promise<string[] | undefined> ---
  const _pathVal: string[] | undefined = await path() // ok
  // @

  const _langVal: string | undefined = await lang() // ok
  // @ts-expect-error — lang() does not return a number
  const _langBad: number = await lang()

  // --- locale: Promise<string | undefined> ---
  const _localeVal: stri


t { lang, locale } from 'next/root-params'
import { Suspense } from 'react'

export default async function Page({ params }) {
  return (

        root params shouldn't need a suspense a suspense boundary

          {JSON.stringify({
            lang: await lang(),
            locale: await locale(),
          })}

        in cacheComponents, dynamic params need a suspense boundary
        (because we didn't pr

in generateStaticParams)
        <Suspense>
          <DynamicSlug params={params} />
        </Suspense>

  )
}

async function DynamicSlug({ params }) {
  const { slug } = await params
  return


g } from 'next/root-params'
import { cacheTag, updateTag } from 'next/cache'
import { connection } from 'next/server'
import { Suspense } from 'react'

let flagValue = false

async function getFlag() {
  'use cache: remote'
  cacheTag('flag-tag')
  return flagValue
}

async function conditionalOnAnotherCache() {
  'use cache: remote'

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

  const flag = await getFlag()
  if (flag) {
    return

lt.lang)}

{result.random}

      <form>
        <button
          id="enable-flag"
          formAction={async () => {
            'use server'
            flagValue = true
            updateTag('flag-tag')
          }}

{ lang: await lang(), random }
  }
  return { lang: null, random }
}

async function Runtime() {
  await connection()
  const result = await conditionalOnAnotherCache()

  return (

{String(resu


 { lang } from 'next/root-params'
import { connection } from 'next/server'
import { Suspense } from 'react'

async function maybeReadsRootParam(readLang: boolean) {
  'use cache: remote'

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

  if (readLang) {
    return { lang: await lang(), random }
  }

  return { lang: null, random }
}

async function Runtime() {
  await connection()

  // The `true` call must come first so that `saveToCac

{withLang.random}

{String(withLang.lang)}

{withoutLang.random}

{String(withoutLang.lang)}

  )
}

expor

heHandler` adds `lang`
  // to `knownRootParamsByFunctionId` before the `false` call's lookup runs.
  const withLang = await maybeReadsRootParam(true)
  const withoutLang = await maybeReadsRootParam(false)

  return (
    <div>


 from 'e2e-utils'

describe('tailwind-css', () => {
  const { next } = nextTestSetup({
    files: __dirname,
    dependencies: {
      autoprefixer: '10.4.19',
      postcss: '8.4.38',
      tailwindcss: '3.4.4',
    },
  })

  it('works when importing tailwind/tailwind.css', async () => {
    const browser = await next.browser('/')
    try {
      const text = await browser.elementByCss('.text-6xl').text()
      expect(text).toMatch(/Welcome to/)
      const cssBlue = await browser
        .elementByCss('#

      .getComputedCss('color')
      expect(cssBlue).toBe('rgb(37, 99, 235)')

      expect(next.cliOutput).not.toContain('Module not found')
      expect(next.cliOutput).not.toContain("Can't resolve")
    } finally {
      await browser.close()
    }
  })


 120 },
  { pathname: '/nested/c', dynamic: true, revalidate: 120 },
  { pathname: '/metadata', dynamic: true, revalidate: 120 },
  { pathname: '/on-demand/a', dynamic: true },
  { pathname: '/on-demand/b', dynamic: true },
  { pathname: '/on-demand/c', dynamic: true },
  { pathname: '/loading/a', dynamic: true, revalidate: 120 },
  { pathname: '/loading/b', dynamic: true, revalidate: 120 },
  { pathname: '/loading/c', dynamic: true, revalidate: 120 },
  { pathname: '/static', dynamic: false },
  { pathname

 '/no-suspense/nested/c', dynamic: true, emptyStaticPart: true },
  { pathname: '/dynamic/force-dynamic', dynamic: 'force-dynamic' },
  { pathname: '/dynamic/force-dynamic/nested/a', dynamic: 'force-dynamic' },
  { pathname: '/dynamic/force-dynamic/nested/

: '/no-suspense', dynamic: true, emptyStaticPart: true },
  { pathname: '/no-suspense/nested/a', dynamic: true, emptyStaticPart: true },
  { pathname: '/no-suspense/nested/b', dynamic: true, emptyStaticPart: true },
  { pathname:


const Dynamic = async ({ pathname, fallback = null, params = null }) => {
  if (fallback) {
    return
Dynamic Loading...

  }

  const headers = await next.headers()
  const messages = []
  for (const name of ['x-test-input', 'user-agent']) {
    messages.push({ name, value: headers.get(name) })
  }

  return (

      {pathname && (
        <>

Pathname

{pathname}

        </>
      )}
      {messages.map(({ name,

            Header: {name}

{value ?? `MISSING:${name.toUpperCase()}`}

        </React.Fragment>
      ))}
      {params && (
        <>

Params

          <dd data-params={JSON.

 value }) => (
        <React.Fragment key={name}>


uspense, unstable_postpone as postpone } from 'react'
import { Optimistic } from '../../../../components/optimistic'
import { ServerHtml } from '../../../../components/server-html'

export const dynamic = 'force-dynamic'

export default async (props) => {
  const searchParams = await props.searchParams
  return (
    <>
      <ServerHtml />
      <Suspense fallback="loading...">
        <Optimistic searchParams={searchParams} />
      </Suspense>
      <Suspense fallback="loading...">
        <IncidentalPos

 </Suspense>
    </>
  )
}

function IncidentalPostpone() {
  // This component will postpone but is not using
  // any dynamic APIs so we expect it to simply client render
  if (typeof window === 'undefined') {
    postpone('incidentally')
  }
  return <d


dent'
import { isNextDev, isNextStart, nextTestSetup } from 'e2e-utils'

describe('app-fetch-deduping', () => {
  if (isNextStart) {
    describe('during static generation', () => {
      const { next } = nextTestSetup({ files: __dirname, skipStart: true })
      let externalServerPort: number
      let externalServer: http.Server
      let successfulRequests = []

      beforeAll(async () => {
        externalServerPort = await findPort()
        externalServer = http.createServer((req, res) => {

tus" search param, override the response status
          if (overrideStatus) {
            res.statusCode = Number(overrideStatus)
          } else {
            successfulRequests.push(req.url)
          }

          // Generate a response with more than

 const parsedUrl = new URL(
            req.url,
            `http://localhost:${externalServerPort}`
          )
          const overrideStatus = parsedUrl.searchParams.get('status')

          // if the requested url has a "sta


nents/app-router-headers'
describe('app dir - css - experimental inline css', () => {
  const { next, isNextDev } = nextTestSetup({
    files: __dirname,
  })

  ;(isNextDev ? describe.skip : describe)('Production only', () => {
    it('should render page with correct styles', async () => {
      const browser = await next.browser('/')

      const inlineStyleTag = await browser.elementByCss('style')
      expect(await inlineStyleTag.text()).toContain('color')

      const p = await browser.elementByCss('p'

await next.fetch(`/a?${NEXT_RSC_UNION_QUERY}`, {
          method: 'GET',
          headers: {
            rsc: '1',
          },
        })
      ).text()

      const style = 'font-size'

      expect(rscPayload).toContain('__PAGE__') // sanity check

)
      expect(await p.getComputedCss('color')).toBe('rgb(255, 255, 0)') // yellow
    })

    it('should not return rsc payload with inlined style as a dynamic client nav', async () => {
      const rscPayload = await (


eerio from 'cheerio'
import { join } from 'path'
import { getCacheHeader } from 'next-test-utils'

describe('app-root-param-getters - generateStaticParams', () => {
  const { next } = nextTestSetup({
    files: join(__dirname, 'fixtures', 'generate-static-params'),
  })

  it('should be statically prerenderable', async () => {
    const params = { lang: 'en', locale: 'us' }
    const response = await next.fetch(`/${params.lang}/${params.locale}`)
    expect(response.status).toBe(200)
    expect(getCacheHead

{
    const params = { lang: 'en', locale: 'us' }
    const browser = await next.browser(
      `/${params.lang}/${params.locale}/other/1`,
      {
        // prevent streaming (dynamic) content from being inserted into the DOM
        disableJavaScript: t

er(response)).toBeOneOf(['HIT', 'PRERENDER'])
    const $ = cheerio.load(await response.text())
    expect($('p').text()).toBe(`hello world ${JSON.stringify(params)}`)
  })

  it('should be part of the static shell', async () =>


"use cache" - dev', async () => {
      const browser = await next.browser('/en/us/use-cache')
      await waitForNoRedbox(browser)
      expect(await browser.elementById('param').text()).toBe('en us')
    })

    it('should error when using root params within `unstable_cache` - dev', async () => {
      const browser = await next.browser('/en/us/unstable_cache')
      await expect(browser).toDisplayRedbox(`
       {
         "code": "E1141",
         "description": "Route /[lang]/[countryCode]/unstable_cac

g]/[countryCode]/unstable_cache/page.tsx (33:28) @ uncachedGetParams
       > 33 |   return { lang: await lang(), countryCode: await countryCode() }
            |                            ^",
         "stack": [
           "uncachedGetParams app/[lang]/[

he used \`import('next/root-params').lang()\` inside \`unstable_cache\`. This is not supported. Use \`"use cache"\` instead.",
         "environmentLabel": "Server",
         "label": "Runtime Error",
         "source": "app/[lan


uspense } from 'react'
import { lang, countryCode } from 'next/root-params'
import { connection } from 'next/server'
import { unstable_cache } from 'next/cache'

export default async function Page() {
  return (
    <Suspense fallback="...">
      <Runtime />
    </Suspense>
  )
}

async function Runtime() {
  await connection()

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

  return (
    <p

id="param">
        {rootParams.lang} {rootParams.countryCode}
      {' '}
      {data}

  )
}

const getCachedParams = unstable_cache(async function uncachedGetParams() {
  return { lang: await lang(), countryCode:


ng, countryCode } from 'next/root-params'
import { connection } from 'next/server'
import { Suspense } from 'react'

async function getCachedData() {
  'use cache'

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

  return { lang: await lang(), countryCode: await countryCode(), random }
}

export default async function Page() {
  const result = await getCachedData()

  return (
    <>

          {result

{result.random}

      <Suspense fallback={
Loading...
}>
        <Dynamic />
      </Suspense>
    </>
  )
}

async function Dynamic() {
  await connection()
  return
dynamic

}

.lang} {result.countryCode}
        </span>{' '}


          Welcome to{' '}

            Next.js!

          Get started by editing{' '}
          <code className="p-3 font-mono t

s.org/docs"
            className="p-6 mt-6 text-left border w-96 rounded-xl hover:text-blue-600 focus:text-blue-600"
          >

Documentation →

ext-lg bg-gray-100 rounded-md">
            pages/index.js
          </code>
        </p>

        <div className="flex flex-wrap items-center justify-around max-w-4xl mt-6 sm:w-full">
          <a
            href="https://nextj


uspense, unstable_postpone as postpone } from 'react'
import { Optimistic } from '../../../../components/optimistic'
import { ServerHtml } from '../../../../components/server-html'

export const dynamic = 'force-static'

export default async (props) => {
  const searchParams = await props.searchParams
  return (
    <>
      <ServerHtml />
      <Suspense fallback="loading...">
        <Optimistic searchParams={searchParams} />
      </Suspense>
      <Suspense fallback="loading...">
        <IncidentalPost

</Suspense>
    </>
  )
}

function IncidentalPostpone() {
  // This component will postpone but is not using
  // any dynamic APIs so we expect it to simply client render
  if (typeof window === 'undefined') {
    postpone('incidentally')
  }
  return <di


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

describe('css-modules-scoping', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should not prefix grid areas', async () => {
    const browser = await next.browser('/grid')

    // Check grid-area of header
    await retry(async () => {
      expect(
        await browser.eval(
          `window.getComputedStyle(document.querySelector('#header')).gridArea`
        )
      ).toBe('header')
    })

    // Check grid-area of

 main
    await retry(async () => {
      expect(
        await browser.eval(
          `window.getComputedStyle(document.querySelector('#main')).gridArea`
        )
      ).toBe('main')
    })

    // Check grid-area of footer
    await retry(async () =>

 sidebar
    await retry(async () => {
      expect(
        await browser.eval(
          `window.getComputedStyle(document.querySelector('#sidebar')).gridArea`
        )
      ).toBe('sidebar')
    })

    // Check grid-area of


outdent'
import { createRequestTracker } from '../../../lib/e2e-utils/request-tracker'

describe('app-root-param-getters - simple', () => {
  let currentCliOutputIndex = 0
  beforeEach(() => {
    resetCliOutput()
  })

  const getCliOutput = () => {
    if (next.cliOutput.length < currentCliOutputIndex) {
      // cliOutput shrank since we started the test, so something (like a `sandbox`) reset the logs
      currentCliOutputIndex = 0
    }
    return next.cliOutput.slice(currentCliOutputIndex)
  }

  cons

 reading root params', async () => {
    const params = { lang: 'en', locale: 'us' }
    const $ = await next.render$(`/${params.lang}/${params.locale}`)
    expect($('p').text()).toBe(`hello world ${JSON.stringify(params)}`)
  })

  it('should allow readi

t resetCliOutput = () => {
    currentCliOutputIndex = next.cliOutput.length
  }

  const { next, isNextDev, isTurbopack, isNextDeploy } = nextTestSetup({
    files: join(__dirname, 'fixtures', 'simple'),
  })

  it('should allow


import { Suspense } from 'react'

async function conditionalOnRootParam() {
  'use cache: remote'

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

  const currentLang = await lang()
  if (currentLang === 'en') {
    return { lang: currentLang, countryCode: await countryCode(), random }
  }
  return { lang: currentLang, countryCode: null, random }
}

async function Runtime() {
  await connection()
  const result = await conditionalOnRoot

e">{String(result.lang)}

{String(result.countryCode)}

{result.random}

  )
}

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

Param()

  return (

      <p id="lang-valu


nc function getData() {
  const responses = await Promise.all([
    fetch(`http://localhost:${process.env.TEST_SERVER_PORT}`, {
      headers: {
        traceparent: '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
        tracestate: 'vendor1=value1',
      },
    }).then((res) => res.text()),
    fetch(`http://localhost:${process.env.TEST_SERVER_PORT}`, {
      headers: {
        traceparent: '00-1af7651916cd43dd8448eb211c80319c-c7ad6b7169203332-01',
        tracestate: 'vendor2=value2',
      }

ue3',
      },
    }).then((res) => res.text()),
  ])

  return responses
}

export default async function StaticTracePage() {
  const data = await getData()

  return (

Static Page with Trace Headers

All responses should

,
    }).then((res) => res.text()),
    fetch(`http://localhost:${process.env.TEST_SERVER_PORT}`, {
      headers: {
        traceparent: '00-2af7651916cd43dd8448eb211c80319c-d7ad6b7169203333-01',
        tracestate: 'vendor3=val


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

export default async function Page() {
  const currentLang = await lang()
  const currentLocale = await locale()
  return (

        Root params are{' '}

          {currentLang} {currentLocale}

      <Suspense fallback="Loading...">
        <Timestamp />
      </Suspense>
      <form
        action={async () => {
          'use server'

ide the action result
          const cookieStore = await cookies()
          cookieStore.set('my-cookie', Date.now() + '')
        }}
      >
        <button type="submit">Submit form</button>
      </form>
    </main>
  )
}

async function Timestamp() {

// rerender the page and return it alongs