@@ -12,6 +12,7 @@ import type {
12
12
RoutingResult ,
13
13
} from "types/open-next" ;
14
14
15
+ import type { AssetResolver } from "types/overrides" ;
15
16
import { debug , error } from "../adapters/logger" ;
16
17
import { cacheInterceptor } from "./routing/cacheInterceptor" ;
17
18
import { detectLocale } from "./routing/i18n" ;
@@ -47,23 +48,31 @@ const geoHeaderToNextHeader = {
47
48
"x-open-next-longitude" : "x-vercel-ip-longitude" ,
48
49
} ;
49
50
51
+ /**
52
+ * Adds the middleware headers to an event or result.
53
+ *
54
+ * @param eventOrResult
55
+ * @param middlewareHeaders
56
+ */
50
57
function applyMiddlewareHeaders (
51
- eventHeaders : Record < string , string | string [ ] > ,
58
+ eventOrResult : InternalEvent | InternalResult ,
52
59
middlewareHeaders : Record < string , string | string [ ] | undefined > ,
53
- setPrefix = true ,
54
60
) {
55
- const keyPrefix = setPrefix ? MIDDLEWARE_HEADER_PREFIX : "" ;
61
+ // Use the `MIDDLEWARE_HEADER_PREFIX` prefix for events, they will be processed by the request handler later.
62
+ // Results do not go through the request handler and should not be prefixed.
63
+ const isResult = isInternalResult ( eventOrResult ) ;
64
+ const headers = eventOrResult . headers ;
65
+ const keyPrefix = isResult ? "" : MIDDLEWARE_HEADER_PREFIX ;
56
66
Object . entries ( middlewareHeaders ) . forEach ( ( [ key , value ] ) => {
57
67
if ( value ) {
58
- eventHeaders [ keyPrefix + key ] = Array . isArray ( value )
59
- ? value . join ( "," )
60
- : value ;
68
+ headers [ keyPrefix + key ] = Array . isArray ( value ) ? value . join ( "," ) : value ;
61
69
}
62
70
} ) ;
63
71
}
64
72
65
73
export default async function routingHandler (
66
74
event : InternalEvent ,
75
+ { assetResolver } : { assetResolver ?: AssetResolver } ,
67
76
) : Promise < InternalResult | RoutingResult > {
68
77
try {
69
78
// Add Next geo headers
@@ -87,14 +96,17 @@ export default async function routingHandler(
87
96
}
88
97
}
89
98
90
- const nextHeaders = getNextConfigHeaders ( event , ConfigHeaders ) ;
99
+ // Headers from the Next config and middleware (the later are applied further down).
100
+ let headers : Record < string , string | string [ ] | undefined > =
101
+ getNextConfigHeaders ( event , ConfigHeaders ) ;
91
102
92
- let internalEvent = fixDataPage ( event , BuildId ) ;
93
- if ( "statusCode" in internalEvent ) {
94
- return internalEvent ;
103
+ let eventOrResult = fixDataPage ( event , BuildId ) ;
104
+
105
+ if ( isInternalResult ( eventOrResult ) ) {
106
+ return eventOrResult ;
95
107
}
96
108
97
- const redirect = handleRedirects ( internalEvent , RoutesManifest . redirects ) ;
109
+ const redirect = handleRedirects ( eventOrResult , RoutesManifest . redirects ) ;
98
110
if ( redirect ) {
99
111
// We need to encode the value in the Location header to make sure it is valid according to RFC
100
112
// https://stackoverflow.com/a/7654605/16587222
@@ -105,70 +117,84 @@ export default async function routingHandler(
105
117
return redirect ;
106
118
}
107
119
108
- const eventOrResult = await handleMiddleware (
109
- internalEvent ,
120
+ const middlewareEventOrResult = await handleMiddleware (
121
+ eventOrResult ,
110
122
// We need to pass the initial search without any decoding
111
123
// TODO: we'd need to refactor InternalEvent to include the initial querystring directly
112
124
// Should be done in another PR because it is a breaking change
113
125
new URL ( event . url ) . search ,
114
126
) ;
115
- const isResult = "statusCode" in eventOrResult ;
116
- if ( isResult ) {
117
- return eventOrResult ;
127
+ if ( isInternalResult ( middlewareEventOrResult ) ) {
128
+ return middlewareEventOrResult ;
118
129
}
119
- const middlewareResponseHeaders = eventOrResult . responseHeaders ;
120
- let isExternalRewrite = eventOrResult . isExternalRewrite ?? false ;
121
- // internalEvent is `InternalEvent | MiddlewareEvent`
122
- internalEvent = eventOrResult ;
130
+
131
+ headers = {
132
+ ...middlewareEventOrResult . responseHeaders ,
133
+ ...headers ,
134
+ } ;
135
+ let isExternalRewrite = middlewareEventOrResult . isExternalRewrite ?? false ;
136
+ eventOrResult = middlewareEventOrResult ;
123
137
124
138
if ( ! isExternalRewrite ) {
125
139
// First rewrite to be applied
126
- const beforeRewrites = handleRewrites (
127
- internalEvent ,
140
+ const beforeRewrite = handleRewrites (
141
+ eventOrResult ,
128
142
RoutesManifest . rewrites . beforeFiles ,
129
143
) ;
130
- internalEvent = beforeRewrites . internalEvent ;
131
- isExternalRewrite = beforeRewrites . isExternalRewrite ;
144
+ eventOrResult = beforeRewrite . internalEvent ;
145
+ isExternalRewrite = beforeRewrite . isExternalRewrite ;
146
+ // Check for matching public files after `beforeFiles` rewrites
147
+ // See:
148
+ // - https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order
149
+ // - https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites
150
+ if ( ! isExternalRewrite ) {
151
+ const assetResult =
152
+ await assetResolver ?. maybeGetAssetResult ?.( eventOrResult ) ;
153
+ if ( assetResult ) {
154
+ applyMiddlewareHeaders ( assetResult , headers ) ;
155
+ return assetResult ;
156
+ }
157
+ }
132
158
}
133
- const foundStaticRoute = staticRouteMatcher ( internalEvent . rawPath ) ;
159
+ const foundStaticRoute = staticRouteMatcher ( eventOrResult . rawPath ) ;
134
160
const isStaticRoute = ! isExternalRewrite && foundStaticRoute . length > 0 ;
135
161
136
162
if ( ! ( isStaticRoute || isExternalRewrite ) ) {
137
163
// Second rewrite to be applied
138
- const afterRewrites = handleRewrites (
139
- internalEvent ,
164
+ const afterRewrite = handleRewrites (
165
+ eventOrResult ,
140
166
RoutesManifest . rewrites . afterFiles ,
141
167
) ;
142
- internalEvent = afterRewrites . internalEvent ;
143
- isExternalRewrite = afterRewrites . isExternalRewrite ;
168
+ eventOrResult = afterRewrite . internalEvent ;
169
+ isExternalRewrite = afterRewrite . isExternalRewrite ;
144
170
}
145
171
146
172
let isISR = false ;
147
173
// We want to run this just before the dynamic route check
148
174
// We can skip it if its an external rewrite
149
175
if ( ! isExternalRewrite ) {
150
176
const fallbackResult = handleFallbackFalse (
151
- internalEvent ,
177
+ eventOrResult ,
152
178
PrerenderManifest ,
153
179
) ;
154
- internalEvent = fallbackResult . event ;
180
+ eventOrResult = fallbackResult . event ;
155
181
isISR = fallbackResult . isISR ;
156
182
}
157
183
158
- const foundDynamicRoute = dynamicRouteMatcher ( internalEvent . rawPath ) ;
184
+ const foundDynamicRoute = dynamicRouteMatcher ( eventOrResult . rawPath ) ;
159
185
const isDynamicRoute = ! isExternalRewrite && foundDynamicRoute . length > 0 ;
160
186
161
187
if ( ! ( isDynamicRoute || isStaticRoute || isExternalRewrite ) ) {
162
188
// Fallback rewrite to be applied
163
189
const fallbackRewrites = handleRewrites (
164
- internalEvent ,
190
+ eventOrResult ,
165
191
RoutesManifest . rewrites . fallback ,
166
192
) ;
167
- internalEvent = fallbackRewrites . internalEvent ;
193
+ eventOrResult = fallbackRewrites . internalEvent ;
168
194
isExternalRewrite = fallbackRewrites . isExternalRewrite ;
169
195
}
170
196
171
- const isNextImageRoute = internalEvent . rawPath . startsWith ( "/_next/image" ) ;
197
+ const isNextImageRoute = eventOrResult . rawPath . startsWith ( "/_next/image" ) ;
172
198
173
199
const isRouteFoundBeforeAllRewrites =
174
200
isStaticRoute || isDynamicRoute || isExternalRewrite ;
@@ -180,16 +206,16 @@ export default async function routingHandler(
180
206
isRouteFoundBeforeAllRewrites ||
181
207
isNextImageRoute ||
182
208
// We need to check again once all rewrites have been applied
183
- staticRouteMatcher ( internalEvent . rawPath ) . length > 0 ||
184
- dynamicRouteMatcher ( internalEvent . rawPath ) . length > 0
209
+ staticRouteMatcher ( eventOrResult . rawPath ) . length > 0 ||
210
+ dynamicRouteMatcher ( eventOrResult . rawPath ) . length > 0
185
211
)
186
212
) {
187
- internalEvent = {
188
- ...internalEvent ,
213
+ eventOrResult = {
214
+ ...eventOrResult ,
189
215
rawPath : "/404" ,
190
- url : constructNextUrl ( internalEvent . url , "/404" ) ,
216
+ url : constructNextUrl ( eventOrResult . url , "/404" ) ,
191
217
headers : {
192
- ...internalEvent . headers ,
218
+ ...eventOrResult . headers ,
193
219
"x-middleware-response-cache-control" :
194
220
"private, no-cache, no-store, max-age=0, must-revalidate" ,
195
221
} ,
@@ -198,28 +224,18 @@ export default async function routingHandler(
198
224
199
225
if (
200
226
globalThis . openNextConfig . dangerous ?. enableCacheInterception &&
201
- ! ( "statusCode" in internalEvent )
227
+ ! isInternalResult ( eventOrResult )
202
228
) {
203
229
debug ( "Cache interception enabled" ) ;
204
- internalEvent = await cacheInterceptor ( internalEvent ) ;
205
- if ( "statusCode" in internalEvent ) {
206
- applyMiddlewareHeaders (
207
- internalEvent . headers ,
208
- {
209
- ...middlewareResponseHeaders ,
210
- ...nextHeaders ,
211
- } ,
212
- false ,
213
- ) ;
214
- return internalEvent ;
230
+ eventOrResult = await cacheInterceptor ( eventOrResult ) ;
231
+ if ( isInternalResult ( eventOrResult ) ) {
232
+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
233
+ return eventOrResult ;
215
234
}
216
235
}
217
236
218
237
// We apply the headers from the middleware response last
219
- applyMiddlewareHeaders ( internalEvent . headers , {
220
- ...middlewareResponseHeaders ,
221
- ...nextHeaders ,
222
- } ) ;
238
+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
223
239
224
240
const resolvedRoutes : ResolvedRoute [ ] = [
225
241
...foundStaticRoute ,
@@ -229,14 +245,14 @@ export default async function routingHandler(
229
245
debug ( "resolvedRoutes" , resolvedRoutes ) ;
230
246
231
247
return {
232
- internalEvent,
248
+ internalEvent : eventOrResult ,
233
249
isExternalRewrite,
234
250
origin : false ,
235
251
isISR,
236
252
resolvedRoutes,
237
253
initialURL : event . url ,
238
254
locale : NextConfig . i18n
239
- ? detectLocale ( internalEvent , NextConfig . i18n )
255
+ ? detectLocale ( eventOrResult , NextConfig . i18n )
240
256
: undefined ,
241
257
} ;
242
258
} catch ( e ) {
@@ -266,3 +282,13 @@ export default async function routingHandler(
266
282
} ;
267
283
}
268
284
}
285
+
286
+ /**
287
+ * @param eventOrResult
288
+ * @returns Whether the event is an instance of `InternalResult`
289
+ */
290
+ function isInternalResult (
291
+ eventOrResult : InternalEvent | InternalResult ,
292
+ ) : eventOrResult is InternalResult {
293
+ return eventOrResult != null && "statusCode" in eventOrResult ;
294
+ }
0 commit comments