@netlify/plugin-nextjs 5.10.6 → 5.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm-chunks/{package-GOVVBALR.js → package-SGSU42JZ.js} +3 -3
- package/dist/run/handlers/cache.cjs +1 -1
- package/dist/run/handlers/tracing.js +1 -1
- package/edge-runtime/lib/middleware.test.ts +92 -0
- package/edge-runtime/lib/middleware.ts +26 -0
- package/edge-runtime/lib/response.ts +40 -18
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import "./chunk-OEQOKJGE.js";
|
|
|
8
8
|
|
|
9
9
|
// package.json
|
|
10
10
|
var name = "@netlify/plugin-nextjs";
|
|
11
|
-
var version = "5.10.
|
|
11
|
+
var version = "5.10.7";
|
|
12
12
|
var description = "Run Next.js seamlessly on Netlify";
|
|
13
13
|
var main = "./dist/index.js";
|
|
14
14
|
var type = "module";
|
|
@@ -57,7 +57,7 @@ var bugs = {
|
|
|
57
57
|
var homepage = "https://opennext.js.org/netlify";
|
|
58
58
|
var devDependencies = {
|
|
59
59
|
"@fastly/http-compute-js": "1.1.5",
|
|
60
|
-
"@netlify/blobs": "^8.
|
|
60
|
+
"@netlify/blobs": "^8.2.0",
|
|
61
61
|
"@netlify/build": "^32.0.0",
|
|
62
62
|
"@netlify/edge-bundler": "^13.0.2",
|
|
63
63
|
"@netlify/edge-functions": "^2.11.1",
|
|
@@ -88,7 +88,7 @@ var devDependencies = {
|
|
|
88
88
|
memfs: "^4.9.2",
|
|
89
89
|
"mock-require": "^3.0.3",
|
|
90
90
|
msw: "^2.0.7",
|
|
91
|
-
"netlify-cli": "^20.0.
|
|
91
|
+
"netlify-cli": "^20.0.2",
|
|
92
92
|
next: "^15.0.0-canary.28",
|
|
93
93
|
os: "^0.1.2",
|
|
94
94
|
outdent: "^0.8.0",
|
|
@@ -195,7 +195,7 @@ var import_constants = require("next/dist/lib/constants.js");
|
|
|
195
195
|
|
|
196
196
|
// package.json
|
|
197
197
|
var name = "@netlify/plugin-nextjs";
|
|
198
|
-
var version = "5.10.
|
|
198
|
+
var version = "5.10.7";
|
|
199
199
|
|
|
200
200
|
// src/run/handlers/cache.cts
|
|
201
201
|
var import_cache_types = require("../../shared/cache-types.cjs");
|
|
@@ -68881,7 +68881,7 @@ var import_semantic_conventions = __toESM(require_src(), 1);
|
|
|
68881
68881
|
import { getLogger } from "./request-context.cjs";
|
|
68882
68882
|
var {
|
|
68883
68883
|
default: { version, name }
|
|
68884
|
-
} = await import("../../esm-chunks/package-
|
|
68884
|
+
} = await import("../../esm-chunks/package-SGSU42JZ.js");
|
|
68885
68885
|
var sdk = new import_sdk_node.NodeSDK({
|
|
68886
68886
|
resource: new import_resources.Resource({
|
|
68887
68887
|
[import_semantic_conventions.SEMRESATTRS_SERVICE_NAME]: name,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { assertEquals } from 'https://deno.land/std@0.175.0/testing/asserts.ts'
|
|
2
|
+
import { mergeMiddlewareCookies } from './middleware.ts'
|
|
3
|
+
|
|
4
|
+
const MIDDLEWARE_HEADER = 'x-middleware-set-cookie'
|
|
5
|
+
|
|
6
|
+
Deno.test('mergeMiddlewareCookies', async (t) => {
|
|
7
|
+
await t.step('should handle empty cookies', async () => {
|
|
8
|
+
const request = new Request('https://www.test-url.com')
|
|
9
|
+
const response = new Response()
|
|
10
|
+
|
|
11
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
12
|
+
assertEquals(result, '')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
await t.step('should return request cookies when there are no middleware headers', async () => {
|
|
16
|
+
const request = new Request('https://www.test-url.com')
|
|
17
|
+
const response = new Response()
|
|
18
|
+
|
|
19
|
+
request.headers.set('Cookie', 'oatmeal=raisin')
|
|
20
|
+
|
|
21
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
22
|
+
assertEquals(result, 'oatmeal=raisin')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
await t.step('should not require cookies in request to be set', async () => {
|
|
26
|
+
const request = new Request('https://www.test-url.com')
|
|
27
|
+
const response = new Response()
|
|
28
|
+
|
|
29
|
+
response.headers.set(MIDDLEWARE_HEADER, 'peanut=butter; Path=/')
|
|
30
|
+
|
|
31
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
32
|
+
assertEquals(result, 'peanut=butter')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
await t.step('should merge request and middleware cookies', async () => {
|
|
36
|
+
const request = new Request('https://www.test-url.com')
|
|
37
|
+
const response = new Response()
|
|
38
|
+
|
|
39
|
+
request.headers.set('Cookie', 'oatmeal=raisin')
|
|
40
|
+
response.headers.set(MIDDLEWARE_HEADER, 'peanut=butter; Path=/')
|
|
41
|
+
|
|
42
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
43
|
+
assertEquals(result, 'oatmeal=raisin; peanut=butter')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await t.step('should overwrite request cookies with latest values', async () => {
|
|
47
|
+
const request = new Request('https://www.test-url.com')
|
|
48
|
+
const response = new Response()
|
|
49
|
+
|
|
50
|
+
request.headers.set('Cookie', 'oatmeal=chocolate')
|
|
51
|
+
response.headers.set(MIDDLEWARE_HEADER, 'oatmeal=raisin; Path=/')
|
|
52
|
+
|
|
53
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
54
|
+
assertEquals(result, 'oatmeal=raisin')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
await t.step('should not decode middleware cookie values', async () => {
|
|
58
|
+
const request = new Request('https://www.test-url.com')
|
|
59
|
+
const response = new Response()
|
|
60
|
+
|
|
61
|
+
response.headers.set(MIDDLEWARE_HEADER, 'greeting=Hello%20from%20the%20cookie; Path=/')
|
|
62
|
+
|
|
63
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
64
|
+
assertEquals(result, 'greeting=Hello%20from%20the%20cookie')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await t.step('should support multiple cookies being set in middleware', async () => {
|
|
68
|
+
const request = new Request('https://www.test-url.com')
|
|
69
|
+
const response = new Response()
|
|
70
|
+
|
|
71
|
+
response.headers.set(
|
|
72
|
+
MIDDLEWARE_HEADER,
|
|
73
|
+
'oatmeal=raisin; Path=/,peanut=butter; Path=/,chocolate=chip; Path=/',
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
77
|
+
assertEquals(result, 'oatmeal=raisin; peanut=butter; chocolate=chip')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
await t.step('should ignore comma in middleware cookie expiry', async () => {
|
|
81
|
+
const request = new Request('https://www.test-url.com')
|
|
82
|
+
const response = new Response()
|
|
83
|
+
|
|
84
|
+
response.headers.set(
|
|
85
|
+
MIDDLEWARE_HEADER,
|
|
86
|
+
'oatmeal=raisin; Path=/; Expires=Wed, 23 Apr 2025 13:37:43 GMT; Max-Age=604800',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const result = mergeMiddlewareCookies(response, request)
|
|
90
|
+
assertEquals(result, 'oatmeal=raisin')
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@netlify/edge-functions'
|
|
2
2
|
|
|
3
3
|
import type { ElementHandlers } from '../vendor/deno.land/x/htmlrewriter@v1.0.0/src/index.ts'
|
|
4
|
+
import { getCookies } from '../vendor/deno.land/std@0.175.0/http/cookie.ts'
|
|
4
5
|
|
|
5
6
|
type NextDataTransform = <T>(data: T) => T
|
|
6
7
|
|
|
@@ -58,3 +59,28 @@ export const addMiddlewareHeaders = async (
|
|
|
58
59
|
})
|
|
59
60
|
return response
|
|
60
61
|
}
|
|
62
|
+
|
|
63
|
+
// This serves the same purpose as the mergeMiddlewareCookies in Next.js but has been customized to our domain
|
|
64
|
+
// See: https://github.com/vercel/next.js/blob/6e4495f8430eab33b12cd11dffdd8e27eee6e0cf/packages/next/src/server/async-storage/request-store.ts#L78-L105
|
|
65
|
+
export function mergeMiddlewareCookies(middlewareResponse: Response, lambdaRequest: Request) {
|
|
66
|
+
let mergedCookies = getCookies(lambdaRequest.headers)
|
|
67
|
+
const middlewareCookies = middlewareResponse.headers.get('x-middleware-set-cookie')
|
|
68
|
+
|
|
69
|
+
if (middlewareCookies) {
|
|
70
|
+
// Targets commas that are not followed by whitespace
|
|
71
|
+
// See: https://github.com/vercel/next.js/blob/e6145d3a37bb4c7b481fd58e05cdff9046ace8ad/packages/next/src/server/web/spec-extension/response.ts#L58-L66
|
|
72
|
+
const regex = new RegExp(/,(?!\s)/)
|
|
73
|
+
|
|
74
|
+
middlewareCookies.split(regex).forEach((entry) => {
|
|
75
|
+
// Extra directives within a cookie are joined on separated by "; "
|
|
76
|
+
// See: https://github.com/vercel/next.js/blob/0edb1123066a010eff2aac274f948ca2c6e85c0f/packages/next/src/compiled/%40edge-runtime/cookies/index.js#L32-L47
|
|
77
|
+
const [cookie] = entry.split('; ')
|
|
78
|
+
const [name, value] = cookie.split('=')
|
|
79
|
+
mergedCookies[name] = value
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Object.entries(mergedCookies)
|
|
84
|
+
.map((kv) => kv.join('='))
|
|
85
|
+
.join('; ')
|
|
86
|
+
}
|
|
@@ -6,7 +6,12 @@ import {
|
|
|
6
6
|
|
|
7
7
|
import { updateModifiedHeaders } from './headers.ts'
|
|
8
8
|
import type { StructuredLogger } from './logging.ts'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
addMiddlewareHeaders,
|
|
11
|
+
isMiddlewareRequest,
|
|
12
|
+
isMiddlewareResponse,
|
|
13
|
+
mergeMiddlewareCookies,
|
|
14
|
+
} from './middleware.ts'
|
|
10
15
|
import { RequestData } from './next-request.ts'
|
|
11
16
|
import {
|
|
12
17
|
addBasePath,
|
|
@@ -116,12 +121,13 @@ export const buildResponse = async ({
|
|
|
116
121
|
}
|
|
117
122
|
return rewriter.transform(response.originResponse)
|
|
118
123
|
}
|
|
119
|
-
|
|
124
|
+
|
|
125
|
+
const edgeResponse = new Response(result.response.body, result.response)
|
|
120
126
|
request.headers.set('x-nf-next-middleware', 'skip')
|
|
121
127
|
|
|
122
|
-
let rewrite =
|
|
123
|
-
let redirect =
|
|
124
|
-
let nextRedirect =
|
|
128
|
+
let rewrite = edgeResponse.headers.get('x-middleware-rewrite')
|
|
129
|
+
let redirect = edgeResponse.headers.get('location')
|
|
130
|
+
let nextRedirect = edgeResponse.headers.get('x-nextjs-redirect')
|
|
125
131
|
|
|
126
132
|
// Data requests (i.e. requests for /_next/data ) need special handling
|
|
127
133
|
const isDataReq = request.headers.has('x-nextjs-data')
|
|
@@ -152,7 +158,7 @@ export const buildResponse = async ({
|
|
|
152
158
|
// Data requests might be rewritten to an external URL
|
|
153
159
|
// This header tells the client router the redirect target, and if it's external then it will do a full navigation
|
|
154
160
|
|
|
155
|
-
|
|
161
|
+
edgeResponse.headers.set('x-nextjs-rewrite', relativeUrl)
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
if (rewriteUrl.origin !== baseUrl.origin) {
|
|
@@ -178,7 +184,7 @@ export const buildResponse = async ({
|
|
|
178
184
|
})
|
|
179
185
|
}
|
|
180
186
|
|
|
181
|
-
return addMiddlewareHeaders(fetch(proxyRequest, { redirect: 'manual' }),
|
|
187
|
+
return addMiddlewareHeaders(fetch(proxyRequest, { redirect: 'manual' }), edgeResponse)
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
if (isDataReq) {
|
|
@@ -197,9 +203,17 @@ export const buildResponse = async ({
|
|
|
197
203
|
logger.withFields({ rewrite_url: rewrite }).debug('Rewrite url is same as original url')
|
|
198
204
|
return
|
|
199
205
|
}
|
|
200
|
-
|
|
206
|
+
edgeResponse.headers.set('x-middleware-rewrite', relativeUrl)
|
|
201
207
|
request.headers.set('x-middleware-rewrite', target)
|
|
202
|
-
|
|
208
|
+
|
|
209
|
+
// coookies set in middleware need to be available during the lambda request
|
|
210
|
+
const newRequest = new Request(target, request)
|
|
211
|
+
const newRequestCookies = mergeMiddlewareCookies(edgeResponse, newRequest)
|
|
212
|
+
if (newRequestCookies) {
|
|
213
|
+
newRequest.headers.set('Cookie', newRequestCookies)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return addMiddlewareHeaders(context.next(newRequest), edgeResponse)
|
|
203
217
|
}
|
|
204
218
|
|
|
205
219
|
if (redirect) {
|
|
@@ -208,27 +222,35 @@ export const buildResponse = async ({
|
|
|
208
222
|
logger.withFields({ redirect_url: redirect }).debug('Redirect url is same as original url')
|
|
209
223
|
return
|
|
210
224
|
}
|
|
211
|
-
|
|
225
|
+
edgeResponse.headers.set('location', redirect)
|
|
212
226
|
}
|
|
213
227
|
|
|
214
228
|
// Data requests shouldn't automatically redirect in the browser (they might be HTML pages): they're handled by the router
|
|
215
229
|
if (redirect && isDataReq) {
|
|
216
|
-
|
|
217
|
-
|
|
230
|
+
edgeResponse.headers.delete('location')
|
|
231
|
+
edgeResponse.headers.set('x-nextjs-redirect', relativizeURL(redirect, request.url))
|
|
218
232
|
}
|
|
219
233
|
|
|
220
|
-
nextRedirect =
|
|
234
|
+
nextRedirect = edgeResponse.headers.get('x-nextjs-redirect')
|
|
221
235
|
|
|
222
236
|
if (nextRedirect && isDataReq) {
|
|
223
|
-
|
|
237
|
+
edgeResponse.headers.set('x-nextjs-redirect', normalizeDataUrl(nextRedirect))
|
|
224
238
|
}
|
|
225
239
|
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
if (edgeResponse.headers.get('x-middleware-next') === '1') {
|
|
241
|
+
edgeResponse.headers.delete('x-middleware-next')
|
|
242
|
+
|
|
243
|
+
// coookies set in middleware need to be available during the lambda request
|
|
244
|
+
const newRequest = new Request(request)
|
|
245
|
+
const newRequestCookies = mergeMiddlewareCookies(edgeResponse, newRequest)
|
|
246
|
+
if (newRequestCookies) {
|
|
247
|
+
newRequest.headers.set('Cookie', newRequestCookies)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return addMiddlewareHeaders(context.next(newRequest), edgeResponse)
|
|
229
251
|
}
|
|
230
252
|
|
|
231
|
-
return
|
|
253
|
+
return edgeResponse
|
|
232
254
|
}
|
|
233
255
|
|
|
234
256
|
/**
|