@opennextjs/aws 3.2.0 → 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.
Files changed (140) hide show
  1. package/README.md +15 -1
  2. package/dist/adapters/cache.d.ts +1 -23
  3. package/dist/adapters/cache.js +22 -18
  4. package/dist/adapters/config/index.js +1 -1
  5. package/dist/adapters/config/util.js +2 -2
  6. package/dist/adapters/dynamo-provider.js +3 -5
  7. package/dist/adapters/edge-adapter.d.ts +0 -3
  8. package/dist/adapters/edge-adapter.js +43 -38
  9. package/dist/adapters/image-optimization-adapter.js +4 -4
  10. package/dist/adapters/logger.d.ts +0 -3
  11. package/dist/adapters/logger.js +3 -5
  12. package/dist/adapters/middleware.js +42 -18
  13. package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +0 -3
  14. package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +0 -3
  15. package/dist/adapters/revalidate.js +2 -2
  16. package/dist/adapters/server-adapter.d.ts +0 -3
  17. package/dist/adapters/server-adapter.js +3 -1
  18. package/dist/adapters/util.js +2 -2
  19. package/dist/build/bundleNextServer.d.ts +3 -1
  20. package/dist/build/bundleNextServer.js +3 -3
  21. package/dist/build/compileConfig.js +3 -3
  22. package/dist/build/copyTracedFiles.js +16 -27
  23. package/dist/build/createAssets.js +35 -26
  24. package/dist/build/createImageOptimizationBundle.js +4 -1
  25. package/dist/build/createMiddleware.d.ts +4 -4
  26. package/dist/build/createMiddleware.js +16 -19
  27. package/dist/build/createRevalidationBundle.js +1 -1
  28. package/dist/build/createServerBundle.js +34 -45
  29. package/dist/build/createWarmerBundle.js +1 -1
  30. package/dist/build/edge/createEdgeBundle.d.ts +9 -7
  31. package/dist/build/edge/createEdgeBundle.js +19 -14
  32. package/dist/build/generateOutput.js +7 -13
  33. package/dist/build/helper.d.ts +22 -4
  34. package/dist/build/helper.js +26 -12
  35. package/dist/build/installDeps.js +12 -2
  36. package/dist/build/patch/patchedAsyncStorage.js +1 -1
  37. package/dist/build/utils.js +3 -2
  38. package/dist/build/validateConfig.js +1 -0
  39. package/dist/core/createGenericHandler.d.ts +2 -4
  40. package/dist/core/createGenericHandler.js +6 -7
  41. package/dist/core/createMainHandler.d.ts +0 -17
  42. package/dist/core/createMainHandler.js +10 -9
  43. package/dist/core/edgeFunctionHandler.d.ts +1 -51
  44. package/dist/core/patchAsyncStorage.js +5 -6
  45. package/dist/core/requestHandler.js +98 -89
  46. package/dist/core/require-hooks.js +8 -7
  47. package/dist/core/resolve.d.ts +13 -7
  48. package/dist/core/resolve.js +29 -35
  49. package/dist/core/routing/cacheInterceptor.js +12 -13
  50. package/dist/core/routing/i18n/accept-header.js +3 -2
  51. package/dist/core/routing/matcher.d.ts +2 -4
  52. package/dist/core/routing/matcher.js +29 -31
  53. package/dist/core/routing/middleware.js +22 -18
  54. package/dist/core/routing/util.d.ts +0 -13
  55. package/dist/core/routing/util.js +32 -91
  56. package/dist/core/routingHandler.d.ts +2 -0
  57. package/dist/core/routingHandler.js +30 -12
  58. package/dist/core/util.js +5 -3
  59. package/dist/helpers/withSST.d.ts +1 -1
  60. package/dist/http/openNextResponse.d.ts +5 -9
  61. package/dist/http/openNextResponse.js +29 -31
  62. package/dist/http/request.d.ts +0 -3
  63. package/dist/http/request.js +4 -2
  64. package/dist/http/util.d.ts +1 -1
  65. package/dist/http/util.js +8 -12
  66. package/dist/minimize-js.js +19 -19
  67. package/dist/overrides/converters/aws-apigw-v1.d.ts +2 -2
  68. package/dist/overrides/converters/aws-apigw-v1.js +2 -1
  69. package/dist/overrides/converters/aws-apigw-v2.d.ts +2 -2
  70. package/dist/overrides/converters/aws-apigw-v2.js +6 -7
  71. package/dist/overrides/converters/aws-cloudfront.d.ts +2 -2
  72. package/dist/overrides/converters/aws-cloudfront.js +11 -30
  73. package/dist/overrides/converters/dummy.d.ts +1 -1
  74. package/dist/overrides/converters/edge.d.ts +5 -1
  75. package/dist/overrides/converters/edge.js +16 -17
  76. package/dist/overrides/converters/node.d.ts +1 -1
  77. package/dist/overrides/converters/node.js +1 -1
  78. package/dist/overrides/converters/sqs-revalidate.d.ts +1 -1
  79. package/dist/overrides/imageLoader/dummy.d.ts +1 -1
  80. package/dist/overrides/imageLoader/host.d.ts +1 -1
  81. package/dist/overrides/imageLoader/s3.d.ts +1 -1
  82. package/dist/overrides/imageLoader/s3.js +1 -1
  83. package/dist/overrides/incrementalCache/dummy.d.ts +1 -1
  84. package/dist/overrides/incrementalCache/s3-lite.d.ts +1 -1
  85. package/dist/overrides/incrementalCache/s3-lite.js +15 -19
  86. package/dist/overrides/incrementalCache/s3.d.ts +1 -1
  87. package/dist/overrides/incrementalCache/s3.js +1 -1
  88. package/dist/overrides/originResolver/dummy.d.ts +1 -1
  89. package/dist/overrides/originResolver/pattern-env.d.ts +1 -1
  90. package/dist/overrides/originResolver/pattern-env.js +10 -11
  91. package/dist/overrides/proxyExternalRequest/dummy.d.ts +3 -0
  92. package/dist/overrides/proxyExternalRequest/dummy.js +7 -0
  93. package/dist/overrides/proxyExternalRequest/fetch.d.ts +3 -0
  94. package/dist/overrides/proxyExternalRequest/fetch.js +25 -0
  95. package/dist/overrides/proxyExternalRequest/node.d.ts +3 -0
  96. package/dist/overrides/proxyExternalRequest/node.js +67 -0
  97. package/dist/overrides/queue/dummy.d.ts +1 -1
  98. package/dist/overrides/queue/sqs-lite.d.ts +1 -1
  99. package/dist/overrides/queue/sqs-lite.js +7 -9
  100. package/dist/overrides/queue/sqs.d.ts +1 -1
  101. package/dist/overrides/queue/sqs.js +1 -1
  102. package/dist/overrides/tagCache/constants.js +2 -2
  103. package/dist/overrides/tagCache/dummy.d.ts +1 -1
  104. package/dist/overrides/tagCache/dynamodb-lite.d.ts +1 -1
  105. package/dist/overrides/tagCache/dynamodb-lite.js +10 -12
  106. package/dist/overrides/tagCache/dynamodb.d.ts +1 -1
  107. package/dist/overrides/tagCache/dynamodb.js +2 -2
  108. package/dist/overrides/warmer/aws-lambda.d.ts +1 -1
  109. package/dist/overrides/warmer/dummy.d.ts +1 -1
  110. package/dist/overrides/wrappers/aws-lambda-streaming.d.ts +2 -2
  111. package/dist/overrides/wrappers/aws-lambda.d.ts +2 -2
  112. package/dist/overrides/wrappers/cloudflare.d.ts +2 -1
  113. package/dist/overrides/wrappers/cloudflare.js +21 -3
  114. package/dist/overrides/wrappers/dummy.d.ts +2 -2
  115. package/dist/overrides/wrappers/node.d.ts +2 -2
  116. package/dist/overrides/wrappers/node.js +3 -3
  117. package/dist/plugins/edge.d.ts +3 -2
  118. package/dist/plugins/edge.js +33 -34
  119. package/dist/plugins/replacement.d.ts +1 -1
  120. package/dist/plugins/replacement.js +5 -6
  121. package/dist/plugins/resolve.d.ts +3 -1
  122. package/dist/plugins/resolve.js +4 -1
  123. package/dist/types/aws-lambda.d.ts +2 -5
  124. package/dist/types/cache.d.ts +0 -2
  125. package/dist/types/global.d.ts +188 -0
  126. package/dist/types/next-types.d.ts +1 -3
  127. package/dist/types/open-next.d.ts +24 -45
  128. package/dist/types/overrides.d.ts +87 -0
  129. package/dist/utils/promise.d.ts +3 -0
  130. package/dist/utils/promise.js +44 -0
  131. package/dist/utils/stream.d.ts +0 -1
  132. package/package.json +2 -2
  133. package/dist/adapters/plugins/without-routing/requestHandler.d.ts +0 -2
  134. package/dist/adapters/plugins/without-routing/requestHandler.js +0 -10
  135. package/dist/overrides/incrementalCache/types.d.ts +0 -34
  136. package/dist/overrides/queue/types.d.ts +0 -12
  137. package/dist/overrides/tagCache/types.d.ts +0 -11
  138. package/dist/overrides/tagCache/types.js +0 -1
  139. /package/dist/{overrides/incrementalCache/types.js → types/global.js} +0 -0
  140. /package/dist/{overrides/queue/types.js → types/overrides.js} +0 -0
@@ -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
- return { ...acc, [key]: value };
27
+ acc[key] = value;
28
+ return acc;
28
29
  }, {});
29
30
  }
30
31
  /**
@@ -37,7 +38,7 @@ export function getUrlParts(url, isExternal) {
37
38
  const match = url.match(regex);
38
39
  return {
39
40
  hostname: "",
40
- pathname: match?.[1] ?? url,
41
+ pathname: match?.[1] ? `/${match[1]}` : url,
41
42
  protocol: "",
42
43
  queryString: match?.[2] ?? "",
43
44
  };
@@ -50,7 +51,7 @@ export function getUrlParts(url, isExternal) {
50
51
  return {
51
52
  protocol: match[1] ?? "https:",
52
53
  hostname: match[2],
53
- pathname: match[3],
54
+ pathname: match[3] ?? "",
54
55
  queryString: match[4]?.slice(1) ?? "",
55
56
  };
56
57
  }
@@ -84,13 +85,16 @@ export function convertRes(res) {
84
85
  * @__PURE__
85
86
  */
86
87
  export function convertToQueryString(query) {
88
+ // URLSearchParams is a representation of the PARSED query.
89
+ // So we must decode the value before appending it to the URLSearchParams.
90
+ // https://stackoverflow.com/a/45516812
87
91
  const urlQuery = new URLSearchParams();
88
92
  Object.entries(query).forEach(([key, value]) => {
89
93
  if (Array.isArray(value)) {
90
- value.forEach((entry) => urlQuery.append(key, entry));
94
+ value.forEach((entry) => urlQuery.append(key, decodeURIComponent(entry)));
91
95
  }
92
96
  else {
93
- urlQuery.append(key, value);
97
+ urlQuery.append(key, decodeURIComponent(value));
94
98
  }
95
99
  });
96
100
  const queryString = urlQuery.toString();
@@ -125,45 +129,20 @@ export function getMiddlewareMatch(middlewareManifest) {
125
129
  * @__PURE__
126
130
  */
127
131
  export function escapeRegex(str) {
128
- let path = str.replace(/\(\.\)/g, "_µ1_");
129
- path = path.replace(/\(\.{2}\)/g, "_µ2_");
130
- path = path.replace(/\(\.{3}\)/g, "_µ3_");
131
- return path;
132
+ return str
133
+ .replaceAll("(.)", "_µ1_")
134
+ .replaceAll("(..)", "_µ2_")
135
+ .replaceAll("(...)", "_µ3_");
132
136
  }
133
137
  /**
134
138
  *
135
139
  * @__PURE__
136
140
  */
137
141
  export function unescapeRegex(str) {
138
- let path = str.replace(/_µ1_/g, "(.)");
139
- path = path.replace(/_µ2_/g, "(..)");
140
- path = path.replace(/_µ3_/g, "(...)");
141
- return path;
142
- }
143
- /**
144
- *
145
- * @__PURE__
146
- */
147
- function filterHeadersForProxy(headers) {
148
- const filteredHeaders = {};
149
- const disallowedHeaders = [
150
- "host",
151
- "connection",
152
- "via",
153
- "x-cache",
154
- "transfer-encoding",
155
- "content-encoding",
156
- "content-length",
157
- ];
158
- Object.entries(headers).forEach(([key, value]) => {
159
- const lowerKey = key.toLowerCase();
160
- if (disallowedHeaders.includes(lowerKey) || lowerKey.startsWith("x-amz"))
161
- return;
162
- else {
163
- filteredHeaders[key] = value?.toString() ?? "";
164
- }
165
- });
166
- return filteredHeaders;
142
+ return str
143
+ .replaceAll("_µ1_", "(.)")
144
+ .replaceAll("_µ2_", "(..)")
145
+ .replaceAll("_µ3_", "(...)");
167
146
  }
168
147
  /**
169
148
  * @__PURE__
@@ -181,50 +160,6 @@ export function convertBodyToReadableStream(method, body) {
181
160
  });
182
161
  return readable;
183
162
  }
184
- /**
185
- *
186
- * @__PURE__
187
- */
188
- export async function proxyRequest(internalEvent, res) {
189
- const { url, headers, method, body } = internalEvent;
190
- const request = await import("node:https").then((m) => m.request);
191
- debug("proxyRequest", url);
192
- await new Promise((resolve, reject) => {
193
- const filteredHeaders = filterHeadersForProxy(headers);
194
- debug("filteredHeaders", filteredHeaders);
195
- const req = request(url, {
196
- headers: filteredHeaders,
197
- method,
198
- rejectUnauthorized: false,
199
- }, (_res) => {
200
- res.writeHead(_res.statusCode ?? 200, filterHeadersForProxy(_res.headers));
201
- if (_res.headers["content-encoding"] === "br") {
202
- _res.pipe(require("node:zlib").createBrotliDecompress()).pipe(res);
203
- }
204
- else if (_res.headers["content-encoding"] === "gzip") {
205
- _res.pipe(require("node:zlib").createGunzip()).pipe(res);
206
- }
207
- else {
208
- _res.pipe(res);
209
- }
210
- _res.on("error", (e) => {
211
- error("proxyRequest error", e);
212
- res.end();
213
- reject(e);
214
- });
215
- res.on("finish", () => {
216
- resolve();
217
- });
218
- });
219
- if (body && method !== "GET" && method !== "HEAD") {
220
- req.write(body);
221
- }
222
- req.end();
223
- });
224
- // console.log("result", result);
225
- // res.writeHead(result.status, resHeaders);
226
- // res.end(await result.text());
227
- }
228
163
  var CommonHeaders;
229
164
  (function (CommonHeaders) {
230
165
  CommonHeaders["CACHE_CONTROL"] = "cache-control";
@@ -244,7 +179,8 @@ export function fixCacheHeaderForHtmlPages(rawPath, headers) {
244
179
  "private, no-cache, no-store, max-age=0, must-revalidate";
245
180
  return;
246
181
  }
247
- // WORKAROUND: `NextServer` does not set cache headers for HTML pages — https://github.com/serverless-stack/open-next#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
248
184
  if (HtmlPages.includes(rawPath)) {
249
185
  headers[CommonHeaders.CACHE_CONTROL] =
250
186
  "public, max-age=0, s-maxage=31536000, must-revalidate";
@@ -276,7 +212,8 @@ export function addOpenNextHeader(headers) {
276
212
  }
277
213
  if (globalThis.openNextDebug) {
278
214
  headers["X-OpenNext-Version"] = globalThis.openNextVersion;
279
- headers["X-OpenNext-RequestId"] = globalThis.__als.getStore()?.requestId;
215
+ headers["X-OpenNext-RequestId"] =
216
+ globalThis.__openNextAls.getStore()?.requestId;
280
217
  }
281
218
  }
282
219
  /**
@@ -307,13 +244,13 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
307
244
  // If data has the same etag during these 5 min dedup window, it will be deduplicated and not revalidated.
308
245
  try {
309
246
  const hash = (str) => crypto.createHash("md5").update(str).digest("hex");
310
- const requestId = globalThis.__als.getStore()?.requestId ?? "";
247
+ const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
311
248
  const lastModified = globalThis.lastModified[requestId] > 0
312
249
  ? globalThis.lastModified[requestId]
313
250
  : "";
314
251
  // For some weird cases, lastModified is not set, haven't been able to figure out yet why
315
252
  // For those cases we add the etag to the deduplication id, it might help
316
- const etag = headers["etag"] ?? headers["ETag"] ?? "";
253
+ const etag = headers.etag ?? headers.ETag ?? "";
317
254
  await globalThis.queue.send({
318
255
  MessageBody: { host, url: revalidateUrl },
319
256
  MessageDeduplicationId: hash(`${rawPath}-${lastModified}-${etag}`),
@@ -335,19 +272,22 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
335
272
  export function generateMessageGroupId(rawPath) {
336
273
  let a = cyrb128(rawPath);
337
274
  // We use mulberry32 to generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
338
- var t = (a += 0x6d2b79f5);
275
+ let t = (a += 0x6d2b79f5);
339
276
  t = Math.imul(t ^ (t >>> 15), t | 1);
340
277
  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
341
278
  const randomFloat = ((t ^ (t >>> 14)) >>> 0) / 4294967296;
342
279
  // This will generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
343
280
  // This means that we could have 1000 revalidate request at the same time
344
- const maxConcurrency = parseInt(process.env.MAX_REVALIDATE_CONCURRENCY ?? "10");
281
+ const maxConcurrency = Number.parseInt(process.env.MAX_REVALIDATE_CONCURRENCY ?? "10");
345
282
  const randomInt = Math.floor(randomFloat * maxConcurrency);
346
283
  return `revalidate-${randomInt}`;
347
284
  }
348
285
  // Used to generate a hash int from a string
349
286
  function cyrb128(str) {
350
- let h1 = 1779033703, h2 = 3144134277, h3 = 1013904242, h4 = 2773480762;
287
+ let h1 = 1779033703;
288
+ let h2 = 3144134277;
289
+ let h3 = 1013904242;
290
+ let h4 = 2773480762;
351
291
  for (let i = 0, k; i < str.length; i++) {
352
292
  k = str.charCodeAt(i);
353
293
  h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
@@ -359,6 +299,7 @@ function cyrb128(str) {
359
299
  h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
360
300
  h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
361
301
  h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
302
+ // biome-ignore lint/style/noCommaOperator:
362
303
  (h1 ^= h2 ^ h3 ^ h4), (h2 ^= h1), (h3 ^= h1), (h4 ^= h1);
363
304
  return h1 >>> 0;
364
305
  }
@@ -372,7 +313,7 @@ export function fixISRHeaders(headers) {
372
313
  "private, no-cache, no-store, max-age=0, must-revalidate";
373
314
  return;
374
315
  }
375
- const requestId = globalThis.__als.getStore()?.requestId ?? "";
316
+ const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
376
317
  const _lastModified = globalThis.lastModified[requestId] ?? 0;
377
318
  if (headers[CommonHeaders.NEXT_CACHE] === "HIT" && _lastModified > 0) {
378
319
  // calculate age
@@ -384,7 +325,7 @@ export function fixISRHeaders(headers) {
384
325
  if (typeof cacheControl !== "string")
385
326
  return;
386
327
  const match = cacheControl.match(regex);
387
- const sMaxAge = match ? parseInt(match[1]) : undefined;
328
+ const sMaxAge = match ? Number.parseInt(match[1]) : undefined;
388
329
  // 31536000 is the default s-maxage value for SSG pages
389
330
  if (sMaxAge && sMaxAge !== 31536000) {
390
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 { addNextConfigHeaders, fixDataPage, handleFallbackFalse, handleRedirects, handleRewrites, } from "./routing/matcher";
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 = !!RoutesManifest.locales.length
8
- ? `^/(?:${RoutesManifest.locales.map((locale) => locale + "/?").join("|")})?`
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 = !!RoutesManifest.basePath
13
+ const optionalBasepathPrefixRegex = RoutesManifest.basePath
12
14
  ? `^${RoutesManifest.basePath}/?`
13
15
  : "^/";
14
16
  // Add the basePath prefix to the api routes
15
- const apiPrefix = !!RoutesManifest.basePath
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[`${setPrefix ? "x-middleware-response-" : ""}${key}`] =
28
- Array.isArray(value) ? value.join(",") : value;
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
- const nextHeaders = addNextConfigHeaders(event, ConfigHeaders) ?? {};
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
- else {
49
- middlewareResponseHeaders = middleware.responseHeaders || {};
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 React — https://github.com/serverless-stack/open-next#workaround-set-__next_private_prebundled_react-to-use-prebundled-react
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 — https://github.com/serverless-stack/open-next#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
- } | undefined;
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
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
- /// <reference types="node" />
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.__als?.getStore()?.mergeHeadersPriority ?? "middleware";
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
- else {
166
- const existingHeader = this.getHeader(key);
167
- const toAppend = Array.isArray(value) ? value : [value];
168
- const newValue = Array.isArray(existingHeader)
169
- ? [...existingHeader, ...toAppend]
170
- : [existingHeader, ...toAppend];
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.getBody().length < 1 &&
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
@@ -1,6 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import http from "node:http";
5
2
  export declare class IncomingMessage extends http.IncomingMessage {
6
3
  constructor({ method, url, headers, body, remoteAddress, }: {
@@ -13,8 +13,10 @@ export class IncomingMessage extends http.IncomingMessage {
13
13
  end: Function.prototype,
14
14
  destroy: Function.prototype,
15
15
  });
16
- if (typeof headers["content-length"] === "undefined") {
17
- headers["content-length"] = Buffer.byteLength(body).toString();
16
+ // Set the content length when there is a body.
17
+ // See https://httpwg.org/specs/rfc9110.html#field.content-length
18
+ if (body) {
19
+ headers["content-length"] ??= String(Buffer.byteLength(body));
18
20
  }
19
21
  Object.assign(this, {
20
22
  ip: remoteAddress,
@@ -1,4 +1,4 @@
1
1
  import type http from "node:http";
2
2
  export declare const parseHeaders: (headers?: http.OutgoingHttpHeader[] | http.OutgoingHttpHeaders) => Record<string, string>;
3
3
  export declare const convertHeader: (header: http.OutgoingHttpHeader) => string;
4
- export declare function parseCookies(cookies?: string | string[]): string[] | undefined;
4
+ export declare function parseCookies(cookies: string | string[] | null | undefined): string[];
package/dist/http/util.js CHANGED
@@ -7,9 +7,7 @@ export const parseHeaders = (headers) => {
7
7
  if (value === undefined) {
8
8
  continue;
9
9
  }
10
- else {
11
- result[key.toLowerCase()] = convertHeader(value);
12
- }
10
+ result[key.toLowerCase()] = convertHeader(value);
13
11
  }
14
12
  return result;
15
13
  };
@@ -17,18 +15,16 @@ export const convertHeader = (header) => {
17
15
  if (typeof header === "string") {
18
16
  return header;
19
17
  }
20
- else if (Array.isArray(header)) {
18
+ if (Array.isArray(header)) {
21
19
  return header.join(",");
22
20
  }
23
- else {
24
- return String(header);
25
- }
21
+ return String(header);
26
22
  };
27
23
  export function parseCookies(cookies) {
28
- if (!cookies)
29
- return;
30
- if (typeof cookies === "string") {
31
- return cookies.split(/(?<!Expires=\w+),/i).map((c) => c.trim());
24
+ if (!cookies) {
25
+ return [];
32
26
  }
33
- return cookies;
27
+ return typeof cookies === "string"
28
+ ? cookies.split(/(?<!Expires=\w+),/i).map((c) => c.trim())
29
+ : cookies;
34
30
  }
@@ -6,9 +6,9 @@ import fs from "node:fs/promises";
6
6
  import path from "node:path";
7
7
  import minify from "@node-minify/core";
8
8
  import terser from "@node-minify/terser";
9
- var failed_files = [];
10
- var total_files = 0;
11
- var options = {};
9
+ const failed_files = [];
10
+ let total_files = 0;
11
+ const options = {};
12
12
  const promiseSeries = async (tasks, initial) => {
13
13
  if (!Array.isArray(tasks)) {
14
14
  return Promise.reject(new TypeError("promise.series only accepts an array of functions"));
@@ -40,12 +40,12 @@ const minifyJSON = async (file) => {
40
40
  try {
41
41
  if (options.compress_json || options.packagejson) {
42
42
  total_files++;
43
- var is_package_json = file.indexOf("package.json") > -1;
44
- var data = await fs.readFile(file, "utf8");
45
- var json = JSON.parse(data);
46
- var new_json = {};
43
+ const is_package_json = file.indexOf("package.json") > -1;
44
+ const data = await fs.readFile(file, "utf8");
45
+ const json = JSON.parse(data);
46
+ let new_json = {};
47
47
  if (options.packagejson && is_package_json) {
48
- var { name, version, bin, main, binary, engines } = json;
48
+ const { name, version, bin, main, binary, engines } = json;
49
49
  new_json = { name, version };
50
50
  if (bin)
51
51
  new_json.bin = bin;
@@ -66,14 +66,14 @@ const minifyJSON = async (file) => {
66
66
  //process.stdout.write(".");
67
67
  };
68
68
  const walk = async (currentDirPath) => {
69
- var js_files = [];
70
- var json_files = [];
71
- var dirs = [];
72
- var current_dirs = await fs.readdir(currentDirPath);
69
+ const js_files = [];
70
+ const json_files = [];
71
+ const dirs = [];
72
+ const current_dirs = await fs.readdir(currentDirPath);
73
73
  for (const name of current_dirs) {
74
- var filePath = path.join(currentDirPath, name);
75
- var stat = await fs.stat(filePath);
76
- var is_bin = /\.bin$/;
74
+ const filePath = path.join(currentDirPath, name);
75
+ const stat = await fs.stat(filePath);
76
+ const is_bin = /\.bin$/;
77
77
  if (stat.isFile()) {
78
78
  if (filePath.substr(-5) === ".json")
79
79
  json_files.push(filePath);
@@ -84,8 +84,8 @@ const walk = async (currentDirPath) => {
84
84
  dirs.push(filePath);
85
85
  }
86
86
  }
87
- var js_promise = Promise.all(js_files.map((f) => minifyJS(f)));
88
- var json_promise = Promise.all(json_files.map((f) => minifyJSON(f)));
87
+ const js_promise = Promise.all(js_files.map((f) => minifyJS(f)));
88
+ const json_promise = Promise.all(json_files.map((f) => minifyJSON(f)));
89
89
  await Promise.all([js_promise, json_promise]);
90
90
  await promiseSeries(dirs.map((dir) => () => walk(dir)));
91
91
  };
@@ -96,7 +96,7 @@ export async function minifyAll(dir, opts) {
96
96
  //process.stdout.write(".\n");
97
97
  //console.log("Total found files: " + total_files);
98
98
  if (failed_files.length) {
99
- console.log(`\n\nFailed to minify files:`);
100
- failed_files.forEach((f) => console.log("\t" + f));
99
+ console.log("\n\nFailed to minify files:");
100
+ failed_files.forEach((f) => console.log(`\t${f}`));
101
101
  }
102
102
  }
@@ -1,3 +1,3 @@
1
- import type { Converter, InternalEvent, InternalResult } from "../../types/open-next";
2
- declare const _default: Converter<InternalEvent, InternalResult>;
1
+ import type { Converter } from "../../types/overrides";
2
+ declare const _default: Converter;
3
3
  export default _default;
@@ -52,7 +52,8 @@ async function convertFromAPIGatewayProxyEvent(event) {
52
52
  query: removeUndefinedFromQuery(event.multiValueQueryStringParameters ?? {}),
53
53
  cookies: event.multiValueHeaders?.cookie?.reduce((acc, cur) => {
54
54
  const [key, value] = cur.split("=");
55
- return { ...acc, [key]: value };
55
+ acc[key] = value;
56
+ return acc;
56
57
  }, {}) ?? {},
57
58
  };
58
59
  }