sst 2.41.5 → 2.43.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,35 +1,20 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import zlib from "zlib";
4
3
  import crypto from "crypto";
5
- import { globSync } from "glob";
6
- import { Duration as CdkDuration, RemovalPolicy, CustomResource, } from "aws-cdk-lib/core";
7
- import { Code, Runtime, Function as CdkFunction, Architecture, LayerVersion, } from "aws-cdk-lib/aws-lambda";
4
+ import { Duration as CdkDuration, RemovalPolicy, CustomResource, Fn, } from "aws-cdk-lib/core";
5
+ import { Code, Runtime, Function as CdkFunction, Architecture, } from "aws-cdk-lib/aws-lambda";
8
6
  import { AttributeType, Billing, TableV2 as Table, } from "aws-cdk-lib/aws-dynamodb";
9
7
  import { Provider } from "aws-cdk-lib/custom-resources";
10
8
  import { Queue } from "aws-cdk-lib/aws-sqs";
11
9
  import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
12
10
  import { Stack } from "./Stack.js";
13
- import { SsrSite } from "./SsrSite.js";
11
+ import { SsrSite, } from "./SsrSite.js";
14
12
  import { toCdkSize } from "./util/size.js";
15
- import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
13
+ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
16
14
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
17
15
  import { VisibleError } from "../error.js";
18
- import { Asset } from "aws-cdk-lib/aws-s3-assets";
19
- import { useFunctions } from "./Function.js";
20
- import { useDeferredTasks } from "./deferred_task.js";
21
16
  import { Logger } from "../logger.js";
22
- const LAYER_VERSION = "2";
23
- const DEFAULT_OPEN_NEXT_VERSION = "2.3.8";
24
- const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
25
- "accept",
26
- "rsc",
27
- "next-router-prefetch",
28
- "next-router-state-tree",
29
- "next-url",
30
- "x-prerender-bypass",
31
- "x-prerender-revalidate",
32
- ];
17
+ const DEFAULT_OPEN_NEXT_VERSION = "3.0.2";
33
18
  /**
34
19
  * The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
35
20
  * @example
@@ -48,118 +33,135 @@ export class NextjsSite extends SsrSite {
48
33
  appPathsManifest;
49
34
  pagesManifest;
50
35
  prerenderManifest;
51
- constructor(scope, id, rawProps) {
52
- const props = {
53
- logging: rawProps?.logging ?? "per-route",
54
- experimental: {
55
- streaming: rawProps?.experimental?.streaming ?? false,
56
- disableDynamoDBCache: rawProps?.experimental?.disableDynamoDBCache ?? false,
57
- disableIncrementalCache: rawProps?.experimental?.disableIncrementalCache ?? false,
58
- ...rawProps?.experimental,
59
- },
60
- ...rawProps,
61
- };
36
+ openNextOutput;
37
+ constructor(scope, id, props = {}) {
62
38
  super(scope, id, {
63
39
  buildCommand: [
64
40
  "npx",
65
41
  "--yes",
66
42
  `open-next@${props?.openNextVersion ?? DEFAULT_OPEN_NEXT_VERSION}`,
67
43
  "build",
68
- ...(props.openNext?.buildOutputPath
69
- ? ["--build-output-path", props.openNext.buildOutputPath]
70
- : []),
71
- ...(props.experimental.streaming ? ["--streaming"] : []),
72
- ...(props.experimental.disableDynamoDBCache
73
- ? ["--dangerously-disable-dynamodb-cache"]
74
- : []),
75
- ...(props.experimental.disableIncrementalCache
76
- ? ["--dangerously-disable-incremental-cache"]
77
- : []),
78
44
  ].join(" "),
79
45
  ...props,
80
46
  });
47
+ const disableIncrementalCache = this.openNextOutput?.additionalProps?.disableIncrementalCache ?? false;
48
+ const disableTagCache = this.openNextOutput?.additionalProps?.disableTagCache ?? false;
81
49
  this.handleMissingSourcemap();
82
- if (this.isPerRouteLoggingEnabled()) {
83
- //this.disableDefaultLogging();
84
- this.uploadSourcemaps();
50
+ if (this.openNextOutput?.edgeFunctions?.middleware) {
51
+ this.setMiddlewareEnv();
85
52
  }
86
- if (!props.experimental.disableIncrementalCache) {
53
+ if (!disableIncrementalCache) {
87
54
  this.createRevalidationQueue();
88
- if (!props.experimental.disableDynamoDBCache) {
55
+ if (!disableTagCache) {
89
56
  this.createRevalidationTable();
90
57
  }
91
58
  }
92
59
  }
93
- static buildDefaultServerCachePolicyProps() {
94
- return super.buildDefaultServerCachePolicyProps(DEFAULT_CACHE_POLICY_ALLOWED_HEADERS);
60
+ createFunctionOrigin(fn, key, bucket) {
61
+ const { path: sitePath, environment, cdk } = this.props;
62
+ const baseServerConfig = {
63
+ description: "Next.js Server",
64
+ environment: {
65
+ CACHE_BUCKET_NAME: bucket.bucketName,
66
+ CACHE_BUCKET_KEY_PREFIX: "_cache",
67
+ CACHE_BUCKET_REGION: Stack.of(this).region,
68
+ },
69
+ };
70
+ return {
71
+ type: "function",
72
+ constructId: `${key}ServerFunction`,
73
+ function: {
74
+ ...baseServerConfig,
75
+ handler: fn.handler,
76
+ bundle: path.join(sitePath, fn.bundle),
77
+ runtime: "nodejs18.x",
78
+ architecture: Architecture.ARM_64,
79
+ memorySize: 1536,
80
+ environment: {
81
+ ...environment,
82
+ ...baseServerConfig.environment,
83
+ },
84
+ },
85
+ streaming: fn.streaming,
86
+ injections: [],
87
+ };
88
+ }
89
+ createEcsOrigin(ecs, key, bucket) {
90
+ throw new Error("Ecs origin are not supported yet");
95
91
  }
96
- plan(bucket) {
97
- const { path: sitePath, edge, experimental, imageOptimization, cdk, } = this.props;
98
- const stack = Stack.of(this);
99
- const serverConfig = {
100
- description: "Next.js server",
101
- bundle: path.join(sitePath, ".open-next", "server-function"),
102
- handler: "index.handler",
92
+ createEdgeOrigin(fn, key, bucket) {
93
+ const { path: sitePath, cdk, environment } = this.props;
94
+ const baseServerConfig = {
103
95
  environment: {
104
96
  CACHE_BUCKET_NAME: bucket.bucketName,
105
97
  CACHE_BUCKET_KEY_PREFIX: "_cache",
106
98
  CACHE_BUCKET_REGION: Stack.of(this).region,
107
99
  },
108
- layers: this.isPerRouteLoggingEnabled()
109
- ? [
110
- LayerVersion.fromLayerVersionArn(this, "SSTExtension", cdk?.server?.architecture?.name === Architecture.X86_64.name
111
- ? `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-amd64:${LAYER_VERSION}`
112
- : `arn:aws:lambda:${stack.region}:226609089145:layer:sst-extension-arm64:${LAYER_VERSION}`),
113
- ]
114
- : undefined,
115
100
  };
116
- this.removeSourcemaps();
101
+ return {
102
+ constructId: `${key}EdgeFunction`,
103
+ function: {
104
+ handler: fn.handler,
105
+ bundle: path.join(sitePath, fn.bundle),
106
+ runtime: "nodejs18.x",
107
+ memorySize: 1024,
108
+ environment: {
109
+ ...environment,
110
+ ...baseServerConfig.environment,
111
+ },
112
+ },
113
+ };
114
+ }
115
+ plan(bucket) {
116
+ const { path: sitePath } = this.props;
117
+ const imageOptimization = this.props.imageOptimization;
118
+ const openNextOutputPath = path.join(sitePath ?? ".", ".open-next", "open-next.output.json");
119
+ if (!fs.existsSync(openNextOutputPath)) {
120
+ throw new VisibleError(`Failed to load ".open-next/output.json" for the "${this.id}" site.`);
121
+ }
122
+ const openNextOutput = JSON.parse(fs.readFileSync(openNextOutputPath).toString());
123
+ this.openNextOutput = openNextOutput;
124
+ const imageOpt = openNextOutput.origins
125
+ .imageOptimizer;
126
+ const defaultOrigin = openNextOutput.origins.default;
127
+ const remainingOrigins = Object.entries(openNextOutput.origins).filter(([key, value]) => {
128
+ const result = key !== "imageOptimizer" && key !== "default" && key !== "s3";
129
+ return result;
130
+ });
131
+ const edgeFunctions = Object.entries(openNextOutput.edgeFunctions).reduce((acc, [key, value]) => {
132
+ return { ...acc, [key]: this.createEdgeOrigin(value, key, bucket) };
133
+ }, {});
117
134
  return this.validatePlan({
118
- edge: edge ?? false,
135
+ edge: false,
119
136
  cloudFrontFunctions: {
120
137
  serverCfFunction: {
121
138
  constructId: "CloudFrontFunction",
122
139
  injections: [
123
140
  this.useCloudFrontFunctionHostHeaderInjection(),
124
- this.useCloudFrontFunctionPrerenderBypassHeaderInjection(),
141
+ this.useCloudFrontFunctionCacheHeaderKey(),
142
+ this.useCloudfrontGeoHeadersInjection(),
125
143
  ],
126
144
  },
127
145
  },
128
- edgeFunctions: edge
129
- ? {
130
- edgeServer: {
131
- constructId: "ServerFunction",
132
- function: serverConfig,
133
- },
134
- }
135
- : undefined,
146
+ edgeFunctions,
136
147
  origins: {
137
- ...(edge
138
- ? {}
139
- : {
140
- regionalServer: {
141
- type: "function",
142
- constructId: "ServerFunction",
143
- function: serverConfig,
144
- streaming: experimental?.streaming,
145
- injections: this.isPerRouteLoggingEnabled()
146
- ? [this.useServerFunctionPerRouteLoggingInjection()]
147
- : [],
148
- },
149
- }),
148
+ s3: openNextOutput.origins.s3,
150
149
  imageOptimizer: {
151
150
  type: "image-optimization-function",
152
- constructId: "ImageFunction",
153
151
  function: {
154
- description: "Next.js image optimizer",
155
- handler: "index.handler",
156
- code: Code.fromAsset(path.join(sitePath, ".open-next/image-optimization-function")),
152
+ description: "Next.js Image Optimization Function",
153
+ handler: imageOpt.handler,
154
+ code: Code.fromAsset(path.join(sitePath, imageOpt.bundle)),
157
155
  runtime: Runtime.NODEJS_18_X,
158
156
  architecture: Architecture.ARM_64,
159
157
  environment: {
160
158
  BUCKET_NAME: bucket.bucketName,
161
159
  BUCKET_KEY_PREFIX: "_assets",
160
+ ...(this.props.imageOptimization?.staticImageOptimization
161
+ ? { OPENNEXT_STATIC_ETAG: "true" }
162
+ : {}),
162
163
  },
164
+ permissions: ["s3"],
163
165
  memorySize: imageOptimization?.memorySize
164
166
  ? typeof imageOptimization.memorySize === "string"
165
167
  ? toCdkSize(imageOptimization.memorySize).toMebibytes()
@@ -167,98 +169,55 @@ export class NextjsSite extends SsrSite {
167
169
  : 1536,
168
170
  },
169
171
  },
170
- s3: {
171
- type: "s3",
172
- originPath: "_assets",
173
- copy: [
174
- {
175
- from: ".open-next/assets",
176
- to: "_assets",
177
- cached: true,
178
- versionedSubDir: "_next",
179
- },
180
- { from: ".open-next/cache", to: "_cache", cached: false },
181
- ],
182
- },
172
+ default: defaultOrigin.type === "ecs"
173
+ ? this.createEcsOrigin(defaultOrigin, "default", bucket)
174
+ : this.createFunctionOrigin(defaultOrigin, "default", bucket),
175
+ ...Object.fromEntries(remainingOrigins.map(([key, value]) => [
176
+ key,
177
+ value.type === "ecs"
178
+ ? this.createEcsOrigin(value, key, bucket)
179
+ : this.createFunctionOrigin(value, key, bucket),
180
+ ])),
183
181
  },
184
- behaviors: [
185
- ...(edge
186
- ? [
187
- {
188
- cacheType: "server",
189
- cfFunction: "serverCfFunction",
190
- edgeFunction: "edgeServer",
191
- origin: "s3",
192
- },
193
- {
194
- cacheType: "server",
195
- pattern: this.prefixPattern("api/*"),
196
- cfFunction: "serverCfFunction",
197
- edgeFunction: "edgeServer",
198
- origin: "s3",
199
- },
200
- {
201
- cacheType: "server",
202
- pattern: this.prefixPattern("_next/data/*"),
203
- cfFunction: "serverCfFunction",
204
- edgeFunction: "edgeServer",
205
- origin: "s3",
206
- },
207
- ]
208
- : [
209
- {
210
- cacheType: "server",
211
- cfFunction: "serverCfFunction",
212
- origin: "regionalServer",
213
- },
214
- {
215
- cacheType: "server",
216
- pattern: this.prefixPattern("api/*"),
217
- cfFunction: "serverCfFunction",
218
- origin: "regionalServer",
219
- },
220
- {
221
- cacheType: "server",
222
- pattern: this.prefixPattern("_next/data/*"),
223
- cfFunction: "serverCfFunction",
224
- origin: "regionalServer",
225
- },
226
- ]),
227
- {
228
- cacheType: "server",
229
- pattern: this.prefixPattern("_next/image*"),
182
+ behaviors: openNextOutput.behaviors.map((behavior) => {
183
+ return {
184
+ pattern: behavior.pattern === "*" ? undefined : behavior.pattern,
185
+ origin: behavior.origin,
186
+ cacheType: behavior.origin === "s3" ? "static" : "server",
230
187
  cfFunction: "serverCfFunction",
231
- origin: "imageOptimizer",
232
- },
233
- // create 1 behaviour for each top level asset file/folder
234
- ...fs.readdirSync(path.join(sitePath, ".open-next/assets")).map((item) => ({
235
- cacheType: "static",
236
- pattern: this.prefixPattern(fs
237
- .statSync(path.join(sitePath, ".open-next/assets", item))
238
- .isDirectory()
239
- ? `${item}/*`
240
- : item),
241
- origin: "s3",
242
- })),
243
- ],
188
+ edgeFunction: behavior.edgeFunction ?? "",
189
+ };
190
+ }),
191
+ buildId: this.getBuildId(),
192
+ warmer: openNextOutput.additionalProps?.warmer
193
+ ? {
194
+ function: path.join(sitePath, openNextOutput.additionalProps.warmer.bundle),
195
+ }
196
+ : undefined,
244
197
  serverCachePolicy: {
245
- allowedHeaders: DEFAULT_CACHE_POLICY_ALLOWED_HEADERS,
198
+ allowedHeaders: ["x-open-next-cache-key"],
246
199
  },
247
- buildId: this.getBuildId(),
248
200
  });
249
201
  }
250
- prefixPattern(pattern) {
251
- // Prefix CloudFront distribution behavior path patterns with `basePath` if configured
252
- const { basePath } = this.useRoutesManifest();
253
- return basePath && basePath.length > 0
254
- ? `${basePath.slice(1)}/${pattern}`
255
- : pattern;
202
+ setMiddlewareEnv() {
203
+ const origins = this.serverFunctions.reduce((acc, server) => {
204
+ return {
205
+ ...acc,
206
+ [server.function
207
+ ? server.id.replace("ServerFunction", "")
208
+ : server.id.replace("ServerContainer", "")]: {
209
+ host: Fn.parseDomainName(server.url ?? ""),
210
+ port: 443,
211
+ protocol: "https",
212
+ },
213
+ };
214
+ }, {});
215
+ this.edgeFunctions?.middleware?.addEnvironment("OPEN_NEXT_ORIGIN", Fn.toJsonString(origins));
256
216
  }
257
217
  createRevalidationQueue() {
258
218
  if (!this.serverFunction)
259
219
  return;
260
220
  const { cdk } = this.props;
261
- const server = this.serverFunction;
262
221
  const queue = new Queue(this, "RevalidationQueue", {
263
222
  fifo: true,
264
223
  receiveMessageWaitTime: CdkDuration.seconds(20),
@@ -272,16 +231,17 @@ export class NextjsSite extends SsrSite {
272
231
  ...cdk?.revalidation,
273
232
  });
274
233
  consumer.addEventSource(new SqsEventSource(queue, { batchSize: 5 }));
275
- // Allow server to send messages to the queue
276
- server.addEnvironment("REVALIDATION_QUEUE_URL", queue.queueUrl);
277
- server.addEnvironment("REVALIDATION_QUEUE_REGION", Stack.of(this).region);
278
- queue.grantSendMessages(server.role);
234
+ this.serverFunctions.forEach((server) => {
235
+ // Allow server to send messages to the queue
236
+ server.addEnvironment("REVALIDATION_QUEUE_URL", queue.queueUrl);
237
+ server.addEnvironment("REVALIDATION_QUEUE_REGION", Stack.of(this).region);
238
+ queue.grantSendMessages(server.role);
239
+ });
279
240
  }
280
241
  createRevalidationTable() {
281
242
  if (!this.serverFunction)
282
243
  return;
283
244
  const { path: sitePath } = this.props;
284
- const server = this.serverFunction;
285
245
  const table = new Table(this, "RevalidationTable", {
286
246
  partitionKey: { name: "tag", type: AttributeType.STRING },
287
247
  sortKey: { name: "path", type: AttributeType.STRING },
@@ -296,8 +256,10 @@ export class NextjsSite extends SsrSite {
296
256
  ],
297
257
  removalPolicy: RemovalPolicy.DESTROY,
298
258
  });
299
- server?.addEnvironment("CACHE_DYNAMO_TABLE", table.tableName);
300
- table.grantReadWriteData(server.role);
259
+ this.serverFunctions.forEach((server) => {
260
+ server?.addEnvironment("CACHE_DYNAMO_TABLE", table.tableName);
261
+ table.grantReadWriteData(server.role);
262
+ });
301
263
  const dynamodbProviderPath = path.join(sitePath, ".open-next", "dynamodb-provider");
302
264
  if (fs.existsSync(dynamodbProviderPath)) {
303
265
  // Provision 128MB of memory for every 4,000 prerendered routes,
@@ -338,35 +300,11 @@ export class NextjsSite extends SsrSite {
338
300
  }
339
301
  }
340
302
  getConstructMetadata() {
341
- const metadata = this.getConstructMetadataBase();
342
303
  return {
343
- ...metadata,
344
304
  type: "NextjsSite",
345
- data: {
346
- ...metadata.data,
347
- routes: this.isPerRouteLoggingEnabled()
348
- ? {
349
- logGroupPrefix: `/sst/lambda/${this.serverFunction.functionName}`,
350
- data: this.useRoutes().map(({ route, logGroupPath }) => ({
351
- route,
352
- logGroupPath,
353
- })),
354
- }
355
- : undefined,
356
- },
305
+ ...this.getConstructMetadataBase(),
357
306
  };
358
307
  }
359
- removeSourcemaps() {
360
- const { path: sitePath } = this.props;
361
- const files = globSync("**/*.js.map", {
362
- cwd: path.join(sitePath, ".open-next", "server-function"),
363
- nodir: true,
364
- dot: true,
365
- });
366
- for (const file of files) {
367
- fs.rmSync(path.join(sitePath, ".open-next", "server-function", file));
368
- }
369
- }
370
308
  useRoutes() {
371
309
  if (this._routes)
372
310
  return this._routes;
@@ -510,38 +448,57 @@ export class NextjsSite extends SsrSite {
510
448
  Logger.debug("Failed to load prerender-manifest.json", e);
511
449
  }
512
450
  }
513
- useServerFunctionPerRouteLoggingInjection() {
451
+ // This function is used to improve cache hit ratio by setting the cache key based on the request headers and the path
452
+ // next/image only need the accept header, and this header is not useful for the rest of the query
453
+ useCloudFrontFunctionCacheHeaderKey() {
514
454
  return `
515
- if (event.rawPath) {
516
- const routeData = ${JSON.stringify(
517
- // @ts-expect-error
518
- this.useRoutes().map(({ regexMatch, prefixMatch, logGroupPath }) => ({
519
- regex: regexMatch,
520
- prefix: prefixMatch,
521
- logGroupPath,
522
- })))}.find(({ regex, prefix }) => {
523
- if (regex) return event.rawPath.match(new RegExp(regex));
524
- if (prefix) return event.rawPath === prefix || (event.rawPath === prefix + "/");
525
- return false;
526
- });
527
- if (routeData) {
528
- console.log("::sst::" + JSON.stringify({
529
- action:"log.split",
530
- properties: {
531
- logGroupName:"/sst/lambda/" + context.functionName + routeData.logGroupPath,
532
- },
533
- }));
455
+ function getHeader(key) {
456
+ var header = request.headers[key];
457
+ if(header) {
458
+ if(header.multiValue){
459
+ return header.multiValue.map((header) => header.value).join(",");
460
+ }
461
+ if(header.value){
462
+ return header.value;
463
+ }
464
+ }
465
+ return ""
466
+ }
467
+ var cacheKey = "";
468
+ if(request.uri.startsWith("/_next/image")) {
469
+ cacheKey = getHeader("accept");
470
+ }else {
471
+ cacheKey = getHeader("rsc") + getHeader("next-router-prefetch") + getHeader("next-router-state-tree") + getHeader("next-url") + getHeader("x-prerender-revalidate");
472
+ }
473
+ if(request.cookies["__prerender_bypass"]) {
474
+ cacheKey += request.cookies["__prerender_bypass"] ? request.cookies["__prerender_bypass"].value : "";
534
475
  }
535
- }`;
476
+ var crypto = require('crypto');
477
+
478
+ var hashedKey = crypto.createHash('md5').update(cacheKey).digest('hex');
479
+ request.headers["x-open-next-cache-key"] = {value: hashedKey};
480
+ `;
536
481
  }
537
- useCloudFrontFunctionPrerenderBypassHeaderInjection() {
538
- // In Next.js page router preview mode (depends on the cookie __prerender_bypass),
539
- // to ensure we receive the cached page instead of the preview version, we set the
540
- // header "x-prerender-bypass", and add it to cache policy's allowed headers.
482
+ // Inject the CloudFront viewer country, region, latitude, and longitude headers into the request headers
483
+ // for OpenNext to use them
484
+ useCloudfrontGeoHeadersInjection() {
541
485
  return `
542
- if (request.cookies["__prerender_bypass"]) {
543
- request.headers["x-prerender-bypass"] = { value: "true" };
544
- }`;
486
+ if(request.headers["cloudfront-viewer-city"]) {
487
+ request.headers["x-open-next-city"] = request.headers["cloudfront-viewer-city"];
488
+ }
489
+ if(request.headers["cloudfront-viewer-country"]) {
490
+ request.headers["x-open-next-country"] = request.headers["cloudfront-viewer-country"];
491
+ }
492
+ if(request.headers["cloudfront-viewer-region"]) {
493
+ request.headers["x-open-next-region"] = request.headers["cloudfront-viewer-region"];
494
+ }
495
+ if(request.headers["cloudfront-viewer-latitude"]) {
496
+ request.headers["x-open-next-latitude"] = request.headers["cloudfront-viewer-latitude"];
497
+ }
498
+ if(request.headers["cloudfront-viewer-longitude"]) {
499
+ request.headers["x-open-next-longitude"] = request.headers["cloudfront-viewer-longitude"];
500
+ }
501
+ `;
545
502
  }
546
503
  getBuildId() {
547
504
  const { path: sitePath } = this.props;
@@ -605,11 +562,6 @@ if (request.cookies["__prerender_bypass"]) {
605
562
  return;
606
563
  return sourcemapPath;
607
564
  }
608
- isPerRouteLoggingEnabled() {
609
- return (!this.doNotDeploy &&
610
- !this.props.edge &&
611
- this.props.logging === "per-route");
612
- }
613
565
  handleMissingSourcemap() {
614
566
  if (this.doNotDeploy || this.props.edge)
615
567
  return;
@@ -618,48 +570,6 @@ if (request.cookies["__prerender_bypass"]) {
618
570
  return;
619
571
  this.serverFunction._overrideMissingSourcemap();
620
572
  }
621
- disableDefaultLogging() {
622
- const stack = Stack.of(this);
623
- const server = this.serverFunction;
624
- const policy = new Policy(this, "DisableLoggingPolicy", {
625
- statements: [
626
- new PolicyStatement({
627
- effect: Effect.DENY,
628
- actions: [
629
- "logs:CreateLogGroup",
630
- "logs:CreateLogStream",
631
- "logs:PutLogEvents",
632
- ],
633
- resources: [
634
- `arn:aws:logs:${stack.region}:${stack.account}:log-group:/aws/lambda/${server.functionName}`,
635
- `arn:aws:logs:${stack.region}:${stack.account}:log-group:/aws/lambda/${server.functionName}:*`,
636
- ],
637
- }),
638
- ],
639
- });
640
- server.role?.attachInlinePolicy(policy);
641
- }
642
- uploadSourcemaps() {
643
- const stack = Stack.of(this);
644
- const server = this.serverFunction;
645
- this.useRoutes().forEach(({ sourcemapPath, sourcemapKey }) => {
646
- if (!sourcemapPath || !sourcemapKey)
647
- return;
648
- useDeferredTasks().add(async () => {
649
- // zip sourcemap
650
- const zipPath = `${sourcemapPath}.gz.zip`;
651
- const data = await fs.promises.readFile(sourcemapPath);
652
- await fs.promises.writeFile(zipPath, zlib.gzipSync(data));
653
- const asset = new Asset(this, `Sourcemap-${sourcemapKey}`, {
654
- path: zipPath,
655
- });
656
- useFunctions().sourcemaps.add(stack.stackName, {
657
- asset,
658
- tarKey: path.join(server.functionArn, sourcemapKey),
659
- });
660
- });
661
- });
662
- }
663
573
  static buildCloudWatchRouteName(route) {
664
574
  return route.replace(/[^a-zA-Z0-9_\-/.#]/g, "");
665
575
  }
@@ -13,7 +13,7 @@ export interface RDSProps {
13
13
  /**
14
14
  * Database engine of the cluster. Cannot be changed once set.
15
15
  */
16
- engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
16
+ engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.12" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
17
17
  /**
18
18
  * Name of a database which is automatically created inside the cluster.
19
19
  */
@@ -191,7 +191,7 @@ export declare class RDS extends Construct implements SSTConstruct {
191
191
  getConstructMetadata(): {
192
192
  type: "RDS";
193
193
  data: {
194
- engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
194
+ engine: "mysql5.6" | "mysql5.7" | "mysql8.0" | "postgresql11.13" | "postgresql11.16" | "postgresql13.12" | "postgresql13.9" | "postgresql14.10" | "postgresql15.5" | "postgresql16.1";
195
195
  secretArn: string;
196
196
  types: RDSTypes | undefined;
197
197
  clusterArn: string;
package/constructs/RDS.js CHANGED
@@ -219,6 +219,11 @@ export class RDS extends Construct {
219
219
  version: AuroraPostgresEngineVersion.VER_11_16,
220
220
  });
221
221
  }
222
+ else if (engine === "postgresql13.12") {
223
+ return DatabaseClusterEngine.auroraPostgres({
224
+ version: AuroraPostgresEngineVersion.VER_13_12,
225
+ });
226
+ }
222
227
  else if (engine === "postgresql13.9") {
223
228
  return DatabaseClusterEngine.auroraPostgres({
224
229
  version: AuroraPostgresEngineVersion.VER_13_9,
@@ -239,7 +244,7 @@ export class RDS extends Construct {
239
244
  version: AuroraPostgresEngineVersion.VER_16_1,
240
245
  });
241
246
  }
242
- throw new Error(`The specified "engine" is not supported for sst.RDS. Only mysql5.6, mysql5.7, postgresql11.13, postgresql11.16, and postgres13.9 engines are currently supported.`);
247
+ throw new Error(`The specified "engine" is not supported for sst.RDS. Only mysql5.6, mysql5.7, postgresql11.13, postgresql11.16, postgresql13.12, and postgresql13.9 engines are currently supported.`);
243
248
  }
244
249
  getScaling(scaling) {
245
250
  return {
@@ -90,6 +90,9 @@ export declare class RemixSite extends SsrSite {
90
90
  allowedHeaders?: string[] | undefined;
91
91
  } | undefined;
92
92
  buildId?: string | undefined;
93
+ warmer?: {
94
+ function: string;
95
+ } | undefined;
93
96
  };
94
97
  private hasViteConfig;
95
98
  private getServerModuleFormat;
@@ -659,8 +659,9 @@ export class Service extends Construct {
659
659
  `type=${build?.cacheTo.type}`,
660
660
  ...(build?.cacheTo?.params
661
661
  ? Object.entries(build?.cacheTo?.params).map(([pk, pv]) => `${pk}=${pv}`)
662
- : []).join(","),
663
- ],
662
+ : []),
663
+ ].join(","),
664
+ ,
664
665
  ]
665
666
  : []),
666
667
  this.props.path,