@wraps.dev/cli 2.18.2 → 2.18.4

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
@@ -5979,8 +5979,15 @@ function displaySuccess(outputs) {
5979
5979
  clack8.outro(pc10.green("Email infrastructure deployed successfully!"));
5980
5980
  console.log(lines.join("\n"));
5981
5981
  if (outputs.dnsAutoCreated && outputs.domain) {
5982
+ const providerNames = {
5983
+ route53: "AWS Route53",
5984
+ vercel: "Vercel DNS",
5985
+ cloudflare: "Cloudflare",
5986
+ manual: "Manual"
5987
+ };
5988
+ const providerDisplay = providerNames[outputs.dnsProvider ?? "route53"] ?? "your DNS provider";
5982
5989
  clack8.note(
5983
- `DNS records (DKIM, SPF, DMARC) were automatically created in Route53 for ${pc10.cyan(
5990
+ `DNS records (DKIM, SPF, DMARC) were automatically created in ${providerDisplay} for ${pc10.cyan(
5984
5991
  outputs.domain
5985
5992
  )}.
5986
5993
 
@@ -8971,9 +8978,8 @@ Run ${pc27.cyan("wraps email init")} to deploy email infrastructure.
8971
8978
  process.exit(1);
8972
8979
  return;
8973
8980
  }
8974
- const emailConfig = metadata.services.email;
8975
- const domain = emailConfig.config.domain;
8976
- if (!domain) {
8981
+ const trackedDomains = getAllTrackedDomains(metadata);
8982
+ if (trackedDomains.length === 0) {
8977
8983
  progress.stop();
8978
8984
  clack25.log.error("No domain configured in email infrastructure");
8979
8985
  console.log(`
@@ -8982,6 +8988,24 @@ Run ${pc27.cyan("wraps email init")} to set up a domain.
8982
8988
  process.exit(1);
8983
8989
  return;
8984
8990
  }
8991
+ let domain;
8992
+ if (trackedDomains.length === 1) {
8993
+ domain = trackedDomains[0].domain;
8994
+ } else {
8995
+ const selected = await clack25.select({
8996
+ message: "Which domain do you want to send from?",
8997
+ options: trackedDomains.map((d) => ({
8998
+ value: d.domain,
8999
+ label: d.domain,
9000
+ hint: d.isPrimary ? "primary" : d.purpose
9001
+ }))
9002
+ });
9003
+ if (clack25.isCancel(selected)) {
9004
+ clack25.cancel("Operation cancelled.");
9005
+ process.exit(0);
9006
+ }
9007
+ domain = selected;
9008
+ }
8985
9009
  let toEmail = options.to;
8986
9010
  if (!toEmail) {
8987
9011
  if (isJsonMode() && !options.scenario) {
@@ -17707,12 +17731,7 @@ async function createSMTPCredentials(config2) {
17707
17731
  {
17708
17732
  Effect: "Allow",
17709
17733
  Action: "ses:SendRawEmail",
17710
- Resource: "*",
17711
- Condition: {
17712
- StringEquals: {
17713
- "ses:ConfigurationSetName": config2.configSetName
17714
- }
17715
- }
17734
+ Resource: "*"
17716
17735
  }
17717
17736
  ]
17718
17737
  })
@@ -19742,18 +19761,17 @@ Run ${pc23.cyan("wraps email init")} first to deploy email infrastructure.
19742
19761
  }
19743
19762
  clack21.log.step(`Adding ${pc23.cyan(domain)}`);
19744
19763
  const sesClient = new SESv2Client4({ region });
19764
+ let domainAlreadyExists = false;
19765
+ let dkimTokens = [];
19745
19766
  try {
19746
- await sesClient.send(
19767
+ const existing = await sesClient.send(
19747
19768
  new GetEmailIdentityCommand2({ EmailIdentity: domain })
19748
19769
  );
19749
- progress.stop();
19750
- clack21.log.warn(`Domain ${domain} already exists in SES`);
19751
- console.log(
19752
- `
19753
- Run ${pc23.cyan(`wraps email domains verify --domain ${domain}`)} to check verification status.
19754
- `
19770
+ domainAlreadyExists = true;
19771
+ dkimTokens = existing.DkimAttributes?.Tokens || [];
19772
+ clack21.log.info(
19773
+ `Domain ${pc23.cyan(domain)} already exists in SES \u2014 adopting into Wraps`
19755
19774
  );
19756
- return;
19757
19775
  } catch (error) {
19758
19776
  if (!isAWSNotFoundError(error)) {
19759
19777
  throw error;
@@ -19763,22 +19781,34 @@ Run ${pc23.cyan(`wraps email domains verify --domain ${domain}`)} to check verif
19763
19781
  if (!options.yes) {
19764
19782
  purpose = await promptDomainPurpose();
19765
19783
  }
19766
- const { CreateEmailIdentityCommand } = await import("@aws-sdk/client-sesv2");
19767
- await progress.execute("Creating SES identity", async () => {
19768
- await sesClient.send(
19769
- new CreateEmailIdentityCommand({
19770
- EmailIdentity: domain,
19771
- ConfigurationSetName: "wraps-email-tracking",
19772
- DkimSigningAttributes: {
19773
- NextSigningKeyLength: "RSA_2048_BIT"
19774
- }
19775
- })
19784
+ if (domainAlreadyExists) {
19785
+ const { PutEmailIdentityConfigurationSetAttributesCommand } = await import("@aws-sdk/client-sesv2");
19786
+ await progress.execute("Associating tracking configuration", async () => {
19787
+ await sesClient.send(
19788
+ new PutEmailIdentityConfigurationSetAttributesCommand({
19789
+ EmailIdentity: domain,
19790
+ ConfigurationSetName: "wraps-email-tracking"
19791
+ })
19792
+ );
19793
+ });
19794
+ } else {
19795
+ const { CreateEmailIdentityCommand } = await import("@aws-sdk/client-sesv2");
19796
+ await progress.execute("Creating SES identity", async () => {
19797
+ await sesClient.send(
19798
+ new CreateEmailIdentityCommand({
19799
+ EmailIdentity: domain,
19800
+ ConfigurationSetName: "wraps-email-tracking",
19801
+ DkimSigningAttributes: {
19802
+ NextSigningKeyLength: "RSA_2048_BIT"
19803
+ }
19804
+ })
19805
+ );
19806
+ });
19807
+ const sesIdentity = await sesClient.send(
19808
+ new GetEmailIdentityCommand2({ EmailIdentity: domain })
19776
19809
  );
19777
- });
19778
- const sesIdentity = await sesClient.send(
19779
- new GetEmailIdentityCommand2({ EmailIdentity: domain })
19780
- );
19781
- const dkimTokens = sesIdentity.DkimAttributes?.Tokens || [];
19810
+ dkimTokens = sesIdentity.DkimAttributes?.Tokens || [];
19811
+ }
19782
19812
  let mailFromDomain;
19783
19813
  if (options.yes) {
19784
19814
  mailFromDomain = `mail.${domain}`;
@@ -20470,12 +20500,30 @@ async function inboundInit(options) {
20470
20500
  if (!metadata?.services?.email) {
20471
20501
  throw errors.inboundRequiresOutbound();
20472
20502
  }
20473
- const emailService = metadata.services.email;
20474
- const emailConfig = emailService.config;
20475
- const domain = emailConfig.domain;
20476
- if (!domain) {
20503
+ const trackedDomains = getAllTrackedDomains(metadata);
20504
+ if (trackedDomains.length === 0) {
20477
20505
  throw errors.inboundRequiresOutbound();
20478
20506
  }
20507
+ let domain;
20508
+ if (trackedDomains.length === 1) {
20509
+ domain = trackedDomains[0].domain;
20510
+ } else {
20511
+ const selected = await clack22.select({
20512
+ message: "Which domain do you want to receive email on?",
20513
+ options: trackedDomains.map((d) => ({
20514
+ value: d.domain,
20515
+ label: d.domain,
20516
+ hint: d.isPrimary ? "primary" : d.purpose
20517
+ }))
20518
+ });
20519
+ if (clack22.isCancel(selected)) {
20520
+ clack22.cancel("Operation cancelled.");
20521
+ process.exit(0);
20522
+ }
20523
+ domain = selected;
20524
+ }
20525
+ const emailService = metadata.services.email;
20526
+ const emailConfig = emailService.config;
20479
20527
  const subdomain = options.root ? "" : options.subdomain ?? (options.yes ? "inbound" : await promptInboundSubdomain(domain));
20480
20528
  const receivingDomain = subdomain ? `${subdomain}.${domain}` : domain;
20481
20529
  clack22.log.info(`Receiving domain: ${pc24.cyan(receivingDomain)}`);
@@ -21651,6 +21699,21 @@ async function init2(options) {
21651
21699
  if (domain) {
21652
21700
  emailConfig.domain = domain;
21653
21701
  }
21702
+ if (domain && !options.quick && preset !== "custom") {
21703
+ const wantsMailFrom = await clack26.confirm({
21704
+ message: `Configure MAIL FROM for ${pc28.cyan(domain)}? ${pc28.dim("(improves DMARC alignment)")}`,
21705
+ initialValue: true
21706
+ });
21707
+ if (clack26.isCancel(wantsMailFrom)) {
21708
+ clack26.cancel("Operation cancelled.");
21709
+ process.exit(0);
21710
+ }
21711
+ if (wantsMailFrom) {
21712
+ const mailFromFull = await promptMailFromSubdomain(domain);
21713
+ const suffix = `.${domain}`;
21714
+ emailConfig.mailFromSubdomain = mailFromFull.endsWith(suffix) ? mailFromFull.slice(0, -suffix.length) || "mail" : "mail";
21715
+ }
21716
+ }
21654
21717
  let costSummary;
21655
21718
  if (!options.quick) {
21656
21719
  const estimatedVolume = await promptEstimatedVolume();
@@ -22099,6 +22162,7 @@ ${pc28.yellow(pc28.bold("Configuration Warnings:"))}`);
22099
22162
  tableName: outputs.tableName,
22100
22163
  dnsRecords: void 0,
22101
22164
  dnsAutoCreated,
22165
+ dnsProvider,
22102
22166
  domain: outputs.domain,
22103
22167
  mailFromDomain: outputs.mailFromDomain
22104
22168
  });