@opennextjs/aws 3.4.1 → 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 +75 -37
- 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 +3 -3
- 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 +5 -7
- package/dist/build/helper.d.ts +1 -1
- package/dist/build/helper.js +9 -2
- 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 +10 -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 +17 -14
- 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/incrementalCache/multi-tier-ddb-s3.js +1 -1
- package/dist/overrides/proxyExternalRequest/fetch.js +2 -1
- 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 +10 -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 +73 -7
- 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
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { loadFunctionsConfigManifest, loadMiddlewareManifest, } from "../adapters/config/util.js";
|
|
3
4
|
import logger from "../logger.js";
|
|
4
5
|
import { buildEdgeBundle } from "./edge/createEdgeBundle.js";
|
|
5
6
|
import * as buildHelper from "./helper.js";
|
|
6
7
|
import { installDependencies } from "./installDeps.js";
|
|
8
|
+
import { buildBundledNodeMiddleware, buildExternalNodeMiddleware, } from "./middleware/buildNodeMiddleware.js";
|
|
7
9
|
/**
|
|
8
10
|
* Compiles the middleware bundle.
|
|
9
11
|
*
|
|
@@ -12,10 +14,21 @@ import { installDependencies } from "./installDeps.js";
|
|
|
12
14
|
*/
|
|
13
15
|
export async function createMiddleware(options, { forceOnlyBuildOnce = false } = {}) {
|
|
14
16
|
logger.info("Bundling middleware function...");
|
|
15
|
-
const {
|
|
17
|
+
const { config, outputDir } = options;
|
|
18
|
+
const buildOutputDotNextDir = path.join(options.appBuildOutputPath, ".next");
|
|
16
19
|
// Get middleware manifest
|
|
17
|
-
const middlewareManifest =
|
|
18
|
-
const
|
|
20
|
+
const middlewareManifest = loadMiddlewareManifest(buildOutputDotNextDir);
|
|
21
|
+
const edgeMiddlewareInfo = middlewareManifest.middleware["/"];
|
|
22
|
+
if (!edgeMiddlewareInfo) {
|
|
23
|
+
// If there is no middleware info, it might be a node middleware
|
|
24
|
+
const functionsConfigManifest = loadFunctionsConfigManifest(buildOutputDotNextDir);
|
|
25
|
+
if (functionsConfigManifest?.functions["/_middleware"]) {
|
|
26
|
+
await (config.middleware?.external
|
|
27
|
+
? buildExternalNodeMiddleware(options)
|
|
28
|
+
: buildBundledNodeMiddleware(options));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
if (config.middleware?.external) {
|
|
20
33
|
const outputPath = path.join(outputDir, "middleware");
|
|
21
34
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
@@ -25,7 +38,7 @@ export async function createMiddleware(options, { forceOnlyBuildOnce = false } =
|
|
|
25
38
|
await buildEdgeBundle({
|
|
26
39
|
entrypoint: path.join(options.openNextDistDir, "adapters", "middleware.js"),
|
|
27
40
|
outfile: path.join(outputPath, "handler.mjs"),
|
|
28
|
-
middlewareInfo,
|
|
41
|
+
middlewareInfo: edgeMiddlewareInfo,
|
|
29
42
|
options,
|
|
30
43
|
overrides: {
|
|
31
44
|
...config.middleware.override,
|
|
@@ -43,7 +56,7 @@ export async function createMiddleware(options, { forceOnlyBuildOnce = false } =
|
|
|
43
56
|
await buildEdgeBundle({
|
|
44
57
|
entrypoint: path.join(options.openNextDistDir, "core", "edgeFunctionHandler.js"),
|
|
45
58
|
outfile: path.join(options.buildDir, "middleware.mjs"),
|
|
46
|
-
middlewareInfo,
|
|
59
|
+
middlewareInfo: edgeMiddlewareInfo,
|
|
47
60
|
options,
|
|
48
61
|
onlyBuildOnce: true,
|
|
49
62
|
name: "middleware",
|
|
@@ -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
|
},
|
|
@@ -167,7 +165,7 @@ export async function generateOutput(options) {
|
|
|
167
165
|
const patterns = "patterns" in value ? value.patterns : ["*"];
|
|
168
166
|
patterns.forEach((pattern) => {
|
|
169
167
|
behaviors.push({
|
|
170
|
-
pattern: prefixer(pattern.replace(/BUILD_ID/, getBuildId(
|
|
168
|
+
pattern: prefixer(pattern.replace(/BUILD_ID/, getBuildId(options))),
|
|
171
169
|
origin: value.placement === "global" ? undefined : key,
|
|
172
170
|
edgeFunction: value.placement === "global"
|
|
173
171
|
? key
|
package/dist/build/helper.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare function traverseFiles(root: string, conditionFn: (paths: Travers
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function removeFiles(root: string, conditionFn: (paths: TraversePath) => boolean): void;
|
|
50
50
|
export declare function getHtmlPages(dotNextPath: string): Set<string>;
|
|
51
|
-
export declare function getBuildId(
|
|
51
|
+
export declare function getBuildId(options: BuildOptions): string;
|
|
52
52
|
export declare function getOpenNextVersion(): string;
|
|
53
53
|
export declare function getNextVersion(appPath: string): string;
|
|
54
54
|
/**
|
package/dist/build/helper.js
CHANGED
|
@@ -101,12 +101,15 @@ export function esbuildSync(esbuildOptions, options) {
|
|
|
101
101
|
}
|
|
102
102
|
export async function esbuildAsync(esbuildOptions, options) {
|
|
103
103
|
const { openNextVersion, debug, minify } = options;
|
|
104
|
+
// Dump ESBuild build metadata to file in debug mode
|
|
105
|
+
const metafile = debug && esbuildOptions.outfile !== undefined;
|
|
104
106
|
const result = await buildAsync({
|
|
105
107
|
target: "esnext",
|
|
106
108
|
format: "esm",
|
|
107
109
|
platform: "node",
|
|
108
110
|
bundle: true,
|
|
109
111
|
minify,
|
|
112
|
+
metafile,
|
|
110
113
|
mainFields: ["module", "main"],
|
|
111
114
|
sourcemap: debug ? "inline" : false,
|
|
112
115
|
sourcesContent: false,
|
|
@@ -129,6 +132,10 @@ export async function esbuildAsync(esbuildOptions, options) {
|
|
|
129
132
|
result.errors.forEach((error) => logger.error(error));
|
|
130
133
|
throw new Error(`There was a problem bundling ${esbuildOptions.entryPoints[0]}.`);
|
|
131
134
|
}
|
|
135
|
+
if (result.metafile) {
|
|
136
|
+
const metaFile = `${esbuildOptions.outfile}.meta.json`;
|
|
137
|
+
fs.writeFileSync(metaFile, JSON.stringify(result.metafile, null, 2));
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
/**
|
|
134
141
|
* Recursively traverse files in a directory and call `callbackFn` when `conditionFn` returns true
|
|
@@ -180,9 +187,9 @@ export function getHtmlPages(dotNextPath) {
|
|
|
180
187
|
.map(([_, value]) => value.replace(/^pages\//, ""))
|
|
181
188
|
.reduce((acc, page) => acc.add(page), new Set());
|
|
182
189
|
}
|
|
183
|
-
export function getBuildId(
|
|
190
|
+
export function getBuildId(options) {
|
|
184
191
|
return fs
|
|
185
|
-
.readFileSync(path.join(
|
|
192
|
+
.readFileSync(path.join(options.appBuildOutputPath, ".next/BUILD_ID"), "utf-8")
|
|
186
193
|
.trim();
|
|
187
194
|
}
|
|
188
195
|
export function getOpenNextVersion() {
|
|
@@ -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) {
|
|
@@ -120,16 +121,19 @@ export async function cacheInterceptor(event) {
|
|
|
120
121
|
try {
|
|
121
122
|
const cachedData = await globalThis.incrementalCache.get(localizedPath);
|
|
122
123
|
debug("cached data in interceptor", cachedData);
|
|
124
|
+
if (!cachedData?.value) {
|
|
125
|
+
return event;
|
|
126
|
+
}
|
|
127
|
+
// We need to check the tag cache now
|
|
123
128
|
if (cachedData.value?.type === "app") {
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
// If some tags are stale we need to force revalidation
|
|
129
|
+
const tags = getTagsFromValue(cachedData.value);
|
|
130
|
+
const _hasBeenRevalidated = await hasBeenRevalidated(localizedPath, tags, cachedData);
|
|
131
|
+
if (_hasBeenRevalidated) {
|
|
128
132
|
return event;
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
const host = event.headers.host;
|
|
132
|
-
switch (cachedData
|
|
136
|
+
switch (cachedData?.value?.type) {
|
|
133
137
|
case "app":
|
|
134
138
|
case "page":
|
|
135
139
|
return generateResult(event, localizedPath, cachedData.value, cachedData.lastModified);
|
|
@@ -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
|
}
|