Skip to content

[Next.js/dylib] Preserve NextRequest and nextUrl.searchParams across App Route imports #8036

Description

@proggeramlug

What happened?

The Perch Next.js lower-bound fixture on Perry 0.5.1503 could construct a NextRequest in one module and call an imported route function, but could not rely on the callee receiving a usable request argument. NextRequest.nextUrl.searchParams was also not reliable through that path, so the benchmark had to ignore the request and hard-code its workload.

This remains a distinct end-to-end gap even though important lower-level fixes have landed: #7252 roots cross-module call arguments and #6738 backs URLSearchParams subclasses. The missing regression is the actual production Next request shape across the generated App Route/import boundary.

Reproduction app (provided)

Use the exact pinned app and verifier attached to #8034. Do not create a new app. Its route is deliberately re-exported across a module boundary:

AppRouteRouteModule.handle
  -> app/api/benchmark/route.ts
  -> lib/route-impl.ts::GET/POST

Build with:

bash make-next-app-route-fixture.sh
cd perry-next-app-route-fixture
npm install --package-lock-only
npm ci
npm run build

The focused request is:

curl -i \
  -H 'x-request-id: request-7' \
  'http://127.0.0.1:3100/api/benchmark?id=request-7&iterations=7'

The returned JSON must contain:

{
  "method": "GET",
  "pathname": "/api/benchmark",
  "id": "request-7",
  "iterations": 7,
  "beforeAwait": "request-7",
  "afterAwait": "request-7",
  "requestBody": ""
}

The attached verifier also sends a POST and requires requestBody === "perry-request-body".

Required implementation behavior

  • Preserve the NextRequest object passed by the generated App Route module into the re-exported user handler.
  • Preserve method, URL, nextUrl, ReadonlyURLSearchParams, headers, and body state across the imported call.
  • Keep the request live across allocations, dynamic import, promise continuation, timer continuation, and GC movement.
  • Do not special-case the fixture or lower NextRequest to a partial plain object.
  • Keep direct calls, re-exports, sync handlers, and async handlers on the same correct path.

Regression coverage

Add a small compiler fixture for a request-shaped class passed through a re-exported imported function, plus the production integration test from #8034. Run the small fixture with forced evacuation / GC verification so a stale-but-lucky pointer cannot pass.

Acceptance criteria

  • All request-field assertions in [Next.js/dylib] Add a pinned production App Route parity fixture and CI gate #8034's verify.mjs pass through the production routeModule.handle path.
  • GET query data and POST body data are correct on the first cold call and subsequent warm calls.
  • The 20 concurrent requests never exchange IDs or headers.
  • The test passes with forced/verified GC and with normal GC.
  • No direct call to the userland GET, hard-coded query value, response fabrication, or other compatibility fallback exists in the Perry test path.

Baseline / references

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions