Repository: vercel/next.js
Pull request: Fix missing incremental cache in middleware unstable_cache

## Summary
- pass `IncrementalCache` into the shared middleware template adapter so middleware/proxy modules initialize `globalThis.__incrementalCache`
- add regression coverage for `unstable_cache` usage inside both deprecated `middleware` (edge) and `proxy` (node) fixtures
- verify both e2e suites pass without hitting `Invariant: incrementalCache missing in unstable_cache`

## Verification
- `pnpm build`
- `NEXT_SKIP_ISOLATE=1 NEXT_TEST_MODE=dev pnpm testonly test/e2e/app-dir/app-middleware/app-middleware.test.ts`
- `NEXT_SKIP_ISOLATE=1 NEXT_TEST_MODE=dev pnpm testonly test/e2e/app-dir/app-middleware-proxy/app-middleware-proxy.test.ts`


Changed files:
filename: packages/next/src/build/templates/middleware.ts
patch: @@ -3,6 +3,7 @@ import '../adapter/setup-node-env.external'
 import '../../server/web/globals'
 
 import { adapter } from '../../server/web/adapter'
+import { IncrementalCache } from '../../server/lib/incremental-cache'
 
 // Import the userland code.
 import * as _mod from 'VAR_USERLAND'
@@ -74,6 +75,7 @@ function errorHandledHandler(fn: AdapterOptions['handler']) {
 const internalHandler: EdgeHandler = (opts) => {
   return adapter({
     ...opts,
+    IncrementalCache,
     page,
     handler: errorHandledHandler(handlerUserland),
   })

filename: test/e2e/app-dir/app-middleware-proxy/app-middleware-proxy.test.ts
patch: @@ -136,6 +136,14 @@ describe('app-dir with proxy', () => {
     )
   })
 
+  it('should support unstable_cache in proxy', async () => {
+    const res = await next.fetch('/unstable-cache')
+    expect(res.status).toBe(200)
+    expect(await res.json()).toEqual({
+      value: expect.any(String),
+    })
+  })
+
   it('should be possible to modify cookies & read them in an RSC in a single request', async () => {
     const browser = await next.browser('/rsc-cookies')
 

filename: test/e2e/app-dir/app-middleware-proxy/proxy.js
patch: @@ -1,7 +1,13 @@
 import { NextResponse } from 'next/server'
 
+import { unstable_cache } from 'next/cache'
 import { headers as nextHeaders } from 'next/headers'
 
+const getCachedValue = unstable_cache(
+  async () => Math.random().toString(),
+  ['proxy-cache-probe']
+)
+
 /**
  * @param {import('next/server').NextRequest} request
  */
@@ -80,6 +86,11 @@ export async function proxy(request) {
     return res
   }
 
+  if (request.nextUrl.pathname === '/unstable-cache') {
+    const value = await getCachedValue()
+    return NextResponse.json({ value })
+  }
+
   if (request.nextUrl.pathname === '/test-location-header') {
     return NextResponse.json(
       { foo: 'bar' },

filename: test/e2e/app-dir/app-middleware/app-middleware.test.ts
patch: @@ -137,6 +137,14 @@ describe('app-dir with middleware', () => {
     )
   })
 
+  it('should support unstable_cache in middleware', async () => {
+    const res = await next.fetch('/unstable-cache')
+    expect(res.status).toBe(200)
+    expect(await res.json()).toEqual({
+      value: expect.any(String),
+    })
+  })
+
   it('should be possible to modify cookies & read them in an RSC in a single request', async () => {
     const browser = await next.browser('/rsc-cookies')
 

filename: test/e2e/app-dir/app-middleware/middleware.js
patch: @@ -1,7 +1,13 @@
 import { NextResponse } from 'next/server'
 
+import { unstable_cache } from 'next/cache'
 import { headers as nextHeaders, draftMode } from 'next/headers'
 
+const getCachedValue = unstable_cache(
+  async () => Math.random().toString(),
+  ['middleware-cache-probe']
+)
+
 /**
  * @param {import('next/server').NextRequest} request
  */
@@ -78,6 +84,11 @@ export async function middleware(request) {
     return res
   }
 
+  if (request.nextUrl.pathname === '/unstable-cache') {
+    const value = await getCachedValue()
+    return NextResponse.json({ value })
+  }
+
   if (request.nextUrl.pathname === '/test-location-header') {
     return NextResponse.json(
       { foo: 'bar' },