Skip to content

Commit bff8e95

Browse files
committed
refactor
1 parent 26b4946 commit bff8e95

File tree

5 files changed

+77
-105
lines changed

5 files changed

+77
-105
lines changed

packages/open-next/src/adapters/edge-adapter.ts

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import type { ReadableStream } from "node:stream/web";
22

33
import type { InternalEvent, InternalResult } from "types/open-next";
4-
import {
5-
awaitAllDetachedPromise,
6-
provideNextAfterProvider,
7-
} from "utils/promise";
4+
import { runWithOpenNextRequestContext } from "utils/promise";
85
import { emptyReadableStream } from "utils/stream";
96

107
// We import it like that so that the edge plugin can replace it
@@ -14,7 +11,6 @@ import {
1411
convertBodyToReadableStream,
1512
convertToQueryString,
1613
} from "../core/routing/util";
17-
import { generateOpenNextRequestContext } from "./util";
1814

1915
globalThis.__als = new AsyncLocalStorage();
2016

@@ -23,64 +19,55 @@ const defaultHandler = async (
2319
): Promise<InternalResult> => {
2420
globalThis.isEdgeRuntime = true;
2521

26-
const { requestId, pendingPromiseRunner, isISRRevalidation } =
27-
generateOpenNextRequestContext();
28-
2922
// We run everything in the async local storage context so that it is available in edge runtime functions
30-
return globalThis.__als.run(
31-
{ requestId, pendingPromiseRunner, isISRRevalidation },
32-
async () => {
33-
provideNextAfterProvider();
34-
const host = internalEvent.headers.host
35-
? `https://${internalEvent.headers.host}`
36-
: "http://localhost:3000";
37-
const initialUrl = new URL(internalEvent.rawPath, host);
38-
initialUrl.search = convertToQueryString(internalEvent.query);
39-
const url = initialUrl.toString();
40-
41-
// @ts-expect-error - This is bundled
42-
const handler = await import(`./middleware.mjs`);
23+
return runWithOpenNextRequestContext(false, async () => {
24+
const host = internalEvent.headers.host
25+
? `https://${internalEvent.headers.host}`
26+
: "http://localhost:3000";
27+
const initialUrl = new URL(internalEvent.rawPath, host);
28+
initialUrl.search = convertToQueryString(internalEvent.query);
29+
const url = initialUrl.toString();
4330

44-
const response: Response = await handler.default({
45-
headers: internalEvent.headers,
46-
method: internalEvent.method || "GET",
47-
nextConfig: {
48-
basePath: NextConfig.basePath,
49-
i18n: NextConfig.i18n,
50-
trailingSlash: NextConfig.trailingSlash,
51-
},
52-
url,
53-
body: convertBodyToReadableStream(
54-
internalEvent.method,
55-
internalEvent.body,
56-
),
57-
});
58-
const responseHeaders: Record<string, string | string[]> = {};
59-
response.headers.forEach((value, key) => {
60-
if (key.toLowerCase() === "set-cookie") {
61-
responseHeaders[key] = responseHeaders[key]
62-
? [...responseHeaders[key], value]
63-
: [value];
64-
} else {
65-
responseHeaders[key] = value;
66-
}
67-
});
31+
// @ts-expect-error - This is bundled
32+
const handler = await import(`./middleware.mjs`);
6833

69-
const body =
70-
(response.body as ReadableStream<Uint8Array>) ?? emptyReadableStream();
34+
const response: Response = await handler.default({
35+
headers: internalEvent.headers,
36+
method: internalEvent.method || "GET",
37+
nextConfig: {
38+
basePath: NextConfig.basePath,
39+
i18n: NextConfig.i18n,
40+
trailingSlash: NextConfig.trailingSlash,
41+
},
42+
url,
43+
body: convertBodyToReadableStream(
44+
internalEvent.method,
45+
internalEvent.body,
46+
),
47+
});
48+
const responseHeaders: Record<string, string | string[]> = {};
49+
response.headers.forEach((value, key) => {
50+
if (key.toLowerCase() === "set-cookie") {
51+
responseHeaders[key] = responseHeaders[key]
52+
? [...responseHeaders[key], value]
53+
: [value];
54+
} else {
55+
responseHeaders[key] = value;
56+
}
57+
});
7158

72-
await awaitAllDetachedPromise();
59+
const body =
60+
(response.body as ReadableStream<Uint8Array>) ?? emptyReadableStream();
7361

74-
return {
75-
type: "core",
76-
statusCode: response.status,
77-
headers: responseHeaders,
78-
body: body,
79-
// Do we need to handle base64 encoded response?
80-
isBase64Encoded: false,
81-
};
82-
},
83-
);
62+
return {
63+
type: "core",
64+
statusCode: response.status,
65+
headers: responseHeaders,
66+
body: body,
67+
// Do we need to handle base64 encoded response?
68+
isBase64Encoded: false,
69+
};
70+
});
8471
};
8572

8673
export const handler = await createGenericHandler({

packages/open-next/src/adapters/middleware.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import type { InternalEvent, Origin } from "types/open-next";
2-
import {
3-
awaitAllDetachedPromise,
4-
provideNextAfterProvider,
5-
} from "utils/promise";
2+
import { runWithOpenNextRequestContext } from "utils/promise";
63

74
import { debug } from "../adapters/logger";
85
import { createGenericHandler } from "../core/createGenericHandler";
@@ -13,7 +10,6 @@ import {
1310
resolveTagCache,
1411
} from "../core/resolve";
1512
import routingHandler from "../core/routingHandler";
16-
import { generateOpenNextRequestContext } from "./util";
1713

1814
globalThis.internalFetch = fetch;
1915
globalThis.__als = new AsyncLocalStorage();
@@ -37,26 +33,17 @@ const defaultHandler = async (internalEvent: InternalEvent) => {
3733
);
3834
//#endOverride
3935

40-
const { requestId, pendingPromiseRunner, isISRRevalidation } =
41-
generateOpenNextRequestContext(internalEvent.headers["x-isr"] === "1");
42-
4336
// We run everything in the async local storage context so that it is available in the external middleware
44-
return globalThis.__als.run(
45-
{
46-
requestId,
47-
pendingPromiseRunner,
48-
isISRRevalidation,
49-
},
37+
return runWithOpenNextRequestContext(
38+
internalEvent.headers["x-isr"] === "1",
5039
async () => {
51-
provideNextAfterProvider();
5240
const result = await routingHandler(internalEvent);
5341
if ("internalEvent" in result) {
5442
debug("Middleware intercepted event", internalEvent);
5543
let origin: Origin | false = false;
5644
if (!result.isExternalRewrite) {
5745
origin = await originResolver.resolve(result.internalEvent.rawPath);
5846
}
59-
await awaitAllDetachedPromise();
6047
return {
6148
type: "middleware",
6249
internalEvent: result.internalEvent,

packages/open-next/src/adapters/util.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//TODO: We should probably move all the utils to a separate location
22

3-
import { DetachedPromiseRunner } from "utils/promise";
4-
53
export function setNodeEnv() {
64
process.env.NODE_ENV = process.env.NODE_ENV ?? "production";
75
}
@@ -10,14 +8,6 @@ export function generateUniqueId() {
108
return Math.random().toString(36).slice(2, 8);
119
}
1210

13-
export function generateOpenNextRequestContext(isISRRevalidation = false) {
14-
return {
15-
requestId: Math.random().toString(36),
16-
pendingPromiseRunner: new DetachedPromiseRunner(),
17-
isISRRevalidation,
18-
};
19-
}
20-
2111
/**
2212
* Create an array of arrays of size `chunkSize` from `items`
2313
* @param items Array of T

packages/open-next/src/core/requestHandler.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@ import { AsyncLocalStorage } from "node:async_hooks";
33
import type { OpenNextNodeResponse, StreamCreator } from "http/index.js";
44
import { IncomingMessage } from "http/index.js";
55
import type { InternalEvent, InternalResult } from "types/open-next";
6-
import {
7-
awaitAllDetachedPromise,
8-
provideNextAfterProvider,
9-
} from "utils/promise";
6+
import { runWithOpenNextRequestContext } from "utils/promise";
107

118
import { debug, error, warn } from "../adapters/logger";
12-
import { generateOpenNextRequestContext } from "../adapters/util";
139
import { patchAsyncStorage } from "./patchAsyncStorage";
1410
import { convertRes, createServerResponse, proxyRequest } from "./routing/util";
1511
import type { MiddlewareOutputEvent } from "./routingHandler";
@@ -25,22 +21,14 @@ export async function openNextHandler(
2521
internalEvent: InternalEvent,
2622
responseStreaming?: StreamCreator,
2723
): Promise<InternalResult> {
28-
const { requestId, pendingPromiseRunner, isISRRevalidation } =
29-
generateOpenNextRequestContext(internalEvent.headers["x-isr"] === "1");
30-
3124
// We run everything in the async local storage context so that it is available in the middleware as well as in NextServer
32-
return globalThis.__als.run(
33-
{
34-
requestId,
35-
pendingPromiseRunner,
36-
isISRRevalidation,
37-
},
25+
return runWithOpenNextRequestContext(
26+
internalEvent.headers["x-isr"] === "1",
3827
async () => {
3928
if (internalEvent.headers["x-forwarded-host"]) {
4029
internalEvent.headers.host = internalEvent.headers["x-forwarded-host"];
4130
}
4231
debug("internalEvent", internalEvent);
43-
provideNextAfterProvider();
4432

4533
let preprocessResult: InternalResult | MiddlewareOutputEvent = {
4634
internalEvent: internalEvent,
@@ -145,11 +133,12 @@ export async function openNextHandler(
145133
body,
146134
isBase64Encoded,
147135
};
136+
const requestId = store?.requestId;
148137

149-
// reset lastModified. We need to do this to avoid memory leaks
150-
delete globalThis.lastModified[requestId];
151-
152-
await awaitAllDetachedPromise();
138+
if (requestId) {
139+
// reset lastModified. We need to do this to avoid memory leaks
140+
delete globalThis.lastModified[requestId];
141+
}
153142

154143
return internalResult;
155144
}

packages/open-next/src/utils/promise.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class DetachedPromiseRunner {
5959
}
6060
}
6161

62-
export async function awaitAllDetachedPromise() {
62+
async function awaitAllDetachedPromise() {
6363
const promisesToAwait =
6464
globalThis.__als.getStore()?.pendingPromiseRunner.await() ??
6565
Promise.resolve();
@@ -70,7 +70,7 @@ export async function awaitAllDetachedPromise() {
7070
await promisesToAwait;
7171
}
7272

73-
export function provideNextAfterProvider() {
73+
function provideNextAfterProvider() {
7474
/** This should be considered unstable until `unstable_after` is stablized. */
7575
const NEXT_REQUEST_CONTEXT_SYMBOL = Symbol.for("@next/request-context");
7676

@@ -100,3 +100,22 @@ export function provideNextAfterProvider() {
100100
globalThis[VERCEL_REQUEST_CONTEXT_SYMBOL] = nextAfterContext;
101101
}
102102
}
103+
104+
export function runWithOpenNextRequestContext<T>(
105+
isISRRevalidation = false,
106+
fn: () => Promise<T>,
107+
): Promise<T> {
108+
return globalThis.__als.run(
109+
{
110+
requestId: Math.random().toString(36),
111+
pendingPromiseRunner: new DetachedPromiseRunner(),
112+
isISRRevalidation,
113+
},
114+
async () => {
115+
provideNextAfterProvider();
116+
const result = await fn();
117+
await awaitAllDetachedPromise();
118+
return result;
119+
},
120+
);
121+
}

0 commit comments

Comments
 (0)