sst 2.26.11 → 2.28.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/constructs/AstroSite.d.ts +83 -41
- package/constructs/AstroSite.js +160 -56
- package/constructs/NextjsSite.d.ts +49 -10
- package/constructs/NextjsSite.js +88 -8
- package/constructs/RemixSite.d.ts +3 -0
- package/constructs/Service.d.ts +5 -2
- package/constructs/Service.js +3 -0
- package/constructs/SolidStartSite.d.ts +3 -0
- package/constructs/SsrFunction.js +1 -1
- package/constructs/SsrSite.d.ts +24 -5
- package/constructs/SsrSite.js +120 -98
- package/constructs/SvelteKitSite.d.ts +3 -0
- package/node/graphql/index.d.ts +1 -1
- package/package.json +2 -1
- package/support/ssr-warmer/index.mjs +16 -13
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
import { SsrSite } from "./SsrSite.js";
|
|
1
|
+
import { SsrSite, SsrSiteNormalizedProps, SsrSiteProps } from "./SsrSite.js";
|
|
2
|
+
import { AllowedMethods } from "aws-cdk-lib/aws-cloudfront";
|
|
3
|
+
import { Construct } from "constructs";
|
|
4
|
+
export interface AstroSiteProps extends SsrSiteProps {
|
|
5
|
+
regional?: SsrSiteProps["regional"] & {
|
|
6
|
+
/**
|
|
7
|
+
* List all routes that will be handling non-GET requests. For example, routes like form submissions, logins, and API endpoints.
|
|
8
|
+
*
|
|
9
|
+
* Route patterns are case sensitive. And the following wildcard characters can be used:
|
|
10
|
+
* - "*" matches 0 or more characters.
|
|
11
|
+
* - "?" matches exactly 1 character.
|
|
12
|
+
*
|
|
13
|
+
* Matched routes will be handled directly by the server function.
|
|
14
|
+
* @default true
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* regional: {
|
|
18
|
+
* serverRoutes: [
|
|
19
|
+
* "feedback", // Feedback page which requires POST method
|
|
20
|
+
* "login", // Login page which requires POST method
|
|
21
|
+
* "user/*", // Directory of user routes which are all SSR
|
|
22
|
+
* "api/*" // Directory of API endpoints which require all methods
|
|
23
|
+
* ]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
serverRoutes?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Supports [streaming](https://docs.astro.build/en/guides/server-side-rendering/#using-streaming-to-improve-page-performance) responses.
|
|
30
|
+
* @default true
|
|
31
|
+
* @example
|
|
32
|
+
* ```js
|
|
33
|
+
* regional: {
|
|
34
|
+
* streaming: false,
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
streaming?: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
type AstroSiteNormalizedProps = AstroSiteProps & SsrSiteNormalizedProps;
|
|
2
42
|
/**
|
|
3
43
|
* The `AstroSite` construct is a higher level CDK construct that makes it easy to create a Astro app.
|
|
4
44
|
* @example
|
|
@@ -11,55 +51,56 @@ import { SsrSite } from "./SsrSite.js";
|
|
|
11
51
|
* ```
|
|
12
52
|
*/
|
|
13
53
|
export declare class AstroSite extends SsrSite {
|
|
14
|
-
|
|
54
|
+
props: AstroSiteNormalizedProps;
|
|
55
|
+
constructor(scope: Construct, id: string, props?: AstroSiteProps);
|
|
56
|
+
private static getBuildMeta;
|
|
57
|
+
private static getCFRoutingFunction;
|
|
15
58
|
protected plan(): {
|
|
16
|
-
cloudFrontFunctions?: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
handler: string;
|
|
48
|
-
};
|
|
49
|
-
streaming: true;
|
|
50
|
-
} | undefined;
|
|
51
|
-
};
|
|
59
|
+
cloudFrontFunctions?: Record<string, {
|
|
60
|
+
constructId: string;
|
|
61
|
+
injections: string[];
|
|
62
|
+
}> | undefined;
|
|
63
|
+
edgeFunctions?: Record<string, {
|
|
64
|
+
constructId: string;
|
|
65
|
+
function: import("./EdgeFunction.js").EdgeFunctionProps;
|
|
66
|
+
}> | undefined;
|
|
67
|
+
origins: Record<string, {
|
|
68
|
+
type: "function";
|
|
69
|
+
constructId: string;
|
|
70
|
+
function: import("./SsrFunction.js").SsrFunctionProps;
|
|
71
|
+
streaming?: boolean | undefined;
|
|
72
|
+
} | {
|
|
73
|
+
type: "image-optimization-function";
|
|
74
|
+
function: import("aws-cdk-lib/aws-lambda").FunctionProps;
|
|
75
|
+
} | {
|
|
76
|
+
type: "s3";
|
|
77
|
+
originPath?: string | undefined;
|
|
78
|
+
copy: {
|
|
79
|
+
from: string;
|
|
80
|
+
to: string;
|
|
81
|
+
cached: boolean;
|
|
82
|
+
versionedSubDir?: string | undefined;
|
|
83
|
+
}[];
|
|
84
|
+
} | {
|
|
85
|
+
type: "group";
|
|
86
|
+
primaryOriginName: string;
|
|
87
|
+
fallbackOriginName: string;
|
|
88
|
+
fallbackStatusCodes?: number[] | undefined;
|
|
89
|
+
}>;
|
|
52
90
|
behaviors: {
|
|
53
91
|
cacheType: "server" | "static";
|
|
54
92
|
pattern?: string | undefined;
|
|
55
|
-
origin:
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
origin: string;
|
|
94
|
+
allowedMethods?: AllowedMethods | undefined;
|
|
95
|
+
cfFunction?: string | undefined;
|
|
96
|
+
edgeFunction?: string | undefined;
|
|
58
97
|
}[];
|
|
98
|
+
errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
|
|
59
99
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
60
100
|
buildId?: string | undefined;
|
|
61
101
|
warmerConfig?: {
|
|
62
102
|
function: string;
|
|
103
|
+
schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
|
|
63
104
|
} | undefined;
|
|
64
105
|
};
|
|
65
106
|
getConstructMetadata(): {
|
|
@@ -76,3 +117,4 @@ export declare class AstroSite extends SsrSite {
|
|
|
76
117
|
type: "AstroSite";
|
|
77
118
|
};
|
|
78
119
|
}
|
|
120
|
+
export {};
|
package/constructs/AstroSite.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { SsrSite } from "./SsrSite.js";
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { SsrSite, } from "./SsrSite.js";
|
|
4
|
+
import { AllowedMethods } from "aws-cdk-lib/aws-cloudfront";
|
|
5
|
+
const BUILD_META_FILE_NAME = "sst.buildMeta.json";
|
|
4
6
|
/**
|
|
5
7
|
* The `AstroSite` construct is a higher level CDK construct that makes it easy to create a Astro app.
|
|
6
8
|
* @example
|
|
@@ -13,79 +15,181 @@ import { SsrSite } from "./SsrSite.js";
|
|
|
13
15
|
* ```
|
|
14
16
|
*/
|
|
15
17
|
export class AstroSite extends SsrSite {
|
|
16
|
-
|
|
18
|
+
constructor(scope, id, props) {
|
|
19
|
+
super(scope, id, {
|
|
20
|
+
...props,
|
|
21
|
+
typesPath: props?.typesPath ?? "src",
|
|
22
|
+
fileOptions: props?.fileOptions ?? [
|
|
23
|
+
{
|
|
24
|
+
exclude: "*",
|
|
25
|
+
include: "*.css",
|
|
26
|
+
cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
27
|
+
contentType: "text/css; charset=UTF-8",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
exclude: "*",
|
|
31
|
+
include: "*.js",
|
|
32
|
+
cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
33
|
+
contentType: "application/javascript; charset=UTF-8",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
exclude: "*",
|
|
37
|
+
include: "*.html",
|
|
38
|
+
cacheControl: "public,max-age=0,s-maxage=31536000,must-revalidate",
|
|
39
|
+
contentType: "text/html; charset=UTF-8",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
regional: {
|
|
43
|
+
streaming: props?.regional?.streaming ?? true,
|
|
44
|
+
...props?.regional,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
static getBuildMeta(filePath) {
|
|
49
|
+
if (!existsSync(filePath)) {
|
|
50
|
+
throw new Error(`Could not find build meta file at ${filePath}. Update your 'astro-sst' package version and rebuild your Astro site.`);
|
|
51
|
+
}
|
|
52
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
53
|
+
}
|
|
54
|
+
static getCFRoutingFunction({ routes, pageResolution, }) {
|
|
55
|
+
const serializedRoutes = "[\n" +
|
|
56
|
+
routes
|
|
57
|
+
.map((route) => {
|
|
58
|
+
return ` {route: "${route.route}", pattern: ${route.pattern}, type: "${route.type}", ${typeof route.prerender !== "undefined"
|
|
59
|
+
? `prerender: ${route.prerender}, `
|
|
60
|
+
: ``}${route.redirectPath ? `redirectPath: "${route.redirectPath}", ` : ""}${route.redirectStatus
|
|
61
|
+
? `redirectStatus: ${route.redirectStatus}`
|
|
62
|
+
: ""} }`;
|
|
63
|
+
})
|
|
64
|
+
.join(",\n") +
|
|
65
|
+
"\n ]";
|
|
66
|
+
return ` // AstroSite CF Routing Function
|
|
67
|
+
var astroRoutes = ${serializedRoutes};
|
|
68
|
+
var matchedRoute = astroRoutes.find((route) => route.pattern.test(request.uri));
|
|
69
|
+
if (matchedRoute) {
|
|
70
|
+
if (matchedRoute.type === "redirect") {
|
|
71
|
+
var redirectPath = matchedRoute.redirectPath;
|
|
72
|
+
matchedRoute.pattern.exec(request.uri).forEach((match, index) => {
|
|
73
|
+
redirectPath = redirectPath.replace(\`\\\${\${index}}\`, match);
|
|
74
|
+
});
|
|
75
|
+
var statusCode = matchedRoute.redirectStatus || 308;
|
|
76
|
+
return {
|
|
77
|
+
statusCode,
|
|
78
|
+
headers: { location: { value: redirectPath } },
|
|
79
|
+
};
|
|
80
|
+
} else if (matchedRoute.type === "page" && matchedRoute.prerender) {
|
|
81
|
+
${pageResolution === "file"
|
|
82
|
+
? `request.uri = request.uri === "/" ? "/index.html" : request.uri.replace(/\\/?$/, ".html");`
|
|
83
|
+
: `request.uri = request.uri.replace(/\\/?$/, "/index.html");`}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// End AstroSite CF Routing Function`;
|
|
87
|
+
}
|
|
17
88
|
plan() {
|
|
18
|
-
const { path: sitePath, edge } = this.props;
|
|
89
|
+
const { path: sitePath, edge, regional } = this.props;
|
|
90
|
+
const buildMeta = AstroSite.getBuildMeta(join(sitePath, "dist", BUILD_META_FILE_NAME));
|
|
19
91
|
const serverConfig = {
|
|
20
92
|
description: "Server handler for Astro",
|
|
21
|
-
handler:
|
|
93
|
+
handler: join(sitePath, "dist", "server", "entry.handler"),
|
|
22
94
|
};
|
|
23
|
-
|
|
95
|
+
const plan = {
|
|
24
96
|
cloudFrontFunctions: {
|
|
25
97
|
serverCfFunction: {
|
|
26
98
|
constructId: "CloudFrontFunction",
|
|
27
|
-
injections: [
|
|
99
|
+
injections: [
|
|
100
|
+
this.useCloudFrontFunctionHostHeaderInjection(),
|
|
101
|
+
...(!edge ? [AstroSite.getCFRoutingFunction(buildMeta)] : []),
|
|
102
|
+
],
|
|
28
103
|
},
|
|
29
104
|
},
|
|
30
|
-
edgeFunctions: edge
|
|
31
|
-
? {
|
|
32
|
-
edgeServer: {
|
|
33
|
-
constructId: "Server",
|
|
34
|
-
function: {
|
|
35
|
-
scopeOverride: this,
|
|
36
|
-
...serverConfig,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
}
|
|
40
|
-
: undefined,
|
|
41
105
|
origins: {
|
|
42
|
-
|
|
43
|
-
? {}
|
|
44
|
-
: {
|
|
45
|
-
regionalServer: {
|
|
46
|
-
type: "function",
|
|
47
|
-
constructId: "ServerFunction",
|
|
48
|
-
function: serverConfig,
|
|
49
|
-
streaming: true,
|
|
50
|
-
},
|
|
51
|
-
}),
|
|
52
|
-
s3: {
|
|
106
|
+
staticsServer: {
|
|
53
107
|
type: "s3",
|
|
54
108
|
copy: [
|
|
55
109
|
{
|
|
56
|
-
from:
|
|
110
|
+
from: buildMeta.clientBuildOutputDir,
|
|
57
111
|
to: "",
|
|
58
112
|
cached: true,
|
|
59
|
-
versionedSubDir:
|
|
113
|
+
versionedSubDir: buildMeta.clientBuildVersionedSubDir,
|
|
60
114
|
},
|
|
61
115
|
],
|
|
62
116
|
},
|
|
63
117
|
},
|
|
64
|
-
behaviors: [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
cfFunction: "serverCfFunction",
|
|
75
|
-
origin: "regionalServer",
|
|
118
|
+
behaviors: [],
|
|
119
|
+
errorResponses: [],
|
|
120
|
+
};
|
|
121
|
+
if (edge) {
|
|
122
|
+
plan.edgeFunctions = {
|
|
123
|
+
edgeServer: {
|
|
124
|
+
constructId: "Server",
|
|
125
|
+
function: {
|
|
126
|
+
scopeOverride: this,
|
|
127
|
+
...serverConfig,
|
|
76
128
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
plan.behaviors.push({
|
|
132
|
+
cacheType: "server",
|
|
133
|
+
cfFunction: "serverCfFunction",
|
|
134
|
+
edgeFunction: "edgeServer",
|
|
135
|
+
origin: "staticsServer",
|
|
136
|
+
}, ...readdirSync(join(sitePath, buildMeta.clientBuildOutputDir)).map((item) => ({
|
|
137
|
+
cacheType: "static",
|
|
138
|
+
pattern: statSync(join(sitePath, buildMeta.clientBuildOutputDir, item)).isDirectory()
|
|
139
|
+
? `${item}/*`
|
|
140
|
+
: item,
|
|
141
|
+
origin: "staticsServer",
|
|
142
|
+
})));
|
|
143
|
+
plan.behaviors.push({
|
|
144
|
+
cacheType: "server",
|
|
145
|
+
cfFunction: "serverCfFunction",
|
|
146
|
+
edgeFunction: "edgeServer",
|
|
147
|
+
origin: "staticsServer",
|
|
148
|
+
}, ...readdirSync(join(sitePath, buildMeta.clientBuildOutputDir)).map((item) => ({
|
|
149
|
+
cacheType: "static",
|
|
150
|
+
pattern: statSync(join(sitePath, buildMeta.clientBuildOutputDir, item)).isDirectory()
|
|
151
|
+
? `${item}/*`
|
|
152
|
+
: item,
|
|
153
|
+
origin: "staticsServer",
|
|
154
|
+
})));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
plan.origins.regionalServer = {
|
|
158
|
+
type: "function",
|
|
159
|
+
constructId: "ServerFunction",
|
|
160
|
+
function: serverConfig,
|
|
161
|
+
streaming: regional?.streaming,
|
|
162
|
+
};
|
|
163
|
+
plan.origins.fallthroughServer = {
|
|
164
|
+
type: "group",
|
|
165
|
+
primaryOriginName: "staticsServer",
|
|
166
|
+
fallbackOriginName: "regionalServer",
|
|
167
|
+
fallbackStatusCodes: [403, 404],
|
|
168
|
+
};
|
|
169
|
+
plan.behaviors.push({
|
|
170
|
+
cacheType: "server",
|
|
171
|
+
cfFunction: "serverCfFunction",
|
|
172
|
+
origin: "fallthroughServer",
|
|
173
|
+
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
|
|
174
|
+
}, {
|
|
175
|
+
cacheType: "static",
|
|
176
|
+
pattern: `${buildMeta.clientBuildVersionedSubDir}/*`,
|
|
177
|
+
origin: "staticsServer",
|
|
178
|
+
}, ...(regional?.serverRoutes ?? []).map((route) => ({
|
|
179
|
+
cacheType: "server",
|
|
180
|
+
pattern: route,
|
|
181
|
+
origin: "regionalServer",
|
|
182
|
+
})));
|
|
183
|
+
const notFoundRoute = buildMeta.routes.find(({ route, type }) => route.match(/^\/404\/?$/) && type === "page");
|
|
184
|
+
if (notFoundRoute) {
|
|
185
|
+
plan.errorResponses?.push({
|
|
186
|
+
httpStatus: 404,
|
|
187
|
+
responsePagePath: "/404.html",
|
|
188
|
+
responseHttpStatus: 404,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return this.validatePlan(plan);
|
|
89
193
|
}
|
|
90
194
|
getConstructMetadata() {
|
|
91
195
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import { Runtime, FunctionProps, Architecture } from "aws-cdk-lib/aws-lambda";
|
|
3
|
-
import { SsrSite, SsrSiteProps } from "./SsrSite.js";
|
|
3
|
+
import { SsrSite, SsrSiteNormalizedProps, SsrSiteProps } from "./SsrSite.js";
|
|
4
4
|
import { Size } from "./util/size.js";
|
|
5
5
|
import { Bucket } from "aws-cdk-lib/aws-s3";
|
|
6
6
|
export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
|
|
@@ -10,11 +10,49 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
|
|
|
10
10
|
* @default 1024 MB
|
|
11
11
|
* @example
|
|
12
12
|
* ```js
|
|
13
|
-
*
|
|
13
|
+
* imageOptimization: {
|
|
14
|
+
* memorySize: "512 MB",
|
|
15
|
+
* }
|
|
14
16
|
* ```
|
|
15
17
|
*/
|
|
16
18
|
memorySize?: number | Size;
|
|
17
19
|
};
|
|
20
|
+
experimental?: {
|
|
21
|
+
/**
|
|
22
|
+
* Enable streaming. Currently an experimental feature in OpenNext.
|
|
23
|
+
* @default false
|
|
24
|
+
* @example
|
|
25
|
+
* ```js
|
|
26
|
+
* experimental: {
|
|
27
|
+
* streaming: true,
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
streaming?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Disabling incremental cache will cause the entire page to be revalidated on each request. This can result in ISR and SSG pages to be in an inconsistent state. Specify this option if you are using SSR pages only.
|
|
34
|
+
*
|
|
35
|
+
* Note that it is possible to disable incremental cache while leaving on-demand revalidation enabled.
|
|
36
|
+
* @default false
|
|
37
|
+
* @example
|
|
38
|
+
* ```js
|
|
39
|
+
* experimental: {
|
|
40
|
+
* disableIncrementalCache: true,
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
disableIncrementalCache?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Disabling DynamoDB cache will cause on-demand revalidation by path (`revalidatePath`) and by cache tag (`revalidateTag`) to fail silently.
|
|
46
|
+
* @default false
|
|
47
|
+
* @example
|
|
48
|
+
* ```js
|
|
49
|
+
* experimental: {
|
|
50
|
+
* disableDynamoDBCache: true,
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
disableDynamoDBCache?: boolean;
|
|
55
|
+
};
|
|
18
56
|
cdk?: SsrSiteProps["cdk"] & {
|
|
19
57
|
revalidation?: Pick<FunctionProps, "vpc" | "vpcSubnets">;
|
|
20
58
|
/**
|
|
@@ -47,6 +85,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
|
|
|
47
85
|
serverCachePolicy?: NonNullable<SsrSiteProps["cdk"]>["serverCachePolicy"];
|
|
48
86
|
};
|
|
49
87
|
}
|
|
88
|
+
type NextjsSiteNormalizedProps = NextjsSiteProps & SsrSiteNormalizedProps;
|
|
50
89
|
/**
|
|
51
90
|
* The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
|
|
52
91
|
* @example
|
|
@@ -59,13 +98,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
|
|
|
59
98
|
* ```
|
|
60
99
|
*/
|
|
61
100
|
export declare class NextjsSite extends SsrSite {
|
|
62
|
-
|
|
63
|
-
path: Exclude<NextjsSiteProps["path"], undefined>;
|
|
64
|
-
runtime: Exclude<NextjsSiteProps["runtime"], undefined>;
|
|
65
|
-
timeout: Exclude<NextjsSiteProps["timeout"], undefined>;
|
|
66
|
-
memorySize: Exclude<NextjsSiteProps["memorySize"], undefined>;
|
|
67
|
-
waitForInvalidation: Exclude<NextjsSiteProps["waitForInvalidation"], undefined>;
|
|
68
|
-
};
|
|
101
|
+
props: NextjsSiteNormalizedProps;
|
|
69
102
|
constructor(scope: Construct, id: string, props?: NextjsSiteProps);
|
|
70
103
|
protected plan(bucket: Bucket): {
|
|
71
104
|
cloudFrontFunctions?: {
|
|
@@ -134,22 +167,27 @@ export declare class NextjsSite extends SsrSite {
|
|
|
134
167
|
CACHE_BUCKET_REGION: string;
|
|
135
168
|
};
|
|
136
169
|
};
|
|
170
|
+
streaming: boolean | undefined;
|
|
137
171
|
} | undefined;
|
|
138
172
|
};
|
|
139
173
|
behaviors: {
|
|
140
174
|
cacheType: "server" | "static";
|
|
141
175
|
pattern?: string | undefined;
|
|
142
176
|
origin: "s3" | "regionalServer" | "imageOptimizer";
|
|
177
|
+
allowedMethods?: import("aws-cdk-lib/aws-cloudfront").AllowedMethods | undefined;
|
|
143
178
|
cfFunction?: "serverCfFunction" | undefined;
|
|
144
179
|
edgeFunction?: "edgeServer" | undefined;
|
|
145
180
|
}[];
|
|
181
|
+
errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
|
|
146
182
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
147
183
|
buildId?: string | undefined;
|
|
148
184
|
warmerConfig?: {
|
|
149
185
|
function: string;
|
|
186
|
+
schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
|
|
150
187
|
} | undefined;
|
|
151
188
|
};
|
|
152
|
-
|
|
189
|
+
private createRevalidationQueue;
|
|
190
|
+
private createRevalidationTable;
|
|
153
191
|
getConstructMetadata(): {
|
|
154
192
|
data: {
|
|
155
193
|
mode: "placeholder" | "deployed";
|
|
@@ -164,3 +202,4 @@ export declare class NextjsSite extends SsrSite {
|
|
|
164
202
|
type: "NextjsSite";
|
|
165
203
|
};
|
|
166
204
|
}
|
|
205
|
+
export {};
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { Duration as CdkDuration } from "aws-cdk-lib/core";
|
|
3
|
+
import { Duration as CdkDuration, RemovalPolicy, CustomResource, } from "aws-cdk-lib/core";
|
|
4
4
|
import { Code, Runtime, Function as CdkFunction, Architecture, } from "aws-cdk-lib/aws-lambda";
|
|
5
|
+
import { AttributeType, Billing, TableV2 as Table, } from "aws-cdk-lib/aws-dynamodb";
|
|
6
|
+
import { Provider } from "aws-cdk-lib/custom-resources";
|
|
5
7
|
import { Queue } from "aws-cdk-lib/aws-sqs";
|
|
6
8
|
import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
|
|
7
9
|
import { Stack } from "./Stack.js";
|
|
8
10
|
import { SsrSite } from "./SsrSite.js";
|
|
9
11
|
import { toCdkSize } from "./util/size.js";
|
|
12
|
+
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
13
|
+
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
10
14
|
/**
|
|
11
15
|
* The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
|
|
12
16
|
* @example
|
|
@@ -20,14 +24,34 @@ import { toCdkSize } from "./util/size.js";
|
|
|
20
24
|
*/
|
|
21
25
|
export class NextjsSite extends SsrSite {
|
|
22
26
|
constructor(scope, id, props) {
|
|
27
|
+
const { streaming, disableDynamoDBCache, disableIncrementalCache } = {
|
|
28
|
+
streaming: false,
|
|
29
|
+
disableDynamoDBCache: false,
|
|
30
|
+
disableIncrementalCache: false,
|
|
31
|
+
...props?.experimental,
|
|
32
|
+
};
|
|
23
33
|
super(scope, id, {
|
|
24
|
-
buildCommand:
|
|
34
|
+
buildCommand: [
|
|
35
|
+
"npx --yes open-next@2.2.1 build",
|
|
36
|
+
...(streaming ? ["--streaming"] : []),
|
|
37
|
+
...(disableDynamoDBCache
|
|
38
|
+
? ["--dangerously-disable-dynamodb-cache"]
|
|
39
|
+
: []),
|
|
40
|
+
...(disableIncrementalCache
|
|
41
|
+
? ["--dangerously-disable-incremental-cache"]
|
|
42
|
+
: []),
|
|
43
|
+
].join(" "),
|
|
25
44
|
...props,
|
|
26
45
|
});
|
|
27
|
-
|
|
46
|
+
if (!disableIncrementalCache) {
|
|
47
|
+
this.createRevalidationQueue();
|
|
48
|
+
if (!disableDynamoDBCache) {
|
|
49
|
+
this.createRevalidationTable();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
28
52
|
}
|
|
29
53
|
plan(bucket) {
|
|
30
|
-
const { path: sitePath, edge, imageOptimization } = this.props;
|
|
54
|
+
const { path: sitePath, edge, experimental, imageOptimization, } = this.props;
|
|
31
55
|
const serverConfig = {
|
|
32
56
|
description: "Next.js server",
|
|
33
57
|
bundle: path.join(sitePath, ".open-next", "server-function"),
|
|
@@ -61,6 +85,7 @@ export class NextjsSite extends SsrSite {
|
|
|
61
85
|
type: "function",
|
|
62
86
|
constructId: "ServerFunction",
|
|
63
87
|
function: serverConfig,
|
|
88
|
+
streaming: experimental?.streaming,
|
|
64
89
|
},
|
|
65
90
|
}),
|
|
66
91
|
imageOptimizer: {
|
|
@@ -172,10 +197,11 @@ export class NextjsSite extends SsrSite {
|
|
|
172
197
|
},
|
|
173
198
|
});
|
|
174
199
|
}
|
|
175
|
-
|
|
200
|
+
createRevalidationQueue() {
|
|
176
201
|
if (!this.serverFunction)
|
|
177
202
|
return;
|
|
178
203
|
const { cdk } = this.props;
|
|
204
|
+
const server = this.serverFunction;
|
|
179
205
|
const queue = new Queue(this, "RevalidationQueue", {
|
|
180
206
|
fifo: true,
|
|
181
207
|
receiveMessageWaitTime: CdkDuration.seconds(20),
|
|
@@ -190,10 +216,64 @@ export class NextjsSite extends SsrSite {
|
|
|
190
216
|
});
|
|
191
217
|
consumer.addEventSource(new SqsEventSource(queue, { batchSize: 5 }));
|
|
192
218
|
// Allow server to send messages to the queue
|
|
219
|
+
server.addEnvironment("REVALIDATION_QUEUE_URL", queue.queueUrl);
|
|
220
|
+
server.addEnvironment("REVALIDATION_QUEUE_REGION", Stack.of(this).region);
|
|
221
|
+
queue.grantSendMessages(server.role);
|
|
222
|
+
}
|
|
223
|
+
createRevalidationTable() {
|
|
224
|
+
if (!this.serverFunction)
|
|
225
|
+
return;
|
|
226
|
+
const { path: sitePath } = this.props;
|
|
193
227
|
const server = this.serverFunction;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
228
|
+
const table = new Table(this, "RevalidationTable", {
|
|
229
|
+
partitionKey: { name: "tag", type: AttributeType.STRING },
|
|
230
|
+
sortKey: { name: "path", type: AttributeType.STRING },
|
|
231
|
+
pointInTimeRecovery: true,
|
|
232
|
+
billing: Billing.onDemand(),
|
|
233
|
+
globalSecondaryIndexes: [
|
|
234
|
+
{
|
|
235
|
+
indexName: "revalidate",
|
|
236
|
+
partitionKey: { name: "path", type: AttributeType.STRING },
|
|
237
|
+
sortKey: { name: "revalidatedAt", type: AttributeType.NUMBER },
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
241
|
+
});
|
|
242
|
+
server?.addEnvironment("CACHE_DYNAMO_TABLE", table.tableName);
|
|
243
|
+
table.grantReadWriteData(server.role);
|
|
244
|
+
const dynamodbProviderPath = path.join(sitePath, ".open-next", "dynamodb-provider");
|
|
245
|
+
if (fs.existsSync(dynamodbProviderPath)) {
|
|
246
|
+
const insertFn = new CdkFunction(this, "RevalidationInsertFunction", {
|
|
247
|
+
description: "Next.js revalidation data insert",
|
|
248
|
+
handler: "index.handler",
|
|
249
|
+
code: Code.fromAsset(dynamodbProviderPath),
|
|
250
|
+
runtime: Runtime.NODEJS_18_X,
|
|
251
|
+
timeout: CdkDuration.minutes(15),
|
|
252
|
+
initialPolicy: [
|
|
253
|
+
new PolicyStatement({
|
|
254
|
+
actions: [
|
|
255
|
+
"dynamodb:BatchWriteItem",
|
|
256
|
+
"dynamodb:PutItem",
|
|
257
|
+
"dynamodb:DescribeTable",
|
|
258
|
+
],
|
|
259
|
+
resources: [table.tableArn],
|
|
260
|
+
}),
|
|
261
|
+
],
|
|
262
|
+
environment: {
|
|
263
|
+
CACHE_DYNAMO_TABLE: table.tableName,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
const provider = new Provider(this, "RevalidationProvider", {
|
|
267
|
+
onEventHandler: insertFn,
|
|
268
|
+
logRetention: RetentionDays.ONE_DAY,
|
|
269
|
+
});
|
|
270
|
+
new CustomResource(this, "RevalidationResource", {
|
|
271
|
+
serviceToken: provider.serviceToken,
|
|
272
|
+
properties: {
|
|
273
|
+
version: Date.now().toString(),
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
}
|
|
197
277
|
}
|
|
198
278
|
getConstructMetadata() {
|
|
199
279
|
return {
|
|
@@ -69,13 +69,16 @@ export declare class RemixSite extends SsrSite {
|
|
|
69
69
|
cacheType: "server" | "static";
|
|
70
70
|
pattern?: string | undefined;
|
|
71
71
|
origin: "s3" | "regionalServer";
|
|
72
|
+
allowedMethods?: import("aws-cdk-lib/aws-cloudfront").AllowedMethods | undefined;
|
|
72
73
|
cfFunction?: "serverCfFunction" | "staticCfFunction" | undefined;
|
|
73
74
|
edgeFunction?: "edgeServer" | undefined;
|
|
74
75
|
}[];
|
|
76
|
+
errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
|
|
75
77
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
76
78
|
buildId?: string | undefined;
|
|
77
79
|
warmerConfig?: {
|
|
78
80
|
function: string;
|
|
81
|
+
schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
|
|
79
82
|
} | undefined;
|
|
80
83
|
};
|
|
81
84
|
protected getServerModuleFormat(): "cjs" | "esm";
|
package/constructs/Service.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
+
import { DistributionProps } from "aws-cdk-lib/aws-cloudfront";
|
|
2
3
|
import { DistributionDomainProps } from "./Distribution.js";
|
|
3
4
|
import { SSTConstruct } from "./Construct.js";
|
|
4
5
|
import { Permissions } from "./util/permission.js";
|
|
@@ -18,6 +19,8 @@ declare const supportedCpus: {
|
|
|
18
19
|
};
|
|
19
20
|
export interface ServiceDomainProps extends DistributionDomainProps {
|
|
20
21
|
}
|
|
22
|
+
export interface ServiceCdkDistributionProps extends Omit<DistributionProps, "defaultBehavior"> {
|
|
23
|
+
}
|
|
21
24
|
export interface ServiceProps {
|
|
22
25
|
/**
|
|
23
26
|
* Path to the directory where the app is located.
|
|
@@ -272,7 +275,7 @@ export interface ServiceProps {
|
|
|
272
275
|
};
|
|
273
276
|
cdk?: {
|
|
274
277
|
/**
|
|
275
|
-
* By default, SST creates a CloudFront distribution.
|
|
278
|
+
* By default, SST creates a CloudFront distribution. Pass in a value to override the default settings this construct uses to create the CDK `Distribution` internally. Alternatively, set this to `false` to skip creating the distribution.
|
|
276
279
|
* @default true
|
|
277
280
|
* @example
|
|
278
281
|
* ```js
|
|
@@ -283,7 +286,7 @@ export interface ServiceProps {
|
|
|
283
286
|
* }
|
|
284
287
|
* ```
|
|
285
288
|
*/
|
|
286
|
-
cloudfrontDistribution?: boolean;
|
|
289
|
+
cloudfrontDistribution?: boolean | ServiceCdkDistributionProps;
|
|
287
290
|
/**
|
|
288
291
|
* By default, SST creates an Application Load Balancer to distribute requests across containers. Set this to `false` to skip creating the load balancer.
|
|
289
292
|
* @default true
|
package/constructs/Service.js
CHANGED
|
@@ -51,13 +51,16 @@ export declare class SolidStartSite extends SsrSite {
|
|
|
51
51
|
cacheType: "server" | "static";
|
|
52
52
|
pattern?: string | undefined;
|
|
53
53
|
origin: "s3" | "regionalServer";
|
|
54
|
+
allowedMethods?: import("aws-cdk-lib/aws-cloudfront").AllowedMethods | undefined;
|
|
54
55
|
cfFunction?: "serverCfFunction" | undefined;
|
|
55
56
|
edgeFunction?: "edgeServer" | undefined;
|
|
56
57
|
}[];
|
|
58
|
+
errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
|
|
57
59
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
58
60
|
buildId?: string | undefined;
|
|
59
61
|
warmerConfig?: {
|
|
60
62
|
function: string;
|
|
63
|
+
schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
|
|
61
64
|
} | undefined;
|
|
62
65
|
};
|
|
63
66
|
getConstructMetadata(): {
|
package/constructs/SsrSite.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import { Bucket, BucketProps, IBucket } from "aws-cdk-lib/aws-s3";
|
|
3
3
|
import { Function as CdkFunction, FunctionProps as CdkFunctionProps } from "aws-cdk-lib/aws-lambda";
|
|
4
|
-
import { ICachePolicy, IResponseHeadersPolicy,
|
|
4
|
+
import { ICachePolicy, IResponseHeadersPolicy, AllowedMethods, ErrorResponse } from "aws-cdk-lib/aws-cloudfront";
|
|
5
|
+
import { Schedule } from "aws-cdk-lib/aws-events";
|
|
5
6
|
import { DistributionDomainProps } from "./Distribution.js";
|
|
6
7
|
import { SSTConstruct } from "./Construct.js";
|
|
7
8
|
import { NodeJSProps, FunctionProps } from "./Function.js";
|
|
@@ -40,6 +41,13 @@ type S3OriginConfig = {
|
|
|
40
41
|
versionedSubDir?: string;
|
|
41
42
|
}[];
|
|
42
43
|
};
|
|
44
|
+
type OriginGroupConfig = {
|
|
45
|
+
type: "group";
|
|
46
|
+
primaryOriginName: string;
|
|
47
|
+
fallbackOriginName: string;
|
|
48
|
+
fallbackStatusCodes?: number[];
|
|
49
|
+
};
|
|
50
|
+
export type Plan = ReturnType<SsrSite["validatePlan"]>;
|
|
43
51
|
export interface SsrSiteNodeJSProps extends NodeJSProps {
|
|
44
52
|
}
|
|
45
53
|
export interface SsrDomainProps extends DistributionDomainProps {
|
|
@@ -68,6 +76,11 @@ export interface SsrSiteProps {
|
|
|
68
76
|
* @default "."
|
|
69
77
|
*/
|
|
70
78
|
path?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Path relative to the app location where the type definitions are located.
|
|
81
|
+
* @default "."
|
|
82
|
+
*/
|
|
83
|
+
typesPath?: string;
|
|
71
84
|
/**
|
|
72
85
|
* The command for building the website
|
|
73
86
|
* @default `npm run build`
|
|
@@ -292,8 +305,9 @@ export interface SsrSiteProps {
|
|
|
292
305
|
*/
|
|
293
306
|
fileOptions?: SsrSiteFileOptions[];
|
|
294
307
|
}
|
|
295
|
-
type SsrSiteNormalizedProps = SsrSiteProps & {
|
|
308
|
+
export type SsrSiteNormalizedProps = SsrSiteProps & {
|
|
296
309
|
path: Exclude<SsrSiteProps["path"], undefined>;
|
|
310
|
+
typesPath: Exclude<SsrSiteProps["typesPath"], undefined>;
|
|
297
311
|
runtime: Exclude<SsrSiteProps["runtime"], undefined>;
|
|
298
312
|
timeout: Exclude<SsrSiteProps["timeout"], undefined>;
|
|
299
313
|
memorySize: Exclude<SsrSiteProps["memorySize"], undefined>;
|
|
@@ -314,7 +328,6 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
314
328
|
readonly id: string;
|
|
315
329
|
protected props: SsrSiteNormalizedProps;
|
|
316
330
|
protected doNotDeploy: boolean;
|
|
317
|
-
protected typesPath: string;
|
|
318
331
|
protected bucket: Bucket;
|
|
319
332
|
protected serverFunction?: EdgeFunction | SsrFunction;
|
|
320
333
|
private serverFunctionForDev?;
|
|
@@ -335,7 +348,7 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
335
348
|
get cdk(): {
|
|
336
349
|
function: import("aws-cdk-lib/aws-lambda").IFunction | CdkFunction | undefined;
|
|
337
350
|
bucket: Bucket;
|
|
338
|
-
distribution: IDistribution;
|
|
351
|
+
distribution: import("aws-cdk-lib/aws-cloudfront").IDistribution;
|
|
339
352
|
hostedZone: import("aws-cdk-lib/aws-route53").IHostedZone | undefined;
|
|
340
353
|
certificate: import("aws-cdk-lib/aws-certificatemanager").ICertificate | undefined;
|
|
341
354
|
} | undefined;
|
|
@@ -367,7 +380,7 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
367
380
|
getFunctionBinding(): FunctionBindingProps;
|
|
368
381
|
protected useCloudFrontFunctionHostHeaderInjection(): string;
|
|
369
382
|
protected abstract plan(bucket: Bucket): ReturnType<typeof this.validatePlan>;
|
|
370
|
-
protected validatePlan<CloudFrontFunctions extends Record<string, CloudFrontFunctionConfig>, EdgeFunctions extends Record<string, EdgeFunctionConfig>, Origins extends Record<string, FunctionOriginConfig | ImageOptimizationFunctionOriginConfig | S3OriginConfig>>(input: {
|
|
383
|
+
protected validatePlan<CloudFrontFunctions extends Record<string, CloudFrontFunctionConfig>, EdgeFunctions extends Record<string, EdgeFunctionConfig>, Origins extends Record<string, FunctionOriginConfig | ImageOptimizationFunctionOriginConfig | S3OriginConfig | OriginGroupConfig>>(input: {
|
|
371
384
|
cloudFrontFunctions?: CloudFrontFunctions;
|
|
372
385
|
edgeFunctions?: EdgeFunctions;
|
|
373
386
|
origins: Origins;
|
|
@@ -375,13 +388,16 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
375
388
|
cacheType: "server" | "static";
|
|
376
389
|
pattern?: string;
|
|
377
390
|
origin: keyof Origins;
|
|
391
|
+
allowedMethods?: AllowedMethods;
|
|
378
392
|
cfFunction?: keyof CloudFrontFunctions;
|
|
379
393
|
edgeFunction?: keyof EdgeFunctions;
|
|
380
394
|
}[];
|
|
395
|
+
errorResponses?: ErrorResponse[];
|
|
381
396
|
cachePolicyAllowedHeaders?: string[];
|
|
382
397
|
buildId?: string;
|
|
383
398
|
warmerConfig?: {
|
|
384
399
|
function: string;
|
|
400
|
+
schedule?: Schedule;
|
|
385
401
|
};
|
|
386
402
|
}): {
|
|
387
403
|
cloudFrontFunctions?: CloudFrontFunctions | undefined;
|
|
@@ -391,13 +407,16 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
|
|
|
391
407
|
cacheType: "server" | "static";
|
|
392
408
|
pattern?: string;
|
|
393
409
|
origin: keyof Origins;
|
|
410
|
+
allowedMethods?: AllowedMethods;
|
|
394
411
|
cfFunction?: keyof CloudFrontFunctions;
|
|
395
412
|
edgeFunction?: keyof EdgeFunctions;
|
|
396
413
|
}[];
|
|
414
|
+
errorResponses?: ErrorResponse[] | undefined;
|
|
397
415
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
398
416
|
buildId?: string | undefined;
|
|
399
417
|
warmerConfig?: {
|
|
400
418
|
function: string;
|
|
419
|
+
schedule?: Schedule | undefined;
|
|
401
420
|
} | undefined;
|
|
402
421
|
};
|
|
403
422
|
}
|
package/constructs/SsrSite.js
CHANGED
|
@@ -13,7 +13,7 @@ import { Function as CdkFunction, Code, Runtime, FunctionUrlAuthType, InvokeMode
|
|
|
13
13
|
import { Asset } from "aws-cdk-lib/aws-s3-assets";
|
|
14
14
|
import { ViewerProtocolPolicy, AllowedMethods, CachedMethods, LambdaEdgeEventType, CachePolicy, CacheQueryStringBehavior, CacheHeaderBehavior, CacheCookieBehavior, OriginRequestPolicy, Function as CfFunction, FunctionCode as CfFunctionCode, FunctionEventType as CfFunctionEventType, } from "aws-cdk-lib/aws-cloudfront";
|
|
15
15
|
import { AwsCliLayer } from "aws-cdk-lib/lambda-layer-awscli";
|
|
16
|
-
import { S3Origin, HttpOrigin } from "aws-cdk-lib/aws-cloudfront-origins";
|
|
16
|
+
import { S3Origin, HttpOrigin, OriginGroup, } from "aws-cdk-lib/aws-cloudfront-origins";
|
|
17
17
|
import { Rule, Schedule } from "aws-cdk-lib/aws-events";
|
|
18
18
|
import { LambdaFunction } from "aws-cdk-lib/aws-events-targets";
|
|
19
19
|
import { Stack } from "./Stack.js";
|
|
@@ -47,7 +47,6 @@ export class SsrSite extends Construct {
|
|
|
47
47
|
id;
|
|
48
48
|
props;
|
|
49
49
|
doNotDeploy;
|
|
50
|
-
typesPath = ".";
|
|
51
50
|
bucket;
|
|
52
51
|
serverFunction;
|
|
53
52
|
serverFunctionForDev;
|
|
@@ -56,6 +55,7 @@ export class SsrSite extends Construct {
|
|
|
56
55
|
super(scope, rawProps?.cdk?.id || id);
|
|
57
56
|
const props = {
|
|
58
57
|
path: ".",
|
|
58
|
+
typesPath: ".",
|
|
59
59
|
waitForInvalidation: false,
|
|
60
60
|
runtime: "nodejs18.x",
|
|
61
61
|
timeout: "10 seconds",
|
|
@@ -67,11 +67,11 @@ export class SsrSite extends Construct {
|
|
|
67
67
|
const app = scope.node.root;
|
|
68
68
|
const stack = Stack.of(this);
|
|
69
69
|
const self = this;
|
|
70
|
-
const { path: sitePath, buildCommand, runtime, timeout, memorySize, edge, regional, dev, nodejs, permissions, environment, bind, customDomain, waitForInvalidation, fileOptions, warm, cdk, } = props;
|
|
70
|
+
const { path: sitePath, typesPath, buildCommand, runtime, timeout, memorySize, edge, regional, dev, nodejs, permissions, environment, bind, customDomain, waitForInvalidation, fileOptions, warm, cdk, } = props;
|
|
71
71
|
this.doNotDeploy = !stack.isActive || (app.mode === "dev" && !dev?.deploy);
|
|
72
72
|
validateSiteExists();
|
|
73
73
|
validateTimeout();
|
|
74
|
-
writeTypesFile(
|
|
74
|
+
writeTypesFile(typesPath);
|
|
75
75
|
useSites().add(stack.stackName, id, this.constructor.name, props);
|
|
76
76
|
if (this.doNotDeploy) {
|
|
77
77
|
// @ts-expect-error
|
|
@@ -211,13 +211,13 @@ export class SsrSite extends Construct {
|
|
|
211
211
|
return;
|
|
212
212
|
// Create warmer function
|
|
213
213
|
const warmer = new CdkFunction(self, "WarmerFunction", {
|
|
214
|
-
description: "
|
|
214
|
+
description: "SSR warmer",
|
|
215
215
|
code: Code.fromAsset(plan.warmerConfig?.function ??
|
|
216
216
|
path.join(__dirname, "../support/ssr-warmer")),
|
|
217
217
|
runtime: Runtime.NODEJS_18_X,
|
|
218
218
|
handler: "index.handler",
|
|
219
219
|
timeout: CdkDuration.minutes(15),
|
|
220
|
-
memorySize:
|
|
220
|
+
memorySize: 128,
|
|
221
221
|
environment: {
|
|
222
222
|
FUNCTION_NAME: ssrFunctions[0].functionName,
|
|
223
223
|
CONCURRENCY: warm.toString(),
|
|
@@ -226,7 +226,7 @@ export class SsrSite extends Construct {
|
|
|
226
226
|
ssrFunctions[0].grantInvoke(warmer);
|
|
227
227
|
// Create cron job
|
|
228
228
|
new Rule(self, "WarmerRule", {
|
|
229
|
-
schedule: Schedule.rate(CdkDuration.minutes(5)),
|
|
229
|
+
schedule: plan.warmerConfig?.schedule ?? Schedule.rate(CdkDuration.minutes(5)),
|
|
230
230
|
targets: [new LambdaFunction(warmer, { retryAttempts: 0 })],
|
|
231
231
|
});
|
|
232
232
|
// Create custom resource to prewarm on deploy
|
|
@@ -272,6 +272,7 @@ export class SsrSite extends Construct {
|
|
|
272
272
|
}, {}),
|
|
273
273
|
...(cdk?.distribution?.additionalBehaviors || {}),
|
|
274
274
|
},
|
|
275
|
+
errorResponses: plan.errorResponses,
|
|
275
276
|
},
|
|
276
277
|
},
|
|
277
278
|
});
|
|
@@ -300,7 +301,7 @@ export class SsrSite extends Construct {
|
|
|
300
301
|
return {
|
|
301
302
|
origin,
|
|
302
303
|
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
303
|
-
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
|
|
304
|
+
allowedMethods: behavior.allowedMethods ?? AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
|
|
304
305
|
cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
305
306
|
compress: true,
|
|
306
307
|
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
|
|
@@ -319,7 +320,7 @@ export class SsrSite extends Construct {
|
|
|
319
320
|
return {
|
|
320
321
|
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
321
322
|
origin,
|
|
322
|
-
allowedMethods: AllowedMethods.ALLOW_ALL,
|
|
323
|
+
allowedMethods: behavior.allowedMethods ?? AllowedMethods.ALLOW_ALL,
|
|
323
324
|
cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
|
|
324
325
|
compress: true,
|
|
325
326
|
cachePolicy: cdk?.serverCachePolicy ?? useServerBehaviorCachePolicy(),
|
|
@@ -405,99 +406,120 @@ function handler(event) {
|
|
|
405
406
|
});
|
|
406
407
|
return functions;
|
|
407
408
|
}
|
|
409
|
+
function createS3Origin(props) {
|
|
410
|
+
const s3Origin = new S3Origin(bucket, {
|
|
411
|
+
originPath: "/" + (props.originPath ?? ""),
|
|
412
|
+
});
|
|
413
|
+
const assets = createS3OriginAssets(props.copy);
|
|
414
|
+
const assetFileOptions = fileOptions || createS3OriginAssetFileOptions(props.copy);
|
|
415
|
+
const s3deployCR = createS3OriginDeployment(assets, assetFileOptions);
|
|
416
|
+
s3DeployCRs.push(s3deployCR);
|
|
417
|
+
return s3Origin;
|
|
418
|
+
}
|
|
419
|
+
function createFunctionOrigin(props) {
|
|
420
|
+
const fn = new SsrFunction(self, props.constructId, {
|
|
421
|
+
runtime,
|
|
422
|
+
timeout,
|
|
423
|
+
memorySize,
|
|
424
|
+
bind,
|
|
425
|
+
permissions,
|
|
426
|
+
...props.function,
|
|
427
|
+
nodejs: {
|
|
428
|
+
format: "esm",
|
|
429
|
+
...nodejs,
|
|
430
|
+
...props.function.nodejs,
|
|
431
|
+
esbuild: {
|
|
432
|
+
...nodejs?.esbuild,
|
|
433
|
+
...props.function.nodejs?.esbuild,
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
environment: {
|
|
437
|
+
...environment,
|
|
438
|
+
...props.function.environment,
|
|
439
|
+
},
|
|
440
|
+
...cdk?.server,
|
|
441
|
+
});
|
|
442
|
+
ssrFunctions.push(fn);
|
|
443
|
+
bucket.grantReadWrite(fn?.role);
|
|
444
|
+
const fnUrl = fn.addFunctionUrl({
|
|
445
|
+
authType: regional?.enableServerUrlIamAuth
|
|
446
|
+
? FunctionUrlAuthType.AWS_IAM
|
|
447
|
+
: FunctionUrlAuthType.NONE,
|
|
448
|
+
invokeMode: props.streaming
|
|
449
|
+
? InvokeMode.RESPONSE_STREAM
|
|
450
|
+
: InvokeMode.BUFFERED,
|
|
451
|
+
});
|
|
452
|
+
if (regional?.enableServerUrlIamAuth) {
|
|
453
|
+
useFunctionUrlSigningFunction().attachPermissions([
|
|
454
|
+
new PolicyStatement({
|
|
455
|
+
actions: ["lambda:InvokeFunctionUrl"],
|
|
456
|
+
resources: [fn.functionArn],
|
|
457
|
+
}),
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
return new HttpOrigin(Fn.parseDomainName(fnUrl.url), {
|
|
461
|
+
readTimeout: typeof timeout === "string"
|
|
462
|
+
? toCdkDuration(timeout)
|
|
463
|
+
: CdkDuration.seconds(timeout),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function createOriginGroup(props, origins) {
|
|
467
|
+
return new OriginGroup({
|
|
468
|
+
primaryOrigin: origins[props.primaryOriginName],
|
|
469
|
+
fallbackOrigin: origins[props.fallbackOriginName],
|
|
470
|
+
fallbackStatusCodes: props.fallbackStatusCodes,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
function createImageOptimizationFunctionOrigin(props) {
|
|
474
|
+
const fn = new CdkFunction(self, `ImageFunction`, {
|
|
475
|
+
currentVersionOptions: {
|
|
476
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
477
|
+
},
|
|
478
|
+
logRetention: RetentionDays.THREE_DAYS,
|
|
479
|
+
timeout: CdkDuration.seconds(25),
|
|
480
|
+
initialPolicy: [
|
|
481
|
+
new PolicyStatement({
|
|
482
|
+
actions: ["s3:GetObject"],
|
|
483
|
+
resources: [bucket.arnForObjects("*")],
|
|
484
|
+
}),
|
|
485
|
+
],
|
|
486
|
+
...props.function,
|
|
487
|
+
});
|
|
488
|
+
const fnUrl = fn.addFunctionUrl({
|
|
489
|
+
authType: regional?.enableServerUrlIamAuth
|
|
490
|
+
? FunctionUrlAuthType.AWS_IAM
|
|
491
|
+
: FunctionUrlAuthType.NONE,
|
|
492
|
+
});
|
|
493
|
+
if (regional?.enableServerUrlIamAuth) {
|
|
494
|
+
useFunctionUrlSigningFunction().attachPermissions([
|
|
495
|
+
new PolicyStatement({
|
|
496
|
+
actions: ["lambda:InvokeFunctionUrl"],
|
|
497
|
+
resources: [fn.functionArn],
|
|
498
|
+
}),
|
|
499
|
+
]);
|
|
500
|
+
}
|
|
501
|
+
return new HttpOrigin(Fn.parseDomainName(fnUrl.url));
|
|
502
|
+
}
|
|
408
503
|
function createOrigins() {
|
|
409
504
|
const origins = {};
|
|
505
|
+
// Create non-group origins
|
|
410
506
|
Object.entries(plan.origins ?? {}).forEach(([name, props]) => {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
s3DeployCRs.push(s3deployCR);
|
|
507
|
+
switch (props.type) {
|
|
508
|
+
case "s3":
|
|
509
|
+
origins[name] = createS3Origin(props);
|
|
510
|
+
break;
|
|
511
|
+
case "function":
|
|
512
|
+
origins[name] = createFunctionOrigin(props);
|
|
513
|
+
break;
|
|
514
|
+
case "image-optimization-function":
|
|
515
|
+
origins[name] = createImageOptimizationFunctionOrigin(props);
|
|
516
|
+
break;
|
|
422
517
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
memorySize,
|
|
429
|
-
bind,
|
|
430
|
-
permissions,
|
|
431
|
-
...props.function,
|
|
432
|
-
nodejs: {
|
|
433
|
-
format: "esm",
|
|
434
|
-
...nodejs,
|
|
435
|
-
...props.function.nodejs,
|
|
436
|
-
esbuild: {
|
|
437
|
-
...nodejs?.esbuild,
|
|
438
|
-
...props.function.nodejs?.esbuild,
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
environment: {
|
|
442
|
-
...environment,
|
|
443
|
-
...props.function.environment,
|
|
444
|
-
},
|
|
445
|
-
...cdk?.server,
|
|
446
|
-
});
|
|
447
|
-
ssrFunctions.push(fn);
|
|
448
|
-
bucket.grantReadWrite(fn?.role);
|
|
449
|
-
const fnUrl = fn.addFunctionUrl({
|
|
450
|
-
authType: regional?.enableServerUrlIamAuth
|
|
451
|
-
? FunctionUrlAuthType.AWS_IAM
|
|
452
|
-
: FunctionUrlAuthType.NONE,
|
|
453
|
-
invokeMode: props.streaming
|
|
454
|
-
? InvokeMode.RESPONSE_STREAM
|
|
455
|
-
: undefined,
|
|
456
|
-
});
|
|
457
|
-
if (regional?.enableServerUrlIamAuth) {
|
|
458
|
-
useFunctionUrlSigningFunction().attachPermissions([
|
|
459
|
-
new PolicyStatement({
|
|
460
|
-
actions: ["lambda:InvokeFunctionUrl"],
|
|
461
|
-
resources: [fn.functionArn],
|
|
462
|
-
}),
|
|
463
|
-
]);
|
|
464
|
-
}
|
|
465
|
-
origins[name] = new HttpOrigin(Fn.parseDomainName(fnUrl.url), {
|
|
466
|
-
readTimeout: typeof timeout === "string"
|
|
467
|
-
? toCdkDuration(timeout)
|
|
468
|
-
: CdkDuration.seconds(timeout),
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
// Image Optimization Origin
|
|
472
|
-
else if (props.type === "image-optimization-function") {
|
|
473
|
-
const fn = new CdkFunction(self, `ImageFunction`, {
|
|
474
|
-
currentVersionOptions: {
|
|
475
|
-
removalPolicy: RemovalPolicy.DESTROY,
|
|
476
|
-
},
|
|
477
|
-
logRetention: RetentionDays.THREE_DAYS,
|
|
478
|
-
timeout: CdkDuration.seconds(25),
|
|
479
|
-
initialPolicy: [
|
|
480
|
-
new PolicyStatement({
|
|
481
|
-
actions: ["s3:GetObject"],
|
|
482
|
-
resources: [bucket.arnForObjects("*")],
|
|
483
|
-
}),
|
|
484
|
-
],
|
|
485
|
-
...props.function,
|
|
486
|
-
});
|
|
487
|
-
const fnUrl = fn.addFunctionUrl({
|
|
488
|
-
authType: regional?.enableServerUrlIamAuth
|
|
489
|
-
? FunctionUrlAuthType.AWS_IAM
|
|
490
|
-
: FunctionUrlAuthType.NONE,
|
|
491
|
-
});
|
|
492
|
-
if (regional?.enableServerUrlIamAuth) {
|
|
493
|
-
useFunctionUrlSigningFunction().attachPermissions([
|
|
494
|
-
new PolicyStatement({
|
|
495
|
-
actions: ["lambda:InvokeFunctionUrl"],
|
|
496
|
-
resources: [fn.functionArn],
|
|
497
|
-
}),
|
|
498
|
-
]);
|
|
499
|
-
}
|
|
500
|
-
origins[name] = new HttpOrigin(Fn.parseDomainName(fnUrl.url));
|
|
518
|
+
});
|
|
519
|
+
// Create group origins
|
|
520
|
+
Object.entries(plan.origins ?? {}).forEach(([name, props]) => {
|
|
521
|
+
if (props.type === "group") {
|
|
522
|
+
origins[name] = createOriginGroup(props, origins);
|
|
501
523
|
}
|
|
502
524
|
});
|
|
503
525
|
return origins;
|
|
@@ -83,13 +83,16 @@ export declare class SvelteKitSite extends SsrSite {
|
|
|
83
83
|
cacheType: "server" | "static";
|
|
84
84
|
pattern?: string | undefined;
|
|
85
85
|
origin: "s3" | "regionalServer";
|
|
86
|
+
allowedMethods?: import("aws-cdk-lib/aws-cloudfront").AllowedMethods | undefined;
|
|
86
87
|
cfFunction?: "serverCfFunction" | undefined;
|
|
87
88
|
edgeFunction?: "edgeServer" | undefined;
|
|
88
89
|
}[];
|
|
90
|
+
errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
|
|
89
91
|
cachePolicyAllowedHeaders?: string[] | undefined;
|
|
90
92
|
buildId?: string | undefined;
|
|
91
93
|
warmerConfig?: {
|
|
92
94
|
function: string;
|
|
95
|
+
schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
|
|
93
96
|
} | undefined;
|
|
94
97
|
};
|
|
95
98
|
getConstructMetadata(): {
|
package/node/graphql/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "sst",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.28.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sst": "cli/sst.js"
|
|
7
7
|
},
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"@types/ws": "^8.5.3",
|
|
120
120
|
"@types/yargs": "^17.0.13",
|
|
121
121
|
"archiver": "^5.3.1",
|
|
122
|
+
"astro-sst": "2.28.0",
|
|
122
123
|
"tsx": "^3.12.1",
|
|
123
124
|
"typescript": "^5.2.2",
|
|
124
125
|
"vitest": "^0.33.0"
|
|
@@ -34178,15 +34178,13 @@ async function handler(_event, context) {
|
|
|
34178
34178
|
new import_client_lambda.InvokeCommand({
|
|
34179
34179
|
FunctionName: FUNCTION_NAME,
|
|
34180
34180
|
InvocationType: "RequestResponse",
|
|
34181
|
-
Payload:
|
|
34182
|
-
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
})
|
|
34189
|
-
)
|
|
34181
|
+
Payload: JSON.stringify({
|
|
34182
|
+
type: "warmer",
|
|
34183
|
+
warmerId,
|
|
34184
|
+
index: i,
|
|
34185
|
+
concurrency: CONCURRENCY,
|
|
34186
|
+
delay: 75
|
|
34187
|
+
})
|
|
34190
34188
|
})
|
|
34191
34189
|
);
|
|
34192
34190
|
} catch (e) {
|
|
@@ -34200,10 +34198,15 @@ async function handler(_event, context) {
|
|
|
34200
34198
|
console.error(`failed to warm up #${i}:`, r?.Payload?.toString());
|
|
34201
34199
|
return;
|
|
34202
34200
|
}
|
|
34203
|
-
const
|
|
34204
|
-
|
|
34205
|
-
|
|
34206
|
-
|
|
34201
|
+
const payloadString = r.Payload.transformToString();
|
|
34202
|
+
if (payloadString) {
|
|
34203
|
+
const payload = JSON.parse(
|
|
34204
|
+
r.Payload.transformToString()
|
|
34205
|
+
);
|
|
34206
|
+
warmedServerIds.push(payload.serverId);
|
|
34207
|
+
} else {
|
|
34208
|
+
warmedServerIds.push("unknown");
|
|
34209
|
+
}
|
|
34207
34210
|
});
|
|
34208
34211
|
console.log({
|
|
34209
34212
|
event: "warmer result",
|