@opennextjs/aws 3.6.6 → 3.7.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.js +4 -4
- package/dist/adapters/composable-cache.js +14 -4
- package/dist/adapters/edge-adapter.js +5 -1
- package/dist/adapters/middleware.js +18 -8
- package/dist/core/createMainHandler.js +4 -1
- package/dist/core/requestHandler.js +11 -2
- package/dist/core/resolve.d.ts +5 -0
- package/dist/core/resolve.js +11 -0
- package/dist/core/routing/cacheInterceptor.js +11 -0
- package/dist/core/routing/matcher.d.ts +5 -0
- package/dist/core/routing/matcher.js +5 -0
- package/dist/core/routing/util.js +2 -0
- package/dist/core/routingHandler.d.ts +5 -1
- package/dist/core/routingHandler.js +75 -52
- package/dist/overrides/assetResolver/dummy.d.ts +8 -0
- package/dist/overrides/assetResolver/dummy.js +9 -0
- package/dist/types/global.d.ts +9 -1
- package/dist/types/open-next.d.ts +8 -1
- package/dist/types/overrides.d.ts +19 -5
- package/dist/utils/cache.d.ts +2 -1
- package/dist/utils/cache.js +32 -0
- package/dist/utils/promise.d.ts +2 -1
- package/dist/utils/promise.js +3 -2
- package/package.json +1 -1
package/dist/adapters/cache.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getTagsFromValue, hasBeenRevalidated } from "../utils/cache";
|
|
1
|
+
import { getTagsFromValue, hasBeenRevalidated, writeTags } from "../utils/cache";
|
|
2
2
|
import { isBinaryContentType } from "../utils/binary";
|
|
3
3
|
import { debug, error, warn } from "./logger";
|
|
4
4
|
function isFetchCache(options) {
|
|
@@ -246,7 +246,7 @@ export default class Cache {
|
|
|
246
246
|
try {
|
|
247
247
|
if (globalThis.tagCache.mode === "nextMode") {
|
|
248
248
|
const paths = (await globalThis.tagCache.getPathsByTags?.(_tags)) ?? [];
|
|
249
|
-
await
|
|
249
|
+
await writeTags(_tags);
|
|
250
250
|
if (paths.length > 0) {
|
|
251
251
|
// TODO: we should introduce a new method in cdnInvalidationHandler to invalidate paths by tags for cdn that supports it
|
|
252
252
|
// It also means that we'll need to provide the tags used in every request to the wrapper or converter.
|
|
@@ -291,7 +291,7 @@ export default class Cache {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
// Update all keys with the given tag with revalidatedAt set to now
|
|
294
|
-
await
|
|
294
|
+
await writeTags(toInsert);
|
|
295
295
|
// We can now invalidate all paths in the CDN
|
|
296
296
|
// This only applies to `revalidateTag`, not to `res.revalidate()`
|
|
297
297
|
const uniquePaths = Array.from(new Set(toInsert
|
|
@@ -340,7 +340,7 @@ export default class Cache {
|
|
|
340
340
|
const storedTags = await globalThis.tagCache.getByPath(key);
|
|
341
341
|
const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
|
|
342
342
|
if (tagsToWrite.length > 0) {
|
|
343
|
-
await
|
|
343
|
+
await writeTags(tagsToWrite.map((tag) => ({
|
|
344
344
|
path: key,
|
|
345
345
|
tag: tag,
|
|
346
346
|
// In case the tags are not there we just need to create them
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { writeTags } from "../utils/cache";
|
|
1
2
|
import { fromReadableStream, toReadableStream } from "../utils/stream";
|
|
2
3
|
import { debug } from "./logger";
|
|
4
|
+
const pendingWritePromiseMap = new Map();
|
|
3
5
|
export default {
|
|
4
6
|
async get(cacheKey) {
|
|
5
7
|
try {
|
|
8
|
+
// We first check if we have a pending write for this cache key
|
|
9
|
+
// If we do, we return the pending promise instead of fetching the cache
|
|
10
|
+
if (pendingWritePromiseMap.has(cacheKey)) {
|
|
11
|
+
return pendingWritePromiseMap.get(cacheKey);
|
|
12
|
+
}
|
|
6
13
|
const result = await globalThis.incrementalCache.get(cacheKey, "composable");
|
|
7
14
|
if (!result?.value?.value) {
|
|
8
15
|
return undefined;
|
|
@@ -32,7 +39,10 @@ export default {
|
|
|
32
39
|
}
|
|
33
40
|
},
|
|
34
41
|
async set(cacheKey, pendingEntry) {
|
|
35
|
-
|
|
42
|
+
pendingWritePromiseMap.set(cacheKey, pendingEntry);
|
|
43
|
+
const entry = await pendingEntry.finally(() => {
|
|
44
|
+
pendingWritePromiseMap.delete(cacheKey);
|
|
45
|
+
});
|
|
36
46
|
const valueToStore = await fromReadableStream(entry.value);
|
|
37
47
|
await globalThis.incrementalCache.set(cacheKey, {
|
|
38
48
|
...entry,
|
|
@@ -42,7 +52,7 @@ export default {
|
|
|
42
52
|
const storedTags = await globalThis.tagCache.getByPath(cacheKey);
|
|
43
53
|
const tagsToWrite = entry.tags.filter((tag) => !storedTags.includes(tag));
|
|
44
54
|
if (tagsToWrite.length > 0) {
|
|
45
|
-
await
|
|
55
|
+
await writeTags(tagsToWrite.map((tag) => ({ tag, path: cacheKey })));
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
},
|
|
@@ -60,7 +70,7 @@ export default {
|
|
|
60
70
|
},
|
|
61
71
|
async expireTags(...tags) {
|
|
62
72
|
if (globalThis.tagCache.mode === "nextMode") {
|
|
63
|
-
return
|
|
73
|
+
return writeTags(tags);
|
|
64
74
|
}
|
|
65
75
|
const tagCache = globalThis.tagCache;
|
|
66
76
|
const revalidatedAt = Date.now();
|
|
@@ -79,7 +89,7 @@ export default {
|
|
|
79
89
|
for (const entry of pathsToUpdate.flat()) {
|
|
80
90
|
setToWrite.add(entry);
|
|
81
91
|
}
|
|
82
|
-
await
|
|
92
|
+
await writeTags(Array.from(setToWrite));
|
|
83
93
|
},
|
|
84
94
|
// This one is necessary for older versions of next
|
|
85
95
|
async receiveExpiredTags(...tags) {
|
|
@@ -4,11 +4,15 @@ import { emptyReadableStream } from "../utils/stream";
|
|
|
4
4
|
import { NextConfig } from "../adapters/config";
|
|
5
5
|
import { createGenericHandler } from "../core/createGenericHandler";
|
|
6
6
|
import { convertBodyToReadableStream } from "../core/routing/util";
|
|
7
|
+
import { INTERNAL_EVENT_REQUEST_ID } from "../core/routingHandler";
|
|
7
8
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
8
9
|
const defaultHandler = async (internalEvent, options) => {
|
|
9
10
|
globalThis.isEdgeRuntime = true;
|
|
11
|
+
const requestId = globalThis.openNextConfig.middleware?.external
|
|
12
|
+
? internalEvent.headers[INTERNAL_EVENT_REQUEST_ID]
|
|
13
|
+
: Math.random().toString(36);
|
|
10
14
|
// We run everything in the async local storage context so that it is available in edge runtime functions
|
|
11
|
-
return runWithOpenNextRequestContext({ isISRRevalidation: false, waitUntil: options?.waitUntil }, async () => {
|
|
15
|
+
return runWithOpenNextRequestContext({ isISRRevalidation: false, waitUntil: options?.waitUntil, requestId }, async () => {
|
|
12
16
|
// @ts-expect-error - This is bundled
|
|
13
17
|
const handler = await import("./middleware.mjs");
|
|
14
18
|
const response = await handler.default({
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { runWithOpenNextRequestContext } from "../utils/promise";
|
|
2
2
|
import { debug, error } from "../adapters/logger";
|
|
3
3
|
import { createGenericHandler } from "../core/createGenericHandler";
|
|
4
|
-
import { resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
|
|
4
|
+
import { resolveAssetResolver, resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
|
|
5
5
|
import { constructNextUrl } from "../core/routing/util";
|
|
6
|
-
import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
|
|
6
|
+
import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
|
|
7
7
|
globalThis.internalFetch = fetch;
|
|
8
8
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
9
9
|
const defaultHandler = async (internalEvent, options) => {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const config = globalThis.openNextConfig.middleware;
|
|
11
|
+
const originResolver = await resolveOriginResolver(config?.originResolver);
|
|
12
|
+
const externalRequestProxy = await resolveProxyRequest(config?.override?.proxyExternalRequest);
|
|
13
|
+
const assetResolver = await resolveAssetResolver(config?.assetResolver);
|
|
12
14
|
//#override includeCacheInMiddleware
|
|
13
|
-
globalThis.tagCache = await resolveTagCache(
|
|
14
|
-
globalThis.queue = await resolveQueue(
|
|
15
|
-
globalThis.incrementalCache = await resolveIncrementalCache(
|
|
15
|
+
globalThis.tagCache = await resolveTagCache(config?.override?.tagCache);
|
|
16
|
+
globalThis.queue = await resolveQueue(config?.override?.queue);
|
|
17
|
+
globalThis.incrementalCache = await resolveIncrementalCache(config?.override?.incrementalCache);
|
|
16
18
|
//#endOverride
|
|
19
|
+
const requestId = Math.random().toString(36);
|
|
17
20
|
// We run everything in the async local storage context so that it is available in the external middleware
|
|
18
21
|
return runWithOpenNextRequestContext({
|
|
19
22
|
isISRRevalidation: internalEvent.headers["x-isr"] === "1",
|
|
20
23
|
waitUntil: options?.waitUntil,
|
|
24
|
+
requestId,
|
|
21
25
|
}, async () => {
|
|
22
|
-
const result = await routingHandler(internalEvent);
|
|
26
|
+
const result = await routingHandler(internalEvent, { assetResolver });
|
|
23
27
|
if ("internalEvent" in result) {
|
|
24
28
|
debug("Middleware intercepted event", internalEvent);
|
|
25
29
|
if (!result.isExternalRewrite) {
|
|
@@ -32,6 +36,7 @@ const defaultHandler = async (internalEvent, options) => {
|
|
|
32
36
|
...result.internalEvent.headers,
|
|
33
37
|
[INTERNAL_HEADER_INITIAL_URL]: internalEvent.url,
|
|
34
38
|
[INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes),
|
|
39
|
+
[INTERNAL_EVENT_REQUEST_ID]: requestId,
|
|
35
40
|
},
|
|
36
41
|
},
|
|
37
42
|
isExternalRewrite: result.isExternalRewrite,
|
|
@@ -50,6 +55,10 @@ const defaultHandler = async (internalEvent, options) => {
|
|
|
50
55
|
type: "middleware",
|
|
51
56
|
internalEvent: {
|
|
52
57
|
...result.internalEvent,
|
|
58
|
+
headers: {
|
|
59
|
+
...result.internalEvent.headers,
|
|
60
|
+
[INTERNAL_EVENT_REQUEST_ID]: requestId,
|
|
61
|
+
},
|
|
53
62
|
rawPath: "/500",
|
|
54
63
|
url: constructNextUrl(result.internalEvent.url, "/500"),
|
|
55
64
|
method: "GET",
|
|
@@ -63,6 +72,7 @@ const defaultHandler = async (internalEvent, options) => {
|
|
|
63
72
|
};
|
|
64
73
|
}
|
|
65
74
|
}
|
|
75
|
+
result.headers[INTERNAL_EVENT_REQUEST_ID] = requestId;
|
|
66
76
|
debug("Middleware response", result);
|
|
67
77
|
return result;
|
|
68
78
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { debug } from "../adapters/logger";
|
|
2
2
|
import { generateUniqueId } from "../adapters/util";
|
|
3
3
|
import { openNextHandler } from "./requestHandler";
|
|
4
|
-
import { resolveCdnInvalidation, resolveConverter, resolveIncrementalCache, resolveProxyRequest, resolveQueue, resolveTagCache, resolveWrapper, } from "./resolve";
|
|
4
|
+
import { resolveAssetResolver, resolveCdnInvalidation, resolveConverter, resolveIncrementalCache, resolveProxyRequest, resolveQueue, resolveTagCache, resolveWrapper, } from "./resolve";
|
|
5
5
|
export async function createMainHandler() {
|
|
6
6
|
// @ts-expect-error `./open-next.config.mjs` exists only in the build output
|
|
7
7
|
const config = await import("./open-next.config.mjs").then((m) => m.default);
|
|
@@ -16,6 +16,9 @@ export async function createMainHandler() {
|
|
|
16
16
|
globalThis.queue = await resolveQueue(thisFunction.override?.queue);
|
|
17
17
|
globalThis.incrementalCache = await resolveIncrementalCache(thisFunction.override?.incrementalCache);
|
|
18
18
|
globalThis.tagCache = await resolveTagCache(thisFunction.override?.tagCache);
|
|
19
|
+
if (config.middleware?.external !== true) {
|
|
20
|
+
globalThis.assetResolver = await resolveAssetResolver(globalThis.openNextConfig.middleware?.assetResolver);
|
|
21
|
+
}
|
|
19
22
|
globalThis.proxyExternalRequest = await resolveProxyRequest(thisFunction.override?.proxyExternalRequest);
|
|
20
23
|
globalThis.cdnInvalidationHandler = await resolveCdnInvalidation(thisFunction.override?.cdnInvalidation);
|
|
21
24
|
// From the config, we create the converter
|
|
@@ -5,7 +5,7 @@ import { NextConfig } from "../adapters/config/index";
|
|
|
5
5
|
import { debug, error } from "../adapters/logger";
|
|
6
6
|
import { patchAsyncStorage } from "./patchAsyncStorage";
|
|
7
7
|
import { constructNextUrl, convertRes, convertToQuery, createServerResponse, } from "./routing/util";
|
|
8
|
-
import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
|
|
8
|
+
import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
|
|
9
9
|
import { requestHandler, setNextjsPrebundledReact } from "./util";
|
|
10
10
|
// This is used to identify requests in the cache
|
|
11
11
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
@@ -14,10 +14,17 @@ patchAsyncStorage();
|
|
|
14
14
|
//#endOverride
|
|
15
15
|
export async function openNextHandler(internalEvent, options) {
|
|
16
16
|
const initialHeaders = internalEvent.headers;
|
|
17
|
+
// We only use the requestId header if we are using an external middleware
|
|
18
|
+
// This is to ensure that no one can spoof the requestId
|
|
19
|
+
// When using an external middleware, we always assume that headers cannot be spoofed
|
|
20
|
+
const requestId = globalThis.openNextConfig.middleware?.external
|
|
21
|
+
? internalEvent.headers[INTERNAL_EVENT_REQUEST_ID]
|
|
22
|
+
: Math.random().toString(36);
|
|
17
23
|
// We run everything in the async local storage context so that it is available in the middleware as well as in NextServer
|
|
18
24
|
return runWithOpenNextRequestContext({
|
|
19
25
|
isISRRevalidation: initialHeaders["x-isr"] === "1",
|
|
20
26
|
waitUntil: options?.waitUntil,
|
|
27
|
+
requestId,
|
|
21
28
|
}, async () => {
|
|
22
29
|
await globalThis.__next_route_preloader("waitUntil");
|
|
23
30
|
if (initialHeaders["x-forwarded-host"]) {
|
|
@@ -40,7 +47,9 @@ export async function openNextHandler(internalEvent, options) {
|
|
|
40
47
|
...internalHeaders,
|
|
41
48
|
};
|
|
42
49
|
//#override withRouting
|
|
43
|
-
routingResult = await routingHandler(internalEvent
|
|
50
|
+
routingResult = await routingHandler(internalEvent, {
|
|
51
|
+
assetResolver: globalThis.assetResolver,
|
|
52
|
+
});
|
|
44
53
|
//#endOverride
|
|
45
54
|
const headers = "type" in routingResult
|
|
46
55
|
? routingResult.headers
|
package/dist/core/resolve.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export declare function resolveImageLoader(imageLoader: RemoveUndefined<OpenNext
|
|
|
35
35
|
* @__PURE__
|
|
36
36
|
*/
|
|
37
37
|
export declare function resolveOriginResolver(originResolver: RemoveUndefined<OpenNextConfig["middleware"]>["originResolver"]): Promise<import("../types/overrides").OriginResolver>;
|
|
38
|
+
/**
|
|
39
|
+
* @returns
|
|
40
|
+
* @__PURE__
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveAssetResolver(assetResolver: RemoveUndefined<OpenNextConfig["middleware"]>["assetResolver"]): Promise<import("../types/overrides").AssetResolver>;
|
|
38
43
|
/**
|
|
39
44
|
* @__PURE__
|
|
40
45
|
*/
|
package/dist/core/resolve.js
CHANGED
|
@@ -78,6 +78,17 @@ export async function resolveOriginResolver(originResolver) {
|
|
|
78
78
|
const m_1 = await import("../overrides/originResolver/pattern-env.js");
|
|
79
79
|
return m_1.default;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* @returns
|
|
83
|
+
* @__PURE__
|
|
84
|
+
*/
|
|
85
|
+
export async function resolveAssetResolver(assetResolver) {
|
|
86
|
+
if (typeof assetResolver === "function") {
|
|
87
|
+
return assetResolver();
|
|
88
|
+
}
|
|
89
|
+
const m_1 = await import("../overrides/assetResolver/dummy.js");
|
|
90
|
+
return m_1.default;
|
|
91
|
+
}
|
|
81
92
|
/**
|
|
82
93
|
* @__PURE__
|
|
83
94
|
*/
|
|
@@ -7,6 +7,16 @@ import { localizePath } from "./i18n";
|
|
|
7
7
|
import { generateMessageGroupId } from "./queue";
|
|
8
8
|
const CACHE_ONE_YEAR = 60 * 60 * 24 * 365;
|
|
9
9
|
const CACHE_ONE_MONTH = 60 * 60 * 24 * 30;
|
|
10
|
+
/*
|
|
11
|
+
* We use this header to prevent Firefox (and possibly some CDNs) from incorrectly reusing the RSC responses during caching.
|
|
12
|
+
* This can especially happen when there's a redirect in the middleware as the `_rsc` query parameter is not visible there.
|
|
13
|
+
* So it will get dropped during the redirect, which results in the RSC response being cached instead of the actual HTML on the path `/`.
|
|
14
|
+
* This value can be found in the routes manifest, under `rsc.varyHeader`.
|
|
15
|
+
* They recompute it here in Next:
|
|
16
|
+
* https://github.com/vercel/next.js/blob/c5bf5bb4c8b01b1befbbfa7ad97a97476ee9d0d7/packages/next/src/server/base-server.ts#L2011
|
|
17
|
+
* Also see this PR: https://github.com/vercel/next.js/pull/79426
|
|
18
|
+
*/
|
|
19
|
+
const VARY_HEADER = "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Next-Url";
|
|
10
20
|
async function computeCacheControl(path, body, host, revalidate, lastModified) {
|
|
11
21
|
let finalRevalidate = CACHE_ONE_YEAR;
|
|
12
22
|
const existingRoute = Object.entries(PrerenderManifest.routes).find((p) => p[0] === path)?.[1];
|
|
@@ -100,6 +110,7 @@ async function generateResult(event, localizedPath, cachedValue, lastModified) {
|
|
|
100
110
|
...cacheControl,
|
|
101
111
|
"content-type": type,
|
|
102
112
|
...cachedValue.meta?.headers,
|
|
113
|
+
vary: VARY_HEADER,
|
|
103
114
|
},
|
|
104
115
|
};
|
|
105
116
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Header, PrerenderManifest, RedirectDefinition, RewriteDefinition } from "../../types/next-types";
|
|
2
2
|
import type { InternalEvent, InternalResult } from "../../types/open-next";
|
|
3
3
|
export declare function getNextConfigHeaders(event: InternalEvent, configHeaders?: Header[] | undefined): Record<string, string | undefined>;
|
|
4
|
+
/**
|
|
5
|
+
* TODO: This method currently only check for the first match.
|
|
6
|
+
* It should check for all matches for `beforeFiles` and `afterFiles` rewrite
|
|
7
|
+
* See https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites
|
|
8
|
+
*/
|
|
4
9
|
export declare function handleRewrites<T extends RewriteDefinition>(event: InternalEvent, rewrites: T[]): {
|
|
5
10
|
internalEvent: {
|
|
6
11
|
query: Record<string, string | string[]>;
|
|
@@ -97,6 +97,11 @@ export function getNextConfigHeaders(event, configHeaders) {
|
|
|
97
97
|
}
|
|
98
98
|
return requestHeaders;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* TODO: This method currently only check for the first match.
|
|
102
|
+
* It should check for all matches for `beforeFiles` and `afterFiles` rewrite
|
|
103
|
+
* See https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites
|
|
104
|
+
*/
|
|
100
105
|
export function handleRewrites(event, rewrites) {
|
|
101
106
|
const { rawPath, headers, query, cookies, url } = event;
|
|
102
107
|
const localizedRawPath = localizePath(event);
|
|
@@ -233,6 +233,8 @@ export function addOpenNextHeader(headers) {
|
|
|
233
233
|
}
|
|
234
234
|
if (globalThis.openNextDebug) {
|
|
235
235
|
headers["X-OpenNext-Version"] = globalThis.openNextVersion;
|
|
236
|
+
}
|
|
237
|
+
if (process.env.OPEN_NEXT_REQUEST_ID_HEADER || globalThis.openNextDebug) {
|
|
236
238
|
headers["X-OpenNext-RequestId"] =
|
|
237
239
|
globalThis.__openNextAls.getStore()?.requestId;
|
|
238
240
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { InternalEvent, InternalResult, RoutingResult } from "../types/open-next";
|
|
2
|
+
import type { AssetResolver } from "../types/overrides";
|
|
2
3
|
export declare const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
|
|
3
4
|
export declare const MIDDLEWARE_HEADER_PREFIX_LEN: number;
|
|
4
5
|
export declare const INTERNAL_HEADER_PREFIX = "x-opennext-";
|
|
5
6
|
export declare const INTERNAL_HEADER_INITIAL_URL = "x-opennext-initial-url";
|
|
6
7
|
export declare const INTERNAL_HEADER_LOCALE = "x-opennext-locale";
|
|
7
8
|
export declare const INTERNAL_HEADER_RESOLVED_ROUTES = "x-opennext-resolved-routes";
|
|
8
|
-
export
|
|
9
|
+
export declare const INTERNAL_EVENT_REQUEST_ID = "x-opennext-request-id";
|
|
10
|
+
export default function routingHandler(event: InternalEvent, { assetResolver }: {
|
|
11
|
+
assetResolver?: AssetResolver;
|
|
12
|
+
}): Promise<InternalResult | RoutingResult>;
|
|
@@ -12,6 +12,7 @@ export const INTERNAL_HEADER_PREFIX = "x-opennext-";
|
|
|
12
12
|
export const INTERNAL_HEADER_INITIAL_URL = `${INTERNAL_HEADER_PREFIX}initial-url`;
|
|
13
13
|
export const INTERNAL_HEADER_LOCALE = `${INTERNAL_HEADER_PREFIX}locale`;
|
|
14
14
|
export const INTERNAL_HEADER_RESOLVED_ROUTES = `${INTERNAL_HEADER_PREFIX}resolved-routes`;
|
|
15
|
+
export const INTERNAL_EVENT_REQUEST_ID = `${INTERNAL_HEADER_PREFIX}request-id`;
|
|
15
16
|
// Geolocation headers starting from Nextjs 15
|
|
16
17
|
// See https://github.com/vercel/vercel/blob/7714b1c/packages/functions/src/headers.ts
|
|
17
18
|
const geoHeaderToNextHeader = {
|
|
@@ -21,17 +22,25 @@ const geoHeaderToNextHeader = {
|
|
|
21
22
|
"x-open-next-latitude": "x-vercel-ip-latitude",
|
|
22
23
|
"x-open-next-longitude": "x-vercel-ip-longitude",
|
|
23
24
|
};
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Adds the middleware headers to an event or result.
|
|
27
|
+
*
|
|
28
|
+
* @param eventOrResult
|
|
29
|
+
* @param middlewareHeaders
|
|
30
|
+
*/
|
|
31
|
+
function applyMiddlewareHeaders(eventOrResult, middlewareHeaders) {
|
|
32
|
+
// Use the `MIDDLEWARE_HEADER_PREFIX` prefix for events, they will be processed by the request handler later.
|
|
33
|
+
// Results do not go through the request handler and should not be prefixed.
|
|
34
|
+
const isResult = isInternalResult(eventOrResult);
|
|
35
|
+
const headers = eventOrResult.headers;
|
|
36
|
+
const keyPrefix = isResult ? "" : MIDDLEWARE_HEADER_PREFIX;
|
|
26
37
|
Object.entries(middlewareHeaders).forEach(([key, value]) => {
|
|
27
38
|
if (value) {
|
|
28
|
-
|
|
29
|
-
? value.join(",")
|
|
30
|
-
: value;
|
|
39
|
+
headers[keyPrefix + key] = Array.isArray(value) ? value.join(",") : value;
|
|
31
40
|
}
|
|
32
41
|
});
|
|
33
42
|
}
|
|
34
|
-
export default async function routingHandler(event) {
|
|
43
|
+
export default async function routingHandler(event, { assetResolver }) {
|
|
35
44
|
try {
|
|
36
45
|
// Add Next geo headers
|
|
37
46
|
for (const [openNextGeoName, nextGeoName] of Object.entries(geoHeaderToNextHeader)) {
|
|
@@ -48,12 +57,13 @@ export default async function routingHandler(event) {
|
|
|
48
57
|
delete event.headers[key];
|
|
49
58
|
}
|
|
50
59
|
}
|
|
51
|
-
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
// Headers from the Next config and middleware (the later are applied further down).
|
|
61
|
+
let headers = getNextConfigHeaders(event, ConfigHeaders);
|
|
62
|
+
let eventOrResult = fixDataPage(event, BuildId);
|
|
63
|
+
if (isInternalResult(eventOrResult)) {
|
|
64
|
+
return eventOrResult;
|
|
55
65
|
}
|
|
56
|
-
const redirect = handleRedirects(
|
|
66
|
+
const redirect = handleRedirects(eventOrResult, RoutesManifest.redirects);
|
|
57
67
|
if (redirect) {
|
|
58
68
|
// We need to encode the value in the Location header to make sure it is valid according to RFC
|
|
59
69
|
// https://stackoverflow.com/a/7654605/16587222
|
|
@@ -61,99 +71,105 @@ export default async function routingHandler(event) {
|
|
|
61
71
|
debug("redirect", redirect);
|
|
62
72
|
return redirect;
|
|
63
73
|
}
|
|
64
|
-
const
|
|
74
|
+
const middlewareEventOrResult = await handleMiddleware(eventOrResult,
|
|
65
75
|
// We need to pass the initial search without any decoding
|
|
66
76
|
// TODO: we'd need to refactor InternalEvent to include the initial querystring directly
|
|
67
77
|
// Should be done in another PR because it is a breaking change
|
|
68
78
|
new URL(event.url).search);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return eventOrResult;
|
|
79
|
+
if (isInternalResult(middlewareEventOrResult)) {
|
|
80
|
+
return middlewareEventOrResult;
|
|
72
81
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
headers = {
|
|
83
|
+
...middlewareEventOrResult.responseHeaders,
|
|
84
|
+
...headers,
|
|
85
|
+
};
|
|
86
|
+
let isExternalRewrite = middlewareEventOrResult.isExternalRewrite ?? false;
|
|
87
|
+
eventOrResult = middlewareEventOrResult;
|
|
77
88
|
if (!isExternalRewrite) {
|
|
78
89
|
// First rewrite to be applied
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
isExternalRewrite =
|
|
90
|
+
const beforeRewrite = handleRewrites(eventOrResult, RoutesManifest.rewrites.beforeFiles);
|
|
91
|
+
eventOrResult = beforeRewrite.internalEvent;
|
|
92
|
+
isExternalRewrite = beforeRewrite.isExternalRewrite;
|
|
93
|
+
// Check for matching public files after `beforeFiles` rewrites
|
|
94
|
+
// See:
|
|
95
|
+
// - https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order
|
|
96
|
+
// - https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites
|
|
97
|
+
if (!isExternalRewrite) {
|
|
98
|
+
const assetResult = await assetResolver?.maybeGetAssetResult?.(eventOrResult);
|
|
99
|
+
if (assetResult) {
|
|
100
|
+
applyMiddlewareHeaders(assetResult, headers);
|
|
101
|
+
return assetResult;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
82
104
|
}
|
|
83
|
-
const foundStaticRoute = staticRouteMatcher(
|
|
105
|
+
const foundStaticRoute = staticRouteMatcher(eventOrResult.rawPath);
|
|
84
106
|
const isStaticRoute = !isExternalRewrite && foundStaticRoute.length > 0;
|
|
85
107
|
if (!(isStaticRoute || isExternalRewrite)) {
|
|
86
108
|
// Second rewrite to be applied
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
isExternalRewrite =
|
|
109
|
+
const afterRewrite = handleRewrites(eventOrResult, RoutesManifest.rewrites.afterFiles);
|
|
110
|
+
eventOrResult = afterRewrite.internalEvent;
|
|
111
|
+
isExternalRewrite = afterRewrite.isExternalRewrite;
|
|
90
112
|
}
|
|
91
113
|
let isISR = false;
|
|
92
114
|
// We want to run this just before the dynamic route check
|
|
93
115
|
// We can skip it if its an external rewrite
|
|
94
116
|
if (!isExternalRewrite) {
|
|
95
|
-
const fallbackResult = handleFallbackFalse(
|
|
96
|
-
|
|
117
|
+
const fallbackResult = handleFallbackFalse(eventOrResult, PrerenderManifest);
|
|
118
|
+
eventOrResult = fallbackResult.event;
|
|
97
119
|
isISR = fallbackResult.isISR;
|
|
98
120
|
}
|
|
99
|
-
const foundDynamicRoute = dynamicRouteMatcher(
|
|
121
|
+
const foundDynamicRoute = dynamicRouteMatcher(eventOrResult.rawPath);
|
|
100
122
|
const isDynamicRoute = !isExternalRewrite && foundDynamicRoute.length > 0;
|
|
101
123
|
if (!(isDynamicRoute || isStaticRoute || isExternalRewrite)) {
|
|
102
124
|
// Fallback rewrite to be applied
|
|
103
|
-
const fallbackRewrites = handleRewrites(
|
|
104
|
-
|
|
125
|
+
const fallbackRewrites = handleRewrites(eventOrResult, RoutesManifest.rewrites.fallback);
|
|
126
|
+
eventOrResult = fallbackRewrites.internalEvent;
|
|
105
127
|
isExternalRewrite = fallbackRewrites.isExternalRewrite;
|
|
106
128
|
}
|
|
107
|
-
const isNextImageRoute =
|
|
129
|
+
const isNextImageRoute = eventOrResult.rawPath.startsWith("/_next/image");
|
|
108
130
|
const isRouteFoundBeforeAllRewrites = isStaticRoute || isDynamicRoute || isExternalRewrite;
|
|
109
131
|
// If we still haven't found a route, we show the 404 page
|
|
110
132
|
// We need to ensure that rewrites are applied before showing the 404 page
|
|
111
133
|
if (!(isRouteFoundBeforeAllRewrites ||
|
|
112
134
|
isNextImageRoute ||
|
|
113
135
|
// We need to check again once all rewrites have been applied
|
|
114
|
-
staticRouteMatcher(
|
|
115
|
-
dynamicRouteMatcher(
|
|
116
|
-
|
|
117
|
-
...
|
|
136
|
+
staticRouteMatcher(eventOrResult.rawPath).length > 0 ||
|
|
137
|
+
dynamicRouteMatcher(eventOrResult.rawPath).length > 0)) {
|
|
138
|
+
eventOrResult = {
|
|
139
|
+
...eventOrResult,
|
|
118
140
|
rawPath: "/404",
|
|
119
|
-
url: constructNextUrl(
|
|
141
|
+
url: constructNextUrl(eventOrResult.url, "/404"),
|
|
120
142
|
headers: {
|
|
121
|
-
...
|
|
143
|
+
...eventOrResult.headers,
|
|
122
144
|
"x-middleware-response-cache-control": "private, no-cache, no-store, max-age=0, must-revalidate",
|
|
123
145
|
},
|
|
124
146
|
};
|
|
125
147
|
}
|
|
126
148
|
if (globalThis.openNextConfig.dangerous?.enableCacheInterception &&
|
|
127
|
-
!(
|
|
149
|
+
!isInternalResult(eventOrResult)) {
|
|
128
150
|
debug("Cache interception enabled");
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
applyMiddlewareHeaders(
|
|
132
|
-
|
|
133
|
-
...nextHeaders,
|
|
134
|
-
}, false);
|
|
135
|
-
return internalEvent;
|
|
151
|
+
eventOrResult = await cacheInterceptor(eventOrResult);
|
|
152
|
+
if (isInternalResult(eventOrResult)) {
|
|
153
|
+
applyMiddlewareHeaders(eventOrResult, headers);
|
|
154
|
+
return eventOrResult;
|
|
136
155
|
}
|
|
137
156
|
}
|
|
138
157
|
// We apply the headers from the middleware response last
|
|
139
|
-
applyMiddlewareHeaders(
|
|
140
|
-
...middlewareResponseHeaders,
|
|
141
|
-
...nextHeaders,
|
|
142
|
-
});
|
|
158
|
+
applyMiddlewareHeaders(eventOrResult, headers);
|
|
143
159
|
const resolvedRoutes = [
|
|
144
160
|
...foundStaticRoute,
|
|
145
161
|
...foundDynamicRoute,
|
|
146
162
|
];
|
|
147
163
|
debug("resolvedRoutes", resolvedRoutes);
|
|
148
164
|
return {
|
|
149
|
-
internalEvent,
|
|
165
|
+
internalEvent: eventOrResult,
|
|
150
166
|
isExternalRewrite,
|
|
151
167
|
origin: false,
|
|
152
168
|
isISR,
|
|
153
169
|
resolvedRoutes,
|
|
154
170
|
initialURL: event.url,
|
|
155
171
|
locale: NextConfig.i18n
|
|
156
|
-
? detectLocale(
|
|
172
|
+
? detectLocale(eventOrResult, NextConfig.i18n)
|
|
157
173
|
: undefined,
|
|
158
174
|
};
|
|
159
175
|
}
|
|
@@ -184,3 +200,10 @@ export default async function routingHandler(event) {
|
|
|
184
200
|
};
|
|
185
201
|
}
|
|
186
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* @param eventOrResult
|
|
205
|
+
* @returns Whether the event is an instance of `InternalResult`
|
|
206
|
+
*/
|
|
207
|
+
function isInternalResult(eventOrResult) {
|
|
208
|
+
return eventOrResult != null && "statusCode" in eventOrResult;
|
|
209
|
+
}
|
package/dist/types/global.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import type { OutgoingHttpHeaders } from "node:http";
|
|
3
|
-
import type { CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
|
|
3
|
+
import type { AssetResolver, CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
|
|
4
4
|
import type { DetachedPromiseRunner } from "../utils/promise";
|
|
5
5
|
import type { OpenNextConfig, WaitUntil } from "./open-next";
|
|
6
6
|
export interface RequestData {
|
|
@@ -53,6 +53,8 @@ interface OpenNextRequestContext {
|
|
|
53
53
|
mergeHeadersPriority?: "middleware" | "handler";
|
|
54
54
|
lastModified?: number;
|
|
55
55
|
waitUntil?: WaitUntil;
|
|
56
|
+
/** We use this to deduplicate write of the tags*/
|
|
57
|
+
writtenTags: Set<string>;
|
|
56
58
|
}
|
|
57
59
|
declare global {
|
|
58
60
|
/**
|
|
@@ -179,6 +181,12 @@ declare global {
|
|
|
179
181
|
* Defined in `createMainHandler`
|
|
180
182
|
*/
|
|
181
183
|
var cdnInvalidationHandler: CDNInvalidationHandler;
|
|
184
|
+
/**
|
|
185
|
+
* The function called to resolve assets.
|
|
186
|
+
* Available in main functions
|
|
187
|
+
* Defined in `createMainHandler` when the middleware is internal
|
|
188
|
+
*/
|
|
189
|
+
var assetResolver: AssetResolver | undefined;
|
|
182
190
|
/**
|
|
183
191
|
* A function to preload the routes.
|
|
184
192
|
* This needs to be defined on globalThis because it can be used by custom overrides.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReadableStream } from "node:stream/web";
|
|
2
2
|
import type { Writable } from "node:stream";
|
|
3
3
|
import type { WarmerEvent, WarmerResponse } from "../adapters/warmer-function";
|
|
4
|
-
import type { CDNInvalidationHandler, Converter, ImageLoader, IncrementalCache, OriginResolver, ProxyExternalRequest, Queue, TagCache, Warmer, Wrapper } from "./overrides";
|
|
4
|
+
import type { AssetResolver, CDNInvalidationHandler, Converter, ImageLoader, IncrementalCache, OriginResolver, ProxyExternalRequest, Queue, TagCache, Warmer, Wrapper } from "./overrides";
|
|
5
5
|
export type BaseEventOrResult<T extends string = string> = {
|
|
6
6
|
type: T;
|
|
7
7
|
};
|
|
@@ -103,6 +103,7 @@ export type IncludedOriginResolver = "pattern-env" | "dummy";
|
|
|
103
103
|
export type IncludedWarmer = "aws-lambda" | "dummy";
|
|
104
104
|
export type IncludedProxyExternalRequest = "node" | "fetch" | "dummy";
|
|
105
105
|
export type IncludedCDNInvalidationHandler = "cloudfront" | "dummy";
|
|
106
|
+
export type IncludedAssetResolver = "dummy";
|
|
106
107
|
export interface DefaultOverrideOptions<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> {
|
|
107
108
|
/**
|
|
108
109
|
* This is the main entrypoint of your app.
|
|
@@ -273,6 +274,12 @@ export interface OpenNextConfig {
|
|
|
273
274
|
* @default "pattern-env"
|
|
274
275
|
*/
|
|
275
276
|
originResolver?: IncludedOriginResolver | LazyLoadedOverride<OriginResolver>;
|
|
277
|
+
/**
|
|
278
|
+
* The assetResolver is used to resolve assets in the routing layer.
|
|
279
|
+
*
|
|
280
|
+
* @default "dummy"
|
|
281
|
+
*/
|
|
282
|
+
assetResolver?: IncludedAssetResolver | LazyLoadedOverride<AssetResolver>;
|
|
276
283
|
};
|
|
277
284
|
/**
|
|
278
285
|
* Override the default warmer
|
|
@@ -15,6 +15,19 @@ export interface Queue {
|
|
|
15
15
|
send(message: QueueMessage): Promise<void>;
|
|
16
16
|
name: string;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolves assets in the routing layer.
|
|
20
|
+
*/
|
|
21
|
+
export interface AssetResolver {
|
|
22
|
+
name: string;
|
|
23
|
+
/**
|
|
24
|
+
* Called by the routing layer to check for a matching static asset.
|
|
25
|
+
*
|
|
26
|
+
* @param event
|
|
27
|
+
* @returns an `InternalResult` when an asset is found a the path from the event, undefined otherwise.
|
|
28
|
+
*/
|
|
29
|
+
maybeGetAssetResult?: (event: InternalEvent) => Promise<InternalResult | undefined> | undefined;
|
|
30
|
+
}
|
|
18
31
|
export type CachedFile = {
|
|
19
32
|
type: "redirect";
|
|
20
33
|
props?: Object;
|
|
@@ -95,6 +108,11 @@ export type NextModeTagCache = BaseTagCache & {
|
|
|
95
108
|
writeTags(tags: string[]): Promise<void>;
|
|
96
109
|
getPathsByTags?: (tags: string[]) => Promise<string[]>;
|
|
97
110
|
};
|
|
111
|
+
export interface OriginalTagCacheWriteInput {
|
|
112
|
+
tag: string;
|
|
113
|
+
path: string;
|
|
114
|
+
revalidatedAt?: number;
|
|
115
|
+
}
|
|
98
116
|
/**
|
|
99
117
|
* On get :
|
|
100
118
|
We just check for the cache key in the tag cache. If it has been revalidated we just return null, otherwise we continue
|
|
@@ -119,11 +137,7 @@ export type OriginalTagCache = BaseTagCache & {
|
|
|
119
137
|
getByTag(tag: string): Promise<string[]>;
|
|
120
138
|
getByPath(path: string): Promise<string[]>;
|
|
121
139
|
getLastModified(path: string, lastModified?: number): Promise<number>;
|
|
122
|
-
writeTags(tags:
|
|
123
|
-
tag: string;
|
|
124
|
-
path: string;
|
|
125
|
-
revalidatedAt?: number;
|
|
126
|
-
}[]): Promise<void>;
|
|
140
|
+
writeTags(tags: OriginalTagCacheWriteInput[]): Promise<void>;
|
|
127
141
|
};
|
|
128
142
|
export type TagCache = NextModeTagCache | OriginalTagCache;
|
|
129
143
|
export type WrapperHandler<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> = (handler: OpenNextHandler<E, R>, converter: Converter<E, R>) => Promise<(...args: any[]) => any>;
|
package/dist/utils/cache.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { CacheValue, WithLastModified } from "../types/overrides";
|
|
1
|
+
import type { CacheValue, OriginalTagCacheWriteInput, WithLastModified } from "../types/overrides";
|
|
2
2
|
export declare function hasBeenRevalidated(key: string, tags: string[], cacheEntry: WithLastModified<CacheValue<any>>): Promise<boolean>;
|
|
3
3
|
export declare function getTagsFromValue(value?: CacheValue<"cache">): string[];
|
|
4
|
+
export declare function writeTags(tags: (string | OriginalTagCacheWriteInput)[]): Promise<void>;
|
package/dist/utils/cache.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { debug } from "../adapters/logger";
|
|
1
2
|
export async function hasBeenRevalidated(key, tags, cacheEntry) {
|
|
2
3
|
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
3
4
|
return false;
|
|
@@ -30,3 +31,34 @@ export function getTagsFromValue(value) {
|
|
|
30
31
|
return [];
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
function getTagKey(tag) {
|
|
35
|
+
if (typeof tag === "string") {
|
|
36
|
+
return tag;
|
|
37
|
+
}
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
tag: tag.tag,
|
|
40
|
+
path: tag.path,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export async function writeTags(tags) {
|
|
44
|
+
const store = globalThis.__openNextAls.getStore();
|
|
45
|
+
debug("Writing tags", tags, store);
|
|
46
|
+
if (!store || globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const tagsToWrite = tags.filter((t) => {
|
|
50
|
+
const tagKey = getTagKey(t);
|
|
51
|
+
const shouldWrite = !store.writtenTags.has(tagKey);
|
|
52
|
+
// We preemptively add the tag to the writtenTags set
|
|
53
|
+
// to avoid writing the same tag multiple times in the same request
|
|
54
|
+
if (shouldWrite) {
|
|
55
|
+
store.writtenTags.add(tagKey);
|
|
56
|
+
}
|
|
57
|
+
return shouldWrite;
|
|
58
|
+
});
|
|
59
|
+
if (tagsToWrite.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Here we know that we have the correct type
|
|
63
|
+
await globalThis.tagCache.writeTags(tagsToWrite);
|
|
64
|
+
}
|
package/dist/utils/promise.d.ts
CHANGED
|
@@ -17,7 +17,8 @@ export declare class DetachedPromiseRunner {
|
|
|
17
17
|
add<T>(promise: Promise<T>): void;
|
|
18
18
|
await(): Promise<void>;
|
|
19
19
|
}
|
|
20
|
-
export declare function runWithOpenNextRequestContext<T>({ isISRRevalidation, waitUntil, }: {
|
|
20
|
+
export declare function runWithOpenNextRequestContext<T>({ isISRRevalidation, waitUntil, requestId, }: {
|
|
21
21
|
isISRRevalidation: boolean;
|
|
22
22
|
waitUntil?: WaitUntil;
|
|
23
|
+
requestId?: string;
|
|
23
24
|
}, fn: () => Promise<T>): Promise<T>;
|
package/dist/utils/promise.js
CHANGED
|
@@ -77,12 +77,13 @@ function provideNextAfterProvider() {
|
|
|
77
77
|
globalThis[VERCEL_REQUEST_CONTEXT_SYMBOL] = nextAfterContext;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
export function runWithOpenNextRequestContext({ isISRRevalidation, waitUntil, }, fn) {
|
|
80
|
+
export function runWithOpenNextRequestContext({ isISRRevalidation, waitUntil, requestId = Math.random().toString(36), }, fn) {
|
|
81
81
|
return globalThis.__openNextAls.run({
|
|
82
|
-
requestId
|
|
82
|
+
requestId,
|
|
83
83
|
pendingPromiseRunner: new DetachedPromiseRunner(),
|
|
84
84
|
isISRRevalidation,
|
|
85
85
|
waitUntil,
|
|
86
|
+
writtenTags: new Set(),
|
|
86
87
|
}, async () => {
|
|
87
88
|
provideNextAfterProvider();
|
|
88
89
|
let result;
|