@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
package/dist/adapters/cache.d.ts
CHANGED
|
@@ -1,63 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
kind: "FETCH";
|
|
3
|
-
data: {
|
|
4
|
-
headers: {
|
|
5
|
-
[k: string]: string;
|
|
6
|
-
};
|
|
7
|
-
body: string;
|
|
8
|
-
url: string;
|
|
9
|
-
status?: number;
|
|
10
|
-
tags?: string[];
|
|
11
|
-
};
|
|
12
|
-
revalidate: number;
|
|
13
|
-
}
|
|
14
|
-
interface CachedRedirectValue {
|
|
15
|
-
kind: "REDIRECT";
|
|
16
|
-
props: Object;
|
|
17
|
-
}
|
|
18
|
-
interface CachedRouteValue {
|
|
19
|
-
kind: "ROUTE" | "APP_ROUTE";
|
|
20
|
-
body: Buffer;
|
|
21
|
-
status: number;
|
|
22
|
-
headers: Record<string, undefined | string | string[]>;
|
|
23
|
-
}
|
|
24
|
-
interface CachedImageValue {
|
|
25
|
-
kind: "IMAGE";
|
|
26
|
-
etag: string;
|
|
27
|
-
buffer: Buffer;
|
|
28
|
-
extension: string;
|
|
29
|
-
isMiss?: boolean;
|
|
30
|
-
isStale?: boolean;
|
|
31
|
-
}
|
|
32
|
-
interface IncrementalCachedPageValue {
|
|
33
|
-
kind: "PAGE" | "PAGES";
|
|
34
|
-
html: string;
|
|
35
|
-
pageData: Object;
|
|
36
|
-
status?: number;
|
|
37
|
-
headers?: Record<string, undefined | string>;
|
|
38
|
-
}
|
|
39
|
-
interface IncrementalCachedAppPageValue {
|
|
40
|
-
kind: "APP_PAGE";
|
|
41
|
-
html: string;
|
|
42
|
-
rscData: Buffer;
|
|
43
|
-
headers?: Record<string, undefined | string | string[]>;
|
|
44
|
-
postponed?: string;
|
|
45
|
-
status?: number;
|
|
46
|
-
}
|
|
47
|
-
type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
|
|
48
|
-
type IncrementalCacheContext = {
|
|
49
|
-
revalidate?: number | false | undefined;
|
|
50
|
-
fetchCache?: boolean | undefined;
|
|
51
|
-
fetchUrl?: string | undefined;
|
|
52
|
-
fetchIdx?: number | undefined;
|
|
53
|
-
tags?: string[] | undefined;
|
|
54
|
-
};
|
|
55
|
-
interface CacheHandlerValue {
|
|
56
|
-
lastModified?: number;
|
|
57
|
-
age?: number;
|
|
58
|
-
cacheState?: string;
|
|
59
|
-
value: IncrementalCacheValue | null;
|
|
60
|
-
}
|
|
1
|
+
import type { CacheHandlerValue, IncrementalCacheContext, IncrementalCacheValue } from "../types/cache";
|
|
61
2
|
export default class Cache {
|
|
62
3
|
get(key: string, options?: boolean | {
|
|
63
4
|
fetchCache?: boolean;
|
|
@@ -70,5 +11,5 @@ export default class Cache {
|
|
|
70
11
|
getIncrementalCache(key: string): Promise<CacheHandlerValue | null>;
|
|
71
12
|
set(key: string, data?: IncrementalCacheValue, ctx?: IncrementalCacheContext): Promise<void>;
|
|
72
13
|
revalidateTag(tags: string | string[]): Promise<void>;
|
|
14
|
+
private updateTagsOnSet;
|
|
73
15
|
}
|
|
74
|
-
export {};
|
package/dist/adapters/cache.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getTagsFromValue, hasBeenRevalidated } from "../utils/cache";
|
|
1
2
|
import { isBinaryContentType } from "../utils/binary";
|
|
2
3
|
import { debug, error, warn } from "./logger";
|
|
3
4
|
function isFetchCache(options) {
|
|
@@ -31,11 +32,11 @@ export default class Cache {
|
|
|
31
32
|
const cachedEntry = await globalThis.incrementalCache.get(key, true);
|
|
32
33
|
if (cachedEntry?.value === undefined)
|
|
33
34
|
return null;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const _tags = [...(tags ?? []), ...(softTags ?? [])];
|
|
36
|
+
const _lastModified = cachedEntry.lastModified ?? Date.now();
|
|
37
|
+
const _hasBeenRevalidated = await hasBeenRevalidated(key, _tags, cachedEntry);
|
|
38
|
+
if (_hasBeenRevalidated)
|
|
37
39
|
return null;
|
|
38
|
-
}
|
|
39
40
|
// For cases where we don't have tags, we need to ensure that the soft tags are not being revalidated
|
|
40
41
|
// We only need to check for the path as it should already contain all the tags
|
|
41
42
|
if ((tags ?? []).length === 0) {
|
|
@@ -44,8 +45,8 @@ export default class Cache {
|
|
|
44
45
|
!tag.endsWith("layout") &&
|
|
45
46
|
!tag.endsWith("page"));
|
|
46
47
|
if (path) {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
48
|
+
const hasPathBeenUpdated = await hasBeenRevalidated(path.replace("_N_T_/", ""), [], cachedEntry);
|
|
49
|
+
if (hasPathBeenUpdated) {
|
|
49
50
|
// In case the path has been revalidated, we don't want to use the fetch cache
|
|
50
51
|
return null;
|
|
51
52
|
}
|
|
@@ -68,15 +69,17 @@ export default class Cache {
|
|
|
68
69
|
if (!cachedEntry?.value) {
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const cacheData = cachedEntry.value;
|
|
73
|
+
const meta = cacheData.meta;
|
|
74
|
+
const tags = getTagsFromValue(cacheData);
|
|
75
|
+
const _lastModified = cachedEntry.lastModified ?? Date.now();
|
|
76
|
+
const _hasBeenRevalidated = await hasBeenRevalidated(key, tags, cachedEntry);
|
|
77
|
+
if (_hasBeenRevalidated)
|
|
75
78
|
return null;
|
|
79
|
+
const store = globalThis.__openNextAls.getStore();
|
|
80
|
+
if (store) {
|
|
81
|
+
store.lastModified = _lastModified;
|
|
76
82
|
}
|
|
77
|
-
const cacheData = cachedEntry?.value;
|
|
78
|
-
const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
|
|
79
|
-
globalThis.lastModified[requestId] = _lastModified;
|
|
80
83
|
if (cacheData?.type === "route") {
|
|
81
84
|
return {
|
|
82
85
|
lastModified: _lastModified,
|
|
@@ -214,27 +217,7 @@ export default class Cache {
|
|
|
214
217
|
break;
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
|
-
|
|
218
|
-
// If we use an in house version of getDerivedTags in build we should use it here instead of next's one
|
|
219
|
-
const derivedTags = data?.kind === "FETCH"
|
|
220
|
-
? (ctx?.tags ?? data?.data?.tags ?? []) // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
|
|
221
|
-
: data?.kind === "PAGE"
|
|
222
|
-
? (data.headers?.["x-next-cache-tags"]?.split(",") ?? [])
|
|
223
|
-
: [];
|
|
224
|
-
debug("derivedTags", derivedTags);
|
|
225
|
-
// Get all tags stored in dynamodb for the given key
|
|
226
|
-
// If any of the derived tags are not stored in dynamodb for the given key, write them
|
|
227
|
-
const storedTags = await globalThis.tagCache.getByPath(key);
|
|
228
|
-
const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
|
|
229
|
-
if (tagsToWrite.length > 0) {
|
|
230
|
-
await globalThis.tagCache.writeTags(tagsToWrite.map((tag) => ({
|
|
231
|
-
path: key,
|
|
232
|
-
tag: tag,
|
|
233
|
-
// In case the tags are not there we just need to create them
|
|
234
|
-
// but we don't want them to return from `getLastModified` as they are not stale
|
|
235
|
-
revalidatedAt: 1,
|
|
236
|
-
})));
|
|
237
|
-
}
|
|
220
|
+
await this.updateTagsOnSet(key, data, ctx);
|
|
238
221
|
debug("Finished setting cache");
|
|
239
222
|
}
|
|
240
223
|
catch (e) {
|
|
@@ -252,6 +235,26 @@ export default class Cache {
|
|
|
252
235
|
}
|
|
253
236
|
try {
|
|
254
237
|
const _tags = Array.isArray(tags) ? tags : [tags];
|
|
238
|
+
if (globalThis.tagCache.mode === "nextMode") {
|
|
239
|
+
const paths = (await globalThis.tagCache.getPathsByTags?.(_tags)) ?? [];
|
|
240
|
+
await globalThis.tagCache.writeTags(_tags);
|
|
241
|
+
if (paths.length > 0) {
|
|
242
|
+
// TODO: we should introduce a new method in cdnInvalidationHandler to invalidate paths by tags for cdn that supports it
|
|
243
|
+
// It also means that we'll need to provide the tags used in every request to the wrapper or converter.
|
|
244
|
+
await globalThis.cdnInvalidationHandler.invalidatePaths(paths.map((path) => ({
|
|
245
|
+
initialPath: path,
|
|
246
|
+
rawPath: path,
|
|
247
|
+
resolvedRoutes: [
|
|
248
|
+
{
|
|
249
|
+
route: path,
|
|
250
|
+
// TODO: ideally here we should check if it's an app router page or route
|
|
251
|
+
type: "app",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
})));
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
255
258
|
for (const tag of _tags) {
|
|
256
259
|
debug("revalidateTag", tag);
|
|
257
260
|
// Find all keys with the given tag
|
|
@@ -305,4 +308,35 @@ export default class Cache {
|
|
|
305
308
|
error("Failed to revalidate tag", e);
|
|
306
309
|
}
|
|
307
310
|
}
|
|
311
|
+
// TODO: We should delete/update tags in this method
|
|
312
|
+
// This will require an update to the tag cache interface
|
|
313
|
+
async updateTagsOnSet(key, data, ctx) {
|
|
314
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache ||
|
|
315
|
+
globalThis.tagCache.mode === "nextMode" ||
|
|
316
|
+
// Here it means it's a delete
|
|
317
|
+
!data) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Write derivedTags to the tag cache
|
|
321
|
+
// If we use an in house version of getDerivedTags in build we should use it here instead of next's one
|
|
322
|
+
const derivedTags = data?.kind === "FETCH"
|
|
323
|
+
? (ctx?.tags ?? data?.data?.tags ?? []) // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
|
|
324
|
+
: data?.kind === "PAGE"
|
|
325
|
+
? (data.headers?.["x-next-cache-tags"]?.split(",") ?? [])
|
|
326
|
+
: [];
|
|
327
|
+
debug("derivedTags", derivedTags);
|
|
328
|
+
// Get all tags stored in dynamodb for the given key
|
|
329
|
+
// If any of the derived tags are not stored in dynamodb for the given key, write them
|
|
330
|
+
const storedTags = await globalThis.tagCache.getByPath(key);
|
|
331
|
+
const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
|
|
332
|
+
if (tagsToWrite.length > 0) {
|
|
333
|
+
await globalThis.tagCache.writeTags(tagsToWrite.map((tag) => ({
|
|
334
|
+
path: key,
|
|
335
|
+
tag: tag,
|
|
336
|
+
// In case the tags are not there we just need to create them
|
|
337
|
+
// but we don't want them to return from `getLastModified` as they are not stale
|
|
338
|
+
revalidatedAt: 1,
|
|
339
|
+
})));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
308
342
|
}
|
|
@@ -27,3 +27,4 @@ export declare const AppPathsManifestKeys: string[];
|
|
|
27
27
|
export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
|
|
28
28
|
export declare const AppPathsManifest: Record<string, string>;
|
|
29
29
|
export declare const AppPathRoutesManifest: Record<string, string>;
|
|
30
|
+
export declare const FunctionsConfigManifest: import("../../types/next-types").FunctionsConfigManifest;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { debug } from "../logger";
|
|
3
|
-
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
|
|
3
|
+
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
|
|
4
4
|
export const NEXT_DIR = path.join(__dirname, ".next");
|
|
5
5
|
export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
|
|
6
6
|
debug({ NEXT_DIR, OPEN_NEXT_DIR });
|
|
@@ -20,3 +20,5 @@ export const MiddlewareManifest =
|
|
|
20
20
|
export const AppPathsManifest = /* @__PURE__ */ loadAppPathsManifest(NEXT_DIR);
|
|
21
21
|
export const AppPathRoutesManifest =
|
|
22
22
|
/* @__PURE__ */ loadAppPathRoutesManifest(NEXT_DIR);
|
|
23
|
+
export const FunctionsConfigManifest =
|
|
24
|
+
/* @__PURE__ */ loadFunctionsConfigManifest(NEXT_DIR);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MiddlewareManifest, NextConfig, PrerenderManifest } from "../../types/next-types";
|
|
1
|
+
import type { FunctionsConfigManifest, MiddlewareManifest, NextConfig, PrerenderManifest } from "../../types/next-types";
|
|
2
2
|
import type { PublicFiles } from "../../build";
|
|
3
3
|
export declare function loadConfig(nextDir: string): NextConfig;
|
|
4
4
|
export declare function loadBuildId(nextDir: string): string;
|
|
@@ -28,3 +28,4 @@ export declare function loadAppPathsManifest(nextDir: string): Record<string, st
|
|
|
28
28
|
export declare function loadAppPathRoutesManifest(nextDir: string): Record<string, string>;
|
|
29
29
|
export declare function loadAppPathsManifestKeys(nextDir: string): string[];
|
|
30
30
|
export declare function loadMiddlewareManifest(nextDir: string): MiddlewareManifest;
|
|
31
|
+
export declare function loadFunctionsConfigManifest(nextDir: string): FunctionsConfigManifest;
|
|
@@ -11,7 +11,7 @@ export function loadBuildId(nextDir) {
|
|
|
11
11
|
return fs.readFileSync(filePath, "utf-8").trim();
|
|
12
12
|
}
|
|
13
13
|
export function loadHtmlPages(nextDir) {
|
|
14
|
-
const filePath = path.join(nextDir, "server
|
|
14
|
+
const filePath = path.join(nextDir, "server/pages-manifest.json");
|
|
15
15
|
const json = fs.readFileSync(filePath, "utf-8");
|
|
16
16
|
return Object.entries(JSON.parse(json))
|
|
17
17
|
.filter(([_, value]) => value.endsWith(".html"))
|
|
@@ -61,7 +61,7 @@ export function loadPrerenderManifest(nextDir) {
|
|
|
61
61
|
return JSON.parse(json);
|
|
62
62
|
}
|
|
63
63
|
export function loadAppPathsManifest(nextDir) {
|
|
64
|
-
const appPathsManifestPath = path.join(nextDir, "server
|
|
64
|
+
const appPathsManifestPath = path.join(nextDir, "server/app-paths-manifest.json");
|
|
65
65
|
const appPathsManifestJson = fs.existsSync(appPathsManifestPath)
|
|
66
66
|
? fs.readFileSync(appPathsManifestPath, "utf-8")
|
|
67
67
|
: "{}";
|
|
@@ -88,7 +88,17 @@ export function loadAppPathsManifestKeys(nextDir) {
|
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
export function loadMiddlewareManifest(nextDir) {
|
|
91
|
-
const filePath = path.join(nextDir, "server
|
|
91
|
+
const filePath = path.join(nextDir, "server/middleware-manifest.json");
|
|
92
92
|
const json = fs.readFileSync(filePath, "utf-8");
|
|
93
93
|
return JSON.parse(json);
|
|
94
94
|
}
|
|
95
|
+
export function loadFunctionsConfigManifest(nextDir) {
|
|
96
|
+
const filePath = path.join(nextDir, "server/functions-config-manifest.json");
|
|
97
|
+
try {
|
|
98
|
+
const json = fs.readFileSync(filePath, "utf-8");
|
|
99
|
+
return JSON.parse(json);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
return { functions: {}, version: 1 };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -16,6 +16,14 @@ async function defaultHandler(event) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
async function insert(requestType) {
|
|
19
|
+
// If it is in nextMode, we don't need to do anything
|
|
20
|
+
if (tagCache.mode === "nextMode") {
|
|
21
|
+
return {
|
|
22
|
+
type: "initializationFunction",
|
|
23
|
+
requestType,
|
|
24
|
+
resourceId: PHYSICAL_RESOURCE_ID,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
19
27
|
const file = readFileSync("dynamodb-cache.json", "utf8");
|
|
20
28
|
const data = JSON.parse(file);
|
|
21
29
|
const parsedData = data.map((item) => ({
|
|
@@ -3,18 +3,12 @@ import { emptyReadableStream } from "../utils/stream";
|
|
|
3
3
|
// We import it like that so that the edge plugin can replace it
|
|
4
4
|
import { NextConfig } from "../adapters/config";
|
|
5
5
|
import { createGenericHandler } from "../core/createGenericHandler";
|
|
6
|
-
import { convertBodyToReadableStream
|
|
6
|
+
import { convertBodyToReadableStream } from "../core/routing/util";
|
|
7
7
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
8
|
-
const defaultHandler = async (internalEvent) => {
|
|
8
|
+
const defaultHandler = async (internalEvent, options) => {
|
|
9
9
|
globalThis.isEdgeRuntime = true;
|
|
10
10
|
// We run everything in the async local storage context so that it is available in edge runtime functions
|
|
11
|
-
return runWithOpenNextRequestContext({ isISRRevalidation: false }, async () => {
|
|
12
|
-
const host = internalEvent.headers.host
|
|
13
|
-
? `https://${internalEvent.headers.host}`
|
|
14
|
-
: "http://localhost:3000";
|
|
15
|
-
const initialUrl = new URL(internalEvent.rawPath, host);
|
|
16
|
-
initialUrl.search = convertToQueryString(internalEvent.query);
|
|
17
|
-
const url = initialUrl.toString();
|
|
11
|
+
return runWithOpenNextRequestContext({ isISRRevalidation: false, waitUntil: options?.waitUntil }, async () => {
|
|
18
12
|
// @ts-expect-error - This is bundled
|
|
19
13
|
const handler = await import("./middleware.mjs");
|
|
20
14
|
const response = await handler.default({
|
|
@@ -25,7 +19,7 @@ const defaultHandler = async (internalEvent) => {
|
|
|
25
19
|
i18n: NextConfig.i18n,
|
|
26
20
|
trailingSlash: NextConfig.trailingSlash,
|
|
27
21
|
},
|
|
28
|
-
url,
|
|
22
|
+
url: internalEvent.url,
|
|
29
23
|
body: convertBodyToReadableStream(internalEvent.method, internalEvent.body),
|
|
30
24
|
});
|
|
31
25
|
const responseHeaders = {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { InternalEvent, InternalResult
|
|
1
|
+
import type { InternalEvent, InternalResult } from "../types/open-next.js";
|
|
2
|
+
import type { OpenNextHandlerOptions } from "../types/overrides.js";
|
|
2
3
|
export declare const handler: (...args: any[]) => any;
|
|
3
|
-
export declare function defaultHandler(event: InternalEvent,
|
|
4
|
+
export declare function defaultHandler(event: InternalEvent, options?: OpenNextHandlerOptions): Promise<InternalResult>;
|
|
@@ -36,7 +36,7 @@ export const handler = await createGenericHandler({
|
|
|
36
36
|
handler: defaultHandler,
|
|
37
37
|
type: "imageOptimization",
|
|
38
38
|
});
|
|
39
|
-
export async function defaultHandler(event,
|
|
39
|
+
export async function defaultHandler(event, options) {
|
|
40
40
|
// Images are handled via header and query param information.
|
|
41
41
|
debug("handler event", event);
|
|
42
42
|
const { headers, query: queryString } = event;
|
|
@@ -65,10 +65,10 @@ export async function defaultHandler(event, streamCreator) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
const result = await optimizeImage(headers, imageParams, nextConfig, downloadHandler);
|
|
68
|
-
return buildSuccessResponse(result, streamCreator, etag);
|
|
68
|
+
return buildSuccessResponse(result, options?.streamCreator, etag);
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
71
|
-
return buildFailureResponse(e, streamCreator);
|
|
71
|
+
return buildFailureResponse(e, options?.streamCreator);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
//////////////////////
|
|
@@ -2,10 +2,11 @@ import { runWithOpenNextRequestContext } from "../utils/promise";
|
|
|
2
2
|
import { debug, error } from "../adapters/logger";
|
|
3
3
|
import { createGenericHandler } from "../core/createGenericHandler";
|
|
4
4
|
import { resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
|
|
5
|
-
import
|
|
5
|
+
import { constructNextUrl } from "../core/routing/util";
|
|
6
|
+
import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
|
|
6
7
|
globalThis.internalFetch = fetch;
|
|
7
8
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
8
|
-
const defaultHandler = async (internalEvent) => {
|
|
9
|
+
const defaultHandler = async (internalEvent, options) => {
|
|
9
10
|
const originResolver = await resolveOriginResolver(globalThis.openNextConfig.middleware?.originResolver);
|
|
10
11
|
const externalRequestProxy = await resolveProxyRequest(globalThis.openNextConfig.middleware?.override?.proxyExternalRequest);
|
|
11
12
|
//#override includeCacheInMiddleware
|
|
@@ -14,7 +15,10 @@ const defaultHandler = async (internalEvent) => {
|
|
|
14
15
|
globalThis.incrementalCache = await resolveIncrementalCache(globalThis.openNextConfig.middleware?.override?.incrementalCache);
|
|
15
16
|
//#endOverride
|
|
16
17
|
// We run everything in the async local storage context so that it is available in the external middleware
|
|
17
|
-
return runWithOpenNextRequestContext({
|
|
18
|
+
return runWithOpenNextRequestContext({
|
|
19
|
+
isISRRevalidation: internalEvent.headers["x-isr"] === "1",
|
|
20
|
+
waitUntil: options?.waitUntil,
|
|
21
|
+
}, async () => {
|
|
18
22
|
const result = await routingHandler(internalEvent);
|
|
19
23
|
if ("internalEvent" in result) {
|
|
20
24
|
debug("Middleware intercepted event", internalEvent);
|
|
@@ -26,14 +30,14 @@ const defaultHandler = async (internalEvent) => {
|
|
|
26
30
|
...result.internalEvent,
|
|
27
31
|
headers: {
|
|
28
32
|
...result.internalEvent.headers,
|
|
29
|
-
[
|
|
30
|
-
[INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes)
|
|
33
|
+
[INTERNAL_HEADER_INITIAL_URL]: internalEvent.url,
|
|
34
|
+
[INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes),
|
|
31
35
|
},
|
|
32
36
|
},
|
|
33
37
|
isExternalRewrite: result.isExternalRewrite,
|
|
34
38
|
origin,
|
|
35
39
|
isISR: result.isISR,
|
|
36
|
-
|
|
40
|
+
initialURL: result.initialURL,
|
|
37
41
|
resolvedRoutes: result.resolvedRoutes,
|
|
38
42
|
};
|
|
39
43
|
}
|
|
@@ -47,14 +51,14 @@ const defaultHandler = async (internalEvent) => {
|
|
|
47
51
|
internalEvent: {
|
|
48
52
|
...result.internalEvent,
|
|
49
53
|
rawPath: "/500",
|
|
50
|
-
url: "/500",
|
|
54
|
+
url: constructNextUrl(result.internalEvent.url, "/500"),
|
|
51
55
|
method: "GET",
|
|
52
56
|
},
|
|
53
57
|
// On error we need to rewrite to the 500 page which is an internal rewrite
|
|
54
58
|
isExternalRewrite: false,
|
|
55
59
|
origin: false,
|
|
56
60
|
isISR: result.isISR,
|
|
57
|
-
|
|
61
|
+
initialURL: result.internalEvent.url,
|
|
58
62
|
resolvedRoutes: [{ route: "/500", type: "page" }],
|
|
59
63
|
};
|
|
60
64
|
}
|
|
@@ -29,7 +29,7 @@ export async function compileOpenNextConfig(baseDir, openNextConfigPath, nodeExt
|
|
|
29
29
|
validateConfig(config);
|
|
30
30
|
// We need to check if the config uses the edge runtime at any point
|
|
31
31
|
// If it does, we need to compile it with the edge runtime
|
|
32
|
-
const usesEdgeRuntime = config.middleware?.external ||
|
|
32
|
+
const usesEdgeRuntime = (config.middleware?.external && config.middleware.runtime !== "node") ||
|
|
33
33
|
Object.values(config.functions || {}).some((fn) => fn.runtime === "edge");
|
|
34
34
|
if (!usesEdgeRuntime) {
|
|
35
35
|
logger.debug("No edge runtime found in the open-next.config.ts. Using default config.");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MIDDLEWARE_TRACE_FILE = "server/middleware.js.nft.json";
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
interface CopyTracedFilesOptions {
|
|
2
|
+
buildOutputPath: string;
|
|
3
|
+
packagePath: string;
|
|
4
|
+
outputDir: string;
|
|
5
|
+
routes: string[];
|
|
6
|
+
bundledNextServer: boolean;
|
|
7
|
+
skipServerFiles?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }: CopyTracedFilesOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import url from "node:url";
|
|
2
2
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { loadConfig, loadPrerenderManifest } from "../adapters/config/util.js";
|
|
4
5
|
import logger from "../logger.js";
|
|
6
|
+
import { MIDDLEWARE_TRACE_FILE } from "./constant.js";
|
|
5
7
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
6
8
|
function copyPatchFile(outputDir) {
|
|
7
9
|
const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js");
|
|
@@ -9,11 +11,12 @@ function copyPatchFile(outputDir) {
|
|
|
9
11
|
copyFileSync(patchFile, outputPatchFile);
|
|
10
12
|
}
|
|
11
13
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
12
|
-
export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, routes, bundledNextServer) {
|
|
14
|
+
export async function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }) {
|
|
13
15
|
const tsStart = Date.now();
|
|
14
16
|
const dotNextDir = path.join(buildOutputPath, ".next");
|
|
15
17
|
const standaloneDir = path.join(dotNextDir, "standalone");
|
|
16
18
|
const standaloneNextDir = path.join(standaloneDir, packagePath, ".next");
|
|
19
|
+
const standaloneServerDir = path.join(standaloneNextDir, "server");
|
|
17
20
|
const outputNextDir = path.join(outputDir, packagePath, ".next");
|
|
18
21
|
const extractFiles = (files, from = standaloneNextDir) => files.map((f) => path.resolve(from, f));
|
|
19
22
|
// On next 14+, we might not have to include those files
|
|
@@ -23,16 +26,18 @@ export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, r
|
|
|
23
26
|
: "next-server.js.nft.json"), "utf8"));
|
|
24
27
|
const filesToCopy = new Map();
|
|
25
28
|
// Files necessary by the server
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
if (!skipServerFiles) {
|
|
30
|
+
extractFiles(requiredServerFiles.files).forEach((f) => {
|
|
31
|
+
filesToCopy.set(f, f.replace(standaloneDir, outputDir));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
29
34
|
// create directory for pages
|
|
30
|
-
if (existsSync(path.join(
|
|
35
|
+
if (existsSync(path.join(standaloneServerDir, "pages"))) {
|
|
31
36
|
mkdirSync(path.join(outputNextDir, "server/pages"), {
|
|
32
37
|
recursive: true,
|
|
33
38
|
});
|
|
34
39
|
}
|
|
35
|
-
if (existsSync(path.join(
|
|
40
|
+
if (existsSync(path.join(standaloneServerDir, "app"))) {
|
|
36
41
|
mkdirSync(path.join(outputNextDir, "server/app"), {
|
|
37
42
|
recursive: true,
|
|
38
43
|
});
|
|
@@ -83,6 +88,11 @@ File ${fullFilePath} does not exist
|
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
};
|
|
91
|
+
if (existsSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE))) {
|
|
92
|
+
// We still need to copy the nft.json file so that computeCopyFilesForPage doesn't throw
|
|
93
|
+
copyFileSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE), path.join(standaloneNextDir, MIDDLEWARE_TRACE_FILE));
|
|
94
|
+
computeCopyFilesForPage("middleware");
|
|
95
|
+
}
|
|
86
96
|
const hasPageDir = routes.some((route) => route.startsWith("pages/"));
|
|
87
97
|
const hasAppDir = routes.some((route) => route.startsWith("app/"));
|
|
88
98
|
// We need to copy all the base files like _app, _document, _error, etc
|
|
@@ -145,10 +155,10 @@ File ${fullFilePath} does not exist
|
|
|
145
155
|
.forEach((file) => copyFileSync(path.join(standaloneNextDir, file), path.join(outputNextDir, file)));
|
|
146
156
|
// We then need to copy all the files at the root of server
|
|
147
157
|
mkdirSync(path.join(outputNextDir, "server"), { recursive: true });
|
|
148
|
-
readdirSync(
|
|
149
|
-
.filter((fileOrDir) => !statSync(path.join(
|
|
158
|
+
readdirSync(standaloneServerDir)
|
|
159
|
+
.filter((fileOrDir) => !statSync(path.join(standaloneServerDir, fileOrDir)).isDirectory())
|
|
150
160
|
.filter((file) => file !== "server.js")
|
|
151
|
-
.forEach((file) => copyFileSync(path.join(
|
|
161
|
+
.forEach((file) => copyFileSync(path.join(standaloneServerDir, file), path.join(path.join(outputNextDir, "server"), file)));
|
|
152
162
|
// Copy patch file
|
|
153
163
|
copyPatchFile(path.join(outputDir, packagePath));
|
|
154
164
|
// TODO: Recompute all the files.
|
|
@@ -172,10 +182,10 @@ File ${fullFilePath} does not exist
|
|
|
172
182
|
// Maybe even move this to s3 directly
|
|
173
183
|
if (hasPageDir) {
|
|
174
184
|
// First we get truly static files - i.e. pages without getStaticProps
|
|
175
|
-
const staticFiles = Object.values(JSON.parse(readFileSync(path.join(
|
|
185
|
+
const staticFiles = Object.values(JSON.parse(readFileSync(path.join(standaloneServerDir, "pages-manifest.json"), "utf8")));
|
|
176
186
|
// Then we need to get all fallback: true dynamic routes html
|
|
177
|
-
const prerenderManifest =
|
|
178
|
-
const config =
|
|
187
|
+
const prerenderManifest = loadPrerenderManifest(standaloneNextDir);
|
|
188
|
+
const config = loadConfig(standaloneNextDir);
|
|
179
189
|
const locales = config.i18n?.locales;
|
|
180
190
|
Object.values(prerenderManifest.dynamicRoutes).forEach((route) => {
|
|
181
191
|
if (typeof route.fallback === "string") {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import type { TagCacheMetaFile } from "../types/cache.js";
|
|
1
2
|
import * as buildHelper from "./helper.js";
|
|
2
3
|
export declare function createStaticAssets(options: buildHelper.BuildOptions): void;
|
|
3
4
|
/**
|
|
4
5
|
* Create the cache assets.
|
|
5
6
|
*
|
|
6
7
|
* @param options Build options.
|
|
7
|
-
* @returns Whether tag cache is used.
|
|
8
|
+
* @returns Whether the tag cache is used, and the meta files collected.
|
|
8
9
|
*/
|
|
9
10
|
export declare function createCacheAssets(options: buildHelper.BuildOptions): {
|
|
10
11
|
useTagCache: boolean;
|
|
12
|
+
metaFiles: TagCacheMetaFile[];
|
|
11
13
|
};
|
|
@@ -39,7 +39,7 @@ export function createStaticAssets(options) {
|
|
|
39
39
|
* Create the cache assets.
|
|
40
40
|
*
|
|
41
41
|
* @param options Build options.
|
|
42
|
-
* @returns Whether tag cache is used.
|
|
42
|
+
* @returns Whether the tag cache is used, and the meta files collected.
|
|
43
43
|
*/
|
|
44
44
|
export function createCacheAssets(options) {
|
|
45
45
|
logger.info("Bundling cache assets...");
|
|
@@ -165,5 +165,5 @@ export function createCacheAssets(options) {
|
|
|
165
165
|
}
|
|
166
166
|
// We need to remove files later because we need the metafiles for dynamodb tags cache
|
|
167
167
|
buildHelper.removeFiles(outputPath, ({ relativePath }) => !relativePath.endsWith(".cache"));
|
|
168
|
-
return { useTagCache };
|
|
168
|
+
return { useTagCache, metaFiles };
|
|
169
169
|
}
|
|
@@ -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",
|