@wraps.dev/cli 0.1.3 → 0.1.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
@@ -18,6 +18,205 @@ var init_esm_shims = __esm({
18
18
  }
19
19
  });
20
20
 
21
+ // src/utils/errors.ts
22
+ import * as clack from "@clack/prompts";
23
+ import pc from "picocolors";
24
+ function handleCLIError(error) {
25
+ console.error("");
26
+ if (error instanceof WrapsError) {
27
+ clack.log.error(error.message);
28
+ if (error.suggestion) {
29
+ console.log(`
30
+ ${pc.yellow("Suggestion:")}`);
31
+ console.log(` ${pc.white(error.suggestion)}
32
+ `);
33
+ }
34
+ if (error.docsUrl) {
35
+ console.log(`${pc.dim("Documentation:")}`);
36
+ console.log(` ${pc.blue(error.docsUrl)}
37
+ `);
38
+ }
39
+ process.exit(1);
40
+ }
41
+ clack.log.error("An unexpected error occurred");
42
+ console.error(error);
43
+ console.log(`
44
+ ${pc.dim("If this persists, please report at:")}`);
45
+ console.log(` ${pc.blue("https://github.com/wraps-team/wraps/issues")}
46
+ `);
47
+ process.exit(1);
48
+ }
49
+ var WrapsError, errors;
50
+ var init_errors = __esm({
51
+ "src/utils/errors.ts"() {
52
+ "use strict";
53
+ init_esm_shims();
54
+ WrapsError = class extends Error {
55
+ constructor(message, code, suggestion, docsUrl) {
56
+ super(message);
57
+ this.code = code;
58
+ this.suggestion = suggestion;
59
+ this.docsUrl = docsUrl;
60
+ this.name = "WrapsError";
61
+ }
62
+ };
63
+ errors = {
64
+ noAWSCredentials: () => new WrapsError(
65
+ "AWS credentials not found",
66
+ "NO_AWS_CREDENTIALS",
67
+ "Run: aws configure\nOr set AWS_PROFILE environment variable",
68
+ "https://docs.wraps.dev/setup/aws-credentials"
69
+ ),
70
+ stackExists: (stackName) => new WrapsError(
71
+ `Stack "${stackName}" already exists`,
72
+ "STACK_EXISTS",
73
+ `To update: wraps upgrade
74
+ To remove: wraps destroy --stack ${stackName}`,
75
+ "https://docs.wraps.dev/cli/upgrade"
76
+ ),
77
+ invalidRegion: (region) => new WrapsError(
78
+ `Invalid AWS region: ${region}`,
79
+ "INVALID_REGION",
80
+ "Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1",
81
+ "https://docs.aws.amazon.com/general/latest/gr/rande.html"
82
+ ),
83
+ pulumiError: (message) => new WrapsError(
84
+ `Infrastructure deployment failed: ${message}`,
85
+ "PULUMI_ERROR",
86
+ "Check your AWS permissions and try again",
87
+ "https://docs.wraps.dev/troubleshooting"
88
+ ),
89
+ noStack: () => new WrapsError(
90
+ "No Wraps infrastructure found in this AWS account",
91
+ "NO_STACK",
92
+ "Run: wraps init\nTo deploy new infrastructure",
93
+ "https://docs.wraps.dev/cli/init"
94
+ ),
95
+ pulumiNotInstalled: () => new WrapsError(
96
+ "Pulumi CLI is not installed",
97
+ "PULUMI_NOT_INSTALLED",
98
+ "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/",
99
+ "https://www.pulumi.com/docs/install/"
100
+ )
101
+ };
102
+ }
103
+ });
104
+
105
+ // src/utils/aws.ts
106
+ var aws_exports = {};
107
+ __export(aws_exports, {
108
+ checkRegion: () => checkRegion,
109
+ getAWSRegion: () => getAWSRegion,
110
+ isSESSandbox: () => isSESSandbox,
111
+ listSESDomains: () => listSESDomains,
112
+ validateAWSCredentials: () => validateAWSCredentials
113
+ });
114
+ import {
115
+ GetIdentityVerificationAttributesCommand,
116
+ ListIdentitiesCommand,
117
+ SESClient
118
+ } from "@aws-sdk/client-ses";
119
+ import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
120
+ async function validateAWSCredentials() {
121
+ const sts = new STSClient({ region: "us-east-1" });
122
+ try {
123
+ const identity = await sts.send(new GetCallerIdentityCommand({}));
124
+ return {
125
+ accountId: identity.Account,
126
+ userId: identity.UserId,
127
+ arn: identity.Arn
128
+ };
129
+ } catch (_error) {
130
+ throw errors.noAWSCredentials();
131
+ }
132
+ }
133
+ async function checkRegion(region) {
134
+ const validRegions = [
135
+ "us-east-1",
136
+ "us-east-2",
137
+ "us-west-1",
138
+ "us-west-2",
139
+ "af-south-1",
140
+ "ap-east-1",
141
+ "ap-south-1",
142
+ "ap-northeast-1",
143
+ "ap-northeast-2",
144
+ "ap-northeast-3",
145
+ "ap-southeast-1",
146
+ "ap-southeast-2",
147
+ "ap-southeast-3",
148
+ "ca-central-1",
149
+ "eu-central-1",
150
+ "eu-west-1",
151
+ "eu-west-2",
152
+ "eu-west-3",
153
+ "eu-south-1",
154
+ "eu-north-1",
155
+ "me-south-1",
156
+ "sa-east-1"
157
+ ];
158
+ return validRegions.includes(region);
159
+ }
160
+ async function getAWSRegion() {
161
+ if (process.env.AWS_REGION) {
162
+ return process.env.AWS_REGION;
163
+ }
164
+ if (process.env.AWS_DEFAULT_REGION) {
165
+ return process.env.AWS_DEFAULT_REGION;
166
+ }
167
+ return "us-east-1";
168
+ }
169
+ async function listSESDomains(region) {
170
+ const ses = new SESClient({ region });
171
+ try {
172
+ const identitiesResponse = await ses.send(
173
+ new ListIdentitiesCommand({
174
+ IdentityType: "Domain"
175
+ })
176
+ );
177
+ const identities = identitiesResponse.Identities || [];
178
+ if (identities.length === 0) {
179
+ return [];
180
+ }
181
+ const attributesResponse = await ses.send(
182
+ new GetIdentityVerificationAttributesCommand({
183
+ Identities: identities
184
+ })
185
+ );
186
+ const attributes = attributesResponse.VerificationAttributes || {};
187
+ return identities.map((domain) => ({
188
+ domain,
189
+ verified: attributes[domain]?.VerificationStatus === "Success"
190
+ }));
191
+ } catch (error) {
192
+ console.error("Error listing SES domains:", error);
193
+ return [];
194
+ }
195
+ }
196
+ async function isSESSandbox(region) {
197
+ const ses = new SESClient({ region });
198
+ try {
199
+ await ses.send(
200
+ new ListIdentitiesCommand({
201
+ MaxItems: 1
202
+ })
203
+ );
204
+ return false;
205
+ } catch (error) {
206
+ if (error.name === "InvalidParameterValue") {
207
+ return true;
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ var init_aws = __esm({
213
+ "src/utils/aws.ts"() {
214
+ "use strict";
215
+ init_esm_shims();
216
+ init_errors();
217
+ }
218
+ });
219
+
21
220
  // src/utils/costs.ts
22
221
  function estimateStorageSize(emailsPerMonth, retention, numEventTypes = 8) {
23
222
  const avgRecordSizeKB = 2;
@@ -1333,7 +1532,10 @@ async function createIAMRole(config) {
1333
1532
  "dynamodb:Scan",
1334
1533
  "dynamodb:BatchGetItem"
1335
1534
  ],
1336
- Resource: "arn:aws:dynamodb:*:*:table/wraps-email-*"
1535
+ Resource: [
1536
+ "arn:aws:dynamodb:*:*:table/wraps-email-*",
1537
+ "arn:aws:dynamodb:*:*:table/wraps-email-*/index/*"
1538
+ ]
1337
1539
  });
1338
1540
  }
1339
1541
  if (config.emailConfig.eventTracking?.enabled) {
@@ -1729,143 +1931,8 @@ async function deployEmailStack(config) {
1729
1931
  };
1730
1932
  }
1731
1933
 
1732
- // src/utils/aws.ts
1733
- init_esm_shims();
1734
- import {
1735
- GetIdentityVerificationAttributesCommand,
1736
- ListIdentitiesCommand,
1737
- SESClient
1738
- } from "@aws-sdk/client-ses";
1739
- import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
1740
-
1741
- // src/utils/errors.ts
1742
- init_esm_shims();
1743
- import * as clack from "@clack/prompts";
1744
- import pc from "picocolors";
1745
- var WrapsError = class extends Error {
1746
- constructor(message, code, suggestion, docsUrl) {
1747
- super(message);
1748
- this.code = code;
1749
- this.suggestion = suggestion;
1750
- this.docsUrl = docsUrl;
1751
- this.name = "WrapsError";
1752
- }
1753
- };
1754
- function handleCLIError(error) {
1755
- console.error("");
1756
- if (error instanceof WrapsError) {
1757
- clack.log.error(error.message);
1758
- if (error.suggestion) {
1759
- console.log(`
1760
- ${pc.yellow("Suggestion:")}`);
1761
- console.log(` ${pc.white(error.suggestion)}
1762
- `);
1763
- }
1764
- if (error.docsUrl) {
1765
- console.log(`${pc.dim("Documentation:")}`);
1766
- console.log(` ${pc.blue(error.docsUrl)}
1767
- `);
1768
- }
1769
- process.exit(1);
1770
- }
1771
- clack.log.error("An unexpected error occurred");
1772
- console.error(error);
1773
- console.log(`
1774
- ${pc.dim("If this persists, please report at:")}`);
1775
- console.log(` ${pc.blue("https://github.com/wraps-team/wraps/issues")}
1776
- `);
1777
- process.exit(1);
1778
- }
1779
- var errors = {
1780
- noAWSCredentials: () => new WrapsError(
1781
- "AWS credentials not found",
1782
- "NO_AWS_CREDENTIALS",
1783
- "Run: aws configure\nOr set AWS_PROFILE environment variable",
1784
- "https://docs.wraps.dev/setup/aws-credentials"
1785
- ),
1786
- stackExists: (stackName) => new WrapsError(
1787
- `Stack "${stackName}" already exists`,
1788
- "STACK_EXISTS",
1789
- `To update: wraps upgrade
1790
- To remove: wraps destroy --stack ${stackName}`,
1791
- "https://docs.wraps.dev/cli/upgrade"
1792
- ),
1793
- invalidRegion: (region) => new WrapsError(
1794
- `Invalid AWS region: ${region}`,
1795
- "INVALID_REGION",
1796
- "Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1",
1797
- "https://docs.aws.amazon.com/general/latest/gr/rande.html"
1798
- ),
1799
- pulumiError: (message) => new WrapsError(
1800
- `Infrastructure deployment failed: ${message}`,
1801
- "PULUMI_ERROR",
1802
- "Check your AWS permissions and try again",
1803
- "https://docs.wraps.dev/troubleshooting"
1804
- ),
1805
- noStack: () => new WrapsError(
1806
- "No Wraps infrastructure found in this AWS account",
1807
- "NO_STACK",
1808
- "Run: wraps init\nTo deploy new infrastructure",
1809
- "https://docs.wraps.dev/cli/init"
1810
- ),
1811
- pulumiNotInstalled: () => new WrapsError(
1812
- "Pulumi CLI is not installed",
1813
- "PULUMI_NOT_INSTALLED",
1814
- "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/",
1815
- "https://www.pulumi.com/docs/install/"
1816
- )
1817
- };
1818
-
1819
- // src/utils/aws.ts
1820
- async function validateAWSCredentials() {
1821
- const sts = new STSClient({ region: "us-east-1" });
1822
- try {
1823
- const identity = await sts.send(new GetCallerIdentityCommand({}));
1824
- return {
1825
- accountId: identity.Account,
1826
- userId: identity.UserId,
1827
- arn: identity.Arn
1828
- };
1829
- } catch (_error) {
1830
- throw errors.noAWSCredentials();
1831
- }
1832
- }
1833
- async function getAWSRegion() {
1834
- if (process.env.AWS_REGION) {
1835
- return process.env.AWS_REGION;
1836
- }
1837
- if (process.env.AWS_DEFAULT_REGION) {
1838
- return process.env.AWS_DEFAULT_REGION;
1839
- }
1840
- return "us-east-1";
1841
- }
1842
- async function listSESDomains(region) {
1843
- const ses = new SESClient({ region });
1844
- try {
1845
- const identitiesResponse = await ses.send(
1846
- new ListIdentitiesCommand({
1847
- IdentityType: "Domain"
1848
- })
1849
- );
1850
- const identities = identitiesResponse.Identities || [];
1851
- if (identities.length === 0) {
1852
- return [];
1853
- }
1854
- const attributesResponse = await ses.send(
1855
- new GetIdentityVerificationAttributesCommand({
1856
- Identities: identities
1857
- })
1858
- );
1859
- const attributes = attributesResponse.VerificationAttributes || {};
1860
- return identities.map((domain) => ({
1861
- domain,
1862
- verified: attributes[domain]?.VerificationStatus === "Success"
1863
- }));
1864
- } catch (error) {
1865
- console.error("Error listing SES domains:", error);
1866
- return [];
1867
- }
1868
- }
1934
+ // src/commands/connect.ts
1935
+ init_aws();
1869
1936
 
1870
1937
  // src/utils/fs.ts
1871
1938
  init_esm_shims();
@@ -2234,6 +2301,7 @@ init_prompts();
2234
2301
 
2235
2302
  // src/utils/pulumi.ts
2236
2303
  init_esm_shims();
2304
+ init_errors();
2237
2305
  import { exec } from "child_process";
2238
2306
  import { promisify } from "util";
2239
2307
  import * as automation2 from "@pulumi/pulumi/automation/index.js";
@@ -3802,6 +3870,7 @@ async function startConsoleServer(config) {
3802
3870
  }
3803
3871
 
3804
3872
  // src/commands/console.ts
3873
+ init_aws();
3805
3874
  async function runConsole(options) {
3806
3875
  clack5.intro(pc5.bold("Wraps Console"));
3807
3876
  const progress = new DeploymentProgress();
@@ -3853,6 +3922,7 @@ async function runConsole(options) {
3853
3922
 
3854
3923
  // src/commands/destroy.ts
3855
3924
  init_esm_shims();
3925
+ init_aws();
3856
3926
  import * as clack6 from "@clack/prompts";
3857
3927
  import * as pulumi7 from "@pulumi/pulumi";
3858
3928
  import pc6 from "picocolors";
@@ -3921,6 +3991,7 @@ init_esm_shims();
3921
3991
  import * as clack7 from "@clack/prompts";
3922
3992
  import * as pulumi8 from "@pulumi/pulumi";
3923
3993
  import pc7 from "picocolors";
3994
+ init_aws();
3924
3995
  init_costs();
3925
3996
  init_presets();
3926
3997
  init_prompts();
@@ -4127,6 +4198,7 @@ async function init(options) {
4127
4198
 
4128
4199
  // src/commands/restore.ts
4129
4200
  init_esm_shims();
4201
+ init_aws();
4130
4202
  import * as clack8 from "@clack/prompts";
4131
4203
  import * as pulumi9 from "@pulumi/pulumi";
4132
4204
  import pc8 from "picocolors";
@@ -4231,6 +4303,7 @@ ${pc8.green("\u2713")} ${pc8.bold("Infrastructure removed successfully!")}
4231
4303
 
4232
4304
  // src/commands/status.ts
4233
4305
  init_esm_shims();
4306
+ init_aws();
4234
4307
  import * as clack9 from "@clack/prompts";
4235
4308
  import * as pulumi10 from "@pulumi/pulumi";
4236
4309
  import pc9 from "picocolors";
@@ -4301,6 +4374,7 @@ init_esm_shims();
4301
4374
  import * as clack10 from "@clack/prompts";
4302
4375
  import * as pulumi11 from "@pulumi/pulumi";
4303
4376
  import pc10 from "picocolors";
4377
+ init_aws();
4304
4378
  init_costs();
4305
4379
  init_presets();
4306
4380
  init_prompts();
@@ -4450,6 +4524,36 @@ ${pc10.bold("Current Configuration:")}
4450
4524
  break;
4451
4525
  }
4452
4526
  case "tracking-domain": {
4527
+ if (!config.domain) {
4528
+ clack10.log.error(
4529
+ "No sending domain configured. You must configure a sending domain before adding a custom tracking domain."
4530
+ );
4531
+ clack10.log.info(
4532
+ `Use ${pc10.cyan("wraps init")} to set up a sending domain first.`
4533
+ );
4534
+ process.exit(1);
4535
+ }
4536
+ const { listSESDomains: listSESDomains2 } = await Promise.resolve().then(() => (init_aws(), aws_exports));
4537
+ const domains = await progress.execute(
4538
+ "Checking domain verification status",
4539
+ async () => await listSESDomains2(region)
4540
+ );
4541
+ const sendingDomain = domains.find((d) => d.domain === config.domain);
4542
+ if (!sendingDomain?.verified) {
4543
+ clack10.log.error(
4544
+ `Sending domain ${pc10.cyan(config.domain)} is not verified.`
4545
+ );
4546
+ clack10.log.info(
4547
+ "You must verify your sending domain before adding a custom tracking domain."
4548
+ );
4549
+ clack10.log.info(
4550
+ `Use ${pc10.cyan("wraps verify")} to check DNS records and complete verification.`
4551
+ );
4552
+ process.exit(1);
4553
+ }
4554
+ progress.info(
4555
+ `Sending domain ${pc10.cyan(config.domain)} is verified ${pc10.green("\u2713")}`
4556
+ );
4453
4557
  const trackingDomain = await clack10.text({
4454
4558
  message: "Custom tracking redirect domain:",
4455
4559
  placeholder: "track.yourdomain.com",
@@ -4741,6 +4845,7 @@ ${pc10.green("\u2713")} ${pc10.bold("Upgrade complete!")}
4741
4845
 
4742
4846
  // src/commands/verify.ts
4743
4847
  init_esm_shims();
4848
+ init_aws();
4744
4849
  import { Resolver } from "dns/promises";
4745
4850
  import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as SESv2Client3 } from "@aws-sdk/client-sesv2";
4746
4851
  import * as clack11 from "@clack/prompts";
@@ -4909,6 +5014,7 @@ function printCompletionScript() {
4909
5014
  }
4910
5015
 
4911
5016
  // src/cli.ts
5017
+ init_errors();
4912
5018
  setupTabCompletion();
4913
5019
  function showHelp() {
4914
5020
  clack12.intro(pc12.bold("WRAPS CLI"));