Skip to content

Commit 85514f2

Browse files
tlhunterrochdev
authored andcommitted
next.js: complete v14.x compatibility (fixing >=14.2.7) (#4916)
- fixs compatibility with Next.js >=v14.2.7 - 14.x - previously there were 27 test failures - note that this doesn't address v15.x, I'll do that in a follow up PR - Next.js 14.2.7 broke compat when the internal headers concept was replaced with a symbol on the request object - it was further made complicated by us relying on the removal of said internal headers - now that they use a symbol they just keep the data around throughout the various stages of the request - for that reason I'm using a `WeakSet` to track the two stages of the request - @see AIDM-339
1 parent c9f2654 commit 85514f2

File tree

3 files changed

+23
-9
lines changed

3 files changed

+23
-9
lines changed

.github/workflows/appsec.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,12 @@ jobs:
211211

212212
next:
213213
strategy:
214+
fail-fast: false
214215
matrix:
215216
version:
216217
- 18
217218
- latest
218-
range: ['9.5.0', '11.1.4', '13.2.0', '14.2.6']
219+
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15']
219220
runs-on: ubuntu-latest
220221
env:
221222
PLUGINS: next

.github/workflows/plugins.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -739,11 +739,12 @@ jobs:
739739
# TODO: fix performance issues and test more Node versions
740740
next:
741741
strategy:
742+
fail-fast: false
742743
matrix:
743744
version:
744745
- 18
745746
- latest
746-
range: ['9.5.0', '11.1.4', '13.2.0', '14.2.6']
747+
range: ['9.5.0', '11.1.4', '13.2.0', '>=14.0.0 <=14.2.6', '>=14.2.7 <15']
747748
runs-on: ubuntu-latest
748749
env:
749750
PLUGINS: next

packages/datadog-instrumentations/src/next.js

+19-7
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ const queryParsedChannel = channel('apm:next:query-parsed')
1414
const requests = new WeakSet()
1515
const nodeNextRequestsToNextRequests = new WeakMap()
1616

17+
// Next.js <= 14.2.6
1718
const MIDDLEWARE_HEADER = 'x-middleware-invoke'
1819

20+
// Next.js >= 14.2.7
21+
const NEXT_REQUEST_META = Symbol.for('NextInternalRequestMeta')
22+
const META_IS_MIDDLEWARE = 'middlewareInvoke'
23+
const encounteredMiddleware = new WeakSet()
24+
1925
function wrapHandleRequest (handleRequest) {
2026
return function (req, res, pathname, query) {
2127
return instrument(req, res, () => handleRequest.apply(this, arguments))
@@ -111,6 +117,11 @@ function getPageFromPath (page, dynamicRoutes = []) {
111117
return getPagePath(page)
112118
}
113119

120+
function getRequestMeta (req, key) {
121+
const meta = req[NEXT_REQUEST_META] || {}
122+
return typeof key === 'string' ? meta[key] : meta
123+
}
124+
114125
function instrument (req, res, error, handler) {
115126
if (typeof error === 'function') {
116127
handler = error
@@ -121,8 +132,9 @@ function instrument (req, res, error, handler) {
121132
res = res.originalResponse || res
122133

123134
// TODO support middleware properly in the future?
124-
const isMiddleware = req.headers[MIDDLEWARE_HEADER]
125-
if (isMiddleware || requests.has(req)) {
135+
const isMiddleware = req.headers[MIDDLEWARE_HEADER] || getRequestMeta(req, META_IS_MIDDLEWARE)
136+
if ((isMiddleware && !encounteredMiddleware.has(req)) || requests.has(req)) {
137+
encounteredMiddleware.add(req)
126138
if (error) {
127139
errorChannel.publish({ error })
128140
}
@@ -188,7 +200,7 @@ function finish (ctx, result, err) {
188200
// however, it is not provided as a class function or exported property
189201
addHook({
190202
name: 'next',
191-
versions: ['>=13.3.0 <14.2.7'],
203+
versions: ['>=13.3.0 <15'],
192204
file: 'dist/server/web/spec-extension/adapters/next-request.js'
193205
}, NextRequestAdapter => {
194206
shimmer.wrap(NextRequestAdapter.NextRequestAdapter, 'fromNodeNextRequest', fromNodeNextRequest => {
@@ -203,7 +215,7 @@ addHook({
203215

204216
addHook({
205217
name: 'next',
206-
versions: ['>=11.1 <14.2.7'],
218+
versions: ['>=11.1 <15'],
207219
file: 'dist/server/serve-static.js'
208220
}, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
209221

@@ -213,7 +225,7 @@ addHook({
213225
file: 'dist/next-server/server/serve-static.js'
214226
}, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
215227

216-
addHook({ name: 'next', versions: ['>=11.1 <14.2.7'], file: 'dist/server/next-server.js' }, nextServer => {
228+
addHook({ name: 'next', versions: ['>=11.1 <15'], file: 'dist/server/next-server.js' }, nextServer => {
217229
const Server = nextServer.default
218230

219231
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
@@ -230,7 +242,7 @@ addHook({ name: 'next', versions: ['>=11.1 <14.2.7'], file: 'dist/server/next-se
230242
})
231243

232244
// `handleApiRequest` changes parameters/implementation at 13.2.0
233-
addHook({ name: 'next', versions: ['>=13.2 <14.2.7'], file: 'dist/server/next-server.js' }, nextServer => {
245+
addHook({ name: 'next', versions: ['>=13.2 <15'], file: 'dist/server/next-server.js' }, nextServer => {
234246
const Server = nextServer.default
235247
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
236248
return nextServer
@@ -264,7 +276,7 @@ addHook({
264276

265277
addHook({
266278
name: 'next',
267-
versions: ['>=13 <14.2.7'],
279+
versions: ['>=13 <15'],
268280
file: 'dist/server/web/spec-extension/request.js'
269281
}, request => {
270282
const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')

0 commit comments

Comments
 (0)