@wraps.dev/cli 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -146,7 +146,7 @@ var require_package = __commonJS({
146
146
  "package.json"(exports, module) {
147
147
  module.exports = {
148
148
  name: "@wraps.dev/cli",
149
- version: "1.4.0",
149
+ version: "1.4.1",
150
150
  description: "CLI for deploying Wraps email infrastructure to your AWS account",
151
151
  type: "module",
152
152
  main: "./dist/cli.js",
@@ -546,6 +546,12 @@ To remove: wraps destroy --stack ${stackName}`,
546
546
  "PULUMI_NOT_INSTALLED",
547
547
  "Install Pulumi:\n macOS: brew install pulumi/tap/pulumi\n Linux: curl -fsSL https://get.pulumi.com | sh\n Windows: choco install pulumi\n\nOr download from: https://www.pulumi.com/docs/install/",
548
548
  "https://www.pulumi.com/docs/install/"
549
+ ),
550
+ stackLocked: () => new WrapsError(
551
+ "The Pulumi stack is locked from a previous run",
552
+ "STACK_LOCKED",
553
+ "This happens when a previous deployment was interrupted.\n\nTo unlock, run:\n rm -rf ~/.wraps/pulumi/.pulumi/locks\n\nThen try your command again.",
554
+ "https://wraps.dev/docs/troubleshooting"
549
555
  )
550
556
  };
551
557
  }
@@ -3792,7 +3798,7 @@ Try running: pnpm build`
3792
3798
  entryPoints: [sourcePath],
3793
3799
  bundle: true,
3794
3800
  platform: "node",
3795
- target: "node20",
3801
+ target: "node24",
3796
3802
  format: "esm",
3797
3803
  outfile: join3(outdir, "index.mjs"),
3798
3804
  external: ["@aws-sdk/*"],
@@ -3864,7 +3870,7 @@ async function deployLambdaFunctions(config2) {
3864
3870
  functionName,
3865
3871
  {
3866
3872
  name: functionName,
3867
- runtime: aws4.lambda.Runtime.NodeJS20dX,
3873
+ runtime: "nodejs24.x",
3868
3874
  handler: "index.handler",
3869
3875
  role: lambdaRole.arn,
3870
3876
  code: new pulumi3.asset.FileArchive(eventProcessorCode),
@@ -3888,7 +3894,7 @@ async function deployLambdaFunctions(config2) {
3888
3894
  }
3889
3895
  ) : new aws4.lambda.Function(functionName, {
3890
3896
  name: functionName,
3891
- runtime: aws4.lambda.Runtime.NodeJS20dX,
3897
+ runtime: "nodejs24.x",
3892
3898
  handler: "index.handler",
3893
3899
  role: lambdaRole.arn,
3894
3900
  code: new pulumi3.asset.FileArchive(eventProcessorCode),
@@ -3957,6 +3963,28 @@ async function configurationSetExists(configSetName, region) {
3957
3963
  return false;
3958
3964
  }
3959
3965
  }
3966
+ async function eventDestinationExists(configSetName, eventDestName, region) {
3967
+ try {
3968
+ const {
3969
+ SESv2Client: SESv2Client5,
3970
+ GetConfigurationSetEventDestinationsCommand
3971
+ } = await import("@aws-sdk/client-sesv2");
3972
+ const ses = new SESv2Client5({ region });
3973
+ const response = await ses.send(
3974
+ new GetConfigurationSetEventDestinationsCommand({
3975
+ ConfigurationSetName: configSetName
3976
+ })
3977
+ );
3978
+ return response.EventDestinations?.some(
3979
+ (dest) => dest.Name === eventDestName
3980
+ ) ?? false;
3981
+ } catch (error) {
3982
+ if (error.name === "NotFoundException") {
3983
+ return false;
3984
+ }
3985
+ return false;
3986
+ }
3987
+ }
3960
3988
  async function emailIdentityExists(emailIdentity, region) {
3961
3989
  try {
3962
3990
  const { SESv2Client: SESv2Client5, GetEmailIdentityCommand: GetEmailIdentityCommand4 } = await import("@aws-sdk/client-sesv2");
@@ -4010,29 +4038,37 @@ async function createSESResources(config2) {
4010
4038
  });
4011
4039
  if (config2.eventTrackingEnabled) {
4012
4040
  const eventDestName = "wraps-email-eventbridge";
4013
- new aws5.sesv2.ConfigurationSetEventDestination("wraps-email-all-events", {
4014
- configurationSetName: configSet.configurationSetName,
4015
- eventDestinationName: eventDestName,
4016
- eventDestination: {
4017
- enabled: true,
4018
- matchingEventTypes: [
4019
- "SEND",
4020
- "DELIVERY",
4021
- "OPEN",
4022
- "CLICK",
4023
- "BOUNCE",
4024
- "COMPLAINT",
4025
- "REJECT",
4026
- "RENDERING_FAILURE",
4027
- "DELIVERY_DELAY",
4028
- "SUBSCRIPTION"
4029
- ],
4030
- eventBridgeDestination: {
4031
- // SES requires default bus - cannot use custom bus
4032
- eventBusArn: defaultEventBus.arn
4041
+ new aws5.sesv2.ConfigurationSetEventDestination(
4042
+ "wraps-email-all-events",
4043
+ {
4044
+ configurationSetName: configSet.configurationSetName,
4045
+ eventDestinationName: eventDestName,
4046
+ eventDestination: {
4047
+ enabled: true,
4048
+ matchingEventTypes: [
4049
+ "SEND",
4050
+ "DELIVERY",
4051
+ "OPEN",
4052
+ "CLICK",
4053
+ "BOUNCE",
4054
+ "COMPLAINT",
4055
+ "REJECT",
4056
+ "RENDERING_FAILURE",
4057
+ "DELIVERY_DELAY",
4058
+ "SUBSCRIPTION"
4059
+ ],
4060
+ eventBridgeDestination: {
4061
+ // SES requires default bus - cannot use custom bus
4062
+ eventBusArn: defaultEventBus.arn
4063
+ }
4033
4064
  }
4065
+ },
4066
+ {
4067
+ // Import existing resource if it already exists in AWS
4068
+ // This prevents AlreadyExistsException when the resource exists but isn't in Pulumi state
4069
+ import: config2.importExistingEventDestination ? `wraps-email-tracking|${eventDestName}` : void 0
4034
4070
  }
4035
- });
4071
+ );
4036
4072
  }
4037
4073
  let domainIdentity;
4038
4074
  let dkimTokens;
@@ -4244,6 +4280,11 @@ async function deployEmailStack(config2) {
4244
4280
  }
4245
4281
  let sesResources;
4246
4282
  if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {
4283
+ const shouldImportEventDest = emailConfig.eventTracking?.enabled && await eventDestinationExists(
4284
+ "wraps-email-tracking",
4285
+ "wraps-email-eventbridge",
4286
+ config2.region
4287
+ );
4247
4288
  sesResources = await createSESResources({
4248
4289
  domain: emailConfig.domain,
4249
4290
  mailFromDomain: emailConfig.mailFromDomain,
@@ -4252,8 +4293,10 @@ async function deployEmailStack(config2) {
4252
4293
  eventTypes: emailConfig.eventTracking?.events,
4253
4294
  eventTrackingEnabled: emailConfig.eventTracking?.enabled,
4254
4295
  // Pass flag to create EventBridge destination
4255
- tlsRequired: emailConfig.tlsRequired
4296
+ tlsRequired: emailConfig.tlsRequired,
4256
4297
  // Require TLS encryption for all emails
4298
+ importExistingEventDestination: shouldImportEventDest
4299
+ // Import if exists to avoid AlreadyExistsException
4257
4300
  });
4258
4301
  }
4259
4302
  let dynamoTables;
@@ -4318,6 +4361,7 @@ async function deployEmailStack(config2) {
4318
4361
 
4319
4362
  // src/commands/email/config.ts
4320
4363
  init_aws();
4364
+ init_errors();
4321
4365
 
4322
4366
  // src/utils/shared/pulumi.ts
4323
4367
  init_esm_shims();
@@ -4484,6 +4528,8 @@ ${pc4.bold("Current Configuration:")}
4484
4528
  metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`
4485
4529
  );
4486
4530
  await stack.setConfig("aws:region", { value: region });
4531
+ await stack.refresh({ onOutput: () => {
4532
+ } });
4487
4533
  const upResult = await stack.up({ onOutput: () => {
4488
4534
  } });
4489
4535
  const pulumiOutputs = upResult.outputs;
@@ -4500,12 +4546,8 @@ ${pc4.bold("Current Configuration:")}
4500
4546
  }
4501
4547
  );
4502
4548
  } catch (error) {
4503
- clack3.log.error("Infrastructure update failed");
4504
4549
  if (error.message?.includes("stack is currently locked")) {
4505
- clack3.log.warn("\nThe Pulumi stack is locked from a previous run.");
4506
- clack3.log.info("To fix this, run:");
4507
- clack3.log.info(` ${pc4.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4508
- clack3.log.info("\nThen try running wraps update again.");
4550
+ throw errors.stackLocked();
4509
4551
  }
4510
4552
  throw new Error(`Pulumi update failed: ${error.message}`);
4511
4553
  }
@@ -4546,6 +4588,7 @@ import * as pulumi6 from "@pulumi/pulumi";
4546
4588
  import pc6 from "picocolors";
4547
4589
  init_presets();
4548
4590
  init_aws();
4591
+ init_errors();
4549
4592
  init_prompts();
4550
4593
 
4551
4594
  // src/utils/shared/scanner.ts
@@ -4920,12 +4963,8 @@ async function connect(options) {
4920
4963
  }
4921
4964
  );
4922
4965
  } catch (error) {
4923
- clack5.log.error("Infrastructure deployment failed");
4924
4966
  if (error.message?.includes("stack is currently locked")) {
4925
- clack5.log.warn("\nThe Pulumi stack is locked from a previous run.");
4926
- clack5.log.info("To fix this, run:");
4927
- clack5.log.info(` ${pc6.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4928
- clack5.log.info("\nThen try running wraps email connect again.");
4967
+ throw errors.stackLocked();
4929
4968
  }
4930
4969
  throw new Error(`Pulumi deployment failed: ${error.message}`);
4931
4970
  }
@@ -5423,6 +5462,7 @@ import pc8 from "picocolors";
5423
5462
  init_costs();
5424
5463
  init_presets();
5425
5464
  init_aws();
5465
+ init_errors();
5426
5466
  init_prompts();
5427
5467
  async function init(options) {
5428
5468
  clack7.intro(pc8.bold("Wraps Email Infrastructure Setup"));
@@ -5584,12 +5624,8 @@ ${pc8.yellow(pc8.bold("Configuration Warnings:"))}`);
5584
5624
  }
5585
5625
  );
5586
5626
  } catch (error) {
5587
- clack7.log.error("Infrastructure deployment failed");
5588
5627
  if (error.message?.includes("stack is currently locked")) {
5589
- clack7.log.warn("\nThe Pulumi stack is locked from a previous run.");
5590
- clack7.log.info("To fix this, run:");
5591
- clack7.log.info(` ${pc8.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
5592
- clack7.log.info("\nThen try running wraps email init again.");
5628
+ throw errors.stackLocked();
5593
5629
  }
5594
5630
  throw new Error(`Pulumi deployment failed: ${error.message}`);
5595
5631
  }
@@ -5762,6 +5798,7 @@ import pc10 from "picocolors";
5762
5798
  init_costs();
5763
5799
  init_presets();
5764
5800
  init_aws();
5801
+ init_errors();
5765
5802
  init_prompts();
5766
5803
  async function upgrade(options) {
5767
5804
  clack9.intro(pc10.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
@@ -6483,12 +6520,8 @@ ${pc10.bold("Cost Impact:")}`);
6483
6520
  }
6484
6521
  );
6485
6522
  } catch (error) {
6486
- clack9.log.error("Infrastructure upgrade failed");
6487
6523
  if (error.message?.includes("stack is currently locked")) {
6488
- clack9.log.warn("\nThe Pulumi stack is locked from a previous run.");
6489
- clack9.log.info("To fix this, run:");
6490
- clack9.log.info(` ${pc10.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
6491
- clack9.log.info("\nThen try running wraps upgrade again.");
6524
+ throw errors.stackLocked();
6492
6525
  }
6493
6526
  throw new Error(`Pulumi upgrade failed: ${error.message}`);
6494
6527
  }
@@ -8142,7 +8175,9 @@ function showHelp() {
8142
8175
  ` ${pc15.cyan("email connect")} Connect to existing AWS SES`
8143
8176
  );
8144
8177
  console.log(` ${pc15.cyan("email domains verify")} Verify domain DNS records`);
8145
- console.log(` ${pc15.cyan("email config")} Update infrastructure`);
8178
+ console.log(
8179
+ ` ${pc15.cyan("email sync")} Apply CLI updates to infrastructure`
8180
+ );
8146
8181
  console.log(` ${pc15.cyan("email upgrade")} Add features`);
8147
8182
  console.log(
8148
8183
  ` ${pc15.cyan("email restore")} Restore original configuration
@@ -8347,6 +8382,7 @@ async function run() {
8347
8382
  });
8348
8383
  break;
8349
8384
  case "config":
8385
+ case "sync":
8350
8386
  await config({
8351
8387
  region: flags.region,
8352
8388
  yes: flags.yes