sst 2.29.0 → 2.30.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.
@@ -2,8 +2,8 @@ import * as cxapi from "@aws-cdk/cx-api";
2
2
  import { Tag } from "sst-aws-cdk/lib/cdk-toolkit.js";
3
3
  import { AssetManifestBuilder } from "sst-aws-cdk/lib/util/asset-manifest-builder.js";
4
4
  import { ISDK, SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/index.js";
5
+ import { EnvironmentResources } from "sst-aws-cdk/lib/api/environment-resources.js";
5
6
  import { HotswapMode } from "sst-aws-cdk/lib/api/hotswap/common.js";
6
- import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
7
7
  import { ResourcesToImport } from "sst-aws-cdk/lib/api/util/cloudformation.js";
8
8
  import { StackActivityProgress } from "sst-aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
9
9
  type TemplateBodyParameter = {
@@ -56,7 +56,7 @@ export interface DeployStackOptions {
56
56
  /**
57
57
  * Information about the bootstrap stack found in the target environment
58
58
  */
59
- readonly toolkitInfo: ToolkitInfo;
59
+ readonly envResources: EnvironmentResources;
60
60
  /**
61
61
  * Role to pass to CloudFormation to execute the change set
62
62
  *
@@ -200,14 +200,14 @@ export declare function deployStack(options: DeployStackOptions): Promise<Deploy
200
200
  * @param stack the synthesized stack that provides the CloudFormation template
201
201
  * @param toolkitInfo information about the toolkit stack
202
202
  */
203
- export declare function makeBodyParameter(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, assetManifest: AssetManifestBuilder, toolkitInfo: ToolkitInfo, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
203
+ export declare function makeBodyParameter(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, assetManifest: AssetManifestBuilder, resources: EnvironmentResources, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
204
204
  /**
205
205
  * Prepare a body parameter for CFN, performing the upload
206
206
  *
207
207
  * Return it as-is if it is small enough to pass in the API call,
208
208
  * upload to S3 and return the coordinates if it is not.
209
209
  */
210
- export declare function makeBodyParameterAndUpload(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, toolkitInfo: ToolkitInfo, sdkProvider: SdkProvider, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
210
+ export declare function makeBodyParameterAndUpload(stack: cxapi.CloudFormationStackArtifact, resolvedEnvironment: cxapi.Environment, resources: EnvironmentResources, sdkProvider: SdkProvider, sdk: ISDK, overrideTemplate?: any): Promise<TemplateBodyParameter>;
211
211
  export interface DestroyStackOptions {
212
212
  /**
213
213
  * The stack to be destroyed
@@ -37,7 +37,7 @@ export async function deployStack(options) {
37
37
  // an ad-hoc asset manifest, while passing their locations via template
38
38
  // parameters.
39
39
  const legacyAssets = new AssetManifestBuilder();
40
- const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.toolkitInfo, options.reuseAssets);
40
+ const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.envResources, options.reuseAssets);
41
41
  const finalParameterValues = { ...options.parameters, ...assetParams };
42
42
  const templateParams = TemplateParameters.fromTemplate(stackArtifact.template);
43
43
  const stackParams = options.usePreviousParameters
@@ -58,7 +58,7 @@ export async function deployStack(options) {
58
58
  else {
59
59
  debug(`${deployName}: deploying...`);
60
60
  }
61
- const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.toolkitInfo, options.sdk, options.overrideTemplate);
61
+ const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.envResources, options.sdk, options.overrideTemplate);
62
62
  await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, {
63
63
  parallel: options.assetParallelism,
64
64
  });
@@ -352,7 +352,7 @@ class FullCloudFormationDeployment {
352
352
  * @param stack the synthesized stack that provides the CloudFormation template
353
353
  * @param toolkitInfo information about the toolkit stack
354
354
  */
355
- export async function makeBodyParameter(stack, resolvedEnvironment, assetManifest, toolkitInfo, sdk, overrideTemplate) {
355
+ export async function makeBodyParameter(stack, resolvedEnvironment, assetManifest, resources, sdk, overrideTemplate) {
356
356
  // If the template has already been uploaded to S3, just use it from there.
357
357
  if (stack.stackTemplateAssetObjectUrl && !overrideTemplate) {
358
358
  return {
@@ -364,6 +364,7 @@ export async function makeBodyParameter(stack, resolvedEnvironment, assetManifes
364
364
  if (templateJson.length <= LARGE_TEMPLATE_SIZE_KB * 1024) {
365
365
  return { TemplateBody: templateJson };
366
366
  }
367
+ const toolkitInfo = await resources.lookupToolkit();
367
368
  if (!toolkitInfo.found) {
368
369
  error(`The template for stack "${stack.displayName}" is ${Math.round(templateJson.length / 1024)}KiB. ` +
369
370
  `Templates larger than ${LARGE_TEMPLATE_SIZE_KB}KiB must be uploaded to S3.\n` +
@@ -394,14 +395,14 @@ export async function makeBodyParameter(stack, resolvedEnvironment, assetManifes
394
395
  * Return it as-is if it is small enough to pass in the API call,
395
396
  * upload to S3 and return the coordinates if it is not.
396
397
  */
397
- export async function makeBodyParameterAndUpload(stack, resolvedEnvironment, toolkitInfo, sdkProvider, sdk, overrideTemplate) {
398
+ export async function makeBodyParameterAndUpload(stack, resolvedEnvironment, resources, sdkProvider, sdk, overrideTemplate) {
398
399
  // We don't have access to the actual asset manifest here, so pretend that the
399
400
  // stack doesn't have a pre-published URL.
400
401
  const forceUploadStack = Object.create(stack, {
401
402
  stackTemplateAssetObjectUrl: { value: undefined },
402
403
  });
403
404
  const builder = new AssetManifestBuilder();
404
- const bodyparam = await makeBodyParameter(forceUploadStack, resolvedEnvironment, builder, toolkitInfo, sdk, overrideTemplate);
405
+ const bodyparam = await makeBodyParameter(forceUploadStack, resolvedEnvironment, builder, resources, sdk, overrideTemplate);
405
406
  const manifest = builder.toManifest(stack.assembly.directory);
406
407
  await publishAssets(manifest, sdkProvider, resolvedEnvironment, {
407
408
  quiet: true,
@@ -3,7 +3,6 @@ import { AssetManifest } from "cdk-assets";
3
3
  import { debug } from "sst-aws-cdk/lib/logging.js";
4
4
  import { CloudFormationStack, TemplateParameters, waitForStackDelete, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
5
5
  import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
6
- import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
7
6
  import { addMetadataAssetsToManifest } from "sst-aws-cdk/lib/assets.js";
8
7
  import { publishAssets } from "sst-aws-cdk/lib/util/asset-publishing.js";
9
8
  import { AssetManifestBuilder } from "sst-aws-cdk/lib/util/asset-manifest-builder.js";
@@ -11,7 +10,7 @@ import { Deployments, } from "./deployments.js";
11
10
  import { makeBodyParameter } from "./deploy-stack.js";
12
11
  import { lazy } from "../util/lazy.js";
13
12
  export async function publishDeployAssets(sdkProvider, options) {
14
- const { deployment, toolkitInfo, stackSdk, resolvedEnvironment, cloudFormationRoleArn, } = await useDeployment().get(sdkProvider, options);
13
+ const { deployment, envResources, stackSdk, resolvedEnvironment, cloudFormationRoleArn, } = await useDeployment().get(sdkProvider, options);
15
14
  // TODO
16
15
  // old
17
16
  //await deployment.publishStackAssets(options.stack, toolkitInfo, {
@@ -44,7 +43,7 @@ export async function publishDeployAssets(sdkProvider, options) {
44
43
  sdkProvider,
45
44
  roleArn: cloudFormationRoleArn,
46
45
  reuseAssets: options.reuseAssets,
47
- toolkitInfo,
46
+ envResources,
48
47
  tags: options.tags,
49
48
  deploymentMethod: options.deploymentMethod,
50
49
  force: options.force,
@@ -67,13 +66,12 @@ const useDeployment = lazy(() => {
67
66
  const region = options.stack.environment.region;
68
67
  if (!state.has(region)) {
69
68
  const deployment = new Deployments({ sdkProvider });
70
- const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await deployment.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
71
- const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName);
69
+ const { stackSdk, resolvedEnvironment, cloudFormationRoleArn, envResources, } = await deployment.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
72
70
  // Do a verification of the bootstrap stack version
73
- await deployment.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, toolkitInfo);
71
+ await deployment.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, envResources);
74
72
  state.set(region, {
75
73
  deployment,
76
- toolkitInfo,
74
+ envResources,
77
75
  stackSdk,
78
76
  resolvedEnvironment,
79
77
  cloudFormationRoleArn,
@@ -106,13 +104,13 @@ async function deployStack(options) {
106
104
  // an ad-hoc asset manifest, while passing their locations via template
107
105
  // parameters.
108
106
  const legacyAssets = new AssetManifestBuilder();
109
- const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.toolkitInfo, options.reuseAssets);
107
+ const assetParams = await addMetadataAssetsToManifest(stackArtifact, legacyAssets, options.envResources, options.reuseAssets);
110
108
  const finalParameterValues = { ...options.parameters, ...assetParams };
111
109
  const templateParams = TemplateParameters.fromTemplate(stackArtifact.template);
112
110
  const stackParams = options.usePreviousParameters
113
111
  ? templateParams.updateExisting(finalParameterValues, cloudFormationStack.parameters)
114
112
  : templateParams.supplyAll(finalParameterValues);
115
- const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.toolkitInfo, options.sdk, options.overrideTemplate);
113
+ const bodyParameter = await makeBodyParameter(stackArtifact, options.resolvedEnvironment, legacyAssets, options.envResources, options.sdk, options.overrideTemplate);
116
114
  await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, {
117
115
  parallel: options.assetParallelism,
118
116
  });
@@ -6,7 +6,7 @@ import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
6
6
  import { ISDK } from "sst-aws-cdk/lib/api/aws-auth/sdk.js";
7
7
  import { SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/sdk-provider.js";
8
8
  import { DeployStackResult, DeploymentMethod } from "./deploy-stack.js";
9
- import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
9
+ import { EnvironmentResources } from "sst-aws-cdk/lib/api/environment-resources.js";
10
10
  import { Template, ResourcesToImport, ResourceIdentifierSummaries } from "sst-aws-cdk/lib/api/util/cloudformation.js";
11
11
  import { StackActivityProgress } from "sst-aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
12
12
  import { HotswapMode } from "sst-aws-cdk/lib/api/hotswap/common.js";
@@ -31,6 +31,10 @@ export interface PreparedSdkWithLookupRoleForEnvironment {
31
31
  * the default credentials (not the assume role credentials)
32
32
  */
33
33
  readonly didAssumeRole: boolean;
34
+ /**
35
+ * An object for accessing the bootstrap resources in this environment
36
+ */
37
+ readonly envResources: EnvironmentResources;
34
38
  }
35
39
  export interface DeployStackOptions {
36
40
  /**
@@ -212,6 +216,7 @@ export interface StackExistsOptions {
212
216
  }
213
217
  export interface DeploymentsProps {
214
218
  sdkProvider: SdkProvider;
219
+ readonly toolkitStackName?: string;
215
220
  readonly quiet?: boolean;
216
221
  }
217
222
  /**
@@ -234,6 +239,10 @@ export interface PreparedSdkForEnvironment {
234
239
  * @default - no execution role is used
235
240
  */
236
241
  readonly cloudFormationRoleArn?: string;
242
+ /**
243
+ * Access class for environmental resources to help the deployment
244
+ */
245
+ readonly envResources: EnvironmentResources;
237
246
  }
238
247
  /**
239
248
  * Scope for a single set of deployments from a set of Cloud Assembly Artifacts
@@ -243,13 +252,13 @@ export interface PreparedSdkForEnvironment {
243
252
  export declare class Deployments {
244
253
  private readonly props;
245
254
  private readonly sdkProvider;
246
- private readonly toolkitInfoCache;
247
255
  private readonly sdkCache;
248
256
  private readonly publisherCache;
257
+ private readonly environmentResources;
249
258
  constructor(props: DeploymentsProps);
250
259
  readCurrentTemplateWithNestedStacks(rootStackArtifact: cxapi.CloudFormationStackArtifact, retrieveProcessedTemplate?: boolean): Promise<Template>;
251
260
  readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template>;
252
- resourceIdentifierSummaries(stackArtifact: cxapi.CloudFormationStackArtifact, toolkitStackName?: string): Promise<ResourceIdentifierSummaries>;
261
+ resourceIdentifierSummaries(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<ResourceIdentifierSummaries>;
253
262
  deployStack(options: DeployStackOptions): Promise<DeployStackResult | undefined>;
254
263
  destroyStack(options: DestroyStackOptions): Promise<void>;
255
264
  stackExists(options: StackExistsOptions): Promise<boolean>;
@@ -283,10 +292,6 @@ export declare class Deployments {
283
292
  * function can then decide to use them or fallback to another role.
284
293
  */
285
294
  prepareSdkWithLookupRoleFor(stack: cxapi.CloudFormationStackArtifact): Promise<PreparedSdkWithLookupRoleForEnvironment>;
286
- /**
287
- * Look up the toolkit for a given environment, using a given SDK
288
- */
289
- lookupToolkit(resolvedEnvironment: cxapi.Environment, sdk: ISDK, toolkitStackName?: string): Promise<ToolkitInfo>;
290
295
  private prepareAndValidateAssets;
291
296
  /**
292
297
  * Build all assets in a manifest
@@ -314,8 +319,10 @@ export declare class Deployments {
314
319
  isSingleAssetPublished(assetManifest: AssetManifest, asset: IManifestEntry, options: PublishStackAssetsOptions): Promise<boolean>;
315
320
  /**
316
321
  * Validate that the bootstrap stack has the right version for this stack
322
+ *
323
+ * Call into envResources.validateVersion, but prepend the stack name in case of failure.
317
324
  */
318
- validateBootstrapStackVersion(stackName: string, requiresBootstrapStackVersion: number | undefined, bootstrapStackVersionSsmParameter: string | undefined, toolkitInfo: ToolkitInfo): Promise<void>;
325
+ validateBootstrapStackVersion(stackName: string, requiresBootstrapStackVersion: number | undefined, bootstrapStackVersionSsmParameter: string | undefined, envResources: EnvironmentResources): Promise<void>;
319
326
  private cachedSdkForEnvironment;
320
327
  private cachedPublisher;
321
328
  }
@@ -6,11 +6,10 @@ import { debug, warning } from "sst-aws-cdk/lib/logging.js";
6
6
  import { buildAssets, publishAssets, PublishingAws, EVENT_TO_LOGGER, } from "sst-aws-cdk/lib/util/asset-publishing.js";
7
7
  import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
8
8
  import { deployStack, destroyStack, makeBodyParameterAndUpload, } from "./deploy-stack.js";
9
- import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
10
- import { ToolkitInfo } from "sst-aws-cdk/lib/api/toolkit-info.js";
9
+ import { EnvironmentResourcesRegistry, } from "sst-aws-cdk/lib/api/environment-resources.js";
10
+ import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, flattenNestedStackNames, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
11
11
  import { CloudFormationStack, } from "sst-aws-cdk/lib/api/util/cloudformation.js";
12
12
  import { replaceEnvPlaceholders } from "sst-aws-cdk/lib/api/util/placeholders.js";
13
- import { callWithRetry } from "./util.js";
14
13
  /**
15
14
  * Scope for a single set of deployments from a set of Cloud Assembly Artifacts
16
15
  *
@@ -19,17 +18,22 @@ import { callWithRetry } from "./util.js";
19
18
  export class Deployments {
20
19
  props;
21
20
  sdkProvider;
22
- toolkitInfoCache = new Map();
23
21
  sdkCache = new Map();
24
22
  publisherCache = new Map();
23
+ environmentResources;
25
24
  constructor(props) {
26
25
  this.props = props;
27
26
  this.sdkProvider = props.sdkProvider;
27
+ this.environmentResources = new EnvironmentResourcesRegistry(props.toolkitStackName);
28
28
  }
29
29
  async readCurrentTemplateWithNestedStacks(rootStackArtifact, retrieveProcessedTemplate = false) {
30
30
  const sdk = (await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact))
31
31
  .stackSdk;
32
- return (await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate)).deployedTemplate;
32
+ const templateWithNestedStacks = await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk, retrieveProcessedTemplate);
33
+ return {
34
+ deployedTemplate: templateWithNestedStacks.deployedTemplate,
35
+ nestedStackCount: flattenNestedStackNames(templateWithNestedStacks.nestedStackNames).length,
36
+ };
33
37
  }
34
38
  async readCurrentTemplate(stackArtifact) {
35
39
  debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
@@ -37,15 +41,14 @@ export class Deployments {
37
41
  .stackSdk;
38
42
  return loadCurrentTemplate(stackArtifact, sdk);
39
43
  }
40
- async resourceIdentifierSummaries(stackArtifact, toolkitStackName) {
44
+ async resourceIdentifierSummaries(stackArtifact) {
41
45
  debug(`Retrieving template summary for stack ${stackArtifact.displayName}.`);
42
46
  // Currently, needs to use `deploy-role` since it may need to read templates in the staging
43
47
  // bucket which have been encrypted with a KMS key (and lookup-role may not read encrypted things)
44
- const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
48
+ const { stackSdk, resolvedEnvironment, envResources } = await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading);
45
49
  const cfn = stackSdk.cloudFormation();
46
- const toolkitInfo = await this.lookupToolkit(resolvedEnvironment, stackSdk, toolkitStackName);
47
50
  // Upload the template, if necessary, before passing it to CFN
48
- const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment, toolkitInfo, this.sdkProvider, stackSdk);
51
+ const cfnParam = await makeBodyParameterAndUpload(stackArtifact, resolvedEnvironment, envResources, this.sdkProvider, stackSdk);
49
52
  const response = await cfn.getTemplateSummary(cfnParam).promise();
50
53
  if (!response.ResourceIdentifierSummaries) {
51
54
  debug('GetTemplateSummary API call did not return "ResourceIdentifierSummaries"');
@@ -64,10 +67,9 @@ export class Deployments {
64
67
  execute: options.execute,
65
68
  };
66
69
  }
67
- const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
68
- const toolkitInfo = await callWithRetry(() => this.lookupToolkit(resolvedEnvironment, stackSdk, options.toolkitStackName));
70
+ const { stackSdk, resolvedEnvironment, cloudFormationRoleArn, envResources, } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
69
71
  // Do a verification of the bootstrap stack version
70
- await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, toolkitInfo);
72
+ await this.validateBootstrapStackVersion(options.stack.stackName, options.stack.requiresBootstrapStackVersion, options.stack.bootstrapStackVersionSsmParameter, envResources);
71
73
  // Deploy assets
72
74
  const assetArtifacts = options.stack.dependencies.filter(cxapi.AssetManifestArtifact.isAssetManifestArtifact);
73
75
  for (const asset of assetArtifacts) {
@@ -89,7 +91,7 @@ export class Deployments {
89
91
  sdkProvider: this.sdkProvider,
90
92
  roleArn: cloudFormationRoleArn,
91
93
  reuseAssets: options.reuseAssets,
92
- toolkitInfo,
94
+ envResources,
93
95
  tags: options.tags,
94
96
  deploymentMethod,
95
97
  force: options.force,
@@ -129,6 +131,7 @@ export class Deployments {
129
131
  return {
130
132
  resolvedEnvironment: result.resolvedEnvironment,
131
133
  stackSdk: result.sdk,
134
+ envResources: result.envResources,
132
135
  };
133
136
  }
134
137
  }
@@ -164,6 +167,7 @@ export class Deployments {
164
167
  stackSdk: stackSdk.sdk,
165
168
  resolvedEnvironment,
166
169
  cloudFormationRoleArn: arns.cloudFormationRoleArn,
170
+ envResources: this.environmentResources.for(resolvedEnvironment, stackSdk.sdk),
167
171
  };
168
172
  }
169
173
  /**
@@ -198,11 +202,12 @@ export class Deployments {
198
202
  assumeRoleArn: arns.lookupRoleArn,
199
203
  assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId,
200
204
  });
205
+ const envResources = this.environmentResources.for(resolvedEnvironment, stackSdk.sdk);
201
206
  // if we succeed in assuming the lookup role, make sure we have the correct bootstrap stack version
202
207
  if (stackSdk.didAssumeRole &&
203
208
  stack.lookupRole?.bootstrapStackVersionSsmParameter &&
204
209
  stack.lookupRole.requiresBootstrapStackVersion) {
205
- const version = await ToolkitInfo.versionFromSsmParameter(stackSdk.sdk, stack.lookupRole.bootstrapStackVersionSsmParameter);
210
+ const version = await envResources.versionFromSsmParameter(stack.lookupRole.bootstrapStackVersionSsmParameter);
206
211
  if (version < stack.lookupRole.requiresBootstrapStackVersion) {
207
212
  throw new Error(`Bootstrap stack version '${stack.lookupRole.requiresBootstrapStackVersion}' is required, found version '${version}'.`);
208
213
  }
@@ -213,7 +218,7 @@ export class Deployments {
213
218
  stack.lookupRole?.requiresBootstrapStackVersion) {
214
219
  warning(upgradeMessage);
215
220
  }
216
- return { ...stackSdk, resolvedEnvironment };
221
+ return { ...stackSdk, resolvedEnvironment, envResources };
217
222
  }
218
223
  catch (e) {
219
224
  debug(e);
@@ -226,26 +231,11 @@ export class Deployments {
226
231
  throw e;
227
232
  }
228
233
  }
229
- /**
230
- * Look up the toolkit for a given environment, using a given SDK
231
- */
232
- async lookupToolkit(resolvedEnvironment, sdk, toolkitStackName) {
233
- const key = `${resolvedEnvironment.account}:${resolvedEnvironment.region}:${toolkitStackName}`;
234
- const existing = this.toolkitInfoCache.get(key);
235
- if (existing) {
236
- return existing;
237
- }
238
- const ret = await ToolkitInfo.lookup(resolvedEnvironment, sdk, toolkitStackName);
239
- this.toolkitInfoCache.set(key, ret);
240
- return ret;
241
- }
242
234
  async prepareAndValidateAssets(asset, options) {
243
- const { stackSdk, resolvedEnvironment } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
244
- const toolkitInfo = await this.lookupToolkit(resolvedEnvironment, stackSdk, options.toolkitStackName);
245
- const stackEnv = await this.sdkProvider.resolveEnvironment(options.stack.environment);
246
- await this.validateBootstrapStackVersion(options.stack.stackName, asset.requiresBootstrapStackVersion, asset.bootstrapStackVersionSsmParameter, toolkitInfo);
235
+ const { envResources } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
236
+ await this.validateBootstrapStackVersion(options.stack.stackName, asset.requiresBootstrapStackVersion, asset.bootstrapStackVersionSsmParameter, envResources);
247
237
  const manifest = AssetManifest.fromFile(asset.file);
248
- return { manifest, stackEnv };
238
+ return { manifest, stackEnv: envResources.environment };
249
239
  }
250
240
  /**
251
241
  * Build all assets in a manifest
@@ -270,10 +260,9 @@ export class Deployments {
270
260
  */
271
261
  // eslint-disable-next-line max-len
272
262
  async buildSingleAsset(assetArtifact, assetManifest, asset, options) {
273
- const { stackSdk, resolvedEnvironment: stackEnv } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
274
- const toolkitInfo = await this.lookupToolkit(stackEnv, stackSdk, options.toolkitStackName);
275
- await this.validateBootstrapStackVersion(options.stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, toolkitInfo);
276
- const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
263
+ const { resolvedEnvironment, envResources } = await this.prepareSdkFor(options.stack, options.roleArn, Mode.ForWriting);
264
+ await this.validateBootstrapStackVersion(options.stack.stackName, assetArtifact.requiresBootstrapStackVersion, assetArtifact.bootstrapStackVersionSsmParameter, envResources);
265
+ const publisher = this.cachedPublisher(assetManifest, resolvedEnvironment, options.stackName);
277
266
  await publisher.buildEntry(asset);
278
267
  }
279
268
  /**
@@ -296,13 +285,12 @@ export class Deployments {
296
285
  }
297
286
  /**
298
287
  * Validate that the bootstrap stack has the right version for this stack
288
+ *
289
+ * Call into envResources.validateVersion, but prepend the stack name in case of failure.
299
290
  */
300
- async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter, toolkitInfo) {
301
- if (requiresBootstrapStackVersion === undefined) {
302
- return;
303
- }
291
+ async validateBootstrapStackVersion(stackName, requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter, envResources) {
304
292
  try {
305
- await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
293
+ await envResources.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
306
294
  }
307
295
  catch (e) {
308
296
  throw new Error(`${stackName}: ${e.message}`);
@@ -81,13 +81,10 @@ export const bind = (program) => program
81
81
  });
82
82
  async function buildApp() {
83
83
  const [_metafile, sstConfig] = await Stacks.load(project.paths.config);
84
- const cwd = process.cwd();
85
- process.chdir(project.paths.root);
86
84
  await Stacks.synth({
87
85
  fn: sstConfig.stacks,
88
86
  mode: "remove",
89
87
  });
90
- process.chdir(cwd);
91
88
  }
92
89
  function isInSsrSite() {
93
90
  const cwd = process.cwd();
@@ -8,10 +8,20 @@ export const types = (program) => program.command("types", "Generate resource ty
8
8
  try {
9
9
  const project = useProject();
10
10
  const [_metafile, sstConfig] = await Stacks.load(project.paths.config);
11
- await Stacks.synth({
12
- fn: sstConfig.stacks,
11
+ // Note: do not run synth which requires AWS credentials. B/c generating
12
+ // types is usually done inside CI pipelines. And credentials
13
+ // might not be available. ie.
14
+ // await Stacks.synth({
15
+ // fn: sstConfig.stacks,
16
+ // mode: "remove",
17
+ // });
18
+ const app = new App({
13
19
  mode: "remove",
20
+ stage: project.config.stage,
21
+ name: project.config.name,
22
+ region: project.config.region,
14
23
  });
24
+ sstConfig.stacks(app);
15
25
  Colors.line(Colors.success(`✔ `), `Types generated in ${path.resolve(project.paths.out, "types")}`);
16
26
  await exit();
17
27
  }
package/cli/sst.js CHANGED
@@ -3,7 +3,6 @@ import { blue, red } from "colorette";
3
3
  import { program } from "./program.js";
4
4
  import { SilentError, VisibleError } from "../error.js";
5
5
  import { useSpinners } from "./spinner.js";
6
- import { Logger } from "../logger.js";
7
6
  import dotenv from "dotenv";
8
7
  dotenv.config({
9
8
  override: true,
@@ -48,7 +47,6 @@ if ("setSourceMapsEnabled" in process) {
48
47
  }
49
48
  process.removeAllListeners("uncaughtException");
50
49
  process.on("uncaughtException", (err) => {
51
- Logger.debug(err);
52
50
  const spinners = useSpinners();
53
51
  for (const spinner of spinners) {
54
52
  if (spinner.isSpinning)
@@ -1,9 +1,18 @@
1
1
  import { ErrorResponse, DistributionProps, BehaviorOptions, IOrigin } from "aws-cdk-lib/aws-cloudfront";
2
2
  export interface BaseSiteFileOptions {
3
- exclude: string | string[];
4
- include: string | string[];
5
- cacheControl: string;
3
+ filters: {
4
+ [key in "include" | "exclude"]?: string;
5
+ }[];
6
+ cacheControl?: string;
6
7
  contentType?: string;
8
+ contentEncoding?: string;
9
+ }
10
+ export interface BaseSiteFileOptionsDeprecated {
11
+ include?: string | string[];
12
+ exclude?: string | string[];
13
+ cacheControl?: string;
14
+ contentType?: string;
15
+ contentEncoding?: string;
7
16
  }
8
17
  export interface BaseSiteEnvironmentOutputsInfo {
9
18
  path: string;
@@ -132,7 +132,7 @@ export declare class Distribution extends Construct {
132
132
  hostedZone: IHostedZone | undefined;
133
133
  certificate: ICertificate | undefined;
134
134
  };
135
- createInvalidation(buildId?: string): CustomResource;
135
+ createInvalidation(buildId?: string, paths?: string[]): CustomResource;
136
136
  private validateCloudFrontDistributionSettings;
137
137
  private validateCustomDomainSettings;
138
138
  private lookupHostedZone;
@@ -70,7 +70,7 @@ export class Distribution extends Construct {
70
70
  certificate: this.certificate,
71
71
  };
72
72
  }
73
- createInvalidation(buildId) {
73
+ createInvalidation(buildId, paths) {
74
74
  const stack = Stack.of(this);
75
75
  const policy = new Policy(this.scope, "CloudFrontInvalidatorPolicy", {
76
76
  statements: [
@@ -93,7 +93,7 @@ export class Distribution extends Construct {
93
93
  properties: {
94
94
  buildId: buildId || Date.now().toString(),
95
95
  distributionId: this.distribution.distributionId,
96
- paths: ["/*"],
96
+ paths: paths ?? ["/*"],
97
97
  waitForInvalidation: this.props.waitForInvalidation,
98
98
  },
99
99
  });
@@ -3,6 +3,7 @@ import { Runtime, FunctionProps, Architecture } from "aws-cdk-lib/aws-lambda";
3
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
+ import { CachePolicyProps } from "aws-cdk-lib/aws-cloudfront";
6
7
  export interface NextjsSiteProps extends Omit<SsrSiteProps, "nodejs"> {
7
8
  imageOptimization?: {
8
9
  /**
@@ -101,6 +102,7 @@ export declare class NextjsSite extends SsrSite {
101
102
  props: NextjsSiteNormalizedProps;
102
103
  private buildId?;
103
104
  constructor(scope: Construct, id: string, props?: NextjsSiteProps);
105
+ static buildDefaultServerCachePolicyProps(): CachePolicyProps;
104
106
  protected plan(bucket: Bucket): {
105
107
  cloudFrontFunctions?: {
106
108
  serverCfFunction: {
@@ -12,6 +12,13 @@ import { toCdkSize } from "./util/size.js";
12
12
  import { PolicyStatement } from "aws-cdk-lib/aws-iam";
13
13
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
14
14
  import { VisibleError } from "../error.js";
15
+ const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
16
+ "accept",
17
+ "rsc",
18
+ "next-router-prefetch",
19
+ "next-router-state-tree",
20
+ "next-url",
21
+ ];
15
22
  /**
16
23
  * The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
17
24
  * @example
@@ -52,6 +59,9 @@ export class NextjsSite extends SsrSite {
52
59
  }
53
60
  }
54
61
  }
62
+ static buildDefaultServerCachePolicyProps() {
63
+ return super.buildDefaultServerCachePolicyProps(DEFAULT_CACHE_POLICY_ALLOWED_HEADERS);
64
+ }
55
65
  plan(bucket) {
56
66
  const { path: sitePath, edge, experimental, imageOptimization, } = this.props;
57
67
  const serverConfig = {
@@ -184,13 +194,7 @@ export class NextjsSite extends SsrSite {
184
194
  origin: "s3",
185
195
  })),
186
196
  ],
187
- cachePolicyAllowedHeaders: [
188
- "accept",
189
- "rsc",
190
- "next-router-prefetch",
191
- "next-router-state-tree",
192
- "next-url",
193
- ],
197
+ cachePolicyAllowedHeaders: DEFAULT_CACHE_POLICY_ALLOWED_HEADERS,
194
198
  buildId: this.getBuildId(),
195
199
  warmerConfig: {
196
200
  function: path.join(sitePath, ".open-next", "warmer-function"),
@@ -287,15 +291,22 @@ export class NextjsSite extends SsrSite {
287
291
  };
288
292
  }
289
293
  wrapHandler() {
290
- const { path: sitePath } = this.props;
294
+ const { path: sitePath, experimental } = this.props;
291
295
  const wrapperName = "nextjssite-index";
292
296
  const serverPath = path.join(sitePath, ".open-next", "server-function");
293
- fs.writeFileSync(path.join(serverPath, `${wrapperName}.mjs`), [
294
- `import { handler as rawHandler } from "./index.mjs";`,
295
- `export const handler = (event, context) => {`,
296
- ` return rawHandler(event, context);`,
297
- `};`,
298
- ].join("\n"));
297
+ fs.writeFileSync(path.join(serverPath, `${wrapperName}.mjs`), experimental?.streaming
298
+ ? [
299
+ `export const handler = awslambda.streamifyResponse(async (...args) => {`,
300
+ ` const { handler: rawHandler} = await import("./index.mjs");`,
301
+ ` return rawHandler(...args);`,
302
+ `});`,
303
+ ].join("\n")
304
+ : [
305
+ `export const handler = async (...args) => {`,
306
+ ` const { handler: rawHandler} = await import("./index.mjs");`,
307
+ ` return rawHandler(...args);`,
308
+ `};`,
309
+ ].join("\n"));
299
310
  return `${wrapperName}.handler`;
300
311
  }
301
312
  getRoutes() {
@@ -51,8 +51,7 @@ export interface ScriptProps {
51
51
  function?: FunctionProps;
52
52
  };
53
53
  /**
54
- * Creates the function that runs when the Script is created.
55
- *
54
+ * Specifies the function to be run once when the Script construct is created.
56
55
  * @example
57
56
  * ```js
58
57
  * new Script(stack, "Api", {
@@ -62,8 +61,11 @@ export interface ScriptProps {
62
61
  */
63
62
  onCreate?: FunctionDefinition;
64
63
  /**
65
- * Creates the function that runs on every deploy after the Script is created
64
+ * Specifies the function to be run each time the Script construct is redeployed. If a version is provided,
65
+ * the function is only executed when the version changes.
66
66
  *
67
+ * Note that the `onUpdate` function is not run during the initial creation of the Script construct.
68
+ * For initial creation, use `onCreate`.
67
69
  * @example
68
70
  * ```js
69
71
  * new Script(stack, "Api", {
@@ -73,8 +75,8 @@ export interface ScriptProps {
73
75
  */
74
76
  onUpdate?: FunctionDefinition;
75
77
  /**
76
- * Create the function that runs when the Script is deleted from the stack.
77
- *
78
+ * Specifies the function to be run once when the Script construct is deleted from the stack or
79
+ * when the entire stack is removed from the app.
78
80
  * @example
79
81
  * ```js
80
82
  * new Script(stack, "Api", {