@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.
@@ -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.6";
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.1.2",
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.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.6";
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-GOVVBALR.js");
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 { addMiddlewareHeaders, isMiddlewareRequest, isMiddlewareResponse } from './middleware.ts'
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
- const res = new Response(result.response.body, result.response)
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 = res.headers.get('x-middleware-rewrite')
123
- let redirect = res.headers.get('location')
124
- let nextRedirect = res.headers.get('x-nextjs-redirect')
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
- res.headers.set('x-nextjs-rewrite', relativeUrl)
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' }), res)
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
- res.headers.set('x-middleware-rewrite', relativeUrl)
206
+ edgeResponse.headers.set('x-middleware-rewrite', relativeUrl)
201
207
  request.headers.set('x-middleware-rewrite', target)
202
- return addMiddlewareHeaders(context.rewrite(target), res)
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
- res.headers.set('location', redirect)
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
- res.headers.delete('location')
217
- res.headers.set('x-nextjs-redirect', relativizeURL(redirect, request.url))
230
+ edgeResponse.headers.delete('location')
231
+ edgeResponse.headers.set('x-nextjs-redirect', relativizeURL(redirect, request.url))
218
232
  }
219
233
 
220
- nextRedirect = res.headers.get('x-nextjs-redirect')
234
+ nextRedirect = edgeResponse.headers.get('x-nextjs-redirect')
221
235
 
222
236
  if (nextRedirect && isDataReq) {
223
- res.headers.set('x-nextjs-redirect', normalizeDataUrl(nextRedirect))
237
+ edgeResponse.headers.set('x-nextjs-redirect', normalizeDataUrl(nextRedirect))
224
238
  }
225
239
 
226
- if (res.headers.get('x-middleware-next') === '1') {
227
- res.headers.delete('x-middleware-next')
228
- return addMiddlewareHeaders(context.next(), res)
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 res
253
+ return edgeResponse
232
254
  }
233
255
 
234
256
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.10.6",
3
+ "version": "5.10.7",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",