@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.
Files changed (135) hide show
  1. package/README.md +15 -1
  2. package/dist/adapters/cache.d.ts +0 -23
  3. package/dist/adapters/cache.js +10 -12
  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 +1 -1
  22. package/dist/build/copyTracedFiles.js +14 -25
  23. package/dist/build/createAssets.js +35 -26
  24. package/dist/build/createImageOptimizationBundle.js +1 -1
  25. package/dist/build/createMiddleware.js +7 -2
  26. package/dist/build/createRevalidationBundle.js +1 -1
  27. package/dist/build/createServerBundle.js +23 -29
  28. package/dist/build/createWarmerBundle.js +1 -1
  29. package/dist/build/edge/createEdgeBundle.d.ts +7 -3
  30. package/dist/build/edge/createEdgeBundle.js +12 -5
  31. package/dist/build/generateOutput.js +7 -13
  32. package/dist/build/helper.d.ts +22 -4
  33. package/dist/build/helper.js +26 -12
  34. package/dist/build/installDeps.js +1 -1
  35. package/dist/build/patch/patchedAsyncStorage.js +1 -1
  36. package/dist/build/utils.js +3 -2
  37. package/dist/build/validateConfig.js +1 -0
  38. package/dist/core/createGenericHandler.d.ts +2 -4
  39. package/dist/core/createGenericHandler.js +1 -2
  40. package/dist/core/createMainHandler.d.ts +0 -17
  41. package/dist/core/createMainHandler.js +5 -4
  42. package/dist/core/edgeFunctionHandler.d.ts +1 -51
  43. package/dist/core/patchAsyncStorage.js +5 -6
  44. package/dist/core/requestHandler.js +98 -89
  45. package/dist/core/require-hooks.js +8 -7
  46. package/dist/core/resolve.d.ts +13 -7
  47. package/dist/core/resolve.js +29 -35
  48. package/dist/core/routing/cacheInterceptor.js +12 -13
  49. package/dist/core/routing/i18n/accept-header.js +3 -2
  50. package/dist/core/routing/matcher.d.ts +2 -4
  51. package/dist/core/routing/matcher.js +17 -26
  52. package/dist/core/routing/middleware.js +17 -16
  53. package/dist/core/routing/util.d.ts +0 -13
  54. package/dist/core/routing/util.js +25 -87
  55. package/dist/core/routingHandler.d.ts +2 -0
  56. package/dist/core/routingHandler.js +30 -12
  57. package/dist/core/util.js +5 -3
  58. package/dist/helpers/withSST.d.ts +1 -1
  59. package/dist/http/openNextResponse.d.ts +5 -9
  60. package/dist/http/openNextResponse.js +29 -31
  61. package/dist/http/request.d.ts +0 -3
  62. package/dist/http/request.js +4 -2
  63. package/dist/http/util.d.ts +1 -1
  64. package/dist/http/util.js +8 -12
  65. package/dist/minimize-js.js +19 -19
  66. package/dist/overrides/converters/aws-apigw-v1.d.ts +2 -2
  67. package/dist/overrides/converters/aws-apigw-v1.js +2 -1
  68. package/dist/overrides/converters/aws-apigw-v2.d.ts +2 -2
  69. package/dist/overrides/converters/aws-apigw-v2.js +6 -7
  70. package/dist/overrides/converters/aws-cloudfront.d.ts +2 -2
  71. package/dist/overrides/converters/aws-cloudfront.js +11 -30
  72. package/dist/overrides/converters/dummy.d.ts +1 -1
  73. package/dist/overrides/converters/edge.d.ts +2 -1
  74. package/dist/overrides/converters/edge.js +11 -15
  75. package/dist/overrides/converters/node.d.ts +1 -1
  76. package/dist/overrides/converters/node.js +1 -1
  77. package/dist/overrides/converters/sqs-revalidate.d.ts +1 -1
  78. package/dist/overrides/imageLoader/dummy.d.ts +1 -1
  79. package/dist/overrides/imageLoader/host.d.ts +1 -1
  80. package/dist/overrides/imageLoader/s3.d.ts +1 -1
  81. package/dist/overrides/imageLoader/s3.js +1 -1
  82. package/dist/overrides/incrementalCache/dummy.d.ts +1 -1
  83. package/dist/overrides/incrementalCache/s3-lite.d.ts +1 -1
  84. package/dist/overrides/incrementalCache/s3-lite.js +15 -19
  85. package/dist/overrides/incrementalCache/s3.d.ts +1 -1
  86. package/dist/overrides/incrementalCache/s3.js +1 -1
  87. package/dist/overrides/originResolver/dummy.d.ts +1 -1
  88. package/dist/overrides/originResolver/pattern-env.d.ts +1 -1
  89. package/dist/overrides/originResolver/pattern-env.js +10 -11
  90. package/dist/overrides/proxyExternalRequest/dummy.d.ts +3 -0
  91. package/dist/overrides/proxyExternalRequest/dummy.js +7 -0
  92. package/dist/overrides/proxyExternalRequest/fetch.d.ts +3 -0
  93. package/dist/overrides/proxyExternalRequest/fetch.js +25 -0
  94. package/dist/overrides/proxyExternalRequest/node.d.ts +3 -0
  95. package/dist/overrides/proxyExternalRequest/node.js +67 -0
  96. package/dist/overrides/queue/dummy.d.ts +1 -1
  97. package/dist/overrides/queue/sqs-lite.d.ts +1 -1
  98. package/dist/overrides/queue/sqs-lite.js +7 -9
  99. package/dist/overrides/queue/sqs.d.ts +1 -1
  100. package/dist/overrides/queue/sqs.js +1 -1
  101. package/dist/overrides/tagCache/constants.js +2 -2
  102. package/dist/overrides/tagCache/dummy.d.ts +1 -1
  103. package/dist/overrides/tagCache/dynamodb-lite.d.ts +1 -1
  104. package/dist/overrides/tagCache/dynamodb-lite.js +10 -12
  105. package/dist/overrides/tagCache/dynamodb.d.ts +1 -1
  106. package/dist/overrides/tagCache/dynamodb.js +2 -2
  107. package/dist/overrides/warmer/aws-lambda.d.ts +1 -1
  108. package/dist/overrides/warmer/dummy.d.ts +1 -1
  109. package/dist/overrides/wrappers/aws-lambda-streaming.d.ts +2 -2
  110. package/dist/overrides/wrappers/aws-lambda.d.ts +2 -2
  111. package/dist/overrides/wrappers/cloudflare.d.ts +2 -1
  112. package/dist/overrides/wrappers/cloudflare.js +20 -2
  113. package/dist/overrides/wrappers/dummy.d.ts +2 -2
  114. package/dist/overrides/wrappers/node.d.ts +2 -2
  115. package/dist/overrides/wrappers/node.js +3 -3
  116. package/dist/plugins/edge.js +6 -5
  117. package/dist/plugins/replacement.js +2 -2
  118. package/dist/plugins/resolve.d.ts +3 -1
  119. package/dist/plugins/resolve.js +4 -1
  120. package/dist/types/aws-lambda.d.ts +2 -5
  121. package/dist/types/cache.d.ts +0 -2
  122. package/dist/types/global.d.ts +188 -0
  123. package/dist/types/next-types.d.ts +1 -3
  124. package/dist/types/open-next.d.ts +15 -41
  125. package/dist/types/overrides.d.ts +87 -0
  126. package/dist/utils/promise.d.ts +3 -0
  127. package/dist/utils/promise.js +44 -0
  128. package/dist/utils/stream.d.ts +0 -1
  129. package/package.json +2 -2
  130. package/dist/overrides/incrementalCache/types.d.ts +0 -34
  131. package/dist/overrides/queue/types.d.ts +0 -12
  132. package/dist/overrides/tagCache/types.d.ts +0 -11
  133. package/dist/overrides/tagCache/types.js +0 -1
  134. /package/dist/{overrides/incrementalCache/types.js → types/global.js} +0 -0
  135. /package/dist/{overrides/queue/types.js → types/overrides.js} +0 -0
package/README.md CHANGED
@@ -36,7 +36,7 @@ OpenNext aims to support all Next.js 14 features. Some features are work in prog
36
36
  - [x] Image optimization
37
37
  - [x] [NextAuth.js](https://next-auth.js.org)
38
38
  - [x] Running at edge
39
- - [x] [Almost no coldstart (*)](#coldstart)
39
+ - [x] [Almost no coldstart (\*)](#coldstart)
40
40
 
41
41
  ## Who is using OpenNext?
42
42
 
@@ -56,6 +56,20 @@ This will output A LOT of additional logs to the console. This also disable mini
56
56
 
57
57
  You can read more about the configuration in the [docs](https://opennext.js.org/aws/config)
58
58
 
59
+ ## Preleases
60
+
61
+ Besides the standard npm releases we also automatically publish prerelease packages on branch pushes (using [`pkg.pr.new`](https://github.com/stackblitz-labs/pkg.pr.new)):
62
+
63
+ - `https://pkg.pr.new/@opennextjs/aws@main`:
64
+ Updated with every push to the `main` branch, this prerelease contains the most up to date yet (reasonably) stable version of the package.
65
+ - `https://pkg.pr.new/@opennextjs/aws@experimental`
66
+ Updated with every push to the `experimental` branch, this prerelease contains the latest experimental version of the package (containing features that we want to test/experiment on before committing to).
67
+
68
+ Which you can simply install directly with your package manager of choice, for example:
69
+
70
+ ```bash
71
+ npm i https://pkg.pr.new/@opennextjs/aws@main
72
+ ```
59
73
 
60
74
  ## Contribute
61
75
 
@@ -1,7 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import type { IncrementalCache } from "../overrides/incrementalCache/types.js";
4
- import type { TagCache } from "../overrides/tagCache/types.js";
5
1
  interface CachedFetchValue {
6
2
  kind: "FETCH";
7
3
  data: {
@@ -56,16 +52,6 @@ type IncrementalCacheContext = {
56
52
  fetchIdx?: number | undefined;
57
53
  tags?: string[] | undefined;
58
54
  };
59
- interface CacheHandlerContext {
60
- fs?: never;
61
- dev?: boolean;
62
- flushToDisk?: boolean;
63
- serverDistDir?: string;
64
- maxMemoryCacheSize?: number;
65
- _appDir: boolean;
66
- _requestHeaders: never;
67
- fetchCacheKeyPrefix?: string;
68
- }
69
55
  interface CacheHandlerValue {
70
56
  lastModified?: number;
71
57
  age?: number;
@@ -73,16 +59,7 @@ interface CacheHandlerValue {
73
59
  value: IncrementalCacheValue | null;
74
60
  }
75
61
  export declare function hasCacheExtension(key: string): boolean;
76
- declare global {
77
- var incrementalCache: IncrementalCache;
78
- var tagCache: TagCache;
79
- var disableDynamoDBCache: boolean;
80
- var disableIncrementalCache: boolean;
81
- var lastModified: Record<string, number>;
82
- var isNextAfter15: boolean;
83
- }
84
62
  export default class S3Cache {
85
- constructor(_ctx: CacheHandlerContext);
86
63
  get(key: string, options?: boolean | {
87
64
  fetchCache?: boolean;
88
65
  kindHint?: "app" | "pages" | "fetch";
@@ -1,5 +1,5 @@
1
- import { isBinaryContentType } from "./binary.js";
2
- import { debug, error, warn } from "./logger.js";
1
+ import { isBinaryContentType } from "./binary";
2
+ import { debug, error, warn } from "./logger";
3
3
  /** Beginning single backslash is intentional, to look for the dot + the extension. Do not escape it again. */
4
4
  const CACHE_EXTENSION_REGEX = /\.(cache|fetch)$/;
5
5
  export function hasCacheExtension(key) {
@@ -18,7 +18,6 @@ function isFetchCache(options) {
18
18
  }
19
19
  // We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time
20
20
  export default class S3Cache {
21
- constructor(_ctx) { }
22
21
  async get(key,
23
22
  // fetchCache is for next 13.5 and above, kindHint is for next 14 and above and boolean is for earlier versions
24
23
  options) {
@@ -82,7 +81,7 @@ export default class S3Cache {
82
81
  // If some tags are stale we need to force revalidation
83
82
  return null;
84
83
  }
85
- const requestId = globalThis.__als.getStore()?.requestId ?? "";
84
+ const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
86
85
  globalThis.lastModified[requestId] = _lastModified;
87
86
  if (cacheData?.type === "route") {
88
87
  return {
@@ -97,7 +96,7 @@ export default class S3Cache {
97
96
  },
98
97
  };
99
98
  }
100
- else if (cacheData?.type === "page" || cacheData?.type === "app") {
99
+ if (cacheData?.type === "page" || cacheData?.type === "app") {
101
100
  if (globalThis.isNextAfter15 && cacheData?.type === "app") {
102
101
  return {
103
102
  lastModified: _lastModified,
@@ -122,7 +121,7 @@ export default class S3Cache {
122
121
  },
123
122
  };
124
123
  }
125
- else if (cacheData?.type === "redirect") {
124
+ if (cacheData?.type === "redirect") {
126
125
  return {
127
126
  lastModified: _lastModified,
128
127
  value: {
@@ -131,10 +130,8 @@ export default class S3Cache {
131
130
  },
132
131
  };
133
132
  }
134
- else {
135
- warn("Unknown cache type", cacheData);
136
- return null;
137
- }
133
+ warn("Unknown cache type", cacheData);
134
+ return null;
138
135
  }
139
136
  catch (e) {
140
137
  // We can usually ignore errors here as they are usually due to cache not being found
@@ -148,7 +145,7 @@ export default class S3Cache {
148
145
  }
149
146
  // This one might not even be necessary anymore
150
147
  // Better be safe than sorry
151
- const detachedPromise = globalThis.__als
148
+ const detachedPromise = globalThis.__openNextAls
152
149
  .getStore()
153
150
  ?.pendingPromiseRunner.withResolvers();
154
151
  try {
@@ -158,7 +155,7 @@ export default class S3Cache {
158
155
  else {
159
156
  switch (data.kind) {
160
157
  case "ROUTE":
161
- case "APP_ROUTE":
158
+ case "APP_ROUTE": {
162
159
  const { body, status, headers } = data;
163
160
  await globalThis.incrementalCache.set(key, {
164
161
  type: "route",
@@ -171,6 +168,7 @@ export default class S3Cache {
171
168
  },
172
169
  }, false);
173
170
  break;
171
+ }
174
172
  case "PAGE":
175
173
  case "PAGES": {
176
174
  const { html, pageData, status, headers } = data;
@@ -1,4 +1,4 @@
1
- import path from "path";
1
+ import path from "node:path";
2
2
  import { debug } from "../logger";
3
3
  import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest,
4
4
  // loadPublicAssets,
@@ -1,5 +1,5 @@
1
- import fs from "fs";
2
- import path from "path";
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
3
  export function loadConfig(nextDir) {
4
4
  const filePath = path.join(nextDir, "required-server-files.json");
5
5
  const json = fs.readFileSync(filePath, "utf-8");
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "fs";
1
+ import { readFileSync } from "node:fs";
2
2
  import { createGenericHandler } from "../core/createGenericHandler.js";
3
3
  import { resolveTagCache } from "../core/resolve.js";
4
4
  const PHYSICAL_RESOURCE_ID = "dynamodb-cache";
@@ -11,19 +11,17 @@ async function defaultHandler(event) {
11
11
  switch (event.requestType) {
12
12
  case "delete":
13
13
  return remove();
14
- case "create":
15
- case "update":
16
14
  default:
17
15
  return insert(event.requestType);
18
16
  }
19
17
  }
20
18
  async function insert(requestType) {
21
- const file = readFileSync(`dynamodb-cache.json`, "utf8");
19
+ const file = readFileSync("dynamodb-cache.json", "utf8");
22
20
  const data = JSON.parse(file);
23
21
  const parsedData = data.map((item) => ({
24
22
  tag: item.tag.S,
25
23
  path: item.path.S,
26
- revalidatedAt: parseInt(item.revalidatedAt.N),
24
+ revalidatedAt: Number.parseInt(item.revalidatedAt.N),
27
25
  }));
28
26
  await tagCache.writeTags(parsedData);
29
27
  return {
@@ -1,6 +1,3 @@
1
- declare global {
2
- var isEdgeRuntime: true;
3
- }
4
1
  export declare const handler: (...args: any[]) => any;
5
2
  declare const _default: {
6
3
  fetch: (...args: any[]) => any;
@@ -1,49 +1,54 @@
1
+ import { runWithOpenNextRequestContext } from "../utils/promise";
1
2
  import { emptyReadableStream } from "../utils/stream";
2
3
  // We import it like that so that the edge plugin can replace it
3
4
  import { NextConfig } from "../adapters/config";
4
5
  import { createGenericHandler } from "../core/createGenericHandler";
5
6
  import { convertBodyToReadableStream, convertToQueryString, } from "../core/routing/util";
7
+ globalThis.__openNextAls = new AsyncLocalStorage();
6
8
  const defaultHandler = async (internalEvent) => {
7
9
  globalThis.isEdgeRuntime = true;
8
- const host = internalEvent.headers.host
9
- ? `https://${internalEvent.headers.host}`
10
- : "http://localhost:3000";
11
- const initialUrl = new URL(internalEvent.rawPath, host);
12
- initialUrl.search = convertToQueryString(internalEvent.query);
13
- const url = initialUrl.toString();
14
- // @ts-expect-error - This is bundled
15
- const handler = await import(`./middleware.mjs`);
16
- const response = await handler.default({
17
- headers: internalEvent.headers,
18
- method: internalEvent.method || "GET",
19
- nextConfig: {
20
- basePath: NextConfig.basePath,
21
- i18n: NextConfig.i18n,
22
- trailingSlash: NextConfig.trailingSlash,
23
- },
24
- url,
25
- body: convertBodyToReadableStream(internalEvent.method, internalEvent.body),
10
+ // We run everything in the async local storage context so that it is available in edge runtime functions
11
+ return runWithOpenNextRequestContext({ isISRRevalidation: false }, async () => {
12
+ const host = internalEvent.headers.host
13
+ ? `https://${internalEvent.headers.host}`
14
+ : "http://localhost:3000";
15
+ const initialUrl = new URL(internalEvent.rawPath, host);
16
+ initialUrl.search = convertToQueryString(internalEvent.query);
17
+ const url = initialUrl.toString();
18
+ // @ts-expect-error - This is bundled
19
+ const handler = await import("./middleware.mjs");
20
+ const response = await handler.default({
21
+ headers: internalEvent.headers,
22
+ method: internalEvent.method || "GET",
23
+ nextConfig: {
24
+ basePath: NextConfig.basePath,
25
+ i18n: NextConfig.i18n,
26
+ trailingSlash: NextConfig.trailingSlash,
27
+ },
28
+ url,
29
+ body: convertBodyToReadableStream(internalEvent.method, internalEvent.body),
30
+ });
31
+ const responseHeaders = {};
32
+ response.headers.forEach((value, key) => {
33
+ if (key.toLowerCase() === "set-cookie") {
34
+ responseHeaders[key] = responseHeaders[key]
35
+ ? [...responseHeaders[key], value]
36
+ : [value];
37
+ }
38
+ else {
39
+ responseHeaders[key] = value;
40
+ }
41
+ });
42
+ const body = response.body ?? emptyReadableStream();
43
+ return {
44
+ type: "core",
45
+ statusCode: response.status,
46
+ headers: responseHeaders,
47
+ body: body,
48
+ // Do we need to handle base64 encoded response?
49
+ isBase64Encoded: false,
50
+ };
26
51
  });
27
- const responseHeaders = {};
28
- response.headers.forEach((value, key) => {
29
- if (key.toLowerCase() === "set-cookie") {
30
- responseHeaders[key] = responseHeaders[key]
31
- ? [...responseHeaders[key], value]
32
- : [value];
33
- }
34
- else {
35
- responseHeaders[key] = value;
36
- }
37
- });
38
- const body = response.body ?? emptyReadableStream();
39
- return {
40
- type: "core",
41
- statusCode: response.status,
42
- headers: responseHeaders,
43
- body: body,
44
- // Do we need to handle base64 encoded response?
45
- isBase64Encoded: false,
46
- };
47
52
  };
48
53
  export const handler = await createGenericHandler({
49
54
  handler: defaultHandler,
@@ -46,7 +46,7 @@ export async function defaultHandler(event, streamCreator) {
46
46
  // It might be useful for cases where the user wants to use a different host than the one in the request
47
47
  // It could even allow to have multiple hosts for the image optimization by setting the HOST environment variable in the wrapper for example
48
48
  if (!process.env.HOST) {
49
- const headersHost = headers["x-forwarded-host"] || headers["host"];
49
+ const headersHost = headers["x-forwarded-host"] || headers.host;
50
50
  process.env.HOST = headersHost;
51
51
  }
52
52
  const imageParams = validateImageParams(headers, queryString === null ? undefined : queryString);
@@ -104,7 +104,7 @@ function buildSuccessResponse(result, streamCreator, etag) {
104
104
  };
105
105
  debug("result", result);
106
106
  if (etag) {
107
- headers["ETag"] = etag;
107
+ headers.ETag = etag;
108
108
  }
109
109
  if (streamCreator) {
110
110
  const response = new OpenNextNodeResponse(() => void 0, async () => void 0, streamCreator);
@@ -125,7 +125,7 @@ function buildFailureResponse(e, streamCreator) {
125
125
  const response = new OpenNextNodeResponse(() => void 0, async () => void 0, streamCreator);
126
126
  response.writeHead(500, {
127
127
  Vary: "Accept",
128
- "Cache-Control": `public,max-age=60,immutable`,
128
+ "Cache-Control": "public,max-age=60,immutable",
129
129
  "Content-Type": "application/json",
130
130
  });
131
131
  response.end(e?.message || e?.toString() || "An error occurred");
@@ -137,7 +137,7 @@ function buildFailureResponse(e, streamCreator) {
137
137
  headers: {
138
138
  Vary: "Accept",
139
139
  // For failed images, allow client to retry after 1 minute.
140
- "Cache-Control": `public,max-age=60,immutable`,
140
+ "Cache-Control": "public,max-age=60,immutable",
141
141
  "Content-Type": "application/json",
142
142
  },
143
143
  body: toReadableStream(e?.message || e?.toString() || "An error occurred"),
@@ -1,6 +1,3 @@
1
- declare global {
2
- var openNextDebug: boolean;
3
- }
4
1
  export declare function debug(...args: any[]): void;
5
2
  export declare function warn(...args: any[]): void;
6
3
  export declare function error(...args: any[]): void;
@@ -29,14 +29,12 @@ export function error(...args) {
29
29
  debug(...args);
30
30
  return;
31
31
  }
32
- else if (error.logLevel === 1) {
32
+ if (error.logLevel === 1) {
33
33
  warn(...args);
34
34
  return;
35
35
  }
36
- else {
37
- console.error(...args);
38
- return;
39
- }
36
+ console.error(...args);
37
+ return;
40
38
  }
41
39
  else {
42
40
  console.error(...args);
@@ -1,32 +1,56 @@
1
- import { debug } from "../adapters/logger";
1
+ import { runWithOpenNextRequestContext } from "../utils/promise";
2
+ import { debug, error } from "../adapters/logger";
2
3
  import { createGenericHandler } from "../core/createGenericHandler";
3
- import { resolveIncrementalCache, resolveOriginResolver, resolveQueue, resolveTagCache, } from "../core/resolve";
4
+ import { resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
4
5
  import routingHandler from "../core/routingHandler";
5
6
  globalThis.internalFetch = fetch;
7
+ globalThis.__openNextAls = new AsyncLocalStorage();
6
8
  const defaultHandler = async (internalEvent) => {
7
9
  const originResolver = await resolveOriginResolver(globalThis.openNextConfig.middleware?.originResolver);
10
+ const externalRequestProxy = await resolveProxyRequest(globalThis.openNextConfig.middleware?.override?.proxyExternalRequest);
8
11
  //#override includeCacheInMiddleware
9
12
  globalThis.tagCache = await resolveTagCache(globalThis.openNextConfig.middleware?.override?.tagCache);
10
13
  globalThis.queue = await resolveQueue(globalThis.openNextConfig.middleware?.override?.queue);
11
14
  globalThis.incrementalCache = await resolveIncrementalCache(globalThis.openNextConfig.middleware?.override?.incrementalCache);
12
15
  //#endOverride
13
- const result = await routingHandler(internalEvent);
14
- if ("internalEvent" in result) {
15
- debug("Middleware intercepted event", internalEvent);
16
- let origin = false;
17
- if (!result.isExternalRewrite) {
18
- origin = await originResolver.resolve(result.internalEvent.rawPath);
16
+ // We run everything in the async local storage context so that it is available in the external middleware
17
+ return runWithOpenNextRequestContext({ isISRRevalidation: internalEvent.headers["x-isr"] === "1" }, async () => {
18
+ const result = await routingHandler(internalEvent);
19
+ if ("internalEvent" in result) {
20
+ debug("Middleware intercepted event", internalEvent);
21
+ if (!result.isExternalRewrite) {
22
+ const origin = await originResolver.resolve(result.internalEvent.rawPath);
23
+ return {
24
+ type: "middleware",
25
+ internalEvent: result.internalEvent,
26
+ isExternalRewrite: result.isExternalRewrite,
27
+ origin,
28
+ isISR: result.isISR,
29
+ };
30
+ }
31
+ try {
32
+ return externalRequestProxy.proxy(result.internalEvent);
33
+ }
34
+ catch (e) {
35
+ error("External request failed.", e);
36
+ return {
37
+ type: "middleware",
38
+ internalEvent: {
39
+ ...result.internalEvent,
40
+ rawPath: "/500",
41
+ url: "/500",
42
+ method: "GET",
43
+ },
44
+ // On error we need to rewrite to the 500 page which is an internal rewrite
45
+ isExternalRewrite: false,
46
+ origin: false,
47
+ isISR: result.isISR,
48
+ };
49
+ }
19
50
  }
20
- return {
21
- type: "middleware",
22
- internalEvent: result.internalEvent,
23
- isExternalRewrite: result.isExternalRewrite,
24
- origin,
25
- isISR: result.isISR,
26
- };
27
- }
28
- debug("Middleware response", result);
29
- return result;
51
+ debug("Middleware response", result);
52
+ return result;
53
+ });
30
54
  };
31
55
  export const handler = await createGenericHandler({
32
56
  handler: defaultHandler,
@@ -1,6 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import type { IncomingMessage, ServerResponse } from "node:http";
5
2
  import type { APIGatewayProxyEventHeaders } from "aws-lambda";
6
3
  import type { NextConfig } from "next/dist/server/config-shared";
@@ -1,6 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import type { IncomingMessage, ServerResponse } from "node:http";
5
2
  import type { APIGatewayProxyEventHeaders } from "aws-lambda";
6
3
  import type { NextConfig } from "next/dist/server/config-shared";
@@ -8,7 +8,7 @@ const defaultHandler = async (event) => {
8
8
  const failedRecords = [];
9
9
  for (const record of event.records) {
10
10
  const { host, url } = record;
11
- debug(`Revalidating stale page`, { host, url });
11
+ debug("Revalidating stale page", { host, url });
12
12
  // Make a HEAD request to the page to revalidate it. This will trigger
13
13
  // the page to be re-rendered and cached in S3
14
14
  // - HEAD request is used b/c it's not necessary to make a GET request
@@ -39,7 +39,7 @@ const defaultHandler = async (event) => {
39
39
  resolve(res);
40
40
  });
41
41
  req.on("error", (err) => {
42
- error(`Error revalidating page`, { host, url });
42
+ error("Error revalidating page", { host, url });
43
43
  reject(err);
44
44
  });
45
45
  req.end();
@@ -1,4 +1 @@
1
- declare global {
2
- var internalFetch: typeof fetch;
3
- }
4
1
  export declare const handler: (...args: any[]) => any;
@@ -8,6 +8,7 @@ import { setNodeEnv } from "./util.js";
8
8
  setNodeEnv();
9
9
  setBuildIdEnv();
10
10
  setNextjsServerWorkingDirectory();
11
+ // Because next is messing with fetch, we have to make sure that we use an untouched version of fetch
11
12
  globalThis.internalFetch = fetch;
12
13
  /////////////
13
14
  // Handler //
@@ -17,7 +18,8 @@ export const handler = await createMainHandler();
17
18
  // Helper functions //
18
19
  //////////////////////
19
20
  function setNextjsServerWorkingDirectory() {
20
- // WORKAROUND: Set `NextServer` working directory (AWS specific) — https://github.com/serverless-stack/open-next#workaround-set-nextserver-working-directory-aws-specific
21
+ // WORKAROUND: Set `NextServer` working directory (AWS specific)
22
+ // See https://opennext.js.org/aws/v2/advanced/workaround#workaround-set-nextserver-working-directory-aws-specific
21
23
  process.chdir(__dirname);
22
24
  }
23
25
  function setBuildIdEnv() {
@@ -23,6 +23,6 @@ export function parseNumberFromEnv(envValue) {
23
23
  if (typeof envValue !== "string") {
24
24
  return envValue;
25
25
  }
26
- const parsedValue = parseInt(envValue);
27
- return isNaN(parsedValue) ? undefined : parsedValue;
26
+ const parsedValue = Number.parseInt(envValue);
27
+ return Number.isNaN(parsedValue) ? undefined : parsedValue;
28
28
  }
@@ -1 +1,3 @@
1
- export declare function bundleNextServer(outputDir: string, appPath: string): Promise<void>;
1
+ export declare function bundleNextServer(outputDir: string, appPath: string, { debug }: {
2
+ debug?: boolean | undefined;
3
+ }): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
+ import path from "node:path";
2
3
  import { build } from "esbuild";
3
- import path from "path";
4
4
  const externals = [
5
5
  // This one was causing trouble, don't know why
6
6
  "../experimental/testmode/server",
@@ -35,7 +35,7 @@ const externals = [
35
35
  // Adding it cause to add a lot of unnecessary deps
36
36
  "next/dist/compiled/next-server",
37
37
  ];
38
- export async function bundleNextServer(outputDir, appPath) {
38
+ export async function bundleNextServer(outputDir, appPath, { debug = false }) {
39
39
  const require = createRequire(`${appPath}/package.json`);
40
40
  const entrypoint = require.resolve("next/dist/esm/server/next-server.js");
41
41
  await build({
@@ -46,7 +46,7 @@ export async function bundleNextServer(outputDir, appPath) {
46
46
  // packages: "external",
47
47
  format: "cjs",
48
48
  external: externals,
49
- minify: true,
49
+ minify: !debug,
50
50
  outfile: path.join(outputDir, "next-server.runtime.prod.js"),
51
51
  sourcemap: false,
52
52
  plugins: [
@@ -23,7 +23,7 @@ export async function compileOpenNextConfig(baseDir, openNextConfigPath, nodeExt
23
23
  configPath = `file://${configPath}`;
24
24
  const config = (await import(configPath)).default;
25
25
  if (!config || !config.default) {
26
- logger.error(`config.default cannot be empty, it should be at least {}, see more info here: https://opennext.js.org/config#configuration-file`);
26
+ logger.error("config.default cannot be empty, it should be at least {}, see more info here: https://opennext.js.org/config#configuration-file");
27
27
  process.exit(1);
28
28
  }
29
29
  validateConfig(config);
@@ -1,6 +1,6 @@
1
1
  import url from "node:url";
2
- import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "fs";
3
- import path from "path";
2
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "node:fs";
3
+ import path from "node:path";
4
4
  import logger from "../logger.js";
5
5
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
6
6
  function copyPatchFile(outputDir) {
@@ -15,9 +15,7 @@ export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, r
15
15
  const standaloneDir = path.join(dotNextDir, "standalone");
16
16
  const standaloneNextDir = path.join(standaloneDir, packagePath, ".next");
17
17
  const outputNextDir = path.join(outputDir, packagePath, ".next");
18
- const extractFiles = (files, from = standaloneNextDir) => {
19
- return files.map((f) => path.resolve(from, f));
20
- };
18
+ const extractFiles = (files, from = standaloneNextDir) => files.map((f) => path.resolve(from, f));
21
19
  // On next 14+, we might not have to include those files
22
20
  // For next 13, we need to include them otherwise we get runtime error
23
21
  const requiredServerFiles = JSON.parse(readFileSync(path.join(dotNextDir, bundledNextServer
@@ -59,13 +57,11 @@ See the docs for more information on how to bundle edge runtime functions.
59
57
  --------------------------------------------------------------------------------
60
58
  `);
61
59
  }
62
- else {
63
- throw new Error(`
60
+ throw new Error(`
64
61
  --------------------------------------------------------------------------------
65
62
  We cannot find the route for ${pagePath}.
66
63
  File ${fullFilePath} does not exist
67
64
  --------------------------------------------------------------------------------`);
68
- }
69
65
  }
70
66
  const dir = path.dirname(fullFilePath);
71
67
  extractFiles(requiredFiles.files, path.join(standaloneNextDir, dir)).forEach((f) => {
@@ -144,20 +140,15 @@ File ${fullFilePath} does not exist
144
140
  copyFileSync(from, to);
145
141
  }
146
142
  });
147
- readdirSync(standaloneNextDir).forEach((f) => {
148
- if (statSync(path.join(standaloneNextDir, f)).isDirectory())
149
- return;
150
- copyFileSync(path.join(standaloneNextDir, f), path.join(outputNextDir, f));
151
- });
143
+ readdirSync(standaloneNextDir)
144
+ .filter((fileOrDir) => !statSync(path.join(standaloneNextDir, fileOrDir)).isDirectory())
145
+ .forEach((file) => copyFileSync(path.join(standaloneNextDir, file), path.join(outputNextDir, file)));
152
146
  // We then need to copy all the files at the root of server
153
147
  mkdirSync(path.join(outputNextDir, "server"), { recursive: true });
154
- readdirSync(path.join(standaloneNextDir, "server")).forEach((f) => {
155
- if (statSync(path.join(standaloneNextDir, "server", f)).isDirectory())
156
- return;
157
- if (f !== "server.js") {
158
- copyFileSync(path.join(standaloneNextDir, "server", f), path.join(path.join(outputNextDir, "server"), f));
159
- }
160
- });
148
+ readdirSync(path.join(standaloneNextDir, "server"))
149
+ .filter((fileOrDir) => !statSync(path.join(standaloneNextDir, "server", fileOrDir)).isDirectory())
150
+ .filter((file) => file !== "server.js")
151
+ .forEach((file) => copyFileSync(path.join(standaloneNextDir, "server", file), path.join(path.join(outputNextDir, "server"), file)));
161
152
  // Copy patch file
162
153
  copyPatchFile(path.join(outputDir, packagePath));
163
154
  // TODO: Recompute all the files.
@@ -198,11 +189,9 @@ File ${fullFilePath} does not exist
198
189
  }
199
190
  }
200
191
  });
201
- staticFiles.forEach((f) => {
202
- if (f.endsWith(".html")) {
203
- copyStaticFile(`server/${f}`);
204
- }
205
- });
192
+ staticFiles
193
+ .filter((file) => file.endsWith(".html"))
194
+ .forEach((file) => copyStaticFile(`server/${file}`));
206
195
  }
207
196
  logger.debug("copyTracedFiles:", Date.now() - tsStart, "ms");
208
197
  }