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

Activity

  1. added
    bugConfirmed defect or regression
    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
    on Aug 13, 2026
  2. proggeramlug commented on Aug 16, 2026

    @proggeramlug
    ContributorAuthor

    Closing: everything this issue asked for has landed on main except the forced-evacuation arm, which was split out as #8163 and stays open there.

    PR #8082's body says "Closes #8034 and #8036", but GitHub only links the first reference in that phrasing (each issue needs its own keyword), so #8034 auto-closed on merge and this one did not. Closing manually per that intent.

    What landed, and where it is gated

    What is NOT closed here — acceptance bullet 4, "passes with forced/verified GC": the production route under PERRY_GC_FORCE_EVACUATE=1 … PERRY_GC_SCHEDULE_SEED=8036 still dies with TypeError: value is not a function from a holder outside the GC heap. That is #8163, opt-in in the fixture behind PERRY_NEXT_ROUTE_FORCED_GC=1 (off, fails loudly when set — not a SKIP, not continue-on-error).

    One reconciliation note for whoever touches the fixtures next: the release-tier copy's perry-host.js does not carry the bypass guard that tests/fixtures/next-app-route/perry-host.js has; it relies on the forbidden-diagnostic grep instead. Both copies should converge on the armed host.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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