sst 2.26.10 → 2.27.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 { BUILD_META_FILE_NAME } from "astro-sst/build-meta";
4
+ import { SsrSite, } from "./SsrSite.js";
5
+ import { AllowedMethods } from "aws-cdk-lib/aws-cloudfront";
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"> {
@@ -47,6 +47,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
47
47
  serverCachePolicy?: NonNullable<SsrSiteProps["cdk"]>["serverCachePolicy"];
48
48
  };
49
49
  }
50
+ type NextjsSiteNormalizedProps = NextjsSiteProps & SsrSiteNormalizedProps;
50
51
  /**
51
52
  * The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
52
53
  * @example
@@ -59,13 +60,7 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
59
60
  * ```
60
61
  */
61
62
  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
- };
63
+ props: NextjsSiteNormalizedProps;
69
64
  constructor(scope: Construct, id: string, props?: NextjsSiteProps);
70
65
  protected plan(bucket: Bucket): {
71
66
  cloudFrontFunctions?: {
@@ -140,13 +135,16 @@ export declare class NextjsSite extends SsrSite {
140
135
  cacheType: "server" | "static";
141
136
  pattern?: string | undefined;
142
137
  origin: "s3" | "regionalServer" | "imageOptimizer";
138
+ allowedMethods?: import("aws-cdk-lib/aws-cloudfront").AllowedMethods | undefined;
143
139
  cfFunction?: "serverCfFunction" | undefined;
144
140
  edgeFunction?: "edgeServer" | undefined;
145
141
  }[];
142
+ errorResponses?: import("aws-cdk-lib/aws-cloudfront").ErrorResponse[] | undefined;
146
143
  cachePolicyAllowedHeaders?: string[] | undefined;
147
144
  buildId?: string | undefined;
148
145
  warmerConfig?: {
149
146
  function: string;
147
+ schedule?: import("aws-cdk-lib/aws-events").Schedule | undefined;
150
148
  } | undefined;
151
149
  };
152
150
  protected createRevalidation(): void;
@@ -164,3 +162,4 @@ export declare class NextjsSite extends SsrSite {
164
162
  type: "NextjsSite";
165
163
  };
166
164
  }
165
+ export {};
@@ -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";
@@ -4,7 +4,7 @@ import { SSTConstruct } from "./Construct.js";
4
4
  import { Permissions } from "./util/permission.js";
5
5
  import { FunctionBindingProps } from "./util/functionBinding.js";
6
6
  import { IVpc } from "aws-cdk-lib/aws-ec2";
7
- import { Cluster, ContainerDefinitionOptions, CpuArchitecture, FargateServiceProps } from "aws-cdk-lib/aws-ecs";
7
+ import { Cluster, ContainerDefinitionOptions, CpuArchitecture, FargateService, FargateTaskDefinition, FargateServiceProps } from "aws-cdk-lib/aws-ecs";
8
8
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
9
9
  import { ApplicationTargetGroupProps } from "aws-cdk-lib/aws-elasticloadbalancingv2";
10
10
  declare const supportedCpus: {
@@ -394,6 +394,7 @@ export declare class Service extends Construct implements SSTConstruct {
394
394
  private cluster;
395
395
  private container;
396
396
  private taskDefinition;
397
+ private service;
397
398
  private distribution?;
398
399
  constructor(scope: Construct, id: string, props?: ServiceProps);
399
400
  /**
@@ -411,6 +412,8 @@ export declare class Service extends Construct implements SSTConstruct {
411
412
  get cdk(): {
412
413
  vpc: IVpc;
413
414
  cluster: Cluster;
415
+ fargateService: FargateService;
416
+ taskDefinition: FargateTaskDefinition;
414
417
  distribution: import("aws-cdk-lib/aws-cloudfront").IDistribution | undefined;
415
418
  hostedZone: import("aws-cdk-lib/aws-route53").IHostedZone | undefined;
416
419
  certificate: import("aws-cdk-lib/aws-certificatemanager").ICertificate | undefined;
@@ -145,6 +145,7 @@ export class Service extends Construct {
145
145
  cluster;
146
146
  container;
147
147
  taskDefinition;
148
+ service;
148
149
  distribution;
149
150
  constructor(scope, id, props) {
150
151
  super(scope, id);
@@ -168,7 +169,9 @@ export class Service extends Construct {
168
169
  useServices().add(stack.stackName, id, this.props);
169
170
  if (this.doNotDeploy) {
170
171
  // @ts-expect-error
171
- this.vpc = this.cluster = this.container = this.taskDefinition = null;
172
+ this.vpc = this.cluster = null;
173
+ // @ts-expect-error
174
+ this.service = this.container = this.taskDefinition = null;
172
175
  // @ts-expect-error
173
176
  this.distribution = null;
174
177
  this.devFunction = this.createDevFunction();
@@ -184,6 +187,7 @@ export class Service extends Construct {
184
187
  this.distribution = this.createDistribution(alb);
185
188
  this.vpc = vpc;
186
189
  this.cluster = cluster;
190
+ this.service = service;
187
191
  this.container = container;
188
192
  this.taskDefinition = taskDefinition;
189
193
  this.bindForService(props?.bind || []);
@@ -244,6 +248,8 @@ export class Service extends Construct {
244
248
  return {
245
249
  vpc: this.vpc,
246
250
  cluster: this.cluster,
251
+ fargateService: this.service,
252
+ taskDefinition: this.taskDefinition,
247
253
  distribution: this.distribution?.cdk.distribution,
248
254
  hostedZone: this.distribution?.cdk.hostedZone,
249
255
  certificate: this.distribution?.cdk.certificate,
@@ -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.10",
4
+ "version": "2.27.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.27.0",
122
123
  "tsx": "^3.12.1",
123
124
  "typescript": "^5.2.2",
124
125
  "vitest": "^0.33.0"
package/project.d.ts CHANGED
@@ -57,6 +57,7 @@ interface Project {
57
57
  metafile: Metafile;
58
58
  stacks: SSTConfig["stacks"];
59
59
  }
60
+ export declare function setProject(p: Project): void;
60
61
  export declare function useProject(): Project;
61
62
  interface GlobalOptions {
62
63
  profile?: string;
package/project.js CHANGED
@@ -13,6 +13,9 @@ const DEFAULTS = {
13
13
  ssmPrefix: undefined,
14
14
  };
15
15
  let project;
16
+ export function setProject(p) {
17
+ project = p;
18
+ }
16
19
  export function useProject() {
17
20
  if (!project)
18
21
  throw new Error("Project not initialized");
@@ -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",