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.
@@ -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
- protected typesPath: string;
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
- serverCfFunction: {
18
- constructId: string;
19
- injections: string[];
20
- };
21
- } | undefined;
22
- edgeFunctions?: {
23
- edgeServer: {
24
- constructId: string;
25
- function: {
26
- description: string;
27
- handler: string;
28
- scopeOverride: AstroSite;
29
- };
30
- };
31
- } | undefined;
32
- origins: {
33
- s3: {
34
- type: "s3";
35
- copy: {
36
- from: string;
37
- to: string;
38
- cached: true;
39
- versionedSubDir: string;
40
- }[];
41
- };
42
- regionalServer?: {
43
- type: "function";
44
- constructId: string;
45
- function: {
46
- description: string;
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: "s3" | "regionalServer";
56
- cfFunction?: "serverCfFunction" | undefined;
57
- edgeFunction?: "edgeServer" | undefined;
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 {};
@@ -1,6 +1,8 @@
1
- import fs from "fs";
2
- import path from "path";
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
- typesPath = "src";
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: path.join(sitePath, "dist", "server", "entry.handler"),
93
+ handler: join(sitePath, "dist", "server", "entry.handler"),
22
94
  };
23
- return this.validatePlan({
95
+ const plan = {
24
96
  cloudFrontFunctions: {
25
97
  serverCfFunction: {
26
98
  constructId: "CloudFrontFunction",
27
- injections: [this.useCloudFrontFunctionHostHeaderInjection()],
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
- ...(edge
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: "dist/client",
110
+ from: buildMeta.clientBuildOutputDir,
57
111
  to: "",
58
112
  cached: true,
59
- versionedSubDir: "assets",
113
+ versionedSubDir: buildMeta.clientBuildVersionedSubDir,
60
114
  },
61
115
  ],
62
116
  },
63
117
  },
64
- behaviors: [
65
- edge
66
- ? {
67
- cacheType: "server",
68
- cfFunction: "serverCfFunction",
69
- edgeFunction: "edgeServer",
70
- origin: "s3",
71
- }
72
- : {
73
- cacheType: "server",
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
- // create 1 behaviour for each top level asset file/folder
78
- ...fs.readdirSync(path.join(sitePath, "dist/client")).map((item) => ({
79
- cacheType: "static",
80
- pattern: fs
81
- .statSync(path.join(sitePath, "dist/client", item))
82
- .isDirectory()
83
- ? `${item}/*`
84
- : item,
85
- origin: "s3",
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
- * memorySize: "512 MB",
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
- protected props: NextjsSiteProps & {
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
- protected createRevalidation(): void;
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 {};
@@ -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: "npx --yes open-next@2.1.5 build",
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
- this.createRevalidation();
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
- createRevalidation() {
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
- server?.addEnvironment("REVALIDATION_QUEUE_URL", queue.queueUrl);
195
- server?.addEnvironment("REVALIDATION_QUEUE_REGION", Stack.of(this).region);
196
- queue.grantSendMessages(server?.role);
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";
@@ -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. Set this to `false` to skip creating the 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
@@ -517,6 +517,9 @@ export class Service extends Construct {
517
517
  cachePolicy,
518
518
  originRequestPolicy: OriginRequestPolicy.ALL_VIEWER,
519
519
  },
520
+ ...(cdk?.cloudfrontDistribution === true
521
+ ? {}
522
+ : cdk?.cloudfrontDistribution),
520
523
  },
521
524
  },
522
525
  });
@@ -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(): {
@@ -32,9 +32,9 @@ export class SsrFunction extends Construct {
32
32
  super(scope, id);
33
33
  this.id = id;
34
34
  this.props = {
35
- ...props,
36
35
  timeout: 10,
37
36
  memorySize: 1024,
37
+ ...props,
38
38
  environment: props.environment || {},
39
39
  permissions: props.permissions || [],
40
40
  };
@@ -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, IDistribution } from "aws-cdk-lib/aws-cloudfront";
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
  }
@@ -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(this.typesPath);
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: "Next.js warmer",
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: 1024,
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
- if (!props)
412
- return;
413
- // S3 Origin
414
- if (props.type === "s3") {
415
- origins[name] = new S3Origin(bucket, {
416
- originPath: "/" + (props.originPath ?? ""),
417
- });
418
- const assets = createS3OriginAssets(props.copy);
419
- const assetFileOptions = fileOptions || createS3OriginAssetFileOptions(props.copy);
420
- const s3deployCR = createS3OriginDeployment(assets, assetFileOptions);
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
- // Server Origin
424
- else if (props.type === "function") {
425
- const fn = new SsrFunction(self, props.constructId, {
426
- runtime,
427
- timeout,
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(): {
@@ -9,7 +9,7 @@ export declare function GraphQLHandler<UserContext extends {}>(options: YogaServ
9
9
  headers: {
10
10
  [k: string]: string;
11
11
  };
12
- body: string;
12
+ body: any;
13
13
  isBase64Encoded: boolean;
14
14
  }>;
15
15
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.26.11",
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: Buffer.from(
34182
- JSON.stringify({
34183
- type: "warmer",
34184
- warmerId,
34185
- index: i,
34186
- concurrency: CONCURRENCY,
34187
- delay: 75
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 payload = JSON.parse(
34204
- Buffer.from(r.Payload).toString()
34205
- );
34206
- warmedServerIds.push(payload.serverId);
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",