@opennextjs/aws 3.4.2 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cache.d.ts +2 -61
- package/dist/adapters/cache.js +68 -34
- package/dist/adapters/config/index.d.ts +1 -0
- package/dist/adapters/config/index.js +3 -1
- package/dist/adapters/config/util.d.ts +2 -1
- package/dist/adapters/config/util.js +13 -3
- package/dist/adapters/dynamo-provider.js +8 -0
- package/dist/adapters/edge-adapter.js +4 -10
- package/dist/adapters/image-optimization-adapter.d.ts +3 -2
- package/dist/adapters/image-optimization-adapter.js +3 -3
- package/dist/adapters/middleware.js +12 -8
- package/dist/build/compileConfig.js +1 -1
- package/dist/build/compileTagCacheProvider.js +1 -0
- package/dist/build/constant.d.ts +1 -0
- package/dist/build/constant.js +2 -0
- package/dist/build/copyTracedFiles.d.ts +10 -1
- package/dist/build/copyTracedFiles.js +22 -12
- package/dist/build/createAssets.d.ts +3 -1
- package/dist/build/createAssets.js +2 -2
- package/dist/build/createMiddleware.js +18 -5
- package/dist/build/createServerBundle.js +7 -1
- package/dist/build/edge/createEdgeBundle.js +14 -12
- package/dist/build/generateOutput.js +4 -6
- package/dist/build/middleware/buildNodeMiddleware.d.ts +3 -0
- package/dist/build/middleware/buildNodeMiddleware.js +97 -0
- package/dist/core/createMainHandler.js +0 -1
- package/dist/core/edgeFunctionHandler.js +3 -1
- package/dist/core/nodeMiddlewareHandler.d.ts +4 -0
- package/dist/core/nodeMiddlewareHandler.js +25 -0
- package/dist/core/requestHandler.d.ts +3 -2
- package/dist/core/requestHandler.js +58 -25
- package/dist/core/routing/cacheInterceptor.js +7 -6
- package/dist/core/routing/i18n/index.d.ts +2 -0
- package/dist/core/routing/i18n/index.js +1 -1
- package/dist/core/routing/matcher.js +13 -12
- package/dist/core/routing/middleware.js +14 -29
- package/dist/core/routing/queue.d.ts +1 -0
- package/dist/core/routing/queue.js +41 -0
- package/dist/core/routing/util.d.ts +12 -3
- package/dist/core/routing/util.js +24 -50
- package/dist/core/routingHandler.d.ts +2 -1
- package/dist/core/routingHandler.js +10 -4
- package/dist/core/util.d.ts +1 -4
- package/dist/core/util.js +7 -15
- package/dist/overrides/converters/aws-apigw-v1.js +4 -3
- package/dist/overrides/converters/aws-apigw-v2.js +4 -3
- package/dist/overrides/converters/aws-cloudfront.js +6 -4
- package/dist/overrides/converters/edge.js +1 -10
- package/dist/overrides/converters/node.js +10 -8
- package/dist/overrides/converters/utils.d.ts +7 -0
- package/dist/overrides/converters/utils.js +9 -0
- package/dist/overrides/imageLoader/s3-lite.d.ts +3 -0
- package/dist/overrides/imageLoader/s3-lite.js +61 -0
- package/dist/overrides/tagCache/dummy.js +1 -0
- package/dist/overrides/tagCache/dynamodb-lite.js +1 -0
- package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +7 -0
- package/dist/overrides/tagCache/dynamodb-nextMode.js +119 -0
- package/dist/overrides/tagCache/dynamodb.js +1 -0
- package/dist/overrides/tagCache/fs-dev.js +1 -0
- package/dist/overrides/wrappers/aws-lambda-streaming.js +1 -1
- package/dist/overrides/wrappers/aws-lambda.js +3 -1
- package/dist/overrides/wrappers/cloudflare-edge.js +3 -2
- package/dist/overrides/wrappers/cloudflare-node.js +5 -8
- package/dist/overrides/wrappers/dummy.js +2 -2
- package/dist/overrides/wrappers/express-dev.js +4 -4
- package/dist/overrides/wrappers/node.js +2 -2
- package/dist/plugins/edge.d.ts +1 -3
- package/dist/plugins/edge.js +4 -11
- package/dist/plugins/externalMiddleware.d.ts +2 -0
- package/dist/plugins/externalMiddleware.js +10 -0
- package/dist/plugins/resolve.js +2 -2
- package/dist/types/cache.d.ts +34 -4
- package/dist/types/global.d.ts +19 -31
- package/dist/types/next-types.d.ts +13 -0
- package/dist/types/open-next.d.ts +12 -5
- package/dist/types/overrides.d.ts +72 -6
- package/dist/utils/cache.d.ts +3 -0
- package/dist/utils/cache.js +32 -0
- package/dist/utils/promise.d.ts +3 -1
- package/dist/utils/promise.js +18 -11
- package/dist/utils/regex.js +1 -1
- 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(
|
|
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
|
|
123
|
-
fs.mkdirSync(
|
|
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,
|
|
127
|
+
copyOpenNextConfig(options.buildDir, outputDir, true);
|
|
126
128
|
// Load middleware manifest
|
|
127
|
-
const middlewareManifest =
|
|
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(
|
|
138
|
+
mkdirSync(path.join(outputDir, "wasm"), { recursive: true });
|
|
137
139
|
for (const wasmFile of wasmFiles) {
|
|
138
|
-
fs.copyFileSync(path.join(
|
|
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(
|
|
144
|
+
mkdirSync(path.join(outputDir, "assets"), { recursive: true });
|
|
143
145
|
for (const asset of assets) {
|
|
144
|
-
fs.copyFileSync(path.join(
|
|
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
|
|
149
|
-
outfile: path.join(
|
|
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
|
-
|
|
82
|
-
const
|
|
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:
|
|
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,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
|
-
|
|
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,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
|
|
2
|
-
|
|
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, {
|
|
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,
|
|
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({
|
|
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[
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
92
|
-
}, headers,
|
|
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:
|
|
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
|
|
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,
|
|
128
|
-
await processRequest(req, res,
|
|
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,
|
|
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
|
-
|
|
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 "./
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
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,4 @@
|
|
|
1
|
+
import type { i18nConfig } from "../../../types/next-types";
|
|
1
2
|
import type { InternalEvent } from "../../../types/open-next";
|
|
3
|
+
export declare function detectLocale(internalEvent: InternalEvent, i18n: i18nConfig): string;
|
|
2
4
|
export declare function localizePath(internalEvent: InternalEvent): string;
|
|
@@ -12,7 +12,7 @@ function getLocaleFromCookie(cookies) {
|
|
|
12
12
|
? i18n?.locales.find((locale) => nextLocale === locale.toLowerCase())
|
|
13
13
|
: undefined;
|
|
14
14
|
}
|
|
15
|
-
function detectLocale(internalEvent, i18n) {
|
|
15
|
+
export function detectLocale(internalEvent, i18n) {
|
|
16
16
|
if (i18n.localeDetection === false) {
|
|
17
17
|
return i18n.defaultLocale;
|
|
18
18
|
}
|
|
@@ -3,7 +3,7 @@ import { compile, match } from "path-to-regexp";
|
|
|
3
3
|
import { emptyReadableStream, toReadableStream } from "../../utils/stream";
|
|
4
4
|
import { debug } from "../../adapters/logger";
|
|
5
5
|
import { localizePath } from "./i18n";
|
|
6
|
-
import { convertFromQueryString, convertToQueryString, escapeRegex, getUrlParts, isExternal, unescapeRegex, } from "./util";
|
|
6
|
+
import { constructNextUrl, convertFromQueryString, convertToQueryString, escapeRegex, getUrlParts, isExternal, unescapeRegex, } from "./util";
|
|
7
7
|
const routeHasMatcher = (headers, cookies, query) => (redirect) => {
|
|
8
8
|
switch (redirect.type) {
|
|
9
9
|
case "header":
|
|
@@ -96,7 +96,7 @@ export function getNextConfigHeaders(event, configHeaders) {
|
|
|
96
96
|
return requestHeaders;
|
|
97
97
|
}
|
|
98
98
|
export function handleRewrites(event, rewrites) {
|
|
99
|
-
const { rawPath, headers, query, cookies } = event;
|
|
99
|
+
const { rawPath, headers, query, cookies, url } = event;
|
|
100
100
|
const localizedRawPath = localizePath(event);
|
|
101
101
|
const matcher = routeHasMatcher(headers, cookies, query);
|
|
102
102
|
const computeHas = computeParamHas(headers, cookies, query);
|
|
@@ -107,7 +107,7 @@ export function handleRewrites(event, rewrites) {
|
|
|
107
107
|
checkHas(matcher, route.missing, true));
|
|
108
108
|
});
|
|
109
109
|
let finalQuery = query;
|
|
110
|
-
let rewrittenUrl =
|
|
110
|
+
let rewrittenUrl = url;
|
|
111
111
|
const isExternalRewrite = isExternal(rewrite?.destination);
|
|
112
112
|
debug("isExternalRewrite", isExternalRewrite);
|
|
113
113
|
if (rewrite) {
|
|
@@ -121,12 +121,12 @@ export function handleRewrites(event, rewrites) {
|
|
|
121
121
|
// %2B does not get interpreted as a space in the URL thus breaking the query string.
|
|
122
122
|
const encodePlusQueryString = queryString.replaceAll("+", "%20");
|
|
123
123
|
debug("urlParts", { pathname, protocol, hostname, queryString });
|
|
124
|
-
const toDestinationPath = compile(escapeRegex(pathname
|
|
125
|
-
const toDestinationHost = compile(escapeRegex(hostname
|
|
126
|
-
const toDestinationQuery = compile(escapeRegex(encodePlusQueryString
|
|
124
|
+
const toDestinationPath = compile(escapeRegex(pathname));
|
|
125
|
+
const toDestinationHost = compile(escapeRegex(hostname));
|
|
126
|
+
const toDestinationQuery = compile(escapeRegex(encodePlusQueryString));
|
|
127
127
|
const params = {
|
|
128
128
|
// params for the source
|
|
129
|
-
...getParamsFromSource(match(escapeRegex(rewrite
|
|
129
|
+
...getParamsFromSource(match(escapeRegex(rewrite.source)))(pathToUse),
|
|
130
130
|
// params for the has
|
|
131
131
|
...rewrite.has?.reduce((acc, cur) => {
|
|
132
132
|
return Object.assign(acc, computeHas(cur));
|
|
@@ -147,19 +147,20 @@ export function handleRewrites(event, rewrites) {
|
|
|
147
147
|
}
|
|
148
148
|
rewrittenUrl = isExternalRewrite
|
|
149
149
|
? `${protocol}//${rewrittenHost}${rewrittenPath}`
|
|
150
|
-
:
|
|
150
|
+
: new URL(rewrittenPath, event.url).href;
|
|
151
151
|
// Should we merge the query params or use only the ones from the rewrite?
|
|
152
152
|
finalQuery = {
|
|
153
153
|
...query,
|
|
154
154
|
...convertFromQueryString(rewrittenQuery),
|
|
155
155
|
};
|
|
156
|
+
rewrittenUrl += convertToQueryString(finalQuery);
|
|
156
157
|
debug("rewrittenUrl", { rewrittenUrl, finalQuery, isUsingParams });
|
|
157
158
|
}
|
|
158
159
|
return {
|
|
159
160
|
internalEvent: {
|
|
160
161
|
...event,
|
|
161
|
-
rawPath: rewrittenUrl,
|
|
162
|
-
url:
|
|
162
|
+
rawPath: new URL(rewrittenUrl).pathname,
|
|
163
|
+
url: rewrittenUrl,
|
|
163
164
|
},
|
|
164
165
|
__rewrite: rewrite,
|
|
165
166
|
isExternalRewrite,
|
|
@@ -251,7 +252,7 @@ export function fixDataPage(internalEvent, buildId) {
|
|
|
251
252
|
...internalEvent,
|
|
252
253
|
rawPath: newPath,
|
|
253
254
|
query,
|
|
254
|
-
url: `${newPath}${convertToQueryString(query)}`,
|
|
255
|
+
url: new URL(`${newPath}${convertToQueryString(query)}`, internalEvent.url).href,
|
|
255
256
|
};
|
|
256
257
|
}
|
|
257
258
|
return internalEvent;
|
|
@@ -278,7 +279,7 @@ export function handleFallbackFalse(internalEvent, prerenderManifest) {
|
|
|
278
279
|
event: {
|
|
279
280
|
...internalEvent,
|
|
280
281
|
rawPath: "/404",
|
|
281
|
-
url: "/404",
|
|
282
|
+
url: constructNextUrl(internalEvent.url, "/404"),
|
|
282
283
|
headers: {
|
|
283
284
|
...internalEvent.headers,
|
|
284
285
|
"x-invoke-status": "404",
|