sst 2.3.7 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,10 @@
1
1
  import path from "path";
2
2
  import { VisibleError } from "../../error.js";
3
- const SITE_CONFIG = {
3
+ const SSR_SITE_CONFIG = {
4
4
  NextjsSite: "next.config",
5
5
  AstroSite: "astro.config",
6
6
  RemixSite: "remix.config",
7
7
  SolidStartSite: "vite.config",
8
- StaticSite: "vite.config",
9
8
  SlsNextjsSite: "next.config",
10
9
  };
11
10
  export const bind = (program) => program
@@ -18,6 +17,7 @@ export const bind = (program) => program
18
17
  const { useBus } = await import("../../bus.js");
19
18
  const { useIOT } = await import("../../iot.js");
20
19
  const { Colors } = await import("../colors.js");
20
+ const { Logger } = await import("../../logger.js");
21
21
  if (args._[0] === "env") {
22
22
  Colors.line(Colors.warning(`Warning: ${Colors.bold(`sst env`)} has been renamed to ${Colors.bold(`sst bind`)}`));
23
23
  }
@@ -25,17 +25,18 @@ export const bind = (program) => program
25
25
  const bus = useBus();
26
26
  const project = useProject();
27
27
  const command = args.command?.join(" ");
28
- const isFrontend = await isRunningInFrontend();
28
+ const isSsrSite = await isRunningInSsrSite();
29
29
  let p;
30
30
  let timer;
31
- let metadataCache;
31
+ let siteConfigCache;
32
32
  // Handle missing command
33
33
  if (!command) {
34
- throw new VisibleError(`Command is required, e.g. sst bind ${isFrontend ? "next dev" : "env"}`);
34
+ throw new VisibleError(`Command is required, e.g. sst bind ${isSsrSite ? "next dev" : "env"}`);
35
35
  }
36
36
  // Bind script
37
37
  const initialMetadata = await getSiteMetadata();
38
- if (!initialMetadata) {
38
+ if (!initialMetadata && !isSsrSite) {
39
+ Logger.debug("Running in script mode.");
39
40
  return await bindScript();
40
41
  }
41
42
  // Bind site
@@ -44,33 +45,44 @@ export const bind = (program) => program
44
45
  bus.subscribe("stacks.metadata.deleted", () => bindSite("metadata_updated"));
45
46
  bus.subscribe("config.secret.updated", (payload) => {
46
47
  const secretName = payload.properties.name;
47
- if (metadataCache?.secrets === undefined)
48
+ if (siteConfigCache?.secrets === undefined)
48
49
  return;
49
- if (!metadataCache.secrets.includes(secretName))
50
+ if (!siteConfigCache.secrets.includes(secretName))
50
51
  return;
51
52
  Colors.line(`\n`, `SST secrets have been updated. Restarting \`${command}\`...`);
52
53
  bindSite("secrets_updated");
53
54
  });
54
- async function isRunningInFrontend() {
55
+ async function isRunningInSsrSite() {
55
56
  const { existsAsync } = await import("../../util/fs.js");
56
- const results = await Promise.all(Object.values(SITE_CONFIG)
57
- .map((config) => [".js", ".cjs", ".mjs", ".ts"].map((ext) => existsAsync(`${config}${ext}`)))
57
+ const { readFile } = await import("fs/promises");
58
+ const results = await Promise.all(Object.values(SSR_SITE_CONFIG)
59
+ .map((config) => [".js", ".cjs", ".mjs", ".ts"].map(async (ext) => {
60
+ const exists = await existsAsync(`${config}${ext}`);
61
+ if (exists && config === "vite.config") {
62
+ const content = await readFile(`${config}${ext}`);
63
+ return content.includes("solid-start");
64
+ }
65
+ return exists;
66
+ }))
58
67
  .flat());
59
68
  return results.some(Boolean);
60
69
  }
61
70
  async function bindSite(reason) {
62
71
  // Get metadata
63
- const metadata = (reason === "init" ? initialMetadata : await getSiteMetadata());
72
+ const siteMetadata = (reason === "init"
73
+ ? initialMetadata
74
+ : await getSiteMetadataUntilAvailable());
75
+ const siteConfig = await parseSiteConfig(siteMetadata);
64
76
  // Handle rebind due to metadata updated
65
77
  if (reason === "metadata_updated") {
66
- if (areEnvsSame(metadata.envs, metadataCache?.envs || {}))
78
+ if (areEnvsSame(siteConfig.envs, siteConfigCache?.envs || {}))
67
79
  return;
68
80
  Colors.line(`\n`, `SST resources have been updated. Restarting \`${command}\`...`);
69
81
  }
70
- metadataCache = metadata;
82
+ siteConfigCache = siteConfig;
71
83
  // Assume function's role credentials
72
- if (metadata.role) {
73
- const credentials = await assumeSsrRole(metadata.role);
84
+ if (siteConfig.role) {
85
+ const credentials = await assumeSsrRole(siteConfig.role);
74
86
  if (credentials) {
75
87
  // refresh crecentials 1 minute before expiration
76
88
  const expireAt = credentials.Expiration.getTime() - 60000;
@@ -80,7 +92,7 @@ export const bind = (program) => program
80
92
  bindSite("iam_expired");
81
93
  }, expireAt - Date.now());
82
94
  runCommand({
83
- ...metadata.envs,
95
+ ...siteConfig.envs,
84
96
  AWS_ACCESS_KEY_ID: credentials.AccessKeyId,
85
97
  AWS_SECRET_ACCESS_KEY: credentials.SecretAccessKey,
86
98
  AWS_SESSION_TOKEN: credentials.SessionToken,
@@ -90,7 +102,7 @@ export const bind = (program) => program
90
102
  }
91
103
  // Fallback to use local IAM credentials
92
104
  runCommand({
93
- ...metadata.envs,
105
+ ...siteConfig.envs,
94
106
  ...(await localIamCredentials()),
95
107
  });
96
108
  }
@@ -101,24 +113,30 @@ export const bind = (program) => program
101
113
  ...(await localIamCredentials()),
102
114
  });
103
115
  }
104
- async function getSiteMetadata() {
105
- const { metadata } = await import("../../stacks/metadata.js");
106
- const { createSpinner } = await import("../spinner.js");
116
+ async function parseSiteConfig(metadata) {
107
117
  const { LambdaClient, GetFunctionCommand } = await import("@aws-sdk/client-lambda");
108
118
  const { useAWSClient } = await import("../../credentials.js");
119
+ const isBindSupported = metadata.type !== "StaticSite" && metadata.type !== "SlsNextjsSite";
120
+ // Handle StaticSite
121
+ if (!isBindSupported) {
122
+ return { envs: metadata.data.environment };
123
+ }
124
+ // Get function details
125
+ const lambda = useAWSClient(LambdaClient);
126
+ const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
127
+ FunctionName: metadata.data.server,
128
+ }));
129
+ return {
130
+ role: functionConfig?.Role,
131
+ envs: functionConfig?.Environment?.Variables || {},
132
+ secrets: metadata.data.secrets,
133
+ };
134
+ }
135
+ async function getSiteMetadataUntilAvailable() {
136
+ const { createSpinner } = await import("../spinner.js");
109
137
  const spinner = createSpinner({});
110
138
  while (true) {
111
- const metadataData = await metadata();
112
- const data = Object.values(metadataData)
113
- .flat()
114
- .filter((c) => Boolean(c))
115
- .filter((c) => Boolean(SITE_CONFIG[c.type]))
116
- .find((c) => path.resolve(project.paths.root, c.data.path) ===
117
- process.cwd());
118
- // Do not retry if not running in frontend
119
- if (!data && !isFrontend) {
120
- return;
121
- }
139
+ const data = await getSiteMetadata();
122
140
  // Handle site metadata not found
123
141
  if (!data) {
124
142
  spinner.start(
@@ -137,25 +155,20 @@ export const bind = (program) => program
137
155
  continue;
138
156
  }
139
157
  spinner.isSpinning && spinner.stop().clear();
140
- // Handle StaticSite
141
- if (!isBindSupported) {
142
- return { envs: data.data.environment };
143
- }
144
- // Get function details
145
- const lambda = useAWSClient(LambdaClient);
146
- const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
147
- FunctionName: data.data.server,
148
- }));
149
- return {
150
- role: functionConfig?.Role,
151
- envs: functionConfig?.Environment?.Variables || {},
152
- secrets: data.data.secrets,
153
- };
158
+ return data;
154
159
  }
155
160
  }
161
+ async function getSiteMetadata() {
162
+ const { metadata } = await import("../../stacks/metadata.js");
163
+ const metadataData = await metadata();
164
+ return Object.values(metadataData)
165
+ .flat()
166
+ .filter((c) => Boolean(c))
167
+ .filter((c) => c.type === "StaticSite" || Boolean(SSR_SITE_CONFIG[c.type]))
168
+ .find((c) => path.resolve(project.paths.root, c.data.path) === process.cwd());
169
+ }
156
170
  async function assumeSsrRole(roleArn) {
157
171
  const { STSClient, AssumeRoleCommand } = await import("@aws-sdk/client-sts");
158
- const { Logger } = await import("../../logger.js");
159
172
  const { useAWSClient } = await import("../../credentials.js");
160
173
  const sts = useAWSClient(STSClient);
161
174
  const assumeRole = async (duration) => {
@@ -184,7 +197,7 @@ export const bind = (program) => program
184
197
  err = e;
185
198
  }
186
199
  }
187
- Colors.line(Colors.warning(`Failed to assume SSR role ${roleArn}. Falling back to using local IAM credentials.`));
200
+ Colors.line("Using local IAM credentials since `sst dev` is not running.");
188
201
  Logger.debug(`Failed to assume ${roleArn}.`, err);
189
202
  }
190
203
  async function localIamCredentials() {
@@ -2,6 +2,7 @@ import fs from "fs";
2
2
  import url from "url";
3
3
  import path from "path";
4
4
  import crypto from "crypto";
5
+ import spawn from "cross-spawn";
5
6
  import { Construct } from "constructs";
6
7
  import { Effect, Role, Policy, PolicyStatement, CompositePrincipal, ServicePrincipal, ManagedPolicy, } from "aws-cdk-lib/aws-iam";
7
8
  import { Version, Code, Runtime, Function as CdkFunction, } from "aws-cdk-lib/aws-lambda";
@@ -135,9 +136,20 @@ export class EdgeFunction extends Construct {
135
136
  const filePath = path.join(bundle, handlerFilename);
136
137
  const fileData = fs.readFileSync(filePath, "utf8");
137
138
  fs.writeFileSync(filePath, `process.env = { ...process.env, ..."{{ _SST_FUNCTION_ENVIRONMENT_ }}" };\n${fileData}`);
139
+ // Note: cannot point the bundle to the `.open-next/server-function`
140
+ // b/c the folder contains node_modules. And pnpm node_modules
141
+ // contains symlinks. CDK cannot zip symlinks correctly.
142
+ // https://github.com/aws/aws-cdk/issues/9251
143
+ // We will zip the folder ourselves.
144
+ const outputPath = path.resolve(useProject().paths.artifacts, `SsrFunction-${this.node.id}-${this.node.addr}`);
145
+ const script = path.resolve(__dirname, "../support/ssr-site-function-archiver.mjs");
146
+ const result = spawn.sync("node", [script, path.join(bundle), path.join(outputPath, "server-function.zip")], { stdio: "inherit" });
147
+ if (result.status !== 0) {
148
+ throw new Error(`There was a problem generating the assets package.`);
149
+ }
138
150
  // Create asset
139
151
  const asset = new Asset(this.scope, `FunctionAsset`, {
140
- path: bundle,
152
+ path: path.join(outputPath, "server-function.zip"),
141
153
  });
142
154
  return { handlerFilename, asset };
143
155
  }
@@ -1,9 +1,10 @@
1
1
  import { Construct } from "constructs";
2
2
  import { Function as CdkFunction } from "aws-cdk-lib/aws-lambda";
3
- import { Distribution, CachePolicy } from "aws-cdk-lib/aws-cloudfront";
3
+ import { Distribution } from "aws-cdk-lib/aws-cloudfront";
4
+ import { EdgeFunction } from "./EdgeFunction.js";
4
5
  import { SsrSite, SsrSiteProps } from "./SsrSite.js";
5
6
  import { Size } from "./util/size.js";
6
- export interface NextjsSiteProps extends Omit<SsrSiteProps, "edge" | "nodejs"> {
7
+ export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
7
8
  imageOptimization?: {
8
9
  /**
9
10
  * The amount of memory in MB allocated for image optimization function.
@@ -43,9 +44,15 @@ export declare class NextjsSite extends SsrSite {
43
44
  clientBuildVersionedSubDir: string;
44
45
  };
45
46
  protected createFunctionForRegional(): CdkFunction;
46
- private createImageOptimizationFunctionForRegional;
47
- private createMiddlewareEdgeFunctionForRegional;
47
+ protected createFunctionForEdge(): EdgeFunction;
48
+ private createImageOptimizationFunction;
48
49
  protected createCloudFrontDistributionForRegional(): Distribution;
49
- protected createCloudFrontServerCachePolicy(): CachePolicy;
50
+ protected createCloudFrontDistributionForEdge(): Distribution;
51
+ private buildServerBehaviorForRegional;
52
+ private buildServerBehaviorForEdge;
53
+ private buildImageBehavior;
54
+ private buildStaticFileBehavior;
55
+ private buildDefaultNextjsBehaviorForRegional;
56
+ private buildDefaultNextjsBehaviorForEdge;
50
57
  protected generateBuildId(): string;
51
58
  }
@@ -4,7 +4,7 @@ import path from "path";
4
4
  import { Fn, Duration as CdkDuration, RemovalPolicy } from "aws-cdk-lib";
5
5
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
6
6
  import { Function as CdkFunction, Code, Runtime, Architecture, FunctionUrlAuthType, } from "aws-cdk-lib/aws-lambda";
7
- import { Distribution, ViewerProtocolPolicy, AllowedMethods, LambdaEdgeEventType, CachedMethods, CachePolicy, CacheQueryStringBehavior, CacheCookieBehavior, CacheHeaderBehavior, } from "aws-cdk-lib/aws-cloudfront";
7
+ import { Distribution, ViewerProtocolPolicy, AllowedMethods, LambdaEdgeEventType, CachedMethods, CachePolicy, } from "aws-cdk-lib/aws-cloudfront";
8
8
  import { S3Origin, HttpOrigin, OriginGroup, } from "aws-cdk-lib/aws-cloudfront-origins";
9
9
  import { SsrFunction } from "./SsrFunction.js";
10
10
  import { EdgeFunction } from "./EdgeFunction.js";
@@ -26,7 +26,7 @@ const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
26
26
  export class NextjsSite extends SsrSite {
27
27
  constructor(scope, id, props) {
28
28
  super(scope, id, {
29
- buildCommand: "npx --yes open-next@latest build",
29
+ buildCommand: "npx --yes open-next@^0.8.0 build",
30
30
  ...props,
31
31
  });
32
32
  }
@@ -54,7 +54,20 @@ export class NextjsSite extends SsrSite {
54
54
  });
55
55
  return ssrFn.function;
56
56
  }
57
- createImageOptimizationFunctionForRegional() {
57
+ createFunctionForEdge() {
58
+ const { runtime, timeout, memorySize, bind, permissions, environment } = this.props;
59
+ return new EdgeFunction(this, "ServerFunction", {
60
+ bundle: path.join(this.props.path, ".open-next", "server-function"),
61
+ handler: "index.handler",
62
+ runtime,
63
+ timeout,
64
+ memorySize,
65
+ bind,
66
+ permissions,
67
+ environment,
68
+ });
69
+ }
70
+ createImageOptimizationFunction() {
58
71
  const { imageOptimization, path: sitePath } = this.props;
59
72
  return new CdkFunction(this, `ImageFunction`, {
60
73
  description: "Image optimization handler for Next.js",
@@ -83,86 +96,7 @@ export class NextjsSite extends SsrSite {
83
96
  ],
84
97
  });
85
98
  }
86
- createMiddlewareEdgeFunctionForRegional() {
87
- const { permissions, environment, path: sitePath } = this.props;
88
- const middlewarePath = path.resolve(sitePath, ".open-next/middleware-function");
89
- if (fs.existsSync(middlewarePath)) {
90
- return new EdgeFunction(this, "Middleware", {
91
- bundle: middlewarePath,
92
- handler: "index.handler",
93
- runtime: "nodejs18.x",
94
- timeout: 5,
95
- memorySize: 128,
96
- permissions,
97
- environment,
98
- nodejs: {
99
- format: "esm",
100
- },
101
- });
102
- }
103
- }
104
99
  createCloudFrontDistributionForRegional() {
105
- const { cdk } = this.props;
106
- const cfDistributionProps = cdk?.distribution || {};
107
- const s3Origin = new S3Origin(this.cdk.bucket);
108
- // Create server behavior
109
- const middlewareFn = this.createMiddlewareEdgeFunctionForRegional();
110
- const serverFnUrl = this.serverLambdaForRegional.addFunctionUrl({
111
- authType: FunctionUrlAuthType.NONE,
112
- });
113
- const serverBehavior = {
114
- viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
115
- origin: new HttpOrigin(Fn.parseDomainName(serverFnUrl.url)),
116
- allowedMethods: AllowedMethods.ALLOW_ALL,
117
- cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
118
- compress: true,
119
- cachePolicy: cdk?.serverCachePolicy ?? this.createCloudFrontServerCachePolicy(),
120
- edgeLambdas: middlewareFn && [
121
- {
122
- eventType: LambdaEdgeEventType.VIEWER_REQUEST,
123
- functionVersion: middlewareFn.currentVersion,
124
- },
125
- ],
126
- };
127
- // Create image optimization behavior
128
- const imageFn = this.createImageOptimizationFunctionForRegional();
129
- const imageFnUrl = imageFn.addFunctionUrl({
130
- authType: FunctionUrlAuthType.NONE,
131
- });
132
- const imageBehavior = {
133
- viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
134
- origin: new HttpOrigin(Fn.parseDomainName(imageFnUrl.url)),
135
- allowedMethods: AllowedMethods.ALLOW_ALL,
136
- cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
137
- compress: true,
138
- cachePolicy: serverBehavior.cachePolicy,
139
- };
140
- // Create statics behavior
141
- const staticFileBehaviour = {
142
- origin: s3Origin,
143
- viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
144
- allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
145
- cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
146
- compress: true,
147
- cachePolicy: CachePolicy.CACHING_OPTIMIZED,
148
- };
149
- // Create default behavior
150
- // default handler for requests that don't match any other path:
151
- // - try lambda handler first
152
- // - if failed, fall back to S3
153
- const fallbackOriginGroup = new OriginGroup({
154
- primaryOrigin: serverBehavior.origin,
155
- fallbackOrigin: s3Origin,
156
- fallbackStatusCodes: [404],
157
- });
158
- const defaultBehavior = {
159
- origin: fallbackOriginGroup,
160
- viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
161
- compress: true,
162
- cachePolicy: serverBehavior.cachePolicy,
163
- edgeLambdas: serverBehavior.edgeLambdas,
164
- ...(cfDistributionProps.defaultBehavior || {}),
165
- };
166
100
  /**
167
101
  * Next.js requests
168
102
  *
@@ -212,6 +146,42 @@ export class NextjsSite extends SsrSite {
212
146
  * - Cache-Control: public, max-age=0, must-revalidate
213
147
  * - x-vercel-cache: MISS
214
148
  */
149
+ const { cdk } = this.props;
150
+ const cfDistributionProps = cdk?.distribution || {};
151
+ const s3Origin = new S3Origin(this.cdk.bucket);
152
+ const serverFnUrl = this.serverLambdaForRegional.addFunctionUrl({
153
+ authType: FunctionUrlAuthType.NONE,
154
+ });
155
+ const serverOrigin = new HttpOrigin(Fn.parseDomainName(serverFnUrl.url));
156
+ const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
157
+ const originRequestPolicy = this.buildServerOriginRequestPolicy();
158
+ const serverBehavior = this.buildServerBehaviorForRegional(serverOrigin, cachePolicy, originRequestPolicy);
159
+ return new Distribution(this, "Distribution", {
160
+ // these values can be overwritten by cfDistributionProps
161
+ defaultRootObject: "",
162
+ // Override props.
163
+ ...cfDistributionProps,
164
+ // these values can NOT be overwritten by cfDistributionProps
165
+ domainNames: this.buildDistributionDomainNames(),
166
+ certificate: this.cdk.certificate,
167
+ defaultBehavior: this.buildDefaultNextjsBehaviorForRegional(serverOrigin, s3Origin, cachePolicy, originRequestPolicy),
168
+ additionalBehaviors: {
169
+ "api/*": serverBehavior,
170
+ "_next/data/*": serverBehavior,
171
+ "_next/image*": this.buildImageBehavior(cachePolicy),
172
+ "_next/*": this.buildStaticFileBehavior(s3Origin),
173
+ ...(cfDistributionProps.additionalBehaviors || {}),
174
+ },
175
+ });
176
+ }
177
+ createCloudFrontDistributionForEdge() {
178
+ const { cdk } = this.props;
179
+ const cfDistributionProps = cdk?.distribution || {};
180
+ const s3Origin = new S3Origin(this.cdk.bucket);
181
+ const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
182
+ const originRequestPolicy = this.buildServerOriginRequestPolicy();
183
+ const functionVersion = this.serverLambdaForEdge.currentVersion;
184
+ const serverBehavior = this.buildServerBehaviorForEdge(functionVersion, s3Origin, cachePolicy, originRequestPolicy);
215
185
  return new Distribution(this, "Distribution", {
216
186
  // these values can be overwritten by cfDistributionProps
217
187
  defaultRootObject: "",
@@ -220,34 +190,111 @@ export class NextjsSite extends SsrSite {
220
190
  // these values can NOT be overwritten by cfDistributionProps
221
191
  domainNames: this.buildDistributionDomainNames(),
222
192
  certificate: this.cdk.certificate,
223
- defaultBehavior: defaultBehavior,
193
+ defaultBehavior: this.buildDefaultNextjsBehaviorForEdge(functionVersion, s3Origin, cachePolicy, originRequestPolicy),
224
194
  additionalBehaviors: {
225
195
  "api/*": serverBehavior,
226
196
  "_next/data/*": serverBehavior,
227
- "_next/image*": imageBehavior,
228
- "_next/*": staticFileBehaviour,
197
+ "_next/image*": this.buildImageBehavior(cachePolicy),
198
+ "_next/*": this.buildStaticFileBehavior(s3Origin),
229
199
  ...(cfDistributionProps.additionalBehaviors || {}),
230
200
  },
231
201
  });
232
202
  }
233
- createCloudFrontServerCachePolicy() {
234
- return new CachePolicy(this, "ServerCache", {
235
- queryStringBehavior: CacheQueryStringBehavior.all(),
236
- headerBehavior: CacheHeaderBehavior.allowList(
237
- // required by image optimization request
238
- "accept",
239
- // required by server request
240
- "x-op-middleware-request-headers", "x-op-middleware-response-headers", "x-nextjs-data", "x-middleware-prefetch",
241
- // required by server request (in-place routing)
242
- "rsc", "next-router-prefetch", "next-router-state-tree"),
243
- cookieBehavior: CacheCookieBehavior.all(),
244
- defaultTtl: CdkDuration.days(0),
245
- maxTtl: CdkDuration.days(365),
246
- minTtl: CdkDuration.days(0),
247
- enableAcceptEncodingBrotli: true,
248
- enableAcceptEncodingGzip: true,
249
- comment: "SST server response cache policy",
203
+ buildServerBehaviorForRegional(serverOrigin, cachePolicy, originRequestPolicy) {
204
+ return {
205
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
206
+ origin: serverOrigin,
207
+ allowedMethods: AllowedMethods.ALLOW_ALL,
208
+ cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
209
+ compress: true,
210
+ cachePolicy,
211
+ originRequestPolicy,
212
+ };
213
+ }
214
+ buildServerBehaviorForEdge(functionVersion, s3Origin, cachePolicy, originRequestPolicy) {
215
+ return {
216
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
217
+ origin: s3Origin,
218
+ allowedMethods: AllowedMethods.ALLOW_ALL,
219
+ cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
220
+ compress: true,
221
+ cachePolicy,
222
+ originRequestPolicy,
223
+ edgeLambdas: [
224
+ {
225
+ includeBody: true,
226
+ eventType: LambdaEdgeEventType.ORIGIN_REQUEST,
227
+ functionVersion,
228
+ },
229
+ ],
230
+ };
231
+ }
232
+ buildImageBehavior(cachePolicy) {
233
+ const imageFn = this.createImageOptimizationFunction();
234
+ const imageFnUrl = imageFn.addFunctionUrl({
235
+ authType: FunctionUrlAuthType.NONE,
250
236
  });
237
+ return {
238
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
239
+ origin: new HttpOrigin(Fn.parseDomainName(imageFnUrl.url)),
240
+ allowedMethods: AllowedMethods.ALLOW_ALL,
241
+ cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
242
+ compress: true,
243
+ cachePolicy,
244
+ };
245
+ }
246
+ buildStaticFileBehavior(s3Origin) {
247
+ return {
248
+ origin: s3Origin,
249
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
250
+ allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
251
+ cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
252
+ compress: true,
253
+ cachePolicy: CachePolicy.CACHING_OPTIMIZED,
254
+ };
255
+ }
256
+ buildDefaultNextjsBehaviorForRegional(serverOrigin, s3Origin, cachePolicy, originRequestPolicy) {
257
+ // Create default behavior
258
+ // default handler for requests that don't match any other path:
259
+ // - try lambda handler first
260
+ // - if failed, fall back to S3
261
+ const { cdk } = this.props;
262
+ const cfDistributionProps = cdk?.distribution || {};
263
+ const fallbackOriginGroup = new OriginGroup({
264
+ primaryOrigin: serverOrigin,
265
+ fallbackOrigin: s3Origin,
266
+ fallbackStatusCodes: [404],
267
+ });
268
+ return {
269
+ origin: fallbackOriginGroup,
270
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
271
+ compress: true,
272
+ cachePolicy,
273
+ originRequestPolicy,
274
+ ...(cfDistributionProps.defaultBehavior || {}),
275
+ };
276
+ }
277
+ buildDefaultNextjsBehaviorForEdge(functionVersion, s3Origin, cachePolicy, originRequestPolicy) {
278
+ const { cdk } = this.props;
279
+ const cfDistributionProps = cdk?.distribution || {};
280
+ return {
281
+ viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
282
+ origin: s3Origin,
283
+ allowedMethods: AllowedMethods.ALLOW_ALL,
284
+ cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
285
+ compress: true,
286
+ cachePolicy,
287
+ originRequestPolicy,
288
+ ...(cfDistributionProps.defaultBehavior || {}),
289
+ edgeLambdas: [
290
+ {
291
+ includeBody: true,
292
+ eventType: LambdaEdgeEventType.ORIGIN_REQUEST,
293
+ functionVersion,
294
+ },
295
+ ...(cfDistributionProps.defaultBehavior?.edgeLambdas || []),
296
+ ],
297
+ };
251
298
  }
252
299
  generateBuildId() {
253
300
  const filePath = path.join(this.props.path, ".next/BUILD_ID");
@@ -4,7 +4,6 @@ import { Function as CdkFunction, FunctionProps } from "aws-cdk-lib/aws-lambda";
4
4
  import { IHostedZone } from "aws-cdk-lib/aws-route53";
5
5
  import { Distribution, ICachePolicy, BehaviorOptions, CachePolicy } from "aws-cdk-lib/aws-cloudfront";
6
6
  import { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
7
- import { S3Origin } from "aws-cdk-lib/aws-cloudfront-origins";
8
7
  import { SSTConstruct } from "./Construct.js";
9
8
  import { NodeJSProps, Function } from "./Function.js";
10
9
  import { EdgeFunction } from "./EdgeFunction.js";
@@ -198,7 +197,7 @@ export declare class SsrSite extends Construct implements SSTConstruct {
198
197
  protected props: SsrSiteNormalizedProps;
199
198
  private doNotDeploy;
200
199
  protected buildConfig: SsrBuildConfig;
201
- private serverLambdaForEdge?;
200
+ protected serverLambdaForEdge?: EdgeFunction;
202
201
  protected serverLambdaForRegional?: CdkFunction;
203
202
  private serverLambdaForDev?;
204
203
  private bucket;
@@ -264,14 +263,14 @@ export declare class SsrSite extends Construct implements SSTConstruct {
264
263
  private createFunctionPermissionsForEdge;
265
264
  private validateCloudFrontDistributionSettings;
266
265
  protected createCloudFrontDistributionForRegional(): Distribution;
267
- private createCloudFrontDistributionForEdge;
266
+ protected createCloudFrontDistributionForEdge(): Distribution;
268
267
  protected buildDistributionDomainNames(): string[];
269
- private buildDistributionDefaultBehaviorForRegional;
270
- private buildDistributionDefaultBehaviorForEdge;
268
+ protected buildDefaultBehaviorForRegional(): BehaviorOptions;
269
+ private buildDefaultBehaviorForEdge;
271
270
  private buildBehaviorFunctionAssociations;
272
- protected buildDistributionStaticFileBehaviors(origin: S3Origin): Record<string, BehaviorOptions>;
273
- protected createCloudFrontServerCachePolicy(): CachePolicy;
274
- protected createCloudFrontServerOriginRequestPolicy(): import("aws-cdk-lib/aws-cloudfront").IOriginRequestPolicy;
271
+ private buildStaticFileBehaviors;
272
+ protected buildServerCachePolicy(): CachePolicy;
273
+ protected buildServerOriginRequestPolicy(): import("aws-cdk-lib/aws-cloudfront").IOriginRequestPolicy;
275
274
  private createCloudFrontInvalidation;
276
275
  protected validateCustomDomainSettings(): void;
277
276
  protected lookupHostedZone(): IHostedZone | undefined;