@opennextjs/aws 3.4.2 → 3.5.1

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 (82) hide show
  1. package/dist/adapters/cache.d.ts +2 -61
  2. package/dist/adapters/cache.js +68 -34
  3. package/dist/adapters/config/index.d.ts +1 -0
  4. package/dist/adapters/config/index.js +3 -1
  5. package/dist/adapters/config/util.d.ts +2 -1
  6. package/dist/adapters/config/util.js +13 -3
  7. package/dist/adapters/dynamo-provider.js +8 -0
  8. package/dist/adapters/edge-adapter.js +4 -10
  9. package/dist/adapters/image-optimization-adapter.d.ts +3 -2
  10. package/dist/adapters/image-optimization-adapter.js +3 -3
  11. package/dist/adapters/middleware.js +12 -8
  12. package/dist/build/compileConfig.js +1 -1
  13. package/dist/build/compileTagCacheProvider.js +1 -0
  14. package/dist/build/constant.d.ts +1 -0
  15. package/dist/build/constant.js +2 -0
  16. package/dist/build/copyTracedFiles.d.ts +10 -1
  17. package/dist/build/copyTracedFiles.js +22 -12
  18. package/dist/build/createAssets.d.ts +3 -1
  19. package/dist/build/createAssets.js +2 -2
  20. package/dist/build/createMiddleware.js +18 -5
  21. package/dist/build/createServerBundle.js +7 -1
  22. package/dist/build/edge/createEdgeBundle.js +14 -12
  23. package/dist/build/generateOutput.js +4 -6
  24. package/dist/build/middleware/buildNodeMiddleware.d.ts +3 -0
  25. package/dist/build/middleware/buildNodeMiddleware.js +97 -0
  26. package/dist/core/createMainHandler.js +0 -1
  27. package/dist/core/edgeFunctionHandler.js +3 -1
  28. package/dist/core/nodeMiddlewareHandler.d.ts +4 -0
  29. package/dist/core/nodeMiddlewareHandler.js +25 -0
  30. package/dist/core/requestHandler.d.ts +3 -2
  31. package/dist/core/requestHandler.js +58 -25
  32. package/dist/core/routing/cacheInterceptor.js +7 -6
  33. package/dist/core/routing/i18n/index.d.ts +29 -1
  34. package/dist/core/routing/i18n/index.js +98 -4
  35. package/dist/core/routing/matcher.js +17 -13
  36. package/dist/core/routing/middleware.js +14 -29
  37. package/dist/core/routing/queue.d.ts +1 -0
  38. package/dist/core/routing/queue.js +41 -0
  39. package/dist/core/routing/util.d.ts +12 -3
  40. package/dist/core/routing/util.js +25 -50
  41. package/dist/core/routingHandler.d.ts +2 -1
  42. package/dist/core/routingHandler.js +10 -4
  43. package/dist/core/util.d.ts +1 -4
  44. package/dist/core/util.js +7 -15
  45. package/dist/overrides/converters/aws-apigw-v1.js +4 -3
  46. package/dist/overrides/converters/aws-apigw-v2.js +4 -3
  47. package/dist/overrides/converters/aws-cloudfront.js +6 -4
  48. package/dist/overrides/converters/edge.js +1 -10
  49. package/dist/overrides/converters/node.js +10 -8
  50. package/dist/overrides/converters/utils.d.ts +7 -0
  51. package/dist/overrides/converters/utils.js +9 -0
  52. package/dist/overrides/imageLoader/s3-lite.d.ts +3 -0
  53. package/dist/overrides/imageLoader/s3-lite.js +61 -0
  54. package/dist/overrides/tagCache/dummy.js +1 -0
  55. package/dist/overrides/tagCache/dynamodb-lite.js +1 -0
  56. package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +7 -0
  57. package/dist/overrides/tagCache/dynamodb-nextMode.js +119 -0
  58. package/dist/overrides/tagCache/dynamodb.js +1 -0
  59. package/dist/overrides/tagCache/fs-dev.js +1 -0
  60. package/dist/overrides/wrappers/aws-lambda-streaming.js +1 -1
  61. package/dist/overrides/wrappers/aws-lambda.js +3 -1
  62. package/dist/overrides/wrappers/cloudflare-edge.js +3 -2
  63. package/dist/overrides/wrappers/cloudflare-node.js +5 -8
  64. package/dist/overrides/wrappers/dummy.js +2 -2
  65. package/dist/overrides/wrappers/express-dev.js +4 -4
  66. package/dist/overrides/wrappers/node.js +2 -2
  67. package/dist/plugins/edge.d.ts +1 -3
  68. package/dist/plugins/edge.js +4 -11
  69. package/dist/plugins/externalMiddleware.d.ts +2 -0
  70. package/dist/plugins/externalMiddleware.js +10 -0
  71. package/dist/plugins/resolve.js +2 -2
  72. package/dist/types/cache.d.ts +34 -4
  73. package/dist/types/global.d.ts +19 -31
  74. package/dist/types/next-types.d.ts +20 -0
  75. package/dist/types/open-next.d.ts +12 -5
  76. package/dist/types/overrides.d.ts +72 -6
  77. package/dist/utils/cache.d.ts +3 -0
  78. package/dist/utils/cache.js +32 -0
  79. package/dist/utils/promise.d.ts +3 -1
  80. package/dist/utils/promise.js +18 -11
  81. package/dist/utils/regex.js +1 -1
  82. package/package.json +5 -4
@@ -100,7 +100,13 @@ async function generateBundle(name, options, fnOptions) {
100
100
  // Copy env files
101
101
  buildHelper.copyEnvFile(appBuildOutputPath, packagePath, outputPath);
102
102
  // Copy all necessary traced files
103
- await copyTracedFiles(appBuildOutputPath, packagePath, outputPath, fnOptions.routes ?? ["app/page.tsx"], isBundled);
103
+ await copyTracedFiles({
104
+ buildOutputPath: appBuildOutputPath,
105
+ packagePath,
106
+ outputDir: outputPath,
107
+ routes: fnOptions.routes ?? ["app/page.tsx"],
108
+ bundledNextServer: isBundled,
109
+ });
104
110
  // Build Lambda code
105
111
  // note: bundle in OpenNext package b/c the adapter relies on the
106
112
  // "serverless-http" package which is not a dependency in user's
@@ -2,8 +2,10 @@ import { mkdirSync } from "node:fs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { build } from "esbuild";
5
+ import { loadMiddlewareManifest } from "../../adapters/config/util.js";
5
6
  import logger from "../../logger.js";
6
7
  import { openNextEdgePlugins } from "../../plugins/edge.js";
8
+ import { openNextExternalMiddlewarePlugin } from "../../plugins/externalMiddleware.js";
7
9
  import { openNextReplacementPlugin } from "../../plugins/replacement.js";
8
10
  import { openNextResolvePlugin } from "../../plugins/resolve.js";
9
11
  import { getCrossPlatformPathRegex } from "../../utils/regex.js";
@@ -45,10 +47,10 @@ export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, opt
45
47
  target: getCrossPlatformPathRegex("adapters/middleware.js"),
46
48
  deletes: includeCache ? [] : ["includeCacheInMiddleware"],
47
49
  }),
50
+ openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core/edgeFunctionHandler.js")),
48
51
  openNextEdgePlugins({
49
52
  middlewareInfo,
50
53
  nextDir: path.join(options.appBuildOutputPath, ".next"),
51
- edgeFunctionHandlerPath: path.join(options.openNextDistDir, "core", "edgeFunctionHandler.js"),
52
54
  isInCloudfare,
53
55
  }),
54
56
  ],
@@ -116,15 +118,15 @@ Object.defineProperty = function(o, p, a) {
116
118
  }
117
119
  }
118
120
  export async function generateEdgeBundle(name, options, fnOptions) {
119
- const { appBuildOutputPath, outputDir } = options;
120
121
  logger.info(`Generating edge bundle for: ${name}`);
122
+ const buildOutputDotNextDir = path.join(options.appBuildOutputPath, ".next");
121
123
  // Create output folder
122
- const outputPath = path.join(outputDir, "server-functions", name);
123
- fs.mkdirSync(outputPath, { recursive: true });
124
+ const outputDir = path.join(options.outputDir, "server-functions", name);
125
+ fs.mkdirSync(outputDir, { recursive: true });
124
126
  // Copy open-next.config.mjs
125
- copyOpenNextConfig(options.buildDir, outputPath, true);
127
+ copyOpenNextConfig(options.buildDir, outputDir, true);
126
128
  // Load middleware manifest
127
- const middlewareManifest = JSON.parse(fs.readFileSync(path.join(appBuildOutputPath, ".next/server/middleware-manifest.json"), "utf8"));
129
+ const middlewareManifest = loadMiddlewareManifest(buildOutputDotNextDir);
128
130
  // Find functions
129
131
  const functions = Object.values(middlewareManifest.functions).filter((fn) => fnOptions.routes.includes(fn.name));
130
132
  if (functions.length > 1) {
@@ -133,20 +135,20 @@ export async function generateEdgeBundle(name, options, fnOptions) {
133
135
  const middlewareInfo = functions[0];
134
136
  //Copy wasm files
135
137
  const wasmFiles = middlewareInfo.wasm;
136
- mkdirSync(path.join(outputPath, "wasm"), { recursive: true });
138
+ mkdirSync(path.join(outputDir, "wasm"), { recursive: true });
137
139
  for (const wasmFile of wasmFiles) {
138
- fs.copyFileSync(path.join(appBuildOutputPath, ".next", wasmFile.filePath), path.join(outputPath, `wasm/${wasmFile.name}.wasm`));
140
+ fs.copyFileSync(path.join(buildOutputDotNextDir, wasmFile.filePath), path.join(outputDir, `wasm/${wasmFile.name}.wasm`));
139
141
  }
140
142
  // Copy assets
141
143
  const assets = middlewareInfo.assets;
142
- mkdirSync(path.join(outputPath, "assets"), { recursive: true });
144
+ mkdirSync(path.join(outputDir, "assets"), { recursive: true });
143
145
  for (const asset of assets) {
144
- fs.copyFileSync(path.join(appBuildOutputPath, ".next", asset.filePath), path.join(outputPath, `assets/${asset.name}`));
146
+ fs.copyFileSync(path.join(buildOutputDotNextDir, asset.filePath), path.join(outputDir, `assets/${asset.name}`));
145
147
  }
146
148
  await buildEdgeBundle({
147
149
  middlewareInfo,
148
- entrypoint: path.join(options.openNextDistDir, "adapters", "edge-adapter.js"),
149
- outfile: path.join(outputPath, "index.mjs"),
150
+ entrypoint: path.join(options.openNextDistDir, "adapters/edge-adapter.js"),
151
+ outfile: path.join(outputDir, "index.mjs"),
150
152
  options,
151
153
  overrides: fnOptions.override,
152
154
  additionalExternals: options.config.edgeExternals,
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { loadConfig } from "../adapters/config/util.js";
3
4
  import { getBuildId } from "./helper.js";
4
5
  const indexHandler = "index.handler";
5
6
  async function canStream(opts) {
@@ -78,9 +79,8 @@ export async function generateOutput(options) {
78
79
  }
79
80
  });
80
81
  const defaultOriginCanstream = await canStream(config.default);
81
- //Load required-server-files.json
82
- const requiredServerFiles = JSON.parse(fs.readFileSync(path.join(appBuildOutputPath, ".next", "required-server-files.json"), "utf-8")).config;
83
- const prefixer = prefixPattern(requiredServerFiles.basePath ?? "");
82
+ const nextConfig = loadConfig(path.join(appBuildOutputPath, ".next"));
83
+ const prefixer = prefixPattern(nextConfig.basePath ?? "");
84
84
  // First add s3 origins and image optimization
85
85
  const defaultOrigins = {
86
86
  s3: {
@@ -89,9 +89,7 @@ export async function generateOutput(options) {
89
89
  copy: [
90
90
  {
91
91
  from: ".open-next/assets",
92
- to: requiredServerFiles.basePath
93
- ? `_assets${requiredServerFiles.basePath}`
94
- : "_assets",
92
+ to: nextConfig.basePath ? `_assets${nextConfig.basePath}` : "_assets",
95
93
  cached: true,
96
94
  versionedSubDir: prefixer("_next"),
97
95
  },
@@ -0,0 +1,3 @@
1
+ import * as buildHelper from "../helper.js";
2
+ export declare function buildExternalNodeMiddleware(options: buildHelper.BuildOptions): Promise<void>;
3
+ export declare function buildBundledNodeMiddleware(options: buildHelper.BuildOptions): Promise<void>;
@@ -0,0 +1,97 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { getCrossPlatformPathRegex } from "../../utils/regex.js";
4
+ import { openNextExternalMiddlewarePlugin } from "../../plugins/externalMiddleware.js";
5
+ import { openNextReplacementPlugin } from "../../plugins/replacement.js";
6
+ import { openNextResolvePlugin } from "../../plugins/resolve.js";
7
+ import { copyTracedFiles } from "../copyTracedFiles.js";
8
+ import * as buildHelper from "../helper.js";
9
+ import { installDependencies } from "../installDeps.js";
10
+ export async function buildExternalNodeMiddleware(options) {
11
+ const { appBuildOutputPath, config, outputDir } = options;
12
+ if (!config.middleware?.external) {
13
+ throw new Error("This function should only be called for external middleware");
14
+ }
15
+ const outputPath = path.join(outputDir, "middleware");
16
+ fs.mkdirSync(outputPath, { recursive: true });
17
+ // Copy open-next.config.mjs
18
+ buildHelper.copyOpenNextConfig(options.buildDir, outputPath, await buildHelper.isEdgeRuntime(config.middleware.override));
19
+ const overrides = {
20
+ ...config.middleware.override,
21
+ originResolver: config.middleware.originResolver,
22
+ };
23
+ const includeCache = config.dangerous?.enableCacheInterception;
24
+ const packagePath = buildHelper.getPackagePath(options);
25
+ // TODO: change this so that we don't copy unnecessary files
26
+ await copyTracedFiles({
27
+ buildOutputPath: appBuildOutputPath,
28
+ packagePath,
29
+ outputDir: outputPath,
30
+ routes: [],
31
+ bundledNextServer: false,
32
+ skipServerFiles: true,
33
+ });
34
+ function override(target) {
35
+ return typeof overrides?.[target] === "string"
36
+ ? overrides[target]
37
+ : undefined;
38
+ }
39
+ // Bundle middleware
40
+ await buildHelper.esbuildAsync({
41
+ entryPoints: [
42
+ path.join(options.openNextDistDir, "adapters", "middleware.js"),
43
+ ],
44
+ outfile: path.join(outputPath, "handler.mjs"),
45
+ external: ["./.next/*"],
46
+ platform: "node",
47
+ plugins: [
48
+ openNextResolvePlugin({
49
+ overrides: {
50
+ wrapper: override("wrapper") ?? "aws-lambda",
51
+ converter: override("converter") ?? "aws-cloudfront",
52
+ ...(includeCache
53
+ ? {
54
+ tagCache: override("tagCache") ?? "dynamodb-lite",
55
+ incrementalCache: override("incrementalCache") ?? "s3-lite",
56
+ queue: override("queue") ?? "sqs-lite",
57
+ }
58
+ : {}),
59
+ originResolver: override("originResolver") ?? "pattern-env",
60
+ proxyExternalRequest: override("proxyExternalRequest") ?? "node",
61
+ },
62
+ fnName: "middleware",
63
+ }),
64
+ openNextReplacementPlugin({
65
+ name: "externalMiddlewareOverrides",
66
+ target: getCrossPlatformPathRegex("adapters/middleware.js"),
67
+ deletes: includeCache ? [] : ["includeCacheInMiddleware"],
68
+ }),
69
+ openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core", "nodeMiddlewareHandler.js")),
70
+ ],
71
+ banner: {
72
+ js: [
73
+ `globalThis.monorepoPackagePath = '${packagePath}';`,
74
+ "import process from 'node:process';",
75
+ "import { Buffer } from 'node:buffer';",
76
+ "import { AsyncLocalStorage } from 'node:async_hooks';",
77
+ "import { createRequire as topLevelCreateRequire } from 'module';",
78
+ "const require = topLevelCreateRequire(import.meta.url);",
79
+ "import bannerUrl from 'url';",
80
+ "const __dirname = bannerUrl.fileURLToPath(new URL('.', import.meta.url));",
81
+ ].join(""),
82
+ },
83
+ }, options);
84
+ // Do we need to copy or do something with env file here?
85
+ installDependencies(outputPath, config.middleware?.install);
86
+ }
87
+ export async function buildBundledNodeMiddleware(options) {
88
+ await buildHelper.esbuildAsync({
89
+ entryPoints: [
90
+ path.join(options.openNextDistDir, "core/nodeMiddlewareHandler.js"),
91
+ ],
92
+ external: ["./.next/*"],
93
+ outfile: path.join(options.buildDir, "middleware.mjs"),
94
+ bundle: true,
95
+ platform: "node",
96
+ }, options);
97
+ }
@@ -16,7 +16,6 @@ export async function createMainHandler() {
16
16
  globalThis.tagCache = await resolveTagCache(thisFunction.override?.tagCache);
17
17
  globalThis.proxyExternalRequest = await resolveProxyRequest(thisFunction.override?.proxyExternalRequest);
18
18
  globalThis.cdnInvalidationHandler = await resolveCdnInvalidation(thisFunction.override?.cdnInvalidation);
19
- globalThis.lastModified = {};
20
19
  // From the config, we create the converter
21
20
  const converter = await resolveConverter(thisFunction.override?.converter);
22
21
  // Then we create the handler
@@ -16,7 +16,9 @@ export default async function edgeFunctionHandler(request) {
16
16
  },
17
17
  },
18
18
  });
19
- await result.waitUntil;
19
+ globalThis.__openNextAls
20
+ .getStore()
21
+ ?.pendingPromiseRunner.add(result.waitUntil);
20
22
  const response = result.response;
21
23
  return response;
22
24
  }
@@ -0,0 +1,4 @@
1
+ import type { RequestData } from "../types/global";
2
+ type EdgeRequest = Omit<RequestData, "page">;
3
+ export default function middlewareHandler(request: EdgeRequest): Promise<Response>;
4
+ export {};
@@ -0,0 +1,25 @@
1
+ // Do we need Buffer here?
2
+ import { Buffer } from "node:buffer";
3
+ globalThis.Buffer = Buffer;
4
+ // AsyncLocalStorage is needed to be defined globally
5
+ import { AsyncLocalStorage } from "node:async_hooks";
6
+ globalThis.AsyncLocalStorage = AsyncLocalStorage;
7
+ let _module;
8
+ export default async function middlewareHandler(request) {
9
+ if (!_module) {
10
+ // We use await import here so that we are sure that it is loaded after AsyncLocalStorage is defined on globalThis
11
+ // We need both await here, same way as in https://github.com/opennextjs/opennextjs-aws/pull/704
12
+ //@ts-expect-error - This file should be bundled with esbuild
13
+ _module = await (await import("./.next/server/middleware.js")).default;
14
+ }
15
+ const adapterFn = _module.default || _module;
16
+ const result = await adapterFn({
17
+ handler: _module.middleware || _module,
18
+ request: request,
19
+ page: "middleware",
20
+ });
21
+ globalThis.__openNextAls
22
+ .getStore()
23
+ ?.pendingPromiseRunner.add(result.waitUntil);
24
+ return result.response;
25
+ }
@@ -1,2 +1,3 @@
1
- import type { InternalEvent, InternalResult, StreamCreator } from "../types/open-next";
2
- export declare function openNextHandler(internalEvent: InternalEvent, responseStreaming?: StreamCreator): Promise<InternalResult>;
1
+ import type { InternalEvent, InternalResult } from "../types/open-next";
2
+ import type { OpenNextHandlerOptions } from "../types/overrides";
3
+ export declare function openNextHandler(internalEvent: InternalEvent, options?: OpenNextHandlerOptions): Promise<InternalResult>;
@@ -1,25 +1,29 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { IncomingMessage } from "../http/index.js";
3
3
  import { runWithOpenNextRequestContext } from "../utils/promise";
4
+ import { NextConfig } from "../adapters/config/index";
4
5
  import { debug, error, warn } from "../adapters/logger";
5
6
  import { patchAsyncStorage } from "./patchAsyncStorage";
6
- import { convertRes, createServerResponse } from "./routing/util";
7
- import routingHandler, { INTERNAL_HEADER_INITIAL_PATH, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
7
+ import { constructNextUrl, convertRes, convertToQuery, createServerResponse, } from "./routing/util";
8
+ import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
8
9
  import { requestHandler, setNextjsPrebundledReact } from "./util";
9
10
  // This is used to identify requests in the cache
10
11
  globalThis.__openNextAls = new AsyncLocalStorage();
11
12
  patchAsyncStorage();
12
- export async function openNextHandler(internalEvent, responseStreaming) {
13
+ export async function openNextHandler(internalEvent, options) {
13
14
  const initialHeaders = internalEvent.headers;
14
15
  // We run everything in the async local storage context so that it is available in the middleware as well as in NextServer
15
- return runWithOpenNextRequestContext({ isISRRevalidation: initialHeaders["x-isr"] === "1" }, async () => {
16
+ return runWithOpenNextRequestContext({
17
+ isISRRevalidation: initialHeaders["x-isr"] === "1",
18
+ waitUntil: options?.waitUntil,
19
+ }, async () => {
16
20
  if (initialHeaders["x-forwarded-host"]) {
17
21
  initialHeaders.host = initialHeaders["x-forwarded-host"];
18
22
  }
19
23
  debug("internalEvent", internalEvent);
20
24
  // These 2 will get overwritten by the routing handler if not using an external middleware
21
25
  const internalHeaders = {
22
- initialPath: initialHeaders[INTERNAL_HEADER_INITIAL_PATH] ?? internalEvent.rawPath,
26
+ initialPath: initialHeaders[INTERNAL_HEADER_INITIAL_URL] ?? internalEvent.rawPath,
23
27
  resolvedRoutes: initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES]
24
28
  ? JSON.parse(initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES])
25
29
  : [],
@@ -29,6 +33,7 @@ export async function openNextHandler(internalEvent, responseStreaming) {
29
33
  isExternalRewrite: false,
30
34
  origin: false,
31
35
  isISR: false,
36
+ initialURL: internalEvent.url,
32
37
  ...internalHeaders,
33
38
  };
34
39
  //#override withRouting
@@ -65,7 +70,7 @@ export async function openNextHandler(internalEvent, responseStreaming) {
65
70
  rawPath: "/500",
66
71
  method: "GET",
67
72
  headers: {},
68
- url: "/500",
73
+ url: constructNextUrl(internalEvent.url, "/500"),
69
74
  query: {},
70
75
  cookies: {},
71
76
  remoteAddress: "",
@@ -74,22 +79,22 @@ export async function openNextHandler(internalEvent, responseStreaming) {
74
79
  isExternalRewrite: false,
75
80
  isISR: false,
76
81
  origin: false,
77
- initialPath: internalEvent.rawPath,
82
+ initialURL: internalEvent.url,
78
83
  resolvedRoutes: [{ route: "/500", type: "page" }],
79
84
  };
80
85
  }
81
86
  }
82
87
  if ("type" in routingResult) {
83
88
  // response is used only in the streaming case
84
- if (responseStreaming) {
89
+ if (options?.streamCreator) {
85
90
  const response = createServerResponse({
86
91
  internalEvent,
87
92
  isExternalRewrite: false,
88
93
  isISR: false,
89
94
  resolvedRoutes: [],
90
95
  origin: false,
91
- initialPath: internalEvent.rawPath,
92
- }, headers, responseStreaming);
96
+ initialURL: internalEvent.url,
97
+ }, headers, options.streamCreator);
93
98
  response.statusCode = routingResult.statusCode;
94
99
  response.flushHeaders();
95
100
  const [bodyToConsume, bodyToReturn] = routingResult.body.tee();
@@ -103,12 +108,13 @@ export async function openNextHandler(internalEvent, responseStreaming) {
103
108
  }
104
109
  const preprocessedEvent = routingResult.internalEvent;
105
110
  debug("preprocessedEvent", preprocessedEvent);
111
+ const { search, pathname, hash } = new URL(preprocessedEvent.url);
106
112
  const reqProps = {
107
113
  method: preprocessedEvent.method,
108
- url: preprocessedEvent.url,
114
+ url: `${pathname}${search}${hash}`,
109
115
  //WORKAROUND: We pass this header to the serverless function to mimic a prefetch request which will not trigger revalidation since we handle revalidation differently
110
116
  // There is 3 way we can handle revalidation:
111
- // 1. We could just let the revalidation go as normal, but due to race condtions the revalidation will be unreliable
117
+ // 1. We could just let the revalidation go as normal, but due to race conditions the revalidation will be unreliable
112
118
  // 2. We could alter the lastModified time of our cache to make next believe that the cache is fresh, but this could cause issues with stale data since the cdn will cache the stale data as if it was fresh
113
119
  // 3. OUR CHOICE: We could pass a purpose prefetch header to the serverless function to make next believe that the request is a prefetch request and not trigger revalidation (This could potentially break in the future if next changes the behavior of prefetch requests)
114
120
  headers: { ...headers, purpose: "prefetch" },
@@ -124,8 +130,8 @@ export async function openNextHandler(internalEvent, responseStreaming) {
124
130
  store.mergeHeadersPriority = mergeHeadersPriority;
125
131
  }
126
132
  const req = new IncomingMessage(reqProps);
127
- const res = createServerResponse(routingResult, overwrittenResponseHeaders, responseStreaming);
128
- await processRequest(req, res, preprocessedEvent);
133
+ const res = createServerResponse(routingResult, overwrittenResponseHeaders, options?.streamCreator);
134
+ await processRequest(req, res, routingResult);
129
135
  const { statusCode, headers: responseHeaders, isBase64Encoded, body, } = convertRes(res);
130
136
  const internalResult = {
131
137
  type: internalEvent.type,
@@ -134,36 +140,56 @@ export async function openNextHandler(internalEvent, responseStreaming) {
134
140
  body,
135
141
  isBase64Encoded,
136
142
  };
137
- const requestId = store?.requestId;
138
- if (requestId) {
139
- // reset lastModified. We need to do this to avoid memory leaks
140
- delete globalThis.lastModified[requestId];
141
- }
142
143
  return internalResult;
143
144
  });
144
145
  }
145
- async function processRequest(req, res, internalEvent) {
146
+ async function processRequest(req, res, routingResult) {
146
147
  // @ts-ignore
147
148
  // Next.js doesn't parse body if the property exists
148
149
  // https://github.com/dougmoscrop/serverless-http/issues/227
149
150
  delete req.body;
150
151
  try {
151
152
  //#override applyNextjsPrebundledReact
152
- setNextjsPrebundledReact(internalEvent.rawPath);
153
+ setNextjsPrebundledReact(routingResult.internalEvent.rawPath);
153
154
  //#endOverride
155
+ // Here we try to apply as much request metadata as possible
156
+ // We apply every metadata from `resolve-routes` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/resolve-routes.ts
157
+ // and `router-server` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts
158
+ const initialURL = new URL(routingResult.initialURL);
159
+ let invokeStatus;
160
+ if (routingResult.internalEvent.rawPath === "/500") {
161
+ invokeStatus = 500;
162
+ }
163
+ else if (routingResult.internalEvent.rawPath === "/404") {
164
+ invokeStatus = 404;
165
+ }
166
+ const requestMetadata = {
167
+ isNextDataReq: routingResult.internalEvent.query.__nextDataReq === "1",
168
+ initURL: routingResult.initialURL,
169
+ initQuery: convertToQuery(initialURL.search),
170
+ initProtocol: initialURL.protocol,
171
+ defaultLocale: NextConfig.i18n?.defaultLocale,
172
+ locale: routingResult.locale,
173
+ middlewareInvoke: false,
174
+ // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js
175
+ invokePath: routingResult.internalEvent.rawPath,
176
+ invokeQuery: routingResult.internalEvent.query,
177
+ // invokeStatus is only used for error pages
178
+ invokeStatus,
179
+ };
154
180
  // Next Server
155
- await requestHandler(req, res);
181
+ await requestHandler(requestMetadata)(req, res);
156
182
  }
157
183
  catch (e) {
158
184
  // This might fail when using bundled next, importing won't do the trick either
159
185
  if (e.constructor.name === "NoFallbackError") {
160
186
  // Do we need to handle _not-found
161
187
  // Ideally this should never get triggered and be intercepted by the routing handler
162
- await tryRenderError("404", res, internalEvent);
188
+ await tryRenderError("404", res, routingResult.internalEvent);
163
189
  }
164
190
  else {
165
191
  error("NextJS request failed.", e);
166
- await tryRenderError("500", res, internalEvent);
192
+ await tryRenderError("500", res, routingResult.internalEvent);
167
193
  }
168
194
  }
169
195
  }
@@ -176,7 +202,14 @@ async function tryRenderError(type, res, internalEvent) {
176
202
  body: internalEvent.body,
177
203
  remoteAddress: internalEvent.remoteAddress,
178
204
  });
179
- await requestHandler(_req, res);
205
+ // By setting this it will allow us to bypass and directly render the 404 or 500 page
206
+ const requestMetadata = {
207
+ // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js
208
+ invokePath: type === "404" ? "/404" : "/500",
209
+ invokeStatus: type === "404" ? 404 : 500,
210
+ middlewareInvoke: false,
211
+ };
212
+ await requestHandler(requestMetadata)(_req, res);
180
213
  }
181
214
  catch (e) {
182
215
  error("NextJS request failed.", e);
@@ -1,9 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { NextConfig, PrerenderManifest } from "../../adapters/config/index";
3
3
  import { emptyReadableStream, toReadableStream } from "../../utils/stream";
4
+ import { getTagsFromValue, hasBeenRevalidated } from "../../utils/cache";
4
5
  import { debug } from "../../adapters/logger";
5
6
  import { localizePath } from "./i18n";
6
- import { generateMessageGroupId } from "./util";
7
+ import { generateMessageGroupId } from "./queue";
7
8
  const CACHE_ONE_YEAR = 60 * 60 * 24 * 365;
8
9
  const CACHE_ONE_MONTH = 60 * 60 * 24 * 30;
9
10
  async function computeCacheControl(path, body, host, revalidate, lastModified) {
@@ -123,11 +124,11 @@ export async function cacheInterceptor(event) {
123
124
  if (!cachedData?.value) {
124
125
  return event;
125
126
  }
126
- if (cachedData?.value?.type === "app") {
127
- // We need to check the tag cache now
128
- const _lastModified = await globalThis.tagCache.getLastModified(localizedPath, cachedData.lastModified);
129
- if (_lastModified === -1) {
130
- // If some tags are stale we need to force revalidation
127
+ // We need to check the tag cache now
128
+ if (cachedData.value?.type === "app") {
129
+ const tags = getTagsFromValue(cachedData.value);
130
+ const _hasBeenRevalidated = await hasBeenRevalidated(localizedPath, tags, cachedData);
131
+ if (_hasBeenRevalidated) {
131
132
  return event;
132
133
  }
133
134
  }
@@ -1,2 +1,30 @@
1
- import type { InternalEvent } from "../../../types/open-next";
1
+ import type { DomainLocale, i18nConfig } from "../../../types/next-types";
2
+ import type { InternalEvent, InternalResult } from "../../../types/open-next";
3
+ /**
4
+ * @param arg an object containing the hostname and detectedLocale
5
+ * @returns The `DomainLocale` object if a domain is detected, `undefined` otherwise
6
+ */
7
+ export declare function detectDomainLocale({ hostname, detectedLocale, }: {
8
+ hostname?: string;
9
+ detectedLocale?: string;
10
+ }): DomainLocale | undefined;
11
+ /**
12
+ *
13
+ * @param internalEvent
14
+ * @param i18n
15
+ * @returns The detected locale, if `localeDetection` is set to `false` it will return the default locale **or** the domain default locale if a domain is detected.
16
+ */
17
+ export declare function detectLocale(internalEvent: InternalEvent, i18n: i18nConfig): string;
18
+ /**
19
+ * This function is used for OpenNext internal routing to localize the path for next config rewrite/redirects/headers and the middleware
20
+ * @param internalEvent
21
+ * @returns The localized path
22
+ */
2
23
  export declare function localizePath(internalEvent: InternalEvent): string;
24
+ /**
25
+ *
26
+ * @param internalEvent
27
+ * In this function, for domain locale redirect we need to rely on the host to be present and correct
28
+ * @returns `false` if no redirect is needed, `InternalResult` if a redirect is needed
29
+ */
30
+ export declare function handleLocaleRedirect(internalEvent: InternalEvent): false | InternalResult;