@opennextjs/aws 3.3.1 → 3.4.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.js +23 -3
- package/dist/adapters/logger.js +2 -2
- package/dist/build/createAssets.js +1 -1
- package/dist/build/createImageOptimizationBundle.js +2 -1
- package/dist/build/createServerBundle.js +8 -9
- package/dist/build/edge/createEdgeBundle.js +2 -1
- package/dist/build/helper.d.ts +10 -1
- package/dist/build/helper.js +19 -3
- package/dist/build/validateConfig.js +2 -1
- package/dist/core/createMainHandler.js +2 -1
- package/dist/core/edgeFunctionHandler.js +2 -1
- package/dist/core/requestHandler.js +9 -2
- package/dist/core/resolve.d.ts +4 -0
- package/dist/core/resolve.js +10 -0
- package/dist/core/routing/middleware.js +3 -1
- package/dist/core/routing/util.d.ts +3 -2
- package/dist/core/routing/util.js +18 -1
- package/dist/overrides/cdnInvalidation/cloudfront.d.ts +9 -0
- package/dist/overrides/cdnInvalidation/cloudfront.js +26 -0
- package/dist/overrides/cdnInvalidation/dummy.d.ts +9 -0
- package/dist/overrides/cdnInvalidation/dummy.js +6 -0
- package/dist/overrides/imageLoader/fs-dev.d.ts +10 -0
- package/dist/overrides/imageLoader/fs-dev.js +14 -0
- package/dist/overrides/incrementalCache/fs-dev.d.ts +3 -0
- package/dist/overrides/incrementalCache/fs-dev.js +30 -0
- package/dist/overrides/incrementalCache/multi-tier-ddb-s3.d.ts +10 -0
- package/dist/overrides/incrementalCache/multi-tier-ddb-s3.js +120 -0
- package/dist/overrides/incrementalCache/s3-lite.d.ts +2 -0
- package/dist/overrides/incrementalCache/s3-lite.js +1 -1
- package/dist/overrides/proxyExternalRequest/node.js +6 -5
- package/dist/overrides/queue/direct.d.ts +3 -0
- package/dist/overrides/queue/direct.js +18 -0
- package/dist/overrides/tagCache/dynamodb-lite.js +8 -4
- package/dist/overrides/tagCache/dynamodb.js +8 -4
- package/dist/overrides/tagCache/fs-dev.d.ts +3 -0
- package/dist/overrides/tagCache/fs-dev.js +32 -0
- package/dist/overrides/wrappers/cloudflare-edge.js +11 -4
- package/dist/overrides/wrappers/dummy.d.ts +1 -1
- package/dist/overrides/wrappers/dummy.js +6 -2
- package/dist/overrides/wrappers/express-dev.d.ts +7 -0
- package/dist/overrides/wrappers/express-dev.js +43 -0
- package/dist/plugins/edge.js +4 -3
- package/dist/plugins/replacement.d.ts +1 -1
- package/dist/plugins/replacement.js +1 -1
- package/dist/plugins/resolve.d.ts +1 -0
- package/dist/plugins/resolve.js +4 -1
- package/dist/types/global.d.ts +19 -12
- package/dist/types/open-next.d.ts +12 -6
- package/dist/types/overrides.d.ts +10 -1
- package/dist/utils/lru.d.ts +8 -0
- package/dist/utils/lru.js +29 -0
- package/dist/utils/regex.d.ts +18 -0
- package/dist/utils/regex.js +16 -0
- package/package.json +4 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { customFetchClient } from "../../utils/fetch";
|
|
2
|
+
import { LRUCache } from "../../utils/lru";
|
|
3
|
+
import { debug } from "../../adapters/logger";
|
|
4
|
+
import S3Cache, { getAwsClient } from "./s3-lite";
|
|
5
|
+
// TTL for the local cache in milliseconds
|
|
6
|
+
const localCacheTTL = process.env.OPEN_NEXT_LOCAL_CACHE_TTL_MS
|
|
7
|
+
? Number.parseInt(process.env.OPEN_NEXT_LOCAL_CACHE_TTL_MS, 10)
|
|
8
|
+
: 0;
|
|
9
|
+
// Maximum size of the local cache in nb of entries
|
|
10
|
+
const maxCacheSize = process.env.OPEN_NEXT_LOCAL_CACHE_SIZE
|
|
11
|
+
? Number.parseInt(process.env.OPEN_NEXT_LOCAL_CACHE_SIZE, 10)
|
|
12
|
+
: 1000;
|
|
13
|
+
const localCache = new LRUCache(maxCacheSize);
|
|
14
|
+
const awsFetch = (body, type = "get") => {
|
|
15
|
+
const { CACHE_BUCKET_REGION } = process.env;
|
|
16
|
+
const client = getAwsClient();
|
|
17
|
+
return customFetchClient(client)(`https://dynamodb.${CACHE_BUCKET_REGION}.amazonaws.com`, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
21
|
+
"X-Amz-Target": `DynamoDB_20120810.${type === "get" ? "GetItem" : "PutItem"}`,
|
|
22
|
+
},
|
|
23
|
+
body,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const buildDynamoKey = (key) => {
|
|
27
|
+
const { NEXT_BUILD_ID } = process.env;
|
|
28
|
+
return `__meta_${NEXT_BUILD_ID}_${key}`;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* This cache implementation uses a multi-tier cache with a local cache, a DynamoDB metadata cache and an S3 cache.
|
|
32
|
+
* It uses the same DynamoDB table as the default tag cache and the same S3 bucket as the default incremental cache.
|
|
33
|
+
* It will first check the local cache.
|
|
34
|
+
* If the local cache is expired, it will check the DynamoDB metadata cache to see if the local cache is still valid.
|
|
35
|
+
* Lastly it will check the S3 cache.
|
|
36
|
+
*/
|
|
37
|
+
const multiTierCache = {
|
|
38
|
+
name: "multi-tier-ddb-s3",
|
|
39
|
+
async get(key, isFetch) {
|
|
40
|
+
// First we check the local cache
|
|
41
|
+
const localCacheEntry = localCache.get(key);
|
|
42
|
+
if (localCacheEntry) {
|
|
43
|
+
if (Date.now() - localCacheEntry.lastModified < localCacheTTL) {
|
|
44
|
+
debug("Using local cache without checking ddb");
|
|
45
|
+
return localCacheEntry;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
// Here we'll check ddb metadata to see if the local cache is still valid
|
|
49
|
+
const { CACHE_DYNAMO_TABLE } = process.env;
|
|
50
|
+
const result = await awsFetch(JSON.stringify({
|
|
51
|
+
TableName: CACHE_DYNAMO_TABLE,
|
|
52
|
+
Key: {
|
|
53
|
+
path: { S: buildDynamoKey(key) },
|
|
54
|
+
tag: { S: buildDynamoKey(key) },
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
57
|
+
if (result.status === 200) {
|
|
58
|
+
const data = await result.json();
|
|
59
|
+
const hasBeenDeleted = data.Item?.deleted?.BOOL;
|
|
60
|
+
if (hasBeenDeleted) {
|
|
61
|
+
localCache.delete(key);
|
|
62
|
+
return { value: undefined, lastModified: 0 };
|
|
63
|
+
}
|
|
64
|
+
// If the metadata is older than the local cache, we can use the local cache
|
|
65
|
+
// If it's not found we assume that no write has been done yet and we can use the local cache
|
|
66
|
+
const lastModified = data.Item?.revalidatedAt?.N
|
|
67
|
+
? Number.parseInt(data.Item.revalidatedAt.N, 10)
|
|
68
|
+
: 0;
|
|
69
|
+
if (lastModified <= localCacheEntry.lastModified) {
|
|
70
|
+
debug("Using local cache after checking ddb");
|
|
71
|
+
return localCacheEntry;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
debug("Failed to get metadata from ddb", e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const result = await S3Cache.get(key, isFetch);
|
|
80
|
+
if (result.value) {
|
|
81
|
+
localCache.set(key, {
|
|
82
|
+
value: result.value,
|
|
83
|
+
lastModified: result.lastModified ?? Date.now(),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
// Both for set and delete we choose to do the write to S3 first and then to DynamoDB
|
|
89
|
+
// Which means that if it fails in DynamoDB, instance that don't have local cache will work as expected.
|
|
90
|
+
// But instance that have local cache will have a stale cache until the next working set or delete.
|
|
91
|
+
async set(key, value, isFetch) {
|
|
92
|
+
const revalidatedAt = Date.now();
|
|
93
|
+
await S3Cache.set(key, value, isFetch);
|
|
94
|
+
await awsFetch(JSON.stringify({
|
|
95
|
+
TableName: process.env.CACHE_DYNAMO_TABLE,
|
|
96
|
+
Item: {
|
|
97
|
+
tag: { S: buildDynamoKey(key) },
|
|
98
|
+
path: { S: buildDynamoKey(key) },
|
|
99
|
+
revalidatedAt: { N: String(revalidatedAt) },
|
|
100
|
+
},
|
|
101
|
+
}), "set");
|
|
102
|
+
localCache.set(key, {
|
|
103
|
+
value,
|
|
104
|
+
lastModified: revalidatedAt,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
async delete(key) {
|
|
108
|
+
await S3Cache.delete(key);
|
|
109
|
+
await awsFetch(JSON.stringify({
|
|
110
|
+
TableName: process.env.CACHE_DYNAMO_TABLE,
|
|
111
|
+
Item: {
|
|
112
|
+
tag: { S: buildDynamoKey(key) },
|
|
113
|
+
path: { S: buildDynamoKey(key) },
|
|
114
|
+
deleted: { BOOL: true },
|
|
115
|
+
},
|
|
116
|
+
}), "set");
|
|
117
|
+
localCache.delete(key);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
export default multiTierCache;
|
|
@@ -5,7 +5,7 @@ import { IgnorableError, RecoverableError } from "../../utils/error";
|
|
|
5
5
|
import { customFetchClient } from "../../utils/fetch";
|
|
6
6
|
import { parseNumberFromEnv } from "../../adapters/util";
|
|
7
7
|
let awsClient = null;
|
|
8
|
-
const getAwsClient = () => {
|
|
8
|
+
export const getAwsClient = () => {
|
|
9
9
|
const { CACHE_BUCKET_REGION } = process.env;
|
|
10
10
|
if (awsClient) {
|
|
11
11
|
return awsClient;
|
|
@@ -36,16 +36,17 @@ const nodeProxy = {
|
|
|
36
36
|
method,
|
|
37
37
|
rejectUnauthorized: false,
|
|
38
38
|
}, (_res) => {
|
|
39
|
-
const
|
|
39
|
+
const resHeaders = _res.headers;
|
|
40
|
+
const nodeReadableStream = resHeaders["content-encoding"] === "br"
|
|
40
41
|
? _res.pipe(require("node:zlib").createBrotliDecompress())
|
|
41
|
-
:
|
|
42
|
+
: resHeaders["content-encoding"] === "gzip"
|
|
42
43
|
? _res.pipe(require("node:zlib").createGunzip())
|
|
43
44
|
: _res;
|
|
44
|
-
const isBase64Encoded = isBinaryContentType(
|
|
45
|
-
!!
|
|
45
|
+
const isBase64Encoded = isBinaryContentType(resHeaders["content-type"]) ||
|
|
46
|
+
!!resHeaders["content-encoding"];
|
|
46
47
|
const result = {
|
|
47
48
|
type: "core",
|
|
48
|
-
headers: filterHeadersForProxy(
|
|
49
|
+
headers: filterHeadersForProxy(resHeaders),
|
|
49
50
|
statusCode: _res.statusCode ?? 200,
|
|
50
51
|
// TODO: check base64 encoding
|
|
51
52
|
isBase64Encoded,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const queue = {
|
|
2
|
+
name: "dev-queue",
|
|
3
|
+
send: async (message) => {
|
|
4
|
+
const prerenderManifest = (await import("../../adapters/config/index.js"))
|
|
5
|
+
.PrerenderManifest;
|
|
6
|
+
const { host, url } = message.MessageBody;
|
|
7
|
+
const protocol = host.includes("localhost") ? "http" : "https";
|
|
8
|
+
const revalidateId = prerenderManifest.preview.previewModeId;
|
|
9
|
+
await globalThis.internalFetch(`${protocol}://${host}${url}`, {
|
|
10
|
+
method: "HEAD",
|
|
11
|
+
headers: {
|
|
12
|
+
"x-prerender-revalidate": revalidateId,
|
|
13
|
+
"x-isr": "1",
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export default queue;
|
|
@@ -49,8 +49,9 @@ function buildDynamoObject(path, tags, revalidatedAt) {
|
|
|
49
49
|
const tagCache = {
|
|
50
50
|
async getByPath(path) {
|
|
51
51
|
try {
|
|
52
|
-
if (globalThis.
|
|
52
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
53
53
|
return [];
|
|
54
|
+
}
|
|
54
55
|
const { CACHE_DYNAMO_TABLE, NEXT_BUILD_ID } = process.env;
|
|
55
56
|
const result = await awsFetch(JSON.stringify({
|
|
56
57
|
TableName: CACHE_DYNAMO_TABLE,
|
|
@@ -79,8 +80,9 @@ const tagCache = {
|
|
|
79
80
|
},
|
|
80
81
|
async getByTag(tag) {
|
|
81
82
|
try {
|
|
82
|
-
if (globalThis.
|
|
83
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
83
84
|
return [];
|
|
85
|
+
}
|
|
84
86
|
const { CACHE_DYNAMO_TABLE, NEXT_BUILD_ID } = process.env;
|
|
85
87
|
const result = await awsFetch(JSON.stringify({
|
|
86
88
|
TableName: CACHE_DYNAMO_TABLE,
|
|
@@ -107,8 +109,9 @@ const tagCache = {
|
|
|
107
109
|
},
|
|
108
110
|
async getLastModified(key, lastModified) {
|
|
109
111
|
try {
|
|
110
|
-
if (globalThis.
|
|
112
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
111
113
|
return lastModified ?? Date.now();
|
|
114
|
+
}
|
|
112
115
|
const { CACHE_DYNAMO_TABLE } = process.env;
|
|
113
116
|
const result = await awsFetch(JSON.stringify({
|
|
114
117
|
TableName: CACHE_DYNAMO_TABLE,
|
|
@@ -139,8 +142,9 @@ const tagCache = {
|
|
|
139
142
|
async writeTags(tags) {
|
|
140
143
|
try {
|
|
141
144
|
const { CACHE_DYNAMO_TABLE } = process.env;
|
|
142
|
-
if (globalThis.
|
|
145
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
143
146
|
return;
|
|
147
|
+
}
|
|
144
148
|
const dataChunks = chunk(tags, MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT).map((Items) => ({
|
|
145
149
|
RequestItems: {
|
|
146
150
|
[CACHE_DYNAMO_TABLE ?? ""]: Items.map((Item) => ({
|
|
@@ -27,8 +27,9 @@ function buildDynamoObject(path, tags, revalidatedAt) {
|
|
|
27
27
|
const tagCache = {
|
|
28
28
|
async getByPath(path) {
|
|
29
29
|
try {
|
|
30
|
-
if (globalThis.
|
|
30
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
31
31
|
return [];
|
|
32
|
+
}
|
|
32
33
|
const result = await dynamoClient.send(new QueryCommand({
|
|
33
34
|
TableName: CACHE_DYNAMO_TABLE,
|
|
34
35
|
IndexName: "revalidate",
|
|
@@ -52,8 +53,9 @@ const tagCache = {
|
|
|
52
53
|
},
|
|
53
54
|
async getByTag(tag) {
|
|
54
55
|
try {
|
|
55
|
-
if (globalThis.
|
|
56
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
56
57
|
return [];
|
|
58
|
+
}
|
|
57
59
|
const { Items } = await dynamoClient.send(new QueryCommand({
|
|
58
60
|
TableName: CACHE_DYNAMO_TABLE,
|
|
59
61
|
KeyConditionExpression: "#tag = :tag",
|
|
@@ -75,8 +77,9 @@ const tagCache = {
|
|
|
75
77
|
},
|
|
76
78
|
async getLastModified(key, lastModified) {
|
|
77
79
|
try {
|
|
78
|
-
if (globalThis.
|
|
80
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
79
81
|
return lastModified ?? Date.now();
|
|
82
|
+
}
|
|
80
83
|
const result = await dynamoClient.send(new QueryCommand({
|
|
81
84
|
TableName: CACHE_DYNAMO_TABLE,
|
|
82
85
|
IndexName: "revalidate",
|
|
@@ -102,8 +105,9 @@ const tagCache = {
|
|
|
102
105
|
},
|
|
103
106
|
async writeTags(tags) {
|
|
104
107
|
try {
|
|
105
|
-
if (globalThis.
|
|
108
|
+
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
|
|
106
109
|
return;
|
|
110
|
+
}
|
|
107
111
|
const dataChunks = chunk(tags, MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT).map((Items) => ({
|
|
108
112
|
RequestItems: {
|
|
109
113
|
[CACHE_DYNAMO_TABLE ?? ""]: Items.map((Item) => ({
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
const tagFile = "../../dynamodb-provider/dynamodb-cache.json";
|
|
3
|
+
const tagContent = fs.readFileSync(tagFile, "utf-8");
|
|
4
|
+
let tags = JSON.parse(tagContent);
|
|
5
|
+
const tagCache = {
|
|
6
|
+
name: "fs-dev",
|
|
7
|
+
getByPath: async (path) => {
|
|
8
|
+
return tags
|
|
9
|
+
.filter((tagPathMapping) => tagPathMapping.path.S === path)
|
|
10
|
+
.map((tag) => tag.tag.S);
|
|
11
|
+
},
|
|
12
|
+
getByTag: async (tag) => {
|
|
13
|
+
return tags
|
|
14
|
+
.filter((tagPathMapping) => tagPathMapping.tag.S === tag)
|
|
15
|
+
.map((tag) => tag.path.S);
|
|
16
|
+
},
|
|
17
|
+
getLastModified: async (path, lastModified) => {
|
|
18
|
+
const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === path &&
|
|
19
|
+
Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0));
|
|
20
|
+
return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now());
|
|
21
|
+
},
|
|
22
|
+
writeTags: async (newTags) => {
|
|
23
|
+
const newTagsSet = new Set(newTags.map(({ tag, path }) => `${tag}-${path}`));
|
|
24
|
+
const unchangedTags = tags.filter(({ tag, path }) => !newTagsSet.has(`${tag.S}-${path.S}`));
|
|
25
|
+
tags = unchangedTags.concat(newTags.map((tag) => ({
|
|
26
|
+
tag: { S: tag.tag },
|
|
27
|
+
path: { S: tag.path },
|
|
28
|
+
revalidatedAt: { N: String(tag.revalidatedAt) },
|
|
29
|
+
})));
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export default tagCache;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const
|
|
2
|
-
city
|
|
1
|
+
const cfPropNameMapping = {
|
|
2
|
+
// The city name is percent-encoded.
|
|
3
|
+
// See https://github.com/vercel/vercel/blob/4cb6143/packages/functions/src/headers.ts#L94C19-L94C37
|
|
4
|
+
city: [encodeURIComponent, "x-open-next-city"],
|
|
3
5
|
country: "x-open-next-country",
|
|
4
6
|
regionCode: "x-open-next-region",
|
|
5
7
|
latitude: "x-open-next-latitude",
|
|
@@ -20,10 +22,15 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
|
|
|
20
22
|
// See https://developers.cloudflare.com/workers/runtime-apis/request
|
|
21
23
|
// Note: This code could be moved to a cloudflare specific converter when one is created.
|
|
22
24
|
const cfProperties = request.cf;
|
|
23
|
-
for (const [propName,
|
|
25
|
+
for (const [propName, mapping] of Object.entries(cfPropNameMapping)) {
|
|
24
26
|
const propValue = cfProperties?.[propName];
|
|
25
27
|
if (propValue != null) {
|
|
26
|
-
|
|
28
|
+
const [encode, headerName] = Array.isArray(mapping)
|
|
29
|
+
? mapping
|
|
30
|
+
: [null, mapping];
|
|
31
|
+
internalEvent.headers[headerName] = encode
|
|
32
|
+
? encode(propValue)
|
|
33
|
+
: propValue;
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
const response = await handler(internalEvent);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
const dummyWrapper = async (
|
|
1
|
+
const dummyWrapper = async (handler, converter) => {
|
|
2
|
+
return async (event, responseStream) => {
|
|
3
|
+
return await handler(event, responseStream);
|
|
4
|
+
};
|
|
5
|
+
};
|
|
2
6
|
export default {
|
|
3
7
|
name: "dummy",
|
|
4
8
|
wrapper: dummyWrapper,
|
|
5
|
-
supportStreaming:
|
|
9
|
+
supportStreaming: true,
|
|
6
10
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
const wrapper = async (handler, converter) => {
|
|
3
|
+
const app = express();
|
|
4
|
+
// To serve static assets
|
|
5
|
+
app.use(express.static("../../assets"));
|
|
6
|
+
const imageHandlerPath = "../../image-optimization-function/index.mjs";
|
|
7
|
+
const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
|
|
8
|
+
app.all("/_next/image", async (req, res) => {
|
|
9
|
+
const internalEvent = await converter.convertFrom(req);
|
|
10
|
+
const _res = {
|
|
11
|
+
writeHeaders: (prelude) => {
|
|
12
|
+
res.writeHead(prelude.statusCode, prelude.headers);
|
|
13
|
+
return res;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
await imageHandler(internalEvent, _res);
|
|
17
|
+
});
|
|
18
|
+
app.all("*paths", async (req, res) => {
|
|
19
|
+
const internalEvent = await converter.convertFrom(req);
|
|
20
|
+
const _res = {
|
|
21
|
+
writeHeaders: (prelude) => {
|
|
22
|
+
res.writeHead(prelude.statusCode, prelude.headers);
|
|
23
|
+
return res;
|
|
24
|
+
},
|
|
25
|
+
onFinish: () => { },
|
|
26
|
+
};
|
|
27
|
+
await handler(internalEvent, _res);
|
|
28
|
+
});
|
|
29
|
+
const server = app.listen(Number.parseInt(process.env.PORT ?? "3000", 10), () => {
|
|
30
|
+
console.log(`Server running on port ${process.env.PORT ?? 3000}`);
|
|
31
|
+
});
|
|
32
|
+
app.on("error", (err) => {
|
|
33
|
+
console.error("error", err);
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
server.close();
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export default {
|
|
40
|
+
wrapper,
|
|
41
|
+
name: "expresss-dev",
|
|
42
|
+
supportStreaming: true,
|
|
43
|
+
};
|
package/dist/plugins/edge.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
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
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
6
7
|
/**
|
|
7
8
|
* @param opts.nextDir - The path to the .next directory
|
|
8
9
|
* @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
|
|
@@ -28,7 +29,7 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
|
|
|
28
29
|
logger.debug(chalk.blue("OpenNext Edge plugin"));
|
|
29
30
|
if (edgeFunctionHandlerPath) {
|
|
30
31
|
// If we bundle the routing, we need to resolve the middleware
|
|
31
|
-
build.onResolve({ filter:
|
|
32
|
+
build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => {
|
|
32
33
|
return {
|
|
33
34
|
path: edgeFunctionHandlerPath,
|
|
34
35
|
};
|
|
@@ -55,7 +56,7 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
|
|
|
55
56
|
loader: "js",
|
|
56
57
|
}));
|
|
57
58
|
// We inject the entry files into the edgeFunctionHandler
|
|
58
|
-
build.onLoad({ filter:
|
|
59
|
+
build.onLoad({ filter: getCrossPlatformPathRegex("/edgeFunctionHandler.js") }, async (args) => {
|
|
59
60
|
let contents = readFileSync(args.path, "utf-8");
|
|
60
61
|
contents = `
|
|
61
62
|
globalThis._ENTRIES = {};
|
|
@@ -117,7 +118,7 @@ ${contents}
|
|
|
117
118
|
contents,
|
|
118
119
|
};
|
|
119
120
|
});
|
|
120
|
-
build.onLoad({ filter: /
|
|
121
|
+
build.onLoad({ filter: getCrossPlatformPathRegex("adapters/config/index") }, async () => {
|
|
121
122
|
const NextConfig = loadConfig(nextDir);
|
|
122
123
|
const BuildId = loadBuildId(nextDir);
|
|
123
124
|
const HtmlPages = loadHtmlPages(nextDir);
|
|
@@ -6,7 +6,7 @@ const importPattern = /\/\/#import([\s\S]*?)\n\/\/#endImport/gm;
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
8
8
|
* openNextPlugin({
|
|
9
|
-
* target: /plugins
|
|
9
|
+
* target: /plugins(\/|\\)default\.js/g,
|
|
10
10
|
* replacements: [require.resolve("./plugins/default.js")],
|
|
11
11
|
* deletes: ["id1"],
|
|
12
12
|
* })
|
|
@@ -12,6 +12,7 @@ export interface IPluginSettings {
|
|
|
12
12
|
originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
|
|
13
13
|
warmer?: LazyLoadedOverride<Warmer> | IncludedWarmer;
|
|
14
14
|
proxyExternalRequest?: OverrideOptions["proxyExternalRequest"];
|
|
15
|
+
cdnInvalidation?: OverrideOptions["cdnInvalidation"];
|
|
15
16
|
};
|
|
16
17
|
fnName?: string;
|
|
17
18
|
}
|
package/dist/plugins/resolve.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import logger from "../logger.js";
|
|
4
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
4
5
|
function getOverrideOrDummy(override) {
|
|
5
6
|
if (typeof override === "string") {
|
|
6
7
|
return override;
|
|
@@ -19,6 +20,7 @@ const nameToFolder = {
|
|
|
19
20
|
originResolver: "originResolver",
|
|
20
21
|
warmer: "warmer",
|
|
21
22
|
proxyExternalRequest: "proxyExternalRequest",
|
|
23
|
+
cdnInvalidation: "cdnInvalidation",
|
|
22
24
|
};
|
|
23
25
|
const defaultOverrides = {
|
|
24
26
|
wrapper: "aws-lambda",
|
|
@@ -30,6 +32,7 @@ const defaultOverrides = {
|
|
|
30
32
|
originResolver: "pattern-env",
|
|
31
33
|
warmer: "aws-lambda",
|
|
32
34
|
proxyExternalRequest: "node",
|
|
35
|
+
cdnInvalidation: "dummy",
|
|
33
36
|
};
|
|
34
37
|
/**
|
|
35
38
|
* @param opts.overrides - The name of the overrides to use
|
|
@@ -40,7 +43,7 @@ export function openNextResolvePlugin({ overrides, fnName, }) {
|
|
|
40
43
|
name: "opennext-resolve",
|
|
41
44
|
setup(build) {
|
|
42
45
|
logger.debug(chalk.blue("OpenNext Resolve plugin"), fnName ? `for ${fnName}` : "");
|
|
43
|
-
build.onLoad({ filter: /
|
|
46
|
+
build.onLoad({ filter: getCrossPlatformPathRegex("core/resolve.js") }, async (args) => {
|
|
44
47
|
let contents = readFileSync(args.path, "utf-8");
|
|
45
48
|
const overridesEntries = Object.entries(overrides ?? {});
|
|
46
49
|
for (let [overrideName, overrideValue] of overridesEntries) {
|
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 { IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
|
|
3
|
+
import type { CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
|
|
4
4
|
import type { DetachedPromiseRunner } from "../utils/promise";
|
|
5
5
|
import type { OpenNextConfig } from "./open-next";
|
|
6
6
|
export interface RequestData {
|
|
@@ -29,16 +29,17 @@ export interface RequestData {
|
|
|
29
29
|
body?: ReadableStream<Uint8Array>;
|
|
30
30
|
signal: AbortSignal;
|
|
31
31
|
}
|
|
32
|
+
interface Entry {
|
|
33
|
+
default: (props: {
|
|
34
|
+
page: string;
|
|
35
|
+
request: RequestData;
|
|
36
|
+
}) => Promise<{
|
|
37
|
+
response: Response;
|
|
38
|
+
waitUntil: Promise<void>;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
32
41
|
interface Entries {
|
|
33
|
-
[k: string]:
|
|
34
|
-
default: (props: {
|
|
35
|
-
page: string;
|
|
36
|
-
request: RequestData;
|
|
37
|
-
}) => Promise<{
|
|
38
|
-
response: Response;
|
|
39
|
-
waitUntil: Promise<void>;
|
|
40
|
-
}>;
|
|
41
|
-
};
|
|
42
|
+
[k: string]: Entry | Promise<Entry>;
|
|
42
43
|
}
|
|
43
44
|
export interface EdgeRoute {
|
|
44
45
|
name: string;
|
|
@@ -66,13 +67,13 @@ declare global {
|
|
|
66
67
|
var tagCache: TagCache;
|
|
67
68
|
/**
|
|
68
69
|
* A boolean that indicates if the DynamoDB cache is disabled.
|
|
69
|
-
*
|
|
70
|
+
* @deprecated This will be removed, use `globalThis.openNextConfig.dangerous?.disableTagCache` instead.
|
|
70
71
|
* Defined in esbuild banner for the cache adapter.
|
|
71
72
|
*/
|
|
72
73
|
var disableDynamoDBCache: boolean;
|
|
73
74
|
/**
|
|
74
75
|
* A boolean that indicates if the incremental cache is disabled.
|
|
75
|
-
*
|
|
76
|
+
* @deprecated This will be removed, use `globalThis.openNextConfig.dangerous?.disableIncrementalCache` instead.
|
|
76
77
|
* Defined in esbuild banner for the cache adapter.
|
|
77
78
|
*/
|
|
78
79
|
var disableIncrementalCache: boolean;
|
|
@@ -184,5 +185,11 @@ declare global {
|
|
|
184
185
|
* Defined in `createMainHandler`.
|
|
185
186
|
*/
|
|
186
187
|
var proxyExternalRequest: ProxyExternalRequest;
|
|
188
|
+
/**
|
|
189
|
+
* The function that will be called when the CDN needs invalidating (either from `revalidateTag` or from `res.revalidate`)
|
|
190
|
+
* Available in main functions
|
|
191
|
+
* Defined in `createMainHandler`
|
|
192
|
+
*/
|
|
193
|
+
var cdnInvalidationHandler: CDNInvalidationHandler;
|
|
187
194
|
}
|
|
188
195
|
export {};
|
|
@@ -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 { Converter, ImageLoader, IncrementalCache, OriginResolver, ProxyExternalRequest, Queue, TagCache, Warmer, Wrapper } from "./overrides";
|
|
4
|
+
import type { 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
|
};
|
|
@@ -66,7 +66,7 @@ export interface Origin {
|
|
|
66
66
|
port?: number;
|
|
67
67
|
customHeaders?: Record<string, string>;
|
|
68
68
|
}
|
|
69
|
-
export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "dummy";
|
|
69
|
+
export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "express-dev" | "dummy";
|
|
70
70
|
export type IncludedConverter = "aws-apigw-v2" | "aws-apigw-v1" | "aws-cloudfront" | "edge" | "node" | "sqs-revalidate" | "dummy";
|
|
71
71
|
export type RouteType = "route" | "page" | "app";
|
|
72
72
|
export interface ResolvedRoute {
|
|
@@ -83,13 +83,14 @@ export interface RoutingResult {
|
|
|
83
83
|
}
|
|
84
84
|
export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"middleware"> {
|
|
85
85
|
}
|
|
86
|
-
export type IncludedQueue = "sqs" | "sqs-lite" | "dummy";
|
|
87
|
-
export type IncludedIncrementalCache = "s3" | "s3-lite" | "dummy";
|
|
88
|
-
export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dummy";
|
|
89
|
-
export type IncludedImageLoader = "s3" | "host" | "dummy";
|
|
86
|
+
export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";
|
|
87
|
+
export type IncludedIncrementalCache = "s3" | "s3-lite" | "multi-tier-ddb-s3" | "fs-dev" | "dummy";
|
|
88
|
+
export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "fs-dev" | "dummy";
|
|
89
|
+
export type IncludedImageLoader = "s3" | "host" | "fs-dev" | "dummy";
|
|
90
90
|
export type IncludedOriginResolver = "pattern-env" | "dummy";
|
|
91
91
|
export type IncludedWarmer = "aws-lambda" | "dummy";
|
|
92
92
|
export type IncludedProxyExternalRequest = "node" | "fetch" | "dummy";
|
|
93
|
+
export type IncludedCDNInvalidationHandler = "cloudfront" | "dummy";
|
|
93
94
|
export interface DefaultOverrideOptions<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> {
|
|
94
95
|
/**
|
|
95
96
|
* This is the main entrypoint of your app.
|
|
@@ -129,6 +130,11 @@ export interface OverrideOptions extends DefaultOverrideOptions {
|
|
|
129
130
|
* @default "node"
|
|
130
131
|
*/
|
|
131
132
|
proxyExternalRequest?: IncludedProxyExternalRequest | LazyLoadedOverride<ProxyExternalRequest>;
|
|
133
|
+
/**
|
|
134
|
+
* Add possibility to override the default cdn invalidation for On Demand Revalidation
|
|
135
|
+
* @default "dummy"
|
|
136
|
+
*/
|
|
137
|
+
cdnInvalidation?: IncludedCDNInvalidationHandler | LazyLoadedOverride<CDNInvalidationHandler>;
|
|
132
138
|
}
|
|
133
139
|
export interface InstallOptions {
|
|
134
140
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from "node:stream";
|
|
2
2
|
import type { Meta } from "./cache";
|
|
3
|
-
import type { BaseEventOrResult, BaseOverride, InternalEvent, InternalResult, Origin, StreamCreator } from "./open-next";
|
|
3
|
+
import type { BaseEventOrResult, BaseOverride, InternalEvent, InternalResult, Origin, ResolvedRoute, StreamCreator } from "./open-next";
|
|
4
4
|
export interface QueueMessage {
|
|
5
5
|
MessageDeduplicationId: string;
|
|
6
6
|
MessageBody: {
|
|
@@ -84,3 +84,12 @@ export type OriginResolver = BaseOverride & {
|
|
|
84
84
|
export type ProxyExternalRequest = BaseOverride & {
|
|
85
85
|
proxy: (event: InternalEvent) => Promise<InternalResult>;
|
|
86
86
|
};
|
|
87
|
+
type CDNPath = {
|
|
88
|
+
initialPath: string;
|
|
89
|
+
rawPath: string;
|
|
90
|
+
resolvedRoutes: ResolvedRoute[];
|
|
91
|
+
};
|
|
92
|
+
export type CDNInvalidationHandler = BaseOverride & {
|
|
93
|
+
invalidatePaths: (paths: CDNPath[]) => Promise<void>;
|
|
94
|
+
};
|
|
95
|
+
export {};
|