@opennextjs/aws 3.3.1 → 3.4.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.
- package/dist/adapters/cache.js +23 -3
- package/dist/adapters/logger.js +2 -2
- package/dist/build/createAssets.js +1 -1
- package/dist/build/createImageOptimizationBundle.js +2 -1
- package/dist/build/createServerBundle.js +8 -9
- package/dist/build/edge/createEdgeBundle.js +2 -1
- package/dist/build/helper.d.ts +10 -1
- package/dist/build/helper.js +19 -3
- package/dist/build/validateConfig.js +2 -1
- package/dist/core/createMainHandler.js +2 -1
- package/dist/core/edgeFunctionHandler.js +2 -1
- package/dist/core/requestHandler.js +9 -2
- package/dist/core/resolve.d.ts +4 -0
- package/dist/core/resolve.js +10 -0
- package/dist/core/routing/middleware.js +3 -1
- package/dist/core/routing/util.d.ts +3 -2
- package/dist/core/routing/util.js +18 -1
- package/dist/overrides/cdnInvalidation/cloudfront.d.ts +9 -0
- package/dist/overrides/cdnInvalidation/cloudfront.js +26 -0
- package/dist/overrides/cdnInvalidation/dummy.d.ts +9 -0
- package/dist/overrides/cdnInvalidation/dummy.js +6 -0
- package/dist/overrides/imageLoader/fs-dev.d.ts +10 -0
- package/dist/overrides/imageLoader/fs-dev.js +14 -0
- package/dist/overrides/incrementalCache/fs-dev.d.ts +3 -0
- package/dist/overrides/incrementalCache/fs-dev.js +30 -0
- package/dist/overrides/incrementalCache/multi-tier-ddb-s3.d.ts +10 -0
- package/dist/overrides/incrementalCache/multi-tier-ddb-s3.js +120 -0
- package/dist/overrides/incrementalCache/s3-lite.d.ts +2 -0
- package/dist/overrides/incrementalCache/s3-lite.js +1 -1
- package/dist/overrides/proxyExternalRequest/node.js +6 -5
- package/dist/overrides/queue/direct.d.ts +3 -0
- package/dist/overrides/queue/direct.js +18 -0
- package/dist/overrides/tagCache/dynamodb-lite.js +8 -4
- package/dist/overrides/tagCache/dynamodb.js +8 -4
- package/dist/overrides/tagCache/fs-dev.d.ts +3 -0
- package/dist/overrides/tagCache/fs-dev.js +32 -0
- package/dist/overrides/wrappers/cloudflare-edge.js +11 -4
- package/dist/overrides/wrappers/dummy.d.ts +1 -1
- package/dist/overrides/wrappers/dummy.js +6 -2
- package/dist/overrides/wrappers/express-dev.d.ts +7 -0
- package/dist/overrides/wrappers/express-dev.js +43 -0
- package/dist/plugins/edge.js +4 -3
- package/dist/plugins/replacement.d.ts +1 -1
- package/dist/plugins/replacement.js +1 -1
- package/dist/plugins/resolve.d.ts +1 -0
- package/dist/plugins/resolve.js +4 -1
- package/dist/types/global.d.ts +19 -12
- package/dist/types/open-next.d.ts +12 -6
- package/dist/types/overrides.d.ts +10 -1
- package/dist/utils/lru.d.ts +8 -0
- package/dist/utils/lru.js +29 -0
- package/dist/utils/regex.d.ts +18 -0
- package/dist/utils/regex.js +16 -0
- package/package.json +4 -1
package/dist/adapters/cache.js
CHANGED
|
@@ -16,7 +16,7 @@ export default class Cache {
|
|
|
16
16
|
async get(key,
|
|
17
17
|
// fetchCache is for next 13.5 and above, kindHint is for next 14 and above and boolean is for earlier versions
|
|
18
18
|
options) {
|
|
19
|
-
if (globalThis.disableIncrementalCache) {
|
|
19
|
+
if (globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
22
22
|
const softTags = typeof options === "object" ? options.softTags : [];
|
|
@@ -130,7 +130,7 @@ export default class Cache {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
async set(key, data, ctx) {
|
|
133
|
-
if (globalThis.disableIncrementalCache) {
|
|
133
|
+
if (globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
|
|
134
134
|
return;
|
|
135
135
|
}
|
|
136
136
|
// This one might not even be necessary anymore
|
|
@@ -242,7 +242,8 @@ export default class Cache {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
async revalidateTag(tags) {
|
|
245
|
-
|
|
245
|
+
const config = globalThis.openNextConfig.dangerous;
|
|
246
|
+
if (config?.disableTagCache || config?.disableIncrementalCache) {
|
|
246
247
|
return;
|
|
247
248
|
}
|
|
248
249
|
try {
|
|
@@ -275,6 +276,25 @@ export default class Cache {
|
|
|
275
276
|
}
|
|
276
277
|
// Update all keys with the given tag with revalidatedAt set to now
|
|
277
278
|
await globalThis.tagCache.writeTags(toInsert);
|
|
279
|
+
// We can now invalidate all paths in the CDN
|
|
280
|
+
// This only applies to `revalidateTag`, not to `res.revalidate()`
|
|
281
|
+
const uniquePaths = Array.from(new Set(toInsert
|
|
282
|
+
// We need to filter fetch cache key as they are not in the CDN
|
|
283
|
+
.filter((t) => t.tag.startsWith("_N_T_/"))
|
|
284
|
+
.map((t) => `/${t.path}`)));
|
|
285
|
+
if (uniquePaths.length > 0) {
|
|
286
|
+
await globalThis.cdnInvalidationHandler.invalidatePaths(uniquePaths.map((path) => ({
|
|
287
|
+
initialPath: path,
|
|
288
|
+
rawPath: path,
|
|
289
|
+
resolvedRoutes: [
|
|
290
|
+
{
|
|
291
|
+
route: path,
|
|
292
|
+
// TODO: ideally here we should check if it's an app router page or route
|
|
293
|
+
type: "app",
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
})));
|
|
297
|
+
}
|
|
278
298
|
}
|
|
279
299
|
}
|
|
280
300
|
catch (e) {
|
package/dist/adapters/logger.js
CHANGED
|
@@ -21,9 +21,9 @@ const isDownplayedErrorLog = (errorLog) => DOWNPLAYED_ERROR_LOGS.some((downplaye
|
|
|
21
21
|
export function error(...args) {
|
|
22
22
|
// we try to catch errors from the aws-sdk client and downplay some of them
|
|
23
23
|
if (args.some((arg) => isDownplayedErrorLog(arg))) {
|
|
24
|
-
debug(...args);
|
|
24
|
+
return debug(...args);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
if (args.some((arg) => isOpenNextError(arg))) {
|
|
27
27
|
// In case of an internal error, we log it with the appropriate log level
|
|
28
28
|
const error = args.find((arg) => isOpenNextError(arg));
|
|
29
29
|
if (error.logLevel < getOpenNextErrorLogLevel()) {
|
|
@@ -44,7 +44,7 @@ export function createStaticAssets(options) {
|
|
|
44
44
|
export function createCacheAssets(options) {
|
|
45
45
|
logger.info("Bundling cache assets...");
|
|
46
46
|
const { appBuildOutputPath, outputDir } = options;
|
|
47
|
-
const packagePath =
|
|
47
|
+
const packagePath = buildHelper.getPackagePath(options);
|
|
48
48
|
const buildId = buildHelper.getBuildId(appBuildOutputPath);
|
|
49
49
|
let useTagCache = false;
|
|
50
50
|
// Copy pages to cache folder
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import logger from "../logger.js";
|
|
5
5
|
import { openNextReplacementPlugin } from "../plugins/replacement.js";
|
|
6
6
|
import { openNextResolvePlugin } from "../plugins/resolve.js";
|
|
7
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
7
8
|
import * as buildHelper from "./helper.js";
|
|
8
9
|
import { installDependencies } from "./installDeps.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
@@ -28,7 +29,7 @@ export async function createImageOptimizationBundle(options) {
|
|
|
28
29
|
if (buildHelper.compareSemver(options.nextVersion, "14.1.1") >= 0) {
|
|
29
30
|
plugins.push(openNextReplacementPlugin({
|
|
30
31
|
name: "opennext-14.1.1-image-optimization",
|
|
31
|
-
target: /
|
|
32
|
+
target: getCrossPlatformPathRegex("plugins/image-optimization/image-optimization.js"),
|
|
32
33
|
replacements: [
|
|
33
34
|
require.resolve("../adapters/plugins/image-optimization/image-optimization.replacement.js"),
|
|
34
35
|
],
|
|
@@ -4,6 +4,7 @@ import logger from "../logger.js";
|
|
|
4
4
|
import { minifyAll } from "../minimize-js.js";
|
|
5
5
|
import { openNextReplacementPlugin } from "../plugins/replacement.js";
|
|
6
6
|
import { openNextResolvePlugin } from "../plugins/resolve.js";
|
|
7
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
7
8
|
import { bundleNextServer } from "./bundleNextServer.js";
|
|
8
9
|
import { compileCache } from "./compileCache.js";
|
|
9
10
|
import { copyTracedFiles } from "./copyTracedFiles.js";
|
|
@@ -35,10 +36,9 @@ export async function createServerBundle(options) {
|
|
|
35
36
|
// We build every other function than default before so we know which route there is left
|
|
36
37
|
await Promise.all(promises);
|
|
37
38
|
const remainingRoutes = new Set();
|
|
38
|
-
const { appBuildOutputPath
|
|
39
|
-
const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
|
|
39
|
+
const { appBuildOutputPath } = options;
|
|
40
40
|
// Find remaining routes
|
|
41
|
-
const serverPath = path.join(appBuildOutputPath, ".next
|
|
41
|
+
const serverPath = path.join(appBuildOutputPath, ".next/standalone", buildHelper.getPackagePath(options), ".next/server");
|
|
42
42
|
// Find app dir routes
|
|
43
43
|
if (fs.existsSync(path.join(serverPath, "app"))) {
|
|
44
44
|
const appPath = path.join(serverPath, "app");
|
|
@@ -77,7 +77,7 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
77
77
|
// `node_modules` inside `.next/standalone`, and others inside
|
|
78
78
|
// `.next/standalone/package/path` (ie. `.next`, `server.js`).
|
|
79
79
|
// We need to output the handler file inside the package path.
|
|
80
|
-
const packagePath =
|
|
80
|
+
const packagePath = buildHelper.getPackagePath(options);
|
|
81
81
|
fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true });
|
|
82
82
|
const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs";
|
|
83
83
|
fs.copyFileSync(path.join(options.buildDir, `cache.${ext}`), path.join(outputPath, packagePath, "cache.cjs"));
|
|
@@ -109,12 +109,12 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
109
109
|
buildHelper.compareSemver(options.nextVersion, "13.4.1") <= 0;
|
|
110
110
|
const overrides = fnOptions.override ?? {};
|
|
111
111
|
const isBefore13413 = buildHelper.compareSemver(options.nextVersion, "13.4.13") <= 0;
|
|
112
|
-
const isAfter141 = buildHelper.compareSemver(options.nextVersion, "14.
|
|
112
|
+
const isAfter141 = buildHelper.compareSemver(options.nextVersion, "14.1") >= 0;
|
|
113
113
|
const disableRouting = isBefore13413 || config.middleware?.external;
|
|
114
114
|
const plugins = [
|
|
115
115
|
openNextReplacementPlugin({
|
|
116
116
|
name: `requestHandlerOverride ${name}`,
|
|
117
|
-
target: /
|
|
117
|
+
target: getCrossPlatformPathRegex("core/requestHandler.js"),
|
|
118
118
|
deletes: [
|
|
119
119
|
...(disableNextPrebundledReact ? ["applyNextjsPrebundledReact"] : []),
|
|
120
120
|
...(disableRouting ? ["withRouting"] : []),
|
|
@@ -122,11 +122,10 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
122
122
|
}),
|
|
123
123
|
openNextReplacementPlugin({
|
|
124
124
|
name: `utilOverride ${name}`,
|
|
125
|
-
target: /
|
|
125
|
+
target: getCrossPlatformPathRegex("core/util.js"),
|
|
126
126
|
deletes: [
|
|
127
127
|
...(disableNextPrebundledReact ? ["requireHooks"] : []),
|
|
128
|
-
...(
|
|
129
|
-
...(!isBefore13413 ? ["requestHandlerHost"] : []),
|
|
128
|
+
...(isBefore13413 ? ["trustHostHeader"] : ["requestHandlerHost"]),
|
|
130
129
|
...(isAfter141
|
|
131
130
|
? ["experimentalIncrementalCacheHandler"]
|
|
132
131
|
: ["stableIncrementalCache"]),
|
|
@@ -6,6 +6,7 @@ import logger from "../../logger.js";
|
|
|
6
6
|
import { openNextEdgePlugins } from "../../plugins/edge.js";
|
|
7
7
|
import { openNextReplacementPlugin } from "../../plugins/replacement.js";
|
|
8
8
|
import { openNextResolvePlugin } from "../../plugins/resolve.js";
|
|
9
|
+
import { getCrossPlatformPathRegex } from "../../utils/regex.js";
|
|
9
10
|
import { isEdgeRuntime } from "../helper.js";
|
|
10
11
|
import { copyOpenNextConfig, esbuildAsync } from "../helper.js";
|
|
11
12
|
export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, }) {
|
|
@@ -41,7 +42,7 @@ export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, opt
|
|
|
41
42
|
}),
|
|
42
43
|
openNextReplacementPlugin({
|
|
43
44
|
name: "externalMiddlewareOverrides",
|
|
44
|
-
target: /
|
|
45
|
+
target: getCrossPlatformPathRegex("adapters/middleware.js"),
|
|
45
46
|
deletes: includeCache ? [] : ["includeCacheInMiddleware"],
|
|
46
47
|
}),
|
|
47
48
|
openNextEdgePlugins({
|
package/dist/build/helper.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare function normalizeOptions(config: OpenNextConfig, distDir: string
|
|
|
15
15
|
openNextVersion: string;
|
|
16
16
|
openNextDistDir: string;
|
|
17
17
|
outputDir: string;
|
|
18
|
-
packager: "
|
|
18
|
+
packager: "bun" | "npm" | "yarn" | "pnpm";
|
|
19
19
|
tempBuildDir: string;
|
|
20
20
|
};
|
|
21
21
|
export declare function esbuildSync(esbuildOptions: ESBuildOptions, options: BuildOptions): void;
|
|
@@ -51,6 +51,14 @@ export declare function getHtmlPages(dotNextPath: string): Set<string>;
|
|
|
51
51
|
export declare function getBuildId(dotNextPath: string): string;
|
|
52
52
|
export declare function getOpenNextVersion(): string;
|
|
53
53
|
export declare function getNextVersion(appPath: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Compare two semver versions.
|
|
56
|
+
*
|
|
57
|
+
* @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
|
|
58
|
+
* @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
|
|
59
|
+
* @example
|
|
60
|
+
* compareSemver("1.0.0", "1.0.0") // 0
|
|
61
|
+
*/
|
|
54
62
|
export declare function compareSemver(v1: string, v2: string): number;
|
|
55
63
|
export declare function copyOpenNextConfig(inputDir: string, outputDir: string, isEdge?: boolean): void;
|
|
56
64
|
export declare function copyEnvFile(appPath: string, packagePath: string, outputPath: string): void;
|
|
@@ -73,3 +81,4 @@ export declare function initOutputDir(options: BuildOptions): void;
|
|
|
73
81
|
* @returns Whether the edge runtime is used
|
|
74
82
|
*/
|
|
75
83
|
export declare function isEdgeRuntime(overrides: DefaultOverrideOptions | undefined): Promise<boolean | undefined>;
|
|
84
|
+
export declare function getPackagePath(options: BuildOptions): string;
|
package/dist/build/helper.js
CHANGED
|
@@ -45,10 +45,12 @@ function findMonorepoRoot(appPath) {
|
|
|
45
45
|
let currentPath = appPath;
|
|
46
46
|
while (currentPath !== "/") {
|
|
47
47
|
const found = [
|
|
48
|
+
// bun can generate yaml lock files (`bun install --yarn`) so bun should be before yarn
|
|
49
|
+
{ file: "bun.lockb", packager: "bun" },
|
|
50
|
+
{ file: "bun.lock", packager: "bun" },
|
|
48
51
|
{ file: "package-lock.json", packager: "npm" },
|
|
49
52
|
{ file: "yarn.lock", packager: "yarn" },
|
|
50
53
|
{ file: "pnpm-lock.yaml", packager: "pnpm" },
|
|
51
|
-
{ file: "bun.lockb", packager: "bun" },
|
|
52
54
|
].find((f) => fs.existsSync(path.join(currentPath, f.file)));
|
|
53
55
|
if (found) {
|
|
54
56
|
if (currentPath !== appPath) {
|
|
@@ -198,6 +200,14 @@ export function getNextVersion(appPath) {
|
|
|
198
200
|
// Drop the -canary.n suffix
|
|
199
201
|
return version.split("-")[0];
|
|
200
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Compare two semver versions.
|
|
205
|
+
*
|
|
206
|
+
* @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
|
|
207
|
+
* @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
|
|
208
|
+
* @example
|
|
209
|
+
* compareSemver("1.0.0", "1.0.0") // 0
|
|
210
|
+
*/
|
|
201
211
|
export function compareSemver(v1, v2) {
|
|
202
212
|
if (v1 === "latest")
|
|
203
213
|
return 1;
|
|
@@ -209,8 +219,11 @@ export function compareSemver(v1, v2) {
|
|
|
209
219
|
// biome-ignore lint/style/noParameterAssign:
|
|
210
220
|
v2 = v2.substring(1);
|
|
211
221
|
}
|
|
212
|
-
const [major1, minor1, patch1] = v1.split(".").map(Number);
|
|
213
|
-
const [major2, minor2, patch2] = v2.split(".").map(Number);
|
|
222
|
+
const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
|
|
223
|
+
const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
|
|
224
|
+
if (Number.isNaN(major1) || Number.isNaN(major2)) {
|
|
225
|
+
throw new Error("The major version is required.");
|
|
226
|
+
}
|
|
214
227
|
if (major1 !== major2)
|
|
215
228
|
return major1 - major2;
|
|
216
229
|
if (minor1 !== minor2)
|
|
@@ -276,3 +289,6 @@ export async function isEdgeRuntime(overrides) {
|
|
|
276
289
|
}
|
|
277
290
|
return (await overrides?.wrapper?.())?.edgeRuntime;
|
|
278
291
|
}
|
|
292
|
+
export function getPackagePath(options) {
|
|
293
|
+
return path.relative(options.monorepoRoot, options.appBuildOutputPath);
|
|
294
|
+
}
|
|
@@ -11,7 +11,8 @@ const compatibilityMatrix = {
|
|
|
11
11
|
"cloudflare-edge": ["edge"],
|
|
12
12
|
"cloudflare-node": ["edge"],
|
|
13
13
|
node: ["node"],
|
|
14
|
-
|
|
14
|
+
"express-dev": ["node"],
|
|
15
|
+
dummy: ["dummy"],
|
|
15
16
|
};
|
|
16
17
|
function validateFunctionOptions(fnOptions) {
|
|
17
18
|
if (fnOptions.runtime === "edge" && fnOptions.experimentalBundledNextServer) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { debug } from "../adapters/logger";
|
|
2
2
|
import { generateUniqueId } from "../adapters/util";
|
|
3
3
|
import { openNextHandler } from "./requestHandler";
|
|
4
|
-
import { resolveConverter, resolveIncrementalCache, resolveProxyRequest, resolveQueue, resolveTagCache, resolveWrapper, } from "./resolve";
|
|
4
|
+
import { resolveCdnInvalidation, resolveConverter, resolveIncrementalCache, resolveProxyRequest, resolveQueue, resolveTagCache, resolveWrapper, } from "./resolve";
|
|
5
5
|
export async function createMainHandler() {
|
|
6
6
|
// @ts-expect-error `./open-next.config.mjs` exists only in the build output
|
|
7
7
|
const config = await import("./open-next.config.mjs").then((m) => m.default);
|
|
@@ -15,6 +15,7 @@ export async function createMainHandler() {
|
|
|
15
15
|
globalThis.incrementalCache = await resolveIncrementalCache(thisFunction.override?.incrementalCache);
|
|
16
16
|
globalThis.tagCache = await resolveTagCache(thisFunction.override?.tagCache);
|
|
17
17
|
globalThis.proxyExternalRequest = await resolveProxyRequest(thisFunction.override?.proxyExternalRequest);
|
|
18
|
+
globalThis.cdnInvalidationHandler = await resolveCdnInvalidation(thisFunction.override?.cdnInvalidation);
|
|
18
19
|
globalThis.lastModified = {};
|
|
19
20
|
// From the config, we create the converter
|
|
20
21
|
const converter = await resolveConverter(thisFunction.override?.converter);
|
|
@@ -6,7 +6,8 @@ export default async function edgeFunctionHandler(request) {
|
|
|
6
6
|
if (!correspondingRoute) {
|
|
7
7
|
throw new Error(`No route found for ${request.url}`);
|
|
8
8
|
}
|
|
9
|
-
const
|
|
9
|
+
const entry = await self._ENTRIES[`middleware_${correspondingRoute.name}`];
|
|
10
|
+
const result = await entry.default({
|
|
10
11
|
page: correspondingRoute.page,
|
|
11
12
|
request: {
|
|
12
13
|
...request,
|
|
@@ -82,7 +82,14 @@ export async function openNextHandler(internalEvent, responseStreaming) {
|
|
|
82
82
|
if ("type" in routingResult) {
|
|
83
83
|
// response is used only in the streaming case
|
|
84
84
|
if (responseStreaming) {
|
|
85
|
-
const response = createServerResponse(
|
|
85
|
+
const response = createServerResponse({
|
|
86
|
+
internalEvent,
|
|
87
|
+
isExternalRewrite: false,
|
|
88
|
+
isISR: false,
|
|
89
|
+
resolvedRoutes: [],
|
|
90
|
+
origin: false,
|
|
91
|
+
initialPath: internalEvent.rawPath,
|
|
92
|
+
}, headers, responseStreaming);
|
|
86
93
|
response.statusCode = routingResult.statusCode;
|
|
87
94
|
response.flushHeaders();
|
|
88
95
|
const [bodyToConsume, bodyToReturn] = routingResult.body.tee();
|
|
@@ -117,7 +124,7 @@ export async function openNextHandler(internalEvent, responseStreaming) {
|
|
|
117
124
|
store.mergeHeadersPriority = mergeHeadersPriority;
|
|
118
125
|
}
|
|
119
126
|
const req = new IncomingMessage(reqProps);
|
|
120
|
-
const res = createServerResponse(
|
|
127
|
+
const res = createServerResponse(routingResult, overwrittenResponseHeaders, responseStreaming);
|
|
121
128
|
await processRequest(req, res, preprocessedEvent);
|
|
122
129
|
const { statusCode, headers: responseHeaders, isBase64Encoded, body, } = convertRes(res);
|
|
123
130
|
const internalResult = {
|
package/dist/core/resolve.d.ts
CHANGED
|
@@ -43,4 +43,8 @@ export declare function resolveWarmerInvoke(warmer: RemoveUndefined<OpenNextConf
|
|
|
43
43
|
* @__PURE__
|
|
44
44
|
*/
|
|
45
45
|
export declare function resolveProxyRequest(proxyRequest: OverrideOptions["proxyExternalRequest"]): Promise<import("../types/overrides").ProxyExternalRequest>;
|
|
46
|
+
/**
|
|
47
|
+
* @__PURE__
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveCdnInvalidation(cdnInvalidation: OverrideOptions["cdnInvalidation"]): Promise<import("../types/overrides").CDNInvalidationHandler>;
|
|
46
50
|
export {};
|
package/dist/core/resolve.js
CHANGED
|
@@ -98,3 +98,13 @@ export async function resolveProxyRequest(proxyRequest) {
|
|
|
98
98
|
const m_1 = await import("../overrides/proxyExternalRequest/node.js");
|
|
99
99
|
return m_1.default;
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* @__PURE__
|
|
103
|
+
*/
|
|
104
|
+
export async function resolveCdnInvalidation(cdnInvalidation) {
|
|
105
|
+
if (typeof cdnInvalidation === "function") {
|
|
106
|
+
return cdnInvalidation();
|
|
107
|
+
}
|
|
108
|
+
const m_1 = await import("../overrides/cdnInvalidation/dummy.js");
|
|
109
|
+
return m_1.default;
|
|
110
|
+
}
|
|
@@ -44,7 +44,9 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
44
44
|
const result = await middleware.default({
|
|
45
45
|
// `geo` is pre Next 15.
|
|
46
46
|
geo: {
|
|
47
|
-
city
|
|
47
|
+
// The city name is percent-encoded.
|
|
48
|
+
// See https://github.com/vercel/vercel/blob/4cb6143/packages/functions/src/headers.ts#L94C19-L94C37
|
|
49
|
+
city: decodeURIComponent(headers["x-open-next-city"]),
|
|
48
50
|
country: headers["x-open-next-country"],
|
|
49
51
|
region: headers["x-open-next-region"],
|
|
50
52
|
latitude: headers["x-open-next-latitude"],
|
|
@@ -2,7 +2,7 @@ import type { OutgoingHttpHeaders } from "node:http";
|
|
|
2
2
|
import type { IncomingMessage } from "../../http/index.js";
|
|
3
3
|
import { OpenNextNodeResponse } from "../../http/openNextResponse.js";
|
|
4
4
|
import type { MiddlewareManifest } from "../../types/next-types";
|
|
5
|
-
import type { InternalEvent, InternalResult, StreamCreator } from "../../types/open-next.js";
|
|
5
|
+
import type { InternalEvent, InternalResult, RoutingResult, StreamCreator } from "../../types/open-next.js";
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
8
8
|
* @__PURE__
|
|
@@ -90,4 +90,5 @@ export declare function fixISRHeaders(headers: OutgoingHttpHeaders): void;
|
|
|
90
90
|
* @returns
|
|
91
91
|
* @__PURE__
|
|
92
92
|
*/
|
|
93
|
-
export declare function createServerResponse(
|
|
93
|
+
export declare function createServerResponse(routingResult: RoutingResult, headers: Record<string, string | string[] | undefined>, responseStream?: StreamCreator): OpenNextNodeResponse;
|
|
94
|
+
export declare function invalidateCDNOnRequest(params: RoutingResult, headers: OutgoingHttpHeaders): Promise<void>;
|
|
@@ -352,7 +352,8 @@ export function fixISRHeaders(headers) {
|
|
|
352
352
|
* @returns
|
|
353
353
|
* @__PURE__
|
|
354
354
|
*/
|
|
355
|
-
export function createServerResponse(
|
|
355
|
+
export function createServerResponse(routingResult, headers, responseStream) {
|
|
356
|
+
const internalEvent = routingResult.internalEvent;
|
|
356
357
|
return new OpenNextNodeResponse((_headers) => {
|
|
357
358
|
fixCacheHeaderForHtmlPages(internalEvent, _headers);
|
|
358
359
|
fixSWRCacheHeader(_headers);
|
|
@@ -360,5 +361,21 @@ export function createServerResponse(internalEvent, headers, responseStream) {
|
|
|
360
361
|
fixISRHeaders(_headers);
|
|
361
362
|
}, async (_headers) => {
|
|
362
363
|
await revalidateIfRequired(internalEvent.headers.host, internalEvent.rawPath, _headers);
|
|
364
|
+
await invalidateCDNOnRequest(routingResult, _headers);
|
|
363
365
|
}, responseStream, headers);
|
|
364
366
|
}
|
|
367
|
+
// This function is used only for `res.revalidate()`
|
|
368
|
+
export async function invalidateCDNOnRequest(params, headers) {
|
|
369
|
+
const { internalEvent, initialPath, resolvedRoutes } = params;
|
|
370
|
+
const isIsrRevalidation = internalEvent.headers["x-isr"] === "1";
|
|
371
|
+
if (!isIsrRevalidation &&
|
|
372
|
+
headers[CommonHeaders.NEXT_CACHE] === "REVALIDATED") {
|
|
373
|
+
await globalThis.cdnInvalidationHandler.invalidatePaths([
|
|
374
|
+
{
|
|
375
|
+
initialPath,
|
|
376
|
+
rawPath: internalEvent.rawPath,
|
|
377
|
+
resolvedRoutes,
|
|
378
|
+
},
|
|
379
|
+
]);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CloudFrontClient, CreateInvalidationCommand, } from "@aws-sdk/client-cloudfront";
|
|
2
|
+
const cloudfront = new CloudFrontClient({});
|
|
3
|
+
export default {
|
|
4
|
+
name: "cloudfront",
|
|
5
|
+
invalidatePaths: async (paths) => {
|
|
6
|
+
const constructedPaths = paths.flatMap(({ initialPath, resolvedRoutes }) => {
|
|
7
|
+
const isAppRouter = resolvedRoutes.some((route) => route.type === "app");
|
|
8
|
+
// revalidateTag doesn't have any leading slash, remove it just to be sure
|
|
9
|
+
const path = initialPath.replace(/^\//, "");
|
|
10
|
+
return isAppRouter
|
|
11
|
+
? [`/${path}`, `/${path}?_rsc=*`]
|
|
12
|
+
: [
|
|
13
|
+
`/${path}`,
|
|
14
|
+
`/_next/data/${process.env.NEXT_BUILD_ID}${path === "/" ? "/index" : `/${path}`}.json*`,
|
|
15
|
+
];
|
|
16
|
+
});
|
|
17
|
+
await cloudfront.send(new CreateInvalidationCommand({
|
|
18
|
+
DistributionId: process.env.CLOUDFRONT_DISTRIBUTION_ID,
|
|
19
|
+
InvalidationBatch: {
|
|
20
|
+
// Do we need to limit the number of paths? Or batch them into multiple commands?
|
|
21
|
+
Paths: { Quantity: constructedPaths.length, Items: constructedPaths },
|
|
22
|
+
CallerReference: `${Date.now()}`,
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
export default {
|
|
3
|
+
name: "fs-dev",
|
|
4
|
+
load: async (url) => {
|
|
5
|
+
const basePath = "../../assets";
|
|
6
|
+
const body = fs.createReadStream(`${basePath}/${url}`);
|
|
7
|
+
const contentType = url.endsWith(".png") ? "image/png" : "image/jpeg";
|
|
8
|
+
return {
|
|
9
|
+
body,
|
|
10
|
+
contentType,
|
|
11
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const buildId = process.env.NEXT_BUILD_ID;
|
|
4
|
+
const basePath = path.resolve(process.cwd(), `../../cache/${buildId}`);
|
|
5
|
+
const getCacheKey = (key) => {
|
|
6
|
+
return path.join(basePath, `${key}.cache`);
|
|
7
|
+
};
|
|
8
|
+
const cache = {
|
|
9
|
+
name: "fs-dev",
|
|
10
|
+
get: async (key) => {
|
|
11
|
+
const fileData = await fs.readFile(getCacheKey(key), "utf-8");
|
|
12
|
+
const data = JSON.parse(fileData);
|
|
13
|
+
const { mtime } = await fs.stat(getCacheKey(key));
|
|
14
|
+
return {
|
|
15
|
+
value: data,
|
|
16
|
+
lastModified: mtime.getTime(),
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
set: async (key, value, isFetch) => {
|
|
20
|
+
const data = JSON.stringify(value);
|
|
21
|
+
const cacheKey = getCacheKey(key);
|
|
22
|
+
// We need to create the directory before writing the file
|
|
23
|
+
await fs.mkdir(path.dirname(cacheKey), { recursive: true });
|
|
24
|
+
await fs.writeFile(cacheKey, data);
|
|
25
|
+
},
|
|
26
|
+
delete: async (key) => {
|
|
27
|
+
await fs.rm(getCacheKey(key));
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default cache;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IncrementalCache } from "../../types/overrides";
|
|
2
|
+
/**
|
|
3
|
+
* This cache implementation uses a multi-tier cache with a local cache, a DynamoDB metadata cache and an S3 cache.
|
|
4
|
+
* It uses the same DynamoDB table as the default tag cache and the same S3 bucket as the default incremental cache.
|
|
5
|
+
* It will first check the local cache.
|
|
6
|
+
* If the local cache is expired, it will check the DynamoDB metadata cache to see if the local cache is still valid.
|
|
7
|
+
* Lastly it will check the S3 cache.
|
|
8
|
+
*/
|
|
9
|
+
declare const multiTierCache: IncrementalCache;
|
|
10
|
+
export default multiTierCache;
|