@opennextjs/aws 3.3.0 → 3.3.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.d.ts +1 -2
- package/dist/adapters/cache.js +1 -11
- package/dist/adapters/config/index.d.ts +2 -0
- package/dist/adapters/config/index.js +4 -3
- package/dist/adapters/config/util.d.ts +2 -0
- package/dist/adapters/config/util.js +12 -2
- package/dist/adapters/middleware.js +13 -2
- package/dist/adapters/util.js +5 -1
- package/dist/build/createAssets.js +22 -23
- package/dist/build/edge/createEdgeBundle.d.ts +4 -3
- package/dist/build/edge/createEdgeBundle.js +12 -21
- package/dist/core/requestHandler.js +15 -4
- package/dist/core/routing/routeMatcher.d.ts +10 -0
- package/dist/core/routing/routeMatcher.js +45 -0
- package/dist/core/routing/util.d.ts +1 -1
- package/dist/core/routing/util.js +6 -4
- package/dist/core/routingHandler.d.ts +3 -0
- package/dist/core/routingHandler.js +30 -29
- package/dist/overrides/wrappers/cloudflare-node.js +4 -1
- package/dist/plugins/edge.js +6 -1
- package/dist/types/open-next.d.ts +7 -0
- package/dist/types/overrides.d.ts +3 -3
- package/package.json +1 -1
package/dist/adapters/cache.d.ts
CHANGED
|
@@ -58,8 +58,7 @@ interface CacheHandlerValue {
|
|
|
58
58
|
cacheState?: string;
|
|
59
59
|
value: IncrementalCacheValue | null;
|
|
60
60
|
}
|
|
61
|
-
export
|
|
62
|
-
export default class S3Cache {
|
|
61
|
+
export default class Cache {
|
|
63
62
|
get(key: string, options?: boolean | {
|
|
64
63
|
fetchCache?: boolean;
|
|
65
64
|
kindHint?: "app" | "pages" | "fetch";
|
package/dist/adapters/cache.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { isBinaryContentType } from "../utils/binary";
|
|
2
2
|
import { debug, error, warn } from "./logger";
|
|
3
|
-
/** Beginning single backslash is intentional, to look for the dot + the extension. Do not escape it again. */
|
|
4
|
-
const CACHE_EXTENSION_REGEX = /\.(cache|fetch)$/;
|
|
5
|
-
export function hasCacheExtension(key) {
|
|
6
|
-
return CACHE_EXTENSION_REGEX.test(key);
|
|
7
|
-
}
|
|
8
3
|
function isFetchCache(options) {
|
|
9
4
|
if (typeof options === "boolean") {
|
|
10
5
|
return options;
|
|
@@ -17,7 +12,7 @@ function isFetchCache(options) {
|
|
|
17
12
|
return false;
|
|
18
13
|
}
|
|
19
14
|
// We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time
|
|
20
|
-
export default class
|
|
15
|
+
export default class Cache {
|
|
21
16
|
async get(key,
|
|
22
17
|
// fetchCache is for next 13.5 and above, kindHint is for next 14 and above and boolean is for earlier versions
|
|
23
18
|
options) {
|
|
@@ -34,7 +29,6 @@ export default class S3Cache {
|
|
|
34
29
|
debug("get fetch cache", { key, softTags, tags });
|
|
35
30
|
try {
|
|
36
31
|
const { value, lastModified } = await globalThis.incrementalCache.get(key, true);
|
|
37
|
-
// const { Body, LastModified } = await this.getS3Object(key, "fetch");
|
|
38
32
|
const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
|
|
39
33
|
if (_lastModified === -1) {
|
|
40
34
|
// If some tags are stale we need to force revalidation
|
|
@@ -71,10 +65,6 @@ export default class S3Cache {
|
|
|
71
65
|
async getIncrementalCache(key) {
|
|
72
66
|
try {
|
|
73
67
|
const { value: cacheData, lastModified } = await globalThis.incrementalCache.get(key, false);
|
|
74
|
-
// const { Body, LastModified } = await this.getS3Object(key, "cache");
|
|
75
|
-
// const cacheData = JSON.parse(
|
|
76
|
-
// (await Body?.transformToString()) ?? "{}",
|
|
77
|
-
// ) as S3CachedFile;
|
|
78
68
|
const meta = cacheData?.meta;
|
|
79
69
|
const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
|
|
80
70
|
if (_lastModified === -1) {
|
|
@@ -25,3 +25,5 @@ export declare const ConfigHeaders: import("../../types/next-types").Header[] |
|
|
|
25
25
|
export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest;
|
|
26
26
|
export declare const AppPathsManifestKeys: string[];
|
|
27
27
|
export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
|
|
28
|
+
export declare const AppPathsManifest: Record<string, string>;
|
|
29
|
+
export declare const AppPathRoutesManifest: Record<string, string>;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { debug } from "../logger";
|
|
3
|
-
import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest,
|
|
4
|
-
// loadPublicAssets,
|
|
5
|
-
loadRoutesManifest, } from "./util.js";
|
|
3
|
+
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
|
|
6
4
|
export const NEXT_DIR = path.join(__dirname, ".next");
|
|
7
5
|
export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
|
|
8
6
|
debug({ NEXT_DIR, OPEN_NEXT_DIR });
|
|
@@ -19,3 +17,6 @@ export const AppPathsManifestKeys =
|
|
|
19
17
|
/* @__PURE__ */ loadAppPathsManifestKeys(NEXT_DIR);
|
|
20
18
|
export const MiddlewareManifest =
|
|
21
19
|
/* @__PURE__ */ loadMiddlewareManifest(NEXT_DIR);
|
|
20
|
+
export const AppPathsManifest = /* @__PURE__ */ loadAppPathsManifest(NEXT_DIR);
|
|
21
|
+
export const AppPathRoutesManifest =
|
|
22
|
+
/* @__PURE__ */ loadAppPathRoutesManifest(NEXT_DIR);
|
|
@@ -24,5 +24,7 @@ export declare function loadRoutesManifest(nextDir: string): {
|
|
|
24
24
|
};
|
|
25
25
|
export declare function loadConfigHeaders(nextDir: string): import("../../types/next-types").Header[] | undefined;
|
|
26
26
|
export declare function loadPrerenderManifest(nextDir: string): PrerenderManifest;
|
|
27
|
+
export declare function loadAppPathsManifest(nextDir: string): Record<string, string>;
|
|
28
|
+
export declare function loadAppPathRoutesManifest(nextDir: string): Record<string, string>;
|
|
27
29
|
export declare function loadAppPathsManifestKeys(nextDir: string): string[];
|
|
28
30
|
export declare function loadMiddlewareManifest(nextDir: string): MiddlewareManifest;
|
|
@@ -60,12 +60,22 @@ export function loadPrerenderManifest(nextDir) {
|
|
|
60
60
|
const json = fs.readFileSync(filePath, "utf-8");
|
|
61
61
|
return JSON.parse(json);
|
|
62
62
|
}
|
|
63
|
-
export function
|
|
63
|
+
export function loadAppPathsManifest(nextDir) {
|
|
64
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
|
: "{}";
|
|
68
|
-
|
|
68
|
+
return JSON.parse(appPathsManifestJson);
|
|
69
|
+
}
|
|
70
|
+
export function loadAppPathRoutesManifest(nextDir) {
|
|
71
|
+
const appPathRoutesManifestPath = path.join(nextDir, "app-path-routes-manifest.json");
|
|
72
|
+
if (fs.existsSync(appPathRoutesManifestPath)) {
|
|
73
|
+
return JSON.parse(fs.readFileSync(appPathRoutesManifestPath, "utf-8"));
|
|
74
|
+
}
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
export function loadAppPathsManifestKeys(nextDir) {
|
|
78
|
+
const appPathsManifest = loadAppPathsManifest(nextDir);
|
|
69
79
|
return Object.keys(appPathsManifest).map((key) => {
|
|
70
80
|
// Remove parallel route
|
|
71
81
|
let cleanedKey = key.replace(/\/@[^\/]+/g, "");
|
|
@@ -2,7 +2,7 @@ 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 routingHandler from "../core/routingHandler";
|
|
5
|
+
import routingHandler, { INTERNAL_HEADER_INITIAL_PATH, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
|
|
6
6
|
globalThis.internalFetch = fetch;
|
|
7
7
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
8
8
|
const defaultHandler = async (internalEvent) => {
|
|
@@ -22,10 +22,19 @@ const defaultHandler = async (internalEvent) => {
|
|
|
22
22
|
const origin = await originResolver.resolve(result.internalEvent.rawPath);
|
|
23
23
|
return {
|
|
24
24
|
type: "middleware",
|
|
25
|
-
internalEvent:
|
|
25
|
+
internalEvent: {
|
|
26
|
+
...result.internalEvent,
|
|
27
|
+
headers: {
|
|
28
|
+
...result.internalEvent.headers,
|
|
29
|
+
[INTERNAL_HEADER_INITIAL_PATH]: internalEvent.rawPath,
|
|
30
|
+
[INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes) ?? "[]",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
26
33
|
isExternalRewrite: result.isExternalRewrite,
|
|
27
34
|
origin,
|
|
28
35
|
isISR: result.isISR,
|
|
36
|
+
initialPath: result.initialPath,
|
|
37
|
+
resolvedRoutes: result.resolvedRoutes,
|
|
29
38
|
};
|
|
30
39
|
}
|
|
31
40
|
try {
|
|
@@ -45,6 +54,8 @@ const defaultHandler = async (internalEvent) => {
|
|
|
45
54
|
isExternalRewrite: false,
|
|
46
55
|
origin: false,
|
|
47
56
|
isISR: result.isISR,
|
|
57
|
+
initialPath: result.internalEvent.rawPath,
|
|
58
|
+
resolvedRoutes: [{ route: "/500", type: "page" }],
|
|
48
59
|
};
|
|
49
60
|
}
|
|
50
61
|
}
|
package/dist/adapters/util.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
//TODO: We should probably move all the utils to a separate location
|
|
2
2
|
export function setNodeEnv() {
|
|
3
|
-
|
|
3
|
+
// Note: we create a `processEnv` variable instead of just using `process.env` directly
|
|
4
|
+
// because build tools can substitute `process.env.NODE_ENV` on build making
|
|
5
|
+
// assignments such as `process.env.NODE_ENV = ...` problematic
|
|
6
|
+
const processEnv = process.env;
|
|
7
|
+
processEnv.NODE_ENV = process.env.NODE_ENV ?? "production";
|
|
4
8
|
}
|
|
5
9
|
export function generateUniqueId() {
|
|
6
10
|
return Math.random().toString(36).slice(2, 8);
|
|
@@ -109,10 +109,29 @@ export function createCacheAssets(options) {
|
|
|
109
109
|
};
|
|
110
110
|
fs.writeFileSync(cacheFilePath, JSON.stringify(cacheFileContent));
|
|
111
111
|
});
|
|
112
|
+
// We need to traverse the cache to find every .meta file
|
|
113
|
+
const metaFiles = [];
|
|
114
|
+
// Copy fetch-cache to cache folder
|
|
115
|
+
const fetchCachePath = path.join(appBuildOutputPath, ".next/cache/fetch-cache");
|
|
116
|
+
if (fs.existsSync(fetchCachePath)) {
|
|
117
|
+
const fetchOutputPath = path.join(outputDir, "cache", "__fetch", buildId);
|
|
118
|
+
fs.mkdirSync(fetchOutputPath, { recursive: true });
|
|
119
|
+
fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
|
|
120
|
+
buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
|
|
121
|
+
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
122
|
+
const fileData = JSON.parse(fileContent);
|
|
123
|
+
fileData?.tags?.forEach((tag) => {
|
|
124
|
+
metaFiles.push({
|
|
125
|
+
tag: { S: path.posix.join(buildId, tag) },
|
|
126
|
+
path: {
|
|
127
|
+
S: path.posix.join(buildId, relativePath),
|
|
128
|
+
},
|
|
129
|
+
revalidatedAt: { N: "1" },
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
112
134
|
if (!options.config.dangerous?.disableTagCache) {
|
|
113
|
-
// Generate dynamodb data
|
|
114
|
-
// We need to traverse the cache to find every .meta file
|
|
115
|
-
const metaFiles = [];
|
|
116
135
|
// Compute dynamodb cache data
|
|
117
136
|
// Traverse files inside cache to find all meta files and cache tags associated with them
|
|
118
137
|
buildHelper.traverseFiles(outputPath, ({ absolutePath }) => absolutePath.endsWith(".meta"), ({ absolutePath, relativePath }) => {
|
|
@@ -134,26 +153,6 @@ export function createCacheAssets(options) {
|
|
|
134
153
|
});
|
|
135
154
|
}
|
|
136
155
|
});
|
|
137
|
-
// Copy fetch-cache to cache folder
|
|
138
|
-
const fetchCachePath = path.join(appBuildOutputPath, ".next/cache/fetch-cache");
|
|
139
|
-
if (fs.existsSync(fetchCachePath)) {
|
|
140
|
-
const fetchOutputPath = path.join(outputDir, "cache", "__fetch", buildId);
|
|
141
|
-
fs.mkdirSync(fetchOutputPath, { recursive: true });
|
|
142
|
-
fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
|
|
143
|
-
buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
|
|
144
|
-
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
145
|
-
const fileData = JSON.parse(fileContent);
|
|
146
|
-
fileData?.tags?.forEach((tag) => {
|
|
147
|
-
metaFiles.push({
|
|
148
|
-
tag: { S: path.posix.join(buildId, tag) },
|
|
149
|
-
path: {
|
|
150
|
-
S: path.posix.join(buildId, relativePath),
|
|
151
|
-
},
|
|
152
|
-
revalidatedAt: { N: "1" },
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
156
|
if (metaFiles.length > 0) {
|
|
158
157
|
useTagCache = true;
|
|
159
158
|
const providerPath = path.join(outputDir, "dynamodb-provider");
|
|
@@ -2,14 +2,15 @@ import type { MiddlewareInfo } from "../../types/next-types";
|
|
|
2
2
|
import type { IncludedConverter, IncludedOriginResolver, LazyLoadedOverride, OverrideOptions, SplittedFunctionOptions } from "../../types/open-next";
|
|
3
3
|
import type { OriginResolver } from "../../types/overrides.js";
|
|
4
4
|
import { type BuildOptions } from "../helper.js";
|
|
5
|
+
type Override = OverrideOptions & {
|
|
6
|
+
originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
|
|
7
|
+
};
|
|
5
8
|
interface BuildEdgeBundleOptions {
|
|
6
9
|
middlewareInfo?: MiddlewareInfo;
|
|
7
10
|
entrypoint: string;
|
|
8
11
|
outfile: string;
|
|
9
12
|
options: BuildOptions;
|
|
10
|
-
overrides?:
|
|
11
|
-
originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
|
|
12
|
-
};
|
|
13
|
+
overrides?: Override;
|
|
13
14
|
defaultConverter?: IncludedConverter;
|
|
14
15
|
additionalInject?: string;
|
|
15
16
|
includeCache?: boolean;
|
|
@@ -10,6 +10,11 @@ import { isEdgeRuntime } from "../helper.js";
|
|
|
10
10
|
import { copyOpenNextConfig, esbuildAsync } from "../helper.js";
|
|
11
11
|
export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, }) {
|
|
12
12
|
const isInCloudfare = await isEdgeRuntime(overrides);
|
|
13
|
+
function override(target) {
|
|
14
|
+
return typeof overrides?.[target] === "string"
|
|
15
|
+
? overrides[target]
|
|
16
|
+
: undefined;
|
|
17
|
+
}
|
|
13
18
|
await esbuildAsync({
|
|
14
19
|
entryPoints: [entrypoint],
|
|
15
20
|
bundle: true,
|
|
@@ -20,31 +25,17 @@ export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, opt
|
|
|
20
25
|
plugins: [
|
|
21
26
|
openNextResolvePlugin({
|
|
22
27
|
overrides: {
|
|
23
|
-
wrapper:
|
|
24
|
-
|
|
25
|
-
: "aws-lambda",
|
|
26
|
-
converter: typeof overrides?.converter === "string"
|
|
27
|
-
? overrides.converter
|
|
28
|
-
: defaultConverter,
|
|
28
|
+
wrapper: override("wrapper") ?? "aws-lambda",
|
|
29
|
+
converter: override("converter") ?? defaultConverter,
|
|
29
30
|
...(includeCache
|
|
30
31
|
? {
|
|
31
|
-
tagCache:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
incrementalCache: typeof overrides?.incrementalCache === "string"
|
|
35
|
-
? overrides.incrementalCache
|
|
36
|
-
: "s3-lite",
|
|
37
|
-
queue: typeof overrides?.queue === "string"
|
|
38
|
-
? overrides.queue
|
|
39
|
-
: "sqs-lite",
|
|
32
|
+
tagCache: override("tagCache") ?? "dynamodb-lite",
|
|
33
|
+
incrementalCache: override("incrementalCache") ?? "s3-lite",
|
|
34
|
+
queue: override("queue") ?? "sqs-lite",
|
|
40
35
|
}
|
|
41
36
|
: {}),
|
|
42
|
-
originResolver:
|
|
43
|
-
|
|
44
|
-
: "pattern-env",
|
|
45
|
-
proxyExternalRequest: typeof overrides?.proxyExternalRequest === "string"
|
|
46
|
-
? overrides.proxyExternalRequest
|
|
47
|
-
: "node",
|
|
37
|
+
originResolver: override("originResolver") ?? "pattern-env",
|
|
38
|
+
proxyExternalRequest: override("proxyExternalRequest") ?? "node",
|
|
48
39
|
},
|
|
49
40
|
fnName: name,
|
|
50
41
|
}),
|
|
@@ -4,23 +4,32 @@ import { runWithOpenNextRequestContext } from "../utils/promise";
|
|
|
4
4
|
import { debug, error, warn } from "../adapters/logger";
|
|
5
5
|
import { patchAsyncStorage } from "./patchAsyncStorage";
|
|
6
6
|
import { convertRes, createServerResponse } from "./routing/util";
|
|
7
|
-
import routingHandler, { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
|
|
7
|
+
import routingHandler, { INTERNAL_HEADER_INITIAL_PATH, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
|
|
8
8
|
import { requestHandler, setNextjsPrebundledReact } from "./util";
|
|
9
9
|
// This is used to identify requests in the cache
|
|
10
10
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
11
11
|
patchAsyncStorage();
|
|
12
12
|
export async function openNextHandler(internalEvent, responseStreaming) {
|
|
13
|
+
const initialHeaders = internalEvent.headers;
|
|
13
14
|
// We run everything in the async local storage context so that it is available in the middleware as well as in NextServer
|
|
14
|
-
return runWithOpenNextRequestContext({ isISRRevalidation:
|
|
15
|
-
if (
|
|
16
|
-
|
|
15
|
+
return runWithOpenNextRequestContext({ isISRRevalidation: initialHeaders["x-isr"] === "1" }, async () => {
|
|
16
|
+
if (initialHeaders["x-forwarded-host"]) {
|
|
17
|
+
initialHeaders.host = initialHeaders["x-forwarded-host"];
|
|
17
18
|
}
|
|
18
19
|
debug("internalEvent", internalEvent);
|
|
20
|
+
// These 2 will get overwritten by the routing handler if not using an external middleware
|
|
21
|
+
const internalHeaders = {
|
|
22
|
+
initialPath: initialHeaders[INTERNAL_HEADER_INITIAL_PATH] ?? internalEvent.rawPath,
|
|
23
|
+
resolvedRoutes: initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES]
|
|
24
|
+
? JSON.parse(initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES])
|
|
25
|
+
: [],
|
|
26
|
+
};
|
|
19
27
|
let routingResult = {
|
|
20
28
|
internalEvent,
|
|
21
29
|
isExternalRewrite: false,
|
|
22
30
|
origin: false,
|
|
23
31
|
isISR: false,
|
|
32
|
+
...internalHeaders,
|
|
24
33
|
};
|
|
25
34
|
//#override withRouting
|
|
26
35
|
try {
|
|
@@ -65,6 +74,8 @@ export async function openNextHandler(internalEvent, responseStreaming) {
|
|
|
65
74
|
isExternalRewrite: false,
|
|
66
75
|
isISR: false,
|
|
67
76
|
origin: false,
|
|
77
|
+
initialPath: internalEvent.rawPath,
|
|
78
|
+
resolvedRoutes: [{ route: "/500", type: "page" }],
|
|
68
79
|
};
|
|
69
80
|
}
|
|
70
81
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RouteType } from "../../types/open-next";
|
|
2
|
+
export declare const apiPrefix: string;
|
|
3
|
+
export declare const staticRouteMatcher: (path: string) => {
|
|
4
|
+
route: string;
|
|
5
|
+
type: RouteType;
|
|
6
|
+
}[];
|
|
7
|
+
export declare const dynamicRouteMatcher: (path: string) => {
|
|
8
|
+
route: string;
|
|
9
|
+
type: RouteType;
|
|
10
|
+
}[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AppPathRoutesManifest, RoutesManifest } from "../../adapters/config/index";
|
|
2
|
+
// Add the locale prefix to the regex so we correctly match the rawPath
|
|
3
|
+
const optionalLocalePrefixRegex = `^/(?:${RoutesManifest.locales.map((locale) => `${locale}/?`).join("|")})?`;
|
|
4
|
+
// Add the basepath prefix to the regex so we correctly match the rawPath
|
|
5
|
+
const optionalBasepathPrefixRegex = RoutesManifest.basePath
|
|
6
|
+
? `^${RoutesManifest.basePath}/?`
|
|
7
|
+
: "^/";
|
|
8
|
+
// Add the basePath prefix to the api routes
|
|
9
|
+
export const apiPrefix = `${RoutesManifest.basePath ?? ""}/api`;
|
|
10
|
+
const optionalPrefix = optionalLocalePrefixRegex.replace("^/", optionalBasepathPrefixRegex);
|
|
11
|
+
function routeMatcher(routeDefinitions) {
|
|
12
|
+
const regexp = routeDefinitions.map((route) => ({
|
|
13
|
+
page: route.page,
|
|
14
|
+
regexp: new RegExp(route.regex.replace("^/", optionalPrefix)),
|
|
15
|
+
}));
|
|
16
|
+
const appPathsSet = new Set();
|
|
17
|
+
const routePathsSet = new Set();
|
|
18
|
+
// We need to use AppPathRoutesManifest here
|
|
19
|
+
for (const [k, v] of Object.entries(AppPathRoutesManifest)) {
|
|
20
|
+
if (k.endsWith("page")) {
|
|
21
|
+
appPathsSet.add(v);
|
|
22
|
+
}
|
|
23
|
+
else if (k.endsWith("route")) {
|
|
24
|
+
routePathsSet.add(v);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return function matchRoute(path) {
|
|
28
|
+
const foundRoutes = regexp.filter((route) => route.regexp.test(path));
|
|
29
|
+
return foundRoutes.map((foundRoute) => {
|
|
30
|
+
let routeType = "page";
|
|
31
|
+
if (appPathsSet.has(foundRoute.page)) {
|
|
32
|
+
routeType = "app";
|
|
33
|
+
}
|
|
34
|
+
else if (routePathsSet.has(foundRoute.page)) {
|
|
35
|
+
routeType = "route";
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
route: foundRoute.page,
|
|
39
|
+
type: routeType,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export const staticRouteMatcher = routeMatcher(RoutesManifest.routes.static);
|
|
45
|
+
export const dynamicRouteMatcher = routeMatcher(RoutesManifest.routes.dynamic);
|
|
@@ -60,7 +60,7 @@ export declare function convertBodyToReadableStream(method: string, body?: strin
|
|
|
60
60
|
*
|
|
61
61
|
* @__PURE__
|
|
62
62
|
*/
|
|
63
|
-
export declare function fixCacheHeaderForHtmlPages(
|
|
63
|
+
export declare function fixCacheHeaderForHtmlPages(internalEvent: InternalEvent, headers: OutgoingHttpHeaders): void;
|
|
64
64
|
/**
|
|
65
65
|
*
|
|
66
66
|
* @__PURE__
|
|
@@ -5,6 +5,7 @@ import { OpenNextNodeResponse } from "../../http/openNextResponse.js";
|
|
|
5
5
|
import { parseHeaders } from "../../http/util.js";
|
|
6
6
|
import { debug, error } from "../../adapters/logger.js";
|
|
7
7
|
import { isBinaryContentType } from "../../utils/binary.js";
|
|
8
|
+
import { localizePath } from "./i18n/index.js";
|
|
8
9
|
/**
|
|
9
10
|
*
|
|
10
11
|
* @__PURE__
|
|
@@ -169,9 +170,9 @@ var CommonHeaders;
|
|
|
169
170
|
*
|
|
170
171
|
* @__PURE__
|
|
171
172
|
*/
|
|
172
|
-
export function fixCacheHeaderForHtmlPages(
|
|
173
|
+
export function fixCacheHeaderForHtmlPages(internalEvent, headers) {
|
|
173
174
|
// We don't want to cache error pages
|
|
174
|
-
if (rawPath === "/404" || rawPath === "/500") {
|
|
175
|
+
if (internalEvent.rawPath === "/404" || internalEvent.rawPath === "/500") {
|
|
175
176
|
if (process.env.OPEN_NEXT_DANGEROUSLY_SET_ERROR_HEADERS === "true") {
|
|
176
177
|
return;
|
|
177
178
|
}
|
|
@@ -179,9 +180,10 @@ export function fixCacheHeaderForHtmlPages(rawPath, headers) {
|
|
|
179
180
|
"private, no-cache, no-store, max-age=0, must-revalidate";
|
|
180
181
|
return;
|
|
181
182
|
}
|
|
183
|
+
const localizedPath = localizePath(internalEvent);
|
|
182
184
|
// WORKAROUND: `NextServer` does not set cache headers for HTML pages
|
|
183
185
|
// https://opennext.js.org/aws/v2/advanced/workaround#workaround-nextserver-does-not-set-cache-headers-for-html-pages
|
|
184
|
-
if (HtmlPages.includes(
|
|
186
|
+
if (HtmlPages.includes(localizedPath)) {
|
|
185
187
|
headers[CommonHeaders.CACHE_CONTROL] =
|
|
186
188
|
"public, max-age=0, s-maxage=31536000, must-revalidate";
|
|
187
189
|
}
|
|
@@ -352,7 +354,7 @@ export function fixISRHeaders(headers) {
|
|
|
352
354
|
*/
|
|
353
355
|
export function createServerResponse(internalEvent, headers, responseStream) {
|
|
354
356
|
return new OpenNextNodeResponse((_headers) => {
|
|
355
|
-
fixCacheHeaderForHtmlPages(internalEvent
|
|
357
|
+
fixCacheHeaderForHtmlPages(internalEvent, _headers);
|
|
356
358
|
fixSWRCacheHeader(_headers);
|
|
357
359
|
addOpenNextHeader(_headers);
|
|
358
360
|
fixISRHeaders(_headers);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { InternalEvent, InternalResult, RoutingResult } from "../types/open-next";
|
|
2
2
|
export declare const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
|
|
3
3
|
export declare const MIDDLEWARE_HEADER_PREFIX_LEN: number;
|
|
4
|
+
export declare const INTERNAL_HEADER_PREFIX = "x-opennext-";
|
|
5
|
+
export declare const INTERNAL_HEADER_INITIAL_PATH = "x-opennext-initial-path";
|
|
6
|
+
export declare const INTERNAL_HEADER_RESOLVED_ROUTES = "x-opennext-resolved-routes";
|
|
4
7
|
export default function routingHandler(event: InternalEvent): Promise<InternalResult | RoutingResult>;
|
|
@@ -3,26 +3,12 @@ import { debug } from "../adapters/logger";
|
|
|
3
3
|
import { cacheInterceptor } from "./routing/cacheInterceptor";
|
|
4
4
|
import { fixDataPage, getNextConfigHeaders, handleFallbackFalse, handleRedirects, handleRewrites, } from "./routing/matcher";
|
|
5
5
|
import { handleMiddleware } from "./routing/middleware";
|
|
6
|
+
import { apiPrefix, dynamicRouteMatcher, staticRouteMatcher, } from "./routing/routeMatcher";
|
|
6
7
|
export const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
|
|
7
8
|
export const MIDDLEWARE_HEADER_PREFIX_LEN = MIDDLEWARE_HEADER_PREFIX.length;
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
: "^/";
|
|
12
|
-
// Add the basepath prefix to the regex so we correctly match the rawPath
|
|
13
|
-
const optionalBasepathPrefixRegex = RoutesManifest.basePath
|
|
14
|
-
? `^${RoutesManifest.basePath}/?`
|
|
15
|
-
: "^/";
|
|
16
|
-
// Add the basePath prefix to the api routes
|
|
17
|
-
const apiPrefix = RoutesManifest.basePath
|
|
18
|
-
? `${RoutesManifest.basePath}/api`
|
|
19
|
-
: "/api";
|
|
20
|
-
const staticRegexp = RoutesManifest.routes.static.map((route) => new RegExp(route.regex
|
|
21
|
-
.replace("^/", optionalLocalePrefixRegex)
|
|
22
|
-
.replace("^/", optionalBasepathPrefixRegex)));
|
|
23
|
-
const dynamicRegexp = RoutesManifest.routes.dynamic.map((route) => new RegExp(route.regex
|
|
24
|
-
.replace("^/", optionalLocalePrefixRegex)
|
|
25
|
-
.replace("^/", optionalBasepathPrefixRegex)));
|
|
9
|
+
export const INTERNAL_HEADER_PREFIX = "x-opennext-";
|
|
10
|
+
export const INTERNAL_HEADER_INITIAL_PATH = `${INTERNAL_HEADER_PREFIX}initial-path`;
|
|
11
|
+
export const INTERNAL_HEADER_RESOLVED_ROUTES = `${INTERNAL_HEADER_PREFIX}resolved-routes`;
|
|
26
12
|
// Geolocation headers starting from Nextjs 15
|
|
27
13
|
// See https://github.com/vercel/vercel/blob/7714b1c/packages/functions/src/headers.ts
|
|
28
14
|
const geoHeaderToNextHeader = {
|
|
@@ -50,6 +36,14 @@ export default async function routingHandler(event) {
|
|
|
50
36
|
event.headers[nextGeoName] = value;
|
|
51
37
|
}
|
|
52
38
|
}
|
|
39
|
+
// First we remove internal headers
|
|
40
|
+
// We don't want to allow users to set these headers
|
|
41
|
+
for (const key of Object.keys(event.headers)) {
|
|
42
|
+
if (key.startsWith(INTERNAL_HEADER_PREFIX) ||
|
|
43
|
+
key.startsWith(MIDDLEWARE_HEADER_PREFIX)) {
|
|
44
|
+
delete event.headers[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
53
47
|
const nextHeaders = getNextConfigHeaders(event, ConfigHeaders);
|
|
54
48
|
let internalEvent = fixDataPage(event, BuildId);
|
|
55
49
|
if ("statusCode" in internalEvent) {
|
|
@@ -75,9 +69,9 @@ export default async function routingHandler(event) {
|
|
|
75
69
|
internalEvent = beforeRewrites.internalEvent;
|
|
76
70
|
isExternalRewrite = beforeRewrites.isExternalRewrite;
|
|
77
71
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (!isStaticRoute
|
|
72
|
+
const foundStaticRoute = staticRouteMatcher(internalEvent.rawPath);
|
|
73
|
+
const isStaticRoute = !isExternalRewrite && foundStaticRoute.length > 0;
|
|
74
|
+
if (!(isStaticRoute || isExternalRewrite)) {
|
|
81
75
|
// Second rewrite to be applied
|
|
82
76
|
const afterRewrites = handleRewrites(internalEvent, RoutesManifest.rewrites.afterFiles);
|
|
83
77
|
internalEvent = afterRewrites.internalEvent;
|
|
@@ -86,9 +80,9 @@ export default async function routingHandler(event) {
|
|
|
86
80
|
// We want to run this just before the dynamic route check
|
|
87
81
|
const { event: fallbackEvent, isISR } = handleFallbackFalse(internalEvent, PrerenderManifest);
|
|
88
82
|
internalEvent = fallbackEvent;
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
if (!isDynamicRoute
|
|
83
|
+
const foundDynamicRoute = dynamicRouteMatcher(internalEvent.rawPath);
|
|
84
|
+
const isDynamicRoute = !isExternalRewrite && foundDynamicRoute.length > 0;
|
|
85
|
+
if (!(isDynamicRoute || isStaticRoute || isExternalRewrite)) {
|
|
92
86
|
// Fallback rewrite to be applied
|
|
93
87
|
const fallbackRewrites = handleRewrites(internalEvent, RoutesManifest.rewrites.fallback);
|
|
94
88
|
internalEvent = fallbackRewrites.internalEvent;
|
|
@@ -103,12 +97,12 @@ export default async function routingHandler(event) {
|
|
|
103
97
|
const isRouteFoundBeforeAllRewrites = isStaticRoute || isDynamicRoute || isExternalRewrite;
|
|
104
98
|
// If we still haven't found a route, we show the 404 page
|
|
105
99
|
// We need to ensure that rewrites are applied before showing the 404 page
|
|
106
|
-
if (!isRouteFoundBeforeAllRewrites
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
if (!(isRouteFoundBeforeAllRewrites ||
|
|
101
|
+
isApiRoute ||
|
|
102
|
+
isNextImageRoute ||
|
|
109
103
|
// We need to check again once all rewrites have been applied
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
staticRouteMatcher(internalEvent.rawPath).length > 0 ||
|
|
105
|
+
dynamicRouteMatcher(internalEvent.rawPath).length > 0)) {
|
|
112
106
|
internalEvent = {
|
|
113
107
|
...internalEvent,
|
|
114
108
|
rawPath: "/404",
|
|
@@ -136,10 +130,17 @@ export default async function routingHandler(event) {
|
|
|
136
130
|
...middlewareResponseHeaders,
|
|
137
131
|
...nextHeaders,
|
|
138
132
|
});
|
|
133
|
+
const resolvedRoutes = [
|
|
134
|
+
...foundStaticRoute,
|
|
135
|
+
...foundDynamicRoute,
|
|
136
|
+
];
|
|
137
|
+
debug("resolvedRoutes", resolvedRoutes);
|
|
139
138
|
return {
|
|
140
139
|
internalEvent,
|
|
141
140
|
isExternalRewrite,
|
|
142
141
|
origin: false,
|
|
143
142
|
isISR,
|
|
143
|
+
initialPath: event.rawPath,
|
|
144
|
+
resolvedRoutes,
|
|
144
145
|
};
|
|
145
146
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Writable } from "node:stream";
|
|
2
|
+
// Response with null body status (101, 204, 205, or 304) cannot have a body.
|
|
3
|
+
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
|
2
4
|
const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
3
5
|
globalThis.process = process;
|
|
4
6
|
globalThis.openNextWaitUntil = ctx.waitUntil.bind(ctx);
|
|
@@ -29,7 +31,8 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
29
31
|
controller.enqueue(Uint8Array.from(chunk.chunk ?? chunk));
|
|
30
32
|
},
|
|
31
33
|
});
|
|
32
|
-
const
|
|
34
|
+
const body = NULL_BODY_STATUSES.has(statusCode) ? null : readable;
|
|
35
|
+
const response = new Response(body, {
|
|
33
36
|
status: statusCode,
|
|
34
37
|
headers: responseHeaders,
|
|
35
38
|
});
|
package/dist/plugins/edge.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
|
|
4
|
+
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
|
|
5
5
|
import logger from "../logger.js";
|
|
6
6
|
/**
|
|
7
7
|
* @param opts.nextDir - The path to the .next directory
|
|
@@ -126,6 +126,8 @@ ${contents}
|
|
|
126
126
|
const PrerenderManifest = loadPrerenderManifest(nextDir);
|
|
127
127
|
const AppPathsManifestKeys = loadAppPathsManifestKeys(nextDir);
|
|
128
128
|
const MiddlewareManifest = loadMiddlewareManifest(nextDir);
|
|
129
|
+
const AppPathsManifest = loadAppPathsManifest(nextDir);
|
|
130
|
+
const AppPathRoutesManifest = loadAppPathRoutesManifest(nextDir);
|
|
129
131
|
const contents = `
|
|
130
132
|
import path from "node:path";
|
|
131
133
|
|
|
@@ -146,6 +148,9 @@ ${contents}
|
|
|
146
148
|
export const PrerenderManifest = ${JSON.stringify(PrerenderManifest)};
|
|
147
149
|
export const AppPathsManifestKeys = ${JSON.stringify(AppPathsManifestKeys)};
|
|
148
150
|
export const MiddlewareManifest = ${JSON.stringify(MiddlewareManifest)};
|
|
151
|
+
export const AppPathsManifest = ${JSON.stringify(AppPathsManifest)};
|
|
152
|
+
export const AppPathRoutesManifest = ${JSON.stringify(AppPathRoutesManifest)};
|
|
153
|
+
|
|
149
154
|
|
|
150
155
|
process.env.NEXT_BUILD_ID = BuildId;
|
|
151
156
|
`;
|
|
@@ -68,11 +68,18 @@ export interface Origin {
|
|
|
68
68
|
}
|
|
69
69
|
export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "dummy";
|
|
70
70
|
export type IncludedConverter = "aws-apigw-v2" | "aws-apigw-v1" | "aws-cloudfront" | "edge" | "node" | "sqs-revalidate" | "dummy";
|
|
71
|
+
export type RouteType = "route" | "page" | "app";
|
|
72
|
+
export interface ResolvedRoute {
|
|
73
|
+
route: string;
|
|
74
|
+
type: RouteType;
|
|
75
|
+
}
|
|
71
76
|
export interface RoutingResult {
|
|
72
77
|
internalEvent: InternalEvent;
|
|
73
78
|
isExternalRewrite: boolean;
|
|
74
79
|
origin: Origin | false;
|
|
75
80
|
isISR: boolean;
|
|
81
|
+
initialPath: string;
|
|
82
|
+
resolvedRoutes: ResolvedRoute[];
|
|
76
83
|
}
|
|
77
84
|
export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"middleware"> {
|
|
78
85
|
}
|
|
@@ -13,7 +13,7 @@ export interface Queue {
|
|
|
13
13
|
send(message: QueueMessage): Promise<void>;
|
|
14
14
|
name: string;
|
|
15
15
|
}
|
|
16
|
-
export type
|
|
16
|
+
export type CachedFile = {
|
|
17
17
|
type: "redirect";
|
|
18
18
|
props?: Object;
|
|
19
19
|
meta?: Meta;
|
|
@@ -32,12 +32,12 @@ export type S3CachedFile = {
|
|
|
32
32
|
body: string;
|
|
33
33
|
meta?: Meta;
|
|
34
34
|
};
|
|
35
|
-
export type
|
|
35
|
+
export type FetchCache = Object;
|
|
36
36
|
export type WithLastModified<T> = {
|
|
37
37
|
lastModified?: number;
|
|
38
38
|
value?: T;
|
|
39
39
|
};
|
|
40
|
-
export type CacheValue<IsFetch extends boolean> = (IsFetch extends true ?
|
|
40
|
+
export type CacheValue<IsFetch extends boolean> = (IsFetch extends true ? FetchCache : CachedFile) & {
|
|
41
41
|
revalidate?: number | false;
|
|
42
42
|
};
|
|
43
43
|
export type IncrementalCache = {
|