@opennextjs/aws 3.7.4 → 3.7.6
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/build/copyTracedFiles.js +5 -1
- package/dist/build/createAssets.js +13 -7
- package/dist/build/createImageOptimizationBundle.js +2 -1
- package/dist/build/createServerBundle.js +1 -0
- package/dist/build/installDeps.js +3 -2
- package/dist/build/patch/patches/index.d.ts +1 -0
- package/dist/build/patch/patches/index.js +1 -0
- package/dist/build/patch/patches/patchNextServer.d.ts +0 -4
- package/dist/build/patch/patches/patchNextServer.js +0 -16
- package/dist/build/patch/patches/patchNodeEnvironment.d.ts +8 -0
- package/dist/build/patch/patches/patchNodeEnvironment.js +27 -0
- package/dist/build.js +1 -1
- package/dist/core/routing/cacheInterceptor.js +18 -1
- package/dist/http/openNextResponse.js +6 -0
- package/dist/overrides/tagCache/fs-dev-nextMode.d.ts +8 -0
- package/dist/overrides/tagCache/fs-dev-nextMode.js +42 -0
- package/dist/overrides/tagCache/fs-dev.js +14 -10
- package/dist/overrides/wrappers/cloudflare-node.js +5 -1
- package/dist/overrides/wrappers/express-dev.js +8 -0
- package/dist/overrides/wrappers/node.js +8 -1
- package/dist/types/open-next.d.ts +2 -1
- package/dist/utils/safe-json-parse.d.ts +1 -0
- package/dist/utils/safe-json-parse.js +10 -0
- package/package.json +1 -1
|
@@ -13,6 +13,10 @@ const EXCLUDED_PACKAGES = [
|
|
|
13
13
|
// This seems to be only in Next 15
|
|
14
14
|
// Some of sharp deps are under the @img scope
|
|
15
15
|
"@img",
|
|
16
|
+
"typescript",
|
|
17
|
+
"next/dist/compiled/babel",
|
|
18
|
+
"next/dist/compiled/babel-packages",
|
|
19
|
+
"next/dist/compiled/amphtml-validator",
|
|
16
20
|
];
|
|
17
21
|
function isExcluded(srcPath) {
|
|
18
22
|
return EXCLUDED_PACKAGES.some((excluded) => srcPath.match(getCrossPlatformPathRegex(`/node_modules/${excluded}/`)));
|
|
@@ -167,7 +171,7 @@ File ${serverPath} does not exist
|
|
|
167
171
|
const tracedFiles = [];
|
|
168
172
|
//Actually copy the files
|
|
169
173
|
filesToCopy.forEach((to, from) => {
|
|
170
|
-
// We don't want to copy excluded packages (
|
|
174
|
+
// We don't want to copy excluded packages (e.g. sharp)
|
|
171
175
|
if (isExcluded(from)) {
|
|
172
176
|
return;
|
|
173
177
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { loadConfig } from "../adapters/config/util.js";
|
|
4
|
+
import { safeParseJsonFile } from "../utils/safe-json-parse.js";
|
|
4
5
|
import logger from "../logger.js";
|
|
5
6
|
import { isBinaryContentType } from "../utils/binary.js";
|
|
6
7
|
import * as buildHelper from "./helper.js";
|
|
@@ -106,15 +107,20 @@ export function createCacheAssets(options) {
|
|
|
106
107
|
// Generate cache file
|
|
107
108
|
Object.entries(cacheFilesPath).forEach(([cacheFilePath, files]) => {
|
|
108
109
|
const cacheFileMeta = files.meta
|
|
109
|
-
?
|
|
110
|
+
? safeParseJsonFile(fs.readFileSync(files.meta, "utf8"), cacheFilePath)
|
|
110
111
|
: undefined;
|
|
112
|
+
const cacheJson = files.json
|
|
113
|
+
? safeParseJsonFile(fs.readFileSync(files.json, "utf8"), cacheFilePath)
|
|
114
|
+
: undefined;
|
|
115
|
+
if ((files.meta && !cacheFileMeta) || (files.json && !cacheJson)) {
|
|
116
|
+
logger.warn(`Skipping invalid cache file: ${cacheFilePath}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
111
119
|
const cacheFileContent = {
|
|
112
120
|
type: files.body ? "route" : files.json ? "page" : "app",
|
|
113
121
|
meta: cacheFileMeta,
|
|
114
122
|
html: files.html ? fs.readFileSync(files.html, "utf8") : undefined,
|
|
115
|
-
json:
|
|
116
|
-
? JSON.parse(fs.readFileSync(files.json, "utf8"))
|
|
117
|
-
: undefined,
|
|
123
|
+
json: cacheJson,
|
|
118
124
|
rsc: files.rsc ? fs.readFileSync(files.rsc, "utf8") : undefined,
|
|
119
125
|
body: files.body
|
|
120
126
|
? fs
|
|
@@ -138,7 +144,7 @@ export function createCacheAssets(options) {
|
|
|
138
144
|
fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
|
|
139
145
|
buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
|
|
140
146
|
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
141
|
-
const fileData =
|
|
147
|
+
const fileData = safeParseJsonFile(fileContent, absolutePath);
|
|
142
148
|
fileData?.tags?.forEach((tag) => {
|
|
143
149
|
metaFiles.push({
|
|
144
150
|
tag: { S: path.posix.join(buildId, tag) },
|
|
@@ -156,8 +162,8 @@ export function createCacheAssets(options) {
|
|
|
156
162
|
sourceDirs.forEach((sourceDir) => {
|
|
157
163
|
buildHelper.traverseFiles(sourceDir, ({ absolutePath, relativePath }) => absolutePath.endsWith(".meta") && !isFileSkipped(relativePath), ({ absolutePath, relativePath }) => {
|
|
158
164
|
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
159
|
-
const fileData =
|
|
160
|
-
if (fileData
|
|
165
|
+
const fileData = safeParseJsonFile(fileContent, absolutePath);
|
|
166
|
+
if (fileData?.headers?.["x-next-cache-tags"]) {
|
|
161
167
|
fileData.headers["x-next-cache-tags"]
|
|
162
168
|
.split(",")
|
|
163
169
|
.forEach((tag) => {
|
|
@@ -76,7 +76,8 @@ export async function createImageOptimizationBundle(options) {
|
|
|
76
76
|
const sharpVersion = process.env.SHARP_VERSION ?? "0.32.6";
|
|
77
77
|
installDependencies(outputPath, config.imageOptimization?.install ?? {
|
|
78
78
|
packages: [`sharp@${sharpVersion}`],
|
|
79
|
-
arch
|
|
79
|
+
// By not specifying an arch, `npm install` will choose one for us (i.e. our system one)
|
|
80
|
+
arch: config.imageOptimization?.loader === "fs-dev" ? undefined : "arm64",
|
|
80
81
|
nodeVersion: "18",
|
|
81
82
|
libc: "glibc",
|
|
82
83
|
});
|
|
@@ -126,6 +126,7 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
126
126
|
patches.patchEnvVars,
|
|
127
127
|
patches.patchBackgroundRevalidation,
|
|
128
128
|
patches.patchUseCacheForISR,
|
|
129
|
+
patches.patchNodeEnvironment,
|
|
129
130
|
...additionalCodePatches,
|
|
130
131
|
]);
|
|
131
132
|
// Build Lambda code
|
|
@@ -3,6 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
import logger from "../logger.js";
|
|
6
|
+
const AFFECTED_NODE_VERSIONS = ["22.17.0", "22.17.1", "22.18.0"];
|
|
6
7
|
export function installDependencies(outputDir, installOptions) {
|
|
7
8
|
try {
|
|
8
9
|
if (!installOptions) {
|
|
@@ -37,8 +38,8 @@ export function installDependencies(outputDir, installOptions) {
|
|
|
37
38
|
fs.cpSync(path.join(tempInstallDir, "node_modules"), path.join(outputDir, "node_modules"), { recursive: true, force: true, dereference: true });
|
|
38
39
|
// This is a workaround for Node `22.17.0` and `22.17.1`
|
|
39
40
|
// https://github.com/nodejs/node/issues/59168
|
|
40
|
-
const nodeVersion = process.
|
|
41
|
-
if (
|
|
41
|
+
const nodeVersion = process.versions.node;
|
|
42
|
+
if (AFFECTED_NODE_VERSIONS.includes(nodeVersion)) {
|
|
42
43
|
const tempBinDir = path.join(tempInstallDir, "node_modules", ".bin");
|
|
43
44
|
const outputBinDir = path.join(outputDir, "node_modules", ".bin");
|
|
44
45
|
for (const fileName of fs.readdirSync(tempBinDir)) {
|
|
@@ -3,3 +3,4 @@ export { patchNextServer } from "./patchNextServer.js";
|
|
|
3
3
|
export { patchFetchCacheForISR, patchUnstableCacheForISR, patchUseCacheForISR, } from "./patchFetchCacheISR.js";
|
|
4
4
|
export { patchFetchCacheSetMissingWaitUntil } from "./patchFetchCacheWaitUntil.js";
|
|
5
5
|
export { patchBackgroundRevalidation } from "./patchBackgroundRevalidation.js";
|
|
6
|
+
export { patchNodeEnvironment } from "./patchNodeEnvironment.js";
|
|
@@ -3,3 +3,4 @@ export { patchNextServer } from "./patchNextServer.js";
|
|
|
3
3
|
export { patchFetchCacheForISR, patchUnstableCacheForISR, patchUseCacheForISR, } from "./patchFetchCacheISR.js";
|
|
4
4
|
export { patchFetchCacheSetMissingWaitUntil } from "./patchFetchCacheWaitUntil.js";
|
|
5
5
|
export { patchBackgroundRevalidation } from "./patchBackgroundRevalidation.js";
|
|
6
|
+
export { patchNodeEnvironment } from "./patchNodeEnvironment.js";
|
|
@@ -8,8 +8,4 @@ export declare const removeMiddlewareManifestRule = "\nrule:\n kind: statement_
|
|
|
8
8
|
* @returns A rule to replace the body with a `throw`
|
|
9
9
|
*/
|
|
10
10
|
export declare function createEmptyBodyRule(methodName: string): string;
|
|
11
|
-
/**
|
|
12
|
-
* Drops `require("./node-environment-extensions/error-inspect");`
|
|
13
|
-
*/
|
|
14
|
-
export declare const errorInspectRule = "\nrule:\n pattern: require(\"./node-environment-extensions/error-inspect\");\nfix: |-\n // Removed by OpenNext\n // require(\"./node-environment-extensions/error-inspect\");\n";
|
|
15
11
|
export declare const patchNextServer: CodePatcher;
|
|
@@ -48,16 +48,6 @@ fix: |-
|
|
|
48
48
|
}
|
|
49
49
|
`;
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Drops `require("./node-environment-extensions/error-inspect");`
|
|
53
|
-
*/
|
|
54
|
-
export const errorInspectRule = `
|
|
55
|
-
rule:
|
|
56
|
-
pattern: require("./node-environment-extensions/error-inspect");
|
|
57
|
-
fix: |-
|
|
58
|
-
// Removed by OpenNext
|
|
59
|
-
// require("./node-environment-extensions/error-inspect");
|
|
60
|
-
`;
|
|
61
51
|
const pathFilter = getCrossPlatformPathRegex(String.raw `/next/dist/server/next-server\.js$`, {
|
|
62
52
|
escape: false,
|
|
63
53
|
});
|
|
@@ -81,12 +71,6 @@ const babelPatches = [
|
|
|
81
71
|
contentFilter: /runEdgeFunction\(/,
|
|
82
72
|
patchCode: createPatchCode(createEmptyBodyRule("runEdgeFunction")),
|
|
83
73
|
},
|
|
84
|
-
// Drop `error-inspect` that pulls babel
|
|
85
|
-
{
|
|
86
|
-
pathFilter,
|
|
87
|
-
contentFilter: /error-inspect/,
|
|
88
|
-
patchCode: createPatchCode(errorInspectRule),
|
|
89
|
-
},
|
|
90
74
|
];
|
|
91
75
|
export const patchNextServer = {
|
|
92
76
|
name: "patch-next-server",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CodePatcher } from "../codePatcher.js";
|
|
2
|
+
/**
|
|
3
|
+
* Drops `require("./node-environment-extensions/error-inspect");`
|
|
4
|
+
*
|
|
5
|
+
* This is to avoid pulling babel (~4MB)
|
|
6
|
+
*/
|
|
7
|
+
export declare const rule = "\nrule:\n pattern: require(\"./node-environment-extensions/error-inspect\");\nfix: |-\n // Removed by OpenNext\n // require(\"./node-environment-extensions/error-inspect\");\n";
|
|
8
|
+
export declare const patchNodeEnvironment: CodePatcher;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getCrossPlatformPathRegex } from "../../../utils/regex.js";
|
|
2
|
+
import { createPatchCode } from "../astCodePatcher.js";
|
|
3
|
+
/**
|
|
4
|
+
* Drops `require("./node-environment-extensions/error-inspect");`
|
|
5
|
+
*
|
|
6
|
+
* This is to avoid pulling babel (~4MB)
|
|
7
|
+
*/
|
|
8
|
+
export const rule = `
|
|
9
|
+
rule:
|
|
10
|
+
pattern: require("./node-environment-extensions/error-inspect");
|
|
11
|
+
fix: |-
|
|
12
|
+
// Removed by OpenNext
|
|
13
|
+
// require("./node-environment-extensions/error-inspect");
|
|
14
|
+
`;
|
|
15
|
+
export const patchNodeEnvironment = {
|
|
16
|
+
name: "patch-node-environment-error-inspect",
|
|
17
|
+
patches: [
|
|
18
|
+
{
|
|
19
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `/next/dist/server/node-environment\.js$`, {
|
|
20
|
+
escape: false,
|
|
21
|
+
}),
|
|
22
|
+
contentFilter: /error-inspect/,
|
|
23
|
+
patchCode: createPatchCode(rule),
|
|
24
|
+
versions: ">=15.0.0",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
package/dist/build.js
CHANGED
|
@@ -28,10 +28,10 @@ export async function build(openNextConfigPath, nodeExternals) {
|
|
|
28
28
|
// Build Next.js app
|
|
29
29
|
printHeader("Building Next.js app");
|
|
30
30
|
setStandaloneBuildMode(options);
|
|
31
|
+
buildHelper.initOutputDir(options);
|
|
31
32
|
buildNextjsApp(options);
|
|
32
33
|
// Generate deployable bundle
|
|
33
34
|
printHeader("Generating bundle");
|
|
34
|
-
buildHelper.initOutputDir(options);
|
|
35
35
|
// Compile cache.ts
|
|
36
36
|
compileCache(options);
|
|
37
37
|
// Compile middleware
|
|
@@ -1,6 +1,7 @@
|
|
|
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 { isBinaryContentType } from "../../utils/binary";
|
|
4
5
|
import { getTagsFromValue, hasBeenRevalidated } from "../../utils/cache";
|
|
5
6
|
import { debug } from "../../adapters/logger";
|
|
6
7
|
import { localizePath } from "./i18n";
|
|
@@ -174,7 +175,8 @@ export async function cacheInterceptor(event) {
|
|
|
174
175
|
return event;
|
|
175
176
|
}
|
|
176
177
|
// We need to check the tag cache now
|
|
177
|
-
if (cachedData.value?.type === "app"
|
|
178
|
+
if (cachedData.value?.type === "app" ||
|
|
179
|
+
cachedData.value?.type === "route") {
|
|
178
180
|
const tags = getTagsFromValue(cachedData.value);
|
|
179
181
|
const _hasBeenRevalidated = await hasBeenRevalidated(localizedPath, tags, cachedData);
|
|
180
182
|
if (_hasBeenRevalidated) {
|
|
@@ -200,6 +202,21 @@ export async function cacheInterceptor(event) {
|
|
|
200
202
|
isBase64Encoded: false,
|
|
201
203
|
};
|
|
202
204
|
}
|
|
205
|
+
case "route": {
|
|
206
|
+
const cacheControl = await computeCacheControl(localizedPath, cachedData.value.body, host, cachedData.value.revalidate, cachedData.lastModified);
|
|
207
|
+
const isBinary = isBinaryContentType(String(cachedData.value.meta?.headers?.["content-type"]));
|
|
208
|
+
return {
|
|
209
|
+
type: "core",
|
|
210
|
+
statusCode: cachedData.value.meta?.status ?? 200,
|
|
211
|
+
body: toReadableStream(cachedData.value.body, isBinary),
|
|
212
|
+
headers: {
|
|
213
|
+
...cacheControl,
|
|
214
|
+
...cachedData.value.meta?.headers,
|
|
215
|
+
vary: VARY_HEADER,
|
|
216
|
+
},
|
|
217
|
+
isBase64Encoded: isBinary,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
203
220
|
default:
|
|
204
221
|
return event;
|
|
205
222
|
}
|
|
@@ -65,6 +65,12 @@ export class OpenNextNodeResponse extends Transform {
|
|
|
65
65
|
statusCode <= 599) {
|
|
66
66
|
this.statusCode = statusCode;
|
|
67
67
|
}
|
|
68
|
+
// https://github.com/vercel/next.js/blob/ea08bf2/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L46-L54
|
|
69
|
+
// We want to destroy this response when the original response/request is closed. (i.e when the client disconnects)
|
|
70
|
+
// This is to support `request.signal.onabort` in route handlers
|
|
71
|
+
streamCreator?.abortSignal?.addEventListener("abort", () => {
|
|
72
|
+
this.destroy();
|
|
73
|
+
});
|
|
68
74
|
}
|
|
69
75
|
// Necessary for next 12
|
|
70
76
|
// We might have to implement all the methods here
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
name: string;
|
|
3
|
+
mode: "nextMode";
|
|
4
|
+
getLastRevalidated: (tags: string[]) => Promise<number>;
|
|
5
|
+
hasBeenRevalidated: (tags: string[], lastModified?: number) => Promise<boolean>;
|
|
6
|
+
writeTags: (tags: string[]) => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { debug } from "../../adapters/logger";
|
|
2
|
+
const tagsMap = new Map();
|
|
3
|
+
export default {
|
|
4
|
+
name: "fs-dev-nextMode",
|
|
5
|
+
mode: "nextMode",
|
|
6
|
+
getLastRevalidated: async (tags) => {
|
|
7
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
let lastRevalidated = 0;
|
|
11
|
+
tags.forEach((tag) => {
|
|
12
|
+
const tagTime = tagsMap.get(tag);
|
|
13
|
+
if (tagTime && tagTime > lastRevalidated) {
|
|
14
|
+
lastRevalidated = tagTime;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
debug("getLastRevalidated result:", lastRevalidated);
|
|
18
|
+
return lastRevalidated;
|
|
19
|
+
},
|
|
20
|
+
hasBeenRevalidated: async (tags, lastModified) => {
|
|
21
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const hasRevalidatedTag = tags.some((tag) => {
|
|
25
|
+
const tagRevalidatedAt = tagsMap.get(tag);
|
|
26
|
+
return tagRevalidatedAt ? tagRevalidatedAt > (lastModified ?? 0) : false;
|
|
27
|
+
});
|
|
28
|
+
debug("hasBeenRevalidated result:", hasRevalidatedTag);
|
|
29
|
+
return hasRevalidatedTag;
|
|
30
|
+
},
|
|
31
|
+
writeTags: async (tags) => {
|
|
32
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache ||
|
|
33
|
+
tags.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
debug("writeTags", { tags: tags });
|
|
37
|
+
tags.forEach((tag) => {
|
|
38
|
+
tagsMap.set(tag, Date.now());
|
|
39
|
+
});
|
|
40
|
+
debug("writeTags completed, written", tags.length, "tags");
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -4,31 +4,35 @@ import { getMonorepoRelativePath } from "../../utils/normalize-path";
|
|
|
4
4
|
const tagFile = path.join(getMonorepoRelativePath(), "dynamodb-provider/dynamodb-cache.json");
|
|
5
5
|
const tagContent = fs.readFileSync(tagFile, "utf-8");
|
|
6
6
|
let tags = JSON.parse(tagContent);
|
|
7
|
+
const { NEXT_BUILD_ID } = process.env;
|
|
8
|
+
function buildKey(key) {
|
|
9
|
+
return path.posix.join(NEXT_BUILD_ID ?? "", key);
|
|
10
|
+
}
|
|
7
11
|
const tagCache = {
|
|
8
12
|
name: "fs-dev",
|
|
9
13
|
mode: "original",
|
|
10
14
|
getByPath: async (path) => {
|
|
11
15
|
return tags
|
|
12
|
-
.filter((tagPathMapping) => tagPathMapping.path.S === path)
|
|
13
|
-
.map((tag) => tag.tag.S);
|
|
16
|
+
.filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path))
|
|
17
|
+
.map((tag) => tag.tag.S.replace(`${NEXT_BUILD_ID}/`, ""));
|
|
14
18
|
},
|
|
15
19
|
getByTag: async (tag) => {
|
|
16
20
|
return tags
|
|
17
|
-
.filter((tagPathMapping) => tagPathMapping.tag.S === tag)
|
|
18
|
-
.map((
|
|
21
|
+
.filter((tagPathMapping) => tagPathMapping.tag.S === buildKey(tag))
|
|
22
|
+
.map((tagEntry) => tagEntry.path.S.replace(`${NEXT_BUILD_ID}/`, ""));
|
|
19
23
|
},
|
|
20
24
|
getLastModified: async (path, lastModified) => {
|
|
21
|
-
const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === path &&
|
|
25
|
+
const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path) &&
|
|
22
26
|
Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0));
|
|
23
27
|
return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now());
|
|
24
28
|
},
|
|
25
29
|
writeTags: async (newTags) => {
|
|
26
|
-
const newTagsSet = new Set(newTags.map(({ tag, path }) => `${tag}-${path}`));
|
|
30
|
+
const newTagsSet = new Set(newTags.map(({ tag, path }) => `${buildKey(tag)}-${buildKey(path)}`));
|
|
27
31
|
const unchangedTags = tags.filter(({ tag, path }) => !newTagsSet.has(`${tag.S}-${path.S}`));
|
|
28
|
-
tags = unchangedTags.concat(newTags.map((
|
|
29
|
-
tag: { S:
|
|
30
|
-
path: { S:
|
|
31
|
-
revalidatedAt: { N:
|
|
32
|
+
tags = unchangedTags.concat(newTags.map((item) => ({
|
|
33
|
+
tag: { S: buildKey(item.tag) },
|
|
34
|
+
path: { S: buildKey(item.path) },
|
|
35
|
+
revalidatedAt: { N: `${item.revalidatedAt ?? Date.now()}` },
|
|
32
36
|
})));
|
|
33
37
|
},
|
|
34
38
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Writable } from "node:stream";
|
|
2
2
|
// Response with null body status (101, 204, 205, or 304) cannot have a body.
|
|
3
3
|
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
|
4
|
-
const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
4
|
+
const handler = async (handler, converter) => async (request, env, ctx, abortSignal) => {
|
|
5
5
|
globalThis.process = process;
|
|
6
6
|
// Set the environment variables
|
|
7
7
|
// Cloudflare suggests to not override the process.env object but instead apply the values to it
|
|
@@ -38,6 +38,10 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
38
38
|
resolveResponse(response);
|
|
39
39
|
return Writable.fromWeb(writable);
|
|
40
40
|
},
|
|
41
|
+
// This is for passing along the original abort signal from the initial Request you retrieve in your worker
|
|
42
|
+
// Ensures that the response we pass to NextServer is aborted if the request is aborted
|
|
43
|
+
// By doing this `request.signal.onabort` will work in route handlers
|
|
44
|
+
abortSignal: abortSignal,
|
|
41
45
|
};
|
|
42
46
|
ctx.waitUntil(handler(internalEvent, {
|
|
43
47
|
streamCreator,
|
|
@@ -4,6 +4,9 @@ import { NextConfig } from "../../adapters/config/index";
|
|
|
4
4
|
import { getMonorepoRelativePath } from "../../utils/normalize-path";
|
|
5
5
|
const wrapper = async (handler, converter) => {
|
|
6
6
|
const app = express();
|
|
7
|
+
// We disable this cause we wanna use it ourself
|
|
8
|
+
// https://stackoverflow.com/a/13055495/16587222
|
|
9
|
+
app.disable("x-powered-by");
|
|
7
10
|
// To serve static assets
|
|
8
11
|
const basePath = NextConfig.basePath ?? "";
|
|
9
12
|
app.use(basePath, express.static(path.join(getMonorepoRelativePath(), "assets")));
|
|
@@ -27,6 +30,7 @@ const wrapper = async (handler, converter) => {
|
|
|
27
30
|
req.headers["x-forwarded-proto"] = req.protocol;
|
|
28
31
|
}
|
|
29
32
|
const internalEvent = await converter.convertFrom(req);
|
|
33
|
+
const abortController = new AbortController();
|
|
30
34
|
const streamCreator = {
|
|
31
35
|
writeHeaders: (prelude) => {
|
|
32
36
|
res.setHeader("Set-Cookie", prelude.cookies);
|
|
@@ -35,7 +39,11 @@ const wrapper = async (handler, converter) => {
|
|
|
35
39
|
return res;
|
|
36
40
|
},
|
|
37
41
|
onFinish: () => { },
|
|
42
|
+
abortSignal: abortController.signal,
|
|
38
43
|
};
|
|
44
|
+
res.on("close", () => {
|
|
45
|
+
abortController.abort();
|
|
46
|
+
});
|
|
39
47
|
await handler(internalEvent, { streamCreator });
|
|
40
48
|
});
|
|
41
49
|
const server = app.listen(Number.parseInt(process.env.PORT ?? "3000", 10), () => {
|
|
@@ -3,6 +3,7 @@ import { debug, error } from "../../adapters/logger";
|
|
|
3
3
|
const wrapper = async (handler, converter) => {
|
|
4
4
|
const server = createServer(async (req, res) => {
|
|
5
5
|
const internalEvent = await converter.convertFrom(req);
|
|
6
|
+
const abortController = new AbortController();
|
|
6
7
|
const streamCreator = {
|
|
7
8
|
writeHeaders: (prelude) => {
|
|
8
9
|
res.setHeader("Set-Cookie", prelude.cookies);
|
|
@@ -10,7 +11,11 @@ const wrapper = async (handler, converter) => {
|
|
|
10
11
|
res.flushHeaders();
|
|
11
12
|
return res;
|
|
12
13
|
},
|
|
14
|
+
abortSignal: abortController.signal,
|
|
13
15
|
};
|
|
16
|
+
res.on("close", () => {
|
|
17
|
+
abortController.abort();
|
|
18
|
+
});
|
|
14
19
|
if (internalEvent.rawPath === "/__health") {
|
|
15
20
|
res.writeHead(200, {
|
|
16
21
|
"Content-Type": "text/plain",
|
|
@@ -18,7 +23,9 @@ const wrapper = async (handler, converter) => {
|
|
|
18
23
|
res.end("OK");
|
|
19
24
|
}
|
|
20
25
|
else {
|
|
21
|
-
await handler(internalEvent, {
|
|
26
|
+
await handler(internalEvent, {
|
|
27
|
+
streamCreator,
|
|
28
|
+
});
|
|
22
29
|
}
|
|
23
30
|
});
|
|
24
31
|
await new Promise((resolve) => {
|
|
@@ -30,6 +30,7 @@ export interface StreamCreator {
|
|
|
30
30
|
}): Writable;
|
|
31
31
|
onWrite?: () => void;
|
|
32
32
|
onFinish?: (length: number) => void;
|
|
33
|
+
abortSignal?: AbortSignal;
|
|
33
34
|
}
|
|
34
35
|
export type WaitUntil = (promise: Promise<void>) => void;
|
|
35
36
|
export interface DangerousOptions {
|
|
@@ -99,7 +100,7 @@ export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"midd
|
|
|
99
100
|
}
|
|
100
101
|
export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";
|
|
101
102
|
export type IncludedIncrementalCache = "s3" | "s3-lite" | "multi-tier-ddb-s3" | "fs-dev" | "dummy";
|
|
102
|
-
export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dynamodb-nextMode" | "fs-dev" | "dummy";
|
|
103
|
+
export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dynamodb-nextMode" | "fs-dev" | "fs-dev-nextMode" | "dummy";
|
|
103
104
|
export type IncludedImageLoader = "s3" | "s3-lite" | "host" | "fs-dev" | "dummy";
|
|
104
105
|
export type IncludedOriginResolver = "pattern-env" | "dummy";
|
|
105
106
|
export type IncludedWarmer = "aws-lambda" | "dummy";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function safeParseJsonFile<T = any>(input: string, filePath: string, fallback?: T): T | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import logger from "../logger.js";
|
|
2
|
+
export function safeParseJsonFile(input, filePath, fallback) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(input);
|
|
5
|
+
}
|
|
6
|
+
catch (err) {
|
|
7
|
+
logger.warn(`Failed to parse JSON file "${filePath}". Error: ${err.message}`);
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
}
|