From 3006938f07396f601ad17c5bc3d5e9e9122cafcd Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 26 Jan 2026 11:52:33 -0500 Subject: [PATCH 1/3] chore(nextjs): Update middleware check for proxy usage (#7269) Co-authored-by: Robert Soriano --- .changeset/tangy-sides-crash.md | 5 + .../tests/middleware-placement.test.ts | 38 +++- packages/nextjs/src/app-router/server/auth.ts | 4 +- .../src/server/fs/middleware-location.ts | 16 +- .../src/utils/__tests__/sdk-versions.test.ts | 165 ++++++++++++++++++ packages/nextjs/src/utils/sdk-versions.ts | 25 +++ 6 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 .changeset/tangy-sides-crash.md create mode 100644 packages/nextjs/src/utils/__tests__/sdk-versions.test.ts create mode 100644 packages/nextjs/src/utils/sdk-versions.ts diff --git a/.changeset/tangy-sides-crash.md b/.changeset/tangy-sides-crash.md new file mode 100644 index 00000000000..318a91808ce --- /dev/null +++ b/.changeset/tangy-sides-crash.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Updates middleware location check to account for proxy.ts in next 16+ applications. diff --git a/integration/tests/middleware-placement.test.ts b/integration/tests/middleware-placement.test.ts index 42bc1b7227f..c756d83374a 100644 --- a/integration/tests/middleware-placement.test.ts +++ b/integration/tests/middleware-placement.test.ts @@ -71,10 +71,16 @@ test.describe('next start - missing middleware @quickstart', () => { }); test('Display error for missing middleware', async ({ page, context }) => { + const { version } = await detectNext(app); + const major = parseSemverMajor(version) ?? 0; const u = createTestUtils({ app, page, context }); await u.page.goToAppHome(); - expect(app.serveOutput).toContain('Your Middleware exists at ./src/middleware.(ts|js)'); + const expectedMessage = + major >= 16 + ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your Middleware exists at ./src/middleware.(ts|js)'; + expect(app.serveOutput).toContain(expectedMessage); }); }); @@ -105,10 +111,16 @@ test.describe('next start - invalid middleware at root on src/ @quickstart', () const u = createTestUtils({ app, page, context }); await u.page.goToAppHome(); - expect(app.serveOutput).not.toContain('Your Middleware exists at ./src/middleware.(ts|js)'); - expect(app.serveOutput).toContain( - 'Clerk: clerkMiddleware() was not run, your middleware file might be misplaced. Move your middleware file to ./src/middleware.ts. Currently located at ./middleware.ts', - ); + const expectedMessage = + major >= 16 + ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your Middleware exists at ./src/middleware.(ts|js)'; + expect(app.serveOutput).not.toContain(expectedMessage); + const expectedError = + major >= 16 + ? 'Clerk: clerkMiddleware() was not run, your middleware or proxy file might be misplaced. Move your middleware or proxy file to ./src/middleware.ts. Currently located at ./middleware.ts' + : 'Clerk: clerkMiddleware() was not run, your middleware file might be misplaced. Move your middleware file to ./src/middleware.ts. Currently located at ./middleware.ts'; + expect(app.serveOutput).toContain(expectedError); }); test('Does not display misplaced middleware error on Next 16+', async ({ page, context }) => { @@ -142,11 +154,19 @@ test.describe('next start - invalid middleware inside app on src/ @quickstart', page, context, }) => { + const { version } = await detectNext(app); + const major = parseSemverMajor(version) ?? 0; const u = createTestUtils({ app, page, context }); await u.page.goToAppHome(); - expect(app.serveOutput).not.toContain('Your Middleware exists at ./src/middleware.(ts|js)'); - expect(app.serveOutput).toContain( - 'Clerk: clerkMiddleware() was not run, your middleware file might be misplaced. Move your middleware file to ./src/middleware.ts. Currently located at ./src/app/middleware.ts', - ); + const expectedMessage = + major >= 16 + ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your Middleware exists at ./src/middleware.(ts|js)'; + expect(app.serveOutput).not.toContain(expectedMessage); + const expectedError = + major >= 16 + ? 'Clerk: clerkMiddleware() was not run, your middleware or proxy file might be misplaced. Move your middleware or proxy file to ./src/middleware.ts. Currently located at ./src/app/middleware.ts' + : 'Clerk: clerkMiddleware() was not run, your middleware file might be misplaced. Move your middleware file to ./src/middleware.ts. Currently located at ./src/app/middleware.ts'; + expect(app.serveOutput).toContain(expectedError); }); }); diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index f5ce9a3190d..897bc10abcc 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -11,6 +11,7 @@ import { unauthorized } from '../../server/nextErrors'; import type { AuthProtect } from '../../server/protect'; import { createProtect } from '../../server/protect'; import { decryptClerkRequestData } from '../../server/utils'; +import { isNext16OrHigher } from '../../utils/sdk-versions'; import { buildRequestLike } from './utils'; /** @@ -76,7 +77,8 @@ export const auth: AuthFn = (async (options?: AuthOptions) => { const stepsBasedOnSrcDirectory = async () => { try { const isSrcAppDir = await import('../../server/fs/middleware-location.js').then(m => m.hasSrcAppDir()); - return [`Your Middleware exists at ./${isSrcAppDir ? 'src/' : ''}middleware.(ts|js)`]; + const fileName = isNext16OrHigher ? 'middleware.(ts|js) or proxy.(ts|js)' : 'middleware.(ts|js)'; + return [`Your Middleware exists at ./${isSrcAppDir ? 'src/' : ''}${fileName}`]; } catch { return []; } diff --git a/packages/nextjs/src/server/fs/middleware-location.ts b/packages/nextjs/src/server/fs/middleware-location.ts index 3586d4a1ae2..67a7393e20d 100644 --- a/packages/nextjs/src/server/fs/middleware-location.ts +++ b/packages/nextjs/src/server/fs/middleware-location.ts @@ -1,3 +1,4 @@ +import { isNext16OrHigher } from '../../utils/sdk-versions'; import { nodeCwdOrThrow, nodeFsOrThrow, nodePathOrThrow } from './utils'; function hasSrcAppDir() { @@ -12,12 +13,17 @@ function hasSrcAppDir() { function suggestMiddlewareLocation() { const fileExtensions = ['ts', 'js'] as const; + // Next.js 16+ supports both middleware.ts (Edge runtime) and proxy.ts (Node.js runtime) + const fileNames = isNext16OrHigher ? ['middleware', 'proxy'] : ['middleware']; + const fileNameDisplay = isNext16OrHigher ? 'middleware or proxy' : 'middleware'; + const suggestionMessage = ( + fileName: string, extension: (typeof fileExtensions)[number], to: 'src/' | '', from: 'src/app/' | 'app/' | '', ) => - `Clerk: clerkMiddleware() was not run, your middleware file might be misplaced. Move your middleware file to ./${to}middleware.${extension}. Currently located at ./${from}middleware.${extension}`; + `Clerk: clerkMiddleware() was not run, your ${fileNameDisplay} file might be misplaced. Move your ${fileNameDisplay} file to ./${to}${fileName}.${extension}. Currently located at ./${from}${fileName}.${extension}`; const { existsSync } = nodeFsOrThrow(); const path = nodePathOrThrow(); @@ -31,9 +37,11 @@ function suggestMiddlewareLocation() { to: 'src/' | '', from: 'src/app/' | 'app/' | '', ): string | undefined => { - for (const fileExtension of fileExtensions) { - if (existsSync(path.join(basePath, `middleware.${fileExtension}`))) { - return suggestionMessage(fileExtension, to, from); + for (const fileName of fileNames) { + for (const fileExtension of fileExtensions) { + if (existsSync(path.join(basePath, `${fileName}.${fileExtension}`))) { + return suggestionMessage(fileName, fileExtension, to, from); + } } } return undefined; diff --git a/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts b/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts new file mode 100644 index 00000000000..c37be227534 --- /dev/null +++ b/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts @@ -0,0 +1,165 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +describe('sdk-versions', () => { + beforeEach(() => { + // Clear module cache to allow re-importing with different mocks + vi.resetModules(); + }); + + describe('meetsNextMinimumVersion', () => { + it('should return true when version meets minimum major version', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should return true when version exceeds minimum major version', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '17.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should return false when version is below minimum major version', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '15.9.9' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should return false when version is exactly one below minimum', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '15.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should handle patch versions correctly', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.5.3' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should handle beta/prerelease versions correctly', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.0.0-beta.1' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should return false when version is missing', async () => { + vi.doMock('next/package.json', () => ({ + default: {}, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should return false when version is null', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: null }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should return false when version is undefined', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: undefined }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should return false when version is an empty string', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should return false when version cannot be parsed as a number', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: 'invalid-version' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should handle single-digit major versions', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '9.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + + it('should handle double-digit major versions', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '20.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should handle version strings with leading zeros', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '016.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + }); + + describe('isNext16OrHigher', () => { + it('should be a boolean value', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(typeof isNext16OrHigher).toBe('boolean'); + }); + + it('should correctly identify Next.js 16', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.0.0' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(true); + }); + + it('should correctly identify Next.js 15 as not 16+', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '15.2.3' }, + })); + + const { isNext16OrHigher } = await import('../sdk-versions.js'); + expect(isNext16OrHigher).toBe(false); + }); + }); +}); diff --git a/packages/nextjs/src/utils/sdk-versions.ts b/packages/nextjs/src/utils/sdk-versions.ts new file mode 100644 index 00000000000..2dc49c1698e --- /dev/null +++ b/packages/nextjs/src/utils/sdk-versions.ts @@ -0,0 +1,25 @@ +import nextPkg from 'next/package.json'; + +function meetsNextMinimumVersion(minimumMajorVersion: number) { + if (!nextPkg?.version) { + return false; + } + + const majorVersion = parseInt(nextPkg.version.split('.')[0], 10); + return !isNaN(majorVersion) && majorVersion >= minimumMajorVersion; +} + +const isNext13 = nextPkg?.version?.startsWith('13.') ?? false; + +/** + * Those versions are affected by a bundling issue that will break the application if `node:fs` is used inside a server function. + * The affected versions are >=next@13.5.4 and <=next@14.0.4 + */ +const isNextWithUnstableServerActions = isNext13 || (nextPkg?.version?.startsWith('14.0') ?? false); + +/** + * Next.js 16+ renamed middleware.ts to proxy.ts + */ +const isNext16OrHigher = meetsNextMinimumVersion(16); + +export { isNext13, isNextWithUnstableServerActions, isNext16OrHigher }; From 6be46e910c234d3376262d5fb8e22c1b4db16dec Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 29 Jan 2026 17:08:31 -0500 Subject: [PATCH 2/3] updates --- .../tests/middleware-placement.test.ts | 12 ++++---- packages/nextjs/src/app-router/server/auth.ts | 11 ++++--- packages/nextjs/src/server/errors.ts | 15 ++++++---- .../src/server/fs/middleware-location.ts | 5 ++-- .../src/utils/__tests__/sdk-versions.test.ts | 29 +++++++++++++++++++ packages/nextjs/src/utils/sdk-versions.ts | 7 ++++- 6 files changed, 59 insertions(+), 20 deletions(-) diff --git a/integration/tests/middleware-placement.test.ts b/integration/tests/middleware-placement.test.ts index c756d83374a..c846914f9d8 100644 --- a/integration/tests/middleware-placement.test.ts +++ b/integration/tests/middleware-placement.test.ts @@ -78,8 +78,8 @@ test.describe('next start - missing middleware @quickstart', () => { const expectedMessage = major >= 16 - ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' - : 'Your Middleware exists at ./src/middleware.(ts|js)'; + ? 'Your middleware or proxy file exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your middleware file exists at ./src/middleware.(ts|js)'; expect(app.serveOutput).toContain(expectedMessage); }); }); @@ -113,8 +113,8 @@ test.describe('next start - invalid middleware at root on src/ @quickstart', () const expectedMessage = major >= 16 - ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' - : 'Your Middleware exists at ./src/middleware.(ts|js)'; + ? 'Your middleware or proxy file exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your middleware file exists at ./src/middleware.(ts|js)'; expect(app.serveOutput).not.toContain(expectedMessage); const expectedError = major >= 16 @@ -160,8 +160,8 @@ test.describe('next start - invalid middleware inside app on src/ @quickstart', await u.page.goToAppHome(); const expectedMessage = major >= 16 - ? 'Your Middleware exists at ./src/middleware.(ts|js) or proxy.(ts|js)' - : 'Your Middleware exists at ./src/middleware.(ts|js)'; + ? 'Your middleware or proxy file exists at ./src/middleware.(ts|js) or proxy.(ts|js)' + : 'Your middleware file exists at ./src/middleware.(ts|js)'; expect(app.serveOutput).not.toContain(expectedMessage); const expectedError = major >= 16 diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index 897bc10abcc..af7af1d44a4 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -11,7 +11,7 @@ import { unauthorized } from '../../server/nextErrors'; import type { AuthProtect } from '../../server/protect'; import { createProtect } from '../../server/protect'; import { decryptClerkRequestData } from '../../server/utils'; -import { isNext16OrHigher } from '../../utils/sdk-versions'; +import { middlewareFileReference } from '../../utils/sdk-versions'; import { buildRequestLike } from './utils'; /** @@ -77,15 +77,18 @@ export const auth: AuthFn = (async (options?: AuthOptions) => { const stepsBasedOnSrcDirectory = async () => { try { const isSrcAppDir = await import('../../server/fs/middleware-location.js').then(m => m.hasSrcAppDir()); - const fileName = isNext16OrHigher ? 'middleware.(ts|js) or proxy.(ts|js)' : 'middleware.(ts|js)'; - return [`Your Middleware exists at ./${isSrcAppDir ? 'src/' : ''}${fileName}`]; + const fileName = + middlewareFileReference === 'middleware or proxy' + ? 'middleware.(ts|js) or proxy.(ts|js)' + : 'middleware.(ts|js)'; + return [`Your ${middlewareFileReference} file exists at ./${isSrcAppDir ? 'src/' : ''}${fileName}`]; } catch { return []; } }; const authObject = await createAsyncGetAuth({ debugLoggerName: 'auth()', - noAuthStatusMessage: authAuthHeaderMissing('auth', await stepsBasedOnSrcDirectory()), + noAuthStatusMessage: authAuthHeaderMissing('auth', await stepsBasedOnSrcDirectory(), middlewareFileReference), })(request, { treatPendingAsSignedOut: options?.treatPendingAsSignedOut, acceptsToken: options?.acceptsToken ?? TokenType.SessionToken, diff --git a/packages/nextjs/src/server/errors.ts b/packages/nextjs/src/server/errors.ts index bb0fc99ba4a..59f13223656 100644 --- a/packages/nextjs/src/server/errors.ts +++ b/packages/nextjs/src/server/errors.ts @@ -1,3 +1,5 @@ +import { middlewareFileReference } from '../utils/sdk-versions'; + export const missingDomainAndProxy = ` Missing domain and proxyUrl. A satellite application needs to specify a domain or a proxyUrl. @@ -18,16 +20,17 @@ Check if signInUrl is missing from your configuration or if it is not an absolut NEXT_PUBLIC_CLERK_SIGN_IN_URL='SOME_URL' NEXT_PUBLIC_CLERK_IS_SATELLITE='true'`; -export const getAuthAuthHeaderMissing = () => authAuthHeaderMissing('getAuth'); +export const getAuthAuthHeaderMissing = () => authAuthHeaderMissing('getAuth', undefined, middlewareFileReference); -export const authAuthHeaderMissing = (helperName = 'auth', prefixSteps?: string[]) => - `Clerk: ${helperName}() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: -- ${prefixSteps ? [...prefixSteps, ''].join('\n- ') : ' '}clerkMiddleware() is used in your Next.js Middleware. -- Your Middleware matcher is configured to match this route or page. -- If you are using the src directory, make sure the Middleware file is inside of it. +export const authAuthHeaderMissing = (helperName = 'auth', prefixSteps?: string[], fileReference = 'middleware') => { + return `Clerk: ${helperName}() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: +- ${prefixSteps ? [...prefixSteps, ''].join('\n- ') : ' '}clerkMiddleware() is used in your Next.js ${fileReference} file. +- Your ${fileReference} matcher is configured to match this route or page. +- If you are using the src directory, make sure the ${fileReference} file is inside of it. For more details, see https://clerk.com/err/auth-middleware `; +}; export const authSignatureInvalid = `Clerk: Unable to verify request, this usually means the Clerk middleware did not run. Ensure Clerk's middleware is properly integrated and matches the current route. For more information, see: https://clerk.com/docs/reference/nextjs/clerk-middleware. (code=auth_signature_invalid)`; diff --git a/packages/nextjs/src/server/fs/middleware-location.ts b/packages/nextjs/src/server/fs/middleware-location.ts index 67a7393e20d..765e710c359 100644 --- a/packages/nextjs/src/server/fs/middleware-location.ts +++ b/packages/nextjs/src/server/fs/middleware-location.ts @@ -1,4 +1,4 @@ -import { isNext16OrHigher } from '../../utils/sdk-versions'; +import { isNext16OrHigher, middlewareFileReference } from '../../utils/sdk-versions'; import { nodeCwdOrThrow, nodeFsOrThrow, nodePathOrThrow } from './utils'; function hasSrcAppDir() { @@ -15,7 +15,6 @@ function suggestMiddlewareLocation() { const fileExtensions = ['ts', 'js'] as const; // Next.js 16+ supports both middleware.ts (Edge runtime) and proxy.ts (Node.js runtime) const fileNames = isNext16OrHigher ? ['middleware', 'proxy'] : ['middleware']; - const fileNameDisplay = isNext16OrHigher ? 'middleware or proxy' : 'middleware'; const suggestionMessage = ( fileName: string, @@ -23,7 +22,7 @@ function suggestMiddlewareLocation() { to: 'src/' | '', from: 'src/app/' | 'app/' | '', ) => - `Clerk: clerkMiddleware() was not run, your ${fileNameDisplay} file might be misplaced. Move your ${fileNameDisplay} file to ./${to}${fileName}.${extension}. Currently located at ./${from}${fileName}.${extension}`; + `Clerk: clerkMiddleware() was not run, your ${middlewareFileReference} file might be misplaced. Move your ${middlewareFileReference} file to ./${to}${fileName}.${extension}. Currently located at ./${from}${fileName}.${extension}`; const { existsSync } = nodeFsOrThrow(); const path = nodePathOrThrow(); diff --git a/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts b/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts index c37be227534..b68bff1b1fd 100644 --- a/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts +++ b/packages/nextjs/src/utils/__tests__/sdk-versions.test.ts @@ -162,4 +162,33 @@ describe('sdk-versions', () => { expect(isNext16OrHigher).toBe(false); }); }); + + describe('middlewareFileReference', () => { + it('should return "middleware or proxy" for Next.js 16+', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '16.0.0' }, + })); + + const { middlewareFileReference } = await import('../sdk-versions.js'); + expect(middlewareFileReference).toBe('middleware or proxy'); + }); + + it('should return "middleware" for Next.js < 16', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '15.9.9' }, + })); + + const { middlewareFileReference } = await import('../sdk-versions.js'); + expect(middlewareFileReference).toBe('middleware'); + }); + + it('should return "middleware or proxy" for Next.js 17+', async () => { + vi.doMock('next/package.json', () => ({ + default: { version: '17.0.0' }, + })); + + const { middlewareFileReference } = await import('../sdk-versions.js'); + expect(middlewareFileReference).toBe('middleware or proxy'); + }); + }); }); diff --git a/packages/nextjs/src/utils/sdk-versions.ts b/packages/nextjs/src/utils/sdk-versions.ts index 2dc49c1698e..5981abd54dc 100644 --- a/packages/nextjs/src/utils/sdk-versions.ts +++ b/packages/nextjs/src/utils/sdk-versions.ts @@ -18,8 +18,13 @@ const isNext13 = nextPkg?.version?.startsWith('13.') ?? false; const isNextWithUnstableServerActions = isNext13 || (nextPkg?.version?.startsWith('14.0') ?? false); /** - * Next.js 16+ renamed middleware.ts to proxy.ts + * Next.js 16+ supports proxy.ts (Node.js runtime) as an alternative to middleware.ts (Edge runtime) */ const isNext16OrHigher = meetsNextMinimumVersion(16); +/** + * Display name for middleware/proxy file references in error messages + */ +export const middlewareFileReference = isNext16OrHigher ? 'middleware or proxy' : 'middleware'; + export { isNext13, isNextWithUnstableServerActions, isNext16OrHigher }; From 6ea99af51be3738e7bb330d590a70dc9a7091a6e Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 29 Jan 2026 17:11:55 -0500 Subject: [PATCH 3/3] Apply suggestions from code review --- .changeset/tangy-sides-crash.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/.changeset/tangy-sides-crash.md b/.changeset/tangy-sides-crash.md index 318a91808ce..a845151cc84 100644 --- a/.changeset/tangy-sides-crash.md +++ b/.changeset/tangy-sides-crash.md @@ -1,5 +1,2 @@ --- -'@clerk/nextjs': patch --- - -Updates middleware location check to account for proxy.ts in next 16+ applications.