-code-frame'

describe('parse line numbers', () => {
  it('parse line numbers from code frame', () => {
    const input = {
      stackFrame: {
        file: 'app/page.tsx',
        line1: 2,
        column1: 9,
        methodName: 'Page',
        arguments: [],
        ignored: false,
      },
      //   1 | export default function Page() {
      // > 2 |   throw new Error('test error')
      //     |         ^
      //   3 |   return
hello world

      //   4 | }
      codeFrame:
        "\u001b[0m


e-label/error-type-label'

import { DialogContent } from '../../dialog'
import { styles as toolbarStyles } from '../error-overlay-toolbar/error-overlay-toolbar'
import { ErrorOverlayFooter } from '../error-overlay-footer/error-overlay-footer'
import {
  ErrorMessage,
  styles as errorMessageStyles,
} from '../error-message/error-message'
import {
  ErrorTypeLabel,
  styles as errorTypeLabelStyles,
} from '../error-type-label/error-type-label'
import {
  ErrorOverlayNav,
  styles as floatingHeaderStyles,
} f

Body, DIALOG_BODY_STYLES } from '../dialog/body'
import { OVERLAY_STYLES, ErrorOverlayOverlay } from '../overlay/overlay'
import type { ErrorBaseProps } from '../error-overlay/error-overlay'
import type { ReadyRuntimeError } from '../../../utils/get-error-

rom '../error-overlay-nav/error-overlay-nav'

import { ErrorOverlayDialog, DIALOG_STYLES } from '../dialog/dialog'
import {
  ErrorOverlayDialogHeader,
  DIALOG_HEADER_STYLES,
} from '../dialog/header'
import { ErrorOverlayDialog


seContext,
  useEffect,
  useMemo,
  useRef,
  type Ref,
  type RefObject,
} from 'react'

interface DragContextValue {
  register: (el: HTMLElement) => void
  unregister: (el: HTMLElement) => void
  handles: Set<HTMLElement>
  disabled: boolean
}

const DragContext = createContext<DragContextValue | null>(null)

export function DragProvider({
  children,
  disabled = false,
}: {
  children: React.ReactNode
  disabled?: boolean
}) {
  const handlesRef = useRef<Set<HTMLElement>>(new Set())

  const register

> ({
      register,
      unregister,
      handles:
        // eslint-disable-next-line react-hooks/refs -- TODO
        handlesRef.current,
      disabled,
    }),
    [register, unregister, disabled]
  )

  return <DragContext.Provider value={value}>{c

= useCallback((el: HTMLElement) => {
    handlesRef.current.add(el)
  }, [])

  const unregister = useCallback((el: HTMLElement) => {
    handlesRef.current.delete(el)
  }, [])

  const value = useMemo<DragContextValue>(
    () =


ntext } from '../../../../menu/context'
import { css } from '../../../../utils/css'

interface DevToolsHeaderProps {
  title: React.ReactNode
  children?: React.ReactNode
}
export function DevToolsHeader({
  title,
  children,
  ref,
}: DevToolsHeaderProps & { ref?: React.Ref<HTMLDivElement> }) {
  const { setPanel } = usePanelRouterContext()
  return (
    <div
      style={{
        width: '100%',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        paddi

 fontSize: '14px',
          color: 'var(--color-text-primary)',
          fontWeight: 'normal',
        }}
      >
        {title}
      </h3>
      {children}
      <button
        id="_next-devtools-panel-close"
        className="dev-tools-info-close-b

ng: '8px 20px',
        userSelect: 'none',
        WebkitUserSelect: 'none',
        borderBottom: '1px solid var(--color-gray-alpha-400)',
      }}
      ref={ref}
    >
      <h3
        style={{
          margin: 0,


rk-icon'
import SystemIcon from '../../../../icons/system-icon'
import { ShortcutRecorder } from './shortcut-recorder'
import { useRestartServer } from '../../error-overlay-toolbar/use-restart-server'
import { saveDevToolsConfig } from '../../../../utils/save-devtools-config'

export function UserPreferencesBody({
  theme,
  hide,
  hideShortcut,
  setHideShortcut,
  scale,
  setPosition,
  setScale,
  position,
}: {
  theme: 'dark' | 'light' | 'system'
  hide: () => void
  hideShortcut: string | null
  set

artServer, isPending } = useRestartServer()
  const { shadowRoot } = useDevOverlayContext()

  const handleThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const portal = shadowRoot.host
    if (e.target.value === 'system') {
      portal.c

HideShortcut: (value: string | null) => void
  setPosition: (position: DevToolsIndicatorPosition) => void
  position: DevToolsIndicatorPosition
  scale: DevToolsScale
  setScale: (value: DevToolsScale) => void
}) {
  const { rest


*
 * Format component stack into pseudo HTML
 * component stack is an array of strings, e.g.: ['p', 'p', 'Page', ...]
 *
 * For html tags mismatch, it will render it for the code block
 *
 * ```
 * <pre>
 *  <code>{`
 *    <Page>
 *       <p red>
 *         <p red>
 *  `}</code>
 * </pre>
 * ```
 *
 * For text mismatch, it will render it for the code block
 *
 * ```
 *

 * {`
 *   <Page>
 *

 *       "Server Text" (green)
 *       "Client Text" (red)
 *

 *   </Page>
 * `}

 *     </div>
 *   </Page>
 * `}</code>
 * ```
 *
 */
export function PseudoHtmlDiff({
  reactOutputComponentDiff,
}: {
  reactOutputComponentDiff: string
}) {
  const [isDiffCollapsed, toggleCollapseHtml] = useState(true)

  const htmlComponents = useMem

* ```
 *
 * For bad text under a tag it will render it for the code block,
 * e.g. "Mismatched Text" under <p>
 *
 * ```
 *

 * {`
 *   <Page>
 *

 *

 *         "Mismatched Text" (red)
 *


et previousBodyPaddingRight: string | undefined
let previousBodyOverflowSetting: string | undefined

let activeLocks = 0

export function lock() {
  setTimeout(() => {
    if (activeLocks++ > 0) {
      return
    }

    const scrollBarGap =
      window.innerWidth - document.documentElement.clientWidth

    if (scrollBarGap > 0) {
      previousBodyPaddingRight = document.body.style.paddingRight
      document.body.style.paddingRight = `${scrollBarGap}px`
    }

    previousBodyOverflowSetting = document.b

ndefined) {
      document.body.style.paddingRight = previousBodyPaddingRight
      previousBodyPaddingRight = undefined
    }

    if (previousBodyOverflowSetting !== undefined) {
      document.body.style.overflow = previousBodyOverflowSetting
      prev

ody.style.overflow
    document.body.style.overflow = 'hidden'
  })
}

export function unlock() {
  setTimeout(() => {
    if (activeLocks === 0 || --activeLocks !== 0) {
      return
    }

    if (previousBodyPaddingRight !== u


NT_DOCS,
  SYNC_IO_DOCS,
  getCards,
  type FixCard,
  type FixCardIcon,
  type GuidanceKind,
  type GuidanceVariant,
} from './instant-guidance-data'

export { SYNC_IO_CLIENT_DOCS, SYNC_IO_DOCS } from './instant-guidance-data'
export type { GuidanceKind, GuidanceVariant } from './instant-guidance-data'

function getCardIcon(icon: FixCardIcon) {
  switch (icon) {
    case 'align-left':
      return <FixCardAlignLeftIcon />
    case 'server-stack':
      return <FixCardServerStackIcon />
    case 'pointer-cl

g':
      return <FixCardLoadingIcon />
    case 'zap':
      return <FixCardZapIcon />
    case 'layout':
      return <FixCardLayoutIcon />
    default:
      icon satisfies never
      return null
  }
}

function CardGrid({ cards }: { cards: FixCard[] }

ick':
      return <FixCardPointerClickIcon />
    case 'history':
      return <FixCardHistoryIcon />
    case 'database':
      return <FixCardDatabaseIcon />
    case 'timer':
      return <FixCardTimerIcon />
    case 'loadin


RATION_ERROR_LINK,
} from '../../../../shared/react-19-hydration-error'
import { parseUrlFromText } from '../../../utils/parse-url-from-text'

const docsURLAllowlist = ['https://nextjs.org', 'https://react.dev']

function docsLinkMatcher(text: string): boolean {
  return docsURLAllowlist.some((url) => text.startsWith(url))
}

function getDocsURLFromErrorMessage(text: string): string | null {
  const urls = parseUrlFromText(text, docsLinkMatcher)

  if (urls.length === 0) {
    return null
  }

  const href

essage }: { errorMessage: string }) {
  const docsURL = getDocsURLFromErrorMessage(errorMessage)

  if (!docsURL) {
    return (
      <button
        title="No related documentation found"
        aria-label="No related documentation found"
        classN

= urls[0]

  // Replace react hydration error link with nextjs hydration error link
  if (href === REACT_HYDRATION_ERROR_LINK) {
    return NEXTJS_HYDRATION_ERROR_LINK
  }

  return href
}

export function DocsLinkButton({ errorM


ct'
import { ErrorOverlayPagination } from './error-overlay-pagination'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'
import { useState } from 'react'
import type { ReadyRuntimeError } from '../../../utils/get-error-by-type'

const meta: Meta<typeof ErrorOverlayPagination> = {
  component: ErrorOverlayPagination,
  parameters: {
    layout: 'centered',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorOverlayPagina

id: 2,
    runtime: true as const,
    error: new Error('Second error'),
    frames: () => Promise.resolve([]),
    type: 'runtime',
  },
  {
    id: 3,
    runtime: true as const,
    error: new Error('Third error'),
    frames: () => Promise.resolve([]),

tion>

// Mock errors for stories
const mockErrors: ReadyRuntimeError[] = [
  {
    id: 1,
    runtime: true as const,
    error: new Error('First error'),
    frames: () => Promise.resolve([]),
    type: 'runtime',
  },
  {


mport type { ReadyRuntimeError } from '../../../utils/get-error-by-type'

type ErrorPaginationProps = {
  runtimeErrors: ReadyRuntimeError[]
  activeIdx: number
  onActiveIndexChange: (index: number) => void
}

export function ErrorOverlayPagination({
  runtimeErrors,
  activeIdx,
  onActiveIndexChange,
}: ErrorPaginationProps) {
  const handlePrevious = useCallback(
    () =>
      startTransition(() => {
        if (activeIdx > 0) {
          onActiveIndexChange(Math.max(0, activeIdx - 1))
        }

ath.min(runtimeErrors.length - 1, activeIdx + 1))
          )
        }
      }),
    [activeIdx, runtimeErrors.length, onActiveIndexChange]
  )

  const buttonLeft = useRef<HTMLButtonElement | null>(null)
  const buttonRight = useRef<HTMLButtonElement | n

 }),
    [activeIdx, onActiveIndexChange]
  )

  const handleNext = useCallback(
    () =>
      startTransition(() => {
        if (activeIdx < runtimeErrors.length - 1) {
          onActiveIndexChange(
            Math.max(0, M


25H8.5V3Z"
      />
    </svg>
  )
}

export function SourceMappingErrorIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="16"
      height="16"
      viewBox="0 0 16 16"
      fill="none"


st/compiled/anser'
import stripAnsi from 'next/dist/compiled/strip-ansi'
import type { StackFrame } from '../../../shared/stack-frame'

// Strip leading spaces out of the code frame
export function formatCodeFrame(codeFrame: string) {
  const lines = codeFrame.split(/\r?\n/g)

  // Find the minimum length of leading spaces after `|` in the code frame
  const miniLeadingSpacesLength = lines
    .map((line) =>
      /^>? +\d+ +\| [ ]+/.exec(stripAnsi(line)) === null
        ? null
        : /^>? +\d+ +\| ( *)

m
  // from the code frame to help the indentation looks better when there's a lot leading spaces.
  if (miniLeadingSpacesLength > 1) {
    return lines
      .map((line, a) =>
        ~(a = line.indexOf('|'))
          ? line.substring(0, a) +

/.exec(stripAnsi(line))
    )
    .filter(Boolean)
    .map((v) => v!.pop()!)
    .reduce((c, n) => (isNaN(c) ? n.length : Math.min(c, n.length)), NaN)

  // When the minimum length of leading spaces is greater than 1, remove the


s-up'
import { ThumbsDown } from '../../../../icons/thumbs/thumbs-down'
import { cx } from '../../../../utils/cx'

interface ErrorFeedbackProps {
  errorCode: string
  className?: string
}
export function ErrorFeedback({ errorCode, className }: ErrorFeedbackProps) {
  const [votedMap, setVotedMap] = useState<Record<string, boolean>>({})
  const voted = votedMap[errorCode]
  const hasVoted = voted !== undefined
  const disabled = process.env.__NEXT_TELEMETRY_DISABLED

  const handleFeedback = useCallback(

      try {
        const response = await fetch(
          `${process.env.__NEXT_ROUTER_BASEPATH || ''}/__nextjs_error_feedback?${new URLSearchParams(
            {
              errorCode,
              wasHelpful: wasHelpful.toString(),
            }

  async (wasHelpful: boolean) => {
      // Optimistically set feedback state without loading/error states to keep implementation simple
      setVotedMap((prev) => ({
        ...prev,
        [errorCode]: wasHelpful,
      }))


ct'
import { Terminal } from './terminal'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof Terminal> = {
  component: Terminal,
  parameters: {
    layout: 'fullscreen',
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows no violation.
            // TODO: We might have setup more explicit backgrounds depending on theme.
            enabled: false,
          },

  ],
      },
    },
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof Terminal>

export const Default: Story = {
  args: {
    content: `./app/page.tsx:1:1
\x1B[31m\x1B[1mModule not found


logHeader } from '../../dialog/dialog-header'

type ErrorOverlayDialogHeaderProps = {
  children?: React.ReactNode
}

export function ErrorOverlayDialogHeader({
  children,
}: ErrorOverlayDialogHeaderProps) {
  return (
    <DialogHeader className="nextjs-container-errors-header">
      {children}
    </DialogHeader>
  )
}

export const DIALOG_HEADER_STYLES = `
  .nextjs-container-errors-header {
    position: relative;
  }
  [data-nextjs-dialog-content] > .nextjs-container-errors-header {
    margin-bottom

rors-header small {
    font-size: var(--size-14);
    color: var(--color-accents-1);
    margin-left: 16px;
  }
  .nextjs-container-errors-header small > span {
    font-family: var(--font-stack-monospace);
  }
  .nextjs-container-errors-header > div > sm

: 0;
  }
  .nextjs-container-errors-header > h1 {
    font-size: var(--size-20);
    line-height: var(--size-24);
    font-weight: bold;
    margin: calc(16px * 1.5) 0;
    color: var(--color-title-h1);
  }
  .nextjs-container-er


rver/dev/parse-version-info'
import type { DebugInfo } from '../../../../shared/types'
import { NodejsInspectorButton } from './nodejs-inspector-button'
import { CopyErrorButton } from './copy-error-button'
import { DocsLinkButton } from './docs-link-button'
import { VersionStalenessInfo } from '../../version-staleness-info/version-staleness-info'

type ErrorOverlayToolbarProps = {
  error: Error
  debugInfo: DebugInfo | undefined
  feedbackButton?: React.ReactNode
  generateErrorInfo: () => Promise<string>

olbarProps) {
  return (
    <span className="error-overlay-toolbar">
      {/* TODO: Move the button inside and remove the feedback on the footer of the error overlay.  */}
      {feedbackButton}
      <CopyErrorButton error={error} generateErrorInfo={gen

  versionInfo?: VersionInfo
  bundlerName?: 'Turbopack' | 'Webpack' | 'Rspack'
}

export function ErrorOverlayToolbar({
  error,
  debugInfo,
  feedbackButton,
  generateErrorInfo,
  versionInfo,
  bundlerName,
}: ErrorOverlayTo


,
  disableDrag = false,
  avoidZone,
  ...props
}: {
  children: React.ReactElement
  position: Corners
  padding: number
  setPosition: (position: Corners) => void
  onDragStart?: () => void
  dragHandleSelector?: string
  disableDrag?: boolean
  style?: React.CSSProperties
  avoidZone?: {
    square: number
    corner: Corners
    padding: number
  }
}) {
  const { ref, animate, ...drag } = useDrag({
    disabled: disableDrag,
    handles: useDragContext()?.handles,
    threshold: 5,
    onDragStart,

   if (distance === 0) {
      ref.current?.style.removeProperty('translate')
      return
    }

    const projectedPosition = {
      x: translation.x + project(velocity.x),
      y: translation.y + project(velocity.y),
    }
    const nearestCorner = ge

 onDragEnd,
    onAnimationEnd,
    dragHandleSelector,
  })

  function onDragEnd(translation: Point, velocity: Point) {
    const distance = Math.sqrt(
      translation.x * translation.x + translation.y * translation.y
    )


ristine, setPristine] = useState(true)
  const [show, setShow] = useState(false)
  const [keys, setKeys] = useState<string[]>(value ?? [])
  const [success, setSuccess] = useState<boolean>(false)
  const timeoutRef = useRef<number | null>(null)
  const buttonRef = useRef<HTMLButtonElement>(null)
  const hasShortcut = Boolean(value) || keys.length > 0

  function handleKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {
    // Don't handle events from the Clear button
    if (e.target !== buttonRef.current)

g session
    if (pristine) {
      setKeys([])
      setPristine(false)
    }

    function handleValidation(next: string[]) {
      timeoutRef.current = window.setTimeout(() => {
        setSuccess(true)
        onChange(next.join('+'))
        timeoutRe

 return
    if (e.key === 'Tab') return
    if (timeoutRef.current) clearTimeout(timeoutRef.current)

    if (!show) {
      setShow(true)
    }

    // Reset current shortcut on first key press
    // if this is a fresh recordin


types'
import type { VersionInfo } from '../../../../../server/dev/parse-version-info'

import { ErrorOverlayPagination } from '../error-overlay-pagination/error-overlay-pagination'
import { ErrorOverlayToolbar } from '../error-overlay-toolbar/error-overlay-toolbar'
import type { ReadyRuntimeError } from '../../../utils/get-error-by-type'

type ErrorOverlayNavProps = {
  runtimeErrors?: ReadyRuntimeError[]
  activeIdx?: number
  setActiveIndex?: (index: number) => void
  versionInfo?: VersionInfo
  error: R

rInfo,
}: ErrorOverlayNavProps) {
  const bundlerName = (process.env.__NEXT_BUNDLER || 'Turbopack') as
    | 'Turbopack'
    | 'Webpack'
    | 'Rspack'
  return (
    <div data-nextjs-error-overlay-nav>
      <NavItem side="left">
        {/* TODO: better

eadyRuntimeError['error']
  debugInfo?: DebugInfo
  generateErrorInfo: () => Promise<string>
}

export function ErrorOverlayNav({
  runtimeErrors,
  activeIdx,
  setActiveIndex,
  versionInfo,
  error,
  debugInfo,
  generateErro


hared/stack-frame'

import { CallStackFrame } from '../call-stack-frame/call-stack-frame'
import { ChevronUpDownIcon } from '../../icons/chevron-up-down'
import { css } from '../../utils/css'

export function CallStack({
  frames,
  isIgnoreListOpen,
  ignoredFramesTally,
  onToggleIgnoreList,
}: {
  frames: readonly OriginalStackFrame[]
  isIgnoreListOpen: boolean
  ignoredFramesTally: number
  onToggleIgnoreList: () => void
}) {
  return (
    <div data-nextjs-call-stack-container>
      <div data-nextjs-

gnoreListOpen value is used by tests to confirm whether it is open or not.
            data-nextjs-call-stack-ignored-list-toggle-button={isIgnoreListOpen}
            onClick={onToggleIgnoreList}
          >
            {`${isIgnoreListOpen ? 'Hide' : 'Sh

call-stack-header>
        <p data-nextjs-call-stack-title>
          Call Stack <span data-nextjs-call-stack-count>{frames.length}</span>
        </p>
        {ignoredFramesTally > 0 && (
          <button
            // The isI


ErrorType =
  | 'Build Error'
  | `Runtime ${string}`
  | `Console ${string}`
  | `Recoverable ${string}`
  | 'Blocking Route'
  | 'Ambiguous Metadata'
  | 'Instant'

type ErrorTypeLabelProps = {
  errorType: ErrorType
}

export function ErrorTypeLabel({ errorType }: ErrorTypeLabelProps) {
  return (
    <span
      id="nextjs__container_errors_label"
      className={`nextjs__container_errors_label ${errorType === 'Blocking Route' || errorType === 'Ambiguous Metadata' ? 'nextjs__container_errors_label_bloc

: 0;
    border-radius: var(--rounded-md-2);
    background: var(--color-red-100);
    font-weight: 600;
    font-size: var(--size-12);
    color: var(--color-red-900);
    font-family: var(--font-stack-monospace);
    line-height: var(--size-20);
  }

  .

king_page' : ''} ${errorType === 'Instant' ? 'nextjs__container_errors_label_instant' : ''}`}
    >
      {errorType}
    </span>
  )
}

export const styles = `
  .nextjs__container_errors_label {
    padding: 2px 6px;
    margin


et-next-error-feedback-middleware'
import type { IncomingMessage, ServerResponse } from 'http'

describe('getNextErrorFeedbackMiddleware', () => {
  const mockTelemetry = {
    record: jest.fn().mockResolvedValue({}),
  } as any

  let mockReq: Partial<IncomingMessage>
  let mockRes: Partial<ServerResponse>
  let mockNext: jest.Mock

  beforeEach(() => {
    mockReq = {
      url: '/__nextjs_error_feedback?errorCode=TEST_ERROR&wasHelpful=true',
    }
    mockRes = {
      setHeader: jest.fn(),
      end: je

 mockReq as IncomingMessage,
      mockRes as ServerResponse,
      mockNext
    )

    expect(mockNext).toHaveBeenCalled()
    expect(mockTelemetry.record).not.toHaveBeenCalled()
  })

  it('records telemetry when feedback is submitted', async () => {

st.fn(),
    }
    mockNext = jest.fn()
    jest.clearAllMocks()
  })

  it('calls next() if path does not match', async () => {
    mockReq.url = '/some-other-path'

    await getNextErrorFeedbackMiddleware(mockTelemetry)(


ates the detection helper added in
// https://github.com/vercel/next.js/pull/91854 (branch: imm/agent-telemetry).
// When that PR lands, whichever branch merges second should collapse the
// duplicate — the contents are intentionally identical so the merge is a
// no-op. The env variable checks mirror the same functionality in the
// `vercel` CLI.

export type AgentName =
  | 'cursor'
  | 'cursor-cli'
  | 'claude'
  | 'cowork'
  | 'devin'
  | 'replit'
  | 'gemini'
  | 'codex'
  | 'antigravity'
  | 'augment-

lot-cli' ? 'github-copilot' : name
      return normalized as AgentName
    }
  }

  if (process.env.CURSOR_TRACE_ID) {
    return 'cursor'
  }

  if (process.env.CURSOR_AGENT) {
    return 'cursor-cli'
  }

  if (process.env.GEMINI_CLI) {
    return 'gemi

cli'
  | 'opencode'
  | 'github-copilot'

export function detectAgent(): AgentName | null {
  if (process.env.AI_AGENT) {
    const name = process.env.AI_AGENT.trim()
    if (name) {
      const normalized = name === 'github-copi


lemetry-payload'

describe('postNextTelemetryPayload', () => {
  let originalFetch: typeof fetch

  beforeEach(() => {
    originalFetch = global.fetch
  })

  afterEach(() => {
    global.fetch = originalFetch
  })

  it('sends telemetry payload successfully', async () => {
    const mockFetch = jest.fn().mockResolvedValue({
      ok: true,
    })
    global.fetch = mockFetch

    const payload = {
      meta: { version: '1.0' },
      context: {
        anonymousId: 'test-id',
        projectId: 'test-pro

mockFetch).toHaveBeenCalledWith(
      'https://telemetry.nextjs.org/api/v1/record',
      {
        method: 'POST',
        body: JSON.stringify(payload),
        headers: { 'content-type': 'application/json' },
        signal: expect.any(AbortSignal),

ject',
        sessionId: 'test-session',
      },
      events: [
        {
          eventName: 'test-event',
          fields: { foo: 'bar' },
        },
      ],
    }

    await postNextTelemetryPayload(payload)

    expect(


ort retry from 'next/dist/compiled/async-retry'

interface Payload {
  meta: { [key: string]: unknown }

  context: {
    anonymousId: string
    projectId: string
    sessionId: string
  }

  events: Array<{
    eventName: string
    fields: object
  }>
}

export function postNextTelemetryPayload(payload: Payload, signal?: any) {
  if (!signal && 'timeout' in AbortSignal) {
    signal = AbortSignal.timeout(5000)
  }
  return (
    retry(
      () =>
        fetch('https://telemetry.nextjs.org/api/v1/record

Error(res.statusText)
            ;(err as any).response = res
            throw err
          }
        }),
      { minTimeout: 500, retries: 1, factor: 1 }
    )
      .catch(() => {
        // We swallow errors when telemetry cannot be sent
      })

', {
          method: 'POST',
          body: JSON.stringify(payload),
          headers: { 'content-type': 'application/json' },
          signal,
        }).then((res) => {
          if (!res.ok) {
            const err = new


c } from 'child_process'

// Q: Why does Next.js need a project ID? Why is it looking at my git remote?
// A:
// Next.js' telemetry is and always will be completely anonymous. Because of
// this, we need a way to differentiate different projects to track feature
// usage accurately. For example, to prevent a feature from appearing to be
// constantly `used` and then `unused` when switching between local projects.
// To reiterate,
// we **never** can read your actual git remote. The value is hashed one-way
/

 Error) => void
    const promise = new Promise<Buffer | string>((res, rej) => {
      resolve = res
      reject = rej
    })

    exec(
      `git config --local --get remote.origin.url`,
      {
        timeout: 1000,
        windowsHide: true,
      },

/ with random salt data, making it impossible for us to reverse or try to
// guess the remote by re-computing hashes.

async function _getProjectIdByGit() {
  try {
    let resolve: (value: Buffer | string) => void, reject: (err:


d } from './post-telemetry-payload'
import { getRawProjectId } from './project-id'
import { AbortController } from 'next/dist/compiled/@edge-runtime/ponyfill'
import fs from 'fs'

// This is the key that stores whether or not telemetry is enabled or disabled.
const TELEMETRY_KEY_ENABLED = 'telemetry.enabled'

// This is the key that specifies when the user was informed about anonymous
// telemetry collection.
const TELEMETRY_KEY_NOTIFY_DATE = 'telemetry.notifiedAt'

// This is a quasi-persistent identifier

 salt value is never sent to us, ensuring privacy and the one-way nature
// of the hash (prevents dictionary lookups of pre-computed hashes).
// See the `oneWayHash` function.
const TELEMETRY_KEY_SALT = `telemetry.salt`

export type TelemetryEvent = { even

used to dedupe recurring events. It's
// generated from random data and completely anonymous.
const TELEMETRY_KEY_ID = `telemetry.anonymousId`

// This is the cryptographic salt that is included within every hashed value.
// This


ATURE_USAGE,
  eventBuildFeatureUsageFromTurbopack,
} from './build'

describe('eventBuildFeatureUsageFromTurbopackDiagnostics', () => {
  it('returns empty for empty input', () => {
    expect(eventBuildFeatureUsageFromTurbopack([])).toEqual([])
  })

  it('maps a single diagnostic to one event', () => {
    const events = eventBuildFeatureUsageFromTurbopack([
      { featureName: 'next/image', invocationCount: 3 },
    ])
    expect(events).toEqual([
      {
        eventName: EVENT_BUILD_FEATURE_USAGE,

eatureName: 'swcRelay', invocationCount: 0 },
    ])
    expect(events).toEqual([
      {
        eventName: EVENT_BUILD_FEATURE_USAGE,
        payload: { featureName: 'swcRelay', invocationCount: 0 },
      },
    ])
  })

  it('passes through multiple di

       payload: { featureName: 'next/image', invocationCount: 3 },
      },
    ])
  })

  it('preserves invocationCount of 0 for disabled boolean flags', () => {
    const events = eventBuildFeatureUsageFromTurbopack([
      { f


_DUNDER =
  /[\\/]__[^\\/]+(?<![\\/]__(?:tests|mocks))__[\\/]/i
const REGEXP_DIRECTORY_TESTS = /[\\/]__(tests|mocks)__[\\/]/i
const REGEXP_FILE_TEST = /\.(?:spec|test)\.[^.]+$/i

const EVENT_TYPE_CHECK_COMPLETED = 'NEXT_TYPE_CHECK_COMPLETED'
type EventTypeCheckCompleted = {
  durationInSeconds: number
  typescriptVersion: string | null
  inputFilesCount?: number
  totalFilesCount?: number
  incremental?: boolean
}

export function eventTypeCheckCompleted(event: EventTypeCheckCompleted): {
  eventName: strin

rationInSeconds: number
  eslintVersion: string | null
  lintedFilesCount?: number
  lintFix?: boolean
  buildLint?: boolean
  nextEslintPluginVersion?: string | null
  nextEslintPluginErrorsCount?: number
  nextEslintPluginWarningsCount?: number
  nextRul

g
  payload: EventTypeCheckCompleted
} {
  return {
    eventName: EVENT_TYPE_CHECK_COMPLETED,
    payload: event,
  }
}

const EVENT_LINT_CHECK_COMPLETED = 'NEXT_LINT_CHECK_COMPLETED'
export type EventLintCheckCompleted = {
  du


mport type { McpToolName } from './build'

describe('MCP Telemetry Events', () => {
  it('should generate correct telemetry events for single tool', () => {
    const usages = [
      {
        featureName: 'mcp/get_errors' as McpToolName,
        invocationCount: 5,
      },
    ]

    const events = eventMcpToolUsage(usages)

    expect(events).toHaveLength(1)
    expect(events[0]).toEqual({
      eventName: EVENT_MCP_TOOL_USAGE,
      payload: {
        toolName: 'mcp/get_errors',
        invocationCount

{
        featureName: 'mcp/get_logs' as McpToolName,
        invocationCount: 1,
      },
      {
        featureName: 'mcp/get_page_metadata' as McpToolName,
        invocationCount: 7,
      },
    ]

    const events = eventMcpToolUsage(usages)

    ex

: 5,
      },
    })
  })

  it('should generate correct telemetry events for multiple tools', () => {
    const usages = [
      {
        featureName: 'mcp/get_errors' as McpToolName,
        invocationCount: 3,
      },


ort findUp from 'next/dist/compiled/find-up'

const EVENT_PLUGIN_PRESENT = 'NEXT_PACKAGE_DETECTED'
type NextPluginsEvent = {
  eventName: string
  payload: {
    packageName: string
    packageVersion: string
  }
}

export async function eventNextPlugins(
  dir: string
): Promise<Array<NextPluginsEvent>> {
  try {
    const packageJsonPath = await findUp('package.json', { cwd: dir })
    if (!packageJsonPath) {
      return []
    }

    const { dependencies = {}, devDependencies = {} } = require(packageJso

n't add deps without a version set
        if (!version) {
          return events
        }

        events.push({
          eventName: EVENT_PLUGIN_PRESENT,
          payload: {
            packageName: plugin,
            packageVersion: version,

nPath)

    const deps = { ...devDependencies, ...dependencies }

    return Object.keys(deps).reduce(
      (events: NextPluginsEvent[], plugin: string): NextPluginsEvent[] => {
        const version = deps[plugin]
        // Do


EVENT_VERSION = 'NEXT_CLI_SESSION_STOPPED'

export type EventCliSessionStopped = {
  cliCommand: string
  nextVersion: string
  nodeVersion: string
  turboFlag?: boolean | null
  durationMilliseconds?: number | null
  pagesDir?: boolean
  appDir?: boolean
  isRspack: boolean
}

export function eventCliSessionStopped(
  event: Omit<
    EventCliSessionStopped,
    'nextVersion' | 'nodeVersion' | 'isRspack'
  >
): { eventName: string; payload: EventCliSessionStopped }[] {
  // This should be an invariant, if

rsion,
    cliCommand: event.cliCommand,
    durationMilliseconds: event.durationMilliseconds,
    ...(typeof event.turboFlag !== 'undefined'
      ? {
          turboFlag: !!event.turboFlag,
        }
      : {}),
    pagesDir: event.pagesDir,
    appDir:

it fails our build tooling is broken.
  if (typeof process.env.__NEXT_VERSION !== 'string') {
    return []
  }

  const payload: EventCliSessionStopped = {
    nextVersion: process.env.__NEXT_VERSION,
    nodeVersion: process.ve


rom '../../trace/shared'
import type { Telemetry } from '../storage'
// @ts-ignore JSON
import { optionalDependencies } from 'next/package.json'

const EVENT_PLUGIN_PRESENT = 'NEXT_SWC_LOAD_FAILURE'
export type EventSwcLoadFailure = {
  eventName: string
  payload: {
    platform: string
    arch: string
    nodeVersion: string
    nextVersion: string
    wasm?: 'enabled' | 'fallback' | 'failed'
    glibcVersion?: string
    installedSwcPackages?: string
    nativeBindingsErrorCode?: string
  }
}

export as

't continue if telemetry isn't set
  if (!telemetry) return

  let glibcVersion
  let installedSwcPackages

  try {
    // @ts-ignore
    glibcVersion = process.report?.getReport().header.glibcVersionRuntime
  } catch {}

  try {
    const pkgNames = Objec

ync function eventSwcLoadFailure(
  event?: Pick<
    EventSwcLoadFailure['payload'],
    'wasm' | 'nativeBindingsErrorCode'
  >
): Promise<void> {
  const telemetry: Telemetry | undefined = traceGlobals.get('telemetry')
  // can


p from 'next/dist/compiled/find-up'
import path from 'path'
import fs from 'fs'
import type { NextConfig } from '../../server/config-shared'

const EVENT_SWC_PLUGIN_PRESENT = 'NEXT_SWC_PLUGIN_DETECTED'
type SwcPluginsEvent = {
  eventName: string
  payload: {
    pluginName: string
    pluginVersion?: string
  }
}

export async function eventSwcPlugins(
  dir: string,
  config: NextConfig
): Promise<Array<SwcPluginsEvent>> {
  try {
    const packageJsonPath = await findUp('package.json', { cwd: dir })

experimental?.swcPlugins?.map(([name, _]) => name) ?? []

    return swcPluginPackages.map((plugin) => {
      // swc plugins can be non-npm pkgs with absolute path doesn't have version
      const version = deps[plugin] ?? undefined
      let pluginName =

if (!packageJsonPath) {
      return []
    }

    const { dependencies = {}, devDependencies = {} } = require(packageJsonPath)

    const deps = { ...devDependencies, ...dependencies }
    const swcPluginPackages =
      config.


SESSION_STARTED'

type EventCliSessionStarted = {
  nextVersion: string
  nodeVersion: string
  cliCommand: string
  isSrcDir: boolean | null
  hasNowJson: boolean
  isCustomServer: boolean | null
  hasNextConfig: boolean
  buildTarget: string
  hasWebpackConfig: boolean
  hasBabelConfig: boolean
  basePathEnabled: boolean
  i18nEnabled: boolean
  imageEnabled: boolean
  imageFutureEnabled: boolean
  locales: string | null
  localeDomainsCount: number | null
  localeDetectionEnabled: boolean | null
  imageD

  nextConfigOutput: string | null
  trailingSlashEnabled: boolean
  reactStrictMode: boolean
  webpackVersion: number | null
  turboFlag: boolean
  useTurbopackWorkerAssetPrefix: boolean
  isRspack: boolean
  appDir: boolean | null
  pagesDir: boolean | nu

omainsCount: number | null
  imageRemotePatternsCount: number | null
  imageLocalPatternsCount: number | null
  imageQualities: string | null
  imageSizes: string | null
  imageLoader: string | null
  imageFormats: string | null


  q: number
  token: string
}

interface Options {
  prefixMatch?: boolean
  type: 'accept-language'
}

function parse(
  raw: string,
  preferences: readonly string[] | undefined,
  options: Options
) {
  const lowers = new Map<string, { orig: string; pos: number }>()
  const header = raw.replace(/[ \t]/g, '')

  if (preferences) {
    let pos = 0
    for (const preference of preferences) {
      const lower = preference.toLowerCase()
      lowers.set(lower, { orig: preference, pos: pos++ })
      if (opt

ference, pos: pos++ })
          }
        }
      }
    }
  }

  const parts = header.split(',')
  const selections: Selection[] = []
  const map = new Set<string>()

  for (let i = 0; i < parts.length; ++i) {
    const part = parts[i]
    if (!part) {

ions.prefixMatch) {
        const parts = lower.split('-')
        while ((parts.pop(), parts.length > 0)) {
          const joined = parts.join('-')
          if (!lowers.has(joined)) {
            lowers.set(joined, { orig: pre


tils/get-next-pathname-info'
import {
  RSC_HEADER,
  NEXT_RSC_UNION_QUERY,
  NEXT_ROUTER_PREFETCH_HEADER,
  NEXT_ROUTER_SEGMENT_PREFETCH_HEADER,
  NEXT_URL,
  NEXT_ROUTER_STATE_TREE_HEADER,
  NEXT_INSTANT_TEST_COOKIE,
} from '../client/components/app-router-headers'
import type {
  MatchOptions,
  RouteMatcherManager,
} from './route-matcher-managers/route-matcher-manager'
import { LocaleRouteNormalizer } from './normalizers/locale-route-normalizer'
import { DefaultRouteMatcherManager } from './route-match

route-matcher-provider'
import { PagesAPIRouteMatcherProvider } from './route-matcher-providers/pages-api-route-matcher-provider'
import { PagesRouteMatcherProvider } from './route-matcher-providers/pages-route-matcher-provider'
import { ServerManifestLoad

er-managers/default-route-matcher-manager'
import { AppPageRouteMatcherProvider } from './route-matcher-providers/app-page-route-matcher-provider'
import { AppRouteRouteMatcherProvider } from './route-matcher-providers/app-route-


h } from 'stream'
import bytes from 'next/dist/compiled/bytes'

const DEFAULT_BODY_CLONE_SIZE_LIMIT = 10 * 1024 * 1024 // 10MB

export function requestToBodyStream(
  context: { ReadableStream: typeof ReadableStream },
  KUint8Array: typeof Uint8Array,
  stream: Readable
) {
  return new context.ReadableStream({
    start: async (controller) => {
      for await (const chunk of stream) {
        controller.enqueue(new KUint8Array(chunk))
      }
      controller.close()
    },
  })
}

function replaceReques

 keyof T] = v
  }

  return base
}

export interface CloneableBody {
  finalize(): Promise<void>
  cloneBodyStream(): Readable
}

export function getCloneableBody<T extends IncomingMessage>(
  readable: T,
  sizeLimit?: number
): CloneableBody {
  let buff

tBody<T extends IncomingMessage>(
  base: T,
  stream: Readable
): T {
  for (const key in stream) {
    let v = stream[key as keyof Readable] as any
    if (typeof v === 'function') {
      v = v.bind(base)
    }
    base[key as


port path from 'path'
import isDockerFunction from 'next/dist/compiled/is-docker'

export function getStorageDirectory(distDir: string): string | undefined {
  // Allow CI environments (notably the Microsoft Playwright Docker image used
  // by our own CI) to opt out of the Docker auto-detection. The detection
  // assumes ephemeral storage and short-circuits to `undefined`, which forces
  // preview/server-action keys to be regenerated on every build and breaks
  // tests asserting determinism between cons

 in the same
  // workspace.
  if (process.env.NEXT_IGNORE_IS_DOCKER === '1') {
    return path.join(distDir, 'cache')
  }

  const isLikelyEphemeral = isDockerFunction()

  if (isLikelyEphemeral) {
    return undefined
  }
  return path.join(distDir, 'cac


le } from './route-modules/app-page/module'

// Combined load times for loading client components
let clientComponentLoadStart = 0
let clientComponentLoadTimes = 0
let clientComponentLoadCount = 0

export function wrapClientComponentLoader(
  ComponentMod: AppPageModule,
  isTracingEnabled: boolean
): AppPageModule['__next_app__'] {
  if (
    !('performance' in globalThis) ||
    (!process.env.NEXT_OTEL_PERFORMANCE_PREFIX && !isTracingEnabled)
  ) {
    return ComponentMod.__next_app__
  }

  return {

 ComponentMod.__next_app__.require(...args)
      } finally {
        clientComponentLoadTimes += performance.now() - startTime
      }
    },
    loadChunk: (...args) => {
      const startTime = performance.now()
      const result = ComponentMod.__next_

require: (...args) => {
      const startTime = performance.now()

      if (clientComponentLoadStart === 0) {
        clientComponentLoadStart = startTime
      }

      try {
        clientComponentLoadCount += 1
        return


tring') {
    return true
  }
  return false
})

const zExportMap: zod.ZodType<ExportPathMap> = z.record(
  z.string(),
  z.object({
    page: z.string(),
    query: z.any(), // NextParsedUrlQuery

    // private optional properties
    _fallbackRouteParams: z.array(z.any()).optional(),
    _isAppDir: z.boolean().optional(),
    _isDynamicError: z.boolean().optional(),
    _isRoutePPREnabled: z.boolean().optional(),
    _allowEmptyStaticShell: z.boolean().optional(),
  })
)

const zRouteHas: zod.ZodType<Rou

 z.string(),
  }),
])

const zRewrite: zod.ZodType<Rewrite> = z.object({
  source: z.string(),
  destination: z.string(),
  basePath: z.literal(false).optional(),
  locale: z.literal(false).optional(),
  has: z.array(zRouteHas).optional(),
  missing: z.arr

teHas> = z.union([
  z.object({
    type: z.enum(['header', 'query', 'cookie']),
    key: z.string(),
    value: z.string().optional(),
  }),
  z.object({
    type: z.literal('host'),
    key: z.undefined().optional(),
    value:


sFileNames?: string[]
  minify?: boolean
  transpileTemplateLiterals?: boolean
  namespace?: string
  pure?: boolean
  cssProp?: boolean
}

export type JSONValue =
  | string
  | number
  | boolean
  | JSONValue[]
  | { [k: string]: JSONValue }

// At the moment, Turbopack options must be JSON-serializable, so restrict values.
export type TurbopackLoaderOptions = Record<string, JSONValue>

export type TurbopackLoaderItem =
  | string
  | {
      loader: string
      options?: TurbopackLoaderOptions
    }

e

ackRuleCondition[] }
  | { not: TurbopackRuleCondition }
  | TurbopackLoaderBuiltinCondition
  | {
      path?: string | RegExp
      content?: RegExp
      query?: string | RegExp
      contentType?: string | RegExp
    }

/**
 * The module type to use fo

xport type TurbopackLoaderBuiltinCondition =
  | 'browser'
  | 'foreign'
  | 'development'
  | 'production'
  | 'node'
  | 'edge-light'

export type TurbopackRuleCondition =
  | { all: TurbopackRuleCondition[] }
  | { any: Turbop


  installed = true

  // hook the Node.js require so that webpack requires are
  // routed to the bundled and now initialized webpack version
  ;(
    require('../server/require-hook') as typeof import('../server/require-hook')
  ).addHookAliases(
    [
      ['webpack', 'next/dist/compiled/webpack/webpack-lib'],
      ['webpack/package', 'next/dist/compiled/webpack/package'],
      ['webpack/package.json', 'next/dist/compiled/webpack/package'],
      ['webpack/lib/webpack', 'next/dist/compiled/webpack/webp

 'webpack/lib/node/NodeEnvironmentPlugin.js',
        'next/dist/compiled/webpack/NodeEnvironmentPlugin',
      ],
      [
        'webpack/lib/BasicEvaluatedExpression',
        'next/dist/compiled/webpack/BasicEvaluatedExpression',
      ],
      [

ack-lib'],
      ['webpack/lib/webpack.js', 'next/dist/compiled/webpack/webpack-lib'],
      [
        'webpack/lib/node/NodeEnvironmentPlugin',
        'next/dist/compiled/webpack/NodeEnvironmentPlugin',
      ],
      [


nsure each test gets a fresh config load
    // This is important because config.ts now has a module-level configCache
    jest.resetModules()

    // Dynamically import the module after reset to get a fresh instance
    const configModule = await import('./config')
    loadConfig = configModule.default
  })
  describe('nextConfig.images defaults', () => {
    it('should assign a `images.remotePatterns` when using assetPrefix', async () => {
      const result = await loadConfig(PHASE_PRODUCTION_BUILD, __di

eSnapshot(`
        [
          {
            "hostname": "cdn.example.com",
            "port": "",
            "protocol": "https",
          },
        ]
      `)
    })

    it('should not assign a duplicate `images.remotePatterns` value when using ass

rname, {
        customConfig: {
          assetPrefix: 'https://cdn.example.com',
          images: {
            formats: ['image/webp'],
          },
        },
      })

      expect(result.images.remotePatterns).toMatchInlin


i-reference/config/next-config-js/turbopackFileSystemCache'
      } else if (message.includes('turbopackPersistentCaching')) {
        // We exit the build when encountering an error in the turbopackPersistentCaching config
        shouldExit = true
        message +=
          "\nUse 'experimental.turbopackFileSystemCacheForDev' instead."
        message +=
          '\nLearn more: https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopackFileSystemCache'
      } else if (message.includes('d

: https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents'
      }
    }

    if (shouldExit) {
      fatalErrors.push(message)
    } else {
      warnings.push(message)
    }
  }

  return [warnings, fatalErrors]
}

export function

ynamicIO')) {
        shouldExit = true
        message +=
          '\n`experimental.dynamicIO` has been replaced by `cacheComponents`. Please update your next.config file accordingly.'
        message +=
          '\nLearn more


urrent: null | Error } = { current: null }

// React.cache is currently only available in canary/experimental React channels.
const cache =
  typeof React.cache === 'function'
    ? React.cache
    : (fn: (key: unknown) => void) => fn

// When Cache Components is enabled, we record these as errors so that they
// are captured by the dev overlay as it's more critical to fix these
// when enabled.
const logErrorOrWarn = process.env.__NEXT_CACHE_COMPONENTS
  ? console.error
  : console.warn

// We don't want t

ars -- cache key
  (key: unknown) => {
    try {
      logErrorOrWarn(errorRef.current)
    } finally {
      errorRef.current = null
    }
  }
)

/**
 * Creates a function that logs an error message that is deduped by the userland
 * callsite.
 * This req

o dedupe across requests.
// The developer might've just attempted to fix the warning so we should warn again if it still happens.
const flushCurrentErrorIfNew = cache(
  // eslint-disable-next-line @typescript-eslint/no-unused-v


s://security.stackexchange.com/questions/184305/why-would-i-ever-use-aes-256-cbc-if-aes-256-gcm-is-more-secure

const CIPHER_ALGORITHM = `aes-256-gcm`,
  CIPHER_KEY_LENGTH = 32, // https://stackoverflow.com/a/28307668/4397028
  CIPHER_IV_LENGTH = 16, // https://stackoverflow.com/a/28307668/4397028
  CIPHER_TAG_LENGTH = 16,
  CIPHER_SALT_LENGTH = 64

const PBKDF2_ITERATIONS = 100_000 // https://support.1password.com/pbkdf2/

export function encryptWithSecret(secret: Buffer, data: string): string {
  const iv

c(
    secret,
    salt,
    PBKDF2_ITERATIONS,
    CIPHER_KEY_LENGTH,
    `sha512`
  )

  const cipher = crypto.createCipheriv(CIPHER_ALGORITHM, key, iv)
  const encrypted = Buffer.concat([cipher.update(data, `utf8`), cipher.final()])

  // https://nodejs

 = crypto.randomBytes(CIPHER_IV_LENGTH)
  const salt = crypto.randomBytes(CIPHER_SALT_LENGTH)

  // https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2sync_password_salt_iterations_keylen_digest
  const key = crypto.pbkdf2Syn


ender/work-unit-async-storage.external'
import { workUnitAsyncStorage } from './app-render/work-unit-async-storage.external'
import { getServerReact, getClientReact } from './runtime-reacts.external'

export function isHangingPromiseRejectionError(
  err: unknown
): err is HangingPromiseRejectionError {
  if (typeof err !== 'object' || err === null || !('digest' in err)) {
    return false
  }

  return err.digest === HANGING_PROMISE_REJECTION
}

const HANGING_PROMISE_REJECTION = 'HANGING_PROMISE_REJECTION'

erendering, ${expression} rejects when the prerender is complete. Typically these errors are handled by React but if you move ${expression} to a different context by using \`setTimeout\`, \`after\`, or similar functions you may observe this error and you s

class HangingPromiseRejectionError extends Error {
  public readonly digest = HANGING_PROMISE_REJECTION

  constructor(
    public readonly route: string,
    public readonly expression: string
  ) {
    super(
      `During pr


FONT,
  DEFAULT_SANS_SERIF_FONT,
} from '../shared/lib/constants'
const capsizeFontsMetrics = require('next/dist/server/capsize-font-metrics.json')

function formatName(str: string): string {
  return str
    .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
      return index === 0 ? word.toLowerCase() : word.toUpperCase()
    })
    .replace(/\s+/g, '')
}

function formatOverrideValue(val: number) {
  return Math.abs(val * 100).toFixed(2)
}

export function calculateSizeAdjustValues(fontName: strin

m
  const fallbackFont =
    category === 'serif' ? DEFAULT_SERIF_FONT : DEFAULT_SANS_SERIF_FONT
  const fallbackFontName = formatName(fallbackFont.name)
  const fallbackFontMetrics = capsizeFontsMetrics[fallbackFontName]
  const fallbackFontAvgWidth =

g) {
  const fontKey = formatName(fontName)
  const fontMetrics = capsizeFontsMetrics[fontKey]
  let { category, ascent, descent, lineGap, unitsPerEm, xWidthAvg } =
    fontMetrics
  const mainFontAvgWidth = xWidthAvg / unitsPerE


mport { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'

export type BuildManifest = {
  devFiles: readonly string[]
  polyfillFiles: readonly string[]
  lowPriorityFiles: readonly string[]
  rootMainFiles: readonly string[]
  // this is a separate field for flying shuttle to allow
  // different root main files per entries/build (ideally temporary)
  // until we can stitch the runtime chunks together

ring
): readonly string[] {
  const normalizedPage = denormalizePagePath(normalizePagePath(page))
  let files = buildManifest.pages[normalizedPage]

  if (!files) {
    console.warn(
      `Could not find files for ${normalizedPage} in .next/build-manifest

safely
  rootMainFilesTree: { [appRoute: string]: readonly string[] }
  pages: {
    '/_app': readonly string[]
    [page: string]: readonly string[]
  }
}

export function getPageFiles(
  buildManifest: BuildManifest,
  page: st


port React from 'react'

type PagesDevOverlayErrorBoundaryProps = {
  children?: React.ReactNode
}
type PagesDevOverlayErrorBoundaryState = {
  hasError: boolean
}

export class PagesDevOverlayErrorBoundary extends React.PureComponent<
  PagesDevOverlayErrorBoundaryProps,
  PagesDevOverlayErrorBoundaryState
> {
  state = { hasError: false }

  static getDerivedStateFromError(
    _: unknown
  ): Partial<PagesDevOverlayErrorBoundaryState> {
    return { hasError: true }
  }

  // Explicit type is needed to a

ated `.d.ts` having a wide return type that could be specific to the `@types/react` version.
  render(): React.ReactNode {
    // The component has to be unmounted or else it would continue to error
    return this.state.hasError ? null : this.props.childr


from '../shared/lib/invariant-error'
import { lookup } from 'dns/promises'
import { isIP } from 'net'
import { ALL } from 'dns'

type XCacheHeader = 'MISS' | 'HIT' | 'STALE'

const AVIF = 'image/avif'
const WEBP = 'image/webp'
const PNG = 'image/png'
const JPEG = 'image/jpeg'
const JXL = 'image/jxl'
const JP2 = 'image/jp2'
const HEIC = 'image/heic'
const GIF = 'image/gif'
const SVG = 'image/svg+xml'
const ICO = 'image/x-icon'
const ICNS = 'image/x-icns'
const TIFF = 'image/tiff'
const BMP = 'image/bmp'
cons

 // should match `next-image-loader`

let _sharp: typeof import('sharp')

async function initCacheEntries(
  cacheDir: string
): Promise<Array<{ key: string; size: number; expireAt: number }>> {
  const cacheKeys = await promises.readdir(cacheDir).catch(()

t PDF = 'application/pdf'
const CACHE_VERSION = 4
const ANIMATABLE_TYPES = [WEBP, PNG, GIF]
const BYPASS_TYPES = [SVG, ICO, ICNS, BMP, JXL, HEIC]
const BLUR_IMG_SIZE = 8 // should match `next-image-loader`
const BLUR_QUALITY = 70


ribe('isPrivateIp', () => {
  it('should return true for private ip addresses', () => {
    expect(isPrivateIp('127.0.0.0')).toBe(true)
    expect(isPrivateIp('127.0.0.1')).toBe(true)
    expect(isPrivateIp('127.0.0.01')).toBe(true)
    expect(isPrivateIp('127.0.0.001')).toBe(true)
    expect(isPrivateIp('0.0.0.0')).toBe(true)
    expect(isPrivateIp('10.0.0.0')).toBe(true)
    expect(isPrivateIp('10.244.0.0')).toBe(true)
    expect(isPrivateIp('192.168.0.0')).toBe(true)
    expect(isPrivateIp('192.168.0.1')

ateIp('192.168.0.1')).toBe(true)
    expect(isPrivateIp('192.168.0.01')).toBe(true)
    expect(isPrivateIp('169.254.169.254')).toBe(true)
    expect(isPrivateIp('::')).toBe(true)
    expect(isPrivateIp('::1')).toBe(true)
    expect(isPrivateIp('::ffff:0.0.

).toBe(true)
    expect(isPrivateIp('192.168.0.01')).toBe(true)
    expect(isPrivateIp('172.16.0.0')).toBe(true)
    expect(isPrivateIp('172.16.0.1')).toBe(true)
    expect(isPrivateIp('172.16.0.01')).toBe(true)
    expect(isPriv


e-module'
import type { BuildManifest } from './get-page-files'
import type { ActionManifest } from '../build/webpack/plugins/flight-client-entry-plugin'

import {
  BUILD_MANIFEST,
  REACT_LOADABLE_MANIFEST,
  CLIENT_REFERENCE_MANIFEST,
  SERVER_REFERENCE_MANIFEST,
  DYNAMIC_CSS_MANIFEST,
  SUBRESOURCE_INTEGRITY_MANIFEST,
} from '../shared/lib/constants'
import { join } from 'path'
import { requirePage } from './require'
import { interopDefault } from '../lib/interop-default'
import { getTracer } from './l

der/manifests-singleton'
import type { DeepReadonly } from '../shared/lib/deep-readonly'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { isStaticMetadataRoute } from '../lib/metadata/is-metadata-route'

export type

ib/trace/tracer'
import { LoadComponentsSpan } from './lib/trace/constants'
import { evalManifest, loadManifest } from './load-manifest.external'
import { wait } from '../lib/wait'
import { setManifestsSingleton } from './app-ren


st } from './get-page-files'

import { BUILD_MANIFEST } from '../shared/lib/constants'
import { join } from 'path'
import { interopDefault } from '../lib/interop-default'
import { getTracer } from './lib/trace/tracer'
import { LoadComponentsSpan } from './lib/trace/constants'
import {
  loadManifestWithRetries,
  type LoadComponentsReturnType,
} from './load-components'
export type ManifestItem = {
  id: number | string
  files: string[]
}

export type ReactLoadableManifest = { [moduleId: string]: ManifestI

-line @next/internal/typechecked-require -- Why not relative imports?
  const Document = interopDefault(require('next/dist/pages/_document'))
  // eslint-disable-next-line @next/internal/typechecked-require -- Why not relative imports?
  const AppMod = req

tem }

export type ErrorModule = typeof import('./route-modules/pages/builtin/_error')

async function loadDefaultErrorComponentsImpl(
  distDir: string
): Promise<LoadComponentsReturnType<ErrorModule>> {
  // eslint-disable-next


ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
import { cyan, green, red } from '../../lib/picocolors'
import child_process from 'child_process'
import fs from 'fs'
import fsp from 'fs/promises'
import os from 'os'
import path from

:
    case 'nano': {
      return true
    }
    default: {
    }
  }
  return false
}

// Map from full process name to binary that starts the process
// We can't just re-use full process name, because it will spawn a new instance
// of the app every time

 'path'
import { fileURLToPath } from 'url'
import shellQuote from 'next/dist/compiled/shell-quote'

function isTerminalEditor(editor: string) {
  switch (editor) {
    case 'vi':
    case 'vim':
    case 'nvim':
    case 'emacs'


errors/code-frame'
import type { StackFrame } from '../../server/lib/parse-stack'
import { ignoreListAnonymousStackFramesIfSandwiched as ignoreListAnonymousStackFramesIfSandwichedGeneric } from '../../server/lib/source-maps'

export type { StackFrame }

export interface IgnorableStackFrame extends StackFrame {
  ignored: boolean
}

export interface OriginalStackFramesRequest {
  frames: readonly StackFrame[]
  isServer: boolean
  isEdgeServer: boolean
  isAppDirectory: boolean
}

export type OriginalStackFr

 & { ignored: boolean }) | null
  originalCodeFrame: string | null
}

type CodeFrameRenderOptions = {
  colors?: boolean
  maxWidth?: number
}

export const DEVTOOLS_CODE_FRAME_MAX_WIDTH = 1000

export function ignoreListAnonymousStackFramesIfSandwiched(

amesResponse = OriginalStackFrameResponseResult[]

export type OriginalStackFrameResponseResult =
  PromiseSettledResult<OriginalStackFrameResponse>

export interface OriginalStackFrameResponse {
  originalStackFrame: (StackFrame


nse, IncomingMessage } from 'http'
import path from 'path'
import * as fs from 'fs/promises'
import { constants } from 'fs'
import * as Log from '../../../build/output/log'
import { middlewareResponse } from '../middleware-response'

const FONT_PREFIX = '/__nextjs_font/'

const VALID_FONTS = [
  'geist-latin-ext.woff2',
  'geist-mono-latin-ext.woff2',
  'geist-latin.woff2',
  'geist-mono-latin.woff2',
]

const FONT_HEADERS = {
  'Content-Type': 'font/woff2',
  'Cache-Control': 'public, max-age=31536000, imm

onst { pathname } = new URL(`http://n${req.url}`)

      if (!pathname.startsWith(FONT_PREFIX)) {
        return next()
      }

      const fontFile = pathname.replace(FONT_PREFIX, '')
      if (!VALID_FONTS.includes(fontFile)) {
        return middleware

utable',
} as const

export function getDevOverlayFontMiddleware() {
  return async function devOverlayFontMiddleware(
    req: IncomingMessage,
    res: ServerResponse,
    next: () => void
  ): Promise<void> {
    try {
      c


port function deepMerge(target: any, source: any): any {
  if (!source || typeof source !== 'object' || Array.isArray(source)) {
    return source
  }

  if (!target || typeof target !== 'object' || Array.isArray(target)) {
    return source
  }

  const result = { ...target }

  for (const key in source) {
    const sourceValue = source[key]
    const targetValue = target[key]

    if (sourceValue !== undefined) {
      if (
        sourceValue &&
        typeof sourceValue === 'object' &&
        !Array.i

alue) &&
        targetValue &&
        typeof targetValue === 'object' &&
        !Array.isArray(targetValue)
      ) {
        result[key] = deepMerge(targetValue, sourceValue)
      } else {
        result[key] = sourceValue
      }
    }
  }

  return


//react.dev/link/hydration-mismatch'
export const NEXTJS_HYDRATION_ERROR_LINK =
  'https://nextjs.org/docs/messages/react-hydration-error'

/**
 * Only React 19+ contains component stack diff in the error message
 */
const errorMessagesWithComponentStackDiff = [
  /^In HTML, (.+?) cannot be a child of <(.+?)>\.(.*)\nThis will cause a hydration error\.(.*)/,
  /^In HTML, (.+?) cannot be a descendant of <(.+?)>\.\nThis will cause a hydration error\.(.*)/,
  /^In HTML, text nodes cannot be a child of <(.+?)>\.

error\./,
]

export function isHydrationError(error: Error): boolean {
  return (
    isErrorMessageWithComponentStackDiff(error.message) ||
    /Hydration failed because the server rendered (text|HTML) didn't match the client\./.test(
      error.message

\nThis will cause a hydration error\./,
  /^In HTML, whitespace text nodes cannot be a child of <(.+?)>\. Make sure you don't have any extra whitespace between tags on each line of your source code\.\nThis will cause a hydration


{ VersionInfo } from '../../server/dev/parse-version-info'

export function getStaleness({ installed, staleness, expected }: VersionInfo) {
  let text = ''
  let title = ''
  let indicatorClass = ''
  const versionLabel = `Next.js ${installed}`
  switch (staleness) {
    case 'newer-than-npm':
    case 'fresh':
      text = versionLabel
      title = `Latest available version is detected (${installed}).`
      indicatorClass = 'fresh'
      break
    case 'stale-patch':
    case 'stale-minor':
      text =

  title = `An outdated version detected (latest is ${expected}), upgrade is highly recommended!`
      indicatorClass = 'outdated'
      break
    }
    case 'stale-prerelease': {
      text = `${versionLabel} (stale)`
      title = `There is a newer canar

`${versionLabel} (stale)`
      title = `There is a newer version (${expected}) available, upgrade recommended! `
      indicatorClass = 'stale'
      break
    case 'stale-major': {
      text = `${versionLabel} (outdated)`


se } from './middleware-response'
import type { ServerResponse, IncomingMessage } from 'http'
import * as inspector from 'inspector'

export function getAttachNodejsDebuggerMiddleware() {
  return async function (
    req: IncomingMessage,
    res: ServerResponse,
    next: () => void
  ): Promise<void> {
    const { pathname } = new URL(`http://n${req.url}`)

    if (pathname !== '/__nextjs_attach-nodejs-inspector') {
      return next()
    }

    try {
      const isInspecting = inspector.url() !== undef

()
      if (inspectorURLRaw === undefined) {
        // could not open, possibly because already in use.
        return middlewareResponse.badRequest(
          res,
          `Failed to open port "${debugPort}". Address may be already in use.`
        )

ined
      const debugPort = process.debugPort
      if (!isInspecting) {
        // Node.js will already log that the inspector is listening.
        inspector.open(debugPort)
      }

      const inspectorURLRaw = inspector.url


m 'http'
import type { DevToolsConfig } from '../dev-overlay/shared'

import { existsSync } from 'fs'
import { readFile, writeFile, mkdir } from 'fs/promises'
import { dirname, join } from 'path'

import { middlewareResponse } from './middleware-response'
import { devToolsConfigSchema } from '../shared/devtools-config-schema'
import { deepMerge } from '../shared/deepmerge'

const DEVTOOLS_CONFIG_FILENAME = 'next-devtools-config.json'
const DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT = '/__nextjs_devtools_config'

e

rn async function devToolsConfigMiddlewareHandler(
    req: IncomingMessage,
    res: ServerResponse,
    next: () => void
  ): Promise<void> {
    const { pathname } = new URL(`http://n${req.url}`)

    if (pathname !== DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT

xport function devToolsConfigMiddleware({
  distDir,
  sendUpdateSignal,
}: {
  distDir: string
  sendUpdateSignal: (data: DevToolsConfig) => void
}) {
  const configPath = join(distDir, 'cache', DEVTOOLS_CONFIG_FILENAME)

  retu


om 'react'
import { dispatcher } from 'next/dist/compiled/next-devtools'
import { RuntimeErrorHandler } from '../../../client/dev/runtime-error-handler'
import { ErrorBoundary } from '../../../client/components/error-boundary'
import DefaultGlobalError from '../../../client/components/builtin/global-error'
import type { GlobalErrorState } from '../../../client/components/app-router-instance'
import { SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE } from './segment-explorer-node'
import {
  AppRouterContext,
  typ

}

type AppDevOverlayErrorBoundaryState = {
  error: null | { thrownValue: unknown }
}

function ErroredHtml({
  globalError: [GlobalError, globalErrorStyles],
  thrownValue,
  reset,
  unstable_retry,
}: {
  globalError: GlobalErrorState
  thrownValue: u

e AppRouterInstance,
} from '../../../shared/lib/app-router-context.shared-runtime'
import isError from '../../../lib/is-error'

type AppDevOverlayErrorBoundaryProps = {
  children: React.ReactNode
  globalError: GlobalErrorState


rt { getOwnerStack } from './errors/stitched-error'

const isTerminalLoggingEnabled = getIsTerminalLoggingEnabled()
const shouldForwardLogs =
  isTerminalLoggingEnabled || !!process.env.__NEXT_MCP_SERVER

const methods: Array<LogMethod> = [
  'log',
  'info',
  'warn',
  'debug',
  'table',
  'assert',
  'dir',
  'dirxml',
  'group',
  'groupCollapsed',
  'groupEnd',
  'trace',
]

const afterThisFrame = (cb: () => void) => {
  let timeout: ReturnType<typeof setTimeout> | undefined

  const rafId = requestAn

tLogEntry>) =>
  entries.map((clientEntry) => {
    switch (clientEntry.kind) {
      case 'any-logged-error':
      case 'console': {
        return {
          ...clientEntry,
          args: clientEntry.args.map(stringifyUserArg),
        }
      }

imationFrame(() => {
    timeout = setTimeout(() => {
      cb()
    })
  })

  return () => {
    cancelAnimationFrame(rafId)
    clearTimeout(timeout)
  }
}

let isPatched = false

const serializeEntries = (entries: Array<Clien


t,
  use,
  useMemo,
  useCallback,
} from 'react'
import { useLayoutEffect } from 'react'
import { dispatcher } from 'next/dist/compiled/next-devtools'
import { GlobalLayoutRouterContext } from '../../../shared/lib/app-router-context.shared-runtime'
import { notFound } from '../../../client/components/not-found'

export type SegmentBoundaryType =
  | 'not-found'
  | 'error'
  | 'loading'
  | 'global-error'

export const SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE =
  'NEXT_DEVTOOLS_SIMULATED_ERROR'

export ty

h: string
}): React.ReactNode {
  const { boundaryType, setBoundaryType } = useSegmentState()
  const nodeState: SegmentNodeState = useMemo(() => {
    return {
      type,
      pagePath,
      boundaryType,
      setBoundaryType,
    }
  }, [type, pagePa

pe SegmentNodeState = {
  type: string
  pagePath: string
  boundaryType: string | null
  setBoundaryType: (type: SegmentBoundaryType | null) => void
}

function SegmentTrieNode({
  type,
  pagePath,
}: {
  type: string
  pagePat


ort React from 'react'
import isError from '../../../../lib/is-error'

const ownerStacks = new WeakMap<Error, string | null>()

export function getOwnerStack(error: Error): string | null | undefined {
  return ownerStacks.get(error)
}
export function setOwnerStack(error: Error, stack: string | null) {
  ownerStacks.set(error, stack)
}

export function coerceError(value: unknown): Error {
  return isError(value) ? value : new Error('' + value)
}

export function setOwnerStackIfAvailable(error: Error): void {

is prevents overwriting a valid owner stack captured earlier
    // (e.g., in onRecoverableError) with null captured later.
    if (ownerStack || !ownerStacks.has(error)) {
      setOwnerStack(error, ownerStack)
    }
  }
}

export function decorateDevErro

  // React 18 and prod does not have `captureOwnerStack`
  if ('captureOwnerStack' in React) {
    const ownerStack = React.captureOwnerStack()
    // Only set if we captured a valid owner stack, or if none exists yet.
    // Th


next-router-error'
import {
  formatConsoleArgs,
  parseConsoleArgs,
} from '../../../../client/lib/console'
import isError from '../../../../lib/is-error'
import { createConsoleError } from '../../../shared/console-error'
import { coerceError, setOwnerStackIfAvailable } from './stitched-error'
import { forwardUnhandledError, logUnhandledRejection } from '../forward-logs'

const queueMicroTask =
  globalThis.queueMicrotask || ((cb: () => void) => Promise.resolve().then(cb))

type ErrorHandler = (error: Erro

Error: unknown,
  consoleErrorArgs: any[]
) {
  let error: Error
  const { environmentName } = parseConsoleArgs(consoleErrorArgs)
  if (isError(originError)) {
    error = createConsoleError(originError, environmentName)
  } else {
    error = createConsol

r) => void

const errorQueue: Array<Error> = []
const errorHandlers: Array<ErrorHandler> = []
const rejectionQueue: Array<Error> = []
const rejectionHandlers: Array<ErrorHandler> = []

export function handleConsoleError(
  origin


ort { dispatcher } from 'next/dist/compiled/next-devtools'
import {
  attachHydrationErrorState,
  storeHydrationErrorStateFromConsoleArgs,
} from './hydration-error-state'
import { Router } from '../../../client/router'
import { getOwnerStack } from '../app/errors/stitched-error'
import { isRecoverableError } from '../../../client/react-client-callbacks/on-recoverable-error'
import { getSquashedHydrationErrorDetails } from './hydration-error-state'
import { PagesDevOverlayErrorBoundary } from './pages-dev-

ct(() => {
    // NDT uses a different React instance so it's not technically a state update
    // scheduled from useInsertionEffect.
    renderPagesDevOverlay(
      getOwnerStack,
      getSquashedHydrationErrorDetails,
      isRecoverableError
    )

overlay-error-boundary'
import {
  initializeDebugLogForwarding,
  forwardUnhandledError,
  logUnhandledRejection,
  forwardErrorLog,
} from '../app/forward-logs'

const usePagesDevOverlayBridge = () => {
  React.useInsertionEffe


t path from 'path'
import type { TelemetryEvent } from './storage'
import { Telemetry } from './storage'
import loadConfig from '../server/config'
import { getProjectDir } from '../lib/get-project-dir'
import { PHASE_DEVELOPMENT_SERVER } from '../shared/lib/constants'

// this process should be started with following arg order
// 1. mode e.g. dev, export, start
// 2. project dir
// 3. events filename (optional, defaults to _events.json)
;(async () => {
  const args = [...process.argv]
  const eventsFile = a

rojectDir(dir)

  const config = await loadConfig(PHASE_DEVELOPMENT_SERVER, dir)
  const distDir = path.join(dir, config.distDir || '.next')
  // Support both old format (no eventsFile arg) and new format (with eventsFile arg)
  const eventsPath = path.joi

rgs.pop()
  let dir = args.pop()
  const mode = args.pop()

  if (!dir || mode !== 'dev') {
    throw new Error(
      `Invalid flags should be run as node detached-flush dev ./path-to/project [eventsFile]`
    )
  }
  dir = getP


 type { ServerResponse } from 'http'
import { inspect } from 'util'

export const middlewareResponse = {
  noContent(res: ServerResponse) {
    res.statusCode = 204
    res.end('No Content')
  },
  badRequest(res: ServerResponse, reason?: string) {
    res.statusCode = 400
    if (reason !== undefined) {
      res.setHeader('Content-Type', 'text/plain')
      res.end(reason)
    } else {
      res.end()
    }
  },
  notFound(res: ServerResponse) {
    res.statusCode = 404
    res.end('Not Found')
  },
  met

lain')
    res.end(
      error !== undefined
        ? inspect(error, { colors: false })
        : 'Internal Server Error'
    )
  },
  json(res: ServerResponse, data: any) {
    res
      .setHeader('Content-Type', 'application/json')
      .end(Buffer.f

hodNotAllowed(res: ServerResponse) {
    res.statusCode = 405
    res.end('Method Not Allowed')
  },
  internalServerError(res: ServerResponse, error?: unknown) {
    res.statusCode = 500
    res.setHeader('Content-Type', 'text/p


ere to determine if the error is from console.error or unhandled promise rejection.
const digestSym = Symbol.for('next.console.error.digest')

// Represent non Error shape unhandled promise rejections or console.error errors.
// Those errors will be captured and displayed in Error Overlay.
type ConsoleError = Error & {
  [digestSym]: 'NEXT_CONSOLE_ERROR'
  environmentName: string
}

export function createConsoleError(
  message: string | Error,
  environmentName?: string | null
): ConsoleError {
  const err

essage) : message
  ) as ConsoleError
  error[digestSym] = 'NEXT_CONSOLE_ERROR'

  if (environmentName && !error.environmentName) {
    error.environmentName = environmentName
  }

  return error
}

export const isConsoleError = (error: any): error is Cons

or = (
    typeof message === 'string' ? new Error(m


ebug'
  | 'table'
  | 'error'
  | 'assert'
  | 'dir'
  | 'dirxml'
  | 'group'
  | 'groupCollapsed'
  | 'groupEnd'
  | 'trace'
  | 'warn'

export type ConsoleEntry<T> = {
  kind: 'console'
  method: LogMethod
  consoleMethodStack: string | null
  args: Array<
    | {
        kind: 'arg'
        data: T
      }
    | {
        kind: 'formatted-error-arg'
        prefix: string
        stack: string
      }
  >
}

export type ConsoleErrorEntry<T> = {
  kind: 'any-logged-error'
  method: 'error'
  consoleErrorS

xport type FormattedErrorEntry = {
  kind: 'formatted-error'
  prefix: string
  stack: string
  method: 'error'
}

export type ClientLogEntry =
  | ConsoleEntry<unknown>
  | ConsoleErrorEntry<unknown>
  | FormattedErrorEntry
export type ServerLogEntry =

tack: string
  args: Array<
    | {
        kind: 'arg'
        data: T
        isRejectionMessage?: boolean
      }
    | {
        kind: 'formatted-error-arg'
        prefix: string
        stack: string | null
      }
  >
}

e


equest,
  StackFrame,
} from '../server/shared'
import {
  isWebpackInternalResource,
  formatStackFrameFile,
} from './webpack-module-path'

export type { StackFrame }

interface ResolvedOriginalStackFrame extends OriginalStackFrameResponse {
  error: false
  reason: null
  external: boolean
  ignored: boolean
  sourceStackFrame: StackFrame
}

interface RejectedOriginalStackFrame extends OriginalStackFrameResponse {
  error: true
  reason: string
  external: boolean
  ignored: boolean
  sourceStackFrame: S

ackFrame> {
  async function _getOriginalStackFrame(): Promise<ResolvedOriginalStackFrame> {
    if (response.status === 'rejected') {
      throw new Error(response.reason)
    }

    const body: OriginalStackFrameResponse = response.value

    return {

tackFrame
}

export type OriginalStackFrame =
  | ResolvedOriginalStackFrame
  | RejectedOriginalStackFrame

function getOriginalStackFrame(
  source: StackFrame,
  response: OriginalStackFrameResponseResult
): Promise<OriginalSt


stable-stringify'
import { getTerminalLoggingConfig } from './terminal-logging-config'
import { UNDEFINED_MARKER } from '../../shared/forward-logs-shared'

const terminalLoggingConfig = getTerminalLoggingConfig()

const PROMISE_MARKER = 'Promise {}'
const UNAVAILABLE_MARKER = '[Unable to view]'

const maximumDepth =
  typeof terminalLoggingConfig === 'object' && terminalLoggingConfig.depthLimit
    ? terminalLoggingConfig.depthLimit
    : 5
const maximumBreadth =
  typeof terminalLoggingConfig === 'object'

 server as it would look in the browser
 * - not read/attempt to serialize promises (next will console error if you do that, and will cause this program to infinitely recurse)
 * - if we read a proxy that throws (no way to detect if something is a proxy),

&& terminalLoggingConfig.edgeLimit
    ? terminalLoggingConfig.edgeLimit
    : 100

export const safeStringifyWithDepth = configure({
  maximumDepth,
  maximumBreadth,
})

/**
 * allows us to:
 * - revive the undefined log in the


./../../lib/is-error'
import { isNextRouterError } from '../../../../client/components/is-next-router-error'
import { handleConsoleError } from './use-error-handler'
import { parseConsoleArgs } from '../../../../client/lib/console'
import { forwardErrorLog } from '../forward-logs'

export const originConsoleError = globalThis.console.error

// Patch console.error to collect information about hydration errors
export function patchConsoleError() {
  // Ensure it's only patched once
  if (typeof window === 'un

nvironmentName } = parseConsoleArgs(args)
      // If environmentName is set, this is a server log replayed by React in the browser
      isReplayedLog = !!environmentName
      if (replayedError) {
        maybeError = replayedError
      } else if (isErr

defined') {
    return
  }
  window.console.error = function error(...args: any[]) {
    let maybeError: unknown
    let isReplayedLog = false
    if (process.env.NODE_ENV !== 'production') {
      const { error: replayedError, e


rror as isReact19HydrationError,
  isErrorMessageWithComponentStackDiff as isReact19HydrationWarning,
} from '../../shared/react-19-hydration-error'
import type { HydrationErrorState } from '../../shared/hydration-error'

// We only need this for React 18 or hydration console errors in React 19.
// Once we surface console.error in the dev overlay in pages router, we should only
// use this for React 18.
let hydrationErrorState: HydrationErrorState = {}

const squashedHydrationErrorDetails = new WeakMap<Erro

ull
}

export function attachHydrationErrorState(error: Error) {
  if (!isReact18HydrationError(error) && !isReact19HydrationError(error)) {
    return
  }

  let parsedHydrationErrorState: typeof hydrationErrorState = {}

  // If there's any extra informa

r, HydrationErrorState>()

export function getSquashedHydrationErrorDetails(
  error: Error
): HydrationErrorState | null {
  return squashedHydrationErrorDetails.has(error)
    ? squashedHydrationErrorDetails.get(error)!
    : n


kerFunction from 'next/dist/compiled/is-docker'
import isWslBoolean from 'next/dist/compiled/is-wsl'
import os from 'os'

import * as ciEnvironment from '../server/ci-info'

type AnonymousMeta = {
  systemPlatform: NodeJS.Platform
  systemRelease: string
  systemArchitecture: string
  cpuCount: number
  cpuModel: string | null
  cpuSpeed: number | null
  memoryInMb: number
  isDocker: boolean
  isNowDev: boolean
  isWsl: boolean
  isCI: boolean
  ciName: string | null
  nextVersion: string
}

let traits: An

systemPlatform: os.platform(),
    systemRelease: os.release(),
    systemArchitecture: os.arch(),
    // Machine information
    cpuCount: cpus.length,
    cpuModel: cpus.length ? cpus[0].model : null,
    cpuSpeed: cpus.length ? cpus[0].speed : null,

onymousMeta | undefined

export function getAnonymousMeta(): AnonymousMeta {
  if (traits) {
    return traits
  }

  const cpus = os.cpus() || []
  const { NOW_REGION } = process.env
  traits = {
    // Software information


m 'http'
import type { Telemetry } from '../../telemetry/storage'
import { RESTART_EXIT_CODE } from '../../server/lib/utils'
import { middlewareResponse } from './middleware-response'
import type { Project } from '../../build/swc/types'
import { invalidateFileSystemCache as invalidateWebpackFileSystemCache } from '../../build/webpack/cache-invalidation'

const EVENT_DEV_OVERLAY_RESTART_SERVER = 'DEV_OVERLAY_RESTART_SERVER'

interface RestartDevServerMiddlewareConfig {
  telemetry: Telemetry
  turbopackProje

ue between 1 and Number.MAX_SAFE_INTEGER (inclusive). The same value is returned
   * on every call to `__nextjs_server_status` until the server is restarted.
   *
   * Can be used to determine if two server status responses are from the same process or a

ct?: Project
  webpackCacheDirectories?: Set<string>
}

export function getRestartDevServerMiddleware({
  telemetry,
  turbopackProject,
  webpackCacheDirectories,
}: RestartDevServerMiddlewareConfig) {
  /**
   * Some random val


ormatStackFrameFile,
  isWebpackInternalResource,
} from './webpack-module-path'

describe('webpack-module-path', () => {
  describe('isWebpackInternalResource', () => {
    it('should return true for webpack-internal paths', () => {
      expect(
        isWebpackInternalResource('webpack-internal:///./src/hello.tsx')
      ).toBe(true)

      expect(isWebpackInternalResource('webpack://_N_E/./src/hello.tsx')).toBe(
        true
      )
      expect(isWebpackInternalResource('webpack://./src/hello.tsx')).t

alse)
      expect(isWebpackInternalResource('file:///src/hello.tsx')).toBe(false)
    })
  })

  describe('formatFrameSourceFile', () => {
    it('should return the original file path', () => {
      expect(formatStackFrameFile('webpack-internal:///./src/

oBe(true)
      expect(isWebpackInternalResource('webpack:///./src/hello.tsx')).toBe(true)
    })

    it('should return false for non-webpack-internal paths', () => {
      expect(isWebpackInternalResource('<anonymous>')).toBe(f


data: unknown) =>
  logStringify(preLogSerializationClone(data))

describe('forward-logs serialization', () => {
  describe('safeClone', () => {
    it('should handle primitive values and null', () => {
      expect(preLogSerializationClone(42)).toBe(42)
      expect(preLogSerializationClone('hello')).toBe('hello')
      expect(preLogSerializationClone(true)).toBe(true)
      expect(preLogSerializationClone(null)).toBe(null)
      expect(preLogSerializationClone(undefined)).toBe(
        '__next_tagged_unde

.self).toBe(cloned)
    })

    it('should handle promises', () => {
      const promise = Promise.resolve(42)
      expect(preLogSerializationClone(promise)).toBe('Promise {}')
    })

    it('should handle arrays', () => {
      const arr = [1, 'test', u

fined'
      )
    })

    it('should handle circular references', () => {
      const obj: any = { a: 1 }
      obj.self = obj
      const cloned = preLogSerializationClone(obj)
      expect(cloned.a).toBe(1)
      expect(cloned


 { eventErrorFeedback } from '../../telemetry/events/error-feedback'
import { middlewareResponse } from './middleware-response'
import type { ServerResponse, IncomingMessage } from 'http'
import type { Telemetry } from '../../telemetry/storage'

// Handles HTTP requests to /__nextjs_error_feedback endpoint for collecting user feedback on error messages
export function getNextErrorFeedbackMiddleware(telemetry: Telemetry) {
  return async function (
    req: IncomingMessage,
    res: ServerResponse,
    next:

et('errorCode')
      const wasHelpful = searchParams.get('wasHelpful')

      if (!errorCode || !wasHelpful) {
        return middlewareResponse.badRequest(res)
      }

      await telemetry.record(
        eventErrorFeedback({
          errorCode,

 () => void
  ): Promise<void> {
    const { pathname, searchParams } = new URL(`http://n${req.url}`)

    if (pathname !== '/__nextjs_error_feedback') {
      return next()
    }

    try {
      const errorCode = searchParams.g


nction isHydrationError(error: unknown): boolean {
  return (
    isError(error) &&
    (error.message ===
      'Hydration failed because the initial UI does not match what was rendered on the server.' ||
      error.message === 'Text content does not match server-rendered HTML.')
  )
}

export function isHydrationWarning(message: unknown): message is string {
  return (
    isHtmlTagsWarning(message) ||
    isTextInTagsMismatchWarning(message) ||
    isTextWarning(message)
  )
}

type NullableText = strin

n a matching <%s> in <%s>.%s',
  'Warning: Did not expect server HTML to contain a <%s> in <%s>.%s',
])
const textAndTagsMismatchWarnings = new Set([
  'Warning: Expected server HTML to contain a matching text node for "%s" in <%s>.%s',
  'Warning: Did not

g | null | undefined

// https://github.com/facebook/react/blob/main/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js used as a reference
const htmlTagsWarnings = new Set([
  'Warning: Expected server HTML to contai


ientError } from './use-error-handler'
import { isNextRouterError } from '../../../../client/components/is-next-router-error'
import { MISSING_ROOT_TAGS_ERROR } from '../../../../shared/lib/errors/constants'

function readSsrError(): (Error & { digest?: string }) | null {
  if (typeof document === 'undefined') {
    return null
  }

  const ssrErrorTemplateTag = document.querySelector(
    'template[data-next-error-message]'
  )
  if (ssrErrorTemplateTag) {
    const message: string = ssrErrorTemplateTag.ge

ror(message)
    if (digest) {
      ;(error as any).digest = digest
    }
    // Skip Next.js SSR'd internal errors that which will be handled by the error boundaries.
    if (isNextRouterError(error)) {
      return null
    }
    error.stack = stack ||

tAttribute(
      'data-next-error-message'
    )!
    const stack = ssrErrorTemplateTag.getAttribute('data-next-error-stack')
    const digest = ssrErrorTemplateTag.getAttribute('data-next-error-digest')
    const error = new Er


{ ServerResponse, IncomingMessage } from 'http'
import { middlewareResponse } from './middleware-response'
import * as Log from '../../build/output/log'
import { devIndicatorServerState } from '../../server/dev/dev-indicator-server-state'

const DISABLE_DEV_INDICATOR_PREFIX = '/__nextjs_disable_dev_indicator'

const COOLDOWN_TIME_MS = process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS
  ? parseInt(process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS)
  : // 1 day from now
    1000 * 60 * 60 * 24

export function getDisab

/n${req.url}`)

      if (!pathname.startsWith(DISABLE_DEV_INDICATOR_PREFIX)) {
        return next()
      }

      if (req.method !== 'POST') {
        return middlewareResponse.methodNotAllowed(res)
      }

      devIndicatorServerState.disabledUntil =

leDevIndicatorMiddleware() {
  return async function disableDevIndicatorMiddleware(
    req: IncomingMessage,
    res: ServerResponse,
    next: () => void
  ): Promise<void> {
    try {
      const { pathname } = new URL(`http:/


./shared/lib/constants'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import { PageNotFoundError, MissingStaticPage } from '../shared/lib/utils'
import { LRUCache } from '../server/lib/lru-cache'
import { loadManifest } from './loa

 distDir: string,
  locales: readonly string[] | undefined,
  isAppPath: boolean
): string | null {
  const cacheKey = `${page}:${distDir}:${locales}:${isAppPath}`

  let pagePath = pagePathCache?.get(cacheKey)

  // If we have a cached path, we can return

d-manifest.external'
import { promises } from 'fs'

const isDev = process.env.NODE_ENV === 'development'
const pagePathCache = !isDev ? new LRUCache<string | null>(1000) : null

export function getMaybePagePath(
  page: string,


erenderManifest,
} from '../../../../build'
import type { DeepReadonly } from '../../../../shared/lib/deep-readonly'
import {
  getRouteMatcher,
  type RouteMatchFn,
} from '../../../../shared/lib/router/utils/route-matcher'
import { getRouteRegex } from '../../../../shared/lib/router/utils/route-regex'

/**
 * A matcher for a dynamic route.
 */
type Matcher = {
  /**
   * The matcher for the dynamic route. This is lazily created when the matcher
   * is first used.
   */
  matcher?: RouteMatchFn

  /**

e: DeepReadonly<DynamicPrerenderManifestRoute>
}

/**
 * A matcher for the prerender manifest.
 *
 * This class is used to match the pathname to the dynamic route.
 */
export class PrerenderManifestMatcher {
  private readonly matchers: Array<Matcher>
  co

* The source of the dynamic route.
   */
  source: string

  /**
   * The route that matches the source.
   */
  route: DeepReadonly<DynamicPrerenderManifestRoute>
}

export type PrerenderManifestMatch = {
  source: string
  rout


taticPaths,
  GetStaticProps,
  NextComponentType,
  PageConfig,
} from '../../../types'
import type { PagesRouteDefinition } from '../../route-definitions/pages-route-definition'
import type { NextParsedUrlQuery } from '../../request-meta'
import type {
  PagesRenderContext,
  PagesSharedContext,
  RenderOpts,
} from '../../render'
import type RenderResult from '../../render-result'
import type { AppType, DocumentType } from '../../../shared/lib/utils'

import {
  RouteModule,
  type RouteModuleHandleConte

module exported by the bundled pages
 * module.
 */
export type PagesModule = typeof import('../../../build/templates/pages')

/**
 * The userland module for a page. This is the module that is exported from the
 * page file that contains the page component

xt,
  type RouteModuleOptions,
} from '../route-module'
import { renderToHTMLImpl, renderToHTML } from '../../render'
import * as vendoredContexts from './vendored/contexts/entrypoints'

/**
 * The PagesModule is the type of the


Generator,
} from '../../response-cache'

import {
  getCacheControlHeader,
  type CacheControl,
} from '../../lib/cache-control'
import { normalizeRepeatedSlashes } from '../../../shared/lib/utils'
import { getRedirectStatus } from '../../../lib/redirect-status'
import {
  CACHE_ONE_YEAR_SECONDS,
  HTML_CONTENT_TYPE_HEADER,
  JSON_CONTENT_TYPE_HEADER,
  NEXT_NAV_DEPLOYMENT_ID_HEADER,
} from '../../../lib/constants'
import path from 'path'
import { sendRenderResult } from '../../send-payload'
import RenderR

nt/components/redirect-status-code'
import { isBot } from '../../../shared/lib/router/utils/is-bot'
import { addPathPrefix } from '../../../shared/lib/router/utils/add-path-prefix'
import { removeTrailingSlash } from '../../../shared/lib/router/utils/remov

esult from '../../render-result'
import { toResponseCacheEntry } from '../../response-cache/utils'
import { NoFallbackError } from '../../../shared/lib/no-fallback-error.external'
import { RedirectStatusCode } from '../../../clie


 as RouterContext from '../../../../../shared/lib/router-context.shared-runtime'
export * as LoadableContext from '../../../../../shared/lib/loadable-context.shared-runtime'
export * as Loadable from '../../../../../shared/lib/loadable.shared-runtime'
export * as ImageConfigContext from '../../../../../shared/lib/image-config-context.shared-runtime'
export * as HtmlContext from '../../../../../shared/lib/html-context.shared-runtime'
export * as HooksClientContext from '../../../../../shared/lib/hooks-client

ext from '../../../../../shared/lib/head-manager-context.shared-runtime'
export * as AppRouterContext from '../../../../../shared/lib/app-router-context.shared-runtime'
export * as ServerInsertedHtml from '../../../../../shared/lib/server-inserted-html.sha

-context.shared-runtime'
export * as HeadManagerCont


../../../../pages/_app'
import Document from '../../../../pages/_document'
import { RouteKind } from '../../../route-kind'

import * as moduleError from '../../../../pages/_error'

import PagesRouteModule from '../module'
import { getHandler } from '../pages-handler'

export const routeModule = new PagesRouteModule({
  // TODO: add descriptor for internal error page
  definition: {
    kind: RouteKind.PAGES,
    page: '/_error',
    pathname: '/_error',
    filename: '',
    bundlePath: '',
  },
  distDir:

veProjectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || '',
  components: {
    App,
    Document,
  },
  userland: moduleError,
})

export const handler = getHandler({
  srcPage: '/_error',
  routeModule,
  userland: moduleError,
  config: {},
  isFallba

process.env.__NEXT_RELATIVE_DIST_DIR || '',
  relati


env.NEXT_RUNTIME === 'edge') {
  module.exports = require('next/dist/server/route-modules/app-route/module.js')
} else {
  if (process.env.__NEXT_EXPERIMENTAL_REACT) {
    if (process.env.NODE_ENV === 'development') {
      if (process.env.TURBOPACK) {
        module.exports = require('next/dist/compiled/next-server/app-route-turbo-experimental.runtime.dev.js')
      } else {
        module.exports = require('next/dist/compiled/next-server/app-route-experimental.runtime.dev.js')
      }
    } else {
      i

-experimental.runtime.prod.js')
      }
    }
  } else {
    if (process.env.NODE_ENV === 'development') {
      if (process.env.TURBOPACK) {
        module.exports = require('next/dist/compiled/next-server/app-route-turbo.runtime.dev.js')
      } else {

f (process.env.TURBOPACK) {
        module.exports = require('next/dist/compiled/next-server/app-route-turbo-experimental.runtime.prod.js')
      } else {
        module.exports = require('next/dist/compiled/next-server/app-route


ers/parsed-url-query-to-params'
import {
  Phase,
  printDebugThrownValueForProspectiveRender,
} from '../../app-render/prospective-render-utils'

import * as serverHooks from '../../../client/components/hooks-server-context'
import { DynamicServerError } from '../../../client/components/hooks-server-context'

import {
  workAsyncStorage,
  type WorkStore,
} from '../../app-render/work-async-storage.external'
import {
  workUnitAsyncStorage,
  type RequestStore,
  type PrerenderStore,
} from '../../app-rend

} from '../../lib/server-action-request-meta'
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'
import { cleanURL } from './helpers/clean-url'
import { StaticGenBailoutError } from '../../../client/components/static-generation-bailo

er/work-unit-async-storage.external'
import {
  actionAsyncStorage,
  type ActionStore,
} from '../../app-render/action-async-storage.external'
import * as sharedModules from './shared-modules'
import { getIsPossibleServerAction


 from '../module'

import { HTTP_METHODS, type HTTP_METHOD } from '../../../web/http'

const AUTOMATIC_ROUTE_METHODS = ['HEAD', 'OPTIONS'] as const

function handleMethodNotAllowedResponse(): Response {
  return new Response(null, { status: 405 })
}

export function autoImplementMethods(
  handlers: AppRouteHandlers
): Record<HTTP_METHOD, AppRouteHandlerFn> {
  // Loop through all the HTTP methods to create the initial methods object.
  // Each of the methods will be set to the 405 response handler.
  const

     [method]: handlers[method] ?? handleMethodNotAllowedResponse,
    }),
    {} as Record<HTTP_METHOD, AppRouteHandlerFn>
  )

  // Get all the methods that could be automatically implemented that were not
  // implemented by the userland module.
  const

 methods: Record<HTTP_METHOD, AppRouteHandlerFn> = HTTP_METHODS.reduce(
    (acc, method) => ({
      ...acc,
      // If the userland module implements the method, then use it. Otherwise,
      // use the 405 response handler.


d: 'Pages Router' | 'App Router'
  routePath: string // the route file path, e.g. /app/blog/[dynamic]
  routeType: 'render' | 'route' | 'action' | 'proxy'
  renderSource?:
    | 'react-server-components'
    | 'react-server-components-payload'
    | 'server-rendering'
  revalidateReason: 'on-demand' | 'stale' | undefined
  // TODO: other future instrumentation context
}

export type InstrumentationOnRequestError = (
  error: unknown,
  errorRequest: Readonly<{
    path: string
    method: string
    headers

t: Readonly<RequestErrorContext>
) => void | Promise<void>

export type InstrumentationModule = {
  register?(): void
  onRequestError?: InstrumentationOnRequestError
}

export namespace Instrumentation {
  export type onRequestError = InstrumentationOnReq

: NodeJS.Dict<string | string[]>
  }>,
  errorContex


{ ServerResponse, IncomingMessage } from 'http'
import {
  getOrCreateMcpServer,
  type McpServerOptions,
} from './get-or-create-mcp-server'
import { parseBody } from '../api-utils/node/parse-body'
import { StreamableHTTPServerTransport } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/streamableHttp'

export function getMcpMiddleware(options: McpServerOptions) {
  return async function (
    req: IncomingMessage,
    res: ServerResponse,
    next: () => void
  ): Promise<void> {
    const { path

   sessionIdGenerator: undefined,
    })
    try {
      res.on('close', () => {
        transport.close()
      })
      await mcpServer.connect(transport)
      const parsedBody = await parseBody(req, 1024 * 1024) // 1MB limit
      await transport.handl

name } = new URL(req.url || '', 'http://n')
    if (!pathname.startsWith('/_next/mcp')) {
      return next()
    }
    const mcpServer = getOrCreateMcpServer(options)
    const transport = new StreamableHTTPServerTransport({


lcontextprotocol/sdk/server/mcp'
import { registerGetProjectMetadataTool } from './tools/get-project-metadata'
import { registerGetErrorsTool } from './tools/get-errors'
import { registerGetPageMetadataTool } from './tools/get-page-metadata'
import { registerGetLogsTool } from './tools/get-logs'
import { registerGetActionByIdTool } from './tools/get-server-action-by-id'
import { registerGetRoutesTool } from './tools/get-routes'
import { registerGetCompilationIssuesTool } from './tools/get-compilation-issues

../../build/swc/types'
import type { FormattedIssue } from './tools/utils/format-compilation-issues'

export interface McpServerOptions {
  projectPath: string
  distDir: string
  nextConfig: NextConfigComplete
  pagesDir: string | undefined
  appDir: stri

'
import { registerCompileRouteTool } from './tools/compile-route'
import type { HmrMessageSentToBrowser } from '../dev/hot-reloader-types'
import type { NextConfigComplete } from '../config-shared'
import type { Project } from '


ribe('MCP Telemetry Tracker', () => {
  beforeEach(() => {
    // Reset tracker state before each test
    mcpTelemetryTracker.reset()
  })

  afterAll(() => {
    // Clean up after all tests
    mcpTelemetryTracker.reset()
  })

  it('should start with no usage', () => {
    expect(mcpTelemetryTracker.hasUsage()).toBe(false)
    expect(mcpTelemetryTracker.getUsages()).toEqual([])
  })

  it('should track single tool call', () => {
    mcpTelemetryTracker.recordToolCall('mcp/get_errors')

    expect(mcpTele

r repeated calls', () => {
    mcpTelemetryTracker.recordToolCall('mcp/get_errors')
    mcpTelemetryTracker.recordToolCall('mcp/get_errors')
    mcpTelemetryTracker.recordToolCall('mcp/get_errors')

    const usages = mcpTelemetryTracker.getUsages()
    ex

metryTracker.hasUsage()).toBe(true)
    expect(mcpTelemetryTracker.getUsages()).toEqual([
      {
        featureName: 'mcp/get_errors',
        invocationCount: 1,
      },
    ])
  })

  it('should increment invocation count fo


for MCP tool call usage.
 * Tracks invocation counts for each MCP tool to be reported via telemetry.
 */

import type { McpToolName } from '../../telemetry/events/build'

export interface McpToolUsage {
  featureName: McpToolName
  invocationCount: number
}

class McpTelemetryTracker {
  private usageMap = new Map<McpToolName, number>()

  /**
   * Record a tool call invocation
   */
  recordToolCall(toolName: McpToolName): void {
    const current = this.usageMap.get(toolName) || 0
    this.usageMap.set(to

count,
    }))
  }

  /**
   * Reset all usage tracking
   */
  reset(): void {
    this.usageMap.clear()
  }

  /**
   * Check if any tools have been called
   */
  hasUsage(): boolean {
    return this.usageMap.size > 0
  }
}

// Singleton instance
expor

olName, current + 1)
  }

  /**
   * Get all tool usages as an array
   */
  getUsages(): McpToolUsage[] {
    return Array.from(this.usageMap.entries()).map(([featureName, count]) => ({
      featureName,
      invocationCount:


nd compilation so the route's assets are built without making an
 * HTTP request to the route. This is the same call path the dev server uses
 * when a route is first navigated to, making it useful for warming the module
 * graph, measuring compile time, or pre-compiling routes for memory
 * benchmarking without requiring live backends.
 */
import type { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
import { mcpTelemetryTracker } from '../mcp-telemetry-tracker'
import type { For

  }) => Promise<{ routeSpecifier: string; issues: FormattedIssue[] }>
) {
  server.registerTool(
    'compile_route',
    {
      description:
        'Compile a specific route (page or API route) without making an HTTP request. ' +
        'Triggers the s

mattedIssue } from './utils/format-compilation-issues'
import z from 'next/dist/compiled/zod'

export function registerCompileRouteTool(
  server: McpServer,
  compileRoute: (opts: {
    routeSpecifier?: string
    path?: string


 all routes via Turbopack.
 *
 * Unlike get_errors (which requires a browser session and reflects the runtime
 * error overlay), this tool builds the module graph for every endpoint and
 * collects Turbopack issues directly — no browser needed. Covers
 * module-not-found, syntax errors, and other transform failures across all
 * routes.
 */
import type { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
import type { Project } from '../../../build/swc/types'
import { mcpTelemetryTra

{
  server.registerTool(
    'get_compilation_issues',
    {
      description:
        'Build the module graph for all routes and return all compilation issues (resolve errors, missing modules, transform errors, etc.). Does not require a browser session.

cker } from '../mcp-telemetry-tracker'
import { formatCompilationIssues } from './utils/format-compilation-issues'

export function registerGetCompilationIssuesTool(
  server: McpServer,
  getProject: () => Project | undefined
)