@wraps.dev/cli 1.4.1 → 1.5.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.
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.1",
149
+ version: "1.5.0",
150
150
  description: "CLI for deploying Wraps email infrastructure to your AWS account",
151
151
  type: "module",
152
152
  main: "./dist/cli.js",
@@ -3198,6 +3198,35 @@ ${pc2.bold("Dashboard:")} ${pc2.blue("https://app.wraps.dev")}`);
3198
3198
  console.log(`${pc2.bold("Docs:")} ${pc2.blue("https://wraps.dev/docs")}
3199
3199
  `);
3200
3200
  }
3201
+ function displayPreview(outputs) {
3202
+ console.log(pc2.yellow("\n--- PREVIEW MODE (no changes will be made) ---\n"));
3203
+ const changes = outputs.changeSummary;
3204
+ const summaryLines = [];
3205
+ if (changes.create && changes.create > 0) {
3206
+ summaryLines.push(` ${pc2.green("+")} ${changes.create} to create`);
3207
+ }
3208
+ if (changes.update && changes.update > 0) {
3209
+ summaryLines.push(` ${pc2.yellow("~")} ${changes.update} to update`);
3210
+ }
3211
+ if (changes.delete && changes.delete > 0) {
3212
+ summaryLines.push(` ${pc2.red("-")} ${changes.delete} to destroy`);
3213
+ }
3214
+ if (changes.same && changes.same > 0) {
3215
+ summaryLines.push(` ${pc2.dim("=")} ${changes.same} unchanged`);
3216
+ }
3217
+ if (changes.replace && changes.replace > 0) {
3218
+ summaryLines.push(` ${pc2.magenta("+-")} ${changes.replace} to replace`);
3219
+ }
3220
+ if (summaryLines.length > 0) {
3221
+ clack2.note(summaryLines.join("\n"), "Resource Changes");
3222
+ } else {
3223
+ clack2.note("No changes detected", "Resource Changes");
3224
+ }
3225
+ if (outputs.costEstimate) {
3226
+ clack2.note(outputs.costEstimate, "Estimated Monthly Cost");
3227
+ }
3228
+ console.log(pc2.yellow("\n--- END PREVIEW (no changes were made) ---\n"));
3229
+ }
3201
3230
 
3202
3231
  // src/commands/dashboard/update-role.ts
3203
3232
  async function updateRole(options) {
@@ -3965,19 +3994,14 @@ async function configurationSetExists(configSetName, region) {
3965
3994
  }
3966
3995
  async function eventDestinationExists(configSetName, eventDestName, region) {
3967
3996
  try {
3968
- const {
3969
- SESv2Client: SESv2Client5,
3970
- GetConfigurationSetEventDestinationsCommand
3971
- } = await import("@aws-sdk/client-sesv2");
3997
+ const { SESv2Client: SESv2Client5, GetConfigurationSetEventDestinationsCommand } = await import("@aws-sdk/client-sesv2");
3972
3998
  const ses = new SESv2Client5({ region });
3973
3999
  const response = await ses.send(
3974
4000
  new GetConfigurationSetEventDestinationsCommand({
3975
4001
  ConfigurationSetName: configSetName
3976
4002
  })
3977
4003
  );
3978
- return response.EventDestinations?.some(
3979
- (dest) => dest.Name === eventDestName
3980
- ) ?? false;
4004
+ return response.EventDestinations?.some((dest) => dest.Name === eventDestName) ?? false;
3981
4005
  } catch (error) {
3982
4006
  if (error.name === "NotFoundException") {
3983
4007
  return false;
@@ -4394,7 +4418,11 @@ async function ensurePulumiInstalled() {
4394
4418
 
4395
4419
  // src/commands/email/config.ts
4396
4420
  async function config(options) {
4397
- clack3.intro(pc4.bold("Wraps Config - Apply CLI Updates to Infrastructure"));
4421
+ clack3.intro(
4422
+ pc4.bold(
4423
+ options.preview ? "Wraps Config Preview" : "Wraps Config - Apply CLI Updates to Infrastructure"
4424
+ )
4425
+ );
4398
4426
  const progress = new DeploymentProgress();
4399
4427
  const wasAutoInstalled = await progress.execute(
4400
4428
  "Checking Pulumi CLI installation",
@@ -4471,7 +4499,7 @@ ${pc4.bold("Current Configuration:")}
4471
4499
  "Your current configuration will be preserved - no features will be added or removed"
4472
4500
  );
4473
4501
  console.log("");
4474
- if (!options.yes) {
4502
+ if (!(options.yes || options.preview)) {
4475
4503
  const confirmed = await clack3.confirm({
4476
4504
  message: "Proceed with update?",
4477
4505
  initialValue: true
@@ -4491,6 +4519,61 @@ ${pc4.bold("Current Configuration:")}
4491
4519
  vercel: vercelConfig,
4492
4520
  emailConfig: config2
4493
4521
  };
4522
+ if (options.preview) {
4523
+ try {
4524
+ const previewResult = await progress.execute(
4525
+ "Generating update preview",
4526
+ async () => {
4527
+ await ensurePulumiWorkDir();
4528
+ const stack = await pulumi5.automation.LocalWorkspace.createOrSelectStack(
4529
+ {
4530
+ stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
4531
+ projectName: "wraps-email",
4532
+ program: async () => {
4533
+ const result2 = await deployEmailStack(stackConfig);
4534
+ return {
4535
+ roleArn: result2.roleArn,
4536
+ configSetName: result2.configSetName,
4537
+ tableName: result2.tableName,
4538
+ region: result2.region,
4539
+ lambdaFunctions: result2.lambdaFunctions,
4540
+ domain: result2.domain,
4541
+ dkimTokens: result2.dkimTokens,
4542
+ customTrackingDomain: result2.customTrackingDomain
4543
+ };
4544
+ }
4545
+ },
4546
+ {
4547
+ workDir: getPulumiWorkDir(),
4548
+ envVars: {
4549
+ PULUMI_CONFIG_PASSPHRASE: "",
4550
+ AWS_REGION: region
4551
+ },
4552
+ secretsProvider: "passphrase"
4553
+ }
4554
+ );
4555
+ await stack.setConfig("aws:region", { value: region });
4556
+ await stack.refresh({ onOutput: () => {
4557
+ } });
4558
+ const result = await stack.preview({ diff: true });
4559
+ return result;
4560
+ }
4561
+ );
4562
+ displayPreview({
4563
+ changeSummary: previewResult.changeSummary,
4564
+ commandName: "wraps email config"
4565
+ });
4566
+ clack3.outro(
4567
+ pc4.green("Preview complete. Run without --preview to update.")
4568
+ );
4569
+ return;
4570
+ } catch (error) {
4571
+ if (error.message?.includes("stack is currently locked")) {
4572
+ throw errors.stackLocked();
4573
+ }
4574
+ throw new Error(`Preview failed: ${error.message}`);
4575
+ }
4576
+ }
4494
4577
  let outputs;
4495
4578
  try {
4496
4579
  outputs = await progress.execute(
@@ -4819,7 +4902,11 @@ async function scanAWSResources(region) {
4819
4902
 
4820
4903
  // src/commands/email/connect.ts
4821
4904
  async function connect(options) {
4822
- clack5.intro(pc6.bold("Wraps Connect - Link Existing Infrastructure"));
4905
+ clack5.intro(
4906
+ pc6.bold(
4907
+ options.preview ? "Wraps Connect Preview" : "Wraps Connect - Link Existing Infrastructure"
4908
+ )
4909
+ );
4823
4910
  const progress = new DeploymentProgress();
4824
4911
  const wasAutoInstalled = await progress.execute(
4825
4912
  "Checking Pulumi CLI installation",
@@ -4897,7 +4984,7 @@ async function connect(options) {
4897
4984
  if (domainIdentities.length > 0) {
4898
4985
  emailConfig.domain = domainIdentities[0];
4899
4986
  }
4900
- if (!options.yes) {
4987
+ if (!(options.yes || options.preview)) {
4901
4988
  const confirmed = await confirmConnect();
4902
4989
  if (!confirmed) {
4903
4990
  clack5.cancel("Connection cancelled.");
@@ -4910,6 +4997,59 @@ async function connect(options) {
4910
4997
  vercel: vercelConfig,
4911
4998
  emailConfig
4912
4999
  };
5000
+ if (options.preview) {
5001
+ try {
5002
+ const previewResult = await progress.execute(
5003
+ "Generating infrastructure preview",
5004
+ async () => {
5005
+ await ensurePulumiWorkDir();
5006
+ const stack = await pulumi6.automation.LocalWorkspace.createOrSelectStack(
5007
+ {
5008
+ stackName: `wraps-${identity.accountId}-${region}`,
5009
+ projectName: "wraps-email",
5010
+ program: async () => {
5011
+ const result2 = await deployEmailStack(stackConfig);
5012
+ return {
5013
+ roleArn: result2.roleArn,
5014
+ configSetName: result2.configSetName,
5015
+ tableName: result2.tableName,
5016
+ region: result2.region,
5017
+ lambdaFunctions: result2.lambdaFunctions,
5018
+ domain: result2.domain,
5019
+ dkimTokens: result2.dkimTokens,
5020
+ customTrackingDomain: result2.customTrackingDomain
5021
+ };
5022
+ }
5023
+ },
5024
+ {
5025
+ workDir: getPulumiWorkDir(),
5026
+ envVars: {
5027
+ PULUMI_CONFIG_PASSPHRASE: "",
5028
+ AWS_REGION: region
5029
+ },
5030
+ secretsProvider: "passphrase"
5031
+ }
5032
+ );
5033
+ await stack.setConfig("aws:region", { value: region });
5034
+ const result = await stack.preview({ diff: true });
5035
+ return result;
5036
+ }
5037
+ );
5038
+ displayPreview({
5039
+ changeSummary: previewResult.changeSummary,
5040
+ commandName: "wraps email connect"
5041
+ });
5042
+ clack5.outro(
5043
+ pc6.green("Preview complete. Run without --preview to connect.")
5044
+ );
5045
+ return;
5046
+ } catch (error) {
5047
+ if (error.message?.includes("stack is currently locked")) {
5048
+ throw errors.stackLocked();
5049
+ }
5050
+ throw new Error(`Preview failed: ${error.message}`);
5051
+ }
5052
+ }
4913
5053
  let outputs;
4914
5054
  try {
4915
5055
  outputs = await progress.execute(
@@ -5465,7 +5605,11 @@ init_aws();
5465
5605
  init_errors();
5466
5606
  init_prompts();
5467
5607
  async function init(options) {
5468
- clack7.intro(pc8.bold("Wraps Email Infrastructure Setup"));
5608
+ clack7.intro(
5609
+ pc8.bold(
5610
+ options.preview ? "Wraps Email Infrastructure Preview" : "Wraps Email Infrastructure Setup"
5611
+ )
5612
+ );
5469
5613
  const progress = new DeploymentProgress();
5470
5614
  const wasAutoInstalled = await progress.execute(
5471
5615
  "Checking Pulumi CLI installation",
@@ -5548,7 +5692,7 @@ ${pc8.yellow(pc8.bold("Configuration Warnings:"))}`);
5548
5692
  if (vercelConfig) {
5549
5693
  metadata.vercel = vercelConfig;
5550
5694
  }
5551
- if (!options.yes) {
5695
+ if (!(options.yes || options.preview)) {
5552
5696
  const confirmed = await confirmDeploy();
5553
5697
  if (!confirmed) {
5554
5698
  clack7.cancel("Deployment cancelled.");
@@ -5561,6 +5705,64 @@ ${pc8.yellow(pc8.bold("Configuration Warnings:"))}`);
5561
5705
  vercel: vercelConfig,
5562
5706
  emailConfig
5563
5707
  };
5708
+ if (options.preview) {
5709
+ try {
5710
+ const previewResult = await progress.execute(
5711
+ "Generating infrastructure preview",
5712
+ async () => {
5713
+ await ensurePulumiWorkDir();
5714
+ const stack = await pulumi7.automation.LocalWorkspace.createOrSelectStack(
5715
+ {
5716
+ stackName: `wraps-${identity.accountId}-${region}`,
5717
+ projectName: "wraps-email",
5718
+ program: async () => {
5719
+ const result2 = await deployEmailStack(stackConfig);
5720
+ return {
5721
+ roleArn: result2.roleArn,
5722
+ configSetName: result2.configSetName,
5723
+ tableName: result2.tableName,
5724
+ region: result2.region,
5725
+ lambdaFunctions: result2.lambdaFunctions,
5726
+ domain: result2.domain,
5727
+ dkimTokens: result2.dkimTokens,
5728
+ customTrackingDomain: result2.customTrackingDomain,
5729
+ mailFromDomain: result2.mailFromDomain,
5730
+ archiveArn: result2.archiveArn,
5731
+ archivingEnabled: result2.archivingEnabled,
5732
+ archiveRetention: result2.archiveRetention
5733
+ };
5734
+ }
5735
+ },
5736
+ {
5737
+ workDir: getPulumiWorkDir(),
5738
+ envVars: {
5739
+ PULUMI_CONFIG_PASSPHRASE: "",
5740
+ AWS_REGION: region
5741
+ },
5742
+ secretsProvider: "passphrase"
5743
+ }
5744
+ );
5745
+ await stack.setConfig("aws:region", { value: region });
5746
+ const result = await stack.preview({ diff: true });
5747
+ return result;
5748
+ }
5749
+ );
5750
+ displayPreview({
5751
+ changeSummary: previewResult.changeSummary,
5752
+ costEstimate: costSummary,
5753
+ commandName: "wraps email init"
5754
+ });
5755
+ clack7.outro(
5756
+ pc8.green("Preview complete. Run without --preview to deploy.")
5757
+ );
5758
+ return;
5759
+ } catch (error) {
5760
+ if (error.message?.includes("stack is currently locked")) {
5761
+ throw errors.stackLocked();
5762
+ }
5763
+ throw new Error(`Preview failed: ${error.message}`);
5764
+ }
5765
+ }
5564
5766
  let outputs;
5565
5767
  try {
5566
5768
  outputs = await progress.execute(
@@ -5686,7 +5888,11 @@ import * as clack8 from "@clack/prompts";
5686
5888
  import * as pulumi8 from "@pulumi/pulumi";
5687
5889
  import pc9 from "picocolors";
5688
5890
  async function restore(options) {
5689
- clack8.intro(pc9.bold("Wraps Restore - Remove Wraps Infrastructure"));
5891
+ clack8.intro(
5892
+ pc9.bold(
5893
+ options.preview ? "Wraps Restore Preview" : "Wraps Restore - Remove Wraps Infrastructure"
5894
+ )
5895
+ );
5690
5896
  clack8.log.info(
5691
5897
  `${pc9.yellow("Note:")} This will remove all Wraps-managed infrastructure.`
5692
5898
  );
@@ -5733,7 +5939,7 @@ ${pc9.bold("The following Wraps resources will be removed:")}
5733
5939
  }
5734
5940
  console.log(` ${pc9.cyan("\u2713")} IAM Role (wraps-email-role)`);
5735
5941
  console.log("");
5736
- if (!options.force) {
5942
+ if (!(options.force || options.preview)) {
5737
5943
  const confirmed = await clack8.confirm({
5738
5944
  message: "Proceed with removal? This cannot be undone.",
5739
5945
  initialValue: false
@@ -5743,6 +5949,50 @@ ${pc9.bold("The following Wraps resources will be removed:")}
5743
5949
  process.exit(0);
5744
5950
  }
5745
5951
  }
5952
+ if (options.preview) {
5953
+ if (metadata.services.email?.pulumiStackName) {
5954
+ try {
5955
+ const previewResult = await progress.execute(
5956
+ "Generating removal preview",
5957
+ async () => {
5958
+ const stack = await pulumi8.automation.LocalWorkspace.selectStack(
5959
+ {
5960
+ stackName: metadata.services.email.pulumiStackName,
5961
+ projectName: "wraps-email",
5962
+ program: async () => {
5963
+ }
5964
+ // Empty program for destroy
5965
+ },
5966
+ {
5967
+ workDir: getPulumiWorkDir(),
5968
+ envVars: {
5969
+ PULUMI_CONFIG_PASSPHRASE: "",
5970
+ AWS_REGION: region
5971
+ },
5972
+ secretsProvider: "passphrase"
5973
+ }
5974
+ );
5975
+ const result = await stack.preview({ diff: true });
5976
+ return result;
5977
+ }
5978
+ );
5979
+ displayPreview({
5980
+ changeSummary: previewResult.changeSummary,
5981
+ costEstimate: "Monthly cost after removal: $0.00",
5982
+ commandName: "wraps email restore"
5983
+ });
5984
+ clack8.outro(
5985
+ pc9.green(
5986
+ "Preview complete. Run without --preview to remove infrastructure."
5987
+ )
5988
+ );
5989
+ return;
5990
+ } catch (error) {
5991
+ throw new Error(`Preview failed: ${error.message}`);
5992
+ }
5993
+ }
5994
+ return;
5995
+ }
5746
5996
  if (metadata.services.email?.pulumiStackName) {
5747
5997
  await progress.execute("Removing Wraps infrastructure", async () => {
5748
5998
  try {
@@ -5801,7 +6051,11 @@ init_aws();
5801
6051
  init_errors();
5802
6052
  init_prompts();
5803
6053
  async function upgrade(options) {
5804
- clack9.intro(pc10.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
6054
+ clack9.intro(
6055
+ pc10.bold(
6056
+ options.preview ? "Wraps Upgrade Preview" : "Wraps Upgrade - Enhance Your Email Infrastructure"
6057
+ )
6058
+ );
5805
6059
  const progress = new DeploymentProgress();
5806
6060
  const wasAutoInstalled = await progress.execute(
5807
6061
  "Checking Pulumi CLI installation",
@@ -6431,7 +6685,7 @@ ${pc10.bold("Cost Impact:")}`);
6431
6685
  );
6432
6686
  }
6433
6687
  console.log("");
6434
- if (!options.yes) {
6688
+ if (!(options.yes || options.preview)) {
6435
6689
  const confirmed = await clack9.confirm({
6436
6690
  message: "Proceed with upgrade?",
6437
6691
  initialValue: true
@@ -6453,6 +6707,73 @@ ${pc10.bold("Cost Impact:")}`);
6453
6707
  vercel: vercelConfig,
6454
6708
  emailConfig: updatedConfig
6455
6709
  };
6710
+ if (options.preview) {
6711
+ try {
6712
+ const previewResult = await progress.execute(
6713
+ "Generating upgrade preview",
6714
+ async () => {
6715
+ await ensurePulumiWorkDir();
6716
+ const stack = await pulumi9.automation.LocalWorkspace.createOrSelectStack(
6717
+ {
6718
+ stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
6719
+ projectName: "wraps-email",
6720
+ program: async () => {
6721
+ const result2 = await deployEmailStack(stackConfig);
6722
+ return {
6723
+ roleArn: result2.roleArn,
6724
+ configSetName: result2.configSetName,
6725
+ tableName: result2.tableName,
6726
+ region: result2.region,
6727
+ lambdaFunctions: result2.lambdaFunctions,
6728
+ domain: result2.domain,
6729
+ dkimTokens: result2.dkimTokens,
6730
+ customTrackingDomain: result2.customTrackingDomain,
6731
+ httpsTrackingEnabled: result2.httpsTrackingEnabled,
6732
+ cloudFrontDomain: result2.cloudFrontDomain,
6733
+ acmCertificateValidationRecords: result2.acmCertificateValidationRecords,
6734
+ archiveArn: result2.archiveArn,
6735
+ archivingEnabled: result2.archivingEnabled,
6736
+ archiveRetention: result2.archiveRetention
6737
+ };
6738
+ }
6739
+ },
6740
+ {
6741
+ workDir: getPulumiWorkDir(),
6742
+ envVars: {
6743
+ PULUMI_CONFIG_PASSPHRASE: "",
6744
+ AWS_REGION: region
6745
+ },
6746
+ secretsProvider: "passphrase"
6747
+ }
6748
+ );
6749
+ await stack.setConfig("aws:region", { value: region });
6750
+ await stack.refresh({ onOutput: () => {
6751
+ } });
6752
+ const result = await stack.preview({ diff: true });
6753
+ return result;
6754
+ }
6755
+ );
6756
+ const costComparison = [
6757
+ `Current: ${formatCost(currentCostData.total.monthly)}/mo`,
6758
+ `After upgrade: ${formatCost(newCostData.total.monthly)}/mo`,
6759
+ costDiff > 0 ? `Change: +${formatCost(costDiff)}/mo` : costDiff < 0 ? `Change: -${formatCost(Math.abs(costDiff))}/mo` : "Change: No cost difference"
6760
+ ].join("\n");
6761
+ displayPreview({
6762
+ changeSummary: previewResult.changeSummary,
6763
+ costEstimate: costComparison,
6764
+ commandName: "wraps email upgrade"
6765
+ });
6766
+ clack9.outro(
6767
+ pc10.green("Preview complete. Run without --preview to upgrade.")
6768
+ );
6769
+ return;
6770
+ } catch (error) {
6771
+ if (error.message?.includes("stack is currently locked")) {
6772
+ throw errors.stackLocked();
6773
+ }
6774
+ throw new Error(`Preview failed: ${error.message}`);
6775
+ }
6776
+ }
6456
6777
  let outputs;
6457
6778
  try {
6458
6779
  outputs = await progress.execute(
@@ -7918,14 +8239,18 @@ import * as clack11 from "@clack/prompts";
7918
8239
  import * as pulumi11 from "@pulumi/pulumi";
7919
8240
  import pc12 from "picocolors";
7920
8241
  async function destroy(options) {
7921
- clack11.intro(pc12.bold("Wraps Email Infrastructure Teardown"));
8242
+ clack11.intro(
8243
+ pc12.bold(
8244
+ options.preview ? "Wraps Destruction Preview" : "Wraps Email Infrastructure Teardown"
8245
+ )
8246
+ );
7922
8247
  const progress = new DeploymentProgress();
7923
8248
  const identity = await progress.execute(
7924
8249
  "Validating AWS credentials",
7925
8250
  async () => validateAWSCredentials()
7926
8251
  );
7927
8252
  const region = await getAWSRegion();
7928
- if (!options.force) {
8253
+ if (!(options.force || options.preview)) {
7929
8254
  const confirmed = await clack11.confirm({
7930
8255
  message: pc12.red(
7931
8256
  "Are you sure you want to destroy all Wraps infrastructure?"
@@ -7937,6 +8262,44 @@ async function destroy(options) {
7937
8262
  process.exit(0);
7938
8263
  }
7939
8264
  }
8265
+ if (options.preview) {
8266
+ try {
8267
+ const previewResult = await progress.execute(
8268
+ "Generating destruction preview",
8269
+ async () => {
8270
+ await ensurePulumiWorkDir();
8271
+ const stackName = `wraps-${identity.accountId}-${region}`;
8272
+ let stack;
8273
+ try {
8274
+ stack = await pulumi11.automation.LocalWorkspace.selectStack({
8275
+ stackName,
8276
+ workDir: getPulumiWorkDir()
8277
+ });
8278
+ } catch (_error) {
8279
+ throw new Error("No Wraps infrastructure found to preview");
8280
+ }
8281
+ const result = await stack.preview({ diff: true });
8282
+ return result;
8283
+ }
8284
+ );
8285
+ displayPreview({
8286
+ changeSummary: previewResult.changeSummary,
8287
+ costEstimate: "Monthly cost after destruction: $0.00",
8288
+ commandName: "wraps destroy"
8289
+ });
8290
+ clack11.outro(
8291
+ pc12.green("Preview complete. Run without --preview to destroy.")
8292
+ );
8293
+ return;
8294
+ } catch (error) {
8295
+ progress.stop();
8296
+ if (error.message.includes("No Wraps infrastructure found")) {
8297
+ clack11.log.warn("No Wraps infrastructure found to preview");
8298
+ process.exit(0);
8299
+ }
8300
+ throw new Error(`Preview failed: ${error.message}`);
8301
+ }
8302
+ }
7940
8303
  try {
7941
8304
  await progress.execute(
7942
8305
  "Destroying infrastructure (this may take 2-3 minutes)",
@@ -8207,6 +8570,9 @@ function showHelp() {
8207
8570
  console.log(` ${pc15.dim("--preset")} Configuration preset`);
8208
8571
  console.log(` ${pc15.dim("-y, --yes")} Skip confirmation prompts`);
8209
8572
  console.log(` ${pc15.dim("-f, --force")} Force destructive operations`);
8573
+ console.log(
8574
+ ` ${pc15.dim("--preview")} Preview changes without deploying`
8575
+ );
8210
8576
  console.log(` ${pc15.dim("-v, --version")} Show version number
8211
8577
  `);
8212
8578
  console.log(
@@ -8266,6 +8632,11 @@ args.options([
8266
8632
  name: "noOpen",
8267
8633
  description: "Don't open browser automatically",
8268
8634
  defaultValue: false
8635
+ },
8636
+ {
8637
+ name: "preview",
8638
+ description: "Preview changes without deploying",
8639
+ defaultValue: false
8269
8640
  }
8270
8641
  ]);
8271
8642
  var flags = args.parse(process.argv);
@@ -8327,13 +8698,15 @@ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-
8327
8698
  region: flags.region,
8328
8699
  domain: flags.domain,
8329
8700
  preset: flags.preset,
8330
- yes: flags.yes
8701
+ yes: flags.yes,
8702
+ preview: flags.preview
8331
8703
  });
8332
8704
  } else {
8333
8705
  await connect({
8334
8706
  provider: flags.provider,
8335
8707
  region: flags.region,
8336
- yes: flags.yes
8708
+ yes: flags.yes,
8709
+ preview: flags.preview
8337
8710
  });
8338
8711
  }
8339
8712
  }
@@ -8371,33 +8744,38 @@ async function run() {
8371
8744
  region: flags.region,
8372
8745
  domain: flags.domain,
8373
8746
  preset: flags.preset,
8374
- yes: flags.yes
8747
+ yes: flags.yes,
8748
+ preview: flags.preview
8375
8749
  });
8376
8750
  break;
8377
8751
  case "connect":
8378
8752
  await connect({
8379
8753
  provider: flags.provider,
8380
8754
  region: flags.region,
8381
- yes: flags.yes
8755
+ yes: flags.yes,
8756
+ preview: flags.preview
8382
8757
  });
8383
8758
  break;
8384
8759
  case "config":
8385
8760
  case "sync":
8386
8761
  await config({
8387
8762
  region: flags.region,
8388
- yes: flags.yes
8763
+ yes: flags.yes,
8764
+ preview: flags.preview
8389
8765
  });
8390
8766
  break;
8391
8767
  case "upgrade":
8392
8768
  await upgrade({
8393
8769
  region: flags.region,
8394
- yes: flags.yes
8770
+ yes: flags.yes,
8771
+ preview: flags.preview
8395
8772
  });
8396
8773
  break;
8397
8774
  case "restore":
8398
8775
  await restore({
8399
8776
  region: flags.region,
8400
- force: flags.force
8777
+ force: flags.force,
8778
+ preview: flags.preview
8401
8779
  });
8402
8780
  break;
8403
8781
  case "domains": {
@@ -8539,7 +8917,8 @@ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-
8539
8917
  break;
8540
8918
  case "destroy":
8541
8919
  await destroy({
8542
- force: flags.force
8920
+ force: flags.force,
8921
+ preview: flags.preview
8543
8922
  });
8544
8923
  break;
8545
8924
  case "completion":