sst 2.1.21 → 2.1.23

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.
@@ -9,6 +9,7 @@ import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, } from "aws-c
9
9
  import { ToolkitInfo } from "aws-cdk/lib/api/toolkit-info.js";
10
10
  import { CloudFormationStack, } from "aws-cdk/lib/api/util/cloudformation.js";
11
11
  import { replaceEnvPlaceholders } from "aws-cdk/lib/api/util/placeholders.js";
12
+ import { callWithRetry } from "./util.js";
12
13
  /**
13
14
  * Try to use the bootstrap lookupRole. There are two scenarios that are handled here
14
15
  * 1. The lookup role may not exist (it was added in bootstrap stack version 7)
@@ -119,7 +120,7 @@ export class CloudFormationDeployments {
119
120
  };
120
121
  }
121
122
  const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(options.stack, options.roleArn);
122
- const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName);
123
+ const toolkitInfo = await callWithRetry(() => ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName));
123
124
  // Publish any assets before doing the actual deploy (do not publish any assets on import operation)
124
125
  if (options.resourcesToImport === undefined) {
125
126
  await this.publishStackAssets(options.stack, toolkitInfo, {
@@ -11,6 +11,7 @@ import { CfnEvaluationException } from "aws-cdk/lib/api/evaluate-cloudformation-
11
11
  import { tryHotswapDeployment } from "aws-cdk/lib/api/hotswap-deployments.js";
12
12
  import { changeSetHasNoChanges, CloudFormationStack, TemplateParameters, waitForChangeSet, waitForStackDeploy, waitForStackDelete, } from "aws-cdk/lib/api/util/cloudformation.js";
13
13
  import { blue } from "colorette";
14
+ import { callWithRetry } from "./util.js";
14
15
  const LARGE_TEMPLATE_SIZE_KB = 50;
15
16
  export async function deployStack(options) {
16
17
  const stackArtifact = options.stack;
@@ -18,7 +19,7 @@ export async function deployStack(options) {
18
19
  options.sdk.appendCustomUserAgent(options.extraUserAgent);
19
20
  const cfn = options.sdk.cloudFormation();
20
21
  const deployName = options.deployName || stackArtifact.stackName;
21
- let cloudFormationStack = await CloudFormationStack.lookup(cfn, deployName);
22
+ let cloudFormationStack = await callWithRetry(() => CloudFormationStack.lookup(cfn, deployName));
22
23
  if (cloudFormationStack.stackStatus.isCreationFailure) {
23
24
  debug(`Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`);
24
25
  await cfn.deleteStack({ StackName: deployName }).promise();
package/cdk/util.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function callWithRetry<T>(cb: () => Promise<T>): Promise<T>;
package/cdk/util.js ADDED
@@ -0,0 +1,17 @@
1
+ export async function callWithRetry(cb) {
2
+ try {
3
+ return await cb();
4
+ }
5
+ catch (e) {
6
+ if ((e.code === "ThrottlingException" && e.message === "Rate exceeded") ||
7
+ (e.code === "Throttling" && e.message === "Rate exceeded") ||
8
+ (e.code === "TooManyRequestsException" &&
9
+ e.message === "Too Many Requests") ||
10
+ e.code === "OperationAbortedException" ||
11
+ e.code === "TimeoutError" ||
12
+ e.code === "NetworkingError") {
13
+ return await callWithRetry(cb);
14
+ }
15
+ throw e;
16
+ }
17
+ }
@@ -13,3 +13,11 @@ export declare const dev: (program: Program) => import("yargs").Argv<{
13
13
  } & {
14
14
  "increase-timeout": boolean | undefined;
15
15
  }>;
16
+ declare module "../../bus.js" {
17
+ interface Events {
18
+ "cli.dev": {
19
+ app: string;
20
+ stage: string;
21
+ };
22
+ }
23
+ }
@@ -29,6 +29,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
29
29
  const { useRDSWarmer } = await import("./plugins/warmer.js");
30
30
  const { useProject } = await import("../../project.js");
31
31
  const { useMetadata } = await import("../../stacks/metadata.js");
32
+ const { useIOT } = await import("../../iot.js");
32
33
  const { clear } = await import("../terminal.js");
33
34
  if (args._[0] === "start") {
34
35
  console.log(yellow(`Warning: ${bold(`sst start`)} has been renamed to ${bold(`sst dev`)}`));
@@ -223,6 +224,27 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
223
224
  });
224
225
  await build();
225
226
  });
227
+ const useDisconnector = Context.memo(async () => {
228
+ const bus = useBus();
229
+ const project = useProject();
230
+ const iot = await useIOT();
231
+ bus.subscribe("cli.dev", async (evt) => {
232
+ const topic = `${iot.prefix}/events`;
233
+ iot.publish(topic, "cli.dev", evt.properties);
234
+ });
235
+ bus.publish("cli.dev", {
236
+ stage: project.config.stage,
237
+ app: project.config.name,
238
+ });
239
+ bus.subscribe("cli.dev", async (evt) => {
240
+ if (evt.properties.stage !== project.config.stage)
241
+ return;
242
+ if (evt.properties.app !== project.config.name)
243
+ return;
244
+ Colors.line(Colors.danger(`➜ `), "Another sst dev session has been started up for this stage. Exiting");
245
+ process.exit(0);
246
+ });
247
+ });
226
248
  const [appMetadata] = await Promise.all([
227
249
  useAppMetadata(),
228
250
  useLocalServer({
@@ -240,6 +262,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
240
262
  clear();
241
263
  await printHeader({ console: true, hint: "ready!" });
242
264
  await Promise.all([
265
+ useDisconnector(),
243
266
  useRuntimeWorkers(),
244
267
  useIOTBridge(),
245
268
  useRuntimeServer(),
@@ -19,12 +19,7 @@ const SessionMemo = /* @__PURE__ */ Context.memo(() => {
19
19
  })(token);
20
20
  return jwt;
21
21
  }
22
- catch {
23
- return {
24
- type: "public",
25
- properties: {},
26
- };
27
- }
22
+ catch { }
28
23
  }
29
24
  return {
30
25
  type: "public",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sst",
3
- "version": "2.1.21",
3
+ "version": "2.1.23",
4
4
  "bin": {
5
5
  "sst": "cli/sst.js"
6
6
  },
@@ -156,6 +156,13 @@ export const useNodeHandler = Context.memo(async () => {
156
156
  .filter((pkg) => !external?.includes(pkg))
157
157
  .filter((pkg) => Object.values(result.metafile?.inputs || {}).some(({ imports }) => imports.some(({ path }) => path === pkg))),
158
158
  ];
159
+ // TODO bubble up the warnings
160
+ const warnings = [];
161
+ Object.entries(result.metafile?.inputs || {}).forEach(([inputPath, { imports }]) => imports
162
+ .filter(({ path }) => path.includes("sst/constructs"))
163
+ .forEach(({ path }) => {
164
+ warnings.push(`You are importing from "${path}" in "${inputPath}". Did you mean to import from "sst/node"?`);
165
+ }));
159
166
  async function find(dir, target) {
160
167
  if (dir === "/")
161
168
  throw new VisibleError("Could not find a package.json file");
package/sst.mjs CHANGED
@@ -2603,6 +2603,11 @@ var init_workers = __esm({
2603
2603
  });
2604
2604
 
2605
2605
  // src/iot.ts
2606
+ var iot_exports = {};
2607
+ __export(iot_exports, {
2608
+ useIOT: () => useIOT,
2609
+ useIOTEndpoint: () => useIOTEndpoint
2610
+ });
2606
2611
  import { IoTClient, DescribeEndpointCommand } from "@aws-sdk/client-iot";
2607
2612
  import iot from "aws-iot-device-sdk";
2608
2613
  function encode(input) {
@@ -2721,8 +2726,8 @@ var init_iot = __esm({
2721
2726
  });
2722
2727
 
2723
2728
  // src/runtime/iot.ts
2724
- var iot_exports = {};
2725
- __export(iot_exports, {
2729
+ var iot_exports2 = {};
2730
+ __export(iot_exports2, {
2726
2731
  useIOTBridge: () => useIOTBridge
2727
2732
  });
2728
2733
  var useIOTBridge;
@@ -3301,6 +3306,23 @@ var init_monitor = __esm({
3301
3306
  }
3302
3307
  });
3303
3308
 
3309
+ // src/cdk/util.ts
3310
+ async function callWithRetry(cb) {
3311
+ try {
3312
+ return await cb();
3313
+ } catch (e) {
3314
+ if (e.code === "ThrottlingException" && e.message === "Rate exceeded" || e.code === "Throttling" && e.message === "Rate exceeded" || e.code === "TooManyRequestsException" && e.message === "Too Many Requests" || e.code === "OperationAbortedException" || e.code === "TimeoutError" || e.code === "NetworkingError") {
3315
+ return await callWithRetry(cb);
3316
+ }
3317
+ throw e;
3318
+ }
3319
+ }
3320
+ var init_util = __esm({
3321
+ "src/cdk/util.ts"() {
3322
+ "use strict";
3323
+ }
3324
+ });
3325
+
3304
3326
  // src/cdk/deploy-stack.ts
3305
3327
  import * as cxapi from "@aws-cdk/cx-api";
3306
3328
  import fs8 from "fs/promises";
@@ -3328,7 +3350,9 @@ async function deployStack(options) {
3328
3350
  options.sdk.appendCustomUserAgent(options.extraUserAgent);
3329
3351
  const cfn = options.sdk.cloudFormation();
3330
3352
  const deployName = options.deployName || stackArtifact.stackName;
3331
- let cloudFormationStack = await CloudFormationStack.lookup(cfn, deployName);
3353
+ let cloudFormationStack = await callWithRetry(
3354
+ () => CloudFormationStack.lookup(cfn, deployName)
3355
+ );
3332
3356
  if (cloudFormationStack.stackStatus.isCreationFailure) {
3333
3357
  debug(
3334
3358
  `Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`
@@ -3601,6 +3625,7 @@ var LARGE_TEMPLATE_SIZE_KB, FullCloudFormationDeployment;
3601
3625
  var init_deploy_stack = __esm({
3602
3626
  "src/cdk/deploy-stack.ts"() {
3603
3627
  "use strict";
3628
+ init_util();
3604
3629
  LARGE_TEMPLATE_SIZE_KB = 50;
3605
3630
  FullCloudFormationDeployment = class {
3606
3631
  constructor(options, cloudFormationStack, stackArtifact, stackParams, bodyParameter) {
@@ -3891,6 +3916,7 @@ var init_cloudformation_deployments = __esm({
3891
3916
  "src/cdk/cloudformation-deployments.ts"() {
3892
3917
  "use strict";
3893
3918
  init_deploy_stack();
3919
+ init_util();
3894
3920
  CloudFormationDeployments = class {
3895
3921
  sdkProvider;
3896
3922
  constructor(props) {
@@ -3954,10 +3980,12 @@ var init_cloudformation_deployments = __esm({
3954
3980
  };
3955
3981
  }
3956
3982
  const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(options.stack, options.roleArn);
3957
- const toolkitInfo = await ToolkitInfo.lookup(
3958
- resolvedEnvironment,
3959
- stackSdk,
3960
- options.toolkitStackName
3983
+ const toolkitInfo = await callWithRetry(
3984
+ () => ToolkitInfo.lookup(
3985
+ resolvedEnvironment,
3986
+ stackSdk,
3987
+ options.toolkitStackName
3988
+ )
3961
3989
  );
3962
3990
  if (options.resourcesToImport === void 0) {
3963
3991
  await this.publishStackAssets(options.stack, toolkitInfo, {
@@ -5027,6 +5055,14 @@ var init_node = __esm({
5027
5055
  )
5028
5056
  )
5029
5057
  ];
5058
+ const warnings = [];
5059
+ Object.entries(result.metafile?.inputs || {}).forEach(
5060
+ ([inputPath, { imports }]) => imports.filter(({ path: path20 }) => path20.includes("sst/constructs")).forEach(({ path: path20 }) => {
5061
+ warnings.push(
5062
+ `You are importing from "${path20}" in "${inputPath}". Did you mean to import from "sst/node"?`
5063
+ );
5064
+ })
5065
+ );
5030
5066
  async function find2(dir, target2) {
5031
5067
  if (dir === "/")
5032
5068
  throw new VisibleError("Could not find a package.json file");
@@ -6760,7 +6796,7 @@ var dev = (program2) => program2.command(
6760
6796
  const { mapValues, omitBy: omitBy2, pipe: pipe3 } = await import("remeda");
6761
6797
  const path20 = await import("path");
6762
6798
  const { useRuntimeWorkers: useRuntimeWorkers2 } = await Promise.resolve().then(() => (init_workers(), workers_exports));
6763
- const { useIOTBridge: useIOTBridge2 } = await Promise.resolve().then(() => (init_iot2(), iot_exports));
6799
+ const { useIOTBridge: useIOTBridge2 } = await Promise.resolve().then(() => (init_iot2(), iot_exports2));
6764
6800
  const { useRuntimeServer: useRuntimeServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
6765
6801
  const { useBus: useBus2 } = await Promise.resolve().then(() => (init_bus(), bus_exports));
6766
6802
  const { useWatcher: useWatcher2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
@@ -6782,6 +6818,7 @@ var dev = (program2) => program2.command(
6782
6818
  const { useRDSWarmer: useRDSWarmer2 } = await Promise.resolve().then(() => (init_warmer(), warmer_exports));
6783
6819
  const { useProject: useProject2 } = await Promise.resolve().then(() => (init_project(), project_exports));
6784
6820
  const { useMetadata: useMetadata2 } = await Promise.resolve().then(() => (init_metadata(), metadata_exports));
6821
+ const { useIOT: useIOT2 } = await Promise.resolve().then(() => (init_iot(), iot_exports));
6785
6822
  const { clear: clear2 } = await Promise.resolve().then(() => (init_terminal(), terminal_exports));
6786
6823
  if (args._[0] === "start") {
6787
6824
  console.log(
@@ -7003,6 +7040,30 @@ var dev = (program2) => program2.command(
7003
7040
  });
7004
7041
  await build2();
7005
7042
  });
7043
+ const useDisconnector = Context2.memo(async () => {
7044
+ const bus = useBus2();
7045
+ const project = useProject2();
7046
+ const iot2 = await useIOT2();
7047
+ bus.subscribe("cli.dev", async (evt) => {
7048
+ const topic = `${iot2.prefix}/events`;
7049
+ iot2.publish(topic, "cli.dev", evt.properties);
7050
+ });
7051
+ bus.publish("cli.dev", {
7052
+ stage: project.config.stage,
7053
+ app: project.config.name
7054
+ });
7055
+ bus.subscribe("cli.dev", async (evt) => {
7056
+ if (evt.properties.stage !== project.config.stage)
7057
+ return;
7058
+ if (evt.properties.app !== project.config.name)
7059
+ return;
7060
+ Colors2.line(
7061
+ Colors2.danger(`\u279C `),
7062
+ "Another sst dev session has been started up for this stage. Exiting"
7063
+ );
7064
+ process.exit(0);
7065
+ });
7066
+ });
7006
7067
  const [appMetadata] = await Promise.all([
7007
7068
  useAppMetadata2(),
7008
7069
  useLocalServer2({
@@ -7019,6 +7080,7 @@ var dev = (program2) => program2.command(
7019
7080
  clear2();
7020
7081
  await printHeader2({ console: true, hint: "ready!" });
7021
7082
  await Promise.all([
7083
+ useDisconnector(),
7022
7084
  useRuntimeWorkers2(),
7023
7085
  useIOTBridge2(),
7024
7086
  useRuntimeServer2(),
@@ -30082,7 +30082,7 @@ var require_StandardRetryStrategy = __commonJS({
30082
30082
  var config_1 = require_config2();
30083
30083
  var constants_1 = require_constants3();
30084
30084
  var defaultRetryToken_1 = require_defaultRetryToken();
30085
- var StandardRetryStrategy = class {
30085
+ var StandardRetryStrategy2 = class {
30086
30086
  constructor(maxAttemptsProvider) {
30087
30087
  this.maxAttemptsProvider = maxAttemptsProvider;
30088
30088
  this.mode = config_1.RETRY_MODES.STANDARD;
@@ -30120,8 +30120,8 @@ var require_StandardRetryStrategy = __commonJS({
30120
30120
  return errorType === "THROTTLING" || errorType === "TRANSIENT";
30121
30121
  }
30122
30122
  };
30123
- __name(StandardRetryStrategy, "StandardRetryStrategy");
30124
- exports.StandardRetryStrategy = StandardRetryStrategy;
30123
+ __name(StandardRetryStrategy2, "StandardRetryStrategy");
30124
+ exports.StandardRetryStrategy = StandardRetryStrategy2;
30125
30125
  }
30126
30126
  });
30127
30127
 
@@ -30283,7 +30283,7 @@ var require_StandardRetryStrategy2 = __commonJS({
30283
30283
  var delayDecider_1 = require_delayDecider();
30284
30284
  var retryDecider_1 = require_retryDecider();
30285
30285
  var util_1 = require_util();
30286
- var StandardRetryStrategy = class {
30286
+ var StandardRetryStrategy2 = class {
30287
30287
  constructor(maxAttemptsProvider, options) {
30288
30288
  var _a, _b, _c;
30289
30289
  this.maxAttemptsProvider = maxAttemptsProvider;
@@ -30351,8 +30351,8 @@ var require_StandardRetryStrategy2 = __commonJS({
30351
30351
  }
30352
30352
  }
30353
30353
  };
30354
- __name(StandardRetryStrategy, "StandardRetryStrategy");
30355
- exports.StandardRetryStrategy = StandardRetryStrategy;
30354
+ __name(StandardRetryStrategy2, "StandardRetryStrategy");
30355
+ exports.StandardRetryStrategy = StandardRetryStrategy2;
30356
30356
  var getDelayFromRetryAfterHeader = /* @__PURE__ */ __name((response) => {
30357
30357
  if (!protocol_http_1.HttpResponse.isInstance(response))
30358
30358
  return;
@@ -103057,7 +103057,7 @@ var require_StandardRetryStrategy3 = __commonJS({
103057
103057
  var defaultRetryQuota_1 = require_defaultRetryQuota2();
103058
103058
  var delayDecider_1 = require_delayDecider2();
103059
103059
  var retryDecider_1 = require_retryDecider2();
103060
- var StandardRetryStrategy = class {
103060
+ var StandardRetryStrategy2 = class {
103061
103061
  constructor(maxAttemptsProvider, options) {
103062
103062
  var _a, _b, _c;
103063
103063
  this.maxAttemptsProvider = maxAttemptsProvider;
@@ -103125,8 +103125,8 @@ var require_StandardRetryStrategy3 = __commonJS({
103125
103125
  }
103126
103126
  }
103127
103127
  };
103128
- __name(StandardRetryStrategy, "StandardRetryStrategy");
103129
- exports.StandardRetryStrategy = StandardRetryStrategy;
103128
+ __name(StandardRetryStrategy2, "StandardRetryStrategy");
103129
+ exports.StandardRetryStrategy = StandardRetryStrategy2;
103130
103130
  var getDelayFromRetryAfterHeader = /* @__PURE__ */ __name((response) => {
103131
103131
  if (!protocol_http_1.HttpResponse.isInstance(response))
103132
103132
  return;
@@ -164635,7 +164635,22 @@ __name(wait, "wait");
164635
164635
  // support/custom-resources/apigateway-cloudwatch-role.ts
164636
164636
  var import_client_api_gateway = __toESM(require_dist_cjs111(), 1);
164637
164637
  var import_client_iam = __toESM(require_dist_cjs112(), 1);
164638
- var apig = new import_client_api_gateway.APIGatewayClient({ logger: console });
164638
+ var import_middleware_retry = __toESM(require_dist_cjs17(), 1);
164639
+ var apig = new import_client_api_gateway.APIGatewayClient({
164640
+ logger: console,
164641
+ retryStrategy: new import_middleware_retry.StandardRetryStrategy(async () => 1e4, {
164642
+ retryDecider: (e) => {
164643
+ if (e.name === "TooManyRequestsException" && e.message === "Too Many Requests") {
164644
+ console.log("Retry on error", e.name);
164645
+ return true;
164646
+ }
164647
+ return false;
164648
+ },
164649
+ delayDecider: (_, attempts) => {
164650
+ return Math.min(1.5 ** attempts * 100, 3e3);
164651
+ }
164652
+ })
164653
+ });
164639
164654
  var iam = new import_client_iam.IAMClient({ logger: console });
164640
164655
  async function ApiGatewayCloudWatchRole(cfnRequest) {
164641
164656
  switch (cfnRequest.RequestType) {
@@ -164702,17 +164717,28 @@ async function createRole(roleName) {
164702
164717
  __name(createRole, "createRole");
164703
164718
  async function attachRoleToApiGateway(roleArn) {
164704
164719
  console.log("attachRoleToApiGateway");
164705
- await apig.send(
164706
- new import_client_api_gateway.UpdateAccountCommand({
164707
- patchOperations: [
164708
- {
164709
- op: "replace",
164710
- path: "/cloudwatchRoleArn",
164711
- value: roleArn
164712
- }
164713
- ]
164714
- })
164715
- );
164720
+ try {
164721
+ await apig.send(
164722
+ new import_client_api_gateway.UpdateAccountCommand({
164723
+ patchOperations: [
164724
+ {
164725
+ op: "replace",
164726
+ path: "/cloudwatchRoleArn",
164727
+ value: roleArn
164728
+ }
164729
+ ]
164730
+ })
164731
+ );
164732
+ } catch (e) {
164733
+ console.log(e);
164734
+ if (e.name === "BadRequestException" && e.message === "The role ARN does not have required permissions configured. Please grant trust permission for API Gateway and add the required role policy.") {
164735
+ console.log("Retry after 1 second");
164736
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
164737
+ await attachRoleToApiGateway(roleArn);
164738
+ return;
164739
+ }
164740
+ throw e;
164741
+ }
164716
164742
  }
164717
164743
  __name(attachRoleToApiGateway, "attachRoleToApiGateway");
164718
164744