@netlify/plugin-nextjs 5.10.6 → 5.11.0
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/build/advanced-api-routes.js +1 -1
- package/dist/build/cache.js +1 -1
- package/dist/build/content/prerendered.js +5 -9
- package/dist/build/content/server.js +22 -15
- package/dist/build/content/static.js +4 -8
- package/dist/build/functions/edge.js +2 -2
- package/dist/build/functions/server.js +4 -8
- package/dist/build/image-cdn.js +1 -1
- package/dist/build/plugin-context.js +2 -2
- package/dist/build/templates/handler-monorepo.tmpl.js +0 -5
- package/dist/build/templates/handler.tmpl.js +0 -4
- package/dist/build/verification.js +3 -3
- package/dist/esm-chunks/{chunk-OEQOKJGE.js → chunk-6BT4RYQJ.js} +1 -12
- package/dist/esm-chunks/chunk-FKDTZJRV.js +832 -0
- package/dist/esm-chunks/{chunk-APO262HE.js → chunk-PFLHY2KD.js} +1 -1
- package/dist/esm-chunks/{chunk-NFOLXH6F.js → chunk-YUXQHOYO.js} +1 -1
- package/dist/index.js +3 -7
- package/dist/run/config.js +3 -3
- package/dist/run/constants.js +3 -3
- package/dist/run/handlers/cache.cjs +24 -234
- package/dist/run/handlers/request-context.cjs +63 -88
- package/dist/run/handlers/server.js +9 -6
- package/dist/run/handlers/tags-handler.cjs +192 -0
- package/dist/run/handlers/tracer.cjs +4 -4
- package/dist/run/handlers/use-cache-handler.js +1553 -0
- package/dist/run/headers.js +1 -1
- package/dist/run/revalidate.js +1 -1
- package/dist/shared/blobkey.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
- package/dist/esm-chunks/chunk-5QSXBV7L.js +0 -89
- package/dist/esm-chunks/chunk-GNGHTHMQ.js +0 -1624
- package/dist/esm-chunks/package-GOVVBALR.js +0 -148
- package/dist/run/handlers/tracing.js +0 -68968
package/dist/run/headers.js
CHANGED
package/dist/run/revalidate.js
CHANGED
package/dist/shared/blobkey.js
CHANGED
|
@@ -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
|
/**
|
package/package.json
CHANGED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
var require = await (async () => {
|
|
3
|
-
var { createRequire } = await import("node:module");
|
|
4
|
-
return createRequire(import.meta.url);
|
|
5
|
-
})();
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
SpanStatusCode,
|
|
9
|
-
context,
|
|
10
|
-
init_esm
|
|
11
|
-
} from "./chunk-GNGHTHMQ.js";
|
|
12
|
-
|
|
13
|
-
// node_modules/@opentelemetry/api/build/esm/experimental/trace/SugaredTracer.js
|
|
14
|
-
init_esm();
|
|
15
|
-
var defaultOnException = function(e, span) {
|
|
16
|
-
span.recordException(e);
|
|
17
|
-
span.setStatus({
|
|
18
|
-
code: SpanStatusCode.ERROR
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
function wrapTracer(tracer) {
|
|
22
|
-
return new SugaredTracer(tracer);
|
|
23
|
-
}
|
|
24
|
-
var SugaredTracer = (
|
|
25
|
-
/** @class */
|
|
26
|
-
function() {
|
|
27
|
-
function SugaredTracer2(tracer) {
|
|
28
|
-
this._tracer = tracer;
|
|
29
|
-
this.startSpan = tracer.startSpan.bind(this._tracer);
|
|
30
|
-
this.startActiveSpan = tracer.startActiveSpan.bind(this._tracer);
|
|
31
|
-
}
|
|
32
|
-
SugaredTracer2.prototype.withActiveSpan = function(name, arg2, arg3, arg4) {
|
|
33
|
-
var _a = massageParams(arg2, arg3, arg4), opts = _a.opts, ctx = _a.ctx, fn = _a.fn;
|
|
34
|
-
return this._tracer.startActiveSpan(name, opts, ctx, function(span) {
|
|
35
|
-
return handleFn(span, opts, fn);
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
SugaredTracer2.prototype.withSpan = function(name, arg2, arg3, arg4) {
|
|
39
|
-
var _a = massageParams(arg2, arg3, arg4), opts = _a.opts, ctx = _a.ctx, fn = _a.fn;
|
|
40
|
-
var span = this._tracer.startSpan(name, opts, ctx);
|
|
41
|
-
return handleFn(span, opts, fn);
|
|
42
|
-
};
|
|
43
|
-
return SugaredTracer2;
|
|
44
|
-
}()
|
|
45
|
-
);
|
|
46
|
-
function massageParams(arg, arg2, arg3) {
|
|
47
|
-
var opts;
|
|
48
|
-
var ctx;
|
|
49
|
-
var fn;
|
|
50
|
-
if (!arg2 && !arg3) {
|
|
51
|
-
fn = arg;
|
|
52
|
-
} else if (!arg3) {
|
|
53
|
-
opts = arg;
|
|
54
|
-
fn = arg2;
|
|
55
|
-
} else {
|
|
56
|
-
opts = arg;
|
|
57
|
-
ctx = arg2;
|
|
58
|
-
fn = arg3;
|
|
59
|
-
}
|
|
60
|
-
opts = opts !== null && opts !== void 0 ? opts : {};
|
|
61
|
-
ctx = ctx !== null && ctx !== void 0 ? ctx : context.active();
|
|
62
|
-
return { opts, ctx, fn };
|
|
63
|
-
}
|
|
64
|
-
function handleFn(span, opts, fn) {
|
|
65
|
-
var _a;
|
|
66
|
-
var onException = (_a = opts.onException) !== null && _a !== void 0 ? _a : defaultOnException;
|
|
67
|
-
var errorHandler = function(e) {
|
|
68
|
-
onException(e, span);
|
|
69
|
-
span.end();
|
|
70
|
-
throw e;
|
|
71
|
-
};
|
|
72
|
-
try {
|
|
73
|
-
var ret = fn(span);
|
|
74
|
-
if (typeof (ret === null || ret === void 0 ? void 0 : ret.then) === "function") {
|
|
75
|
-
return ret.then(function(val) {
|
|
76
|
-
span.end();
|
|
77
|
-
return val;
|
|
78
|
-
}, errorHandler);
|
|
79
|
-
}
|
|
80
|
-
span.end();
|
|
81
|
-
return ret;
|
|
82
|
-
} catch (e) {
|
|
83
|
-
throw errorHandler(e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export {
|
|
88
|
-
wrapTracer
|
|
89
|
-
};
|