@opennextjs/aws 3.1.4
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/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/adapters/binary.d.ts +1 -0
- package/dist/adapters/binary.js +63 -0
- package/dist/adapters/cache.d.ts +96 -0
- package/dist/adapters/cache.js +278 -0
- package/dist/adapters/config/index.d.ts +27 -0
- package/dist/adapters/config/index.js +21 -0
- package/dist/adapters/config/util.d.ts +28 -0
- package/dist/adapters/config/util.js +84 -0
- package/dist/adapters/dynamo-provider.d.ts +8 -0
- package/dist/adapters/dynamo-provider.js +42 -0
- package/dist/adapters/edge-adapter.d.ts +8 -0
- package/dist/adapters/edge-adapter.js +54 -0
- package/dist/adapters/image-optimization-adapter.d.ts +4 -0
- package/dist/adapters/image-optimization-adapter.js +193 -0
- package/dist/adapters/logger.d.ts +13 -0
- package/dist/adapters/logger.js +51 -0
- package/dist/adapters/middleware.d.ts +5 -0
- package/dist/adapters/middleware.js +84 -0
- package/dist/adapters/plugins/14.1/util.d.ts +1 -0
- package/dist/adapters/plugins/14.1/util.js +29 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.d.ts +11 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.js +14 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.replacement.d.ts +11 -0
- package/dist/adapters/plugins/image-optimization/image-optimization.replacement.js +22 -0
- package/dist/adapters/plugins/without-routing/requestHandler.d.ts +2 -0
- package/dist/adapters/plugins/without-routing/requestHandler.js +10 -0
- package/dist/adapters/revalidate.d.ts +9 -0
- package/dist/adapters/revalidate.js +71 -0
- package/dist/adapters/server-adapter.d.ts +4 -0
- package/dist/adapters/server-adapter.js +27 -0
- package/dist/adapters/util.d.ts +10 -0
- package/dist/adapters/util.js +28 -0
- package/dist/adapters/warmer-function.d.ts +12 -0
- package/dist/adapters/warmer-function.js +80 -0
- package/dist/build/bundleNextServer.d.ts +1 -0
- package/dist/build/bundleNextServer.js +87 -0
- package/dist/build/compileConfig.d.ts +3 -0
- package/dist/build/compileConfig.js +64 -0
- package/dist/build/copyTracedFiles.d.ts +1 -0
- package/dist/build/copyTracedFiles.js +207 -0
- package/dist/build/createServerBundle.d.ts +3 -0
- package/dist/build/createServerBundle.js +228 -0
- package/dist/build/edge/createEdgeBundle.d.ts +20 -0
- package/dist/build/edge/createEdgeBundle.js +149 -0
- package/dist/build/generateOutput.d.ts +2 -0
- package/dist/build/generateOutput.js +239 -0
- package/dist/build/helper.d.ts +33 -0
- package/dist/build/helper.js +193 -0
- package/dist/build/patch/patchedAsyncStorage.d.ts +5 -0
- package/dist/build/patch/patchedAsyncStorage.js +16 -0
- package/dist/build/validateConfig.d.ts +2 -0
- package/dist/build/validateConfig.js +76 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +533 -0
- package/dist/cache/incremental/s3-lite.d.ts +3 -0
- package/dist/cache/incremental/s3-lite.js +72 -0
- package/dist/cache/incremental/s3.d.ts +3 -0
- package/dist/cache/incremental/s3.js +44 -0
- package/dist/cache/incremental/types.d.ts +34 -0
- package/dist/cache/incremental/types.js +1 -0
- package/dist/cache/next-types.d.ts +62 -0
- package/dist/cache/next-types.js +1 -0
- package/dist/cache/tag/constants.d.ts +2 -0
- package/dist/cache/tag/constants.js +17 -0
- package/dist/cache/tag/dynamodb-lite.d.ts +3 -0
- package/dist/cache/tag/dynamodb-lite.js +174 -0
- package/dist/cache/tag/dynamodb.d.ts +3 -0
- package/dist/cache/tag/dynamodb.js +129 -0
- package/dist/cache/tag/types.d.ts +11 -0
- package/dist/cache/tag/types.js +1 -0
- package/dist/converters/aws-apigw-v1.d.ts +3 -0
- package/dist/converters/aws-apigw-v1.js +89 -0
- package/dist/converters/aws-apigw-v2.d.ts +3 -0
- package/dist/converters/aws-apigw-v2.js +99 -0
- package/dist/converters/aws-cloudfront.d.ts +3 -0
- package/dist/converters/aws-cloudfront.js +170 -0
- package/dist/converters/dummy.d.ts +7 -0
- package/dist/converters/dummy.js +16 -0
- package/dist/converters/edge.d.ts +6 -0
- package/dist/converters/edge.js +88 -0
- package/dist/converters/node.d.ts +3 -0
- package/dist/converters/node.js +45 -0
- package/dist/converters/sqs-revalidate.d.ts +4 -0
- package/dist/converters/sqs-revalidate.js +22 -0
- package/dist/converters/utils.d.ts +1 -0
- package/dist/converters/utils.js +9 -0
- package/dist/core/createGenericHandler.d.ts +11 -0
- package/dist/core/createGenericHandler.js +16 -0
- package/dist/core/createMainHandler.d.ts +18 -0
- package/dist/core/createMainHandler.js +24 -0
- package/dist/core/edgeFunctionHandler.d.ts +54 -0
- package/dist/core/edgeFunctionHandler.js +21 -0
- package/dist/core/patchAsyncStorage.d.ts +1 -0
- package/dist/core/patchAsyncStorage.js +16 -0
- package/dist/core/requestHandler.d.ts +3 -0
- package/dist/core/requestHandler.js +162 -0
- package/dist/core/require-hooks.d.ts +3 -0
- package/dist/core/require-hooks.js +102 -0
- package/dist/core/resolve.d.ts +31 -0
- package/dist/core/resolve.js +81 -0
- package/dist/core/routing/cacheInterceptor.d.ts +2 -0
- package/dist/core/routing/cacheInterceptor.js +162 -0
- package/dist/core/routing/i18n/accept-header.d.ts +1 -0
- package/dist/core/routing/i18n/accept-header.js +101 -0
- package/dist/core/routing/i18n/index.d.ts +2 -0
- package/dist/core/routing/i18n/index.js +36 -0
- package/dist/core/routing/matcher.d.ts +25 -0
- package/dist/core/routing/matcher.js +294 -0
- package/dist/core/routing/middleware.d.ts +7 -0
- package/dist/core/routing/middleware.js +167 -0
- package/dist/core/routing/util.d.ts +105 -0
- package/dist/core/routing/util.js +415 -0
- package/dist/core/routingHandler.d.ts +8 -0
- package/dist/core/routingHandler.js +127 -0
- package/dist/core/util.d.ts +5 -0
- package/dist/core/util.js +84 -0
- package/dist/helpers/withCloudflare.d.ts +65 -0
- package/dist/helpers/withCloudflare.js +58 -0
- package/dist/helpers/withSST.d.ts +54 -0
- package/dist/helpers/withSST.js +28 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +4 -0
- package/dist/http/openNextResponse.d.ts +78 -0
- package/dist/http/openNextResponse.js +281 -0
- package/dist/http/request.d.ts +12 -0
- package/dist/http/request.js +35 -0
- package/dist/http/util.d.ts +4 -0
- package/dist/http/util.js +34 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +37 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +13 -0
- package/dist/minimize-js.d.ts +1 -0
- package/dist/minimize-js.js +101 -0
- package/dist/overrides/imageLoader/host.d.ts +3 -0
- package/dist/overrides/imageLoader/host.js +29 -0
- package/dist/overrides/imageLoader/s3.d.ts +3 -0
- package/dist/overrides/imageLoader/s3.js +33 -0
- package/dist/plugins/edge.d.ts +15 -0
- package/dist/plugins/edge.js +157 -0
- package/dist/plugins/replacement.d.ts +42 -0
- package/dist/plugins/replacement.js +74 -0
- package/dist/plugins/resolve.d.ts +18 -0
- package/dist/plugins/resolve.js +45 -0
- package/dist/queue/sqs-lite.d.ts +3 -0
- package/dist/queue/sqs-lite.js +52 -0
- package/dist/queue/sqs.d.ts +3 -0
- package/dist/queue/sqs.js +20 -0
- package/dist/queue/types.d.ts +12 -0
- package/dist/queue/types.js +1 -0
- package/dist/types/aws-lambda.d.ts +22 -0
- package/dist/types/aws-lambda.js +1 -0
- package/dist/types/next-types.d.ts +155 -0
- package/dist/types/next-types.js +2 -0
- package/dist/types/open-next.d.ts +303 -0
- package/dist/types/open-next.js +1 -0
- package/dist/utils/error.d.ts +23 -0
- package/dist/utils/error.js +32 -0
- package/dist/utils/fetch.d.ts +2 -0
- package/dist/utils/fetch.js +23 -0
- package/dist/utils/promise.d.ts +18 -0
- package/dist/utils/promise.js +48 -0
- package/dist/utils/stream.d.ts +5 -0
- package/dist/utils/stream.js +30 -0
- package/dist/wrappers/aws-lambda-streaming.d.ts +7 -0
- package/dist/wrappers/aws-lambda-streaming.js +88 -0
- package/dist/wrappers/aws-lambda.d.ts +7 -0
- package/dist/wrappers/aws-lambda.js +39 -0
- package/dist/wrappers/cloudflare.d.ts +11 -0
- package/dist/wrappers/cloudflare.js +20 -0
- package/dist/wrappers/node.d.ts +7 -0
- package/dist/wrappers/node.js +57 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 SST
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://open-next.js.org">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/public/logo-dark.svg">
|
|
5
|
+
<img alt="OpenNext" src="docs/public/logo-light.svg" width="300" />
|
|
6
|
+
</picture>
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://sst.dev/discord"><img alt="Discord" src="https://img.shields.io/discord/983865673656705025?style=flat-square" /></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/open-next"><img alt="npm" src="https://img.shields.io/npm/v/open-next.svg?style=flat-square" /></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://open-next.js.org/">Docs</a> |
|
|
18
|
+
<a href="#example">Example</a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
OpenNext takes the Next.js build output and converts it into a package that can be deployed to any functions as a service platform.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
OpenNext aims to support all Next.js 14 features. Some features are work in progress. Please open a [new issue](/issues/new) to let us know!
|
|
26
|
+
|
|
27
|
+
- [x] App & Pages Router
|
|
28
|
+
- [x] API routes
|
|
29
|
+
- [x] Dynamic routes
|
|
30
|
+
- [x] Static site generation (SSG)
|
|
31
|
+
- [x] Server-side rendering (SSR)
|
|
32
|
+
- [x] Incremental static regeneration (ISR)
|
|
33
|
+
- [x] Middleware
|
|
34
|
+
- [x] Image optimization
|
|
35
|
+
- [x] [NextAuth.js](https://next-auth.js.org)
|
|
36
|
+
- [x] Running at edge
|
|
37
|
+
- [x] No cold start
|
|
38
|
+
|
|
39
|
+
## Who is using OpenNext?
|
|
40
|
+
|
|
41
|
+
[Gymshark UK](https://uk.gymshark.com), [Udacity](https://engineering.udacity.com/deploying-next-js-on-the-edge-with-sst-is-sst-the-game-changer-its-claimed-to-be-1f05a0abc27c), [TUDN](https://www.tudn.com), [NHS England](https://github.com/nhs-england-tools/terraform-aws-opennext)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Example
|
|
46
|
+
|
|
47
|
+
In the `example` folder, you can find a Next.js benchmark app. It contains a variety of pages that each test a single Next.js feature. The app is deployed to both Vercel and AWS using [SST](https://docs.sst.dev/start/nextjs).
|
|
48
|
+
|
|
49
|
+
AWS link: https://d1gwt3w78t4dm3.cloudfront.net
|
|
50
|
+
|
|
51
|
+
Vercel link: https://open-next.vercel.app
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
### Environment variables
|
|
56
|
+
|
|
57
|
+
- AWS_SDK_DYNAMODB_MAX_ATTEMPTS: The maximum number of times requests that encounter retryable failures should be attempted for DynamoDB. Defaults to 3.
|
|
58
|
+
- AWS_SDK_S3_MAX_ATTEMPTS: The maximum number of times requests that encounter retryable failures should be attempted for S3. Defaults to 3.
|
|
59
|
+
- DYNAMO_BATCH_WRITE_COMMAND_CONCURRENCY: The number of concurrent batch write commands to DynamoDB. Defaults to 4 in an effort to leave plenty of DynamoDB write request capacity for the production load.
|
|
60
|
+
|
|
61
|
+
## Contribute
|
|
62
|
+
|
|
63
|
+
To run `OpenNext` locally:
|
|
64
|
+
|
|
65
|
+
1. Clone this repository.
|
|
66
|
+
1. Build `open-next`:
|
|
67
|
+
```bash
|
|
68
|
+
cd open-next
|
|
69
|
+
pnpm build
|
|
70
|
+
```
|
|
71
|
+
1. Run `open-next` in watch mode:
|
|
72
|
+
```bash
|
|
73
|
+
pnpm dev
|
|
74
|
+
```
|
|
75
|
+
1. Now, you can make changes in `open-next` and build your Next.js app to test the changes.
|
|
76
|
+
```bash
|
|
77
|
+
cd path/to/my/nextjs/app
|
|
78
|
+
path/to/open-next/packages/open-next/dist/index.js build
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Acknowledgements
|
|
82
|
+
|
|
83
|
+
We are grateful for the projects that inspired OpenNext and the amazing tools and libraries developed by the community:
|
|
84
|
+
|
|
85
|
+
- [nextjs-lambda](https://github.com/sladg/nextjs-lambda) by [Jan](https://github.com/sladg) for serving as inspiration for packaging Next.js's standalone output to Lambda.
|
|
86
|
+
- [CDK NextJS](https://github.com/jetbridge/cdk-nextjs/) by [JetBridge](https://github.com/jetbridge) for its contribution to the deployment architecture of a Next.js application on AWS.
|
|
87
|
+
- [serverless-http](https://github.com/dougmoscrop/serverless-http) by [Doug Moscrop](https://github.com/dougmoscrop) for developing an excellent library for transforming AWS Lambda events and responses.
|
|
88
|
+
- [serverless-nextjs](https://github.com/serverless-nextjs/serverless-next.js) by [Serverless Framework](https://github.com/serverless) for paving the way for serverless Next.js applications on AWS.
|
|
89
|
+
|
|
90
|
+
Special shoutout to [@khuezy](https://github.com/khuezy) and [@conico974](https://github.com/conico974) for their outstanding contributions to the project.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
Maintained by [SST](https://sst.dev). Join our community: [Discord](https://sst.dev/discord) | [YouTube](https://www.youtube.com/c/sst-dev) | [Twitter](https://twitter.com/SST_dev)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isBinaryContentType(contentType?: string | null): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const commonBinaryMimeTypes = new Set([
|
|
2
|
+
"application/octet-stream",
|
|
3
|
+
// Docs
|
|
4
|
+
"application/epub+zip",
|
|
5
|
+
"application/msword",
|
|
6
|
+
"application/pdf",
|
|
7
|
+
"application/rtf",
|
|
8
|
+
"application/vnd.amazon.ebook",
|
|
9
|
+
"application/vnd.ms-excel",
|
|
10
|
+
"application/vnd.ms-powerpoint",
|
|
11
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
12
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
13
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
14
|
+
// Fonts
|
|
15
|
+
"font/otf",
|
|
16
|
+
"font/woff",
|
|
17
|
+
"font/woff2",
|
|
18
|
+
// Images
|
|
19
|
+
"image/bmp",
|
|
20
|
+
"image/gif",
|
|
21
|
+
"image/jpeg",
|
|
22
|
+
"image/png",
|
|
23
|
+
"image/tiff",
|
|
24
|
+
"image/vnd.microsoft.icon",
|
|
25
|
+
"image/webp",
|
|
26
|
+
// Audio
|
|
27
|
+
"audio/3gpp",
|
|
28
|
+
"audio/aac",
|
|
29
|
+
"audio/basic",
|
|
30
|
+
"audio/mpeg",
|
|
31
|
+
"audio/ogg",
|
|
32
|
+
"audio/wavaudio/webm",
|
|
33
|
+
"audio/x-aiff",
|
|
34
|
+
"audio/x-midi",
|
|
35
|
+
"audio/x-wav",
|
|
36
|
+
// Video
|
|
37
|
+
"video/3gpp",
|
|
38
|
+
"video/mp2t",
|
|
39
|
+
"video/mpeg",
|
|
40
|
+
"video/ogg",
|
|
41
|
+
"video/quicktime",
|
|
42
|
+
"video/webm",
|
|
43
|
+
"video/x-msvideo",
|
|
44
|
+
// Archives
|
|
45
|
+
"application/java-archive",
|
|
46
|
+
"application/vnd.apple.installer+xml",
|
|
47
|
+
"application/x-7z-compressed",
|
|
48
|
+
"application/x-apple-diskimage",
|
|
49
|
+
"application/x-bzip",
|
|
50
|
+
"application/x-bzip2",
|
|
51
|
+
"application/x-gzip",
|
|
52
|
+
"application/x-java-archive",
|
|
53
|
+
"application/x-rar-compressed",
|
|
54
|
+
"application/x-tar",
|
|
55
|
+
"application/x-zip",
|
|
56
|
+
"application/zip",
|
|
57
|
+
]);
|
|
58
|
+
export function isBinaryContentType(contentType) {
|
|
59
|
+
if (!contentType)
|
|
60
|
+
return false;
|
|
61
|
+
const value = contentType?.split(";")[0] ?? "";
|
|
62
|
+
return commonBinaryMimeTypes.has(value);
|
|
63
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { IncrementalCache } from "../cache/incremental/types.js";
|
|
3
|
+
import { TagCache } from "../cache/tag/types.js";
|
|
4
|
+
interface CachedFetchValue {
|
|
5
|
+
kind: "FETCH";
|
|
6
|
+
data: {
|
|
7
|
+
headers: {
|
|
8
|
+
[k: string]: string;
|
|
9
|
+
};
|
|
10
|
+
body: string;
|
|
11
|
+
url: string;
|
|
12
|
+
status?: number;
|
|
13
|
+
tags?: string[];
|
|
14
|
+
};
|
|
15
|
+
revalidate: number;
|
|
16
|
+
}
|
|
17
|
+
interface CachedRedirectValue {
|
|
18
|
+
kind: "REDIRECT";
|
|
19
|
+
props: Object;
|
|
20
|
+
}
|
|
21
|
+
interface CachedRouteValue {
|
|
22
|
+
kind: "ROUTE" | "APP_ROUTE";
|
|
23
|
+
body: Buffer;
|
|
24
|
+
status: number;
|
|
25
|
+
headers: Record<string, undefined | string | string[]>;
|
|
26
|
+
}
|
|
27
|
+
interface CachedImageValue {
|
|
28
|
+
kind: "IMAGE";
|
|
29
|
+
etag: string;
|
|
30
|
+
buffer: Buffer;
|
|
31
|
+
extension: string;
|
|
32
|
+
isMiss?: boolean;
|
|
33
|
+
isStale?: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface IncrementalCachedPageValue {
|
|
36
|
+
kind: "PAGE" | "PAGES";
|
|
37
|
+
html: string;
|
|
38
|
+
pageData: Object;
|
|
39
|
+
status?: number;
|
|
40
|
+
headers?: Record<string, undefined | string>;
|
|
41
|
+
}
|
|
42
|
+
interface IncrementalCachedAppPageValue {
|
|
43
|
+
kind: "APP_PAGE";
|
|
44
|
+
html: string;
|
|
45
|
+
rscData: Buffer;
|
|
46
|
+
headers?: Record<string, undefined | string | string[]>;
|
|
47
|
+
postponed?: string;
|
|
48
|
+
status?: number;
|
|
49
|
+
}
|
|
50
|
+
type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
|
|
51
|
+
type IncrementalCacheContext = {
|
|
52
|
+
revalidate?: number | false | undefined;
|
|
53
|
+
fetchCache?: boolean | undefined;
|
|
54
|
+
fetchUrl?: string | undefined;
|
|
55
|
+
fetchIdx?: number | undefined;
|
|
56
|
+
tags?: string[] | undefined;
|
|
57
|
+
};
|
|
58
|
+
interface CacheHandlerContext {
|
|
59
|
+
fs?: never;
|
|
60
|
+
dev?: boolean;
|
|
61
|
+
flushToDisk?: boolean;
|
|
62
|
+
serverDistDir?: string;
|
|
63
|
+
maxMemoryCacheSize?: number;
|
|
64
|
+
_appDir: boolean;
|
|
65
|
+
_requestHeaders: never;
|
|
66
|
+
fetchCacheKeyPrefix?: string;
|
|
67
|
+
}
|
|
68
|
+
interface CacheHandlerValue {
|
|
69
|
+
lastModified?: number;
|
|
70
|
+
age?: number;
|
|
71
|
+
cacheState?: string;
|
|
72
|
+
value: IncrementalCacheValue | null;
|
|
73
|
+
}
|
|
74
|
+
export declare function hasCacheExtension(key: string): boolean;
|
|
75
|
+
declare global {
|
|
76
|
+
var incrementalCache: IncrementalCache;
|
|
77
|
+
var tagCache: TagCache;
|
|
78
|
+
var disableDynamoDBCache: boolean;
|
|
79
|
+
var disableIncrementalCache: boolean;
|
|
80
|
+
var lastModified: Record<string, number>;
|
|
81
|
+
var isNextAfter15: boolean;
|
|
82
|
+
}
|
|
83
|
+
export default class S3Cache {
|
|
84
|
+
constructor(_ctx: CacheHandlerContext);
|
|
85
|
+
get(key: string, options?: boolean | {
|
|
86
|
+
fetchCache?: boolean;
|
|
87
|
+
kindHint?: "app" | "pages" | "fetch";
|
|
88
|
+
tags?: string[];
|
|
89
|
+
softTags?: string[];
|
|
90
|
+
}): Promise<CacheHandlerValue | null>;
|
|
91
|
+
getFetchCache(key: string, softTags?: string[], tags?: string[]): Promise<CacheHandlerValue | null>;
|
|
92
|
+
getIncrementalCache(key: string): Promise<CacheHandlerValue | null>;
|
|
93
|
+
set(key: string, data?: IncrementalCacheValue, ctx?: IncrementalCacheContext): Promise<void>;
|
|
94
|
+
revalidateTag(tags: string | string[]): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { isBinaryContentType } from "./binary.js";
|
|
2
|
+
import { debug, error, warn } from "./logger.js";
|
|
3
|
+
/** Beginning single backslash is intentional, to look for the dot + the extension. Do not escape it again. */
|
|
4
|
+
const CACHE_EXTENSION_REGEX = /\.(cache|fetch)$/;
|
|
5
|
+
export function hasCacheExtension(key) {
|
|
6
|
+
return CACHE_EXTENSION_REGEX.test(key);
|
|
7
|
+
}
|
|
8
|
+
// We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time
|
|
9
|
+
export default class S3Cache {
|
|
10
|
+
constructor(_ctx) { }
|
|
11
|
+
async get(key,
|
|
12
|
+
// fetchCache is for next 13.5 and above, kindHint is for next 14 and above and boolean is for earlier versions
|
|
13
|
+
options) {
|
|
14
|
+
if (globalThis.disableIncrementalCache) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const isFetchCache = typeof options === "object"
|
|
18
|
+
? options.kindHint
|
|
19
|
+
? options.kindHint === "fetch"
|
|
20
|
+
: options.fetchCache
|
|
21
|
+
: options;
|
|
22
|
+
const softTags = typeof options === "object" ? options.softTags : [];
|
|
23
|
+
const tags = typeof options === "object" ? options.tags : [];
|
|
24
|
+
return isFetchCache
|
|
25
|
+
? this.getFetchCache(key, softTags, tags)
|
|
26
|
+
: this.getIncrementalCache(key);
|
|
27
|
+
}
|
|
28
|
+
async getFetchCache(key, softTags, tags) {
|
|
29
|
+
debug("get fetch cache", { key, softTags, tags });
|
|
30
|
+
try {
|
|
31
|
+
const { value, lastModified } = await globalThis.incrementalCache.get(key, true);
|
|
32
|
+
// const { Body, LastModified } = await this.getS3Object(key, "fetch");
|
|
33
|
+
const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
|
|
34
|
+
if (_lastModified === -1) {
|
|
35
|
+
// If some tags are stale we need to force revalidation
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return null;
|
|
40
|
+
// For cases where we don't have tags, we need to ensure that the soft tags are not being revalidated
|
|
41
|
+
// We only need to check for the path as it should already contain all the tags
|
|
42
|
+
if ((tags ?? []).length === 0) {
|
|
43
|
+
// Then we need to find the path for the given key
|
|
44
|
+
const path = softTags?.find((tag) => tag.startsWith("_N_T_/") &&
|
|
45
|
+
!tag.endsWith("layout") &&
|
|
46
|
+
!tag.endsWith("page"));
|
|
47
|
+
if (path) {
|
|
48
|
+
const pathLastModified = await globalThis.tagCache.getLastModified(path.replace("_N_T_/", ""), lastModified);
|
|
49
|
+
if (pathLastModified === -1) {
|
|
50
|
+
// In case the path has been revalidated, we don't want to use the fetch cache
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
lastModified: _lastModified,
|
|
57
|
+
value: value,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// We can usually ignore errors here as they are usually due to cache not being found
|
|
62
|
+
debug("Failed to get fetch cache", e);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async getIncrementalCache(key) {
|
|
67
|
+
try {
|
|
68
|
+
const { value: cacheData, lastModified } = await globalThis.incrementalCache.get(key, false);
|
|
69
|
+
// const { Body, LastModified } = await this.getS3Object(key, "cache");
|
|
70
|
+
// const cacheData = JSON.parse(
|
|
71
|
+
// (await Body?.transformToString()) ?? "{}",
|
|
72
|
+
// ) as S3CachedFile;
|
|
73
|
+
const meta = cacheData?.meta;
|
|
74
|
+
const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
|
|
75
|
+
if (_lastModified === -1) {
|
|
76
|
+
// If some tags are stale we need to force revalidation
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const requestId = globalThis.__als.getStore()?.requestId ?? "";
|
|
80
|
+
globalThis.lastModified[requestId] = _lastModified;
|
|
81
|
+
if (cacheData?.type === "route") {
|
|
82
|
+
return {
|
|
83
|
+
lastModified: _lastModified,
|
|
84
|
+
value: {
|
|
85
|
+
kind: globalThis.isNextAfter15 ? "APP_ROUTE" : "ROUTE",
|
|
86
|
+
body: Buffer.from(cacheData.body ?? Buffer.alloc(0), isBinaryContentType(String(meta?.headers?.["content-type"]))
|
|
87
|
+
? "base64"
|
|
88
|
+
: "utf8"),
|
|
89
|
+
status: meta?.status,
|
|
90
|
+
headers: meta?.headers,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
else if (cacheData?.type === "page" || cacheData?.type === "app") {
|
|
95
|
+
if (globalThis.isNextAfter15 && cacheData?.type === "app") {
|
|
96
|
+
return {
|
|
97
|
+
lastModified: _lastModified,
|
|
98
|
+
value: {
|
|
99
|
+
kind: "APP_PAGE",
|
|
100
|
+
html: cacheData.html,
|
|
101
|
+
rscData: Buffer.from(cacheData.rsc),
|
|
102
|
+
status: meta?.status,
|
|
103
|
+
headers: meta?.headers,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
lastModified: _lastModified,
|
|
109
|
+
value: {
|
|
110
|
+
kind: globalThis.isNextAfter15 ? "PAGES" : "PAGE",
|
|
111
|
+
html: cacheData.html,
|
|
112
|
+
pageData: cacheData.type === "page" ? cacheData.json : cacheData.rsc,
|
|
113
|
+
status: meta?.status,
|
|
114
|
+
headers: meta?.headers,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
else if (cacheData?.type === "redirect") {
|
|
119
|
+
return {
|
|
120
|
+
lastModified: _lastModified,
|
|
121
|
+
value: {
|
|
122
|
+
kind: "REDIRECT",
|
|
123
|
+
props: cacheData.props,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
warn("Unknown cache type", cacheData);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
// We can usually ignore errors here as they are usually due to cache not being found
|
|
134
|
+
debug("Failed to get body cache", e);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async set(key, data, ctx) {
|
|
139
|
+
if (globalThis.disableIncrementalCache) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// This one might not even be necessary anymore
|
|
143
|
+
// Better be safe than sorry
|
|
144
|
+
const detachedPromise = globalThis.__als
|
|
145
|
+
.getStore()
|
|
146
|
+
?.pendingPromiseRunner.withResolvers();
|
|
147
|
+
try {
|
|
148
|
+
if (data?.kind === "ROUTE" || data?.kind === "APP_ROUTE") {
|
|
149
|
+
const { body, status, headers } = data;
|
|
150
|
+
await globalThis.incrementalCache.set(key, {
|
|
151
|
+
type: "route",
|
|
152
|
+
body: body.toString(isBinaryContentType(String(headers["content-type"]))
|
|
153
|
+
? "base64"
|
|
154
|
+
: "utf8"),
|
|
155
|
+
meta: {
|
|
156
|
+
status,
|
|
157
|
+
headers,
|
|
158
|
+
},
|
|
159
|
+
}, false);
|
|
160
|
+
}
|
|
161
|
+
else if (data?.kind === "PAGE" || data?.kind === "PAGES") {
|
|
162
|
+
const { html, pageData, status, headers } = data;
|
|
163
|
+
const isAppPath = typeof pageData === "string";
|
|
164
|
+
if (isAppPath) {
|
|
165
|
+
await globalThis.incrementalCache.set(key, {
|
|
166
|
+
type: "app",
|
|
167
|
+
html,
|
|
168
|
+
rsc: pageData,
|
|
169
|
+
meta: {
|
|
170
|
+
status,
|
|
171
|
+
headers,
|
|
172
|
+
},
|
|
173
|
+
}, false);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
await globalThis.incrementalCache.set(key, {
|
|
177
|
+
type: "page",
|
|
178
|
+
html,
|
|
179
|
+
json: pageData,
|
|
180
|
+
}, false);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (data?.kind === "APP_PAGE") {
|
|
184
|
+
const { html, rscData, headers, status } = data;
|
|
185
|
+
await globalThis.incrementalCache.set(key, {
|
|
186
|
+
type: "app",
|
|
187
|
+
html,
|
|
188
|
+
rsc: rscData.toString("utf8"),
|
|
189
|
+
meta: {
|
|
190
|
+
status,
|
|
191
|
+
headers,
|
|
192
|
+
},
|
|
193
|
+
}, false);
|
|
194
|
+
}
|
|
195
|
+
else if (data?.kind === "FETCH") {
|
|
196
|
+
await globalThis.incrementalCache.set(key, data, true);
|
|
197
|
+
}
|
|
198
|
+
else if (data?.kind === "REDIRECT") {
|
|
199
|
+
await globalThis.incrementalCache.set(key, {
|
|
200
|
+
type: "redirect",
|
|
201
|
+
props: data.props,
|
|
202
|
+
}, false);
|
|
203
|
+
}
|
|
204
|
+
else if (data === null || data === undefined) {
|
|
205
|
+
await globalThis.incrementalCache.delete(key);
|
|
206
|
+
}
|
|
207
|
+
// Write derivedTags to dynamodb
|
|
208
|
+
// If we use an in house version of getDerivedTags in build we should use it here instead of next's one
|
|
209
|
+
const derivedTags = data?.kind === "FETCH"
|
|
210
|
+
? ctx?.tags ?? data?.data?.tags ?? [] // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
|
|
211
|
+
: data?.kind === "PAGE"
|
|
212
|
+
? data.headers?.["x-next-cache-tags"]?.split(",") ?? []
|
|
213
|
+
: [];
|
|
214
|
+
debug("derivedTags", derivedTags);
|
|
215
|
+
// Get all tags stored in dynamodb for the given key
|
|
216
|
+
// If any of the derived tags are not stored in dynamodb for the given key, write them
|
|
217
|
+
const storedTags = await globalThis.tagCache.getByPath(key);
|
|
218
|
+
const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
|
|
219
|
+
if (tagsToWrite.length > 0) {
|
|
220
|
+
await globalThis.tagCache.writeTags(tagsToWrite.map((tag) => ({
|
|
221
|
+
path: key,
|
|
222
|
+
tag: tag,
|
|
223
|
+
// In case the tags are not there we just need to create them
|
|
224
|
+
// but we don't want them to return from `getLastModified` as they are not stale
|
|
225
|
+
revalidatedAt: 1,
|
|
226
|
+
})));
|
|
227
|
+
}
|
|
228
|
+
debug("Finished setting cache");
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
error("Failed to set cache", e);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
// We need to resolve the promise even if there was an error
|
|
235
|
+
detachedPromise?.resolve();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async revalidateTag(tags) {
|
|
239
|
+
if (globalThis.disableDynamoDBCache || globalThis.disableIncrementalCache) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const _tags = Array.isArray(tags) ? tags : [tags];
|
|
244
|
+
for (const tag of _tags) {
|
|
245
|
+
debug("revalidateTag", tag);
|
|
246
|
+
// Find all keys with the given tag
|
|
247
|
+
const paths = await globalThis.tagCache.getByTag(tag);
|
|
248
|
+
debug("Items", paths);
|
|
249
|
+
const toInsert = paths.map((path) => ({
|
|
250
|
+
path,
|
|
251
|
+
tag,
|
|
252
|
+
}));
|
|
253
|
+
// If the tag is a soft tag, we should also revalidate the hard tags
|
|
254
|
+
if (tag.startsWith("_N_T_/")) {
|
|
255
|
+
for (const path of paths) {
|
|
256
|
+
// We need to find all hard tags for a given path
|
|
257
|
+
const _tags = await globalThis.tagCache.getByPath(path);
|
|
258
|
+
const hardTags = _tags.filter((t) => !t.startsWith("_N_T_/"));
|
|
259
|
+
// For every hard tag, we need to find all paths and revalidate them
|
|
260
|
+
for (const hardTag of hardTags) {
|
|
261
|
+
const _paths = await globalThis.tagCache.getByTag(hardTag);
|
|
262
|
+
debug({ hardTag, _paths });
|
|
263
|
+
toInsert.push(..._paths.map((path) => ({
|
|
264
|
+
path,
|
|
265
|
+
tag: hardTag,
|
|
266
|
+
})));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Update all keys with the given tag with revalidatedAt set to now
|
|
271
|
+
await globalThis.tagCache.writeTags(toInsert);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
error("Failed to revalidate tag", e);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const NEXT_DIR: string;
|
|
2
|
+
export declare const OPEN_NEXT_DIR: string;
|
|
3
|
+
export declare const NextConfig: import("../../types/next-types").NextConfig;
|
|
4
|
+
export declare const BuildId: string;
|
|
5
|
+
export declare const HtmlPages: string[];
|
|
6
|
+
export declare const RoutesManifest: {
|
|
7
|
+
basePath: string | undefined;
|
|
8
|
+
rewrites: {
|
|
9
|
+
beforeFiles: import("../../types/next-types").RewriteDefinition[];
|
|
10
|
+
afterFiles: import("../../types/next-types").RewriteDefinition[];
|
|
11
|
+
fallback: import("../../types/next-types").RewriteDefinition[];
|
|
12
|
+
};
|
|
13
|
+
redirects: import("../../types/next-types").RedirectDefinition[];
|
|
14
|
+
routes: {
|
|
15
|
+
static: import("../../types/next-types").RouteDefinition[];
|
|
16
|
+
dynamic: import("../../types/next-types").RouteDefinition[];
|
|
17
|
+
data: {
|
|
18
|
+
static: import("../../types/next-types").DataRouteDefinition[];
|
|
19
|
+
dynamic: import("../../types/next-types").DataRouteDefinition[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
locales: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare const ConfigHeaders: import("../../types/next-types").Header[] | undefined;
|
|
25
|
+
export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest;
|
|
26
|
+
export declare const AppPathsManifestKeys: string[];
|
|
27
|
+
export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { debug } from "../logger";
|
|
3
|
+
import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest,
|
|
4
|
+
// loadPublicAssets,
|
|
5
|
+
loadRoutesManifest, } from "./util.js";
|
|
6
|
+
export const NEXT_DIR = path.join(__dirname, ".next");
|
|
7
|
+
export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
|
|
8
|
+
debug({ NEXT_DIR, OPEN_NEXT_DIR });
|
|
9
|
+
//TODO: inject these values at build time
|
|
10
|
+
export const NextConfig = /* @__PURE__ */ loadConfig(NEXT_DIR);
|
|
11
|
+
export const BuildId = /* @__PURE__ */ loadBuildId(NEXT_DIR);
|
|
12
|
+
export const HtmlPages = /* @__PURE__ */ loadHtmlPages(NEXT_DIR);
|
|
13
|
+
// export const PublicAssets = loadPublicAssets(OPEN_NEXT_DIR);
|
|
14
|
+
export const RoutesManifest = /* @__PURE__ */ loadRoutesManifest(NEXT_DIR);
|
|
15
|
+
export const ConfigHeaders = /* @__PURE__ */ loadConfigHeaders(NEXT_DIR);
|
|
16
|
+
export const PrerenderManifest =
|
|
17
|
+
/* @__PURE__ */ loadPrerenderManifest(NEXT_DIR);
|
|
18
|
+
export const AppPathsManifestKeys =
|
|
19
|
+
/* @__PURE__ */ loadAppPathsManifestKeys(NEXT_DIR);
|
|
20
|
+
export const MiddlewareManifest =
|
|
21
|
+
/* @__PURE__ */ loadMiddlewareManifest(NEXT_DIR);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MiddlewareManifest, NextConfig, PrerenderManifest } from "../../types/next-types";
|
|
2
|
+
import { PublicFiles } from "../../build";
|
|
3
|
+
export declare function loadConfig(nextDir: string): NextConfig;
|
|
4
|
+
export declare function loadBuildId(nextDir: string): string;
|
|
5
|
+
export declare function loadHtmlPages(nextDir: string): string[];
|
|
6
|
+
export declare function loadPublicAssets(openNextDir: string): PublicFiles;
|
|
7
|
+
export declare function loadRoutesManifest(nextDir: string): {
|
|
8
|
+
basePath: string | undefined;
|
|
9
|
+
rewrites: {
|
|
10
|
+
beforeFiles: import("../../types/next-types").RewriteDefinition[];
|
|
11
|
+
afterFiles: import("../../types/next-types").RewriteDefinition[];
|
|
12
|
+
fallback: import("../../types/next-types").RewriteDefinition[];
|
|
13
|
+
};
|
|
14
|
+
redirects: import("../../types/next-types").RedirectDefinition[];
|
|
15
|
+
routes: {
|
|
16
|
+
static: import("../../types/next-types").RouteDefinition[];
|
|
17
|
+
dynamic: import("../../types/next-types").RouteDefinition[];
|
|
18
|
+
data: {
|
|
19
|
+
static: import("../../types/next-types").DataRouteDefinition[];
|
|
20
|
+
dynamic: import("../../types/next-types").DataRouteDefinition[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
locales: string[];
|
|
24
|
+
};
|
|
25
|
+
export declare function loadConfigHeaders(nextDir: string): import("../../types/next-types").Header[] | undefined;
|
|
26
|
+
export declare function loadPrerenderManifest(nextDir: string): PrerenderManifest;
|
|
27
|
+
export declare function loadAppPathsManifestKeys(nextDir: string): string[];
|
|
28
|
+
export declare function loadMiddlewareManifest(nextDir: string): MiddlewareManifest;
|