@opennextjs/aws 3.2.1 → 3.2.2
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/README.md +15 -1
- package/dist/adapters/cache.d.ts +0 -23
- package/dist/adapters/cache.js +10 -12
- package/dist/adapters/config/index.js +1 -1
- package/dist/adapters/config/util.js +2 -2
- package/dist/adapters/dynamo-provider.js +3 -5
- package/dist/adapters/edge-adapter.d.ts +0 -3
- package/dist/adapters/edge-adapter.js +43 -38
- package/dist/adapters/image-optimization-adapter.js +4 -4
- package/dist/adapters/logger.d.ts +0 -3
- package/dist/adapters/logger.js +3 -5
- package/dist/adapters/middleware.js +42 -18
- package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +0 -3
- package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +0 -3
- package/dist/adapters/revalidate.js +2 -2
- package/dist/adapters/server-adapter.d.ts +0 -3
- package/dist/adapters/server-adapter.js +3 -1
- package/dist/adapters/util.js +2 -2
- package/dist/build/bundleNextServer.d.ts +3 -1
- package/dist/build/bundleNextServer.js +3 -3
- package/dist/build/compileConfig.js +1 -1
- package/dist/build/copyTracedFiles.js +14 -25
- package/dist/build/createAssets.js +35 -26
- package/dist/build/createImageOptimizationBundle.js +1 -1
- package/dist/build/createMiddleware.js +7 -2
- package/dist/build/createRevalidationBundle.js +1 -1
- package/dist/build/createServerBundle.js +23 -29
- package/dist/build/createWarmerBundle.js +1 -1
- package/dist/build/edge/createEdgeBundle.d.ts +7 -3
- package/dist/build/edge/createEdgeBundle.js +12 -5
- package/dist/build/generateOutput.js +7 -13
- package/dist/build/helper.d.ts +22 -4
- package/dist/build/helper.js +26 -12
- package/dist/build/installDeps.js +1 -1
- package/dist/build/patch/patchedAsyncStorage.js +1 -1
- package/dist/build/utils.js +3 -2
- package/dist/build/validateConfig.js +1 -0
- package/dist/core/createGenericHandler.d.ts +2 -4
- package/dist/core/createGenericHandler.js +1 -2
- package/dist/core/createMainHandler.d.ts +0 -17
- package/dist/core/createMainHandler.js +5 -4
- package/dist/core/edgeFunctionHandler.d.ts +1 -51
- package/dist/core/patchAsyncStorage.js +5 -6
- package/dist/core/requestHandler.js +98 -89
- package/dist/core/require-hooks.js +8 -7
- package/dist/core/resolve.d.ts +13 -7
- package/dist/core/resolve.js +29 -35
- package/dist/core/routing/cacheInterceptor.js +12 -13
- package/dist/core/routing/i18n/accept-header.js +3 -2
- package/dist/core/routing/matcher.d.ts +2 -4
- package/dist/core/routing/matcher.js +17 -26
- package/dist/core/routing/middleware.js +17 -16
- package/dist/core/routing/util.d.ts +0 -13
- package/dist/core/routing/util.js +25 -87
- package/dist/core/routingHandler.d.ts +2 -0
- package/dist/core/routingHandler.js +30 -12
- package/dist/core/util.js +5 -3
- package/dist/helpers/withSST.d.ts +1 -1
- package/dist/http/openNextResponse.d.ts +5 -9
- package/dist/http/openNextResponse.js +29 -31
- package/dist/http/request.d.ts +0 -3
- package/dist/http/request.js +4 -2
- package/dist/http/util.d.ts +1 -1
- package/dist/http/util.js +8 -12
- package/dist/minimize-js.js +19 -19
- package/dist/overrides/converters/aws-apigw-v1.d.ts +2 -2
- package/dist/overrides/converters/aws-apigw-v1.js +2 -1
- package/dist/overrides/converters/aws-apigw-v2.d.ts +2 -2
- package/dist/overrides/converters/aws-apigw-v2.js +6 -7
- package/dist/overrides/converters/aws-cloudfront.d.ts +2 -2
- package/dist/overrides/converters/aws-cloudfront.js +11 -30
- package/dist/overrides/converters/dummy.d.ts +1 -1
- package/dist/overrides/converters/edge.d.ts +2 -1
- package/dist/overrides/converters/edge.js +11 -15
- package/dist/overrides/converters/node.d.ts +1 -1
- package/dist/overrides/converters/node.js +1 -1
- package/dist/overrides/converters/sqs-revalidate.d.ts +1 -1
- package/dist/overrides/imageLoader/dummy.d.ts +1 -1
- package/dist/overrides/imageLoader/host.d.ts +1 -1
- package/dist/overrides/imageLoader/s3.d.ts +1 -1
- package/dist/overrides/imageLoader/s3.js +1 -1
- package/dist/overrides/incrementalCache/dummy.d.ts +1 -1
- package/dist/overrides/incrementalCache/s3-lite.d.ts +1 -1
- package/dist/overrides/incrementalCache/s3-lite.js +15 -19
- package/dist/overrides/incrementalCache/s3.d.ts +1 -1
- package/dist/overrides/incrementalCache/s3.js +1 -1
- package/dist/overrides/originResolver/dummy.d.ts +1 -1
- package/dist/overrides/originResolver/pattern-env.d.ts +1 -1
- package/dist/overrides/originResolver/pattern-env.js +10 -11
- package/dist/overrides/proxyExternalRequest/dummy.d.ts +3 -0
- package/dist/overrides/proxyExternalRequest/dummy.js +7 -0
- package/dist/overrides/proxyExternalRequest/fetch.d.ts +3 -0
- package/dist/overrides/proxyExternalRequest/fetch.js +25 -0
- package/dist/overrides/proxyExternalRequest/node.d.ts +3 -0
- package/dist/overrides/proxyExternalRequest/node.js +67 -0
- package/dist/overrides/queue/dummy.d.ts +1 -1
- package/dist/overrides/queue/sqs-lite.d.ts +1 -1
- package/dist/overrides/queue/sqs-lite.js +7 -9
- package/dist/overrides/queue/sqs.d.ts +1 -1
- package/dist/overrides/queue/sqs.js +1 -1
- package/dist/overrides/tagCache/constants.js +2 -2
- package/dist/overrides/tagCache/dummy.d.ts +1 -1
- package/dist/overrides/tagCache/dynamodb-lite.d.ts +1 -1
- package/dist/overrides/tagCache/dynamodb-lite.js +10 -12
- package/dist/overrides/tagCache/dynamodb.d.ts +1 -1
- package/dist/overrides/tagCache/dynamodb.js +2 -2
- package/dist/overrides/warmer/aws-lambda.d.ts +1 -1
- package/dist/overrides/warmer/dummy.d.ts +1 -1
- package/dist/overrides/wrappers/aws-lambda-streaming.d.ts +2 -2
- package/dist/overrides/wrappers/aws-lambda.d.ts +2 -2
- package/dist/overrides/wrappers/cloudflare.d.ts +2 -1
- package/dist/overrides/wrappers/cloudflare.js +20 -2
- package/dist/overrides/wrappers/dummy.d.ts +2 -2
- package/dist/overrides/wrappers/node.d.ts +2 -2
- package/dist/overrides/wrappers/node.js +3 -3
- package/dist/plugins/edge.js +6 -5
- package/dist/plugins/replacement.js +2 -2
- package/dist/plugins/resolve.d.ts +3 -1
- package/dist/plugins/resolve.js +4 -1
- package/dist/types/aws-lambda.d.ts +2 -5
- package/dist/types/cache.d.ts +0 -2
- package/dist/types/global.d.ts +188 -0
- package/dist/types/next-types.d.ts +1 -3
- package/dist/types/open-next.d.ts +15 -41
- package/dist/types/overrides.d.ts +87 -0
- package/dist/utils/promise.d.ts +3 -0
- package/dist/utils/promise.js +44 -0
- package/dist/utils/stream.d.ts +0 -1
- package/package.json +2 -2
- package/dist/overrides/incrementalCache/types.d.ts +0 -34
- package/dist/overrides/queue/types.d.ts +0 -12
- package/dist/overrides/tagCache/types.d.ts +0 -11
- package/dist/overrides/tagCache/types.js +0 -1
- /package/dist/{overrides/incrementalCache/types.js → types/global.js} +0 -0
- /package/dist/{overrides/queue/types.js → types/overrides.js} +0 -0
|
@@ -59,30 +59,22 @@ const computeParamHas = (headers, cookies, query) => (has) => {
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
function convertMatch(match, toDestination, destination) {
|
|
62
|
-
if (match) {
|
|
63
|
-
const { params } = match;
|
|
64
|
-
const isUsingParams = Object.keys(params).length > 0;
|
|
65
|
-
if (isUsingParams) {
|
|
66
|
-
return toDestination(params);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
return destination;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
62
|
+
if (!match) {
|
|
73
63
|
return destination;
|
|
74
64
|
}
|
|
65
|
+
const { params } = match;
|
|
66
|
+
const isUsingParams = Object.keys(params).length > 0;
|
|
67
|
+
return isUsingParams ? toDestination(params) : destination;
|
|
75
68
|
}
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const matcher = routeHasMatcher(headers, cookies, query);
|
|
69
|
+
export function getNextConfigHeaders(event, configHeaders) {
|
|
70
|
+
if (!configHeaders) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
const matcher = routeHasMatcher(event.headers, event.cookies, event.query);
|
|
82
74
|
const requestHeaders = {};
|
|
83
75
|
const localizedRawPath = localizePath(event);
|
|
84
76
|
for (const { headers, has, missing, regex, source, locale, } of configHeaders) {
|
|
85
|
-
const path = locale === false ? rawPath : localizedRawPath;
|
|
77
|
+
const path = locale === false ? event.rawPath : localizedRawPath;
|
|
86
78
|
if (new RegExp(regex).test(path) &&
|
|
87
79
|
checkHas(matcher, has) &&
|
|
88
80
|
checkHas(matcher, missing, true)) {
|
|
@@ -95,7 +87,7 @@ export function addNextConfigHeaders(event, configHeaders) {
|
|
|
95
87
|
requestHeaders[key] = value;
|
|
96
88
|
}
|
|
97
89
|
catch {
|
|
98
|
-
debug(
|
|
90
|
+
debug(`Error matching header ${h.key} with value ${h.value}`);
|
|
99
91
|
requestHeaders[h.key] = h.value;
|
|
100
92
|
}
|
|
101
93
|
});
|
|
@@ -132,16 +124,16 @@ export function handleRewrites(event, rewrites) {
|
|
|
132
124
|
const toDestinationPath = compile(escapeRegex(pathname ?? "") ?? "");
|
|
133
125
|
const toDestinationHost = compile(escapeRegex(hostname ?? "") ?? "");
|
|
134
126
|
const toDestinationQuery = compile(escapeRegex(encodePlusQueryString ?? "") ?? "");
|
|
135
|
-
|
|
127
|
+
const params = {
|
|
136
128
|
// params for the source
|
|
137
129
|
...getParamsFromSource(match(escapeRegex(rewrite?.source) ?? ""))(pathToUse),
|
|
138
130
|
// params for the has
|
|
139
131
|
...rewrite.has?.reduce((acc, cur) => {
|
|
140
|
-
return
|
|
132
|
+
return Object.assign(acc, computeHas(cur));
|
|
141
133
|
}, {}),
|
|
142
134
|
// params for the missing
|
|
143
135
|
...rewrite.missing?.reduce((acc, cur) => {
|
|
144
|
-
return
|
|
136
|
+
return Object.assign(acc, computeHas(cur));
|
|
145
137
|
}, {}),
|
|
146
138
|
};
|
|
147
139
|
const isUsingParams = Object.keys(params).length > 0;
|
|
@@ -200,7 +192,7 @@ function handleTrailingSlashRedirect(event) {
|
|
|
200
192
|
};
|
|
201
193
|
// eslint-disable-next-line sonarjs/elseif-without-else
|
|
202
194
|
}
|
|
203
|
-
|
|
195
|
+
if (!NextConfig.trailingSlash &&
|
|
204
196
|
event.rawPath.endsWith("/") &&
|
|
205
197
|
event.rawPath !== "/") {
|
|
206
198
|
const headersLocation = event.url.split("?");
|
|
@@ -214,8 +206,7 @@ function handleTrailingSlashRedirect(event) {
|
|
|
214
206
|
isBase64Encoded: false,
|
|
215
207
|
};
|
|
216
208
|
}
|
|
217
|
-
|
|
218
|
-
return false;
|
|
209
|
+
return false;
|
|
219
210
|
}
|
|
220
211
|
export function handleRedirects(event, redirects) {
|
|
221
212
|
const trailingSlashRedirect = handleTrailingSlashRedirect(event);
|
|
@@ -273,7 +264,7 @@ export function handleFallbackFalse(internalEvent, prerenderManifest) {
|
|
|
273
264
|
return routeRegexExp.test(rawPath);
|
|
274
265
|
});
|
|
275
266
|
const locales = NextConfig.i18n?.locales;
|
|
276
|
-
const routesAlreadyHaveLocale =
|
|
267
|
+
const routesAlreadyHaveLocale = locales?.includes(rawPath.split("/")[1]) ||
|
|
277
268
|
// If we don't use locales, we don't need to add the default locale
|
|
278
269
|
locales === undefined;
|
|
279
270
|
const localizedPath = routesAlreadyHaveLocale
|
|
@@ -21,35 +21,36 @@ function defaultMiddlewareLoader() {
|
|
|
21
21
|
// @returns undefined | res.end()
|
|
22
22
|
// if res.end() is return, the parent needs to return and not process next server
|
|
23
23
|
export async function handleMiddleware(internalEvent, middlewareLoader = defaultMiddlewareLoader) {
|
|
24
|
-
const
|
|
25
|
-
|
|
24
|
+
const headers = internalEvent.headers;
|
|
25
|
+
// We bypass the middleware if the request is internal
|
|
26
|
+
if (headers["x-isr"])
|
|
27
|
+
return internalEvent;
|
|
26
28
|
// We only need the normalizedPath to check if the middleware should run
|
|
29
|
+
const normalizedPath = localizePath(internalEvent);
|
|
27
30
|
const hasMatch = middleMatch.some((r) => r.test(normalizedPath));
|
|
28
31
|
if (!hasMatch)
|
|
29
32
|
return internalEvent;
|
|
30
|
-
// We bypass the middleware if the request is internal
|
|
31
|
-
if (internalEvent.headers["x-isr"])
|
|
32
|
-
return internalEvent;
|
|
33
33
|
// Retrieve the protocol:
|
|
34
34
|
// - In lambda, the url only contains the rawPath and the query - default to https
|
|
35
35
|
// - In cloudflare, the protocol is usually http in dev and https in production
|
|
36
36
|
const protocol = internalEvent.url.startsWith("http://") ? "http:" : "https:";
|
|
37
|
-
const host =
|
|
38
|
-
? `${protocol}//${
|
|
37
|
+
const host = headers.host
|
|
38
|
+
? `${protocol}//${headers.host}`
|
|
39
39
|
: "http://localhost:3000";
|
|
40
40
|
const initialUrl = new URL(normalizedPath, host);
|
|
41
|
-
initialUrl.search = convertToQueryString(query);
|
|
41
|
+
initialUrl.search = convertToQueryString(internalEvent.query);
|
|
42
42
|
const url = initialUrl.toString();
|
|
43
43
|
const middleware = await middlewareLoader();
|
|
44
44
|
const result = await middleware.default({
|
|
45
|
+
// `geo` is pre Next 15.
|
|
45
46
|
geo: {
|
|
46
|
-
city:
|
|
47
|
-
country:
|
|
48
|
-
region:
|
|
49
|
-
latitude:
|
|
50
|
-
longitude:
|
|
47
|
+
city: headers["x-open-next-city"],
|
|
48
|
+
country: headers["x-open-next-country"],
|
|
49
|
+
region: headers["x-open-next-region"],
|
|
50
|
+
latitude: headers["x-open-next-latitude"],
|
|
51
|
+
longitude: headers["x-open-next-longitude"],
|
|
51
52
|
},
|
|
52
|
-
headers
|
|
53
|
+
headers,
|
|
53
54
|
method: internalEvent.method || "GET",
|
|
54
55
|
nextConfig: {
|
|
55
56
|
basePath: NextConfig.basePath,
|
|
@@ -127,9 +128,9 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
127
128
|
const rewriteUrlObject = new URL(rewriteUrl);
|
|
128
129
|
newUrl = rewriteUrlObject.pathname;
|
|
129
130
|
// Reset the query params if the middleware is a rewrite
|
|
130
|
-
if (middlewareQueryString
|
|
131
|
+
if (middlewareQueryString.__nextDataReq) {
|
|
131
132
|
middlewareQueryString = {
|
|
132
|
-
__nextDataReq: middlewareQueryString
|
|
133
|
+
__nextDataReq: middlewareQueryString.__nextDataReq,
|
|
133
134
|
};
|
|
134
135
|
}
|
|
135
136
|
else {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
/// <reference types="node" />
|
|
4
1
|
import type { OutgoingHttpHeaders } from "node:http";
|
|
5
2
|
import type { IncomingMessage, StreamCreator } from "../../http/index.js";
|
|
6
3
|
import { OpenNextNodeResponse } from "../../http/openNextResponse.js";
|
|
@@ -59,16 +56,6 @@ export declare function unescapeRegex(str: string): string;
|
|
|
59
56
|
* @__PURE__
|
|
60
57
|
*/
|
|
61
58
|
export declare function convertBodyToReadableStream(method: string, body?: string | Buffer): ReadableStream<any> | undefined;
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
* @__PURE__
|
|
65
|
-
*/
|
|
66
|
-
export declare function proxyRequest(internalEvent: InternalEvent, res: OpenNextNodeResponse): Promise<void>;
|
|
67
|
-
declare global {
|
|
68
|
-
var openNextDebug: boolean;
|
|
69
|
-
var openNextVersion: string;
|
|
70
|
-
var lastModified: Record<string, number>;
|
|
71
|
-
}
|
|
72
59
|
/**
|
|
73
60
|
*
|
|
74
61
|
* @__PURE__
|
|
@@ -24,7 +24,8 @@ export function convertFromQueryString(query) {
|
|
|
24
24
|
const queryParts = query.split("&");
|
|
25
25
|
return queryParts.reduce((acc, part) => {
|
|
26
26
|
const [key, value] = part.split("=");
|
|
27
|
-
|
|
27
|
+
acc[key] = value;
|
|
28
|
+
return acc;
|
|
28
29
|
}, {});
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
@@ -128,45 +129,20 @@ export function getMiddlewareMatch(middlewareManifest) {
|
|
|
128
129
|
* @__PURE__
|
|
129
130
|
*/
|
|
130
131
|
export function escapeRegex(str) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
return str
|
|
133
|
+
.replaceAll("(.)", "_µ1_")
|
|
134
|
+
.replaceAll("(..)", "_µ2_")
|
|
135
|
+
.replaceAll("(...)", "_µ3_");
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
137
138
|
*
|
|
138
139
|
* @__PURE__
|
|
139
140
|
*/
|
|
140
141
|
export function unescapeRegex(str) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
*
|
|
148
|
-
* @__PURE__
|
|
149
|
-
*/
|
|
150
|
-
function filterHeadersForProxy(headers) {
|
|
151
|
-
const filteredHeaders = {};
|
|
152
|
-
const disallowedHeaders = [
|
|
153
|
-
"host",
|
|
154
|
-
"connection",
|
|
155
|
-
"via",
|
|
156
|
-
"x-cache",
|
|
157
|
-
"transfer-encoding",
|
|
158
|
-
"content-encoding",
|
|
159
|
-
"content-length",
|
|
160
|
-
];
|
|
161
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
162
|
-
const lowerKey = key.toLowerCase();
|
|
163
|
-
if (disallowedHeaders.includes(lowerKey) || lowerKey.startsWith("x-amz"))
|
|
164
|
-
return;
|
|
165
|
-
else {
|
|
166
|
-
filteredHeaders[key] = value?.toString() ?? "";
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
return filteredHeaders;
|
|
142
|
+
return str
|
|
143
|
+
.replaceAll("_µ1_", "(.)")
|
|
144
|
+
.replaceAll("_µ2_", "(..)")
|
|
145
|
+
.replaceAll("_µ3_", "(...)");
|
|
170
146
|
}
|
|
171
147
|
/**
|
|
172
148
|
* @__PURE__
|
|
@@ -184,50 +160,6 @@ export function convertBodyToReadableStream(method, body) {
|
|
|
184
160
|
});
|
|
185
161
|
return readable;
|
|
186
162
|
}
|
|
187
|
-
/**
|
|
188
|
-
*
|
|
189
|
-
* @__PURE__
|
|
190
|
-
*/
|
|
191
|
-
export async function proxyRequest(internalEvent, res) {
|
|
192
|
-
const { url, headers, method, body } = internalEvent;
|
|
193
|
-
const request = await import("node:https").then((m) => m.request);
|
|
194
|
-
debug("proxyRequest", url);
|
|
195
|
-
await new Promise((resolve, reject) => {
|
|
196
|
-
const filteredHeaders = filterHeadersForProxy(headers);
|
|
197
|
-
debug("filteredHeaders", filteredHeaders);
|
|
198
|
-
const req = request(url, {
|
|
199
|
-
headers: filteredHeaders,
|
|
200
|
-
method,
|
|
201
|
-
rejectUnauthorized: false,
|
|
202
|
-
}, (_res) => {
|
|
203
|
-
res.writeHead(_res.statusCode ?? 200, filterHeadersForProxy(_res.headers));
|
|
204
|
-
if (_res.headers["content-encoding"] === "br") {
|
|
205
|
-
_res.pipe(require("node:zlib").createBrotliDecompress()).pipe(res);
|
|
206
|
-
}
|
|
207
|
-
else if (_res.headers["content-encoding"] === "gzip") {
|
|
208
|
-
_res.pipe(require("node:zlib").createGunzip()).pipe(res);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
_res.pipe(res);
|
|
212
|
-
}
|
|
213
|
-
_res.on("error", (e) => {
|
|
214
|
-
error("proxyRequest error", e);
|
|
215
|
-
res.end();
|
|
216
|
-
reject(e);
|
|
217
|
-
});
|
|
218
|
-
res.on("finish", () => {
|
|
219
|
-
resolve();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
if (body && method !== "GET" && method !== "HEAD") {
|
|
223
|
-
req.write(body);
|
|
224
|
-
}
|
|
225
|
-
req.end();
|
|
226
|
-
});
|
|
227
|
-
// console.log("result", result);
|
|
228
|
-
// res.writeHead(result.status, resHeaders);
|
|
229
|
-
// res.end(await result.text());
|
|
230
|
-
}
|
|
231
163
|
var CommonHeaders;
|
|
232
164
|
(function (CommonHeaders) {
|
|
233
165
|
CommonHeaders["CACHE_CONTROL"] = "cache-control";
|
|
@@ -247,7 +179,8 @@ export function fixCacheHeaderForHtmlPages(rawPath, headers) {
|
|
|
247
179
|
"private, no-cache, no-store, max-age=0, must-revalidate";
|
|
248
180
|
return;
|
|
249
181
|
}
|
|
250
|
-
// WORKAROUND: `NextServer` does not set cache headers for HTML pages
|
|
182
|
+
// WORKAROUND: `NextServer` does not set cache headers for HTML pages
|
|
183
|
+
// https://opennext.js.org/aws/v2/advanced/workaround#workaround-nextserver-does-not-set-cache-headers-for-html-pages
|
|
251
184
|
if (HtmlPages.includes(rawPath)) {
|
|
252
185
|
headers[CommonHeaders.CACHE_CONTROL] =
|
|
253
186
|
"public, max-age=0, s-maxage=31536000, must-revalidate";
|
|
@@ -279,7 +212,8 @@ export function addOpenNextHeader(headers) {
|
|
|
279
212
|
}
|
|
280
213
|
if (globalThis.openNextDebug) {
|
|
281
214
|
headers["X-OpenNext-Version"] = globalThis.openNextVersion;
|
|
282
|
-
headers["X-OpenNext-RequestId"] =
|
|
215
|
+
headers["X-OpenNext-RequestId"] =
|
|
216
|
+
globalThis.__openNextAls.getStore()?.requestId;
|
|
283
217
|
}
|
|
284
218
|
}
|
|
285
219
|
/**
|
|
@@ -310,13 +244,13 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
|
|
|
310
244
|
// If data has the same etag during these 5 min dedup window, it will be deduplicated and not revalidated.
|
|
311
245
|
try {
|
|
312
246
|
const hash = (str) => crypto.createHash("md5").update(str).digest("hex");
|
|
313
|
-
const requestId = globalThis.
|
|
247
|
+
const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
|
|
314
248
|
const lastModified = globalThis.lastModified[requestId] > 0
|
|
315
249
|
? globalThis.lastModified[requestId]
|
|
316
250
|
: "";
|
|
317
251
|
// For some weird cases, lastModified is not set, haven't been able to figure out yet why
|
|
318
252
|
// For those cases we add the etag to the deduplication id, it might help
|
|
319
|
-
const etag = headers
|
|
253
|
+
const etag = headers.etag ?? headers.ETag ?? "";
|
|
320
254
|
await globalThis.queue.send({
|
|
321
255
|
MessageBody: { host, url: revalidateUrl },
|
|
322
256
|
MessageDeduplicationId: hash(`${rawPath}-${lastModified}-${etag}`),
|
|
@@ -338,19 +272,22 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
|
|
|
338
272
|
export function generateMessageGroupId(rawPath) {
|
|
339
273
|
let a = cyrb128(rawPath);
|
|
340
274
|
// We use mulberry32 to generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
|
|
341
|
-
|
|
275
|
+
let t = (a += 0x6d2b79f5);
|
|
342
276
|
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
343
277
|
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
344
278
|
const randomFloat = ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
345
279
|
// This will generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
|
|
346
280
|
// This means that we could have 1000 revalidate request at the same time
|
|
347
|
-
const maxConcurrency = parseInt(process.env.MAX_REVALIDATE_CONCURRENCY ?? "10");
|
|
281
|
+
const maxConcurrency = Number.parseInt(process.env.MAX_REVALIDATE_CONCURRENCY ?? "10");
|
|
348
282
|
const randomInt = Math.floor(randomFloat * maxConcurrency);
|
|
349
283
|
return `revalidate-${randomInt}`;
|
|
350
284
|
}
|
|
351
285
|
// Used to generate a hash int from a string
|
|
352
286
|
function cyrb128(str) {
|
|
353
|
-
let h1 = 1779033703
|
|
287
|
+
let h1 = 1779033703;
|
|
288
|
+
let h2 = 3144134277;
|
|
289
|
+
let h3 = 1013904242;
|
|
290
|
+
let h4 = 2773480762;
|
|
354
291
|
for (let i = 0, k; i < str.length; i++) {
|
|
355
292
|
k = str.charCodeAt(i);
|
|
356
293
|
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
@@ -362,6 +299,7 @@ function cyrb128(str) {
|
|
|
362
299
|
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
363
300
|
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
364
301
|
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
302
|
+
// biome-ignore lint/style/noCommaOperator:
|
|
365
303
|
(h1 ^= h2 ^ h3 ^ h4), (h2 ^= h1), (h3 ^= h1), (h4 ^= h1);
|
|
366
304
|
return h1 >>> 0;
|
|
367
305
|
}
|
|
@@ -375,7 +313,7 @@ export function fixISRHeaders(headers) {
|
|
|
375
313
|
"private, no-cache, no-store, max-age=0, must-revalidate";
|
|
376
314
|
return;
|
|
377
315
|
}
|
|
378
|
-
const requestId = globalThis.
|
|
316
|
+
const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
|
|
379
317
|
const _lastModified = globalThis.lastModified[requestId] ?? 0;
|
|
380
318
|
if (headers[CommonHeaders.NEXT_CACHE] === "HIT" && _lastModified > 0) {
|
|
381
319
|
// calculate age
|
|
@@ -387,7 +325,7 @@ export function fixISRHeaders(headers) {
|
|
|
387
325
|
if (typeof cacheControl !== "string")
|
|
388
326
|
return;
|
|
389
327
|
const match = cacheControl.match(regex);
|
|
390
|
-
const sMaxAge = match ? parseInt(match[1]) : undefined;
|
|
328
|
+
const sMaxAge = match ? Number.parseInt(match[1]) : undefined;
|
|
391
329
|
// 31536000 is the default s-maxage value for SSG pages
|
|
392
330
|
if (sMaxAge && sMaxAge !== 31536000) {
|
|
393
331
|
const remainingTtl = Math.max(sMaxAge - age, 1);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { InternalEvent, InternalResult, Origin } from "../types/open-next";
|
|
2
|
+
export declare const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
|
|
3
|
+
export declare const MIDDLEWARE_HEADER_PREFIX_LEN: number;
|
|
2
4
|
export interface MiddlewareOutputEvent {
|
|
3
5
|
internalEvent: InternalEvent;
|
|
4
6
|
isExternalRewrite: boolean;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { BuildId, ConfigHeaders, PrerenderManifest, RoutesManifest, } from "../adapters/config/index";
|
|
2
2
|
import { debug } from "../adapters/logger";
|
|
3
3
|
import { cacheInterceptor } from "./routing/cacheInterceptor";
|
|
4
|
-
import {
|
|
4
|
+
import { fixDataPage, getNextConfigHeaders, handleFallbackFalse, handleRedirects, handleRewrites, } from "./routing/matcher";
|
|
5
5
|
import { handleMiddleware } from "./routing/middleware";
|
|
6
|
+
export const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
|
|
7
|
+
export const MIDDLEWARE_HEADER_PREFIX_LEN = MIDDLEWARE_HEADER_PREFIX.length;
|
|
6
8
|
// Add the locale prefix to the regex so we correctly match the rawPath
|
|
7
|
-
const optionalLocalePrefixRegex =
|
|
8
|
-
? `^/(?:${RoutesManifest.locales.map((locale) => locale
|
|
9
|
+
const optionalLocalePrefixRegex = RoutesManifest.locales.length
|
|
10
|
+
? `^/(?:${RoutesManifest.locales.map((locale) => `${locale}/?`).join("|")})?`
|
|
9
11
|
: "^/";
|
|
10
12
|
// Add the basepath prefix to the regex so we correctly match the rawPath
|
|
11
|
-
const optionalBasepathPrefixRegex =
|
|
13
|
+
const optionalBasepathPrefixRegex = RoutesManifest.basePath
|
|
12
14
|
? `^${RoutesManifest.basePath}/?`
|
|
13
15
|
: "^/";
|
|
14
16
|
// Add the basePath prefix to the api routes
|
|
15
|
-
const apiPrefix =
|
|
17
|
+
const apiPrefix = RoutesManifest.basePath
|
|
16
18
|
? `${RoutesManifest.basePath}/api`
|
|
17
19
|
: "/api";
|
|
18
20
|
const staticRegexp = RoutesManifest.routes.static.map((route) => new RegExp(route.regex
|
|
@@ -21,16 +23,34 @@ const staticRegexp = RoutesManifest.routes.static.map((route) => new RegExp(rout
|
|
|
21
23
|
const dynamicRegexp = RoutesManifest.routes.dynamic.map((route) => new RegExp(route.regex
|
|
22
24
|
.replace("^/", optionalLocalePrefixRegex)
|
|
23
25
|
.replace("^/", optionalBasepathPrefixRegex)));
|
|
26
|
+
// Geolocation headers starting from Nextjs 15
|
|
27
|
+
// See https://github.com/vercel/vercel/blob/7714b1c/packages/functions/src/headers.ts
|
|
28
|
+
const geoHeaderToNextHeader = {
|
|
29
|
+
"x-open-next-city": "x-vercel-ip-city",
|
|
30
|
+
"x-open-next-country": "x-vercel-ip-country",
|
|
31
|
+
"x-open-next-region": "x-vercel-ip-country-region",
|
|
32
|
+
"x-open-next-latitude": "x-vercel-ip-latitude",
|
|
33
|
+
"x-open-next-longitude": "x-vercel-ip-longitude",
|
|
34
|
+
};
|
|
24
35
|
function applyMiddlewareHeaders(eventHeaders, middlewareHeaders, setPrefix = true) {
|
|
36
|
+
const keyPrefix = setPrefix ? MIDDLEWARE_HEADER_PREFIX : "";
|
|
25
37
|
Object.entries(middlewareHeaders).forEach(([key, value]) => {
|
|
26
38
|
if (value) {
|
|
27
|
-
eventHeaders[
|
|
28
|
-
|
|
39
|
+
eventHeaders[keyPrefix + key] = Array.isArray(value)
|
|
40
|
+
? value.join(",")
|
|
41
|
+
: value;
|
|
29
42
|
}
|
|
30
43
|
});
|
|
31
44
|
}
|
|
32
45
|
export default async function routingHandler(event) {
|
|
33
|
-
|
|
46
|
+
// Add Next geo headers
|
|
47
|
+
for (const [openNextGeoName, nextGeoName] of Object.entries(geoHeaderToNextHeader)) {
|
|
48
|
+
const value = event.headers[openNextGeoName];
|
|
49
|
+
if (value) {
|
|
50
|
+
event.headers[nextGeoName] = value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const nextHeaders = getNextConfigHeaders(event, ConfigHeaders);
|
|
34
54
|
let internalEvent = fixDataPage(event, BuildId);
|
|
35
55
|
if ("statusCode" in internalEvent) {
|
|
36
56
|
return internalEvent;
|
|
@@ -45,10 +65,8 @@ export default async function routingHandler(event) {
|
|
|
45
65
|
if ("statusCode" in middleware) {
|
|
46
66
|
return middleware;
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
internalEvent = middleware;
|
|
51
|
-
}
|
|
68
|
+
middlewareResponseHeaders = middleware.responseHeaders || {};
|
|
69
|
+
internalEvent = middleware;
|
|
52
70
|
// At this point internalEvent is an InternalEvent or a MiddlewareOutputEvent
|
|
53
71
|
let isExternalRewrite = middleware.externalRewrite ?? false;
|
|
54
72
|
if (!isExternalRewrite) {
|
package/dist/core/util.js
CHANGED
|
@@ -5,7 +5,8 @@ import { AppPathsManifestKeys, NextConfig, RoutesManifest, } from "../adapters/c
|
|
|
5
5
|
import NextServer from "next/dist/server/next-server.js";
|
|
6
6
|
import { debug } from "../adapters/logger.js";
|
|
7
7
|
import { applyOverride as applyNextjsRequireHooksOverride, overrideHooks as overrideNextjsRequireHooks, } from "./require-hooks.js";
|
|
8
|
-
// WORKAROUND: Set `__NEXT_PRIVATE_PREBUNDLED_REACT` to use prebundled
|
|
8
|
+
// WORKAROUND: Set `__NEXT_PRIVATE_PREBUNDLED_REACT` to use prebundled Reac
|
|
9
|
+
// See https://opennext.js.org/aws/v2/advanced/workaround#workaround-set-__next_private_prebundled_react-to-use-prebundled-react
|
|
9
10
|
// Step 1: Need to override the require hooks for React before Next.js server
|
|
10
11
|
// overrides them with prebundled ones in the case of app dir
|
|
11
12
|
// Step 2: Import Next.js server
|
|
@@ -31,7 +32,7 @@ export const requestHandler = new NextServer.default({
|
|
|
31
32
|
// our own cache handler to store the cache on S3.
|
|
32
33
|
//#override stableIncrementalCache
|
|
33
34
|
cacheHandler: cacheHandlerPath,
|
|
34
|
-
cacheMaxMemorySize: 0,
|
|
35
|
+
cacheMaxMemorySize: 0, // We need to disable memory cache
|
|
35
36
|
//#endOverride
|
|
36
37
|
experimental: {
|
|
37
38
|
...NextConfig.experimental,
|
|
@@ -62,7 +63,8 @@ export function loadMiddlewareManifest(nextDir) {
|
|
|
62
63
|
}
|
|
63
64
|
//#override setNextjsPrebundledReact
|
|
64
65
|
export function setNextjsPrebundledReact(rawPath) {
|
|
65
|
-
// WORKAROUND: Set `__NEXT_PRIVATE_PREBUNDLED_REACT` to use prebundled React
|
|
66
|
+
// WORKAROUND: Set `__NEXT_PRIVATE_PREBUNDLED_REACT` to use prebundled React
|
|
67
|
+
// See https://opennext.js.org/aws/v2/advanced/workaround#workaround-set-__next_private_prebundled_react-to-use-prebundled-react
|
|
66
68
|
const routes = [
|
|
67
69
|
...RoutesManifest.routes.static,
|
|
68
70
|
...RoutesManifest.routes.dynamic,
|
|
@@ -44,7 +44,7 @@ export declare function withSST<Fn extends Record<string, SSTCompatibleSplittedF
|
|
|
44
44
|
functions?: Fn | undefined;
|
|
45
45
|
middleware?: {
|
|
46
46
|
external: true;
|
|
47
|
-
}
|
|
47
|
+
};
|
|
48
48
|
dangerous?: import("../types/open-next").DangerousOptions | undefined;
|
|
49
49
|
buildCommand?: string | undefined;
|
|
50
50
|
buildOutputPath?: string | undefined;
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/// <reference types="node" />
|
|
6
|
-
import type { IncomingMessage, OutgoingHttpHeader, OutgoingHttpHeaders, ServerResponse } from "http";
|
|
7
|
-
import type { Socket } from "net";
|
|
8
|
-
import type { TransformCallback, Writable } from "stream";
|
|
9
|
-
import { Transform } from "stream";
|
|
1
|
+
import type { IncomingMessage, OutgoingHttpHeader, OutgoingHttpHeaders, ServerResponse } from "node:http";
|
|
2
|
+
import type { Socket } from "node:net";
|
|
3
|
+
import type { TransformCallback, Writable } from "node:stream";
|
|
4
|
+
import { Transform } from "node:stream";
|
|
10
5
|
export interface StreamCreator {
|
|
11
6
|
writeHeaders(prelude: {
|
|
12
7
|
statusCode: number;
|
|
@@ -18,6 +13,7 @@ export interface StreamCreator {
|
|
|
18
13
|
}
|
|
19
14
|
export declare class OpenNextNodeResponse extends Transform implements ServerResponse {
|
|
20
15
|
private fixHeaders;
|
|
16
|
+
private onEnd;
|
|
21
17
|
private streamCreator?;
|
|
22
18
|
private initialHeaders?;
|
|
23
19
|
statusCode: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Transform } from "stream";
|
|
1
|
+
import { Transform } from "node:stream";
|
|
2
2
|
import { debug } from "../adapters/logger";
|
|
3
3
|
import { parseCookies, parseHeaders } from "./util";
|
|
4
4
|
const SET_COOKIE_HEADER = "set-cookie";
|
|
@@ -6,6 +6,7 @@ const CANNOT_BE_USED = "This cannot be used in OpenNext";
|
|
|
6
6
|
// We only need to implement the methods that are used by next.js
|
|
7
7
|
export class OpenNextNodeResponse extends Transform {
|
|
8
8
|
fixHeaders;
|
|
9
|
+
onEnd;
|
|
9
10
|
streamCreator;
|
|
10
11
|
initialHeaders;
|
|
11
12
|
statusCode;
|
|
@@ -53,20 +54,9 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
53
54
|
constructor(fixHeaders, onEnd, streamCreator, initialHeaders) {
|
|
54
55
|
super();
|
|
55
56
|
this.fixHeaders = fixHeaders;
|
|
57
|
+
this.onEnd = onEnd;
|
|
56
58
|
this.streamCreator = streamCreator;
|
|
57
59
|
this.initialHeaders = initialHeaders;
|
|
58
|
-
this.once("finish", () => {
|
|
59
|
-
if (!this.headersSent) {
|
|
60
|
-
this.flushHeaders();
|
|
61
|
-
}
|
|
62
|
-
// In some cases we might not have a store i.e. for example in the image optimization function
|
|
63
|
-
// We may want to reconsider this in the future, it might be intersting to have access to this store everywhere
|
|
64
|
-
globalThis.__als
|
|
65
|
-
?.getStore()
|
|
66
|
-
?.pendingPromiseRunner.add(onEnd(this.headers));
|
|
67
|
-
const bodyLength = this.getBody().length;
|
|
68
|
-
this.streamCreator?.onFinish(bodyLength);
|
|
69
|
-
});
|
|
70
60
|
}
|
|
71
61
|
// Necessary for next 12
|
|
72
62
|
// We might have to implement all the methods here
|
|
@@ -122,7 +112,8 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
122
112
|
this.headersSent = true;
|
|
123
113
|
// Initial headers should be merged with the new headers
|
|
124
114
|
// These initial headers are the one created either in the middleware or in next.config.js
|
|
125
|
-
const mergeHeadersPriority = globalThis.
|
|
115
|
+
const mergeHeadersPriority = globalThis.__openNextAls?.getStore()?.mergeHeadersPriority ??
|
|
116
|
+
"middleware";
|
|
126
117
|
if (this.initialHeaders) {
|
|
127
118
|
this.headers =
|
|
128
119
|
mergeHeadersPriority === "middleware"
|
|
@@ -134,7 +125,7 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
134
125
|
...this.initialHeaders,
|
|
135
126
|
...this.headers,
|
|
136
127
|
};
|
|
137
|
-
const initialCookies = parseCookies(this.initialHeaders[SET_COOKIE_HEADER]
|
|
128
|
+
const initialCookies = parseCookies(this.initialHeaders[SET_COOKIE_HEADER]);
|
|
138
129
|
this._cookies =
|
|
139
130
|
mergeHeadersPriority === "middleware"
|
|
140
131
|
? [...this._cookies, ...initialCookies]
|
|
@@ -162,14 +153,12 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
162
153
|
if (!this.hasHeader(key)) {
|
|
163
154
|
return this.setHeader(key, value);
|
|
164
155
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return this.setHeader(key, newValue);
|
|
172
|
-
}
|
|
156
|
+
const existingHeader = this.getHeader(key);
|
|
157
|
+
const toAppend = Array.isArray(value) ? value : [value];
|
|
158
|
+
const newValue = Array.isArray(existingHeader)
|
|
159
|
+
? [...existingHeader, ...toAppend]
|
|
160
|
+
: [existingHeader, ...toAppend];
|
|
161
|
+
return this.setHeader(key, newValue);
|
|
173
162
|
}
|
|
174
163
|
writeHead(statusCode, statusMessage, headers) {
|
|
175
164
|
let _headers = headers;
|
|
@@ -227,15 +216,24 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
227
216
|
this._internalWrite(chunk, encoding);
|
|
228
217
|
callback();
|
|
229
218
|
}
|
|
230
|
-
//This is only here because of aws broken streaming implementation.
|
|
231
|
-
//Hopefully one day they will be able to give us a working streaming implementation in lambda for everyone
|
|
232
|
-
//If you're lucky you have a working streaming implementation in your aws account and don't need this
|
|
233
|
-
//If not you can set the OPEN_NEXT_FORCE_NON_EMPTY_RESPONSE env variable to true
|
|
234
|
-
//BE CAREFUL: Aws keeps rolling out broken streaming implementations even on accounts that had working ones before
|
|
235
|
-
//This is not dependent on the node runtime used
|
|
236
|
-
//There is another known issue with aws lambda streaming where the request reach the lambda only way after the request has been sent by the client. For this there is absolutely nothing we can do, contact aws support if that's your case
|
|
237
219
|
_flush(callback) {
|
|
238
|
-
if (this.
|
|
220
|
+
if (!this.headersSent) {
|
|
221
|
+
this.flushHeaders();
|
|
222
|
+
}
|
|
223
|
+
// In some cases we might not have a store i.e. for example in the image optimization function
|
|
224
|
+
// We may want to reconsider this in the future, it might be intersting to have access to this store everywhere
|
|
225
|
+
globalThis.__openNextAls
|
|
226
|
+
?.getStore()
|
|
227
|
+
?.pendingPromiseRunner.add(this.onEnd(this.headers));
|
|
228
|
+
const bodyLength = this.getBody().length;
|
|
229
|
+
this.streamCreator?.onFinish(bodyLength);
|
|
230
|
+
//This is only here because of aws broken streaming implementation.
|
|
231
|
+
//Hopefully one day they will be able to give us a working streaming implementation in lambda for everyone
|
|
232
|
+
//If you're lucky you have a working streaming implementation in your aws account and don't need this
|
|
233
|
+
//If not you can set the OPEN_NEXT_FORCE_NON_EMPTY_RESPONSE env variable to true
|
|
234
|
+
//BE CAREFUL: Aws keeps rolling out broken streaming implementations even on accounts that had working ones before
|
|
235
|
+
//This is not dependent on the node runtime used
|
|
236
|
+
if (bodyLength === 0 &&
|
|
239
237
|
// We use an env variable here because not all aws account have the same behavior
|
|
240
238
|
// On some aws accounts the response will hang if the body is empty
|
|
241
239
|
// We are modifying the response body here, this is not a good practice
|
package/dist/http/request.d.ts
CHANGED