@opennextjs/aws 3.4.1 → 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 +75 -37
- 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 +3 -3
- 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 +5 -7
- package/dist/build/helper.d.ts +1 -1
- package/dist/build/helper.js +9 -2
- 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 +10 -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 +17 -14
- 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/incrementalCache/multi-tier-ddb-s3.js +1 -1
- package/dist/overrides/proxyExternalRequest/fetch.js +2 -1
- 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 +10 -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 +73 -7
- 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
|
@@ -1 +1,8 @@
|
|
|
1
1
|
export declare function removeUndefinedFromQuery(query: Record<string, string | string[] | undefined>): Record<string, string | string[]>;
|
|
2
|
+
/**
|
|
3
|
+
* Extract the host from the headers (default to "on")
|
|
4
|
+
*
|
|
5
|
+
* @param headers
|
|
6
|
+
* @returns The host
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractHostFromHeaders(headers: Record<string, string>): string;
|
|
@@ -7,3 +7,12 @@ export function removeUndefinedFromQuery(query) {
|
|
|
7
7
|
}
|
|
8
8
|
return newQuery;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract the host from the headers (default to "on")
|
|
12
|
+
*
|
|
13
|
+
* @param headers
|
|
14
|
+
* @returns The host
|
|
15
|
+
*/
|
|
16
|
+
export function extractHostFromHeaders(headers) {
|
|
17
|
+
return headers["x-forwarded-host"] ?? headers.host ?? "on";
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { AwsClient } from "aws4fetch";
|
|
3
|
+
import { FatalError } from "../../utils/error";
|
|
4
|
+
let awsClient = null;
|
|
5
|
+
const { BUCKET_NAME, BUCKET_KEY_PREFIX } = process.env;
|
|
6
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
|
|
7
|
+
// If AWS_REGION is defined it will override AWS_DEFAULT_REGION
|
|
8
|
+
const BUCKET_REGION = process.env.BUCKET_REGION ??
|
|
9
|
+
process.env.AWS_REGION ??
|
|
10
|
+
process.env.AWS_DEFAULT_REGION;
|
|
11
|
+
const getAwsClient = () => {
|
|
12
|
+
if (awsClient) {
|
|
13
|
+
return awsClient;
|
|
14
|
+
}
|
|
15
|
+
awsClient = new AwsClient({
|
|
16
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
17
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
18
|
+
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
19
|
+
region: BUCKET_REGION,
|
|
20
|
+
});
|
|
21
|
+
return awsClient;
|
|
22
|
+
};
|
|
23
|
+
function ensureEnvExists() {
|
|
24
|
+
if (!(BUCKET_NAME || BUCKET_KEY_PREFIX || BUCKET_REGION)) {
|
|
25
|
+
throw new FatalError("Bucket name, region and key prefix must be defined!");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const awsFetch = async (key, options) => {
|
|
29
|
+
const client = getAwsClient();
|
|
30
|
+
const url = `https://${BUCKET_NAME}.s3.${BUCKET_REGION}.amazonaws.com/${key}`;
|
|
31
|
+
return client.fetch(url, options);
|
|
32
|
+
};
|
|
33
|
+
const s3Loader = {
|
|
34
|
+
name: "s3-lite",
|
|
35
|
+
load: async (key) => {
|
|
36
|
+
ensureEnvExists();
|
|
37
|
+
const keyPrefix = BUCKET_KEY_PREFIX?.replace(/^\/|\/$/g, "");
|
|
38
|
+
const response = await awsFetch(keyPrefix
|
|
39
|
+
? `${keyPrefix}/${key.replace(/^\//, "")}`
|
|
40
|
+
: key.replace(/^\//, ""), {
|
|
41
|
+
method: "GET",
|
|
42
|
+
});
|
|
43
|
+
if (response.status === 404) {
|
|
44
|
+
throw new FatalError("The specified key does not exist.");
|
|
45
|
+
}
|
|
46
|
+
if (response.status !== 200) {
|
|
47
|
+
throw new FatalError(`Failed to get image. Status code: ${response.status}`);
|
|
48
|
+
}
|
|
49
|
+
if (!response.body) {
|
|
50
|
+
throw new FatalError("No body in aws4fetch s3 response");
|
|
51
|
+
}
|
|
52
|
+
// We need to cast it else there will be a TypeError: o.pipe is not a function
|
|
53
|
+
const body = Readable.fromWeb(response.body);
|
|
54
|
+
return {
|
|
55
|
+
body: body,
|
|
56
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
57
|
+
cacheControl: response.headers.get("cache-control") ?? undefined,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export default s3Loader;
|
|
@@ -3,7 +3,8 @@ const fetchProxy = {
|
|
|
3
3
|
name: "fetch-proxy",
|
|
4
4
|
// @ts-ignore
|
|
5
5
|
proxy: async (internalEvent) => {
|
|
6
|
-
const { url, headers, method, body } = internalEvent;
|
|
6
|
+
const { url, headers: eventHeaders, method, body } = internalEvent;
|
|
7
|
+
const headers = Object.fromEntries(Object.entries(eventHeaders).filter(([key]) => key.toLowerCase() !== "cf-connecting-ip"));
|
|
7
8
|
const response = await fetch(url, {
|
|
8
9
|
method,
|
|
9
10
|
headers,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AwsClient } from "aws4fetch";
|
|
2
|
+
import { RecoverableError } from "../../utils/error";
|
|
3
|
+
import { customFetchClient } from "../../utils/fetch";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { debug, error } from "../../adapters/logger";
|
|
6
|
+
import { chunk, parseNumberFromEnv } from "../../adapters/util";
|
|
7
|
+
import { MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT, getDynamoBatchWriteCommandConcurrency, } from "./constants";
|
|
8
|
+
let awsClient = null;
|
|
9
|
+
const getAwsClient = () => {
|
|
10
|
+
const { CACHE_BUCKET_REGION } = process.env;
|
|
11
|
+
if (awsClient) {
|
|
12
|
+
return awsClient;
|
|
13
|
+
}
|
|
14
|
+
awsClient = new AwsClient({
|
|
15
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
16
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
17
|
+
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
18
|
+
region: CACHE_BUCKET_REGION,
|
|
19
|
+
retries: parseNumberFromEnv(process.env.AWS_SDK_S3_MAX_ATTEMPTS),
|
|
20
|
+
});
|
|
21
|
+
return awsClient;
|
|
22
|
+
};
|
|
23
|
+
const awsFetch = (body, type = "query") => {
|
|
24
|
+
const { CACHE_BUCKET_REGION } = process.env;
|
|
25
|
+
const client = getAwsClient();
|
|
26
|
+
return customFetchClient(client)(`https://dynamodb.${CACHE_BUCKET_REGION}.amazonaws.com`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
30
|
+
"X-Amz-Target": `DynamoDB_20120810.${type === "query" ? "BatchGetItem" : "BatchWriteItem"}`,
|
|
31
|
+
},
|
|
32
|
+
body,
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
function buildDynamoKey(key) {
|
|
36
|
+
const { NEXT_BUILD_ID } = process.env;
|
|
37
|
+
// FIXME: We should probably use something else than path.join here
|
|
38
|
+
// this could transform some fetch cache key into a valid path
|
|
39
|
+
return path.posix.join(NEXT_BUILD_ID ?? "", "_tag", key);
|
|
40
|
+
}
|
|
41
|
+
// We use the same key for both path and tag
|
|
42
|
+
// That's mostly for compatibility reason so that it's easier to use this with existing infra
|
|
43
|
+
// FIXME: Allow a simpler object without an unnecessary path key
|
|
44
|
+
function buildDynamoObject(tag, revalidatedAt) {
|
|
45
|
+
return {
|
|
46
|
+
path: { S: buildDynamoKey(tag) },
|
|
47
|
+
tag: { S: buildDynamoKey(tag) },
|
|
48
|
+
revalidatedAt: { N: `${revalidatedAt ?? Date.now()}` },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// This implementation does not support automatic invalidation of paths by the cdn
|
|
52
|
+
export default {
|
|
53
|
+
name: "ddb-nextMode",
|
|
54
|
+
mode: "nextMode",
|
|
55
|
+
hasBeenRevalidated: async (tags, lastModified) => {
|
|
56
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (tags.length > 100) {
|
|
60
|
+
throw new RecoverableError("Cannot query more than 100 tags at once. You should not be using this tagCache implementation for this amount of tags");
|
|
61
|
+
}
|
|
62
|
+
const { CACHE_DYNAMO_TABLE } = process.env;
|
|
63
|
+
// It's unlikely that we will have more than 100 items to query
|
|
64
|
+
// If that's the case, you should not use this tagCache implementation
|
|
65
|
+
const response = await awsFetch(JSON.stringify({
|
|
66
|
+
RequestItems: {
|
|
67
|
+
[CACHE_DYNAMO_TABLE ?? ""]: {
|
|
68
|
+
Keys: tags.map((tag) => ({
|
|
69
|
+
path: { S: buildDynamoKey(tag) },
|
|
70
|
+
tag: { S: buildDynamoKey(tag) },
|
|
71
|
+
})),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
}), "query");
|
|
75
|
+
if (response.status !== 200) {
|
|
76
|
+
throw new RecoverableError(`Failed to query dynamo item: ${response.status}`);
|
|
77
|
+
}
|
|
78
|
+
// Now we need to check for every item if lastModified is greater than the revalidatedAt
|
|
79
|
+
const { Responses } = await response.json();
|
|
80
|
+
if (!Responses) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const revalidatedTags = Responses[CACHE_DYNAMO_TABLE ?? ""].filter((item) => Number.parseInt(item.revalidatedAt.N) > (lastModified ?? 0));
|
|
84
|
+
debug("retrieved tags", revalidatedTags);
|
|
85
|
+
return revalidatedTags.length > 0;
|
|
86
|
+
},
|
|
87
|
+
writeTags: async (tags) => {
|
|
88
|
+
try {
|
|
89
|
+
const { CACHE_DYNAMO_TABLE } = process.env;
|
|
90
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const dataChunks = chunk(tags, MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT).map((Items) => ({
|
|
94
|
+
RequestItems: {
|
|
95
|
+
[CACHE_DYNAMO_TABLE ?? ""]: Items.map((tag) => ({
|
|
96
|
+
PutRequest: {
|
|
97
|
+
Item: {
|
|
98
|
+
...buildDynamoObject(tag),
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
})),
|
|
102
|
+
},
|
|
103
|
+
}));
|
|
104
|
+
const toInsert = chunk(dataChunks, getDynamoBatchWriteCommandConcurrency());
|
|
105
|
+
for (const paramsChunk of toInsert) {
|
|
106
|
+
await Promise.all(paramsChunk.map(async (params) => {
|
|
107
|
+
const response = await awsFetch(JSON.stringify(params), "batchWrite");
|
|
108
|
+
if (response.status !== 200) {
|
|
109
|
+
throw new RecoverableError(`Failed to batch write dynamo item: ${response.status}`);
|
|
110
|
+
}
|
|
111
|
+
return response;
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
error("Failed to batch write dynamo item", e);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
};
|
|
@@ -63,7 +63,7 @@ const handler = async (handler, converter) => awslambda.streamifyResponse(async
|
|
|
63
63
|
return compressedStream ?? responseStream;
|
|
64
64
|
},
|
|
65
65
|
};
|
|
66
|
-
const response = await handler(internalEvent, streamCreator);
|
|
66
|
+
const response = await handler(internalEvent, { streamCreator });
|
|
67
67
|
const isUsingEdge = globalThis.isEdgeRuntime ?? false;
|
|
68
68
|
if (isUsingEdge) {
|
|
69
69
|
debug("Headers has not been set, we must be in the edge runtime");
|
|
@@ -26,7 +26,9 @@ const handler = async (handler, converter) => async (event) => {
|
|
|
26
26
|
});
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
|
-
const response = await handler(internalEvent,
|
|
29
|
+
const response = await handler(internalEvent, {
|
|
30
|
+
streamCreator: fakeStream,
|
|
31
|
+
});
|
|
30
32
|
return converter.convertTo(response, event);
|
|
31
33
|
};
|
|
32
34
|
export default {
|
|
@@ -9,7 +9,6 @@ const cfPropNameMapping = {
|
|
|
9
9
|
};
|
|
10
10
|
const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
11
11
|
globalThis.process = process;
|
|
12
|
-
globalThis.openNextWaitUntil = ctx.waitUntil.bind(ctx);
|
|
13
12
|
// Set the environment variables
|
|
14
13
|
// Cloudflare suggests to not override the process.env object but instead apply the values to it
|
|
15
14
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -33,7 +32,9 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
33
32
|
: propValue;
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
|
-
const response = await handler(internalEvent
|
|
35
|
+
const response = await handler(internalEvent, {
|
|
36
|
+
waitUntil: ctx.waitUntil.bind(ctx),
|
|
37
|
+
});
|
|
37
38
|
const result = await converter.convertTo(response);
|
|
38
39
|
return result;
|
|
39
40
|
};
|
|
@@ -3,7 +3,6 @@ import { Writable } from "node:stream";
|
|
|
3
3
|
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
|
4
4
|
const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
5
5
|
globalThis.process = process;
|
|
6
|
-
globalThis.openNextWaitUntil = ctx.waitUntil.bind(ctx);
|
|
7
6
|
// Set the environment variables
|
|
8
7
|
// Cloudflare suggests to not override the process.env object but instead apply the values to it
|
|
9
8
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -12,12 +11,7 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
13
|
const internalEvent = await converter.convertFrom(request);
|
|
15
|
-
|
|
16
|
-
// The edge converter populate event.url with the url including the origin.
|
|
17
|
-
// This is required for middleware to keep track of the protocol (i.e. http with wrangler dev).
|
|
18
|
-
// However the server expects that the origin is not included.
|
|
19
|
-
const url = new URL(internalEvent.url);
|
|
20
|
-
internalEvent.url = url.href.slice(url.origin.length);
|
|
14
|
+
const url = new URL(request.url);
|
|
21
15
|
const { promise: promiseResponse, resolve: resolveResponse } = Promise.withResolvers();
|
|
22
16
|
const streamCreator = {
|
|
23
17
|
writeHeaders(prelude) {
|
|
@@ -26,6 +20,11 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
26
20
|
for (const cookie of cookies) {
|
|
27
21
|
responseHeaders.append("Set-Cookie", cookie);
|
|
28
22
|
}
|
|
23
|
+
// TODO(vicb): this is a workaround to make PPR work with `wrangler dev`
|
|
24
|
+
// See https://github.com/cloudflare/workers-sdk/issues/8004
|
|
25
|
+
if (url.hostname === "localhost") {
|
|
26
|
+
responseHeaders.set("Content-Encoding", "identity");
|
|
27
|
+
}
|
|
29
28
|
const { readable, writable } = new TransformStream({
|
|
30
29
|
transform(chunk, controller) {
|
|
31
30
|
controller.enqueue(Uint8Array.from(chunk.chunk ?? chunk));
|
|
@@ -40,7 +39,10 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
40
39
|
return Writable.fromWeb(writable);
|
|
41
40
|
},
|
|
42
41
|
};
|
|
43
|
-
ctx.waitUntil(handler(internalEvent,
|
|
42
|
+
ctx.waitUntil(handler(internalEvent, {
|
|
43
|
+
streamCreator,
|
|
44
|
+
waitUntil: ctx.waitUntil.bind(ctx),
|
|
45
|
+
}));
|
|
44
46
|
return promiseResponse;
|
|
45
47
|
};
|
|
46
48
|
export default {
|
|
@@ -7,24 +7,24 @@ const wrapper = async (handler, converter) => {
|
|
|
7
7
|
const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
|
|
8
8
|
app.all("/_next/image", async (req, res) => {
|
|
9
9
|
const internalEvent = await converter.convertFrom(req);
|
|
10
|
-
const
|
|
10
|
+
const streamCreator = {
|
|
11
11
|
writeHeaders: (prelude) => {
|
|
12
12
|
res.writeHead(prelude.statusCode, prelude.headers);
|
|
13
13
|
return res;
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
|
-
await imageHandler(internalEvent,
|
|
16
|
+
await imageHandler(internalEvent, { streamCreator });
|
|
17
17
|
});
|
|
18
18
|
app.all("*paths", async (req, res) => {
|
|
19
19
|
const internalEvent = await converter.convertFrom(req);
|
|
20
|
-
const
|
|
20
|
+
const streamCreator = {
|
|
21
21
|
writeHeaders: (prelude) => {
|
|
22
22
|
res.writeHead(prelude.statusCode, prelude.headers);
|
|
23
23
|
return res;
|
|
24
24
|
},
|
|
25
25
|
onFinish: () => { },
|
|
26
26
|
};
|
|
27
|
-
await handler(internalEvent,
|
|
27
|
+
await handler(internalEvent, { streamCreator });
|
|
28
28
|
});
|
|
29
29
|
const server = app.listen(Number.parseInt(process.env.PORT ?? "3000", 10), () => {
|
|
30
30
|
console.log(`Server running on port ${process.env.PORT ?? 3000}`);
|
|
@@ -3,7 +3,7 @@ import { debug, error } from "../../adapters/logger";
|
|
|
3
3
|
const wrapper = async (handler, converter) => {
|
|
4
4
|
const server = createServer(async (req, res) => {
|
|
5
5
|
const internalEvent = await converter.convertFrom(req);
|
|
6
|
-
const
|
|
6
|
+
const streamCreator = {
|
|
7
7
|
writeHeaders: (prelude) => {
|
|
8
8
|
res.setHeader("Set-Cookie", prelude.cookies);
|
|
9
9
|
res.writeHead(prelude.statusCode, prelude.headers);
|
|
@@ -19,7 +19,7 @@ const wrapper = async (handler, converter) => {
|
|
|
19
19
|
res.end("OK");
|
|
20
20
|
}
|
|
21
21
|
else {
|
|
22
|
-
await handler(internalEvent,
|
|
22
|
+
await handler(internalEvent, { streamCreator });
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
await new Promise((resolve) => {
|
package/dist/plugins/edge.d.ts
CHANGED
|
@@ -2,15 +2,13 @@ import type { Plugin } from "esbuild";
|
|
|
2
2
|
import type { MiddlewareInfo } from "../types/next-types.js";
|
|
3
3
|
export interface IPluginSettings {
|
|
4
4
|
nextDir: string;
|
|
5
|
-
edgeFunctionHandlerPath?: string;
|
|
6
5
|
middlewareInfo?: MiddlewareInfo;
|
|
7
6
|
isInCloudfare?: boolean;
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
9
|
* @param opts.nextDir - The path to the .next directory
|
|
11
|
-
* @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
|
|
12
10
|
* @param opts.middlewareInfo - Information about the middleware
|
|
13
11
|
* @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
|
|
14
12
|
* @returns
|
|
15
13
|
*/
|
|
16
|
-
export declare function openNextEdgePlugins({ nextDir,
|
|
14
|
+
export declare function openNextEdgePlugins({ nextDir, middlewareInfo, isInCloudfare, }: IPluginSettings): Plugin;
|
package/dist/plugins/edge.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
|
|
4
|
+
import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
|
|
5
5
|
import logger from "../logger.js";
|
|
6
6
|
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
7
7
|
/**
|
|
8
8
|
* @param opts.nextDir - The path to the .next directory
|
|
9
|
-
* @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
|
|
10
9
|
* @param opts.middlewareInfo - Information about the middleware
|
|
11
10
|
* @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
|
|
12
11
|
* @returns
|
|
13
12
|
*/
|
|
14
|
-
export function openNextEdgePlugins({ nextDir,
|
|
13
|
+
export function openNextEdgePlugins({ nextDir, middlewareInfo, isInCloudfare, }) {
|
|
15
14
|
const entryFiles = middlewareInfo?.files.map((file) => path.join(nextDir, file)) ?? [];
|
|
16
15
|
const routes = middlewareInfo
|
|
17
16
|
? [
|
|
@@ -27,14 +26,6 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
|
|
|
27
26
|
name: "opennext-edge",
|
|
28
27
|
setup(build) {
|
|
29
28
|
logger.debug(chalk.blue("OpenNext Edge plugin"));
|
|
30
|
-
if (edgeFunctionHandlerPath) {
|
|
31
|
-
// If we bundle the routing, we need to resolve the middleware
|
|
32
|
-
build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => {
|
|
33
|
-
return {
|
|
34
|
-
path: edgeFunctionHandlerPath,
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
29
|
build.onResolve({ filter: /\.(mjs|wasm)$/g }, () => {
|
|
39
30
|
return {
|
|
40
31
|
external: true,
|
|
@@ -129,6 +120,7 @@ ${contents}
|
|
|
129
120
|
const MiddlewareManifest = loadMiddlewareManifest(nextDir);
|
|
130
121
|
const AppPathsManifest = loadAppPathsManifest(nextDir);
|
|
131
122
|
const AppPathRoutesManifest = loadAppPathRoutesManifest(nextDir);
|
|
123
|
+
const FunctionsConfigManifest = loadFunctionsConfigManifest(nextDir);
|
|
132
124
|
const contents = `
|
|
133
125
|
import path from "node:path";
|
|
134
126
|
|
|
@@ -151,6 +143,7 @@ ${contents}
|
|
|
151
143
|
export const MiddlewareManifest = ${JSON.stringify(MiddlewareManifest)};
|
|
152
144
|
export const AppPathsManifest = ${JSON.stringify(AppPathsManifest)};
|
|
153
145
|
export const AppPathRoutesManifest = ${JSON.stringify(AppPathRoutesManifest)};
|
|
146
|
+
export const FunctionsConfigManifest = ${JSON.stringify(FunctionsConfigManifest)};
|
|
154
147
|
|
|
155
148
|
|
|
156
149
|
process.env.NEXT_BUILD_ID = BuildId;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
2
|
+
export function openNextExternalMiddlewarePlugin(functionPath) {
|
|
3
|
+
return {
|
|
4
|
+
name: "open-next-external-node-middleware",
|
|
5
|
+
setup(build) {
|
|
6
|
+
// If we bundle the routing, we need to resolve the middleware
|
|
7
|
+
build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => ({ path: functionPath }));
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
}
|
package/dist/plugins/resolve.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import logger from "../logger.js";
|
|
4
4
|
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
@@ -44,7 +44,7 @@ export function openNextResolvePlugin({ overrides, fnName, }) {
|
|
|
44
44
|
setup(build) {
|
|
45
45
|
logger.debug(chalk.blue("OpenNext Resolve plugin"), fnName ? `for ${fnName}` : "");
|
|
46
46
|
build.onLoad({ filter: getCrossPlatformPathRegex("core/resolve.js") }, async (args) => {
|
|
47
|
-
let contents =
|
|
47
|
+
let contents = await readFile(args.path, "utf-8");
|
|
48
48
|
const overridesEntries = Object.entries(overrides ?? {});
|
|
49
49
|
for (let [overrideName, overrideValue] of overridesEntries) {
|
|
50
50
|
if (!overrideValue) {
|
package/dist/types/cache.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface CachedRedirectValue {
|
|
|
16
16
|
props: Object;
|
|
17
17
|
}
|
|
18
18
|
interface CachedRouteValue {
|
|
19
|
-
kind: "ROUTE";
|
|
19
|
+
kind: "ROUTE" | "APP_ROUTE";
|
|
20
20
|
body: Buffer;
|
|
21
21
|
status: number;
|
|
22
22
|
headers: Record<string, undefined | string | string[]>;
|
|
@@ -30,13 +30,21 @@ interface CachedImageValue {
|
|
|
30
30
|
isStale?: boolean;
|
|
31
31
|
}
|
|
32
32
|
interface IncrementalCachedPageValue {
|
|
33
|
-
kind: "PAGE";
|
|
33
|
+
kind: "PAGE" | "PAGES";
|
|
34
34
|
html: string;
|
|
35
35
|
pageData: Object;
|
|
36
36
|
status?: number;
|
|
37
37
|
headers?: Record<string, undefined | string>;
|
|
38
38
|
}
|
|
39
|
-
|
|
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
|
+
export type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
|
|
40
48
|
export interface CacheHandlerContext {
|
|
41
49
|
fs?: never;
|
|
42
50
|
dev?: boolean;
|
|
@@ -54,9 +62,31 @@ export interface CacheHandlerValue {
|
|
|
54
62
|
value: IncrementalCacheValue | null;
|
|
55
63
|
}
|
|
56
64
|
export type Extension = "cache" | "fetch";
|
|
65
|
+
type MetaHeaders = {
|
|
66
|
+
"x-next-cache-tags"?: string;
|
|
67
|
+
[k: string]: string | string[] | undefined;
|
|
68
|
+
};
|
|
57
69
|
export interface Meta {
|
|
58
70
|
status?: number;
|
|
59
|
-
headers?:
|
|
71
|
+
headers?: MetaHeaders;
|
|
60
72
|
postponed?: string;
|
|
61
73
|
}
|
|
74
|
+
export type TagCacheMetaFile = {
|
|
75
|
+
tag: {
|
|
76
|
+
S: string;
|
|
77
|
+
};
|
|
78
|
+
path: {
|
|
79
|
+
S: string;
|
|
80
|
+
};
|
|
81
|
+
revalidatedAt: {
|
|
82
|
+
N: string;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
export type IncrementalCacheContext = {
|
|
86
|
+
revalidate?: number | false | undefined;
|
|
87
|
+
fetchCache?: boolean | undefined;
|
|
88
|
+
fetchUrl?: string | undefined;
|
|
89
|
+
fetchIdx?: number | undefined;
|
|
90
|
+
tags?: string[] | undefined;
|
|
91
|
+
};
|
|
62
92
|
export {};
|