sst 2.24.22 → 2.24.24

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.
@@ -566,6 +566,19 @@ export interface ContainerProps {
566
566
  * ```
567
567
  */
568
568
  file?: string;
569
+ /**
570
+ * Build args to pass to the docker build command.
571
+ * @default No build args
572
+ * @example
573
+ * ```js
574
+ * container: {
575
+ * buildArgs: {
576
+ * FOO: "bar"
577
+ * }
578
+ * }
579
+ * ```
580
+ */
581
+ buildArgs?: Record<string, string>;
569
582
  }
570
583
  /**
571
584
  * Used to configure additional files to copy into the function bundle
@@ -206,6 +206,9 @@ export class Function extends CDKFunction {
206
206
  ...(props.container?.file
207
207
  ? { file: props.container.file }
208
208
  : {}),
209
+ ...(props.container?.buildArgs
210
+ ? { buildArgs: props.container.buildArgs }
211
+ : {}),
209
212
  exclude: [".sst"],
210
213
  ignoreMode: IgnoreMode.GLOB,
211
214
  }),
@@ -31,6 +31,19 @@ export interface JobContainerProps {
31
31
  * ```
32
32
  */
33
33
  file?: string;
34
+ /**
35
+ * Build args to pass to the docker build command.
36
+ * @default No build args
37
+ * @example
38
+ * ```js
39
+ * container: {
40
+ * buildArgs: {
41
+ * FOO: "bar"
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ buildArgs?: Record<string, string>;
34
47
  }
35
48
  export interface JobProps {
36
49
  /**
package/constructs/Job.js CHANGED
@@ -213,6 +213,7 @@ export class Job extends Construct {
213
213
  ? Platform.custom("linux/arm64")
214
214
  : Platform.custom("linux/amd64"),
215
215
  file: container?.file,
216
+ buildArgs: container?.buildArgs,
216
217
  exclude: [".sst"],
217
218
  ignoreMode: IgnoreMode.GLOB,
218
219
  });
@@ -63,7 +63,7 @@ export declare class NextjsSite extends SsrSite {
63
63
  private createWarmer;
64
64
  protected createCloudFrontDistributionForRegional(): Distribution;
65
65
  protected createCloudFrontDistributionForEdge(): Distribution;
66
- protected buildServerCachePolicy(): CachePolicy;
66
+ protected useServerBehaviorCachePolicy(): CachePolicy;
67
67
  private buildImageBehavior;
68
68
  protected generateBuildId(): string;
69
69
  getConstructMetadata(): {
@@ -255,8 +255,7 @@ export class NextjsSite extends SsrSite {
255
255
  */
256
256
  const { customDomain, cdk } = this.props;
257
257
  const cfDistributionProps = cdk?.distribution || {};
258
- const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
259
- const serverBehavior = this.buildDefaultBehaviorForRegional(cachePolicy);
258
+ const serverBehavior = this.buildDefaultBehaviorForRegional();
260
259
  return new Distribution(this, "CDN", {
261
260
  scopeOverride: this,
262
261
  customDomain,
@@ -271,7 +270,7 @@ export class NextjsSite extends SsrSite {
271
270
  additionalBehaviors: {
272
271
  "api/*": serverBehavior,
273
272
  "_next/data/*": serverBehavior,
274
- "_next/image*": this.buildImageBehavior(cachePolicy),
273
+ "_next/image*": this.buildImageBehavior(),
275
274
  ...(cfDistributionProps.additionalBehaviors || {}),
276
275
  },
277
276
  },
@@ -281,8 +280,7 @@ export class NextjsSite extends SsrSite {
281
280
  createCloudFrontDistributionForEdge() {
282
281
  const { customDomain, cdk } = this.props;
283
282
  const cfDistributionProps = cdk?.distribution || {};
284
- const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
285
- const serverBehavior = this.buildDefaultBehaviorForEdge(cachePolicy);
283
+ const serverBehavior = this.buildDefaultBehaviorForEdge();
286
284
  return new Distribution(this, "CDN", {
287
285
  scopeOverride: this,
288
286
  customDomain,
@@ -297,15 +295,15 @@ export class NextjsSite extends SsrSite {
297
295
  additionalBehaviors: {
298
296
  "api/*": serverBehavior,
299
297
  "_next/data/*": serverBehavior,
300
- "_next/image*": this.buildImageBehavior(cachePolicy),
298
+ "_next/image*": this.buildImageBehavior(),
301
299
  ...(cfDistributionProps.additionalBehaviors || {}),
302
300
  },
303
301
  },
304
302
  },
305
303
  });
306
304
  }
307
- buildServerCachePolicy() {
308
- return super.buildServerCachePolicy([
305
+ useServerBehaviorCachePolicy() {
306
+ return super.useServerBehaviorCachePolicy([
309
307
  "accept",
310
308
  "rsc",
311
309
  "next-router-prefetch",
@@ -313,7 +311,7 @@ export class NextjsSite extends SsrSite {
313
311
  "next-url",
314
312
  ]);
315
313
  }
316
- buildImageBehavior(cachePolicy) {
314
+ buildImageBehavior() {
317
315
  const { cdk, regional } = this.props;
318
316
  const imageFn = this.createImageOptimizationFunction();
319
317
  const imageFnUrl = imageFn.addFunctionUrl({
@@ -327,7 +325,7 @@ export class NextjsSite extends SsrSite {
327
325
  allowedMethods: AllowedMethods.ALLOW_ALL,
328
326
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
329
327
  compress: true,
330
- cachePolicy,
328
+ cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
331
329
  responseHeadersPolicy: cdk?.responseHeadersPolicy,
332
330
  edgeLambdas: regional?.enableServerUrlIamAuth
333
331
  ? [
@@ -20,6 +20,7 @@ export declare class RemixSite extends SsrSite {
20
20
  serverBuildOutputFile: string;
21
21
  clientBuildOutputDir: string;
22
22
  clientBuildVersionedSubDir: string;
23
+ clientCFFunctionInjection: string;
23
24
  };
24
25
  private createServerLambdaBundle;
25
26
  protected createFunctionForRegional(): SsrFunction;
@@ -53,6 +53,11 @@ export class RemixSite extends SsrSite {
53
53
  serverBuildOutputFile: "build/index.js",
54
54
  clientBuildOutputDir: "public",
55
55
  clientBuildVersionedSubDir: "build",
56
+ // Note: When using libraries like remix-flat-routes the file can
57
+ // contains special characters like "+". It needs to be encoded.
58
+ clientCFFunctionInjection: `
59
+ request.uri = request.uri.split('/').map(encodeURIComponent).join('/');
60
+ `,
56
61
  };
57
62
  }
58
63
  createServerLambdaBundle(wrapperFile) {
@@ -4,8 +4,9 @@ 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 } from "aws-cdk-lib/aws-ecs";
7
+ import { Cluster, ContainerDefinitionOptions, CpuArchitecture, FargateServiceProps } from "aws-cdk-lib/aws-ecs";
8
8
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
9
+ import { ApplicationTargetGroupProps } from "aws-cdk-lib/aws-elasticloadbalancingv2";
9
10
  declare const supportedCpus: {
10
11
  "0.25 vCPU": number;
11
12
  "0.5 vCPU": number;
@@ -29,7 +30,18 @@ export interface ServiceProps {
29
30
  */
30
31
  file?: string;
31
32
  /**
32
- * The amount of cpu allocated.
33
+ * The CPU architecture of the container.
34
+ * @default "x86_64"
35
+ * @example
36
+ * ```js
37
+ * {
38
+ * architecture: "arm64",
39
+ * }
40
+ * ```
41
+ */
42
+ architecture?: Lowercase<keyof Pick<typeof CpuArchitecture, "ARM64" | "X86_64">>;
43
+ /**
44
+ * The amount of CPU allocated.
33
45
  * @default "0.25 vCPU"
34
46
  * @example
35
47
  * ```js
@@ -214,6 +226,23 @@ export interface ServiceProps {
214
226
  * ```
215
227
  */
216
228
  waitForInvalidation?: boolean;
229
+ build?: {
230
+ /**
231
+ * Build args to pass to the docker build command.
232
+ * @default No build args
233
+ * @example
234
+ * ```js
235
+ * {
236
+ * build: {
237
+ * buildArgs: {
238
+ * FOO: "bar"
239
+ * }
240
+ * }
241
+ * }
242
+ * ```
243
+ */
244
+ buildArgs?: Record<string, string>;
245
+ };
217
246
  dev?: {
218
247
  /**
219
248
  * When running `sst dev, site is not deployed. This is to ensure `sst dev` can start up quickly.
@@ -248,7 +277,9 @@ export interface ServiceProps {
248
277
  * @example
249
278
  * ```js
250
279
  * {
251
- * cloudfrontDistribution: false
280
+ * cdk: {
281
+ * cloudfrontDistribution: false
282
+ * }
252
283
  * }
253
284
  * ```
254
285
  */
@@ -259,11 +290,44 @@ export interface ServiceProps {
259
290
  * @example
260
291
  * ```js
261
292
  * {
262
- * applicationLoadBalancer: false
293
+ * cdk: {
294
+ * applicationLoadBalancer: false
295
+ * }
263
296
  * }
264
297
  * ```
265
298
  */
266
299
  applicationLoadBalancer?: boolean;
300
+ /**
301
+ * Customize the Application Load Balancer's target group.
302
+ * @default true
303
+ * @example
304
+ * ```js
305
+ * {
306
+ * cdk: {
307
+ * applicationLoadBalancerTargetGroup: {
308
+ * healthCheck: {
309
+ * path: "/health"
310
+ * }
311
+ * }
312
+ * }
313
+ * }
314
+ * ```
315
+ */
316
+ applicationLoadBalancerTargetGroup?: ApplicationTargetGroupProps;
317
+ /**
318
+ * Customize the Fargate Service.
319
+ * @example
320
+ * ```js
321
+ * {
322
+ * cdk: {
323
+ * fargateService: {
324
+ * circuitBreaker: { rollback: true }
325
+ * }
326
+ * }
327
+ * }
328
+ * ```
329
+ */
330
+ fargateService?: Omit<FargateServiceProps, "cluster" | "taskDefinition">;
267
331
  /**
268
332
  * Customizing the container definition for the ECS task.
269
333
  * @example
@@ -272,14 +336,16 @@ export interface ServiceProps {
272
336
  * cdk: {
273
337
  * container: {
274
338
  * healthCheck: {
275
- * command: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"],
276
- * },
339
+ * command: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
340
+ * }
277
341
  * }
278
342
  * }
279
343
  * }
280
344
  * ```
281
345
  */
282
- container?: Omit<ContainerDefinitionOptions, "image">;
346
+ container?: Omit<ContainerDefinitionOptions, "image"> & {
347
+ image?: ContainerDefinitionOptions["image"];
348
+ };
283
349
  /**
284
350
  * Runs codebuild job in the specified VPC. Note this will only work once deployed.
285
351
  *
@@ -300,8 +366,9 @@ export interface ServiceProps {
300
366
  };
301
367
  }
302
368
  type ServiceNormalizedProps = ServiceProps & {
303
- path: Exclude<ServiceProps["path"], undefined>;
369
+ architecture: Exclude<ServiceProps["architecture"], undefined>;
304
370
  cpu: Exclude<ServiceProps["cpu"], undefined>;
371
+ path: Exclude<ServiceProps["path"], undefined>;
305
372
  memory: Exclude<ServiceProps["memory"], undefined>;
306
373
  port: Exclude<ServiceProps["port"], undefined>;
307
374
  logRetention: Exclude<ServiceProps["logRetention"], undefined>;
@@ -19,7 +19,7 @@ import { attachPermissionsToRole } from "./util/permission.js";
19
19
  import { bindEnvironment, bindPermissions, getParameterPath, getReferencedSecrets, } from "./util/functionBinding.js";
20
20
  import { useProject } from "../project.js";
21
21
  import { Vpc, } from "aws-cdk-lib/aws-ec2";
22
- import { AwsLogDriver, Cluster, FargateTaskDefinition, ContainerImage, FargateService, } from "aws-cdk-lib/aws-ecs";
22
+ import { AwsLogDriver, Cluster, ContainerImage, CpuArchitecture, FargateService, FargateTaskDefinition, } from "aws-cdk-lib/aws-ecs";
23
23
  import { LogGroup, LogRetention, RetentionDays } from "aws-cdk-lib/aws-logs";
24
24
  import { Platform } from "aws-cdk-lib/aws-ecr-assets";
25
25
  import { ApplicationLoadBalancer, } from "aws-cdk-lib/aws-elasticloadbalancingv2";
@@ -153,6 +153,7 @@ export class Service extends Construct {
153
153
  this.id = id;
154
154
  this.props = {
155
155
  path: ".",
156
+ architecture: props?.architecture || "x86_64",
156
157
  cpu: props?.cpu || "0.25 vCPU",
157
158
  memory: props?.memory || "0.5 GB",
158
159
  port: props?.port || 3000,
@@ -188,16 +189,19 @@ export class Service extends Construct {
188
189
  this.attachPermissionsForService(props?.permissions || []);
189
190
  Object.entries(props?.environment || {}).map(([key, value]) => this.addEnvironmentForService(key, value));
190
191
  useDeferredTasks().add(async () => {
191
- if (!app.isRunningSSTTest()) {
192
+ if (!app.isRunningSSTTest() && !props?.cdk?.container?.image) {
192
193
  Colors.line(`➜ Building the container image for the "${this.node.id}" service...`);
193
194
  // Build app
194
195
  let dockerfile;
196
+ // case: custom Dockerfile provided
195
197
  if (this.props.file) {
196
198
  dockerfile = this.props.file;
197
199
  }
200
+ // case: default Dockerfile found
198
201
  else if (await existsAsync(path.join(this.props.path, "Dockerfile"))) {
199
202
  dockerfile = "Dockerfile";
200
203
  }
204
+ // case: nixpack
201
205
  else {
202
206
  await this.createNixpacksBuilder();
203
207
  dockerfile = await this.runNixpacksBuild();
@@ -384,7 +388,7 @@ export class Service extends Construct {
384
388
  }));
385
389
  }
386
390
  createService(vpc) {
387
- const { cpu, memory, port, logRetention, cdk } = this.props;
391
+ const { architecture, cpu, memory, port, logRetention, cdk } = this.props;
388
392
  const app = this.node.root;
389
393
  const clusterName = app.logicalPrefixedName(this.node.id);
390
394
  const logGroup = new LogRetention(this, "LogRetention", {
@@ -402,6 +406,11 @@ export class Service extends Construct {
402
406
  // @ts-ignore
403
407
  memoryLimitMiB: supportedMemories[cpu][memory],
404
408
  cpu: supportedCpus[cpu],
409
+ runtimePlatform: {
410
+ cpuArchitecture: architecture === "arm64"
411
+ ? CpuArchitecture.ARM64
412
+ : CpuArchitecture.X86_64,
413
+ },
405
414
  });
406
415
  const container = taskDefinition.addContainer("Container", {
407
416
  logging: new AwsLogDriver({
@@ -415,11 +424,14 @@ export class Service extends Construct {
415
424
  SST_SSM_PREFIX: useProject().config.ssmPrefix,
416
425
  },
417
426
  ...cdk?.container,
418
- image: { bind: () => ({ imageName: "placeholder" }) },
427
+ image: cdk?.container?.image ?? {
428
+ bind: () => ({ imageName: "placeholder" }),
429
+ },
419
430
  });
420
431
  const service = new FargateService(this, "Service", {
421
432
  cluster,
422
433
  taskDefinition,
434
+ ...cdk?.fargateService,
423
435
  });
424
436
  return { cluster, taskDefinition, container, service };
425
437
  }
@@ -427,6 +439,8 @@ export class Service extends Construct {
427
439
  const { cdk } = this.props;
428
440
  // Do not create load balancer if disabled
429
441
  if (cdk?.applicationLoadBalancer === false) {
442
+ if (cdk?.applicationLoadBalancerTargetGroup)
443
+ throw new VisibleError(`In the "${this.node.id}" Service, the "cdk.applicationLoadBalancerTargetGroup" cannot be applied if the Application Load Balancer is diabled.`);
430
444
  return {};
431
445
  }
432
446
  const alb = new ApplicationLoadBalancer(this, "LoadBalancer", {
@@ -437,6 +451,7 @@ export class Service extends Construct {
437
451
  const target = listener.addTargets("TargetGroup", {
438
452
  port: 80,
439
453
  targets: [service],
454
+ ...cdk?.applicationLoadBalancerTargetGroup,
440
455
  });
441
456
  return { alb, target };
442
457
  }
@@ -592,13 +607,16 @@ export class Service extends Construct {
592
607
  return ".nixpacks/Dockerfile";
593
608
  }
594
609
  async runDockerBuild(dockerfile) {
610
+ const { path: servicePath, architecture, build } = this.props;
611
+ const platform = architecture === "arm64" ? "linux/arm64" : "linux/amd64";
595
612
  try {
596
613
  await execAsync([
597
614
  "docker",
598
615
  "build",
599
616
  `-t sst-build:service-${this.node.id}`,
600
- "--platform=linux/amd64",
601
- `-f ${path.join(this.props.path, dockerfile)}`,
617
+ `--platform ${platform}`,
618
+ `-f ${path.join(servicePath, dockerfile)}`,
619
+ ...Object.entries(build?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
602
620
  this.props.path,
603
621
  ].join(" "), {
604
622
  env: {
@@ -612,9 +630,11 @@ export class Service extends Construct {
612
630
  }
613
631
  }
614
632
  updateContainerImage(dockerfile, taskDefinition, container) {
615
- const image = ContainerImage.fromAsset(this.props.path, {
616
- platform: Platform.LINUX_AMD64,
633
+ const { path: servicePath, architecture, build } = this.props;
634
+ const image = ContainerImage.fromAsset(servicePath, {
635
+ platform: architecture === "arm64" ? Platform.LINUX_ARM64 : Platform.LINUX_AMD64,
617
636
  file: dockerfile,
637
+ buildArgs: build?.buildArgs,
618
638
  exclude: [".sst"],
619
639
  ignoreMode: IgnoreMode.GLOB,
620
640
  });
@@ -19,6 +19,7 @@ export type SsrBuildConfig = {
19
19
  clientBuildOutputDir: string;
20
20
  clientBuildVersionedSubDir: string;
21
21
  clientBuildS3KeyPrefix?: string;
22
+ clientCFFunctionInjection?: string;
22
23
  prerenderedBuildOutputDir?: string;
23
24
  prerenderedBuildS3KeyPrefix?: string;
24
25
  };
@@ -284,7 +285,10 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
284
285
  private serverLambdaForDev?;
285
286
  private serverUrlSigningFunction?;
286
287
  protected bucket: Bucket;
287
- private cfFunction;
288
+ private serverCfFunction?;
289
+ private serverBehaviorCachePolicy?;
290
+ private serverBehaviorOriginRequestPolicy?;
291
+ private staticCfFunction?;
288
292
  private s3Origin;
289
293
  private distribution;
290
294
  constructor(scope: Construct, id: string, props?: SsrSiteProps);
@@ -347,19 +351,22 @@ export declare abstract class SsrSite extends Construct implements SSTConstruct
347
351
  private grantServerS3Permissions;
348
352
  private grantServerCloudFrontPermissions;
349
353
  private createCloudFrontS3Origin;
350
- private createCloudFrontFunction;
351
354
  protected createCloudFrontDistributionForRegional(): Distribution;
352
355
  protected createCloudFrontDistributionForEdge(): Distribution;
353
- protected buildDefaultBehaviorForRegional(cachePolicy: ICachePolicy): BehaviorOptions;
354
- protected useServerUrlSigningFunction(): EdgeFunction;
355
- protected buildDefaultBehaviorForEdge(cachePolicy: ICachePolicy): BehaviorOptions;
356
- protected buildBehaviorFunctionAssociations(): {
356
+ protected buildDefaultBehaviorForRegional(): BehaviorOptions;
357
+ protected buildDefaultBehaviorForEdge(): BehaviorOptions;
358
+ protected addStaticFileBehaviors(): void;
359
+ protected useServerBehaviorFunctionAssociations(): {
357
360
  eventType: CfFunctionEventType;
358
361
  function: CfFunction;
359
362
  }[];
360
- protected addStaticFileBehaviors(): void;
361
- protected buildServerCachePolicy(allowedHeaders?: string[]): CachePolicy;
362
- protected buildServerOriginRequestPolicy(): import("aws-cdk-lib/aws-cloudfront").IOriginRequestPolicy;
363
+ protected useStaticBehaviorFunctionAssociations(): {
364
+ eventType: CfFunctionEventType;
365
+ function: CfFunction;
366
+ }[];
367
+ protected useServerUrlSigningFunction(): EdgeFunction;
368
+ protected useServerBehaviorCachePolicy(allowedHeaders?: string[]): CachePolicy;
369
+ private useServerBehaviorOriginRequestPolicy;
363
370
  private getS3ContentReplaceValues;
364
371
  private validateSiteExists;
365
372
  private validateTimeout;
@@ -51,7 +51,10 @@ export class SsrSite extends Construct {
51
51
  serverLambdaForDev;
52
52
  serverUrlSigningFunction;
53
53
  bucket;
54
- cfFunction;
54
+ serverCfFunction;
55
+ serverBehaviorCachePolicy;
56
+ serverBehaviorOriginRequestPolicy;
57
+ staticCfFunction;
55
58
  s3Origin;
56
59
  distribution;
57
60
  constructor(scope, id, props) {
@@ -75,8 +78,8 @@ export class SsrSite extends Construct {
75
78
  this.writeTypesFile();
76
79
  useSites().add(stack.stackName, id, this.constructor.name, this.props);
77
80
  if (this.doNotDeploy) {
78
- // @ts-ignore
79
- this.cfFunction = this.bucket = this.s3Origin = this.distribution = null;
81
+ // @ts-expect-error
82
+ this.bucket = this.s3Origin = this.distribution = null;
80
83
  this.serverLambdaForDev = this.createFunctionForDev();
81
84
  return;
82
85
  }
@@ -92,7 +95,6 @@ export class SsrSite extends Construct {
92
95
  this.grantServerS3Permissions();
93
96
  // Create CloudFront
94
97
  this.s3Origin = this.createCloudFrontS3Origin();
95
- this.cfFunction = this.createCloudFrontFunction();
96
98
  this.distribution = this.props.edge
97
99
  ? this.createCloudFrontDistributionForEdge()
98
100
  : this.createCloudFrontDistributionForRegional();
@@ -486,21 +488,9 @@ export class SsrSite extends Construct {
486
488
  originPath: "/" + (this.buildConfig.clientBuildS3KeyPrefix ?? ""),
487
489
  });
488
490
  }
489
- createCloudFrontFunction() {
490
- return new CfFunction(this, "CloudFrontFunction", {
491
- code: CfFunctionCode.fromInline(`
492
- function handler(event) {
493
- var request = event.request;
494
- request.headers["x-forwarded-host"] = request.headers.host;
495
- ${this.buildConfig.serverCFFunctionInjection || ""}
496
- return request;
497
- }`),
498
- });
499
- }
500
491
  createCloudFrontDistributionForRegional() {
501
492
  const { customDomain, cdk } = this.props;
502
493
  const cfDistributionProps = cdk?.distribution || {};
503
- const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
504
494
  return new Distribution(this, "CDN", {
505
495
  scopeOverride: this,
506
496
  customDomain,
@@ -511,7 +501,7 @@ function handler(event) {
511
501
  // Override props.
512
502
  ...cfDistributionProps,
513
503
  // these values can NOT be overwritten by cfDistributionProps
514
- defaultBehavior: this.buildDefaultBehaviorForRegional(cachePolicy),
504
+ defaultBehavior: this.buildDefaultBehaviorForRegional(),
515
505
  additionalBehaviors: {
516
506
  ...(cfDistributionProps.additionalBehaviors || {}),
517
507
  },
@@ -522,7 +512,6 @@ function handler(event) {
522
512
  createCloudFrontDistributionForEdge() {
523
513
  const { customDomain, cdk } = this.props;
524
514
  const cfDistributionProps = cdk?.distribution || {};
525
- const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
526
515
  return new Distribution(this, "CDN", {
527
516
  scopeOverride: this,
528
517
  customDomain,
@@ -533,7 +522,7 @@ function handler(event) {
533
522
  // Override props.
534
523
  ...cfDistributionProps,
535
524
  // these values can NOT be overwritten by cfDistributionProps
536
- defaultBehavior: this.buildDefaultBehaviorForEdge(cachePolicy),
525
+ defaultBehavior: this.buildDefaultBehaviorForEdge(),
537
526
  additionalBehaviors: {
538
527
  ...(cfDistributionProps.additionalBehaviors || {}),
539
528
  },
@@ -541,7 +530,7 @@ function handler(event) {
541
530
  },
542
531
  });
543
532
  }
544
- buildDefaultBehaviorForRegional(cachePolicy) {
533
+ buildDefaultBehaviorForRegional() {
545
534
  const { timeout, regional, cdk } = this.props;
546
535
  const cfDistributionProps = cdk?.distribution || {};
547
536
  const fnUrl = this.serverLambdaForRegional.addFunctionUrl({
@@ -562,12 +551,12 @@ function handler(event) {
562
551
  allowedMethods: AllowedMethods.ALLOW_ALL,
563
552
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
564
553
  compress: true,
565
- cachePolicy,
554
+ cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
566
555
  responseHeadersPolicy: cdk?.responseHeadersPolicy,
567
- originRequestPolicy: this.buildServerOriginRequestPolicy(),
556
+ originRequestPolicy: this.useServerBehaviorOriginRequestPolicy(),
568
557
  ...(cfDistributionProps.defaultBehavior || {}),
569
558
  functionAssociations: [
570
- ...this.buildBehaviorFunctionAssociations(),
559
+ ...this.useServerBehaviorFunctionAssociations(),
571
560
  ...(cfDistributionProps.defaultBehavior?.functionAssociations || []),
572
561
  ],
573
562
  edgeLambdas: [
@@ -584,25 +573,7 @@ function handler(event) {
584
573
  ],
585
574
  };
586
575
  }
587
- useServerUrlSigningFunction() {
588
- this.serverUrlSigningFunction =
589
- this.serverUrlSigningFunction ??
590
- new EdgeFunction(this, "ServerUrlSigningFunction", {
591
- bundle: path.join(__dirname, "../support/signing-function"),
592
- runtime: "nodejs18.x",
593
- handler: "index.handler",
594
- timeout: 10,
595
- memorySize: 128,
596
- permissions: [
597
- new PolicyStatement({
598
- actions: ["lambda:InvokeFunctionUrl"],
599
- resources: [this.serverLambdaForRegional?.functionArn],
600
- }),
601
- ],
602
- });
603
- return this.serverUrlSigningFunction;
604
- }
605
- buildDefaultBehaviorForEdge(cachePolicy) {
576
+ buildDefaultBehaviorForEdge() {
606
577
  const { cdk } = this.props;
607
578
  const cfDistributionProps = cdk?.distribution || {};
608
579
  return {
@@ -611,12 +582,12 @@ function handler(event) {
611
582
  allowedMethods: AllowedMethods.ALLOW_ALL,
612
583
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
613
584
  compress: true,
614
- cachePolicy,
585
+ cachePolicy: cdk?.serverCachePolicy ?? this.useServerBehaviorCachePolicy(),
615
586
  responseHeadersPolicy: cdk?.responseHeadersPolicy,
616
- originRequestPolicy: this.buildServerOriginRequestPolicy(),
587
+ originRequestPolicy: this.useServerBehaviorOriginRequestPolicy(),
617
588
  ...(cfDistributionProps.defaultBehavior || {}),
618
589
  functionAssociations: [
619
- ...this.buildBehaviorFunctionAssociations(),
590
+ ...this.useServerBehaviorFunctionAssociations(),
620
591
  ...(cfDistributionProps.defaultBehavior?.functionAssociations || []),
621
592
  ],
622
593
  edgeLambdas: [
@@ -629,14 +600,6 @@ function handler(event) {
629
600
  ],
630
601
  };
631
602
  }
632
- buildBehaviorFunctionAssociations() {
633
- return [
634
- {
635
- eventType: CfFunctionEventType.VIEWER_REQUEST,
636
- function: this.cfFunction,
637
- },
638
- ];
639
- }
640
603
  addStaticFileBehaviors() {
641
604
  const { cdk } = this.props;
642
605
  // Create a template for statics behaviours
@@ -650,27 +613,93 @@ function handler(event) {
650
613
  compress: true,
651
614
  cachePolicy: CachePolicy.CACHING_OPTIMIZED,
652
615
  responseHeadersPolicy: cdk?.responseHeadersPolicy,
616
+ functionAssociations: [
617
+ ...this.useStaticBehaviorFunctionAssociations(),
618
+ ],
653
619
  });
654
620
  }
655
621
  }
656
- buildServerCachePolicy(allowedHeaders) {
657
- return new CachePolicy(this, "ServerCache", {
658
- queryStringBehavior: CacheQueryStringBehavior.all(),
659
- headerBehavior: allowedHeaders && allowedHeaders.length > 0
660
- ? CacheHeaderBehavior.allowList(...allowedHeaders)
661
- : CacheHeaderBehavior.none(),
662
- cookieBehavior: CacheCookieBehavior.none(),
663
- defaultTtl: CdkDuration.days(0),
664
- maxTtl: CdkDuration.days(365),
665
- minTtl: CdkDuration.days(0),
666
- enableAcceptEncodingBrotli: true,
667
- enableAcceptEncodingGzip: true,
668
- comment: "SST server response cache policy",
669
- });
622
+ useServerBehaviorFunctionAssociations() {
623
+ this.serverCfFunction =
624
+ this.serverCfFunction ??
625
+ new CfFunction(this, "CloudFrontFunction", {
626
+ code: CfFunctionCode.fromInline(`
627
+ function handler(event) {
628
+ var request = event.request;
629
+ request.headers["x-forwarded-host"] = request.headers.host;
630
+ ${this.buildConfig.serverCFFunctionInjection || ""}
631
+ return request;
632
+ }`),
633
+ });
634
+ return [
635
+ {
636
+ eventType: CfFunctionEventType.VIEWER_REQUEST,
637
+ function: this.serverCfFunction,
638
+ },
639
+ ];
640
+ }
641
+ useStaticBehaviorFunctionAssociations() {
642
+ if (!this.buildConfig.clientCFFunctionInjection)
643
+ return [];
644
+ this.staticCfFunction =
645
+ this.staticCfFunction ??
646
+ new CfFunction(this, "CloudFrontFunctionForStaticBehavior", {
647
+ code: CfFunctionCode.fromInline(`
648
+ function handler(event) {
649
+ var request = event.request;
650
+ ${this.buildConfig.clientCFFunctionInjection || ""}
651
+ return request;
652
+ }`),
653
+ });
654
+ return [
655
+ {
656
+ eventType: CfFunctionEventType.VIEWER_REQUEST,
657
+ function: this.staticCfFunction,
658
+ },
659
+ ];
660
+ }
661
+ useServerUrlSigningFunction() {
662
+ this.serverUrlSigningFunction =
663
+ this.serverUrlSigningFunction ??
664
+ new EdgeFunction(this, "ServerUrlSigningFunction", {
665
+ bundle: path.join(__dirname, "../support/signing-function"),
666
+ runtime: "nodejs18.x",
667
+ handler: "index.handler",
668
+ timeout: 10,
669
+ memorySize: 128,
670
+ permissions: [
671
+ new PolicyStatement({
672
+ actions: ["lambda:InvokeFunctionUrl"],
673
+ resources: [this.serverLambdaForRegional?.functionArn],
674
+ }),
675
+ ],
676
+ });
677
+ return this.serverUrlSigningFunction;
678
+ }
679
+ useServerBehaviorCachePolicy(allowedHeaders) {
680
+ this.serverBehaviorCachePolicy =
681
+ this.serverBehaviorCachePolicy ??
682
+ new CachePolicy(this, "ServerCache", {
683
+ queryStringBehavior: CacheQueryStringBehavior.all(),
684
+ headerBehavior: allowedHeaders && allowedHeaders.length > 0
685
+ ? CacheHeaderBehavior.allowList(...allowedHeaders)
686
+ : CacheHeaderBehavior.none(),
687
+ cookieBehavior: CacheCookieBehavior.none(),
688
+ defaultTtl: CdkDuration.days(0),
689
+ maxTtl: CdkDuration.days(365),
690
+ minTtl: CdkDuration.days(0),
691
+ enableAcceptEncodingBrotli: true,
692
+ enableAcceptEncodingGzip: true,
693
+ comment: "SST server response cache policy",
694
+ });
695
+ return this.serverBehaviorCachePolicy;
670
696
  }
671
- buildServerOriginRequestPolicy() {
697
+ useServerBehaviorOriginRequestPolicy() {
672
698
  // CloudFront's Managed-AllViewerExceptHostHeader policy
673
- return OriginRequestPolicy.fromOriginRequestPolicyId(this, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
699
+ this.serverBehaviorOriginRequestPolicy =
700
+ this.serverBehaviorOriginRequestPolicy ??
701
+ OriginRequestPolicy.fromOriginRequestPolicyId(this, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
702
+ return this.serverBehaviorOriginRequestPolicy;
674
703
  }
675
704
  /////////////////////
676
705
  // Helper Functions
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.24.22",
4
+ "version": "2.24.24",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },
@@ -58,6 +58,7 @@ export const useContainerHandler = Context.memo(async () => {
58
58
  ...(input.props.container?.file
59
59
  ? [`-f ${input.props.container.file}`]
60
60
  : []),
61
+ ...Object.entries(input.props.container?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
61
62
  `.`,
62
63
  ].join(" "), {
63
64
  cwd: project,
@@ -84,6 +85,7 @@ export const useContainerHandler = Context.memo(async () => {
84
85
  ...(input.props.container?.file
85
86
  ? [`-f ${input.props.container.file}`]
86
87
  : []),
88
+ ...Object.entries(input.props.container?.buildArgs || {}).map(([k, v]) => `--build-arg ${k}=${v}`),
87
89
  `--platform ${platform}`,
88
90
  `.`,
89
91
  ].join(" "), {
@@ -68,26 +68,22 @@ export const useJavaHandler = Context.memo(async () => {
68
68
  const buildTask = input.props.java?.buildTask || "build";
69
69
  const outputDir = input.props.java?.buildOutputDir || "distributions";
70
70
  sources.set(input.functionID, srcPath);
71
- async function build() {
72
- // build
73
- await execAsync(`${buildBinary} ${buildTask} -Dorg.gradle.logging.level=${process.env.DEBUG ? "debug" : "lifecycle"}`, {
74
- cwd: srcPath,
75
- });
76
- // unzip
77
- const buildOutput = path.join(srcPath, "build", outputDir);
78
- const zip = (await fs.readdir(buildOutput)).find((f) => f.endsWith(".zip"));
79
- await new Promise((resolve, reject) => {
80
- const zipper = new AdmZip(path.join(buildOutput, zip));
81
- zipper.extractAllToAsync(input.out, false, false, (err) => err ? reject(err) : resolve(undefined));
82
- });
83
- }
84
71
  try {
85
- // Run gradle build once per directory. Otherwise they'll interfere
72
+ // Build
73
+ // Note: run gradle build once per directory. Otherwise they'll interfere
86
74
  // with one another
87
- const buildPromise = runningBuilds.get(buildBinary) ?? build();
75
+ const buildPromise = runningBuilds.get(buildBinary) ??
76
+ execAsync(`${buildBinary} ${buildTask} -Dorg.gradle.logging.level=${process.env.DEBUG ? "debug" : "lifecycle"}`, {
77
+ cwd: srcPath,
78
+ });
88
79
  runningBuilds.set(buildBinary, buildPromise);
89
80
  await buildPromise;
90
81
  runningBuilds.delete(buildBinary);
82
+ // unzip
83
+ const buildOutput = path.join(srcPath, "build", outputDir);
84
+ const zip = (await fs.readdir(buildOutput)).find((f) => f.endsWith(".zip"));
85
+ const zipper = new AdmZip(path.join(buildOutput, zip));
86
+ zipper.extractAllTo(input.out, false, false);
91
87
  return {
92
88
  type: "success",
93
89
  handler: input.props.handler,